diff options
author | Sean Bartell <wingedtachikoma@gmail.com> | 2011-06-25 21:32:25 -0400 |
---|---|---|
committer | Nils Wallménius <nils@rockbox.org> | 2012-04-25 22:13:20 +0200 |
commit | f40bfc9267b13b54e6379dfe7539447662879d24 (patch) | |
tree | 9b20069d5e62809ff434061ad730096836f916f2 /lib/rbcodec/codecs/libgme/ay_apu.c | |
parent | a0009907de7a0107d49040d8a180f140e2eff299 (diff) | |
download | rockbox-f40bfc9267b13b54e6379dfe7539447662879d24.tar.gz rockbox-f40bfc9267b13b54e6379dfe7539447662879d24.zip |
Add codecs to librbcodec.
Change-Id: Id7f4717d51ed02d67cb9f9cb3c0ada4a81843f97
Reviewed-on: http://gerrit.rockbox.org/137
Reviewed-by: Nils Wallménius <nils@rockbox.org>
Tested-by: Nils Wallménius <nils@rockbox.org>
Diffstat (limited to 'lib/rbcodec/codecs/libgme/ay_apu.c')
-rw-r--r-- | lib/rbcodec/codecs/libgme/ay_apu.c | 413 |
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 | ||
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 | // 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). | ||
25 | int const inaudible_freq = 16384; | ||
26 | |||
27 | int const period_factor = 16; | ||
28 | |||
29 | static 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 | |||
56 | static 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 | |||
70 | static 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 | |||
77 | void 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 | |||
107 | void 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 | |||
131 | int 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 | |||
140 | void 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 | |||
177 | int const noise_off = 0x08; | ||
178 | int const tone_off = 0x01; | ||
179 | |||
180 | void 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 | } | ||