52 lines
1.6 KiB
Swift
52 lines
1.6 KiB
Swift
import Foundation
|
|
|
|
public enum PCM16LE {
|
|
public static func encode(samples: [Float]) -> Data {
|
|
var data = Data()
|
|
data.reserveCapacity(samples.count * MemoryLayout<Int16>.size)
|
|
|
|
for sample in samples {
|
|
let clamped = max(-1.0, min(1.0, sample))
|
|
let scaled: Int16
|
|
|
|
if clamped >= 1.0 {
|
|
scaled = Int16.max
|
|
} else if clamped <= -1.0 {
|
|
scaled = Int16.min
|
|
} else {
|
|
scaled = Int16((clamped * Float(Int16.max)).rounded())
|
|
}
|
|
|
|
var littleEndian = scaled.littleEndian
|
|
withUnsafeBytes(of: &littleEndian) { bytes in
|
|
data.append(contentsOf: bytes)
|
|
}
|
|
}
|
|
|
|
return data
|
|
}
|
|
|
|
public static func resampleLinear(samples: [Float], sourceRate: Int, targetRate: Int = 16_000) -> [Float] {
|
|
guard sourceRate > 0, targetRate > 0, !samples.isEmpty else {
|
|
return []
|
|
}
|
|
|
|
guard sourceRate != targetRate else {
|
|
return samples
|
|
}
|
|
|
|
let ratio = Double(sourceRate) / Double(targetRate)
|
|
let outputCount = max(1, Int(Double(samples.count) / ratio))
|
|
|
|
return (0..<outputCount).map { index in
|
|
let sourcePosition = Double(index) * ratio
|
|
let lowerIndex = Int(sourcePosition)
|
|
let upperIndex = min(lowerIndex + 1, samples.count - 1)
|
|
let fraction = Float(sourcePosition - Double(lowerIndex))
|
|
let lower = samples[min(lowerIndex, samples.count - 1)]
|
|
let upper = samples[upperIndex]
|
|
return lower + ((upper - lower) * fraction)
|
|
}
|
|
}
|
|
}
|