Audio 使用WaveOUT API生成无失真的音调声音所需的最小音频缓冲区是什么

Audio 使用WaveOUT API生成无失真的音调声音所需的最小音频缓冲区是什么,audio,signals,buffer,waveout,waveoutwrite,Audio,Signals,Buffer,Waveout,Waveoutwrite,WaveOutAPI是否对当前播放的缓冲区的大小有一些内部限制?我的意思是,如果我提供一个非常小的缓冲区,它会影响扬声器播放的声音。当我用小缓冲器产生和播放窦波时,我感到非常奇怪的噪音。有点像山峰,或“隆起” 完整的故事: 我做了一个程序,可以实时产生窦音信号。 可变参数为频率和体积。项目要求的最大延迟为50毫秒。因此,该程序必须能够实时产生音频信号频率手动可调的正弦信号 我使用Windows WaveOut API、C#和p/invoke来访问API 当声音缓冲区大1000毫秒时,一切正常。如

WaveOutAPI是否对当前播放的缓冲区的大小有一些内部限制?我的意思是,如果我提供一个非常小的缓冲区,它会影响扬声器播放的声音。当我用小缓冲器产生和播放窦波时,我感到非常奇怪的噪音。有点像山峰,或“隆起”

完整的故事: 我做了一个程序,可以实时产生窦音信号。 可变参数为频率和体积。项目要求的最大延迟为50毫秒。因此,该程序必须能够实时产生音频信号频率手动可调的正弦信号

我使用Windows WaveOut API、C#和p/invoke来访问API

当声音缓冲区大1000毫秒时,一切正常。如果我根据延迟要求将缓冲区最小化为50毫秒,那么对于我在每个缓冲区末尾遇到的某些频率,会出现噪音或“颠簸”。我不知道产生的声音是否格式错误(我检查过,没有),或者音频芯片出现了问题,或者初始化和播放时出现了延迟

当我把产生的音频保存到.wav文件时,一切都是完美的

这意味着我的代码中一定有bug,或者音频子系统对发送到它的缓冲区块有限制

对于那些不知道WaveOut的人,必须在第一时间初始化,然后必须为每个缓冲区准备好音频标题,这些缓冲区包含需要播放的字节数和指向包含需要播放的音频的内存的指针

更新

噪声发生在以下组合中:44100采样、16位、2个通道、50毫秒缓冲器和生成的201Hz、202Hz、203Hz、204Hz、205Hz的正弦音频信号。。。219Hz, 220Hz,240Hz,正常


我不知道为什么会有20的差异。

当您需要平稳地输出音频时,需要记住以下几点:

  • waveOutXxxx
    API是较低级别API之上的一个遗留/兼容层,因此具有更大的开销,当您要达到最小延迟时,不建议使用它。请注意,这不太可能是您的主要问题,但这是一项有助于理解的常识
  • 由于Windows不是实时操作系统,其音频子系统也不是实时的,或者您无法控制排队等待输出的音频数据与实际播放数据之间的随机延迟,关键是要保持一定程度的缓冲区满度,以防止播放下溢,并提供平滑播放
  • 使用
    waveOutXxxx
    您不仅可以拥有单个缓冲区,还可以分配多个可重用的缓冲区并回收它们
总之,waveOutXxxx、DirectSound、DirectShow API在50毫秒及以上的延迟下工作良好。使用WASAPI独占模式流,您可以

编辑:我似乎说的关于20毫秒的延迟时间太早了。为了弥补这一点,这里有一个简单的工具(,)来估计您可以实现的延迟。有了足够的缓冲,播放是流畅的,否则你会听到口吃

我的理解是,缓冲区可能会延迟返回,而最小延迟方面的最佳设计在于拥有更多更小的缓冲区,以便尽早返回。例如,10个缓冲区为3 ms/缓冲区,而不是3个缓冲区为10 ms/缓冲区

D:\>LowLatencyWaveOutPlay.exe 48000 10 3
Format: 48000 Hz, 1 channels, 16 bits per sample
Buffer Count: 10
Buffer Length: 3 ms (288 bytes)
Signal Frequency: 1000 Hz
^C

所以我来这里是因为我也想找到waveoutwrite()的基本延迟。我得到了大约25-26毫秒的延迟,然后才得到平滑的正弦音调

这是为了:

AMD Phenom(tm)9850四核处理器2.51 GHz 4.00GB内存 64位操作系统,基于x64的处理器 Windows10EnterpriseN

代码如下。它是Petzold正弦波程序的修改版本,经过重构,可以在命令行上运行。我还将对缓冲区的轮询改为对缓冲区使用回调,以使程序更加高效,但这并没有什么不同

它还有一个运行时间设置,我用它来探测缓冲区上操作的各种时间。使用我得到的:

