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 }