1 #include "burner.h"
2 #include "aud_dsp.h"
3
4 //#ifdef _MSC_VER
5 #include <initguid.h>
6 #include <xaudio2.h>
7 #include <xaudio2fx.h>
8
9 static IXAudio2* pXAudio2 = NULL;
10 static IXAudio2MasteringVoice* pMasterVoice = NULL;
11 static IXAudio2SourceVoice* pSourceVoice = NULL;
12 static XAUDIO2_BUFFER sAudioBuffer;
13 static XAUDIO2_VOICE_STATE vState;
14 static IUnknown* pXAPO = NULL;
15 static bool effectEnable = false;
16
17 int (*XAudio2GetNextSound)(int);
18 static int XAudio2SetVolume(); // forward
19
20 BYTE* pAudioBuffers = NULL;
21 int currentBuffer = 0;
22
23 static int cbLoopLen = 0; // Loop length (in bytes) calculated
24 static int nXAudio2Fps = 0; // Application fps * 100
25 static float nXAudio2Vol = 1.0f;
26
27 struct StreamingVoiceContext : public IXAudio2VoiceCallback
28 {
29 HANDLE hBufferEndEvent;
30
StreamingVoiceContextStreamingVoiceContext31 StreamingVoiceContext() {
32 hBufferEndEvent = NULL;
33 hBufferEndEvent = CreateEvent( NULL, FALSE, FALSE, NULL);
34 }
~StreamingVoiceContextStreamingVoiceContext35 virtual ~StreamingVoiceContext() {
36 CloseHandle(hBufferEndEvent);
37 hBufferEndEvent = NULL;
38 }
39
STDMETHOD_StreamingVoiceContext40 STDMETHOD_(void, OnBufferEnd) (void * /*pBufferContext*/) {
41 SetEvent(hBufferEndEvent);
42 }
43
44 // dummies:
STDMETHOD_StreamingVoiceContext45 STDMETHOD_(void, OnVoiceProcessingPassStart) (UINT32 /*BytesRequired*/) {}
STDMETHOD_StreamingVoiceContext46 STDMETHOD_(void, OnVoiceProcessingPassEnd) () {}
STDMETHOD_StreamingVoiceContext47 STDMETHOD_(void, OnStreamEnd) () {}
STDMETHOD_StreamingVoiceContext48 STDMETHOD_(void, OnBufferStart) (void * /*pBufferContext*/) {}
STDMETHOD_StreamingVoiceContext49 STDMETHOD_(void, OnLoopEnd) (void * /*pBufferContext*/) {}
STDMETHOD_StreamingVoiceContext50 STDMETHOD_(void, OnVoiceError) (void * /*pBufferContext*/, HRESULT /*Error*/) {};
51 };
52 StreamingVoiceContext voiceContext;
53
XAudio2GetNextSoundFiller(int)54 static int XAudio2GetNextSoundFiller(int)
55 {
56 if (nAudNextSound == NULL) {
57 return 1;
58 }
59 AudWriteSilence();
60
61 return 0;
62 }
63
XAudio2SetCallback(int (* pCallback)(int))64 static int XAudio2SetCallback(int (*pCallback)(int))
65 {
66 if (pCallback == NULL) {
67 XAudio2GetNextSound = XAudio2GetNextSoundFiller;
68 } else {
69 XAudio2GetNextSound = pCallback;
70 }
71
72 return 0;
73 }
74
XAudio2BlankSound()75 static int XAudio2BlankSound()
76 {
77 if (pAudioBuffers) {
78 memset(pAudioBuffers, 0, cbLoopLen);
79 }
80
81 // Also blank the nAudNextSound buffer
82 if (nAudNextSound) {
83 AudWriteSilence();
84 }
85
86 return 0;
87 }
88
XAudio2Check()89 static int XAudio2Check()
90 {
91 if (!pSourceVoice || !pAudioBuffers) {
92 return 1;
93 }
94
95 while (true) {
96 pSourceVoice->GetState(&vState);
97
98 assert(vState.BuffersQueued < (unsigned int)nAudSegCount);
99
100 if (vState.BuffersQueued < (unsigned int)nAudSegCount - 1) {
101 if (vState.BuffersQueued == 0) {
102 // buffers ran dry
103 }
104 // there is at least one free buffer
105 break;
106 } else {
107 // the maximum number of buffers is currently queued
108 if (bAlwaysDrawFrames) {
109 // wait for one buffer to finish playing
110 WaitForSingleObject(voiceContext.hBufferEndEvent, INFINITE);
111 } else {
112 // drop current audio frame
113 Sleep(2);
114 return 0;
115 }
116 }
117 }
118
119 XAudio2GetNextSound(true);
120
121 // dsp update
122 if (nAudDSPModule[1] & 1) {
123 if (bRunPause)
124 AudWriteSilence();
125 else
126 DspDo(nAudNextSound, nAudSegLen);
127 }
128
129 if (nAudDSPModule[1] & 2) {
130 if (!effectEnable) {
131 pSourceVoice->EnableEffect(0);
132 effectEnable = true;
133 }
134 } else {
135 if (effectEnable) {
136 pSourceVoice->DisableEffect(0);
137 effectEnable = false;
138 }
139 }
140
141 // copy & protect the audio data in own memory area while playing it
142 memcpy(&pAudioBuffers[currentBuffer * nAudAllocSegLen], nAudNextSound, nAudAllocSegLen);
143
144 sAudioBuffer.AudioBytes = nAudAllocSegLen;
145 sAudioBuffer.pAudioData = &pAudioBuffers[currentBuffer * nAudAllocSegLen];
146
147 currentBuffer++;
148 currentBuffer %= (nAudSegCount);
149
150 HRESULT hr = pSourceVoice->SubmitSourceBuffer(&sAudioBuffer); // send buffer to queue
151 assert(hr == S_OK);
152
153 return 0;
154 }
155
XAudio2EffectInit()156 static int XAudio2EffectInit()
157 {
158 HRESULT hr = XAudio2CreateReverb(&pXAPO);
159 if (FAILED(hr)) {
160 return 1;
161 }
162
163 XAUDIO2_EFFECT_DESCRIPTOR descriptor;
164 descriptor.InitialState = TRUE;
165 descriptor.OutputChannels = 2;
166 descriptor.pEffect = pXAPO;
167
168 XAUDIO2_EFFECT_CHAIN chain;
169 chain.EffectCount = 1;
170 chain.pEffectDescriptors = &descriptor;
171
172 hr = pSourceVoice->SetEffectChain(&chain);
173 if (FAILED(hr)) {
174 return 1;
175 }
176
177 if (nAudDSPModule[1] & 2) {
178 pSourceVoice->EnableEffect(0);
179 effectEnable = true;
180 } else {
181 pSourceVoice->DisableEffect(0);
182 effectEnable = false;
183 }
184
185 return 0;
186 }
187
XAudio2EffectExit()188 static void XAudio2EffectExit()
189 {
190 RELEASE(pXAPO);
191 }
192
XAudio2ExitVoices()193 static void XAudio2ExitVoices()
194 {
195 if (pSourceVoice) {
196 pSourceVoice->Stop(0);
197 pSourceVoice->DestroyVoice();
198 pSourceVoice = NULL;
199 }
200 if (pMasterVoice) {
201 pMasterVoice->DestroyVoice();
202 pMasterVoice = NULL;
203 }
204 }
205
XAudio2Exit()206 static int XAudio2Exit()
207 {
208 DspExit();
209
210 // Cleanup XAudio2
211
212 XAudio2EffectExit();
213
214 XAudio2ExitVoices();
215
216 RELEASE(pXAudio2);
217 CoUninitialize();
218
219 if (nAudNextSound) {
220 free(nAudNextSound);
221 nAudNextSound = NULL;
222 }
223
224 if (pAudioBuffers) {
225 free(pAudioBuffers);
226 pAudioBuffers = NULL;
227 }
228
229 XAudio2GetNextSound = NULL;
230
231 return 0;
232 }
233
XAudio2InitVoices()234 static int XAudio2InitVoices()
235 {
236 HRESULT hr;
237
238 // Create a mastering voice
239 if (FAILED(hr = pXAudio2->CreateMasteringVoice(&pMasterVoice, XAUDIO2_DEFAULT_CHANNELS, nAudSampleRate[1], 0, 0, NULL))) {
240 return 1;
241 }
242
243 // Make the format of the sound
244 WAVEFORMATEX wfx;
245 memset(&wfx, 0, sizeof(wfx));
246 wfx.cbSize = sizeof(wfx);
247 wfx.wFormatTag = WAVE_FORMAT_PCM;
248 wfx.nChannels = 2; // stereo
249 wfx.nSamplesPerSec = nAudSampleRate[1]; // sample rate
250 wfx.wBitsPerSample = 16; // 16-bit
251 wfx.nBlockAlign = wfx.wBitsPerSample * wfx.nChannels / 8; // bytes per sample
252 wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign;
253
254 // Create the source voice
255 if (FAILED(hr = pXAudio2->CreateSourceVoice(&pSourceVoice, &wfx, 0, XAUDIO2_DEFAULT_FREQ_RATIO, &voiceContext, NULL, NULL))) {
256 return 1;
257 }
258
259 return 0;
260 }
261
XAudio2Init()262 static int XAudio2Init()
263 {
264 if (nAudSampleRate[1] <= 0) {
265 return 0;
266 }
267
268 nXAudio2Fps = nAppVirtualFps;
269
270 // Calculate the Seg Length and Loop length (round to nearest sample)
271 nAudSegLen = (nAudSampleRate[1] * 100 + (nXAudio2Fps >> 1)) / nXAudio2Fps;
272 nAudAllocSegLen = nAudSegLen << 2;
273 cbLoopLen = (nAudSegLen * nAudSegCount) << 2;
274
275 // Initialize XAudio2
276
277 if (FAILED(CoInitializeEx(NULL, COINIT_MULTITHREADED))) {
278 return 1;
279 }
280
281 HRESULT hr;
282 if (FAILED(hr = XAudio2Create(&pXAudio2, 0, XAUDIO2_DEFAULT_PROCESSOR))) {
283 CoUninitialize();
284 return 1;
285 }
286
287 if (XAudio2InitVoices()) {
288 XAudio2Exit();
289 return 1;
290 }
291
292 ZeroMemory(&sAudioBuffer, sizeof(sAudioBuffer));
293
294 nAudNextSound = (short*)malloc(nAudAllocSegLen); // The next sound block to put in the stream
295 if (nAudNextSound == NULL) {
296 XAudio2Exit();
297 return 1;
298 }
299
300 // create own buffers to store sound data because it must not be
301 // manipulated while the voice plays from it
302 pAudioBuffers = (BYTE *)malloc(cbLoopLen);
303 if (pAudioBuffers == NULL) {
304 XAudio2Exit();
305 return 1;
306 }
307 currentBuffer = 0;
308
309 XAudio2SetCallback(NULL);
310
311 DspInit();
312
313 XAudio2EffectInit();
314
315 return 0;
316 }
317
XAudio2Play()318 static int XAudio2Play()
319 {
320 if (pSourceVoice == NULL) {
321 return 1;
322 }
323
324 XAudio2BlankSound();
325 XAudio2SetVolume();
326
327 if (FAILED(pSourceVoice->Start(0))) {
328 return 1;
329 }
330 return 0;
331 }
332
XAudio2Stop()333 static int XAudio2Stop()
334 {
335 if (!bAudOkay) {
336 return 1;
337 }
338
339 if (pSourceVoice) {
340 pSourceVoice->Stop(0);
341 }
342
343 return 0;
344 }
345
XAudio2SetVolume()346 static int XAudio2SetVolume()
347 {
348 if (nAudVolume == 10000) {
349 nXAudio2Vol = 1.0f;
350 } else {
351 if (nAudVolume == 0) {
352 nXAudio2Vol = 0.0f;
353 } else {
354 nXAudio2Vol = 1.0f - (1.0f * pow(10.0, nAudVolume / -5000.0)) + 0.01f;
355 }
356 }
357
358 if (nXAudio2Vol < 0.0f) {
359 nXAudio2Vol = 0.0f;
360 }
361
362 if (!pSourceVoice) {
363 return 0;
364 }
365
366 pSourceVoice->SetVolume(nXAudio2Vol);
367
368 return 1; // 1 succeeds here.
369 }
370
XAudio2GetSettings(InterfaceInfo * pInfo)371 static int XAudio2GetSettings(InterfaceInfo* pInfo)
372 {
373 TCHAR szString[MAX_PATH] = _T("");
374 _sntprintf(szString, MAX_PATH, _T("Audio is delayed by approx. %ims"), int(100000.0 / (nXAudio2Fps / (nAudSegCount - 1.0))));
375 IntInfoAddStringModule(pInfo, szString);
376 return 0;
377 }
378
379 struct AudOut AudOutXAudio2 = { XAudio2BlankSound, XAudio2Check, XAudio2Init, XAudio2SetCallback, XAudio2Play, XAudio2Stop, XAudio2Exit, XAudio2SetVolume, XAudio2GetSettings, _T("XAudio2 audio output") };
380 //#else
381 //struct AudOut AudOutXAudio2 = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, _T("XAudio2 audio output") };
382 //#endif
383