Skip to content

Commit 36fb78c

Browse files
authored
Merge pull request #126 from mgth/main
release: promote main → release for 0.4.1
2 parents 3bd6adb + 6a2cdcf commit 36fb78c

72 files changed

Lines changed: 5100 additions & 1025 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/ci.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,14 @@ jobs:
114114
working-directory: omniphony-studio
115115
run: npm run build
116116

117+
# Translation parity is reported but never blocks a merge: incomplete
118+
# locales fall back to English at runtime, so this is a heads-up, not a
119+
# gate. Emits ::warning:: annotations on the PR.
120+
- name: Check i18n parity (warn-only)
121+
working-directory: omniphony-studio
122+
continue-on-error: true
123+
run: npm run i18n:check
124+
117125
build-macos:
118126
# macOS arm64 compile gate. The renderer workspace pulls the CoreAudio
119127
# output backend (cpal) and the `sys` signal/IO layer, both of which carry

docs/osc-control-contract.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,24 @@ latency. See `PI_TUNING_PROCEDURE.md` and `docs/latency-regulation.md`.
119119
| `/control/render/bridge_path` | s | Path to the format bridge library. |
120120
| `/control/render/input_pipe` | s | Named-pipe input path. |
121121

122+
### Head tracking (binaural)
123+
124+
Live head-pose control for the binaural (headphone) path. The orientation
125+
*feed* itself arrives on a **user-configured** address
126+
(`head_tracking.osc_address`, e.g. `/gamerotationvector` for Sensors2OSC /
127+
`nxosc`) parsed per `head_tracking.format` — that is config, not a fixed
128+
contract address. See `omniphony-renderer/BINAURAL.md`.
129+
130+
| Address | Args | Meaning |
131+
|---|---|---|
132+
| `/control/head/orientation` | f×3 (euler °) | Set head pose directly (yaw, pitch, roll). |
133+
| `/control/head/quat` | f×4 | Set head pose directly (quaternion). |
134+
| `/control/head/recenter` || Capture the current orientation as "front". |
135+
| `/control/head/tracking/address` | s | Feed address the engine listens on (`""` disables tracking). |
136+
| `/control/head/tracking/format` | s | `auto` \| `quat` \| `rotvec` \| `euler`. |
137+
| `/control/head/tracking/smoothing` | f `[0,0.99]` | Pose smoothing (higher = smoother/laggier). |
138+
| `/control/head/tracking/invert` | int bool | Mirror the applied rotation. |
139+
122140
### Layout
123141

124142
| Address | Args | Meaning |
@@ -168,6 +186,8 @@ exhaustive machine-readable list.
168186
`render/bridge_path`, `render/bridge_error`, `vbap/allow_negative_z`,
169187
`render_evaluation/*` (mirrors of the control resolutions), `speakers`,
170188
`speakers/recomputing`, `speakers/recompute_error`, `layout`.
189+
- **Head tracking**`head_pose` (4-float quaternion `w,x,y,z`, broadcast at
190+
~30 Hz while a tracking feed is active, for low-latency client display).
171191
- **Metering / timing**`clip`, `decode_time_ms`, `render_time_ms`,
172192
`write_time_ms`, `crossover_time_ms`, `frame_duration_ms`, `monitoring`,
173193
`loudness`, `realtime/{master_gain,object_gain,speaker_gain}`.

omniphony-renderer/BINAURAL.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,22 @@ the phone strapped to the headband:
8989
`smoothing` (0–0.99, default 0.2) trades a little latency for pose stability;
9090
with Game Rotation Vector you can usually lower it.
9191

92+
### Other sources
93+
94+
The setup above uses a phone, but any OSC orientation source works. For the
95+
**Waves Nx Head Tracker** (Bluetooth LE, Linux/BlueZ) there is a small Rust
96+
CLI — **[`nxosc`](https://github.com/mgth/nx-tracker-osc)** — that decodes the
97+
tracker and emits the same `/gamerotationvector` feed, so it drops straight
98+
into the steps above in place of Sensors2OSC:
99+
100+
```sh
101+
nxosc run --profile omniphony --osc-address /gamerotationvector --osc-target 127.0.0.1:9000
102+
```
103+
104+
Keep `head_tracking.osc_address: /gamerotationvector` and `format: auto`.
105+
`nxosc` also has a `--profile scenerotator` mode to drive an IEM SceneRotator
106+
directly instead.
107+
92108
## Usage tips
93109

94110
- **The room is YOUR room, not the scene's.** The reflections and the reverb

omniphony-renderer/audio_output/src/control.rs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,15 @@ pub struct RequestedAudioOutputConfig {
1515
pub latency_target_ms: Option<u32>,
1616
pub adaptive_enabled: bool,
1717
pub adaptive: AdaptiveResamplingConfig,
18+
/// Live-requested output backend id ("pipewire" / "asio" / "file"), or
19+
/// `None` to keep the active one. Parsed by the CLI's runtime sync layer
20+
/// (this crate stays decoupled from the CLI's `OutputBackend` enum).
21+
pub output_backend: Option<String>,
22+
/// Live-requested destination for the `file` backend (`-` = stdout, or a
23+
/// file/FIFO path).
24+
pub output_file: Option<String>,
25+
/// Live-requested `file` backend encoding ("raw_f32" / "caf").
26+
pub output_file_format: Option<String>,
1827
}
1928

2029
impl Default for RequestedAudioOutputConfig {
@@ -25,6 +34,9 @@ impl Default for RequestedAudioOutputConfig {
2534
latency_target_ms: None,
2635
adaptive_enabled: false,
2736
adaptive: AdaptiveResamplingConfig::default(),
37+
output_backend: None,
38+
output_file: None,
39+
output_file_format: None,
2840
}
2941
}
3042
}
@@ -88,6 +100,30 @@ impl AudioControl {
88100
self.requested_snapshot().output_device
89101
}
90102

103+
pub fn set_requested_output_backend(&self, backend: Option<String>) {
104+
self.update_requested(|requested| requested.output_backend = backend);
105+
}
106+
107+
pub fn requested_output_backend(&self) -> Option<String> {
108+
self.requested_snapshot().output_backend
109+
}
110+
111+
pub fn set_requested_output_file(&self, output_file: Option<String>) {
112+
self.update_requested(|requested| requested.output_file = output_file);
113+
}
114+
115+
pub fn requested_output_file(&self) -> Option<String> {
116+
self.requested_snapshot().output_file
117+
}
118+
119+
pub fn set_requested_output_file_format(&self, format: Option<String>) {
120+
self.update_requested(|requested| requested.output_file_format = format);
121+
}
122+
123+
pub fn requested_output_file_format(&self) -> Option<String> {
124+
self.requested_snapshot().output_file_format
125+
}
126+
91127
pub fn set_requested_output_sample_rate(&self, rate_hz: Option<u32>) {
92128
self.update_requested(|requested| requested.output_sample_rate_hz = rate_hz);
93129
}

0 commit comments

Comments
 (0)