Sine wave output program
Channels:         2
Sample rate:      44100
Bytes per second: 176400
Block align:      4
Bits per sample:  16
Time per buffer:  0.025850
Total time prepare header:   87.5000000000 usec
Total time to fill:          327.9000000000 usec
Total time for waveOutWrite: 90.8000000000 usec
节目:

/*******************************************************************************

WaveOut example program

Based on C. Petzold's sine wave example, outputs a sine wave via the waveOut
API in Win32.

*******************************************************************************/

#include <stdio.h>
#include <windows.h>
#include <math.h>
#include <limits.h>
#include <unistd.h>

#define SAMPLE_RATE      44100
#define FREQ_INIT        440
#define OUT_BUFFER_SIZE  570*4
#define PI               3.14159
#define CHANNELS         2
#define BITS             16
#define MAXTIM           1000000000

double        fAngle;
LARGE_INTEGER perffreq;
PWAVEHDR      pWaveHdr1, pWaveHdr2;
int           iFreq = FREQ_INIT;

VOID FillBuffer (short* pBuffer, int iFreq)

{

     int i;
     int c;

     for (i = 0 ; i < OUT_BUFFER_SIZE ; i += CHANNELS) {

          for (c = 0; c < CHANNELS; c++)
            pBuffer[i+c] = (short)(SHRT_MAX*sin (fAngle));
          fAngle += 2*PI*iFreq/SAMPLE_RATE;
          if (fAngle > 2 * PI) fAngle -= 2*PI;

     }

}

double elapsed(LARGE_INTEGER t)

{

    LARGE_INTEGER rt;
    long tt;

    QueryPerformanceCounter(&rt);
    tt = rt.QuadPart-t.QuadPart;

    return (tt*(1.0/(double)perffreq.QuadPart));

}

void CALLBACK waveOutProc(HWAVEOUT hwo, UINT uMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2)

{

    if (uMsg == WOM_DONE) {

        if (pWaveHdr1->dwFlags & WHDR_DONE) {

            FillBuffer((short*)pWaveHdr1->lpData, iFreq);
            waveOutWrite(hwo, pWaveHdr1, sizeof(WAVEHDR));

        }
        if (pWaveHdr2->dwFlags & WHDR_DONE) {

            FillBuffer((short*)pWaveHdr2->lpData, iFreq);
            waveOutWrite(hwo, pWaveHdr2, sizeof(WAVEHDR));

        }

    }

}

int main()

