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和大小,所以找到数据子块还是比较简单的。
- 使用
FileHandle
打开文件
- 寻找字节 12,这会让您越过 header 并将您置于第一个子块的开头(应该是 fmt)。
- 读取 4 个字节并转换为字符串,然后再读取 4 个字节并转换为整数。字符串是子块名称,整数是该子块的大小。如果字符串不是“数据”,则向前查找“大小”字节数并重复步骤 3。
- 阅读文件的其余部分 - 这是您的 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
}
如何从 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和大小,所以找到数据子块还是比较简单的。
- 使用
FileHandle
打开文件
- 寻找字节 12,这会让您越过 header 并将您置于第一个子块的开头(应该是 fmt)。
- 读取 4 个字节并转换为字符串,然后再读取 4 个字节并转换为整数。字符串是子块名称,整数是该子块的大小。如果字符串不是“数据”,则向前查找“大小”字节数并重复步骤 3。
- 阅读文件的其余部分 - 这是您的 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
}