├── README.md ├── arduino_LFO └── arduino_LFO.ino ├── arduino_attenuverter └── arduino_attenuverter.ino ├── arduino_envelope_generator └── arduino_envelope_generator.ino ├── arduino_envelope_generator_V2 └── arduino_envelope_generator_V2.ino ├── arduino_euclidean_sequencer └── arduino_euclidean_sequencer.ino ├── arduino_janky_sequencer └── arduino_janky_sequencer.ino ├── arduino_quantizer └── arduino_quantizer.ino ├── arduino_sample_or_track_and_hold └── arduino_sample_or_track_and_hold.ino ├── arduino_squarewave_clock_trigger_source └── arduino_squarewave_clock_trigger_source.ino ├── arduino_squarewave_clock_trigger_source_V2 └── arduino_squarewave_clock_trigger_source_V2.ino ├── arduino_squarewave_clock_trigger_source_V3 └── arduino_squarewave_clock_trigger_source_V3.ino ├── arduino_two_mode_sequencer └── arduino_two_mode_sequencer.ino ├── mozzi_dual_drone_VCO_VCA └── mozzi_dual_drone_VCO_VCA.ino ├── mozzi_dual_mode_VCO_with_VCA ├── halfsin2048_int8.h └── mozzi_dual_mode_VCO_with_VCA.ino └── mozzi_simpleVCO_V1 └── mozzi_simpleVCO_V1.ino /README.md: -------------------------------------------------------------------------------- 1 | I love noise. A lot. In pursuit of deeply textured noise machines, I've been tinkering around with lo-fi digital synthesis for a little while now. Unfortunately, most of them are, well, limited. Most of my attempts have either hit the deeply textured noise goal but at the cost of all musicality, or are capable of only the merest beeps, boops, and warbles. MCUs only have so many clock cycles and pins to play around with, and they get used up super quickly when designing little noise machines. Which is where modular comes in. 2 | 3 | Like any good lover of noise and aspiring maker of machines that make it, I think modular synthesizers are radical. They are also prohibitively expensive. I'm a stay-at-home dad with very (very) little disposable income so something that costs $300 and doesn't even make any noise until I've also purchased a handful of other $300 things isn't really an option. On the flip side, the DIY modular systems I come across, while extremely cool and inspirational, always seem more complicated than necessary by avoiding MCUs: requiring a dozen or so different ICs and (sometimes) nontrivial peripheral circuitry. So, I wanted to make a system that was suuuuuuper simple, requiring just a handful of the same parts in each module and ridiculously basic peripheral circuits. 4 | 5 | Et voila! 6 | 7 | All together, the tested modules include the following: 8 | 9 | square wave generator 10 | envelope generator 11 | VCO with VCA 12 | LFO 13 | attenuverter with offset 14 | sample- or track-and-hold 15 | quantizer 16 | sequencer with two modes (step and Euclidean). 17 | 18 | Hopefully someone out there will find these useful and/or inspirational for their own project. 19 | 20 | ----------------------------------- 21 | 22 | NOTES: 23 | - To get the VCO to play properly you'll need the Mozzi library by Tim Barrass (https://sensorium.github.io/Mozzi/) and you'll have to set it to Hi-Fi mode. To do so, open the mozzi_config header file and uncomment this line #define AUDIO_MODE HIFI and then comment out whichever other one was set. 24 | 25 | - To get the sequencer to work you'll need three libraries: the Adafruit_NeoPixel library (https://learn.adafruit.com/adafruit-neopixel-uberguide/arduino-library-installation), Paul Stoffregan's Encoder library (https://github.com/PaulStoffregen/Encoder), and version 1.4.2 of the AceButton library (apparently the newer version pops errors at the moment). 26 | 27 | - The passive low-pass filter circuits on the PWM outputs are the wrong version in the schematics. They should use a 1K resistor and a 0.1uF capacitor. The ones in the schematic won't attenuate the PWM frequencies that the current code uses so are basically useless. 28 | 29 | - The VCO uses a half-sine wavetable that I made myself so the code throws an error. You can either replace that wavetable for another one that comes standard with Mozzi, or you can add the half-sine wavetable to the Mozzi library "tables" folder. It can be downloaded at my github here: https://github.com/scraptured/DIY_modular_Arduino/tree/master/mozzi_dual_mode_VCO_with_VCA 30 | -------------------------------------------------------------------------------- /arduino_LFO/arduino_LFO.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * A LFO module using 64-sample wavetable for eight different waveforms. 3 | * - Each module contains four LFOs with independent waveform and frequency control 4 | * - Frequencies range from ~0.125Hz to ~15Hz 5 | * - Each LFO requires three pins: two analog pins and one PWM pin. This caps the number of LFO per Arduino Nano at four without compicating the peripheral circuitry. 6 | * 7 | * THIS MODULE IS FINISHED. No optimizations necessary. 8 | */ 9 | 10 | 11 | byte wavetableLength = 64; 12 | 13 | /* 14 | * sine 15 | * triangle 16 | * halfsine 17 | * square 18 | * saw 19 | * random pulse 20 | * random saw 21 | * random noise 22 | */ 23 | byte wavetables[8][64] = {{127, 139, 151, 163, 175, 186, 197, 207, 216, 225, 232, 239, 244, 248, 251, 253, 254, 253, 251, 248, 244, 239, 232, 225, 216, 207, 197, 186, 175, 163, 151, 139, 127, 114, 102, 90, 78, 67, 56, 46, 37, 28, 21, 14, 9, 5, 2, 0, 0, 0, 2, 5, 9, 14, 21, 28, 37, 46, 56, 67, 78, 90, 102, 114}, 24 | {0, 8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 88, 96, 104, 112, 120, 128, 136, 144, 152, 160, 168, 176, 184, 192, 200, 208, 216, 224, 232, 240, 248, 248, 240, 232, 224, 216, 208, 200, 192, 184, 176, 168, 160, 152, 144, 136, 128, 120, 112, 104, 96, 88, 80, 72, 64, 56, 48, 40, 32, 24, 16, 8, 0}, 25 | {0, 12, 24, 37, 49, 61, 74, 85, 97, 109, 120, 131, 141, 151, 161, 171, 180, 188, 197, 204, 212, 218, 224, 230, 235, 240, 244, 247, 250, 252, 253, 254, 255, 254, 253, 252, 250, 247, 244, 240, 235, 230, 224, 218, 212, 204, 197, 188, 180, 171, 161, 151, 141, 131, 120, 109, 97, 85, 74, 61, 49, 37, 24, 12}, 26 | {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 27 | {0, 4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 44, 48, 52, 56, 60, 64, 68, 72, 76, 80, 84, 88, 92, 96, 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140, 144, 148, 152, 156, 160, 164, 168, 172, 176, 180, 184, 188, 192, 196, 200, 204, 208, 212, 216, 220, 224, 228, 232, 236, 240, 244, 248, 252}, 28 | {0, 0, 0, 255, 255, 0, 255, 255, 0, 0, 0, 255, 0, 0, 0, 0, 255, 0, 0, 255, 255, 255, 0, 255, 0, 255, 0, 255, 255, 0, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 0, 0, 255, 255, 0, 0, 255}, 29 | {0, 0, 8, 0, 0, 0, 0, 28, 0, 36, 0, 44, 0, 52, 56, 0, 0, 68, 72, 76, 80, 0, 88, 0, 96, 100, 0, 108, 112, 116, 120, 0, 128, 132, 0, 140, 144, 0, 0, 0, 160, 164, 168, 0, 176, 0, 0, 188, 0, 196, 0, 0, 208, 212, 0, 0, 0, 0, 232, 236, 240, 0, 0, 0}, 30 | {243, 109, 118, 169, 49, 242, 235, 54, 78, 102, 10, 108, 166, 238, 27, 0, 172, 215, 227, 134, 187, 77, 104, 230, 218, 44, 246, 101, 216, 19, 187, 99, 248, 87, 52, 160, 165, 156, 188, 36, 49, 225, 75, 226, 24, 182, 71, 177, 155, 117, 207, 207, 133, 165, 47, 181, 250, 102, 22, 214, 146, 62, 131, 86}}; 31 | 32 | class LFO 33 | { 34 | //pins 35 | byte sampleRatePin, waveformPin, outputPin; 36 | 37 | //state variables 38 | unsigned long previousMillis; 39 | unsigned int sampleRate; 40 | byte waveform, output, sampleIndex; 41 | 42 | public: 43 | LFO(byte pin1, byte pin2, byte pin3) 44 | { 45 | sampleRatePin = pin1; 46 | waveformPin = pin2; 47 | outputPin = pin3; 48 | } 49 | 50 | void updateControlParams() { 51 | sampleRate = 128 - (analogRead(sampleRatePin) >> 3); 52 | waveform = analogRead(waveformPin) >> 7; 53 | } 54 | 55 | void setupFunc() { 56 | pinMode(outputPin, OUTPUT); 57 | previousMillis = millis(); 58 | output = 0; 59 | } 60 | 61 | void loopFunc() { 62 | if (millis() - previousMillis >= sampleRate) { 63 | sampleIndex++; 64 | previousMillis = millis(); 65 | if (sampleIndex >= wavetableLength) { 66 | sampleIndex = 0; 67 | } 68 | } 69 | output = wavetables[waveform][sampleIndex]; 70 | analogWrite(outputPin, output); 71 | //Serial.println(sampleIndex); 72 | } 73 | }; 74 | 75 | 76 | LFO lfo1(A0, A1, 11); //sampleRate, waveform, output 77 | LFO lfo2(A2, A3, 10); 78 | LFO lfo3(A4, A5, 9); 79 | LFO lfo4(A6, A7, 3); 80 | 81 | unsigned long update_control_timer = 0; 82 | int update_control_period = 30; 83 | 84 | 85 | 86 | 87 | void setup() { 88 | lfo1.setupFunc(); 89 | lfo2.setupFunc(); 90 | lfo3.setupFunc(); 91 | lfo4.setupFunc(); 92 | update_control_timer = millis(); 93 | //Serial.begin(9600); 94 | TCCR0B = TCCR0B & B11111000 | B00000010; // set PWM freq on pin 5/6 to ~7.8kHz 95 | TCCR1B = TCCR1B & B11111000 | B00000010; // set PWM freq on pin 9/10 to ~3.9kHz 96 | TCCR2B = TCCR2B & B11111000 | B00000010; // set PWM freq on pin 3/11 to ~3.9kHz 97 | } 98 | 99 | void loop() { 100 | lfo1.loopFunc(); 101 | lfo2.loopFunc(); 102 | lfo3.loopFunc(); 103 | lfo4.loopFunc(); 104 | 105 | if (millis() - update_control_timer >= update_control_period) { 106 | lfo1.updateControlParams(); 107 | lfo2.updateControlParams(); 108 | lfo3.updateControlParams(); 109 | lfo4.updateControlParams(); 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /arduino_attenuverter/arduino_attenuverter.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * An attenuverter module with DC offset. 3 | * - Each module contains two attenuverters w/ adjustable offset. 4 | * - Gain and offset are controlled by potentiometers and whether the signal gets inverted is controlled by a switch 5 | * - Each attenvuerter requires six pins: three analog pins, two PWM pin, and one GPIO. This caps the number of attenverters per Arduino Nano at two without compicating the peripheral circuitry. 6 | * 7 | * THIS MODULE IS FINISHED. No optimizations necessary. 8 | */ 9 | 10 | class Attenuverter 11 | { 12 | //pins 13 | byte signalPin, factorPin, outputPin, invertPin, offsetPin, ledPin; 14 | 15 | //state variables 16 | int s, f, i, x, o, output; // signal, factor pin read, invert, intermediary variable, offset value, output 17 | 18 | public: 19 | Attenuverter(byte pin1, byte pin2, byte pin3, byte pin4, byte pin5, byte pin6) 20 | { 21 | signalPin = pin1; 22 | factorPin = pin2; 23 | outputPin = pin3; 24 | invertPin = pin4; 25 | offsetPin = pin5; 26 | ledPin = pin6; 27 | } 28 | 29 | void updateControlParams() { 30 | s = analogRead(signalPin) >> 2; 31 | f = analogRead(factorPin) >> 5; 32 | i = digitalRead(invertPin); 33 | o = analogRead(offsetPin) >> 2; 34 | 35 | if (i == 0) { 36 | output = (s * f) >> 5; 37 | } 38 | else { 39 | output = ((255 - s) * f) >> 5; 40 | } 41 | output += o; 42 | if (output > 255) { output = 255; } 43 | } 44 | 45 | void setupFunc() { 46 | pinMode(outputPin, OUTPUT); 47 | pinMode(ledPin, OUTPUT); 48 | pinMode(invertPin, INPUT); 49 | } 50 | 51 | void loopFunc() { 52 | analogWrite(outputPin, output); 53 | analogWrite(ledPin, output); 54 | } 55 | }; 56 | 57 | Attenuverter a1(A0, A1, 10, 12, A2, 11); //signal, factor, output, invert, offset, led 58 | Attenuverter a2(A3, A4, 5, 7, A5, 6); 59 | 60 | 61 | unsigned long update_control_timer = 0; 62 | int update_control_period = 30; 63 | 64 | 65 | 66 | void setup() { 67 | TCCR0B = TCCR0B & B11111000 | B00000010; // set PWM freq on pin 5/6 to ~7.8kHz 68 | TCCR1B = TCCR1B & B11111000 | B00000010; // set PWM freq on pin 9/10 to ~3.9kHz 69 | TCCR2B = TCCR2B & B11111000 | B00000010; // set PWM freq on pin 3/11 to ~3.9kHz 70 | } 71 | 72 | void loop() { 73 | if (millis() - update_control_timer >= update_control_period) { 74 | a1.updateControlParams(); 75 | a2.updateControlParams(); 76 | } 77 | 78 | a1.loopFunc(); 79 | a2.loopFunc(); 80 | } 81 | -------------------------------------------------------------------------------- /arduino_envelope_generator/arduino_envelope_generator.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * 128 sample AD (with optional sustain) envelope generator with linear, exponential, and logarithmic curves 3 | */ 4 | 5 | 6 | 7 | //PINS! 8 | const byte analog_inputs[3] = {A0, A1, A2}; // {attack time(128ms - 1280ms), decay time(128ms-128ms), curve shape(0-8)} 9 | const byte trigger_pin = 2; 10 | const byte sustain_pin = 3; 11 | const byte output_pin = 9; 12 | 13 | 14 | //global variables 15 | bool trigger = 0; 16 | bool sustain = 0; 17 | bool note_on = 0; 18 | byte curve_shape = 0; 19 | 20 | byte attack_index = 0; 21 | byte decay_index = 0; 22 | 23 | unsigned long last_time_of_attack_sample; 24 | unsigned long last_time_of_decay_sample; 25 | int attack_sample_rate = 4; 26 | int decay_sample_rate = 4; 27 | 28 | unsigned long last_time_of_update; 29 | int update_controls_delta = 128; //every 512ms update control parameters 30 | 31 | 32 | //wavetables 33 | const byte linear_curve[64] = {0, 4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 44, 48, 52, 56, 60, 64, 34 | 68, 72, 76, 80, 84, 88, 92, 96, 100, 104, 108, 112, 116, 120, 124, 128, 35 | 132, 136, 140, 144, 148, 152, 156, 160, 164, 168, 172, 176, 180, 184, 188, 192, 36 | 196, 200, 204, 208, 212, 216, 220, 224, 228, 232, 236, 240, 244, 248, 252}; 37 | 38 | const byte log_curve[64] = {0, 16, 30, 43, 54, 64, 73, 82, 90, 97, 104, 111, 117, 123, 128, 134, 139, 144, 148, 39 | 153, 157, 161, 165, 169, 173, 176, 180, 183, 186, 189, 193, 196, 198, 201, 204, 207, 40 | 209, 212, 215, 217, 219, 222, 224, 226, 229, 231, 233, 235, 237, 239, 241, 243, 245, 41 | 247, 249, 250, 252, 254, 255, 255, 255, 255, 255, 255}; 42 | 43 | const byte exp_curve[64] = {0, 0, 0, 0, 0, 0, 1, 3, 5, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 29, 31, 33, 44 | 36, 38, 40, 43, 46, 48, 51, 54, 57, 59, 62, 66, 69, 72, 75, 79, 82, 86, 90, 94, 45 | 98, 102, 107, 111, 116, 121, 127, 132, 138, 144, 151, 158, 165, 173, 182, 191, 46 | 201, 212, 225, 239, 255}; 47 | 48 | 49 | 50 | //Functions 51 | int time_map(int input) { //2ms per sample to 20ms per sample 52 | return (input >> 4) + 1; 53 | } 54 | int waveshape_map(int input) { 55 | return (input*9) >> 10; 56 | } 57 | 58 | 59 | void update_controls() { 60 | attack_sample_rate = time_map(analogRead(analog_inputs[0])); 61 | decay_sample_rate = time_map(analogRead(analog_inputs[1])); 62 | curve_shape = waveshape_map(analogRead(analog_inputs[2])); 63 | sustain = digitalRead(sustain_pin); 64 | } 65 | 66 | byte attack_choice(byte n) { 67 | if (n < 3) {return linear_curve[attack_index];} 68 | else if (n < 6) {return exp_curve[attack_index];} 69 | else {return log_curve[attack_index];} 70 | } 71 | 72 | byte decay_choice(byte n) { 73 | if ((n == 0) or (n==3) or (n==6)) {return linear_curve[63 - decay_index];} 74 | else if ((n==1) or (n==4) or (n==7)) {return exp_curve[63 - decay_index];} 75 | else {return log_curve[63 - decay_index];} 76 | } 77 | 78 | 79 | void read_trigger() { 80 | if (digitalRead(trigger_pin) == true and trigger == false) { 81 | trigger = true; 82 | attack_index = 0; 83 | note_on = true; 84 | } 85 | else if (digitalRead(trigger_pin) == false and trigger == true) { 86 | trigger = false; 87 | decay_index = 0; 88 | note_on = false; 89 | } 90 | } 91 | 92 | 93 | 94 | 95 | void setup() { 96 | 97 | } 98 | 99 | 100 | 101 | void loop() { 102 | //update control parameters periodically based on update_controls_delta variable 103 | if (millis() - last_time_of_update >= update_controls_delta) { 104 | update_controls(); 105 | } 106 | 107 | //read trigger pin and set boolean variables depending on state 108 | read_trigger(); 109 | 110 | if (note_on == true) { 111 | analogWrite(output_pin, attack_choice(curve_shape)); 112 | if ((sustain == false) and (attack_index >= 63)) { 113 | note_on = false; 114 | } 115 | else if ((millis() - last_time_of_attack_sample >= attack_sample_rate) and (attack_index < 63)) { 116 | attack_index++; 117 | last_time_of_attack_sample = millis(); 118 | } 119 | } 120 | 121 | else { 122 | analogWrite(output_pin, decay_choice(curve_shape)); 123 | if ((millis() - last_time_of_decay_sample >= decay_sample_rate) and (decay_index < 63)) { 124 | decay_index++; 125 | last_time_of_decay_sample = millis(); 126 | } 127 | } 128 | 129 | 130 | 131 | 132 | 133 | } 134 | -------------------------------------------------------------------------------- /arduino_envelope_generator_V2/arduino_envelope_generator_V2.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * 128 sample AD envelope module with optional sustain and linear, exponential, and logarithmic curves 3 | * - Each module contains two AD envelope generators. 4 | * - Attack time, decay time, and wave shape are controlled with potentiometers while sustain is controlled by a switch. 5 | * - Each AD envelope generator requires six pins: three analog pins, one PWM pin, and two GPIO. This caps the number of AD envelope generators per Arduino Nano at two without compicating the peripheral circuitry. 6 | * 7 | * THIS MODULE IS FINISHED. No optimizations necessary. 8 | */ 9 | 10 | 11 | //wavetables 12 | const byte linear_curve[64] = {0, 4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 44, 48, 52, 56, 60, 64, 13 | 68, 72, 76, 80, 84, 88, 92, 96, 100, 104, 108, 112, 116, 120, 124, 128, 14 | 132, 136, 140, 144, 148, 152, 156, 160, 164, 168, 172, 176, 180, 184, 188, 192, 15 | 196, 200, 204, 208, 212, 216, 220, 224, 228, 232, 236, 240, 244, 248, 252}; 16 | 17 | const byte log_curve[64] = {0, 16, 30, 43, 54, 64, 73, 82, 90, 97, 104, 111, 117, 123, 128, 134, 139, 144, 148, 18 | 153, 157, 161, 165, 169, 173, 176, 180, 183, 186, 189, 193, 196, 198, 201, 204, 207, 19 | 209, 212, 215, 217, 219, 222, 224, 226, 229, 231, 233, 235, 237, 239, 241, 243, 245, 20 | 247, 249, 250, 252, 254, 255, 255, 255, 255, 255, 255}; 21 | 22 | const byte exp_curve[64] = {0, 0, 0, 0, 0, 0, 1, 3, 5, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 29, 31, 33, 23 | 36, 38, 40, 43, 46, 48, 51, 54, 57, 59, 62, 66, 69, 72, 75, 79, 82, 86, 90, 94, 24 | 98, 102, 107, 111, 116, 121, 127, 132, 138, 144, 151, 158, 165, 173, 182, 191, 25 | 201, 212, 225, 239, 255}; 26 | 27 | 28 | //Functions 29 | int time_map(int input) { //2ms per sample to 20ms per sample 30 | return (input >> 4) + 1; 31 | } 32 | int waveshape_map(int input) { 33 | return (input*9) >> 10; 34 | } 35 | 36 | 37 | class EnvelopeGenerator 38 | { 39 | //pins 40 | byte attackPin, decayPin, shapePin, triggerPin, sustainPin, outputPin; 41 | 42 | //state variables 43 | bool trigger = 0; 44 | bool sustain = 0; 45 | bool note_on = 0; 46 | byte curve_shape = 0; 47 | 48 | byte attack_index = 0; 49 | byte decay_index = 0; 50 | 51 | unsigned long last_time_of_attack_sample; 52 | unsigned long last_time_of_decay_sample; 53 | int attack_sample_rate = 4; 54 | int decay_sample_rate = 4; 55 | 56 | public: 57 | EnvelopeGenerator(byte pin1, byte pin2, byte pin3, byte pin4, byte pin5, byte pin6) 58 | { 59 | attackPin = pin1; 60 | decayPin = pin2; 61 | shapePin = pin3; 62 | triggerPin = pin4; 63 | sustainPin = pin5; 64 | outputPin = pin6; 65 | } 66 | 67 | void update_controls() { 68 | attack_sample_rate = time_map(analogRead(attackPin)); 69 | decay_sample_rate = time_map(analogRead(decayPin)); 70 | curve_shape = waveshape_map(analogRead(shapePin)); 71 | sustain = digitalRead(sustainPin); 72 | } 73 | 74 | byte attack_choice(byte n) { 75 | if (n < 3) {return linear_curve[attack_index];} 76 | else if (n < 6) {return exp_curve[attack_index];} 77 | else {return log_curve[attack_index];} 78 | } 79 | 80 | byte decay_choice(byte n) { 81 | if ((n == 0) or (n==3) or (n==6)) {return linear_curve[63 - decay_index];} 82 | else if ((n==1) or (n==4) or (n==7)) {return exp_curve[63 - decay_index];} 83 | else {return log_curve[63 - decay_index];} 84 | } 85 | 86 | void read_trigger() { 87 | if (digitalRead(triggerPin) == true and trigger == false) { 88 | trigger = true; 89 | attack_index = 0; 90 | note_on = true; 91 | } 92 | else if (digitalRead(triggerPin) == false and trigger == true) { 93 | trigger = false; 94 | decay_index = 0; 95 | note_on = false; 96 | } 97 | } 98 | 99 | void loopFunc() { 100 | //read trigger pin and set boolean variables depending on state 101 | read_trigger(); 102 | 103 | if (note_on == true) { 104 | analogWrite(outputPin, attack_choice(curve_shape)); 105 | if ((sustain == false) and (attack_index >= 63)) { 106 | note_on = false; 107 | } 108 | else if ((millis() - last_time_of_attack_sample >= attack_sample_rate) and (attack_index < 63)) { 109 | attack_index++; 110 | last_time_of_attack_sample = millis(); 111 | } 112 | } 113 | 114 | else { 115 | analogWrite(outputPin, decay_choice(curve_shape)); 116 | if ((millis() - last_time_of_decay_sample >= decay_sample_rate) and (decay_index < 63)) { 117 | decay_index++; 118 | last_time_of_decay_sample = millis(); 119 | } 120 | } 121 | } 122 | }; 123 | 124 | EnvelopeGenerator e1(A0, A1, A2, 12, 11, 10); 125 | EnvelopeGenerator e2(A3, A4, A5, 8, 7, 6); 126 | 127 | 128 | unsigned long last_time_of_update; 129 | int update_controls_delta = 30; //every 512ms update control parameters 130 | 131 | 132 | void setup() { 133 | TCCR0B = TCCR0B & B11111000 | B00000010; // set PWM freq on pin 5/6 to ~7.8kHz 134 | TCCR1B = TCCR1B & B11111000 | B00000010; // set PWM freq on pin 9/10 to ~3.9kHz 135 | TCCR2B = TCCR2B & B11111000 | B00000010; // set PWM freq on pin 3/11 to ~3.9kHz 136 | } 137 | 138 | 139 | 140 | void loop() { 141 | //update control parameters periodically based on update_controls_delta variable 142 | if (millis() - last_time_of_update >= update_controls_delta) { 143 | e1.update_controls(); 144 | e2.update_controls(); 145 | } 146 | 147 | e1.loopFunc(); 148 | e2.loopFunc(); 149 | } 150 | -------------------------------------------------------------------------------- /arduino_euclidean_sequencer/arduino_euclidean_sequencer.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | */ 4 | 5 | 6 | #include 7 | #include 8 | #include 9 | using namespace ace_button; 10 | 11 | #include 12 | #include 13 | 14 | // Neopixel variables 15 | const byte neoPixelPin = 9; 16 | const byte neoPixelCount = 9; 17 | Adafruit_NeoPixel leds(neoPixelCount, neoPixelPin, NEO_RGB + NEO_KHZ800); 18 | 19 | // Rotary encoder variables 20 | Encoder encoder(2, 3); 21 | const byte L = neoPixelCount - 1; //max sequence length 22 | byte counters[3][4] = {{4, 0, 0, 4}, 23 | {4, 0, 0, 4}, 24 | {4, 0, 0, 4}}; 25 | byte values[3][4] = {{1, 0, 0, 1}, // {seq length, beats, offset, clk divider} 26 | {1, 0, 0, 1}, 27 | {1, 0, 0, 1}}; 28 | byte maxes[3][4] = {{L, L, L, L}, 29 | {L, L, L, L}, 30 | {L, L, L, L}}; 31 | byte mins[3][4] = {{1, 0, 0, 1}, 32 | {1, 0, 0, 1}, 33 | {1, 0, 0, 1}}; 34 | 35 | //button variables 36 | const byte buttonPin = 4; 37 | AceButton button(buttonPin); 38 | unsigned long seqSelectCounter = 0; 39 | byte seqSelect = 0; 40 | unsigned long paramSelectCounter = 0; 41 | byte paramSelect = 0; 42 | void handleEvent(AceButton*, uint8_t, uint8_t); 43 | 44 | //Euclidean Sequencer variables 45 | bool outputArrays[3][L] = {{0, 0, 0, 0, 0, 0, 0, 0}, 46 | {0, 0, 0, 0, 0, 0, 0, 0}, 47 | {0, 0, 0, 0, 0, 0, 0, 0}}; 48 | byte outputIndexes[3] = {0, 0, 0}; 49 | unsigned long outputClockCounters[3] = {0, 0, 0}; 50 | 51 | //trigger variables 52 | const byte outputPins[3] = {6, 7, 8}; 53 | const byte triggerPin = 12; 54 | bool trigger = 0; 55 | byte triggerCounts[3] = {0, 0, 0}; 56 | unsigned long indexCounters[3] = {0, 0, 0}; 57 | byte indexes[3] = {0, 0, 0}; 58 | 59 | 60 | unsigned long update_control_timer = 0; 61 | int update_control_period = 100; 62 | 63 | 64 | 65 | void setup() { 66 | for (byte i=0;i<3;i++) { 67 | pinMode(outputPins[i], OUTPUT); 68 | } 69 | 70 | leds.begin(); 71 | leds.show(); 72 | 73 | pinMode(buttonPin, INPUT_PULLUP); 74 | button.init(buttonPin, HIGH); 75 | ButtonConfig* buttonConfig = button.getButtonConfig(); 76 | buttonConfig->setEventHandler(handleEvent); 77 | buttonConfig->setFeature(ButtonConfig::kFeatureDoubleClick); 78 | buttonConfig->setFeature( 79 | ButtonConfig::kFeatureSuppressClickBeforeDoubleClick); 80 | buttonConfig->setFeature(ButtonConfig::kFeatureSuppressAfterClick); 81 | buttonConfig->setFeature(ButtonConfig::kFeatureSuppressAfterDoubleClick); 82 | 83 | Serial.begin(115200); 84 | } 85 | 86 | 87 | void loop() { 88 | 89 | //Button block 90 | button.check(); 91 | 92 | //Rotary encoder block 93 | long newPosition = encoder.read(); 94 | 95 | byte x = maxes[seqSelect][paramSelect] << 2; 96 | byte y = mins[seqSelect][paramSelect] << 2; 97 | if (newPosition > x) { 98 | newPosition = x; 99 | encoder.write(x); 100 | } 101 | else if (newPosition < y) { 102 | newPosition = y; 103 | encoder.write(y); 104 | } 105 | 106 | if (newPosition != counters[seqSelect][paramSelect]) { 107 | counters[seqSelect][paramSelect] = newPosition; 108 | printUpdate(); 109 | //printSeqs(); 110 | } 111 | 112 | updateParamValues(); 113 | updateParamMaxes(); 114 | 115 | for (byte i=0; i<3; i++) { 116 | if (values[i][1] == 0) { clearOutputArray(i); } 117 | else { setOutputArray(i); } 118 | } 119 | 120 | 121 | //trigger block 122 | bool t = digitalRead(triggerPin); 123 | if (t == HIGH and trigger == LOW) { 124 | trigger = HIGH; 125 | for (byte i=0;i<3;i++) { 126 | triggerCounts[i]++; 127 | if (triggerCounts[i] >= values[i][3]) { // if trigger counter for seq is equal to the clk divider inc index and reset to zero 128 | triggerCounts[i] = 0; 129 | indexCounters[i]++; 130 | indexes[i] = (indexCounters[i] + values[i][2]) % values[i][0]; 131 | } 132 | } 133 | } 134 | else if (t == LOW and trigger == HIGH) { 135 | trigger = LOW; 136 | } 137 | 138 | 139 | //NeoPixel block 140 | if (millis() - update_control_timer >= update_control_period) { 141 | setLEDS(); 142 | leds.show(); 143 | } 144 | 145 | //output block 146 | for (byte i=0;i<3;i++) { 147 | if (outputArrays[i][indexes[i]] == 1) { 148 | digitalWrite(outputPins[i], HIGH); 149 | } 150 | else { 151 | digitalWrite(outputPins[i], LOW); 152 | } 153 | } 154 | 155 | } 156 | 157 | 158 | 159 | //////////////////////////////////////////////////////// 160 | /* 161 | * FUNCTIONS 162 | */ 163 | 164 | 165 | // The event handler for the button. 166 | void handleEvent(AceButton* /* button */, uint8_t eventType, 167 | uint8_t buttonState) { 168 | switch (eventType) { 169 | case AceButton::kEventClicked: 170 | break; 171 | case AceButton::kEventReleased: 172 | seqSelectCounter++; 173 | seqSelect = seqSelectCounter % 3; 174 | paramSelectCounter = 0; 175 | paramSelect = paramSelectCounter % 4; 176 | encoder.write(counters[seqSelect][paramSelect]); 177 | break; 178 | case AceButton::kEventDoubleClicked: 179 | paramSelectCounter++; 180 | paramSelect = paramSelectCounter % 4; 181 | encoder.write(counters[seqSelect][paramSelect]); 182 | break; 183 | case AceButton::kEventLongPressed: 184 | break; 185 | } 186 | } 187 | 188 | void setLEDS() { 189 | // sequencer LEDs 190 | for (byte i=0; i<8; i++) { 191 | if (i == indexes[seqSelect]) { leds.setPixelColor(i, 60, 60, 60); } 192 | else if (outputArrays[seqSelect][i] == 1) { 193 | if (seqSelect == 0) { leds.setPixelColor(i, 150, 0, 0); } 194 | else if (seqSelect == 1) { leds.setPixelColor(i, 0, 150, 0); } 195 | else { leds.setPixelColor(i, 0, 0, 150); } 196 | } 197 | else { leds.setPixelColor(i, 0, 0, 0); } 198 | } 199 | 200 | // Param LED 201 | if (paramSelect == 0) { leds.setPixelColor(L, 100, 0, 50); } 202 | else if (paramSelect == 1) { leds.setPixelColor(L, 50, 100, 0); } 203 | else if (paramSelect == 2) { leds.setPixelColor(L, 0, 75, 75); } 204 | else { leds.setPixelColor(L, 60, 60, 60); } 205 | } 206 | 207 | void updateParamMaxes() { 208 | for (byte row=0; row<3; row++) { 209 | for (byte col=1; col<3; col++) { 210 | maxes[row][col] = values[row][0]; 211 | } 212 | } 213 | } 214 | 215 | void updateParamValues() { 216 | for (byte row=0; row<3; row++) { 217 | for (byte col=0; col<4; col++) { 218 | values[row][col] = counters[row][col] / 4; 219 | } 220 | } 221 | } 222 | 223 | void printUpdate() { 224 | for (byte i=0; i<3; i++) { 225 | Serial.print("Sequencer "); 226 | Serial.print(i+1); 227 | Serial.print(" Params: "); 228 | Serial.print(values[i][0]); 229 | Serial.print(", "); 230 | Serial.print(values[i][1]); 231 | Serial.print(", "); 232 | Serial.print(values[i][2]); 233 | Serial.print(", "); 234 | Serial.print(values[i][3]); 235 | Serial.print(", "); 236 | Serial.print("\n"); 237 | } 238 | Serial.print("\n\n\n"); 239 | } 240 | 241 | void printSeqs() { 242 | for (byte i=0; i<3; i++) { 243 | Serial.print("Sequencer "); 244 | Serial.print(i); 245 | Serial.print(": "); 246 | for (byte j=0; j b_count) { 275 | g[0] = a_size; 276 | g[1] = a_count; 277 | g[2] = b_size; 278 | g[3] = b_count; 279 | } 280 | else { 281 | g[0] = b_size; 282 | g[1] = b_count; 283 | g[2] = a_size; 284 | g[3] = a_count; 285 | } 286 | 287 | //clear array 288 | for (byte i=0; i 0 or g[3] > 0) { 300 | outputArrays[seq][index] = 1; 301 | if (counter % c_mod == 0) { 302 | if (g[3] > 0) { 303 | index += g[2]; 304 | g[3] -= 1; 305 | } 306 | } 307 | else { 308 | if (g[1] > 0) { 309 | index += g[0]; 310 | g[1] -= 1; 311 | } 312 | } 313 | counter++; 314 | } 315 | } 316 | 317 | 318 | 319 | 320 | 321 | -------------------------------------------------------------------------------- /arduino_janky_sequencer/arduino_janky_sequencer.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * A janky sort of sequencer module. It takes a trigger and an analog signal. 3 | * When the trigger goes high it samples a 6-bit value from the analog signal and uses those bits to toggle its outputs ON/OFF. Outputs are spit out on PORTB. 4 | * It also has the option to read a second analog signal and use some bitwise operators to manipulate to outputs. 5 | * 6 | * MODULE FINISHED. No optimizations necessary. 7 | */ 8 | 9 | 10 | 11 | //pins 12 | const byte inputPins[2] = {A0, A1}; 13 | const byte modePin = A2; 14 | const byte triggerPin = 17; 15 | 16 | //state variables 17 | bool trigger = 0; 18 | byte output = 0; 19 | byte in1 = 0; 20 | byte in2 = 0; 21 | byte mode = 0; 22 | 23 | 24 | void read_trigger() { 25 | if (digitalRead(triggerPin) == true and trigger == false) { 26 | in1 = analogRead(inputPins[0]) >> 4; 27 | in2 = analogRead(inputPins[1]) >> 4; 28 | trigger = true; 29 | } 30 | else if (digitalRead(triggerPin) == false and trigger == true) { 31 | trigger = false; 32 | } 33 | } 34 | 35 | 36 | void setup() { 37 | for (byte i=8;i<14;i++) { 38 | pinMode(i, OUTPUT); 39 | } 40 | } 41 | 42 | void loop() { 43 | read_trigger(); 44 | 45 | mode = analogRead(modePin) >> 8; 46 | if (mode == 0) { output = in1; } 47 | else if (mode == 1) { output = in1 | in2; } 48 | else if (mode == 2) { output = in1 & in2; } 49 | else { output = in1 ^ in2; } 50 | 51 | PORTB = output; 52 | } 53 | -------------------------------------------------------------------------------- /arduino_quantizer/arduino_quantizer.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * A quantizer module. 3 | * - Each module contains two quantizers 4 | * - Allowed notes are determined using switches to toglle each tone and potentiometers to set the octave range 5 | * - Each quantizer requires four pins: three analog pins and one PWM pin. Plus they share the tone toggles which require twelve pins; this uses every pin on the Arduino Nano. 6 | * 7 | * THIS MODULE IS FINISHED. No optimizations necessary. 8 | */ 9 | 10 | 11 | //pins 12 | byte tonePins[12] = {2, 3, 4, 5, 6, 7, 8, 11, 12, 13, 14, 15}; 13 | 14 | //note to output value table 15 | byte toneArray[60]; 16 | 17 | 18 | class Quantizer 19 | { 20 | //pins 21 | byte inputPin, outputPin, rangeStartPin, rangeEndPin; 22 | 23 | byte startOctave, endOctave, numActiveNotes, numTotalNotes, output, index; 24 | bool notes[12]; 25 | byte quantizerArray[60]; 26 | 27 | public: 28 | Quantizer(byte pin1, byte pin2, byte pin3, byte pin4) 29 | { 30 | inputPin = pin1; 31 | outputPin = pin2; 32 | rangeStartPin = pin3; 33 | rangeEndPin = pin4; 34 | } 35 | 36 | void updateControlParams() { 37 | //update all basic parameters 38 | startOctave = map(analogRead(rangeStartPin), 0, 1023, 0, 4); 39 | endOctave = min(5, startOctave + map(analogRead(rangeEndPin), 0, 1023, 1, 5)); 40 | for (byte i=0;i<12;i++) { 41 | notes[i] = digitalRead(tonePins[i]); 42 | } 43 | 44 | numActiveNotes = 0; 45 | for (byte i=0;i<12;i++) { 46 | numActiveNotes += notes[i]; 47 | } 48 | numTotalNotes = numActiveNotes * (endOctave - startOctave); 49 | 50 | //update quantizer tone array 51 | index = 0; 52 | for (byte i=startOctave*12; i= update_control_period) { 89 | q1.updateControlParams(); 90 | q2.updateControlParams(); 91 | } 92 | 93 | q1.loopFunc(); 94 | q2.loopFunc(); 95 | } 96 | -------------------------------------------------------------------------------- /arduino_sample_or_track_and_hold/arduino_sample_or_track_and_hold.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * A sample-and-hold or track-and-hold module 3 | * - Each module contains four S&Hs 4 | * - Toggling between S&H and T&H is done with a switch 5 | * - Each S&H requires four pins: one analog pin, one PWM pin, and two GPIOs. This caps the number of S&Hs per Arduino Nano at four (16/18 pins used) without complicating the peripheral circuitry. 6 | * 7 | * THIS MODULE IS FINISHED. No optimizations necessary. 8 | */ 9 | 10 | //PINS 11 | const byte input_pins[4] = {A0, A2, A4, A6}; 12 | const byte output_pins[4] = {11, 10, 9, 3}; 13 | const byte trigger_pins[4] = {15, 17, 19, 21}; 14 | const byte mode_pins[4] = {5, 6, 7, 12}; 15 | 16 | bool toggles[4] = {0, 0, 0, 0}; 17 | bool triggers[4] = {0, 0, 0, 0}; 18 | byte output_values[4] = {0, 0, 0, 0}; 19 | 20 | 21 | void setup() { 22 | TCCR0B = TCCR0B & B11111000 | B00000010; // set PWM freq on pin 5/6 to ~7.8kHz 23 | TCCR1B = TCCR1B & B11111000 | B00000010; // set PWM freq on pin 9/10 to ~3.9kHz 24 | TCCR2B = TCCR2B & B11111000 | B00000010; // set PWM freq on pin 3/11 to ~3.9kHz 25 | } 26 | 27 | void loop() { 28 | for (byte i=0;i<4;i++) { 29 | //read pins 30 | bool mode = digitalRead(mode_pins[i]); 31 | triggers[i] = digitalRead(trigger_pins[i]); 32 | 33 | // S&H mode 34 | if (mode == 0) { 35 | if ((toggles[i] == 0) && (triggers[i] == 1)) { 36 | output_values[i] = analogRead(input_pins[i]) >> 2; 37 | toggles[i] = 1; 38 | } 39 | else if ((toggles[i] == 1) && (triggers[i] == 0)) { 40 | toggles[i] = 0; 41 | } 42 | } 43 | 44 | //T&H mode 45 | else { 46 | if (triggers[i] == 1) { 47 | output_values[i] = analogRead(input_pins[i]) >> 2; 48 | } 49 | } 50 | analogWrite(output_pins[i], output_values[i]); 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /arduino_squarewave_clock_trigger_source/arduino_squarewave_clock_trigger_source.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * A square wave module with audio and LFO frequency modes. Usable as a clock/gate/trigger source or a square wave tone. 3 | * - Each module contains four square wave oscillators. 4 | * - Frequency is controlled by a potentiometer and a switch. Range is ~0.25Hz to 10Hz in LFO mode and ~30Hz to ~500Hz in audio mode 5 | * - Each oscillator requires three pins: one analog pin and two GPIO. 6 | * 7 | * THIS MODULE IS FINISHED. No optimizations necessary. 8 | * 9 | * EDIT! V2 WILL SACRIFICE TWO OSCILLATORS FOR PULSE WIDTH CONTROL! I think this will be super nice to have for the LFO and also a pretty fun option for the VCO. 10 | */ 11 | 12 | 13 | class SquareWaveClock 14 | { 15 | //pins 16 | byte periodPin, modePin, outputPin; 17 | 18 | //state variables 19 | unsigned long previousMillis; 20 | int period; 21 | bool mode, output; 22 | 23 | public: 24 | SquareWaveClock(byte pin1, byte pin2, byte pin3) 25 | { 26 | periodPin = pin1; 27 | modePin = pin2; 28 | outputPin = pin3; 29 | } 30 | 31 | void toggleOutput() { 32 | if (output==0) { output = 1; } 33 | else { output = 0; } 34 | } 35 | 36 | void updateControlParams() { 37 | mode = digitalRead(modePin); 38 | if (mode == 0) { period = 4196 - (analogRead(periodPin) << 2); } 39 | else { period = 34 - (analogRead(periodPin) >> 5); } 40 | } 41 | 42 | void setupFunc() { 43 | pinMode(outputPin, OUTPUT); 44 | pinMode(modePin, INPUT); 45 | previousMillis = millis(); 46 | output = LOW; 47 | } 48 | 49 | void loopFunc() { 50 | if (millis() - previousMillis >= period) { 51 | toggleOutput(); 52 | previousMillis = millis(); 53 | } 54 | digitalWrite(outputPin, output); 55 | } 56 | 57 | /* 58 | void debug() { 59 | Serial.print("MODE: "); 60 | Serial.print(mode); 61 | Serial.print("\n"); 62 | Serial.print("PERIOD"); 63 | Serial.print(period); 64 | Serial.print("\n"); 65 | Serial.print("PREVIOUS MILLIS VALUE: "); 66 | Serial.print(previousMillis); 67 | Serial.print("\n"); 68 | Serial.print("\n"); 69 | Serial.print("\n"); 70 | } 71 | */ 72 | }; 73 | 74 | 75 | 76 | SquareWaveClock swc1(A0, 2, 3); 77 | SquareWaveClock swc2(A1, 4, 5); 78 | SquareWaveClock swc3(A2, 6, 7); 79 | SquareWaveClock swc4(A3, 8, 9); 80 | SquareWaveClock swc5(A4, 10, 11); 81 | SquareWaveClock swc6(A5, 12, 13); 82 | 83 | 84 | unsigned long update_control_timer = 0; 85 | int update_control_period = 30; 86 | 87 | //int debug_counter = 0; 88 | 89 | void setup() { 90 | swc1.setupFunc(); 91 | swc2.setupFunc(); 92 | swc3.setupFunc(); 93 | swc4.setupFunc(); 94 | swc5.setupFunc(); 95 | swc6.setupFunc(); 96 | update_control_timer = millis(); 97 | //Serial.begin(9600); 98 | } 99 | 100 | void loop() { 101 | swc1.loopFunc(); 102 | swc2.loopFunc(); 103 | swc3.loopFunc(); 104 | swc4.loopFunc(); 105 | swc5.loopFunc(); 106 | swc6.loopFunc(); 107 | 108 | if (millis() - update_control_timer >= update_control_period) { 109 | swc1.updateControlParams(); 110 | swc2.updateControlParams(); 111 | swc3.updateControlParams(); 112 | swc4.updateControlParams(); 113 | swc5.updateControlParams(); 114 | swc6.updateControlParams(); 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /arduino_squarewave_clock_trigger_source_V2/arduino_squarewave_clock_trigger_source_V2.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * A square wave module with audio and LFO frequency modes. Usable as a clock/gate/trigger source or a square wave tone. 3 | * - Each module contains four square wave oscillators. 4 | * - Frequency is controlled by a potentiometer and a switch. Range is ~0.25Hz to 10Hz in LFO mode and ~30Hz to ~500Hz in audio mode 5 | * - Each oscillator requires three pins: one analog pin and two GPIO. 6 | * 7 | * THIS MODULE IS FINISHED. No optimizations necessary. 8 | * 9 | * EDIT! V2 WILL SACRIFICE TWO OSCILLATORS FOR PULSE WIDTH CONTROL! I think this will be super nice to have for the LFO and also a pretty fun option for the VCO. 10 | */ 11 | 12 | 13 | class SquareWaveClock 14 | { 15 | //pins 16 | byte periodPin, modePin, outputPin, dutyCyclePin; 17 | 18 | //state variables 19 | unsigned long previousMillis; 20 | int period, low, high, dutyCycle; 21 | bool mode, output; 22 | 23 | public: 24 | SquareWaveClock(byte pin1, byte pin2, byte pin3, byte pin4) 25 | { 26 | periodPin = pin1; 27 | modePin = pin2; 28 | outputPin = pin3; 29 | dutyCyclePin = pin4; 30 | } 31 | 32 | void toggleOutput() { 33 | if (output==0) { output = 1; } 34 | else { output = 0; } 35 | } 36 | 37 | void updateControlParams() { 38 | mode = digitalRead(modePin); 39 | if (mode == 0) { period = 4196 - (analogRead(periodPin) << 2); } 40 | else { period = 34 - (analogRead(periodPin) >> 5); } 41 | 42 | high = map(analogRead(dutyCyclePin), 0, 1023, 0, period); 43 | low = period - high; 44 | } 45 | 46 | void setupFunc() { 47 | pinMode(outputPin, OUTPUT); 48 | pinMode(modePin, INPUT); 49 | previousMillis = millis(); 50 | output = LOW; 51 | } 52 | 53 | void loopFunc() { 54 | if (output == LOW) { 55 | if (millis() - previousMillis >= low) { 56 | toggleOutput(); 57 | previousMillis = millis(); 58 | } 59 | } 60 | else { 61 | if (millis() - previousMillis >= high) { 62 | toggleOutput(); 63 | previousMillis = millis(); 64 | } 65 | } 66 | 67 | digitalWrite(outputPin, output); 68 | } 69 | 70 | /* 71 | void debug() { 72 | Serial.print("MODE: "); 73 | Serial.print(mode); 74 | Serial.print("\n"); 75 | Serial.print("PERIOD"); 76 | Serial.print(period); 77 | Serial.print("\n"); 78 | Serial.print("PREVIOUS MILLIS VALUE: "); 79 | Serial.print(previousMillis); 80 | Serial.print("\n"); 81 | Serial.print("\n"); 82 | Serial.print("\n"); 83 | } 84 | */ 85 | }; 86 | 87 | 88 | 89 | SquareWaveClock swc1(A0, 2, 3, A4); 90 | SquareWaveClock swc2(A1, 4, 5, A5); 91 | SquareWaveClock swc3(A2, 6, 7, A6); 92 | SquareWaveClock swc4(A3, 8, 9, A7); 93 | 94 | 95 | unsigned long update_control_timer = 0; 96 | int update_control_period = 30; 97 | 98 | //int debug_counter = 0; 99 | 100 | void setup() { 101 | swc1.setupFunc(); 102 | swc2.setupFunc(); 103 | swc3.setupFunc(); 104 | swc4.setupFunc(); 105 | update_control_timer = millis(); 106 | //Serial.begin(9600); 107 | } 108 | 109 | void loop() { 110 | swc1.loopFunc(); 111 | swc2.loopFunc(); 112 | swc3.loopFunc(); 113 | swc4.loopFunc(); 114 | 115 | if (millis() - update_control_timer >= update_control_period) { 116 | swc1.updateControlParams(); 117 | swc2.updateControlParams(); 118 | swc3.updateControlParams(); 119 | swc4.updateControlParams(); 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /arduino_squarewave_clock_trigger_source_V3/arduino_squarewave_clock_trigger_source_V3.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * A square wave module with audio and LFO frequency modes. Usable as a clock/gate/trigger source or a square wave tone. 3 | * - Each module contains four square wave oscillators. 4 | * - Frequency is controlled by a potentiometer and a switch. Range is ~0.25Hz to 10Hz in LFO mode and ~30Hz to ~500Hz in audio mode 5 | * - Each oscillator requires three pins: one analog pin and two GPIO. 6 | * 7 | * THIS MODULE IS FINISHED. No optimizations necessary. 8 | * 9 | * V2: Removed two oscillators and added control over duty cycle to each. 10 | * V3: Changed the way the frequenies are calculated, using an array of preset values instead of a rough calculation. This is to give the spread of frequencies a logarithmic feel, making selecting one's desired frequency much easier. 11 | */ 12 | 13 | 14 | float note_freq[25] = {55.00, 58.27, 61.74, 65.41, 69.30, 73.42, 77.78, 82.41, 87.31, 92.50, 98.00, 103.83, 15 | 110.00, 116.54, 123.47, 130.81, 138.59, 146.83, 155.56, 164.81, 174.61, 185.00, 196.00, 207.65, 16 | 220.00}; 17 | 18 | float mapFloat(float x, float in_min, float in_max, float out_min, float out_max) { 19 | return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; 20 | } 21 | 22 | class SquareWaveClock 23 | { 24 | //pins 25 | byte periodPin, modePin, outputPin, dutyCyclePin, ledPin; 26 | 27 | //state variables 28 | unsigned long previousMillis; 29 | unsigned period, low, high, dutyCycle; 30 | bool mode, output; 31 | 32 | float freq; 33 | 34 | public: 35 | SquareWaveClock(byte pin1, byte pin2, byte pin3, byte pin4, byte pin5) 36 | { 37 | periodPin = pin1; 38 | modePin = pin2; 39 | outputPin = pin3; 40 | dutyCyclePin = pin4; 41 | ledPin = pin5; 42 | } 43 | 44 | void toggleOutput() { 45 | if (output==0) { output = 1; } 46 | else { output = 0; } 47 | } 48 | 49 | void updateControlParams() { 50 | mode = digitalRead(modePin); 51 | if (mode == 0) { 52 | period = period = 34 - (analogRead(periodPin) >> 5);; 53 | } 54 | else { 55 | freq = pow(2, mapFloat(analogRead(periodPin), 0, 1023, -1, 3)); 56 | period = round((1000 * (1/freq))); 57 | } 58 | 59 | high = map(analogRead(dutyCyclePin), 0, 1023, 0, period); 60 | low = period - high; 61 | } 62 | 63 | void setupFunc() { 64 | pinMode(outputPin, OUTPUT); 65 | pinMode(ledPin, OUTPUT); 66 | pinMode(modePin, INPUT); 67 | previousMillis = millis(); 68 | output = LOW; 69 | } 70 | 71 | void loopFunc() { 72 | if (output == LOW) { 73 | if (millis() - previousMillis >= low) { 74 | toggleOutput(); 75 | previousMillis = millis(); 76 | } 77 | } 78 | else { 79 | if (millis() - previousMillis >= high) { 80 | toggleOutput(); 81 | previousMillis = millis(); 82 | } 83 | } 84 | 85 | digitalWrite(outputPin, output); 86 | digitalWrite(ledPin, output); 87 | } 88 | 89 | /* 90 | void debug() { 91 | Serial.print("MODE: "); 92 | Serial.print(mode); 93 | Serial.print("\n"); 94 | Serial.print("PERIOD"); 95 | Serial.print(period); 96 | Serial.print("\n"); 97 | Serial.print("PREVIOUS MILLIS VALUE: "); 98 | Serial.print(previousMillis); 99 | Serial.print("\n"); 100 | Serial.print("\n"); 101 | Serial.print("\n"); 102 | } 103 | */ 104 | }; 105 | 106 | 107 | 108 | SquareWaveClock swc1(A0, 2, 3, A1, 4); 109 | SquareWaveClock swc2(A2, 5, 6, A3, 7); 110 | SquareWaveClock swc3(A4, 8, 9, A5, 10); 111 | SquareWaveClock swc4(A6, 11, 12, A7, 13); 112 | 113 | 114 | unsigned long update_control_timer = 0; 115 | int update_control_period = 30; 116 | 117 | //int debug_counter = 0; 118 | 119 | void setup() { 120 | swc1.setupFunc(); 121 | swc2.setupFunc(); 122 | swc3.setupFunc(); 123 | swc4.setupFunc(); 124 | update_control_timer = millis(); 125 | //Serial.begin(9600); 126 | } 127 | 128 | void loop() { 129 | swc1.loopFunc(); 130 | swc2.loopFunc(); 131 | swc3.loopFunc(); 132 | swc4.loopFunc(); 133 | 134 | if (millis() - update_control_timer >= update_control_period) { 135 | swc1.updateControlParams(); 136 | swc2.updateControlParams(); 137 | swc3.updateControlParams(); 138 | swc4.updateControlParams(); 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /arduino_two_mode_sequencer/arduino_two_mode_sequencer.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | */ 4 | 5 | 6 | #include 7 | #include 8 | #include 9 | using namespace ace_button; 10 | 11 | #include 12 | #include 13 | 14 | // Neopixel variables 15 | const byte neoPixelPin = 9; 16 | const byte neoPixelCount = 9; 17 | Adafruit_NeoPixel leds(neoPixelCount, neoPixelPin, NEO_RGB + NEO_KHZ800); 18 | 19 | // Rotary encoder variables 20 | Encoder encoder(2, 3); 21 | const byte L = neoPixelCount - 1; //max sequence length 22 | byte counters[3][4] = {{4, 0, 0, 4}, 23 | {4, 0, 0, 4}, 24 | {4, 0, 0, 4}}; 25 | byte values[3][4] = {{1, 0, 0, 1}, // {seq length, beats, offset, clk divider} 26 | {1, 0, 0, 1}, 27 | {1, 0, 0, 1}}; 28 | byte maxes[3][4] = {{L, L, L, L}, 29 | {L, L, L, L}, 30 | {L, L, L, L}}; 31 | byte mins[3][4] = {{1, 0, 0, 1}, 32 | {1, 0, 0, 1}, 33 | {1, 0, 0, 1}}; 34 | 35 | //button variables 36 | const byte buttonPin = 4; 37 | AceButton button(buttonPin); 38 | unsigned long seqSelectCounter = 0; 39 | byte seqSelect = 0; 40 | unsigned long paramSelectCounter = 0; 41 | byte paramSelect = 0; 42 | void handleEvent(AceButton*, uint8_t, uint8_t); 43 | 44 | //Euclidean Sequencer variables 45 | bool outputArrays[3][L] = {{0, 0, 0, 0, 0, 0, 0, 0}, 46 | {0, 0, 0, 0, 0, 0, 0, 0}, 47 | {0, 0, 0, 0, 0, 0, 0, 0}}; 48 | byte outputIndexes[3] = {0, 0, 0}; 49 | unsigned long outputClockCounters[3] = {0, 0, 0}; 50 | 51 | //trigger variables 52 | const byte outputPins[3] = {6, 7, 8}; 53 | const byte triggerPin = 12; 54 | bool trigger = 0; 55 | byte triggerCounts[3] = {0, 0, 0}; 56 | unsigned long indexCounters[3] = {0, 0, 0}; 57 | byte indexes[3] = {0, 0, 0}; 58 | 59 | const byte modePin = 10; 60 | bool mode = 0; 61 | unsigned long beatCounter = 0; 62 | byte beatSelect = 0; 63 | bool outputArrays2[3][L] = {{0, 0, 0, 0, 0, 0, 0, 0}, 64 | {0, 0, 0, 0, 0, 0, 0, 0}, 65 | {0, 0, 0, 0, 0, 0, 0, 0}}; 66 | byte indexes2[3] = {0, 0, 0}; 67 | 68 | 69 | unsigned long update_control_timer = 0; 70 | int update_control_period = 100; 71 | 72 | 73 | 74 | void setup() { 75 | for (byte i=0;i<3;i++) { 76 | pinMode(outputPins[i], OUTPUT); 77 | } 78 | 79 | leds.begin(); 80 | leds.show(); 81 | 82 | pinMode(buttonPin, INPUT_PULLUP); 83 | button.init(buttonPin, HIGH); 84 | ButtonConfig* buttonConfig = button.getButtonConfig(); 85 | buttonConfig->setEventHandler(handleEvent); 86 | buttonConfig->setFeature(ButtonConfig::kFeatureDoubleClick); 87 | buttonConfig->setFeature( 88 | ButtonConfig::kFeatureSuppressClickBeforeDoubleClick); 89 | buttonConfig->setFeature(ButtonConfig::kFeatureSuppressAfterClick); 90 | buttonConfig->setFeature(ButtonConfig::kFeatureSuppressAfterDoubleClick); 91 | 92 | Serial.begin(115200); 93 | } 94 | 95 | 96 | void loop() { 97 | 98 | //mode read block 99 | bool newRead = digitalRead(modePin); 100 | if (newRead == HIGH and mode == LOW) { 101 | encoder.write(0); 102 | } 103 | else if (newRead == LOW and mode == HIGH) { 104 | encoder.write(counters[seqSelect][paramSelect]); 105 | } 106 | mode = newRead; 107 | 108 | //Button block 109 | button.check(); 110 | 111 | //Rotary encoder block 112 | long newPosition = encoder.read(); 113 | 114 | if (mode == 0) { 115 | byte x = maxes[seqSelect][paramSelect] << 2; 116 | byte y = mins[seqSelect][paramSelect] << 2; 117 | if (newPosition > x) { 118 | newPosition = x; 119 | encoder.write(x); 120 | } 121 | else if (newPosition < y) { 122 | newPosition = y; 123 | encoder.write(y); 124 | } 125 | 126 | if (newPosition != counters[seqSelect][paramSelect]) { 127 | counters[seqSelect][paramSelect] = newPosition; 128 | //printUpdate(); 129 | //printSeqs(); 130 | } 131 | } 132 | else { 133 | if (newPosition != beatCounter) { 134 | beatCounter = newPosition; 135 | beatSelect = (beatCounter >> 2) % L; 136 | //printUpdate(); 137 | //printSeqs(); 138 | } 139 | } 140 | 141 | 142 | updateParamValues(); 143 | updateParamMaxes(); 144 | 145 | for (byte i=0; i<3; i++) { 146 | if (values[i][1] == 0) { clearOutputArray(i); } 147 | else { setOutputArray(i); } 148 | } 149 | 150 | 151 | //trigger block 152 | bool t = digitalRead(triggerPin); 153 | if (t == HIGH and trigger == LOW) { 154 | trigger = HIGH; 155 | if (mode == 0) { 156 | for (byte i=0;i<3;i++) { 157 | triggerCounts[i]++; 158 | if (triggerCounts[i] >= values[i][3]) { // if trigger counter for seq is equal to the clk divider inc index and reset to zero 159 | triggerCounts[i] = 0; 160 | indexCounters[i]++; 161 | } 162 | } 163 | } 164 | else { 165 | for (byte i=0;i<3;i++) { 166 | indexes2[i]++; 167 | if (indexes2[i] >= L) { indexes2[i] = 0; } 168 | } 169 | } 170 | } 171 | 172 | else if (t == LOW and trigger == HIGH) { 173 | trigger = LOW; 174 | } 175 | for (byte i=0; i<3; i++) { 176 | indexes[i] = (indexCounters[i] + values[i][2]) % values[i][0]; 177 | } 178 | 179 | 180 | //NeoPixel block 181 | if (millis() - update_control_timer >= update_control_period) { 182 | setLEDS(); 183 | leds.show(); 184 | } 185 | 186 | //output block 187 | if (mode == 0) { 188 | for (byte i=0;i<3;i++) { 189 | if (outputArrays[i][indexes[i]] == 1) { 190 | digitalWrite(outputPins[i], HIGH); 191 | } 192 | else { 193 | digitalWrite(outputPins[i], LOW); 194 | } 195 | } 196 | } 197 | else { 198 | for (byte i=0;i<3;i++) { 199 | if (outputArrays2[i][indexes2[i]] == 1) { 200 | digitalWrite(outputPins[i], HIGH); 201 | } 202 | else { 203 | digitalWrite(outputPins[i], LOW); 204 | } 205 | } 206 | } 207 | 208 | } 209 | 210 | 211 | 212 | //////////////////////////////////////////////////////// 213 | /* 214 | * FUNCTIONS 215 | */ 216 | 217 | void toggleBeat() { 218 | if (outputArrays2[seqSelect][beatSelect] == 0) { 219 | outputArrays2[seqSelect][beatSelect] = 1; 220 | } 221 | else { 222 | outputArrays2[seqSelect][beatSelect] = 0; 223 | } 224 | } 225 | 226 | // The event handler for the button. 227 | void handleEvent(AceButton* /* button */, uint8_t eventType, 228 | uint8_t buttonState) { 229 | switch (eventType) { 230 | case AceButton::kEventClicked: 231 | break; 232 | case AceButton::kEventReleased: 233 | seqSelectCounter++; 234 | seqSelect = seqSelectCounter % 3; 235 | if (mode == 0) { 236 | paramSelectCounter = 0; 237 | paramSelect = paramSelectCounter % 4; 238 | encoder.write(counters[seqSelect][paramSelect]); 239 | } 240 | break; 241 | case AceButton::kEventDoubleClicked: 242 | if (mode == 0) { 243 | paramSelectCounter++; 244 | paramSelect = paramSelectCounter % 4; 245 | encoder.write(counters[seqSelect][paramSelect]); 246 | } 247 | else { 248 | toggleBeat(); 249 | } 250 | break; 251 | case AceButton::kEventLongPressed: 252 | break; 253 | } 254 | } 255 | 256 | void setLEDS() { 257 | // sequencer LEDs 258 | if (mode == 0) { 259 | for (byte i=0; i b_count) { 364 | g[0] = a_size; 365 | g[1] = a_count; 366 | g[2] = b_size; 367 | g[3] = b_count; 368 | } 369 | else { 370 | g[0] = b_size; 371 | g[1] = b_count; 372 | g[2] = a_size; 373 | g[3] = a_count; 374 | } 375 | 376 | //clear array 377 | for (byte i=0; i 0 or g[3] > 0) { 389 | outputArrays[seq][index] = 1; 390 | if (counter % c_mod == 0) { 391 | if (g[3] > 0) { 392 | index += g[2]; 393 | g[3] -= 1; 394 | } 395 | } 396 | else { 397 | if (g[1] > 0) { 398 | index += g[0]; 399 | g[1] -= 1; 400 | } 401 | } 402 | counter++; 403 | } 404 | } 405 | 406 | 407 | 408 | 409 | 410 | -------------------------------------------------------------------------------- /mozzi_dual_drone_VCO_VCA/mozzi_dual_drone_VCO_VCA.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * A VCO+VCA module. 3 | * - Each module contains a 14-bit VCO with six different waveforms and a VCA 4 | * - Requires four pins: two analog pins and two PWM pins for Mozzi HI-FI output mode. 5 | * 6 | * THIS MODULE IS FINISHED (for now). 7 | * - No optimizations are necessary, but I might wan to add more waveform types or even add a second VCO+VCA and just sum the outputs. 8 | * This would lower the resolution to 13-bit and wonkify the volume some but it would be really easy to code. 9 | */ 10 | 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | 24 | //PINS! 25 | const byte analog_inputs[2][5] = {{A0, A1, A2}, 26 | {A3, A4, A2}}; // {tone, volume, waveform} 27 | const byte mode_inputs[2] = {5, 6}; 28 | 29 | //global variables 30 | int freqs[2] = {0, 0}; 31 | byte volumes[2] = {0, 0}; 32 | byte waveforms[2] = {0, 0}; 33 | bool modes[2] = {0, 0}; 34 | 35 | //oscillator 36 | Oscil <2048, AUDIO_RATE> oscillators[2]; 37 | 38 | //maps 39 | AutoMap freq_map(0, 1023, 55, 880); 40 | AutoMap tone_index_map(0, 1023, 0, 48); 41 | 42 | 43 | //Quantized frequencies array from A1 to A7 44 | float note_freq[49] = {55.00, 58.27, 61.74, 65.41, 69.30, 73.42, 77.78, 82.41, 87.31, 92.50, 98.00, 103.83, 45 | 110.00, 116.54, 123.47, 130.81, 138.59, 146.83, 155.56, 164.81, 174.61, 185.00, 196.00, 207.65, 46 | 220.00, 233.08, 246.94, 261.63, 277.18, 293.66, 311.13, 329.63, 349.23, 369.99, 392.00, 415.30, 47 | 440.00, 466.16, 493.88, 523.25, 554.37, 587.33, 622.25, 659.25, 698.46, 739.99, 783.99, 830.61, 48 | 880.00}; 49 | 50 | //Quantized frequencies array from A1 to A7 with quarter tones 51 | 52 | 53 | 54 | //Functions 55 | void set_wavetable(byte n, byte osc) { 56 | if (n==0) {oscillators[osc].setTable(SIN2048_DATA);} 57 | else if (n==1) {oscillators[osc].setTable(HALFSIN2048_DATA);} 58 | else if (n==2) {oscillators[osc].setTable(SAW2048_DATA);} 59 | else if (n==3) {oscillators[osc].setTable(SQUARE_NO_ALIAS_2048_DATA);} 60 | } 61 | 62 | int set_freq(byte osc) { 63 | if (modes[osc] == 0) { 64 | return freq_map(mozziAnalogRead(analog_inputs[osc][0])); 65 | } 66 | else { 67 | float value = note_freq[tone_index_map(mozziAnalogRead(analog_inputs[osc][0]))]; 68 | return round(value); 69 | } 70 | } 71 | 72 | 73 | void setup() { 74 | startMozzi(); 75 | } 76 | 77 | 78 | 79 | 80 | void updateControl() { 81 | for (byte i=0; i<2; i++) { 82 | //read inputs 83 | modes[i] = digitalRead(mode_inputs[i]); 84 | freqs[i] = set_freq(i); 85 | volumes[i] = mozziAnalogRead(analog_inputs[i][1]) >> 2; 86 | waveforms[i] = mozziAnalogRead(analog_inputs[i][2]) >> 8; 87 | 88 | set_wavetable(waveforms[i], i); 89 | oscillators[i].setFreq(freqs[i]); 90 | } 91 | } 92 | 93 | 94 | 95 | 96 | int updateAudio() { 97 | int audio1 = (oscillators[0].next() * volumes[0]) >> 2; 98 | int audio2 = (oscillators[1].next() * volumes[1]) >> 2; 99 | return (audio1 + audio2) >> 1; 100 | } 101 | 102 | 103 | 104 | 105 | void loop() { 106 | audioHook(); 107 | } 108 | -------------------------------------------------------------------------------- /mozzi_dual_mode_VCO_with_VCA/halfsin2048_int8.h: -------------------------------------------------------------------------------- 1 | #ifndef HALFSIN2048_H_ 2 | #define HALFSIN2048_H_ 3 | 4 | #if ARDUINO >= 100 5 | #include "Arduino.h" 6 | #else 7 | #include "WProgram.h" 8 | #endif 9 | #include "mozzi_pgmspace.h" 10 | 11 | #define HALFSIN2048_NUM_CELLS 2048 12 | #define HALFSIN2048_SAMPLERATE 2048 13 | 14 | CONSTTABLE_STORAGE(int8_t) HALFSIN2048_DATA [] = 15 | { 16 | -128, -128, -128, -127, -127, -127, -126, -126, -125, -125, -125, -124, -124, -123, -123, -123, -122, -122, -121, -121, -121, -120, -120, -120, -119, -119, -118, -118, -118, -117, -117, -116, -116, -116, -115, -115, -114, -114, -114, -113, -113, -112, -112, -112, -111, -111, -111, -110, -110, -109, -109, -109, -108, -108, -107, -107, -107, -106, -106, -105, -105, -105, -104, -104, -104, -103, -103, -102, -102, -102, -101, -101, -100, -100, -100, -99, -99, -98, -98, -98, -97, -97, -97, -96, -96, -95, -95, -95, -94, -94, -93, -93, -93, -92, -92, -91, -91, -91, -90, -90, -90, -89, -89, -88, -88, -88, -87, -87, -86, -86, -86, -85, -85, -85, -84, -84, -83, -83, -83, -82, -82, -81, -81, -81, -80, -80, -80, -79, -79, -78, -78, -78, -77, -77, -76, -76, -76, -75, -75, -75, -74, -74, -73, -73, -73, -72, -72, -71, -71, -71, -70, -70, -70, -69, -69, -68, -68, -68, -67, -67, -67, -66, -66, -65, -65, -65, -64, -64, -64, -63, -63, -62, -62, -62, -61, -61, -60, -60, -60, -59, -59, -59, -58, -58, -57, -57, -57, -56, -56, -56, -55, -55, -54, -54, -54, -53, -53, -53, -52, -52, -51, -51, -51, -50, -50, -50, -49, -49, -49, -48, -48, -47, -47, -47, -46, -46, -46, -45, -45, -44, -44, -44, -43, -43, -43, -42, -42, -41, -41, -41, -40, -40, -40, -39, -39, -39, -38, -38, -37, -37, -37, -36, -36, -36, -35, -35, -35, -34, -34, -33, -33, -33, -32, -32, -32, -31, -31, -31, -30, -30, -29, -29, -29, -28, -28, -28, -27, -27, -27, -26, -26, -26, -25, -25, -24, -24, -24, -23, -23, -23, -22, -22, -22, -21, -21, -21, -20, -20, -19, -19, -19, -18, -18, -18, -17, -17, -17, -16, -16, -16, -15, -15, -15, -14, -14, -13, -13, -13, -12, -12, -12, -11, -11, -11, -10, -10, -10, -9, -9, -9, -8, -8, -8, -7, -7, -7, -6, -6, -6, -5, -5, -5, -4, -4, -3, -3, -3, -2, -2, -2, -1, -1, -1, 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 6, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10, 11, 11, 11, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 15, 15, 15, 16, 16, 16, 17, 17, 17, 18, 18, 18, 19, 19, 19, 20, 20, 20, 21, 21, 21, 22, 22, 22, 22, 23, 23, 23, 24, 24, 24, 25, 25, 25, 26, 26, 26, 27, 27, 27, 27, 28, 28, 28, 29, 29, 29, 30, 30, 30, 31, 31, 31, 31, 32, 32, 32, 33, 33, 33, 34, 34, 34, 34, 35, 35, 35, 36, 36, 36, 37, 37, 37, 37, 38, 38, 38, 39, 39, 39, 40, 40, 40, 40, 41, 41, 41, 42, 42, 42, 42, 43, 43, 43, 44, 44, 44, 44, 45, 45, 45, 46, 46, 46, 46, 47, 47, 47, 48, 48, 48, 48, 49, 49, 49, 50, 50, 50, 50, 51, 51, 51, 52, 52, 52, 52, 53, 53, 53, 53, 54, 54, 54, 55, 55, 55, 55, 56, 56, 56, 56, 57, 57, 57, 58, 58, 58, 58, 59, 59, 59, 59, 60, 60, 60, 60, 61, 61, 61, 61, 62, 62, 62, 63, 63, 63, 63, 64, 64, 64, 64, 65, 65, 65, 65, 66, 66, 66, 66, 67, 67, 67, 67, 68, 68, 68, 68, 69, 69, 69, 69, 70, 70, 70, 70, 71, 71, 71, 71, 72, 72, 72, 72, 73, 73, 73, 73, 73, 74, 74, 74, 74, 75, 75, 75, 75, 76, 76, 76, 76, 77, 77, 77, 77, 77, 78, 78, 78, 78, 79, 79, 79, 79, 80, 80, 80, 80, 80, 81, 81, 81, 81, 82, 82, 82, 82, 82, 83, 83, 83, 83, 84, 84, 84, 84, 84, 85, 85, 85, 85, 85, 86, 86, 86, 86, 87, 87, 87, 87, 87, 88, 88, 88, 88, 88, 89, 89, 89, 89, 89, 90, 90, 90, 90, 90, 91, 91, 91, 91, 91, 92, 92, 92, 92, 92, 93, 93, 93, 93, 93, 94, 94, 94, 94, 94, 95, 95, 95, 95, 95, 95, 96, 96, 96, 96, 96, 97, 97, 97, 97, 97, 97, 98, 98, 98, 98, 98, 99, 99, 99, 99, 99, 99, 100, 100, 100, 100, 100, 100, 101, 101, 101, 101, 101, 102, 102, 102, 102, 102, 102, 103, 103, 103, 103, 103, 103, 104, 104, 104, 104, 104, 104, 104, 105, 105, 105, 105, 105, 105, 106, 106, 106, 106, 106, 106, 106, 107, 107, 107, 107, 107, 107, 108, 108, 108, 108, 108, 108, 108, 109, 109, 109, 109, 109, 109, 109, 110, 110, 110, 110, 110, 110, 110, 111, 111, 111, 111, 111, 111, 111, 111, 112, 112, 112, 112, 112, 112, 112, 113, 113, 113, 113, 113, 113, 113, 113, 114, 114, 114, 114, 114, 114, 114, 114, 114, 115, 115, 115, 115, 115, 115, 115, 115, 116, 116, 116, 116, 116, 116, 116, 116, 116, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 127, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 122, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, 116, 116, 116, 116, 116, 116, 116, 116, 116, 115, 115, 115, 115, 115, 115, 115, 115, 114, 114, 114, 114, 114, 114, 114, 114, 114, 113, 113, 113, 113, 113, 113, 113, 113, 112, 112, 112, 112, 112, 112, 112, 111, 111, 111, 111, 111, 111, 111, 111, 110, 110, 110, 110, 110, 110, 110, 109, 109, 109, 109, 109, 109, 109, 108, 108, 108, 108, 108, 108, 108, 107, 107, 107, 107, 107, 107, 106, 106, 106, 106, 106, 106, 106, 105, 105, 105, 105, 105, 105, 104, 104, 104, 104, 104, 104, 104, 103, 103, 103, 103, 103, 103, 102, 102, 102, 102, 102, 102, 101, 101, 101, 101, 101, 100, 100, 100, 100, 100, 100, 99, 99, 99, 99, 99, 99, 98, 98, 98, 98, 98, 97, 97, 97, 97, 97, 97, 96, 96, 96, 96, 96, 95, 95, 95, 95, 95, 95, 94, 94, 94, 94, 94, 93, 93, 93, 93, 93, 92, 92, 92, 92, 92, 91, 91, 91, 91, 91, 90, 90, 90, 90, 90, 89, 89, 89, 89, 89, 88, 88, 88, 88, 88, 87, 87, 87, 87, 87, 86, 86, 86, 86, 85, 85, 85, 85, 85, 84, 84, 84, 84, 84, 83, 83, 83, 83, 82, 82, 82, 82, 82, 81, 81, 81, 81, 80, 80, 80, 80, 80, 79, 79, 79, 79, 78, 78, 78, 78, 77, 77, 77, 77, 77, 76, 76, 76, 76, 75, 75, 75, 75, 74, 74, 74, 74, 73, 73, 73, 73, 73, 72, 72, 72, 72, 71, 71, 71, 71, 70, 70, 70, 70, 69, 69, 69, 69, 68, 68, 68, 68, 67, 67, 67, 67, 66, 66, 66, 66, 65, 65, 65, 65, 64, 64, 64, 64, 63, 63, 63, 63, 62, 62, 62, 61, 61, 61, 61, 60, 60, 60, 60, 59, 59, 59, 59, 58, 58, 58, 58, 57, 57, 57, 56, 56, 56, 56, 55, 55, 55, 55, 54, 54, 54, 53, 53, 53, 53, 52, 52, 52, 52, 51, 51, 51, 50, 50, 50, 50, 49, 49, 49, 48, 48, 48, 48, 47, 47, 47, 46, 46, 46, 46, 45, 45, 45, 44, 44, 44, 44, 43, 43, 43, 42, 42, 42, 42, 41, 41, 41, 40, 40, 40, 40, 39, 39, 39, 38, 38, 38, 37, 37, 37, 37, 36, 36, 36, 35, 35, 35, 34, 34, 34, 34, 33, 33, 33, 32, 32, 32, 31, 31, 31, 31, 30, 30, 30, 29, 29, 29, 28, 28, 28, 27, 27, 27, 27, 26, 26, 26, 25, 25, 25, 24, 24, 24, 23, 23, 23, 22, 22, 22, 22, 21, 21, 21, 20, 20, 20, 19, 19, 19, 18, 18, 18, 17, 17, 17, 16, 16, 16, 15, 15, 15, 14, 14, 14, 13, 13, 13, 13, 12, 12, 12, 11, 11, 11, 10, 10, 10, 9, 9, 9, 8, 8, 8, 7, 7, 7, 6, 6, 6, 5, 5, 5, 4, 4, 4, 3, 3, 3, 2, 2, 2, 1, 1, 1, 0, 0, 0, -1, -1, -1, -2, -2, -2, -3, -3, -3, -4, -4, -5, -5, -5, -6, -6, -6, -7, -7, -7, -8, -8, -8, -9, -9, -9, -10, -10, -10, -11, -11, -11, -12, -12, -12, -13, -13, -13, -14, -14, -15, -15, -15, -16, -16, -16, -17, -17, -17, -18, -18, -18, -19, -19, -19, -20, -20, -21, -21, -21, -22, -22, -22, -23, -23, -23, -24, -24, -24, -25, -25, -26, -26, -26, -27, -27, -27, -28, -28, -28, -29, -29, -29, -30, -30, -31, -31, -31, -32, -32, -32, -33, -33, -33, -34, -34, -35, -35, -35, -36, -36, -36, -37, -37, -37, -38, -38, -39, -39, -39, -40, -40, -40, -41, -41, -41, -42, -42, -43, -43, -43, -44, -44, -44, -45, -45, -46, -46, -46, -47, -47, -47, -48, -48, -49, -49, -49, -50, -50, -50, -51, -51, -51, -52, -52, -53, -53, -53, -54, -54, -54, -55, -55, -56, -56, -56, -57, -57, -57, -58, -58, -59, -59, -59, -60, -60, -60, -61, -61, -62, -62, -62, -63, -63, -64, -64, -64, -65, -65, -65, -66, -66, -67, -67, -67, -68, -68, -68, -69, -69, -70, -70, -70, -71, -71, -71, -72, -72, -73, -73, -73, -74, -74, -75, -75, -75, -76, -76, -76, -77, -77, -78, -78, -78, -79, -79, -80, -80, -80, -81, -81, -81, -82, -82, -83, -83, -83, -84, -84, -85, -85, -85, -86, -86, -86, -87, -87, -88, -88, -88, -89, -89, -90, -90, -90, -91, -91, -91, -92, -92, -93, -93, -93, -94, -94, -95, -95, -95, -96, -96, -97, -97, -97, -98, -98, -98, -99, -99, -100, -100, -100, -101, -101, -102, -102, -102, -103, -103, -104, -104, -104, -105, -105, -105, -106, -106, -107, -107, -107, -108, -108, -109, -109, -109, -110, -110, -111, -111, -111, -112, -112, -112, -113, -113, -114, -114, -114, -115, -115, -116, -116, -116, -117, -117, -118, -118, -118, -119, -119, -120, -120, -120, -121, -121, -121, -122, -122, -123, -123, -123, -124, -124, -125, -125, -125, -126, -126, -127, -127, -127, -128, -128 17 | }; 18 | 19 | #endif /* HALFSIN2048_H_ */ 20 | -------------------------------------------------------------------------------- /mozzi_dual_mode_VCO_with_VCA/mozzi_dual_mode_VCO_with_VCA.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * A VCO+VCA module. 3 | * - Each module contains a 14-bit VCO with six different waveforms and a VCA 4 | * - Requires four pins: two analog pins and two PWM pins for Mozzi HI-FI output mode. 5 | * 6 | * THIS MODULE IS FINISHED (for now). 7 | * - No optimizations are necessary, but I might wan to add more waveform types or even add a second VCO+VCA and just sum the outputs. 8 | * This would lower the resolution to 13-bit and wonkify the volume some but it would be really easy to code. 9 | */ 10 | 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include 19 | #include 20 | #include 21 | #include ` 22 | 23 | 24 | //PINS! 25 | const byte analog_inputs[2][3] = {{A1, A0, A2}, 26 | {A4, A3, A2}}; // {tone, volume, waveform} 27 | const byte mode_inputs[2] = {5, 6}; 28 | 29 | //global variables 30 | int freqs[2] = {0, 0}; 31 | byte volumes[2] = {0, 0}; 32 | byte waveforms[2] = {0, 0}; 33 | bool modes[2] = {0, 0}; 34 | 35 | //oscillator 36 | Oscil <2048, AUDIO_RATE> oscillators[2]; 37 | 38 | //maps 39 | AutoMap freq_map(0, 1023, 55, 880); 40 | AutoMap tone_index_map(0, 1023, 0, 48); 41 | 42 | 43 | //Quantized frequencies array from A1 to A7 44 | float note_freq[49] = {55.00, 58.27, 61.74, 65.41, 69.30, 73.42, 77.78, 82.41, 87.31, 92.50, 98.00, 103.83, 45 | 110.00, 116.54, 123.47, 130.81, 138.59, 146.83, 155.56, 164.81, 174.61, 185.00, 196.00, 207.65, 46 | 220.00, 233.08, 246.94, 261.63, 277.18, 293.66, 311.13, 329.63, 349.23, 369.99, 392.00, 415.30, 47 | 440.00, 466.16, 493.88, 523.25, 554.37, 587.33, 622.25, 659.25, 698.46, 739.99, 783.99, 830.61, 48 | 880.00}; 49 | 50 | //Quantized frequencies array from A1 to A7 with quarter tones 51 | 52 | 53 | 54 | //Functions 55 | void set_wavetable(byte n, byte osc) { 56 | if (n==0) {oscillators[osc].setTable(SIN2048_DATA);} 57 | else if (n==1) {oscillators[osc].setTable(HALFSIN2048_DATA);} 58 | else if (n==2) {oscillators[osc].setTable(SAW2048_DATA);} 59 | else if (n==3) {oscillators[osc].setTable(SQUARE_NO_ALIAS_2048_DATA);} 60 | } 61 | 62 | int set_freq(byte osc) { 63 | if (modes[osc] == 0) { 64 | return freq_map(mozziAnalogRead(analog_inputs[osc][0])); 65 | } 66 | else { 67 | float value = note_freq[tone_index_map(mozziAnalogRead(analog_inputs[osc][0]))]; 68 | return round(value); 69 | } 70 | } 71 | 72 | 73 | void setup() { 74 | startMozzi(); 75 | } 76 | 77 | 78 | 79 | 80 | void updateControl() { 81 | for (byte i=0; i<2; i++) { 82 | //read inputs 83 | modes[i] = digitalRead(mode_inputs[i]); 84 | freqs[i] = set_freq(i); 85 | volumes[i] = mozziAnalogRead(analog_inputs[i][1]) >> 2; 86 | waveforms[i] = mozziAnalogRead(analog_inputs[i][2]) >> 8; 87 | 88 | set_wavetable(waveforms[i], i); 89 | oscillators[i].setFreq(freqs[i]); 90 | } 91 | } 92 | 93 | 94 | 95 | 96 | int updateAudio() { 97 | int audio1 = (oscillators[0].next() * volumes[0]) >> 2; 98 | int audio2 = (oscillators[1].next() * volumes[1]) >> 2; 99 | return (audio1 + audio2) >> 1; 100 | } 101 | 102 | 103 | 104 | 105 | void loop() { 106 | audioHook(); 107 | } 108 | -------------------------------------------------------------------------------- /mozzi_simpleVCO_V1/mozzi_simpleVCO_V1.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * A VCO+VCA module. 3 | * - Each module contains a 14-bit VCO with six different waveforms and a VCA 4 | * - Requires four pins: two analog pins and two PWM pins for Mozzi HI-FI output mode. 5 | * 6 | * THIS MODULE IS FINISHED (for now). 7 | * - No optimizations are necessary, but I might wan to add more waveform types or even add a second VCO+VCA and just sum the outputs. 8 | * This would lower the resolution to 13-bit and wonkify the volume some but it would be really easy to code. 9 | */ 10 | 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | 26 | //PINS! 27 | const byte analog_inputs[5] = {A0, A1, A2, A3, A4}; // {tone, volume, waveform, cutoff, resonance} 28 | const byte trigger_pin = 4; 29 | 30 | 31 | //global variables 32 | bool trigger = LOW; 33 | byte tone_index = 0; 34 | byte volume = 0; 35 | byte waveform = 0; 36 | byte cutoff = 20; 37 | byte resonance = 0; 38 | 39 | //oscillator 40 | Oscil <2048, AUDIO_RATE> oscillator; 41 | 42 | //envelope 43 | //ADSR envelope; 44 | 45 | //low pass filter 46 | //LowPassFilter lpf; // cutoff 0-255 -> 0-8192Hz; resonance 0-255 -> low-high 47 | 48 | //maps 49 | AutoMap tone_index_map(0, 1023, 0, 59); 50 | AutoMap wavetable_choice_map(0, 1023, 0, 5); 51 | 52 | 53 | //Quantized frequencies array from A1 to A7 54 | float note_freq[60] = {55.00, 58.27, 61.74, 65.41, 69.30, 73.42, 77.78, 82.41, 87.31, 92.50, 98.00, 103.83, 55 | 110.00, 116.54, 123.47, 130.81, 138.59, 146.83, 155.56, 164.81, 174.61, 185.00, 196.00, 207.65, 56 | 220.00, 233.08, 246.94, 261.63, 277.18, 293.66, 311.13, 329.63, 349.23, 369.99, 392.00, 415.30, 57 | 440.00, 466.16, 493.88, 523.25, 554.37, 587.33, 622.25, 659.25, 698.46, 739.99, 783.99, 830.61, 58 | 880.00, 932.33, 987.77, 1046.50, 1108.73, 1174.66, 1244.51, 1318.51, 1396.91, 1479.98, 1567.98, 1661.22}; 59 | 60 | //Quantized frequencies array from A1 to A7 with quarter tones 61 | /*float note_freq_quarter_tones[120] = {}; 62 | for (int i = 0; i < 119; i++) { 63 | float x = (note_freq[i/2] + note_freq[(i+1)/2]) / 2; 64 | note_freq_quarter_tones[i] = x; 65 | } 66 | note_freq_quarter_tones[119] = 1661.22; 67 | */ 68 | 69 | 70 | 71 | //Functions 72 | void set_wavetable(byte n) { 73 | if (n==0) {oscillator.setTable(SIN2048_DATA);} 74 | else if (n==1) {oscillator.setTable(TRIANGLE2048_DATA);} 75 | else if (n==2) {oscillator.setTable(HALFSIN2048_DATA);} 76 | else if (n==3) {oscillator.setTable(SAW2048_DATA);} 77 | else if (n==4) {oscillator.setTable(SQUARE_NO_ALIAS_2048_DATA);} 78 | else {oscillator.setTable(HOMEBREW_CLASH2048_DATA);} 79 | } 80 | 81 | /* 82 | void read_trigger() { 83 | if (digitalRead(trigger_pin) == HIGH and trigger == LOW) { 84 | envelope.noteOn(); 85 | trigger = HIGH; 86 | } 87 | else if (digitalRead(trigger_pin) == LOW and trigger == HIGH) { 88 | envelope.noteOff(); 89 | trigger = LOW; 90 | } 91 | } 92 | */ 93 | 94 | 95 | 96 | 97 | void setup() { 98 | startMozzi(); 99 | oscillator.setFreq(220); 100 | oscillator.setTable(SIN2048_DATA); 101 | //lpf.setResonance(0); 102 | //lpf.setCutoffFreq(255); 103 | //envelope.setLevels(255, 150, 150, 0); 104 | //envelope.setTimes(20, 20, 4000, 20); 105 | } 106 | 107 | 108 | 109 | 110 | void updateControl() { 111 | //read inputs 112 | tone_index = tone_index_map(mozziAnalogRead(analog_inputs[0])); 113 | volume = mozziAnalogRead(analog_inputs[1]) >> 2; 114 | waveform = wavetable_choice_map(mozziAnalogRead(analog_inputs[2])); 115 | 116 | //read_trigger(); 117 | 118 | set_wavetable(waveform); 119 | oscillator.setFreq(note_freq[tone_index]); 120 | 121 | //cutoff = (mozziAnalogRead(analog_inputs[3]) >> 3) + 50; 122 | //resonance = mozziAnalogRead(analog_inputs[4]) >> 4; 123 | //lpf.setResonance(resonance); 124 | //lpf.setCutoffFreq(cutoff); 125 | } 126 | 127 | 128 | 129 | 130 | int updateAudio() { 131 | int audio; 132 | if (waveform != 3 or waveform != 4) { audio = (oscillator.next() * volume) >> 2; } 133 | else { audio = (oscillator.next() * volume) >> 4; } // the square and saw waves are fucking loud -- this mellows them to be about the same volume as the other waveforms 134 | //int filtered_audio = lpf.next(audio); 135 | return audio; 136 | //return filtered_audio; 137 | } 138 | 139 | 140 | 141 | 142 | void loop() { 143 | audioHook(); 144 | } 145 | --------------------------------------------------------------------------------