From ef2758198093f7a0a4688649484afc6498090ec9 Mon Sep 17 00:00:00 2001 From: dinkc64 <12570148+dinkc64@users.noreply.github.com> Date: Mon, 2 May 2016 01:05:54 +0000 Subject: [PATCH] WIP sn76477 soundcore, hook it up to speak & rescue/stratovox --- makefile.burn_rules | 2 +- src/burn/drv/pre90s/d_route16.cpp | 43 +- src/burn/snd/sn76477.cpp | 984 ++++++++++++++++++++++++++++++ src/burn/snd/sn76477.h | 82 +++ 4 files changed, 1108 insertions(+), 3 deletions(-) create mode 100644 src/burn/snd/sn76477.cpp create mode 100644 src/burn/snd/sn76477.h diff --git a/makefile.burn_rules b/makefile.burn_rules index 7125c1671..3d6a0c86d 100644 --- a/makefile.burn_rules +++ b/makefile.burn_rules @@ -93,7 +93,7 @@ depobj := $(drvobj) \ ay8910.o burn_y8950.o burn_ym2151.o burn_ym2203.o burn_ym2413.o burn_ym2608.o burn_ym2610.o burn_ym2612.o \ burn_ym3526.o burn_ym3812.o burn_ymf278b.o c6280.o dac.o es5506.o es8712.o flt_rc.o fm.o fmopl.o gaelco.o ics2115.o iremga20.o \ k005289.o k007232.o k051649.o k053260.o k054539.o msm5205.o msm5232.o msm6295.o namco_snd.o nes_apu.o tms36xx.o phoenixsound.o pleiadssound.o pokey.o rf5c68.o saa1099.o \ - samples.o segapcm.o sn76496.o upd7759.o vlm5030.o x1010.o ym2151.o ym2413.o ymdeltat.o ymf278b.o ymz280b.o \ + samples.o segapcm.o sn76477.o sn76496.o upd7759.o vlm5030.o x1010.o ym2151.o ym2413.o ymdeltat.o ymf278b.o ymz280b.o \ \ arm7_intf.o arm_intf.o h6280_intf.o hd6309_intf.o konami_intf.o m6502_intf.o m6800_intf.o m6805_intf.o m6809_intf.o \ m68000_intf.o nec_intf.o pic16c5x_intf.o s2650_intf.o tlcs90_intf.o z80_intf.o \ diff --git a/src/burn/drv/pre90s/d_route16.cpp b/src/burn/drv/pre90s/d_route16.cpp index 2d34fe709..551693f21 100644 --- a/src/burn/drv/pre90s/d_route16.cpp +++ b/src/burn/drv/pre90s/d_route16.cpp @@ -8,6 +8,7 @@ extern "C" { #include "ay8910.h" } +#include "sn76477.h" //------------------------------------------------------------------------------------------------ @@ -405,9 +406,23 @@ static INT32 DrvDoReset() return 0; } -void stratvox_sn76477_write(UINT32, UINT32) +void stratvox_sn76477_write(UINT32, UINT32 data) { + SN76477_enable_w(0, (data >> 0) & 1); + SN76477_vco_w(0, (data >> 1) & 1); + SN76477_envelope_1_w(0, (data >> 2) & 1); + SN76477_envelope_2_w(0, (data >> 3) & 1); + SN76477_mixer_a_w(0, (data >> 4) & 1); + SN76477_mixer_b_w(0, (data >> 5) & 1); + SN76477_mixer_c_w(0, (data >> 6) & 1); +#if 0 + SN76477_mixer_w(0,(data >> 4) & 7); + SN76477_envelope_w(0,(data >> 2) & 3); + SN76477_vco_w(0,(data >> 1) & 1); + SN76477_enable_w(0,data & 1); +#endif + } static INT32 DrvSyncDAC() @@ -460,6 +475,28 @@ static INT32 DrvInit() pAY8910Buffer[1] = pFMBuffer + nBurnSoundLen * 1; pAY8910Buffer[2] = pFMBuffer + nBurnSoundLen * 2; + SN76477_init(0); + SN76477_set_noise_res(0, RES_K(47)); + SN76477_set_filter_res(0, RES_K(150)); + SN76477_set_filter_cap(0, CAP_U(0.001)); + SN76477_set_decay_res(0, RES_M(3.3)); + SN76477_set_attack_decay_cap(0, CAP_U(1.0)); + SN76477_set_attack_res(0, RES_K(4.7)); + SN76477_set_amplitude_res(0, RES_K(200)); + SN76477_set_feedback_res(0, RES_K(55)); + SN76477_set_oneshot_res(0, RES_K(4.7)); + SN76477_set_oneshot_cap(0, CAP_U(2.2)); + SN76477_set_pitch_voltage(0, 5.0); + SN76477_set_slf_res(0, RES_K(75)); + SN76477_set_slf_cap(0, CAP_U(1.0)); + SN76477_set_vco_res(0, RES_K(100)); + SN76477_set_vco_cap(0, CAP_U(0.022)); + SN76477_set_vco_voltage(0, 5.0*2/(2+10)); + SN76477_mixer_w(0, 0); + SN76477_envelope_w(0, 0); + + SN76477_set_mastervol(0, 1.00); + AY8910Init(0, 1250000, nBurnSoundRate, NULL, NULL, &stratvox_sn76477_write, NULL); AY8910SetAllRoutes(0, 0.50, BURN_SND_ROUTE_BOTH); @@ -477,6 +514,7 @@ static INT32 DrvExit() DACExit(); ZetExit(); AY8910Exit(0); + SN76477_exit(0); BurnFree (Mem); BurnFree (pFMBuffer); @@ -621,7 +659,7 @@ static INT32 DrvFrame() INT32 nSegmentLength = nBurnSoundLen / nInterleave; INT16* pSoundBuf = pBurnSoundOut + (nSoundBufferPos << 1); AY8910Render(&pAY8910Buffer[0], pSoundBuf, nSegmentLength, 0); - + SN76477_sound_update(0, pSoundBuf, nSegmentLength); nSoundBufferPos += nSegmentLength; } } @@ -632,6 +670,7 @@ static INT32 DrvFrame() INT16* pSoundBuf = pBurnSoundOut + (nSoundBufferPos << 1); if (nSegmentLength) { AY8910Render(&pAY8910Buffer[0], pSoundBuf, nSegmentLength, 0); + SN76477_sound_update(0, pSoundBuf, nSegmentLength); } DACUpdate(pBurnSoundOut, nBurnSoundLen); diff --git a/src/burn/snd/sn76477.cpp b/src/burn/snd/sn76477.cpp new file mode 100644 index 000000000..b8fc635fd --- /dev/null +++ b/src/burn/snd/sn76477.cpp @@ -0,0 +1,984 @@ +/***************************************************************************** + SN76477 pins and assigned interface variables/functions + + SN76477_envelope_w() + / \ + [ 1] ENV SEL 1 ENV SEL 2 [28] + [ 2] GND MIXER C [27] \ + SN76477_noise_clock_w [ 3] NOISE EXT OSC MIXER A [26] > SN76477_mixer_w() + noise_res [ 4] RES NOISE OSC MIXER B [25] / + filter_res [ 5] NOISE FILTER RES O/S RES [24] oneshot_res + filter_cap [ 6] NOISE FILTER CAP O/S CAP [23] oneshot_cap + decay_res [ 7] DECAY RES VCO SEL [22] SN76477_vco_w() + attack_decay_cap [ 8] A/D CAP SLF CAP [21] slf_cap + SN76477_enable_w() [ 9] ENABLE SLF RES [20] slf_res + attack_res [10] ATTACK RES PITCH [19] pitch_voltage + amplitude_res [11] AMP VCO RES [18] vco_res + feedback_res [12] FEEDBACK VCO CAP [17] vco_cap + [13] OUTPUT VCO EXT CONT [16] vco_voltage + [14] Vcc +5V REG OUT [15] + + All resistor values in Ohms. + All capacitor values in Farads. + Use RES_K, RES_M and CAP_U, CAP_N, CAP_P macros to convert + magnitudes, eg. 220k = RES_K(220), 47nF = CAP_N(47) + + *****************************************************************************/ + +#include "burnint.h" +#include "sn76477.h" + +#define VERBOSE 0 + +#if VERBOSE >= 0 +#define LOG(n,x) //if( VERBOSE >= (n) ) logerror x +#else +#define LOG(n,x) +#endif + +#define VMIN 0x0000 +#define VMAX 0x7fff + +struct SN76477 { + double mastervol; + int samplerate; /* from Machine->sample_rate */ + int vol; /* current volume (attack/decay) */ + int vol_count; /* volume adjustment counter */ + int vol_rate; /* volume adjustment rate - dervied from attack/decay */ + int vol_step; /* volume adjustment step */ + + double slf_count; /* SLF emulation */ + double slf_freq; /* frequency - derived */ + double slf_level; /* triangular wave level */ + int slf_dir; /* triangular wave direction */ + int slf_out; /* rectangular output signal state */ + + double vco_count; /* VCO emulation */ + double vco_freq; /* frequency - derived */ + double vco_step; /* modulated frequency - derived */ + int vco_out; /* rectangular output signal state */ + + int noise_count; /* NOISE emulation */ + int noise_clock; /* external clock signal */ + int noise_freq; /* filter frequency - derived */ + int noise_poly; /* polynome */ + int noise_out; /* rectangular output signal state */ + + double envelope_timer; /* ENVELOPE timer */ + int envelope_state; /* attack / decay toggle */ + + double attack_time; /* ATTACK time (time until vol reaches 100%) */ + double decay_time; /* DECAY time (time until vol reaches 0%) */ + double oneshot_time; /* ONE-SHOT time */ + double oneshot_timer; /* ONE-SHOT timer */ + + int envelope; /* pin 1, pin 28 */ + double noise_res; /* pin 4 */ + double filter_res; /* pin 5 */ + double filter_cap; /* pin 6 */ + double decay_res; /* pin 7 */ + double attack_decay_cap;/* pin 8 */ + int enable; /* pin 9 */ + double attack_res; /* pin 10 */ + double amplitude_res; /* pin 11 */ + double feedback_res; /* pin 12 */ + double vco_voltage; /* pin 16 */ + double vco_cap; /* pin 17 */ + double vco_res; /* pin 18 */ + double pitch_voltage; /* pin 19 */ + double slf_res; /* pin 20 */ + double slf_cap; /* pin 21 */ + int vco_select; /* pin 22 */ + double oneshot_cap; /* pin 23 */ + double oneshot_res; /* pin 24 */ + int mixer; /* pin 25,26,27 */ + + INT16 vol_lookup[VMAX+1-VMIN]; /* volume lookup table */ +}; + +//static struct SN76477interface *intf; +struct SN76477 *sn76477[MAX_SN76477]; + +static void attack_decay(int param) +{ + struct SN76477 *sn = sn76477[param]; + sn->envelope_state ^= 1; + if( sn->envelope_state ) + { + /* start ATTACK */ + sn->vol_rate = ( sn->attack_time > 0 ) ? VMAX / sn->attack_time : VMAX; + sn->vol_step = +1; + LOG(2,("SN76477 #%d: ATTACK rate %d/%d = %d/sec\n", param, sn->vol_rate, sn->samplerate, sn->vol_rate/sn->samplerate)); + } + else + { + /* start DECAY */ + sn->vol = VMAX; /* just in case... */ + sn->vol_rate = ( sn->decay_time > 0 ) ? VMAX / sn->decay_time : VMAX; + sn->vol_step = -1; + LOG(2,("SN76477 #%d: DECAY rate %d/%d = %d/sec\n", param, sn->vol_rate, sn->samplerate, sn->vol_rate/sn->samplerate)); + } +} + +static void vco_envelope_cb(int param) +{ + attack_decay(param); +} + +static void oneshot_envelope_cb(int param) +{ + attack_decay(param); +} + +static void sntimer_tick(int param) +{ + if (sn76477[param]->envelope_timer > 0.0) { + vco_envelope_cb(param); + sn76477[param]->envelope_timer -= 0.0001; + //if (!sn76477[param]->envelope_timer) + // bprintf(0, _T("envtimer done!")); + } + + if (sn76477[param]->oneshot_timer > 0.0) { + oneshot_envelope_cb(param); + sn76477[param]->oneshot_timer -= 0.0001; + //if (!sn76477[param]->oneshot_timer) + // bprintf(0, _T("shottimer done!")); + } +} + +#if VERBOSE +static const char *mixer_mode[8] = { + "VCO", + "SLF", + "Noise", + "VCO/Noise", + "SLF/Noise", + "SLF/VCO/Noise", + "SLF/VCO", + "Inhibit" +}; +#endif + +/***************************************************************************** + * set MIXER select inputs + *****************************************************************************/ +void SN76477_mixer_w(int chip, int data) +{ + struct SN76477 *sn = sn76477[chip]; + + if( data == sn->mixer ) + return; + + sn->mixer = data; + LOG(1,("SN76477 #%d: MIXER mode %d [%s]\n", chip, sn->mixer, mixer_mode[sn->mixer])); +} + +void SN76477_mixer_a_w(int chip, int data) +{ + struct SN76477 *sn = sn76477[chip]; + + data = data ? 1 : 0; + if( data == (sn->mixer & 1) ) + return; + + sn->mixer = (sn->mixer & ~1) | data; + LOG(1,("SN76477 #%d: MIXER mode %d [%s]\n", chip, sn->mixer, mixer_mode[sn->mixer])); +} + +void SN76477_mixer_b_w(int chip, int data) +{ + struct SN76477 *sn = sn76477[chip]; + + data = data ? 2 : 0; + if( data == (sn->mixer & 2) ) + return; + + sn->mixer = (sn->mixer & ~2) | data; + LOG(1,("SN76477 #%d: MIXER mode %d [%s]\n", chip, sn->mixer, mixer_mode[sn->mixer])); +} + +void SN76477_mixer_c_w(int chip, int data) +{ + struct SN76477 *sn = sn76477[chip]; + + data = data ? 4 : 0; + if( data == (sn->mixer & 4) ) + return; + + sn->mixer = (sn->mixer & ~4) | data; + LOG(1,("SN76477 #%d: MIXER mode %d [%s]\n", chip, sn->mixer, mixer_mode[sn->mixer])); +} + +#if VERBOSE +static const char *envelope_mode[4] = { + "VCO", + "One-Shot", + "Mixer only", + "VCO with alternating Polarity" +}; +#endif + +/***************************************************************************** + * set ENVELOPE select inputs + *****************************************************************************/ +void SN76477_envelope_w(int chip, int data) +{ + struct SN76477 *sn = sn76477[chip]; + + if( data == sn->envelope ) + return; + + sn->envelope = data; + LOG(1,("SN76477 #%d: ENVELOPE mode %d [%s]\n", chip, sn->envelope, envelope_mode[sn->envelope])); +} + +void SN76477_envelope_1_w(int chip, int data) +{ + struct SN76477 *sn = sn76477[chip]; + + if( data == (sn->envelope & 1) ) + return; + + sn->envelope = (sn->envelope & ~1) | data; + LOG(1,("SN76477 #%d: ENVELOPE mode %d [%s]\n", chip, sn->envelope, envelope_mode[sn->envelope])); +} + +void SN76477_envelope_2_w(int chip, int data) +{ + struct SN76477 *sn = sn76477[chip]; + + data <<= 1; + + if( data == (sn->envelope & 2) ) + return; + + sn->envelope = (sn->envelope & ~2) | data; + LOG(1,("SN76477 #%d: ENVELOPE mode %d [%s]\n", chip, sn->envelope, envelope_mode[sn->envelope])); +} + +/***************************************************************************** + * set VCO external/SLF input + *****************************************************************************/ +void SN76477_vco_w(int chip, int data) +{ + struct SN76477 *sn = sn76477[chip]; + + if( data == sn->vco_select ) + return; + + sn->vco_select = data; + LOG(1,("SN76477 #%d: VCO select %d [%s]\n", chip, sn->vco_select, sn->vco_select ? "Internal (SLF)" : "External (Pin 16)")); +} + +/***************************************************************************** + * set VCO enable input + *****************************************************************************/ +void SN76477_enable_w(int chip, int data) +{ + struct SN76477 *sn = sn76477[chip]; + + if( data == sn->enable ) + return; + + sn->enable = data; + sn->envelope_state = data; + + sn->envelope_timer = 0; + sn->oneshot_timer = 0; + + if( sn->enable == 0 ) + { + switch( sn->envelope ) + { + case 0: /* VCO */ + if( sn->vco_res > 0 && sn->vco_cap > 0 ) + sn->envelope_timer = TIME_IN_HZ(0.64/(sn->vco_res * sn->vco_cap)); + else + oneshot_envelope_cb(chip); + break; + case 1: /* One-Shot */ + oneshot_envelope_cb(chip); + if (sn->oneshot_time > 0) + sn->oneshot_timer = sn->oneshot_time; + break; + case 2: /* MIXER only */ + sn->vol = VMAX; + break; + default: /* VCO with alternating polariy */ + /* huh? */ + if( sn->vco_res > 0 && sn->vco_cap > 0 ) + sn->envelope_timer = TIME_IN_HZ(0.64/(sn->vco_res * sn->vco_cap)/2); + else + oneshot_envelope_cb(chip); + break; + } + } + else + { + switch( sn->envelope ) + { + case 0: /* VCO */ + if( sn->vco_res > 0 && sn->vco_cap > 0 ) + sn->envelope_timer = TIME_IN_HZ(0.64/(sn->vco_res * sn->vco_cap)); + else + oneshot_envelope_cb(chip); + break; + case 1: /* One-Shot */ + oneshot_envelope_cb(chip); + break; + case 2: /* MIXER only */ + sn->vol = VMIN; + break; + default: /* VCO with alternating polariy */ + /* huh? */ + if( sn->vco_res > 0 && sn->vco_cap > 0 ) + sn->envelope_timer = TIME_IN_HZ(0.64/(sn->vco_res * sn->vco_cap)/2); + else + oneshot_envelope_cb(chip); + break; + } + } + LOG(1,("SN76477 #%d: ENABLE line %d [%s]\n", chip, sn->enable, sn->enable ? "Inhibited" : "Enabled" )); +} + +/***************************************************************************** + * set NOISE external signal (pin 3) + *****************************************************************************/ +void SN76477_noise_clock_w(int chip, int data) +{ + struct SN76477 *sn = sn76477[chip]; + + if( data == sn->noise_clock ) + return; + + sn->noise_clock = data; + /* on the rising edge shift the polynome */ + if( sn->noise_clock ) + sn->noise_poly = ((sn->noise_poly << 7) + (sn->noise_poly >> 10) + 0x18000) & 0x1ffff; +} + +/***************************************************************************** + * set NOISE resistor (pin 4) + *****************************************************************************/ +void SN76477_set_noise_res(int chip, double res) +{ + struct SN76477 *sn = sn76477[chip]; + + sn->noise_res = res; +} + +/***************************************************************************** + * set NOISE FILTER resistor (pin 5) + *****************************************************************************/ +void SN76477_set_filter_res(int chip, double res) +{ + struct SN76477 *sn = sn76477[chip]; + + if( res == sn->filter_res ) + return; + + sn->filter_res = res; + if( sn->filter_res > 0 && sn->filter_cap > 0 ) + { + sn->noise_freq = (int)(1.28 / (sn->filter_res * sn->filter_cap)); + LOG(1,("SN76477 #%d: NOISE FILTER freqency %d\n", chip, sn->noise_freq)); + } + else + sn->noise_freq = sn->samplerate; +} + +/***************************************************************************** + * set NOISE FILTER capacitor (pin 6) + *****************************************************************************/ +void SN76477_set_filter_cap(int chip, double cap) +{ + struct SN76477 *sn = sn76477[chip]; + + if( cap == sn->filter_cap ) + return; + + sn->filter_cap = cap; + if( sn->filter_res > 0 && sn->filter_cap > 0 ) + { + sn->noise_freq = (int)(1.28 / (sn->filter_res * sn->filter_cap)); + LOG(1,("SN76477 #%d: NOISE FILTER freqency %d\n", chip, sn->noise_freq)); + } + else + sn->noise_freq = sn->samplerate; +} + +/***************************************************************************** + * set DECAY resistor (pin 7) + *****************************************************************************/ +void SN76477_set_decay_res(int chip, double res) +{ + struct SN76477 *sn = sn76477[chip]; + + if( res == sn->decay_res ) + return; + + sn->decay_res = res; + sn->decay_time = sn->decay_res * sn->attack_decay_cap; + LOG(1,("SN76477 #%d: DECAY time is %fs\n", chip, sn->decay_time)); +} + +/***************************************************************************** + * set ATTACK/DECAY capacitor (pin 8) + *****************************************************************************/ +void SN76477_set_attack_decay_cap(int chip, double cap) +{ + struct SN76477 *sn = sn76477[chip]; + + if( cap == sn->attack_decay_cap ) + return; + + sn->attack_decay_cap = cap; + sn->decay_time = sn->decay_res * sn->attack_decay_cap; + sn->attack_time = sn->attack_res * sn->attack_decay_cap; + LOG(1,("SN76477 #%d: ATTACK time is %fs\n", chip, sn->attack_time)); + LOG(1,("SN76477 #%d: DECAY time is %fs\n", chip, sn->decay_time)); +} + +/***************************************************************************** + * set ATTACK resistor (pin 10) + *****************************************************************************/ +void SN76477_set_attack_res(int chip, double res) +{ + struct SN76477 *sn = sn76477[chip]; + + if( res == sn->attack_res ) + return; + + sn->attack_res = res; + sn->attack_time = sn->attack_res * sn->attack_decay_cap; + LOG(1,("SN76477 #%d: ATTACK time is %fs\n", chip, sn->attack_time)); +} + +/***************************************************************************** + * set AMP resistor (pin 11) + *****************************************************************************/ +void SN76477_set_amplitude_res(int chip, double res) +{ + struct SN76477 *sn = sn76477[chip]; + int i; + + if( res == sn->amplitude_res ) + return; + + sn->amplitude_res = res; + if( sn->amplitude_res > 0 ) + { +#if VERBOSE + int clip = 0; +#endif + for( i = 0; i < VMAX+1; i++ ) + { + int vol = (int)((3.4 * sn->feedback_res / sn->amplitude_res) * 32767 * i / (VMAX+1)); +#if VERBOSE + if( vol > 32767 && !clip ) + clip = i; + LOG(3,("%d\n", vol)); +#endif + if( vol > 32767 ) vol = 32767; + sn->vol_lookup[i] = vol * sn->mastervol / 100; + } + LOG(1,("SN76477 #%d: volume range from -%d to +%d (clip at %d%%)\n", chip, sn->vol_lookup[VMAX-VMIN], sn->vol_lookup[VMAX-VMIN], clip * 100 / 256)); + } + else + { + memset(sn->vol_lookup, 0, sizeof(sn->vol_lookup)); + } +} + +/***************************************************************************** + * set FEEDBACK resistor (pin 12) + *****************************************************************************/ +void SN76477_set_feedback_res(int chip, double res) +{ + struct SN76477 *sn = sn76477[chip]; + int i; + + if( res == sn->feedback_res ) + return; + + sn->feedback_res = res; + if( sn->amplitude_res > 0 ) + { +#if VERBOSE + int clip = 0; +#endif + for( i = 0; i < VMAX+1; i++ ) + { + int vol = (int)((3.4 * sn->feedback_res / sn->amplitude_res) * 32767 * i / (VMAX+1)); +#if VERBOSE + if( vol > 32767 && !clip ) clip = i; +#endif + if( vol > 32767 ) vol = 32767; + sn->vol_lookup[i] = vol * sn->mastervol / 100; + } + LOG(1,("SN76477 #%d: volume range from -%d to +%d (clip at %d%%)\n", chip, sn->vol_lookup[VMAX-VMIN], sn->vol_lookup[VMAX-VMIN], clip * 100 / 256)); + } + else + { + memset(sn->vol_lookup, 0, sizeof(sn->vol_lookup)); + } +} + +/***************************************************************************** + * set PITCH voltage (pin 19) + * TODO: fill with live... + *****************************************************************************/ +void SN76477_set_pitch_voltage(int chip, double voltage) +{ + struct SN76477 *sn = sn76477[chip]; + + if( voltage == sn->pitch_voltage ) + return; + + sn->pitch_voltage = voltage; + LOG(1,("SN76477 #%d: VCO pitch voltage %f (%d%% duty cycle)\n", chip, sn->pitch_voltage, 0)); +} + +/***************************************************************************** + * set VCO resistor (pin 18) + *****************************************************************************/ +void SN76477_set_vco_res(int chip, double res) +{ + struct SN76477 *sn = sn76477[chip]; + + if( res == sn->vco_res ) + return; + + sn->vco_res = res; + if( sn->vco_res > 0 && sn->vco_cap > 0 ) + { + sn->vco_freq = 0.64 / (sn->vco_res * sn->vco_cap); + LOG(1,("SN76477 #%d: VCO freqency %f\n", chip, sn->vco_freq)); + } + else + sn->vco_freq = 0; +} + +/***************************************************************************** + * set VCO capacitor (pin 17) + *****************************************************************************/ +void SN76477_set_vco_cap(int chip, double cap) +{ + struct SN76477 *sn = sn76477[chip]; + + if( cap == sn->vco_cap ) + return; + + sn->vco_cap = cap; + if( sn->vco_res > 0 && sn->vco_cap > 0 ) + { + sn->vco_freq = 0.64 / (sn->vco_res * sn->vco_cap); + LOG(1,("SN76477 #%d: VCO freqency %f\n", chip, sn->vco_freq)); + } + else + sn->vco_freq = 0; +} + +/***************************************************************************** + * set VCO voltage (pin 16) + *****************************************************************************/ +void SN76477_set_vco_voltage(int chip, double voltage) +{ + struct SN76477 *sn = sn76477[chip]; + + if( voltage == sn->vco_voltage ) + return; + + sn->vco_voltage = voltage; + LOG(1,("SN76477 #%d: VCO ext. voltage %f (%f * %f = %f Hz)\n", chip, + sn->vco_voltage, + sn->vco_freq, + 10.0 * (5.0 - sn->vco_voltage) / 5.0, + sn->vco_freq * 10.0 * (5.0 - sn->vco_voltage) / 5.0)); +} + +/***************************************************************************** + * set SLF resistor (pin 20) + *****************************************************************************/ +void SN76477_set_slf_res(int chip, double res) +{ + struct SN76477 *sn = sn76477[chip]; + + if( res == sn->slf_res ) + return; + + sn->slf_res = res; + if( sn->slf_res > 0 && sn->slf_cap > 0 ) + { + sn->slf_freq = 0.64 / (sn->slf_res * sn->slf_cap); + LOG(1,("SN76477 #%d: SLF freqency %f\n", chip, sn->slf_freq)); + } + else + sn->slf_freq = 0; +} + +/***************************************************************************** + * set SLF capacitor (pin 21) + *****************************************************************************/ +void SN76477_set_slf_cap(int chip, double cap) +{ + struct SN76477 *sn = sn76477[chip]; + + if( cap == sn->slf_cap ) + return; + + sn->slf_cap = cap; + if( sn->slf_res > 0 && sn->slf_cap > 0 ) + { + sn->slf_freq = 0.64 / (sn->slf_res * sn->slf_cap); + LOG(1,("SN76477 #%d: SLF freqency %f\n", chip, sn->slf_freq)); + } + else + sn->slf_freq = 0; +} + +/***************************************************************************** + * set ONESHOT resistor (pin 24) + *****************************************************************************/ +void SN76477_set_oneshot_res(int chip, double res) +{ + struct SN76477 *sn = sn76477[chip]; + + if( res == sn->oneshot_res ) + return; + sn->oneshot_res = res; + sn->oneshot_time = 0.8 * sn->oneshot_res * sn->oneshot_cap; + //bprintf(0,_T("SN76477 #%d: ONE-SHOT(res) time %fs\n"), chip, sn->oneshot_time); +} + +/***************************************************************************** + * set ONESHOT capacitor (pin 23) + *****************************************************************************/ +void SN76477_set_oneshot_cap(int chip, double cap) +{ + struct SN76477 *sn = sn76477[chip]; + + if( cap == sn->oneshot_cap ) + return; + sn->oneshot_cap = cap; + sn->oneshot_time = 0.8 * sn->oneshot_res * sn->oneshot_cap; + //bprintf(0,_T("SN76477 #%d: ONE-SHOT(cap) time %fs\n"), chip, sn->oneshot_time); +} + +void SN76477_set_mastervol(int chip, double vol) +{ + struct SN76477 *sn = sn76477[chip]; + + sn->mastervol = vol; +} + +#define UPDATE_SLF \ + /************************************* \ + * SLF super low frequency oscillator \ + * frequency = 0.64 / (r_slf * c_slf) \ + *************************************/ \ + sn->slf_count -= sn->slf_freq; \ + while( sn->slf_count <= 0 ) \ + { \ + sn->slf_count += sn->samplerate; \ + sn->slf_out ^= 1; \ + } + +#define UPDATE_VCO \ + /************************************ \ + * VCO voltage controlled oscilator \ + * min. freq = 0.64 / (r_vco * c_vco) \ + * freq. range is approx. 10:1 \ + ************************************/ \ + if( sn->vco_select ) \ + { \ + /* VCO is controlled by SLF */ \ + if( sn->slf_dir == 0 ) \ + { \ + sn->slf_level -= sn->slf_freq * 2 * 5.0 / sn->samplerate; \ + if( sn->slf_level <= 0.0 ) \ + { \ + sn->slf_level = 0.0; \ + sn->slf_dir = 1; \ + } \ + } \ + else \ + if( sn->slf_dir == 1 ) \ + { \ + sn->slf_level += sn->slf_freq * 2 * 5.0 / sn->samplerate; \ + if( sn->slf_level >= 5.0 ) \ + { \ + sn->slf_level = 5.0; \ + sn->slf_dir = 0; \ + } \ + } \ + sn->vco_step = sn->vco_freq * sn->slf_level; \ + } \ + else \ + { \ + /* VCO is controlled by external voltage */ \ + sn->vco_step = sn->vco_freq * sn->vco_voltage; \ + } \ + sn->vco_count -= sn->vco_step; \ + while( sn->vco_count <= 0 ) \ + { \ + sn->vco_count += sn->samplerate; \ + sn->vco_out ^= 1; \ + } + +#define UPDATE_NOISE \ + /************************************* \ + * NOISE pseudo rand number generator \ + *************************************/ \ + if( sn->noise_res > 0 ) \ + sn->noise_poly = ( (sn->noise_poly << 7) + \ + (sn->noise_poly >> 10) + \ + 0x18000 ) & 0x1ffff; \ + \ + /* low pass filter: sample every noise_freq pseudo random value */ \ + sn->noise_count -= sn->noise_freq; \ + while( sn->noise_count <= 0 ) \ + { \ + sn->noise_count = sn->samplerate; \ + sn->noise_out = sn->noise_poly & 1; \ + } + +#define UPDATE_VOLUME \ + /************************************* \ + * VOLUME adjust for attack/decay \ + *************************************/ \ + sn->vol_count -= sn->vol_rate; \ + if( sn->vol_count <= 0 ) \ + { \ + int n = - sn->vol_count / sn->samplerate + 1; /* number of steps */ \ + sn->vol_count += n * sn->samplerate; \ + sn->vol += n * sn->vol_step; \ + if( sn->vol < VMIN ) sn->vol = VMIN; \ + if( sn->vol > VMAX ) sn->vol = VMAX; \ + LOG(3,("SN76477 #%d: vol = $%04X\n", chip, sn->vol)); \ + } + + +/***************************************************************************** + * mixer select 0 0 0 : VCO + *****************************************************************************/ +static void SN76477_update_0(int chip, INT16 *buffer, int length) +{ + struct SN76477 *sn = sn76477[chip]; + while( length-- ) + { + UPDATE_VCO; + UPDATE_VOLUME; + INT16 sam = (sn->vco_out ? sn->vol_lookup[sn->vol-VMIN] : -sn->vol_lookup[sn->vol-VMIN]);// * SNMASTERVOL; + *buffer = BURN_SND_CLIP(*buffer + sam); + buffer++; + *buffer = BURN_SND_CLIP(*buffer + sam); + buffer++; + sntimer_tick(chip); + } +} + +/***************************************************************************** + * mixer select 0 0 1 : SLF + *****************************************************************************/ +static void SN76477_update_1(int chip, INT16 *buffer, int length) +{ + struct SN76477 *sn = sn76477[chip]; + while( length-- ) + { + UPDATE_SLF; + UPDATE_VOLUME; + INT16 sam = (sn->slf_out ? sn->vol_lookup[sn->vol-VMIN] : -sn->vol_lookup[sn->vol-VMIN]);// * SNMASTERVOL; + *buffer = BURN_SND_CLIP(*buffer + sam); + buffer++; + *buffer = BURN_SND_CLIP(*buffer + sam); + buffer++; + sntimer_tick(chip); + } +} + +/***************************************************************************** + * mixer select 0 1 0 : NOISE + *****************************************************************************/ +static void SN76477_update_2(int chip, INT16 *buffer, int length) +{ + struct SN76477 *sn = sn76477[chip]; + while( length-- ) + { + UPDATE_NOISE; + UPDATE_VOLUME; + INT16 sam = (sn->noise_out ? sn->vol_lookup[sn->vol-VMIN] : -sn->vol_lookup[sn->vol-VMIN]);// * SNMASTERVOL; + *buffer = BURN_SND_CLIP(*buffer + sam); + buffer++; + *buffer = BURN_SND_CLIP(*buffer + sam); + buffer++; + sntimer_tick(chip); + } +} + +/***************************************************************************** + * mixer select 0 1 1 : VCO and NOISE + *****************************************************************************/ +static void SN76477_update_3(int chip, INT16 *buffer, int length) +{ + struct SN76477 *sn = sn76477[chip]; + while( length-- ) + { + UPDATE_VCO; + UPDATE_NOISE; + UPDATE_VOLUME; + INT16 sam = ((sn->vco_out & sn->noise_out) ? sn->vol_lookup[sn->vol-VMIN] : -sn->vol_lookup[sn->vol-VMIN]);// * SNMASTERVOL; + *buffer = BURN_SND_CLIP(*buffer + sam); + buffer++; + *buffer = BURN_SND_CLIP(*buffer + sam); + buffer++; + sntimer_tick(chip); + } +} + +/***************************************************************************** + * mixer select 1 0 0 : SLF and NOISE + *****************************************************************************/ +static void SN76477_update_4(int chip, INT16 *buffer, int length) +{ + struct SN76477 *sn = sn76477[chip]; + while( length-- ) + { + UPDATE_SLF; + UPDATE_NOISE; + UPDATE_VOLUME; + INT16 sam = ((sn->slf_out & sn->noise_out) ? sn->vol_lookup[sn->vol-VMIN] : -sn->vol_lookup[sn->vol-VMIN]);// * SNMASTERVOL; + *buffer = BURN_SND_CLIP(*buffer + sam); + buffer++; + *buffer = BURN_SND_CLIP(*buffer + sam); + buffer++; + sntimer_tick(chip); + } +} + +/***************************************************************************** + * mixer select 1 0 1 : VCO, SLF and NOISE + *****************************************************************************/ +static void SN76477_update_5(int chip, INT16 *buffer, int length) +{ + struct SN76477 *sn = sn76477[chip]; + while( length-- ) + { + UPDATE_SLF; + UPDATE_VCO; + UPDATE_NOISE; + UPDATE_VOLUME; + INT16 sam = ((sn->vco_out & sn->slf_out & sn->noise_out) ? sn->vol_lookup[sn->vol-VMIN] : -sn->vol_lookup[sn->vol-VMIN]);// * SNMASTERVOL; + *buffer = BURN_SND_CLIP(*buffer + sam); + buffer++; + *buffer = BURN_SND_CLIP(*buffer + sam); + buffer++; + sntimer_tick(chip); + } +} + +/***************************************************************************** + * mixer select 1 1 0 : VCO and SLF + *****************************************************************************/ +static void SN76477_update_6(int chip, INT16 *buffer, int length) +{ + struct SN76477 *sn = sn76477[chip]; + while( length-- ) + { + UPDATE_SLF; + UPDATE_VCO; + UPDATE_VOLUME; + INT16 sam = ((sn->vco_out & sn->slf_out) ? sn->vol_lookup[sn->vol-VMIN] : -sn->vol_lookup[sn->vol-VMIN]);// * SNMASTERVOL; + *buffer = BURN_SND_CLIP(*buffer + sam); + buffer++; + *buffer = BURN_SND_CLIP(*buffer + sam); + buffer++; + sntimer_tick(chip); + } +} + +/***************************************************************************** + * mixer select 1 1 1 : Inhibit + *****************************************************************************/ +static void SN76477_update_7(int chip, INT16 *buffer, int length) +{ + /*while( length-- ) { + *buffer++ = 0; + *buffer++ = 0; + }*/ +} + +void SN76477_sound_update(int param, INT16 *buffer, int length) +{ + struct SN76477 *sn = sn76477[param]; + if( sn->enable ) + { + SN76477_update_7(param,buffer,length); + } + else + { + switch( sn->mixer ) + { + case 0: + SN76477_update_0(param,buffer,length); + break; + case 1: + SN76477_update_1(param,buffer,length); + break; + case 2: + SN76477_update_2(param,buffer,length); + break; + case 3: + SN76477_update_3(param,buffer,length); + break; + case 4: + SN76477_update_4(param,buffer,length); + break; + case 5: + SN76477_update_5(param,buffer,length); + break; + case 6: + SN76477_update_6(param,buffer,length); + break; + default: + SN76477_update_7(param,buffer,length); + break; + } + } +#if 0 + if (sn76477[param]->oneshot_timer > 0.0) + bprintf(0, _T("ost: %fs, "), sn76477[param]->oneshot_timer); + if (sn76477[param]->envelope_timer > 0.0) + bprintf(0, _T("envt: %fs, "), sn76477[param]->envelope_timer); +#endif +} + +void SN76477_exit(int num) // yea, needs work. I know.. +{ + if (sn76477[num]) { + free(sn76477[num]); + sn76477[num] = NULL; + } +} + +void SN76477_init(int num) +{ + sn76477[num] = (SN76477 *)malloc(sizeof(struct SN76477)); + if( !sn76477[num] ) + { + LOG(0,("%s failed to malloc struct SN76477\n", name)); + return; + } + memset(sn76477[num], 0, sizeof(struct SN76477)); + + sn76477[num]->samplerate = nBurnSoundRate; + + sn76477[num]->envelope_timer = 0; + sn76477[num]->oneshot_timer = 0; + sn76477[num]->mastervol = 1.00; + + SN76477_mixer_w(num, 0x07); /* turn off mixing */ + SN76477_envelope_w(num, 0x03); /* envelope inputs open */ + SN76477_enable_w(num, 0x01); /* enable input open */ +} diff --git a/src/burn/snd/sn76477.h b/src/burn/snd/sn76477.h new file mode 100644 index 000000000..0561094d4 --- /dev/null +++ b/src/burn/snd/sn76477.h @@ -0,0 +1,82 @@ +/***************************************************************************** + SN76477 pins and assigned interface variables/functions + + SN76477_envelope_w() + / \ + [ 1] ENV SEL 1 ENV SEL 2 [28] + [ 2] GND MIXER C [27] \ + SN76477_noise_w() [ 3] NOISE EXT OSC MIXER A [26] > SN76477_mixer_w() + noise_res [ 4] RES NOISE OSC MIXER B [25] / + filter_res [ 5] NOISE FILTER RES O/S RES [24] oneshot_res + filter_cap [ 6] NOISE FILTER CAP O/S CAP [23] oneshot_cap + decay_res [ 7] DECAY RES VCO SEL [22] SN76477_vco_w() + attack_decay_cap [ 8] A/D CAP SLF CAP [21] slf_cap + SN76477_enable_w() [ 9] ENABLE SLF RES [20] slf_res + attack_res [10] ATTACK RES PITCH [19] pitch_voltage + amplitude_res [11] AMP VCO RES [18] vco_res + feedback_res [12] FEEDBACK VCO CAP [17] vco_cap + [13] OUTPUT VCO EXT CONT [16] vco_voltage + [14] Vcc +5V REG OUT [15] + + All resistor values in Ohms. + All capacitor values in Farads. + Use RES_K, RES_M and CAP_U, CAP_N, CAP_P macros to convert + magnitudes, eg. 220k = RES_K(220), 47nF = CAP_N(47) + + *****************************************************************************/ + +#define MAX_SN76477 4 + +/* Little helpers for magnitude conversions */ +#define RES_K(res) ((double)res*1e3) +#define RES_M(res) ((double)res*1e6) +#define CAP_U(cap) ((double)cap*1e-6) +#define CAP_N(cap) ((double)cap*1e-9) +#define CAP_P(cap) ((double)cap*1e-12) +#define TIME_IN_HZ(hz) (1.0 / (double)(hz)) + +/* Noise clock write, useful only if noise_res is zero */ +extern void SN76477_noise_clock_w(int chip, int data); + +/* Enable (one input line: 0 enabled, 1 inhibited) - resets one shot */ +extern void SN76477_enable_w(int chip, int data); + +/* Mixer select (three input lines, data 0 to 7) */ +extern void SN76477_mixer_w(int chip, int data); + +/* Alternatively write the single input lines */ +extern void SN76477_mixer_a_w(int chip, int data); +extern void SN76477_mixer_b_w(int chip, int data); +extern void SN76477_mixer_c_w(int chip, int data); + +/* Select envelope (two input lines, data 0 to 3) */ +extern void SN76477_envelope_w(int chip, int data); + +/* Alternatively use the single input lines */ +extern void SN76477_envelope_1_w(int chip, int data); +extern void SN76477_envelope_2_w(int chip, int data); + +/* VCO select (one input line: 0 external control, 1: SLF control) */ +extern void SN76477_vco_w(int chip, int data); + +void SN76477_set_noise_res(int chip, double res); +void SN76477_set_filter_res(int chip, double res); +void SN76477_set_filter_cap(int chip, double cap); +void SN76477_set_decay_res(int chip, double res); +void SN76477_set_attack_decay_cap(int chip, double cap); +void SN76477_set_attack_res(int chip, double res); +void SN76477_set_amplitude_res(int chip, double res); +void SN76477_set_feedback_res(int chip, double res); +void SN76477_set_slf_res(int chip, double res); +void SN76477_set_slf_cap(int chip, double cap); +void SN76477_set_oneshot_res(int chip, double res); +void SN76477_set_oneshot_cap(int chip, double cap); +void SN76477_set_vco_res(int chip, double res); +void SN76477_set_vco_cap(int chip, double cap); +void SN76477_set_pitch_voltage(int chip, double voltage); +void SN76477_set_vco_voltage(int chip, double voltage); +void SN76477_set_mastervol(int chip, double vol); +void SN76477_sound_update(int param, INT16 *buffer, int length); + +void SN76477_init(int num); +void SN76477_exit(int num);