GME update with bug-fixes

- True fix for infinite loops [please verify!]
- True fix for KSS silence
- Fix for HES distorted and unstable/random tempo
This commit is contained in:
Vitaly Novichkov 2019-08-18 03:19:28 +03:00 committed by alexey.lysiuk
parent 7d73616fda
commit 1a070d12a1
7 changed files with 318 additions and 287 deletions

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
// Game_Music_Emu 0.6.0. http://www.slack.net/~ant/
// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/
/*
Last validated with zexall 2006.11.14 2:19 PM
@ -162,11 +162,6 @@ static byte const ed_dd_timing [0x100] = {
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,
};
// even on x86, using short and unsigned char was slower
typedef int fint16;
typedef unsigned fuint16;
typedef unsigned fuint8;
bool Kss_Cpu::run( cpu_time_t end_time )
{
set_end_time( end_time );
@ -183,10 +178,10 @@ bool Kss_Cpu::run( cpu_time_t end_time )
rg = this->r.b;
cpu_time_t s_time = s.time;
fuint16 pc = r.pc;
fuint16 sp = r.sp;
fuint16 ix = r.ix; // TODO: keep in memory for direct access?
fuint16 iy = r.iy;
uint_fast32_t pc = r.pc;
uint_fast32_t sp = r.sp;
uint_fast32_t ix = r.ix; // TODO: keep in memory for direct access?
uint_fast32_t iy = r.iy;
int flags = r.b.flags;
goto loop;
@ -208,7 +203,7 @@ loop:
uint8_t const* instr = s.read [pc >> page_shift];
#define GET_ADDR() GET_LE16( instr )
fuint8 opcode;
uint_fast8_t opcode;
// TODO: eliminate this special case
#if BLARGG_NONPORTABLE
@ -241,7 +236,7 @@ loop:
11,10,10, 4,17,11, 7,11,11, 6,10, 4,17, 8, 7,11, // F
};
fuint16 data;
uint_fast16_t data;
data = base_timing [opcode];
if ( (s_time += data) >= 0 )
goto possibly_out_of_time;
@ -297,7 +292,7 @@ possibly_out_of_time:
goto loop;
case 0x3A:{// LD A,(addr)
fuint16 addr = GET_ADDR();
uint_fast16_t addr = GET_ADDR();
pc += 2;
rg.a = READ( addr );
goto loop;
@ -385,7 +380,7 @@ possibly_out_of_time:
case 0xCD:{// CALL addr
call_taken:
fuint16 addr = pc + 2;
uint_fast16_t addr = pc + 2;
pc = GET_ADDR();
sp = uint16_t (sp - 2);
WRITE_WORD( sp, addr );
@ -501,7 +496,7 @@ possibly_out_of_time:
add_hl_data: {
blargg_ulong sum = rp.hl + data;
data ^= rp.hl;
rp.hl = (uint16_t)sum;
rp.hl = sum;
flags = (flags & (S80 | Z40 | V04)) |
(sum >> 16) |
(sum >> 8 & (F20 | F08)) |
@ -691,21 +686,21 @@ possibly_out_of_time:
goto loop;
case 0x2A:{// LD HL,(addr)
fuint16 addr = GET_ADDR();
uint_fast16_t addr = GET_ADDR();
pc += 2;
rp.hl = READ_WORD( addr );
goto loop;
}
case 0x32:{// LD (addr),A
fuint16 addr = GET_ADDR();
uint_fast16_t addr = GET_ADDR();
pc += 2;
WRITE( addr, rg.a );
goto loop;
}
case 0x22:{// LD (addr),HL
fuint16 addr = GET_ADDR();
uint_fast16_t addr = GET_ADDR();
pc += 2;
WRITE_WORD( addr, rp.hl );
goto loop;
@ -728,7 +723,7 @@ possibly_out_of_time:
// Rotate
case 0x07:{// RLCA
fuint16 temp = rg.a;
uint_fast16_t temp = rg.a;
temp = (temp << 1) | (temp >> 7);
flags = (flags & (S80 | Z40 | P04)) |
(temp & (F20 | F08 | C01));
@ -737,7 +732,7 @@ possibly_out_of_time:
}
case 0x0F:{// RRCA
fuint16 temp = rg.a;
uint_fast16_t temp = rg.a;
flags = (flags & (S80 | Z40 | P04)) |
(temp & C01);
temp = (temp << 7) | (temp >> 1);
@ -756,7 +751,7 @@ possibly_out_of_time:
}
case 0x1F:{// RRA
fuint16 temp = (flags << 7) | (rg.a >> 1);
uint_fast16_t temp = (flags << 7) | (rg.a >> 1);
flags = (flags & (S80 | Z40 | P04)) |
(temp & (F20 | F08)) |
(rg.a & C01);
@ -766,7 +761,7 @@ possibly_out_of_time:
// Misc
case 0x2F:{// CPL
fuint16 temp = ~rg.a;
uint_fast16_t temp = ~rg.a;
flags = (flags & (S80 | Z40 | P04 | C01)) |
(temp & (F20 | F08)) |
(H10 | N02);
@ -792,21 +787,21 @@ possibly_out_of_time:
goto loop;
case 0xE3:{// EX (SP),HL
fuint16 temp = READ_WORD( sp );
uint_fast16_t temp = READ_WORD( sp );
WRITE_WORD( sp, rp.hl );
rp.hl = temp;
goto loop;
}
case 0xEB:{// EX DE,HL
fuint16 temp = rp.hl;
uint_fast16_t temp = rp.hl;
rp.hl = rp.de;
rp.de = temp;
goto loop;
}
case 0xD9:{// EXX DE,HL
fuint16 temp = r.alt.w.bc;
uint_fast16_t temp = r.alt.w.bc;
r.alt.w.bc = rp.bc;
rp.bc = temp;
@ -847,7 +842,7 @@ possibly_out_of_time:
// Rotate left
#define RLC( read, write ) {\
fuint8 result = read;\
uint_fast8_t result = read;\
result = uint8_t (result << 1) | (result >> 7);\
flags = SZ28P( result ) | (result & C01);\
write;\
@ -866,7 +861,7 @@ possibly_out_of_time:
}
#define RL( read, write ) {\
fuint16 result = (read << 1) | (flags & C01);\
uint_fast16_t result = (read << 1) | (flags & C01);\
flags = SZ28PC( result );\
write;\
goto loop;\
@ -884,7 +879,7 @@ possibly_out_of_time:
}
#define SLA( read, add, write ) {\
fuint16 result = (read << 1) | add;\
uint_fast16_t result = (read << 1) | add;\
flags = SZ28PC( result );\
write;\
goto loop;\
@ -915,7 +910,7 @@ possibly_out_of_time:
// Rotate right
#define RRC( read, write ) {\
fuint8 result = read;\
uint_fast8_t result = read;\
flags = result & C01;\
result = uint8_t (result << 7) | (result >> 1);\
flags |= SZ28P( result );\
@ -935,8 +930,8 @@ possibly_out_of_time:
}
#define RR( read, write ) {\
fuint8 result = read;\
fuint8 temp = result & C01;\
uint_fast8_t result = read;\
uint_fast8_t temp = result & C01;\
result = uint8_t (flags << 7) | (result >> 1);\
flags = SZ28P( result ) | temp;\
write;\
@ -955,7 +950,7 @@ possibly_out_of_time:
}
#define SRA( read, write ) {\
fuint8 result = read;\
uint_fast8_t result = read;\
flags = result & C01;\
result = (result & 0x80) | (result >> 1);\
flags |= SZ28P( result );\
@ -975,7 +970,7 @@ possibly_out_of_time:
}
#define SRL( read, write ) {\
fuint8 result = read;\
uint_fast8_t result = read;\
flags = result & C01;\
result >>= 1;\
flags |= SZ28P( result );\
@ -1083,7 +1078,7 @@ possibly_out_of_time:
blargg_ulong sum = temp + (flags & C01);
flags = ~data >> 2 & N02;
if ( flags )
sum = (blargg_ulong)-(blargg_long)sum;
sum = -sum;
sum += rp.hl;
temp ^= rp.hl;
temp ^= sum;
@ -1091,7 +1086,7 @@ possibly_out_of_time:
(temp >> 8 & H10) |
(sum >> 8 & (S80 | F20 | F08)) |
((temp - -0x8000) >> 14 & V04);
rp.hl = (uint16_t)sum;
rp.hl = sum;
if ( (uint16_t) sum )
goto loop;
flags |= Z40;
@ -1119,7 +1114,7 @@ possibly_out_of_time:
case 0x43: // LD (ADDR),BC
case 0x53: // LD (ADDR),DE
temp = R16( data, 4, 0x43 );
fuint16 addr = GET_ADDR();
uint_fast16_t addr = GET_ADDR();
pc += 2;
WRITE_WORD( addr, temp );
goto loop;
@ -1127,21 +1122,21 @@ possibly_out_of_time:
case 0x4B: // LD BC,(ADDR)
case 0x5B:{// LD DE,(ADDR)
fuint16 addr = GET_ADDR();
uint_fast16_t addr = GET_ADDR();
pc += 2;
R16( data, 4, 0x4B ) = READ_WORD( addr );
goto loop;
}
case 0x7B:{// LD SP,(ADDR)
fuint16 addr = GET_ADDR();
uint_fast16_t addr = GET_ADDR();
pc += 2;
sp = READ_WORD( addr );
goto loop;
}
case 0x67:{// RRD
fuint8 temp = READ( rp.hl );
uint_fast8_t temp = READ( rp.hl );
WRITE( rp.hl, (rg.a << 4) | (temp >> 4) );
temp = (rg.a & 0xF0) | (temp & 0x0F);
flags = (flags & C01) | SZ28P( temp );
@ -1150,7 +1145,7 @@ possibly_out_of_time:
}
case 0x6F:{// RLD
fuint8 temp = READ( rp.hl );
uint_fast8_t temp = READ( rp.hl );
WRITE( rp.hl, (temp << 4) | (rg.a & 0x0F) );
temp = (rg.a & 0xF0) | (temp >> 4);
flags = (flags & C01) | SZ28P( temp );
@ -1174,7 +1169,7 @@ possibly_out_of_time:
case 0xA1: // CPI
case 0xB1: // CPIR
inc = +1;
fuint16 addr = rp.hl;
uint_fast16_t addr = rp.hl;
rp.hl = addr + inc;
int temp = READ( addr );
@ -1207,7 +1202,7 @@ possibly_out_of_time:
case 0xA0: // LDI
case 0xB0: // LDIR
inc = +1;
fuint16 addr = rp.hl;
uint_fast16_t addr = rp.hl;
rp.hl = addr + inc;
int temp = READ( addr );
@ -1239,7 +1234,7 @@ possibly_out_of_time:
case 0xA3: // OUTI
case 0xB3: // OTIR
inc = +1;
fuint16 addr = rp.hl;
uint_fast16_t addr = rp.hl;
rp.hl = addr + inc;
int temp = READ( addr );
@ -1265,7 +1260,7 @@ possibly_out_of_time:
case 0xB2: // INIR
inc = +1;
fuint16 addr = rp.hl;
uint_fast16_t addr = rp.hl;
rp.hl = addr + inc;
int temp = IN( rp.bc );
@ -1330,7 +1325,7 @@ possibly_out_of_time:
//////////////////////////////////////// DD/FD prefix
{
fuint16 ixy;
uint_fast16_t ixy;
case 0xDD:
ixy = ix;
goto ix_prefix;
@ -1526,7 +1521,7 @@ possibly_out_of_time:
goto loop;
case 0x22:{// LD (ADDR),IXY
fuint16 addr = GET_ADDR();
uint_fast16_t addr = GET_ADDR();
pc += 2;
WRITE_WORD( addr, ixy );
goto loop;
@ -1538,7 +1533,7 @@ possibly_out_of_time:
goto set_ixy;
case 0x2A:{// LD IXY,(addr)
fuint16 addr = GET_ADDR();
uint_fast16_t addr = GET_ADDR();
ixy = READ_WORD( addr );
pc += 2;
goto set_ixy;
@ -1562,7 +1557,7 @@ possibly_out_of_time:
case 0x3E: goto srl_data_addr; // SRL (IXY)
CASE8( 46, 4E, 56, 5E, 66, 6E, 76, 7E ):{// BIT b,(IXY+disp)
fuint8 temp = READ( data );
uint_fast8_t temp = READ( data );
int masked = temp & 1 << (data2 >> 3 & 7);
flags = (flags & C01) | H10 |
(masked & S80) |
@ -1664,7 +1659,7 @@ possibly_out_of_time:
goto loop;
case 0xE3:{// EX (SP),IXY
fuint16 temp = READ_WORD( sp );
uint_fast16_t temp = READ_WORD( sp );
WRITE_WORD( sp, ixy );
ixy = temp;
goto set_ixy;

View File

@ -64,6 +64,8 @@ Music_Emu::Music_Emu()
equalizer_.treble = -1.0;
equalizer_.bass = 60;
emu_autoload_playback_limit_ = true;
static const char* const names [] = {
"Voice 1", "Voice 2", "Voice 3", "Voice 4",
"Voice 5", "Voice 6", "Voice 7", "Voice 8"
@ -187,6 +189,16 @@ void Music_Emu::end_track_if_error( blargg_err_t err )
}
}
bool Music_Emu::autoload_playback_limit() const
{
return emu_autoload_playback_limit_;
}
void Music_Emu::set_autoload_playback_limit( bool do_autoload_limit )
{
emu_autoload_playback_limit_ = do_autoload_limit;
}
// Tell/Seek
blargg_long Music_Emu::msec_to_samples( blargg_long msec ) const

View File

@ -13,7 +13,7 @@ public:
// Set output sample rate. Must be called only once before loading file.
blargg_err_t set_sample_rate( long sample_rate );
// specifies if all 8 voices get rendered to their own stereo channel
// default implementation of Music_Emu always returns not supported error (i.e. no multichannel support by default)
// derived emus must override this if they support multichannel rendering
@ -40,8 +40,8 @@ public:
// Names of voices
const char** voice_names() const;
bool multi_channel() const;
bool multi_channel() const;
// Track status/control
@ -67,6 +67,13 @@ public:
// true. Fade time can be changed while track is playing.
void set_fade( long start_msec, long length_msec = 8000 );
// Controls whether or not to automatically load and obey track length
// metadata for supported emulators.
//
// @since 0.6.2.
bool autoload_playback_limit() const;
void set_autoload_playback_limit( bool do_autoload_limit );
// Disable automatic end-of-track detection and skipping of silence at beginning
void ignore_silence( bool disable = true );
@ -134,7 +141,7 @@ protected:
double gain() const { return gain_; }
double tempo() const { return tempo_; }
void remute_voices();
blargg_err_t set_multi_channel_( bool is_enabled );
blargg_err_t set_multi_channel_( bool is_enabled );
virtual blargg_err_t set_sample_rate_( long sample_rate ) = 0;
virtual void set_equalizer_( equalizer_t const& ) { }
@ -158,10 +165,10 @@ private:
double tempo_;
double gain_;
bool multi_channel_;
// returns the number of output channels, i.e. usually 2 for stereo, unlesss multi_channel_ == true
int out_channels() const { return this->multi_channel() ? 2*8 : 2; }
long sample_rate_;
blargg_long msec_to_samples( blargg_long msec ) const;
@ -170,6 +177,7 @@ private:
blargg_long out_time; // number of samples played since start of track
blargg_long emu_time; // number of samples emulator has generated since start of track
bool emu_track_ended_; // emulator has reached end of track
bool emu_autoload_playback_limit_; // whether to load and obey track length by default
volatile bool track_ended_;
void clear_track_vars();
void end_track_if_error( blargg_err_t );

View File

@ -299,6 +299,12 @@ blargg_err_t Spc_Emu::start_track_( int track )
RETURN_ERR( apu.load_spc( file_data, file_size ) );
filter.set_gain( (int) (gain() * SPC_Filter::gain_unit) );
apu.clear_echo();
track_info_t spc_info;
RETURN_ERR( track_info_( &spc_info, track ) );
// Set a default track length, need a non-zero fadeout
if ( autoload_playback_limit() && ( spc_info.length > 0 ) )
set_fade ( spc_info.length, 50 );
return 0;
}

View File

@ -103,10 +103,10 @@ BLARGG_EXPORT gme_type_t gme_identify_extension( const char* extension_ )
char const* end = strrchr( extension_, '.' );
if ( end )
extension_ = end + 1;
char extension [6];
to_uppercase( extension_, sizeof extension, extension );
for ( gme_type_t const* types = gme_type_list(); *types; types++ )
if ( !strcmp( extension, (*types)->extension_ ) )
return *types;
@ -133,30 +133,30 @@ BLARGG_EXPORT gme_err_t gme_identify_file( const char* path, gme_type_t* type_ou
RETURN_ERR( in.read( header, sizeof header ) );
*type_out = gme_identify_extension( gme_identify_header( header ) );
}
return 0;
return 0;
}
BLARGG_EXPORT gme_err_t gme_open_data( void const* data, long size, Music_Emu** out, int sample_rate )
{
require( (data || !size) && out );
*out = 0;
gme_type_t file_type = 0;
if ( size >= 4 )
file_type = gme_identify_extension( gme_identify_header( data ) );
if ( !file_type )
return gme_wrong_file_type;
Music_Emu* emu = gme_new_emu( file_type, sample_rate );
CHECK_ALLOC( emu );
gme_err_t err = gme_load_data( emu, data, size );
if ( err )
delete emu;
else
*out = emu;
return err;
}
@ -164,13 +164,13 @@ BLARGG_EXPORT gme_err_t gme_open_file( const char* path, Music_Emu** out, int sa
{
require( path && out );
*out = 0;
GME_FILE_READER in;
RETURN_ERR( in.open( path ) );
char header [4];
int header_size = 0;
gme_type_t file_type = gme_identify_extension( path );
if ( !file_type )
{
@ -180,23 +180,33 @@ BLARGG_EXPORT gme_err_t gme_open_file( const char* path, Music_Emu** out, int sa
}
if ( !file_type )
return gme_wrong_file_type;
Music_Emu* emu = gme_new_emu( file_type, sample_rate );
CHECK_ALLOC( emu );
// optimization: avoids seeking/re-reading header
Remaining_Reader rem( header, header_size, &in );
gme_err_t err = emu->load( rem );
in.close();
if ( err )
delete emu;
else
*out = emu;
return err;
}
BLARGG_EXPORT void gme_set_autoload_playback_limit( Music_Emu *emu, int do_autoload_limit )
{
emu->set_autoload_playback_limit( do_autoload_limit != 0 );
}
BLARGG_EXPORT int gme_autoload_playback_limit( Music_Emu *const emu )
{
return emu->autoload_playback_limit();
}
// Used to implement gme_new_emu and gme_new_emu_multi_channel
Music_Emu* gme_internal_new_emu_( gme_type_t type, int rate, bool multi_channel )
{
@ -204,7 +214,7 @@ Music_Emu* gme_internal_new_emu_( gme_type_t type, int rate, bool multi_channel
{
if ( rate == gme_info_only )
return type->new_info();
Music_Emu* me = type->new_emu();
if ( me )
{
@ -224,7 +234,7 @@ Music_Emu* gme_internal_new_emu_( gme_type_t type, int rate, bool multi_channel
if ( me->effects_buffer )
me->set_buffer( me->effects_buffer );
}
if ( !(type->flags_ & 1) || me->effects_buffer )
#endif
{
@ -276,30 +286,30 @@ BLARGG_EXPORT int gme_track_count( Music_Emu const* me ) { return me->track_coun
struct gme_info_t_ : gme_info_t
{
track_info_t info;
BLARGG_DISABLE_NOTHROW
};
BLARGG_EXPORT gme_err_t gme_track_info( Music_Emu const* me, gme_info_t** out, int track )
{
*out = NULL;
gme_info_t_* info = BLARGG_NEW gme_info_t_;
CHECK_ALLOC( info );
gme_err_t err = me->track_info( &info->info, track );
if ( err )
{
gme_free_info( info );
return err;
}
#define COPY(name) info->name = info->info.name;
COPY( length );
COPY( intro_length );
COPY( loop_length );
info->i4 = -1;
info->i5 = -1;
info->i6 = -1;
@ -312,7 +322,7 @@ BLARGG_EXPORT gme_err_t gme_track_info( Music_Emu const* me, gme_info_t** out, i
info->i13 = -1;
info->i14 = -1;
info->i15 = -1;
info->s7 = "";
info->s8 = "";
info->s9 = "";
@ -322,7 +332,7 @@ BLARGG_EXPORT gme_err_t gme_track_info( Music_Emu const* me, gme_info_t** out, i
info->s13 = "";
info->s14 = "";
info->s15 = "";
COPY( system );
COPY( game );
COPY( song );
@ -330,9 +340,9 @@ BLARGG_EXPORT gme_err_t gme_track_info( Music_Emu const* me, gme_info_t** out, i
COPY( copyright );
COPY( comment );
COPY( dumper );
#undef COPY
info->play_length = info->length;
if ( info->play_length <= 0 )
{
@ -340,9 +350,9 @@ BLARGG_EXPORT gme_err_t gme_track_info( Music_Emu const* me, gme_info_t** out, i
if ( info->play_length <= 0 )
info->play_length = 150 * 1000; // 2.5 minutes
}
*out = info;
return 0;
}

View File

@ -153,6 +153,7 @@ MusInfo *GME_OpenSong(FileReader &reader, const char *fmt)
return nullptr;
}
gme_set_stereo_depth(emu, clamp(*gme_stereodepth, 0.f, 1.f));
gme_set_fade(emu, -1); // Enable infinite loop
return new GMESong(emu, sample_rate);
}