FS2_Open
Open source remastering of the Freespace 2 engine
openal.cpp
Go to the documentation of this file.
1 
2 #include "globalincs/pstypes.h"
3 #include "sound/openal.h"
4 #include "osapi/osregistry.h"
5 
6 #include <string>
7 #include <algorithm>
8 
9 #ifdef _WIN32
10 #define VC_EXTRALEAN
11 #include <windows.h>
12 #endif
13 
14 
15 static SCP_string Playback_device;
16 static SCP_string Capture_device;
17 
18 
19 enum {
23 };
24 
25 typedef struct OALdevice {
27  int type;
28  bool usable;
29 
31  type(OAL_DEVICE_GENERIC), usable(false)
32  {
33  }
34 
35  OALdevice(const char *name) :
36  device_name(name), type(OAL_DEVICE_GENERIC), usable(false)
37  {
38  }
39 } OALdevice;
40 
41 static SCP_vector<OALdevice> PlaybackDevices;
42 static SCP_vector<OALdevice> CaptureDevices;
43 
44 
45 // enumeration extension
46 #ifndef ALC_ALL_DEVICES_SPECIFIER
47 #define ALC_ALL_DEVICES_SPECIFIER 0x1013
48 #endif
49 
50 
51 //--------------------------------------------------------------------------
52 // openal_error_string()
53 //
54 // Returns the human readable error string if there is an error or NULL if not
55 //
56 const char *openal_error_string(int get_alc)
57 {
58  int i;
59 
60  if (get_alc) {
61  ALCdevice *device = alcGetContextsDevice( alcGetCurrentContext() );
62 
63  i = alcGetError(device);
64 
65  if ( i != ALC_NO_ERROR )
66  return (const char*) alcGetString(device, i);
67  }
68  else {
69  i = alGetError();
70 
71  if ( i != AL_NO_ERROR )
72  return (const char*)alGetString(i);
73  }
74 
75  return NULL;
76 }
77 
79 {
80  ALenum format = AL_INVALID_VALUE;
81 
82  if ( (n_channels < 1) || (n_channels > 2) ) {
83  return format;
84  }
85 
86  switch (bits) {
87  case 32:
88  format = (n_channels == 1) ? AL_FORMAT_MONO_FLOAT32 : AL_FORMAT_STEREO_FLOAT32;
89  break;
90 
91  case 16:
92  format = (n_channels == 1) ? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16;
93  break;
94 
95  case 8:
96  format = (n_channels == 1) ? AL_FORMAT_MONO8 : AL_FORMAT_STEREO8;
97  break;
98  }
99 
100  return format;
101 }
102 
103 static bool openal_device_sort_func(const OALdevice &d1, const OALdevice &d2)
104 {
105  if (d1.type > d2.type) {
106  return true;
107  }
108 
109  return false;
110 }
111 
112 static void find_playback_device()
113 {
114  const char *user_device = os_config_read_string( "Sound", "PlaybackDevice", NULL );
115  const char *default_device = (const char*) alcGetString( NULL, ALC_DEFAULT_DEVICE_SPECIFIER );
116 
117  // in case they are the same, we only want to test it once
118  if ( (user_device && default_device) && !strcmp(user_device, default_device) ) {
119  user_device = NULL;
120  }
121 
122  if ( alcIsExtensionPresent(NULL, (const ALCchar*)"ALC_ENUMERATION_EXT") == AL_TRUE ) {
123  const char *all_devices = NULL;
124 
125  if ( alcIsExtensionPresent(NULL, (const ALCchar*)"ALC_ENUMERATE_ALL_EXT") == AL_TRUE ) {
126  all_devices = (const char*) alcGetString(NULL, ALC_ALL_DEVICES_SPECIFIER);
127  } else {
128  all_devices = (const char*) alcGetString(NULL, ALC_DEVICE_SPECIFIER);
129  }
130 
131  const char *str_list = all_devices;
132  int ext_length = 0;
133 
134  if ( (str_list != NULL) && ((ext_length = strlen(str_list)) > 0) ) {
135  while (ext_length) {
136  OALdevice new_device(str_list);
137 
138  if (user_device && !strcmp(str_list, user_device)) {
139  new_device.type = OAL_DEVICE_USER;
140  } else if (default_device && !strcmp(str_list, default_device)) {
141  new_device.type = OAL_DEVICE_DEFAULT;
142  }
143 
144  PlaybackDevices.push_back( new_device );
145 
146  str_list += (ext_length + 1);
147  ext_length = strlen(str_list);
148  }
149  }
150  } else {
151  if (default_device) {
152  OALdevice new_device(default_device);
153  new_device.type = OAL_DEVICE_DEFAULT;
154 
155  PlaybackDevices.push_back( new_device );
156  }
157 
158  if (user_device) {
159  OALdevice new_device(user_device);
160  new_device.type = OAL_DEVICE_USER;
161 
162  PlaybackDevices.push_back( new_device );
163  }
164  }
165 
166  if ( PlaybackDevices.empty() ) {
167  return;
168  }
169 
170  std::sort( PlaybackDevices.begin(), PlaybackDevices.end(), openal_device_sort_func );
171 
172 
173  ALCdevice *device = NULL;
174  ALCcontext *context = NULL;
175 
176  // for each device that we have available, try and figure out which to use
177  for (size_t idx = 0; idx < PlaybackDevices.size(); idx++) {
178  OALdevice *pdev = &PlaybackDevices[idx];
179 
180  // open our specfic device
181  device = alcOpenDevice( (const ALCchar*) pdev->device_name.c_str() );
182 
183  if (device == NULL) {
184  continue;
185  }
186 
187  context = alcCreateContext(device, NULL);
188 
189  if (context == NULL) {
190  alcCloseDevice(device);
191  continue;
192  }
193 
194  alcMakeContextCurrent(context);
195  alcGetError(device);
196 
197  // check how many sources we can create
198  static const int MIN_SOURCES = 48; // MAX_CHANNELS + 16 spare
199  int si = 0;
200 
201  for (si = 0; si < MIN_SOURCES; si++) {
202  ALuint source_id = 0;
203  alGenSources(1, &source_id);
204 
205  if (alGetError() != AL_NO_ERROR) {
206  break;
207  }
208 
209  alDeleteSources(1, &source_id);
210  }
211 
212  if (si == MIN_SOURCES) {
213  // ok, it supports our minimum requirements
214  pdev->usable = true;
215 
216  // need this for the future
217  Playback_device = pdev->device_name;
218 
219  // done
220  break;
221  } else {
222  // clean up for next pass
223  alcMakeContextCurrent(NULL);
224  alcDestroyContext(context);
225  alcCloseDevice(device);
226 
227  context = NULL;
228  device = NULL;
229  }
230  }
231 
232  alcMakeContextCurrent(NULL);
233 
234  if (context) {
235  alcDestroyContext(context);
236  }
237 
238  if (device) {
239  alcCloseDevice(device);
240  }
241 }
242 
243 static void find_capture_device()
244 {
245  const char *user_device = os_config_read_string( "Sound", "CaptureDevice", NULL );
246  const char *default_device = (const char*) alcGetString( NULL, ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER );
247 
248  // in case they are the same, we only want to test it once
249  if ( (user_device && default_device) && !strcmp(user_device, default_device) ) {
250  user_device = NULL;
251  }
252 
253  if ( alcIsExtensionPresent(NULL, (const ALCchar*)"ALC_ENUMERATION_EXT") == AL_TRUE ) {
254  const char *all_devices = (const char*) alcGetString(NULL, ALC_CAPTURE_DEVICE_SPECIFIER);
255 
256  const char *str_list = all_devices;
257  int ext_length = 0;
258 
259  if ( (str_list != NULL) && ((ext_length = strlen(str_list)) > 0) ) {
260  while (ext_length) {
261  OALdevice new_device(str_list);
262 
263  if (user_device && !strcmp(str_list, user_device)) {
264  new_device.type = OAL_DEVICE_USER;
265  } else if (default_device && !strcmp(str_list, default_device)) {
266  new_device.type = OAL_DEVICE_DEFAULT;
267  }
268 
269  CaptureDevices.push_back( new_device );
270 
271  str_list += (ext_length + 1);
272  ext_length = strlen(str_list);
273  }
274  }
275  } else {
276  if (default_device) {
277  OALdevice new_device(default_device);
278  new_device.type = OAL_DEVICE_DEFAULT;
279 
280  CaptureDevices.push_back( new_device );
281  }
282 
283  if (user_device) {
284  OALdevice new_device(user_device);
285  new_device.type = OAL_DEVICE_USER;
286 
287  CaptureDevices.push_back( new_device );
288  }
289  }
290 
291  if ( CaptureDevices.empty() ) {
292  return;
293  }
294 
295  std::sort( CaptureDevices.begin(), CaptureDevices.end(), openal_device_sort_func );
296 
297 
298  // for each device that we have available, try and figure out which to use
299  for (size_t idx = 0; idx < CaptureDevices.size(); idx++) {
300  const ALCchar *device_name = CaptureDevices[idx].device_name.c_str();
301 
302  ALCdevice *device = alcCaptureOpenDevice(device_name, 22050, AL_FORMAT_MONO8, 22050 * 2);
303 
304  if (device == NULL) {
305  continue;
306  }
307 
308  if (alcGetError(device) != ALC_NO_ERROR) {
309  alcCaptureCloseDevice(device);
310  continue;
311  }
312 
313  // ok, we should be good with this one
314  Capture_device = CaptureDevices[idx].device_name;
315 
316  alcCaptureCloseDevice(device);
317 
318  break;
319  }
320 }
321 
322 // initializes hardware device from perferred/default/enumerated list
323 bool openal_init_device(SCP_string *playback, SCP_string *capture)
324 {
325  if ( !Playback_device.empty() ) {
326  if (playback) {
327  *playback = Playback_device;
328  }
329 
330  if (capture) {
331  *capture = Capture_device;
332  }
333 
334  return true;
335  }
336 
337  if (playback) {
338  playback->erase();
339  }
340 
341  if (capture) {
342  capture->erase();
343  }
344 
345  // initialize default setup first, for version check...
346 
347  ALCdevice *device = alcOpenDevice(NULL);
348 
349  if (device == NULL) {
350  return false;
351  }
352 
353  ALCcontext *context = alcCreateContext(device, NULL);
354 
355  if (context == NULL) {
356  alcCloseDevice(device);
357  return false;
358  }
359 
360  alcMakeContextCurrent(context);
361 
362  // version check (for 1.0 or 1.1)
363  ALCint AL_minor_version = 0;
364  alcGetIntegerv(NULL, ALC_MINOR_VERSION, sizeof(ALCint), &AL_minor_version);
365 
366  if (AL_minor_version < 1) {
367 #ifdef _WIN32
368  MessageBox(NULL, "OpenAL 1.1 or newer is required for proper operation. Please upgrade your OpenAL drivers, which\nare available at http://www.openal.org/downloads.html, and try running the game again.", NULL, MB_OK);
369 #else
370  printf("OpenAL 1.1 or newer is required for proper operation.\n");
371  printf("Please upgrade to a newer version if on OS X or switch\n");
372  printf("to OpenAL-Soft on Linux.\n");
373 #endif
374 
375  alcMakeContextCurrent(NULL);
376  alcDestroyContext(context);
377  alcCloseDevice(device);
378 
379  return false;
380  }
381 
382  alcGetError(device);
383 
384  // close default device
385  alcMakeContextCurrent(NULL);
386  alcDestroyContext(context);
387  alcCloseDevice(device);
388 
389 
390  // go through and find out what devices we actually want to use ...
391  find_playback_device();
392  find_capture_device();
393 
394  if ( Playback_device.empty() ) {
395  return false;
396  }
397 
398 
399 #ifndef NDEBUG
400  if ( !PlaybackDevices.empty() ) {
401  nprintf(("OpenAL", " Available Playback Devices:\n"));
402 
403  for (size_t idx = 0; idx < PlaybackDevices.size(); idx++) {
404  nprintf(("OpenAL", " %s", PlaybackDevices[idx].device_name.c_str()));
405 
406  if (PlaybackDevices[idx].type == OAL_DEVICE_USER) {
407  nprintf(("OpenAL", " *preferred*\n"));
408  } else if (PlaybackDevices[idx].type == OAL_DEVICE_DEFAULT) {
409  nprintf(("OpenAL", " *default*\n"));
410  } else {
411  nprintf(("OpenAL", "\n"));
412  }
413  }
414  }
415 
416  if ( !CaptureDevices.empty() ) {
417  if ( !PlaybackDevices.empty() ) {
418  nprintf(("OpenAL", "\n"));
419  }
420 
421  nprintf(("OpenAL", " Available Capture Devices:\n"));
422 
423  for (size_t idx = 0; idx < CaptureDevices.size(); idx++) {
424  nprintf(("OpenAL", " %s", CaptureDevices[idx].device_name.c_str()));
425 
426  if (CaptureDevices[idx].type == OAL_DEVICE_USER) {
427  nprintf(("OpenAL", " *preferred*\n"));
428  } else if (CaptureDevices[idx].type == OAL_DEVICE_DEFAULT) {
429  nprintf(("OpenAL", " *default*\n"));
430  } else {
431  nprintf(("OpenAL", "\n"));
432  }
433  }
434 
435  nprintf(("OpenAL", "\n"));
436  }
437 #endif
438 
439 
440  // cleanup
441  PlaybackDevices.clear();
442  CaptureDevices.clear();
443 
444 
445  if (playback) {
446  *playback = Playback_device;
447  }
448 
449  if (capture) {
450  *capture = Capture_device;
451  }
452 
453  return true;
454 }
455 
GLenum GLsizei GLenum format
Definition: Gl.h:1509
int i
Definition: multi_pxo.cpp:466
struct OALdevice OALdevice
ALuint *typedef ALuint *typedef ALenum
Definition: ds.cpp:133
const char * os_config_read_string(const char *section, const char *name, const char *default_value)
Definition: osregistry.cpp:322
std::basic_string< char, std::char_traits< char >, std::allocator< char > > SCP_string
Definition: vmallocator.h:21
GLenum type
Definition: Gl.h:1492
ALuint *typedef ALuint *typedef ALint
Definition: ds.cpp:133
#define nprintf(args)
Definition: pstypes.h:239
GLenum GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei const GLvoid * bits
Definition: Glext.h:10400
SCP_string device_name
Definition: openal.cpp:26
#define MB_OK
Definition: config.h:179
#define AL_FORMAT_STEREO_FLOAT32
Definition: openal.h:119
int idx
Definition: multiui.cpp:761
GLuint const GLchar * name
Definition: Glext.h:5608
bool usable
Definition: openal.cpp:28
OALdevice(const char *name)
Definition: openal.cpp:35
ALenum openal_get_format(ALint bits, ALint n_channels)
Definition: openal.cpp:78
int MessageBox(HWND h, const char *s1, const char *s2, int i)
bool openal_init_device(SCP_string *playback, SCP_string *capture)
Definition: openal.cpp:323
const char * openal_error_string(int get_alc)
Definition: openal.cpp:56
false
Definition: lua.cpp:6789
#define ALC_ALL_DEVICES_SPECIFIER
Definition: openal.cpp:47
#define AL_FORMAT_MONO_FLOAT32
Definition: openal.h:118
OALdevice()
Definition: openal.cpp:30
int type
Definition: openal.cpp:27