chore: project brief and docs

main
Samuel Zielke 2 days ago
commit 86d1b44000

@ -0,0 +1,2 @@
Lies docs/05-testplan-hardware.md und docs/06-netzwerk.md.
Erstelle für den aktuellen Stand der App eine Checkliste, die ich im Testaufbau abarbeiten kann: welche Geräte, welche IP-Adressen, welche Szene, welche Messung (Latenz, CPU-Last, Aussetzer über Zeit). Ergänze fehlende Schritte direkt in docs/05-testplan-hardware.md.

@ -0,0 +1,6 @@
Lies docs/04-fahrplan.md und finde den ersten Meilenstein, der noch nicht abgehakt ist.
1. Fasse Ziel und Abnahmetest in 3 Sätzen zusammen.
2. Mach einen Plan in 3–6 Schritten und warte auf mein OK.
3. Setze um, mit Tests für reine Logik.
4. Beschreibe am Ende genau, wie ich den Abnahmetest durchführe (Geräte, Klicks, worauf achten).
5. Hake erst ab, wenn ich den Test als bestanden bestätige.

@ -0,0 +1 @@
Gib mir einen kurzen Stand: welcher Meilenstein ist offen, was ist fertig, was ist ungetestet, welche offenen Fragen stehen in den docs. Maximal 10 Zeilen.

20
.gitignore vendored

@ -0,0 +1,20 @@
# macOS
.DS_Store
# Xcode
build/
DerivedData/
*.xcuserstate
xcuserdata/
*.xcscmblueprint
*.moved-aside
# SwiftPM
.build/
.swiftpm/
# NDI SDK – Lizenz: nicht ins Repo
Vendor/NDI/
# Zertifikate und Schlüssel (Intercom HTTPS)
*.pem
*.key
*.p12
Secrets/
Vendor/ATEM/

@ -0,0 +1,52 @@
# CLAUDE.md – Musubi
Du arbeitest an **Musubi**, einer nativen macOS-App von Zielke Studio für kleine Live-Übertragungen. Lies vor jeder Aufgabe die relevanten Dateien in `docs/`. Der Fahrplan in `docs/04-fahrplan.md` ist verbindlich: Ein Meilenstein gilt erst als fertig, wenn sein Abnahmetest bestanden ist.
## Kontext in drei Sätzen
Ein ATEM steht im OP, die Regie sitzt im Saal, dazwischen liegt eine einzige Netzwerkleitung. Zwei ZowieBoxen schicken Multiview und PGM als NDI|HX3 ins Netz. Musubi läuft auf dem MacBook (Regie) und dem Mac mini (Bühne), zeigt die NDI-Bilder auf Bildschirmen, legt Ton auf Kanäle des MOTU M2 und stellt später ein Intercom fürs Handy bereit.
## Technische Leitplanken
- **Plattform:** macOS, Deployment Target **macOS 14**, **Universal Build** (arm64 + x86_64). Der Mac mini 2018 (Intel, macOS 15 Sequoia) ist Zielgerät und muss jede Version flüssig ausführen.
- **Sprache:** Swift 6, SwiftUI für die Oberfläche, AppKit wo nötig (Vollbildfenster auf bestimmten `NSScreen`, Anzeigemodi).
- **Abhängigkeiten:** Keine Drittanbieter-Pakete ohne Rückfrage. Erlaubt ohne Rückfrage: Apple-Frameworks, das NDI SDK (C-Bibliothek, via Modulemap/Bridging), später das Blackmagic ATEM SDK.
- **NDI SDK:** liegt lokal in `Vendor/NDI/` und wird nie eingecheckt. Fehlt es, brich nicht ab, sondern erkläre, was zu tun ist (`Vendor/README.md`).
- **Module** (lokale Swift Packages oder Gruppen, siehe `docs/02-architektur.md`):
- `MusubiCore` – Modelle, Szenen, Einstellungen (reine Logik, testbar)
- `NDIKit` – dünner Swift-Wrapper ums NDI SDK (Finder, Receiver, Audio-only)
- `VideoOutput` – Darstellung per Metal bzw. `AVSampleBufferDisplayLayer`, Vollbild je Bildschirm, 50 Hz
- `AudioMatrix` – Core Audio / `AVAudioEngine`, Quelle → Ausgangskanal
- `Remote` – Regie steuert Knoten per Bonjour (ab Meilenstein 3)
- `Intercom` – HTTPS + WebRTC, nur an die Klinik-Schnittstelle gebunden (ab Meilenstein 4)
- `Tally` – ATEM-Status (ab Meilenstein 6)
- **Echtzeit:** Nie Video- oder Audioarbeit auf dem Main Thread. Keine Allokationen im Audio-Render-Callback.
- **Betriebssicherheit:** Ruhezustand per `IOPMAssertion` verhindern, solange eine Szene läuft. Bei Verbindungsabbruch automatisch neu verbinden. Beim Start letzte Szene laden.
- **Netze trennen:** Technik-Netz und Klinik-Netz werden nie verbunden. Dienste binden explizit an eine Schnittstelle. Keine Internetfreigabe.
## Design
Halte dich an `docs/03-design.md` und `design/Theme.swift`. Kurz: dunkle Regie-Oberfläche, Indigo als einzige Markenfarbe, Rot/Grün ausschließlich für Tally (PGM/Vorschau), SF Pro in der App, SF Mono für Zahlen. Texte auf Deutsch, in Du-Form, kurz, im Stil von apps.zielke.studio („NDI rein. Bild, Ton und Stimme raus.“).
## Arbeitsweise
1. Vor Code: kurzer Plan in 3–6 Punkten, dann umsetzen.
2. Kleine Schritte, nach jedem lauffähigen Stand committen (Conventional Commits auf Englisch).
3. Reine Logik (Szenen, Kanalzuordnung, Parser) bekommt Unit-Tests mit Swift Testing.
4. Was sich nur mit Hardware prüfen lässt, beschreibst du als Schritt in `docs/05-testplan-hardware.md`, statt es zu behaupten.
5. Nach jedem Meilenstein: Checkbox in `docs/04-fahrplan.md` abhaken, kurze Notiz unter „Erkenntnisse“ ergänzen.
6. Code und Kommentare auf Englisch, UI-Texte auf Deutsch über einen String Catalog.
7. Wenn etwas unklar ist (Lizenz, Netz der Klinik, Hardware-Verhalten): fragen, nicht raten.
## Testen ohne Hardware
- **NDI-Quellen:** NDI Tools (Test Patterns, Screen Capture) auf einem zweiten Mac oder OBS mit DistroAV als NDI-Ausgang.
- **Audio:** MOTU M2 ist vorhanden; ohne M2 das eingebaute Ausgabegerät mit zwei Kanälen nutzen.
- **ATEM:** vorhanden, für Tally ab Meilenstein 6.
- **Hardware im Zulauf:** 2× ZowieBox, 2× TP-Link TL-SG2008P, MT-VIKI Dante-Adapter, Soundcraft Ui12.
## Nicht tun
- Kein Senden/Encoding von NDI, keine Aufnahme, keine Bildmischung (macht der ATEM).
- Keine Cloud-Dienste. Ein Relay (TURN) ist nur Plan B und läuft auf dem eigenen Server.
- Keine Funktionen eines späteren Meilensteins vorziehen.

@ -0,0 +1,38 @@
# Musubi
**NDI rein. Bild, Ton und Stimme raus.**
Musubi (結び, japanisch: das Knüpfen, Verbinden) ist eine macOS-App von Zielke Studio für kleine Live-Übertragungen. Sie macht MacBook und Mac mini zu den fehlenden Bausteinen einer Regie:
- NDI-Quellen im Vollbild auf beliebige Bildschirme
- NDI-Ton (auch ohne Bild) gezielt auf einzelne Kanäle eines Audio-Interfaces
- Talkback per Sprechtaste
- Intercom, PGM-Vorschau und Tally fürs Handy im Browser
- Szenen, die Regie-Mac und Bühnen-Mac auf einen Klick einrichten
Erster Einsatz: OP-Liveübertragung (ATEM im OP, Regie im Saal, eine Netzwerkleitung dazwischen).
## Status
Planung abgeschlossen, Entwicklung startet mit Meilenstein 0. Siehe [docs/04-fahrplan.md](docs/04-fahrplan.md).
## Dokumente
| Datei | Inhalt |
|---|---|
| [CLAUDE.md](CLAUDE.md) | Arbeitsanweisungen für Claude Code |
| [START.md](START.md) | Startbefehl für Claude Code |
| [docs/01-konzept.md](docs/01-konzept.md) | Idee, Rollen, Funktionsumfang |
| [docs/02-architektur.md](docs/02-architektur.md) | Module, Frameworks, Datenflüsse |
| [docs/03-design.md](docs/03-design.md) | Designsystem, Farben, Typografie, Texte |
| [docs/04-fahrplan.md](docs/04-fahrplan.md) | Meilensteine mit Abnahmetests |
| [docs/05-testplan-hardware.md](docs/05-testplan-hardware.md) | Tests, sobald die Hardware da ist |
| [docs/06-netzwerk.md](docs/06-netzwerk.md) | IP-Plan, Schnittstellen, Relay als Plan B |
## Voraussetzungen
- Xcode (aktuell), macOS auf Apple Silicon zum Entwickeln
- NDI SDK für macOS (nicht im Repo, siehe [Vendor/README.md](Vendor/README.md))
- Zielgeräte: MacBook (Apple Silicon) und Mac mini 2018 (Intel, macOS 15) → Universal Build
© Zielke Studio

