import Accelerate import CoreML import Foundation import SpeechCore /// Unified audio tower (multifunction Core ML model, ANE): /// mel bucket -> function tower_5s / tower_10s / tower_30s -> hidden /// -> projector (CPU). Masks come from LMDecoder.maskGen (390-sized, sliced). package final class AudioTower { private let modelURL: URL private let computeUnits: MLComputeUnits private var modelsByBucket: [Int: MLModel] = [:] private let store: AssetStore private static let functionByBucket = [500: "tower_5s", 1000: "tower_10s", 3000: "tower_30s"] package static let tokensByBucket = [500: 65, 1000: 130, 3000: 390] package init(unifiedModelURL: URL, store: AssetStore, computeUnits: MLComputeUnits = .cpuAndNeuralEngine) throws { self.computeUnits = computeUnits self.store = store if unifiedModelURL.pathExtension == "mlpackage" { modelURL = try MLModel.compileModel(at: unifiedModelURL) } else { modelURL = unifiedModelURL } } /// Loads (and caches) the function for a bucket. First load per bucket /// triggers the ANE compile; call warmUp() at app start. private func model(forBucket bucket: Int) throws -> MLModel { if let m = modelsByBucket[bucket] { return m } let config = MLModelConfiguration() config.computeUnits = computeUnits config.functionName = Self.functionByBucket[bucket]! let m = try MLModel(contentsOf: modelURL, configuration: config) modelsByBucket[bucket] = m return m } package func warmUp(buckets: [Int] = [500, 3000]) { for b in buckets { _ = try? model(forBucket: b) } } private static func multiArray(_ values: [Float], shape: [NSNumber]) throws -> MLMultiArray { let arr = try MLMultiArray(shape: shape, dataType: .float32) values.withUnsafeBufferPointer { src in arr.dataPointer.bindMemory(to: Float.self, capacity: values.count) .update(from: src.baseAddress!, count: values.count) } return arr } private static func floats(_ arr: MLMultiArray) -> [Float] { switch arr.dataType { case .float32: return arr.withUnsafeBufferPointer(ofType: Float.self) { Array($0) } case .float16: return arr.withUnsafeBufferPointer(ofType: Float16.self) { $0.map(Float.init) } default: return (0.. projected embeddings (N, 512) package func embed(mel: [Float], bucketFrames: Int, attnMask390: [Float], validMask390: [Bool], sampleCount: Int) throws -> [[Float]] { let tokens = Self.tokensByBucket[bucketFrames]! // slice the 390x390 additive mask to tokens x tokens, fp16-safe fill var mask = [Float](repeating: 0, count: tokens * tokens) for q in 0..