summaryrefslogtreecommitdiff
path: root/lib/rbcodec/codecs/libgme/kss_emu.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/rbcodec/codecs/libgme/kss_emu.c')
-rw-r--r--lib/rbcodec/codecs/libgme/kss_emu.c714
1 files changed, 714 insertions, 0 deletions
diff --git a/lib/rbcodec/codecs/libgme/kss_emu.c b/lib/rbcodec/codecs/libgme/kss_emu.c
new file mode 100644
index 0000000000..ba80ef613e
--- /dev/null
+++ b/lib/rbcodec/codecs/libgme/kss_emu.c
@@ -0,0 +1,714 @@
1// Game_Music_Emu 0.6-pre. http://www.slack.net/~ant/
2
3#include "kss_emu.h"
4
5#include "blargg_endian.h"
6
7/* Copyright (C) 2006 Shay Green. This module is free software; you
8can redistribute it and/or modify it under the terms of the GNU Lesser
9General Public License as published by the Free Software Foundation; either
10version 2.1 of the License, or (at your option) any later version. This
11module is distributed in the hope that it will be useful, but WITHOUT ANY
12WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
14details. You should have received a copy of the GNU Lesser General Public
15License along with this module; if not, write to the Free Software Foundation,
16Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
17
18#include "blargg_source.h"
19
20int const clock_rate = 3579545;
21
22const char gme_wrong_file_type [] = "Wrong file type for this emulator";
23
24static void clear_track_vars( struct Kss_Emu* this )
25{
26 this->current_track = -1;
27 track_stop( &this->track_filter );
28}
29
30static blargg_err_t init_opl_apu( enum opl_type_t type, struct Opl_Apu* out )
31{
32 blip_time_t const period = 72;
33 int const rate = clock_rate / period;
34 return Opl_init( out, rate * period, rate, period, type );
35}
36
37void Kss_init( struct Kss_Emu* this )
38{
39 this->sample_rate = 0;
40 this->mute_mask_ = 0;
41 this->tempo = (int)(FP_ONE_TEMPO);
42 this->gain = (int)FP_ONE_GAIN;
43 this->chip_flags = 0;
44
45 // defaults
46 this->tfilter = *track_get_setup( &this->track_filter );
47 this->tfilter.max_initial = 2;
48 this->tfilter.lookahead = 6;
49 this->track_filter.silence_ignored_ = false;
50
51 memset( this->unmapped_read, 0xFF, sizeof this->unmapped_read );
52
53 // Init all stuff
54 Buffer_init( &this->stereo_buf );
55
56 Z80_init( &this->cpu );
57 Rom_init( &this->rom, page_size );
58
59 // Initialize all apus just once (?)
60 Sms_apu_init( &this->sms.psg);
61 Ay_apu_init( &this->msx.psg );
62 Scc_init( &this->msx.scc );
63
64#ifndef KSS_EMU_NO_FMOPL
65 init_opl_apu( type_smsfmunit, &this->sms.fm );
66 init_opl_apu( type_msxmusic, &this->msx.music );
67 init_opl_apu( type_msxaudio, &this->msx.audio );
68#endif
69
70 this->voice_count = 0;
71 this->voice_types = 0;
72 clear_track_vars( this );
73}
74
75// Track info
76
77static blargg_err_t check_kss_header( void const* header )
78{
79 if ( memcmp( header, "KSCC", 4 ) && memcmp( header, "KSSX", 4 ) )
80 return gme_wrong_file_type;
81 return 0;
82}
83
84// Setup
85
86static void update_gain_( struct Kss_Emu* this )
87{
88 int g = this->gain;
89 if ( msx_music_enabled( this ) || msx_audio_enabled( this )
90 || sms_fm_enabled( this ) )
91 {
92 g = (g*3) / 4; //g *= 0.75;
93 }
94 else
95 {
96 if ( this->scc_accessed )
97 g = (g*6) / 5; //g *= 1.2;
98 }
99
100 if ( sms_psg_enabled( this ) ) Sms_apu_volume( &this->sms.psg, g );
101 if ( sms_fm_enabled( this ) ) Opl_volume( &this->sms.fm, g );
102 if ( msx_psg_enabled( this ) ) Ay_apu_volume( &this->msx.psg, g );
103 if ( msx_scc_enabled( this ) ) Scc_volume( &this->msx.scc, g );
104 if ( msx_music_enabled( this ) ) Opl_volume( &this->msx.music, g );
105 if ( msx_audio_enabled( this ) ) Opl_volume( &this->msx.audio, g );
106}
107
108static void update_gain( struct Kss_Emu* this )
109{
110 if ( this->scc_accessed )
111 {
112 /* dprintf( "SCC accessed\n" ); */
113 update_gain_( this );
114 }
115}
116
117blargg_err_t Kss_load_mem( struct Kss_Emu* this, const void* data, long size )
118{
119 /* warning( core.warning() ); */
120 memset( &this->header, 0, sizeof this->header );
121 assert( offsetof (header_t,msx_audio_vol) == header_size - 1 );
122 RETURN_ERR( Rom_load( &this->rom, data, size, header_base_size, &this->header, 0 ) );
123
124 RETURN_ERR( check_kss_header( this->header.tag ) );
125
126 this->chip_flags = 0;
127 this->header.last_track [0] = 255;
128 if ( this->header.tag [3] == 'C' )
129 {
130 if ( this->header.extra_header )
131 {
132 this->header.extra_header = 0;
133 /* warning( "Unknown data in header" ); */
134 }
135 if ( this->header.device_flags & ~0x0F )
136 {
137 this->header.device_flags &= 0x0F;
138 /* warning( "Unknown data in header" ); */
139 }
140 }
141 else if ( this->header.extra_header )
142 {
143 if ( this->header.extra_header != header_ext_size )
144 {
145 this->header.extra_header = 0;
146 /* warning( "Invalid extra_header_size" ); */
147 }
148 else
149 {
150 memcpy( this->header.data_size, this->rom.file_data, header_ext_size );
151 }
152 }
153
154 #ifndef NDEBUG
155 {
156 int ram_mode = this->header.device_flags & 0x84; // MSX
157 if ( this->header.device_flags & 0x02 ) // SMS
158 ram_mode = (this->header.device_flags & 0x88);
159
160 if ( ram_mode )
161 blargg_dprintf_( "RAM not supported\n" ); // TODO: support
162 }
163 #endif
164
165 this->track_count = get_le16( this->header.last_track ) + 1;
166 this->m3u.size = 0;
167
168 this->scc_enabled = false;
169 if ( this->header.device_flags & 0x02 ) // Sega Master System
170 {
171 int const osc_count = sms_osc_count + opl_osc_count;
172
173 // sms.psg
174 this->voice_count = sms_osc_count;
175 static int const types [sms_osc_count + opl_osc_count] = {
176 wave_type+1, wave_type+3, wave_type+2, mixed_type+1, wave_type+0
177 };
178 this->voice_types = types;
179 this->chip_flags |= sms_psg_flag;
180
181 // sms.fm
182 if ( this->header.device_flags & 0x01 )
183 {
184 this->voice_count = osc_count;
185 this->chip_flags |= sms_fm_flag;
186 }
187 }
188 else // MSX
189 {
190 int const osc_count = ay_osc_count + opl_osc_count;
191
192 // msx.psg
193 this->voice_count = ay_osc_count;
194 static int const types [ay_osc_count + opl_osc_count] = {
195 wave_type+1, wave_type+3, wave_type+2, wave_type+0
196 };
197 this->voice_types = types;
198 this->chip_flags |= msx_psg_flag;
199
200 /* if ( this->header.device_flags & 0x10 )
201 warning( "MSX stereo not supported" ); */
202
203 // msx.music
204 if ( this->header.device_flags & 0x01 )
205 {
206 this->voice_count = osc_count;
207 this->chip_flags |= msx_music_flag;
208 }
209
210 #ifndef KSS_EMU_NO_FMOPL
211 // msx.audio
212 if ( this->header.device_flags & 0x08 )
213 {
214 this->voice_count = osc_count;
215 this->chip_flags |= msx_audio_flag;
216 }
217 #endif
218
219 if ( !(this->header.device_flags & 0x80) )
220 {
221 if ( !(this->header.device_flags & 0x84) )
222 this->scc_enabled = scc_enabled_true;
223
224 // msx.scc
225 this->chip_flags |= msx_scc_flag;
226 this->voice_count = ay_osc_count + scc_osc_count;
227 static int const types [ay_osc_count + scc_osc_count] = {
228 wave_type+1, wave_type+3, wave_type+2,
229 wave_type+0, wave_type+4, wave_type+5, wave_type+6, wave_type+7,
230 };
231 this->voice_types = types;
232 }
233 }
234
235 this->tfilter.lookahead = 6;
236 if ( sms_fm_enabled( this ) || msx_music_enabled( this ) || msx_audio_enabled( this ) )
237 {
238 if ( !Opl_supported() )
239 ; /* warning( "FM sound not supported" ); */
240 else
241 this->tfilter.lookahead = 3; // Opl_Apu is really slow
242 }
243
244 this->clock_rate_ = clock_rate;
245 Buffer_clock_rate( &this->stereo_buf, clock_rate );
246 RETURN_ERR( Buffer_set_channel_count( &this->stereo_buf, this->voice_count, this->voice_types ) );
247 this->buf_changed_count = Buffer_channels_changed_count( &this->stereo_buf );
248
249 Sound_set_tempo( this, this->tempo );
250 Sound_mute_voices( this, this->mute_mask_ );
251 return 0;
252}
253
254static void set_voice( struct Kss_Emu* this, int i, struct Blip_Buffer* center, struct Blip_Buffer* left, struct Blip_Buffer* right )
255{
256 if ( sms_psg_enabled( this ) ) // Sega Master System
257 {
258 i -= sms_osc_count;
259 if ( i < 0 )
260 {
261 Sms_apu_set_output( &this->sms.psg, i + sms_osc_count, center, left, right );
262 return;
263 }
264
265 if ( sms_fm_enabled( this ) && i < opl_osc_count )
266 Opl_set_output( &this->sms.fm, center );
267 }
268 else if ( msx_psg_enabled( this ) ) // MSX
269 {
270 i -= ay_osc_count;
271 if ( i < 0 )
272 {
273 Ay_apu_set_output( &this->msx.psg, i + ay_osc_count, center );
274 return;
275 }
276
277 if ( msx_scc_enabled( this ) && i < scc_osc_count ) Scc_set_output( &this->msx.scc, i, center );
278 if ( msx_music_enabled( this ) && i < opl_osc_count ) Opl_set_output( &this->msx.music, center );
279 if ( msx_audio_enabled( this ) && i < opl_osc_count ) Opl_set_output( &this->msx.audio, center );
280 }
281}
282
283// Emulation
284
285void jsr( struct Kss_Emu* this, byte const addr [] )
286{
287 this->ram [--this->cpu.r.sp] = idle_addr >> 8;
288 this->ram [--this->cpu.r.sp] = idle_addr & 0xFF;
289 this->cpu.r.pc = get_le16( addr );
290}
291
292static void set_bank( struct Kss_Emu* this, int logical, int physical )
293{
294 int const bank_size = (16 * 1024L) >> (this->header.bank_mode >> 7 & 1);
295
296 int addr = 0x8000;
297 if ( logical && bank_size == 8 * 1024 )
298 addr = 0xA000;
299
300 physical -= this->header.first_bank;
301 if ( (unsigned) physical >= (unsigned) this->bank_count )
302 {
303 byte* data = this->ram + addr;
304 Z80_map_mem( &this->cpu, addr, bank_size, data, data );
305 }
306 else
307 {
308 int offset, phys = physical * bank_size;
309 for ( offset = 0; offset < bank_size; offset += page_size )
310 Z80_map_mem( &this->cpu, addr + offset, page_size,
311 this->unmapped_write, Rom_at_addr( &this->rom, phys + offset ) );
312
313 }
314}
315
316void cpu_write( struct Kss_Emu* this, addr_t addr, int data )
317{
318 *Z80_write( &this->cpu, addr ) = data;
319 if ( (addr & this->scc_enabled) == 0x8000 ) {
320 // TODO: SCC+ support
321
322 data &= 0xFF;
323 switch ( addr )
324 {
325 case 0x9000:
326 set_bank( this, 0, data );
327 return;
328
329 case 0xB000:
330 set_bank( this, 1, data );
331 return;
332
333 case 0xBFFE: // selects between mapping areas (we just always enable both)
334 if ( data == 0 || data == 0x20 )
335 return;
336 }
337
338 int scc_addr = (addr & 0xDFFF) - 0x9800;
339 if ( msx_scc_enabled( this ) && (unsigned) scc_addr < 0xB0 )
340 {
341 this->scc_accessed = true;
342 //if ( (unsigned) (scc_addr - 0x90) < 0x10 )
343 // scc_addr -= 0x10; // 0x90-0x9F mirrors to 0x80-0x8F
344 if ( scc_addr < scc_reg_count )
345 Scc_write( &this->msx.scc, Z80_time( &this->cpu ), addr, data );
346 return;
347 }
348 }
349}
350
351void cpu_out( struct Kss_Emu* this, kss_time_t time, kss_addr_t addr, int data )
352{
353 data &= 0xFF;
354 switch ( addr & 0xFF )
355 {
356 case 0xA0:
357 if ( msx_psg_enabled( this ) )
358 Ay_apu_write_addr( &this->msx.psg, data );
359 return;
360
361 case 0xA1:
362 if ( msx_psg_enabled( this ) )
363 Ay_apu_write_data( &this->msx.psg, time, data );
364 return;
365
366 case 0x06:
367 if ( sms_psg_enabled( this ) && (this->header.device_flags & 0x04) )
368 {
369 Sms_apu_write_ggstereo( &this->sms.psg, time, data );
370 return;
371 }
372 break;
373
374 case 0x7E:
375 case 0x7F:
376 if ( sms_psg_enabled( this ) )
377 {
378 Sms_apu_write_data( &this->sms.psg, time, data );
379 return;
380 }
381 break;
382
383 #define OPL_WRITE_HANDLER( base, name, opl )\
384 case base : if ( name##_enabled( this ) ) { Opl_write_addr( opl, data ); return; } break;\
385 case base+1: if ( name##_enabled( this ) ) { Opl_write_data( opl, time, data ); return; } break;
386
387 OPL_WRITE_HANDLER( 0x7C, msx_music, &this->msx.music )
388 OPL_WRITE_HANDLER( 0xC0, msx_audio, &this->msx.audio )
389 OPL_WRITE_HANDLER( 0xF0, sms_fm, &this->sms.fm )
390
391 case 0xFE:
392 set_bank( this, 0, data );
393 return;
394
395 #ifndef NDEBUG
396 case 0xA8: // PPI
397 return;
398 #endif
399 }
400
401 /* cpu_out( time, addr, data ); */
402}
403
404int cpu_in( struct Kss_Emu* this, kss_time_t time, kss_addr_t addr )
405{
406 switch ( addr & 0xFF )
407 {
408 case 0xC0:
409 case 0xC1:
410 if ( msx_audio_enabled( this ) )
411 return Opl_read( &this->msx.audio, time, addr & 1 );
412 break;
413
414 case 0xA2:
415 if ( msx_psg_enabled( this ) )
416 return Ay_apu_read( &this->msx.psg );
417 break;
418
419 #ifndef NDEBUG
420 case 0xA8: // PPI
421 return 0;
422 #endif
423 }
424
425 /* return cpu_in( time, addr ); */
426 return 0xFF;
427}
428
429static blargg_err_t run_clocks( struct Kss_Emu* this, blip_time_t* duration_ )
430{
431 blip_time_t duration = *duration_;
432 RETURN_ERR( end_frame( this, duration ) );
433
434 if ( sms_psg_enabled( this ) ) Sms_apu_end_frame( &this->sms.psg, duration );
435 if ( sms_fm_enabled( this ) ) Opl_end_frame( &this->sms.fm, duration );
436 if ( msx_psg_enabled( this ) ) Ay_apu_end_frame( &this->msx.psg, duration );
437 if ( msx_scc_enabled( this ) ) Scc_end_frame( &this->msx.scc, duration );
438 if ( msx_music_enabled( this ) ) Opl_end_frame( &this->msx.music, duration );
439 if ( msx_audio_enabled( this ) ) Opl_end_frame( &this->msx.audio, duration );
440
441 return 0;
442}
443
444blargg_err_t end_frame( struct Kss_Emu* this, kss_time_t end )
445{
446 while ( Z80_time( &this->cpu ) < end )
447 {
448 kss_time_t next = min( end, this->next_play );
449 run_cpu( this, next );
450 if ( this->cpu.r.pc == idle_addr )
451 Z80_set_time( &this->cpu, next );
452
453 if ( Z80_time( &this->cpu ) >= this->next_play )
454 {
455 this->next_play += this->play_period;
456 if ( this->cpu.r.pc == idle_addr )
457 {
458 if ( !this->gain_updated )
459 {
460 this->gain_updated = true;
461 update_gain( this );
462 }
463
464 jsr( this, this->header.play_addr );
465 }
466 }
467 }
468
469 this->next_play -= end;
470 check( this->next_play >= 0 );
471 Z80_adjust_time( &this->cpu, -end );
472
473 return 0;
474}
475
476// MUSIC
477
478blargg_err_t Kss_set_sample_rate( struct Kss_Emu* this, int rate )
479{
480 require( !this->sample_rate ); // sample rate can't be changed once set
481 RETURN_ERR( Buffer_set_sample_rate( &this->stereo_buf, rate, 1000 / 20 ) );
482
483 // Set bass frequency
484 Buffer_bass_freq( &this->stereo_buf, 180 );
485
486 this->sample_rate = rate;
487 RETURN_ERR( track_init( &this->track_filter, this ) );
488 this->tfilter.max_silence = 6 * stereo * this->sample_rate;
489 return 0;
490}
491
492void Sound_mute_voice( struct Kss_Emu* this, int index, bool mute )
493{
494 require( (unsigned) index < (unsigned) this->voice_count );
495 int bit = 1 << index;
496 int mask = this->mute_mask_ | bit;
497 if ( !mute )
498 mask ^= bit;
499 Sound_mute_voices( this, mask );
500}
501
502void Sound_mute_voices( struct Kss_Emu* this, int mask )
503{
504 require( this->sample_rate ); // sample rate must be set first
505 this->mute_mask_ = mask;
506
507 int i;
508 for ( i = this->voice_count; i--; )
509 {
510 if ( mask & (1 << i) )
511 {
512 set_voice( this, i, 0, 0, 0 );
513 }
514 else
515 {
516 struct channel_t ch = Buffer_channel( &this->stereo_buf, i );
517 assert( (ch.center && ch.left && ch.right) ||
518 (!ch.center && !ch.left && !ch.right) ); // all or nothing
519 set_voice( this, i, ch.center, ch.left, ch.right );
520 }
521 }
522}
523
524void Sound_set_tempo( struct Kss_Emu* this, int t )
525{
526 require( this->sample_rate ); // sample rate must be set first
527 int const min = (int)(FP_ONE_TEMPO*0.02);
528 int const max = (int)(FP_ONE_TEMPO*4.00);
529 if ( t < min ) t = min;
530 if ( t > max ) t = max;
531 this->tempo = t;
532
533 blip_time_t period =
534 (this->header.device_flags & 0x40 ? clock_rate / 50 : clock_rate / 60);
535 this->play_period = (blip_time_t) ((period * FP_ONE_TEMPO) / t);
536}
537
538blargg_err_t Kss_start_track( struct Kss_Emu* this, int track )
539{
540 clear_track_vars( this );
541
542 // Remap track if playlist available
543 if ( this->m3u.size > 0 ) {
544 struct entry_t* e = &this->m3u.entries[track];
545 track = e->track;
546 }
547
548 this->current_track = track;
549
550 Buffer_clear( &this->stereo_buf );
551
552 if ( sms_psg_enabled( this ) ) Sms_apu_reset( &this->sms.psg, 0, 0 );
553 if ( sms_fm_enabled( this ) ) Opl_reset( &this->sms.fm );
554 if ( msx_psg_enabled( this ) ) Ay_apu_reset( &this->msx.psg );
555 if ( msx_scc_enabled( this ) ) Scc_reset( &this->msx.scc );
556 if ( msx_music_enabled( this ) ) Opl_reset( &this->msx.music );
557 if ( msx_audio_enabled( this ) ) Opl_reset( &this->msx.audio );
558
559 this->scc_accessed = false;
560 update_gain_( this );
561
562 memset( this->ram, 0xC9, 0x4000 );
563 memset( this->ram + 0x4000, 0, sizeof this->ram - 0x4000 );
564
565 // copy driver code to lo RAM
566 static byte const bios [] = {
567 0xD3, 0xA0, 0xF5, 0x7B, 0xD3, 0xA1, 0xF1, 0xC9, // $0001: WRTPSG
568 0xD3, 0xA0, 0xDB, 0xA2, 0xC9 // $0009: RDPSG
569 };
570 static byte const vectors [] = {
571 0xC3, 0x01, 0x00, // $0093: WRTPSG vector
572 0xC3, 0x09, 0x00, // $0096: RDPSG vector
573 };
574 memcpy( this->ram + 0x01, bios, sizeof bios );
575 memcpy( this->ram + 0x93, vectors, sizeof vectors );
576
577 // copy non-banked data into RAM
578 int load_addr = get_le16( this->header.load_addr );
579 int orig_load_size = get_le16( this->header.load_size );
580 int load_size = min( orig_load_size, (int) this->rom.file_size );
581 load_size = min( load_size, (int) mem_size - load_addr );
582 /* if ( load_size != orig_load_size )
583 warning( "Excessive data size" ); */
584 memcpy( this->ram + load_addr, this->rom.file_data + this->header.extra_header, load_size );
585
586 Rom_set_addr( &this->rom, -load_size - this->header.extra_header );
587
588 // check available bank data
589 int const bank_size = (16 * 1024L) >> (this->header.bank_mode >> 7 & 1);
590 int max_banks = (this->rom.file_size - load_size + bank_size - 1) / bank_size;
591 this->bank_count = this->header.bank_mode & 0x7F;
592 if ( this->bank_count > max_banks )
593 {
594 this->bank_count = max_banks;
595 /* warning( "Bank data missing" ); */
596 }
597 //dprintf( "load_size : $%X\n", load_size );
598 //dprintf( "bank_size : $%X\n", bank_size );
599 //dprintf( "bank_count: %d (%d claimed)\n", bank_count, this->header.bank_mode & 0x7F );
600
601 this->ram [idle_addr] = 0xFF;
602 Z80_reset( &this->cpu, this->unmapped_write, this->unmapped_read );
603 Z80_map_mem( &this->cpu, 0, mem_size, this->ram, this->ram );
604
605 this->cpu.r.sp = 0xF380;
606 this->cpu.r.b.a = track;
607 this->cpu.r.b.h = 0;
608 this->next_play = this->play_period;
609 this->gain_updated = false;
610 jsr( this, this->header.init_addr );
611
612 // convert filter times to samples
613 struct setup_t s = this->tfilter;
614 s.max_initial *= this->sample_rate * stereo;
615 #ifdef GME_DISABLE_SILENCE_LOOKAHEAD
616 s.lookahead = 1;
617 #endif
618 track_setup( &this->track_filter, &s );
619
620 return track_start( &this->track_filter );
621}
622
623// Tell/Seek
624
625static int msec_to_samples( int msec, int sample_rate )
626{
627 int sec = msec / 1000;
628 msec -= sec * 1000;
629 return (sec * sample_rate + msec * sample_rate / 1000) * stereo;
630}
631
632int Track_tell( struct Kss_Emu* this )
633{
634 int rate = this->sample_rate * stereo;
635 int sec = track_sample_count( &this->track_filter ) / rate;
636 return sec * 1000 + (track_sample_count( &this->track_filter ) - sec * rate) * 1000 / rate;
637}
638
639blargg_err_t Track_seek( struct Kss_Emu* this, int msec )
640{
641 int time = msec_to_samples( msec, this->sample_rate );
642 if ( time < track_sample_count( &this->track_filter ) )
643 RETURN_ERR( Kss_start_track( this, this->current_track ) );
644 return Track_skip( this, time - track_sample_count( &this->track_filter ) );
645}
646
647blargg_err_t skip_( void *emu, int count )
648{
649 struct Kss_Emu* this = (struct Kss_Emu*) emu;
650
651 // for long skip, mute sound
652 const int threshold = 32768;
653 if ( count > threshold )
654 {
655 int saved_mute = this->mute_mask_;
656 Sound_mute_voices( this, ~0 );
657
658 int n = count - threshold/2;
659 n &= ~(2048-1); // round to multiple of 2048
660 count -= n;
661 RETURN_ERR( skippy_( &this->track_filter, n ) );
662
663 Sound_mute_voices( this, saved_mute );
664 }
665
666 return skippy_( &this->track_filter, count );
667}
668
669blargg_err_t Track_skip( struct Kss_Emu* this, int count )
670{
671 require( this->current_track >= 0 ); // start_track() must have been called already
672 return track_skip( &this->track_filter, count );
673}
674
675void Track_set_fade( struct Kss_Emu* this, int start_msec, int length_msec )
676{
677 track_set_fade( &this->track_filter, msec_to_samples( start_msec, this->sample_rate ),
678 length_msec * this->sample_rate / (1000 / stereo) );
679}
680
681blargg_err_t Kss_play( struct Kss_Emu* this, int out_count, sample_t* out )
682{
683 require( this->current_track >= 0 );
684 require( out_count % stereo == 0 );
685 return track_play( &this->track_filter, out_count, out );
686}
687
688blargg_err_t play_( void *emu, int count, sample_t* out )
689{
690 struct Kss_Emu* this = (struct Kss_Emu*) emu;
691
692 int remain = count;
693 while ( remain )
694 {
695 Buffer_disable_immediate_removal( &this->stereo_buf );
696 remain -= Buffer_read_samples( &this->stereo_buf, &out [count - remain], remain );
697 if ( remain )
698 {
699 if ( this->buf_changed_count != Buffer_channels_changed_count( &this->stereo_buf ) )
700 {
701 this->buf_changed_count = Buffer_channels_changed_count( &this->stereo_buf );
702
703 // Remute voices
704 Sound_mute_voices( this, this->mute_mask_ );
705 }
706 int msec = Buffer_length( &this->stereo_buf );
707 blip_time_t clocks_emulated = msec * this->clock_rate_ / 1000 - 100;
708 RETURN_ERR( run_clocks( this, &clocks_emulated ) );
709 assert( clocks_emulated );
710 Buffer_end_frame( &this->stereo_buf, clocks_emulated );
711 }
712 }
713 return 0;
714}