将 waveOutWrite 与动态分配的内存一起使用
Using waveOutWrite with Dynamically Allocated Memory
我一直在用 C 来试验声音。
我找到了一段代码,可以让我生成PCM波形声音并通过声卡输出。
它的效果很好。
这是有效的代码:(您需要 link winmm 才能正常工作)
(警告:声音很大。)
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <mmsystem.h>
//#pragma comment(lib, "winmm.lib")
int main()
{
HWAVEOUT hWaveOut=0;
WAVEFORMATEX wfx = {WAVE_FORMAT_PCM,1,8000,8000,1,8,0};
//buffer to hold 1 minute of pcm wave data (8 bit, 8khz sample rate)
char buffer[8000*60];
int i;
for (i=0;i<8000*60;i++){
//generate pseudo-random sound.
buffer[i] = (i*5&(i>>7)|i*3&(i*4>>10));
}
waveOutOpen(&hWaveOut, WAVE_MAPPER, &wfx, 0, 0, CALLBACK_NULL);
WAVEHDR header = { buffer, sizeof(buffer), 0, 0, 0, 0, 0, 0 };
waveOutPrepareHeader(hWaveOut, &header, sizeof(WAVEHDR));
waveOutWrite(hWaveOut, &header, sizeof(WAVEHDR));
Sleep(60*1000);
waveOutUnprepareHeader(hWaveOut, &header, sizeof(WAVEHDR));
waveOutClose(hWaveOut);
return 0;
}
但是,我想使用类似的方法来解析和播放 .wav 文件。
我已经意识到,虽然上面的代码有效(缓冲区是静态分配的),但是,当我动态分配内存时(例如使用 malloc),没有播放声音输出。我听到很沙哑的嗡嗡声,说明waveOutWrite()
功能已经执行,但是没有其他声音在播放。
下面是不起作用的示例:
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <mmsystem.h>
//#pragma comment(lib, "winmm.lib")
int main()
{
HWAVEOUT hWaveOut=0;
WAVEFORMATEX wfx = {WAVE_FORMAT_PCM,1,8000,8000,1,8,0};
//buffer to hold 1 minute of pcm wave data (8 bit, 8khz sample rate)
char *buffer = malloc(sizeof(char)*8000*60);
int i;
for (i=0;i<8000*60;i++){
//generate pseudo-random sound.
buffer[i] = (i*5&(i>>7)|i*3&(i*4>>10));
}
waveOutOpen(&hWaveOut, WAVE_MAPPER, &wfx, 0, 0, CALLBACK_NULL);
WAVEHDR header = { buffer, sizeof(buffer), 0, 0, 0, 0, 0, 0 };
waveOutPrepareHeader(hWaveOut, &header, sizeof(WAVEHDR));
waveOutWrite(hWaveOut, &header, sizeof(WAVEHDR));
Sleep(60*1000);
waveOutUnprepareHeader(hWaveOut, &header, sizeof(WAVEHDR));
waveOutClose(hWaveOut);
return 0;
}
注意:我听说有一种方法可以“锁定”部分内存,我可以用它来播放动态分配的音频缓冲区。如果可以,我该怎么做?
我还有一个小烦恼。我发现如果我在没有耳机的情况下启动程序,音频将通过我的计算机扬声器播放,这是预期的,但是当我插入耳机时,音频仍将继续通过计算机扬声器播放。如果默认播放设备发生变化,我将如何调整代码以将播放设备切换为默认值?
非常感谢您的宝贵时间!你们真棒。
更新:
我发现 Microsoft 文档使用函数 GlobalAlloc()
、HeapAlloc()
、LocalAlloc()
和 VirtualAlloc()
来为波形缓冲区分配内存。
下面的代码取自 this example:
char *buffer = LocalAlloc(LMEM_FIXED,8000*60); //Doesn't work. Still no sound.
这也摘自another example:
HANDLE hData = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, 8000*60);
char *buffer = GlobalLock(hData); //Also doesn't work, no sound.
问题在这里:
WAVEHDR header = { buffer, sizeof(buffer), 0, 0, 0, 0, 0, 0 };
这里,sizeof(buffer)
是buffer指针的大小,因为buffer是一个char *
。在您的第一个示例中,缓冲区是一个字符数组,因此 sizeof(buffer)
返回了缓冲区的大小。
您应该提供上面计算出的分配大小。我会使用几个常量或变量来跟踪大小,这样您就不会重复计算。例如:
//buffer to hold 1 minute of pcm wave data (8 bit, 8khz sample rate)
const size_t samples_per_second = 8000;
const size_t seconds = 60;
const size_t size = samples_per_second * seconds;
unsigned char *buffer = malloc(size);
size_t i;
for (i=0;i<size;i++){
//generate pseudo-random sound.
buffer[i] = (i*5&(i>>7)|i*3&(i*4>>10));
}
waveOutOpen(&hWaveOut, WAVE_MAPPER, &wfx, 0, 0, CALLBACK_NULL);
WAVEHDR header = { buffer, size, 0, 0, 0, 0, 0, 0 };
您可能希望使用 unsigned char
作为样本。我相信 WAV 格式需要 8 位样本的无符号值。 (对于其他尺寸,您需要签名样品。)
我一直在用 C 来试验声音。
我找到了一段代码,可以让我生成PCM波形声音并通过声卡输出。
它的效果很好。
这是有效的代码:(您需要 link winmm 才能正常工作)
(警告:声音很大。)
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <mmsystem.h>
//#pragma comment(lib, "winmm.lib")
int main()
{
HWAVEOUT hWaveOut=0;
WAVEFORMATEX wfx = {WAVE_FORMAT_PCM,1,8000,8000,1,8,0};
//buffer to hold 1 minute of pcm wave data (8 bit, 8khz sample rate)
char buffer[8000*60];
int i;
for (i=0;i<8000*60;i++){
//generate pseudo-random sound.
buffer[i] = (i*5&(i>>7)|i*3&(i*4>>10));
}
waveOutOpen(&hWaveOut, WAVE_MAPPER, &wfx, 0, 0, CALLBACK_NULL);
WAVEHDR header = { buffer, sizeof(buffer), 0, 0, 0, 0, 0, 0 };
waveOutPrepareHeader(hWaveOut, &header, sizeof(WAVEHDR));
waveOutWrite(hWaveOut, &header, sizeof(WAVEHDR));
Sleep(60*1000);
waveOutUnprepareHeader(hWaveOut, &header, sizeof(WAVEHDR));
waveOutClose(hWaveOut);
return 0;
}
但是,我想使用类似的方法来解析和播放 .wav 文件。
我已经意识到,虽然上面的代码有效(缓冲区是静态分配的),但是,当我动态分配内存时(例如使用 malloc),没有播放声音输出。我听到很沙哑的嗡嗡声,说明waveOutWrite()
功能已经执行,但是没有其他声音在播放。
下面是不起作用的示例:
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <mmsystem.h>
//#pragma comment(lib, "winmm.lib")
int main()
{
HWAVEOUT hWaveOut=0;
WAVEFORMATEX wfx = {WAVE_FORMAT_PCM,1,8000,8000,1,8,0};
//buffer to hold 1 minute of pcm wave data (8 bit, 8khz sample rate)
char *buffer = malloc(sizeof(char)*8000*60);
int i;
for (i=0;i<8000*60;i++){
//generate pseudo-random sound.
buffer[i] = (i*5&(i>>7)|i*3&(i*4>>10));
}
waveOutOpen(&hWaveOut, WAVE_MAPPER, &wfx, 0, 0, CALLBACK_NULL);
WAVEHDR header = { buffer, sizeof(buffer), 0, 0, 0, 0, 0, 0 };
waveOutPrepareHeader(hWaveOut, &header, sizeof(WAVEHDR));
waveOutWrite(hWaveOut, &header, sizeof(WAVEHDR));
Sleep(60*1000);
waveOutUnprepareHeader(hWaveOut, &header, sizeof(WAVEHDR));
waveOutClose(hWaveOut);
return 0;
}
注意:我听说有一种方法可以“锁定”部分内存,我可以用它来播放动态分配的音频缓冲区。如果可以,我该怎么做?
我还有一个小烦恼。我发现如果我在没有耳机的情况下启动程序,音频将通过我的计算机扬声器播放,这是预期的,但是当我插入耳机时,音频仍将继续通过计算机扬声器播放。如果默认播放设备发生变化,我将如何调整代码以将播放设备切换为默认值?
非常感谢您的宝贵时间!你们真棒。
更新:
我发现 Microsoft 文档使用函数 GlobalAlloc()
、HeapAlloc()
、LocalAlloc()
和 VirtualAlloc()
来为波形缓冲区分配内存。
下面的代码取自 this example:
char *buffer = LocalAlloc(LMEM_FIXED,8000*60); //Doesn't work. Still no sound.
这也摘自another example:
HANDLE hData = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, 8000*60);
char *buffer = GlobalLock(hData); //Also doesn't work, no sound.
问题在这里:
WAVEHDR header = { buffer, sizeof(buffer), 0, 0, 0, 0, 0, 0 };
这里,sizeof(buffer)
是buffer指针的大小,因为buffer是一个char *
。在您的第一个示例中,缓冲区是一个字符数组,因此 sizeof(buffer)
返回了缓冲区的大小。
您应该提供上面计算出的分配大小。我会使用几个常量或变量来跟踪大小,这样您就不会重复计算。例如:
//buffer to hold 1 minute of pcm wave data (8 bit, 8khz sample rate)
const size_t samples_per_second = 8000;
const size_t seconds = 60;
const size_t size = samples_per_second * seconds;
unsigned char *buffer = malloc(size);
size_t i;
for (i=0;i<size;i++){
//generate pseudo-random sound.
buffer[i] = (i*5&(i>>7)|i*3&(i*4>>10));
}
waveOutOpen(&hWaveOut, WAVE_MAPPER, &wfx, 0, 0, CALLBACK_NULL);
WAVEHDR header = { buffer, size, 0, 0, 0, 0, 0, 0 };
您可能希望使用 unsigned char
作为样本。我相信 WAV 格式需要 8 位样本的无符号值。 (对于其他尺寸,您需要签名样品。)