Mercurial > audlegacy
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 |