// license:BSD-3-Clause // copyright-holders:Peter Trauner /*========================================================================= */ /* 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 , */ /* 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 "sidenvel.h" #include "sid.h" #include "side6581.h" #include "sidvoice.h" const uint8_t masterVolumeLevels[16] = { 0, 17, 34, 51, 68, 85, 102, 119, 136, 153, 170, 187, 204, 221, 238, 255 }; static uint16_t 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_t attackRates[16]; static uint32_t decayReleaseRates[16]; #else static uint32_t attackRates[16]; static uint32_t attackRatesP[16]; static uint32_t decayReleaseRates[16]; static uint32_t decayReleaseRatesP[16]; #endif static const uint32_t attackTabLen = 255; static uint32_t releaseTabLen; static uint32_t releasePos[256]; void enveEmuInit( uint32_t updateFreq, int measuredValues ) { uint32_t 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_t tmpVol = j; if (measuredValues) { tmpVol = (uint16_t) ((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_t scaledenvelen = (uint32_t)floor(( attackTimes[i] * updateFreq ) / 1000UL ); if (scaledenvelen == 0) scaledenvelen = 1; attackRates[i] = (attackTabLen << 16) / scaledenvelen; scaledenvelen = (uint32_t)floor(( decayReleaseTimes[i] * updateFreq ) / 1000UL ); if (scaledenvelen == 0) scaledenvelen = 1; decayReleaseRates[i] = (releaseTabLen << 16) / scaledenvelen; #else uint32_t scaledenvelen = (uint32_t)(/*floor*/(( attackTimes[i] * updateFreq ) / 1000UL )); if (scaledenvelen == 0) scaledenvelen = 1; attackRates[i] = attackTabLen / scaledenvelen; attackRatesP[i] = (( attackTabLen % scaledenvelen ) * 65536UL ) / scaledenvelen; scaledenvelen = (uint32_t)(/*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; } static inline uint16_t enveEmuStartAttack(sidOperator*); static inline uint16_t enveEmuStartDecay(sidOperator*); static inline uint16_t enveEmuStartRelease(sidOperator*); static inline uint16_t enveEmuAlterAttack(sidOperator*); static inline uint16_t enveEmuAlterDecay(sidOperator*); static inline uint16_t enveEmuAlterSustain(sidOperator*); static inline uint16_t enveEmuAlterSustainDecay(sidOperator*); static inline uint16_t enveEmuAlterRelease(sidOperator*); static inline uint16_t enveEmuAttack(sidOperator*); static inline uint16_t enveEmuDecay(sidOperator*); static inline uint16_t enveEmuSustain(sidOperator*); static inline uint16_t enveEmuSustainDecay(sidOperator*); static inline uint16_t enveEmuRelease(sidOperator*); static inline uint16_t enveEmuMute(sidOperator*); static inline uint16_t enveEmuStartShortAttack(sidOperator*); static inline uint16_t enveEmuAlterShortAttack(sidOperator*); static inline uint16_t 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. */ static 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. */ static inline uint16_t enveEmuMute(sidOperator* pVoice) { return 0; } /* */ /* Release */ /* */ static inline uint16_t enveEmuRelease(sidOperator* pVoice) { #ifdef SID_FPUENVE pVoice->enveStep = (uint16_t)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 ]; } } static inline uint16_t enveEmuAlterRelease(sidOperator* pVoice) { uint8_t 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); } static inline uint16_t 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 */ /* */ static inline uint16_t enveEmuSustain(sidOperator* pVoice) { return masterAmplModTable[pVoice->sid->masterVolumeAmplIndex+pVoice->enveVol]; } static inline uint16_t enveEmuSustainDecay(sidOperator* pVoice) { #ifdef SID_FPUENVE pVoice->enveStep = (uint16_t)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(). */ static inline uint16_t enveEmuAlterSustainDecay(sidOperator* pVoice) { uint8_t 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(). */ static inline uint16_t 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 */ /* */ static inline uint16_t enveEmuDecay(sidOperator* pVoice) { #ifdef SID_FPUENVE pVoice->enveStep = (uint16_t)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 ]; } } } static inline uint16_t enveEmuAlterDecay(sidOperator* pVoice) { uint8_t 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); } static inline uint16_t 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 */ /* */ static inline uint16_t enveEmuAttack(sidOperator* pVoice) { #ifdef SID_FPUENVE pVoice->enveStep = (uint16_t)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 ]; } } static inline uint16_t enveEmuAlterAttack(sidOperator* pVoice) { uint8_t 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); } static inline uint16_t 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 */ /*#include */ static inline uint16_t enveEmuShortAttack(sidOperator* pVoice) { #ifdef SID_FPUENVE pVoice->enveStep = (uint16_t)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 ]; } static inline uint16_t enveEmuAlterShortAttack(sidOperator* pVoice) { uint8_t 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); } static inline uint16_t 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); }