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