在纯 WinAPI 中从内存缓冲区播放声音

Posted

技术标签:

【中文标题】在纯 WinAPI 中从内存缓冲区播放声音【英文标题】:Playing sound from a memory buffer in pure WinAPI 【发布时间】:2021-01-22 19:19:18 【问题描述】:

我尝试了几种使用 MediaFoundation 或简单的 PlaySound 方法播放声音的方法。这两个似乎都需要一个 .WAV 文件。我无法从 WAVE 文件中播放声音,因为我是从 Internet 获取数据并在播放后立即将其丢弃。我需要它高效快速,直接从内存缓冲区完成。 MediaFoundations 播放音频的代码很长,需要几个步骤。虽然 PlaySound 很简单,但它不会从内存缓冲区播放我的声音。我也尝试过 SDL_mixer,但我似乎无法直接设置声音缓冲区,而不是依赖 SDL_LoadWav。这是我的 PlaySound 代码,取自 Load wavs into memory then play sounds asynchronously using Win32 API:

unsigned char* sounddata;
hr = pSoundBuffer->Lock(&sounddata, NULL, &soundlength);
PlaySound((LPCWSTR) sounddata, GetModuleHandle(NULL), SND_MEMORY);

这是我的 SDL_mixer 代码:

SDL_Init(SDL_INIT_AUDIO);
Mix_OpenAudio(22050, AUDIO_S16, 2, NULL);
Mix_Chunk* sound = new Mix_Chunk();
sound->allocated = 1;
sound->abuf = sounddata;
sound->alen = soundlength;
sound->volume = 128;
Mix_PlayChannel(-1, sound, 0);

使用 SDL_mixer 我只会得到随机噪音。所以我的问题是如何使用 SDL_mixer 或 PlaySound 函数简单地从内存缓冲区播放声音。还有其他选择吗(除了 MediaFoundation)?

【问题讨论】:

您是否验证了声音数据的格式正确(16 位立体声,而不是 8 位)并且 16 位数据的声音字节顺序正确(可能低字节在前)? @1201ProgramAlarm 我会检查的。此外,数据是否需要封装到带有标题和所有内容的 WAVE 文件中,或者 SDL_mixer 是否直接获取原始数据。 即使我将 AUDIO_S8 设置为 1 或 2 个通道,它也不能更好地工作。 【参考方案1】:

根据this page(强调我的):

SND_MEMORY 标志表明lpszSoundName 参数是一个指向WAVE 文件的内存映像的指针。

换句话说,您需要在原始数据前添加 WAV 文件头(否则,PlaySound 怎么知道数据的长度以及采样率等?)。

WAV 文件头的格式被广泛记录,例如here.

【讨论】:

谢谢,但我想知道 SDL_mixer(如果它需要 WAV 标头)。此外,将字节数组转换为 LPCWSTR 是正确的方法吗(对于 PlaySound 函数)? 恐怕我对 SDL 一无所知,所以我无法帮助您,但是对于 PlaySound,您需要在原始音频前面添加一个合适的 WAV 标头并传递一个指向那。做起来很容易,为什么不试试呢?

以上是关于在纯 WinAPI 中从内存缓冲区播放声音的主要内容,如果未能解决你的问题,请参考以下文章

同时播放两种声音的最简单方法(c++ winapi)

通过WinAPI播放PCM声音

为啥我使用 WinAPI C++ 录制的声音无法正常播放?

在 Swift 3 中从数组中播放随机声音

在delphi中从数组数据中播放声音的最简单方法是啥

Windows 的声音管理器类