-
-
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 :)