Loopback is saved in buffer, how to write to disk

2019-09-11 19:38发布

问题:

In the code below, you can copy paste it to an empty proejct, add main.cpp and it is saving it to buffer, however I don't know how to write that buffer to file. You can see in SetFormat I set it up, and then in CopyData I am writing to the buffer.

Complete C++ beginner here, I was able to mash this up. How can I write the buffer to file?

#include <mmdeviceapi.h>
#include <audioclient.h>
#include "debug.h"
#include <comdef.h>

#define UNICODE

//-----------------------------------------------------------
// Record an audio stream from the default audio capture
// device. The RecordAudioStream function allocates a shared
// buffer big enough to hold one second of PCM audio data.
// The function uses this buffer to stream data from the
// capture device. The main loop runs every 1/2 second.
//-----------------------------------------------------------

// REFERENCE_TIME time units per second and per millisecond
#define REFTIMES_PER_SEC  10000000
#define REFTIMES_PER_MILLISEC  10000

// function strof(jsint) { var prim = jsint >>> 0; return prim.toString(16) }

#define EXIT_ON_ERROR(hres, title)  \
            if (FAILED(hres)) { \
                _com_error hres_str(hres); \
                debug_log("exit due to error on title", title, "hres:", hres, "hres_str:", hres_str.ErrorMessage()); \
                goto Exit; \
            }
#define SAFE_RELEASE(punk)  \
              if ((punk) != NULL)  \
                { (punk)->Release(); (punk) = NULL; }

const CLSID CLSID_MMDeviceEnumerator = __uuidof(MMDeviceEnumerator);
const IID IID_IMMDeviceEnumerator = __uuidof(IMMDeviceEnumerator);
const IID IID_IAudioClient = __uuidof(IAudioClient);
const IID IID_IAudioCaptureClient = __uuidof(IAudioCaptureClient);


PWAVEFORMATEX m_pwfx;
byte* recordBuffer;;

HRESULT SetFormat(WAVEFORMATEX *pwfx, UINT32 bufferSize) {
    HRESULT hr;

    m_pwfx = pwfx;
    recordBuffer = new byte[bufferSize * pwfx->nBlockAlign];
    debug_log("Record buffer set to:", (bufferSize * pwfx->nBlockAlign), "bytes");


    return 0;
}



HRESULT CopyData(BYTE *pData, UINT32 numFramesAvailable, BOOL *bDone) {
    /* Get the lock */
    // memcpy((void *)MyAudioSink::buff, (void *)pData, numFramesAvailable);
    // printf("%f\n", buff[0]);

    memcpy((void*)recordBuffer, pData, numFramesAvailable * m_pwfx->nBlockAlign);

    for (size_t i = 0; i < numFramesAvailable; i++) {
        debug_log((short)pData[i]);
    }
    return 0;
}

