Mercurial > spc_convert
diff fast_dsp/SPC_DSP.h @ 0:e38dacceb958
initial import
author | Robert McIntyre <rlm@mit.edu> |
---|---|
date | Fri, 21 Oct 2011 05:53:11 -0700 |
parents | |
children |
line wrap: on
line diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/fast_dsp/SPC_DSP.h Fri Oct 21 05:53:11 2011 -0700 1.3 @@ -0,0 +1,212 @@ 1.4 +// Fast SNES SPC-700 DSP emulator (about 3x speed of accurate one) 1.5 + 1.6 +// snes_spc 0.9.0 1.7 +#ifndef SPC_DSP_H 1.8 +#define SPC_DSP_H 1.9 + 1.10 +#include "blargg_common.h" 1.11 + 1.12 +class SPC_DSP { 1.13 +public: 1.14 + typedef BOOST::uint8_t uint8_t; 1.15 + 1.16 +// Setup 1.17 + 1.18 + // Initializes DSP and has it use the 64K RAM provided 1.19 + void init( void* ram_64k ); 1.20 + 1.21 + // Sets destination for output samples. If out is NULL or out_size is 0, 1.22 + // doesn't generate any. 1.23 + typedef short sample_t; 1.24 + void set_output( sample_t* out, int out_size ); 1.25 + 1.26 + // Number of samples written to output since it was last set, always 1.27 + // a multiple of 2. Undefined if more samples were generated than 1.28 + // output buffer could hold. 1.29 + int sample_count() const; 1.30 + 1.31 +// Emulation 1.32 + 1.33 + // Resets DSP to power-on state 1.34 + void reset(); 1.35 + 1.36 + // Emulates pressing reset switch on SNES 1.37 + void soft_reset(); 1.38 + 1.39 + // Reads/writes DSP registers. For accuracy, you must first call spc_run_dsp() 1.40 + // to catch the DSP up to present. 1.41 + int read ( int addr ) const; 1.42 + void write( int addr, int data ); 1.43 + 1.44 + // Runs DSP for specified number of clocks (~1024000 per second). Every 32 clocks 1.45 + // a pair of samples is be generated. 1.46 + void run( int clock_count ); 1.47 + 1.48 +// Sound control 1.49 + 1.50 + // Mutes voices corresponding to non-zero bits in mask (overrides VxVOL with 0). 1.51 + // Reduces emulation accuracy. 1.52 + enum { voice_count = 8 }; 1.53 + void mute_voices( int mask ); 1.54 + 1.55 + // If true, prevents channels and global volumes from being phase-negated 1.56 + void disable_surround( bool disable = true ); 1.57 + 1.58 +// State 1.59 + 1.60 + // Resets DSP and uses supplied values to initialize registers 1.61 + enum { register_count = 128 }; 1.62 + void load( uint8_t const regs [register_count] ); 1.63 + 1.64 +// DSP register addresses 1.65 + 1.66 + // Global registers 1.67 + enum { 1.68 + r_mvoll = 0x0C, r_mvolr = 0x1C, 1.69 + r_evoll = 0x2C, r_evolr = 0x3C, 1.70 + r_kon = 0x4C, r_koff = 0x5C, 1.71 + r_flg = 0x6C, r_endx = 0x7C, 1.72 + r_efb = 0x0D, r_pmon = 0x2D, 1.73 + r_non = 0x3D, r_eon = 0x4D, 1.74 + r_dir = 0x5D, r_esa = 0x6D, 1.75 + r_edl = 0x7D, 1.76 + r_fir = 0x0F // 8 coefficients at 0x0F, 0x1F ... 0x7F 1.77 + }; 1.78 + 1.79 + // Voice registers 1.80 + enum { 1.81 + v_voll = 0x00, v_volr = 0x01, 1.82 + v_pitchl = 0x02, v_pitchh = 0x03, 1.83 + v_srcn = 0x04, v_adsr0 = 0x05, 1.84 + v_adsr1 = 0x06, v_gain = 0x07, 1.85 + v_envx = 0x08, v_outx = 0x09 1.86 + }; 1.87 + 1.88 +public: 1.89 + enum { extra_size = 16 }; 1.90 + sample_t* extra() { return m.extra; } 1.91 + sample_t const* out_pos() const { return m.out; } 1.92 +public: 1.93 + BLARGG_DISABLE_NOTHROW 1.94 + 1.95 + typedef BOOST::int8_t int8_t; 1.96 + typedef BOOST::int16_t int16_t; 1.97 + 1.98 + enum { echo_hist_size = 8 }; 1.99 + 1.100 + enum env_mode_t { env_release, env_attack, env_decay, env_sustain }; 1.101 + enum { brr_buf_size = 12 }; 1.102 + struct voice_t 1.103 + { 1.104 + int buf [brr_buf_size*2];// decoded samples (twice the size to simplify wrap handling) 1.105 + int* buf_pos; // place in buffer where next samples will be decoded 1.106 + int interp_pos; // relative fractional position in sample (0x1000 = 1.0) 1.107 + int brr_addr; // address of current BRR block 1.108 + int brr_offset; // current decoding offset in BRR block 1.109 + int kon_delay; // KON delay/current setup phase 1.110 + env_mode_t env_mode; 1.111 + int env; // current envelope level 1.112 + int hidden_env; // used by GAIN mode 7, very obscure quirk 1.113 + int volume [2]; // copy of volume from DSP registers, with surround disabled 1.114 + int enabled; // -1 if enabled, 0 if muted 1.115 + }; 1.116 +private: 1.117 + struct state_t 1.118 + { 1.119 + uint8_t regs [register_count]; 1.120 + 1.121 + // Echo history keeps most recent 8 samples (twice the size to simplify wrap handling) 1.122 + int echo_hist [echo_hist_size * 2] [2]; 1.123 + int (*echo_hist_pos) [2]; // &echo_hist [0 to 7] 1.124 + 1.125 + int every_other_sample; // toggles every sample 1.126 + int kon; // KON value when last checked 1.127 + int noise; 1.128 + int echo_offset; // offset from ESA in echo buffer 1.129 + int echo_length; // number of bytes that echo_offset will stop at 1.130 + int phase; // next clock cycle to run (0-31) 1.131 + unsigned counters [4]; 1.132 + 1.133 + int new_kon; 1.134 + int t_koff; 1.135 + 1.136 + voice_t voices [voice_count]; 1.137 + 1.138 + unsigned* counter_select [32]; 1.139 + 1.140 + // non-emulation state 1.141 + uint8_t* ram; // 64K shared RAM between DSP and SMP 1.142 + int mute_mask; 1.143 + int surround_threshold; 1.144 + sample_t* out; 1.145 + sample_t* out_end; 1.146 + sample_t* out_begin; 1.147 + sample_t extra [extra_size]; 1.148 + }; 1.149 + state_t m; 1.150 + 1.151 + void init_counter(); 1.152 + void run_counter( int ); 1.153 + void soft_reset_common(); 1.154 + void write_outline( int addr, int data ); 1.155 + void update_voice_vol( int addr ); 1.156 +}; 1.157 + 1.158 +#include <assert.h> 1.159 + 1.160 +inline int SPC_DSP::sample_count() const { return m.out - m.out_begin; } 1.161 + 1.162 +inline int SPC_DSP::read( int addr ) const 1.163 +{ 1.164 + assert( (unsigned) addr < register_count ); 1.165 + return m.regs [addr]; 1.166 +} 1.167 + 1.168 +inline void SPC_DSP::update_voice_vol( int addr ) 1.169 +{ 1.170 + int l = (int8_t) m.regs [addr + v_voll]; 1.171 + int r = (int8_t) m.regs [addr + v_volr]; 1.172 + 1.173 + if ( l * r < m.surround_threshold ) 1.174 + { 1.175 + // signs differ, so negate those that are negative 1.176 + l ^= l >> 7; 1.177 + r ^= r >> 7; 1.178 + } 1.179 + 1.180 + voice_t& v = m.voices [addr >> 4]; 1.181 + int enabled = v.enabled; 1.182 + v.volume [0] = l & enabled; 1.183 + v.volume [1] = r & enabled; 1.184 +} 1.185 + 1.186 +inline void SPC_DSP::write( int addr, int data ) 1.187 +{ 1.188 + assert( (unsigned) addr < register_count ); 1.189 + 1.190 + m.regs [addr] = (uint8_t) data; 1.191 + int low = addr & 0x0F; 1.192 + if ( low < 0x2 ) // voice volumes 1.193 + { 1.194 + update_voice_vol( low ^ addr ); 1.195 + } 1.196 + else if ( low == 0xC ) 1.197 + { 1.198 + if ( addr == r_kon ) 1.199 + m.new_kon = (uint8_t) data; 1.200 + 1.201 + if ( addr == r_endx ) // always cleared, regardless of data written 1.202 + m.regs [r_endx] = 0; 1.203 + } 1.204 +} 1.205 + 1.206 +inline void SPC_DSP::disable_surround( bool disable ) 1.207 +{ 1.208 + m.surround_threshold = disable ? 0 : -0x4000; 1.209 +} 1.210 + 1.211 +#define SPC_NO_COPY_STATE_FUNCS 1 1.212 + 1.213 +#define SPC_LESS_ACCURATE 1 1.214 + 1.215 +#endif