Skip to main content
Arduino

Return to Revisions

2 of 4
Obtained min/max fader extents using average of several samples.
tim
  • 699
  • 6
  • 15

Firstly, you need a robust way to synchronise to and parse MIDI messages which can be 1 byte, 2 bytes or 3 bytes or more... See this Summary of MIDI 1.0 Messages by the MIDI Association.

A good way to achieve this is by using a state machine. I've written a simple one below that parses 3-byte MIDI messages. It could be expanded to parse different types of commands.

typedef enum State
{
 Idle,
 Command,
 Data1
};
State state = State::Idle;
const float INCOMING_VELOCITY_SCALE = 1023.0 / 127.0;
byte incomingCommand;
byte incomingNote;
byte incomingVelocity;
int targetPosition;
void loop()
{
 if (midiSerial.available()) // Use the `if` statement to get the next available byte.
 {
 byte incoming = midiSerial.read();
 // Synchronise to and parse the 3-byte MIDI message packet, i.e. 1nnnnnnn 0nnnnnnn 0nnnnnnn
 switch (state)
 {
 case State::Idle:
 if (IsMidiCommand(incoming))
 {
 Serial.println("Received command byte.");
 incomingCommand = incoming;
 state = State::Command;
 }
 else if (IsMidiData(incoming))
 {
 Serial.println("Received data byte.");
 }
 break;
 case State::Command:
 if (IsMidiCommand(incoming)) // Double check for an out of sync command.
 {
 Serial.println("Received command byte.");
 incomingCommand = incoming;
 state = State::Command;
 }
 else if (IsMidiData(incoming))
 {
 Serial.println("Received data1 byte.");
 incomingNote = incoming;
 state = State::Data1;
 }
 break;
 case State::Data1:
 if (IsMidiCommand(incoming)) // Triple check for an out of sync command.
 {
 Serial.println("Received command byte.");
 incomingCommand = incoming;
 state = State::Command;
 }
 else if (IsMidiData(incoming))
 {
 Serial.println("Received data2 byte.");
 incomingVelocity = incoming;
 midiSerial.write(incomingCommand);
 midiSerial.write(incomingNote);
 midiSerial.write(incomingVelocity);
 Serial.write(incomingCommand);
 Serial.write(incomingNote);
 Serial.write(incomingVelocity);
 Serial.println();
 targetPosition = incomingVelocity * INCOMING_VELOCITY_SCALE;
 if (targetPosition > faderMax) // Clip if out of bounds.
 {
 targetPosition = faderMax;
 }
 else if (targetPosition < faderMin)
 {
 targetPosition = faderMin;
 }
 state = State::Idle;
 }
 break;
 default:
 state = State::Idle;
 break;
 }
 }
 . . .
 updateFader(targetPosition);
 . . .
}
bool IsMidiCommand(const byte b)
{
 return b >= 128; // For MIDI command bytes, MSB is 1.
}
bool IsMidiData(const byte b)
{
 return b <= 127; // For MIDI data bytes, MSB is 0.
}

Secondly, there is an issue with the updateFader() function regarding the tolerance. It's 40 in your first version and 10 in your second version. Consider the following values for the variables:

// In setup()
digitalWrite(motorDown, HIGH);
analogWrite(motorPWM, motorSpeed);
delay(300);
faderMin = analogRead(wiper) + tolerance;
faderMin = 0 + 40; // Bottom of range.
faderMin = 40;
// In loop()
incomingVelocity = midiSerial.read() * 8.05511811024;
incomingVelocity = 1 * 8.05511811024; // A quiet note, e.g. between 0 and 5, will a give a value of 0 to 40.
incomingVelocity = 8;
// In updateFader()
if (position < analogRead(wiper) - tolerance && position > faderMin)
if (8 < 512 - 40 && 8 > 40 )
if (8 < 472 && 8 > 40 )
if (true && false )
if (false )

Result: the fader will not budge if a quiet note is received that puts position below faderMin and will leave the fader jamming somewhere in the middle of its scale rather than boogieing on down to faderMin. This effect would be exaggerated if faderMin were higher than 40 when analogRead(wiper) reads greater than zero in setup().

Similarly, this also happens with very loud notes and faderMax at the other end of the scale:

// In updateFader()
else if (position > analogRead(wiper) + tolerance && position < faderMax)

A better way would be to clip the targetPosition to the valid range before calling updateFader() and remove the second condition from the if statements in updateFader():

targetPosition = incomingVelocity * INCOMING_VELOCITY_SCALE;
if (targetPosition > faderMax) // Clip if out of bounds.
{
 targetPosition = faderMax;
}
else if (targetPosition < faderMin)
{
 targetPosition = faderMin;
}
. . .
updateFader(targetPosition);
. . .
if (position < analogRead(wiper) - tolerance)
. . .
else if (position > analogRead(wiper) + tolerance)
. . .

Thirdly, updateFader() blocks until the fader motor has finished moving. If notes are being received faster than the fader can move, the tempo will rallentando, so consider using a non-blocking style like Blink Without Delay.

Fourthly, faderMin and faderMax should be just that – the minimum and maximum extent of fader travel. But by adding/substracting tolerance from min/max it is unnecessarily reducing the range. The tolerance is effectively being doubled by adjusting for it in both setup() and adjustFader(). The extent of fader travel can be obtained by taking an average of several values in calibrateFader(). Then the tolerance can be adjusted for only once in adjustFader() as described above.

void calibrateFader()
{
 const byte num_samples = 10;
 digitalWrite(motorUp, HIGH);
 analogWrite(motorPWM, motorSpeed);
 delay(300);
 digitalWrite(motorUp, LOW);
 unsigned int faderTotal = 0;
 for (byte i = 0; i < num_samples; i++)
 {
 faderTotal += analogRead(wiper);
 }
 faderMin = faderTotal / num_samples;
 Serial.println(faderMin);
 digitalWrite(motorDown, HIGH);
 analogWrite(motorPWM, motorSpeed);
 delay(300);
 digitalWrite(motorDown, LOW);
 faderTotal = 0;
 for (byte i = 0; i < num_samples; i++)
 {
 faderTotal += analogRead(wiper);
 }
 faderMax = faderTotal / num_samples;
 Serial.println(faderMax);
}
tim
  • 699
  • 6
  • 15

AltStyle によって変換されたページ (->オリジナル) /