Skip to content

Commit bf7fdbb

Browse files
committed
Clean up scale types
1 parent c78bc68 commit bf7fdbb

File tree

3 files changed

+41
-107
lines changed

3 files changed

+41
-107
lines changed

Sources/FDWaveformView/FDWaveformRenderOperation.swift

Lines changed: 30 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -6,27 +6,22 @@ import Accelerate
66
import UIKit
77

88
/// Format options for FDWaveformRenderOperation
9-
//MAYBE: Make this public
109
struct FDWaveformRenderFormat {
1110

1211
/// The type of waveform to render
13-
//TODO: make this public after reconciling FDWaveformView.WaveformType and FDWaveformType
14-
var type: FDWaveformType
12+
var type: FDWaveformView.WaveformType
1513

1614
/// The color of the waveform
17-
internal var wavesColor: UIColor
15+
var wavesColor: UIColor
1816

1917
/// The scale factor to apply to the rendered image (usually the current screen's scale)
20-
public var scale: CGFloat
18+
var scale: CGFloat
2119

22-
public init() {
23-
self.init(
24-
type: .linear,
25-
wavesColor: .black,
26-
scale: UIScreen.main.scale)
27-
}
28-
29-
init(type: FDWaveformType, wavesColor: UIColor, scale: CGFloat) {
20+
init(
21+
type: FDWaveformView.WaveformType = .linear,
22+
wavesColor: UIColor = .black,
23+
scale: CGFloat = UIScreen.main.scale
24+
) {
3025
self.type = type
3126
self.wavesColor = wavesColor
3227
self.scale = scale
@@ -39,6 +34,9 @@ final class FDWaveformRenderOperation: Operation {
3934
/// When samples per pixel falls below this threshold, switch from binned maximums to individual samples
4035
private let samplesPerPixelThreshold = 2
4136

37+
/// The noise floor for logarithmic display (in dB)
38+
private let noiseFloorDecibels: Float = -50.0
39+
4240
/// The data source used to build the waveform
4341
let dataSource: FDWaveformDataSource
4442

@@ -147,7 +145,7 @@ final class FDWaveformRenderOperation: Operation {
147145

148146
// Apply logarithmic transformation only to amplitude data (not signed samples)
149147
if !isSignedSamples {
150-
format.type.process(normalizedSamples: &amplitudes)
148+
applyWaveformType(&amplitudes)
151149
}
152150

153151
let samples = amplitudes.map { CGFloat($0) }
@@ -162,11 +160,25 @@ final class FDWaveformRenderOperation: Operation {
162160
}
163161
}
164162

165-
/// Read the asset and create a lower resolution set of samples
166-
// TODO: Implement sliceAsset if needed for backwards compatibility
167-
// This method is no longer used as we now use the dataSource protocol
163+
/// Applies the waveform type transformation to the amplitude samples
164+
private func applyWaveformType(_ samples: inout [Float]) {
165+
switch format.type {
166+
case .linear:
167+
return
168+
case .logarithmic:
169+
// Convert normalized [0, 1] samples to dB scale
170+
let epsilon: Float = 1e-10
171+
for i in 0..<samples.count {
172+
let amplitude = max(samples[i], epsilon)
173+
var dB = 20.0 * log10(amplitude)
174+
dB = max(dB, noiseFloorDecibels)
175+
dB = min(dB, 0)
176+
// Normalize to [0, 1] where 0 = noiseFloor and 1 = 0 dB
177+
samples[i] = (dB - noiseFloorDecibels) / (0 - noiseFloorDecibels)
178+
}
179+
}
180+
}
168181

169-
// TODO: report progress? (for issue #2)
170182
func plotWaveformGraph(
171183
_ samples: [CGFloat], maximumValue max: CGFloat, zeroValue min: CGFloat,
172184
isSignedSamples: Bool = false

Sources/FDWaveformView/FDWaveformView.swift

Lines changed: 5 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -146,9 +146,6 @@ open class FDWaveformView: UIView {
146146
/// If this number is not available then a re-render will be performed
147147
private var verticalOverdrawAllowed = 1.0...3.0
148148

149-
/// The "zero" level (in dB)
150-
fileprivate let noiseFloor: CGFloat = -50.0
151-
152149
/// Minimum number of samples that can be displayed (limits maximum zoom)
153150
private let minimumZoomSamples = 10
154151

@@ -203,15 +200,6 @@ open class FDWaveformView: UIView {
203200
return window?.screen.scale ?? UIScreen.main.scale
204201
}
205202

206-
/// Waveform type for rendering waveforms
207-
//TODO: make this public after reconciling FDWaveformView.WaveformType and FDWaveformType
208-
var waveformRenderType: FDWaveformType {
209-
switch waveformType {
210-
case .linear: return .linear
211-
case .logarithmic: return .logarithmic(noiseFloor: noiseFloor)
212-
}
213-
}
214-
215203
/// Represents the status of the waveform renderings
216204
fileprivate enum CacheStatus {
217205
case dirty
@@ -341,7 +329,7 @@ open class FDWaveformView: UIView {
341329
func isWaveformRenderOperationDirty(_ renderOperation: FDWaveformRenderOperation?) -> Bool? {
342330
guard let renderOperation = renderOperation else { return nil }
343331

344-
if renderOperation.format.type != waveformRenderType {
332+
if renderOperation.format.type != waveformType {
345333
return true
346334
}
347335
if renderOperation.format.scale != desiredImageScale {
@@ -460,7 +448,7 @@ open class FDWaveformView: UIView {
460448
let heightInPixels = frame.height * CGFloat(horizontalOverdrawTarget)
461449
let imageSize = CGSize(width: widthInPixels, height: heightInPixels)
462450
let renderFormat = FDWaveformRenderFormat(
463-
type: waveformRenderType, wavesColor: .black, scale: desiredImageScale)
451+
type: waveformType, wavesColor: .black, scale: desiredImageScale)
464452

465453
let waveformRenderOperation = FDWaveformRenderOperation(
466454
dataSource: dataSource, totalSamples: totalSamples, imageSize: imageSize,
@@ -488,61 +476,6 @@ open class FDWaveformView: UIView {
488476
}
489477
}
490478

491-
//TODO: make this public after reconciling FDWaveformView.WaveformType and FDWaveformType
492-
enum FDWaveformType: Equatable {
493-
/// Waveform is rendered using a linear scale
494-
case linear
495-
496-
/// Waveform is rendered using a logarithmic scale
497-
/// noiseFloor: The "zero" level (in dB)
498-
case logarithmic(noiseFloor: CGFloat)
499-
500-
// See http://stackoverflow.com/questions/24339807/how-to-test-equality-of-swift-enums-with-associated-values
501-
public static func == (lhs: FDWaveformType, rhs: FDWaveformType) -> Bool {
502-
switch lhs {
503-
case .linear:
504-
if case .linear = rhs {
505-
return true
506-
}
507-
case .logarithmic(let lhsNoiseFloor):
508-
if case .logarithmic(let rhsNoiseFloor) = rhs {
509-
return lhsNoiseFloor == rhsNoiseFloor
510-
}
511-
}
512-
return false
513-
}
514-
515-
public var floorValue: CGFloat {
516-
switch self {
517-
case .linear: return 0
518-
case .logarithmic(let noiseFloor): return noiseFloor
519-
}
520-
}
521-
522-
func process(normalizedSamples: inout [Float]) {
523-
switch self {
524-
case .linear:
525-
return
526-
527-
case .logarithmic(let noiseFloor):
528-
// Convert normalized [0, 1] samples to dB scale
529-
// Formula: dB = 20 * log10(amplitude)
530-
// For amplitude in [0, 1]: dB ranges from -infinity to 0
531-
// We use a small epsilon to avoid log(0) = -infinity
532-
let epsilon: Float = 1e-10
533-
for i in 0..<normalizedSamples.count {
534-
let amplitude = max(normalizedSamples[i], epsilon)
535-
var dB = 20.0 * log10(amplitude)
536-
// Clip to [noiseFloor, 0]
537-
dB = max(dB, Float(noiseFloor))
538-
dB = min(dB, 0)
539-
// Normalize to [0, 1] where 0 = noiseFloor and 1 = 0 dB
540-
normalizedSamples[i] = (dB - Float(noiseFloor)) / (0 - Float(noiseFloor))
541-
}
542-
}
543-
}
544-
}
545-
546479
extension FDWaveformView: UIGestureRecognizerDelegate {
547480
public func gestureRecognizer(
548481
_ gestureRecognizer: UIGestureRecognizer,
@@ -666,9 +599,9 @@ extension FDWaveformView: UIGestureRecognizerDelegate {
666599
let clampedDelta = max(maxBackwardDelta, min(maxForwardDelta, wholeSampleDelta))
667600

668601
if clampedDelta != 0 {
669-
zoomSamples =
670-
(clampedZoomSamples.startIndex + clampedDelta)
671-
..<(clampedZoomSamples.endIndex + clampedDelta)
602+
let newStart = clampedZoomSamples.startIndex + clampedDelta
603+
let newEnd = clampedZoomSamples.endIndex + clampedDelta
604+
zoomSamples = newStart..<newEnd
672605
}
673606

674607
accumulatedPanDelta -= CGFloat(wholeSampleDelta)

Tests/FDWaveformViewTests/FDWaveformViewTests.swift

Lines changed: 6 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -39,26 +39,15 @@ final class FDWaveformViewTests: XCTestCase {
3939
XCTAssertEqual(waveformView.progressColor, .green)
4040
}
4141

42-
func testFDWaveformTypeEquality() throws {
43-
// Test FDWaveformType enum equality
44-
let linear1 = FDWaveformType.linear
45-
let linear2 = FDWaveformType.linear
46-
let logarithmic1 = FDWaveformType.logarithmic(noiseFloor: -50.0)
47-
let logarithmic2 = FDWaveformType.logarithmic(noiseFloor: -50.0)
48-
let logarithmic3 = FDWaveformType.logarithmic(noiseFloor: -60.0)
42+
func testWaveformTypeEquality() throws {
43+
// Test WaveformType enum equality
44+
let linear1 = FDWaveformView.WaveformType.linear
45+
let linear2 = FDWaveformView.WaveformType.linear
46+
let logarithmic1 = FDWaveformView.WaveformType.logarithmic
47+
let logarithmic2 = FDWaveformView.WaveformType.logarithmic
4948

5049
XCTAssertEqual(linear1, linear2)
5150
XCTAssertEqual(logarithmic1, logarithmic2)
5251
XCTAssertNotEqual(linear1, logarithmic1)
53-
XCTAssertNotEqual(logarithmic1, logarithmic3)
54-
}
55-
56-
func testFDWaveformTypeFloorValue() throws {
57-
// Test floor value property
58-
let linear = FDWaveformType.linear
59-
let logarithmic = FDWaveformType.logarithmic(noiseFloor: -45.0)
60-
61-
XCTAssertEqual(linear.floorValue, 0)
62-
XCTAssertEqual(logarithmic.floorValue, -45.0)
6352
}
6453
}

0 commit comments

Comments
 (0)