diff options
Diffstat (limited to 'apps/codecs/libgme/sgc_emu.c')
-rw-r--r-- | apps/codecs/libgme/sgc_emu.c | 673 |
1 files changed, 673 insertions, 0 deletions
diff --git a/apps/codecs/libgme/sgc_emu.c b/apps/codecs/libgme/sgc_emu.c new file mode 100644 index 0000000000..9abfc00d2c --- /dev/null +++ b/apps/codecs/libgme/sgc_emu.c | |||
@@ -0,0 +1,673 @@ | |||
1 | // Game_Music_Emu 0.6-pre. http://www.slack.net/~ant/ | ||
2 | |||
3 | #include "sgc_emu.h" | ||
4 | |||
5 | /* Copyright (C) 2009 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 | int const osc_count = sms_osc_count + fm_apu_osc_count; | ||
19 | |||
20 | int const stereo = 2; // number of channels for stereo | ||
21 | int const silence_max = 6; // seconds | ||
22 | int const silence_threshold = 0x10; | ||
23 | long const fade_block_size = 512; | ||
24 | int const fade_shift = 8; // fade ends with gain at 1.0 / (1 << fade_shift) | ||
25 | |||
26 | const char gme_wrong_file_type [] = "Wrong file type for this emulator"; | ||
27 | |||
28 | void clear_track_vars( struct Sgc_Emu* this ) | ||
29 | { | ||
30 | this->current_track = -1; | ||
31 | this->out_time = 0; | ||
32 | this->emu_time = 0; | ||
33 | this->emu_track_ended_ = true; | ||
34 | this->track_ended = true; | ||
35 | this->fade_start = INT_MAX / 2 + 1; | ||
36 | this->fade_step = 1; | ||
37 | this->silence_time = 0; | ||
38 | this->silence_count = 0; | ||
39 | this->buf_remain = 0; | ||
40 | /* warning(); // clear warning */ | ||
41 | } | ||
42 | |||
43 | void Sgc_init( struct Sgc_Emu* this ) | ||
44 | { | ||
45 | assert( offsetof (struct header_t,copyright [32]) == header_size ); | ||
46 | |||
47 | this->sample_rate = 0; | ||
48 | this->mute_mask_ = 0; | ||
49 | this->tempo = 1.0; | ||
50 | this->gain = 1.0; | ||
51 | this->voice_count = 0; | ||
52 | |||
53 | // defaults | ||
54 | this->max_initial_silence = 2; | ||
55 | this->silence_lookahead = 6; | ||
56 | this->ignore_silence = false; | ||
57 | |||
58 | Sms_apu_init( &this->apu ); | ||
59 | Fm_apu_create( &this->fm_apu ); | ||
60 | |||
61 | Rom_init( &this->rom, 0x4000 ); | ||
62 | Z80_init( &this->cpu ); | ||
63 | |||
64 | Sound_set_gain( this, 1.2 ); | ||
65 | |||
66 | // Unload | ||
67 | clear_track_vars( this ); | ||
68 | } | ||
69 | |||
70 | // Setup | ||
71 | |||
72 | blargg_err_t Sgc_load_mem( struct Sgc_Emu* this, const void* data, long size ) | ||
73 | { | ||
74 | RETURN_ERR( Rom_load( &this->rom, data, size, header_size, &this->header, 0 ) ); | ||
75 | |||
76 | if ( !valid_tag( &this->header ) ) | ||
77 | return gme_wrong_file_type; | ||
78 | |||
79 | /* if ( header.vers != 1 ) | ||
80 | warning( "Unknown file version" ); */ | ||
81 | |||
82 | /* if ( header.system > 2 ) | ||
83 | warning( "Unknown system" ); */ | ||
84 | |||
85 | addr_t load_addr = get_le16( this->header.load_addr ); | ||
86 | /* if ( load_addr < 0x400 ) | ||
87 | set_warning( "Invalid load address" ); */ | ||
88 | |||
89 | Rom_set_addr( &this->rom, load_addr ); | ||
90 | this->play_period = clock_rate( this ) / 60; | ||
91 | |||
92 | if ( sega_mapping( this ) && Fm_apu_supported() ) | ||
93 | RETURN_ERR( Fm_apu_init( &this->fm_apu, clock_rate( this ), clock_rate( this ) / 72 ) ); | ||
94 | |||
95 | this->m3u.size = 0; | ||
96 | this->track_count = this->header.song_count; | ||
97 | this->voice_count = sega_mapping( this ) ? osc_count : sms_osc_count; | ||
98 | |||
99 | Sms_apu_volume( &this->apu, this->gain ); | ||
100 | Fm_apu_volume( &this->fm_apu, this->gain ); | ||
101 | |||
102 | // Setup buffer | ||
103 | this->clock_rate_ = clock_rate( this ); | ||
104 | Buffer_clock_rate( &this->stereo_buf, clock_rate( this ) ); | ||
105 | this->buf_changed_count = Buffer_channels_changed_count( &this->stereo_buf ); | ||
106 | Sound_set_tempo( this, this->tempo ); | ||
107 | |||
108 | // Remute voices | ||
109 | Sound_mute_voices( this, this->mute_mask_ ); | ||
110 | return 0; | ||
111 | } | ||
112 | |||
113 | void Sound_set_voice( struct Sgc_Emu* this, int i, struct Blip_Buffer* c, struct Blip_Buffer* l, struct Blip_Buffer* r ) | ||
114 | { | ||
115 | if ( i < sms_osc_count ) | ||
116 | Sms_apu_set_output( &this->apu, i, c, l, r ); | ||
117 | else | ||
118 | Fm_apu_set_output( &this->fm_apu, c ); | ||
119 | } | ||
120 | |||
121 | blargg_err_t run_clocks( struct Sgc_Emu* this, blip_time_t* duration, int msec ) | ||
122 | { | ||
123 | #if defined(ROCKBOX) | ||
124 | (void) msec; | ||
125 | #endif | ||
126 | |||
127 | cpu_time_t t = *duration; | ||
128 | while ( Z80_time( &this->cpu ) < t ) | ||
129 | { | ||
130 | cpu_time_t next = min( t, this->next_play ); | ||
131 | if ( run_cpu( this, next ) ) | ||
132 | { | ||
133 | /* warning( "Unsupported CPU instruction" ); */ | ||
134 | Z80_set_time( &this->cpu, next ); | ||
135 | } | ||
136 | |||
137 | if ( this->cpu.r.pc == this->idle_addr ) | ||
138 | Z80_set_time( &this->cpu, next ); | ||
139 | |||
140 | if ( Z80_time( &this->cpu ) >= this->next_play ) | ||
141 | { | ||
142 | this->next_play += this->play_period; | ||
143 | if ( this->cpu.r.pc == this->idle_addr ) | ||
144 | jsr( this, this->header.play_addr ); | ||
145 | } | ||
146 | } | ||
147 | |||
148 | this->next_play -= t; | ||
149 | check( this->next_play >= 0 ); | ||
150 | Z80_adjust_time( &this->cpu, -t ); | ||
151 | |||
152 | Sms_apu_end_frame( &this->apu, t ); | ||
153 | if ( sega_mapping( this ) && this->fm_accessed ) | ||
154 | { | ||
155 | if ( Fm_apu_supported() ) | ||
156 | Fm_apu_end_frame( &this->fm_apu, t ); | ||
157 | /* else | ||
158 | warning( "FM sound not supported" ); */ | ||
159 | } | ||
160 | |||
161 | return 0; | ||
162 | } | ||
163 | |||
164 | // Emulation | ||
165 | |||
166 | void cpu_out( struct Sgc_Emu* this, cpu_time_t time, addr_t addr, int data ) | ||
167 | { | ||
168 | int port = addr & 0xFF; | ||
169 | |||
170 | if ( sega_mapping( this ) ) | ||
171 | { | ||
172 | switch ( port ) | ||
173 | { | ||
174 | case 0x06: | ||
175 | Sms_apu_write_ggstereo( &this->apu, time, data ); | ||
176 | return; | ||
177 | |||
178 | case 0x7E: | ||
179 | case 0x7F: | ||
180 | Sms_apu_write_data( &this->apu, time, data ); /* dprintf( "$7E<-%02X\n", data ); */ | ||
181 | return; | ||
182 | |||
183 | case 0xF0: | ||
184 | this->fm_accessed = true; | ||
185 | if ( Fm_apu_supported() ) | ||
186 | Fm_apu_write_addr( &this->fm_apu, data );//, dprintf( "$F0<-%02X\n", data ); | ||
187 | return; | ||
188 | |||
189 | case 0xF1: | ||
190 | this->fm_accessed = true; | ||
191 | if ( Fm_apu_supported() ) | ||
192 | Fm_apu_write_data( &this->fm_apu, time, data );//, dprintf( "$F1<-%02X\n", data ); | ||
193 | return; | ||
194 | } | ||
195 | } | ||
196 | else if ( port >= 0xE0 ) | ||
197 | { | ||
198 | Sms_apu_write_data( &this->apu, time, data ); | ||
199 | return; | ||
200 | } | ||
201 | } | ||
202 | |||
203 | void jsr( struct Sgc_Emu* this, byte addr [2] ) | ||
204 | { | ||
205 | *Z80_write( &this->cpu, --this->cpu.r.sp ) = this->idle_addr >> 8; | ||
206 | *Z80_write( &this->cpu, --this->cpu.r.sp ) = this->idle_addr & 0xFF; | ||
207 | this->cpu.r.pc = get_le16( addr ); | ||
208 | } | ||
209 | |||
210 | void set_bank( struct Sgc_Emu* this, int bank, void const* data ) | ||
211 | { | ||
212 | //dprintf( "map bank %d to %p\n", bank, (byte*) data - rom.at_addr( 0 ) ); | ||
213 | Z80_map_mem( &this->cpu, bank * this->rom.bank_size, this->rom.bank_size, this->unmapped_write, data ); | ||
214 | } | ||
215 | |||
216 | void cpu_write( struct Sgc_Emu* this, addr_t addr, int data ) | ||
217 | { | ||
218 | if ( (addr ^ 0xFFFC) > 3 || !sega_mapping( this ) ) | ||
219 | { | ||
220 | *Z80_write( &this->cpu, addr ) = data; | ||
221 | return; | ||
222 | } | ||
223 | |||
224 | switch ( addr ) | ||
225 | { | ||
226 | case 0xFFFC: | ||
227 | Z80_map_mem_rw( &this->cpu, 2 * this->rom.bank_size, this->rom.bank_size, this->ram2 ); | ||
228 | if ( data & 0x08 ) | ||
229 | break; | ||
230 | |||
231 | this->bank2 = this->ram2; | ||
232 | // FALL THROUGH | ||
233 | |||
234 | case 0xFFFF: { | ||
235 | bool rom_mapped = (Z80_read( &this->cpu, 2 * this->rom.bank_size ) == this->bank2); | ||
236 | this->bank2 = Rom_at_addr( &this->rom, data * this->rom.bank_size ); | ||
237 | if ( rom_mapped ) | ||
238 | set_bank( this, 2, this->bank2 ); | ||
239 | break; | ||
240 | } | ||
241 | |||
242 | case 0xFFFD: | ||
243 | set_bank( this, 0, Rom_at_addr( &this->rom, data * this->rom.bank_size ) ); | ||
244 | break; | ||
245 | |||
246 | case 0xFFFE: | ||
247 | set_bank( this, 1, Rom_at_addr( &this->rom, data * this->rom.bank_size ) ); | ||
248 | break; | ||
249 | } | ||
250 | } | ||
251 | |||
252 | blargg_err_t Sgc_set_sample_rate( struct Sgc_Emu* this, long rate ) | ||
253 | { | ||
254 | require( !this->sample_rate ); // sample rate can't be changed once set | ||
255 | Buffer_init( &this->stereo_buf ); | ||
256 | Buffer_set_sample_rate( &this->stereo_buf, rate, 1000 / 20 ); | ||
257 | |||
258 | // Set buffer bass | ||
259 | Buffer_bass_freq( &this->stereo_buf, 80 ); | ||
260 | |||
261 | this->sample_rate = rate; | ||
262 | return 0; | ||
263 | } | ||
264 | |||
265 | void Sound_mute_voice( struct Sgc_Emu* this, int index, bool mute ) | ||
266 | { | ||
267 | require( (unsigned) index < (unsigned) this->voice_count ); | ||
268 | int bit = 1 << index; | ||
269 | int mask = this->mute_mask_ | bit; | ||
270 | if ( !mute ) | ||
271 | mask ^= bit; | ||
272 | Sound_mute_voices( this, mask ); | ||
273 | } | ||
274 | |||
275 | void Sound_mute_voices( struct Sgc_Emu* this, int mask ) | ||
276 | { | ||
277 | require( this->sample_rate ); // sample rate must be set first | ||
278 | this->mute_mask_ = mask; | ||
279 | |||
280 | int i; | ||
281 | for ( i = this->voice_count; i--; ) | ||
282 | { | ||
283 | if ( mask & (1 << i) ) | ||
284 | { | ||
285 | Sound_set_voice( this, i, 0, 0, 0 ); | ||
286 | } | ||
287 | else | ||
288 | { | ||
289 | struct channel_t ch = Buffer_channel( &this->stereo_buf ); | ||
290 | assert( (ch.center && ch.left && ch.right) || | ||
291 | (!ch.center && !ch.left && !ch.right) ); // all or nothing | ||
292 | Sound_set_voice( this, i, ch.center, ch.left, ch.right ); | ||
293 | } | ||
294 | } | ||
295 | } | ||
296 | |||
297 | void Sound_set_tempo( struct Sgc_Emu* this, double t ) | ||
298 | { | ||
299 | require( this->sample_rate ); // sample rate must be set first | ||
300 | double const min = 0.02; | ||
301 | double const max = 4.00; | ||
302 | if ( t < min ) t = min; | ||
303 | if ( t > max ) t = max; | ||
304 | this->tempo = t; | ||
305 | |||
306 | this->play_period = (int) (clock_rate( this ) / (this->header.rate ? 50 : 60) / t); | ||
307 | } | ||
308 | |||
309 | void fill_buf( struct Sgc_Emu* this ) ICODE_ATTR; | ||
310 | blargg_err_t Sgc_start_track( struct Sgc_Emu* this, int track ) | ||
311 | { | ||
312 | clear_track_vars( this ); | ||
313 | |||
314 | // Remap track if playlist available | ||
315 | if ( this->m3u.size > 0 ) { | ||
316 | struct entry_t* e = &this->m3u.entries[track]; | ||
317 | track = e->track; | ||
318 | } | ||
319 | |||
320 | this->current_track = track; | ||
321 | |||
322 | if ( sega_mapping( this ) ) | ||
323 | { | ||
324 | Sms_apu_reset( &this->apu, 0, 0 ); | ||
325 | Fm_apu_reset( &this->fm_apu ); | ||
326 | this->fm_accessed = false; | ||
327 | } | ||
328 | else | ||
329 | { | ||
330 | Sms_apu_reset( &this->apu, 0x0003, 15 ); | ||
331 | } | ||
332 | |||
333 | memset( this->ram , 0, sizeof this->ram ); | ||
334 | memset( this->ram2, 0, sizeof this->ram2 ); | ||
335 | memset( this->vectors, 0xFF, sizeof this->vectors ); | ||
336 | Z80_reset( &this->cpu, this->unmapped_write, this->rom.unmapped ); | ||
337 | |||
338 | if ( sega_mapping( this ) ) | ||
339 | { | ||
340 | this->vectors_addr = 0x10000 - page_size; | ||
341 | this->idle_addr = this->vectors_addr; | ||
342 | int i; | ||
343 | for ( i = 1; i < 8; ++i ) | ||
344 | { | ||
345 | this->vectors [i*8 + 0] = 0xC3; // JP addr | ||
346 | this->vectors [i*8 + 1] = this->header.rst_addrs [i - 1] & 0xff; | ||
347 | this->vectors [i*8 + 2] = this->header.rst_addrs [i - 1] >> 8; | ||
348 | } | ||
349 | |||
350 | Z80_map_mem_rw( &this->cpu, 0xC000, 0x2000, this->ram ); | ||
351 | Z80_map_mem( &this->cpu, this->vectors_addr, page_size, this->unmapped_write, this->vectors ); | ||
352 | |||
353 | this->bank2 = NULL; | ||
354 | for ( i = 0; i < 4; ++i ) | ||
355 | cpu_write( this, 0xFFFC + i, this->header.mapping [i] ); | ||
356 | } | ||
357 | else | ||
358 | { | ||
359 | if ( !this->coleco_bios ) | ||
360 | return "Coleco BIOS not set"; /* BLARGG_ERR( BLARGG_ERR_CALLER, "Coleco BIOS not set" ); */ | ||
361 | |||
362 | this->vectors_addr = 0; | ||
363 | Z80_map_mem( &this->cpu, 0, 0x2000, this->unmapped_write, this->coleco_bios ); | ||
364 | int i; | ||
365 | for ( i = 0; i < 8; ++i ) | ||
366 | Z80_map_mem_rw( &this->cpu, 0x6000 + i*0x400, 0x400, this->ram ); | ||
367 | |||
368 | this->idle_addr = 0x2000; | ||
369 | Z80_map_mem( &this->cpu, 0x2000, page_size, this->unmapped_write, this->vectors ); | ||
370 | |||
371 | for ( i = 0; i < 0x8000 / this->rom.bank_size; ++i ) | ||
372 | { | ||
373 | int addr = 0x8000 + i*this->rom.bank_size; | ||
374 | Z80_map_mem( &this->cpu, addr, this->rom.bank_size, this->unmapped_write, Rom_at_addr( &this->rom, addr ) ); | ||
375 | } | ||
376 | } | ||
377 | |||
378 | this->cpu.r.sp = get_le16( this->header.stack_ptr ); | ||
379 | this->cpu.r.b.a = track; | ||
380 | this->next_play = this->play_period; | ||
381 | |||
382 | jsr( this, this->header.init_addr ); | ||
383 | |||
384 | Buffer_clear( &this->stereo_buf ); | ||
385 | |||
386 | this->emu_track_ended_ = false; | ||
387 | this->track_ended = false; | ||
388 | |||
389 | if ( !this->ignore_silence ) | ||
390 | { | ||
391 | // play until non-silence or end of track | ||
392 | long end; | ||
393 | for ( end = this->max_initial_silence * stereo * this->sample_rate; this->emu_time < end; ) | ||
394 | { | ||
395 | fill_buf( this ); | ||
396 | if ( this->buf_remain | (int) this->emu_track_ended_ ) | ||
397 | break; | ||
398 | } | ||
399 | |||
400 | this->emu_time = this->buf_remain; | ||
401 | this->out_time = 0; | ||
402 | this->silence_time = 0; | ||
403 | this->silence_count = 0; | ||
404 | } | ||
405 | /* return track_ended() ? warning() : 0; */ | ||
406 | return 0; | ||
407 | } | ||
408 | |||
409 | // Tell/Seek | ||
410 | |||
411 | blargg_long msec_to_samples( blargg_long msec, long sample_rate ) | ||
412 | { | ||
413 | blargg_long sec = msec / 1000; | ||
414 | msec -= sec * 1000; | ||
415 | return (sec * sample_rate + msec * sample_rate / 1000) * stereo; | ||
416 | } | ||
417 | |||
418 | long Track_tell( struct Sgc_Emu* this ) | ||
419 | { | ||
420 | blargg_long rate = this->sample_rate * stereo; | ||
421 | blargg_long sec = this->out_time / rate; | ||
422 | return sec * 1000 + (this->out_time - sec * rate) * 1000 / rate; | ||
423 | } | ||
424 | |||
425 | blargg_err_t Track_seek( struct Sgc_Emu* this, long msec ) | ||
426 | { | ||
427 | blargg_long time = msec_to_samples( msec, this->sample_rate ); | ||
428 | if ( time < this->out_time ) | ||
429 | RETURN_ERR( Sgc_start_track( this, this->current_track ) ); | ||
430 | return Track_skip( this, time - this->out_time ); | ||
431 | } | ||
432 | |||
433 | blargg_err_t skip_( struct Sgc_Emu* this, long count ) ICODE_ATTR; | ||
434 | blargg_err_t Track_skip( struct Sgc_Emu* this, long count ) | ||
435 | { | ||
436 | require( this->current_track >= 0 ); // start_track() must have been called already | ||
437 | this->out_time += count; | ||
438 | |||
439 | // remove from silence and buf first | ||
440 | { | ||
441 | long n = min( count, this->silence_count ); | ||
442 | this->silence_count -= n; | ||
443 | count -= n; | ||
444 | |||
445 | n = min( count, this->buf_remain ); | ||
446 | this->buf_remain -= n; | ||
447 | count -= n; | ||
448 | } | ||
449 | |||
450 | if ( count && !this->emu_track_ended_ ) | ||
451 | { | ||
452 | this->emu_time += count; | ||
453 | |||
454 | // End track if error | ||
455 | if ( skip_( this, count ) ) { | ||
456 | this->emu_track_ended_ = true; | ||
457 | } | ||
458 | } | ||
459 | |||
460 | if ( !(this->silence_count | this->buf_remain) ) // caught up to emulator, so update track ended | ||
461 | this->track_ended |= this->emu_track_ended_; | ||
462 | |||
463 | return 0; | ||
464 | } | ||
465 | |||
466 | blargg_err_t play_( struct Sgc_Emu* this, long count, sample_t* out ) ICODE_ATTR; | ||
467 | blargg_err_t skip_( struct Sgc_Emu* this, long count ) | ||
468 | { | ||
469 | // for long skip, mute sound | ||
470 | const long threshold = 30000; | ||
471 | if ( count > threshold ) | ||
472 | { | ||
473 | int saved_mute = this->mute_mask_; | ||
474 | Sound_mute_voices( this, ~0 ); | ||
475 | |||
476 | while ( count > threshold / 2 && !this->emu_track_ended_ ) | ||
477 | { | ||
478 | RETURN_ERR( play_( this, buf_size, this->buf ) ); | ||
479 | count -= buf_size; | ||
480 | } | ||
481 | |||
482 | Sound_mute_voices( this, saved_mute ); | ||
483 | } | ||
484 | |||
485 | while ( count && !this->emu_track_ended_ ) | ||
486 | { | ||
487 | long n = buf_size; | ||
488 | if ( n > count ) | ||
489 | n = count; | ||
490 | count -= n; | ||
491 | RETURN_ERR( play_( this, n, this->buf ) ); | ||
492 | } | ||
493 | return 0; | ||
494 | } | ||
495 | |||
496 | // Fading | ||
497 | |||
498 | void Track_set_fade( struct Sgc_Emu* this, long start_msec, long length_msec ) | ||
499 | { | ||
500 | this->fade_step = this->sample_rate * length_msec / (fade_block_size * fade_shift * 1000 / stereo); | ||
501 | this->fade_start = msec_to_samples( start_msec, this->sample_rate ); | ||
502 | } | ||
503 | |||
504 | // unit / pow( 2.0, (double) x / step ) | ||
505 | static int int_log( blargg_long x, int step, int unit ) | ||
506 | { | ||
507 | int shift = x / step; | ||
508 | int fraction = (x - shift * step) * unit / step; | ||
509 | return ((unit - fraction) + (fraction >> 1)) >> shift; | ||
510 | } | ||
511 | |||
512 | void handle_fade( struct Sgc_Emu* this, long out_count, sample_t* out ) | ||
513 | { | ||
514 | int i; | ||
515 | for ( i = 0; i < out_count; i += fade_block_size ) | ||
516 | { | ||
517 | int const shift = 14; | ||
518 | int const unit = 1 << shift; | ||
519 | int gain = int_log( (this->out_time + i - this->fade_start) / fade_block_size, | ||
520 | this->fade_step, unit ); | ||
521 | if ( gain < (unit >> fade_shift) ) | ||
522 | this->track_ended = this->emu_track_ended_ = true; | ||
523 | |||
524 | sample_t* io = &out [i]; | ||
525 | int count; | ||
526 | for ( count = min( fade_block_size, out_count - i ); count; --count ) | ||
527 | { | ||
528 | *io = (sample_t) ((*io * gain) >> shift); | ||
529 | ++io; | ||
530 | } | ||
531 | } | ||
532 | } | ||
533 | |||
534 | // Silence detection | ||
535 | |||
536 | void emu_play( struct Sgc_Emu* this, long count, sample_t* out ) | ||
537 | { | ||
538 | check( this->current_track_ >= 0 ); | ||
539 | this->emu_time += count; | ||
540 | if ( this->current_track >= 0 && !this->emu_track_ended_ ) { | ||
541 | // End track if error | ||
542 | if ( play_( this, count, out ) ) | ||
543 | this->emu_track_ended_ = true; | ||
544 | } | ||
545 | else | ||
546 | memset( out, 0, count * sizeof *out ); | ||
547 | } | ||
548 | |||
549 | // number of consecutive silent samples at end | ||
550 | static long count_silence( sample_t* begin, long size ) | ||
551 | { | ||
552 | sample_t first = *begin; | ||
553 | *begin = silence_threshold; // sentinel | ||
554 | sample_t* p = begin + size; | ||
555 | while ( (unsigned) (*--p + silence_threshold / 2) <= (unsigned) silence_threshold ) { } | ||
556 | *begin = first; | ||
557 | return size - (p - begin); | ||
558 | } | ||
559 | |||
560 | // fill internal buffer and check it for silence | ||
561 | void fill_buf( struct Sgc_Emu* this ) | ||
562 | { | ||
563 | assert( !this->buf_remain ); | ||
564 | if ( !this->emu_track_ended_ ) | ||
565 | { | ||
566 | emu_play( this, buf_size, this->buf ); | ||
567 | long silence = count_silence( this->buf, buf_size ); | ||
568 | if ( silence < buf_size ) | ||
569 | { | ||
570 | this->silence_time = this->emu_time - silence; | ||
571 | this->buf_remain = buf_size; | ||
572 | return; | ||
573 | } | ||
574 | } | ||
575 | this->silence_count += buf_size; | ||
576 | } | ||
577 | |||
578 | blargg_err_t Sgc_play( struct Sgc_Emu* this, long out_count, sample_t* out ) | ||
579 | { | ||
580 | if ( this->track_ended ) | ||
581 | { | ||
582 | memset( out, 0, out_count * sizeof *out ); | ||
583 | } | ||
584 | else | ||
585 | { | ||
586 | require( this->current_track >= 0 ); | ||
587 | require( out_count % stereo == 0 ); | ||
588 | |||
589 | assert( this->emu_time >= this->out_time ); | ||
590 | |||
591 | // prints nifty graph of how far ahead we are when searching for silence | ||
592 | //debug_printf( "%*s \n", int ((emu_time - out_time) * 7 / sample_rate()), "*" ); | ||
593 | |||
594 | long pos = 0; | ||
595 | if ( this->silence_count ) | ||
596 | { | ||
597 | // during a run of silence, run emulator at >=2x speed so it gets ahead | ||
598 | long ahead_time = this->silence_lookahead * (this->out_time + out_count - this->silence_time) + this->silence_time; | ||
599 | while ( this->emu_time < ahead_time && !(this->buf_remain | this->emu_track_ended_) ) | ||
600 | fill_buf( this ); | ||
601 | |||
602 | // fill with silence | ||
603 | pos = min( this->silence_count, out_count ); | ||
604 | memset( out, 0, pos * sizeof *out ); | ||
605 | this->silence_count -= pos; | ||
606 | |||
607 | if ( this->emu_time - this->silence_time > silence_max * stereo * this->sample_rate ) | ||
608 | { | ||
609 | this->track_ended = this->emu_track_ended_ = true; | ||
610 | this->silence_count = 0; | ||
611 | this->buf_remain = 0; | ||
612 | } | ||
613 | } | ||
614 | |||
615 | if ( this->buf_remain ) | ||
616 | { | ||
617 | // empty silence buf | ||
618 | long n = min( this->buf_remain, out_count - pos ); | ||
619 | memcpy( &out [pos], this->buf + (buf_size - this->buf_remain), n * sizeof *out ); | ||
620 | this->buf_remain -= n; | ||
621 | pos += n; | ||
622 | } | ||
623 | |||
624 | // generate remaining samples normally | ||
625 | long remain = out_count - pos; | ||
626 | if ( remain ) | ||
627 | { | ||
628 | emu_play( this, remain, out + pos ); | ||
629 | this->track_ended |= this->emu_track_ended_; | ||
630 | |||
631 | if ( !this->ignore_silence || this->out_time > this->fade_start ) | ||
632 | { | ||
633 | // check end for a new run of silence | ||
634 | long silence = count_silence( out + pos, remain ); | ||
635 | if ( silence < remain ) | ||
636 | this->silence_time = this->emu_time - silence; | ||
637 | |||
638 | if ( this->emu_time - this->silence_time >= buf_size ) | ||
639 | fill_buf( this ); // cause silence detection on next play() | ||
640 | } | ||
641 | } | ||
642 | |||
643 | if ( this->out_time > this->fade_start ) | ||
644 | handle_fade( this, out_count, out ); | ||
645 | } | ||
646 | this->out_time += out_count; | ||
647 | return 0; | ||
648 | } | ||
649 | |||
650 | blargg_err_t play_( struct Sgc_Emu* this, long count, sample_t* out ) | ||
651 | { | ||
652 | long remain = count; | ||
653 | while ( remain ) | ||
654 | { | ||
655 | remain -= Buffer_read_samples( &this->stereo_buf, &out [count - remain], remain ); | ||
656 | if ( remain ) | ||
657 | { | ||
658 | if ( this->buf_changed_count != Buffer_channels_changed_count( &this->stereo_buf ) ) | ||
659 | { | ||
660 | this->buf_changed_count = Buffer_channels_changed_count( &this->stereo_buf ); | ||
661 | |||
662 | // Remute voices | ||
663 | Sound_mute_voices( this, this->mute_mask_ ); | ||
664 | } | ||
665 | int msec = Buffer_length( &this->stereo_buf ); | ||
666 | blip_time_t clocks_emulated = msec * this->clock_rate_ / 1000 - 100; | ||
667 | RETURN_ERR( run_clocks( this, &clocks_emulated, msec ) ); | ||
668 | assert( clocks_emulated ); | ||
669 | Buffer_end_frame( &this->stereo_buf, clocks_emulated ); | ||
670 | } | ||
671 | } | ||
672 | return 0; | ||
673 | } | ||