summaryrefslogblamecommitdiffstatshomepage
path: root/src/mame/audio/polepos.cpp
blob: f911dd2ae2ebb82ba0dc24749da2af2d75cd5c62 (plain) (tree)
1
2
3
4
5
6
7
8
9
                       
                                   



                                                                             
                
                    
 
                    
                    


                           
 
                                 
 


                                  
 
                                                        


                                                            
 
                                     










                                                                                      

                                                                                           
 

                           
 





















                                                    






                               
 





                                                                 









































                                                                                                               


                                           








                                                   





                                                              







                                                              



















                                                                           
                                                                                                                                           
 





                                                                                      

                           










                                               

                                                                       

                                            
                                                    
 


                         
                                                                                                      









                                                                            
                                                                                                                           
                                                              




                                                       
                                 








                                                   
 


                                                   
 
                               

                                                                                                                        

                                                                                 
                                                                                 

 








                                                   
                                              







                                                                                                                                      
 

                                   




                                                    
                             
         
                                                             



                                                
                                                                                                                              


                                           
                                       
                                    
                                                                                  

                                
                         
         


                                                                                                            



                                                
                                                     
                                                                          
                                                                              

                                                                                           
 
                                                                               
                 
                                                                                                     

                                         
                                           


         
 









                                                                          


                                      
                                                               

                                                                              


                                   




                                      
                                                               
 

                                 











                                            



                                       



                                                                                                           
                                                 
                                                                             




                                                        




                                                                                                           





                                                






                                                                 










                                                  



                                                            










                                                  



                                                            










                                                  

  
 
                                      

                                                         

                                                          


                                                       
                                                       

                                                         

                                                          
                                
                                                                   
                                                                        

                                                          


                                                                         


                                                                                                 
                                                              


                                                               

                                                          
                                
                                                                   
                                                                        

                                                          


                                                                         


                                                                                                 
                                                              


                                                               

                                                          
                                
                                                                   
                                                                        

                                                          


                                                                         


                                                                                                 
                                                              


                                                               

                                                          

                                                                                            
                                                                   
                                                                        


                                                          
                                                              

                                                               



                                                              

                                                             



                                                              

                                                            

                                                                              

                                                                


                                                                                             

                                                         

                                                          


                                                    
                                                    
                  
// license:BSD-3-Clause
// copyright-holders:Derrick Renaud
/***************************************************************************
    polepos.c
    Sound handler
****************************************************************************/
#include "emu.h"
#include "polepos.h"

#include "namco52.h"
#include "namco54.h"

#include "machine/rescap.h"


#define OUTPUT_RATE         24000

#define POLEPOS_R166        1000.0
#define POLEPOS_R167        2200.0
#define POLEPOS_R168        4700.0

/* resistor values when shorted by 4066 running at 5V */
#define POLEPOS_R166_SHUNT  1.0/(1.0/POLEPOS_R166 + 1.0/250)
#define POLEPOS_R167_SHUNT  1.0/(1.0/POLEPOS_R166 + 1.0/250)
#define POLEPOS_R168_SHUNT  1.0/(1.0/POLEPOS_R166 + 1.0/250)

static const double volume_table[8] =
{
	(POLEPOS_R168_SHUNT + POLEPOS_R167_SHUNT + POLEPOS_R166_SHUNT + 2200) / 10000,
	(POLEPOS_R168_SHUNT + POLEPOS_R167_SHUNT + POLEPOS_R166       + 2200) / 10000,
	(POLEPOS_R168_SHUNT + POLEPOS_R167       + POLEPOS_R166_SHUNT + 2200) / 10000,
	(POLEPOS_R168_SHUNT + POLEPOS_R167       + POLEPOS_R166       + 2200) / 10000,
	(POLEPOS_R168       + POLEPOS_R167_SHUNT + POLEPOS_R166_SHUNT + 2200) / 10000,
	(POLEPOS_R168       + POLEPOS_R167_SHUNT + POLEPOS_R166       + 2200) / 10000,
	(POLEPOS_R168       + POLEPOS_R167       + POLEPOS_R166_SHUNT + 2200) / 10000,
	(POLEPOS_R168       + POLEPOS_R167       + POLEPOS_R166       + 2200) / 10000
};

static const double r_filt_out[3] = {RES_K(4.7), RES_K(7.5), RES_K(10)};
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));

/* Max filter order */
#define FILTER_ORDER_MAX 51

/* Define to use integer calculation */
#define FILTER_USE_INT

#ifdef FILTER_USE_INT
typedef int filter_real;
#define FILTER_INT_FRACT 15 /* fractional bits */
#else
typedef double filter_real;
#endif

struct filter
{
	filter_real xcoeffs[(FILTER_ORDER_MAX+1)/2];
	unsigned order;
};

struct filter_state
{
	unsigned prev_mac;
	filter_real xprev[FILTER_ORDER_MAX];
};

/* Filter types */
#define FILTER_LOWPASS      0
#define FILTER_HIGHPASS     1
#define FILTER_BANDPASS     2

#define Q_TO_DAMP(q)    (1.0/q)


/* Setup the filter context based on the passed filter type info.
 * type - 1 of the 3 defined filter types
 * fc   - center frequency
 * d    - damp = 1/Q
 * gain - overall filter gain. Set to 1 if not needed.
 */
void polepos_sound_device::filter2_context::setup(device_t *device, int type, double fc, double d, double gain)
{
	int const sample_rate = device->machine().sample_rate();
	double const two_over_T = 2*sample_rate;
	double const two_over_T_squared = two_over_T * two_over_T;

	/* calculate digital filter coefficents */
	/* cutoff freq, in radians/sec */
	/*w = 2.0*M_PI*fc; no pre-warping */
	double const w = sample_rate*2.0*tan(M_PI*fc/sample_rate); /* pre-warping */
	double const w_squared = w*w;

	/* temp variable */
	double const den = two_over_T_squared + d*w*two_over_T + w_squared;

	a1 = 2.0*(-two_over_T_squared + w_squared)/den;
	a2 = (two_over_T_squared - d*w*two_over_T + w_squared)/den;

	switch (type)
	{
	case FILTER_LOWPASS:
		b0 = b2 = w_squared/den;
		b1 = 2.0*(b0);
		break;
	case FILTER_BANDPASS:
		b0 = d*w*two_over_T/den;
		b1 = 0.0;
		b2 = -(b0);
		break;
	case FILTER_HIGHPASS:
		b0 = b2 = two_over_T_squared/den;
		b1 = -2.0*(b0);
		break;
	default:
		device->logerror("filter2_setup() - Invalid filter type for 2nd order filter.");
		break;
	}

	b0 *= gain;
	b1 *= gain;
	b2 *= gain;
}


/* Reset the input/output voltages to 0. */
void polepos_sound_device::filter2_context::reset()
{
	x0 = 0;
	x1 = 0;
	x2 = 0;
	y0 = 0;
	y1 = 0;
	y2 = 0;
}


/* Step the filter.
 * x0 is the new input, which needs to be set before stepping.
 * y0 is the new filter output.
 */
void polepos_sound_device::filter2_context::step()
{
	y0 = -a1 * y1 - a2 * y2 + b0 * x0 + b1 * x1 + b2 * x2;
	x2 = x1;
	x1 = x0;
	y2 = y1;
	y1 = y0;
}


/* Setup a filter2 structure based on an op-amp multipole bandpass circuit.
 * NOTE: If r2 is not used then set to 0.
 *       vRef is not needed to setup filter.
 *
 *                             .--------+---------.
 *                             |        |         |
 *                            --- c1    Z         |
 *                            ---       Z r3      |
 *                             |        Z         |
 *            r1               |  c2    |  |\     |
 *   In >----ZZZZ----+---------+--||----+  | \    |
 *                   Z                  '--|- \   |
 *                   Z r2                  |   >--+------> out
 *                   Z                  .--|+ /
 *                   |                  |  | /
 *                  gnd        vRef >---'  |/
 *
 */
void polepos_sound_device::filter2_context::opamp_m_bandpass_setup(device_t *device, double r1, double r2, double r3, double c1, double c2)
{
	if (r1 == 0)
	{
		device->logerror("filter_opamp_m_bandpass_setup() - r1 can not be 0");
		return; /* Filter can not be setup.  Undefined results. */
	}

	double  r_in, gain;

	if (r2 == 0)
	{
		gain = 1;
		r_in = r1;
	}
	else
	{
		gain = r2 / (r1 + r2);
		r_in = 1.0 / (1.0/r1 + 1.0/r2);
	}

	double const fc = 1.0 / (2 * M_PI * sqrt(r_in * r3 * c1 * c2));
	double const d = (c1 + c2) / sqrt(r3 / r_in * c1 * c2);
	gain *= -r3 / r_in * c2 / (c1 + c2);

	setup(device, FILTER_BANDPASS, fc, d, gain);
}


