Update GME, including support for tag-specified fade times for formats which support it, which are SPC and SFM.

This commit is contained in:
Christopher Snowhill 2017-03-12 21:10:35 -07:00
parent b8110b85de
commit c594690aab
10 changed files with 456 additions and 411 deletions

View file

@ -103,6 +103,9 @@ public:
// on others this has no effect. Should be called only once *before* set_sample_rate(). // on others this has no effect. Should be called only once *before* set_sample_rate().
virtual void set_buffer( class Multi_Buffer* ) { } virtual void set_buffer( class Multi_Buffer* ) { }
// Mutes native effects of a given sound engine. Currently only applies to the SPC emulator.
virtual void mute_effects( bool mute ) { }
// Sound equalization (treble/bass) // Sound equalization (treble/bass)
// Frequency equalizer parameters (see gme.txt) // Frequency equalizer parameters (see gme.txt)
@ -178,7 +181,7 @@ protected:
public: public:
gme_t(); gme_t();
~gme_t(); ~gme_t();
BLARGG_DEPRECATED( const char** voice_names() const { return CONST_CAST(const char**,voice_names_); } ) const char** voice_names() const { return CONST_CAST(const char**,voice_names_); }
protected: protected:
virtual void unload(); virtual void unload();

View file

@ -28,6 +28,13 @@ public:
// Enables gaussian, cubic or sinc interpolation // Enables gaussian, cubic or sinc interpolation
void interpolation_level( int level = 0 ) { smp.dsp.spc_dsp.interpolation_level( level ); } void interpolation_level( int level = 0 ) { smp.dsp.spc_dsp.interpolation_level( level ); }
// Enables an analog signal simulation filter
void enable_filter( bool enable = true ) { _enable_filter = enable; if (enable) filter.clear(); }
// Enables native echo
void enable_echo( bool enable = true ) { smp.dsp.spc_dsp.enable_echo( enable ); }
virtual void mute_effects( bool mute ) { enable_echo(!mute); }
SuperFamicom::SMP const* get_smp() const; SuperFamicom::SMP const* get_smp() const;
SuperFamicom::SMP * get_smp(); SuperFamicom::SMP * get_smp();
@ -82,6 +89,8 @@ private:
Spc_Filter filter; Spc_Filter filter;
SuperFamicom::SMP smp; SuperFamicom::SMP smp;
bool _enable_filter;
byte const* trailer_() const; byte const* trailer_() const;
int trailer_size_() const; int trailer_size_() const;
blargg_err_t play_and_filter( int count, sample_t out [] ); blargg_err_t play_and_filter( int count, sample_t out [] );

View file

