mirror of
https://github.com/holub/mame
synced 2025-10-04 16:34:53 +03:00
2060 lines
49 KiB
C++
2060 lines
49 KiB
C++
// license:BSD-3-Clause
|
|
// copyright-holders:Zsolt Vasvari
|
|
// thanks-to:Derrick Renaud
|
|
/*****************************************************************************
|
|
|
|
Texas Instruments SN76477 emulator
|
|
|
|
authors: Derrick Renaud - info
|
|
Zsolt Vasvari - software
|
|
|
|
(see sn76477.h for details)
|
|
|
|
Notes:
|
|
* All formulas were derived by taking measurements of a real device,
|
|
then running the data sets through the numerical analysis
|
|
application at http://zunzun.com to come up with the functions.
|
|
|
|
Known issues/to-do's:
|
|
* Use RES_INF for unconnected resistor pins and treat 0 as a short
|
|
circuit
|
|
|
|
* VCO
|
|
* confirm value of VCO_MAX_EXT_VOLTAGE, VCO_TO_SLF_VOLTAGE_DIFF
|
|
VCO_CAP_VOLTAGE_MIN and VCO_CAP_VOLTAGE_MAX
|
|
* confirm value of VCO_MIN_DUTY_CYCLE
|
|
* get real formulas for VCO cap charging and discharging
|
|
* get real formula for VCO duty cycle
|
|
* what happens if no vco_res
|
|
* what happens if no vco_cap (needed for laserbat/lazarian)
|
|
|
|
* Attack/Decay
|
|
* get real formulas for a/d cap charging and discharging
|
|
|
|
* Output
|
|
* what happens if output is taken at pin 12 with no feedback_res
|
|
(needed for laserbat/lazarian)
|
|
|
|
*****************************************************************************/
|
|
|
|
#include "emu.h"
|
|
#include "sn76477.h"
|
|
|
|
#include "wavwrite.h"
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* Debugging
|
|
*
|
|
*****************************************************************************/
|
|
|
|
#define VERBOSE 1
|
|
|
|
/* if 1, create a .wav file of the sound produced */
|
|
#define LOG_WAV 0
|
|
|
|
/* if 1 and LOG_WAV is 1, only logs to .wav file in chip is enabled */
|
|
#define LOG_WAV_ENABLED_ONLY 0
|
|
|
|
/* determines what value will be logged in the left channel of the .wav file */
|
|
#define LOG_WAV_VALUE_L 0 /* 0 = OUT voltage */
|
|
/* 1 = enable line */
|
|
/* 2 = one-shot cap voltage */
|
|
/* 3 = a/d cap voltage */
|
|
/* 4 = slf cap voltage */
|
|
/* 5 = vco cap voltage */
|
|
/* 6 = noise filter cap voltage */
|
|
|
|
/* determines what value will be logged in the right channel of the .wav file
|
|
same values as for the left channel above */
|
|
#define LOG_WAV_VALUE_R 3
|
|
|
|
#define LOG_WAV_GAIN_FACTOR 1000
|
|
|
|
#define LOG_WAV_FILE_NAME "sn76477_%s.wav"
|
|
|
|
|
|
#define LOG(n,...) do { if (VERBOSE >= (n)) logerror(__VA_ARGS__); } while (0)
|
|
|
|
#define CHECK_BOOLEAN assert((state & 0x01) == state)
|
|
#define CHECK_POSITIVE assert(data >= 0.0)
|
|
#define CHECK_VOLTAGE assert((data >= 0.0) && (data <= 5.0))
|
|
#define CHECK_CAP_VOLTAGE assert(((data >= 0.0) && (data <= 5.0)) || (data == EXTERNAL_VOLTAGE_DISCONNECT))
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* Constants
|
|
*
|
|
*****************************************************************************/
|
|
|
|
#define ONE_SHOT_CAP_VOLTAGE_MIN (0) /* the voltage at which the one-shot starts from (measured) */
|
|
#define ONE_SHOT_CAP_VOLTAGE_MAX (2.5) /* the voltage at which the one-shot finishes (measured) */
|
|
#define ONE_SHOT_CAP_VOLTAGE_RANGE (ONE_SHOT_CAP_VOLTAGE_MAX - ONE_SHOT_CAP_VOLTAGE_MIN)
|
|
|
|
#define SLF_CAP_VOLTAGE_MIN (0.33) /* the voltage at the bottom peak of the SLF triangle wave (measured) */
|
|
#define SLF_CAP_VOLTAGE_MAX (2.37) /* the voltage at the top peak of the SLF triangle wave (measured) */
|
|
#define SLF_CAP_VOLTAGE_RANGE (SLF_CAP_VOLTAGE_MAX - SLF_CAP_VOLTAGE_MIN)
|
|
|
|
#define VCO_MAX_EXT_VOLTAGE (2.35) /* the external voltage at which the VCO saturates and produces no output,
|
|
also used as the voltage threshold for the SLF */
|
|
#define VCO_TO_SLF_VOLTAGE_DIFF (0.35)
|
|
#define VCO_CAP_VOLTAGE_MIN (SLF_CAP_VOLTAGE_MIN) /* the voltage at the bottom peak of the VCO triangle wave */
|
|
#define VCO_CAP_VOLTAGE_MAX (SLF_CAP_VOLTAGE_MAX + VCO_TO_SLF_VOLTAGE_DIFF) /* the voltage at the bottom peak of the VCO triangle wave */
|
|
#define VCO_CAP_VOLTAGE_RANGE (VCO_CAP_VOLTAGE_MAX - VCO_CAP_VOLTAGE_MIN)
|
|
#define VCO_DUTY_CYCLE_50 (5.0) /* the high voltage that produces a 50% duty cycle */
|
|
#define VCO_MIN_DUTY_CYCLE (18) /* the smallest possible duty cycle, in % */
|
|
|
|
#define NOISE_MIN_CLOCK_RES RES_K(10) /* the maximum resistor value that still produces a noise (measured) */
|
|
#define NOISE_MAX_CLOCK_RES RES_M(3.3) /* the minimum resistor value that still produces a noise (measured) */
|
|
#define NOISE_CAP_VOLTAGE_MIN (0) /* the minimum voltage that the noise filter cap can hold (measured) */
|
|
#define NOISE_CAP_VOLTAGE_MAX (5.0) /* the maximum voltage that the noise filter cap can hold (measured) */
|
|
#define NOISE_CAP_VOLTAGE_RANGE (NOISE_CAP_VOLTAGE_MAX - NOISE_CAP_VOLTAGE_MIN)
|
|
#define NOISE_CAP_HIGH_THRESHOLD (3.35) /* the voltage at which the filtered noise bit goes to 0 (measured) */
|
|
#define NOISE_CAP_LOW_THRESHOLD (0.74) /* the voltage at which the filtered noise bit goes to 1 (measured) */
|
|
|
|
#define AD_CAP_VOLTAGE_MIN (0) /* the minimum voltage the attack/decay cap can hold (measured) */
|
|
#define AD_CAP_VOLTAGE_MAX (4.44) /* the minimum voltage the attack/decay cap can hold (measured) */
|
|
#define AD_CAP_VOLTAGE_RANGE (AD_CAP_VOLTAGE_MAX - AD_CAP_VOLTAGE_MIN)
|
|
|
|
#define OUT_CENTER_LEVEL_VOLTAGE (2.57) /* the voltage that gets outputted when the volumne is 0 (measured) */
|
|
#define OUT_HIGH_CLIP_THRESHOLD (3.51) /* the maximum voltage that can be put out (measured) */
|
|
#define OUT_LOW_CLIP_THRESHOLD (0.715) /* the minimum voltage that can be put out (measured) */
|
|
|
|
/* gain factors for OUT voltage in 0.1V increments (measured) */
|
|
static constexpr double out_pos_gain[] =
|
|
{
|
|
0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.01, /* 0.0 - 0.9V */
|
|
0.03, 0.11, 0.15, 0.19, 0.21, 0.23, 0.26, 0.29, 0.31, 0.33, /* 1.0 - 1.9V */
|
|
0.36, 0.38, 0.41, 0.43, 0.46, 0.49, 0.52, 0.54, 0.57, 0.60, /* 2.0 - 2.9V */
|
|
0.62, 0.65, 0.68, 0.70, 0.73, 0.76, 0.80, 0.82, 0.84, 0.87, /* 3.0 - 3.9V */
|
|
0.90, 0.93, 0.96, 0.98, 1.00 /* 4.0 - 4.4V */
|
|
};
|
|
|
|
static constexpr double out_neg_gain[] =
|
|
{
|
|
0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, -0.01, /* 0.0 - 0.9V */
|
|
-0.02, -0.09, -0.13, -0.15, -0.17, -0.19, -0.22, -0.24, -0.26, -0.28, /* 1.0 - 1.9V */
|
|
-0.30, -0.32, -0.34, -0.37, -0.39, -0.41, -0.44, -0.46, -0.48, -0.51, /* 2.0 - 2.9V */
|
|
-0.53, -0.56, -0.58, -0.60, -0.62, -0.65, -0.67, -0.69, -0.72, -0.74, /* 3.0 - 3.9V */
|
|
-0.76, -0.78, -0.81, -0.84, -0.85 /* 4.0 - 4.4V */
|
|
};
|
|
|
|
|
|
DEFINE_DEVICE_TYPE(SN76477, sn76477_device, "sn76477", "TI SN76477 CSG")
|
|
|
|
sn76477_device::sn76477_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
|
|
: device_t(mconfig, SN76477, tag, owner, clock),
|
|
device_sound_interface(mconfig, *this),
|
|
m_enable(0),
|
|
m_envelope_mode(0),
|
|
m_vco_mode(0),
|
|
m_mixer_mode(0),
|
|
m_one_shot_res(RES_INF),
|
|
m_one_shot_cap(0),
|
|
m_one_shot_cap_voltage_ext(0),
|
|
m_slf_res(RES_INF),
|
|
m_slf_cap(0),
|
|
m_slf_cap_voltage_ext(0),
|
|
m_vco_voltage(0),
|
|
m_vco_res(RES_INF),
|
|
m_vco_cap(0),
|
|
m_vco_cap_voltage_ext(0),
|
|
m_noise_clock_res(RES_INF),
|
|
m_noise_clock_ext(0),
|
|
m_noise_clock(0),
|
|
m_noise_filter_res(RES_INF),
|
|
m_noise_filter_cap(0),
|
|
m_noise_filter_cap_voltage_ext(0),
|
|
m_attack_res(RES_INF),
|
|
m_decay_res(RES_INF),
|
|
m_attack_decay_cap(0),
|
|
m_attack_decay_cap_voltage_ext(0),
|
|
m_amplitude_res(RES_INF),
|
|
m_feedback_res(RES_INF),
|
|
m_pitch_voltage(0),
|
|
m_one_shot_cap_voltage(0),
|
|
m_one_shot_running_ff(0),
|
|
m_slf_cap_voltage(0),
|
|
m_slf_out_ff(0),
|
|
m_vco_cap_voltage(0),
|
|
m_vco_out_ff(0),
|
|
m_vco_alt_pos_edge_ff(0),
|
|
m_noise_filter_cap_voltage(0),
|
|
m_real_noise_bit_ff(0),
|
|
m_filtered_noise_bit_ff(0),
|
|
m_noise_gen_count(0),
|
|
m_attack_decay_cap_voltage(0),
|
|
m_rng(0),
|
|
m_mixer_a(0),
|
|
m_mixer_b(0),
|
|
m_mixer_c(0),
|
|
m_envelope_1(0),
|
|
m_envelope_2(0),
|
|
m_channel(nullptr),
|
|
m_our_sample_rate(0),
|
|
m_file(nullptr)
|
|
{
|
|
}
|
|
|
|
//-------------------------------------------------
|
|
// device_start - device-specific startup
|
|
//-------------------------------------------------
|
|
|
|
void sn76477_device::device_start()
|
|
{
|
|
m_channel = machine().sound().stream_alloc(*this, 0, 1, machine().sample_rate());
|
|
|
|
if (clock() > 0)
|
|
{
|
|
m_our_sample_rate = clock();
|
|
}
|
|
else
|
|
{
|
|
m_our_sample_rate = machine().sample_rate();
|
|
}
|
|
|
|
intialize_noise();
|
|
|
|
// set up mixer and envelope modes, based on interface values
|
|
mixer_a_w(m_mixer_a);
|
|
mixer_b_w(m_mixer_b);
|
|
mixer_c_w(m_mixer_c);
|
|
envelope_1_w(m_envelope_1);
|
|
envelope_2_w(m_envelope_2);
|
|
|
|
m_one_shot_cap_voltage = ONE_SHOT_CAP_VOLTAGE_MIN;
|
|
m_slf_cap_voltage = SLF_CAP_VOLTAGE_MIN;
|
|
m_vco_cap_voltage = VCO_CAP_VOLTAGE_MIN;
|
|
m_noise_filter_cap_voltage = NOISE_CAP_VOLTAGE_MIN;
|
|
m_attack_decay_cap_voltage = AD_CAP_VOLTAGE_MIN;
|
|
|
|
state_save_register();
|
|
|
|
log_complete_state();
|
|
|
|
if (LOG_WAV)
|
|
open_wav_file();
|
|
}
|
|
|
|
//-------------------------------------------------
|
|
// device_stop - device-specific stop
|
|
//-------------------------------------------------
|
|
|
|
void sn76477_device::device_stop()
|
|
{
|
|
if (LOG_WAV)
|
|
close_wav_file();
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* Max/min
|
|
*
|
|
*****************************************************************************/
|
|
|
|
#undef max
|
|
#undef min
|
|
|
|
static inline double max(double a, double b)
|
|
{
|
|
return (a > b) ? a : b;
|
|
}
|
|
|
|
|
|
static inline double min(double a, double b)
|
|
{
|
|
return (a < b) ? a : b;
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* Functions for computing frequencies, voltages and similar values based
|
|
* on the hardware itself. Do NOT put anything emulation specific here,
|
|
* such as calculations based on sample_rate.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
double sn76477_device::compute_one_shot_cap_charging_rate() /* in V/sec */
|
|
{
|
|
/* this formula was derived using the data points below
|
|
|
|
Res (kohms) Cap (uF) Time (millisec)
|
|
47 0.33 11.84
|
|
47 1.0 36.2
|
|
47 1.5 52.1
|
|
47 2.0 76.4
|
|
100 0.33 24.4
|
|
100 1.0 75.2
|
|
100 1.5 108.5
|
|
100 2.0 158.4
|
|
*/
|
|
|
|
double ret = 0;
|
|
|
|
if ((m_one_shot_res > 0) && (m_one_shot_cap > 0))
|
|
{
|
|
ret = ONE_SHOT_CAP_VOLTAGE_RANGE / (0.8024 * m_one_shot_res * m_one_shot_cap + 0.002079);
|
|
}
|
|
else if (m_one_shot_cap > 0)
|
|
{
|
|
/* if no resistor, there is no current to charge the cap,
|
|
effectively making the one-shot time effectively infinite */
|
|
ret = +1e-30;
|
|
}
|
|
else if (m_one_shot_res > 0)
|
|
{
|
|
/* if no cap, the voltage changes extremely fast,
|
|
effectively making the one-shot time 0 */
|
|
ret = +1e+30;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
double sn76477_device::compute_one_shot_cap_discharging_rate() /* in V/sec */
|
|
{
|
|
/* this formula was derived using the data points below
|
|
|
|
Cap (uF) Time (microsec)
|
|
0.33 300
|
|
1.0 850
|
|
1.5 1300
|
|
2.0 1900
|
|
*/
|
|
|
|
double ret = 0;
|
|
|
|
if ((m_one_shot_res > 0) && (m_one_shot_cap > 0))
|
|
{
|
|
ret = ONE_SHOT_CAP_VOLTAGE_RANGE / (854.7 * m_one_shot_cap + 0.00001795);
|
|
}
|
|
else if (m_one_shot_res > 0)
|
|
{
|
|
/* if no cap, the voltage changes extremely fast,
|
|
effectively making the one-shot time 0 */
|
|
ret = +1e+30;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
double sn76477_device::compute_slf_cap_charging_rate() /* in V/sec */
|
|
{
|
|
/* this formula was derived using the data points below
|
|
|
|
Res (kohms) Cap (uF) Time (millisec)
|
|
47 0.47 14.3
|
|
120 0.47 35.6
|
|
200 0.47 59.2
|
|
47 1.00 28.6
|
|
120 1.00 71.6
|
|
200 1.00 119.0
|
|
*/
|
|
double ret = 0;
|
|
|
|
if ((m_slf_res > 0) && (m_slf_cap > 0))
|
|
{
|
|
ret = SLF_CAP_VOLTAGE_RANGE / (0.5885 * m_slf_res * m_slf_cap + 0.001300);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
double sn76477_device::compute_slf_cap_discharging_rate() /* in V/sec */
|
|
{
|
|
/* this formula was derived using the data points below
|
|
|
|
Res (kohms) Cap (uF) Time (millisec)
|
|
47 0.47 13.32
|
|
120 0.47 32.92
|
|
200 0.47 54.4
|
|
47 1.00 26.68
|
|
120 1.00 66.2
|
|
200 1.00 109.6
|
|
*/
|
|
double ret = 0;
|
|
|
|
if ((m_slf_res > 0) && (m_slf_cap > 0))
|
|
{
|
|
ret = SLF_CAP_VOLTAGE_RANGE / (0.5413 * m_slf_res * m_slf_cap + 0.001343);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
double sn76477_device::compute_vco_cap_charging_discharging_rate() /* in V/sec */
|
|
{
|
|
double ret = 0;
|
|
|
|
if ((m_vco_res > 0) && (m_vco_cap > 0))
|
|
{
|
|
ret = 0.64 * 2 * VCO_CAP_VOLTAGE_RANGE / (m_vco_res * m_vco_cap);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
double sn76477_device::compute_vco_duty_cycle() /* no measure, just a number */
|
|
{
|
|
double ret = 0.5; /* 50% */
|
|
|
|
if ((m_vco_voltage > 0) && (m_pitch_voltage != VCO_DUTY_CYCLE_50))
|
|
{
|
|
ret = max(0.5 * (m_pitch_voltage / m_vco_voltage), (VCO_MIN_DUTY_CYCLE / 100.0));
|
|
|
|
ret = min(ret, 1);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
uint32_t sn76477_device::compute_noise_gen_freq() /* in Hz */
|
|
{
|
|
/* this formula was derived using the data points below
|
|
|
|
Res (ohms) Freq (Hz)
|
|
10k 97493
|
|
12k 83333
|
|
15k 68493
|
|
22k 49164
|
|
27k 41166
|
|
33k 34449
|
|
36k 31969
|
|
47k 25126
|
|
56k 21322
|
|
68k 17721.5
|
|
82k 15089.2
|
|
100k 12712.0
|
|
150k 8746.4
|
|
220k 6122.4
|
|
270k 5101.5
|
|
330k 4217.2
|
|
390k 3614.5
|
|
470k 3081.7
|
|
680k 2132.7
|
|
820k 1801.8
|
|
1M 1459.9
|
|
2.2M 705.13
|
|
3.3M 487.59
|
|
*/
|
|
|
|
uint32_t ret = 0;
|
|
|
|
if ((m_noise_clock_res >= NOISE_MIN_CLOCK_RES) &&
|
|
(m_noise_clock_res <= NOISE_MAX_CLOCK_RES))
|
|
{
|
|
ret = 339100000 * pow(m_noise_clock_res, -0.8849);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
double sn76477_device::compute_noise_filter_cap_charging_rate() /* in V/sec */
|
|
{
|
|
/* this formula was derived using the data points below
|
|
|
|
R*C Time (sec)
|
|
.000068 .0000184
|
|
.0001496 .0000378
|
|
.0002244 .0000548
|
|
.0003196 .000077
|
|
.0015 .000248
|
|
.0033 .000540
|
|
.00495 .000792
|
|
.00705 .001096
|
|
*/
|
|
|
|
double ret = 0;
|
|
|
|
if ((m_noise_filter_res > 0) && (m_noise_filter_cap > 0))
|
|
{
|
|
ret = NOISE_CAP_VOLTAGE_RANGE / (0.1571 * m_noise_filter_res * m_noise_filter_cap + 0.00001430);
|
|
}
|
|
else if (m_noise_filter_cap > 0)
|
|
{
|
|
/* if no resistor, there is no current to charge the cap,
|
|
effectively making the filter's output constants */
|
|
ret = +1e-30;
|
|
}
|
|
else if (m_noise_filter_res > 0)
|
|
{
|
|
/* if no cap, the voltage changes extremely fast,
|
|
effectively disabling the filter */
|
|
ret = +1e+30;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
double sn76477_device::compute_noise_filter_cap_discharging_rate() /* in V/sec */
|
|
{
|
|
/* this formula was derived using the data points below
|
|
|
|
R*C Time (sec)
|
|
.000068 .000016
|
|
.0001496 .0000322
|
|
.0002244 .0000472
|
|
.0003196 .0000654
|
|
.0015 .000219
|
|
.0033 .000468
|
|
.00495 .000676
|
|
.00705 .000948
|
|
*/
|
|
|
|
double ret = 0;
|
|
|
|
if ((m_noise_filter_res > 0) && (m_noise_filter_cap > 0))
|
|
{
|
|
ret = NOISE_CAP_VOLTAGE_RANGE / (0.1331 * m_noise_filter_res * m_noise_filter_cap + 0.00001734);
|
|
}
|
|
else if (m_noise_filter_cap > 0)
|
|
{
|
|
/* if no resistor, there is no current to charge the cap,
|
|
effectively making the filter's output constants */
|
|
ret = +1e-30;
|
|
}
|
|
else if (m_noise_filter_res > 0)
|
|
{
|
|
/* if no cap, the voltage changes extremely fast,
|
|
effectively disabling the filter */
|
|
ret = +1e+30;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
double sn76477_device::compute_attack_decay_cap_charging_rate() /* in V/sec */
|
|
{
|
|
double ret = 0;
|
|
|
|
if ((m_attack_res > 0) && (m_attack_decay_cap > 0))
|
|
{
|
|
ret = AD_CAP_VOLTAGE_RANGE / (m_attack_res * m_attack_decay_cap);
|
|
}
|
|
else if (m_attack_decay_cap > 0)
|
|
{
|
|
/* if no resistor, there is no current to charge the cap,
|
|
effectively making the attack time infinite */
|
|
ret = +1e-30;
|
|
}
|
|
else if (m_attack_res > 0)
|
|
{
|
|
/* if no cap, the voltage changes extremely fast,
|
|
effectively making the attack time 0 */
|
|
ret = +1e+30;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
double sn76477_device::compute_attack_decay_cap_discharging_rate() /* in V/sec */
|
|
{
|
|
double ret = 0;
|
|
|
|
if ((m_decay_res > 0) && (m_attack_decay_cap > 0))
|
|
{
|
|
ret = AD_CAP_VOLTAGE_RANGE / (m_decay_res * m_attack_decay_cap);
|
|
}
|
|
else if (m_attack_decay_cap > 0)
|
|
{
|
|
/* if no resistor, there is no current to charge the cap,
|
|
effectively making the decay time infinite */
|
|
ret = +1e-30;
|
|
}
|
|
else if (m_attack_res > 0)
|
|
{
|
|
/* if no cap, the voltage changes extremely fast,
|
|
effectively making the decay time 0 */
|
|
ret = +1e+30;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
double sn76477_device::compute_center_to_peak_voltage_out()
|
|
{
|
|
/* this formula was derived using the data points below
|
|
|
|
Ra (kohms) Rf (kohms) Voltage
|
|
150 47 1.28
|
|
200 47 0.96
|
|
47 22 1.8
|
|
100 22 0.87
|
|
150 22 0.6
|
|
200 22 0.45
|
|
47 10 0.81
|
|
100 10 0.4
|
|
150 10 0.27
|
|
*/
|
|
|
|
double ret = 0;
|
|
|
|
if (m_amplitude_res > 0)
|
|
{
|
|
ret = 3.818 * (m_feedback_res / m_amplitude_res) + 0.03;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* Logging functions
|
|
*
|
|
*****************************************************************************/
|
|
|
|
void sn76477_device::log_enable_line()
|
|
{
|
|
static const char *const desc[] =
|
|
{
|
|
"Enabled", "Inhibited"
|
|
};
|
|
|
|
LOG(1, "SN76477: Enable line (9): %d [%s]\n", m_enable, desc[m_enable]);
|
|
}
|
|
|
|
|
|
void sn76477_device::log_mixer_mode()
|
|
{
|
|
static const char *const desc[] =
|
|
{
|
|
"VCO", "SLF", "Noise", "VCO/Noise",
|
|
"SLF/Noise", "SLF/VCO/Noise", "SLF/VCO", "Inhibit"
|
|
};
|
|
|
|
LOG(1, "SN76477: Mixer mode (25-27): %d [%s]\n", m_mixer_mode, desc[m_mixer_mode]);
|
|
}
|
|
|
|
|
|
void sn76477_device::log_envelope_mode()
|
|
{
|
|
static const char *const desc[] =
|
|
{
|
|
"VCO", "One-Shot", "Mixer Only", "VCO with Alternating Polarity"
|
|
};
|
|
|
|
LOG(1, "SN76477: Envelope mode (1,28): %d [%s]\n", m_envelope_mode, desc[m_envelope_mode]);
|
|
}
|
|
|
|
|
|
void sn76477_device::log_vco_mode()
|
|
{
|
|
static const char *const desc[] =
|
|
{
|
|
"External (Pin 16)", "Internal (SLF)"
|
|
};
|
|
|
|
LOG(1, "SN76477: VCO mode (22): %d [%s]\n", m_vco_mode, desc[m_vco_mode]);
|
|
}
|
|
|
|
|
|
void sn76477_device::log_one_shot_time()
|
|
{
|
|
if (!m_one_shot_cap_voltage_ext)
|
|
{
|
|
if (compute_one_shot_cap_charging_rate() > 0)
|
|
{
|
|
LOG(1, "SN76477: One-shot time (23,24): %.4f sec\n", ONE_SHOT_CAP_VOLTAGE_RANGE * (1 / compute_one_shot_cap_charging_rate()));
|
|
}
|
|
else
|
|
{
|
|
LOG(1, "SN76477: One-shot time (23,24): N/A\n");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LOG(1, "SN76477: One-shot time (23,24): External (cap = %.2fV)\n", m_one_shot_cap_voltage);
|
|
}
|
|
}
|
|
|
|
|
|
void sn76477_device::log_slf_freq()
|
|
{
|
|
if (!m_slf_cap_voltage_ext)
|
|
{
|
|
if (compute_slf_cap_charging_rate() > 0)
|
|
{
|
|
double charging_time = (1 / compute_slf_cap_charging_rate()) * SLF_CAP_VOLTAGE_RANGE;
|
|
double discharging_time = (1 / compute_slf_cap_discharging_rate()) * SLF_CAP_VOLTAGE_RANGE;
|
|
|
|
LOG(1, "SN76477: SLF frequency (20,21): %.2f Hz\n", 1 / (charging_time + discharging_time));
|
|
}
|
|
else
|
|
{
|
|
LOG(1, "SN76477: SLF frequency (20,21): N/A\n");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LOG(1, "SN76477: SLF frequency (20,21): External (cap = %.2fV)\n", m_slf_cap_voltage);
|
|
}
|
|
}
|
|
|
|
|
|
void sn76477_device::log_vco_pitch_voltage()
|
|
{
|
|
LOG(1, "SN76477: VCO pitch voltage (19): %.2fV\n", m_pitch_voltage);
|
|
}
|
|
|
|
|
|
void sn76477_device::log_vco_duty_cycle()
|
|
{
|
|
LOG(1, "SN76477: VCO duty cycle (16,19): %.0f%%\n", compute_vco_duty_cycle() * 100.0);
|
|
}
|
|
|
|
|
|
void sn76477_device::log_vco_freq()
|
|
{
|
|
if (!m_vco_cap_voltage_ext)
|
|
{
|
|
if (compute_vco_cap_charging_discharging_rate() > 0)
|
|
{
|
|
double min_freq = compute_vco_cap_charging_discharging_rate() / (2 * VCO_CAP_VOLTAGE_RANGE);
|
|
double max_freq = compute_vco_cap_charging_discharging_rate() / (2 * VCO_TO_SLF_VOLTAGE_DIFF);
|
|
|
|
LOG(1, "SN76477: VCO frequency (17,18): %.2f Hz - %.1f Hz\n", min_freq, max_freq);
|
|
}
|
|
else
|
|
{
|
|
LOG(1, "SN76477: VCO frequency (17,18): N/A\n");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LOG(1, "SN76477: VCO frequency (17,18): External (cap = %.2fV)\n", m_vco_cap_voltage);
|
|
}
|
|
}
|
|
|
|
|
|
void sn76477_device::log_vco_ext_voltage()
|
|
{
|
|
if (m_vco_voltage <= VCO_MAX_EXT_VOLTAGE)
|
|
{
|
|
double min_freq = compute_vco_cap_charging_discharging_rate() / (2 * VCO_CAP_VOLTAGE_RANGE);
|
|
double max_freq = compute_vco_cap_charging_discharging_rate() / (2 * VCO_TO_SLF_VOLTAGE_DIFF);
|
|
|
|
LOG(1, "SN76477: VCO ext. voltage (16): %.2fV (%.2f Hz)\n",
|
|
m_vco_voltage,
|
|
min_freq + ((max_freq - min_freq) * m_vco_voltage / VCO_MAX_EXT_VOLTAGE));
|
|
}
|
|
else
|
|
{
|
|
LOG(1, "SN76477: VCO ext. voltage (16): %.2fV (saturated, no output)\n", m_vco_voltage);
|
|
}
|
|
}
|
|
|
|
|
|
void sn76477_device::log_noise_gen_freq()
|
|
{
|
|
if (m_noise_clock_ext)
|
|
{
|
|
LOG(1, "SN76477: Noise gen frequency (4): External\n");
|
|
}
|
|
else
|
|
{
|
|
if (compute_noise_gen_freq() > 0)
|
|
{
|
|
LOG(1, "SN76477: Noise gen frequency (4): %d Hz\n", compute_noise_gen_freq());
|
|
}
|
|
else
|
|
{
|
|
LOG(1, "SN76477: Noise gen frequency (4): N/A\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void sn76477_device::log_noise_filter_freq()
|
|
{
|
|
if (!m_noise_filter_cap_voltage_ext)
|
|
{
|
|
double charging_rate = compute_noise_filter_cap_charging_rate();
|
|
|
|
if (charging_rate > 0)
|
|
{
|
|
if (charging_rate < 1000000.0)
|
|
{
|
|
double charging_time = (1 / charging_rate) * NOISE_CAP_VOLTAGE_RANGE;
|
|
double discharging_time = (1 / charging_rate) * NOISE_CAP_VOLTAGE_RANGE;
|
|
|
|
LOG(1, "SN76477: Noise filter frequency (5,6): %.0f Hz\n", 1 / (charging_time + discharging_time));
|
|
}
|
|
else
|
|
{
|
|
LOG(1, "SN76477: Noise filter frequency (5,6): Very Large (Filtering Disabled)\n");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LOG(1, "SN76477: Noise filter frequency (5,6): N/A\n");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LOG(1, "SN76477: Noise filter frequency (5,6): External (cap = %.2fV)\n", m_noise_filter_cap);
|
|
}
|
|
}
|
|
|
|
|
|
void sn76477_device::log_attack_time()
|
|
{
|
|
if (!m_attack_decay_cap_voltage_ext)
|
|
{
|
|
if (compute_attack_decay_cap_charging_rate() > 0)
|
|
{
|
|
LOG(1, "SN76477: Attack time (8,10): %.4f sec\n", AD_CAP_VOLTAGE_RANGE * (1 / compute_attack_decay_cap_charging_rate()));
|
|
}
|
|
else
|
|
{
|
|
LOG(1, "SN76477: Attack time (8,10): N/A\n");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LOG(1, "SN76477: Attack time (8,10): External (cap = %.2fV)\n", m_attack_decay_cap_voltage);
|
|
}
|
|
}
|
|
|
|
|
|
void sn76477_device::log_decay_time()
|
|
{
|
|
if (!m_attack_decay_cap_voltage_ext)
|
|
{
|
|
if (compute_attack_decay_cap_discharging_rate() > 0)
|
|
{
|
|
LOG(1, "SN76477: Decay time (7,8): %.4f sec\n", AD_CAP_VOLTAGE_RANGE * (1 / compute_attack_decay_cap_discharging_rate()));
|
|
}
|
|
else
|
|
{
|
|
LOG(1, "SN76477: Decay time (8,10): N/A\n");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LOG(1, "SN76477: Decay time (7, 8): External (cap = %.2fV)\n", m_attack_decay_cap_voltage);
|
|
}
|
|
}
|
|
|
|
|
|
void sn76477_device::log_voltage_out()
|
|
{
|
|
LOG(1, "SN76477: Voltage OUT range (11,12): %.2fV - %.2fV (clips above %.2fV)\n",
|
|
OUT_CENTER_LEVEL_VOLTAGE + compute_center_to_peak_voltage_out() * out_neg_gain[(int)(AD_CAP_VOLTAGE_MAX * 10)],
|
|
OUT_CENTER_LEVEL_VOLTAGE + compute_center_to_peak_voltage_out() * out_pos_gain[(int)(AD_CAP_VOLTAGE_MAX * 10)],
|
|
OUT_HIGH_CLIP_THRESHOLD);
|
|
}
|
|
|
|
|
|
void sn76477_device::log_complete_state()
|
|
{
|
|
log_enable_line();
|
|
log_mixer_mode();
|
|
log_envelope_mode();
|
|
log_vco_mode();
|
|
log_one_shot_time();
|
|
log_slf_freq();
|
|
log_vco_freq();
|
|
log_vco_ext_voltage();
|
|
log_vco_pitch_voltage();
|
|
log_vco_duty_cycle();
|
|
log_noise_filter_freq();
|
|
log_noise_gen_freq();
|
|
log_attack_time();
|
|
log_decay_time();
|
|
log_voltage_out();
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* .WAV file functions
|
|
*
|
|
*****************************************************************************/
|
|
|
|
|
|
void sn76477_device::open_wav_file()
|
|
{
|
|
std::string s = tag();
|
|
std::replace(s.begin(), s.end(), ':', '_');
|
|
|
|
const std::string wav_file_name = util::string_format(LOG_WAV_FILE_NAME, s).c_str();
|
|
|
|
m_file = wav_open(wav_file_name.c_str(), m_our_sample_rate, 2);
|
|
|
|
LOG(1, "SN76477: Logging output: %s\n", wav_file_name);
|
|
}
|
|
|
|
|
|
void sn76477_device::close_wav_file()
|
|
{
|
|
wav_close(m_file);
|
|
}
|
|
|
|
|
|
void sn76477_device::add_wav_data(int16_t data_l, int16_t data_r)
|
|
{
|
|
wav_add_data_16lr(m_file, &data_l, &data_r, 1);
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* Noise generator
|
|
*
|
|
*****************************************************************************/
|
|
|
|
void sn76477_device::intialize_noise()
|
|
{
|
|
m_rng = 0;
|
|
}
|
|
|
|
|
|
inline uint32_t sn76477_device::generate_next_real_noise_bit()
|
|
{
|
|
uint32_t out = ((m_rng >> 28) & 1) ^ ((m_rng >> 0) & 1);
|
|
|
|
/* if bits 0-4 and 28 are all zero then force the output to 1 */
|
|
if ((m_rng & 0x1000001f) == 0)
|
|
{
|
|
out = 1;
|
|
}
|
|
|
|
m_rng = (m_rng >> 1) | (out << 30);
|
|
|
|
return out;
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* Set enable input
|
|
*
|
|
*****************************************************************************/
|
|
|
|
WRITE_LINE_MEMBER(sn76477_device::enable_w)
|
|
{
|
|
CHECK_BOOLEAN;
|
|
|
|
if (state != m_enable)
|
|
{
|
|
m_channel->update();
|
|
|
|
m_enable = state;
|
|
|
|
/* if falling edge */
|
|
if (!m_enable)
|
|
{
|
|
/* start the attack phase */
|
|
m_attack_decay_cap_voltage = AD_CAP_VOLTAGE_MIN;
|
|
|
|
/* one-shot runs regardless of envelope mode */
|
|
m_one_shot_running_ff = 1;
|
|
}
|
|
|
|
log_enable_line();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* Set mixer select inputs
|
|
*
|
|
*****************************************************************************/
|
|
|
|
WRITE_LINE_MEMBER( sn76477_device::mixer_a_w )
|
|
{
|
|
CHECK_BOOLEAN;
|
|
|
|
if (state != ((m_mixer_mode >> 0) & 0x01))
|
|
{
|
|
m_channel->update();
|
|
|
|
m_mixer_mode = (m_mixer_mode & ~0x01) | state;
|
|
|
|
log_mixer_mode();
|
|
}
|
|
}
|
|
|
|
WRITE_LINE_MEMBER( sn76477_device::mixer_b_w )
|
|
{
|
|
CHECK_BOOLEAN;
|
|
|
|
if (state != ((m_mixer_mode >> 1) & 0x01))
|
|
{
|
|
m_channel->update();
|
|
|
|
m_mixer_mode = (m_mixer_mode & ~0x02) | (state << 1);
|
|
|
|
log_mixer_mode();
|
|
}
|
|
}
|
|
|
|
WRITE_LINE_MEMBER( sn76477_device::mixer_c_w )
|
|
{
|
|
CHECK_BOOLEAN;
|
|
|
|
if (state != ((m_mixer_mode >> 2) & 0x01))
|
|
{
|
|
m_channel->update();
|
|
|
|
m_mixer_mode = (m_mixer_mode & ~0x04) | (state << 2);
|
|
|
|
log_mixer_mode();
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* Set envelope select inputs
|
|
*
|
|
*****************************************************************************/
|
|
|
|
WRITE_LINE_MEMBER( sn76477_device::envelope_1_w )
|
|
{
|
|
CHECK_BOOLEAN;
|
|
|
|
if (state != ((m_envelope_mode >> 0) & 0x01))
|
|
{
|
|
m_channel->update();
|
|
|
|
m_envelope_mode = (m_envelope_mode & ~0x01) | state;
|
|
|
|
log_envelope_mode();
|
|
}
|
|
}
|
|
|
|
WRITE_LINE_MEMBER( sn76477_device::envelope_2_w )
|
|
{
|
|
CHECK_BOOLEAN;
|
|
|
|
if (state != ((m_envelope_mode >> 1) & 0x01))
|
|
{
|
|
m_channel->update();
|
|
|
|
m_envelope_mode = (m_envelope_mode & ~0x02) | (state << 1);
|
|
|
|
log_envelope_mode();
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* Set VCO select input
|
|
*
|
|
*****************************************************************************/
|
|
|
|
WRITE_LINE_MEMBER( sn76477_device::vco_w )
|
|
{
|
|
CHECK_BOOLEAN;
|
|
|
|
if (state != m_vco_mode)
|
|
{
|
|
m_channel->update();
|
|
|
|
m_vco_mode = state;
|
|
|
|
log_vco_mode();
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* Set one-shot resistor
|
|
*
|
|
*****************************************************************************/
|
|
|
|
void sn76477_device::one_shot_res_w(double data)
|
|
{
|
|
if (data != RES_INF)
|
|
CHECK_POSITIVE;
|
|
|
|
if (data != m_one_shot_res)
|
|
{
|
|
m_channel->update();
|
|
|
|
m_one_shot_res = data;
|
|
|
|
log_one_shot_time();
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* Set one-shot capacitor
|
|
*
|
|
*****************************************************************************/
|
|
|
|
void sn76477_device::one_shot_cap_w(double data)
|
|
{
|
|
CHECK_POSITIVE;
|
|
|
|
if (data != m_one_shot_cap)
|
|
{
|
|
m_channel->update();
|
|
|
|
m_one_shot_cap = data;
|
|
|
|
log_one_shot_time();
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* Set the voltage on the one-shot capacitor
|
|
*
|
|
*****************************************************************************/
|
|
|
|
void sn76477_device::one_shot_cap_voltage_w(double data)
|
|
{
|
|
CHECK_CAP_VOLTAGE;
|
|
|
|
if (data == EXTERNAL_VOLTAGE_DISCONNECT)
|
|
{
|
|
/* switch to internal, if not already */
|
|
if (m_one_shot_cap_voltage_ext)
|
|
{
|
|
m_channel->update();
|
|
|
|
m_one_shot_cap_voltage_ext = 0;
|
|
|
|
log_one_shot_time();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* set the voltage on the cap */
|
|
if (!m_one_shot_cap_voltage_ext || (data != m_one_shot_cap_voltage))
|
|
{
|
|
m_channel->update();
|
|
|
|
m_one_shot_cap_voltage_ext = 1;
|
|
m_one_shot_cap_voltage = data;
|
|
|
|
log_one_shot_time();
|
|
}
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* Set SLF resistor
|
|
*
|
|
*****************************************************************************/
|
|
|
|
void sn76477_device::slf_res_w(double data)
|
|
{
|
|
if (data != RES_INF)
|
|
CHECK_POSITIVE;
|
|
|
|
if (data != m_slf_res)
|
|
{
|
|
m_channel->update();
|
|
|
|
m_slf_res = data;
|
|
|
|
log_slf_freq();
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* Set SLF capacitor
|
|
*
|
|
*****************************************************************************/
|
|
|
|
void sn76477_device::slf_cap_w(double data)
|
|
{
|
|
CHECK_POSITIVE;
|
|
|
|
if (data != m_slf_cap)
|
|
{
|
|
m_channel->update();
|
|
|
|
m_slf_cap = data;
|
|
|
|
log_slf_freq();
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* Set the voltage on the SLF capacitor
|
|
*
|
|
* This is an alternate way of controlling the VCO as described in the book
|
|
*
|
|
*****************************************************************************/
|
|
|
|
void sn76477_device::slf_cap_voltage_w(double data)
|
|
{
|
|
CHECK_CAP_VOLTAGE;
|
|
|
|
if (data == EXTERNAL_VOLTAGE_DISCONNECT)
|
|
{
|
|
/* switch to internal, if not already */
|
|
if (m_slf_cap_voltage_ext)
|
|
{
|
|
m_channel->update();
|
|
|
|
m_slf_cap_voltage_ext = 0;
|
|
|
|
log_slf_freq();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* set the voltage on the cap */
|
|
if (!m_slf_cap_voltage_ext || (data != m_slf_cap_voltage))
|
|
{
|
|
m_channel->update();
|
|
|
|
m_slf_cap_voltage_ext = 1;
|
|
m_slf_cap_voltage = data;
|
|
|
|
log_slf_freq();
|
|
}
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* Set VCO resistor
|
|
*
|
|
*****************************************************************************/
|
|
|
|
void sn76477_device::vco_res_w(double data)
|
|
{
|
|
if (data != RES_INF)
|
|
CHECK_POSITIVE;
|
|
|
|
if (data != m_vco_res)
|
|
{
|
|
m_channel->update();
|
|
|
|
m_vco_res = data;
|
|
|
|
log_vco_freq();
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* Set VCO capacitor
|
|
*
|
|
*****************************************************************************/
|
|
|
|
void sn76477_device::vco_cap_w(double data)
|
|
{
|
|
CHECK_POSITIVE;
|
|
|
|
if (data != m_vco_cap)
|
|
{
|
|
m_channel->update();
|
|
|
|
m_vco_cap = data;
|
|
|
|
log_vco_freq();
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* Set the voltage on the VCO capacitor
|
|
*
|
|
*****************************************************************************/
|
|
|
|
void sn76477_device::vco_cap_voltage_w(double data)
|
|
{
|
|
CHECK_CAP_VOLTAGE;
|
|
|
|
if (data == EXTERNAL_VOLTAGE_DISCONNECT)
|
|
{
|
|
/* switch to internal, if not already */
|
|
if (m_vco_cap_voltage_ext)
|
|
{
|
|
m_channel->update();
|
|
|
|
m_vco_cap_voltage_ext = 0;
|
|
|
|
log_vco_freq();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* set the voltage on the cap */
|
|
if (!m_vco_cap_voltage_ext || (data != m_vco_cap_voltage))
|
|
{
|
|
m_channel->update();
|
|
|
|
m_vco_cap_voltage_ext = 1;
|
|
m_vco_cap_voltage = data;
|
|
|
|
log_vco_freq();
|
|
}
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* Set VCO voltage
|
|
*
|
|
*****************************************************************************/
|
|
|
|
void sn76477_device::vco_voltage_w(double data)
|
|
{
|
|
CHECK_VOLTAGE;
|
|
|
|
if (data != m_vco_voltage)
|
|
{
|
|
m_channel->update();
|
|
|
|
m_vco_voltage = data;
|
|
|
|
log_vco_ext_voltage();
|
|
log_vco_duty_cycle();
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* Set pitch voltage
|
|
*
|
|
*****************************************************************************/
|
|
|
|
void sn76477_device::pitch_voltage_w(double data)
|
|
{
|
|
CHECK_VOLTAGE;
|
|
|
|
if (data != m_pitch_voltage)
|
|
{
|
|
m_channel->update();
|
|
|
|
m_pitch_voltage = data;
|
|
|
|
log_vco_pitch_voltage();
|
|
log_vco_duty_cycle();
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* Set noise external clock
|
|
*
|
|
*****************************************************************************/
|
|
|
|
WRITE_LINE_MEMBER( sn76477_device::noise_clock_w )
|
|
{
|
|
CHECK_BOOLEAN;
|
|
|
|
if (state != m_noise_clock)
|
|
{
|
|
m_noise_clock = state;
|
|
|
|
/* on the rising edge shift generate next value,
|
|
if external control is enabled */
|
|
if (m_noise_clock && m_noise_clock_ext)
|
|
{
|
|
m_channel->update();
|
|
|
|
m_real_noise_bit_ff = generate_next_real_noise_bit();
|
|
}
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* Set noise clock resistor
|
|
*
|
|
*****************************************************************************/
|
|
|
|
void sn76477_device::noise_clock_res_w(double data)
|
|
{
|
|
if (data != RES_INF)
|
|
CHECK_POSITIVE;
|
|
|
|
if (((data == 0) && !m_noise_clock_ext) ||
|
|
((data != 0) && (data != m_noise_clock_res)))
|
|
{
|
|
m_channel->update();
|
|
|
|
if (data == 0)
|
|
{
|
|
m_noise_clock_ext = 1;
|
|
}
|
|
else
|
|
{
|
|
m_noise_clock_ext = 0;
|
|
|
|
m_noise_clock_res = data;
|
|
}
|
|
|
|
log_noise_gen_freq();
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* Set noise filter resistor
|
|
*
|
|
*****************************************************************************/
|
|
|
|
void sn76477_device::noise_filter_res_w(double data)
|
|
{
|
|
if (data != RES_INF)
|
|
CHECK_POSITIVE;
|
|
|
|
if (data != m_noise_filter_res)
|
|
{
|
|
m_channel->update();
|
|
|
|
m_noise_filter_res = data;
|
|
|
|
log_noise_filter_freq();
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* Set noise filter capacitor
|
|
*
|
|
*****************************************************************************/
|
|
|
|
void sn76477_device::noise_filter_cap_w(double data)
|
|
{
|
|
CHECK_POSITIVE;
|
|
|
|
if (data != m_noise_filter_cap)
|
|
{
|
|
m_channel->update();
|
|
|
|
m_noise_filter_cap = data;
|
|
|
|
log_noise_filter_freq();
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* Set the voltage on the noise filter capacitor
|
|
*
|
|
*****************************************************************************/
|
|
|
|
void sn76477_device::noise_filter_cap_voltage_w(double data)
|
|
{
|
|
CHECK_CAP_VOLTAGE;
|
|
|
|
if (data == EXTERNAL_VOLTAGE_DISCONNECT)
|
|
{
|
|
/* switch to internal, if not already */
|
|
if (m_noise_filter_cap_voltage_ext)
|
|
{
|
|
m_channel->update();
|
|
|
|
m_noise_filter_cap_voltage_ext = 0;
|
|
|
|
log_noise_filter_freq();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* set the voltage on the cap */
|
|
if (!m_noise_filter_cap_voltage_ext || (data != m_noise_filter_cap_voltage))
|
|
{
|
|
m_channel->update();
|
|
|
|
m_noise_filter_cap_voltage_ext = 1;
|
|
m_noise_filter_cap_voltage = data;
|
|
|
|
log_noise_filter_freq();
|
|
}
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* Set attack resistor
|
|
*
|
|
*****************************************************************************/
|
|
|
|
void sn76477_device::attack_res_w(double data)
|
|
{
|
|
if (data != RES_INF)
|
|
CHECK_POSITIVE;
|
|
|
|
if (data != m_attack_res)
|
|
{
|
|
m_channel->update();
|
|
|
|
m_attack_res = data;
|
|
|
|
log_attack_time();
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* Set decay resistor
|
|
*
|
|
*****************************************************************************/
|
|
|
|
void sn76477_device::decay_res_w(double data)
|
|
{
|
|
if (data != RES_INF)
|
|
CHECK_POSITIVE;
|
|
|
|
if (data != m_decay_res)
|
|
{
|
|
m_channel->update();
|
|
|
|
m_decay_res = data;
|
|
|
|
log_decay_time();
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* Set attack/decay capacitor
|
|
*
|
|
*****************************************************************************/
|
|
|
|
void sn76477_device::attack_decay_cap_w(double data)
|
|
{
|
|
CHECK_POSITIVE;
|
|
|
|
if (data != m_attack_decay_cap)
|
|
{
|
|
m_channel->update();
|
|
|
|
m_attack_decay_cap = data;
|
|
|
|
log_attack_time();
|
|
log_decay_time();
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* Set the voltage on the attack/decay capacitor
|
|
*
|
|
*****************************************************************************/
|
|
|
|
void sn76477_device::attack_decay_cap_voltage_w(double data)
|
|
{
|
|
CHECK_CAP_VOLTAGE;
|
|
|
|
if (data == EXTERNAL_VOLTAGE_DISCONNECT)
|
|
{
|
|
/* switch to internal, if not already */
|
|
if (m_attack_decay_cap_voltage_ext)
|
|
{
|
|
m_channel->update();
|
|
|
|
m_attack_decay_cap_voltage_ext = 0;
|
|
|
|
log_attack_time();
|
|
log_decay_time();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* set the voltage on the cap */
|
|
if (!m_attack_decay_cap_voltage_ext || (data != m_attack_decay_cap_voltage))
|
|
{
|
|
m_channel->update();
|
|
|
|
m_attack_decay_cap_voltage_ext = 1;
|
|
m_attack_decay_cap_voltage = data;
|
|
|
|
log_attack_time();
|
|
log_decay_time();
|
|
}
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* Set amplitude resistor
|
|
*
|
|
*****************************************************************************/
|
|
|
|
void sn76477_device::amplitude_res_w(double data)
|
|
{
|
|
if (data != RES_INF)
|
|
CHECK_POSITIVE;
|
|
|
|
if (data != m_amplitude_res)
|
|
{
|
|
m_channel->update();
|
|
|
|
m_amplitude_res = data;
|
|
|
|
log_voltage_out();
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* Set feedback resistor
|
|
*
|
|
*****************************************************************************/
|
|
|
|
void sn76477_device::feedback_res_w(double data)
|
|
{
|
|
if (data != RES_INF)
|
|
CHECK_POSITIVE;
|
|
|
|
if (data != m_feedback_res)
|
|
{
|
|
m_channel->update();
|
|
|
|
m_feedback_res = data;
|
|
|
|
log_voltage_out();
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* State saving
|
|
*
|
|
*****************************************************************************/
|
|
|
|
void sn76477_device::state_save_register()
|
|
{
|
|
save_item(NAME(m_enable));
|
|
save_item(NAME(m_envelope_mode));
|
|
save_item(NAME(m_vco_mode));
|
|
save_item(NAME(m_mixer_mode));
|
|
|
|
save_item(NAME(m_one_shot_res));
|
|
save_item(NAME(m_one_shot_cap));
|
|
save_item(NAME(m_one_shot_cap_voltage_ext));
|
|
|
|
save_item(NAME(m_slf_res));
|
|
save_item(NAME(m_slf_cap));
|
|
save_item(NAME(m_slf_cap_voltage_ext));
|
|
|
|
save_item(NAME(m_vco_voltage));
|
|
save_item(NAME(m_vco_res));
|
|
save_item(NAME(m_vco_cap));
|
|
save_item(NAME(m_vco_cap_voltage_ext));
|
|
|
|
save_item(NAME(m_noise_clock_res));
|
|
save_item(NAME(m_noise_clock_ext));
|
|
save_item(NAME(m_noise_clock));
|
|
save_item(NAME(m_noise_filter_res));
|
|
save_item(NAME(m_noise_filter_cap));
|
|
save_item(NAME(m_noise_filter_cap_voltage_ext));
|
|
|
|
save_item(NAME(m_attack_res));
|
|
save_item(NAME(m_decay_res));
|
|
save_item(NAME(m_attack_decay_cap));
|
|
save_item(NAME(m_attack_decay_cap_voltage_ext));
|
|
|
|
save_item(NAME(m_amplitude_res));
|
|
save_item(NAME(m_feedback_res));
|
|
save_item(NAME(m_pitch_voltage));
|
|
|
|
save_item(NAME(m_one_shot_cap_voltage));
|
|
save_item(NAME(m_one_shot_running_ff));
|
|
|
|
save_item(NAME(m_slf_cap_voltage));
|
|
save_item(NAME(m_slf_out_ff));
|
|
|
|
save_item(NAME(m_vco_cap_voltage));
|
|
save_item(NAME(m_vco_out_ff));
|
|
save_item(NAME(m_vco_alt_pos_edge_ff));
|
|
|
|
save_item(NAME(m_noise_filter_cap_voltage));
|
|
save_item(NAME(m_real_noise_bit_ff));
|
|
save_item(NAME(m_filtered_noise_bit_ff));
|
|
save_item(NAME(m_noise_gen_count));
|
|
|
|
save_item(NAME(m_attack_decay_cap_voltage));
|
|
|
|
save_item(NAME(m_rng));
|
|
}
|
|
|
|
//-------------------------------------------------
|
|
// sound_stream_update - handle a stream update
|
|
//-------------------------------------------------
|
|
|
|
void sn76477_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
|
|
{
|
|
double one_shot_cap_charging_step;
|
|
double one_shot_cap_discharging_step;
|
|
double slf_cap_charging_step;
|
|
double slf_cap_discharging_step;
|
|
double vco_duty_cycle_multiplier;
|
|
double vco_cap_charging_step;
|
|
double vco_cap_discharging_step;
|
|
double vco_cap_voltage_max;
|
|
uint32_t noise_gen_freq;
|
|
double noise_filter_cap_charging_step;
|
|
double noise_filter_cap_discharging_step;
|
|
double attack_decay_cap_charging_step;
|
|
double attack_decay_cap_discharging_step;
|
|
int attack_decay_cap_charging;
|
|
double voltage_out;
|
|
double center_to_peak_voltage_out;
|
|
|
|
stream_sample_t *buffer = outputs[0];
|
|
|
|
/* compute charging values, doing it here ensures that we always use the latest values */
|
|
one_shot_cap_charging_step = compute_one_shot_cap_charging_rate() / m_our_sample_rate;
|
|
one_shot_cap_discharging_step = compute_one_shot_cap_discharging_rate() / m_our_sample_rate;
|
|
|
|
slf_cap_charging_step = compute_slf_cap_charging_rate() / m_our_sample_rate;
|
|
slf_cap_discharging_step = compute_slf_cap_discharging_rate() / m_our_sample_rate;
|
|
|
|
vco_duty_cycle_multiplier = (1 - compute_vco_duty_cycle()) * 2;
|
|
vco_cap_charging_step = compute_vco_cap_charging_discharging_rate() / vco_duty_cycle_multiplier / m_our_sample_rate;
|
|
vco_cap_discharging_step = compute_vco_cap_charging_discharging_rate() * vco_duty_cycle_multiplier / m_our_sample_rate;
|
|
|
|
noise_filter_cap_charging_step = compute_noise_filter_cap_charging_rate() / m_our_sample_rate;
|
|
noise_filter_cap_discharging_step = compute_noise_filter_cap_discharging_rate() / m_our_sample_rate;
|
|
noise_gen_freq = compute_noise_gen_freq();
|
|
|
|
attack_decay_cap_charging_step = compute_attack_decay_cap_charging_rate() / m_our_sample_rate;
|
|
attack_decay_cap_discharging_step = compute_attack_decay_cap_discharging_rate() / m_our_sample_rate;
|
|
|
|
center_to_peak_voltage_out = compute_center_to_peak_voltage_out();
|
|
|
|
|
|
/* process 'samples' number of samples */
|
|
while (samples--)
|
|
{
|
|
/* update the one-shot cap voltage */
|
|
if (!m_one_shot_cap_voltage_ext)
|
|
{
|
|
if (m_one_shot_running_ff)
|
|
{
|
|
/* charging */
|
|
m_one_shot_cap_voltage = min(m_one_shot_cap_voltage + one_shot_cap_charging_step, ONE_SHOT_CAP_VOLTAGE_MAX);
|
|
}
|
|
else
|
|
{
|
|
/* discharging */
|
|
m_one_shot_cap_voltage = max(m_one_shot_cap_voltage - one_shot_cap_discharging_step, ONE_SHOT_CAP_VOLTAGE_MIN);
|
|
}
|
|
}
|
|
|
|
if (m_one_shot_cap_voltage >= ONE_SHOT_CAP_VOLTAGE_MAX)
|
|
{
|
|
m_one_shot_running_ff = 0;
|
|
}
|
|
|
|
|
|
/* update the SLF (super low frequency oscillator) */
|
|
if (!m_slf_cap_voltage_ext)
|
|
{
|
|
/* internal */
|
|
if (!m_slf_out_ff)
|
|
{
|
|
/* charging */
|
|
m_slf_cap_voltage = min(m_slf_cap_voltage + slf_cap_charging_step, SLF_CAP_VOLTAGE_MAX);
|
|
}
|
|
else
|
|
{
|
|
/* discharging */
|
|
m_slf_cap_voltage = max(m_slf_cap_voltage - slf_cap_discharging_step, SLF_CAP_VOLTAGE_MIN);
|
|
}
|
|
}
|
|
|
|
if (m_slf_cap_voltage >= SLF_CAP_VOLTAGE_MAX)
|
|
{
|
|
m_slf_out_ff = 1;
|
|
}
|
|
else if (m_slf_cap_voltage <= SLF_CAP_VOLTAGE_MIN)
|
|
{
|
|
m_slf_out_ff = 0;
|
|
}
|
|
|
|
|
|
/* update the VCO (voltage controlled oscillator) */
|
|
if (m_vco_mode)
|
|
{
|
|
/* VCO is controlled by SLF */
|
|
vco_cap_voltage_max = m_slf_cap_voltage + VCO_TO_SLF_VOLTAGE_DIFF;
|
|
}
|
|
else
|
|
{
|
|
/* VCO is controlled by external voltage */
|
|
vco_cap_voltage_max = m_vco_voltage + VCO_TO_SLF_VOLTAGE_DIFF;
|
|
}
|
|
|
|
if (!m_vco_cap_voltage_ext)
|
|
{
|
|
if (!m_vco_out_ff)
|
|
{
|
|
/* charging */
|
|
m_vco_cap_voltage = min(m_vco_cap_voltage + vco_cap_charging_step, vco_cap_voltage_max);
|
|
}
|
|
else
|
|
{
|
|
/* discharging */
|
|
m_vco_cap_voltage = max(m_vco_cap_voltage - vco_cap_discharging_step, VCO_CAP_VOLTAGE_MIN);
|
|
}
|
|
}
|
|
|
|
if (m_vco_cap_voltage >= vco_cap_voltage_max)
|
|
{
|
|
if (!m_vco_out_ff)
|
|
{
|
|
/* positive edge */
|
|
m_vco_alt_pos_edge_ff = !m_vco_alt_pos_edge_ff;
|
|
}
|
|
|
|
m_vco_out_ff = 1;
|
|
}
|
|
else if (m_vco_cap_voltage <= VCO_CAP_VOLTAGE_MIN)
|
|
{
|
|
m_vco_out_ff = 0;
|
|
}
|
|
|
|
|
|
/* update the noise generator */
|
|
while (!m_noise_clock_ext && (m_noise_gen_count <= noise_gen_freq))
|
|
{
|
|
m_noise_gen_count = m_noise_gen_count + m_our_sample_rate;
|
|
|
|
m_real_noise_bit_ff = generate_next_real_noise_bit();
|
|
}
|
|
|
|
m_noise_gen_count = m_noise_gen_count - noise_gen_freq;
|
|
|
|
|
|
/* update the noise filter */
|
|
if (!m_noise_filter_cap_voltage_ext)
|
|
{
|
|
/* internal */
|
|
if (m_real_noise_bit_ff)
|
|
{
|
|
/* charging */
|
|
m_noise_filter_cap_voltage = min(m_noise_filter_cap_voltage + noise_filter_cap_charging_step, NOISE_CAP_VOLTAGE_MAX);
|
|
}
|
|
else
|
|
{
|
|
/* discharging */
|
|
m_noise_filter_cap_voltage = max(m_noise_filter_cap_voltage - noise_filter_cap_discharging_step, NOISE_CAP_VOLTAGE_MIN);
|
|
}
|
|
}
|
|
|
|
/* check the thresholds */
|
|
if (m_noise_filter_cap_voltage >= NOISE_CAP_HIGH_THRESHOLD)
|
|
{
|
|
m_filtered_noise_bit_ff = 0;
|
|
}
|
|
else if (m_noise_filter_cap_voltage <= NOISE_CAP_LOW_THRESHOLD)
|
|
{
|
|
m_filtered_noise_bit_ff = 1;
|
|
}
|
|
|
|
|
|
/* based on the envelope mode figure out the attack/decay phase we are in */
|
|
switch (m_envelope_mode)
|
|
{
|
|
case 0: /* VCO */
|
|
attack_decay_cap_charging = m_vco_out_ff;
|
|
break;
|
|
|
|
case 1: /* one-shot */
|
|
attack_decay_cap_charging = m_one_shot_running_ff;
|
|
break;
|
|
|
|
case 2:
|
|
default: /* mixer only */
|
|
attack_decay_cap_charging = 1; /* never a decay phase */
|
|
break;
|
|
|
|
case 3: /* VCO with alternating polarity */
|
|
attack_decay_cap_charging = m_vco_out_ff && m_vco_alt_pos_edge_ff;
|
|
break;
|
|
}
|
|
|
|
|
|
/* update a/d cap voltage */
|
|
if (!m_attack_decay_cap_voltage_ext)
|
|
{
|
|
if (attack_decay_cap_charging)
|
|
{
|
|
if (attack_decay_cap_charging_step > 0)
|
|
{
|
|
m_attack_decay_cap_voltage = min(m_attack_decay_cap_voltage + attack_decay_cap_charging_step, AD_CAP_VOLTAGE_MAX);
|
|
}
|
|
else
|
|
{
|
|
/* no attack, voltage to max instantly */
|
|
m_attack_decay_cap_voltage = AD_CAP_VOLTAGE_MAX;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* discharging */
|
|
if (attack_decay_cap_discharging_step > 0)
|
|
{
|
|
m_attack_decay_cap_voltage = max(m_attack_decay_cap_voltage - attack_decay_cap_discharging_step, AD_CAP_VOLTAGE_MIN);
|
|
}
|
|
else
|
|
{
|
|
/* no decay, voltage to min instantly */
|
|
m_attack_decay_cap_voltage = AD_CAP_VOLTAGE_MIN;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/* mix the output, if enabled, or not saturated by the VCO */
|
|
if (!m_enable && (m_vco_cap_voltage <= VCO_CAP_VOLTAGE_MAX))
|
|
{
|
|
uint32_t out;
|
|
|
|
/* enabled */
|
|
switch (m_mixer_mode)
|
|
{
|
|
case 0: /* VCO */
|
|
out = m_vco_out_ff;
|
|
break;
|
|
|
|
case 1: /* SLF */
|
|
out = m_slf_out_ff;
|
|
break;
|
|
|
|
case 2: /* noise */
|
|
out = m_filtered_noise_bit_ff;
|
|
break;
|
|
|
|
case 3: /* VCO and noise */
|
|
out = m_vco_out_ff & m_filtered_noise_bit_ff;
|
|
break;
|
|
|
|
case 4: /* SLF and noise */
|
|
out = m_slf_out_ff & m_filtered_noise_bit_ff;
|
|
break;
|
|
|
|
case 5: /* VCO, SLF and noise */
|
|
out = m_vco_out_ff & m_slf_out_ff & m_filtered_noise_bit_ff;
|
|
break;
|
|
|
|
case 6: /* VCO and SLF */
|
|
out = m_vco_out_ff & m_slf_out_ff;
|
|
break;
|
|
|
|
case 7: /* inhibit */
|
|
default:
|
|
out = 0;
|
|
break;
|
|
}
|
|
|
|
/* determine the OUT voltage from the attack/delay cap voltage and clip it */
|
|
if (out)
|
|
{
|
|
voltage_out = OUT_CENTER_LEVEL_VOLTAGE + center_to_peak_voltage_out * out_pos_gain[(int)(m_attack_decay_cap_voltage * 10)],
|
|
voltage_out = min(voltage_out, OUT_HIGH_CLIP_THRESHOLD);
|
|
}
|
|
else
|
|
{
|
|
voltage_out = OUT_CENTER_LEVEL_VOLTAGE + center_to_peak_voltage_out * out_neg_gain[(int)(m_attack_decay_cap_voltage * 10)],
|
|
voltage_out = max(voltage_out, OUT_LOW_CLIP_THRESHOLD);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* disabled */
|
|
voltage_out = OUT_CENTER_LEVEL_VOLTAGE;
|
|
}
|
|
|
|
|
|
/* convert it to a signed 16-bit sample,
|
|
-32767 = OUT_LOW_CLIP_THRESHOLD
|
|
0 = OUT_CENTER_LEVEL_VOLTAGE
|
|
32767 = 2 * OUT_CENTER_LEVEL_VOLTAGE + OUT_LOW_CLIP_THRESHOLD
|
|
|
|
/ Vout - Vmin \
|
|
sample = | ----------- - 1 | * 32767
|
|
\ Vcen - Vmin /
|
|
*/
|
|
*buffer++ = (((voltage_out - OUT_LOW_CLIP_THRESHOLD) / (OUT_CENTER_LEVEL_VOLTAGE - OUT_LOW_CLIP_THRESHOLD)) - 1) * 32767;
|
|
|
|
if (LOG_WAV && (!m_enable || !LOG_WAV_ENABLED_ONLY))
|
|
{
|
|
int16_t log_data_l;
|
|
int16_t log_data_r;
|
|
|
|
switch (LOG_WAV_VALUE_L)
|
|
{
|
|
case 0:
|
|
log_data_l = LOG_WAV_GAIN_FACTOR * voltage_out;
|
|
break;
|
|
case 1:
|
|
log_data_l = LOG_WAV_GAIN_FACTOR * m_enable;
|
|
break;
|
|
case 2:
|
|
log_data_l = LOG_WAV_GAIN_FACTOR * m_one_shot_cap_voltage;
|
|
break;
|
|
case 3:
|
|
log_data_l = LOG_WAV_GAIN_FACTOR * m_attack_decay_cap_voltage;
|
|
break;
|
|
case 4:
|
|
log_data_l = LOG_WAV_GAIN_FACTOR * m_slf_cap_voltage;
|
|
break;
|
|
case 5:
|
|
log_data_l = LOG_WAV_GAIN_FACTOR * m_vco_cap_voltage;
|
|
break;
|
|
case 6:
|
|
log_data_l = LOG_WAV_GAIN_FACTOR * m_noise_filter_cap_voltage;
|
|
break;
|
|
}
|
|
|
|
switch (LOG_WAV_VALUE_R)
|
|
{
|
|
case 0:
|
|
log_data_r = LOG_WAV_GAIN_FACTOR * voltage_out;
|
|
break;
|
|
case 1:
|
|
log_data_r = LOG_WAV_GAIN_FACTOR * m_enable;
|
|
break;
|
|
case 2:
|
|
log_data_r = LOG_WAV_GAIN_FACTOR * m_one_shot_cap_voltage;
|
|
break;
|
|
case 3:
|
|
log_data_r = LOG_WAV_GAIN_FACTOR * m_attack_decay_cap_voltage;
|
|
break;
|
|
case 4:
|
|
log_data_r = LOG_WAV_GAIN_FACTOR * m_slf_cap_voltage;
|
|
break;
|
|
case 5:
|
|
log_data_r = LOG_WAV_GAIN_FACTOR * m_vco_cap_voltage;
|
|
break;
|
|
case 6:
|
|
log_data_r = LOG_WAV_GAIN_FACTOR * m_noise_filter_cap_voltage;
|
|
break;
|
|
}
|
|
|
|
add_wav_data(log_data_l, log_data_r);
|
|
}
|
|
}
|
|
}
|