@ -0,0 +1,21 @@
# Start mit Claude Code
## Einmalig vorbereiten
```bash
cd ~/Developer/musubi # dein Repo
git init && git add . && git commit -m "chore: project brief and docs"
# NDI SDK installieren und nach Vendor/NDI/ kopieren (siehe Vendor/README.md)
```
## Startbefehl
```bash
claude "Lies CLAUDE.md und alle Dateien in docs/. Fasse in 5 Sätzen zusammen, was Musubi ist und was Meilenstein 0 verlangt. Mach dann einen Plan für Meilenstein 0 (Xcode-Projekt als Universal Build für macOS 14, Modulstruktur laut docs/02-architektur.md, NDI SDK einbinden, Designsystem aus design/Theme.swift, leeres Hauptfenster mit Quellenliste). Warte auf mein OK, bevor du Dateien anlegst."
```
## Danach
- Nächster Meilenstein: `/meilenstein` (Slash-Command in `.claude/commands/`)
- Hardware-Test vorbereiten: `/hardwaretest`
- Stand prüfen: `/status`

21
Vendor/README.md vendored

@ -0,0 +1,21 @@
# Vendor
## NDI SDK
Das NDI SDK wird **nicht** eingecheckt (Lizenzbedingungen).
1. NDI SDK für macOS von ndi.video herunterladen und installieren.
2. Header und `libndi` nach `Vendor/NDI/` kopieren:
```
Vendor/NDI/include/Processing.NDI.Lib.h (+ übrige Header)
Vendor/NDI/lib/libndi.dylib (Universal: arm64 + x86_64)
```
3. Prüfen, dass die Bibliothek beide Architekturen enthält:
```
lipo -info Vendor/NDI/lib/libndi.dylib
```
4. Lizenz- und Namensregeln (NDI®-Kennzeichnung, Weitergabe) vor jedem Verkauf prüfen.
## Blackmagic ATEM Switchers SDK (ab Meilenstein 6)
Aus dem Blackmagic Support Center laden (Developer SDK zu ATEM Switchers). Ablage in `Vendor/ATEM/`, nicht einchecken, falls die Lizenz es verlangt.

@ -0,0 +1,49 @@
import SwiftUI
/// Musubi design tokens. Source of truth: design/tokens.json and docs/03-design.md.
enum Theme {
enum Color {
static let sumi = adaptive(dark: 0x14151B, light: 0xF4F4F6) // background
static let kasumi = adaptive(dark: 0x1E2029, light: 0xFFFFFF) // surface
static let usuzumi = adaptive(dark: 0x2B2E3A, light: 0xDADBE2) // lines
static let washi = adaptive(dark: 0xECE9E2, light: 0x16171D) // text
static let nezumi = adaptive(dark: 0x8E8C96, light: 0x6A6974) // muted text
static let ai = adaptive(dark: 0x8A99F2, light: 0x4F63D9) // the one brand accent
// Status colours – only for states, never decoration.
static let shu = SwiftUI.Color(hex: 0xE5484D) // tally PGM, talk active
static let moegi = SwiftUI.Color(hex: 0x3DBE7A) // tally preview, connection ok
static let kohaku = SwiftUI.Color(hex: 0xE8A23A) // warning
}
enum Radius { static let card: CGFloat = 12, button: CGFloat = 8, chip: CGFloat = 6 }
enum Space { static let unit: CGFloat = 8, card: CGFloat = 16 }
enum Stroke { static let line: CGFloat = 1, cord: CGFloat = 2, knot: CGFloat = 6 }
enum Font {
static let title = SwiftUI.Font.system(.title3, weight: .semibold)
static let body = SwiftUI.Font.system(.body)
static let caption = SwiftUI.Font.system(.caption)
static let number = SwiftUI.Font.system(.body, design: .monospaced).monospacedDigit()
}
private static func adaptive(dark: UInt32, light: UInt32) -> SwiftUI.Color {
SwiftUI.Color(nsColor: NSColor(name: nil) { appearance in
appearance.bestMatch(from: [.darkAqua, .aqua]) == .darkAqua
? NSColor(hex: dark) : NSColor(hex: light)
})
}
}
extension SwiftUI.Color {
init(hex: UInt32) { self.init(nsColor: NSColor(hex: hex)) }
}
extension NSColor {
convenience init(hex: UInt32) {
self.init(srgbRed: CGFloat((hex >> 16) & 0xFF) / 255,
green: CGFloat((hex >> 8) & 0xFF) / 255,
blue: CGFloat(hex & 0xFF) / 255,
alpha: 1)
}
}

