diff options
Diffstat (limited to 'apps/codecs/libgme/gbs_emu.c')
-rw-r--r-- | apps/codecs/libgme/gbs_emu.c | 631 |
1 files changed, 631 insertions, 0 deletions
diff --git a/apps/codecs/libgme/gbs_emu.c b/apps/codecs/libgme/gbs_emu.c new file mode 100644 index 0000000000..693e84a39b --- /dev/null +++ b/apps/codecs/libgme/gbs_emu.c | |||
@@ -0,0 +1,631 @@ | |||
1 | // Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ | ||
2 | |||
3 | #include "gbs_emu.h" | ||
4 | |||
5 | #include "blargg_endian.h" | ||
6 | #include "blargg_source.h" | ||
7 | |||
8 | /* Copyright (C) 2003-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 | |||
20 | const char gme_wrong_file_type [] ICONST_ATTR = "Wrong file type for this emulator"; | ||
21 | |||
22 | int const idle_addr = 0xF00D; | ||
23 | int const tempo_unit = 16; | ||
24 | |||
25 | int const stereo = 2; // number of channels for stereo | ||
26 | int const silence_max = 6; // seconds | ||
27 | int const silence_threshold = 0x10; | ||
28 | long const fade_block_size = 512; | ||
29 | int const fade_shift = 8; // fade ends with gain at 1.0 / (1 << fade_shift) | ||
30 | |||
31 | void clear_track_vars( struct Gbs_Emu* this ) | ||
32 | { | ||
33 | this->current_track_ = -1; | ||
34 | this->out_time = 0; | ||
35 | this->emu_time = 0; | ||
36 | this->emu_track_ended_ = true; | ||
37 | this->track_ended = true; | ||
38 | this->fade_start = LONG_MAX / 2 + 1; | ||
39 | this->fade_step = 1; | ||
40 | this->silence_time = 0; | ||
41 | this->silence_count = 0; | ||
42 | this->buf_remain = 0; | ||
43 | } | ||
44 | |||
45 | void Gbs_init( struct Gbs_Emu* this ) | ||
46 | { | ||
47 | this->sample_rate_ = 0; | ||
48 | this->mute_mask_ = 0; | ||
49 | this->tempo_ = 1.0; | ||
50 | |||
51 | // Unload | ||
52 | this->header.timer_mode = 0; | ||
53 | clear_track_vars( this ); | ||
54 | |||
55 | this->ignore_silence = false; | ||
56 | this->silence_lookahead = 6; | ||
57 | this->max_initial_silence = 21; | ||
58 | Sound_set_gain( this, 1.2 ); | ||
59 | |||
60 | Rom_init( &this->rom, 0x4000 ); | ||
61 | |||
62 | Apu_init( &this->apu ); | ||
63 | Cpu_init( &this->cpu ); | ||
64 | |||
65 | this->tempo = tempo_unit; | ||
66 | this->sound_hardware = sound_gbs; | ||
67 | |||
68 | // Reduce apu sound clicks? | ||
69 | Apu_reduce_clicks( &this->apu, true ); | ||
70 | } | ||
71 | |||
72 | static blargg_err_t check_gbs_header( void const* header ) | ||
73 | { | ||
74 | if ( memcmp( header, "GBS", 3 ) ) | ||
75 | return gme_wrong_file_type; | ||
76 | return 0; | ||
77 | } | ||
78 | |||
79 | // Setup | ||
80 | |||
81 | blargg_err_t Gbs_load( struct Gbs_Emu* this, void* data, long size ) | ||
82 | { | ||
83 | // Unload | ||
84 | this->header.timer_mode = 0; | ||
85 | this->voice_count_ = 0; | ||
86 | this->m3u.size = 0; | ||
87 | clear_track_vars( this ); | ||
88 | |||
89 | assert( offsetof (struct header_t,copyright [32]) == header_size ); | ||
90 | RETURN_ERR( Rom_load( &this->rom, data, size, header_size, &this->header, 0 ) ); | ||
91 | |||
92 | RETURN_ERR( check_gbs_header( &this->header ) ); | ||
93 | |||
94 | /* Ignore warnings? */ | ||
95 | /*if ( header_.vers != 1 ) | ||
96 | warning( "Unknown file version" ); | ||
97 | |||
98 | if ( header_.timer_mode & 0x78 ) | ||
99 | warning( "Invalid timer mode" ); */ | ||
100 | |||
101 | /* unsigned load_addr = get_le16( this->header.load_addr ); */ | ||
102 | /* if ( (header_.load_addr [1] | header_.init_addr [1] | header_.play_addr [1]) > 0x7F || | ||
103 | load_addr < 0x400 ) | ||
104 | warning( "Invalid load/init/play address" ); */ | ||
105 | |||
106 | unsigned load_addr = get_le16( this->header.load_addr ); | ||
107 | /* if ( (this->header.load_addr [1] | this->header.init_addr [1] | this->header.play_addr [1]) > 0x7F || | ||
108 | load_addr < 0x400 ) | ||
109 | warning( "Invalid load/init/play address" ); */ | ||
110 | |||
111 | this->cpu.rst_base = load_addr; | ||
112 | Rom_set_addr( &this->rom, load_addr ); | ||
113 | |||
114 | this->voice_count_ = osc_count; | ||
115 | Apu_volume( &this->apu, this->gain_ ); | ||
116 | |||
117 | // Change clock rate & setup buffer | ||
118 | this->clock_rate_ = 4194304; | ||
119 | Buffer_clock_rate( &this->stereo_buf, 4194304 ); | ||
120 | this->buf_changed_count = Buffer_channels_changed_count( &this->stereo_buf ); | ||
121 | |||
122 | // Post load | ||
123 | Sound_set_tempo( this, this->tempo_ ); | ||
124 | |||
125 | // Remute voices | ||
126 | Sound_mute_voices( this, this->mute_mask_ ); | ||
127 | |||
128 | // Reset track count | ||
129 | this->track_count = this->header.track_count; | ||
130 | return 0; | ||
131 | } | ||
132 | |||
133 | // Emulation | ||
134 | |||
135 | // see gb_cpu_io.h for read/write functions | ||
136 | |||
137 | void Set_bank( struct Gbs_Emu* this, int n ) | ||
138 | { | ||
139 | addr_t addr = mask_addr( n * this->rom.bank_size, this->rom.mask ); | ||
140 | if ( addr == 0 && this->rom.size > this->rom.bank_size ) | ||
141 | addr = this->rom.bank_size; // MBC1&2 behavior, bank 0 acts like bank 1 | ||
142 | Cpu_map_code( &this->cpu, this->rom.bank_size, this->rom.bank_size, Rom_at_addr( &this->rom, addr ) ); | ||
143 | } | ||
144 | |||
145 | void Update_timer( struct Gbs_Emu* this ) | ||
146 | { | ||
147 | this->play_period = 70224 / tempo_unit; /// 59.73 Hz | ||
148 | |||
149 | if ( this->header.timer_mode & 0x04 ) | ||
150 | { | ||
151 | // Using custom rate | ||
152 | static byte const rates [4] = { 6, 0, 2, 4 }; | ||
153 | // TODO: emulate double speed CPU mode rather than halving timer rate | ||
154 | int double_speed = this->header.timer_mode >> 7; | ||
155 | int shift = rates [this->ram [hi_page + 7] & 3] - double_speed; | ||
156 | this->play_period = (256 - this->ram [hi_page + 6]) << shift; | ||
157 | } | ||
158 | |||
159 | this->play_period *= this->tempo; | ||
160 | } | ||
161 | |||
162 | // Jumps to routine, given pointer to address in file header. Pushes idle_addr | ||
163 | // as return address, NOT old PC. | ||
164 | void Jsr_then_stop( struct Gbs_Emu* this, byte const addr [] ) | ||
165 | { | ||
166 | check( this->cpu.r.sp == get_le16( this->header.stack_ptr ) ); | ||
167 | this->cpu.r.pc = get_le16( addr ); | ||
168 | Write_mem( this, --this->cpu.r.sp, idle_addr >> 8 ); | ||
169 | Write_mem( this, --this->cpu.r.sp, idle_addr ); | ||
170 | } | ||
171 | |||
172 | blargg_err_t Run_until( struct Gbs_Emu* this, int end ) | ||
173 | { | ||
174 | this->end_time = end; | ||
175 | Cpu_set_time( &this->cpu, Cpu_time( &this->cpu ) - end ); | ||
176 | while ( true ) | ||
177 | { | ||
178 | Run_cpu( this ); | ||
179 | if ( Cpu_time( &this->cpu ) >= 0 ) | ||
180 | break; | ||
181 | |||
182 | if ( this->cpu.r.pc == idle_addr ) | ||
183 | { | ||
184 | if ( this->next_play > this->end_time ) | ||
185 | { | ||
186 | Cpu_set_time( &this->cpu, 0 ); | ||
187 | break; | ||
188 | } | ||
189 | |||
190 | if ( Cpu_time( &this->cpu ) < this->next_play - this->end_time ) | ||
191 | Cpu_set_time( &this->cpu, this->next_play - this->end_time ); | ||
192 | this->next_play += this->play_period; | ||
193 | Jsr_then_stop( this, this->header.play_addr ); | ||
194 | } | ||
195 | else if ( this->cpu.r.pc > 0xFFFF ) | ||
196 | { | ||
197 | /* warning( "PC wrapped around\n" ); */ | ||
198 | this->cpu.r.pc &= 0xFFFF; | ||
199 | } | ||
200 | else | ||
201 | { | ||
202 | /* warning( "Emulation error (illegal/unsupported instruction)" ); */ | ||
203 | this->cpu.r.pc = (this->cpu.r.pc + 1) & 0xFFFF; | ||
204 | Cpu_set_time( &this->cpu, Cpu_time( &this->cpu ) + 6 ); | ||
205 | } | ||
206 | } | ||
207 | |||
208 | return 0; | ||
209 | } | ||
210 | |||
211 | blargg_err_t End_frame( struct Gbs_Emu* this, int end ) | ||
212 | { | ||
213 | RETURN_ERR( Run_until( this, end ) ); | ||
214 | |||
215 | this->next_play -= end; | ||
216 | if ( this->next_play < 0 ) // happens when play routine takes too long | ||
217 | { | ||
218 | #if !defined(GBS_IGNORE_STARVED_PLAY) | ||
219 | check( false ); | ||
220 | #endif | ||
221 | this->next_play = 0; | ||
222 | } | ||
223 | |||
224 | Apu_end_frame( &this->apu, end ); | ||
225 | |||
226 | return 0; | ||
227 | } | ||
228 | |||
229 | blargg_err_t Run_clocks( struct Gbs_Emu* this, blip_time_t duration ) | ||
230 | { | ||
231 | return End_frame( this, duration ); | ||
232 | } | ||
233 | |||
234 | blargg_err_t play_( struct Gbs_Emu* this, long count, sample_t* out ) | ||
235 | { | ||
236 | long remain = count; | ||
237 | while ( remain ) | ||
238 | { | ||
239 | remain -= Buffer_read_samples( &this->stereo_buf, &out [count - remain], remain ); | ||
240 | if ( remain ) | ||
241 | { | ||
242 | if ( this->buf_changed_count != Buffer_channels_changed_count( &this->stereo_buf ) ) | ||
243 | { | ||
244 | this->buf_changed_count = Buffer_channels_changed_count( &this->stereo_buf ); | ||
245 | |||
246 | // Remute voices | ||
247 | Sound_mute_voices( this, this->mute_mask_ ); | ||
248 | } | ||
249 | int msec = Buffer_length( &this->stereo_buf ); | ||
250 | blip_time_t clocks_emulated = (blargg_long) msec * this->clock_rate_ / 1000; | ||
251 | RETURN_ERR( Run_clocks( this, clocks_emulated ) ); | ||
252 | assert( clocks_emulated ); | ||
253 | Buffer_end_frame( &this->stereo_buf, clocks_emulated ); | ||
254 | } | ||
255 | } | ||
256 | return 0; | ||
257 | } | ||
258 | |||
259 | blargg_err_t Gbs_set_sample_rate( struct Gbs_Emu* this, long rate ) | ||
260 | { | ||
261 | require( !this->sample_rate_ ); // sample rate can't be changed once set | ||
262 | Buffer_init( &this->stereo_buf ); | ||
263 | RETURN_ERR( Buffer_set_sample_rate( &this->stereo_buf, rate, 1000 / 20 ) ); | ||
264 | |||
265 | // Set bass frequency | ||
266 | Buffer_bass_freq( &this->stereo_buf, 300 ); | ||
267 | |||
268 | this->sample_rate_ = rate; | ||
269 | return 0; | ||
270 | } | ||
271 | |||
272 | |||
273 | // Sound | ||
274 | |||
275 | void Sound_mute_voice( struct Gbs_Emu* this, int index, bool mute ) | ||
276 | { | ||
277 | require( (unsigned) index < (unsigned) this->voice_count_ ); | ||
278 | int bit = 1 << index; | ||
279 | int mask = this->mute_mask_ | bit; | ||
280 | if ( !mute ) | ||
281 | mask ^= bit; | ||
282 | Sound_mute_voices( this, mask ); | ||
283 | } | ||
284 | |||
285 | void Sound_mute_voices( struct Gbs_Emu* this, int mask ) | ||
286 | { | ||
287 | require( this->sample_rate_ ); // sample rate must be set first | ||
288 | this->mute_mask_ = mask; | ||
289 | |||
290 | int i; | ||
291 | for ( i = this->voice_count_; i--; ) | ||
292 | { | ||
293 | if ( mask & (1 << i) ) | ||
294 | { | ||
295 | Apu_set_output( &this->apu, i, 0, 0, 0 ); | ||
296 | } | ||
297 | else | ||
298 | { | ||
299 | struct channel_t ch = Buffer_channel( &this->stereo_buf ); | ||
300 | assert( (ch.center && ch.left && ch.right) || | ||
301 | (!ch.center && !ch.left && !ch.right) ); // all or nothing | ||
302 | Apu_set_output( &this->apu, i, ch.center, ch.left, ch.right ); | ||
303 | } | ||
304 | } | ||
305 | } | ||
306 | |||
307 | void Sound_set_tempo( struct Gbs_Emu* this, double t ) | ||
308 | { | ||
309 | require( this->sample_rate_ ); // sample rate must be set first | ||
310 | double const min = 0.02; | ||
311 | double const max = 4.00; | ||
312 | if ( t < min ) t = min; | ||
313 | if ( t > max ) t = max; | ||
314 | this->tempo_ = t; | ||
315 | |||
316 | this->tempo = (int) (tempo_unit / t + 0.5 ); | ||
317 | Apu_set_tempo( &this->apu, t ); | ||
318 | Update_timer( this ); | ||
319 | } | ||
320 | |||
321 | void fill_buf( struct Gbs_Emu* this ); | ||
322 | blargg_err_t Gbs_start_track( struct Gbs_Emu* this, int track ) | ||
323 | { | ||
324 | clear_track_vars( this ); | ||
325 | |||
326 | // Remap track if playlist available | ||
327 | if ( this->m3u.size > 0 ) { | ||
328 | struct entry_t* e = &this->m3u.entries[track]; | ||
329 | track = e->track; | ||
330 | } | ||
331 | |||
332 | this->current_track_ = track; | ||
333 | |||
334 | Buffer_clear( &this->stereo_buf ); | ||
335 | |||
336 | // Reset APU to state expected by most rips | ||
337 | static byte const sound_data [] ICONST_ATTR = { | ||
338 | 0x80, 0xBF, 0x00, 0x00, 0xB8, // square 1 DAC disabled | ||
339 | 0x00, 0x3F, 0x00, 0x00, 0xB8, // square 2 DAC disabled | ||
340 | 0x7F, 0xFF, 0x9F, 0x00, 0xB8, // wave DAC disabled | ||
341 | 0x00, 0xFF, 0x00, 0x00, 0xB8, // noise DAC disabled | ||
342 | 0x77, 0xFF, 0x80, // max volume, all chans in center, power on | ||
343 | }; | ||
344 | |||
345 | enum sound_t mode = this->sound_hardware; | ||
346 | if ( mode == sound_gbs ) | ||
347 | mode = (this->header.timer_mode & 0x80) ? sound_cgb : sound_dmg; | ||
348 | |||
349 | Apu_reset( &this->apu, (enum gb_mode_t) mode, false ); | ||
350 | Apu_write_register( &this->apu, 0, 0xFF26, 0x80 ); // power on | ||
351 | int i; | ||
352 | for ( i = 0; i < (int) sizeof sound_data; i++ ) | ||
353 | Apu_write_register( &this->apu, 0, i + io_addr, sound_data [i] ); | ||
354 | Apu_end_frame( &this->apu, 1 ); // necessary to get click out of the way */ | ||
355 | |||
356 | memset( this->ram, 0, 0x4000 ); | ||
357 | memset( this->ram + 0x4000, 0xFF, 0x1F80 ); | ||
358 | memset( this->ram + 0x5F80, 0, sizeof this->ram - 0x5F80 ); | ||
359 | this->ram [hi_page] = 0; // joypad reads back as 0 | ||
360 | this->ram [idle_addr - ram_addr] = 0xED; // illegal instruction | ||
361 | this->ram [hi_page + 6] = this->header.timer_modulo; | ||
362 | this->ram [hi_page + 7] = this->header.timer_mode; | ||
363 | |||
364 | Cpu_reset( &this->cpu, this->rom.unmapped ); | ||
365 | Cpu_map_code( &this->cpu, ram_addr, 0x10000 - ram_addr, this->ram ); | ||
366 | Cpu_map_code( &this->cpu, 0, this->rom.bank_size, Rom_at_addr( &this->rom, 0 ) ); | ||
367 | Set_bank( this, this->rom.size > this->rom.bank_size ); | ||
368 | |||
369 | Update_timer( this ); | ||
370 | this->next_play = this->play_period; | ||
371 | this->cpu.r.rp.fa = track; | ||
372 | this->cpu.r.sp = get_le16( this->header.stack_ptr ); | ||
373 | this->cpu_time = 0; | ||
374 | Jsr_then_stop( this, this->header.init_addr ); | ||
375 | |||
376 | this->emu_track_ended_ = false; | ||
377 | this->track_ended = false; | ||
378 | |||
379 | if ( !this->ignore_silence ) | ||
380 | { | ||
381 | // play until non-silence or end of track | ||
382 | long end; | ||
383 | for ( end = this->max_initial_silence * stereo * this->sample_rate_; this->emu_time < end; ) | ||
384 | { | ||
385 | fill_buf( this ); | ||
386 | if ( this->buf_remain | (int) this->emu_track_ended_ ) | ||
387 | break; | ||
388 | } | ||
389 | |||
390 | this->emu_time = this->buf_remain; | ||
391 | this->out_time = 0; | ||
392 | this->silence_time = 0; | ||
393 | this->silence_count = 0; | ||
394 | } | ||
395 | /* return track_ended() ? warning() : 0; */ | ||
396 | return 0; | ||
397 | } | ||
398 | |||
399 | |||
400 | // Track | ||
401 | |||
402 | blargg_long msec_to_samples( blargg_long msec, long sample_rate ) | ||
403 | { | ||
404 | blargg_long sec = msec / 1000; | ||
405 | msec -= sec * 1000; | ||
406 | return (sec * sample_rate + msec * sample_rate / 1000) * stereo; | ||
407 | } | ||
408 | |||
409 | long Track_tell( struct Gbs_Emu* this ) | ||
410 | { | ||
411 | blargg_long rate = this->sample_rate_ * stereo; | ||
412 | blargg_long sec = this->out_time / rate; | ||
413 | return sec * 1000 + (this->out_time - sec * rate) * 1000 / rate; | ||
414 | } | ||
415 | |||
416 | blargg_err_t Track_seek( struct Gbs_Emu* this, long msec ) | ||
417 | { | ||
418 | blargg_long time = msec_to_samples( msec, this->sample_rate_ ); | ||
419 | if ( time < this->out_time ) | ||
420 | RETURN_ERR( Gbs_start_track( this, this->current_track_ ) ); | ||
421 | return Track_skip( this, time - this->out_time ); | ||
422 | } | ||
423 | |||
424 | blargg_err_t skip_( struct Gbs_Emu* this, long count ) | ||
425 | { | ||
426 | // for long skip, mute sound | ||
427 | const long threshold = 30000; | ||
428 | if ( count > threshold ) | ||
429 | { | ||
430 | int saved_mute = this->mute_mask_; | ||
431 | Sound_mute_voices( this, ~0 ); | ||
432 | |||
433 | while ( count > threshold / 2 && !this->emu_track_ended_ ) | ||
434 | { | ||
435 | RETURN_ERR( play_( this, buf_size, this->buf ) ); | ||
436 | count -= buf_size; | ||
437 | } | ||
438 | |||
439 | Sound_mute_voices( this, saved_mute ); | ||
440 | } | ||
441 | |||
442 | while ( count && !this->emu_track_ended_ ) | ||
443 | { | ||
444 | long n = buf_size; | ||
445 | if ( n > count ) | ||
446 | n = count; | ||
447 | count -= n; | ||
448 | RETURN_ERR( play_( this, n, this->buf ) ); | ||
449 | } | ||
450 | return 0; | ||
451 | } | ||
452 | |||
453 | blargg_err_t Track_skip( struct Gbs_Emu* this, long count ) | ||
454 | { | ||
455 | require( this->current_track_ >= 0 ); // start_track() must have been called already | ||
456 | this->out_time += count; | ||
457 | |||
458 | // remove from silence and buf first | ||
459 | { | ||
460 | long n = min( count, this->silence_count ); | ||
461 | this->silence_count -= n; | ||
462 | count -= n; | ||
463 | |||
464 | n = min( count, this->buf_remain ); | ||
465 | this->buf_remain -= n; | ||
466 | count -= n; | ||
467 | } | ||
468 | |||
469 | if ( count && !this->emu_track_ended_ ) | ||
470 | { | ||
471 | this->emu_time += count; | ||
472 | // End track if error | ||
473 | if ( skip_( this, count ) ) | ||
474 | this->emu_track_ended_ = true; | ||
475 | } | ||
476 | |||
477 | if ( !(this->silence_count | this->buf_remain) ) // caught up to emulator, so update track ended | ||
478 | this->track_ended |= this->emu_track_ended_; | ||
479 | |||
480 | return 0; | ||
481 | } | ||
482 | |||
483 | // Fading | ||
484 | |||
485 | void Track_set_fade( struct Gbs_Emu* this, long start_msec, long length_msec ) | ||
486 | { | ||
487 | this->fade_step = this->sample_rate_ * length_msec / (fade_block_size * fade_shift * 1000 / stereo); | ||
488 | this->fade_start = msec_to_samples( start_msec, this->sample_rate_ ); | ||
489 | } | ||
490 | |||
491 | // unit / pow( 2.0, (double) x / step ) | ||
492 | static int int_log( blargg_long x, int step, int unit ) | ||
493 | { | ||
494 | int shift = x / step; | ||
495 | int fraction = (x - shift * step) * unit / step; | ||
496 | return ((unit - fraction) + (fraction >> 1)) >> shift; | ||
497 | } | ||
498 | |||
499 | void handle_fade( struct Gbs_Emu* this, long out_count, sample_t* out ) | ||
500 | { | ||
501 | int i; | ||
502 | for ( i = 0; i < out_count; i += fade_block_size ) | ||
503 | { | ||
504 | int const shift = 14; | ||
505 | int const unit = 1 << shift; | ||
506 | int gain = int_log( (this->out_time + i - this->fade_start) / fade_block_size, | ||
507 | this->fade_step, unit ); | ||
508 | if ( gain < (unit >> fade_shift) ) | ||
509 | this->track_ended = this->emu_track_ended_ = true; | ||
510 | |||
511 | sample_t* io = &out [i]; | ||
512 | int count; | ||
513 | for ( count = min( fade_block_size, out_count - i ); count; --count ) | ||
514 | { | ||
515 | *io = (sample_t) ((*io * gain) >> shift); | ||
516 | ++io; | ||
517 | } | ||
518 | } | ||
519 | } | ||
520 | |||
521 | // Silence detection | ||
522 | |||
523 | void emu_play( struct Gbs_Emu* this, long count, sample_t* out ) | ||
524 | { | ||
525 | check( current_track_ >= 0 ); | ||
526 | this->emu_time += count; | ||
527 | if ( this->current_track_ >= 0 && !this->emu_track_ended_ ) { | ||
528 | // End track if error | ||
529 | if ( play_( this, count, out ) ) this->emu_track_ended_ = true; | ||
530 | } | ||
531 | else | ||
532 | memset( out, 0, count * sizeof *out ); | ||
533 | } | ||
534 | |||
535 | // number of consecutive silent samples at end | ||
536 | static long count_silence( sample_t* begin, long size ) | ||
537 | { | ||
538 | sample_t first = *begin; | ||
539 | *begin = silence_threshold; // sentinel | ||
540 | sample_t* p = begin + size; | ||
541 | while ( (unsigned) (*--p + silence_threshold / 2) <= (unsigned) silence_threshold ) { } | ||
542 | *begin = first; | ||
543 | return size - (p - begin); | ||
544 | } | ||
545 | |||
546 | // fill internal buffer and check it for silence | ||
547 | void fill_buf( struct Gbs_Emu* this ) | ||
548 | { | ||
549 | assert( !this->buf_remain ); | ||
550 | if ( !this->emu_track_ended_ ) | ||
551 | { | ||
552 | emu_play( this, buf_size, this->buf ); | ||
553 | long silence = count_silence( this->buf, buf_size ); | ||
554 | if ( silence < buf_size ) | ||
555 | { | ||
556 | this->silence_time = this->emu_time - silence; | ||
557 | this->buf_remain = buf_size; | ||
558 | return; | ||
559 | } | ||
560 | } | ||
561 | this->silence_count += buf_size; | ||
562 | } | ||
563 | |||
564 | blargg_err_t Gbs_play( struct Gbs_Emu* this, long out_count, sample_t* out ) | ||
565 | { | ||
566 | if ( this->track_ended ) | ||
567 | { | ||
568 | memset( out, 0, out_count * sizeof *out ); | ||
569 | } | ||
570 | else | ||
571 | { | ||
572 | require( this->current_track_ >= 0 ); | ||
573 | require( out_count % stereo == 0 ); | ||
574 | |||
575 | assert( this->emu_time >= this->out_time ); | ||
576 | |||
577 | long pos = 0; | ||
578 | if ( this->silence_count ) | ||
579 | { | ||
580 | // during a run of silence, run emulator at >=2x speed so it gets ahead | ||
581 | long ahead_time = this->silence_lookahead * (this->out_time + out_count - this->silence_time) + this->silence_time; | ||
582 | while ( this->emu_time < ahead_time && !(this->buf_remain | this->emu_track_ended_) ) | ||
583 | fill_buf( this ); | ||
584 | |||
585 | // fill with silence | ||
586 | pos = min( this->silence_count, out_count ); | ||
587 | memset( out, 0, pos * sizeof *out ); | ||
588 | this->silence_count -= pos; | ||
589 | |||
590 | if ( this->emu_time - this->silence_time > silence_max * stereo * this->sample_rate_ ) | ||
591 | { | ||
592 | this->track_ended = this->emu_track_ended_ = true; | ||
593 | this->silence_count = 0; | ||
594 | this->buf_remain = 0; | ||
595 | } | ||
596 | } | ||
597 | |||
598 | if ( this->buf_remain ) | ||
599 | { | ||
600 | // empty silence buf | ||
601 | long n = min( this->buf_remain, out_count - pos ); | ||
602 | memcpy( &out [pos], this->buf + (buf_size - this->buf_remain), n * sizeof *out ); | ||
603 | this->buf_remain -= n; | ||
604 | pos += n; | ||
605 | } | ||
606 | |||
607 | // generate remaining samples normally | ||
608 | long remain = out_count - pos; | ||
609 | if ( remain ) | ||
610 | { | ||
611 | emu_play( this, remain, out + pos ); | ||
612 | this->track_ended |= this->emu_track_ended_; | ||
613 | |||
614 | if ( !this->ignore_silence || this->out_time > this->fade_start ) | ||
615 | { | ||
616 | // check end for a new run of silence | ||
617 | long silence = count_silence( out + pos, remain ); | ||
618 | if ( silence < remain ) | ||
619 | this->silence_time = this->emu_time - silence; | ||
620 | |||
621 | if ( this->emu_time - this->silence_time >= buf_size ) | ||
622 | fill_buf( this ); // cause silence detection on next play() | ||
623 | } | ||
624 | } | ||
625 | |||
626 | if ( this->out_time > this->fade_start ) | ||
627 | handle_fade( this, out_count, out ); | ||
628 | } | ||
629 | this->out_time += out_count; | ||
630 | return 0; | ||
631 | } | ||