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/gb_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/gb_apu.c')
-rw-r--r-- | lib/rbcodec/codecs/libgme/gb_apu.c | 410 |
1 files changed, 410 insertions, 0 deletions
diff --git a/lib/rbcodec/codecs/libgme/gb_apu.c b/lib/rbcodec/codecs/libgme/gb_apu.c new file mode 100644 index 0000000000..e8bf3afcf5 --- /dev/null +++ b/lib/rbcodec/codecs/libgme/gb_apu.c | |||
@@ -0,0 +1,410 @@ | |||
1 | // Gb_Snd_Emu 0.1.4. http://www.slack.net/~ant/ | ||
2 | |||
3 | #include "gb_apu.h" | ||
4 | |||
5 | //#include "gb_apu_logger.h" | ||
6 | |||
7 | /* Copyright (C) 2003-2008 Shay Green. This module is free software; you | ||
8 | can redistribute it and/or modify it under the terms of the GNU Lesser | ||
9 | General Public License as published by the Free Software Foundation; either | ||
10 | version 2.1 of the License, or (at your option) any later version. This | ||
11 | module is distributed in the hope that it will be useful, but WITHOUT ANY | ||
12 | WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS | ||
13 | FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more | ||
14 | details. You should have received a copy of the GNU Lesser General Public | ||
15 | License along with this module; if not, write to the Free Software Foundation, | ||
16 | Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ | ||
17 | |||
18 | #include "blargg_source.h" | ||
19 | |||
20 | int const vol_reg = 0xFF24; | ||
21 | int const stereo_reg = 0xFF25; | ||
22 | int const status_reg = 0xFF26; | ||
23 | int const wave_ram = 0xFF30; | ||
24 | |||
25 | int const power_mask = 0x80; | ||
26 | |||
27 | static inline int calc_output( struct Gb_Apu* this, int osc ) | ||
28 | { | ||
29 | int bits = this->regs [stereo_reg - io_addr] >> osc; | ||
30 | return (bits >> 3 & 2) | (bits & 1); | ||
31 | } | ||
32 | |||
33 | void Apu_set_output( struct Gb_Apu* this, int i, struct Blip_Buffer* center, struct Blip_Buffer* left, struct Blip_Buffer* right ) | ||
34 | { | ||
35 | // Must be silent (all NULL), mono (left and right NULL), or stereo (none NULL) | ||
36 | require( !center || (center && !left && !right) || (center && left && right) ); | ||
37 | require( (unsigned) i < osc_count ); // fails if you pass invalid osc index | ||
38 | |||
39 | if ( !center || !left || !right ) | ||
40 | { | ||
41 | left = center; | ||
42 | right = center; | ||
43 | } | ||
44 | |||
45 | struct Gb_Osc* o = this->oscs [i]; | ||
46 | o->outputs [1] = right; | ||
47 | o->outputs [2] = left; | ||
48 | o->outputs [3] = center; | ||
49 | o->output = o->outputs [calc_output( this, i )]; | ||
50 | } | ||
51 | |||
52 | static void synth_volume( struct Gb_Apu* this, int iv ) | ||
53 | { | ||
54 | int v = (this->volume_ * 6) / 10 / osc_count / 15 /*steps*/ / 8 /*master vol range*/ * iv; | ||
55 | Synth_volume( &this->synth, v ); | ||
56 | } | ||
57 | |||
58 | static void apply_volume( struct Gb_Apu* this ) | ||
59 | { | ||
60 | // TODO: Doesn't handle differing left and right volumes (panning). | ||
61 | // Not worth the complexity. | ||
62 | int data = this->regs [vol_reg - io_addr]; | ||
63 | int left = data >> 4 & 7; | ||
64 | int right = data & 7; | ||
65 | //if ( data & 0x88 ) dprintf( "Vin: %02X\n", data & 0x88 ); | ||
66 | //if ( left != right ) dprintf( "l: %d r: %d\n", left, right ); | ||
67 | synth_volume( this, max( left, right ) + 1 ); | ||
68 | } | ||
69 | |||
70 | void Apu_volume( struct Gb_Apu* this, int v ) | ||
71 | { | ||
72 | if ( this->volume_ != v ) | ||
73 | { | ||
74 | this->volume_ = v; | ||
75 | apply_volume( this ); | ||
76 | } | ||
77 | } | ||
78 | |||
79 | static void reset_regs( struct Gb_Apu* this ) | ||
80 | { | ||
81 | int i; | ||
82 | for ( i = 0; i < 0x20; i++ ) | ||
83 | this->regs [i] = 0; | ||
84 | |||
85 | Sweep_reset ( &this->square1 ); | ||
86 | Square_reset( &this->square2 ); | ||
87 | Wave_reset ( &this->wave ); | ||
88 | Noise_reset ( &this->noise ); | ||
89 | |||
90 | apply_volume( this ); | ||
91 | } | ||
92 | |||
93 | static void reset_lengths( struct Gb_Apu* this ) | ||
94 | { | ||
95 | this->square1.osc.length_ctr = 64; | ||
96 | this->square2.osc.length_ctr = 64; | ||
97 | this->wave .osc.length_ctr = 256; | ||
98 | this->noise .osc.length_ctr = 64; | ||
99 | } | ||
100 | |||
101 | void Apu_reduce_clicks( struct Gb_Apu* this, bool reduce ) | ||
102 | { | ||
103 | this->reduce_clicks_ = reduce; | ||
104 | |||
105 | // Click reduction makes DAC off generate same output as volume 0 | ||
106 | int dac_off_amp = 0; | ||
107 | if ( reduce && this->wave.osc.mode != mode_agb ) // AGB already eliminates clicks | ||
108 | dac_off_amp = -dac_bias; | ||
109 | |||
110 | int i; | ||
111 | for ( i = 0; i < osc_count; i++ ) | ||
112 | this->oscs [i]->dac_off_amp = dac_off_amp; | ||
113 | |||
114 | // AGB always eliminates clicks on wave channel using same method | ||
115 | if ( this->wave.osc.mode == mode_agb ) | ||
116 | this->wave.osc.dac_off_amp = -dac_bias; | ||
117 | } | ||
118 | |||
119 | void Apu_reset( struct Gb_Apu* this, enum gb_mode_t mode, bool agb_wave ) | ||
120 | { | ||
121 | // Hardware mode | ||
122 | if ( agb_wave ) | ||
123 | mode = mode_agb; // using AGB wave features implies AGB hardware | ||
124 | this->wave.agb_mask = agb_wave ? 0xFF : 0; | ||
125 | int i; | ||
126 | for ( i = 0; i < osc_count; i++ ) | ||
127 | this->oscs [i]->mode = mode; | ||
128 | Apu_reduce_clicks( this, this->reduce_clicks_ ); | ||
129 | |||
130 | // Reset state | ||
131 | this->frame_time = 0; | ||
132 | this->last_time = 0; | ||
133 | this->frame_phase = 0; | ||
134 | |||
135 | reset_regs( this ); | ||
136 | reset_lengths( this ); | ||
137 | |||
138 | // Load initial wave RAM | ||
139 | static byte const initial_wave [2] [16] = { | ||
140 | {0x84,0x40,0x43,0xAA,0x2D,0x78,0x92,0x3C,0x60,0x59,0x59,0xB0,0x34,0xB8,0x2E,0xDA}, | ||
141 | {0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF}, | ||
142 | }; | ||
143 | int b; | ||
144 | for ( b = 2; --b >= 0; ) | ||
145 | { | ||
146 | // Init both banks (does nothing if not in AGB mode) | ||
147 | // TODO: verify that this works | ||
148 | Apu_write_register( this, 0, 0xFF1A, b * 0x40 ); | ||
149 | unsigned i; | ||
150 | for ( i = 0; i < sizeof initial_wave [0]; i++ ) | ||
151 | Apu_write_register( this, 0, i + wave_ram, initial_wave [(mode != mode_dmg)] [i] ); | ||
152 | } | ||
153 | } | ||
154 | |||
155 | void Apu_set_tempo( struct Gb_Apu* this, int t ) | ||
156 | { | ||
157 | this->frame_period = 4194304 / 512; // 512 Hz | ||
158 | if ( t != (int)FP_ONE_TEMPO ) | ||
159 | this->frame_period = t ? (blip_time_t) ((this->frame_period * FP_ONE_TEMPO) / t) : (blip_time_t) (0); | ||
160 | } | ||
161 | |||
162 | void Apu_init( struct Gb_Apu* this ) | ||
163 | { | ||
164 | this->wave.wave_ram = &this->regs [wave_ram - io_addr]; | ||
165 | |||
166 | Synth_init( &this->synth ); | ||
167 | |||
168 | this->oscs [0] = &this->square1.osc; | ||
169 | this->oscs [1] = &this->square2.osc; | ||
170 | this->oscs [2] = &this->wave.osc; | ||
171 | this->oscs [3] = &this->noise.osc; | ||
172 | |||
173 | int i; | ||
174 | for ( i = osc_count; --i >= 0; ) | ||
175 | { | ||
176 | struct Gb_Osc* o = this->oscs [i]; | ||
177 | o->regs = &this->regs [i * 5]; | ||
178 | o->output = NULL; | ||
179 | o->outputs [0] = NULL; | ||
180 | o->outputs [1] = NULL; | ||
181 | o->outputs [2] = NULL; | ||
182 | o->outputs [3] = NULL; | ||
183 | o->synth = &this->synth; | ||
184 | } | ||
185 | |||
186 | this->reduce_clicks_ = false; | ||
187 | Apu_set_tempo( this, (int)FP_ONE_TEMPO ); | ||
188 | this->volume_ = (int)FP_ONE_VOLUME; | ||
189 | Apu_reset( this, mode_cgb, false ); | ||
190 | } | ||
191 | |||
192 | static void run_until_( struct Gb_Apu* this, blip_time_t end_time ) | ||
193 | { | ||
194 | if ( !this->frame_period ) | ||
195 | this->frame_time += end_time - this->last_time; | ||
196 | |||
197 | while ( true ) | ||
198 | { | ||
199 | // run oscillators | ||
200 | blip_time_t time = end_time; | ||
201 | if ( time > this->frame_time ) | ||
202 | time = this->frame_time; | ||
203 | |||
204 | Square_run( &this->square1, this->last_time, time ); | ||
205 | Square_run( &this->square2, this->last_time, time ); | ||
206 | Wave_run ( &this->wave, this->last_time, time ); | ||
207 | Noise_run ( &this->noise, this->last_time, time ); | ||
208 | this->last_time = time; | ||
209 | |||
210 | if ( time == end_time ) | ||
211 | break; | ||
212 | |||
213 | // run frame sequencer | ||
214 | assert( this->frame_period ); | ||
215 | this->frame_time += this->frame_period * clk_mul; | ||
216 | switch ( this->frame_phase++ ) | ||
217 | { | ||
218 | case 2: | ||
219 | case 6: | ||
220 | // 128 Hz | ||
221 | clock_sweep( &this->square1 ); | ||
222 | case 0: | ||
223 | case 4: | ||
224 | // 256 Hz | ||
225 | Osc_clock_length( &this->square1.osc ); | ||
226 | Osc_clock_length( &this->square2.osc); | ||
227 | Osc_clock_length( &this->wave.osc); | ||
228 | Osc_clock_length( &this->noise.osc); | ||
229 | break; | ||
230 | |||
231 | case 7: | ||
232 | // 64 Hz | ||
233 | this->frame_phase = 0; | ||
234 | Square_clock_envelope( &this->square1 ); | ||
235 | Square_clock_envelope( &this->square2 ); | ||
236 | Noise_clock_envelope( &this->noise ); | ||
237 | } | ||
238 | } | ||
239 | } | ||
240 | |||
241 | static inline void run_until( struct Gb_Apu* this, blip_time_t time ) | ||
242 | { | ||
243 | require( time >= this->last_time ); // end_time must not be before previous time | ||
244 | if ( time > this->last_time ) | ||
245 | run_until_( this, time ); | ||
246 | } | ||
247 | |||
248 | void Apu_end_frame( struct Gb_Apu* this, blip_time_t end_time ) | ||
249 | { | ||
250 | #ifdef LOG_FRAME | ||
251 | LOG_FRAME( end_time ); | ||
252 | #endif | ||
253 | |||
254 | if ( end_time > this->last_time ) | ||
255 | run_until( this, end_time ); | ||
256 | |||
257 | this->frame_time -= end_time; | ||
258 | assert( this->frame_time >= 0 ); | ||
259 | |||
260 | this->last_time -= end_time; | ||
261 | assert( this->last_time >= 0 ); | ||
262 | } | ||
263 | |||
264 | static void silence_osc( struct Gb_Apu* this, struct Gb_Osc* o ) | ||
265 | { | ||
266 | int delta = -o->last_amp; | ||
267 | if ( this->reduce_clicks_ ) | ||
268 | delta += o->dac_off_amp; | ||
269 | |||
270 | if ( delta ) | ||
271 | { | ||
272 | o->last_amp = o->dac_off_amp; | ||
273 | if ( o->output ) | ||
274 | { | ||
275 | Blip_set_modified( o->output ); | ||
276 | Synth_offset( &this->synth, this->last_time, delta, o->output ); | ||
277 | } | ||
278 | } | ||
279 | } | ||
280 | |||
281 | static void apply_stereo( struct Gb_Apu* this ) | ||
282 | { | ||
283 | int i; | ||
284 | for ( i = osc_count; --i >= 0; ) | ||
285 | { | ||
286 | struct Gb_Osc* o = this->oscs [i]; | ||
287 | struct Blip_Buffer* out = o->outputs [calc_output( this, i )]; | ||
288 | if ( o->output != out ) | ||
289 | { | ||
290 | silence_osc( this, o ); | ||
291 | o->output = out; | ||
292 | } | ||
293 | } | ||
294 | } | ||
295 | |||
296 | void Apu_write_register( struct Gb_Apu* this, blip_time_t time, int addr, int data ) | ||
297 | { | ||
298 | require( (unsigned) data < 0x100 ); | ||
299 | |||
300 | int reg = addr - io_addr; | ||
301 | if ( (unsigned) reg >= io_size ) | ||
302 | { | ||
303 | require( false ); | ||
304 | return; | ||
305 | } | ||
306 | |||
307 | #ifdef LOG_WRITE | ||
308 | LOG_WRITE( time, addr, data ); | ||
309 | #endif | ||
310 | |||
311 | if ( addr < status_reg && !(this->regs [status_reg - io_addr] & power_mask) ) | ||
312 | { | ||
313 | // Power is off | ||
314 | |||
315 | // length counters can only be written in DMG mode | ||
316 | if ( this->wave.osc.mode != mode_dmg || (reg != 1 && reg != 5+1 && reg != 10+1 && reg != 15+1) ) | ||
317 | return; | ||
318 | |||
319 | if ( reg < 10 ) | ||
320 | data &= 0x3F; // clear square duty | ||
321 | } | ||
322 | |||
323 | run_until( this, time ); | ||
324 | |||
325 | if ( addr >= wave_ram ) | ||
326 | { | ||
327 | Wave_write( &this->wave, addr, data ); | ||
328 | } | ||
329 | else | ||
330 | { | ||
331 | int old_data = this->regs [reg]; | ||
332 | this->regs [reg] = data; | ||
333 | |||
334 | if ( addr < vol_reg ) | ||
335 | { | ||
336 | // Oscillator | ||
337 | write_osc( this, reg, old_data, data ); | ||
338 | } | ||
339 | else if ( addr == vol_reg && data != old_data ) | ||
340 | { | ||
341 | // Master volume | ||
342 | int i; | ||
343 | for ( i = osc_count; --i >= 0; ) | ||
344 | silence_osc( this, this->oscs [i] ); | ||
345 | |||
346 | apply_volume( this ); | ||
347 | } | ||
348 | else if ( addr == stereo_reg ) | ||
349 | { | ||
350 | // Stereo panning | ||
351 | apply_stereo( this ); | ||
352 | } | ||
353 | else if ( addr == status_reg && (data ^ old_data) & power_mask ) | ||
354 | { | ||
355 | // Power control | ||
356 | this->frame_phase = 0; | ||
357 | int i; | ||
358 | for ( i = osc_count; --i >= 0; ) | ||
359 | silence_osc( this, this->oscs [i] ); | ||
360 | |||
361 | reset_regs( this ); | ||
362 | if ( this->wave.osc.mode != mode_dmg ) | ||
363 | reset_lengths( this ); | ||
364 | |||
365 | this->regs [status_reg - io_addr] = data; | ||
366 | } | ||
367 | } | ||
368 | } | ||
369 | |||
370 | int Apu_read_register( struct Gb_Apu* this, blip_time_t time, int addr ) | ||
371 | { | ||
372 | if ( addr >= status_reg ) | ||
373 | run_until( this, time ); | ||
374 | |||
375 | int reg = addr - io_addr; | ||
376 | if ( (unsigned) reg >= io_size ) | ||
377 | { | ||
378 | require( false ); | ||
379 | return 0; | ||
380 | } | ||
381 | |||
382 | if ( addr >= wave_ram ) | ||
383 | return Wave_read( &this->wave, addr ); | ||
384 | |||
385 | // Value read back has some bits always set | ||
386 | static byte const masks [] = { | ||
387 | 0x80,0x3F,0x00,0xFF,0xBF, | ||
388 | 0xFF,0x3F,0x00,0xFF,0xBF, | ||
389 | 0x7F,0xFF,0x9F,0xFF,0xBF, | ||
390 | 0xFF,0xFF,0x00,0x00,0xBF, | ||
391 | 0x00,0x00,0x70, | ||
392 | 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF | ||
393 | }; | ||
394 | int mask = masks [reg]; | ||
395 | if ( this->wave.agb_mask && (reg == 10 || reg == 12) ) | ||
396 | mask = 0x1F; // extra implemented bits in wave regs on AGB | ||
397 | int data = this->regs [reg] | mask; | ||
398 | |||
399 | // Status register | ||
400 | if ( addr == status_reg ) | ||
401 | { | ||
402 | data &= 0xF0; | ||
403 | data |= (int) this->square1.osc.enabled << 0; | ||
404 | data |= (int) this->square2.osc.enabled << 1; | ||
405 | data |= (int) this->wave .osc.enabled << 2; | ||
406 | data |= (int) this->noise .osc.enabled << 3; | ||
407 | } | ||
408 | |||
409 | return data; | ||
410 | } | ||