HRESULT RecordAudioStream() {
    HRESULT hr;
    REFERENCE_TIME hnsRequestedDuration = REFTIMES_PER_SEC;
    REFERENCE_TIME hnsActualDuration;
    UINT32 bufferFrameCount;
    UINT32 numFramesAvailable;
    IMMDeviceEnumerator *pEnumerator = NULL;
    IMMDevice *pDevice = NULL;
    IAudioClient *pAudioClient = NULL;
    IAudioCaptureClient *pCaptureClient = NULL;
    WAVEFORMATEX *pwfx = NULL;
    UINT32 packetLength = 0;
    BOOL bDone = FALSE;
    BYTE *pData;
    DWORD flags;

    hr = CoInitialize(NULL);
    EXIT_ON_ERROR(hr, "CoInitialize");

    hr = CoCreateInstance(CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, IID_IMMDeviceEnumerator, (void**)&pEnumerator);
    EXIT_ON_ERROR(hr, "CoCreateInstance");

    hr = pEnumerator->GetDefaultAudioEndpoint(eCapture, eConsole, &pDevice);
    EXIT_ON_ERROR(hr, "GetDefaultAudioEndpoint");

    hr = pDevice->Activate(IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&pAudioClient);
    EXIT_ON_ERROR(hr, "Activate");

    hr = pAudioClient->GetMixFormat(&pwfx);
    EXIT_ON_ERROR(hr, "GetMixFormat");

    hr = pAudioClient->Initialize(AUDCLNT_SHAREMODE_SHARED, 0, hnsRequestedDuration, 0, pwfx, NULL);
    EXIT_ON_ERROR(hr, "Initialize");

    // Get the size of the allocated buffer.
    hr = pAudioClient->GetBufferSize(&bufferFrameCount);
    EXIT_ON_ERROR(hr, "GetBufferSize");

    hr = pAudioClient->GetService(IID_IAudioCaptureClient, (void**)&pCaptureClient);
    EXIT_ON_ERROR(hr, "GetService");

    // Notify the audio sink which format to use.
    hr = SetFormat(pwfx, bufferFrameCount);
    EXIT_ON_ERROR(hr, "SetFormat");

    // Calculate the actual duration of the allocated buffer.
    hnsActualDuration = (double)REFTIMES_PER_SEC * bufferFrameCount / pwfx->nSamplesPerSec;

    hr = pAudioClient->Start();  // Start recording.
    EXIT_ON_ERROR(hr, "Start");

    // Each loop fills about half of the shared buffer.
    while (bDone == FALSE) {
        debug_log("at top of loop");
        // Sleep for half the buffer duration.
        Sleep(hnsActualDuration / REFTIMES_PER_MILLISEC / 2);

        hr = pCaptureClient->GetNextPacketSize(&packetLength);
        EXIT_ON_ERROR(hr, "GetNextPacketSize");

        while (packetLength != 0 && bDone == FALSE) {
            // Get the available data in the shared buffer.
            hr = pCaptureClient->GetBuffer(&pData, &numFramesAvailable, &flags, NULL, NULL);
            EXIT_ON_ERROR(hr, "GetBuffer");

            if (flags & AUDCLNT_BUFFERFLAGS_SILENT) {
                pData = NULL;  // Tell CopyData to write silence.
            }

            // Copy the available capture data to the audio sink.
            hr = CopyData(pData, numFramesAvailable, &bDone);
            EXIT_ON_ERROR(hr, "CopyData");

            hr = pCaptureClient->ReleaseBuffer(numFramesAvailable);
            EXIT_ON_ERROR(hr, "ReleaseBuffer");

            hr = pCaptureClient->GetNextPacketSize(&packetLength);
            EXIT_ON_ERROR(hr, "GetNextPacketSize 2");
        }
    }

    hr = pAudioClient->Stop();  // Stop recording.
    EXIT_ON_ERROR(hr, "Stop");

Exit:
    CoTaskMemFree(pwfx);
    SAFE_RELEASE(pEnumerator);
    SAFE_RELEASE(pDevice);
    SAFE_RELEASE(pAudioClient);
    SAFE_RELEASE(pCaptureClient);

    CoUninitialize();

    return hr;
}

int main() {
    RecordAudioStream();
}

debug.h - in case you want to copy paste the above

#include <utility>
#include <fstream>
#include <string>

template <typename HeadType> bool
debug_log_rec(std::ostream& out, HeadType&& head) {
    out << head;
    out << std::endl;
    return true;
}

template <typename HeadType, typename... TailTypes> bool
debug_log_rec(std::ostream& out, HeadType&& head, TailTypes&&... tails) {
    out << head;
    out << " ";
    debug_log_rec(out, std::forward<TailTypes>(tails)...);
    return true;
}

template <typename... ArgTypes> bool
debug_log(ArgTypes&&... args) {
    std::fstream fs;
    fs.open("C:\\Users\\Mercurius\\Desktop\\log.txt", std::fstream::app);
    debug_log_rec(fs, std::forward<ArgTypes>(args)...);
    fs.close();
    return true;
}