1

我正在将音频文件以WAVE 格式实时写入 SD/MMC 存储卡,在ARM 板上工作。所述卡是(并且必须保持)FAT32 格式。只要我事先知道要写多少,我就可以写一个有效的 WAVE 文件。

我希望能够将占位符数据放在 RIFF 和数据块的块数据大小字段中,写入我的音频数据,然后返回并更新这两个块中的块数据大小字段,以便它们具有正确的值,但是...

我有一个工作文件系统和一些 stdio 函数,但有一些警告:

  • fwrite() 支持rwa,但不支持任何+模式。
  • fseek() 在写入模式下不起作用。

我没有编写上述函数的实现(我使用的是 ARM 的 RL-FLashFS),我不确定限制/部分实现的理由是什么。个人添加缺少的功能可能是一种选择,但如果可能的话,我想避免它(我对这些功能没有其他需要,不要预见任何功能,也不能在上面花费太多时间。)切换到不同的实现也不是这里的选择。

我的可用内存非常有限,我不知道会收到多少音频数据,但几乎可以肯定它会超过我在任何时候都可以保存在内存中的数据。

可以写一个包含原始交错音频数据的文件,同时跟踪我写了多少字节,关闭它,然后再次打开它进行读取,打开第二个文件进行写入,将标题写入第二个文件,然后复制音频数据结束。也就是说,我可以将其后处理为格式正确的有效 WAVE 文件。我已经这样做了,而且效果很好。我想尽可能避免对大量数据进行后处理。

也许我可以以某种方式连接两个文件?(即写入数据,然后将块写入一个单独的文件,然后将它们加入文件系统,避免花费大量时间复制潜在的大量数据。)我对此的理解是,如果可能的话,它仍然会涉及由于存储的块方向,一些复制。

建议?

编辑:我真的应该提到这一点,但这里没有运行操作系统。我在硬件抽象层之上运行了一些 stdio 函数,仅此而已。

4

2 回答 2

3

这应该是可能的,但它涉及编写一组 FAT 表操作例程。

FAT的概念很简单:文件存储在“簇”链中——固定大小的块。集群在磁盘上不必是连续的。文件的目录条目包括第一个集群的 ID。FAT 包含每个簇的一个值,它可以是链中下一个簇的 ID,也可以是“链结束”(EOC) 标记。

因此,您可以通过更改第一个文件的 EOC 标记以指向第二个文件的头簇来将文件连接在一起。

对于您的应用程序,您可以写入所有数据,将第一个簇(使用正确的标题)重写到一个新文件中,然后进行 FAT 手术将新的头部移植到旧的尾部:

  • 确定 FAT 簇大小 ( S)
  • 确定 WAV 标头的大小,直到第一个数据字节 ( F)
  • 将传入数据写入临时文件。流结束时关闭。
  • 创建一个具有所需名称的新文件。
  • 打开临时文件进行读取,并将标题复制到新文件中,同时正确填写大小字段(如您之前所做的那样)。
  • 写入min(S-F, bytes_remaining)新文件。
  • 关闭新文件。
  • 如果没有剩余字节,你就完成了,
  • 别的,
    • 将 FAT 和目录读入内存。
    • 阅读目录以获取
      • 临时文件 (T1) 的第一个集群(包含所有数据),
      • wav 文件的第一个簇 (W1)。(使用正确的标题)
    • 读取 T1 的 FAT 条目以找到第二个临时群集 (T2)。
    • 将 W1 的 FAT 条目从“EOC”更改为 T2。
    • 将 T1 的 FAT 条目从 T2 更改为“EOC”。
    • 交换目录中两个文件的 FileSize 条目。
    • 将 FAT 和目录写回磁盘。
    • 删除临时文件。

当然,当你这样做的时候,你可能会很好地理解文件系统来实现fseek(fp,0,SEEK_SET),这应该会给你足够的功能来通过标准库调用来修复头文件。

于 2011-11-30T06:45:39.063 回答
1

在我们的项目 - 记录器应用程序中,我们正在使用与您完全相同的场景。由于文件的长度是未知的——我们在开头写一个长度为 0 的 RIFF 头(以保留空间)并在关闭时——回到 0 位置(使用 fseek)并写入正确的头。因此,我认为您必须调试为什么 fseek 在写入模式下不起作用,否则您将无法有效地执行此任务。

顺便说一句,您最好不要使用文件系统内部特定的解决方法,例如连接块等 - 这几乎是不可能的,不会是可移植的,并且会给您带来新的问题。让我们改用标准且经过验证的方法。

更新

(在发现你的 FS 是 ARM 的 RL-FlashFS 之后)为什么不使用rewind http://www.keil.com/support/man/docs/rlarm/rlarm_rewind.htm而不是 fseek?

于 2011-11-30T05:18:46.350 回答