- MIDI is not sound. It is performance data that tells an instrument what to play, when to play it, and how to play it, which is why it stays so editable inside a DAW.
- The biggest advantage of MIDI is flexibility. You can change notes, timing, chord voicings, dynamics, and even swap instruments without re-recording the part.
- MIDI and audio serve different purposes in production. MIDI is best for writing, arranging, and experimenting, while audio is the recorded result you usually commit to when moving toward mixing and finalizing.
- MIDI powers far more than keyboard parts. It is used for drums, synths, automation, live performance control, film scoring, game audio, and other workflows where musical ideas need to stay editable.
A MIDI message is a stream of 8-bit binary bytes, which translate to integers ranging from 0 to 255. These numbers are split into two primary categories: Status Bytes (128–255), which dictate the action and MIDI channel, and Data Bytes (0–127), which specify the details of that action.
Anatomy of a MIDI Message
Most standard MIDI messages (like pressing a key) consist of three numbers sent in a specific sequence:
Example (Note On)
1. Status Byte(128 – 255)Defines the command (e.g., Note On) and the MIDI channel (1–16).144 (Note On, Channel 1)
2. Data Byte 1( 0 – 127)Specifies the note number / pitch (Middle C is 60).60 (Middle C)
3. Data Byte 2 (0 – 127}Specifies the velocity (how hard the key was struck).100 (Fairly loud)
Common MIDI Number Values
1. Command Identifiers (Status Prefix):
The status byte combines the command type and the channel. In decimal form, the base numbers for Channel 1 are:
- 128: Note Off
- 144: Note On (Key pressed)
- 160: Polyphonic Key Pressure (Aftertouch)
- 176: Control Change (Knobs, faders, sustain pedal)
- 192: Program Change (Changing instrument patches)
- 224: Pitch Bend Change
Note: To change the MIDI channel, you simply add the channel number minus one to the base status number (e.g., 144 + 0 = Channel 1, 144 + 1 = Channel 2).
2. Key/Pitch Numbers (Data 1):
MIDI maps musical pitches across a 0–127 scale, where every integer is a half-step:
- 0: Lowest possible MIDI note (C-1, roughly 8.18 Hz)
- 60: Middle C (C4)
- 69: Concert A (A4, 440 Hz)
- 127: Highest possible MIDI note (G9, roughly 12,543 Hz)
3. Velocity and Controller Values (Data 2):
For expressive data like volume, velocity, or modulation wheels, the values span from 0 to 127:
- 0: Minimum value (e.g., silent velocity, or off position for a pedal)
- 64: Middle value (e.g., standard half-way position)
- 127: Maximum value (e.g., maximum volume, or hardest possible key strike)
Examples:
talkMIDI(0xB0, 0, 0x00); //Default bank GM1
talkMIDI(0xC0, instrument, 0); //Set instrument number. 0xC0 is a 1 data byte command
//Note on channel 1 (0x90), some note value (note), middle velocity (0x45):
noteOn(0, note, 60);
delay(50);
//Turn off the note with a given off/release velocity
noteOff(0, note, 60);
delay(50);
//=================================================================
// On an Arduino Mega, hardware serial can be used, but keeping SoftwareSerial on pins 2 & 3
// maintains direct drop-in pin compatibility with the SparkFun Musical Instrument Shield.
#include <SoftwareSerial.h>
SoftwareSerial mySerial(2, 3); // RX = Pin 2, TX = Pin 3
const byte resetMIDI = 4; // Tied to VS1053 Reset line
const byte ledPin = 13; // MIDI traffic indicator
// Pin assignments for the 8 active-low IR sensors (adjust pin numbers to match your wiring)
const byte sensorPins[8] = {22, 23, 24, 25, 26, 27, 28, 29};
// G Major Scale starting at G3 (MIDI note numbers):
// G3 (55), A3 (57), B3 (59), C4 (60), D4 (62), E4 (64), F#4 (66), G4 (67)
const byte scaleNotes[8] = {55, 57, 59, 60, 62, 64, 66, 67};
// Track current and previous trigger states
bool notePlaying[8] = {false, false, false, false, false, false, false, false};
void setup() {
Serial.begin(57600);
pinMode(ledPin, OUTPUT);
// Configure sensor pins with internal pull-ups (ensures HIGH when unlit/blocked)
for (byte i = 0; i < 8; i++) {
pinMode(sensorPins[i], INPUT_PULLUP);
}
// MIDI communication setup at 31250 baud
mySerial.begin(31250);
// Reset the VS1053 chip
pinMode(resetMIDI, OUTPUT);
digitalWrite(resetMIDI, LOW);
delay(100);
digitalWrite(resetMIDI, HIGH);
delay(100);
// Set default bank: GM1
talkMIDI(0xB0, 0x00, 0x00);
// Set channel 0 volume to max (127)
talkMIDI(0xB0, 0x07, 127);
// Instrument selection: 19 = Church Organ
talkMIDI(0xC0, 19, 0);
Serial.println("Paper tape IR reader ready.");
}
void loop() {
for (byte i = 0; i < 8; i++) {
// Sensor output is active-low: LOW indicates light detected through hole
bool isTriggered = (digitalRead(sensorPins[i]) == LOW);
// Hole detected: start note if not already sounding
if (isTriggered && !notePlaying[i]) {
noteOn(0, scaleNotes[i], 90);
notePlaying[i] = true;
}
// Hole passed / blocked: end note
else if (!isTriggered && notePlaying[i]) {
noteOff(0, scaleNotes[i], 60);
notePlaying[i] = false;
}
}
// Small debounce / scan interval
delay(5);
}
void noteOn(byte channel, byte note, byte attack_velocity) {
talkMIDI((0x90 | (channel & 0x0F)), note, attack_velocity);
}
void noteOff(byte channel, byte note, byte release_velocity) {
talkMIDI((0x80 | (channel & 0x0F)), note, release_velocity);
}
void talkMIDI(byte cmd, byte data1, byte data2) {
digitalWrite(ledPin, HIGH);
mySerial.write(cmd);
mySerial.write(data1);
if ((cmd & 0xF0) <= 0xB0 || (cmd & 0xF0) >= 0xE0) {
mySerial.write(data2);
}
digitalWrite(ledPin, LOW);
}



No comments:
Post a Comment