{

    HWAVEOUT     hWaveOut ;

    short*       pBuffer1;
    short*       pBuffer2;
    short*       pBuffer3;

    WAVEFORMATEX waveformat;
    UINT         wReturn;
    int          bytes;
    long         t;
    LARGE_INTEGER rt;
    double       timprep;
    double       filtim;
    double       waveouttim;

    printf("Sine wave output program\n");

    fAngle = 0; /* start sine angle */

    QueryPerformanceFrequency(&perffreq);

    pWaveHdr1 = malloc (sizeof (WAVEHDR));
    pWaveHdr2 = malloc (sizeof (WAVEHDR));
    pBuffer1  = malloc (OUT_BUFFER_SIZE*sizeof(short));
    pBuffer2  = malloc (OUT_BUFFER_SIZE*sizeof(short));
    pBuffer3  = malloc (OUT_BUFFER_SIZE*sizeof(short));

    if (!pWaveHdr1 || !pWaveHdr2 || !pBuffer1 || !pBuffer2) {

        if (!pWaveHdr1) free (pWaveHdr1) ;
        if (!pWaveHdr2) free (pWaveHdr2) ;
        if (!pBuffer1)  free (pBuffer1) ;
        if (!pBuffer2)  free (pBuffer2) ;

        fprintf(stderr, "*** Error: No memory\n");
        exit(1);

    }

    // Load prime parameters to format
    waveformat.wFormatTag      = WAVE_FORMAT_PCM;
    waveformat.nChannels       = CHANNELS;
    waveformat.nSamplesPerSec  = SAMPLE_RATE;
    waveformat.wBitsPerSample  = BITS;
    waveformat.cbSize          = 0;

    // Calculate other parameters
    bytes = waveformat.wBitsPerSample/8; /* find bytes per sample */
    if (waveformat.wBitsPerSample&8) bytes++; /* round  up */
    bytes *= waveformat.nChannels; /* find total channels size */
    waveformat.nBlockAlign = bytes; /* set block align */
    /* find average bytes/sec */
    waveformat.nAvgBytesPerSec = bytes*waveformat.nSamplesPerSec;

    printf("Channels:         %d\n", waveformat.nChannels);
    printf("Sample rate:      %d\n", waveformat.nSamplesPerSec);
    printf("Bytes per second: %d\n", waveformat.nAvgBytesPerSec);
    printf("Block align:      %d\n", waveformat.nBlockAlign);
    printf("Bits per sample:  %d\n", waveformat.wBitsPerSample);
    printf("Time per buffer:  %f\n",
        OUT_BUFFER_SIZE*sizeof(short)/(double)waveformat.nAvgBytesPerSec);

    if (waveOutOpen (&hWaveOut, WAVE_MAPPER, &waveformat, (DWORD_PTR)waveOutProc, 0, CALLBACK_FUNCTION)
        != MMSYSERR_NOERROR) {

        free (pWaveHdr1) ;
        free (pWaveHdr2) ;
        free (pBuffer1) ;
        free (pBuffer2) ;

        hWaveOut = NULL ;
        fprintf(stderr, "*** Error: No memory\n");
        exit(1);

    }

    // Set up headers and prepare them

    pWaveHdr1->lpData          = (LPSTR)pBuffer1;
    pWaveHdr1->dwBufferLength  = OUT_BUFFER_SIZE*sizeof(short);
    pWaveHdr1->dwBytesRecorded = 0;
    pWaveHdr1->dwUser          = 0;
    pWaveHdr1->dwFlags         = WHDR_DONE;
    pWaveHdr1->dwLoops         = 1;
    pWaveHdr1->lpNext          = NULL;
    pWaveHdr1->reserved        = 0;

QueryPerformanceCounter(&rt);
    waveOutPrepareHeader(hWaveOut, pWaveHdr1, sizeof (WAVEHDR));
timprep = elapsed(rt);

    pWaveHdr2->lpData          = (LPSTR)pBuffer2;
    pWaveHdr2->dwBufferLength  = OUT_BUFFER_SIZE*sizeof(short);
    pWaveHdr2->dwBytesRecorded = 0;
    pWaveHdr2->dwUser          = 0;
    pWaveHdr2->dwFlags         = WHDR_DONE;
    pWaveHdr2->dwLoops         = 1;
    pWaveHdr2->lpNext          = NULL;
    pWaveHdr2->reserved        = 0;

    waveOutPrepareHeader(hWaveOut, pWaveHdr2, sizeof (WAVEHDR));

    // Send two buffers to waveform output device

QueryPerformanceCounter(&rt);
    FillBuffer (pBuffer1, iFreq);
filtim = elapsed(rt);

QueryPerformanceCounter(&rt);
    waveOutWrite (hWaveOut, pWaveHdr1, sizeof (WAVEHDR));
waveouttim = elapsed(rt);

    FillBuffer (pBuffer2, iFreq);
    waveOutWrite (hWaveOut, pWaveHdr2, sizeof (WAVEHDR));

    // Run waveform loop
    sleep(10);

printf("Total time prepare header:   %.10f usec\n", timprep*1000000);
printf("Total time to fill:          %.10f usec\n", filtim*1000000);
printf("Total time for waveOutWrite: %.10f usec\n", waveouttim*1000000);

    waveOutUnprepareHeader(hWaveOut, pWaveHdr1, sizeof (WAVEHDR));
    waveOutUnprepareHeader(hWaveOut, pWaveHdr2, sizeof (WAVEHDR));
    // Close waveform file
    free (pWaveHdr1) ;
    free (pWaveHdr2) ;
    free (pBuffer1) ;
    free (pBuffer2) ;

}
/*******************************************************************************
WaveOut示例程序
基于C.Petzold的正弦波示例,通过waveOut输出正弦波
Win32中的API。
*******************************************************************************/
#包括
#包括
#包括
#包括
#包括
#定义采样率44100
#定义频率初始化440
#定义输出缓冲区大小570*4
#定义PI 3.14159
#定义通道2
#定义位16
#定义MAXTIM 100000000
双尖牙;
大整数性能频率;
PWAVEHDR pWaveHdr1,pWaveHdr2;
int iFreq=FREQ_INIT;
无效填充缓冲区(短*pBuffer,int-iFreq)
{
int i;
INTC;
对于(i=0;i2*PI)方格-=2*PI;
}
}
双时间(大整数t)
{
大整数rt;
长tt;
查询性能计数器(&rt);
tt=rt.QuadPart-t.QuadPart;
返回(tt*(1.0/(双)性能频率四部分);
}
无效回调waveOutProc(HWAVEOUT hwo、UINT uMsg、DWORD_PTR DWARAMENT、DWORD_PTR DWARAM1、DWORD_PTR DWARAM2)
{
如果(uMsg==WOM_DONE){
如果(pWaveHdr1->dwFlags和WHDR_DONE){
FillBuffer((短*)pWaveHdr1->lpData,iFreq);
waveOutWrite(hwo、pWaveHdr1、sizeof(WAVEHDR));
}
如果(pWaveHdr2->dwFlags和WHDR_DONE){
FillBuffer((短*)pWaveHdr2->lpData,iFreq);
waveOutWrite(hwo、pWaveHdr2、sizeof(WAVEHDR));