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