@ -0,0 +1,21 @@
{
"name": "Musubi",
"color": {
"sumi": { "dark": "#14151B", "light": "#F4F4F6", "role": "background" },
"kasumi": { "dark": "#1E2029", "light": "#FFFFFF", "role": "surface" },
"usuzumi": { "dark": "#2B2E3A", "light": "#DADBE2", "role": "line" },
"washi": { "dark": "#ECE9E2", "light": "#16171D", "role": "text" },
"nezumi": { "dark": "#8E8C96", "light": "#6A6974", "role": "textMuted" },
"ai": { "dark": "#8A99F2", "light": "#4F63D9", "role": "accent" },
"shu": { "value": "#E5484D", "role": "statusLive" },
"moegi": { "value": "#3DBE7A", "role": "statusPreview" },
"kohaku": { "value": "#E8A23A", "role": "statusWarning" }
},
"radius": { "card": 12, "button": 8, "chip": 6 },
"spacing": { "unit": 8, "cardPadding": 16 },
"stroke": { "line": 1, "cord": 2, "knot": 6 },
"type": {
"app": { "ui": "SF Pro", "numbers": "SF Mono" },
"brand": { "heading": "Kanit 600", "body": "Kanit 400", "highlight": "Instrument Serif Italic" }
}
}

@ -0,0 +1,44 @@
# Konzept
Musubi macht MacBook und Mac mini zu den fehlenden Bausteinen einer kleinen Übertragungsregie: NDI-Bilder auf Bildschirme, NDI-Ton auf Kanäle eines Audio-Interfaces, Talkback und ein Intercom fürs Handy – in einer App mit gespeicherten Szenen.
## Problem
Dieselbe Aufgabe braucht heute mehrere Programme: NDI Tools oder OBS fürs Bild, Loopback oder BlackHole fürs Audio-Routing, und fürs Intercom gibt es nichts Passendes. Im OP ist klassisches Intercom wegen Kabeln, Sterilität und Funkfreigabe besonders schwierig.
## Rollen
| Rolle | Gerät | Aufgaben |
|---|---|---|
| Regie | MacBook am Regieplatz | Multiview anzeigen, PGM-Ton und Talkback auf MOTU-Kanäle legen, Intercom-Server, Tally aus dem ATEM, Szenen laden, Knoten steuern |
| Knoten | Mac mini (2018, Intel) auf der Bühne | PGM auf den Saal-Screen, Multiview auf den Moderator-Monitor; folgt den Szenen der Regie |
| Gast | Handy im OP (Browser) | Talkback hören, per Taste sprechen, PGM-Vorschau, Tally der eigenen Kamera |
Regie und Knoten finden sich per Bonjour im Technik-Netz. Fällt die Regie aus, zeigt der Knoten seine letzte Szene weiter.
## Funktionen Version 1
- **Quellen:** NDI-Quellen automatisch finden, Liste mit kleiner Vorschau.
- **Bildausgabe:** Quelle → Bildschirm, Vollbild ohne Menüleiste, Anzeige auf 50 Hz.
- **Ton-Matrix:** NDI-Ton (auch reiner Audio-Empfang) und Mikrofone auf einzelne Ausgangskanäle, z. B. MOTU M2 Out 1/2; Pegel, Gain, Mute.
- **Talkback:** Headset-Mikro per Sprechtaste auf einen Kanal.
- **Szenen:** komplette Belegung speichern, laden, exportieren („OP-Live“).
- **Betriebssicherheit:** kein Ruhezustand, Auto-Reconnect, letzte Szene beim Start.
- **Status:** Verbindung, verlorene Frames, Pegel je Quelle.
- **Fernsteuerung:** Regie belegt den Knoten.
## Intercom und Tally fürs Handy
Das MacBook stellt über seine zweite Netzwerkschnittstelle (Klinik-Netz bzw. eigenes Netz der IT) eine Web-Seite bereit. Keine App-Installation auf dem Handy.
- **Verbinden:** QR-Code scannen, Name und Rolle wählen („Kamera 2, Handkamera“).
- **Hören:** Talkback der Regie, wahlweise Moderator.
- **Sprechen:** große Sprechtaste; Ton landet in der Ton-Matrix.
- **PGM-Vorschau:** Ziel ca. 640 × 360, 25 fps, unter 0,5 s Verzögerung.
- **Tally:** Rand rot bei PGM, grün bei Vorschau.
Reihenfolge der Netzwege: 1. eigenes Netz der Klinik-IT (lokal, schnellster Weg), 2. Klinik-WLAN, 3. **Plan B: eigener TURN-Relay** (siehe `06-netzwerk.md`).
## Nicht in Version 1
Senden/Encoding von NDI, Aufnahme, Bildmischung, Cloud-Dienste.

