将 .wav 加载到内存中,然后在 C++ 中播放

Posted

技术标签:

【中文标题】将 .wav 加载到内存中,然后在 C++ 中播放【英文标题】:Loading a .wav into memory then playing it in C++ 【发布时间】:2021-08-24 05:39:42 【问题描述】:

我能找到的其他答案似乎都不适合我。我已将sound.wav 的内容放入一个类似这样的变量中,以便将声音数据嵌入到可执行文件中而不处理资源文件

sound.h:

std::string sound = R"(RIFFÆø^ WAVEfmt ...)";

由于这个声音文件大约 30 秒长,它是一个非常大的变量,由 12,624 行 垃圾组成(这可能是问题的根源)。

我将sound.h 包含在main.cpp 中,然后尝试使用窗口playsoundapi 播放声音:

int main() 
    std::cout << "Hello" << std::endl;
    PlaySoundA(sound.c_str(), nullptr, SND_MEMORY | SND_SYNC);

    return 0;

程序简单地编译,打印“Hello”,并以代码 0 退出,没有任何错误。感觉我现在已经尝试了所有方法,所以任何信息都有帮助。

【问题讨论】:

如果从包含代表相同数据的字节的文件中加载时相同的代码可以工作,那么您确实对sound 的定义有问题。 您是如何将原始二进制数据转换为文本的?请记住,并非所有二进制字节都可以表示为字符。 你最好使用一个资源文件来为你处理嵌入 调用 PlaySound 时不检查错误 【参考方案1】:

您正在调用的 std::string 构造函数从源序列到第一个 NUL 字符构造一个 std::string 对象。

WAVE 文件使用Resource Interchange File Format。在RIFF 块标识符之后,有一个 4 字节序列指定长度。对于小于 ~16.4MiB 的文件,第四个字节为零,因此您最终会得到长度为 7 的 std::string。即使对于较大的文件,最终也会有值为 0 的字节,导致 std::string 存储一个输入的截断版本。

您有多种选择来处理这个问题(从最不推荐到最推荐):

使用带有显式count 参数的std::string constructor。这样可以避免像通常发生的那样截断输入。 使用数组,例如unsigned char const sound[] = R"(RIFF ...)";。由于 C++ 没有提供描述二进制文字的方法,因此建议使用数值初始化每个元素,例如unsigned char const sound[] = 0x52, 0x49, 0x46, 0x46, ... ;。 将音频文件链接到可执行映像的资源中。这是将二进制资产放入可执行映像的推荐方法。 Playing WAVE Resources 有用于检索数据的示例代码。

请注意,PlaySound 有一个返回值,您的代码会忽略该值。调用完全有可能以您调用它的方式返回FALSE。在诊断问题时,观察错误应该是您的首要行动。

【讨论】:

我刚刚在处理资源文件时遇到了问题,但这些信息非常有用,谢谢!

以上是关于将 .wav 加载到内存中,然后在 C++ 中播放的主要内容,如果未能解决你的问题,请参考以下文章

加载和保存 WAV 文件

如何正确播放已解码的内存PCM与Oboe?

如何从内存中播放 Wav 声音样本

用于播放/录制音频(.wav、.ogg)的 C++ 多平台库

C++ 中的 Playsound:.wav 文件的文件错误

在 C++ 中连接 .wav 文件