Re: Standalone continuous sustain pedal?

I'd like to share my final code for the Arduino nano:

#include <MIDI.h>

// Read an analog value and convert it to MIDI out CC64 (sustain)

MIDI_CREATE_DEFAULT_INSTANCE();

// Variables will change:
int controlValue = 0;
int lastcontrolValue = 0;

void setup()
{
    MIDI.begin();        // Launch MIDI
    Serial.begin(115200); // will change baud rate of MIDI traffic from 31250 to 115200
}

void loop()
{
  // read the input on analog pin 0
  int sensorValue = analogRead(A0);

  // convert the analog reading (which goes from 0 - 1023) to a 7-bit value (0 - 127)
  int controlValue = sensorValue / 8;

  // compare controlValue to its previous value.
  if (controlValue != lastcontrolValue ) {
    // send a ControlChange (ControlNumber 64, controlValue, on channel 1)
     MIDI.sendControlChange(64,controlValue,1);
  }

  // remember previous value
  lastcontrolValue = controlValue;

  // delay for stability and rate limiting.
  delay(40);
}

Re: Standalone continuous sustain pedal?

I have been RESISTING building something like this for a while now.
Not that my playing NEEDS 3 or 4 pedals (or even ONE) but I have a sense that Arduino is SUCKING me towards a project

Manoreken2's project makes the 3 pedal solution look very attractive, the RPU-3 being quite a reasonable floor unit, so I am noodling around with code while waiting for my next trip that will take me near to the local micro-center on July 2nd.  I may pass a guitar center on that day and pick up an RPU-3.

It SEEMS that "fluttering" could be a problem at any value, so I am SUGGESTING a bit of code that filters out tiny changes, e.g. smaller than 3 or 4 (arbitrary, but reasonable).  There seems no need to transmit something like 34, 35, 34, 36, 34, 35,,, etc. just transmit the 34 and nothing until something greater than 38 or less than 30.

Similarly, great leaps in value in the small fraction of a second that it takes to loop back and take another sample are very unlikely, (the human foot can move only so fast, but wipers on potentiometers can lose/regain contact) so changes greater than 20 since the last transmission could/should be ignored.

I don't see a need for a delay in the loop, I would keep the transmission rate down just by filtering for "worthwhile and real changes" i.e. not noise.

These are just my thoughts/suggestions, I may change my mind completely once I get something built and see what the midi stream actually looks like.

Re: Standalone continuous sustain pedal?

aandrmusic wrote:

It SEEMS that "fluttering" could be a problem at any value, so I am SUGGESTING a bit of code that filters out tiny changes, e.g. smaller than 3 or 4 (arbitrary, but reasonable).  There seems no need to transmit something like 34, 35, 34, 36, 34, 35,,, etc. just transmit the 34 and nothing until something greater than 38 or less than 30.

Hi,

with my "final code" I have no fluttering with a fully pressed pedal any longer (I forgot to mention that in my last post, sorry). The mapping-coefficient 1/8 results in a steeper curve than my previous 127/1023 and thus the maximum  cc-value 127 is reached  earlier and the "full-pedalling"-zone is stable with this trick (inspired by Manoreken2, thnx!). No frills with Pianoteq needed any longer (just using its MIDI-defaults).

aandrmusic wrote:

Similarly, great leaps in value in the small fraction of a second that it takes to loop back and take another sample are very unlikely, (the human foot can move only so fast, but wipers on potentiometers can lose/regain contact) so changes greater than 20 since the last transmission could/should be ignored.

I don't see a need for a delay in the loop, I would keep the transmission rate down just by filtering for "worthwhile and real changes" i.e. not noise.

These are just my thoughts/suggestions, I may change my mind completely once I get something built and see what the midi stream actually looks like.

My 40 ms delay in the loop seems to be a good trade-off between realtime response of the pedal and avoiding unnecessary MIDI-events when the pedal is pressed or released fast (when 127 or 0 is intended). But I'm open to other solutions in the wild, once they are tested ...

Re: Standalone continuous sustain pedal?

I get that, but since ALL A to D can only ever be precise to +/- 1/2 LSB the value is likely to "flutter" due to rounding (as well as noise).
This is why your  "if (controlValue != lastcontrolValue )" will return true a lot more often than necessary and lead to excess midi messages. Which was probably what led you to the "need" for a delay before the next sample.

