x32scene 0.1.0__tar.gz

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.
Files changed (103) hide show
  1. x32scene-0.1.0/LICENSE +21 -0
  2. x32scene-0.1.0/PKG-INFO +276 -0
  3. x32scene-0.1.0/README.md +249 -0
  4. x32scene-0.1.0/pyproject.toml +52 -0
  5. x32scene-0.1.0/setup.cfg +4 -0
  6. x32scene-0.1.0/src/x32scene/__init__.py +16 -0
  7. x32scene-0.1.0/src/x32scene/_cli_edits.py +268 -0
  8. x32scene-0.1.0/src/x32scene/_json.py +152 -0
  9. x32scene-0.1.0/src/x32scene/_parsers.py +297 -0
  10. x32scene-0.1.0/src/x32scene/_parsers_live.py +39 -0
  11. x32scene-0.1.0/src/x32scene/_views.py +252 -0
  12. x32scene-0.1.0/src/x32scene/_views_desk.py +96 -0
  13. x32scene-0.1.0/src/x32scene/_views_ports.py +80 -0
  14. x32scene-0.1.0/src/x32scene/_views_report.py +163 -0
  15. x32scene-0.1.0/src/x32scene/cli.py +279 -0
  16. x32scene-0.1.0/src/x32scene/model.py +153 -0
  17. x32scene-0.1.0/src/x32scene/orchestrators/__init__.py +1 -0
  18. x32scene-0.1.0/src/x32scene/orchestrators/_schema.py +202 -0
  19. x32scene-0.1.0/src/x32scene/orchestrators/_sections.py +253 -0
  20. x32scene-0.1.0/src/x32scene/orchestrators/band_swap.py +235 -0
  21. x32scene-0.1.0/src/x32scene/py.typed +0 -0
  22. x32scene-0.1.0/src/x32scene/services/__init__.py +1 -0
  23. x32scene-0.1.0/src/x32scene/services/audit.py +106 -0
  24. x32scene-0.1.0/src/x32scene/services/channelfx.py +260 -0
  25. x32scene-0.1.0/src/x32scene/services/console.py +135 -0
  26. x32scene-0.1.0/src/x32scene/services/describe.py +148 -0
  27. x32scene-0.1.0/src/x32scene/services/desk.py +156 -0
  28. x32scene-0.1.0/src/x32scene/services/diff.py +31 -0
  29. x32scene-0.1.0/src/x32scene/services/fx.py +187 -0
  30. x32scene-0.1.0/src/x32scene/services/groups.py +87 -0
  31. x32scene-0.1.0/src/x32scene/services/headers.py +56 -0
  32. x32scene-0.1.0/src/x32scene/services/history.py +20 -0
  33. x32scene-0.1.0/src/x32scene/services/iem.py +160 -0
  34. x32scene-0.1.0/src/x32scene/services/matrix.py +131 -0
  35. x32scene-0.1.0/src/x32scene/services/meters.py +106 -0
  36. x32scene-0.1.0/src/x32scene/services/osc.py +140 -0
  37. x32scene-0.1.0/src/x32scene/services/preflight.py +193 -0
  38. x32scene-0.1.0/src/x32scene/services/preflight_config.py +125 -0
  39. x32scene-0.1.0/src/x32scene/services/preflight_groups.py +114 -0
  40. x32scene-0.1.0/src/x32scene/services/preflight_links.py +113 -0
  41. x32scene-0.1.0/src/x32scene/services/preflight_monitor.py +115 -0
  42. x32scene-0.1.0/src/x32scene/services/preflight_outputs.py +92 -0
  43. x32scene-0.1.0/src/x32scene/services/preflight_routing.py +105 -0
  44. x32scene-0.1.0/src/x32scene/services/preflight_sends.py +122 -0
  45. x32scene-0.1.0/src/x32scene/services/preflight_stage.py +55 -0
  46. x32scene-0.1.0/src/x32scene/services/presets.py +156 -0
  47. x32scene-0.1.0/src/x32scene/services/routing.py +217 -0
  48. x32scene-0.1.0/src/x32scene/services/routing_edit.py +171 -0
  49. x32scene-0.1.0/src/x32scene/services/scopes.py +56 -0
  50. x32scene-0.1.0/src/x32scene/services/show.py +146 -0
  51. x32scene-0.1.0/src/x32scene/services/snippets.py +224 -0
  52. x32scene-0.1.0/src/x32scene/services/stage.py +105 -0
  53. x32scene-0.1.0/src/x32scene/services/transforms.py +235 -0
  54. x32scene-0.1.0/src/x32scene/services/transplant.py +69 -0
  55. x32scene-0.1.0/src/x32scene/services/userctrl.py +110 -0
  56. x32scene-0.1.0/src/x32scene/tables.py +280 -0
  57. x32scene-0.1.0/src/x32scene/tables_fx.py +268 -0
  58. x32scene-0.1.0/src/x32scene.egg-info/PKG-INFO +276 -0
  59. x32scene-0.1.0/src/x32scene.egg-info/SOURCES.txt +101 -0
  60. x32scene-0.1.0/src/x32scene.egg-info/dependency_links.txt +1 -0
  61. x32scene-0.1.0/src/x32scene.egg-info/entry_points.txt +2 -0
  62. x32scene-0.1.0/src/x32scene.egg-info/requires.txt +6 -0
  63. x32scene-0.1.0/src/x32scene.egg-info/top_level.txt +1 -0
  64. x32scene-0.1.0/tests/test_audit.py +71 -0
  65. x32scene-0.1.0/tests/test_band_swap.py +298 -0
  66. x32scene-0.1.0/tests/test_band_swap_outputs.py +73 -0
  67. x32scene-0.1.0/tests/test_band_swap_plan.py +134 -0
  68. x32scene-0.1.0/tests/test_band_swap_sections.py +138 -0
  69. x32scene-0.1.0/tests/test_band_swap_sends.py +275 -0
  70. x32scene-0.1.0/tests/test_channelfx.py +277 -0
  71. x32scene-0.1.0/tests/test_cli.py +255 -0
  72. x32scene-0.1.0/tests/test_console.py +127 -0
  73. x32scene-0.1.0/tests/test_describe.py +158 -0
  74. x32scene-0.1.0/tests/test_desk.py +112 -0
  75. x32scene-0.1.0/tests/test_fx_groups.py +233 -0
  76. x32scene-0.1.0/tests/test_fx_write.py +168 -0
  77. x32scene-0.1.0/tests/test_headers.py +97 -0
  78. x32scene-0.1.0/tests/test_history.py +77 -0
  79. x32scene-0.1.0/tests/test_iem_copy.py +122 -0
  80. x32scene-0.1.0/tests/test_json.py +71 -0
  81. x32scene-0.1.0/tests/test_matrix.py +131 -0
  82. x32scene-0.1.0/tests/test_meters.py +113 -0
  83. x32scene-0.1.0/tests/test_osc.py +142 -0
  84. x32scene-0.1.0/tests/test_preflight.py +256 -0
  85. x32scene-0.1.0/tests/test_preflight_groups.py +122 -0
  86. x32scene-0.1.0/tests/test_preflight_links.py +117 -0
  87. x32scene-0.1.0/tests/test_preflight_monitor.py +123 -0
  88. x32scene-0.1.0/tests/test_preflight_outputs.py +102 -0
  89. x32scene-0.1.0/tests/test_preflight_routing.py +97 -0
  90. x32scene-0.1.0/tests/test_preflight_sends.py +121 -0
  91. x32scene-0.1.0/tests/test_presets.py +178 -0
  92. x32scene-0.1.0/tests/test_report.py +98 -0
  93. x32scene-0.1.0/tests/test_roundtrip.py +170 -0
  94. x32scene-0.1.0/tests/test_routing.py +155 -0
  95. x32scene-0.1.0/tests/test_routing_edit.py +206 -0
  96. x32scene-0.1.0/tests/test_show_build.py +102 -0
  97. x32scene-0.1.0/tests/test_snippets.py +229 -0
  98. x32scene-0.1.0/tests/test_stage.py +179 -0
  99. x32scene-0.1.0/tests/test_taps.py +21 -0
  100. x32scene-0.1.0/tests/test_transforms.py +219 -0
  101. x32scene-0.1.0/tests/test_transforms_mix.py +112 -0
  102. x32scene-0.1.0/tests/test_transplant.py +138 -0
  103. x32scene-0.1.0/tests/test_views.py +152 -0
