Mercurial > audlegacy-plugins
comparison src/console/Spc_Dsp.h @ 1037:c31e94fefd2a trunk
[svn] - upstream updates regarding handling of SPC700 instructions and runtime
length issues.
author | nenolod |
---|---|
date | Tue, 15 May 2007 13:18:35 -0700 |
parents | 986f098da058 |
children |
comparison
equal
deleted
inserted
replaced
1036:47db8268cb7b | 1037:c31e94fefd2a |
---|---|
1 // Super Nintendo (SNES) SPC DSP emulator | 1 // Fast SNES SPC-700 DSP emulator (about 3x speed of accurate one) |
2 | 2 |
3 // Game_Music_Emu 0.5.2 | 3 // snes_spc 0.9.0 |
4 #ifndef SPC_DSP_H | 4 #ifndef SPC_DSP_H |
5 #define SPC_DSP_H | 5 #define SPC_DSP_H |
6 | 6 |
7 #include "blargg_common.h" | 7 #include "blargg_common.h" |
8 | 8 |
9 class Spc_Dsp { | 9 class Spc_Dsp { |
10 typedef BOOST::int8_t int8_t; | 10 public: |
11 typedef BOOST::uint8_t uint8_t; | 11 typedef BOOST::uint8_t uint8_t; |
12 public: | 12 |
13 | 13 // Setup |
14 // Keeps pointer to 64K ram | 14 |
15 Spc_Dsp( uint8_t* ram ); | 15 // Initializes DSP and has it use the 64K RAM provided |
16 | 16 void init( void* ram_64k ); |
17 // Mute voice n if bit n (1 << n) of mask is clear. | 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. | |
18 enum { voice_count = 8 }; | 49 enum { voice_count = 8 }; |
19 void mute_voices( int mask ); | 50 void mute_voices( int mask ); |
20 | 51 |
21 // Clear state and silence everything. | 52 // If true, prevents channels and global volumes from being phase-negated |
22 void reset(); | 53 void disable_surround( bool disable = true ); |
23 | 54 |
24 // Set gain, where 1.0 is normal. When greater than 1.0, output is clamped to | 55 enum { gain_unit = 0x100 }; |
25 // the 16-bit sample range. | 56 void set_gain( int gain ); |
26 void set_gain( double ); | 57 |
27 | 58 // State |
28 // If true, prevent channels and global volumes from being phase-negated | 59 |
29 void disable_surround( bool disable ); | 60 // Resets DSP and uses supplied values to initialize registers |
30 | |
31 // Read/write register 'n', where n ranges from 0 to register_count - 1. | |
32 enum { register_count = 128 }; | 61 enum { register_count = 128 }; |
33 int read ( int n ); | 62 void load( uint8_t const regs [register_count] ); |
34 void write( int n, int ); | 63 |
35 | 64 // DSP register addresses |
36 // Run DSP for 'count' samples. Write resulting samples to 'buf' if not NULL. | 65 |
37 void run( long count, short* buf = NULL ); | 66 // Global registers |
38 | 67 enum { |
39 | 68 r_mvoll = 0x0C, r_mvolr = 0x1C, |
40 // End of public interface | 69 r_evoll = 0x2C, r_evolr = 0x3C, |
70 r_kon = 0x4C, r_koff = 0x5C, | |
71 r_flg = 0x6C, r_endx = 0x7C, | |
72 r_efb = 0x0D, r_pmon = 0x2D, | |
73 r_non = 0x3D, r_eon = 0x4D, | |
74 r_dir = 0x5D, r_esa = 0x6D, | |
75 r_edl = 0x7D, | |
76 r_fir = 0x0F // 8 coefficients at 0x0F, 0x1F ... 0x7F | |
77 }; | |
78 | |
79 // Voice registers | |
80 enum { | |
81 v_voll = 0x00, v_volr = 0x01, | |
82 v_pitchl = 0x02, v_pitchh = 0x03, | |
83 v_srcn = 0x04, v_adsr0 = 0x05, | |
84 v_adsr1 = 0x06, v_gain = 0x07, | |
85 v_envx = 0x08, v_outx = 0x09 | |
86 }; | |
87 | |
88 public: | |
89 enum { extra_size = 16 }; | |
90 sample_t* extra() { return m.extra; } | |
91 sample_t const* out_pos() const { return m.out; } | |
92 public: | |
93 BLARGG_DISABLE_NOTHROW | |
94 | |
95 typedef BOOST::int8_t int8_t; | |
96 typedef BOOST::int16_t int16_t; | |
97 | |
98 enum { echo_hist_size = 8 }; | |
99 | |
100 enum env_mode_t { env_release, env_attack, env_decay, env_sustain }; | |
101 enum { brr_buf_size = 12 }; | |
102 struct voice_t | |
103 { | |
104 int buf [brr_buf_size*2];// decoded samples (twice the size to simplify wrap handling) | |
105 int* buf_pos; // place in buffer where next samples will be decoded | |
106 int interp_pos; // relative fractional position in sample (0x1000 = 1.0) | |
107 int brr_addr; // address of current BRR block | |
108 int brr_offset; // current decoding offset in BRR block | |
109 int kon_delay; // KON delay/current setup phase | |
110 env_mode_t env_mode; | |
111 int env; // current envelope level | |
112 int hidden_env; // used by GAIN mode 7, very obscure quirk | |
113 int volume [2]; // copy of volume from DSP registers, with surround disabled | |
114 int enabled; // -1 if enabled, 0 if muted | |
115 }; | |
41 private: | 116 private: |
42 | 117 struct state_t |
43 struct raw_voice_t { | 118 { |
44 int8_t left_vol; | 119 uint8_t regs [register_count]; |
45 int8_t right_vol; | 120 |
46 uint8_t rate [2]; | 121 // Echo history keeps most recent 8 samples (twice the size to simplify wrap handling) |
47 uint8_t waveform; | 122 int echo_hist [echo_hist_size * 2] [2]; |
48 uint8_t adsr [2]; // envelope rates for attack, decay, and sustain | 123 int (*echo_hist_pos) [2]; // &echo_hist [0 to 7] |
49 uint8_t gain; // envelope gain (if not using ADSR) | 124 |
50 int8_t envx; // current envelope level | 125 int every_other_sample; // toggles every sample |
51 int8_t outx; // current sample | 126 int kon; // KON value when last checked |
52 int8_t unused [6]; | 127 int noise; |
53 }; | 128 int echo_offset; // offset from ESA in echo buffer |
54 | 129 int echo_length; // number of bytes that echo_offset will stop at |
55 struct globals_t { | 130 int phase; // next clock cycle to run (0-31) |
56 int8_t unused1 [12]; | 131 unsigned counters [4]; |
57 int8_t left_volume; // 0C Main Volume Left (-.7) | 132 |
58 int8_t echo_feedback; // 0D Echo Feedback (-.7) | 133 int new_kon; |
59 int8_t unused2 [14]; | 134 int t_koff; |
60 int8_t right_volume; // 1C Main Volume Right (-.7) | 135 |
61 int8_t unused3 [15]; | 136 voice_t voices [voice_count]; |
62 int8_t left_echo_volume; // 2C Echo Volume Left (-.7) | 137 |
63 uint8_t pitch_mods; // 2D Pitch Modulation on/off for each voice | 138 unsigned* counter_select [32]; |
64 int8_t unused4 [14]; | 139 |
65 int8_t right_echo_volume; // 3C Echo Volume Right (-.7) | 140 // non-emulation state |
66 uint8_t noise_enables; // 3D Noise output on/off for each voice | 141 uint8_t* ram; // 64K shared RAM between DSP and SMP |
67 int8_t unused5 [14]; | 142 int mute_mask; |
68 uint8_t key_ons; // 4C Key On for each voice | 143 int gain; |
69 uint8_t echo_ons; // 4D Echo on/off for each voice | 144 int surround_threshold; |
70 int8_t unused6 [14]; | 145 sample_t* out; |
71 uint8_t key_offs; // 5C key off for each voice (instantiates release mode) | 146 sample_t* out_end; |
72 uint8_t wave_page; // 5D source directory (wave table offsets) | 147 sample_t* out_begin; |
73 int8_t unused7 [14]; | 148 sample_t extra [extra_size]; |
74 uint8_t flags; // 6C flags and noise freq | 149 }; |
75 uint8_t echo_page; // 6D | 150 state_t m; |
76 int8_t unused8 [14]; | 151 |
77 uint8_t wave_ended; // 7C | 152 void init_counter(); |
78 uint8_t echo_delay; // 7D ms >> 4 | 153 void run_counter( int ); |
79 char unused9 [2]; | 154 void soft_reset_common(); |
80 }; | 155 void write_outline( int addr, int data ); |
81 | 156 void update_voice_vol( int addr ); |
82 union { | |
83 raw_voice_t voice [voice_count]; | |
84 uint8_t reg [register_count]; | |
85 globals_t g; | |
86 }; | |
87 | |
88 uint8_t* const ram; | |
89 | |
90 // Cache of echo FIR values for faster access | |
91 short fir_coeff [voice_count]; | |
92 | |
93 // fir_buf [i + 8] == fir_buf [i], to avoid wrap checking in FIR code | |
94 short fir_buf [16] [2]; | |
95 int fir_offset; // (0 to 7) | |
96 | |
97 enum { emu_gain_bits = 8 }; | |
98 int emu_gain; | |
99 | |
100 int keyed_on; // 8-bits for 8 voices | |
101 int keys; | |
102 | |
103 int echo_ptr; | |
104 int noise_amp; | |
105 int noise; | |
106 int noise_count; | |
107 | |
108 int surround_threshold; | |
109 | |
110 static BOOST::int16_t const gauss []; | |
111 | |
112 enum state_t { | |
113 state_attack, | |
114 state_decay, | |
115 state_sustain, | |
116 state_release | |
117 }; | |
118 | |
119 struct voice_t { | |
120 short volume [2]; | |
121 short fraction;// 12-bit fractional position | |
122 short interp3; // most recent four decoded samples | |
123 short interp2; | |
124 short interp1; | |
125 short interp0; | |
126 short block_remain; // number of nybbles remaining in current block | |
127 unsigned short addr; | |
128 short block_header; // header byte from current block | |
129 short envcnt; | |
130 short envx; | |
131 short on_cnt; | |
132 short enabled; // 7 if enabled, 31 if disabled | |
133 short envstate; | |
134 short unused; // pad to power of 2 | |
135 }; | |
136 | |
137 voice_t voice_state [voice_count]; | |
138 | |
139 int clock_envelope( int ); | |
140 }; | 157 }; |
141 | 158 |
142 inline void Spc_Dsp::disable_surround( bool disable ) { surround_threshold = disable ? 0 : -0x7FFF; } | 159 #include <assert.h> |
143 | 160 |
144 inline void Spc_Dsp::set_gain( double v ) { emu_gain = (int) (v * (1 << emu_gain_bits)); } | 161 inline int Spc_Dsp::sample_count() const { return m.out - m.out_begin; } |
145 | 162 |
146 inline int Spc_Dsp::read( int i ) | 163 inline int Spc_Dsp::read( int addr ) const |
147 { | 164 { |
148 assert( (unsigned) i < register_count ); | 165 assert( (unsigned) addr < register_count ); |
149 return reg [i]; | 166 return m.regs [addr]; |
150 } | 167 } |
168 | |
169 inline void Spc_Dsp::update_voice_vol( int addr ) | |
170 { | |
171 int l = (int8_t) m.regs [addr + v_voll]; | |
172 int r = (int8_t) m.regs [addr + v_volr]; | |
173 | |
174 if ( l * r < m.surround_threshold ) | |
175 { | |
176 // signs differ, so negate those that are negative | |
177 l ^= l >> 7; | |
178 r ^= r >> 7; | |
179 } | |
180 | |
181 voice_t& v = m.voices [addr >> 4]; | |
182 int enabled = v.enabled; | |
183 v.volume [0] = l & enabled; | |
184 v.volume [1] = r & enabled; | |
185 } | |
186 | |
187 inline void Spc_Dsp::write( int addr, int data ) | |
188 { | |
189 assert( (unsigned) addr < register_count ); | |
190 | |
191 m.regs [addr] = (uint8_t) data; | |
192 int low = addr & 0x0F; | |
193 if ( low < 0x2 ) // voice volumes | |
194 { | |
195 update_voice_vol( low ^ addr ); | |
196 } | |
197 else if ( low == 0xC ) | |
198 { | |
199 if ( addr == r_kon ) | |
200 m.new_kon = (uint8_t) data; | |
201 | |
202 if ( addr == r_endx ) // always cleared, regardless of data written | |
203 m.regs [r_endx] = 0; | |
204 } | |
205 } | |
206 | |
207 inline void Spc_Dsp::set_gain( int gain ) { m.gain = gain; } | |
208 | |
209 inline void Spc_Dsp::disable_surround( bool disable ) | |
210 { | |
211 m.surround_threshold = disable ? 0 : -0x4000; | |
212 } | |
213 | |
214 #define SPC_NO_COPY_STATE_FUNCS 1 | |
215 | |
216 #define SPC_LESS_ACCURATE 1 | |
151 | 217 |
152 #endif | 218 #endif |