93
README.md
Normal file
93
README.md
Normal file
@@ -0,0 +1,93 @@
|
||||
# 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.
|
||||
Reference in New Issue
Block a user