/// Stereo 4-point Hermite resampler for small rate corrections and 44.1 ↔ 48 kHz. /// /// `step` is input frames per output frame (1.0 = no change). Allocation-free; safe on the render thread. public struct Resampler: Sendable { // History per channel: x[-1], x[0], x[1], x[2]. Output is interpolated between x[0] and x[1]. private var left: (Float, Float, Float, Float) = (0, 0, 0, 0) private var right: (Float, Float, Float, Float) = (0, 0, 0, 0) private var fraction: Double = 0 public init() {} /// Number of input frames `process` will consume for `outputFrames` at `step`. public func inputFramesNeeded(outputFrames: Int, step: Double) -> Int { var position = fraction var consumed = 0 for _ in 0..= 1 { position -= 1 consumed += 1 } } return consumed } /// Consumes exactly `inputFramesNeeded(outputFrames:step:)` interleaved stereo frames from `input` /// and writes `outputFrames` interleaved stereo frames to `output`. public mutating func process( input: UnsafePointer, output: UnsafeMutablePointer, outputFrames: Int, step: Double ) { var read = 0 for frame in 0..= 1 { fraction -= 1 left = (left.1, left.2, left.3, input[read * 2]) right = (right.1, right.2, right.3, input[read * 2 + 1]) read += 1 } } } public mutating func reset() { left = (0, 0, 0, 0) right = (0, 0, 0, 0) fraction = 0 } @inline(__always) private static func hermite(_ x: (Float, Float, Float, Float), _ t: Float) -> Float { let c0 = x.1 let c1 = 0.5 * (x.2 - x.0) let c2 = x.0 - 2.5 * x.1 + 2 * x.2 - 0.5 * x.3 let c3 = 0.5 * (x.3 - x.0) + 1.5 * (x.1 - x.2) return ((c3 * t + c2) * t + c1) * t + c0 } }