comparison Plugins/Input/console/Sms_Apu.cpp @ 493:c04dff121e1d trunk

[svn] hostile merge, phase 2: reimport based on new plugin code
author nenolod
date Tue, 24 Jan 2006 20:19:01 -0800
parents
children f12d7e208b43
comparison
equal deleted inserted replaced
492:ccb68bad47b2 493:c04dff121e1d
1
2 // Sms_Snd_Emu 0.1.3. http://www.slack.net/~ant/
3
4 #include "Sms_Apu.h"
5
6 /* Copyright (C) 2003-2006 Shay Green. This module is free software; you
7 can redistribute it and/or modify it under the terms of the GNU Lesser
8 General Public License as published by the Free Software Foundation; either
9 version 2.1 of the License, or (at your option) any later version. This
10 module is distributed in the hope that it will be useful, but WITHOUT ANY
11 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12 FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
13 more details. You should have received a copy of the GNU Lesser General
14 Public License along with this module; if not, write to the Free Software
15 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
16
17 #include BLARGG_SOURCE_BEGIN
18
19 // Sms_Osc
20
21 Sms_Osc::Sms_Osc()
22 {
23 output = NULL;
24 outputs [0] = NULL; // always stays NULL
25 outputs [1] = NULL;
26 outputs [2] = NULL;
27 outputs [3] = NULL;
28 }
29
30 void Sms_Osc::reset()
31 {
32 delay = 0;
33 last_amp = 0;
34 volume = 0;
35 output_select = 3;
36 output = outputs [3];
37 }
38
39 // Sms_Square
40
41 inline void Sms_Square::reset()
42 {
43 period = 0;
44 phase = 0;
45 Sms_Osc::reset();
46 }
47
48 void Sms_Square::run( sms_time_t time, sms_time_t end_time )
49 {
50 if ( !volume || period <= 128 )
51 {
52 // ignore 16kHz and higher
53 if ( last_amp )
54 {
55 synth->offset( time, -last_amp, output );
56 last_amp = 0;
57 }
58 time += delay;
59 if ( !period )
60 {
61 time = end_time;
62 }
63 else if ( time < end_time )
64 {
65 // keep calculating phase
66 int count = (end_time - time + period - 1) / period;
67 phase = (phase + count) & 1;
68 time += count * period;
69 }
70 }
71 else
72 {
73 int amp = phase ? volume : -volume;
74 int delta = amp - last_amp;
75 if ( delta )
76 {
77 last_amp = amp;
78 synth->offset( time, delta, output );
79 }
80
81 time += delay;
82 if ( time < end_time )
83 {
84 Blip_Buffer* const output = this->output;
85 int delta = amp * 2;
86 do
87 {
88 delta = -delta;
89 synth->offset_inline( time, delta, output );
90 time += period;
91 phase ^= 1;
92 }
93 while ( time < end_time );
94 this->last_amp = phase ? volume : -volume;
95 }
96 }
97 delay = time - end_time;
98 }
99
100 // Sms_Noise
101
102 static const int noise_periods [3] = { 0x100, 0x200, 0x400 };
103
104 inline void Sms_Noise::reset()
105 {
106 period = &noise_periods [0];
107 shifter = 0x8000;
108 tap = 12;
109 Sms_Osc::reset();
110 }
111
112 void Sms_Noise::run( sms_time_t time, sms_time_t end_time )
113 {
114 int amp = volume;
115 if ( shifter & 1 )
116 amp = -amp;
117
118 int delta = amp - last_amp;
119 if ( delta )
120 {
121 last_amp = amp;
122 synth.offset( time, delta, output );
123 }
124
125 time += delay;
126 if ( !volume )
127 time = end_time;
128
129 if ( time < end_time )
130 {
131 Blip_Buffer* const output = this->output;
132 unsigned shifter = this->shifter;
133 int delta = amp * 2;
134 int period = *this->period * 2;
135 if ( !period )
136 period = 16;
137
138 do
139 {
140 int changed = (shifter + 1) & 2; // set if prev and next bits differ
141 shifter = (((shifter << 15) ^ (shifter << tap)) & 0x8000) | (shifter >> 1);
142 if ( changed )
143 {
144 delta = -delta;
145 synth.offset_inline( time, delta, output );
146 }
147 time += period;
148 }
149 while ( time < end_time );
150
151 this->shifter = shifter;
152 this->last_amp = delta >> 1;
153 }
154 delay = time - end_time;
155 }
156
157 // Sms_Apu
158
159 Sms_Apu::Sms_Apu()
160 {
161 for ( int i = 0; i < 3; i++ )
162 {
163 squares [i].synth = &square_synth;
164 oscs [i] = &squares [i];
165 }
166 oscs [3] = &noise;
167
168 volume( 1.0 );
169 reset();
170 }
171
172 Sms_Apu::~Sms_Apu()
173 {
174 }
175
176 void Sms_Apu::volume( double vol )
177 {
178 vol *= 0.85 / (osc_count * 64 * 2);
179 square_synth.volume( vol );
180 noise.synth.volume( vol );
181 }
182
183 void Sms_Apu::treble_eq( const blip_eq_t& eq )
184 {
185 square_synth.treble_eq( eq );
186 noise.synth.treble_eq( eq );
187 }
188
189 void Sms_Apu::osc_output( int index, Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right )
190 {
191 require( (unsigned) index < osc_count );
192 require( (center && left && right) || (!center && !left && !right) );
193 Sms_Osc& osc = *oscs [index];
194 osc.outputs [1] = right;
195 osc.outputs [2] = left;
196 osc.outputs [3] = center;
197 osc.output = osc.outputs [osc.output_select];
198 }
199
200 void Sms_Apu::output( Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right )
201 {
202 for ( int i = 0; i < osc_count; i++ )
203 osc_output( i, center, left, right );
204 }
205
206 void Sms_Apu::reset()
207 {
208 stereo_found = false;
209 last_time = 0;
210 latch = 0;
211
212 squares [0].reset();
213 squares [1].reset();
214 squares [2].reset();
215 noise.reset();
216 }
217
218 void Sms_Apu::run_until( sms_time_t end_time )
219 {
220 require( end_time >= last_time ); // end_time must not be before previous time
221
222 if ( end_time > last_time )
223 {
224 // run oscillators
225 for ( int i = 0; i < osc_count; ++i )
226 {
227 Sms_Osc& osc = *oscs [i];
228 if ( osc.output )
229 {
230 if ( osc.output != osc.outputs [3] )
231 stereo_found = true; // playing on side output
232
233 if ( i < 3 )
234 squares [i].run( last_time, end_time );
235 else
236 noise.run( last_time, end_time );
237 }
238 }
239
240 last_time = end_time;
241 }
242 }
243
244 bool Sms_Apu::end_frame( sms_time_t end_time )
245 {
246 if ( end_time > last_time )
247 run_until( end_time );
248
249 assert( last_time >= end_time );
250 last_time -= end_time;
251
252 bool result = stereo_found;
253 stereo_found = false;
254 return result;
255 }
256
257 void Sms_Apu::write_ggstereo( sms_time_t time, int data )
258 {
259 require( (unsigned) data <= 0xFF );
260
261 run_until( time );
262
263 for ( int i = 0; i < osc_count; i++ )
264 {
265 Sms_Osc& osc = *oscs [i];
266 int flags = data >> i;
267 Blip_Buffer* old_output = osc.output;
268 osc.output_select = (flags >> 3 & 2) | (flags & 1);
269 osc.output = osc.outputs [osc.output_select];
270 if ( osc.output != old_output && osc.last_amp )
271 {
272 if ( old_output )
273 square_synth.offset( time, -osc.last_amp, old_output );
274 osc.last_amp = 0;
275 }
276 }
277 }
278
279 static const unsigned char volumes [16] = {
280 // volumes [i] = 64 * pow( 1.26, 15 - i ) / pow( 1.26, 15 )
281 64, 50, 39, 31, 24, 19, 15, 12, 9, 7, 5, 4, 3, 2, 1, 0
282 };
283
284 void Sms_Apu::write_data( sms_time_t time, int data )
285 {
286 require( (unsigned) data <= 0xFF );
287
288 run_until( time );
289
290 if ( data & 0x80 )
291 latch = data;
292
293 int index = (latch >> 5) & 3;
294 if ( latch & 0x10 )
295 {
296 oscs [index]->volume = volumes [data & 15];
297 }
298 else if ( index < 3 )
299 {
300 Sms_Square& sq = squares [index];
301 if ( data & 0x80 )
302 sq.period = (sq.period & 0xFF00) | (data << 4 & 0x00FF);
303 else
304 sq.period = (sq.period & 0x00FF) | (data << 8 & 0x3F00);
305 }
306 else
307 {
308 int select = data & 3;
309 if ( select < 3 )
310 noise.period = &noise_periods [select];
311 else
312 noise.period = &squares [2].period;
313
314 int const tap_disabled = 16;
315 noise.tap = (data & 0x04) ? 12 : tap_disabled;
316 noise.shifter = 0x8000;
317 }
318 }
319