Last active
December 16, 2015 09:49
-
-
Save yrrah/5415948 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <SD.h> | |
#include <MIDI.h> | |
#include <TimerOne.h> | |
#include <DmxSimple.h> | |
#define DEBUG 1 | |
#define MIDI_BAUDRATE 31250 | |
#define COMPILE_MIDI_IN 1 // Set this setting to 1 to use the MIDI input. | |
#define COMPILE_MIDI_OUT 0 // Set this setting to 1 to use the MIDI output. | |
#define COMPILE_MIDI_THRU 0 // Set this setting to 1 to use the MIDI Soft Thru feature | |
#define USE_SERIAL_PORT Serial | |
#define USE_RUNNING_STATUS 0 | |
#define USE_1BYTE_PARSING 1 | |
#define USE_CALLBACKS 0 | |
#define MIDI_SYSEX_ARRAY_SIZE 64 | |
#define FRAME_GROUP 0 | |
#define LISTEN_CHAN 1 | |
#define CASE_VAL_L 2 | |
#define CASE_VAL_H 3 | |
#define MIDI_VAR 4 | |
#define MSG_TYPE 5 | |
#define FG_ADDR 1 | |
#define FG_LENGTH 0 | |
#define CURRENT_F 2 | |
#define PITCH 0 | |
#define VELOCITY 1 | |
#define POT1 41 | |
#define POT2 42 | |
#define POT3 43 | |
#define POT4 44 | |
#define FADER 45 | |
File myFile; | |
volatile int milliSec = 0; | |
char* temp = ""; | |
int** caseArray; | |
int caseCount; | |
int** caseInfo; | |
int** fades; | |
int fgcount; //number of frame groups & corresponding cases | |
int maxChannel = 20; | |
double global_note, global_velocity; | |
volatile int period = 0; | |
int step = 1; | |
void loadFrame(int FrameID) { | |
int i; | |
int chan, val, upVal; | |
char in, in2; | |
#ifdef DEBUG | |
Serial.print("Load Frame Group:"); | |
Serial.print(FrameID); | |
Serial.print(" Frame:"); | |
Serial.println(caseInfo[FrameID][CURRENT_F]); | |
#endif | |
//seek to location of frame group on SD card | |
myFile.seek(caseInfo[FrameID][FG_ADDR]); | |
//find location of frame | |
i = 0; | |
while (i <= caseInfo[FrameID][CURRENT_F]) { | |
while (myFile.read() != '%'); | |
i++; | |
} | |
//read and execute DMX instructions | |
in = myFile.read(); | |
while (myFile.available() && in != '%' && in != '#') { | |
switch (in) { | |
case 'S': | |
case 'P': | |
case 'F': | |
case 'M': | |
in2 = myFile.read(); | |
myFile.readBytes(temp, 2); | |
chan = asciiHexToInt(temp, 2); | |
myFile.readBytes(temp, 2); | |
val = asciiHexToInt(temp, 2); | |
myFile.readBytes(temp, 2); | |
upVal = asciiHexToInt(temp, 2); | |
#ifdef DEBUG | |
Serial.flush(); | |
Serial.print("DMX Instruction: chan "); | |
Serial.print(chan, HEX); | |
Serial.print(", "); | |
Serial.print(val, HEX); | |
Serial.print(", "); | |
Serial.println(upVal, HEX); | |
#endif | |
double potValue; | |
int out; | |
switch (in) { | |
case 'S'://ST = Static | |
DmxSimple.write(chan, val); | |
break; | |
case 'P': | |
switch (in2) { | |
case '1'://P1 = Potentiometer 1 | |
potValue = analogRead(POT1); | |
break; | |
case '2'://P1 = Potentiometer 2 | |
potValue = analogRead(POT2); | |
break; | |
case '3'://P1 = Potentiometer 3 | |
potValue = analogRead(POT3); | |
break; | |
case '4'://P1 = Potentiometer 4 | |
potValue = analogRead(POT4); | |
break; | |
case '5'://P1 = Fader / Slider | |
potValue = analogRead(FADER); | |
break; | |
} | |
out = (int) ((potValue / 1023)*(upVal - val) + val); | |
DmxSimple.write(chan, out); | |
break; | |
case 'F': | |
switch (in2) { | |
case 'U'://FU = Fade Up | |
fades[chan][0] = 1; | |
DmxSimple.write(chan, val); | |
break; | |
case 'D'://FD = Fade Down | |
fades[chan][0] = 2; | |
DmxSimple.write(chan, upVal); | |
break; | |
} | |
fades[chan][1] = val; | |
fades[chan][2] = upVal; | |
break; | |
case 'M': | |
/* Serial.print(val); | |
Serial.print(" "); | |
Serial.println(upVal); | |
*/ switch (in2) { | |
case '1'://M1 = MIDI Note | |
out = (int) ((global_note / 127)*(upVal - val) + val); | |
break; | |
case '2'://M2 = MIDI Velocity | |
out = (int) ((global_velocity / 127)*(upVal - val) + val); | |
break; | |
} | |
// Serial.println(float2s(out, 5)); | |
DmxSimple.write(chan, out); | |
break; | |
} | |
break; | |
default: | |
#ifdef DEBUG | |
Serial.println("DMX Instuction Type not implemented yet"); | |
Serial.print(caseInfo[FrameID][CURRENT_F]); | |
Serial.print(" "); | |
Serial.print(FrameID); | |
Serial.print(" "); | |
Serial.print(in); | |
Serial.println(in2); | |
#endif | |
break; | |
} | |
in = myFile.read(); | |
} | |
//increment frame counter | |
if (++caseInfo[FrameID][CURRENT_F] >= caseInfo[FrameID][FG_LENGTH]) | |
caseInfo[FrameID][CURRENT_F] = 0; | |
} | |
void loadScene(char* fileName) { | |
int i; | |
int looplength; | |
// open the file. note that only one file can be open at a time, | |
// so you have to close this one before opening another. | |
myFile.close(); | |
myFile = SD.open(fileName, FILE_READ); | |
if (!myFile) { | |
// if the file didn't open, print an error: | |
#ifdef DEBUG | |
Serial.print("error opening scene: "); | |
Serial.println(fileName); | |
#endif | |
}else{ | |
//free the memory for the old case array if it exists | |
if (fgcount != 0) { | |
for (i = 0; i < fgcount; i++) | |
free(caseArray[i]); | |
free(caseArray); | |
} | |
myFile.readBytes(temp, 2); | |
fgcount = asciiHexToInt(temp, 2); | |
myFile.readBytes(temp, 2); | |
looplength = asciiHexToInt(temp, 2); | |
myFile.readBytes(temp, 2); | |
maxChannel = asciiHexToInt(temp, 2); | |
fades = (int**) malloc(maxChannel * sizeof (int*)); | |
for (i = 0; i < maxChannel; i++) { | |
fades[i] = (int*) malloc(3 * sizeof (int)); | |
fades[i][0] = 0; | |
} | |
#ifdef DEBUG | |
Serial.print("fgcount "); | |
Serial.println(fgcount); | |
Serial.print("looplength "); | |
Serial.println(looplength); | |
Serial.print("maxChannel "); | |
Serial.println(maxChannel); | |
#endif | |
caseInfo = (int**) malloc(fgcount * sizeof (int*)); | |
for (i = 0; i < fgcount; i++) { | |
caseInfo[i] = (int*) malloc(3 * sizeof (int)); | |
while (myFile.read() != '#'); | |
myFile.readBytes(temp, 2); | |
caseInfo[i][FG_LENGTH] = asciiHexToInt(temp, 2); | |
caseInfo[i][FG_ADDR] = myFile.position(); | |
caseInfo[i][CURRENT_F] = 0; | |
#ifdef DEBUG | |
Serial.flush(); | |
Serial.print("------ Case Info "); | |
Serial.print(i); | |
Serial.println(" -------"); | |
Serial.print("FG Length: "); | |
Serial.println(caseInfo[i][FG_LENGTH]); | |
Serial.print("FG Address: "); | |
Serial.println(caseInfo[i][FG_ADDR]); | |
Serial.print("Current Frame: "); | |
Serial.println(caseInfo[i][CURRENT_F]); | |
#endif | |
} | |
while (myFile.read() != '$'); | |
myFile.readBytes(temp, 2); | |
caseCount = asciiHexToInt(temp, 2); | |
int fg = 0; | |
caseArray = (int**) malloc(caseCount * sizeof (int*)); | |
for (i = 0; i < caseCount; i++) { | |
caseArray[i] = (int*) malloc(6 * sizeof (int)); | |
char c = myFile.read(); | |
if(c == '$'){ | |
fg++; | |
} | |
caseArray[i][FRAME_GROUP] = fg; | |
//MIDI Listen Channel | |
myFile.readBytes(temp, 2); | |
caseArray[i][LISTEN_CHAN] = asciiHexToInt(temp, 2); | |
//MIDI low case value | |
myFile.readBytes(temp, 2); | |
caseArray[i][CASE_VAL_L] = asciiHexToInt(temp, 2); | |
//MIDI high case value | |
myFile.readBytes(temp, 2); | |
caseArray[i][CASE_VAL_H] = asciiHexToInt(temp, 2); | |
//MIDI varible (pitch,velocity,etc) | |
myFile.readBytes(temp, 1); | |
caseArray[i][MIDI_VAR] = asciiHexToInt(temp, 1); | |
//MIDI message type (note on, note off, etc) | |
myFile.readBytes(temp, 1); | |
caseArray[i][MSG_TYPE] = asciiHexToInt(temp, 1); | |
#ifdef DEBUG | |
Serial.flush(); | |
Serial.print("------ Case Array Index "); | |
Serial.print(i); | |
Serial.println(" -------"); | |
Serial.print("Frame Group "); | |
Serial.println(caseArray[i][FRAME_GROUP]); | |
Serial.print("MIDI Listen Channel "); | |
Serial.println(caseArray[i][LISTEN_CHAN]); | |
Serial.print("MIDI low case value "); | |
Serial.println(caseArray[i][CASE_VAL_L]); | |
Serial.print("MIDI high case value "); | |
Serial.println(caseArray[i][CASE_VAL_H]); | |
Serial.print("MIDI varible "); | |
Serial.println(caseArray[i][MIDI_VAR]); | |
Serial.print("MIDI message type "); | |
Serial.println(caseArray[i][MSG_TYPE]); | |
Serial.flush(); | |
#endif | |
} | |
} | |
} | |
void count() { | |
milliSec++; | |
// Serial.println(milliSec); | |
step = 255/period; | |
if(step==0)step = 1; | |
} | |
void setup() { | |
DmxSimple.usePin(7); | |
DmxSimple.maxChannel(8); | |
//pinMode(3, OUTPUT); | |
//MIDI.begin(MIDI_CHANNEL_OMNI); | |
Timer1.initialize(10000); //in microseconds (1000000us = 1s) | |
Timer1.attachInterrupt(count); //runs every millisecond | |
Timer1.start(); | |
#ifdef DEBUG | |
Serial.print("Initializing SD card..."); | |
#endif | |
// On the Ethernet Shield, CS is pin 4. It's set as an output by default.*/ | |
// Note that even if it's not used as the CS pin, the hardware SS pin | |
// (10 on most Arduino boards, 53 on the Mega) must be left as an output | |
// or the SD library functions will not work. | |
pinMode(20, OUTPUT); | |
if (!SD.begin(20)) { | |
#ifdef DEBUG | |
Serial.println("initialization failed!"); | |
#endif | |
return; | |
} | |
myFile = SD.open("config.xml", FILE_READ); | |
if (!myFile) { | |
// if the file didn't open, print an error: | |
#ifdef DEBUG | |
Serial.println("error opening config file"); | |
#endif | |
}else{ | |
#ifdef DEBUG | |
Serial.println("initialization done."); | |
#endif | |
loadScene("TestTwo.scn"); | |
} | |
} | |
int asciiHexToInt(char* a, int length) { | |
//converts acii representation of hex eg 'ff' to 0xFF | |
int val; | |
int in; | |
int out = 0; | |
for (int i = length - 1; i >= 0; i--) { | |
in = a[i]; | |
if (in >= 'A' && in <= 'F') { | |
val = in - 55; | |
} else if (in >= 'a' && in <= 'f') { | |
val = in - 87; | |
} else if (in >= '0' & in <= '9') { | |
val = in - 48; | |
} else return -1; | |
out += val * (int) ceil((pow(16, length - 1 - i))); | |
} | |
return out; | |
} | |
boolean handleMIDI(int midiType, int midiChan, int note, int velocity) { | |
global_note = note; | |
global_velocity = velocity; | |
int midiValue = 0; | |
int i; | |
//MIDI Clock Signal | |
if (midiType == NoteOn && midiChan == 1 && note == 12/*C1*/ && velocity == 10) { | |
#ifdef DEBUG | |
Serial.print("Clock... Period: "); | |
Serial.println(period); | |
#endif | |
period = milliSec; | |
milliSec = 0; | |
} | |
#ifdef DEBUG | |
Serial.print("Type: "); | |
Serial.print(midiType); | |
Serial.print(" Chan: "); | |
Serial.print(midiChan); | |
Serial.print(" note: "); | |
Serial.print(note); | |
Serial.print(" velocity: "); | |
Serial.println(velocity); | |
#endif | |
//loop through all cases | |
for (int i = 0; i < caseCount; i++) { | |
//check to see if correct message type (Note on, Note off, etc) | |
if (caseArray[i][MSG_TYPE] == midiType | |
&& caseArray[i][LISTEN_CHAN] == midiChan) { | |
//get relevant incoming midi data | |
switch (caseArray[i][MIDI_VAR]) { | |
case PITCH: | |
midiValue = note; | |
break; | |
case VELOCITY: | |
midiValue = velocity; | |
break; | |
} | |
//do comparison and load frame if true | |
if (midiValue >= caseArray[i][CASE_VAL_L] | |
&& midiValue <= caseArray[i][CASE_VAL_H]) { | |
loadFrame(caseArray[i][FRAME_GROUP]); | |
return 1; | |
} | |
} | |
} | |
return 0; | |
} | |
void loop() { | |
boolean dmx = 0; | |
int i; | |
if (usbMIDI.read()) | |
dmx = handleMIDI(usbMIDI.getType(), usbMIDI.getChannel(), usbMIDI.getData1(), usbMIDI.getData2()); | |
else if (MIDI.read()) | |
handleMIDI(MIDI.getType(), MIDI.getChannel(), MIDI.getData1(), MIDI.getData2()); | |
if (step){ | |
for (i = 0; i < maxChannel; i++) { | |
if(fades[i][0] == 1){//fade up | |
fades[i][1] = fades[i][1] + step;//increase fade | |
if(fades[i][1]<fades[i][2]){//if val < max | |
DmxSimple.write(i, fades[i][1]); | |
/* Serial.print(i); | |
Serial.print(" fade up "); | |
Serial.print(period); | |
Serial.print(" x "); | |
Serial.println(fades[i][1]); | |
*/ }else{ | |
fades[i][0] = 0;//disable fade | |
DmxSimple.write(i, 0); | |
} | |
}else if(fades[i][0] == 2){//fade down | |
fades[i][2] = fades[i][2] - step;//decrease fade | |
if(fades[i][2]>fades[i][1]){//if val > min | |
DmxSimple.write(i, fades[i][2]); | |
/* Serial.print(i); | |
Serial.print(" fade dn "); | |
Serial.print(period); | |
Serial.print(" x "); | |
Serial.println(fades[i][2]); | |
*/ }else{ | |
fades[i][0] = 0;//disable fade | |
DmxSimple.write(i, 0); | |
} | |
}else{ | |
// DmxSimple.write(i, 0); | |
} | |
} | |
step=0; | |
} | |
} | |
char * float2s(double f, unsigned int digits) { | |
int index = 0; | |
static char s[16]; // buffer to build string representation | |
// handle sign | |
if (f < 0.0) { | |
s[index++] = '-'; | |
f = -f; | |
} | |
// handle infinite values | |
if (isinf(f)) { | |
strcpy(&s[index], "INF"); | |
return s; | |
} | |
// handle Not a Number | |
if (isnan(f)) { | |
strcpy(&s[index], "NaN"); | |
return s; | |
} | |
// max digits | |
if (digits > 9) digits = 9; | |
long multiplier = pow(10, digits); // fix int => long | |
int exponent = int(log10(f)); | |
double g = f / pow(10, exponent); | |
if ((g < 1.0) && (g != 0.0)) { | |
g *= 10; | |
exponent--; | |
} | |
long whole = long(g); // single digit | |
long part = long((g - whole) * multiplier); // # digits | |
char format[16]; | |
sprintf(format, "%%ld.%%0%dld E%%+d", digits); | |
sprintf(&s[index], format, whole, part, exponent); | |
return s; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment