FSR pedal

Status
Not open for further replies.
If the pedal notes were something like if A0 > threshold for > X milliseconds play note #42, if A0 > threshold for < X milliseconds play note #43?
 
If the pedal notes were something like if A0 > threshold for > X milliseconds play note #42, if A0 > threshold for < X milliseconds play note #43?

But the splash, unlike the chick, is produced when transitioning from closed to open, correct?

And the chick is the sound you get when you close the pedal (which I assume has a short envelope as the closed cymbals dampen quickly)?

The logic is based on the close-point. If an open cymbol is closed it will 'chick' (unless done very slowly) and if an closed cymbal is opened 'quickly-enough' it will 'splash'

(Technically the notes are supressed if the absolute difference in raw readings is not greater than a threshold value.)

Both will have velocity values proportional to the change in (raw) signal values on either side of the relvenant transition.

For 'splash' we're looking for a drop below the closed point and a raw value difference at least as great as minSplash.

So increasing minSplash will make the splash less sensitive and you will be able to release it more quickly without firing it.

But the timing parameter will also impact the other parameters and not always in a linear and predicable way.

I know you think we were close before and somehow a minor tweak will give you want you want but it's not that simple.


What I need now is to be sure the code I share with you has workable zone-boundaries.

I can't recall the way you set up the FSR as a voltage divider so I can't guess the min and max readings possible.

What is the value of that resistor and are you pulling to ground while the FRS is tied to HIGH signal?


So here's the line by line on the configuration. This is where 95% of the work remaining lies... we need to figure out how to get a usable signal from the FSR that does both zone-mapping of MIDI notes for A1 and can trigger notes itself when it's signal moves across the close-point from zoneMap() if the reading across the transition is above a threshold value.


Code:
//********************************************************************************* SET PIN OPTIONS**************
const int PINS = 3;                                   // number of piezo signals incoming (does not include HH signal pin)
const int analogPin[PINS] = {A1,A2,A3};               //array of analog PINs used for piezo readings
const int zonePin = A0;                               // high-hat pedal pin                <- I NEED THIS AT 'A9' FOR TESTING AND MAY FORGET TO RETURN TO 'A0'
These lines set the pins. Note I need to set to A9 on my test rig so I may share code with zonePin set to that instead of A0 so watch for that!.

If you have 20 piezo senors then PINS = 20 and the analogPin[] array needs 20 elements with the analog pin numbers in the order you want to read them.




Code:
//********************************************************************************* SET HIGH HAT ZONE CONFIGURATION OPTIONS**************
const int ZONECOUNT = 5 ;                             // number of zones for FSR as modifier for HH
const int zoneMap[ZONECOUNT] = {300,500,700,850,1024}; //raw reading value boundaries between zones
This stuff is key to getting a usable signal for note selection as well as finding the close-point you want to use for triggering splash and chick.

1024 makes sure the 'while' condition fails when j = 4 but it's the element before (shown as 850 here) that is the critical close-point of the pedal. Above this point the splash is possible if you release fast enough and if you rise to this point from below quickly enough it will 'chick'. These zones can be set with the timing parameter (below) set very large to keep the number of prints small.


Code:
//********************************************************************************* SET MIDI CHANNEL AND NOTE VALUES *************************
const int channel = 10;                               // General MIDI: channel 10 = percussion sounds
const int note[PINS] = {-1,36,38};                    // note values for each pin - negative value flags to read the HH array instead
const int hhNote[ZONECOUNT] = {46,2,3,4,42} ;         // note values for flavours of HH from open to closed (no general MIDI for intermediaries so used low values as placeholders)
const int splashNote = 55;                            // note value for 'splash' note sent by HH pedal
const int chickNote = 42;                             // note value for 'chick' note sent by HH pedal (no general MIDI so set to 'closed hat' for now)
This is where you configure for MIDI values.

The ones I'm not sure about are the chickNote, which I've set to the same as the closed HH hit, and the intermediate values in the HHNote[] array which I've set to non-drum map (low) note values 2,3 and 4.

The array note[] holds the midi note values for the piezo triggers with -1 there to indicate which PIN is the HH piezo. As I have it in the first entry that corresponds with the A1 pin in the analogPin[] array.

The note values sent by that pin are modified by the current zone value by selecting from the nnNote[] array with the current zone value.


Code:
//********************************************************************************* SET TIMING AND THRESHOLD ADJUSTMENT OPTIONS**************
//****PIEZOS
const int peakTrackMillis = 12;      // how long to keep reading looking for the peak to become velocity
const int aftershockMillis = 25;     // aftershocks & vibration reject
const int thresholdMin = 60;         // minimum reading before a note will be generated (too low = phantom notes)
These are the pieizo settings for how long to window readings looking for maxima, how long to wait for aftershocks to die down before allowing a repeat note on that sensor and the minimum value that will trigger a note event (of the maxima tracked during the window set by peakTrackMillis).

This algorithm is unchanged from the example file you started with. It will break if there is too much processing outside of the main loop which is one reason to keep the FRS logic light and on a limited updated.

Piezo signals are typically in the audio range while the trigger event we're extracting is on the human time-scale. The algorithm relies on the vast difference in these time scales to find a representative maximum value to map into a velocity. There are other more robust methods of tracking a percussive signal envelope from a piezo should this turn out to be unreliable reading only one piezo per code loop.


Code:
//****HIGH HAT PEDAL
const int zoneRefesh = 8;            // mS between HH pedal updates -  hat pedal is low freq. so we can check every few mS.
const int minSplash = 150;            // raw value difference required to fire 'splash'
const int minChick = 90;             // raw value difference required to fire 'chick'
const int deadBandLimit = 10;        // raw value deadband to limit HH updates above noise threshold - can very large with small number of zones
These are the settings that control the HH pedal's behaviour.

The settings I've listed here a likely more usable than in the posted code.

The first, zoneRefresh, is the minimum time between scans of the HH-FSR's raw reading. Set this too high and you get latency before zones change or notes fire. For setting the zoneMap[] you may want to turn this way up (500 mS?) to limit the data being sent to the serial montor/plotter while you get these values tuned. Once you have that I think single digits should work but if your pedal is too touchy you might want to boost up to 10 or 12 and if it's working well you might want to creep the value downward as far as you can before note firing become difficult.

The values minSplash and minChick are the minimum difference between raw readings of the FSR after it transitions 'to' or 'from' zone 4 that will result in a note being generated.

I assume splash should be higher as you have to drop the pedal to get the sound and chick should still fire down into the delicate range. Note there are raw difference values and their optimum setting will depend on both the range of usable values you're getting from the FSR voltage divider and on how fast the zoneRefresh is set. If it's very fast (small value) then there is less time for the value to change and so a smaller value will represent the same speed of signal change when the FSR is polled more frequently. This makes these harder to tune and you should expect to have to come back to them each time you adjust the polling speed.
 
Here is the line-by-line for the HH pedal stuff:


Code:
//*************HIGH HAT PEDAL SIGNAL HANDLING - AS MODIFIER AND AS NOTE GENERATOR
void HHpedal() {
  if (zonePoll>zoneRefesh) { // modifiying pedal is low Freq. so limit reads

     [I].... stuff happens here![/I]

    zonePoll = 0;
    //Serial.println(zone);
  }
}
Here's the main thing to understand about this code. It gets called every loop but the inner-workings only run when the timer value zonePoll is greater than our polling minimum.

It is a timer value which is the number of milliseconds elapsed since it was last reset it is reset to zero at the bottom of the code every time it's run.

So it will run at approximately 1/(zoneRefesh / 1000) hertz so set to 8 mS it will run about 125 times per second which is super-fast by human standards but pretty slow for Teensy.

This makes reading this signal fundamentally different that the piezo that we want to read thousands of times per seconds if possible.

Code:
    rawZone = analogRead(zonePin);
    rawDelta =  rawZone - rawZoneLag;
Here we read the current raw value from the FSR and store it to an intermediary variable rawZone.
We then make a 'delta' or change value that holds a signed integer whose magnitude is proportional to the change in signal between this firing and the previous.



Next is the conditional check to see if the change in the FSR signal is big enough to bother with:

Code:
    if (abs(rawDelta)>deadBandLimit){ //also limit by change size

    [I] ...stuff happens here[/I]


      rawZoneLag = rawZone;
    }

Note the lag value is only reset after we accept a new value for zone. This means the dead-band zone moves with us each time we move outside of it. The zone value is recalculated each time this occurs but it will often have the same value.

Inside this conditional we have several things happening.

1) we check to see what zone the raw value corresponds to, and set the zone variable so it can modify the HH piezo-signal note value.
2) we need a velocity value to use with chick and splash notes if required
3) we need to check whether the signal has crossed the close point since the lagged reading in a downward direction to (potentially) trigger splash
4) we need to check whether the signal has crossed the close point since the lagged reading in a upwards direction to (potentially) trigger chick

Here is 1)
Code:
      int j = 0;
      while ( rawZone>zoneMap[j] ) {
        j++;
      }
      zone = j;
This counts upwards each time it finds the raw reading is greater than the next boundary from the zone-map. If the conditional statement rawZone>zoneMap[j] is true j is incremented by one until it is no longer true. If it's past the close point then j = 4 when this becomes false and so never gets incremented to 5. Then zone is set to the last j value. If none are true it will return zone = 0.

Note zone is set every few mS and whatever it's current value will determine what midi note is sent when the HH piezo is played.

2) Velocity:

Code:
      int velocity = abs(rawDelta)>>3;
The absolute value (magnitude of the value not counting the sign if it's negative) is bit-shifted by 3 bits to bring it from the 10 bits of default resolution to the seven bits of MIDI resolution. It is the same as dividing by 8 (but it's really dividing by 2 three times!).

3) Splash
Code:
      if (rawZoneLag > zoneMap[ZONECOUNT-1] && rawZone < zoneMap[ZONECOUNT-1]) {

        usbMIDI.sendNoteOff(chickNote, 0, channel); // can send note-on with vel=0 instead
        if (rawDelta + minSplash < 0) { // delta signal is negative when splash is possible
          usbMIDI.sendNoteOn(splashNote, velocity, channel);
        }
      }
The outer conditional says "if the lagged reading was greater than the close point (which is at index=3 or ZONECOUNT-1) and the current reading is less than the closepoint"

For both to be TRUE the signal crossed must have dropped across the close point. The chick note should end by this point and so a note-off is sent. A second conditional (requiring the change to be a bigger negative value than minSplash) limits the splash notes when the pedal is released gently.

4) Chick
Code:
      if (rawZone > zoneMap[ZONECOUNT-1]  && rawZoneLag < zoneMap[ZONECOUNT-1]) {
        // stop 'splash' and play 'chick' if over threshold
        usbMIDI.sendNoteOff(splashNote, 0, channel); // can send note-on with vel=0 instead
        if (rawDelta > minChick) { 
          usbMIDI.sendNoteOn(chickNote, velocity, channel);
        }
      }
Similarly the Splash is turned off when the close-point is reached from below and the chick is fired only if the signal change is greater than the threshold value (this time both are positive and can be compared directly).



I hope this will help getting this configured. I was able to track a pot well enough I could make splash and chick notes at will and select whatever zone I want but it's not very responsive trying to turn a knob quick enough to test the splash realistically.
 
Last edited:
This is going to take some time for me to digest, I am beginning to understand a little bit. The FSR is connected to +3v and A0, I have a 10k resistor connected to A0 and gnd.

But the splash, unlike the chick, is produced when transitioning from closed to open, correct?

In real life the sound is created when the cymbals are brought together, if they are quickly released the cymbals ring or create the splash sound. If they are held together just long enough to kill the ring then the chick sound. What the best way to simulate that it is I don't know.
 
no... they're suggestions adjusting the current code while I work on it's replacement. The important thing is to know you can control the zone selection for now.

Can you get it to produce reliable 0-4 zone readings in the serial monitor (or better yet the serial plotter!) by adjusting the boundaries in the zoneMap[] array?

What is the 'no load' reading on your voltage divider and what are you using for 'closed' (the point where you'd expect the chick/splash to fire)?


Now that it's clear you fire the splash with the close/open combination I may yet get you to try your idea of letting the peak-detect have a go at identifying the note events for the HH pedal but I'd like to finish this approach first.*

I think I can still get this code to work with a minor adjustment so that it reads the intensity of the close (pressure change that currently is mapped as the velocity on the chick) and then picks the note to play on the next pass. Chick if it's still closed and splash if it's open.

Basically I'll use a global velocity value and set it to the signal change when the close is detected but withhold a note event for the next pass.

When the code reads the FSR on the next pass following detecting the close the current zone can be used to pick chick or splash.

The signal would use the velocity read on the previous pass as both a flag (to say a note event it due) and as the velocity of the note to-be-played.

(There would be a second velocity from how quickly the 'close' was re-opened that could also be used to pick the playback note or otherwise modify the note value produced; it could even set a note-off time that might alter the way your sample player plays the splash sound - so that both its attack and decay are affected by the way the note is generated.)

If you can answer the above you can leave the old code for now and wait for the next version (perhaps tonight?).


*This may be as simple as pointing one of the analogPin[] array at the FSR signal and modifying the note selected in the same way as the A0 signal. But it is also possible that the same timing parameters will not be able to work for both piezos and FSR as the signal they produce from a percussive force are likely very different in both duration and signal characteristics. But given it's possible it could work I may yet have a go at it AFTER I finish with the current approach.
 
OK, if you mean the sketch in #37 the monitor just shows 0s / 4s no 2s or 3s and I usually get double notes, occasionally I get a single note pl1.PNG
with regards to the splash I'm for doing whatever is easiest, the splash is the least often used and doesn't need to be recreated exactly like a mechanical hi hat. That is why I was happy with the method used in the sketch in#16, so if the splash note would fire at 3 and the chick at 4 maybe? The fact that we're just triggering 2 samples and not an impact note followed by one of 2 different sustain notes makes a less realistic approach more logical to me.
 
Last edited:
?

your pedal does not allow the player to open and close a parameter analogous to the distance between the cymbals.

It provides an impulse signal when struck and (hopefully!) retains higher voltage readings while stressed so that the player can alternate between open and closed hits.

If so then a more binary version of the controller may still be possible to allow basic splash/chick and open/closed selection.


Is this where we are?
 
the player will just play samples. all the samples vary greatly in length, the sample will play until it ends unless it's in a choke group in which case the next note will terminate it. When I used a different sketch I could vary the readings from the pedal from close to 0 to over 900, and I could hold it any ware in between like a pot.
 
Last edited:
ok... then it's just configuration. what are the raw readings with no force and where you think the close point should be?

Just researching commercial pedals. Some seem to use a rubber actuator that applies increasing pressure as it's compressed by the pedal. Is yours doing something similar?
 
I have 2 layers of mouse pad material on mine, I had several different test sketches and they all read differently so maybe you should send me whatever your using.
 
I can't find the other test I used, this one dances around at 0-30 when pedal is up then goes to about 240 pedal down. I can hold it any ware in between accurate to about 50


Code:
#define DRUM1 14    // Our analog pin

byte val = 0;      // Initializing the variable for the voltage value

void setup()
{
  Serial.begin(9600);  // Initializing the serial port at 9600 baud
}

void loop()
{
  val = analogRead(DRUM1);  // Read the voltage
  Serial.println(val, DEC); // Print the voltage to the terminal
}
 
240 is on the upper end? I thought you said you where getting up to 900 or is that only when standing on it?
 
const int zoneMap[ZONECOUNT] = {40,120,180,240,1024};

Maybe this map would work for you then


try these for the HH settings (but the noise might be too high with such limited range to drop the deadband further and may need to be higher)
Code:
//****HIGH HAT PEDAL
const int zoneRefesh = 8;            // mS between HH pedal updates -  hat pedal is low freq. so we can check every few mS.
const int minSplash = 40;            // raw value difference required to fire 'splash'
const int minChick = 20;             // raw value difference required to fire 'chick'
const int deadBandLimit = 10;        // raw value deadband to limit HH updates above noise threshold - can very large with small number of zones
 
With the settings in #64 I get no sound, with the original settings I get double notes. the pedal value is between 20 and 900, the other reading was voltage and should have been 2.40 max.
 
Post 37 is my last code posting... any changes were meant to be to that code but at this point it doesn't matter any more as I take it setting the bottom range to 30 will allow zone 0 to be selected when the pedal is 'open' and setting the close point to about 800 should allow the user to hit zone 4 to trigger notes.

const int zoneMap[ZONECOUNT] = {30,500,600,800,1024};

I believe the code I have now will work but it needs some testing and debugging before I post here... a couple days perhaps.
 
Starting over with your suggestion of using the peakdetect for HH too but it will require a lot of changes to handle the HH notes.

Using changes over time doesn't work very well for getting usable velocity values.


There was something intuitive about looking for rate of change in the HH rather than magnitude as the speed of the motion is more important than the force. There might be some advantage in such a system but I think it would have to rely on interrupts rather than polling value (which will be much more complicated).
 
Could you show me where it's determined which notes are played in this sketch? I know this is not a very scientific approach but the pedal really works in this sketch. The only thing that really matters is that I can trigger either of the 2 notes at will, which I can in this sketch.



Code:
[const int channel = 10;  // General MIDI: channel 10 = percussion sounds
const int PINS = 3;     // number of signals incoming

const int note[][PINS] = {
{36,46,56},
{37,47,57},
{38,48,58}
};
const int zonePin = A0;
const int analogPin[PINS] = {A0,A1,A2,}; //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
const int deadBandLimit = 100; // huge deadband but can very large for three state output

const int maxRead = 950; //enter the FSR pin reading at full pressure
const int minRead = 30; //enter the FSR pin reading at zero pressure 

int zone;        // calculated zone
int rawZone;     // raw pin reading
int rawZoneLag;  // lagged raw reading

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]; // timers to end states 1 and 2

void setup() {
  Serial.begin(115200);
  while (!Serial && millis() < 2500) /* wait for serial monitor */ ;
  Serial.println("Piezo Peak Capture");
    for (int i=0;i<PINS;i++){
    //delay(200);
    piezo[i] = analogRead(analogPin[i]);
    rawZone = analogRead(zonePin);
    if (abs(rawZone-rawZoneLag)>deadBandLimit){
      zone = map(rawZone, minRead, maxRead, 0, 2);
      //Serial.println(zone);
      rawZoneLag = rawZone;
    }
    peakDetect(i);
    // 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 loop() {
 rawZone = analogRead(zonePin);
 if (abs(rawZone-rawZoneLag)>deadBandLimit){
    zone = map(rawZone, minRead, maxRead, 0, 2);
    Serial.println(zone);
    rawZoneLag = rawZone;
  }
  for (int i=0;i<PINS;i++){
    piezo[i] = analogRead(analogPin[i]);
    peakDetect(i);
  }
  while (usbMIDI.read()) {
    // ignore incoming messages
  }
}

  void peakDetect(int i) {

        //Serial.println(state[i]);

  switch (state[i]) {
    // 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 (piezo[i] > thresholdMin) {
        //Serial.print("begin peak track ");
        //Serial.println(piezo[i]);
        peak[i] = piezo[i];
        msec[i] = 0;
        state[i] = 1;
      }
      return;

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

    // Ignore Aftershock state: wait for things to be quiet again.
    default:
      if (piezo[i] > thresholdMin) {
        msec[i] = 0; // keep resetting timer if above threshold
      } else if (msec[i] > aftershockMillis) {
        usbMIDI.sendNoteOn(note[zone][i], 0, channel);
        state[i] = 0; // go back to idle when
      }
  }
}/CODE]
 
Could you show me where it's determined which notes are played in this sketch? I know this is not a very scientific approach but the pedal really works in this sketch. The only thing that really matters is that I can trigger either of the 2 notes at will, which I can in this sketch.



Code:
[const int channel = 10;  // General MIDI: channel 10 = percussion sounds
const int PINS = 3;     // number of signals incoming

const int note[][PINS] = {
[COLOR="#FF0000"]{36,46,56},
{37,47,57},
{38,48,58}[/COLOR]
};


...
}

Not sure what you're asking but the MIDI notes are assigned by the note[][] array the values highlighted in red in the quoted code.

I have proper code based on this approach nearly ready. I figured out how to give it different timing parameters for the FSR so the user has a chance of getting the HH to splash. But I have no way of testing it so I need to come up with some way of making sure the logic is working as expected before passing it over to see if it will work with your hardware.
 
OK, I was thinking if I could adjust this to where it worked well with 2 notes you could copy some of the parameters. I'll just wait.
 
Untested code

I didn't get a chance to test this and it may be a few days before I can get back to it so I thought I'd see it you can test for me.

I've added a print routine to show the details on what MIDI it is generating and how. Hopefully it will work and you can provide details on what works and what doesn't.

As this code has not been run on a Teensy (but does compile) it's possible it will not work at all.

Following your suggestion it is based very closely on the original piezo code and my multi-piezo adaptation with a trick to give very different timing and scaling parameters to the FSR/voltage divider signal from those used for the piezo with two-value arrays that take the first value for piezo settings and the second value for the HH signal on the first pin (when the index value = 0 in loops through the pin-array). But because it's now tightly knit into the piezo code it's much harder to test with limited hardware so hopefully you'll be able to let me know if I'm close.

Code:
/* Use a Piezo sensor (percussion / drum) to send USB MIDI note on
messages, where the "velocity" represents how hard the Piezo was
tapped.

This example code is in the public domain.
*multi-pad extension by oddson (under-tested)*
*HH pedal support added for this thread: https://forum.pjrc.com/threads/50114-FSR-pedal
*/

//***************************************** SET PIN ASSIGNMENT CONFIGURATION OPTIONS**************
const int PINS = 10; // number of signals incoming
const int analogPin[PINS] = {A0,A1,A2,A3,A4,A5,A6,A7,A8,A9}; //array of analog PINs 

//***************************************** SET HIGH HAT ZONE CONFIGURATION OPTIONS**************
const int ZONECOUNT = 5 ; // number of zones for FSR as modifier for HH
const int zoneMap[ZONECOUNT] = {40,150,450,800,1024};
const int zoneClose = 800;

//***************************************** SET THRESHOLD AND TIMING CONFIGURATION OPTIONS**************
//** two value arrays are for normal piezo in index 0 and HH pedal in index 1 (use !i in as index during PINS loops)
const int thresholdMin[2] = {60,800}; // minimum reading, avoid noise and false starts
const int velocityScaleMax[2] = {1023,950}; // used to scale velocity as effective maximum
const int peakTrackMillis[2] = {12,25};
const int aftershockMillis[2] = {25,45}; // aftershocks & vibration reject


//***************************************** SET MIDI NOTE VALUE CONFIGURATION OPTIONS**************
const int channel = 10; // General MIDI: channel 10 = percussion sounds
const int note[PINS] = {0,0,36,38,40,41,47,50,49,51}; // array of MIDI note values for read signals (0 placeholders for HH)
const int HHstickNote[ZONECOUNT] = {46,2,3,4,42} ; // note values for flavours of HH from open to closed (no general MIDI for intermediaries so used low values as placeholders)
const int HHsplashNote[ZONECOUNT] = {55,55,55,55,44}; // note values for splash/chick 





int zone; // calculated zone
int state[PINS]; // 0=idle, 1=looking for peak, 2=ignore aftershocks
int peak[PINS]; // remember the highest reading
int piezo[PINS];
elapsedMillis displayLimiter;
elapsedMillis msec[PINS]; // timers to end states 1 and 2
int zoneNote[ZONECOUNT][PINS]; // matrix of note values with zone alterations for HH and it's piezo calc'd during setup

