├── README.md ├── MIDIUSB_Library └── MIDIUSB_Library.ino ├── tone-keyboard └── tone-keyboard.ino ├── Buttons └── Buttons.ino ├── Chat GPT MIDI Controller - buttons and pots └── Chat GPT MIDI Controller - buttons and pots.ino ├── Programming from Scratch ├── botoes_&_potenciometros │ └── botoes_&_potenciometros │ │ └── botoes_&_potenciometros.ino └── coding_buttons_pots │ └── coding_buttons_pots.ino └── Potentiometers └── Potentiometers.ino /README.md: -------------------------------------------------------------------------------- 1 | # arduino_audio_midi_programming 2 | 3 | This repo contains a series of codes from tutorials where I teach how to code a MIDI controllers from scratch. 4 | 5 | To build MIDI controllers with buttons, potentiometers, rotary encoders, leds, displays, etc, please visit our courses page, the NERD MUSICIAN PRO! 6 | 7 | https://www.musiconerd.com/nerd-musician-pro/ 8 | 9 | by Gustavo Silveira 10 | gustavosilveira@gmusiconerd.com -------------------------------------------------------------------------------- /MIDIUSB_Library/MIDIUSB_Library.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | byte randNote; 4 | byte note[8] = { 36, 38, 40, 42, 44, 46, 48, 50 }; 5 | 6 | void setup() { 7 | // put your setup code here, to run once: 8 | } 9 | 10 | void loop() { 11 | 12 | randNote = random(8); 13 | 14 | noteOn(0, note[randNote], 127); 15 | MidiUSB.flush(); 16 | delay(random(50, 200)); 17 | noteOn(0, note[randNote], 0); 18 | MidiUSB.flush(); 19 | delay(random(50, 200)); 20 | 21 | } 22 | 23 | ///////////////////////////////////////////// 24 | // Arduino (pro)micro midi functions MIDIUSB Library 25 | void noteOn(byte channel, byte pitch, byte velocity) { 26 | midiEventPacket_t noteOn = { 0x09, 0x90 | channel, pitch, velocity }; 27 | MidiUSB.sendMIDI(noteOn); 28 | MidiUSB.sendMIDI(noteOn); 29 | } 30 | 31 | void noteOff(byte channel, byte pitch, byte velocity) { 32 | midiEventPacket_t noteOff = { 0x08, 0x80 | channel, pitch, velocity }; 33 | MidiUSB.sendMIDI(noteOff); 34 | } 35 | 36 | void controlChange(byte channel, byte control, byte value) { 37 | midiEventPacket_t event = { 0x0B, 0xB0 | channel, control, value }; 38 | MidiUSB.sendMIDI(event); 39 | } 40 | 41 | void pitchBend(byte channel, int value) { 42 | byte lowValue = value & 0x7F; 43 | byte highValue = value >> 7; 44 | midiEventPacket_t pitchBend = { 0x0E, 0xE0 | channel, lowValue, highValue }; 45 | MidiUSB.sendMIDI(pitchBend); 46 | } -------------------------------------------------------------------------------- /tone-keyboard/tone-keyboard.ino: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | Keyboard 4 | 5 | Plays a pitch that changes based on a changing 6 | input circuit: 7 | 8 | * 8-ohm speaker on digital pin 8 9 | */ 10 | const int noteN = 7; 11 | int buttonPin[noteN] = { 2, 3, 4, 5, 6, 7, 8 }; 12 | int note[noteN] = { 0, 2, 4, 5, 7, 9, 11 }; 13 | int octave = 6 * 12; 14 | int buttonState[noteN]; 15 | 16 | int speakerPin = 9; 17 | 18 | boolean isPlaying = false; 19 | int counter = 0; 20 | 21 | 22 | void setup() { 23 | 24 | Serial.begin(115200); 25 | 26 | pinMode(speakerPin, OUTPUT); 27 | 28 | for (int i = 0; i < noteN; i++) { 29 | pinMode(buttonPin[i], INPUT_PULLUP); 30 | } 31 | } 32 | 33 | void loop() { 34 | // tone(8, 440, 100); // play tone 57 (A4 = 440 Hz) 35 | 36 | 37 | for (int i = 0; i < noteN; i++) { 38 | buttonState[i] = digitalRead(buttonPin[i]); 39 | if (buttonState[i] == LOW) { 40 | isPlaying = true; 41 | } else if (buttonState[0] && buttonState[1] && buttonState[2] && buttonState[3] && buttonState[4] && buttonState[5] && buttonState[6] == HIGH) { 42 | isPlaying = false; 43 | } 44 | } 45 | 46 | for (int i = 0; i < noteN; i++) { 47 | if (buttonState[i] == LOW) { 48 | // play a note 49 | tone(speakerPin, midiToFreq(note[i] + octave)); 50 | } 51 | if (isPlaying == false) { 52 | noTone(speakerPin); 53 | } 54 | } 55 | } 56 | 57 | float midiToFreq(float midival) { 58 | float f = 0.0; 59 | if (midival) f = 8.1757989156 * pow(2.0, midival / 12.0); 60 | return f; 61 | } -------------------------------------------------------------------------------- /Buttons/Buttons.ino: -------------------------------------------------------------------------------- 1 | /* 2 | This code reads the Arduino digital ports (buttons) and sends MIDI notes. 3 | Tutorial: https://youtu.be/IcjKv6ZTiHI 4 | 5 | by Gustavo Silveira, a.k.a. Nerd Musician 6 | Learn how to builf your MIDI controllers: https://go.musiconerd.com/ 7 | */ 8 | 9 | #include "MIDIUSB.h" // Include the MIDIUSB library for MIDI communication 10 | 11 | // Define the number of buttons 12 | const int N_BUTTONS = 3; 13 | 14 | // Define the Arduino pins for buttons and their corresponding MIDI note numbers 15 | int buttonPin[N_BUTTONS] = { 4, 3, 2 }; 16 | int buttonNote[N_BUTTONS] = { 36, 38, 42 }; 17 | 18 | // Arrays to store the current and previous button states 19 | int buttonState[N_BUTTONS] = { 0 }; 20 | int buttonPState[N_BUTTONS] = { 0 }; // previous state of the button 21 | 22 | // Arrays to manage button debouncing 23 | unsigned long lastDebounceTime[N_BUTTONS] = { 0 }; 24 | unsigned long debounceTimer[N_BUTTONS] = { 0 }; 25 | int debounceDelay = 10; 26 | 27 | int BUTTON_CH = 0; // MIDI channel for button events 28 | 29 | void setup() { 30 | Serial.begin(9600); // Initialize serial communication for debugging 31 | 32 | // Configure each button pin as INPUT_PULLUP 33 | for (int i = 0; i < N_BUTTONS; i++) { 34 | pinMode(buttonPin[i], INPUT_PULLUP); 35 | } 36 | 37 | // Additional setup code can be added here 38 | } 39 | 40 | void loop() { 41 | buttons(); // Call the function to handle button events 42 | } 43 | 44 | // Function to read and handle button states 45 | void buttons() { 46 | for (int i = 0; i < N_BUTTONS; i++) { 47 | buttonState[i] = digitalRead(buttonPin[i]); // Read the current button state 48 | 49 | debounceTimer[i] = millis() - lastDebounceTime[i]; // Calculate time since last state change 50 | 51 | // Check if enough time has passed for debounce 52 | if (debounceTimer[i] > debounceDelay) { 53 | if (buttonState[i] != buttonPState[i]) { // Check if button state has changed 54 | lastDebounceTime[i] = millis(); // Update the debounce time 55 | 56 | // Check if the button is pressed 57 | if (buttonState[i] == LOW) { 58 | noteOn(BUTTON_CH, buttonNote[i], 127); // Send MIDI note-on message 59 | MidiUSB.flush(); // Flush the MIDI data 60 | 61 | Serial.print("Button "); 62 | Serial.print(i); 63 | Serial.print(" "); 64 | Serial.println("on"); 65 | } else { // Button is released 66 | noteOn(BUTTON_CH, buttonNote[i], 0); // Send MIDI note-off message 67 | MidiUSB.flush(); // Flush the MIDI data 68 | 69 | Serial.print("Button "); 70 | Serial.print(i); 71 | Serial.print(" "); 72 | Serial.println("off"); 73 | } 74 | 75 | buttonPState[i] = buttonState[i]; // Update the previous button state 76 | } 77 | } 78 | } 79 | } 80 | 81 | // Function to send a MIDI note-on message 82 | void noteOn(byte channel, byte pitch, byte velocity) { 83 | midiEventPacket_t noteOn = { 0x09, 0x90 | channel, pitch, velocity }; 84 | MidiUSB.sendMIDI(noteOn); 85 | } 86 | 87 | // Function to send a MIDI control change message (not used in this code) 88 | void controlChange(byte channel, byte control, byte value) { 89 | midiEventPacket_t event = { 0x0B, 0xB0 | channel, control, value }; 90 | MidiUSB.sendMIDI(event); 91 | } -------------------------------------------------------------------------------- /Chat GPT MIDI Controller - buttons and pots/Chat GPT MIDI Controller - buttons and pots.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | const int buttonPin[] = {2, 3, 4}; // buttons connected to digital pins 2, 3, 4 4 | const int buttonNotes[] = {60, 64, 67}; // notes to be sent when buttons are pressed 5 | const int potPin[] = {A0, A1}; // potentiometer 1 connected to analog pin A0, potentiometer 2 connected to analog pin A1 6 | const int controlNumber[] = {1, 2}; // MIDI control number for potentiometer 1 and 2 7 | const int debounce = 50; // debounce time in milliseconds 8 | unsigned long lastButtonPress = 0; // timestamp of last button press 9 | unsigned long lastPotEvent[2] = {0}; // timestamp of last potentiometer event 10 | int potValue[2] = {0}; 11 | bool buttonState[3] = {false}; //keeps track of button state 12 | int potThreshold = 10; // threshold value for potentiometer movement 13 | int potDuration = 300; // duration of control change messages in ms 14 | 15 | #define MAX_NOTES 12 // max number of notes that can be played at the same time 16 | int notesPlaying[MAX_NOTES] = {0}; // array to keep track of the notes that are currently playing 17 | int notesPlayingIndex = 0; // index for the notesPlaying array 18 | 19 | void setup() { 20 | // MidiUSB.begin(); 21 | for (int i = 0; i < 3; i++) { 22 | pinMode(buttonPin[i], INPUT_PULLUP); 23 | } 24 | for (int i = 0; i < 2; i++) { 25 | pinMode(potPin[i], INPUT); 26 | } 27 | } 28 | 29 | void loop() { 30 | handleButtons(); 31 | handlePotentiometers(); 32 | } 33 | 34 | void handleButtons() { 35 | for (int i = 0; i < 3; i++) { 36 | int currentButtonState = digitalRead(buttonPin[i]); 37 | if (currentButtonState == LOW && (millis() - lastButtonPress) > debounce) { 38 | if (!buttonState[i]) { // if button is not pressed 39 | midiEventPacket_t noteOn = {0x09, 0x90, buttonNotes[i], 100}; 40 | MidiUSB.sendMIDI(noteOn); 41 | MidiUSB.flush(); 42 | buttonState[i] = true; //update state 43 | notesPlaying[notesPlayingIndex] = buttonNotes[i]; // add the note to the notesPlaying array 44 | notesPlayingIndex++; 45 | } 46 | lastButtonPress = millis(); 47 | } else if (currentButtonState == HIGH) { 48 | if (buttonState[i]) { 49 | // Send MIDI note off message 50 | midiEventPacket_t noteOff = {0x08, 0x80, buttonNotes[i], 0}; 51 | MidiUSB.sendMIDI(noteOff); 52 | MidiUSB.flush(); 53 | buttonState[i] = false; //update state 54 | //remove the note from the notesPlaying array 55 | for (int j = 0; j < notesPlayingIndex; j++) { 56 | if (notesPlaying[j] == buttonNotes[i]) { 57 | for (int k = j; k < notesPlayingIndex - 1; k++) { 58 | notesPlaying[k] = notesPlaying[k + 1]; 59 | } 60 | notesPlayingIndex--; 61 | break; 62 | } 63 | } 64 | } 65 | } 66 | } 67 | } 68 | 69 | void handlePotentiometers(){ 70 | for (int i = 0; i < 2; i++) { 71 | int currentPotValue = analogRead(potPin[i]); 72 | if(abs(potValue[i] - currentPotValue) > potThreshold){ 73 | lastPotEvent[i] = millis(); 74 | potValue[i] = currentPotValue; 75 | } 76 | if((millis() - lastPotEvent[i]) < potDuration ){ 77 | midiEventPacket_t controlChange = {0x0B, 0xB0, controlNumber[i], map(potValue[i], 0, 1023, 0, 127)}; 78 | MidiUSB.sendMIDI(controlChange); 79 | MidiUSB.flush(); // send the MIDI message 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /Programming from Scratch/botoes_&_potenciometros/botoes_&_potenciometros/botoes_&_potenciometros.ino: -------------------------------------------------------------------------------- 1 | // Arduino Pro Micro usa o ATmega32U4 (Leonardo, Micro, Pro Micro) 2 | #include "MIDIUSB.h" 3 | 4 | // BOTOES 5 | const int N_BUTTON = 3; // numero total de botoes 6 | byte buttonPin[N_BUTTON] = { 2, 3, 4 }; // pinos digitais 7 | byte buttonState[N_BUTTON] = { 0 }; // estado atual 8 | byte buttonPState[N_BUTTON] = { 0 }; // estado previo 9 | unsigned long lastBounce[N_BUTTON] = { 0 }; // para "resetar" o relogio 10 | unsigned long buttonTimer[N_BUTTON] = { 0 }; // timer do botao 11 | int buttonTimeout = 15; 12 | 13 | // - - - - - - - - - - - - - - 14 | 15 | // POTENCIOMETROS 16 | const int N_POTS = 2; // numero total de pots 17 | byte potPin[N_POTS] = { A0, A1 }; // pinos analogicos 18 | int potState[N_POTS] = { 0 }; 19 | int potPState[N_POTS] = { 0 }; 20 | byte midiState[N_POTS] = { 0 }; 21 | byte midiPState[N_POTS] = { 0 }; 22 | int varThreshold = 20; 23 | 24 | boolean openGate[N_POTS] = { false }; 25 | 26 | unsigned long lastPot[N_POTS] = { 0 }; 27 | unsigned long potTimer[N_POTS] = { 0 }; 28 | 29 | int potTimeout = 300; 30 | 31 | int MIDI_CH = 0; // 0-15 32 | int NN[N_BUTTON] = { 36, 38, 40 }; 33 | int CC[N_POTS] = { 11, 13 }; 34 | 35 | void setup() { 36 | 37 | Serial.begin(115200); 38 | 39 | // delclarar os pinos do arduino 40 | for (int i = 0; i < N_BUTTON; i++) { 41 | pinMode(buttonPin[i], INPUT_PULLUP); 42 | } 43 | } 44 | 45 | // - - - - - - - - - - - - - - 46 | 47 | void loop() { 48 | // put your main code here, to run repeatedly: 49 | 50 | buttons(); 51 | potentiometers(); 52 | } 53 | 54 | // - - - - - - - - - - - - - - 55 | 56 | void buttons() { 57 | 58 | for (int i = 0; i < N_BUTTON; i++) { 59 | buttonState[i] = digitalRead(buttonPin[i]); 60 | 61 | buttonTimer[i] = millis() - lastBounce[i]; // corre o timer 62 | 63 | if (buttonTimer[i] > buttonTimeout) { 64 | 65 | if (buttonState[i] != buttonPState[i]) { 66 | lastBounce[i] = millis(); // armazena a hora 67 | 68 | if (buttonState[i] == 0) { 69 | // mandar note on 70 | noteOn(MIDI_CH, NN[i], 127); 71 | MidiUSB.flush(); 72 | 73 | Serial.print("Botao "); 74 | Serial.print(i); 75 | Serial.println(": on"); 76 | } else { 77 | // mandar note off 78 | noteOn(MIDI_CH, NN[i], 0); 79 | MidiUSB.flush(); 80 | 81 | Serial.print("Botao "); 82 | Serial.print(i); 83 | Serial.println(": off"); 84 | } 85 | 86 | buttonPState[i] = buttonState[i]; 87 | } 88 | } 89 | } 90 | } 91 | 92 | // - - - - - - - - - - - - - - 93 | 94 | void potentiometers() { 95 | 96 | for (int i = 0; i < N_POTS; i++) { 97 | potState[i] = analogRead(potPin[i]); // leio o estado do pot | 0-1023 98 | midiState[i] = map(potState[i], 0, 1020, 0, 127); 99 | 100 | int potVar = abs(potState[i] - potPState[i]); 101 | 102 | if (potVar > varThreshold) { 103 | lastPot[i] = millis(); // salva que horas sao 104 | } 105 | 106 | potTimer[i] = millis() - lastPot[i]; // corre o timer 107 | 108 | if (potTimer[i] < potTimeout) { 109 | if (midiState[i] != midiPState[i]) { // estado atual for diferente do previo 110 | // mandar CC 111 | controlChange(MIDI_CH, CC[i], midiState[i]); 112 | MidiUSB.flush(); 113 | 114 | Serial.print("Pot "); 115 | Serial.print(i); 116 | Serial.print(": "); 117 | Serial.println(midiState[i]); 118 | 119 | midiPState[i] = midiState[i]; 120 | potPState[i] = potState[i]; 121 | } 122 | } 123 | } 124 | } 125 | 126 | // - - - - - - - - - - - - - - - - - - - - - - - - - 127 | 128 | // Arduino (pro)micro midi functions MIDIUSB Library 129 | void noteOn(byte channel, byte pitch, byte velocity) { 130 | midiEventPacket_t noteOn = { 0x09, 0x90 | channel, pitch, velocity }; 131 | MidiUSB.sendMIDI(noteOn); 132 | } 133 | 134 | void controlChange(byte channel, byte control, byte value) { 135 | midiEventPacket_t event = { 0x0B, 0xB0 | channel, control, value }; 136 | MidiUSB.sendMIDI(event); 137 | } -------------------------------------------------------------------------------- /Potentiometers/Potentiometers.ino: -------------------------------------------------------------------------------- 1 | /* 2 | This code reads the Arduino analog pins and send MIDI CC using the MIDIUSB lib 3 | Tutorial: https://youtu.be/hcm5H6f8MI8 4 | 5 | by Gustavo Silveira, a.k.a. Nerd Musician 6 | Learn how to builf your MIDI controllers: https://go.musiconerd.com/ 7 | */ 8 | 9 | #include "MIDIUSB.h" // Include the MIDIUSB library for MIDI communication 10 | #include // Include the ResponsiveAnalogRead library from https://github.com/dxinteractive/ResponsiveAnalogRead 11 | 12 | const int N_POTS = 5; // Define the number of potentiometers 13 | 14 | int potPin[N_POTS] = { A0, A1, A2, A3, A4 }; // Define analog input pins for the potentiometers 15 | int potCC[N_POTS] = { 11, 12 }; // Define MIDI control change (CC) values for the potentiometers 16 | 17 | int potReading[N_POTS] = { 0 }; // Initialize an array to store raw potentiometer readings 18 | int potState[N_POTS] = { 0 }; // Initialize an array to store smoothed potentiometer values 19 | int potPState[N_POTS] = { 0 }; // Initialize an array to store the previous potentiometer values 20 | 21 | int midiState[N_POTS] = { 0 }; // Initialize an array to store mapped MIDI values 22 | int midiPState[N_POTS] = { 0 }; // Initialize an array to store the previous MIDI values 23 | 24 | const byte potThreshold = 15; // Define a threshold for potentiometer value change 25 | const int POT_TIMEOUT = 300; // Define a timeout for detecting potentiometer changes 26 | unsigned long pPotTime[N_POTS] = { 0 }; // Initialize an array to store the last time a potentiometer changed 27 | unsigned long potTimer[N_POTS] = { 0 }; // Initialize an array to store the time elapsed since a potentiometer change 28 | 29 | byte POT_CH = 0; // Define the MIDI channel for the potentiometers 30 | 31 | float snapMultiplier = 0.01; // Define a multiplier for smoothing potentiometer readings 32 | ResponsiveAnalogRead responsivePot[N_POTS] = {}; // Create an array for responsive potentiometers. It gets filled in the Setup. 33 | 34 | void setup() { 35 | // Setup code runs once when the Arduino is powered on or reset. 36 | 37 | Serial.begin(9600); // Initialize serial communication for debugging 38 | 39 | for (int i = 0; i < N_POTS; i++) { 40 | responsivePot[i] = ResponsiveAnalogRead(0, true, snapMultiplier); // Initialize responsive potentiometers 41 | responsivePot[i].setAnalogResolution(1023); // Set the analog resolution 42 | } 43 | } 44 | 45 | void loop() { 46 | // Main loop code runs repeatedly. 47 | 48 | for (int i = 0; i < N_POTS; i++) { 49 | 50 | potReading[i] = analogRead(potPin[i]); // Read the raw potentiometer value 51 | responsivePot[i].update(potReading[i]); // Update the responsive potentiometer 52 | potState[i] = responsivePot[i].getValue(); // Get the smoothed potentiometer value 53 | midiState[i] = map(potState[i], 0, 1023, 0, 128); // Map the potentiometer value to MIDI range 54 | 55 | int potVar = abs(potState[i] - potPState[i]); // Calculate the absolute change in potentiometer value 56 | 57 | if (potVar > potThreshold) { 58 | pPotTime[i] = millis(); // Record the time when the potentiometer changed 59 | } 60 | 61 | potTimer[i] = millis() - pPotTime[i]; // Calculate the time elapsed since the potentiometer changed 62 | 63 | if (potTimer[i] < POT_TIMEOUT) { 64 | if (midiState[i] != midiPState[i]) { 65 | 66 | controlChange(POT_CH, potCC[i], midiState[i]); // Send a MIDI control change message 67 | MidiUSB.flush(); // Flush the MIDI output 68 | 69 | Serial.print("Pot "); 70 | Serial.print(i); 71 | Serial.print(" | "); 72 | Serial.print("PotState: "); 73 | Serial.print(potState[i]); 74 | Serial.print(" - midiState: "); 75 | Serial.println(midiState[i]); 76 | 77 | midiPState[i] = midiState[i]; // Update the previous MIDI state 78 | } 79 | potPState[i] = potState[i]; // Update the previous potentiometer state 80 | } 81 | } 82 | } 83 | 84 | // Arduino (pro)micro MIDI functions using the MIDIUSB Library 85 | void noteOn(byte channel, byte pitch, byte velocity) { 86 | midiEventPacket_t noteOn = { 0x09, 0x90 | channel, pitch, velocity }; 87 | MidiUSB.sendMIDI(noteOn); 88 | } 89 | 90 | void noteOff(byte channel, byte pitch, byte velocity) { 91 | midiEventPacket_t noteOff = { 0x08, 0x80 | channel, pitch, velocity }; 92 | MidiUSB.sendMIDI(noteOff); 93 | } 94 | 95 | void controlChange(byte channel, byte control, byte value) { 96 | midiEventPacket_t event = { 0x0B, 0xB0 | channel, control, value }; 97 | MidiUSB.sendMIDI(event); 98 | } 99 | -------------------------------------------------------------------------------- /Programming from Scratch/coding_buttons_pots/coding_buttons_pots.ino: -------------------------------------------------------------------------------- 1 | /* 2 | by Gustavo Silveira | aka Nerd Musician 3 | -> Nerd Musician Pro: https://www.musiconerd.com/nerd-musician-pro/ 4 | -> Curso Fazendo Música com Arduino em Português: https://www.musiconerd.com/curso-completo 5 | gustavosilveira@musiconerd.com 6 | */ 7 | 8 | // - - - - - - - - - - - - - - - - - - - - - - - - - 9 | 10 | #include "MIDIUSB.h" 11 | 12 | // BUTTONS 13 | const int N_BUTTON = 3; // total number of buttons 14 | byte buttonPin[N_BUTTON] = { 2, 3, 4 }; // button pins 15 | byte buttonState[N_BUTTON] = { 0 }; // button state 16 | byte buttonPState[N_BUTTON] = { 0 }; // button previous state 17 | 18 | int debounceDelay = 10; // 10 ms 19 | unsigned long lastDebounceTime[N_BUTTON] = { 0 }; 20 | unsigned long debounceTimer[N_BUTTON] = { 0 }; 21 | 22 | // - - - - - - - - - - - - - - - - - - - - - - - - - 23 | 24 | // POTENTIOMETERS 25 | const int N_POT = 2; 26 | byte potPin[N_POT] = { A0, A1 }; 27 | int potState[N_POT] = { 0 }; 28 | int potPState[N_POT] = { 0 }; 29 | byte midiState[N_POT] = { 0 }; 30 | byte midiPState[N_POT] = { 0 }; 31 | 32 | int varThreshold = 20; // how much it need to vary to send a message 33 | 34 | unsigned long potTimer[N_POT] = { 0 }; 35 | unsigned long lastPotTime[N_POT] = { 0 }; 36 | const int POT_TIMEOUT = 300; 37 | boolean potGate[N_POT] = { false }; 38 | 39 | // - - - - - - - - - - - - - - - - - - - - - - - - - 40 | 41 | byte MIDI_CH = 0; 42 | byte NN[N_BUTTON] = { 36, 38, 40 }; // note list 43 | byte CC[N_BUTTON] = { 10, 11 }; // CC list 44 | 45 | void setup() { 46 | // put your setup code here, to run once: 47 | 48 | Serial.begin(115200); // starts serial monitor 49 | 50 | for (int i = 0; i < N_BUTTON; i++) { 51 | pinMode(buttonPin[i], INPUT_PULLUP); // declare the button pins 52 | } 53 | } 54 | 55 | // - - - - - - - - - - - - - - - - - - - - - - - - - 56 | 57 | void loop() { 58 | // put your main code here, to run repeatedly: 59 | 60 | buttons(); 61 | potentiometers(); 62 | } 63 | 64 | // - - - - - - - - - - - - - - - - - - - - - - - - - 65 | 66 | void buttons() { 67 | 68 | for (int i = 0; i < N_BUTTON; i++) { 69 | buttonState[i] = digitalRead(buttonPin[i]); 70 | 71 | // we only want to send a message if the message now is different from before 72 | 73 | debounceTimer[i] = millis() - lastDebounceTime[i]; 74 | 75 | if (debounceTimer[i] > debounceDelay) { 76 | 77 | if (buttonState[i] != buttonPState[i]) { 78 | 79 | // start timer 80 | lastDebounceTime[i] = millis(); 81 | 82 | if (buttonState[i] == LOW) { 83 | // note on 84 | 85 | noteOn(MIDI_CH, NN[i], 127); 86 | MidiUSB.flush(); 87 | 88 | Serial.print("Button "); 89 | Serial.print(i); 90 | Serial.print(": on"); 91 | Serial.println(); 92 | 93 | } else { 94 | // note off 95 | 96 | noteOn(MIDI_CH, NN[i], 0); 97 | MidiUSB.flush(); 98 | 99 | Serial.print("Button "); 100 | Serial.print(i); 101 | Serial.print(": off"); 102 | Serial.println(); 103 | } 104 | 105 | buttonPState[i] = buttonState[i]; 106 | } 107 | } 108 | } 109 | } 110 | 111 | // - - - - - - - - - - - - - - - - - - - - - - - - - 112 | 113 | void potentiometers() { 114 | 115 | for (int i = 0; i < N_POT; i++) { 116 | // reads all the pots 117 | potState[i] = analogRead(potPin[i]); // stores the analog val 118 | midiState[i] = map(potState[i], 0, 1023, 0, 127); 119 | 120 | int potVar = abs(potState[i] - potPState[i]); // variation between the previous and the current val 121 | //Serial.println(potVar); 122 | 123 | if (potVar > varThreshold) { 124 | lastPotTime[i] = millis(); 125 | } 126 | 127 | potTimer[i] = millis() - lastPotTime[i]; 128 | 129 | if (potTimer[i] < POT_TIMEOUT) { 130 | potGate[i] = true; 131 | } else { 132 | potGate[i] = false; 133 | } 134 | 135 | if (potGate[i] = true) { 136 | if (midiState[i] != midiPState[i]) { 137 | 138 | controlChange(MIDI_CH, CC[i], midiState[i]); // sends control change 139 | MidiUSB.flush(); 140 | 141 | Serial.print("Pot "); 142 | Serial.print(i); 143 | Serial.print(": "); 144 | Serial.print(midiState[i]); 145 | Serial.println(); 146 | 147 | midiPState[i] = midiState[i]; 148 | potPState[i] = potState[i]; 149 | } 150 | } 151 | } 152 | } 153 | 154 | // - - - - - - - - - - - - - - - - - - - - - - - - - 155 | 156 | // Arduino (pro)micro midi functions MIDIUSB Library 157 | void noteOn(byte channel, byte pitch, byte velocity) { 158 | midiEventPacket_t noteOn = { 0x09, 0x90 | channel, pitch, velocity }; 159 | MidiUSB.sendMIDI(noteOn); 160 | } 161 | 162 | void controlChange(byte channel, byte control, byte value) { 163 | midiEventPacket_t event = { 0x0B, 0xB0 | channel, control, value }; 164 | MidiUSB.sendMIDI(event); 165 | } --------------------------------------------------------------------------------