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