My suggestion is that you test for a difference greater than the lest significant bit, I arbitrarily chose a value of 3, a better value could be determined from measured noise.

something along the line of
diff = abs(controlValue - lastcontrolValue)
If (diff > 3)
{
send it, update lastcontrolValue, etc.
}

Well, just a suggestion.

Re: Standalone continuous sustain pedal?

aandrmusic wrote:

I get that, but since ALL A to D can only ever be precise to +/- 1/2 LSB the value is likely to "flutter" due to rounding (as well as noise).

Yes, but it is very unlikely to press a position, where noise rounds up and down with the loop-rate. The "noise-floor" of a nervous leg and a good sustain-technique is much bigger. And because these are a signature of the player - why not to transport?   

This is why your  "if (controlValue != lastcontrolValue )" will return true a lot more often than necessary and lead to excess midi messages. Which was probably what led you to the "need" for a delay before the next sample.

My suggestion is that you test for a difference greater than the lest significant bit, I arbitrarily chose a value of 3, a better value could be determined from measured noise.

Yes, I thought about that before, this lowers the resolution from native 128 values to 64 (with 2) or 43 (with 3). But in the past it always bugged me, that even expensive keyboards sometimes have limited velocity-ranges, that can be much smaller than 128 (I've seen ranges from 20 -100). I never will tolerate this!!! ;-)

something along the line of
diff = abs(controlValue - lastcontrolValue)
If (diff > 3)
{
send it, update lastcontrolValue, etc.
}

Well, just a suggestion.

Hm, with "distance" 3, the half-pedalling standard-value 64 cannot be triggered (just 63 and 66). And neither full-pedal 127 (just 126).

Your suggested distance 4 means a resolution of 32 values and contains the half-pedal value 64. Not bad, but it ends with the maximum 124. Could be "sufficient" and worth a try. - On the other hand, I have no problems with my full-resolution of 128 in a 40 ms loop. The more analog an instrument behaves, the better! (IMHO)

Last edited by groovy (15-06-2015 20:40)

Re: Standalone continuous sustain pedal?

No, it doesn't affect the range of values, it merely filters out tiny changes (due to rounding or electronic noise).
It wouldn't prevent a change from 123 to 127, or from 126 to 122, or from 0 to 4, from 5 to 1, etc.
It allows changes from any value that has already been sent to any other value, as long as it is a DIFFERENCE of greater than 3 (or 4 or whatever you choose).
It does NOT affect the granularity of the pedal, i.e. steps of 5, 6, 7, 13 etc still get through.
It is just a noise and rounding error filter.

As I said, just a suggestion - idea sharing.

Last edited by aandrmusic (15-06-2015 21:47)

Re: Standalone continuous sustain pedal?

Ah, got it. Of course you are right, the gaps are not always at the same position (facepalm).

But I see another problem with your example "difference of greater than 4". Given the lastcontrolValue has been 4. How to reach sustain 0 from there? Let off the pedal gives a new controlValue of 0, but the (lastcontrolValue - controlValue) = 4 - 0 = 4 and thus not *greater* than 4. A change would be not detected and sustain remains at 4.

The same with lastcontrolValue=123. How to reach 127 from there, if a change is only detected with a diff > 4 ?

I appreciate your suggestions, thank you!

Re: Standalone continuous sustain pedal?

I admit to having submitted it as "something along the line of..." when I first saw the != and (based on other experience) the 1/2 LSB rounding issue jumped out at me.
Thinking out loud, etc.
I don't know what the value should be, but 1 is clearly wrong, perhaps => 2 would do it.

With a bit more thought a few more bits of code could handle whatever end/edge conditions are considered important.
64 might be in the middle of a special switching zone, hmmm... 
I don't know,,, does going from 3 to 0 REALLY matter ?  or 125 to 127 ?
Are 3 and 4 FUNCTIONALLY equivalent to zero anyway ?
Is there any real difference between what pianoteq does with 124, 125, 126 and 127 ? 
Personally I doubt that I can reliably produce the same dozen levels on a pedal anyway.

I'll give it some more thought as I shop for parts.

Re: Standalone continuous sustain pedal?

I may have been over thinking this.
analogueRead() returns 0-1023, i.e. 10 bits

Why not just DROP the least significant 3 bits to normalize it to 0-127 ?
e.g. sensorValue(i) = analogueRead(i) >> 3;

Noise in the least significant bits could still get through if those three bits are close to 111 or 000, so
using the noise filter I suggested earlier with >= 2 would still be worthwhile.

Re: Standalone continuous sustain pedal?

aandrmusic wrote:

I may have been over thinking this.
analogueRead() returns 0-1023, i.e. 10 bits

Why not just DROP the least significant 3 bits to normalize it to 0-127 ?
e.g. sensorValue(i) = analogueRead(i) >> 3;

Noise in the least significant bits could still get through if those three bits are close to 111 or 000, so
using the noise filter I suggested earlier with >= 2 would still be worthwhile.

Hi,

yes, there are several ways to map 0-1023 to 0-127 (even a special function map() exists for this purpose) and >=2 could be a good compromise. --  I found another board, that has USB-MIDI-support built-in already! The "Teensy 2.0" is directly supported by the OS as an USB-MIDI device ("class compliant"). No helper-application like 'ttymidi' or 'hairless' is needed on the PC. In Pianoteq it appears as 'Teensy MIDI' in the Devices list. This is my new build:

My Teensy sustain-controller

Teensy is not a member of the Arduino family, but it can be programmed with the Arduino-Software, when Teensyduino is installed. It comes with an nice example called  AnalogControlChange, that I could easily adapt. It originally had a 20 ms loop BTW.

The code is a little different, but it does basically the same as my Arduino nano snippet:

/* USB MIDI AnalogControlChange Example

   You must select MIDI from the "Tools > USB Type" menu
   http://www.pjrc.com/teensy/td_midi.html

   This example code is in the public domain.
*/

#include <Bounce.h>

// the MIDI channel number to send messages
const int channel = 1;

// the MIDI continuous controller for each analog input
const int controllerA0 = 64; // 64 = sustain

void setup() {
}

// store previously sent values, to detect changes
int previousA0 = -1;

elapsedMillis msec = 0;

void loop() {
  // only check the analog inputs 50 times per second,
  // to prevent a flood of MIDI messages (PS: with msec >= 20)
  if (msec >= 40) {
    msec = 0;
    int n0 = analogRead(A0) / 8;
    // only transmit MIDI messages if analog input changed
    if (n0 != previousA0) {
      usbMIDI.sendControlChange(controllerA0, n0, channel);
      previousA0 = n0;
    }
  }

  // MIDI Controllers should discard incoming MIDI messages.
  // http://forum.pjrc.com/threads/24179-Tee...midi-crash
  while (usbMIDI.read()) {
    // ignore incoming messages
  }
}

With around 20 EUR the Teensy 2.0 is more expensive than the Arduino nano and not as easy to flash for a beginner.

cheers

Last edited by groovy (16-06-2015 22:50)

Re: Standalone continuous sustain pedal?

Almost worth getting one so I can tell people what it is when they ask

Teensyduino

Seriously, I like it and might build with it.
The picture shows that it isn't much bigger than the end of the USB cable.

Re: Standalone continuous sustain pedal?

aandrmusic wrote:

Almost worth getting one so I can tell people what it is when they ask

Teensyduino

Seriously, I like it and might build with it.
The picture shows that it isn't much bigger than the end of the USB cable.

:-) As you suggested, I modified the code a bit now, so that not each controller change is detected, but only if it is greater than 1 MIDI-value (i.e. 2, 3, 4, ...). This "hysteresis" is enough to make each pedal-position of my Roland DP-10 stable. As we discussed, it can happen now, that with a let-off pedal the cc64 remains at value 1 (and not 0).

