/** * encoding: UTF-8 * This file is part of reSID, a MOS6581 SID emulator engine. * Copyright (C) 2004 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * @author Ken Händel * */ package resid; import resid.ISIDDefs.chip_model; /** * * The audio output stage in a Commodore 64 consists of two STC networks, a * low-pass filter with 3-dB frequency 16kHz followed by a high-pass filter with * 3-dB frequency 16Hz (the latter provided an audio equipment input impedance * of 1kOhm). * <P> * The STC networks are connected with a BJT supposedly meant to act as a unity * gain buffer, which is not really how it works. A more elaborate model would * include the BJT, however DC circuit analysis yields BJT base-emitter and * emitter-base impedances sufficiently low to produce additional low-pass and * high-pass 3dB-frequencies in the order of hundreds of kHz. This calls for a * sampling frequency of several MHz, which is far too high for practical use. * * @author Ken Händel * */ public class ExternalFilter { /** * Filter enabled. */ protected boolean enabled; /** * Maximum mixer DC offset. */ protected int /* sound_sample */mixer_DC; /** * State of filters. lowpass */ protected int /* sound_sample */Vlp; /** * State of filters. highpass */ protected int /* sound_sample */Vhp; /** * State of filters. */ protected int /* sound_sample */Vo; /** * Cutoff frequencies. */ protected int /* sound_sample */w0lp; /** * Cutoff frequencies. */ protected int /* sound_sample */w0hp; // ---------------------------------------------------------------------------- // Inline functions. // The following functions are defined inline because they are called every // time a sample is calculated. // ---------------------------------------------------------------------------- /** * SID clocking - 1 cycle. * * @param Vi */ public void clock(int /* 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; } // 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; int /* sound_sample */dVlp = (w0lp >> 8) * (Vi - Vlp) >> 12; int /* sound_sample */dVhp = w0hp * (Vlp - Vhp) >> 20; Vo = Vlp - Vhp; Vlp += dVlp; Vhp += dVhp; } /** * SID clocking - delta_t cycles. * * @param delta_t * @param Vi */ public void clock(int /* cycle_count */delta_t, int /* 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. int /* cycle_count */delta_t_flt = 8; while (delta_t != 0) { 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; int /* sound_sample */dVlp = (w0lp * delta_t_flt >> 8) * (Vi - Vlp) >> 12; int /* sound_sample */dVhp = w0hp * delta_t_flt * (Vlp - Vhp) >> 20; Vo = Vlp - Vhp; Vlp += dVlp; Vhp += dVhp; delta_t -= delta_t_flt; } } /** * Audio output (20 bits). * * Audio output (19.5 bits). * * @return Vo */ public int /* sound_sample */output() { return Vo; } // ---------------------------------------------------------------------------- // END Inline functions. // ---------------------------------------------------------------------------- /** * Constructor. */ public ExternalFilter() { reset(); enable_filter(true); set_sampling_parameter(15915.6); set_chip_model(chip_model.MOS6581); } /** * Enable filter. * * @param enable * enable filter */ public void enable_filter(boolean enable) { enabled = enable; } /** * Setup of the external filter sampling parameters. * * @param pass_freq */ public void set_sampling_parameter(double pass_freq) { final double pi = 3.1415926535897932385; // Low-pass: R = 10kOhm, C = 1000pF; w0l = 1/RC = 1/(1e4*1e-9) = 100000 // High-pass: R = 1kOhm, C = 10uF; w0h = 1/RC = 1/(1e3*1e-5) = 100 // Multiply with 1.048576 to facilitate division by 1 000 000 by right- // shifting 20 times (2 ^ 20 = 1048576). w0hp = 105; w0lp = (int /* sound_sample */) (pass_freq * (2.0 * pi * 1.048576)); if (w0lp > 104858) w0lp = 104858; } /** * Set chip model. * * @param model * chip model */ public void set_chip_model(chip_model model) { if (model == chip_model.MOS6581) { // Maximum mixer DC output level; to be removed if the external // filter is turned off: ((wave DC + voice DC) * voices + mixer DC) // * volume // See Voice.java and Filter.java for an explanation of the values. mixer_DC = ((((0x800 - 0x380) + 0x800) * 0xff * 3 - 0xfff * 0xff / 18) >> 7) * 0x0f; } else { // No DC offsets in the MOS8580. mixer_DC = 0; } } /** * SID reset. */ public void reset() { // State of filter. Vlp = 0; Vhp = 0; Vo = 0; } }