GME: Implemented functionality to update the metadata on an instance of gme_t, for SFM only. Also extended the SFM metadata to include log looping, track length/fade, and textual information.

CQTexperiment
Chris Moeller 2013-10-28 21:55:13 -07:00
parent 9b7b8d5fd7
commit bb0f0ed511
11 changed files with 249 additions and 116 deletions

View File

@ -328,7 +328,7 @@ void Bml_Parser::setValue(std::string const& path, long value)
setValue( path, str.str().c_str() ); setValue( path, str.str().c_str() );
} }
void Bml_Parser::serialize(std::string & out) void Bml_Parser::serialize(std::string & out) const
{ {
std::ostringstream strOut; std::ostringstream strOut;
serialize(strOut, &document, 0); serialize(strOut, &document, 0);
@ -342,13 +342,15 @@ void Bml_Parser::serialize(std::ostringstream & out, Bml_Node const* node, unsig
if ( indent ) if ( indent )
{ {
out << node->getName(); out << node->getName();
if (node->getValue()) out << ":" << node->getValue(); if (node->getValue() && strlen(node->getValue())) out << ":" << node->getValue();
out << std::endl; out << std::endl;
} }
for (unsigned i = 0, j = node->getChildCount(); i < j; ++i) for (unsigned i = 0, j = node->getChildCount(); i < j; ++i)
{ {
Bml_Node const& child = node->getChild(i); Bml_Node const& child = node->getChild(i);
if ( (!child.getValue() || !strlen(child.getValue())) && !child.getChildCount() )
continue;
serialize( out, &child, indent + 1 ); serialize( out, &child, indent + 1 );
if ( indent == 0 ) out << std::endl; if ( indent == 0 ) out << std::endl;
} }

View File

@ -52,7 +52,8 @@ public:
void setValue(std::string const& path, long value); void setValue(std::string const& path, long value);
void setValue(std::string const& path, const char * value); void setValue(std::string const& path, const char * value);
void serialize(std::string & out); void serialize(std::string & out) const;
private: private:
void serialize(std::ostringstream & out, Bml_Node const* node, unsigned int indent) const; void serialize(std::ostringstream & out, Bml_Node const* node, unsigned int indent) const;
}; };

View File

@ -41,6 +41,8 @@ public:
// Info for currently playing track // Info for currently playing track
using Gme_File::track_info; using Gme_File::track_info;
blargg_err_t track_info( track_info_t* out ) const; blargg_err_t track_info( track_info_t* out ) const;
blargg_err_t set_track_info( const track_info_t* in );
blargg_err_t set_track_info( const track_info_t* in, int track_number );
struct Hash_Function struct Hash_Function
{ {
@ -48,7 +50,7 @@ public:
}; };
virtual blargg_err_t hash_( Hash_Function& ) const BLARGG_PURE( ; ) virtual blargg_err_t hash_( Hash_Function& ) const BLARGG_PURE( ; )
virtual blargg_err_t save( gme_writer_t, void* your_data) const { return "Not supported by this format"; } blargg_err_t save( gme_writer_t writer, void* your_data) const;
// Track status/control // Track status/control
@ -166,6 +168,11 @@ protected:
// Skip count samples. Count will always be even. // Skip count samples. Count will always be even.
virtual blargg_err_t skip_( int count ); virtual blargg_err_t skip_( int count );
// Save current state of file to specified writer.
virtual blargg_err_t save_( gme_writer_t, void* ) const { return "Not supported by this format"; }
// Set track info
virtual blargg_err_t set_track_info_( const track_info_t*, int ) { return "Not supported by this format"; }
// Implementation // Implementation
public: public:
@ -221,6 +228,21 @@ inline blargg_err_t Music_Emu::track_info( track_info_t* out ) const
return track_info( out, current_track_ ); return track_info( out, current_track_ );
} }
inline blargg_err_t Music_Emu::save(gme_writer_t writer, void *your_data) const
{
return save_( writer, your_data );
}
inline blargg_err_t Music_Emu::set_track_info(const track_info_t *in)
{
return set_track_info_( in, current_track_ );
}
inline blargg_err_t Music_Emu::set_track_info(const track_info_t *in, int track)
{
return set_track_info_( in, track );
}
inline int Music_Emu::sample_rate() const { return sample_rate_; } inline int Music_Emu::sample_rate() const { return sample_rate_; }
inline int Music_Emu::voice_count() const { return voice_count_; } inline int Music_Emu::voice_count() const { return voice_count_; }
inline int Music_Emu::current_track() const { return current_track_; } inline int Music_Emu::current_track() const { return current_track_; }

View File

