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