summaryrefslogtreecommitdiff
path: root/lib/rbcodec/codecs/libgme/ay_apu.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/rbcodec/codecs/libgme/ay_apu.c')
-rw-r--r--lib/rbcodec/codecs/libgme/ay_apu.c413
1 files changed, 413 insertions, 0 deletions
diff --git a/lib/rbcodec/codecs/libgme/ay_apu.c b/lib/rbcodec/codecs/libgme/ay_apu.c
new file mode 100644
index 0000000000..c84e92d43d
--- /dev/null
+++ b/lib/rbcodec/codecs/libgme/ay_apu.c
@@ -0,0 +1,413 @@
1// Game_Music_Emu 0.6-pre. http://www.slack.net/~ant/
2
3#include "ay_apu.h"
4
5/* Copyright (C) 2006-2008 Shay Green. This module is free software; you
6can redistribute it and/or modify it under the terms of the GNU Lesser
7General Public License as published by the Free Software Foundation; either
8version 2.1 of the License, or (at your option) any later version. This
9module is distributed in the hope that it will be useful, but WITHOUT ANY
10WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
12details. You should have received a copy of the GNU Lesser General Public
13License along with this module; if not, write to the Free Software Foundation,
14Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
15
16#include "blargg_source.h"
17
18// Emulation inaccuracies:
19// * Noise isn't run when not in use
20// * Changes to envelope and noise periods are delayed until next reload
21// * Super-sonic tone should attenuate output to about 60%, not 50%
22
23// Tones above this frequency are treated as disabled tone at half volume.
24// Power of two is more efficient (avoids division).
25int const inaudible_freq = 16384;
26
27int const period_factor = 16;
28
29static byte const amp_table [16] =
30{
31#define ENTRY( n ) (byte) (n * ay_amp_range + 0.5)
32 // With channels tied together and 1K resistor to ground (as datasheet recommends),
33 // output nearly matches logarithmic curve as claimed. Approx. 1.5 dB per step.
34 ENTRY(0.000000),ENTRY(0.007813),ENTRY(0.011049),ENTRY(0.015625),
35 ENTRY(0.022097),ENTRY(0.031250),ENTRY(0.044194),ENTRY(0.062500),
36 ENTRY(0.088388),ENTRY(0.125000),ENTRY(0.176777),ENTRY(0.250000),
37 ENTRY(0.353553),ENTRY(0.500000),ENTRY(0.707107),ENTRY(1.000000),
38
39 /*
40 // Measured from an AY-3-8910A chip with date code 8611.
41
42 // Direct voltages without any load (very linear)
43 ENTRY(0.000000),ENTRY(0.046237),ENTRY(0.064516),ENTRY(0.089785),
44 ENTRY(0.124731),ENTRY(0.173118),ENTRY(0.225806),ENTRY(0.329032),
45 ENTRY(0.360215),ENTRY(0.494624),ENTRY(0.594624),ENTRY(0.672043),
46 ENTRY(0.766129),ENTRY(0.841935),ENTRY(0.926882),ENTRY(1.000000),
47 // With only some load
48 ENTRY(0.000000),ENTRY(0.011940),ENTRY(0.017413),ENTRY(0.024876),
49 ENTRY(0.036318),ENTRY(0.054229),ENTRY(0.072637),ENTRY(0.122388),
50 ENTRY(0.174129),ENTRY(0.239303),ENTRY(0.323881),ENTRY(0.410945),
51 ENTRY(0.527363),ENTRY(0.651741),ENTRY(0.832338),ENTRY(1.000000),
52 */
53#undef ENTRY
54};
55
56static byte const modes [8] =
57{
58#define MODE( a0,a1, b0,b1, c0,c1 ) \
59 (a0 | a1<<1 | b0<<2 | b1<<3 | c0<<4 | c1<<5)
60 MODE( 1,0, 1,0, 1,0 ),
61 MODE( 1,0, 0,0, 0,0 ),
62 MODE( 1,0, 0,1, 1,0 ),
63 MODE( 1,0, 1,1, 1,1 ),
64 MODE( 0,1, 0,1, 0,1 ),
65 MODE( 0,1, 1,1, 1,1 ),
66 MODE( 0,1, 1,0, 0,1 ),
67 MODE( 0,1, 0,0, 0,0 ),
68};
69
70static void set_output( struct Ay_Apu* this, struct Blip_Buffer* b )
71{
72 int i;
73 for ( i = 0; i < ay_osc_count; ++i )
74 Ay_apu_set_output( this, i, b );
75}
76
77void Ay_apu_init( struct Ay_Apu* this )
78{
79 Synth_init( &this->synth_ );
80
81 // build full table of the upper 8 envelope waveforms
82 int m;
83 for ( m = 8; m--; )
84 {
85 byte* out = this->env_modes [m];
86 int x, y, flags = modes [m];
87 for ( x = 3; --x >= 0; )
88 {
89 int amp = flags & 1;
90 int end = flags >> 1 & 1;
91 int step = end - amp;
92 amp *= 15;
93 for ( y = 16; --y >= 0; )
94 {
95 *out++ = amp_table [amp];
96 amp += step;
97 }
98 flags >>= 2;
99 }
100 }
101
102 set_output( this, NULL );
103 Ay_apu_volume( this, (int)FP_ONE_VOLUME );
104 Ay_apu_reset( this );
105}
106
107void Ay_apu_reset( struct Ay_Apu* this )
108{
109 this->addr_ = 0;
110 this->last_time = 0;
111 this->noise_delay = 0;
112 this->noise_lfsr = 1;
113
114 struct osc_t* osc;
115 for ( osc = &this->oscs [ay_osc_count]; osc != this->oscs; )
116 {
117 osc--;
118 osc->period = period_factor;
119 osc->delay = 0;
120 osc->last_amp = 0;
121 osc->phase = 0;
122 }
123
124 int i;
125 for ( i = sizeof this->regs; --i >= 0; )
126 this->regs [i] = 0;
127 this->regs [7] = 0xFF;
128 write_data_( this, 13, 0 );
129}
130
131int Ay_apu_read( struct Ay_Apu* this )
132{
133 static byte const masks [ay_reg_count] = {
134 0xFF, 0x0F, 0xFF, 0x0F, 0xFF, 0x0F, 0x1F, 0x3F,
135 0x1F, 0x1F, 0x1F, 0xFF, 0xFF, 0x0F, 0x00, 0x00
136 };
137 return this->regs [this->addr_] & masks [this->addr_];
138}
139
140void write_data_( struct Ay_Apu* this, int addr, int data )
141{
142 assert( (unsigned) addr < ay_reg_count );
143
144 /* if ( (unsigned) addr >= 14 )
145 dprintf( "Wrote to I/O port %02X\n", (int) addr ); */
146
147 // envelope mode
148 if ( addr == 13 )
149 {
150 if ( !(data & 8) ) // convert modes 0-7 to proper equivalents
151 data = (data & 4) ? 15 : 9;
152 this->env_wave = this->env_modes [data - 7];
153 this->env_pos = -48;
154 this->env_delay = 0; // will get set to envelope period in run_until()
155 }
156 this->regs [addr] = data;
157
158 // handle period changes accurately
159 int i = addr >> 1;
160 if ( i < ay_osc_count )
161 {
162 blip_time_t period = (this->regs [i * 2 + 1] & 0x0F) * (0x100 * period_factor) +
163 this->regs [i * 2] * period_factor;
164 if ( !period )
165 period = period_factor;
166
167 // adjust time of next timer expiration based on change in period
168 struct osc_t* osc = &this->oscs [i];
169 if ( (osc->delay += period - osc->period) < 0 )
170 osc->delay = 0;
171 osc->period = period;
172 }
173
174 // TODO: same as above for envelope timer, and it also has a divide by two after it
175}
176
177int const noise_off = 0x08;
178int const tone_off = 0x01;
179
180void run_until( struct Ay_Apu* this, blip_time_t final_end_time )
181{
182 require( final_end_time >= this->last_time );
183
184 // noise period and initial values
185 blip_time_t const noise_period_factor = period_factor * 2; // verified
186 blip_time_t noise_period = (this->regs [6] & 0x1F) * noise_period_factor;
187 if ( !noise_period )
188 noise_period = noise_period_factor;
189 blip_time_t const old_noise_delay = this->noise_delay;
190 unsigned const old_noise_lfsr = this->noise_lfsr;
191
192 // envelope period
193 blip_time_t const env_period_factor = period_factor * 2; // verified
194 blip_time_t env_period = (this->regs [12] * 0x100 + this->regs [11]) * env_period_factor;
195 if ( !env_period )
196 env_period = env_period_factor; // same as period 1 on my AY chip
197 if ( !this->env_delay )
198 this->env_delay = env_period;
199
200 // run each osc separately
201 int index;
202 for ( index = 0; index < ay_osc_count; index++ )
203 {
204 struct osc_t* const osc = &this->oscs [index];
205 int osc_mode = this->regs [7] >> index;
206
207 // output
208 struct Blip_Buffer* const osc_output = osc->output;
209 if ( !osc_output )
210 continue;
211 Blip_set_modified( osc_output );
212
213 // period
214 int half_vol = 0;
215 blip_time_t inaudible_period = (unsigned) (Blip_clock_rate( osc_output ) +
216 inaudible_freq) / (unsigned) (inaudible_freq * 2);
217 if ( osc->period <= inaudible_period && !(osc_mode & tone_off) )
218 {
219 half_vol = 1; // Actually around 60%, but 50% is close enough
220 osc_mode |= tone_off;
221 }
222
223 // envelope
224 blip_time_t start_time = this->last_time;
225 blip_time_t end_time = final_end_time;
226 int const vol_mode = this->regs [0x08 + index];
227 int volume = amp_table [vol_mode & 0x0F] >> half_vol;
228 int osc_env_pos = this->env_pos;
229 if ( vol_mode & 0x10 )
230 {
231 volume = this->env_wave [osc_env_pos] >> half_vol;
232 // use envelope only if it's a repeating wave or a ramp that hasn't finished
233 if ( !(this->regs [13] & 1) || osc_env_pos < -32 )
234 {
235 end_time = start_time + this->env_delay;
236 if ( end_time >= final_end_time )
237 end_time = final_end_time;
238
239 //if ( !(regs [12] | regs [11]) )
240 // dprintf( "Used envelope period 0\n" );
241 }
242 else if ( !volume )
243 {
244 osc_mode = noise_off | tone_off;
245 }
246 }
247 else if ( !volume )
248 {
249 osc_mode = noise_off | tone_off;
250 }
251
252 // tone time
253 blip_time_t const period = osc->period;
254 blip_time_t time = start_time + osc->delay;
255 if ( osc_mode & tone_off ) // maintain tone's phase when off
256 {
257 int count = (final_end_time - time + period - 1) / period;
258 time += count * period;
259 osc->phase ^= count & 1;
260 }
261
262 // noise time
263 blip_time_t ntime = final_end_time;
264 unsigned noise_lfsr = 1;
265 if ( !(osc_mode & noise_off) )
266 {
267 ntime = start_time + old_noise_delay;
268 noise_lfsr = old_noise_lfsr;
269 //if ( (regs [6] & 0x1F) == 0 )
270 // dprintf( "Used noise period 0\n" );
271 }
272
273 // The following efficiently handles several cases (least demanding first):
274 // * Tone, noise, and envelope disabled, where channel acts as 4-bit DAC
275 // * Just tone or just noise, envelope disabled
276 // * Envelope controlling tone and/or noise
277 // * Tone and noise disabled, envelope enabled with high frequency
278 // * Tone and noise together
279 // * Tone and noise together with envelope
280
281 // this loop only runs one iteration if envelope is disabled. If envelope
282 // is being used as a waveform (tone and noise disabled), this loop will
283 // still be reasonably efficient since the bulk of it will be skipped.
284 while ( 1 )
285 {
286 // current amplitude
287 int amp = 0;
288 if ( (osc_mode | osc->phase) & 1 & (osc_mode >> 3 | noise_lfsr) )
289 amp = volume;
290 {
291 int delta = amp - osc->last_amp;
292 if ( delta )
293 {
294 osc->last_amp = amp;
295 Synth_offset( &this->synth_, start_time, delta, osc_output );
296 }
297 }
298
299 // Run wave and noise interleved with each catching up to the other.
300 // If one or both are disabled, their "current time" will be past end time,
301 // so there will be no significant performance hit.
302 if ( ntime < end_time || time < end_time )
303 {
304 // Since amplitude was updated above, delta will always be +/- volume,
305 // so we can avoid using last_amp every time to calculate the delta.
306 int delta = amp * 2 - volume;
307 int delta_non_zero = delta != 0;
308 int phase = osc->phase | (osc_mode & tone_off); assert( tone_off == 0x01 );
309 do
310 {
311 // run noise
312 blip_time_t end = end_time;
313 if ( end_time > time ) end = time;
314 if ( phase & delta_non_zero )
315 {
316 while ( ntime <= end ) // must advance *past* time to avoid hang
317 {
318 int changed = noise_lfsr + 1;
319 noise_lfsr = (-(noise_lfsr & 1) & 0x12000) ^ (noise_lfsr >> 1);
320 if ( changed & 2 )
321 {
322 delta = -delta;
323 Synth_offset( &this->synth_, ntime, delta, osc_output );
324 }
325 ntime += noise_period;
326 }
327 }
328 else
329 {
330 // 20 or more noise periods on average for some music
331 int remain = end - ntime;
332 int count = remain / noise_period;
333 if ( remain >= 0 )
334 ntime += noise_period + count * noise_period;
335 }
336
337 // run tone
338 end = end_time;
339 if ( end_time > ntime ) end = ntime;
340 if ( noise_lfsr & delta_non_zero )
341 {
342 while ( time < end )
343 {
344 delta = -delta;
345 Synth_offset( &this->synth_, time, delta, osc_output );
346 time += period;
347
348 // alternate (less-efficient) implementation
349 //phase ^= 1;
350 }
351 phase = (unsigned) (-delta) >> (CHAR_BIT * sizeof (unsigned) - 1);
352 check( phase == (delta > 0) );
353 }
354 else
355 {
356 // loop usually runs less than once
357 //SUB_CASE_COUNTER( (time < end) * (end - time + period - 1) / period );
358
359 while ( time < end )
360 {
361 time += period;
362 phase ^= 1;
363 }
364 }
365 }
366 while ( time < end_time || ntime < end_time );
367
368 osc->last_amp = (delta + volume) >> 1;
369 if ( !(osc_mode & tone_off) )
370 osc->phase = phase;
371 }
372
373 if ( end_time >= final_end_time )
374 break; // breaks first time when envelope is disabled
375
376 // next envelope step
377 if ( ++osc_env_pos >= 0 )
378 osc_env_pos -= 32;
379 volume = this->env_wave [osc_env_pos] >> half_vol;
380
381 start_time = end_time;
382 end_time += env_period;
383 if ( end_time > final_end_time )
384 end_time = final_end_time;
385 }
386 osc->delay = time - final_end_time;
387
388 if ( !(osc_mode & noise_off) )
389 {
390 this->noise_delay = ntime - final_end_time;
391 this->noise_lfsr = noise_lfsr;
392 }
393 }
394
395 // TODO: optimized saw wave envelope?
396
397 // maintain envelope phase
398 blip_time_t remain = final_end_time - this->last_time - this->env_delay;
399 if ( remain >= 0 )
400 {
401 int count = (remain + env_period) / env_period;
402 this->env_pos += count;
403 if ( this->env_pos >= 0 )
404 this->env_pos = (this->env_pos & 31) - 32;
405 remain -= count * env_period;
406 assert( -remain <= env_period );
407 }
408 this->env_delay = -remain;
409 assert( this->env_delay > 0 );
410 assert( this->env_pos < 0 );
411
412 this->last_time = final_end_time;
413}