├── README.md └── Auduino_2__playable_.ino /README.md: -------------------------------------------------------------------------------- 1 | # Auduino2 2 | An Arduino-based granular synthesizer with CV/Gate control, inbuilt triggerability, and design-time bpm/scale selection. 3 | 4 | Based on Peter Knight's original Auduino code, which seems to have nearly vanished from the Internet. 5 | -------------------------------------------------------------------------------- /Auduino_2__playable_.ino: -------------------------------------------------------------------------------- 1 | // Auduino2, the Lo-Fi granular synthesiser, written for Control Voltage / Gate signals, including sending itself Gate. 2 | // 3 | // Original Auduino by Peter Knight, Tinker.it http://tinker.it (defunct) 4 | // Help: http://code.google.com/p/tinkerit/wiki/Auduino 5 | // More help: http://groups.google.com/group/auduino 6 | 7 | // modified by Joshua A.C. Newman http://glyphpress.com 8 | // to add CV/Gate, reorganize the pins, add triggering, and add more explicit comments. 9 | // GitHub project: https://github.com/JoshuaACNewman/Auduino2 10 | // 11 | // THE PINS: 12 | // These two work together to make the first of two waveforms: 13 | // Analog in 0: Grain 1 pitch. 14 | // Analog in 1: Grain 2 decay. 15 | // 16 | // These two work together to make the second of two waveforms: 17 | // Analog in 2: Grain 1 decay. 18 | // Analog in 3: Grain 2 pitch. 19 | // 20 | // Analog in 4: Grain repetition frequency. At the bottom of the range, this is BPM. Above a certain level, it's pitch. 21 | // 22 | // Digital 3: Audio out (Digital 11 on ATmega8). This pin is being accessed directly and affected by the timers. 23 | // To use for outgoing voltage control, you may need to add a voltage follower circuit 24 | // to avoid drawing too much current and affecting the signal. 25 | // 26 | // Digital 2: Gate input. This triggers the synthesizer. 27 | // Connect it to 5v permanently to always play, with a switch to switch between Gate and constant play, or 28 | // 29 | // VOLTAGE CONTROL 30 | // For incoming Control Voltage, attach a vactrol to each potentiometer. 31 | // Optionally, install a three-pole switch between the incoming legs of the vactrol and either side of the pot to allow for inverting the CV. 32 | // 33 | // One channel of a TRS cable goes to the vactrol and one to the GATE pin. 34 | // 35 | // Changelog: 36 | // 19 Nov 2008: Added support for ATmega8 boards 37 | // 21 Mar 2009: Added support for ATmega328 boards 38 | // 7 Apr 2009: Fixed interrupt vector for ATmega328 boards 39 | // 8 Apr 2009: Added support for ATmega1280 boards (Arduino Mega) 40 | // 8 Nov 2018: Reorganized pins, added comments, added Gate (Trigger) input, functionalized and abstracted 41 | 42 | #include 43 | #include 44 | #include 45 | 46 | uint16_t syncPhaseAcc; 47 | uint16_t syncPhaseInc; 48 | uint16_t grainPhaseAcc; 49 | uint16_t grainPhaseInc; 50 | uint16_t grainAmp; 51 | uint8_t grainDecay; 52 | uint16_t grain2PhaseAcc; 53 | uint16_t grain2PhaseInc; 54 | uint16_t grain2Amp; 55 | uint8_t grain2Decay; 56 | 57 | // To retrigger the grains when Gate first gets pulled HIGH, we need to know if the sound has been just trailing off. 58 | boolean triggerGate; 59 | 60 | // Map Analogue channels 61 | #define SYNC_CONTROL (0) // BPM or pitch, depending on how high you turn it. 62 | // To control pitch with CV while using a tone-frequency Sync value, plug in here. 63 | 64 | #define GRAIN_FREQ_CONTROL (1) // Frequency of grain 1 65 | #define GRAIN_DECAY_CONTROL (2) // Decay rate of grain 1 66 | 67 | #define GRAIN2_FREQ_CONTROL (3) // Frequency of grain 2 68 | #define GRAIN2_DECAY_CONTROL (4) // Decay rate of grain 2 69 | 70 | #define GATE_CONTROL (2) // Half of CV/Gate. Connect the Gate channel of all your inputs to this pin. 71 | // Control Voltage should go to individual parameters. 72 | // If you want to voltage-control pitch, connect it to the SYNC_CONTROL pin. 73 | 74 | // PIN SELECTION BY BOARD 75 | // Compile-time selectiopn of pins by µC/development board specs: 76 | // Changing these will also requires rewriting audioOn(). 77 | 78 | #if defined(__AVR_ATmega8__) 79 | // 80 | // On old ATmega8 boards. 81 | // Output is on pin 11 82 | // 83 | #define LED_PIN 13 // Use for outgoing Gate signal. 84 | #define LED_PORT PORTB 85 | #define LED_BIT 5 86 | #define PWM_PIN 11 87 | #define PWM_VALUE OCR2 88 | #define PWM_INTERRUPT TIMER2_OVF_vect 89 | #elif defined(__AVR_ATmega1280__) 90 | // 91 | // On the Arduino Mega 92 | // Output is on pin 3 93 | // 94 | #define LED_PIN 13 // Use for outgoing Gate signal. 95 | #define LED_PORT PORTB 96 | #define LED_BIT 7 97 | #define PWM_PIN 3 98 | #define PWM_VALUE OCR3C 99 | #define PWM_INTERRUPT TIMER3_OVF_vect 100 | #else 101 | // 102 | // For modern ATmega168 and ATmega328 boards 103 | // Output is on pin 3 104 | // 105 | #define PWM_PIN 3 106 | #define PWM_VALUE OCR2B 107 | #define LED_PIN 13 // Use for outgoing Gate signal. 108 | #define LED_PORT PORTB 109 | #define LED_BIT 5 110 | #define PWM_INTERRUPT TIMER2_OVF_vect 111 | #endif 112 | 113 | /* SYNC FREQUENCY MAPPING 114 | * Select which mapping you use at runtime in grainBuild(). 115 | * Values sound like a beat under ~10, and tonal from about 10 up. 116 | */ 117 | 118 | // Smooth logarithmic mapping 119 | // 120 | uint16_t antilogTable[] = { 121 | 64830, 64132, 63441, 62757, 62081, 61413, 60751, 60097, 59449, 58809, 58176, 57549, 56929, 56316, 55709, 55109, 122 | 54515, 53928, 53347, 52773, 52204, 51642, 51085, 50535, 49991, 49452, 48920, 48393, 47871, 47356, 46846, 46341, 123 | 45842, 45348, 44859, 44376, 43898, 43425, 42958, 42495, 42037, 41584, 41136, 40693, 40255, 39821, 39392, 38968, 124 | 38548, 38133, 37722, 37316, 36914, 36516, 36123, 35734, 35349, 34968, 34591, 34219, 33850, 33486, 33125, 32768 125 | }; 126 | uint16_t mapPhaseInc(uint16_t input) { 127 | return (antilogTable[input & 0x3f]) >> (input >> 6); 128 | } 129 | 130 | // Stepped chromatic mapping 131 | // 132 | uint16_t midiTable[] = { 133 | 17, 18, 19, 20, 22, 23, 24, 26, 27, 29, 31, 32, 34, 36, 38, 41, 43, 46, 48, 51, 54, 58, 61, 65, 69, 73, 134 | 77, 82, 86, 92, 97, 103, 109, 115, 122, 129, 137, 145, 154, 163, 173, 183, 194, 206, 218, 231, 135 | 244, 259, 274, 291, 308, 326, 346, 366, 388, 411, 435, 461, 489, 518, 549, 581, 616, 652, 691, 136 | 732, 776, 822, 871, 923, 978, 1036, 1097, 1163, 1232, 1305, 1383, 1465, 1552, 1644, 1742, 137 | 1845, 1955, 2071, 2195, 2325, 2463, 2610, 2765, 2930, 3104, 3288, 3484, 3691, 3910, 4143, 138 | 4389, 4650, 4927, 5220, 5530, 5859, 6207, 6577, 6968, 7382, 7821, 8286, 8779, 9301, 9854, 139 | 10440, 11060, 11718, 12415, 13153, 13935, 14764, 15642, 16572, 17557, 18601, 19708, 20879, 140 | 22121, 23436, 24830, 26306 141 | }; 142 | uint16_t mapMidi(uint16_t input) { 143 | return (midiTable[(1023 - input) >> 3]); 144 | } 145 | 146 | // Stepped Pentatonic mapping 147 | // 148 | uint16_t pentatonicTable[54] = { 149 | 0, 19, 22, 26, 29, 32, 38, 43, 51, 58, 65, 77, 86, 103, 115, 129, 154, 173, 206, 231, 259, 308, 346, 150 | 411, 461, 518, 616, 691, 822, 923, 1036, 1232, 1383, 1644, 1845, 2071, 2463, 2765, 3288, 151 | 3691, 4143, 4927, 5530, 6577, 7382, 8286, 9854, 11060, 13153, 14764, 16572, 19708, 22121, 26306 152 | }; 153 | 154 | uint16_t mapPentatonic(uint16_t input) { 155 | uint8_t value = (1023 - input) / (1024 / 53); 156 | return (pentatonicTable[value]); 157 | } 158 | 159 | // Fibonacci Sequence, low-frequency 160 | // 161 | uint16_t fibonacciBeatTable[20] = { 162 | 1, 1, 2 , 3, 5, 8, 13, 21, 34, 55, 89, 144 163 | }; 164 | 165 | 166 | uint16_t mapFibonacciBeat(uint16_t input) { 167 | uint8_t value = (1023 - input) / (1024 / 20); 168 | return (fibonacciBeatTable[value]); 169 | } 170 | 171 | // Linear, low-frequency, such as for danceable rhythms or an LFO. 172 | // 173 | uint16_t bpmTable[16] = { 174 | 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 175 | }; 176 | 177 | uint16_t mapBPM(uint16_t input) { 178 | uint8_t value = (1023 - input) / (1024 / 16); 179 | return (bpmTable[value]); 180 | } 181 | 182 | void audioOn() { 183 | #if defined(__AVR_ATmega8__) 184 | // ATmega8 has different registers 185 | TCCR2 = _BV(WGM20) | _BV(COM21) | _BV(CS20); 186 | TIMSK = _BV(TOIE2); 187 | #elif defined(__AVR_ATmega1280__) 188 | TCCR3A = _BV(COM3C1) | _BV(WGM30); 189 | TCCR3B = _BV(CS30); 190 | TIMSK3 = _BV(TOIE3); 191 | #else 192 | // Set up PWM to 31.25kHz, phase accurate 193 | TCCR2A = _BV(COM2B1) | _BV(WGM20); 194 | TCCR2B = _BV(CS20); 195 | TIMSK2 = _BV(TOIE2); 196 | #endif 197 | } 198 | 199 | 200 | void setup() { 201 | pinMode(PWM_PIN, OUTPUT); 202 | audioOn(); 203 | pinMode(LED_PIN, OUTPUT); 204 | pinMode(GATE_CONTROL, INPUT); // Use a pullup resistor to ground to clean the signal. Can't use INPUT_PULLUP because Gate is triggered with HIGH. 205 | } 206 | 207 | void loop() { 208 | // The loop is pretty simple - it just updates the parameters for the oscillators while the GATE pin is HIGH. 209 | // 210 | // Avoid using any functions that make extensive use of interrupts, or turn interrupts off. 211 | // They will cause clicks and poops in the audio. 212 | /* 213 | Remove this if() if you want it to play constantly 214 | OR install a switch between the GATE pin and 5v 215 | to switch between constant play and gate trigger. 216 | */ 217 | 218 | if (digitalRead (GATE_CONTROL) == HIGH) { // Flip to LOW if you're using active-low Gate signal. 219 | if (triggerGate == true) { 220 | grainStart(); 221 | triggerGate = false; 222 | } 223 | 224 | grainBuild(); 225 | 226 | } else { 227 | // Reset the trigger so it starts back from the beginning 228 | triggerGate = true; 229 | grainBuild (); 230 | } 231 | } 232 | 233 | void grainStart () { 234 | grainPhaseAcc = 0; 235 | grainAmp = 0x7fff; 236 | grain2PhaseAcc = 0; 237 | grain2Amp = 0x7fff; 238 | LED_PORT ^= 1 << LED_BIT; // Faster than using digitalWrite. You can use this for a Gate signal. 239 | } 240 | 241 | void grainBuild () { 242 | // Smooth frequency mapping 243 | //syncPhaseInc = mapPhaseInc(analogRead(SYNC_CONTROL)) / 4; 244 | 245 | // Stepped mapping to MIDI notes: C, Db, D, Eb, E, F... 246 | //syncPhaseInc = mapMidi(analogRead(SYNC_CONTROL)); 247 | 248 | // Stepped pentatonic mapping: D, E, G, A, B 249 | // syncPhaseInc = mapPentatonic(analogRead(SYNC_CONTROL)); 250 | 251 | // Stepped mapping to low-Hz Fibonacci beats: 1, 1, 2, 3, 5, 8, 13... 252 | // syncPhaseInc = mapFibonacciBeat(analogRead(SYNC_CONTROL)); 253 | 254 | // Linear, low-Hz mapping: 1, 2, 3, 4... 255 | syncPhaseInc = mapBPM(analogRead(SYNC_CONTROL)); 256 | 257 | // Read the grain inputs and send them to the timers 258 | grainPhaseInc = mapPhaseInc(analogRead(GRAIN_FREQ_CONTROL)) / 2; 259 | grainDecay = analogRead(GRAIN_DECAY_CONTROL) / 8; 260 | grain2PhaseInc = mapPhaseInc(analogRead(GRAIN2_FREQ_CONTROL)) / 2; 261 | grain2Decay = analogRead(GRAIN2_DECAY_CONTROL) / 4; 262 | } 263 | 264 | SIGNAL(PWM_INTERRUPT) { 265 | uint8_t value; 266 | uint16_t output; 267 | 268 | syncPhaseAcc += syncPhaseInc; 269 | if (syncPhaseAcc < syncPhaseInc) { 270 | if (digitalRead (GATE_CONTROL) == HIGH) { // Flip to LOW if you're using active-low gate signal. 271 | grainStart(); 272 | } 273 | } 274 | 275 | // Increment the phase of the grain oscillators 276 | grainPhaseAcc += grainPhaseInc; 277 | grain2PhaseAcc += grain2PhaseInc; 278 | 279 | // Convert phase into a triangle wave 280 | value = (grainPhaseAcc >> 7) & 0xff; 281 | if (grainPhaseAcc & 0x8000) value = ~value; 282 | // Multiply by current grain amplitude to get sample 283 | output = value * (grainAmp >> 8); 284 | 285 | // Repeat for second grain 286 | value = (grain2PhaseAcc >> 7) & 0xff; 287 | if (grain2PhaseAcc & 0x8000) value = ~value; 288 | output += value * (grain2Amp >> 8); 289 | 290 | // Make the grain amplitudes decay by a factor every sample (exponential decay) 291 | grainAmp -= (grainAmp >> 8) * grainDecay; 292 | grain2Amp -= (grain2Amp >> 8) * grain2Decay; 293 | 294 | // Scale output to the available range, clipping if necessary 295 | output >>= 9; 296 | if (output > 255) output = 255; 297 | 298 | // Output to PWM (this is faster than using analogWrite) 299 | PWM_VALUE = output; 300 | } 301 | 302 | --------------------------------------------------------------------------------