Hi all,
as guentergunter did, I did some research and reprogramming and could now finalize my "Pedal-To-MIDI Box".
My box supports not only 6 pedals but now it supports
Pedal Noise and
Half Pedal with a Sustain pedal in continous mode (Roland DP-10 in my case).
I had 2 main intentions for the box:
1. Reduce cabling going up to NS3. Keep cabling of (max.) 6 pedals on the floor. (normally I don't need all of them - just to be prepared)
2. Replace expensive Nord Triple Pedal
So finally this box can replace the Nord Triple Pedal with all of it's special features - and more!
My box is based on an Arduino Pro Mini 328 - 5V/16MHz.
Schematic is based on
download/file.php?id=9232 (in this thread).
Modifications (based on my preferations):
- MIDI IN: left out
- Program up/down switches: left out
+ Rotary Speed toggle switch
+ Soft (left) pedal
+ Organ volume
I don't post photos of my box since it is a prototype and not very nice
It is just a small plastic case which can hold the In and Out Jacks and the board with the arduino inside - nothing fancy at all.
Here's my finalized code:
Code: Select all
//
// Midi Box for Nord Stage 3
// by Klondyke77 03/2020
// Finalized V2.0: 16.03.2020
// V2.1: 19.12.2020 (starting improvement tests)
// Trying new code for sustain pedal
// Reducing of Midi commands for sustain pedal successfull
// V2.3: 20.12.2020 (continuing from 2.1, discarding V2.2)
// Pedal noise successfull. Calibrated sustain pedal trigger positions
// V2.4: 21.12.2020 implementing sustain pedal speed detection
// Sustain pedal including Half pedal and Pedal noise is working.
// code may still need some finalizing.
// V2.5: 25.12.2020 code finalizing and documenting
// ---------------------------------
// Contributions:
// code is based on:
// https://www.norduserforum.com/nord-stage-forum-f3/custom-comprehensive-nord-stage-pedal-board-t14141.html
// by "guentergunter"
// My own "Pedal-to-MIDI Box" is based on an Arduino Pro Mini 328 - 5V/16MHz
// Schematic is based on: https://www.norduserforum.com/download/file.php?id=9232 (in above thread)
// Schematic is modified:
// - MIDI IN: left out (not needed)
// - Program up/down switches: left out (not needed)
// + Rotary Speed toggle switch
// + Soft (left) pedal
// + Organ volume
// ---------------------------------
// Main improvement to V2.0 is "Velocity dependant Damper Noise" thanks to engineering of guentergunter!
// New findings regarding Sustain pedal in continous mode:
//
// In the following "send values" means always sending a Midi message on Control Channel (CC) 64,
// which is Standard Control Channel for Sustain.
//
// 1. My last implementation of continous sustain pedal was to send position values with every movement of the pedal.
// This is also the way other manufacturers handle Midi messages with a continous Sustain Pedal (My Yamaha Clavinova does it this way).
// But with the new knowledge I assume that this is not the way Nord has intended to handle it.
// This may be the explanation why you get unintended Pedal Noise when doing so.
// You would have to disable Pedal Noise per program - so did I - until now ;-)
//
// 2. What Nord expects:
// - Send just one Midi message when pedal meets positions "Up / Half / Down", values: 0 / 64 / 127.
// - To activate Damper Noise via Midi:
// + Send additional Midi message (on CC 64) right before position message Up or Down
// + Slow pedal movement (low velocity) is represented by lower values and will cause light and silent pedal noise.
// + Fast pedal movement (high velocity) is represented by higher values and will cause strong and loud(er) pedal noise.
// + There are different value ranges for pedal up and pedal down:
// * Pedal down: 27..42
// * Pedal up: 10..18
// Example for pedal down:
// When pedal reaches defined position for pedal down, the following two commands are sent:
// send_MIDI_message(0xB0, MIDI_CC_Sustain, 30); // assuming 30 as derived damper speed
// send_MIDI_message(0xB0, MIDI_CC_Sustain, 127); // message for pedal down
//
// + Speed detection is needed to make the values (2.) velocity dependant.
// I decided to derive speed from measuring delta pedal position value in a
// specified amount of time (sample interval).
// Actually damper_speed is calculated continously but only used once when
// damper positions down or up is reached.
// There maybe ways to improve speed detection but for now I'm happy to have
// it working fine!
// 3. Half pedal can be realized by sending value 64 once when pedal reaches defined position ("Half").
// Nord Stage 3 has only one position of half pedal implemented with corresponding samples - not more.
// ---------------------------------
//
// Toggle Debug Mode
// Debug mode configures/redirects serial output for serial monitoring
//#define DEBUG
#ifdef DEBUG
const int SERIAL_PORT_RATE = 9600;
#else
const int SERIAL_PORT_RATE = 31250;
#endif
//
// MIDI CC (Control Channel) Numbers for NORD STAGE 3 Keyboard
//
const int MIDI_CC_Sustain = 64;
const int MIDI_CC_Sostenuto = 66;
const int MIDI_CC_SoftPedal = 67;
const int MIDI_CC_CrtlPedalExpr = 11;
const int MIDI_CC_RotarySpeed = 108;
const int MIDI_CC_OrganLevel = 13;
const int MIDI_CC_OrganSwell = 4;
//
// MIDI constant values
//
const int RotarySlow = 43;
const int RotaryFast = 127;
#define MIDI_Channel 0
// Pin Numbers
#define PIN_orgs A0 // Organ Swell Pedal (continous) Roland only!
#define PIN_sust A1 // Sustain Pedal (continous) Roland only!
#define PIN_expr A2 // Expression Pedal (continous) Yamaha only!
#define PIN_sost 2 // Sostenuto Pedal (momentary)
#define PIN_soft 3 // Soft Pedal (momentary)
#define PIN_rota 4 // Rotary Speed (momentary)
// constant values
const int POT_THRESHOLD = 3; // Threshold amount to guard against false values
// variable init
uint8_t sost = 0;
uint8_t sost_last = 0;
uint8_t soft = 0;
uint8_t soft_last = 0;
uint8_t rota = 0;
uint8_t rota_last = 0;
uint8_t rotary_state = 0;
uint8_t damper = 0;
uint8_t damper_last = 0;
// variables for speed detection
unsigned long currentMillis;
unsigned long previousMillis = 0;
unsigned long time_delta;
int damper_val1 = 0;
int damper_val2 = 0;
int damper_delta = 0;
const long interval = 50; // sample time in milliseconds
bool read_val = true;
int damper_speed = 0;
//
// End variable declaration/initialization
//
//
// Functions
//
void send_MIDI_message (byte cmd, byte byte1, byte byte2)
{
#ifdef DEBUG
// redirect output in Debug mode
char buf[100];
sprintf(buf, "%d %d %d",cmd + MIDI_Channel, byte1, byte2);
Serial.println(buf);
#else
Serial.write(cmd + MIDI_Channel);
Serial.write(byte1);
Serial.write(byte2);
#endif
}
void setup() // Run once, when the sketch starts
{
pinMode(PIN_sost, INPUT_PULLUP);
pinMode(PIN_soft, INPUT_PULLUP);
pinMode(PIN_rota, INPUT_PULLUP);
Serial.begin(SERIAL_PORT_RATE); // Starts communication with the serial port
Serial.flush();
}
void loop() // Run over and over again
{
// **************************************
// Momentary Switches
// **************************************
//
// Sostenuto (Middle) Pedal (momentary/closed)
//
sost = digitalRead(PIN_sost);
if (sost != sost_last)
{
sost_last = sost;
send_MIDI_message(0xB0, MIDI_CC_Sostenuto, (sost * 127));
}
//
// Soft (Left) Pedal (momentary/closed)
//
soft = digitalRead(PIN_soft);
if (soft != soft_last)
{
soft_last = soft;
send_MIDI_message(0xB0, MIDI_CC_SoftPedal, (soft * 127));
}
//
// Rotary Speed switch (momentary/closed)
// (toggle switch)
//
rota = digitalRead(PIN_rota);
if (rota != rota_last && rota == 1)
{
if (rotary_state == 0)
{
rotary_state = 1; // Rotary fast
send_MIDI_message(0xB0, MIDI_CC_RotarySpeed, RotaryFast);
}
else if (rotary_state == 1)
{
rotary_state = 0; // Rotary slow
send_MIDI_message(0xB0, MIDI_CC_RotarySpeed, RotarySlow);
}
}
rota_last = rota;
// **************************************
// Continous Pedals
// **************************************
//
// Sustain Pedal (continous)
// Values calibrated for:
// Roland DP10 in continous mode (Min=35, Max=1020)
//
//
// Sustain Pedal - Speed detection
//
currentMillis = millis();
// read and keep first value until second val is read
if (read_val)
{
damper_val1 = analogRead(PIN_sust);
read_val = false;
}
// wait interval time befor reading second value
if (currentMillis - previousMillis >= interval)
{
previousMillis = currentMillis;
damper_val2 = analogRead(PIN_sust);
damper_delta = damper_val2 - damper_val1;
// enable next speed detection
read_val = true;
// disable noise
if (abs(damper_delta) > 2)
{
// calculate Midi values for damper speed
// pedal down (delta positive): 27..42
// pedal up (delta negative): 10..18
if (damper_delta > 0) // pedal down
{
damper_speed = map (damper_delta, 0, 950, 27, 42);
damper_speed = constrain (damper_speed, 27, 42);
}
if (damper_delta < 0) // pedal up
{
damper_speed = map (damper_delta, 0, -900, 10, 18);
damper_speed = constrain (damper_speed, 10, 18);
}
/*
#ifdef DEBUG
// monitoring output
char buf[120];
sprintf(buf, "damper_delta:%4d damper_speed %2d", damper_delta, damper_speed);
Serial.println(buf);
#endif
*/
}
}
//
// Sustain Pedal
// position detection and MIDI output
//
static int s_SPmin = 30;
static int s_SPmax = 1000;
static int dposMid = 45; // treshold relase to half-pressed
static int dposHigh = 65; // treshold half-pressed to pressed
damper_val2 = constrain((damper_val2),s_SPmin, s_SPmax);
int damper_pos = map(damper_val2, s_SPmin, s_SPmax, 0, 127); // Map the value to 0-127
// Manually defined mapping to be able to define non-linear
// pedal positions for half-pedal and down position.
// (calculated positions via map command did not fit my expections)
//
if (damper_pos <= dposMid) {
damper = 0;
}
else if (damper_pos > dposMid and damper_pos <= dposHigh) {
damper = 1;
}
else
{
damper = 2;
}
if(damper != damper_last)
{
damper_last = damper;
switch (damper)
{
case 0:
send_MIDI_message(0xB0, MIDI_CC_Sustain, damper_speed);
send_MIDI_message(0xB0, MIDI_CC_Sustain, 0);
break;
case 1:
send_MIDI_message(0xB0, MIDI_CC_Sustain, 64);
break;
case 2:
send_MIDI_message(0xB0, MIDI_CC_Sustain, damper_speed);
send_MIDI_message(0xB0, MIDI_CC_Sustain, 127);
break;
}
}
//
// Expression pedal (continous)
// Values calibrated for:
// Yamaha EV-7 (Min=0, Max=1019)
//
static int s_EPmin = 0;
static int s_EPmax = 1019;
static int s_nLastPot0Value = 0;
static int s_nLastMapped0Value = 0;
int nCurrentPot0Value = analogRead(PIN_expr);
if(abs(nCurrentPot0Value - s_nLastPot0Value) >= POT_THRESHOLD)
{
s_nLastPot0Value = nCurrentPot0Value;
nCurrentPot0Value = constrain( (nCurrentPot0Value),s_EPmin, s_EPmax);
int nMapped0Value = map(nCurrentPot0Value, s_EPmin, s_EPmax, 0, 127); // Map the value to 0-127
if(nMapped0Value != s_nLastMapped0Value)
{
s_nLastMapped0Value = nMapped0Value;
send_MIDI_message(0xB0, MIDI_CC_CrtlPedalExpr, nMapped0Value);
}
}
//
// Organ Swell pedal (continous)
// Values calibrated for:
// Roland EV-7 (Min=0, Max=900)
//
static int s_SWmin = 0;
static int s_SWmax = 900;
static int s_nLastPot2Value = 0;
static int s_nLastMapped2Value = 0;
int nCurrentPot2Value = analogRead(PIN_orgs);
if(abs(nCurrentPot2Value - s_nLastPot2Value) >= POT_THRESHOLD)
{
s_nLastPot2Value = nCurrentPot2Value;
int nMapped2Value = map(nCurrentPot2Value, s_SWmin, s_SWmax, 0, 127); // Map the value to 0-127
nCurrentPot2Value = constrain( (nCurrentPot2Value),s_SWmin, s_SWmax);
if(nMapped2Value != s_nLastMapped2Value)
{
s_nLastMapped2Value = nMapped2Value;
send_MIDI_message(0xB0, MIDI_CC_OrganSwell, nMapped2Value);
}
}
}
Hope this helps other people to realize similar projects.
Reiner