199 lines
4.7 KiB
Text
199 lines
4.7 KiB
Text
|
/*
|
|||
|
* This file is part of libsidplayfp, a SID player engine.
|
|||
|
*
|
|||
|
* Copyright 2011-2014 Leandro Nini <drfiemost@users.sourceforge.net>
|
|||
|
* Copyright 2007-2010 Antti Lankila
|
|||
|
* Copyright 2004,2010 Dag Lem <resid@nimrod.no>
|
|||
|
*
|
|||
|
* This program is free software; you can redistribute it and/or modify
|
|||
|
* it under the terms of the GNU General Public License as published by
|
|||
|
* the Free Software Foundation; either version 2 of the License, or
|
|||
|
* (at your option) any later version.
|
|||
|
*
|
|||
|
* This program is distributed in the hope that it will be useful,
|
|||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|||
|
* GNU General Public License for more details.
|
|||
|
*
|
|||
|
* You should have received a copy of the GNU General Public License
|
|||
|
* along with this program; if not, write to the Free Software
|
|||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|||
|
*/
|
|||
|
|
|||
|
#ifndef FILTER8580_H
|
|||
|
#define FILTER8580_H
|
|||
|
|
|||
|
#include <cmath>
|
|||
|
#include <cstring>
|
|||
|
|
|||
|
#include <stdint.h>
|
|||
|
|
|||
|
#include "siddefs-fp.h"
|
|||
|
|
|||
|
#include "Filter.h"
|
|||
|
|
|||
|
#include "sidcxx11.h"
|
|||
|
|
|||
|
namespace reSIDfp
|
|||
|
{
|
|||
|
|
|||
|
/**
|
|||
|
* Simple white noise generator.
|
|||
|
* Generates small low quality pseudo random numbers
|
|||
|
* useful to prevent float denormals.
|
|||
|
*
|
|||
|
* Based on the paper [Denormal numbers in floating point signal
|
|||
|
* processing applications](http://ldesoras.free.fr/prod.html#doc_denormal)
|
|||
|
* from Laurent de Soras.
|
|||
|
*/
|
|||
|
class antiDenormalNoise
|
|||
|
{
|
|||
|
private:
|
|||
|
uint32_t rand_state;
|
|||
|
|
|||
|
private:
|
|||
|
/**
|
|||
|
* Reduce 32bit integer to float with a magnitude of about 10^–20.
|
|||
|
*/
|
|||
|
static inline float reduce(uint32_t val)
|
|||
|
{
|
|||
|
// FIXME may not be fully portable
|
|||
|
// This code assumes IEEE-754 floating point representation
|
|||
|
// and same endianness for integers and floats
|
|||
|
const uint32_t mantissa = val & 0x807F0000; // Keep only most significant bits
|
|||
|
const uint32_t flt_rnd = mantissa | 0x1E000000; // Set exponent
|
|||
|
float temp;
|
|||
|
memcpy(&temp, &flt_rnd, sizeof(float));
|
|||
|
return temp;
|
|||
|
}
|
|||
|
|
|||
|
public:
|
|||
|
antiDenormalNoise() :
|
|||
|
rand_state(1) {}
|
|||
|
|
|||
|
inline float get()
|
|||
|
{
|
|||
|
// LCG from Numerical Recipes
|
|||
|
rand_state = rand_state * 1664525 + 1013904223;
|
|||
|
|
|||
|
return reduce(rand_state);
|
|||
|
}
|
|||
|
};
|
|||
|
|
|||
|
/**
|
|||
|
* Filter for 8580 chip based on simple linear approximation
|
|||
|
* of the FC control.
|
|||
|
*/
|
|||
|
class Filter8580 final : public Filter
|
|||
|
{
|
|||
|
private:
|
|||
|
/// Cutoff frequency in Hertz
|
|||
|
double highFreq;
|
|||
|
|
|||
|
/// Lowpass filter voltage
|
|||
|
float Vlp;
|
|||
|
|
|||
|
/// Bandpass filter voltage
|
|||
|
float Vbp;
|
|||
|
|
|||
|
/// Highpass filter voltage
|
|||
|
float Vhp;
|
|||
|
|
|||
|
float w0;
|
|||
|
|
|||
|
/// Resonance parameter
|
|||
|
float _1_div_Q;
|
|||
|
|
|||
|
/// External input voltage
|
|||
|
int ve;
|
|||
|
|
|||
|
antiDenormalNoise noise;
|
|||
|
|
|||
|
public:
|
|||
|
Filter8580() :
|
|||
|
highFreq(12500.),
|
|||
|
Vlp(0.f),
|
|||
|
Vbp(0.f),
|
|||
|
Vhp(0.f),
|
|||
|
w0(0.f),
|
|||
|
_1_div_Q(0.f),
|
|||
|
ve(0) {}
|
|||
|
|
|||
|
int clock(int voice1, int voice2, int voice3) override;
|
|||
|
|
|||
|
/**
|
|||
|
* Set filter cutoff frequency.
|
|||
|
*/
|
|||
|
void updatedCenterFrequency() override { w0 = static_cast<float>(2. * M_PI * highFreq * fc / 2047. / 1e6); }
|
|||
|
|
|||
|
/**
|
|||
|
* Set filter resonance.
|
|||
|
*
|
|||
|
* The following function for 1/Q has been modeled in the MOS 8580:
|
|||
|
*
|
|||
|
* 1/Q = 2^(1/2)*2^(-x/8) = 2^(1/2 - x/8) = 2^((4 - x)/8)
|
|||
|
*
|
|||
|
* @param res the new resonance value
|
|||
|
*/
|
|||
|
void updateResonance(unsigned char res) override { _1_div_Q = static_cast<float>(pow(2., (4 - res) / 8.)); }
|
|||
|
|
|||
|
void input(int input) override { ve = input << 4; }
|
|||
|
|
|||
|
void updatedMixing() override {}
|
|||
|
|
|||
|
/**
|
|||
|
* Set filter curve type based on single parameter.
|
|||
|
*
|
|||
|
* @param curvePosition filter's center frequency expressed in Hertz, default is 12500
|
|||
|
*/
|
|||
|
void setFilterCurve(double curvePosition) { highFreq = curvePosition; }
|
|||
|
};
|
|||
|
|
|||
|
} // namespace reSIDfp
|
|||
|
|
|||
|
#if RESID_INLINING || defined(FILTER8580_CPP)
|
|||
|
|
|||
|
#include <cassert>
|
|||
|
|
|||
|
namespace reSIDfp
|
|||
|
{
|
|||
|
|
|||
|
RESID_INLINE
|
|||
|
int Filter8580::clock(int voice1, int voice2, int voice3)
|
|||
|
{
|
|||
|
int Vi = 0;
|
|||
|
int Vo = 0;
|
|||
|
|
|||
|
(filt1 ? Vi : Vo) += voice1;
|
|||
|
(filt2 ? Vi : Vo) += voice2;
|
|||
|
|
|||
|
// NB! Voice 3 is not silenced by voice3off if it is routed
|
|||
|
// through the filter.
|
|||
|
if (filt3) Vi += voice3;
|
|||
|
else if (!voice3off) Vo += voice3;
|
|||
|
|
|||
|
(filtE ? Vi : Vo) += ve;
|
|||
|
|
|||
|
Vlp -= w0 * Vbp;
|
|||
|
Vbp -= w0 * Vhp;
|
|||
|
Vhp = (Vbp * _1_div_Q) - Vlp - static_cast<float>(Vi >> 7) + noise.get();
|
|||
|
|
|||
|
assert(std::fpclassify(Vlp) != FP_SUBNORMAL);
|
|||
|
assert(std::fpclassify(Vbp) != FP_SUBNORMAL);
|
|||
|
assert(std::fpclassify(Vhp) != FP_SUBNORMAL);
|
|||
|
|
|||
|
float Vof = static_cast<float>(Vo >> 7);
|
|||
|
|
|||
|
if (lp) Vof += Vlp;
|
|||
|
if (bp) Vof += Vbp;
|
|||
|
if (hp) Vof += Vhp;
|
|||
|
|
|||
|
return static_cast<int>(floor(Vof + 0.5f)) * vol >> 4;
|
|||
|
}
|
|||
|
|
|||
|
} // namespace reSIDfp
|
|||
|
|
|||
|
#endif
|
|||
|
|
|||
|
#endif
|