docs: capture touchosc v3 tosc schema ground truth
This commit is contained in:
@@ -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.
@@ -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>
|
||||
Reference in New Issue
Block a user