mirror of
https://github.com/jhbruhn/eurorack.git
synced 2025-03-15 02:55:49 +00:00
Add code
This commit is contained in:
parent
ba4512c174
commit
40ff253bca
1 changed files with 589 additions and 0 deletions
589
MIDI2CV.ino
Normal file
589
MIDI2CV.ino
Normal file
|
@ -0,0 +1,589 @@
|
||||||
|
#include <MIDI.h>
|
||||||
|
#include <Wire.h>
|
||||||
|
#include <mcp4728.h>
|
||||||
|
#include <EEPROM.h>
|
||||||
|
|
||||||
|
#define MIDI_CHANNEL 1
|
||||||
|
|
||||||
|
#define OCTAVE_RANGE 5
|
||||||
|
#define NOTE_RANGE (OCTAVE_RANGE * 12)
|
||||||
|
#define MAX_CV (OCTAVE_RANGE * 1000)
|
||||||
|
#define BASE_NOTE 34
|
||||||
|
|
||||||
|
// Define Pitch Bend range to be a major second
|
||||||
|
#define BEND_RANGE ((1000.0 / 12.0) * 1.5)
|
||||||
|
|
||||||
|
|
||||||
|
#define PIN_GATE_0 9
|
||||||
|
#define PIN_GATE_1 10
|
||||||
|
#define PIN_GATE_2 11
|
||||||
|
#define PIN_GATE_3 12
|
||||||
|
|
||||||
|
#define MODE_MONO 0
|
||||||
|
#define MODE_ARP 1
|
||||||
|
#define MODE_SEQ 2
|
||||||
|
|
||||||
|
#define PIN_SPEED A3
|
||||||
|
|
||||||
|
#define PIN_FLIP_SWITCH_0 A6
|
||||||
|
#define PIN_FLIP_SWITCH_1 A7
|
||||||
|
|
||||||
|
#define FLIP_SWITCH_UP 0
|
||||||
|
#define FLIP_SWITCH_MIDDLE 1
|
||||||
|
#define FLIP_SWITCH_DOWN 2
|
||||||
|
|
||||||
|
#define ROTARY_SWITCH_PIN0 2
|
||||||
|
#define ROTARY_SWITCH_PIN1 3
|
||||||
|
#define ROTARY_SWITCH_PIN2 4
|
||||||
|
#define ROTARY_SWITCH_PIN3 5
|
||||||
|
#define ROTARY_SWITCH_PIN4 6
|
||||||
|
#define ROTARY_SWITCH_PIN5 7
|
||||||
|
|
||||||
|
#define MIDI_CLOCK_THRESHOLD_MILLIS 500
|
||||||
|
|
||||||
|
#define MIDI_CLOCK_DIVIDE_QUARTER 24
|
||||||
|
#define MIDI_CLOCK_DIVIDE_HALF MIDI_CLOCK_DIVIDE_QUARTER * 2
|
||||||
|
#define MIDI_CLOCK_DIVIDE_WHOLE MIDI_CLOCK_DIVIDE_HALF * 2
|
||||||
|
#define MIDI_CLOCK_DIVIDE_EIGTH MIDI_CLOCK_DIVIDE_QUARTER / 2
|
||||||
|
#define MIDI_CLOCK_DIVIDE_EIGHT_T MIDI_CLOCK_DIVIDE_QUARTER / 3
|
||||||
|
#define MIDI_CLOCK_DIVIDE_SIXTEENTH MIDI_CLOCK_DIVIDE_EIGTH / 2
|
||||||
|
#define MIDI_CLOCK_DIVIDE_SIXTEENTH_T MIDI_CLOCK_DIVIDE_EIGTH / 3
|
||||||
|
#define MIDI_CLOCK_DIVIDE_THIRTY_SECOND MIDI_CLOCK_DIVIDE_SIXTEENTH / 2
|
||||||
|
#define MIDI_CLOCK_DIVIDE_THIRTY_SECOND_T MIDI_CLOCK_DIVIDE_SIXTEENTH / 3
|
||||||
|
|
||||||
|
#define MIDI_CLOCK_DIVISION_COUNT 9
|
||||||
|
const int clockDivisions[] = { MIDI_CLOCK_DIVIDE_WHOLE, MIDI_CLOCK_DIVIDE_HALF,
|
||||||
|
MIDI_CLOCK_DIVIDE_QUARTER,
|
||||||
|
MIDI_CLOCK_DIVIDE_EIGTH, MIDI_CLOCK_DIVIDE_EIGHT_T,
|
||||||
|
MIDI_CLOCK_DIVIDE_SIXTEENTH, MIDI_CLOCK_DIVIDE_SIXTEENTH_T,
|
||||||
|
MIDI_CLOCK_DIVIDE_THIRTY_SECOND, MIDI_CLOCK_DIVIDE_THIRTY_SECOND_T
|
||||||
|
};
|
||||||
|
|
||||||
|
#define MANUAL_CLOCK_MIN 20
|
||||||
|
#define MANUAL_CLOCK_MAX 2000
|
||||||
|
|
||||||
|
#define SEQUENCE_COUNT 6
|
||||||
|
#define SEQUENCE_LENGTH_MAX 128
|
||||||
|
|
||||||
|
#define ARP_DIRECTION_UP 0
|
||||||
|
#define ARP_DIRECTION_DOWN 1
|
||||||
|
#define ARP_DIRECTION_BOTH 2
|
||||||
|
#define ARP_DIRECTION_ORDER 3
|
||||||
|
#define ARP_DIRECTION_RANDOM 4
|
||||||
|
#define ARP_DIRECTION_NONE 5
|
||||||
|
|
||||||
|
#define ARP_MODE_LATCH FLIP_SWITCH_UP
|
||||||
|
#define ARP_MODE_FORGET FLIP_SWITCH_MIDDLE
|
||||||
|
#define ARP_MODE_ADD FLIP_SWITCH_DOWN
|
||||||
|
|
||||||
|
|
||||||
|
mcp4728 dac = mcp4728(0);
|
||||||
|
|
||||||
|
MIDI_CREATE_DEFAULT_INSTANCE();
|
||||||
|
|
||||||
|
byte currentMode = MODE_MONO;
|
||||||
|
|
||||||
|
byte flipSwitch0 = FLIP_SWITCH_UP;
|
||||||
|
byte flipSwitch1 = FLIP_SWITCH_UP;
|
||||||
|
byte rotarySwitch = 0;
|
||||||
|
int speedValue = 0;
|
||||||
|
|
||||||
|
|
||||||
|
int cv[] = {0, 0, 0, 0}; // 0 to MAX_CV
|
||||||
|
bool gate[] = {LOW, LOW, LOW, LOW}; // LOW or HIGH
|
||||||
|
bool midiStartSignal = LOW;
|
||||||
|
bool midiClockSignal = LOW;
|
||||||
|
unsigned long activeNotes[NOTE_RANGE];
|
||||||
|
unsigned long noteCount = 1;
|
||||||
|
byte activeNoteCount = 0;
|
||||||
|
int currentPitchBend = 0;
|
||||||
|
byte currentModulation = 0;
|
||||||
|
byte currentVelocity = 0;
|
||||||
|
bool trigger = false;
|
||||||
|
bool released = false;
|
||||||
|
|
||||||
|
byte lastChannel = 0;
|
||||||
|
|
||||||
|
int clockCounter = 0;
|
||||||
|
unsigned long lastClockMillis = 0;
|
||||||
|
byte currentClockDivide = 0;
|
||||||
|
byte nextClockDivide = 0;
|
||||||
|
|
||||||
|
unsigned long lastManualClockMillis = 0;
|
||||||
|
|
||||||
|
byte currentSequencePosition = 0;
|
||||||
|
|
||||||
|
byte currentArpPosition = 0;
|
||||||
|
unsigned long activeArpNotes[NOTE_RANGE];
|
||||||
|
byte activeArpNoteCount = 0;
|
||||||
|
bool arpLatchReadyForNew = true;
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
dac.begin();
|
||||||
|
dac.vdd(5000);
|
||||||
|
dac.setVref(1,1,1,1); // set to use internal voltage reference (2.048V)
|
||||||
|
dac.setGain(1, 1); // set the gain of internal voltage reference ( 2.048V x 2 = 4.096V )
|
||||||
|
|
||||||
|
pinMode(PIN_GATE_0, OUTPUT);
|
||||||
|
pinMode(PIN_GATE_1, OUTPUT);
|
||||||
|
pinMode(PIN_GATE_2, OUTPUT);
|
||||||
|
pinMode(PIN_GATE_3, OUTPUT);
|
||||||
|
|
||||||
|
MIDI.begin(MIDI_CHANNEL_OMNI);
|
||||||
|
MIDI.setHandleClock(onMidiClock);
|
||||||
|
MIDI.setHandleNoteOn(onMidiNoteOn);
|
||||||
|
MIDI.setHandleNoteOff(onMidiNoteOff);
|
||||||
|
MIDI.setHandleStart(onMidiStart);
|
||||||
|
MIDI.setHandleControlChange(onMidiControlChange);
|
||||||
|
MIDI.setHandlePitchBend(onMidiPitchBend);
|
||||||
|
}
|
||||||
|
|
||||||
|
void onMidiClock() {
|
||||||
|
if(clockCounter == 0) {
|
||||||
|
midiClockSignal = HIGH;
|
||||||
|
}
|
||||||
|
clockCounter++;
|
||||||
|
if(clockCounter == clockDivisions[currentClockDivide]) {
|
||||||
|
clockCounter = 0;
|
||||||
|
currentClockDivide = nextClockDivide;
|
||||||
|
}
|
||||||
|
lastClockMillis = millis();
|
||||||
|
}
|
||||||
|
|
||||||
|
void onMidiNoteOn(byte channel, byte note, byte velocity) {
|
||||||
|
if(channel > MIDI_CHANNEL + 1) return;
|
||||||
|
|
||||||
|
lastChannel = channel;
|
||||||
|
|
||||||
|
if(velocity == 0) { onMidiNoteOff(channel, note, velocity); return; }
|
||||||
|
|
||||||
|
activeNotes[note - BASE_NOTE] = noteCount++;
|
||||||
|
activeNoteCount++;
|
||||||
|
currentVelocity = velocity;
|
||||||
|
trigger = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void onMidiNoteOff(byte channel, byte note, byte velocity) {
|
||||||
|
if(channel > MIDI_CHANNEL + 1) return;
|
||||||
|
activeNotes[note - BASE_NOTE] = 0;
|
||||||
|
activeNoteCount--;
|
||||||
|
if(activeNoteCount == 0) currentVelocity = 0;
|
||||||
|
released = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void onMidiControlChange(byte channel, byte number, byte value) {
|
||||||
|
if(channel > MIDI_CHANNEL + 1) return;
|
||||||
|
currentModulation = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void onMidiPitchBend(byte channel, int bend) {
|
||||||
|
if(channel > MIDI_CHANNEL + 1) return;
|
||||||
|
currentPitchBend = bend;
|
||||||
|
}
|
||||||
|
|
||||||
|
void onMidiStart() {
|
||||||
|
midiStartSignal = HIGH;
|
||||||
|
clockCounter = 0;
|
||||||
|
currentClockDivide = nextClockDivide;
|
||||||
|
}
|
||||||
|
|
||||||
|
int midiToCV(byte note) {
|
||||||
|
if(note < BASE_NOTE) return 0;
|
||||||
|
if(note - BASE_NOTE > NOTE_RANGE) return MAX_CV;
|
||||||
|
return map(note - BASE_NOTE, 0, NOTE_RANGE, 0, MAX_CV);
|
||||||
|
}
|
||||||
|
|
||||||
|
byte getMostRecentNote() {
|
||||||
|
byte note;
|
||||||
|
unsigned long maxTime = 0;
|
||||||
|
for(byte i = 0; i < NOTE_RANGE; i++) {
|
||||||
|
if(activeNotes[i] != 0 && activeNotes[i] >= maxTime) {
|
||||||
|
note = i;
|
||||||
|
maxTime = activeNotes[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return note;
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
readSwitches();
|
||||||
|
|
||||||
|
if(flipSwitch0 == FLIP_SWITCH_UP) currentMode = MODE_MONO;
|
||||||
|
else if(flipSwitch0 == FLIP_SWITCH_MIDDLE) currentMode = MODE_SEQ;
|
||||||
|
else if(flipSwitch0 == FLIP_SWITCH_DOWN) currentMode = MODE_ARP;
|
||||||
|
|
||||||
|
readSpeed();
|
||||||
|
MIDI.read();
|
||||||
|
|
||||||
|
if(millis() - lastClockMillis > MIDI_CLOCK_THRESHOLD_MILLIS) {
|
||||||
|
// Apparently we are not getting any midi clocks, so we will just generate our own signal.
|
||||||
|
long waitTime = map(speedValue, 0, 1023, (60000) / MANUAL_CLOCK_MIN, (60000) / MANUAL_CLOCK_MAX);
|
||||||
|
if(millis() - lastManualClockMillis > waitTime) {
|
||||||
|
// Time for another clock signal!
|
||||||
|
lastManualClockMillis = millis();
|
||||||
|
midiClockSignal = HIGH;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
nextClockDivide = map(speedValue, 0, 1023, 0, MIDI_CLOCK_DIVISION_COUNT);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool triggerOut = false;
|
||||||
|
|
||||||
|
|
||||||
|
switch(currentMode) {
|
||||||
|
case MODE_MONO:
|
||||||
|
if(activeNoteCount > 0) {
|
||||||
|
// Find most recent hit key
|
||||||
|
byte note = getMostRecentNote();
|
||||||
|
|
||||||
|
int value = midiToCV(note + BASE_NOTE);
|
||||||
|
cv[0] = value + mapfloat(currentPitchBend, 0, MIDI_PITCHBEND_MAX, -BEND_RANGE, BEND_RANGE);
|
||||||
|
cv[1] = map(currentVelocity, 0, 127, 0, MAX_CV);
|
||||||
|
cv[2] = map(currentModulation, 0, 127, 0, MAX_CV);
|
||||||
|
triggerOut = trigger;
|
||||||
|
if(lastChannel == MIDI_CHANNEL + 1 && trigger) { // IF on second channel, we retrigger the gate.
|
||||||
|
gate[0] = LOW;
|
||||||
|
} else {
|
||||||
|
gate[0] = HIGH;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
gate[0] = LOW;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case MODE_SEQ:
|
||||||
|
if(flipSwitch1 == FLIP_SWITCH_UP) {
|
||||||
|
// Rec mode.
|
||||||
|
if(activeNoteCount > 0) {
|
||||||
|
// Find most recent hit key
|
||||||
|
byte note = getMostRecentNote();
|
||||||
|
int value = midiToCV(note + BASE_NOTE);
|
||||||
|
cv[0] = value + mapfloat(currentPitchBend, 0, MIDI_PITCHBEND_MAX, -BEND_RANGE, BEND_RANGE);
|
||||||
|
gate[0] = HIGH;
|
||||||
|
if(trigger) {
|
||||||
|
triggerOut = true;
|
||||||
|
|
||||||
|
EEPROM.update(rotarySwitch, EEPROM[rotarySwitch] + 1);
|
||||||
|
byte notePosition = EEPROM[rotarySwitch] - 1;
|
||||||
|
EEPROM.update(SEQUENCE_COUNT + rotarySwitch * SEQUENCE_LENGTH_MAX + notePosition, note);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
gate[0] = LOW;
|
||||||
|
triggerOut = false;
|
||||||
|
}
|
||||||
|
} else if(flipSwitch1 == FLIP_SWITCH_MIDDLE) {
|
||||||
|
// Play Mode
|
||||||
|
if(activeNoteCount > 0 && EEPROM[rotarySwitch] > 0) {
|
||||||
|
byte note = getMostRecentNote();
|
||||||
|
byte currentSequenceValue = EEPROM[SEQUENCE_COUNT + rotarySwitch * SEQUENCE_LENGTH_MAX + currentSequencePosition];
|
||||||
|
note = currentSequenceValue + note - EEPROM[SEQUENCE_COUNT + rotarySwitch * SEQUENCE_LENGTH_MAX + 0];
|
||||||
|
|
||||||
|
if(trigger && millis() - lastClockMillis > MIDI_CLOCK_THRESHOLD_MILLIS) {
|
||||||
|
// no midi input but a key was pressed, so we start the sequence now instead of at next clock signal
|
||||||
|
midiClockSignal = HIGH;
|
||||||
|
lastManualClockMillis = millis();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(midiClockSignal) { // trigger a new note!
|
||||||
|
triggerOut = true;
|
||||||
|
int value = midiToCV(note + BASE_NOTE);
|
||||||
|
cv[0] = value + mapfloat(currentPitchBend, 0, MIDI_PITCHBEND_MAX, -BEND_RANGE, BEND_RANGE);
|
||||||
|
currentSequencePosition = (currentSequencePosition + 1) % EEPROM[rotarySwitch];
|
||||||
|
}
|
||||||
|
gate[0] = HIGH;
|
||||||
|
} else {
|
||||||
|
gate[0] = LOW;
|
||||||
|
currentSequencePosition = 0;
|
||||||
|
}
|
||||||
|
} else if(flipSwitch1 == FLIP_SWITCH_DOWN) {
|
||||||
|
// clear on note
|
||||||
|
if(activeNoteCount > 0 && trigger) {
|
||||||
|
EEPROM.update(rotarySwitch, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case MODE_ARP:
|
||||||
|
if(flipSwitch1 == ARP_MODE_LATCH) {
|
||||||
|
// ARP Mode LATCH
|
||||||
|
if(trigger) {
|
||||||
|
if(activeNoteCount == 1) {
|
||||||
|
// first note of the new arpeggio pressed
|
||||||
|
currentArpPosition = 0;
|
||||||
|
activeArpNoteCount = 1;
|
||||||
|
for(int i = 0; i < NOTE_RANGE; i++) {
|
||||||
|
if(activeNotes[i] != 0) currentArpPosition = i; // This is the first note of our arpeggio, so set it to this
|
||||||
|
activeArpNotes[i] = activeNotes[i];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// new note for current arpeggio
|
||||||
|
for(int i = 0; i < NOTE_RANGE; i++) {
|
||||||
|
if(activeNotes[i] != 0 && activeArpNotes[i] == 0) activeArpNoteCount++;
|
||||||
|
if(activeNotes[i] != 0) // We don't want to disable any notes, only activate new ones
|
||||||
|
activeArpNotes[i] = activeNotes[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if(flipSwitch1 == ARP_MODE_FORGET) {
|
||||||
|
// activeArpNotes is equivalent to activeNotes. ezpz
|
||||||
|
for(int i = 0; i < NOTE_RANGE; i++) {
|
||||||
|
activeArpNotes[i] = activeNotes[i];
|
||||||
|
}
|
||||||
|
activeArpNoteCount = activeNoteCount;
|
||||||
|
if(released && activeNoteCount == 0) {
|
||||||
|
currentArpPosition = 0;
|
||||||
|
}
|
||||||
|
} else if(flipSwitch1 == ARP_MODE_ADD) {
|
||||||
|
// we just add any active Note until some mysterious signal tells us to reset (modulation > 63!)
|
||||||
|
if(currentModulation < 64) {
|
||||||
|
byte active = 0;
|
||||||
|
for(int i = 0; i < NOTE_RANGE; i++) {
|
||||||
|
if(activeNotes[i] > 0) {
|
||||||
|
activeArpNotes[i] = activeNotes[i];
|
||||||
|
|
||||||
|
}
|
||||||
|
if(activeArpNotes[i] != 0)
|
||||||
|
active++;
|
||||||
|
}
|
||||||
|
activeArpNoteCount = active;
|
||||||
|
} else {
|
||||||
|
activeArpNoteCount = 0;
|
||||||
|
for(int i = 0; i < NOTE_RANGE; i++)
|
||||||
|
activeArpNotes[i] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if(activeArpNoteCount > 0) {
|
||||||
|
|
||||||
|
if( activeArpNotes[currentArpPosition] == 0) {
|
||||||
|
// find the first note in the arp.
|
||||||
|
switch(rotarySwitch) {
|
||||||
|
case ARP_DIRECTION_UP:
|
||||||
|
case ARP_DIRECTION_BOTH:
|
||||||
|
for(int i = 0; i < NOTE_RANGE; i++)
|
||||||
|
if(activeArpNotes[i] != 0) {
|
||||||
|
currentArpPosition = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ARP_DIRECTION_DOWN:
|
||||||
|
for(int i = NOTE_RANGE - 1; i >= 0; i--)
|
||||||
|
if(activeArpNotes[i] != 0) {
|
||||||
|
currentArpPosition = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ARP_DIRECTION_RANDOM:
|
||||||
|
byte newIndex = 0;
|
||||||
|
|
||||||
|
do {
|
||||||
|
newIndex = random(0, 255);
|
||||||
|
} while(activeArpNotes[newIndex] == 0 || currentArpPosition == newIndex);
|
||||||
|
|
||||||
|
currentArpPosition = newIndex;
|
||||||
|
break;
|
||||||
|
case ARP_DIRECTION_ORDER:
|
||||||
|
byte minValue = 0;
|
||||||
|
byte minValueIndex = 0;
|
||||||
|
for(int i = 0; i < NOTE_RANGE; i++) {
|
||||||
|
if(i == currentArpPosition || activeArpNotes[i] == 0) continue;
|
||||||
|
if(activeArpNotes[i] < minValue) {
|
||||||
|
minValue = activeArpNotes[i];
|
||||||
|
minValueIndex = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
currentArpPosition = minValueIndex;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
byte note = currentArpPosition; // This is the current Arp Note to play
|
||||||
|
|
||||||
|
if(midiClockSignal) { // trigger a new note!
|
||||||
|
triggerOut = true;
|
||||||
|
int value = midiToCV(note + BASE_NOTE);
|
||||||
|
cv[0] = value + mapfloat(currentPitchBend, 0, MIDI_PITCHBEND_MAX, -BEND_RANGE, BEND_RANGE);
|
||||||
|
// now we have to find the next arp position. this depends on the current mode.
|
||||||
|
bool foundNote = false;
|
||||||
|
|
||||||
|
switch(rotarySwitch) {
|
||||||
|
case ARP_DIRECTION_UP:
|
||||||
|
if(activeArpNoteCount == 1) break;
|
||||||
|
|
||||||
|
// We just go up and if we reach the last note in the arp, we go back to the first one
|
||||||
|
for(int i = currentArpPosition + 1; i < NOTE_RANGE; i++) {
|
||||||
|
if(activeArpNotes[i] != 0) {
|
||||||
|
// found the next note!
|
||||||
|
currentArpPosition = i;
|
||||||
|
foundNote = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!foundNote) {
|
||||||
|
// We have to find the first note at the beginning of the arpeggio now...
|
||||||
|
for(int i = 0; i < NOTE_RANGE; i++) {
|
||||||
|
if(activeArpNotes[i] != 0) {
|
||||||
|
// found the next note!
|
||||||
|
currentArpPosition = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case ARP_DIRECTION_DOWN:
|
||||||
|
if(activeArpNoteCount == 1) break;
|
||||||
|
// We just go down and if we reach the first note in the arp, we go forward to the last one
|
||||||
|
for(int i = currentArpPosition - 1; i >= 0; i--) {
|
||||||
|
if(activeArpNotes[i] != 0) {
|
||||||
|
// found the next note!
|
||||||
|
currentArpPosition = i;
|
||||||
|
foundNote = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!foundNote) {
|
||||||
|
// We have to find the last note at the beginning of the arpeggio now...
|
||||||
|
for(int i = NOTE_RANGE - 1; i >= 0; i--) {
|
||||||
|
if(activeArpNotes[i] != 0) {
|
||||||
|
// found the next note!
|
||||||
|
currentArpPosition = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ARP_DIRECTION_BOTH:
|
||||||
|
if(activeArpNoteCount == 1) break;
|
||||||
|
// We just go up and if we reach the last note in the arp, we go back one note
|
||||||
|
for(int i = currentArpPosition + 1; i < NOTE_RANGE; i++) {
|
||||||
|
if(activeArpNotes[i] != 0) {
|
||||||
|
// found the next note!
|
||||||
|
currentArpPosition = i;
|
||||||
|
foundNote = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// we are going downwards, so let's search!
|
||||||
|
if(!foundNote) {
|
||||||
|
for(int i = currentArpPosition - 1; i >= 0; i--) {
|
||||||
|
if(activeArpNotes[i] != 0) {
|
||||||
|
// found the next note!
|
||||||
|
currentArpPosition = i;
|
||||||
|
foundNote = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case ARP_DIRECTION_ORDER:
|
||||||
|
if(activeArpNoteCount == 1) break;
|
||||||
|
// We find the note that has the least distance to the current notes value.
|
||||||
|
// If we can't find one, we assign the note with the lowest value
|
||||||
|
byte minValue = 255;
|
||||||
|
byte minValueIndex = 0;
|
||||||
|
for(int i = 0; i < NOTE_RANGE; i++) {
|
||||||
|
if(i == currentArpPosition || activeArpNotes[i] == 0) continue;
|
||||||
|
byte distance = abs(activeArpNotes[i] - activeArpNotes[currentArpPosition]);
|
||||||
|
if(distance < minValue) {
|
||||||
|
minValue = distance;
|
||||||
|
minValueIndex = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(minValue == 255) { // We did not find a higher note, so let's go back to the first one.
|
||||||
|
minValue = 0;
|
||||||
|
for(int i = 0; i < NOTE_RANGE; i++) {
|
||||||
|
if(i == currentArpPosition || activeArpNotes[i] == 0) continue;
|
||||||
|
if(activeArpNotes[i] < minValue) {
|
||||||
|
minValue = activeArpNotes[i];
|
||||||
|
minValueIndex = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
currentArpPosition = minValueIndex;
|
||||||
|
break;
|
||||||
|
case ARP_DIRECTION_RANDOM:
|
||||||
|
byte newIndex = 0;
|
||||||
|
|
||||||
|
do {
|
||||||
|
newIndex = random(0, 255);
|
||||||
|
} while(activeArpNotes[newIndex] == 0 || currentArpPosition == newIndex);
|
||||||
|
|
||||||
|
currentArpPosition = newIndex;
|
||||||
|
|
||||||
|
break;
|
||||||
|
case ARP_DIRECTION_NONE:
|
||||||
|
// Noone knows what happens here.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
gate[0] = HIGH;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
} else {
|
||||||
|
gate[0] = LOW;
|
||||||
|
currentArpPosition = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
gate[1] = triggerOut ? HIGH : LOW;
|
||||||
|
gate[2] = midiStartSignal;
|
||||||
|
gate[3] = midiClockSignal;
|
||||||
|
|
||||||
|
trigger = false;
|
||||||
|
released = false;
|
||||||
|
midiStartSignal = LOW;
|
||||||
|
midiClockSignal = LOW;
|
||||||
|
|
||||||
|
writeDACs();
|
||||||
|
writeGates();
|
||||||
|
}
|
||||||
|
|
||||||
|
float mapfloat(float x, float in_min, float in_max, float out_min, float out_max)
|
||||||
|
{
|
||||||
|
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
|
||||||
|
}
|
||||||
|
|
||||||
|
void readSwitches() {
|
||||||
|
int analogValue = analogRead(PIN_FLIP_SWITCH_0);
|
||||||
|
if(analogValue < 100) flipSwitch0 = FLIP_SWITCH_UP;
|
||||||
|
else if(analogValue < 900) flipSwitch0 = FLIP_SWITCH_DOWN;
|
||||||
|
else flipSwitch0 = FLIP_SWITCH_MIDDLE;
|
||||||
|
analogValue = analogRead(PIN_FLIP_SWITCH_1);
|
||||||
|
if(analogValue < 100) flipSwitch1 = FLIP_SWITCH_UP;
|
||||||
|
else if(analogValue < 900) flipSwitch1 = FLIP_SWITCH_DOWN;
|
||||||
|
else flipSwitch1 = FLIP_SWITCH_MIDDLE;
|
||||||
|
|
||||||
|
if(digitalRead(ROTARY_SWITCH_PIN0) == LOW) rotarySwitch = 0;
|
||||||
|
else if(digitalRead(ROTARY_SWITCH_PIN1) == LOW) rotarySwitch = 1;
|
||||||
|
else if(digitalRead(ROTARY_SWITCH_PIN2) == LOW) rotarySwitch = 2;
|
||||||
|
else if(digitalRead(ROTARY_SWITCH_PIN3) == LOW) rotarySwitch = 3;
|
||||||
|
else if(digitalRead(ROTARY_SWITCH_PIN4) == LOW) rotarySwitch = 4;
|
||||||
|
else if(digitalRead(ROTARY_SWITCH_PIN5) == LOW) rotarySwitch = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
void readSpeed() {
|
||||||
|
speedValue = analogRead(PIN_SPEED);
|
||||||
|
}
|
||||||
|
|
||||||
|
void writeDACs() {
|
||||||
|
dac.analogWrite(map(cv[0], 0, MAX_CV, 0, 4095), map(cv[1], 0, MAX_CV, 0, 4095), map(cv[2], 0, MAX_CV, 0, 4095), map(cv[3], 0, MAX_CV, 0, 4095));
|
||||||
|
}
|
||||||
|
|
||||||
|
void writeGates() {
|
||||||
|
digitalWrite(PIN_GATE_0, gate[0] == HIGH ? LOW : HIGH);
|
||||||
|
digitalWrite(PIN_GATE_1, gate[1] == HIGH ? LOW : HIGH);
|
||||||
|
digitalWrite(PIN_GATE_2, gate[2] == HIGH ? LOW : HIGH);
|
||||||
|
digitalWrite(PIN_GATE_3, gate[3] == HIGH ? LOW : HIGH);
|
||||||
|
}
|
Loading…
Reference in a new issue