Skip to content

Instantly share code, notes, and snippets.

@yrrah
Last active December 16, 2015 09:49
Show Gist options
  • Save yrrah/5415948 to your computer and use it in GitHub Desktop.
Save yrrah/5415948 to your computer and use it in GitHub Desktop.
#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