But this is no problem, because Pianoteq drops all values <=10 to value 0, and all values >=117 to value 127 when calibrating the pedalcurve with the calibrator. 

FYI:

/* USB MIDI AnalogControlChange Example

   You must select MIDI from the "Tools > USB Type" menu
   http://www.pjrc.com/teensy/td_midi.html

   This example code is in the public domain.
*/

#include <Bounce.h>

// the MIDI channel number to send messages
const int channel = 1;

// the MIDI continuous controller for each analog input
const int controllerA0 = 64; // 64 = sustain

void setup() {
}

// store previously sent values, to detect changes
int previousA0 = -1;

elapsedMillis msec = 0;

void loop() {
  // only check the analog inputs all 40 ms
  // to prevent a flood of MIDI messages
  if (msec >= 40) {
    msec = 0;
    int n0 = analogRead(A0) / 8;
    // only transmit MIDI messages if analog input changed
    // if (n0 != previousA0) {
    // only transmit MIDI if change is 2 or more
    int diff = abs (n0 - previousA0);
    if (diff > 1) {
      usbMIDI.sendControlChange(controllerA0, n0, channel);
      previousA0 = n0;
    }
  }

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

Re: Standalone continuous sustain pedal?

I have a "philosophical problem" with an approach of not sampling noisy analogue signals too often on the grounds that it generates too many changes, i.e. too much message traffic, especially when the amount of noise isn't known.
It acknowledges that there may be noise and that the "Solution" is to not listen as often.
It is a gamble, you may get a noise spike, a gap valley, or genuine signal - - but using that anyway, then coming back some fixed time interval later and selecting another unknown is just WRONG {IMO, etc.}.
Sure, it reduces traffic, but the traffic it sends is suspect at best, i.e. it is not "qualified" as good signal.

I would prefer an approach that samples as often as possible and actively FILTERS OUT noise.

Propose;
Sample as often as possible.
Store values in a cyclic list (length TBD experimentally)
Discard values that differ from previous table average by more than some percentage.
If table average has changed more than some TBD value Send table average, update previous table average, etc.

This would provide some smoothing as well as spike and valley noise removal.

I can't DO this until I have parts assembled, but I will run with NO filtering initially to determine the amount of noise on my pedals and the maximum speed at which my foot can perform full strokes - down is almost certainly faster than release.
Then I can determine table size and thresholds for discarding spikes and valleys.

Last edited by aandrmusic (26-06-2015 12:27)

Re: Standalone continuous sustain pedal?

Calculating "running averages" probably would be the best solution, ack. 

aandrmusic wrote:

[...]when the amount of noise isn't known.

... but that is an important point IMHO. Maybe the noise of the analog input is negligible. For example all MIDI-events are stable, when I use fixed positions :-)
-->
http://fs1.directupload.net/images/150626/2dbtdyxa.jpg

