Swift 如何将Int16音频样本数据转换为浮点音频样本数组
我目前正在处理音频样本。 我从Avassetrader获得它们,并有一个Swift 如何将Int16音频样本数据转换为浮点音频样本数组,swift,audio,nsarray,avfoundation,nsdata,Swift,Audio,Nsarray,Avfoundation,Nsdata,我目前正在处理音频样本。 我从Avassetrader获得它们,并有一个CMSampleBuffer,类似这样: guard let sampleBuffer = readerOutput.copyNextSampleBuffer() else { guard reader.status == .completed else { return nil } // Completed // samples is an array of Int16 let samples = sampleData.w
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)
}
guard let sampleBuffer=readerOutput.copyNextSampleBuffer()else{
guard reader.status==.completed else{return nil}
//完成
//samples是一个Int16数组
让samples=sampleData.withUnsafeBytes{
数组(非安全缓冲指针)(
开始:$0,计数:sampleData.count/MemoryLayout.size)
}
//我找到的唯一转换[Int16]->[Float]的方法。。。
return samples.map{Float($0)/Float(Int16.max)}
}
guard let blockBuffer=CMSampleBufferGetDataBuffer(sampleBuffer)else{
归零
}
let length=CMBlockBufferGetDataLength(块缓冲区)
让sampleBytes=UnsafeMutablePointer.allocate(容量:长度)
CMBlockBufferCopyDataBytes(块缓冲区,0,长度,采样字节)
追加(sampleBytes,计数:长度)
}
正如您所看到的,我找到的唯一转换[Int16]->[Float]的方法是
samples.map{Float($0)/Float(Int16.max)
,但通过这样做,我的处理时间在增加。是否存在将Int16指针转换为Float指针的其他方法?用于:Xcode 8.3.3•Swift 3.1
extension Collection where Iterator.Element == Int16 {
var floatArray: [Float] {
return flatMap{ Float($0) }
}
}
用法:
let int16Array: [Int16] = [1, 2, 3 ,4]
let floatArray = int16Array.floatArray
“投射”或“重新绑定”指针只会改变内存的使用方式
已解释。要从整数计算浮点值,
新值具有不同的内存表示形式(以及不同的
尺寸)
因此,您必须以某种方式迭代所有输入值
然后计算新的值。您可以做的是省略数组
创建:
let samples = sampleData.withUnsafeBytes {
UnsafeBufferPointer<Int16>(start: $0, count: sampleData.count / MemoryLayout<Int16>.size)
}
return samples.map { Float($0) / Float(Int16.max) }
在您的情况下,另一个选项可能是使用
CMBlockBufferGetDataPointer()
而不是CMBlockBufferCopyDataBytes()
到分配的内存中。如果使用进行转换,您可以做得更好:
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: https://stackoverflow.com/a/25022722/887210
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($0) }
}
// Results
//
// Time elapsed for vDSP : 0.000429034233093262 s.
// Time elapsed for flatMap: 0.00233501195907593 s.
导入加速
//设置随机[Int]
var randomInt=[Int16]()
随机点保留容量(10000)
对于0中的uu…()){
让startTime=CFAbsoluteTimeGetCurrent()
操作()
让时间流逝=CFAbsoluteTimeGetCurrent()-startTime
打印(“已用时间\(标题):\(已用时间)s.)
}
//测试
运行代码时的打印时间延迟(标题:“vDSP”){
var randomFloat=[Float](重复:0,计数:randomInt.capacity)
vDSP_vflt16(随机整数,1和随机浮点,1,vDSP_长度(随机整数容量))
}
运行代码时的打印时间延迟(标题:“地图”){
randomInt.map{Float($0)}
}
//结果
//
//vDSP经过的时间:0.000429034233093262秒。
//flatMap经过的时间:0.00233501195907593秒。
它的速度提高了大约5倍
(编辑:添加了Martin R建议的一些更改)@Martin R和@ColGraff给出了非常好的答案,感谢大家和快速回复。 然而,我发现了一种更简单的方法,无需任何计算。
avassetraderaudiomixoutput
需要一个音频设置字典。在里面,我们可以设置键AVLinearPCMIsFloatKey:true
。这样我就可以像这样读取数据
let samples = sampleData.withUnsafeBytes {
UnsafeBufferPointer<Float>(start: $0,
count: sampleData.count / MemoryLayout<Float>.size)
}
let samples=sampleData.withUnsafeBytes{
UnsafeBufferPointer(开始:$0,
计数:sampleData.count/MemoryLayout.size)
}
如果您打算将整数-32767…32767转换为-1.0…1.0,那么就是这样做的。但是您不需要创建数组,您可以映射UnsafeBufferPointer。如何“强制转换指针”?1)Int16是2个字节,浮点是4个字节。2)从整数计算浮点值。@MartinR事实上,我希望迭代通过执行Int16value/Int16.max
来计算浮点。在Swift中不是说“这是一个Int16的指针,你能给我计算出的浮点指针吗?”?当我使用UnsafemtableRawPointer(UInt8的指针)时,我能够做一些类似的事情:var floatValues=bytes.bindMemory(to:Float.self,capacity:bytesTotal)
Casting(或Swift行话中的“重新绑定”)只会改变内存的解释方式,而不会改变内存本身。抱歉,也许我解释得不好,但我想将Int16音频样本转换为浮点音频样本,而不必像在floatArray方法中那样重新迭代整个样本。似乎我们有相同的想法:)–一些备注:您的代码崩溃,因为randomFloat是在设置randomInt.capacity之前创建的,这使它成为一个空数组。您只需将&randomInt.map
传递给vDSP_vflt16()。vDSP测量应包括randomInt数组创建。没有理由使用flatMap,只需randomInt.map{Float($0)}
@MartinR Ahh是的,我将randomFloat
的初始化移到更早的时间来整理代码,但忽略了测试。我会更正它并重新测试。谢谢!有趣的是,对于0中的I..@MartinR,它更接近了。我仍然看到了一些改进,在使用vDSP vs for loop时,可能有50%到75%的时间。关于f vDSP、缓存以及此聊天中对测试的影响:您的第一个代码块有问题,它无法将UnsafeBufferPointer.init
start
参数从UnsafeRawBufferPointer
转换为UnsafePointer?
@ColGraff:MysampleData
具有数据类型>.我从问题中的sampleData.withUnsafeBytes()
猜到了。明白了,这是有道理的。
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: https://stackoverflow.com/a/25022722/887210
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($0) }
}
// Results
//
// Time elapsed for vDSP : 0.000429034233093262 s.
// Time elapsed for flatMap: 0.00233501195907593 s.
let samples = sampleData.withUnsafeBytes {
UnsafeBufferPointer<Float>(start: $0,
count: sampleData.count / MemoryLayout<Float>.size)
}