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