03074: dkongjr and clones: Discrete audio output is lower pitched than normal (Derrick Renaud)

This commit is contained in:
Derrick Renaud 2010-10-19 03:14:33 +00:00
parent bb26dc0816
commit 4c4fd7e862
3 changed files with 107 additions and 92 deletions

View File

@ -14,6 +14,8 @@
* DSD_555_CC - NE555 Constant Current VCO
* DSD_555_VCO1 - Op-Amp linear ramp based 555 VCO
* DSD_566 - NE566 Simulation
* DSD_LS624
* DSD_LS629
*
************************************************************************
*
@ -135,12 +137,12 @@ struct dsd_ls624_context
struct dsd_ls629_context
{
double v_bias;
double v_cap;
double v_peak;
double v_threshold;
double k_vmod_to_i;
double exponent;
double t_used;
double vmod_scale;
double v_cap_freq_in;
int flip_flop;
int has_freq_in_cap;
int out_type;
};
@ -1813,11 +1815,12 @@ static DISCRETE_RESET(dsd_ls624)
#define DSD_LS629__VRNG DISCRETE_INPUT(2)
#define DSD_LS629__C DISCRETE_INPUT(3)
#define DSD_LS629__R_FREQ_IN DISCRETE_INPUT(4)
#define DSD_LS629__OUTTYPE DISCRETE_INPUT(5)
#define DSD_LS629__C_FREQ_IN DISCRETE_INPUT(5)
#define DSD_LS629__OUTTYPE DISCRETE_INPUT(6)
#define LS624_R_EXT 600.0 /* as specified in data sheet */
#define LS624_OUT_HIGH 4.5 /* measured */
#define LS624_FREQ_R_IN RES_K(95) /* measured */
#define LS624_FREQ_R_IN RES_K(90) /* measured & 70K + 20k per data sheet */
/*
* The 74LS624 series are constant current based VCOs. The Freq Control voltage
@ -1835,97 +1838,103 @@ static DISCRETE_RESET(dsd_ls624)
* The Range voltage adjusts the threshold voltage. The higher the Range voltage,
* the lower the threshold voltage, the longer the cap can charge, the lower the frequency.
*
* In a perfect world it would work like this:
* The current is based on the mysterious Rext mentioned in the data sheet.
* I = (VfreqControl / 5) / Rext
* I = (VfreqControl * 20k/90k) / Rext
* where Rext = 600 ohms or external Rext on a 74LS628
* The Freq Control has an input impedance of approximately 100k, so any input resistance
* The Freq Control has an input impedance of approximately 90k, so any input resistance
* connected to the Freq Control pin works as a voltage divider.
* I = (VfreqControl / 5 * 100000 / (RfreqControl + 100000)) / Rext
* I = (VfreqControl * 20k/(90k + RfreqControlIn)) / Rext
* That gives us a change in voltage on the cap of
* dV = I / sampleRate / C_inFarads
*
* Unfortunately the chip does not behave linearly do to internal interactions,
* so I have just worked out the formula (using zunzun.com) of FreqControl and
* range to frequency out for a fixed cap value of 0.1uf. Other cap values can just
* scale from that. From the freq, we calculate the time of 1/2 cycle using 1/Freq/2.
* Then just use that to toggle a waveform.
*/
/* The range to bias and threshold routines were created from testing a real chip.
* This data was entered into the function finder routine at www.zunzun.com
* to create the following 2 routines
*/
double range_to_bias(double range)
{
/* Quadratic2D_model */
double bias;
// coefficients
const double a = 2.3107142857142846E+00;
const double b = -1.0714285714278806E-03;
const double c = -1.7857142857144002E-03;
bias = a;
bias += b * range;
bias += c * pow(range, 2.0);
return bias;
}
double range_to_threshold(double range)
{
/* Polyfunctional2D_model */
double threshold;
// coefficients
const double a = -1.6124587587173614E-01;
const double b = -1.3501987413150401E-01;
const double offset = 2.2550390868489893E+00;
threshold = a * range;
threshold += b * exp(-1.0 * range);
threshold += offset;
return threshold;
}
static DISCRETE_STEP(dsd_ls629)
{
struct dsd_ls629_context *context = (struct dsd_ls629_context *)node->context;
double i; /* Charging current created by Freq Control */
double dt; /* change in time */
double x_time = 0;
double v_cap; /* Current voltage on capacitor, before dt */
double freq, t1;
double t_used = context->t_used;
double dt = node->info->sample_time;;
double vmod = DSD_LS629__VMOD;
double vmod_2, vmod_3, vmod_4;
double range = DSD_LS629__VRNG;
int count_f = 0, count_r = 0;
/* coefficients */
const double k1 = 1.9904769024796283E+03;
const double k2 = 1.2070059213983407E+03;
const double k3 = 1.3266985579561108E+03;
const double k4 = -1.5500979825922698E+02;
const double k5 = 2.8184536266938172E+00;
const double k6 = -2.3503421582744556E+02;
const double k7 = -3.3836786704527788E+02;
const double k8 = -1.3569136703258670E+02;
const double k9 = 2.9914575453819188E+00;
const double k10 = 1.6855569086173170E+00;
if (UNEXPECTED(DSD_LS629__ENABLE == 0))
return;
dt = node->info->sample_time; /* Change in time */
v_cap = context->v_cap; /* Set to voltage before change */
/* scale due to input resistance */
if ((EXPECTED(DSD_LS629__R_FREQ_IN > 0)))
vmod *= context->vmod_scale;
/* apply cap if needed */
if (context->has_freq_in_cap)
{
context->v_cap_freq_in += (vmod - context->v_cap_freq_in) * context->exponent;
vmod = context->v_cap_freq_in;
}
/* Calculate charging current */
i = DSD_LS629__VMOD * context->k_vmod_to_i;
/* Polyfunctional3D_model created by zunzun.com using sum of squared absolute error */
vmod_2 = vmod * vmod;
vmod_3 = vmod_2 * vmod;
vmod_4 = vmod_3 * vmod;
freq = k1;
freq += k2 * vmod;
freq += k3 * vmod_2;
freq += k4 * vmod_3;
freq += k5 * vmod_4;
freq += k6 * range;
freq += k7 * range * vmod;
freq += k8 * range * vmod_2;
freq += k9 * range * vmod_3;
freq += k10 * range * vmod_4;
/* Keep looping until all toggling in this time sample is used up. */
freq *= CAP_U(0.1) / DSD_LS629__C;
t1 = 0.5 / freq ;
t_used += node->info->sample_time;
do
{
/* Always Discharging */
v_cap -= i * dt / DSD_LS629__C;
dt = 0;
/* has it discharged past lower limit? */
if (UNEXPECTED(v_cap < context->v_threshold))
dt = 0;
if (t_used > t1)
{
/* calculate the overshoot time */
dt = DSD_LS629__C * (context->v_threshold - v_cap) / i;
v_cap = context->v_peak;
t_used -= t1;
context->flip_flop ^= 1;
if (context->flip_flop)
count_r++;
else
count_f++;
x_time = dt;
/* fix up any frequency increase change errors */
while(t_used > node->info->sample_time)
t_used -= node->info->sample_time;
x_time = t_used;
dt = t_used;
}
} while(dt);
}while(dt);
context->v_cap = v_cap;
context->t_used = t_used;
/* Convert last switch time to a ratio */
x_time = x_time / node->info->sample_time;
@ -1937,7 +1946,7 @@ static DISCRETE_STEP(dsd_ls629)
break;
case DISC_LS624_OUT_ENERGY:
if (x_time == 0) x_time = 1.0;
node->output[0] = LS624_OUT_HIGH * (context->flip_flop ? x_time : (1.0 - x_time));
node->output[0] = LS624_OUT_HIGH * (context->flip_flop ? x_time : (1.0 - x_time));
break;
case DISC_LS624_OUT_LOGIC:
node->output[0] = context->flip_flop;
@ -1957,23 +1966,24 @@ static DISCRETE_STEP(dsd_ls629)
}
}
#define LS624_LOSS_FACTOR 0.92
static DISCRETE_RESET(dsd_ls629)
{
struct dsd_ls629_context *context = (struct dsd_ls629_context *)node->context;
context->out_type = (int)DSD_LS629__OUTTYPE;
context->v_bias = range_to_bias(DSD_LS629__VRNG);
context->v_cap = context->v_bias;
context->v_threshold = range_to_threshold(DSD_LS629__VRNG);
/* The voltage at the current charge pin after a state change */
context->v_peak = context->v_bias * 2 - context->v_threshold;
context->flip_flop = 0;
/* precalulate the voltage to current formula */
context->k_vmod_to_i = 1.0 / 5 * LS624_FREQ_R_IN / (DSD_LS629__R_FREQ_IN + LS624_FREQ_R_IN) / LS624_R_EXT;
context->k_vmod_to_i *= LS624_LOSS_FACTOR;
context->t_used = 0;
context->vmod_scale = RES_K(90) / (DSD_LS629__R_FREQ_IN + RES_K(90));
if (DSD_LS629__C_FREQ_IN > 0)
{
context->has_freq_in_cap = 1;
context->exponent = RC_CHARGE_EXP(RES_2_PARALLEL(DSD_LS629__R_FREQ_IN, RES_K(90)) * DSD_LS629__C_FREQ_IN);
context->v_cap_freq_in = 0;
}
else
context->has_freq_in_cap = 0;
node->output[0] = 0;
}