Remains "dynamic noise" - is there any generated, when the pedal is moving dynamically up and down? I don't know, all I can say is, that I don't find any spikes in the opposite direction of the pedal movement  (the deeper the pedal is pressed, the greater are the CC64-values in the event-list and vice versa).

aandrmusic wrote:

i.e. it is not "qualified" as good signal.

But that doesn't mean, that it is not "good enough". Ok, "qualified" would be better approach, yes. It would be interesting to see, how averaging handles fast 0 <-> 127 movements or erratic changes in between.

I'm an absolut beginner with Teensy or Arduino, probably "smoothing" of the analog input would be the next step:

https://www.arduino.cc/en/Tutorial/Smoothing

(... where overengineering begins?? ;-)

Re: Standalone continuous sustain pedal?

Well, I have only a 3D printer experience with Arduino Mega, so I am having to draw on other experience with analogue signals, the very NATURE of potentiometers, also some A to D electronics background from a previous life.
Plus I have barely touched the Arduino code for that printer, so my "experience" is very limited.

A point about your last reply; Sure there is little/no noise when the pedal is stationary, as depicted with a clamp on it - but your code senses a change and assumes the value to be valid IMMEDIATELY.  The point being that these initial change values are probably the MOST invalid.

Potentiometer wipers tend to BOUNCE and their settling time is typically in milliseconds.
Arduino's time to ReadAnalogue() is about 100 microseconds according to the reference page.
That seems awfully SLOW to me, but the exact time doesn't matter, it is an order of magnitude faster than the settling time of a potentiometer, so you could get around a read loop several times while a pot finds its steady value.
This is my main point, the arduino can be WATCHING the settling of the pot instead of WAITING some arbitrary amount of time to gather a next value that is no more credible, it is merely "different" and some number of milliseconds later.

