OpenTTD Source: src/sound/xaudio2_s.cpp Source File

OpenTTD Source 12.0-beta1

xaudio2_s.cpp
Go to the documentation of this file.
1 /*
2 * This file is part of OpenTTD.
3 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
4 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
5 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
6 */
7
10 #include "../stdafx.h"
11 #include "../openttd.h"
12 #include "../driver.h"
13 #include "../mixer.h"
14 #include "../debug.h"
15 #include "../core/alloc_func.hpp"
16 #include "../core/bitmath_func.hpp"
17 #include "../core/math_func.hpp"
18
19 // Windows 8 SDK required for XAudio2
20 #undef NTDDI_VERSION
21 #undef _WIN32_WINNT
22
23 #define NTDDI_VERSION NTDDI_WIN8
24 #define _WIN32_WINNT _WIN32_WINNT_WIN8
25
26 #include "xaudio2_s.h"
27
28 #include <windows.h>
29 #include <mmsystem.h>
30 #include <wrl\client.h>
31 #include <xaudio2.h>
32
33 using Microsoft::WRL::ComPtr;
34
35 #include "../os/windows/win32.h"
36 #include "../safeguards.h"
37
38 // Definition of the "XAudio2Create" call used to initialise XAudio2
39 typedef HRESULT(__stdcall *API_XAudio2Create)(_Outptr_ IXAudio2** ppXAudio2, UINT32 Flags, XAUDIO2_PROCESSOR XAudio2Processor);
40
41 static FSoundDriver_XAudio2 iFSoundDriver_XAudio2;
42
47 class StreamingVoiceContext : public IXAudio2VoiceCallback
48 {
49 private:
50 int bufferLength;
51 char *buffer;
52
53 public:
54 IXAudio2SourceVoice* SourceVoice;
55
56 StreamingVoiceContext(int bufferLength)
57 {
58 this->bufferLength = bufferLength;
59 this->buffer = MallocT<char>(bufferLength);
60 }
61
62 virtual ~StreamingVoiceContext()
63 {
64 free(this->buffer);
65 }
66
67 HRESULT SubmitBuffer()
68 {
69 // Ensure we do have a valid voice
70 if (this->SourceVoice == nullptr)
71 {
72 return E_FAIL;
73 }
74
75 MxMixSamples(this->buffer, this->bufferLength / 4);
76
77 XAUDIO2_BUFFER buf = { 0 };
78 buf.AudioBytes = this->bufferLength;
79 buf.pAudioData = (const BYTE *) this->buffer;
80
81 return SourceVoice->SubmitSourceBuffer(&buf);
82 }
83
84 STDMETHOD_(void, OnVoiceProcessingPassStart)(UINT32) override
85 {
86 }
87
88 STDMETHOD_(void, OnVoiceProcessingPassEnd)() override
89 {
90 }
91
92 STDMETHOD_(void, OnStreamEnd)() override
93 {
94 }
95
96 STDMETHOD_(void, OnBufferStart)(void*) override
97 {
98 }
99
100 STDMETHOD_(void, OnBufferEnd)(void*) override
101 {
102 SubmitBuffer();
103 }
104
105 STDMETHOD_(void, OnLoopEnd)(void*) override
106 {
107 }
108
109 STDMETHOD_(void, OnVoiceError)(void*, HRESULT) override
110 {
111 }
112 };
113
114 static HMODULE _xaudio_dll_handle;
115 static IXAudio2SourceVoice* _source_voice = nullptr;
116 static IXAudio2MasteringVoice* _mastering_voice = nullptr;
117 static ComPtr<IXAudio2> _xaudio2;
118 static StreamingVoiceContext* _voice_context = nullptr;
119
127 const char *SoundDriver_XAudio2::Start(const StringList &parm)
128 {
129 HRESULT hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
130
131 if (FAILED(hr))
132 {
133 Debug(driver, 0, "xaudio2_s: CoInitializeEx failed ({:08x})", (uint)hr);
134 return "Failed to initialise COM";
135 }
136
137 _xaudio_dll_handle = LoadLibraryA(XAUDIO2_DLL_A);
138
139 if (_xaudio_dll_handle == nullptr)
140 {
141 CoUninitialize();
142
143 Debug(driver, 0, "xaudio2_s: Unable to load " XAUDIO2_DLL_A);
144 return "Failed to load XAudio2 DLL";
145 }
146
147 API_XAudio2Create xAudio2Create = (API_XAudio2Create) GetProcAddress(_xaudio_dll_handle, "XAudio2Create");
148
149 if (xAudio2Create == nullptr)
150 {
151 FreeLibrary(_xaudio_dll_handle);
152 CoUninitialize();
153
154 Debug(driver, 0, "xaudio2_s: Unable to find XAudio2Create function in DLL");
155 return "Failed to load XAudio2 DLL";
156 }
157
158 // Create the XAudio engine
159 UINT32 flags = 0;
160 hr = xAudio2Create(_xaudio2.GetAddressOf(), flags, XAUDIO2_DEFAULT_PROCESSOR);
161
162 if (FAILED(hr))
163 {
164 FreeLibrary(_xaudio_dll_handle);
165 CoUninitialize();
166
167 Debug(driver, 0, "xaudio2_s: XAudio2Create failed ({:08x})", (uint)hr);
168 return "Failed to inititialise the XAudio2 engine";
169 }
170
171 // Create a mastering voice
172 hr = _xaudio2->CreateMasteringVoice(&_mastering_voice);
173
174 if (FAILED(hr))
175 {
176 _xaudio2.Reset();
177 FreeLibrary(_xaudio_dll_handle);
178 CoUninitialize();
179
180 Debug(driver, 0, "xaudio2_s: CreateMasteringVoice failed ({:08x})", (uint)hr);
181 return "Failed to create a mastering voice";
182 }
183
184 // Create a source voice to stream our audio
185 WAVEFORMATEX wfex;
186
187 wfex.wFormatTag = WAVE_FORMAT_PCM;
188 wfex.nChannels = 2;
189 wfex.wBitsPerSample = 16;
190 wfex.nSamplesPerSec = GetDriverParamInt(parm, "hz", 44100);
191 wfex.nBlockAlign = (wfex.nChannels * wfex.wBitsPerSample) / 8;
192 wfex.nAvgBytesPerSec = wfex.nSamplesPerSec * wfex.nBlockAlign;
193
194 // Limit buffer size to prevent overflows
195 int bufsize = GetDriverParamInt(parm, "bufsize", 8192);
196 bufsize = std::min<int>(bufsize, UINT16_MAX);
197
198 _voice_context = new StreamingVoiceContext(bufsize * 4);
199
200 if (_voice_context == nullptr)
201 {
202 _mastering_voice->DestroyVoice();
203 _xaudio2.Reset();
204 FreeLibrary(_xaudio_dll_handle);
205 CoUninitialize();
206
207 return "Failed to create streaming voice context";
208 }
209
210 hr = _xaudio2->CreateSourceVoice(&_source_voice, &wfex, 0, 1.0f, _voice_context);
211
212 if (FAILED(hr))
213 {
214 _mastering_voice->DestroyVoice();
215 _xaudio2.Reset();
216 FreeLibrary(_xaudio_dll_handle);
217 CoUninitialize();
218
219 Debug(driver, 0, "xaudio2_s: CreateSourceVoice failed ({:08x})", (uint)hr);
220 return "Failed to create a source voice";
221 }
222
223 _voice_context->SourceVoice = _source_voice;
224 hr = _source_voice->Start(0, 0);
225
226 if (FAILED(hr))
227 {
228 Debug(driver, 0, "xaudio2_s: _source_voice->Start failed ({:08x})", (uint)hr);
229
230 Stop();
231 return "Failed to start the source voice";
232 }
233
234 MxInitialize(wfex.nSamplesPerSec);
235
236 // Submit the first buffer
237 hr = _voice_context->SubmitBuffer();
238
239 if (FAILED(hr))
240 {
241 Debug(driver, 0, "xaudio2_s: _voice_context->SubmitBuffer failed ({:08x})", (uint)hr);
242
243 Stop();
244 return "Failed to submit the first audio buffer";
245 }
246
247 return nullptr;
248 }
249
253 void SoundDriver_XAudio2::Stop()
254 {
255 // Clean up XAudio2
256 _source_voice->DestroyVoice();
257
258 delete _voice_context;
259 _voice_context = nullptr;
260
261 _mastering_voice->DestroyVoice();
262
263 _xaudio2.Reset();
264
265 FreeLibrary(_xaudio_dll_handle);
266 CoUninitialize();
267 }
FSoundDriver_XAudio2
Factory for the XAudio2 sound driver.
Definition: xaudio2_s.h:25
StreamingVoiceContext
Implementation of the IXAudio2VoiceCallback interface.
Definition: xaudio2_s.cpp:47
StringList
std::vector< std::string > StringList
Type for a list of strings.
Definition: string_type.h:58
SoundDriver_XAudio2::Start
const char * Start(const StringList &param) override
Initialises the XAudio2 driver.
Definition: xaudio2_s.cpp:127
GetDriverParamInt
int GetDriverParamInt(const StringList &parm, const char *name, int def)
Get an integer parameter the list of parameters.
Definition: driver.cpp:73
SoundDriver_XAudio2::Stop
void Stop() override
Terminates the XAudio2 driver.
Definition: xaudio2_s.cpp:253
Debug
#define Debug(name, level, format_string,...)
Ouptut a line of debugging information.
Definition: debug.h:37
free
static void free(const void *ptr)
Version of the standard free that accepts const pointers.
Definition: stdafx.h:460
xaudio2_s.h