FilterModelConfig.h
/*
* This file is part of libsidplayfp, a SID player engine.
*
* Copyright 2011-2024 Leandro Nini <drfiemost@users.sourceforge.net>
* Copyright 2007-2010 Antti Lankila
* Copyright 2004,2010 Dag Lem
*
* 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 FILTERMODELCONFIG_H
#define FILTERMODELCONFIG_H
#include <algorithm>
#include <random>
#include <cassert>
#include <climits>
#include "OpAmp.h"
#include "Spline.h"
#include "sidcxx11.h"
namespace reSIDfp
{
class FilterModelConfig
{
public:
// The highpass summer has 2 - 6 inputs (bandpass, lowpass, and 0 - 4 voices).
template<int i>
struct summer_offset
{
static constexpr int value = summer_offset<i - 1>::value + ((2 + i - 1) << 16);
};
// The mixer has 0 - 7 inputs (0 - 4 voices and 0 - 3 filter outputs).
template<int i>
struct mixer_offset
{
static constexpr int value = mixer_offset<i - 1>::value + ((i - 1) << 16);
};
protected:
static inline unsigned short to_ushort_dither(double x, double d_noise)
{
const int tmp = static_cast<int>(x + d_noise);
assert((tmp >= 0) && (tmp <= USHRT_MAX));
return static_cast<unsigned short>(tmp);
}
static inline unsigned short to_ushort(double x)
{
return to_ushort_dither(x, 0.5);
}
private:
/*
* Hack to add quick dither when converting values from float to int
* and avoid quantization noise.
* Hopefully this can be removed the day we move all the analog part
* processing to floats.
*
* Not sure about the effect of using such small buffer of numbers
* since the random sequence repeats every 1024 values but for
* now it seems to do the job.
*/
class Randomnoise
{
private:
double buffer[1024];
mutable int index = 0;
public:
Randomnoise()
{
std::uniform_real_distribution<double> unif(0., 1.);
std::default_random_engine re;
for (int i=0; i<1024; i++)
buffer[i] = unif(re);
}
double getNoise() const { index = (index + 1) & 0x3ff; return buffer[index]; }
};
protected:
const double C;
static constexpr double Ut = 26.0e-3;
const double Vdd;
const double Vth;
const double Vddt;
double uCox;
// Derived stuff
const double vmin, vmax;
const double denorm, norm;
const double N16;
const double voice_voltage_range;
double currFactorCoeff;
unsigned short* mixer; //-V730_NOINIT this is initialized in the derived class constructor
unsigned short* summer; //-V730_NOINIT this is initialized in the derived class constructor
unsigned short* volume; //-V730_NOINIT this is initialized in the derived class constructor
unsigned short* resonance; //-V730_NOINIT this is initialized in the derived class constructor
unsigned short opamp_rev[1 << 16]; //-V730_NOINIT this is initialized in the derived class constructor
private:
Randomnoise rnd;
private:
FilterModelConfig(const FilterModelConfig&) = delete;
FilterModelConfig& operator= (const FilterModelConfig&) = delete;
inline double getVoiceVoltage(float value, unsigned int env) const
{
return value * voice_voltage_range + getVoiceDC(env);
}
protected:
FilterModelConfig(
double vvr,
double c,
double vdd,
double vth,
double ucox,
const Spline::Point *opamp_voltage,
int opamp_size
);
~FilterModelConfig();
void setUCox(double new_uCox);
virtual double getVoiceDC(unsigned int env) const = 0;
inline void buildSummerTable(const OpAmp& opampModel)
{
const double r_N16 = 1. / N16;
int idx = 0;
for (int i = 0; i < 5; i++)
{
const int idiv = 2 + i; // 2 - 6 input "resistors".
const int size = idiv << 16;
const double n = idiv;
const double r_idiv = 1. / idiv;
opampModel.reset();
for (int vi = 0; vi < size; vi++)
{
const double vin = vmin + vi * r_N16 * r_idiv; /* vmin .. vmax */
summer[idx++] = getNormalizedValue(opampModel.solve(n, vin));
}
}
}
inline void buildMixerTable(const OpAmp& opampModel, double nRatio)
{
const double r_N16 = 1. / N16;
int idx = 0;
for (int i = 0; i < 8; i++)
{
const int idiv = (i == 0) ? 1 : i;
const int size = (i == 0) ? 1 : i << 16;
const double n = i * nRatio;
const double r_idiv = 1. / idiv;
opampModel.reset();
for (int vi = 0; vi < size; vi++)
{
const double vin = vmin + vi * r_N16 * r_idiv; /* vmin .. vmax */
mixer[idx++] = getNormalizedValue(opampModel.solve(n, vin));
}
}
}
inline void buildVolumeTable(const OpAmp& opampModel, double nDivisor)
{
const double r_N16 = 1. / N16;
int idx = 0;
for (int n8 = 0; n8 < 16; n8++)
{
constexpr int size = 1 << 16;
const double n = n8 / nDivisor;
opampModel.reset();
for (int vi = 0; vi < size; vi++)
{
const double vin = vmin + vi * r_N16; /* vmin .. vmax */
volume[idx++] = getNormalizedValue(opampModel.solve(n, vin));
}
}
}
inline void buildResonanceTable(const OpAmp& opampModel, const double resonance_n[16])
{
const double r_N16 = 1. / N16;
int idx = 0;
for (int n8 = 0; n8 < 16; n8++)
{
constexpr int size = 1 << 16;
opampModel.reset();
for (int vi = 0; vi < size; vi++)
{
const double vin = vmin + vi * r_N16; /* vmin .. vmax */
resonance[idx++] = getNormalizedValue(opampModel.solve(resonance_n[n8], vin));
}
}
}
public:
unsigned short* getVolume() { return volume; }
unsigned short* getResonance() { return resonance; }
unsigned short* getSummer() { return summer; }
unsigned short* getMixer() { return mixer; }
inline unsigned short getOpampRev(int i) const { return opamp_rev[i]; }
inline double getVddt() const { return Vddt; }
inline double getVth() const { return Vth; }
// helper functions
inline unsigned short getNormalizedValue(double value) const
{
return to_ushort_dither(N16 * (value - vmin), rnd.getNoise());
}
template<int N>
inline unsigned short getNormalizedCurrentFactor(double wl) const
{
return to_ushort((1 << N) * currFactorCoeff * wl);
}
inline unsigned short getNVmin() const
{
return to_ushort(N16 * vmin);
}
inline int getNormalizedVoice(float value, unsigned int env) const
{
return static_cast<int>(getNormalizedValue(getVoiceVoltage(value, env)));
}
};
template<>
struct FilterModelConfig::summer_offset<0>
{
static constexpr int value = 0;
};
template<>
struct FilterModelConfig::mixer_offset<1>
{
static constexpr int value = 1;
};
template<>
struct FilterModelConfig::mixer_offset<0>
{
static constexpr int value = 0;
};
} // namespace reSIDfp
#endif
reSIDfp::FilterModelConfig
Definition FilterModelConfig.h:40
reSIDfp::FilterModelConfig::buildVolumeTable
void buildVolumeTable(const OpAmp &opampModel, double nDivisor)
Definition FilterModelConfig.h:238
reSIDfp::FilterModelConfig::Vdd
const double Vdd
Positive supply voltage.
Definition FilterModelConfig.h:105
reSIDfp::FilterModelConfig::buildResonanceTable
void buildResonanceTable(const OpAmp &opampModel, const double resonance_n[16])
Definition FilterModelConfig.h:264
reSIDfp::FilterModelConfig::mixer
unsigned short * mixer
Lookup tables for gain and summer op-amps in output stage / filter.
Definition FilterModelConfig.h:125
reSIDfp::FilterModelConfig::uCox
double uCox
Transconductance coefficient: u*Cox.
Definition FilterModelConfig.h:108
reSIDfp::FilterModelConfig::Vddt
const double Vddt
Vdd - Vth.
Definition FilterModelConfig.h:107
reSIDfp::FilterModelConfig::currFactorCoeff
double currFactorCoeff
Current factor coefficient for op-amp integrators.
Definition FilterModelConfig.h:121
reSIDfp::FilterModelConfig::C
const double C
Capacitor value.
Definition FilterModelConfig.h:98
reSIDfp::FilterModelConfig::opamp_rev
unsigned short opamp_rev[1<< 16]
Reverse op-amp transfer function.
Definition FilterModelConfig.h:132
reSIDfp::FilterModelConfig::Ut
static constexpr double Ut
Transistor parameters.
Definition FilterModelConfig.h:103
reSIDfp::FilterModelConfig::Vth
const double Vth
Threshold voltage.
Definition FilterModelConfig.h:106
reSIDfp::FilterModelConfig::buildMixerTable
void buildMixerTable(const OpAmp &opampModel, double nRatio)
Definition FilterModelConfig.h:210
reSIDfp::FilterModelConfig::N16
const double N16
Fixed point scaling for 16 bit op-amp output.
Definition FilterModelConfig.h:116
reSIDfp::FilterModelConfig::buildSummerTable
void buildSummerTable(const OpAmp &opampModel)
Definition FilterModelConfig.h:181
reSIDfp::OpAmp
Definition OpAmp.h:71
reSIDfp::OpAmp::solve
double solve(double n, double vi) const
Definition OpAmp.cpp:33
reSIDfp::OpAmp::reset
void reset() const
Definition OpAmp.h:102
reSIDfp::FilterModelConfig::mixer_offset
Definition FilterModelConfig.h:52
reSIDfp::FilterModelConfig::summer_offset
Definition FilterModelConfig.h:45