I will stream in raw values once I get hardware set up.
It shouldn't take long to establish settling times and noise floors/ceilings, then I can decide if any of this matters more than my lack of pianistic skills - - Oh, I know that already.  I need to PLAY more and PRACTICE more diligently.
   
BTW, a 10 bit A to D should take little more than 10 clock cycles, that is why I think 100 microseconds is SLOW.

Re: Standalone continuous sustain pedal?

Do we have any indication, that the deviation by bouncing/settling is not masked by the hysteresis of +/-2 MIDI-values?

And if that noise couldn't be masked all the time - would it have practical consequences?


aandrmusic wrote:

I will stream in raw values once I get hardware set up.

Yes, statistics and simulation could give an answer.

Re: Standalone continuous sustain pedal?

Both codes are married now. My simple code of 17-06-2015 23:11 and the Smoothing-example from the Tutorial.

In words it does the following: It checks every 40 ms, if an average of the last ten CC64-values has changed for more than >= 2. In that case a MIDI-Event is sent to Pianoteq via USB (else not).

The analog input is read continuously every ~1 ms (because the loop has the delay(1)). So the ten readings in the cyclic list represent the last ~10 ms before the change-check.

/* USB MIDI AnalogControlChange Example

   You must select MIDI from the "Tools > USB Type" menu
   http://www.pjrc.com/teensy/td_midi.html

   This example code is in the public domain.
*/

#include <Bounce.h>

// the MIDI channel number to send messages
const int channel = 1;

// the MIDI continuous controller for each analog input
const int controllerA0 = 64; // 64 = sustain

const int numReadings = 10;

int readings[numReadings];      // the readings from the analog input
int index = 0;                  // the index of the current reading
int total = 0;                  // the running total
int average = 0;                // the average

int inputPin = A0;

void setup() {
        // initialize all the readings to 0:
        for (int thisReading = 0; thisReading < numReadings; thisReading++)
          readings[thisReading] = 0;
}

// store previously sent values, to detect changes
int previousA0 = -1;

elapsedMillis msec = 0;

void loop() {
  // subtract the last reading:
  total= total - readings[index];        
  // read from the sensor:  
  readings[index] = analogRead(inputPin);
  // add the reading to the total:
  total= total + readings[index];      
  // advance to the next position in the array:  
  index = index + 1;                    

  // if we're at the end of the array...
  if (index >= numReadings)              
    // ...wrap around to the beginning:
    index = 0;                          

  // calculate the average:
  average = total / numReadings;        
  
  // only check for changes all 40 ms
  // to prevent a flood of MIDI messages
  if (msec >= 40) {
    msec = 0;
    int n0 = average / 8;
    // only transmit MIDI messages if analog input changed
    // if (n0 != previousA0) {
    // only transmit MIDI if change is 2 or more
    int diff = abs (n0 - previousA0);
    if (diff > 1) {
      usbMIDI.sendControlChange(controllerA0, n0, channel);
      previousA0 = n0;
    }
      delay(1);        // delay in between reads for stability
  }

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

At first glance it seems not to behave differently to the simplistic approach, but it should smoothen potential noise.

Re: Standalone continuous sustain pedal?

groovy wrote:

Do we have any indication, that the deviation by bouncing/settling is not masked by the hysteresis of +/-2 MIDI-values?

And if that noise couldn't be masked all the time - would it have practical consequences?


aandrmusic wrote:

I will stream in raw values once I get hardware set up.

Yes, statistics and simulation could give an answer.

Yes, it is likely to be LARGE and PERSISTENT, i.e. bounce around for milliseconds.
The very existence of the code that you pointed to here;  https://www.arduino.cc/en/Tutorial/Smoothing
supports the need for what I described earlier as maintaining a cyclic list of values and discarding values
that deviate a lot from the running average - different terms, but essentially the same basis and principles.
Spurious signals need to be filtered out, not just accepted at larger time intervals.
 
Again, micro processors are fast, pots bounce slowly, feet move even slower.

There is no hysteresis, use of that term will likely lead to other readers becoming confused.

Re: Standalone continuous sustain pedal?

aandrmusic wrote:

Yes, it is likely to be LARGE and PERSISTENT, i.e. bounce around for milliseconds.

... one more reason for contactless Hall-Sensors ...

Thank you! ;-)