@ -0,0 +1,51 @@
# Architektur
## Überblick
```
Technik-Netz Musubi (MacBook, Regie) Ausgänge
────────────── ─────────────────────────── ─────────────────────
ZowieBox MV ─NDI|HX3─▶ NDIKit ──▶ VideoOutput ───────────────▶ Regie-Bildschirm
ZowieBox PGM ─NDI|HX3─▶ │
└──▶ AudioMatrix ──────────────────▶ MOTU M2 Out 1/2 → Ui12
Headset-Mikro ─────────────────▶ AudioMatrix
ATEM ─────────Status───▶ Tally ──▶ Intercom ◀──WebRTC──▶ Handy im OP (Klinik-Netz)
MusubiCore (Szenen) ─Bonjour─▶ Mac mini (Knoten, Bühne)
```
Der Mac mini empfängt NDI selbst direkt aus dem Technik-Netz und wird von der Regie nur gesteuert.
## Module
| Modul | Aufgabe | Technik |
|---|---|---|
| MusubiCore | Modelle (Quelle, Ausgang, Route, Szene), Persistenz, Einstellungen | Swift, Codable/JSON, Swift Testing |
| NDIKit | Quellen finden, empfangen (Video+Audio oder nur Audio), Statistik | NDI SDK (C), `NDIlib_find_*`, `NDIlib_recv_*`, Bandbreite `audio_only` für reinen Ton |
| VideoOutput | Frames darstellen, ein Vollbildfenster je `NSScreen`, Anzeigemodus 50 Hz | Metal oder `AVSampleBufferDisplayLayer`, `CGDisplayCopyAllDisplayModes` |
| AudioMatrix | Quellen → Ausgangskanäle, Gain, Mute, Pegel, Sprechtaste | Core Audio / `AVAudioEngine`, Kanalzuordnung (`channelMap`) |
| Remote | Regie ↔ Knoten: Szene setzen, Status melden | Bonjour (`NWListener`/`NWBrowser`), JSON-Nachrichten |
| Intercom | Web-Seite, Signalisierung, Audio hin/zurück, PGM-Vorschau | eingebetteter HTTPS-Server, WebRTC (Opus, H.264 via VideoToolbox), Bindung an Schnittstelle |
| Tally | PGM/Vorschau-Eingänge des ATEM lesen | Blackmagic ATEM Switchers SDK |
## Datenmodell (Entwurf)
```swift
struct Source: Codable, Hashable { var ndiName: String; var label: String }
enum Output: Codable, Hashable { case screen(displayID: UInt32), audioChannel(deviceUID: String, channel: Int) }
struct Route: Codable, Hashable { var source: Source; var output: Output; var mode: Mode
enum Mode: String, Codable { case videoAndAudio, videoOnly, audioOnly } }
struct Scene: Codable, Identifiable { var id: UUID; var name: String; var role: Role; var routes: [Route] }
enum Role: String, Codable { case control, node }
```
## Threads
- NDI-Empfang je Quelle auf eigener Queue.
- Video: Frames ohne Kopie an die Darstellung (CVPixelBuffer), Rendern im Takt des Displays.
- Audio: Ringpuffer zwischen NDI-Empfang und Render-Callback, keine Allokationen im Callback, Drift-Ausgleich (Resampling) zwischen NDI-Takt und Interface-Takt.
## Netzwerk-Bindung
- NDI, Bonjour, Remote: nur Technik-Schnittstelle.
- Intercom: nur Klinik-Schnittstelle (oder TURN-Relay nach außen).
- Schnittstellen in den Einstellungen per Name wählbar (z. B. `en7` Technik, `en0` Klinik).

@ -0,0 +1,73 @@
# Design
Musubi gehört zur Familie von apps.zielke.studio (Patchcord, drop.two, drop.look): gleiche Typografie, gleiche Tonalität, gleiche Zurückhaltung – aber eine **eigene Farbwelt**. Die Farben kommen aus dem japanischen Färbehandwerk (Indigo/Ai-zome), passend zum Namen.
> Hinweis: Die Farben der anderen Apps ließen sich von der Website nicht automatisch auslesen. Vor dem Festlegen einmal nebeneinanderlegen und prüfen, dass Indigo sich klar abhebt.
## Leitidee
Ein Knoten verbindet Fäden. In Musubi werden Quellen und Ausgänge **geknüpft**: Die zentrale Ansicht zeigt links die Quellen, rechts Bildschirme und Kanäle, dazwischen Schnüre. Eine aktive Verbindung ist eine Indigo-Schnur mit kleinem Knoten an der Stelle, wo sie anliegt. Das ist das eine wiedererkennbare Element; alles andere bleibt ruhig.
## Farben
Regie-Räume sind dunkel. **Dunkel ist Standard**, hell ist unterstützt.
| Name | Rolle | Dunkel | Hell |
|---|---|---|---|
| Sumi (Tusche) | Hintergrund | `#14151B` | `#F4F4F6` |
| Kasumi (Dunst) | Flächen, Karten | `#1E2029` | `#FFFFFF` |
| Usuzumi (verdünnte Tusche) | Linien, Rahmen, gehobene Flächen | `#2B2E3A` | `#DADBE2` |
| Washi (Papier) | Text | `#ECE9E2` | `#16171D` |
| Nezumi (Maus­grau) | gedämpfter Text, Beschriftungen | `#8E8C96` | `#6A6974` |
| **Ai (Indigo)** | **einzige Markenfarbe**: aktive Schnüre, Auswahl, Hauptaktion | `#8A99F2` | `#4F63D9` |
### Signalfarben – nur für Zustände, nie als Deko
| Name | Bedeutung | Wert |
|---|---|---|
| Shu (Zinnober) | Tally PGM / „auf Sendung“, Sprechtaste aktiv | `#E5484D` |
| Moegi (Frühlingsgrün) | Tally Vorschau, Verbindung gut | `#3DBE7A` |
| Kohaku (Bernstein) | Warnung: Frames verloren, Pegel nah an 0 dBFS | `#E8A23A` |
Rot und Grün tauchen ausschließlich in diesen Bedeutungen auf. So bleibt Tally auf einen Blick lesbar.
## Typografie
| Einsatz | Schrift |
|---|---|
| App-Oberfläche | SF Pro (System) – Titel 600, Text 400 |
| Zahlen: Pegel, IP-Adressen, Latenz, Frames | SF Mono, tabellarische Ziffern |
| Website, Onboarding, App-Store-Bilder | Kanit 600 für Überschriften, Kanit 400 für Text |
| Ein hervorgehobenes Wort pro Überschrift | Instrument Serif *italic*, gleiche Größe wie Kanit |
Beispiel-Hero (Website): „Ein Kabel. Alle *Signale*.“
## Formen und Abstände
- Abstände in 8er-Schritten, innerhalb von Karten 16.
- Ecken: Karten 12, Schaltflächen 8, Chips 6.
- Linien 1 pt in Usuzumi. Keine Schatten, keine Verläufe.
- Schnüre in der Knoten-Ansicht: 2 pt, inaktiv Usuzumi, aktiv Ai; Knoten = gefüllter Kreis 6 pt.
## Texte
Stil wie auf apps.zielke.studio: kurz, konkret, Du-Form.
- Untertitel: **„NDI rein. Bild, Ton und Stimme raus.“**
- Begriffe durchgängig: *Quelle, Bildschirm, Kanal, Szene, Regie, Knoten, Sprechtaste, Vorschau, Sendung*.
- Schaltflächen sagen, was passiert: „Szene laden“, „Mit Knoten verbinden“, „Sprechen“.
- Fehler erklären Ursache und Ausweg:
- „Keine NDI-Quellen gefunden. Prüfe, ob das Technik-Netz an `en7` hängt.“
- „ZowieBox PGM antwortet nicht mehr. Neuer Versuch in 3 s.“
- Leere Zustände laden zum Handeln ein: „Noch keine Szene. Zieh eine Quelle auf einen Bildschirm.“
## Bildschirme (Version 1)
1. **Knoten-Ansicht** (Hauptfenster): Quellen links, Ausgänge rechts, Schnüre dazwischen; oben Szenenname, rechts oben Status (Netz, Knoten verbunden, Ruhezustand blockiert).
2. **Ausgabe-Fenster:** randloses Vollbild je Bildschirm; optional dezente Statuszeile nur für die Regie.
3. **Ton:** Kanalzüge je Ausgangskanal mit Pegel, Gain, Mute; Sprechtaste groß unten rechts.
4. **Einstellungen:** Schnittstellen (Technik/Klinik), Audio-Gerät, Startverhalten.
## App-Icon (Idee)
Eine Indigo-Schnur, die einen einfachen Knoten um ein kleines, abgerundetes Bildschirm-Rechteck bildet, auf Sumi-Grund. Kein Text.

@ -0,0 +1,70 @@
# Fahrplan
Jeder Meilenstein endet mit einem Abnahmetest. Erst wenn er bestanden ist, beginnt der nächste. Termine sind offen.
| | Meilenstein | Ohne neue Hardware testbar? |
|---|---|---|
| M0 | Grundgerüst | ja |
| M1 | Bild | ja (NDI Tools / OBS als Quelle) |
| M2 | Ton-Matrix | ja (MOTU M2 vorhanden) |
| M3 | Szenen und Fernsteuerung | ja (MacBook + Mac mini) |
| M4 | Intercom Stufe 1: Hören + PGM-Vorschau | ja (Heimnetz) |
| M5 | Intercom Stufe 2: Sprechen | ja (Heimnetz, HTTPS) |
| M6 | Tally | ja (ATEM vorhanden) |
| M7 | Generalprobe | nein – braucht ZowieBoxen, Switches, Dante, Ui12 |
## M0 – Grundgerüst
- [ ] Xcode-Projekt „Musubi“, macOS 14, Universal Build (arm64 + x86_64), Bundle-ID `studio.zielke.musubi`
- [ ] Modulstruktur laut `02-architektur.md`
- [ ] NDI SDK eingebunden (Modulemap), Version wird beim Start geloggt
- [ ] `design/Theme.swift` übernommen, leeres Hauptfenster mit Quellenliste
- [ ] Swift-Testing-Target mit erstem Test für `MusubiCore`
- **Abnahme:** Release-Build startet auf MacBook **und** Mac mini 2018; die Quellenliste zeigt eine NDI-Testquelle aus dem Netz.
## M1 – Bild
- [ ] NDI-Empfang einer Quelle (HX3 und volles NDI)
- [ ] Vollbild auf gewähltem Bildschirm, ohne Menüleiste, Mauszeiger ausgeblendet
- [ ] Anzeigemodus 50 Hz setzen, wenn verfügbar
- [ ] Zwei Quellen gleichzeitig auf zwei Bildschirmen (Mac mini: PGM + Multiview)
- [ ] Ruhezustand blockieren, Auto-Reconnect
- **Abnahme:** 4 Stunden, 2 Quellen, 2 Bildschirme auf dem Mac mini 2018 – keine sichtbaren Aussetzer, CPU-Last notiert, verlorene Frames < 0,1 %.
## M2 – Ton-Matrix
- [ ] NDI-Ton einer Quelle auf beliebige Ausgangskanäle (z. B. MOTU M2 Out 2)
- [ ] Reiner Audio-Empfang (ohne Video-Bandbreite)
- [ ] Mikrofon-Eingang (Headset) auf Kanal (Out 1), Sprechtaste (Tastenkürzel, gedrückt halten / umschalten)
- [ ] Pegelanzeige, Gain, Mute je Route
- [ ] Drift-Ausgleich zwischen NDI-Takt und Interface-Takt
- **Abnahme:** 4 Stunden ohne Knacksen und ohne hörbaren Versatz; Sprechtaste reagiert ohne Verzögerung.
## M3 – Szenen und Fernsteuerung
- [ ] Szenen speichern, laden, exportieren (JSON)
- [ ] Rolle Regie/Knoten; Knoten per Bonjour finden
- [ ] Regie setzt Szene am Knoten; Knoten meldet Status zurück
- [ ] Beim Start letzte Szene laden
- **Abnahme:** Beide Macs neu starten – die Szene „OP-Live“ stellt sich ohne Eingriff wieder her.
## M4 – Intercom Stufe 1
- [ ] Web-Server nur auf der Klinik-Schnittstelle
- [ ] QR-Code mit Adresse in der App
- [ ] Handy-Seite: Name/Rolle, Talkback hören, PGM-Vorschau (WebRTC)
- **Abnahme:** iPhone und Android im Heimnetz, Verzögerung der Vorschau gemessen < 0,5 s.
## M5 – Intercom Stufe 2
- [ ] HTTPS mit eigener Stammzertifizierung (Installationsanleitung fürs Handy)
- [ ] Sprechtaste auf dem Handy → Ton-Matrix
- [ ] Optional: TURN-Relay als Plan B konfigurierbar (`06-netzwerk.md`)
- **Abnahme:** Gegensprechen iPhone ↔ Regie und Android ↔ Regie im selben Durchgang.
## M6 – Tally
- [ ] ATEM-Status (PGM/Vorschau) lesen
- [ ] Kamera-Eingang einer Handy-Rolle zuordnen
- [ ] Handy-Rand rot (PGM) / grün (Vorschau)
- **Abnahme:** Schnitt am Micro Panel färbt das richtige Handy in unter 200 ms.
## M7 – Generalprobe
- [ ] Kompletter Aufbau laut `05-testplan-hardware.md`
- **Abnahme:** 3 Stunden durchgehender Betrieb mit allen Geräten; OBS + DistroAV bleibt als Notlösung auf beiden Macs installiert.
## Erkenntnisse
_(nach jedem Meilenstein ergänzen)_

