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