// device type definition
DEFINE_DEVICE_TYPE(POLEPOS_SOUND, polepos_sound_device, "polepos_sound", "Pole Position Custom Sound")


//**************************************************************************
//  LIVE DEVICE
//**************************************************************************

//-------------------------------------------------
//  polepos_sound_device - constructor
//-------------------------------------------------

polepos_sound_device::polepos_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
	: device_t(mconfig, POLEPOS_SOUND, tag, owner, clock),
		device_sound_interface(mconfig, *this),
		m_current_position(0),
		m_sample_msb(0),
		m_sample_lsb(0),
		m_sample_enable(0),
		m_stream(nullptr)
{
}


//-------------------------------------------------
//  device_start - device-specific startup
//-------------------------------------------------

void polepos_sound_device::device_start()
{
	m_stream = stream_alloc(0, 1, OUTPUT_RATE);
	m_sample_msb = m_sample_lsb = 0;
	m_sample_enable = 0;

	/* setup the filters */
	m_filter_engine[0].opamp_m_bandpass_setup(this, RES_K(220), RES_K(33), RES_K(390), CAP_U(.01),  CAP_U(.01));
	m_filter_engine[1].opamp_m_bandpass_setup(this, RES_K(150), RES_K(22), RES_K(330), CAP_U(.0047),  CAP_U(.0047));
	/* Filter 3 is a little different.  Because of the input capacitor, it is
	 * a high pass filter. */
	m_filter_engine[2].setup(this, FILTER_HIGHPASS, 950, Q_TO_DAMP(.707), 1);
}


//-------------------------------------------------
//  device_reset - device-specific reset
//-------------------------------------------------

void polepos_sound_device::device_reset()
{
	int loop;
	for (loop = 0; loop < 3; loop++)
		m_filter_engine[loop].reset();
}


//-------------------------------------------------
//  sound_stream_update - handle a stream update
//-------------------------------------------------

void polepos_sound_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
{
	uint32_t step, clock, slot;
	uint8_t *base;
	double volume, i_total;
	stream_sample_t *buffer = outputs[0];
	int loop;

	/* if we're not enabled, just fill with 0 */
	if (!m_sample_enable)
	{
		memset(buffer, 0, samples * sizeof(*buffer));
		return;
	}

	/* determine the effective clock rate */
	clock = (machine().device("maincpu")->unscaled_clock() / 16) * ((m_sample_msb + 1) * 64 + m_sample_lsb + 1) / (64*64);
	step = (clock << 12) / OUTPUT_RATE;

	/* determine the volume */
	slot = (m_sample_msb >> 3) & 7;
	volume = volume_table[slot];
	base = &machine().root_device().memregion("engine")->base()[slot * 0x800];

	/* fill in the sample */
	while (samples--)
	{
		m_filter_engine[0].x0 = (3.4 / 255 * base[(m_current_position >> 12) & 0x7ff] - 2) * volume;
		m_filter_engine[1].x0 = m_filter_engine[0].x0;
		m_filter_engine[2].x0 = m_filter_engine[0].x0;

		i_total = 0;
		for (loop = 0; loop < 3; loop++)
		{
			m_filter_engine[loop].step();
			/* The op-amp powered @ 5V will clip to 0V & 3.5V.
			 * Adjusted to vRef of 2V, we will clip as follows: */
			if (m_filter_engine[loop].y0 > 1.5) m_filter_engine[loop].y0 = 1.5;
			if (m_filter_engine[loop].y0 < -2)  m_filter_engine[loop].y0 = -2;

			i_total += m_filter_engine[loop].y0 / r_filt_out[loop];
		}
		i_total *= r_filt_total * 32000/2;  /* now contains voltage adjusted by final gain */

		*buffer++ = (int)i_total;
		m_current_position += step;
	}
}


WRITE_LINE_MEMBER(polepos_sound_device::clson_w)
{
	if (!state)
	{
		polepos_engine_sound_lsb_w(machine().dummy_space(), 0, 0);
		polepos_engine_sound_msb_w(machine().dummy_space(), 0, 0);
	}
}


/************************************/
/* Write LSB of engine sound        */
/************************************/
WRITE8_MEMBER(polepos_sound_device::polepos_engine_sound_lsb_w)
{
	/* Update stream first so all samples at old frequency are updated. */
	m_stream->update();
	m_sample_lsb = data & 62;
	m_sample_enable = data & 1;
}

/************************************/
/* Write MSB of engine sound        */
/************************************/
WRITE8_MEMBER(polepos_sound_device::polepos_engine_sound_msb_w)
{
	m_stream->update();
	m_sample_msb = data & 63;
}


/*************************************
 *
 *  Pole Position
 *
 *  Discrete sound emulation: Feb 2007, D.R.
 *
 *************************************/

/* nodes - sounds */
#define POLEPOS_CHANL1_SND      NODE_11
#define POLEPOS_CHANL2_SND      NODE_12
#define POLEPOS_CHANL3_SND      NODE_13
#define POLEPOS_CHANL4_SND      NODE_14

#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)))
static const discrete_dac_r1_ladder polepos_54xx_dac =
{
	4,               /* number of DAC bits */
						/* 54XX_0   54XX_1  54XX_2 */
	{ RES_K(47),     /* R124,    R136,   R152 */
		RES_K(22),   /* R120,    R132,   R142 */
		RES_K(10),   /* R119,    R131,   R138 */
		RES_K(4.7)}, /* R118,    R126,   R103 */
	0, 0, 0, 0       /* nothing extra */
};

#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)))
static const discrete_dac_r1_ladder polepos_52xx_dac =
{
	4,              /* number of DAC bits */
	{ RES_K(100),   /* R160 */
		RES_K(47),  /* R159 */
		RES_K(22),  /* R155 */
		RES_K(10)}, /* R154 */
	0, 0, 0, 0      /* nothing extra */
};

/*                           R117        R116         R117 */
#define POLEPOS_VREF (5.0 * (RES_K(1) / (RES_K(1.5) + RES_K(1))))

static const discrete_op_amp_filt_info polepos_chanl1_filt =
{
	POLEPOS_54XX_DAC_R + RES_K(22), /* R121 */
	0,                  /* no second input */
	RES_K(12),          /* R125 */
	0,                  /* not used */
	RES_K(120),         /* R122 */
	CAP_U(0.0022),      /* C27 */
	CAP_U(0.0022),      /* C28 */
	0,                  /* not used */
	POLEPOS_VREF,       /* vRef */
	5,                  /* vP */
	0                   /* vN */
};

static const discrete_op_amp_filt_info polepos_chanl2_filt =
{
	POLEPOS_54XX_DAC_R + RES_K(15), /* R133 */
	0,                  /* no second input */
	RES_K(15),          /* R137 */
	0,                  /* not used */
	RES_K(120),         /* R134 */
	CAP_U(0.022),       /* C29 */
	CAP_U(0.022),       /* C30 */
	0,                  /* not used */
	POLEPOS_VREF,       /* vRef */
	5,                  /* vP */
	0                   /* vN */
};

static const discrete_op_amp_filt_info polepos_chanl3_filt =
{
	POLEPOS_54XX_DAC_R + RES_K(22), /* R139 */
	0,                  /* no second input */
	RES_K(22),          /* R143 */
	0,                  /* not used */
	RES_K(180),         /* R140 */
	CAP_U(0.047),       /* C33 */
	CAP_U(0.047),       /* C34 */
	0,                  /* not used */
	POLEPOS_VREF,       /* vRef */
	5,                  /* vP */
	0                   /* vN */
};