http://fs1.directupload.net/images/150628/hy4bwkij.jpg

Re: Standalone continuous sustain pedal?

Hey there, just registered,
have pianoteq for a while, saw a DIY thread and well, then I just had to register :-D

I have 1) a suggestion and 2) a question

1) value fluttering

I don't know how fast this fluttering is and how much of an *audible* change from e.g. 126 to 127 does.
Does the value e.g. 126 or 127 sometimes stay for a long time and then it flips to the other number, stays for a long time and flips again? Depending on the audible change it wouldn't be too bad perhaps.
But if it indeed changes "reliably" between a few very close values, with no long pauses, maybe some sort of low-pass filtering would help?
Which means no values in the range, and no deltas are excluded from the output categorically, just that time is factored in there.

I'm not quite a DSP guru, only have very fractional knowledge there.
But IIRC, the simplest form of low pass, which might work here, would be just to average a box of values.
Say, you have a circular buffer of size 3 (perhaps sufficient, perhaps needs to be bigger). In this case it would make sense I guess to put one new value in the buffer and discard the oldest *one* (you could also move about other lengths over the input stream, but intuitively I'd say that makes no sense here).
You average all values in the buffer, and pass on the result of that, which should be more stable than the fluttering input. If 3 is not stable enough, try 4..5.. etc).

Example:
The input stream of Sustain events: {126,125,127,125,126,127,126}
Output stream of averages (with fractional part | rounded by 0.5)
(After program start, when the first CC event arrives, I guess you need to fill the buffer initially completely with the first ever received value before generating the first output event. After that, each new incoming event just does the regular step: "put newest into buffer, discard oldest from buffer, average all, generate output event")
{126,126,126} -> 126    | 126
{126,126,125} -> 125.6 | 126
{126,125,127} -> 126    | 126
{125,127,125} -> 125.6 | 126
{127,125,126} -> 126    | 126
{125,126,127} -> 126    | 126
{126,127,126} -> 126.3 | 126

As you see, the rounded output remains stable. This depends of course on how widely and with what kind of distribution the input fluctuates and how many values the averaging buffer has.
But differences of (on average) 1 are still possible, which it would not with a solution like "only pass deltas >= 4" or so.
E.g., if it fluctuates, say, around 125 +/-1, and you kick the pedal fully, and it fluctuates around 126 +/-1.


2) Pianoteq and extra MIDI Sustain pedal as own MIDI device

Seeing this thread here, tells me, it must be possible in Pianoteq to have more than one MIDI input device, each for a different purpose?
E.g. use my MIDI keyboard which does not have a continuous sustain pedal input for notes, and a Pedal as own MIDI device as pedal input?
Can someone tell me how to set this up in Pianoteq 5? (I think I have the cheapest version...)

Re: Standalone continuous sustain pedal?

sleepydog wrote:

Hey there, just registered,
have pianoteq for a while, saw a DIY thread and well, then I just had to register :-D

I have 1) a suggestion and 2) a question

1) value fluttering

I don't know how fast this fluttering is and how much of an *audible* change from e.g. 126 to 127 does.
Does the value e.g. 126 or 127 sometimes stay for a long time and then it flips to the other number, stays for a long time and flips again? Depending on the audible change it wouldn't be too bad perhaps.
But if it indeed changes "reliably" between a few very close values, with no long pauses, maybe some sort of low-pass filtering would help?
Which means no values in the range, and no deltas are excluded from the output categorically, just that time is factored in there.

I'm not quite a DSP guru, only have very fractional knowledge there.
But IIRC, the simplest form of low pass, which might work here, would be just to average a box of values.
Say, you have a circular buffer of size 3 (perhaps sufficient, perhaps needs to be bigger). In this case it would make sense I guess to put one new value in the buffer and discard the oldest *one* (you could also move about other lengths over the input stream, but intuitively I'd say that makes no sense here).
You average all values in the buffer, and pass on the result of that, which should be more stable than the fluttering input. If 3 is not stable enough, try 4..5.. etc).

