1 //**************************************************************************
2 //**
3 //** ## ## ## ## ## #### #### ### ###
4 //** ## ## ## ## ## ## ## ## ## ## #### ####
5 //** ## ## ## ## ## ## ## ## ## ## ## ## ## ##
6 //** ## ## ######## ## ## ## ## ## ## ## ### ##
7 //** ### ## ## ### ## ## ## ## ## ##
8 //** # ## ## # #### #### ## ##
9 //**
10 //** $Id: snd_al.cpp 3866 2008-11-17 18:58:49Z dj_jl $
11 //**
12 //** Copyright (C) 1999-2006 Jānis Legzdiņš
13 //**
14 //** This program is free software; you can redistribute it and/or
15 //** modify it under the terms of the GNU General Public License
16 //** as published by the Free Software Foundation; either version 2
17 //** of the License, or (at your option) any later version.
18 //**
19 //** This program is distributed in the hope that it will be useful,
20 //** but WITHOUT ANY WARRANTY; without even the implied warranty of
21 //** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 //** GNU General Public License for more details.
23 //**
24 //**************************************************************************
25
26 #ifdef _WIN32
27 #include "winlocal.h"
28 #else
29 #define INITGUID
30 #endif
31 #include <AL/al.h>
32 #include <AL/alc.h>
33 // Linux headers doesn't define this
34 #ifndef OPENAL
35 #define OPENAL
36 #endif
37 #include "eax.h"
38
39 #include "gamedefs.h"
40 #include "snd_local.h"
41
42 class VOpenALDevice : public VSoundDevice
43 {
44 private:
45 enum { MAX_VOICES = 256 };
46
47 enum { NUM_STRM_BUFFERS = 8 };
48 enum { STRM_BUFFER_SIZE = 1024 };
49
50 ALCdevice* Device;
51 ALCcontext* Context;
52 ALuint* Buffers;
53
54 bool supportEAX;
55 EAXGet pEAXGet;
56 EAXSet pEAXSet;
57
58 ALuint StrmSampleRate;
59 ALuint StrmFormat;
60 ALuint StrmBuffers[NUM_STRM_BUFFERS];
61 ALuint StrmAvailableBuffers[NUM_STRM_BUFFERS];
62 int StrmNumAvailableBuffers;
63 ALuint StrmSource;
64 short StrmDataBuffer[STRM_BUFFER_SIZE * 2];
65
66 static VCvarF doppler_factor;
67 static VCvarF doppler_velocity;
68 static VCvarF rolloff_factor;
69 static VCvarF reference_distance;
70 static VCvarF max_distance;
71 static VCvarI eax_environment;
72
73 public:
74 // VSoundDevice interface.
75 bool Init();
76 int SetChannels(int);
77 void Shutdown();
78 void Tick(float);
79 int PlaySound(int, float, float, float, bool);
80 int PlaySound3D(int, const TVec&, const TVec&, float, float, bool);
81 void UpdateChannel(int, float, float);
82 void UpdateChannel3D(int, const TVec&, const TVec&);
83 bool IsChannelPlaying(int);
84 void StopChannel(int);
85 void UpdateListener(const TVec&, const TVec&, const TVec&, const TVec&,
86 const TVec&, VReverbInfo*);
87
88 bool OpenStream(int, int, int);
89 void CloseStream();
90 int GetStreamAvailable();
91 short* GetStreamBuffer();
92 void SetStreamData(short*, int);
93 void SetStreamVolume(float);
94 void PauseStream();
95 void ResumeStream();
96
97 bool LoadSound(int);
98 };
99
100 IMPLEMENT_SOUND_DEVICE(VOpenALDevice, SNDDRV_OpenAL, "OpenAL",
101 "OpenAL sound device", "-openal");
102
103 VCvarF VOpenALDevice::doppler_factor("al_doppler_factor", "1.0", CVAR_Archive);
104 VCvarF VOpenALDevice::doppler_velocity("al_doppler_velocity", "10000.0", CVAR_Archive);
105 VCvarF VOpenALDevice::rolloff_factor("al_rolloff_factor", "1.0", CVAR_Archive);
106 VCvarF VOpenALDevice::reference_distance("al_reference_distance", "64.0", CVAR_Archive);
107 VCvarF VOpenALDevice::max_distance("al_max_distance", "2024.0", CVAR_Archive);
108 VCvarI VOpenALDevice::eax_environment("al_eax_environment", "0");
109
110 //==========================================================================
111 //
112 // VOpenALDevice::Init
113 //
114 // Inits sound
115 //
116 //==========================================================================
117
Init()118 bool VOpenALDevice::Init()
119 {
120 guard(VOpenALDevice::Init);
121 ALenum E;
122
123 Device = NULL;
124 Context = NULL;
125 Buffers = NULL;
126 supportEAX = false;
127 pEAXGet = NULL;
128 pEAXSet = NULL;
129 StrmSource = 0;
130 StrmNumAvailableBuffers = 0;
131
132 // Connect to a device.
133 Device = alcOpenDevice(NULL);
134 if (!Device)
135 {
136 GCon->Log(NAME_Init, "Couldn't open OpenAL device");
137 return false;
138 }
139 // In Linux it's not implemented.
140 #ifdef ALC_DEVICE_SPECIFIER
141 GCon->Logf(NAME_Init, "Opened OpenAL device %s", alcGetString(Device, ALC_DEVICE_SPECIFIER));
142 #endif
143
144 // Create a context and make it current.
145 Context = alcCreateContext(Device, NULL);
146 if (!Context)
147 {
148 Sys_Error("Failed to create OpenAL context");
149 }
150 alcMakeContextCurrent(Context);
151 E = alGetError();
152 if (E != AL_NO_ERROR)
153 {
154 Sys_Error("OpenAL error: %s", alGetString(E));
155 }
156
157 // Print some information.
158 GCon->Logf(NAME_Init, "AL_VENDOR: %s", alGetString(AL_VENDOR));
159 GCon->Logf(NAME_Init, "AL_RENDERER: %s", alGetString(AL_RENDERER));
160 GCon->Logf(NAME_Init, "AL_VERSION: %s", alGetString(AL_VERSION));
161 GCon->Log(NAME_Init, "AL_EXTENSIONS:");
162 TArray<VStr> Exts;
163 VStr((char*)alGetString(AL_EXTENSIONS)).Split(' ', Exts);
164 for (int i = 0; i < Exts.Num(); i++)
165 {
166 GCon->Log(NAME_Init, VStr("- ") + Exts[i]);
167 }
168 GCon->Log(NAME_Init, "ALC_EXTENSIONS:");
169 VStr((char*)alcGetString(Device, ALC_EXTENSIONS)).Split(' ', Exts);
170 for (int i = 0; i < Exts.Num(); i++)
171 {
172 GCon->Log(NAME_Init, VStr("- ") + Exts[i]);
173 }
174
175 if (alIsExtensionPresent((ALchar*)"EAX"))
176 {
177 GCon->Log(NAME_Init, "EAX 2.0 supported");
178 pEAXSet = (EAXSet)alGetProcAddress((ALchar*)"EAXSet");
179 pEAXGet = (EAXGet)alGetProcAddress((ALchar*)"EAXGet");
180 supportEAX = true;
181 }
182
183 // Allocate array for buffers.
184 Buffers = new ALuint[GSoundManager->S_sfx.Num()];
185 memset(Buffers, 0, sizeof(ALuint) * GSoundManager->S_sfx.Num());
186 Sound3D = true;
187 return true;
188 unguard;
189 }
190
191 //==========================================================================
192 //
193 // VOpenALDevice::SetChannels
194 //
195 //==========================================================================
196
SetChannels(int InNumChannels)197 int VOpenALDevice::SetChannels(int InNumChannels)
198 {
199 guard(VOpenALDevice::SetChannels);
200 int NumChannels = MAX_VOICES;
201 if (NumChannels > InNumChannels)
202 NumChannels = InNumChannels;
203 return NumChannels;
204 unguard;
205 }
206
207 //==========================================================================
208 //
209 // VOpenALDevice::Shutdown
210 //
211 //==========================================================================
212
Shutdown()213 void VOpenALDevice::Shutdown()
214 {
215 guard(VOpenALDevice::Shutdown);
216 // Delete buffers.
217 if (Buffers)
218 {
219 alDeleteBuffers(GSoundManager->S_sfx.Num(), Buffers);
220 delete[] Buffers;
221 Buffers = NULL;
222 }
223
224 // Destroy context.
225 if (Context)
226 {
227 #ifndef __linux__
228 // This causes a freeze in Linux
229 alcMakeContextCurrent(NULL);
230 #endif
231 alcDestroyContext(Context);
232 Context = NULL;
233 }
234 // Disconnect from a device.
235 if (Device)
236 {
237 alcCloseDevice(Device);
238 Device = NULL;
239 }
240 unguard;
241 }
242
243 //==========================================================================
244 //
245 // VOpenALDevice::Tick
246 //
247 //==========================================================================
248
Tick(float)249 void VOpenALDevice::Tick(float)
250 {
251 }
252
253 //==========================================================================
254 //
255 // VOpenALDevice::LoadSound
256 //
257 //==========================================================================
258
LoadSound(int sound_id)259 bool VOpenALDevice::LoadSound(int sound_id)
260 {
261 guard(VOpenALDevice::LoadSound);
262 if (Buffers[sound_id])
263 {
264 return true;
265 }
266
267 // Check, that sound lump is loaded
268 if (!GSoundManager->LoadSound(sound_id))
269 {
270 // Missing sound.
271 return false;
272 }
273
274 // Clear error code.
275 alGetError();
276
277 // Create buffer.
278 alGenBuffers(1, &Buffers[sound_id]);
279 if (alGetError() != AL_NO_ERROR)
280 {
281 GCon->Log(NAME_Dev, "Failed to gen buffer");
282 GSoundManager->DoneWithLump(sound_id);
283 return false;
284 }
285
286 // Load buffer data.
287 alBufferData(Buffers[sound_id],
288 GSoundManager->S_sfx[sound_id].SampleBits == 8 ? AL_FORMAT_MONO8 :
289 AL_FORMAT_MONO16, GSoundManager->S_sfx[sound_id].Data,
290 GSoundManager->S_sfx[sound_id].DataSize,
291 GSoundManager->S_sfx[sound_id].SampleRate);
292 if (alGetError() != AL_NO_ERROR)
293 {
294 GCon->Log(NAME_Dev, "Failed to load buffer data");
295 GSoundManager->DoneWithLump(sound_id);
296 return false;
297 }
298
299 // We don't need to keep lump static
300 GSoundManager->DoneWithLump(sound_id);
301 return true;
302 unguard;
303 }
304
305 //==========================================================================
306 //
307 // VOpenALDevice::PlaySound
308 //
309 // This function adds a sound to the list of currently active sounds, which
310 // is maintained as a given number of internal channels.
311 //
312 //==========================================================================
313
PlaySound(int sound_id,float volume,float,float pitch,bool Loop)314 int VOpenALDevice::PlaySound(int sound_id, float volume, float, float pitch,
315 bool Loop)
316 {
317 guard(VOpenALDevice::PlaySound);
318 if (!LoadSound(sound_id))
319 {
320 return -1;
321 }
322
323 ALuint src;
324 alGetError(); // Clear error code.
325 alGenSources(1, &src);
326 if (alGetError() != AL_NO_ERROR)
327 {
328 GCon->Log(NAME_Dev, "Failed to gen source");
329 return -1;
330 }
331
332 alSourcei(src, AL_BUFFER, Buffers[sound_id]);
333
334 alSourcef(src, AL_GAIN, volume);
335 alSourcef(src, AL_ROLLOFF_FACTOR, rolloff_factor);
336 alSourcei(src, AL_SOURCE_RELATIVE, AL_TRUE);
337 alSource3f(src, AL_POSITION, 0.0, 0.0, -16.0);
338 alSourcef(src, AL_REFERENCE_DISTANCE, reference_distance);
339 alSourcef(src, AL_MAX_DISTANCE, max_distance);
340 alSourcef(src, AL_PITCH, pitch);
341 if (Loop)
342 alSourcei(src, AL_LOOPING, AL_TRUE);
343 alSourcePlay(src);
344 return src;
345 unguard;
346 }
347
348 //==========================================================================
349 //
350 // VOpenALDevice::PlaySound3D
351 //
352 //==========================================================================
353
PlaySound3D(int sound_id,const TVec & origin,const TVec & velocity,float volume,float pitch,bool Loop)354 int VOpenALDevice::PlaySound3D(int sound_id, const TVec &origin,
355 const TVec &velocity, float volume, float pitch, bool Loop)
356 {
357 guard(VOpenALDevice::PlaySound3D);
358 if (!LoadSound(sound_id))
359 {
360 return -1;
361 }
362
363 ALuint src;
364 alGetError(); // Clear error code.
365 alGenSources(1, &src);
366 if (alGetError() != AL_NO_ERROR)
367 {
368 GCon->Log(NAME_Dev, "Failed to gen source");
369 return -1;
370 }
371
372 alSourcei(src, AL_BUFFER, Buffers[sound_id]);
373
374 alSourcef(src, AL_GAIN, volume);
375 alSourcef(src, AL_ROLLOFF_FACTOR, rolloff_factor);
376 alSource3f(src, AL_POSITION, origin.x, origin.y, origin.z);
377 alSource3f(src, AL_VELOCITY, velocity.x, velocity.y, velocity.z);
378 alSourcef(src, AL_REFERENCE_DISTANCE, reference_distance);
379 alSourcef(src, AL_MAX_DISTANCE, max_distance);
380 alSourcef(src, AL_PITCH, pitch);
381 if (Loop)
382 alSourcei(src, AL_LOOPING, AL_TRUE);
383 alSourcePlay(src);
384 return src;
385 unguard;
386 }
387
388 //==========================================================================
389 //
390 // VOpenALDevice::UpdateChannel
391 //
392 //==========================================================================
393
UpdateChannel(int,float,float)394 void VOpenALDevice::UpdateChannel(int, float, float)
395 {
396 }
397
398 //==========================================================================
399 //
400 // VOpenALDevice::UpdateChannel3D
401 //
402 //==========================================================================
403
UpdateChannel3D(int Handle,const TVec & Org,const TVec & Vel)404 void VOpenALDevice::UpdateChannel3D(int Handle, const TVec& Org,
405 const TVec& Vel)
406 {
407 guard(VOpenALDevice::UpdateChannel3D);
408 if (Handle == -1)
409 {
410 return;
411 }
412 alSource3f(Handle, AL_POSITION, Org.x, Org.y, Org.z);
413 alSource3f(Handle, AL_VELOCITY, Vel.x, Vel.y, Vel.z);
414 unguard;
415 }
416
417 //==========================================================================
418 //
419 // VOpenALDevice::IsChannelPlaying
420 //
421 //==========================================================================
422
IsChannelPlaying(int Handle)423 bool VOpenALDevice::IsChannelPlaying(int Handle)
424 {
425 guard(VOpenALDevice::IsChannelPlaying);
426 if (Handle == -1)
427 {
428 return false;
429 }
430 ALint State;
431 alGetSourcei(Handle, AL_SOURCE_STATE, &State);
432 return State == AL_PLAYING;
433 unguard;
434 }
435
436 //==========================================================================
437 //
438 // VOpenALDevice::StopChannel
439 //
440 // Stop the sound. Necessary to prevent runaway chainsaw, and to stop
441 // rocket launches when an explosion occurs.
442 // All sounds MUST be stopped;
443 //
444 //==========================================================================
445
StopChannel(int Handle)446 void VOpenALDevice::StopChannel(int Handle)
447 {
448 guard(VOpenALDevice::StopChannel);
449 if (Handle == -1)
450 {
451 return;
452 }
453 // Stop buffer
454 alSourceStop(Handle);
455 alDeleteSources(1, (ALuint*)&Handle);
456 unguard;
457 }
458
459 //==========================================================================
460 //
461 // VOpenALDevice::UpdateListener
462 //
463 //==========================================================================
464
UpdateListener(const TVec & org,const TVec & vel,const TVec & fwd,const TVec &,const TVec & up,VReverbInfo * Env)465 void VOpenALDevice::UpdateListener(const TVec& org, const TVec& vel,
466 const TVec& fwd, const TVec&, const TVec& up, VReverbInfo* Env)
467 {
468 guard(VOpenALDevice::UpdateListener);
469 alListener3f(AL_POSITION, org.x, org.y, org.z);
470 alListener3f(AL_VELOCITY, vel.x, vel.y, vel.z);
471
472 ALfloat orient[6] = { fwd.x, fwd.y, fwd.z, up.x, up.y, up.z};
473 alListenerfv(AL_ORIENTATION, orient);
474
475 alDopplerFactor(doppler_factor);
476 alDopplerVelocity(doppler_velocity);
477
478 if (supportEAX)
479 {
480 EAXLISTENERPROPERTIES Prop;
481 Prop.lRoom = Env->Props.Room;
482 Prop.lRoomHF = Env->Props.RoomHF;
483 Prop.flRoomRolloffFactor = Env->Props.RoomRolloffFactor;
484 Prop.flDecayTime = Env->Props.DecayTime;
485 Prop.flDecayHFRatio = Env->Props.DecayHFRatio;
486 Prop.lReflections = Env->Props.Reflections;
487 Prop.flReflectionsDelay = Env->Props.ReflectionsDelay;
488 Prop.lReverb = Env->Props.Reverb;
489 Prop.flReverbDelay = Env->Props.ReverbDelay;
490 Prop.dwEnvironment = Env->Props.Environment;
491 Prop.flEnvironmentSize = Env->Props.EnvironmentSize;
492 Prop.flEnvironmentDiffusion = Env->Props.EnvironmentDiffusion;
493 Prop.flAirAbsorptionHF = Env->Props.AirAbsorptionHF;
494 Prop.dwFlags = Env->Props.Flags & 0x3f;
495 pEAXSet(&DSPROPSETID_EAX_ListenerProperties,
496 DSPROPERTY_EAXLISTENER_ALLPARAMETERS, 0, &Prop, sizeof(Prop));
497
498 if (Env->Id == 1)
499 {
500 int envId = eax_environment;
501 if (envId < 0 || envId >= EAX_ENVIRONMENT_COUNT)
502 envId = EAX_ENVIRONMENT_GENERIC;
503 pEAXSet(&DSPROPSETID_EAX_ListenerProperties,
504 DSPROPERTY_EAXLISTENER_ENVIRONMENT, 0, &envId, sizeof(int));
505
506 float envSize = GAudio->EAX_CalcEnvSize();
507 pEAXSet(&DSPROPSETID_EAX_ListenerProperties,
508 DSPROPERTY_EAXLISTENER_ENVIRONMENTSIZE, 0, &envSize, sizeof(float));
509 }
510 }
511 unguard;
512 }
513
514 //==========================================================================
515 //
516 // VOpenALDevice::OpenStream
517 //
518 //==========================================================================
519
OpenStream(int Rate,int Bits,int Channels)520 bool VOpenALDevice::OpenStream(int Rate, int Bits, int Channels)
521 {
522 guard(VOpenALDevice::OpenStream);
523 StrmSampleRate = Rate;
524 StrmFormat = Channels == 2 ?
525 Bits == 8 ? AL_FORMAT_STEREO8 : AL_FORMAT_STEREO16 :
526 Bits == 8 ? AL_FORMAT_MONO8 : AL_FORMAT_MONO16;
527
528 alGetError(); // Clear error code.
529 alGenSources(1, &StrmSource);
530 if (alGetError() != AL_NO_ERROR)
531 {
532 GCon->Log(NAME_Dev, "Failed to gen source");
533 return false;
534 }
535 alSourcei(StrmSource, AL_SOURCE_RELATIVE, AL_TRUE);
536 alGenBuffers(NUM_STRM_BUFFERS, StrmBuffers);
537 alSourceQueueBuffers(StrmSource, NUM_STRM_BUFFERS, StrmBuffers);
538 alSourcePlay(StrmSource);
539 StrmNumAvailableBuffers = 0;
540 return true;
541 unguard;
542 }
543
544 //==========================================================================
545 //
546 // VOpenALDevice::CloseStream
547 //
548 //==========================================================================
549
CloseStream()550 void VOpenALDevice::CloseStream()
551 {
552 guard(VOpenALDevice::CloseStream);
553 if (StrmSource)
554 {
555 alDeleteBuffers(NUM_STRM_BUFFERS, StrmBuffers);
556 alDeleteSources(1, &StrmSource);
557 StrmSource = 0;
558 }
559 unguard;
560 }
561
562 //==========================================================================
563 //
564 // VOpenALDevice::GetStreamAvailable
565 //
566 //==========================================================================
567
GetStreamAvailable()568 int VOpenALDevice::GetStreamAvailable()
569 {
570 guard(VOpenALDevice::GetStreamAvailable);
571 if (!StrmSource)
572 return 0;
573
574 ALint NumProc;
575 alGetSourcei(StrmSource, AL_BUFFERS_PROCESSED, &NumProc);
576 if (NumProc > 0)
577 {
578 alSourceUnqueueBuffers(StrmSource, NumProc,
579 StrmAvailableBuffers + StrmNumAvailableBuffers);
580 StrmNumAvailableBuffers += NumProc;
581 }
582 return StrmNumAvailableBuffers > 0 ? STRM_BUFFER_SIZE : 0;
583 unguard;
584 }
585
586 //==========================================================================
587 //
588 // VOpenALDevice::SetStreamData
589 //
590 //==========================================================================
591
GetStreamBuffer()592 short* VOpenALDevice::GetStreamBuffer()
593 {
594 guard(VOpenALDevice::GetStreamBuffer);
595 return StrmDataBuffer;
596 unguard;
597 }
598
599 //==========================================================================
600 //
601 // VOpenALDevice::SetStreamData
602 //
603 //==========================================================================
604
SetStreamData(short * Data,int Len)605 void VOpenALDevice::SetStreamData(short* Data, int Len)
606 {
607 guard(VOpenALDevice::SetStreamData);
608 ALuint Buf;
609 ALint State;
610
611 Buf = StrmAvailableBuffers[StrmNumAvailableBuffers - 1];
612 StrmNumAvailableBuffers--;
613 alBufferData(Buf, StrmFormat, Data, Len * 4, StrmSampleRate);
614 alSourceQueueBuffers(StrmSource, 1, &Buf);
615 alGetSourcei(StrmSource, AL_SOURCE_STATE, &State);
616 if (State != AL_PLAYING)
617 {
618 alSourcePlay(StrmSource);
619 }
620 unguard;
621 }
622
623 //==========================================================================
624 //
625 // VOpenALDevice::SetStreamVolume
626 //
627 //==========================================================================
628
SetStreamVolume(float Vol)629 void VOpenALDevice::SetStreamVolume(float Vol)
630 {
631 guard(VOpenALDevice::SetStreamVolume);
632 if (StrmSource)
633 {
634 alSourcef(StrmSource, AL_GAIN, Vol);
635 }
636 unguard;
637 }
638
639 //==========================================================================
640 //
641 // VOpenALDevice::PauseStream
642 //
643 //==========================================================================
644
PauseStream()645 void VOpenALDevice::PauseStream()
646 {
647 guard(VOpenALDevice::PauseStream);
648 if (StrmSource)
649 {
650 alSourcePause(StrmSource);
651 }
652 unguard;
653 }
654
655 //==========================================================================
656 //
657 // VOpenALDevice::ResumeStream
658 //
659 //==========================================================================
660
ResumeStream()661 void VOpenALDevice::ResumeStream()
662 {
663 guard(VOpenALDevice::ResumeStream);
664 if (StrmSource)
665 {
666 alSourcePlay(StrmSource);
667 }
668 unguard;
669 }
670