将waveOutWrite与动态分配的内存结合使用

时间:2019-02-10 01:27:22

标签: c audio dynamic-memory-allocation static-memory-allocation

我一直在尝试使用C进行声音

我找到了一段代码,可以让我生成PCM波声音并通过声卡输出。

按原样运行效果很好。

以下是有效的代码:(您需要链接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()为wave缓冲区分配内存。 以下代码摘自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.

1 个答案:

答案 0 :(得分:1)

问题出在这里

WAVEHDR header = { buffer, sizeof(buffer), 0, 0, 0, 0, 0, 0 };

在这里,sizeof(buffer)是指向缓冲区的指针的大小,因为缓冲区是char *。在您的第一个示例中,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位样本的无符号值。 (对于其他尺寸,您需要签名的样品。)