Mercurial > audlegacy-plugins
comparison src/console/Hes_Apu.cxx @ 316:fb513e10174e trunk
[svn] - merge libconsole-blargg into mainline libconsole:
+ obsoletes plugins-ugly:sapplug
author | nenolod |
---|---|
date | Thu, 30 Nov 2006 19:54:33 -0800 |
parents | |
children | 986f098da058 |
comparison
equal
deleted
inserted
replaced
315:2294f3a6f136 | 316:fb513e10174e |
---|---|
1 // Game_Music_Emu 0.5.1. http://www.slack.net/~ant/ | |
2 | |
3 #include "Hes_Apu.h" | |
4 | |
5 #include <string.h> | |
6 | |
7 /* Copyright (C) 2006 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 bool const center_waves = true; // reduces asymmetry and clamping when starting notes | |
21 | |
22 Hes_Apu::Hes_Apu() | |
23 { | |
24 Hes_Osc* osc = &oscs [osc_count]; | |
25 do | |
26 { | |
27 osc--; | |
28 osc->outputs [0] = 0; | |
29 osc->outputs [1] = 0; | |
30 osc->chans [0] = 0; | |
31 osc->chans [1] = 0; | |
32 osc->chans [2] = 0; | |
33 } | |
34 while ( osc != oscs ); | |
35 | |
36 reset(); | |
37 } | |
38 | |
39 void Hes_Apu::reset() | |
40 { | |
41 latch = 0; | |
42 balance = 0xFF; | |
43 | |
44 Hes_Osc* osc = &oscs [osc_count]; | |
45 do | |
46 { | |
47 osc--; | |
48 memset( osc, 0, offsetof (Hes_Osc,outputs) ); | |
49 osc->noise_lfsr = 1; | |
50 osc->control = 0x40; | |
51 osc->balance = 0xFF; | |
52 } | |
53 while ( osc != oscs ); | |
54 } | |
55 | |
56 void Hes_Apu::osc_output( int index, Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right ) | |
57 { | |
58 require( (unsigned) index < osc_count ); | |
59 oscs [index].chans [0] = center; | |
60 oscs [index].chans [1] = left; | |
61 oscs [index].chans [2] = right; | |
62 | |
63 Hes_Osc* osc = &oscs [osc_count]; | |
64 do | |
65 { | |
66 osc--; | |
67 balance_changed( *osc ); | |
68 } | |
69 while ( osc != oscs ); | |
70 } | |
71 | |
72 void Hes_Osc::run_until( synth_t& synth_, blip_time_t end_time ) | |
73 { | |
74 Blip_Buffer* const osc_outputs_0 = outputs [0]; // cache often-used values | |
75 if ( osc_outputs_0 && control & 0x80 ) | |
76 { | |
77 int dac = this->dac; | |
78 | |
79 int const volume_0 = volume [0]; | |
80 { | |
81 int delta = dac * volume_0 - last_amp [0]; | |
82 if ( delta ) | |
83 synth_.offset( last_time, delta, osc_outputs_0 ); | |
84 osc_outputs_0->set_modified(); | |
85 } | |
86 | |
87 Blip_Buffer* const osc_outputs_1 = outputs [1]; | |
88 int const volume_1 = volume [1]; | |
89 if ( osc_outputs_1 ) | |
90 { | |
91 int delta = dac * volume_1 - last_amp [1]; | |
92 if ( delta ) | |
93 synth_.offset( last_time, delta, osc_outputs_1 ); | |
94 osc_outputs_1->set_modified(); | |
95 } | |
96 | |
97 blip_time_t time = last_time + delay; | |
98 if ( time < end_time ) | |
99 { | |
100 if ( noise & 0x80 ) | |
101 { | |
102 if ( volume_0 | volume_1 ) | |
103 { | |
104 // noise | |
105 int const period = (32 - (noise & 0x1F)) * 64; // TODO: correct? | |
106 unsigned noise_lfsr = this->noise_lfsr; | |
107 do | |
108 { | |
109 int new_dac = 0x1F & -(noise_lfsr >> 1 & 1); | |
110 // Implemented using "Galios configuration" | |
111 // TODO: find correct LFSR algorithm | |
112 noise_lfsr = (noise_lfsr >> 1) ^ (0xE008 & -(noise_lfsr & 1)); | |
113 //noise_lfsr = (noise_lfsr >> 1) ^ (0x6000 & -(noise_lfsr & 1)); | |
114 int delta = new_dac - dac; | |
115 if ( delta ) | |
116 { | |
117 dac = new_dac; | |
118 synth_.offset( time, delta * volume_0, osc_outputs_0 ); | |
119 if ( osc_outputs_1 ) | |
120 synth_.offset( time, delta * volume_1, osc_outputs_1 ); | |
121 } | |
122 time += period; | |
123 } | |
124 while ( time < end_time ); | |
125 | |
126 this->noise_lfsr = noise_lfsr; | |
127 assert( noise_lfsr ); | |
128 } | |
129 } | |
130 else if ( !(control & 0x40) ) | |
131 { | |
132 // wave | |
133 int phase = (this->phase + 1) & 0x1F; // pre-advance for optimal inner loop | |
134 int period = this->period * 2; | |
135 if ( period >= 14 && (volume_0 | volume_1) ) | |
136 { | |
137 do | |
138 { | |
139 int new_dac = wave [phase]; | |
140 phase = (phase + 1) & 0x1F; | |
141 int delta = new_dac - dac; | |
142 if ( delta ) | |
143 { | |
144 dac = new_dac; | |
145 synth_.offset( time, delta * volume_0, osc_outputs_0 ); | |
146 if ( osc_outputs_1 ) | |
147 synth_.offset( time, delta * volume_1, osc_outputs_1 ); | |
148 } | |
149 time += period; | |
150 } | |
151 while ( time < end_time ); | |
152 } | |
153 else | |
154 { | |
155 if ( !period ) | |
156 { | |
157 // TODO: Gekisha Boy assumes that period = 0 silences wave | |
158 //period = 0x1000 * 2; | |
159 period = 1; | |
160 //if ( !(volume_0 | volume_1) ) | |
161 // dprintf( "Used period 0\n" ); | |
162 } | |
163 | |
164 // maintain phase when silent | |
165 blargg_long count = (end_time - time + period - 1) / period; | |
166 phase += count; // phase will be masked below | |
167 time += count * period; | |
168 } | |
169 this->phase = (phase - 1) & 0x1F; // undo pre-advance | |
170 } | |
171 } | |
172 time -= end_time; | |
173 if ( time < 0 ) | |
174 time = 0; | |
175 delay = time; | |
176 | |
177 this->dac = dac; | |
178 last_amp [0] = dac * volume_0; | |
179 last_amp [1] = dac * volume_1; | |
180 } | |
181 last_time = end_time; | |
182 } | |
183 | |
184 void Hes_Apu::balance_changed( Hes_Osc& osc ) | |
185 { | |
186 static short const log_table [32] = { // ~1.5 db per step | |
187 #define ENTRY( factor ) short (factor * Hes_Osc::amp_range / 31.0 + 0.5) | |
188 ENTRY( 0.000000 ),ENTRY( 0.005524 ),ENTRY( 0.006570 ),ENTRY( 0.007813 ), | |
189 ENTRY( 0.009291 ),ENTRY( 0.011049 ),ENTRY( 0.013139 ),ENTRY( 0.015625 ), | |
190 ENTRY( 0.018581 ),ENTRY( 0.022097 ),ENTRY( 0.026278 ),ENTRY( 0.031250 ), | |
191 ENTRY( 0.037163 ),ENTRY( 0.044194 ),ENTRY( 0.052556 ),ENTRY( 0.062500 ), | |
192 ENTRY( 0.074325 ),ENTRY( 0.088388 ),ENTRY( 0.105112 ),ENTRY( 0.125000 ), | |
193 ENTRY( 0.148651 ),ENTRY( 0.176777 ),ENTRY( 0.210224 ),ENTRY( 0.250000 ), | |
194 ENTRY( 0.297302 ),ENTRY( 0.353553 ),ENTRY( 0.420448 ),ENTRY( 0.500000 ), | |
195 ENTRY( 0.594604 ),ENTRY( 0.707107 ),ENTRY( 0.840896 ),ENTRY( 1.000000 ), | |
196 #undef ENTRY | |
197 }; | |
198 | |
199 int vol = (osc.control & 0x1F) - 0x1E * 2; | |
200 | |
201 int left = vol + (osc.balance >> 3 & 0x1E) + (balance >> 3 & 0x1E); | |
202 if ( left < 0 ) left = 0; | |
203 | |
204 int right = vol + (osc.balance << 1 & 0x1E) + (balance << 1 & 0x1E); | |
205 if ( right < 0 ) right = 0; | |
206 | |
207 left = log_table [left ]; | |
208 right = log_table [right]; | |
209 | |
210 // optimizing for the common case of being centered also allows easy | |
211 // panning using Effects_Buffer | |
212 osc.outputs [0] = osc.chans [0]; // center | |
213 osc.outputs [1] = 0; | |
214 if ( left != right ) | |
215 { | |
216 osc.outputs [0] = osc.chans [1]; // left | |
217 osc.outputs [1] = osc.chans [2]; // right | |
218 } | |
219 | |
220 if ( center_waves ) | |
221 { | |
222 osc.last_amp [0] += (left - osc.volume [0]) * 16; | |
223 osc.last_amp [1] += (right - osc.volume [1]) * 16; | |
224 } | |
225 | |
226 osc.volume [0] = left; | |
227 osc.volume [1] = right; | |
228 } | |
229 | |
230 void Hes_Apu::write_data( blip_time_t time, int addr, int data ) | |
231 { | |
232 if ( addr == 0x800 ) | |
233 { | |
234 latch = data & 7; | |
235 } | |
236 else if ( addr == 0x801 ) | |
237 { | |
238 if ( balance != data ) | |
239 { | |
240 balance = data; | |
241 | |
242 Hes_Osc* osc = &oscs [osc_count]; | |
243 do | |
244 { | |
245 osc--; | |
246 osc->run_until( synth, time ); | |
247 balance_changed( *oscs ); | |
248 } | |
249 while ( osc != oscs ); | |
250 } | |
251 } | |
252 else if ( latch < osc_count ) | |
253 { | |
254 Hes_Osc& osc = oscs [latch]; | |
255 osc.run_until( synth, time ); | |
256 switch ( addr ) | |
257 { | |
258 case 0x802: | |
259 osc.period = (osc.period & 0xF00) | data; | |
260 break; | |
261 | |
262 case 0x803: | |
263 osc.period = (osc.period & 0x0FF) | ((data & 0x0F) << 8); | |
264 break; | |
265 | |
266 case 0x804: | |
267 if ( osc.control & 0x40 & ~data ) | |
268 osc.phase = 0; | |
269 osc.control = data; | |
270 balance_changed( osc ); | |
271 break; | |
272 | |
273 case 0x805: | |
274 osc.balance = data; | |
275 balance_changed( osc ); | |
276 break; | |
277 | |
278 case 0x806: | |
279 data &= 0x1F; | |
280 if ( !(osc.control & 0x40) ) | |
281 { | |
282 osc.wave [osc.phase] = data; | |
283 osc.phase = (osc.phase + 1) & 0x1F; | |
284 } | |
285 else if ( osc.control & 0x80 ) | |
286 { | |
287 osc.dac = data; | |
288 } | |
289 break; | |
290 | |
291 case 0x807: | |
292 if ( &osc >= &oscs [4] ) | |
293 osc.noise = data; | |
294 break; | |
295 | |
296 case 0x809: | |
297 if ( !(data & 0x80) && (data & 0x03) != 0 ) | |
298 dprintf( "HES LFO not supported\n" ); | |
299 } | |
300 } | |
301 } | |
302 | |
303 void Hes_Apu::end_frame( blip_time_t end_time ) | |
304 { | |
305 Hes_Osc* osc = &oscs [osc_count]; | |
306 do | |
307 { | |
308 osc--; | |
309 if ( end_time > osc->last_time ) | |
310 osc->run_until( synth, end_time ); | |
311 assert( osc->last_time >= end_time ); | |
312 osc->last_time -= end_time; | |
313 } | |
314 while ( osc != oscs ); | |
315 } |