velocity sensitive piezo trigger

Thread Starter

seayaker

Joined Jan 27, 2009
54
Is there a way to modify this code to make it velocity sensitive? This is the only code I found that works with my board (Teensy 3.6). I have a piezo connected to (A0) that triggers a midi note but only at one volume, The value of the piezo varies from 50-150 depending how hard it's hit. Can this be read and sent as a midi velocity message?



C:
/*
   MIDIUSB_test.ino

   Created: 4/6/2015 10:47:08 AM
   Author: gurbrinder grewal
   Modified by Arduino LLC (2015) & Grumpy Mike
*/

#include "MIDIUSB.h"

// First parameter is the event type (0x09 = note on, 0x08 = note off).
// Second parameter is note-on/note-off, combined with the channel.
// Channel can be anything between 0-15. Typically reported to the user as 1-16.
// Third parameter is the note number (48 = middle C).
// Fourth parameter is the velocity (64 = normal, 127 = fastest).

void noteOn(byte channel, byte pitch, byte velocity) {
  midiEventPacket_t noteOn = {0x09, 0x90 | channel, pitch, velocity};
  MidiUSB.sendMIDI(noteOn);
}

void noteOff(byte channel, byte pitch, byte velocity) {
  midiEventPacket_t noteOff = {0x08, 0x80 | channel, pitch, velocity};
  MidiUSB.sendMIDI(noteOff);
}

void setup() {
  Serial.begin(115200);
}

// First parameter is the event type (0x0B = control change).
// Second parameter is the event type, combined with the channel.
// Third parameter is the control number number (0-119).
// Fourth parameter is the control value (0-127).

void loop() {
  int val;
  val = analogRead(A0);
  if (val > 600) { // if it is greater than the threshold
    noteOn(0, 50, 127); // send a note on message the 68 is the pitch of the note
    MidiUSB.flush(); // send the MIDI message
    while (analogRead(A0) > 600) { } // wait here until the signal has dropped
    noteOff(0, 50, 127); // send the note off message
    MidiUSB.flush(); // send the MIDI message
  }
Mod edit: code tags
 
Last edited by a moderator:

MrSoftware

Joined Oct 29, 2013
1,778
I only skimmed, but if I skimmed correctly then the 3rd parameter to noteOn() is the "velocity" or volume. Your variable "val" is the analog value read from your piezo input pin. Right now you only use that variable once in an if() as a binary on/off. Instead you need to pass "val" as the 3rd parameter into noteOn(), BUT FIRST you need to find out what the max and min values that noteOn() will take and multiply "val" by some factor that will give you a number within the range that is OK for noteOn().
 

JohnInTX

Joined Jun 26, 2012
4,072
I only skimmed, but if I skimmed correctly then the 3rd parameter to noteOn() is the "velocity" or volume. Your variable "val" is the analog value read from your piezo input pin. Right now you only use that variable once in an if() as a binary on/off. Instead you need to pass "val" as the 3rd parameter into noteOn(), BUT FIRST you need to find out what the max and min values that noteOn() will take and multiply "val" by some factor that will give you a number within the range that is OK for noteOn().
To amplify on that a bit, you might implement a peak-detector. Once you get above the threshold at line 39, continue to sample the piezo using analogRead(A0) in a tight loop. Compare each new value with the previous one. If its larger, store that as previous value and read again. Eventually the new value will be less than the previous one as the piezo signal drops. When that happens, the 'previous' value is your 'peak' and you can use it to specify the volume, maybe with some scaling as @MrSoftware describes.
Whether you can get this all done in 'Arduino time' without losing the beat I don't know. Don't forget to reset the 'previous' value on each hit.

Good luck!
 

Thread Starter

seayaker

Joined Jan 27, 2009
54
I only skimmed, but if I skimmed correctly then the 3rd parameter to noteOn() is the "velocity" or volume. Your variable "val" is the analog value read from your piezo input pin. Right now you only use that variable once in an if() as a binary on/off. Instead you need to pass "val" as the 3rd parameter into noteOn(), BUT FIRST you need to find out what the max and min values that noteOn() will take and multiply "val" by some factor that will give you a number within the range that is OK for noteOn().
Can you tell me why this doesn't work? This sketch was modified to use 21 pins but still only works with 1 A0, anybody know whats wrong?

const int channel = 10; // General MIDI: channel 10 = percussion sounds
const int PINS = 22; // number of signals incoming
const int note[PINS] = {36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57}; // array of MIDI note values for read signals

const int analogPin[PINS] = {A0,A1,A2,A3,A4,A5,A6,A7,A8,A9,A10,A11,A12,A13,A14,A15,A16,A17,A18,A19,A20,A21}; //array of analog PINs
const int thresholdMin = 60; // minimum reading, avoid noise and false starts
const int peakTrackMillis = 12;
const int aftershockMillis = 25; // aftershocks & vibration reject

int state[PINS]; // 0=idle, 1=looking for peak, 2=ignore aftershocks
int peak[PINS]; // remember the highest reading
int piezo[PINS];
elapsedMillis msec[PINS]; // timer to end states 1 and 2

void setup() {
Serial.begin(115200);
while (!Serial && millis() < 2500) /* wait for serial monitor */ ;
Serial.println("Piezo Peak Capture");
}


void loop() {


for (int i=0;i<PINS;i++){
piezo = analogRead(analogPin);

int voltage=piezo;

switch (state) {
// IDLE state: wait for any reading is above threshold. Do not set
// the threshold too low. You don't want to be too sensitive to slight
// vibration.
case 0:
if (voltage > thresholdMin) {
//Serial.print("begin peak track ");
//Serial.println(voltage);
peak = voltage;
msec = 0;
state = 1;
}
return;

// Peak Tracking state: capture largest reading
case 1:
if (voltage > peak) {
peak = voltage;
}
if (msec >= peakTrackMillis) {
//Serial.print("peak = ");
//Serial.println(peak);
int velocity = map(peak, thresholdMin, 1023, 22, 127);
usbMIDI.sendNoteOn(note, velocity, channel);
msec = 0;
state = 2;
}
return;

// Ignore Aftershock state: wait for things to be quiet again.
default:
if (voltage > thresholdMin) {
msec = 0; // keep resetting timer if above threshold
} else if (msec > aftershockMillis) {
usbMIDI.sendNoteOff(note, 0, channel);
state = 0; // go back to idle when
}
}
}
// Add other tasks to loop, but avoid using delay() or waiting.
// You need loop() to keep running rapidly to detect Piezo peaks!

// MIDI Controllers should discard incoming MIDI messages.
// http://forum.pjrc.com/threads/24179-Teensy-3-Ableton-Analog-CC-causes-midi-crash
while (usbMIDI.read()) {
// ignore incoming messages
}
}


void peakDetect(int voltage) {
}
 
Last edited:
Top