waveInProc / Windows音频问题

时间:2011-01-08 05:20:47

标签: c++ windows winapi audio

我正在使用Windows API获取音频输入。我已经按照MSDN上的所有步骤进行操作,并设法将音频录制到WAV文件中。没问题。我正在使用多个缓冲区。我想用缓冲区做更多事情而不是简单地写入文件,所以现在我已经设置了一个回调函数。它工作得很好,而且我正在获取数据,但是一旦我拥有它,我不知道该怎么做。

这是我的回调......这里的一切都有效:

// Media API callback
void CALLBACK AudioRecorder::waveInProc(HWAVEIN hWaveIn, UINT uMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2)
{

    // Data received
    if (uMsg == WIM_DATA)
    {

        // Get wav header
        LPWAVEHDR mBuffer = (WAVEHDR *)dwParam1;

        // Now what?
        for (unsigned i = 0; i != mBuffer->dwBytesRecorded; ++i)
        {

            // I can see the char, how do get them into my file and audio buffers?
            cout << mBuffer->lpData[i] << "\n";

        }

        // Re-use buffer
        mResultHnd = waveInAddBuffer(hWaveIn, mBuffer, sizeof(mInputBuffer[0])); // mInputBuffer is a const WAVEHDR *

    }

}

// waveInOpen cannot use an instance method as its callback, 
// so we create a static method which calls the instance version
void CALLBACK AudioRecorder::staticWaveInProc(HWAVEIN hWaveIn, UINT uMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2)
{

    // Call instance version of method
    reinterpret_cast<AudioRecorder *>(dwParam1)->waveInProc(hWaveIn, uMsg, dwInstance, dwParam1, dwParam2);

}

就像我说的那样,效果很好,但我正在努力做到以下几点:

  • 将数据转换为short并复制到数组
  • 将数据转换为float并复制到数组
  • 将数据复制到一个更大的char数组,我将写入WAV
  • 将数据中继到任意输出设备

我和FMOD一起工作了很多,而且我熟悉交错和所有这些。但FMOD把所有东西都当作花车。在这种情况下,我会走另一条路。我想我基本上只是在寻找有关如何从LPSTR转到short,float和unsigned char的资源。

提前多多谢谢!

3 个答案:

答案 0 :(得分:2)

typedef struct { 
    LPSTR      lpData; 
    // etc..
} WAVEHDR; 

Hmya,那里有尴尬的类型。它当然不是STR,应该是PVOID。只需将其转换为您需要的任何类型:

short* data = (short*)(mBuffer->lpData);
unsigned samples = mBuffer->dwBytesRecorded / sizeof(short);
// etc..

答案 1 :(得分:2)

我已经完成了所有这些,但这里有太多信息要详细解释。我建议你看一下source code of PortAudio:这是MME的一个非常好的实现,虽然有些部分有点太乱了,但它包含了你所追求的一切,包括转换。

  • 转换数据:它取决于您的输入格式。 MME缓冲区将所有内容存储在char *中,但这并不意味着实际样本是字符。它们可能是8,16,24,32(int或float)或64位(float)。通常使用简单的位移来完成从一个整数到另一个整数的转换。将整数转换为float是正常的,首先使用简单的强制转换将整数转换为float,然后将其除以该整数的最大值,以获得介于-1.0和1.0之间的数字。这也称为规范化。您还可以为所有这些转化添加抖动。
  • 缓冲数据:你不想在回调中花费太多时间,或者你会丢弃缓冲区,因此回调中的磁盘I / O没有完成。 Imo循环缓冲区结合生产者/消费者模式是解决这个问题最方便的:缓冲区在MME回调中被填充(使用非常快的简单memcpy),然后它会发出另一个线程的信号。在发出信号时,线程会检查缓冲区中有多少数据,并将其写入文件(如果足够的话)(您不希望每次都将小块写入文件,而是等待大量数据并转储它立刻)。这样就可以从回调中分离慢速磁盘I / O.
  • 将数据输出到输出设备:就像你有一个waveInProc一样,你将有一个waveOutProc用于输出。在该回调中,您应该写入要输出到缓冲区的数据。

答案 2 :(得分:2)

将8/16/32位/样本数组转换为浮点数组:

void src_BYTE_to_float_array(const unsigned char* in, float* out, int len)
{
    while (len)
    {
        len--;
        out[len]= (float) (in [len] / (1.0 * 0x80) -1.0);
    }
}

void src_short_to_float_array(const short* in, float* out, int len)
{
    while (len)
    {
        len--;
        out[len]= (float) (in [len] / (1.0 * 0x8000)) ;
    }
}

void src_int_to_float_array(const int* in, float* out, int len)
{
    while (len)
    {
        len--;
        out[len]= (float) (in [len] / (8.0 * 0x10000000)) ;
    }
}

要播放样本,你必须做相反的事情:

void src_float_to_BYTE_array(const float* in, unsigned char* out, int len)
{
    double scaled_value;

    while (len)
    {
        len--;

        scaled_value= in[len] * (8.0 * 0x10000000);
        if (scaled_value >= (1.0 * 0x7FFFFFFF))
        {
            out[len]= 255;
            continue ;
        }

        out[len]= (unsigned char)((lrint(scaled_value) >> 24) + 0x80);
    }
}

void src_float_to_short_array(const float* in, short* out, int len)
{   
    double scaled_value;

    while (len)
    {
        len--;

        scaled_value= in[len] * (8.0 * 0x10000000);
        if (scaled_value >= (1.0 * 0x7FFFFFFF))
        {
            out[len]= 32767;
            continue ;
        }

        out[len]= (short)(lrint(scaled_value) >> 16);
    }
}

void src_float_to_int_array(const float* in, int* out, int len)
{   
    double scaled_value;

    while (len)
    {
        len--;

        scaled_value= in[len] * (8.0 * 0x10000000);
        if (scaled_value >= (1.0 * 0x7FFFFFFF))
        {
            out[len]= 0x7fffffff;
            continue;
        }

        out[len]= lrint(scaled_value);
    }
}