@ -27,6 +27,8 @@ Sfm_Emu::Sfm_Emu()
set_gain( 1.4 ); set_gain( 1.4 );
set_max_initial_silence( 30 ); set_max_initial_silence( 30 );
set_silence_lookahead( 30 ); // Some SFMs may have a lot of initialization code set_silence_lookahead( 30 ); // Some SFMs may have a lot of initialization code
enable_filter( false );
enable_echo( true );
} }
Sfm_Emu::~Sfm_Emu() { } Sfm_Emu::~Sfm_Emu() { }
@ -68,7 +70,7 @@ static void copy_info( track_info_t* out, const Bml_Parser& in )
if ( value ) if ( value )
out->fade_length = strtoul( value, &end, 10 ); out->fade_length = strtoul( value, &end, 10 );
else else
out->fade_length = 0; out->fade_length = -1;
} }
blargg_err_t Sfm_Emu::track_info_( track_info_t* out, int ) const blargg_err_t Sfm_Emu::track_info_( track_info_t* out, int ) const
@ -297,9 +299,9 @@ blargg_err_t Sfm_Emu::start_track_( int track )
value = metadata.enumValue("smp:ports"); value = metadata.enumValue("smp:ports");
if (value) if (value)
{ {
for (auto &n : smp.sfm_last) for (int i = 0; i < _countof(smp.sfm_last); i++)
{ {
n = strtol(value, &end, 10); smp.sfm_last[i] = strtol(value, &end, 10);
if (*end == ',') if (*end == ',')
value = end + 1; value = end + 1;
else else
@ -482,10 +484,10 @@ void Sfm_Emu::create_updated_metadata( Bml_Parser &out ) const
oss.str(""); oss.str("");
oss.clear(); oss.clear();
first = true; first = true;
for (auto n : smp.sfm_last) for (int i = 0; i < _countof(smp.sfm_last); i++)
{ {
if (!first) oss << ","; if (!first) oss << ",";
oss << (unsigned long)n; oss << (unsigned long)smp.sfm_last[i];
first = false; first = false;
} }
out.setValue("smp:ports", oss.str().c_str()); out.setValue("smp:ports", oss.str().c_str());
@ -621,7 +623,7 @@ blargg_err_t Sfm_Emu::save_( gme_writer_t writer, void* your_data ) const
blargg_err_t Sfm_Emu::play_and_filter( int count, sample_t out [] ) blargg_err_t Sfm_Emu::play_and_filter( int count, sample_t out [] )
{ {
smp.render( out, count ); smp.render( out, count );
filter.run( out, count ); if ( _enable_filter ) filter.run( out, count );
return blargg_ok; return blargg_ok;
} }

View file

@ -36,7 +36,14 @@ public:
// Enables gaussian, cubic or sinc interpolation // Enables gaussian, cubic or sinc interpolation
void interpolation_level( int level = 0 ) { smp.dsp.spc_dsp.interpolation_level( level ); } void interpolation_level( int level = 0 ) { smp.dsp.spc_dsp.interpolation_level( level ); }
SuperFamicom::SMP const* get_smp() const; // Enables an analog signal simulation filter
void enable_filter( bool enable = true ) { _enable_filter = enable; if (enable) filter.clear(); }
// Enables native echo
void enable_echo(bool enable = true) { smp.dsp.spc_dsp.enable_echo(enable); }
virtual void mute_effects(bool mute) { enable_echo(!mute); }
SuperFamicom::SMP const* get_smp() const;
SuperFamicom::SMP * get_smp(); SuperFamicom::SMP * get_smp();
blargg_err_t hash_( Hash_Function& ) const; blargg_err_t hash_( Hash_Function& ) const;
@ -65,6 +72,8 @@ private:
Spc_Filter filter; Spc_Filter filter;
SuperFamicom::SMP smp; SuperFamicom::SMP smp;
bool _enable_filter;
Bml_Parser metadata; Bml_Parser metadata;
void create_updated_metadata(Bml_Parser &out) const; void create_updated_metadata(Bml_Parser &out) const;

View file

@ -257,8 +257,8 @@ gme_err_t gme_track_info( Music_Emu const* me, gme_info_t** out, int track )
COPY( length ); COPY( length );
COPY( intro_length ); COPY( intro_length );
COPY( loop_length ); COPY( loop_length );
COPY( fade_length );
info->i4 = -1;
info->i5 = -1; info->i5 = -1;
info->i6 = -1; info->i6 = -1;
info->i7 = -1; info->i7 = -1;
@ -360,6 +360,7 @@ void gme_set_equalizer ( Music_Emu* gme, gme_equalizer_t const* eq ) { gme
void gme_equalizer ( Music_Emu const* gme, gme_equalizer_t* o ) { *o = gme->equalizer(); } void gme_equalizer ( Music_Emu const* gme, gme_equalizer_t* o ) { *o = gme->equalizer(); }
const char* gme_voice_name ( Music_Emu const* gme, int i ) { return gme->voice_name( i ); } const char* gme_voice_name ( Music_Emu const* gme, int i ) { return gme->voice_name( i ); }
gme_err_t gme_save ( Music_Emu const* gme, gme_writer_t writer, void* your_data ) { return gme->save( writer, your_data ); } gme_err_t gme_save ( Music_Emu const* gme, gme_writer_t writer, void* your_data ) { return gme->save( writer, your_data ); }
void gme_mute_effects ( Music_Emu* gme, gme_bool disable ) { gme->mute_effects(disable); }
void gme_effects( Music_Emu const* gme, gme_effects_t* out ) void gme_effects( Music_Emu const* gme, gme_effects_t* out )
{ {

View file

@ -97,7 +97,10 @@ struct gme_info_t
otherwise a default of 150000 (2.5 minutes). */ otherwise a default of 150000 (2.5 minutes). */
int play_length; int play_length;
int i4,i5,i6,i7,i8,i9,i10,i11,i12,i13,i14,i15; /* reserved */ /* Fade duration, in milliseconds, if the file specifies it */
int fade_length;
int i5,i6,i7,i8,i9,i10,i11,i12,i13,i14,i15; /* reserved */
/* empty string ("") if not available */ /* empty string ("") if not available */
const char* system; const char* system;
@ -135,6 +138,9 @@ void gme_mute_voice( gme_t*, int index, gme_bool mute );
voices, 0 unmutes them all, 0x01 mutes just the first voice, etc. */ voices, 0 unmutes them all, 0x01 mutes just the first voice, etc. */
void gme_mute_voices( gme_t*, int muting_mask ); void gme_mute_voices( gme_t*, int muting_mask );
/* Disables native effects, and possibly others. */
void gme_mute_effects( gme_t*, gme_bool mute );
/* Frequency equalizer parameters (see gme.txt) */ /* Frequency equalizer parameters (see gme.txt) */
typedef struct gme_equalizer_t typedef struct gme_equalizer_t
{ {

View file

@ -70,17 +70,16 @@ static BOOST::uint8_t const initial_regs [SPC_DSP::register_count] =
#define WRITE_SAMPLES( l, r, out ) \ #define WRITE_SAMPLES( l, r, out ) \
{\ {\
if ( out >= m.out_end )\
{\
int count = sample_count();\
m.out_begin = (SPC_DSP::sample_t *) realloc( m.out_begin, (count ? count * 2 : 8192) * sizeof(SPC_DSP::sample_t) );\
out = m.out_begin + count;\
m.out_end = m.out_begin + count * 2;\
}\
out [0] = l;\ out [0] = l;\
out [1] = r;\ out [1] = r;\
out += 2;\ out += 2;\
if ( out >= m.out_end )\
{\
check( out == m.out_end );\
check( m.out_end != &m.extra [extra_size] || \
(m.extra <= m.out_begin && m.extra < &m.extra [extra_size]) );\
out = m.extra;\
m.out_end = &m.extra [extra_size];\
}\
}\ }\
void SPC_DSP::set_output( sample_t* out, int size ) void SPC_DSP::set_output( sample_t* out, int size )
@ -88,8 +87,7 @@ void SPC_DSP::set_output( sample_t* out, int size )
require( (size & 1) == 0 ); // must be even require( (size & 1) == 0 ); // must be even
if ( !out ) if ( !out )
{ {
out = m.extra; size = 0;
size = extra_size;
} }
m.out_begin = out; m.out_begin = out;
m.out = out; m.out = out;
@ -739,7 +737,7 @@ MISC_CLOCK( 30 )
if ( m.every_other_sample ) if ( m.every_other_sample )
{ {
m.kon = m.new_kon; m.kon = m.new_kon;
m.t_koff = REG(koff) | m.mute_mask; m.t_koff = REG(koff);
} }
run_counters(); run_counters();
@ -893,6 +891,10 @@ inline void SPC_DSP::voice_output( voice_t const* v, int ch )
if ( abs_amp > m.max_level[v - (const SPC_DSP::voice_t *)&m.voices][ch] ) if ( abs_amp > m.max_level[v - (const SPC_DSP::voice_t *)&m.voices][ch] )
m.max_level[v - (const SPC_DSP::voice_t *)&m.voices][ch] = abs_amp; m.max_level[v - (const SPC_DSP::voice_t *)&m.voices][ch] = abs_amp;
// FIX: audibly mute, rather than do it in a way the SPC code can easily detect
if ( m.mute_mask & ( 1 << ( v - m.voices ) ) )
amp = 0;
// Add to output total // Add to output total
m.t_main_out [ch] += amp; m.t_main_out [ch] += amp;
CLAMP16( m.t_main_out [ch] ); CLAMP16( m.t_main_out [ch] );
@ -1067,7 +1069,7 @@ inline int SPC_DSP::echo_output( int ch )
vol ^= vol >> 7; vol ^= vol >> 7;
int out = (int16_t) ((m.t_main_out [ch] * vol) >> 7) + int out = (int16_t) ((m.t_main_out [ch] * vol) >> 7) +
(int16_t) ((m.t_echo_in [ch] * (int8_t) REG(evoll + ch * 0x10)) >> 7); (int16_t) ((m.t_echo_in [ch] * (int8_t) REG(evoll + ch * 0x10)) >> 7) * m.enable_echo;
CLAMP16( out ); CLAMP16( out );
return out; return out;
} }

View file

@ -22,6 +22,7 @@ public:
// doesn't generate any. // doesn't generate any.
typedef short sample_t; typedef short sample_t;
void set_output( sample_t* out, int out_size ); void set_output( sample_t* out, int out_size );
sample_t* get_output();
// Number of samples written to output since it was last set, always // Number of samples written to output since it was last set, always
// a multiple of 2. Undefined if more samples were generated than // a multiple of 2. Undefined if more samples were generated than
@ -91,11 +92,10 @@ public:
}; };
public: public:
enum { extra_size = 16 };
sample_t* extra() { return m.extra; }
sample_t const* out_pos() const { return m.out; } sample_t const* out_pos() const { return m.out; }
void disable_surround( bool disable = true ); void disable_surround( bool disable = true );
void interpolation_level( int level = 0 ) { m.interpolation_level = level; } void interpolation_level( int level = 0 ) { m.interpolation_level = level; }
void enable_echo( bool enable = true ) { m.enable_echo = enable ? 1 : 0; }
public: public:
BLARGG_DISABLE_NOTHROW BLARGG_DISABLE_NOTHROW
@ -184,10 +184,10 @@ public:
int mute_mask; int mute_mask;
int surround_threshold; int surround_threshold;
int interpolation_level; int interpolation_level;
int enable_echo;
sample_t* out; sample_t* out;
sample_t* out_end; sample_t* out_end;
sample_t* out_begin; sample_t* out_begin;
sample_t extra [extra_size];
int max_level[voice_count][2]; int max_level[voice_count][2];
}; };
@ -299,6 +299,11 @@ inline bool SPC_DSP::check_kon()
return old; return old;
} }
inline SPC_DSP::sample_t* SPC_DSP::get_output()
{
return m.out_begin;
}
#if !SPC_NO_COPY_STATE_FUNCS #if !SPC_NO_COPY_STATE_FUNCS
class SPC_State_Copier { class SPC_State_Copier {

View file

@ -18,6 +18,7 @@ extern gme_err_t readCallback( void* data, void* out, long count );
Music_Emu* emu; Music_Emu* emu;
id<CogSource> source; id<CogSource> source;
long length; long length;
long fade;
} }
- (void)setSource:(id<CogSource>)s; - (void)setSource:(id<CogSource>)s;

View file

@ -122,6 +122,13 @@ gme_err_t readCallback( void* data, void* out, long count )
DLog(@"Setting default: %li", length); DLog(@"Setting default: %li", length);
} }
if (info->fade_length >= 0) {
fade = info->fade_length;
}
else {
fade = 8000;
}
gme_free_info( info ); gme_free_info( info );
DLog(@"Length: %li", length); DLog(@"Length: %li", length);
@ -134,7 +141,7 @@ gme_err_t readCallback( void* data, void* out, long count )
return NO; return NO;
} }
length += 8000; length += fade;
[self willChangeValueForKey:@"properties"]; [self willChangeValueForKey:@"properties"];
@ -167,7 +174,7 @@ gme_err_t readCallback( void* data, void* out, long count )
if ( IsRepeatOneSet() ) if ( IsRepeatOneSet() )
gme_set_fade( emu, -1, 0 ); gme_set_fade( emu, -1, 0 );
else else
gme_set_fade( emu, (int)(length - 8000), 8000 ); gme_set_fade( emu, (int)(length - fade), fade );
gme_play(emu, numSamples, (short int *)buf); gme_play(emu, numSamples, (short int *)buf);