scummvm/audio/softsynth/sid.cpp
Thierry Crozat 0b6ef93b85 GUI: Use consistent capitalization for options
As discussed on the mailing list we should use title capitalization
only for push buttons and tabs and use sentence capitalization for
everything else.
2018-07-22 21:28:12 +01:00

1453 lines
33 KiB
C++

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* 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.
*
*/
/*
* This file is based on reSID, a MOS6581 SID emulator engine.
* Copyright (C) 2004 Dag Lem <resid@nimrod.no>
*/
#ifndef DISABLE_SID
#include "audio/softsynth/sid.h"
#include "audio/null.h"
namespace Resid {
// Fixpoint constants (16.16 bits).
const int SID::FIXP_SHIFT = 16;
const int SID::FIXP_MASK = 0xffff;
/*
* WaveformGenerator
*/
WaveformGenerator::WaveformGenerator() {
sync_source = this;
reset();
}
void WaveformGenerator::set_sync_source(WaveformGenerator* source) {
sync_source = source;
source->sync_dest = this;
}
void WaveformGenerator::writeFREQ_LO(reg8 freq_lo) {
freq = (freq & 0xff00) | (freq_lo & 0x00ff);
}
void WaveformGenerator::writeFREQ_HI(reg8 freq_hi) {
freq = ((freq_hi << 8) & 0xff00) | (freq & 0x00ff);
}
void WaveformGenerator::writePW_LO(reg8 pw_lo) {
pw = (pw & 0xf00) | (pw_lo & 0x0ff);
}
void WaveformGenerator::writePW_HI(reg8 pw_hi) {
pw = ((pw_hi << 8) & 0xf00) | (pw & 0x0ff);
}
void WaveformGenerator::writeCONTROL_REG(reg8 control) {
waveform = (control >> 4) & 0x0f;
ring_mod = control & 0x04;
sync = control & 0x02;
reg8 test_next = control & 0x08;
// Test bit set.
if (test_next) {
accumulator = 0;
shift_register = 0;
}
// Test bit cleared.
else if (test) {
shift_register = 0x7ffff8;
}
test = test_next;
// The gate bit is handled by the EnvelopeGenerator.
}
reg8 WaveformGenerator::readOSC() {
return output() >> 4;
}
void WaveformGenerator::reset() {
accumulator = 0;
shift_register = 0x7ffff8;
freq = 0;
pw = 0;
test = 0;
ring_mod = 0;
sync = 0;
msb_rising = false;
}
RESID_INLINE void WaveformGenerator::updateClock(cycle_count delta_t) {
// No operation if test bit is set.
if (test) {
return;
}
reg24 accumulator_prev = accumulator;
// Calculate new accumulator value;
reg24 delta_accumulator = delta_t*freq;
accumulator += delta_accumulator;
accumulator &= 0xffffff;
// Check whether the MSB is set high. This is used for synchronization.
msb_rising = !(accumulator_prev & 0x800000) && (accumulator & 0x800000);
// Shift noise register once for each time accumulator bit 19 is set high.
// Bit 19 is set high each time 2^20 (0x100000) is added to the accumulator.
reg24 shift_period = 0x100000;
while (delta_accumulator) {
if (delta_accumulator < shift_period) {
shift_period = delta_accumulator;
// Determine whether bit 19 is set on the last period.
// NB! Requires two's complement integer.
if (shift_period <= 0x080000) {
// Check for flip from 0 to 1.
if (((accumulator - shift_period) & 0x080000) || !(accumulator & 0x080000))
{
break;
}
}
else {
// Check for flip from 0 (to 1 or via 1 to 0) or from 1 via 0 to 1.
if (((accumulator - shift_period) & 0x080000) && !(accumulator & 0x080000))
{
break;
}
}
}
// Shift the noise/random register.
// NB! The shift is actually delayed 2 cycles, this is not modeled.
reg24 bit0 = ((shift_register >> 22) ^ (shift_register >> 17)) & 0x1;
shift_register <<= 1;
shift_register &= 0x7fffff;
shift_register |= bit0;
delta_accumulator -= shift_period;
}
}
/**
* Synchronize oscillators.
* This must be done after all the oscillators have been updateClock()'ed since the
* oscillators operate in parallel.
* Note that the oscillators must be clocked exactly on the cycle when the
* MSB is set high for hard sync to operate correctly. See SID::updateClock().
*/
RESID_INLINE void WaveformGenerator::synchronize() {
// A special case occurs when a sync source is synced itself on the same
// cycle as when its MSB is set high. In this case the destination will
// not be synced. This has been verified by sampling OSC3.
if (msb_rising && sync_dest->sync && !(sync && sync_source->msb_rising)) {
sync_dest->accumulator = 0;
}
}
/*
* Output functions
*/
// No waveform: Zero output.
RESID_INLINE reg12 WaveformGenerator::output____() {
return 0x000;
}
// Triangle:
RESID_INLINE reg12 WaveformGenerator::output___T() {
reg24 msb = (ring_mod ? accumulator ^ sync_source->accumulator : accumulator)
& 0x800000;
return ((msb ? ~accumulator : accumulator) >> 11) & 0xfff;
}
// Sawtooth:
RESID_INLINE reg12 WaveformGenerator::output__S_() {
return accumulator >> 12;
}
// Pulse:
RESID_INLINE reg12 WaveformGenerator::output_P__() {
return (test || (accumulator >> 12) >= pw) ? 0xfff : 0x000;
}
// Noise:
RESID_INLINE reg12 WaveformGenerator::outputN___() {
return
((shift_register & 0x400000) >> 11) |
((shift_register & 0x100000) >> 10) |
((shift_register & 0x010000) >> 7) |
((shift_register & 0x002000) >> 5) |
((shift_register & 0x000800) >> 4) |
((shift_register & 0x000080) >> 1) |
((shift_register & 0x000010) << 1) |
((shift_register & 0x000004) << 2);
}
// Combined waveforms:
RESID_INLINE reg12 WaveformGenerator::output__ST() {
return wave6581__ST[output__S_()] << 4;
}
RESID_INLINE reg12 WaveformGenerator::output_P_T() {
return (wave6581_P_T[output___T() >> 1] << 4) & output_P__();
}
RESID_INLINE reg12 WaveformGenerator::output_PS_() {
return (wave6581_PS_[output__S_()] << 4) & output_P__();
}
RESID_INLINE reg12 WaveformGenerator::output_PST() {
return (wave6581_PST[output__S_()] << 4) & output_P__();
}
// Combined waveforms including noise:
RESID_INLINE reg12 WaveformGenerator::outputN__T() {
return 0;
}
RESID_INLINE reg12 WaveformGenerator::outputN_S_() {
return 0;
}
RESID_INLINE reg12 WaveformGenerator::outputN_ST() {
return 0;
}
RESID_INLINE reg12 WaveformGenerator::outputNP__() {
return 0;
}
RESID_INLINE reg12 WaveformGenerator::outputNP_T() {
return 0;
}
RESID_INLINE reg12 WaveformGenerator::outputNPS_() {
return 0;
}
RESID_INLINE reg12 WaveformGenerator::outputNPST() {
return 0;
}
/**
* Select one of 16 possible combinations of waveforms.
*/
RESID_INLINE reg12 WaveformGenerator::output() {
// It may seem cleaner to use an array of member functions to return
// waveform output; however a switch with inline functions is faster.
switch (waveform) {
default:
case 0x0:
return output____();
case 0x1:
return output___T();
case 0x2:
return output__S_();
case 0x3:
return output__ST();
case 0x4:
return output_P__();
case 0x5:
return output_P_T();
case 0x6:
return output_PS_();
case 0x7:
return output_PST();
case 0x8:
return outputN___();
case 0x9:
return outputN__T();
case 0xa:
return outputN_S_();
case 0xb:
return outputN_ST();
case 0xc:
return outputNP__();
case 0xd:
return outputNP_T();
case 0xe:
return outputNPS_();
case 0xf:
return outputNPST();
}
}
/*
* Our objective is to construct a smooth interpolating single-valued function
* y = f(x).
* Our approach is to approximate the properties of Catmull-Rom splines for
* piecewice cubic polynomials.
*/
/**
* Calculation of coefficients.
*/
inline void cubic_coefficients(double x1, double y1, double x2, double y2,
double k1, double k2,
double& a, double& b, double& c, double& d)
{
double dx = x2 - x1, dy = y2 - y1;
a = ((k1 + k2) - 2*dy/dx)/(dx*dx);
b = ((k2 - k1)/dx - 3*(x1 + x2)*a)/2;
c = k1 - (3*x1*a + 2*b)*x1;
d = y1 - ((x1*a + b)*x1 + c)*x1;
}
/**
* Evaluation of cubic polynomial by forward differencing.
*/
template<class PointPlotter>
inline void interpolate_segment(double x1, double y1, double x2, double y2,
double k1, double k2,
PointPlotter plot, double res)
{
double a, b, c, d;
cubic_coefficients(x1, y1, x2, y2, k1, k2, a, b, c, d);
double y = ((a*x1 + b)*x1 + c)*x1 + d;
double dy = (3*a*(x1 + res) + 2*b)*x1*res + ((a*res + b)*res + c)*res;
double d2y = (6*a*(x1 + res) + 2*b)*res*res;
double d3y = 6*a*res*res*res;
// Calculate each point.
for (double x = x1; x <= x2; x += res) {
plot(x, y);
y += dy; dy += d2y; d2y += d3y;
}
}
template<class PointIter>
inline double x(PointIter p) {
return (*p)[0];
}
template<class PointIter>
inline double y(PointIter p) {
return (*p)[1];
}
/**
* Evaluation of complete interpolating function.
* Note that since each curve segment is controlled by four points, the
* end points will not be interpolated. If extra control points are not
* desirable, the end points can simply be repeated to ensure interpolation.
* Note also that points of non-differentiability and discontinuity can be
* introduced by repeating points.
*/
template<class PointIter, class PointPlotter>
inline void interpolate(PointIter p0, PointIter pn, PointPlotter plot, double res) {
double k1, k2;
// Set up points for first curve segment.
PointIter p1 = p0; ++p1;
PointIter p2 = p1; ++p2;
PointIter p3 = p2; ++p3;
// Draw each curve segment.
for (; p2 != pn; ++p0, ++p1, ++p2, ++p3) {
// p1 and p2 equal; single point.
if (x(p1) == x(p2)) {
continue;
}
// Both end points repeated; straight line.
if (x(p0) == x(p1) && x(p2) == x(p3)) {
k1 = k2 = (y(p2) - y(p1))/(x(p2) - x(p1));
}
// p0 and p1 equal; use f''(x1) = 0.
else if (x(p0) == x(p1)) {
k2 = (y(p3) - y(p1))/(x(p3) - x(p1));
k1 = (3*(y(p2) - y(p1))/(x(p2) - x(p1)) - k2)/2;
}
// p2 and p3 equal; use f''(x2) = 0.
else if (x(p2) == x(p3)) {
k1 = (y(p2) - y(p0))/(x(p2) - x(p0));
k2 = (3*(y(p2) - y(p1))/(x(p2) - x(p1)) - k1)/2;
}
// Normal curve.
else {
k1 = (y(p2) - y(p0))/(x(p2) - x(p0));
k2 = (y(p3) - y(p1))/(x(p3) - x(p1));
}
interpolate_segment(x(p1), y(p1), x(p2), y(p2), k1, k2, plot, res);
}
}
/**
* Class for plotting integers into an array.
*/
template<class F>
class PointPlotter {
protected:
F* f;
public:
PointPlotter(F* arr) : f(arr) {
}
void operator ()(double x, double y) {
// Clamp negative values to zero.
if (y < 0) {
y = 0;
}
f[F(x)] = F(y);
}
};
fc_point Filter::f0_points_6581[] = {
// FC f FCHI FCLO
// ----------------------------
{ 0, 220 }, // 0x00 - repeated end point
{ 0, 220 }, // 0x00
{ 128, 230 }, // 0x10
{ 256, 250 }, // 0x20
{ 384, 300 }, // 0x30
{ 512, 420 }, // 0x40
{ 640, 780 }, // 0x50
{ 768, 1600 }, // 0x60
{ 832, 2300 }, // 0x68
{ 896, 3200 }, // 0x70
{ 960, 4300 }, // 0x78
{ 992, 5000 }, // 0x7c
{ 1008, 5400 }, // 0x7e
{ 1016, 5700 }, // 0x7f
{ 1023, 6000 }, // 0x7f 0x07
{ 1023, 6000 }, // 0x7f 0x07 - discontinuity
{ 1024, 4600 }, // 0x80 -
{ 1024, 4600 }, // 0x80
{ 1032, 4800 }, // 0x81
{ 1056, 5300 }, // 0x84
{ 1088, 6000 }, // 0x88
{ 1120, 6600 }, // 0x8c
{ 1152, 7200 }, // 0x90
{ 1280, 9500 }, // 0xa0
{ 1408, 12000 }, // 0xb0
{ 1536, 14500 }, // 0xc0
{ 1664, 16000 }, // 0xd0
{ 1792, 17100 }, // 0xe0
{ 1920, 17700 }, // 0xf0
{ 2047, 18000 }, // 0xff 0x07
{ 2047, 18000 } // 0xff 0x07 - repeated end point
};
/*
* Filter
*/
Filter::Filter() {
fc = 0;
res = 0;
filt = 0;
voice3off = 0;
hp_bp_lp = 0;
vol = 0;
// State of filter.
Vhp = 0;
Vbp = 0;
Vlp = 0;
Vnf = 0;
enable_filter(true);
// Create mappings from FC to cutoff frequency.
interpolate(f0_points_6581, f0_points_6581
+ sizeof(f0_points_6581)/sizeof(*f0_points_6581) - 1,
PointPlotter<sound_sample>(f0_6581), 1.0);
mixer_DC = (-0xfff*0xff/18) >> 7;
f0 = f0_6581;
f0_points = f0_points_6581;
f0_count = sizeof(f0_points_6581)/sizeof(*f0_points_6581);
set_w0();
set_Q();
}
void Filter::enable_filter(bool enable) {
enabled = enable;
}
void Filter::reset() {
fc = 0;
res = 0;
filt = 0;
voice3off = 0;
hp_bp_lp = 0;
vol = 0;
// State of filter.
Vhp = 0;
Vbp = 0;
Vlp = 0;
Vnf = 0;
set_w0();
set_Q();
}
void Filter::writeFC_LO(reg8 fc_lo) {
fc = (fc & 0x7f8) | (fc_lo & 0x007);
set_w0();
}
void Filter::writeFC_HI(reg8 fc_hi) {
fc = ((fc_hi << 3) & 0x7f8) | (fc & 0x007);
set_w0();
}
void Filter::writeRES_FILT(reg8 res_filt) {
res = (res_filt >> 4) & 0x0f;
set_Q();
filt = res_filt & 0x0f;
}
void Filter::writeMODE_VOL(reg8 mode_vol) {
voice3off = mode_vol & 0x80;
hp_bp_lp = (mode_vol >> 4) & 0x07;
vol = mode_vol & 0x0f;
}
// Set filter cutoff frequency.
void Filter::set_w0() {
const double pi = 3.1415926535897932385;
// Multiply with 1.048576 to facilitate division by 1 000 000 by right-
// shifting 20 times (2 ^ 20 = 1048576).
w0 = static_cast<sound_sample>(2*pi*f0[fc]*1.048576);
// Limit f0 to 16kHz to keep 1 cycle filter stable.
const sound_sample w0_max_1 = static_cast<sound_sample>(2*pi*16000*1.048576);
w0_ceil_1 = w0 <= w0_max_1 ? w0 : w0_max_1;
// Limit f0 to 4kHz to keep delta_t cycle filter stable.
const sound_sample w0_max_dt = static_cast<sound_sample>(2*pi*4000*1.048576);
w0_ceil_dt = w0 <= w0_max_dt ? w0 : w0_max_dt;
}
// Set filter resonance.
void Filter::set_Q() {
// Q is controlled linearly by res. Q has approximate range [0.707, 1.7].
// As resonance is increased, the filter must be clocked more often to keep
// stable.
// The coefficient 1024 is dispensed of later by right-shifting 10 times
// (2 ^ 10 = 1024).
_1024_div_Q = static_cast<sound_sample>(1024.0/(0.707 + 1.0*res/0x0f));
}
RESID_INLINE void Filter::updateClock(cycle_count delta_t,
sound_sample voice1,
sound_sample voice2,
sound_sample voice3)
{
// Scale each voice down from 20 to 13 bits.
voice1 >>= 7;
voice2 >>= 7;
// NB! Voice 3 is not silenced by voice3off if it is routed through
// the filter.
if (voice3off && !(filt & 0x04)) {
voice3 = 0;
}
else {
voice3 >>= 7;
}
// Enable filter on/off.
// This is not really part of SID, but is useful for testing.
// On slow CPUs it may be necessary to bypass the filter to lower the CPU
// load.
if (!enabled) {
Vnf = voice1 + voice2 + voice3;
Vhp = Vbp = Vlp = 0;
return;
}
// Route voices into or around filter.
// The code below is expanded to a switch for faster execution.
// (filt1 ? Vi : Vnf) += voice1;
// (filt2 ? Vi : Vnf) += voice2;
// (filt3 ? Vi : Vnf) += voice3;
sound_sample Vi;
switch (filt) {
default:
case 0x0:
Vi = 0;
Vnf = voice1 + voice2 + voice3;
break;
case 0x1:
Vi = voice1;
Vnf = voice2 + voice3;
break;
case 0x2:
Vi = voice2;
Vnf = voice1 + voice3;
break;
case 0x3:
Vi = voice1 + voice2;
Vnf = voice3;
break;
case 0x4:
Vi = voice3;
Vnf = voice1 + voice2;
break;
case 0x5:
Vi = voice1 + voice3;
Vnf = voice2;
break;
case 0x6:
Vi = voice2 + voice3;
Vnf = voice1;
break;
case 0x7:
Vi = voice1 + voice2 + voice3;
Vnf = 0;
break;
case 0x8:
Vi = 0;
Vnf = voice1 + voice2 + voice3;
break;
case 0x9:
Vi = voice1;
Vnf = voice2 + voice3;
break;
case 0xa:
Vi = voice2;
Vnf = voice1 + voice3;
break;
case 0xb:
Vi = voice1 + voice2;
Vnf = voice3;
break;
case 0xc:
Vi = voice3;
Vnf = voice1 + voice2;
break;
case 0xd:
Vi = voice1 + voice3;
Vnf = voice2;
break;
case 0xe:
Vi = voice2 + voice3;
Vnf = voice1;
break;
case 0xf:
Vi = voice1 + voice2 + voice3;
Vnf = 0;
break;
}
// Maximum delta cycles for the filter to work satisfactorily under current
// cutoff frequency and resonance constraints is approximately 8.
cycle_count delta_t_flt = 8;
while (delta_t) {
if (delta_t < delta_t_flt) {
delta_t_flt = delta_t;
}
// delta_t is converted to seconds given a 1MHz clock by dividing
// with 1 000 000. This is done in two operations to avoid integer
// multiplication overflow.
// Calculate filter outputs.
// Vhp = Vbp/Q - Vlp - Vi;
// dVbp = -w0*Vhp*dt;
// dVlp = -w0*Vbp*dt;
sound_sample w0_delta_t = w0_ceil_dt*delta_t_flt >> 6;
sound_sample dVbp = (w0_delta_t*Vhp >> 14);
sound_sample dVlp = (w0_delta_t*Vbp >> 14);
Vbp -= dVbp;
Vlp -= dVlp;
Vhp = (Vbp*_1024_div_Q >> 10) - Vlp - Vi;
delta_t -= delta_t_flt;
}
}
RESID_INLINE sound_sample Filter::output() {
// This is handy for testing.
if (!enabled) {
return (Vnf + mixer_DC)*static_cast<sound_sample>(vol);
}
// Mix highpass, bandpass, and lowpass outputs. The sum is not
// weighted, this can be confirmed by sampling sound output for
// e.g. bandpass, lowpass, and bandpass+lowpass from a SID chip.
// The code below is expanded to a switch for faster execution.
// if (hp) Vf += Vhp;
// if (bp) Vf += Vbp;
// if (lp) Vf += Vlp;
sound_sample Vf;
switch (hp_bp_lp) {
default:
case 0x0:
Vf = 0;
break;
case 0x1:
Vf = Vlp;
break;
case 0x2:
Vf = Vbp;
break;
case 0x3:
Vf = Vlp + Vbp;
break;
case 0x4:
Vf = Vhp;
break;
case 0x5:
Vf = Vlp + Vhp;
break;
case 0x6:
Vf = Vbp + Vhp;
break;
case 0x7:
Vf = Vlp + Vbp + Vhp;
break;
}
// Sum non-filtered and filtered output.
// Multiply the sum with volume.
return (Vnf + Vf + mixer_DC)*static_cast<sound_sample>(vol);
}
/*
* EnvelopeGenerator
*/
EnvelopeGenerator::EnvelopeGenerator() {
reset();
}
void EnvelopeGenerator::reset() {
envelope_counter = 0;
attack = 0;
decay = 0;
sustain = 0;
release = 0;
gate = 0;
rate_counter = 0;
exponential_counter = 0;
exponential_counter_period = 1;
state = RELEASE;
rate_period = rate_counter_period[release];
hold_zero = true;
}
reg16 EnvelopeGenerator::rate_counter_period[] = {
9, // 2ms*1.0MHz/256 = 7.81
32, // 8ms*1.0MHz/256 = 31.25
63, // 16ms*1.0MHz/256 = 62.50
95, // 24ms*1.0MHz/256 = 93.75
149, // 38ms*1.0MHz/256 = 148.44
220, // 56ms*1.0MHz/256 = 218.75
267, // 68ms*1.0MHz/256 = 265.63
313, // 80ms*1.0MHz/256 = 312.50
392, // 100ms*1.0MHz/256 = 390.63
977, // 250ms*1.0MHz/256 = 976.56
1954, // 500ms*1.0MHz/256 = 1953.13
3126, // 800ms*1.0MHz/256 = 3125.00
3907, // 1 s*1.0MHz/256 = 3906.25
11720, // 3 s*1.0MHz/256 = 11718.75
19532, // 5 s*1.0MHz/256 = 19531.25
31251 // 8 s*1.0MHz/256 = 31250.00
};
reg8 EnvelopeGenerator::sustain_level[] = {
0x00,
0x11,
0x22,
0x33,
0x44,
0x55,
0x66,
0x77,
0x88,
0x99,
0xaa,
0xbb,
0xcc,
0xdd,
0xee,
0xff,
};
void EnvelopeGenerator::writeCONTROL_REG(reg8 control) {
reg8 gate_next = control & 0x01;
// The rate counter is never reset, thus there will be a delay before the
// envelope counter starts counting up (attack) or down (release).
// Gate bit on: Start attack, decay, sustain.
if (!gate && gate_next) {
state = ATTACK;
rate_period = rate_counter_period[attack];
// Switching to attack state unlocks the zero freeze.
hold_zero = false;
}
// Gate bit off: Start release.
else if (gate && !gate_next) {
state = RELEASE;
rate_period = rate_counter_period[release];
}
gate = gate_next;
}
void EnvelopeGenerator::writeATTACK_DECAY(reg8 attack_decay) {
attack = (attack_decay >> 4) & 0x0f;
decay = attack_decay & 0x0f;
if (state == ATTACK) {
rate_period = rate_counter_period[attack];
}
else if (state == DECAY_SUSTAIN) {
rate_period = rate_counter_period[decay];
}
}
void EnvelopeGenerator::writeSUSTAIN_RELEASE(reg8 sustain_release) {
sustain = (sustain_release >> 4) & 0x0f;
release = sustain_release & 0x0f;
if (state == RELEASE) {
rate_period = rate_counter_period[release];
}
}
reg8 EnvelopeGenerator::readENV() {
return output();
}
RESID_INLINE void EnvelopeGenerator::updateClock(cycle_count delta_t) {
// Check for ADSR delay bug.
// If the rate counter comparison value is set below the current value of the
// rate counter, the counter will continue counting up until it wraps around
// to zero at 2^15 = 0x8000, and then count rate_period - 1 before the
// envelope can finally be stepped.
// This has been verified by sampling ENV3.
//
// NB! This requires two's complement integer.
int rate_step = rate_period - rate_counter;
if (rate_step <= 0) {
rate_step += 0x7fff;
}
while (delta_t) {
if (delta_t < rate_step) {
rate_counter += delta_t;
if (rate_counter & 0x8000) {
++rate_counter &= 0x7fff;
}
return;
}
rate_counter = 0;
delta_t -= rate_step;
// The first envelope step in the attack state also resets the exponential
// counter. This has been verified by sampling ENV3.
//
if (state == ATTACK || ++exponential_counter == exponential_counter_period)
{
exponential_counter = 0;
// Check whether the envelope counter is frozen at zero.
if (hold_zero) {
rate_step = rate_period;
continue;
}
switch (state) {
case ATTACK:
// The envelope counter can flip from 0xff to 0x00 by changing state to
// release, then to attack. The envelope counter is then frozen at
// zero; to unlock this situation the state must be changed to release,
// then to attack. This has been verified by sampling ENV3.
//
++envelope_counter &= 0xff;
if (envelope_counter == 0xff) {
state = DECAY_SUSTAIN;
rate_period = rate_counter_period[decay];
}
break;
case DECAY_SUSTAIN:
if (envelope_counter != sustain_level[sustain]) {
--envelope_counter;
}
break;
case RELEASE:
// The envelope counter can flip from 0x00 to 0xff by changing state to
// attack, then to release. The envelope counter will then continue
// counting down in the release state.
// This has been verified by sampling ENV3.
// NB! The operation below requires two's complement integer.
//
--envelope_counter &= 0xff;
break;
}
// Check for change of exponential counter period.
switch (envelope_counter) {
case 0xff:
exponential_counter_period = 1;
break;
case 0x5d:
exponential_counter_period = 2;
break;
case 0x36:
exponential_counter_period = 4;
break;
case 0x1a:
exponential_counter_period = 8;
break;
case 0x0e:
exponential_counter_period = 16;
break;
case 0x06:
exponential_counter_period = 30;
break;
case 0x00:
exponential_counter_period = 1;
// When the envelope counter is changed to zero, it is frozen at zero.
// This has been verified by sampling ENV3.
hold_zero = true;
break;
}
}
rate_step = rate_period;
}
}
RESID_INLINE reg8 EnvelopeGenerator::output() {
return envelope_counter;
}
/*
* ExternalFilter
*/
ExternalFilter::ExternalFilter() {
reset();
enable_filter(true);
set_sampling_parameter(15915.6);
mixer_DC = ((((0x800 - 0x380) + 0x800)*0xff*3 - 0xfff*0xff/18) >> 7)*0x0f;
}
void ExternalFilter::enable_filter(bool enable) {
enabled = enable;
}
void ExternalFilter::set_sampling_parameter(double pass_freq) {
static const double pi = 3.1415926535897932385;
w0hp = 105;
w0lp = (sound_sample) (pass_freq * (2.0 * pi * 1.048576));
if (w0lp > 104858)
w0lp = 104858;
}
void ExternalFilter::reset() {
// State of filter.
Vlp = 0;
Vhp = 0;
Vo = 0;
}
RESID_INLINE void ExternalFilter::updateClock(cycle_count delta_t, sound_sample Vi) {
// This is handy for testing.
if (!enabled) {
// Remove maximum DC level since there is no filter to do it.
Vlp = Vhp = 0;
Vo = Vi - mixer_DC;
return;
}
// Maximum delta cycles for the external filter to work satisfactorily
// is approximately 8.
cycle_count delta_t_flt = 8;
while (delta_t) {
if (delta_t < delta_t_flt) {
delta_t_flt = delta_t;
}
// delta_t is converted to seconds given a 1MHz clock by dividing
// with 1 000 000.
// Calculate filter outputs.
// Vo = Vlp - Vhp;
// Vlp = Vlp + w0lp*(Vi - Vlp)*delta_t;
// Vhp = Vhp + w0hp*(Vlp - Vhp)*delta_t;
sound_sample dVlp = (w0lp*delta_t_flt >> 8)*(Vi - Vlp) >> 12;
sound_sample dVhp = w0hp*delta_t_flt*(Vlp - Vhp) >> 20;
Vo = Vlp - Vhp;
Vlp += dVlp;
Vhp += dVhp;
delta_t -= delta_t_flt;
}
}
RESID_INLINE sound_sample ExternalFilter::output() {
return Vo;
}
/*
* Voice
*/
Voice::Voice() {
wave_zero = 0x380;
voice_DC = 0x800*0xff;
}
void Voice::set_sync_source(Voice* source) {
wave.set_sync_source(&source->wave);
}
void Voice::writeCONTROL_REG(reg8 control) {
wave.writeCONTROL_REG(control);
envelope.writeCONTROL_REG(control);
}
void Voice::reset() {
wave.reset();
envelope.reset();
}
/*
* SID
*/
SID::SID() {
voice[0].set_sync_source(&voice[2]);
voice[1].set_sync_source(&voice[0]);
voice[2].set_sync_source(&voice[1]);
set_sampling_parameters(985248, 44100);
bus_value = 0;
bus_value_ttl = 0;
}
SID::~SID() {}
void SID::reset() {
for (int i = 0; i < 3; i++) {
voice[i].reset();
}
filter.reset();
extfilt.reset();
bus_value = 0;
bus_value_ttl = 0;
}
int SID::output() {
const int range = 1 << 16;
const int half = range >> 1;
int sample = extfilt.output()/((4095*255 >> 7)*3*15*2/range);
if (sample >= half) {
return half - 1;
}
if (sample < -half) {
return -half;
}
return sample;
}
/**
* Read registers.
*
* Reading a write only register returns the last byte written to any SID
* register. The individual bits in this value start to fade down towards
* zero after a few cycles. All bits reach zero within approximately
* $2000 - $4000 cycles.
* It has been claimed that this fading happens in an orderly fashion, however
* sampling of write only registers reveals that this is not the case.
* NB! This is not correctly modeled.
* The actual use of write only registers has largely been made in the belief
* that all SID registers are readable. To support this belief the read
* would have to be done immediately after a write to the same register
* (remember that an intermediate write to another register would yield that
* value instead). With this in mind we return the last value written to
* any SID register for $2000 cycles without modeling the bit fading.
*/
reg8 SID::read(reg8 offset) {
switch (offset) {
case 0x19:
case 0x1a:
return 0; //readPOT();
case 0x1b:
return voice[2].wave.readOSC();
case 0x1c:
return voice[2].envelope.readENV();
default:
return bus_value;
}
}
void SID::write(reg8 offset, reg8 value) {
bus_value = value;
bus_value_ttl = 0x2000;
switch (offset) {
case 0x00:
voice[0].wave.writeFREQ_LO(value);
break;
case 0x01:
voice[0].wave.writeFREQ_HI(value);
break;
case 0x02:
voice[0].wave.writePW_LO(value);
break;
case 0x03:
voice[0].wave.writePW_HI(value);
break;
case 0x04:
voice[0].writeCONTROL_REG(value);
break;
case 0x05:
voice[0].envelope.writeATTACK_DECAY(value);
break;
case 0x06:
voice[0].envelope.writeSUSTAIN_RELEASE(value);
break;
case 0x07:
voice[1].wave.writeFREQ_LO(value);
break;
case 0x08:
voice[1].wave.writeFREQ_HI(value);
break;
case 0x09:
voice[1].wave.writePW_LO(value);
break;
case 0x0a:
voice[1].wave.writePW_HI(value);
break;
case 0x0b:
voice[1].writeCONTROL_REG(value);
break;
case 0x0c:
voice[1].envelope.writeATTACK_DECAY(value);
break;
case 0x0d:
voice[1].envelope.writeSUSTAIN_RELEASE(value);
break;
case 0x0e:
voice[2].wave.writeFREQ_LO(value);
break;
case 0x0f:
voice[2].wave.writeFREQ_HI(value);
break;
case 0x10:
voice[2].wave.writePW_LO(value);
break;
case 0x11:
voice[2].wave.writePW_HI(value);
break;
case 0x12:
voice[2].writeCONTROL_REG(value);
break;
case 0x13:
voice[2].envelope.writeATTACK_DECAY(value);
break;
case 0x14:
voice[2].envelope.writeSUSTAIN_RELEASE(value);
break;
case 0x15:
filter.writeFC_LO(value);
break;
case 0x16:
filter.writeFC_HI(value);
break;
case 0x17:
filter.writeRES_FILT(value);
break;
case 0x18:
filter.writeMODE_VOL(value);
break;
default:
break;
}
}
void SID::enable_filter(bool enable) {
filter.enable_filter(enable);
}
void SID::enable_external_filter(bool enable) {
extfilt.enable_filter(enable);
}
/**
* Setting of SID sampling parameters.
*
* Use a clock freqency of 985248Hz for PAL C64, 1022730Hz for NTSC C64.
* The default end of passband frequency is pass_freq = 0.9*sample_freq/2
* for sample frequencies up to ~ 44.1kHz, and 20kHz for higher sample
* frequencies.
*
* For resampling, the ratio between the clock frequency and the sample
* frequency is limited as follows:
* 125*clock_freq/sample_freq < 16384
* E.g. provided a clock frequency of ~ 1MHz, the sample frequency can not
* be set lower than ~ 8kHz. A lower sample frequency would make the
* resampling code overfill its 16k sample ring buffer.
*
* The end of passband frequency is also limited:
* pass_freq <= 0.9*sample_freq/2
*
* E.g. for a 44.1kHz sampling rate the end of passband frequency is limited
* to slightly below 20kHz. This constraint ensures that the FIR table is
* not overfilled.
*/
bool SID::set_sampling_parameters(double clock_freq,
double sample_freq, double pass_freq,
double filter_scale)
{
// The default passband limit is 0.9*sample_freq/2 for sample
// frequencies below ~ 44.1kHz, and 20kHz for higher sample frequencies.
if (pass_freq < 0) {
pass_freq = 20000;
if (2*pass_freq/sample_freq >= 0.9) {
pass_freq = 0.9*sample_freq/2;
}
}
// Check whether the FIR table would overfill.
else if (pass_freq > 0.9*sample_freq/2) {
return false;
}
// The filter scaling is only included to avoid clipping, so keep
// it sane.
if (filter_scale < 0.9 || filter_scale > 1.0) {
return false;
}
// Set the external filter to the pass freq
extfilt.set_sampling_parameter (pass_freq);
clock_frequency = clock_freq;
cycles_per_sample =
cycle_count(clock_freq/sample_freq*(1 << FIXP_SHIFT) + 0.5);
sample_offset = 0;
sample_prev = 0;
return true;
}
void SID::updateClock(cycle_count delta_t) {
int i;
if (delta_t <= 0) {
return;
}
// Age bus value.
bus_value_ttl -= delta_t;
if (bus_value_ttl <= 0) {
bus_value = 0;
bus_value_ttl = 0;
}
// Clock amplitude modulators.
for (i = 0; i < 3; i++) {
voice[i].envelope.updateClock(delta_t);
}
// Clock and synchronize oscillators.
// Loop until we reach the current cycle.
cycle_count delta_t_osc = delta_t;
while (delta_t_osc) {
cycle_count delta_t_min = delta_t_osc;
// Find minimum number of cycles to an oscillator accumulator MSB toggle.
// We have to clock on each MSB on / MSB off for hard sync to operate
// correctly.
for (i = 0; i < 3; i++) {
WaveformGenerator& wave = voice[i].wave;
// It is only necessary to clock on the MSB of an oscillator that is
// a sync source and has freq != 0.
if (!(wave.sync_dest->sync && wave.freq)) {
continue;
}
reg16 freq = wave.freq;
reg24 accumulator = wave.accumulator;
// Clock on MSB off if MSB is on, clock on MSB on if MSB is off.
reg24 delta_accumulator =
(accumulator & 0x800000 ? 0x1000000 : 0x800000) - accumulator;
cycle_count delta_t_next = delta_accumulator/freq;
if (delta_accumulator%freq) {
++delta_t_next;
}
if (delta_t_next < delta_t_min) {
delta_t_min = delta_t_next;
}
}
// Clock oscillators.
for (i = 0; i < 3; i++) {
voice[i].wave.updateClock(delta_t_min);
}
// Synchronize oscillators.
for (i = 0; i < 3; i++) {
voice[i].wave.synchronize();
}
delta_t_osc -= delta_t_min;
}
// Clock filter.
filter.updateClock(delta_t,
voice[0].output(), voice[1].output(), voice[2].output());
// Clock external filter.
extfilt.updateClock(delta_t, filter.output());
}
/**
* SID clocking with audio sampling.
* Fixpoint arithmetics is used.
*/
int SID::updateClock(cycle_count& delta_t, short* buf, int n, int interleave) {
int s = 0;
for (;;) {
cycle_count next_sample_offset = sample_offset + cycles_per_sample + (1 << (FIXP_SHIFT - 1));
cycle_count delta_t_sample = next_sample_offset >> FIXP_SHIFT;
if (delta_t_sample > delta_t) {
break;
}
if (s >= n) {
return s;
}
updateClock(delta_t_sample);
delta_t -= delta_t_sample;
sample_offset = (next_sample_offset & FIXP_MASK) - (1 << (FIXP_SHIFT - 1));
buf[s++*interleave] = output();
}
updateClock(delta_t);
sample_offset -= delta_t << FIXP_SHIFT;
delta_t = 0;
return s;
}
}
// Plugin interface
// (This can only create a null driver since C64 audio support is not part of the
// midi driver architecture. But we need the plugin for the options menu in the launcher
// and for MidiDriver::detectDevice() which is more or less used by all engines.)
class C64MusicPlugin : public NullMusicPlugin {
public:
const char *getName() const {
return _s("C64 Audio emulator");
}
const char *getId() const {
return "C64";
}
MusicDevices getDevices() const;
};
MusicDevices C64MusicPlugin::getDevices() const {
MusicDevices devices;
devices.push_back(MusicDevice(this, "", MT_C64));
return devices;
}
//#if PLUGIN_ENABLED_DYNAMIC(C64)
//REGISTER_PLUGIN_DYNAMIC(C64, PLUGIN_TYPE_MUSIC, C64MusicPlugin);
//#else
REGISTER_PLUGIN_STATIC(C64, PLUGIN_TYPE_MUSIC, C64MusicPlugin);
//#endif
#endif