AVAudioRecorder 生成奇怪的 Wav 文件(错误 header)

AVAudioRecorder generates strange Wav file(wrong header)

如何从 AVAudioRecorder 文件中获取唯一的 PCM 数据? 这些是我用来录制文件的设置:

        let settings : [String : Any] = [
            AVFormatIDKey: Int(kAudioFormatLinearPCM),
            AVSampleRateKey: Int(stethoscopeSampleRateDefault),
            AVNumberOfChannelsKey: 1,
            AVEncoderAudioQualityKey: AVAudioQuality.medium.rawValue,
        ]

结果是带有奇怪 header 的奇怪 wav 文件。 我怎样才能只从中提取 PCM 数据?

wav 文件中的实际声音数据位于该文件的“数据”子块中 - this format description 可能会帮助您可视化必须浏览的结构。但也许让您感到困惑的是 Apple 在声音数据之前包含一个名为“fllr”的额外子块,因此您也必须寻找过去。幸运的是每个子块都有一个id和大小,所以找到数据子块还是比较简单的。

  1. 使用FileHandle
  2. 打开文件
  3. 寻找字节 12,这会让您越过 header 并将您置于第一个子块的开头(应该是 fmt)。
  4. 读取 4 个字节并转换为字符串,然后再读取 4 个字节并转换为整数。字符串是子块名称,整数是该子块的大小。如果字符串不是“数据”,则向前查找“大小”字节数并重复步骤 3。
  5. 阅读文件的其余部分 - 这是您的 PCM 数据。

在 Jamie 的指导下,我设法解决了这个问题。这是我的代码:

    func extractSubchunks(data:Data) -> RiffFile?{
        var data = data
        var chunks = [SubChunk]()
        let position = data.subdata(in: 8..<12)
        let filelength = Int(data.subdata(in: 4..<8).uint32)
        let wave = String(bytes: position, encoding: .utf8) ?? "NoName"
        guard wave == "WAVE" else {
            print("File is \(wave) not WAVE")
            return nil
        }
        data.removeSubrange(0..<12)
        print("Found chunks")
        while data.count != 0{
            let position = data.subdata(in: 0..<4)
            let length = Int(data.subdata(in: 4..<8).uint32)
            guard let current = String(bytes: position, encoding: .utf8) else{
                return nil
            }
            data.removeSubrange(0..<8)
            let chunkData = data.subdata(in: 0..<length)
            data.removeSubrange(0..<length)
            let subchunk = SubChunk(name: current, size: length, data: chunkData)
            chunks.append(subchunk)
            print(subchunk.debugDescription)
        }
        let riff = RiffFile(size: filelength, subChunks: chunks)
        return riff
    }

这是 RiffFile 和 SubChunk 结构的定义:

struct RiffFile {
    var size : Int
    var subChunks : [SubChunk]
}

struct SubChunk {
    var debugDescription: String {
        return "name : \(name) size : \(size) dataAssignedsize : \(data.count)"
    }
    var name : String
    var size : Int
    var data : Data
}