View File

@ -4526,7 +4526,7 @@ enum
#define DISCRETE_555_VCO1_CV(NODE,RESET,VIN,CTRLV,OPTIONS) { NODE, DSD_555_VCO1 , 3, { RESET,VIN,CTRLV }, { RESET,VIN,CTRLV }, OPTIONS, "DISCRETE_555_VCO1_CV" },
#define DISCRETE_566(NODE,VMOD,R,C,VPOS,VNEG,VCHARGE,OPTIONS) { NODE, DSD_566 , 7, { VMOD,R,C,NODE_NC,NODE_NC,VCHARGE,NODE_NC }, { VMOD,R,C,VPOS,VNEG,VCHARGE,OPTIONS }, NULL, "DISCRETE_566" },
#define DISCRETE_74LS624(NODE,VMOD,VRNG,C,OUTTYPE) { NODE, DSD_LS624 , 4, { VMOD,NODE_NC,NODE_NC,NODE_NC }, { VMOD,VRNG,C,OUTTYPE }, NULL, "DISCRETE_74LS624" },
#define DISCRETE_74LS629(NODE,ENAB,VMOD,VRNG,C,R_FREQ_IN,OUTTYPE) { NODE, DSD_LS629 , 6, { ENAB,VMOD,NODE_NC,NODE_NC,NODE_NC,NODE_NC }, { ENAB,VMOD,VRNG,C,R_FREQ_IN,OUTTYPE }, NULL, "DISCRETE_74LS629" },
#define DISCRETE_74LS629(NODE,ENAB,VMOD,VRNG,C,R_FREQ_IN,C_FREQ_IN,OUTTYPE) { NODE, DSD_LS629 , 7, { ENAB,VMOD,NODE_NC,NODE_NC,NODE_NC,NODE_NC,NODE_NC }, { ENAB,VMOD,VRNG,C,R_FREQ_IN,C_FREQ_IN,OUTTYPE }, NULL, "DISCRETE_74LS629" },
/* NOP */
#define DISCRETE_NOP(NODE) { NODE, DSS_NOP , 0, { 0 }, { 0 }, NULL, "DISCRETE_NOP" },