@ -0,0 +1,37 @@
# Testplan Hardware
Sobald die bestellte Hardware da ist, wird der echte Aufbau zu Hause nachgestellt – zuerst ohne Musubi (Basis), dann mit.
## Geräte
| Gerät | Status |
|---|---|
| ATEM Mini Extreme ISO G2 + Micro Panel | vorhanden |
| MacBook (Apple Silicon), Mac mini 2018 (Intel) | vorhanden |
| MOTU M2 | vorhanden |
| 2× ZowieBox (NDI|HX3) | bestellt |
| 2× TP-Link TL-SG2008P | bestellt |
| MT-VIKI Dante-Adapter (Ein- und Ausgang) | bestellt |
| Soundcraft Ui12 | bestellt |
| langes Netzwerkkabel (Ersatz für die Klinikleitung) | besorgen |
## Phase A – Basis ohne Musubi
- [ ] Switches: EEE aus, QoS DSCP, IGMP Snooping an, feste IPs, Konfig auf beide übertragen
- [ ] `iperf3` zwischen den Switches: stabil ~940 Mbit/s
- [ ] ZowieBoxen: 1080p50, NDI|HX3, Audioquelle HDMI; MV-Stream trägt PGM-Ton
- [ ] NDI Tools: beide Streams auf MacBook und Mac mini sichtbar
- [ ] Dante Controller: MT-VIKI erkannt, Pegel einstellbar, PoE, nach Stromausfall von selbst wieder da
- [ ] Ui12 im Browser über das Technik-Netz erreichbar
## Phase B – mit Musubi (je nach Meilenstein)
- [ ] M1: Mac mini zeigt PGM + Multiview 4 h ohne Aussetzer; CPU-Last: ___ %
- [ ] M1: Latenz Multiview messen (Uhr mit Millisekunden vor Kamera → ATEM → ZowieBox → Musubi): ___ ms
- [ ] M2: PGM-Ton über M2 Out 2 → Ui12 Kanal 4; Talkback M2 Out 1 → Ui12 Kanal 3 (nur Aux 2)
- [ ] M3: Szene „OP-Live“ übersteht Neustart beider Macs
- [ ] M4/M5: Handy über zweite Schnittstelle: Hören, Sprechen, Vorschau
- [ ] M6: Tally auf dem Handy folgt dem Micro Panel
## Messwerte
_(hier eintragen)_

@ -0,0 +1,41 @@
# Netzwerk
## Zwei Netze, strikt getrennt
| Netz | Schnittstelle MacBook | Dienste |
|---|---|---|
| Technik-Netz (eigene Switches, 1:1-Strecke) | USB-C-Ethernet-Adapter, z. B. `en7` | NDI, Dante, ATEM-Steuerung, Ui12, Bonjour (Musubi Remote) |
| Klinik-Netz bzw. eigenes Netz der IT | WLAN oder zweiter Adapter, z. B. `en0` | nur Musubi Intercom (HTTPS/WebRTC) |
Keine Internetfreigabe, kein Routing zwischen den Netzen. Musubi bindet jeden Dienst explizit an eine Schnittstelle.
## IP-Plan Technik-Netz (Vorschlag)
| Adresse | Gerät |
|---|---|
| 192.168.10.2 | Switch OP |
| 192.168.10.3 | Switch Saal |
| 192.168.10.10 | ATEM Mini Extreme ISO G2 |
| 192.168.10.21 | ZowieBox Multiview |
| 192.168.10.22 | ZowieBox PGM |
| 192.168.10.30 | Soundcraft Ui12 |
| 192.168.10.40 | Mac mini (Knoten) |
| 192.168.10.50 | MacBook (Regie) |
| automatisch | Dante-Adapter (Link-Local, eigene Erkennung) |
Maske 255.255.255.0, kein Gateway.
## Intercom: Reihenfolge der Wege
1. **Eigenes Netz der Klinik-IT** – lokal, schnellster Weg. Bevorzugt.
2. **Klinik-WLAN** – nur wenn Geräte sich dort gegenseitig erreichen dürfen (keine Client-Isolation).
3. **Plan B: TURN-Relay auf eigenem Server**
- coturn im Homelab, erreichbar über TLS auf Port 443 (kommt durch fast jede Firewall)
- Handy und MacBook verbinden sich beide nach außen; WebRTC nutzt das Relay nur, wenn kein direkter Weg geht
- Zusätzliche Verzögerung grob 40–80 ms innerhalb Deutschlands
- Medien bleiben Ende-zu-Ende verschlüsselt (DTLS-SRTP); **kein SFU**, das die Medien entschlüsseln würde
- OP-Bild verlässt damit das Klinik-Netz → vorher mit Klinik und Datenschutz abstimmen
## Bandbreite
NDI|HX3 1080p: ca. 30–80 Mbit/s je Abruf. Jeder Empfänger bekommt eine eigene Kopie. Planungsgrenze: 70 % der Gigabit-Strecke pro Richtung.
Loading…
Cancel
Save

Powered by TurnKey Linux.