docs: capture touchosc v3 tosc schema ground truth

This commit is contained in:
L'électron rare
2026-06-28 12:49:13 +02:00
parent 1101758719
commit 048403e92b
4 changed files with 197 additions and 0 deletions
@@ -0,0 +1,126 @@
# TouchOSC `.tosc` v3 Schema Reference (ground truth)
**Date:** 2026-06-28
**Status:** Format pinned from generator output; **visual fidelity gate
(render in TouchOSC editor) pending human confirmation** — see §6.
**Consumes/feeds:** `touchosc/gen/schema.py` (the single format choke point).
## 1. Provenance
The plan's Task 1 step 1 (hand-author a reference layout in the TouchOSC
editor) was replaced by a **generate-and-iterate** path (no pre-existing
`.tosc` was found on disk at planning time). The authoritative bytes here come
from `touchosc/gen/schema.py` emitting a minimal GROUP > FADER probe, captured
in `touchosc/reference/probe-roundtrip.{tosc,xml,pretty.xml}`.
A byte-identical copy of this probe was found in TouchOSC's iCloud document
cache (`~/Library/Application Support/CloudDocs/session/i/`, md5
`aaa466e27c16ab9b25e18c84a98a71a1`), indicating TouchOSC ingested the file into
its library — weak-positive evidence the format is accepted. The XML structure
matches the documented Hexler v3 `lexml` schema in every element. **Final
proof — the layout rendering correctly in the TouchOSC editor — is a human
at-screen check (§6).**
## 2. Container
- A `.tosc` is **gzip-compressed UTF-8 XML** (Python `gzip` default level 9).
- Root element: `<lexml version="3">` containing exactly **one** root `<node>`.
- XML declaration present: `<?xml version='1.0' encoding='UTF-8'?>`.
## 3. Node
```
<node ID="<uuid4>" type="<CONTROL_TYPE>">
<properties> property* </properties>
<values/> <!-- present even when empty -->
<messages> osc* </messages>
<children> node* </children>
</node>
```
- `ID` — a UUID4 string (`tosc_id()`).
- `type` — control type: `GROUP BOX BUTTON LABEL TEXT FADER XY RADIAL ENCODER
RADAR RADIO PAGER GRID` (per v3). The probe confirms `GROUP` and `FADER`.
- All four sub-blocks (`properties`, `values`, `messages`, `children`) are
emitted, empty as `<.../>` self-closing when they have no entries.
## 4. Property
```
<property type="<code>">
<key>name</key>
<value>...</value>
</property>
```
Type codes (the `<value>` child shape depends on the code):
| code | meaning | `<value>` shape |
|------|---------|-----------------|
| `s` | string | plain text (`<value>vol</value>`); also used for the `script` Lua property |
| `b` | bool | plain text `0`/`1` |
| `i` | int | plain text |
| `f` | float | plain text |
| `r` | frame/rect | `<value><x/><y/><w/><h/></value>` |
| `c` | color | `<value><r/><g/><b/><a/></value>` |
The `frame` property (`type="r"`) is confirmed verbatim in the probe:
`<value><x>10</x><y>20</y><w>60</w><h>200</h></value>`.
## 5. OSC message
```
<osc enabled="1" send="1" receive="0" feedback="0" connections="00001">
<triggers>
<trigger><var>x</var><condition>ANY</condition></trigger>
</triggers>
<path>
<partial> ... </partial> <!-- the address, segment by segment -->
</path>
<arguments>
<partial> ... </partial>* <!-- zero or more OSC arguments -->
</arguments>
</osc>
```
- **Attributes:** `enabled send receive feedback connections`, all present.
- **`connections`** = a fixed-width bit-string, **one flag per OSC connection,
left-to-right = connection 1..N**. TouchOSC writes **5 flags**; a fresh
message targeting connection 1 only is **`"00001"`** (rightmost = connection
1). This is `schema.CONN_DEFAULT`. (NB: the plan text guessed `"00000001"`;
the pinned value is `"00001"`.)
### 5.1 Partial (path segment OR argument)
Fields are **child elements** (NOT attributes — this is the single biggest
deviation from the inactive `tosclib` reference, which used attributes):
```
<partial>
<type>CONSTANT|VALUE</type>
<conversion>STRING|FLOAT|INTEGER|BOOLEAN</conversion>
<value>...</value>
<scaleMin>0</scaleMin>
<scaleMax>1</scaleMax>
</partial>
```
- `CONSTANT`/`STRING` with `<value>/launch/vol</value>` → a literal address (or
a literal string argument like `kick`).
- `VALUE`/`FLOAT` with `<value>x</value>` → bound to the control's `x` value.
- `scaleMin`/`scaleMax` present on every partial (default `0`/`1`).
## 6. Visual fidelity gate (OPEN — human step)
The generated bytes are structurally correct v3. The remaining check is purely
visual and must be done by a human at the GrosMac screen:
1. In TouchOSC: **File > Open** → `touchosc/dist/_probe.tosc` (or the full
`av-live-control.tosc` once built).
2. Confirm the control(s) render with no parse error dialog.
`open -a TouchOSC <file>` launches the app but was observed to show an
"Untitled" window rather than reliably loading the doc — prefer **File > Open**
inside TouchOSC for the gate. If a layout fails to load, fix
`touchosc/gen/schema.py` against this reference and regenerate; no other file
needs to change.
@@ -0,0 +1,69 @@
<?xml version="1.0" encoding="UTF-8"?>
<lexml version="3">
<node ID="7b287d4a-3ded-435e-96d3-3d7a1fb7d977" type="GROUP">
<properties>
<property type="s">
<key>name</key>
<value>root</value>
</property>
</properties>
<values/>
<messages/>
<children>
<node ID="2b908650-d7aa-49be-a3e5-dff646c214f2" type="FADER">
<properties>
<property type="s">
<key>name</key>
<value>vol</value>
</property>
<property type="r">
<key>frame</key>
<value>
<x>10</x>
<y>20</y>
<w>60</w>
<h>200</h>
</value>
</property>
</properties>
<values/>
<messages>
<osc enabled="1" send="1" receive="0" feedback="0" connections="00001">
<triggers>
<trigger>
<var>x</var>
<condition>ANY</condition>
</trigger>
</triggers>
<path>
<partial>
<type>CONSTANT</type>
<conversion>STRING</conversion>
<value>/launch/vol</value>
<scaleMin>0</scaleMin>
<scaleMax>1</scaleMax>
</partial>
</path>
<arguments>
<partial>
<type>CONSTANT</type>
<conversion>STRING</conversion>
<value>kick</value>
<scaleMin>0</scaleMin>
<scaleMax>1</scaleMax>
</partial>
<partial>
<type>VALUE</type>
<conversion>FLOAT</conversion>
<value>x</value>
<scaleMin>0</scaleMin>
<scaleMax>1</scaleMax>
</partial>
</arguments>
</osc>
</messages>
<children/>
</node>
</children>
</node>
</lexml>
Binary file not shown.
+2
View File
@@ -0,0 +1,2 @@
<?xml version='1.0' encoding='UTF-8'?>
<lexml version="3"><node ID="7b287d4a-3ded-435e-96d3-3d7a1fb7d977" type="GROUP"><properties><property type="s"><key>name</key><value>root</value></property></properties><values /><messages /><children><node ID="2b908650-d7aa-49be-a3e5-dff646c214f2" type="FADER"><properties><property type="s"><key>name</key><value>vol</value></property><property type="r"><key>frame</key><value><x>10</x><y>20</y><w>60</w><h>200</h></value></property></properties><values /><messages><osc enabled="1" send="1" receive="0" feedback="0" connections="00001"><triggers><trigger><var>x</var><condition>ANY</condition></trigger></triggers><path><partial><type>CONSTANT</type><conversion>STRING</conversion><value>/launch/vol</value><scaleMin>0</scaleMin><scaleMax>1</scaleMax></partial></path><arguments><partial><type>CONSTANT</type><conversion>STRING</conversion><value>kick</value><scaleMin>0</scaleMin><scaleMax>1</scaleMax></partial><partial><type>VALUE</type><conversion>FLOAT</conversion><value>x</value><scaleMin>0</scaleMin><scaleMax>1</scaleMax></partial></arguments></osc></messages><children /></node></children></node></lexml>