#include "xaudio2.hpp" #include struct AudioXAudio2 : Audio, public IXAudio2VoiceCallback { AudioXAudio2() { initialize(); } ~AudioXAudio2() { terminate(); } auto ready() -> bool { return _ready; } auto information() -> Information { Information information; information.devices = {"Default"}; information.channels = {2}; information.frequencies = {44100.0, 48000.0, 96000.0}; information.latencies = {20, 40, 60, 80, 100}; return information; } auto blocking() -> bool { return _blocking; } auto channels() -> uint { return _channels; } auto frequency() -> double { return _frequency; } auto latency() -> uint { return _latency; } auto setBlocking(bool blocking) -> bool { if(_blocking == blocking) return true; _blocking = blocking; return true; } auto setFrequency(double frequency) -> bool { if(_frequency == frequency) return true; _frequency = frequency; return initialize(); } auto setLatency(uint latency) -> bool { if(_latency == latency) return true; _latency = latency; return initialize(); } auto clear() -> void { if(!_sourceVoice) return; _sourceVoice->Stop(0); _sourceVoice->FlushSourceBuffers(); //calls OnBufferEnd for all currently submitted buffers _bufferIndex = 0; _bufferOffset = 0; if(_buffer) memory::fill(_buffer, _period * _bufferCount * sizeof(uint32_t)); _sourceVoice->Start(0); } auto output(const double samples[]) -> void { _buffer[_bufferIndex * _period + _bufferOffset++] = uint16_t(samples[0] * 32768.0) << 0 | uint16_t(samples[1] * 32768.0) << 16; if(_bufferOffset < _period) return; _bufferOffset = 0; if(_bufferQueue == _bufferCount - 1) { if(_blocking) { //wait until there is at least one other free buffer for the next sample while(_bufferQueue == _bufferCount - 1); } else { //we need one free buffer for the next sample, so ignore the current contents return; } } pushBuffer(_period * 4, _buffer + _bufferIndex * _period); _bufferIndex = (_bufferIndex + 1) % _bufferCount; } private: auto initialize() -> bool { terminate(); _bufferCount = 8; _period = _frequency * _latency / _bufferCount / 1000.0 + 0.5; _buffer = new uint32_t[_period * _bufferCount]; _bufferOffset = 0; _bufferIndex = 0; _bufferQueue = 0; if(FAILED(XAudio2Create(&_interface, 0 , XAUDIO2_DEFAULT_PROCESSOR))) return false; uint deviceCount = 0; _interface->GetDeviceCount(&deviceCount); if(deviceCount == 0) return terminate(), false; uint deviceID = 0; for(uint deviceIndex : range(deviceCount)) { XAUDIO2_DEVICE_DETAILS deviceDetails = {}; _interface->GetDeviceDetails(deviceIndex, &deviceDetails); if(deviceDetails.Role & DefaultGameDevice) deviceID = deviceIndex; } if(FAILED(_interface->CreateMasteringVoice(&_masterVoice, _channels, (uint)_frequency, 0, deviceID, nullptr))) return terminate(), false; WAVEFORMATEX waveFormat; waveFormat.wFormatTag = WAVE_FORMAT_PCM; waveFormat.nChannels = _channels; waveFormat.nSamplesPerSec = (uint)_frequency; waveFormat.nBlockAlign = 4; waveFormat.wBitsPerSample = 16; waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign; waveFormat.cbSize = 0; if(FAILED(_interface->CreateSourceVoice(&_sourceVoice, (WAVEFORMATEX*)&waveFormat, XAUDIO2_VOICE_NOSRC, XAUDIO2_DEFAULT_FREQ_RATIO, this, nullptr, nullptr))) return terminate(), false; clear(); return _ready = true; } auto terminate() -> void { _ready = false; if(_sourceVoice) { _sourceVoice->Stop(0); _sourceVoice->DestroyVoice(); _sourceVoice = nullptr; } if(_masterVoice) { _masterVoice->DestroyVoice(); _masterVoice = nullptr; } if(_interface) { _interface->Release(); _interface = nullptr; } delete[] _buffer; _buffer = nullptr; } auto pushBuffer(uint bytes, uint32_t* _audioData) -> void { XAUDIO2_BUFFER buffer = {}; buffer.AudioBytes = bytes; buffer.pAudioData = reinterpret_cast(_audioData); buffer.pContext = 0; InterlockedIncrement(&_bufferQueue); _sourceVoice->SubmitSourceBuffer(&buffer); } bool _ready = false; bool _blocking = true; uint _channels = 2; double _frequency = 48000.0; uint _latency = 80; uint32_t* _buffer = nullptr; uint _period = 0; uint _bufferCount = 0; uint _bufferOffset = 0; uint _bufferIndex = 0; volatile long _bufferQueue = 0; //how many buffers are queued and ready for playback IXAudio2* _interface = nullptr; IXAudio2MasteringVoice* _masterVoice = nullptr; IXAudio2SourceVoice* _sourceVoice = nullptr; //inherited from IXAudio2VoiceCallback STDMETHODIMP_(void) OnBufferStart(void* pBufferContext){} STDMETHODIMP_(void) OnLoopEnd(void* pBufferContext){} STDMETHODIMP_(void) OnStreamEnd() {} STDMETHODIMP_(void) OnVoiceError(void* pBufferContext, HRESULT Error) {} STDMETHODIMP_(void) OnVoiceProcessingPassEnd() {} STDMETHODIMP_(void) OnVoiceProcessingPassStart(UINT32 BytesRequired) {} STDMETHODIMP_(void) OnBufferEnd(void* pBufferContext) { InterlockedDecrement(&_bufferQueue); } };