summaryrefslogtreecommitdiffstatshomepage
path: root/src/emu/sound/sid.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/emu/sound/sid.c')
-rw-r--r--src/emu/sound/sid.c364
1 files changed, 364 insertions, 0 deletions
diff --git a/src/emu/sound/sid.c b/src/emu/sound/sid.c
new file mode 100644
index 00000000000..b6825add8a4
--- /dev/null
+++ b/src/emu/sound/sid.c
@@ -0,0 +1,364 @@
+/*
+ copyright peter trauner 2000
+
+ based on michael schwend's sid play
+
+ Noise generation algorithm is used courtesy of Asger Alstrup Nielsen.
+ His original publication can be found on the SID home page.
+
+ Noise table optimization proposed by Phillip Wooller. The output of
+ each table does not differ.
+
+ MOS-8580 R5 combined waveforms recorded by Dennis "Deadman" Lindroos.
+*/
+
+#include <math.h>
+
+#include "sndintrf.h"
+#include "sidvoice.h"
+#include "sidenvel.h"
+#include "sid.h"
+
+static float *filterTable;
+static float *bandPassParam;
+#define lowPassParam filterTable
+float filterResTable[16];
+
+#define maxLogicalVoices 4
+
+static const int mix16monoMiddleIndex = 256*maxLogicalVoices/2;
+static UINT16 mix16mono[256*maxLogicalVoices];
+
+static UINT16 zero16bit=0; /* either signed or unsigned */
+UINT32 splitBufferLen;
+
+void MixerInit(int threeVoiceAmplify)
+{
+ long si;
+ UINT16 ui;
+ long ampDiv = maxLogicalVoices;
+
+ if (threeVoiceAmplify)
+ {
+ ampDiv = (maxLogicalVoices-1);
+ }
+
+ /* Mixing formulas are optimized by sample input value. */
+
+ si = (-128*maxLogicalVoices) * 256;
+ for (ui = 0; ui < sizeof(mix16mono)/sizeof(UINT16); ui++ )
+ {
+ mix16mono[ui] = (UINT16)(si/ampDiv) + zero16bit;
+ si+=256;
+ }
+
+}
+
+
+INLINE void syncEm(SID6581 *This)
+{
+ int sync1 = (This->optr1.modulator->cycleLenCount <= 0);
+ int sync2 = (This->optr2.modulator->cycleLenCount <= 0);
+ int sync3 = (This->optr3.modulator->cycleLenCount <= 0);
+
+ This->optr1.cycleLenCount--;
+ This->optr2.cycleLenCount--;
+ This->optr3.cycleLenCount--;
+
+ if (This->optr1.sync && sync1)
+ {
+ This->optr1.cycleLenCount = 0;
+ This->optr1.outProc = &sidWaveCalcNormal;
+#if defined(DIRECT_FIXPOINT)
+ optr1.waveStep.l = 0;
+#else
+ This->optr1.waveStep = (This->optr1.waveStepPnt = 0);
+#endif
+ }
+ if (This->optr2.sync && sync2)
+ {
+ This->optr2.cycleLenCount = 0;
+ This->optr2.outProc = &sidWaveCalcNormal;
+#if defined(DIRECT_FIXPOINT)
+ This->optr2.waveStep.l = 0;
+#else
+ This->optr2.waveStep = (This->optr2.waveStepPnt = 0);
+#endif
+ }
+ if (This->optr3.sync && sync3)
+ {
+ This->optr3.cycleLenCount = 0;
+ This->optr3.outProc = &sidWaveCalcNormal;
+#if defined(DIRECT_FIXPOINT)
+ optr3.waveStep.l = 0;
+#else
+ This->optr3.waveStep = (This->optr3.waveStepPnt = 0);
+#endif
+ }
+}
+
+
+void sidEmuFillBuffer(SID6581 *This, stream_sample_t *buffer, UINT32 bufferLen )
+{
+//void* fill16bitMono( SID6581 *This, void* buffer, UINT32 numberOfSamples )
+
+ for ( ; bufferLen > 0; bufferLen-- )
+ {
+ *buffer++ = (INT16) mix16mono[(unsigned)(mix16monoMiddleIndex
+ +(*This->optr1.outProc)(&This->optr1)
+ +(*This->optr2.outProc)(&This->optr2)
+ +(This->optr3.outProc(&This->optr3)&This->optr3_outputmask)
+/* hack for digi sounds
+ does n't seam to come from a tone operator
+ ghostbusters and goldrunner everything except volume zeroed */
+ +(This->masterVolume<<2)
+// +(*sampleEmuRout)()
+ )];
+ syncEm(This);
+ }
+}
+
+/* --------------------------------------------------------------------- Init */
+
+
+/* Reset. */
+
+int sidEmuReset(SID6581 *This)
+{
+ sidClearOperator( &This->optr1 );
+ enveEmuResetOperator( &This->optr1 );
+ sidClearOperator( &This->optr2 );
+ enveEmuResetOperator( &This->optr2 );
+ sidClearOperator( &This->optr3 );
+ enveEmuResetOperator( &This->optr3 );
+ This->optr3_outputmask = ~0; /* on */
+
+// sampleEmuReset();
+
+ This->filter.Type = (This->filter.CurType = 0);
+ This->filter.Value = 0;
+ This->filter.Dy = (This->filter.ResDy = 0);
+
+ sidEmuSet( &This->optr1 );
+ sidEmuSet( &This->optr2 );
+ sidEmuSet( &This->optr3 );
+
+ sidEmuSet2( &This->optr1 );
+ sidEmuSet2( &This->optr2 );
+ sidEmuSet2( &This->optr3 );
+
+ return TRUE;
+}
+
+
+void filterTableInit(void)
+{
+ UINT16 uk;
+ /* Parameter calculation has not been moved to a separate function */
+ /* by purpose. */
+ const float filterRefFreq = 44100.0;
+
+ float yMax = 1.0;
+ float yMin = 0.01;
+ float yAdd;
+ float yTmp, rk, rk2;
+
+ float resDyMax;
+ float resDyMin;
+ float resDy;
+
+ filterTable = auto_malloc(sizeof(*filterTable) * 0x800);
+ bandPassParam = auto_malloc(sizeof(*bandPassParam) * 0x800);
+
+ uk = 0;
+ for ( rk = 0; rk < 0x800; rk++ )
+ {
+ filterTable[uk] = (((exp(rk/0x800*log(400.0))/60.0)+0.05)
+ *filterRefFreq) / Machine->sample_rate;
+ if ( filterTable[uk] < yMin )
+ filterTable[uk] = yMin;
+ if ( filterTable[uk] > yMax )
+ filterTable[uk] = yMax;
+ uk++;
+ }
+
+ /*extern float bandPassParam[0x800]; */
+ yMax = 0.22;
+ yMin = 0.05; /* less for some R1/R4 chips */
+ yAdd = (yMax-yMin)/2048.0;
+ yTmp = yMin;
+ uk = 0;
+ /* Some C++ compilers still have non-local scope! */
+ for ( rk2 = 0; rk2 < 0x800; rk2++ )
+ {
+ bandPassParam[uk] = (yTmp*filterRefFreq) / Machine->sample_rate;
+ yTmp += yAdd;
+ uk++;
+ }
+
+ /*extern float filterResTable[16]; */
+ resDyMax = 1.0;
+ resDyMin = 2.0;
+ resDy = resDyMin;
+ for ( uk = 0; uk < 16; uk++ )
+ {
+ filterResTable[uk] = resDy;
+ resDy -= (( resDyMin - resDyMax ) / 15 );
+ }
+ filterResTable[0] = resDyMin;
+ filterResTable[15] = resDyMax;
+}
+
+void sid6581_init (SID6581 *This)
+{
+ This->optr1.sid=This;
+ This->optr2.sid=This;
+ This->optr3.sid=This;
+
+ This->optr1.modulator = &This->optr3;
+ This->optr3.carrier = &This->optr1;
+ This->optr1.filtVoiceMask = 1;
+
+ This->optr2.modulator = &This->optr1;
+ This->optr1.carrier = &This->optr2;
+ This->optr2.filtVoiceMask = 2;
+
+ This->optr3.modulator = &This->optr2;
+ This->optr2.carrier = &This->optr3;
+ This->optr3.filtVoiceMask = 4;
+
+
+
+ This->PCMsid = (UINT32)(This->PCMfreq * (16777216.0 / This->clock));
+ This->PCMsidNoise = (UINT32)((This->clock*256.0)/This->PCMfreq);
+
+ This->filter.Enabled = TRUE;
+
+ sidInitMixerEngine();
+ filterTableInit();
+
+ sidInitWaveformTables(This->type);
+
+ enveEmuInit(This->PCMfreq, TRUE);
+
+ MixerInit(0);
+
+ sidEmuReset(This);
+}
+
+void sid6581_port_w (SID6581 *This, int offset, int data)
+{
+ offset &= 0x1f;
+
+ switch (offset)
+ {
+ case 0x19: case 0x1a: case 0x1b: case 0x1c:
+ case 0x1d:
+ case 0x1e:
+ case 0x1f:
+ break;
+ case 0x15: case 0x16: case 0x17:
+ case 0x18:
+ stream_update(This->mixer_channel);
+ This->reg[offset] = data;
+ This->masterVolume = ( This->reg[0x18] & 15 );
+ This->masterVolumeAmplIndex = This->masterVolume << 8;
+
+ if ((This->reg[0x18]&0x80) &&
+ ((This->reg[0x17]&This->optr3.filtVoiceMask)==0))
+ This->optr3_outputmask = 0; /* off */
+ else
+ This->optr3_outputmask = ~0; /* on */
+
+ This->filter.Type = This->reg[0x18] & 0x70;
+ if (This->filter.Type != This->filter.CurType)
+ {
+ This->filter.CurType = This->filter.Type;
+ This->optr1.filtLow = (This->optr1.filtRef = 0);
+ This->optr2.filtLow = (This->optr2.filtRef = 0);
+ This->optr3.filtLow = (This->optr3.filtRef = 0);
+ }
+ if ( This->filter.Enabled )
+ {
+ This->filter.Value = 0x7ff & ( (This->reg[0x15]&7) | ( (UINT16)This->reg[0x16] << 3 ));
+ if (This->filter.Type == 0x20)
+ This->filter.Dy = bandPassParam ? bandPassParam[This->filter.Value] : 0.0;
+ else
+ This->filter.Dy = lowPassParam ? lowPassParam[This->filter.Value] : 0.0;
+ This->filter.ResDy = filterResTable[This->reg[0x17] >> 4] - This->filter.Dy;
+ if ( This->filter.ResDy < 1.0 )
+ This->filter.ResDy = 1.0;
+ }
+
+ sidEmuSet( &This->optr1 );
+ sidEmuSet( &This->optr3 );
+ sidEmuSet( &This->optr2 );
+
+ // relies on sidEmuSet also for other channels!
+ sidEmuSet2( &This->optr1 );
+ sidEmuSet2( &This->optr2 );
+ sidEmuSet2( &This->optr3 );
+ break;
+
+ default:
+ stream_update(This->mixer_channel);
+ This->reg[offset] = data;
+
+ if (offset<7) {
+ This->optr1.reg[offset] = data;
+ } else if (offset<14) {
+ This->optr2.reg[offset-7] = data;
+ } else if (offset<21) {
+ This->optr3.reg[offset-14] = data;
+ }
+
+ sidEmuSet( &This->optr1 );
+ sidEmuSet( &This->optr3 );
+ sidEmuSet( &This->optr2 );
+
+ // relies on sidEmuSet also for other channels!
+ sidEmuSet2( &This->optr1 );
+ sidEmuSet2( &This->optr2 );
+ sidEmuSet2( &This->optr3 );
+ break;
+ }
+}
+
+int sid6581_port_r (SID6581 *This, int offset)
+{
+ int data;
+/* SIDPLAY reads last written at a sid address value */
+ offset &= 0x1f;
+ switch (offset)
+ {
+ case 0x1d:
+ case 0x1e:
+ case 0x1f:
+ data=0xff;
+ break;
+ case 0x19: /* paddle 1 */
+ if (This->ad_read != NULL)
+ data=This->ad_read (0);
+ else
+ data=0;
+ break;
+ case 0x1a: /* paddle 2 */
+ if (This->ad_read != NULL)
+ data=This->ad_read (1);
+ else
+ data=0;
+ break;
+ case 0x1b:
+ stream_update(This->mixer_channel);
+ data = This->optr3.output;
+ break;
+ case 0x1c:
+ stream_update(This->mixer_channel);
+ data = This->optr3.enveVol;
+ break;
+ default:
+ data=This->reg[offset];
+ }
+ return data;
+}
+