Gemessen über kAudioProcessPropertyIsRunningInput: mit laufendem Mixer nimmt
Onyx auf, ohne ihn nicht. Ein Process Tap ist für macOS eine Tonaufnahme —
auch wenn damit nur die Lautstärke geregelt wird. Solange der Mixer Chrome und
Firefox abgreift, steht das orange Symbol in der Leiste.
Der Mixer stand seit der Fehlersuche am Wochenende auf „automatisch starten",
deshalb war es dauerhaft. Das Häkchen steht jetzt wieder aus.
Ein Hinweis unter dem Häkchen sagt das jetzt, wo die Entscheidung fällt: wer
das Symbol ohne Erklärung sieht, sucht den Fehler bei sich oder hält die App
für unsauber.
Dazu ein echter Fund an derselben Stelle: der Mikrofonpegel hing an
`onDisappear` der View — und das bleibt bei einem NSPopover aus. Dessen
Controller lebt nach dem Schließen weiter, die View verschwindet nie aus der
Hierarchie. Die Messung wäre also beim ersten Öffnen angegangen und nie wieder
aus. Sie hängt jetzt am Popover selbst: `MenuBarModule` bekommt
`popoverDidOpen`/`popoverDidClose`, gemeldet auch dann, wenn das Popover von
selbst zugeht — beim Wechsel in eine andere App etwa.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Der Regler ging bisher immer bis 200 %, ohne dass man das wollen musste. Jetzt
endet er bei 100 % — der Stellung, in der ein Programm klingt, wie es gemeint
war —, und ein Blitz in der Zeile gibt den Bereich darüber frei. Je Programm:
ein Browser braucht ihn für eine leise Konferenz, die Musik daneben nicht.
Oberhalb von 100 % gilt die Kennlinie nicht mehr. Dort steht die Zahl für den
Faktor selbst: 400 % sind das Vierfache der Amplitude. Mit der Kennlinie wären
daraus 4^1,66 ≈ 10,4 geworden — eine Beschriftung, die etwas anderes verspricht,
als der Regler tut. Bei genau 100 % stimmen beide Hälften überein, die Kurve hat
also keinen Sprung, nur einen Knick. Unterhalb bleibt alles wie es war.
Beim Abschalten wird die Lautstärke zurückgeholt. Sonst bliebe ein Programm
dreifach verstärkt, während sein Regler am rechten Anschlag steht und 100 %
anzeigt. Dasselbe beim Laden alter Einstellungen: ein gespeichertes 180 % aus
der Zeit ohne Freigabe fällt auf 100 %.
Die 100 %-Markierung im Regler erscheint nur noch mit Overdrive — ohne ihn läge
sie am rechten Anschlag und sagte dort nichts mehr.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Ein Häkchen im Mixer selbst. Standardmäßig aus: der Mixer greift in den
Tonweg der geregelten Programme ein, und das soll niemandem ungefragt
passieren — beim ersten Start soll nichts umgeleitet werden, was der Nutzer
nicht angeschaltet hat.
Das Häkchen steht in beiden Zuständen: eingeschaltet, um es wieder
abzuwählen, ausgeschaltet, um es gleich mit zu setzen. Sonst müsste man den
Mixer erst einschalten, um sagen zu können, dass er von selbst einschalten
soll.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Ein kleiner Pfeil je Programm stellt die Lautstärke auf 100 % zurück. Er
erscheint nur, wenn der Wert davon abweicht — es gibt sonst nichts
zurückzusetzen.
Der Platz dafür ist trotzdem **immer** reserviert. Ein Knopf, der auftaucht
und dabei den Regler schmaler macht, verschiebt genau das, was man gerade
bedient: man zieht auf 90 %, der Knopf erscheint, der Regler schrumpft, und
der Griff sitzt woanders als die Hand.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Ausgabe und Eingabe stehen jetzt über den Programmen — mit Lautstärke,
Stummschalter und der Gerätewahl direkt am Namen. Ein eigenes Auswahlfeld
daneben wäre eine zweite Stelle für dieselbe Sache.
Sie stehen **außerhalb** des Mixer-Schalters, und das ist der Punkt: hier
wird kein Ton abgegriffen, sondern es werden die Regler des Systems bedient.
Das braucht keinen Tap, kein Aggregate-Gerät und keine Berechtigung — wer
nur die Lautstärke sucht, soll dafür nichts einschalten müssen.
Zwei Dinge, die beim Bauen nicht offensichtlich waren:
Viele Geräte führen keinen Regler für Element 0, sondern nur je Kanal.
Fragt man bloß den Hauptregler, hält man ein regelbares Gerät für
unregelbar. Also erst der Hauptregler, dann die Kanäle — und beim Setzen
beide Kanäle, sonst wandert das Stereobild.
Manche Geräte bringen gar keinen Regler mit, HDMI etwa. Dort steht „hier
nicht regelbar" statt eines Reglers, der nichts tut.
Warntöne folgen dem Ausgabegerät mit. Zwei getrennte Wahlen sind die
Quelle für „warum kommt der Systemton aus dem Notebook".
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Die Fläche reichte über die ganze Breite bis an die Bildschirmoberkante,
damit sie mit der schwarzen Notch zu einem Körper verschmilzt. Bei einer
Kachel ging das auf; bei sechs deckt sie die komplette Menüleiste zu, samt
aller Statuselemente.
Die Silhouette ist jetzt ein T: oben ein Steg in Notch-Breite, ab Unterkante
der Menüleiste die volle Fläche. Die Notch bleibt verschmolzen, die
Menüleiste bleibt frei.
Das allein hätte nur so ausgesehen, als wäre es gelöst. Das Fenster liegt
weiterhin über der Leiste, dort nur durchsichtig — und ein durchsichtiges
Fenster schluckt Klicks trotzdem. Die Trefferprüfung reicht sie jetzt
durch: links und rechts der Notch gehört der oberste Streifen der
Menüleiste.
Aus demselben Grund fragt der Koordinator nicht mehr den Fensterrahmen,
sondern die sichtbare Fläche. Wer den Rahmen befragt, hält einen Klick auf
ein fremdes Statuselement für einen Klick ins Panel — und einen Zeiger über
der Menüleiste für einen im Panel.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Die Mediensteuerung stand nebeneinander: Cover links, alles andere in den
verbleibenden gut hundert Punkten. Daher „Bernhard H…" statt eines Titels.
Jetzt übereinander, wie es zu einer hochkanten Kachel passt.
Das Cover ragte zusätzlich über die Kachel hinaus, weil `clipShape` vor dem
Rahmen stand — beschnitten wurde damit auf die Eigengröße des Bildes, und
die ist bei einem Podcast-Cover ein Vielfaches. Erst Rahmen, dann
beschneiden.
Menüleistensymbole waren kaum zu sehen. `color.set()` vor `image.draw(in:)`
färbt ein Template-Bild nicht: es wird schwarz gezeichnet, auf einer dunklen
Menüleiste also unsichtbar. AppKit färbt Template-Bilder nur ein, wenn sie
als `image` eines Buttons gesetzt sind — in einer selbst gezeichneten Ansicht
muss die Farbe in die Symbolkonfiguration. Betraf alle fünf Zeichenstellen,
nicht nur die Lüfter.
„fans.auto" stand als roher Schlüssel da: Xcode hatte ihn extrahiert, gefüllt
wurde er nie. Beim Nachsehen waren es 35 solche Einträge über alle Kataloge
hinweg — die meisten reine Formatschlüssel, die als Durchreicher trotzdem
einen Wert brauchen.
Der Automatik-Knopf ist jetzt eine gefüllte Pille, wenn sie greift. Blauer
Text sieht aus wie ein Link und nicht wie ein Zustand.
Und ein Absturz, der noch niemandem passiert ist: meldet der Helfer für einen
stehenden Lüfter keine Grenzen, hätte `Slider(in: 0...0)` die App beendet.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Die Einrichtung beim ersten Start erklärt jede der vier Sachen einzeln und
fragt jede einzeln an. Vier Systemdialoge hintereinander wären der übliche
Weg und der falsche: wer nicht weiß wofür, klickt viermal „Nicht erlauben",
und danach fragt macOS nie wieder. Sie kommt beim ersten Start immer, auch
wenn zufällig schon alles erlaubt ist — sie erklärt auch den Helfer und den
Autostart.
Beim Autostart ist `requiresApproval` der Zustand, der weh tut. Wer das
Anmeldeobjekt in den Systemeinstellungen abgeschaltet hat, kann in Onyx auf
den Schalter drücken, so oft er will. Als „aus" angezeigt führt das in eine
Schleife, also steht dort jetzt, wo es weitergeht.
Der Mixer verliert den Schalter je Programm. Zehn Mini-Schalter untereinander
sind das, was den Eindruck macht; am Regler zu ziehen ist ohnehin die
Entscheidung, dieses Programm zu regeln, und der große Ausschalter stellt
alles zurück. Der Regler ist selbst gezeichnet — der Systemregler bringt sein
eigenes Erscheinungsbild mit und kann nicht zeigen, worauf es hier ankommt:
den Pegel und die Grenze bei 100 %.
Der Pegel wird im selben Durchlauf gemessen, in dem verstärkt wird, und über
`Atomic` weitergereicht. Eine gewöhnliche Eigenschaft wäre ein Wettlauf, eine
Sperre im Audiothread der klassische Weg zu Aussetzern. Dabei fällt das
`Date()` aus dem Echtzeitpfad, das dort nie hingehört hat.
Zwei Funde beim Bauen des Release-Wegs, beide hätten erst nach dem Hochladen
zur Ablehnung geführt: der Helfer wurde ohne sicheren Zeitstempel signiert,
und das Adapter-Framework ad-hoc. Beides gilt jetzt nur noch im Debug.
Scripts/release.sh prüft Zertifikat und Notarisierungsprofil vorweg, statt
nach zehn Minuten Bauen zu scheitern.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Xcode meldete beim Start "Could not attach to pid — ensure that scarriffle has
permission to debug Onyx". Ursache war mein eigener Build-Schritt: er signierte
die App mit Onyx.entitlements neu, und darin fehlt get-task-allow — die
Entitlement, die das Anhängen eines Debuggers überhaupt erlaubt. Xcode setzt sie
im Debug-Build selbst; mein Neusignieren entfernte sie stillschweigend wieder.
Die Reihenfolge im Bauprotokoll zeigt, dass der Schritt gar nicht nötig war:
CodeSign OnyxHelper (Zeile 943)
PhaseScriptExecution (Zeile 1092)
CodeSign Onyx.app (Zeile 1743)
Xcode signiert die App als Letztes, nach dem Skript. Damit repariert es das
Siegel von selbst, das durch das Einbetten des Helfers bricht — mein
Neusignieren war ein Nachbau von etwas, das ohnehin passiert, nur ohne die
Entitlements, die Xcode beilegt.
Der Schritt kopiert jetzt nur noch und signiert ausschließlich den Helfer, der
sein eigenes Siegel braucht. Nachgeprüft: App-Siegel gültig, get-task-allow im
Debug vorhanden, Helfer trägt den richtigen Bezeichner und weiterhin keine
Entitlements.
Dass get-task-allow auch im Release-Build steht, ist kein Fehler: Xcode legt sie
bei jedem mit einem Development-Zertifikat signierten Build bei. Mit einem
Developer-ID-Zertifikat entfällt sie — das kommt in Phase 9 mit der
Notarisierung.
Ein Tap mit mutedWhenTapped schaltet die ursprüngliche Ausgabe eines Programms
stumm und liefert das Signal stattdessen bei Onyx ab; ein privates
Aggregate-Device aus Tap und echtem Ausgabegerät gibt es nach der Verstärkung
wieder aus. Das ist der einzige Weg, den macOS ohne eigenen Treiber anbietet.
Die Kennlinie ist hergeleitet, nicht geraten: halbe empfundene Lautstärke sind
rund −10 dB, also Faktor 0,316. Aus 0,5^k = 0,316 folgt k ≈ 1,66. Die Skala ist
damit symmetrisch — 50 % sind −10 dB, 200 % sind +10 dB. Mein erster Ansatz mit
der vierten Potenz ergab bei halbem Regler 6 % statt 32 %; der Test hat das
gefangen, bevor es an einen Lautsprecher kam.
Über 100 % greift ein weicher Begrenzer. Ohne ihn schneidet die Hardware die
Werte hart ab, und das knackt hörbar. Er arbeitet erst ab 0,7, weil eine
Begrenzung, die schon bei Zimmerlautstärke einsetzt, matt klingt.
Der Mixer ist standardmäßig aus, und auch eingeschaltet greift er nur bei
ausdrücklich gewählten Programmen ein. Alles andere läuft unverändert vorbei.
Der Notausschalter steht sichtbar in der Ansicht, nicht in einem Menü: wenn der
Ton weg ist, ist das der Knopf, den man sucht.
Drei Fehlerfälle sind eingeplant, weil jeder von ihnen ein stummes Programm
bedeutet. Der Wachhund baut Taps neu auf, die nur noch Stille liefern — der
bekannte Fall aus Spike C. Ein Wechsel des Ausgabegeräts baut alle Taps neu
auf, weil sie am alten Gerät hängen. Und ein fehlgeschlagener Aufbau räumt sich
selbst weg, statt in einem halben Zustand zu verharren.
Beim Beenden von Onyx wird der Mixer abgebaut: sonst bleiben die abgegriffenen
Programme stumm zurück.
178 Tests grün, App-Signatur gültig.