如何将 Int16 音频样本数据转换为浮点音频样本数组

DEA*_*EEF 4 audio avfoundation nsdata nsarray swift

我目前正在处理音频样本。我从 AVAssetReader 获取它们并有一个CMSampleBuffer类似这样的内容:

guard let sampleBuffer = readerOutput.copyNextSampleBuffer() else {
guard reader.status == .completed else { return nil }
// Completed
// samples is an array of Int16
let samples = sampleData.withUnsafeBytes {
  Array(UnsafeBufferPointer<Int16>(
  start: $0, count: sampleData.count / MemoryLayout<Int16>.size))
 }

 // The only way I found to convert [Int16] -> [Float]...
 return samples.map { Float($0) / Float(Int16.max)}
}

guard let blockBuffer = CMSampleBufferGetDataBuffer(sampleBuffer) else {
return nil
}

let length = CMBlockBufferGetDataLength(blockBuffer)
let sampleBytes = UnsafeMutablePointer<UInt8>.allocate(capacity: length)
      CMBlockBufferCopyDataBytes(blockBuffer, 0, length, sampleBytes)

      sampleData.append(sampleBytes, count: length)
}
Run Code Online (Sandbox Code Playgroud)

正如你所看到的,我发现转换 [Int16] -> [Float] 的唯一方法是,samples.map { Float($0) / Float(Int16.max)但通过这样做,我的处理时间增加了。是否存在其他方法将 Int16 指针转换为 Float 指针?

Mar*_*n R 5

“转换”或“重新绑定”指针只会改变内存的解释方式。您想要从整数计算浮点值,新值具有不同的内存表示形式(并且大小也不同)。

因此,您必须以某种方式迭代所有输入值并计算新值。你可以做的是省略Array 创建:

let samples = sampleData.withUnsafeBytes {
    UnsafeBufferPointer<Int16>(start: $0, count: sampleData.count / MemoryLayout<Int16>.size)
}
return samples.map { Float($0) / Float(Int16.max) }
Run Code Online (Sandbox Code Playgroud)

另一种选择是使用Accelerate 框架中的vDSP函数:

import Accelerate
// ...

let numSamples = sampleData.count / MemoryLayout<Int16>.size
var factor = Float(Int16.max)
var floats: [Float] = Array(repeating: 0.0, count: numSamples)

// Int16 array to Float array:
sampleData.withUnsafeBytes {
    vDSP_vflt16($0, 1, &floats, 1, vDSP_Length(numSamples))
}
// Scaling:
vDSP_vsdiv(&floats, 1, &factor, &floats, 1, vDSP_Length(numSamples))
Run Code Online (Sandbox Code Playgroud)

不知道是不是更快,你得检查一下。(更新:速度更快,正如 ColGraff 在他的回答中所证明的那样。)

显式循环也比使用快得多map

let factor = Float(Int16.max)
let samples = sampleData.withUnsafeBytes {
    UnsafeBufferPointer<Int16>(start: $0, count: sampleData.count / MemoryLayout<Int16>.size)
}
var floats: [Float] = Array(repeating: 0.0, count: samples.count)
for i in 0..<samples.count {
    floats[i] = Float(samples[i]) / factor
}
return floats
Run Code Online (Sandbox Code Playgroud)

您的情况的另一个选择可能是使用CMBlockBufferGetDataPointer()而不是CMBlockBufferCopyDataBytes() 分配到分配的内存中。