diff options
Diffstat (limited to 'apps/codecs/libgme/hes_apu.c')
-rw-r--r-- | apps/codecs/libgme/hes_apu.c | 306 |
1 files changed, 181 insertions, 125 deletions
diff --git a/apps/codecs/libgme/hes_apu.c b/apps/codecs/libgme/hes_apu.c index 2a831426f5..054b164a9a 100644 --- a/apps/codecs/libgme/hes_apu.c +++ b/apps/codecs/libgme/hes_apu.c | |||
@@ -18,8 +18,7 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ | |||
18 | 18 | ||
19 | enum { center_waves = 1 }; // reduces asymmetry and clamping when starting notes | 19 | enum { center_waves = 1 }; // reduces asymmetry and clamping when starting notes |
20 | 20 | ||
21 | static void Apu_balance_changed( struct Hes_Apu* this, struct Hes_Osc* osc ); | 21 | static void balance_changed( struct Hes_Apu* this, struct Hes_Osc* osc ) |
22 | static void Apu_balance_changed( struct Hes_Apu* this, struct Hes_Osc* osc ) | ||
23 | { | 22 | { |
24 | static short const log_table [32] = { // ~1.5 db per step | 23 | static short const log_table [32] = { // ~1.5 db per step |
25 | #define ENTRY( factor ) (short) (factor * amp_range / 31.0 + 0.5) | 24 | #define ENTRY( factor ) (short) (factor * amp_range / 31.0 + 0.5) |
@@ -42,27 +41,40 @@ static void Apu_balance_changed( struct Hes_Apu* this, struct Hes_Osc* osc ) | |||
42 | int right = vol + (osc->balance << 1 & 0x1E) + (this->balance << 1 & 0x1E); | 41 | int right = vol + (osc->balance << 1 & 0x1E) + (this->balance << 1 & 0x1E); |
43 | if ( right < 0 ) right = 0; | 42 | if ( right < 0 ) right = 0; |
44 | 43 | ||
45 | left = log_table [left ]; | ||
46 | right = log_table [right]; | ||
47 | |||
48 | // optimizing for the common case of being centered also allows easy | 44 | // optimizing for the common case of being centered also allows easy |
49 | // panning using Effects_Buffer | 45 | // panning using Effects_Buffer |
50 | osc->outputs [0] = osc->chans [0]; // center | 46 | |
51 | osc->outputs [1] = 0; | 47 | // Separate balance into center volume and additional on either left or right |
52 | if ( left != right ) | 48 | osc->output [0] = osc->outputs [0]; // center |
49 | osc->output [1] = osc->outputs [2]; // right | ||
50 | int base = log_table [left ]; | ||
51 | int side = log_table [right] - base; | ||
52 | if ( side < 0 ) | ||
53 | { | ||
54 | base += side; | ||
55 | side = -side; | ||
56 | osc->output [1] = osc->outputs [1]; // left | ||
57 | } | ||
58 | |||
59 | // Optimize when output is far left, center, or far right | ||
60 | if ( !base || osc->output [0] == osc->output [1] ) | ||
53 | { | 61 | { |
54 | osc->outputs [0] = osc->chans [1]; // left | 62 | base += side; |
55 | osc->outputs [1] = osc->chans [2]; // right | 63 | side = 0; |
64 | osc->output [0] = osc->output [1]; | ||
65 | osc->output [1] = NULL; | ||
66 | osc->last_amp [1] = 0; | ||
56 | } | 67 | } |
57 | 68 | ||
58 | if ( center_waves ) | 69 | if ( center_waves ) |
59 | { | 70 | { |
60 | osc->last_amp [0] += (left - osc->volume [0]) * 16; | 71 | // TODO: this can leave a non-zero level in a buffer (minor) |
61 | osc->last_amp [1] += (right - osc->volume [1]) * 16; | 72 | osc->last_amp [0] += (base - osc->volume [0]) * 16; |
73 | osc->last_amp [1] += (side - osc->volume [1]) * 16; | ||
62 | } | 74 | } |
63 | 75 | ||
64 | osc->volume [0] = left; | 76 | osc->volume [0] = base; |
65 | osc->volume [1] = right; | 77 | osc->volume [1] = side; |
66 | } | 78 | } |
67 | 79 | ||
68 | void Apu_init( struct Hes_Apu* this ) | 80 | void Apu_init( struct Hes_Apu* this ) |
@@ -71,11 +83,11 @@ void Apu_init( struct Hes_Apu* this ) | |||
71 | do | 83 | do |
72 | { | 84 | { |
73 | osc--; | 85 | osc--; |
74 | osc->outputs [0] = 0; | 86 | osc->output [0] = NULL; |
75 | osc->outputs [1] = 0; | 87 | osc->output [1] = NULL; |
76 | osc->chans [0] = 0; | 88 | osc->outputs [0] = NULL; |
77 | osc->chans [1] = 0; | 89 | osc->outputs [1] = NULL; |
78 | osc->chans [2] = 0; | 90 | osc->outputs [2] = NULL; |
79 | } | 91 | } |
80 | while ( osc != this->oscs ); | 92 | while ( osc != this->oscs ); |
81 | 93 | ||
@@ -92,139 +104,183 @@ void Apu_reset( struct Hes_Apu* this ) | |||
92 | { | 104 | { |
93 | osc--; | 105 | osc--; |
94 | memset( osc, 0, offsetof (struct Hes_Osc,outputs) ); | 106 | memset( osc, 0, offsetof (struct Hes_Osc,outputs) ); |
95 | osc->noise_lfsr = 1; | 107 | osc->lfsr = 1; |
96 | osc->control = 0x40; | 108 | osc->control = 0x40; |
97 | osc->balance = 0xFF; | 109 | osc->balance = 0xFF; |
98 | } | 110 | } |
99 | while ( osc != this->oscs ); | 111 | while ( osc != this->oscs ); |
112 | |||
113 | // Only last two oscs support noise | ||
114 | this->oscs [osc_count - 2].lfsr = 0x200C3; // equivalent to 1 in Fibonacci LFSR | ||
115 | this->oscs [osc_count - 1].lfsr = 0x200C3; | ||
100 | } | 116 | } |
101 | 117 | ||
102 | void Apu_osc_output( struct Hes_Apu* this, int index, struct Blip_Buffer* center, struct Blip_Buffer* left, struct Blip_Buffer* right ) | 118 | void Apu_osc_output( struct Hes_Apu* this, int i, struct Blip_Buffer* center, struct Blip_Buffer* left, struct Blip_Buffer* right ) |
103 | { | 119 | { |
104 | require( (unsigned) index < osc_count ); | 120 | // Must be silent (all NULL), mono (left and right NULL), or stereo (none NULL) |
105 | this->oscs [index].chans [0] = center; | 121 | require( !center || (center && !left && !right) || (center && left && right) ); |
106 | this->oscs [index].chans [1] = left; | 122 | require( (unsigned) i < osc_count ); // fails if you pass invalid osc index |
107 | this->oscs [index].chans [2] = right; | ||
108 | 123 | ||
109 | struct Hes_Osc* osc = &this->oscs [osc_count]; | 124 | if ( !center || !left || !right ) |
110 | do | ||
111 | { | 125 | { |
112 | osc--; | 126 | left = center; |
113 | Apu_balance_changed( this, osc ); | 127 | right = center; |
114 | } | 128 | } |
115 | while ( osc != this->oscs ); | 129 | |
130 | struct Hes_Osc* o = &this->oscs [i]; | ||
131 | o->outputs [0] = center; | ||
132 | o->outputs [1] = right; | ||
133 | o->outputs [2] = left; | ||
134 | balance_changed( this, o ); | ||
116 | } | 135 | } |
117 | 136 | ||
118 | void Osc_run_until( struct Hes_Osc* this, struct Blip_Synth* synth_, blip_time_t end_time ) | 137 | void run_osc( struct Hes_Osc* o, struct Blip_Synth* syn, blip_time_t end_time ) |
119 | { | 138 | { |
120 | struct Blip_Buffer* const osc_outputs_0 = this->outputs [0]; // cache often-used values | 139 | int vol0 = o->volume [0]; |
121 | if ( osc_outputs_0 && this->control & 0x80 ) | 140 | int vol1 = o->volume [1]; |
141 | int dac = o->dac; | ||
142 | |||
143 | struct Blip_Buffer* out0 = o->output [0]; // cache often-used values | ||
144 | struct Blip_Buffer* out1 = o->output [1]; | ||
145 | if ( !(o->control & 0x80) ) | ||
146 | out0 = NULL; | ||
147 | |||
148 | if ( out0 ) | ||
122 | { | 149 | { |
123 | int dac = this->dac; | 150 | // Update amplitudes |
124 | 151 | if ( out1 ) | |
125 | int const volume_0 = this->volume [0]; | ||
126 | { | 152 | { |
127 | int delta = dac * volume_0 - this->last_amp [0]; | 153 | int delta = dac * vol1 - o->last_amp [1]; |
128 | if ( delta ) | 154 | if ( delta ) |
129 | Synth_offset( synth_, this->last_time, delta, osc_outputs_0 ); | 155 | { |
130 | Blip_set_modified( osc_outputs_0 ); | 156 | Synth_offset( syn, o->last_time, delta, out1 ); |
157 | Blip_set_modified( out1 ); | ||
158 | } | ||
131 | } | 159 | } |
132 | 160 | int delta = dac * vol0 - o->last_amp [0]; | |
133 | struct Blip_Buffer* const osc_outputs_1 = this->outputs [1]; | 161 | if ( delta ) |
134 | int const volume_1 = this->volume [1]; | ||
135 | if ( osc_outputs_1 ) | ||
136 | { | 162 | { |
137 | int delta = dac * volume_1 - this->last_amp [1]; | 163 | Synth_offset( syn, o->last_time, delta, out0 ); |
138 | if ( delta ) | 164 | Blip_set_modified( out0 ); |
139 | Synth_offset( synth_, this->last_time, delta, osc_outputs_1 ); | ||
140 | Blip_set_modified( osc_outputs_1 ); | ||
141 | } | 165 | } |
142 | 166 | ||
143 | blip_time_t time = this->last_time + this->delay; | 167 | // Don't generate if silent |
168 | if ( !(vol0 | vol1) ) | ||
169 | out0 = NULL; | ||
170 | } | ||
171 | |||
172 | // Generate noise | ||
173 | int noise = 0; | ||
174 | if ( o->lfsr ) | ||
175 | { | ||
176 | noise = o->noise & 0x80; | ||
177 | |||
178 | blip_time_t time = o->last_time + o->noise_delay; | ||
144 | if ( time < end_time ) | 179 | if ( time < end_time ) |
145 | { | 180 | { |
146 | if ( this->noise & 0x80 ) | 181 | int period = (~o->noise & 0x1F) * 128; |
182 | if ( !period ) | ||
183 | period = 64; | ||
184 | |||
185 | if ( noise && out0 ) | ||
147 | { | 186 | { |
148 | if ( volume_0 | volume_1 ) | 187 | unsigned lfsr = o->lfsr; |
188 | do | ||
149 | { | 189 | { |
150 | // noise | 190 | int new_dac = -(lfsr & 1); |
151 | int const period = (32 - (this->noise & 0x1F)) * 64; // TODO: correct? | 191 | lfsr = (lfsr >> 1) ^ (0x30061 & new_dac); |
152 | unsigned noise_lfsr = this->noise_lfsr; | 192 | |
153 | do | 193 | int delta = (new_dac &= 0x1F) - dac; |
194 | if ( delta ) | ||
154 | { | 195 | { |
155 | int new_dac = 0x1F & -(noise_lfsr >> 1 & 1); | 196 | dac = new_dac; |
156 | // Implemented using "Galios configuration" | 197 | Synth_offset( syn, time, delta * vol0, out0 ); |
157 | // TODO: find correct LFSR algorithm | 198 | if ( out1 ) |
158 | noise_lfsr = (noise_lfsr >> 1) ^ (0xE008 & -(noise_lfsr & 1)); | 199 | Synth_offset( syn, time, delta * vol1, out1 ); |
159 | //noise_lfsr = (noise_lfsr >> 1) ^ (0x6000 & -(noise_lfsr & 1)); | ||
160 | int delta = new_dac - dac; | ||
161 | if ( delta ) | ||
162 | { | ||
163 | dac = new_dac; | ||
164 | Synth_offset( synth_, time, delta * volume_0, osc_outputs_0 ); | ||
165 | if ( osc_outputs_1 ) | ||
166 | Synth_offset( synth_, time, delta * volume_1, osc_outputs_1 ); | ||
167 | } | ||
168 | time += period; | ||
169 | } | 200 | } |
170 | while ( time < end_time ); | 201 | time += period; |
171 | |||
172 | this->noise_lfsr = noise_lfsr; | ||
173 | assert( noise_lfsr ); | ||
174 | } | 202 | } |
175 | } | 203 | while ( time < end_time ); |
176 | else if ( !(this->control & 0x40) ) | 204 | |
177 | { | 205 | if ( !lfsr ) |
178 | // wave | ||
179 | int phase = (this->phase + 1) & 0x1F; // pre-advance for optimal inner loop | ||
180 | int period = this->period * 2; | ||
181 | if ( period >= 14 && (volume_0 | volume_1) ) | ||
182 | { | 206 | { |
183 | do | 207 | lfsr = 1; |
184 | { | 208 | check( false ); |
185 | int new_dac = this->wave [phase]; | ||
186 | phase = (phase + 1) & 0x1F; | ||
187 | int delta = new_dac - dac; | ||
188 | if ( delta ) | ||
189 | { | ||
190 | dac = new_dac; | ||
191 | Synth_offset( synth_, time, delta * volume_0, osc_outputs_0 ); | ||
192 | if ( osc_outputs_1 ) | ||
193 | Synth_offset( synth_, time, delta * volume_1, osc_outputs_1 ); | ||
194 | } | ||
195 | time += period; | ||
196 | } | ||
197 | while ( time < end_time ); | ||
198 | } | 209 | } |
199 | else | 210 | o->lfsr = lfsr; |
211 | |||
212 | Blip_set_modified( out0 ); | ||
213 | if ( out1 ) | ||
214 | Blip_set_modified( out1 ); | ||
215 | } | ||
216 | else | ||
217 | { | ||
218 | // Maintain phase when silent | ||
219 | int count = (end_time - time + period - 1) / period; | ||
220 | time += count * period; | ||
221 | |||
222 | // not worth it | ||
223 | //while ( count-- ) | ||
224 | // o->lfsr = (o->lfsr >> 1) ^ (0x30061 * (o->lfsr & 1)); | ||
225 | } | ||
226 | } | ||
227 | o->noise_delay = time - end_time; | ||
228 | } | ||
229 | |||
230 | // Generate wave | ||
231 | blip_time_t time = o->last_time + o->delay; | ||
232 | if ( time < end_time ) | ||
233 | { | ||
234 | int phase = (o->phase + 1) & 0x1F; // pre-advance for optimal inner loop | ||
235 | int period = o->period * 2; | ||
236 | |||
237 | if ( period >= 14 && out0 && !((o->control & 0x40) | noise) ) | ||
238 | { | ||
239 | do | ||
240 | { | ||
241 | int new_dac = o->wave [phase]; | ||
242 | phase = (phase + 1) & 0x1F; | ||
243 | int delta = new_dac - dac; | ||
244 | if ( delta ) | ||
200 | { | 245 | { |
201 | if ( !period ) | 246 | dac = new_dac; |
202 | { | 247 | Synth_offset( syn, time, delta * vol0, out0 ); |
203 | // TODO: Gekisha Boy assumes that period = 0 silences wave | 248 | if ( out1 ) |
204 | //period = 0x1000 * 2; | 249 | Synth_offset( syn, time, delta * vol1, out1 ); |
205 | period = 1; | ||
206 | //if ( !(volume_0 | volume_1) ) | ||
207 | // dprintf( "Used period 0\n" ); | ||
208 | } | ||
209 | |||
210 | // maintain phase when silent | ||
211 | blargg_long count = (end_time - time + period - 1) / period; | ||
212 | phase += count; // phase will be masked below | ||
213 | time += count * period; | ||
214 | } | 250 | } |
215 | this->phase = (phase - 1) & 0x1F; // undo pre-advance | 251 | time += period; |
216 | } | 252 | } |
253 | while ( time < end_time ); | ||
254 | Blip_set_modified( out0 ); | ||
255 | if ( out1 ) | ||
256 | Blip_set_modified( out1 ); | ||
257 | } | ||
258 | else | ||
259 | { | ||
260 | // Maintain phase when silent | ||
261 | int count = end_time - time; | ||
262 | if ( !period ) | ||
263 | period = 1; | ||
264 | count = (count + period - 1) / period; | ||
265 | |||
266 | phase += count; // phase will be masked below | ||
267 | time += count * period; | ||
217 | } | 268 | } |
218 | time -= end_time; | ||
219 | if ( time < 0 ) | ||
220 | time = 0; | ||
221 | this->delay = time; | ||
222 | 269 | ||
223 | this->dac = dac; | 270 | // TODO: Find whether phase increments even when both volumes are zero. |
224 | this->last_amp [0] = dac * volume_0; | 271 | // CAN'T simply check for out0 being non-NULL, since it could be NULL |
225 | this->last_amp [1] = dac * volume_1; | 272 | // if channel is muted in player, but still has non-zero volume. |
273 | // City Hunter breaks when this check is removed. | ||
274 | if ( !(o->control & 0x40) && (vol0 | vol1) ) | ||
275 | o->phase = (phase - 1) & 0x1F; // undo pre-advance | ||
226 | } | 276 | } |
227 | this->last_time = end_time; | 277 | o->delay = time - end_time; |
278 | check( o->delay >= 0 ); | ||
279 | |||
280 | o->last_time = end_time; | ||
281 | o->dac = dac; | ||
282 | o->last_amp [0] = dac * vol0; | ||
283 | o->last_amp [1] = dac * vol1; | ||
228 | } | 284 | } |
229 | 285 | ||
230 | void Apu_write_data( struct Hes_Apu* this, blip_time_t time, int addr, int data ) | 286 | void Apu_write_data( struct Hes_Apu* this, blip_time_t time, int addr, int data ) |
@@ -243,8 +299,8 @@ void Apu_write_data( struct Hes_Apu* this, blip_time_t time, int addr, int data | |||
243 | do | 299 | do |
244 | { | 300 | { |
245 | osc--; | 301 | osc--; |
246 | Osc_run_until( osc, &this->synth, time ); | 302 | run_osc( osc, &this->synth, time ); |
247 | Apu_balance_changed( this, this->oscs ); | 303 | balance_changed( this, this->oscs ); |
248 | } | 304 | } |
249 | while ( osc != this->oscs ); | 305 | while ( osc != this->oscs ); |
250 | } | 306 | } |
@@ -252,7 +308,7 @@ void Apu_write_data( struct Hes_Apu* this, blip_time_t time, int addr, int data | |||
252 | else if ( this->latch < osc_count ) | 308 | else if ( this->latch < osc_count ) |
253 | { | 309 | { |
254 | struct Hes_Osc* osc = &this->oscs [this->latch]; | 310 | struct Hes_Osc* osc = &this->oscs [this->latch]; |
255 | Osc_run_until( osc, &this->synth, time ); | 311 | run_osc( osc, &this->synth, time ); |
256 | switch ( addr ) | 312 | switch ( addr ) |
257 | { | 313 | { |
258 | case 0x802: | 314 | case 0x802: |
@@ -267,12 +323,12 @@ void Apu_write_data( struct Hes_Apu* this, blip_time_t time, int addr, int data | |||
267 | if ( osc->control & 0x40 & ~data ) | 323 | if ( osc->control & 0x40 & ~data ) |
268 | osc->phase = 0; | 324 | osc->phase = 0; |
269 | osc->control = data; | 325 | osc->control = data; |
270 | Apu_balance_changed( this, osc ); | 326 | balance_changed( this, osc ); |
271 | break; | 327 | break; |
272 | 328 | ||
273 | case 0x805: | 329 | case 0x805: |
274 | osc->balance = data; | 330 | osc->balance = data; |
275 | Apu_balance_changed( this, osc ); | 331 | balance_changed( this, osc ); |
276 | break; | 332 | break; |
277 | 333 | ||
278 | case 0x806: | 334 | case 0x806: |
@@ -289,9 +345,9 @@ void Apu_write_data( struct Hes_Apu* this, blip_time_t time, int addr, int data | |||
289 | break; | 345 | break; |
290 | 346 | ||
291 | case 0x807: | 347 | case 0x807: |
292 | if ( osc >= &this->oscs [4] ) | 348 | osc->noise = data; |
293 | osc->noise = data; | ||
294 | break; | 349 | break; |
350 | |||
295 | case 0x809: | 351 | case 0x809: |
296 | if ( !(data & 0x80) && (data & 0x03) != 0 ) { | 352 | if ( !(data & 0x80) && (data & 0x03) != 0 ) { |
297 | dprintf( "HES LFO not supported\n" ); | 353 | dprintf( "HES LFO not supported\n" ); |
@@ -307,7 +363,7 @@ void Apu_end_frame( struct Hes_Apu* this, blip_time_t end_time ) | |||
307 | { | 363 | { |
308 | osc--; | 364 | osc--; |
309 | if ( end_time > osc->last_time ) | 365 | if ( end_time > osc->last_time ) |
310 | Osc_run_until( osc, &this->synth, end_time ); | 366 | run_osc( osc, &this->synth, end_time ); |
311 | assert( osc->last_time >= end_time ); | 367 | assert( osc->last_time >= end_time ); |
312 | osc->last_time -= end_time; | 368 | osc->last_time -= end_time; |
313 | } | 369 | } |