How can I generate an array of floats from an audio file in Swift

前端 未结 4 580
遇见更好的自我
遇见更好的自我 2020-12-31 10:35

I would like to load mp3 and wav audio files as arrays of floats or doubles, similar to the io.wavfile.read function in scipy. I can do this with microphone data or playing

相关标签:
4条回答
  • 2020-12-31 10:52

    Above answers didn't work for me, I'm using Swift5, found this extensions that worked for me here: https://gist.github.com/jtodaone/f2fa59c19794811dbe989dff65a772bc

    Also here is how i use the code on 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)
    
    0 讨论(0)
  • 2020-12-31 10:56

    It's really tricky to find everything about UnsafeBufferPointer

    Here I am posting updated code for 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.
            }
        }
    }
    

    Hope it will help you :)

    0 讨论(0)
  • 2020-12-31 11:04

    AVAudioFile built-in to iOS (and OS X), is very convenient and will also do format conversions for you:

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

    Sadly, for doubles it doesn't seem to be enough to substitute .PCMFormatFloat32 with .PCMFormatFloat64 because AVAudioPCMBuffer doesn't have a float64ChannelData method.

    update because I don't know swift well

    You can avoid copying the array by working with the UnsafeBufferPointer, which is a perfectly good collection type:

    let floatArray = UnsafeBufferPointer(start: buf.floatChannelData[0], count:Int(buf.frameLength))
    
    0 讨论(0)
  • 2020-12-31 11:06

    I have updated the code from @rhythmicfistman to Swift5. There were about a dozen changes to make: apparently things changed dramatically in swift world.

    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
    
    }
    
    0 讨论(0)
提交回复
热议问题