diff options
Diffstat (limited to 'lib/rbcodec/codecs/libgme/sms_apu.c')
-rw-r--r-- | lib/rbcodec/codecs/libgme/sms_apu.c | 306 |
1 files changed, 306 insertions, 0 deletions
diff --git a/lib/rbcodec/codecs/libgme/sms_apu.c b/lib/rbcodec/codecs/libgme/sms_apu.c new file mode 100644 index 0000000000..379fcf1cbf --- /dev/null +++ b/lib/rbcodec/codecs/libgme/sms_apu.c | |||
@@ -0,0 +1,306 @@ | |||
1 | // Sms_Snd_Emu 0.1.1. http://www.slack.net/~ant/ | ||
2 | |||
3 | #include "sms_apu.h" | ||
4 | |||
5 | /* Copyright (C) 2003-2008 Shay Green. This module is free software; you | ||
6 | can redistribute it and/or modify it under the terms of the GNU Lesser | ||
7 | General Public License as published by the Free Software Foundation; either | ||
8 | version 2.1 of the License, or (at your option) any later version. This | ||
9 | module is distributed in the hope that it will be useful, but WITHOUT ANY | ||
10 | WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS | ||
11 | FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more | ||
12 | details. You should have received a copy of the GNU Lesser General Public | ||
13 | License along with this module; if not, write to the Free Software Foundation, | ||
14 | Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ | ||
15 | |||
16 | #include "blargg_source.h" | ||
17 | |||
18 | int const noise_osc = 3; | ||
19 | |||
20 | void Sms_apu_volume( struct Sms_Apu* this, int vol ) | ||
21 | { | ||
22 | vol = (vol - (vol*3)/20) / sms_osc_count / 64; | ||
23 | Synth_volume( &this->synth, vol ); | ||
24 | } | ||
25 | |||
26 | static inline int calc_output( struct Sms_Apu* this, int i ) | ||
27 | { | ||
28 | int flags = this->ggstereo >> i; | ||
29 | return (flags >> 3 & 2) | (flags & 1); | ||
30 | } | ||
31 | |||
32 | void Sms_apu_set_output( struct Sms_Apu* this, int i, struct Blip_Buffer* center, struct Blip_Buffer* left, struct Blip_Buffer* right ) | ||
33 | { | ||
34 | #if defined(ROCKBOX) | ||
35 | (void) left; | ||
36 | (void) right; | ||
37 | #endif | ||
38 | |||
39 | // Must be silent (all NULL), mono (left and right NULL), or stereo (none NULL) | ||
40 | require( !center || (center && !left && !right) || (center && left && right) ); | ||
41 | require( (unsigned) i < sms_osc_count ); // fails if you pass invalid osc index | ||
42 | |||
43 | if ( center ) | ||
44 | { | ||
45 | unsigned const divisor = 16384 * 16 * 2; | ||
46 | this->min_tone_period = ((unsigned) Blip_clock_rate( center ) + divisor/2) / divisor; | ||
47 | } | ||
48 | |||
49 | if ( !center || !left || !right ) | ||
50 | { | ||
51 | left = center; | ||
52 | right = center; | ||
53 | } | ||
54 | |||
55 | struct Osc* o = &this->oscs [i]; | ||
56 | o->outputs [0] = NULL; | ||
57 | o->outputs [1] = right; | ||
58 | o->outputs [2] = left; | ||
59 | o->outputs [3] = center; | ||
60 | o->output = o->outputs [calc_output( this, i )]; | ||
61 | } | ||
62 | |||
63 | static inline unsigned fibonacci_to_galois_lfsr( unsigned fibonacci, int width ) | ||
64 | { | ||
65 | unsigned galois = 0; | ||
66 | while ( --width >= 0 ) | ||
67 | { | ||
68 | galois = (galois << 1) | (fibonacci & 1); | ||
69 | fibonacci >>= 1; | ||
70 | } | ||
71 | return galois; | ||
72 | } | ||
73 | |||
74 | void Sms_apu_reset( struct Sms_Apu* this, unsigned feedback, int noise_width ) | ||
75 | { | ||
76 | this->last_time = 0; | ||
77 | this->latch = 0; | ||
78 | this->ggstereo = 0; | ||
79 | |||
80 | // Calculate noise feedback values | ||
81 | if ( !feedback || !noise_width ) | ||
82 | { | ||
83 | feedback = 0x0009; | ||
84 | noise_width = 16; | ||
85 | } | ||
86 | this->looped_feedback = 1 << (noise_width - 1); | ||
87 | this->noise_feedback = fibonacci_to_galois_lfsr( feedback, noise_width ); | ||
88 | |||
89 | // Reset oscs | ||
90 | int i; | ||
91 | for ( i = sms_osc_count; --i >= 0; ) | ||
92 | { | ||
93 | struct Osc* o = &this->oscs [i]; | ||
94 | o->output = NULL; | ||
95 | o->last_amp = 0; | ||
96 | o->delay = 0; | ||
97 | o->phase = 0; | ||
98 | o->period = 0; | ||
99 | o->volume = 15; // silent | ||
100 | } | ||
101 | |||
102 | this->oscs [noise_osc].phase = 0x8000; | ||
103 | Sms_apu_write_ggstereo( this, 0, 0xFF ); | ||
104 | } | ||
105 | |||
106 | void Sms_apu_init( struct Sms_Apu* this ) | ||
107 | { | ||
108 | this->min_tone_period = 7; | ||
109 | |||
110 | Synth_init( &this->synth ); | ||
111 | |||
112 | // Clear outputs to NULL FIRST | ||
113 | this->ggstereo = 0; | ||
114 | |||
115 | int i; | ||
116 | for ( i = sms_osc_count; --i >= 0; ) | ||
117 | Sms_apu_set_output( this, i, NULL, NULL, NULL ); | ||
118 | |||
119 | Sms_apu_volume( this, (int)FP_ONE_VOLUME ); | ||
120 | Sms_apu_reset( this, 0, 0 ); | ||
121 | } | ||
122 | |||
123 | static void run_until( struct Sms_Apu* this, blip_time_t end_time ) | ||
124 | { | ||
125 | require( end_time >= this->last_time ); | ||
126 | if ( end_time <= this->last_time ) | ||
127 | return; | ||
128 | |||
129 | // Synthesize each oscillator | ||
130 | int idx; | ||
131 | for ( idx = sms_osc_count; --idx >= 0; ) | ||
132 | { | ||
133 | struct Osc* osc = &this->oscs [idx]; | ||
134 | int vol = 0; | ||
135 | int amp = 0; | ||
136 | |||
137 | // Determine what will be generated | ||
138 | struct Blip_Buffer* const out = osc->output; | ||
139 | if ( out ) | ||
140 | { | ||
141 | // volumes [i] ~= 64 * pow( 1.26, 15 - i ) / pow( 1.26, 15 ) | ||
142 | static unsigned char const volumes [16] = { | ||
143 | 64, 50, 40, 32, 25, 20, 16, 13, 10, 8, 6, 5, 4, 3, 2, 0 | ||
144 | }; | ||
145 | |||
146 | vol = volumes [osc->volume]; | ||
147 | amp = (osc->phase & 1) * vol; | ||
148 | |||
149 | // Square freq above 16 kHz yields constant amplitude at half volume | ||
150 | if ( idx != noise_osc && osc->period < this->min_tone_period ) | ||
151 | { | ||
152 | amp = vol >> 1; | ||
153 | vol = 0; | ||
154 | } | ||
155 | |||
156 | // Update amplitude | ||
157 | int delta = amp - osc->last_amp; | ||
158 | if ( delta ) | ||
159 | { | ||
160 | osc->last_amp = amp; | ||
161 | Synth_offset( &this->synth, this->last_time, delta, out ); | ||
162 | Blip_set_modified( out ); | ||
163 | } | ||
164 | } | ||
165 | |||
166 | // Generate wave | ||
167 | blip_time_t time = this->last_time + osc->delay; | ||
168 | if ( time < end_time ) | ||
169 | { | ||
170 | // Calculate actual period | ||
171 | int period = osc->period; | ||
172 | if ( idx == noise_osc ) | ||
173 | { | ||
174 | period = 0x20 << (period & 3); | ||
175 | if ( period == 0x100 ) | ||
176 | period = this->oscs [2].period * 2; | ||
177 | } | ||
178 | period *= 0x10; | ||
179 | if ( !period ) | ||
180 | period = 0x10; | ||
181 | |||
182 | // Maintain phase when silent | ||
183 | int phase = osc->phase; | ||
184 | if ( !vol ) | ||
185 | { | ||
186 | int count = (end_time - time + period - 1) / period; | ||
187 | time += count * period; | ||
188 | if ( idx != noise_osc ) // TODO: maintain noise LFSR phase? | ||
189 | phase ^= count & 1; | ||
190 | } | ||
191 | else | ||
192 | { | ||
193 | int delta = amp * 2 - vol; | ||
194 | |||
195 | if ( idx != noise_osc ) | ||
196 | { | ||
197 | // Square | ||
198 | do | ||
199 | { | ||
200 | delta = -delta; | ||
201 | Synth_offset( &this->synth, time, delta, out ); | ||
202 | time += period; | ||
203 | } | ||
204 | while ( time < end_time ); | ||
205 | phase = (delta >= 0); | ||
206 | } | ||
207 | else | ||
208 | { | ||
209 | // Noise | ||
210 | unsigned const feedback = (osc->period & 4 ? this->noise_feedback : this->looped_feedback); | ||
211 | do | ||
212 | { | ||
213 | unsigned changed = phase + 1; | ||
214 | phase = ((phase & 1) * feedback) ^ (phase >> 1); | ||
215 | if ( changed & 2 ) // true if bits 0 and 1 differ | ||
216 | { | ||
217 | delta = -delta; | ||
218 | Synth_offset_inline( &this->synth, time, delta, out ); | ||
219 | } | ||
220 | time += period; | ||
221 | } | ||
222 | while ( time < end_time ); | ||
223 | check( phase ); | ||
224 | } | ||
225 | osc->last_amp = (phase & 1) * vol; | ||
226 | Blip_set_modified( out ); | ||
227 | } | ||
228 | osc->phase = phase; | ||
229 | } | ||
230 | osc->delay = time - end_time; | ||
231 | } | ||
232 | this->last_time = end_time; | ||
233 | } | ||
234 | |||
235 | void Sms_apu_write_ggstereo( struct Sms_Apu* this, blip_time_t time, int data ) | ||
236 | { | ||
237 | require( (unsigned) data <= 0xFF ); | ||
238 | |||
239 | run_until( this, time ); | ||
240 | this->ggstereo = data; | ||
241 | |||
242 | int i; | ||
243 | for ( i = sms_osc_count; --i >= 0; ) | ||
244 | { | ||
245 | struct Osc* osc = &this->oscs [i]; | ||
246 | |||
247 | struct Blip_Buffer* old = osc->output; | ||
248 | osc->output = osc->outputs [calc_output( this, i )]; | ||
249 | if ( osc->output != old ) | ||
250 | { | ||
251 | int delta = -osc->last_amp; | ||
252 | if ( delta ) | ||
253 | { | ||
254 | osc->last_amp = 0; | ||
255 | if ( old ) | ||
256 | { | ||
257 | Blip_set_modified( old ); | ||
258 | Synth_offset( &this->synth, this->last_time, delta, old ); | ||
259 | } | ||
260 | } | ||
261 | } | ||
262 | } | ||
263 | } | ||
264 | |||
265 | void Sms_apu_write_data( struct Sms_Apu* this, blip_time_t time, int data ) | ||
266 | { | ||
267 | require( (unsigned) data <= 0xFF ); | ||
268 | |||
269 | run_until( this, time ); | ||
270 | |||
271 | if ( data & 0x80 ) | ||
272 | this->latch = data; | ||
273 | |||
274 | // We want the raw values written so our save state format can be | ||
275 | // as close to hardware as possible and unspecific to any emulator. | ||
276 | int idx = this->latch >> 5 & 3; | ||
277 | struct Osc* osc = &this->oscs [idx]; | ||
278 | if ( this->latch & 0x10 ) | ||
279 | { | ||
280 | osc->volume = data & 0x0F; | ||
281 | } | ||
282 | else | ||
283 | { | ||
284 | if ( idx == noise_osc ) | ||
285 | osc->phase = 0x8000; // reset noise LFSR | ||
286 | |||
287 | // Replace high 6 bits/low 4 bits of register with data | ||
288 | int lo = osc->period; | ||
289 | int hi = data << 4; | ||
290 | if ( idx == noise_osc || (data & 0x80) ) | ||
291 | { | ||
292 | hi = lo; | ||
293 | lo = data; | ||
294 | } | ||
295 | osc->period = (hi & 0x3F0) | (lo & 0x00F); | ||
296 | } | ||
297 | } | ||
298 | |||
299 | void Sms_apu_end_frame( struct Sms_Apu* this, blip_time_t end_time ) | ||
300 | { | ||
301 | if ( end_time > this->last_time ) | ||
302 | run_until( this, end_time ); | ||
303 | |||
304 | this->last_time -= end_time; | ||
305 | assert( this->last_time >= 0 ); | ||
306 | } | ||