/*========================================================================= */
/* This source implements the ADSR volume envelope of the SID-chip. */
/* Two different envelope shapes are implemented, an exponential */
/* approximation and the linear shape, which can easily be determined */
/* by reading the registers of the third SID operator. */
/* */
/* Accurate volume envelope times as of November 1994 are used */
/* courtesy of George W. Taylor <aa601@cfn.cs.dal.ca>, <yurik@io.org> */
/* They are slightly modified. */
/* */
/* To use the rounded envelope times from the C64 Programmers Reference */
/* Book define SID_REFTIMES at the Makefile level. */
/* */
/* To perform realtime calculations with floating point precision define */
/* SID_FPUENVE at the Makefile level. On high-end FPUs (not Pentium !), */
/* this can result in speed improvement. Default is integer fixpoint. */
/* */
/* Global Makefile definables: */
/* */
/* DIRECT_FIXPOINT - use a union to access integer fixpoint operands */
/* in memory. This makes an assumption about the */
/* hardware and software architecture and therefore */
/* is considered a hack ! */
/* */
/* Local (or Makefile) definables: */
/* */
/* SID_REFTIMES - use rounded envelope times */
/* SID_FPUENVE - use floating point precision for calculations */
/* (will override the global DIRECT_FIXPOINT setting !) */
/* */
/*========================================================================= */
#include "emu.h"
#include "sidvoice.h"
#include "sid.h"
#include "side6581.h"
#include "sidenvel.h"
const UINT8 masterVolumeLevels[16] =
{
0, 17, 34, 51, 68, 85, 102, 119,
136, 153, 170, 187, 204, 221, 238, 255
};
static UINT16 masterAmplModTable[16*256];
static const float attackTimes[16] =
{
/* milliseconds */
#if defined(SID_REFTIMES)
2.0f, 8.0f, 16.0f, 24.0f, 38.0f, 56.0f, 68.0f, 80.0f,
100.0f, 250.0f, 500.0f, 800.0f, 1000.0f, 3000.0f, 5000.0f, 8000.0f
#else
2.2528606f, 8.0099577f, 15.7696042f, 23.7795619f, 37.2963655f, 55.0684591f,
66.8330845f, 78.3473987f,
98.1219818f, 244.554021f, 489.108042f, 782.472742f, 977.715461f, 2933.64701f,
4889.07793f, 7822.72493f
#endif
};
static const float decayReleaseTimes[16] =
{
/* milliseconds */
#if defined(SID_REFTIMES)
8.0f, 24.0f, 48.0f, 72.0f, 114.0f, 168.0f, 204.0f, 240.0f,
300.0f, 750.0f, 1500.0f, 2400.0f, 3000.0f, 9000.0f, 15000.0f, 24000.0f
#else
8.91777693f, 24.594051f, 48.4185907f, 73.0116639f, 114.512475f, 169.078356f,
205.199432f, 240.551975f,
301.266125f, 750.858245f, 1501.71551f, 2402.43682f, 3001.89298f, 9007.21405f,
15010.998f, 24018.2111f
#endif
};
#ifdef SID_FPUENVE
static float attackRates[16];
static float decayReleaseRates[16];
#elif defined(DIRECT_FIXPOINT)
static UINT32 attackRates[16];
static UINT32 decayReleaseRates[16];
#else
static UINT32 attackRates[16];
static UINT32 attackRatesP[16];
static UINT32 decayReleaseRates[16];
static UINT32 decayReleaseRatesP[16];
#endif
static const UINT32 attackTabLen = 255;
static UINT32 releaseTabLen;
static UINT32 releasePos[256];
void enveEmuInit( UINT32 updateFreq, int measuredValues )
{
UINT32 i, j, k;
releaseTabLen = sizeof(releaseTab);
for ( i = 0; i < 256; i++ )
{
j = 0;
while (( j < releaseTabLen ) && (releaseTab[j] > i) )
{
j++;
}
if ( j < releaseTabLen )
{
releasePos[i] = j;
}
else
{
releasePos[i] = releaseTabLen -1;
}
}
k = 0;
for ( i = 0; i < 16; i++ )
{
for ( j = 0; j < 256; j++ )
{
UINT16 tmpVol = j;
if (measuredValues)
{
tmpVol = (UINT16) ((293.0*(1-exp(j/-130.0)))+4.0);
if (j == 0)
tmpVol = 0;
if (tmpVol > 255)
tmpVol = 255;
}
/* Want the modulated volume value in the high byte. */
masterAmplModTable[k++] = ((tmpVol * masterVolumeLevels[i]) / 255) << 8;
}
}
for ( i = 0; i < 16; i++ )
{
#ifdef SID_FPUENVE
double scaledenvelen = floor(( attackTimes[i] * updateFreq ) / 1000UL );
if (scaledenvelen == 0)
scaledenvelen = 1;
attackRates[i] = attackTabLen / scaledenvelen;
scaledenvelen = floor(( decayReleaseTimes[i] * updateFreq ) / 1000UL );
if (scaledenvelen == 0)
scaledenvelen = 1;
decayReleaseRates[i] = releaseTabLen / scaledenvelen;
#elif defined(DIRECT_FIXPOINT)
UINT32 scaledenvelen = (UINT32)floor(( attackTimes[i] * updateFreq ) / 1000UL );
if (scaledenvelen == 0)
scaledenvelen = 1;
attackRates[i] = (attackTabLen << 16) / scaledenvelen;
scaledenvelen = (UINT32)floor(( decayReleaseTimes[i] * updateFreq ) / 1000UL );
if (scaledenvelen == 0)
scaledenvelen = 1;
decayReleaseRates[i] = (releaseTabLen << 16) / scaledenvelen;
#else
UINT32 scaledenvelen = (UINT32)(/*floor*/(( attackTimes[i] * updateFreq ) / 1000UL ));
if (scaledenvelen == 0)
scaledenvelen = 1;
attackRates[i] = attackTabLen / scaledenvelen;
attackRatesP[i] = (( attackTabLen % scaledenvelen ) * 65536UL ) / scaledenvelen;
scaledenvelen = (UINT32)(/*floor*/(( decayReleaseTimes[i] * updateFreq ) / 1000UL ));
if (scaledenvelen == 0)
scaledenvelen = 1;
decayReleaseRates[i] = releaseTabLen / scaledenvelen;
decayReleaseRatesP[i] = (( releaseTabLen % scaledenvelen ) * 65536UL ) / scaledenvelen;
#endif
}
}
/* Reset op. */
void enveEmuResetOperator(sidOperator* pVoice)
{
/* mute, end of R-phase */
pVoice->ADSRctrl = ENVE_MUTE;
// pVoice->gateOnCtrl = (pVoice->gateOffCtrl = false);
#ifdef SID_FPUENVE
pVoice->fenveStep = (pVoice->fenveStepAdd = 0);
pVoice->enveStep = 0;
#elif defined(DIRECT_FIXPOINT)
pVoice->enveStep.l = (pVoice->enveStepAdd.l = 0);
#else
pVoice->enveStep = (pVoice->enveStepPnt = 0);
pVoice->enveStepAdd = (pVoice->enveStepAddPnt = 0);
#endif
pVoice->enveSusVol = 0;
pVoice->enveVol = 0;
pVoice->enveShortAttackCount = 0;
}
INLINE UINT16 enveEmuStartAttack(sidOperator*);
INLINE UINT16 enveEmuStartDecay(sidOperator*);
INLINE UINT16 enveEmuStartRelease(sidOperator*);
INLINE UINT16 enveEmuAlterAttack(sidOperator*);
INLINE UINT16 enveEmuAlterDecay(sidOperator*);
INLINE UINT16 enveEmuAlterSustain(sidOperator*);
INLINE UINT16 enveEmuAlterSustainDecay(sidOperator*);
INLINE UINT16 enveEmuAlterRelease(sidOperator*);
INLINE UINT16 enveEmuAttack(sidOperator*);
INLINE UINT16 enveEmuDecay(sidOperator*);
INLINE UINT16 enveEmuSustain(sidOperator*);
INLINE UINT16 enveEmuSustainDecay(sidOperator*);
INLINE UINT16 enveEmuRelease(sidOperator*);
INLINE UINT16 enveEmuMute(sidOperator*);
INLINE UINT16 enveEmuStartShortAttack(sidOperator*);
INLINE UINT16 enveEmuAlterShortAttack(sidOperator*);
INLINE UINT16 enveEmuShortAttack(sidOperator*);
const ptr2sidUwordFunc enveModeTable[] =
{
/* 0 */
&enveEmuStartAttack, &enveEmuStartRelease,
&enveEmuAttack, &enveEmuDecay, &enveEmuSustain, &enveEmuRelease,
&enveEmuSustainDecay, &enveEmuMute,
/* 16 */
&enveEmuStartShortAttack,
&enveEmuMute, &enveEmuMute, &enveEmuMute,
&enveEmuMute, &enveEmuMute, &enveEmuMute, &enveEmuMute,
/* 32 */
&enveEmuStartAttack, &enveEmuStartRelease,
&enveEmuAlterAttack, &enveEmuAlterDecay, &enveEmuAlterSustain, &enveEmuAlterRelease,
&enveEmuAlterSustainDecay, &enveEmuMute,
/* 48 */
&enveEmuStartShortAttack,
&enveEmuMute, &enveEmuMute, &enveEmuMute,
&enveEmuMute, &enveEmuMute, &enveEmuMute, &enveEmuMute
};
/* Real-time functions. */
/* Order is important because of inline optimizations. */
/* */
/* ADSRctrl is (index*2) to enveModeTable[], because of KEY-bit. */
INLINE void enveEmuEnveAdvance(sidOperator* pVoice)
{
#ifdef SID_FPUENVE
pVoice->fenveStep += pVoice->fenveStepAdd;
#elif defined(DIRECT_FIXPOINT)
pVoice->enveStep.l += pVoice->enveStepAdd.l;
#else
pVoice->enveStepPnt += pVoice->enveStepAddPnt;
pVoice->enveStep += pVoice->enveStepAdd + ( pVoice->enveStepPnt > 65535 );
pVoice->enveStepPnt &= 0xFFFF;
#endif
}
/* */
/* Mute/Idle. */
/* */
/* Only used in the beginning. */
INLINE UINT16 enveEmuMute(sidOperator* pVoice)
{
return 0;
}
/* */
/* Release */
/* */
INLINE UINT16 enveEmuRelease(sidOperator* pVoice)
{
#ifdef SID_FPUENVE
pVoice->enveStep = (UINT16)pVoice->fenveStep;
#endif
#if defined(DIRECT_FIXPOINT) && !defined(SID_FPUENVE)
if ( pVoice->enveStep.w[HI] >= releaseTabLen )
#else
if ( pVoice->enveStep >= releaseTabLen )
#endif
{
pVoice->enveVol = releaseTab[releaseTabLen -1];
return masterAmplModTable[ pVoice->sid->masterVolumeAmplIndex + pVoice->enveVol ];
}
else
{
#if defined(DIRECT_FIXPOINT) && !defined(SID_FPUENVE)
pVoice->enveVol = releaseTab[pVoice->enveStep.w[HI]];
#else
pVoice->enveVol = releaseTab[pVoice->enveStep];
#endif
enveEmuEnveAdvance(pVoice);
return masterAmplModTable[ pVoice->sid->masterVolumeAmplIndex + pVoice->enveVol ];
}
}
INLINE UINT16 enveEmuAlterRelease(sidOperator* pVoice)
{
UINT8 release = pVoice->SIDSR & 0x0F;
#ifdef SID_FPUENVE
pVoice->fenveStepAdd = decayReleaseRates[release];
#elif defined(DIRECT_FIXPOINT)
pVoice->enveStepAdd.l = decayReleaseRates[release];
#else
pVoice->enveStepAdd = decayReleaseRates[release];
pVoice->enveStepAddPnt = decayReleaseRatesP[release];
#endif
pVoice->ADSRproc = &enveEmuRelease;
return enveEmuRelease(pVoice);
}
INLINE UINT16 enveEmuStartRelease(sidOperator* pVoice)
{
pVoice->ADSRctrl = ENVE_RELEASE;
#ifdef SID_FPUENVE
pVoice->fenveStep = releasePos[pVoice->enveVol];
#elif defined(DIRECT_FIXPOINT)
pVoice->enveStep.w[HI] = releasePos[pVoice->enveVol];
pVoice->enveStep.w[LO] = 0;
#else
pVoice->enveStep = releasePos[pVoice->enveVol];
pVoice->enveStepPnt = 0;
#endif
return enveEmuAlterRelease(pVoice);
}
/* */
/* Sustain */
/* */
INLINE UINT16 enveEmuSustain(sidOperator* pVoice)
{
return masterAmplModTable[pVoice->sid->masterVolumeAmplIndex+pVoice->enveVol];
}
INLINE UINT16 enveEmuSustainDecay(sidOperator* pVoice)
{
#ifdef SID_FPUENVE
pVoice->enveStep = (UINT16)pVoice->fenveStep;
#endif
#if defined(DIRECT_FIXPOINT) && !defined(SID_FPUENVE)
if ( pVoice->enveStep.w[HI] >= releaseTabLen )
#else
if ( pVoice->enveStep >= releaseTabLen )
#endif
{
pVoice->enveVol = releaseTab[releaseTabLen-1];
return enveEmuAlterSustain(pVoice);
}
else
{
#if defined(DIRECT_FIXPOINT) && !defined(SID_FPUENVE)
pVoice->enveVol = releaseTab[pVoice->enveStep.w[HI]];
#else
pVoice->enveVol = releaseTab[pVoice->enveStep];
#endif
/* Will be controlled from sidEmuSet2(). */
if ( pVoice->enveVol <= pVoice->enveSusVol )
{
pVoice->enveVol = pVoice->enveSusVol;
return enveEmuAlterSustain(pVoice);
}
else
{
enveEmuEnveAdvance(pVoice);
return masterAmplModTable[ pVoice->sid->masterVolumeAmplIndex + pVoice->enveVol ];
}
}
}
/* This is the same as enveEmuStartSustainDecay(). */
INLINE UINT16 enveEmuAlterSustainDecay(sidOperator* pVoice)
{
UINT8 decay = pVoice->SIDAD & 0x0F ;
#ifdef SID_FPUENVE
pVoice->fenveStepAdd = decayReleaseRates[decay];
#elif defined(DIRECT_FIXPOINT)
pVoice->enveStepAdd.l = decayReleaseRates[decay];
#else
pVoice->enveStepAdd = decayReleaseRates[decay];
pVoice->enveStepAddPnt = decayReleaseRatesP[decay];
#endif
pVoice->ADSRproc = &enveEmuSustainDecay;
return enveEmuSustainDecay(pVoice);
}
/* This is the same as enveEmuStartSustain(). */
INLINE UINT16 enveEmuAlterSustain(sidOperator* pVoice)
{
if ( pVoice->enveVol > pVoice->enveSusVol )
{
pVoice->ADSRctrl = ENVE_SUSTAINDECAY;
pVoice->ADSRproc = &enveEmuSustainDecay;
return enveEmuAlterSustainDecay(pVoice);
}
else
{
pVoice->ADSRctrl = ENVE_SUSTAIN;
pVoice->ADSRproc = &enveEmuSustain;
return enveEmuSustain(pVoice);
}
}
/* */
/* Decay */
/* */
INLINE UINT16 enveEmuDecay(sidOperator* pVoice)
{
#ifdef SID_FPUENVE
pVoice->enveStep = (UINT16)pVoice->fenveStep;
#endif
#if defined(DIRECT_FIXPOINT) && !defined(SID_FPUENVE)
if ( pVoice->enveStep.w[HI] >= releaseTabLen )
#else
if ( pVoice->enveStep >= releaseTabLen )
#endif
{
pVoice->enveVol = pVoice->enveSusVol;
return enveEmuAlterSustain(pVoice); /* start sustain */
}
else
{
#if defined(DIRECT_FIXPOINT) && !defined(SID_FPUENVE)
pVoice->enveVol = releaseTab[pVoice->enveStep.w[HI]];
#else
pVoice->enveVol = releaseTab[pVoice->enveStep];
#endif
/* Will be controlled from sidEmuSet2(). */
if ( pVoice->enveVol <= pVoice->enveSusVol )
{
pVoice->enveVol = pVoice->enveSusVol;
return enveEmuAlterSustain(pVoice); /* start sustain */
}
else
{
enveEmuEnveAdvance(pVoice);
return masterAmplModTable[ pVoice->sid->masterVolumeAmplIndex + pVoice->enveVol ];
}
}
}
INLINE UINT16 enveEmuAlterDecay(sidOperator* pVoice)
{
UINT8 decay = pVoice->SIDAD & 0x0F ;
#ifdef SID_FPUENVE
pVoice->fenveStepAdd = decayReleaseRates[decay];
#elif defined(DIRECT_FIXPOINT)
pVoice->enveStepAdd.l = decayReleaseRates[decay];
#else
pVoice->enveStepAdd = decayReleaseRates[decay];
pVoice->enveStepAddPnt = decayReleaseRatesP[decay];
#endif
pVoice->ADSRproc = &enveEmuDecay;
return enveEmuDecay(pVoice);
}
INLINE UINT16 enveEmuStartDecay(sidOperator* pVoice)
{
pVoice->ADSRctrl = ENVE_DECAY;
#ifdef SID_FPUENVE
pVoice->fenveStep = 0;
#elif defined(DIRECT_FIXPOINT)
pVoice->enveStep.l = 0;
#else
pVoice->enveStep = (pVoice->enveStepPnt = 0);
#endif
return enveEmuAlterDecay(pVoice);
}
/* */
/* Attack */
/* */
INLINE UINT16 enveEmuAttack(sidOperator* pVoice)
{
#ifdef SID_FPUENVE
pVoice->enveStep = (UINT16)pVoice->fenveStep;
#endif
#if defined(DIRECT_FIXPOINT) && !defined(SID_FPUENVE)
if ( pVoice->enveStep.w[HI] > attackTabLen )
#else
if ( pVoice->enveStep >= attackTabLen )
#endif
return enveEmuStartDecay(pVoice);
else
{
#if defined(DIRECT_FIXPOINT) && !defined(SID_FPUENVE)
pVoice->enveVol = pVoice->enveStep.w[HI];
#else
pVoice->enveVol = pVoice->enveStep;
#endif
enveEmuEnveAdvance(pVoice);
return masterAmplModTable[ pVoice->sid->masterVolumeAmplIndex + pVoice->enveVol ];
}
}
INLINE UINT16 enveEmuAlterAttack(sidOperator* pVoice)
{
UINT8 attack = pVoice->SIDAD >> 4;
#ifdef SID_FPUENVE
pVoice->fenveStepAdd = attackRates[attack];
#elif defined(DIRECT_FIXPOINT)
pVoice->enveStepAdd.l = attackRates[attack];
#else
pVoice->enveStepAdd = attackRates[attack];
pVoice->enveStepAddPnt = attackRatesP[attack];
#endif
pVoice->ADSRproc = &enveEmuAttack;
return enveEmuAttack(pVoice);
}
INLINE UINT16 enveEmuStartAttack(sidOperator* pVoice)
{
pVoice->ADSRctrl = ENVE_ATTACK;
#ifdef SID_FPUENVE
pVoice->fenveStep = (float)pVoice->enveVol;
#elif defined(DIRECT_FIXPOINT)
pVoice->enveStep.w[HI] = pVoice->enveVol;
pVoice->enveStep.w[LO] = 0;
#else
pVoice->enveStep = pVoice->enveVol;
pVoice->enveStepPnt = 0;
#endif
return enveEmuAlterAttack(pVoice);
}
/* */
/* Experimental. */
/* */
/*#include <iostream.h> */
/*#include <iomanip.h> */
INLINE UINT16 enveEmuShortAttack(sidOperator* pVoice)
{
#ifdef SID_FPUENVE
pVoice->enveStep = (UINT16)pVoice->fenveStep;
#endif
#if defined(DIRECT_FIXPOINT) && !defined(SID_FPUENVE)
if ((pVoice->enveStep.w[HI] > attackTabLen) ||
(pVoice->enveShortAttackCount == 0))
#else
if ((pVoice->enveStep >= attackTabLen) ||
(pVoice->enveShortAttackCount == 0))
#endif
/* return enveEmuStartRelease(pVoice); */
return enveEmuStartDecay(pVoice);
#if defined(DIRECT_FIXPOINT) && !defined(SID_FPUENVE)
pVoice->enveVol = pVoice->enveStep.w[HI];
#else
pVoice->enveVol = pVoice->enveStep;
#endif
pVoice->enveShortAttackCount--;
/* cout << hex << pVoice->enveShortAttackCount << " / " << pVoice->enveVol << endl; */
enveEmuEnveAdvance(pVoice);
return masterAmplModTable[ pVoice->sid->masterVolumeAmplIndex + pVoice->enveVol ];
}
INLINE UINT16 enveEmuAlterShortAttack(sidOperator* pVoice)
{
UINT8 attack = pVoice->SIDAD >> 4;
#ifdef SID_FPUENVE
pVoice->fenveStepAdd = attackRates[attack];
#elif defined(DIRECT_FIXPOINT)
pVoice->enveStepAdd.l = attackRates[attack];
#else
pVoice->enveStepAdd = attackRates[attack];
pVoice->enveStepAddPnt = attackRatesP[attack];
#endif
pVoice->ADSRproc = &enveEmuShortAttack;
return enveEmuShortAttack(pVoice);
}
INLINE UINT16 enveEmuStartShortAttack(sidOperator* pVoice)
{
pVoice->ADSRctrl = ENVE_SHORTATTACK;
#ifdef SID_FPUENVE
pVoice->fenveStep = (float)pVoice->enveVol;
#elif defined(DIRECT_FIXPOINT)
pVoice->enveStep.w[HI] = pVoice->enveVol;
pVoice->enveStep.w[LO] = 0;
#else
pVoice->enveStep = pVoice->enveVol;
pVoice->enveStepPnt = 0;
#endif
pVoice->enveShortAttackCount = 65535; /* unused */
return enveEmuAlterShortAttack(pVoice);
}