simple-dog

1.2.0 • Public • Published

Linux ALSA Sequencer UDP interface

Purpose

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.

Similar work

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).

Prerequisites: Debian, Ubuntu

apt install -y --no-recommends libasound2 libasound2-dev libuv1 libuv1-dev build-essential

Prerequisites: Alpine Linux

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_HOSTand DEST_PORT environment variables.

API

Either

import {AlsaMidiClient} from 'simple-dog'

or

const {AlsaMidiClient} = require('simple-dog')

new AlsaMidiClient

const midi = new AlsaMidiClient('MIDI')

createPort

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')

deletePort

await midi.deletePort(port)

sendMidi

await midi.sendMidi([0x91,0x40,velocity])

portConnect

const client_id = await midi.clientId()
await midi.portConnect(client_id,port, other_client_id,other_port)

portDisconnect

await midi.portDisconnect(client_id,port, other_client_id,other_port)

.on('ready', () => … )

Called when the system is ready to accept commands.

.on('midi', (midi_data) => … )

Receives plain MIDI data.

.on( event, (data) => … )

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.

Message types

  • 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.

Common fields

  • 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

Additional fields

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)

Readme

Keywords

Package Sidebar

Install

npm i simple-dog

Weekly Downloads

4

Version

1.2.0

License

MIT

Unpacked Size

87.6 kB

Total Files

18

Last publish

Collaborators

  • shimaore