Overdrive je Programm: bis 400 %, aber nur auf Ansage

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>
This commit is contained in:
Scarriffle
2026-08-13 18:02:43 +02:00
parent d001e439a6
commit ed8b095377
6 changed files with 228 additions and 19 deletions

View File

@@ -152,6 +152,7 @@ private struct ProcessRow: View {
private var managed: Bool { mixer.isManaged(group.bundleID) }
private var muted: Bool { mixer.isMuted(group.bundleID) }
private var volume: Double { mixer.volume(for: group.bundleID) }
private var overdriven: Bool { mixer.isOverdriven(group.bundleID) }
var body: some View {
// Eine Zeile statt zwei: Symbol, Stummschalter, Regler, Wert. Der Name
@@ -176,6 +177,7 @@ private struct ProcessRow: View {
VolumeBar(
volume: volume,
maximum: Gain.maximum(overdrive: overdriven),
level: mixer.level(for: group.bundleID),
isActive: managed && !muted,
onChange: { newValue in
@@ -195,6 +197,7 @@ private struct ProcessRow: View {
// Feste Breite, sonst wandert der Regler beim Ziehen.
.frame(width: 36, alignment: .trailing)
overdriveButton
resetButton
}
.contentShape(.rect)
@@ -208,6 +211,26 @@ private struct ProcessRow: View {
.animation(Onyx.Motion.value, value: managed)
}
/// Der Overdrive-Schalter.
///
/// Er steht bei jedem Programm, auch bei einem ungeregelten sonst müsste
/// man erst den Regler anfassen, um an den Schalter zu kommen, und hätte
/// damit schon geregelt. Eingeschaltet reicht der Regler bis 400 %.
@ViewBuilder
private var overdriveButton: some View {
Button {
if !managed { mixer.setManaged(true, for: group.bundleID) }
mixer.setOverdrive(!overdriven, for: group.bundleID)
} label: {
Image(systemName: "bolt.fill")
.font(.system(size: 9, weight: .medium))
}
.buttonStyle(.plain)
.foregroundStyle(overdriven ? Onyx.Color.warning : Onyx.Color.textTertiary.opacity(0.55))
.help(Text("mixer.overdrive", bundle: .module))
.frame(width: 14, height: 14)
}
/// Zurück auf 100 %.
///
/// Der Platz dafür ist **immer** reserviert, auch wenn der Knopf nicht zu
@@ -290,6 +313,8 @@ private struct ProcessRow: View {
/// die Grenze bei 100 %.
private struct VolumeBar: View {
let volume: Double
/// Der rechte Anschlag: 1 im Normalfall, 4 mit Overdrive.
let maximum: Double
let level: Double
let isActive: Bool
let onChange: (Double) -> Void
@@ -301,7 +326,7 @@ private struct VolumeBar: View {
var body: some View {
GeometryReader { geometry in
let width = geometry.size.width
let fraction = min(max(volume / Gain.maximumVolume, 0), 1)
let fraction = min(max(volume / maximum, 0), 1)
let knob: CGFloat = isDragging ? 13 : 11
// Der Knopf soll an den Enden nicht überstehen.
let travel = max(width - knob, 1)
@@ -313,11 +338,14 @@ private struct VolumeBar: View {
.frame(height: trackHeight)
// Markierung bei 100 %: darüber wird verstärkt, und man soll
// sehen, wann man diese Grenze überschreitet.
Rectangle()
.fill(Onyx.Color.hairline)
.frame(width: 1, height: trackHeight + 4)
.offset(x: knob / 2 + travel / Gain.maximumVolume)
// sehen, wann man diese Grenze überschreitet. Ohne Overdrive
// liegt sie am rechten Anschlag und sagt dort nichts mehr.
if maximum > 1 {
Rectangle()
.fill(Onyx.Color.hairline)
.frame(width: 1, height: trackHeight + 4)
.offset(x: knob / 2 + travel / maximum)
}
Capsule()
.fill(volume > 1 ? Onyx.Color.warning : Onyx.Color.accent)
@@ -349,7 +377,7 @@ private struct VolumeBar: View {
.onChanged { value in
isDragging = true
let position = min(max((value.location.x - knob / 2) / travel, 0), 1)
onChange(position * Gain.maximumVolume)
onChange(position * maximum)
}
.onEnded { _ in isDragging = false })
}