#include #include #include #include #include #include #include //----------------------------------------------------------- // 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 #define EXIT_ON_ERROR(hres) \ if (FAILED(hres)) { printf("%s:%d\n", __func__, __LINE__); 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); class MyAudioSink { public: MyAudioSink() { Wfx = WAVEFORMATEX(); } HRESULT SetFormat(WAVEFORMATEX* pwfx) { Wfx = *pwfx; /* printf("format: %d, channels: %d, sampleRate: %d, blockAlign: %d, bitsPerSample: %d", Wfx.wFormatTag,Wfx.nChannels, Wfx.nSamplesPerSec, Wfx.nBlockAlign, Wfx.wBitsPerSample); */ return S_OK; } HRESULT CopyData(BYTE* pData, UINT32 numFramesAvailable, BOOL* bDone) { if (!pData) return S_FALSE; std::ofstream ofs; ofs.open("D:\\AudioCapture.pcm", std::ios::app | std::ios::binary); ofs.write(reinterpret_cast(pData), numFramesAvailable * Wfx.nBlockAlign); ofs.close(); if (stop) { *bDone = TRUE; } return S_OK; } void StopSink() { printf("stop recording\n"); stop = true; }; private: bool stop = { false }; WAVEFORMATEX Wfx; }; HRESULT RecordAudioStream(MyAudioSink *pMySink) { 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 = CoCreateInstance( CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, IID_IMMDeviceEnumerator, (void**)&pEnumerator); EXIT_ON_ERROR(hr) hr = pEnumerator->GetDefaultAudioEndpoint( eRender, eConsole, &pDevice); EXIT_ON_ERROR(hr) hr = pDevice->Activate( IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&pAudioClient); EXIT_ON_ERROR(hr) hr = pAudioClient->GetMixFormat(&pwfx); EXIT_ON_ERROR(hr) hr = pAudioClient->Initialize( AUDCLNT_SHAREMODE_SHARED, //0, AUDCLNT_STREAMFLAGS_LOOPBACK, hnsRequestedDuration, 0, pwfx, NULL); EXIT_ON_ERROR(hr) // Get the size of the allocated buffer. hr = pAudioClient->GetBufferSize(&bufferFrameCount); EXIT_ON_ERROR(hr) hr = pAudioClient->GetService( IID_IAudioCaptureClient, (void**)&pCaptureClient); EXIT_ON_ERROR(hr) // Notify the audio sink which format to use. hr = pMySink->SetFormat(pwfx); EXIT_ON_ERROR(hr) // 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) // Each loop fills about half of the shared buffer. while (bDone == FALSE) { // Sleep for half the buffer duration. Sleep(hnsActualDuration/REFTIMES_PER_MILLISEC/2); hr = pCaptureClient->GetNextPacketSize(&packetLength); EXIT_ON_ERROR(hr) while (packetLength != 0) { // Get the available data in the shared buffer. hr = pCaptureClient->GetBuffer( &pData, &numFramesAvailable, &flags, NULL, NULL); EXIT_ON_ERROR(hr) if (flags & AUDCLNT_BUFFERFLAGS_SILENT) { pData = NULL; // Tell CopyData to write silence. } // Copy the available capture data to the audio sink. hr = pMySink->CopyData( pData, numFramesAvailable, &bDone); EXIT_ON_ERROR(hr) hr = pCaptureClient->ReleaseBuffer(numFramesAvailable); EXIT_ON_ERROR(hr) hr = pCaptureClient->GetNextPacketSize(&packetLength); EXIT_ON_ERROR(hr) } } hr = pAudioClient->Stop(); // Stop recording. EXIT_ON_ERROR(hr) Exit: CoTaskMemFree(pwfx); SAFE_RELEASE(pEnumerator) SAFE_RELEASE(pDevice) SAFE_RELEASE(pAudioClient) SAFE_RELEASE(pCaptureClient) return hr; } int main() { MyAudioSink sink; HRESULT hr = CoInitialize(NULL); std::cout << "CoInitialize: " << hr << std::endl; auto f = std::async(std::launch::deferred, RecordAudioStream, &sink); printf("Press ENTER to Stop\n"); getchar(); sink.StopSink(); f.wait(); if (!FAILED(hr)) CoUninitialize(); }