cog/Frameworks/libsidplay/sidplay-residfp-code/.svn/pristine/af/af4041036cbbaf47aeb5a7b3ccf...

320 lines
9.6 KiB
Plaintext

/*
* This file is part of libsidplayfp, a SID player engine.
*
* Copyright 2011-2016 Leandro Nini <drfiemost@users.sourceforge.net>
* Copyright 2007-2010 Antti Lankila
* Copyright 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.
*/
#include "FilterModelConfig.h"
#include <cmath>
#include <cassert>
#include "Integrator.h"
#include "OpAmp.h"
namespace reSIDfp
{
#ifndef HAVE_CXX11
/**
* Compute log(1+x) without losing precision for small values of x
*
* @note when compiling with -ffastm-math the compiler will
* optimize the expression away leaving a plain log(1. + x)
*/
inline double log1p(double x)
{
return log(1. + x) - (((1. + x) - 1.) - x) / (1. + x);
}
#endif
const unsigned int OPAMP_SIZE = 33;
/**
* This is the SID 6581 op-amp voltage transfer function, measured on
* CAP1B/CAP1A on a chip marked MOS 6581R4AR 0687 14.
* All measured chips have op-amps with output voltages (and thus input
* voltages) within the range of 0.81V - 10.31V.
*/
const Spline::Point opamp_voltage[OPAMP_SIZE] =
{
{ 0.81, 10.31 }, // Approximate start of actual range
{ 2.40, 10.31 },
{ 2.60, 10.30 },
{ 2.70, 10.29 },
{ 2.80, 10.26 },
{ 2.90, 10.17 },
{ 3.00, 10.04 },
{ 3.10, 9.83 },
{ 3.20, 9.58 },
{ 3.30, 9.32 },
{ 3.50, 8.69 },
{ 3.70, 8.00 },
{ 4.00, 6.89 },
{ 4.40, 5.21 },
{ 4.54, 4.54 }, // Working point (vi = vo)
{ 4.60, 4.19 },
{ 4.80, 3.00 },
{ 4.90, 2.30 }, // Change of curvature
{ 4.95, 2.03 },
{ 5.00, 1.88 },
{ 5.05, 1.77 },
{ 5.10, 1.69 },
{ 5.20, 1.58 },
{ 5.40, 1.44 },
{ 5.60, 1.33 },
{ 5.80, 1.26 },
{ 6.00, 1.21 },
{ 6.40, 1.12 },
{ 7.00, 1.02 },
{ 7.50, 0.97 },
{ 8.50, 0.89 },
{ 10.00, 0.81 },
{ 10.31, 0.81 }, // Approximate end of actual range
};
std::unique_ptr<FilterModelConfig> FilterModelConfig::instance(nullptr);
FilterModelConfig* FilterModelConfig::getInstance()
{
if (!instance.get())
{
instance.reset(new FilterModelConfig());
}
return instance.get();
}
FilterModelConfig::FilterModelConfig() :
voice_voltage_range(1.5),
voice_DC_voltage(5.0),
C(470e-12),
Vdd(12.18),
Vth(1.31),
Ut(26.0e-3),
k(1.0),
uCox(20e-6),
WL_vcr(9.0 / 1.0),
WL_snake(1.0 / 115.0),
kVddt(k * (Vdd - Vth)),
dac_zero(6.65),
dac_scale(2.63),
vmin(opamp_voltage[0].x),
vmax(kVddt < opamp_voltage[0].y ? opamp_voltage[0].y : kVddt),
denorm(vmax - vmin),
norm(1.0 / denorm),
N16(norm * ((1 << 16) - 1)),
dac(DAC_BITS)
{
dac.kinkedDac(MOS6581);
// Convert op-amp voltage transfer to 16 bit values.
Spline::Point scaled_voltage[OPAMP_SIZE];
for (unsigned int i = 0; i < OPAMP_SIZE; i++)
{
scaled_voltage[i].x = (N16 * (opamp_voltage[i].x - opamp_voltage[i].y) + (1 << 16)) / 2.;
scaled_voltage[i].y = N16 * (opamp_voltage[i].x - vmin);
}
// Create lookup table mapping capacitor voltage to op-amp input voltage:
Spline s(scaled_voltage, OPAMP_SIZE);
for (int x = 0; x < (1 << 16); x++)
{
const Spline::Point out = s.evaluate(x);
double tmp = out.x;
if (tmp < 0.) tmp = 0.;
assert(tmp < 65535.5);
opamp_rev[x] = static_cast<unsigned short>(tmp + 0.5);
}
// Create lookup tables for gains / summers.
OpAmp opampModel(opamp_voltage, OPAMP_SIZE, kVddt);
// The filter summer operates at n ~ 1, and has 5 fundamentally different
// input configurations (2 - 6 input "resistors").
//
// Note that all "on" transistors are modeled as one. This is not
// entirely accurate, since the input for each transistor is different,
// and transistors are not linear components. However modeling all
// transistors separately would be extremely costly.
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;
opampModel.reset();
summer[i] = new unsigned short[size];
for (int vi = 0; vi < size; vi++)
{
const double vin = vmin + vi / N16 / idiv; /* vmin .. vmax */
const double tmp = (opampModel.solve(n, vin) - vmin) * N16;
assert(tmp > -0.5 && tmp < 65535.5);
summer[i][vi] = static_cast<unsigned short>(tmp + 0.5);
}
}
// The audio mixer operates at n ~ 8/6, and has 8 fundamentally different
// input configurations (0 - 7 input "resistors").
//
// All "on", transistors are modeled as one - see comments above for
// the filter summer.
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 * 8.0 / 6.0;
opampModel.reset();
mixer[i] = new unsigned short[size];
for (int vi = 0; vi < size; vi++)
{
const double vin = vmin + vi / N16 / idiv; /* vmin .. vmax */
const double tmp = (opampModel.solve(n, vin) - vmin) * N16;
assert(tmp > -0.5 && tmp < 65535.5);
mixer[i][vi] = static_cast<unsigned short>(tmp + 0.5);
}
}
// 4 bit "resistor" ladders in the bandpass resonance gain and the audio
// output gain necessitate 16 gain tables.
// From die photographs of the bandpass and volume "resistor" ladders
// it follows that gain ~ vol/8 and 1/Q ~ ~res/8 (assuming ideal
// op-amps and ideal "resistors").
for (int n8 = 0; n8 < 16; n8++)
{
const int size = 1 << 16;
const double n = n8 / 8.0;
opampModel.reset();
gain[n8] = new unsigned short[size];
for (int vi = 0; vi < size; vi++)
{
const double vin = vmin + vi / N16; /* vmin .. vmax */
const double tmp = (opampModel.solve(n, vin) - vmin) * N16;
assert(tmp > -0.5 && tmp < 65535.5);
gain[n8][vi] = static_cast<unsigned short>(tmp + 0.5);
}
}
const double nkVddt = N16 * kVddt;
const double nVmin = N16 * vmin;
for (unsigned int i = 0; i < (1 << 16); i++)
{
// The table index is right-shifted 16 times in order to fit in
// 16 bits; the argument to sqrt is thus multiplied by (1 << 16).
//
// The returned value must be corrected for translation. Vg always
// takes part in a subtraction as follows:
//
// k*Vg - Vx = (k*Vg - t) - (Vx - t)
//
// I.e. k*Vg - t must be returned.
const double Vg = nkVddt - sqrt((double)(i << 16));
const double tmp = k * Vg - nVmin;
assert(tmp > -0.5 && tmp < 65535.5);
vcr_kVg[i] = static_cast<unsigned short>(tmp + 0.5);
}
// EKV model:
//
// Ids = Is*(if - ir)
// Is = 2*u*Cox*Ut^2/k*W/L
// if = ln^2(1 + e^((k*(Vg - Vt) - Vs)/(2*Ut))
// ir = ln^2(1 + e^((k*(Vg - Vt) - Vd)/(2*Ut))
const double kVt = k * Vth;
const double Is = 2. * uCox * Ut * Ut / k * WL_vcr;
// Normalized current factor for 1 cycle at 1MHz.
const double N15 = norm * ((1 << 15) - 1);
const double n_Is = N15 * 1.0e-6 / C * Is;
// kVg_Vx = k*Vg - Vx
// I.e. if k != 1.0, Vg must be scaled accordingly.
for (int kVg_Vx = 0; kVg_Vx < (1 << 16); kVg_Vx++)
{
const double log_term = log1p(exp((kVg_Vx / N16 - kVt) / (2. * Ut)));
// Scaled by m*2^15
const double tmp = n_Is * log_term * log_term;
assert(tmp > -0.5 && tmp < 65535.5);
vcr_n_Ids_term[kVg_Vx] = static_cast<unsigned short>(tmp + 0.5);
}
}
FilterModelConfig::~FilterModelConfig()
{
for (int i = 0; i < 5; i++)
{
delete [] summer[i];
}
for (int i = 0; i < 8; i++)
{
delete [] mixer[i];
}
for (int i = 0; i < 16; i++)
{
delete [] gain[i];
}
}
unsigned short* FilterModelConfig::getDAC(double adjustment) const
{
const double dac_zero = getDacZero(adjustment);
unsigned short* f0_dac = new unsigned short[1 << DAC_BITS];
for (unsigned int i = 0; i < (1 << DAC_BITS); i++)
{
const double fcd = dac.getOutput(i);
const double tmp = N16 * (dac_zero + fcd * dac_scale / (1 << DAC_BITS) - vmin);
assert(tmp > -0.5 && tmp < 65535.5);
f0_dac[i] = static_cast<unsigned short>(tmp + 0.5);
}
return f0_dac;
}
std::unique_ptr<Integrator> FilterModelConfig::buildIntegrator()
{
// Vdd - Vth, normalized so that translated values can be subtracted:
// k*Vddt - x = (k*Vddt - t) - (x - t)
double tmp = N16 * (kVddt - vmin);
assert(tmp > -0.5 && tmp < 65535.5);
const unsigned short nkVddt = static_cast<unsigned short>(tmp + 0.5);
// Normalized snake current factor, 1 cycle at 1MHz.
// Fit in 5 bits.
tmp = denorm * (1 << 13) * (uCox / (2. * k) * WL_snake * 1.0e-6 / C);
assert(tmp > -0.5 && tmp < 65535.5);
const unsigned short n_snake = static_cast<unsigned short>(tmp + 0.5);
return std::unique_ptr<Integrator>(new Integrator(vcr_kVg, vcr_n_Ids_term, opamp_rev, nkVddt, n_snake));
}
} // namespace reSIDfp