@ -38,12 +38,65 @@ static void hash_sfm_file( byte const* data, int data_size, Music_Emu::Hash_Func
out.hash_( data, data_size ); out.hash_( data, data_size );
} }
static void copy_field( char* out, size_t size, const Bml_Parser& in, char const* in_path )
{
const char * value = in.enumValue( in_path );
if ( value ) strncpy( out, value, size - 1 ), out[ size - 1 ] = 0;
else out[ 0 ] = 0;
}
static void copy_info( track_info_t* out, const Bml_Parser& in )
{
copy_field( out->song, sizeof(out->song), in, "information:title" );
copy_field( out->game, sizeof(out->game), in, "information:game" );
copy_field( out->author, sizeof(out->author), in, "information:author" );
copy_field( out->composer, sizeof(out->composer), in, "information:composer" );
copy_field( out->copyright, sizeof(out->copyright), in, "information:copyright" );
copy_field( out->date, sizeof(out->date), in, "information:date" );
copy_field( out->track, sizeof(out->track), in, "information:track" );
copy_field( out->disc, sizeof(out->disc), in, "information:disc" );
copy_field( out->dumper, sizeof(out->dumper), in, "information:dumper" );
char * end;
const char * value = in.enumValue( "timing:length" );
if ( value )
out->length = strtoul( value, &end, 10 );
else
out->length = 0;
value = in.enumValue( "timing:fade" );
if ( value )
out->fade_length = strtoul( value, &end, 10 );
else
out->fade_length = 0;
}
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
{ {
const char * title = metadata.enumValue("information:title"); copy_info( out, metadata );
if (title) strncpy( out->song, title, 255 ); return blargg_ok;
else out->song[0] = 0; }
out->song[255] = '\0';
static void set_track_info( const track_info_t* in, Bml_Parser& out )
{
out.setValue( "information:title", in->song );
out.setValue( "information:game", in->game );
out.setValue( "information:author", in->author );
out.setValue( "information:composer", in->composer );
out.setValue( "information:copyright", in->copyright );
out.setValue( "information:date", in->date );
out.setValue( "information:track", in->track );
out.setValue( "information:disc", in->disc );
out.setValue( "information:dumper", in->dumper );
out.setValue( "timing:length", in->length );
out.setValue( "timing:fade", in->fade_length );
}
blargg_err_t Sfm_Emu::set_track_info_( const track_info_t* in, int )
{
::set_track_info(in, metadata);
return blargg_ok; return blargg_ok;
} }
@ -72,19 +125,19 @@ struct Sfm_File : Gme_Info_
if ( file_size < 8 ) if ( file_size < 8 )
return "SFM file too small"; return "SFM file too small";
int metadata_size = get_le32( data.begin() + 4 ); int metadata_size = get_le32( data.begin() + 4 );
byte temp = data[ 8 + metadata_size ]; metadata.parseDocument( (const char *)data.begin() + 8, metadata_size );
data[ 8 + metadata_size ] = '\0';
metadata.parseDocument( (const char *)data.begin() + 8 );
data[ 8 + metadata_size ] = temp;
return blargg_ok; return blargg_ok;
} }
blargg_err_t track_info_( track_info_t* out, int ) const blargg_err_t track_info_( track_info_t* out, int ) const
{ {
const char * title = metadata.enumValue("information:title"); copy_info( out, metadata );
if (title) strncpy( out->song, title, 255 ); return blargg_ok;
else out->song[0] = 0; }
out->song[255] = '\0';
blargg_err_t set_track_info_( const track_info_t* in, int )
{
::set_track_info( in, metadata );
return blargg_ok; return blargg_ok;
} }
@ -93,6 +146,11 @@ struct Sfm_File : Gme_Info_
hash_sfm_file( data.begin(), data.end() - data.begin(), out ); hash_sfm_file( data.begin(), data.end() - data.begin(), out );
return blargg_ok; return blargg_ok;
} }
blargg_err_t save_( gme_writer_t writer, void* your_data )
{
}
}; };
static Music_Emu* new_sfm_emu () { return BLARGG_NEW Sfm_Emu ; } static Music_Emu* new_sfm_emu () { return BLARGG_NEW Sfm_Emu ; }
@ -131,7 +189,15 @@ blargg_err_t Sfm_Emu::load_mem_( byte const in [], int size )
}; };
set_voice_names( names ); set_voice_names( names );
return check_sfm_header( in ); RETURN_ERR( check_sfm_header( in ) );
const byte * ptr = file_begin();
int metadata_size = get_le32(ptr + 4);
if ( file_size() < metadata_size + Sfm_Emu::sfm_min_file_size )
return "SFM file too small";
metadata.parseDocument((const char *) ptr + 8, metadata_size);
return blargg_ok;
} }
// Emulation // Emulation
@ -164,12 +230,7 @@ blargg_err_t Sfm_Emu::start_track_( int track )
resampler.clear(); resampler.clear();
filter.clear(); filter.clear();
const byte * ptr = file_begin(); const byte * ptr = file_begin();
if ( file_size() < Sfm_Emu::sfm_min_file_size )
return "SFM file too small";
int metadata_size = get_le32(ptr + 4); int metadata_size = get_le32(ptr + 4);
if ( file_size() < metadata_size + Sfm_Emu::sfm_min_file_size )
return "SFM file too small";
metadata.parseDocument((const char *) ptr + 8, metadata_size);
memcpy( smp.iplrom, ipl_rom, 64 ); memcpy( smp.iplrom, ipl_rom, 64 );
@ -179,11 +240,17 @@ blargg_err_t Sfm_Emu::start_track_( int track )
memcpy( smp.dsp.spc_dsp.m.regs, ptr + 8 + metadata_size + 65536, 128 ); memcpy( smp.dsp.spc_dsp.m.regs, ptr + 8 + metadata_size + 65536, 128 );
smp.set_sfm_queue( ptr + 8 + metadata_size + 65536 + 128, ptr + file_size() ); const uint8_t* log_begin = ptr + 8 + metadata_size + 65536 + 128;
const uint8_t* log_end = ptr + file_size();
size_t loop_begin = log_end - log_begin;
char * end; char * end;
const char * value; const char * value;
loop_begin = META_ENUM_INT("timing:loopstart", loop_begin);
smp.set_sfm_queue( log_begin, log_end, log_begin + loop_begin );
uint32_t test = META_ENUM_INT("smp:test", 0); uint32_t test = META_ENUM_INT("smp:test", 0);
smp.status.clock_speed = (test >> 6) & 3; smp.status.clock_speed = (test >> 6) & 3;
smp.status.timer_speed = (test >> 4) & 3; smp.status.timer_speed = (test >> 4) & 3;
@ -375,32 +442,32 @@ blargg_err_t Sfm_Emu::start_track_( int track )
#undef META_ENUM_INT #undef META_ENUM_INT
blargg_err_t Sfm_Emu::save( gme_writer_t writer, void* your_data ) const void Sfm_Emu::create_updated_metadata( Bml_Parser &out ) const
{ {
bool first; bool first;
std::string name; std::string name;
std::ostringstream oss; std::ostringstream oss;
Bml_Parser metadata;
const byte * ptr = file_begin();
int metadata_size = get_le32(ptr + 4);
metadata.parseDocument((const char *) ptr + 8, metadata_size);
metadata.setValue( "smp:test", (smp.status.clock_speed << 6) | (smp.status.timer_speed << 4) | (smp.status.timers_enable << 3) | (smp.status.ram_disable << 2) | (smp.status.ram_writable << 1) | (smp.status.timers_disable << 0) ); metadata.serialize(name);
metadata.setValue( "smp:iplrom", smp.status.iplrom_enable );
metadata.setValue( "smp:dspaddr", smp.status.dsp_addr ); out.parseDocument(name.c_str());
out.setValue( "smp:test", (smp.status.clock_speed << 6) | (smp.status.timer_speed << 4) | (smp.status.timers_enable << 3) | (smp.status.ram_disable << 2) | (smp.status.ram_writable << 1) | (smp.status.timers_disable << 0) );
out.setValue( "smp:iplrom", smp.status.iplrom_enable );
out.setValue( "smp:dspaddr", smp.status.dsp_addr );
oss.str(""); oss.str("");
oss.clear(); oss.clear();
oss << smp.status.ram00f8 << "," << smp.status.ram00f9; oss << smp.status.ram00f8 << "," << smp.status.ram00f9;
metadata.setValue( "smp:ram", oss.str().c_str() ); out.setValue( "smp:ram", oss.str().c_str() );
name = "smp:regs:"; name = "smp:regs:";
metadata.setValue( name + "pc", smp.regs.pc ); out.setValue( name + "pc", smp.regs.pc );
metadata.setValue( name + "a", smp.regs.a ); out.setValue( name + "a", smp.regs.a );
metadata.setValue( name + "x", smp.regs.x ); out.setValue( name + "x", smp.regs.x );
metadata.setValue( name + "y", smp.regs.y ); out.setValue( name + "y", smp.regs.y );
metadata.setValue( name + "s", smp.regs.s ); out.setValue( name + "s", smp.regs.s );
metadata.setValue( name + "psw", smp.regs.p ); out.setValue( name + "psw", smp.regs.p );
oss.str(""); oss.str("");
oss.clear(); oss.clear();
@ -411,7 +478,7 @@ blargg_err_t Sfm_Emu::save( gme_writer_t writer, void* your_data ) const
oss << (unsigned long)n; oss << (unsigned long)n;
first = false; first = false;
} }
metadata.setValue("smp:ports", oss.str().c_str()); out.setValue("smp:ports", oss.str().c_str());
for (int i = 0; i < 3; ++i) for (int i = 0; i < 3; ++i)
{ {
@ -420,19 +487,19 @@ blargg_err_t Sfm_Emu::save( gme_writer_t writer, void* your_data ) const
oss.clear(); oss.clear();
oss << "smp:timer[" << i << "]:"; oss << "smp:timer[" << i << "]:";
name = oss.str(); name = oss.str();
metadata.setValue( name + "enable", t.enable ); out.setValue( name + "enable", t.enable );
metadata.setValue( name + "target", t.target ); out.setValue( name + "target", t.target );
oss.str(""); oss.str("");
oss.clear(); oss.clear();
oss << (unsigned long)t.stage0_ticks << "," << (unsigned long)t.stage1_ticks << "," oss << (unsigned long)t.stage0_ticks << "," << (unsigned long)t.stage1_ticks << ","
<< (unsigned long)t.stage2_ticks << "," << (unsigned long)t.stage3_ticks; << (unsigned long)t.stage2_ticks << "," << (unsigned long)t.stage3_ticks;
metadata.setValue( name + "stage", oss.str().c_str() ); out.setValue( name + "stage", oss.str().c_str() );
metadata.setValue( name + "line", t.current_line ); out.setValue( name + "line", t.current_line );
} }
metadata.setValue( "dsp:clock", smp.dsp.clock / 4096 ); out.setValue( "dsp:clock", smp.dsp.clock / 4096 );
metadata.setValue( "dsp:echohistaddr", smp.dsp.spc_dsp.m.echo_hist_pos - smp.dsp.spc_dsp.m.echo_hist ); out.setValue( "dsp:echohistaddr", smp.dsp.spc_dsp.m.echo_hist_pos - smp.dsp.spc_dsp.m.echo_hist );
oss.str(""); oss.str("");
oss.clear(); oss.clear();
@ -442,41 +509,41 @@ blargg_err_t Sfm_Emu::save( gme_writer_t writer, void* your_data ) const
<< smp.dsp.spc_dsp.m.echo_hist[i][1]; << smp.dsp.spc_dsp.m.echo_hist[i][1];
if ( i != 7 ) oss << ","; if ( i != 7 ) oss << ",";
} }
metadata.setValue( "dsp:echohistdata", oss.str().c_str() ); out.setValue( "dsp:echohistdata", oss.str().c_str() );
metadata.setValue( "dsp:sample", smp.dsp.spc_dsp.m.phase ); out.setValue( "dsp:sample", smp.dsp.spc_dsp.m.phase );
metadata.setValue( "dsp:kon", smp.dsp.spc_dsp.m.kon ); out.setValue( "dsp:kon", smp.dsp.spc_dsp.m.kon );
metadata.setValue( "dsp:noise", smp.dsp.spc_dsp.m.noise ); out.setValue( "dsp:noise", smp.dsp.spc_dsp.m.noise );
metadata.setValue( "dsp:counter", smp.dsp.spc_dsp.m.counter ); out.setValue( "dsp:counter", smp.dsp.spc_dsp.m.counter );
metadata.setValue( "dsp:echooffset", smp.dsp.spc_dsp.m.echo_offset ); out.setValue( "dsp:echooffset", smp.dsp.spc_dsp.m.echo_offset );
metadata.setValue( "dsp:echolength", smp.dsp.spc_dsp.m.echo_length ); out.setValue( "dsp:echolength", smp.dsp.spc_dsp.m.echo_length );
metadata.setValue( "dsp:koncache", smp.dsp.spc_dsp.m.new_kon ); out.setValue( "dsp:koncache", smp.dsp.spc_dsp.m.new_kon );
metadata.setValue( "dsp:endx", smp.dsp.spc_dsp.m.endx_buf ); out.setValue( "dsp:endx", smp.dsp.spc_dsp.m.endx_buf );
metadata.setValue( "dsp:envx", smp.dsp.spc_dsp.m.envx_buf ); out.setValue( "dsp:envx", smp.dsp.spc_dsp.m.envx_buf );
metadata.setValue( "dsp:outx", smp.dsp.spc_dsp.m.outx_buf ); out.setValue( "dsp:outx", smp.dsp.spc_dsp.m.outx_buf );
metadata.setValue( "dsp:pmon", smp.dsp.spc_dsp.m.t_pmon ); out.setValue( "dsp:pmon", smp.dsp.spc_dsp.m.t_pmon );
metadata.setValue( "dsp:non", smp.dsp.spc_dsp.m.t_non ); out.setValue( "dsp:non", smp.dsp.spc_dsp.m.t_non );
metadata.setValue( "dsp:eon", smp.dsp.spc_dsp.m.t_eon ); out.setValue( "dsp:eon", smp.dsp.spc_dsp.m.t_eon );
metadata.setValue( "dsp:dir", smp.dsp.spc_dsp.m.t_dir ); out.setValue( "dsp:dir", smp.dsp.spc_dsp.m.t_dir );
metadata.setValue( "dsp:koff", smp.dsp.spc_dsp.m.t_koff ); out.setValue( "dsp:koff", smp.dsp.spc_dsp.m.t_koff );
metadata.setValue( "dsp:brrnext", smp.dsp.spc_dsp.m.t_brr_next_addr ); out.setValue( "dsp:brrnext", smp.dsp.spc_dsp.m.t_brr_next_addr );
metadata.setValue( "dsp:adsr0", smp.dsp.spc_dsp.m.t_adsr0 ); out.setValue( "dsp:adsr0", smp.dsp.spc_dsp.m.t_adsr0 );
metadata.setValue( "dsp:brrheader", smp.dsp.spc_dsp.m.t_brr_header ); out.setValue( "dsp:brrheader", smp.dsp.spc_dsp.m.t_brr_header );
metadata.setValue( "dsp:brrdata", smp.dsp.spc_dsp.m.t_brr_byte ); out.setValue( "dsp:brrdata", smp.dsp.spc_dsp.m.t_brr_byte );
metadata.setValue( "dsp:srcn", smp.dsp.spc_dsp.m.t_srcn ); out.setValue( "dsp:srcn", smp.dsp.spc_dsp.m.t_srcn );
metadata.setValue( "dsp:esa", smp.dsp.spc_dsp.m.t_esa ); out.setValue( "dsp:esa", smp.dsp.spc_dsp.m.t_esa );
metadata.setValue( "dsp:echodisable", !smp.dsp.spc_dsp.m.t_echo_enabled ); out.setValue( "dsp:echodisable", !smp.dsp.spc_dsp.m.t_echo_enabled );
metadata.setValue( "dsp:diraddr", smp.dsp.spc_dsp.m.t_dir_addr ); out.setValue( "dsp:diraddr", smp.dsp.spc_dsp.m.t_dir_addr );
metadata.setValue( "dsp:pitch", smp.dsp.spc_dsp.m.t_pitch ); out.setValue( "dsp:pitch", smp.dsp.spc_dsp.m.t_pitch );
metadata.setValue( "dsp:output", smp.dsp.spc_dsp.m.t_output ); out.setValue( "dsp:output", smp.dsp.spc_dsp.m.t_output );
metadata.setValue( "dsp:looped", smp.dsp.spc_dsp.m.t_looped ); out.setValue( "dsp:looped", smp.dsp.spc_dsp.m.t_looped );
metadata.setValue( "dsp:echoaddr", smp.dsp.spc_dsp.m.t_echo_ptr ); out.setValue( "dsp:echoaddr", smp.dsp.spc_dsp.m.t_echo_ptr );
#define META_WRITE_LEVELS(n, o) \ #define META_WRITE_LEVELS(n, o) \
oss.str(""); \ oss.str(""); \
oss.clear(); \ oss.clear(); \
oss << (o)[0] << "," << (o)[1]; \ oss << (o)[0] << "," << (o)[1]; \
metadata.setValue((n), oss.str().c_str()); out.setValue((n), oss.str().c_str());
META_WRITE_LEVELS("dsp:mainout", smp.dsp.spc_dsp.m.t_main_out); META_WRITE_LEVELS("dsp:mainout", smp.dsp.spc_dsp.m.t_main_out);
META_WRITE_LEVELS("dsp:echoout", smp.dsp.spc_dsp.m.t_echo_out); META_WRITE_LEVELS("dsp:echoout", smp.dsp.spc_dsp.m.t_echo_out);
@ -491,7 +558,7 @@ blargg_err_t Sfm_Emu::save( gme_writer_t writer, void* your_data ) const
oss << "dsp:voice[" << i << "]:"; oss << "dsp:voice[" << i << "]:";
name = oss.str(); name = oss.str();
SuperFamicom::SPC_DSP::voice_t const& voice = smp.dsp.spc_dsp.m.voices[i]; SuperFamicom::SPC_DSP::voice_t const& voice = smp.dsp.spc_dsp.m.voices[i];
metadata.setValue( name + "brrhistaddr", voice.buf_pos ); out.setValue( name + "brrhistaddr", voice.buf_pos );
oss.str(""); oss.str("");
oss.clear(); oss.clear();
for (int j = 0; j < SuperFamicom::SPC_DSP::brr_buf_size; ++j) for (int j = 0; j < SuperFamicom::SPC_DSP::brr_buf_size; ++j)
@ -500,29 +567,36 @@ blargg_err_t Sfm_Emu::save( gme_writer_t writer, void* your_data ) const
if ( j != SuperFamicom::SPC_DSP::brr_buf_size - 1 ) if ( j != SuperFamicom::SPC_DSP::brr_buf_size - 1 )
oss << ","; oss << ",";
} }
metadata.setValue( name + "brrhistdata", oss.str().c_str() ); out.setValue( name + "brrhistdata", oss.str().c_str() );
metadata.setValue( name + "interpaddr", voice.interp_pos ); out.setValue( name + "interpaddr", voice.interp_pos );
metadata.setValue( name + "brraddr", voice.brr_addr ); out.setValue( name + "brraddr", voice.brr_addr );
metadata.setValue( name + "brroffset", voice.brr_offset ); out.setValue( name + "brroffset", voice.brr_offset );
metadata.setValue( name + "vbit", voice.vbit ); out.setValue( name + "vbit", voice.vbit );
metadata.setValue( name + "vidx", voice.regs - smp.dsp.spc_dsp.m.regs); out.setValue( name + "vidx", voice.regs - smp.dsp.spc_dsp.m.regs);
metadata.setValue( name + "kondelay", voice.kon_delay ); out.setValue( name + "kondelay", voice.kon_delay );
metadata.setValue( name + "envmode", voice.env_mode ); out.setValue( name + "envmode", voice.env_mode );
metadata.setValue( name + "env", voice.env ); out.setValue( name + "env", voice.env );
metadata.setValue( name + "envxout", voice.t_envx_out ); out.setValue( name + "envxout", voice.t_envx_out );
metadata.setValue( name + "envcache", voice.hidden_env ); out.setValue( name + "envcache", voice.hidden_env );
}
} }
metadata.serialize( name ); blargg_err_t Sfm_Emu::save_( gme_writer_t writer, void* your_data ) const
{
std::string meta_serialized;
Bml_Parser metadata;
create_updated_metadata( metadata );
metadata.serialize( meta_serialized );
RETURN_ERR( writer( your_data, "SFM1", 4 ) ); RETURN_ERR( writer( your_data, "SFM1", 4 ) );
uint8_t temp[4]; uint8_t temp[4];
uint32_t meta_length = (uint32_t) name.length(); uint32_t meta_length = (uint32_t) meta_serialized.length();
set_le32( temp, meta_length ); set_le32( temp, meta_length );
RETURN_ERR( writer( your_data, temp, 4 ) ); RETURN_ERR( writer( your_data, temp, 4 ) );
RETURN_ERR( writer( your_data, name.c_str(), meta_length ) ); RETURN_ERR( writer( your_data, meta_serialized.c_str(), meta_length ) );
RETURN_ERR( writer( your_data, smp.apuram, 65536 ) ); RETURN_ERR( writer( your_data, smp.apuram, 65536 ) );

View File

@ -43,8 +43,6 @@ public:
static gme_type_t static_type() { return gme_sfm_type; } static gme_type_t static_type() { return gme_sfm_type; }
virtual blargg_err_t save( gme_writer_t, void* ) const;
// Implementation // Implementation
public: public:
Sfm_Emu(); Sfm_Emu();
@ -53,12 +51,14 @@ public:
protected: protected:
virtual blargg_err_t load_mem_( byte const [], int ); virtual blargg_err_t load_mem_( byte const [], int );
virtual blargg_err_t track_info_( track_info_t*, int track ) const; virtual blargg_err_t track_info_( track_info_t*, int track ) const;
virtual blargg_err_t set_track_info_( const track_info_t*, int track );
virtual blargg_err_t set_sample_rate_( int ); virtual blargg_err_t set_sample_rate_( int );
virtual blargg_err_t start_track_( int ); virtual blargg_err_t start_track_( int );
virtual blargg_err_t play_( int, sample_t [] ); virtual blargg_err_t play_( int, sample_t [] );
virtual blargg_err_t skip_( int ); virtual blargg_err_t skip_( int );
virtual void mute_voices_( int ); virtual void mute_voices_( int );
virtual void set_tempo_( double ); virtual void set_tempo_( double );
virtual blargg_err_t save_( gme_writer_t, void* ) const;
private: private:
Spc_Emu_Resampler resampler; Spc_Emu_Resampler resampler;
@ -66,6 +66,7 @@ private:
SuperFamicom::SMP smp; SuperFamicom::SMP smp;
Bml_Parser metadata; Bml_Parser metadata;
void create_updated_metadata(Bml_Parser &out) 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

@ -224,6 +224,8 @@ void gme_delete( Music_Emu* gme ) { delete gme; }
gme_type_t gme_type( Music_Emu const* gme ) { return gme->type(); } gme_type_t gme_type( Music_Emu const* gme ) { return gme->type(); }
const char* gme_type_system( gme_type_t_ const* type ) { return type->system; }
const char* gme_warning( Music_Emu* gme ) { return gme->warning(); } const char* gme_warning( Music_Emu* gme ) { return gme->warning(); }
int gme_track_count( Music_Emu const* gme ) { return gme->track_count(); } int gme_track_count( Music_Emu const* gme ) { return gme->track_count(); }
@ -301,6 +303,37 @@ gme_err_t gme_track_info( Music_Emu const* me, gme_info_t** out, int track )
return blargg_ok; return blargg_ok;
} }
gme_err_t gme_set_track_info( Music_Emu * me, gme_info_t* in, int track )
{
track_info_t* info = BLARGG_NEW track_info_t;
CHECK_ALLOC( info );
#define COPY(name) info->name = in->name;
COPY( length );
COPY( intro_length );
COPY( loop_length );
#undef COPY
#define COPY(name) if ( in->name ) strncpy( info->name, in->name, sizeof(info->name) - 1 ), info->name[sizeof(info->name)-1] = '\0'; else info->name[0] = '\0';
COPY( system );
COPY( game );
COPY( song );
COPY( author );
COPY( copyright );
COPY( comment );
COPY( dumper );
#undef COPY
blargg_err_t err = me->set_track_info( info, track );
delete info;
return err;
}
void gme_free_info( gme_info_t* info ) void gme_free_info( gme_info_t* info )
{ {
delete STATIC_CAST(gme_info_t_*,info); delete STATIC_CAST(gme_info_t_*,info);
@ -316,6 +349,7 @@ void gme_set_fade ( Music_Emu* gme, int start_msec, int length_msec )
gme_bool gme_track_ended ( Music_Emu const* gme ) { return gme->track_ended(); } gme_bool gme_track_ended ( Music_Emu const* gme ) { return gme->track_ended(); }
int gme_tell ( Music_Emu const* gme ) { return gme->tell(); } int gme_tell ( Music_Emu const* gme ) { return gme->tell(); }
gme_err_t gme_seek ( Music_Emu* gme, int msec ) { return gme->seek( msec ); } gme_err_t gme_seek ( Music_Emu* gme, int msec ) { return gme->seek( msec ); }
gme_err_t gme_skip ( Music_Emu* gme, int samples ) { return gme->skip( samples ); }
int gme_voice_count ( Music_Emu const* gme ) { return gme->voice_count(); } int gme_voice_count ( Music_Emu const* gme ) { return gme->voice_count(); }
void gme_ignore_silence ( Music_Emu* gme, gme_bool disable ) { gme->ignore_silence( disable != 0 ); } void gme_ignore_silence ( Music_Emu* gme, gme_bool disable ) { gme->ignore_silence( disable != 0 ); }
void gme_set_tempo ( Music_Emu* gme, double t ) { gme->set_tempo( t ); } void gme_set_tempo ( Music_Emu* gme, double t ) { gme->set_tempo( t ); }

View File

@ -55,6 +55,9 @@ int gme_tell( const gme_t* );
/* Seeks to new time in track. Seeking backwards or far forward can take a while. */ /* Seeks to new time in track. Seeking backwards or far forward can take a while. */
gme_err_t gme_seek( gme_t*, int msec ); gme_err_t gme_seek( gme_t*, int msec );
/* Skips the specified number of samples. */
gme_err_t gme_skip( gme_t*, int samples );
/******** Informational ********/ /******** Informational ********/
@ -78,6 +81,8 @@ Must be freed after use. */
typedef struct gme_info_t gme_info_t; typedef struct gme_info_t gme_info_t;
gme_err_t gme_track_info( const gme_t*, gme_info_t** out, int track ); gme_err_t gme_track_info( const gme_t*, gme_info_t** out, int track );
gme_err_t gme_set_track_info( gme_t*, const gme_info_t* in, int track );
/* Frees track information */ /* Frees track information */
void gme_free_info( gme_info_t* ); void gme_free_info( gme_info_t* );

View File

@ -22,14 +22,6 @@ void SMP::port_write(uint8_t port, uint8_t data) {
uint8_t SMP::op_busread(uint16_t addr) { uint8_t SMP::op_busread(uint16_t addr) {
unsigned result; unsigned result;
if ( !(dsp.read( SPC_DSP::r_flg ) & 0x20) ) {
int start = 0x100 * dsp.read( SPC_DSP::r_esa );
int end = start + 0x800 * (dsp.read( SPC_DSP::r_edl ) & 0x0F);
if ( end > 0x10000 )
end = 0x10000;
if ( addr >= start || addr < end) synchronize_dsp();
}
switch(addr) { switch(addr) {
case 0xf0: //TEST -- write-only register case 0xf0: //TEST -- write-only register
return 0x00; return 0x00;
@ -42,7 +34,6 @@ uint8_t SMP::op_busread(uint16_t addr) {
case 0xf3: //DSPDATA case 0xf3: //DSPDATA
//0x80-0xff are read-only mirrors of 0x00-0x7f //0x80-0xff are read-only mirrors of 0x00-0x7f
synchronize_dsp();
return dsp.read(status.dsp_addr & 0x7f); return dsp.read(status.dsp_addr & 0x7f);
case 0xf4: //CPUIO0 case 0xf4: //CPUIO0
@ -50,8 +41,11 @@ uint8_t SMP::op_busread(uint16_t addr) {
case 0xf6: //CPUIO2 case 0xf6: //CPUIO2
case 0xf7: //CPUIO3 case 0xf7: //CPUIO3
if (sfm_queue && sfm_queue < sfm_queue_end) { if (sfm_queue && sfm_queue < sfm_queue_end) {
sfm_last[addr - 0xf4] = *sfm_queue; result = *sfm_queue;
return *sfm_queue++; if (++sfm_queue == sfm_queue_end)
sfm_queue = sfm_queue_repeat;
sfm_last[addr - 0xf4] = result;
return result;
} }
return sfm_last[addr - 0xf4]; return sfm_last[addr - 0xf4];
@ -86,7 +80,6 @@ uint8_t SMP::op_busread(uint16_t addr) {
} }
void SMP::op_buswrite(uint16_t addr, uint8_t data) { void SMP::op_buswrite(uint16_t addr, uint8_t data) {
synchronize_dsp();
switch(addr) { switch(addr) {
case 0xf0: //TEST case 0xf0: //TEST
if(regs.p.p) break; //writes only valid when P flag is clear if(regs.p.p) break; //writes only valid when P flag is clear

View File

@ -139,7 +139,7 @@ void SMP::reset() {
SMP::SMP() : dsp( *this ), timer0( *this ), timer1( *this ), timer2( *this ), clock( 0 ) { SMP::SMP() : dsp( *this ), timer0( *this ), timer1( *this ), timer2( *this ), clock( 0 ) {
for(auto& byte : iplrom) byte = 0; for(auto& byte : iplrom) byte = 0;
set_sfm_queue(0, 0); set_sfm_queue(0, 0, 0);
set_tempo(1.0); set_tempo(1.0);
} }

View File

@ -37,8 +37,9 @@ struct SMP : Processor::SPC700 {
private: private:
uint8_t const* sfm_queue; uint8_t const* sfm_queue;
uint8_t const* sfm_queue_end; uint8_t const* sfm_queue_end;
uint8_t const* sfm_queue_repeat;
public: public:
void set_sfm_queue(const uint8_t* queue, const uint8_t* queue_end); void set_sfm_queue(const uint8_t* queue, const uint8_t* queue_end, const uint8_t* queue_repeat);
const uint8_t* get_sfm_queue() const; const uint8_t* get_sfm_queue() const;
size_t get_sfm_queue_remain() const; size_t get_sfm_queue_remain() const;
@ -120,7 +121,7 @@ public:
inline void SMP::set_tempo(double speed) { dsp_clock_step = (int64_t)(4096.0 / speed); } inline void SMP::set_tempo(double speed) { dsp_clock_step = (int64_t)(4096.0 / speed); }
inline void SMP::set_sfm_queue(const uint8_t *queue, const uint8_t *queue_end) { sfm_queue = queue; sfm_queue_end = queue_end; sfm_last[0] = 0; sfm_last[1] = 0; sfm_last[2] = 0; sfm_last[3] = 0; } inline void SMP::set_sfm_queue(const uint8_t *queue, const uint8_t *queue_end, const uint8_t *queue_repeat) { sfm_queue = queue; sfm_queue_end = queue_end; sfm_queue_repeat = queue_repeat; sfm_last[0] = 0; sfm_last[1] = 0; sfm_last[2] = 0; sfm_last[3] = 0; }
inline const uint8_t* SMP::get_sfm_queue() const { return sfm_queue; } inline const uint8_t* SMP::get_sfm_queue() const { return sfm_queue; }
inline size_t SMP::get_sfm_queue_remain() const { return sfm_queue_end - sfm_queue; } inline size_t SMP::get_sfm_queue_remain() const { return sfm_queue_end - sfm_queue; }

View File

@ -2,7 +2,7 @@
void SMP::add_clocks(unsigned clocks) { void SMP::add_clocks(unsigned clocks) {
step(clocks); step(clocks);
//synchronize_dsp(); synchronize_dsp();
} }
void SMP::cycle_edge() { void SMP::cycle_edge() {