Skip to content

Instantly share code, notes, and snippets.

@ubidefeo
Last active June 12, 2020 20:00
Show Gist options
  • Save ubidefeo/6098d95332b6529f1b0599bfd443ebae to your computer and use it in GitHub Desktop.
Save ubidefeo/6098d95332b6529f1b0599bfd443ebae to your computer and use it in GitHub Desktop.
Arduino MIDI controller Demo
/* 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;
}
@bitbank2
Copy link

The "optimizer" in me really doesn't like that filter function. The following would be a better way to do it:

  1. Use a circular buffer, don't move all the values
  2. Keep the sum variable as a static so that you can just add the new value and subtract the oldest each time through
  3. 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 :)

@ubidefeo
Copy link
Author

The "optimizer" in me really doesn't like that filter function. The following would be a better way to do it:

  1. Use a circular buffer, don't move all the values
  2. Keep the sum variable as a static so that you can just add the new value and subtract the oldest each time through
  3. 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.

@bitbank2
Copy link

bitbank2 commented Jun 12, 2020

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;
}

@ubidefeo
Copy link
Author

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment