1 // license:BSD-3-Clause
2 // copyright-holders:Derrick Renaud
3 /***************************************************************************
4     polepos.c
5     Sound handler
6 ****************************************************************************/
7 #include "emu.h"
8 #include "polepos.h"
9 
10 #include "namco52.h"
11 #include "namco54.h"
12 
13 #include "machine/rescap.h"
14 
15 
16 #define OUTPUT_RATE         24000
17 
18 #define POLEPOS_R166        1000.0
19 #define POLEPOS_R167        2200.0
20 #define POLEPOS_R168        4700.0
21 
22 /* resistor values when shorted by 4066 running at 5V */
23 #define POLEPOS_R166_SHUNT  1.0/(1.0/POLEPOS_R166 + 1.0/250)
24 #define POLEPOS_R167_SHUNT  1.0/(1.0/POLEPOS_R166 + 1.0/250)
25 #define POLEPOS_R168_SHUNT  1.0/(1.0/POLEPOS_R166 + 1.0/250)
26 
27 static const double volume_table[8] =
28 {
29 	(POLEPOS_R168_SHUNT + POLEPOS_R167_SHUNT + POLEPOS_R166_SHUNT + 2200) / 10000,
30 	(POLEPOS_R168_SHUNT + POLEPOS_R167_SHUNT + POLEPOS_R166       + 2200) / 10000,
31 	(POLEPOS_R168_SHUNT + POLEPOS_R167       + POLEPOS_R166_SHUNT + 2200) / 10000,
32 	(POLEPOS_R168_SHUNT + POLEPOS_R167       + POLEPOS_R166       + 2200) / 10000,
33 	(POLEPOS_R168       + POLEPOS_R167_SHUNT + POLEPOS_R166_SHUNT + 2200) / 10000,
34 	(POLEPOS_R168       + POLEPOS_R167_SHUNT + POLEPOS_R166       + 2200) / 10000,
35 	(POLEPOS_R168       + POLEPOS_R167       + POLEPOS_R166_SHUNT + 2200) / 10000,
36 	(POLEPOS_R168       + POLEPOS_R167       + POLEPOS_R166       + 2200) / 10000
37 };
38 
39 static const double r_filt_out[3] = {RES_K(4.7), RES_K(7.5), RES_K(10)};
40 static const double r_filt_total = 1.0 / (1.0/RES_K(4.7) + 1.0/RES_K(7.5) + 1.0/RES_K(10));
41 
42 /* Max filter order */
43 #define FILTER_ORDER_MAX 51
44 
45 /* Define to use integer calculation */
46 #define FILTER_USE_INT
47 
48 #ifdef FILTER_USE_INT
49 typedef int filter_real;
50 #define FILTER_INT_FRACT 15 /* fractional bits */
51 #else
52 typedef double filter_real;
53 #endif
54 
55 struct filter
56 {
57 	filter_real xcoeffs[(FILTER_ORDER_MAX+1)/2];
58 	unsigned order;
59 };
60 
61 struct filter_state
62 {
63 	unsigned prev_mac;
64 	filter_real xprev[FILTER_ORDER_MAX];
65 };
66 
67 /* Filter types */
68 #define FILTER_LOWPASS      0
69 #define FILTER_HIGHPASS     1
70 #define FILTER_BANDPASS     2
71 
72 #define Q_TO_DAMP(q)    (1.0/q)
73 
74 
75 /* Setup the filter context based on the passed filter type info.
76  * type - 1 of the 3 defined filter types
77  * fc   - center frequency
78  * d    - damp = 1/Q
79  * gain - overall filter gain. Set to 1 if not needed.
80  */
setup(device_t * device,int type,double fc,double d,double gain)81 void polepos_sound_device::filter2_context::setup(device_t *device, int type, double fc, double d, double gain)
82 {
83 	int const sample_rate = device->machine().sample_rate();
84 	double const two_over_T = 2*sample_rate;
85 	double const two_over_T_squared = two_over_T * two_over_T;
86 
87 	/* calculate digital filter coefficents */
88 	/* cutoff freq, in radians/sec */
89 	/*w = 2.0*M_PI*fc; no pre-warping */
90 	double const w = sample_rate*2.0*tan(M_PI*fc/sample_rate); /* pre-warping */
91 	double const w_squared = w*w;
92 
93 	/* temp variable */
94 	double const den = two_over_T_squared + d*w*two_over_T + w_squared;
95 
96 	a1 = 2.0*(-two_over_T_squared + w_squared)/den;
97 	a2 = (two_over_T_squared - d*w*two_over_T + w_squared)/den;
98 
99 	switch (type)
100 	{
101 	case FILTER_LOWPASS:
102 		b0 = b2 = w_squared/den;
103 		b1 = 2.0*(b0);
104 		break;
105 	case FILTER_BANDPASS:
106 		b0 = d*w*two_over_T/den;
107 		b1 = 0.0;
108 		b2 = -(b0);
109 		break;
110 	case FILTER_HIGHPASS:
111 		b0 = b2 = two_over_T_squared/den;
112 		b1 = -2.0*(b0);
113 		break;
114 	default:
115 		device->logerror("filter2_setup() - Invalid filter type for 2nd order filter.");
116 		break;
117 	}
118 
119 	b0 *= gain;
120 	b1 *= gain;
121 	b2 *= gain;
122 }
123 
124 
125 /* Reset the input/output voltages to 0. */
reset()126 void polepos_sound_device::filter2_context::reset()
127 {
128 	x0 = 0;
129 	x1 = 0;
130 	x2 = 0;
131 	y0 = 0;
132 	y1 = 0;
133 	y2 = 0;
134 }
135 
136 
137 /* Step the filter.
138  * x0 is the new input, which needs to be set before stepping.
139  * y0 is the new filter output.
140  */
step()141 void polepos_sound_device::filter2_context::step()
142 {
143 	y0 = -a1 * y1 - a2 * y2 + b0 * x0 + b1 * x1 + b2 * x2;
144 	x2 = x1;
145 	x1 = x0;
146 	y2 = y1;
147 	y1 = y0;
148 }
149 
150 
151 /* Setup a filter2 structure based on an op-amp multipole bandpass circuit.
152  * NOTE: If r2 is not used then set to 0.
153  *       vRef is not needed to setup filter.
154  *
155  *                             .--------+---------.
156  *                             |        |         |
157  *                            --- c1    Z         |
158  *                            ---       Z r3      |
159  *                             |        Z         |
160  *            r1               |  c2    |  |\     |
161  *   In >----ZZZZ----+---------+--||----+  | \    |
162  *                   Z                  '--|- \   |
163  *                   Z r2                  |   >--+------> out
164  *                   Z                  .--|+ /
165  *                   |                  |  | /
166  *                  gnd        vRef >---'  |/
167  *
168  */
opamp_m_bandpass_setup(device_t * device,double r1,double r2,double r3,double c1,double c2)169 void polepos_sound_device::filter2_context::opamp_m_bandpass_setup(device_t *device, double r1, double r2, double r3, double c1, double c2)
170 {
171 	if (r1 == 0)
172 	{
173 		device->logerror("filter_opamp_m_bandpass_setup() - r1 can not be 0");
174 		return; /* Filter can not be setup.  Undefined results. */
175 	}
176 
177 	double  r_in, gain;
178 
179 	if (r2 == 0)
180 	{
181 		gain = 1;
182 		r_in = r1;
183 	}
184 	else
185 	{
186 		gain = r2 / (r1 + r2);
187 		r_in = 1.0 / (1.0/r1 + 1.0/r2);
188 	}
189 
190 	double const fc = 1.0 / (2 * M_PI * sqrt(r_in * r3 * c1 * c2));
191 	double const d = (c1 + c2) / sqrt(r3 / r_in * c1 * c2);
192 	gain *= -r3 / r_in * c2 / (c1 + c2);
193 
194 	setup(device, FILTER_BANDPASS, fc, d, gain);
195 }
196 
197 
198 // device type definition
199 DEFINE_DEVICE_TYPE(POLEPOS_SOUND, polepos_sound_device, "polepos_sound", "Pole Position Custom Sound")
200 
201 
202 //**************************************************************************
203 //  LIVE DEVICE
204 //**************************************************************************
205 
206 //-------------------------------------------------
207 //  polepos_sound_device - constructor
208 //-------------------------------------------------
209 
polepos_sound_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)210 polepos_sound_device::polepos_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
211 	: device_t(mconfig, POLEPOS_SOUND, tag, owner, clock),
212 		device_sound_interface(mconfig, *this),
213 		m_current_position(0),
214 		m_sample_msb(0),
215 		m_sample_lsb(0),
216 		m_sample_enable(0),
217 		m_stream(nullptr)
218 {
219 }
220 
221 
222 //-------------------------------------------------
223 //  device_start - device-specific startup
224 //-------------------------------------------------
225 
device_start()226 void polepos_sound_device::device_start()
227 {
228 	m_stream = stream_alloc(0, 1, OUTPUT_RATE);
229 	m_sample_msb = m_sample_lsb = 0;
230 	m_sample_enable = 0;
231 
232 	/* setup the filters */
233 	m_filter_engine[0].opamp_m_bandpass_setup(this, RES_K(220), RES_K(33), RES_K(390), CAP_U(.01),  CAP_U(.01));
234 	m_filter_engine[1].opamp_m_bandpass_setup(this, RES_K(150), RES_K(22), RES_K(330), CAP_U(.0047),  CAP_U(.0047));
235 	/* Filter 3 is a little different.  Because of the input capacitor, it is
236 	 * a high pass filter. */
237 	m_filter_engine[2].setup(this, FILTER_HIGHPASS, 950, Q_TO_DAMP(.707), 1);
238 }
239 
240 
241 //-------------------------------------------------
242 //  device_reset - device-specific reset
243 //-------------------------------------------------
244 
device_reset()245 void polepos_sound_device::device_reset()
246 {
247 	int loop;
248 	for (loop = 0; loop < 3; loop++)
249 		m_filter_engine[loop].reset();
250 }
251 
252 
253 //-------------------------------------------------
254 //  sound_stream_update - handle a stream update
255 //-------------------------------------------------
256 
sound_stream_update(sound_stream & stream,std::vector<read_stream_view> const & inputs,std::vector<write_stream_view> & outputs)257 void polepos_sound_device::sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs)
258 {
259 	uint32_t step, clock, slot;
260 	uint8_t *base;
261 	double volume, i_total;
262 	auto &buffer = outputs[0];
263 	int loop;
264 
265 	/* if we're not enabled, just fill with 0 */
266 	if (!m_sample_enable)
267 	{
268 		buffer.fill(0);
269 		return;
270 	}
271 
272 	/* determine the effective clock rate */
273 	clock = (unscaled_clock() / 16) * ((m_sample_msb + 1) * 64 + m_sample_lsb + 1) / (64*64);
274 	step = (clock << 12) / OUTPUT_RATE;
275 
276 	/* determine the volume */
277 	slot = (m_sample_msb >> 3) & 7;
278 	volume = volume_table[slot];
279 	base = &machine().root_device().memregion("engine")->base()[slot * 0x800];
280 
281 	/* fill in the sample */
282 	for (int sampindex = 0; sampindex < buffer.samples(); sampindex++)
283 	{
284 		m_filter_engine[0].x0 = (3.4 / 255 * base[(m_current_position >> 12) & 0x7ff] - 2) * volume;
285 		m_filter_engine[1].x0 = m_filter_engine[0].x0;
286 		m_filter_engine[2].x0 = m_filter_engine[0].x0;
287 
288 		i_total = 0;
289 		for (loop = 0; loop < 3; loop++)
290 		{
291 			m_filter_engine[loop].step();
292 			/* The op-amp powered @ 5V will clip to 0V & 3.5V.
293 			 * Adjusted to vRef of 2V, we will clip as follows: */
294 			if (m_filter_engine[loop].y0 > 1.5) m_filter_engine[loop].y0 = 1.5;
295 			if (m_filter_engine[loop].y0 < -2)  m_filter_engine[loop].y0 = -2;
296 
297 			i_total += m_filter_engine[loop].y0 / r_filt_out[loop];
298 		}
299 		i_total *= r_filt_total/2;  /* now contains voltage adjusted by final gain */
300 
301 		buffer.put(sampindex, i_total);
302 		m_current_position += step;
303 	}
304 }
305 
306 
WRITE_LINE_MEMBER(polepos_sound_device::clson_w)307 WRITE_LINE_MEMBER(polepos_sound_device::clson_w)
308 {
309 	if (!state)
310 	{
311 		polepos_engine_sound_lsb_w(0);
312 		polepos_engine_sound_msb_w(0);
313 	}
314 }
315 
316 
317 /************************************/
318 /* Write LSB of engine sound        */
319 /************************************/
polepos_engine_sound_lsb_w(uint8_t data)320 void polepos_sound_device::polepos_engine_sound_lsb_w(uint8_t data)
321 {
322 	/* Update stream first so all samples at old frequency are updated. */
323 	m_stream->update();
324 	m_sample_lsb = data & 62;
325 	m_sample_enable = data & 1;
326 }
327 
328 /************************************/
329 /* Write MSB of engine sound        */
330 /************************************/
polepos_engine_sound_msb_w(uint8_t data)331 void polepos_sound_device::polepos_engine_sound_msb_w(uint8_t data)
332 {
333 	m_stream->update();
334 	m_sample_msb = data & 63;
335 }
336 
337 
338 /*************************************
339  *
340  *  Pole Position
341  *
342  *  Discrete sound emulation: Feb 2007, D.R.
343  *
344  *************************************/
345 
346 /* nodes - sounds */
347 #define POLEPOS_CHANL1_SND      NODE_11
348 #define POLEPOS_CHANL2_SND      NODE_12
349 #define POLEPOS_CHANL3_SND      NODE_13
350 #define POLEPOS_CHANL4_SND      NODE_14
351 
352 #define POLEPOS_54XX_DAC_R (1.0 / (1.0 / RES_K(47) + 1.0 / RES_K(22) + 1.0 / RES_K(10) + 1.0 / RES_K(4.7)))
353 static const discrete_dac_r1_ladder polepos_54xx_dac =
354 {
355 	4,               /* number of DAC bits */
356 						/* 54XX_0   54XX_1  54XX_2 */
357 	{ RES_K(47),     /* R124,    R136,   R152 */
358 		RES_K(22),   /* R120,    R132,   R142 */
359 		RES_K(10),   /* R119,    R131,   R138 */
360 		RES_K(4.7)}, /* R118,    R126,   R103 */
361 	0, 0, 0, 0       /* nothing extra */
362 };
363 
364 #define POLEPOS_52XX_DAC_R (1.0 / (1.0 / RES_K(100) + 1.0 / RES_K(47) + 1.0 / RES_K(22) + 1.0 / RES_K(10)))
365 static const discrete_dac_r1_ladder polepos_52xx_dac =
366 {
367 	4,              /* number of DAC bits */
368 	{ RES_K(100),   /* R160 */
369 		RES_K(47),  /* R159 */
370 		RES_K(22),  /* R155 */
371 		RES_K(10)}, /* R154 */
372 	0, 0, 0, 0      /* nothing extra */
373 };
374 
375 /*                           R117        R116         R117 */
376 #define POLEPOS_VREF (5.0 * (RES_K(1) / (RES_K(1.5) + RES_K(1))))
377 
378 static const discrete_op_amp_filt_info polepos_chanl1_filt =
379 {
380 	POLEPOS_54XX_DAC_R + RES_K(22), /* R121 */
381 	0,                  /* no second input */
382 	RES_K(12),          /* R125 */
383 	0,                  /* not used */
384 	RES_K(120),         /* R122 */
385 	CAP_U(0.0022),      /* C27 */
386 	CAP_U(0.0022),      /* C28 */
387 	0,                  /* not used */
388 	POLEPOS_VREF,       /* vRef */
389 	5,                  /* vP */
390 	0                   /* vN */
391 };
392 
393 static const discrete_op_amp_filt_info polepos_chanl2_filt =
394 {
395 	POLEPOS_54XX_DAC_R + RES_K(15), /* R133 */
396 	0,                  /* no second input */
397 	RES_K(15),          /* R137 */
398 	0,                  /* not used */
399 	RES_K(120),         /* R134 */
400 	CAP_U(0.022),       /* C29 */
401 	CAP_U(0.022),       /* C30 */
402 	0,                  /* not used */
403 	POLEPOS_VREF,       /* vRef */
404 	5,                  /* vP */
405 	0                   /* vN */
406 };
407 
408 static const discrete_op_amp_filt_info polepos_chanl3_filt =
409 {
410 	POLEPOS_54XX_DAC_R + RES_K(22), /* R139 */
411 	0,                  /* no second input */
412 	RES_K(22),          /* R143 */
413 	0,                  /* not used */
414 	RES_K(180),         /* R140 */
415 	CAP_U(0.047),       /* C33 */
416 	CAP_U(0.047),       /* C34 */
417 	0,                  /* not used */
418 	POLEPOS_VREF,       /* vRef */
419 	5,                  /* vP */
420 	0                   /* vN */
421 };
422 
423 
424 DISCRETE_SOUND_START(polepos_discrete)
425 
426 	/************************************************
427 	 * Input register mapping
428 	 ************************************************/
429 	DISCRETE_INPUT_DATA(NAMCO_54XX_0_DATA(NODE_01))
430 	DISCRETE_INPUT_DATA(NAMCO_54XX_1_DATA(NODE_01))
431 	DISCRETE_INPUT_DATA(NAMCO_54XX_2_DATA(NODE_01))
432 	DISCRETE_INPUT_DATA(NAMCO_52XX_P_DATA(NODE_04))
433 
434 	/************************************************
435 	 * CHANL1 sound
436 	 ************************************************/
437 	DISCRETE_DAC_R1(NODE_20,
438 					NAMCO_54XX_2_DATA(NODE_01),
439 					4,          /* 4V - unmeasured*/
440 					&polepos_54xx_dac)
441 	DISCRETE_OP_AMP_FILTER(NODE_21,
442 					1,          /* ENAB */
443 					NODE_20,    /* INP0 */
444 					0,          /* INP1 - not used */
445 					DISC_OP_AMP_FILTER_IS_BAND_PASS_1M, &polepos_chanl1_filt)
446 	/* fake it so 0 is now vRef */
447 	DISCRETE_ADDER2(POLEPOS_CHANL1_SND,
448 					1,          /* ENAB */
449 					NODE_21, -POLEPOS_VREF)
450 
451 	/************************************************
452 	 * CHANL2 sound
453 	 ************************************************/
454 	DISCRETE_DAC_R1(NODE_30,
455 					NAMCO_54XX_1_DATA(NODE_01),
456 					4,          /* 4V - unmeasured*/
457 					&polepos_54xx_dac)
458 	DISCRETE_OP_AMP_FILTER(NODE_31,
459 					1,          /* ENAB */
460 					NODE_30,    /* INP0 */
461 					0,          /* INP1 - not used */
462 					DISC_OP_AMP_FILTER_IS_BAND_PASS_1M, &polepos_chanl2_filt)
463 	/* fake it so 0 is now vRef */
464 	DISCRETE_ADDER2(POLEPOS_CHANL2_SND,
465 					1,          /* ENAB */
466 					NODE_31, -POLEPOS_VREF)
467 
468 	/************************************************
469 	 * CHANL3 sound
470 	 ************************************************/
471 	DISCRETE_DAC_R1(NODE_40,
472 					NAMCO_54XX_0_DATA(NODE_01),
473 					4,          /* 4V - unmeasured*/
474 					&polepos_54xx_dac)
475 	DISCRETE_OP_AMP_FILTER(NODE_41,
476 					1,          /* ENAB */
477 					NODE_40,    /* INP0 */
478 					0,          /* INP1 - not used */
479 					DISC_OP_AMP_FILTER_IS_BAND_PASS_1M, &polepos_chanl3_filt)
480 	/* fake it so 0 is now vRef */
481 	DISCRETE_ADDER2(POLEPOS_CHANL3_SND,
482 					1,          /* ENAB */
483 					NODE_41, -POLEPOS_VREF)
484 
485 	/************************************************
486 	 * CHANL4 sound
487 	 ************************************************/
488 	/* this circuit was simulated in SPICE and an equivalent filter circuit generated */
489 	DISCRETE_DAC_R1(NODE_50,
490 					NAMCO_52XX_P_DATA(NODE_04),
491 					4,          /* 4V - unmeasured*/
492 					&polepos_52xx_dac)
493 	/* fake it so 0 is now vRef */
494 	DISCRETE_ADDER2(NODE_51,
495 					1,          /* ENAB */
496 					NODE_50, -POLEPOS_VREF)
497 	DISCRETE_FILTER2(NODE_52,
498 					1,          /* ENAB */
499 					NODE_51,    /* INP0 */
500 					100,        /* FREQ */
501 					1.0 / 0.3,  /* DAMP */
502 					DISC_FILTER_HIGHPASS)
503 	DISCRETE_FILTER2(NODE_53,
504 					1,          /* ENAB */
505 					NODE_52,    /* INP0 */
506 					1200,       /* FREQ */
507 					1.0 / 0.8,  /* DAMP */
508 					DISC_FILTER_LOWPASS)
509 	DISCRETE_GAIN(NODE_54,
510 					NODE_53,    /* IN0 */
511 					0.5         /* overall filter GAIN */)
512 	/* clamp to the maximum of the op-amp shifted by vRef */
513 	DISCRETE_CLAMP(POLEPOS_CHANL4_SND,
514 					NODE_54,    /* IN0 */
515 					0,          /* MIN */
516 					5.0 - OP_AMP_VP_RAIL_OFFSET - POLEPOS_VREF) /* MAX */
517 
518 	/************************************************
519 	 * Output
520 	 ************************************************/
521 	DISCRETE_OUTPUT(POLEPOS_CHANL1_SND, 32767/2)
522 	DISCRETE_OUTPUT(POLEPOS_CHANL2_SND, 32767/2)
523 	DISCRETE_OUTPUT(POLEPOS_CHANL3_SND, 32767/2)
524 	DISCRETE_OUTPUT(POLEPOS_CHANL4_SND, 32767/2)
525 DISCRETE_SOUND_END
526