The goal of this project is to provide a network adapter for the Linux ALSA Sequencer API.
npm install simple-dogLinux ALSA Sequencer UDP interface
==================================
The goal of this project is to provide a network adapter for the Linux ALSA Sequencer API.
This project grew out of limitations experienced with MIDI packages in Node.js (issues when using MIDI and networking, or MIDI and Electron, for example), and the lack of up-to-date MIDI packages for Python.
It should also be useful to easily integrate third-party, IoT-based devices into the Linux MIDI stack.
This project implements a MIDI-to-UDP interface which allows to receive, send, and control ALSA MIDI Sequencer sentences. The UDP interface is then made available as an API in a separate application; in the present case this package provides a Node.js API, but the implementation is language-agnostic.
The MIDI/UDP gateway is written entirely in C with alsa-lib (what else) and libuv to ensure fast delivery of messages.
I am not aware of similar work but would greatly appreciate any references.
This is orthogonal to the purpose of RTP MIDI, in that RTP-MIDI is meant to be used between MIDI instruments (horizontally) while this is meant to be used between a MIDI stack and a non-MIDI environment (vertically).
apt install -y --no-recommends libasound2 libasound2-dev libuv1 libuv1-dev build-essential
`
$3
`
apk add musl-dev alsa-lib alsa-lib-dev libuv libuv-dev gcc
`Installation
Using npm
`
npm install simple-dog
`Using yarn
`
yarn add simple-dog
`Starting the process
As indicated, this package contains two parts,
- a Linux ALSA MIDI to UDP gateway written in C
- an implementation of an API for Node.js.
In the regular case, the gateway is started (on the local host) by the Node.js process.
However the gateway can also be started manually, either on the local machine or on a remote machine.
`
./bin/alsa-midi-gateway
`The process will bind on UDP port 3001 and send messages to UDP port 3002 on localhost by default.
The bind options can be overriden with
SOURCE_HOST and SOURCE_PORT environment variables.
The remote options can be overriden with DEST_HOST and DEST_PORT environment variables.API
Either
`
import {AlsaMidiClient} from 'simple-dog'
`
or
`
const {AlsaMidiClient} = require('simple-dog')
`$3
`
const midi = new AlsaMidiClient('MIDI')
`$3
Create a new MIDI port for input and output.
`
const port = await midi.createPort('Port 1')
`Create a new MIDI port for input only.
`
const port = await midi.createPort('Port 1','input')
`Create a new MIDI port for output only.
`
const port = await midi.createPort('Port 1','output')
`$3
`
await midi.deletePort(port)
`$3
`
await midi.sendMidi([0x91,0x40,velocity])
`$3
`
const client_id = await midi.clientId()
await midi.portConnect(client_id,port, other_client_id,other_port)
`$3
`
await midi.portDisconnect(client_id,port, other_client_id,other_port)
`$3
Called when the system is ready to accept commands.
$3
Receives plain MIDI data.
$3
Receives any ALSA MIDI Sequencer event.
Some of these are standard MIDI events:
NOTEON, NOTEOFF, KEYPRESS, CONTROLLER, … START, STOP, …
Others are ALSA MIDI specific events: CLIENT_START, CLIENT_EXIT, …The complete list is available in the source.
Packet Format
Multi-byte values are Big-Endian-encoded.
$3
-
SD_MSG_MARK (timestamp-only) is sent from the interface to provide real-time timestamp information (MIDI data length = 0).
- SD_MSG_MIDI (UDP→MIDI out) is sent to the interface to force the output of MIDI content onto the interface; MIDI data length = 0 is used to indicate end of stream, while any other value is parsed and sent out accordingly.
- SD_MSG_SEQ_EVENT (MIDI in→UDP) is sent from the interface to provide event notifications, based on the ALSA Sequencer specifications.$3
- offset 0: 1 byte version + time flags
- offset 1: 1 byte message type (
SD_MSG_*)
- offset 2: 8 bytes for timestamp
- offset 10: 2 bytes for MIDI data length = M (0 if no MIDI data is included)
- offset 12: M bytes for MIDI data$3
For
SD_MSG_SEQ_EVENT, the MIDI data might be present or absent (MIDI data length = 0) and the following fields are added:- offset 12+M: 1 byte for event type (
SND_SEQ_EVENT_* defined in ALSA-lib)
- offset 13+M: 1 byte for event flags
- offset 14+M: 1 byte for queue
- offset 15+M: 1 byte for source client address
- offset 16+M: 1 byte for source client port
- offset 17+M: 1 byte for destination client address
- offset 18+M: 1 byte for destination client port
- offset 19+M: 1 byte for event data type (SD_TYPE_*, indicates the type of the data event content)
- offset 20+M: 2 byte for event data length = N // Not sure why I'm doing this, wbuf can only store 128 bytes anyway
- offset 22+M: N bytes for event data (interpreted per SD_TYPE`)