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