fix: add VUScale.spokenLevel with proper clamping and non-finite handling
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -20,6 +20,14 @@ public enum VUScale {
|
||||
public static func angle(forFraction fraction: Double) -> Double {
|
||||
(-MeterTokens.vuArcDegrees / 2 + MeterTokens.vuArcDegrees * fraction) * .pi / 180
|
||||
}
|
||||
|
||||
/// Level as VoiceOver should say it: rounded to steps of 3 VU, clamped to the scale, "stil" for non-finite input.
|
||||
public static func spokenLevel(fromDbfs dbfs: Double) -> String {
|
||||
guard dbfs.isFinite else { return "stil" }
|
||||
let stepped = (vu(fromDbfs: dbfs) / 3).rounded() * 3
|
||||
let clamped = min(MeterTokens.vuMax, max(MeterTokens.vuMin, stepped))
|
||||
return "\(Int(clamped)) VU"
|
||||
}
|
||||
}
|
||||
|
||||
/// Second-order needle: reaches ~99 % in `response` seconds, slight overshoot for damping < 1.
|
||||
|
||||
@@ -60,8 +60,7 @@ public struct VUMeterView: View {
|
||||
/// Rounded to steps of 3 VU so VoiceOver is not flooded with changes.
|
||||
private var spokenValue: String {
|
||||
guard isActive else { return "stil" }
|
||||
let vu = (VUScale.vu(fromDbfs: levelDbfs) / 3).rounded() * 3
|
||||
return "\(Int(vu)) VU"
|
||||
return VUScale.spokenLevel(fromDbfs: levelDbfs)
|
||||
}
|
||||
|
||||
// MARK: drawing (design coordinates: MeterTokens.vuWidth × vuHeight)
|
||||
|
||||
@@ -106,3 +106,26 @@ struct LEDBarTests {
|
||||
#expect(LEDBar.litCount(forFraction: -1) == 0)
|
||||
}
|
||||
}
|
||||
|
||||
struct SpokenLevelTests {
|
||||
@Test func nonFiniteInputReturnsStil() {
|
||||
#expect(VUScale.spokenLevel(fromDbfs: -.infinity) == "stil")
|
||||
#expect(VUScale.spokenLevel(fromDbfs: .nan) == "stil")
|
||||
}
|
||||
|
||||
@Test func referenceLevel() {
|
||||
#expect(VUScale.spokenLevel(fromDbfs: -20) == "0 VU")
|
||||
}
|
||||
|
||||
@Test func negativeLevel() {
|
||||
#expect(VUScale.spokenLevel(fromDbfs: -30) == "-9 VU")
|
||||
}
|
||||
|
||||
@Test func clampedToMinimum() {
|
||||
#expect(VUScale.spokenLevel(fromDbfs: -120) == "-20 VU")
|
||||
}
|
||||
|
||||
@Test func clampedToMaximum() {
|
||||
#expect(VUScale.spokenLevel(fromDbfs: 10) == "3 VU")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user