代码之家  ›  专栏  ›  技术社区  ›  DEADBEEF

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

  •  4
  • DEADBEEF  · 技术社区  · 7 年前

    我目前正在处理音频样本。 我从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.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)
    }
    

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

    4 回复  |  直到 7 年前
        1
  •  4
  •   Martin R    7 年前

    “投射”或“重新绑定”指针只会改变内存的方式 解释。你想要 计算 整数的浮点值, 尺寸)。

    不得不 迭代所有输入值 Array 创造:

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

    另一种选择是使用 加速框架:

    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))
    

    我不知道这是否更快,你得检查一下。 更新: 正如科尔格拉夫在回答中所证明的那样,速度更快。)

    显式循环也比使用 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
    

    在您的情况下,另一个选择可能是使用 CMBlockBufferGetDataPointer() 而不是 CMBlockBufferCopyDataBytes()

        2
  •  2
  •   user887210 user887210    7 年前

    如果使用 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: 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.
    

        3
  •  2
  •   DEADBEEF    7 年前

    @MartinR和@ColGraff给出了非常好的答案,感谢大家的快速回复。 AVAssetReaderAudioMixOutput 需要音频设置字典。我们可以在里面设置钥匙 AVLinearPCMIsFloatKey: true

    let samples = sampleData.withUnsafeBytes {
        UnsafeBufferPointer<Float>(start: $0, 
                                   count: sampleData.count / MemoryLayout<Float>.size)
    }
    
        4
  •  0
  •   swift2geek    7 年前

    适用于: 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