This tutorial refers to a product that has reached its end-of-life status.

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

  • 1x LMV358 or TLV2462

  • 2x 100k resistor

  • 2x 47k resistor

  • 1x 100n capacitor

  • 1x 3.5mm jack

The Circuit

The circuit for this tutorial.
The circuit for this tutorial.

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

The circuit for this tutorial.
The circuit for this tutorial.

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 MIDIUSB

Functions 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 MKR1000
4
5 Demonstrates how to sample an input signal and get back its corresponding MIDI note
6
7 This example code is in the public domain
8
9 https://www.arduino.cc/en/Tutorial/AnalogToMidi
10
11 created by Arturo Guadalupi <a.guadalupi@arduino.cc>
12
13 29 Jan 2016
14
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 frequencies
23#define HUMAN_RATE 50 // Defines 50ms corresponding to 20 notes/s
24#define MAX_DURATION 1000 // Defines the max play duration of the note
25
26AudioFrequencyMeter meter;
27
28int notesArray[DEPTH]; // Array to store detected notes and find the "correct" note which occurred the most often
29int occurrences[DEPTH]; // Array in which the number of occurrences for each note are stored
30
31bool marked[DEPTH]; // Array to indicate which of the notes have been checked
32int frequencyIndex = 0; // Used to navigate to where the current note must be stored
33
34int previousNote;
35unsigned int startTime; // Used to determine when the note must stop
36unsigned 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 64
39
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 600Hz
49
50 meter.begin(A0, 45000); // Initialize A0 at sample rate of 45kHz
51}
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 frequency
62
63 int note = notePitch[noteIndex]; // Use that index to find the corresponding note in the LUT
64
65 notesArray[frequencyIndex++] = note; // Store the note and continue to next value in array
66
67 if (frequencyIndex > DEPTH) // If all the notes have been stored
68
69 {
70
71 frequencyIndex = 0; // Reset the index
72
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 yet
98
99 if ((!marked[j]) && (notesArray[j] == notesArray[i]))
100
101 {
102
103 occurrences[i]++; // Increment the number of occurrences
104
105 marked[j] = true; // Signal the note as marked
106
107 }
108
109 }
110
111 }
112
113 int numberOfdifferentFrequencies = 0; // Used to determine how many different Frequencies have been detected
114
115 for (i = 0; i < DEPTH; i++)
116
117 {
118
119 // If the counter does not equal zero
120
121 if (occurrences[i] != 0)
122
123 {
124
125 // Store the the various detected Frequencies
126
127 notesArray[numberOfdifferentFrequencies] = notesArray[i];
128
129 // And the number of occurrences for each note
130
131 occurrences[numberOfdifferentFrequencies] = occurrences[i];
132
133 numberOfdifferentFrequencies++; // Increment the number of detected Frequencies
134
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 exist
150
151 if (occurrences[i] > maxNumberOfFrequencies)
152
153 {
154
155 // Update the value
156
157 maxNumberOfFrequencies = occurrences[i];
158
159 // Update the index
160
161 rightIndex = i;
162
163 }
164
165 }
166
167 note = notesArray[rightIndex]; // Note to be played is that with the most occurrences
168
169 // If the specified time has elapsed before the next note
170
171 if (millis() - humanTime > HUMAN_RATE)
172
173 {
174
175 humanTime = millis(); // Update the timer
176
177 startTime = millis(); // Update the note duration
178
179 noteOff(0, previousNote, intensity); // Stop playing the previous note
180
181 previousNote = note; // Update previous note with the new one
182
183 Serial.println(note); // Print the note to be played
184
185 noteOn(0, note, intensity); // Play the note!
186
187 }
188
189 }
190
191 }
192
193 if (millis() - startTime > MAX_DURATION) // If maximum time elapsed
194
195 noteOff(0, previousNote, intensity); // Turn the note off
196}
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.