Mercurial > audlegacy-plugins
comparison src/Input/console/Gb_Apu.cxx @ 0:13389e613d67 trunk
[svn] - initial import of audacious-plugins tree (lots to do)
author | nenolod |
---|---|
date | Mon, 18 Sep 2006 01:11:49 -0700 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:13389e613d67 |
---|---|
1 | |
2 // Gb_Snd_Emu 0.1.4. http://www.slack.net/~ant/ | |
3 | |
4 #include "Gb_Apu.h" | |
5 | |
6 #include <string.h> | |
7 | |
8 /* Copyright (C) 2003-2006 Shay Green. This module is free software; you | |
9 can redistribute it and/or modify it under the terms of the GNU Lesser | |
10 General Public License as published by the Free Software Foundation; either | |
11 version 2.1 of the License, or (at your option) any later version. This | |
12 module is distributed in the hope that it will be useful, but WITHOUT ANY | |
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS | |
14 FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for | |
15 more details. You should have received a copy of the GNU Lesser General | |
16 Public License along with this module; if not, write to the Free Software | |
17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ | |
18 | |
19 #include BLARGG_SOURCE_BEGIN | |
20 | |
21 const unsigned vol_reg = 0xFF24; | |
22 const unsigned status_reg = 0xFF26; | |
23 | |
24 Gb_Apu::Gb_Apu() | |
25 { | |
26 square1.synth = &square_synth; | |
27 square2.synth = &square_synth; | |
28 wave.synth = &other_synth; | |
29 noise.synth = &other_synth; | |
30 | |
31 oscs [0] = &square1; | |
32 oscs [1] = &square2; | |
33 oscs [2] = &wave; | |
34 oscs [3] = &noise; | |
35 | |
36 for ( int i = 0; i < osc_count; i++ ) | |
37 { | |
38 Gb_Osc& osc = *oscs [i]; | |
39 osc.regs = ®s [i * 5]; | |
40 osc.output = NULL; | |
41 osc.outputs [0] = NULL; | |
42 osc.outputs [1] = NULL; | |
43 osc.outputs [2] = NULL; | |
44 osc.outputs [3] = NULL; | |
45 } | |
46 | |
47 volume( 1.0 ); | |
48 reset(); | |
49 } | |
50 | |
51 Gb_Apu::~Gb_Apu() | |
52 { | |
53 } | |
54 | |
55 void Gb_Apu::treble_eq( const blip_eq_t& eq ) | |
56 { | |
57 square_synth.treble_eq( eq ); | |
58 other_synth.treble_eq( eq ); | |
59 } | |
60 | |
61 void Gb_Apu::osc_output( int index, Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right ) | |
62 { | |
63 require( (unsigned) index < osc_count ); | |
64 require( (center && left && right) || (!center && !left && !right) ); | |
65 Gb_Osc& osc = *oscs [index]; | |
66 osc.outputs [1] = right; | |
67 osc.outputs [2] = left; | |
68 osc.outputs [3] = center; | |
69 osc.output = osc.outputs [osc.output_select]; | |
70 } | |
71 | |
72 void Gb_Apu::output( Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right ) | |
73 { | |
74 for ( int i = 0; i < osc_count; i++ ) | |
75 osc_output( i, center, left, right ); | |
76 } | |
77 | |
78 void Gb_Apu::update_volume() | |
79 { | |
80 // to do: doesn't handle differing left/right global volume | |
81 int data = regs [vol_reg - start_addr]; | |
82 double vol = (max( data & 7, data >> 4 & 7 ) + 1) * volume_unit; | |
83 square_synth.volume( vol ); | |
84 other_synth.volume( vol ); | |
85 } | |
86 | |
87 static unsigned char const powerup_regs [0x30] = { | |
88 0x80,0x3F,0x00,0xFF,0xBF, // square 1 | |
89 0xFF,0x3F,0x00,0xFF,0xBF, // square 2 | |
90 0x7F,0xFF,0x9F,0xFF,0xBF, // wave | |
91 0xFF,0xFF,0x00,0x00,0xBF, // noise | |
92 0x00, // left/right enables | |
93 0x77, // master volume | |
94 0x80, // power | |
95 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, | |
96 0x84,0x40,0x43,0xAA,0x2D,0x78,0x92,0x3C, // wave table | |
97 0x60,0x59,0x59,0xB0,0x34,0xB8,0x2E,0xDA | |
98 }; | |
99 | |
100 void Gb_Apu::reset() | |
101 { | |
102 next_frame_time = 0; | |
103 last_time = 0; | |
104 frame_count = 0; | |
105 stereo_found = false; | |
106 | |
107 square1.reset(); | |
108 square2.reset(); | |
109 wave.reset(); | |
110 noise.reset(); | |
111 noise.bits = 1; | |
112 wave.wave_pos = 0; | |
113 | |
114 // avoid click at beginning | |
115 regs [vol_reg - start_addr] = 0x77; | |
116 update_volume(); | |
117 | |
118 regs [status_reg - start_addr] = 0x01; // force power | |
119 write_register( 0, status_reg, 0x00 ); | |
120 } | |
121 | |
122 // to do: remove | |
123 static unsigned long abs_time; | |
124 | |
125 void Gb_Apu::run_until( gb_time_t end_time ) | |
126 { | |
127 require( end_time >= last_time ); // end_time must not be before previous time | |
128 if ( end_time == last_time ) | |
129 return; | |
130 | |
131 while ( true ) | |
132 { | |
133 gb_time_t time = next_frame_time; | |
134 if ( time > end_time ) | |
135 time = end_time; | |
136 | |
137 // run oscillators | |
138 for ( int i = 0; i < osc_count; ++i ) | |
139 { | |
140 Gb_Osc& osc = *oscs [i]; | |
141 if ( osc.output ) | |
142 { | |
143 int playing = false; | |
144 if ( osc.enabled && osc.volume && | |
145 (!(osc.regs [4] & osc.len_enabled_mask) || osc.length) ) | |
146 playing = -1; | |
147 if ( osc.output != osc.outputs [3] ) | |
148 stereo_found = true; | |
149 switch ( i ) | |
150 { | |
151 case 0: square1.run( last_time, time, playing ); break; | |
152 case 1: square2.run( last_time, time, playing ); break; | |
153 case 2: wave .run( last_time, time, playing ); break; | |
154 case 3: noise .run( last_time, time, playing ); break; | |
155 } | |
156 } | |
157 } | |
158 last_time = time; | |
159 | |
160 if ( time == end_time ) | |
161 break; | |
162 | |
163 next_frame_time += 4194304 / 256; // 256 Hz | |
164 | |
165 // 256 Hz actions | |
166 square1.clock_length(); | |
167 square2.clock_length(); | |
168 wave.clock_length(); | |
169 noise.clock_length(); | |
170 | |
171 frame_count = (frame_count + 1) & 3; | |
172 if ( frame_count == 0 ) | |
173 { | |
174 // 64 Hz actions | |
175 square1.clock_envelope(); | |
176 square2.clock_envelope(); | |
177 noise.clock_envelope(); | |
178 } | |
179 | |
180 if ( frame_count & 1 ) | |
181 square1.clock_sweep(); // 128 Hz action | |
182 } | |
183 } | |
184 | |
185 bool Gb_Apu::end_frame( gb_time_t end_time ) | |
186 { | |
187 if ( end_time > last_time ) | |
188 run_until( end_time ); | |
189 | |
190 abs_time += end_time; | |
191 | |
192 assert( next_frame_time >= end_time ); | |
193 next_frame_time -= end_time; | |
194 | |
195 assert( last_time >= end_time ); | |
196 last_time -= end_time; | |
197 | |
198 bool result = stereo_found; | |
199 stereo_found = false; | |
200 return result; | |
201 } | |
202 | |
203 void Gb_Apu::write_register( gb_time_t time, gb_addr_t addr, int data ) | |
204 { | |
205 require( (unsigned) data < 0x100 ); | |
206 | |
207 int reg = addr - start_addr; | |
208 if ( (unsigned) reg >= register_count ) | |
209 return; | |
210 | |
211 run_until( time ); | |
212 | |
213 int old_reg = regs [reg]; | |
214 regs [reg] = data; | |
215 | |
216 if ( addr < vol_reg ) | |
217 { | |
218 write_osc( reg / 5, reg, data ); | |
219 } | |
220 else if ( addr == vol_reg && data != old_reg ) // global volume | |
221 { | |
222 // return all oscs to 0 | |
223 for ( int i = 0; i < osc_count; i++ ) | |
224 { | |
225 Gb_Osc& osc = *oscs [i]; | |
226 int amp = osc.last_amp; | |
227 osc.last_amp = 0; | |
228 if ( amp && osc.enabled && osc.output ) | |
229 other_synth.offset( time, -amp, osc.output ); | |
230 } | |
231 | |
232 if ( wave.outputs [3] ) | |
233 other_synth.offset( time, 30, wave.outputs [3] ); | |
234 | |
235 update_volume(); | |
236 | |
237 if ( wave.outputs [3] ) | |
238 other_synth.offset( time, -30, wave.outputs [3] ); | |
239 | |
240 // oscs will update with new amplitude when next run | |
241 } | |
242 else if ( addr == 0xFF25 || addr == status_reg ) | |
243 { | |
244 int mask = (regs [status_reg - start_addr] & 0x80) ? ~0 : 0; | |
245 int flags = regs [0xFF25 - start_addr] & mask; | |
246 | |
247 // left/right assignments | |
248 for ( int i = 0; i < osc_count; i++ ) | |
249 { | |
250 Gb_Osc& osc = *oscs [i]; | |
251 osc.enabled &= mask; | |
252 int bits = flags >> i; | |
253 Blip_Buffer* old_output = osc.output; | |
254 osc.output_select = (bits >> 3 & 2) | (bits & 1); | |
255 osc.output = osc.outputs [osc.output_select]; | |
256 if ( osc.output != old_output ) | |
257 { | |
258 int amp = osc.last_amp; | |
259 osc.last_amp = 0; | |
260 if ( amp && old_output ) | |
261 other_synth.offset( time, -amp, old_output ); | |
262 } | |
263 } | |
264 | |
265 if ( addr == status_reg && data != old_reg ) | |
266 { | |
267 if ( !(data & 0x80) ) | |
268 { | |
269 for ( int i = 0; i < (int) sizeof powerup_regs; i++ ) | |
270 { | |
271 if ( i != status_reg - start_addr ) | |
272 write_register( time, i + start_addr, powerup_regs [i] ); | |
273 } | |
274 } | |
275 else | |
276 { | |
277 //dprintf( "APU powered on\n" ); | |
278 } | |
279 } | |
280 } | |
281 else if ( addr >= 0xFF30 ) | |
282 { | |
283 | |
284 int index = (addr & 0x0F) * 2; | |
285 wave.wave [index] = data >> 4; | |
286 wave.wave [index + 1] = data & 0x0F; | |
287 } | |
288 } | |
289 | |
290 int Gb_Apu::read_register( gb_time_t time, gb_addr_t addr ) | |
291 { | |
292 run_until( time ); | |
293 | |
294 int index = addr - start_addr; | |
295 require( (unsigned) index < register_count ); | |
296 int data = regs [index]; | |
297 | |
298 if ( addr == status_reg ) | |
299 { | |
300 data = (data & 0x80) | 0x70; | |
301 for ( int i = 0; i < osc_count; i++ ) | |
302 { | |
303 const Gb_Osc& osc = *oscs [i]; | |
304 if ( osc.enabled && (osc.length || !(osc.regs [4] & osc.len_enabled_mask)) ) | |
305 data |= 1 << i; | |
306 } | |
307 } | |
308 | |
309 return data; | |
310 } | |
311 |