Files
midi-sequencer/README.md
2026-04-25 04:31:11 +00:00

94 lines
3.7 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# midi-sequencer
A browser-based MIDI step sequencer with a C ALSA backend for accurate timing and a Node.js/Express web UI.
## Architecture
```
Browser (app.mjs)
↕ HTTP + SSE
Node server (server.mjs, port 3000)
↕ Unix socket (/tmp/midi-sequencer.sock)
C backend (midi-sequencer binary)
↕ ALSA sequencer API
MIDI output
```
- **`protocol.yaml`** — single source of truth for the binary protocol; drives codegen for both C and Node
- **`codegen/gen.mjs`** — generates `c-backend/generated/protocol.{h,c}` and `node-server/src/generated/protocol.mjs`
- **`c-backend/`** — C11, drift-free tick thread using `clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME)`, links libasound + libpthread
- **`node-server/`** — Express 5 web app, serves the UI and bridges the browser to the C backend
## Features
- **Multi-track playback** — up to 16 patterns playing simultaneously, each looping at its own independent length
- **Live track control** — add/remove patterns from the playing set without stopping; mute and solo per track
- **GM percussion editor** — channel 10 patterns show GM drum names in drum-machine layout with family colour coding
- **Auto-save** — step grid edits are sent to the backend immediately on each toggle
- **Preview notes** — per-row ▶ button auditions a note without affecting sequencer state
- **Click-to-rename** — note and percussion labels are editable inline
- **Sub-patterns** — patterns can reference other patterns as sub-sequences at any step
- **BPM** — tempo settable to 0.1 BPM resolution (stored as `bpm_x10` uint16, no floats in protocol)
## Protocol
Binary frames: `[record_type: u8][payload_length: u16le][payload…]`
| ID | Name | Direction | Description |
|---|---|---|---|
| 0x01 | HELLO | node→C | Version handshake |
| 0x02 | DEFINE_PATTERN | node→C | Create/redefine a pattern |
| 0x03 | CLEAR_PATTERN | node→C | Remove all notes from a pattern |
| 0x04 | ADD_NOTE | node→C | Add a note event at a step |
| 0x05 | ADD_SUB_PATTERN | node→C | Schedule a sub-pattern within a pattern |
| 0x06 | PLAY | node→C | Single-pattern play (backward compat) |
| 0x07 | STOP | node→C | Stop all playback |
| 0x08 | SET_TEMPO | node→C | Set BPM (as bpm×10) |
| 0x09 | PREVIEW_NOTE | node→C | Play a single note immediately |
| 0x0A | ADD_TRACK | node→C | Add a pattern to the multi-track set |
| 0x0B | REMOVE_TRACK | node→C | Remove a pattern from the multi-track set |
| 0x0C | PLAY_TRACKS | node→C | Start the multi-track engine |
| 0x0D | SET_TRACK_MUTE | node→C | Mute/unmute a track |
| 0x81 | ACK | C→node | Acknowledge a command |
| 0x82 | ERROR | C→node | Report an error |
| 0x83 | BEAT_TICK | C→node | Step notification (one per track per tick) |
| 0x84 | PATTERN_END | C→node | Pattern completed one full cycle |
## Build
Requires: GCC, make, libasound2-dev, Node.js ≥ 18
```sh
# Full build (codegen + npm install + C binary)
make
# C backend only
cd c-backend && make
# Run the Node server
cd node-server && make start
# → http://localhost:3000
```
The C backend runs separately and is started independently:
```sh
./c-backend/midi-sequencer [socket_path] [alsa_client_name]
# defaults: /tmp/midi-sequencer.sock midi-sequencer
```
## Usage
1. Start the C backend
2. Start the Node server (`make start` in `node-server/`)
3. Open `http://localhost:3000`
4. Create melodic or drum patterns with **+ Melodic** / **🥁 Drums**
5. Click the **●** dot next to each pattern to add it to the playback set
6. Press **▶ Play** to start — all active patterns loop independently
## Timing
One step = one 16th note. Step duration: `150,000,000,000 ns / bpm_x10`
At 120.0 BPM: 125 ms/step. The tick thread uses an absolute deadline so tempo drift does not accumulate.