Submit initial patch from FS#12176. Adds support for several new game music formats...
[kugel-rb.git] / apps / codecs / libgme / sms_apu.c
blob4be63db0736b04b863c68bb544059aa1eb889fa0
1 // Sms_Snd_Emu 0.1.1. http://www.slack.net/~ant/
3 #include "sms_apu.h"
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 */
16 #include "blargg_source.h"
18 int const noise_osc = 3;
20 void Sms_apu_volume( struct Sms_Apu* this, double vol )
22 vol *= 0.85 / sms_osc_count / 64;
23 Synth_volume( &this->synth, vol );
26 inline int calc_output( struct Sms_Apu* this, int i )
28 int flags = this->ggstereo >> i;
29 return (flags >> 3 & 2) | (flags & 1);
32 void Sms_apu_set_output( struct Sms_Apu* this, int i, struct Blip_Buffer* center, struct Blip_Buffer* left, struct Blip_Buffer* right )
34 #if defined(ROCKBOX)
35 (void) left;
36 (void) right;
37 #endif
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
43 if ( center )
45 unsigned const divisor = 16384 * 16 * 2;
46 this->min_tone_period = ((unsigned) Blip_clock_rate( center ) + divisor/2) / divisor;
49 if ( !center || !left || !right )
51 left = center;
52 right = center;
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 )];
63 static inline unsigned fibonacci_to_galois_lfsr( unsigned fibonacci, int width )
65 unsigned galois = 0;
66 while ( --width >= 0 )
68 galois = (galois << 1) | (fibonacci & 1);
69 fibonacci >>= 1;
71 return galois;
74 void Sms_apu_reset( struct Sms_Apu* this, unsigned feedback, int noise_width )
76 this->last_time = 0;
77 this->latch = 0;
78 this->ggstereo = 0;
80 // Calculate noise feedback values
81 if ( !feedback || !noise_width )
83 feedback = 0x0009;
84 noise_width = 16;
86 this->looped_feedback = 1 << (noise_width - 1);
87 this->noise_feedback = fibonacci_to_galois_lfsr( feedback, noise_width );
89 // Reset oscs
90 int i;
91 for ( i = sms_osc_count; --i >= 0; )
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
102 this->oscs [noise_osc].phase = 0x8000;
103 Sms_apu_write_ggstereo( this, 0, 0xFF );
106 void Sms_apu_init( struct Sms_Apu* this )
108 this->min_tone_period = 7;
110 Synth_init( &this->synth );
112 // Clear outputs to NULL FIRST
113 this->ggstereo = 0;
115 int i;
116 for ( i = sms_osc_count; --i >= 0; )
117 Sms_apu_set_output( this, i, NULL, NULL, NULL );
119 Sms_apu_volume( this, 1.0 );
120 Sms_apu_reset( this, 0, 0 );
123 static void run_until( struct Sms_Apu* this, blip_time_t end_time )
125 require( end_time >= this->last_time );
126 if ( end_time <= this->last_time )
127 return;
129 // Synthesize each oscillator
130 int idx;
131 for ( idx = sms_osc_count; --idx >= 0; )
133 struct Osc* osc = &this->oscs [idx];
134 int vol = 0;
135 int amp = 0;
137 // Determine what will be generated
138 struct Blip_Buffer* const out = osc->output;
139 if ( out )
141 // volumes [i] ~= 64 * pow( 1.26, 15 - i ) / pow( 1.26, 15 )
142 static unsigned char const volumes [16] ICONST_ATTR = {
143 64, 50, 40, 32, 25, 20, 16, 13, 10, 8, 6, 5, 4, 3, 2, 0
146 vol = volumes [osc->volume];
147 amp = (osc->phase & 1) * vol;
149 // Square freq above 16 kHz yields constant amplitude at half volume
150 if ( idx != noise_osc && osc->period < this->min_tone_period )
152 amp = vol >> 1;
153 vol = 0;
156 // Update amplitude
157 int delta = amp - osc->last_amp;
158 if ( delta )
160 osc->last_amp = amp;
161 /* norm_synth.offset( last_time, delta, out ); */
162 Synth_offset( &this->synth, this->last_time, delta, out );
163 /* out->set_modified(); */
164 Blip_set_modified( out );
168 // Generate wave
169 blip_time_t time = this->last_time + osc->delay;
170 if ( time < end_time )
172 // Calculate actual period
173 int period = osc->period;
174 if ( idx == noise_osc )
176 period = 0x20 << (period & 3);
177 if ( period == 0x100 )
178 period = this->oscs [2].period * 2;
180 period *= 0x10;
181 if ( !period )
182 period = 0x10;
184 // Maintain phase when silent
185 int phase = osc->phase;
186 if ( !vol )
188 int count = (end_time - time + period - 1) / period;
189 time += count * period;
190 if ( idx != noise_osc ) // TODO: maintain noise LFSR phase?
191 phase ^= count & 1;
193 else
195 int delta = amp * 2 - vol;
197 if ( idx != noise_osc )
199 // Square
202 delta = -delta;
203 /* norm_synth.offset( time, delta, out ); */
204 Synth_offset( &this->synth, time, delta, out );
205 time += period;
207 while ( time < end_time );
208 phase = (delta >= 0);
210 else
212 // Noise
213 unsigned const feedback = (osc->period & 4 ? this->noise_feedback : this->looped_feedback);
216 unsigned changed = phase + 1;
217 phase = ((phase & 1) * feedback) ^ (phase >> 1);
218 if ( changed & 2 ) // true if bits 0 and 1 differ
220 delta = -delta;
221 /* fast_synth.offset_inline( time, delta, out ); */
222 Synth_offset_inline( &this->synth, time, delta, out );
224 time += period;
226 while ( time < end_time );
227 check( phase );
229 osc->last_amp = (phase & 1) * vol;
230 Blip_set_modified( out );
232 osc->phase = phase;
234 osc->delay = time - end_time;
236 this->last_time = end_time;
239 void Sms_apu_write_ggstereo( struct Sms_Apu* this, blip_time_t time, int data )
241 require( (unsigned) data <= 0xFF );
243 run_until( this, time );
244 this->ggstereo = data;
246 int i;
247 for ( i = sms_osc_count; --i >= 0; )
249 struct Osc* osc = &this->oscs [i];
251 struct Blip_Buffer* old = osc->output;
252 osc->output = osc->outputs [calc_output( this, i )];
253 if ( osc->output != old )
255 int delta = -osc->last_amp;
256 if ( delta )
258 osc->last_amp = 0;
259 if ( old )
261 Blip_set_modified( old );
262 Synth_offset( &this->synth, this->last_time, delta, old );
269 void Sms_apu_write_data( struct Sms_Apu* this, blip_time_t time, int data )
271 require( (unsigned) data <= 0xFF );
273 run_until( this, time );
275 if ( data & 0x80 )
276 this->latch = data;
278 // We want the raw values written so our save state format can be
279 // as close to hardware as possible and unspecific to any emulator.
280 int idx = this->latch >> 5 & 3;
281 struct Osc* osc = &this->oscs [idx];
282 if ( this->latch & 0x10 )
284 osc->volume = data & 0x0F;
286 else
288 if ( idx == noise_osc )
289 osc->phase = 0x8000; // reset noise LFSR
291 // Replace high 6 bits/low 4 bits of register with data
292 int lo = osc->period;
293 int hi = data << 4;
294 if ( idx == noise_osc || (data & 0x80) )
296 hi = lo;
297 lo = data;
299 osc->period = (hi & 0x3F0) | (lo & 0x00F);
303 void Sms_apu_end_frame( struct Sms_Apu* this, blip_time_t end_time )
305 if ( end_time > this->last_time )
306 run_until( this, end_time );
308 this->last_time -= end_time;
309 assert( this->last_time >= 0 );