Skip to content

Instantly share code, notes, and snippets.

@Jacajack
Created September 2, 2020 21:41
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Jacajack/9c6043b6901f3d34b6b3120fe46000f5 to your computer and use it in GitHub Desktop.
Save Jacajack/9c6043b6901f3d34b6b3120fe46000f5 to your computer and use it in GitHub Desktop.
Simple and lightweight polyphonic MIDI interpreter
#include "midi.h"
#include <stddef.h>
#include <inttypes.h>
#include <string.h>
void midi_init(midi_status *midi)
{
midi->dlim = 0;
midi->dcnt = 0;
midi->status = 0;
midi->channel = 0;
midi->program = 0;
midi->pitchbend = 16384;
midi->reset = 0;
memset(midi->voices, 0, sizeof(midi_voice));
memset(midi->control, 0, 128);
}
/**
Finds an empty voice slot and uses it or overwrites the oldest
active slot
*/
static inline void midi_note_on(midi_status *midi, uint8_t note, uint8_t velocity)
{
uint8_t oldest_empty_slot = MIDI_VOICES;
uint8_t oldest_active_slot = 0;
int8_t oldest_empty = -1;
int8_t oldest_active = -1;
for (uint8_t i = 0; i < MIDI_VOICES; i++)
{
int8_t age = midi->voices[i].age;
if (midi->voices[i].gate)
{
if (age > oldest_active)
{
oldest_active_slot = i;
oldest_active = midi->voices[i].age;
}
}
else
{
if (age > oldest_empty)
{
oldest_empty_slot = i;
oldest_empty = age;
}
}
}
// Increment age of all voices
for (uint8_t i = 0; i < MIDI_VOICES; i++)
midi->voices[i].age++;
// Always prefer empty slot over reuse
uint8_t slot = oldest_empty_slot == MIDI_VOICES ? oldest_active_slot : oldest_empty_slot;
midi->voices[slot].gate = 1;
midi->voices[slot].note = note;
midi->voices[slot].velocity = velocity;
midi->voices[slot].age = 0;
}
static inline void midi_note_off(midi_status *midi, uint8_t note)
{
for (uint8_t i = 0; i < MIDI_VOICES; i++)
if (midi->voices[i].note == note)
midi->voices[i].gate = 0;
}
void midi_process_byte(midi_status *midi, uint8_t byte, uint8_t channel)
{
uint8_t dlim, dcnt, status;
// Null pointer check
if (midi == NULL) return;
// Get configuration from the struct
dlim = midi->dlim;
dcnt = midi->dcnt;
status = midi->status;
// Handle synthesizer reset
if (byte == 0xff) midi->reset = 1;
if (byte & (1 << 7)) // Handle status bytes
{
// Extract information from status byte
status = byte & 0x70;
dcnt = dlim = 0;
midi->channel = byte & 0x0f;
// Check data length for each MIDI command
switch (status)
{
// Note on
case 0x10:
dlim = 2;
break;
// Note off
case 0x00:
dlim = 2;
break;
// Controller change
case 0x30:
dlim = 2;
break;
// Program change
case 0x40:
dlim = 1;
break;
// Pitch
case 0x60:
dlim = 2;
break;
// Uknown command
default:
dlim = 0;
break;
}
}
else if (midi->channel == channel) // Handle data bytes
{
// Data byte
midi->dbuf[dcnt++] = byte;
// Interpret command
if (dcnt >= dlim)
{
switch (status)
{
// Note on
case 0x10:
midi_note_on(midi, midi->dbuf[0], midi->dbuf[1]);
break;
// Note off
case 0x00:
midi_note_off(midi, midi->dbuf[0]);
break;
// Controller change
case 0x30:
midi->control[midi->dbuf[0]] = midi->dbuf[1];
break;
// Program change
case 0x40:
midi->program = midi->dbuf[0];
break;
// Pitch
case 0x60:
midi->pitchbend = midi->dbuf[0] | (midi->dbuf[1] << 7);
break;
default:
break;
}
dcnt = 0;
}
}
// Write config back to the struct
midi->dlim = dlim;
midi->dcnt = dcnt;
midi->status = status;
}
#ifndef MIDI_H
#define MIDI_H
#include <inttypes.h>
#ifndef MIDI_VOICES
#error MIDI_VOICES is not defined
#endif
typedef struct midi_voice
{
uint8_t note;
uint8_t velocity;
uint8_t gate;
int8_t age;
} midi_voice;
typedef struct midi_status
{
// Internal state of the interpreter
uint8_t dlim;
uint8_t dcnt;
uint8_t status;
uint8_t channel;
uint8_t dbuf[4];
// Per voice controls
midi_voice voices[MIDI_VOICES];
// Basic MIDI controls
uint8_t program;
uint16_t pitchbend;
uint8_t reset;
// MIDI controllers
uint8_t control[128];
} midi_status;
extern void midi_init(midi_status *midi);
extern void midi_process_byte(midi_status *midi, uint8_t byte, uint8_t channel);
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment