-
-
Save ubidefeo/6098d95332b6529f1b0599bfd443ebae to your computer and use it in GitHub Desktop.
| /* Arduino analog to USB MIDI | |
| Ubi de Feo @ Bar Arduino | |
| https://www.youtube.com/watch?v=0zNmt_IKwRg | |
| supported boards: | |
| - Arduino MKR Family | |
| - Arduino Nano 33 IoT | |
| **** Sketch for BLE MIDI (Nano BLE and Nano BLE Sense) **** | |
| **** can be found here https://gist.github.com/ubidefeo/3e7362235cee5317cec4a36f07585e29 **** | |
| **** Thank you Larry Bank for pointing out the lack of a BLE version **** | |
| */ | |
| #include "MIDIUSB.h" | |
| void controlChange(byte channel, byte control, byte value) { | |
| midiEventPacket_t event = {0x0B, 0xB0 | channel, control, value}; | |
| MidiUSB.sendMIDI(event); | |
| } | |
| const int SAMPLES_COUNT = 20; | |
| // FIFO | |
| int samplesBuffer[SAMPLES_COUNT] = {0}; | |
| int sampleIndex = 0; | |
| const int knobInputPin = A1; | |
| const int changeThreshold = 20; | |
| int lastSensorValue; | |
| int lastMappedValue; | |
| unsigned long samplesSum = 0; | |
| void setup() { | |
| Serial.begin(57600); | |
| delay(5000); | |
| Serial.println("SETUP"); | |
| } | |
| void loop() { | |
| int knobReadOut = filterAnalogValue(knobInputPin); | |
| int mappedValue = map(knobReadOut, 0, 1023, 0, 127); | |
| if(lastMappedValue != mappedValue){ | |
| // Serial.println("************ CHANGE ***************"); | |
| // Serial.print(">>> "); | |
| // Serial.print(mappedValue); | |
| // Serial.println(" <<<"); | |
| controlChange(0, 4, mappedValue); | |
| } | |
| lastMappedValue = mappedValue; | |
| } | |
| int filterAnalogValue(int _inputPin){ | |
| samplesSum = 0; | |
| int analogValue = analogRead(_inputPin); | |
| //int valueDelta = abs(analogValue - lastSensorValue); | |
| // Serial.print(analogValue); | |
| // Serial.print(" - "); | |
| // Serial.println(lastSensorValue); | |
| // if(valueDelta < changeThreshold){ | |
| // analogValue = lastSensorValue; | |
| // } | |
| if(sampleIndex < SAMPLES_COUNT){ | |
| samplesBuffer[sampleIndex] = analogValue; | |
| sampleIndex++; | |
| }else{ | |
| for(int samplePosition = 0; samplePosition < sampleIndex - 1; samplePosition++){0; | |
| samplesBuffer[samplePosition] = samplesBuffer[samplePosition + 1]; | |
| } | |
| samplesBuffer[SAMPLES_COUNT - 1] = analogValue; | |
| } | |
| for(int samplePosition = 0; samplePosition < sampleIndex; samplePosition++){0; | |
| samplesSum += samplesBuffer[samplePosition]; | |
| } | |
| //lastSensorValue = analogValue; | |
| if(samplesSum == 0) return 0; | |
| unsigned int filteredValue = samplesSum / sampleIndex; | |
| return filteredValue; | |
| } |
The "optimizer" in me really doesn't like that filter function. The following would be a better way to do it:
- Use a circular buffer, don't move all the values
- Keep the sum variable as a static so that you can just add the new value and subtract the oldest each time through
- Use a power of 2 buffer length so that you can just shift the sum to get the average instead of using divide
Carry on :)
great approach.
I initially went for memmove() but it turned out to be even slower than this cycle approach.
A circular buffer came to my mind in the beginning, then I thought this was easier to understand.
Would you care for writing your version and keep the thread running?
I think I can learn something from your proposed optimisation, and I'd love to :)
thanks
u.
The MIDIUSB library complains about compiling on the Nano 33 BLE, but I believe this will work:
#include "MIDIUSB.h"
const int SAMPLES_COUNT = 16; // use a power of 2 to avoid having to use divide for averaging
const int SAMPLES_SHIFT = 4; // the log2 of the samples count
void controlChange(byte channel, byte control, byte value) {
midiEventPacket_t event = {0x0B, 0xB0 | channel, control, value};
MidiUSB.sendMIDI(event);
}
// Circular buffer
int samplesBuffer[SAMPLES_COUNT] = {0};
int sampleIndex = 0;
unsigned long sampleCount = 0; // total samples collected
const int knobInputPin = A1;
const int changeThreshold = 20;
int lastSensorValue;
int lastMappedValue;
int samplesSum = 0;
void setup() {
Serial.begin(57600);
delay(5000);
Serial.println("SETUP");
}
void loop() {
int knobReadOut = filterAnalogValue(knobInputPin);
int mappedValue = map(knobReadOut, 0, 1023, 0, 127);
if(lastMappedValue != mappedValue){
// Serial.println("************ CHANGE ***************");
// Serial.print(">>> ");
// Serial.print(mappedValue);
// Serial.println(" <<<");
controlChange(0, 4, mappedValue);
}
lastMappedValue = mappedValue;
delay(100);
}
int filterAnalogValue(int _inputPin){
unsigned int filteredValue
int analogValue = analogRead(_inputPin);
//int valueDelta = abs(analogValue - lastSensorValue);
// Serial.print(analogValue);
// Serial.print(" - ");
// Serial.println(lastSensorValue);
// if(valueDelta < changeThreshold){
// analogValue = lastSensorValue;
// }
samplesSum -= samplesBuffer[sampleIndex]; // subtract the oldest value (the index wrapped around)
samplesBuffer[sampleIndex++] = analogValue;
sampleIndex &= (SAMPLE_COUNT-1); // ** MUST BE A POWER OF 2! **
sampleCount++; // keep track of total samples collected
samplesSum += analogValue; // add the newest value
// At startup, return the raw value; once we have enough samples, return the average
if (sampleCount < SAMPLES_COUNT) {
filteredValue = analogValue;
} else {
filteredValue = (samplesSum >> SAMPLES_SHIFT);
}
return filteredValue;
}
hey @bitbank2
you can find the BLE Sketch here https://gist.github.com/ubidefeo/3e7362235cee5317cec4a36f07585e29
I've also added a comment to thank you for pointing it out :)
The "optimizer" in me really doesn't like that filter function. The following would be a better way to do it:
Carry on :)