Example:
The input stream of Sustain events: {126,125,127,125,126,127,126}
Output stream of averages (with fractional part | rounded by 0.5)
(After program start, when the first CC event arrives, I guess you need to fill the buffer initially completely with the first ever received value before generating the first output event. After that, each new incoming event just does the regular step: "put newest into buffer, discard oldest from buffer, average all, generate output event")
{126,126,126} -> 126    | 126
{126,126,125} -> 125.6 | 126
{126,125,127} -> 126    | 126
{125,127,125} -> 125.6 | 126
{127,125,126} -> 126    | 126
{125,126,127} -> 126    | 126
{126,127,126} -> 126.3 | 126

As you see, the rounded output remains stable. This depends of course on how widely and with what kind of distribution the input fluctuates and how many values the averaging buffer has.
But differences of (on average) 1 are still possible, which it would not with a solution like "only pass deltas >= 4" or so.
E.g., if it fluctuates, say, around 125 +/-1, and you kick the pedal fully, and it fluctuates around 126 +/-1.


2) Pianoteq and extra MIDI Sustain pedal as own MIDI device

Seeing this thread here, tells me, it must be possible in Pianoteq to have more than one MIDI input device, each for a different purpose?
E.g. use my MIDI keyboard which does not have a continuous sustain pedal input for notes, and a Pedal as own MIDI device as pedal input?
Can someone tell me how to set this up in Pianoteq 5? (I think I have the cheapest version...)

I see little/no point in mere "Smoothing" of suspect values.
Noise from potentiometer wipers is likely to be in the form of spikes (both negative and positive) and could last for milliseconds.
I regard suspect values as anything wildly different from a recent average, those should be discarded immediately, not put with recent valid values to pollute the valid average.  At first guess any change greater than 10% of the current average is too large and has happened too fast to be valid - feet can't move that fast.
In view of the times scales involved, e.g. feet move on a time scale of fractions of a second, but code runs around loops in microseconds - any averaging buffers could be quite LARGE.  The arduino might as well be doing this filtering instead of sitting on a wait for tens of milliseconds.

Clearly differences of 1 need to be filtered out since they can occur from rounding no matter how good the sensor is, this is the plus or minus half the least significant bit problem that ALL analogue to digital conversion has.
Beyond that, sad to say it depends on the particular sensor in each particular pedal, i.e. if you use a 3 pedal unit such as the RPU-3 it would be wise to gather data for each pedal and set your filters to each one.  If you PLAN to do this the code should be simple enough.
My guess is that a cyclic list 64 or 128 locations long might be a good starting place.
From there experimentation is needed, again with the particular pedals that you are using as well as the processing speed of the arduino and its libraries.



Yes, pianoteq can be set to listen on all midi inputs, for example mine is set to listen to midi cable and to loopBE internal midi.
Also many/most midi devices have "midi in" and "midi through", so you can "daisy chain" midi devices.

Last edited by aandrmusic (24-07-2015 15:03)

Re: Standalone continuous sustain pedal?

aandrmusic wrote:

I see little/no point in mere "Smoothing" of suspect values.

Well, averaging pot ADC input is not uncommon. Since I don't know his exact setup / circuit, it was one thing he could try. But the plastic box + wild wires looked noisy enough ;-)
It helped in a few things I built against small but constantly present fluctuatuations due to external noise when pots remained untouched (front panel).
I have no experience with filtering wild expression pedal action ;-)
Or with the output of hall sensors, for that matter. I imagine it would be less "fractured" than potentiometers'.

Having an adequate analog input filter before the ADC is half the rent, anyway.

Noise from potentiometer wipers is likely to be in the form of spikes (both negative and positive) and could last for milliseconds.

You mean like from an old pot, sudden discontinuations in the surface? Is it really the duty of the software to fix pots which should be replaced? ^^ Well I guess making them live a little longer might be worth it.
But last for several ms? How that? I would hardly call that spikes then, given the rate of change we're talking about here.
I haven't seen anything like that so far, maybe because I haven't used anything I built so extensively that the pots could be worn out.

I regard suspect values as anything wildly different from a recent average, those should be discarded immediately, not put with recent valid values to pollute the valid average.

Sounds reasonable, I'll keep that strategy in the back of my head for the time I ever encounter huge jumps and the offending pot is "mostly good" still - *if* it is. And try to balance this against proper reading of quick real changes.

As for the foot not moving so fast, well it can be quite fast. If I kick the pedal, the travel might be well under 10ms for a full scale jump, easily under 20ms. I'd certainly not just discard such a jump.