DISCRETE_SOUND_START(polepos_discrete)

	/************************************************
	 * Input register mapping
	 ************************************************/
	DISCRETE_INPUT_DATA(NAMCO_54XX_0_DATA(NODE_01))
	DISCRETE_INPUT_DATA(NAMCO_54XX_1_DATA(NODE_01))
	DISCRETE_INPUT_DATA(NAMCO_54XX_2_DATA(NODE_01))
	DISCRETE_INPUT_DATA(NAMCO_52XX_P_DATA(NODE_04))

	/************************************************
	 * CHANL1 sound
	 ************************************************/
	DISCRETE_DAC_R1(NODE_20,
					NAMCO_54XX_2_DATA(NODE_01),
					4,          /* 4V - unmeasured*/
					&polepos_54xx_dac)
	DISCRETE_OP_AMP_FILTER(NODE_21,
					1,          /* ENAB */
					NODE_20,    /* INP0 */
					0,          /* INP1 - not used */
					DISC_OP_AMP_FILTER_IS_BAND_PASS_1M, &polepos_chanl1_filt)
	/* fake it so 0 is now vRef */
	DISCRETE_ADDER2(POLEPOS_CHANL1_SND,
					1,          /* ENAB */
					NODE_21, -POLEPOS_VREF)

	/************************************************
	 * CHANL2 sound
	 ************************************************/
	DISCRETE_DAC_R1(NODE_30,
					NAMCO_54XX_1_DATA(NODE_01),
					4,          /* 4V - unmeasured*/
					&polepos_54xx_dac)
	DISCRETE_OP_AMP_FILTER(NODE_31,
					1,          /* ENAB */
					NODE_30,    /* INP0 */
					0,          /* INP1 - not used */
					DISC_OP_AMP_FILTER_IS_BAND_PASS_1M, &polepos_chanl2_filt)
	/* fake it so 0 is now vRef */
	DISCRETE_ADDER2(POLEPOS_CHANL2_SND,
					1,          /* ENAB */
					NODE_31, -POLEPOS_VREF)

	/************************************************
	 * CHANL3 sound
	 ************************************************/
	DISCRETE_DAC_R1(NODE_40,
					NAMCO_54XX_0_DATA(NODE_01),
					4,          /* 4V - unmeasured*/
					&polepos_54xx_dac)
	DISCRETE_OP_AMP_FILTER(NODE_41,
					1,          /* ENAB */
					NODE_40,    /* INP0 */
					0,          /* INP1 - not used */
					DISC_OP_AMP_FILTER_IS_BAND_PASS_1M, &polepos_chanl3_filt)
	/* fake it so 0 is now vRef */
	DISCRETE_ADDER2(POLEPOS_CHANL3_SND,
					1,          /* ENAB */
					NODE_41, -POLEPOS_VREF)

	/************************************************
	 * CHANL4 sound
	 ************************************************/
	/* this circuit was simulated in SPICE and an equivalent filter circuit generated */
	DISCRETE_DAC_R1(NODE_50,
					NAMCO_52XX_P_DATA(NODE_04),
					4,          /* 4V - unmeasured*/
					&polepos_52xx_dac)
	/* fake it so 0 is now vRef */
	DISCRETE_ADDER2(NODE_51,
					1,          /* ENAB */
					NODE_50, -POLEPOS_VREF)
	DISCRETE_FILTER2(NODE_52,
					1,          /* ENAB */
					NODE_51,    /* INP0 */
					100,        /* FREQ */
					1.0 / 0.3,  /* DAMP */
					DISC_FILTER_HIGHPASS)
	DISCRETE_FILTER2(NODE_53,
					1,          /* ENAB */
					NODE_52,    /* INP0 */
					1200,       /* FREQ */
					1.0 / 0.8,  /* DAMP */
					DISC_FILTER_LOWPASS)
	DISCRETE_GAIN(NODE_54,
					NODE_53,    /* IN0 */
					0.5         /* overall filter GAIN */)
	/* clamp to the maximum of the op-amp shifted by vRef */
	DISCRETE_CLAMP(POLEPOS_CHANL4_SND,
					NODE_54,    /* IN0 */
					0,          /* MIN */
					5.0 - OP_AMP_VP_RAIL_OFFSET - POLEPOS_VREF) /* MAX */

	/************************************************
	 * Output
	 ************************************************/
	DISCRETE_OUTPUT(POLEPOS_CHANL1_SND, 32767/2)
	DISCRETE_OUTPUT(POLEPOS_CHANL2_SND, 32767/2)
	DISCRETE_OUTPUT(POLEPOS_CHANL3_SND, 32767/2)
	DISCRETE_OUTPUT(POLEPOS_CHANL4_SND, 32767/2)
DISCRETE_SOUND_END