diff options
Diffstat (limited to 'apps/codecs/libgme/hes_emu.c')
-rw-r--r-- | apps/codecs/libgme/hes_emu.c | 877 |
1 files changed, 877 insertions, 0 deletions
diff --git a/apps/codecs/libgme/hes_emu.c b/apps/codecs/libgme/hes_emu.c new file mode 100644 index 0000000000..a44eded8d7 --- /dev/null +++ b/apps/codecs/libgme/hes_emu.c | |||
@@ -0,0 +1,877 @@ | |||
1 | // Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ | ||
2 | |||
3 | #include "hes_emu.h" | ||
4 | |||
5 | #include "blargg_endian.h" | ||
6 | #include "blargg_source.h" | ||
7 | |||
8 | /* Copyright (C) 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 more | ||
15 | details. You should have received a copy of the GNU Lesser General Public | ||
16 | License along with this module; if not, write to the Free Software Foundation, | ||
17 | Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ | ||
18 | |||
19 | int const timer_mask = 0x04; | ||
20 | int const vdp_mask = 0x02; | ||
21 | int const i_flag_mask = 0x04; | ||
22 | int const unmapped = 0xFF; | ||
23 | |||
24 | long const period_60hz = 262 * 455L; // scanlines * clocks per scanline | ||
25 | |||
26 | int const stereo = 2; // number of channels for stereo | ||
27 | int const silence_max = 6; // seconds | ||
28 | int const silence_threshold = 0x10; | ||
29 | long const fade_block_size = 512; | ||
30 | int const fade_shift = 8; // fade ends with gain at 1.0 / (1 << fade_shift) | ||
31 | |||
32 | const char gme_wrong_file_type [] ICONST_ATTR = "Wrong file type for this emulator"; | ||
33 | |||
34 | void clear_track_vars( struct Hes_Emu* this ) | ||
35 | { | ||
36 | this->current_track_ = -1; | ||
37 | this->out_time = 0; | ||
38 | this->emu_time = 0; | ||
39 | this->emu_track_ended_ = true; | ||
40 | this->track_ended = true; | ||
41 | this->fade_start = LONG_MAX / 2 + 1; | ||
42 | this->fade_step = 1; | ||
43 | this->silence_time = 0; | ||
44 | this->silence_count = 0; | ||
45 | this->buf_remain = 0; | ||
46 | } | ||
47 | |||
48 | void Hes_init( struct Hes_Emu* this ) | ||
49 | { | ||
50 | this->sample_rate_ = 0; | ||
51 | this->mute_mask_ = 0; | ||
52 | this->tempo_ = 1.0; | ||
53 | |||
54 | // defaults | ||
55 | this->max_initial_silence = 2; | ||
56 | this->ignore_silence = false; | ||
57 | |||
58 | // Unload | ||
59 | this->voice_count_ = 0; | ||
60 | clear_track_vars( this ); | ||
61 | |||
62 | this->timer.raw_load = 0; | ||
63 | this->silence_lookahead = 6; | ||
64 | Sound_set_gain( this, 1.11 ); | ||
65 | |||
66 | Rom_init( &this->rom, 0x2000 ); | ||
67 | |||
68 | Apu_init( &this->apu ); | ||
69 | Adpcm_init( &this->adpcm ); | ||
70 | Cpu_init( &this->cpu ); | ||
71 | |||
72 | /* Set default track count */ | ||
73 | this->track_count = 255; | ||
74 | } | ||
75 | |||
76 | static blargg_err_t check_hes_header( void const* header ) | ||
77 | { | ||
78 | if ( memcmp( header, "HESM", 4 ) ) | ||
79 | return gme_wrong_file_type; | ||
80 | return 0; | ||
81 | } | ||
82 | |||
83 | // Setup | ||
84 | |||
85 | blargg_err_t Hes_load( struct Hes_Emu* this, void* data, long size ) | ||
86 | { | ||
87 | // Unload | ||
88 | this->voice_count_ = 0; | ||
89 | clear_track_vars( this ); | ||
90 | |||
91 | assert( offsetof (struct header_t,unused [4]) == header_size ); | ||
92 | RETURN_ERR( Rom_load( &this->rom, data, size, header_size, &this->header, unmapped ) ); | ||
93 | |||
94 | RETURN_ERR( check_hes_header( this->header.tag ) ); | ||
95 | |||
96 | /* if ( header_.vers != 0 ) | ||
97 | warning( "Unknown file version" ); | ||
98 | |||
99 | if ( memcmp( header_.data_tag, "DATA", 4 ) ) | ||
100 | warning( "Data header missing" ); | ||
101 | |||
102 | if ( memcmp( header_.unused, "\0\0\0\0", 4 ) ) | ||
103 | warning( "Unknown header data" ); */ | ||
104 | |||
105 | // File spec supports multiple blocks, but I haven't found any, and | ||
106 | // many files have bad sizes in the only block, so it's simpler to | ||
107 | // just try to load the damn data as best as possible. | ||
108 | |||
109 | long addr = get_le32( this->header.addr ); | ||
110 | /* long rom_size = get_le32( this->header.size ); */ | ||
111 | long const rom_max = 0x100000; | ||
112 | if ( addr & ~(rom_max - 1) ) | ||
113 | { | ||
114 | /* warning( "Invalid address" ); */ | ||
115 | addr &= rom_max - 1; | ||
116 | } | ||
117 | /* if ( (unsigned long) (addr + size) > (unsigned long) rom_max ) | ||
118 | warning( "Invalid size" ); | ||
119 | |||
120 | if ( rom_size != rom.file_size() ) | ||
121 | { | ||
122 | if ( size <= rom.file_size() - 4 && !memcmp( rom.begin() + size, "DATA", 4 ) ) | ||
123 | warning( "Multiple DATA not supported" ); | ||
124 | else if ( size < rom.file_size() ) | ||
125 | warning( "Extra file data" ); | ||
126 | else | ||
127 | warning( "Missing file data" ); | ||
128 | } */ | ||
129 | |||
130 | Rom_set_addr( &this->rom, addr ); | ||
131 | |||
132 | this->voice_count_ = osc_count + adpcm_osc_count; | ||
133 | |||
134 | Apu_volume( &this->apu, this->gain_ ); | ||
135 | Adpcm_volume( &this->adpcm, this->gain_ ); | ||
136 | |||
137 | // Setup buffer | ||
138 | this->clock_rate_ = 7159091; | ||
139 | Buffer_clock_rate( &this->stereo_buf, 7159091 ); | ||
140 | this->buf_changed_count = Buffer_channels_changed_count( &this->stereo_buf ); | ||
141 | |||
142 | Sound_set_tempo( this, this->tempo_ ); | ||
143 | Sound_mute_voices( this, this->mute_mask_ ); | ||
144 | |||
145 | // Reset track count | ||
146 | this->track_count = 255; | ||
147 | this->m3u.size = 0; | ||
148 | return 0; | ||
149 | } | ||
150 | |||
151 | |||
152 | // Emulation | ||
153 | |||
154 | void recalc_timer_load( struct Hes_Emu* this ) ICODE_ATTR; | ||
155 | void recalc_timer_load( struct Hes_Emu* this ) | ||
156 | { | ||
157 | this->timer.load = this->timer.raw_load * this->timer_base + 1; | ||
158 | } | ||
159 | |||
160 | // Hardware | ||
161 | |||
162 | void irq_changed( struct Hes_Emu* this ) ICODE_ATTR; | ||
163 | void run_until( struct Hes_Emu* this, hes_time_t present ) ICODE_ATTR; | ||
164 | void Cpu_write_vdp( struct Hes_Emu* this, int addr, int data ) | ||
165 | { | ||
166 | switch ( addr ) | ||
167 | { | ||
168 | case 0: | ||
169 | this->vdp.latch = data & 0x1F; | ||
170 | break; | ||
171 | |||
172 | case 2: | ||
173 | if ( this->vdp.latch == 5 ) | ||
174 | { | ||
175 | /* if ( data & 0x04 ) | ||
176 | warning( "Scanline interrupt unsupported" ); */ | ||
177 | run_until( this, Cpu_time( &this->cpu ) ); | ||
178 | this->vdp.control = data; | ||
179 | irq_changed( this ); | ||
180 | } | ||
181 | else | ||
182 | { | ||
183 | dprintf( "VDP not supported: $%02X <- $%02X\n", this->vdp.latch, data ); | ||
184 | } | ||
185 | break; | ||
186 | |||
187 | case 3: | ||
188 | dprintf( "VDP MSB not supported: $%02X <- $%02X\n", this->vdp.latch, data ); | ||
189 | break; | ||
190 | } | ||
191 | } | ||
192 | |||
193 | int Cpu_done( struct Hes_Emu* this ) | ||
194 | { | ||
195 | check( time() >= end_time() || | ||
196 | (!(r.status & i_flag_mask) && time() >= irq_time()) ); | ||
197 | |||
198 | if ( !(this->cpu.r.status & i_flag_mask) ) | ||
199 | { | ||
200 | hes_time_t present = Cpu_time( &this->cpu ); | ||
201 | |||
202 | if ( this->irq.timer <= present && !(this->irq.disables & timer_mask) ) | ||
203 | { | ||
204 | this->timer.fired = true; | ||
205 | this->irq.timer = future_hes_time; | ||
206 | irq_changed( this ); // overkill, but not worth writing custom code | ||
207 | #if defined (GME_FRAME_HOOK_DEFINED) | ||
208 | { | ||
209 | unsigned const threshold = period_60hz / 30; | ||
210 | unsigned long elapsed = present - last_frame_hook; | ||
211 | if ( elapsed - period_60hz + threshold / 2 < threshold ) | ||
212 | { | ||
213 | last_frame_hook = present; | ||
214 | GME_FRAME_HOOK( this ); | ||
215 | } | ||
216 | } | ||
217 | #endif | ||
218 | return 0x0A; | ||
219 | } | ||
220 | |||
221 | if ( this->irq.vdp <= present && !(this->irq.disables & vdp_mask) ) | ||
222 | { | ||
223 | // work around for bugs with music not acknowledging VDP | ||
224 | //run_until( present ); | ||
225 | //irq.vdp = future_hes_time; | ||
226 | //irq_changed(); | ||
227 | #if defined(GME_FRAME_HOOK_DEFINED) | ||
228 | last_frame_hook = present; | ||
229 | GME_FRAME_HOOK( this ); | ||
230 | #endif | ||
231 | return 0x08; | ||
232 | } | ||
233 | } | ||
234 | return 0; | ||
235 | } | ||
236 | |||
237 | void Emu_cpu_write( struct Hes_Emu* this, hes_addr_t addr, int data ) | ||
238 | { | ||
239 | hes_time_t time = Cpu_time( &this->cpu ); | ||
240 | if ( (unsigned) (addr - start_addr) <= end_addr - start_addr ) | ||
241 | { | ||
242 | GME_APU_HOOK( this, addr - apu.start_addr, data ); | ||
243 | // avoid going way past end when a long block xfer is writing to I/O space | ||
244 | hes_time_t t = min( time, this->cpu.end_time + 8 ); | ||
245 | Apu_write_data( &this->apu, t, addr, data ); | ||
246 | return; | ||
247 | } | ||
248 | |||
249 | if ( (unsigned) (addr - io_addr) < io_size ) | ||
250 | { | ||
251 | hes_time_t t = min( time, this->cpu.end_time + 6 ); | ||
252 | Adpcm_write_data( &this->adpcm, t, addr, data ); | ||
253 | return; | ||
254 | } | ||
255 | |||
256 | switch ( addr ) | ||
257 | { | ||
258 | case 0x0000: | ||
259 | case 0x0002: | ||
260 | case 0x0003: | ||
261 | Cpu_write_vdp( this, addr, data ); | ||
262 | return; | ||
263 | |||
264 | case 0x0C00: { | ||
265 | run_until( this, time ); | ||
266 | this->timer.raw_load = (data & 0x7F) + 1; | ||
267 | recalc_timer_load( this ); | ||
268 | this->timer.count = this->timer.load; | ||
269 | break; | ||
270 | } | ||
271 | |||
272 | case 0x0C01: | ||
273 | data &= 1; | ||
274 | if ( this->timer.enabled == data ) | ||
275 | return; | ||
276 | run_until( this, time ); | ||
277 | this->timer.enabled = data; | ||
278 | if ( data ) | ||
279 | this->timer.count = this->timer.load; | ||
280 | break; | ||
281 | |||
282 | case 0x1402: | ||
283 | run_until( this, time ); | ||
284 | this->irq.disables = data; | ||
285 | |||
286 | // flag questionable values | ||
287 | if ( (data & 0xF8) && (data & 0xF8) != 0xF8 ) { | ||
288 | dprintf( "Int mask: $%02X\n", data ); | ||
289 | } | ||
290 | break; | ||
291 | |||
292 | case 0x1403: | ||
293 | run_until( this, time ); | ||
294 | if ( this->timer.enabled ) | ||
295 | this->timer.count = this->timer.load; | ||
296 | this->timer.fired = false; | ||
297 | break; | ||
298 | |||
299 | #ifndef NDEBUG | ||
300 | case 0x1000: // I/O port | ||
301 | case 0x0402: // palette | ||
302 | case 0x0403: | ||
303 | case 0x0404: | ||
304 | case 0x0405: | ||
305 | return; | ||
306 | |||
307 | default: | ||
308 | dprintf( "unmapped write $%04X <- $%02X\n", addr, data ); | ||
309 | return; | ||
310 | #endif | ||
311 | } | ||
312 | |||
313 | irq_changed( this ); | ||
314 | } | ||
315 | |||
316 | int Emu_cpu_read( struct Hes_Emu* this, hes_addr_t addr ) | ||
317 | { | ||
318 | hes_time_t time = Cpu_time( &this->cpu ); | ||
319 | addr &= page_size - 1; | ||
320 | switch ( addr ) | ||
321 | { | ||
322 | case 0x0000: | ||
323 | if ( this->irq.vdp > time ) | ||
324 | return 0; | ||
325 | this->irq.vdp = future_hes_time; | ||
326 | run_until( this, time ); | ||
327 | irq_changed( this ); | ||
328 | return 0x20; | ||
329 | |||
330 | case 0x0002: | ||
331 | case 0x0003: | ||
332 | dprintf( "VDP read not supported: %d\n", addr ); | ||
333 | return 0; | ||
334 | |||
335 | case 0x0C01: | ||
336 | //return timer.enabled; // TODO: remove? | ||
337 | case 0x0C00: | ||
338 | run_until( this, time ); | ||
339 | dprintf( "Timer count read\n" ); | ||
340 | return (unsigned) (this->timer.count - 1) / this->timer_base; | ||
341 | |||
342 | case 0x1402: | ||
343 | return this->irq.disables; | ||
344 | |||
345 | case 0x1403: | ||
346 | { | ||
347 | int status = 0; | ||
348 | if ( this->irq.timer <= time ) status |= timer_mask; | ||
349 | if ( this->irq.vdp <= time ) status |= vdp_mask; | ||
350 | return status; | ||
351 | } | ||
352 | |||
353 | case 0x180A: | ||
354 | case 0x180B: | ||
355 | case 0x180C: | ||
356 | case 0x180D: | ||
357 | return Adpcm_read_data( &this->adpcm, time, addr ); | ||
358 | |||
359 | #ifndef NDEBUG | ||
360 | case 0x1000: // I/O port | ||
361 | // case 0x180C: // CD-ROM | ||
362 | // case 0x180D: | ||
363 | break; | ||
364 | |||
365 | default: | ||
366 | dprintf( "unmapped read $%04X\n", addr ); | ||
367 | #endif | ||
368 | } | ||
369 | |||
370 | return unmapped; | ||
371 | } | ||
372 | |||
373 | // see hes_cpu_io.h for core read/write functions | ||
374 | |||
375 | // Emulation | ||
376 | |||
377 | void run_until( struct Hes_Emu* this, hes_time_t present ) | ||
378 | { | ||
379 | while ( this->vdp.next_vbl < present ) | ||
380 | this->vdp.next_vbl += this->play_period; | ||
381 | |||
382 | hes_time_t elapsed = present - this->timer.last_time; | ||
383 | if ( elapsed > 0 ) | ||
384 | { | ||
385 | if ( this->timer.enabled ) | ||
386 | { | ||
387 | this->timer.count -= elapsed; | ||
388 | if ( this->timer.count <= 0 ) | ||
389 | this->timer.count += this->timer.load; | ||
390 | } | ||
391 | this->timer.last_time = present; | ||
392 | } | ||
393 | } | ||
394 | |||
395 | void irq_changed( struct Hes_Emu* this ) | ||
396 | { | ||
397 | hes_time_t present = Cpu_time( &this->cpu ); | ||
398 | |||
399 | if ( this->irq.timer > present ) | ||
400 | { | ||
401 | this->irq.timer = future_hes_time; | ||
402 | if ( this->timer.enabled && !this->timer.fired ) | ||
403 | this->irq.timer = present + this->timer.count; | ||
404 | } | ||
405 | |||
406 | if ( this->irq.vdp > present ) | ||
407 | { | ||
408 | this->irq.vdp = future_hes_time; | ||
409 | if ( this->vdp.control & 0x08 ) | ||
410 | this->irq.vdp = this->vdp.next_vbl; | ||
411 | } | ||
412 | |||
413 | hes_time_t time = future_hes_time; | ||
414 | if ( !(this->irq.disables & timer_mask) ) time = this->irq.timer; | ||
415 | if ( !(this->irq.disables & vdp_mask) ) time = min( time, this->irq.vdp ); | ||
416 | |||
417 | // Set cpu irq time | ||
418 | this->cpu.state->time += Cpu_update_end_time( &this->cpu, this->cpu.r.status, | ||
419 | this->cpu.end_time, (this->cpu.irq_time = time) ); | ||
420 | } | ||
421 | |||
422 | static void adjust_time( blargg_long* time, hes_time_t delta ) ICODE_ATTR; | ||
423 | static void adjust_time( blargg_long* time, hes_time_t delta ) | ||
424 | { | ||
425 | if ( *time < future_hes_time ) | ||
426 | { | ||
427 | *time -= delta; | ||
428 | if ( *time < 0 ) | ||
429 | *time = 0; | ||
430 | } | ||
431 | } | ||
432 | |||
433 | blargg_err_t run_clocks( struct Hes_Emu* this, blip_time_t* duration_ ) ICODE_ATTR; | ||
434 | blargg_err_t run_clocks( struct Hes_Emu* this, blip_time_t* duration_ ) | ||
435 | { | ||
436 | blip_time_t duration = *duration_; // cache | ||
437 | |||
438 | Cpu_run( this, duration ); | ||
439 | /* warning( "Emulation error (illegal instruction)" ); */ | ||
440 | |||
441 | check( time() >= duration ); | ||
442 | //check( time() - duration < 20 ); // Txx instruction could cause going way over | ||
443 | |||
444 | run_until( this, duration ); | ||
445 | |||
446 | // end time frame | ||
447 | this->timer.last_time -= duration; | ||
448 | this->vdp.next_vbl -= duration; | ||
449 | #if defined (GME_FRAME_HOOK_DEFINED) | ||
450 | last_frame_hook -= *duration; | ||
451 | #endif | ||
452 | |||
453 | // End cpu frame | ||
454 | this->cpu.state_.base -= duration; | ||
455 | if ( this->cpu.irq_time < future_hes_time ) this->cpu.irq_time -= duration; | ||
456 | if ( this->cpu.end_time < future_hes_time ) this->cpu.end_time -= duration; | ||
457 | |||
458 | adjust_time( &this->irq.timer, duration ); | ||
459 | adjust_time( &this->irq.vdp, duration ); | ||
460 | Apu_end_frame( &this->apu, duration ); | ||
461 | Adpcm_end_frame( &this->adpcm, duration ); | ||
462 | |||
463 | return 0; | ||
464 | } | ||
465 | |||
466 | blargg_err_t play_( struct Hes_Emu* this, long count, sample_t* out ) ICODE_ATTR; | ||
467 | blargg_err_t play_( struct Hes_Emu* this, long count, sample_t* out ) | ||
468 | { | ||
469 | long remain = count; | ||
470 | while ( remain ) | ||
471 | { | ||
472 | remain -= Buffer_read_samples( &this->stereo_buf, &out [count - remain], remain ); | ||
473 | if ( remain ) | ||
474 | { | ||
475 | if ( this->buf_changed_count != Buffer_channels_changed_count( &this->stereo_buf ) ) | ||
476 | { | ||
477 | this->buf_changed_count = Buffer_channels_changed_count( &this->stereo_buf ); | ||
478 | // Remute voices | ||
479 | Sound_mute_voices( this, this->mute_mask_ ); | ||
480 | } | ||
481 | |||
482 | int msec = Buffer_length( &this->stereo_buf ); | ||
483 | blip_time_t clocks_emulated = (blargg_long) msec * this->clock_rate_ / 1000; | ||
484 | RETURN_ERR( run_clocks( this, &clocks_emulated ) ); | ||
485 | assert( clocks_emulated ); | ||
486 | Buffer_end_frame( &this->stereo_buf, clocks_emulated ); | ||
487 | } | ||
488 | } | ||
489 | return 0; | ||
490 | } | ||
491 | |||
492 | |||
493 | // Music emu | ||
494 | |||
495 | blargg_err_t Hes_set_sample_rate( struct Hes_Emu* this, long rate ) | ||
496 | { | ||
497 | require( !this->sample_rate_ ); // sample rate can't be changed once set | ||
498 | Buffer_init( &this->stereo_buf ); | ||
499 | RETURN_ERR( Buffer_set_sample_rate( &this->stereo_buf, rate, 1000 / 20 ) ); | ||
500 | |||
501 | // Set bass frequency | ||
502 | Buffer_bass_freq( &this->stereo_buf, 60 ); | ||
503 | |||
504 | this->sample_rate_ = rate; | ||
505 | return 0; | ||
506 | } | ||
507 | |||
508 | void Sound_mute_voice( struct Hes_Emu* this, int index, bool mute ) | ||
509 | { | ||
510 | require( (unsigned) index < (unsigned) this->voice_count_ ); | ||
511 | int bit = 1 << index; | ||
512 | int mask = this->mute_mask_ | bit; | ||
513 | if ( !mute ) | ||
514 | mask ^= bit; | ||
515 | Sound_mute_voices( this, mask ); | ||
516 | } | ||
517 | |||
518 | void Sound_mute_voices( struct Hes_Emu* this, int mask ) | ||
519 | { | ||
520 | require( this->sample_rate_ ); // sample rate must be set first | ||
521 | this->mute_mask_ = mask; | ||
522 | |||
523 | // Set adpcm voice | ||
524 | struct channel_t ch = Buffer_channel( &this->stereo_buf ); | ||
525 | if ( mask & (1 << this->voice_count_ ) ) | ||
526 | Adpcm_set_output( &this->adpcm, 0, 0, 0, 0 ); | ||
527 | else | ||
528 | Adpcm_set_output( &this->adpcm, 0, ch.center, ch.left, ch.right ); | ||
529 | |||
530 | // Set apu voices | ||
531 | int i = this->voice_count_ - 1; | ||
532 | for ( ; i--; ) | ||
533 | { | ||
534 | if ( mask & (1 << i) ) | ||
535 | { | ||
536 | Apu_osc_output( &this->apu, i, 0, 0, 0 ); | ||
537 | } | ||
538 | else | ||
539 | { | ||
540 | assert( (ch.center && ch.left && ch.right) || | ||
541 | (!ch.center && !ch.left && !ch.right) ); // all or nothing | ||
542 | Apu_osc_output( &this->apu, i, ch.center, ch.left, ch.right ); | ||
543 | } | ||
544 | } | ||
545 | } | ||
546 | |||
547 | void Sound_set_tempo( struct Hes_Emu* this, double t ) | ||
548 | { | ||
549 | require( this->sample_rate_ ); // sample rate must be set first | ||
550 | double const min = 0.02; | ||
551 | double const max = 4.00; | ||
552 | if ( t < min ) t = min; | ||
553 | if ( t > max ) t = max; | ||
554 | this->play_period = (hes_time_t) (period_60hz / t); | ||
555 | this->timer_base = (int) (1024 / t); | ||
556 | recalc_timer_load( this ); | ||
557 | this->tempo_ = t; | ||
558 | } | ||
559 | |||
560 | void fill_buf( struct Hes_Emu* this ) ICODE_ATTR; | ||
561 | blargg_err_t Hes_start_track( struct Hes_Emu* this, int track ) | ||
562 | { | ||
563 | clear_track_vars( this ); | ||
564 | |||
565 | // Remap track if playlist available | ||
566 | if ( this->m3u.size > 0 ) { | ||
567 | struct entry_t* e = &this->m3u.entries[track]; | ||
568 | track = e->track; | ||
569 | } | ||
570 | |||
571 | this->current_track_ = track; | ||
572 | |||
573 | Buffer_clear( &this->stereo_buf ); | ||
574 | |||
575 | memset( this->cpu.ram, 0, sizeof this->cpu.ram ); // some HES music relies on zero fill | ||
576 | memset( this->sgx, 0, sizeof this->sgx ); | ||
577 | |||
578 | Apu_reset( &this->apu ); | ||
579 | Adpcm_reset( &this->adpcm ); | ||
580 | Cpu_reset( &this->cpu ); | ||
581 | |||
582 | unsigned i; | ||
583 | for ( i = 0; i < sizeof this->header.banks; i++ ) | ||
584 | Cpu_set_mmr( this, i, this->header.banks [i] ); | ||
585 | Cpu_set_mmr( this, page_count, 0xFF ); // unmapped beyond end of address space | ||
586 | |||
587 | this->irq.disables = timer_mask | vdp_mask; | ||
588 | this->irq.timer = future_hes_time; | ||
589 | this->irq.vdp = future_hes_time; | ||
590 | |||
591 | this->timer.enabled = false; | ||
592 | this->timer.raw_load= 0x80; | ||
593 | this->timer.count = this->timer.load; | ||
594 | this->timer.fired = false; | ||
595 | this->timer.last_time = 0; | ||
596 | |||
597 | this->vdp.latch = 0; | ||
598 | this->vdp.control = 0; | ||
599 | this->vdp.next_vbl = 0; | ||
600 | |||
601 | this->cpu.ram [0x1FF] = (idle_addr - 1) >> 8; | ||
602 | this->cpu.ram [0x1FE] = (idle_addr - 1) & 0xFF; | ||
603 | this->cpu.r.sp = 0xFD; | ||
604 | this->cpu.r.pc = get_le16( this->header.init_addr ); | ||
605 | this->cpu.r.a = track; | ||
606 | |||
607 | recalc_timer_load( this ); | ||
608 | this->last_frame_hook = 0; | ||
609 | |||
610 | this->emu_track_ended_ = false; | ||
611 | this->track_ended = false; | ||
612 | |||
613 | if ( !this->ignore_silence ) | ||
614 | { | ||
615 | // play until non-silence or end of track | ||
616 | long end; | ||
617 | for ( end =this-> max_initial_silence * stereo * this->sample_rate_; this->emu_time < end; ) | ||
618 | { | ||
619 | fill_buf( this ); | ||
620 | if ( this->buf_remain | (int) this->emu_track_ended_ ) | ||
621 | break; | ||
622 | } | ||
623 | |||
624 | this->emu_time = this->buf_remain; | ||
625 | this->out_time = 0; | ||
626 | this->silence_time = 0; | ||
627 | this->silence_count = 0; | ||
628 | } | ||
629 | /* return track_ended() ? warning() : 0; */ | ||
630 | return 0; | ||
631 | } | ||
632 | |||
633 | // Tell/Seek | ||
634 | |||
635 | blargg_long msec_to_samples( blargg_long msec, long sample_rate ) | ||
636 | { | ||
637 | blargg_long sec = msec / 1000; | ||
638 | msec -= sec * 1000; | ||
639 | return (sec * sample_rate + msec * sample_rate / 1000) * stereo; | ||
640 | } | ||
641 | |||
642 | long Track_tell( struct Hes_Emu* this ) | ||
643 | { | ||
644 | blargg_long rate = this->sample_rate_ * stereo; | ||
645 | blargg_long sec = this->out_time / rate; | ||
646 | return sec * 1000 + (this->out_time - sec * rate) * 1000 / rate; | ||
647 | } | ||
648 | |||
649 | blargg_err_t Track_seek( struct Hes_Emu* this, long msec ) | ||
650 | { | ||
651 | blargg_long time = msec_to_samples( msec, this->sample_rate_ ); | ||
652 | if ( time < this->out_time ) | ||
653 | RETURN_ERR( Hes_start_track( this, this->current_track_ ) ); | ||
654 | return Track_skip( this, time - this->out_time ); | ||
655 | } | ||
656 | |||
657 | blargg_err_t skip_( struct Hes_Emu* this, long count ) ICODE_ATTR; | ||
658 | blargg_err_t skip_( struct Hes_Emu* this, long count ) | ||
659 | { | ||
660 | // for long skip, mute sound | ||
661 | const long threshold = 30000; | ||
662 | if ( count > threshold ) | ||
663 | { | ||
664 | int saved_mute = this->mute_mask_; | ||
665 | Sound_mute_voices( this, ~0 ); | ||
666 | |||
667 | while ( count > threshold / 2 && !this->emu_track_ended_ ) | ||
668 | { | ||
669 | RETURN_ERR( play_( this, buf_size, this->buf ) ); | ||
670 | count -= buf_size; | ||
671 | } | ||
672 | |||
673 | Sound_mute_voices( this, saved_mute ); | ||
674 | } | ||
675 | |||
676 | while ( count && !this->emu_track_ended_ ) | ||
677 | { | ||
678 | long n = buf_size; | ||
679 | if ( n > count ) | ||
680 | n = count; | ||
681 | count -= n; | ||
682 | RETURN_ERR( play_( this, n, this->buf ) ); | ||
683 | } | ||
684 | return 0; | ||
685 | } | ||
686 | |||
687 | blargg_err_t Track_skip( struct Hes_Emu* this, long count ) | ||
688 | { | ||
689 | require( this->current_track_ >= 0 ); // start_track() must have been called already | ||
690 | this->out_time += count; | ||
691 | |||
692 | // remove from silence and buf first | ||
693 | { | ||
694 | long n = min( count, this->silence_count ); | ||
695 | this->silence_count -= n; | ||
696 | count -= n; | ||
697 | |||
698 | n = min( count, this->buf_remain ); | ||
699 | this->buf_remain -= n; | ||
700 | count -= n; | ||
701 | } | ||
702 | |||
703 | if ( count && !this->emu_track_ended_ ) | ||
704 | { | ||
705 | this->emu_time += count; | ||
706 | |||
707 | // End track if error | ||
708 | if ( skip_( this, count ) ) | ||
709 | this->emu_track_ended_ = true; | ||
710 | } | ||
711 | |||
712 | if ( !(this->silence_count | this->buf_remain) ) // caught up to emulator, so update track ended | ||
713 | this->track_ended |= this->emu_track_ended_; | ||
714 | |||
715 | return 0; | ||
716 | } | ||
717 | |||
718 | |||
719 | |||
720 | // Fading | ||
721 | |||
722 | void Track_set_fade( struct Hes_Emu* this, long start_msec, long length_msec ) | ||
723 | { | ||
724 | this->fade_step = this->sample_rate_ * length_msec / (fade_block_size * fade_shift * 1000 / stereo); | ||
725 | this->fade_start = msec_to_samples( start_msec, this->sample_rate_ ); | ||
726 | } | ||
727 | |||
728 | // unit / pow( 2.0, (double) x / step ) | ||
729 | static int int_log( blargg_long x, int step, int unit ) ICODE_ATTR; | ||
730 | static int int_log( blargg_long x, int step, int unit ) | ||
731 | { | ||
732 | int shift = x / step; | ||
733 | int fraction = (x - shift * step) * unit / step; | ||
734 | return ((unit - fraction) + (fraction >> 1)) >> shift; | ||
735 | } | ||
736 | |||
737 | void handle_fade( struct Hes_Emu* this, long out_count, sample_t* out ) ICODE_ATTR; | ||
738 | void handle_fade( struct Hes_Emu* this, long out_count, sample_t* out ) | ||
739 | { | ||
740 | int i; | ||
741 | for ( i = 0; i < out_count; i += fade_block_size ) | ||
742 | { | ||
743 | int const shift = 14; | ||
744 | int const unit = 1 << shift; | ||
745 | int gain = int_log( (this->out_time + i - this->fade_start) / fade_block_size, | ||
746 | this->fade_step, unit ); | ||
747 | if ( gain < (unit >> fade_shift) ) | ||
748 | this->track_ended = this->emu_track_ended_ = true; | ||
749 | |||
750 | sample_t* io = &out [i]; | ||
751 | int count; | ||
752 | for ( count = min( fade_block_size, out_count - i ); count; --count ) | ||
753 | { | ||
754 | *io = (sample_t) ((*io * gain) >> shift); | ||
755 | ++io; | ||
756 | } | ||
757 | } | ||
758 | } | ||
759 | |||
760 | // Silence detection | ||
761 | |||
762 | void emu_play( struct Hes_Emu* this, long count, sample_t* out ) ICODE_ATTR; | ||
763 | void emu_play( struct Hes_Emu* this, long count, sample_t* out ) | ||
764 | { | ||
765 | check( current_track_ >= 0 ); | ||
766 | this->emu_time += count; | ||
767 | if ( this->current_track_ >= 0 && !this->emu_track_ended_ ) { | ||
768 | |||
769 | // End track if error | ||
770 | if ( play_( this, count, out ) ) | ||
771 | this->emu_track_ended_ = true; | ||
772 | } | ||
773 | else | ||
774 | memset( out, 0, count * sizeof *out ); | ||
775 | } | ||
776 | |||
777 | // number of consecutive silent samples at end | ||
778 | static long count_silence( sample_t* begin, long size ) ICODE_ATTR; | ||
779 | static long count_silence( sample_t* begin, long size ) | ||
780 | { | ||
781 | sample_t first = *begin; | ||
782 | *begin = silence_threshold; // sentinel | ||
783 | sample_t* p = begin + size; | ||
784 | while ( (unsigned) (*--p + silence_threshold / 2) <= (unsigned) silence_threshold ) { } | ||
785 | *begin = first; | ||
786 | return size - (p - begin); | ||
787 | } | ||
788 | |||
789 | // fill internal buffer and check it for silence | ||
790 | void fill_buf( struct Hes_Emu* this ) | ||
791 | { | ||
792 | assert( !this->buf_remain ); | ||
793 | if ( !this->emu_track_ended_ ) | ||
794 | { | ||
795 | emu_play( this, buf_size, this->buf ); | ||
796 | long silence = count_silence( this->buf, buf_size ); | ||
797 | if ( silence < buf_size ) | ||
798 | { | ||
799 | this->silence_time = this->emu_time - silence; | ||
800 | this->buf_remain = buf_size; | ||
801 | return; | ||
802 | } | ||
803 | } | ||
804 | this->silence_count += buf_size; | ||
805 | } | ||
806 | |||
807 | blargg_err_t Hes_play( struct Hes_Emu* this, long out_count, sample_t* out ) | ||
808 | { | ||
809 | if ( this->track_ended ) | ||
810 | { | ||
811 | memset( out, 0, out_count * sizeof *out ); | ||
812 | } | ||
813 | else | ||
814 | { | ||
815 | require( this->current_track_ >= 0 ); | ||
816 | require( out_count % stereo == 0 ); | ||
817 | |||
818 | assert( this->emu_time >= this->out_time ); | ||
819 | |||
820 | // prints nifty graph of how far ahead we are when searching for silence | ||
821 | //dprintf( "%*s \n", int ((emu_time - out_time) * 7 / sample_rate()), "*" ); | ||
822 | |||
823 | long pos = 0; | ||
824 | if ( this->silence_count ) | ||
825 | { | ||
826 | // during a run of silence, run emulator at >=2x speed so it gets ahead | ||
827 | long ahead_time = this->silence_lookahead * (this->out_time + out_count - this->silence_time) + this->silence_time; | ||
828 | while ( this->emu_time < ahead_time && !(this->buf_remain | this->emu_track_ended_) ) | ||
829 | fill_buf( this ); | ||
830 | |||
831 | // fill with silence | ||
832 | pos = min( this->silence_count, out_count ); | ||
833 | memset( out, 0, pos * sizeof *out ); | ||
834 | this->silence_count -= pos; | ||
835 | |||
836 | if ( this->emu_time - this->silence_time > silence_max * stereo * this->sample_rate_ ) | ||
837 | { | ||
838 | this->track_ended = this->emu_track_ended_ = true; | ||
839 | this->silence_count = 0; | ||
840 | this->buf_remain = 0; | ||
841 | } | ||
842 | } | ||
843 | |||
844 | if ( this->buf_remain ) | ||
845 | { | ||
846 | // empty silence buf | ||
847 | long n = min( this->buf_remain, out_count - pos ); | ||
848 | memcpy( &out [pos], this->buf + (buf_size - this->buf_remain), n * sizeof *out ); | ||
849 | this->buf_remain -= n; | ||
850 | pos += n; | ||
851 | } | ||
852 | |||
853 | // generate remaining samples normally | ||
854 | long remain = out_count - pos; | ||
855 | if ( remain ) | ||
856 | { | ||
857 | emu_play( this, remain, out + pos ); | ||
858 | this->track_ended |= this->emu_track_ended_; | ||
859 | |||
860 | if ( !this->ignore_silence || this->out_time > this->fade_start ) | ||
861 | { | ||
862 | // check end for a new run of silence | ||
863 | long silence = count_silence( out + pos, remain ); | ||
864 | if ( silence < remain ) | ||
865 | this->silence_time = this->emu_time - silence; | ||
866 | |||
867 | if ( this->emu_time - this->silence_time >= buf_size ) | ||
868 | fill_buf( this ); // cause silence detection on next play() | ||
869 | } | ||
870 | } | ||
871 | |||
872 | if ( this->out_time > this->fade_start ) | ||
873 | handle_fade( this, out_count, out ); | ||
874 | } | ||
875 | this->out_time += out_count; | ||
876 | return 0; | ||
877 | } | ||