static HRESULT Test(int deviceId) { HRESULT hr; WasapiWrap ww; HRR(ww.Init()); HRG(ww.DoDeviceEnumeration()); HRG(ww.ChooseDevice(deviceId)); ww.PrintMixFormat(); WWInspectArg inspectArg; inspectArg.Set(16, 16, 44100, 2); ww.Inspect(inspectArg); inspectArg.Set(16, 16, 48000, 2); ww.Inspect(inspectArg); inspectArg.Set(24, 24, 176400, 2); ww.Inspect(inspectArg); inspectArg.Set(32, 24, 176400, 2); ww.Inspect(inspectArg); end: ww.Unsetup(); ww.Term(); return hr; }
static WWBitsPerSampleType InspectDeviceBitsPerSample(int deviceId) { HRESULT hr; WWBitsPerSampleType deviceBitsPerSample = WWBpsNone; WasapiWrap ww; HRG(ww.Init()); HRG(ww.DoDeviceEnumeration()); HRG(ww.ChooseDevice(deviceId)); WWInspectArg inspectArg; inspectArg.Set(32, 24, 176400, 2); if (SUCCEEDED(ww.Inspect(inspectArg))) { deviceBitsPerSample = WWBps32v24; } inspectArg.Set(24, 24, 176400, 2); if (SUCCEEDED(ww.Inspect(inspectArg))) { deviceBitsPerSample = WWBps24; } end: ww.Unsetup(); ww.Term(); return deviceBitsPerSample; }
static HRESULT DeviceIdStringGet(IMMDeviceCollection *dc, UINT id, wchar_t *deviceIdStr, size_t deviceIdStrBytes) { HRESULT hr = 0; IMMDevice *device = nullptr; LPWSTR s = nullptr; assert(dc); assert(deviceIdStr); assert(0 < deviceIdStrBytes); deviceIdStr[0] = 0; HRG(dc->Item(id, &device)); HRG(device->GetId(&s)); wcsncpy_s(deviceIdStr, deviceIdStrBytes/2, s, deviceIdStrBytes/2 -1); end: CoTaskMemFree(s); s = nullptr; SafeRelease(&device); return hr; }
static HRESULT DeviceNameGet(IMMDeviceCollection *dc, UINT id, wchar_t *name, size_t nameBytes) { HRESULT hr = 0; IMMDevice *device = nullptr; IPropertyStore *ps = nullptr; PROPVARIANT pv; assert(dc); assert(name); assert(0 < nameBytes); name[0] = 0; PropVariantInit(&pv); HRG(dc->Item(id, &device)); HRG(device->OpenPropertyStore(STGM_READ, &ps)); HRG(ps->GetValue(PKEY_Device_FriendlyName, &pv)); wcsncpy_s(name, nameBytes/2, pv.pwszVal, nameBytes/2 -1); end: PropVariantClear(&pv); SafeRelease(&ps); SafeRelease(&device); return hr; }
HRESULT WasapiWrap::DoDeviceEnumeration(void) { HRESULT hr = 0; IMMDeviceEnumerator *deviceEnumerator = nullptr; m_deviceInfo.clear(); HRR(CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&deviceEnumerator))); HRR(deviceEnumerator->EnumAudioEndpoints( eRender, DEVICE_STATE_ACTIVE, &m_deviceCollection)); UINT nDevices = 0; HRG(m_deviceCollection->GetCount(&nDevices)); for (UINT i = 0; i<nDevices; ++i) { wchar_t name[WW_DEVICE_NAME_COUNT]; HRG(DeviceNameGet(m_deviceCollection, i, name, sizeof name)); m_deviceInfo.push_back(WWDeviceInfo(i, name)); } end: SafeRelease(&deviceEnumerator); return hr; }
HRESULT WWMFResampler::GetSampleDataFromMFTransform(WWMFSampleData *sampleData_return) { HRESULT hr = S_OK; IMFMediaBuffer *pBuffer = NULL; MFT_OUTPUT_STREAM_INFO streamInfo; MFT_OUTPUT_DATA_BUFFER outputDataBuffer; DWORD dwStatus; memset(&streamInfo, 0, sizeof streamInfo); memset(&outputDataBuffer, 0, sizeof outputDataBuffer); assert(sampleData_return); assert(NULL == sampleData_return->data); HRG(MFCreateSample(&(outputDataBuffer.pSample))); HRG(MFCreateMemoryBuffer(sampleData_return->bytes, &pBuffer)); HRG(outputDataBuffer.pSample->AddBuffer(pBuffer)); outputDataBuffer.dwStreamID = 0; outputDataBuffer.dwStatus = 0; outputDataBuffer.pEvents = NULL; hr = m_pTransform->ProcessOutput(0, 1, &outputDataBuffer, &dwStatus); if (FAILED(hr)) { goto end; } HRG(ConvertMFSampleToWWSampleData(outputDataBuffer.pSample, sampleData_return)); end: SafeRelease(&pBuffer); SafeRelease(&outputDataBuffer.pSample); return hr; }
void WasapiWrap::PrintMixFormat(void) { HRESULT hr = 0; WAVEFORMATEX *waveFormat = nullptr; assert(m_deviceToUse); assert(!m_audioClient); HRG(m_deviceToUse->Activate( __uuidof(IAudioClient), CLSCTX_INPROC_SERVER, nullptr, (void**)&m_audioClient)); assert(m_audioClient); assert(!waveFormat); HRG(m_audioClient->GetMixFormat(&waveFormat)); assert(waveFormat); WAVEFORMATEXTENSIBLE * wfext = (WAVEFORMATEXTENSIBLE*)waveFormat; printf("WASAPI shared mix format:\n"); WaveFormatDebug(waveFormat); if (22 <= waveFormat->cbSize) { WFExtensibleDebug(wfext); } end: if (waveFormat) { CoTaskMemFree(waveFormat); waveFormat = nullptr; wfext = nullptr; } SafeRelease(&m_audioClient); }
HRESULT WasapiWrap::Start(void) { BYTE *pData = nullptr; HRESULT hr = 0; assert(m_pcmData); assert(!m_shutdownEvent); m_shutdownEvent = CreateEventEx(nullptr, nullptr, 0, EVENT_MODIFY_STATE | SYNCHRONIZE); CHK(m_shutdownEvent); m_renderThread = CreateThread(nullptr, 0, RenderEntry, this, 0, nullptr); assert(m_renderThread); assert(m_renderClient); HRG(m_renderClient->GetBuffer(m_bufferFrameNum, &pData)); memset(pData, 0, m_bufferFrameNum * m_frameBytes); HRG(m_renderClient->ReleaseBuffer(m_bufferFrameNum, 0)); m_footerCount = 0; assert(m_audioClient); HRG(m_audioClient->Start()); end: return hr; }
static HRESULT Run(int deviceId, int latencyMillisec, WWPcmData &pcm) { HRESULT hr; WasapiWrap ww; HRR(ww.Init()); HRG(ww.DoDeviceEnumeration()); HRG(ww.ChooseDevice(deviceId)); WWSetupArg setupArg; setupArg.Set(pcm.bitsPerSample,pcm.validBitsPerSample, pcm.nSamplesPerSec, pcm.nChannels, latencyMillisec); HRG(ww.Setup(setupArg)); ww.SetOutputData(pcm); ww.Start(); while (!ww.Run(1000)) { printf("%d / %d\n", ww.GetPosFrame(), ww.GetTotalFrameNum()); } hr = S_OK; end: ww.Stop(); ww.Unsetup(); ww.Term(); return hr; }
static HRESULT FixWavHeader(FILE *fpw, DWORD writeDataTotalBytes) { HRESULT hr = E_FAIL; fseek(fpw, 4, SEEK_SET); HRG(WriteInt32(fpw, writeDataTotalBytes + 0x24)); fseek(fpw, 0x28, SEEK_SET); HRG(WriteInt32(fpw, writeDataTotalBytes)); end: return hr; }
HRESULT WWMFResampler::Resample(const BYTE *buff, DWORD bytes, WWMFSampleData *sampleData_return) { HRESULT hr = E_FAIL; IMFSample *pSample = NULL; WWMFSampleData tmpData; WWMFSampleData inputData((BYTE*)buff, bytes); DWORD dwStatus; DWORD cbOutputBytes = (DWORD)((int64_t)bytes * m_outputFormat.BytesPerSec() / m_inputFormat.BytesPerSec()); // cbOutputBytes must be product of frambytes cbOutputBytes = (cbOutputBytes + (m_outputFormat.FrameBytes()-1)) / m_outputFormat.FrameBytes() * m_outputFormat.FrameBytes(); // add extra receive size cbOutputBytes += 16 * m_outputFormat.FrameBytes(); assert(sampleData_return); assert(NULL == sampleData_return->data); HRG(ConvertWWSampleDataToMFSample(inputData, &pSample)); HRG(m_pTransform->GetInputStatus(0, &dwStatus)); if ( MFT_INPUT_STATUS_ACCEPT_DATA != dwStatus) { dprintf("E: ApplyTransform() pTransform->GetInputStatus() not accept data.\n"); hr = E_FAIL; goto end; } HRG(m_pTransform->ProcessInput(0, pSample, 0)); // set sampleData_return->bytes = 0 sampleData_return->Forget(); for (;;) { tmpData.bytes = cbOutputBytes; hr = GetSampleDataFromMFTransform(&tmpData); if (MF_E_TRANSFORM_NEED_MORE_INPUT == hr) { hr = S_OK; goto end; } if (FAILED(hr)) { goto end; } sampleData_return->MoveAdd(tmpData); tmpData.Release(); } end: tmpData.Release(); inputData.Forget(); SafeRelease(&pSample); return hr; }
static HRESULT PrintDeviceList(void) { HRESULT hr; WasapiWrap ww; HRR(ww.Init()); HRG(ww.DoDeviceEnumeration()); printf("Device list:\n"); for (int i=0; i<ww.GetDeviceCount(); ++i) { wchar_t namew[WW_DEVICE_NAME_COUNT]; ww.GetDeviceName(i, namew, sizeof namew); char namec[WW_DEVICE_NAME_COUNT]; memset(namec, 0, sizeof namec); WideCharToMultiByte(CP_ACP, 0, namew, -1, namec, sizeof namec-1, NULL, NULL); printf(" deviceId=%d: %s\n", i, namec); } printf("\n"); end: ww.Term(); return hr; }
DWORD WasapiWrap::RenderMain(void) { bool stillPlaying = true; HANDLE waitArray[2] = { m_shutdownEvent, m_audioSamplesReadyEvent }; DWORD waitResult; HRESULT hr = 0; HRG(CoInitializeEx(nullptr, COINIT_MULTITHREADED)); while (stillPlaying) { waitResult = WaitForMultipleObjects(2, waitArray, FALSE, INFINITE); switch (waitResult) { case WAIT_OBJECT_0 + 0: // m_shutdownEvent stillPlaying = false; break; case WAIT_OBJECT_0 + 1: // m_audioSamplesReadyEvent stillPlaying = AudioSamplesReadyProc(); break; } } end: CoUninitialize(); return hr; }
HRESULT WWMFResampler::ConvertMFSampleToWWSampleData(IMFSample *pSample, WWMFSampleData *sampleData_return) { HRESULT hr = E_FAIL; #ifdef USE_ATL CComPtr<IMFMediaBuffer> spBuffer; #else // USE_ATL IMFMediaBuffer *spBuffer = NULL; #endif // USE_ATL BYTE *pByteBuffer = NULL; assert(pSample); DWORD cbBytes = 0; assert(sampleData_return); assert(NULL == sampleData_return->data); HRG(pSample->ConvertToContiguousBuffer(&spBuffer)); HRG(spBuffer->GetCurrentLength(&cbBytes)); if (0 == cbBytes) { sampleData_return->bytes = 0; sampleData_return->data = NULL; hr = S_OK; goto end; } HRG(spBuffer->Lock(&pByteBuffer, NULL, NULL)); assert(NULL == sampleData_return->data); sampleData_return->data = new BYTE[cbBytes]; if (NULL == sampleData_return->data) { printf("no memory\n"); goto end; } memcpy(sampleData_return->data, pByteBuffer, cbBytes); sampleData_return->bytes = cbBytes; m_outputFrameTotal += cbBytes / m_outputFormat.FrameBytes(); pByteBuffer = NULL; HRG(spBuffer->Unlock()); end: #ifndef USE_ATL SafeRelease(&spBuffer); #endif // USE_ATL return hr; }
static HRESULT CreateResamplerMFT( const WWMFPcmFormat &fmtIn, const WWMFPcmFormat &fmtOut, int halfFilterLength, IMFTransform **ppTransform) { HRESULT hr = S_OK; #ifdef USE_ATL CComPtr<IMFMediaType> spInputType; CComPtr<IMFMediaType> spOutputType; CComPtr<IUnknown> spTransformUnk; CComPtr<IWMResamplerProps> spResamplerProps; #else // USE_ATL IMFMediaType *spInputType = NULL; IMFMediaType *spOutputType = NULL; IUnknown *spTransformUnk = NULL; IWMResamplerProps *spResamplerProps = NULL; #endif // USE_ATL IMFTransform *pTransform = NULL; assert(ppTransform); *ppTransform = NULL; HRG(CoCreateInstance(CLSID_CResamplerMediaObject, NULL, CLSCTX_INPROC_SERVER, IID_IUnknown, (void**)&spTransformUnk)); HRG(spTransformUnk->QueryInterface(IID_PPV_ARGS(&pTransform))); HRG(CreateAudioMediaType(fmtIn, &spInputType)); HRG(pTransform->SetInputType(0, spInputType, 0)); HRG(CreateAudioMediaType(fmtOut, &spOutputType)); HRG(pTransform->SetOutputType(0, spOutputType, 0)); HRG(spTransformUnk->QueryInterface(IID_PPV_ARGS(&spResamplerProps))); HRG(spResamplerProps->SetHalfFilterLength(halfFilterLength)); *ppTransform = pTransform; pTransform = NULL; //< prevent release end: SafeRelease(&pTransform); #ifndef USE_ATL SafeRelease(&spInputType); SafeRelease(&spOutputType); SafeRelease(&spTransformUnk); SafeRelease(&spResamplerProps); #endif // USE_ATL return hr; }
HRESULT WWAudioDeviceEnumerator::BuildDeviceList(WWDeviceType t) { HRESULT hr = 0; switch (t) { case WWDTPlay: m_dataFlow = eRender; break; case WWDTRec: m_dataFlow = eCapture; break; default: assert(0); return E_FAIL; } HRR(CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&m_deviceEnumerator))); if (m_deviceEnumerator) { for (auto it = m_notificationClientList.begin(); it != m_notificationClientList.end(); ++it) { m_deviceEnumerator->RegisterEndpointNotificationCallback(*it); } } HRR(m_deviceEnumerator->EnumAudioEndpoints(m_dataFlow, DEVICE_STATE_ACTIVE, &m_deviceCollection)); UINT nDevices = 0; HRG(m_deviceCollection->GetCount(&nDevices)); for (UINT i=0; i<nDevices; ++i) { wchar_t name[WW_DEVICE_NAME_COUNT]; wchar_t idStr[WW_DEVICE_IDSTR_COUNT]; HRG(DeviceNameGet(m_deviceCollection, i, name, sizeof name)); HRG(DeviceIdStringGet(m_deviceCollection, i, idStr, sizeof idStr)); m_deviceInfo.push_back(WWDeviceInfo(i, name, idStr)); } end: return hr; }
HRESULT WWMFResampler::Initialize(const WWMFPcmFormat &inputFormat, const WWMFPcmFormat &outputFormat, int halfFilterLength) { HRESULT hr = S_OK; m_inputFormat = inputFormat; m_outputFormat = outputFormat; assert(m_pTransform == NULL); m_inputFrameTotal = 0; m_outputFrameTotal = 0; HRG(MFStartup(MF_VERSION, MFSTARTUP_NOSOCKET)); m_isMFStartuped = true; HRG(CreateResamplerMFT(m_inputFormat, m_outputFormat, halfFilterLength, &m_pTransform)); HRG(m_pTransform->ProcessMessage(MFT_MESSAGE_COMMAND_FLUSH, NULL)); HRG(m_pTransform->ProcessMessage(MFT_MESSAGE_NOTIFY_BEGIN_STREAMING, NULL)); HRG(m_pTransform->ProcessMessage(MFT_MESSAGE_NOTIFY_START_OF_STREAM, NULL)); end: return hr; }
HRESULT WWMFResampler::Drain(DWORD resampleInputBytes, WWMFSampleData *sampleData_return) { HRESULT hr = S_OK; WWMFSampleData tmpData; DWORD cbOutputBytes = (DWORD)((int64_t)resampleInputBytes * m_outputFormat.BytesPerSec() / m_inputFormat.BytesPerSec()); // cbOutputBytes must be product of frambytes cbOutputBytes = (cbOutputBytes + (m_outputFormat.FrameBytes()-1)) / m_outputFormat.FrameBytes() * m_outputFormat.FrameBytes(); assert(sampleData_return); assert(NULL == sampleData_return->data); HRG(m_pTransform->ProcessMessage(MFT_MESSAGE_NOTIFY_END_OF_STREAM, NULL)); HRG(m_pTransform->ProcessMessage(MFT_MESSAGE_COMMAND_DRAIN, NULL)); // set sampleData_return->bytes = 0 sampleData_return->Forget(); for (;;) { tmpData.bytes = cbOutputBytes; hr = GetSampleDataFromMFTransform(&tmpData); if (MF_E_TRANSFORM_NEED_MORE_INPUT == hr) { // end HRG(m_pTransform->ProcessMessage(MFT_MESSAGE_NOTIFY_END_STREAMING, NULL)); goto end; } if (FAILED(hr)) { goto end; } sampleData_return->MoveAdd(tmpData); tmpData.Release(); } end: tmpData.Release(); return hr; }
HRESULT WWMFResampler::ConvertWWSampleDataToMFSample(WWMFSampleData &sampleData, IMFSample **ppSample) { HRESULT hr = S_OK; IMFSample *pSample = NULL; #ifdef USE_ATL CComPtr<IMFMediaBuffer> spBuffer; #else // USE_ATL IMFMediaBuffer *spBuffer = NULL; #endif // USE_ATL BYTE *pByteBufferTo = NULL; //LONGLONG hnsSampleDuration; //LONGLONG hnsSampleTime; int frameCount; assert(ppSample); *ppSample = NULL; HRG(MFCreateMemoryBuffer(sampleData.bytes, &spBuffer)); HRG(spBuffer->Lock(&pByteBufferTo, NULL, NULL)); memcpy(pByteBufferTo, sampleData.data, sampleData.bytes); pByteBufferTo = NULL; HRG(spBuffer->Unlock()); HRG(spBuffer->SetCurrentLength(sampleData.bytes)); HRG(MFCreateSample(&pSample)); HRG(pSample->AddBuffer(spBuffer)); frameCount = sampleData.bytes / m_inputFormat.FrameBytes(); /* hnsSampleDuration = (LONGLONG)(10.0 * 1000 * 1000 * frameCount / m_inputFormat.sampleRate); hnsSampleTime = (LONGLONG)(10.0 * 1000 * 1000 * m_inputFrameTotal / m_inputFormat.sampleRate); HRG(pSample->SetSampleDuration(hnsSampleDuration)); HRG(pSample->SetSampleTime(hnsSampleTime)); */ m_inputFrameTotal += frameCount; // succeeded. *ppSample = pSample; pSample = NULL; //< prevent release end: SafeRelease(&pSample); #ifndef USE_ATL SafeRelease(&spBuffer); #endif // uSE_ATL return hr; }
HRESULT WasapiWrap::ChooseDevice(int id) { HRESULT hr = 0; if (id < 0) { goto end; } assert(m_deviceCollection); assert(!m_deviceToUse); HRG(m_deviceCollection->Item(id, &m_deviceToUse)); end: SafeRelease(&m_deviceCollection); return hr; }
IMMDevice * WWAudioDeviceEnumerator::GetDevice(int id) { HRESULT hr = 0; IMMDevice *device = nullptr; if (id < 0 || (int)m_deviceInfo.size() <= id) { return nullptr; } if (!m_deviceCollection) { return nullptr; } HRG(m_deviceCollection->Item(id, &device)); end: return device; }
HRESULT WasapiWrap::Setup(const WWSetupArg & arg) { HRESULT hr = 0; WAVEFORMATEX *waveFormat = nullptr; m_sampleRate = arg.nSamplesPerSec; m_bitsPerSample = arg.bitsPerSample; m_validBitsPerSample = arg.validBitsPerSample; m_audioSamplesReadyEvent = CreateEventEx(nullptr, nullptr, 0, EVENT_MODIFY_STATE | SYNCHRONIZE); CHK(m_audioSamplesReadyEvent); assert(m_deviceToUse); assert(!m_audioClient); HRG(m_deviceToUse->Activate( __uuidof(IAudioClient), CLSCTX_INPROC_SERVER, nullptr, (void**)&m_audioClient)); assert(m_audioClient); assert(!waveFormat); HRG(m_audioClient->GetMixFormat(&waveFormat)); assert(waveFormat); WAVEFORMATEXTENSIBLE * wfex = (WAVEFORMATEXTENSIBLE*)waveFormat; printf("original Mix Format:\n"); WaveFormatDebug(waveFormat); WFExtensibleDebug(wfex); if (waveFormat->wFormatTag != WAVE_FORMAT_EXTENSIBLE) { printf("E: unsupported device ! mixformat == 0x%08x\n", waveFormat->wFormatTag); hr = E_FAIL; goto end; } wfex->SubFormat = KSDATAFORMAT_SUBTYPE_PCM; wfex->Format.wBitsPerSample = m_bitsPerSample; wfex->Format.nSamplesPerSec = arg.nSamplesPerSec; wfex->Format.nBlockAlign = (m_bitsPerSample / 8) * waveFormat->nChannels; wfex->Format.nAvgBytesPerSec = wfex->Format.nSamplesPerSec*wfex->Format.nBlockAlign; wfex->Samples.wValidBitsPerSample = m_validBitsPerSample; printf("preferred Format:\n"); WaveFormatDebug(waveFormat); WFExtensibleDebug(wfex); HRG(m_audioClient->IsFormatSupported(AUDCLNT_SHAREMODE_EXCLUSIVE, waveFormat, nullptr)); m_frameBytes = waveFormat->nBlockAlign; REFERENCE_TIME bufferDuration = arg.latencyInMillisec * 10000; hr = m_audioClient->Initialize( AUDCLNT_SHAREMODE_EXCLUSIVE, AUDCLNT_STREAMFLAGS_EVENTCALLBACK, bufferDuration, bufferDuration, waveFormat, nullptr); if (hr == AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED) { HRG(m_audioClient->GetBufferSize(&m_bufferFrameNum)); SafeRelease(&m_audioClient); bufferDuration = (REFERENCE_TIME)( 10000.0 * // (REFERENCE_TIME(100ns) / ms) * 1000 * // (ms / s) * m_bufferFrameNum / // frames / waveFormat->nSamplesPerSec + // (frames / s) 0.5); HRG(m_deviceToUse->Activate( __uuidof(IAudioClient), CLSCTX_INPROC_SERVER, nullptr, (void**)&m_audioClient)); hr = m_audioClient->Initialize( AUDCLNT_SHAREMODE_EXCLUSIVE, AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_NOPERSIST | AUDCLNT_STREAMFLAGS_RATEADJUST, bufferDuration, bufferDuration, waveFormat, nullptr); } if (FAILED(hr)) { printf("E: audioClient->Initialize failed 0x%08x\n", hr); goto end; } HRG(m_audioClient->GetBufferSize(&m_bufferFrameNum)); HRG(m_audioClient->SetEventHandle(m_audioSamplesReadyEvent)); HRG(m_audioClient->GetService(IID_PPV_ARGS(&m_renderClient))); end: if (waveFormat) { CoTaskMemFree(waveFormat); waveFormat = nullptr; } return hr; }
int WasapiWrap::Inspect(const WWInspectArg & arg) { HRESULT hr = 0; WAVEFORMATEXTENSIBLE wfext; LPCWAVEFORMATEX pWfex = (LPCWAVEFORMATEX)&wfext; assert(m_deviceToUse); assert(!m_audioClient); HRG(m_deviceToUse->Activate( __uuidof(IAudioClient), CLSCTX_INPROC_SERVER, nullptr, (void**)&m_audioClient)); assert(m_audioClient); hr = m_deviceToUse->Activate(__uuidof(IAudioClient2), CLSCTX_INPROC_SERVER, nullptr, (void**)&m_audioClient2); if (FAILED(hr)) { m_audioClient2 = nullptr; } ZeroMemory(&wfext, sizeof wfext); wfext.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; wfext.Format.nChannels = arg.nChannels; wfext.Format.nSamplesPerSec = arg.nSamplesPerSec; wfext.Format.nBlockAlign = (arg.bitsPerSample / 8) * arg.nChannels; wfext.Format.nAvgBytesPerSec = arg.nSamplesPerSec * wfext.Format.nBlockAlign; wfext.Format.wBitsPerSample = arg.bitsPerSample; wfext.Format.cbSize = 22; wfext.Samples.wValidBitsPerSample = arg.validBitsPerSample; wfext.dwChannelMask = 3; wfext.SubFormat = KSDATAFORMAT_SUBTYPE_PCM; printf("WAVEFORMATEXTENSIBLE KSFORMAT_SUBTYPE_PCM %dHz %dbit %dch: ", arg.nSamplesPerSec, arg.bitsPerSample, arg.nChannels); hr = m_audioClient->IsFormatSupported(AUDCLNT_SHAREMODE_EXCLUSIVE, pWfex, nullptr); if (AUDCLNT_E_UNSUPPORTED_FORMAT == hr) { printf("not supported.\n"); goto end; } if (FAILED(hr)) { printf("IAudioClient::IsFormatSupported failed: hr = 0x%08x.\n", hr); goto end; } printf("supported.\n"); if (m_audioClient2 != nullptr) { for (int i = 0; i < AUDIO_STREAM_CATEGORY_NUM; ++i) { BOOL offloadCapable; HRG(m_audioClient2->IsOffloadCapable((AUDIO_STREAM_CATEGORY)i, &offloadCapable)); printf("IsOffloadCapable(%22s)=%d\n", s_audioStreamCategoryStr[i], offloadCapable); } REFERENCE_TIME eventMinBufferDuration; REFERENCE_TIME eventMaxBufferDuration; REFERENCE_TIME timerMinBufferDuration; REFERENCE_TIME timerMaxBufferDuration; HRG(m_audioClient2->GetBufferSizeLimits(pWfex, true, &eventMinBufferDuration, &eventMaxBufferDuration)); HRG(m_audioClient2->GetBufferSizeLimits(pWfex, false, &timerMinBufferDuration, &timerMaxBufferDuration)); printf("Buffer size: eventMin=%lldms eventMax=%lldms timerMin=%lldms timerMax=%lldms\n", eventMinBufferDuration / 10000, eventMaxBufferDuration / 10000, timerMinBufferDuration / 10000, timerMaxBufferDuration / 10000); } end: SafeRelease(&m_audioClient2); SafeRelease(&m_audioClient); return hr; }
HRESULT WWPlayPcmGroup::DoResample(WWPcmFormat &targetFmt, int conversionQuality) { HRESULT hr = S_OK; WWMFResampler resampler; size_t n = m_playPcmDataList.size(); const int PROCESS_FRAMES = 128 * 1024; BYTE *buff = new BYTE[PROCESS_FRAMES * m_pcmFormat.BytesPerFrame()]; std::list<size_t> toPcmDataIdxList; size_t numConvertedPcmData = 0; assert(1 <= conversionQuality && conversionQuality <= 60); if (nullptr == buff) { hr = E_OUTOFMEMORY; goto end; } // 共有モードのサンプルレート変更。 HRG(resampler.Initialize( WWMFPcmFormat( (WWMFBitFormatType)WWPcmDataSampleFormatTypeIsFloat(m_pcmFormat.sampleFormat), (WORD)m_pcmFormat.numChannels, (WORD)WWPcmDataSampleFormatTypeToBitsPerSample(m_pcmFormat.sampleFormat), m_pcmFormat.sampleRate, 0, //< TODO: target dwChannelMask (WORD)WWPcmDataSampleFormatTypeToValidBitsPerSample(m_pcmFormat.sampleFormat)), WWMFPcmFormat( WWMFBitFormatFloat, (WORD)targetFmt.numChannels, 32, targetFmt.sampleRate, 0, //< TODO: target dwChannelMask 32), conversionQuality)); for (size_t i=0; i<n; ++i) { WWPcmData *pFrom = &m_playPcmDataList[i]; WWPcmData pcmDataTo; if (!pcmDataTo.Init(pFrom->id, targetFmt.sampleFormat, targetFmt.numChannels, (int64_t)(((double)targetFmt.sampleRate / m_pcmFormat.sampleRate) * pFrom->nFrames), targetFmt.numChannels * WWPcmDataSampleFormatTypeToBitsPerSample(targetFmt.sampleFormat)/8, WWPcmDataContentMusicData, m_pcmFormat.streamType)) { dprintf("E: %s malloc failed. pcm id=%d\n", __FUNCTION__, pFrom->id); hr = E_OUTOFMEMORY; goto end; } m_playPcmDataList.push_back(pcmDataTo); pFrom = &m_playPcmDataList[i]; toPcmDataIdxList.push_back(n+i); dprintf("D: pFrom stream=%p nFrames=%lld\n", pFrom->stream, pFrom->nFrames); for (size_t posFrames=0; ; posFrames += PROCESS_FRAMES) { WWMFSampleData mfSampleData; DWORD consumedBytes = 0; int buffBytes = pFrom->GetBufferData(posFrames * m_pcmFormat.BytesPerFrame(), PROCESS_FRAMES * m_pcmFormat.BytesPerFrame(), buff); dprintf("D: pFrom->GetBufferData posBytes=%Iu bytes=%d rv=%d\n", posFrames * m_pcmFormat.BytesPerFrame(), PROCESS_FRAMES * m_pcmFormat.BytesPerFrame(), buffBytes); if (0 == buffBytes) { break; } HRG(resampler.Resample(buff, buffBytes, &mfSampleData)); dprintf("D: resampler.Resample mfSampleData.bytes=%u\n", mfSampleData.bytes); consumedBytes = 0; while (0 < toPcmDataIdxList.size() && consumedBytes < mfSampleData.bytes) { size_t toIdx = toPcmDataIdxList.front(); WWPcmData *pTo = &m_playPcmDataList[toIdx]; assert(pTo); int rv = pTo->FillBufferAddData(&mfSampleData.data[consumedBytes], mfSampleData.bytes - consumedBytes); dprintf("D: consumedBytes=%d/%d FillBufferAddData() pTo->stream=%p pTo->nFrames=%lld rv=%d\n", consumedBytes, mfSampleData.bytes, pTo->stream, pTo->nFrames, rv); consumedBytes += rv; if (0 == rv) { pTo->FillBufferEnd(); ++numConvertedPcmData; toPcmDataIdxList.pop_front(); } } mfSampleData.Release(); } pFrom->Term(); } { WWMFSampleData mfSampleData; DWORD consumedBytes = 0; HRG(resampler.Drain(PROCESS_FRAMES * m_pcmFormat.BytesPerFrame(), &mfSampleData)); consumedBytes = 0; while (0 < toPcmDataIdxList.size() && consumedBytes < mfSampleData.bytes) { size_t toIdx = toPcmDataIdxList.front(); WWPcmData *pTo = &m_playPcmDataList[toIdx]; assert(pTo); int rv = pTo->FillBufferAddData(&mfSampleData.data[consumedBytes], mfSampleData.bytes - consumedBytes); consumedBytes += rv; if (0 == rv) { pTo->FillBufferEnd(); ++numConvertedPcmData; toPcmDataIdxList.pop_front(); } } mfSampleData.Release(); } while (0 < toPcmDataIdxList.size()) { size_t toIdx = toPcmDataIdxList.front(); WWPcmData *pTo = &m_playPcmDataList[toIdx]; assert(pTo); pTo->FillBufferEnd(); if (0 == pTo->nFrames) { hr = E_FAIL; goto end; } ++numConvertedPcmData; toPcmDataIdxList.pop_front(); } assert(n == numConvertedPcmData); for (size_t i=0; i<n; ++i) { m_playPcmDataList[i] = m_playPcmDataList[n+i]; m_playPcmDataList[n+i].Forget(); } m_playPcmDataList.resize(numConvertedPcmData); // update pcm format info m_pcmFormat.sampleFormat = targetFmt.sampleFormat; m_pcmFormat.sampleRate = targetFmt.sampleRate; m_pcmFormat.numChannels = targetFmt.numChannels; m_pcmFormat.dwChannelMask = targetFmt.dwChannelMask; // reduce volume level when out of range sample value is found { float maxV = 0.0f; float minV = 0.0f; const float SAMPLE_VALUE_MAX_FLOAT = 1.0f; const float SAMPLE_VALUE_MIN_FLOAT = -1.0f; for (size_t i=0; i<n; ++i) { float currentMax = 0.0f; float currentMin = 0.0f; m_playPcmDataList[i].FindSampleValueMinMax(¤tMin, ¤tMax); if (currentMin < minV) { minV = currentMin; } if (maxV < currentMax) { maxV = currentMax; } } float scale = 1.0f; if (SAMPLE_VALUE_MAX_FLOAT < maxV) { scale = SAMPLE_VALUE_MAX_FLOAT / maxV; } if (minV < SAMPLE_VALUE_MIN_FLOAT && SAMPLE_VALUE_MIN_FLOAT / minV < scale) { scale = SAMPLE_VALUE_MIN_FLOAT / minV; } if (scale < 1.0f) { for (size_t i=0; i<n; ++i) { m_playPcmDataList[i].ScaleSampleValue(scale); } } } end: resampler.Finalize(); delete [] buff; buff = nullptr; return hr; }
int main(int argc, char *argv[]) { HRESULT hr; if (argc != 2) { printf("Usage: %s readWavFile\n", argv[0]); return 1; } FILE *fpr = fopen(argv[1], "rb"); if (NULL == fpr) { printf("read error %s\n", argv[1]); return 1; } WWMFPcmFormat fmt; DWORD remainBytes = 0; unsigned char *pUC = NULL; unsigned short *pUS = NULL; int readBytes = 0; int prevValue = INT_MAX; int prevPosition = INT_MAX; int *histogram16 = NULL; int *histogram24 = NULL; HRG(ReadWavHeader(fpr, &fmt, &remainBytes)); pUC = new unsigned char[remainBytes]; pUS = (unsigned short *)pUC; readBytes = fread(pUC, 1, remainBytes, fpr); if (readBytes != remainBytes) { printf("read error\n"); hr = E_FAIL; goto end; } int totalSamples = readBytes/(fmt.bits/8); switch (fmt.bits) { case 16: histogram16 = new int[65536]; memset(histogram16, 0, 65536*4); for (int i=0; i<remainBytes/2; ++i) { ++histogram16[pUS[i]]; } prevPosition = 32767; for (int i=32768; i<65536; ++i) { if (prevValue == histogram16[i]) { continue; } if (prevPosition != i-1) { printf("%d %d %f\n", i-1-65536, prevValue, (double)prevValue / totalSamples); } prevValue = histogram16[i]; prevPosition = i; printf("%d %d %f\n", i-65536, histogram16[i], (double)histogram16[i] / totalSamples); } prevPosition -= 65536; for (int i=0; i<32768; ++i) { if (prevValue == histogram16[i]) { continue; } if (prevPosition != i-1) { printf("%d %d %f\n", i-1, prevValue, (double)prevValue / totalSamples); } prevValue = histogram16[i]; prevPosition = i; printf("%d %d %f\n", i, histogram16[i], (double)histogram16[i] / totalSamples); } break; case 24: histogram24 = new int[16777216]; memset(histogram24, 0, 16777216*4); for (int i=0; i<remainBytes/3; ++i) { unsigned int v = pUC[i*3] + 256*pUC[i*3+1] + 65536*pUC[i*3+2]; ++histogram24[v]; } prevPosition = 8388607; for (int i=8388608; i<16777216; ++i) { if (abs(prevValue - histogram24[i]) < 1) { continue; } if (prevPosition != i-1) { printf("%12d %12d %f\n", i-1-16777216, prevValue, (double)prevValue / totalSamples); } prevValue = histogram24[i]; prevPosition = i; printf("%12d %12d %f\n", i-16777216, histogram24[i], (double)histogram24[i] / totalSamples); } for (int i=0; i<8388608; ++i) { if (abs(prevValue - histogram24[i]) < 1) { continue; } if (prevPosition != i-1) { printf("%12d %12d %f\n", i-1, prevValue, (double)prevValue / totalSamples); } prevValue = histogram24[i]; prevPosition = i; printf("%12d %12d %f\n", i, histogram24[i], (double)histogram24[i] / totalSamples); } break; default: printf("fmt.bits is not 16 nor 24\n"); break; } end: fclose(fpr); fpr = NULL; delete [] histogram16; histogram16 = NULL; delete [] histogram24; histogram24 = NULL; delete [] pUC; pUC = NULL; pUS = NULL; return hr == S_OK ? 0 : 1; }
int wmain(int argc, wchar_t *argv[]) { // _CrtSetBreakAlloc(35); // COM leak cannot be detected by debug heap manager ... _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF); HRESULT hr = S_OK; bool bCoInitialize = false; FILE *fpr = NULL; FILE *fpw = NULL; errno_t ercd; BYTE *buff = NULL; DWORD buffBytes = 0; DWORD readBytes = 0; DWORD remainBytes = 0; DWORD expectedOutputDataBytes = 0; DWORD result = 0; DWORD writeDataTotalBytes = 0; int conversionQuality = 60; WWMFResampler resampler; WWMFPcmFormat inputFormat; WWMFPcmFormat outputFormat; WWMFSampleData sampleData; if (argc != 6) { PrintUsage(argv[0]); return 1; } HRG(CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE)); bCoInitialize = true; ercd = _wfopen_s(&fpr, argv[1], L"rb"); if (0 != ercd) { printf("file open error %S\n", argv[1]); PrintUsage(argv[0]); hr = E_FAIL; goto end; } ercd = _wfopen_s(&fpw, argv[2], L"wb"); if (0 != ercd) { printf("file open error %S\n", argv[2]); PrintUsage(argv[0]); hr = E_FAIL; goto end; } HRG(ReadWavHeader(fpr, &inputFormat, &remainBytes)); outputFormat = inputFormat; outputFormat.sampleRate = _wtoi(argv[3]); outputFormat.bits = (short)_wtoi(argv[4]); conversionQuality = _wtoi(argv[5]); if (0 == outputFormat.sampleRate || 0 == conversionQuality) { PrintUsage(argv[0]); hr = E_FAIL; goto end; } outputFormat.validBitsPerSample = outputFormat.bits; switch (outputFormat.bits) { case 16: case 24: outputFormat.sampleFormat = WWMFBitFormatInt; break; case 32: outputFormat.sampleFormat = WWMFBitFormatFloat; break; default: PrintUsage(argv[0]); hr = E_FAIL; goto end; } expectedOutputDataBytes = (int64_t)remainBytes * outputFormat.BytesPerSec() / inputFormat .BytesPerSec(); HRG(WriteWavHeader(fpw, outputFormat, expectedOutputDataBytes)); HRG(resampler.Initialize(inputFormat, outputFormat, conversionQuality)); buffBytes = 128 * 1024 * inputFormat.FrameBytes(); buff = new BYTE[buffBytes]; for (;;) { // read PCM data from file readBytes = buffBytes; if (remainBytes < readBytes) { readBytes = remainBytes; } remainBytes -= readBytes; result = fread(buff, 1, readBytes, fpr); if (result != readBytes) { printf("file read error\n"); hr = E_FAIL; goto end; } // convert HRG(resampler.Resample(buff, readBytes, &sampleData)); // write to file result = fwrite(sampleData.data, 1, sampleData.bytes, fpw); if (result != sampleData.bytes) { printf("file write error\n"); hr = E_FAIL; goto end; } writeDataTotalBytes += sampleData.bytes; sampleData.Release(); if (remainBytes == 0) { // end HRG(resampler.Drain(buffBytes, &sampleData)); // write remaining PCM data to file result = fwrite(sampleData.data, 1, sampleData.bytes, fpw); if (result != sampleData.bytes) { printf("file write error\n"); hr = E_FAIL; goto end; } writeDataTotalBytes += sampleData.bytes; sampleData.Release(); break; } } // data chunk align is 2 bytes if (writeDataTotalBytes & 1) { if (0 != fputc(0, fpw)) { printf("file write error\n"); hr = E_FAIL; goto end; } ++writeDataTotalBytes; } HRG(FixWavHeader(fpw, writeDataTotalBytes)); hr = S_OK; end: resampler.Finalize(); if (bCoInitialize) { CoUninitialize(); bCoInitialize = false; } delete[] buff; buff = NULL; if (fpw != NULL) { fclose(fpw); fpw = NULL; } if (fpr != NULL) { fclose(fpr); fpr = NULL; } return SUCCEEDED(hr) ? 0 : 1; }
static HRESULT WriteWavHeader(FILE *fpw, WWMFPcmFormat &format, DWORD dataBytes) { HRESULT hr = E_FAIL; int dataChunkSize = ((dataBytes+1)&(~1)) + 4; HRG(WriteBytes(fpw, "RIFF", 4U)); HRG(WriteInt32(fpw, dataChunkSize + 0x24)); HRG(WriteBytes(fpw, "WAVE", 4U)); HRG(WriteBytes(fpw, "fmt ", 4U)); HRG(WriteInt32(fpw, 16)); // fmt audioFormat size==2 1==int 3==float switch (format.sampleFormat) { case WWMFBitFormatInt: HRG(WriteInt16(fpw, 1)); break; case WWMFBitFormatFloat: HRG(WriteInt16(fpw, 3)); break; default: goto end; } // fmt numChannels size==2 HRG(WriteInt16(fpw, format.nChannels)); // fmt sampleRate size==4 HRG(WriteInt32(fpw, format.sampleRate)); // fmt byteRate size==4 HRG(WriteInt32(fpw, format.BytesPerSec())); // fmt blockAlign size==2 HRG(WriteInt16(fpw, format.FrameBytes())); // fmt bitspersample size==2 HRG(WriteInt16(fpw, format.bits)); HRG(WriteBytes(fpw, "data", 4U)); HRG(WriteInt32(fpw, dataChunkSize)); end: return hr; }
static HRESULT ReadWavHeader(FILE *fpr, WWMFPcmFormat *format_return, DWORD *dataBytes_return) { HRESULT hr = E_FAIL; BYTE buff[16]; int chunkBytes = 0; int fmtChunkSize = 0; short shortValue; int intValue; HRG(ReadBytes(fpr, 12U, buff)); if (0 != memcmp(buff, "RIFF", 4)) { printf("file is not riff wave file\n"); goto end; } if (0 != memcmp(&buff[8], "WAVE", 4)) { printf("file is not riff wave file\n"); goto end; } for (;;) { HRG(ReadBytes(fpr, 4U, buff)); if (0 == memcmp(buff, "fmt ", 4)) { // fmt chunk // chunkSize size==4 HRG(ReadInt32(fpr, &fmtChunkSize)); if (16 != fmtChunkSize && 18 != fmtChunkSize && 40 != fmtChunkSize) { printf("unrecognized format"); goto end; } // audioFormat size==2 HRG(ReadInt16(fpr, &shortValue)); if (1 == shortValue) { format_return->sampleFormat = WWMFBitFormatInt; } else if (3 == shortValue) { format_return->sampleFormat = WWMFBitFormatFloat; } else if (0xfffe == (unsigned short)shortValue) { // SampleFormat is written on WAVEFORMATEXTENSIBLE format_return->sampleFormat = WWMFBitFormatUnknown; } else { printf("unrecognized format"); goto end; } // numChannels size==2 HRG(ReadInt16(fpr, &shortValue)); format_return->nChannels = shortValue; // sampleRate size==4 HRG(ReadInt32(fpr, &intValue)); format_return->sampleRate = intValue; // byteRate size==4 HRG(ReadInt32(fpr, &intValue)); // blockAlign size==2 HRG(ReadInt16(fpr, &shortValue)); // bitspersample size==2 HRG(ReadInt16(fpr, &shortValue)); format_return->bits = shortValue; format_return->validBitsPerSample = shortValue; if (16 < fmtChunkSize) { // subchunksize HRG(ReadInt16(fpr, &shortValue)); if (0 == shortValue) { hr = S_OK; goto end; } else if (22 == shortValue) { // validbitspersample HRG(ReadInt16(fpr, &shortValue)); format_return->validBitsPerSample = shortValue; // dwChannelMask HRG(ReadInt32(fpr, (int*)&format_return->dwChannelMask)); // format GUID HRG(ReadBytes(fpr, 16U, buff)); if (0 == memcmp(buff, &MFAudioFormat_Float, 16)) { format_return->sampleFormat = WWMFBitFormatFloat; } else if (0 == memcmp(buff, &MFAudioFormat_PCM, 16)) { format_return->sampleFormat = WWMFBitFormatInt; } else { printf("unrecognized format guid"); goto end; } } else { printf("unrecognized format"); goto end; } } } else if (0 == memcmp(buff, "data", 4)) { // data chunk HRG(ReadInt32(fpr, (int*)dataBytes_return)); break; } else { // skip this chunk HRG(ReadInt32(fpr, &chunkBytes)); if (chunkBytes < 4) { printf("E: chunk bytes == %d\n", chunkBytes); goto end; } fseek(fpr, chunkBytes, SEEK_CUR); } } end: if (S_OK == hr && format_return->sampleFormat == WWMFBitFormatUnknown) { printf("unrecognized format"); hr = E_FAIL; } return hr; }
static HRESULT CreateAudioMediaType(const WWMFPcmFormat &fmt, IMFMediaType** ppMediaType) { HRESULT hr; IMFMediaType *pMediaType = NULL; *ppMediaType = NULL; HRG(MFCreateMediaType(&pMediaType) ); HRG(pMediaType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio)); HRG(pMediaType->SetGUID(MF_MT_SUBTYPE, (fmt.sampleFormat == WWMFBitFormatInt) ? MFAudioFormat_PCM : MFAudioFormat_Float)); HRG(pMediaType->SetUINT32(MF_MT_AUDIO_NUM_CHANNELS, fmt.nChannels)); HRG(pMediaType->SetUINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, fmt.sampleRate)); HRG(pMediaType->SetUINT32(MF_MT_AUDIO_BLOCK_ALIGNMENT, fmt.FrameBytes())); HRG(pMediaType->SetUINT32(MF_MT_AUDIO_AVG_BYTES_PER_SECOND, fmt.BytesPerSec())); HRG(pMediaType->SetUINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, fmt.bits)); HRG(pMediaType->SetUINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, TRUE)); if (0 != fmt.dwChannelMask) { HRG(pMediaType->SetUINT32(MF_MT_AUDIO_CHANNEL_MASK, fmt.dwChannelMask)); } if (fmt.bits != fmt.validBitsPerSample) { HRG(pMediaType->SetUINT32(MF_MT_AUDIO_VALID_BITS_PER_SAMPLE, fmt.validBitsPerSample)); } *ppMediaType = pMediaType; pMediaType = NULL; //< prevent release end: SafeRelease(&pMediaType); return hr; }