#include #include #include #include #include #include #include "libs/pthread/include/pthread.h" #define INSTANT_RECORD_DATA 2 extern std::string gSdkConfigBasePath; extern std::vector gWaveData; extern volatile bool gFinishRecording; extern std::mutex gMutex; extern std::condition_variable gCondition; extern pthread_mutex_t gPMutex; extern pthread_cond_t gPCondition; using namespace std; // ns(nanosecond) : 纳秒,时间单位。一秒的十亿分之一 // 1秒=1000毫秒; 1毫秒=1000微秒; 1微秒=1000纳秒 // The REFERENCE_TIME data type defines the units for reference times in DirectShow. // Each unit of reference time is 100 nanoseconds.(100纳秒为一个REFERENCE_TIME时间单位) // 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("录音失败!\n"); 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); #define MoveMemory RtlMoveMemory #define CopyMemory RtlCopyMemory #define FillMemory RtlFillMemory #define ZeroMemory RtlZeroMemory #define min(a,b) (((a) < (b)) ? (a) : (b)) // // 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: bytes containing a waveformat structure. // DATA header: 8 bytes consisting of the signature "data" followed by a 4 byte file length. // wave data: 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(BufferSize); BYTE *waveFileData = new (std::nothrow) BYTE[waveFileSize]; BYTE *waveFilePointer = waveFileData; WAVEHEADER *waveHeader = reinterpret_cast(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(waveFilePointer)) = static_cast(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) { HRESULT hr = NOERROR; SYSTEMTIME st; GetLocalTime(&st); TCHAR waveFileName[MAX_PATH]; #ifdef UNICODE _stprintf_s(waveFileName, MAX_PATH, _T("%S\\recordTest.wav"), gSdkConfigBasePath.c_str() );//%S宽字符 #else _stprintf_s(waveFileName, MAX_PATH, _T("%s\\recordTest.wav"), gSdkConfigBasePath.c_str() );//%s单字符 #endif 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)) { std::cout<<"Successfully wrote WAVE data to "<GetDefaultAudioEndpoint(eCapture, eConsole, &pDevice); // 采集麦克风 //hr = pEnumerator->GetDefaultAudioEndpoint(eCapture, eMultimedia, &pDevice); #else hr = pEnumerator->GetDefaultAudioEndpoint(eRender, eConsole, &pDevice); // 采集声卡 #endif EXIT_ON_ERROR(hr) // 创建一个管理对象,通过它可以获取到你需要的一切数据 hr = pDevice->Activate(IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&pAudioClient); EXIT_ON_ERROR(hr) memset(&sampleFormat, 0, sizeof(sampleFormat)); sampleFormat.wFormatTag = WAVE_FORMAT_PCM; sampleFormat.nChannels = 1; sampleFormat.nSamplesPerSec = 16000; sampleFormat.wBitsPerSample = 16; sampleFormat.nBlockAlign = sampleFormat.nChannels * (sampleFormat.wBitsPerSample / 8); sampleFormat.nAvgBytesPerSec = sampleFormat.nSamplesPerSec * sampleFormat.nBlockAlign; sampleFormat.cbSize = 0; printf("\n设定录音参数\n"); //cout<<"wFormatTag : "<wFormatTag<nChannels<nSamplesPerSec<nAvgBytesPerSec<nBlockAlign<wBitsPerSample<cbSize<nChannels << endl << "采样率 : " << pwfx->nSamplesPerSec << endl << "位数 : " << pwfx->wBitsPerSample << endl; ////////////////////////////////////////////////////////////////////////// nFrameSize = (pwfx->wBitsPerSample / 8) * pwfx->nChannels; //cout<<"nFrameSize : "<Initialize( AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_NOPERSIST, hnsRequestedDuration, 0, pwfx, NULL); #else /* The AUDCLNT_STREAMFLAGS_LOOPBACK flag enables loopback recording. In loopback recording, the audio engine copies the audio stream that is being played by a rendering endpoint device into an audio endpoint buffer so that a WASAPI client can capture the stream. If this flag is set, the IAudioClient::Initialize method attempts to open a capture buffer on the rendering device. This flag is valid only for a rendering device and only if the Initialize call sets the ShareMode parameter to AUDCLNT_SHAREMODE_SHARED. Otherwise the Initialize call will fail. If the call succeeds, the client can call the IAudioClient::GetService method to obtain an IAudioCaptureClient interface on the rendering device. For more information, see Loopback Recording. */ hr = pAudioClient->Initialize( AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_LOOPBACK, // 这种模式下,音频engine会将rending设备正在播放的音频流, 拷贝一份到音频的endpoint buffer // 这样的话,WASAPI client可以采集到the stream. // 如果AUDCLNT_STREAMFLAGS_LOOPBACK被设置,IAudioClient::Initialize会尝试 // 在rending设备开辟一块capture buffer。 // AUDCLNT_STREAMFLAGS_LOOPBACK只对rending设备有效, // Initialize仅在AUDCLNT_SHAREMODE_SHARED时才可以使用, 否则Initialize会失败。 // Initialize成功后,可以用IAudioClient::GetService可获取该rending设备的IAudioCaptureClient接口。 hnsRequestedDuration, 0, pwfx, NULL); #endif EXIT_ON_ERROR(hr) /* 1. 该函数返回当前流的最大延时 在IAudioClient对象的生命周期内 不会发生变化 2. Rendering客户端可以用这个延时值,来计算每次处理pass可以写的最小数据量。 注: 使用前须先调用IAudioClient::Initialize */ hr = pAudioClient->GetStreamLatency(&hnsStreamLatency); EXIT_ON_ERROR(hr) //cout<<"GetStreamLatency : "<GetDevicePeriod(&hnsDefaultDevicePeriod, &hnsMinimumDevicePeriod); EXIT_ON_ERROR(hr) //cout<<"GetDevicePeriod ...\n" // <<"hnsDefaultDevicePeriod : "<GetBufferSize(&bufferFrameCount); EXIT_ON_ERROR(hr) //cout<SetEventHandle(hAudioSamplesReadyEvent); if (FAILED(hr)) { printf("Unable to set ready event: %x.\n", hr); return false; } ////////////////////////////////////////////////////////////////////////// // 创建采集管理接口 hr = pAudioClient->GetService(IID_IAudioCaptureClient, (void**)&pCaptureClient); EXIT_ON_ERROR(hr) hr = pAudioClient->Start(); // Start recording. EXIT_ON_ERROR(hr) printf("\n开始录音.....\n\n"); //gWaveData.clear(); nCnt = 0; nCaptureBufferSize = 8*1024*1024; nCurrentCaptureIndex = 0; pbyCaptureBuffer = new (std::nothrow) BYTE[nCaptureBufferSize]; waitArray[0]= hAudioSamplesReadyEvent; //stillPlaying = true; // Each loop fills about half of the shared buffer. while (stillPlaying) { DWORD waitResult = WaitForMultipleObjects(1, waitArray, FALSE, INFINITE); switch (waitResult) { case WAIT_OBJECT_0 + 0: // _AudioSamplesReadyEvent hr = pCaptureClient->GetNextPacketSize(&packetLength); EXIT_ON_ERROR(hr) //printf("%06d # _AudioSamplesReadyEvent packetLength:%06u \n", nCnt, packetLength); while (packetLength != 0) { // Get the available data in the shared buffer. // 锁定缓冲区,获取数据 hr = pCaptureClient->GetBuffer(&pData, &numFramesAvailable, &flags, NULL, NULL); EXIT_ON_ERROR(hr) nCnt++; // test flags ////////////////////////////////////////////////////////////////////////// if (flags & AUDCLNT_BUFFERFLAGS_SILENT) { //printf("AUDCLNT_BUFFERFLAGS_SILENT \n"); ; } if (flags & AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY) { //printf("%06d # AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY \n", nCnt); ; } ////////////////////////////////////////////////////////////////////////// unsigned long circleCount = numFramesAvailable * nFrameSize; unsigned long iCount = 0; while (iCount < circleCount) { gWaveData.push_back((BYTE)pData[iCount]);//will change size,so no need to call resize iCount++; } ///* //std::unique_lock lock(gMutex); if (iCount > 0) { gCondition.notify_all(); } //std::cout << "notify_all :"<< iCount << std::endl; //lock.unlock(); //pthread_mutex_lock(&gPMutex); //pthread_cond_signal(&gPCondition); //pthread_mutex_unlock(&gPMutex); //*/ ///// UINT32 framesToCopy = min(numFramesAvailable, static_cast((nCaptureBufferSize - nCurrentCaptureIndex) / nFrameSize)); if (framesToCopy != 0) { // // The flags on capture tell us information about the data. // // We only really care about the silent flag since we want to put frames of silence into the buffer // when we receive silence. We rely on the fact that a logical bit 0 is silence for both float and int formats. // if (flags & AUDCLNT_BUFFERFLAGS_SILENT) { // // Fill 0s from the capture buffer to the output buffer. // size_t nCurrentCaptureIndexTemp = nCurrentCaptureIndex; ZeroMemory(&pbyCaptureBuffer[nCurrentCaptureIndex], framesToCopy*nFrameSize); //ZeroMemory(&gWaveData[nCurrentCaptureIndexTemp] , framesToCopy*nFrameSize); } else { // // Copy data from the audio engine buffer to the output buffer. // size_t nCurrentCaptureIndexTemp = nCurrentCaptureIndex; CopyMemory(&pbyCaptureBuffer[nCurrentCaptureIndex], pData, framesToCopy*nFrameSize); //CopyMemory(&gWaveData[nCurrentCaptureIndexTemp] , pData, framesToCopy*nFrameSize); } // // Bump the capture buffer pointer. // nCurrentCaptureIndex += framesToCopy*nFrameSize; } hr = pCaptureClient->ReleaseBuffer(numFramesAvailable); EXIT_ON_ERROR(hr) hr = pCaptureClient->GetNextPacketSize(&packetLength); EXIT_ON_ERROR(hr) // test GetCurrentPadding ////////////////////////////////////////////////////////////////////////// /* This method retrieves a padding value that indicates the amount of valid, unread data that the endpoint buffer currently contains. 返回buffer中合法的未读取的数据大小。 The padding value is expressed as a number of audio frames. The size in bytes of an audio frame equals the number of channels in the stream multiplied by the sample size per channel. For example, the frame size is four bytes for a stereo (2-channel) stream with 16-bit samples. The padding value的单位是audio frame。 一个audio frame的大小等于 通道数 * 每个通道的sample大小。 For a shared-mode capture stream, the padding value reported by GetCurrentPadding specifies the number of frames of capture data that are available in the next packet in the endpoint buffer. */ UINT32 ui32NumPaddingFrames; hr = pAudioClient->GetCurrentPadding(&ui32NumPaddingFrames); EXIT_ON_ERROR(hr) if (0 != ui32NumPaddingFrames) { //printf("GetCurrentPadding : %6u\n", ui32NumPaddingFrames); ; } ////////////////////////////////////////////////////////////////////////// if (gFinishRecording) { stillPlaying = false; break; } } // end of 'while (packetLength != 0)' break; } // end of 'switch (waitResult)' } // end of 'while (stillPlaying)' // // We've now captured our wave data. Now write it out in a wave file. // //SaveWaveData(pbyCaptureBuffer, nCurrentCaptureIndex, pwfx); pTemp = reinterpret_cast(&gWaveData.front()); printf("%d, %d", gWaveData.size() , nCurrentCaptureIndex); SaveWaveData(pTemp, gWaveData.size(), pwfx); printf("\n\n"); printf("\n结束录音.\n"); printf("\n\n"); hr = pAudioClient->Stop(); // Stop recording. EXIT_ON_ERROR(hr) Exit: //CoTaskMemFree(pwfx); SAFE_RELEASE(pEnumerator) SAFE_RELEASE(pDevice) SAFE_RELEASE(pAudioClient) SAFE_RELEASE(pCaptureClient) CoUninitialize(); if (pbyCaptureBuffer) { delete [] pbyCaptureBuffer; pbyCaptureBuffer = NULL; } if (hAudioSamplesReadyEvent) { CloseHandle(hAudioSamplesReadyEvent); hAudioSamplesReadyEvent = NULL; } //getchar(); std::cerr << "recorderThread finished" << std::endl; return 0; }