Files
onyx/docs/spikes/C-audiotap.md
Guido Schmit 41ee3cc174 Phase 0: Repo-Gerüst und vier Verifikations-Spikes
Vier Annahmen des Plans vor jedem Implementierungscode geprüft:

A (SMC):  Lüftersteuerung bestätigt — F0md/F1md (klein!) als Auto/Manuell-
          Umschalter, F0Tg als Ziel, Grenzen aus F0Mn/F0Mx. SMC-Integer sind
          little-endian, nicht big-endian wie im verbreiteten Intel-Beispielcode.
          Ladelimit: CHWA/CH0B/CH0C/bfE0/bfF0 existieren auf Mac17,9 nicht.
          Key noch nicht identifiziert, deshalb smc-diff.sh für eine
          Differenzmessung ohne jeden Schreibzugriff.
B (Media): mediaremote-adapter läuft unter 26.6.1, aus dem Quellcode gebaut.
          Liefert vollständige Metadaten inkl. Cover aus Google Chrome — der
          Fall, den AppleScript nicht erreicht.
C (Audio): Process Tap + privates Aggregate-Device liefern echte Samples
          (469 Callbacks/5 s, 0 stille Puffer). Konflikt gefunden: SoundSource
          6.1.1 mit ARK.driver ist bereits installiert.
D (Group): App-Group-Container ist aus einem nicht-sandboxed Prozess erreichbar.
          Damit trägt die Sandbox-Entscheidung, die Bridge-App entfällt.

Spikes sind Wegwerfcode zur Verifikation, kein Produktionscode.
2026-08-10 16:47:54 +02:00

3.1 KiB

Spike C — Core Audio Process Taps auf M5 Pro / macOS 26.6.1

Gemessen am 10.08.2026. Werkzeug: Spikes/audiotap-probe.swift, signiert mit dem Team-Zertifikat. Der Tap lief mit muteBehavior = .unmuted — der Spike hört nur zu und verändert die Wiedergabe nicht.

Ergebnis: bestätigt

Schritt Ergebnis
kAudioHardwarePropertyProcessObjectList 35 Audio-Prozesse, mit PID, Bundle-ID und IsRunningOutput
AudioHardwareCreateProcessTap erzeugt, Tap-Objekt 195, Format 48 kHz / 2 Kanäle / 32 bit Float
AudioHardwareCreateAggregateDevice (privat) erzeugt, Device 196
IOProc über 5 s 469 Callbacks (93/s), 480 256 Frames, Spitzenpegel 0,4516, 0 stille Puffer

480 256 Frames ÷ 2 Kanäle ÷ 5 s = 48 025 Hz — deckt sich exakt mit der Abtastrate. 93 Callbacks/s entsprechen ~512 Frames je Puffer, also rund 10,7 ms Latenz. Für einen Lautstärkemixer unkritisch.

Der bekannte Fehlerfall „Puffer laufen nach einiger Zeit auf Null" trat im 5-Sekunden-Fenster nicht auf. Das widerlegt ihn nicht — der Watchdog aus dem Plan bleibt.

Kein TCC-Dialog erschienen. Das ist noch kein Freibrief: .unmuted ist der harmlose Fall. Ob .mutedWhenTapped — der Modus, den der Mixer wirklich braucht — eine Berechtigung anfordert, muss in Phase 7 gegengeprüft werden.

Fallstrick beim Bauen

Die verbreitete Kurzform für Core-Audio-Property-Arrays

var out = [T](repeating: unsafeBitCast(0 as Int, to: T.self), count: count)

stürzt mit SIGTRAP ab: unsafeBitCast verlangt identische Größen, Int hat 8 Byte, AudioObjectID 4. Da stdout in eine Pipe voll gepuffert ist, kam nicht einmal die erste Zeile Ausgabe — der Absturz sah aus wie ein stiller Fehlstart. MetricsProvider und AudioProvider verwenden deshalb die Variante über UnsafeMutableRawPointer.allocate mit MemoryLayout<T>.stride.

Konflikt: SoundSource ist bereits installiert

Die Prozessliste zeigt com.rogueamoeba.soundsource und com.rogueamoeba.arkaudiod, dazu liegt /Library/Audio/Plug-Ins/HAL/ARK.driver im System. SoundSource 6.1.1 ist Rogue Amoebas Per-App-Lautstärkemixer — funktional genau das, was Phase 7 bauen soll.

Zwei Folgen:

  1. Doppelung. Der Mixer aus Phase 7 wäre ein Nachbau einer bereits vorhandenen, ausgereiften und bezahlten App.
  2. Technisches Risiko. ARK.driver ist ein HAL-Plugin, das Audio abfängt, bevor es das Ausgabegerät erreicht. Leitet SoundSource einen Prozess bereits um, sieht ein Onyx-Process-Tap auf denselben Prozess unter Umständen nichts mehr — oder beide Ebenen schalten sich gegenseitig stumm. Sauber vorhersagen lässt sich das nicht; es müsste gemessen werden.

Vor Phase 7 zu klären. Bis dahin ist der Befund neutral festgehalten, nicht entschieden.

Nebenbefund

Alcove.app und Sapphire.app sind beide installiert — also die beiden Apps, die als Vorbild genannt wurden. Für die Notch-Shell in Phase 1 heißt das: mindestens eine davon greift denselben Bildschirmbereich ab. Für die Entwicklung sollten sie beendet sein, sonst überlagern sich Hover-Zonen und die Panels streiten sich um dieselbe Fläche.