#define WIN32_LEAN_AND_MEAN #include #include #include #include #include #include #include #include // file contains code to: // enumerate Windows Media codecs on the system (GetCodecName()) // enumerate all input prop types of a IWMWriter (EnumInProps()) // Find a Windows Media encoder format given a bps value and a WAVEFORMATEX (FindAudioformat()) // capture about 26 seconds of live audio in 88kbps WMA format (RecordAudioStream()) // transform 32-bit float PCM to 16-bit short integer PCM with SSE intrnsics (ConvertTo16Bit()) // find the max float in an array using SSE (unused in the actual code) (FindMaxFloat()) struct INPUTINFO { UINT channels; UINT sampleRate; }; #pragma comment(lib, "wmvcore.lib") // 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)) { 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); template class ComType { private: Type* data; public: ComType(Type* data) : data(data) { static_cast(data); } ComType() : data(NULL) {} ~ComType() { if(data) { data->Release(); } } Type* get() const { assert(data && "No object set"); return data; } Type* operator*() const { return get(); } Type** operator&() { assert(!data && "Object already initialized"); return &data; } Type* operator->() const { return get(); } bool operator!() const { return !data; } void Init(Type* newData) { assert(!data && "Object already initialized"); data = newData; } Type* Release() { Type* temp = data; data = NULL; return temp; } }; #define RETURN_ON_FAILED_EXP(exp) \ { \ HRESULT hr = (exp); \ if(FAILED(hr)) \ { \ return hr; \ } \ } #define GOTO_EXIT_IF_FAILED(hr) \ if(FAILED((hr))) \ { \ goto Exit; \ } // The FindAudioFormat function finds a compressed audio format that // matches the criteria defined by the input parameters. HRESULT FindAudioFormat(GUID SubType, WAVEFORMATEX* pWaveLimits, DWORD dwMaxRate, BOOL fAVSync, IWMCodecInfo3* pCodecInfo, IWMStreamConfig** ppStreamConfig) { HRESULT hr = S_OK; const DWORD INVALID_INDEX = MAXWORD; ComType pBestMatch; // This value is beyond the codec indexes // and will be used to verify success. DWORD codecIndex = INVALID_INDEX, cEntries = 0; // Get the number of audio codecs for which there is information. hr = pCodecInfo->GetCodecInfoCount(WMMEDIATYPE_Audio, &cEntries); GOTO_EXIT_IF_FAILED(hr); // Find the index of the codec corresponding to the requested subytpe. for(DWORD index = 0; (index < cEntries) && (codecIndex == INVALID_INDEX); ++index) { ComType pStreamConfig; // Get the first format for each codec. hr = pCodecInfo->GetCodecFormat(WMMEDIATYPE_Audio, index, 0, &pStreamConfig); if(FAILED(hr)) { continue; } WCHAR name[90]; DWORD nameSize = ARRAYSIZE(name); pCodecInfo->GetCodecName(WMMEDIATYPE_Audio, index, name, &nameSize); // Get the media properties interface. ComType pProps; hr = pStreamConfig->QueryInterface(&pProps); GOTO_EXIT_IF_FAILED(hr); // Get the size required for the media type structure. DWORD typeSize = 0; hr = pProps->GetMediaType(NULL, &typeSize); GOTO_EXIT_IF_FAILED(hr); BYTE mediaType[200] = {0}; BYTE* pMediaTypeBuf = mediaType; if(typeSize > sizeof(mediaType)) { // Allocate memory for the media type structure. pMediaTypeBuf = new BYTE[typeSize]; } WM_MEDIA_TYPE* pMediaType = reinterpret_cast(pMediaTypeBuf); // Get the media type structure. hr = pProps->GetMediaType(pMediaType, &typeSize); // Check this codec against the one requested. if(pMediaType->subtype == SubType) { codecIndex = index; } // The subtypes did not match. Clean up for next iteration. if(pMediaTypeBuf != mediaType) { delete [] pMediaTypeBuf; } } // for index // The subtype is invalid if no codec was found that matches it. if(codecIndex == INVALID_INDEX) { hr = E_INVALIDARG; goto Exit; } DWORD dwBestRate = 0; DWORD PacketsPerSecond = 0; // Get the number of formats supported for the codec. hr = pCodecInfo->GetCodecFormatCount(WMMEDIATYPE_Audio, codecIndex, &cEntries); GOTO_EXIT_IF_FAILED(hr); // Loop through the formats for the codec, looking for matches. for(DWORD index = 0; index < cEntries; ++index) { ComType pStreamConfig; // Get the next format. WCHAR desc[300]; DWORD descSize = ARRAYSIZE(desc); hr = pCodecInfo->GetCodecFormatDesc(WMMEDIATYPE_Audio, codecIndex, index, &pStreamConfig, desc, &descSize); GOTO_EXIT_IF_FAILED(hr); // Get the media properties interface. ComType pProps; hr = pStreamConfig->QueryInterface(&pProps); GOTO_EXIT_IF_FAILED(hr); // Get the size required for the media type structure. DWORD typeSize = 0; hr = pProps->GetMediaType(NULL, &typeSize); GOTO_EXIT_IF_FAILED(hr); BYTE mediaType[200] = {0}; BYTE* pMediaTypeBuf = mediaType; if(typeSize > sizeof(mediaType)) { // Allocate memory for the media type structure. pMediaTypeBuf = new BYTE[typeSize]; } WM_MEDIA_TYPE* pMediaType = reinterpret_cast(pMediaTypeBuf); // Get the media type structure. hr = pProps->GetMediaType(pMediaType, &typeSize); GOTO_EXIT_IF_FAILED(hr); WAVEFORMATEX* pWave = NULL; // Check that the format data is present. if(pMediaType->cbFormat >= sizeof(WAVEFORMATEX)) { pWave = (WAVEFORMATEX*)pMediaType->pbFormat; } else { // The returned media type should always have an attached // WAVEFORMATEX structure. hr = E_UNEXPECTED; if(pMediaTypeBuf != mediaType) { delete [] pMediaTypeBuf; } goto Exit; } // Start checking data. // Do not check particulars unless the bit rate is in range. if((pWave->nAvgBytesPerSec * 8) > dwBestRate && (pWave->nAvgBytesPerSec * 8) <= dwMaxRate) { // Check the limits. if((pWave->nChannels == pWaveLimits->nChannels) && (pWave->wBitsPerSample == pWaveLimits->wBitsPerSample) && (pWave->nSamplesPerSec == pWaveLimits->nSamplesPerSec)) { // If audio/video synchronization requested, check the number // of packets per second (Bps / BlockAlign). The bit rate is // greater than 3200 bps, this value must be 5. // Otherwise this value is 3. // This is an ASF requirement. if(fAVSync) { if((pWave->nAvgBytesPerSec / pWave->nBlockAlign) >= ((pWave->nAvgBytesPerSec >= 4000) ? 5.0 : 3.0)) { // Release the previous best match. pBestMatch.Release()->Release(); // Set this stream configuration as the new best match. pBestMatch.Init(*pStreamConfig); pBestMatch->AddRef(); // Set the best bit rate. dwBestRate = (pWave->nAvgBytesPerSec * 8); } } // if fAVSync else { wprintf(L"Selected codec config %s\n", desc); // Release the previous best match. if(IWMStreamConfig* pConfig = pBestMatch.Release()) { pConfig->Release(); } // Set this stream configuration as the new best match. pBestMatch.Init(*pStreamConfig); pBestMatch->AddRef(); // Set the best bit rate. dwBestRate = (pWave->nAvgBytesPerSec * 8); } // else } // if matching limits } // if valid bit rate pWave = NULL; // The subtypes did not match. Clean up for next iteration. if(pMediaTypeBuf != mediaType) { delete [] pMediaTypeBuf; } } // for index // If no match was found, the arguments were not valid for the codec. if(!pBestMatch) { hr = E_INVALIDARG; goto Exit; } // Set the pointer to the stream configuration. *ppStreamConfig = pBestMatch.Release(); pBestMatch = NULL; Exit: return hr; } void PrintMediaType(WM_MEDIA_TYPE* pMediaType) { LPOLESTR pMajorGuid, pMinorGuid, pFormatType; StringFromIID(pMediaType->majortype, &pMajorGuid); StringFromIID(pMediaType->subtype, &pMinorGuid); StringFromIID(pMediaType->formattype, &pFormatType); std::wcout << L"Major Type: " << pMajorGuid << L"\nMinor Type: " << pMinorGuid << L"\nFormat Type: " << pFormatType << L"\nFixed Size Samples: " << pMediaType->bFixedSizeSamples << L"\nSample Size: " << pMediaType->lSampleSize << L'\n'; CoTaskMemFree(pMajorGuid); CoTaskMemFree(pMinorGuid); CoTaskMemFree(pFormatType); if(pMediaType->formattype == WMFORMAT_WaveFormatEx) { WAVEFORMATEX* pwfx = (WAVEFORMATEX*)pMediaType->pbFormat; std::wcout << L"WaveFormatEx:\n" << L"\tChannels: " << pwfx->nChannels << L"\n\tSamples per Second: " << pwfx->nSamplesPerSec << L"\n\tAverage bytes per Second: " << pwfx->nAvgBytesPerSec << L"\n\tBlock Align: " << pwfx->nBlockAlign << L"\n\tBits per Sample: " << pwfx->wBitsPerSample << L"\n\tFormat Tag: " << pwfx->wFormatTag << L'\n'; } std::wcout << L'\n'; } void PrintCodecFormats(REFGUID type, IWMCodecInfo3* pCodec, DWORD codecIndex) { DWORD numFormats = 0; HRESULT hr = pCodec->GetCodecFormatCount(type, codecIndex, &numFormats); printf("\tFound %lu formats\n", numFormats); for(DWORD i = 0; i < numFormats; ++i) { ComType pConfig; WCHAR descBuf[300] = {0}; DWORD descBufSize = ARRAYSIZE(descBuf); hr = pCodec->GetCodecFormatDesc(type, codecIndex, i, &pConfig, descBuf, &descBufSize); DWORD bps = 0, bufWindow = 0; pConfig->GetBitrate(&bps); WCHAR connName[90] = {0}, streamName[90] = {0}; WORD connNameLen = ARRAYSIZE(connName), streamNameLen = ARRAYSIZE(streamName); pConfig->GetConnectionName(connName, &connNameLen); pConfig->GetStreamName(streamName, &streamNameLen); pConfig->GetBufferWindow(&bufWindow); wprintf(L"\t%lu Stream %s, Conn %s, bps %lu, bufWin %lu\n%s\n", i+1, streamName, connName, bps, bufWindow, descBuf); ComType pProps; pConfig->QueryInterface(&pProps); DWORD connNameSize = sizeof(connName); pProps->GetMediaType((WM_MEDIA_TYPE*)connName, &connNameSize); PrintMediaType((WM_MEDIA_TYPE*)connName); } } HRESULT GetCodecNames(IWMCodecInfo3* pCodecInfo) { HRESULT hr = S_OK; DWORD cCodecs = 0; WCHAR* pwszCodecName = NULL; DWORD cchCodecName = 0; // Retrieve the number of supported audio codecs on the system. hr = pCodecInfo->GetCodecInfoCount(WMMEDIATYPE_Audio, &cCodecs); if(SUCCEEDED(hr)) printf("Number of audio codecs: %d\n\n", cCodecs); else { printf("Could not get the count of audio codecs.\n"); return hr; } // Loop through all the audio codecs. for(DWORD dwCodecIndex = 0; dwCodecIndex < cCodecs; dwCodecIndex++) { REFGUID type = WMMEDIATYPE_Audio; // Get the codec name: // First, get the size of the name. hr = pCodecInfo->GetCodecName(type, dwCodecIndex, NULL, &cchCodecName); if(FAILED(hr)) { printf("Could not get the size of the codec name.\n"); return hr; } // Allocate a string of the appropriate size. pwszCodecName = new WCHAR[cchCodecName]; if(pwszCodecName == NULL) { printf("Could not allocate memory.\n"); return E_OUTOFMEMORY; } // Retrieve the codec name. hr = pCodecInfo->GetCodecName(type, dwCodecIndex, pwszCodecName, &cchCodecName); if(FAILED(hr)) { delete[] pwszCodecName; printf("Could not get the codec name.\n"); return hr; } // Print the codec name. printf("%d %S\n", dwCodecIndex, pwszCodecName); // Clean up for the next iteration. delete[] pwszCodecName; pwszCodecName = NULL; cchCodecName = 0; PrintCodecFormats(type, pCodecInfo, dwCodecIndex); } // Retrieve the number of supported video codecs on the system. hr = pCodecInfo->GetCodecInfoCount(WMMEDIATYPE_Video, &cCodecs); if(SUCCEEDED(hr)) printf("\n\nNumber of video codecs: %d.\n\n", cCodecs); else { printf("Could not get the count of video codecs.\n"); return hr; } // Loop through all the video codecs. for(DWORD dwCodecIndex = 0; dwCodecIndex < cCodecs; dwCodecIndex++) { REFGUID type = WMMEDIATYPE_Video; // Get the codec name: // First, get the size of the name. hr = pCodecInfo->GetCodecName(type, dwCodecIndex, NULL, &cchCodecName); if(FAILED(hr)) { printf("Could not get the size of the codec name.\n"); return hr; } // Allocate a string of the appropriate size. pwszCodecName = new WCHAR[cchCodecName]; if(pwszCodecName == NULL) { printf("Could not allocate memory.\n"); return E_OUTOFMEMORY; } // Retrieve the codec name. hr = pCodecInfo->GetCodecName(type, dwCodecIndex, pwszCodecName, &cchCodecName); if(FAILED(hr)) { printf("Could not get the codec name.\n"); return hr; } // Print the codec name. printf("%d %S\n", dwCodecIndex, pwszCodecName); delete[] pwszCodecName; pwszCodecName = NULL; cchCodecName = 0; PrintCodecFormats(type, pCodecInfo, dwCodecIndex); } return S_OK; } void GetCodecNames() { ComType pManager; WMCreateProfileManager(&pManager); ComType pCodecInf; pManager->QueryInterface(&pCodecInf); GetCodecNames(*pCodecInf); } HRESULT CreateAudioCaptureProfile(LPCWSTR pwszName, WAVEFORMATEX* pwfx, DWORD bps, IWMProfile*& pOutProf, IWMCodecInfo3*& pCodec) { ComType pManager; RETURN_ON_FAILED_EXP(WMCreateProfileManager(&pManager)); ComType pCodecTemp; RETURN_ON_FAILED_EXP(pManager->QueryInterface(&pCodecTemp)); ComType pTempProfile; RETURN_ON_FAILED_EXP(pManager->CreateEmptyProfile(WMT_VER_9_0, &pTempProfile)); ComType pStreamConfig; WAVEFORMATEX wfx = *pwfx; wfx.wBitsPerSample = 16; RETURN_ON_FAILED_EXP(FindAudioFormat(WMMEDIASUBTYPE_WMAudioV8, &wfx, bps, FALSE, *pCodecTemp, &pStreamConfig)); pStreamConfig->GetBitrate(&bps); WORD streamNum = 0; HRESULT hr = pStreamConfig->GetStreamNumber(&streamNum); pStreamConfig->SetStreamNumber(1); pStreamConfig->SetStreamName(L"Audio"); pTempProfile->SetName(pwszName); RETURN_ON_FAILED_EXP(pTempProfile->AddStream(*pStreamConfig)); pCodec = pCodecTemp.Release(); pOutProf = pTempProfile.Release(); return S_OK; } HRESULT CreateFileSink(LPCWSTR fileName, IWMProfile* pProfile, IWMWriter*& pWriter) { ComType pWriterTemp; RETURN_ON_FAILED_EXP(WMCreateWriter(NULL, &pWriterTemp)); // set the relevant bits RETURN_ON_FAILED_EXP(pWriterTemp->SetOutputFilename(fileName)); RETURN_ON_FAILED_EXP(pWriterTemp->SetProfile(pProfile)); ComType pAdvancedWriter; RETURN_ON_FAILED_EXP(pWriterTemp->QueryInterface(&pAdvancedWriter)); pAdvancedWriter->SetLiveSource(TRUE); pWriter = pWriterTemp.Release(); return S_OK; } void EnumInProps(IWMWriter* pWriter) { DWORD numInputs = 0; HRESULT hr = pWriter->GetInputCount(&numInputs); for(DWORD i = 0; i < numInputs; ++i) { DWORD numFormats = 0; hr = pWriter->GetInputFormatCount(i, &numFormats); for(DWORD j = 0; j < numFormats; ++j) { IWMInputMediaProps* pProps = NULL; hr = pWriter->GetInputFormat(i, j, &pProps); BYTE buffer[200]; DWORD sizeReq = sizeof(buffer); WM_MEDIA_TYPE* pMediaType = (WM_MEDIA_TYPE*)buffer; pProps->GetMediaType(pMediaType, &sizeReq); hr = pProps->GetMediaType(pMediaType, &sizeReq); std::wcout << L"Input format " << j << ":\n"; PrintMediaType(pMediaType); pProps->Release(); } } } __m128 FindMaxFloatInt(const __m128& thisBatch, __m128& max) { // make temp as thisBatch[1], thisBatch[0], thisBatch[2], thisBatch[3] __m128 temp = _mm_shuffle_ps(thisBatch, thisBatch, _MM_SHUFFLE(2, 3, 1, 0)); // max of thisBatch[0 - 1] __m128 thisIterMax = _mm_max_ss(thisBatch, temp); // make temp thisBatch[2], thisBatch[3] x3 temp = _mm_shuffle_ps(thisIterMax, thisIterMax, _MM_SHUFFLE(1, 0, 0, 0)); // max of thisBatch[0 - 2] thisIterMax = _mm_max_ss(thisIterMax, temp); // make temp consist of thisBatch[3] x4 temp = _mm_shuffle_ps(thisIterMax, thisIterMax, _MM_SHUFFLE(0, 0, 0, 0)); // max[0] = max of 4 floats thisIterMax = _mm_max_ss(thisIterMax, temp); return _mm_max_ss(max, thisIterMax); } __m128 FindMaxFloatInt(float* pFloats, __m128& max) { __m128 thisBatch = _mm_load_ps(pFloats); return FindMaxFloatInt(thisBatch, max); } float FindMaxFloat(float* pFloats, UINT howMany) { UINT howManyOcts = howMany / 8; UINT processedOcts = 0; __m128 max = {0}; while((processedOcts++) < howManyOcts) { // find the max 4 of these 8 floats __m128 firstFour = _mm_load_ps(pFloats); __m128 secondFour = _mm_load_ps(pFloats + 4); __m128 maxFour = _mm_max_ps(firstFour, secondFour); max = FindMaxFloatInt(maxFour, max); pFloats += 8; } if(howMany & 4) { max = FindMaxFloatInt(pFloats, max); pFloats += 4; } _mm_empty(); float maxFloat = max.m128_f32[0]; switch(howMany & 3) { case 3: { float next = *(pFloats++); if(next > maxFloat) maxFloat = next; } break; case 2: { float next = *(pFloats++); if(next > maxFloat) maxFloat = next; } break; case 1: { float next = *(pFloats++); if(next > maxFloat) maxFloat = next; } break; } return maxFloat; } void ConvertTo16Bit(INPUTINFO& sfInf, BYTE* pDest, BYTE* pSrc, UINT numFrames, DWORD& destSize) { float* pFloats = reinterpret_cast(pSrc); UINT numShorts = numFrames * sfInf.channels; UINT numFrames4Units = numShorts / 4; short* pWrite = reinterpret_cast(pDest); UINT frames = 0; float scaleFactor = SHRT_MAX; __m128 scaleFactors = _mm_load1_ps(&scaleFactor); while((frames++) < numFrames4Units) { __m128 procSamples = _mm_load_ps(pFloats); procSamples = _mm_mul_ps(procSamples, scaleFactors); __m64 shorts = _mm_cvtps_pi16(procSamples); memcpy(pWrite, &shorts, sizeof(shorts)); pWrite += 4; pFloats += 4; } _mm_empty(); // and odd bits left over switch(numShorts & 3) { case 3: { float sample = *(pFloats++); *(pWrite++) = static_cast(sample * scaleFactor); } case 2: { float sample = *(pFloats++); *(pWrite++) = static_cast(sample * scaleFactor); } case 1: { float sample = *(pFloats++); *(pWrite++) = static_cast(sample * scaleFactor); } } destSize = static_cast(reinterpret_cast(pWrite) - pDest); } HRESULT RecordAudioStream() { HRESULT hr; REFERENCE_TIME hnsRequestedDuration = REFTIMES_PER_SEC; REFERENCE_TIME hnsActualDuration; UINT32 bufferFrameCount; IMMDeviceEnumerator *pEnumerator = NULL; IMMDevice *pDevice = NULL; IAudioClient *pAudioClient = NULL; IAudioCaptureClient *pCaptureClient = NULL; WAVEFORMATEX *pwfx = NULL; UINT32 packetLength = 0; BOOL bDone = FALSE; 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_INPROC_SERVER, NULL, (void**)&pAudioClient); EXIT_ON_ERROR(hr) hr = pAudioClient->GetMixFormat(&pwfx); EXIT_ON_ERROR(hr) hr = pAudioClient->Initialize( AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_LOOPBACK, hnsRequestedDuration, 0, pwfx, NULL); EXIT_ON_ERROR(hr); IWMProfile* pProfile = NULL; IWMCodecInfo3* pCodec = NULL; CreateAudioCaptureProfile(L"MyProfile", pwfx, 96000, pProfile, pCodec); IWMWriter* pWriter = NULL; CreateFileSink(L"C:\\test\\test.wma", pProfile, pWriter); //EnumInProps(pWriter); IWMInputMediaProps* pProps = NULL; pWriter->GetInputFormat(0, 0, &pProps); pWriter->SetInputProps(0, pProps); pProps->Release(); // 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) // Calculate the actual duration of the allocated buffer. hnsActualDuration = (double)REFTIMES_PER_SEC * bufferFrameCount / pwfx->nSamplesPerSec; // Each loop fills about half of the shared buffer. DWORD loops = 0, samplesPerSecond = pwfx->nSamplesPerSec; UINT64 framesSeen = 0; double timeForOneSample = REFTIMES_PER_SEC / static_cast(samplesPerSecond); DWORD startOfTime = 0; INPUTINFO sfInf = {0}; sfInf.channels = pwfx->nChannels; sfInf.samplerate = pwfx->nSamplesPerSec; hr = pAudioClient->Start(); // Start recording. EXIT_ON_ERROR(hr) hr = pWriter->BeginWriting(); while ((bDone == FALSE) && (loops < 50)) { // Sleep for half the buffer duration. Sleep(hnsActualDuration/REFTIMES_PER_MILLISEC/2); hr = pCaptureClient->GetNextPacketSize(&packetLength); EXIT_ON_ERROR(hr) while (packetLength != 0) { INSSBuffer* pWMBuffer = NULL; hr = pWriter->AllocateSample(packetLength * pwfx->nBlockAlign, &pWMBuffer); // Get the available data in the shared buffer. UINT64 frameStart = 0, timeStart = 0; BYTE *pData; DWORD flags; UINT numFramesAvailable; hr = pCaptureClient->GetBuffer( &pData, &numFramesAvailable, &flags, NULL, NULL); EXIT_ON_ERROR(hr) double frameTime = framesSeen * timeForOneSample; wprintf(L"framesSeen = %I64u, frameTime = %lf\n", framesSeen, frameTime); DWORD dataSize = pwfx->nBlockAlign * numFramesAvailable; BYTE* pWMDataBuffer = NULL; hr = pWMBuffer->GetBuffer(&pWMDataBuffer); if (flags & AUDCLNT_BUFFERFLAGS_SILENT) { // write silence memset(pWMDataBuffer, 0, dataSize); } else { // Convert the data ConvertTo16Bit(sfInf, pWMDataBuffer, pData, numFramesAvailable, dataSize); } pWMBuffer->SetLength(dataSize); if(flags & AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY) { _putws(L""); } hr = pWriter->WriteSample(0, ULONGLONG(frameTime), 0, pWMBuffer); pWMBuffer->Release(); if(FAILED(hr)) { wprintf(L"WriteSample (%#08x)\n", hr); } framesSeen += numFramesAvailable; hr = pCaptureClient->ReleaseBuffer(numFramesAvailable); EXIT_ON_ERROR(hr) hr = pCaptureClient->GetNextPacketSize(&packetLength); EXIT_ON_ERROR(hr) } ++loops; } hr = pWriter->EndWriting(); hr = pAudioClient->Stop(); pWriter->Release(); pProfile->Release(); pCodec->Release(); EXIT_ON_ERROR(hr) Exit: CoTaskMemFree(pwfx); SAFE_RELEASE(pEnumerator) SAFE_RELEASE(pDevice) SAFE_RELEASE(pAudioClient) SAFE_RELEASE(pCaptureClient) return hr; } int __cdecl wmain(int argc, wchar_t** argv) { CoInitialize(NULL); if(argc > 1) { GetCodecNames(); // lists installed WMA codecs and profiles } else { RecordAudioStream(); // saves live audio to C:\\test\\test.wma } CoUninitialize(); return 0; }