Analog To Midi with MKR 1000
Build a device that recognizes an input frequency and outputs it to MIDI as the nearest corresponding note of the chromatic scale.
With this tutorial you use the Audio Frequency Meter Library and the Arduino Midi USB library and build a device that recognizes an input frequency and outputs it to MIDI as the nearest corresponding note of the chromatic scale.
Hardware Required
Arduino MKR1000 Board
1x 10k trimmer
2x 100k resistor
2x 47k resistor
1x 100n capacitor
1x 3.5mm jack
The Circuit
In order to get the most dynamic range even from low level inputs, the circuit consist of a non-inverting amplifier that brings the amplitude of the signal to the full input voltage range supported by the ADC. Sampling at full resolution means a better accuracy.
The 10k trimpot allows to adjust the gain of the amplifier matching the signal level with the ADC input range. This adjustment should be made looking at the output on the Arduino Software (IDE) Serial Monitor: when the note reading is stable while playing the same note, the gain is properly set.
As an alternative, you may purchase the Electret microphone amplifier - MAX4466 with adjustable gain that was designed specifically for this purpose.
Works on: MKR1000 and Zero boards.
Schematic
Software Essentials
Includes:
<AudioFrequencyMeter.h>
Library that contains the methods and functions to manage analog frequencies read through the analog inputs. In this sketch it recognizes the main frequency to play through MIDI.<MIDIUSB.h>
Library that creates a MIDI device through USB Host port; the device outputs notes in MIDI format and needs a sound generator.<frequencyToNote.h>
Defines the correspondence between audio frequencies and notes of the 88 keys scale. Part of MIDIUSB<pitchToNote.h>
Defines the correspondence between notes and MIDI note values. Part of MIDIUSBFunctions In Sketch
searchForNote(float frequency) Search for the nearest frequency that is in the vector of frequencies noteFrequency[ ]
noteOn(byte channel, byte pitch, byte velocity) Sends out to MIDI the event to turn on the note of the specified pitch on the specified MIDI Channel
void noteOff(byte channel, byte pitch, byte velocity) Sends out to MIDI the event to turn off the note of the specified pitch on the specified MIDI Channel
Code
1/*2
3 Analog to Midi Converter for Arduino MKR10004
5 Demonstrates how to sample an input signal and get back its corresponding MIDI note6
7 This example code is in the public domain8
9 https://www.arduino.cc/en/Tutorial/AnalogToMidi10
11 created by Arturo Guadalupi <a.guadalupi@arduino.cc>12
13 29 Jan 201614
15*/16
17#include <AudioFrequencyMeter.h>18#include <MIDIUSB.h>19#include <frequencyToNote.h>20#include <pitchToNote.h>21
22#define DEPTH 60 // Defines depth of the array for averaged frequencies23#define HUMAN_RATE 50 // Defines 50ms corresponding to 20 notes/s24#define MAX_DURATION 1000 // Defines the max play duration of the note25
26AudioFrequencyMeter meter;27
28int notesArray[DEPTH]; // Array to store detected notes and find the "correct" note which occurred the most often29int occurrences[DEPTH]; // Array in which the number of occurrences for each note are stored30
31bool marked[DEPTH]; // Array to indicate which of the notes have been checked32int frequencyIndex = 0; // Used to navigate to where the current note must be stored33
34int previousNote;35unsigned int startTime; // Used to determine when the note must stop36unsigned int humanTime; // Used to determine when the next note can be sampled (using HUMAN_RATE timing)37
38int intensity = 64; // The volume of the played note is fixed at 6439
40void setup() {41
42 // put your setup code here, to run once:43
44 Serial.begin(115200);45
46 pinMode(11, OUTPUT);47
48 meter.setBandwidth(75.00, 600.00); // Set available bandwidth between 75Hz and 600Hz49
50 meter.begin(A0, 45000); // Initialize A0 at sample rate of 45kHz51}52
53void loop() {54
55 float frequency = meter.getFrequency();56
57 if (frequency > 0)58
59 {60
61 int noteIndex = searchForNote(frequency); // Find the index of the corresponding frequency62
63 int note = notePitch[noteIndex]; // Use that index to find the corresponding note in the LUT64
65 notesArray[frequencyIndex++] = note; // Store the note and continue to next value in array66
67 if (frequencyIndex > DEPTH) // If all the notes have been stored68
69 {70
71 frequencyIndex = 0; // Reset the index72
73 int i, j;74
75 /*Reset all the occurrences and marked positions*/76
77 for (i = 0; i < DEPTH; i++)78
79 {80
81 occurrences[i] = 0;82
83 marked[i] = 0;84
85 }86
87 /*Count the number of occurrences*/88
89 for (i = 0; i < DEPTH; i++)90
91 {92
93 for (j = 0; j < DEPTH; j++)94
95 {96
97 // If notes are the same and the note has not been marked yet98
99 if ((!marked[j]) && (notesArray[j] == notesArray[i]))100
101 {102
103 occurrences[i]++; // Increment the number of occurrences104
105 marked[j] = true; // Signal the note as marked106
107 }108
109 }110
111 }112
113 int numberOfdifferentFrequencies = 0; // Used to determine how many different Frequencies have been detected114
115 for (i = 0; i < DEPTH; i++)116
117 {118
119 // If the counter does not equal zero120
121 if (occurrences[i] != 0)122
123 {124
125 // Store the the various detected Frequencies126
127 notesArray[numberOfdifferentFrequencies] = notesArray[i];128
129 // And the number of occurrences for each note130
131 occurrences[numberOfdifferentFrequencies] = occurrences[i];132
133 numberOfdifferentFrequencies++; // Increment the number of detected Frequencies134
135 }136
137 }138
139 /*Search for the maximum number of occurrences to discriminate the played note*/140
141 int maxNumberOfFrequencies = occurrences[0];142
143 int rightIndex = 0;144
145 for (i = 0; i < numberOfdifferentFrequencies; i++);146
147 {148
149 // If a new maximum exist150
151 if (occurrences[i] > maxNumberOfFrequencies)152
153 {154
155 // Update the value156
157 maxNumberOfFrequencies = occurrences[i];158
159 // Update the index160
161 rightIndex = i;162
163 }164
165 }166
167 note = notesArray[rightIndex]; // Note to be played is that with the most occurrences168
169 // If the specified time has elapsed before the next note170
171 if (millis() - humanTime > HUMAN_RATE)172
173 {174
175 humanTime = millis(); // Update the timer176
177 startTime = millis(); // Update the note duration178
179 noteOff(0, previousNote, intensity); // Stop playing the previous note180
181 previousNote = note; // Update previous note with the new one182
183 Serial.println(note); // Print the note to be played184
185 noteOn(0, note, intensity); // Play the note!186
187 }188
189 }190
191 }192
193 if (millis() - startTime > MAX_DURATION) // If maximum time elapsed194
195 noteOff(0, previousNote, intensity); // Turn the note off196}197
198int searchForNote(float frequency)199{200
201 float minimum = abs(frequency - noteFrequency[0]);202
203 float newMinimum;204
205 int index = 0;206
207 /*Search for the nearest frequency that is in the vector*/208
209 for (int i = 0; i < NUMBER_OF_NOTES - 1; i++)210
211 {212
213 newMinimum = abs(frequency - noteFrequency[i]);214
215 if (newMinimum < minimum)216
217 {218
219 minimum = newMinimum;220
221 index = i;222
223 }224
225 }226
227 return index;228}229
230void noteOn(byte channel, byte pitch, byte velocity) {231
232 midiEventPacket_t noteOn = {0x09, 0x90 | channel, pitch, velocity};233
234 MidiUSB.sendMIDI(noteOn);235}236
237void noteOff(byte channel, byte pitch, byte velocity) {238
239 midiEventPacket_t noteOff = {0x08, 0x80 | channel, pitch, velocity};240
241 MidiUSB.sendMIDI(noteOff);242}
Suggest changes
The content on docs.arduino.cc is facilitated through a public GitHub repository. If you see anything wrong, you can edit this page here.
License
The Arduino documentation is licensed under the Creative Commons Attribution-Share Alike 4.0 license.