summaryrefslogtreecommitdiffstatshomepage
path: root/src/emu/sound/disc_wav.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/emu/sound/disc_wav.c')
-rw-r--r--src/emu/sound/disc_wav.c1707
1 files changed, 1707 insertions, 0 deletions
diff --git a/src/emu/sound/disc_wav.c b/src/emu/sound/disc_wav.c
new file mode 100644
index 00000000000..d397511628f
--- /dev/null
+++ b/src/emu/sound/disc_wav.c
@@ -0,0 +1,1707 @@
+/************************************************************************
+ *
+ * MAME - Discrete sound system emulation library
+ *
+ * Written by Keith Wilkins (mame@esplexo.co.uk)
+ *
+ * (c) K.Wilkins 2000
+ *
+ ************************************************************************
+ *
+ * DSS_COUNTER - External clock Binary Counter
+ * DSS_LFSR_NOISE - Linear Feedback Shift Register Noise
+ * DSS_NOISE - Noise Source - Random source
+ * DSS_NOTE - Note/tone generator
+ * DSS_OP_AMP_OSC - Op Amp oscillator circuits
+ * DSS_SAWTOOTHWAVE - Sawtooth waveform generator
+ * DSS_SCHMITT_OSC - Schmitt Feedback Oscillator
+ * DSS_SINEWAVE - Sinewave generator source code
+ * DSS_SQUAREWAVE - Squarewave generator source code
+ * DSS_SQUAREWFIX - Squarewave generator - fixed frequency
+ * DSS_SQUAREWAVE2 - Squarewave generator - by tOn/tOff
+ * DSS_TRIANGLEWAVE - Triangle waveform generator
+ *
+ ************************************************************************/
+
+struct dss_adsr_context
+{
+ double phase;
+};
+
+struct dss_counter_context
+{
+ int clock_type;
+ int out_type;
+ int is_7492;
+ int last; // Last clock state
+ int count; // current count
+ double t_clock; // fixed counter clock in seconds
+ double t_left; // time unused during last sample in seconds
+};
+
+struct dss_lfsr_context
+{
+ unsigned int lfsr_reg;
+ int last; // Last clock state
+ double t_clock; // fixed counter clock in seconds
+ double t_left; // time unused during last sample in seconds
+ double sampleStep;
+ double shiftStep;
+ double t;
+ UINT8 reset_on_high;
+ UINT8 invert_output;
+ UINT8 out_is_f0;
+};
+
+struct dss_noise_context
+{
+ double phase;
+};
+
+struct dss_note_context
+{
+ int clock_type;
+ int out_type;
+ int last; // Last clock state
+ double t_clock; // fixed counter clock in seconds
+ double t_left; // time unused during last sample in seconds
+ int max1; // Max 1 Count stored as int for easy use.
+ int max2; // Max 2 Count stored as int for easy use.
+ int count1; // current count1
+ int count2; // current count2
+};
+
+struct dss_op_amp_osc_context
+{
+ const double *r1; // pointers to resistor values
+ const double *r2;
+ const double *r3;
+ const double *r4;
+ const double *r5;
+ const double *r6;
+ const double *r7;
+ const double *r8;
+ int type;
+ UINT8 flip_flop; // flip/flop output state
+ UINT8 flip_flopXOR; // flip_flop ^ flip_flopXOR, 0 = discharge, 1 = charge
+ UINT8 is_squarewave;
+ double high_out_V;
+ double thresholdLow; // falling threshold
+ double thresholdHigh; // rising threshold
+ double vCap; // current capacitor voltage
+ double rTotal; // all input resistors in parallel
+ double iFixed; // fixed current at the input
+ double temp1; // Multi purpose
+ double temp2; // Multi purpose
+ double temp3; // Multi purpose
+};
+
+struct dss_sawtoothwave_context
+{
+ double phase;
+ int type;
+};
+
+struct dss_schmitt_osc_context
+{
+ double ratioIn; // ratio of total charging voltage that comes from the input
+ double ratioFeedback; // ratio of total charging voltage that comes from the feedback
+ double vCap; // current capacitor voltage
+ double rc; // r*c
+ double exponent;
+ int state; // state of the output
+ int enable_type;
+ UINT8 input_is_voltage;
+};
+
+struct dss_sinewave_context
+{
+ double phase;
+};
+
+struct dss_squarewave_context
+{
+ double phase;
+ double trigger;
+};
+
+struct dss_squarewfix_context
+{
+ int flip_flop;
+ double sampleStep;
+ double tLeft;
+ double tOff;
+ double tOn;
+};
+
+struct dss_trianglewave_context
+{
+ double phase;
+};
+
+struct dss_inverter_osc_context
+{
+ double w;
+ double wc;
+ double vCap;
+ double vG2_old;
+ double Rp;
+ double R1;
+ double R2;
+ double C;
+ double tf_a;
+ double tf_b;
+};
+
+/************************************************************************
+ *
+ * DSS_COUNTER - External clock Binary Counter
+ *
+ * input0 - Enable input value
+ * input1 - Reset input (active high)
+ * input2 - Clock Input
+ * input3 - Max count
+ * input4 - Direction - 0=down, 1=up
+ * input5 - Reset Value
+ * input6 - Clock type
+ *
+ * Jan 2004, D Renaud.
+ ************************************************************************/
+#define DSS_COUNTER__ENABLE (*(node->input[0]))
+#define DSS_COUNTER__RESET (*(node->input[1]))
+#define DSS_COUNTER__CLOCK (*(node->input[2]))
+#define DSS_COUNTER__MAX (*(node->input[3]))
+#define DSS_COUNTER__DIR (*(node->input[4]))
+#define DSS_COUNTER__INIT (*(node->input[5]))
+#define DSS_COUNTER__CLOCK_TYPE (*(node->input[6]))
+
+static const int disc_7492_count[6] = {0x00, 0x01, 0x02, 0x04, 0x05, 0x06};
+
+void dss_counter_step(node_description *node)
+{
+ struct dss_counter_context *context = node->context;
+ double cycles;
+ int clock = 0, last_count, inc = 0;
+ int max = DSS_COUNTER__MAX;
+ double xTime = 0;
+
+ if (context->clock_type == DISC_CLK_IS_FREQ)
+ {
+ /* We need to keep clocking the internal clock even if disabled. */
+ cycles = (context->t_left + discrete_current_context->sample_time) / context->t_clock;
+ inc = (int)cycles;
+ context->t_left = (cycles - inc) * context->t_clock;
+ if (inc) xTime = context->t_left / discrete_current_context->sample_time;
+ }
+ else
+ {
+ clock = (int)DSS_COUNTER__CLOCK;
+ xTime = DSS_COUNTER__CLOCK - clock;
+ }
+
+
+ /* If reset enabled then set output to the reset value. No xTime in reset. */
+ if (DSS_COUNTER__RESET)
+ {
+ context->count = DSS_COUNTER__INIT;
+ node->output = context->is_7492 ? 0 : context->count;
+ return;
+ }
+
+ /*
+ * Only count if module is enabled.
+ * This has the effect of holding the output at it's current value.
+ */
+ if (DSS_COUNTER__ENABLE)
+ {
+ last_count = context->count;
+
+ switch (context->clock_type)
+ {
+ case DISC_CLK_ON_F_EDGE:
+ case DISC_CLK_ON_R_EDGE:
+ /* See if the clock has toggled to the proper edge */
+ clock = (clock != 0);
+ if (context->last != clock)
+ {
+ context->last = clock;
+ if (context->clock_type == clock)
+ {
+ /* Toggled */
+ inc = 1;
+ }
+ }
+ break;
+
+ case DISC_CLK_BY_COUNT:
+ /* Clock number of times specified. */
+ inc = clock;
+ break;
+ }
+
+ for (clock = 0; clock < inc; clock++)
+ {
+ context->count += DSS_COUNTER__DIR ? 1 : -1; // up/down
+ if (context->count < 0) context->count = max;
+ if (context->count > max) context->count = 0;
+ }
+
+ node->output = context->is_7492 ? disc_7492_count[context->count] : context->count;
+
+ if (context->count != last_count)
+ {
+ /* the xTime is only output if the output changed. */
+ switch (context->out_type)
+ {
+ case DISC_OUT_IS_ENERGY:
+ if (xTime != 0)
+ node->output = (context->count > last_count) ? (last_count + xTime) : (last_count - xTime);
+ break;
+ case DISC_OUT_HAS_XTIME:
+ node->output += xTime;
+ break;
+ }
+ }
+ }
+ else
+ node->output = context->count;
+}
+
+void dss_counter_reset(node_description *node)
+{
+ struct dss_counter_context *context = node->context;
+
+ context->clock_type = (int)DSS_COUNTER__CLOCK_TYPE;
+ if (context->clock_type == DISC_COUNTER_IS_7492)
+ {
+ context->clock_type = DISC_CLK_ON_F_EDGE;
+ context->is_7492 = 1;
+ }
+ else
+ context->is_7492 = 0;
+ if ((context->clock_type < DISC_CLK_ON_F_EDGE) || (context->clock_type > DISC_CLK_IS_FREQ))
+ discrete_log("Invalid clock type passed in NODE_%d\n", node->node - NODE_START);
+ context->last = 0;
+ if (context->clock_type == DISC_CLK_IS_FREQ) context->t_clock = 1.0 / DSS_COUNTER__CLOCK;
+ context->t_left = 0;
+ context->count = DSS_COUNTER__INIT; /* count starts at reset value */
+ node->output = DSS_COUNTER__INIT;
+}
+
+
+/************************************************************************
+ *
+ * DSS_LFSR_NOISE - Usage of node_description values for LFSR noise gen
+ *
+ * input0 - Enable input value
+ * input1 - Register reset
+ * input2 - Clock Input
+ * input3 - Amplitude input value
+ * input4 - Input feed bit
+ * input5 - Bias
+ *
+ * also passed dss_lfsr_context structure
+ *
+ ************************************************************************/
+#define DSS_LFSR_NOISE__ENABLE (*(node->input[0]))
+#define DSS_LFSR_NOISE__RESET (*(node->input[1]))
+#define DSS_LFSR_NOISE__CLOCK (*(node->input[2]))
+#define DSS_LFSR_NOISE__AMP (*(node->input[3]))
+#define DSS_LFSR_NOISE__FEED (*(node->input[4]))
+#define DSS_LFSR_NOISE__BIAS (*(node->input[5]))
+
+int dss_lfsr_function(int myfunc,int in0,int in1,int bitmask)
+{
+ int retval;
+
+ in0&=bitmask;
+ in1&=bitmask;
+
+ switch(myfunc)
+ {
+ case DISC_LFSR_XOR:
+ retval=in0^in1;
+ break;
+ case DISC_LFSR_OR:
+ retval=in0|in1;
+ break;
+ case DISC_LFSR_AND:
+ retval=in0&in1;
+ break;
+ case DISC_LFSR_XNOR:
+ retval=in0^in1;
+ retval=retval^bitmask; /* Invert output */
+ break;
+ case DISC_LFSR_NOR:
+ retval=in0|in1;
+ retval=retval^bitmask; /* Invert output */
+ break;
+ case DISC_LFSR_NAND:
+ retval=in0&in1;
+ retval=retval^bitmask; /* Invert output */
+ break;
+ case DISC_LFSR_IN0:
+ retval=in0;
+ break;
+ case DISC_LFSR_IN1:
+ retval=in1;
+ break;
+ case DISC_LFSR_NOT_IN0:
+ retval=in0^bitmask;
+ break;
+ case DISC_LFSR_NOT_IN1:
+ retval=in1^bitmask;
+ break;
+ case DISC_LFSR_REPLACE:
+ retval=in0&~in1;
+ retval=retval|in1;
+ break;
+ case DISC_LFSR_XOR_INV_IN0:
+ retval = in0^bitmask; /* invert in0 */
+ retval = retval^in1; /* xor in1 */
+ break;
+ case DISC_LFSR_XOR_INV_IN1:
+ retval = in1^bitmask; /* invert in1 */
+ retval = retval^in0; /* xor in0 */
+ break;
+ default:
+ discrete_log("dss_lfsr_function - Invalid function type passed");
+ retval=0;
+ break;
+ }
+ return retval;
+}
+
+/* reset prototype so that it can be used in init function */
+void dss_lfsr_reset(node_description *node);
+
+void dss_lfsr_step(node_description *node)
+{
+ const discrete_lfsr_desc *lfsr_desc = node->custom;
+ struct dss_lfsr_context *context = node->context;
+ double cycles;
+ int clock, inc = 0;
+ int fb0,fb1,fbresult;
+
+ if (lfsr_desc->clock_type == DISC_CLK_IS_FREQ)
+ {
+ /* We need to keep clocking the internal clock even if disabled. */
+ cycles = (context->t_left + discrete_current_context->sample_time) / context->t_clock;
+ inc = (int)cycles;
+ context->t_left = (cycles - inc) * context->t_clock;
+ }
+
+ /* Reset everything if necessary */
+ if((DSS_LFSR_NOISE__RESET ? 1 : 0) == context->reset_on_high)
+ {
+ dss_lfsr_reset(node);
+ return;
+ }
+
+ switch (lfsr_desc->clock_type)
+ {
+ case DISC_CLK_ON_F_EDGE:
+ case DISC_CLK_ON_R_EDGE:
+ /* See if the clock has toggled to the proper edge */
+ clock = (DSS_LFSR_NOISE__CLOCK != 0);
+ if (context->last != clock)
+ {
+ context->last = clock;
+ if (lfsr_desc->clock_type == clock)
+ {
+ /* Toggled */
+ inc = 1;
+ }
+ }
+ break;
+
+ case DISC_CLK_BY_COUNT:
+ /* Clock number of times specified. */
+ inc = (int)DSS_LFSR_NOISE__CLOCK;
+ break;
+ }
+
+ for (clock = 0; clock < inc; clock++)
+ {
+ /* Fetch the last feedback result */
+ fbresult=((context->lfsr_reg)>>(lfsr_desc->bitlength))&0x01;
+
+ /* Stage 2 feedback combine fbresultNew with infeed bit */
+ fbresult=dss_lfsr_function(lfsr_desc->feedback_function1,fbresult,((DSS_LFSR_NOISE__FEED)?0x01:0x00),0x01);
+
+ /* Stage 3 first we setup where the bit is going to be shifted into */
+ fbresult=fbresult*lfsr_desc->feedback_function2_mask;
+ /* Then we left shift the register, */
+ context->lfsr_reg=(context->lfsr_reg)<<1;
+ /* Now move the fbresult into the shift register and mask it to the bitlength */
+ context->lfsr_reg=dss_lfsr_function(lfsr_desc->feedback_function2,fbresult, (context->lfsr_reg), ((1<<(lfsr_desc->bitlength))-1));
+
+ /* Now get and store the new feedback result */
+ /* Fetch the feedback bits */
+ fb0=((context->lfsr_reg)>>(lfsr_desc->feedback_bitsel0))&0x01;
+ fb1=((context->lfsr_reg)>>(lfsr_desc->feedback_bitsel1))&0x01;
+ /* Now do the combo on them */
+ fbresult=dss_lfsr_function(lfsr_desc->feedback_function0,fb0,fb1,0x01);
+ context->lfsr_reg=dss_lfsr_function(DISC_LFSR_REPLACE,(context->lfsr_reg), fbresult<<(lfsr_desc->bitlength), ((2<<(lfsr_desc->bitlength))-1));
+
+ /* Now select the output bit */
+ if (context->out_is_f0)
+ node->output = fbresult & 0x01;
+ else
+ node->output=((context->lfsr_reg)>>(lfsr_desc->output_bit))&0x01;
+
+ /* Final inversion if required */
+ if(context->invert_output) node->output=(node->output)?0.0:1.0;
+
+ /* Gain stage */
+ node->output=(node->output)?(DSS_LFSR_NOISE__AMP)/2:-(DSS_LFSR_NOISE__AMP)/2;
+ /* Bias input as required */
+ node->output=node->output+DSS_LFSR_NOISE__BIAS;
+ }
+
+ if(!DSS_LFSR_NOISE__ENABLE)
+ {
+ node->output=0;
+ }
+}
+
+void dss_lfsr_reset(node_description *node)
+{
+ const discrete_lfsr_desc *lfsr_desc = node->custom;
+ struct dss_lfsr_context *context = node->context;
+ int fb0,fb1,fbresult;
+
+ context->reset_on_high = (lfsr_desc->flags & DISC_LFSR_FLAG_RESET_TYPE_H) ? 1 : 0;
+ context->invert_output = lfsr_desc->flags & DISC_LFSR_FLAG_OUT_INVERT;
+ context->out_is_f0 = (lfsr_desc->flags & DISC_LFSR_FLAG_OUTPUT_F0) ? 1 : 0;
+
+ if ((lfsr_desc->clock_type < DISC_CLK_ON_F_EDGE) || (lfsr_desc->clock_type > DISC_CLK_IS_FREQ))
+ discrete_log("Invalid clock type passed in NODE_%d\n", node->node - NODE_START);
+ context->last = (DSS_COUNTER__CLOCK != 0);
+ if (lfsr_desc->clock_type == DISC_CLK_IS_FREQ) context->t_clock = 1.0 / DSS_LFSR_NOISE__CLOCK;
+ context->t_left = 0;
+
+ context->lfsr_reg=lfsr_desc->reset_value;
+
+ /* Now get and store the new feedback result */
+ /* Fetch the feedback bits */
+ fb0=((context->lfsr_reg)>>(lfsr_desc->feedback_bitsel0))&0x01;
+ fb1=((context->lfsr_reg)>>(lfsr_desc->feedback_bitsel1))&0x01;
+ /* Now do the combo on them */
+ fbresult=dss_lfsr_function(lfsr_desc->feedback_function0,fb0,fb1,0x01);
+ context->lfsr_reg=dss_lfsr_function(DISC_LFSR_REPLACE,(context->lfsr_reg), fbresult<<(lfsr_desc->bitlength), ((2<<(lfsr_desc->bitlength))-1));
+
+ /* Now select and setup the output bit */
+ node->output=((context->lfsr_reg)>>(lfsr_desc->output_bit))&0x01;
+
+ /* Final inversion if required */
+ if(lfsr_desc->flags&DISC_LFSR_FLAG_OUT_INVERT) node->output=(node->output)?0.0:1.0;
+
+ /* Gain stage */
+ node->output=(node->output)?(DSS_LFSR_NOISE__AMP)/2:-(DSS_LFSR_NOISE__AMP)/2;
+ /* Bias input as required */
+ node->output=node->output+DSS_LFSR_NOISE__BIAS;
+}
+
+
+/************************************************************************
+ *
+ * DSS_NOISE - Usage of node_description values for white nose generator
+ *
+ * input0 - Enable input value
+ * input1 - Noise sample frequency
+ * input2 - Amplitude input value
+ * input3 - DC Bias value
+ *
+ ************************************************************************/
+#define DSS_NOISE__ENABLE (*(node->input[0]))
+#define DSS_NOISE__FREQ (*(node->input[1]))
+#define DSS_NOISE__AMP (*(node->input[2]))
+#define DSS_NOISE__BIAS (*(node->input[3]))
+
+void dss_noise_step(node_description *node)
+{
+ struct dss_noise_context *context = node->context;
+
+ if(DSS_NOISE__ENABLE)
+ {
+ /* Only sample noise on rollover to next cycle */
+ if(context->phase > (2.0*M_PI))
+ {
+ /* GCC's rand returns a RAND_MAX value of 0x7fff */
+ int newval = (mame_rand(Machine) & 0x7fff) - 16384;
+
+ /* make sure the peak to peak values are the amplitude */
+ node->output = DSS_NOISE__AMP / 2;
+ if (newval > 0)
+ node->output *= ((double)newval / 16383);
+ else
+ node->output *= ((double)newval / 16384);
+
+ /* Add DC Bias component */
+ node->output += DSS_NOISE__BIAS;
+ }
+ }
+ else
+ {
+ node->output = 0;
+ }
+
+ /* Keep the new phasor in the 2Pi range.*/
+ context->phase = fmod(context->phase, 2.0*M_PI);
+
+ /* The enable input only curtails output, phase rotation still occurs. */
+ /* We allow the phase to exceed 2Pi here, so we can tell when to sample the noise. */
+ context->phase += ((2.0*M_PI * DSS_NOISE__FREQ) / discrete_current_context->sample_rate);
+}
+
+
+void dss_noise_reset(node_description *node)
+{
+ struct dss_noise_context *context = node->context;
+
+ context->phase=0;
+ dss_noise_step(node);
+}
+
+
+/************************************************************************
+ *
+ * DSS_NOTE - Note/tone generator
+ *
+ * input0 - Enable input value
+ * input1 - Clock Input
+ * input2 - data value
+ * input3 - Max count 1
+ * input4 - Max count 2
+ * input5 - Clock type
+ *
+ * Mar 2004, D Renaud.
+ ************************************************************************/
+ #define DSS_NOTE__ENABLE (*(node->input[0]))
+ #define DSS_NOTE__CLOCK (*(node->input[1]))
+ #define DSS_NOTE__DATA (*(node->input[2]))
+ #define DSS_NOTE__MAX1 (*(node->input[3]))
+ #define DSS_NOTE__MAX2 (*(node->input[4]))
+ #define DSS_NOTE__CLOCK_TYPE (*(node->input[5]))
+
+void dss_note_step(node_description *node)
+{
+ struct dss_note_context *context = node->context;
+
+ double cycles;
+ int clock = 0, last_count2, inc = 0;
+ double xTime = 0;
+
+ if (context->clock_type == DISC_CLK_IS_FREQ)
+ {
+ /* We need to keep clocking the internal clock even if disabled. */
+ cycles = (context->t_left + discrete_current_context->sample_time) / context->t_clock;
+ inc = (int)cycles;
+ context->t_left = (cycles - inc) * context->t_clock;
+ if (inc) xTime = context->t_left / discrete_current_context->sample_time;
+ }
+ else
+ {
+ /* Seperate clock info from xTime info. */
+ clock = (int)DSS_NOTE__CLOCK;
+ xTime = DSS_NOTE__CLOCK - clock;
+ }
+
+ if (DSS_NOTE__ENABLE)
+ {
+ last_count2 = context->count2;
+
+ switch (context->clock_type)
+ {
+ case DISC_CLK_ON_F_EDGE:
+ case DISC_CLK_ON_R_EDGE:
+ /* See if the clock has toggled to the proper edge */
+ clock = (clock != 0);
+ if (context->last != clock)
+ {
+ context->last = clock;
+ if (context->clock_type == clock)
+ {
+ /* Toggled */
+ inc = 1;
+ }
+ }
+ break;
+
+ case DISC_CLK_BY_COUNT:
+ /* Clock number of times specified. */
+ inc = clock;
+ break;
+ }
+
+ /* Count output as long as the data loaded is not already equal to max 1 count. */
+ if (DSS_NOTE__DATA != DSS_NOTE__MAX1)
+ {
+ for (clock = 0; clock < inc; clock++)
+ {
+ context->count1++;
+ if (context->count1 > context->max1)
+ {
+ /* Max 1 count reached. Load Data into counter. */
+ context->count1 = (int)DSS_NOTE__DATA;
+ context->count2 += 1;
+ if (context->count2 > context->max2) context->count2 = 0;
+ }
+ }
+ }
+
+ node->output = context->count2;
+ if (context->count2 != last_count2)
+ {
+ /* the xTime is only output if the output changed. */
+ switch (context->out_type)
+ {
+ case DISC_OUT_IS_ENERGY:
+ if (xTime != 0)
+ node->output = (context->count2 > last_count2) ? (last_count2 + xTime) : (last_count2 - xTime);
+ break;
+ case DISC_OUT_HAS_XTIME:
+ node->output += xTime;
+ break;
+ }
+ }
+ }
+ else
+ node->output = 0;
+}
+
+void dss_note_reset(node_description *node)
+{
+ struct dss_note_context *context = node->context;
+
+ context->clock_type = (int)DSS_NOTE__CLOCK_TYPE & DISC_CLK_MASK;
+ context->out_type = (int)DSS_NOTE__CLOCK_TYPE & DISC_OUT_MASK;
+ context->last = (DSS_NOTE__CLOCK != 0);
+ if (context->clock_type == DISC_CLK_IS_FREQ) context->t_clock = 1.0 / DSS_NOTE__CLOCK;
+ context->t_left = 0;
+
+ context->count1 = (int)DSS_NOTE__DATA;
+ context->count2 = 0;
+ context->max1 = (int)DSS_NOTE__MAX1;
+ context->max2 = (int)DSS_NOTE__MAX2;
+ node->output = 0;
+}
+
+/************************************************************************
+ *
+ * DSS_OP_AMP_OSC - Op Amp Oscillators
+ *
+ * input0 - Enable input value
+ * input1 - vMod1 (if needed)
+ * input2 - vMod2 (if needed)
+ *
+ * also passed discrete_op_amp_osc_info structure
+ *
+ * Mar 2004, D Renaud.
+ ************************************************************************/
+#define DSS_OP_AMP_OSC__ENABLE (*(node->input[0]))
+#define DSS_OP_AMP_OSC__VMOD1 (*(node->input[1]))
+#define DSS_OP_AMP_OSC__VMOD2 (*(node->input[2]))
+
+/* The inputs on a norton op-amp are (info->vP - OP_AMP_NORTON_VBE) */
+/* which is the same as the output high voltage. We will define them */
+/* the same to save a calculation step */
+#define DSS_OP_AMP_OSC_NORTON_VP_IN context->high_out_V
+
+void dss_op_amp_osc_step(node_description *node)
+{
+ const discrete_op_amp_osc_info *info = node->custom;
+ struct dss_op_amp_osc_context *context = node->context;
+
+
+ double i; // Charging current created by vIn
+ double v = 0; // all input voltages mixed
+ double dt; // change in time
+ double vC; // Current voltage on capacitor, before dt
+ double vCnext = 0; // Voltage on capacitor, after dt
+ double iCharge[2] = {0};
+ UINT8 force_charge = 0;
+ UINT8 enable = DSS_OP_AMP_OSC__ENABLE;
+
+ dt = discrete_current_context->sample_time; // Change in time
+ vC = context->vCap; // Set to voltage before change
+
+ /* work out the charge currents for the VCOs. */
+ switch (context->type)
+ {
+ case DISC_OP_AMP_OSCILLATOR_VCO_1:
+ /* Work out the charge rates. */
+ i = DSS_OP_AMP_OSC__VMOD1 * context->temp1; // i is not a current. It is being used as a temp variable.
+ iCharge[0] = (DSS_OP_AMP_OSC__VMOD1 - i) / info->r1;
+ iCharge[1] = (i - (DSS_OP_AMP_OSC__VMOD1 * context->temp2)) / context->temp3;
+ break;
+
+ case DISC_OP_AMP_OSCILLATOR_1 | DISC_OP_AMP_IS_NORTON:
+ {
+ /* resistors can be nodes, so everything needs updating */
+ double i1, i2;
+ /* Work out the charge rates. */
+ iCharge[0] = DSS_OP_AMP_OSC_NORTON_VP_IN / *context->r1;
+ iCharge[1] = (context->high_out_V - OP_AMP_NORTON_VBE) / *context->r2 - iCharge[0];
+ /* Work out the Inverting Schmitt thresholds. */
+ i1 = DSS_OP_AMP_OSC_NORTON_VP_IN / *context->r5;
+ i2 = (0.0 - OP_AMP_NORTON_VBE) / *context->r4;
+ context->thresholdLow = (i1 + i2) * *context->r3 + OP_AMP_NORTON_VBE;
+ i2 = (context->high_out_V - OP_AMP_NORTON_VBE) / *context->r4;
+ context->thresholdHigh = (i1 + i2) * *context->r3 + OP_AMP_NORTON_VBE;
+ break;
+ }
+
+ case DISC_OP_AMP_OSCILLATOR_VCO_1 | DISC_OP_AMP_IS_NORTON:
+ /* Millman the input voltages. */
+ if (info->r7 == 0)
+ {
+ /* No r7 means that the modulation circuit is fed directly into the circuit. */
+ v = DSS_OP_AMP_OSC__VMOD1;
+ }
+ else
+ {
+ /* we need to mix any bias and all modulation voltages together. */
+ i = context->iFixed;
+ i += DSS_OP_AMP_OSC__VMOD1 / info->r7;
+ if (info->r8 != 0)
+ i += DSS_OP_AMP_OSC__VMOD2 / info->r8;
+ v = i * context->rTotal;
+ }
+
+ /* Work out the charge rates. */
+ v -= OP_AMP_NORTON_VBE;
+ iCharge[0] = v / info->r1;
+ iCharge[1] = v / info->r2 - iCharge[0];
+
+ /* use the real enable circuit */
+ force_charge = !enable;
+ enable = 1;
+ break;
+
+ case DISC_OP_AMP_OSCILLATOR_VCO_2 | DISC_OP_AMP_IS_NORTON:
+ /* Work out the charge rates. */
+ i = DSS_OP_AMP_OSC__VMOD1 / info->r1;
+ iCharge[0] = i - context->temp1;
+ iCharge[1] = context->temp2 - i;
+ /* if the negative pin current is less then the positive pin current, */
+ /* then the osc is disabled and the cap keeps charging */
+ if (iCharge[0] < 0)
+ {
+ force_charge = 1;
+ iCharge[0] *= -1;
+ }
+ break;
+
+ case DISC_OP_AMP_OSCILLATOR_VCO_3 | DISC_OP_AMP_IS_NORTON:
+ /* we need to mix any bias and all modulation voltages together. */
+ iCharge[0] = context->iFixed;
+ v = DSS_OP_AMP_OSC__VMOD1 - OP_AMP_NORTON_VBE;
+ iCharge[0] += v / info->r1;
+ if (info->r6 != 0)
+ {
+ v = DSS_OP_AMP_OSC__VMOD2 - OP_AMP_NORTON_VBE;
+ iCharge[0] += v / info->r6;
+ }
+ iCharge[1] = context->temp1 - iCharge[0];
+ break;
+ }
+
+ if (enable)
+ {
+ int toggled = 0;
+ /* Keep looping until all toggling in time sample is used up. */
+ do
+ {
+ if ((context->flip_flop ^ context->flip_flopXOR) || force_charge)
+ {
+ /* Charging */
+ /* iC=C*dv/dt works out to dv=iC*dt/C */
+ vCnext = vC + (iCharge[1] * dt / info->c);
+ dt = 0;
+
+ /* has it charged past upper limit? */
+ if (vCnext > context->thresholdHigh)
+ {
+ context->flip_flop = context->flip_flopXOR;
+ toggled++;
+ if (force_charge)
+ {
+ /* we need to keep charging the cap to the max thereby disabling the circuit */
+ if (vCnext > context->high_out_V)
+ vCnext = context->high_out_V;
+ }
+ else
+ {
+ /* calculate the overshoot time */
+ dt = info->c * (vCnext - context->thresholdHigh) / iCharge[1];
+ vC = context->thresholdHigh;
+ }
+ }
+ }
+ else
+ {
+ /* Discharging */
+ vCnext = vC - (iCharge[0] * dt / info->c);
+ dt = 0;
+
+ /* has it discharged past lower limit? */
+ if (vCnext < context->thresholdLow)
+ {
+ context->flip_flop = !context->flip_flopXOR;
+ toggled++;
+ /* calculate the overshoot time */
+ dt = info->c * (context->thresholdLow - vCnext) / iCharge[0];
+ vC = context->thresholdLow;
+ }
+ }
+ } while(dt);
+
+ context->vCap = vCnext;
+
+ if (context->is_squarewave)
+ {
+ if (toggled == 2)
+ /* Some oscillators have rapid rise or fall times causing 1 part of the */
+ /* squarewave to happen in the sample time causing it to be missed. */
+ /* If we toggle 2 states we force the missed output for 1 sample. */
+ /* If more then 2 states happen, there is no hope, the sample rate is just too low. */
+ node->output = context->high_out_V * (context->flip_flop ? 0 : 1);
+ else
+ node->output = context->high_out_V * context->flip_flop;
+ }
+ else
+ node->output = context->vCap;
+ }
+ else
+ {
+ /* we will just output 0 for oscillators that have no real enable. */
+ node->output = 0;
+ }
+}
+
+void dss_op_amp_osc_reset(node_description *node)
+{
+ const discrete_op_amp_osc_info *info = node->custom;
+ struct dss_op_amp_osc_context *context = node->context;
+ const double *r_info_ptr;
+ const double **r_context_ptr;
+ int loop;
+ node_description *r_node;
+
+ double i1 = 0; // inverting input current
+ double i2 = 0; // non-inverting input current
+
+ /* link to resistor static or node values */
+ r_info_ptr = &info->r1;
+ r_context_ptr = &context->r1;
+ for (loop = 0; loop < 8; loop ++)
+ {
+ if IS_VALUE_A_NODE(*r_info_ptr)
+ {
+ r_node = discrete_find_node(NULL, *r_info_ptr);
+ *r_context_ptr = &(r_node->output);
+ }
+ else
+ *r_context_ptr = r_info_ptr;
+ r_info_ptr++;
+ r_context_ptr++;
+ }
+
+ context->is_squarewave = (info->type & DISC_OP_AMP_OSCILLATOR_OUT_SQW);
+ context->type = info->type & DISC_OP_AMP_OSCILLATOR_TYPE_MASK;
+
+ switch (context->type)
+ {
+ case DISC_OP_AMP_OSCILLATOR_VCO_1:
+ /* The charge rates vary depending on vMod so they are not precalculated. */
+ /* Charges while FlipFlop High */
+ context->flip_flopXOR = 0;
+ /* Work out the Non-inverting Schmitt thresholds. */
+ context->temp1 = (info->vP / 2) / info->r4;
+ context->temp2 = (info->vP - OP_AMP_VP_RAIL_OFFSET) / info->r3;
+ context->temp3 = 1.0 / (1.0 / info->r3 + 1.0 / info->r4);
+ context->thresholdLow = context->temp1 * context->temp3;
+ context->thresholdHigh = (context->temp1 + context->temp2) * context->temp3;
+ /* There is no charge on the cap so the schmitt goes high at init. */
+ context->flip_flop = 1;
+ /* Setup some commonly used stuff */
+ context->temp1 = info->r5 / (info->r2 + info->r5); // voltage ratio across r5
+ context->temp2 = info->r6 / (info->r1 + info->r6); // voltage ratio across r6
+ context->temp3 = 1.0 / (1.0 / info->r1 + 1.0 / info->r6); // input resistance when r6 switched in
+ break;
+
+ case DISC_OP_AMP_OSCILLATOR_1 | DISC_OP_AMP_IS_NORTON:
+ /* Charges while FlipFlop High */
+ context->flip_flopXOR = 0;
+ /* There is no charge on the cap so the schmitt inverter goes high at init. */
+ context->flip_flop = 1;
+ break;
+
+ case DISC_OP_AMP_OSCILLATOR_VCO_1 | DISC_OP_AMP_IS_NORTON:
+ /* Charges while FlipFlop Low */
+ context->flip_flopXOR = 1;
+ /* There is no charge on the cap so the schmitt goes low at init. */
+ context->flip_flop = 0;
+ /* The charge rates vary depending on vMod so they are not precalculated. */
+ /* But we can precalculate the fixed currents. */
+ context->iFixed = 0;
+ if (info->r6 != 0) context->iFixed += info->vP / info->r6;
+ context->iFixed += OP_AMP_NORTON_VBE / info->r1;
+ context->iFixed += OP_AMP_NORTON_VBE / info->r2;
+ /* Work out the input resistance to be used later to calculate the Millman voltage. */
+ context->rTotal = 1.0 / info->r1 + 1.0 / info->r2 + 1.0 / info->r7;
+ if (info->r6) context->rTotal += 1.0 / info->r6;
+ if (info->r8) context->rTotal += 1.0 / info->r8;
+ context->rTotal = 1.0 / context->rTotal;
+ /* Work out the Non-inverting Schmitt thresholds. */
+ i1 = (info->vP - OP_AMP_NORTON_VBE) / info->r5;
+ i2 = (info->vP - OP_AMP_NORTON_VBE - OP_AMP_NORTON_VBE) / info->r4;
+ context->thresholdLow = (i1 - i2) * info->r3 + OP_AMP_NORTON_VBE;
+ i2 = (0.0 - OP_AMP_NORTON_VBE) / info->r4;
+ context->thresholdHigh = (i1 - i2) * info->r3 + OP_AMP_NORTON_VBE;
+ break;
+
+ case DISC_OP_AMP_OSCILLATOR_VCO_2 | DISC_OP_AMP_IS_NORTON:
+ /* Charges while FlipFlop High */
+ context->flip_flopXOR = 0;
+ /* There is no charge on the cap so the schmitt inverter goes high at init. */
+ context->flip_flop = 1;
+ /* Work out the charge rates. */
+ context->temp1 = (info->vP - OP_AMP_NORTON_VBE) / info->r2;
+ context->temp2 = (info->vP - OP_AMP_NORTON_VBE) * (1.0 / info->r2 + 1.0 / info->r6);
+ /* Work out the Inverting Schmitt thresholds. */
+ i1 = (info->vP - OP_AMP_NORTON_VBE) / info->r5;
+ i2 = (0.0 - OP_AMP_NORTON_VBE) / info->r4;
+ context->thresholdLow = (i1 + i2) * info->r3 + OP_AMP_NORTON_VBE;
+ i2 = (info->vP - OP_AMP_NORTON_VBE - OP_AMP_NORTON_VBE) / info->r4;
+ context->thresholdHigh = (i1 + i2) * info->r3 + OP_AMP_NORTON_VBE;
+ break;
+
+ case DISC_OP_AMP_OSCILLATOR_VCO_3 | DISC_OP_AMP_IS_NORTON:
+ /* Charges while FlipFlop High */
+ context->flip_flopXOR = 0;
+ /* There is no charge on the cap so the schmitt inverter goes high at init. */
+ context->flip_flop = 1;
+ /* Work out the charge rates. */
+ /* The charge rates vary depending on vMod so they are not precalculated. */
+ /* But we can precalculate the fixed currents. */
+ if (info->r7 != 0) context->iFixed = (info->vP - OP_AMP_NORTON_VBE) / info->r7;
+ context->temp1 = (info->vP - OP_AMP_NORTON_VBE - OP_AMP_NORTON_VBE) / info->r2;
+ /* Work out the Inverting Schmitt thresholds. */
+ i1 = (info->vP - OP_AMP_NORTON_VBE) / info->r5;
+ i2 = (0.0 - OP_AMP_NORTON_VBE) / info->r4;
+ context->thresholdLow = (i1 + i2) * info->r3 + OP_AMP_NORTON_VBE;
+ i2 = (info->vP - OP_AMP_NORTON_VBE - OP_AMP_NORTON_VBE) / info->r4;
+ context->thresholdHigh = (i1 + i2) * info->r3 + OP_AMP_NORTON_VBE;
+ break;
+ }
+
+ context->high_out_V = info->vP - ((context->type & DISC_OP_AMP_IS_NORTON) ? OP_AMP_NORTON_VBE : OP_AMP_VP_RAIL_OFFSET);
+ context->vCap = 0;
+
+ dss_op_amp_osc_step(node);
+}
+
+
+/************************************************************************
+ *
+ * DSS_SAWTOOTHWAVE - Usage of node_description values for step function
+ *
+ * input0 - Enable input value
+ * input1 - Frequency input value
+ * input2 - Amplitde input value
+ * input3 - DC Bias Value
+ * input4 - Gradient
+ * input5 - Initial Phase
+ *
+ ************************************************************************/
+#define DSS_SAWTOOTHWAVE__ENABLE (*(node->input[0]))
+#define DSS_SAWTOOTHWAVE__FREQ (*(node->input[1]))
+#define DSS_SAWTOOTHWAVE__AMP (*(node->input[2]))
+#define DSS_SAWTOOTHWAVE__BIAS (*(node->input[3]))
+#define DSS_SAWTOOTHWAVE__GRAD (*(node->input[4]))
+#define DSS_SAWTOOTHWAVE__PHASE (*(node->input[5]))
+
+void dss_sawtoothwave_step(node_description *node)
+{
+ struct dss_sawtoothwave_context *context = node->context;
+
+ if(DSS_SAWTOOTHWAVE__ENABLE)
+ {
+ node->output=(context->type==0)?context->phase*(DSS_SAWTOOTHWAVE__AMP/(2.0*M_PI)):DSS_SAWTOOTHWAVE__AMP-(context->phase*(DSS_SAWTOOTHWAVE__AMP/(2.0*M_PI)));
+ node->output-=DSS_SAWTOOTHWAVE__AMP/2.0;
+ /* Add DC Bias component */
+ node->output=node->output+DSS_SAWTOOTHWAVE__BIAS;
+ }
+ else
+ {
+ node->output=0;
+ }
+
+ /* Work out the phase step based on phase/freq & sample rate */
+ /* The enable input only curtails output, phase rotation */
+ /* still occurs */
+ /* phase step = 2Pi/(output period/sample period) */
+ /* boils out to */
+ /* phase step = (2Pi*output freq)/sample freq) */
+ /* Also keep the new phasor in the 2Pi range. */
+ context->phase=fmod((context->phase+((2.0*M_PI*DSS_SAWTOOTHWAVE__FREQ)/discrete_current_context->sample_rate)),2.0*M_PI);
+}
+
+void dss_sawtoothwave_reset(node_description *node)
+{
+ struct dss_sawtoothwave_context *context = node->context;
+ double start;
+
+ /* Establish starting phase, convert from degrees to radians */
+ start=(DSS_SAWTOOTHWAVE__PHASE/360.0)*(2.0*M_PI);
+ /* Make sure its always mod 2Pi */
+ context->phase=fmod(start,2.0*M_PI);
+
+ /* Invert gradient depending on sawtooth type /|/|/|/|/| or |\|\|\|\|\ */
+ context->type=(DSS_SAWTOOTHWAVE__GRAD)?1:0;
+
+ /* Step the node to set the output */
+ dss_sawtoothwave_step(node);
+}
+
+
+/************************************************************************
+ *
+ * DSS_SCHMITT_OSC - Schmitt feedback oscillator
+ *
+ * input0 - Enable input value
+ * input1 - Vin
+ * input2 - Amplitude
+ *
+ * also passed discrete_schmitt_osc_disc structure
+ *
+ * Mar 2004, D Renaud.
+ ************************************************************************/
+#define DSS_SCHMITT_OSC__ENABLE (int)(*(node->input[0]))
+#define DSS_SCHMITT_OSC__VIN (*(node->input[1]))
+#define DSS_SCHMITT_OSC__AMP (*(node->input[2]))
+
+void dss_schmitt_osc_step(node_description *node)
+{
+ const discrete_schmitt_osc_desc *info = node->custom;
+ struct dss_schmitt_osc_context *context = node->context;
+
+ double supply, vCap, new_vCap, t, exponent;
+
+ /* We will always oscillate. The enable just affects the output. */
+ vCap = context->vCap;
+ exponent = context->exponent;
+
+ /* Keep looping until all toggling in time sample is used up. */
+ do
+ {
+ t = 0;
+ /* The charging voltage to the cap is the sum of the input voltage and the gate
+ * output voltage in the ratios determined by their resistors in a divider network.
+ * The input voltage is selectable as straight voltage in or logic level that will
+ * use vGate as its voltage. Note that ratioIn is just the ratio of the total
+ * voltage and needs to be multipled by the input voltage. ratioFeedback has
+ * already been multiplied by vGate to save time because that voltage never changes. */
+ supply = context->input_is_voltage ? context->ratioIn * DSS_SCHMITT_OSC__VIN : (DSS_SCHMITT_OSC__VIN ? context->ratioIn * info->vGate : 0);
+ supply += (context->state ? context->ratioFeedback : 0);
+ new_vCap = vCap + ((supply - vCap) * exponent);
+ if (context->state)
+ {
+ /* Charging */
+ /* has it charged past upper limit? */
+ if (new_vCap > info->trshRise)
+ {
+ /* calculate the overshoot time */
+ t = context->rc * log(1.0 / (1.0 - ((new_vCap - info->trshRise) / (info->vGate - vCap))));
+ /* calculate new exponent because of reduced time */
+ exponent = 1.0 - exp(-t / context->rc);
+ vCap = new_vCap = info->trshRise;
+ context->state = 0;
+ }
+ }
+ else
+ {
+ /* Discharging */
+ /* has it discharged past lower limit? */
+ if (new_vCap < info->trshFall)
+ {
+ /* calculate the overshoot time */
+ t = context->rc * log(1.0 / (1.0 - ((info->trshFall - new_vCap) / vCap)));
+ /* calculate new exponent because of reduced time */
+ exponent = 1.0 - exp(-t / context->rc);
+ vCap = new_vCap = info->trshFall;
+ context->state = 1;
+ }
+ }
+ } while(t);
+
+ context->vCap = new_vCap;
+
+ switch (context->enable_type)
+ {
+ case DISC_SCHMITT_OSC_ENAB_IS_AND:
+ node->output = DSS_SCHMITT_OSC__ENABLE && context->state;
+ break;
+ case DISC_SCHMITT_OSC_ENAB_IS_NAND:
+ node->output = !(DSS_SCHMITT_OSC__ENABLE && context->state);
+ break;
+ case DISC_SCHMITT_OSC_ENAB_IS_OR:
+ node->output = DSS_SCHMITT_OSC__ENABLE || context->state;
+ break;
+ case DISC_SCHMITT_OSC_ENAB_IS_NOR:
+ node->output = !(DSS_SCHMITT_OSC__ENABLE || context->state);
+ break;
+ }
+ node->output *= DSS_SCHMITT_OSC__AMP;
+}
+
+void dss_schmitt_osc_reset(node_description *node)
+{
+ const discrete_schmitt_osc_desc *info = node->custom;
+ struct dss_schmitt_osc_context *context = node->context;
+ double rSource;
+
+ context->enable_type = info->options & DISC_SCHMITT_OSC_ENAB_MASK;
+ context->input_is_voltage = (info->options & DISC_SCHMITT_OSC_IN_IS_VOLTAGE) ? 1 : 0;
+
+ /* The 2 resistors make a voltage divider, so their ratios add together
+ * to make the charging voltage. */
+ context->ratioIn = info->rFeedback / (info->rIn + info->rFeedback);
+ context->ratioFeedback = info->rIn / (info->rIn + info->rFeedback) * info->vGate;
+
+ /* The voltage source resistance works out to the 2 resistors in parallel.
+ * So use this for the RC charge constant. */
+ rSource = 1.0 / ((1.0 / info->rIn) + (1.0 / info->rFeedback));
+ context->rc = rSource * info->c;
+ context->exponent = -1.0 / (context->rc * discrete_current_context->sample_rate);
+ context->exponent = 1.0 - exp(context->exponent);
+
+ /* Cap is at 0V on power up. Causing output to be high. */
+ context->vCap = 0;
+ context->state = 1;
+
+ node->output = info->options ? 0 : DSS_SCHMITT_OSC__AMP;
+}
+
+
+/************************************************************************
+ *
+ * DSS_SINEWAVE - Usage of node_description values for step function
+ *
+ * input0 - Enable input value
+ * input1 - Frequency input value
+ * input2 - Amplitude input value
+ * input3 - DC Bias
+ * input4 - Starting phase
+ *
+ ************************************************************************/
+#define DSS_SINEWAVE__ENABLE (*(node->input[0]))
+#define DSS_SINEWAVE__FREQ (*(node->input[1]))
+#define DSS_SINEWAVE__AMPL (*(node->input[2]))
+#define DSS_SINEWAVE__BIAS (*(node->input[3]))
+#define DSS_SINEWAVE__PHASE (*(node->input[4]))
+
+void dss_sinewave_step(node_description *node)
+{
+ struct dss_sinewave_context *context = node->context;
+
+ /* Set the output */
+ if(DSS_SINEWAVE__ENABLE)
+ {
+ node->output=(DSS_SINEWAVE__AMPL/2.0) * sin(context->phase);
+ /* Add DC Bias component */
+ node->output=node->output+DSS_SINEWAVE__BIAS;
+ }
+ else
+ {
+ node->output=0;
+ }
+
+ /* Work out the phase step based on phase/freq & sample rate */
+ /* The enable input only curtails output, phase rotation */
+ /* still occurs */
+ /* phase step = 2Pi/(output period/sample period) */
+ /* boils out to */
+ /* phase step = (2Pi*output freq)/sample freq) */
+ /* Also keep the new phasor in the 2Pi range. */
+ context->phase=fmod((context->phase+((2.0*M_PI*DSS_SINEWAVE__FREQ)/discrete_current_context->sample_rate)),2.0*M_PI);
+}
+
+void dss_sinewave_reset(node_description *node)
+{
+ struct dss_sinewave_context *context = node->context;
+ double start;
+
+ /* Establish starting phase, convert from degrees to radians */
+ start=(DSS_SINEWAVE__PHASE/360.0)*(2.0*M_PI);
+ /* Make sure its always mod 2Pi */
+ context->phase=fmod(start,2.0*M_PI);
+ /* Step the output to make it correct */
+ dss_sinewave_step(node);
+}
+
+
+/************************************************************************
+ *
+ * DSS_SQUAREWAVE - Usage of node_description values for step function
+ *
+ * input0 - Enable input value
+ * input1 - Frequency input value
+ * input2 - Amplitude input value
+ * input3 - Duty Cycle
+ * input4 - DC Bias level
+ * input5 - Start Phase
+ *
+ ************************************************************************/
+#define DSS_SQUAREWAVE__ENABLE (*(node->input[0]))
+#define DSS_SQUAREWAVE__FREQ (*(node->input[1]))
+#define DSS_SQUAREWAVE__AMP (*(node->input[2]))
+#define DSS_SQUAREWAVE__DUTY (*(node->input[3]))
+#define DSS_SQUAREWAVE__BIAS (*(node->input[4]))
+#define DSS_SQUAREWAVE__PHASE (*(node->input[5]))
+
+void dss_squarewave_step(node_description *node)
+{
+ struct dss_squarewave_context *context = node->context;
+
+ /* Establish trigger phase from duty */
+ context->trigger=((100-DSS_SQUAREWAVE__DUTY)/100)*(2.0*M_PI);
+
+ /* Set the output */
+ if(DSS_SQUAREWAVE__ENABLE)
+ {
+ if(context->phase>context->trigger)
+ node->output=(DSS_SQUAREWAVE__AMP/2.0);
+ else
+ node->output=-(DSS_SQUAREWAVE__AMP/2.0);
+
+ /* Add DC Bias component */
+ node->output=node->output+DSS_SQUAREWAVE__BIAS;
+ }
+ else
+ {
+ node->output=0;
+ }
+
+ /* Work out the phase step based on phase/freq & sample rate */
+ /* The enable input only curtails output, phase rotation */
+ /* still occurs */
+ /* phase step = 2Pi/(output period/sample period) */
+ /* boils out to */
+ /* phase step = (2Pi*output freq)/sample freq) */
+ /* Also keep the new phasor in the 2Pi range. */
+ context->phase=fmod((context->phase+((2.0*M_PI*DSS_SQUAREWAVE__FREQ)/discrete_current_context->sample_rate)),2.0*M_PI);
+}
+
+void dss_squarewave_reset(node_description *node)
+{
+ struct dss_squarewave_context *context = node->context;
+ double start;
+
+ /* Establish starting phase, convert from degrees to radians */
+ start=(DSS_SQUAREWAVE__PHASE/360.0)*(2.0*M_PI);
+ /* Make sure its always mod 2Pi */
+ context->phase=fmod(start,2.0*M_PI);
+
+ /* Step the output */
+ dss_squarewave_step(node);
+}
+
+/************************************************************************
+ *
+ * DSS_SQUAREWFIX - Usage of node_description values for step function
+ *
+ * input0 - Enable input value
+ * input1 - Frequency input value
+ * input2 - Amplitude input value
+ * input3 - Duty Cycle
+ * input4 - DC Bias level
+ * input5 - Start Phase
+ *
+ ************************************************************************/
+#define DSS_SQUAREWFIX__ENABLE (*(node->input[0]))
+#define DSS_SQUAREWFIX__FREQ (*(node->input[1]))
+#define DSS_SQUAREWFIX__AMP (*(node->input[2]))
+#define DSS_SQUAREWFIX__DUTY (*(node->input[3]))
+#define DSS_SQUAREWFIX__BIAS (*(node->input[4]))
+#define DSS_SQUAREWFIX__PHASE (*(node->input[5]))
+
+void dss_squarewfix_step(node_description *node)
+{
+ struct dss_squarewfix_context *context = node->context;
+
+ context->tLeft -= context->sampleStep;
+
+ /* The enable input only curtails output, phase rotation still occurs */
+ while (context->tLeft <= 0)
+ {
+ context->flip_flop = context->flip_flop ? 0 : 1;
+ context->tLeft += context->flip_flop ? context->tOn : context->tOff;
+ }
+
+ if(DSS_SQUAREWFIX__ENABLE)
+ {
+ /* Add gain and DC Bias component */
+
+ context->tOff = 1.0 / DSS_SQUAREWFIX__FREQ; /* cycle time */
+ context->tOn = context->tOff * (DSS_SQUAREWFIX__DUTY / 100.0);
+ context->tOff -= context->tOn;
+
+ node->output = (context->flip_flop ? DSS_SQUAREWFIX__AMP / 2.0 : -(DSS_SQUAREWFIX__AMP / 2.0)) + DSS_SQUAREWFIX__BIAS;
+ }
+ else
+ {
+ node->output=0;
+ }
+}
+
+void dss_squarewfix_reset(node_description *node)
+{
+ struct dss_squarewfix_context *context = node->context;
+
+ context->sampleStep = 1.0 / discrete_current_context->sample_rate;
+ context->flip_flop = 1;
+
+ /* Do the intial time shift and convert freq to off/on times */
+ context->tOff = 1.0 / DSS_SQUAREWFIX__FREQ; /* cycle time */
+ context->tLeft = DSS_SQUAREWFIX__PHASE / 360.0; /* convert start phase to % */
+ context->tLeft = context->tLeft - (int)context->tLeft; /* keep % between 0 & 1 */
+ context->tLeft = (context->tLeft < 0) ? 1.0 + context->tLeft : context->tLeft; /* if - then flip to + phase */
+ context->tLeft *= context->tOff;
+ context->tOn = context->tOff * (DSS_SQUAREWFIX__DUTY / 100.0);
+ context->tOff -= context->tOn;
+
+ context->tLeft = -context->tLeft;
+
+ /* toggle output and work out intial time shift */
+ while (context->tLeft <= 0)
+ {
+ context->flip_flop = context->flip_flop ? 0 : 1;
+ context->tLeft += context->flip_flop ? context->tOn : context->tOff;
+ }
+
+ /* Step the output */
+ dss_squarewfix_step(node);
+}
+
+
+/************************************************************************
+ *
+ * DSS_SQUAREWAVE2 - Usage of node_description values
+ *
+ * input0 - Enable input value
+ * input1 - Amplitude input value
+ * input2 - OFF Time
+ * input3 - ON Time
+ * input4 - DC Bias level
+ * input5 - Initial Time Shift
+ *
+ ************************************************************************/
+#define DSS_SQUAREWAVE2__ENABLE (*(node->input[0]))
+#define DSS_SQUAREWAVE2__AMP (*(node->input[1]))
+#define DSS_SQUAREWAVE2__T_OFF (*(node->input[2]))
+#define DSS_SQUAREWAVE2__T_ON (*(node->input[3]))
+#define DSS_SQUAREWAVE2__BIAS (*(node->input[4]))
+#define DSS_SQUAREWAVE2__SHIFT (*(node->input[5]))
+
+void dss_squarewave2_step(node_description *node)
+{
+ struct dss_squarewave_context *context = node->context;
+ double newphase;
+
+ if(DSS_SQUAREWAVE2__ENABLE)
+ {
+ /* Establish trigger phase from time periods */
+ context->trigger=(DSS_SQUAREWAVE2__T_OFF / (DSS_SQUAREWAVE2__T_OFF + DSS_SQUAREWAVE2__T_ON)) * (2.0 * M_PI);
+
+ /* Work out the phase step based on phase/freq & sample rate */
+ /* The enable input only curtails output, phase rotation */
+ /* still occurs */
+
+ /* phase step = 2Pi/(output period/sample period) */
+ /* boils out to */
+ /* phase step = 2Pi/(output period*sample freq) */
+ newphase = context->phase + ((2.0 * M_PI) / ((DSS_SQUAREWAVE2__T_OFF + DSS_SQUAREWAVE2__T_ON) * discrete_current_context->sample_rate));
+ /* Keep the new phasor in the 2Pi range.*/
+ context->phase = fmod(newphase, 2.0 * M_PI);
+
+ if(context->phase>context->trigger)
+ node->output=(DSS_SQUAREWAVE2__AMP/2.0);
+ else
+ node->output=-(DSS_SQUAREWAVE2__AMP/2.0);
+
+ /* Add DC Bias component */
+ node->output = node->output + DSS_SQUAREWAVE2__BIAS;
+ }
+ else
+ {
+ node->output=0;
+ }
+}
+
+void dss_squarewave2_reset(node_description *node)
+{
+ struct dss_squarewave_context *context = node->context;
+ double start;
+
+ /* Establish starting phase, convert from degrees to radians */
+ /* Only valid if we have set the on/off time */
+ if((DSS_SQUAREWAVE2__T_OFF + DSS_SQUAREWAVE2__T_ON) != 0.0)
+ start = (DSS_SQUAREWAVE2__SHIFT / (DSS_SQUAREWAVE2__T_OFF + DSS_SQUAREWAVE2__T_ON)) * (2.0 * M_PI);
+ else
+ start = 0.0;
+ /* Make sure its always mod 2Pi */
+ context->phase = fmod(start, 2.0 * M_PI);
+
+ /* Step the output */
+ dss_squarewave2_step(node);
+}
+
+/************************************************************************
+ *
+ * DSS_INVERTER_OSC - Usage of node_description values
+ *
+ * input0 - Enable input value
+ * input1 - RC Resistor
+ * input2 - RP Resistor
+ * input3 - C Capacitor
+ * input4 - Desc
+ *
+ ************************************************************************/
+#define DSS_INVERTER_OSC__ENABLE (*(node->input[0]))
+#define DSS_INVERTER_OSC__MOD (*(node->input[1]))
+#define DSS_INVERTER_OSC__RC (*(node->input[2]))
+#define DSS_INVERTER_OSC__RP (*(node->input[3]))
+#define DSS_INVERTER_OSC__C (*(node->input[4]))
+#define DSS_INVERTER_OSC__R2 (*(node->input[5]))
+
+static double dss_inverter_tf(node_description *node, double x)
+{
+ const discrete_inverter_osc_desc *info = node->custom;
+ struct dss_inverter_osc_context *context = node->context;
+
+ x = x / info->vB;
+ if (x>0)
+ return info->vB * exp(-context->tf_a * pow(x,context->tf_b));
+ else
+ return info->vB;
+}
+
+void dss_inverter_osc_step(node_description *node)
+{
+ struct dss_inverter_osc_context *context = node->context;
+ const discrete_inverter_osc_desc *info = node->custom;
+ double diff,vG1,vG2, vG3, vI;
+ double vMix, rMix;
+
+ /* Get new state */
+ vI = context->vCap + context->vG2_old;
+ switch (info->options & DISC_OSC_INVERTER_TYPE_MASK)
+ {
+ case DISC_OSC_INVERTER_IS_TYPE1:
+ case DISC_OSC_INVERTER_IS_TYPE3:
+ vG1 = dss_inverter_tf(node,vI);
+ vG2 = dss_inverter_tf(node,vG1);
+ vG3 = dss_inverter_tf(node,vG2);
+ break;
+ case DISC_OSC_INVERTER_IS_TYPE2:
+ vG1 = 0;
+ vG3 = dss_inverter_tf(node,vI);
+ vG2 = dss_inverter_tf(node,vG3);
+ break;
+ case DISC_OSC_INVERTER_IS_TYPE4:
+ vI = MIN(DSS_INVERTER_OSC__ENABLE, vI + 0.7);
+ vG1 = 0;
+ vG3 = dss_inverter_tf(node,vI);
+ vG2 = dss_inverter_tf(node,vG3);
+ break;
+ case DISC_OSC_INVERTER_IS_TYPE5:
+ vI = MAX(DSS_INVERTER_OSC__ENABLE, vI - 0.7);
+ vG1 = 0;
+ vG3 = dss_inverter_tf(node,vI);
+ vG2 = dss_inverter_tf(node,vG3);
+ break;
+ default:
+ fatalerror("DISCRETE_INVERTER_OSC - Wrong type on NODE_%02d", node->node - NODE_00);
+ }
+ switch (info->options & DISC_OSC_INVERTER_TYPE_MASK)
+ {
+ case DISC_OSC_INVERTER_IS_TYPE1:
+ case DISC_OSC_INVERTER_IS_TYPE2:
+ case DISC_OSC_INVERTER_IS_TYPE3:
+ if ((info->clamp >= 0.0) && ((vI< - info->clamp) || (vI> info->vB+info->clamp)))
+ {
+ vI = MAX(vI, (- info->clamp));
+ vI = MIN(vI, info->vB + info->clamp);
+ diff = vG3 * (context->Rp / (context->Rp + context->R1))
+ - (context->vCap + vG2)
+ + vI*(context->R1 / (context->Rp + context->R1));
+ diff = diff - diff * exp(context->wc * discrete_current_context->sample_time);
+ }
+ else
+ {
+ diff = vG3 - (context->vCap + vG2);
+ diff = diff - diff * exp(context->w * discrete_current_context->sample_time);
+ }
+ break;
+ case DISC_OSC_INVERTER_IS_TYPE4:
+ if ((info->clamp >= 0.0) && ((vI< - info->clamp) || (vI> info->vB+info->clamp)))
+ {
+ vI = MAX(vI, (- info->clamp));
+ vI = MIN(vI, info->vB + info->clamp);
+ }
+ // FIXME handle R2 = 0
+ rMix = (context->R1 * context->R2) / (context->R1 + context->R2);
+ vMix = rMix* ((vG3-vG2) / context->R1 + (DSS_INVERTER_OSC__MOD-vG2) / context->R2);
+ if (vMix < (vI-vG2-0.7))
+ {
+ rMix = 1.0/rMix + 1.0/context->Rp;
+ rMix = 1.0 / rMix;
+ vMix = rMix* ( (vG3-vG2) / context->R1 + (DSS_INVERTER_OSC__MOD-vG2) / context->R2 + (vI-0.7-vG2)/context->Rp);
+ }
+ diff = vMix - context->vCap;
+ diff = diff - diff * exp(-discrete_current_context->sample_time/(context->C * rMix));
+ break;
+ case DISC_OSC_INVERTER_IS_TYPE5:
+ if ((info->clamp >= 0.0) && ((vI< - info->clamp) || (vI> info->vB+info->clamp)))
+ {
+ vI = MAX(vI, (- info->clamp));
+ vI = MIN(vI, info->vB + info->clamp);
+ }
+ // FIXME handle R2 = 0
+ rMix = (context->R1 * context->R2) / (context->R1 + context->R2);
+ vMix = rMix* ((vG3-vG2) / context->R1 + (DSS_INVERTER_OSC__MOD-vG2) / context->R2);
+ if (vMix > (vI-vG2+0.7))
+ {
+ rMix = 1.0/rMix + 1.0/context->Rp;
+ rMix = 1.0 / rMix;
+ vMix = rMix* ( (vG3-vG2) / context->R1 + (DSS_INVERTER_OSC__MOD-vG2) / context->R2 + (vI+0.7-vG2)/context->Rp);
+ }
+ diff = vMix - context->vCap;
+ diff = diff - diff * exp(-discrete_current_context->sample_time/(context->C * rMix));
+ break;
+ default:
+ fatalerror("DISCRETE_INVERTER_OSC - Wrong type on NODE_%02d", node->node - NODE_00);
+ }
+ context->vCap += diff;
+ context->vG2_old = vG2;
+ if ((info->options & DISC_OSC_INVERTER_TYPE_MASK)==DISC_OSC_INVERTER_IS_TYPE3)
+ node->output = vG1;
+ else
+ node->output = vG3;
+ if (info->options & DISC_OSC_INVERTER_OUT_IS_LOGIC)
+ node->output = (node->output > info->vInFall);
+}
+
+void dss_inverter_osc_reset(node_description *node)
+{
+ struct dss_inverter_osc_context *context = node->context;
+ const discrete_inverter_osc_desc *info = node->custom;
+
+ /* exponent */
+ context->w = -1.0 / (DSS_INVERTER_OSC__RC * DSS_INVERTER_OSC__C);
+ context->wc = -1.0 / ((DSS_INVERTER_OSC__RC * DSS_INVERTER_OSC__RP) / (DSS_INVERTER_OSC__RP + DSS_INVERTER_OSC__RC) * DSS_INVERTER_OSC__C);
+ node->output = 0;
+ context->vCap = 0;
+ context->vG2_old = 0;
+ context->Rp = DSS_INVERTER_OSC__RP;
+ context->R1 = DSS_INVERTER_OSC__RC;
+ context->R2 = DSS_INVERTER_OSC__R2;
+ context->C = DSS_INVERTER_OSC__C;
+ context->tf_b = (log(0.0 - log(info->vOutLow/info->vB)) - log(0.0 - log((info->vOutHigh/info->vB))) ) / log(info->vInRise / info->vInFall);
+ context->tf_a = log(0.0 - log(info->vOutLow/info->vB)) - context->tf_b * log(info->vInRise/info->vB);
+ context->tf_a = exp(context->tf_a);
+}
+
+/************************************************************************
+ *
+ * DSS_TRIANGLEWAVE - Usage of node_description values for step function
+ *
+ * input0 - Enable input value
+ * input1 - Frequency input value
+ * input2 - Amplitde input value
+ * input3 - DC Bias value
+ * input4 - Initial Phase
+ *
+ ************************************************************************/
+#define DSS_TRIANGLEWAVE__ENABLE (*(node->input[0]))
+#define DSS_TRIANGLEWAVE__FREQ (*(node->input[1]))
+#define DSS_TRIANGLEWAVE__AMP (*(node->input[2]))
+#define DSS_TRIANGLEWAVE__BIAS (*(node->input[3]))
+#define DSS_TRIANGLEWAVE__PHASE (*(node->input[4]))
+
+void dss_trianglewave_step(node_description *node)
+{
+ struct dss_trianglewave_context *context = node->context;
+
+ if(DSS_TRIANGLEWAVE__ENABLE)
+ {
+ node->output=context->phase < M_PI ? (DSS_TRIANGLEWAVE__AMP * (context->phase / (M_PI/2.0) - 1.0))/2.0 :
+ (DSS_TRIANGLEWAVE__AMP * (3.0 - context->phase / (M_PI/2.0)))/2.0 ;
+
+ /* Add DC Bias component */
+ node->output=node->output+DSS_TRIANGLEWAVE__BIAS;
+ }
+ else
+ {
+ node->output=0;
+ }
+
+ /* Work out the phase step based on phase/freq & sample rate */
+ /* The enable input only curtails output, phase rotation */
+ /* still occurs */
+ /* phase step = 2Pi/(output period/sample period) */
+ /* boils out to */
+ /* phase step = (2Pi*output freq)/sample freq) */
+ /* Also keep the new phasor in the 2Pi range. */
+ context->phase=fmod((context->phase+((2.0*M_PI*DSS_TRIANGLEWAVE__FREQ)/discrete_current_context->sample_rate)),2.0*M_PI);
+}
+
+void dss_trianglewave_reset(node_description *node)
+{
+ struct dss_trianglewave_context *context = node->context;
+ double start;
+
+ /* Establish starting phase, convert from degrees to radians */
+ start=(DSS_TRIANGLEWAVE__PHASE/360.0)*(2.0*M_PI);
+ /* Make sure its always mod 2Pi */
+ context->phase=fmod(start,2.0*M_PI);
+
+ /* Step to set the output */
+ dss_trianglewave_step(node);
+}
+
+
+/************************************************************************
+ *
+ * DSS_ADSR - Attack Decay Sustain Release
+ *
+ * input0 - Enable input value
+ * input1 - Trigger value
+ * input2 - gain scaling factor
+ *
+ ************************************************************************/
+#define DSS_ADSR__ENABLE (*(node->input[0]))
+
+void dss_adsrenv_step(node_description *node)
+{
+// struct dss_adsr_context *context = node->context;
+
+ if(DSS_ADSR__ENABLE)
+ {
+ node->output=0;
+ }
+ else
+ {
+ node->output=0;
+ }
+}
+
+
+void dss_adsrenv_reset(node_description *node)
+{
+ dss_adsrenv_step(node);
+}