Mercurial > spc_convert
comparison 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 |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:e38dacceb958 |
---|---|
1 // Fast SNES SPC-700 DSP emulator (about 3x speed of accurate one) | |
2 | |
3 // snes_spc 0.9.0 | |
4 #ifndef SPC_DSP_H | |
5 #define SPC_DSP_H | |
6 | |
7 #include "blargg_common.h" | |
8 | |
9 class SPC_DSP { | |
10 public: | |
11 typedef BOOST::uint8_t uint8_t; | |
12 | |
13 // Setup | |
14 | |
15 // Initializes DSP and has it use the 64K RAM provided | |
16 void init( void* ram_64k ); | |
17 | |
18 // Sets destination for output samples. If out is NULL or out_size is 0, | |
19 // doesn't generate any. | |
20 typedef short sample_t; | |
21 void set_output( sample_t* out, int out_size ); | |
22 | |
23 // Number of samples written to output since it was last set, always | |
24 // a multiple of 2. Undefined if more samples were generated than | |
25 // output buffer could hold. | |
26 int sample_count() const; | |
27 | |
28 // Emulation | |
29 | |
30 // Resets DSP to power-on state | |
31 void reset(); | |
32 | |
33 // Emulates pressing reset switch on SNES | |
34 void soft_reset(); | |
35 | |
36 // Reads/writes DSP registers. For accuracy, you must first call spc_run_dsp() | |
37 // to catch the DSP up to present. | |
38 int read ( int addr ) const; | |
39 void write( int addr, int data ); | |
40 | |
41 // Runs DSP for specified number of clocks (~1024000 per second). Every 32 clocks | |
42 // a pair of samples is be generated. | |
43 void run( int clock_count ); | |
44 | |
45 // Sound control | |
46 | |
47 // Mutes voices corresponding to non-zero bits in mask (overrides VxVOL with 0). | |
48 // Reduces emulation accuracy. | |
49 enum { voice_count = 8 }; | |
50 void mute_voices( int mask ); | |
51 | |
52 // If true, prevents channels and global volumes from being phase-negated | |
53 void disable_surround( bool disable = true ); | |
54 | |
55 // State | |
56 | |
57 // Resets DSP and uses supplied values to initialize registers | |
58 enum { register_count = 128 }; | |
59 void load( uint8_t const regs [register_count] ); | |
60 | |
61 // DSP register addresses | |
62 | |
63 // Global registers | |
64 enum { | |
65 r_mvoll = 0x0C, r_mvolr = 0x1C, | |
66 r_evoll = 0x2C, r_evolr = 0x3C, | |
67 r_kon = 0x4C, r_koff = 0x5C, | |
68 r_flg = 0x6C, r_endx = 0x7C, | |
69 r_efb = 0x0D, r_pmon = 0x2D, | |
70 r_non = 0x3D, r_eon = 0x4D, | |
71 r_dir = 0x5D, r_esa = 0x6D, | |
72 r_edl = 0x7D, | |
73 r_fir = 0x0F // 8 coefficients at 0x0F, 0x1F ... 0x7F | |
74 }; | |
75 | |
76 // Voice registers | |
77 enum { | |
78 v_voll = 0x00, v_volr = 0x01, | |
79 v_pitchl = 0x02, v_pitchh = 0x03, | |
80 v_srcn = 0x04, v_adsr0 = 0x05, | |
81 v_adsr1 = 0x06, v_gain = 0x07, | |
82 v_envx = 0x08, v_outx = 0x09 | |
83 }; | |
84 | |
85 public: | |
86 enum { extra_size = 16 }; | |
87 sample_t* extra() { return m.extra; } | |
88 sample_t const* out_pos() const { return m.out; } | |
89 public: | |
90 BLARGG_DISABLE_NOTHROW | |
91 | |
92 typedef BOOST::int8_t int8_t; | |
93 typedef BOOST::int16_t int16_t; | |
94 | |
95 enum { echo_hist_size = 8 }; | |
96 | |
97 enum env_mode_t { env_release, env_attack, env_decay, env_sustain }; | |
98 enum { brr_buf_size = 12 }; | |
99 struct voice_t | |
100 { | |
101 int buf [brr_buf_size*2];// decoded samples (twice the size to simplify wrap handling) | |
102 int* buf_pos; // place in buffer where next samples will be decoded | |
103 int interp_pos; // relative fractional position in sample (0x1000 = 1.0) | |
104 int brr_addr; // address of current BRR block | |
105 int brr_offset; // current decoding offset in BRR block | |
106 int kon_delay; // KON delay/current setup phase | |
107 env_mode_t env_mode; | |
108 int env; // current envelope level | |
109 int hidden_env; // used by GAIN mode 7, very obscure quirk | |
110 int volume [2]; // copy of volume from DSP registers, with surround disabled | |
111 int enabled; // -1 if enabled, 0 if muted | |
112 }; | |
113 private: | |
114 struct state_t | |
115 { | |
116 uint8_t regs [register_count]; | |
117 | |
118 // Echo history keeps most recent 8 samples (twice the size to simplify wrap handling) | |
119 int echo_hist [echo_hist_size * 2] [2]; | |
120 int (*echo_hist_pos) [2]; // &echo_hist [0 to 7] | |
121 | |
122 int every_other_sample; // toggles every sample | |
123 int kon; // KON value when last checked | |
124 int noise; | |
125 int echo_offset; // offset from ESA in echo buffer | |
126 int echo_length; // number of bytes that echo_offset will stop at | |
127 int phase; // next clock cycle to run (0-31) | |
128 unsigned counters [4]; | |
129 | |
130 int new_kon; | |
131 int t_koff; | |
132 | |
133 voice_t voices [voice_count]; | |
134 | |
135 unsigned* counter_select [32]; | |
136 | |
137 // non-emulation state | |
138 uint8_t* ram; // 64K shared RAM between DSP and SMP | |
139 int mute_mask; | |
140 int surround_threshold; | |
141 sample_t* out; | |
142 sample_t* out_end; | |
143 sample_t* out_begin; | |
144 sample_t extra [extra_size]; | |
145 }; | |
146 state_t m; | |
147 | |
148 void init_counter(); | |
149 void run_counter( int ); | |
150 void soft_reset_common(); | |
151 void write_outline( int addr, int data ); | |
152 void update_voice_vol( int addr ); | |
153 }; | |
154 | |
155 #include <assert.h> | |
156 | |
157 inline int SPC_DSP::sample_count() const { return m.out - m.out_begin; } | |
158 | |
159 inline int SPC_DSP::read( int addr ) const | |
160 { | |
161 assert( (unsigned) addr < register_count ); | |
162 return m.regs [addr]; | |
163 } | |
164 | |
165 inline void SPC_DSP::update_voice_vol( int addr ) | |
166 { | |
167 int l = (int8_t) m.regs [addr + v_voll]; | |
168 int r = (int8_t) m.regs [addr + v_volr]; | |
169 | |
170 if ( l * r < m.surround_threshold ) | |
171 { | |
172 // signs differ, so negate those that are negative | |
173 l ^= l >> 7; | |
174 r ^= r >> 7; | |
175 } | |
176 | |
177 voice_t& v = m.voices [addr >> 4]; | |
178 int enabled = v.enabled; | |
179 v.volume [0] = l & enabled; | |
180 v.volume [1] = r & enabled; | |
181 } | |
182 | |
183 inline void SPC_DSP::write( int addr, int data ) | |
184 { | |
185 assert( (unsigned) addr < register_count ); | |
186 | |
187 m.regs [addr] = (uint8_t) data; | |
188 int low = addr & 0x0F; | |
189 if ( low < 0x2 ) // voice volumes | |
190 { | |
191 update_voice_vol( low ^ addr ); | |
192 } | |
193 else if ( low == 0xC ) | |
194 { | |
195 if ( addr == r_kon ) | |
196 m.new_kon = (uint8_t) data; | |
197 | |
198 if ( addr == r_endx ) // always cleared, regardless of data written | |
199 m.regs [r_endx] = 0; | |
200 } | |
201 } | |
202 | |
203 inline void SPC_DSP::disable_surround( bool disable ) | |
204 { | |
205 m.surround_threshold = disable ? 0 : -0x4000; | |
206 } | |
207 | |
208 #define SPC_NO_COPY_STATE_FUNCS 1 | |
209 | |
210 #define SPC_LESS_ACCURATE 1 | |
211 | |
212 #endif |