summaryrefslogtreecommitdiff
path: root/lib/rbcodec/codecs/libgme/sms_apu.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/rbcodec/codecs/libgme/sms_apu.c')
-rw-r--r--lib/rbcodec/codecs/libgme/sms_apu.c306
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
6can redistribute it and/or modify it under the terms of the GNU Lesser
7General Public License as published by the Free Software Foundation; either
8version 2.1 of the License, or (at your option) any later version. This
9module is distributed in the hope that it will be useful, but WITHOUT ANY
10WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
12details. You should have received a copy of the GNU Lesser General Public
13License along with this module; if not, write to the Free Software Foundation,
14Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
15
16#include "blargg_source.h"
17
18int const noise_osc = 3;
19
20void 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
26static 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
32void 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
63static 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
74void 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
106void 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
123static 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
235void 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
265void 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
299void 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}