CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
Path: blob/master/UWP/XAudioSoundStream.cpp
Views: 1401
#include "pch.h"12#include <XAudio2.h>34#include <algorithm>5#include <cstdint>67#include "Common/Log.h"8#include "Common/Thread/ThreadUtil.h"9#include "XAudioSoundStream.h"1011#include <process.h>1213const size_t BUFSIZE = 32 * 1024;1415class XAudioBackend : public WindowsAudioBackend {16public:17XAudioBackend();18~XAudioBackend() override;1920bool Init(HWND window, StreamCallback callback, int sampleRate) override; // If fails, can safely delete the object21int GetSampleRate() const override { return sampleRate_; }2223private:24bool RunSound();25bool CreateBuffer();26void PollLoop();2728StreamCallback callback_ = nullptr;2930IXAudio2 *xaudioDevice = nullptr;31IXAudio2MasteringVoice *xaudioMaster = nullptr;32IXAudio2SourceVoice *xaudioVoice = nullptr;3334int sampleRate_ = 0;3536char realtimeBuffer_[BUFSIZE]{};37uint32_t cursor_ = 0;3839HANDLE thread_ = 0;40HANDLE exitEvent_ = 0;4142bool exit = false;43};4445// TODO: Get rid of this46static XAudioBackend *g_dsound;4748XAudioBackend::XAudioBackend() {49exitEvent_ = CreateEvent(nullptr, true, true, L"");50}5152inline int RoundDown128(int x) {53return x & (~127);54}5556bool XAudioBackend::CreateBuffer() {57if FAILED(xaudioDevice->CreateMasteringVoice(&xaudioMaster, 2, sampleRate_, 0, 0, NULL))58return false;5960WAVEFORMATEX waveFormat;61waveFormat.cbSize = sizeof(waveFormat);62waveFormat.nAvgBytesPerSec = sampleRate_ * 4;63waveFormat.nBlockAlign = 4;64waveFormat.nChannels = 2;65waveFormat.nSamplesPerSec = sampleRate_;66waveFormat.wBitsPerSample = 16;67waveFormat.wFormatTag = WAVE_FORMAT_PCM;6869if FAILED(xaudioDevice->CreateSourceVoice(&xaudioVoice, &waveFormat, 0, 1.0, nullptr, nullptr, nullptr))70return false;7172xaudioVoice->SetFrequencyRatio(1.0);73return true;74}7576bool XAudioBackend::RunSound() {77if FAILED(XAudio2Create(&xaudioDevice, 0, XAUDIO2_DEFAULT_PROCESSOR)) {78xaudioDevice = NULL;79return false;80}8182XAUDIO2_DEBUG_CONFIGURATION dbgCfg;83ZeroMemory(&dbgCfg, sizeof(dbgCfg));84dbgCfg.TraceMask = XAUDIO2_LOG_WARNINGS | XAUDIO2_LOG_DETAIL;85//dbgCfg.BreakMask = XAUDIO2_LOG_ERRORS;86xaudioDevice->SetDebugConfiguration(&dbgCfg);8788if (!CreateBuffer()) {89xaudioDevice->Release();90xaudioDevice = NULL;91return false;92}9394cursor_ = 0;9596if FAILED(xaudioVoice->Start(0, XAUDIO2_COMMIT_NOW)) {97xaudioDevice->Release();98xaudioDevice = NULL;99return false;100}101102thread_ = (HANDLE)_beginthreadex(0, 0, [](void* param)103{104SetCurrentThreadName("XAudio2");105XAudioBackend *backend = (XAudioBackend *)param;106backend->PollLoop();107return 0U;108}, (void *)this, 0, 0);109SetThreadPriority(thread_, THREAD_PRIORITY_ABOVE_NORMAL);110111return true;112}113114XAudioBackend::~XAudioBackend() {115if (!xaudioDevice)116return;117118if (!xaudioVoice)119return;120121exit = true;122WaitForSingleObject(exitEvent_, INFINITE);123CloseHandle(exitEvent_);124125xaudioDevice->Release();126}127128bool XAudioBackend::Init(HWND window, StreamCallback _callback, int sampleRate) {129callback_ = _callback;130sampleRate_ = sampleRate;131return RunSound();132}133134void XAudioBackend::PollLoop() {135ResetEvent(exitEvent_);136137while (!exit) {138XAUDIO2_VOICE_STATE state;139xaudioVoice->GetState(&state);140141// TODO: Still plenty of tuning to do here.142// 4 seems to work fine.143if (state.BuffersQueued > 4) {144Sleep(1);145continue;146}147148uint32_t bytesRequired = (sampleRate_ * 4) / 100;149150uint32_t bytesLeftInBuffer = BUFSIZE - cursor_;151uint32_t readCount = std::min(bytesRequired, bytesLeftInBuffer);152153// realtimeBuffer_ is just used as a ring of scratch space to be submitted, since SubmitSourceBuffer doesn't154// take ownership of the data. It needs to be big enough to fit the max number of buffers we check for155// above, which it is, easily.156157int stereoSamplesRendered = (*callback_)((short*)&realtimeBuffer_[cursor_], readCount / 4, sampleRate_);158int numBytesRendered = 2 * sizeof(short) * stereoSamplesRendered;159160XAUDIO2_BUFFER xaudioBuffer{};161xaudioBuffer.pAudioData = (const BYTE*)&realtimeBuffer_[cursor_];162xaudioBuffer.AudioBytes = numBytesRendered;163164if FAILED(xaudioVoice->SubmitSourceBuffer(&xaudioBuffer, NULL)) {165WARN_LOG(Log::Audio, "XAudioBackend: Failed writing bytes");166}167cursor_ += numBytesRendered;168if (cursor_ >= BUFSIZE) {169cursor_ = 0;170bytesLeftInBuffer = BUFSIZE;171}172}173174SetEvent(exitEvent_);175}176177WindowsAudioBackend *CreateAudioBackend(AudioBackendType type) {178// Only one type available on UWP.179return new XAudioBackend();180}181182183