如何将 Int16 音频样本数据转换为浮点音频样本数组
How to convert Data of Int16 audio samples to array of float audio samples
我目前正在处理音频样本。
我从 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: [=10=], count: sampleData.count / MemoryLayout<Int16>.size))
}
// The only way I found to convert [Int16] -> [Float]...
return samples.map { Float([=10=]) / 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)
}
如您所见,我发现转换 [Int16] -> [Float] 的唯一方法是 samples.map { Float([=12=]) / Float(Int16.max)
,但这样做会增加我的处理时间。是否存在将 Int16 指针转换为 Float 指针的其他方法?
对于:Xcode 8.3.3 • Swift 3.1
extension Collection where Iterator.Element == Int16 {
var floatArray: [Float] {
return flatMap{ Float([=10=]) }
}
}
用法:
let int16Array: [Int16] = [1, 2, 3 ,4]
let floatArray = int16Array.floatArray
"Casting" 或 "rebinding" 指针只会改变内存的方式
解释。您想从整数中计算浮点值,
新值有不同的内存表示(也有不同的
尺寸)。
因此你不知何故必须遍历所有输入值
并计算新值。你可以做的是省略 Array
创建:
let samples = sampleData.withUnsafeBytes {
UnsafeBufferPointer<Int16>(start: [=10=], count: sampleData.count / MemoryLayout<Int16>.size)
}
return samples.map { Float([=10=]) / Float(Int16.max) }
另一种选择是使用 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([=11=], 1, &floats, 1, vDSP_Length(numSamples))
}
// Scaling:
vDSP_vsdiv(&floats, 1, &factor, &floats, 1, vDSP_Length(numSamples))
我不知道那是否更快,您必须检查一下。
(更新: 它更快,正如 ColGraff 在他的回答中所展示的那样。)
显式循环也比使用 map
:
快得多
let factor = Float(Int16.max)
let samples = sampleData.withUnsafeBytes {
UnsafeBufferPointer<Int16>(start: [=12=], 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
您的情况的另一个选择可能是使用 CMBlockBufferGetDataPointer()
而不是 CMBlockBufferCopyDataBytes()
进入分配的内存。
如果使用 Accelerate Framework 进行转换,您可以做得更好:
import Accelerate
// Set up random [Int]
var randomInt = [Int16]()
randomInt.reserveCapacity(10000)
for _ in 0..<randomInt.capacity {
let value = Int16(Int32(arc4random_uniform(UInt32(UInt16.max))) - Int32(UInt16.max / 2))
randomInt.append(value)
}
// Time elapsed helper:
func printTimeElapsedWhenRunningCode(title:String, operation:()->()) {
let startTime = CFAbsoluteTimeGetCurrent()
operation()
let timeElapsed = CFAbsoluteTimeGetCurrent() - startTime
print("Time elapsed for \(title): \(timeElapsed) s.")
}
// Testing
printTimeElapsedWhenRunningCode(title: "vDSP") {
var randomFloat = [Float](repeating: 0, count: randomInt.capacity)
vDSP_vflt16(randomInt, 1, &randomFloat, 1, vDSP_Length(randomInt.capacity))
}
printTimeElapsedWhenRunningCode(title: "map") {
randomInt.map { Float([=10=]) }
}
// Results
//
// Time elapsed for vDSP : 0.000429034233093262 s.
// Time elapsed for flatMap: 0.00233501195907593 s.
速度提高了大约 5 倍。
(编辑:添加了 Martin R 建议的一些更改)
@MartinR 和@ColGraff 给出了非常好的答案,感谢大家的快速回复。
但是我找到了一种更简单的方法来做到这一点而无需任何计算。 AVAssetReaderAudioMixOutput
需要音频设置字典。在里面我们可以设置key AVLinearPCMIsFloatKey: true
。这样我就会像这样读取我的数据
let samples = sampleData.withUnsafeBytes {
UnsafeBufferPointer<Float>(start: [=10=],
count: sampleData.count / MemoryLayout<Float>.size)
}
我目前正在处理音频样本。
我从 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: [=10=], count: sampleData.count / MemoryLayout<Int16>.size))
}
// The only way I found to convert [Int16] -> [Float]...
return samples.map { Float([=10=]) / 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)
}
如您所见,我发现转换 [Int16] -> [Float] 的唯一方法是 samples.map { Float([=12=]) / Float(Int16.max)
,但这样做会增加我的处理时间。是否存在将 Int16 指针转换为 Float 指针的其他方法?
对于:Xcode 8.3.3 • Swift 3.1
extension Collection where Iterator.Element == Int16 {
var floatArray: [Float] {
return flatMap{ Float([=10=]) }
}
}
用法:
let int16Array: [Int16] = [1, 2, 3 ,4]
let floatArray = int16Array.floatArray
"Casting" 或 "rebinding" 指针只会改变内存的方式 解释。您想从整数中计算浮点值, 新值有不同的内存表示(也有不同的 尺寸)。
因此你不知何故必须遍历所有输入值
并计算新值。你可以做的是省略 Array
创建:
let samples = sampleData.withUnsafeBytes {
UnsafeBufferPointer<Int16>(start: [=10=], count: sampleData.count / MemoryLayout<Int16>.size)
}
return samples.map { Float([=10=]) / Float(Int16.max) }
另一种选择是使用 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([=11=], 1, &floats, 1, vDSP_Length(numSamples))
}
// Scaling:
vDSP_vsdiv(&floats, 1, &factor, &floats, 1, vDSP_Length(numSamples))
我不知道那是否更快,您必须检查一下。 (更新: 它更快,正如 ColGraff 在他的回答中所展示的那样。)
显式循环也比使用 map
:
let factor = Float(Int16.max)
let samples = sampleData.withUnsafeBytes {
UnsafeBufferPointer<Int16>(start: [=12=], 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
您的情况的另一个选择可能是使用 CMBlockBufferGetDataPointer()
而不是 CMBlockBufferCopyDataBytes()
进入分配的内存。
如果使用 Accelerate Framework 进行转换,您可以做得更好:
import Accelerate
// Set up random [Int]
var randomInt = [Int16]()
randomInt.reserveCapacity(10000)
for _ in 0..<randomInt.capacity {
let value = Int16(Int32(arc4random_uniform(UInt32(UInt16.max))) - Int32(UInt16.max / 2))
randomInt.append(value)
}
// Time elapsed helper:
func printTimeElapsedWhenRunningCode(title:String, operation:()->()) {
let startTime = CFAbsoluteTimeGetCurrent()
operation()
let timeElapsed = CFAbsoluteTimeGetCurrent() - startTime
print("Time elapsed for \(title): \(timeElapsed) s.")
}
// Testing
printTimeElapsedWhenRunningCode(title: "vDSP") {
var randomFloat = [Float](repeating: 0, count: randomInt.capacity)
vDSP_vflt16(randomInt, 1, &randomFloat, 1, vDSP_Length(randomInt.capacity))
}
printTimeElapsedWhenRunningCode(title: "map") {
randomInt.map { Float([=10=]) }
}
// Results
//
// Time elapsed for vDSP : 0.000429034233093262 s.
// Time elapsed for flatMap: 0.00233501195907593 s.
速度提高了大约 5 倍。
(编辑:添加了 Martin R 建议的一些更改)
@MartinR 和@ColGraff 给出了非常好的答案,感谢大家的快速回复。
但是我找到了一种更简单的方法来做到这一点而无需任何计算。 AVAssetReaderAudioMixOutput
需要音频设置字典。在里面我们可以设置key AVLinearPCMIsFloatKey: true
。这样我就会像这样读取我的数据
let samples = sampleData.withUnsafeBytes {
UnsafeBufferPointer<Float>(start: [=10=],
count: sampleData.count / MemoryLayout<Float>.size)
}