Eigene Aggregate-Geräte gehören nicht in die Geräteauswahl

Für jedes geregelte Programm legt Onyx ein Aggregate-Gerät an. Sie sind als
privat markiert — das verbirgt sie aber nur vor **fremden** Prozessen, nicht
vor Onyx selbst. In der Ausgabeauswahl standen sie deshalb mitten zwischen
den echten Geräten, und wer eines davon wählt, hängt seinen Ton in eine
Schleife: die Ausgabe eines Taps wird zur Quelle desselben Taps.

Erkannt werden sie über einen Vorsatz in der Gerätekennung, nicht über den
Namen. Der Name enthält den Programmnamen und ließe sich von einem echten
Gerät nicht sicher unterscheiden — die Kennung vergibt Onyx selbst.

Der Vorsatz nennt Onyx beim Namen: taucht so ein Gerät doch einmal irgendwo
auf, im Audio-MIDI-Setup etwa, sieht man, wer es angelegt hat.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Scarriffle
2026-08-13 14:24:12 +02:00
parent c990ea558b
commit 312514e6ce
3 changed files with 76 additions and 1 deletions

View File

@@ -0,0 +1,41 @@
import Testing
@testable import AudioProvider
// Für jedes geregelte Programm legt Onyx ein Aggregate-Gerät an. Sie sind als
// privat markiert das verbirgt sie aber nur vor **fremden** Prozessen, nicht
// vor Onyx selbst. In der Geräteauswahl standen sie deshalb mitten zwischen
// den echten Ausgabegeräten, und wer eines davon wählt, hängt seinen Ton in
// eine Schleife.
@Suite("Eigene Geräte erkennen")
struct OwnDeviceTests {
@Test("Ein von Onyx angelegtes Gerät wird erkannt")
func ownDeviceIsDetected() {
let uid = ProcessTap.aggregateUIDPrefix + "1234-ABCD"
#expect(SystemAudio.isOwnDevice(uid: uid))
}
@Test("Fremde Geräte bleiben unangetastet")
func foreignDevicesPass() {
for uid in ["BuiltInSpeakerDevice", "AppleUSBAudioEngine:Hue:Sync",
"com.apple.audio.AggregateDevice:1234"] {
#expect(!SystemAudio.isOwnDevice(uid: uid))
}
}
@Test("Ohne Kennung gilt ein Gerät nicht als eigenes")
func missingUIDIsForeign() {
// Lieber ein Gerät zu viel in der Liste als ein echtes Ausgabegerät zu
// verschlucken, das der Nutzer braucht.
#expect(!SystemAudio.isOwnDevice(uid: nil))
#expect(!SystemAudio.isOwnDevice(uid: ""))
}
@Test("Die Kennung trägt den Programmnamen, nicht nur eine Zufallszahl")
func prefixIsRecognisable() {
// Taucht sie doch einmal irgendwo auf im Audio-MIDI-Setup etwa ,
// soll man sehen, wer sie angelegt hat.
#expect(ProcessTap.aggregateUIDPrefix.contains("onyx"))
}
}