MIDI is an amazing protocol. For almost 40 years it’s been allowing musicians to express their creativity, was one of the factors for the rapid development of electronic music and completely revolutionized the music industry. Because it is a low-level protocol, using it directly might be a bit challenging. Web MIDI API provides an interface to communicate with both input and output MIDI devices, but there is no abstraction layer on top of basic transport methods. To fix this problem, I have created MIDIVal — platform-agnostic library for all your MIDI needs. It provides high-level methods and can greatly simplify your workflow. Moreover, as it was designed to be platform-agnostic in mind, you can use the same code to communicate with your MIDI devices in the browsers ), node.js, and even in React Native (plugin in development).
Getting started
MIDIVal can be found in the npm (node package manager) and it’s split into several packages. The core one you are most likely to use is @midival/core. It provides the main functionality of the library and browser bindings. To use it in node or React Native environment you'd need to install adapters included in @midival/node
or @midival/react-native
respectively (The React Native library is still under development, get in touch if you'd like to use it or want to helphelp with development!).
Main goals
I designed MIDIVal with few main goals in mind:
- High-level API
- Platform agnosticism
- Extendability
High-Level API
The main goal of the project is to provide a high-level API — methods represent music concepts like keypress, control change, etc. API can be easily used without prior knowledge of the MIDI protocol and it’s a perfect choice if you want to quickly prototype a web synthesizer, hack quickly your idea but can be also used in production.
Platform agnosticism
No matter if you want to create a website that interacts with MIDI, a node.js application, or a mobile application written in React Native, you should be able to use MIDIVal in the same way. All implementation details are hidden away in the abstractions and switching between platforms requires minimal setup. The browser implementation is already public and the node and React Native bindings will follow.
Extendability
MIDI devices come in many different shapes and forms — you can use the same messages to control your synthesizer, software effect, set control surface lights, and much more. It is not possible to write a single library to cover all devices and uses. That’s why MIDIVal can be easily extended. You can easily extend the basic functionality and if you need more control over the messages you can still send raw data, including SysEx.
MIDIVal 101 — interacting with your device
Installation
To install MIDIVal you need to install @midival/core
package using your favorite package manager:
npm --save install @midival/core
# OR
yarn add @midival/core
Sending MIDI messages to your device
Let’s get started with MIDIVal and send a note to the first connected MIDI interface.
import MIDIVal, { MIDIValOutput } from "@midival/core";
MIDIVal.connect()
.then(async ({outputs}) => {
const firstInput = new MIDIValOutput(outputs[0]);
firstInput.sendNoteOn(60, 128);
await delay(1000);
firstInput.sendNoteOff(60);
});
The code above waits for the MIDIVal interface to be connected then connects to the first output and plays the C4 note (MIDI Value 60) for a second. Implementation of delay function was omitted for simplicity. You can also send other parameters like Control Change and Program Change easily:
firstInput.sendControlChange(7, 100); // changing channel volume: Control Number 7
firstInput.sendProgramChange(39); // sets Synth Bass 1 (if instrument implements General MIDI instruments)
Reading MIDI messages
Similarly, you can read MIDI messages sent by your device (for example MIDI controller, control surface, etc).
import MIDIVal, { MIDIValInput } from "@midival/core";
MIDIVal.connect()
.then(async ({ inputs }) => {
const input = new MIDIValInput(inputs[0]);
input.onAllNoteOn((note, midiMessage) => {
console.log(`Note ${note} has been pressed`);
});
});
The code above connects to the first input interface and subscribes to all notes on messages. All .onX methods return callbacks which can be used to unsubscribe. Similarly, you can subscribe to messages for notes off, control change, program change, and even arbitral SysEx data. If you want to filter out the messages (listen to a specific key being pressed or a specific control key), you can do so:
input.onControlChange(5, (midiMessage) => {
console.log(`Portamento changed to: ${midiMessage.data2}`);
})
The code above listens to all portamento changes (channel 5).
Those are just a few examples of how to get started. For full documentation head to the official documentation.