简体   繁体   English

如何在 WASAPICaptureSharedEventDriven 中以原始格式保存音频文件

[英]How to save audio file with raw format in WASAPICaptureSharedEventDriven

I want to record audio from microphone and save data to raw format and i find WASAPICaptureSharedEventDriven,This sample application uses the Core Audio APIs to capture audio data from an input device specified by the user and writes it to wav file.我想从麦克风录制音频并将数据保存为原始格式,我发现 WASAPICaptureSharedEventDriven,此示例应用程序使用核心音频 API 从用户指定的输入设备捕获音频数据并将其写入 wav 文件。 This is code:这是代码:

//  WAV file writer.
//
//  This is a VERY simple .WAV file writer.
//

//
//  A wave file consists of:
//
//  RIFF header:    8 bytes consisting of the signature "RIFF" followed by a 4 byte file length.
//  WAVE header:    4 bytes consisting of the signature "WAVE".
//  fmt header:     4 bytes consisting of the signature "fmt " followed by a WAVEFORMATEX 
//  WAVEFORMAT:     <n> bytes containing a waveformat structure.
//  DATA header:    8 bytes consisting of the signature "data" followed by a 4 byte file length.
//  wave data:      <m> bytes containing wave data.
//
//
//  Header for a WAV file - we define a structure describing the first few fields in the header for convenience.
//
struct WAVEHEADER
{
    DWORD   dwRiff;                     // "RIFF"
    DWORD   dwSize;                     // Size
    DWORD   dwWave;                     // "WAVE"
    DWORD   dwFmt;                      // "fmt "
    DWORD   dwFmtSize;                  // Wave Format Size
};

//  Static RIFF header, we'll append the format to it.
const BYTE WaveHeader[] = 
{
    'R',   'I',   'F',   'F',  0x00,  0x00,  0x00,  0x00, 'W',   'A',   'V',   'E',   'f',   'm',   't',   ' ', 0x00, 0x00, 0x00, 0x00
};

//  Static wave DATA tag.
const BYTE WaveData[] = { 'd', 'a', 't', 'a'};

//
//  Write the contents of a WAV file.  We take as input the data to write and the format of that data.
//
bool WriteWaveFile(HANDLE FileHandle, const BYTE *Buffer, const size_t BufferSize, const WAVEFORMATEX *WaveFormat)
{
    DWORD waveFileSize = sizeof(WAVEHEADER) + sizeof(WAVEFORMATEX) + WaveFormat->cbSize + sizeof(WaveData) + sizeof(DWORD) + static_cast<DWORD>(BufferSize);
    BYTE *waveFileData = new (std::nothrow) BYTE[waveFileSize];
    BYTE *waveFilePointer = waveFileData;
    WAVEHEADER *waveHeader = reinterpret_cast<WAVEHEADER *>(waveFileData);

    if (waveFileData == NULL)
    {
        printf("Unable to allocate %d bytes to hold output wave data\n", waveFileSize);
        return false;
    }

    //
    //  Copy in the wave header - we'll fix up the lengths later.
    //
    CopyMemory(waveFilePointer, WaveHeader, sizeof(WaveHeader));
    waveFilePointer += sizeof(WaveHeader);

    //
    //  Update the sizes in the header.
    //
    waveHeader->dwSize = waveFileSize - (2 * sizeof(DWORD));
    waveHeader->dwFmtSize = sizeof(WAVEFORMATEX) + WaveFormat->cbSize;

    //
    //  Next copy in the WaveFormatex structure.
    //
    CopyMemory(waveFilePointer, WaveFormat, sizeof(WAVEFORMATEX) + WaveFormat->cbSize);
    waveFilePointer += sizeof(WAVEFORMATEX) + WaveFormat->cbSize;


    //
    //  Then the data header.
    //
    CopyMemory(waveFilePointer, WaveData, sizeof(WaveData));
    waveFilePointer += sizeof(WaveData);
    *(reinterpret_cast<DWORD *>(waveFilePointer)) = static_cast<DWORD>(BufferSize);
    waveFilePointer += sizeof(DWORD);

    //
    //  And finally copy in the audio data.
    //
    CopyMemory(waveFilePointer, Buffer, BufferSize);

    //
    //  Last but not least, write the data to the file.
    //
    DWORD bytesWritten;
    if (!WriteFile(FileHandle, waveFileData, waveFileSize, &bytesWritten, NULL))
    {
        printf("Unable to write wave file: %d\n", GetLastError());
        delete []waveFileData;
        return false;
    }

    if (bytesWritten != waveFileSize)
    {
        printf("Failed to write entire wave file\n");
        delete []waveFileData;
        return false;
    }
    delete []waveFileData;
    return true;
}

//
//  Write the captured wave data to an output file so that it can be examined later.
//
void SaveWaveData(BYTE *CaptureBuffer, size_t BufferSize, const WAVEFORMATEX *WaveFormat)
{
    wchar_t waveFileName[MAX_PATH];
    HRESULT hr = StringCbCopy(waveFileName, sizeof(waveFileName), L"WASAPICaptureEventDriven-");
    if (SUCCEEDED(hr))
    {
        GUID testGuid;
        if (SUCCEEDED(CoCreateGuid(&testGuid)))
        {
            wchar_t *guidString;
            if (SUCCEEDED(StringFromCLSID(testGuid, &guidString)))
            {
                hr = StringCbCat(waveFileName, sizeof(waveFileName), guidString);
                if (SUCCEEDED(hr))
                {
                    hr = StringCbCat(waveFileName, sizeof(waveFileName), L".WAV");
                    if (SUCCEEDED(hr))
                    {
                        HANDLE waveHandle = CreateFile(waveFileName, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, 
                            FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, 
                            NULL);
                        if (waveHandle != INVALID_HANDLE_VALUE)
                        {
                            if (WriteWaveFile(waveHandle, CaptureBuffer, BufferSize, WaveFormat))
                            {
                                printf("Successfully wrote WAVE data to %S\n", waveFileName);
                            }
                            else
                            {
                                printf("Unable to write wave file\n");
                            }
                            CloseHandle(waveHandle);
                        }
                        else
                        {
                            printf("Unable to open output WAV file %S: %d\n", waveFileName, GetLastError());
                        }
                    }
                }
                CoTaskMemFree(guidString);
            }
        }
    }
}

I try to save audio from buffer:我尝试从缓冲区保存音频:

FILE* _file;
int16_t* _data;
_data = (int16_t*)Buffer;
_file = fopen("utterance", "wb +");
fwrite(_data, 1,BufferSize, _file);
fclose(_file);

output audio is so bad,To view it I use this code to plot Raw file: output 音频太糟糕了,要查看它我使用此代码到 plot 原始文件:

import numpy as np
import matplotlib.pyplot as plt
with open ('test.raw', 'rb') as f:
    buf = f.read ()
    data = np.frombuffer (buf, dtype = 'int16')
    L = data [:: 2]
    R = data [1 :: 2]

newdata = np.squeeze(data) # Shape is now: (10, 80)
plt.plot(newdata) # plotting by columns
plt.show()

And output is this,you can it is a rectangle,the sound is very shy:而output就是这个,你可以是一个长方形,声音很害羞:

在此处输入图像描述

where are i wrong?我哪里错了? Please help me!请帮我!

After checking, there is no problem with the properties of the raw audio sample.经检查,原始音频样本的属性没有问题。 Please try my code sample and add wav header to re-encode.请尝试我的代码示例并添加 wav header 以重新编码。

#include <Windows.h>
#include <stdio.h>
#include <MMDeviceAPI.h>
#include <AudioClient.h>
#include <assert.h>
#include <avrt.h>
#include <strsafe.h>
#include <fstream> 

using namespace std;

#pragma warning(disable:4996)

struct WAVEHEADER
{
    DWORD   dwRiff;                     // "RIFF"
    DWORD   dwSize;                     // Size
    DWORD   dwWave;                     // "WAVE"
    DWORD   dwFmt;                      // "fmt "
    DWORD   dwFmtSize;                  // Wave Format Size
};

const BYTE WaveHeader[] =
{
    'R',   'I',   'F',   'F',  0x00,  0x00,  0x00,  0x00, 'W',   'A',   'V',   'E',   'f',   'm',   't',   ' ', 0x00, 0x00, 0x00, 0x00
};

const BYTE WaveData[] = { 'd', 'a', 't', 'a' };

bool WriteWaveFile(HANDLE FileHandle, const BYTE* Buffer, const size_t BufferSize, WAVEFORMATEX* WaveFormat)
{
    DWORD waveFileSize = sizeof(WAVEHEADER) + sizeof(WAVEFORMATEX) + WaveFormat->cbSize + sizeof(WaveData) + sizeof(DWORD) + static_cast<DWORD>(BufferSize);
    BYTE* waveFileData = new (std::nothrow) BYTE[waveFileSize];
    BYTE* waveFilePointer = waveFileData;
    WAVEHEADER* waveHeader = reinterpret_cast<WAVEHEADER*>(waveFileData);

    if (waveFileData == NULL)
    {
        printf("Unable to allocate %d bytes to hold output wave data\n", waveFileSize);
        return false;
    }

    //
    //  Copy in the wave header - we'll fix up the lengths later.
    //
    CopyMemory(waveFilePointer, WaveHeader, sizeof(WaveHeader));
    waveFilePointer += sizeof(WaveHeader);

    //
    //  Update the sizes in the header.
    //
    waveHeader->dwSize = waveFileSize - (2 * sizeof(DWORD));
    waveHeader->dwFmtSize = sizeof(WAVEFORMATEX) + WaveFormat->cbSize;

    //
    //  Next copy in the WaveFormatex structure.
    //
    CopyMemory(waveFilePointer, WaveFormat, sizeof(WAVEFORMATEX) + WaveFormat->cbSize);
    waveFilePointer += sizeof(WAVEFORMATEX) + WaveFormat->cbSize;


    //
    //  Then the data header.
    //
    CopyMemory(waveFilePointer, WaveData, sizeof(WaveData));
    waveFilePointer += sizeof(WaveData);
    *(reinterpret_cast<DWORD*>(waveFilePointer)) = static_cast<DWORD>(BufferSize);
    waveFilePointer += sizeof(DWORD);

    //
    //  And finally copy in the audio data.
    // 

    CopyMemory(waveFilePointer, Buffer, BufferSize);
    //
    //  Last but not least, write the data to the file.
    //
    DWORD bytesWritten;
    if (!WriteFile(FileHandle, waveFileData, waveFileSize, &bytesWritten, NULL))
    {
        printf("Unable to write wave file: %d\n", GetLastError());
        delete[]waveFileData;
        return false;
    }

    if (bytesWritten != waveFileSize)
    {
        printf("Failed to write entire wave file\n");
        delete[]waveFileData;
        return false;
    }
    delete[]waveFileData;
    return true;
}