void setup() {
  Serial.begin(115200);
  while (!Serial && millis() < 2500) /* wait for serial monitor */ ;
  Serial.println("Piezo Peak Capture w/ HH pedal");
  //*****build matrix to hold zone options (different only for HH and it's piezo)
  for (int i=0;i<PINS;i++){
    for (int j=0;j<ZONECOUNT;j++){ 
      if (i==0){
        zoneNote[j][i] = HHsplashNote[j];  // HH pedal notes 
      }else if(i==1){ 
        zoneNote[j][i] = HHstickNote[j];   // HH piezo notes
      }else{
        zoneNote[j][i] = note[i] ;         // other piezo - same for all j
      }
    } 
  }
}

void loop() {
  for (int i=0;i<PINS;i++){
    piezo[i] = analogRead(analogPin[i]);
    int j = 0;
    while ( piezo[0]>zoneMap[j] ) {
      j++;
    }
    zone = j;
    peakDetect(i);
  }
  // MIDI Controllers should discard incoming MIDI messages.
  // http://forum.pjrc.com/threads/24179-...ses-midi-crash
  while (usbMIDI.read()) {
    // ignore incoming messages
  }
}


void peakDetect(int i) {
  switch (state[i]) {
    // IDLE state: wait for any reading is above threshold. Do not set threshold too low. 
  case 0:
    if (piezo[i] > thresholdMin[!i]) {
      peak[i] = piezo[i];
      msec[i] = 0;
      state[i] = 1;
    }
    return;
    
    // Peak Tracking state: capture largest reading
  case 1:
    if (piezo[i] > peak[i]) {
      peak[i] = piezo[i]; 
    }
    if (msec[i] >= peakTrackMillis[!i]) {
      int velocity = map(peak[i], thresholdMin[!i], velocityScaleMax[!i], 1, 127);
      usbMIDI.sendNoteOn(zoneNote[zone][i], velocity, channel);
      printMIDI(true, zoneNote[zone][i], velocity, zone, peak[i]);
      msec[i] = 0;
      state[i] = 2;
    }
    return;
    
    // Ignore Aftershock state: wait for things to be quiet again.
  default:
    if (piezo[i] > thresholdMin[i] && i>0) { // except HH??
      msec[i] = 0; // keep resetting timer if above threshold
    } else if (msec[i] > aftershockMillis[!i]) {
      usbMIDI.sendNoteOff(zoneNote[zone][i], 0, channel);
      printMIDI(false, zoneNote[zone][i], 0, zone, peak[i]);
      state[i] = 0; // go back to idle when
    }
  }
}



void printMIDI(boolean on, int noteID, int vel, int zone, int peak){
  Serial.print("NoteOn: ");
  Serial.print(on);
  Serial.print(" NoteID: ");
  Serial.print(noteID);
  Serial.print(" Velocity: ");
  Serial.print(vel);
  Serial.print(" Zone: ");
  Serial.print(zone);
  Serial.print(" Peak: ");
  Serial.println(peak);
}
 
Last edited:
Well we have a few issues, it plays 2 notes as soon as I start the program (like noise). I had a similar problem when I tinkered with the notes in #70. I have some other things I have to do today but I'll work on it when I can later tonight and tomorrow. I'll make sure it's not my hardware before changing anything.
 
Oh... is the FSR's 'wiper' grounded by a pull-up resister? They're famous for floating when there is no pressure on them. I'd originally assumed there always is pressure in your design but I'm not certain.

If it floats you will get errant messages all the time on A0.
- edit - nonsense... it's an FSR not a soft-pot.

If it is somewhat working check if you are able to alter the MIDI note numbers for A1 based on A0 pressure. The HH pedal notes will likely require some timing tests and configuration work; assuming the code is more-or-less correct.
 
I had a 10 k resistor from A0 to gnd. weather it's there or not doesn't seam to make a difference with this sketch. It does with the sketch in #16 in that sketch if I take it out the pedal does nothing. Back to this sketch, when I upload it it immediately played 2 notes alternating rapidly, I had to change the threshold min from 60 to 350 to get that to stop but when I hold the pedal down it does it also. I can't tell weather the notes are changing on the pad. I don't have time now but I'll do more testing later.
 
Status
Not open for further replies.
Back
Top