【问题标题】:How can I generate an array of floats from an audio file in Swift如何从 Swift 中的音频文件生成浮点数组
【发布时间】:2016-04-17 12:26:47
【问题描述】:

我想将 mp3 和 wav 音频文件加载为浮点数或双精度数组,类似于 scipy 中的 io.wavfile.read 函数。我可以通过将音频流写入缓冲区来处理麦克风数据或播放音频。但是,我不确定如何一次加载所有音频文件的数据。

-- 更新

对于将来使用音频信号数据的任何人,这里有一个功能可以解决问题。它基于 Rhythmic Fistman 的回答。

    func loadAudioSignal(audioURL: NSURL) -> (signal: [Float], rate: Double, frameCount: Int) {
        let file = try! AVAudioFile(forReading: audioURL)
        let format = AVAudioFormat(commonFormat: .PCMFormatFloat32, sampleRate: file.fileFormat.sampleRate, channels: file.fileFormat.channelCount, interleaved: false)
        let buf = AVAudioPCMBuffer(PCMFormat: format, frameCapacity: UInt32(file.length))
        try! file.readIntoBuffer(buf) // You probably want better error handling
        let floatArray = Array(UnsafeBufferPointer(start: buf.floatChannelData[0], count:Int(buf.frameLength)))
        return (signal: floatArray, rate: file.fileFormat.sampleRate, frameCount: Int(file.length))
    }

【问题讨论】:

  • 看看EZAudiogithub.com/syedhali/EZAudio
  • 这是一个不错的库,谢谢。我无法让它在 Swift 中为我工作,我会更新我的问题。

标签: ios swift audio


【解决方案1】:

AVAudioFile内置于iOS(和OS X),非常方便,还会为你做格式转换:

import AVFoundation
// ...

let url = NSBundle.mainBundle().URLForResource("your audio file", withExtension: "wav")
let file = try! AVAudioFile(forReading: url!)
let format = AVAudioFormat(commonFormat: .PCMFormatFloat32, sampleRate: file.fileFormat.sampleRate, channels: 1, interleaved: false)

let buf = AVAudioPCMBuffer(PCMFormat: format, frameCapacity: 1024)
try! file.readIntoBuffer(buf)

// this makes a copy, you might not want that
let floatArray = Array(UnsafeBufferPointer(start: buf.floatChannelData[0], count:Int(buf.frameLength)))

print("floatArray \(floatArray)\n")

遗憾的是,对于双打来说,用.PCMFormatFloat64 替换.PCMFormatFloat32 似乎是不够的,因为AVAudioPCMBuffer 没有float64ChannelData 方法。

更新因为我不太了解swift

您可以通过使用UnsafeBufferPointer 来避免复制数组,这是一个非常好的集合类型:

let floatArray = UnsafeBufferPointer(start: buf.floatChannelData[0], count:Int(buf.frameLength))

【讨论】:

  • 我们可以再次将数组转换为缓冲区吗?因为在水龙头中我想互相附加数组,当它们达到一定长度(5秒)时,我想将它们保存到磁盘。还是我应该直接附加缓冲区?但我找不到办法做到这一点。如果你愿意,我可以提出一个问题。
  • 请这样做,尽管您可能已经想通了。我才看到这个。我应该为我们使用的任何版本的 swift 更新这个答案。是 4 吗?
【解决方案2】:

要找到有关UnsafeBufferPointer 的所有信息真的很棘手

我在这里发布 Swift 5.0 的更新代码

if let url = Bundle.main.url(forResource: "silence", withExtension: "mp3") {
    let file = try! AVAudioFile(forReading: url)
    if let format = AVAudioFormat(commonFormat: .pcmFormatFloat32, sampleRate: file.fileFormat.sampleRate, channels: 1, interleaved: false) {
        if let buf = AVAudioPCMBuffer(pcmFormat: format, frameCapacity: 1024) {
            try! file.read(into: buf)

            // this makes a copy, you might not want that
            let floatArray = UnsafeBufferPointer(start: buf.floatChannelData![0], count:Int(buf.frameLength))
            // convert to data
            var data = Data()
            for buf in floatArray {
                data.append(withUnsafeBytes(of: buf) { Data($0) })
            }
            // use the data if required.
        }
    }
}

希望对你有帮助:)

【讨论】:

    【解决方案3】:

    以上答案对我不起作用,我使用的是 Swift5,在这里找到了适合我的扩展:https://gist.github.com/jtodaone/f2fa59c19794811dbe989dff65a772bc

    这也是我在 Playground 上使用代码的方式

    import UIKit
    import AVFoundation
    
    let filePath: String = Bundle.main.path(forResource: "nameOfFile", ofType: "wav")!
    print("\(filePath)")
    let fileURL: NSURL = NSURL(fileURLWithPath: filePath)
    let audioFile = try AVAudioFile(forReading: fileURL as URL)
    let audioFormat = audioFile.processingFormat
    let audioFrameCount = UInt32(audioFile.length)
    let audioFileBuffer = AVAudioPCMBuffer(pcmFormat: audioFormat, frameCapacity: audioFrameCount)
    
    try audioFile.read(into: audioFileBuffer!)
    
    extension AudioBuffer {
        func array() -> [Float] {
            return Array(UnsafeBufferPointer(self))
        }
    }
    
    extension AVAudioPCMBuffer {
        func array() -> [Float] {
            return self.audioBufferList.pointee.mBuffers.array()
        }
    }
    
    extension Array where Element: FloatingPoint {
        mutating func buffer() -> AudioBuffer {
            return AudioBuffer(mNumberChannels: 1, mDataByteSize: UInt32(self.count * MemoryLayout<Element>.size), mData: &self)
        }
    }
    
    let array = audioFileBuffer?.array()
    print(array?.count) //Optional(2705408)
    

    【讨论】:

      【解决方案4】:

      我已将代码从@rhythmicfistman 更新为Swift5。大约需要进行十几个更改:显然swift 世界的情况发生了巨大变化。

      func readWavIntoFloats(fname: String, ext: String) -> [Float] {
      
          let url = Bundle.main.url(forResource: fname, withExtension: ext)
          let file = try! AVAudioFile(forReading: url!)
          let format = AVAudioFormat(commonFormat: .pcmFormatFloat32, sampleRate: file.fileFormat.sampleRate, channels: 1, interleaved: false) ?? <#default value#>
      
          let buf = AVAudioPCMBuffer(pcmFormat: format, frameCapacity: 1024)!
          try! file.read(into: buf)
      
          // this makes a copy, you might not want that
          let floatArray = Array(UnsafeBufferPointer(start: buf.floatChannelData?[0], count:Int(buf.frameLength)))
      
          return floatArray
      
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-05-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-03-06
        • 2016-11-03
        • 1970-01-01
        • 2011-12-14
        相关资源
        最近更新 更多