Add: INR currency (#8136)
[openttd-github.git] / src / sound / xaudio2_s.cpp
blob8b9afbd1b15e0e5540e1aac0f34d6be64a86a4e7
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 */
8 /** @file xaudio2_s.cpp XAudio2 sound driver. */
10 #ifdef WITH_XAUDIO2
12 #include "../stdafx.h"
13 #include "../openttd.h"
14 #include "../driver.h"
15 #include "../mixer.h"
16 #include "../debug.h"
17 #include "../core/alloc_func.hpp"
18 #include "../core/bitmath_func.hpp"
19 #include "../core/math_func.hpp"
21 // Windows 8 SDK required for XAudio2
22 #undef NTDDI_VERSION
23 #undef _WIN32_WINNT
25 #define NTDDI_VERSION NTDDI_WIN8
26 #define _WIN32_WINNT _WIN32_WINNT_WIN8
28 #include "xaudio2_s.h"
30 #include <windows.h>
31 #include <mmsystem.h>
32 #include <wrl\client.h>
33 #include <xaudio2.h>
35 using Microsoft::WRL::ComPtr;
37 #include "../os/windows/win32.h"
38 #include "../safeguards.h"
40 // Definition of the "XAudio2Create" call used to initialise XAudio2
41 typedef HRESULT(__stdcall *API_XAudio2Create)(_Outptr_ IXAudio2** ppXAudio2, UINT32 Flags, XAUDIO2_PROCESSOR XAudio2Processor);
43 static FSoundDriver_XAudio2 iFSoundDriver_XAudio2;
45 /**
46 * Implementation of the IXAudio2VoiceCallback interface.
47 * Provides buffered audio to XAudio2 from the OpenTTD mixer.
49 class StreamingVoiceContext : public IXAudio2VoiceCallback
51 private:
52 int bufferLength;
53 char *buffer;
55 public:
56 IXAudio2SourceVoice* SourceVoice;
58 StreamingVoiceContext(int bufferLength)
60 this->bufferLength = bufferLength;
61 this->buffer = MallocT<char>(bufferLength);
64 virtual ~StreamingVoiceContext()
66 free(this->buffer);
69 HRESULT SubmitBuffer()
71 // Ensure we do have a valid voice
72 if (this->SourceVoice == nullptr)
74 return E_FAIL;
77 MxMixSamples(this->buffer, this->bufferLength / 4);
79 XAUDIO2_BUFFER buf = { 0 };
80 buf.AudioBytes = this->bufferLength;
81 buf.pAudioData = (const BYTE *) this->buffer;
83 return SourceVoice->SubmitSourceBuffer(&buf);
86 STDMETHOD_(void, OnVoiceProcessingPassStart)(UINT32) override
90 STDMETHOD_(void, OnVoiceProcessingPassEnd)() override
94 STDMETHOD_(void, OnStreamEnd)() override
98 STDMETHOD_(void, OnBufferStart)(void*) override
102 STDMETHOD_(void, OnBufferEnd)(void*) override
104 SubmitBuffer();
107 STDMETHOD_(void, OnLoopEnd)(void*) override
111 STDMETHOD_(void, OnVoiceError)(void*, HRESULT) override
116 static HMODULE _xaudio_dll_handle;
117 static IXAudio2SourceVoice* _source_voice = nullptr;
118 static IXAudio2MasteringVoice* _mastering_voice = nullptr;
119 static ComPtr<IXAudio2> _xaudio2;
120 static StreamingVoiceContext* _voice_context = nullptr;
123 * Initialises the XAudio2 driver.
125 * @param parm Driver parameters.
126 * @return An error message if unsuccessful, or nullptr otherwise.
129 const char *SoundDriver_XAudio2::Start(const char * const *parm)
131 HRESULT hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
133 if (FAILED(hr))
135 DEBUG(driver, 0, "xaudio2_s: CoInitializeEx failed (%08x)", hr);
136 return "Failed to initialise COM";
139 _xaudio_dll_handle = LoadLibraryA(XAUDIO2_DLL_A);
141 if (_xaudio_dll_handle == nullptr)
143 CoUninitialize();
145 DEBUG(driver, 0, "xaudio2_s: Unable to load " XAUDIO2_DLL_A);
146 return "Failed to load XAudio2 DLL";
149 API_XAudio2Create xAudio2Create = (API_XAudio2Create) GetProcAddress(_xaudio_dll_handle, "XAudio2Create");
151 if (xAudio2Create == nullptr)
153 FreeLibrary(_xaudio_dll_handle);
154 CoUninitialize();
156 DEBUG(driver, 0, "xaudio2_s: Unable to find XAudio2Create function in DLL");
157 return "Failed to load XAudio2 DLL";
160 // Create the XAudio engine
161 UINT32 flags = 0;
162 hr = xAudio2Create(_xaudio2.GetAddressOf(), flags, XAUDIO2_DEFAULT_PROCESSOR);
164 if (FAILED(hr))
166 FreeLibrary(_xaudio_dll_handle);
167 CoUninitialize();
169 DEBUG(driver, 0, "xaudio2_s: XAudio2Create failed (%08x)", hr);
170 return "Failed to inititialise the XAudio2 engine";
173 // Create a mastering voice
174 hr = _xaudio2->CreateMasteringVoice(&_mastering_voice);
176 if (FAILED(hr))
178 _xaudio2.Reset();
179 FreeLibrary(_xaudio_dll_handle);
180 CoUninitialize();
182 DEBUG(driver, 0, "xaudio2_s: CreateMasteringVoice failed (%08x)", hr);
183 return "Failed to create a mastering voice";
186 // Create a source voice to stream our audio
187 WAVEFORMATEX wfex;
189 wfex.wFormatTag = WAVE_FORMAT_PCM;
190 wfex.nChannels = 2;
191 wfex.wBitsPerSample = 16;
192 wfex.nSamplesPerSec = GetDriverParamInt(parm, "hz", 44100);
193 wfex.nBlockAlign = (wfex.nChannels * wfex.wBitsPerSample) / 8;
194 wfex.nAvgBytesPerSec = wfex.nSamplesPerSec * wfex.nBlockAlign;
196 // Limit buffer size to prevent overflows
197 int bufsize = GetDriverParamInt(parm, "bufsize", 8192);
198 bufsize = min(bufsize, UINT16_MAX);
200 _voice_context = new StreamingVoiceContext(bufsize * 4);
202 if (_voice_context == nullptr)
204 _mastering_voice->DestroyVoice();
205 _xaudio2.Reset();
206 FreeLibrary(_xaudio_dll_handle);
207 CoUninitialize();
209 return "Failed to create streaming voice context";
212 hr = _xaudio2->CreateSourceVoice(&_source_voice, &wfex, 0, 1.0f, _voice_context);
214 if (FAILED(hr))
216 _mastering_voice->DestroyVoice();
217 _xaudio2.Reset();
218 FreeLibrary(_xaudio_dll_handle);
219 CoUninitialize();
221 DEBUG(driver, 0, "xaudio2_s: CreateSourceVoice failed (%08x)", hr);
222 return "Failed to create a source voice";
225 _voice_context->SourceVoice = _source_voice;
226 hr = _source_voice->Start(0, 0);
228 if (FAILED(hr))
230 DEBUG(driver, 0, "xaudio2_s: _source_voice->Start failed (%08x)", hr);
232 Stop();
233 return "Failed to start the source voice";
236 MxInitialize(wfex.nSamplesPerSec);
238 // Submit the first buffer
239 hr = _voice_context->SubmitBuffer();
241 if (FAILED(hr))
243 DEBUG(driver, 0, "xaudio2_s: _voice_context->SubmitBuffer failed (%08x)", hr);
245 Stop();
246 return "Failed to submit the first audio buffer";
249 return nullptr;
253 * Terminates the XAudio2 driver.
255 void SoundDriver_XAudio2::Stop()
257 // Clean up XAudio2
258 _source_voice->DestroyVoice();
260 delete _voice_context;
261 _voice_context = nullptr;
263 _mastering_voice->DestroyVoice();
265 _xaudio2.Reset();
267 FreeLibrary(_xaudio_dll_handle);
268 CoUninitialize();
271 #endif