1 // Sms_Snd_Emu 0.1.4. http://www.slack.net/~ant/
2
3 #include "Sms_Apu.h"
4
5 /* Copyright (C) 2003-2006 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 // Sms_Osc
19
Sms_Osc()20 Sms_Osc::Sms_Osc()
21 {
22 output = 0;
23 outputs [0] = 0; // always stays NULL
24 outputs [1] = 0;
25 outputs [2] = 0;
26 outputs [3] = 0;
27 }
28
reset()29 void Sms_Osc::reset()
30 {
31 delay = 0;
32 last_amp = 0;
33 volume = 0;
34 output_select = 3;
35 output = outputs [3];
36 }
37
38 // Sms_Square
39
reset()40 inline void Sms_Square::reset()
41 {
42 period = 0;
43 phase = 0;
44 Sms_Osc::reset();
45 }
46
run(blip_time_t time,blip_time_t end_time)47 void Sms_Square::run( blip_time_t time, blip_time_t end_time )
48 {
49 if ( !volume || period <= 128 )
50 {
51 // ignore 16kHz and higher
52 if ( last_amp )
53 {
54 synth->offset( time, -last_amp, output );
55 last_amp = 0;
56 }
57 time += delay;
58 if ( !period )
59 {
60 time = end_time;
61 }
62 else if ( time < end_time )
63 {
64 // keep calculating phase
65 int count = (end_time - time + period - 1) / period;
66 phase = (phase + count) & 1;
67 time += count * period;
68 }
69 }
70 else
71 {
72 int amp = phase ? volume : -volume;
73 {
74 int delta = amp - last_amp;
75 if ( delta )
76 {
77 last_amp = amp;
78 synth->offset( time, delta, output );
79 }
80 }
81
82 time += delay;
83 if ( time < end_time )
84 {
85 Blip_Buffer* const output = this->output;
86 int delta = amp * 2;
87 do
88 {
89 delta = -delta;
90 synth->offset_inline( time, delta, output );
91 time += period;
92 phase ^= 1;
93 }
94 while ( time < end_time );
95 this->last_amp = phase ? volume : -volume;
96 }
97 }
98 delay = time - end_time;
99 }
100
101 // Sms_Noise
102
103 static int const noise_periods [3] = { 0x100, 0x200, 0x400 };
104
reset()105 inline void Sms_Noise::reset()
106 {
107 period = &noise_periods [0];
108 shifter = 0x8000;
109 feedback = 0x9000;
110 Sms_Osc::reset();
111 }
112
run(blip_time_t time,blip_time_t end_time)113 void Sms_Noise::run( blip_time_t time, blip_time_t end_time )
114 {
115 int amp = volume;
116 if ( shifter & 1 )
117 amp = -amp;
118
119 {
120 int delta = amp - last_amp;
121 if ( delta )
122 {
123 last_amp = amp;
124 synth.offset( time, delta, output );
125 }
126 }
127
128 time += delay;
129 if ( !volume )
130 time = end_time;
131
132 if ( time < end_time )
133 {
134 Blip_Buffer* const output = this->output;
135 unsigned shifter = this->shifter;
136 int delta = amp * 2;
137 int period = *this->period * 2;
138 if ( !period )
139 period = 16;
140
141 do
142 {
143 int changed = shifter + 1;
144 shifter = (feedback & -(shifter & 1)) ^ (shifter >> 1);
145 if ( changed & 2 ) // true if bits 0 and 1 differ
146 {
147 delta = -delta;
148 synth.offset_inline( time, delta, output );
149 }
150 time += period;
151 }
152 while ( time < end_time );
153
154 this->shifter = shifter;
155 this->last_amp = delta >> 1;
156 }
157 delay = time - end_time;
158 }
159
160 // Sms_Apu
161
Sms_Apu()162 Sms_Apu::Sms_Apu()
163 {
164 for ( int i = 0; i < 3; i++ )
165 {
166 squares [i].synth = &square_synth;
167 oscs [i] = &squares [i];
168 }
169 oscs [3] = &noise;
170
171 volume( 1.0 );
172 reset();
173 }
174
~Sms_Apu()175 Sms_Apu::~Sms_Apu()
176 {
177 }
178
volume(double vol)179 void Sms_Apu::volume( double vol )
180 {
181 vol *= 0.85 / (osc_count * 64 * 2);
182 square_synth.volume( vol );
183 noise.synth.volume( vol );
184 }
185
treble_eq(const blip_eq_t & eq)186 void Sms_Apu::treble_eq( const blip_eq_t& eq )
187 {
188 square_synth.treble_eq( eq );
189 noise.synth.treble_eq( eq );
190 }
191
osc_output(int index,Blip_Buffer * center,Blip_Buffer * left,Blip_Buffer * right)192 void Sms_Apu::osc_output( int index, Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right )
193 {
194 require( (unsigned) index < osc_count );
195 require( (center && left && right) || (!center && !left && !right) );
196 Sms_Osc& osc = *oscs [index];
197 osc.outputs [1] = right;
198 osc.outputs [2] = left;
199 osc.outputs [3] = center;
200 osc.output = osc.outputs [osc.output_select];
201 }
202
output(Blip_Buffer * center,Blip_Buffer * left,Blip_Buffer * right)203 void Sms_Apu::output( Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right )
204 {
205 for ( int i = 0; i < osc_count; i++ )
206 osc_output( i, center, left, right );
207 }
208
reset(unsigned feedback,int noise_width)209 void Sms_Apu::reset( unsigned feedback, int noise_width )
210 {
211 last_time = 0;
212 latch = 0;
213
214 if ( !feedback || !noise_width )
215 {
216 feedback = 0x0009;
217 noise_width = 16;
218 }
219 // convert to "Galios configuration"
220 looped_feedback = 1 << (noise_width - 1);
221 noise_feedback = 0;
222 while ( noise_width-- )
223 {
224 noise_feedback = (noise_feedback << 1) | (feedback & 1);
225 feedback >>= 1;
226 }
227
228 squares [0].reset();
229 squares [1].reset();
230 squares [2].reset();
231 noise.reset();
232 }
233
run_until(blip_time_t end_time)234 void Sms_Apu::run_until( blip_time_t end_time )
235 {
236 require( end_time >= last_time ); // end_time must not be before previous time
237
238 if ( end_time > last_time )
239 {
240 // run oscillators
241 for ( int i = 0; i < osc_count; ++i )
242 {
243 Sms_Osc& osc = *oscs [i];
244 if ( osc.output )
245 {
246 osc.output->set_modified();
247 if ( i < 3 )
248 squares [i].run( last_time, end_time );
249 else
250 noise.run( last_time, end_time );
251 }
252 }
253
254 last_time = end_time;
255 }
256 }
257
end_frame(blip_time_t end_time)258 void Sms_Apu::end_frame( blip_time_t end_time )
259 {
260 if ( end_time > last_time )
261 run_until( end_time );
262
263 assert( last_time >= end_time );
264 last_time -= end_time;
265 }
266
write_ggstereo(blip_time_t time,int data)267 void Sms_Apu::write_ggstereo( blip_time_t time, int data )
268 {
269 require( (unsigned) data <= 0xFF );
270
271 run_until( time );
272
273 for ( int i = 0; i < osc_count; i++ )
274 {
275 Sms_Osc& osc = *oscs [i];
276 int flags = data >> i;
277 Blip_Buffer* old_output = osc.output;
278 osc.output_select = (flags >> 3 & 2) | (flags & 1);
279 osc.output = osc.outputs [osc.output_select];
280 if ( osc.output != old_output && osc.last_amp )
281 {
282 if ( old_output )
283 {
284 old_output->set_modified();
285 square_synth.offset( time, -osc.last_amp, old_output );
286 }
287 osc.last_amp = 0;
288 }
289 }
290 }
291
292 // volumes [i] = 64 * pow( 1.26, 15 - i ) / pow( 1.26, 15 )
293 static unsigned char const volumes [16] = {
294 64, 50, 39, 31, 24, 19, 15, 12, 9, 7, 5, 4, 3, 2, 1, 0
295 };
296
write_data(blip_time_t time,int data)297 void Sms_Apu::write_data( blip_time_t time, int data )
298 {
299 require( (unsigned) data <= 0xFF );
300
301 run_until( time );
302
303 if ( data & 0x80 )
304 latch = data;
305
306 int index = (latch >> 5) & 3;
307 if ( latch & 0x10 )
308 {
309 oscs [index]->volume = volumes [data & 15];
310 }
311 else if ( index < 3 )
312 {
313 Sms_Square& sq = squares [index];
314 if ( data & 0x80 )
315 sq.period = (sq.period & 0xFF00) | (data << 4 & 0x00FF);
316 else
317 sq.period = (sq.period & 0x00FF) | (data << 8 & 0x3F00);
318 }
319 else
320 {
321 int select = data & 3;
322 if ( select < 3 )
323 noise.period = &noise_periods [select];
324 else
325 noise.period = &squares [2].period;
326
327 noise.feedback = (data & 0x04) ? noise_feedback : looped_feedback;
328 noise.shifter = 0x8000;
329 }
330 }
331