1 /*
2  * Copyright (C) 2002  Terence M. Welsh
3  * Ported to Linux by Tugrul Galatali <tugrul@galatali.com>
4  *
5  * Skyrocket is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License version 2 as
7  * published by the Free Software Foundation.
8  *
9  * Skyrocket 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17  */
18 
19 #include "skyrocket_sound.h"
20 
21 #ifdef HAVE_OPENAL
22 
23 #include <math.h>
24 #include <stdlib.h>
25 #include <AL/al.h>
26 #include <AL/alut.h>
27 
28 #include <cstdlib>
29 #include <list>
30 
31 #include "loadTexture.h"
32 
33 // reference distance, max distance
34 // reference distance = sound is partly attenuated
35 // maximum distance = sound is completely attenuated
36 float sound_distances[NUM_BUFFERS][2] = {
37 	{ 300.0f, 600.0f },	// launch sounds
38 	{ 300.0f, 600.0f },
39 	{ 4000.0f, 8000.0f },	// booms
40 	{ 4000.0f, 8000.0f },
41 	{ 4000.0f, 8000.0f },
42 	{ 6000.0f, 12000.0f },
43 	{ 2500.0f, 5000.0f },	// poppers
44 	{ 10000.0f, 20000.0f },	// suck
45 	{ 20000.0f, 40000.0f },	// nuke
46 	{ 2500.0f, 5000.0f }	// whistle
47 };
48 
49 class soundnode;
50 
51 ALuint g_Buffers[NUM_BUFFERS];
52 ALuint g_Sources[NUM_SOURCES];
53 extern float elapsedTime;
54 extern int dSound;
55 extern int kSlowMotion;
56 
swap_bytes(unsigned char * data,int bytes)57 void swap_bytes (unsigned char *data, int bytes)
58 {
59 	int i = 0;
60 
61 	for (i = 0; i < bytes - 1; i += 2) {
62 		int temp = data[i];
63 
64 		data[i] = data[i + 1];
65 		data[i + 1] = temp;
66 	}
67 }
68 
initSound()69 void initSound ()
70 {
71 	int i = 1;
72 	unsigned char *j;
73 	unsigned char *l_snd;
74 
75 	j = (unsigned char *)&i;
76 
77 	alutInit (NULL, 0);
78 
79 	alDistanceModel (AL_INVERSE_DISTANCE);
80 	// As of October, 2001, AL_ROLLOFF_FACTOR isn't implemented in the Windows
81 	// version of OpenAL, so the distance attenuation models won't work.  You
82 	// have to set the gains yourself.
83 
84 	//alDistanceModel(AL_NONE);
85 
86 	alDopplerVelocity (1130.0f);	// Sound travels at 1130 feet/sec
87 	alListenerf (AL_GAIN, float (dSound) * 0.01f);	// Volume
88 
89 	// Initialize sound data
90 	alGenBuffers (NUM_BUFFERS, g_Buffers);
91 	alGenSources (NUM_SOURCES, g_Sources);
92 
93 #define BUFFER_SOUND(index, data, compressedsize, size) \
94 	LOAD_TEXTURE(l_snd, data, compressedsize, size) \
95 	if (!*j) swap_bytes(l_snd, size); \
96 	alBufferData(g_Buffers[index], AL_FORMAT_MONO16, l_snd, size, 44100); \
97 	FREE_TEXTURE(l_snd)
98 
99 	BUFFER_SOUND (BOOM1SOUND, launch1SoundData, launch1SoundData_compressedsize, launch1SoundData_size)
100 	BUFFER_SOUND (BOOM2SOUND, launch2SoundData, launch2SoundData_compressedsize, launch2SoundData_size)
101 	BUFFER_SOUND (BOOM1SOUND, boom1SoundData, boom1SoundData_compressedsize, boom1SoundData_size)
102 	BUFFER_SOUND (BOOM2SOUND, boom2SoundData, boom2SoundData_compressedsize, boom2SoundData_size)
103 	BUFFER_SOUND (BOOM3SOUND, boom3SoundData, boom3SoundData_compressedsize, boom3SoundData_size)
104 	BUFFER_SOUND (BOOM4SOUND, boom4SoundData, boom4SoundData_compressedsize, boom4SoundData_size)
105 	BUFFER_SOUND (POPPERSOUND, popperSoundData, popperSoundData_compressedsize, popperSoundData_size)
106 	BUFFER_SOUND (POPPERSOUND, suckSoundData, suckSoundData_compressedsize, suckSoundData_size)
107 	BUFFER_SOUND (POPPERSOUND, nukeSoundData, nukeSoundData_compressedsize, nukeSoundData_size)
108 	BUFFER_SOUND (POPPERSOUND, whistleSoundData, whistleSoundData_compressedsize, whistleSoundData_size)
109 }
110 
111 class soundnode {
112       public:
113 	int sound;
114 	float pos[3];
115 	float dist;
116 	float time;		// time until sound plays
117 
soundnode()118 	  soundnode () {
119 	};
120 
~soundnode()121 	~soundnode () {
122 	};
123 };
124 
125 std::list < soundnode > soundlist;
126 
insertSoundNode(int sound,rsVec source,rsVec observer)127 void insertSoundNode (int sound, rsVec source, rsVec observer)
128 {
129 	soundnode *node;
130 	rsVec dir = observer - source;
131 
132 	soundlist.push_back (soundnode ());
133 	node = &soundlist.back ();
134 	node->sound = sound;
135 	node->pos[0] = source[0];
136 	node->pos[1] = source[1];
137 	node->pos[2] = source[2];
138 	// distance to sound
139 	node->dist = sqrt (dir[0] * dir[0] + dir[1] * dir[1] + dir[2] * dir[2]);
140 	// Sound travels at 1130 feet/sec
141 	node->time = node->dist * 0.000885f;
142 	if (node->sound == POPPERSOUND)	// poppers have a little delay
143 		node->time += 2.5f;
144 }
145 
updateSound(float * listenerPos,float * listenerVel,float * listenerOri)146 void updateSound (float *listenerPos, float *listenerVel, float *listenerOri)
147 {
148 	static int source = 0;
149 
150 	// Set current listener attributes
151 	alListenerfv (AL_POSITION, listenerPos);
152 	alListenerfv (AL_VELOCITY, listenerVel);
153 	alListenerfv (AL_ORIENTATION, listenerOri);
154 
155 	std::list < soundnode >::iterator cursound = soundlist.begin ();
156 	while (cursound != soundlist.end ()) {
157 		cursound->time -= elapsedTime;
158 		if (cursound->time <= 0.0f) {
159 			if (cursound->dist < sound_distances[cursound->sound][1]) {
160 				alSourceStop (g_Sources[source]);
161 				alSourcei (g_Sources[source], AL_BUFFER, g_Buffers[cursound->sound]);
162 				//alSourcef(g_Sources[source], AL_REFERENCE_DISTANCE, sound_distances[cursound->sound][0]);
163 				//alSourcef(g_Sources[source], AL_ROLLOFF_FACTOR, 0.013557606f);
164 				// HACK:  AL_REFERENCE_DISTANCE must be set or OpenAL won't make any sound at all.
165 				// I assume this will be fixed in future implementations of OpenAL.
166 				alSourcef (g_Sources[source], AL_REFERENCE_DISTANCE, 1000000.0f);
167 				// As of October, 2001, AL_ROLLOFF_FACTOR isn't implemented in the Windows
168 				// version of OpenAL, so the distance attenuation models won't work.  You
169 				// have to set the gains yourself.
170 				// Here I implement the AL_INVERSE_DISTANCE model formula from the OpenAL spec.
171 				//float gain = 1.0f - 20.0f * log10(1.0f + 0.013557606f
172 				//      * (cursound->dist - sound_distances[cursound->sound][0])
173 				//      / sound_distances[cursound->sound][0]);
174 				// But I don't like the way it sounds, so I'll just fudge everything
175 				float gain = 1.0f - (cursound->dist / sound_distances[cursound->sound][1]);
176 
177 				if (gain > 1.0f)
178 					gain = 1.0f;
179 				if (gain < 0.0f)
180 					gain = 0.0f;
181 				alSourcef (g_Sources[source], AL_GAIN, gain);
182 				alSourcefv (g_Sources[source], AL_POSITION, cursound->pos);
183 				alSourcei (g_Sources[source], AL_LOOPING, AL_FALSE);
184 				if (kSlowMotion)	// Slow down the sound
185 					alSourcef (g_Sources[source], AL_PITCH, 0.5f);
186 				else	// Sound at regular speed
187 					alSourcef (g_Sources[source], AL_PITCH, 1.0f);
188 				alSourcePlay (g_Sources[source]);
189 				source++;
190 				if (source >= NUM_SOURCES)
191 					source = 0;
192 			}
193 			cursound = soundlist.erase (cursound);
194 		} else
195 			cursound++;
196 	}
197 }
198 
cleanupSound()199 void cleanupSound ()
200 {
201 	alDeleteSources (NUM_SOURCES, g_Sources);
202 	alDeleteBuffers (NUM_BUFFERS, g_Buffers);
203 
204 	ALCcontext * const context = alcGetCurrentContext ();
205 	ALCdevice * const device = alcGetContextsDevice (context);
206 
207 	alcMakeContextCurrent (NULL);
208 	alcDestroyContext (context);
209 	alcCloseDevice (device);
210 
211 	alutExit ();
212 }
213 
214 #endif
215