1 /**
2 * @file driver_openal.cpp
3 * OpenAL audio plugin. @ingroup dsopenal
4 *
5 * @bug Not 64bit clean: In function 'DS_SFX_CreateBuffer': cast to pointer from integer of different size
6 * @bug Not 64bit clean: In function 'DS_SFX_DestroyBuffer': cast to pointer from integer of different size
7 * @bug Not 64bit clean: In function 'DS_SFX_Play': cast to pointer from integer of different size
8 * @bug Not 64bit clean: In function 'DS_SFX_Stop': cast to pointer from integer of different size
9 * @bug Not 64bit clean: In function 'DS_SFX_Refresh': cast to pointer from integer of different size
10 * @bug Not 64bit clean: In function 'DS_SFX_Set': cast to pointer from integer of different size
11 * @bug Not 64bit clean: In function 'DS_SFX_Setv': cast to pointer from integer of different size
12 *
13 * @authors Copyright © 2003-2017 Jaakko Keränen <jaakko.keranen@iki.fi>
14 * @authors Copyright © 2006-2009 Daniel Swanson <danij@dengine.net>
15 *
16 * @par License
17 * GPL: http://www.gnu.org/licenses/gpl.html
18 *
19 * <small>This program is free software; you can redistribute it and/or modify
20 * it under the terms of the GNU General Public License as published by the
21 * Free Software Foundation; either version 2 of the License, or (at your
22 * option) any later version. This program is distributed in the hope that it
23 * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
24 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
25 * Public License for more details. You should have received a copy of the GNU
26 * General Public License along with this program; if not, write to the Free
27 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
28 * 02110-1301 USA</small>
29 */
30
31 #ifdef WIN32
32 # define WIN32_LEAN_AND_MEAN
33 # include <windows.h>
34 #endif
35
36 #ifdef MSVC
37 # pragma warning (disable: 4244)
38 #endif
39
40 #ifdef HAVE_AL_H
41 # include <al.h>
42 # include <alc.h>
43 #elif defined (DENG_IOS)
44 # include <OpenAL/al.h>
45 # include <OpenAL/alc.h>
46 #else
47 # include <AL/al.h>
48 # include <AL/alc.h>
49 #endif
50 #include <stdio.h>
51 #include <cassert>
52 #include <iostream>
53 #include <cstring>
54 #include <cmath>
55
56 #include "api_audiod.h"
57 #include "api_audiod_sfx.h"
58 #include "doomsday.h"
59
60 #include <de/c_wrapper.h>
61
62 DENG_DECLARE_API(Con);
63
64 #define SRC(buf) ( (ALuint) PTR2INT(buf->ptr3D) )
65 #define BUF(buf) ( (ALuint) PTR2INT(buf->ptr) )
66
67 //enum { VX, VY, VZ };
68
69 #ifdef WIN32
70 ALenum(*EAXGet) (const struct _GUID* propertySetID, ALuint prop, ALuint source, ALvoid* value, ALuint size);
71 ALenum(*EAXSet) (const struct _GUID* propertySetID, ALuint prop, ALuint source, ALvoid* value, ALuint size);
72 #endif
73
74 // Doomsday expects symbols to be exported without mangling.
75
76 extern "C" {
77
78 int DS_Init(void);
79 void DS_Shutdown(void);
80 void DS_Event(int type);
81
82 int DS_SFX_Init(void);
83 sfxbuffer_t* DS_SFX_CreateBuffer(int flags, int bits, int rate);
84 void DS_SFX_DestroyBuffer(sfxbuffer_t* buf);
85 void DS_SFX_Load(sfxbuffer_t* buf, struct sfxsample_s* sample);
86 void DS_SFX_Reset(sfxbuffer_t* buf);
87 void DS_SFX_Play(sfxbuffer_t* buf);
88 void DS_SFX_Stop(sfxbuffer_t* buf);
89 void DS_SFX_Refresh(sfxbuffer_t* buf);
90 void DS_SFX_Set(sfxbuffer_t* buf, int prop, float value);
91 void DS_SFX_Setv(sfxbuffer_t* buf, int prop, float* values);
92 void DS_SFX_Listener(int prop, float value);
93 void DS_SFX_Listenerv(int prop, float* values);
94 int DS_SFX_Getv(int prop, void* values);
95
96 } // extern "C"
97
98 #ifdef WIN32
99 // EAX 2.0 GUIDs
100 struct _GUID DSPROPSETID_EAX20_ListenerProperties = {
101 0x306a6a8, 0xb224, 0x11d2, {0x99, 0xe5, 0x0, 0x0, 0xe8, 0xd8, 0xc7, 0x22}
102 };
103 struct _GUID DSPROPSETID_EAX20_BufferProperties = {
104 0x306a6a7, 0xb224, 0x11d2, {0x99, 0xe5, 0x0, 0x0, 0xe8, 0xd8, 0xc7, 0x22}
105 };
106 #endif
107
108 static dd_bool initOk = false;
109 static dd_bool hasEAX = false;
110 static float unitsPerMeter = 1;
111 static float headYaw, headPitch; // In radians.
112 static ALCdevice* device = 0;
113 static ALCcontext* context = 0;
114
115 #ifdef DENG_DSOPENAL_DEBUG
116 # define DSOPENAL_TRACE(args) std::cerr << "[dsOpenAL] " << args << std::endl;
117 #else
118 # define DSOPENAL_TRACE(args)
119 #endif
120
121 #define DSOPENAL_ERRCHECK(errorcode) \
122 error(errorcode, __FILE__, __LINE__)
123
error(ALenum errorCode,const char * file,int line)124 static int error(ALenum errorCode, const char* file, int line)
125 {
126 if(errorCode == AL_NO_ERROR) return false;
127 std::cerr << "[dsOpenAL] Error at " << file << ", line " << line
128 << ": (" << (int)errorCode << ") " << (const char*)alGetString(errorCode);
129 return true;
130 }
131
loadExtensions(void)132 static void loadExtensions(void)
133 {
134 #ifdef WIN32
135 // Check for EAX 2.0.
136 hasEAX = alIsExtensionPresent((ALchar*) "EAX2.0");
137 if(hasEAX)
138 {
139 EAXGet = (ALenum (*)(const struct _GUID*, ALuint, ALuint, ALvoid*, ALuint))alGetProcAddress("EAXGet");
140 EAXSet = (ALenum (*)(const struct _GUID*, ALuint, ALuint, ALvoid*, ALuint))alGetProcAddress("EAXSet");
141 if(!EAXGet || !EAXSet)
142 hasEAX = false;
143 }
144 #else
145 hasEAX = false;
146 #endif
147 }
148
DS_Init(void)149 int DS_Init(void)
150 {
151 // Already initialized?
152 if(initOk) return true;
153
154 // Open the default playback device.
155 device = alcOpenDevice(NULL);
156 if(!device)
157 {
158 App_Log(DE2_AUDIO_ERROR, "OpenAL init failed (using default playback device)");
159 return false;
160 }
161
162 // Create and make current a new context.
163 alcMakeContextCurrent(context = alcCreateContext(device, NULL));
164 DSOPENAL_ERRCHECK(alGetError());
165
166 // Attempt to load and configure the EAX extensions.
167 loadExtensions();
168
169 // Configure the listener and global OpenAL properties/state.
170 alListenerf(AL_GAIN, 1);
171 alDistanceModel(AL_INVERSE_DISTANCE_CLAMPED);
172 headYaw = headPitch = 0;
173 unitsPerMeter = 36;
174
175 // Everything is OK.
176 DSOPENAL_TRACE("DS_Init: OpenAL initialized%s." << (hasEAX? " (EAX 2.0 available)" : ""));
177 initOk = true;
178 return true;
179 }
180
DS_Shutdown(void)181 void DS_Shutdown(void)
182 {
183 if(!initOk) return;
184
185 alcMakeContextCurrent(NULL);
186 alcDestroyContext(context);
187 alcCloseDevice(device);
188
189 context = NULL;
190 device = NULL;
191 initOk = false;
192 }
193
DS_Event(int)194 void DS_Event(int /*type*/)
195 {
196 // Not supported.
197 }
198
DS_SFX_Init(void)199 int DS_SFX_Init(void)
200 {
201 return true;
202 }
203
DS_SFX_CreateBuffer(int flags,int bits,int rate)204 sfxbuffer_t* DS_SFX_CreateBuffer(int flags, int bits, int rate)
205 {
206 sfxbuffer_t* buf;
207 ALuint bufName, srcName;
208
209 // Create a new buffer and a new source.
210 alGenBuffers(1, &bufName);
211 if(DSOPENAL_ERRCHECK(alGetError()))
212 return NULL;
213
214 alGenSources(1, &srcName);
215 if(DSOPENAL_ERRCHECK(alGetError()))
216 {
217 alDeleteBuffers(1, &bufName);
218 return NULL;
219 }
220
221 /*
222 // Attach the buffer to the source.
223 alSourcei(srcName, AL_BUFFER, bufName);
224 if(DSOPENAL_ERRCHECK(alGetError()))
225 {
226 alDeleteSources(1, &srcName);
227 alDeleteBuffers(1, &bufName);
228 return NULL;
229 }
230 */
231
232 if(!(flags & SFXBF_3D))
233 {
234 // 2D sounds are around the listener.
235 alSourcei(srcName, AL_SOURCE_RELATIVE, AL_TRUE);
236 alSourcef(srcName, AL_ROLLOFF_FACTOR, 0);
237 }
238
239 // Create the buffer object.
240 buf = static_cast<sfxbuffer_t*>(Z_Calloc(sizeof(*buf), PU_APPSTATIC, 0));
241
242 buf->ptr = INT2PTR(void, bufName);
243 buf->ptr3D = INT2PTR(void, srcName);
244 buf->bytes = bits / 8;
245 buf->rate = rate;
246 buf->flags = flags;
247 buf->freq = rate; // Modified by calls to Set(SFXBP_FREQUENCY).
248
249 return buf;
250 }
251
DS_SFX_DestroyBuffer(sfxbuffer_t * buf)252 void DS_SFX_DestroyBuffer(sfxbuffer_t* buf)
253 {
254 ALuint srcName, bufName;
255
256 if(!buf) return;
257
258 srcName = SRC(buf);
259 bufName = BUF(buf);
260
261 alDeleteSources(1, &srcName);
262 alDeleteBuffers(1, &bufName);
263
264 Z_Free(buf);
265 }
266
DS_SFX_Load(sfxbuffer_t * buf,struct sfxsample_s * sample)267 void DS_SFX_Load(sfxbuffer_t* buf, struct sfxsample_s* sample)
268 {
269 if(!buf || !sample) return;
270
271 // Does the buffer already have a sample loaded?
272 if(buf->sample)
273 {
274 // Is the same one?
275 if(buf->sample->id == sample->id)
276 return; // No need to reload.
277 }
278
279 // Make sure its not bound right now.
280 alSourcei(SRC(buf), AL_BUFFER, 0);
281
282 alBufferData(BUF(buf),
283 sample->bytesPer == 1 ? AL_FORMAT_MONO8 : AL_FORMAT_MONO16,
284 sample->data, sample->size, sample->rate);
285
286 if(DSOPENAL_ERRCHECK(alGetError()))
287 {
288 /// @todo What to do?
289 }
290 buf->sample = sample;
291 }
292
293 /**
294 * Stops the buffer and makes it forget about its sample.
295 */
DS_SFX_Reset(sfxbuffer_t * buf)296 void DS_SFX_Reset(sfxbuffer_t* buf)
297 {
298 if(!buf) return;
299
300 DS_SFX_Stop(buf);
301 alSourcei(SRC(buf), AL_BUFFER, 0);
302 buf->sample = NULL;
303 }
304
DS_SFX_Play(sfxbuffer_t * buf)305 void DS_SFX_Play(sfxbuffer_t* buf)
306 {
307 ALuint source;
308
309 // Playing is quite impossible without a sample.
310 if(!buf || !buf->sample) return;
311
312 source = SRC(buf);
313 alSourcei(source, AL_BUFFER, BUF(buf));
314 alSourcei(source, AL_LOOPING, (buf->flags & SFXBF_REPEAT) != 0);
315 alSourcePlay(source);
316 DSOPENAL_ERRCHECK(alGetError());
317
318 // The buffer is now playing.
319 buf->flags |= SFXBF_PLAYING;
320 }
321
DS_SFX_Stop(sfxbuffer_t * buf)322 void DS_SFX_Stop(sfxbuffer_t* buf)
323 {
324 if(!buf || !buf->sample) return;
325
326 alSourceRewind(SRC(buf));
327 buf->flags &= ~SFXBF_PLAYING;
328 }
329
DS_SFX_Refresh(sfxbuffer_t * buf)330 void DS_SFX_Refresh(sfxbuffer_t* buf)
331 {
332 ALint state;
333
334 if(!buf || !buf->sample) return;
335
336 alGetSourcei(SRC(buf), AL_SOURCE_STATE, &state);
337 if(state == AL_STOPPED)
338 {
339 buf->flags &= ~SFXBF_PLAYING;
340 }
341 }
342
343 /**
344 * @param yaw Yaw in radians.
345 * @param pitch Pitch in radians.
346 * @param front Ptr to front vector, can be @c NULL.
347 * @param up Ptr to up vector, can be @c NULL.
348 */
vectors(float yaw,float pitch,float * front,float * up)349 static void vectors(float yaw, float pitch, float* front, float* up)
350 {
351 if(!front && !up)
352 return; // Nothing to do.
353
354 if(front)
355 {
356 front[VX] = (float) (cos(yaw) * cos(pitch));
357 front[VZ] = (float) (sin(yaw) * cos(pitch));
358 front[VY] = (float) sin(pitch);
359 }
360
361 if(up)
362 {
363 up[VX] = (float) (-cos(yaw) * sin(pitch));
364 up[VZ] = (float) (-sin(yaw) * sin(pitch));
365 up[VY] = (float) cos(pitch);
366 }
367 }
368
369 /**
370 * Pan is linear, from -1 to 1. 0 is in the middle.
371 */
setPan(ALuint source,float pan)372 static void setPan(ALuint source, float pan)
373 {
374 float pos[3];
375
376 vectors((float) (headYaw - pan * DD_PI / 2), headPitch, pos, 0);
377 alSourcefv(source, AL_POSITION, pos);
378 }
379
DS_SFX_Set(sfxbuffer_t * buf,int prop,float value)380 void DS_SFX_Set(sfxbuffer_t* buf, int prop, float value)
381 {
382 ALuint source;
383
384 if(!buf) return;
385
386 source = SRC(buf);
387
388 switch(prop)
389 {
390 case SFXBP_VOLUME:
391 alSourcef(source, AL_GAIN, value);
392 break;
393
394 case SFXBP_FREQUENCY: {
395 unsigned int dw = (int) (buf->rate * value);
396 if(dw != buf->freq) // Don't set redundantly.
397 {
398 buf->freq = dw;
399 alSourcef(source, AL_PITCH, value);
400 }
401 break; }
402
403 case SFXBP_PAN:
404 setPan(source, value);
405 break;
406
407 case SFXBP_MIN_DISTANCE:
408 alSourcef(source, AL_REFERENCE_DISTANCE, value / unitsPerMeter);
409 break;
410
411 case SFXBP_MAX_DISTANCE:
412 alSourcef(source, AL_MAX_DISTANCE, value / unitsPerMeter);
413 break;
414
415 case SFXBP_RELATIVE_MODE:
416 alSourcei(source, AL_SOURCE_RELATIVE, value ? AL_TRUE : AL_FALSE);
417 break;
418
419 default: break;
420 }
421 }
422
DS_SFX_Setv(sfxbuffer_t * buf,int prop,float * values)423 void DS_SFX_Setv(sfxbuffer_t* buf, int prop, float* values)
424 {
425 ALuint source;
426
427 if(!buf || !values) return;
428
429 source = SRC(buf);
430
431 switch(prop)
432 {
433 case SFXBP_POSITION:
434 alSource3f(source, AL_POSITION, values[VX] / unitsPerMeter,
435 values[VZ] / unitsPerMeter, values[VY] / unitsPerMeter);
436 break;
437
438 case SFXBP_VELOCITY:
439 alSource3f(source, AL_VELOCITY, values[VX] / unitsPerMeter,
440 values[VZ] / unitsPerMeter, values[VY] / unitsPerMeter);
441 break;
442
443 default: break;
444 }
445 }
446
DS_SFX_Listener(int prop,float value)447 void DS_SFX_Listener(int prop, float value)
448 {
449 switch(prop)
450 {
451 case SFXLP_UNITS_PER_METER:
452 unitsPerMeter = value;
453 break;
454
455 case SFXLP_DOPPLER:
456 alDopplerFactor(value);
457 break;
458
459 default: break;
460 }
461 }
462
DS_SFX_Listenerv(int prop,float * values)463 void DS_SFX_Listenerv(int prop, float* values)
464 {
465 float ori[6];
466
467 if(!values) return;
468
469 switch(prop)
470 {
471 case SFXLP_PRIMARY_FORMAT:
472 // No need to concern ourselves with this kind of things...
473 break;
474
475 case SFXLP_POSITION:
476 alListener3f(AL_POSITION, values[VX] / unitsPerMeter,
477 values[VZ] / unitsPerMeter, values[VY] / unitsPerMeter);
478 break;
479
480 case SFXLP_VELOCITY:
481 alListener3f(AL_VELOCITY, values[VX] / unitsPerMeter,
482 values[VZ] / unitsPerMeter, values[VY] / unitsPerMeter);
483 break;
484
485 case SFXLP_ORIENTATION:
486 vectors(headYaw = (float) (values[VX] / 180 * DD_PI),
487 headPitch = (float) (values[VY] / 180 * DD_PI),
488 ori, ori + 3);
489 alListenerfv(AL_ORIENTATION, ori);
490 break;
491
492 case SFXLP_REVERB: // Not supported.
493 break;
494
495 default:
496 DS_SFX_Listener(prop, 0);
497 break;
498 }
499 }
500
DS_SFX_Getv(int,void *)501 int DS_SFX_Getv(int /*prop*/, void* /*values*/)
502 {
503 // Stub.
504 return 0;
505 }
506
507 /**
508 * Declares the type of the plugin so the engine knows how to treat it. Called
509 * automatically when the plugin is loaded.
510 */
deng_LibraryType(void)511 DENG_EXTERN_C DENG_VISIBLE_SYMBOL const char* deng_LibraryType(void)
512 {
513 return "deng-plugin/audio";
514 }
515
516 DENG_API_EXCHANGE(
517 DENG_GET_API(DE_API_CONSOLE, Con);
518 )
519