Hi all, I'm really glad I found these forums, they have been an invaluable resource! I have a question that someone might be able to help with if they have a bit of familiarity with C++ ...I am still only starting to get my head around it.
I've used this example by Ian Harvey as a starting point to create a MIDI hand-drum. The idea is piezo triggers send velocity sensitive 'hits' to MIDI over USB. There are two pots to control scale and key.
The issue I'm having is when I connect to my Mac running Logic, there are low velocity hits running constantly in the background. I'm not sure where they are coming from in the code. When I strike the piezos they sometimes work to strike a note and sometimes do not work. I've tried adjusting the sensitivity parameters up and down, but it does not seem to have much effect.
In addition, in some scales the controller seems to send short staccato notes, sometimes long sustained notes. I haven't worked out why this is.
Can anyone see at a glance where the background signals are coming from, and what's causing the discrepancy in note length? I have to admit I don't quite understand the MIDI_noteOff function and the various timers. I'm unreasonably pleased for just getting the pots to work for setting scales!
Any help gratefully received.
]
#include "MIDIUSB.h"
// Needs MIDIUSB library to be installed
static void MIDI_setup();
static void MIDI_noteOn(int ch, int note, int velocity);
static void MIDI_noteOff(int ch, int note);
const int MIDI_CHANNEL=1;
const int BAUD_RATE=115200;
const int scalePin = A0;
const int keyPin = A1;
const int NCHANNELS = 3;
const int KEYRANGE = 12; // Range in semitones
const int NSCALES = 7; // Number of modes/scales
const int BASENOTE = 50; // Lowest available note (MIDI number)
const int inPins[NCHANNELS] = { A2, A3};
const int thresholdLevel[NCHANNELS] = { 50, 50 }; // ADC reading to trigger; lower => more sensitive
const int maxLevel[NCHANNELS] = { 200, 200 }; // ADC reading for full velocity; lower => more sensitive
const int SCALES[NSCALES][9] = {
{0,2,4,5,7,9,11,12,14}, // Ionian, Major
{0,4,7,11,12,16,18,19,23}, // Aegean
{0,3,7,10,12,14,15,19,21}, // Voyager
{0,3,7,8,10,13,15,16,20}, // Equinox
{0,5,7,8,10,12,14,15,17}, // Dorian
{0,7,8,10,12,14,15,19,21}, // Phrygian
{0,5,7,8,12,13,17,19,20}, // Ake Bono
};
int midiNotes[NCHANNELS];
int scaleCounter;
int key;
int scale;
static unsigned int vmax[NCHANNELS] = { 0 };
static unsigned int trigLevel[NCHANNELS];
static unsigned int counter[NCHANNELS] = { 0 };
static unsigned int CTR_NOTEON = 10; // Roughly 5ms sampling peak voltage
static unsigned int CTR_NOTEOFF = CTR_NOTEON + 30; // Duration roughly 15ms
// 0 -> not triggered
// 1..CTR_NOTEON -> sampling note on
// CTR_NOTEON+1 .. CTR_NOTEOFF -> note off
static int statusPin = 2;
void setup() {
// put your setup code here, to run once:
Serial.begin(BAUD_RATE);
analogReference(DEFAULT);
pinMode(statusPin, OUTPUT);
digitalWrite(statusPin, LOW);
for (int i = 0; i < NCHANNELS; i++)
{
pinMode(inPins[i], INPUT);
analogRead(inPins[i]);
trigLevel[i] = thresholdLevel[i];
}
MIDI_setup();
}
void loop()
{
// volumeCheck();//check the volume knob
drumCheck();//check if any of the capacitive pads have been touched
// dcValCheck();//check the decay knob
// buttonCheck();//check for button presses to change the scale
scaleCounter++;
if(scaleCounter == 100);//check scale and key periodically
{
scaleCheck();
scaleCounter = 0;
}
// counter++;
// if(counter == 10000);//don't check battery all the time, slows opperation
// {
// getBattery();
// counter = 0;
// }
// oledPrint();//print to TeensyView
}
void scaleCheck() {
int sv = analogRead(scalePin);
int s = map(sv, 0, 1023, 0, NSCALES);
int kv = analogRead(keyPin);
int k = map(kv, 0, 1023, 0, KEYRANGE);
if ( s != scale || k != key )
{
int n;
for (n=0; n < NCHANNELS; n++)
{
midiNotes[n] = BASENOTE + k + SCALES[s][n];
}
s = scale;
k = key;
}
}
void drumCheck() {
int ch;
for (ch=0; ch < NCHANNELS; ch++)
{
unsigned int v = analogRead(inPins[ch]);
if ( counter[ch] == 0 )
{
if ( v >= trigLevel[ch] )
{
vmax[ch] = v;
counter[ch] = 1;
digitalWrite(statusPin, HIGH);
}
}
else
{
if ( v > vmax[ch] )
vmax[ch] = v;
counter[ch]++;
if ( counter[ch] == CTR_NOTEON )
{
long int vel = ((long int)vmax[ch]*127)/maxLevel[ch];
//Serial.println(vel);
if (vel < 5) vel = 5;
if (vel > 127) vel = 127;
MIDI_noteOn(MIDI_CHANNEL, midiNotes[ch], vel);
trigLevel[ch] = vmax[ch];
}
else if ( counter[ch] >= CTR_NOTEOFF )
{
MIDI_noteOff(MIDI_CHANNEL, midiNotes[ch]);
counter[ch] = 0;
digitalWrite(statusPin, LOW);
}
}
// The signal from the piezo is a damped oscillation decaying with
// time constant 8-10ms. Prevent false retriggering by raising
// trigger level when first triggered, then decaying it to the
// threshold over several future samples.
trigLevel[ch] = ((trigLevel[ch] * 29) + (thresholdLevel[ch] * 1)) / 30;
}
}
// MIDI Code
//
// See https://www.midi.org/specifications/item/table-1-summary-of-midi-message
void MIDI_setup()
{
}
void MIDI_noteOn(int ch, int note, int velocity)
{
midiEventPacket_t noteOn = {0x09, 0x90 | (ch-1), note & 0x7F, velocity & 0x7F};
MidiUSB.sendMIDI(noteOn);
MidiUSB.flush();
}
void MIDI_noteOff(int ch, int note)
{
midiEventPacket_t noteOff = {0x08, 0x80 | (ch-1), note, 1};
MidiUSB.sendMIDI(noteOff);
MidiUSB.flush();
}