1 /*
2 Copyright (C) 2009-2010 wxLauncher Team
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 */
18
19 #include <wx/wx.h>
20 #include <wx/dynlib.h>
21 #include "generated/configure_launcher.h"
22 #include "apis/FlagListManager.h"
23 #include "apis/OpenALManager.h"
24 #include "apis/ProfileManager.h"
25 #include "global/ids.h"
26
27 #if USE_OPENAL
28 #include <al.h>
29 #include <alc.h>
30 #endif
31
32 #include "global/MemoryDebugging.h"
33
34 // from FSO, code/sound/openal.cpp, SVN r8840
35 // enumeration extension
36 #ifndef ALC_DEFAULT_ALL_DEVICES_SPECIFIER
37 #define ALC_DEFAULT_ALL_DEVICES_SPECIFIER 0x1012
38 #endif
39
40 #ifndef ALC_ALL_DEVICES_SPECIFIER
41 #define ALC_ALL_DEVICES_SPECIFIER 0x1013
42 #endif
43
44 const wxByte BUILD_CAP_NEW_SND = 1<<2;
45
46 #if USE_OPENAL
47 namespace OpenALMan {
48 wxDynamicLibrary OpenALLib;
49 bool isInitialized = false;
50 };
51 using namespace OpenALMan;
52 #endif
53
Initialize()54 bool OpenALMan::Initialize() {
55 #if USE_OPENAL
56 if ( isInitialized ) {
57 return true;
58 #if IS_APPLE
59 } else if ( OpenALLib.Load(_T("/System/Library/Frameworks/OpenAL.framework/OpenAL"),
60 wxDL_VERBATIM) ) {
61 isInitialized = true;
62 return true;
63 } else if ( OpenALLib.Load(_T("/Library/Frameworks/OpenAL.framework/OpenAL"),
64 wxDL_VERBATIM) ) {
65 isInitialized = true;
66 return true;
67 #else
68 } else if ( OpenALLib.Load(_T("OpenAL32")) ) {
69 isInitialized = true;
70 return true;
71 } else if ( OpenALLib.Load(_T("libopenal")) ) {
72 isInitialized = true;
73 return true;
74 } else if ( OpenALLib.Load(_T("OpenAL")) ) {
75 isInitialized = true;
76 return true;
77 #endif
78 } else {
79 return false;
80 }
81 #else
82 return false;
83 #endif
84 }
85
DeInitialize()86 bool OpenALMan::DeInitialize() {
87 #if USE_OPENAL
88 OpenALLib.Unload();
89 return true;
90 #else
91 return false;
92 #endif
93 }
94
IsInitialized()95 bool OpenALMan::IsInitialized() {
96 #if USE_OPENAL
97 return isInitialized;
98 #else
99 return false;
100 #endif
101 }
102
WasCompiledIn()103 bool OpenALMan::WasCompiledIn() {
104 #if USE_OPENAL
105 return true;
106 #else
107 return false;
108 #endif
109 }
110
111 #if USE_OPENAL
112 typedef const ALCchar* (ALC_APIENTRY *alcGetStringType)(ALCdevice*, ALenum);
113 typedef ALCboolean (ALC_APIENTRY *alcIsExtensionPresentType)(ALCdevice*, const ALchar*);
114 typedef const ALchar* (AL_APIENTRY *alGetStringType)(ALenum);
115 typedef ALenum (AL_APIENTRY *alGetErrorType)(void);
116 typedef ALCdevice* (ALC_APIENTRY *alcOpenDeviceType)(const ALCchar *);
117 typedef ALCboolean (ALC_APIENTRY *alcCloseDeviceType)(ALCdevice *);
118 typedef ALCcontext* (ALC_APIENTRY *alcCreateContextType)(const ALCdevice*, const ALCint*);
119 typedef ALCboolean (ALC_APIENTRY *alcMakeContextCurrentType)(ALCcontext*);
120 typedef void (ALC_APIENTRY *alcDestroyContextType)(ALCcontext*);
121
122 namespace OpenALMan {
123 template< typename funcPtrType>
124 funcPtrType GetOpenALFunctionPointer(const wxString& name, size_t line);
125 bool checkForALError_(size_t line);
126 };
127 #define ___GetOALFuncPtr(type, name, line) GetOpenALFunctionPointer<type>(_T(name), line)
128 #define GetOALFuncPtr(type, name) ___GetOALFuncPtr(type, #name, __LINE__)
129 #define checkForALError() OpenALMan::checkForALError_(__LINE__)
130
131 template< typename funcPtrType>
132 funcPtrType
GetOpenALFunctionPointer(const wxString & name,size_t line)133 OpenALMan::GetOpenALFunctionPointer(const wxString& name, size_t line) {
134 if ( !OpenALLib.HasSymbol(name) ) {
135 wxLogError(
136 _T("OpenAL does not have %s() for function containing line %d"),
137 name.c_str(), line);
138 return NULL;
139 }
140
141 funcPtrType pointer = NULL;
142
143 pointer = reinterpret_cast<funcPtrType>(
144 OpenALLib.GetSymbol(name));
145
146 if ( pointer == NULL ) {
147 wxLogError(_T("Unable to get %s() function from OpenAL, even though it apparently exists for function containing line %d"), name.c_str(), line);
148 return NULL;
149 }
150
151 return pointer;
152 }
153
checkForALError_(size_t line)154 bool OpenALMan::checkForALError_(size_t line) {
155 alGetErrorType getError = GetOALFuncPtr(alGetErrorType, alGetError);
156 if ( getError == NULL ) {
157 return false;
158 }
159 ALenum errorcode = (*getError)();
160 if ( errorcode == AL_NO_ERROR ) {
161 return true;
162 } else if ( errorcode == AL_INVALID_NAME ) {
163 wxLogError(_T("OpenAL:%d: a bad name (ID) was passed to an OpenAL function"), line);
164 } else if ( errorcode == AL_INVALID_ENUM ) {
165 wxLogError(_T("OpenAL:%d: an invalid enum value was passed to an OpenAL function"), line);
166 } else {
167 wxLogError(_T("OpenAL:%d: Unknown error number 0x%08x"), line, errorcode);
168 }
169 #if PLATFORM_HAS_BROKEN_OPENAL == 1
170 /** \todo a hack to fix certain OpenAL implementations that are not
171 clearing the errors correctly. */
172 return true;
173 #else
174 return false;
175 #endif
176 }
177 #endif
178
GetAvailableDevices(const ALenum deviceType)179 wxArrayString GetAvailableDevices(const ALenum deviceType) {
180 wxArrayString arr;
181 #if USE_OPENAL
182 wxCHECK_MSG(OpenALMan::IsInitialized(), arr,
183 _T("GetAvailableDevices called but OpenALMan not initialized"));
184
185 wxCHECK_MSG(deviceType == ALC_DEVICE_SPECIFIER ||
186 deviceType == ALC_CAPTURE_DEVICE_SPECIFIER, arr,
187 wxString::Format(_T("GetAvailableDevices given invalid specifier %d"),
188 deviceType));
189
190 ALenum adjustedDeviceType = deviceType;
191
192 alcIsExtensionPresentType isExtensionPresent =
193 GetOALFuncPtr(alcIsExtensionPresentType, alcIsExtensionPresent);
194
195 if ( isExtensionPresent != NULL ) {
196 if ( (*isExtensionPresent)(NULL, "ALC_ENUMERATION_EXT") != AL_TRUE ) {
197 wxLogFatalError(_T("OpenAL does not seem to support device enumeration."));
198 return arr;
199 }
200 } else {
201 return arr;
202 }
203
204 if ((deviceType == ALC_DEVICE_SPECIFIER) &&
205 (*isExtensionPresent)(NULL, "ALC_ENUMERATE_ALL_EXT") == AL_TRUE) {
206 adjustedDeviceType = ALC_ALL_DEVICES_SPECIFIER;
207 }
208
209 alcGetStringType GetString = GetOALFuncPtr(alcGetStringType, alcGetString);
210
211 if ( GetString != NULL ) {
212 const ALCchar* devices = (*GetString)(NULL, adjustedDeviceType);
213 if ( devices != NULL ) {
214 size_t len;
215 size_t offset = 0;
216 do {
217 len = strlen(devices+offset);
218 if ( len > 0 ) {
219 wxString device(devices+offset, wxConvUTF8);
220 arr.Add(device);
221 }
222 offset += len+1;
223 } while ( len != 0 );
224 } else {
225 wxLogError(_T("OpenAL gave NULL for list of devices."));
226 return arr;
227 }
228 } else {
229 return arr;
230 }
231 #endif
232 return arr;
233 }
234
GetAvailablePlaybackDevices()235 wxArrayString OpenALMan::GetAvailablePlaybackDevices() {
236 #if USE_OPENAL
237 wxCHECK_MSG(OpenALMan::IsInitialized(), wxArrayString(),
238 _T("GetAvailablePlaybackDevices called but OpenALMan not initialized"));
239 return GetAvailableDevices(ALC_DEVICE_SPECIFIER);
240 #else
241 return wxArrayString();
242 #endif
243 }
244
GetAvailableCaptureDevices()245 wxArrayString OpenALMan::GetAvailableCaptureDevices() {
246 #if USE_OPENAL
247 wxCHECK_MSG(OpenALMan::IsInitialized(), wxArrayString(),
248 _T("GetAvailableCaptureDevices called but OpenALMan not initialized"));
249 return GetAvailableDevices(ALC_CAPTURE_DEVICE_SPECIFIER);
250 #else
251 return wxArrayString();
252 #endif
253 }
254
GetSystemDefaultDevice(const ALenum deviceType)255 wxString GetSystemDefaultDevice(const ALenum deviceType) {
256 #if USE_OPENAL
257 wxCHECK_MSG( OpenALMan::IsInitialized(), wxEmptyString,
258 _T("GetSystemDefaultDevice called but OpenALMan not initialized"));
259 wxCHECK_MSG(deviceType == ALC_DEFAULT_DEVICE_SPECIFIER ||
260 deviceType == ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER, wxEmptyString,
261 wxString::Format(_T("GetSystemDefaultDevice given invalid specifier %d"),
262 deviceType));
263
264 ALenum adjustedDeviceType = deviceType;
265
266 alcIsExtensionPresentType isExtensionPresent =
267 GetOALFuncPtr(alcIsExtensionPresentType, alcIsExtensionPresent);
268
269 if ((isExtensionPresent != NULL) &&
270 (deviceType == ALC_DEFAULT_DEVICE_SPECIFIER) &&
271 ((*isExtensionPresent)(NULL, "ALC_ENUMERATE_ALL_EXT") == AL_TRUE)) {
272 adjustedDeviceType = ALC_DEFAULT_ALL_DEVICES_SPECIFIER;
273 }
274
275 alcGetStringType GetString = GetOALFuncPtr(alcGetStringType, alcGetString);
276
277 if ( GetString == NULL ) {
278 return wxEmptyString;
279 } else {
280 const ALCchar* defaultDevice = (*GetString)(NULL, adjustedDeviceType);
281 if ( defaultDevice == NULL ) {
282 wxLogError(_("Unable to get system default OpenAL %sdevice"),
283 (deviceType == ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER ?
284 _T("capture ") : wxEmptyString));
285 return wxEmptyString;
286 } else {
287 return wxString(defaultDevice, wxConvUTF8);
288 }
289 }
290 #else
291 return wxEmptyString;
292 #endif
293 }
294
GetSystemDefaultPlaybackDevice()295 wxString OpenALMan::GetSystemDefaultPlaybackDevice() {
296 #if USE_OPENAL
297 wxCHECK_MSG(OpenALMan::IsInitialized(), wxEmptyString,
298 _T("GetSystemDefaultPlaybackDevice called but OpenALMan not initialized"));
299 return GetSystemDefaultDevice(ALC_DEFAULT_DEVICE_SPECIFIER);
300 #else
301 return wxEmptyString;
302 #endif
303 }
304
GetSystemDefaultCaptureDevice()305 wxString OpenALMan::GetSystemDefaultCaptureDevice() {
306 #if USE_OPENAL
307 wxCHECK_MSG(OpenALMan::IsInitialized(), wxEmptyString,
308 _T("GetSystemDefaultCaptureDevice called but OpenALMan not initialized"));
309 return GetSystemDefaultDevice(ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER);
310 #else
311 return wxEmptyString;
312 #endif
313 }
314
315
GetCurrentVersion()316 wxString OpenALMan::GetCurrentVersion() {
317 #if USE_OPENAL
318 alGetStringType GetString = GetOALFuncPtr(alGetStringType,alGetString);
319
320 if ( GetString == NULL ) {
321 return _("Unknown version");
322 }
323
324 wxString selectedDevice;
325 ProMan::GetProfileManager()->ProfileRead(PRO_CFG_OPENAL_DEVICE, &selectedDevice);
326
327 // clear errors, I have not done any openAL stuff, so make sure that any
328 // errors that are active are because of me.
329 checkForALError();
330
331 alcOpenDeviceType OpenDevice =
332 GetOALFuncPtr(alcOpenDeviceType,alcOpenDevice);
333 if ( OpenDevice == NULL || checkForALError() == false) {
334 return _("Unable to open device");
335 }
336
337 ALCdevice* device = (*OpenDevice)(selectedDevice.char_str());
338 if ( device == NULL || checkForALError() == false ) {
339 wxLogError(_T("alcOpenDevice returned NULL for selected device '%s'"),
340 selectedDevice.c_str());
341 return _("Error opening device");
342 }
343
344 alcCreateContextType CreateContext =
345 GetOALFuncPtr(alcCreateContextType,alcCreateContext);
346 if ( OpenDevice == NULL || checkForALError() == false ) {
347 return _("Unable to open context on device");
348 }
349
350 ALCint attributes = 0;
351 ALCcontext* context = (*CreateContext)(device,NULL);
352 if ( context == NULL || checkForALError() == false) {
353 return _("Error in opening context");
354 }
355
356 alcMakeContextCurrentType MakeContextCurrent =
357 GetOALFuncPtr(alcMakeContextCurrentType,alcMakeContextCurrent);
358 if ( MakeContextCurrent == NULL || checkForALError() == false) {
359 return _("Unable to set context as current");
360 }
361
362 if ( (*MakeContextCurrent)(context) != ALC_TRUE || checkForALError() == false ) {
363 return _("Error in setting context as current");
364 }
365
366 const ALCchar* version = (*GetString)(AL_VERSION);
367 if ( !checkForALError() || version == NULL ) {
368 wxLogError(_T("OpenAL: Unable to retrieve Version String"));
369 return _("Unknown version");
370 }
371 wxString Version(version, wxConvUTF8);
372
373 // unset the current context
374 (*MakeContextCurrent)(NULL);
375
376 alcDestroyContextType DestroyContext =
377 GetOALFuncPtr(alcDestroyContextType,alcDestroyContext);
378 if ( DestroyContext == NULL ) {
379 return _("Unable to destroy context");
380 }
381
382 (*DestroyContext)(context);
383 context = NULL;
384 if ( checkForALError() == false ) {
385 return _("Error in destroying context");
386 }
387
388 alcCloseDeviceType CloseDevice =
389 GetOALFuncPtr(alcCloseDeviceType,alcCloseDevice);
390 if ( CloseDevice == NULL ) {
391 return _("Unable to close device");
392 }
393
394 (*CloseDevice)(device);
395 if ( checkForALError() == false ) {
396 return _("Error in closing device");
397 }
398
399 return wxString::Format(_("Detected OpenAL version: %s"), Version.c_str());
400 #else
401 return wxEmptyString;
402 #endif
403 }
404
405 // bits are adapted from GetCurrentVersion() and FSO, sound/ds.cpp, ds_init()
IsEFXSupported(const wxString & playbackDeviceName)406 bool OpenALMan::IsEFXSupported(const wxString& playbackDeviceName) {
407 #if USE_OPENAL
408 wxCHECK_MSG( OpenALMan::IsInitialized(), false,
409 _T("IsEFXSupported called but OpenALMan not initialized"));
410
411 if (playbackDeviceName.IsEmpty()) {
412 wxLogError(_T("IsEFXSupported: playback device name is empty"));
413 return false;
414 }
415
416 // clear errors, I have not done any openAL stuff, so make sure that any
417 // errors that are active are because of me.
418 checkForALError();
419
420 alcOpenDeviceType OpenDevice =
421 GetOALFuncPtr(alcOpenDeviceType, alcOpenDevice);
422
423 if (OpenDevice == NULL || checkForALError() == false) {
424 wxLogError(_T("IsEFXSupported: Unable to open device."));
425 return false;
426 }
427
428 ALCdevice* playbackDevice = (*OpenDevice)(playbackDeviceName.char_str());
429
430 if (playbackDevice == NULL || checkForALError() == false) {
431 wxLogError(
432 _T("IsEFXSupported: alcOpenDevice returned NULL when opening device '%s'"),
433 playbackDeviceName.c_str());
434 return false;
435 }
436
437 alcIsExtensionPresentType isExtensionPresent =
438 GetOALFuncPtr(alcIsExtensionPresentType, alcIsExtensionPresent);
439
440 if (isExtensionPresent == NULL || checkForALError() == false) {
441 wxLogError(
442 _T("IsEFXSupported: Could not get alcIsExtensionPresent function."));
443 return false;
444 }
445
446 bool hasEFX = (*isExtensionPresent)(playbackDevice, "ALC_EXT_EFX") == AL_TRUE;
447
448 if (checkForALError() == false) {
449 wxLogError(_T("IsEFXSupported: Error in checking for EFX extension"));
450 return false;
451 }
452
453 alcCloseDeviceType CloseDevice =
454 GetOALFuncPtr(alcCloseDeviceType, alcCloseDevice);
455
456 if (CloseDevice == NULL) {
457 wxLogError(_T("IsEFXSupported: Unable to close device."));
458 return false;
459 }
460
461 (*CloseDevice)(playbackDevice);
462
463 if (checkForALError() == false) {
464 wxLogError(_T("IsEFXSupported: Error in closing device"));
465 return false;
466 }
467
468 return hasEFX;
469 #else
470 return false;
471 #endif
472 }
473
BuildHasNewSoundCode()474 bool OpenALMan::BuildHasNewSoundCode() {
475 wxCHECK_MSG(OpenALMan::IsInitialized(), false,
476 _T("OpenALMan has not been initialized."));
477 wxCHECK_MSG(FlagListManager::IsInitialized(), false,
478 _T("FlagListManager has not been initialized."));
479 wxCHECK_MSG(FlagListManager::GetFlagListManager()->IsProcessingOK(), false,
480 _T("Flag file processing has not (yet) succeeded."));
481
482 return FlagListManager::GetFlagListManager()->GetBuildCaps() & BUILD_CAP_NEW_SND;
483 }
484