Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- //-----------------------------------------------------------
- // Play an exclusive-mode stream on the default audio
- // rendering device. The PlayExclusiveStream function uses
- // event-driven buffering and MMCSS to play the stream at
- // the minimum latency supported by the device.
- //-----------------------------------------------------------
- // NOTE: Must link against avrt.lib (a pragma could do)
- #include <ObjBase.h>
- #include <mmdeviceapi.h>
- #include <Audioclient.h>
- #include <avrt.h>
- #include <cstring>
- #include <iostream>
- #include <cmath>
- #include <string>
- #include <vector>
- #include <algorithm>
- #define PI 3.1415926535897932384626433832795
- #define PI2 2 * PI
- // 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_IAudioRenderClient = __uuidof(IAudioRenderClient);
- class MyAudioSource
- {
- private:
- WAVEFORMATEX mFormat;
- UINT32 mMilliseconds;
- UINT32 mPlayedFrames;
- WORD mCurrentChannel;
- public:
- MyAudioSource()
- : mMilliseconds(0),
- mPlayedFrames(0),
- mCurrentChannel(0)
- {
- ZeroMemory(&mFormat, sizeof(WAVEFORMATEX));
- }
- HRESULT SetFormat(const WAVEFORMATEX *pwfx)
- {
- memcpy(&mFormat, pwfx, sizeof(WAVEFORMATEX));
- return S_OK;
- }
- HRESULT LoadData(UINT32 bufferFrameCount, BYTE *pData, DWORD *pFlags)
- {
- // Rough code to play for at least cMaxMilliseconds per channel
- const UINT32 cMaxMilliseconds = 2000;
- const double cTestFrequencyHz = 400;
- const UINT32 cBytesPerSample = mFormat.wBitsPerSample / 8;
- if (mMilliseconds >= cMaxMilliseconds)
- {
- ++mCurrentChannel;
- if (mCurrentChannel >= mFormat.nChannels)
- {
- ZeroMemory(pData, bufferFrameCount * mFormat.nChannels * cBytesPerSample);
- *pFlags = AUDCLNT_BUFFERFLAGS_SILENT;
- return S_OK;
- }
- mMilliseconds = 0;
- mPlayedFrames = 0;
- }
- const double cAmplitude = 0.9 * std::pow(2.0, static_cast<double>(mFormat.wBitsPerSample - 1));
- for (UINT32 i = 0; i < bufferFrameCount; ++i)
- {
- const UINT32 cSampleValue = static_cast<UINT32>(cAmplitude * std::sin(cTestFrequencyHz * PI2 * (mPlayedFrames + i) / mFormat.nSamplesPerSec));
- for (WORD c = 0; c < mFormat.nChannels; ++c)
- {
- if (c == mCurrentChannel) std::memcpy(pData, &cSampleValue, cBytesPerSample);
- else ZeroMemory(pData, cBytesPerSample);
- pData += cBytesPerSample;
- }
- }
- *pFlags = 0;
- mMilliseconds += 1000 * bufferFrameCount / mFormat.nSamplesPerSec;
- mPlayedFrames += bufferFrameCount;
- return S_OK;
- }
- };
- HRESULT GetStreamFormat(IAudioClient *pAudioClient, WAVEFORMATEX **ppwfx)
- {
- WAVEFORMATEXTENSIBLE wfx;
- ZeroMemory(&wfx, sizeof(WAVEFORMATEXTENSIBLE));
- wfx.Format.cbSize = 22; // minimum required, and all that is needed (skips the SubFormat)
- wfx.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
- wfx.Format.nChannels = 6;
- wfx.Format.nSamplesPerSec = 96000;
- wfx.Format.wBitsPerSample = 24;
- wfx.Format.nBlockAlign = 18;
- wfx.Format.nAvgBytesPerSec = 1728000;
- wfx.Samples.wValidBitsPerSample = 24;
- wfx.dwChannelMask = KSAUDIO_SPEAKER_5POINT1;
- wfx.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
- std::cout << "Trying WAVEFORMAT\n"
- << "\tChannels: " << wfx.Format.nChannels << "\n"
- << "\tSamples per second: " << wfx.Format.wBitsPerSample << "\n"
- << "\tBits per sample: " << wfx.Format.wBitsPerSample << "\n"
- << "\tBlock align: " << wfx.Format.nBlockAlign << "\n"
- << "\tAverage bytes per sec: " << wfx.Format.nAvgBytesPerSec << "\n";
- HRESULT hr = pAudioClient->IsFormatSupported(AUDCLNT_SHAREMODE_EXCLUSIVE, &wfx.Format, ppwfx);
- if (hr < 0)
- {
- std::cout << "IAudioClient::IsFormatSupported failed: ";
- switch (hr)
- {
- case E_POINTER: std::cout << "E_POINTER"; break;
- case E_INVALIDARG: std::cout << "E_INVALIDARG"; break;
- case AUDCLNT_E_DEVICE_INVALIDATED: std::cout << "AUDCLNT_E_DEVICE_INVALIDATED"; break;
- case AUDCLNT_E_SERVICE_NOT_RUNNING: std::cout << "AUDCLNT_E_SERVICE_NOT_RUNNING"; break;
- case AUDCLNT_E_UNSUPPORTED_FORMAT: std::cout << "AUDCLNT_E_UNSUPPORTED_FORMAT"; break;
- default: std::cout << "HRESULT(" << std::hex << hr << ")"; break;
- }
- std::cout << "\n";
- }
- if (*ppwfx == nullptr)
- {
- if (hr != S_OK)
- {
- std::cout << "Returning the proposed WAVEFORMAT anyway\n";
- }
- // Force it
- *ppwfx = reinterpret_cast<WAVEFORMATEX *>(CoTaskMemAlloc(sizeof(wfx)));
- std::memcpy(*ppwfx, &wfx, sizeof(wfx));
- hr = S_OK;
- }
- return hr;
- }
- template <typename V>
- void ClearDeviceVector(V &v)
- {
- for (auto i = v.begin(); i != v.end(); ++i)
- {
- SAFE_RELEASE(*i);
- }
- v.clear();
- }
- HRESULT PlayExclusiveStream(MyAudioSource *pMySource)
- {
- HRESULT hr;
- REFERENCE_TIME hnsRequestedDuration = 0;
- IMMDeviceEnumerator *pEnumerator = NULL;
- IMMDeviceCollection *pCollection = NULL;
- IMMDevice *pDevice = NULL;
- IAudioClient *pAudioClient = NULL;
- IAudioRenderClient *pRenderClient = NULL;
- WAVEFORMATEX *pwfx = NULL;
- HANDLE hEvent = NULL;
- HANDLE hTask = NULL;
- UINT cDevices;
- UINT32 bufferFrameCount;
- BYTE *pData;
- PWSTR strId;
- DWORD flags = 0;
- std::vector<IMMDevice *> vDevices;
- std::string lInput;
- std::cout << "CoCreateInstance\n";
- hr = CoCreateInstance(
- CLSID_MMDeviceEnumerator, NULL,
- CLSCTX_ALL, IID_IMMDeviceEnumerator,
- (void**)&pEnumerator);
- EXIT_ON_ERROR(hr)
- std::cout << "IMMDeviceEnumerator::EnumAudioEndpoints\n";
- hr = pEnumerator->EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE, &pCollection);
- EXIT_ON_ERROR(hr)
- hr = pCollection->GetCount(&cDevices);
- EXIT_ON_ERROR(hr)
- for (UINT i = 0; i < cDevices; ++i)
- {
- hr = pCollection->Item(i, &pDevice);
- EXIT_ON_ERROR(hr)
- hr = pDevice->GetId(&strId);
- EXIT_ON_ERROR(hr)
- vDevices.push_back(pDevice);
- if (strId != nullptr)
- {
- std::wcout << L"\tDevice " << i << ": " << strId << std::endl;
- CoTaskMemFree(strId);
- strId = nullptr;
- }
- pDevice = nullptr;
- }
- std::cout << "Select the device to use (just press ENTER for the default): ";
- std::getline(std::cin, lInput);
- while (!lInput.empty() && (lInput.back() == '\r' || lInput.back() == '\n'))
- {
- lInput.pop_back();
- }
- if (lInput.empty())
- {
- hr = pEnumerator->GetDefaultAudioEndpoint(
- eRender, eConsole, &pDevice);
- EXIT_ON_ERROR(hr)
- }
- else
- {
- long iDevice = std::strtol(lInput.c_str(), nullptr, 10);
- if (iDevice < 0 || iDevice >= static_cast<long>(vDevices.size()))
- {
- std::cout << "Wrong choice\n";
- EXIT_ON_ERROR(E_INVALIDARG)
- }
- std::swap(pDevice, vDevices[iDevice]);
- }
- hr = pDevice->GetId(&strId);
- EXIT_ON_ERROR(hr)
- if (strId != nullptr)
- {
- std::wcout << L"Default device: " << strId << std::endl;
- CoTaskMemFree(strId);
- strId = nullptr;
- }
- std::cout << "IMMDevice::Activate\n";
- hr = pDevice->Activate(
- IID_IAudioClient, CLSCTX_ALL,
- NULL, (void**)&pAudioClient);
- EXIT_ON_ERROR(hr)
- // Call a helper function to negotiate with the audio
- // device for an exclusive-mode stream format.
- hr = GetStreamFormat(pAudioClient, &pwfx);
- EXIT_ON_ERROR(hr)
- // Initialize the stream to play at the minimum latency.
- std::cout << "IAudioClient::GetDevicePeriod\n";
- hr = pAudioClient->GetDevicePeriod(NULL, &hnsRequestedDuration);
- EXIT_ON_ERROR(hr)
- std::cout << "IAudioDevice::Initialize\n";
- hr = pAudioClient->Initialize(
- AUDCLNT_SHAREMODE_EXCLUSIVE,
- AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
- hnsRequestedDuration,
- hnsRequestedDuration,
- pwfx,
- NULL);
- EXIT_ON_ERROR(hr)
- // Tell the audio source which format to use.
- hr = pMySource->SetFormat(pwfx);
- EXIT_ON_ERROR(hr)
- // Create an event handle and register it for
- // buffer-event notifications.
- hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
- if (hEvent == NULL)
- {
- hr = E_FAIL;
- goto Exit;
- }
- hr = pAudioClient->SetEventHandle(hEvent);
- EXIT_ON_ERROR(hr);
- // Get the actual size of the two allocated buffers.
- hr = pAudioClient->GetBufferSize(&bufferFrameCount);
- EXIT_ON_ERROR(hr)
- hr = pAudioClient->GetService(
- IID_IAudioRenderClient,
- (void**)&pRenderClient);
- EXIT_ON_ERROR(hr)
- // To reduce latency, load the first buffer with data
- // from the audio source before starting the stream.
- hr = pRenderClient->GetBuffer(bufferFrameCount, &pData);
- EXIT_ON_ERROR(hr)
- hr = pMySource->LoadData(bufferFrameCount, pData, &flags);
- EXIT_ON_ERROR(hr)
- hr = pRenderClient->ReleaseBuffer(bufferFrameCount, flags);
- EXIT_ON_ERROR(hr)
- // Ask MMCSS to temporarily boost the thread priority
- // to reduce glitches while the low-latency stream plays.
- DWORD taskIndex = 0;
- hTask = AvSetMmThreadCharacteristics(TEXT("Pro Audio"), &taskIndex);
- if (hTask == NULL)
- {
- hr = E_FAIL;
- EXIT_ON_ERROR(hr)
- }
- std::cout << "IAudioDevice::Start\n";
- hr = pAudioClient->Start(); // Start playing.
- EXIT_ON_ERROR(hr)
- // Each loop fills one of the two buffers.
- while (flags != AUDCLNT_BUFFERFLAGS_SILENT)
- {
- // Wait for next buffer event to be signaled.
- DWORD retval = WaitForSingleObject(hEvent, 2000);
- if (retval != WAIT_OBJECT_0)
- {
- // Event handle timed out after a 2-second wait.
- pAudioClient->Stop();
- hr = ERROR_TIMEOUT;
- goto Exit;
- }
- // Grab the next empty buffer from the audio device.
- hr = pRenderClient->GetBuffer(bufferFrameCount, &pData);
- EXIT_ON_ERROR(hr)
- // Load the buffer with data from the audio source.
- hr = pMySource->LoadData(bufferFrameCount, pData, &flags);
- EXIT_ON_ERROR(hr)
- hr = pRenderClient->ReleaseBuffer(bufferFrameCount, flags);
- EXIT_ON_ERROR(hr)
- }
- // Wait for the last buffer to play before stopping.
- Sleep((DWORD)(hnsRequestedDuration/REFTIMES_PER_MILLISEC));
- std::cout << "IAudioDevice::Stop\n";
- hr = pAudioClient->Stop(); // Stop playing.
- EXIT_ON_ERROR(hr)
- Exit:
- if (hEvent != NULL)
- {
- CloseHandle(hEvent);
- }
- if (hTask != NULL)
- {
- AvRevertMmThreadCharacteristics(hTask);
- }
- CoTaskMemFree(pwfx);
- ClearDeviceVector(vDevices);
- SAFE_RELEASE(pEnumerator)
- SAFE_RELEASE(pCollection)
- SAFE_RELEASE(pDevice)
- SAFE_RELEASE(pAudioClient)
- SAFE_RELEASE(pRenderClient)
- return hr;
- }
- int main()
- {
- if (CoInitializeEx(nullptr, COINIT_MULTITHREADED) != S_OK)
- {
- return 1;
- }
- MyAudioSource mas;
- PlayExclusiveStream(&mas);
- CoUninitialize();
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement