12

我正在 iPhone 上进行一个项目,我正在使用 AVAudioRecorder 从设备麦克风录制音频,然后将操纵录音。

为了确保我从文件中正确读取样本,我使用 python 的 wave 模块来查看它是否返回相同的样本。

但是,python 的 wave 模块在尝试打开 AVAudioRecorder 保存的 wav 文件时返回“fmt 块和/或数据块丢失”。

这些是我用来记录文件的设置:

[audioSettings setObject:[NSNumber numberWithInt:kAudioFormatLinearPCM] forKey:AVFormatIDKey];
[audioSettings setObject:[NSNumber numberWithInt:16] forKey:AVLinearPCMBitDepthKey];
[audioSettings setObject:[NSNumber numberWithBool:NO] forKey:AVLinearPCMIsBigEndianKey];
[audioSettings setObject:[NSNumber numberWithFloat:4096] forKey:AVSampleRateKey];
[audioSettings setObject:[NSNumber numberWithInt:1] forKey:AVNumberOfChannelsKey];
[audioSettings setObject:[NSNumber numberWithBool:YES] forKey:AVLinearPCMIsNonInterleaved];
[audioSettings setObject:[NSNumber numberWithBool:NO] forKey:AVLinearPCMIsFloatKey]; 

之后,我只是打电话给 recordForDuration 来实际进行录音。

录制成功——我可以播放文件等,我可以使用 AudioFile 服务读取样本,但我无法验证它,因为我无法使用 Python 的 wave 模块打开文件。

这是文件的前 128 个字节的样子:

1215N:~/Downloads$ od -c --read-bytes 128 testFile.wav
0000000   R   I   F   F   x   H 001  \0   W   A   V   E   f   m   t    
0000020 020  \0  \0  \0 001  \0 001  \0   @ 037  \0  \0 200   >  \0  \0
0000040 002  \0 020  \0   F   L   L   R 314 017  \0  \0  \0  \0  \0  \0
0000060  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0
*
0000200

知道我需要做什么来确保 AVAudioRecorder 写出正确的 WAV 标头吗?

4

2 回答 2

40

"FLLR"Apple 软件通常会在子块之后和"fmt "子块之前创建具有非标准(但符合“规范”)子块的 WAVE 文件"data"。我假设“FLLR”代表“填充”,并且我假设子块的目的是启用某种数据对齐优化。子块的长度通常约为 4000 字节,但其实际长度可能会根据其前面的数据长度而有所不同。

将任意子块添加到 WAVE 文件通常被认为符合规范,因为 WAVE 是RIFF的子集,并且 RIFF 文件处理中的常见做法是忽略具有无法识别标识符的块和子块。该标识符"FLLR"是“非标准的”,因此遇到它的任何软件都应该忽略它。

有相当多的软件对 WAVE 格式的处理比应有的严格得多,我怀疑您正在使用的库可能是这些软件之一。例如,我见过假设音频字节总是从偏移量 44 开始的软件——这是一个不正确的假设。

事实上,在 WAVE 文件中查找音频字节必须通过查找"data"RIFF 中子块的位置和大小来完成;这是在 WAVE 文件中定位音频字节的正确方法。

正确阅读 WAVE 文件必须真正从定位和识别 RIFF 子块的练习开始。RIFF 子块有一个 8 字节的标头:4 字节用于标识符/名称字段,传统上用人类可读的 ASCII 字符(例如"fmt ")填充,以及一个 4 字节的小端无符号整数,指定子块数据中的字节数有效负载——子块的数据有效负载紧跟在其 8 字节标头之后。

WAVE 文件格式保留某些对 WAVE 格式有意义的子块标识符(或“名称”)。每个 WAVE 文件中必须始终出现至少两个子块:

  1. "fmt "- 具有此标识符的子块有一个有效载荷,它描述了有关音频格式的基本信息:采样率、位深度等。
  2. "data"- 具有此标识符的子块在其有效负载中具有实际的音频字节

"fact"是下一个最常见的子块标识符。它通常出现在使用压缩编解码器(例如 μ-law)的 WAVE 文件中。有关当今在野外使用的各种子块标识符的更多信息,以及有关其有效负载结构的信息,请参阅此爱好者网页。

从纯粹的 RIFF 角度来看,子块不需要以任何特定的顺序出现在文件中,也不需要以任何特定的固定偏移量出现。然而在实践中,几乎所有软件都期望"fmt "子块是第一个子块。这是对实用性的让步:在数据流的早期就知道 WAVE 包含什么格式的音频很方便——例如,这使得从网络流中播放波形文件变得更加容易。如果 WAVE 文件使用压缩格式,例如 μ-law,通常假设"fact"子块将直接出现在"fmt ".

在指定格式的块不碍事后,应该放弃关于子块的位置、顺序和命名的假设。此时,软件应仅按名称(例如"data")定位预期的子块。如果遇到名称无法识别的子块(例如"FLLR"),则应简单地跳过并忽略这些子块。跳过一个子块需要读取它的长度,以便您可以跳过正确的字节数。

Apple 对 subchunk 所做的事情"FLLR"有点不寻常,我对某些软件被它绊倒并不感到惊讶。我怀疑您正在使用的库根本没有准备好处理"FLLR"子块的存在。我认为这是图书馆的一个缺陷。图书馆作者所犯的错误可能是这样的:

  1. 他们可能期望"data"子块出现在文件开头的前 N ​​个字节内,其中 N 小于 ~4kB。如果他们必须扫描太远的文件,他们可能会放弃查找。Apple子块将子"FLLR"块推"data"送到文件中 >~4kB 的位置。

  2. 他们可能期望"data"子块在 RIFF 中具有特定的序号子块位置或字节偏移量。或许他们期待"data"之后马上出现"fmt "。但是,这是处理 RIFF 文件的错误方法。"data"不应假定子块的序数位置和/或偏移位置。

只要我们在谈论正确的 WAVE 文件处理,我不妨提醒大家,音频字节(data子块的有效负载)可能不会完全运行到文件末尾。可以在有效载荷之后插入子块。data一些程序使用它在文件末尾存储文本“评论”字段。如果您从data有效负载的开头一直盲目地阅读到 EOF,您可能会将一些元数据子块作为音频拉入,这在播放结束时听起来像是“点击”。您需要遵守data子块的长度字段,并在消耗完整个数据有效负载后停止读取音频——当您点击 EOF 时不要停止。

于 2011-07-08T00:52:34.877 回答
1

您在磁盘上录制的文件的名称是什么?我有一个类似的问题,只是通过.wav添加到我的文件名的末尾来解决它......我想AVAudioRecorder需要一个扩展来解决问题。

于 2011-06-17T20:31:18.770 回答