#include <Audio.h>
#include <MIDI.h>
#include <SD.h>
#include <SPI.h>
#include <SerialFlash.h>
#include <Wire.h>
#include "ext_envelope.h"
#include "observer.h"
// GUItool: begin automatically generated code
AudioSynthWaveformDc dc4; // xy=627,242
AudioSynthWaveformDc dc3; // xy=628,181
AudioSynthWaveformDc dc1; // xy=633,62
AudioSynthWaveformDc dc2; // xy=633,126
AudioSynthWaveformDc dc6; // xy=632,398
AudioSynthWaveformDc dc5; // xy=634,310
AudioExternalEnvelope envelope4; // xy=870,261
AudioExternalEnvelope envelope6; // xy=871,393
AudioExternalEnvelope envelope3; // xy=872,191
AudioExternalEnvelope envelope5; // xy=872,325
AudioExternalEnvelope envelope2; // xy=878,132
AudioExternalEnvelope envelope1; // xy=879,70
AudioMixer4 mixer1; // xy=1049,116
AudioMixer4 mixer2; // xy=1052,315
AudioSynthNoiseWhite noise1; // xy=1063,424
AudioMixer4 mixer3; // xy=1203,248
AudioOutputAnalog pwm1; // xy=1370,264
AudioConnection patchCord1(dc4, envelope4);
AudioConnection patchCord2(dc3, envelope3);
AudioConnection patchCord3(dc1, envelope1);
AudioConnection patchCord4(dc2, envelope2);
AudioConnection patchCord5(dc6, envelope6);
AudioConnection patchCord6(dc5, envelope5);
AudioConnection patchCord7(envelope4, 0, mixer1, 3);
AudioConnection patchCord8(envelope6, 0, mixer2, 1);
AudioConnection patchCord9(envelope3, 0, mixer1, 2);
AudioConnection patchCord10(envelope5, 0, mixer2, 0);
AudioConnection patchCord11(envelope2, 0, mixer1, 1);
AudioConnection patchCord12(envelope1, 0, mixer1, 0);
AudioConnection patchCord13(mixer1, 0, mixer3, 0);
AudioConnection patchCord14(mixer2, 0, mixer3, 1);
AudioConnection patchCord15(noise1, 0, mixer3, 3);
AudioConnection patchCord16(mixer3, pwm1);
// GUItool: end automatically generated code
MIDI_CREATE_INSTANCE(HardwareSerial, Serial1, MIDI);
MIDI_CREATE_INSTANCE(HardwareSerial, Serial3, MIDI3);
const int numLeds = 6;
int pwmLeds[numLeds] = {16, 6, 3, 10, 17, 20};
int triggerPins[numLeds] = {13, 2, 8, 12, 14, 19};
AudioExternalEnvelope* envelopes[numLeds] = {
&envelope1, &envelope2, &envelope3, &envelope4, &envelope5, &envelope6};
AudioExternalEnvelope* envelope = NULL;
bool CLOCK_RUNNING = false;
int delayOneShot = 10;
// fixme these are fake
unsigned long futures[] = {0, 0, 0, 0, 0, 0};
const int baseline = 600;
int RECEIVE_CHANNEL = 10;
const int MAX_PINS = 24;
float pitches[MAX_PINS];
float envelopeAmounts[MAX_PINS];
int envelopeValues[MAX_PINS];
int currentPinValues[MAX_PINS];
unsigned long ttimer = 0;
// minimum refactor
// snare should trigger tom
// low syn
// tom //snare wb // rim
// high
void onEnvelopeChange(int refId, int value) {
// Serial.println("onEnvelopeChange " + String(refId) + ", " +
// String(triggerPins[refId]) + ", " + String(pwmLeds[refId]) + ", " +
// String(value)); analogWrite(pwmLeds[refId], 100 + (value));
envelopeValues[refId] = value;
updatePitch(refId, false);
}
void setup() {
Serial.begin(115200);
Serial2.begin(19200);
while (!Serial && millis() < 3000)
;
AudioMemory(6);
analogWriteResolution(12);
setupMidi();
setupModulation();
noise1.amplitude(0.4);
dc1.amplitude(1.0);
dc2.amplitude(1.0);
dc3.amplitude(1.0);
dc4.amplitude(1.0);
dc5.amplitude(1.0);
dc6.amplitude(1.0);
mixer1.gain(0, 1.0);
mixer1.gain(1, 1.0);
mixer1.gain(2, 1.0);
mixer1.gain(3, 1.0);
mixer2.gain(0, 1.0);
mixer2.gain(1, 1.0);
mixer2.gain(2, 1.0);
mixer2.gain(3, 1.0);
mixer3.gain(0, 0.0);
mixer3.gain(1, 0.0);
mixer3.gain(2, 0.0);
mixer3.gain(3, 1.0);
int pwmFreq = 600000; // 600000
for (int i = 0; i < numLeds; i++) {
pinMode(pwmLeds[i], OUTPUT);
pinMode(triggerPins[i], OUTPUT);
// analogWriteFrequency(pwmLeds[i], pwmFreq);
}
// pinMode(2, INPUT_PULLUP);
// pushbuttonTrigger.attach(2);
// pushbuttonTrigger.interval(5);
digitalWrite(13, HIGH);
Serial.println("OK");
// put your setup code here, to run once:
// envelope1.setObserver(this);
setupEnvelopes();
Serial.println("envelopes OK");
delay(10);
// analogWrite(20, 2175);
digitalWrite(13, LOW);
for (int i = 0; i < numLeds; i++) {
updatePitch(pwmLeds[i], true);
}
Serial.println("DONE");
// parseControlCommand("cc:1,7,43");
}
void parseControlCommand(String input) {
int where = input.indexOf("cc:");
String temp;
byte ch;
byte cc;
byte val;
String remain;
if (where != -1) {
where = input.indexOf(":");
// everything after :
temp = input.substring(where + 1);
// Serial.println("A: " + temp);
where = temp.indexOf(",");
// from start to ,
remain = temp.substring(where + 1);
// Serial.println("remain: " + remain);
temp = temp.substring(0, where);
// Serial.println("B: " + temp);
ch = temp.toInt();
// Serial.println("channel: " + String(ch));
where = remain.indexOf(",");
temp = remain.substring(0, where);
remain = remain.substring(where + 1);
// Serial.println("remain: " + remain);
cc = temp.toInt();
// Serial.println("cc: " + String(cc));
where = remain.indexOf(",");
// from start to ,
temp = remain.substring(0, where);
// Serial.println("D: " + temp);
val = temp.toInt();
// Serial.println("value: " + String(val));
handleControlChange(ch, cc, val);
}
}
String buffer = "";
String checkSerial() {
String payload = "";
if (Serial2.available()) {
int inByte = Serial2.read();
buffer = buffer + String((char)inByte);
if (inByte == 13) {
payload = buffer.trim();
// Serial1.println("ACK:" + buffer);
buffer = "";
}
}
return payload;
}
void setupModulation() {
for (int i = 0; i < MAX_PINS; i++) {
pitches[i] = 0.2f;
envelopeAmounts[i] = 0.0f;
envelopeValues[i] = 0;
currentPinValues[i] = 0;
}
}
void setupEnvelopes() {
for (int i = 0; i < numLeds; i++) {
envelope = envelopes[i];
envelope->setRefId(pwmLeds[i]);
envelope->setCallback(onEnvelopeChange);
envelope->sustain(0);
envelope->decay(200);
envelope->release(200);
// analogWrite(pwmLeds[i], baseline);
}
// delay(10);
// for (int i = 0; i < numLeds; i++) {
// envelope = envelopes[i];
// envelope->noteOn();
// }
}
void handleNoteOnMidi(byte channel, byte pitch, byte velocity) {
// digitalWrite(13, HIGH);
Serial.println(String(pitch));
// 36
// 35
// 38
// 40
// 42
// 46
// 50 l
// 51
// 47 m
// 49
// 43 h
// 60 x 49
// 61
if (pitch < 37) {
// sample_player_1.playBuffered(velocity); // kick
triggerOneShot(0);
} else if (pitch == 50) { // High Tom
triggerOneShot(2);
} else if (pitch == 38 || pitch == 40) { // Low-Mid Tom // will go to SD
triggerOneShot(4);
} else if (pitch == 43) { // High Floor Tom
triggerOneShot(5);
} else if (pitch == 47) { // Crash Cymbal 1
triggerOneShot(1);
} else if (pitch == 61) { // Low Bongo
triggerOneShot(3);
}
}
// 0 2 4 1 5 3
void handleNoteOffMidi(byte channel, byte pitch, byte velocity) {
// digitalWrite(13, LOW);
// if (pitch < 37) {
// // sample_player_1.playBuffered(velocity); // kick
// stopOneShot(0);
// } else if (pitch == 50) {
// stopOneShot(2);
// } else if (pitch == 47) {
// stopOneShot(4);
// } else if (pitch == 43) {
// stopOneShot(1);
// } else if (pitch == 61 || pitch == 49) {
// stopOneShot(5);
// } else if (pitch == 60 || pitch == 37) {
// stopOneShot(3);
// }
}
void onClockStart() {
CLOCK_RUNNING = true;
// for (int i = 0; i < numLeds; i++) {
// updatePitch(pwmLeds[i], true);
// }
}
void onClockStop() {
CLOCK_RUNNING = false;
// for (int i = 0; i < numLeds; i++) {
// analogWrite(pwmLeds[i], 0);
// }
// for (int i = 0; i < numLeds; i++) {
// updatePitch(pwmLeds[i], true);
// }
}
void setPitch(int pin, int value) {
float ratio = value / 4064.0;
pitches[pin] = ratio;
updatePitch(pin, false);
}
void setEnvelope(int pin, int value) {
// float ratio = value / 4064.0;
float ratio = value / 4064.0;
envelopeAmounts[pin] = ratio;
updatePitch(pin, false);
}
int updatePitch(int pin, bool force) {
// Multiply pitch by 4096
// Multiply envelope * envelope amplitude
if (force) {
currentPinValues[pin] = -1;
}
float pitchSetting = pitches[pin];
float envelopeSetting = envelopeAmounts[pin];
int envelopeValue = envelopeValues[pin];
int value = (pitchSetting * 4096) + (envelopeSetting * envelopeValue);
if (value > 4096) {
value = 4096;
}
// Serial.println("updatePitch " + String(pin) + ", " + String(value));
if (currentPinValues[pin] != value) {
// if (value > 0) {
// Serial.println("updatePitch " + String(pin) + ", " + String(value));
// }
currentPinValues[pin] = value;
analogWrite(pin, value);
}
}
int controlMode = 0;
void handleControlChange(unsigned char channel, unsigned char controlNumber,
unsigned char controlValue) {
if (controlNumber == 71) {
if (controlValue < 32) {
controlMode = 0;
} else if (controlValue < 64) {
controlMode = 1;
} else if (controlValue < 96) {
controlMode = 2;
} else if (controlValue < 128) {
controlMode = 3;
}
}
if (controlMode == 0) {
// 0 2 4 1 5 3
switch (controlNumber) {
case 31:
setPitch(pwmLeds[0], controlValue * 32);
break;
case 32:
setPitch(pwmLeds[2], controlValue * 32);
break;
case 33:
setPitch(pwmLeds[4], controlValue * 32);
break;
case 34:
setPitch(pwmLeds[1], controlValue * 32);
break;
case 35:
setPitch(pwmLeds[5], controlValue * 32);
break;
case 36:
setPitch(pwmLeds[3], controlValue * 32);
break;
default:
break;
}
} else if (controlMode == 1) {
// 0 2 4 1 5 3
switch (controlNumber) {
case 31:
setEnvelope(pwmLeds[0], controlValue * 32);
break;
case 32:
setEnvelope(pwmLeds[2], controlValue * 32);
break;
case 33:
setEnvelope(pwmLeds[4], controlValue * 32);
break;
case 34:
setEnvelope(pwmLeds[1], controlValue * 32);
break;
case 35:
setEnvelope(pwmLeds[5], controlValue * 32);
break;
case 36:
setEnvelope(pwmLeds[3], controlValue * 32);
break;
default:
break;
}
} else if (controlMode == 2) {
// 0 2 4 1 5 3
float decay = (controlValue * 16);
switch (controlNumber) {
case 31:
envelope1.decay(decay);
envelope1.release(decay);
break;
case 32:
envelope3.decay(decay);
envelope3.release(decay);
break;
case 33:
envelope5.decay(decay);
envelope5.release(decay);
break;
case 34:
envelope2.decay(decay);
envelope2.release(decay);
break;
case 35:
envelope6.decay(decay);
envelope6.release(decay);
break;
case 36:
envelope4.decay(decay);
envelope4.release(decay);
break;
default:
break;
}
} else if (controlMode == 3) {
RECEIVE_CHANNEL = controlValue / 8;
}
}
void handlePitchBend(byte channel, int pitch) {}
void setupMidi() {
Serial.println("setupMidi");
// use MIDI_CHANNEL_OMNI for testing
MIDI.begin(
RECEIVE_CHANNEL); // Launch MIDI, by default listening to channel 10.
// Connect MIDI status changes involving a channel to handlers
MIDI.setHandleNoteOn(handleNoteOnMidi);
MIDI.setHandleNoteOff(handleNoteOffMidi);
MIDI.setHandleControlChange(handleControlChange);
// MIDI.setHandleProgramChange(handleProgramChange);
MIDI.setHandlePitchBend(handlePitchBend);
//
// MIDI.setHandleClock(myClock);
MIDI.setHandleStart(onClockStart);
MIDI.setHandleContinue(onClockStart);
MIDI.setHandleStop(onClockStop);
MIDI3.begin(
MIDI_CHANNEL_OMNI); // Launch MIDI, by default listening to channel 10.
// Connect MIDI status changes involving a channel to handlers
MIDI3.setHandleNoteOn(handleNoteOnMidi);
MIDI3.setHandleNoteOff(handleNoteOffMidi);
MIDI3.setHandleControlChange(handleControlChange);
// MIDI.setHandleProgramChange(handleProgramChange);
MIDI3.setHandlePitchBend(handlePitchBend);
//
// MIDI.setHandleClock(myClock);
MIDI3.setHandleStart(onClockStart);
MIDI3.setHandleContinue(onClockStart);
MIDI3.setHandleStop(onClockStop);
Serial.println("setupMidi DONE");
}
void triggerOneShot(int which) {
// for (int i = 0; i < numLeds; i++) {
// envelope = envelopes[i];
// envelope->noteOn();
// }
envelopes[which]->noteOn();
Serial.println("triggerOneShot " + String(which) + ", " +
String(triggerPins[which]));
digitalWrite(triggerPins[which], HIGH);
futures[which] = millis() + delayOneShot;
}
void stopOneShot(int which) {
envelopes[which]->noteOff();
Serial.println("stopOneShot " + String(which) + ", " +
String(triggerPins[which]));
digitalWrite(triggerPins[which], LOW);
futures[which] = 0;
}
void loop() {
MIDI.read();
MIDI3.read();
if (!CLOCK_RUNNING) {
// checkSerial is not for time-critical loops
String temp = checkSerial();
if (temp.length() > 0) {
Serial.println(temp);
parseControlCommand(temp);
}
}
for (int i = 0; i < numLeds; i++) {
if (futures[i] > 0) {
if (millis() >= futures[i]) {
stopOneShot(i);
}
}
}
if (ttimer == 0) {
ttimer = millis() + 250;
}
}