在 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)

如何检测Windows XP机器上是不是播放任何声音

windows声音设置,一个音源,多个设备

我的 javascript 声音不会在 windows XP 中播放?

在 pygame Windows 中播放声音的问题