/* 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 "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