Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
hrydgard
GitHub Repository: hrydgard/ppsspp
Path: blob/master/UWP/XAudioSoundStream.cpp
3185 views
1
#include "pch.h"
2
3
#include <XAudio2.h>
4
5
#include <algorithm>
6
#include <cstdint>
7
#include <thread>
8
9
#include "Common/Log.h"
10
#include "Common/Thread/ThreadUtil.h"
11
#include "Common/Audio/AudioBackend.h"
12
#include "XAudioSoundStream.h"
13
14
const size_t BUFSIZE = 32 * 1024;
15
16
class XAudioBackend : public AudioBackend {
17
public:
18
XAudioBackend();
19
~XAudioBackend() override;
20
21
void EnumerateDevices(std::vector<AudioDeviceDesc> *outputDevices, bool captureDevices = false) {
22
// Do nothing! Auto is the only option.
23
}
24
void SetRenderCallback(RenderCallback callback, void *userdata) override {
25
callback_ = callback;
26
userdata_ = userdata;
27
}
28
29
bool InitOutputDevice(std::string_view uniqueId, LatencyMode latencyMode, bool *reverted) override;
30
int SampleRate() const override { return sampleRate_; }
31
int PeriodFrames() const override { return samplesPerBuffer; }
32
int BufferSize() const override { return samplesPerBuffer * bufferCount; }
33
34
private:
35
void Start();
36
void Stop();
37
38
RenderCallback callback_ = nullptr;
39
40
IXAudio2 *xaudioDevice = nullptr;
41
IXAudio2MasteringVoice *masterVoice_ = nullptr;
42
IXAudio2SourceVoice *sourceVoice_ = nullptr;
43
44
void *userdata_ = nullptr;
45
46
WAVEFORMATEX format_;
47
48
int sampleRate_ = 44100;
49
int periodFrames_ = 0;
50
51
enum {
52
samplesPerBuffer = 480, // 10 ms @ 48kHz. maybe we can tweak this using latency mode.
53
bufferCount = 3,
54
channels = 2,
55
};
56
float audioBuffer_[bufferCount][samplesPerBuffer * channels];
57
int curBuffer_ = 0;
58
59
uint32_t cursor_ = 0;
60
61
std::thread thread_;
62
std::atomic<bool> running_{};
63
};
64
65
// TODO: Get rid of this
66
static XAudioBackend *g_dsound;
67
68
XAudioBackend::XAudioBackend() {
69
format_.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
70
format_.nChannels = channels;
71
format_.nSamplesPerSec = 48000;
72
format_.wBitsPerSample = 32;
73
format_.nBlockAlign = format_.nChannels * format_.wBitsPerSample / 8;
74
format_.nAvgBytesPerSec = format_.nSamplesPerSec * format_.nBlockAlign;
75
format_.cbSize = 0;
76
}
77
78
XAudioBackend::~XAudioBackend() {
79
Stop();
80
}
81
82
void XAudioBackend::Stop() {
83
running_ = false;
84
if (thread_.joinable()) {
85
thread_.join();
86
}
87
88
if (xaudioDevice) {
89
xaudioDevice->Release();
90
xaudioDevice = nullptr;
91
sourceVoice_ = nullptr;
92
}
93
}
94
95
bool XAudioBackend::InitOutputDevice(std::string_view uniqueId, LatencyMode latencyMode, bool *reverted) {
96
Stop();
97
98
*reverted = false;
99
if FAILED(XAudio2Create(&xaudioDevice, 0, XAUDIO2_DEFAULT_PROCESSOR)) {
100
xaudioDevice = nullptr;
101
return false;
102
}
103
104
XAUDIO2_DEBUG_CONFIGURATION dbgCfg;
105
ZeroMemory(&dbgCfg, sizeof(dbgCfg));
106
dbgCfg.TraceMask = XAUDIO2_LOG_WARNINGS | XAUDIO2_LOG_DETAIL;
107
//dbgCfg.BreakMask = XAUDIO2_LOG_ERRORS;
108
xaudioDevice->SetDebugConfiguration(&dbgCfg);
109
110
if FAILED(xaudioDevice->CreateMasteringVoice(&masterVoice_, 2, sampleRate_, 0, 0, nullptr)) {
111
xaudioDevice->Release();
112
xaudioDevice = nullptr;
113
return false;
114
}
115
116
if FAILED(xaudioDevice->CreateSourceVoice(&sourceVoice_, &format_, 0, 1.0, nullptr, nullptr, nullptr)) {
117
xaudioDevice->Release();
118
xaudioDevice = nullptr;
119
return false;
120
}
121
122
sourceVoice_->SetFrequencyRatio(1.0);
123
124
cursor_ = 0;
125
126
if FAILED(sourceVoice_->Start(0, XAUDIO2_COMMIT_NOW)) {
127
xaudioDevice->Release();
128
xaudioDevice = nullptr;
129
return false;
130
}
131
132
running_ = true;
133
thread_ = std::thread([this]() {
134
while (running_) {
135
XAUDIO2_VOICE_STATE state = {};
136
sourceVoice_->GetState(&state);
137
if (state.BuffersQueued < bufferCount) {
138
// Fill buffer with audio
139
callback_(audioBuffer_[curBuffer_], samplesPerBuffer, format_.nSamplesPerSec, userdata_);
140
141
XAUDIO2_BUFFER buf = {};
142
buf.AudioBytes = samplesPerBuffer * 2 * sizeof(float);
143
buf.pAudioData = reinterpret_cast<BYTE*>(audioBuffer_[curBuffer_]);
144
buf.Flags = 0;
145
sourceVoice_->SubmitSourceBuffer(&buf);
146
curBuffer_ += 1;
147
if (curBuffer_ >= bufferCount) {
148
curBuffer_ = 0;
149
}
150
} else {
151
std::this_thread::sleep_for(std::chrono::milliseconds(1));
152
}
153
}
154
});
155
return true;
156
}
157
158
AudioBackend *System_CreateAudioBackend() {
159
// Only one type available on UWP.
160
return new XAudioBackend();
161
}
162
163