"In this tutorial, I will show you how to use Arduino UNO to make basic MIDI instruments. This instrument continuously plays three notes (three sums of strings). Use different potentiometers, you can move the note up and down. You can also control the delay between each note. I think I learned some interesting things about the MIDI controller from this project. I hope you will also.
This instrument is a very basic version of the Arpeggio. This is a fancy moving video. Arpeggics use chords of natural music, and there is a lot of wonderful sounds without blowing ash. They usually appear as part of the synthesizer, you only need to press and hold the chords you want to voices. In this example, we only have a button that triggers the arpeggio and three buttons that control the start tightening and its position on the housing.
For Arduino UNO, the easiest way to use the MIDI protocol is to pass the serial port. It requires some configuration to run on Windows, I will introduce. I used the Ardunio MIDI library of Fortyseneffects. I also follow these excellent YouTube tutorial: Arduino's MIDI, how to build a MIDI controller using Arduino. I wrote the code for control buttons and potentiometers based on the code of the second video series. It does have a good record in the entire free course of making MIDI controllers.
In this project, I use the Grove Starter Kit Plus for the RGB backlight LCD screen. This is not absolutely necessary, but I am very happy to see the note you are playing.
Supplies
4 potentiometers (I used anything around me)
1 button
1 1K resistor for buttons
1 Arduino Uno
1 Grove Getting Started / RGB Backlight LCD (Optional)
Connection wire
Computer related tools
DAW
Hairness Midi (Serial Bridge - Converts Serial Output to MIDI Entering)
LOOP MIDI (create a virtual loopback port for your computer.)
Arduino IDE
Step 1: Set board board
Here is the layout map I use without LCD. This button will be used as a trigger that starts the Arpeggio. The three potentiots will control the different aspects of the arpeggio, and the last potentiometer will be used as MIDI control.
In my actual circuit layout, I used the Grove board (connected to the top of Arduino Uno). It comes with a button and a nice potentiometer that you can see in the figure that they have inserted into their respective ports. The LCD screen is very easy, and you simply connect a cable that comes with the GROVE to the I2C port from the LCD screen.
Be
Be
Name
quantity
Assembly
Be
Be
Be
Be
R5
1
220 Ω resistance
Be
Be
U9 1
1
Arduino Uno R3
Be
Be
Rpot9
Rpot10
Rpot11
Rpot12
4
250 kΩ potentiometer
Be
Be
R8
1
10 kΩ resistance
Be
Be
S1
1
Button
Be
Be
Circuit map original
Basic Arpeggio Nolcd.ino
Step 2: Familiar with MIDI
MIDI is a communication protocol built specifically for instruments created in the early 1980s. Through the software settings we have, we can use MIDI to enter and output to our Arduino, but for the sake of simplicity, we will focus on the output. Specifically we will use two different channels of voice messages: note and controller changes. Note ON allows us to tell DAW or anything we have to turn on or off, and how to speed (Midi said weird). The controller change tells the DAW controller's value to change. You can imagine the twisted dial on the amplifier, whether it is for volume or some effects, such as distortion or reverberation. There are still many other messages, and things will become very complicated, very fast. The MIDI library we use has a lot of functions to support these different messages.
Is interested in learning more? I will introduce you to the YouTube link I started at the beginning of the tutorial. Then I searched around to see what people did using MIDI to understand the possibility.
For Note ON, this is a link to the sound high list and its assigned MIDI value. For example, the MIDI value of C4 is 60. Each number is mapped to a half voice, so it is very easy to use the module method to determine what is much. Given the C4 = 60, I know C5 = 72, C6 = 84, there is no need to look at the table, because I know that the octave is divided by 12 half. This will send a manner when we convert MIDI values to notes to display on the LCD screen. In addition, if we save the CMAJ scheduled MIDI value to the list, we can use the MOD7 division to move the value in the scale.
Step 3: Code
If you don't need / don't need LCD, I with a version, I commented the LCD component. I made a lot of comments in the code, but here I will point out some things, starting from the top.
Initialize variables:
The variable of the button "Debounce_delay" and the variable "timeout" and "var_threshold" can be used to reduce some noise and error readings from the buttons and potentiometer. If you encounter problems, it will be a good starting point. Since we convert a continuous range to discrete integers, the potentiometer may become unstable between the boundaries. There are several ways to solve if there are too many potentiometers flashing between states. One solution I have not implemented here is that after any further processing, read several readings and take average.
The "Musical" list is the notes we want to play in the arpeggio. I set it to play the starting symbol and recorded 2 tones and 4 messages in a high-key manner. This will form basic major challenges, small tuning and mining strings. If you want some more interesting things, you can add more notes in the "Notes" list. Just don't forget to change the constant "chord_length". For example, you can do this:
Be
Be
const Int Chord_length = 6; // Number of Notes in Chord
INT MIDI_VALS [Chord_length] = {}; // array to store midi-output to play
INT Notes [Chord_length] = {0, 2, 4, 2, 5, 7}; // 1st 3rd, And 5th Note On Scale, Plus the 7th. this is more jazzy
Be
Be
By creating a list "cmaj_scale", I created a template, I can move up and down to create a string. If I want to change the key, what I need to do is to use the input changes from the third potentiometer "MIDI_C_STATE [2]". If I just want to move from a given initiator, I just change "MIDI_C_STATE [0]" from the first potentiometer.
set up:
You need to set the correct baud rate for MIDI to read your serial input correctly. Otherwise you will get errors in Hair MiDi, such as "get an unexpected data byte"
The LCD screen has 16 columns and 2 lines.
Cycle and related functions
The function "pause_length" controls the latency between the notes being played.
"Starting_scale_position" allows you to move up and down without changing the key. "Start Jones" allows you to move all content up or down to a halftone value, allowing you to change. Both functions call "Move_Chord" and re-map the note you selected to the MIDI value.
Changing the delay, the starting tone position, the MIDI control or notes will cause the LCD to refresh.
Basic Arpeggio .ino
Be
Be
// Basic Midi Arpeggiator Instrument for the Arduino Uno by Joseph THompson.
// this Plays Triads That Can Be shifted in Pitch and Position On The Scale
// Inspired by and uses the button / potentiometer checking from https://github.com/silveirago/DIY-Midi-Controller/blob/master/Code%20-%20c%C3%B3digo/en-DIY_midi_controller/en-DIY_midi_controller .ino
// Uses Grove RGB_LCD, from Grove Starter Kit. https://wiki.seeedstudio.com/grove-lcd_rgb_backlight/ this is not essential and can be remodes
#include
#include
MIDI_CREATE_DEFAULT_INSTANCE (); // Creates Midi Object To Interact with Serial
// Buttons
// only has 1 Button to Trigger The Arpeggio
Constint n_buttons = 1; // * Total Numbers of Buttons
Constint Button_ARDUINO_PIN [N_BUTTONS] = {3}; // * Pins of Each Button Connected Straight To The Arduino
INT button_c_state [n_buttons] = {}; // Stores The Button Current Value
INT button_P_State [n_buttons] = {}; // Stores The Button Previous Value
// debounce
Unsignedlong last_debounce_time [n_buttons] = {0}; // the last time the output pin WAS TOGGLED
Unsignedlong debounce_delay = 50; // * the debsunce time; Increase etu the output flickers
// potentiometer
Constint n_pots = 4;
Constint Pot_ARDUINO_PIN [N_POTS] = {A0, A1, A2, A3}; // * Pins of Each Pot Connected Straight To the Arduino
INT Pot_c_state [n_pots] = {0, 0, 0, 0}; // Current State of the Pot
INT POT_P_STATE [N_POTS] = {0,0,0,0}; // Previous State of the Pot
INT Pot_var = 0; // Difference Between The Current and Previous State of The Pot
/ * MIDI_C_STATE [0] -> Position On Scale
* MIDI_C_STATE [1] -> Pause Length
* MIDI_C_STATE [2] -> Adjusts Position on Midi Note Map. I.E. C5 = 60, C5 # = 61 ...
* MIDI_C_STATE [3] -> MIDI Control
* /
INT MIDI_C_STATE [N_POTS] = {0,0,0,0}; // Current State of The Midi Value
INT MIDI_P_STATE [N_POTS] = {0,0,0,0}; // previousstate of the midi value
constint TIMEOUT = 300; //* Amount of time the potentiometer will be read after it exceeds the var_threshold
constint var_threshold = 10; //* Threshold for the potentiometer signal variation
boolean pot_moving= true; // If the potentiometer is moving
unsignedlong PTime[N_POTS] = {0,0,0,0}; // Previously stored time
unsignedlong timer[N_POTS] = {0,0,0,0}; // Stores the time that has elapsed since the timer was reset
// MIDI
byte midi_ch = 1; //* MIDI channel to be used
byte cc = 1; //* Lowest MIDI CC to be used
//Scale info
String note_names[] = {""C "", ""C#"", ""D "", ""D#"", ""E "", ""F "", ""F#"", ""G "", ""G#"", ""A "", ""A#"", ""B ""}; //for display on the LCD
int Cmaj_scale[] = {60,62,64,65,67,69,71,72}; //MIDI code for C Major scale. In midi, each number is a semi-tone
int scale_start = 0; //starting position in scale
constint CHORD_LENGTH = 3; //number of notes in chord
int midi_vals[CHORD_LENGTH] = {}; //array to store midi-output to play
int notes[CHORD_LENGTH] = {0,2,4}; //1st 3rd, and 5th note on scale, makes basic triad
int note_delay = 100;
//LCD
rgb_lcd lcd; //create LCD instance
constint colorR = 255; //set background colors
constint colorG = 0;
constint colorB = 100;
voidsetup() {
// put your setup code here, to run once:
Serial.begin(115200);
for (int i = 0; i < N_BUTTONS; i++) {
pinMode(BUTTON_ARDUINO_PIN[i], INPUT_PULLUP);
//LCD
lcd.begin(16,2); //rows and columns of LCD
lcd.setRGB(colorR, colorG, colorB); //starts background color
lcd.print(""Get Ready to Jam!"");
}
}
voidloop() {
//buttons
arp_trigger(); //scale_starts the arpegio
//pots
starting_note(); //adjusts starting note
starting_scale_position(); //adjusts scale position
pause_length(); //adjust time between notes in arppegio
pot_midi(); //adjusts MIDI control pots
}
// BUTTONS
voidarp_trigger() {
button_c_state[0] = digitalRead(BUTTON_ARDUINO_PIN[0]); // read pin from arduino
if ((millis() - last_debounce_time[0]) > debounce_delay) {//Stops multiple triggers in too short of a timespan(debouncing)
if (button_p_state[0] != button_c_state[0]) { //Checks if something changed
last_debounce_time[0] = millis();
if (button_c_state[0] == LOW) { //Plays the arpegio
for( int i =0; i
MIDI.sendNoteOn(midi_vals[i], 127, midi_ch);// note, velocity, channel
delay(note_delay); //delay controlled by potentiometer 1
}
}
else { //stops playing arpegio
for( int i =0; i
MIDI.sendNoteOn(midi_vals[i], 0, midi_ch);
}
}
button_p_state[0] = button_c_state[0]; //saves current state as past state
}
}
}
voidmove_chord(){
/* helper function to shift chord around the scale. midi_c_state[0] is controlled by potentiometer 0 */
int note;
scale_start = midi_c_state[0];
for (int i =0; i< CHORD_LENGTH ; i++){ //Go through each note in arpeggio. Here it is the basic triads in major scale
note = notes[i];
if(scale_start+ note >7){//This allows us to play notes in next octave
midi_vals[i] = Cmaj_scale[(scale_start+note)%7] + 12 + midi_c_state[2]; //converts note to default scale, adds twelve semitones to move it up an octave
}else{
midi_vals[i] = Cmaj_scale[scale_start+note]+ midi_c_state[2]; //note is in current octave, we can store it normally
}
}
}
// POTENTIOMETERS
voidstarting_scale_position() {
/* This function determines where on scale you start. This is controlled by potentiometer 0 (0 is start of scale, 7 is the octave) */
pot_c_state[0] = analogRead(POT_ARDUINO_PIN[0]); // reads the pins from arduino
midi_c_state[0] = map(pot_c_state[0], 0, 1023, 0, 7); // Maps the reading of thepot_c_state to a value that we will add to the starting midi value
pot_var = abs(pot_c_state[0] - pot_p_state[0]); // Calculates the absolute value between the difference between the current and previous state of the pot
if (pot_var > var_threshold) { // Opens the gate if the potentiometer variation is greater than the threshold
PTime[0] = millis(); // Stores the previous time
}
timer[0] = millis() - PTime[0]; // Resets the timer 11000 - 11000 = 0ms
if (timer[0] < TIMEOUT) { // If the timer is less than the maximum allowed time it means that the potentiometer is still moving
pot_moving= true;
}
else {
pot_moving= false;
}
if (pot_moving== true) { // If the potentiometer is still moving, send the change control
if (midi_p_state[0] != midi_c_state[0]) {
move_chord(); //Calls move chord function. This shifts the chord to the values mapped to the potentiometers
update_lcd(); //updates LCD to reflect changes
pot_p_state[0] =pot_c_state[0]; // Stores the current reading of the potentiomet
Our other product: