58 lines
2.1 KiB
Swift
58 lines
2.1 KiB
Swift
import AVFoundation
|
|
import Foundation
|
|
import MastermindPOCCore
|
|
|
|
enum MicrophonePCMConverter {
|
|
static func extractFrame(from buffer: AVAudioPCMBuffer) -> PCMFrame? {
|
|
let frameCount = Int(buffer.frameLength)
|
|
guard frameCount > 0 else {
|
|
return nil
|
|
}
|
|
|
|
let channelCount = max(1, Int(buffer.format.channelCount))
|
|
let sourceRate = Int(buffer.format.sampleRate.rounded())
|
|
let samples: [Float]
|
|
|
|
if let floatData = buffer.floatChannelData {
|
|
samples = mixFloatChannels(floatData, channelCount: channelCount, frameCount: frameCount)
|
|
} else if let int16Data = buffer.int16ChannelData {
|
|
samples = mixInt16Channels(int16Data, channelCount: channelCount, frameCount: frameCount)
|
|
} else {
|
|
return nil
|
|
}
|
|
|
|
let mono16k = PCM16LE.resampleLinear(samples: samples, sourceRate: sourceRate, targetRate: 16_000)
|
|
let data = PCM16LE.encode(samples: mono16k)
|
|
|
|
return PCMFrame(source: .microphone, sampleRate: 16_000, channels: 1, pcmS16LE: data)
|
|
}
|
|
|
|
private static func mixFloatChannels(_ channelData: UnsafePointer<UnsafeMutablePointer<Float>>, channelCount: Int, frameCount: Int) -> [Float] {
|
|
var mono = [Float](repeating: 0, count: frameCount)
|
|
let divisor = Float(channelCount)
|
|
|
|
for channel in 0..<channelCount {
|
|
let channelPointer = channelData[channel]
|
|
for frame in 0..<frameCount {
|
|
mono[frame] += channelPointer[frame] / divisor
|
|
}
|
|
}
|
|
|
|
return mono
|
|
}
|
|
|
|
private static func mixInt16Channels(_ channelData: UnsafePointer<UnsafeMutablePointer<Int16>>, channelCount: Int, frameCount: Int) -> [Float] {
|
|
var mono = [Float](repeating: 0, count: frameCount)
|
|
let divisor = Float(channelCount)
|
|
|
|
for channel in 0..<channelCount {
|
|
let channelPointer = channelData[channel]
|
|
for frame in 0..<frameCount {
|
|
mono[frame] += (Float(channelPointer[frame]) / Float(Int16.max)) / divisor
|
|
}
|
|
}
|
|
|
|
return mono
|
|
}
|
|
}
|