在swift中将浮点数组写入wav音频文件

时间:2017-02-11 17:29:53

标签: ios swift audio core-audio audiotoolbox

我现在有这个流程:我用AudioEngine录制音频,将其发送到音频处理库并获得音频缓冲区,然后我有强烈的意愿将其写入wav文件,但我完全糊涂了怎么样在swift中做到这一点。

我已经从另一个stackoverflow回答尝试了这个片段,但它写了一个空的和损坏的文件。(load a pcm into a AVAudioPCMBuffer

//get data from library

var len : CLong = 0
let res: UnsafePointer<Double> = getData(CLong(), &len )
let bufferPointer: UnsafeBufferPointer = UnsafeBufferPointer(start: res, count: len)

//tranform it to Data

let arrayDouble = Array(bufferPointer)
let arrayFloats = arrayDouble.map{Float($0)}
let data = try Data(buffer: bufferPointer)

//attempt to write in file
    do {
        let format = AVAudioFormat(commonFormat: .pcmFormatFloat32, sampleRate: 16000, channels: 2, interleaved: false)

        var buffer = AVAudioPCMBuffer(pcmFormat: format, frameCapacity: AVAudioFrameCount(data.count))
        buffer.floatChannelData!.pointee.withMemoryRebound(to: UInt8.self, capacity: data.count) {
            let stream = OutputStream(toBuffer: $0, capacity: data.count)
            stream.open()
            _ = data.withUnsafeBytes {
                stream.write($0, maxLength: data.count)
            }
            stream.close()
        }
        //settings are from AudioEngine.inputNode!.inputFormat(forBus: 0).settings

        var audioFile = try AVAudioFile(forWriting: url, settings: settings)
        try audioFile.write(from: buffer)

    } catch let error as NSError {
        print("ERROR HERE", error.localizedDescription)
    }

所以,我想我对floatChannelData的这个变换做错了或者一切都搞错了。任何建议或指示在哪里阅读它都会很棒!

2 个答案:

答案 0 :(得分:5)

有了很好的同事帮助,我们设法让它发挥作用。显然,AudioPCMBuffer在填充后还需要通知它的新大小。 我也使用完全错误的格式。

以下是代码:

let SAMPLE_RATE =  Float64(16000.0)

let outputFormatSettings = [
    AVFormatIDKey:kAudioFormatLinearPCM,
    AVLinearPCMBitDepthKey:32,
    AVLinearPCMIsFloatKey: true,
    //  AVLinearPCMIsBigEndianKey: false,
    AVSampleRateKey: SAMPLE_RATE,
    AVNumberOfChannelsKey: 1
    ] as [String : Any]

let audioFile = try? AVAudioFile(forWriting: url, settings: outputFormatSettings, commonFormat: AVAudioCommonFormat.pcmFormatFloat32, interleaved: true)

let bufferFormat = AVAudioFormat(settings: outputFormatSettings)

let outputBuffer = AVAudioPCMBuffer(pcmFormat: bufferFormat, frameCapacity: AVAudioFrameCount(buff.count))

// i had my samples in doubles, so convert then write

for i in 0..<buff.count {
    outputBuffer.floatChannelData!.pointee[i] = Float( buff[i] )
}
outputBuffer.frameLength = AVAudioFrameCount( buff.count )

do{
    try audioFile?.write(from: outputBuffer)

} catch let error as NSError {
    print("error:", error.localizedDescription)
}

答案 1 :(得分:0)

<块引用>

Swift 5 更新

这是在 swift 5 中将浮点数数组写入 wav 音频文件的更新。该函数可用作以下saveWav([channel1, channel2])

func saveWav(_ buf: [[Float]]) {
    if let format = AVAudioFormat(commonFormat: .pcmFormatFloat32, sampleRate: 44100, channels: 2, interleaved: false) {
        let pcmBuf = AVAudioPCMBuffer(pcmFormat: format, frameCapacity: AVAudioFrameCount(buf[0].count))
        memcpy(pcmBuf?.floatChannelData?[0], buf[0], 4 * buf[0].count)
        memcpy(pcmBuf?.floatChannelData?[1], buf[1], 4 * buf[1].count)
        pcmBuf?.frameLength = UInt32(buf[0].count)

        let fileManager = FileManager.default
        do {
            let documentDirectory = try fileManager.url(for: .documentDirectory, in: .userDomainMask, appropriateFor:nil, create:false)
            try FileManager.default.createDirectory(atPath: documentDirectory.path, withIntermediateDirectories: true, attributes: nil)
            let fileURL = documentDirectory.appendingPathComponent("out.wav")
            print(fileURL.path)
            let audioFile = try AVAudioFile(forWriting: fileURL, settings: format.settings)
            try audioFile.write(from: pcmBuf!)
        } catch {
            print(error)
        }
    }
}

为确保上述函数正常工作,请使用以下函数将音频文件转换为浮点数组,并使用 saveWav 将其保存回音频文件

do {
    guard let url = Bundle.main.url(forResource: "audio_example", withExtension: "wav") else { return }
    let file = try AVAudioFile(forReading: url)
    if let format = AVAudioFormat(commonFormat: .pcmFormatFloat32, sampleRate: file.fileFormat.sampleRate, channels: file.fileFormat.channelCount, interleaved: false), let buf = AVAudioPCMBuffer(pcmFormat: format, frameCapacity: AVAudioFrameCount(file.length)) {

        try file.read(into: buf)
        guard let floatChannelData = buf.floatChannelData else { return }
        let frameLength = Int(buf.frameLength)
        // we convert audio using audio pcm buffer to arrays of floats with two channels
        let channel1 = Array(UnsafeBufferPointer(start:floatChannelData[0], count:frameLength))
        let channel2 = Array(UnsafeBufferPointer(start:floatChannelData[1], count:frameLength))
        // we save the audio back using saveWave function
        saveWav([channel1,channel2])
    }
} catch {
    print("Audio Error: \(error)")
}