|
| 1 | +// ADSRduino |
| 2 | +// |
| 3 | +// a simple ADSR for the Arduino |
| 4 | +// m0xpd |
| 5 | +// Feb 2017 |
| 6 | +// |
| 7 | +// Modified by katspaugh in 2018 to use the Wire library |
| 8 | +// |
| 9 | +// see http://m0xpd.blogspot.co.uk/2017/02/signal-processing-on-arduino.html |
| 10 | +// |
| 11 | +// Uses a DAC microchip to produce CV |
| 12 | +// receives gate inputs on digital pin 2 (remember to protect e.g. with a 200R resistor and a 5V1 Zener diode) |
| 13 | +// and a loop mode input on digital pin 3 (pulling to 0V selects loop mode) |
| 14 | +// |
| 15 | +// Voltages between 0 and 5V (e.g. from potentiometers) on analog pins A0:A3 control Attack, Decay, Sustain & Release |
| 16 | + |
| 17 | +#include <Wire.h> |
| 18 | + |
| 19 | +// Pin definitions... |
| 20 | +const int gatePin = 2; |
| 21 | + |
| 22 | +// Analog pins for the potentiometers |
| 23 | +const int pot0 = 6; |
| 24 | +const int pot1 = 0; |
| 25 | +const int pot2 = 7; |
| 26 | +const int pot3 = 1; |
| 27 | + |
| 28 | +// Pins A4 (SDA), A5 (SCL) are reserved for the DAC |
| 29 | +const int DAC_vin = A3; // A2 |
| 30 | +const int DAC_ground = A2; // A3 |
| 31 | +const byte DAC_address = 0x60; // The I2C address can vary |
| 32 | + |
| 33 | +float alpha = 0.7; // this is the pole location ('time constant') used for the first-order difference equation |
| 34 | +double alpha1 = 0.9; // initial value for attack |
| 35 | +double alpha2 = 0.9; // initial value for decay |
| 36 | +double alpha3 = 0.95; // initial value for release |
| 37 | + |
| 38 | +float envelope = 0.0; // initialise the envelope |
| 39 | +float CV0 = 0.0; // result of reads from potentiometers (yes - it will only be an int, but helps with the casting!) |
| 40 | +float CV1 = 0.0; |
| 41 | +int CV2 = 0; |
| 42 | +float CV3 = 0.0; |
| 43 | + |
| 44 | +int drive = 0; |
| 45 | +int sustain_Level = 0; |
| 46 | +int scan = 0; |
| 47 | +boolean note_active = false; |
| 48 | +boolean trigger = false; |
| 49 | +boolean decay = false; |
| 50 | +boolean release_done = true; |
| 51 | + |
| 52 | + |
| 53 | +void writeToDAC(int DC_Value) { |
| 54 | + Wire.beginTransmission(DAC_address); |
| 55 | + Wire.write(byte((DC_Value & 0x0f00) >> 8)); |
| 56 | + Wire.write(byte(DC_Value & 0xff)); |
| 57 | + Wire.endTransmission(); |
| 58 | +} |
| 59 | + |
| 60 | +void setup() { |
| 61 | + pinMode(gatePin, INPUT); |
| 62 | + |
| 63 | + // Power up the DAC |
| 64 | + pinMode(DAC_vin, OUTPUT); |
| 65 | + pinMode(DAC_ground, OUTPUT); |
| 66 | + digitalWrite(DAC_vin, HIGH); |
| 67 | + digitalWrite(DAC_ground, LOW); |
| 68 | + |
| 69 | + Wire.begin(); |
| 70 | + |
| 71 | + Serial.begin(9600); |
| 72 | +} |
| 73 | + |
| 74 | +void loop() { |
| 75 | + boolean gate = digitalRead(gatePin); // read the gate input every time through the loop |
| 76 | + Serial.println(gate); |
| 77 | + update_params(scan); // scan only one of the other inputs each pass |
| 78 | + |
| 79 | + boolean trigger = gate; // trigger an ADSR even if there's a gate OR if we're in loop mode |
| 80 | + while (trigger) { |
| 81 | + if (note_active == false) { // if a note isn't active and we're triggered, then start one! |
| 82 | + decay = false; |
| 83 | + drive = 4096; // drive toward full value |
| 84 | + alpha = alpha1; // set 'time constant' alpha1 for attack phase |
| 85 | + note_active = true; // set the note_active flag |
| 86 | + } |
| 87 | + if ((decay == false) && (envelope > 4000) && (drive == 4096)) { // if we've reached envelope >4000 with drive= 4096, |
| 88 | + // we must be at the end of attack phase |
| 89 | + // so switch to decay... |
| 90 | + decay = true; // set decay flag |
| 91 | + drive = sustain_Level; // drive toward sustain level |
| 92 | + alpha = alpha2; // and set 'time constant' alpha2 for decay phase |
| 93 | + } |
| 94 | + |
| 95 | + envelope = ((1.0 - alpha) * drive + alpha * envelope); // implement difference equation: y(k) = (1 - alpha) * x(k) + alpha * y(k-1) |
| 96 | + writeToDAC(round(envelope)); // and output the envelope to the DAC |
| 97 | + |
| 98 | + gate = digitalRead(gatePin); // read the gate pin (remember we're in the while loop) |
| 99 | + trigger = gate; // and re-evaluate the trigger function |
| 100 | + } |
| 101 | + |
| 102 | + if (note_active == true) { // this is the start of the release phase |
| 103 | + drive = 0; // drive towards zero |
| 104 | + alpha = alpha3; // set 'time comnstant' alpha3 for release phase |
| 105 | + note_active = false; // turn off note_active flag |
| 106 | + release_done = false; // and set release_flag done false |
| 107 | + } |
| 108 | + |
| 109 | + envelope = ((1.0 - alpha3) * drive + alpha3 * envelope); // implement the difference equation again (outside the while loop) |
| 110 | + writeToDAC(round(envelope)); // and output envelope |
| 111 | + gate = digitalRead(gatePin); // watch out for a new note |
| 112 | + scan += 1; // prepare to look at a new parameter input |
| 113 | + if (envelope < 4) { // is the release phase ended? |
| 114 | + release_done = true; // yes - so flag it |
| 115 | + } |
| 116 | + if (scan == 4) { // increment the scan pointer |
| 117 | + scan = 0; |
| 118 | + } |
| 119 | +} |
| 120 | + |
| 121 | +// read the input parameters |
| 122 | +void update_params(int scan) { |
| 123 | + switch (scan) { |
| 124 | + case 0: |
| 125 | + CV0 = analogRead(pot0); // get the attack pole location |
| 126 | + alpha1 = 0.999 * cos((1023 - CV0) / 795); |
| 127 | + alpha1 = sqrt(alpha1); |
| 128 | + break; |
| 129 | + case 1: |
| 130 | + CV1 = analogRead(pot1); // get the release pole location |
| 131 | + alpha2 = 0.999 * cos((1023 - CV1) / 795); |
| 132 | + alpha2 = sqrt(alpha2); |
| 133 | + break; |
| 134 | + case 2: |
| 135 | + CV2 = analogRead(pot2); // get the (integer) sustain level |
| 136 | + sustain_Level = CV2 << 2; |
| 137 | + break; |
| 138 | + case 3: |
| 139 | + CV3 = analogRead(pot3); // get the release pole location (potentially closer to 1.0) |
| 140 | + alpha3 = 0.99999 * cos((1023 - CV3) / 795); |
| 141 | + alpha3 = sqrt(alpha3); |
| 142 | + break; |
| 143 | + } |
| 144 | +} |
0 commit comments