1 /*
2 FBAlpha port by dink, Sept 2018
3
4 *****************************************************************************/
5
6 #include "burnint.h"
7 #include "redbaron.h"
8 #include <math.h>
9
10 #define OUTPUT_RATE (6000*4)
11 #define FRAME_SIZE 400
12
13 static INT16 *m_mixer_buffer; // re-sampler
14
15 static INT32 (*pCPUTotalCycles)() = NULL;
16 static UINT32 nDACCPUMHZ = 0;
17 static INT32 nCurrentPosition = 0;
18
19 static void bzone_update_int(INT16 *buffer, INT32 samples);
20
21 // Streambuffer handling
SyncInternal()22 static INT32 SyncInternal()
23 {
24 return (INT32)(float)(FRAME_SIZE * (pCPUTotalCycles() / (nDACCPUMHZ / (nBurnFPS / 100.0000))));
25 }
26
UpdateStream(INT32 length)27 static void UpdateStream(INT32 length)
28 {
29 if (length > FRAME_SIZE) length = FRAME_SIZE;
30
31 length -= nCurrentPosition;
32 if (length <= 0) return;
33
34 INT16 *lbuf = m_mixer_buffer + nCurrentPosition;
35
36 bzone_update_int(lbuf, length);
37
38 nCurrentPosition += length;
39 }
40
41 /* Statics */
42 static INT16 *discharge = NULL;
43 #define EXP(charge,n) (charge ? 0x7fff - discharge[0x7fff-n] : discharge[n])
44
45 static INT32 latch;
46 static INT32 poly_counter;
47 static INT32 poly_shift;
48
49 static INT32 explosion_clock;
50 static INT32 explosion_out;
51 static INT32 explosion_amp;
52 static INT32 explosion_amp_counter;
53
54 static INT32 shell_clock;
55 static INT32 shell_out;
56 static INT32 shell_amp;
57 static INT32 shell_amp_counter;
58
59 static INT32 motor_counter;
60 static INT32 motor_counter_a;
61 static INT32 motor_counter_b;
62 static INT32 motor_rate;
63 static INT32 motor_rate_new;
64 static INT32 motor_rate_counter;
65 static INT32 motor_amp;
66 static INT32 motor_amp_new;
67 static INT32 motor_amp_step;
68 static INT32 motor_amp_counter;
69
70 INT32 bzone_sound_enable = 0;
71
bzone_sound_scan(INT32 nAction,INT32 *)72 void bzone_sound_scan(INT32 nAction, INT32 *)
73 {
74 SCAN_VAR(latch);
75 SCAN_VAR(poly_counter);
76 SCAN_VAR(poly_shift);
77
78 SCAN_VAR(explosion_clock);
79 SCAN_VAR(explosion_out);
80 SCAN_VAR(explosion_amp);
81 SCAN_VAR(explosion_amp_counter);
82
83 SCAN_VAR(shell_clock);
84 SCAN_VAR(shell_out);
85 SCAN_VAR(shell_amp);
86 SCAN_VAR(shell_amp_counter);
87
88 SCAN_VAR(motor_counter);
89 SCAN_VAR(motor_counter_a);
90 SCAN_VAR(motor_counter_b);
91 SCAN_VAR(motor_rate);
92 SCAN_VAR(motor_rate_new);
93 SCAN_VAR(motor_rate_counter);
94 SCAN_VAR(motor_amp);
95 SCAN_VAR(motor_amp_new);
96 SCAN_VAR(motor_amp_step);
97 SCAN_VAR(motor_amp_counter);
98 }
99
bzone_sound_reset()100 void bzone_sound_reset()
101 {
102 latch = 0;
103 poly_counter = 0;
104 poly_shift = 0;
105
106 explosion_clock = 0;
107 explosion_out = 0;
108 explosion_amp = 0;
109 explosion_amp_counter = 0;
110
111 shell_clock = 0;
112 shell_out = 0;
113 shell_amp = 0;
114 shell_amp_counter = 0;
115
116 motor_counter = 0;
117 motor_counter_a = 0;
118 motor_counter_b = 0;
119 motor_rate = 0;
120 motor_rate_new = 0;
121 motor_rate_counter = 0;
122 motor_amp = 0;
123 motor_amp_new = 0;
124 motor_amp_step = 0;
125 motor_amp_counter = 0;
126
127 bzone_sound_enable = 0;
128 }
129
bzone_sound_init(INT32 (* pCPUCyclesCB)(),INT32 nCpuMHZ)130 void bzone_sound_init(INT32 (*pCPUCyclesCB)(), INT32 nCpuMHZ)
131 {
132 pCPUTotalCycles = pCPUCyclesCB;
133 nDACCPUMHZ = nCpuMHZ;
134
135 m_mixer_buffer = (INT16*)BurnMalloc(2 * sizeof(INT16) * OUTPUT_RATE);
136
137 discharge = (INT16 *)BurnMalloc(32768 * sizeof(INT16));
138 for(INT32 i = 0; i < 0x8000; i++ )
139 discharge[0x7fff-i] = (INT16) (0x7fff/exp(1.0*i/4096));
140 }
141
bzone_sound_exit()142 void bzone_sound_exit()
143 {
144 BurnFree(m_mixer_buffer);
145 BurnFree(discharge);
146 }
147
bzone_update_int(INT16 * buffer,INT32 length)148 void bzone_update_int(INT16 *buffer, INT32 length)
149 {
150 while( length-- )
151 {
152 static INT32 last_val = 0;
153 INT32 sum = 0;
154
155 /* polynome shifter H5 and H4 (LS164) clocked with 6kHz */
156 poly_counter -= 6000;
157 while( poly_counter <= 0 )
158 {
159 INT32 clock;
160
161 poly_counter += OUTPUT_RATE;
162 if( ((poly_shift & 0x0008) == 0) == ((poly_shift & 0x4000) == 0) )
163 poly_shift = (poly_shift << 1) | 1;
164 else
165 poly_shift <<= 1;
166
167 /* NAND gate J4 */
168 clock = ((poly_shift & 0x7000) == 0x7000) ? 0 : 1;
169
170 /* raising edge on pin 3 of J5 (LS74)? */
171 if( clock && !explosion_clock )
172 explosion_out ^= 1;
173
174 /* save explo clock level */
175 explosion_clock = clock;
176
177 /* input 11 of J5 (LS74) */
178 clock = (poly_shift >> 15) & 1;
179
180 /* raising edge on pin 11 of J5 (LS74)? */
181 if( clock && !shell_clock )
182 shell_out ^= 1;
183
184 /* save shell clock level */
185 shell_clock = clock;
186 }
187
188 /* explosion enable: charge C14 */
189 if( latch & 0x01 )
190 explosion_amp = 32767;
191
192 /* explosion output? */
193 if( explosion_out )
194 {
195 if( explosion_amp > 0 )
196 {
197 /*
198 * discharge C14 through R17 + R16
199 * time constant is 10e-6 * 23000 = 0.23 seconds
200 * (samples were decaying much slower: 1/4th rate? )
201 */
202 explosion_amp_counter -= (int)(32767 / (0.23*1));
203 if( explosion_amp_counter < 0 )
204 {
205 INT32 n = (-explosion_amp_counter / OUTPUT_RATE) + 1;
206 explosion_amp_counter += n * OUTPUT_RATE;
207 if( (explosion_amp -= n) < 0 )
208 explosion_amp = 0;
209 }
210 }
211 /*
212 * I don't know the amplification of the op-amp
213 * and feedback, so the loud/soft values are arbitrary
214 */
215 if( latch & 0x02 ) /* explosion loud ? */
216 sum += BURN_SND_CLIP(EXP(0,explosion_amp)/3);
217 else
218 sum += BURN_SND_CLIP(EXP(0,explosion_amp)/4);
219 }
220
221 /* shell enable: charge C9 */
222 if( latch & 0x04 )
223 shell_amp = 32767;
224
225 /* shell output? */
226 if( shell_out )
227 {
228 if( shell_amp > 0 )
229 {
230 /*
231 * discharge C9 through R14 + R15
232 * time constant is 4.7e-6 * 23000 = 0.1081 seconds
233 * (samples were decaying much slower: 1/4th rate? )
234 */
235 shell_amp_counter -= (int)(32767 / (0.1081*4));
236 if( shell_amp_counter < 0 )
237 {
238 INT32 n = (-shell_amp_counter / OUTPUT_RATE) + 1;
239 shell_amp_counter += n * OUTPUT_RATE;
240 if( (shell_amp -= n) < 0 )
241 shell_amp = 0;
242 }
243 }
244 /*
245 * I don't know the amplification of the op-amp
246 * and feedback, so the loud/soft values are arbitrary
247 */
248 if( latch & 0x08 ) /* shell loud ? */
249 sum += BURN_SND_CLIP(EXP(0,shell_amp)/6);
250 else
251 sum += BURN_SND_CLIP(EXP(0,shell_amp)/8);
252 }
253
254 if( latch & 0x80 )
255 {
256 /* NE5555 timer
257 * C = 0.018u, Ra = 100k, Rb = 125k
258 * charge time = 0.693 * (Ra + Rb) * C = 3870us
259 * discharge time = 0.693 * Rb * C = 1559.25us
260 * freq approx. 184 Hz
261 * I have no idea what frequencies are coming from the NE555
262 * with "MOTOR REV EN" being high or low. I took 240Hz as
263 * higher rate and sweep up or down to the new rate in 0.25s
264 */
265 motor_rate_new = (latch & 0x10) ? (940) : (240);
266 if( motor_rate != motor_rate_new )
267 {
268 /* sweep rate to new rate */
269 motor_rate_counter -= (int)(((940) - (240)) / 0.25);
270 while( motor_rate_counter <= 0 )
271 {
272 motor_rate_counter += OUTPUT_RATE;
273 motor_rate += (motor_rate < motor_rate_new) ? +1 : -1;
274 }
275 }
276 motor_counter -= motor_rate;
277 while( motor_counter <= 0 )
278 {
279 double r0, r1;
280
281 motor_counter += OUTPUT_RATE;
282
283 r0 = 1.0/1e12;
284 r1 = 1.0/1e12;
285
286 if( ++motor_counter_a == 16 )
287 motor_counter_a = 6;
288 if( ++motor_counter_b == 16 )
289 motor_counter_b = 4;
290
291 if( motor_counter_a & 8 ) /* bit 3 */
292 r1 += 1.0/33000;
293 else
294 r0 += 1.0/33000;
295 if( motor_counter_a == 15 ) /* ripple carry */
296 r1 += 1.0/33000;
297 else
298 r0 += 1.0/33000;
299
300 if( motor_counter_b & 8 ) /* bit 3 */
301 r1 += 1.0/33000;
302 else
303 r0 += 1.0/33000;
304 if( motor_counter_b == 15 ) /* ripple carry */
305 r1 += 1.0/33000;
306 else
307 r0 += 1.0/33000;
308
309 /* new voltage at C29 */
310 r0 = 1.0/r0;
311 r1 = 1.0/r1;
312 motor_amp_new = (int)(32767 * r0 / (r0 + r1));
313
314 /* charge/discharge C29 (0.47uF) */
315 if( motor_amp_new > motor_amp )
316 motor_amp_step = (int)((motor_amp_new - motor_amp) / (r1*0.47e-6));
317 else
318 motor_amp_step = (int)((motor_amp - motor_amp_new) / (r0*0.47e-6));
319 }
320 if( motor_amp != motor_amp_new )
321 {
322 motor_amp_counter -= motor_amp_step;
323 if( motor_amp_counter < 0 )
324 {
325 INT32 n = (-motor_amp_counter / OUTPUT_RATE) + 1;
326 motor_amp_counter += n * OUTPUT_RATE;
327 if( motor_amp > motor_amp_new )
328 {
329 motor_amp -= n;
330 if( motor_amp < motor_amp_new )
331 motor_amp = motor_amp_new;
332 }
333 else
334 {
335 motor_amp += n;
336 if( motor_amp > motor_amp_new )
337 motor_amp = motor_amp_new;
338 }
339 }
340 }
341 sum += EXP((motor_amp<motor_amp_new),motor_amp)/30;
342 }
343
344 *buffer++ = BURN_SND_CLIP((sum + last_val) / 2);
345
346 /* crude 75% low pass filter */
347 last_val = 0;//(sum + last_val * 3) / 4;
348 }
349 }
350
bzone_sound_write(UINT8 data)351 void bzone_sound_write(UINT8 data)
352 {
353 if( data == latch )
354 return;
355
356 bzone_sound_enable = data & (1 << 5);
357
358 UpdateStream(SyncInternal());
359
360 latch = data;
361 }
362
bzone_sound_update(INT16 * inputs,INT32 sample_len)363 void bzone_sound_update(INT16 *inputs, INT32 sample_len)
364 {
365 if (sample_len != nBurnSoundLen) {
366 bprintf(PRINT_ERROR, _T("*** bzone_sound_update(): call once per frame!\n"));
367 return;
368 }
369
370 INT32 samples_from = (INT32)((double)((OUTPUT_RATE * 100) / nBurnFPS) + 0.5);
371
372 UpdateStream(samples_from);
373
374 for (INT32 j = 0; j < sample_len; j++)
375 {
376 INT32 k = (samples_from * j) / nBurnSoundLen;
377
378 INT32 rlmono = m_mixer_buffer[k];
379
380 inputs[0] = BURN_SND_CLIP(inputs[0] + BURN_SND_CLIP(rlmono));
381 inputs[1] = BURN_SND_CLIP(inputs[1] + BURN_SND_CLIP(rlmono));
382 inputs += 2;
383 }
384
385 memset(m_mixer_buffer, 0, samples_from * sizeof(INT16));
386 nCurrentPosition = 0;
387 }
388