View File

@ -21,7 +21,7 @@
/* Set to 1 to use faster custom mixer */
#define DK_USE_CUSTOM (1)
#define USE_LS629 (0) /* set to use new LS624 code */
#define USE_LS629 (1) /* set to use new LS624 code */
/* Issue surrounded by this define need to be analyzed and
* reviewed at a later time.
@ -893,7 +893,7 @@ static DISCRETE_SOUND_START(dkongjr)
DISCRETE_TASK_START(1)
DISCRETE_LOGIC_INVERT(DS_SOUND7,DS_SOUND7_INV)
DISCRETE_COUNTER(NODE_100, 1, 0, NODE_118, 0, 0xFFFF, DISC_COUNT_UP, 0, DISC_CLK_BY_COUNT)
DISCRETE_COUNTER(NODE_100, 1, 0, NODE_118, 0, 0x3FFF, DISC_COUNT_UP, 0, DISC_CLK_BY_COUNT)
DISCRETE_BIT_DECODE(NODE_101, NODE_100, 6, 1) /*LS157 2A */
DISCRETE_BIT_DECODE(NODE_102, NODE_100, 3, 1) /*LS157 2B */
@ -901,7 +901,7 @@ DISCRETE_TASK_START(1)
DISCRETE_BIT_DECODE(NODE_104, NODE_100, 11, 1) /*LS157 3B */
/* LS157 Switches */
DISCRETE_SWITCH(NODE_105, 1, DS_SOUND7_INV, GND, NODE_113) /* Switch 1 from LS624 */
// DISCRETE_SWITCH(NODE_105, 1, DS_SOUND7_INV, GND, NODE_113) /* Switch 1 from LS624 */
DISCRETE_SWITCH(NODE_106, 1, DS_SOUND7_INV, NODE_101, NODE_102) /* Switch 2 */
DISCRETE_SWITCH(NODE_107, 1, DS_SOUND7_INV, NODE_103, NODE_104) /* Switch 3 */
@ -909,7 +909,6 @@ DISCRETE_TASK_START(1)
DISCRETE_SWITCH(NODE_111, /* invert voltage */
1, NODE_110, /* ENAB, SWITCH */
TTL_HIGH, 0) /* INP0, INP1 */
DISCRETE_RCFILTER(NODE_112, NODE_111, JR_R10, JR_C17)
/* Breadboarded measurements IC 5K, pin 7
D.R. Oct 2010
@ -928,19 +927,20 @@ DISCRETE_TASK_START(1)
#if (USE_LS629)
DISCRETE_74LS629(NODE_113, /* IC 5K, pin 7 */
1, /* ENAB */
NODE_112, DK_SUP_V, /* VMOD, VRNG */
JR_C18, JR_R10, /* C, R_FREQ_IN */
NODE_111, DK_SUP_V, /* VMOD, VRNG */
JR_C18, JR_R10, JR_C17, /* C, R_FREQ_IN, C_FREQ_IN */
DISC_LS624_OUT_ENERGY)
#else
DISCRETE_RCFILTER(NODE_112, NODE_111, JR_R10, JR_C17)
DISCRETE_74LS624(NODE_113, NODE_112, DK_SUP_V, JR_C18, DISC_LS624_OUT_ENERGY)
#endif
DISCRETE_SWITCH(NODE_105, 1, DS_SOUND7_INV, GND, NODE_113) /* Switch 1 from LS624 */
DISCRETE_LOGIC_XOR(NODE_115, NODE_105, NODE_106)
DISCRETE_SWITCH(NODE_116, /* invert with TTL voltage */
1, NODE_107, /* ENAB, SWITCH */
TTL_HIGH, 0) /* INP0, INP1 */
DISCRETE_RCFILTER(NODE_117, NODE_116, JR_R11, JR_C16)
/* Breadboarded measurements IC 5K, pin 10
D.R. Oct 2010
@ -956,13 +956,14 @@ DISCRETE_TASK_START(1)
4.15 59400
*/
#if (0 && USE_LS629)
#if (USE_LS629)
DISCRETE_74LS629(NODE_118, /* IC 5K, pin 10 */
1, /* ENAB */
NODE_117, DK_SUP_V, /* VMOD, VRNG */
JR_C19, JR_R11, /* C, R_FREQ_IN */
NODE_116, DK_SUP_V, /* VMOD, VRNG */
JR_C19, JR_R11, JR_C16, /* C, R_FREQ_IN, C_FREQ_IN */
DISC_LS624_OUT_COUNT_F)
#else
DISCRETE_RCFILTER(NODE_117, NODE_116, JR_R11, JR_C16)
DISCRETE_74LS624(NODE_118, NODE_117, DK_SUP_V, JR_C19, DISC_LS624_OUT_COUNT_F)
#endif
@ -1001,7 +1002,8 @@ DISCRETE_TASK_START(2)
DISCRETE_74LS629(NODE_14, /* IC 8L, pin 10 */
1, /* ENAB */
NODE_13, DK_SUP_V, /* VMOD, VRNG */
JR_C22, RES_2_PARALLEL(JR_R12, JR_R13), /* C, R_FREQ_IN */
/* C_FREQ_IN is taken care of by the NODE_13 mixer */
JR_C22, RES_2_PARALLEL(JR_R13, JR_R12), 0, /* C, R_FREQ_IN, C_FREQ_IN */
DISC_LS624_OUT_ENERGY)
#else
DISCRETE_74LS624( NODE_14, NODE_13, 0.98*DK_SUP_V, JR_C22, DISC_LS624_OUT_ENERGY)
@ -1055,11 +1057,10 @@ DISCRETE_TASK_START(1)
*/
#if (USE_LS629)
DISCRETE_RCFILTER(NODE_90, DS_SOUND9_INV, JR_R14, JR_C26)
DISCRETE_74LS629(NODE_91, /* IC 7P, pin 7 */
1, /* ENAB */
NODE_90, DK_SUP_V, /* VMOD, VRNG */
JR_C37, JR_R14, /* C, R_FREQ_IN */
DS_SOUND9_INV, DK_SUP_V, /* VMOD, VRNG */
JR_C37, JR_R14, JR_C26, /* C, R_FREQ_IN, C_FREQ_IN */
DISC_LS624_OUT_ENERGY)
DISCRETE_SWITCH(DS_OUT_SOUND9, 1, DS_SOUND9_INV, NODE_91, 0)
#else
@ -1104,7 +1105,11 @@ DISCRETE_TASK_START(3)
* Just a 1:n amplifier without filters - just the output filter
*/
DISCRETE_CRFILTER(NODE_295,NODE_288, 1000, JR_C13)
#if (USE_LS629)
DISCRETE_OUTPUT(NODE_295, 32767.0/5.0 * 5)
#else
DISCRETE_OUTPUT(NODE_295, 32767.0/5.0 * 10)
#endif
DISCRETE_TASK_END()
DISCRETE_SOUND_END