// Simple 2-poles resonant filter void CSoundFile::SetupChannelFilter(ModChannel *pChn, bool bReset, int flt_modifier) const { int cutoff = (int)pChn->nCutOff + (int)pChn->nCutSwing; int resonance = (int)(pChn->nResonance & 0x7F) + (int)pChn->nResSwing; Limit(cutoff, 0, 127); Limit(resonance, 0, 127); if(!m_playBehaviour[kMPTOldSwingBehaviour]) { pChn->nCutOff = (uint8)cutoff; pChn->nCutSwing = 0; pChn->nResonance = (uint8)resonance; pChn->nResSwing = 0; } // flt_modifier is in [-256, 256], so cutoff is in [0, 127 * 2] after this calculation. const int computedCutoff = cutoff * (flt_modifier + 256) / 256; // Filtering is only ever done in IT if either cutoff is not full or if resonance is set. if(m_playBehaviour[kITFilterBehaviour] && resonance == 0 && computedCutoff >= 254) { if(pChn->rowCommand.IsNote() && !pChn->rowCommand.IsPortamento() && !pChn->nMasterChn && m_SongFlags[SONG_FIRSTTICK]) { // Z7F next to a note disables the filter, however in other cases this should not happen. // Test cases: filter-reset.it, filter-reset-carry.it, filter-nna.it pChn->dwFlags.reset(CHN_FILTER); } return; } pChn->dwFlags.set(CHN_FILTER); // 2 * damping factor const float dmpfac = std::pow(10.0f, -resonance * ((24.0f / 128.0f) / 20.0f)); const float fc = CutOffToFrequency(cutoff, flt_modifier) * (2.0f * (float)M_PI); float d, e; if(m_playBehaviour[kITFilterBehaviour] && !m_SongFlags[SONG_EXFILTERRANGE]) { const float r = m_MixerSettings.gdwMixingFreq / fc; d = dmpfac * r + dmpfac - 1.0f; e = r * r; } else { const float r = fc / m_MixerSettings.gdwMixingFreq; d = (1.0f - 2.0f * dmpfac) * r; LimitMax(d, 2.0f); d = (2.0f * dmpfac - d) / r; e = 1.0f / (r * r); } float fg = 1.0f / (1.0f + d + e); float fb0 = (d + e + e) / (1 + d + e); float fb1 = -e / (1.0f + d + e); #if defined(MPT_INTMIXER) #define FILTER_CONVERT(x) Util::Round<mixsample_t>((x) * (1 << MIXING_FILTER_PRECISION)) #else #define FILTER_CONVERT(x) (x) #endif switch(pChn->nFilterMode) { case FLTMODE_HIGHPASS: pChn->nFilter_A0 = FILTER_CONVERT(1.0f - fg); pChn->nFilter_B0 = FILTER_CONVERT(fb0); pChn->nFilter_B1 = FILTER_CONVERT(fb1); #ifdef MPT_INTMIXER pChn->nFilter_HP = -1; #else pChn->nFilter_HP = 1.0f; #endif // MPT_INTMIXER break; default: pChn->nFilter_A0 = FILTER_CONVERT(fg); pChn->nFilter_B0 = FILTER_CONVERT(fb0); pChn->nFilter_B1 = FILTER_CONVERT(fb1); #ifdef MPT_INTMIXER if(pChn->nFilter_A0 == 0) pChn->nFilter_A0 = 1; // Prevent silence at low filter cutoff and very high sampling rate pChn->nFilter_HP = 0; #else pChn->nFilter_HP = 0; #endif // MPT_INTMIXER break; } #undef FILTER_CONVERT if (bReset) { pChn->nFilter_Y[0][0] = pChn->nFilter_Y[0][1] = 0; pChn->nFilter_Y[1][0] = pChn->nFilter_Y[1][1] = 0; } }
// Simple 2-poles resonant filter void CSoundFile::SetupChannelFilter(ModChannel *pChn, bool bReset, int flt_modifier) const //---------------------------------------------------------------------------------------- { int cutoff = (int)pChn->nCutOff + (int)pChn->nCutSwing; int resonance = (int)(pChn->nResonance & 0x7F) + (int)pChn->nResSwing; Limit(cutoff, 0, 127); Limit(resonance, 0, 127); if(!GetModFlag(MSF_OLDVOLSWING)) { pChn->nCutOff = (uint8)cutoff; pChn->nCutSwing = 0; pChn->nResonance = (uint8)resonance; pChn->nResSwing = 0; } float d, e; // flt_modifier is in [-256, 256], so cutoff is in [0, 127 * 2] after this calculation. const int computedCutoff = cutoff * (flt_modifier + 256) / 256; // Filtering is only ever done in IT if either cutoff is not full or if resonance is set. if(IsCompatibleMode(TRK_IMPULSETRACKER) && resonance == 0 && computedCutoff >= 254) { if(pChn->rowCommand.IsNote() && !pChn->rowCommand.IsPortamento() && !pChn->nMasterChn && m_SongFlags[SONG_FIRSTTICK]) { // Z7F next to a note disables the filter, however in other cases this should not happen. // Test cases: filter-reset.it, filter-reset-carry.it, filter-nna.it pChn->dwFlags.reset(CHN_FILTER); } return; } pChn->dwFlags.set(CHN_FILTER); if(UseITFilterMode()) { const float freqParameterMultiplier = 128.0f / (24.0f * 256.0f); // 2 ^ (i / 24 * 256) float frequency = 110.0f * pow(2.0f, 0.25f + (float)computedCutoff * freqParameterMultiplier); LimitMax(frequency, (float)(m_MixerSettings.gdwMixingFreq / 2)); const float r = (float)m_MixerSettings.gdwMixingFreq / (2.0f * (float)M_PI * frequency); d = ITResonanceTable[resonance] * r + ITResonanceTable[resonance] - 1.0f; e = r * r; } else { float fc = (float)CutOffToFrequency(cutoff, flt_modifier); const float dmpfac = pow(10.0f, -((24.0f / 128.0f) * (float)resonance) / 20.0f); fc *= (float)(2.0f * (float)M_PI / (float)m_MixerSettings.gdwMixingFreq); d = (1.0f - 2.0f * dmpfac) * fc; LimitMax(d, 2.0f); d = (2.0f * dmpfac - d) / fc; e = pow(1.0f / fc, 2.0f); } float fg = 1.0f / (1.0f + d + e); float fb0 = (d + e + e) / (1 + d + e); float fb1 = -e / (1.0f + d + e); #if defined(MPT_INTMIXER) #define FILTER_CONVERT(x) static_cast<mixsample_t>((x) * (1 << MIXING_FILTER_PRECISION)) #else #define FILTER_CONVERT(x) (x) #endif switch(pChn->nFilterMode) { case FLTMODE_HIGHPASS: pChn->nFilter_A0 = FILTER_CONVERT(1.0f - fg); pChn->nFilter_B0 = FILTER_CONVERT(fb0); pChn->nFilter_B1 = FILTER_CONVERT(fb1); #ifdef MPT_INTMIXER pChn->nFilter_HP = -1; #else pChn->nFilter_HP = 1.0f; #endif // MPT_INTMIXER break; default: pChn->nFilter_A0 = FILTER_CONVERT(fg); pChn->nFilter_B0 = FILTER_CONVERT(fb0); pChn->nFilter_B1 = FILTER_CONVERT(fb1); #ifdef MPT_INTMIXER pChn->nFilter_HP = 0; #else pChn->nFilter_HP = 0; #endif // MPT_INTMIXER break; } #undef FILTER_CONVERT if (bReset) { pChn->nFilter_Y[0][0] = pChn->nFilter_Y[0][1] = 0; pChn->nFilter_Y[1][0] = pChn->nFilter_Y[1][1] = 0; } }