/* 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 */ #ifndef AUDIO_SOFTSYNTH_SID_H #define AUDIO_SOFTSYNTH_SID_H // Inlining on/off. #define RESID_INLINE inline namespace Resid { // We could have used the smallest possible data type for each SID register, // however this would give a slower engine because of data type conversions. // An int is assumed to be at least 32 bits (necessary in the types reg24, // cycle_count, and sound_sample). GNU does not support 16-bit machines // (GNU Coding Standards: Portability between CPUs), so this should be // a valid assumption. typedef unsigned int reg4; typedef unsigned int reg8; typedef unsigned int reg12; typedef unsigned int reg16; typedef unsigned int reg24; typedef int cycle_count; typedef int sound_sample; typedef sound_sample fc_point[2]; class WaveformGenerator { public: WaveformGenerator(); void set_sync_source(WaveformGenerator *); void updateClock(cycle_count delta_t); void synchronize(); void reset(); void writeFREQ_LO(reg8); void writeFREQ_HI(reg8); void writePW_LO(reg8); void writePW_HI(reg8); void writeCONTROL_REG(reg8); reg8 readOSC(); // 12-bit waveform output. reg12 output(); protected: const WaveformGenerator* sync_source; WaveformGenerator* sync_dest; // Tell whether the accumulator MSB was set high on this cycle. bool msb_rising; reg24 accumulator; reg24 shift_register; // Fout = (Fn*Fclk/16777216)Hz reg16 freq; // PWout = (PWn/40.95)% reg12 pw; // The control register right-shifted 4 bits; used for output function // table lookup. reg8 waveform; // The remaining control register bits. reg8 test; reg8 ring_mod; reg8 sync; // The gate bit is handled by the EnvelopeGenerator. // 16 possible combinations of waveforms. reg12 output____(); reg12 output___T(); reg12 output__S_(); reg12 output__ST(); reg12 output_P__(); reg12 output_P_T(); reg12 output_PS_(); reg12 output_PST(); reg12 outputN___(); reg12 outputN__T(); reg12 outputN_S_(); reg12 outputN_ST(); reg12 outputNP__(); reg12 outputNP_T(); reg12 outputNPS_(); reg12 outputNPST(); // Sample data for combinations of waveforms. static const reg8 wave6581__ST[]; static const reg8 wave6581_P_T[]; static const reg8 wave6581_PS_[]; static const reg8 wave6581_PST[]; friend class Voice; friend class SID; }; class Filter { public: Filter(); void enable_filter(bool enable); void updateClock(cycle_count delta_t, sound_sample voice1, sound_sample voice2, sound_sample voice3); void reset(); // Write registers. void writeFC_LO(reg8); void writeFC_HI(reg8); void writeRES_FILT(reg8); void writeMODE_VOL(reg8); // SID audio output (16 bits). sound_sample output(); protected: void set_w0(); void set_Q(); // Filter enabled. bool enabled; // Filter cutoff frequency. reg12 fc; // Filter resonance. reg8 res; // Selects which inputs to route through filter. reg8 filt; // Switch voice 3 off. reg8 voice3off; // Highpass, bandpass, and lowpass filter modes. reg8 hp_bp_lp; // Output master volume. reg4 vol; // Mixer DC offset. sound_sample mixer_DC; // State of filter. sound_sample Vhp; // highpass sound_sample Vbp; // bandpass sound_sample Vlp; // lowpass sound_sample Vnf; // not filtered // Cutoff frequency, resonance. sound_sample w0, w0_ceil_1, w0_ceil_dt; sound_sample _1024_div_Q; // Cutoff frequency tables. // FC is an 11 bit register. sound_sample f0_6581[2048]; sound_sample* f0; static fc_point f0_points_6581[]; fc_point* f0_points; int f0_count; friend class SID; }; class EnvelopeGenerator { public: EnvelopeGenerator(); enum State { ATTACK, DECAY_SUSTAIN, RELEASE }; void updateClock(cycle_count delta_t); void reset(); void writeCONTROL_REG(reg8); void writeATTACK_DECAY(reg8); void writeSUSTAIN_RELEASE(reg8); reg8 readENV(); // 8-bit envelope output. reg8 output(); protected: reg16 rate_counter; reg16 rate_period; reg8 exponential_counter; reg8 exponential_counter_period; reg8 envelope_counter; bool hold_zero; reg4 attack; reg4 decay; reg4 sustain; reg4 release; reg8 gate; State state; // Lookup table to convert from attack, decay, or release value to rate // counter period. static reg16 rate_counter_period[]; // The 16 selectable sustain levels. static reg8 sustain_level[]; friend class SID; }; class ExternalFilter { public: ExternalFilter(); void enable_filter(bool enable); void set_sampling_parameter(double pass_freq); void updateClock(cycle_count delta_t, sound_sample Vi); void reset(); // Audio output (20 bits). sound_sample output(); protected: // Filter enabled. bool enabled; // Maximum mixer DC offset. sound_sample mixer_DC; // State of filters. sound_sample Vlp; // lowpass sound_sample Vhp; // highpass sound_sample Vo; // Cutoff frequencies. sound_sample w0lp; sound_sample w0hp; friend class SID; }; class Voice { public: Voice(); void set_sync_source(Voice *); void reset(); void writeCONTROL_REG(reg8); // Amplitude modulated waveform output. // Range [-2048*255, 2047*255]. sound_sample output() { // Multiply oscillator output with envelope output. return (wave.output() - wave_zero)*envelope.output() + voice_DC; } protected: WaveformGenerator wave; EnvelopeGenerator envelope; // Waveform D/A zero level. sound_sample wave_zero; // Multiplying D/A DC offset. sound_sample voice_DC; friend class SID; }; class SID { public: SID(); ~SID(); void enable_filter(bool enable); void enable_external_filter(bool enable); bool set_sampling_parameters(double clock_freq, double sample_freq, double pass_freq = -1, double filter_scale = 0.97); void updateClock(cycle_count delta_t); int updateClock(cycle_count& delta_t, short* buf, int n, int interleave = 1); void reset(); // Read/write registers. reg8 read(reg8 offset); void write(reg8 offset, reg8 value); // 16-bit output (AUDIO OUT). int output(); protected: Voice voice[3]; Filter filter; ExternalFilter extfilt; reg8 bus_value; cycle_count bus_value_ttl; double clock_frequency; // Fixpoint constants. static const int FIXP_SHIFT; static const int FIXP_MASK; // Sampling variables. cycle_count cycles_per_sample; cycle_count sample_offset; short sample_prev; }; } #endif // not AUDIO_SOFTSYNTH_SID_H