Header-only C++17 MIDI library with complete MIDI 1.0 implementation.
- Full MIDI 1.0 specification support
- Header-only, no dependencies for core functionality
- Zero-allocation parser suitable for embedded systems
- Type-safe message handling
- Running status optimization
- SysEx with configurable buffer management
- NRPN/RPN parameter tracking
- Platform-independent I/O abstraction
- C++17 or later
- CMake 3.10+ (optional, for building tests/examples)
- RtMidi (optional, for platform MIDI I/O examples)
Copy the include/littlemidi directory to your project and include:
#include <littlemidi/littlemidi.hpp>#include <littlemidi/littlemidi.hpp>
using namespace littlemidi;
// Parse MIDI data
Parser<> parser;
parser.setMessageCallback([](const MidiMessage& msg) {
// Handle parsed messages
});
parser.processByte(midiByteFromInput);
// Build messages
MessageBuilder builder;
auto noteOn = builder.channel(0).noteOn(Note{60}, Velocity{100});
auto serialized = noteOn.serialize();
// Use utilities
std::string noteName = NoteNames::noteName(Note{60}); // "C4"
double freq = Frequency::noteToFrequency(Note{69}); // 440.0 HzAll standard MIDI messages are supported:
- Channel Voice Messages (Note On/Off, Control Change, Program Change, etc.)
- Channel Mode Messages (All Notes Off, Reset Controllers, etc.)
- System Common (SysEx, MTC Quarter Frame, Song Position, etc.)
- System Real-Time (Clock, Start/Stop, Active Sensing, etc.)
- Extended messages (NRPN, RPN with automatic state tracking)
The parser implements a complete MIDI state machine with:
- Running status optimization
- Real-time message handling (can interrupt any message)
- SysEx buffer management with configurable size
- NRPN/RPN state tracking per channel
- Error callbacks for malformed data
The library provides an abstract MidiPort interface for platform-specific implementations:
class CustomMidiPort : public MidiPort {
bool open() override;
bool close() override;
bool sendBytes(const Byte* data, size_t length) override;
// Implement for your platform
};Memory and loopback ports are included for testing.
mkdir build && cd build
cmake ..
make
./test_littlemidi # Run testsFor real MIDI I/O on macOS/Linux/Windows:
brew install rtmidi # macOS
# or apt-get install librtmidi-dev on Linux
c++ -std=c++17 -I./include $(pkg-config --cflags --libs rtmidi) \
-o midi_monitor examples/midi_monitor.cpp
./midi_monitor # Creates virtual MIDI portThe included midi_monitor.cpp provides a full-featured MIDI monitor with:
- Virtual port creation
- Hardware port selection
- Color-coded message display
- Visual indicators for pitch bend, velocity, and controllers
MessageDispatcher dispatcher;
dispatcher.onNoteOn([](Channel ch, Note note, Velocity vel) {
std::cout << "Note: " << NoteNames::noteName(note) << "\n";
});
dispatcher.onControlChange([](Channel ch, Controller ctrl, Byte value) {
if (ctrl.value == 64) { // Sustain pedal
std::cout << "Sustain: " << (value >= 64 ? "ON" : "OFF") << "\n";
}
});The library consists of several modular components:
types.hpp- Core MIDI types with type safetymessages.hpp- Message structures for all MIDI message typesparser.hpp- State machine parser with running statusdispatcher.hpp- Callback-based message routingutils.hpp- Utilities for note names, frequencies, pitch bend, etc.io.hpp- Platform I/O abstraction layer
For embedded systems:
// Use small SysEx buffer, no dynamic allocation
Parser<256> parser;
// Process bytes from UART
void UART_IRQHandler() {
while (UART_HasData()) {
parser.processByte(UART_ReadByte());
}
}Bug reports and pull requests welcome. Please follow the existing code style.
MIT License - see LICENSE file for details.