1 /*
2
3 Copyright (C) 2015-2018 Night Dive Studios, LLC.
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 */
19 /*
20 * $Source: r:/prj/cit/src/RCS/digifx.c $
21 * $Revision: 1.48 $
22 * $Author: dc $
23 * $Date: 1994/11/28 08:38:42 $
24 */
25
26 #include "criterr.h"
27 #include "objects.h"
28 #include "player.h"
29 #include "musicai.h"
30 #include "faketime.h"
31 #include "tools.h"
32 #include "trigger.h"
33 #ifdef AUDIOLOGS
34 #include "audiolog.h"
35 #endif
36
37 #define NUM_DIGI_FX 114
38
39 char volumes[NUM_DIGI_FX];
40 char flags[NUM_DIGI_FX];
41 char priorities[NUM_DIGI_FX];
42
43 // This has to be changed if the resource changes location!
44 #define SFX_BASE 201
45
46 extern uchar curr_alog_vol;
47
48 #ifdef NOT_YET
49
50 //#define ASYNCH_DIGI
51
52 int digi_timer_id;
start_asynch_digi_fx()53 void start_asynch_digi_fx() {
54 #ifdef ASYNCH_DIGI
55 if (sfx_on)
56 tm_activate_process(digi_timer_id);
57 #endif
58 }
59
stop_asynch_digi_fx()60 void stop_asynch_digi_fx() {
61 #ifdef ASYNCH_DIGI
62 if (sfx_on)
63 tm_deactivate_process(digi_timer_id);
64 #endif
65 }
66
67 #endif
68
stop_digi_fx()69 errtype stop_digi_fx() {
70 #ifdef AUDIOLOGS
71 if (audiolog_setting)
72 audiolog_stop();
73 #endif
74 if (sfx_on) {
75 extern void sound_frame_update(void);
76 snd_kill_all_samples();
77 sound_frame_update();
78 }
79 return (ERR_NOEFFECT);
80 }
81
clear_digi_fx()82 void clear_digi_fx() { stop_digi_fx(); }
83
84 //#define SND_TEST
85
digifx_init()86 errtype digifx_init() {
87
88 FILE *fp = fopen_caseless("res/data/digiparm.bin", "rb");
89 if (fp == NULL) {
90 printf("Failed to open digiparm.bin\n");
91 return ERR_FOPEN;
92 }
93
94 fread(volumes, NUM_DIGI_FX, 1, fp);
95 fread(flags, NUM_DIGI_FX, 1, fp);
96 fread(priorities, NUM_DIGI_FX, 1, fp);
97 fclose(fp);
98 // clear_digi_fx();
99
100 // snd_finish = digifx_EOS_callback;
101
102 /* KLC - not needed now.
103 #ifdef ASYNCH_DIGI
104 extern void asynch_digi_fx(void);
105 digi_timer_id = tm_add_process(asynch_digi_fx, 0, CIT_FREQ << 2);
106 #endif
107 */
108 return (OK);
109 }
110
111 #define DIGIFX_TIMEOUT_TICKS (CIT_CYCLE * 3) >> 1
112 #define DIGIFX_DUPE_TICKS CIT_CYCLE >> 2
113
114 // Returns a 0-255 factor of how loud the sound should be.
115 #define VOL_FULL 0xFF
116 #define FIX_VOL_FULL (fix_make(VOL_FULL, 0))
117 #define MAX_DIGIFX_DIST fix_make(15, 0)
118 #define MIN_DIGIFX_DIST fix_make(2, 0)
119
120 extern int curr_alog;
121
122 // ------------
123 // PROTOYTPES
124 // ------------
125 int compute_sfx_vol(ushort x1, ushort y1, ushort x2, ushort y2);
126 int compute_sfx_pan(ushort x1, ushort y1, ushort x2, ushort y2, fixang our_ang);
127 uchar set_sample_pan_gain(snd_digi_parms *sdp);
128
compute_sfx_vol(ushort x1,ushort y1,ushort x2,ushort y2)129 int compute_sfx_vol(ushort x1, ushort y1, ushort x2, ushort y2) {
130 fix dx, dy, dist;
131 int retval;
132
133 dx = fix_from_obj_coord(x1) - fix_from_obj_coord(x2);
134 dy = fix_from_obj_coord(y1) - fix_from_obj_coord(y2);
135 dist = fix_fast_pyth_dist(dx, dy);
136
137 if (dist > MAX_DIGIFX_DIST)
138 retval = 0;
139 else if (dist < MIN_DIGIFX_DIST)
140 retval = VOL_FULL;
141 else
142 // What, no fix_mul_div_int?
143 retval = fix_int(fix_mul_div(FIX_VOL_FULL, MAX_DIGIFX_DIST - dist, MAX_DIGIFX_DIST - MIN_DIGIFX_DIST));
144 return (retval);
145 }
146
147 // i should just fix this....
compute_sfx_pan(ushort x1,ushort y1,ushort x2,ushort y2,fixang our_ang)148 int compute_sfx_pan(ushort x1, ushort y1, ushort x2, ushort y2, fixang our_ang) {
149 fixang sfx_ang;
150 fix dx, dy;
151 int retval;
152
153 dx = fix_from_obj_coord(x1) - fix_from_obj_coord(x2);
154 dy = fix_from_obj_coord(y1) - fix_from_obj_coord(y2);
155
156 // Do some trig, and move the angle into our relative frame
157 // wait, our_ang is 0-65536 from North, clockwise // so, ah, what is going on????
158 our_ang = 0x4000 - our_ang;
159 sfx_ang = fix_atan2(dy, dx) - our_ang;
160 // Now we have an angle, sfx_ang, which supposedly represents the angle of the source relative to our facing...
161 retval = fix_int(fix_mul(fix_fastsin(sfx_ang), fix_make(-63, 0))) +
162 64; // was cos,i made it sin, flipped to be left-right
163 return (retval);
164 }
165
166 // Returns whether or not in the humble opinion of the
167 // sound system, the sample should be politely obliterated out of existence
set_sample_pan_gain(snd_digi_parms * sdp)168 uchar set_sample_pan_gain(snd_digi_parms *sdp) {
169 uchar temp_vol, vol;
170 uint raw_data = (uint)sdp->data;
171 extern uchar curr_sfx_vol;
172
173 if (raw_data & 0x80000000) {
174 //terrain elevator
175 short x = (raw_data & 0x7FFF0000) >> 16;
176 short y = (raw_data & 0xFFFF);
177
178 temp_vol = compute_sfx_vol(x, y, objs[PLAYER_OBJ].loc.x, objs[PLAYER_OBJ].loc.y);
179 sdp->pan = compute_sfx_pan(x, y, objs[PLAYER_OBJ].loc.x, objs[PLAYER_OBJ].loc.y, objs[PLAYER_OBJ].loc.h << 8);
180 } else if (raw_data != 0) {
181 ObjID id;
182 id = (ObjID)raw_data;
183 temp_vol = compute_sfx_vol(objs[id].loc.x, objs[id].loc.y, objs[PLAYER_OBJ].loc.x, objs[PLAYER_OBJ].loc.y);
184 sdp->pan = compute_sfx_pan(objs[id].loc.x, objs[id].loc.y, objs[PLAYER_OBJ].loc.x, objs[PLAYER_OBJ].loc.y,
185 objs[PLAYER_OBJ].loc.h << 8);
186 } else if (sdp->snd_ref == 0) { // audiolog
187 // following is temp sdp->vol=curr_alog_vol;
188 sdp->vol = curr_alog_vol;
189 snd_sample_reload_parms(sdp);
190 return (FALSE);
191 } else {
192 // sdp->pan=SND_DEF_PAN;
193 temp_vol = VOL_FULL;
194 }
195 vol = volumes[sdp->snd_ref - SFX_BASE];
196
197 /*
198 * shamaz: the origial condition was (vol == -1) which is always
199 * false because vol is unsigned. Perhaps the author was thinking
200 * of something like this:
201 */
202 if (vol == VOL_FULL)
203 vol = 127;
204 vol = vol * curr_sfx_vol / 100;
205 sdp->vol = vol * temp_vol / VOL_FULL;
206 snd_sample_reload_parms(sdp);
207 return (FALSE);
208 }
209
stop_terrain_elevator_sound(short sem)210 void stop_terrain_elevator_sound(short sem)
211 {
212 int i;
213
214 //stop all sound channels that are playing terrain elevator sound with semaphore index sem
215 for (i = 0; i < SND_MAX_SAMPLES; i++)
216 {
217 snd_digi_parms *sdp = snd_sample_parms(i);
218 uint raw_data = (uint)sdp->data;
219
220 if (raw_data & 0x80000000)
221 {
222 short x = (raw_data & 0x7FFF0000) >> 16;
223 short y = (raw_data & 0xFFFF);
224
225 extern height_semaphor h_sems[NUM_HEIGHT_SEMAPHORS];
226
227 if (h_sems[sem].x == (x >> 8) && h_sems[sem].y == (y >> 8) && h_sems[sem].inuse)
228 snd_end_sample(i);
229 }
230 }
231 }
232
233 #ifdef NOT_YET //
234
235 #pragma disable_message(202)
digifx_volume_shift(short x,short y,short z,short phi,short theta,int basevol)236 int digifx_volume_shift(short x, short y, short z, short phi, short theta, int basevol) {
237 int retval;
238 // Note that "x" is really the object ID of the thing we care about
239 // unless phi is set, in which case phi and theta are a literal location to use
240 if (x != OBJ_NULL)
241 retval = compute_sfx_vol(objs[x].loc.x, objs[x].loc.y, objs[PLAYER_OBJ].loc.x, objs[PLAYER_OBJ].loc.y);
242 else if (phi != 0)
243 retval = compute_sfx_vol(phi, theta, objs[PLAYER_OBJ].loc.x, objs[PLAYER_OBJ].loc.y);
244 else
245 retval = VOL_FULL;
246
247 // Now normalize vs basevol
248 retval = basevol * retval / VOL_FULL;
249 return (retval);
250 }
251
digifx_pan_shift(short x,short y,short z,short phi,short theta)252 int digifx_pan_shift(short x, short y, short z, short phi, short theta) {
253 int retval;
254 // Note that "x" is really the object ID of the thing we care about
255 // unless phi is set, in which case phi and theta are a literal location to use
256 if (x != OBJ_NULL)
257 retval = compute_sfx_pan(objs[x].loc.x, objs[x].loc.y, objs[PLAYER_OBJ].loc.x, objs[PLAYER_OBJ].loc.y,
258 objs[PLAYER_OBJ].loc.h << 8);
259 else if (phi != 0)
260 retval =
261 compute_sfx_pan(phi, theta, objs[PLAYER_OBJ].loc.x, objs[PLAYER_OBJ].loc.y, objs[PLAYER_OBJ].loc.h << 8);
262 else
263 retval = 128; // is this right??
264 retval = (retval + 64) >> 1;
265 Spew(DSRC_AUDIO_Testing, ("Modified PAN value=%d\n", retval));
266 return (retval);
267 }
268 #pragma enable_message(202)
269
270 #endif // NOT_YET
271
272 uchar sfx_volume_levels[] = {0, 0x9, 0xF};
273 #define ALWAYS_QUEUE_TOLERANCE 2
274 #define NO_GAIN_THRESHOLD 0x6A
275 #define HARSH_GAIN_THRESHOLD 0xBA
276
277 snd_digi_parms s_dprm;
278 char secret_global_pan = SND_DEF_PAN;
279
play_digi_fx_master(int sfx_code,int num_loops,ObjID id,ushort x,ushort y)280 int play_digi_fx_master(int sfx_code, int num_loops, ObjID id, ushort x, ushort y) {
281 Id vocRes;
282 int retval, real_code = sfx_code, len;
283 uchar *addr;
284 extern uchar sfx_on;
285 extern uchar curr_sfx_vol;
286
287 if (!sfx_on)
288 return -2;
289 if ((sfx_code == -1) || (sfx_code == 255))
290 return -1; // why do we call this with things we dont use?
291
292 #ifdef AUDIOLOGS
293 if (sfx_code > 255)
294 sfx_code = 0;
295 if (audiolog_playing(-1)) // what is this, really?
296 if (sfx_code != real_code)
297 audiolog_stop();
298 if (sfx_code == real_code)
299 #endif
300 {
301 // If the sound effect is too far away, don't even bother us
302 if (id != OBJ_NULL) {
303 if (compute_sfx_vol(objs[id].loc.x, objs[id].loc.y, objs[PLAYER_OBJ].loc.x, objs[PLAYER_OBJ].loc.y) == 0)
304 return -1;
305 } else if (x != 0)
306 if (compute_sfx_vol(x, y, objs[PLAYER_OBJ].loc.x, objs[PLAYER_OBJ].loc.y) == 0)
307 return -1;
308 }
309
310 vocRes = SFX_BASE + real_code;
311
312 // s_dprm is the static data set for the parameters
313 s_dprm.loops = num_loops;
314 s_dprm.pri = priorities[sfx_code];
315 s_dprm.pan = secret_global_pan;
316 #ifdef AUDIOLOGS
317 if (sfx_code != real_code) {
318 s_dprm.data = 0;
319 s_dprm.vol = volumes[sfx_code] * curr_sfx_vol / 100;
320 } else
321 #endif
322 {
323 if (id != OBJ_NULL)
324 s_dprm.data = id;
325 else if (x != 0)
326 s_dprm.data = (0x80000000 | (x << 16) | y);
327 else
328 s_dprm.data = 0;
329 s_dprm.snd_ref = vocRes; // okay, I'm cheating a little here
330 set_sample_pan_gain(&s_dprm);
331 }
332
333 // have to hash x,y no id to a secret ID code, eh?
334 s_dprm.flags = 0;
335 addr = (uchar *)ResLock(vocRes);
336 len = ResSize(vocRes);
337 if (addr != NULL) {
338 retval = snd_sample_play(vocRes, len, addr, &s_dprm);
339 } else
340 critical_error(CRITERR_MEM | 9);
341 if (retval == SND_PERROR) {
342 ResUnlock(vocRes);
343 return -3;
344 }
345 return retval; // which sample id
346 }
347
348 // scan through the whole list
digi_fx_playing(int fx_id,int * handle_ptr)349 uchar digi_fx_playing(int fx_id, int *handle_ptr) {
350 snd_digi_parms *sdp;
351
352 if (fx_id == -1)
353 return FALSE;
354
355 // should scan all current sfx's for snd_ref=fx_id+SFX_BASE
356 for (int i = 0; i < SND_MAX_SAMPLES; i++) {
357 if (snd_sample_playing(i)) {
358 sdp = snd_sample_parms(i);
359 if (sdp->snd_ref == fx_id + SFX_BASE) {
360 if (handle_ptr != NULL)
361 *handle_ptr = i;
362 return TRUE;
363 }
364 }
365 }
366 return FALSE;
367 }
368