在 Windows 上播放任意声音?
Posted
技术标签:
【中文标题】在 Windows 上播放任意声音?【英文标题】:Playing an arbitrary sound on Windows? 【发布时间】:2011-04-28 07:01:51 【问题描述】:例如,我如何在 Windows(32 位和 64 位,直到 Windows 7)本机上播放具有给定幅度和给定频率构成(例如,由频率 2 kHz 和 3 kHz 组成)的声音?
(本机我的意思是不使用外部库。)
我认为这需要waveOutWrite 方法,但我不知道它是如何工作的。
【问题讨论】:
【参考方案1】:我得到了一些工作......
#define _USE_MATH_DEFINES 1
#include <math.h>
#include <stdio.h>
#include <tchar.h>
#include <windows.h>
#include <mmreg.h>
#include <complex>
#pragma comment(lib, "Winmm.lib")
MMRESULT play(float nSeconds,
float signal(float timeInSeconds, unsigned short channel, void *context),
void *context = NULL, unsigned long samplesPerSecond = 48000)
UINT timePeriod = 1;
MMRESULT mmresult = MMSYSERR_NOERROR;
WAVEFORMATEX waveFormat = 0;
waveFormat.cbSize = 0;
waveFormat.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
waveFormat.nChannels = 2;
waveFormat.nSamplesPerSec = samplesPerSecond;
const size_t nBuffer =
(size_t)(nSeconds * waveFormat.nChannels * waveFormat.nSamplesPerSec);
float *buffer;
waveFormat.wBitsPerSample = CHAR_BIT * sizeof(buffer[0]);
waveFormat.nBlockAlign =
waveFormat.nChannels * waveFormat.wBitsPerSample / CHAR_BIT;
waveFormat.nAvgBytesPerSec =
waveFormat.nSamplesPerSec * waveFormat.nBlockAlign;
buffer = (float *)calloc(nBuffer, sizeof(*buffer));
__try
for (size_t i = 0; i < nBuffer; i += waveFormat.nChannels)
for (unsigned short j = 0; j < waveFormat.nChannels; j++)
buffer[i+j] = signal((i+j) * nSeconds / nBuffer, j, context);
HWAVEOUT hWavOut = NULL;
mmresult = waveOutOpen(&hWavOut, WAVE_MAPPER,
&waveFormat, NULL, NULL, CALLBACK_NULL);
if (mmresult == MMSYSERR_NOERROR)
__try
timeBeginPeriod(timePeriod);
__try
WAVEHDR hdr = 0;
hdr.dwBufferLength =
(ULONG)(nBuffer * sizeof(buffer[0]));
hdr.lpData = (LPSTR)&buffer[0];
mmresult = waveOutPrepareHeader(hWavOut,
&hdr, sizeof(hdr));
if (mmresult == MMSYSERR_NOERROR)
__try
ULONG start = GetTickCount();
mmresult =
waveOutWrite(hWavOut, &hdr, sizeof(hdr));
Sleep((ULONG)(1000 * nSeconds
- (GetTickCount() - start)));
__finally
waveOutUnprepareHeader(hWavOut, &hdr, sizeof(hdr));
__finally timeEndPeriod(timePeriod);
__finally waveOutClose(hWavOut);
__finally free(buffer);
return mmresult;
// Triangle wave generator
float triangle(float timeInSeconds, unsigned short channel, void *context)
const float frequency = *(const float *)context;
const float angle = (float)(frequency * 2 * M_PI * timeInSeconds);
switch (channel)
case 0: return (float)asin(sin(angle + 0 * M_PI / 2));
default: return (float)asin(sin(angle + 1 * M_PI / 2));
// Pure tone generator
float pure(float timeInSeconds, unsigned short channel, void *context)
const float frequency = *(const float *)context;
const float angle = (float)(frequency * 2 * M_PI * timeInSeconds);
switch (channel)
case 0: return (float)sin(angle + 0 * M_PI / 2);
default: return (float)sin(angle + 1 * M_PI / 2);
int _tmain(int argc, _TCHAR* argv[])
float frequency = 2 * 261.626F;
play(1, pure, &frequency);
return 0;
【讨论】:
+1。但我认为三角波发生器是错误的。根据Wikipedia:return (float)asin(sin(angle + 0 * M_PI / 2)) * 2 / M_PI;
.【参考方案2】:
Beep
BOOL WINAPI Beep(
__in DWORD dwFreq,
__in DWORD dwDuration
);
【讨论】:
【参考方案3】:waveOut
函数处理声音波形数据(如果我没记错的话,是 WAV 格式)。
虽然这是针对 WPF 应用程序,但以下链接应该对任何桌面应用程序都有帮助:
Sound Generation in WPF Applications
【讨论】:
【参考方案4】:通过 PC 扬声器或使用 Directx Sound 发出哔声。 如果您需要,我可以提供一些 sn-ps。
【讨论】:
Beep 功能不起作用,因为它无法生成频率组合。 DirectX 的声音似乎有点矫枉过正,因为我只想生成一个简单的音调……有没有办法用waveOutWrite
之类的东西来做到这一点?我认为这是正确的方法,但我不知道该提供什么数据。
@Mehrdad:wavOutWrite 需要 WAV 格式的声音块,iirc。
@Adam:啊,现在那是一个很好的答案。 =)请随意张贴,以便我接受! (顺便说一句,它是 完整 波形数据还是只是其中的一部分?我是否也需要发送所有 RIFF 标头?)以上是关于在 Windows 上播放任意声音?的主要内容,如果未能解决你的问题,请参考以下文章
如何在 Windows 上使用 C++ 在特定音频设备上播放声音?
在 Windows 移动设备上播放声音文件(特别是 .wav)