x32scene-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Mike Farr
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,276 @@
1
+ Metadata-Version: 2.4
2
+ Name: x32scene
3
+ Version: 0.1.0
4
+ Summary: Read, diff, edit and verify Behringer X32 / M32 console files (scenes, snippets, presets, shows) and read the live desk over OSC
5
+ Author: Mike Farr
6
+ License-Expression: MIT
7
+ Project-URL: Repository, https://github.com/phierceweb/x32scene
8
+ Project-URL: Changelog, https://github.com/phierceweb/x32scene/blob/main/CHANGELOG.md
9
+ Project-URL: Issues, https://github.com/phierceweb/x32scene/issues
10
+ Keywords: x32,m32,behringer,osc,audio,mixing-console,live-sound,scene-files,snippets
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: End Users/Desktop
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Programming Language :: Python :: 3.13
16
+ Classifier: Topic :: Multimedia :: Sound/Audio
17
+ Classifier: Typing :: Typed
18
+ Requires-Python: >=3.12
19
+ Description-Content-Type: text/markdown
20
+ License-File: LICENSE
21
+ Requires-Dist: pf-core~=0.22.0
22
+ Provides-Extra: dev
23
+ Requires-Dist: pytest>=8.0; extra == "dev"
24
+ Requires-Dist: pytest-cov>=5.0; extra == "dev"
25
+ Requires-Dist: ruff>=0.6; extra == "dev"
26
+ Dynamic: license-file
27
+
28
+ # x32scene
29
+
30
+ [![PyPI](https://img.shields.io/pypi/v/x32scene)](https://pypi.org/project/x32scene/)
31
+
32
+ Create X32 / M32 console files on the fly. Build, update, modify, transfer settings using AI via Python.
33
+
34
+ # Why x32scene
35
+
36
+ The X32 is a wildly capable mixer. Almost anything can be routed anywhere between hardware
37
+ inputs and outputs. With that capability comes a lot of complexity and a steep learning
38
+ curve, and developing a great scene is a lot of work: the right EQs, frequency-keyed gate
39
+ settings, the aux setup for each player's in-ears, the routing to the stage boxes. Copying
40
+ any of it by hand, one control at a time, is slow and easy to get wrong. **x32scene**
41
+ exposes commands that do that work on the scene file and save a new scene that loads
42
+ onto the desk.
43
+
44
+ Some of the things it does that are hard to do any other way:
45
+
46
+ - **Move a whole set of inputs to a second stage box.** Each channel's source is re-pointed
47
+ through the routing banks and its head-amp gain and phantom travel with it, because
48
+ the X32 stores gain by physical input, not by channel; the move is all-or-nothing, with
49
+ every target input range-checked first.
50
+ - **Carry one player's in-ear mix into the next band's scene.** A monitor mix is a bus
51
+ strip plus every sender's send into it, both sides of a stereo pair; `transplant` moves
52
+ exactly those lines and nothing else, and `snippet --bus` writes the same mix as a
53
+ snippet the desk recalls mid-changeover while every other aux stays put. Loaded on a
54
+ console, a full pull afterwards differed from the starting scene in only that pair's
55
+ lines.
56
+ - **Set an effect by parameter name.** Every one of the 61 effect types is mapped
57
+ parameter by parameter, so "plate in FX 4, decay 2.1, damping 8 kHz" is one command,
58
+ written in the desk's own token format; a type change starts from the parameter line
59
+ the desk itself writes.
60
+ - **Describe a whole night in one file.** A JSON plan — names, head amps, EQ, dynamics,
61
+ monitor mixes, effects, routing, outputs — applied to a known-good template, every
62
+ value checked against the console's vocabulary, and the result refused if it changed
63
+ anything the plan did not name.
64
+ - **Know what actually changed.** `diff --by-strip` says "channel 23, send to bus 9,
65
+ level −14.0 → −11.0", and `history` follows one parameter across every scene you have
66
+ saved.
67
+
68
+ ## What it is
69
+
70
+ An X32 stores its state as plain text, one parameter per line: input patching, head amps,
71
+ every monitor mix, effects, routing. The console and its editor edit that state one
72
+ control at a time. x32scene works on the files instead. It parses every kind the console
73
+ reads and writes, keeps each file byte for byte (`Scene.parse(text).dump() == text`), and
74
+ applies edits so that the result differs from its source by exactly the intended lines —
75
+ which `x32scene diff` shows. Edit commands never overwrite the file they read; the
76
+ changed file is loaded on the console by you.
77
+
78
+ The live layer is read-only: `pull` captures the running desk as a scene file so every
79
+ command works on the live console, `desk` reports its identity, status and memory slots,
80
+ `meters` reports levels. Pushing changes to the desk over the network is not a feature.
81
+
82
+ Every command has a `--json` form, the vocabularies the console accepts are listed by
83
+ commands, and the reference docs state which facts were observed on hardware and which
84
+ were read from a manual, so the tool can be driven by an AI agent as well as by hand.
85
+
86
+ ## What it handles
87
+
88
+ | File | What it is | Read | Write |
89
+ |---|---|---|---|
90
+ | `.scn` | a whole console | yes | every edit command, `band-setup`, `transplant` |
91
+ | `.snp` | a snippet — lines the desk recalls *in place*, everything else untouched | yes | `snippet` |
92
+ | `.chn` | one channel strip, per section | yes | `extract-preset` / `apply-preset` |
93
+ | `.efx` | one effect slot | yes | `extract-fx` / `apply-fx` |
94
+ | `.rou` | the input routing banks | yes | `extract-routing` / `apply-routing` |
95
+ | `.shw` | a show: cues that recall scenes and snippets | yes | `show-build` |
96
+
97
+ [docs/capabilities.md](https://github.com/phierceweb/x32scene/blob/main/docs/capabilities.md)
98
+ lists every command by the job it does.
99
+
100
+ ## What the operations are
101
+
102
+ **Reading.** Channel sources resolved through the routing banks and user patch, every
103
+ output's feed and tap point, the mix buses and their stereo pairs, one monitor mix or all
104
+ of them as a matrix, the USB record map, the FX rack with parameters by name, DCA and
105
+ mute groups, the desk-wide settings (monitor, talkback, oscillator, recorder, user-assign
106
+ buttons) in words, and a markdown report of the whole scene.
107
+
108
+ **Comparing.** `diff` between two files, by path or grouped by strip with the moved
109
+ fields named; `history` of one parameter across a dated library; `audit` of a library's
110
+ structural invariants; `live-diff` of the desk against a file.
111
+
112
+ **Editing.** Strip settings (name, fader, mute, pan, EQ, low cut, compressor, gate), FX
113
+ slots by parameter name, a channel's input source, an output's feed, the routing banks.
114
+ Values are written in the console's own token formats, checked against its vocabularies,
115
+ and mirrored to a stereo-linked partner unless told not to.
116
+
117
+ **Carrying between scenes.** `transplant` copies chosen lines from one scene into another
118
+ and touches nothing else: a whole monitor mix (the bus strip and every send into it), any
119
+ path pattern, or a channel's sections with the head amp re-mapped. Presets do the same
120
+ one strip, slot or bank at a time.
121
+
122
+ **Snippets.** The delta between two scenes, edits applied in memory, or one monitor mix
123
+ whole, written with the filter masks the desk reads derived from the body. Loaded on a
124
+ console, a snippet of one bus pair changed that pair's lines and no others; the details are
125
+ in the console-behaviour doc.
126
+
127
+ **Plans and shows.** `band-setup` applies one JSON plan (names, head amps, processing,
128
+ monitor mixes, effects, routing, outputs) to a template, validates every value first, and
129
+ refuses to write if the result strays outside the paths the plan named. `show-build`
130
+ writes a show index with cues and its companion files in the shape X32-Edit imports.
131
+
132
+ **Checks.** `preflight` compares a scene against a documented rig — outputs, monitor
133
+ skeleton, routing, stereo links, send taps, groups — and a stage sidecar recording which
134
+ jack and which person each output feeds. Exit 1 on a failure.
135
+
136
+ ## What the desk does with a file
137
+
138
+ A file that round-trips and diffs clean can still not do what was intended once the
139
+ console loads it. The docs record what was learned by loading files on hardware and
140
+ reading the state back: the token formats a bad value silently aborts a line on, the
141
+ frequencies and effect parameters the desk snaps to its own grid, how stereo-linked pairs
142
+ reconcile on recall, and the difference between the desk's USB import and X32-Edit's Load
143
+ (which pushes a file's lines from the computer). See
144
+ [docs/console-behavior.md](https://github.com/phierceweb/x32scene/blob/main/docs/console-behavior.md)
145
+ and its load-test loop before trusting a generated file at a gig.
146
+
147
+ ## Scope
148
+
149
+ - Files and a read-only view of the desk. No network writes to the console.
150
+ - Round-trip and diff prove a file is structurally correct and changed only where
151
+ intended; the console is the only proof it is semantically correct.
152
+ - Snippets and presets loaded from a USB stick go through the desk's own recall and
153
+ header masks; X32-Edit's Load pushes the lines instead. Both were used in testing; the
154
+ USB route was not observed directly.
155
+ - Pre-1.0: pin to a tagged release.
156
+
157
+ ## How it relates to other tools
158
+
159
+ | Tool | What it is | How x32scene relates |
160
+ |---|---|---|
161
+ | X32-Edit, Mixing Station | Editors for the live console | They change the desk one control at a time. x32scene works on the files those editors save and load, and shows what changed between two of them |
162
+ | Patrick-Gilles Maillot's X32 utilities and protocol document | The reference for the console's OSC dialect, and command-line tools that copy, save and drive it | x32scene's OSC layer implements the read side of that dialect; its enumerations were checked against a console and the document's corrections are noted where the desk disagreed |
163
+ | OSC libraries (python-osc and the like) | Generic OSC transport | x32scene carries its own small X32 dialect encoder, because the console's node queries and meter blobs are not plain OSC |
164
+ | A scene library in git | Version control of `.scn` files | x32scene adds a diff in the console's terms and edits that keep every other byte in place |
165
+
166
+ ## Install
167
+
168
+ ```bash
169
+ pip install x32scene
170
+ ```
171
+
172
+ Python 3.12 or newer, one dependency ([pf-core](https://pypi.org/project/pf-core/)).
173
+ Releases are tagged; `main` is the development line. Release notes:
174
+ [CHANGELOG.md](https://github.com/phierceweb/x32scene/blob/main/CHANGELOG.md).
175
+
176
+ ## Commands
177
+
178
+ ```bash
179
+ x32scene info | inputs | ports | buses | iem | iem-matrix | record-map | fx | dca | console | report scene.scn
180
+ x32scene header FILE # any file's header decoded
181
+ x32scene show show.shw # a show index
182
+ x32scene diff a.scn b.scn [--by-strip] # what changed
183
+ x32scene history PATH… --dir DIR # one parameter across a library
184
+ x32scene set-eq | set-comp | set-gate | set-lowcut | set-fader | set-mute | set-pan | rename scene.scn STRIP … -o out.scn
185
+ x32scene set-fx | set-input | set-output | set-routing scene.scn … -o out.scn
186
+ x32scene transplant src.scn dst.scn -o out.scn --bus 1 --path /headamp/000 --ch 3 --scope eq
187
+ x32scene snippet a.scn b.scn -o delta.snp | a.scn -o mix.snp --bus 9 | a.scn -o eq.snp --edit "set-eq 5 2 --gain 3"
188
+ x32scene band-setup template.scn plan.json -o out.scn [--snippet out.snp]
189
+ x32scene show-build -o DIR --name Night --scene a.scn --snippet x.snp --cue "1 Opener scene=0"
190
+ x32scene preflight scene.scn --config rig.json [--stage stage.json]
191
+ x32scene pull reference.scn -o live.scn | live-diff scene.scn | desk | meters # --ip or X32SCENE_IP
192
+ ```
193
+
194
+ Every command, flag and environment variable:
195
+ [docs/cli.md](https://github.com/phierceweb/x32scene/blob/main/docs/cli.md). Example
196
+ inputs: [`config/example-preflight.json`](https://github.com/phierceweb/x32scene/blob/main/config/example-preflight.json),
197
+ [`config/example-plan.json`](https://github.com/phierceweb/x32scene/blob/main/config/example-plan.json),
198
+ [`config/example-stage.json`](https://github.com/phierceweb/x32scene/blob/main/config/example-stage.json).
199
+
200
+ ## Use it from Python
201
+
202
+ ```python
203
+ from x32scene import Scene
204
+ from x32scene.services import transforms as T, transplant
205
+
206
+ sc = Scene.load("scene.scn")
207
+ T.rename_channel(sc, 1, "Kick In")
208
+ T.set_headamp(sc, "local", 1, gain_db=30.0, phantom=False)
209
+ sc.save("out.scn")
210
+
211
+ other = Scene.load("friday.scn")
212
+ transplant.transplant(sc, other, buses=[9]) # this scene's bus-9 mix into the other
213
+ ```
214
+
215
+ The band-setup workflow is one call and raises rather than write anything outside the plan:
216
+
217
+ ```python
218
+ from x32scene.orchestrators.band_swap import load_plan, run
219
+
220
+ report = run("template.scn", load_plan("plan.json"), "out.scn")
221
+ ```
222
+
223
+ ## Working from a checkout
224
+
225
+ ```bash
226
+ git clone https://github.com/phierceweb/x32scene && cd x32scene
227
+ bin/run setup # venv, editable install
228
+ bin/run pytest # the suite
229
+ bin/run lint # ruff + the pf-core structural gate
230
+ bin/run x32scene … # the CLI, with .env loaded
231
+ ```
232
+
233
+ `X32SCENE_CORPUS=~/path/to/scenes bin/run pytest tests/test_roundtrip.py` runs the
234
+ round-trip test over your own library.
235
+
236
+ ## Docs
237
+
238
+ [docs/README.md](https://github.com/phierceweb/x32scene/blob/main/docs/README.md) is the
239
+ index, maintained beside the docs.
240
+
241
+ - [capabilities.md](https://github.com/phierceweb/x32scene/blob/main/docs/capabilities.md) — everything x32scene does, by job, and what it deliberately does not do
242
+ - [cli.md](https://github.com/phierceweb/x32scene/blob/main/docs/cli.md) — every command, flag and environment variable
243
+ - [format.md](https://github.com/phierceweb/x32scene/blob/main/docs/format.md) — every file kind and every line, field by field; the enumerations; what is verified against hardware and what is inferred
244
+ - [console-behavior.md](https://github.com/phierceweb/x32scene/blob/main/docs/console-behavior.md) — what the desk does when it loads a file, and how a file reaches the desk
245
+ - [routing.md](https://github.com/phierceweb/x32scene/blob/main/docs/routing.md) — the two-layer routing model, resolving a channel to its jack, the record map
246
+ - [stage-sidecar.md](https://github.com/phierceweb/x32scene/blob/main/docs/stage-sidecar.md) — recording which jack and which person each output feeds, and what `preflight` checks against it
247
+
248
+ ## Built on pf-core
249
+
250
+ x32scene is built on [pf-core](https://github.com/phierceweb/pf-core)
251
+ ([PyPI](https://pypi.org/project/pf-core/)), a Python foundation for LLM-facing
252
+ applications. x32scene uses its atomic-write utilities, structured logging, exception
253
+ boundary and config-from-env, and its structural gate, which fails the build when a file
254
+ outgrows its line budget.
255
+
256
+ For other phierceweb projects, see [github.com/phierceweb](https://github.com/phierceweb).
257
+
258
+ ## Contributing
259
+
260
+ [CONTRIBUTING.md](https://github.com/phierceweb/x32scene/blob/main/CONTRIBUTING.md): never
261
+ break the round-trip, run `bin/run lint` and the suite, and a real scene file that does not
262
+ round-trip is the most useful bug report — with the console model and firmware, not the file.
263
+
264
+ ## Security
265
+
266
+ [SECURITY.md](https://github.com/phierceweb/x32scene/blob/main/SECURITY.md). x32scene
267
+ sends nothing off the local network, stores no credentials, and its live layer only reads
268
+ the desk.
269
+
270
+ ## License
271
+
272
+ MIT — see [LICENSE](https://github.com/phierceweb/x32scene/blob/main/LICENSE).
273
+
274
+ Not affiliated with, endorsed by, or sponsored by Music Tribe. BEHRINGER, X32, and M32 are
275
+ trademarks of Music Tribe Global Brands Ltd. See
276
+ [TRADEMARKS.md](https://github.com/phierceweb/x32scene/blob/main/TRADEMARKS.md).
@@ -0,0 +1,249 @@
1
+ # x32scene
2
+
3
+ [![PyPI](https://img.shields.io/pypi/v/x32scene)](https://pypi.org/project/x32scene/)
4
+
5
+ Create X32 / M32 console files on the fly. Build, update, modify, transfer settings using AI via Python.
6
+
7
+ # Why x32scene
8
+
9
+ The X32 is a wildly capable mixer. Almost anything can be routed anywhere between hardware
10
+ inputs and outputs. With that capability comes a lot of complexity and a steep learning
11
+ curve, and developing a great scene is a lot of work: the right EQs, frequency-keyed gate
12
+ settings, the aux setup for each player's in-ears, the routing to the stage boxes. Copying
13
+ any of it by hand, one control at a time, is slow and easy to get wrong. **x32scene**
14
+ exposes commands that do that work on the scene file and save a new scene that loads
15
+ onto the desk.
16
+
17
+ Some of the things it does that are hard to do any other way:
18
+
19
+ - **Move a whole set of inputs to a second stage box.** Each channel's source is re-pointed
20
+ through the routing banks and its head-amp gain and phantom travel with it, because
21
+ the X32 stores gain by physical input, not by channel; the move is all-or-nothing, with
22
+ every target input range-checked first.
23
+ - **Carry one player's in-ear mix into the next band's scene.** A monitor mix is a bus
24
+ strip plus every sender's send into it, both sides of a stereo pair; `transplant` moves
25
+ exactly those lines and nothing else, and `snippet --bus` writes the same mix as a
26
+ snippet the desk recalls mid-changeover while every other aux stays put. Loaded on a
27
+ console, a full pull afterwards differed from the starting scene in only that pair's
28
+ lines.
29
+ - **Set an effect by parameter name.** Every one of the 61 effect types is mapped
30
+ parameter by parameter, so "plate in FX 4, decay 2.1, damping 8 kHz" is one command,
31
+ written in the desk's own token format; a type change starts from the parameter line
32
+ the desk itself writes.
33
+ - **Describe a whole night in one file.** A JSON plan — names, head amps, EQ, dynamics,
34
+ monitor mixes, effects, routing, outputs — applied to a known-good template, every
35
+ value checked against the console's vocabulary, and the result refused if it changed
36
+ anything the plan did not name.
37
+ - **Know what actually changed.** `diff --by-strip` says "channel 23, send to bus 9,
38
+ level −14.0 → −11.0", and `history` follows one parameter across every scene you have
39
+ saved.
40
+
41
+ ## What it is
42
+
43
+ An X32 stores its state as plain text, one parameter per line: input patching, head amps,
44
+ every monitor mix, effects, routing. The console and its editor edit that state one
45
+ control at a time. x32scene works on the files instead. It parses every kind the console
46
+ reads and writes, keeps each file byte for byte (`Scene.parse(text).dump() == text`), and
47
+ applies edits so that the result differs from its source by exactly the intended lines —
48
+ which `x32scene diff` shows. Edit commands never overwrite the file they read; the
49
+ changed file is loaded on the console by you.
50
+
51
+ The live layer is read-only: `pull` captures the running desk as a scene file so every
52
+ command works on the live console, `desk` reports its identity, status and memory slots,
53
+ `meters` reports levels. Pushing changes to the desk over the network is not a feature.
54
+
55
+ Every command has a `--json` form, the vocabularies the console accepts are listed by
56
+ commands, and the reference docs state which facts were observed on hardware and which
57
+ were read from a manual, so the tool can be driven by an AI agent as well as by hand.
58
+
59
+ ## What it handles
60
+
61
+ | File | What it is | Read | Write |
62
+ |---|---|---|---|
63
+ | `.scn` | a whole console | yes | every edit command, `band-setup`, `transplant` |
64
+ | `.snp` | a snippet — lines the desk recalls *in place*, everything else untouched | yes | `snippet` |
65
+ | `.chn` | one channel strip, per section | yes | `extract-preset` / `apply-preset` |
66
+ | `.efx` | one effect slot | yes | `extract-fx` / `apply-fx` |
67
+ | `.rou` | the input routing banks | yes | `extract-routing` / `apply-routing` |
68
+ | `.shw` | a show: cues that recall scenes and snippets | yes | `show-build` |
69
+
70
+ [docs/capabilities.md](https://github.com/phierceweb/x32scene/blob/main/docs/capabilities.md)
71
+ lists every command by the job it does.
72
+
73
+ ## What the operations are
74
+
75
+ **Reading.** Channel sources resolved through the routing banks and user patch, every
76
+ output's feed and tap point, the mix buses and their stereo pairs, one monitor mix or all
77
+ of them as a matrix, the USB record map, the FX rack with parameters by name, DCA and
78
+ mute groups, the desk-wide settings (monitor, talkback, oscillator, recorder, user-assign
79
+ buttons) in words, and a markdown report of the whole scene.
80
+
81
+ **Comparing.** `diff` between two files, by path or grouped by strip with the moved
82
+ fields named; `history` of one parameter across a dated library; `audit` of a library's
83
+ structural invariants; `live-diff` of the desk against a file.
84
+
85
+ **Editing.** Strip settings (name, fader, mute, pan, EQ, low cut, compressor, gate), FX
86
+ slots by parameter name, a channel's input source, an output's feed, the routing banks.
87
+ Values are written in the console's own token formats, checked against its vocabularies,
88
+ and mirrored to a stereo-linked partner unless told not to.
89
+
90
+ **Carrying between scenes.** `transplant` copies chosen lines from one scene into another
91
+ and touches nothing else: a whole monitor mix (the bus strip and every send into it), any
92
+ path pattern, or a channel's sections with the head amp re-mapped. Presets do the same
93
+ one strip, slot or bank at a time.
94
+
95
+ **Snippets.** The delta between two scenes, edits applied in memory, or one monitor mix
96
+ whole, written with the filter masks the desk reads derived from the body. Loaded on a
97
+ console, a snippet of one bus pair changed that pair's lines and no others; the details are
98
+ in the console-behaviour doc.
99
+
100
+ **Plans and shows.** `band-setup` applies one JSON plan (names, head amps, processing,
101
+ monitor mixes, effects, routing, outputs) to a template, validates every value first, and
102
+ refuses to write if the result strays outside the paths the plan named. `show-build`
103
+ writes a show index with cues and its companion files in the shape X32-Edit imports.
104
+
105
+ **Checks.** `preflight` compares a scene against a documented rig — outputs, monitor
106
+ skeleton, routing, stereo links, send taps, groups — and a stage sidecar recording which
107
+ jack and which person each output feeds. Exit 1 on a failure.
108
+
109
+ ## What the desk does with a file
110
+
111
+ A file that round-trips and diffs clean can still not do what was intended once the
112
+ console loads it. The docs record what was learned by loading files on hardware and
113
+ reading the state back: the token formats a bad value silently aborts a line on, the
114
+ frequencies and effect parameters the desk snaps to its own grid, how stereo-linked pairs
115
+ reconcile on recall, and the difference between the desk's USB import and X32-Edit's Load
116
+ (which pushes a file's lines from the computer). See
117
+ [docs/console-behavior.md](https://github.com/phierceweb/x32scene/blob/main/docs/console-behavior.md)
118
+ and its load-test loop before trusting a generated file at a gig.
119
+
120
+ ## Scope
121
+
122
+ - Files and a read-only view of the desk. No network writes to the console.
123
+ - Round-trip and diff prove a file is structurally correct and changed only where
124
+ intended; the console is the only proof it is semantically correct.
125
+ - Snippets and presets loaded from a USB stick go through the desk's own recall and
126
+ header masks; X32-Edit's Load pushes the lines instead. Both were used in testing; the
127
+ USB route was not observed directly.
128
+ - Pre-1.0: pin to a tagged release.
129
+
130
+ ## How it relates to other tools
131
+
132
+ | Tool | What it is | How x32scene relates |
133
+ |---|---|---|
134
+ | X32-Edit, Mixing Station | Editors for the live console | They change the desk one control at a time. x32scene works on the files those editors save and load, and shows what changed between two of them |
135
+ | Patrick-Gilles Maillot's X32 utilities and protocol document | The reference for the console's OSC dialect, and command-line tools that copy, save and drive it | x32scene's OSC layer implements the read side of that dialect; its enumerations were checked against a console and the document's corrections are noted where the desk disagreed |
136
+ | OSC libraries (python-osc and the like) | Generic OSC transport | x32scene carries its own small X32 dialect encoder, because the console's node queries and meter blobs are not plain OSC |
137
+ | A scene library in git | Version control of `.scn` files | x32scene adds a diff in the console's terms and edits that keep every other byte in place |
138
+
139
+ ## Install
140
+
141
+ ```bash
142
+ pip install x32scene
143
+ ```
144
+
145
+ Python 3.12 or newer, one dependency ([pf-core](https://pypi.org/project/pf-core/)).
146
+ Releases are tagged; `main` is the development line. Release notes:
147
+ [CHANGELOG.md](https://github.com/phierceweb/x32scene/blob/main/CHANGELOG.md).
148
+
149
+ ## Commands
150
+
151
+ ```bash
152
+ x32scene info | inputs | ports | buses | iem | iem-matrix | record-map | fx | dca | console | report scene.scn
153
+ x32scene header FILE # any file's header decoded
154
+ x32scene show show.shw # a show index
155
+ x32scene diff a.scn b.scn [--by-strip] # what changed
156
+ x32scene history PATH… --dir DIR # one parameter across a library
157
+ x32scene set-eq | set-comp | set-gate | set-lowcut | set-fader | set-mute | set-pan | rename scene.scn STRIP … -o out.scn
158
+ x32scene set-fx | set-input | set-output | set-routing scene.scn … -o out.scn
159
+ x32scene transplant src.scn dst.scn -o out.scn --bus 1 --path /headamp/000 --ch 3 --scope eq
160
+ x32scene snippet a.scn b.scn -o delta.snp | a.scn -o mix.snp --bus 9 | a.scn -o eq.snp --edit "set-eq 5 2 --gain 3"
161
+ x32scene band-setup template.scn plan.json -o out.scn [--snippet out.snp]
162
+ x32scene show-build -o DIR --name Night --scene a.scn --snippet x.snp --cue "1 Opener scene=0"
163
+ x32scene preflight scene.scn --config rig.json [--stage stage.json]
164
+ x32scene pull reference.scn -o live.scn | live-diff scene.scn | desk | meters # --ip or X32SCENE_IP
165
+ ```
166
+
167
+ Every command, flag and environment variable:
168
+ [docs/cli.md](https://github.com/phierceweb/x32scene/blob/main/docs/cli.md). Example
169
+ inputs: [`config/example-preflight.json`](https://github.com/phierceweb/x32scene/blob/main/config/example-preflight.json),
170
+ [`config/example-plan.json`](https://github.com/phierceweb/x32scene/blob/main/config/example-plan.json),
171
+ [`config/example-stage.json`](https://github.com/phierceweb/x32scene/blob/main/config/example-stage.json).
172
+
173
+ ## Use it from Python
174
+
175
+ ```python
176
+ from x32scene import Scene
177
+ from x32scene.services import transforms as T, transplant
178
+
179
+ sc = Scene.load("scene.scn")
180
+ T.rename_channel(sc, 1, "Kick In")
181
+ T.set_headamp(sc, "local", 1, gain_db=30.0, phantom=False)
182
+ sc.save("out.scn")
183
+
184
+ other = Scene.load("friday.scn")
185
+ transplant.transplant(sc, other, buses=[9]) # this scene's bus-9 mix into the other
186
+ ```
187
+
188
+ The band-setup workflow is one call and raises rather than write anything outside the plan:
189
+
190
+ ```python
191
+ from x32scene.orchestrators.band_swap import load_plan, run
192
+
193
+ report = run("template.scn", load_plan("plan.json"), "out.scn")
194
+ ```
195
+
196
+ ## Working from a checkout
197
+
198
+ ```bash
199
+ git clone https://github.com/phierceweb/x32scene && cd x32scene
200
+ bin/run setup # venv, editable install
201
+ bin/run pytest # the suite
202
+ bin/run lint # ruff + the pf-core structural gate
203
+ bin/run x32scene … # the CLI, with .env loaded
204
+ ```
205
+
206
+ `X32SCENE_CORPUS=~/path/to/scenes bin/run pytest tests/test_roundtrip.py` runs the
207
+ round-trip test over your own library.
208
+
209
+ ## Docs
210
+
211
+ [docs/README.md](https://github.com/phierceweb/x32scene/blob/main/docs/README.md) is the
212
+ index, maintained beside the docs.
213
+
214
+ - [capabilities.md](https://github.com/phierceweb/x32scene/blob/main/docs/capabilities.md) — everything x32scene does, by job, and what it deliberately does not do
215
+ - [cli.md](https://github.com/phierceweb/x32scene/blob/main/docs/cli.md) — every command, flag and environment variable
216
+ - [format.md](https://github.com/phierceweb/x32scene/blob/main/docs/format.md) — every file kind and every line, field by field; the enumerations; what is verified against hardware and what is inferred
217
+ - [console-behavior.md](https://github.com/phierceweb/x32scene/blob/main/docs/console-behavior.md) — what the desk does when it loads a file, and how a file reaches the desk
218
+ - [routing.md](https://github.com/phierceweb/x32scene/blob/main/docs/routing.md) — the two-layer routing model, resolving a channel to its jack, the record map
219
+ - [stage-sidecar.md](https://github.com/phierceweb/x32scene/blob/main/docs/stage-sidecar.md) — recording which jack and which person each output feeds, and what `preflight` checks against it
220
+
221
+ ## Built on pf-core
222
+
223
+ x32scene is built on [pf-core](https://github.com/phierceweb/pf-core)
224
+ ([PyPI](https://pypi.org/project/pf-core/)), a Python foundation for LLM-facing
225
+ applications. x32scene uses its atomic-write utilities, structured logging, exception
226
+ boundary and config-from-env, and its structural gate, which fails the build when a file
227
+ outgrows its line budget.
228
+
229
+ For other phierceweb projects, see [github.com/phierceweb](https://github.com/phierceweb).
230
+
231
+ ## Contributing
232
+
233
+ [CONTRIBUTING.md](https://github.com/phierceweb/x32scene/blob/main/CONTRIBUTING.md): never
234
+ break the round-trip, run `bin/run lint` and the suite, and a real scene file that does not
235
+ round-trip is the most useful bug report — with the console model and firmware, not the file.
236
+
237
+ ## Security
238
+
239
+ [SECURITY.md](https://github.com/phierceweb/x32scene/blob/main/SECURITY.md). x32scene
240
+ sends nothing off the local network, stores no credentials, and its live layer only reads
241
+ the desk.
242
+
243
+ ## License
244
+
245
+ MIT — see [LICENSE](https://github.com/phierceweb/x32scene/blob/main/LICENSE).
246
+
247
+ Not affiliated with, endorsed by, or sponsored by Music Tribe. BEHRINGER, X32, and M32 are
248
+ trademarks of Music Tribe Global Brands Ltd. See
249
+ [TRADEMARKS.md](https://github.com/phierceweb/x32scene/blob/main/TRADEMARKS.md).
@@ -0,0 +1,52 @@
1
+ [build-system]
2
+ requires = ["setuptools>=77", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "x32scene"
7
+ version = "0.1.0"
8
+ description = "Read, diff, edit and verify Behringer X32 / M32 console files (scenes, snippets, presets, shows) and read the live desk over OSC"
9
+ readme = "README.md"
10
+ requires-python = ">=3.12"
11
+ license = "MIT"
12
+ license-files = ["LICENSE"]
13
+ authors = [{ name = "Mike Farr" }]
14
+ keywords = ["x32", "m32", "behringer", "osc", "audio", "mixing-console", "live-sound", "scene-files", "snippets"]
15
+ classifiers = [
16
+ "Development Status :: 4 - Beta",
17
+ "Intended Audience :: End Users/Desktop",
18
+ "Programming Language :: Python :: 3",
19
+ "Programming Language :: Python :: 3.12",
20
+ "Programming Language :: Python :: 3.13",
21
+ "Topic :: Multimedia :: Sound/Audio",
22
+ "Typing :: Typed",
23
+ ]
24
+ dependencies = ["pf-core~=0.22.0"]
25
+
26
+ [project.urls]
27
+ Repository = "https://github.com/phierceweb/x32scene"
28
+ Changelog = "https://github.com/phierceweb/x32scene/blob/main/CHANGELOG.md"
29
+ Issues = "https://github.com/phierceweb/x32scene/issues"
30
+
31
+ [project.scripts]
32
+ x32scene = "x32scene.cli:main"
33
+
34
+ [project.optional-dependencies]
35
+ dev = ["pytest>=8.0", "pytest-cov>=5.0", "ruff>=0.6"]
36
+
37
+ [tool.setuptools.packages.find]
38
+ where = ["src"]
39
+
40
+ [tool.setuptools.package-data]
41
+ x32scene = ["py.typed"]
42
+
43
+ [tool.pytest.ini_options]
44
+ testpaths = ["tests"]
45
+
46
+ [tool.ruff]
47
+ line-length = 100
48
+ target-version = "py312"
49
+
50
+ [tool.ruff.lint]
51
+ select = ["E", "W", "F", "B"]
52
+ ignore = ["E501", "B008", "B904"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,16 @@
1
+ """Byte-faithful X32 / M32 scene-file toolkit."""
2
+
3
+ from importlib.metadata import PackageNotFoundError, version
4
+
5
+ from .model import Line, Scene, tokenize
6
+ from .services import ( # noqa: F401
7
+ audit, channelfx, diff, fx, groups, iem, presets, routing, scopes,
8
+ transforms,
9
+ )
10
+
11
+ try:
12
+ __version__ = version("x32scene")
13
+ except PackageNotFoundError: # running from a source tree without an install
14
+ __version__ = "0.0.0.dev0"
15
+
16
+ __all__ = ["Line", "Scene", "tokenize", "__version__"]