1 /*
2  *  This program is free software; you can redistribute it and/or modify
3  *  it under the terms of the GNU General Public License as published by
4  *  the Free Software Foundation; either version 2 of the License, or
5  *  (at your option) any later version.
6  *
7  *  This program is distributed in the hope that it will be useful,
8  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
9  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  *  GNU General Public License for more details.
11  *
12  *  You should have received a copy of the GNU General Public License
13  *  along with this program; if not, write to the Free Software
14  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
15  */
16 
17 #include "options.h"
18 #include "sound.h"
19 #include "sndintrn.h"
20 #include "libs/reslib.h"
21 #include "libs/log.h"
22 #include "libs/strlib.h"
23 		// for GetStringAddress()
24 #include "libs/strings/strintrn.h"
25 		// for AllocStringTable(), FreeStringTable()
26 #include "libs/memlib.h"
27 #include <math.h>
28 
29 
30 static void CheckFinishedChannels (void);
31 
32 static const SoundPosition notPositional = {FALSE, 0, 0};
33 
34 void
PlayChannel(COUNT channel,SOUND snd,SoundPosition pos,void * positional_object,unsigned char priority)35 PlayChannel (COUNT channel, SOUND snd, SoundPosition pos,
36 		void *positional_object, unsigned char priority)
37 {
38 	SOUNDPTR snd_ptr = GetSoundAddress (snd);
39 	TFB_SoundSample *sample;
40 
41 	StopSource (channel);
42 	// all finished (stopped) channels can be cleaned up at this point
43 	// since this is the only func that can initiate an sfx sound
44 	CheckFinishedChannels ();
45 
46 	if (!snd_ptr)
47 		return; // nothing to play
48 
49 	sample = *(TFB_SoundSample**) snd_ptr;
50 
51 	soundSource[channel].sample = sample;
52 	soundSource[channel].positional_object = positional_object;
53 
54 	UpdateSoundPosition (channel, optStereoSFX ? pos : notPositional);
55 
56 	audio_Sourcei (soundSource[channel].handle, audio_BUFFER,
57 			sample->buffer[0]);
58 	audio_SourcePlay (soundSource[channel].handle);
59 	(void) priority;
60 }
61 
62 void
StopChannel(COUNT channel,unsigned char Priority)63 StopChannel (COUNT channel, unsigned char Priority)
64 {
65 	StopSource (channel);
66 	(void)Priority; // ignored
67 }
68 
69 static void
CheckFinishedChannels(void)70 CheckFinishedChannels (void)
71 {
72 	int i;
73 
74 	for (i = FIRST_SFX_SOURCE; i <= LAST_SFX_SOURCE; ++i)
75 	{
76 		audio_IntVal state;
77 
78 		audio_GetSourcei (soundSource[i].handle, audio_SOURCE_STATE,
79 				&state);
80 		if (state == audio_STOPPED)
81 		{
82 			CleanSource (i);
83 			// and if it failed... we still dont care
84 			audio_GetError();
85 		}
86 	}
87 }
88 
89 BOOLEAN
ChannelPlaying(COUNT WhichChannel)90 ChannelPlaying (COUNT WhichChannel)
91 {
92 	audio_IntVal state;
93 
94 	audio_GetSourcei (soundSource[WhichChannel].handle,
95 			audio_SOURCE_STATE, &state);
96 	if (state == audio_PLAYING)
97 		return TRUE;
98 	return FALSE;
99 }
100 
101 void *
GetPositionalObject(COUNT channel)102 GetPositionalObject (COUNT channel)
103 {
104 	return soundSource[channel].positional_object;
105 }
106 
107 void
SetPositionalObject(COUNT channel,void * positional_object)108 SetPositionalObject (COUNT channel, void *positional_object)
109 {
110 	soundSource[channel].positional_object = positional_object;
111 }
112 
113 void
UpdateSoundPosition(COUNT channel,SoundPosition pos)114 UpdateSoundPosition (COUNT channel, SoundPosition pos)
115 {
116 	const float ATTENUATION = 160.0f;
117 	const float MIN_DISTANCE = 0.5f;
118 	float fpos[3];
119 
120 	if (pos.positional)
121 	{
122 		float dist;
123 
124 		fpos[0] = pos.x / ATTENUATION;
125 		fpos[1] = 0.0f;
126 		fpos[2] = pos.y / ATTENUATION;
127 		dist = (float) sqrt (fpos[0] * fpos[0] + fpos[2] * fpos[2]);
128 		if (dist < MIN_DISTANCE)
129 		{	// object is too close to listener
130 			// move it away along the same vector
131 			float scale = MIN_DISTANCE / dist;
132 			fpos[0] *= scale;
133 			fpos[2] *= scale;
134 		}
135 
136 		audio_Sourcefv (soundSource[channel].handle, audio_POSITION, fpos);
137 		//log_add (log_Debug, "UpdateSoundPosition(): channel %d, pos %d %d, posobj %x",
138 		//		channel, pos.x, pos.y, (unsigned int)soundSource[channel].positional_object);
139 	}
140 	else
141 	{
142 		fpos[0] = fpos[1] = 0.0f;
143 		fpos[2] = -1.0f;
144 		audio_Sourcefv (soundSource[channel].handle, audio_POSITION, fpos);
145 	}
146 }
147 
148 void
SetChannelVolume(COUNT channel,COUNT volume,BYTE priority)149 SetChannelVolume (COUNT channel, COUNT volume, BYTE priority)
150 		// I wonder what this whole priority business is...
151 		// I can probably ignore it.
152 {
153 	audio_Sourcef (soundSource[channel].handle, audio_GAIN,
154 		(volume / (float)MAX_VOLUME) * sfxVolumeScale);
155 	(void)priority; // ignored
156 }
157 
158 void *
_GetSoundBankData(uio_Stream * fp,DWORD length)159 _GetSoundBankData (uio_Stream *fp, DWORD length)
160 {
161 	int snd_ct, n;
162 	DWORD opos;
163 	char CurrentLine[1024], filename[1024];
164 #define MAX_FX 256
165 	TFB_SoundSample *sndfx[MAX_FX];
166 	STRING_TABLE Snd;
167 	STRING str;
168 	int i;
169 
170 	(void) length;  // ignored
171 	opos = uio_ftell (fp);
172 
173 	{
174 		char *s1, *s2;
175 
176 		if (_cur_resfile_name == 0
177 			|| (((s2 = 0), (s1 = strrchr (_cur_resfile_name, '/')) == 0)
178 			&& (s2 = strrchr (_cur_resfile_name, '\\')) == 0))
179 			n = 0;
180 		else
181 		{
182 			if (s2 > s1)
183 				s1 = s2;
184 			n = s1 - _cur_resfile_name + 1;
185 			strncpy (filename, _cur_resfile_name, n);
186 		}
187 	}
188 
189 	snd_ct = 0;
190 	while (uio_fgets (CurrentLine, sizeof (CurrentLine), fp) &&
191 			snd_ct < MAX_FX)
192 	{
193 		TFB_SoundSample* sample;
194 		TFB_SoundDecoder* decoder;
195 		uint32 decoded_bytes;
196 
197 		if (sscanf (CurrentLine, "%s", &filename[n]) != 1)
198 		{
199 			log_add (log_Warning, "_GetSoundBankData: bad line: '%s'",
200 					CurrentLine);
201 			continue;
202 		}
203 
204 		log_add (log_Info, "_GetSoundBankData(): loading %s", filename);
205 
206 		decoder = SoundDecoder_Load (contentDir, filename, 4096, 0, 0);
207 		if (!decoder)
208 		{
209 			log_add (log_Warning, "_GetSoundBankData(): couldn't load %s",
210 					filename);
211 			continue;
212 		}
213 
214 		// SFX samples don't have decoders, everything is pre-decoded below
215 		sample = TFB_CreateSoundSample (NULL, 1, NULL);
216 
217 		// Decode everything and stash it in 1 buffer
218 		decoded_bytes = SoundDecoder_DecodeAll (decoder);
219 		log_add (log_Info, "_GetSoundBankData(): decoded bytes %d",
220 				decoded_bytes);
221 
222 		audio_BufferData (sample->buffer[0], decoder->format,
223 			decoder->buffer, decoded_bytes, decoder->frequency);
224 		// just for informational purposes
225 		sample->length = decoder->length;
226 
227 		SoundDecoder_Free (decoder);
228 
229 		sndfx[snd_ct] = sample;
230 		++snd_ct;
231 	}
232 
233 	if (!snd_ct)
234 		return NULL; // no sounds decoded
235 
236 	Snd = AllocStringTable (snd_ct, 0);
237 	if (!Snd)
238 	{	// Oops, have to delete everything now
239 		while (snd_ct--)
240 			TFB_DestroySoundSample (sndfx[snd_ct]);
241 
242 		return NULL;
243 	}
244 
245 	// Populate the STRING_TABLE with ptrs to sample
246 	for (i = 0, str = Snd->strings; i < snd_ct; ++i, ++str)
247 	{
248 		TFB_SoundSample **target = HMalloc (sizeof (sndfx[0]));
249 		*target = sndfx[i];
250 		str->data = (STRINGPTR)target;
251 		str->length = sizeof (sndfx[0]);
252 	}
253 
254 	return Snd;
255 }
256 
257 BOOLEAN
_ReleaseSoundBankData(void * Snd)258 _ReleaseSoundBankData (void *Snd)
259 {
260 	STRING_TABLE fxTab = Snd;
261 	int index;
262 
263 	if (!fxTab)
264 		return FALSE;
265 
266 	for (index = 0; index < fxTab->size; ++index)
267 	{
268 		int i;
269 		void **sptr = (void**)fxTab->strings[index].data;
270 		TFB_SoundSample *sample = (TFB_SoundSample*)*sptr;
271 
272 		// Check all sources and see if we are currently playing this sample
273 		for (i = 0; i < NUM_SOUNDSOURCES; ++i)
274 		{
275 			if (soundSource[i].sample == sample)
276 			{	// Playing this sample. Have to stop it.
277 				StopSource (i);
278 				soundSource[i].sample = NULL;
279 			}
280 		}
281 
282         if (sample->decoder)
283 			SoundDecoder_Free (sample->decoder);
284 		sample->decoder = NULL;
285 		TFB_DestroySoundSample (sample);
286 		// sptr will be deleted by FreeStringTable() below
287 	}
288 
289 	FreeStringTable (fxTab);
290 
291 	return TRUE;
292 }
293 
294 BOOLEAN
DestroySound(SOUND_REF target)295 DestroySound(SOUND_REF target)
296 {
297 	return _ReleaseSoundBankData (target);
298 }
299 
300 // The type conversions are implicit and will generate errors
301 // or warnings if types change imcompatibly
302 SOUNDPTR
GetSoundAddress(SOUND sound)303 GetSoundAddress (SOUND sound)
304 {
305 	return GetStringAddress (sound);
306 }
307