| import Accelerate |
| import Foundation |
| import SpeechCore |
|
|
| |
| |
| |
| package final class MelExtractor { |
| package static let nFFT = 400 |
| package static let hop = 160 |
| package static let nMels = 128 |
| package static let nFreq = 201 |
| package static let framesPadded = 3000 |
| package static let maxSamples = 480_000 |
| |
| package static let buckets = [500, 1000, 3000] |
|
|
| package static func bucket(forFrames frames: Int) -> Int { |
| for b in buckets where frames <= b { return b } |
| return buckets.last! |
| } |
|
|
| private let window: [Float] |
| private let melFilters: [Float] |
| private let dftCos: [Float] |
| private let dftSin: [Float] |
|
|
| package init(hannWindow: [Float], melFilters: [Float]) { |
| precondition(hannWindow.count == Self.nFFT) |
| precondition(melFilters.count == Self.nFreq * Self.nMels) |
| self.window = hannWindow |
| self.melFilters = melFilters |
|
|
| var cosM = [Float](repeating: 0, count: Self.nFFT * Self.nFreq) |
| var sinM = [Float](repeating: 0, count: Self.nFFT * Self.nFreq) |
| for n in 0..<Self.nFFT { |
| for f in 0..<Self.nFreq { |
| let angle = 2.0 * Double.pi * Double(f) * Double(n) / Double(Self.nFFT) |
| cosM[n * Self.nFreq + f] = Float(cos(angle)) |
| sinM[n * Self.nFreq + f] = Float(-sin(angle)) |
| } |
| } |
| self.dftCos = cosM |
| self.dftSin = sinM |
| } |
|
|
| |
| |
| package func extract(_ samples: [Float]) -> (mel: [Float], encLen: Int, bucketFrames: Int) { |
| var x = samples |
| if x.count > Self.maxSamples { x = Array(x[0..<Self.maxSamples]) } |
| let n = x.count |
| let half = Self.nFFT / 2 |
|
|
| |
| var padded = [Float](repeating: 0, count: n + Self.nFFT) |
| for i in 0..<half { padded[i] = x[half - i] } |
| for i in 0..<n { padded[half + i] = x[i] } |
| for i in 0..<half { padded[half + n + i] = x[n - 2 - i] } |
|
|
| |
| let tFrames = max(n / Self.hop, 1) |
|
|
| |
| var frames = [Float](repeating: 0, count: tFrames * Self.nFFT) |
| for t in 0..<tFrames { |
| let src = t * Self.hop |
| for i in 0..<Self.nFFT { |
| frames[t * Self.nFFT + i] = padded[src + i] * window[i] |
| } |
| } |
|
|
| |
| var re = [Float](repeating: 0, count: tFrames * Self.nFreq) |
| var im = [Float](repeating: 0, count: tFrames * Self.nFreq) |
| cblas_sgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, |
| Int32(tFrames), Int32(Self.nFreq), Int32(Self.nFFT), |
| 1, frames, Int32(Self.nFFT), dftCos, Int32(Self.nFreq), |
| 0, &re, Int32(Self.nFreq)) |
| cblas_sgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, |
| Int32(tFrames), Int32(Self.nFreq), Int32(Self.nFFT), |
| 1, frames, Int32(Self.nFFT), dftSin, Int32(Self.nFreq), |
| 0, &im, Int32(Self.nFreq)) |
|
|
| |
| vDSP.multiply(re, re, result: &re) |
| vDSP.multiply(im, im, result: &im) |
| vDSP.add(re, im, result: &re) |
|
|
| |
| var mel = [Float](repeating: 0, count: tFrames * Self.nMels) |
| cblas_sgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, |
| Int32(tFrames), Int32(Self.nMels), Int32(Self.nFreq), |
| 1, re, Int32(Self.nFreq), melFilters, Int32(Self.nMels), |
| 0, &mel, Int32(Self.nMels)) |
|
|
| |
| var clamped = vDSP.clip(mel, to: 1e-10...Float.greatestFiniteMagnitude) |
| var logMel = [Float](repeating: 0, count: clamped.count) |
| var count = Int32(clamped.count) |
| vvlog10f(&logMel, &clamped, &count) |
| let maxVal = vDSP.maximum(logMel) |
| logMel = vDSP.clip(logMel, to: (maxVal - 8.0)...Float.greatestFiniteMagnitude) |
| logMel = vDSP.add(4.0, logMel) |
| logMel = vDSP.divide(logMel, 4.0) |
|
|
| |
| let bucketFrames = Self.bucket(forFrames: tFrames) |
| var out = [Float](repeating: 0, count: Self.nMels * bucketFrames) |
| let tUse = min(tFrames, bucketFrames) |
| for t in 0..<tUse { |
| for m in 0..<Self.nMels { |
| out[m * bucketFrames + t] = logMel[t * Self.nMels + m] |
| } |
| } |
| |
| let encLen = min(max((n + Self.hop - 1) / Self.hop, 1), tFrames) |
| return (out, encLen, bucketFrames) |
| } |
| } |
|
|