Clearly differences of 1 need to be filtered out since they can occur from rounding no matter how good the sensor is, this is the plus or minus half the least significant bit problem that ALL analogue to digital conversion has.

Sure, the n * LSB error of the ADC alone should not spoil the game here, though, if maybe a not too crappy 10 bit ADC is used when only 7-bit output is needed.
I don't know the Arduino platform and the quality of its ADCs, though.

Now that I know Pianoteq can use this, I'll certainly solder something together and try how well it works ;-)
I wonder what the heck the FATAR guys were thinking when giving my SL990 a on/off pedal input ;-)

Yes, pianoteq can be set to listen on all midi inputs, for example mine is set to listen to midi cable and to loopBE internal midi.
Also many/most midi devices have "midi in" and "midi through", so you can "daisy chain" midi devices.

Thanks! Now I only need to learn to play the piano properly :-D :-D

Re: Standalone continuous sustain pedal?

Hi,

sleepydog wrote:

I don't know how fast this fluttering is and how much of an *audible* change from e.g. 126 to 127 does.

it is not audible, we only don't want to pollute the midi-bus with unnecessary midi-events. In the worst  case two values could "flutter" with the sample-frequency of the ADC.

But If you read the thread thoroughly we already found several working solutions:

a) http://www.forum-pianoteq.com/viewtopic...84#p938384
b) http://www.forum-pianoteq.com/viewtopic...88#p938388
c) http://www.forum-pianoteq.com/viewtopic...75#p938475
d) http://www.forum-pianoteq.com/viewtopic...17#p938617

I prefer the last solution d) (teensy2.0) and I'm using it a few weeks now with my continuous sustain-pedal and with great success. No problems or glitches at all!

Most of your suggestions are already implemented there, if you read the code:

- circular buffer of 10 samples.
- average of the 10 samples ("smoothing")
- sample-interval ~1ms
- change detection every ~40 ms if sustain delta is > 1

The only not implemeted "feature" is aandrmusic's idea of extreme spike-detection and -dropping.

sleepydog wrote:

Seeing this thread here, tells me, it must be possible in Pianoteq to have more than one MIDI input device, each for a different purpose?

Yes, Pianoteq can use several midi-devices simultaneously. For example I have listed two midi-devices in Pianoteq Standard: My Teensy2.0 continuous sustain-pedal and my USB-keyboard-controller.

sleepydog wrote:

I don't know the Arduino platform and the quality of its ADCs, though.

... both the Arduino nano and the Teensy2.0 have 10-bit ADC (0-1023). That's why I divide by 8 to get (0-127).

cheers

Last edited by groovy (24-07-2015 22:27)

Re: Standalone continuous sustain pedal?

Much of the point I was trying to make is that we don't know what needs to be filtered out until we know what is actually there.
Consumer grade pedals are unlikely to have high grade components, so it is LIKELY that they will produce spikes.
What I think worth doing is to hook up the pedal(s) that you have and look at the midi stream with no filtering.
Watch it when clamped at about half pedal, when fully pressed and when released.
Press it slowly, release it slowly, capture that midi stream and look for SMOOTH transitions.
Also sudden stomps and releases.
If you see sudden jumps and/or bounces you will need to filter - for THAT pedal.

My playing barely needs more than a switched damper pedal anyway, so I have only an interest in this based on my electronics background.

Re: Standalone continuous sustain pedal?

When the continuous sustain-pedal is unplugged, the analog input of the teensy2.0 microcontroller would be undefined, so I inserted a "pull-down" resistor (1 Megaohm) from wiper to ground. This guarantees sustain-off. And with a little luck, it evens false-positives from a scratchy wiper as a side-effect.

http://fs1.directupload.net/images/150816/b2un2bed.jpg

LATE PS:
I found a change-detection every 20 ms to be a better compromise, than 40 ms. This means some more CC64 MIDI-events, but on the other hand a better realtime response. A very fast pressed or released pedal should reach 127 or 0 in a reasonable time (with 40 ms for example it can happen, that full sustain 127 is reached after 81 ms,  but this event is sent 39 ms later after 120 ms -- unnecessary lag)

Last edited by groovy (17-08-2015 18:38)