diff options
Diffstat (limited to 'lib/rbcodec/codecs/libgme/hes_apu.c')
-rw-r--r-- | lib/rbcodec/codecs/libgme/hes_apu.c | 371 |
1 files changed, 371 insertions, 0 deletions
diff --git a/lib/rbcodec/codecs/libgme/hes_apu.c b/lib/rbcodec/codecs/libgme/hes_apu.c new file mode 100644 index 0000000000..a9cd32c8aa --- /dev/null +++ b/lib/rbcodec/codecs/libgme/hes_apu.c | |||
@@ -0,0 +1,371 @@ | |||
1 | // Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ | ||
2 | |||
3 | #include "hes_apu.h" | ||
4 | #include <string.h> | ||
5 | |||
6 | /* Copyright (C) 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 more | ||
13 | details. You should have received a copy of the GNU Lesser General Public | ||
14 | License along with this module; if not, write to the Free Software Foundation, | ||
15 | Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ | ||
16 | |||
17 | #include "blargg_source.h" | ||
18 | |||
19 | enum { center_waves = 1 }; // reduces asymmetry and clamping when starting notes | ||
20 | |||
21 | static void balance_changed( struct Hes_Apu* this, struct Hes_Osc* osc ) | ||
22 | { | ||
23 | static short const log_table [32] = { // ~1.5 db per step | ||
24 | #define ENTRY( factor ) (short) (factor * amp_range / 31.0 + 0.5) | ||
25 | ENTRY( 0.000000 ),ENTRY( 0.005524 ),ENTRY( 0.006570 ),ENTRY( 0.007813 ), | ||
26 | ENTRY( 0.009291 ),ENTRY( 0.011049 ),ENTRY( 0.013139 ),ENTRY( 0.015625 ), | ||
27 | ENTRY( 0.018581 ),ENTRY( 0.022097 ),ENTRY( 0.026278 ),ENTRY( 0.031250 ), | ||
28 | ENTRY( 0.037163 ),ENTRY( 0.044194 ),ENTRY( 0.052556 ),ENTRY( 0.062500 ), | ||
29 | ENTRY( 0.074325 ),ENTRY( 0.088388 ),ENTRY( 0.105112 ),ENTRY( 0.125000 ), | ||
30 | ENTRY( 0.148651 ),ENTRY( 0.176777 ),ENTRY( 0.210224 ),ENTRY( 0.250000 ), | ||
31 | ENTRY( 0.297302 ),ENTRY( 0.353553 ),ENTRY( 0.420448 ),ENTRY( 0.500000 ), | ||
32 | ENTRY( 0.594604 ),ENTRY( 0.707107 ),ENTRY( 0.840896 ),ENTRY( 1.000000 ), | ||
33 | #undef ENTRY | ||
34 | }; | ||
35 | |||
36 | int vol = (osc->control & 0x1F) - 0x1E * 2; | ||
37 | |||
38 | int left = vol + (osc->balance >> 3 & 0x1E) + (this->balance >> 3 & 0x1E); | ||
39 | if ( left < 0 ) left = 0; | ||
40 | |||
41 | int right = vol + (osc->balance << 1 & 0x1E) + (this->balance << 1 & 0x1E); | ||
42 | if ( right < 0 ) right = 0; | ||
43 | |||
44 | // optimizing for the common case of being centered also allows easy | ||
45 | // panning using Effects_Buffer | ||
46 | |||
47 | // Separate balance into center volume and additional on either left or right | ||
48 | osc->output [0] = osc->outputs [0]; // center | ||
49 | osc->output [1] = osc->outputs [2]; // right | ||
50 | int base = log_table [left ]; | ||
51 | int side = log_table [right] - base; | ||
52 | if ( side < 0 ) | ||
53 | { | ||
54 | base += side; | ||
55 | side = -side; | ||
56 | osc->output [1] = osc->outputs [1]; // left | ||
57 | } | ||
58 | |||
59 | // Optimize when output is far left, center, or far right | ||
60 | if ( !base || osc->output [0] == osc->output [1] ) | ||
61 | { | ||
62 | base += side; | ||
63 | side = 0; | ||
64 | osc->output [0] = osc->output [1]; | ||
65 | osc->output [1] = NULL; | ||
66 | osc->last_amp [1] = 0; | ||
67 | } | ||
68 | |||
69 | if ( center_waves ) | ||
70 | { | ||
71 | // TODO: this can leave a non-zero level in a buffer (minor) | ||
72 | osc->last_amp [0] += (base - osc->volume [0]) * 16; | ||
73 | osc->last_amp [1] += (side - osc->volume [1]) * 16; | ||
74 | } | ||
75 | |||
76 | osc->volume [0] = base; | ||
77 | osc->volume [1] = side; | ||
78 | } | ||
79 | |||
80 | void Apu_init( struct Hes_Apu* this ) | ||
81 | { | ||
82 | struct Hes_Osc* osc = &this->oscs [osc_count]; | ||
83 | do | ||
84 | { | ||
85 | osc--; | ||
86 | osc->output [0] = NULL; | ||
87 | osc->output [1] = NULL; | ||
88 | osc->outputs [0] = NULL; | ||
89 | osc->outputs [1] = NULL; | ||
90 | osc->outputs [2] = NULL; | ||
91 | } | ||
92 | while ( osc != this->oscs ); | ||
93 | |||
94 | Apu_reset( this ); | ||
95 | } | ||
96 | |||
97 | void Apu_reset( struct Hes_Apu* this ) | ||
98 | { | ||
99 | this->latch = 0; | ||
100 | this->balance = 0xFF; | ||
101 | |||
102 | struct Hes_Osc* osc = &this->oscs [osc_count]; | ||
103 | do | ||
104 | { | ||
105 | osc--; | ||
106 | memset( osc, 0, offsetof (struct Hes_Osc,outputs) ); | ||
107 | osc->lfsr = 1; | ||
108 | osc->control = 0x40; | ||
109 | osc->balance = 0xFF; | ||
110 | } | ||
111 | while ( osc != this->oscs ); | ||
112 | |||
113 | // Only last two oscs support noise | ||
114 | this->oscs [osc_count - 2].lfsr = 0x200C3; // equivalent to 1 in Fibonacci LFSR | ||
115 | this->oscs [osc_count - 1].lfsr = 0x200C3; | ||
116 | } | ||
117 | |||
118 | void Apu_osc_output( struct Hes_Apu* this, int i, struct Blip_Buffer* center, struct Blip_Buffer* left, struct Blip_Buffer* right ) | ||
119 | { | ||
120 | // Must be silent (all NULL), mono (left and right NULL), or stereo (none NULL) | ||
121 | require( !center || (center && !left && !right) || (center && left && right) ); | ||
122 | require( (unsigned) i < osc_count ); // fails if you pass invalid osc index | ||
123 | |||
124 | if ( !center || !left || !right ) | ||
125 | { | ||
126 | left = center; | ||
127 | right = center; | ||
128 | } | ||
129 | |||
130 | struct Hes_Osc* o = &this->oscs [i]; | ||
131 | o->outputs [0] = center; | ||
132 | o->outputs [1] = right; | ||
133 | o->outputs [2] = left; | ||
134 | balance_changed( this, o ); | ||
135 | } | ||
136 | |||
137 | static void run_osc( struct Hes_Osc* o, struct Blip_Synth* syn, blip_time_t end_time ) | ||
138 | { | ||
139 | int vol0 = o->volume [0]; | ||
140 | int vol1 = o->volume [1]; | ||
141 | int dac = o->dac; | ||
142 | |||
143 | struct Blip_Buffer* out0 = o->output [0]; // cache often-used values | ||
144 | struct Blip_Buffer* out1 = o->output [1]; | ||
145 | if ( !(o->control & 0x80) ) | ||
146 | out0 = NULL; | ||
147 | |||
148 | if ( out0 ) | ||
149 | { | ||
150 | // Update amplitudes | ||
151 | if ( out1 ) | ||
152 | { | ||
153 | int delta = dac * vol1 - o->last_amp [1]; | ||
154 | if ( delta ) | ||
155 | { | ||
156 | Synth_offset( syn, o->last_time, delta, out1 ); | ||
157 | Blip_set_modified( out1 ); | ||
158 | } | ||
159 | } | ||
160 | int delta = dac * vol0 - o->last_amp [0]; | ||
161 | if ( delta ) | ||
162 | { | ||
163 | Synth_offset( syn, o->last_time, delta, out0 ); | ||
164 | Blip_set_modified( out0 ); | ||
165 | } | ||
166 | |||
167 | // Don't generate if silent | ||
168 | if ( !(vol0 | vol1) ) | ||
169 | out0 = NULL; | ||
170 | } | ||
171 | |||
172 | // Generate noise | ||
173 | int noise = 0; | ||
174 | if ( o->lfsr ) | ||
175 | { | ||
176 | noise = o->noise & 0x80; | ||
177 | |||
178 | blip_time_t time = o->last_time + o->noise_delay; | ||
179 | if ( time < end_time ) | ||
180 | { | ||
181 | int period = (~o->noise & 0x1F) * 128; | ||
182 | if ( !period ) | ||
183 | period = 64; | ||
184 | |||
185 | if ( noise && out0 ) | ||
186 | { | ||
187 | unsigned lfsr = o->lfsr; | ||
188 | do | ||
189 | { | ||
190 | int new_dac = -(lfsr & 1); | ||
191 | lfsr = (lfsr >> 1) ^ (0x30061 & new_dac); | ||
192 | |||
193 | int delta = (new_dac &= 0x1F) - dac; | ||
194 | if ( delta ) | ||
195 | { | ||
196 | dac = new_dac; | ||
197 | Synth_offset( syn, time, delta * vol0, out0 ); | ||
198 | if ( out1 ) | ||
199 | Synth_offset( syn, time, delta * vol1, out1 ); | ||
200 | } | ||
201 | time += period; | ||
202 | } | ||
203 | while ( time < end_time ); | ||
204 | |||
205 | if ( !lfsr ) | ||
206 | { | ||
207 | lfsr = 1; | ||
208 | check( false ); | ||
209 | } | ||
210 | o->lfsr = lfsr; | ||
211 | |||
212 | Blip_set_modified( out0 ); | ||
213 | if ( out1 ) | ||
214 | Blip_set_modified( out1 ); | ||
215 | } | ||
216 | else | ||
217 | { | ||
218 | // Maintain phase when silent | ||
219 | int count = (end_time - time + period - 1) / period; | ||
220 | time += count * period; | ||
221 | |||
222 | // not worth it | ||
223 | //while ( count-- ) | ||
224 | // o->lfsr = (o->lfsr >> 1) ^ (0x30061 * (o->lfsr & 1)); | ||
225 | } | ||
226 | } | ||
227 | o->noise_delay = time - end_time; | ||
228 | } | ||
229 | |||
230 | // Generate wave | ||
231 | blip_time_t time = o->last_time + o->delay; | ||
232 | if ( time < end_time ) | ||
233 | { | ||
234 | int phase = (o->phase + 1) & 0x1F; // pre-advance for optimal inner loop | ||
235 | int period = o->period * 2; | ||
236 | |||
237 | if ( period >= 14 && out0 && !((o->control & 0x40) | noise) ) | ||
238 | { | ||
239 | do | ||
240 | { | ||
241 | int new_dac = o->wave [phase]; | ||
242 | phase = (phase + 1) & 0x1F; | ||
243 | int delta = new_dac - dac; | ||
244 | if ( delta ) | ||
245 | { | ||
246 | dac = new_dac; | ||
247 | Synth_offset( syn, time, delta * vol0, out0 ); | ||
248 | if ( out1 ) | ||
249 | Synth_offset( syn, time, delta * vol1, out1 ); | ||
250 | } | ||
251 | time += period; | ||
252 | } | ||
253 | while ( time < end_time ); | ||
254 | Blip_set_modified( out0 ); | ||
255 | if ( out1 ) | ||
256 | Blip_set_modified( out1 ); | ||
257 | } | ||
258 | else | ||
259 | { | ||
260 | // Maintain phase when silent | ||
261 | int count = end_time - time; | ||
262 | if ( !period ) | ||
263 | period = 1; | ||
264 | count = (count + period - 1) / period; | ||
265 | |||
266 | phase += count; // phase will be masked below | ||
267 | time += count * period; | ||
268 | } | ||
269 | |||
270 | // TODO: Find whether phase increments even when both volumes are zero. | ||
271 | // CAN'T simply check for out0 being non-NULL, since it could be NULL | ||
272 | // if channel is muted in player, but still has non-zero volume. | ||
273 | // City Hunter breaks when this check is removed. | ||
274 | if ( !(o->control & 0x40) && (vol0 | vol1) ) | ||
275 | o->phase = (phase - 1) & 0x1F; // undo pre-advance | ||
276 | } | ||
277 | o->delay = time - end_time; | ||
278 | check( o->delay >= 0 ); | ||
279 | |||
280 | o->last_time = end_time; | ||
281 | o->dac = dac; | ||
282 | o->last_amp [0] = dac * vol0; | ||
283 | o->last_amp [1] = dac * vol1; | ||
284 | } | ||
285 | |||
286 | void Apu_write_data( struct Hes_Apu* this, blip_time_t time, int addr, int data ) | ||
287 | { | ||
288 | if ( addr == 0x800 ) | ||
289 | { | ||
290 | this->latch = data & 7; | ||
291 | } | ||
292 | else if ( addr == 0x801 ) | ||
293 | { | ||
294 | if ( this->balance != data ) | ||
295 | { | ||
296 | this->balance = data; | ||
297 | |||
298 | struct Hes_Osc* osc = &this->oscs [osc_count]; | ||
299 | do | ||
300 | { | ||
301 | osc--; | ||
302 | run_osc( osc, &this->synth, time ); | ||
303 | balance_changed( this, this->oscs ); | ||
304 | } | ||
305 | while ( osc != this->oscs ); | ||
306 | } | ||
307 | } | ||
308 | else if ( this->latch < osc_count ) | ||
309 | { | ||
310 | struct Hes_Osc* osc = &this->oscs [this->latch]; | ||
311 | run_osc( osc, &this->synth, time ); | ||
312 | switch ( addr ) | ||
313 | { | ||
314 | case 0x802: | ||
315 | osc->period = (osc->period & 0xF00) | data; | ||
316 | break; | ||
317 | |||
318 | case 0x803: | ||
319 | osc->period = (osc->period & 0x0FF) | ((data & 0x0F) << 8); | ||
320 | break; | ||
321 | |||
322 | case 0x804: | ||
323 | if ( osc->control & 0x40 & ~data ) | ||
324 | osc->phase = 0; | ||
325 | osc->control = data; | ||
326 | balance_changed( this, osc ); | ||
327 | break; | ||
328 | |||
329 | case 0x805: | ||
330 | osc->balance = data; | ||
331 | balance_changed( this, osc ); | ||
332 | break; | ||
333 | |||
334 | case 0x806: | ||
335 | data &= 0x1F; | ||
336 | if ( !(osc->control & 0x40) ) | ||
337 | { | ||
338 | osc->wave [osc->phase] = data; | ||
339 | osc->phase = (osc->phase + 1) & 0x1F; | ||
340 | } | ||
341 | else if ( osc->control & 0x80 ) | ||
342 | { | ||
343 | osc->dac = data; | ||
344 | } | ||
345 | break; | ||
346 | |||
347 | case 0x807: | ||
348 | osc->noise = data; | ||
349 | break; | ||
350 | |||
351 | case 0x809: | ||
352 | if ( !(data & 0x80) && (data & 0x03) != 0 ) { | ||
353 | dprintf( "HES LFO not supported\n" ); | ||
354 | } | ||
355 | } | ||
356 | } | ||
357 | } | ||
358 | |||
359 | void Apu_end_frame( struct Hes_Apu* this, blip_time_t end_time ) | ||
360 | { | ||
361 | struct Hes_Osc* osc = &this->oscs [osc_count]; | ||
362 | do | ||
363 | { | ||
364 | osc--; | ||
365 | if ( end_time > osc->last_time ) | ||
366 | run_osc( osc, &this->synth, end_time ); | ||
367 | assert( osc->last_time >= end_time ); | ||
368 | osc->last_time -= end_time; | ||
369 | } | ||
370 | while ( osc != this->oscs ); | ||
371 | } | ||