summaryrefslogtreecommitdiff
path: root/lib/rbcodec/codecs/libgme/hes_apu.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/rbcodec/codecs/libgme/hes_apu.c')
-rw-r--r--lib/rbcodec/codecs/libgme/hes_apu.c371
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
7can redistribute it and/or modify it under the terms of the GNU Lesser
8General Public License as published by the Free Software Foundation; either
9version 2.1 of the License, or (at your option) any later version. This
10module is distributed in the hope that it will be useful, but WITHOUT ANY
11WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
13details. You should have received a copy of the GNU Lesser General Public
14License along with this module; if not, write to the Free Software Foundation,
15Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
16
17#include "blargg_source.h"
18
19enum { center_waves = 1 }; // reduces asymmetry and clamping when starting notes
20
21static 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
80void 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
97void 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
118void 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
137static 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
286void 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
359void 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}