//
//  Write the captured wave data to an output file so that it can be examined later.
//
void SaveWaveData(BYTE* CaptureBuffer, size_t BufferSize, WAVEFORMATEX* WaveFormat)
{
    wchar_t waveFileName[MAX_PATH];
    HRESULT hr = StringCbCopy(waveFileName, sizeof(waveFileName), L"WASAPICaptureEventDriven-");
    if (SUCCEEDED(hr))
    {
        GUID testGuid;
        if (SUCCEEDED(CoCreateGuid(&testGuid)))
        {
            wchar_t* guidString;
            if (SUCCEEDED(StringFromCLSID(testGuid, &guidString)))
            {
                hr = StringCbCat(waveFileName, sizeof(waveFileName), guidString);
                if (SUCCEEDED(hr))
                {
                    hr = StringCbCat(waveFileName, sizeof(waveFileName), L".WAV");
                    if (SUCCEEDED(hr))
                    {
                        HANDLE waveHandle = CreateFile(waveFileName, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS,
                            FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,
                            NULL);
                        if (waveHandle != INVALID_HANDLE_VALUE)
                        {
                            if (WriteWaveFile(waveHandle, CaptureBuffer, BufferSize, WaveFormat))
                            {
                                printf("Successfully wrote WAVE data to %S\n", waveFileName);
                            }
                            else
                            {
                                printf("Unable to write wave file\n");
                            }
                            CloseHandle(waveHandle);
                        }
                        else
                        {
                            printf("Unable to open output WAV file %S: %d\n", waveFileName, GetLastError());
                        }
                    }
                }
                CoTaskMemFree(guidString);
            }
        }
    }
}

int main()
{
    long buffersize = 3528000;// For 10s audio sample, we can set the value of buffersize to 3528000
    BYTE* captureBuffer = new (std::nothrow) BYTE[buffersize]; 
    
    FILE* _file;
    _file = fopen("utterance.raw", "rb"); //raw audio path
    fread(captureBuffer, 1, buffersize, _file);
    fclose(_file);

    WAVEFORMATEX wavformat;
    wavformat.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
    wavformat.nChannels = 2;
    wavformat.nSamplesPerSec = 44100;
    wavformat.nAvgBytesPerSec = 352800;
    wavformat.nBlockAlign = 8;
    wavformat.wBitsPerSample = 32;
    wavformat.cbSize = 22;
    SaveWaveData(captureBuffer, buffersize, &wavformat);

    return 0;
}

You can also use ofstream to write raw audio file.您还可以使用ofstream来编写原始音频文件。

Some code:一些代码:

//  Write the contents of a WAV file.  We take as input the data to write and the format of that data.
//Added in WASAPICaptureSharedEventDriven sample
bool WriteWaveFile(HANDLE FileHandle, const BYTE *Buffer, const size_t BufferSize, const WAVEFORMATEX *WaveFormat)
{
    ofstream binaryFile("file.raw", ios::out | ios::binary);
    binaryFile.write((char*)Buffer, BufferSize);
    binaryFile.close();

    ...

After getting the raw file, use ifstream to open the file and add wav header.得到raw文件后,使用ifstream打开文件,添加wav header。

ifstream infile("utterance.raw", std::ifstream::binary);
 // get size of file
infile.seekg(0, infile.end);
long size = infile.tellg();
infile.seekg(0);

BYTE* captureBuffer = new (std::nothrow) BYTE[size];

infile.read((char*)captureBuffer, size);
infile.close();

WAVEFORMATEX wavformat;
wavformat.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
wavformat.nChannels = 2;
wavformat.nSamplesPerSec = 44100;
wavformat.nAvgBytesPerSec = 352800;
wavformat.nBlockAlign = 8;
wavformat.wBitsPerSample = 32;
wavformat.cbSize = 22;
SaveWaveData(captureBuffer, size, &wavformat);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM