/src/vlc/contrib/contrib-build/game-music-emu/gme/Nes_Apu.cpp
Line | Count | Source |
1 | | // Nes_Snd_Emu 0.1.8. http://www.slack.net/~ant/ |
2 | | |
3 | | #include "Nes_Apu.h" |
4 | | |
5 | | /* Copyright (C) 2003-2006 Shay Green. This module is free software; you |
6 | | can redistribute it and/or modify it under the terms of the GNU Lesser |
7 | | General Public License as published by the Free Software Foundation; either |
8 | | version 2.1 of the License, or (at your option) any later version. This |
9 | | module is distributed in the hope that it will be useful, but WITHOUT ANY |
10 | | WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
11 | | FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more |
12 | | details. You should have received a copy of the GNU Lesser General Public |
13 | | License along with this module; if not, write to the Free Software Foundation, |
14 | | Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ |
15 | | |
16 | | #include "blargg_source.h" |
17 | | |
18 | | static int const amp_range = 15; |
19 | | |
20 | | Nes_Apu::Nes_Apu() : |
21 | 1.38k | square1( &square_synth ), |
22 | 1.38k | square2( &square_synth ) |
23 | 1.38k | { |
24 | 1.38k | tempo_ = 1.0; |
25 | 1.38k | dmc.apu = this; |
26 | 1.38k | dmc.prg_reader = NULL; |
27 | 1.38k | irq_notifier_ = NULL; |
28 | | |
29 | 1.38k | oscs [0] = &square1; |
30 | 1.38k | oscs [1] = &square2; |
31 | 1.38k | oscs [2] = ▵ |
32 | 1.38k | oscs [3] = &noise; |
33 | 1.38k | oscs [4] = &dmc; |
34 | | |
35 | 1.38k | output( NULL ); |
36 | 1.38k | volume( 1.0 ); |
37 | 1.38k | reset( false ); |
38 | 1.38k | } |
39 | | |
40 | | void Nes_Apu::treble_eq( const blip_eq_t& eq ) |
41 | 2.22k | { |
42 | 2.22k | square_synth.treble_eq( eq ); |
43 | 2.22k | triangle.synth.treble_eq( eq ); |
44 | 2.22k | noise.synth.treble_eq( eq ); |
45 | 2.22k | dmc.synth.treble_eq( eq ); |
46 | 2.22k | } |
47 | | |
48 | | void Nes_Apu::enable_nonlinear( double v ) |
49 | 0 | { |
50 | 0 | dmc.nonlinear = true; |
51 | 0 | square_synth.volume( 1.3 * 0.25751258 / 0.742467605 * 0.25 / amp_range * v ); |
52 | |
|
53 | 0 | const double tnd = 0.48 / 202 * nonlinear_tnd_gain(); |
54 | 0 | triangle.synth.volume( 3.0 * tnd ); |
55 | 0 | noise.synth.volume( 2.0 * tnd ); |
56 | 0 | dmc.synth.volume( tnd ); |
57 | |
|
58 | 0 | square1 .last_amp = 0; |
59 | 0 | square2 .last_amp = 0; |
60 | 0 | triangle.last_amp = 0; |
61 | 0 | noise .last_amp = 0; |
62 | 0 | dmc .last_amp = 0; |
63 | 0 | } |
64 | | |
65 | | void Nes_Apu::volume( double v ) |
66 | 2.75k | { |
67 | 2.75k | dmc.nonlinear = false; |
68 | 2.75k | square_synth.volume( 0.1128 / amp_range * v ); |
69 | 2.75k | triangle.synth.volume( 0.12765 / amp_range * v ); |
70 | 2.75k | noise.synth.volume( 0.0741 / amp_range * v ); |
71 | 2.75k | dmc.synth.volume( 0.42545 / 127 * v ); |
72 | 2.75k | } |
73 | | |
74 | | void Nes_Apu::output( Blip_Buffer* buffer ) |
75 | 1.38k | { |
76 | 8.29k | for ( int i = 0; i < osc_count; i++ ) |
77 | 6.91k | osc_output( i, buffer ); |
78 | 1.38k | } |
79 | | |
80 | | void Nes_Apu::set_tempo( double t ) |
81 | 11.8k | { |
82 | 11.8k | tempo_ = t; |
83 | 11.8k | frame_period = (dmc.pal_mode ? 8314 : 7458); |
84 | 11.8k | if ( t != 1.0 ) |
85 | 0 | frame_period = (int) (frame_period / t) & ~1; // must be even |
86 | 11.8k | } |
87 | | |
88 | | void Nes_Apu::reset( bool pal_mode, int initial_dmc_dac ) |
89 | 10.1k | { |
90 | 10.1k | dmc.pal_mode = pal_mode; |
91 | 10.1k | set_tempo( tempo_ ); |
92 | | |
93 | 10.1k | square1.reset(); |
94 | 10.1k | square2.reset(); |
95 | 10.1k | triangle.reset(); |
96 | 10.1k | noise.reset(); |
97 | 10.1k | dmc.reset(); |
98 | | |
99 | 10.1k | last_time = 0; |
100 | 10.1k | last_dmc_time = 0; |
101 | 10.1k | osc_enables = 0; |
102 | 10.1k | irq_flag = false; |
103 | 10.1k | earliest_irq_ = no_irq; |
104 | 10.1k | frame_delay = 1; |
105 | 10.1k | write_register( 0, 0x4017, 0x00 ); |
106 | 10.1k | write_register( 0, 0x4015, 0x00 ); |
107 | | |
108 | 214k | for ( nes_addr_t addr = start_addr; addr <= 0x4013; addr++ ) |
109 | 203k | write_register( 0, addr, (addr & 3) ? 0x00 : 0x10 ); |
110 | | |
111 | 10.1k | dmc.dac = initial_dmc_dac; |
112 | 10.1k | if ( !dmc.nonlinear ) |
113 | 10.1k | triangle.last_amp = 15; |
114 | 10.1k | if ( !dmc.nonlinear ) // TODO: remove? |
115 | 10.1k | dmc.last_amp = initial_dmc_dac; // prevent output transition |
116 | 10.1k | } |
117 | | |
118 | | void Nes_Apu::irq_changed() |
119 | 31.1k | { |
120 | 31.1k | nes_time_t new_irq = dmc.next_irq; |
121 | 31.1k | if ( dmc.irq_flag | irq_flag ) { |
122 | 0 | new_irq = 0; |
123 | 0 | } |
124 | 31.1k | else if ( new_irq > next_irq ) { |
125 | 31.1k | new_irq = next_irq; |
126 | 31.1k | } |
127 | | |
128 | 31.1k | if ( new_irq != earliest_irq_ ) { |
129 | 10.1k | earliest_irq_ = new_irq; |
130 | 10.1k | if ( irq_notifier_ ) |
131 | 0 | irq_notifier_( irq_data ); |
132 | 10.1k | } |
133 | 31.1k | } |
134 | | |
135 | | // frames |
136 | | |
137 | | void Nes_Apu::run_until( nes_time_t end_time ) |
138 | 0 | { |
139 | 0 | require( end_time >= last_dmc_time ); |
140 | 0 | if ( end_time > next_dmc_read_time() ) |
141 | 0 | { |
142 | 0 | nes_time_t start = last_dmc_time; |
143 | 0 | last_dmc_time = end_time; |
144 | 0 | dmc.run( start, end_time ); |
145 | 0 | } |
146 | 0 | } |
147 | | |
148 | | void Nes_Apu::run_until_( nes_time_t end_time ) |
149 | 1.65M | { |
150 | 1.65M | require( end_time >= last_time ); |
151 | | |
152 | 1.65M | if ( end_time == last_time ) |
153 | 235k | return; |
154 | | |
155 | 1.41M | if ( last_dmc_time < end_time ) |
156 | 1.41M | { |
157 | 1.41M | nes_time_t start = last_dmc_time; |
158 | 1.41M | last_dmc_time = end_time; |
159 | 1.41M | dmc.run( start, end_time ); |
160 | 1.41M | } |
161 | | |
162 | 16.8M | while ( true ) |
163 | 16.8M | { |
164 | | // earlier of next frame time or end time |
165 | 16.8M | nes_time_t time = last_time + frame_delay; |
166 | 16.8M | if ( time > end_time ) |
167 | 1.41M | time = end_time; |
168 | 16.8M | frame_delay -= time - last_time; |
169 | | |
170 | | // run oscs to present |
171 | 16.8M | square1.run( last_time, time ); |
172 | 16.8M | square2.run( last_time, time ); |
173 | 16.8M | triangle.run( last_time, time ); |
174 | 16.8M | noise.run( last_time, time ); |
175 | 16.8M | last_time = time; |
176 | | |
177 | 16.8M | if ( time == end_time ) |
178 | 1.41M | break; // no more frames to run |
179 | | |
180 | | // take frame-specific actions |
181 | 15.3M | frame_delay = frame_period; |
182 | 15.3M | switch ( frame++ ) |
183 | 15.3M | { |
184 | 3.84M | case 0: |
185 | 3.84M | if ( !(frame_mode & 0xC0) ) { |
186 | 3.84M | next_irq = time + frame_period * 4 + 2; |
187 | 3.84M | irq_flag = true; |
188 | 3.84M | } |
189 | | // fall through |
190 | 7.68M | case 2: |
191 | | // clock length and sweep on frames 0 and 2 |
192 | 7.68M | square1.clock_length( 0x20 ); |
193 | 7.68M | square2.clock_length( 0x20 ); |
194 | 7.68M | noise.clock_length( 0x20 ); |
195 | 7.68M | triangle.clock_length( 0x80 ); // different bit for halt flag on triangle |
196 | | |
197 | 7.68M | square1.clock_sweep( -1 ); |
198 | 7.68M | square2.clock_sweep( 0 ); |
199 | | |
200 | | // frame 2 is slightly shorter in mode 1 |
201 | 7.68M | if ( dmc.pal_mode && frame == 3 ) |
202 | 1.46M | frame_delay -= 2; |
203 | 7.68M | break; |
204 | | |
205 | 3.85M | case 1: |
206 | | // frame 1 is slightly shorter in mode 0 |
207 | 3.85M | if ( !dmc.pal_mode ) |
208 | 2.37M | frame_delay -= 2; |
209 | 3.85M | break; |
210 | | |
211 | 3.84M | case 3: |
212 | 3.84M | frame = 0; |
213 | | |
214 | | // frame 3 is almost twice as long in mode 1 |
215 | 3.84M | if ( frame_mode & 0x80 ) |
216 | 0 | frame_delay += frame_period - (dmc.pal_mode ? 2 : 6); |
217 | 3.84M | break; |
218 | 15.3M | } |
219 | | |
220 | | // clock envelopes and linear counter every frame |
221 | 15.3M | triangle.clock_linear_counter(); |
222 | 15.3M | square1.clock_envelope(); |
223 | 15.3M | square2.clock_envelope(); |
224 | 15.3M | noise.clock_envelope(); |
225 | 15.3M | } |
226 | 1.41M | } |
227 | | |
228 | | template<class T> |
229 | | inline void zero_apu_osc( T* osc, nes_time_t time ) |
230 | 0 | { |
231 | 0 | Blip_Buffer* output = osc->output; |
232 | 0 | int last_amp = osc->last_amp; |
233 | 0 | osc->last_amp = 0; |
234 | 0 | if ( output && last_amp ) |
235 | 0 | osc->synth.offset( time, -last_amp, output ); |
236 | 0 | } Unexecuted instantiation: void zero_apu_osc<Nes_Square>(Nes_Square*, int) Unexecuted instantiation: void zero_apu_osc<Nes_Triangle>(Nes_Triangle*, int) Unexecuted instantiation: void zero_apu_osc<Nes_Noise>(Nes_Noise*, int) Unexecuted instantiation: void zero_apu_osc<Nes_Dmc>(Nes_Dmc*, int) |
237 | | |
238 | | void Nes_Apu::end_frame( nes_time_t end_time ) |
239 | 1.41M | { |
240 | 1.41M | if ( end_time > last_time ) |
241 | 1.41M | run_until_( end_time ); |
242 | | |
243 | 1.41M | if ( dmc.nonlinear ) |
244 | 0 | { |
245 | 0 | zero_apu_osc( &square1, last_time ); |
246 | 0 | zero_apu_osc( &square2, last_time ); |
247 | 0 | zero_apu_osc( &triangle, last_time ); |
248 | 0 | zero_apu_osc( &noise, last_time ); |
249 | 0 | zero_apu_osc( &dmc, last_time ); |
250 | 0 | } |
251 | | |
252 | | // make times relative to new frame |
253 | 1.41M | last_time -= end_time; |
254 | 1.41M | require( last_time >= 0 ); |
255 | | |
256 | 1.41M | last_dmc_time -= end_time; |
257 | 1.41M | require( last_dmc_time >= 0 ); |
258 | | |
259 | 1.41M | if ( next_irq != no_irq ) { |
260 | 1.41M | next_irq -= end_time; |
261 | 1.41M | check( next_irq >= 0 ); |
262 | 1.41M | } |
263 | 1.41M | if ( dmc.next_irq != no_irq ) { |
264 | 0 | dmc.next_irq -= end_time; |
265 | 0 | check( dmc.next_irq >= 0 ); |
266 | 0 | } |
267 | 1.41M | if ( earliest_irq_ != no_irq ) { |
268 | 1.41M | earliest_irq_ -= end_time; |
269 | 1.41M | if ( earliest_irq_ < 0 ) |
270 | 1.41M | earliest_irq_ = 0; |
271 | 1.41M | } |
272 | 1.41M | } |
273 | | |
274 | | // registers |
275 | | |
276 | | static const unsigned char length_table [0x20] = { |
277 | | 0x0A, 0xFE, 0x14, 0x02, 0x28, 0x04, 0x50, 0x06, |
278 | | 0xA0, 0x08, 0x3C, 0x0A, 0x0E, 0x0C, 0x1A, 0x0E, |
279 | | 0x0C, 0x10, 0x18, 0x12, 0x30, 0x14, 0x60, 0x16, |
280 | | 0xC0, 0x18, 0x48, 0x1A, 0x10, 0x1C, 0x20, 0x1E |
281 | | }; |
282 | | |
283 | | void Nes_Apu::write_register( nes_time_t time, nes_addr_t addr, int data ) |
284 | 235k | { |
285 | 235k | require( addr > 0x20 ); // addr must be actual address (i.e. 0x40xx) |
286 | 235k | require( (unsigned) data <= 0xFF ); |
287 | | |
288 | | // Ignore addresses outside range |
289 | 235k | if ( unsigned (addr - start_addr) > end_addr - start_addr ) |
290 | 0 | return; |
291 | | |
292 | 235k | run_until_( time ); |
293 | | |
294 | 235k | if ( addr < 0x4014 ) |
295 | 204k | { |
296 | | // Write to channel |
297 | 204k | int osc_index = (addr - start_addr) >> 2; |
298 | 204k | Nes_Osc* osc = oscs [osc_index]; |
299 | | |
300 | 204k | int reg = addr & 3; |
301 | 204k | osc->regs [reg] = data; |
302 | 204k | osc->reg_written [reg] = true; |
303 | | |
304 | 204k | if ( osc_index == 4 ) |
305 | 40.7k | { |
306 | | // handle DMC specially |
307 | 40.7k | dmc.write_register( reg, data ); |
308 | 40.7k | } |
309 | 163k | else if ( reg == 3 ) |
310 | 40.7k | { |
311 | | // load length counter |
312 | 40.7k | if ( (osc_enables >> osc_index) & 1 ) |
313 | 0 | osc->length_counter = length_table [(data >> 3) & 0x1F]; |
314 | | |
315 | | // reset square phase |
316 | 40.7k | if ( osc_index < 2 ) |
317 | 20.3k | ((Nes_Square*) osc)->phase = Nes_Square::phase_range - 1; |
318 | 40.7k | } |
319 | 204k | } |
320 | 31.1k | else if ( addr == 0x4015 ) |
321 | 15.5k | { |
322 | | // Channel enables |
323 | 93.5k | for ( int i = osc_count; i--; ) |
324 | 77.9k | if ( !((data >> i) & 1) ) |
325 | 56.3k | oscs [i]->length_counter = 0; |
326 | | |
327 | 15.5k | bool recalc_irq = dmc.irq_flag; |
328 | 15.5k | dmc.irq_flag = false; |
329 | | |
330 | 15.5k | int old_enables = osc_enables; |
331 | 15.5k | osc_enables = data; |
332 | 15.5k | if ( !(data & 0x10) ) { |
333 | 15.5k | dmc.next_irq = no_irq; |
334 | 15.5k | recalc_irq = true; |
335 | 15.5k | } |
336 | 0 | else if ( !(old_enables & 0x10) ) { |
337 | 0 | dmc.start(); // dmc just enabled |
338 | 0 | } |
339 | | |
340 | 15.5k | if ( recalc_irq ) |
341 | 15.5k | irq_changed(); |
342 | 15.5k | } |
343 | 15.5k | else if ( addr == 0x4017 ) |
344 | 15.5k | { |
345 | | // Frame mode |
346 | 15.5k | frame_mode = data; |
347 | | |
348 | 15.5k | bool irq_enabled = !(data & 0x40); |
349 | 15.5k | irq_flag &= irq_enabled; |
350 | 15.5k | next_irq = no_irq; |
351 | | |
352 | | // mode 1 |
353 | 15.5k | frame_delay = (frame_delay & 1); |
354 | 15.5k | frame = 0; |
355 | | |
356 | 15.5k | if ( !(data & 0x80) ) |
357 | 15.5k | { |
358 | | // mode 0 |
359 | 15.5k | frame = 1; |
360 | 15.5k | frame_delay += frame_period; |
361 | 15.5k | if ( irq_enabled ) |
362 | 15.5k | next_irq = time + frame_delay + frame_period * 3 + 1; |
363 | 15.5k | } |
364 | | |
365 | 15.5k | irq_changed(); |
366 | 15.5k | } |
367 | 235k | } |
368 | | |
369 | | int Nes_Apu::read_status( nes_time_t time ) |
370 | 0 | { |
371 | 0 | run_until_( time - 1 ); |
372 | |
|
373 | 0 | int result = (dmc.irq_flag << 7) | (irq_flag << 6); |
374 | |
|
375 | 0 | for ( int i = 0; i < osc_count; i++ ) |
376 | 0 | if ( oscs [i]->length_counter ) |
377 | 0 | result |= 1 << i; |
378 | |
|
379 | 0 | run_until_( time ); |
380 | |
|
381 | 0 | if ( irq_flag ) |
382 | 0 | { |
383 | 0 | result |= 0x40; |
384 | 0 | irq_flag = false; |
385 | 0 | irq_changed(); |
386 | 0 | } |
387 | | |
388 | | //debug_printf( "%6d/%d Read $4015->$%02X\n", frame_delay, frame, result ); |
389 | |
|
390 | 0 | return result; |
391 | 0 | } |