1 /*
2    FBAlpha port by iq_132, dink, Sept 2018
3 
4 *****************************************************************************/
5 
6 #include "burnint.h"
7 #include "redbaron.h"
8 #include <math.h>
9 
10 #define OUTPUT_RATE 48000
11 #define FRAME_SIZE 800
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 redbaron_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 	redbaron_update_int(lbuf, length);
37 
38 	nCurrentPosition += length;
39 }
40 
41 static INT32 m_latch;
42 static INT32 m_poly_counter;
43 static INT32 m_poly_shift;
44 static INT32 m_filter_counter;
45 static INT32 m_crash_amp;
46 static INT32 m_shot_amp;
47 static INT32 m_shot_amp_counter;
48 static INT32 m_squeal_amp;
49 static INT32 m_squeal_amp_counter;
50 static INT32 m_squeal_off_counter;
51 static INT32 m_squeal_on_counter;
52 static INT32 m_squeal_out;
53 static INT16 *m_vol_lookup;
54 static INT16 m_vol_crash[16];
55 
redbaron_sound_scan(INT32 nAction,INT32 *)56 void redbaron_sound_scan(INT32 nAction, INT32 *)
57 {
58 	SCAN_VAR(m_latch);
59 	SCAN_VAR(m_poly_counter);
60 	SCAN_VAR(m_poly_shift);
61 	SCAN_VAR(m_filter_counter);
62 	SCAN_VAR(m_crash_amp);
63 	SCAN_VAR(m_shot_amp);
64 	SCAN_VAR(m_shot_amp_counter);
65 	SCAN_VAR(m_squeal_amp);
66 	SCAN_VAR(m_squeal_amp_counter);
67 	SCAN_VAR(m_squeal_off_counter);
68 	SCAN_VAR(m_squeal_on_counter);
69 	SCAN_VAR(m_squeal_out);
70 }
71 
redbaron_sound_reset()72 void redbaron_sound_reset()
73 {
74 	m_latch = 0;
75 	m_poly_counter = 0;
76 	m_poly_shift = 0;
77 	m_filter_counter = 0;
78 	m_crash_amp = 0;
79 	m_shot_amp = 0;
80 	m_shot_amp_counter = 0;
81 	m_squeal_amp = 0;
82 	m_squeal_amp_counter = 0;
83 	m_squeal_off_counter = 0;
84 	m_squeal_on_counter = 0;
85 	m_squeal_out = 0;
86 }
87 
redbaron_sound_init(INT32 (* pCPUCyclesCB)(),INT32 nCpuMHZ)88 void redbaron_sound_init(INT32 (*pCPUCyclesCB)(), INT32 nCpuMHZ)
89 {
90 	INT32 i;
91 
92 	pCPUTotalCycles = pCPUCyclesCB;
93 	nDACCPUMHZ = nCpuMHZ;
94 
95 	m_vol_lookup = (INT16*)BurnMalloc(0x8000 * 4);
96 	m_mixer_buffer = (INT16*)BurnMalloc(2 * sizeof(INT16) * OUTPUT_RATE);
97 
98 	for( i = 0; i < 0x8000; i++ )
99 		m_vol_lookup[0x7fff-i] = (INT16) (0x7fff/exp(1.0*i/4096));
100 
101 	for( i = 0; i < 16; i++ )
102 	{
103 		/* r0 = R18 and R24, r1 = open */
104 		double r0 = 1.0/(5600 + 680), r1 = 1/6e12;
105 
106 		/* R14 */
107 		if( i & 1 )
108 			r1 += 1.0/8200;
109 		else
110 			r0 += 1.0/8200;
111 		/* R15 */
112 		if( i & 2 )
113 			r1 += 1.0/3900;
114 		else
115 			r0 += 1.0/3900;
116 		/* R16 */
117 		if( i & 4 )
118 			r1 += 1.0/2200;
119 		else
120 			r0 += 1.0/2200;
121 		/* R17 */
122 		if( i & 8 )
123 			r1 += 1.0/1000;
124 		else
125 			r0 += 1.0/1000;
126 
127 		r0 = 1.0/r0;
128 		r1 = 1.0/r1;
129 		m_vol_crash[i] = (INT16)(32767 * r0 / (r0 + r1));
130 	}
131 }
132 
redbaron_sound_exit()133 void redbaron_sound_exit()
134 {
135 	BurnFree(m_mixer_buffer);
136 	BurnFree(m_vol_lookup);
137 }
138 
redbaron_update_int(INT16 * buffer,INT32 samples)139 void redbaron_update_int(INT16 *buffer, INT32 samples)
140 {
141 	while( samples-- )
142 	{
143 		INT32 sum = 0;
144 
145 		/* polynomial shifter E5 and F4 (LS164) clocked with 12kHz */
146 		m_poly_counter -= 12000;
147 		while( m_poly_counter <= 0 )
148 		{
149 			m_poly_counter += OUTPUT_RATE;
150 			if( ((m_poly_shift & 0x0001) == 0) == ((m_poly_shift & 0x4000) == 0) )
151 				m_poly_shift = (m_poly_shift << 1) | 1;
152 			else
153 				m_poly_shift <<= 1;
154 		}
155 
156 		/* What is the exact low pass filter frequency? */
157 		m_filter_counter -= 330;
158 		while( m_filter_counter <= 0 )
159 		{
160 			m_filter_counter += OUTPUT_RATE;
161 			m_crash_amp = (m_poly_shift & 1) ? m_latch >> 4 : 0;
162 		}
163 		/* mix crash sound at 35% */
164 		sum += BURN_SND_CLIP(m_vol_crash[m_crash_amp] * 35 / 100);
165 
166 		/* shot not active: charge C32 (0.1u) */
167 		if( (m_latch & 0x04) == 0 )
168 			m_shot_amp = 32767;
169 		else
170 		if( (m_poly_shift & 0x8000) == 0 )
171 		{
172 			if( m_shot_amp > 0 )
173 			{
174 				/* I think this is too short. Is C32 really 1u? */
175 				#define C32_DISCHARGE_TIME (int)(32767 / 0.03264);
176 				m_shot_amp_counter -= C32_DISCHARGE_TIME;
177 				while( m_shot_amp_counter <= 0 )
178 				{
179 					m_shot_amp_counter += OUTPUT_RATE;
180 					if( --m_shot_amp == 0 )
181 						break;
182 				}
183 				/* mix shot sound at 35% */
184 				sum += m_vol_lookup[m_shot_amp] * 35 / 100;
185 			}
186 		}
187 
188 
189 		if( (m_latch & 0x02) == 0 )
190 			m_squeal_amp = 32767;
191 		else
192 		{
193 			if( m_squeal_amp >= 0 )
194 			{
195 				/* charge C5 (22u) over R3 (68k) and CR1 (1N914)
196 				 * time = 0.68 * C5 * R3 = 1017280us
197 				 */
198 				#define C5_CHARGE_TIME (int)(32767 / 1.01728);
199 				m_squeal_amp_counter -= C5_CHARGE_TIME;
200 				while( m_squeal_amp_counter <= 0 )
201 				{
202 					m_squeal_amp_counter += OUTPUT_RATE;
203 					if( --m_squeal_amp == 0 )
204 						break;
205 				}
206 			}
207 
208 			if( m_squeal_out )
209 			{
210 				/* NE555 setup as pulse position modulator
211 				 * C = 0.01u, Ra = 33k, Rb = 47k
212 				 * frequency = 1.44 / ((33k + 2*47k) * 0.01u) = 1134Hz
213 				 * modulated by squeal_amp
214 				 */
215 				m_squeal_off_counter -= (((1134 + 1134) * 3) * m_squeal_amp / 32767) / 3;
216 				while( m_squeal_off_counter <= 0 )
217 				{
218 					m_squeal_off_counter += OUTPUT_RATE;
219 					m_squeal_out = 0;
220 				}
221 			}
222 			else
223 			{
224 				m_squeal_on_counter -= 1134*10;
225 				while( m_squeal_on_counter <= 0 )
226 				{
227 					m_squeal_on_counter += OUTPUT_RATE;
228 					m_squeal_out = 1;
229 				}
230 			}
231 		}
232 
233 		/* mix sequal sound at 40% */
234 		if( m_squeal_out )
235 			sum += 32767 * 25 / 100;
236 
237 		*buffer = BURN_SND_CLIP(sum);
238 		buffer++;
239 	}
240 }
241 
redbaron_sound_write(UINT8 data)242 void redbaron_sound_write(UINT8 data)
243 {
244 	if( data == m_latch )
245 		return;
246 
247 	UpdateStream(SyncInternal());
248 
249 	m_latch = data;
250 }
251 
redbaron_sound_update(INT16 * inputs,INT32 sample_len)252 void redbaron_sound_update(INT16 *inputs, INT32 sample_len)
253 {
254 	if (sample_len != nBurnSoundLen) {
255 		bprintf(PRINT_ERROR, _T("*** redbaron_sound_update(): call once per frame!\n"));
256 		return;
257 	}
258 
259 	INT32 samples_from = (INT32)((double)((OUTPUT_RATE * 100) / nBurnFPS) + 0.5);
260 
261 	UpdateStream(samples_from);
262 
263 	for (INT32 j = 0; j < sample_len; j++)
264 	{
265 		INT32 k = (samples_from * j) / nBurnSoundLen;
266 
267 		INT32 rlmono = m_mixer_buffer[k];
268 
269 		inputs[0] = BURN_SND_CLIP(inputs[0] + BURN_SND_CLIP(rlmono));
270 		inputs[1] = BURN_SND_CLIP(inputs[1] + BURN_SND_CLIP(rlmono));
271 		inputs += 2;
272 	}
273 
274 	memset(m_mixer_buffer, 0, samples_from * sizeof(INT16));
275 	nCurrentPosition = 0;
276 }
277