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