Skip to content
Permalink
81ec22619c
Go to file
 
 
Cannot retrieve contributors at this time
652 lines (515 sloc) 14 KB
/*
* TELEXo Eurorack Module
* (c) 2016, 2017 Brendon Cassidy
* MIT License
*/
// debug flag turns on serial debugging over USB
// this can drastically affect performance of the DAC
// depending on where you output to serial - so take care when using it
// #define DEBUG 1
// i2c Wire Library (for Teensy)
#include <i2c_t3.h>
// DAC stuff
#include <SPI.h>
#include "DAC7565.h"
// support libraries
#include "telex.h"
#include "TriggerOutput.h"
#include "CVOutput.h"
#include "TxHelper.h"
// logging values
#define WRITERATE 50.
#define LEDINTERVAL 100
#define LOGINTERVAL 10000
// sampling rate and LED rate values
#define SAMPLINGRATE 15625
#define LEDRATE 50
/*
* Ugly Globals
*/
// i2c pullup setting
bool enablePullups = false;
// config inputs
int configPins[] = { 15, 16, 17 };
int configID = TO;
// write timer and its local variables
IntervalTimer writeTimer;
int readerTemp = 0;
int p = 0;
// CV Outputs
DAC dac(-1, 10, -1, 11, 13);
int dacOutputs[] = { DAC_CHANNEL_D, DAC_CHANNEL_C, DAC_CHANNEL_B, DAC_CHANNEL_A };
int pwmLedPins[] = { 3,4,5,6 };
CVOutput *cvOutputs[4];
int writeRate = 100;
// Trigger Outputs
int trLedPins[] = { 0,1,2,7 };
int trPins[] = { 23, 22, 21, 20 };
TriggerOutput *triggerOutputs[4];
// target output
int targetOutput = 0;
// iterator values
int i = 0;
int q = 0;
// timing loop
unsigned long currentTime;
unsigned long nextTime;
unsigned long dacTime;
unsigned long kTime;
#ifdef DEBUG
// led status for tx/rx
int LED = 13;
unsigned long ledInterval;
bool ledOn = false;
// clocking variables
volatile unsigned long n = 0;
unsigned long l = 0;
unsigned long t = 0;
float persec = 0;
#endif
bool dacOn = true;
/*
* Setup Function
*/
void setup() {
delay(1);
// config
int cfg = 0;
for (i=0; i < 3; i++){
pinMode(configPins[i], INPUT);
delay(1);
cfg += digitalRead(configPins[i]) << i;
}
configID += cfg;
writeRate = 1000000 / SAMPLINGRATE;
#ifdef DEBUG
// set the behind-the-scenes LED output pin
pinMode(LED, OUTPUT);
// turn on serial
Serial.begin(9600);
// wait for debugging connection
while (!Serial);
// kick off the logging
t = millis() + LOGINTERVAL;
Serial.printf("ConfigID: %d\n", configID);
#endif
// initialize the DAC
if (dacOn) {
dac.init();
dac.setReference(DAC_REFERENCE_ALWAYS_POWERED_UP);
dac.writeChannel(DAC_CHANNEL_ALL, DAC_MAX_SCALE / 2);
}
// initialize the outputs
for (i=0; i < 4; i++) {
// set up the trigger and cv outputs
triggerOutputs[i] = new TriggerOutput(trPins[i], trLedPins[i]);
cvOutputs[i] = new CVOutput(dacOutputs[i], pwmLedPins[i], dac, SAMPLINGRATE);
cvOutputs[i]->ReferenceTriggers(triggerOutputs, sizeof(triggerOutputs));
}
// start the write timer
writeTimer.begin(writeOutputs, writeRate);
kTime = millis() + LEDRATE;
// initialize the teensy optimized wire library
Wire.begin(I2C_SLAVE, configID, I2C_PINS_18_19, enablePullups ? I2C_PULLUP_INT : I2C_PULLUP_EXT, I2C_RATE_400); // I2C_RATE_2400
Wire.onReceive(receiveEvent);
}
/*
* Main Arduino Appliation Loop
*/
void loop() {
currentTime = millis();
// update the TRIGGERS
for (i=0; i< 4; i++){
// update the triggers
triggerOutputs[i]->Update(currentTime);
}
// update the CV LEDs
if (currentTime >= kTime){
for (i = 0; i < 4; i++)
cvOutputs[i]->UpdateLED();
kTime = currentTime + LEDRATE;
}
#ifdef DEBUG
// flash the back LED and log the timing
if (currentTime >= t){
noInterrupts();
l = n;
n = 0;
interrupts();
t = currentTime + LOGINTERVAL;
persec = l / 10.;
Serial.printf("per sec: %f\n", persec);
}
// turn off activity LED
if (ledOn && currentTime > ledInterval) {
ledOn = false;
ledInterval = 0;
}
#endif
}
/*
* Call the Update Function for the Outputs (removed FASTRUN)
*/
void writeOutputs() {
#ifdef DEBUG
// counts the ops/sec
n++;
#endif
// iterate through the values
for (p=0; p< 4; p++){
// update the cv
cvOutputs[p]->Update();
}
}
/*
* Wire Callback
*/
void receiveEvent(size_t len) {
#ifdef DEBUG
// set the back LED active to indicate data transfer
Serial.printf("Event received of size %d \n", len);
digitalWrite(LED, HIGH);
ledOn = true;
ledInterval = millis() + LEDINTERVAL;
#endif
// parse the response
TxResponse response = TxHelper::Parse(len);
// act on the command
actOnCommand(response.Command, response.Output, response.Value);
}
/*
* Act on the Commands Delivered to the TXo
*/
void actOnCommand(byte cmd, byte out, int value){
// zero-adjust the output number
targetOutput = out;
if (targetOutput < 0) return;
#ifdef DEBUG
Serial.printf("Action: %d, Output: %d, Value: %d\n", cmd, targetOutput, value);
#endif
unsigned long ms = millis();
// noInterrupts();
switch(cmd) {
case TO_CV_SET:
// set the value directly - no slew
cvOutputs[targetOutput]->SetValue(value << 1);
break;
case TO_CV:
// set the target value and slew to it
cvOutputs[targetOutput]->TargetValue(value << 1);
break;
case TO_CV_SLEW:
// set the slew value
cvOutputs[targetOutput]->SetSlew(value, 0);
break;
case TO_CV_SLEW_S:
// set the slew value
cvOutputs[targetOutput]->SetSlew(value, 1);
break;
case TO_CV_SLEW_M:
// set the slew value
cvOutputs[targetOutput]->SetSlew(value, 2);
break;
case TO_CV_OFF:
// set the offset
cvOutputs[targetOutput]->SetOffset(value << 1);
break;
case TO_CV_QT:
// Set Pulse Time Format Trigger
cvOutputs[targetOutput]->TargetQuantizedValue(value);
break;
case TO_CV_QT_SET:
// Set Pulse Time Format Trigger
cvOutputs[targetOutput]->SetQuantizedValue(value);
break;
case TO_CV_N:
// Set Pulse Time Format Trigger
cvOutputs[targetOutput]->TargetNote(value);
break;
case TO_CV_N_SET:
// Set Pulse Time Format Trigger
cvOutputs[targetOutput]->SetNote(value);
break;
case TO_CV_SCALE:
// Set Pulse Time Format Trigger
cvOutputs[targetOutput]->SetQuantizationScale(value);
break;
case TO_CV_LOG:
// Enable Logarithmic Transformationn
cvOutputs[targetOutput]->SetLog(value);
break;
case TO_OSC:
cvOutputs[targetOutput]->TargetVOct(value);
break;
case TO_OSC_SET:
cvOutputs[targetOutput]->SetVOct(value);
break;
case TO_OSC_QT:
cvOutputs[targetOutput]->TargetQuantizedVOct(value);
break;
case TO_OSC_QT_SET:
cvOutputs[targetOutput]->SetQuantizedVOct(value);
break;
case TO_OSC_FQ:
//
cvOutputs[targetOutput]->TargetFrequency(value);
break;
case TO_OSC_FQ_SET:
//
cvOutputs[targetOutput]->SetFrequency(value);
break;
case TO_OSC_N:
cvOutputs[targetOutput]->TargetOscNote(value);
break;
case TO_OSC_N_SET:
cvOutputs[targetOutput]->SetOscNote(value);
break;
case TO_OSC_LFO:
//
cvOutputs[targetOutput]->TargetLFO(value);
break;
case TO_OSC_LFO_SET:
//
cvOutputs[targetOutput]->SetLFO(value);
break;
case TO_OSC_SYNC:
//
cvOutputs[targetOutput]->Sync();
break;
case TO_OSC_PHASE:
//
cvOutputs[targetOutput]->SetPhaseOffset(value);
break;
case TO_OSC_WAVE:
//
cvOutputs[targetOutput]->SetWaveform(value);
break;
case TO_OSC_WIDTH:
//
cvOutputs[targetOutput]->SetWidth(value);
break;
case TO_OSC_RECT:
//
cvOutputs[targetOutput]->SetRectify(value);
break;
case TO_OSC_SCALE:
//
cvOutputs[targetOutput]->SetOscQuantizationScale(value);
break;
case TO_OSC_SLEW:
//
cvOutputs[targetOutput]->SetFrequencySlew(value, 0);
break;
case TO_OSC_SLEW_S:
//
cvOutputs[targetOutput]->SetFrequencySlew(value, 1);
break;
case TO_OSC_SLEW_M:
//
cvOutputs[targetOutput]->SetFrequencySlew(value, 2);
break;
case TO_OSC_CYC:
//
cvOutputs[targetOutput]->TargetCycle(value, 0);
break;
case TO_OSC_CYC_S:
//
cvOutputs[targetOutput]->TargetCycle(value, 1);
break;
case TO_OSC_CYC_M:
//
cvOutputs[targetOutput]->TargetCycle(value, 2);
break;
case TO_OSC_CYC_SET:
//
cvOutputs[targetOutput]->SetCycle(value, 0);
break;
case TO_OSC_CYC_S_SET:
//
cvOutputs[targetOutput]->SetCycle(value, 1);
break;
case TO_OSC_CYC_M_SET:
//
cvOutputs[targetOutput]->SetCycle(value, 2);
break;
case TO_OSC_CTR:
//
cvOutputs[targetOutput]->SetCenter(value << 1);
break;
case TO_ENV_ACT:
//
cvOutputs[targetOutput]->SetEnvelopeMode(value);
break;
case TO_ENV_ATT:
//
cvOutputs[targetOutput]->SetAttack(value, 0);
break;
case TO_ENV_ATT_S:
//
cvOutputs[targetOutput]->SetAttack(value, 1);
break;
case TO_ENV_ATT_M:
//
cvOutputs[targetOutput]->SetAttack(value, 2);
break;
case TO_ENV_DEC:
//
cvOutputs[targetOutput]->SetDecay(value, 0);
break;
case TO_ENV_DEC_S:
//
cvOutputs[targetOutput]->SetDecay(value, 1);
break;
case TO_ENV_DEC_M:
//
cvOutputs[targetOutput]->SetDecay(value, 2);
break;
case TO_ENV_TRIG:
//
cvOutputs[targetOutput]->TriggerEnvelope();
break;
case TO_ENV_EOR:
//
cvOutputs[targetOutput]->SetEOR(value - 1);
break;
case TO_ENV_EOC:
//
cvOutputs[targetOutput]->SetEOC(value - 1);
break;
case TO_ENV_LOOP:
//
cvOutputs[targetOutput]->SetLoop(value);
break;
case TO_TR:
// Set Trigger Value
triggerOutputs[targetOutput]->SetState(value > 0);
break;
case TO_TR_TOG:
// Toggle Trigger State
triggerOutputs[targetOutput]->ToggleState();
break;
case TO_TR_TIME:
// Set Pulse Time for Trigger
triggerOutputs[targetOutput]->SetTime(value, 0);
break;
case TO_TR_TIME_S:
// Set Pulse Time for Trigger
triggerOutputs[targetOutput]->SetTime(value, 1);
break;
case TO_TR_TIME_M:
// Set Pulse Time for Trigger
triggerOutputs[targetOutput]->SetTime(value, 2);
break;
case TO_TR_PULSE:
// Pulse the Trigger
triggerOutputs[targetOutput]->Pulse();
break;
case TO_TR_POL:
// Set the Trigger's Polarity
triggerOutputs[targetOutput]->SetPolarity(value != 0);
break;
case TO_TR_PULSE_DIV:
// Set Clock Divider
triggerOutputs[targetOutput]->SetDivision(value);
break;
case TO_TR_M_MUL:
// Set Clock Divider
triggerOutputs[targetOutput]->SetMultiplier(value);
break;
case TO_TR_M_ACT:
// Set Clock Divider
triggerOutputs[targetOutput]->SetMetro(value, ms);
break;
case TO_TR_M:
// Set Pulse Time for TR Metro
triggerOutputs[targetOutput]->SetMetroTime(value, 0);
break;
case TO_TR_M_S:
// Set Pulse Time for TR Metro
triggerOutputs[targetOutput]->SetMetroTime(value, 1);
break;
case TO_TR_M_M:
// Set Pulse Time for TR Metro
triggerOutputs[targetOutput]->SetMetroTime(value, 2);
break;
case TO_TR_M_BPM:
// Set Pulse Time for TR Metro
triggerOutputs[targetOutput]->SetMetroTime(value, 3);
break;
case TO_TR_M_SYNC:
// Sync Pulse Time for TR Metro
triggerOutputs[targetOutput]->Sync();
break;
case TO_M_ACT:
// Set Clock Divider
for (int w = 0; w < 4; w++)
triggerOutputs[w]->SetMetro(value, ms);
break;
case TO_M:
// Set Pulse Time for TR Metro
for (int w = 0; w < 4; w++)
triggerOutputs[w]->SetMetroTime(value, 0);
break;
case TO_M_S:
// Set Pulse Time for TR Metro
for (int w = 0; w < 4; w++)
triggerOutputs[w]->SetMetroTime(value, 1);
break;
case TO_M_M:
// Set Pulse Time for TR Metro
for (int w = 0; w < 4; w++)
triggerOutputs[w]->SetMetroTime(value, 2);
break;
case TO_M_BPM:
// Set Pulse Time for TR Metro
for (int w = 0; w < 4; w++)
triggerOutputs[w]->SetMetroTime(value, 3);
break;
case TO_M_COUNT:
// Set Count for M Repeats
for (int w = 0; w < 4; w++)
triggerOutputs[w]->SetMetroCount(value);
break;
case TO_M_SYNC:
// Sync Pulse Time for TR Metro
for (int w = 0; w < 4; w++)
triggerOutputs[w]->Sync(ms);
break;
case TO_TR_WIDTH:
// Set Pulse Time for TR Metro
triggerOutputs[targetOutput]->SetWidth(value);
break;
case TO_TR_M_COUNT:
// Set Count for M Repeats
triggerOutputs[targetOutput]->SetMetroCount(value);
break;
case TO_TR_PULSE_MUTE:
// Mute/Unmute the appropriate output
triggerOutputs[targetOutput]->SetMute(value == 1);
break;
case TO_KILL:
for(int w=0; w<4; w++){
// Kill Each Trigger
triggerOutputs[w]->Kill();
// stop all slewwing
cvOutputs[w]->Kill();
}
break;
case TO_TR_INIT:
// initialize the TR Output
triggerOutputs[targetOutput]->Reset();
break;
case TO_CV_INIT:
// initialize the CV Output
cvOutputs[targetOutput]->Reset();
break;
case TO_INIT:
// initialize all TR and CV Outputs
for(int w=0; w<4; w++){
triggerOutputs[w]->Reset();
cvOutputs[w]->Reset();
}
break;
}
// interrupts();
}