musalce-server 0.4.10 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/notify-plugin.yml +17 -0
- data/.gitignore +3 -0
- data/.version +6 -0
- data/.yardopts +6 -0
- data/README.md +317 -9
- data/docs/architecture.md +242 -0
- data/lib/bitwig/bitwig.rb +32 -0
- data/lib/bitwig/controllers.rb +93 -0
- data/lib/bitwig/handler.rb +30 -0
- data/lib/bitwig/tracks.rb +46 -0
- data/lib/daw.rb +193 -4
- data/lib/live/handler.rb +14 -0
- data/lib/live/live.rb +36 -0
- data/lib/live/tracks.rb +67 -2
- data/lib/midi-devices.rb +58 -1
- data/lib/musalce-server.rb +34 -2
- data/lib/surface-bridge.rb +171 -0
- data/lib/surface.rb +369 -0
- data/lib/version.rb +15 -0
- data/musalce-server.gemspec +26 -17
- metadata +82 -49
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 2230cb912aab835e98533a65b0a8b0ae44c5b815bce0555ecfe1433807048502
|
|
4
|
+
data.tar.gz: 4b3323bdc75f6a1e54f9ec75a42ccb649aaa82ed20fb150bf84258c098e9b908
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: cfbc543f1927cf246604d5d0c25390fbba0f932a81c43232e5ae31e6020e06dc5995b8bd5eec44f251f76e40a22f8eccc13c6fadc1c1d5bcbeed6373d4ea80de
|
|
7
|
+
data.tar.gz: 0bacccdb81eaea73d307b3667871ed4a092e791ba5822c10870ac5bf56ecc3ec3e815946634c5e9035f2ff658745fb1f60f21cd652c1df5b14d2928e35b37c08
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
name: Notify Plugin Rebuild
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [master]
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
notify:
|
|
9
|
+
runs-on: ubuntu-latest
|
|
10
|
+
steps:
|
|
11
|
+
- name: Trigger nota-plugin-for-claude rebuild
|
|
12
|
+
uses: peter-evans/repository-dispatch@v3
|
|
13
|
+
with:
|
|
14
|
+
token: ${{ secrets.PLUGIN_REBUILD_PAT }}
|
|
15
|
+
repository: javier-sy/nota-plugin-for-claude
|
|
16
|
+
event-type: source-updated
|
|
17
|
+
client-payload: '{"repo": "${{ github.repository }}", "sha": "${{ github.sha }}"}'
|
data/.gitignore
CHANGED
data/.version
ADDED
data/.yardopts
ADDED
data/README.md
CHANGED
|
@@ -1,17 +1,325 @@
|
|
|
1
|
-
#
|
|
1
|
+
# MusaLCE Server
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.ruby-lang.org/)
|
|
4
|
+
[](https://www.gnu.org/licenses/gpl-3.0.html)
|
|
4
5
|
|
|
5
|
-
**
|
|
6
|
+
**Musa-DSL Live Coding Environment Server for Ableton Live 11+ and Bitwig Studio 5+**
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
**TODO**
|
|
8
|
+
This server enables live coding music composition using [Musa-DSL](https://github.com/javier-sy/musa-dsl) with your favorite DAW and code editor.
|
|
9
9
|
|
|
10
|
-
##
|
|
11
|
-
|
|
10
|
+
## Overview
|
|
11
|
+
|
|
12
|
+
The MusaLCE system allows you to write Ruby code in your editor (Visual Studio Code recommended) and have it executed in real-time, sending MIDI to tracks in your DAW. The typical workflow is:
|
|
13
|
+
|
|
14
|
+
1. Start the MusaLCE Server with your DAW choice
|
|
15
|
+
2. Open your code editor with the MusaLCE extension
|
|
16
|
+
3. Write and execute Musa-DSL code interactively
|
|
17
|
+
4. The code controls MIDI instruments in your DAW
|
|
18
|
+
|
|
19
|
+
## Requirements
|
|
20
|
+
|
|
21
|
+
- Ruby 2.7+
|
|
22
|
+
- [Musa-DSL](https://github.com/javier-sy/musa-dsl) (installed automatically as dependency)
|
|
23
|
+
- A supported DAW with its controller extension:
|
|
24
|
+
- **Bitwig Studio 5+** with [MusaLCE for Bitwig](https://github.com/javier-sy/MusaLCEforBitwig) Controller Extension
|
|
25
|
+
- **Ableton Live 11+** with [MusaLCE for Live](https://github.com/javier-sy/MusaLCEforLive) MIDI Remote Script
|
|
26
|
+
- A code editor with MusaLCE client extension:
|
|
27
|
+
- **Visual Studio Code** (recommended) with [MusaLCE Client for VSCode](https://github.com/javier-sy/MusaLCEClientForVSCode)
|
|
28
|
+
- Atom with [MusaLCE Client for Atom](https://github.com/javier-sy/MusaLCEClientForAtom) (Atom is discontinued)
|
|
29
|
+
|
|
30
|
+
## Installation
|
|
31
|
+
|
|
32
|
+
If you're using Bundler, add this line to your application's Gemfile:
|
|
33
|
+
|
|
34
|
+
```ruby
|
|
35
|
+
gem 'musalce-server'
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Otherwise:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
gem install musalce-server
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Quick Start
|
|
45
|
+
|
|
46
|
+
1. **Install the DAW controller extension** for your DAW (Bitwig or Live)
|
|
47
|
+
2. **Install the VSCode extension** [MusaLCE Client for VSCode](https://github.com/javier-sy/MusaLCEClientForVSCode)
|
|
48
|
+
3. **Start your DAW** and ensure the MusaLCE controller is loaded
|
|
49
|
+
4. **Start the server** (see below)
|
|
50
|
+
5. **Open VSCode** and create a `.rb` file
|
|
51
|
+
6. **Execute code** using the MusaLCE extension commands
|
|
52
|
+
|
|
53
|
+
## Starting the Server
|
|
54
|
+
|
|
55
|
+
The `musalce-server` command starts the live coding server:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
musalce-server <daw>
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Where `<daw>` is one of:
|
|
62
|
+
- `bitwig` - for Bitwig Studio
|
|
63
|
+
- `live` - for Ableton Live
|
|
64
|
+
|
|
65
|
+
Examples:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
musalce-server bitwig # Start server for Bitwig Studio
|
|
69
|
+
musalce-server live # Start server for Ableton Live
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
The server runs in the foreground and logs activity to the console. Use `Ctrl+C` or the `shutdown` command from the editor to stop it.
|
|
73
|
+
|
|
74
|
+
## Running Environment
|
|
75
|
+
|
|
76
|
+
A complete MusaLCE live coding session requires **three components running simultaneously**:
|
|
77
|
+
|
|
78
|
+
1. **Code Editor** (Visual Studio Code with MusaLCE extension)
|
|
79
|
+
- Where you write and execute Ruby/Musa-DSL code
|
|
80
|
+
- Connects to musalce-server via TCP port 1327
|
|
81
|
+
|
|
82
|
+
2. **MusaLCE Server** (`musalce-server` command)
|
|
83
|
+
- Receives code from the editor and executes it
|
|
84
|
+
- Communicates with the DAW via OSC
|
|
85
|
+
- Manages MIDI routing and the Musa-DSL sequencer
|
|
86
|
+
|
|
87
|
+
3. **DAW** (Bitwig Studio or Ableton Live)
|
|
88
|
+
- With the corresponding MusaLCE controller extension loaded
|
|
89
|
+
- Receives transport and sync commands from the server
|
|
90
|
+
- Routes MIDI from the server to instruments/tracks
|
|
91
|
+
|
|
92
|
+
## Architecture
|
|
93
|
+
|
|
94
|
+
The MusaLCE system connects three components:
|
|
95
|
+
|
|
96
|
+
```
|
|
97
|
+
┌─────────────────────┐ TCP ┌─────────────────────┐
|
|
98
|
+
│ Code Editor │◄──────────────────►│ MusaLCE Server │
|
|
99
|
+
│ (VSCode + Plugin) │ port 1327 │ (Ruby + REPL) │
|
|
100
|
+
└─────────────────────┘ └─────────────────────┘
|
|
101
|
+
│ ▲
|
|
102
|
+
OSC │ │ OSC
|
|
103
|
+
port 10001 │ │ port 11011
|
|
104
|
+
▼ │
|
|
105
|
+
┌─────────────────────┐
|
|
106
|
+
│ DAW Controller │
|
|
107
|
+
│ Extension │
|
|
108
|
+
│ (Bitwig or Live) │
|
|
109
|
+
└─────────────────────┘
|
|
110
|
+
│
|
|
111
|
+
│ MIDI
|
|
112
|
+
▼
|
|
113
|
+
┌─────────────────────┐
|
|
114
|
+
│ DAW Tracks & │
|
|
115
|
+
│ Instruments │
|
|
116
|
+
└─────────────────────┘
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### Communication Ports
|
|
120
|
+
|
|
121
|
+
| Port | Protocol | Direction | Purpose |
|
|
122
|
+
|------|----------|-----------|---------|
|
|
123
|
+
| 1327 | TCP | Editor ↔ Server | REPL code execution |
|
|
124
|
+
| 10001 | OSC/UDP | Server → DAW | Transport commands, sync requests |
|
|
125
|
+
| 11011 | OSC/UDP | DAW → Server | Track info, controller registration |
|
|
126
|
+
|
|
127
|
+
## REPL Commands Reference
|
|
128
|
+
|
|
129
|
+
The following commands are available in the REPL context (executed from your editor):
|
|
130
|
+
|
|
131
|
+
### DAW Access
|
|
132
|
+
|
|
133
|
+
```ruby
|
|
134
|
+
daw # Access the DAW controller object
|
|
135
|
+
daw.sequencer # Access the Musa-DSL sequencer
|
|
136
|
+
daw.clock # Access the MIDI clock
|
|
137
|
+
daw.transport # Access the transport (callbacks survive Stop/Play)
|
|
138
|
+
daw.tracks # Access all tracks
|
|
139
|
+
daw.surface # Access the control surface (Stream Deck, etc.)
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
#### Persistent actions across DAW Stop/Play
|
|
143
|
+
|
|
144
|
+
Every DAW Stop wipes the sequencer (`at`, `every`, `play`, `on :event`
|
|
145
|
+
handlers are all cleared by the built-in `transport.after_stop { sequencer.reset }`).
|
|
146
|
+
Top-level Ruby state (methods, modules, constants) survives, but
|
|
147
|
+
anything you scheduled or subscribed to via the sequencer DSL does
|
|
148
|
+
not. To re-install those on every Play, register an `on_start`
|
|
149
|
+
callback on the transport:
|
|
150
|
+
|
|
151
|
+
```ruby
|
|
152
|
+
daw.transport.on_start do
|
|
153
|
+
load 'persistent_actions.rb' # rehydrate on :event, every, at, …
|
|
154
|
+
end
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
`on_start` callbacks accumulate (they're an append-only list), so you
|
|
158
|
+
can register more from the REPL at any time. They run on every Start,
|
|
159
|
+
after the built-in `before_begin`. Use `before_begin` instead if you
|
|
160
|
+
want a callback that runs **only on the very first Start** of the
|
|
161
|
+
session.
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
### Track Operations
|
|
165
|
+
|
|
166
|
+
```ruby
|
|
167
|
+
# Get a track by name
|
|
168
|
+
bass = daw.track('Bass')
|
|
169
|
+
|
|
170
|
+
# Get all tracks with a name (Live only, can have duplicates)
|
|
171
|
+
drums = daw.track('Drums', all: true)
|
|
172
|
+
|
|
173
|
+
# Send MIDI to a track
|
|
174
|
+
bass.out.note(60, velocity: 100, duration: 1)
|
|
175
|
+
bass.out.note_on(60, 100)
|
|
176
|
+
bass.out.note_off(60)
|
|
177
|
+
bass.out.control_change(1, 64)
|
|
178
|
+
bass.out.program_change(5)
|
|
179
|
+
bass.out.all_notes_off
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### Transport Controls
|
|
183
|
+
|
|
184
|
+
Transport controls send commands to the DAW. **Only available for Bitwig Studio** (Live's MIDI Remote Script API doesn't support transport control).
|
|
185
|
+
|
|
186
|
+
```ruby
|
|
187
|
+
daw.play # Start playback
|
|
188
|
+
daw.stop # Stop playback
|
|
189
|
+
daw.continue # Continue from current position
|
|
190
|
+
daw.goto(5) # Go to bar 5
|
|
191
|
+
daw.record # Start recording
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### Sequencer (Musa-DSL)
|
|
195
|
+
|
|
196
|
+
All [Musa-DSL](https://github.com/javier-sy/musa-dsl) sequencer methods are available:
|
|
197
|
+
|
|
198
|
+
```ruby
|
|
199
|
+
# Schedule events at specific positions
|
|
200
|
+
at 1 do
|
|
201
|
+
bass.out.note(48, velocity: 80, duration: 0.5)
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
# Wait relative to current position
|
|
205
|
+
wait 2 do
|
|
206
|
+
bass.out.note(52, velocity: 80, duration: 0.5)
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
# Schedule repeating patterns (returns EveryControl)
|
|
210
|
+
pattern = every 4 do
|
|
211
|
+
drums.out.note(36, velocity: 100, duration: 0.25)
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
# Play a series (returns PlayControl)
|
|
215
|
+
serie = S(60, 62, 64, 65, 67)
|
|
216
|
+
melody = play serie do |note|
|
|
217
|
+
bass.out.note(note, velocity: 80, duration: 1)
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
# Animate values over time (returns MoveControl)
|
|
221
|
+
sweep = move from: 0, to: 127, duration: 4 do |value|
|
|
222
|
+
bass.out.control_change(1, value.to_i)
|
|
223
|
+
end
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
### Controlling Playback
|
|
227
|
+
|
|
228
|
+
The `play`, `every`, and `move` methods return control objects that allow you to stop, pause, and monitor playback:
|
|
229
|
+
|
|
230
|
+
```ruby
|
|
231
|
+
# Stop a pattern or playback
|
|
232
|
+
pattern.stop
|
|
233
|
+
melody.stop
|
|
234
|
+
|
|
235
|
+
# Check status
|
|
236
|
+
pattern.stopped? # true if stopped
|
|
237
|
+
melody.paused? # true if paused
|
|
238
|
+
|
|
239
|
+
# Pause and continue (for play)
|
|
240
|
+
melody.pause
|
|
241
|
+
melody.continue
|
|
242
|
+
|
|
243
|
+
# Callback when stopped
|
|
244
|
+
pattern.on_stop do
|
|
245
|
+
puts "Pattern stopped"
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
# Callback after play completes
|
|
249
|
+
melody.after(2) do
|
|
250
|
+
puts "2 bars after melody finished"
|
|
251
|
+
end
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
### Utility Commands
|
|
255
|
+
|
|
256
|
+
```ruby
|
|
257
|
+
reload # Reload DAW controller extension
|
|
258
|
+
daw.sync # Re-synchronize track information
|
|
259
|
+
daw.panic! # Send All Notes Off to all tracks
|
|
260
|
+
shutdown # Stop the server
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
### Module Import
|
|
264
|
+
|
|
265
|
+
```ruby
|
|
266
|
+
# Import additional modules into the REPL context
|
|
267
|
+
import(MyHelperModule)
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
### File Require
|
|
271
|
+
|
|
272
|
+
```ruby
|
|
273
|
+
# Require files relative to your editor's current file
|
|
274
|
+
require_relative 'my_patterns'
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
## DAW-Specific Notes
|
|
278
|
+
|
|
279
|
+
### Bitwig Studio
|
|
280
|
+
|
|
281
|
+
Requires [MusaLCE for Bitwig](https://github.com/javier-sy/MusaLCEforBitwig) controller extension.
|
|
282
|
+
|
|
283
|
+
- Full transport control support (play, stop, continue, goto, record)
|
|
284
|
+
- Track names must be unique
|
|
285
|
+
- MIDI clock sync from any controller marked as clock source
|
|
286
|
+
- Controllers and channels configured in the Bitwig extension
|
|
287
|
+
|
|
288
|
+
### Ableton Live
|
|
289
|
+
|
|
290
|
+
Requires [MusaLCE for Live](https://github.com/javier-sy/MusaLCEforLive) MIDI Remote Script.
|
|
291
|
+
|
|
292
|
+
- **No transport control** (Live's MIDI Remote Script API limitation)
|
|
293
|
+
- Multiple tracks can have the same name
|
|
294
|
+
- Use `daw.midi_sync('Device Name')` to set MIDI clock source
|
|
295
|
+
- Track routing configured in Live's preferences
|
|
296
|
+
|
|
297
|
+
```ruby
|
|
298
|
+
# Set MIDI clock source for Live
|
|
299
|
+
daw.midi_sync('IAC Driver Bus 1')
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
## Related Projects
|
|
303
|
+
|
|
304
|
+
| Component | Description |
|
|
305
|
+
| --- | --- |
|
|
306
|
+
| [Musa-DSL](https://github.com/javier-sy/musa-dsl) | Core music composition DSL |
|
|
307
|
+
| [MusaLCE Server](https://github.com/javier-sy/musalce-server) | Live coding server (this gem) |
|
|
308
|
+
| [MusaLCE for Bitwig](https://github.com/javier-sy/MusaLCEforBitwig) | Bitwig Studio controller extension |
|
|
309
|
+
| [MusaLCE for Live](https://github.com/javier-sy/MusaLCEforLive) | Ableton Live MIDI Remote Script |
|
|
310
|
+
| [MusaLCE Client for VSCode](https://github.com/javier-sy/MusaLCEClientForVSCode) | VSCode extension (recommended) |
|
|
311
|
+
| [MusaLCE Client for Atom](https://github.com/javier-sy/MusaLCEClientForAtom) | Atom plugin (discontinued) |
|
|
12
312
|
|
|
13
313
|
## Documentation
|
|
14
|
-
|
|
314
|
+
|
|
315
|
+
API documentation is available via YARD:
|
|
316
|
+
|
|
317
|
+
```bash
|
|
318
|
+
bundle exec yard doc
|
|
319
|
+
bundle exec yard server
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
Then open http://localhost:8808 in your browser.
|
|
15
323
|
|
|
16
324
|
## Author
|
|
17
325
|
|
|
@@ -19,4 +327,4 @@ Musa-DSL Live Coding Environment for Ableton Live and Bitwig Studio (Server part
|
|
|
19
327
|
|
|
20
328
|
## License
|
|
21
329
|
|
|
22
|
-
[MusaLCE
|
|
330
|
+
[MusaLCE Server](https://github.com/javier-sy/musalce-server) Copyright (c) 2021-2026 [Javier Sánchez Yeste](https://yeste.studio), licensed under GPL 3.0 License
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
# MusaLCE Suite Architecture
|
|
2
|
+
|
|
3
|
+
This document is the canonical reference for the **suite workflow** of MusaLCE — running [musalce-server](https://github.com/javier-sy/musalce-server) together with the per-DAW extension to drive Bitwig Studio or Ableton Live from a code editor in real time, optionally with Stream Deck integration through [Pulso](https://github.com/javier-sy/pulso).
|
|
4
|
+
|
|
5
|
+
It is a companion to (not a replacement for) the lower-level [musa-dsl REPL subsystem doc](https://github.com/javier-sy/musa-dsl/blob/master/docs/subsystems/repl.md), which covers the **standalone REPL** workflow (case 1). The suite documented here is **internally a specialization** of that case — `musalce-server` opens `Musa::REPL::REPL.new(binding)` after pre-building the sequencer, clock, transport, DAW handler and surface, so you don't have to.
|
|
6
|
+
|
|
7
|
+
## When to use this (vs the standalone REPL)
|
|
8
|
+
|
|
9
|
+
| Use this (suite) | Use the standalone REPL |
|
|
10
|
+
|---|---|
|
|
11
|
+
| Target is Bitwig Studio or Ableton Live | Target is SuperCollider, Max/MSP, OSC apps, custom hardware |
|
|
12
|
+
| You want `daw.*`, `surface[:event]` and DAW transport out of the box | You want full control over the wiring |
|
|
13
|
+
| You want Stream Deck integration via Pulso (Bitwig only today) | You're prototyping a personal live-coding DSL |
|
|
14
|
+
| Worked example: `_demo-13b-live-coding-suite` (planned) | Worked example: [`_demo-13-live-coding`](https://github.com/javier-sy/musadsl-demo) |
|
|
15
|
+
|
|
16
|
+
Both workflows use the same [MusaLCEClientForVSCode](https://github.com/javier-sy/MusaLCEClientForVSCode) extension; the extension does not know or care which server is on the other end of TCP/1327.
|
|
17
|
+
|
|
18
|
+
## The big picture
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
┌────────────────────────────┐
|
|
22
|
+
│ Stream Deck plugin │
|
|
23
|
+
│ (Pulso Workflow .sdPlugin) │
|
|
24
|
+
└─────────────┬──────────────┘
|
|
25
|
+
│ OSC over UDP (Pulso wire)
|
|
26
|
+
▼
|
|
27
|
+
┌────────────────────┐ TCP 1327 ┌──────────────────┐ ┌─────────────────────────┐ ┌──────────────────┐
|
|
28
|
+
│ VSCode + │ ◀──────────▶ │ musalce-server │ ─────▶ │ MusaLCEforBitwig │ ◀────▶ │ Bitwig Studio │
|
|
29
|
+
│ MusaLCEClient │ (REPL) │ (Ruby gem) │ │ + Pulso Bridge relay │ └──────────────────┘
|
|
30
|
+
│ ForVSCode │ │ │ UDP └─────────────────────────┘
|
|
31
|
+
└────────────────────┘ │ • REPL │ OSC OR
|
|
32
|
+
│ • Sequencer │
|
|
33
|
+
│ • DAW handler │ ─────▶ ┌─────────────────────────┐ ┌──────────────────┐
|
|
34
|
+
│ • Surface │ │ MusaLCEforLive │ ◀────▶ │ Ableton Live │
|
|
35
|
+
│ • MIDI out │ │ (Python) │ └──────────────────┘
|
|
36
|
+
└──────────────────┘ └─────────────────────────┘
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Two parallel OSC contracts cross the server ↔ extension boundary:
|
|
40
|
+
|
|
41
|
+
- **Handler protocol** — `/musalce4bitwig/*` or `/musalce4live/*` plus a common `/hello`, `/version`, `/reload`. Carries DAW control (transport, track sync, channels). Documented [below](#osc-handler-protocol).
|
|
42
|
+
- **Surface protocol** — `/musalce/surface/*`. Carries Stream Deck control state (inventory, triggers, state propagation). [Single source of truth in pulso/docs/osc-protocol.md](https://github.com/javier-sy/pulso/blob/main/docs/osc-protocol.md). Bitwig only today; Live side not implemented.
|
|
43
|
+
|
|
44
|
+
## Component responsibilities
|
|
45
|
+
|
|
46
|
+
| Component | Repo | Role | Language |
|
|
47
|
+
|---|---|---|---|
|
|
48
|
+
| **musa-dsl** | [musa-dsl](https://github.com/javier-sy/musa-dsl) | The composition framework: series, sequencer, neumas, transport, REPL primitive. | Ruby |
|
|
49
|
+
| **musalce-server** | [musalce-server](https://github.com/javier-sy/musalce-server) | Packages the REPL + sequencer + per-DAW handler + surface into a single command (`musalce-server bitwig\|live`). | Ruby |
|
|
50
|
+
| **MusaLCEforBitwig** | [MusaLCEforBitwig](https://github.com/javier-sy/MusaLCEforBitwig) | Bitwig controller extension; bridges Bitwig and musalce-server over OSC, includes the `MusaLCESurfaceRelay` for Pulso. | Java (Bitwig Extension API 18) |
|
|
51
|
+
| **MusaLCEforLive** | [MusaLCEforLive](https://github.com/javier-sy/MusaLCEforLive) | Ableton Live MIDI Remote Script; bridges Live and musalce-server over OSC. Inherits `/live/*` from AbletonOSC. | Python |
|
|
52
|
+
| **MusaLCEClientForVSCode** | [MusaLCEClientForVSCode](https://github.com/javier-sy/MusaLCEClientForVSCode) | VSCode extension that is a REPL client over TCP/1327. | TypeScript |
|
|
53
|
+
| **Pulso Bridge** *(optional)* | [pulso](https://github.com/javier-sy/pulso) | Bitwig controller that bridges between MusaLCEforBitwig (Surface relay side) and the Stream Deck plugin. | Java |
|
|
54
|
+
|
|
55
|
+
## REPL DSL surface (`daw.*`)
|
|
56
|
+
|
|
57
|
+
The DSL context exposed in the REPL of the suite workflow extends what's available in the standalone REPL with a `daw` accessor. Quick reference (full reference: [musalce-server README → REPL Commands Reference](https://github.com/javier-sy/musalce-server#readme)):
|
|
58
|
+
|
|
59
|
+
| Accessor | Returns | What it's for |
|
|
60
|
+
|---|---|---|
|
|
61
|
+
| `daw` | `Daw` (Bitwig or Live subclass) | Root entry point |
|
|
62
|
+
| `daw.sequencer` | `Musa::Sequencer::Sequencer` | The sequencer, also the implicit DSL host |
|
|
63
|
+
| `daw.clock` | `Musa::Clock::InputMidiClock` | The MIDI clock |
|
|
64
|
+
| `daw.transport` | `Musa::Transport::Transport` | The transport. Exposes `on_start`/`after_stop`/`before_begin` for callbacks that survive Stop/Play (since v0.7.2). |
|
|
65
|
+
| `daw.tracks` | DAW-specific | Track collection (`daw.track('Name')`) |
|
|
66
|
+
| `daw.surface` | `Surface` | Stream Deck / hardware surface object — `surface[:event]` (see below) |
|
|
67
|
+
| `daw.play`, `daw.stop`, `daw.continue`, `daw.goto(bar)`, `daw.record` | — | Transport remote control. **Bitwig only** (Live API limitation). |
|
|
68
|
+
| `daw.panic!` | — | All-notes-off to every track |
|
|
69
|
+
|
|
70
|
+
## `surface[:event]` DSL — Stream Deck integration
|
|
71
|
+
|
|
72
|
+
A *Surface* control is named by an **event Symbol** that doubles as the identifier the sequencer uses when dispatching from a physical control. The surface is registered by the Pulso Bridge (via the inventory protocol — see [pulso/docs/osc-protocol.md](https://github.com/javier-sy/pulso/blob/main/docs/osc-protocol.md)); the server **owns the state** (message, enabled, value, range) and propagates changes outbound.
|
|
73
|
+
|
|
74
|
+
There are three control types: **Toggle**, **Trigger** and **Encoder**.
|
|
75
|
+
|
|
76
|
+
### Trigger — momentary
|
|
77
|
+
|
|
78
|
+
```ruby
|
|
79
|
+
# In score code:
|
|
80
|
+
surface[:launch_chorus].set(message: "Chorus")
|
|
81
|
+
|
|
82
|
+
on :launch_chorus do |payload|
|
|
83
|
+
puts "Stream Deck button fired (#{payload.inspect})"
|
|
84
|
+
# ... schedule the chorus section ...
|
|
85
|
+
end
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
A Trigger has no persistent state beyond an optional `message`. Pressing the Stream Deck button reaches the `on :launch_chorus do |payload| … end` handler through the sequencer's event dispatch.
|
|
89
|
+
|
|
90
|
+
### Toggle — three-state (Action / Ready / Idle)
|
|
91
|
+
|
|
92
|
+
```ruby
|
|
93
|
+
surface[:mode].set(message: "Verse", enabled: true)
|
|
94
|
+
|
|
95
|
+
on :mode do |payload|
|
|
96
|
+
if surface[:mode].enabled
|
|
97
|
+
surface[:mode].enabled = false
|
|
98
|
+
# ... toggle off behaviour ...
|
|
99
|
+
else
|
|
100
|
+
surface[:mode].enabled = true
|
|
101
|
+
# ... toggle on behaviour ...
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
`enabled` accepts `true` / `false` / `:inactive` — Pulso paints the three states with distinct palettes (Action / Ready / Idle).
|
|
107
|
+
|
|
108
|
+
### Encoder — integer with range
|
|
109
|
+
|
|
110
|
+
```ruby
|
|
111
|
+
surface[:tempo].set(value: 120, range: [60, 200], message: "BPM")
|
|
112
|
+
|
|
113
|
+
on :tempo do |payload|
|
|
114
|
+
new_value = payload[:value].to_i
|
|
115
|
+
surface[:tempo].value = new_value
|
|
116
|
+
# ... apply tempo change ...
|
|
117
|
+
end
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
The encoder owns its `value` (an integer) and its `range` (min, max integers). State propagates back to the Stream Deck plugin so the key/dial repaints.
|
|
121
|
+
|
|
122
|
+
## Stop/Play semantics
|
|
123
|
+
|
|
124
|
+
The server registers exactly one built-in callback on the transport:
|
|
125
|
+
|
|
126
|
+
```ruby
|
|
127
|
+
transport.after_stop { sequencer.reset }
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
`sequencer.reset` (`musa-dsl/sequencer/base-sequencer.rb`) wipes:
|
|
131
|
+
|
|
132
|
+
- `@timeslots` — all `at`-scheduled events
|
|
133
|
+
- `@everying` — all `every` loops
|
|
134
|
+
- `@playing` — all `play` operations
|
|
135
|
+
- `@moving` — all `move` operations
|
|
136
|
+
- `@event_handlers` — **all `on :event` handlers**
|
|
137
|
+
|
|
138
|
+
What survives a Stop/Play cycle:
|
|
139
|
+
|
|
140
|
+
| | Survives Stop? |
|
|
141
|
+
|---|---|
|
|
142
|
+
| Top-level Ruby (`def`, constants, modules) | ✅ (Ruby process is alive) |
|
|
143
|
+
| `daw.*` (sequencer, transport, tracks, surface) | ✅ (instance state of `Daw`) |
|
|
144
|
+
| `surface[:event]` **control declarations** | ✅ (live on `@surface`) |
|
|
145
|
+
| `surface[:event]` **handler blocks** (`on :event do … end`) | ❌ (wiped with `@event_handlers`) |
|
|
146
|
+
| `at`, `every`, `play`, `move` | ❌ |
|
|
147
|
+
|
|
148
|
+
**Asymmetry warning**: the Stream Deck button keeps painting after a Stop, but pressing it dispatches to a handler that is no longer registered — silence.
|
|
149
|
+
|
|
150
|
+
### Rehydration pattern
|
|
151
|
+
|
|
152
|
+
Use `daw.transport.on_start` (exposed since v0.7.2 — see [musalce-server commit `fb8480f`](https://github.com/javier-sy/musalce-server/commit/fb8480f)) to re-install handlers and schedules on every Play:
|
|
153
|
+
|
|
154
|
+
```ruby
|
|
155
|
+
daw.transport.on_start do
|
|
156
|
+
load 'persistent_actions.rb' # re-establishes on :event, every, at, …
|
|
157
|
+
end
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
`on_start` callbacks accumulate (append-only list), so you can register more from the REPL at any time. Use `before_begin` for callbacks that should run **only on the first Start of the session**, and `after_stop` for cleanup (e.g. `voices.panic`).
|
|
161
|
+
|
|
162
|
+
## OSC handler protocol
|
|
163
|
+
|
|
164
|
+
Two unidirectional channels, both UDP:
|
|
165
|
+
|
|
166
|
+
- **server listens** on `127.0.0.1:11011` (extension → server)
|
|
167
|
+
- **server sends** to `127.0.0.1:10001` (server → extension)
|
|
168
|
+
|
|
169
|
+
Both ports are **hardcoded** on the server side (`musalce-server/lib/daw.rb`). The DAW extensions match these defaults.
|
|
170
|
+
|
|
171
|
+
### Common addresses (both DAWs)
|
|
172
|
+
|
|
173
|
+
| Direction | Address | Args | Purpose |
|
|
174
|
+
|---|---|---|---|
|
|
175
|
+
| ext → server | `/hello` | — | Extension announces itself on init. Server replies with `/version` + a per-DAW sync request. |
|
|
176
|
+
| server → ext | `/version` | `s` (VERSION) | Server announces its gem version. Extension can refuse to talk to incompatible versions. |
|
|
177
|
+
| server → ext | `/reload` | — | Asks the extension to reset and re-sync (used by `reload` REPL command). |
|
|
178
|
+
|
|
179
|
+
### Bitwig-specific (`/musalce4bitwig/*`)
|
|
180
|
+
|
|
181
|
+
| Direction | Address | Args | Purpose |
|
|
182
|
+
|---|---|---|---|
|
|
183
|
+
| server → ext | `/musalce4bitwig/sync` | — | Ask the extension to re-emit controllers + channels. |
|
|
184
|
+
| server → ext | `/musalce4bitwig/play` | — | Bitwig transport play. |
|
|
185
|
+
| server → ext | `/musalce4bitwig/stop` | — | Bitwig transport stop. |
|
|
186
|
+
| server → ext | `/musalce4bitwig/continue` | — | Bitwig transport continue. |
|
|
187
|
+
| server → ext | `/musalce4bitwig/goto` | `d` (position in beats from bar 1) | Move playhead. |
|
|
188
|
+
| server → ext | `/musalce4bitwig/record` | — | Toggle record. |
|
|
189
|
+
| ext → server | `/musalce4bitwig/controllers` | `s s s …` (names) | Register all controllers in Bitwig. |
|
|
190
|
+
| ext → server | `/musalce4bitwig/controller` | `s s i` (name, port_name, is_clock 0/1) | Register a single controller. |
|
|
191
|
+
| ext → server | `/musalce4bitwig/controller/update` | `s s s i` (old_name, new_name, port_name, is_clock) | Rename / update a controller. |
|
|
192
|
+
| ext → server | `/musalce4bitwig/channels` | `s i i …` (controller_name, channels…) | Register channels for a controller. |
|
|
193
|
+
|
|
194
|
+
### Live-specific (`/musalce4live/*`)
|
|
195
|
+
|
|
196
|
+
| Direction | Address | Args | Purpose |
|
|
197
|
+
|---|---|---|---|
|
|
198
|
+
| server → ext | `/musalce4live/tracks` | — | Ask the script to re-emit the track registry. |
|
|
199
|
+
| ext → server | `/musalce4live/tracks` | bulk (sliced 10) | Bulk track registry dump. |
|
|
200
|
+
| ext → server | `/musalce4live/track/name` | bulk (sliced 2) | Track names. |
|
|
201
|
+
| ext → server | `/musalce4live/track/midi` | bulk (sliced 3) | MIDI track metadata. |
|
|
202
|
+
| ext → server | `/musalce4live/track/audio` | bulk (sliced 3) | Audio track metadata. |
|
|
203
|
+
| ext → server | `/musalce4live/track/routings` | bulk (sliced 5) | Routing metadata. |
|
|
204
|
+
|
|
205
|
+
### `/live/*` — inherited from AbletonOSC (Live only)
|
|
206
|
+
|
|
207
|
+
MusaLCEforLive inherits the full `/live/*` surface from [ideoforms/AbletonOSC](https://github.com/ideoforms/AbletonOSC) — dozens of endpoints covering volume, pan, send, clip control, devices, etc. These are not MusaLCE-specific and are not documented here; see the upstream README for the full address list.
|
|
208
|
+
|
|
209
|
+
## Surface protocol — Stream Deck via Pulso (Bitwig only)
|
|
210
|
+
|
|
211
|
+
The protocol carrying surface inventory, triggers and state between musalce-server, MusaLCEforBitwig (`MusaLCESurfaceRelay`) and the Pulso Bridge is documented end-to-end in [pulso/docs/osc-protocol.md → MusaLCE surface relay](https://github.com/javier-sy/pulso/blob/main/docs/osc-protocol.md#musalce-surface-relay).
|
|
212
|
+
|
|
213
|
+
Quick summary of address space:
|
|
214
|
+
|
|
215
|
+
- `/musalce/surface/inventory/{begin,add,remove,end}` — surface inventory (Pulso → server)
|
|
216
|
+
- `/musalce/surface/trigger event payload` — Pulso → server, dispatched to `on :event` via `@sequencer.launch`
|
|
217
|
+
- `/musalce/surface/state/{message,enabled,value,range} event …` — server → Pulso, repaints the Stream Deck
|
|
218
|
+
- `/musalce/surface/sync_request`, `/musalce/surface/state_request` — handshake messages
|
|
219
|
+
|
|
220
|
+
In the suite the relay is implemented on the Bitwig side only (`MusaLCEforBitwig/.../MusaLCESurfaceRelay.java`). **There is no Live-side relay yet** — Stream Deck integration is Bitwig-only at the time of writing.
|
|
221
|
+
|
|
222
|
+
## Current versions
|
|
223
|
+
|
|
224
|
+
| Component | Version |
|
|
225
|
+
|---|---|
|
|
226
|
+
| musa-dsl | 0.42.7+ |
|
|
227
|
+
| musalce-server | 0.7.2 |
|
|
228
|
+
| MusaLCEforBitwig | from `git describe` (typically `0.x`) |
|
|
229
|
+
| MusaLCEforLive | (no semver published) |
|
|
230
|
+
| MusaLCEClientForVSCode | 0.1.0 |
|
|
231
|
+
| Pulso Bridge | 0.x (see pulso/docs/osc-protocol.md changelog) |
|
|
232
|
+
| Pulso Workflow (Stream Deck plugin) | matches Bridge |
|
|
233
|
+
|
|
234
|
+
Stay on the same release branch across components — pin musalce-server in your `Gemfile.lock`, build the matching `.bwextension`/Remote Script from the same point in time. There's no semantic compatibility matrix today; protocol changes are coordinated across components by commit (see e.g. the simultaneous `id → event` rename in musalce-server `346fe51` ↔ pulso `0c6536f` ↔ MusaLCEforBitwig `33e4d7c`).
|
|
235
|
+
|
|
236
|
+
## Where to go next
|
|
237
|
+
|
|
238
|
+
- Reference the **standalone REPL** workflow: [musa-dsl/docs/subsystems/repl.md](https://github.com/javier-sy/musa-dsl/blob/master/docs/subsystems/repl.md).
|
|
239
|
+
- Reference the **REPL commands** (`daw.*`, transport controls, sequencer DSL) of the suite: [musalce-server README](https://github.com/javier-sy/musalce-server#readme).
|
|
240
|
+
- Configure the **DAW extensions**: [MusaLCEforBitwig README](https://github.com/javier-sy/MusaLCEforBitwig#readme), [MusaLCEforLive README](https://github.com/javier-sy/MusaLCEforLive#readme).
|
|
241
|
+
- Wire the **VSCode editor**: [MusaLCEClientForVSCode README](https://github.com/javier-sy/MusaLCEClientForVSCode#readme).
|
|
242
|
+
- Wire the **Stream Deck** (Bitwig only): [Pulso README](https://github.com/javier-sy/pulso#readme) + [OSC protocol](https://github.com/javier-sy/pulso/blob/main/docs/osc-protocol.md).
|