summaryrefslogtreecommitdiff
path: root/apps/codecs/libgme
diff options
context:
space:
mode:
authorAndree Buschmann <AndreeBuschmann@t-online.de>2011-08-31 19:19:49 +0000
committerAndree Buschmann <AndreeBuschmann@t-online.de>2011-08-31 19:19:49 +0000
commit13cbade08a07296d92e7a7d3e20475de0032cba1 (patch)
tree731a1a4a99d86632a719ae49e3b3d2a12e764a3a /apps/codecs/libgme
parentd089e104034fdf5562bea125d2cacf4ee486782a (diff)
downloadrockbox-13cbade08a07296d92e7a7d3e20475de0032cba1.zip
rockbox-13cbade08a07296d92e7a7d3e20475de0032cba1.tar.gz
rockbox-13cbade08a07296d92e7a7d3e20475de0032cba1.tar.bz2
rockbox-13cbade08a07296d92e7a7d3e20475de0032cba1.tar.xz
Update libgme to Blargg's Game_Music_Emu 0.6-pre.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@30397 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'apps/codecs/libgme')
-rw-r--r--apps/codecs/libgme/AYSOURCES1
-rw-r--r--apps/codecs/libgme/GBSSOURCES1
-rw-r--r--apps/codecs/libgme/HESSOURCES1
-rw-r--r--apps/codecs/libgme/KSSSOURCES1
-rw-r--r--apps/codecs/libgme/NSFSOURCES1
-rw-r--r--apps/codecs/libgme/SGCSOURCES1
-rw-r--r--apps/codecs/libgme/VGMSOURCES1
-rw-r--r--apps/codecs/libgme/ay_emu.c356
-rw-r--r--apps/codecs/libgme/ay_emu.h71
-rw-r--r--apps/codecs/libgme/blargg_common.h22
-rw-r--r--apps/codecs/libgme/blargg_endian.h77
-rw-r--r--apps/codecs/libgme/blargg_source.h39
-rw-r--r--apps/codecs/libgme/blip_buffer.c237
-rw-r--r--apps/codecs/libgme/blip_buffer.h342
-rw-r--r--apps/codecs/libgme/gbs_cpu.c32
-rw-r--r--apps/codecs/libgme/gbs_emu.c355
-rw-r--r--apps/codecs/libgme/gbs_emu.h82
-rw-r--r--apps/codecs/libgme/gme_types.h21
-rw-r--r--apps/codecs/libgme/hes_apu.c306
-rw-r--r--apps/codecs/libgme/hes_apu.h43
-rw-r--r--apps/codecs/libgme/hes_apu_adpcm.h4
-rw-r--r--apps/codecs/libgme/hes_cpu.c1356
-rw-r--r--apps/codecs/libgme/hes_cpu.h119
-rw-r--r--apps/codecs/libgme/hes_cpu_io.h72
-rw-r--r--apps/codecs/libgme/hes_cpu_run.h1344
-rw-r--r--apps/codecs/libgme/hes_emu.c581
-rw-r--r--apps/codecs/libgme/hes_emu.h156
-rw-r--r--apps/codecs/libgme/kss_emu.c373
-rw-r--r--apps/codecs/libgme/kss_emu.h69
-rw-r--r--apps/codecs/libgme/kss_scc_apu.h2
-rw-r--r--apps/codecs/libgme/multi_buffer.c390
-rw-r--r--apps/codecs/libgme/multi_buffer.h108
-rw-r--r--apps/codecs/libgme/nes_apu.c24
-rw-r--r--apps/codecs/libgme/nes_apu.h43
-rw-r--r--apps/codecs/libgme/nes_cpu_io.h94
-rw-r--r--apps/codecs/libgme/nes_fme7_apu.c7
-rw-r--r--apps/codecs/libgme/nes_fme7_apu.h2
-rw-r--r--apps/codecs/libgme/nes_namco_apu.c15
-rw-r--r--apps/codecs/libgme/nes_namco_apu.h4
-rw-r--r--apps/codecs/libgme/nes_oscs.c73
-rw-r--r--apps/codecs/libgme/nes_oscs.h18
-rw-r--r--apps/codecs/libgme/nes_vrc6_apu.c2
-rw-r--r--apps/codecs/libgme/nes_vrc6_apu.h4
-rw-r--r--apps/codecs/libgme/nsf_emu.c496
-rw-r--r--apps/codecs/libgme/nsf_emu.h104
-rw-r--r--apps/codecs/libgme/nsfe_info.c10
-rw-r--r--apps/codecs/libgme/opl_apu.h4
-rw-r--r--apps/codecs/libgme/resampler.c22
-rw-r--r--apps/codecs/libgme/resampler.h13
-rw-r--r--apps/codecs/libgme/rom_data.c8
-rw-r--r--apps/codecs/libgme/rom_data.h28
-rw-r--r--apps/codecs/libgme/sgc_emu.c319
-rw-r--r--apps/codecs/libgme/sgc_emu.h76
-rw-r--r--apps/codecs/libgme/sms_apu.c4
-rw-r--r--apps/codecs/libgme/sms_apu.h4
-rw-r--r--apps/codecs/libgme/sms_fm_apu.h8
-rw-r--r--apps/codecs/libgme/track_filter.c294
-rw-r--r--apps/codecs/libgme/track_filter.h90
-rw-r--r--apps/codecs/libgme/vgm_emu.c388
-rw-r--r--apps/codecs/libgme/vgm_emu.h103
-rw-r--r--apps/codecs/libgme/ym2612_emu.c1
61 files changed, 4021 insertions, 4801 deletions
diff --git a/apps/codecs/libgme/AYSOURCES b/apps/codecs/libgme/AYSOURCES
index 51253fe..f2b52a5 100644
--- a/apps/codecs/libgme/AYSOURCES
+++ b/apps/codecs/libgme/AYSOURCES
@@ -3,4 +3,5 @@ ay_cpu.c
ay_emu.c
blip_buffer.c
multi_buffer.c
+track_filter.c
z80_cpu.c
diff --git a/apps/codecs/libgme/GBSSOURCES b/apps/codecs/libgme/GBSSOURCES
index 5548fd8..a839a21 100644
--- a/apps/codecs/libgme/GBSSOURCES
+++ b/apps/codecs/libgme/GBSSOURCES
@@ -6,3 +6,4 @@ gbs_emu.c
blip_buffer.c
multi_buffer.c
rom_data.c
+track_filter.c
diff --git a/apps/codecs/libgme/HESSOURCES b/apps/codecs/libgme/HESSOURCES
index 58a38f2..d529388 100644
--- a/apps/codecs/libgme/HESSOURCES
+++ b/apps/codecs/libgme/HESSOURCES
@@ -5,3 +5,4 @@ hes_emu.c
blip_buffer.c
multi_buffer.c
rom_data.c
+track_filter.c
diff --git a/apps/codecs/libgme/KSSSOURCES b/apps/codecs/libgme/KSSSOURCES
index 2607c73..bde2133 100644
--- a/apps/codecs/libgme/KSSSOURCES
+++ b/apps/codecs/libgme/KSSSOURCES
@@ -9,3 +9,4 @@ multi_buffer.c
rom_data.c
emu8950.c
emuadpcm.c
+track_filter.c
diff --git a/apps/codecs/libgme/NSFSOURCES b/apps/codecs/libgme/NSFSOURCES
index 54b4f82..60537ff 100644
--- a/apps/codecs/libgme/NSFSOURCES
+++ b/apps/codecs/libgme/NSFSOURCES
@@ -12,3 +12,4 @@ nsfe_info.c
blip_buffer.c
multi_buffer.c
rom_data.c
+track_filter.c
diff --git a/apps/codecs/libgme/SGCSOURCES b/apps/codecs/libgme/SGCSOURCES
index d91c0e1..d0e8abc 100644
--- a/apps/codecs/libgme/SGCSOURCES
+++ b/apps/codecs/libgme/SGCSOURCES
@@ -4,3 +4,4 @@ z80_cpu.c
blip_buffer.c
multi_buffer.c
rom_data.c
+track_filter.c
diff --git a/apps/codecs/libgme/VGMSOURCES b/apps/codecs/libgme/VGMSOURCES
index 637f87e..bb57e16 100644
--- a/apps/codecs/libgme/VGMSOURCES
+++ b/apps/codecs/libgme/VGMSOURCES
@@ -3,6 +3,7 @@ multi_buffer.c
resampler.c
vgm_emu.c
ym2612_emu.c
+track_filter.c
inflate/bbfuncs.c
inflate/inflate.c
inflate/mallocer.c
diff --git a/apps/codecs/libgme/ay_emu.c b/apps/codecs/libgme/ay_emu.c
index e961797..42e739f 100644
--- a/apps/codecs/libgme/ay_emu.c
+++ b/apps/codecs/libgme/ay_emu.c
@@ -17,12 +17,6 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
#include "blargg_source.h"
-int const stereo = 2; // number of channels for stereo
-int const silence_max = 6; // seconds
-int const silence_threshold = 0x10;
-long const fade_block_size = 512;
-int const fade_shift = 8; // fade ends with gain at 1.0 / (1 << fade_shift)
-
const char* const gme_wrong_file_type = "Wrong file type for this emulator";
// TODO: probably don't need detailed errors as to why file is corrupt
@@ -38,16 +32,7 @@ int const cpc_clock = 2000000;
static void clear_track_vars( struct Ay_Emu *this )
{
this->current_track = -1;
- this->out_time = 0;
- this->emu_time = 0;
- this->emu_track_ended_ = true;
- this->track_ended = true;
- this->fade_start = INT_MAX / 2 + 1;
- this->fade_step = 1;
- this->silence_time = 0;
- this->silence_count = 0;
- this->buf_remain = 0;
- /* warning(); // clear warning */
+ track_stop( &this->track_filter );
}
void Ay_init( struct Ay_Emu *this )
@@ -59,18 +44,21 @@ void Ay_init( struct Ay_Emu *this )
this->track_count = 0;
// defaults
- this->max_initial_silence = 2;
- this->ignore_silence = false;
+ this->tfilter = *track_get_setup( &this->track_filter );
+ this->tfilter.max_initial = 2;
+ this->tfilter.lookahead = 6;
+ this->track_filter.silence_ignored_ = false;
- this->voice_count = 0;
- clear_track_vars( this );
this->beeper_output = NULL;
disable_beeper( this );
Ay_apu_init( &this->apu );
Z80_init( &this->cpu );
-
- this->silence_lookahead = 6 ;
+
+ // clears fields
+ this->voice_count = 0;
+ this->voice_types = 0;
+ clear_track_vars( this );
}
// Track info
@@ -107,35 +95,22 @@ static blargg_err_t parse_header( byte const in [], int size, struct file_t* out
return 0;
}
-long Track_get_length( struct Ay_Emu* this, int n )
-{
- long length = 0;
-
- byte const* track_info = get_data( &this->file, this->file.tracks + n * 4 + 2, 6 );
- if ( track_info )
- length = get_be16( track_info + 4 ) * (1000 / 50); // frames to msec
-
- if ( (this->m3u.size > 0) && (n < this->m3u.size) ) {
- struct entry_t* entry = &this->m3u.entries [n];
- length = entry->length;
- }
-
- if ( length <= 0 )
- length = 120 * 1000; /* 2 minutes */
-
- return length;
-}
-
// Setup
-static void change_clock_rate( struct Ay_Emu *this, long rate )
+static void change_clock_rate( struct Ay_Emu *this, int rate )
{
this->clock_rate_ = rate;
Buffer_clock_rate( &this->stereo_buf, rate );
}
-blargg_err_t Ay_load_mem( struct Ay_Emu *this, byte const in [], int size )
+blargg_err_t Ay_load_mem( struct Ay_Emu *this, byte const in [], long size )
{
+ // Unload
+ this->voice_count = 0;
+ this->track_count = 0;
+ this->m3u.size = 0;
+ clear_track_vars( this );
+
assert( offsetof (struct header_t,track_info [2]) == header_size );
RETURN_ERR( parse_header( in, size, &this->file ) );
@@ -144,19 +119,22 @@ blargg_err_t Ay_load_mem( struct Ay_Emu *this, byte const in [], int size )
warning( "Unknown file version" ); */
this->voice_count = ay_osc_count + 1; // +1 for beeper
+ static int const types [ay_osc_count + 1] = {
+ wave_type+0, wave_type+1, wave_type+2, mixed_type+1
+ };
+ this->voice_types = types;
+
Ay_apu_volume( &this->apu, this->gain);
// Setup buffer
change_clock_rate( this, spectrum_clock );
+ RETURN_ERR( Buffer_set_channel_count( &this->stereo_buf, this->voice_count, this->voice_types ) );
this->buf_changed_count = Buffer_channels_changed_count( &this->stereo_buf );
Sound_set_tempo( this, this->tempo );
-
- // Remute voices
Sound_mute_voices( this, this->mute_mask_ );
this->track_count = this->file.header->max_track + 1;
- this->m3u.size = 0;
return 0;
}
@@ -298,7 +276,7 @@ enable_cpc:
}
}
-blargg_err_t Ay_set_sample_rate( struct Ay_Emu *this, long rate )
+blargg_err_t Ay_set_sample_rate( struct Ay_Emu *this, int rate )
{
require( !this->sample_rate ); // sample rate can't be changed once set
Buffer_init( &this->stereo_buf );
@@ -308,6 +286,8 @@ blargg_err_t Ay_set_sample_rate( struct Ay_Emu *this, long rate )
Buffer_bass_freq( &this->stereo_buf, 160 );
this->sample_rate = rate;
+ RETURN_ERR( track_init( &this->track_filter, this ) );
+ this->tfilter.max_silence = 6 * stereo * this->sample_rate;
return 0;
}
@@ -335,7 +315,7 @@ void Sound_mute_voices( struct Ay_Emu *this, int mask )
}
else
{
- struct channel_t ch = Buffer_channel( &this->stereo_buf );
+ struct channel_t ch = Buffer_channel( &this->stereo_buf, i );
assert( (ch.center && ch.left && ch.right) ||
(!ch.center && !ch.left && !ch.right) ); // all or nothing
set_voice( this, i, ch.center );
@@ -359,7 +339,6 @@ void Sound_set_tempo( struct Ay_Emu *this, int t )
this->play_period = (blip_time_t) ((p * FP_ONE_TEMPO) / t);
}
-void fill_buf( struct Ay_Emu *this );;
blargg_err_t Ay_start_track( struct Ay_Emu *this, int track )
{
clear_track_vars( this );
@@ -496,272 +475,109 @@ blargg_err_t Ay_start_track( struct Ay_Emu *this, int track )
Ay_apu_write_addr( &this->apu, 7 );
Ay_apu_write_data( &this->apu, 0, 0x38 );
- this->emu_track_ended_ = false;
- this->track_ended = false;
+ // convert filter times to samples
+ struct setup_t s = this->tfilter;
+ s.max_initial *= this->sample_rate * stereo;
+ #ifdef GME_DISABLE_SILENCE_LOOKAHEAD
+ s.lookahead = 1;
+ #endif
+ track_setup( &this->track_filter, &s );
- if ( !this->ignore_silence )
- {
- // play until non-silence or end of track
- long end;
- for ( end = this->max_initial_silence * stereo * this->sample_rate; this->emu_time < end; )
- {
- fill_buf( this );
- if ( this->buf_remain | (int) this->emu_track_ended_ )
- break;
- }
-
- this->emu_time = this->buf_remain;
- this->out_time = 0;
- this->silence_time = 0;
- this->silence_count = 0;
- }
- /* return track_ended() ? warning() : 0; */
- return 0;
+ return track_start( &this->track_filter );
}
// Tell/Seek
-static blargg_long msec_to_samples( blargg_long msec, long sample_rate )
+static int msec_to_samples( int msec, int sample_rate )
{
- blargg_long sec = msec / 1000;
+ int sec = msec / 1000;
msec -= sec * 1000;
return (sec * sample_rate + msec * sample_rate / 1000) * stereo;
}
-long Track_tell( struct Ay_Emu *this )
+int Track_tell( struct Ay_Emu *this )
{
- blargg_long rate = this->sample_rate * stereo;
- blargg_long sec = this->out_time / rate;
- return sec * 1000 + (this->out_time - sec * rate) * 1000 / rate;
+ int rate = this->sample_rate * stereo;
+ int sec = track_sample_count( &this->track_filter ) / rate;
+ return sec * 1000 + (track_sample_count( &this->track_filter ) - sec * rate) * 1000 / rate;
}
-blargg_err_t Track_seek( struct Ay_Emu *this, long msec )
+blargg_err_t Track_seek( struct Ay_Emu *this, int msec )
{
- blargg_long time = msec_to_samples( msec, this->sample_rate );
- if ( time < this->out_time )
- RETURN_ERR( Ay_start_track( this, this->current_track ) );
- return Track_skip( this, time - this->out_time );
+ int time = msec_to_samples( msec, this->sample_rate );
+ if ( time < track_sample_count( &this->track_filter ) )
+ RETURN_ERR( Ay_start_track( this, this->current_track ) );
+ return Track_skip( this, time - track_sample_count( &this->track_filter ) );
}
-blargg_err_t play_( struct Ay_Emu *this, long count, sample_t* out );
-static blargg_err_t skip_( struct Ay_Emu *this, long count )
+blargg_err_t skip_( void *emu, int count )
{
+ struct Ay_Emu* this = (struct Ay_Emu*) emu;
+
// for long skip, mute sound
- const long threshold = 30000;
+ const int threshold = 32768;
if ( count > threshold )
{
int saved_mute = this->mute_mask_;
Sound_mute_voices( this, ~0 );
-
- while ( count > threshold / 2 && !this->emu_track_ended_ )
- {
- RETURN_ERR( play_( this, buf_size, this->buf ) );
- count -= buf_size;
- }
-
- Sound_mute_voices( this, saved_mute );
- }
-
- while ( count && !this->emu_track_ended_ )
- {
- long n = buf_size;
- if ( n > count )
- n = count;
- count -= n;
- RETURN_ERR( play_( this, n, this->buf ) );
- }
- return 0;
-}
-blargg_err_t Track_skip( struct Ay_Emu *this, long count )
-{
- require( this->current_track >= 0 ); // start_track() must have been called already
- this->out_time += count;
-
- // remove from silence and buf first
- {
- long n = min( count, this->silence_count );
- this->silence_count -= n;
+ int n = count - threshold/2;
+ n &= ~(2048-1); // round to multiple of 2048
count -= n;
-
- n = min( count, this->buf_remain );
- this->buf_remain -= n;
- count -= n;
- }
-
- if ( count && !this->emu_track_ended_ )
- {
- this->emu_time += count;
-
- // End track if error
- if ( skip_( this, count ) )
- this->emu_track_ended_ = true;
- }
-
- if ( !(this->silence_count | this->buf_remain) ) // caught up to emulator, so update track ended
- this->track_ended |= this->emu_track_ended_;
-
- return 0;
-}
+ RETURN_ERR( skippy_( &this->track_filter, n ) );
-// Fading
+ Sound_mute_voices( this, saved_mute );
+ }
-void Track_set_fade( struct Ay_Emu *this, long start_msec, long length_msec )
-{
- this->fade_step = this->sample_rate * length_msec / (fade_block_size * fade_shift * 1000 / stereo);
- this->fade_start = msec_to_samples( start_msec, this->sample_rate );
+ return skippy_( &this->track_filter, count );
}
-// unit / pow( 2.0, (double) x / step )
-static int int_log( blargg_long x, int step, int unit )
+blargg_err_t Track_skip( struct Ay_Emu *this, int count )
{
- int shift = x / step;
- int fraction = (x - shift * step) * unit / step;
- return ((unit - fraction) + (fraction >> 1)) >> shift;
+ require( this->current_track >= 0 ); // start_track() must have been called already
+ return track_skip( &this->track_filter, count );
}
-static void handle_fade( struct Ay_Emu *this, long out_count, sample_t* out )
+int Track_get_length( struct Ay_Emu* this, int n )
{
- int i;
- for ( i = 0; i < out_count; i += fade_block_size )
- {
- int const shift = 14;
- int const unit = 1 << shift;
- int gain = int_log( (this->out_time + i - this->fade_start) / fade_block_size,
- this->fade_step, unit );
- if ( gain < (unit >> fade_shift) )
- this->track_ended = this->emu_track_ended_ = true;
-
- sample_t* io = &out [i];
- int count;
- for ( count = min( fade_block_size, out_count - i ); count; --count )
- {
- *io = (sample_t) ((*io * gain) >> shift);
- ++io;
- }
- }
-}
+ int length = 0;
-// Silence detection
+ byte const* track_info = get_data( &this->file, this->file.tracks + n * 4 + 2, 6 );
+ if ( track_info )
+ length = get_be16( track_info + 4 ) * (1000 / 50); // frames to msec
-static void emu_play( struct Ay_Emu *this, long count, sample_t* out )
-{
- check( current_track_ >= 0 );
- this->emu_time += count;
- if ( this->current_track >= 0 && !this->emu_track_ended_ ) {
- if ( play_( this, count, out ) )
- this->emu_track_ended_ = true;
- }
- else
- memset( out, 0, count * sizeof *out );
-}
+ if ( (this->m3u.size > 0) && (n < this->m3u.size) ) {
+ struct entry_t* entry = &this->m3u.entries [n];
+ length = entry->length;
+ }
+
+ if ( length <= 0 )
+ length = 120 * 1000; /* 2 minutes */
-// number of consecutive silent samples at end
-static long count_silence( sample_t* begin, long size )
-{
- sample_t first = *begin;
- *begin = silence_threshold; // sentinel
- sample_t* p = begin + size;
- while ( (unsigned) (*--p + silence_threshold / 2) <= (unsigned) silence_threshold ) { }
- *begin = first;
- return size - (p - begin);
+ return length;
}
-// fill internal buffer and check it for silence
-void fill_buf( struct Ay_Emu *this )
+void Track_set_fade( struct Ay_Emu *this, int start_msec, int length_msec )
{
- assert( !this->buf_remain );
- if ( !this->emu_track_ended_ )
- {
- emu_play( this, buf_size, this->buf );
- long silence = count_silence( this->buf, buf_size );
- if ( silence < buf_size )
- {
- this->silence_time = this->emu_time - silence;
- this->buf_remain = buf_size;
- return;
- }
- }
- this->silence_count += buf_size;
+ track_set_fade( &this->track_filter, msec_to_samples( start_msec, this->sample_rate ),
+ length_msec * this->sample_rate / (1000 / stereo) );
}
-blargg_err_t Ay_play( struct Ay_Emu *this, long out_count, sample_t* out )
+blargg_err_t Ay_play( struct Ay_Emu *this, int out_count, sample_t* out )
{
- if ( this->track_ended )
- {
- memset( out, 0, out_count * sizeof *out );
- }
- else
- {
- require( this->current_track >= 0 );
- require( out_count % stereo == 0 );
-
- assert( this->emu_time >= this->out_time );
-
- // prints nifty graph of how far ahead we are when searching for silence
- //debug_printf( "%*s \n", int ((emu_time - out_time) * 7 / sample_rate()), "*" );
-
- long pos = 0;
- if ( this->silence_count )
- {
- // during a run of silence, run emulator at >=2x speed so it gets ahead
- long ahead_time = this->silence_lookahead * (this->out_time + out_count - this->silence_time) + this->silence_time;
- while ( this->emu_time < ahead_time && !(this->buf_remain | this->emu_track_ended_) )
- fill_buf( this );
-
- // fill with silence
- pos = min( this->silence_count, out_count );
- memset( out, 0, pos * sizeof *out );
- this->silence_count -= pos;
-
- if ( this->emu_time - this->silence_time > silence_max * stereo * this->sample_rate )
- {
- this->track_ended = this->emu_track_ended_ = true;
- this->silence_count = 0;
- this->buf_remain = 0;
- }
- }
-
- if ( this->buf_remain )
- {
- // empty silence buf
- long n = min( this->buf_remain, out_count - pos );
- memcpy( &out [pos], this->buf + (buf_size - this->buf_remain), n * sizeof *out );
- this->buf_remain -= n;
- pos += n;
- }
-
- // generate remaining samples normally
- long remain = out_count - pos;
- if ( remain )
- {
- emu_play( this, remain, out + pos );
- this->track_ended |= this->emu_track_ended_;
-
- if ( !this->ignore_silence || this->out_time > this->fade_start )
- {
- // check end for a new run of silence
- long silence = count_silence( out + pos, remain );
- if ( silence < remain )
- this->silence_time = this->emu_time - silence;
-
- if ( this->emu_time - this->silence_time >= buf_size )
- fill_buf( this ); // cause silence detection on next play()
- }
- }
-
- if ( this->out_time > this->fade_start )
- handle_fade( this, out_count, out );
- }
- this->out_time += out_count;
- return 0;
+ require( this->current_track >= 0 );
+ require( out_count % stereo == 0 );
+ return track_play( &this->track_filter, out_count, out );
}
-blargg_err_t play_( struct Ay_Emu *this, long count, sample_t* out )
+blargg_err_t play_( void *emu, int count, sample_t* out )
{
- long remain = count;
+ struct Ay_Emu* this = (struct Ay_Emu*) emu;
+
+ int remain = count;
while ( remain )
{
+ Buffer_disable_immediate_removal( &this->stereo_buf );
remain -= Buffer_read_samples( &this->stereo_buf, &out [count - remain], remain );
if ( remain )
{
@@ -773,7 +589,7 @@ blargg_err_t play_( struct Ay_Emu *this, long count, sample_t* out )
Sound_mute_voices( this, this->mute_mask_ );
}
int msec = Buffer_length( &this->stereo_buf );
- blip_time_t clocks_emulated = (blargg_long) msec * this->clock_rate_ / 1000 - 100;
+ blip_time_t clocks_emulated = msec * this->clock_rate_ / 1000 - 100;
RETURN_ERR( run_clocks( this, &clocks_emulated, msec ) );
assert( clocks_emulated );
Buffer_end_frame( &this->stereo_buf, clocks_emulated );
diff --git a/apps/codecs/libgme/ay_emu.h b/apps/codecs/libgme/ay_emu.h
index b320e69..7334167 100644
--- a/apps/codecs/libgme/ay_emu.h
+++ b/apps/codecs/libgme/ay_emu.h
@@ -10,14 +10,12 @@
#include "z80_cpu.h"
#include "ay_apu.h"
#include "m3u_playlist.h"
-
-typedef short sample_t;
+#include "track_filter.h"
// 64K memory to load code and data into before starting track. Caller
// must parse the AY file.
enum { mem_size = 0x10000 };
enum { ram_addr = 0x4000 }; // where official RAM starts
-enum { buf_size = 2048 };
// AY file header
enum { header_size = 0x14 };
@@ -62,43 +60,30 @@ struct Ay_Emu {
bool cpc_mode;
// general
- int max_initial_silence;
int voice_count;
+ int const* voice_types;
int mute_mask_;
int tempo;
int gain;
- long sample_rate;
+ int sample_rate;
// track-specific
int current_track;
int track_count;
- 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
- volatile bool track_ended;
- bool emu_track_ended_; // emulator has reached end of track
-
- // fading
- blargg_long fade_start;
- int fade_step;
-
- // silence detection
- bool ignore_silence;
- int silence_lookahead; // speed to run emulator when looking ahead for silence
- long silence_time; // number of samples where most recent silence began
- long silence_count; // number of samples of silence to play before using buf
- long buf_remain; // number of samples left in silence buffer
-
- long clock_rate_;
+
+ int clock_rate_;
unsigned buf_changed_count;
// M3u Playlist
struct M3u_Playlist m3u;
// large items
+ struct setup_t tfilter;
+ struct Track_Filter track_filter;
+
struct Ay_Apu apu;
- sample_t buf [buf_size];
- struct Stereo_Buffer stereo_buf; // NULL if using custom buffer
+ struct Multi_Buffer stereo_buf; // NULL if using custom buffer
struct Z80_Cpu cpu;
struct mem_t mem;
};
@@ -106,46 +91,58 @@ struct Ay_Emu {
// Basic functionality (see Gme_File.h for file loading/track info functions)
void Ay_init( struct Ay_Emu* this );
-blargg_err_t Ay_load_mem( struct Ay_Emu* this, byte const in [], int size );
+blargg_err_t Ay_load_mem( struct Ay_Emu* this, byte const in [], long size );
// Set output sample rate. Must be called only once before loading file.
-blargg_err_t Ay_set_sample_rate( struct Ay_Emu* this, long sample_rate );
+blargg_err_t Ay_set_sample_rate( struct Ay_Emu* this, int sample_rate );
// Start a track, where 0 is the first track. Also clears warning string.
blargg_err_t Ay_start_track( struct Ay_Emu* this, int track );
// Generate 'count' samples info 'buf'. Output is in stereo. Any emulation
// errors set warning string, and major errors also end track.
-blargg_err_t Ay_play( struct Ay_Emu* this, long count, sample_t* buf );
+blargg_err_t Ay_play( struct Ay_Emu* this, int count, sample_t* buf );
// Track status/control
// Number of milliseconds (1000 msec = 1 second) played since beginning of track
-long Track_tell( struct Ay_Emu* this );
+int Track_tell( struct Ay_Emu* this );
// Seek to new time in track. Seeking backwards or far forward can take a while.
-blargg_err_t Track_seek( struct Ay_Emu* this, long msec );
-
+blargg_err_t Track_seek( struct Ay_Emu* this, int msec );
+
// Skip n samples
-blargg_err_t Track_skip( struct Ay_Emu* this, long n );
-
+blargg_err_t Track_skip( struct Ay_Emu* this, int n );
+
// Set start time and length of track fade out. Once fade ends track_ended() returns
// true. Fade time can be changed while track is playing.
-void Track_set_fade( struct Ay_Emu* this, long start_msec, long length_msec );
+void Track_set_fade( struct Ay_Emu* this, int start_msec, int length_msec );
+
+// True if a track has reached its end
+static inline bool Track_ended( struct Ay_Emu* this )
+{
+ return track_ended( &this->track_filter );
+}
+
+// Disables automatic end-of-track detection and skipping of silence at beginning
+static inline void Track_ignore_silence( struct Ay_Emu* this, bool disable )
+{
+ this->track_filter.silence_ignored_ = disable;
+}
// Get track length in milliseconds
-long Track_get_length( struct Ay_Emu* this, int n );
+int Track_get_length( struct Ay_Emu* this, int n );
// Sound customization
// Adjust song tempo, where 1.0 = normal, 0.5 = half speed, 2.0 = double speed.
// Track length as returned by track_info() assumes a tempo of 1.0.
void Sound_set_tempo( struct Ay_Emu* this, int t );
-
+
// Mute/unmute voice i, where voice 0 is first voice
void Sound_mute_voice( struct Ay_Emu* this, int index, bool mute );
-
+
// Set muting state of all voices at once using a bit mask, where -1 mutes them all,
// 0 unmutes them all, 0x01 mutes just the first voice, etc.
void Sound_mute_voices( struct Ay_Emu* this, int mask );
@@ -168,5 +165,5 @@ static inline void disable_beeper( struct Ay_Emu *this )
this->beeper_mask = 0;
this->last_beeper = 0;
}
-
+
#endif
diff --git a/apps/codecs/libgme/blargg_common.h b/apps/codecs/libgme/blargg_common.h
index 65ae76a..a023d6b 100644
--- a/apps/codecs/libgme/blargg_common.h
+++ b/apps/codecs/libgme/blargg_common.h
@@ -7,12 +7,12 @@
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
-#include <assert.h>
#include <limits.h>
#undef BLARGG_COMMON_H
// allow blargg_config.h to #include blargg_common.h
#include "blargg_config.h"
+#include "blargg_source.h"
#ifndef BLARGG_COMMON_H
#define BLARGG_COMMON_H
@@ -98,19 +98,13 @@
static bool false = 0;
#endif
-// blargg_long/blargg_ulong = at least 32 bits, int if it's big enough
-#include <limits.h>
-
-#if INT_MAX >= 0x7FFFFFFF
- typedef int blargg_long;
-#else
- typedef long blargg_long;
-#endif
-
-#if UINT_MAX >= 0xFFFFFFFF
- typedef unsigned blargg_ulong;
-#else
- typedef unsigned long blargg_ulong;
+/* My code depends on int being at least 32 bits. Almost everything these days
+uses at least 32-bit ints, so it's hard to even find a system with 16-bit ints
+to test with. The issue can't be gotten around by using a suitable blargg_int
+everywhere either, because int is often converted to implicitly when doing
+arithmetic on smaller types. */
+#if UINT_MAX < 0xFFFFFFFF
+ #error "int must be at least 32 bits"
#endif
// int8_t etc.
diff --git a/apps/codecs/libgme/blargg_endian.h b/apps/codecs/libgme/blargg_endian.h
index ae55d7f..dce5cb2 100644
--- a/apps/codecs/libgme/blargg_endian.h
+++ b/apps/codecs/libgme/blargg_endian.h
@@ -64,45 +64,60 @@ static inline void blargg_verify_byte_order( void )
#endif
}
-static inline unsigned get_le16( void const* p ) {
- return ((unsigned char const*) p) [1] * 0x100u +
- ((unsigned char const*) p) [0];
+static inline unsigned get_le16( void const* p )
+{
+ return (unsigned) ((unsigned char const*) p) [1] << 8 |
+ (unsigned) ((unsigned char const*) p) [0];
}
-static inline unsigned get_be16( void const* p ) {
- return ((unsigned char const*) p) [0] * 0x100u +
- ((unsigned char const*) p) [1];
+
+static inline unsigned get_be16( void const* p )
+{
+ return (unsigned) ((unsigned char const*) p) [0] << 8 |
+ (unsigned) ((unsigned char const*) p) [1];
}
-static inline blargg_ulong get_le32( void const* p ) {
- return ((unsigned char const*) p) [3] * 0x01000000u +
- ((unsigned char const*) p) [2] * 0x00010000u +
- ((unsigned char const*) p) [1] * 0x00000100u +
- ((unsigned char const*) p) [0];
+
+static inline unsigned get_le32( void const* p )
+{
+ return (unsigned) ((unsigned char const*) p) [3] << 24 |
+ (unsigned) ((unsigned char const*) p) [2] << 16 |
+ (unsigned) ((unsigned char const*) p) [1] << 8 |
+ (unsigned) ((unsigned char const*) p) [0];
}
-static inline blargg_ulong get_be32( void const* p ) {
- return ((unsigned char const*) p) [0] * 0x01000000u +
- ((unsigned char const*) p) [1] * 0x00010000u +
- ((unsigned char const*) p) [2] * 0x00000100u +
- ((unsigned char const*) p) [3];
+
+static inline unsigned get_be32( void const* p )
+{
+ return (unsigned) ((unsigned char const*) p) [0] << 24 |
+ (unsigned) ((unsigned char const*) p) [1] << 16 |
+ (unsigned) ((unsigned char const*) p) [2] << 8 |
+ (unsigned) ((unsigned char const*) p) [3];
}
-static inline void set_le16( void* p, unsigned n ) {
+
+static inline void set_le16( void* p, unsigned n )
+{
((unsigned char*) p) [1] = (unsigned char) (n >> 8);
((unsigned char*) p) [0] = (unsigned char) n;
}
-static inline void set_be16( void* p, unsigned n ) {
+
+static inline void set_be16( void* p, unsigned n )
+{
((unsigned char*) p) [0] = (unsigned char) (n >> 8);
((unsigned char*) p) [1] = (unsigned char) n;
}
-static inline void set_le32( void* p, blargg_ulong n ) {
- ((unsigned char*) p) [3] = (unsigned char) (n >> 24);
- ((unsigned char*) p) [2] = (unsigned char) (n >> 16);
- ((unsigned char*) p) [1] = (unsigned char) (n >> 8);
+
+static inline void set_le32( void* p, unsigned n )
+{
((unsigned char*) p) [0] = (unsigned char) n;
+ ((unsigned char*) p) [1] = (unsigned char) (n >> 8);
+ ((unsigned char*) p) [2] = (unsigned char) (n >> 16);
+ ((unsigned char*) p) [3] = (unsigned char) (n >> 24);
}
-static inline void set_be32( void* p, blargg_ulong n ) {
- ((unsigned char*) p) [0] = (unsigned char) (n >> 24);
- ((unsigned char*) p) [1] = (unsigned char) (n >> 16);
- ((unsigned char*) p) [2] = (unsigned char) (n >> 8);
+
+static inline void set_be32( void* p, unsigned n )
+{
((unsigned char*) p) [3] = (unsigned char) n;
+ ((unsigned char*) p) [2] = (unsigned char) (n >> 8);
+ ((unsigned char*) p) [1] = (unsigned char) (n >> 16);
+ ((unsigned char*) p) [0] = (unsigned char) (n >> 24);
}
#if defined(BLARGG_NONPORTABLE)
@@ -132,15 +147,21 @@ static inline void set_be32( void* p, blargg_ulong n ) {
#ifndef GET_LE16
#define GET_LE16( addr ) get_le16( addr )
- #define GET_LE32( addr ) get_le32( addr )
#define SET_LE16( addr, data ) set_le16( addr, data )
+#endif
+
+#ifndef GET_LE32
+ #define GET_LE32( addr ) get_le32( addr )
#define SET_LE32( addr, data ) set_le32( addr, data )
#endif
#ifndef GET_BE16
#define GET_BE16( addr ) get_be16( addr )
- #define GET_BE32( addr ) get_be32( addr )
#define SET_BE16( addr, data ) set_be16( addr, data )
+#endif
+
+#ifndef GET_BE32
+ #define GET_BE32( addr ) get_be32( addr )
#define SET_BE32( addr, data ) set_be32( addr, data )
#endif
diff --git a/apps/codecs/libgme/blargg_source.h b/apps/codecs/libgme/blargg_source.h
index 4bea02a..ab8e1b0 100644
--- a/apps/codecs/libgme/blargg_source.h
+++ b/apps/codecs/libgme/blargg_source.h
@@ -2,16 +2,13 @@
#ifndef BLARGG_SOURCE_H
#define BLARGG_SOURCE_H
-// If debugging is enabled, abort program if expr is false. Meant for checking
-// internal state and consistency. A failed assertion indicates a bug in the module.
-// void assert( bool expr );
-#include <assert.h>
-
// If debugging is enabled and expr is false, abort program. Meant for checking
// caller-supplied parameters and operations that are outside the control of the
// module. A failed requirement indicates a bug outside the module.
// void require( bool expr );
#if defined(ROCKBOX)
+#undef assert
+#define assert( expr )
#undef require
#define require( expr )
#else
@@ -36,28 +33,36 @@ static inline void blargg_dprintf_( const char* fmt, ... ) { }
#undef check
#define check( expr ) ((void) 0)
-// If expr yields error string, return it from current function, otherwise continue.
-#undef RETURN_ERR
-#define RETURN_ERR( expr ) do { \
- blargg_err_t blargg_return_err_ = (expr); \
- if ( blargg_return_err_ ) return blargg_return_err_; \
+/* If expr yields non-NULL error string, returns it from current function,
+otherwise continues normally. */
+#undef RETURN_ERR
+#define RETURN_ERR( expr ) \
+ do {\
+ blargg_err_t blargg_return_err_ = (expr);\
+ if ( blargg_return_err_ )\
+ return blargg_return_err_;\
} while ( 0 )
-// If ptr is 0, return out of memory error string.
-#undef CHECK_ALLOC
-#define CHECK_ALLOC( ptr ) do { if ( (ptr) == 0 ) return "Out of memory"; } while ( 0 )
+/* If ptr is NULL, returns out-of-memory error, otherwise continues normally. */
+#undef CHECK_ALLOC
+#define CHECK_ALLOC( ptr ) \
+ do {\
+ if ( !(ptr) )\
+ return "Out of memory";\
+ } while ( 0 )
#ifndef max
#define max(a,b) (((a) > (b)) ? (a) : (b))
#endif
+
#ifndef min
#define min(a,b) (((a) < (b)) ? (a) : (b))
#endif
-// TODO: good idea? bad idea?
-#undef byte
-#define byte byte_
-typedef unsigned char byte;
+// typedef unsigned char byte;
+typedef unsigned char blargg_byte;
+#undef byte
+#define byte blargg_byte
// deprecated
#define BLARGG_CHECK_ALLOC CHECK_ALLOC
diff --git a/apps/codecs/libgme/blip_buffer.c b/apps/codecs/libgme/blip_buffer.c
index d8ecbb8..ba0a655 100644
--- a/apps/codecs/libgme/blip_buffer.c
+++ b/apps/codecs/libgme/blip_buffer.c
@@ -2,7 +2,6 @@
#include "blip_buffer.h"
-#include <assert.h>
#include <limits.h>
#include <string.h>
#include <stdlib.h>
@@ -19,96 +18,78 @@ details. You should have received a copy of the GNU Lesser General Public
License along with this module; if not, write to the Free Software Foundation,
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
-#ifdef BLARGG_ENABLE_OPTIMIZER
- #include BLARGG_ENABLE_OPTIMIZER
-#endif
-
-int const silent_buf_size = 1; // size used for Silent_Blip_Buffer
+#include "blargg_source.h"
void Blip_init( struct Blip_Buffer* this )
{
- this->factor_ = (blip_ulong)LONG_MAX;
- this->offset_ = 0;
- this->buffer_size_ = 0;
- this->sample_rate_ = 0;
- this->reader_accum_ = 0;
- this->bass_shift_ = 0;
- this->clock_rate_ = 0;
- this->bass_freq_ = 16;
- this->length_ = 0;
-
+ this->factor_ = UINT_MAX/2 + 1;;
+ this->buffer_center_ = NULL;
+ this->buffer_size_ = 0;
+ this->sample_rate_ = 0;
+ this->bass_shift_ = 0;
+ this->clock_rate_ = 0;
+ this->bass_freq_ = 16;
+ this->length_ = 0;
+
// assumptions code makes about implementation-defined features
#ifndef NDEBUG
// right shift of negative value preserves sign
buf_t_ i = -0x7FFFFFFE;
assert( (i >> 1) == -0x3FFFFFFF );
-
+
// casting to short truncates to 16 bits and sign-extends
i = 0x18000;
assert( (short) i == -0x8000 );
#endif
-}
-void Blip_stop( struct Blip_Buffer* this )
-{
- if ( this->buffer_size_ != silent_buf_size )
- free( this->buffer_ );
+ Blip_clear( this );
}
-void Blip_clear( struct Blip_Buffer* this, int entire_buffer )
+void Blip_clear( struct Blip_Buffer* this )
{
- this->offset_ = 0;
+ bool const entire_buffer = true;
+
+ this->offset_ = 0;
this->reader_accum_ = 0;
- this->modified_ = 0;
+ this->modified = false;
+
if ( this->buffer_ )
{
- long count = (entire_buffer ? this->buffer_size_ : Blip_samples_avail( this ));
- memset( this->buffer_, 0, (count + blip_buffer_extra_) * sizeof (buf_t_) );
+ int count = (entire_buffer ? this->buffer_size_ : Blip_samples_avail( this ));
+ memset( this->buffer_, 0, (count + blip_buffer_extra_) * sizeof (delta_t) );
}
}
-blargg_err_t Blip_set_sample_rate( struct Blip_Buffer* this, long new_rate, int msec )
+blargg_err_t Blip_set_sample_rate( struct Blip_Buffer* this, int new_rate, int msec )
{
- if ( this->buffer_size_ == silent_buf_size )
- {
- assert( 0 );
- return "Internal (tried to resize Silent_Blip_Buffer)";
- }
-
- // start with maximum length that resampled time can represent
- long new_size = (ULONG_MAX >> BLIP_BUFFER_ACCURACY) - blip_buffer_extra_ - 64;
- if ( msec != blip_max_length )
- {
- long s = (new_rate * (msec + 1) + 999) / 1000;
- if ( s < new_size )
- new_size = s;
- else
- assert( 0 ); // fails if requested buffer length exceeds limit
- }
-
- if ( new_size > blip_buffer_max )
- return "Out of memory";
-
- this->buffer_size_ = new_size;
- assert( this->buffer_size_ != silent_buf_size );
-
+ // Limit to maximum size that resampled time can represent
+ int max_size = (((blip_resampled_time_t) -1) >> BLIP_BUFFER_ACCURACY) -
+ blip_buffer_extra_ - 64; // TODO: -64 isn't needed
+ int new_size = (new_rate * (msec + 1) + 999) / 1000;
+ if ( new_size > max_size )
+ new_size = max_size;
+
+ // Resize buffer
+ if ( this->buffer_size_ != new_size ) {
+ this->buffer_center_ = this->buffer_ + BLIP_MAX_QUALITY/2;
+ this->buffer_size_ = new_size;
+ }
+
// update things based on the sample rate
this->sample_rate_ = new_rate;
- this->length_ = new_size * 1000 / new_rate - 1;
- if ( msec )
- assert( this->length_ == msec ); // ensure length is same as that passed in
+ this->length_ = new_size * 1000 / new_rate - 1;
if ( this->clock_rate_ )
Blip_set_clock_rate( this, this->clock_rate_ );
Blip_bass_freq( this, this->bass_freq_ );
-
- Blip_clear( this, 1 );
-
+
+ Blip_clear( this );
+
return 0; // success
}
-blip_resampled_time_t Blip_clock_rate_factor( struct Blip_Buffer* this, long rate )
+blip_resampled_time_t Blip_clock_rate_factor( struct Blip_Buffer* this, int rate )
{
- blip_long factor = (blip_long) ( this->sample_rate_ * (1LL << BLIP_BUFFER_ACCURACY) / rate);
+ int factor = (int) ( this->sample_rate_ * (1LL << BLIP_BUFFER_ACCURACY) / rate);
assert( factor > 0 || !this->sample_rate_ ); // fails if clock/output ratio is too large
return (blip_resampled_time_t) factor;
}
@@ -117,119 +98,111 @@ void Blip_bass_freq( struct Blip_Buffer* this, int freq )
{
this->bass_freq_ = freq;
int shift = 31;
- if ( freq > 0 )
+ if ( freq > 0 && this->sample_rate_ )
{
shift = 13;
- long f = (freq << 16) / this->sample_rate_;
+ int f = (freq << 16) / this->sample_rate_;
while ( (f >>= 1) && --shift ) { }
}
- this->bass_shift_ = shift;
+ this->bass_shift_ = shift;
}
void Blip_end_frame( struct Blip_Buffer* this, blip_time_t t )
{
this->offset_ += t * this->factor_;
- assert( Blip_samples_avail( this ) <= (long) this->buffer_size_ ); // time outside buffer length
+ assert( Blip_samples_avail( this ) <= (int) this->buffer_size_ ); // time outside buffer length
}
-void Blip_remove_silence( struct Blip_Buffer* this, long count )
+int Blip_count_samples( struct Blip_Buffer* this, blip_time_t t )
{
- assert( count <= Blip_samples_avail( this ) ); // tried to remove more samples than available
- this->offset_ -= (blip_resampled_time_t) count << BLIP_BUFFER_ACCURACY;
+ blip_resampled_time_t last_sample = Blip_resampled_time( this, t ) >> BLIP_BUFFER_ACCURACY;
+ blip_resampled_time_t first_sample = this->offset_ >> BLIP_BUFFER_ACCURACY;
+ return (int) (last_sample - first_sample);
}
-long Blip_count_samples( struct Blip_Buffer* this, blip_time_t t )
+blip_time_t Blip_count_clocks( struct Blip_Buffer* this, int count )
{
- unsigned long last_sample = Blip_resampled_time( this, t ) >> BLIP_BUFFER_ACCURACY;
- unsigned long first_sample = this->offset_ >> BLIP_BUFFER_ACCURACY;
- return (long) (last_sample - first_sample);
-}
-
-blip_time_t Blip_count_clocks( struct Blip_Buffer* this, long count )
-{
- if ( !this->factor_ )
- {
- assert( 0 ); // sample rate and clock rates must be set first
- return 0;
- }
-
if ( count > this->buffer_size_ )
count = this->buffer_size_;
blip_resampled_time_t time = (blip_resampled_time_t) count << BLIP_BUFFER_ACCURACY;
return (blip_time_t) ((time - this->offset_ + this->factor_ - 1) / this->factor_);
}
-void Blip_remove_samples( struct Blip_Buffer* this, long count )
+void Blip_remove_samples( struct Blip_Buffer* this, int count )
{
if ( count )
{
Blip_remove_silence( this, count );
-
+
// copy remaining samples to beginning and clear old samples
- long remain = Blip_samples_avail( this ) + blip_buffer_extra_;
+ int remain = Blip_samples_avail( this ) + blip_buffer_extra_;
memmove( this->buffer_, this->buffer_ + count, remain * sizeof *this->buffer_ );
memset( this->buffer_ + remain, 0, count * sizeof *this->buffer_ );
}
}
-long Blip_read_samples( struct Blip_Buffer* this, blip_sample_t* BLIP_RESTRICT out, long max_samples, int stereo )
+int Blip_read_samples( struct Blip_Buffer* this, blip_sample_t out_ [], int max_samples, bool stereo )
{
- long count = Blip_samples_avail( this );
+ int count = Blip_samples_avail( this );
if ( count > max_samples )
count = max_samples;
-
+
if ( count )
{
- int const bass = BLIP_READER_BASS( *this );
- BLIP_READER_BEGIN( reader, *this );
-
+ int const bass = this->bass_shift_;
+ delta_t const* reader = this->buffer_ + count;
+ int reader_sum = this->reader_accum_;
+
+ blip_sample_t* BLARGG_RESTRICT out = out_ + count;
+ if ( stereo )
+ out += count;
+ int offset = -count;
+
if ( !stereo )
{
- blip_long n;
- for ( n = count; n; --n )
+ do
{
- blip_long s = BLIP_READER_READ( reader );
- if ( (blip_sample_t) s != s )
- s = 0x7FFF - (s >> 24);
- *out++ = (blip_sample_t) s;
- BLIP_READER_NEXT( reader, bass );
+ int s = reader_sum >> delta_bits;
+
+ reader_sum -= reader_sum >> bass;
+ reader_sum += reader [offset];
+
+ BLIP_CLAMP( s, s );
+ out [offset] = (blip_sample_t) s;
}
+ while ( ++offset );
}
else
{
- blip_long n;
- for ( n = count; n; --n )
+ do
{
- blip_long s = BLIP_READER_READ( reader );
- if ( (blip_sample_t) s != s )
- s = 0x7FFF - (s >> 24);
- *out = (blip_sample_t) s;
- out += 2;
- BLIP_READER_NEXT( reader, bass );
+ int s = reader_sum >> delta_bits;
+
+ reader_sum -= reader_sum >> bass;
+ reader_sum += reader [offset];
+
+ BLIP_CLAMP( s, s );
+ out [offset * 2] = (blip_sample_t) s;
}
+ while ( ++offset );
}
- BLIP_READER_END( reader, *this );
-
+
+ this->reader_accum_ = reader_sum;
+
Blip_remove_samples( this, count );
}
return count;
}
-void Blip_mix_samples( struct Blip_Buffer* this, blip_sample_t const* in, long count )
+void Blip_mix_samples( struct Blip_Buffer* this, blip_sample_t const in [], int count )
{
- if ( this->buffer_size_ == silent_buf_size )
- {
- assert( 0 );
- return;
- }
-
- buf_t_* out = this->buffer_ + (this->offset_ >> BLIP_BUFFER_ACCURACY) + blip_widest_impulse_ / 2;
-
+ delta_t* out = this->buffer_center_ + (this->offset_ >> BLIP_BUFFER_ACCURACY);
+
int const sample_shift = blip_sample_bits - 16;
int prev = 0;
- while ( count-- )
+ while ( --count >= 0 )
{
- blip_long s = (blip_long) *in++ << sample_shift;
+ int s = *in++ << sample_shift;
*out += s - prev;
prev = s;
++out;
@@ -237,40 +210,16 @@ void Blip_mix_samples( struct Blip_Buffer* this, blip_sample_t const* in, long
*out -= prev;
}
-void Blip_set_modified( struct Blip_Buffer* this )
-{
- this->modified_ = 1;
-}
-
-int Blip_clear_modified( struct Blip_Buffer* this )
-{
- int b = this->modified_;
- this->modified_ = 0;
- return b;
-}
-
-blip_resampled_time_t Blip_resampled_duration( struct Blip_Buffer* this, int t )
-{
- return t * this->factor_;
-}
+// Blip_Synth
-blip_resampled_time_t Blip_resampled_time( struct Blip_Buffer* this, blip_time_t t )
+void volume_unit( struct Blip_Synth* this, int new_unit )
{
- return t * this->factor_ + this->offset_;
+ this->delta_factor = (int) (new_unit * (1LL << blip_sample_bits) / FP_ONE_VOLUME);
}
-
-// Blip_Synth
-
void Synth_init( struct Blip_Synth* this )
{
this->buf = 0;
this->last_amp = 0;
this->delta_factor = 0;
}
-
-// Set overall volume of waveform
-void Synth_volume( struct Blip_Synth* this, int v )
-{
- this->delta_factor = (int) (v * (1LL << blip_sample_bits) / FP_ONE_VOLUME);
-}
diff --git a/apps/codecs/libgme/blip_buffer.h b/apps/codecs/libgme/blip_buffer.h
index f9f1f6e..d73a14b 100644
--- a/apps/codecs/libgme/blip_buffer.h
+++ b/apps/codecs/libgme/blip_buffer.h
@@ -4,202 +4,238 @@
#ifndef BLIP_BUFFER_H
#define BLIP_BUFFER_H
-#include <assert.h>
+#include "blargg_common.h"
- // internal
- #include "blargg_common.h"
- #if INT_MAX >= 0x7FFFFFFF
- typedef int blip_long;
- typedef unsigned blip_ulong;
- #else
- typedef long blip_long;
- typedef unsigned long blip_ulong;
- #endif
+typedef unsigned blip_resampled_time_t;
+typedef int blip_time_t;
+typedef int clocks_t;
+
+// Output samples are 16-bit signed, with a range of -32768 to 32767
+typedef short blip_sample_t;
-// Time unit at source clock rate
-typedef blip_long blip_time_t;
+static int const blip_default_length = 1000 / 4; // Default Blip_Buffer length (1/4 second)
+
+#ifndef BLIP_MAX_QUALITY
+ #define BLIP_MAX_QUALITY 2
+#endif
-// Number of bits in resample ratio fraction. Higher values give a more accurate ratio
-// but reduce maximum buffer size.
#ifndef BLIP_BUFFER_ACCURACY
#define BLIP_BUFFER_ACCURACY 16
#endif
-// Number bits in phase offset. Fewer than 6 bits (64 phase offsets) results in
-// noticeable broadband noise when synthesizing high frequency square waves.
-// Affects size of Blip_Synth objects since they store the waveform directly.
+// linear interpolation needs 8 bits
#ifndef BLIP_PHASE_BITS
#define BLIP_PHASE_BITS 8
#endif
-// Output samples are 16-bit signed, with a range of -32768 to 32767
-typedef short blip_sample_t;
-enum { blip_sample_max = 32767 };
-enum { blip_widest_impulse_ = 16 };
-enum { blip_buffer_extra_ = blip_widest_impulse_ + 2 };
-enum { blip_res = 1 << BLIP_PHASE_BITS };
-enum { blip_max_length = 0 };
-enum { blip_default_length = 250 };
+static int const blip_res = 1 << BLIP_PHASE_BITS;
+static int const blip_buffer_extra_ = BLIP_MAX_QUALITY + 2;
+
+// Properties of fixed-point sample position
+typedef unsigned ufixed_t; // unsigned for more range, optimized shifts
+enum { fixed_bits = BLIP_BUFFER_ACCURACY }; // bits in fraction
+enum { fixed_unit = 1 << fixed_bits }; // 1.0 samples
+
+// Deltas in buffer are fixed-point with this many fraction bits.
+// Less than 16 for extra range.
+enum { delta_bits = 14 };
+
+// Pointer to first committed delta sample
+typedef int delta_t;
// Maximun buffer size (48Khz, 50 ms)
enum { blip_buffer_max = 2466 };
-enum { blip_sample_bits = 30 };
-
-typedef blip_time_t buf_t_;
-/* typedef const char* blargg_err_t; */
-typedef blip_ulong blip_resampled_time_t;
struct Blip_Buffer {
- blip_ulong factor_;
- blip_resampled_time_t offset_;
- buf_t_ buffer_ [blip_buffer_max];
- blip_long buffer_size_;
- blip_long reader_accum_;
- int bass_shift_;
-
- long sample_rate_;
- long clock_rate_;
- int bass_freq_;
- int length_;
- int modified_;
+ unsigned factor_;
+ ufixed_t offset_;
+ delta_t* buffer_center_;
+ int buffer_size_;
+ int reader_accum_;
+ int bass_shift_;
+ int bass_freq_;
+ int sample_rate_;
+ int clock_rate_;
+ int length_;
+ bool modified;
+
+ delta_t buffer_ [blip_buffer_max];
};
-// not documented yet
-void Blip_set_modified( struct Blip_Buffer* this );
-int Blip_clear_modified( struct Blip_Buffer* this );
-void Blip_remove_silence( struct Blip_Buffer* this, long count );
-blip_resampled_time_t Blip_resampled_duration( struct Blip_Buffer* this, int t );
-blip_resampled_time_t Blip_resampled_time( struct Blip_Buffer* this, blip_time_t t );
-blip_resampled_time_t Blip_clock_rate_factor( struct Blip_Buffer* this, long clock_rate );
-
-// Initializes Blip_Buffer structure
-void Blip_init( struct Blip_Buffer* this );
-
-// Stops (clear) Blip_Buffer structure
-void Blip_stop( struct Blip_Buffer* this );
+// Blip_Buffer_ implementation
+static inline ufixed_t to_fixed( struct Blip_Buffer *this, clocks_t t )
+{
+ return t * this->factor_ + this->offset_;
+}
-// Set output sample rate and buffer length in milliseconds (1/1000 sec, defaults
-// to 1/4 second), then clear buffer. Returns NULL on success, otherwise if there
-// isn't enough memory, returns error without affecting current buffer setup.
-blargg_err_t Blip_set_sample_rate( struct Blip_Buffer* this, long samples_per_sec, int msec_length );
+static inline delta_t* delta_at( struct Blip_Buffer *this, ufixed_t f )
+{
+ assert( (f >> fixed_bits) < (unsigned) this->buffer_size_ );
+ return this->buffer_center_ + (f >> fixed_bits);
+}
-// Set number of source time units per second
-static inline void Blip_set_clock_rate( struct Blip_Buffer* this, long cps )
+// Number of samples available for reading with read_samples()
+static inline int Blip_samples_avail( struct Blip_Buffer* this )
{
- this->factor_ = Blip_clock_rate_factor( this, this->clock_rate_ = cps );
+ return (int) (this->offset_ >> BLIP_BUFFER_ACCURACY);
}
-// End current time frame of specified duration and make its samples available
-// (along with any still-unread samples) for reading with read_samples(). Begins
-// a new time frame at the end of the current frame.
-void Blip_end_frame( struct Blip_Buffer* this, blip_time_t time );
+static inline void Blip_remove_silence( struct Blip_Buffer* this, int count )
+{
+ assert( count <= Blip_samples_avail( this ) ); // tried to remove more samples than available
+ this->offset_ -= (blip_resampled_time_t) count << BLIP_BUFFER_ACCURACY;
+}
-// Read at most 'max_samples' out of buffer into 'dest', removing them from from
-// the buffer. Returns number of samples actually read and removed. If stereo is
-// true, increments 'dest' one extra time after writing each sample, to allow
-// easy interleving of two channels into a stereo output buffer.
-long Blip_read_samples( struct Blip_Buffer* this, blip_sample_t* dest, long max_samples, int stereo );
+// Initializes Blip_Buffer structure
+void Blip_init( struct Blip_Buffer* this );
-// Additional optional features
+// Sets output sample rate and resizes and clears sample buffer
+blargg_err_t Blip_set_sample_rate( struct Blip_Buffer* this, int samples_per_sec, int msec_length );
// Current output sample rate
-static inline long Blip_sample_rate( struct Blip_Buffer* this )
+static inline int Blip_sample_rate( struct Blip_Buffer* this )
{
return this->sample_rate_;
}
-// Length of buffer, in milliseconds
-static inline int Blip_length( struct Blip_Buffer* this )
+// Sets number of source time units per second
+blip_resampled_time_t Blip_clock_rate_factor( struct Blip_Buffer* this, int clock_rate );
+static inline void Blip_set_clock_rate( struct Blip_Buffer* this, int clocks_per_sec )
{
- return this->length_;
+ this->factor_ = Blip_clock_rate_factor( this, this->clock_rate_ = clocks_per_sec );
}
// Number of source time units per second
-static inline long Blip_clock_rate( struct Blip_Buffer* this )
+static inline int Blip_clock_rate( struct Blip_Buffer* this )
{
return this->clock_rate_;
}
+static inline int Blip_length( struct Blip_Buffer* this )
+{
+ return this->length_;
+}
+
+// Clears buffer and removes all samples
+void Blip_clear( struct Blip_Buffer* this );
+
+// Use Blip_Synth to add waveform to buffer
+
+// Resamples to time t, then subtracts t from current time. Appends result of resampling
+// to buffer for reading.
+void Blip_end_frame( struct Blip_Buffer* this, blip_time_t time ) ICODE_ATTR;
+
+
+// Reads at most n samples to out [0 to n-1] and returns number actually read. If stereo
+// is true, writes to out [0], out [2], out [4] etc. instead.
+int Blip_read_samples( struct Blip_Buffer* this, blip_sample_t out [], int n, bool stereo ) ICODE_ATTR;
+
+
+// More features
+
+// Sets flag that tells some Multi_Buffer types that sound was added to buffer,
+// so they know that it needs to be mixed in. Only needs to be called once
+// per time frame that sound was added. Not needed if not using Multi_Buffer.
+static inline void Blip_set_modified( struct Blip_Buffer* this ) { this->modified = true; }
// Set frequency high-pass filter frequency, where higher values reduce bass more
void Blip_bass_freq( struct Blip_Buffer* this, int frequency );
-// Number of samples delay from synthesis to samples read out
-static inline int Blip_output_latency( void )
-{
- return blip_widest_impulse_ / 2;
-}
-// Remove all available samples and clear buffer to silence. If 'entire_buffer' is
-// false, just clears out any samples waiting rather than the entire buffer.
-void Blip_clear( struct Blip_Buffer* this, int entire_buffer );
+// Low-level features
-// Number of samples available for reading with read_samples()
-static inline long Blip_samples_avail( struct Blip_Buffer* this )
-{
- return (long) (this->offset_ >> BLIP_BUFFER_ACCURACY);
-}
+// Removes the first n samples
+void Blip_remove_samples( struct Blip_Buffer* this, int n ) ICODE_ATTR;
+
+// Returns number of clocks needed until n samples will be available.
+// If buffer cannot even hold n samples, returns number of clocks
+// until buffer becomes full.
+blip_time_t Blip_count_clocks( struct Blip_Buffer* this, int count ) ICODE_ATTR;
+
+// Number of samples that should be mixed before calling Blip_end_frame( t )
+int Blip_count_samples( struct Blip_Buffer* this, blip_time_t t ) ICODE_ATTR;
+
+// Mixes n samples into buffer
+void Blip_mix_samples( struct Blip_Buffer* this, blip_sample_t const in [], int n ) ICODE_ATTR;
-// Remove 'count' samples from those waiting to be read
-void Blip_remove_samples( struct Blip_Buffer* this, long count );
-// Experimental features
+// Resampled time (sorry, poor documentation right now)
-// Count number of clocks needed until 'count' samples will be available.
-// If buffer can't even hold 'count' samples, returns number of clocks until
-// buffer becomes full.
-blip_time_t Blip_count_clocks( struct Blip_Buffer* this, long count );
+// Resampled time is fixed-point, in terms of output samples.
-// Number of raw samples that can be mixed within frame of specified duration.
-long Blip_count_samples( struct Blip_Buffer* this, blip_time_t duration );
+// Converts clock count to resampled time
+static inline blip_resampled_time_t Blip_resampled_duration( struct Blip_Buffer* this, int t )
+{
+ return t * this->factor_;
+}
+
+// Converts clock time since beginning of current time frame to resampled time
+static inline blip_resampled_time_t Blip_resampled_time( struct Blip_Buffer* this, blip_time_t t )
+{
+ return t * this->factor_ + this->offset_;
+}
-// Mix 'count' samples from 'buf' into buffer.
-void Blip_mix_samples( struct Blip_Buffer* this, blip_sample_t const* buf, long count );
// Range specifies the greatest expected change in amplitude. Calculate it
// by finding the difference between the maximum and minimum expected
// amplitudes (max - min).
+typedef char coeff_t;
+
struct Blip_Synth {
- struct Blip_Buffer* buf;
- int last_amp;
int delta_factor;
+ int last_amp;
+ struct Blip_Buffer* buf;
};
+// Blip_Synth_
+void volume_unit( struct Blip_Synth* this, int new_unit );
+
// Initializes Blip_Synth structure
void Synth_init( struct Blip_Synth* this );
-// Set overall volume of waveform
-void Synth_volume( struct Blip_Synth* this, int v );
+// Sets volume of amplitude delta unit
+static inline void Synth_volume( struct Blip_Synth* this, int v )
+{
+ volume_unit( this, v ); // new_unit = 1 / range * v
+}
-// Get/set Blip_Buffer used for output
-const struct Blip_Buffer* Synth_output( struct Blip_Synth* this );
// Low-level interface
- #if defined (__GNUC__) || _MSC_VER >= 1100
- #define BLIP_RESTRICT __restrict
- #else
- #define BLIP_RESTRICT
- #endif
+// (in >> sh & mask) * mul
+#define BLIP_SH_AND_MUL( in, sh, mask, mul ) \
+((int) (in) / ((1U << (sh)) / (mul)) & (unsigned) ((mask) * (mul)))
+
+// (T*) ptr + (off >> sh)
+#define BLIP_PTR_OFF_SH( T, ptr, off, sh ) \
+ ((T*) (BLIP_SH_AND_MUL( off, sh, -1, sizeof (T) ) + (char*) (ptr)))
-// Works directly in terms of fractional output samples. Contact author for more info.
+// Works directly in terms of fractional output samples. Use resampled time functions in Blip_Buffer
+// to convert clock counts to resampled time.
static inline void Synth_offset_resampled( struct Blip_Synth* this, blip_resampled_time_t time,
int delta, struct Blip_Buffer* blip_buf )
{
- // Fails if time is beyond end of Blip_Buffer, due to a bug in caller code or the
- // need for a longer buffer as set by set_sample_rate().
- assert( (blip_long) (time >> BLIP_BUFFER_ACCURACY) < blip_buf->buffer_size_ );
+ int const half_width = 1;
+
+ delta_t* BLARGG_RESTRICT buf = delta_at( blip_buf, time );
delta *= this->delta_factor;
- blip_long* BLIP_RESTRICT buf = blip_buf->buffer_ + (time >> BLIP_BUFFER_ACCURACY);
- int phase = (int) (time >> (BLIP_BUFFER_ACCURACY - BLIP_PHASE_BITS) & (blip_res - 1));
- blip_long left = buf [0] + delta;
+ int const phase_shift = BLIP_BUFFER_ACCURACY - BLIP_PHASE_BITS;
+ int const phase = (half_width & (half_width - 1)) ?
+ (int) BLIP_SH_AND_MUL( time, phase_shift, blip_res - 1, sizeof (coeff_t) ) * half_width :
+ (int) BLIP_SH_AND_MUL( time, phase_shift, blip_res - 1, sizeof (coeff_t) * half_width );
+
+ int left = buf [0] + delta;
// Kind of crappy, but doing shift after multiply results in overflow.
// Alternate way of delaying multiply by delta_factor results in worse
// sub-sample resolution.
- blip_long right = (delta >> BLIP_PHASE_BITS) * phase;
+ int right = (delta >> BLIP_PHASE_BITS) * phase;
+ #ifdef BLIP_BUFFER_NOINTERP
+ // TODO: remove? (just a hack to see how it sounds)
+ right = 0;
+ #endif
left -= right;
right += buf [1];
@@ -213,33 +249,38 @@ static inline void Synth_update( struct Blip_Synth* this, blip_time_t t, int amp
{
int delta = amp - this->last_amp;
this->last_amp = amp;
- Synth_offset_resampled( this, t * this->buf->factor_ + this->buf->offset_, delta, this->buf );
+ Synth_offset_resampled( this, to_fixed(this->buf, t), delta, this->buf );
}
-// Add an amplitude transition of specified delta, optionally into specified buffer
-// rather than the one set with output(). Delta can be positive or negative.
-// The actual change in amplitude is delta * (volume / range)
-static inline void Synth_offset( struct Blip_Synth* this, blip_time_t t, int delta, struct Blip_Buffer* buf )
-{
- Synth_offset_resampled( this, t * buf->factor_ + buf->offset_, delta, buf );
-}
-
-// Same as offset(), except code is inlined for higher performance
+// Adds amplitude transition at time t. Delta can be positive or negative.
+// The actual change in amplitude is delta * volume.
static inline void Synth_offset_inline( struct Blip_Synth* this, blip_time_t t, int delta, struct Blip_Buffer* buf )
{
- Synth_offset_resampled( this, t * buf->factor_ + buf->offset_, delta, buf );
+ Synth_offset_resampled( this, to_fixed(buf, t), delta, buf );
}
+#define Synth_offset( synth, time, delta, buf ) Synth_offset_inline( synth, time, delta, buf )
+
+// Number of bits in raw sample that covers normal output range. Less than 32 bits to give
+// extra amplitude range. That is,
+// +1 << (blip_sample_bits-1) = +1.0
+// -1 << (blip_sample_bits-1) = -1.0
+static int const blip_sample_bits = 30;
+
// Optimized reading from Blip_Buffer, for use in custom sample output
// Begin reading from buffer. Name should be unique to the current block.
#define BLIP_READER_BEGIN( name, blip_buffer ) \
- buf_t_* BLIP_RESTRICT name##_reader_buf = (blip_buffer).buffer_;\
- blip_long name##_reader_accum = (blip_buffer).reader_accum_
+ const delta_t* BLARGG_RESTRICT name##_reader_buf = (blip_buffer).buffer_;\
+ int name##_reader_accum = (blip_buffer).reader_accum_
// Get value to pass to BLIP_READER_NEXT()
#define BLIP_READER_BASS( blip_buffer ) ((blip_buffer).bass_shift_)
+// Constant value to use instead of BLIP_READER_BASS(), for slightly more optimal
+// code at the cost of having no bass_freq() functionality
+static int const blip_reader_default_bass = 9;
+
// Current sample
#define BLIP_READER_READ( name ) (name##_reader_accum >> (blip_sample_bits - 16))
@@ -254,7 +295,7 @@ static inline void Synth_offset_inline( struct Blip_Synth* this, blip_time_t t,
// using Blip_remove_samples().
#define BLIP_READER_END( name, blip_buffer ) \
(void) ((blip_buffer).reader_accum_ = name##_reader_accum)
-
+
#define BLIP_READER_ADJ_( name, offset ) (name##_reader_buf += offset)
#define BLIP_READER_NEXT_IDX_( name, bass, idx ) {\
@@ -262,18 +303,33 @@ static inline void Synth_offset_inline( struct Blip_Synth* this, blip_time_t t,
name##_reader_accum += name##_reader_buf [(idx)];\
}
+#define BLIP_READER_NEXT_RAW_IDX_( name, bass, idx ) {\
+ name##_reader_accum -= name##_reader_accum >> (bass);\
+ name##_reader_accum +=\
+ *(delta_t const*) ((char const*) name##_reader_buf + (idx));\
+}
+
//// BLIP_CLAMP
-#if defined (_M_IX86) || defined (_M_IA64) || defined (__i486__) || \
- defined (__x86_64__) || defined (__ia64__) || defined (__i386__)
- #define BLIP_X86 1
- #define BLIP_CLAMP_( in ) in < -0x8000 || 0x7FFF < in
+#if ARM_ARCH >= 6
+ #define BLIP_CLAMP( sample, out ) \
+ ({ \
+ asm ("ssat %0, #16, %1" \
+ : "=r" ( out ) : "r"( sample ) ); \
+ out; \
+ })
#else
- #define BLIP_CLAMP_( in ) (blip_sample_t) in != in
-#endif
+ #if defined (_M_IX86) || defined (_M_IA64) || defined (__i486__) || \
+ defined (__x86_64__) || defined (__ia64__) || defined (__i386__)
+ #define BLIP_X86 1
+ #define BLIP_CLAMP_( in ) in < -0x8000 || 0x7FFF < in
+ #else
+ #define BLIP_CLAMP_( in ) (blip_sample_t) in != in
+ #endif
-// Clamp sample to blip_sample_t range
-#define BLIP_CLAMP( sample, out )\
- { if ( BLIP_CLAMP_( (sample) ) ) (out) = ((sample) >> 31) ^ 0x7FFF; }
+ // Clamp sample to blip_sample_t range
+ #define BLIP_CLAMP( sample, out )\
+ { if ( BLIP_CLAMP_( (sample) ) ) (out) = ((sample) >> 31) ^ 0x7FFF; }
+#endif
#endif
diff --git a/apps/codecs/libgme/gbs_cpu.c b/apps/codecs/libgme/gbs_cpu.c
index 998888f..1015dd5 100644
--- a/apps/codecs/libgme/gbs_cpu.c
+++ b/apps/codecs/libgme/gbs_cpu.c
@@ -20,7 +20,7 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
#define LOG_MEM( addr, str, data ) data
#endif
-int Read_mem( struct Gbs_Emu* this, addr_t addr )
+int read_mem( struct Gbs_Emu* this, addr_t addr )
{
int result = *Cpu_get_code( &this->cpu, addr );
if ( (unsigned) (addr - io_addr) < io_size )
@@ -29,19 +29,19 @@ int Read_mem( struct Gbs_Emu* this, addr_t addr )
return LOG_MEM( addr, ">", result );
}
-static inline void Write_io_inline( struct Gbs_Emu* this, int offset, int data, int base )
+static inline void write_io_inline( struct Gbs_Emu* this, int offset, int data, int base )
{
if ( (unsigned) (offset - (io_addr - base)) < io_size )
Apu_write_register( &this->apu, Time( this ), offset + base, data & 0xFF );
else if ( (unsigned) (offset - (0xFF06 - base)) < 2 )
- Update_timer( this );
+ update_timer( this );
else if ( offset == io_base - base )
this->ram [base - ram_addr + offset] = 0; // keep joypad return value 0
else
this->ram [base - ram_addr + offset] = 0xFF;
}
-void Write_mem( struct Gbs_Emu* this, addr_t addr, int data )
+void write_mem( struct Gbs_Emu* this, addr_t addr, int data )
{
(void) LOG_MEM( addr, "<", data );
@@ -52,11 +52,11 @@ void Write_mem( struct Gbs_Emu* this, addr_t addr, int data )
offset -= 0xE000 - ram_addr;
if ( (unsigned) offset < 0x1F80 )
- Write_io_inline( this, offset, data, 0xE000 );
+ write_io_inline( this, offset, data, 0xE000 );
}
else if ( (unsigned) (offset - (0x2000 - ram_addr)) < 0x2000 )
{
- Set_bank( this, data & 0xFF );
+ set_bank( this, data & 0xFF );
}
#ifndef NDEBUG
else if ( unsigned (addr - 0x8000) < 0x2000 || unsigned (addr - 0xE000) < 0x1F00 )
@@ -66,21 +66,21 @@ void Write_mem( struct Gbs_Emu* this, addr_t addr, int data )
#endif
}
-static void Write_io_( struct Gbs_Emu* this, int offset, int data )
+static void write_io_( struct Gbs_Emu* this, int offset, int data )
{
- Write_io_inline( this, offset, data, io_base );
+ write_io_inline( this, offset, data, io_base );
}
-static inline void Write_io( struct Gbs_Emu* this, int offset, int data )
+static inline void write_io( struct Gbs_Emu* this, int offset, int data )
{
(void) LOG_MEM( offset + io_base, "<", data );
this->ram [io_base - ram_addr + offset] = data;
if ( (unsigned) offset < 0x80 )
- Write_io_( this, offset, data );
+ write_io_( this, offset, data );
}
-static int Read_io( struct Gbs_Emu* this, int offset )
+static int read_io( struct Gbs_Emu* this, int offset )
{
int const io_base = 0xFF00;
int result = this->ram [io_base - ram_addr + offset];
@@ -106,14 +106,14 @@ static int Read_io( struct Gbs_Emu* this, int offset )
check( out == Read_mem( emu, addr ) );\
}
-#define READ_MEM( emu, addr ) Read_mem( emu, addr )
-#define WRITE_MEM( emu, addr, data ) Write_mem( emu, addr, data )
+#define READ_MEM( emu, addr ) read_mem( emu, addr )
+#define WRITE_MEM( emu, addr, data ) write_mem( emu, addr, data )
-#define WRITE_IO( emu, addr, data ) Write_io( emu, addr, data )
-#define READ_IO( emu, addr, out ) out = Read_io( emu, addr )
+#define WRITE_IO( emu, addr, data ) write_io( emu, addr, data )
+#define READ_IO( emu, addr, out ) out = read_io( emu, addr )
#define CPU_BEGIN \
-void Run_cpu( struct Gbs_Emu* this )\
+void run_cpu( struct Gbs_Emu* this )\
{ \
struct Gb_Cpu* cpu = &this->cpu;
#include "gb_cpu_run.h"
diff --git a/apps/codecs/libgme/gbs_emu.c b/apps/codecs/libgme/gbs_emu.c
index 640ea43..7a6d484 100644
--- a/apps/codecs/libgme/gbs_emu.c
+++ b/apps/codecs/libgme/gbs_emu.c
@@ -3,7 +3,6 @@
#include "gbs_emu.h"
#include "blargg_endian.h"
-#include "blargg_source.h"
/* Copyright (C) 2003-2006 Shay Green. this module is free software; you
can redistribute it and/or modify it under the terms of the GNU Lesser
@@ -16,30 +15,17 @@ details. You should have received a copy of the GNU Lesser General Public
License along with this module; if not, write to the Free Software Foundation,
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+#include "blargg_source.h"
const char gme_wrong_file_type [] = "Wrong file type for this emulator";
int const idle_addr = 0xF00D;
int const tempo_unit = 16;
-int const stereo = 2; // number of channels for stereo
-int const silence_max = 6; // seconds
-int const silence_threshold = 0x10;
-long const fade_block_size = 512;
-int const fade_shift = 8; // fade ends with gain at 1.0 / (1 << fade_shift)
-
static void clear_track_vars( struct Gbs_Emu* this )
{
- this->current_track_ = -1;
- this->out_time = 0;
- this->emu_time = 0;
- this->emu_track_ended_ = true;
- this->track_ended = true;
- this->fade_start = (blargg_long)(LONG_MAX / 2 + 1);
- this->fade_step = 1;
- this->silence_time = 0;
- this->silence_count = 0;
- this->buf_remain = 0;
+ this->current_track_ = -1;
+ track_stop( &this->track_filter );
}
void Gbs_init( struct Gbs_Emu* this )
@@ -50,11 +36,13 @@ void Gbs_init( struct Gbs_Emu* this )
// Unload
this->header.timer_mode = 0;
- clear_track_vars( this );
- this->ignore_silence = false;
- this->silence_lookahead = 6;
- this->max_initial_silence = 21;
+ // defaults
+ this->tfilter = *track_get_setup( &this->track_filter );
+ this->tfilter.max_initial = 21;
+ this->tfilter.lookahead = 6;
+ this->track_filter.silence_ignored_ = false;
+
Sound_set_gain( this, (int)(FP_ONE_GAIN*1.2) );
Rom_init( &this->rom, 0x4000 );
@@ -67,6 +55,11 @@ void Gbs_init( struct Gbs_Emu* this )
// Reduce apu sound clicks?
Apu_reduce_clicks( &this->apu, true );
+
+ // clears fields
+ this->voice_count_ = 0;
+ this->voice_types_ = 0;
+ clear_track_vars( this );
}
static blargg_err_t check_gbs_header( void const* header )
@@ -78,11 +71,12 @@ static blargg_err_t check_gbs_header( void const* header )
// Setup
-blargg_err_t Gbs_load( struct Gbs_Emu* this, void* data, long size )
+blargg_err_t Gbs_load_mem( struct Gbs_Emu* this, void* data, long size )
{
// Unload
this->header.timer_mode = 0;
this->voice_count_ = 0;
+ this->track_count = 0;
this->m3u.size = 0;
clear_track_vars( this );
@@ -112,20 +106,24 @@ blargg_err_t Gbs_load( struct Gbs_Emu* this, void* data, long size )
Rom_set_addr( &this->rom, load_addr );
this->voice_count_ = osc_count;
+ static int const types [osc_count] = {
+ wave_type+1, wave_type+2, wave_type+3, mixed_type+1
+ };
+ this->voice_types_ = types;
+
Apu_volume( &this->apu, this->gain_ );
// Change clock rate & setup buffer
this->clock_rate_ = 4194304;
Buffer_clock_rate( &this->stereo_buf, 4194304 );
+ RETURN_ERR( Buffer_set_channel_count( &this->stereo_buf, this->voice_count_, this->voice_types_ ) );
this->buf_changed_count = Buffer_channels_changed_count( &this->stereo_buf );
// Post load
Sound_set_tempo( this, this->tempo_ );
-
- // Remute voices
Sound_mute_voices( this, this->mute_mask_ );
- // Reset track count
+ // Set track count
this->track_count = this->header.track_count;
return 0;
}
@@ -134,7 +132,7 @@ blargg_err_t Gbs_load( struct Gbs_Emu* this, void* data, long size )
// see gb_cpu_io.h for read/write functions
-void Set_bank( struct Gbs_Emu* this, int n )
+void set_bank( struct Gbs_Emu* this, int n )
{
addr_t addr = mask_addr( n * this->rom.bank_size, this->rom.mask );
if ( addr == 0 && this->rom.size > this->rom.bank_size )
@@ -142,7 +140,7 @@ void Set_bank( struct Gbs_Emu* this, int n )
Cpu_map_code( &this->cpu, this->rom.bank_size, this->rom.bank_size, Rom_at_addr( &this->rom, addr ) );
}
-void Update_timer( struct Gbs_Emu* this )
+void update_timer( struct Gbs_Emu* this )
{
this->play_period = 70224 / tempo_unit; /// 59.73 Hz
@@ -161,21 +159,21 @@ void Update_timer( struct Gbs_Emu* this )
// Jumps to routine, given pointer to address in file header. Pushes idle_addr
// as return address, NOT old PC.
-void Jsr_then_stop( struct Gbs_Emu* this, byte const addr [] )
+void jsr_then_stop( struct Gbs_Emu* this, byte const addr [] )
{
check( this->cpu.r.sp == get_le16( this->header.stack_ptr ) );
this->cpu.r.pc = get_le16( addr );
- Write_mem( this, --this->cpu.r.sp, idle_addr >> 8 );
- Write_mem( this, --this->cpu.r.sp, idle_addr );
+ write_mem( this, --this->cpu.r.sp, idle_addr >> 8 );
+ write_mem( this, --this->cpu.r.sp, idle_addr );
}
-static blargg_err_t Run_until( struct Gbs_Emu* this, int end )
+static blargg_err_t run_until( struct Gbs_Emu* this, int end )
{
this->end_time = end;
Cpu_set_time( &this->cpu, Cpu_time( &this->cpu ) - end );
while ( true )
{
- Run_cpu( this );
+ run_cpu( this );
if ( Cpu_time( &this->cpu ) >= 0 )
break;
@@ -190,7 +188,7 @@ static blargg_err_t Run_until( struct Gbs_Emu* this, int end )
if ( Cpu_time( &this->cpu ) < this->next_play - this->end_time )
Cpu_set_time( &this->cpu, this->next_play - this->end_time );
this->next_play += this->play_period;
- Jsr_then_stop( this, this->header.play_addr );
+ jsr_then_stop( this, this->header.play_addr );
}
else if ( this->cpu.r.pc > 0xFFFF )
{
@@ -208,9 +206,9 @@ static blargg_err_t Run_until( struct Gbs_Emu* this, int end )
return 0;
}
-static blargg_err_t End_frame( struct Gbs_Emu* this, int end )
+static blargg_err_t end_frame( struct Gbs_Emu* this, int end )
{
- RETURN_ERR( Run_until( this, end ) );
+ RETURN_ERR( run_until( this, end ) );
this->next_play -= end;
if ( this->next_play < 0 ) // happens when play routine takes too long
@@ -226,16 +224,19 @@ static blargg_err_t End_frame( struct Gbs_Emu* this, int end )
return 0;
}
-blargg_err_t Run_clocks( struct Gbs_Emu* this, blip_time_t duration )
+blargg_err_t run_clocks( struct Gbs_Emu* this, blip_time_t duration )
{
- return End_frame( this, duration );
+ return end_frame( this, duration );
}
-static blargg_err_t play_( struct Gbs_Emu* this, long count, sample_t* out )
+blargg_err_t play_( void* emu, int count, sample_t* out )
{
- long remain = count;
+ struct Gbs_Emu* this = (struct Gbs_Emu*) emu;
+
+ int remain = count;
while ( remain )
{
+ Buffer_disable_immediate_removal( &this->stereo_buf );
remain -= Buffer_read_samples( &this->stereo_buf, &out [count - remain], remain );
if ( remain )
{
@@ -247,8 +248,8 @@ static blargg_err_t play_( struct Gbs_Emu* this, long count, sample_t* out )
Sound_mute_voices( this, this->mute_mask_ );
}
int msec = Buffer_length( &this->stereo_buf );
- blip_time_t clocks_emulated = (blargg_long) msec * this->clock_rate_ / 1000;
- RETURN_ERR( Run_clocks( this, clocks_emulated ) );
+ blip_time_t clocks_emulated = msec * this->clock_rate_ / 1000 - 100;
+ RETURN_ERR( run_clocks( this, clocks_emulated ) );
assert( clocks_emulated );
Buffer_end_frame( &this->stereo_buf, clocks_emulated );
}
@@ -256,7 +257,7 @@ static blargg_err_t play_( struct Gbs_Emu* this, long count, sample_t* out )
return 0;
}
-blargg_err_t Gbs_set_sample_rate( struct Gbs_Emu* this, long rate )
+blargg_err_t Gbs_set_sample_rate( struct Gbs_Emu* this, int rate )
{
require( !this->sample_rate_ ); // sample rate can't be changed once set
Buffer_init( &this->stereo_buf );
@@ -266,6 +267,8 @@ blargg_err_t Gbs_set_sample_rate( struct Gbs_Emu* this, long rate )
Buffer_bass_freq( &this->stereo_buf, 300 );
this->sample_rate_ = rate;
+ RETURN_ERR( track_init( &this->track_filter, this ) );
+ this->tfilter.max_silence = 6 * stereo * this->sample_rate_;
return 0;
}
@@ -296,7 +299,7 @@ void Sound_mute_voices( struct Gbs_Emu* this, int mask )
}
else
{
- struct channel_t ch = Buffer_channel( &this->stereo_buf );
+ struct channel_t ch = Buffer_channel( &this->stereo_buf, i );
assert( (ch.center && ch.left && ch.right) ||
(!ch.center && !ch.left && !ch.right) ); // all or nothing
Apu_set_output( &this->apu, i, ch.center, ch.left, ch.right );
@@ -315,10 +318,9 @@ void Sound_set_tempo( struct Gbs_Emu* this, int t )
this->tempo = (int) ((tempo_unit * FP_ONE_TEMPO) / t);
Apu_set_tempo( &this->apu, t );
- Update_timer( this );
+ update_timer( this );
}
-void fill_buf( struct Gbs_Emu* this );
blargg_err_t Gbs_start_track( struct Gbs_Emu* this, int track )
{
clear_track_vars( this );
@@ -330,7 +332,6 @@ blargg_err_t Gbs_start_track( struct Gbs_Emu* this, int track )
}
this->current_track_ = track;
-
Buffer_clear( &this->stereo_buf );
// Reset APU to state expected by most rips
@@ -364,268 +365,88 @@ blargg_err_t Gbs_start_track( struct Gbs_Emu* this, int track )
Cpu_reset( &this->cpu, this->rom.unmapped );
Cpu_map_code( &this->cpu, ram_addr, 0x10000 - ram_addr, this->ram );
Cpu_map_code( &this->cpu, 0, this->rom.bank_size, Rom_at_addr( &this->rom, 0 ) );
- Set_bank( this, this->rom.size > this->rom.bank_size );
+ set_bank( this, this->rom.size > this->rom.bank_size );
- Update_timer( this );
+ update_timer( this );
this->next_play = this->play_period;
this->cpu.r.rp.fa = track;
this->cpu.r.sp = get_le16( this->header.stack_ptr );
this->cpu_time = 0;
- Jsr_then_stop( this, this->header.init_addr );
+ jsr_then_stop( this, this->header.init_addr );
- this->emu_track_ended_ = false;
- this->track_ended = false;
+ // convert filter times to samples
+ struct setup_t s = this->tfilter;
+ s.max_initial *= this->sample_rate_ * stereo;
+ #ifdef GME_DISABLE_SILENCE_LOOKAHEAD
+ s.lookahead = 1;
+ #endif
+ track_setup( &this->track_filter, &s );
- if ( !this->ignore_silence )
- {
- // play until non-silence or end of track
- long end;
- for ( end = this->max_initial_silence * stereo * this->sample_rate_; this->emu_time < end; )
- {
- fill_buf( this );
- if ( this->buf_remain | (int) this->emu_track_ended_ )
- break;
- }
-
- this->emu_time = this->buf_remain;
- this->out_time = 0;
- this->silence_time = 0;
- this->silence_count = 0;
- }
- /* return track_ended() ? warning() : 0; */
- return 0;
+ return track_start( &this->track_filter );
}
// Track
-static blargg_long msec_to_samples( blargg_long msec, long sample_rate )
+static int msec_to_samples( int msec, int sample_rate )
{
- blargg_long sec = msec / 1000;
+ int sec = msec / 1000;
msec -= sec * 1000;
return (sec * sample_rate + msec * sample_rate / 1000) * stereo;
}
-long Track_tell( struct Gbs_Emu* this )
+int Track_tell( struct Gbs_Emu* this )
{
- blargg_long rate = this->sample_rate_ * stereo;
- blargg_long sec = this->out_time / rate;
- return sec * 1000 + (this->out_time - sec * rate) * 1000 / rate;
+ int rate = this->sample_rate_ * stereo;
+ int sec = track_sample_count( &this->track_filter ) / rate;
+ return sec * 1000 + (track_sample_count( &this->track_filter ) - sec * rate) * 1000 / rate;
}
-blargg_err_t Track_seek( struct Gbs_Emu* this, long msec )
+blargg_err_t Track_seek( struct Gbs_Emu* this, int msec )
{
- blargg_long time = msec_to_samples( msec, this->sample_rate_ );
- if ( time < this->out_time )
- RETURN_ERR( Gbs_start_track( this, this->current_track_ ) );
- return Track_skip( this, time - this->out_time );
+ int time = msec_to_samples( msec, this->sample_rate_ );
+ if ( time < track_sample_count( &this->track_filter ) )
+ RETURN_ERR( Gbs_start_track( this, this->current_track_ ) );
+ return Track_skip( this, time - track_sample_count( &this->track_filter ) );
}
-static blargg_err_t skip_( struct Gbs_Emu* this, long count )
+blargg_err_t skip_( void* emu, int count )
{
+ struct Gbs_Emu* this = (struct Gbs_Emu*) emu;
+
// for long skip, mute sound
- const long threshold = 30000;
+ const int threshold = 32768;
if ( count > threshold )
{
int saved_mute = this->mute_mask_;
Sound_mute_voices( this, ~0 );
-
- while ( count > threshold / 2 && !this->emu_track_ended_ )
- {
- RETURN_ERR( play_( this, buf_size, this->buf ) );
- count -= buf_size;
- }
-
- Sound_mute_voices( this, saved_mute );
- }
-
- while ( count && !this->emu_track_ended_ )
- {
- long n = buf_size;
- if ( n > count )
- n = count;
- count -= n;
- RETURN_ERR( play_( this, n, this->buf ) );
- }
- return 0;
-}
-blargg_err_t Track_skip( struct Gbs_Emu* this, long count )
-{
- require( this->current_track_ >= 0 ); // start_track() must have been called already
- this->out_time += count;
-
- // remove from silence and buf first
- {
- long n = min( count, this->silence_count );
- this->silence_count -= n;
- count -= n;
-
- n = min( count, this->buf_remain );
- this->buf_remain -= n;
+ int n = count - threshold/2;
+ n &= ~(2048-1); // round to multiple of 2048
count -= n;
- }
-
- if ( count && !this->emu_track_ended_ )
- {
- this->emu_time += count;
- // End track if error
- if ( skip_( this, count ) )
- this->emu_track_ended_ = true;
- }
-
- if ( !(this->silence_count | this->buf_remain) ) // caught up to emulator, so update track ended
- this->track_ended |= this->emu_track_ended_;
-
- return 0;
-}
-
-// Fading
+ RETURN_ERR( skippy_( &this->track_filter, n ) );
-void Track_set_fade( struct Gbs_Emu* this, long start_msec, long length_msec )
-{
- this->fade_step = this->sample_rate_ * length_msec / (fade_block_size * fade_shift * 1000 / stereo);
- this->fade_start = msec_to_samples( start_msec, this->sample_rate_ );
-}
-
-// unit / pow( 2.0, (double) x / step )
-static int int_log( blargg_long x, int step, int unit )
-{
- int shift = x / step;
- int fraction = (x - shift * step) * unit / step;
- return ((unit - fraction) + (fraction >> 1)) >> shift;
-}
-
-static void handle_fade( struct Gbs_Emu* this, long out_count, sample_t* out )
-{
- int i;
- for ( i = 0; i < out_count; i += fade_block_size )
- {
- int const shift = 14;
- int const unit = 1 << shift;
- int gain = int_log( (this->out_time + i - this->fade_start) / fade_block_size,
- this->fade_step, unit );
- if ( gain < (unit >> fade_shift) )
- this->track_ended = this->emu_track_ended_ = true;
-
- sample_t* io = &out [i];
- int count;
- for ( count = min( fade_block_size, out_count - i ); count; --count )
- {
- *io = (sample_t) ((*io * gain) >> shift);
- ++io;
- }
+ Sound_mute_voices( this, saved_mute );
}
-}
-
-// Silence detection
-static void emu_play( struct Gbs_Emu* this, long count, sample_t* out )
-{
- check( current_track_ >= 0 );
- this->emu_time += count;
- if ( this->current_track_ >= 0 && !this->emu_track_ended_ ) {
- // End track if error
- if ( play_( this, count, out ) ) this->emu_track_ended_ = true;
- }
- else
- memset( out, 0, count * sizeof *out );
+ return skippy_( &this->track_filter, count );
}
-// number of consecutive silent samples at end
-static long count_silence( sample_t* begin, long size )
+blargg_err_t Track_skip( struct Gbs_Emu* this, int count )
{
- sample_t first = *begin;
- *begin = silence_threshold; // sentinel
- sample_t* p = begin + size;
- while ( (unsigned) (*--p + silence_threshold / 2) <= (unsigned) silence_threshold ) { }
- *begin = first;
- return size - (p - begin);
+ require( this->current_track_ >= 0 ); // start_track() must have been called already
+ return track_skip( &this->track_filter, count );
}
-// fill internal buffer and check it for silence
-void fill_buf( struct Gbs_Emu* this )
+void Track_set_fade( struct Gbs_Emu* this, int start_msec, int length_msec )
{
- assert( !this->buf_remain );
- if ( !this->emu_track_ended_ )
- {
- emu_play( this, buf_size, this->buf );
- long silence = count_silence( this->buf, buf_size );
- if ( silence < buf_size )
- {
- this->silence_time = this->emu_time - silence;
- this->buf_remain = buf_size;
- return;
- }
- }
- this->silence_count += buf_size;
+ track_set_fade( &this->track_filter, msec_to_samples( start_msec, this->sample_rate_ ),
+ length_msec * this->sample_rate_ / (1000 / stereo) );
}
-blargg_err_t Gbs_play( struct Gbs_Emu* this, long out_count, sample_t* out )
+blargg_err_t Gbs_play( struct Gbs_Emu* this, int out_count, sample_t* out )
{
- if ( this->track_ended )
- {
- memset( out, 0, out_count * sizeof *out );
- }
- else
- {
- require( this->current_track_ >= 0 );
- require( out_count % stereo == 0 );
-
- assert( this->emu_time >= this->out_time );
-
- long pos = 0;
- if ( this->silence_count )
- {
- // during a run of silence, run emulator at >=2x speed so it gets ahead
- long ahead_time = this->silence_lookahead * (this->out_time + out_count - this->silence_time) + this->silence_time;
- while ( this->emu_time < ahead_time && !(this->buf_remain | this->emu_track_ended_) )
- fill_buf( this );
-
- // fill with silence
- pos = min( this->silence_count, out_count );
- memset( out, 0, pos * sizeof *out );
- this->silence_count -= pos;
-
- if ( this->emu_time - this->silence_time > silence_max * stereo * this->sample_rate_ )
- {
- this->track_ended = this->emu_track_ended_ = true;
- this->silence_count = 0;
- this->buf_remain = 0;
- }
- }
-
- if ( this->buf_remain )
- {
- // empty silence buf
- long n = min( this->buf_remain, out_count - pos );
- memcpy( &out [pos], this->buf + (buf_size - this->buf_remain), n * sizeof *out );
- this->buf_remain -= n;
- pos += n;
- }
-
- // generate remaining samples normally
- long remain = out_count - pos;
- if ( remain )
- {
- emu_play( this, remain, out + pos );
- this->track_ended |= this->emu_track_ended_;
-
- if ( !this->ignore_silence || this->out_time > this->fade_start )
- {
- // check end for a new run of silence
- long silence = count_silence( out + pos, remain );
- if ( silence < remain )
- this->silence_time = this->emu_time - silence;
-
- if ( this->emu_time - this->silence_time >= buf_size )
- fill_buf( this ); // cause silence detection on next play()
- }
- }
-
- if ( this->out_time > this->fade_start )
- handle_fade( this, out_count, out );
- }
- this->out_time += out_count;
- return 0;
+ require( this->current_track_ >= 0 );
+ require( out_count % stereo == 0 );
+ return track_play( &this->track_filter, out_count, out );
}
diff --git a/apps/codecs/libgme/gbs_emu.h b/apps/codecs/libgme/gbs_emu.h
index 409cf2d..72671b4 100644
--- a/apps/codecs/libgme/gbs_emu.h
+++ b/apps/codecs/libgme/gbs_emu.h
@@ -9,15 +9,12 @@
#include "gb_apu.h"
#include "gb_cpu.h"
#include "m3u_playlist.h"
-
-/* typedef uint8_t byte; */
-typedef short sample_t;
+#include "track_filter.h"
enum { joypad_addr = 0xFF00 };
enum { ram_addr = 0xA000 };
enum { hi_page = 0xFF00 - ram_addr };
enum { io_base = 0xFF00 };
-enum { buf_size = 2048 };
// Selects which sound hardware to use. AGB hardware is cleaner than the
// others. Doesn't take effect until next start_track().
@@ -59,36 +56,19 @@ struct Gbs_Emu {
blip_time_t next_play;
// Sound
- long clock_rate_;
- long sample_rate_;
+ int clock_rate_;
+ int sample_rate_;
unsigned buf_changed_count;
int voice_count_;
+ int const* voice_types_;
+ int mute_mask_;
int gain_;
int tempo_;
// track-specific
byte track_count;
- volatile bool track_ended;
int current_track_;
- 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
-
- // fading
- blargg_long fade_start;
- int fade_step;
-
- // silence detection
- // Disable automatic end-of-track detection and skipping of silence at beginning
- bool ignore_silence;
- int max_initial_silence;
- int mute_mask_;
- int silence_lookahead; // speed to run emulator when looking ahead for silence
- long silence_time; // number of samples where most recent silence began
- long silence_count; // number of samples of silence to play before using buf
- long buf_remain; // number of samples left in silence buffer
-
// Larger items at the end
// Header for currently loaded file
struct header_t header;
@@ -96,11 +76,12 @@ struct Gbs_Emu {
// M3u Playlist
struct M3u_Playlist m3u;
+ struct setup_t tfilter;
+ struct Track_Filter track_filter;
+
struct Gb_Apu apu;
struct Gb_Cpu cpu;
- struct Stereo_Buffer stereo_buf;
-
- sample_t buf [buf_size];
+ struct Multi_Buffer stereo_buf;
// rom & ram
struct Rom_Data rom;
@@ -116,36 +97,48 @@ void Gbs_init( struct Gbs_Emu* this );
void Gbs_stop( struct Gbs_Emu* this );
// Loads a file from memory
-blargg_err_t Gbs_load( struct Gbs_Emu* this, void* data, long size );
+blargg_err_t Gbs_load_mem( struct Gbs_Emu* this, void* data, long size );
// Set output sample rate. Must be called only once before loading file.
-blargg_err_t Gbs_set_sample_rate( struct Gbs_Emu* this, long sample_rate );
+blargg_err_t Gbs_set_sample_rate( struct Gbs_Emu* this, int sample_rate );
// Start a track, where 0 is the first track. Also clears warning string.
blargg_err_t Gbs_start_track( struct Gbs_Emu* this, int );
// Generate 'count' samples info 'buf'. Output is in stereo. Any emulation
// errors set warning string, and major errors also end track.
-blargg_err_t Gbs_play( struct Gbs_Emu* this, long count, sample_t* buf );
+blargg_err_t Gbs_play( struct Gbs_Emu* this, int count, sample_t* buf );
// Track status/control
// Number of milliseconds (1000 msec = 1 second) played since beginning of track
-long Track_tell( struct Gbs_Emu* this );
+int Track_tell( struct Gbs_Emu* this );
// Seek to new time in track. Seeking backwards or far forward can take a while.
-blargg_err_t Track_seek( struct Gbs_Emu* this, long msec );
+blargg_err_t Track_seek( struct Gbs_Emu* this, int msec );
// Skip n samples
-blargg_err_t Track_skip( struct Gbs_Emu* this, long n );
+blargg_err_t Track_skip( struct Gbs_Emu* this, int n );
// Set start time and length of track fade out. Once fade ends track_ended() returns
// true. Fade time can be changed while track is playing.
-void Track_set_fade( struct Gbs_Emu* this, long start_msec, long length_msec );
+void Track_set_fade( struct Gbs_Emu* this, int start_msec, int length_msec );
+
+// True if a track has reached its end
+static inline bool Track_ended( struct Gbs_Emu* this )
+{
+ return track_ended( &this->track_filter );
+}
+
+// Disables automatic end-of-track detection and skipping of silence at beginning
+static inline void Track_ignore_silence( struct Gbs_Emu* this, bool disable )
+{
+ this->track_filter.silence_ignored_ = disable;
+}
// Get track length in milliseconds
-static inline long Track_get_length( struct Gbs_Emu* this, int n )
+static inline int Track_get_length( struct Gbs_Emu* this, int n )
{
- long length = 120 * 1000; /* 2 minutes */
+ int length = 120 * 1000; /* 2 minutes */
if ( (this->m3u.size > 0) && (n < this->m3u.size) ) {
struct entry_t* entry = &this->m3u.entries [n];
length = entry->length;
@@ -175,19 +168,18 @@ static inline void Sound_set_gain( struct Gbs_Emu* this, int g )
this->gain_ = g;
}
-
// Emulation (You shouldn't touch these)
-blargg_err_t Run_clocks( struct Gbs_Emu* this, blip_time_t duration );
-void Set_bank( struct Gbs_Emu* this, int );
-void Update_timer( struct Gbs_Emu* this );
+blargg_err_t run_clocks( struct Gbs_Emu* this, blip_time_t duration );
+void set_bank( struct Gbs_Emu* this, int );
+void update_timer( struct Gbs_Emu* this );
// Runs CPU until time becomes >= 0
-void Run_cpu( struct Gbs_Emu* this );
+void run_cpu( struct Gbs_Emu* this );
// Reads/writes memory and I/O
-int Read_mem( struct Gbs_Emu* this, addr_t addr );
-void Write_mem( struct Gbs_Emu* this, addr_t addr, int data );
+int read_mem( struct Gbs_Emu* this, addr_t addr );
+void write_mem( struct Gbs_Emu* this, addr_t addr, int data );
// Current time
static inline blip_time_t Time( struct Gbs_Emu* this )
@@ -195,6 +187,6 @@ static inline blip_time_t Time( struct Gbs_Emu* this )
return Cpu_time( &this->cpu ) + this->end_time;
}
-void Jsr_then_stop( struct Gbs_Emu* this, byte const [] );
+void jsr_then_stop( struct Gbs_Emu* this, byte const [] );
#endif
diff --git a/apps/codecs/libgme/gme_types.h b/apps/codecs/libgme/gme_types.h
deleted file mode 100644
index 06226f4..0000000
--- a/apps/codecs/libgme/gme_types.h
+++ /dev/null
@@ -1,21 +0,0 @@
-#ifndef GME_TYPES_H
-#define GME_TYPES_H
-
-/*
- * This is a default gme_types.h for use when *not* using
- * CMake. If CMake is in use gme_types.h.in will be
- * processed instead.
- */
-#define USE_GME_AY
-#define USE_GME_GBS
-#define USE_GME_GYM
-#define USE_GME_HES
-#define USE_GME_KSS
-#define USE_GME_NSF
-#define USE_GME_NSFE
-#define USE_GME_SAP
-#define USE_GME_SPC
-/* VGM and VGZ are a package deal */
-#define USE_GME_VGM
-
-#endif /* GME_TYPES_H */
diff --git a/apps/codecs/libgme/hes_apu.c b/apps/codecs/libgme/hes_apu.c
index 2a83142..054b164 100644
--- a/apps/codecs/libgme/hes_apu.c
+++ b/apps/codecs/libgme/hes_apu.c
@@ -18,8 +18,7 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
enum { center_waves = 1 }; // reduces asymmetry and clamping when starting notes
-static void Apu_balance_changed( struct Hes_Apu* this, struct Hes_Osc* osc );
-static void Apu_balance_changed( struct Hes_Apu* this, struct Hes_Osc* osc )
+static void balance_changed( struct Hes_Apu* this, struct Hes_Osc* osc )
{
static short const log_table [32] = { // ~1.5 db per step
#define ENTRY( factor ) (short) (factor * amp_range / 31.0 + 0.5)
@@ -42,27 +41,40 @@ static void Apu_balance_changed( struct Hes_Apu* this, struct Hes_Osc* osc )
int right = vol + (osc->balance << 1 & 0x1E) + (this->balance << 1 & 0x1E);
if ( right < 0 ) right = 0;
- left = log_table [left ];
- right = log_table [right];
-
// optimizing for the common case of being centered also allows easy
// panning using Effects_Buffer
- osc->outputs [0] = osc->chans [0]; // center
- osc->outputs [1] = 0;
- if ( left != right )
+
+ // Separate balance into center volume and additional on either left or right
+ osc->output [0] = osc->outputs [0]; // center
+ osc->output [1] = osc->outputs [2]; // right
+ int base = log_table [left ];
+ int side = log_table [right] - base;
+ if ( side < 0 )
+ {
+ base += side;
+ side = -side;
+ osc->output [1] = osc->outputs [1]; // left
+ }
+
+ // Optimize when output is far left, center, or far right
+ if ( !base || osc->output [0] == osc->output [1] )
{
- osc->outputs [0] = osc->chans [1]; // left
- osc->outputs [1] = osc->chans [2]; // right
+ base += side;
+ side = 0;
+ osc->output [0] = osc->output [1];
+ osc->output [1] = NULL;
+ osc->last_amp [1] = 0;
}
if ( center_waves )
{
- osc->last_amp [0] += (left - osc->volume [0]) * 16;
- osc->last_amp [1] += (right - osc->volume [1]) * 16;
+ // TODO: this can leave a non-zero level in a buffer (minor)
+ osc->last_amp [0] += (base - osc->volume [0]) * 16;
+ osc->last_amp [1] += (side - osc->volume [1]) * 16;
}
- osc->volume [0] = left;
- osc->volume [1] = right;
+ osc->volume [0] = base;
+ osc->volume [1] = side;
}
void Apu_init( struct Hes_Apu* this )
@@ -71,11 +83,11 @@ void Apu_init( struct Hes_Apu* this )
do
{
osc--;
- osc->outputs [0] = 0;
- osc->outputs [1] = 0;
- osc->chans [0] = 0;
- osc->chans [1] = 0;
- osc->chans [2] = 0;
+ osc->output [0] = NULL;
+ osc->output [1] = NULL;
+ osc->outputs [0] = NULL;
+ osc->outputs [1] = NULL;
+ osc->outputs [2] = NULL;
}
while ( osc != this->oscs );
@@ -92,139 +104,183 @@ void Apu_reset( struct Hes_Apu* this )
{
osc--;
memset( osc, 0, offsetof (struct Hes_Osc,outputs) );
- osc->noise_lfsr = 1;
+ osc->lfsr = 1;
osc->control = 0x40;
osc->balance = 0xFF;
}
while ( osc != this->oscs );
+
+ // Only last two oscs support noise
+ this->oscs [osc_count - 2].lfsr = 0x200C3; // equivalent to 1 in Fibonacci LFSR
+ this->oscs [osc_count - 1].lfsr = 0x200C3;
}
-void Apu_osc_output( struct Hes_Apu* this, int index, struct Blip_Buffer* center, struct Blip_Buffer* left, struct Blip_Buffer* right )
+void Apu_osc_output( struct Hes_Apu* this, int i, struct Blip_Buffer* center, struct Blip_Buffer* left, struct Blip_Buffer* right )
{
- require( (unsigned) index < osc_count );
- this->oscs [index].chans [0] = center;
- this->oscs [index].chans [1] = left;
- this->oscs [index].chans [2] = right;
+ // Must be silent (all NULL), mono (left and right NULL), or stereo (none NULL)
+ require( !center || (center && !left && !right) || (center && left && right) );
+ require( (unsigned) i < osc_count ); // fails if you pass invalid osc index
- struct Hes_Osc* osc = &this->oscs [osc_count];
- do
+ if ( !center || !left || !right )
{
- osc--;
- Apu_balance_changed( this, osc );
+ left = center;
+ right = center;
}
- while ( osc != this->oscs );
+
+ struct Hes_Osc* o = &this->oscs [i];
+ o->outputs [0] = center;
+ o->outputs [1] = right;
+ o->outputs [2] = left;
+ balance_changed( this, o );
}
-void Osc_run_until( struct Hes_Osc* this, struct Blip_Synth* synth_, blip_time_t end_time )
+void run_osc( struct Hes_Osc* o, struct Blip_Synth* syn, blip_time_t end_time )
{
- struct Blip_Buffer* const osc_outputs_0 = this->outputs [0]; // cache often-used values
- if ( osc_outputs_0 && this->control & 0x80 )
+ int vol0 = o->volume [0];
+ int vol1 = o->volume [1];
+ int dac = o->dac;
+
+ struct Blip_Buffer* out0 = o->output [0]; // cache often-used values
+ struct Blip_Buffer* out1 = o->output [1];
+ if ( !(o->control & 0x80) )
+ out0 = NULL;
+
+ if ( out0 )
{
- int dac = this->dac;
-
- int const volume_0 = this->volume [0];
+ // Update amplitudes
+ if ( out1 )
{
- int delta = dac * volume_0 - this->last_amp [0];
+ int delta = dac * vol1 - o->last_amp [1];
if ( delta )
- Synth_offset( synth_, this->last_time, delta, osc_outputs_0 );
- Blip_set_modified( osc_outputs_0 );
+ {
+ Synth_offset( syn, o->last_time, delta, out1 );
+ Blip_set_modified( out1 );
+ }
}
-
- struct Blip_Buffer* const osc_outputs_1 = this->outputs [1];
- int const volume_1 = this->volume [1];
- if ( osc_outputs_1 )
+ int delta = dac * vol0 - o->last_amp [0];
+ if ( delta )
{
- int delta = dac * volume_1 - this->last_amp [1];
- if ( delta )
- Synth_offset( synth_, this->last_time, delta, osc_outputs_1 );
- Blip_set_modified( osc_outputs_1 );
+ Synth_offset( syn, o->last_time, delta, out0 );
+ Blip_set_modified( out0 );
}
- blip_time_t time = this->last_time + this->delay;
+ // Don't generate if silent
+ if ( !(vol0 | vol1) )
+ out0 = NULL;
+ }
+
+ // Generate noise
+ int noise = 0;
+ if ( o->lfsr )
+ {
+ noise = o->noise & 0x80;
+
+ blip_time_t time = o->last_time + o->noise_delay;
if ( time < end_time )
{
- if ( this->noise & 0x80 )
+ int period = (~o->noise & 0x1F) * 128;
+ if ( !period )
+ period = 64;
+
+ if ( noise && out0 )
{
- if ( volume_0 | volume_1 )
+ unsigned lfsr = o->lfsr;
+ do
{
- // noise
- int const period = (32 - (this->noise & 0x1F)) * 64; // TODO: correct?
- unsigned noise_lfsr = this->noise_lfsr;
- do
+ int new_dac = -(lfsr & 1);
+ lfsr = (lfsr >> 1) ^ (0x30061 & new_dac);
+
+ int delta = (new_dac &= 0x1F) - dac;
+ if ( delta )
{
- int new_dac = 0x1F & -(noise_lfsr >> 1 & 1);
- // Implemented using "Galios configuration"
- // TODO: find correct LFSR algorithm
- noise_lfsr = (noise_lfsr >> 1) ^ (0xE008 & -(noise_lfsr & 1));
- //noise_lfsr = (noise_lfsr >> 1) ^ (0x6000 & -(noise_lfsr & 1));
- int delta = new_dac - dac;
- if ( delta )
- {
- dac = new_dac;
- Synth_offset( synth_, time, delta * volume_0, osc_outputs_0 );
- if ( osc_outputs_1 )
- Synth_offset( synth_, time, delta * volume_1, osc_outputs_1 );
- }
- time += period;
+ dac = new_dac;
+ Synth_offset( syn, time, delta * vol0, out0 );
+ if ( out1 )
+ Synth_offset( syn, time, delta * vol1, out1 );
}
- while ( time < end_time );
-
- this->noise_lfsr = noise_lfsr;
- assert( noise_lfsr );
+ time += period;
}
- }
- else if ( !(this->control & 0x40) )
- {
- // wave
- int phase = (this->phase + 1) & 0x1F; // pre-advance for optimal inner loop
- int period = this->period * 2;
- if ( period >= 14 && (volume_0 | volume_1) )
+ while ( time < end_time );
+
+ if ( !lfsr )
{
- do
- {
- int new_dac = this->wave [phase];
- phase = (phase + 1) & 0x1F;
- int delta = new_dac - dac;
- if ( delta )
- {
- dac = new_dac;
- Synth_offset( synth_, time, delta * volume_0, osc_outputs_0 );
- if ( osc_outputs_1 )
- Synth_offset( synth_, time, delta * volume_1, osc_outputs_1 );
- }
- time += period;
- }
- while ( time < end_time );
+ lfsr = 1;
+ check( false );
}
- else
+ o->lfsr = lfsr;
+
+ Blip_set_modified( out0 );
+ if ( out1 )
+ Blip_set_modified( out1 );
+ }
+ else
+ {
+ // Maintain phase when silent
+ int count = (end_time - time + period - 1) / period;
+ time += count * period;
+
+ // not worth it
+ //while ( count-- )
+ // o->lfsr = (o->lfsr >> 1) ^ (0x30061 * (o->lfsr & 1));
+ }
+ }
+ o->noise_delay = time - end_time;
+ }
+
+ // Generate wave
+ blip_time_t time = o->last_time + o->delay;
+ if ( time < end_time )
+ {
+ int phase = (o->phase + 1) & 0x1F; // pre-advance for optimal inner loop
+ int period = o->period * 2;
+
+ if ( period >= 14 && out0 && !((o->control & 0x40) | noise) )
+ {
+ do
+ {
+ int new_dac = o->wave [phase];
+ phase = (phase + 1) & 0x1F;
+ int delta = new_dac - dac;
+ if ( delta )
{
- if ( !period )
- {
- // TODO: Gekisha Boy assumes that period = 0 silences wave
- //period = 0x1000 * 2;
- period = 1;
- //if ( !(volume_0 | volume_1) )
- // dprintf( "Used period 0\n" );
- }
-
- // maintain phase when silent
- blargg_long count = (end_time - time + period - 1) / period;
- phase += count; // phase will be masked below
- time += count * period;
+ dac = new_dac;
+ Synth_offset( syn, time, delta * vol0, out0 );
+ if ( out1 )
+ Synth_offset( syn, time, delta * vol1, out1 );
}
- this->phase = (phase - 1) & 0x1F; // undo pre-advance
+ time += period;
}
+ while ( time < end_time );
+ Blip_set_modified( out0 );
+ if ( out1 )
+ Blip_set_modified( out1 );
+ }
+ else
+ {
+ // Maintain phase when silent
+ int count = end_time - time;
+ if ( !period )
+ period = 1;
+ count = (count + period - 1) / period;
+
+ phase += count; // phase will be masked below
+ time += count * period;
}
- time -= end_time;
- if ( time < 0 )
- time = 0;
- this->delay = time;
- this->dac = dac;
- this->last_amp [0] = dac * volume_0;
- this->last_amp [1] = dac * volume_1;
+ // TODO: Find whether phase increments even when both volumes are zero.
+ // CAN'T simply check for out0 being non-NULL, since it could be NULL
+ // if channel is muted in player, but still has non-zero volume.
+ // City Hunter breaks when this check is removed.
+ if ( !(o->control & 0x40) && (vol0 | vol1) )
+ o->phase = (phase - 1) & 0x1F; // undo pre-advance
}
- this->last_time = end_time;
+ o->delay = time - end_time;
+ check( o->delay >= 0 );
+
+ o->last_time = end_time;
+ o->dac = dac;
+ o->last_amp [0] = dac * vol0;
+ o->last_amp [1] = dac * vol1;
}
void Apu_write_data( struct Hes_Apu* this, blip_time_t time, int addr, int data )
@@ -243,8 +299,8 @@ void Apu_write_data( struct Hes_Apu* this, blip_time_t time, int addr, int data
do
{
osc--;
- Osc_run_until( osc, &this->synth, time );
- Apu_balance_changed( this, this->oscs );
+ run_osc( osc, &this->synth, time );
+ balance_changed( this, this->oscs );
}
while ( osc != this->oscs );
}
@@ -252,7 +308,7 @@ void Apu_write_data( struct Hes_Apu* this, blip_time_t time, int addr, int data
else if ( this->latch < osc_count )
{
struct Hes_Osc* osc = &this->oscs [this->latch];
- Osc_run_until( osc, &this->synth, time );
+ run_osc( osc, &this->synth, time );
switch ( addr )
{
case 0x802:
@@ -267,12 +323,12 @@ void Apu_write_data( struct Hes_Apu* this, blip_time_t time, int addr, int data
if ( osc->control & 0x40 & ~data )
osc->phase = 0;
osc->control = data;
- Apu_balance_changed( this, osc );
+ balance_changed( this, osc );
break;
case 0x805:
osc->balance = data;
- Apu_balance_changed( this, osc );
+ balance_changed( this, osc );
break;
case 0x806:
@@ -289,9 +345,9 @@ void Apu_write_data( struct Hes_Apu* this, blip_time_t time, int addr, int data
break;
case 0x807:
- if ( osc >= &this->oscs [4] )
- osc->noise = data;
+ osc->noise = data;
break;
+
case 0x809:
if ( !(data & 0x80) && (data & 0x03) != 0 ) {
dprintf( "HES LFO not supported\n" );
@@ -307,7 +363,7 @@ void Apu_end_frame( struct Hes_Apu* this, blip_time_t end_time )
{
osc--;
if ( end_time > osc->last_time )
- Osc_run_until( osc, &this->synth, end_time );
+ run_osc( osc, &this->synth, end_time );
assert( osc->last_time >= end_time );
osc->last_time -= end_time;
}
diff --git a/apps/codecs/libgme/hes_apu.h b/apps/codecs/libgme/hes_apu.h
index 8f8a525..0265e6a 100644
--- a/apps/codecs/libgme/hes_apu.h
+++ b/apps/codecs/libgme/hes_apu.h
@@ -5,40 +5,46 @@
#define HES_APU_H
#include "blargg_common.h"
+#include "blargg_source.h"
#include "blip_buffer.h"
enum { amp_range = 0x8000 };
-enum { osc_count = 6 };
-enum { start_addr = 0x0800 };
-enum { end_addr = 0x0809 };
+enum { osc_count = 6 }; // 0 <= chan < osc_count
+
+// Registers are at io_addr to io_addr+io_size-1
+enum { apu_io_addr = 0x0800 };
+enum { apu_io_size = 10 };
struct Hes_Osc
{
- unsigned char wave [32];
+ byte wave [32];
+ int delay;
+ int period;
+ int phase;
+
+ int noise_delay;
+ byte noise;
+ unsigned lfsr;
+
+ byte control;
+ byte balance;
+ byte dac;
short volume [2];
int last_amp [2];
- int delay;
- int period;
- unsigned char noise;
- unsigned char phase;
- unsigned char balance;
- unsigned char dac;
- blip_time_t last_time;
- struct Blip_Buffer* outputs [2];
- struct Blip_Buffer* chans [3];
- unsigned noise_lfsr;
- unsigned char control;
+ blip_time_t last_time;
+ struct Blip_Buffer* output [2];
+ struct Blip_Buffer* outputs [3];
};
void Osc_run_until( struct Hes_Osc* this, struct Blip_Synth* synth, blip_time_t );
struct Hes_Apu {
- struct Hes_Osc oscs [osc_count];
int latch;
int balance;
struct Blip_Synth synth;
+ struct Hes_Osc oscs [osc_count];
};
// Init HES apu sound chip
@@ -48,7 +54,12 @@ void Apu_init( struct Hes_Apu* this );
void Apu_reset( struct Hes_Apu* this );
void Apu_osc_output( struct Hes_Apu* this, int index, struct Blip_Buffer* center, struct Blip_Buffer* left, struct Blip_Buffer* right );
+
+// Emulates to time t, then writes data to addr
void Apu_write_data( struct Hes_Apu* this, blip_time_t, int addr, int data );
+
+// Emulates to time t, then subtracts t from the current time.
+// OK if previous write call had time slightly after t.
void Apu_end_frame( struct Hes_Apu* this, blip_time_t );
static inline void Apu_volume( struct Hes_Apu* this, int v ) { Synth_volume( &this->synth, (v*9)/5 / osc_count / amp_range ); }
diff --git a/apps/codecs/libgme/hes_apu_adpcm.h b/apps/codecs/libgme/hes_apu_adpcm.h
index 4a2afb3..afe160b 100644
--- a/apps/codecs/libgme/hes_apu_adpcm.h
+++ b/apps/codecs/libgme/hes_apu_adpcm.h
@@ -12,8 +12,8 @@ enum { adpcm_amp_range = 2048 };
enum { adpcm_osc_count = 1 }; // 0 <= chan < osc_count
// Registers are at io_addr to io_addr+io_size-1
-enum { io_addr = 0x1800 };
-enum { io_size = 0x400 };
+enum { adpcm_io_addr = 0x1800 };
+enum { adpcm_io_size = 0x400 };
struct State
{
diff --git a/apps/codecs/libgme/hes_cpu.c b/apps/codecs/libgme/hes_cpu.c
index 74b9059..6b833b3 100644
--- a/apps/codecs/libgme/hes_cpu.c
+++ b/apps/codecs/libgme/hes_cpu.c
@@ -1,6 +1,6 @@
// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/
-#include "hes_cpu.h"
+#include "hes_emu.h"
#include "blargg_endian.h"
@@ -17,1305 +17,105 @@ details. You should have received a copy of the GNU Lesser General Public
License along with this module; if not, write to the Free Software Foundation,
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
-// TODO: support T flag, including clearing it at appropriate times?
-
-// all zero-page should really use whatever is at page 1, but that would
-// reduce efficiency quite a bit
-int const ram_addr = 0x2000;
-
-#define FLUSH_TIME() (void) (s.time = s_time)
-#define CACHE_TIME() (void) (s_time = s.time)
-
-#include "hes_cpu_io.h"
-
#include "blargg_source.h"
+#define PAGE HES_CPU_PAGE
-#ifdef BLARGG_NONPORTABLE
- #define PAGE_OFFSET( addr ) (addr)
-#else
- #define PAGE_OFFSET( addr ) ((addr) & (page_size - 1))
-#endif
-
-// status flags
-int const st_n = 0x80;
-int const st_v = 0x40;
-int const st_t = 0x20;
-int const st_b = 0x10;
-int const st_d = 0x08;
-int const st_i = 0x04;
-int const st_z = 0x02;
-int const st_c = 0x01;
-
-void Cpu_init( struct Hes_Cpu* this )
+int read_mem( struct Hes_Emu* this, hes_addr_t addr )
{
- this->state = &this->state_;
+ check( addr < 0x10000 );
+ int result = *Cpu_get_code( &this->cpu, addr );
+ if ( this->cpu.mmr [PAGE( addr )] == 0xFF )
+ result = read_mem_( this, addr );
+ return result;
}
-void Cpu_reset( struct Hes_Cpu* this )
+void write_mem( struct Hes_Emu* this, hes_addr_t addr, int data )
{
- check( this->state == &state_ );
- this->state = &this->state_;
-
- this->state_.time = 0;
- this->state_.base = 0;
- this->irq_time = (hes_time_t)future_hes_time;
- this->end_time = (hes_time_t)future_hes_time;
-
- this->r.status = st_i;
- this->r.sp = 0;
- this->r.pc = 0;
- this->r.a = 0;
- this->r.x = 0;
- this->r.y = 0;
-
- blargg_verify_byte_order();
+ check( addr < 0x10000 );
+ byte* out = this->write_pages [PAGE( addr )];
+ if ( out )
+ out [addr & (page_size - 1)] = data;
+ else if ( this->cpu.mmr [PAGE( addr )] == 0xFF )
+ write_mem_( this, addr, data );
}
-void Cpu_set_mmr( struct Hes_Emu* this, int reg, int bank )
+void set_mmr( struct Hes_Emu* this, int page, int bank )
{
- assert( (unsigned) reg <= page_count ); // allow page past end to be set
- assert( (unsigned) bank < 0x100 );
- this->cpu.mmr [reg] = bank;
- uint8_t const* code = CPU_SET_MMR( this, reg, bank );
- this->cpu.state->code_map [reg] = code - PAGE_OFFSET( reg << page_shift );
-}
-
-#define TIME (s_time + s.base)
-
-#define READ( addr ) CPU_READ( this, (addr), TIME )
-#define WRITE( addr, data ) {CPU_WRITE( this, (addr), (data), TIME );}
-#define READ_LOW( addr ) (cpu->ram [(int) (addr)])
-#define WRITE_LOW( addr, data ) (void) (READ_LOW( addr ) = (data))
-#define READ_PROG( addr ) (s.code_map [(addr) >> page_shift] [PAGE_OFFSET( addr )])
-
-#define SET_SP( v ) (sp = ((v) + 1) | 0x100)
-#define GET_SP() ((sp - 1) & 0xFF)
-#define PUSH( v ) ((sp = (sp - 1) | 0x100), WRITE_LOW( sp, v ))
-
-// even on x86, using short and unsigned char was slower
-typedef int fint16;
-typedef unsigned fuint16;
-typedef unsigned fuint8;
-typedef blargg_long fint32;
-
-bool Cpu_run( struct Hes_Emu* this, hes_time_t end_time )
-{
- bool illegal_encountered = false;
-
- // Set cpu end time
- struct Hes_Cpu* cpu = &this->cpu;
- cpu->state->time += Cpu_update_end_time( cpu, cpu->r.status, (cpu->end_time = end_time), cpu->irq_time );
-
- struct state_t s = cpu->state_;
- cpu->state = &s;
-
- // even on x86, using s.time in place of s_time was slower
- fint16 s_time = s.time;
-
- struct registers_t* r = &cpu->r;
-
- // registers
- fuint16 pc = r->pc;
- fuint8 a = r->a;
- fuint8 x = r->x;
- fuint8 y = r->y;
- fuint16 sp;
- SET_SP( r->sp );
-
- #define IS_NEG (nz & 0x8080)
-
- #define CALC_STATUS( out ) do {\
- out = status & (st_v | st_d | st_i);\
- out |= ((nz >> 8) | nz) & st_n;\
- out |= c >> 8 & st_c;\
- if ( !(nz & 0xFF) ) out |= st_z;\
- } while ( 0 )
-
- #define SET_STATUS( in ) do {\
- status = in & (st_v | st_d | st_i);\
- nz = in << 8;\
- c = nz;\
- nz |= ~in & st_z;\
- } while ( 0 )
-
- fuint8 status;
- fuint16 c; // carry set if (c & 0x100) != 0
- fuint16 nz; // Z set if (nz & 0xFF) == 0, N set if (nz & 0x8080) != 0
- {
- fuint8 temp = r->status;
- SET_STATUS( temp );
- }
-
- goto loop;
-branch_not_taken:
- s_time -= 2;
-loop:
-
- #ifndef NDEBUG
- {
- hes_time_t correct = end_time_;
- if ( !(status & st_i) && correct > irq_time_ )
- correct = irq_time_;
- check( s.base == correct );
- /*
- static long count;
- if ( count == 1844 ) Debugger();
- if ( s.base != correct ) dprintf( "%ld\n", count );
- count++;
- */
- }
- #endif
-
- check( (unsigned) GET_SP() < 0x100 );
- check( (unsigned) a < 0x100 );
- check( (unsigned) x < 0x100 );
-
- uint8_t const* instr = s.code_map [pc >> page_shift];
- fuint8 opcode;
-
- // TODO: eliminate this special case
- #ifdef BLARGG_NONPORTABLE
- opcode = instr [pc];
- pc++;
- instr += pc;
- #else
- instr += PAGE_OFFSET( pc );
- opcode = *instr++;
- pc++;
- #endif
-
- // TODO: each reference lists slightly different timing values, ugh
- static uint8_t const clock_table [256] =
- {// 0 1 2 3 4 5 6 7 8 9 A B C D E F
- 1,7,3, 4,6,4,6,7,3,2,2,2,7,5,7,6,// 0
- 4,7,7, 4,6,4,6,7,2,5,2,2,7,5,7,6,// 1
- 7,7,3, 4,4,4,6,7,4,2,2,2,5,5,7,6,// 2
- 4,7,7, 2,4,4,6,7,2,5,2,2,5,5,7,6,// 3
- 7,7,3, 4,8,4,6,7,3,2,2,2,4,5,7,6,// 4
- 4,7,7, 5,2,4,6,7,2,5,3,2,2,5,7,6,// 5
- 7,7,2, 2,4,4,6,7,4,2,2,2,7,5,7,6,// 6
- 4,7,7,17,4,4,6,7,2,5,4,2,7,5,7,6,// 7
- 4,7,2, 7,4,4,4,7,2,2,2,2,5,5,5,6,// 8
- 4,7,7, 8,4,4,4,7,2,5,2,2,5,5,5,6,// 9
- 2,7,2, 7,4,4,4,7,2,2,2,2,5,5,5,6,// A
- 4,7,7, 8,4,4,4,7,2,5,2,2,5,5,5,6,// B
- 2,7,2,17,4,4,6,7,2,2,2,2,5,5,7,6,// C
- 4,7,7,17,2,4,6,7,2,5,3,2,2,5,7,6,// D
- 2,7,2,17,4,4,6,7,2,2,2,2,5,5,7,6,// E
- 4,7,7,17,2,4,6,7,2,5,4,2,2,5,7,6 // F
- }; // 0x00 was 8
-
- fuint16 data;
- data = clock_table [opcode];
- if ( (s_time += data) >= 0 )
- goto possibly_out_of_time;
-almost_out_of_time:
-
- data = *instr;
-
- #ifdef HES_CPU_LOG_H
- log_cpu( "new", pc - 1, opcode, instr [0], instr [1], instr [2],
- instr [3], instr [4], instr [5] );
- //log_opcode( opcode );
- #endif
-
- switch ( opcode )
- {
-possibly_out_of_time:
- if ( s_time < (int) data )
- goto almost_out_of_time;
- s_time -= data;
- goto out_of_time;
-
-// Macros
-
-#define GET_MSB() (instr [1])
-#define ADD_PAGE( out ) (pc++, out = data + 0x100 * GET_MSB());
-#define GET_ADDR() GET_LE16( instr )
-
-// TODO: is the penalty really always added? the original 6502 was much better
-//#define PAGE_CROSS_PENALTY( lsb ) (void) (s_time += (lsb) >> 8)
-#define PAGE_CROSS_PENALTY( lsb )
-
-// Branch
-
-// TODO: more efficient way to handle negative branch that wraps PC around
-#define BRANCH( cond )\
-{\
- fint16 offset = (int8_t) data;\
- pc++;\
- if ( !(cond) ) goto branch_not_taken;\
- pc = (uint16_t) (pc + offset);\
- goto loop;\
-}
-
- case 0xF0: // BEQ
- BRANCH( !((uint8_t) nz) );
-
- case 0xD0: // BNE
- BRANCH( (uint8_t) nz );
-
- case 0x10: // BPL
- BRANCH( !IS_NEG );
-
- case 0x90: // BCC
- BRANCH( !(c & 0x100) )
-
- case 0x30: // BMI
- BRANCH( IS_NEG )
-
- case 0x50: // BVC
- BRANCH( !(status & st_v) )
-
- case 0x70: // BVS
- BRANCH( status & st_v )
-
- case 0xB0: // BCS
- BRANCH( c & 0x100 )
-
- case 0x80: // BRA
- branch_taken:
- BRANCH( true );
-
- case 0xFF:
- if ( pc == idle_addr + 1 )
- goto idle_done;
- case 0x0F: // BBRn
- case 0x1F:
- case 0x2F:
- case 0x3F:
- case 0x4F:
- case 0x5F:
- case 0x6F:
- case 0x7F:
- case 0x8F: // BBSn
- case 0x9F:
- case 0xAF:
- case 0xBF:
- case 0xCF:
- case 0xDF:
- case 0xEF: {
- fuint16 t = 0x101 * READ_LOW( data );
- t ^= 0xFF;
- pc++;
- data = GET_MSB();
- BRANCH( t & (1 << (opcode >> 4)) )
- }
-
- case 0x4C: // JMP abs
- pc = GET_ADDR();
- goto loop;
-
- case 0x7C: // JMP (ind+X)
- data += x;
- case 0x6C:{// JMP (ind)
- data += 0x100 * GET_MSB();
- pc = GET_LE16( &READ_PROG( data ) );
- goto loop;
- }
-
-// Subroutine
-
- case 0x44: // BSR
- WRITE_LOW( 0x100 | (sp - 1), pc >> 8 );
- sp = (sp - 2) | 0x100;
- WRITE_LOW( sp, pc );
- goto branch_taken;
-
- case 0x20: { // JSR
- fuint16 temp = pc + 1;
- pc = GET_ADDR();
- WRITE_LOW( 0x100 | (sp - 1), temp >> 8 );
- sp = (sp - 2) | 0x100;
- WRITE_LOW( sp, temp );
- goto loop;
- }
-
- case 0x60: // RTS
- pc = 0x100 * READ_LOW( 0x100 | (sp - 0xFF) );
- pc += 1 + READ_LOW( sp );
- sp = (sp - 0xFE) | 0x100;
- goto loop;
-
- case 0x00: // BRK
- goto handle_brk;
-
-// Common
-
- case 0xBD:{// LDA abs,X
- PAGE_CROSS_PENALTY( data + x );
- fuint16 addr = GET_ADDR() + x;
- pc += 2;
- CPU_READ_FAST( this, addr, TIME, nz );
- a = nz;
- goto loop;
- }
-
- case 0x9D:{// STA abs,X
- fuint16 addr = GET_ADDR() + x;
- pc += 2;
- CPU_WRITE_FAST( this, addr, a, TIME );
- goto loop;
- }
-
- case 0x95: // STA zp,x
- data = (uint8_t) (data + x);
- case 0x85: // STA zp
- pc++;
- WRITE_LOW( data, a );
- goto loop;
-
- case 0xAE:{// LDX abs
- fuint16 addr = GET_ADDR();
- pc += 2;
- CPU_READ_FAST( this, addr, TIME, nz );
- x = nz;
- goto loop;
- }
-
- case 0xA5: // LDA zp
- a = nz = READ_LOW( data );
- pc++;
- goto loop;
-
-// Load/store
-
+ this->write_pages [page] = 0;
+ byte* data = Rom_at_addr( &this->rom, bank * page_size );
+ if ( bank >= 0x80 )
{
- fuint16 addr;
- case 0x91: // STA (ind),Y
- addr = 0x100 * READ_LOW( (uint8_t) (data + 1) );
- addr += READ_LOW( data ) + y;
- pc++;
- goto sta_ptr;
-
- case 0x81: // STA (ind,X)
- data = (uint8_t) (data + x);
- case 0x92: // STA (ind)
- addr = 0x100 * READ_LOW( (uint8_t) (data + 1) );
- addr += READ_LOW( data );
- pc++;
- goto sta_ptr;
-
- case 0x99: // STA abs,Y
- data += y;
- case 0x8D: // STA abs
- addr = data + 0x100 * GET_MSB();
- pc += 2;
- sta_ptr:
- CPU_WRITE_FAST( this, addr, a, TIME );
- goto loop;
- }
-
- {
- fuint16 addr;
- case 0xA1: // LDA (ind,X)
- data = (uint8_t) (data + x);
- case 0xB2: // LDA (ind)
- addr = 0x100 * READ_LOW( (uint8_t) (data + 1) );
- addr += READ_LOW( data );
- pc++;
- goto a_nz_read_addr;
-
- case 0xB1:// LDA (ind),Y
- addr = READ_LOW( data ) + y;
- PAGE_CROSS_PENALTY( addr );
- addr += 0x100 * READ_LOW( (uint8_t) (data + 1) );
- pc++;
- goto a_nz_read_addr;
-
- case 0xB9: // LDA abs,Y
- data += y;
- PAGE_CROSS_PENALTY( data );
- case 0xAD: // LDA abs
- addr = data + 0x100 * GET_MSB();
- pc += 2;
- a_nz_read_addr:
- CPU_READ_FAST( this, addr, TIME, nz );
- a = nz;
- goto loop;
- }
-
- case 0xBE:{// LDX abs,y
- PAGE_CROSS_PENALTY( data + y );
- fuint16 addr = GET_ADDR() + y;
- pc += 2;
- FLUSH_TIME();
- x = nz = READ( addr );
- CACHE_TIME();
- goto loop;
- }
-
- case 0xB5: // LDA zp,x
- a = nz = READ_LOW( (uint8_t) (data + x) );
- pc++;
- goto loop;
-
- case 0xA9: // LDA #imm
- pc++;
- a = data;
- nz = data;
- goto loop;
-
-// Bit operations
-
- case 0x3C: // BIT abs,x
- data += x;
- case 0x2C:{// BIT abs
- fuint16 addr;
- ADD_PAGE( addr );
- FLUSH_TIME();
- nz = READ( addr );
- CACHE_TIME();
- goto bit_common;
- }
- case 0x34: // BIT zp,x
- data = (uint8_t) (data + x);
- case 0x24: // BIT zp
- data = READ_LOW( data );
- case 0x89: // BIT imm
- nz = data;
- bit_common:
- pc++;
- status &= ~st_v;
- status |= nz & st_v;
- if ( nz & a )
- goto loop; // Z should be clear, and nz must be non-zero if nz & a is
- nz <<= 8; // set Z flag without affecting N flag
- goto loop;
+ data = 0;
+ switch ( bank )
+ {
+ case 0xF8:
+ data = this->ram;
+ break;
- {
- fuint16 addr;
+ case 0xF9:
+ case 0xFA:
+ case 0xFB:
+ data = &this->sgx [(bank - 0xF9) * page_size];
+ break;
- case 0xB3: // TST abs,x
- addr = GET_MSB() + x;
- goto tst_abs;
-
- case 0x93: // TST abs
- addr = GET_MSB();
- tst_abs:
- addr += 0x100 * instr [2];
- pc++;
- FLUSH_TIME();
- nz = READ( addr );
- CACHE_TIME();
- goto tst_common;
- }
-
- case 0xA3: // TST zp,x
- nz = READ_LOW( (uint8_t) (GET_MSB() + x) );
- goto tst_common;
-
- case 0x83: // TST zp
- nz = READ_LOW( GET_MSB() );
- tst_common:
- pc += 2;
- status &= ~st_v;
- status |= nz & st_v;
- if ( nz & data )
- goto loop; // Z should be clear, and nz must be non-zero if nz & data is
- nz <<= 8; // set Z flag without affecting N flag
- goto loop;
-
- {
- fuint16 addr;
- case 0x0C: // TSB abs
- case 0x1C: // TRB abs
- addr = GET_ADDR();
- pc++;
- goto txb_addr;
-
- // TODO: everyone lists different behaviors for the status flags, ugh
- case 0x04: // TSB zp
- case 0x14: // TRB zp
- addr = data + ram_addr;
- txb_addr:
- FLUSH_TIME();
- nz = a | READ( addr );
- if ( opcode & 0x10 )
- nz ^= a; // bits from a will already be set, so this clears them
- status &= ~st_v;
- status |= nz & st_v;
- pc++;
- WRITE( addr, nz );
- CACHE_TIME();
- goto loop;
- }
-
- case 0x07: // RMBn
- case 0x17:
- case 0x27:
- case 0x37:
- case 0x47:
- case 0x57:
- case 0x67:
- case 0x77:
- pc++;
- READ_LOW( data ) &= ~(1 << (opcode >> 4));
- goto loop;
-
- case 0x87: // SMBn
- case 0x97:
- case 0xA7:
- case 0xB7:
- case 0xC7:
- case 0xD7:
- case 0xE7:
- case 0xF7:
- pc++;
- READ_LOW( data ) |= 1 << ((opcode >> 4) - 8);
- goto loop;
-
-// Load/store
-
- case 0x9E: // STZ abs,x
- data += x;
- case 0x9C: // STZ abs
- ADD_PAGE( data );
- pc++;
- FLUSH_TIME();
- WRITE( data, 0 );
- CACHE_TIME();
- goto loop;
-
- case 0x74: // STZ zp,x
- data = (uint8_t) (data + x);
- case 0x64: // STZ zp
- pc++;
- WRITE_LOW( data, 0 );
- goto loop;
-
- case 0x94: // STY zp,x
- data = (uint8_t) (data + x);
- case 0x84: // STY zp
- pc++;
- WRITE_LOW( data, y );
- goto loop;
-
- case 0x96: // STX zp,y
- data = (uint8_t) (data + y);
- case 0x86: // STX zp
- pc++;
- WRITE_LOW( data, x );
- goto loop;
-
- case 0xB6: // LDX zp,y
- data = (uint8_t) (data + y);
- case 0xA6: // LDX zp
- data = READ_LOW( data );
- case 0xA2: // LDX #imm
- pc++;
- x = data;
- nz = data;
- goto loop;
-
- case 0xB4: // LDY zp,x
- data = (uint8_t) (data + x);
- case 0xA4: // LDY zp
- data = READ_LOW( data );
- case 0xA0: // LDY #imm
- pc++;
- y = data;
- nz = data;
- goto loop;
-
- case 0xBC: // LDY abs,X
- data += x;
- PAGE_CROSS_PENALTY( data );
- case 0xAC:{// LDY abs
- fuint16 addr = data + 0x100 * GET_MSB();
- pc += 2;
- FLUSH_TIME();
- y = nz = READ( addr );
- CACHE_TIME();
- goto loop;
- }
-
- {
- fuint8 temp;
- case 0x8C: // STY abs
- temp = y;
- goto store_abs;
-
- case 0x8E: // STX abs
- temp = x;
- store_abs:
- {
- fuint16 addr = GET_ADDR();
- pc += 2;
- FLUSH_TIME();
- WRITE( addr, temp );
- CACHE_TIME();
- goto loop;
- }
+ default:
+ /* if ( bank != 0xFF )
+ dprintf( "Unmapped bank $%02X\n", bank ); */
+ data = this->rom.unmapped;
+ goto end;
+ }
+
+ this->write_pages [page] = data;
}
+end:
+ Cpu_set_mmr( &this->cpu, page, bank, data );
+}
-// Compare
-
- case 0xEC:{// CPX abs
- fuint16 addr = GET_ADDR();
- pc++;
- FLUSH_TIME();
- data = READ( addr );
- CACHE_TIME();
- goto cpx_data;
- }
-
- case 0xE4: // CPX zp
- data = READ_LOW( data );
- case 0xE0: // CPX #imm
- cpx_data:
- nz = x - data;
- pc++;
- c = ~nz;
- nz &= 0xFF;
- goto loop;
-
- case 0xCC:{// CPY abs
- fuint16 addr = GET_ADDR();
- pc++;
- FLUSH_TIME();
- data = READ( addr );
- CACHE_TIME();
- goto cpy_data;
- }
-
- case 0xC4: // CPY zp
- data = READ_LOW( data );
- case 0xC0: // CPY #imm
- cpy_data:
- nz = y - data;
- pc++;
- c = ~nz;
- nz &= 0xFF;
- goto loop;
-
-// Logical
+#define READ_FAST( addr, out ) \
+{\
+ out = READ_CODE( addr );\
+ if ( cpu->mmr [PAGE( addr )] == 0xFF )\
+ {\
+ FLUSH_TIME();\
+ out = read_mem_( this, addr );\
+ CACHE_TIME();\
+ }\
+}
-#define ARITH_ADDR_MODES( op )\
- case op - 0x04: /* (ind,x) */\
- data = (uint8_t) (data + x);\
- case op + 0x0D: /* (ind) */\
- data = 0x100 * READ_LOW( (uint8_t) (data + 1) ) + READ_LOW( data );\
- goto ptr##op;\
- case op + 0x0C:{/* (ind),y */\
- fuint16 temp = READ_LOW( data ) + y;\
- PAGE_CROSS_PENALTY( temp );\
- data = temp + 0x100 * READ_LOW( (uint8_t) (data + 1) );\
- goto ptr##op;\
+#define WRITE_FAST( addr, data ) \
+{\
+ int page = PAGE( addr );\
+ byte* out = this->write_pages [page];\
+ addr &= page_size - 1;\
+ if ( out )\
+ {\
+ out [addr] = data;\
}\
- case op + 0x10: /* zp,X */\
- data = (uint8_t) (data + x);\
- case op + 0x00: /* zp */\
- data = READ_LOW( data );\
- goto imm##op;\
- case op + 0x14: /* abs,Y */\
- data += y;\
- goto ind##op;\
- case op + 0x18: /* abs,X */\
- data += x;\
- ind##op:\
- PAGE_CROSS_PENALTY( data );\
- case op + 0x08: /* abs */\
- ADD_PAGE( data );\
- ptr##op:\
+ else if ( cpu->mmr [page] == 0xFF )\
+ {\
FLUSH_TIME();\
- data = READ( data );\
+ write_mem_( this, addr, data );\
CACHE_TIME();\
- case op + 0x04: /* imm */\
- imm##op:
-
- ARITH_ADDR_MODES( 0xC5 ) // CMP
- nz = a - data;
- pc++;
- c = ~nz;
- nz &= 0xFF;
- goto loop;
-
- ARITH_ADDR_MODES( 0x25 ) // AND
- nz = (a &= data);
- pc++;
- goto loop;
-
- ARITH_ADDR_MODES( 0x45 ) // EOR
- nz = (a ^= data);
- pc++;
- goto loop;
-
- ARITH_ADDR_MODES( 0x05 ) // ORA
- nz = (a |= data);
- pc++;
- goto loop;
-
-// Add/subtract
-
- ARITH_ADDR_MODES( 0xE5 ) // SBC
- data ^= 0xFF;
- goto adc_imm;
-
- ARITH_ADDR_MODES( 0x65 ) // ADC
- adc_imm: {
- if ( status & st_d ) {
- dprintf( "Decimal mode not supported\n" );
- }
- fint16 carry = c >> 8 & 1;
- fint16 ov = (a ^ 0x80) + carry + (int8_t) data; // sign-extend
- status &= ~st_v;
- status |= ov >> 2 & 0x40;
- c = nz = a + data + carry;
- pc++;
- a = (uint8_t) nz;
- goto loop;
- }
-
-// Shift/rotate
-
- case 0x4A: // LSR A
- c = 0;
- case 0x6A: // ROR A
- nz = c >> 1 & 0x80;
- c = a << 8;
- nz |= a >> 1;
- a = nz;
- goto loop;
-
- case 0x0A: // ASL A
- nz = a << 1;
- c = nz;
- a = (uint8_t) nz;
- goto loop;
-
- case 0x2A: { // ROL A
- nz = a << 1;
- fint16 temp = c >> 8 & 1;
- c = nz;
- nz |= temp;
- a = (uint8_t) nz;
- goto loop;
- }
-
- case 0x5E: // LSR abs,X
- data += x;
- case 0x4E: // LSR abs
- c = 0;
- case 0x6E: // ROR abs
- ror_abs: {
- ADD_PAGE( data );
- FLUSH_TIME();
- int temp = READ( data );
- nz = (c >> 1 & 0x80) | (temp >> 1);
- c = temp << 8;
- goto rotate_common;
- }
-
- case 0x3E: // ROL abs,X
- data += x;
- goto rol_abs;
-
- case 0x1E: // ASL abs,X
- data += x;
- case 0x0E: // ASL abs
- c = 0;
- case 0x2E: // ROL abs
- rol_abs:
- ADD_PAGE( data );
- nz = c >> 8 & 1;
- FLUSH_TIME();
- nz |= (c = READ( data ) << 1);
- rotate_common:
- pc++;
- WRITE( data, (uint8_t) nz );
- CACHE_TIME();
- goto loop;
-
- case 0x7E: // ROR abs,X
- data += x;
- goto ror_abs;
-
- case 0x76: // ROR zp,x
- data = (uint8_t) (data + x);
- goto ror_zp;
-
- case 0x56: // LSR zp,x
- data = (uint8_t) (data + x);
- case 0x46: // LSR zp
- c = 0;
- case 0x66: // ROR zp
- ror_zp: {
- int temp = READ_LOW( data );
- nz = (c >> 1 & 0x80) | (temp >> 1);
- c = temp << 8;
- goto write_nz_zp;
- }
-
- case 0x36: // ROL zp,x
- data = (uint8_t) (data + x);
- goto rol_zp;
-
- case 0x16: // ASL zp,x
- data = (uint8_t) (data + x);
- case 0x06: // ASL zp
- c = 0;
- case 0x26: // ROL zp
- rol_zp:
- nz = c >> 8 & 1;
- nz |= (c = READ_LOW( data ) << 1);
- goto write_nz_zp;
-
-// Increment/decrement
-
-#define INC_DEC_AXY( reg, n ) reg = (uint8_t) (nz = reg + n); goto loop;
-
- case 0x1A: // INA
- INC_DEC_AXY( a, +1 )
-
- case 0xE8: // INX
- INC_DEC_AXY( x, +1 )
-
- case 0xC8: // INY
- INC_DEC_AXY( y, +1 )
-
- case 0x3A: // DEA
- INC_DEC_AXY( a, -1 )
-
- case 0xCA: // DEX
- INC_DEC_AXY( x, -1 )
-
- case 0x88: // DEY
- INC_DEC_AXY( y, -1 )
-
- case 0xF6: // INC zp,x
- data = (uint8_t) (data + x);
- case 0xE6: // INC zp
- nz = 1;
- goto add_nz_zp;
-
- case 0xD6: // DEC zp,x
- data = (uint8_t) (data + x);
- case 0xC6: // DEC zp
- nz = (unsigned) -1;
- add_nz_zp:
- nz += READ_LOW( data );
- write_nz_zp:
- pc++;
- WRITE_LOW( data, nz );
- goto loop;
-
- case 0xFE: // INC abs,x
- data = x + GET_ADDR();
- goto inc_ptr;
-
- case 0xEE: // INC abs
- data = GET_ADDR();
- inc_ptr:
- nz = 1;
- goto inc_common;
-
- case 0xDE: // DEC abs,x
- data = x + GET_ADDR();
- goto dec_ptr;
-
- case 0xCE: // DEC abs
- data = GET_ADDR();
- dec_ptr:
- nz = (unsigned) -1;
- inc_common:
- FLUSH_TIME();
- nz += READ( data );
- pc += 2;
- WRITE( data, (uint8_t) nz );
- CACHE_TIME();
- goto loop;
-
-// Transfer
-
- case 0xA8: // TAY
- y = a;
- nz = a;
- goto loop;
-
- case 0x98: // TYA
- a = y;
- nz = y;
- goto loop;
-
- case 0xAA: // TAX
- x = a;
- nz = a;
- goto loop;
-
- case 0x8A: // TXA
- a = x;
- nz = x;
- goto loop;
-
- case 0x9A: // TXS
- SET_SP( x ); // verified (no flag change)
- goto loop;
-
- case 0xBA: // TSX
- x = nz = GET_SP();
- goto loop;
-
- #define SWAP_REGS( r1, r2 ) {\
- fuint8 t = r1;\
- r1 = r2;\
- r2 = t;\
- goto loop;\
- }
-
- case 0x02: // SXY
- SWAP_REGS( x, y );
-
- case 0x22: // SAX
- SWAP_REGS( a, x );
-
- case 0x42: // SAY
- SWAP_REGS( a, y );
-
- case 0x62: // CLA
- a = 0;
- goto loop;
-
- case 0x82: // CLX
- x = 0;
- goto loop;
-
- case 0xC2: // CLY
- y = 0;
- goto loop;
-
-// Stack
-
- case 0x48: // PHA
- PUSH( a );
- goto loop;
-
- case 0xDA: // PHX
- PUSH( x );
- goto loop;
-
- case 0x5A: // PHY
- PUSH( y );
- goto loop;
-
- case 0x40:{// RTI
- fuint8 temp = READ_LOW( sp );
- pc = READ_LOW( 0x100 | (sp - 0xFF) );
- pc |= READ_LOW( 0x100 | (sp - 0xFE) ) * 0x100;
- sp = (sp - 0xFD) | 0x100;
- data = status;
- SET_STATUS( temp );
- r->status = status; // update externally-visible I flag
- if ( (data ^ status) & st_i )
- {
- hes_time_t new_time = cpu->end_time;
- if ( !(status & st_i) && new_time > cpu->irq_time )
- new_time = cpu->irq_time;
- blargg_long delta = s.base - new_time;
- s.base = new_time;
- s_time += delta;
- }
- goto loop;
- }
-
- #define POP() READ_LOW( sp ); sp = (sp - 0xFF) | 0x100
-
- case 0x68: // PLA
- a = nz = POP();
- goto loop;
-
- case 0xFA: // PLX
- x = nz = POP();
- goto loop;
-
- case 0x7A: // PLY
- y = nz = POP();
- goto loop;
-
- case 0x28:{// PLP
- fuint8 temp = POP();
- fuint8 changed = status ^ temp;
- SET_STATUS( temp );
- if ( !(changed & st_i) )
- goto loop; // I flag didn't change
- if ( status & st_i )
- goto handle_sei;
- goto handle_cli;
- }
- #undef POP
-
- case 0x08: { // PHP
- fuint8 temp;
- CALC_STATUS( temp );
- PUSH( temp | st_b );
- goto loop;
- }
-
-// Flags
-
- case 0x38: // SEC
- c = (unsigned) ~0;
- goto loop;
-
- case 0x18: // CLC
- c = 0;
- goto loop;
-
- case 0xB8: // CLV
- status &= ~st_v;
- goto loop;
-
- case 0xD8: // CLD
- status &= ~st_d;
- goto loop;
-
- case 0xF8: // SED
- status |= st_d;
- goto loop;
-
- case 0x58: // CLI
- if ( !(status & st_i) )
- goto loop;
- status &= ~st_i;
- handle_cli: {
- r->status = status; // update externally-visible I flag
- blargg_long delta = s.base - cpu->irq_time;
- if ( delta <= 0 )
- {
- if ( TIME < cpu->irq_time )
- goto loop;
- goto delayed_cli;
- }
- s.base = cpu->irq_time;
- s_time += delta;
- if ( s_time < 0 )
- goto loop;
-
- if ( delta >= s_time + 1 )
- {
- // delayed irq until after next instruction
- s.base += s_time + 1;
- s_time = -1;
- cpu->irq_time = s.base; // TODO: remove, as only to satisfy debug check in loop
- goto loop;
- }
- delayed_cli:
- dprintf( "Delayed CLI not supported\n" ); // TODO: implement
- goto loop;
- }
-
- case 0x78: // SEI
- if ( status & st_i )
- goto loop;
- status |= st_i;
- handle_sei: {
- r->status = status; // update externally-visible I flag
- blargg_long delta = s.base - cpu->end_time;
- s.base = cpu->end_time;
- s_time += delta;
- if ( s_time < 0 )
- goto loop;
- dprintf( "Delayed SEI not supported\n" ); // TODO: implement
- goto loop;
- }
-
-// Special
-
- case 0x53:{// TAM
- fuint8 const bits = data; // avoid using data across function call
- pc++;
- int i;
- for ( i = 0; i < 8; i++ )
- if ( bits & (1 << i) )
- /* this->cpu.set_mmr( i, a ); */
- Cpu_set_mmr( this, i, a );
- goto loop;
- }
-
- case 0x43:{// TMA
- pc++;
- byte const* in = cpu->mmr;
- do
- {
- if ( data & 1 )
- a = *in;
- in++;
- }
- while ( (data >>= 1) != 0 );
- goto loop;
- }
-
- case 0x03: // ST0
- case 0x13: // ST1
- case 0x23:{// ST2
- fuint16 addr = opcode >> 4;
- if ( addr )
- addr++;
- pc++;
- FLUSH_TIME();
- CPU_WRITE_VDP( this, addr, data, TIME );
- CACHE_TIME();
- goto loop;
- }
-
- case 0xEA: // NOP
- goto loop;
-
- case 0x54: // CSL
- dprintf( "CSL not supported\n" );
- illegal_encountered = true;
- goto loop;
-
- case 0xD4: // CSH
- goto loop;
-
- case 0xF4: { // SET
- //fuint16 operand = GET_MSB();
- dprintf( "SET not handled\n" );
- //switch ( data )
- //{
- //}
- illegal_encountered = true;
- goto loop;
- }
-
-// Block transfer
+ }\
+}
- {
- fuint16 in_alt;
- fint16 in_inc;
- fuint16 out_alt;
- fint16 out_inc;
-
- case 0xE3: // TIA
- in_alt = 0;
- goto bxfer_alt;
-
- case 0xF3: // TAI
- in_alt = 1;
- bxfer_alt:
- in_inc = in_alt ^ 1;
- out_alt = in_inc;
- out_inc = in_alt;
- goto bxfer;
-
- case 0xD3: // TIN
- in_inc = 1;
- out_inc = 0;
- goto bxfer_no_alt;
-
- case 0xC3: // TDD
- in_inc = -1;
- out_inc = -1;
- goto bxfer_no_alt;
-
- case 0x73: // TII
- in_inc = 1;
- out_inc = 1;
- bxfer_no_alt:
- in_alt = 0;
- out_alt = 0;
- bxfer: {
- fuint16 in = GET_LE16( instr + 0 );
- fuint16 out = GET_LE16( instr + 2 );
- int count = GET_LE16( instr + 4 );
- if ( !count )
- count = 0x10000;
- pc += 6;
- WRITE_LOW( 0x100 | (sp - 1), y );
- WRITE_LOW( 0x100 | (sp - 2), a );
- WRITE_LOW( 0x100 | (sp - 3), x );
- FLUSH_TIME();
- do
- {
- // TODO: reads from $0800-$1400 in I/O page return 0 and don't access I/O
- fuint8 t = READ( in );
- in += in_inc;
- in &= 0xFFFF;
- s.time += 6;
- if ( in_alt )
- in_inc = -in_inc;
- WRITE( out, t );
- out += out_inc;
- out &= 0xFFFF;
- if ( out_alt )
- out_inc = -out_inc;
- }
- while ( --count );
- CACHE_TIME();
- goto loop;
- }
- }
+#define READ_LOW( addr ) (this->ram [addr])
+#define WRITE_LOW( addr, data ) (this->ram [addr] = data)
+#define READ_MEM( addr ) read_mem( this, addr )
+#define WRITE_MEM( addr, data ) write_mem( this, addr, data )
+#define WRITE_VDP( addr, data ) write_vdp( this, addr, data )
+#define CPU_DONE( result_out ) { FLUSH_TIME(); result_out = cpu_done( this ); CACHE_TIME(); }
+#define SET_MMR( reg, bank ) set_mmr( this, reg, bank )
-// Illegal
+#define IDLE_ADDR idle_addr
- default:
- assert( (unsigned) opcode <= 0xFF );
- dprintf( "Illegal opcode $%02X at $%04X\n", (int) opcode, (int) pc - 1 );
- illegal_encountered = true;
- goto loop;
- }
- assert( false );
-
- int result_;
-handle_brk:
- pc++;
- result_ = 6;
-
-interrupt:
- {
- s_time += 7;
-
- WRITE_LOW( 0x100 | (sp - 1), pc >> 8 );
- WRITE_LOW( 0x100 | (sp - 2), pc );
- pc = GET_LE16( &READ_PROG( 0xFFF0 ) + result_ );
-
- sp = (sp - 3) | 0x100;
- fuint8 temp;
- CALC_STATUS( temp );
- if ( result_ == 6 )
- temp |= st_b;
- WRITE_LOW( sp, temp );
-
- status &= ~st_d;
- status |= st_i;
- r->status = status; // update externally-visible I flag
-
- blargg_long delta = s.base - cpu->end_time;
- s.base = cpu->end_time;
- s_time += delta;
- goto loop;
- }
-
-idle_done:
- s_time = 0;
-out_of_time:
- pc--;
- FLUSH_TIME();
- CPU_DONE( this, TIME, result_ );
- CACHE_TIME();
- if ( result_ > 0 )
- goto interrupt;
- if ( s_time < 0 )
- goto loop;
-
- s.time = s_time;
-
- r->pc = pc;
- r->sp = GET_SP();
- r->a = a;
- r->x = x;
- r->y = y;
-
- {
- fuint8 temp;
- CALC_STATUS( temp );
- r->status = temp;
- }
+#define CPU_BEGIN \
+bool run_cpu( struct Hes_Emu* this, hes_time_t end_time )\
+{\
+ struct Hes_Cpu* cpu = &this->cpu;\
+ Cpu_set_end_time( cpu, end_time );
- cpu->state_ = s;
- cpu->state = &cpu->state_;
+ #include "hes_cpu_run.h"
return illegal_encountered;
}
-
diff --git a/apps/codecs/libgme/hes_cpu.h b/apps/codecs/libgme/hes_cpu.h
index 4d76c83..0429eea 100644
--- a/apps/codecs/libgme/hes_cpu.h
+++ b/apps/codecs/libgme/hes_cpu.h
@@ -1,56 +1,52 @@
// PC Engine CPU emulator for use with HES music files
-// Game_Music_Emu 0.5.2
+// Game_Music_Emu 0.6-pre
#ifndef HES_CPU_H
#define HES_CPU_H
#include "blargg_common.h"
+#include "blargg_source.h"
-typedef blargg_long hes_time_t; // clock cycle count
-typedef unsigned hes_addr_t; // 16-bit address
+typedef int hes_time_t; // clock cycle count
+typedef int hes_addr_t; // 16-bit address
struct Hes_Emu;
-enum { future_hes_time = LONG_MAX / 2 + 1 };
-enum { page_size = 0x2000 };
-enum { page_shift = 13 };
-enum { page_count = 8 };
+enum { future_time = INT_MAX/2 + 1 };
+enum { page_bits = 13 };
+enum { page_size = 1 << page_bits };
+enum { page_count = 0x10000 / page_size };
-// Attempt to execute instruction here results in CPU advancing time to
-// lesser of irq_time() and end_time() (or end_time() if IRQs are
-// disabled)
-enum { idle_addr = 0x1FFF };
-
// Can read this many bytes past end of a page
enum { cpu_padding = 8 };
-enum { irq_inhibit = 0x04 };
-
+enum { irq_inhibit_mask = 0x04 };
+enum { idle_addr = 0x1FFF };
// Cpu state
-struct state_t {
- uint8_t const* code_map [page_count + 1];
+struct cpu_state_t {
+ byte const* code_map [page_count + 1];
hes_time_t base;
- blargg_long time;
+ int time;
};
-// Cpu registers
+// NOT kept updated during emulation.
struct registers_t {
uint16_t pc;
- uint8_t a;
- uint8_t x;
- uint8_t y;
- uint8_t status;
- uint8_t sp;
+ byte a;
+ byte x;
+ byte y;
+ byte flags;
+ byte sp;
};
struct Hes_Cpu {
struct registers_t r;
- hes_time_t irq_time;
- hes_time_t end_time;
+ hes_time_t irq_time_;
+ hes_time_t end_time_;
- struct state_t* state; // points to state_ or a local copy within run()
- struct state_t state_;
+ struct cpu_state_t* cpu_state; // points to state_ or a local copy within run()
+ struct cpu_state_t cpu_state_;
// page mapping registers
uint8_t mmr [page_count + 1];
@@ -58,7 +54,10 @@ struct Hes_Cpu {
};
// Init cpu state
-void Cpu_init( struct Hes_Cpu* this );
+static inline void Cpu_init( struct Hes_Cpu* this )
+{
+ this->cpu_state = &this->cpu_state_;
+}
// Reset hes cpu
void Cpu_reset( struct Hes_Cpu* this );
@@ -67,29 +66,67 @@ void Cpu_reset( struct Hes_Cpu* this );
// instructions were encountered.
bool Cpu_run( struct Hes_Emu* this, hes_time_t end_time );
-void Cpu_set_mmr( struct Hes_Emu* this, int reg, int bank );
-
// Time of ning of next instruction to be executed
static inline hes_time_t Cpu_time( struct Hes_Cpu* this )
{
- return this->state->time + this->state->base;
+ return this->cpu_state->time + this->cpu_state->base;
}
+static inline void Cpu_set_time( struct Hes_Cpu* this, hes_time_t t ) { this->cpu_state->time = t - this->cpu_state->base; }
+static inline void Cpu_adjust_time( struct Hes_Cpu* this, int delta ) { this->cpu_state->time += delta; }
+
+#define HES_CPU_PAGE( addr ) ((unsigned) (addr) >> page_bits)
+
+#ifdef BLARGG_NONPORTABLE
+ #define HES_CPU_OFFSET( addr ) (addr)
+#else
+ #define HES_CPU_OFFSET( addr ) ((addr) & (page_size - 1))
+#endif
+
static inline uint8_t const* Cpu_get_code( struct Hes_Cpu* this, hes_addr_t addr )
{
- return this->state->code_map [addr >> page_shift] + addr
- #if !defined (BLARGG_NONPORTABLE)
- % (unsigned) page_size
- #endif
- ;
+ return this->cpu_state_.code_map [HES_CPU_PAGE( addr )] + HES_CPU_OFFSET( addr );
+}
+
+static inline void update_end_time( struct Hes_Cpu* this, hes_time_t end, hes_time_t irq )
+{
+ if ( end > irq && !(this->r.flags & irq_inhibit_mask) )
+ end = irq;
+
+ this->cpu_state->time += this->cpu_state->base - end;
+ this->cpu_state->base = end;
+}
+
+static inline hes_time_t Cpu_end_time( struct Hes_Cpu* this ) { return this->end_time_; }
+
+static inline void Cpu_set_irq_time( struct Hes_Cpu* this, hes_time_t t )
+{
+ this->irq_time_ = t;
+ update_end_time( this, this->end_time_, t );
+}
+
+static inline void Cpu_set_end_time( struct Hes_Cpu* this, hes_time_t t )
+{
+ this->end_time_ = t;
+ update_end_time( this, t, this->irq_time_ );
+}
+
+static inline void Cpu_end_frame( struct Hes_Cpu* this, hes_time_t t )
+{
+ assert( this->cpu_state == &this->cpu_state_ );
+ this->cpu_state_.base -= t;
+ if ( this->irq_time_ < future_time ) this->irq_time_ -= t;
+ if ( this->end_time_ < future_time ) this->end_time_ -= t;
}
-static inline int Cpu_update_end_time( struct Hes_Cpu* this, uint8_t reg_status, hes_time_t t, hes_time_t irq )
+static inline void Cpu_set_mmr( struct Hes_Cpu* this, int reg, int bank, void const* code )
{
- if ( irq < t && !(reg_status & irq_inhibit) ) t = irq;
- int delta = this->state->base - t;
- this->state->base = t;
- return delta;
+ assert( (unsigned) reg <= page_count ); // allow page past end to be set
+ assert( (unsigned) bank < 0x100 );
+ this->mmr [reg] = bank;
+ byte const* p = STATIC_CAST(byte const*,code) - HES_CPU_OFFSET( reg << page_bits );
+ this->cpu_state->code_map [reg] = p;
+ this->cpu_state_.code_map [reg] = p;
}
#endif
diff --git a/apps/codecs/libgme/hes_cpu_io.h b/apps/codecs/libgme/hes_cpu_io.h
deleted file mode 100644
index 6b49c69..0000000
--- a/apps/codecs/libgme/hes_cpu_io.h
+++ /dev/null
@@ -1,72 +0,0 @@
-
-#include "hes_emu.h"
-
-#include "blargg_source.h"
-
-int Cpu_read( struct Hes_Emu* this, hes_addr_t addr )
-{
- check( addr <= 0xFFFF );
- int result = *Cpu_get_code( &this->cpu, addr );
- if ( this->cpu.mmr [addr >> page_shift] == 0xFF )
- result = Emu_cpu_read( this, addr );
- return result;
-}
-
-void Cpu_write( struct Hes_Emu* this, hes_addr_t addr, int data )
-{
- check( addr <= 0xFFFF );
- byte* out = this->write_pages [addr >> page_shift];
- addr &= page_size - 1;
- if ( out )
- out [addr] = data;
- else if ( this->cpu.mmr [addr >> page_shift] == 0xFF )
- Emu_cpu_write( this, addr, data );
-}
-
-#define CPU_READ_FAST( emu, addr, time, out ) \
- CPU_READ_FAST_( emu, addr, time, out )
-
-#define CPU_READ_FAST_( emu, addr, time, out ) \
-{\
- out = READ_PROG( addr );\
- if ( emu->cpu.mmr [addr >> page_shift] == 0xFF )\
- {\
- FLUSH_TIME();\
- out = Emu_cpu_read( emu, addr );\
- CACHE_TIME();\
- }\
-}
-
-#define CPU_WRITE_FAST( emu, addr, data, time ) \
- CPU_WRITE_FAST_( emu, addr, data, time )
-
-#define CPU_WRITE_FAST_( emu, addr, data, time ) \
-{\
- byte* out = emu->write_pages [addr >> page_shift];\
- addr &= page_size - 1;\
- if ( out )\
- {\
- out [addr] = data;\
- }\
- else if ( emu->cpu.mmr [addr >> page_shift] == 0xFF )\
- {\
- FLUSH_TIME();\
- Emu_cpu_write( emu, addr, data );\
- CACHE_TIME();\
- }\
-}
-
-#define CPU_READ( emu, addr, time ) \
- Cpu_read( emu, addr )
-
-#define CPU_WRITE( emu, addr, data, time ) \
- Cpu_write( emu, addr, data )
-
-#define CPU_WRITE_VDP( emu, addr, data, time ) \
- Cpu_write_vdp( emu, addr, data )
-
-#define CPU_SET_MMR( emu, page, bank ) \
- Emu_cpu_set_mmr( emu, page, bank )
-
-#define CPU_DONE( emu, time, result_out ) \
- result_out = Cpu_done( emu )
diff --git a/apps/codecs/libgme/hes_cpu_run.h b/apps/codecs/libgme/hes_cpu_run.h
new file mode 100644
index 0000000..bfba2b6
--- /dev/null
+++ b/apps/codecs/libgme/hes_cpu_run.h
@@ -0,0 +1,1344 @@
+// Game_Music_Emu 0.6-pre. http://www.slack.net/~ant/
+
+#if 0
+/* Define these macros in the source file before #including this file.
+- Parameters might be expressions, so they are best evaluated only once,
+though they NEVER have side-effects, so multiple evaluation is OK.
+- Output parameters might be a multiple-assignment expression like "a=x",
+so they must NOT be parenthesized.
+- Except where noted, time() and related functions will NOT work
+correctly inside a macro. TIME() is always correct, and FLUSH_TIME() and
+CACHE_TIME() allow the time changing functions to work.
+- Macros "returning" void may use a {} statement block. */
+
+ // 0 <= addr <= 0xFFFF + page_size
+ // time functions can be used
+ int READ_MEM( addr_t );
+ void WRITE_MEM( addr_t, int data );
+
+ // 0 <= addr <= 0x1FF
+ int READ_LOW( addr_t );
+ void WRITE_LOW( addr_t, int data );
+
+ // 0 <= addr <= 0xFFFF + page_size
+ // Used by common instructions.
+ int READ_FAST( addr_t, int& out );
+ void WRITE_FAST( addr_t, int data );
+
+ // 0 <= addr <= 2
+ // ST0, ST1, ST2 instructions
+ void WRITE_VDP( int addr, int data );
+
+// The following can be used within macros:
+
+ // Current time
+ hes_time_t TIME();
+
+ // Allows use of time functions
+ void FLUSH_TIME();
+
+ // Must be used before end of macro if FLUSH_TIME() was used earlier
+ void CACHE_TIME();
+
+// Configuration (optional; commented behavior if defined)
+
+ // Expanded just before beginning of code, to help debugger
+ #define CPU_BEGIN void my_run_cpu() {
+#endif
+
+/* Copyright (C) 2003-2008 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+// TODO: support T flag, including clearing it at appropriate times?
+
+// all zero-page should really use whatever is at page 1, but that would
+// reduce efficiency quite a bit
+int const ram_addr = 0x2000;
+
+void Cpu_reset( struct Hes_Cpu* this )
+{
+ check( this->cpu_state == &this->cpu_state_ );
+ this->cpu_state = &this->cpu_state_;
+
+ this->cpu_state_.time = 0;
+ this->cpu_state_.base = 0;
+ this->irq_time_ = future_time;
+ this->end_time_ = future_time;
+
+ this->r.flags = 0x04;
+ this->r.sp = 0;
+ this->r.pc = 0;
+ this->r.a = 0;
+ this->r.x = 0;
+ this->r.y = 0;
+
+ // Be sure "blargg_endian.h" has been #included
+ blargg_verify_byte_order();
+}
+
+// Allows MWCW debugger to step through code properly
+#ifdef CPU_BEGIN
+ CPU_BEGIN
+#endif
+
+// Time
+#define TIME() (s_time + s.base)
+#define FLUSH_TIME() {s.time = s_time;}
+#define CACHE_TIME() {s_time = s.time;}
+
+// Memory
+#define READ_STACK READ_LOW
+#define WRITE_STACK WRITE_LOW
+
+#define CODE_PAGE( addr ) s.code_map [HES_CPU_PAGE( addr )]
+#define CODE_OFFSET( addr ) HES_CPU_OFFSET( addr )
+#define READ_CODE( addr ) CODE_PAGE( addr ) [CODE_OFFSET( addr )]
+
+// Stack
+#define SET_SP( v ) (sp = ((v) + 1) | 0x100)
+#define GET_SP() ((sp - 1) & 0xFF)
+#define SP( o ) ((sp + (o - (o>0)*0x100)) | 0x100)
+
+// Truncation
+#define BYTE( n ) ((uint8_t ) (n)) /* (unsigned) n & 0xFF */
+#define SBYTE( n ) ((int8_t ) (n)) /* (BYTE( n ) ^ 0x80) - 0x80 */
+#define WORD( n ) ((uint16_t) (n)) /* (unsigned) n & 0xFFFF */
+
+// Flags with hex value for clarity when used as mask.
+// Stored in indicated variable during emulation.
+int const n80 = 0x80; // nz
+int const v40 = 0x40; // flags
+//int const t20 = 0x20;
+int const b10 = 0x10;
+int const d08 = 0x08; // flags
+int const i04 = 0x04; // flags
+int const z02 = 0x02; // nz
+int const c01 = 0x01; // c
+
+#define IS_NEG (nz & 0x8080)
+
+#define GET_FLAGS( out ) \
+{\
+ out = flags & (v40 | d08 | i04);\
+ out += ((nz >> 8) | nz) & n80;\
+ out += c >> 8 & c01;\
+ if ( !BYTE( nz ) )\
+ out += z02;\
+}
+
+#define SET_FLAGS( in ) \
+{\
+ flags = in & (v40 | d08 | i04);\
+ c = nz = in << 8;\
+ nz += ~in & z02;\
+}
+
+bool illegal_encountered = false;
+{
+ struct cpu_state_t s = cpu->cpu_state_;
+ cpu->cpu_state = &s;
+ // even on x86, using s.time in place of s_time was slower
+ int s_time = s.time;
+
+ // registers
+ int pc = cpu->r.pc;
+ int a = cpu->r.a;
+ int x = cpu->r.x;
+ int y = cpu->r.y;
+ int sp;
+ SET_SP( cpu->r.sp );
+
+ // Flags
+ int flags;
+ int c; // carry set if (c & 0x100) != 0
+ int nz; // Z set if (nz & 0xFF) == 0, N set if (nz & 0x8080) != 0
+ {
+ int temp = cpu->r.flags;
+ SET_FLAGS( temp );
+ }
+
+loop:
+
+ #ifndef NDEBUG
+ {
+ hes_time_t correct = cpu->end_time_;
+ if ( !(flags & i04) && correct > cpu->irq_time_ )
+ correct = cpu->irq_time_;
+ check( s.base == correct );
+ /*
+ static int count;
+ if ( count == 1844 ) Debugger();
+ if ( s.base != correct ) dprintf( "%ld\n", count );
+ count++;
+ */
+ }
+ #endif
+
+ // Check all values
+ check( (unsigned) sp - 0x100 < 0x100 );
+ check( (unsigned) pc < 0x10000 + 0x100 ); // +0x100 so emulator can catch wrap-around
+ check( (unsigned) a < 0x100 );
+ check( (unsigned) x < 0x100 );
+ check( (unsigned) y < 0x100 );
+
+ // Read instruction
+ byte const* instr = CODE_PAGE( pc );
+ int opcode;
+
+ if ( CODE_OFFSET(~0) == ~0 )
+ {
+ opcode = instr [pc];
+ pc++;
+ instr += pc;
+ }
+ else
+ {
+ instr += CODE_OFFSET( pc );
+ opcode = *instr++;
+ pc++;
+ }
+
+ // TODO: each reference lists slightly different timing values, ugh
+ static byte const clock_table [256] =
+ {// 0 1 2 3 4 5 6 7 8 9 A B C D E F
+ 1,7,3, 4,6,4,6,7,3,2,2,2,7,5,7,4,// 0
+ 2,7,7, 4,6,4,6,7,2,5,2,2,7,5,7,4,// 1
+ 7,7,3, 4,4,4,6,7,4,2,2,2,5,5,7,4,// 2
+ 2,7,7, 2,4,4,6,7,2,5,2,2,5,5,7,4,// 3
+ 7,7,3, 4,8,4,6,7,3,2,2,2,4,5,7,4,// 4
+ 2,7,7, 5,2,4,6,7,2,5,3,2,2,5,7,4,// 5
+ 7,7,2, 2,4,4,6,7,4,2,2,2,7,5,7,4,// 6
+ 2,7,7,17,4,4,6,7,2,5,4,2,7,5,7,4,// 7
+ 4,7,2, 7,4,4,4,7,2,2,2,2,5,5,5,4,// 8
+ 2,7,7, 8,4,4,4,7,2,5,2,2,5,5,5,4,// 9
+ 2,7,2, 7,4,4,4,7,2,2,2,2,5,5,5,4,// A
+ 2,7,7, 8,4,4,4,7,2,5,2,2,5,5,5,4,// B
+ 2,7,2,17,4,4,6,7,2,2,2,2,5,5,7,4,// C
+ 2,7,7,17,2,4,6,7,2,5,3,2,2,5,7,4,// D
+ 2,7,2,17,4,4,6,7,2,2,2,2,5,5,7,4,// E
+ 2,7,7,17,2,4,6,7,2,5,4,2,2,5,7,4 // F
+ }; // 0x00 was 8
+
+ // Update time
+ if ( s_time >= 0 )
+ goto out_of_time;
+
+ #ifdef HES_CPU_LOG_H
+ log_cpu( "new", pc - 1, opcode, instr [0], instr [1], instr [2],
+ instr [3], instr [4], instr [5], a, x, y );
+ //log_opcode( opcode );
+ #endif
+
+ s_time += clock_table [opcode];
+
+ int data;
+ data = *instr;
+
+ switch ( opcode )
+ {
+// Macros
+
+#define GET_MSB() (instr [1])
+#define ADD_PAGE( out ) (pc++, out = data + 0x100 * GET_MSB());
+#define GET_ADDR() GET_LE16( instr )
+
+// TODO: is the penalty really always added? the original 6502 was much better
+//#define PAGE_PENALTY( lsb ) (void) (s_time += (lsb) >> 8)
+#define PAGE_PENALTY( lsb )
+
+// Branch
+
+// TODO: more efficient way to handle negative branch that wraps PC around
+#define BRANCH_( cond, adj )\
+{\
+ pc++;\
+ if ( !(cond) ) goto loop;\
+ pc = (uint16_t) (pc + SBYTE( data ));\
+ s_time += adj;\
+ goto loop;\
+}
+
+#define BRANCH( cond ) BRANCH_( cond, 2 )
+
+ case 0xF0: // BEQ
+ BRANCH( !BYTE( nz ) );
+
+ case 0xD0: // BNE
+ BRANCH( BYTE( nz ) );
+
+ case 0x10: // BPL
+ BRANCH( !IS_NEG );
+
+ case 0x90: // BCC
+ BRANCH( !(c & 0x100) )
+
+ case 0x30: // BMI
+ BRANCH( IS_NEG )
+
+ case 0x50: // BVC
+ BRANCH( !(flags & v40) )
+
+ case 0x70: // BVS
+ BRANCH( flags & v40 )
+
+ case 0xB0: // BCS
+ BRANCH( c & 0x100 )
+
+ case 0x80: // BRA
+ branch_taken:
+ BRANCH_( true, 0 );
+
+ case 0xFF:
+ #ifdef IDLE_ADDR
+ if ( pc == IDLE_ADDR + 1 )
+ goto idle_done;
+ #endif
+
+ pc = (uint16_t) pc;
+
+ case 0x0F: // BBRn
+ case 0x1F:
+ case 0x2F:
+ case 0x3F:
+ case 0x4F:
+ case 0x5F:
+ case 0x6F:
+ case 0x7F:
+ case 0x8F: // BBSn
+ case 0x9F:
+ case 0xAF:
+ case 0xBF:
+ case 0xCF:
+ case 0xDF:
+ case 0xEF: {
+ // Make two copies of bits, one negated
+ int t = 0x101 * READ_LOW( data );
+ t ^= 0xFF;
+ pc++;
+ data = GET_MSB();
+ BRANCH( t & (1 << (opcode >> 4)) )
+ }
+
+ case 0x4C: // JMP abs
+ pc = GET_ADDR();
+ goto loop;
+
+ case 0x7C: // JMP (ind+X)
+ data += x;
+ case 0x6C:{// JMP (ind)
+ data += 0x100 * GET_MSB();
+ pc = GET_LE16( &READ_CODE( data ) );
+ goto loop;
+ }
+
+// Subroutine
+
+ case 0x44: // BSR
+ WRITE_STACK( SP( -1 ), pc >> 8 );
+ sp = SP( -2 );
+ WRITE_STACK( sp, pc );
+ goto branch_taken;
+
+ case 0x20: { // JSR
+ int temp = pc + 1;
+ pc = GET_ADDR();
+ WRITE_STACK( SP( -1 ), temp >> 8 );
+ sp = SP( -2 );
+ WRITE_STACK( sp, temp );
+ goto loop;
+ }
+
+ case 0x60: // RTS
+ pc = 1 + READ_STACK( sp );
+ pc += 0x100 * READ_STACK( SP( 1 ) );
+ sp = SP( 2 );
+ goto loop;
+
+ case 0x00: // BRK
+ goto handle_brk;
+
+// Common
+
+ case 0xBD:{// LDA abs,X
+ PAGE_PENALTY( data + x );
+ int addr = GET_ADDR() + x;
+ pc += 2;
+ READ_FAST( addr, nz );
+ a = nz;
+ goto loop;
+ }
+
+ case 0x9D:{// STA abs,X
+ int addr = GET_ADDR() + x;
+ pc += 2;
+ WRITE_FAST( addr, a );
+ goto loop;
+ }
+
+ case 0x95: // STA zp,x
+ data = BYTE( data + x );
+ case 0x85: // STA zp
+ pc++;
+ WRITE_LOW( data, a );
+ goto loop;
+
+ case 0xAE:{// LDX abs
+ int addr = GET_ADDR();
+ pc += 2;
+ READ_FAST( addr, nz );
+ x = nz;
+ goto loop;
+ }
+
+ case 0xA5: // LDA zp
+ a = nz = READ_LOW( data );
+ pc++;
+ goto loop;
+
+// Load/store
+
+ {
+ int addr;
+ case 0x91: // STA (ind),Y
+ addr = 0x100 * READ_LOW( BYTE( data + 1 ) );
+ addr += READ_LOW( data ) + y;
+ pc++;
+ goto sta_ptr;
+
+ case 0x81: // STA (ind,X)
+ data = BYTE( data + x );
+ case 0x92: // STA (ind)
+ addr = 0x100 * READ_LOW( BYTE( data + 1 ) );
+ addr += READ_LOW( data );
+ pc++;
+ goto sta_ptr;
+
+ case 0x99: // STA abs,Y
+ data += y;
+ case 0x8D: // STA abs
+ addr = data + 0x100 * GET_MSB();
+ pc += 2;
+ sta_ptr:
+ WRITE_FAST( addr, a );
+ goto loop;
+ }
+
+ {
+ int addr;
+ case 0xA1: // LDA (ind,X)
+ data = BYTE( data + x );
+ case 0xB2: // LDA (ind)
+ addr = 0x100 * READ_LOW( BYTE( data + 1 ) );
+ addr += READ_LOW( data );
+ pc++;
+ goto a_nz_read_addr;
+
+ case 0xB1:// LDA (ind),Y
+ addr = READ_LOW( data ) + y;
+ PAGE_PENALTY( addr );
+ addr += 0x100 * READ_LOW( BYTE( data + 1 ) );
+ pc++;
+ goto a_nz_read_addr;
+
+ case 0xB9: // LDA abs,Y
+ data += y;
+ PAGE_PENALTY( data );
+ case 0xAD: // LDA abs
+ addr = data + 0x100 * GET_MSB();
+ pc += 2;
+ a_nz_read_addr:
+ READ_FAST( addr, nz );
+ a = nz;
+ goto loop;
+ }
+
+ case 0xBE:{// LDX abs,y
+ PAGE_PENALTY( data + y );
+ int addr = GET_ADDR() + y;
+ pc += 2;
+ FLUSH_TIME();
+ x = nz = READ_MEM( addr );
+ CACHE_TIME();
+ goto loop;
+ }
+
+ case 0xB5: // LDA zp,x
+ a = nz = READ_LOW( BYTE( data + x ) );
+ pc++;
+ goto loop;
+
+ case 0xA9: // LDA #imm
+ pc++;
+ a = data;
+ nz = data;
+ goto loop;
+
+// Bit operations
+
+ case 0x3C: // BIT abs,x
+ data += x;
+ case 0x2C:{// BIT abs
+ int addr;
+ ADD_PAGE( addr );
+ FLUSH_TIME();
+ nz = READ_MEM( addr );
+ CACHE_TIME();
+ goto bit_common;
+ }
+ case 0x34: // BIT zp,x
+ data = BYTE( data + x );
+ case 0x24: // BIT zp
+ data = READ_LOW( data );
+ case 0x89: // BIT imm
+ nz = data;
+ bit_common:
+ pc++;
+ flags = (flags & ~v40) + (nz & v40);
+ if ( nz & a )
+ goto loop; // Z should be clear, and nz must be non-zero if nz & a is
+ nz <<= 8; // set Z flag without affecting N flag
+ goto loop;
+
+ {
+ int addr;
+
+ case 0xB3: // TST abs,x
+ addr = GET_MSB() + x;
+ goto tst_abs;
+
+ case 0x93: // TST abs
+ addr = GET_MSB();
+ tst_abs:
+ addr += 0x100 * instr [2];
+ pc++;
+ FLUSH_TIME();
+ nz = READ_MEM( addr );
+ CACHE_TIME();
+ goto tst_common;
+ }
+
+ case 0xA3: // TST zp,x
+ nz = READ_LOW( BYTE( GET_MSB() + x ) );
+ goto tst_common;
+
+ case 0x83: // TST zp
+ nz = READ_LOW( GET_MSB() );
+ tst_common:
+ pc += 2;
+ flags = (flags & ~v40) + (nz & v40);
+ if ( nz & data )
+ goto loop; // Z should be clear, and nz must be non-zero if nz & data is
+ nz <<= 8; // set Z flag without affecting N flag
+ goto loop;
+
+ {
+ int addr;
+ case 0x0C: // TSB abs
+ case 0x1C: // TRB abs
+ addr = GET_ADDR();
+ pc++;
+ goto txb_addr;
+
+ // TODO: everyone lists different behaviors for the flags flags, ugh
+ case 0x04: // TSB zp
+ case 0x14: // TRB zp
+ addr = data + ram_addr;
+ txb_addr:
+ FLUSH_TIME();
+ nz = a | READ_MEM( addr );
+ if ( opcode & 0x10 )
+ nz ^= a; // bits from a will already be set, so this clears them
+ flags = (flags & ~v40) + (nz & v40);
+ pc++;
+ WRITE_MEM( addr, nz );
+ CACHE_TIME();
+ goto loop;
+ }
+
+ case 0x07: // RMBn
+ case 0x17:
+ case 0x27:
+ case 0x37:
+ case 0x47:
+ case 0x57:
+ case 0x67:
+ case 0x77:
+ pc++;
+ READ_LOW( data ) &= ~(1 << (opcode >> 4));
+ goto loop;
+
+ case 0x87: // SMBn
+ case 0x97:
+ case 0xA7:
+ case 0xB7:
+ case 0xC7:
+ case 0xD7:
+ case 0xE7:
+ case 0xF7:
+ pc++;
+ READ_LOW( data ) |= 1 << ((opcode >> 4) - 8);
+ goto loop;
+
+// Load/store
+
+ case 0x9E: // STZ abs,x
+ data += x;
+ case 0x9C: // STZ abs
+ ADD_PAGE( data );
+ pc++;
+ FLUSH_TIME();
+ WRITE_MEM( data, 0 );
+ CACHE_TIME();
+ goto loop;
+
+ case 0x74: // STZ zp,x
+ data = BYTE( data + x );
+ case 0x64: // STZ zp
+ pc++;
+ WRITE_LOW( data, 0 );
+ goto loop;
+
+ case 0x94: // STY zp,x
+ data = BYTE( data + x );
+ case 0x84: // STY zp
+ pc++;
+ WRITE_LOW( data, y );
+ goto loop;
+
+ case 0x96: // STX zp,y
+ data = BYTE( data + y );
+ case 0x86: // STX zp
+ pc++;
+ WRITE_LOW( data, x );
+ goto loop;
+
+ case 0xB6: // LDX zp,y
+ data = BYTE( data + y );
+ case 0xA6: // LDX zp
+ data = READ_LOW( data );
+ case 0xA2: // LDX #imm
+ pc++;
+ x = data;
+ nz = data;
+ goto loop;
+
+ case 0xB4: // LDY zp,x
+ data = BYTE( data + x );
+ case 0xA4: // LDY zp
+ data = READ_LOW( data );
+ case 0xA0: // LDY #imm
+ pc++;
+ y = data;
+ nz = data;
+ goto loop;
+
+ case 0xBC: // LDY abs,X
+ data += x;
+ PAGE_PENALTY( data );
+ case 0xAC:{// LDY abs
+ int addr = data + 0x100 * GET_MSB();
+ pc += 2;
+ FLUSH_TIME();
+ y = nz = READ_MEM( addr );
+ CACHE_TIME();
+ goto loop;
+ }
+
+ {
+ int temp;
+ case 0x8C: // STY abs
+ temp = y;
+ if ( 0 )
+ case 0x8E: // STX abs
+ temp = x;
+ int addr = GET_ADDR();
+ pc += 2;
+ FLUSH_TIME();
+ WRITE_MEM( addr, temp );
+ CACHE_TIME();
+ goto loop;
+ }
+
+// Compare
+
+ case 0xEC:{// CPX abs
+ int addr = GET_ADDR();
+ pc++;
+ FLUSH_TIME();
+ data = READ_MEM( addr );
+ CACHE_TIME();
+ goto cpx_data;
+ }
+
+ case 0xE4: // CPX zp
+ data = READ_LOW( data );
+ case 0xE0: // CPX #imm
+ cpx_data:
+ nz = x - data;
+ pc++;
+ c = ~nz;
+ nz = BYTE( nz );
+ goto loop;
+
+ case 0xCC:{// CPY abs
+ int addr = GET_ADDR();
+ pc++;
+ FLUSH_TIME();
+ data = READ_MEM( addr );
+ CACHE_TIME();
+ goto cpy_data;
+ }
+
+ case 0xC4: // CPY zp
+ data = READ_LOW( data );
+ case 0xC0: // CPY #imm
+ cpy_data:
+ nz = y - data;
+ pc++;
+ c = ~nz;
+ nz = BYTE( nz );
+ goto loop;
+
+// Logical
+
+#define ARITH_ADDR_MODES( op )\
+ case op - 0x04: /* (ind,x) */\
+ data = BYTE( data + x );\
+ case op + 0x0D: /* (ind) */\
+ data = 0x100 * READ_LOW( BYTE( data + 1 ) ) + READ_LOW( data );\
+ goto ptr##op;\
+ case op + 0x0C:{/* (ind),y */\
+ int temp = READ_LOW( data ) + y;\
+ PAGE_PENALTY( temp );\
+ data = temp + 0x100 * READ_LOW( BYTE( data + 1 ) );\
+ goto ptr##op;\
+ }\
+ case op + 0x10: /* zp,X */\
+ data = BYTE( data + x );\
+ case op + 0x00: /* zp */\
+ data = READ_LOW( data );\
+ goto imm##op;\
+ case op + 0x14: /* abs,Y */\
+ data += y;\
+ goto ind##op;\
+ case op + 0x18: /* abs,X */\
+ data += x;\
+ ind##op:\
+ PAGE_PENALTY( data );\
+ case op + 0x08: /* abs */\
+ ADD_PAGE( data );\
+ ptr##op:\
+ FLUSH_TIME();\
+ data = READ_MEM( data );\
+ CACHE_TIME();\
+ case op + 0x04: /* imm */\
+ imm##op:
+
+ ARITH_ADDR_MODES( 0xC5 ) // CMP
+ nz = a - data;
+ pc++;
+ c = ~nz;
+ nz = BYTE( nz );
+ goto loop;
+
+ ARITH_ADDR_MODES( 0x25 ) // AND
+ nz = (a &= data);
+ pc++;
+ goto loop;
+
+ ARITH_ADDR_MODES( 0x45 ) // EOR
+ nz = (a ^= data);
+ pc++;
+ goto loop;
+
+ ARITH_ADDR_MODES( 0x05 ) // ORA
+ nz = (a |= data);
+ pc++;
+ goto loop;
+
+// Add/subtract
+
+ ARITH_ADDR_MODES( 0xE5 ) // SBC
+ data ^= 0xFF;
+ goto adc_imm;
+
+ ARITH_ADDR_MODES( 0x65 ) // ADC
+ adc_imm: {
+ /* if ( flags & d08 )
+ dprintf( "Decimal mode not supported\n" ); */
+ int carry = c >> 8 & 1;
+ int ov = (a ^ 0x80) + carry + SBYTE( data );
+ flags = (flags & ~v40) + (ov >> 2 & v40);
+ c = nz = a + data + carry;
+ pc++;
+ a = BYTE( nz );
+ goto loop;
+ }
+
+// Shift/rotate
+
+ case 0x4A: // LSR A
+ c = 0;
+ case 0x6A: // ROR A
+ nz = c >> 1 & 0x80;
+ c = a << 8;
+ nz += a >> 1;
+ a = nz;
+ goto loop;
+
+ case 0x0A: // ASL A
+ nz = a << 1;
+ c = nz;
+ a = BYTE( nz );
+ goto loop;
+
+ case 0x2A: { // ROL A
+ nz = a << 1;
+ int temp = c >> 8 & 1;
+ c = nz;
+ nz += temp;
+ a = BYTE( nz );
+ goto loop;
+ }
+
+ case 0x5E: // LSR abs,X
+ data += x;
+ case 0x4E: // LSR abs
+ c = 0;
+ case 0x6E: // ROR abs
+ ror_abs: {
+ ADD_PAGE( data );
+ FLUSH_TIME();
+ int temp = READ_MEM( data );
+ nz = (c >> 1 & 0x80) + (temp >> 1);
+ c = temp << 8;
+ goto rotate_common;
+ }
+
+ case 0x3E: // ROL abs,X
+ data += x;
+ goto rol_abs;
+
+ case 0x1E: // ASL abs,X
+ data += x;
+ case 0x0E: // ASL abs
+ c = 0;
+ case 0x2E: // ROL abs
+ rol_abs:
+ ADD_PAGE( data );
+ nz = c >> 8 & 1;
+ FLUSH_TIME();
+ nz += (c = READ_MEM( data ) << 1);
+ rotate_common:
+ pc++;
+ WRITE_MEM( data, BYTE( nz ) );
+ CACHE_TIME();
+ goto loop;
+
+ case 0x7E: // ROR abs,X
+ data += x;
+ goto ror_abs;
+
+ case 0x76: // ROR zp,x
+ data = BYTE( data + x );
+ goto ror_zp;
+
+ case 0x56: // LSR zp,x
+ data = BYTE( data + x );
+ case 0x46: // LSR zp
+ c = 0;
+ case 0x66: // ROR zp
+ ror_zp: {
+ int temp = READ_LOW( data );
+ nz = (c >> 1 & 0x80) + (temp >> 1);
+ c = temp << 8;
+ goto write_nz_zp;
+ }
+
+ case 0x36: // ROL zp,x
+ data = BYTE( data + x );
+ goto rol_zp;
+
+ case 0x16: // ASL zp,x
+ data = BYTE( data + x );
+ case 0x06: // ASL zp
+ c = 0;
+ case 0x26: // ROL zp
+ rol_zp:
+ nz = c >> 8 & 1;
+ nz += (c = READ_LOW( data ) << 1);
+ goto write_nz_zp;
+
+// Increment/decrement
+
+#define INC_DEC( reg, n ) reg = BYTE( nz = reg + n ); goto loop;
+
+ case 0x1A: // INA
+ INC_DEC( a, +1 )
+
+ case 0xE8: // INX
+ INC_DEC( x, +1 )
+
+ case 0xC8: // INY
+ INC_DEC( y, +1 )
+
+ case 0x3A: // DEA
+ INC_DEC( a, -1 )
+
+ case 0xCA: // DEX
+ INC_DEC( x, -1 )
+
+ case 0x88: // DEY
+ INC_DEC( y, -1 )
+
+ case 0xF6: // INC zp,x
+ data = BYTE( data + x );
+ case 0xE6: // INC zp
+ nz = 1;
+ goto add_nz_zp;
+
+ case 0xD6: // DEC zp,x
+ data = BYTE( data + x );
+ case 0xC6: // DEC zp
+ nz = -1;
+ add_nz_zp:
+ nz += READ_LOW( data );
+ write_nz_zp:
+ pc++;
+ WRITE_LOW( data, nz );
+ goto loop;
+
+ case 0xFE: // INC abs,x
+ data = x + GET_ADDR();
+ goto inc_ptr;
+
+ case 0xEE: // INC abs
+ data = GET_ADDR();
+ inc_ptr:
+ nz = 1;
+ goto inc_common;
+
+ case 0xDE: // DEC abs,x
+ data = x + GET_ADDR();
+ goto dec_ptr;
+
+ case 0xCE: // DEC abs
+ data = GET_ADDR();
+ dec_ptr:
+ nz = -1;
+ inc_common:
+ FLUSH_TIME();
+ pc += 2;
+ nz += READ_MEM( data );
+ WRITE_MEM( data, BYTE( nz ) );
+ CACHE_TIME();
+ goto loop;
+
+// Transfer
+
+ case 0xA8: // TAY
+ y = nz = a;
+ goto loop;
+
+ case 0x98: // TYA
+ a = nz = y;
+ goto loop;
+
+ case 0xAA: // TAX
+ x = nz = a;
+ goto loop;
+
+ case 0x8A: // TXA
+ a = nz = x;
+ goto loop;
+
+ case 0x9A: // TXS
+ SET_SP( x ); // verified (no flag change)
+ goto loop;
+
+ case 0xBA: // TSX
+ x = nz = GET_SP();
+ goto loop;
+
+ #define SWAP_REGS( r1, r2 ) {\
+ int t = r1;\
+ r1 = r2;\
+ r2 = t;\
+ goto loop;\
+ }
+
+ case 0x02: // SXY
+ SWAP_REGS( x, y );
+
+ case 0x22: // SAX
+ SWAP_REGS( a, x );
+
+ case 0x42: // SAY
+ SWAP_REGS( a, y );
+
+ case 0x62: // CLA
+ a = 0;
+ goto loop;
+
+ case 0x82: // CLX
+ x = 0;
+ goto loop;
+
+ case 0xC2: // CLY
+ y = 0;
+ goto loop;
+
+// Stack
+
+ case 0x48: // PHA
+ sp = SP( -1 );
+ WRITE_STACK( sp, a );
+ goto loop;
+
+ case 0x68: // PLA
+ a = nz = READ_STACK( sp );
+ sp = SP( 1 );
+ goto loop;
+
+ case 0xDA: // PHX
+ sp = SP( -1 );
+ WRITE_STACK( sp, x );
+ goto loop;
+
+ case 0x5A: // PHY
+ sp = SP( -1 );
+ WRITE_STACK( sp, y );
+ goto loop;
+
+ case 0x40:{// RTI
+ pc = READ_STACK( SP( 1 ) );
+ pc += READ_STACK( SP( 2 ) ) * 0x100;
+ int temp = READ_STACK( sp );
+ sp = SP( 3 );
+ data = flags;
+ SET_FLAGS( temp );
+ cpu->r.flags = flags; // update externally-visible I flag
+ if ( (data ^ flags) & i04 )
+ {
+ hes_time_t new_time = cpu->end_time_;
+ if ( !(flags & i04) && new_time > cpu->irq_time_ )
+ new_time = cpu->irq_time_;
+ int delta = s.base - new_time;
+ s.base = new_time;
+ s_time += delta;
+ }
+ goto loop;
+ }
+
+ case 0xFA: // PLX
+ x = nz = READ_STACK( sp );
+ sp = SP( 1 );
+ goto loop;
+
+ case 0x7A: // PLY
+ y = nz = READ_STACK( sp );
+ sp = SP( 1 );
+ goto loop;
+
+ case 0x28:{// PLP
+ int temp = READ_STACK( sp );
+ sp = SP( 1 );
+ int changed = flags ^ temp;
+ SET_FLAGS( temp );
+ if ( !(changed & i04) )
+ goto loop; // I flag didn't change
+ if ( flags & i04 )
+ goto handle_sei;
+ goto handle_cli;
+ }
+
+ case 0x08:{// PHP
+ int temp;
+ GET_FLAGS( temp );
+ sp = SP( -1 );
+ WRITE_STACK( sp, temp | b10 );
+ goto loop;
+ }
+
+// Flags
+
+ case 0x38: // SEC
+ c = 0x100;
+ goto loop;
+
+ case 0x18: // CLC
+ c = 0;
+ goto loop;
+
+ case 0xB8: // CLV
+ flags &= ~v40;
+ goto loop;
+
+ case 0xD8: // CLD
+ flags &= ~d08;
+ goto loop;
+
+ case 0xF8: // SED
+ flags |= d08;
+ goto loop;
+
+ case 0x58: // CLI
+ if ( !(flags & i04) )
+ goto loop;
+ flags &= ~i04;
+ handle_cli: {
+ //dprintf( "CLI at %d\n", TIME );
+ cpu->r.flags = flags; // update externally-visible I flag
+ int delta = s.base - cpu->irq_time_;
+ if ( delta <= 0 )
+ {
+ if ( TIME() < cpu->irq_time_ )
+ goto loop;
+ goto delayed_cli;
+ }
+ s.base = cpu->irq_time_;
+ s_time += delta;
+ if ( s_time < 0 )
+ goto loop;
+
+ if ( delta >= s_time + 1 )
+ {
+ // delayed irq until after next instruction
+ s.base += s_time + 1;
+ s_time = -1;
+ cpu->irq_time_ = s.base; // TODO: remove, as only to satisfy debug check in loop
+ goto loop;
+ }
+
+ // TODO: implement
+ delayed_cli:
+ dprintf( "Delayed CLI not supported\n" );
+ goto loop;
+ }
+
+ case 0x78: // SEI
+ if ( flags & i04 )
+ goto loop;
+ flags |= i04;
+ handle_sei: {
+ cpu->r.flags = flags; // update externally-visible I flag
+ int delta = s.base - cpu->end_time_;
+ s.base = cpu->end_time_;
+ s_time += delta;
+ if ( s_time < 0 )
+ goto loop;
+
+ dprintf( "Delayed SEI not supported\n" );
+ goto loop;
+ }
+
+// Special
+
+ case 0x53:{// TAM
+ int bits = data; // avoid using data across function call
+ pc++;
+ for ( int i = 0; i < 8; i++ )
+ if ( bits & (1 << i) )
+ SET_MMR( i, a );
+ goto loop;
+ }
+
+ case 0x43:{// TMA
+ pc++;
+ byte const* in = cpu->mmr;
+ do
+ {
+ if ( data & 1 )
+ a = *in;
+ in++;
+ }
+ while ( (data >>= 1) != 0 );
+ goto loop;
+ }
+
+ case 0x03: // ST0
+ case 0x13: // ST1
+ case 0x23:{// ST2
+ int addr = opcode >> 4;
+ if ( addr )
+ addr++;
+ pc++;
+ FLUSH_TIME();
+ WRITE_VDP( addr, data );
+ CACHE_TIME();
+ goto loop;
+ }
+
+ case 0xEA: // NOP
+ goto loop;
+
+ case 0x54: // CSL
+ dprintf( "CSL not supported\n" );
+ illegal_encountered = true;
+ goto loop;
+
+ case 0xD4: // CSH
+ goto loop;
+
+ case 0xF4: { // SET
+ //int operand = GET_MSB();
+ dprintf( "SET not handled\n" );
+ //switch ( data )
+ //{
+ //}
+ illegal_encountered = true;
+ goto loop;
+ }
+
+// Block transfer
+
+ {
+ int in_alt;
+ int in_inc;
+ int out_alt;
+ int out_inc;
+
+ case 0xE3: // TIA
+ in_alt = 0;
+ goto bxfer_alt;
+
+ case 0xF3: // TAI
+ in_alt = 1;
+ bxfer_alt:
+ in_inc = in_alt ^ 1;
+ out_alt = in_inc;
+ out_inc = in_alt;
+ goto bxfer;
+
+ case 0xD3: // TIN
+ in_inc = 1;
+ out_inc = 0;
+ goto bxfer_no_alt;
+
+ case 0xC3: // TDD
+ in_inc = -1;
+ out_inc = -1;
+ goto bxfer_no_alt;
+
+ case 0x73: // TII
+ in_inc = 1;
+ out_inc = 1;
+ bxfer_no_alt:
+ in_alt = 0;
+ out_alt = 0;
+ bxfer:
+ {
+ int in = GET_LE16( instr + 0 );
+ int out = GET_LE16( instr + 2 );
+ int count = GET_LE16( instr + 4 );
+ if ( !count )
+ count = 0x10000;
+ pc += 6;
+ WRITE_STACK( SP( -1 ), y );
+ WRITE_STACK( SP( -2 ), a );
+ WRITE_STACK( SP( -3 ), x );
+ FLUSH_TIME();
+ do
+ {
+ // TODO: reads from $0800-$1400 in I/O page should do I/O
+ int t = READ_MEM( in );
+ in = WORD( in + in_inc );
+ s.time += 6;
+ if ( in_alt )
+ in_inc = -in_inc;
+ WRITE_MEM( out, t );
+ out = WORD( out + out_inc );
+ if ( out_alt )
+ out_inc = -out_inc;
+ }
+ while ( --count );
+ CACHE_TIME();
+ goto loop;
+ }
+ }
+
+// Illegal
+
+ default:
+ check( (unsigned) opcode <= 0xFF );
+ dprintf( "Illegal opcode $%02X at $%04X\n", (int) opcode, (int) pc - 1 );
+ illegal_encountered = true;
+ goto loop;
+ }
+ assert( false ); // catch missing 'goto loop' or accidental 'break'
+
+ int result_;
+handle_brk:
+ pc++;
+ result_ = 6;
+
+interrupt:
+ {
+ s_time += 7;
+
+ // Save PC and read vector
+ WRITE_STACK( SP( -1 ), pc >> 8 );
+ WRITE_STACK( SP( -2 ), pc );
+ pc = GET_LE16( &READ_CODE( 0xFFF0 ) + result_ );
+
+ // Save flags
+ int temp;
+ GET_FLAGS( temp );
+ if ( result_ == 6 )
+ temp |= b10; // BRK sets B bit
+ sp = SP( -3 );
+ WRITE_STACK( sp, temp );
+
+ // Update I flag in externally-visible flags
+ flags &= ~d08;
+ cpu->r.flags = (flags |= i04);
+
+ // Update time
+ int delta = s.base - cpu->end_time_;
+ if ( delta >= 0 )
+ goto loop;
+ s_time += delta;
+ s.base = cpu->end_time_;
+ goto loop;
+ }
+
+idle_done:
+ s_time = 0;
+
+out_of_time:
+ pc--;
+
+ // Optional action that triggers interrupt or changes irq/end time
+ #ifdef CPU_DONE
+ {
+ CPU_DONE( result_ );
+ if ( result_ >= 0 )
+ goto interrupt;
+ if ( s_time < 0 )
+ goto loop;
+ }
+ #endif
+
+ // Flush cached state
+ cpu->r.pc = pc;
+ cpu->r.sp = GET_SP();
+ cpu->r.a = a;
+ cpu->r.x = x;
+ cpu->r.y = y;
+
+ int temp;
+ GET_FLAGS( temp );
+ cpu->r.flags = temp;
+
+ cpu->cpu_state_.base = s.base;
+ cpu->cpu_state_.time = s_time;
+ cpu->cpu_state = &cpu->cpu_state_;
+}
diff --git a/apps/codecs/libgme/hes_emu.c b/apps/codecs/libgme/hes_emu.c
index a428bee..8ddbb9d 100644
--- a/apps/codecs/libgme/hes_emu.c
+++ b/apps/codecs/libgme/hes_emu.c
@@ -21,28 +21,14 @@ int const vdp_mask = 0x02;
int const i_flag_mask = 0x04;
int const unmapped = 0xFF;
-long const period_60hz = 262 * 455L; // scanlines * clocks per scanline
-
-int const stereo = 2; // number of channels for stereo
-int const silence_max = 6; // seconds
-int const silence_threshold = 0x10;
-long const fade_block_size = 512;
-int const fade_shift = 8; // fade ends with gain at 1.0 / (1 << fade_shift)
+int const period_60hz = 262 * 455; // scanlines * clocks per scanline
const char gme_wrong_file_type [] = "Wrong file type for this emulator";
static void clear_track_vars( struct Hes_Emu* this )
{
- this->current_track_ = -1;
- this->out_time = 0;
- this->emu_time = 0;
- this->emu_track_ended_ = true;
- this->track_ended = true;
- this->fade_start = (blargg_long)(LONG_MAX / 2 + 1);
- this->fade_step = 1;
- this->silence_time = 0;
- this->silence_count = 0;
- this->buf_remain = 0;
+ this->current_track_ = -1;
+ track_stop( &this->track_filter );
}
void Hes_init( struct Hes_Emu* this )
@@ -52,15 +38,12 @@ void Hes_init( struct Hes_Emu* this )
this->tempo_ = (int)(FP_ONE_TEMPO);
// defaults
- this->max_initial_silence = 2;
- this->ignore_silence = false;
-
- // Unload
- this->voice_count_ = 0;
- clear_track_vars( this );
+ this->tfilter = *track_get_setup( &this->track_filter );
+ this->tfilter.max_initial = 2;
+ this->tfilter.lookahead = 6;
+ this->track_filter.silence_ignored_ = false;
this->timer.raw_load = 0;
- this->silence_lookahead = 6;
Sound_set_gain( this, (int)(FP_ONE_GAIN*1.11) );
Rom_init( &this->rom, 0x2000 );
@@ -71,6 +54,11 @@ void Hes_init( struct Hes_Emu* this )
/* Set default track count */
this->track_count = 255;
+
+ // clears fields
+ this->voice_count_ = 0;
+ this->voice_types_ = 0;
+ clear_track_vars( this );
}
static blargg_err_t check_hes_header( void const* header )
@@ -82,10 +70,12 @@ static blargg_err_t check_hes_header( void const* header )
// Setup
-blargg_err_t Hes_load( struct Hes_Emu* this, void* data, long size )
+blargg_err_t Hes_load_mem( struct Hes_Emu* this, void* data, long size )
{
// Unload
this->voice_count_ = 0;
+ this->track_count = 255;
+ this->m3u.size = 0;
clear_track_vars( this );
assert( offsetof (struct header_t,unused [4]) == header_size );
@@ -106,15 +96,15 @@ blargg_err_t Hes_load( struct Hes_Emu* this, void* data, long size )
// many files have bad sizes in the only block, so it's simpler to
// just try to load the damn data as best as possible.
- long addr = get_le32( this->header.addr );
- /* long rom_size = get_le32( this->header.size ); */
- long const rom_max = 0x100000;
- if ( addr & ~(rom_max - 1) )
+ int addr = get_le32( this->header.addr );
+ /* int rom_size = get_le32( this->header.size ); */
+ int const rom_max = 0x100000;
+ if ( (unsigned) addr >= (unsigned) rom_max )
{
/* warning( "Invalid address" ); */
addr &= rom_max - 1;
}
- /* if ( (unsigned long) (addr + size) > (unsigned long) rom_max )
+ /* if ( (unsigned) (addr + size) > (unsigned) rom_max )
warning( "Invalid size" );
if ( rom_size != rom.file_size() )
@@ -130,6 +120,10 @@ blargg_err_t Hes_load( struct Hes_Emu* this, void* data, long size )
Rom_set_addr( &this->rom, addr );
this->voice_count_ = osc_count + adpcm_osc_count;
+ static int const types [osc_count + adpcm_osc_count] = {
+ wave_type+0, wave_type+1, wave_type+2, wave_type+3, mixed_type+0, mixed_type+1, mixed_type+2
+ };
+ this->voice_types_ = types;
Apu_volume( &this->apu, this->gain_ );
Adpcm_volume( &this->adpcm, this->gain_ );
@@ -137,21 +131,17 @@ blargg_err_t Hes_load( struct Hes_Emu* this, void* data, long size )
// Setup buffer
this->clock_rate_ = 7159091;
Buffer_clock_rate( &this->stereo_buf, 7159091 );
+ RETURN_ERR( Buffer_set_channel_count( &this->stereo_buf, this->voice_count_, this->voice_types_ ) );
this->buf_changed_count = Buffer_channels_changed_count( &this->stereo_buf );
Sound_set_tempo( this, this->tempo_ );
Sound_mute_voices( this, this->mute_mask_ );
- // Reset track count
- this->track_count = 255;
- this->m3u.size = 0;
return 0;
}
-
// Emulation
-void recalc_timer_load( struct Hes_Emu* this );
void recalc_timer_load( struct Hes_Emu* this )
{
this->timer.load = this->timer.raw_load * this->timer_base + 1;
@@ -159,9 +149,25 @@ void recalc_timer_load( struct Hes_Emu* this )
// Hardware
-void irq_changed( struct Hes_Emu* this );
-void run_until( struct Hes_Emu* this, hes_time_t present );
-void Cpu_write_vdp( struct Hes_Emu* this, int addr, int data )
+void run_until( struct Hes_Emu* this, hes_time_t present )
+{
+ while ( this->vdp.next_vbl < present )
+ this->vdp.next_vbl += this->play_period;
+
+ hes_time_t elapsed = present - this->timer.last_time;
+ if ( elapsed > 0 )
+ {
+ if ( this->timer.enabled )
+ {
+ this->timer.count -= elapsed;
+ if ( this->timer.count <= 0 )
+ this->timer.count += this->timer.load;
+ }
+ this->timer.last_time = present;
+ }
+}
+
+void write_vdp( struct Hes_Emu* this, int addr, int data )
{
switch ( addr )
{
@@ -178,77 +184,33 @@ void Cpu_write_vdp( struct Hes_Emu* this, int addr, int data )
this->vdp.control = data;
irq_changed( this );
}
- else
+ /* else
{
- dprintf( "VDP not supported: $%02X <- $%02X\n", this->vdp.latch, data );
- }
+ dprintf( "VDP not supported: $%02X <- $%02X\n", vdp.latch, data );
+ } */
break;
case 3:
- dprintf( "VDP MSB not supported: $%02X <- $%02X\n", this->vdp.latch, data );
+ /* dprintf( "VDP MSB not supported: $%02X <- $%02X\n", vdp.latch, data ); */
break;
}
}
-int Cpu_done( struct Hes_Emu* this )
-{
- check( time() >= end_time() ||
- (!(r.status & i_flag_mask) && time() >= irq_time()) );
-
- if ( !(this->cpu.r.status & i_flag_mask) )
- {
- hes_time_t present = Cpu_time( &this->cpu );
-
- if ( this->irq.timer <= present && !(this->irq.disables & timer_mask) )
- {
- this->timer.fired = true;
- this->irq.timer = (hes_time_t)future_hes_time;
- irq_changed( this ); // overkill, but not worth writing custom code
- #if defined (GME_FRAME_HOOK_DEFINED)
- {
- unsigned const threshold = period_60hz / 30;
- unsigned long elapsed = present - last_frame_hook;
- if ( elapsed - period_60hz + threshold / 2 < threshold )
- {
- last_frame_hook = present;
- GME_FRAME_HOOK( this );
- }
- }
- #endif
- return 0x0A;
- }
-
- if ( this->irq.vdp <= present && !(this->irq.disables & vdp_mask) )
- {
- // work around for bugs with music not acknowledging VDP
- //run_until( present );
- //irq.vdp = future_hes_time;
- //irq_changed();
- #if defined(GME_FRAME_HOOK_DEFINED)
- last_frame_hook = present;
- GME_FRAME_HOOK( this );
- #endif
- return 0x08;
- }
- }
- return 0;
-}
-
-void Emu_cpu_write( struct Hes_Emu* this, hes_addr_t addr, int data )
+void write_mem_( struct Hes_Emu* this, hes_addr_t addr, int data )
{
hes_time_t time = Cpu_time( &this->cpu );
- if ( (unsigned) (addr - start_addr) <= end_addr - start_addr )
+ if ( (unsigned) (addr - apu_io_addr) < apu_io_size )
{
- GME_APU_HOOK( this, addr - apu.start_addr, data );
- // avoid going way past end when a long block xfer is writing to I/O space
- hes_time_t t = min( time, this->cpu.end_time + 8 );
+ // Avoid going way past end when a long block xfer is writing to I/O space.
+ // Not a problem for other registers below because they don't write to
+ // Blip_Buffer.
+ hes_time_t t = min( time, Cpu_end_time( &this->cpu ) + 8 );
Apu_write_data( &this->apu, t, addr, data );
return;
}
-
- if ( (unsigned) (addr - io_addr) < io_size )
+ if ( (unsigned) (addr - adpcm_io_addr) < adpcm_io_size )
{
- hes_time_t t = min( time, this->cpu.end_time + 6 );
+ hes_time_t t = min( time, Cpu_end_time( &this->cpu ) + 6 );
Adpcm_write_data( &this->adpcm, t, addr, data );
return;
}
@@ -258,7 +220,7 @@ void Emu_cpu_write( struct Hes_Emu* this, hes_addr_t addr, int data )
case 0x0000:
case 0x0002:
case 0x0003:
- Cpu_write_vdp( this, addr, data );
+ write_vdp( this, addr, data );
return;
case 0x0C00: {
@@ -282,11 +244,8 @@ void Emu_cpu_write( struct Hes_Emu* this, hes_addr_t addr, int data )
case 0x1402:
run_until( this, time );
this->irq.disables = data;
-
- // flag questionable values
- if ( (data & 0xF8) && (data & 0xF8) != 0xF8 ) {
- dprintf( "Int mask: $%02X\n", data );
- }
+ /* if ( (data & 0xF8) && (data & 0xF8) != 0xF8 ) // flag questionable values
+ dprintf( "Int mask: $%02X\n", data ); */
break;
case 0x1403:
@@ -305,7 +264,7 @@ void Emu_cpu_write( struct Hes_Emu* this, hes_addr_t addr, int data )
return;
default:
- dprintf( "unmapped write $%04X <- $%02X\n", addr, data );
+ /* dprintf( "unmapped write $%04X <- $%02X\n", addr, data ); */
return;
#endif
}
@@ -313,7 +272,7 @@ void Emu_cpu_write( struct Hes_Emu* this, hes_addr_t addr, int data )
irq_changed( this );
}
-int Emu_cpu_read( struct Hes_Emu* this, hes_addr_t addr )
+int read_mem_( struct Hes_Emu* this, hes_addr_t addr )
{
hes_time_t time = Cpu_time( &this->cpu );
addr &= page_size - 1;
@@ -322,21 +281,21 @@ int Emu_cpu_read( struct Hes_Emu* this, hes_addr_t addr )
case 0x0000:
if ( this->irq.vdp > time )
return 0;
- this->irq.vdp = (hes_time_t)future_hes_time;
+ this->irq.vdp = future_time;
run_until( this, time );
irq_changed( this );
return 0x20;
- case 0x0002:
+ /* case 0x0002:
case 0x0003:
dprintf( "VDP read not supported: %d\n", addr );
- return 0;
+ return 0; */
case 0x0C01:
//return timer.enabled; // TODO: remove?
case 0x0C00:
run_until( this, time );
- dprintf( "Timer count read\n" );
+ /* dprintf( "Timer count read\n" ); */
return (unsigned) (this->timer.count - 1) / this->timer_base;
case 0x1402:
@@ -349,7 +308,7 @@ int Emu_cpu_read( struct Hes_Emu* this, hes_addr_t addr )
if ( this->irq.vdp <= time ) status |= vdp_mask;
return status;
}
-
+
case 0x180A:
case 0x180B:
case 0x180C:
@@ -358,71 +317,75 @@ int Emu_cpu_read( struct Hes_Emu* this, hes_addr_t addr )
#ifndef NDEBUG
case 0x1000: // I/O port
- // case 0x180C: // CD-ROM
- // case 0x180D:
+ //case 0x180C: // CD-ROM
+ //case 0x180D:
break;
- default:
- dprintf( "unmapped read $%04X\n", addr );
+ /* default:
+ dprintf( "unmapped read $%04X\n", addr ); */
#endif
}
return unmapped;
}
-// see hes_cpu_io.h for core read/write functions
-
-// Emulation
-
-void run_until( struct Hes_Emu* this, hes_time_t present )
-{
- while ( this->vdp.next_vbl < present )
- this->vdp.next_vbl += this->play_period;
-
- hes_time_t elapsed = present - this->timer.last_time;
- if ( elapsed > 0 )
- {
- if ( this->timer.enabled )
- {
- this->timer.count -= elapsed;
- if ( this->timer.count <= 0 )
- this->timer.count += this->timer.load;
- }
- this->timer.last_time = present;
- }
-}
-
void irq_changed( struct Hes_Emu* this )
{
hes_time_t present = Cpu_time( &this->cpu );
if ( this->irq.timer > present )
{
- this->irq.timer = (hes_time_t)future_hes_time;
+ this->irq.timer = future_time;
if ( this->timer.enabled && !this->timer.fired )
this->irq.timer = present + this->timer.count;
}
if ( this->irq.vdp > present )
{
- this->irq.vdp = (hes_time_t)future_hes_time;
+ this->irq.vdp = future_time;
if ( this->vdp.control & 0x08 )
this->irq.vdp = this->vdp.next_vbl;
}
- hes_time_t time = (hes_time_t)future_hes_time;
+ hes_time_t time = future_time;
if ( !(this->irq.disables & timer_mask) ) time = this->irq.timer;
if ( !(this->irq.disables & vdp_mask) ) time = min( time, this->irq.vdp );
- // Set cpu irq time
- this->cpu.state->time += Cpu_update_end_time( &this->cpu, this->cpu.r.status,
- this->cpu.end_time, (this->cpu.irq_time = time) );
+ Cpu_set_irq_time( &this->cpu, time );
}
-static void adjust_time( blargg_long* time, hes_time_t delta );
-static void adjust_time( blargg_long* time, hes_time_t delta )
+int cpu_done( struct Hes_Emu* this )
{
- if ( *time < (blargg_long)future_hes_time )
+ check( Cpu_time( &this->cpu ) >= Cpu_end_time( &this->cpu ) ||
+ (!(this->cpu.r.flags & i_flag_mask) && Cpu_time( &this->cpu ) >= Cpu_irq_time( &this->cpu )) );
+
+ if ( !(this->cpu.r.flags & i_flag_mask) )
+ {
+ hes_time_t present = Cpu_time( &this->cpu );
+
+ if ( this->irq.timer <= present && !(this->irq.disables & timer_mask) )
+ {
+ this->timer.fired = true;
+ this->irq.timer = future_time;
+ irq_changed( this ); // overkill, but not worth writing custom code
+ return 0x0A;
+ }
+
+ if ( this->irq.vdp <= present && !(this->irq.disables & vdp_mask) )
+ {
+ // work around for bugs with music not acknowledging VDP
+ //run_until( present );
+ //irq.vdp = cpu.future_time;
+ //irq_changed();
+ return 0x08;
+ }
+ }
+ return -1;
+}
+
+static void adjust_time( hes_time_t* time, hes_time_t delta )
+{
+ if ( *time < future_time )
{
*time -= delta;
if ( *time < 0 )
@@ -430,15 +393,13 @@ static void adjust_time( blargg_long* time, hes_time_t delta )
}
}
-blargg_err_t run_clocks( struct Hes_Emu* this, blip_time_t* duration_ );
-blargg_err_t run_clocks( struct Hes_Emu* this, blip_time_t* duration_ )
+blargg_err_t end_frame( struct Hes_Emu* this, hes_time_t duration )
{
- blip_time_t duration = *duration_; // cache
+ /* if ( run_cpu( this, duration ) )
+ warning( "Emulation error (illegal instruction)" ); */
+ run_cpu( this, duration );
- Cpu_run( this, duration );
- /* warning( "Emulation error (illegal instruction)" ); */
-
- check( time() >= duration );
+ check( Cpu_time( &this->cpu ) >= duration );
//check( time() - duration < 20 ); // Txx instruction could cause going way over
run_until( this, duration );
@@ -446,15 +407,7 @@ blargg_err_t run_clocks( struct Hes_Emu* this, blip_time_t* duration_ )
// end time frame
this->timer.last_time -= duration;
this->vdp.next_vbl -= duration;
- #if defined (GME_FRAME_HOOK_DEFINED)
- last_frame_hook -= *duration;
- #endif
-
- // End cpu frame
- this->cpu.state_.base -= duration;
- if ( this->cpu.irq_time < (hes_time_t)future_hes_time ) this->cpu.irq_time -= duration;
- if ( this->cpu.end_time < (hes_time_t)future_hes_time ) this->cpu.end_time -= duration;
-
+ Cpu_end_frame( &this->cpu, duration );
adjust_time( &this->irq.timer, duration );
adjust_time( &this->irq.vdp, duration );
Apu_end_frame( &this->apu, duration );
@@ -463,24 +416,31 @@ blargg_err_t run_clocks( struct Hes_Emu* this, blip_time_t* duration_ )
return 0;
}
-blargg_err_t play_( struct Hes_Emu* this, long count, sample_t* out );
-blargg_err_t play_( struct Hes_Emu* this, long count, sample_t* out )
+blargg_err_t run_clocks( struct Hes_Emu* this, blip_time_t* duration_ )
+{
+ return end_frame( this, *duration_ );
+}
+
+blargg_err_t play_( void *emu, int count, sample_t out [] )
{
- long remain = count;
+ struct Hes_Emu* this = (struct Hes_Emu*) emu;
+
+ int remain = count;
while ( remain )
{
+ Buffer_disable_immediate_removal( &this->stereo_buf );
remain -= Buffer_read_samples( &this->stereo_buf, &out [count - remain], remain );
if ( remain )
{
if ( this->buf_changed_count != Buffer_channels_changed_count( &this->stereo_buf ) )
{
this->buf_changed_count = Buffer_channels_changed_count( &this->stereo_buf );
+
// Remute voices
Sound_mute_voices( this, this->mute_mask_ );
}
-
int msec = Buffer_length( &this->stereo_buf );
- blip_time_t clocks_emulated = (blargg_long) msec * this->clock_rate_ / 1000;
+ blip_time_t clocks_emulated = msec * this->clock_rate_ / 1000 - 100;
RETURN_ERR( run_clocks( this, &clocks_emulated ) );
assert( clocks_emulated );
Buffer_end_frame( &this->stereo_buf, clocks_emulated );
@@ -489,10 +449,9 @@ blargg_err_t play_( struct Hes_Emu* this, long count, sample_t* out )
return 0;
}
-
// Music emu
-blargg_err_t Hes_set_sample_rate( struct Hes_Emu* this, long rate )
+blargg_err_t Hes_set_sample_rate( struct Hes_Emu* this, int rate )
{
require( !this->sample_rate_ ); // sample rate can't be changed once set
Buffer_init( &this->stereo_buf );
@@ -502,6 +461,8 @@ blargg_err_t Hes_set_sample_rate( struct Hes_Emu* this, long rate )
Buffer_bass_freq( &this->stereo_buf, 60 );
this->sample_rate_ = rate;
+ RETURN_ERR( track_init( &this->track_filter, this ) );
+ this->tfilter.max_silence = 6 * stereo * this->sample_rate_;
return 0;
}
@@ -521,7 +482,7 @@ void Sound_mute_voices( struct Hes_Emu* this, int mask )
this->mute_mask_ = mask;
// Set adpcm voice
- struct channel_t ch = Buffer_channel( &this->stereo_buf );
+ struct channel_t ch = Buffer_channel( &this->stereo_buf, this->voice_count_ );
if ( mask & (1 << this->voice_count_ ) )
Adpcm_set_output( &this->adpcm, 0, 0, 0, 0 );
else
@@ -536,7 +497,8 @@ void Sound_mute_voices( struct Hes_Emu* this, int mask )
Apu_osc_output( &this->apu, i, 0, 0, 0 );
}
else
- {
+ {
+ struct channel_t ch = Buffer_channel( &this->stereo_buf, i );
assert( (ch.center && ch.left && ch.right) ||
(!ch.center && !ch.left && !ch.right) ); // all or nothing
Apu_osc_output( &this->apu, i, ch.center, ch.left, ch.right );
@@ -557,7 +519,6 @@ void Sound_set_tempo( struct Hes_Emu* this, int t )
this->tempo_ = t;
}
-void fill_buf( struct Hes_Emu* this );
blargg_err_t Hes_start_track( struct Hes_Emu* this, int track )
{
clear_track_vars( this );
@@ -572,7 +533,7 @@ blargg_err_t Hes_start_track( struct Hes_Emu* this, int track )
Buffer_clear( &this->stereo_buf );
- memset( this->cpu.ram, 0, sizeof this->cpu.ram ); // some HES music relies on zero fill
+ memset( this->ram, 0, sizeof this->ram ); // some HES music relies on zero fill
memset( this->sgx, 0, sizeof this->sgx );
Apu_reset( &this->apu );
@@ -581,12 +542,12 @@ blargg_err_t Hes_start_track( struct Hes_Emu* this, int track )
unsigned i;
for ( i = 0; i < sizeof this->header.banks; i++ )
- Cpu_set_mmr( this, i, this->header.banks [i] );
- Cpu_set_mmr( this, page_count, 0xFF ); // unmapped beyond end of address space
+ set_mmr( this, i, this->header.banks [i] );
+ set_mmr( this, page_count, 0xFF ); // unmapped beyond end of address space
this->irq.disables = timer_mask | vdp_mask;
- this->irq.timer = (hes_time_t)future_hes_time;
- this->irq.vdp = (hes_time_t)future_hes_time;
+ this->irq.timer = future_time;
+ this->irq.vdp = future_time;
this->timer.enabled = false;
this->timer.raw_load= 0x80;
@@ -598,280 +559,86 @@ blargg_err_t Hes_start_track( struct Hes_Emu* this, int track )
this->vdp.control = 0;
this->vdp.next_vbl = 0;
- this->cpu.ram [0x1FF] = (idle_addr - 1) >> 8;
- this->cpu.ram [0x1FE] = (idle_addr - 1) & 0xFF;
+ this->ram [0x1FF] = (idle_addr - 1) >> 8;
+ this->ram [0x1FE] = (idle_addr - 1) & 0xFF;
this->cpu.r.sp = 0xFD;
this->cpu.r.pc = get_le16( this->header.init_addr );
this->cpu.r.a = track;
recalc_timer_load( this );
- this->last_frame_hook = 0;
- this->emu_track_ended_ = false;
- this->track_ended = false;
+ // convert filter times to samples
+ struct setup_t s = this->tfilter;
+ s.max_initial *= this->sample_rate_ * stereo;
+ #ifdef GME_DISABLE_SILENCE_LOOKAHEAD
+ s.lookahead = 1;
+ #endif
+ track_setup( &this->track_filter, &s );
- if ( !this->ignore_silence )
- {
- // play until non-silence or end of track
- long end;
- for ( end =this-> max_initial_silence * stereo * this->sample_rate_; this->emu_time < end; )
- {
- fill_buf( this );
- if ( this->buf_remain | (int) this->emu_track_ended_ )
- break;
- }
-
- this->emu_time = this->buf_remain;
- this->out_time = 0;
- this->silence_time = 0;
- this->silence_count = 0;
- }
- /* return track_ended() ? warning() : 0; */
- return 0;
+ return track_start( &this->track_filter );
}
// Tell/Seek
-static blargg_long msec_to_samples( blargg_long msec, long sample_rate )
+static int msec_to_samples( int msec, int sample_rate )
{
- blargg_long sec = msec / 1000;
+ int sec = msec / 1000;
msec -= sec * 1000;
return (sec * sample_rate + msec * sample_rate / 1000) * stereo;
}
-long Track_tell( struct Hes_Emu* this )
+int Track_tell( struct Hes_Emu* this )
{
- blargg_long rate = this->sample_rate_ * stereo;
- blargg_long sec = this->out_time / rate;
- return sec * 1000 + (this->out_time - sec * rate) * 1000 / rate;
+ int rate = this->sample_rate_ * stereo;
+ int sec = track_sample_count( &this->track_filter ) / rate;
+ return sec * 1000 + (track_sample_count( &this->track_filter ) - sec * rate) * 1000 / rate;
}
-blargg_err_t Track_seek( struct Hes_Emu* this, long msec )
+blargg_err_t Track_seek( struct Hes_Emu* this, int msec )
{
- blargg_long time = msec_to_samples( msec, this->sample_rate_ );
- if ( time < this->out_time )
- RETURN_ERR( Hes_start_track( this, this->current_track_ ) );
- return Track_skip( this, time - this->out_time );
+ int time = msec_to_samples( msec, this->sample_rate_ );
+ if ( time < track_sample_count( &this->track_filter ) )
+ RETURN_ERR( Hes_start_track( this, this->current_track_ ) );
+ return Track_skip( this, time - track_sample_count( &this->track_filter ) );
}
-blargg_err_t skip_( struct Hes_Emu* this, long count );
-blargg_err_t skip_( struct Hes_Emu* this, long count )
+blargg_err_t skip_( void* emu, int count )
{
+ struct Hes_Emu* this = (struct Hes_Emu*) emu;
+
// for long skip, mute sound
- const long threshold = 30000;
+ const int threshold = 32768;
if ( count > threshold )
{
int saved_mute = this->mute_mask_;
Sound_mute_voices( this, ~0 );
-
- while ( count > threshold / 2 && !this->emu_track_ended_ )
- {
- RETURN_ERR( play_( this, buf_size, this->buf ) );
- count -= buf_size;
- }
-
- Sound_mute_voices( this, saved_mute );
- }
-
- while ( count && !this->emu_track_ended_ )
- {
- long n = buf_size;
- if ( n > count )
- n = count;
- count -= n;
- RETURN_ERR( play_( this, n, this->buf ) );
- }
- return 0;
-}
-blargg_err_t Track_skip( struct Hes_Emu* this, long count )
-{
- require( this->current_track_ >= 0 ); // start_track() must have been called already
- this->out_time += count;
-
- // remove from silence and buf first
- {
- long n = min( count, this->silence_count );
- this->silence_count -= n;
- count -= n;
-
- n = min( count, this->buf_remain );
- this->buf_remain -= n;
+ int n = count - threshold/2;
+ n &= ~(2048-1); // round to multiple of 2048
count -= n;
- }
-
- if ( count && !this->emu_track_ended_ )
- {
- this->emu_time += count;
-
- // End track if error
- if ( skip_( this, count ) )
- this->emu_track_ended_ = true;
- }
-
- if ( !(this->silence_count | this->buf_remain) ) // caught up to emulator, so update track ended
- this->track_ended |= this->emu_track_ended_;
-
- return 0;
-}
+ RETURN_ERR( skippy_( &this->track_filter, n ) );
-
-
-// Fading
-
-void Track_set_fade( struct Hes_Emu* this, long start_msec, long length_msec )
-{
- this->fade_step = this->sample_rate_ * length_msec / (fade_block_size * fade_shift * 1000 / stereo);
- this->fade_start = msec_to_samples( start_msec, this->sample_rate_ );
-}
-
-// unit / pow( 2.0, (double) x / step )
-static int int_log( blargg_long x, int step, int unit );
-static int int_log( blargg_long x, int step, int unit )
-{
- int shift = x / step;
- int fraction = (x - shift * step) * unit / step;
- return ((unit - fraction) + (fraction >> 1)) >> shift;
-}
-
-void handle_fade( struct Hes_Emu* this, long out_count, sample_t* out );
-void handle_fade( struct Hes_Emu* this, long out_count, sample_t* out )
-{
- int i;
- for ( i = 0; i < out_count; i += fade_block_size )
- {
- int const shift = 14;
- int const unit = 1 << shift;
- int gain = int_log( (this->out_time + i - this->fade_start) / fade_block_size,
- this->fade_step, unit );
- if ( gain < (unit >> fade_shift) )
- this->track_ended = this->emu_track_ended_ = true;
-
- sample_t* io = &out [i];
- int count;
- for ( count = min( fade_block_size, out_count - i ); count; --count )
- {
- *io = (sample_t) ((*io * gain) >> shift);
- ++io;
- }
+ Sound_mute_voices( this, saved_mute );
}
-}
-
-// Silence detection
-void emu_play( struct Hes_Emu* this, long count, sample_t* out );
-void emu_play( struct Hes_Emu* this, long count, sample_t* out )
-{
- check( current_track_ >= 0 );
- this->emu_time += count;
- if ( this->current_track_ >= 0 && !this->emu_track_ended_ ) {
-
- // End track if error
- if ( play_( this, count, out ) )
- this->emu_track_ended_ = true;
- }
- else
- memset( out, 0, count * sizeof *out );
+ return skippy_( &this->track_filter, count );
}
-// number of consecutive silent samples at end
-static long count_silence( sample_t* begin, long size );
-static long count_silence( sample_t* begin, long size )
+blargg_err_t Track_skip( struct Hes_Emu* this, int count )
{
- sample_t first = *begin;
- *begin = silence_threshold; // sentinel
- sample_t* p = begin + size;
- while ( (unsigned) (*--p + silence_threshold / 2) <= (unsigned) silence_threshold ) { }
- *begin = first;
- return size - (p - begin);
+ require( this->current_track_ >= 0 ); // start_track() must have been called already
+ return track_skip( &this->track_filter, count );
}
-// fill internal buffer and check it for silence
-void fill_buf( struct Hes_Emu* this )
+void Track_set_fade( struct Hes_Emu* this, int start_msec, int length_msec )
{
- assert( !this->buf_remain );
- if ( !this->emu_track_ended_ )
- {
- emu_play( this, buf_size, this->buf );
- long silence = count_silence( this->buf, buf_size );
- if ( silence < buf_size )
- {
- this->silence_time = this->emu_time - silence;
- this->buf_remain = buf_size;
- return;
- }
- }
- this->silence_count += buf_size;
+ track_set_fade( &this->track_filter, msec_to_samples( start_msec, this->sample_rate_ ),
+ length_msec * this->sample_rate_ / (1000 / stereo) );
}
-blargg_err_t Hes_play( struct Hes_Emu* this, long out_count, sample_t* out )
+blargg_err_t Hes_play( struct Hes_Emu* this, int out_count, sample_t* out )
{
- if ( this->track_ended )
- {
- memset( out, 0, out_count * sizeof *out );
- }
- else
- {
- require( this->current_track_ >= 0 );
- require( out_count % stereo == 0 );
-
- assert( this->emu_time >= this->out_time );
-
- // prints nifty graph of how far ahead we are when searching for silence
- //dprintf( "%*s \n", int ((emu_time - out_time) * 7 / sample_rate()), "*" );
-
- long pos = 0;
- if ( this->silence_count )
- {
- // during a run of silence, run emulator at >=2x speed so it gets ahead
- long ahead_time = this->silence_lookahead * (this->out_time + out_count - this->silence_time) + this->silence_time;
- while ( this->emu_time < ahead_time && !(this->buf_remain | this->emu_track_ended_) )
- fill_buf( this );
-
- // fill with silence
- pos = min( this->silence_count, out_count );
- memset( out, 0, pos * sizeof *out );
- this->silence_count -= pos;
-
- if ( this->emu_time - this->silence_time > silence_max * stereo * this->sample_rate_ )
- {
- this->track_ended = this->emu_track_ended_ = true;
- this->silence_count = 0;
- this->buf_remain = 0;
- }
- }
-
- if ( this->buf_remain )
- {
- // empty silence buf
- long n = min( this->buf_remain, out_count - pos );
- memcpy( &out [pos], this->buf + (buf_size - this->buf_remain), n * sizeof *out );
- this->buf_remain -= n;
- pos += n;
- }
-
- // generate remaining samples normally
- long remain = out_count - pos;
- if ( remain )
- {
- emu_play( this, remain, out + pos );
- this->track_ended |= this->emu_track_ended_;
-
- if ( !this->ignore_silence || this->out_time > this->fade_start )
- {
- // check end for a new run of silence
- long silence = count_silence( out + pos, remain );
- if ( silence < remain )
- this->silence_time = this->emu_time - silence;
-
- if ( this->emu_time - this->silence_time >= buf_size )
- fill_buf( this ); // cause silence detection on next play()
- }
- }
-
- if ( this->out_time > this->fade_start )
- handle_fade( this, out_count, out );
- }
- this->out_time += out_count;
- return 0;
+ require( this->current_track_ >= 0 );
+ require( out_count % stereo == 0 );
+ return track_play( &this->track_filter, out_count, out );
}
diff --git a/apps/codecs/libgme/hes_emu.h b/apps/codecs/libgme/hes_emu.h
index 0dcd29a..a1dd048 100644
--- a/apps/codecs/libgme/hes_emu.h
+++ b/apps/codecs/libgme/hes_emu.h
@@ -12,88 +12,67 @@
#include "hes_apu_adpcm.h"
#include "hes_cpu.h"
#include "m3u_playlist.h"
-
-typedef short sample_t;
-
-enum { buf_size = 2048 };
+#include "track_filter.h"
// HES file header
+enum { info_offset = 0x20 };
enum { header_size = 0x20 };
struct header_t
{
- byte tag [4];
+ byte tag [4];
byte vers;
byte first_track;
byte init_addr [2];
- byte banks [8];
- byte data_tag [4];
- byte size [4];
- byte addr [4];
- byte unused [4];
+ byte banks [8];
+ byte data_tag [4];
+ byte size [4];
+ byte addr [4];
+ byte unused [4];
};
struct timer_t {
hes_time_t last_time;
- blargg_long count;
- blargg_long load;
- int raw_load;
- byte enabled;
- byte fired;
+ int count;
+ int load;
+ int raw_load;
+ byte enabled;
+ byte fired;
};
struct vdp_t {
hes_time_t next_vbl;
- byte latch;
- byte control;
+ byte latch;
+ byte control;
};
struct irq_t {
hes_time_t timer;
hes_time_t vdp;
- byte disables;
+ byte disables;
};
-
struct Hes_Emu {
hes_time_t play_period;
- hes_time_t last_frame_hook;
int timer_base;
struct timer_t timer;
- struct vdp_t vdp;
- struct irq_t irq;
+ struct vdp_t vdp;
+ struct irq_t irq;
// Sound
- long clock_rate_;
- long sample_rate_;
+ int clock_rate_;
+ int sample_rate_;
unsigned buf_changed_count;
int voice_count_;
+ int const* voice_types_;
+ int mute_mask_;
int tempo_;
int gain_;
// track-specific
byte track_count;
- volatile bool track_ended;
int current_track_;
- 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
-
- // fading
- blargg_long fade_start;
- int fade_step;
-
- // silence detection
- // Disable automatic end-of-track detection and skipping of silence at beginning
- bool ignore_silence;
-
- int max_initial_silence;
- int mute_mask_;
- int silence_lookahead; // speed to run emulator when looking ahead for silence
- long silence_time; // number of samples where most recent silence began
- long silence_count; // number of samples of silence to play before using buf
- long buf_remain; // number of samples left in silence buffer
// Larger files at the end
// Header for currently loaded file
@@ -102,19 +81,22 @@ struct Hes_Emu {
// M3u Playlist
struct M3u_Playlist m3u;
+ struct setup_t tfilter;
+ struct Track_Filter track_filter;
+
// Hes Cpu
- byte* write_pages [page_count + 1]; // 0 if unmapped or I/O space
struct Hes_Cpu cpu;
+ struct Rom_Data rom;
struct Hes_Apu apu;
struct Hes_Apu_Adpcm adpcm;
- struct Stereo_Buffer stereo_buf;
- sample_t buf [buf_size];
+ struct Multi_Buffer stereo_buf;
// rom & ram
- struct Rom_Data rom;
- byte sgx [3 * page_size + cpu_padding];
+ byte* write_pages [page_count + 1]; // 0 if unmapped or I/O space
+ byte ram [page_size];
+ byte sgx [3 * page_size + cpu_padding];
};
@@ -126,36 +108,48 @@ void Hes_init( struct Hes_Emu* this );
void Hes_stop( struct Hes_Emu* this );
// Loads a file from memory
-blargg_err_t Hes_load( struct Hes_Emu* this, void* data, long size );
+blargg_err_t Hes_load_mem( struct Hes_Emu* this, void* data, long size );
// Set output sample rate. Must be called only once before loading file.
-blargg_err_t Hes_set_sample_rate( struct Hes_Emu* this, long sample_rate );
+blargg_err_t Hes_set_sample_rate( struct Hes_Emu* this, int sample_rate );
// Start a track, where 0 is the first track. Also clears warning string.
blargg_err_t Hes_start_track( struct Hes_Emu* this, int );
// Generate 'count' samples info 'buf'. Output is in stereo. Any emulation
// errors set warning string, and major errors also end track.
-blargg_err_t Hes_play( struct Hes_Emu* this, long count, sample_t* buf );
+blargg_err_t Hes_play( struct Hes_Emu* this, int count, sample_t* buf );
// Track status/control
// Number of milliseconds (1000 msec = 1 second) played since ning of track
-long Track_tell( struct Hes_Emu* this );
+int Track_tell( struct Hes_Emu* this );
// Seek to new time in track. Seeking backwards or far forward can take a while.
-blargg_err_t Track_seek( struct Hes_Emu* this, long msec );
+blargg_err_t Track_seek( struct Hes_Emu* this, int msec );
// Skip n samples
-blargg_err_t Track_skip( struct Hes_Emu* this, long n );
+blargg_err_t Track_skip( struct Hes_Emu* this, int n );
// Set start time and length of track fade out. Once fade ends track_ended() returns
// true. Fade time can be changed while track is playing.
-void Track_set_fade( struct Hes_Emu* this, long start_msec, long length_msec );
+void Track_set_fade( struct Hes_Emu* this, int start_msec, int length_msec );
+
+// True if a track has reached its end
+static inline bool Track_ended( struct Hes_Emu* this )
+{
+ return track_ended( &this->track_filter );
+}
+
+// Disables automatic end-of-track detection and skipping of silence at beginning
+static inline void Track_ignore_silence( struct Hes_Emu* this, bool disable )
+{
+ this->track_filter.silence_ignored_ = disable;
+}
// Get track length in milliseconds
-static inline long Track_get_length( struct Hes_Emu* this, int n )
+static inline int Track_get_length( struct Hes_Emu* this, int n )
{
- long length = 120 * 1000; /* 2 minutes */
+ int length = 120 * 1000; /* 2 minutes */
if ( (this->m3u.size > 0) && (n < this->m3u.size) ) {
struct entry_t* entry = &this->m3u.entries [n];
length = entry->length;
@@ -185,45 +179,17 @@ static inline void Sound_set_gain( struct Hes_Emu* this, int g )
this->gain_ = g;
}
-
// Emulation (You shouldn't touch these)
-int Cpu_read( struct Hes_Emu* this, hes_addr_t );
-void Cpu_write( struct Hes_Emu* this, hes_addr_t, int );
-void Cpu_write_vdp( struct Hes_Emu* this, int addr, int data );
-int Cpu_done( struct Hes_Emu* this );
-
-int Emu_cpu_read( struct Hes_Emu* this, hes_addr_t );
-void Emu_cpu_write( struct Hes_Emu* this, hes_addr_t, int data );
-
-static inline byte const* Emu_cpu_set_mmr( struct Hes_Emu* this, int page, int bank )
-{
- this->write_pages [page] = 0;
- if ( bank < 0x80 )
- return Rom_at_addr( &this->rom, bank * (blargg_long) page_size );
-
- byte* data = 0;
- switch ( bank )
- {
- case 0xF8:
- data = this->cpu.ram;
- break;
-
- case 0xF9:
- case 0xFA:
- case 0xFB:
- data = &this->sgx [(bank - 0xF9) * page_size];
- break;
-
- default:
- if ( bank != 0xFF ) {
- dprintf( "Unmapped bank $%02X\n", bank );
- }
- return this->rom.unmapped;
- }
-
- this->write_pages [page] = data;
- return data;
-}
+void irq_changed( struct Hes_Emu* this );
+void run_until( struct Hes_Emu* this, hes_time_t );
+bool run_cpu( struct Hes_Emu* this, hes_time_t end );
+int read_mem_( struct Hes_Emu* this, hes_addr_t );
+int read_mem( struct Hes_Emu* this, hes_addr_t );
+void write_mem_( struct Hes_Emu* this, hes_addr_t, int data );
+void write_mem( struct Hes_Emu* this, hes_addr_t, int );
+void write_vdp( struct Hes_Emu* this, int addr, int data );
+void set_mmr( struct Hes_Emu* this, int reg, int bank );
+int cpu_done( struct Hes_Emu* this );
#endif
diff --git a/apps/codecs/libgme/kss_emu.c b/apps/codecs/libgme/kss_emu.c
index 060a5b7..ba80ef6 100644
--- a/apps/codecs/libgme/kss_emu.c
+++ b/apps/codecs/libgme/kss_emu.c
@@ -1,4 +1,4 @@
-// Game_Music_Emu 0.5.5. http://www.slack.net/~ant/
+// Game_Music_Emu 0.6-pre. http://www.slack.net/~ant/
#include "kss_emu.h"
@@ -17,29 +17,14 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
#include "blargg_source.h"
-long const clock_rate = 3579545;
+int const clock_rate = 3579545;
const char gme_wrong_file_type [] = "Wrong file type for this emulator";
-int const stereo = 2; // number of channels for stereo
-int const silence_max = 6; // seconds
-int const silence_threshold = 0x10;
-long const fade_block_size = 512;
-int const fade_shift = 8; // fade ends with gain at 1.0 / (1 << fade_shift)
-
static void clear_track_vars( struct Kss_Emu* this )
{
- this->current_track = -1;
- this->out_time = 0;
- this->emu_time = 0;
- this->emu_track_ended_ = true;
- this->track_ended = true;
- this->fade_start = INT_MAX / 2 + 1;
- this->fade_step = 1;
- this->silence_time = 0;
- this->silence_count = 0;
- this->buf_remain = 0;
- // warning(); // clear warning
+ this->current_track = -1;
+ track_stop( &this->track_filter );
}
static blargg_err_t init_opl_apu( enum opl_type_t type, struct Opl_Apu* out )
@@ -58,17 +43,15 @@ void Kss_init( struct Kss_Emu* this )
this->chip_flags = 0;
// defaults
- this->max_initial_silence = 2;
- this->silence_lookahead = 6;
- this->ignore_silence = false;
-
- this->voice_count = 0;
- clear_track_vars( this );
+ this->tfilter = *track_get_setup( &this->track_filter );
+ this->tfilter.max_initial = 2;
+ this->tfilter.lookahead = 6;
+ this->track_filter.silence_ignored_ = false;
memset( this->unmapped_read, 0xFF, sizeof this->unmapped_read );
// Init all stuff
- Buffer_init( &this->stereo_buffer );
+ Buffer_init( &this->stereo_buf );
Z80_init( &this->cpu );
Rom_init( &this->rom, page_size );
@@ -83,6 +66,10 @@ void Kss_init( struct Kss_Emu* this )
init_opl_apu( type_msxmusic, &this->msx.music );
init_opl_apu( type_msxaudio, &this->msx.audio );
#endif
+
+ this->voice_count = 0;
+ this->voice_types = 0;
+ clear_track_vars( this );
}
// Track info
@@ -96,7 +83,7 @@ static blargg_err_t check_kss_header( void const* header )
// Setup
-static void update_gain( struct Kss_Emu* this )
+static void update_gain_( struct Kss_Emu* this )
{
int g = this->gain;
if ( msx_music_enabled( this ) || msx_audio_enabled( this )
@@ -118,6 +105,15 @@ static void update_gain( struct Kss_Emu* this )
if ( msx_audio_enabled( this ) ) Opl_volume( &this->msx.audio, g );
}
+static void update_gain( struct Kss_Emu* this )
+{
+ if ( this->scc_accessed )
+ {
+ /* dprintf( "SCC accessed\n" ); */
+ update_gain_( this );
+ }
+}
+
blargg_err_t Kss_load_mem( struct Kss_Emu* this, const void* data, long size )
{
/* warning( core.warning() ); */
@@ -176,6 +172,10 @@ blargg_err_t Kss_load_mem( struct Kss_Emu* this, const void* data, long size )
// sms.psg
this->voice_count = sms_osc_count;
+ static int const types [sms_osc_count + opl_osc_count] = {
+ wave_type+1, wave_type+3, wave_type+2, mixed_type+1, wave_type+0
+ };
+ this->voice_types = types;
this->chip_flags |= sms_psg_flag;
// sms.fm
@@ -191,6 +191,10 @@ blargg_err_t Kss_load_mem( struct Kss_Emu* this, const void* data, long size )
// msx.psg
this->voice_count = ay_osc_count;
+ static int const types [ay_osc_count + opl_osc_count] = {
+ wave_type+1, wave_type+3, wave_type+2, wave_type+0
+ };
+ this->voice_types = types;
this->chip_flags |= msx_psg_flag;
/* if ( this->header.device_flags & 0x10 )
@@ -220,21 +224,27 @@ blargg_err_t Kss_load_mem( struct Kss_Emu* this, const void* data, long size )
// msx.scc
this->chip_flags |= msx_scc_flag;
this->voice_count = ay_osc_count + scc_osc_count;
+ static int const types [ay_osc_count + scc_osc_count] = {
+ wave_type+1, wave_type+3, wave_type+2,
+ wave_type+0, wave_type+4, wave_type+5, wave_type+6, wave_type+7,
+ };
+ this->voice_types = types;
}
}
- this->silence_lookahead = 6;
+ this->tfilter.lookahead = 6;
if ( sms_fm_enabled( this ) || msx_music_enabled( this ) || msx_audio_enabled( this ) )
{
if ( !Opl_supported() )
; /* warning( "FM sound not supported" ); */
else
- this->silence_lookahead = 3; // Opl_Apu is really slow
+ this->tfilter.lookahead = 3; // Opl_Apu is really slow
}
this->clock_rate_ = clock_rate;
- Buffer_clock_rate( &this->stereo_buffer, clock_rate );
- this->buf_changed_count = Buffer_channels_changed_count( &this->stereo_buffer );
+ Buffer_clock_rate( &this->stereo_buf, clock_rate );
+ RETURN_ERR( Buffer_set_channel_count( &this->stereo_buf, this->voice_count, this->voice_types ) );
+ this->buf_changed_count = Buffer_channels_changed_count( &this->stereo_buf );
Sound_set_tempo( this, this->tempo );
Sound_mute_voices( this, this->mute_mask_ );
@@ -265,8 +275,8 @@ static void set_voice( struct Kss_Emu* this, int i, struct Blip_Buffer* center,
}
if ( msx_scc_enabled( this ) && i < scc_osc_count ) Scc_set_output( &this->msx.scc, i, center );
- if ( msx_music_enabled( this ) && i < opl_osc_count ) Opl_set_output( &this->msx.music, center );
- if ( msx_audio_enabled( this ) && i < opl_osc_count ) Opl_set_output( &this->msx.audio, center );
+ if ( msx_music_enabled( this ) && i < opl_osc_count ) Opl_set_output( &this->msx.music, center );
+ if ( msx_audio_enabled( this ) && i < opl_osc_count ) Opl_set_output( &this->msx.audio, center );
}
}
@@ -465,15 +475,17 @@ blargg_err_t end_frame( struct Kss_Emu* this, kss_time_t end )
// MUSIC
-
-blargg_err_t Kss_set_sample_rate( struct Kss_Emu* this, long rate )
+blargg_err_t Kss_set_sample_rate( struct Kss_Emu* this, int rate )
{
require( !this->sample_rate ); // sample rate can't be changed once set
- RETURN_ERR( Buffer_set_sample_rate( &this->stereo_buffer, rate, 1000 / 20 ) );
+ RETURN_ERR( Buffer_set_sample_rate( &this->stereo_buf, rate, 1000 / 20 ) );
// Set bass frequency
- Buffer_bass_freq( &this->stereo_buffer, 180 );
+ Buffer_bass_freq( &this->stereo_buf, 180 );
+
this->sample_rate = rate;
+ RETURN_ERR( track_init( &this->track_filter, this ) );
+ this->tfilter.max_silence = 6 * stereo * this->sample_rate;
return 0;
}
@@ -501,7 +513,7 @@ void Sound_mute_voices( struct Kss_Emu* this, int mask )
}
else
{
- struct channel_t ch = Buffer_channel( &this->stereo_buffer );
+ struct channel_t ch = Buffer_channel( &this->stereo_buf, i );
assert( (ch.center && ch.left && ch.right) ||
(!ch.center && !ch.left && !ch.right) ); // all or nothing
set_voice( this, i, ch.center, ch.left, ch.right );
@@ -523,7 +535,6 @@ void Sound_set_tempo( struct Kss_Emu* this, int t )
this->play_period = (blip_time_t) ((period * FP_ONE_TEMPO) / t);
}
-void fill_buf( struct Kss_Emu* this );
blargg_err_t Kss_start_track( struct Kss_Emu* this, int track )
{
clear_track_vars( this );
@@ -536,7 +547,7 @@ blargg_err_t Kss_start_track( struct Kss_Emu* this, int track )
this->current_track = track;
- Buffer_clear( &this->stereo_buffer );
+ Buffer_clear( &this->stereo_buf );
if ( sms_psg_enabled( this ) ) Sms_apu_reset( &this->sms.psg, 0, 0 );
if ( sms_fm_enabled( this ) ) Opl_reset( &this->sms.fm );
@@ -546,7 +557,7 @@ blargg_err_t Kss_start_track( struct Kss_Emu* this, int track )
if ( msx_audio_enabled( this ) ) Opl_reset( &this->msx.audio );
this->scc_accessed = false;
- update_gain( this );
+ update_gain_( this );
memset( this->ram, 0xC9, 0x4000 );
memset( this->ram + 0x4000, 0, sizeof this->ram - 0x4000 );
@@ -598,286 +609,106 @@ blargg_err_t Kss_start_track( struct Kss_Emu* this, int track )
this->gain_updated = false;
jsr( this, this->header.init_addr );
- this->emu_track_ended_ = false;
- this->track_ended = false;
+ // convert filter times to samples
+ struct setup_t s = this->tfilter;
+ s.max_initial *= this->sample_rate * stereo;
+ #ifdef GME_DISABLE_SILENCE_LOOKAHEAD
+ s.lookahead = 1;
+ #endif
+ track_setup( &this->track_filter, &s );
- if ( !this->ignore_silence )
- {
- // play until non-silence or end of track
- long end;
- for ( end = this->max_initial_silence * stereo * this->sample_rate; this->emu_time < end; )
- {
- fill_buf( this );
- if ( this->buf_remain | (int) this->emu_track_ended_ )
- break;
- }
-
- this->emu_time = this->buf_remain;
- this->out_time = 0;
- this->silence_time = 0;
- this->silence_count = 0;
- }
- /* return track_ended() ? warning() : 0; */
- return 0;
+ return track_start( &this->track_filter );
}
// Tell/Seek
-static blargg_long msec_to_samples( blargg_long msec, long sample_rate )
+static int msec_to_samples( int msec, int sample_rate )
{
- blargg_long sec = msec / 1000;
+ int sec = msec / 1000;
msec -= sec * 1000;
return (sec * sample_rate + msec * sample_rate / 1000) * stereo;
}
-long Track_tell( struct Kss_Emu* this )
+int Track_tell( struct Kss_Emu* this )
{
- blargg_long rate = this->sample_rate * stereo;
- blargg_long sec = this->out_time / rate;
- return sec * 1000 + (this->out_time - sec * rate) * 1000 / rate;
+ int rate = this->sample_rate * stereo;
+ int sec = track_sample_count( &this->track_filter ) / rate;
+ return sec * 1000 + (track_sample_count( &this->track_filter ) - sec * rate) * 1000 / rate;
}
-blargg_err_t Track_seek( struct Kss_Emu* this, long msec )
+blargg_err_t Track_seek( struct Kss_Emu* this, int msec )
{
- blargg_long time = msec_to_samples( msec, this->sample_rate );
- if ( time < this->out_time )
- RETURN_ERR( Kss_start_track( this, this->current_track ) );
- return Track_skip( this, time - this->out_time );
+ int time = msec_to_samples( msec, this->sample_rate );
+ if ( time < track_sample_count( &this->track_filter ) )
+ RETURN_ERR( Kss_start_track( this, this->current_track ) );
+ return Track_skip( this, time - track_sample_count( &this->track_filter ) );
}
-blargg_err_t play_( struct Kss_Emu* this, long count, sample_t* out );
-static blargg_err_t skip_( struct Kss_Emu* this, long count )
+blargg_err_t skip_( void *emu, int count )
{
+ struct Kss_Emu* this = (struct Kss_Emu*) emu;
+
// for long skip, mute sound
- const long threshold = 30000;
+ const int threshold = 32768;
if ( count > threshold )
{
int saved_mute = this->mute_mask_;
Sound_mute_voices( this, ~0 );
-
- while ( count > threshold / 2 && !this->emu_track_ended_ )
- {
- RETURN_ERR( play_( this, buf_size, this->buf ) );
- count -= buf_size;
- }
-
- Sound_mute_voices( this, saved_mute );
- }
-
- while ( count && !this->emu_track_ended_ )
- {
- long n = buf_size;
- if ( n > count )
- n = count;
- count -= n;
- RETURN_ERR( play_( this, n, this->buf ) );
- }
- return 0;
-}
-blargg_err_t Track_skip( struct Kss_Emu* this, long count )
-{
- require( this->current_track >= 0 ); // start_track() must have been called already
- this->out_time += count;
-
- // remove from silence and buf first
- {
- long n = min( count, this->silence_count );
- this->silence_count -= n;
- count -= n;
-
- n = min( count, this->buf_remain );
- this->buf_remain -= n;
+ int n = count - threshold/2;
+ n &= ~(2048-1); // round to multiple of 2048
count -= n;
- }
-
- if ( count && !this->emu_track_ended_ )
- {
- this->emu_time += count;
- if ( skip_( this, count ) )
- this->emu_track_ended_ = true;
- }
-
- if ( !(this->silence_count | this->buf_remain) ) // caught up to emulator, so update track ended
- this->track_ended |= this->emu_track_ended_;
-
- return 0;
-}
-
-// Fading
+ RETURN_ERR( skippy_( &this->track_filter, n ) );
-void Track_set_fade( struct Kss_Emu* this, long start_msec, long length_msec )
-{
- this->fade_step = this->sample_rate * length_msec / (fade_block_size * fade_shift * 1000 / stereo);
- this->fade_start = msec_to_samples( start_msec, this->sample_rate );
-}
-
-// unit / pow( 2.0, (double) x / step )
-static int int_log( blargg_long x, int step, int unit )
-{
- int shift = x / step;
- int fraction = (x - shift * step) * unit / step;
- return ((unit - fraction) + (fraction >> 1)) >> shift;
-}
-
-static void handle_fade( struct Kss_Emu *this, long out_count, sample_t* out )
-{
- int i;
- for ( i = 0; i < out_count; i += fade_block_size )
- {
- int const shift = 14;
- int const unit = 1 << shift;
- int gain = int_log( (this->out_time + i - this->fade_start) / fade_block_size,
- this->fade_step, unit );
- if ( gain < (unit >> fade_shift) )
- this->track_ended = this->emu_track_ended_ = true;
-
- sample_t* io = &out [i];
- int count;
- for ( count = min( fade_block_size, out_count - i ); count; --count )
- {
- *io = (sample_t) ((*io * gain) >> shift);
- ++io;
- }
+ Sound_mute_voices( this, saved_mute );
}
-}
-
-// Silence detection
-static void emu_play( struct Kss_Emu* this, long count, sample_t* out )
-{
- check( current_track_ >= 0 );
- this->emu_time += count;
- if ( this->current_track >= 0 && !this->emu_track_ended_ ) {
- if ( play_( this, count, out ) )
- this->emu_track_ended_ = true;
- }
- else
- memset( out, 0, count * sizeof *out );
+ return skippy_( &this->track_filter, count );
}
-// number of consecutive silent samples at end
-static long count_silence( sample_t* begin, long size )
+blargg_err_t Track_skip( struct Kss_Emu* this, int count )
{
- sample_t first = *begin;
- *begin = silence_threshold; // sentinel
- sample_t* p = begin + size;
- while ( (unsigned) (*--p + silence_threshold / 2) <= (unsigned) silence_threshold ) { }
- *begin = first;
- return size - (p - begin);
+ require( this->current_track >= 0 ); // start_track() must have been called already
+ return track_skip( &this->track_filter, count );
}
-// fill internal buffer and check it for silence
-void fill_buf( struct Kss_Emu* this )
+void Track_set_fade( struct Kss_Emu* this, int start_msec, int length_msec )
{
- assert( !this->buf_remain );
- if ( !this->emu_track_ended_ )
- {
- emu_play( this, buf_size, this->buf );
- long silence = count_silence( this->buf, buf_size );
- if ( silence < buf_size )
- {
- this->silence_time = this->emu_time - silence;
- this->buf_remain = buf_size;
- return;
- }
- }
- this->silence_count += buf_size;
+ track_set_fade( &this->track_filter, msec_to_samples( start_msec, this->sample_rate ),
+ length_msec * this->sample_rate / (1000 / stereo) );
}
-blargg_err_t Kss_play( struct Kss_Emu* this, long out_count, sample_t* out )
+blargg_err_t Kss_play( struct Kss_Emu* this, int out_count, sample_t* out )
{
- if ( this->track_ended )
- {
- memset( out, 0, out_count * sizeof *out );
- }
- else
- {
- require( this->current_track >= 0 );
- require( out_count % stereo == 0 );
-
- assert( this->emu_time >= this->out_time );
-
- // prints nifty graph of how far ahead we are when searching for silence
- //debug_printf( "%*s \n", int ((emu_time - out_time) * 7 / sample_rate()), "*" );
-
- long pos = 0;
- if ( this->silence_count )
- {
- // during a run of silence, run emulator at >=2x speed so it gets ahead
- long ahead_time = this->silence_lookahead * (this->out_time + out_count -this->silence_time) + this->silence_time;
- while ( this->emu_time < ahead_time && !(this->buf_remain |this-> emu_track_ended_) )
- fill_buf( this );
-
- // fill with silence
- pos = min( this->silence_count, out_count );
- memset( out, 0, pos * sizeof *out );
- this->silence_count -= pos;
-
- if ( this->emu_time - this->silence_time > silence_max * stereo * this->sample_rate )
- {
- this->track_ended = this->emu_track_ended_ = true;
- this->silence_count = 0;
- this->buf_remain = 0;
- }
- }
-
- if ( this->buf_remain )
- {
- // empty silence buf
- long n = min( this->buf_remain, out_count - pos );
- memcpy( &out [pos], this->buf + (buf_size - this->buf_remain), n * sizeof *out );
- this->buf_remain -= n;
- pos += n;
- }
-
- // generate remaining samples normally
- long remain = out_count - pos;
- if ( remain )
- {
- emu_play( this, remain, out + pos );
- this->track_ended |= this->emu_track_ended_;
-
- if ( !this->ignore_silence || this->out_time > this->fade_start )
- {
- // check end for a new run of silence
- long silence = count_silence( out + pos, remain );
- if ( silence < remain )
- this->silence_time = this->emu_time - silence;
-
- if ( this->emu_time - this->silence_time >= buf_size )
- fill_buf( this ); // cause silence detection on next play()
- }
- }
-
- if ( this->out_time > this->fade_start )
- handle_fade( this, out_count, out );
- }
- this->out_time += out_count;
- return 0;
+ require( this->current_track >= 0 );
+ require( out_count % stereo == 0 );
+ return track_play( &this->track_filter, out_count, out );
}
-blargg_err_t play_( struct Kss_Emu* this, long count, sample_t* out )
+blargg_err_t play_( void *emu, int count, sample_t* out )
{
- long remain = count;
+ struct Kss_Emu* this = (struct Kss_Emu*) emu;
+
+ int remain = count;
while ( remain )
{
- remain -= Buffer_read_samples( &this->stereo_buffer, &out [count - remain], remain );
+ Buffer_disable_immediate_removal( &this->stereo_buf );
+ remain -= Buffer_read_samples( &this->stereo_buf, &out [count - remain], remain );
if ( remain )
{
- if ( this->buf_changed_count != Buffer_channels_changed_count( &this->stereo_buffer ) )
+ if ( this->buf_changed_count != Buffer_channels_changed_count( &this->stereo_buf ) )
{
- this->buf_changed_count = Buffer_channels_changed_count( &this->stereo_buffer );
+ this->buf_changed_count = Buffer_channels_changed_count( &this->stereo_buf );
+
+ // Remute voices
Sound_mute_voices( this, this->mute_mask_ );
}
- int msec = Buffer_length( &this->stereo_buffer );
- /* blip_time_t clocks_emulated = (blargg_long) msec * clock_rate_ / 1000; */
+ int msec = Buffer_length( &this->stereo_buf );
blip_time_t clocks_emulated = msec * this->clock_rate_ / 1000 - 100;
RETURN_ERR( run_clocks( this, &clocks_emulated ) );
assert( clocks_emulated );
- Buffer_end_frame( &this->stereo_buffer, clocks_emulated );
+ Buffer_end_frame( &this->stereo_buf, clocks_emulated );
}
}
return 0;
}
-
diff --git a/apps/codecs/libgme/kss_emu.h b/apps/codecs/libgme/kss_emu.h
index 43df964..382e4b8 100644
--- a/apps/codecs/libgme/kss_emu.h
+++ b/apps/codecs/libgme/kss_emu.h
@@ -16,8 +16,8 @@
#include "ay_apu.h"
#include "opl_apu.h"
#include "m3u_playlist.h"
+#include "track_filter.h"
-typedef short sample_t;
typedef int kss_time_t;
typedef int kss_addr_t;
typedef struct Z80_Cpu Kss_Cpu;
@@ -35,7 +35,6 @@ enum {
enum { idle_addr = 0xFFFF };
enum { scc_enabled_true = 0xC000 };
enum { mem_size = 0x10000 };
-enum { buf_size = 2048 };
// KSS file header
enum { header_size = 0x20 };
@@ -53,7 +52,7 @@ struct header_t
byte bank_mode;
byte extra_header;
byte device_flags;
-
+
// KSSX extended data, if extra_header==0x10
byte data_size [4];
byte unused [4];
@@ -69,7 +68,7 @@ struct sms_t {
struct Sms_Apu psg;
struct Opl_Apu fm;
};
-
+
struct msx_t {
struct Ay_Apu psg;
struct Scc_Apu scc;
@@ -84,8 +83,6 @@ struct Kss_Emu {
bool scc_accessed;
bool gain_updated;
- int track_count;
-
unsigned scc_enabled; // 0 or 0xC000
int bank_count;
@@ -94,48 +91,34 @@ struct Kss_Emu {
int ay_latch;
// general
- int max_initial_silence;
int voice_count;
+ int const* voice_types;
int mute_mask_;
int tempo;
int gain;
- long sample_rate;
+ int sample_rate;
// track-specific
+ int track_count;
int current_track;
- 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
- volatile bool track_ended;
-
- // fading
- blargg_long fade_start;
- int fade_step;
-
- // silence detection
- int silence_lookahead; // speed to run emulator when looking ahead for silence
- bool ignore_silence;
- long silence_time; // number of samples where most recent silence began
- long silence_count; // number of samples of silence to play before using buf
- long buf_remain; // number of samples left in silence buffer
- struct Stereo_Buffer stereo_buffer; // NULL if using custom buffer
- long clock_rate_;
+ int clock_rate_;
unsigned buf_changed_count;
- // M3u Playlist
+ // M3u Playlist
struct M3u_Playlist m3u;
- // large items
- sample_t buf [buf_size];
+ struct setup_t tfilter;
+ struct Track_Filter track_filter;
struct sms_t sms;
struct msx_t msx;
Kss_Cpu cpu;
+ struct Multi_Buffer stereo_buf; // NULL if using custom buffer
struct Rom_Data rom;
-
+
byte unmapped_read [0x100];
byte unmapped_write [page_size];
byte ram [mem_size + cpu_padding];
@@ -148,34 +131,46 @@ blargg_err_t Kss_load_mem( struct Kss_Emu* this, const void* data, long size );
blargg_err_t end_frame( struct Kss_Emu* this, kss_time_t );
// Set output sample rate. Must be called only once before loading file.
-blargg_err_t Kss_set_sample_rate( struct Kss_Emu* this, long sample_rate );
+blargg_err_t Kss_set_sample_rate( struct Kss_Emu* this, int sample_rate );
// Start a track, where 0 is the first track. Also clears warning string.
blargg_err_t Kss_start_track( struct Kss_Emu* this, int track );
// Generate 'count' samples info 'buf'. Output is in stereo. Any emulation
// errors set warning string, and major errors also end track.
-blargg_err_t Kss_play( struct Kss_Emu* this, long count, sample_t* buf );
+blargg_err_t Kss_play( struct Kss_Emu* this, int count, sample_t* buf );
// Track status/control
// Number of milliseconds (1000 msec = 1 second) played since beginning of track
-long Track_tell( struct Kss_Emu* this );
+int Track_tell( struct Kss_Emu* this );
// Seek to new time in track. Seeking backwards or far forward can take a while.
-blargg_err_t Track_seek( struct Kss_Emu* this, long msec );
+blargg_err_t Track_seek( struct Kss_Emu* this, int msec );
// Skip n samples
-blargg_err_t Track_skip( struct Kss_Emu* this, long n );
+blargg_err_t Track_skip( struct Kss_Emu* this, int n );
// Set start time and length of track fade out. Once fade ends track_ended() returns
// true. Fade time can be changed while track is playing.
-void Track_set_fade( struct Kss_Emu* this, long start_msec, long length_msec );
+void Track_set_fade( struct Kss_Emu* this, int start_msec, int length_msec );
+
+// True if a track has reached its end
+static inline bool Track_ended( struct Kss_Emu* this )
+{
+ return track_ended( &this->track_filter );
+}
+
+// Disables automatic end-of-track detection and skipping of silence at beginning
+static inline void Track_ignore_silence( struct Kss_Emu* this, bool disable )
+{
+ this->track_filter.silence_ignored_ = disable;
+}
// Get track length in milliseconds
-static inline long Track_get_length( struct Kss_Emu* this, int n )
+static inline int Track_get_length( struct Kss_Emu* this, int n )
{
- long length = 0;
+ int length = 0;
if ( (this->m3u.size > 0) && (n < this->m3u.size) ) {
struct entry_t* entry = &this->m3u.entries [n];
diff --git a/apps/codecs/libgme/kss_scc_apu.h b/apps/codecs/libgme/kss_scc_apu.h
index bb20d1d..a696246 100644
--- a/apps/codecs/libgme/kss_scc_apu.h
+++ b/apps/codecs/libgme/kss_scc_apu.h
@@ -34,7 +34,7 @@ void Scc_reset( struct Scc_Apu* this );
// Set overall volume, where 1.0 is normal
void Scc_volume( struct Scc_Apu* this, int v );
-
+
static inline void Scc_set_output( struct Scc_Apu* this, int index, struct Blip_Buffer* b )
{
assert( (unsigned) index < scc_osc_count );
diff --git a/apps/codecs/libgme/multi_buffer.c b/apps/codecs/libgme/multi_buffer.c
index 26cb8cd..554778c 100644
--- a/apps/codecs/libgme/multi_buffer.c
+++ b/apps/codecs/libgme/multi_buffer.c
@@ -1,4 +1,4 @@
-// Blip_Buffer 0.4.1. http://www.slack.net/~ant/
+// Multi_Buffer 0.4.1. http://www.slack.net/~ant/
#include "multi_buffer.h"
@@ -15,212 +15,272 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
#include "blargg_source.h"
-#ifdef BLARGG_ENABLE_OPTIMIZER
- #include BLARGG_ENABLE_OPTIMIZER
-#endif
-
-// Stereo_Buffer
-
-void Buffer_init( struct Stereo_Buffer* this )
-{
- Blip_init( &this->bufs [0] );
- Blip_init( &this->bufs [1] );
- Blip_init( &this->bufs [2] );
-
- this->chan.center = &this->bufs [0];
- this->chan.left = &this->bufs [1];
- this->chan.right = &this->bufs [2];
-
- this->length_ = 0;
- this->sample_rate_ = 0;
- this->channels_changed_count_ = 1;
- this->samples_per_frame_ = 2;
+// Tracked_Blip_Buffer
+
+int const blip_buffer_extra = 32; // TODO: explain why this value
+
+void Tracked_init( struct Tracked_Blip_Buffer* this )
+{
+ Blip_init( &this->blip );
+ this->last_non_silence = 0;
}
-blargg_err_t Buffer_set_sample_rate( struct Stereo_Buffer* this, long rate, int msec )
+void Tracked_clear( struct Tracked_Blip_Buffer* this )
{
- int i;
- for ( i = 0; i < buf_count; i++ )
- RETURN_ERR( Blip_set_sample_rate( &this->bufs[i], rate, msec ) );
-
- this->sample_rate_ = Blip_sample_rate( &this->bufs [0] );
- this->length_ = Blip_length( &this->bufs [0] );
- return 0;
+ this->last_non_silence = 0;
+ Blip_clear( &this->blip );
}
-void Buffer_clock_rate( struct Stereo_Buffer* this, long rate )
+void Tracked_end_frame( struct Tracked_Blip_Buffer* this, blip_time_t t )
{
- int i;
- for ( i = 0; i < buf_count; i++ )
- Blip_set_clock_rate( &this->bufs [i], rate );
+ Blip_end_frame( &this->blip, t );
+ if ( this->blip.modified )
+ {
+ this->blip.modified = false;
+ this->last_non_silence = Blip_samples_avail( &this->blip ) + blip_buffer_extra;
+ }
}
-void Buffer_bass_freq( struct Stereo_Buffer* this, int bass )
+unsigned Tracked_non_silent( struct Tracked_Blip_Buffer* this )
{
- unsigned i;
- for ( i = 0; i < buf_count; i++ )
- Blip_bass_freq( &this->bufs [i], bass );
+ return this->last_non_silence | unsettled( &this->blip );
}
-struct channel_t Buffer_channel( struct Stereo_Buffer* this )
+static inline void remove_( struct Tracked_Blip_Buffer* this, int n )
{
- return this->chan;
+ if ( (this->last_non_silence -= n) < 0 )
+ this->last_non_silence = 0;
}
-void Buffer_clear( struct Stereo_Buffer* this )
+void Tracked_remove_silence( struct Tracked_Blip_Buffer* this, int n )
{
- this->stereo_added = 0;
- this->was_stereo = false;
- int i;
- for ( i = 0; i < buf_count; i++ )
- Blip_clear( &this->bufs [i], 1 );
+ remove_( this, n );
+ Blip_remove_silence( &this->blip, n );
}
-void Buffer_end_frame( struct Stereo_Buffer* this, blip_time_t clock_count )
+void Tracked_remove_samples( struct Tracked_Blip_Buffer* this, int n )
+{
+ remove_( this, n );
+ Blip_remove_samples( &this->blip, n );
+}
+
+void Tracked_remove_all_samples( struct Tracked_Blip_Buffer* this )
+{
+ int avail = Blip_samples_avail( &this->blip );
+ if ( !Tracked_non_silent( this ) )
+ Tracked_remove_silence( this, avail );
+ else
+ Tracked_remove_samples( this, avail );
+}
+
+int Tracked_read_samples( struct Tracked_Blip_Buffer* this, blip_sample_t out [], int count )
+{
+ count = Blip_read_samples( &this->blip, out, count, false );
+ remove_( this, count );
+ return count;
+}
+
+// Stereo_Mixer
+
+// mixers use a single index value to improve performance on register-challenged processors
+// offset goes from negative to zero
+
+void Mixer_init( struct Stereo_Mixer* this )
{
- this->stereo_added = 0;
- unsigned i;
- for ( i = 0; i < buf_count; i++ )
+ this->samples_read = 0;
+}
+
+static void mix_mono( struct Stereo_Mixer* this, blip_sample_t out_ [], int count )
+{
+ int const bass = this->bufs [2]->blip.bass_shift_;
+ delta_t const* center = this->bufs [2]->blip.buffer_ + this->samples_read;
+ int center_sum = this->bufs [2]->blip.reader_accum_;
+
+ typedef blip_sample_t stereo_blip_sample_t [stereo];
+ stereo_blip_sample_t* BLARGG_RESTRICT out = (stereo_blip_sample_t*) out_ + count;
+ int offset = -count;
+ do
{
- this->stereo_added |= Blip_clear_modified( &this->bufs [i] ) << i;
- Blip_end_frame( &this->bufs [i], clock_count );
+ int s = center_sum >> delta_bits;
+
+ center_sum -= center_sum >> bass;
+ center_sum += center [offset];
+
+ BLIP_CLAMP( s, s );
+
+ out [offset] [0] = (blip_sample_t) s;
+ out [offset] [1] = (blip_sample_t) s;
}
+ while ( ++offset );
+
+ this->bufs [2]->blip.reader_accum_ = center_sum;
}
-long Buffer_read_samples( struct Stereo_Buffer* this, blip_sample_t* out, long count )
+static void mix_stereo( struct Stereo_Mixer* this, blip_sample_t out_ [], int count )
{
- require( !(count & 1) ); // count must be even
- count = (unsigned) count / 2;
-
- long avail = Blip_samples_avail( &this->bufs [0] );
- if ( count > avail )
- count = avail;
- if ( count )
+ blip_sample_t* BLARGG_RESTRICT out = out_ + count * stereo;
+ // do left + center and right + center separately to reduce register load
+ struct Tracked_Blip_Buffer* const* buf = &this->bufs [2];
+ while ( true ) // loop runs twice
{
- int bufs_used = this->stereo_added | this->was_stereo;
- //dprintf( "%X\n", bufs_used );
- if ( bufs_used <= 1 )
- {
- Buffer_mix_mono( this, out, count );
- Blip_remove_samples( &this->bufs [0], count );
- Blip_remove_silence( &this->bufs [1], count );
- Blip_remove_silence( &this->bufs [2], count );
- }
- else if ( bufs_used & 1 )
- {
- Buffer_mix_stereo( this, out, count );
- Blip_remove_samples( &this->bufs [0], count );
- Blip_remove_samples( &this->bufs [1], count );
- Blip_remove_samples( &this->bufs [2], count );
- }
- else
- {
- Buffer_mix_stereo_no_center( this, out, count );
- Blip_remove_silence( &this->bufs [0], count );
- Blip_remove_samples( &this->bufs [1], count );
- Blip_remove_samples( &this->bufs [2], count );
- }
-
- // to do: this might miss opportunities for optimization
- if ( !Blip_samples_avail( &this->bufs [0] ) )
+ --buf;
+ --out;
+
+ int const bass = this->bufs [2]->blip.bass_shift_;
+ delta_t const* side = (*buf)->blip.buffer_ + this->samples_read;
+ delta_t const* center = this->bufs [2]->blip.buffer_ + this->samples_read;
+
+ int side_sum = (*buf)->blip.reader_accum_;
+ int center_sum = this->bufs [2]->blip.reader_accum_;
+
+ int offset = -count;
+ do
{
- this->was_stereo = this->stereo_added;
- this->stereo_added = 0;
+ int s = (center_sum + side_sum) >> delta_bits;
+
+ side_sum -= side_sum >> bass;
+ center_sum -= center_sum >> bass;
+
+ side_sum += side [offset];
+ center_sum += center [offset];
+
+ BLIP_CLAMP( s, s );
+
+ ++offset; // before write since out is decremented to slightly before end
+ out [offset * stereo] = (blip_sample_t) s;
}
+ while ( offset );
+
+ (*buf)->blip.reader_accum_ = side_sum;
+
+ if ( buf != this->bufs )
+ continue;
+
+ // only end center once
+ this->bufs [2]->blip.reader_accum_ = center_sum;
+ break;
}
-
- return count * 2;
}
-unsigned Buffer_channels_changed_count( struct Stereo_Buffer* this )
+void Mixer_read_pairs( struct Stereo_Mixer* this, blip_sample_t out [], int count )
{
- return this->channels_changed_count_;
+ // TODO: if caller never marks buffers as modified, uses mono
+ // except that buffer isn't cleared, so caller can encounter
+ // subtle problems and not realize the cause.
+ this->samples_read += count;
+ if ( Tracked_non_silent( this->bufs [0] ) | Tracked_non_silent( this->bufs [1] ) )
+ mix_stereo( this, out, count );
+ else
+ mix_mono( this, out, count );
}
-void Buffer_channels_changed( struct Stereo_Buffer* this )
+// Multi_Buffer
+
+void Buffer_init( struct Multi_Buffer* this )
{
- this->channels_changed_count_++;
+ int const spf = 2;
+
+ Tracked_init( &this->bufs [0] );
+ Tracked_init( &this->bufs [1] );
+ Tracked_init( &this->bufs [2] );
+
+ Mixer_init( &this->mixer );
+
+ this->length_ = 0;
+ this->sample_rate_ = 0;
+ this->channels_changed_count_ = 1;
+ this->channel_types_ = NULL;
+ this->channel_count_ = 0;
+ this->samples_per_frame_ = spf;
+ this->immediate_removal_ = true;
+
+ this->mixer.bufs [2] = &this->bufs [2];
+ this->mixer.bufs [0] = &this->bufs [0];
+ this->mixer.bufs [1] = &this->bufs [1];
+
+ this->chan.center = &this->bufs [2].blip;
+ this->chan.left = &this->bufs [0].blip;
+ this->chan.right = &this->bufs [1].blip;
}
-void Buffer_mix_stereo( struct Stereo_Buffer* this, blip_sample_t* out_, blargg_long count )
+blargg_err_t Buffer_set_sample_rate( struct Multi_Buffer* this, int rate, int msec )
{
- blip_sample_t* BLIP_RESTRICT out = out_;
- int const bass = BLIP_READER_BASS( this->bufs [1] );
- BLIP_READER_BEGIN( left, this->bufs [1] );
- BLIP_READER_BEGIN( right, this->bufs [2] );
- BLIP_READER_BEGIN( center, this->bufs [0] );
-
- for ( ; count; --count )
- {
- int c = BLIP_READER_READ( center );
- blargg_long l = c + BLIP_READER_READ( left );
- blargg_long r = c + BLIP_READER_READ( right );
- if ( (int16_t) l != l )
- l = 0x7FFF - (l >> 24);
-
- BLIP_READER_NEXT( center, bass );
- if ( (int16_t) r != r )
- r = 0x7FFF - (r >> 24);
-
- BLIP_READER_NEXT( left, bass );
- BLIP_READER_NEXT( right, bass );
-
- out [0] = l;
- out [1] = r;
- out += 2;
- }
-
- BLIP_READER_END( center, this->bufs [0] );
- BLIP_READER_END( right, this->bufs [2] );
- BLIP_READER_END( left, this->bufs [1] );
+ int i;
+ for ( i = bufs_size; --i >= 0; )
+ RETURN_ERR( Blip_set_sample_rate( &this->bufs [i].blip, rate, msec ) );
+
+ this->sample_rate_ = Blip_sample_rate( &this->bufs [0].blip );
+ this->length_ = Blip_length( &this->bufs [0].blip );
+ return 0;
}
-void Buffer_mix_stereo_no_center( struct Stereo_Buffer* this, blip_sample_t* out_, blargg_long count )
+void Buffer_clock_rate( struct Multi_Buffer* this, int rate )
{
- blip_sample_t* BLIP_RESTRICT out = out_;
- int const bass = BLIP_READER_BASS( this->bufs [1] );
- BLIP_READER_BEGIN( left, this->bufs [1] );
- BLIP_READER_BEGIN( right, this->bufs [2] );
-
- for ( ; count; --count )
- {
- blargg_long l = BLIP_READER_READ( left );
- if ( (int16_t) l != l )
- l = 0x7FFF - (l >> 24);
-
- blargg_long r = BLIP_READER_READ( right );
- if ( (int16_t) r != r )
- r = 0x7FFF - (r >> 24);
-
- BLIP_READER_NEXT( left, bass );
- BLIP_READER_NEXT( right, bass );
-
- out [0] = l;
- out [1] = r;
- out += 2;
- }
-
- BLIP_READER_END( right, this->bufs [2] );
- BLIP_READER_END( left, this->bufs [1] );
+ int i;
+ for ( i = bufs_size; --i >= 0; )
+ Blip_set_clock_rate( &this->bufs [i].blip, rate );
+}
+
+void Buffer_bass_freq( struct Multi_Buffer* this, int bass )
+{
+ int i;
+ for ( i = bufs_size; --i >= 0; )
+ Blip_bass_freq( &this->bufs [i].blip, bass );
+}
+
+blargg_err_t Buffer_set_channel_count( struct Multi_Buffer* this, int n, int const* types )
+{
+ this->channel_count_ = n;
+ this->channel_types_ = types;
+ return 0;
}
-void Buffer_mix_mono( struct Stereo_Buffer* this, blip_sample_t* out_, blargg_long count )
+struct channel_t Buffer_channel( struct Multi_Buffer* this, int i )
{
- blip_sample_t* BLIP_RESTRICT out = out_;
- int const bass = BLIP_READER_BASS( this->bufs [0] );
- BLIP_READER_BEGIN( center, this->bufs [0] );
-
- for ( ; count; --count )
+ (void) i;
+ return this->chan;
+}
+
+void Buffer_clear( struct Multi_Buffer* this )
+{
+ int i;
+ this->mixer.samples_read = 0;
+ for ( i = bufs_size; --i >= 0; )
+ Tracked_clear( &this->bufs [i] );
+}
+
+void Buffer_end_frame( struct Multi_Buffer* this, blip_time_t clock_count )
+{
+ int i;
+ for ( i = bufs_size; --i >= 0; )
+ Tracked_end_frame( &this->bufs [i], clock_count );
+}
+
+int Buffer_read_samples( struct Multi_Buffer* this, blip_sample_t out [], int out_size )
+{
+ require( (out_size & 1) == 0 ); // must read an even number of samples
+ out_size = min( out_size, Buffer_samples_avail( this ) );
+
+ int pair_count = (int) (out_size >> 1);
+ if ( pair_count )
{
- blargg_long s = BLIP_READER_READ( center );
- if ( (int16_t) s != s )
- s = 0x7FFF - (s >> 24);
-
- BLIP_READER_NEXT( center, bass );
- out [0] = s;
- out [1] = s;
- out += 2;
+ Mixer_read_pairs( &this->mixer, out, pair_count );
+
+ if ( Buffer_samples_avail( this ) <= 0 || this->immediate_removal_ )
+ {
+ int i;
+ for ( i = bufs_size; --i >= 0; )
+ {
+ buf_t* b = &this->bufs [i];
+ // TODO: might miss non-silence settling since it checks END of last read
+ if ( !Tracked_non_silent( b ) )
+ Tracked_remove_silence( b, this->mixer.samples_read );
+ else
+ Tracked_remove_samples( b, this->mixer.samples_read );
+ }
+ this->mixer.samples_read = 0;
+ }
}
-
- BLIP_READER_END( center, this->bufs [0] );
+
+ return out_size;
}
diff --git a/apps/codecs/libgme/multi_buffer.h b/apps/codecs/libgme/multi_buffer.h
index cfdae4f..e5efa5a 100644
--- a/apps/codecs/libgme/multi_buffer.h
+++ b/apps/codecs/libgme/multi_buffer.h
@@ -1,4 +1,4 @@
-// Multi-channel sound buffer interface, and basic mono and stereo buffers
+// Multi-channel sound buffer interface, stereo and effects buffers
// Blip_Buffer 0.4.1
#ifndef MULTI_BUFFER_H
@@ -16,57 +16,99 @@ struct channel_t {
enum { type_index_mask = 0xFF };
enum { wave_type = 0x100, noise_type = 0x200, mixed_type = wave_type | noise_type };
-enum { buf_count = 3 };
-
-struct Stereo_Buffer {
- struct Blip_Buffer bufs [buf_count];
- struct channel_t chan;
- int stereo_added;
- int was_stereo;
-
+enum { stereo = 2 };
+enum { bufs_size = 3 };
+
+// Tracked_Blip_Buffer
+struct Tracked_Blip_Buffer {
+ struct Blip_Buffer blip;
+ int last_non_silence;
+};
+
+void Tracked_init( struct Tracked_Blip_Buffer* this );
+unsigned Tracked_non_silent( struct Tracked_Blip_Buffer* this );
+void Tracked_remove_all_samples( struct Tracked_Blip_Buffer * this );
+int Tracked_read_samples( struct Tracked_Blip_Buffer* this, blip_sample_t [], int );
+void Tracked_remove_silence( struct Tracked_Blip_Buffer* this, int );
+void Tracked_remove_samples( struct Tracked_Blip_Buffer* this, int );
+void Tracked_clear( struct Tracked_Blip_Buffer* this );
+void Tracked_end_frame( struct Tracked_Blip_Buffer* this, blip_time_t );
+
+static inline delta_t unsettled( struct Blip_Buffer* this )
+{
+ return this->reader_accum_ >> delta_bits;
+}
+
+// Stereo Mixer
+struct Stereo_Mixer {
+ struct Tracked_Blip_Buffer* bufs [3];
+ int samples_read;
+};
+
+void Mixer_init( struct Stereo_Mixer* this );
+void Mixer_read_pairs( struct Stereo_Mixer* this, blip_sample_t out [], int count );
+
+typedef struct Tracked_Blip_Buffer buf_t;
+
+// Multi_Buffer
+struct Multi_Buffer {
unsigned channels_changed_count_;
- long sample_rate_;
+ int sample_rate_;
int length_;
+ int channel_count_;
int samples_per_frame_;
+ int const *channel_types_;
+ bool immediate_removal_;
+
+ buf_t bufs [bufs_size];
+ struct Stereo_Mixer mixer;
+ struct channel_t chan;
};
-// Initializes Stereo_Buffer structure
-void Buffer_init( struct Stereo_Buffer* this );
+blargg_err_t Buffer_set_channel_count( struct Multi_Buffer* this, int n, int const* types );
+
+// Buffers used for all channels
+static inline struct Blip_Buffer* center( struct Multi_Buffer* this ) { return &this->bufs [2].blip; }
+static inline struct Blip_Buffer* left( struct Multi_Buffer* this ) { return &this->bufs [0].blip; }
+static inline struct Blip_Buffer* right( struct Multi_Buffer* this ) { return &this->bufs [1].blip; }
-blargg_err_t Buffer_set_sample_rate( struct Stereo_Buffer* this, long, int msec );
-void Buffer_clock_rate( struct Stereo_Buffer* this, long );
-void Buffer_bass_freq( struct Stereo_Buffer* this, int );
-void Buffer_clear( struct Stereo_Buffer* this );
-struct channel_t Buffer_channel( struct Stereo_Buffer* this );
-void Buffer_end_frame( struct Stereo_Buffer* this, blip_time_t );
+// Initializes Multi_Buffer structure
+void Buffer_init( struct Multi_Buffer* this );
-long Buffer_read_samples( struct Stereo_Buffer* this, blip_sample_t*, long );
+blargg_err_t Buffer_set_sample_rate( struct Multi_Buffer* this, int, int msec );
+void Buffer_clock_rate( struct Multi_Buffer* this, int );
+void Buffer_bass_freq( struct Multi_Buffer* this, int );
+void Buffer_clear( struct Multi_Buffer* this );
+void Buffer_end_frame( struct Multi_Buffer* this, blip_time_t ) ICODE_ATTR;
+
+static inline int Buffer_length( struct Multi_Buffer* this )
+{
+ return this->length_;
+}
// Count of changes to channel configuration. Incremented whenever
// a change is made to any of the Blip_Buffers for any channel.
-unsigned Buffer_channels_changed_count( struct Stereo_Buffer* this );
-void Buffer_channels_changed( struct Stereo_Buffer* this );
-
-void Buffer_mix_stereo_no_center( struct Stereo_Buffer* this, blip_sample_t*, blargg_long );
-void Buffer_mix_stereo( struct Stereo_Buffer* this, blip_sample_t*, blargg_long );
-void Buffer_mix_mono( struct Stereo_Buffer* this, blip_sample_t*, blargg_long );
+static inline unsigned Buffer_channels_changed_count( struct Multi_Buffer* this )
+{
+ return this->channels_changed_count_;
+}
-// Number of samples per output frame (1 = mono, 2 = stereo)
-static inline int Buffer_samples_per_frame( struct Stereo_Buffer* this )
+static inline void Buffer_disable_immediate_removal( struct Multi_Buffer* this )
{
- return this->samples_per_frame_;
+ this->immediate_removal_ = false;
}
-// See Blip_Buffer.h
-static inline long Buffer_sample_rate( struct Stereo_Buffer* this )
+static inline int Buffer_sample_rate( struct Multi_Buffer* this )
{
return this->sample_rate_;
}
-// Length of buffer, in milliseconds
-static inline int Buffer_length( struct Stereo_Buffer* this )
+static inline int Buffer_samples_avail( struct Multi_Buffer* this )
{
- return this->length_;
+ return (Blip_samples_avail(&this->bufs [0].blip) - this->mixer.samples_read) * 2;
}
+int Buffer_read_samples( struct Multi_Buffer* this, blip_sample_t*, int ) ICODE_ATTR;
+struct channel_t Buffer_channel( struct Multi_Buffer* this, int i );
+
#endif
diff --git a/apps/codecs/libgme/nes_apu.c b/apps/codecs/libgme/nes_apu.c
index 7d2814b..630e71f 100644
--- a/apps/codecs/libgme/nes_apu.c
+++ b/apps/codecs/libgme/nes_apu.c
@@ -39,19 +39,19 @@ void Apu_init( struct Nes_Apu* this )
this->oscs [4] = &this->dmc.osc;
Apu_output( this, NULL );
+ this->dmc.nonlinear = false;
Apu_volume( this, (int)FP_ONE_VOLUME );
Apu_reset( this, false, 0 );
}
-void Apu_enable_nonlinear( struct Nes_Apu* this, int v )
+void Apu_enable_nonlinear_( struct Nes_Apu* this, double sq, double tnd )
{
this->dmc.nonlinear = true;
- Synth_volume( &this->square_synth, (int)((long long)(1.3 * 0.25751258 / 0.742467605 * 0.25 * FP_ONE_VOLUME) / amp_range * v) );
+ Synth_volume( &this->square_synth, (int)((long long)(sq * FP_ONE_VOLUME) / amp_range) );
- const int tnd = (int)(0.48 / 202 * 0.75 * FP_ONE_VOLUME);
- Synth_volume( &this->triangle.synth, 3 * tnd );
- Synth_volume( &this->noise.synth, 2 * tnd );
- Synth_volume( &this->dmc.synth, tnd );
+ Synth_volume( &this->triangle.synth, tnd * 2.752 );
+ Synth_volume( &this->noise.synth , tnd * 1.849 );
+ Synth_volume( &this->dmc.synth , tnd );
this->square1 .osc.last_amp = 0;
this->square2 .osc.last_amp = 0;
@@ -62,11 +62,13 @@ void Apu_enable_nonlinear( struct Nes_Apu* this, int v )
void Apu_volume( struct Nes_Apu* this, int v )
{
- this->dmc.nonlinear = false;
- Synth_volume( &this->square_synth, (int)((long long)(0.1128 *FP_ONE_VOLUME) * v / amp_range / FP_ONE_VOLUME) );
- Synth_volume( &this->triangle.synth,(int)((long long)(0.12765*FP_ONE_VOLUME) * v / amp_range / FP_ONE_VOLUME) );
- Synth_volume( &this->noise.synth, (int)((long long)(0.0741 *FP_ONE_VOLUME) * v / amp_range / FP_ONE_VOLUME) );
- Synth_volume( &this->dmc.synth, (int)((long long)(0.42545*FP_ONE_VOLUME) * v / 127 / FP_ONE_VOLUME) );
+ if ( !this->dmc.nonlinear )
+ {
+ Synth_volume( &this->square_synth, (int)((long long)((0.125 * (1.0 /1.11)) * FP_ONE_VOLUME) * v / amp_range / FP_ONE_VOLUME) ); // was 0.1128 1.108
+ Synth_volume( &this->triangle.synth,(int)((long long)((0.150 * (1.0 /1.11)) * FP_ONE_VOLUME) * v / amp_range / FP_ONE_VOLUME) ); // was 0.12765 1.175
+ Synth_volume( &this->noise.synth, (int)((long long)((0.095 * (1.0 /1.11)) * FP_ONE_VOLUME) * v / amp_range / FP_ONE_VOLUME) ); // was 0.0741 1.282
+ Synth_volume( &this->dmc.synth, (int)((long long)((0.450 * (1.0 /1.11)) * FP_ONE_VOLUME) * v / 2048 / FP_ONE_VOLUME) ); // was 0.42545 1.058
+ }
}
void Apu_output( struct Nes_Apu* this, struct Blip_Buffer* buffer )
diff --git a/apps/codecs/libgme/nes_apu.h b/apps/codecs/libgme/nes_apu.h
index 11f1f26..0a2b1f5 100644
--- a/apps/codecs/libgme/nes_apu.h
+++ b/apps/codecs/libgme/nes_apu.h
@@ -1,6 +1,6 @@
// NES 2A03 APU sound chip emulator
-// Nes_Snd_Emu 0.1.8
+// Nes_Snd_Emu 0.2.0-pre
#ifndef NES_APU_H
#define NES_APU_H
@@ -9,7 +9,7 @@
enum { apu_status_addr = 0x4015 };
enum { apu_osc_count = 5 };
-enum { apu_no_irq = INT_MAX / 2 + 1 };
+enum { apu_no_irq = INT_MAX/2 + 1 };
enum { apu_irq_waiting = 0 };
enum { apu_io_addr = 0x4000 };
@@ -17,24 +17,16 @@ enum { apu_io_size = 0x18 };
struct apu_state_t;
-struct Nes_Apu {
- nes_time_t last_dmc_time;
- int osc_enables;
-
- struct Nes_Osc* oscs [apu_osc_count];
- struct Nes_Square square1;
- struct Nes_Square square2;
- struct Nes_Noise noise;
- struct Nes_Triangle triangle;
- struct Nes_Dmc dmc;
-
+struct Nes_Apu {
int tempo_;
nes_time_t last_time; // has been run until this time in current frame
+ nes_time_t last_dmc_time;
nes_time_t earliest_irq_;
nes_time_t next_irq;
int frame_period;
int frame_delay; // cycles until frame counter runs next
int frame; // current frame (0-3)
+ int osc_enables;
int frame_mode;
bool irq_flag;
@@ -42,6 +34,13 @@ struct Nes_Apu {
void* irq_data;
Synth square_synth; // shared by squares
+
+ struct Nes_Osc* oscs [apu_osc_count];
+ struct Nes_Square square1;
+ struct Nes_Square square2;
+ struct Nes_Noise noise;
+ struct Nes_Triangle triangle;
+ struct Nes_Dmc dmc;
};
// Init Nes apu
@@ -49,22 +48,22 @@ void Apu_init( struct Nes_Apu* this );
// Set buffer to generate all sound into, or disable sound if NULL
void Apu_output( struct Nes_Apu* this, struct Blip_Buffer* );
-
+
// All time values are the number of cpu clock cycles relative to the
// beginning of the current time frame. Before resetting the cpu clock
// count, call end_frame( last_cpu_time ).
// Write to register (0x4000-0x4017, except 0x4014 and 0x4016)
void Apu_write_register( struct Nes_Apu* this, nes_time_t, addr_t, int data );
-
+
// Read from status register at 0x4015
int Apu_read_status( struct Nes_Apu* this, nes_time_t );
-
+
// Run all oscillators up to specified time, end current time frame, then
// start a new time frame at time 0. Time frames have no effect on emulation
// and each can be whatever length is convenient.
void Apu_end_frame( struct Nes_Apu* this, nes_time_t );
-
+
// Additional optional features (can be ignored without any problem)
// Reset internal frame counter, registers, and all oscillators.
@@ -72,13 +71,13 @@ void Apu_end_frame( struct Nes_Apu* this, nes_time_t );
// Set the DMC oscillator's initial DAC value to initial_dmc_dac without
// any audible click.
void Apu_reset( struct Nes_Apu* this, bool pal_mode, int initial_dmc_dac );
-
+
// Adjust frame period
void Apu_set_tempo( struct Nes_Apu* this, int );
-
+
// Set overall volume (default is 1.0)
void Apu_volume( struct Nes_Apu* this, int );
-
+
// Run DMC until specified time, so that any DMC memory reads can be
// accounted for (i.e. inserting cpu wait states).
void Apu_run_until( struct Nes_Apu* this, nes_time_t );
@@ -124,11 +123,13 @@ static inline nes_time_t Dmc_next_read_time( struct Nes_Dmc* this )
if ( this->osc.length_counter == 0 )
return apu_no_irq; // not reading
- return this->apu->last_dmc_time + this->osc.delay + (long) (this->bits_remain - 1) * this->period;
+ return this->apu->last_dmc_time + this->osc.delay + (this->bits_remain - 1) * this->period;
}
// Time when next DMC memory read will occur
static inline nes_time_t Apu_next_dmc_read_time( struct Nes_Apu* this ) { return Dmc_next_read_time( &this->dmc ); }
void Apu_irq_changed( struct Nes_Apu* this );
+// Experimental
+void Apu_enable_nonlinear_( struct Nes_Apu* this, double sq, double tnd );
#endif
diff --git a/apps/codecs/libgme/nes_cpu_io.h b/apps/codecs/libgme/nes_cpu_io.h
deleted file mode 100644
index 4f9d416..0000000
--- a/apps/codecs/libgme/nes_cpu_io.h
+++ /dev/null
@@ -1,94 +0,0 @@
-
-#include "nsf_emu.h"
-
-#ifndef NSF_EMU_APU_ONLY
- #include "nes_namco_apu.h"
- #include "nes_fds_apu.h"
- #include "nes_mmc5_apu.h"
-#endif
-
-#include "blargg_source.h"
-
-int Cpu_read( struct Nsf_Emu* this, nes_addr_t addr )
-{
- int result = this->cpu.low_mem [addr & 0x7FF];
- if ( addr & 0xE000 )
- {
- result = *Cpu_get_code( &this->cpu, addr );
- if ( addr < sram_addr )
- {
- if ( addr == status_addr )
- result = Apu_read_status( &this->apu, Cpu_time( &this->cpu ) );
- else
- {
- #ifndef NSF_EMU_APU_ONLY
- if ( namco_enabled( this ) && addr == namco_data_reg_addr )
- return Namco_read_data( &this->namco );
-
- if ( fds_enabled( this ) && (unsigned) (addr - fds_io_addr) < fds_io_size )
- return Fds_read( &this->fds, Cpu_time( &this->cpu ), addr );
-
- if ( mmc5_enabled( this ) ) {
- int i = addr - 0x5C00;
- if ( (unsigned) i < mmc5_exram_size )
- return this->mmc5.exram [i];
-
- int m = addr - 0x5205;
- if ( (unsigned) m < 2 )
- return (this->mmc5_mul [0] * this->mmc5_mul [1]) >> (m * 8) & 0xFF;
- }
- #endif
- result = addr >> 8; // simulate open bus
- }
- }
- }
-
- /* if ( addr != 0x2002 )
- debug_printf( "Read unmapped $%.4X\n", (unsigned) addr ); */
-
- return result;
-}
-
-void Cpu_write( struct Nsf_Emu* this, nes_addr_t addr, int data )
-{
- int offset = addr - sram_addr;
- if ( (unsigned) offset < sram_size )
- {
- this->sram [offset] = data;
- }
- else
- {
- // after sram because cpu handles most low_ram accesses internally already
- int temp = addr & (low_ram_size-1); // also handles wrap-around
- if ( !(addr & 0xE000) )
- {
- this->cpu.low_mem [temp] = data;
- }
- else
- {
- int bank = addr - banks_addr;
- if ( (unsigned) bank < bank_count )
- {
- Write_bank( this, bank, data );
- }
- else if ( (unsigned) (addr - start_addr) <= end_addr - start_addr )
- {
- Apu_write_register( &this->apu, Cpu_time( &this->cpu ), addr, data );
- }
- else
- {
- #ifndef NSF_EMU_APU_ONLY
- // 0x8000-0xDFFF is writable
- int i = addr - 0x8000;
- if ( fds_enabled( this ) && (unsigned) i < fdsram_size )
- fdsram( this ) [i] = data;
- else
- #endif
- Cpu_write_misc( this, addr, data );
- }
- }
- }
-}
-
-#define CPU_READ( emu, addr, time ) Cpu_read( emu, addr )
-#define CPU_WRITE( emu, addr, data, time ) Cpu_write( emu, addr, data )
diff --git a/apps/codecs/libgme/nes_fme7_apu.c b/apps/codecs/libgme/nes_fme7_apu.c
index 8a2c21e..3e47e0b 100644
--- a/apps/codecs/libgme/nes_fme7_apu.c
+++ b/apps/codecs/libgme/nes_fme7_apu.c
@@ -64,8 +64,6 @@ void Fme7_run_until( struct Nes_Fme7_Apu* this, blip_time_t end_time )
struct Blip_Buffer* const osc_output = this->oscs [index].output;
if ( !osc_output )
continue;
- /* osc_output->set_modified(); */
- Blip_set_modified( osc_output );
// check for unsupported mode
#ifndef NDEBUG
@@ -92,11 +90,13 @@ void Fme7_run_until( struct Nes_Fme7_Apu* this, blip_time_t end_time )
int amp = volume;
if ( !this->phases [index] )
amp = 0;
+
{
int delta = amp - this->oscs [index].last_amp;
if ( delta )
{
this->oscs [index].last_amp = amp;
+ Blip_set_modified( osc_output );
Synth_offset( &this->synth, this->last_time, delta, osc_output );
}
}
@@ -105,6 +105,7 @@ void Fme7_run_until( struct Nes_Fme7_Apu* this, blip_time_t end_time )
if ( time < end_time )
{
int delta = amp * 2 - volume;
+ Blip_set_modified( osc_output );
if ( volume )
{
do
@@ -123,7 +124,7 @@ void Fme7_run_until( struct Nes_Fme7_Apu* this, blip_time_t end_time )
// maintain phase when silent
int count = (end_time - time + period - 1) / period;
this->phases [index] ^= count & 1;
- time += (blargg_long) count * period;
+ time += count * period;
}
}
diff --git a/apps/codecs/libgme/nes_fme7_apu.h b/apps/codecs/libgme/nes_fme7_apu.h
index 353d82d..c0eac4c 100644
--- a/apps/codecs/libgme/nes_fme7_apu.h
+++ b/apps/codecs/libgme/nes_fme7_apu.h
@@ -1,6 +1,6 @@
// Sunsoft FME-7 sound emulator
-// Game_Music_Emu 0.5.5
+// Game_Music_Emu 0.6-pre
#ifndef NES_FME7_APU_H
#define NES_FME7_APU_H
diff --git a/apps/codecs/libgme/nes_namco_apu.c b/apps/codecs/libgme/nes_namco_apu.c
index 0f64d3c..34df200 100644
--- a/apps/codecs/libgme/nes_namco_apu.c
+++ b/apps/codecs/libgme/nes_namco_apu.c
@@ -68,8 +68,6 @@ void Namco_run_until( struct Nes_Namco_Apu* this, blip_time_t nes_end_time )
struct Blip_Buffer* output = osc->output;
if ( !output )
continue;
- /* output->set_modified(); */
- Blip_set_modified( output );
blip_resampled_time_t time =
Blip_resampled_time( output, this->last_time ) + osc->delay;
@@ -85,12 +83,17 @@ void Namco_run_until( struct Nes_Namco_Apu* this, blip_time_t nes_end_time )
if ( !volume )
continue;
- blargg_long freq = (osc_reg [4] & 3) * 0x10000 + osc_reg [2] * 0x100L + osc_reg [0];
+ int freq = (osc_reg [4] & 3) * 0x10000 + osc_reg [2] * 0x100L + osc_reg [0];
if ( freq < 64 * active_oscs )
continue; // prevent low frequencies from excessively delaying freq changes
+
+ int const master_clock_divider = 12; // NES time derived via divider of master clock
+ int const n106_divider = 45; // N106 then divides master clock by this
+ int const max_freq = 0x3FFFF;
+ int const lowest_freq_period = (max_freq + 1) * n106_divider / master_clock_divider;
+ // divide by 8 to avoid overflow
blip_resampled_time_t period =
- /* output->resampled_duration( 983040 ) / freq * active_oscs; */
- Blip_resampled_duration( output, 983040 ) / freq * active_oscs;
+ Blip_resampled_duration( output, lowest_freq_period / 8 ) / freq * 8 * active_oscs;
int wave_size = 32 - (osc_reg [4] >> 2 & 7) * 4;
if ( !wave_size )
@@ -99,6 +102,8 @@ void Namco_run_until( struct Nes_Namco_Apu* this, blip_time_t nes_end_time )
int last_amp = osc->last_amp;
int wave_pos = osc->wave_pos;
+ Blip_set_modified( output );
+
do
{
// read wave sample
diff --git a/apps/codecs/libgme/nes_namco_apu.h b/apps/codecs/libgme/nes_namco_apu.h
index c47eacc..c428c89 100644
--- a/apps/codecs/libgme/nes_namco_apu.h
+++ b/apps/codecs/libgme/nes_namco_apu.h
@@ -15,13 +15,13 @@ enum { namco_data_reg_addr = 0x4800 };
enum { namco_reg_count = 0x80 };
struct Namco_Osc {
- blargg_long delay;
+ int delay;
struct Blip_Buffer* output;
short last_amp;
short wave_pos;
};
-struct Nes_Namco_Apu {
+struct Nes_Namco_Apu {
struct Namco_Osc oscs [namco_osc_count];
blip_time_t last_time;
diff --git a/apps/codecs/libgme/nes_oscs.c b/apps/codecs/libgme/nes_oscs.c
index 4402b60..f97ce21 100644
--- a/apps/codecs/libgme/nes_oscs.c
+++ b/apps/codecs/libgme/nes_oscs.c
@@ -90,7 +90,7 @@ static inline nes_time_t Square_maintain_phase( struct Nes_Square* this, nes_tim
{
int count = (remain + timer_period - 1) / timer_period;
this->phase = (this->phase + count) & (square_phase_range - 1);
- time += (blargg_long) count * timer_period;
+ time += count * timer_period;
}
return time;
}
@@ -107,8 +107,6 @@ void Square_run( struct Nes_Square* this, nes_time_t time, nes_time_t end_time )
return;
}
- Blip_set_modified( osc->output );
-
int offset = period >> (osc->regs [1] & shift_mask);
if ( osc->regs [1] & negate_flag )
offset = 0;
@@ -117,6 +115,7 @@ void Square_run( struct Nes_Square* this, nes_time_t time, nes_time_t end_time )
if ( volume == 0 || period < 8 || (period + offset) >= 0x800 )
{
if ( osc->last_amp ) {
+ Blip_set_modified( osc->output );
Synth_offset( this->synth, time, -osc->last_amp, osc->output );
osc->last_amp = 0;
}
@@ -137,6 +136,7 @@ void Square_run( struct Nes_Square* this, nes_time_t time, nes_time_t end_time )
if ( this->phase < duty )
amp ^= volume;
+ Blip_set_modified( osc->output );
{
int delta = Osc_update_amp( osc, amp );
if ( delta )
@@ -201,7 +201,7 @@ static inline nes_time_t Triangle_maintain_phase( struct Nes_Triangle* this, nes
int count = (remain + timer_period - 1) / timer_period;
this->phase = ((unsigned) this->phase + 1 - count) & (Triangle_phase_range * 2 - 1);
this->phase++;
- time += (blargg_long) count * timer_period;
+ time += count * timer_period;
}
return time;
}
@@ -219,14 +219,15 @@ void Triangle_run( struct Nes_Triangle* this, nes_time_t time, nes_time_t end_ti
return;
}
- Blip_set_modified( osc->output );
-
// to do: track phase when period < 3
// to do: Output 7.5 on dac when period < 2? More accurate, but results in more clicks.
int delta = Osc_update_amp( osc, Triangle_calc_amp( this ) );
if ( delta )
+ {
+ Blip_set_modified( osc->output );
Synth_offset( &this->synth, time, delta, osc->output );
+ }
time += osc->delay;
if ( osc->length_counter == 0 || this->linear_counter == 0 || timer_period < 3 )
@@ -243,13 +244,15 @@ void Triangle_run( struct Nes_Triangle* this, nes_time_t time, nes_time_t end_ti
phase -= Triangle_phase_range;
volume = -volume;
}
+ Blip_set_modified( osc->output );
do {
if ( --phase == 0 ) {
phase = Triangle_phase_range;
volume = -volume;
}
- else {
+ else
+ {
Synth_offset_inline( &this->synth, time, volume, output );
}
@@ -340,18 +343,27 @@ static inline void Dmc_reload_sample( struct Nes_Dmc* this )
this->osc.length_counter = this->osc.regs [3] * 0x10 + 1;
}
-static byte const dac_table [128] =
+static int const dmc_table [128] =
{
- 0, 1, 2, 3, 4, 5, 6, 7, 7, 8, 9,10,11,12,13,14,
- 15,15,16,17,18,19,20,20,21,22,23,24,24,25,26,27,
- 27,28,29,30,31,31,32,33,33,34,35,36,36,37,38,38,
- 39,40,41,41,42,43,43,44,45,45,46,47,47,48,48,49,
- 50,50,51,52,52,53,53,54,55,55,56,56,57,58,58,59,
- 59,60,60,61,61,62,63,63,64,64,65,65,66,66,67,67,
- 68,68,69,70,70,71,71,72,72,73,73,74,74,75,75,75,
- 76,76,77,77,78,78,79,79,80,80,81,81,82,82,82,83,
+ 0, 24, 48, 71, 94, 118, 141, 163, 186, 209, 231, 253, 275, 297, 319, 340,
+ 361, 383, 404, 425, 445, 466, 486, 507, 527, 547, 567, 587, 606, 626, 645, 664,
+ 683, 702, 721, 740, 758, 777, 795, 813, 832, 850, 867, 885, 903, 920, 938, 955,
+ 972, 989,1006,1023,1040,1056,1073,1089,1105,1122,1138,1154,1170,1185,1201,1217,
+1232,1248,1263,1278,1293,1308,1323,1338,1353,1368,1382,1397,1411,1425,1440,1454,
+1468,1482,1496,1510,1523,1537,1551,1564,1578,1591,1604,1618,1631,1644,1657,1670,
+1683,1695,1708,1721,1733,1746,1758,1771,1783,1795,1807,1819,1831,1843,1855,1867,
+1879,1890,1902,1914,1925,1937,1948,1959,1971,1982,1993,2004,2015,2026,2037,2048,
};
+static inline int update_amp_nonlinear( struct Nes_Dmc* this, int in )
+{
+ if ( !this->nonlinear )
+ in = dmc_table [in];
+ int delta = in - this->osc.last_amp;
+ this->osc.last_amp = in;
+ return delta;
+}
+
void Dmc_write_register( struct Nes_Dmc* this, int addr, int data )
{
if ( addr == 0 )
@@ -363,14 +375,7 @@ void Dmc_write_register( struct Nes_Dmc* this, int addr, int data )
}
else if ( addr == 1 )
{
- int old_dac = this->dac;
this->dac = data & 0x7F;
-
- // adjust last_amp so that "pop" amplitude will be properly non-linear
- // with respect to change in dac
- int faked_nonlinear = this->dac - (dac_table [this->dac] - dac_table [old_dac]);
- if ( !this->nonlinear )
- this->osc.last_amp = faked_nonlinear;
}
}
@@ -407,16 +412,15 @@ void Dmc_fill_buffer( struct Nes_Dmc* this )
void Dmc_run( struct Nes_Dmc* this, nes_time_t time, nes_time_t end_time )
{
struct Nes_Osc* osc = &this->osc;
- int delta = Osc_update_amp( osc, this->dac );
+ int delta = update_amp_nonlinear( this, this->dac );
if ( !osc->output )
{
this->silence = true;
}
- else
+ else if ( delta )
{
Blip_set_modified( osc->output );
- if ( delta )
- Synth_offset( &this->synth, time, delta, osc->output );
+ Synth_offset( &this->synth, time, delta, osc->output );
}
time += osc->delay;
@@ -435,6 +439,8 @@ void Dmc_run( struct Nes_Dmc* this, nes_time_t time, nes_time_t end_time )
const int period = this->period;
int bits = this->bits;
int dac = this->dac;
+ if ( output )
+ Blip_set_modified( output );
do
{
@@ -444,7 +450,7 @@ void Dmc_run( struct Nes_Dmc* this, nes_time_t time, nes_time_t end_time )
bits >>= 1;
if ( (unsigned) (dac + step) <= 0x7F ) {
dac += step;
- Synth_offset_inline( &this->synth, time, step, output );
+ Synth_offset_inline( &this->synth, time, update_amp_nonlinear( this, dac ), output );
}
}
@@ -456,7 +462,8 @@ void Dmc_run( struct Nes_Dmc* this, nes_time_t time, nes_time_t end_time )
if ( !this->buf_full ) {
this->silence = true;
}
- else {
+ else
+ {
this->silence = false;
bits = this->buf;
this->buf_full = false;
@@ -469,7 +476,7 @@ void Dmc_run( struct Nes_Dmc* this, nes_time_t time, nes_time_t end_time )
while ( time < end_time );
this->dac = dac;
- osc->last_amp = dac;
+ //osc->last_amp = dac;
this->bits = bits;
}
this->bits_remain = bits_remain;
@@ -519,14 +526,15 @@ void Noise_run( struct Nes_Noise* this, nes_time_t time, nes_time_t end_time )
return;
}
- Blip_set_modified( osc->output );
-
const int volume = Noise_volume( this );
int amp = (this->noise & 1) ? volume : 0;
{
int delta = Osc_update_amp( osc, amp );
if ( delta )
+ {
+ Blip_set_modified( osc->output );
Synth_offset( &this->synth, time, delta, osc->output );
+ }
}
time += osc->delay;
@@ -557,6 +565,7 @@ void Noise_run( struct Nes_Noise* this, nes_time_t time, nes_time_t end_time )
int noise = this->noise;
int delta = amp * 2 - volume;
const int tap = (osc->regs [2] & mode_flag ? 8 : 13);
+ Blip_set_modified( osc->output );
do {
int feedback = (noise << tap) ^ (noise << 14);
diff --git a/apps/codecs/libgme/nes_oscs.h b/apps/codecs/libgme/nes_oscs.h
index fa6b8ab..1eeb302 100644
--- a/apps/codecs/libgme/nes_oscs.h
+++ b/apps/codecs/libgme/nes_oscs.h
@@ -46,7 +46,7 @@ enum { shift_mask = 0x07 };
enum { square_phase_range = 8 };
typedef struct Blip_Synth Synth;
-
+
struct Nes_Square
{
struct Nes_Osc osc;
@@ -54,12 +54,12 @@ struct Nes_Square
int env_delay;
int phase;
int sweep_delay;
-
+
Synth* synth; // shared between squares
};
static inline void Square_set_synth( struct Nes_Square* this, Synth* s ) { this->synth = s; }
-
+
void Square_clock_sweep( struct Nes_Square* this, int adjust );
void Square_run( struct Nes_Square* this, nes_time_t, nes_time_t );
@@ -73,15 +73,15 @@ static inline void Square_reset( struct Nes_Square* this )
void Square_clock_envelope( struct Nes_Square* this );
int Square_volume( struct Nes_Square* this );
-
+
// Nes_Triangle
enum { Triangle_phase_range = 16 };
-
+
struct Nes_Triangle
{
struct Nes_Osc osc;
-
+
int phase;
int linear_counter;
struct Blip_Synth synth;
@@ -123,11 +123,11 @@ static inline void Noise_reset( struct Nes_Noise* this )
// Nes_Dmc
enum { loop_flag = 0x40 };
-
+
struct Nes_Dmc
{
struct Nes_Osc osc;
-
+
int address; // address of next byte to read
int period;
int buf;
@@ -144,7 +144,7 @@ struct Nes_Dmc
bool pal_mode;
bool nonlinear;
- int (*prg_reader)( void*, addr_t ); // needs to be initialized to prg read function
+ int (*prg_reader)( void*, int ); // needs to be initialized to prg read function
void* prg_reader_data;
struct Nes_Apu* apu;
diff --git a/apps/codecs/libgme/nes_vrc6_apu.c b/apps/codecs/libgme/nes_vrc6_apu.c
index f712216..55fccb7 100644
--- a/apps/codecs/libgme/nes_vrc6_apu.c
+++ b/apps/codecs/libgme/nes_vrc6_apu.c
@@ -83,7 +83,6 @@ void run_square( struct Nes_Vrc6_Apu* this, struct Vrc6_Osc* osc, blip_time_t en
struct Blip_Buffer* output = osc->output;
if ( !output )
return;
- Blip_set_modified( output );
int volume = osc->regs [0] & 15;
if ( !(osc->regs [2] & 0x80) )
@@ -96,6 +95,7 @@ void run_square( struct Nes_Vrc6_Apu* this, struct Vrc6_Osc* osc, blip_time_t en
if ( delta )
{
osc->last_amp += delta;
+ Blip_set_modified( output );
Synth_offset( &this->square_synth, time, delta, output );
}
diff --git a/apps/codecs/libgme/nes_vrc6_apu.h b/apps/codecs/libgme/nes_vrc6_apu.h
index 8f3cbf4..57b8a42 100644
--- a/apps/codecs/libgme/nes_vrc6_apu.h
+++ b/apps/codecs/libgme/nes_vrc6_apu.h
@@ -1,6 +1,6 @@
// Konami VRC6 sound chip emulator
-// Nes_Snd_Emu 0.1.8
+// Nes_Snd_Emu 0.2.0-pre
#ifndef NES_VRC6_APU_H
#define NES_VRC6_APU_H
@@ -14,7 +14,7 @@ enum { vrc6_addr_step = 0x1000 };
struct Vrc6_Osc
{
- uint8_t regs [vrc6_reg_count];
+ uint8_t regs [3];
struct Blip_Buffer* output;
int delay;
int last_amp;
diff --git a/apps/codecs/libgme/nsf_emu.c b/apps/codecs/libgme/nsf_emu.c
index a4873c8..705c345 100644
--- a/apps/codecs/libgme/nsf_emu.c
+++ b/apps/codecs/libgme/nsf_emu.c
@@ -1,4 +1,4 @@
-// Game_Music_Emu 0.5.5. http://www.slack.net/~ant/
+// Game_Music_Emu 0.6-pre. http://www.slack.net/~ant/
#include "nsf_emu.h"
#include "multi_buffer.h"
@@ -19,33 +19,19 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
#include "blargg_source.h"
const char gme_wrong_file_type [] = "Wrong file type for this emulator";
-long const clock_divisor = 12;
-
-int const stereo = 2; // number of channels for stereo
-int const silence_max = 6; // seconds
-int const silence_threshold = 0x10;
-long const fade_block_size = 512;
-int const fade_shift = 8; // fade ends with gain at 1.0 / (1 << fade_shift)
// number of frames until play interrupts init
int const initial_play_delay = 7; // KikiKaikai needed this to work
+int const bank_size = 0x1000;
int const rom_addr = 0x8000;
static void clear_track_vars( struct Nsf_Emu* this )
{
this->current_track = -1;
- this->out_time = 0;
- this->emu_time = 0;
- this->emu_track_ended_ = true;
- this->track_ended = true;
- this->fade_start = INT_MAX / 2 + 1;
- this->fade_step = 1;
- this->silence_time = 0;
- this->silence_count = 0;
- this->buf_remain = 0;
+ track_stop( &this->track_filter );
}
-static int pcm_read( void* emu, addr_t addr )
+static int pcm_read( void* emu, int addr )
{
return *Cpu_get_code( &((struct Nsf_Emu*) emu)->cpu, addr );
}
@@ -58,18 +44,16 @@ void Nsf_init( struct Nsf_Emu* this )
this->gain = (int)(FP_ONE_GAIN);
// defaults
- this->max_initial_silence = 2;
- this->ignore_silence = false;
- this->voice_count = 0;
+ this->tfilter = *track_get_setup( &this->track_filter );
+ this->tfilter.max_initial = 2;
+ this->tfilter.lookahead = 6;
+ this->track_filter.silence_ignored_ = false;
// Set sound gain
Sound_set_gain( this, (int)(FP_ONE_GAIN*1.2) );
- // Unload
- clear_track_vars( this );
-
// Init rom
- Rom_init( &this->rom, 0x1000 );
+ Rom_init( &this->rom, bank_size );
// Init & clear nsfe info
Info_init( &this->info );
@@ -78,16 +62,36 @@ void Nsf_init( struct Nsf_Emu* this )
Cpu_init( &this->cpu );
Apu_init( &this->apu );
Apu_dmc_reader( &this->apu, pcm_read, this );
+
+ // Unload
+ this->voice_count = 0;
+ memset( this->voice_types, 0, sizeof this->voice_types );
+ clear_track_vars( this );
}
// Setup
+static void append_voices( struct Nsf_Emu* this, int const types [], int count )
+{
+ assert( this->voice_count + count < max_voices );
+ int i;
+ for ( i = 0; i < count; i++ ) {
+ this->voice_types [this->voice_count + i] = types [i];
+ }
+ this->voice_count += count;
+}
+
static blargg_err_t init_sound( struct Nsf_Emu* this )
{
- /* if ( header_.chip_flags & ~(fds_flag | namco_flag | vrc6_flag | fme7_flag) )
+/* if ( header_.chip_flags & ~(fds_flag | namco_flag | vrc6_flag | fme7_flag) )
warning( "Uses unsupported audio expansion hardware" ); **/
- this->voice_count = apu_osc_count;
+ {
+ static int const types [apu_osc_count] = {
+ wave_type+1, wave_type+2, mixed_type+1, noise_type+0, mixed_type+1
+ };
+ append_voices( this, types, apu_osc_count );
+ }
int adjusted_gain = (this->gain * 4) / 3;
@@ -103,7 +107,10 @@ static blargg_err_t init_sound( struct Nsf_Emu* this )
Vrc6_init( &this->vrc6 );
adjusted_gain = (adjusted_gain*3) / 4;
- this->voice_count += vrc6_osc_count;
+ static int const types [vrc6_osc_count] = {
+ wave_type+3, wave_type+4, wave_type+5,
+ };
+ append_voices( this, types, vrc6_osc_count );
}
if ( fme7_enabled( this ) )
@@ -111,7 +118,10 @@ static blargg_err_t init_sound( struct Nsf_Emu* this )
Fme7_init( &this->fme7 );
adjusted_gain = (adjusted_gain*3) / 4;
- this->voice_count += fme7_osc_count;
+ static int const types [fme7_osc_count] = {
+ wave_type+3, wave_type+4, wave_type+5,
+ };
+ append_voices( this, types, fme7_osc_count );
}
if ( mmc5_enabled( this ) )
@@ -119,7 +129,11 @@ static blargg_err_t init_sound( struct Nsf_Emu* this )
Mmc5_init( &this->mmc5 );
adjusted_gain = (adjusted_gain*3) / 4;
- this->voice_count += mmc5_osc_count;
+
+ static int const types [mmc5_osc_count] = {
+ wave_type+3, wave_type+4, mixed_type+2
+ };
+ append_voices( this, types, mmc5_osc_count );
}
if ( fds_enabled( this ) )
@@ -127,7 +141,10 @@ static blargg_err_t init_sound( struct Nsf_Emu* this )
Fds_init( &this->fds );
adjusted_gain = (adjusted_gain*3) / 4;
- this->voice_count += fds_osc_count ;
+ static int const types [fds_osc_count] = {
+ wave_type+0
+ };
+ append_voices( this, types, fds_osc_count );
}
if ( namco_enabled( this ) )
@@ -135,22 +152,31 @@ static blargg_err_t init_sound( struct Nsf_Emu* this )
Namco_init( &this->namco );
adjusted_gain = (adjusted_gain*3) / 4;
- this->voice_count += namco_osc_count;
+ static int const types [namco_osc_count] = {
+ wave_type+3, wave_type+4, wave_type+5, wave_type+ 6,
+ wave_type+7, wave_type+8, wave_type+9, wave_type+10,
+ };
+ append_voices( this, types, namco_osc_count );
}
- if ( vrc7_enabled( this ) )
- {
#ifndef NSF_EMU_NO_VRC7
+ if ( vrc7_enabled( this ) )
+ {
+
Vrc7_init( &this->vrc7 );
Vrc7_set_rate( &this->vrc7, this->sample_rate );
- #endif
- adjusted_gain = (adjusted_gain*3) / 4;
+ adjusted_gain = (adjusted_gain*3) / 4;
+
+ static int const types [vrc7_osc_count] = {
+ wave_type+3, wave_type+4, wave_type+5, wave_type+6,
+ wave_type+7, wave_type+8
+ };
+ append_voices( this, types, vrc7_osc_count );
+ }
- this->voice_count += vrc7_osc_count;
- }
-
- if ( vrc7_enabled( this ) ) Vrc7_volume( &this->vrc7, adjusted_gain );
+ if ( vrc7_enabled( this ) ) Vrc7_volume( &this->vrc7, adjusted_gain );
+ #endif
if ( namco_enabled( this ) ) Namco_volume( &this->namco, adjusted_gain );
if ( vrc6_enabled( this ) ) Vrc6_volume( &this->vrc6, adjusted_gain );
if ( fme7_enabled( this ) ) Fme7_volume( &this->fme7, adjusted_gain );
@@ -179,9 +205,9 @@ static bool pal_only( struct header_t* this )
return (this->speed_flags & 3) == 1;
}
-static long clock_rate( struct header_t* this )
+static int clock_rate( struct header_t* this )
{
- return pal_only( this ) ? (long)1662607.125 : (long)1789772.727272727;
+ return pal_only( this ) ? (int)1662607.125 : (int)1789772.727272727;
}
static int play_period( struct header_t* this )
@@ -227,7 +253,7 @@ static blargg_err_t check_nsf_header( struct header_t* h )
return 0;
}
-blargg_err_t Nsf_load( struct Nsf_Emu* this, void* data, long size )
+blargg_err_t Nsf_load_mem( struct Nsf_Emu* this, void* data, long size )
{
// Unload
Info_unload( &this->info ); // TODO: extremely hacky!
@@ -258,11 +284,10 @@ blargg_err_t Nsf_post_load( struct Nsf_Emu* this )
warning( "Unknown file version" ); */
// set up data
- addr_t load_addr = get_le16( this->header.load_addr );
-
+ addr_t load_addr = get_addr( this->header.load_addr );
/* if ( load_addr < (fds_enabled() ? sram_addr : rom_addr) )
warning( "Load address is too low" ); */
-
+
Rom_set_addr( &this->rom, load_addr % this->rom.bank_size );
/* if ( header_.vers != 1 )
@@ -277,17 +302,16 @@ blargg_err_t Nsf_post_load( struct Nsf_Emu* this )
// Post load
Sound_set_tempo( this, this->tempo );
-
- // Remute voices
Sound_mute_voices( this, this->mute_mask_ );
// Set track_count
this->track_count = this->header.track_count;
// Change clock rate & setup buffer
- this->clock_rate__ = (long) clock_rate( &this->header );
+ this->clock_rate__ = clock_rate( &this->header );
Buffer_clock_rate( &this->stereo_buf, this->clock_rate__ );
- this->buf_changed_count = Buffer_channels_changed_count( &this->stereo_buf );
+ RETURN_ERR( Buffer_set_channel_count( &this->stereo_buf, this->voice_count, this->voice_types ) );
+ this->buf_changed_count = Buffer_channels_changed_count( &this->stereo_buf );
return 0;
}
@@ -416,11 +440,13 @@ static void set_voice( struct Nsf_Emu* this, int i, struct Blip_Buffer* buf, str
return;
}
- if ( vrc7_enabled( this ) && (i -= vrc7_osc_count) < 0 )
- {
- Vrc7_set_output( &this->vrc7, i + vrc7_osc_count, buf );
- return;
- }
+ #ifndef NSF_EMU_NO_VRC7
+ if ( vrc7_enabled( this ) && (i -= vrc7_osc_count) < 0 )
+ {
+ Vrc7_set_output( &this->vrc7, i + vrc7_osc_count, buf );
+ return;
+ }
+ #endif
}
#endif
}
@@ -429,7 +455,7 @@ static void set_voice( struct Nsf_Emu* this, int i, struct Blip_Buffer* buf, str
// Music Emu
-blargg_err_t Nsf_set_sample_rate( struct Nsf_Emu* this, long rate )
+blargg_err_t Nsf_set_sample_rate( struct Nsf_Emu* this, int rate )
{
require( !this->sample_rate ); // sample rate can't be changed once set
Buffer_init( &this->stereo_buf );
@@ -439,6 +465,8 @@ blargg_err_t Nsf_set_sample_rate( struct Nsf_Emu* this, long rate )
Buffer_bass_freq( &this->stereo_buf, 80 );
this->sample_rate = rate;
+ RETURN_ERR( track_init( &this->track_filter, this ) );
+ this->tfilter.max_silence = 6 * stereo * this->sample_rate;
return 0;
}
@@ -466,7 +494,7 @@ void Sound_mute_voices( struct Nsf_Emu* this, int mask )
}
else
{
- struct channel_t ch = Buffer_channel( &this->stereo_buf );
+ struct channel_t ch = Buffer_channel( &this->stereo_buf, i );
assert( (ch.center && ch.left && ch.right) ||
(!ch.center && !ch.left && !ch.right) ); // all or nothing
set_voice( this, i, ch.center, ch.left, ch.right );
@@ -533,32 +561,13 @@ int cpu_read( struct Nsf_Emu* this, addr_t addr )
return addr >> 8;
}
-#if 0 /* function currently unused */
-static int unmapped_read( struct Nsf_Emu* this, addr_t addr )
-{
- (void) this;
-
- switch ( addr )
- {
- case 0x2002:
- case 0x4016:
- case 0x4017:
- return addr >> 8;
- }
-
- // Unmapped read
- return addr >> 8;
-}
-#endif
-
void cpu_write( struct Nsf_Emu* this, addr_t addr, int data )
{
#ifndef NSF_EMU_APU_ONLY
{
- nes_time_t time = Cpu_time( &this->cpu );
if ( fds_enabled( this) && (unsigned) (addr - fds_io_addr) < fds_io_size )
{
- Fds_write( &this->fds, time, addr, data );
+ Fds_write( &this->fds, Cpu_time( &this->cpu ), addr, data );
return;
}
@@ -572,7 +581,7 @@ void cpu_write( struct Nsf_Emu* this, addr_t addr, int data )
if ( addr == namco_data_reg_addr )
{
- Namco_write_data( &this->namco, time, data );
+ Namco_write_data( &this->namco, Cpu_time( &this->cpu ), data );
return;
}
}
@@ -583,7 +592,7 @@ void cpu_write( struct Nsf_Emu* this, addr_t addr, int data )
int osc = (unsigned) (addr - vrc6_base_addr) / vrc6_addr_step;
if ( (unsigned) osc < vrc6_osc_count && (unsigned) reg < vrc6_reg_count )
{
- Vrc6_write_osc( &this->vrc6, time, osc, reg, data );
+ Vrc6_write_osc( &this->vrc6, Cpu_time( &this->cpu ), osc, reg, data );
return;
}
}
@@ -597,7 +606,7 @@ void cpu_write( struct Nsf_Emu* this, addr_t addr, int data )
return;
case fme7_data_addr:
- Fme7_write_data( &this->fme7, time, data );
+ Fme7_write_data( &this->fme7, Cpu_time( &this->cpu ), data );
return;
}
}
@@ -606,7 +615,7 @@ void cpu_write( struct Nsf_Emu* this, addr_t addr, int data )
{
if ( (unsigned) (addr - mmc5_regs_addr) < mmc5_regs_size )
{
- Mmc5_write_register( &this->mmc5, time, addr, data );
+ Mmc5_write_register( &this->mmc5, Cpu_time( &this->cpu ), addr, data );
return;
}
@@ -625,49 +634,28 @@ void cpu_write( struct Nsf_Emu* this, addr_t addr, int data )
}
}
- if ( vrc7_enabled( this) )
- {
- if ( addr == 0x9010 )
+ #ifndef NSF_EMU_NO_VRC7
+ if ( vrc7_enabled( this) )
{
- Vrc7_write_reg( &this->vrc7, data );
- return;
- }
+ if ( addr == 0x9010 )
+ {
+ Vrc7_write_reg( &this->vrc7, data );
+ return;
+ }
- if ( (unsigned) (addr - 0x9028) <= 0x08 )
- {
- Vrc7_write_data( &this->vrc7, time, data );
- return;
+ if ( (unsigned) (addr - 0x9028) <= 0x08 )
+ {
+ Vrc7_write_data( &this->vrc7, Cpu_time( &this->cpu ), data );
+ return;
+ }
}
- }
+ #endif
}
#endif
// Unmapped_write
}
-#if 0 /* function currently unused */
-static void unmapped_write( struct Nsf_Emu* this, addr_t addr, int data )
-{
- (void) data;
-
- switch ( addr )
- {
- case 0x8000: // some write to $8000 and $8001 repeatedly
- case 0x8001:
- case 0x4800: // probably namco sound mistakenly turned on in MCK
- case 0xF800:
- case 0xFFF8: // memory mapper?
- return;
- }
-
- if ( mmc5_enabled( this ) && addr == 0x5115 ) return;
-
- // FDS memory
- if ( fds_enabled( this ) && (unsigned) (addr - 0x8000) < 0x6000 ) return;
-}
-#endif
-
-void fill_buf( struct Nsf_Emu* this );
blargg_err_t Nsf_start_track( struct Nsf_Emu* this, int track )
{
clear_track_vars( this );
@@ -695,7 +683,9 @@ blargg_err_t Nsf_start_track( struct Nsf_Emu* this, int track )
if ( vrc6_enabled( this ) ) Vrc6_reset( &this->vrc6 );
if ( fme7_enabled( this ) ) Fme7_reset( &this->fme7 );
if ( mmc5_enabled( this ) ) Apu_reset( &this->mmc5.apu, false, 0 );
- if ( vrc7_enabled( this ) ) Vrc7_reset( &this->vrc7 );
+ #ifndef NSF_EMU_NO_VRC7
+ if ( vrc7_enabled( this ) ) Vrc7_reset( &this->vrc7 );
+ #endif
#endif
int speed_flags = 0;
@@ -728,27 +718,15 @@ blargg_err_t Nsf_start_track( struct Nsf_Emu* this, int track )
/* if ( this->cpu.r.pc < get_addr( header.load_addr ) )
warning( "Init address < load address" ); */
- this->emu_track_ended_ = false;
- this->track_ended = false;
+ // convert filter times to samples
+ struct setup_t s = this->tfilter;
+ s.max_initial *= this->sample_rate * stereo;
+ #ifdef GME_DISABLE_SILENCE_LOOKAHEAD
+ s.lookahead = 1;
+ #endif
+ track_setup( &this->track_filter, &s );
- if ( !this->ignore_silence )
- {
- // play until non-silence or end of track
- long end;
- for ( end = this->max_initial_silence * stereo * this->sample_rate; this->emu_time < end; )
- {
- fill_buf( this );
- if ( this->buf_remain | (int) this->emu_track_ended_ )
- break;
- }
-
- this->emu_time = this->buf_remain;
- this->out_time = 0;
- this->silence_time = 0;
- this->silence_count = 0;
- }
- /* return track_ended() ? warning() : 0; */
- return 0;
+ return track_start( &this->track_filter );
}
void run_once( struct Nsf_Emu* this, nes_time_t end )
@@ -831,265 +809,99 @@ static void end_frame( struct Nsf_Emu* this, nes_time_t end )
if ( mmc5_enabled( this ) ) Apu_end_frame( &this->mmc5.apu, end );
if ( namco_enabled( this ) ) Namco_end_frame( &this->namco, end );
if ( vrc6_enabled( this ) ) Vrc6_end_frame( &this->vrc6, end );
+ #ifndef NSF_EMU_NO_VRC7
if ( vrc7_enabled( this ) ) Vrc7_end_frame( &this->vrc7, end );
+ #endif
#endif
}
// Tell/Seek
-static blargg_long msec_to_samples( long sample_rate, blargg_long msec )
+static int msec_to_samples( int msec, int sample_rate )
{
- blargg_long sec = msec / 1000;
+ int sec = msec / 1000;
msec -= sec * 1000;
return (sec * sample_rate + msec * sample_rate / 1000) * stereo;
}
-long Track_tell( struct Nsf_Emu* this )
+int Track_tell( struct Nsf_Emu* this )
{
- blargg_long rate = this->sample_rate * stereo;
- blargg_long sec = this->out_time / rate;
- return sec * 1000 + (this->out_time - sec * rate) * 1000 / rate;
+ int rate = this->sample_rate * stereo;
+ int sec = track_sample_count( &this->track_filter ) / rate;
+ return sec * 1000 + (track_sample_count( &this->track_filter ) - sec * rate) * 1000 / rate;
}
-blargg_err_t Track_seek( struct Nsf_Emu* this, long msec )
+blargg_err_t Track_seek( struct Nsf_Emu* this, int msec )
{
- blargg_long time = msec_to_samples( this->sample_rate, msec );
- if ( time < this->out_time )
- RETURN_ERR( Nsf_start_track( this, this->current_track ) );
- return Track_skip( this, time - this->out_time );
+ int time = msec_to_samples( msec, this->sample_rate );
+ if ( time < track_sample_count( &this->track_filter ) )
+ RETURN_ERR( Nsf_start_track( this, this->current_track ) );
+ return Track_skip( this, time - track_sample_count( &this->track_filter ) );
}
-blargg_err_t skip_( struct Nsf_Emu* this, long count );
-blargg_err_t Track_skip( struct Nsf_Emu* this, long count )
+blargg_err_t Track_skip( struct Nsf_Emu* this, int count )
{
require( this->current_track >= 0 ); // start_track() must have been called already
- this->out_time += count;
-
- // remove from silence and buf first
- {
- long n = min( count, this->silence_count );
- this->silence_count -= n;
- count -= n;
-
- n = min( count, this->buf_remain );
- this->buf_remain -= n;
- count -= n;
- }
-
- if ( count && !this->emu_track_ended_ )
- {
- this->emu_time += count;
- // End track if error
- if ( skip_( this, count ) )
- this->emu_track_ended_ = true;
- }
-
- if ( !(this->silence_count | this->buf_remain) ) // caught up to emulator, so update track ended
- this->track_ended |= this->emu_track_ended_;
-
- return 0;
+ return track_skip( &this->track_filter, count );
}
-blargg_err_t play_( struct Nsf_Emu* this, long count, sample_t* out );
-blargg_err_t skip_( struct Nsf_Emu* this, long count )
+blargg_err_t skip_( void *emu, int count )
{
+ struct Nsf_Emu* this = (struct Nsf_Emu*) emu;
+
// for long skip, mute sound
- const long threshold = 30000;
+ const int threshold = 32768;
if ( count > threshold )
{
int saved_mute = this->mute_mask_;
Sound_mute_voices( this, ~0 );
-
- while ( count > threshold / 2 && !this->emu_track_ended_ )
- {
- RETURN_ERR( play_( this, buf_size, this->buf ) );
- count -= buf_size;
- }
-
- Sound_mute_voices( this, saved_mute );
- }
-
- while ( count && !this->emu_track_ended_ )
- {
- long n = buf_size;
- if ( n > count )
- n = count;
- count -= n;
- RETURN_ERR( play_( this, n, this->buf ) );
- }
- return 0;
-}
-
-// Fading
-void Track_set_fade( struct Nsf_Emu* this, long start_msec, long length_msec )
-{
- this->fade_step = this->sample_rate * length_msec / (fade_block_size * fade_shift * 1000 / stereo);
- this->fade_start = msec_to_samples( this->sample_rate, start_msec );
-}
-
-// unit / pow( 2.0, (double) x / step )
-static int int_log( blargg_long x, int step, int unit )
-{
- int shift = x / step;
- int fraction = (x - shift * step) * unit / step;
- return ((unit - fraction) + (fraction >> 1)) >> shift;
-}
+ int n = count - threshold/2;
+ n &= ~(2048-1); // round to multiple of 2048
+ count -= n;
+ RETURN_ERR( skippy_( &this->track_filter, n ) );
-static void handle_fade( struct Nsf_Emu* this, long out_count, sample_t* out )
-{
- int i;
- for ( i = 0; i < out_count; i += fade_block_size )
- {
- int const shift = 14;
- int const unit = 1 << shift;
- int gain = int_log( (this->out_time + i - this->fade_start) / fade_block_size,
- this->fade_step, unit );
- if ( gain < (unit >> fade_shift) )
- this->track_ended = this->emu_track_ended_ = true;
-
- sample_t* io = &out [i];
- int count;
- for ( count = min( fade_block_size, out_count - i ); count; --count )
- {
- *io = (sample_t) ((*io * gain) >> shift);
- ++io;
- }
+ Sound_mute_voices( this, saved_mute );
}
-}
-
-// Silence detection
-void emu_play( struct Nsf_Emu* this, long count, sample_t* out );
-void emu_play( struct Nsf_Emu* this, long count, sample_t* out )
-{
- check( current_track_ >= 0 );
- this->emu_time += count;
- if ( this->current_track >= 0 && !this->emu_track_ended_ ) {
-
- // End track if error
- if ( play_( this, count, out ) )
- this->emu_track_ended_ = true;
- }
- else
- memset( out, 0, count * sizeof *out );
+ return skippy_( &this->track_filter, count );
}
-// number of consecutive silent samples at end
-static long count_silence( sample_t* begin, long size )
-{
- sample_t first = *begin;
- *begin = silence_threshold; // sentinel
- sample_t* p = begin + size;
- while ( (unsigned) (*--p + silence_threshold / 2) <= (unsigned) silence_threshold ) { }
- *begin = first;
- return size - (p - begin);
-}
+// Fading
-// fill internal buffer and check it for silence
-void fill_buf( struct Nsf_Emu* this )
+void Track_set_fade( struct Nsf_Emu* this, int start_msec, int length_msec )
{
- assert( !this->buf_remain );
- if ( !this->emu_track_ended_ )
- {
- emu_play( this, buf_size, this->buf );
- long silence = count_silence( this->buf, buf_size );
- if ( silence < buf_size )
- {
- this->silence_time = this->emu_time - silence;
- this->buf_remain = buf_size;
- return;
- }
- }
- this->silence_count += buf_size;
+ track_set_fade( &this->track_filter, msec_to_samples( start_msec, this->sample_rate ),
+ length_msec * this->sample_rate / (1000 / stereo) );
}
-blargg_err_t Nsf_play( struct Nsf_Emu* this, long out_count, sample_t* out )
+blargg_err_t Nsf_play( struct Nsf_Emu* this, int out_count, sample_t* out )
{
- if ( this->track_ended )
- {
- memset( out, 0, out_count * sizeof *out );
- }
- else
- {
- require( this->current_track >= 0 );
- require( out_count % stereo == 0 );
-
- assert( this->emu_time >= this->out_time );
-
- long pos = 0;
- if ( this->silence_count )
- {
- // during a run of silence, run emulator at >=2x speed so it gets ahead
- long ahead_time = this->silence_lookahead * (this->out_time + out_count - this->silence_time) + this->silence_time;
- while ( this->emu_time < ahead_time && !(this->buf_remain | this->emu_track_ended_) )
- fill_buf( this );
-
- // fill with silence
- pos = min( this->silence_count, out_count );
- memset( out, 0, pos * sizeof *out );
- this->silence_count -= pos;
-
- if ( this->emu_time - this->silence_time > silence_max * stereo * this->sample_rate )
- {
- this->track_ended = this->emu_track_ended_ = true;
- this->silence_count = 0;
- this->buf_remain = 0;
- }
- }
-
- if ( this->buf_remain )
- {
- // empty silence buf
- long n = min( this->buf_remain, out_count - pos );
- memcpy( &out [pos], this->buf + (buf_size - this->buf_remain), n * sizeof *out );
- this->buf_remain -= n;
- pos += n;
- }
-
- // generate remaining samples normally
- long remain = out_count - pos;
- if ( remain )
- {
- emu_play( this, remain, out + pos );
- this->track_ended |= this->emu_track_ended_;
-
- if ( !this->ignore_silence || this->out_time > this->fade_start )
- {
- // check end for a new run of silence
- long silence = count_silence( out + pos, remain );
- if ( silence < remain )
- this->silence_time = this->emu_time - silence;
-
- if ( this->emu_time - this->silence_time >= buf_size )
- fill_buf( this ); // cause silence detection on next play()
- }
- }
-
- if ( this->out_time > this->fade_start )
- handle_fade( this, out_count, out );
- }
- this->out_time += out_count;
- return 0;
+ require( this->current_track >= 0 );
+ require( out_count % stereo == 0 );
+ return track_play( &this->track_filter, out_count, out );
}
-blargg_err_t play_( struct Nsf_Emu* this, long count, sample_t* out )
+blargg_err_t play_( void* emu, int count, sample_t* out )
{
- long remain = count;
+ struct Nsf_Emu* this = (struct Nsf_Emu*) emu;
+
+ int remain = count;
while ( remain )
{
+ Buffer_disable_immediate_removal( &this->stereo_buf );
remain -= Buffer_read_samples( &this->stereo_buf, &out [count - remain], remain );
if ( remain )
{
if ( this->buf_changed_count != Buffer_channels_changed_count( &this->stereo_buf ) )
{
this->buf_changed_count = Buffer_channels_changed_count( &this->stereo_buf );
-
+
// Remute voices
Sound_mute_voices( this, this->mute_mask_ );
}
int msec = Buffer_length( &this->stereo_buf );
- blip_time_t clocks_emulated = (blargg_long) msec * this->clock_rate__ / 1000 - 100;
+ blip_time_t clocks_emulated = msec * this->clock_rate__ / 1000 - 100;
RETURN_ERR( run_clocks( this, &clocks_emulated, msec ) );
assert( clocks_emulated );
Buffer_end_frame( &this->stereo_buf, clocks_emulated );
diff --git a/apps/codecs/libgme/nsf_emu.h b/apps/codecs/libgme/nsf_emu.h
index dccfa8c..00bdad4 100644
--- a/apps/codecs/libgme/nsf_emu.h
+++ b/apps/codecs/libgme/nsf_emu.h
@@ -10,6 +10,7 @@
#include "nes_cpu.h"
#include "nsfe_info.h"
#include "m3u_playlist.h"
+#include "track_filter.h"
#ifndef NSF_EMU_APU_ONLY
#include "nes_namco_apu.h"
@@ -17,11 +18,11 @@
#include "nes_fme7_apu.h"
#include "nes_fds_apu.h"
#include "nes_mmc5_apu.h"
- #include "nes_vrc7_apu.h"
+ #ifndef NSF_EMU_NO_VRC7
+ #include "nes_vrc7_apu.h"
+ #endif
#endif
-typedef short sample_t;
-
// Sound chip flags
enum {
vrc6_flag = 1 << 0,
@@ -50,8 +51,7 @@ enum { fdsram_size = 0x6000 };
enum { fdsram_offset = 0x2000 + page_size + 8 };
enum { sram_addr = 0x6000 };
enum { unmapped_size= page_size + 8 };
-
-enum { buf_size = 2048 };
+enum { max_voices = 32 };
// NSF file header
enum { header_size = 0x80 };
@@ -82,37 +82,21 @@ struct Nsf_Emu {
int play_extra;
int play_delay;
struct registers_t saved_state; // of interrupted init routine
-
- int track_count;
// general
- int max_initial_silence;
int voice_count;
+ int voice_types [32];
int mute_mask_;
int tempo;
int gain;
- long sample_rate;
+ int sample_rate;
// track-specific
+ int track_count;
int current_track;
- 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
- volatile bool track_ended;
-
- // fading
- blargg_long fade_start;
- int fade_step;
- // silence detection
- int silence_lookahead; // speed to run emulator when looking ahead for silence
- bool ignore_silence;
- long silence_time; // number of samples where most recent silence began
- long silence_count; // number of samples of silence to play before using buf
- long buf_remain; // number of samples left in silence buffer
-
- long clock_rate__;
+ int clock_rate__;
unsigned buf_changed_count;
// M3u Playlist
@@ -127,7 +111,9 @@ struct Nsf_Emu {
struct Nes_Namco_Apu namco;
struct Nes_Vrc6_Apu vrc6;
struct Nes_Fme7_Apu fme7;
- struct Nes_Vrc7_Apu vrc7;
+ #ifndef NSF_EMU_NO_VRC7
+ struct Nes_Vrc7_Apu vrc7;
+ #endif
#endif
struct Nes_Cpu cpu;
@@ -136,13 +122,15 @@ struct Nsf_Emu {
// Header for currently loaded file
struct header_t header;
- struct Stereo_Buffer stereo_buf;
+ struct setup_t tfilter;
+ struct Track_Filter track_filter;
+
+ struct Multi_Buffer stereo_buf;
struct Rom_Data rom;
// Extended nsf info
struct Nsfe_Info info;
- sample_t buf [buf_size];
byte high_ram[fdsram_size + fdsram_offset];
byte low_ram [low_ram_size];
};
@@ -150,18 +138,18 @@ struct Nsf_Emu {
// Basic functionality (see Gme_File.h for file loading/track info functions)
void Nsf_init( struct Nsf_Emu* this );
-blargg_err_t Nsf_load( struct Nsf_Emu* this, void* data, long size );
+blargg_err_t Nsf_load_mem( struct Nsf_Emu* this, void* data, long size );
blargg_err_t Nsf_post_load( struct Nsf_Emu* this );
// Set output sample rate. Must be called only once before loading file.
-blargg_err_t Nsf_set_sample_rate( struct Nsf_Emu* this, long sample_rate );
-
+blargg_err_t Nsf_set_sample_rate( struct Nsf_Emu* this, int sample_rate );
+
// Start a track, where 0 is the first track. Also clears warning string.
blargg_err_t Nsf_start_track( struct Nsf_Emu* this , int );
-
+
// Generate 'count' samples info 'buf'. Output is in stereo. Any emulation
// errors set warning string, and major errors also end track.
-blargg_err_t Nsf_play( struct Nsf_Emu* this, long count, sample_t* buf );
+blargg_err_t Nsf_play( struct Nsf_Emu* this, int count, sample_t* buf );
void Nsf_clear_playlist( struct Nsf_Emu* this );
void Nsf_disable_playlist( struct Nsf_Emu* this, bool b ); // use clear_playlist()
@@ -169,20 +157,32 @@ void Nsf_disable_playlist( struct Nsf_Emu* this, bool b ); // use clear_playlist
// Track status/control
// Number of milliseconds (1000 msec = 1 second) played since beginning of track
-long Track_tell( struct Nsf_Emu* this );
+int Track_tell( struct Nsf_Emu* this );
// Seek to new time in track. Seeking backwards or far forward can take a while.
-blargg_err_t Track_seek( struct Nsf_Emu* this, long msec );
+blargg_err_t Track_seek( struct Nsf_Emu* this, int msec );
// Skip n samples
-blargg_err_t Track_skip( struct Nsf_Emu* this, long n );
+blargg_err_t Track_skip( struct Nsf_Emu* this, int n );
// Set start time and length of track fade out. Once fade ends track_ended() returns
// true. Fade time can be changed while track is playing.
-void Track_set_fade( struct Nsf_Emu* this, long start_msec, long length_msec );
+void Track_set_fade( struct Nsf_Emu* this, int start_msec, int length_msec );
+
+// True if a track has reached its end
+static inline bool Track_ended( struct Nsf_Emu* this )
+{
+ return track_ended( &this->track_filter );
+}
+
+// Disables automatic end-of-track detection and skipping of silence at beginning
+static inline void Track_ignore_silence( struct Nsf_Emu* this, bool disable )
+{
+ this->track_filter.silence_ignored_ = disable;
+}
// Get track length in milliseconds
-long Track_length( struct Nsf_Emu* this, int n );
+int Track_length( struct Nsf_Emu* this, int n );
// Sound customization
@@ -217,28 +217,28 @@ bool run_cpu_until( struct Nsf_Emu* this, nes_time_t end );
// Sets clocks between calls to play routine to p + 1/2 clock
static inline void set_play_period( struct Nsf_Emu* this, int p ) { this->play_period = p; }
-
+
// Time play routine will next be called
static inline nes_time_t play_time( struct Nsf_Emu* this ) { return this->next_play; }
-
+
// Emulates to at least time t. Might emulate a few clocks extra.
void run_until( struct Nsf_Emu* this, nes_time_t t );
-
+
// Runs cpu to at least time t and returns false, or returns true
// if it encounters illegal instruction (halt).
bool run_cpu_until( struct Nsf_Emu* this, nes_time_t t );
-
+
// cpu calls through to these to access memory (except instructions)
int read_mem( struct Nsf_Emu* this, addr_t );
void write_mem( struct Nsf_Emu* this, addr_t, int );
-
+
// Address of play routine
static inline addr_t play_addr( struct Nsf_Emu* this ) { return get_addr( this->header.play_addr ); }
-
+
// Same as run_until, except emulation stops for any event (routine returned,
// play routine called, illegal instruction).
void run_once( struct Nsf_Emu* this, nes_time_t );
-
+
// Reads byte as cpu would when executing code. Only works for RAM/ROM,
// NOT I/O like sound chips.
int read_code( struct Nsf_Emu* this, addr_t addr );
@@ -248,12 +248,14 @@ static inline byte* sram( struct Nsf_Emu* this ) { return this->high_
static inline byte* unmapped_code( struct Nsf_Emu* this ) { return &this->high_ram [sram_size]; }
#ifndef NSF_EMU_APU_ONLY
-static inline int fds_enabled( struct Nsf_Emu* this ) { return this->header.chip_flags & fds_flag; }
-static inline int vrc6_enabled( struct Nsf_Emu* this ) { return this->header.chip_flags & vrc6_flag; }
-static inline int vrc7_enabled( struct Nsf_Emu* this ) { return this->header.chip_flags & vrc7_flag; }
-static inline int mmc5_enabled( struct Nsf_Emu* this ) { return this->header.chip_flags & mmc5_flag; }
-static inline int namco_enabled( struct Nsf_Emu* this ) { return this->header.chip_flags & namco_flag; }
-static inline int fme7_enabled( struct Nsf_Emu* this ) { return this->header.chip_flags & fme7_flag; }
+ static inline int fds_enabled( struct Nsf_Emu* this ) { return this->header.chip_flags & fds_flag; }
+ static inline int vrc6_enabled( struct Nsf_Emu* this ) { return this->header.chip_flags & vrc6_flag; }
+ #ifndef NSF_EMU_NO_VRC7
+ static inline int vrc7_enabled( struct Nsf_Emu* this ) { return this->header.chip_flags & vrc7_flag; }
+ #endif
+ static inline int mmc5_enabled( struct Nsf_Emu* this ) { return this->header.chip_flags & mmc5_flag; }
+ static inline int namco_enabled( struct Nsf_Emu* this ) { return this->header.chip_flags & namco_flag; }
+ static inline int fme7_enabled( struct Nsf_Emu* this ) { return this->header.chip_flags & fme7_flag; }
#endif
#endif
diff --git a/apps/codecs/libgme/nsfe_info.c b/apps/codecs/libgme/nsfe_info.c
index d22b763..337b1e5 100644
--- a/apps/codecs/libgme/nsfe_info.c
+++ b/apps/codecs/libgme/nsfe_info.c
@@ -154,8 +154,8 @@ blargg_err_t Info_load( struct Nsfe_Info* this, void* data, long size, struct Ns
byte block_header [2] [4];
RETURN_ERR( in_read( block_header, sizeof block_header, data, &offset, size ) );
- blargg_long chunk_size = get_le32( block_header [0] );
- blargg_long tag = get_le32( block_header [1] );
+ int chunk_size = get_le32( block_header [0] );
+ int tag = get_le32( block_header [1] );
switch ( tag )
{
@@ -168,7 +168,7 @@ blargg_err_t Info_load( struct Nsfe_Info* this, void* data, long size, struct Ns
finfo.track_count = 1;
finfo.first_track = 0;
- RETURN_ERR( in_read( &finfo, min( chunk_size, (blargg_long) nsfe_info_size ),
+ RETURN_ERR( in_read( &finfo, min( chunk_size, nsfe_info_size ),
(char*) data, &offset, size ) );
if ( chunk_size > nsfe_info_size )
@@ -248,9 +248,9 @@ blargg_err_t Info_load( struct Nsfe_Info* this, void* data, long size, struct Ns
return 0;
}
-long Track_length( struct Nsf_Emu* this, int n )
+int Track_length( struct Nsf_Emu* this, int n )
{
- long length = 0;
+ int length = 0;
if ( (this->m3u.size > 0) && (n < this->m3u.size) ) {
struct entry_t* entry = &this->m3u.entries [n];
length = entry->length;
diff --git a/apps/codecs/libgme/opl_apu.h b/apps/codecs/libgme/opl_apu.h
index 5ea8185..f24a8d6 100644
--- a/apps/codecs/libgme/opl_apu.h
+++ b/apps/codecs/libgme/opl_apu.h
@@ -37,10 +37,10 @@ struct Opl_Apu {
blargg_err_t Opl_init( struct Opl_Apu* this, long clock, long rate, blip_time_t period, enum opl_type_t type );
void Opl_shutdown( struct Opl_Apu* this );
-
+
void Opl_reset( struct Opl_Apu* this );
static inline void Opl_volume( struct Opl_Apu* this, int v ) { Synth_volume( &this->synth, v / (4096 * 6) ); }
-
+
static inline void Opl_osc_output( struct Opl_Apu* this, int i, struct Blip_Buffer* buf )
{
#if defined(ROCKBOX)
diff --git a/apps/codecs/libgme/resampler.c b/apps/codecs/libgme/resampler.c
index 837f01e..91677cb 100644
--- a/apps/codecs/libgme/resampler.c
+++ b/apps/codecs/libgme/resampler.c
@@ -64,7 +64,7 @@ void Resampler_resize( struct Resampler* this, int pairs )
}
}
-void mix_samples( struct Resampler* this, struct Blip_Buffer* blip_buf, sample_t out_ [] )
+void mix_samples( struct Resampler* this, struct Blip_Buffer* blip_buf, dsample_t out_ [] )
{
int const bass = BLIP_READER_BASS( *blip_buf );
BLIP_READER_BEGIN( sn, *blip_buf );
@@ -72,7 +72,7 @@ void mix_samples( struct Resampler* this, struct Blip_Buffer* blip_buf, sample_t
int count = this->sample_buf_size >> 1;
BLIP_READER_ADJ_( sn, count );
- typedef sample_t stereo_dsample_t [2];
+ typedef dsample_t stereo_dsample_t [2];
stereo_dsample_t* BLARGG_RESTRICT out = (stereo_dsample_t*) out_ + count;
stereo_dsample_t const* BLARGG_RESTRICT in =
(stereo_dsample_t const*) this->sample_buf + count;
@@ -97,14 +97,14 @@ void mix_samples( struct Resampler* this, struct Blip_Buffer* blip_buf, sample_t
BLIP_READER_END( sn, *blip_buf );
}
-sample_t const* resample_( struct Resampler* this, sample_t** out_,
- sample_t const* out_end, sample_t const in [], int in_size )
+dsample_t const* resample_( struct Resampler* this, dsample_t** out_,
+ dsample_t const* out_end, dsample_t const in [], int in_size )
{
in_size -= write_offset;
if ( in_size > 0 )
{
- sample_t* BLARGG_RESTRICT out = *out_;
- sample_t const* const in_end = in + in_size;
+ dsample_t* BLARGG_RESTRICT out = *out_;
+ dsample_t const* const in_end = in + in_size;
int const step = this->step;
int pos = this->pos;
@@ -135,12 +135,12 @@ sample_t const* resample_( struct Resampler* this, sample_t** out_,
return in;
}
-static inline int resample_wrapper( struct Resampler* this, sample_t out [], int* out_size,
- sample_t const in [], int in_size )
+static inline int resample_wrapper( struct Resampler* this, dsample_t out [], int* out_size,
+ dsample_t const in [], int in_size )
{
assert( Resampler_rate( this ) );
- sample_t* out_ = out;
+ dsample_t* out_ = out;
int result = resample_( this, &out_, out + *out_size, in, in_size ) - in;
assert( out_ <= out + *out_size );
assert( result <= in_size );
@@ -161,7 +161,7 @@ int skip_input( struct Resampler* this, int count )
return count;
}
-void play_frame_( struct Resampler* this, struct Blip_Buffer* blip_buf, sample_t* out )
+void play_frame_( struct Resampler* this, struct Blip_Buffer* blip_buf, dsample_t* out )
{
int pair_count = this->sample_buf_size >> 1;
blip_time_t blip_time = Blip_count_clocks( blip_buf, pair_count );
@@ -185,7 +185,7 @@ void play_frame_( struct Resampler* this, struct Blip_Buffer* blip_buf, sample_t
Blip_remove_samples( blip_buf, pair_count );
}
-void Resampler_play( struct Resampler* this, int count, sample_t* out, struct Blip_Buffer* blip_buf )
+void Resampler_play( struct Resampler* this, int count, dsample_t* out, struct Blip_Buffer* blip_buf )
{
// empty extra buffer
int remain = this->sample_buf_size - this->buf_pos;
diff --git a/apps/codecs/libgme/resampler.h b/apps/codecs/libgme/resampler.h
index 1d8f866..3f3710a 100644
--- a/apps/codecs/libgme/resampler.h
+++ b/apps/codecs/libgme/resampler.h
@@ -7,16 +7,15 @@
#include "blargg_common.h"
#include "multi_buffer.h"
-typedef short sample_t;
+typedef short dsample_t;
-enum { stereo = 2 };
enum { max_buf_size = 3960 };
enum { max_resampler_size = 5942 };
enum { write_offset = 8 * stereo };
enum { gain_bits = 14 };
struct Resampler {
- int (*callback)( void*, blip_time_t, int, sample_t* );
+ int (*callback)( void*, blip_time_t, int, dsample_t* );
void* callback_data;
int sample_buffer_size;
@@ -34,8 +33,8 @@ struct Resampler {
int rate_;
- sample_t sample_buf [max_buf_size];
- sample_t buf [max_resampler_size]; // Internal resampler
+ dsample_t sample_buf [max_buf_size];
+ dsample_t buf [max_resampler_size]; // Internal resampler
};
static inline void Resampler_init( struct Resampler* this )
@@ -50,9 +49,9 @@ static inline void Resampler_init( struct Resampler* this )
blargg_err_t Resampler_reset( struct Resampler* this, int max_pairs );
void Resampler_resize( struct Resampler* this, int pairs_per_frame );
-void Resampler_play( struct Resampler* this, int count, sample_t* out, struct Blip_Buffer* );
+void Resampler_play( struct Resampler* this, int count, dsample_t* out, struct Blip_Buffer* );
-static inline void Resampler_set_callback(struct Resampler* this, int (*func)( void*, blip_time_t, int, sample_t* ), void* user_data )
+static inline void Resampler_set_callback(struct Resampler* this, int (*func)( void*, blip_time_t, int, dsample_t* ), void* user_data )
{
this->callback = func;
this->callback_data = user_data;
diff --git a/apps/codecs/libgme/rom_data.c b/apps/codecs/libgme/rom_data.c
index 5fe3115..9c36a99 100644
--- a/apps/codecs/libgme/rom_data.c
+++ b/apps/codecs/libgme/rom_data.c
@@ -21,7 +21,7 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
blargg_err_t Rom_load( struct Rom_Data* this, const void* data, long size,
int header_size, void* header_out, int fill )
{
- long file_offset = this->pad_size;
+ int file_offset = this->pad_size;
this->rom_addr = 0;
this->mask = 0;
@@ -43,11 +43,11 @@ blargg_err_t Rom_load( struct Rom_Data* this, const void* data, long size,
return 0;
}
-void Rom_set_addr( struct Rom_Data* this, long addr )
+void Rom_set_addr( struct Rom_Data* this, int addr )
{
this->rom_addr = addr - this->bank_size - pad_extra;
- long rounded = (addr + this->file_size + this->bank_size - 1) / this->bank_size * this->bank_size;
+ int rounded = (addr + this->file_size + this->bank_size - 1) / this->bank_size * this->bank_size;
if ( rounded <= 0 )
{
rounded = 0;
@@ -55,7 +55,7 @@ void Rom_set_addr( struct Rom_Data* this, long addr )
else
{
int shift = 0;
- unsigned long max_addr = (unsigned long) (rounded - 1);
+ unsigned int max_addr = (unsigned int) (rounded - 1);
while ( max_addr >> shift )
shift++;
this->mask = (1L << shift) - 1;
diff --git a/apps/codecs/libgme/rom_data.h b/apps/codecs/libgme/rom_data.h
index 28b34f2..b8bc54c 100644
--- a/apps/codecs/libgme/rom_data.h
+++ b/apps/codecs/libgme/rom_data.h
@@ -20,22 +20,22 @@ enum { max_rom_size = 2 * max_pad_size };
struct Rom_Data {
byte* file_data;
- blargg_ulong file_size;
+ unsigned file_size;
- blargg_long rom_addr;
- blargg_long bank_size;
- blargg_long rom_size;
- blargg_ulong pad_size;
- blargg_long mask;
- blargg_long size; // TODO: eliminate
- blargg_long rsize_;
+ int rom_addr;
+ int bank_size;
+ int rom_size;
+ unsigned pad_size;
+ int mask;
+ int size; // TODO: eliminate
+ int rsize_;
// Unmapped space
byte unmapped [max_rom_size];
};
// Initialize rom
-static inline void Rom_init( struct Rom_Data* this, blargg_long bank_size )
+static inline void Rom_init( struct Rom_Data* this, int bank_size )
{
this->bank_size = bank_size;
this->pad_size = this->bank_size + pad_extra;
@@ -47,10 +47,10 @@ static inline void Rom_init( struct Rom_Data* this, blargg_long bank_size )
blargg_err_t Rom_load( struct Rom_Data* this, const void* data, long size, int header_size, void* header_out, int fill );
// Set address that file data should start at
-void Rom_set_addr( struct Rom_Data* this, long addr );
+void Rom_set_addr( struct Rom_Data* this, int addr );
// Mask address to nearest power of two greater than size()
-static inline blargg_long mask_addr( blargg_long addr, blargg_long mask )
+static inline int mask_addr( int addr, int mask )
{
#ifdef check
check( addr <= mask );
@@ -59,10 +59,10 @@ static inline blargg_long mask_addr( blargg_long addr, blargg_long mask )
}
// Pointer to page starting at addr. Returns unmapped() if outside data.
-static inline byte* Rom_at_addr( struct Rom_Data* this, blargg_long addr )
+static inline byte* Rom_at_addr( struct Rom_Data* this, int addr )
{
- blargg_ulong offset = mask_addr( addr, this->mask ) - this->rom_addr;
- if ( offset > (blargg_ulong) (this->rsize_ - this->pad_size) )
+ unsigned offset = mask_addr( addr, this->mask ) - this->rom_addr;
+ if ( offset > (unsigned) (this->rsize_ - this->pad_size) )
offset = 0; // unmapped
if ( offset < this->pad_size ) return &this->unmapped [offset];
diff --git a/apps/codecs/libgme/sgc_emu.c b/apps/codecs/libgme/sgc_emu.c
index 3c35866..a2e6b8d 100644
--- a/apps/codecs/libgme/sgc_emu.c
+++ b/apps/codecs/libgme/sgc_emu.c
@@ -10,34 +10,19 @@ module is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
details. You should have received a copy of the GNU Lesser General Public
-License along with this module; if not, write to the Free Software Foundation,
+License aint with this module; if not, write to the Free Software Foundation,
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
#include "blargg_source.h"
int const osc_count = sms_osc_count + fm_apu_osc_count;
-int const stereo = 2; // number of channels for stereo
-int const silence_max = 6; // seconds
-int const silence_threshold = 0x10;
-long const fade_block_size = 512;
-int const fade_shift = 8; // fade ends with gain at 1.0 / (1 << fade_shift)
-
const char gme_wrong_file_type [] = "Wrong file type for this emulator";
static void clear_track_vars( struct Sgc_Emu* this )
{
this->current_track = -1;
- this->out_time = 0;
- this->emu_time = 0;
- this->emu_track_ended_ = true;
- this->track_ended = true;
- this->fade_start = INT_MAX / 2 + 1;
- this->fade_step = 1;
- this->silence_time = 0;
- this->silence_count = 0;
- this->buf_remain = 0;
- /* warning(); // clear warning */
+ track_stop( &this->track_filter );
}
void Sgc_init( struct Sgc_Emu* this )
@@ -48,12 +33,12 @@ void Sgc_init( struct Sgc_Emu* this )
this->mute_mask_ = 0;
this->tempo = (int)FP_ONE_TEMPO;
this->gain = (int)FP_ONE_GAIN;
- this->voice_count = 0;
// defaults
- this->max_initial_silence = 2;
- this->silence_lookahead = 6;
- this->ignore_silence = false;
+ this->tfilter = *track_get_setup( &this->track_filter );
+ this->tfilter.max_initial = 2;
+ this->tfilter.lookahead = 6;
+ this->track_filter.silence_ignored_ = false;
Sms_apu_init( &this->apu );
Fm_apu_create( &this->fm_apu );
@@ -62,8 +47,10 @@ void Sgc_init( struct Sgc_Emu* this )
Z80_init( &this->cpu );
Sound_set_gain( this, (int)(FP_ONE_GAIN*1.2) );
-
+
// Unload
+ this->voice_count = 0;
+ this->voice_types = 0;
clear_track_vars( this );
}
@@ -95,6 +82,10 @@ blargg_err_t Sgc_load_mem( struct Sgc_Emu* this, const void* data, long size )
this->m3u.size = 0;
this->track_count = this->header.song_count;
this->voice_count = sega_mapping( this ) ? osc_count : sms_osc_count;
+ static int const types [osc_count + 1] = {
+ wave_type+1, wave_type+2, wave_type+3, mixed_type+1, mixed_type+2
+ };
+ this->voice_types = types;
Sms_apu_volume( &this->apu, this->gain );
Fm_apu_volume( &this->fm_apu, this->gain );
@@ -102,10 +93,10 @@ blargg_err_t Sgc_load_mem( struct Sgc_Emu* this, const void* data, long size )
// Setup buffer
this->clock_rate_ = clock_rate( this );
Buffer_clock_rate( &this->stereo_buf, clock_rate( this ) );
+ RETURN_ERR( Buffer_set_channel_count( &this->stereo_buf, this->voice_count, this->voice_types ) );
this->buf_changed_count = Buffer_channels_changed_count( &this->stereo_buf );
- Sound_set_tempo( this, this->tempo );
- // Remute voices
+ Sound_set_tempo( this, this->tempo );
Sound_mute_voices( this, this->mute_mask_ );
return 0;
}
@@ -249,7 +240,7 @@ void cpu_write( struct Sgc_Emu* this, addr_t addr, int data )
}
}
-blargg_err_t Sgc_set_sample_rate( struct Sgc_Emu* this, long rate )
+blargg_err_t Sgc_set_sample_rate( struct Sgc_Emu* this, int rate )
{
require( !this->sample_rate ); // sample rate can't be changed once set
Buffer_init( &this->stereo_buf );
@@ -259,6 +250,8 @@ blargg_err_t Sgc_set_sample_rate( struct Sgc_Emu* this, long rate )
Buffer_bass_freq( &this->stereo_buf, 80 );
this->sample_rate = rate;
+ RETURN_ERR( track_init( &this->track_filter, this ) );
+ this->tfilter.max_silence = 6 * stereo * this->sample_rate;
return 0;
}
@@ -286,7 +279,7 @@ void Sound_mute_voices( struct Sgc_Emu* this, int mask )
}
else
{
- struct channel_t ch = Buffer_channel( &this->stereo_buf );
+ struct channel_t ch = Buffer_channel( &this->stereo_buf, i );
assert( (ch.center && ch.left && ch.right) ||
(!ch.center && !ch.left && !ch.right) ); // all or nothing
Sound_set_voice( this, i, ch.center, ch.left, ch.right );
@@ -306,7 +299,6 @@ void Sound_set_tempo( struct Sgc_Emu* this, int t )
this->play_period = (int) ((clock_rate( this ) * FP_ONE_TEMPO) / (this->header.rate ? 50 : 60) / t);
}
-void fill_buf( struct Sgc_Emu* this );
blargg_err_t Sgc_start_track( struct Sgc_Emu* this, int track )
{
clear_track_vars( this );
@@ -383,275 +375,90 @@ blargg_err_t Sgc_start_track( struct Sgc_Emu* this, int track )
Buffer_clear( &this->stereo_buf );
- this->emu_track_ended_ = false;
- this->track_ended = false;
-
- if ( !this->ignore_silence )
- {
- // play until non-silence or end of track
- long end;
- for ( end = this->max_initial_silence * stereo * this->sample_rate; this->emu_time < end; )
- {
- fill_buf( this );
- if ( this->buf_remain | (int) this->emu_track_ended_ )
- break;
- }
-
- this->emu_time = this->buf_remain;
- this->out_time = 0;
- this->silence_time = 0;
- this->silence_count = 0;
- }
- /* return track_ended() ? warning() : 0; */
- return 0;
+ // convert filter times to samples
+ struct setup_t s = this->tfilter;
+ s.max_initial *= this->sample_rate * stereo;
+ #ifdef GME_DISABLE_SILENCE_LOOKAHEAD
+ s.lookahead = 1;
+ #endif
+ track_setup( &this->track_filter, &s );
+
+ return track_start( &this->track_filter );
}
// Tell/Seek
-static blargg_long msec_to_samples( blargg_long msec, long sample_rate )
+static int msec_to_samples( int msec, int sample_rate )
{
- blargg_long sec = msec / 1000;
+ int sec = msec / 1000;
msec -= sec * 1000;
return (sec * sample_rate + msec * sample_rate / 1000) * stereo;
}
-long Track_tell( struct Sgc_Emu* this )
+int Track_tell( struct Sgc_Emu* this )
{
- blargg_long rate = this->sample_rate * stereo;
- blargg_long sec = this->out_time / rate;
- return sec * 1000 + (this->out_time - sec * rate) * 1000 / rate;
+ int rate = this->sample_rate * stereo;
+ int sec = track_sample_count( &this->track_filter ) / rate;
+ return sec * 1000 + (track_sample_count( &this->track_filter ) - sec * rate) * 1000 / rate;
}
-blargg_err_t Track_seek( struct Sgc_Emu* this, long msec )
+blargg_err_t Track_seek( struct Sgc_Emu* this, int msec )
{
- blargg_long time = msec_to_samples( msec, this->sample_rate );
- if ( time < this->out_time )
- RETURN_ERR( Sgc_start_track( this, this->current_track ) );
- return Track_skip( this, time - this->out_time );
+ int time = msec_to_samples( msec, this->sample_rate );
+ if ( time < track_sample_count( &this->track_filter ) )
+ RETURN_ERR( Sgc_start_track( this, this->current_track ) );
+ return Track_skip( this, time - track_sample_count( &this->track_filter ) );
}
-blargg_err_t skip_( struct Sgc_Emu* this, long count );
-blargg_err_t Track_skip( struct Sgc_Emu* this, long count )
+blargg_err_t Track_skip( struct Sgc_Emu* this, int count )
{
require( this->current_track >= 0 ); // start_track() must have been called already
- this->out_time += count;
-
- // remove from silence and buf first
- {
- long n = min( count, this->silence_count );
- this->silence_count -= n;
- count -= n;
-
- n = min( count, this->buf_remain );
- this->buf_remain -= n;
- count -= n;
- }
-
- if ( count && !this->emu_track_ended_ )
- {
- this->emu_time += count;
-
- // End track if error
- if ( skip_( this, count ) ) {
- this->emu_track_ended_ = true;
- }
- }
-
- if ( !(this->silence_count | this->buf_remain) ) // caught up to emulator, so update track ended
- this->track_ended |= this->emu_track_ended_;
-
- return 0;
+ return track_skip( &this->track_filter, count );
}
-blargg_err_t play_( struct Sgc_Emu* this, long count, sample_t* out );
-blargg_err_t skip_( struct Sgc_Emu* this, long count )
+blargg_err_t skip_( void* emu, int count )
{
+ struct Sgc_Emu* this = (struct Sgc_Emu*) emu;
+
// for long skip, mute sound
- const long threshold = 30000;
+ const int threshold = 32768;
if ( count > threshold )
{
int saved_mute = this->mute_mask_;
Sound_mute_voices( this, ~0 );
-
- while ( count > threshold / 2 && !this->emu_track_ended_ )
- {
- RETURN_ERR( play_( this, buf_size, this->buf ) );
- count -= buf_size;
- }
-
- Sound_mute_voices( this, saved_mute );
- }
-
- while ( count && !this->emu_track_ended_ )
- {
- long n = buf_size;
- if ( n > count )
- n = count;
- count -= n;
- RETURN_ERR( play_( this, n, this->buf ) );
- }
- return 0;
-}
-
-// Fading
-
-void Track_set_fade( struct Sgc_Emu* this, long start_msec, long length_msec )
-{
- this->fade_step = this->sample_rate * length_msec / (fade_block_size * fade_shift * 1000 / stereo);
- this->fade_start = msec_to_samples( start_msec, this->sample_rate );
-}
-
-// unit / pow( 2.0, (double) x / step )
-static int int_log( blargg_long x, int step, int unit )
-{
- int shift = x / step;
- int fraction = (x - shift * step) * unit / step;
- return ((unit - fraction) + (fraction >> 1)) >> shift;
-}
-
-static void handle_fade( struct Sgc_Emu* this, long out_count, sample_t* out )
-{
- int i;
- for ( i = 0; i < out_count; i += fade_block_size )
- {
- int const shift = 14;
- int const unit = 1 << shift;
- int gain = int_log( (this->out_time + i - this->fade_start) / fade_block_size,
- this->fade_step, unit );
- if ( gain < (unit >> fade_shift) )
- this->track_ended = this->emu_track_ended_ = true;
-
- sample_t* io = &out [i];
- int count;
- for ( count = min( fade_block_size, out_count - i ); count; --count )
- {
- *io = (sample_t) ((*io * gain) >> shift);
- ++io;
- }
- }
-}
-// Silence detection
+ int n = count - threshold/2;
+ n &= ~(2048-1); // round to multiple of 2048
+ count -= n;
+ RETURN_ERR( skippy_( &this->track_filter, n ) );
-static void emu_play( struct Sgc_Emu* this, long count, sample_t* out )
-{
- check( this->current_track_ >= 0 );
- this->emu_time += count;
- if ( this->current_track >= 0 && !this->emu_track_ended_ ) {
- // End track if error
- if ( play_( this, count, out ) )
- this->emu_track_ended_ = true;
+ Sound_mute_voices( this, saved_mute );
}
- else
- memset( out, 0, count * sizeof *out );
-}
-// number of consecutive silent samples at end
-static long count_silence( sample_t* begin, long size )
-{
- sample_t first = *begin;
- *begin = silence_threshold; // sentinel
- sample_t* p = begin + size;
- while ( (unsigned) (*--p + silence_threshold / 2) <= (unsigned) silence_threshold ) { }
- *begin = first;
- return size - (p - begin);
+ return skippy_( &this->track_filter, count );
}
-// fill internal buffer and check it for silence
-void fill_buf( struct Sgc_Emu* this )
+void Track_set_fade( struct Sgc_Emu* this, int start_msec, int length_msec )
{
- assert( !this->buf_remain );
- if ( !this->emu_track_ended_ )
- {
- emu_play( this, buf_size, this->buf );
- long silence = count_silence( this->buf, buf_size );
- if ( silence < buf_size )
- {
- this->silence_time = this->emu_time - silence;
- this->buf_remain = buf_size;
- return;
- }
- }
- this->silence_count += buf_size;
+ track_set_fade( &this->track_filter, msec_to_samples( start_msec, this->sample_rate ),
+ length_msec * this->sample_rate / (1000 / stereo) );
}
-blargg_err_t Sgc_play( struct Sgc_Emu* this, long out_count, sample_t* out )
+blargg_err_t Sgc_play( struct Sgc_Emu* this, int out_count, sample_t* out )
{
- if ( this->track_ended )
- {
- memset( out, 0, out_count * sizeof *out );
- }
- else
- {
- require( this->current_track >= 0 );
- require( out_count % stereo == 0 );
-
- assert( this->emu_time >= this->out_time );
-
- // prints nifty graph of how far ahead we are when searching for silence
- //debug_printf( "%*s \n", int ((emu_time - out_time) * 7 / sample_rate()), "*" );
-
- long pos = 0;
- if ( this->silence_count )
- {
- // during a run of silence, run emulator at >=2x speed so it gets ahead
- long ahead_time = this->silence_lookahead * (this->out_time + out_count - this->silence_time) + this->silence_time;
- while ( this->emu_time < ahead_time && !(this->buf_remain | this->emu_track_ended_) )
- fill_buf( this );
-
- // fill with silence
- pos = min( this->silence_count, out_count );
- memset( out, 0, pos * sizeof *out );
- this->silence_count -= pos;
-
- if ( this->emu_time - this->silence_time > silence_max * stereo * this->sample_rate )
- {
- this->track_ended = this->emu_track_ended_ = true;
- this->silence_count = 0;
- this->buf_remain = 0;
- }
- }
-
- if ( this->buf_remain )
- {
- // empty silence buf
- long n = min( this->buf_remain, out_count - pos );
- memcpy( &out [pos], this->buf + (buf_size - this->buf_remain), n * sizeof *out );
- this->buf_remain -= n;
- pos += n;
- }
-
- // generate remaining samples normally
- long remain = out_count - pos;
- if ( remain )
- {
- emu_play( this, remain, out + pos );
- this->track_ended |= this->emu_track_ended_;
-
- if ( !this->ignore_silence || this->out_time > this->fade_start )
- {
- // check end for a new run of silence
- long silence = count_silence( out + pos, remain );
- if ( silence < remain )
- this->silence_time = this->emu_time - silence;
-
- if ( this->emu_time - this->silence_time >= buf_size )
- fill_buf( this ); // cause silence detection on next play()
- }
- }
-
- if ( this->out_time > this->fade_start )
- handle_fade( this, out_count, out );
- }
- this->out_time += out_count;
- return 0;
+ require( this->current_track >= 0 );
+ require( out_count % stereo == 0 );
+ return track_play( &this->track_filter, out_count, out );
}
-blargg_err_t play_( struct Sgc_Emu* this, long count, sample_t* out )
+blargg_err_t play_( void* emu, int count, sample_t out [] )
{
- long remain = count;
+ struct Sgc_Emu* this = (struct Sgc_Emu*) emu;
+
+ int remain = count;
while ( remain )
{
+ Buffer_disable_immediate_removal( &this->stereo_buf );
remain -= Buffer_read_samples( &this->stereo_buf, &out [count - remain], remain );
if ( remain )
{
diff --git a/apps/codecs/libgme/sgc_emu.h b/apps/codecs/libgme/sgc_emu.h
index 779ef2d..6595c02 100644
--- a/apps/codecs/libgme/sgc_emu.h
+++ b/apps/codecs/libgme/sgc_emu.h
@@ -12,16 +12,14 @@
#include "sms_fm_apu.h"
#include "sms_apu.h"
#include "m3u_playlist.h"
+#include "track_filter.h"
-typedef short sample_t;
typedef struct Z80_Cpu Sgc_Cpu;
-enum { buf_size = 2048 };
-
// SGC file header
enum { header_size = 0xA0 };
struct header_t
-{
+{
char tag [4]; // "SGC\x1A"
byte vers; // 0x01
byte rate; // 0=NTSC 1=PAL
@@ -48,12 +46,11 @@ struct header_t
static inline bool valid_tag( struct header_t* h )
{
return 0 == memcmp( h->tag, "SGC\x1A", 4 );
-}
-
-static inline int effect_count( struct header_t* h ) { return h->last_effect ? h->last_effect - h->first_effect + 1 : 0; }
+}
+static inline int effect_count( struct header_t* h ) { return h->last_effect ? h->last_effect - h->first_effect + 1 : 0; }
-struct Sgc_Emu {
+struct Sgc_Emu {
bool fm_accessed;
cpu_time_t play_period;
@@ -65,40 +62,28 @@ struct Sgc_Emu {
// general
int voice_count;
+ int const* voice_types;
int mute_mask_;
int tempo;
int gain;
- long sample_rate;
+ int sample_rate;
// track-specific
- volatile bool track_ended;
int current_track;
int track_count;
- 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
-
- // fading
- blargg_long fade_start;
- int fade_step;
-
- // silence detection
- bool ignore_silence;
- int max_initial_silence;
- int silence_lookahead; // speed to run emulator when looking ahead for silence
- long silence_time; // number of samples where most recent silence began
- long silence_count; // number of samples of silence to play before using buf
- long buf_remain; // number of samples left in silence buffer
-
- long clock_rate_;
+
+ int clock_rate_;
unsigned buf_changed_count;
// M3u Playlist
struct M3u_Playlist m3u;
+ struct header_t header;
- sample_t buf [buf_size];
- struct Stereo_Buffer stereo_buf;
+ struct setup_t tfilter;
+ struct Track_Filter track_filter;
+
+ struct Multi_Buffer stereo_buf;
struct Sms_Apu apu;
struct Sms_Fm_Apu fm_apu;
@@ -106,12 +91,11 @@ struct Sgc_Emu {
Sgc_Cpu cpu;
// large items
- struct header_t header;
struct Rom_Data rom;
byte vectors [page_size + page_padding];
+ byte unmapped_write [0x4000];
byte ram [0x2000 + page_padding];
byte ram2 [0x4000 + page_padding];
- byte unmapped_write [0x4000];
};
// Basic functionality (see Gme_File.h for file loading/track info functions)
@@ -119,41 +103,53 @@ struct Sgc_Emu {
void Sgc_init( struct Sgc_Emu* this );
blargg_err_t Sgc_load_mem( struct Sgc_Emu* this, const void* data, long size );
-
+
static inline int clock_rate( struct Sgc_Emu* this ) { return this->header.rate ? 3546893 : 3579545; }
// 0x2000 bytes
static inline void set_coleco_bios( struct Sgc_Emu* this, void* p ) { this->coleco_bios = p; }
// Set output sample rate. Must be called only once before loading file.
-blargg_err_t Sgc_set_sample_rate( struct Sgc_Emu* this, long sample_rate );
+blargg_err_t Sgc_set_sample_rate( struct Sgc_Emu* this, int sample_rate );
// Start a track, where 0 is the first track. Also clears warning string.
blargg_err_t Sgc_start_track( struct Sgc_Emu* this, int track );
// Generate 'count' samples info 'buf'. Output is in stereo. Any emulation
// errors set warning string, and major errors also end track.
-blargg_err_t Sgc_play( struct Sgc_Emu* this, long count, sample_t* buf );
+blargg_err_t Sgc_play( struct Sgc_Emu* this, int count, sample_t* buf );
// Track status/control
// Number of milliseconds (1000 msec = 1 second) played since beginning of track
-long Track_tell( struct Sgc_Emu* this );
+int Track_tell( struct Sgc_Emu* this );
// Seek to new time in track. Seeking backwards or far forward can take a while.
-blargg_err_t Track_seek( struct Sgc_Emu* this, long msec );
+blargg_err_t Track_seek( struct Sgc_Emu* this, int msec );
// Skip n samples
-blargg_err_t Track_skip( struct Sgc_Emu* this, long n );
+blargg_err_t Track_skip( struct Sgc_Emu* this, int n );
// Set start time and length of track fade out. Once fade ends track_ended() returns
// true. Fade time can be changed while track is playing.
-void Track_set_fade( struct Sgc_Emu* this, long start_msec, long length_msec );
+void Track_set_fade( struct Sgc_Emu* this, int start_msec, int length_msec );
+
+// True if a track has reached its end
+static inline bool Track_ended( struct Sgc_Emu* this )
+{
+ return track_ended( &this->track_filter );
+}
+
+// Disables automatic end-of-track detection and skipping of silence at beginning
+static inline void Track_ignore_silence( struct Sgc_Emu* this, bool disable )
+{
+ this->track_filter.silence_ignored_ = disable;
+}
// Get track length in milliseconds
-static inline long Track_get_length( struct Sgc_Emu* this, int n )
+static inline int Track_get_length( struct Sgc_Emu* this, int n )
{
- long length = 120 * 1000; /* 2 minutes */
+ int length = 120 * 1000; /* 2 minutes */
if ( (this->m3u.size > 0) && (n < this->m3u.size) ) {
struct entry_t* entry = &this->m3u.entries [n];
length = entry->length;
diff --git a/apps/codecs/libgme/sms_apu.c b/apps/codecs/libgme/sms_apu.c
index 965e483..379fcf1 100644
--- a/apps/codecs/libgme/sms_apu.c
+++ b/apps/codecs/libgme/sms_apu.c
@@ -158,9 +158,7 @@ static void run_until( struct Sms_Apu* this, blip_time_t end_time )
if ( delta )
{
osc->last_amp = amp;
- /* norm_synth.offset( last_time, delta, out ); */
Synth_offset( &this->synth, this->last_time, delta, out );
- /* out->set_modified(); */
Blip_set_modified( out );
}
}
@@ -200,7 +198,6 @@ static void run_until( struct Sms_Apu* this, blip_time_t end_time )
do
{
delta = -delta;
- /* norm_synth.offset( time, delta, out ); */
Synth_offset( &this->synth, time, delta, out );
time += period;
}
@@ -218,7 +215,6 @@ static void run_until( struct Sms_Apu* this, blip_time_t end_time )
if ( changed & 2 ) // true if bits 0 and 1 differ
{
delta = -delta;
- /* fast_synth.offset_inline( time, delta, out ); */
Synth_offset_inline( &this->synth, time, delta, out );
}
time += period;
diff --git a/apps/codecs/libgme/sms_apu.h b/apps/codecs/libgme/sms_apu.h
index f887dc6..6dd6559 100644
--- a/apps/codecs/libgme/sms_apu.h
+++ b/apps/codecs/libgme/sms_apu.h
@@ -44,10 +44,10 @@ void Sms_apu_set_output( struct Sms_Apu* this, int i, struct Blip_Buffer* center
// Emulates to time t, then writes data to Game Gear left/right assignment byte
void Sms_apu_write_ggstereo( struct Sms_Apu* this, blip_time_t t, int data );
-
+
// Emulates to time t, then writes data
void Sms_apu_write_data( struct Sms_Apu* this, blip_time_t t, int data );
-
+
// Emulates to time t, then subtracts t from the current time.
// OK if previous write call had time slightly after t.
void Sms_apu_end_frame( struct Sms_Apu* this, blip_time_t t );
diff --git a/apps/codecs/libgme/sms_fm_apu.h b/apps/codecs/libgme/sms_fm_apu.h
index cf8cd6c..00bc2b4 100644
--- a/apps/codecs/libgme/sms_fm_apu.h
+++ b/apps/codecs/libgme/sms_fm_apu.h
@@ -25,19 +25,19 @@ void Fm_apu_create( struct Sms_Fm_Apu* this );
static inline bool Fm_apu_supported( void ) { return Ym2413_supported(); }
blargg_err_t Fm_apu_init( struct Sms_Fm_Apu* this, int clock_rate, int sample_rate );
-
+
static inline void Fm_apu_set_output( struct Sms_Fm_Apu* this, struct Blip_Buffer* b )
{
this->output_ = b;
}
static inline void Fm_apu_volume( struct Sms_Fm_Apu* this, int v ) { Synth_volume( &this->synth, (v*2) / 5 / 4096 ); }
-
+
void Fm_apu_reset( struct Sms_Fm_Apu* this );
-
+
static inline void Fm_apu_write_addr( struct Sms_Fm_Apu* this, int data ) { this->addr = data; }
void Fm_apu_write_data( struct Sms_Fm_Apu* this, blip_time_t, int data );
-
+
void Fm_apu_end_frame( struct Sms_Fm_Apu* this, blip_time_t t );
#endif
diff --git a/apps/codecs/libgme/track_filter.c b/apps/codecs/libgme/track_filter.c
new file mode 100644
index 0000000..f468a8c
--- /dev/null
+++ b/apps/codecs/libgme/track_filter.c
@@ -0,0 +1,294 @@
+// Game_Music_Emu 0.6-pre. http://www.slack.net/~ant/
+
+#include "track_filter.h"
+
+/* Copyright (C) 2003-2008 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "blargg_source.h"
+
+int const fade_block_size = 512;
+int const fade_shift = 8; // fade ends with gain at 1.0 / (1 << fade_shift)
+int const silence_threshold = 8;
+
+void track_create( struct Track_Filter* this )
+{
+ this->emu_ = NULL;
+ this->setup_.max_initial = 0;
+ this->setup_.lookahead = 0;
+ this->setup_.max_silence = indefinite_count;
+ this->silence_ignored_ = false;
+ track_stop( this );
+}
+
+blargg_err_t track_init( struct Track_Filter* this, void* emu )
+{
+ this->emu_ = emu;
+ return 0;
+}
+
+void clear_time_vars( struct Track_Filter* this )
+{
+ this->emu_time = this->buf_remain;
+ this->out_time = 0;
+ this->silence_time = 0;
+ this->silence_count = 0;
+}
+
+void track_stop( struct Track_Filter* this )
+{
+ this->emu_track_ended_ = true;
+ this->track_ended_ = true;
+ this->fade_start = indefinite_count;
+ this->fade_step = 1;
+ this->buf_remain = 0;
+ this->emu_error = NULL;
+ clear_time_vars( this );
+}
+
+blargg_err_t track_start( struct Track_Filter* this )
+{
+ this->emu_error = NULL;
+ track_stop( this );
+
+ this->emu_track_ended_ = false;
+ this->track_ended_ = false;
+
+ if ( !this->silence_ignored_ )
+ {
+ // play until non-silence or end of track
+ while ( this->emu_time < this->setup_.max_initial )
+ {
+ fill_buf( this );
+ if ( this->buf_remain | this->emu_track_ended_ )
+ break;
+ }
+ }
+
+ clear_time_vars( this );
+ return this->emu_error;
+}
+
+void end_track_if_error( struct Track_Filter* this, blargg_err_t err )
+{
+ if ( err )
+ {
+ this->emu_error = err;
+ this->emu_track_ended_ = true;
+ }
+}
+
+blargg_err_t track_skip( struct Track_Filter* this, int count )
+{
+ this->emu_error = NULL;
+ this->out_time += count;
+
+ // remove from silence and buf first
+ {
+ int n = min( count, this->silence_count );
+ this->silence_count -= n;
+ count -= n;
+
+ n = min( count, this->buf_remain );
+ this->buf_remain -= n;
+ count -= n;
+ }
+
+ if ( count && !this->emu_track_ended_ )
+ {
+ this->emu_time += count;
+ this->silence_time = this->emu_time; // would otherwise be invalid
+ end_track_if_error( this, skip_( this->emu_, count ) );
+ }
+
+ if ( !(this->silence_count | this->buf_remain) ) // caught up to emulator, so update track ended
+ this->track_ended_ |= this->emu_track_ended_;
+
+ return this->emu_error;
+}
+
+blargg_err_t skippy_( struct Track_Filter* this, int count )
+{
+ while ( count && !this->emu_track_ended_ )
+ {
+ int n = buf_size;
+ if ( n > count )
+ n = count;
+ count -= n;
+ RETURN_ERR( play_( this->emu_, n, this->buf ) );
+ }
+ return 0;
+}
+
+// Fading
+
+void track_set_fade( struct Track_Filter* this, int start, int length )
+{
+ this->fade_start = start;
+ this->fade_step = length / (fade_block_size * fade_shift);
+ if ( this->fade_step < 1 )
+ this->fade_step = 1;
+}
+
+bool is_fading( struct Track_Filter* this )
+{
+ return this->out_time >= this->fade_start && this->fade_start != indefinite_count;
+}
+
+// unit / pow( 2.0, (double) x / step )
+static int int_log( int x, int step, int unit )
+{
+ int shift = x / step;
+ int fraction = (x - shift * step) * unit / step;
+ return ((unit - fraction) + (fraction >> 1)) >> shift;
+}
+
+void handle_fade( struct Track_Filter* this, sample_t out [], int out_count )
+{
+ int i;
+ for ( i = 0; i < out_count; i += fade_block_size )
+ {
+ int const shift = 14;
+ int const unit = 1 << shift;
+ int gain = int_log( (this->out_time + i - this->fade_start) / fade_block_size,
+ this->fade_step, unit );
+ if ( gain < (unit >> fade_shift) )
+ this->track_ended_ = this->emu_track_ended_ = true;
+
+ sample_t* io = &out [i];
+ for ( int count = min( fade_block_size, out_count - i ); count; --count )
+ {
+ *io = (sample_t) ((*io * gain) >> shift);
+ ++io;
+ }
+ }
+}
+
+// Silence detection
+
+void emu_play( struct Track_Filter* this, sample_t out [], int count )
+{
+ this->emu_time += count;
+ if ( !this->emu_track_ended_ )
+ end_track_if_error( this, play_( this->emu_, count, out ) );
+ else
+ memset( out, 0, count * sizeof *out );
+}
+
+// number of consecutive silent samples at end
+static int count_silence( sample_t begin [], int size )
+{
+ sample_t first = *begin;
+ *begin = silence_threshold * 2; // sentinel
+ sample_t* p = begin + size;
+ while ( (unsigned) (*--p + silence_threshold) <= (unsigned) silence_threshold * 2 ) { }
+ *begin = first;
+ return size - (p - begin);
+}
+
+// fill internal buffer and check it for silence
+void fill_buf( struct Track_Filter* this )
+{
+ assert( !this->buf_remain );
+ if ( !this->emu_track_ended_ )
+ {
+ emu_play( this, this->buf, buf_size );
+ int silence = count_silence( this->buf, buf_size );
+ if ( silence < buf_size )
+ {
+ this->silence_time = this->emu_time - silence;
+ this->buf_remain = buf_size;
+ return;
+ }
+ }
+ this->silence_count += buf_size;
+}
+
+blargg_err_t track_play( struct Track_Filter* this, int out_count, sample_t out [] )
+{
+ this->emu_error = NULL;
+ if ( this->track_ended_ )
+ {
+ memset( out, 0, out_count * sizeof *out );
+ }
+ else
+ {
+ assert( this->emu_time >= this->out_time );
+
+ // prints nifty graph of how far ahead we are when searching for silence
+ //dprintf( "%*s \n", int ((emu_time - out_time) * 7 / 44100), "*" );
+
+ // use any remaining silence samples
+ int pos = 0;
+ if ( this->silence_count )
+ {
+ if ( !this->silence_ignored_ )
+ {
+ // during a run of silence, run emulator at >=2x speed so it gets ahead
+ int ahead_time = this->setup_.lookahead * (this->out_time + out_count - this->silence_time) +
+ this->silence_time;
+ while ( this->emu_time < ahead_time && !(this->buf_remain | this->emu_track_ended_) )
+ fill_buf( this );
+
+ // end track if sufficient silence has been found
+ if ( this->emu_time - this->silence_time > this->setup_.max_silence )
+ {
+ this->track_ended_ = this->emu_track_ended_ = true;
+ this->silence_count = out_count;
+ this->buf_remain = 0;
+ }
+ }
+
+ // fill from remaining silence
+ pos = min( this->silence_count, out_count );
+ memset( out, 0, pos * sizeof *out );
+ this->silence_count -= pos;
+ }
+
+ // use any remaining samples from buffer
+ if ( this->buf_remain )
+ {
+ int n = min( this->buf_remain, (int) (out_count - pos) );
+ memcpy( out + pos, this->buf + (buf_size - this->buf_remain), n * sizeof *out );
+ this->buf_remain -= n;
+ pos += n;
+ }
+
+ // generate remaining samples normally
+ int remain = out_count - pos;
+ if ( remain )
+ {
+ emu_play( this, out + pos, remain );
+ this->track_ended_ |= this->emu_track_ended_;
+
+ if ( this->silence_ignored_ && !is_fading( this ) )
+ {
+ // if left unupdated, ahead_time could become too large
+ this->silence_time = this->emu_time;
+ }
+ else
+ {
+ // check end for a new run of silence
+ int silence = count_silence( out + pos, remain );
+ if ( silence < remain )
+ this->silence_time = this->emu_time - silence;
+
+ if ( this->emu_time - this->silence_time >= buf_size )
+ fill_buf( this ); // cause silence detection on next play()
+ }
+ }
+
+ if ( is_fading( this ) )
+ handle_fade( this, out, out_count );
+ }
+ this->out_time += out_count;
+ return this->emu_error;
+}
diff --git a/apps/codecs/libgme/track_filter.h b/apps/codecs/libgme/track_filter.h
new file mode 100644
index 0000000..35049b9
--- /dev/null
+++ b/apps/codecs/libgme/track_filter.h
@@ -0,0 +1,90 @@
+// Removes silence from beginning of track, fades end of track. Also looks ahead
+// for excessive silence, and if found, ends track.
+
+// Game_Music_Emu 0.6-pre
+#ifndef TRACK_FILTER_H
+#define TRACK_FILTER_H
+
+#include "blargg_common.h"
+
+typedef short sample_t;
+typedef int sample_count_t;
+
+enum { indefinite_count = INT_MAX/2 + 1 };
+enum { buf_size = 2048 };
+
+struct setup_t {
+ sample_count_t max_initial; // maximum silence to strip from beginning of track
+ sample_count_t max_silence; // maximum silence in middle of track without it ending
+ int lookahead; // internal speed when looking ahead for silence (2=200% etc.)
+};
+
+struct Track_Filter {
+ void* emu_;
+ struct setup_t setup_;
+ const char* emu_error;
+ bool silence_ignored_;
+
+ // Timing
+ int out_time; // number of samples played since start of track
+ int emu_time; // number of samples emulator has generated since start of track
+ int emu_track_ended_; // emulator has reached end of track
+ volatile int track_ended_;
+
+ // Fading
+ int fade_start;
+ int fade_step;
+
+ // Silence detection
+ int silence_time; // absolute number of samples where most recent silence began
+ int silence_count; // number of samples of silence to play before using buf
+ int buf_remain; // number of samples left in silence buffer
+ sample_t buf [buf_size];
+};
+
+// Initializes filter. Must be done once before using object.
+blargg_err_t track_init( struct Track_Filter* this, void* );
+void track_create( struct Track_Filter* this );
+
+// Gets/sets setup
+static inline struct setup_t const* track_get_setup( struct Track_Filter* this ) { return &this->setup_; }
+static inline void track_setup( struct Track_Filter* this, struct setup_t const* s ) { this->setup_ = *s; }
+
+// Disables automatic end-of-track detection and skipping of silence at beginning
+static inline void track_ignore_silence( struct Track_Filter* this, bool disable ) { this->silence_ignored_ = disable; }
+
+// Clears state and skips initial silence in track
+blargg_err_t track_start( struct Track_Filter* this );
+
+// Sets time that fade starts, and how long until track ends.
+void track_set_fade( struct Track_Filter* this, sample_count_t start, sample_count_t length );
+
+// Generates n samples into buf
+blargg_err_t track_play( struct Track_Filter* this, int n, sample_t buf [] );
+
+// Skips n samples
+blargg_err_t track_skip( struct Track_Filter* this, int n );
+
+// Number of samples played/skipped since start_track()
+static inline int track_sample_count( struct Track_Filter* this ) { return this->out_time; }
+
+// True if track ended. Causes are end of source samples, end of fade,
+// or excessive silence.
+static inline bool track_ended( struct Track_Filter* this ) { return this->track_ended_; }
+
+// Clears state
+void track_stop( struct Track_Filter* this );
+
+// For use by callbacks
+
+// Sets internal "track ended" flag and stops generation of further source samples
+static inline void track_set_end( struct Track_Filter* this ) { this->emu_track_ended_ = true; }
+
+// For use by skip_() callback
+blargg_err_t skippy_( struct Track_Filter* this, int count );
+void fill_buf( struct Track_Filter* this );
+
+// Skip and play callbacks
+blargg_err_t skip_( void* emu, int count );
+blargg_err_t play_( void* emu, int count, sample_t out [] );
+#endif
diff --git a/apps/codecs/libgme/vgm_emu.c b/apps/codecs/libgme/vgm_emu.c
index 391bd02..4f8a2ad 100644
--- a/apps/codecs/libgme/vgm_emu.c
+++ b/apps/codecs/libgme/vgm_emu.c
@@ -23,11 +23,6 @@ const char* const gme_wrong_file_type = "Wrong file type for this emulator";
int const fm_gain = 3; // FM emulators are internally quieter to avoid 16-bit overflow
-int const silence_max = 6; // seconds
-int const silence_threshold = 0x10;
-long const fade_block_size = 512;
-int const fade_shift = 8; // fade ends with gain at 1.0 / (1 << fade_shift)
-
// VGM commands (Spec v1.50)
enum {
cmd_gg_stereo = 0x4F,
@@ -53,15 +48,8 @@ enum {
static void clear_track_vars( struct Vgm_Emu* this )
{
- this->out_time = 0;
- this->emu_time = 0;
- this->emu_track_ended_ = true;
- this->track_ended = true;
- this->fade_start = INT_MAX / 2 + 1;
- this->fade_step = 1;
- this->silence_time = 0;
- this->silence_count = 0;
- this->buf_remain = 0;
+ this->current_track = -1;
+ track_stop( &this->track_filter );
}
int play_frame( struct Vgm_Emu* this, blip_time_t blip_time, int sample_count, sample_t* buf );
@@ -77,9 +65,10 @@ void Vgm_init( struct Vgm_Emu* this )
this->tempo = (int)(FP_ONE_TEMPO);
// defaults
- this->max_initial_silence = 2;
- this->silence_lookahead = 1; // tracks should already be trimmed
- this->ignore_silence = false;
+ this->tfilter = *track_get_setup( &this->track_filter );
+ this->tfilter.max_initial = 2;
+ this->tfilter.lookahead = 1;
+ this->track_filter.silence_ignored_ = false;
// Disable oversampling by default
this->disable_oversampling = true;
@@ -88,10 +77,10 @@ void Vgm_init( struct Vgm_Emu* this )
Sms_apu_init( &this->psg );
Synth_init( &this->pcm );
- Buffer_init( &this->buf );
+ Buffer_init( &this->stereo_buf );
Blip_init( &this->blip_buf );
- // Init fm chips
+ // Init fm chips
Ym2413_init( &this->ym2413 );
Ym2612_init( &this->ym2612 );
@@ -105,7 +94,8 @@ void Vgm_init( struct Vgm_Emu* this )
// Unload
this->voice_count = 0;
- clear_track_vars( this );
+ this->voice_types = 0;
+ clear_track_vars( this );
}
// Track info
@@ -150,13 +140,13 @@ static void parse_gd3( byte const* in, byte const* end, struct track_info_t* out
int const gd3_header_size = 12;
-static long check_gd3_header( byte const* h, long remain )
+static int check_gd3_header( byte const* h, int remain )
{
if ( remain < gd3_header_size ) return 0;
if ( memcmp( h, "Gd3 ", 4 ) ) return 0;
if ( get_le32( h + 4 ) >= 0x200 ) return 0;
- long gd3_size = get_le32( h + 8 );
+ int gd3_size = get_le32( h + 8 );
if ( gd3_size > remain - gd3_header_size )
gd3_size = remain - gd3_header_size;
return gd3_size;
@@ -167,12 +157,12 @@ static byte const* gd3_data( struct Vgm_Emu* this, int* size )
if ( size )
*size = 0;
- long gd3_offset = get_le32( header( this )->gd3_offset ) - 0x2C;
+ int gd3_offset = get_le32( header( this )->gd3_offset ) - 0x2C;
if ( gd3_offset < 0 )
return 0;
byte const* gd3 = this->file_begin + header_size + gd3_offset;
- long gd3_size = check_gd3_header( gd3, this->file_end - gd3 );
+ int gd3_size = check_gd3_header( gd3, this->file_end - gd3 );
if ( !gd3_size )
return 0;
@@ -184,10 +174,10 @@ static byte const* gd3_data( struct Vgm_Emu* this, int* size )
static void get_vgm_length( struct header_t const* h, struct track_info_t* out )
{
- long length = get_le32( h->track_duration ) * 10 / 441;
+ int length = get_le32( h->track_duration ) * 10 / 441;
if ( length > 0 )
{
- long loop = get_le32( h->loop_duration );
+ int loop = get_le32( h->loop_duration );
if ( loop > 0 && get_le32( h->loop_offset ) )
{
out->loop_length = loop * 10 / 441;
@@ -285,24 +275,25 @@ blargg_err_t Vgm_load_mem( struct Vgm_Emu* this, byte const* new_data, long new_
Ym2612_enable( &this->ym2612, false );
Ym2413_enable( &this->ym2413, false );
- Sound_set_tempo( this, (int)(FP_ONE_TEMPO) );
-
this->voice_count = sms_osc_count;
+ static int const types [8] = {
+ wave_type+1, wave_type+2, wave_type+3, noise_type+1,
+ 0, 0, 0, 0
+ };
+ this->voice_types = types;
RETURN_ERR( setup_fm( this ) );
// do after FM in case output buffer is changed
// setup buffer
this->clock_rate_ = this->psg_rate;
- Buffer_clock_rate( &this->buf, this->psg_rate );
-
- // Setup bass
- this->buf_changed_count = Buffer_channels_changed_count( &this->buf );
+ Buffer_clock_rate( &this->stereo_buf, this->psg_rate );
+ RETURN_ERR( Buffer_set_channel_count( &this->stereo_buf, this->voice_count, this->voice_types ) );
+ this->buf_changed_count = Buffer_channels_changed_count( &this->stereo_buf );
// Post load
Sound_set_tempo( this, this->tempo );
Sound_mute_voices( this, this->mute_mask_ );
-
return 0;
}
@@ -362,42 +353,56 @@ blargg_err_t setup_fm( struct Vgm_Emu* this )
// Emulation
blip_time_t run( struct Vgm_Emu* this, vgm_time_t end_time );
-static blargg_err_t run_clocks( struct Vgm_Emu* this, blip_time_t* time_io, int msec )
+static blip_time_t run_psg( struct Vgm_Emu* this, int msec )
{
- *time_io = run( this, msec * this->vgm_rate / 1000 );
- Sms_apu_end_frame( &this->psg, *time_io );
- return 0;
+ blip_time_t t = run( this, msec * this->vgm_rate / 1000 );
+ Sms_apu_end_frame( &this->psg, t );
+ return t;
}
+static void check_end( struct Vgm_Emu* this )
+{
+ if( this->pos >= this->file_end )
+ track_set_end( &this->track_filter );
+}
+static blargg_err_t run_clocks( struct Vgm_Emu* this, blip_time_t* time_io, int msec )
+{
+ check_end( this );
+ *time_io = run_psg( this, msec );
+ return 0;
+}
-static blargg_err_t play_( struct Vgm_Emu* this, long count, sample_t* out )
+blargg_err_t play_( void *emu, int count, sample_t out [] )
{
+ struct Vgm_Emu* this = (struct Vgm_Emu*) emu;
+
if ( !uses_fm( this ) ) {
- long remain = count;
+ int remain = count;
while ( remain )
{
- remain -= Buffer_read_samples( &this->buf, &out [count - remain], remain );
+ Buffer_disable_immediate_removal( &this->stereo_buf );
+ remain -= Buffer_read_samples( &this->stereo_buf, &out [count - remain], remain );
if ( remain )
- {
- if ( this->buf_changed_count != Buffer_channels_changed_count( &this->buf ) )
+ {
+ if ( this->buf_changed_count != Buffer_channels_changed_count( &this->stereo_buf ) )
{
- this->buf_changed_count = Buffer_channels_changed_count( &this->buf );
+ this->buf_changed_count = Buffer_channels_changed_count( &this->stereo_buf );
// Remute voices
Sound_mute_voices( this, this->mute_mask_ );
}
- int msec = Buffer_length( &this->buf );
- blip_time_t clocks_emulated = (blargg_long) msec * this->clock_rate_ / 1000 - 100;
+ int msec = Buffer_length( &this->stereo_buf );
+ blip_time_t clocks_emulated = msec * this->clock_rate_ / 1000 - 100;
RETURN_ERR( run_clocks( this, &clocks_emulated, msec ) );
assert( clocks_emulated );
- Buffer_end_frame( &this->buf, clocks_emulated );
+ Buffer_end_frame( &this->stereo_buf, clocks_emulated );
}
}
return 0;
}
-
+
Resampler_play( &this->resampler, count, out, &this->blip_buf );
return 0;
}
@@ -414,7 +419,7 @@ static inline int command_len( int command )
check( len != 1 );
return len;
}
-
+
static inline fm_time_t to_fm_time( struct Vgm_Emu* this, vgm_time_t t )
{
return (t * this->fm_time_factor + this->fm_time_offset) >> fm_time_bits;
@@ -443,12 +448,10 @@ blip_time_t run( struct Vgm_Emu* this, vgm_time_t end_time )
{
vgm_time_t vgm_time = this->vgm_time;
byte const* pos = this->pos;
- if ( pos >= this->file_end )
+ /* if ( pos > this->file_end )
{
- this->emu_track_ended_ = true;
- /* if ( pos > data_end )
- warning( "Stream lacked end event" ); */
- }
+ warning( "Stream lacked end event" );
+ } */
while ( vgm_time < end_time && pos < this->file_end )
{
@@ -517,7 +520,7 @@ blip_time_t run( struct Vgm_Emu* this, vgm_time_t end_time )
case cmd_data_block: {
check( *pos == cmd_end );
int type = pos [1];
- long size = get_le32( pos + 2 );
+ int size = get_le32( pos + 2 );
pos += 6;
if ( type == pcm_block_type )
this->pcm_data = pos;
@@ -564,6 +567,8 @@ blip_time_t run( struct Vgm_Emu* this, vgm_time_t end_time )
int play_frame( struct Vgm_Emu* this, blip_time_t blip_time, int sample_count, blip_sample_t out [] )
{
+ check_end( this);
+
// to do: timing is working mostly by luck
int min_pairs = (unsigned) sample_count / 2;
int vgm_time = (min_pairs << fm_time_bits) / this->fm_time_factor - 1;
@@ -642,16 +647,18 @@ void update_fm_rates( struct Vgm_Emu* this, int* ym2413_rate, int* ym2612_rate )
// Music Emu
-blargg_err_t Vgm_set_sample_rate( struct Vgm_Emu* this, long rate )
+blargg_err_t Vgm_set_sample_rate( struct Vgm_Emu* this, int rate )
{
require( !this->sample_rate ); // sample rate can't be changed once set
RETURN_ERR( Blip_set_sample_rate( &this->blip_buf, rate, 1000 / 30 ) );
- RETURN_ERR( Buffer_set_sample_rate( &this->buf, rate, 1000 / 20 ) );
+ RETURN_ERR( Buffer_set_sample_rate( &this->stereo_buf, rate, 1000 / 20 ) );
// Set bass frequency
- Buffer_bass_freq( &this->buf, 80 );
+ Buffer_bass_freq( &this->stereo_buf, 80 );
this->sample_rate = rate;
+ RETURN_ERR( track_init( &this->track_filter, this ) );
+ this->tfilter.max_silence = 6 * stereo * this->sample_rate;
return 0;
}
@@ -679,7 +686,7 @@ void Sound_mute_voices( struct Vgm_Emu* this, int mask )
}
else
{
- struct channel_t ch = Buffer_channel( &this->buf );
+ struct channel_t ch = Buffer_channel( &this->stereo_buf, i );
assert( (ch.center && ch.left && ch.right) ||
(!ch.center && !ch.left && !ch.right) ); // all or nothing
set_voice( this, i, ch.center, ch.left, ch.right );
@@ -735,7 +742,6 @@ void Sound_set_tempo( struct Vgm_Emu* this, int t )
}
}
-void fill_buf( struct Vgm_Emu *this );
blargg_err_t Vgm_start_track( struct Vgm_Emu* this )
{
clear_track_vars( this );
@@ -750,7 +756,7 @@ blargg_err_t Vgm_start_track( struct Vgm_Emu* this )
this->vgm_time = 0;
if ( get_le32( header( this )->version ) >= 0x150 )
{
- long data_offset = get_le32( header( this )->data_offset );
+ int data_offset = get_le32( header( this )->data_offset );
check( data_offset );
if ( data_offset )
this->pos += data_offset + offsetof (struct header_t,data_offset) - 0x40;
@@ -764,266 +770,88 @@ blargg_err_t Vgm_start_track( struct Vgm_Emu* this )
if ( Ym2612_enabled( &this->ym2612 ) )
Ym2612_reset( &this->ym2612 );
- Blip_clear( &this->blip_buf, 1 );
+ Blip_clear( &this->blip_buf );
Resampler_clear( &this->resampler );
}
this->fm_time_offset = 0;
- Buffer_clear( &this->buf );
+ Buffer_clear( &this->stereo_buf );
- this->emu_track_ended_ = false;
- this->track_ended = false;
+ // convert filter times to samples
+ struct setup_t s = this->tfilter;
+ s.max_initial *= this->sample_rate * stereo;
+ #ifdef GME_DISABLE_SILENCE_LOOKAHEAD
+ s.lookahead = 1;
+ #endif
+ track_setup( &this->track_filter, &s );
- if ( !this->ignore_silence )
- {
- // play until non-silence or end of track
- long end;
- for ( end = this->max_initial_silence * stereo * this->sample_rate; this->emu_time < end; )
- {
- fill_buf( this );
- if ( this->buf_remain | (int) this->emu_track_ended_ )
- break;
- }
-
- this->emu_time = this->buf_remain;
- this->out_time = 0;
- this->silence_time = 0;
- this->silence_count = 0;
- }
- /* return track_ended() ? warning() : 0; */
- return 0;
+ return track_start( &this->track_filter );
}
// Tell/Seek
-static blargg_long msec_to_samples( blargg_long msec, long sample_rate )
+static int msec_to_samples( int msec, int sample_rate )
{
- blargg_long sec = msec / 1000;
+ int sec = msec / 1000;
msec -= sec * 1000;
return (sec * sample_rate + msec * sample_rate / 1000) * stereo;
}
-long Track_tell( struct Vgm_Emu* this )
+int Track_tell( struct Vgm_Emu* this )
{
- blargg_long rate = this->sample_rate * stereo;
- blargg_long sec = this->out_time / rate;
- return sec * 1000 + (this->out_time - sec * rate) * 1000 / rate;
+ int rate = this->sample_rate * stereo;
+ int sec = track_sample_count( &this->track_filter ) / rate;
+ return sec * 1000 + (track_sample_count( &this->track_filter ) - sec * rate) * 1000 / rate;
}
-blargg_err_t Track_seek( struct Vgm_Emu* this, long msec )
+blargg_err_t Track_seek( struct Vgm_Emu* this, int msec )
{
- blargg_long time = msec_to_samples( msec, this->sample_rate );
- if ( time < this->out_time )
- RETURN_ERR( Vgm_start_track( this ) );
- return Track_skip( this, time - this->out_time );
+ int time = msec_to_samples( msec, this->sample_rate );
+ if ( time < track_sample_count( &this->track_filter ) )
+ RETURN_ERR( Vgm_start_track( this ) );
+ return Track_skip( this, time - track_sample_count( &this->track_filter ) );
}
-blargg_err_t skip_( struct Vgm_Emu* this, long count );
-blargg_err_t Track_skip( struct Vgm_Emu* this, long count )
+blargg_err_t Track_skip( struct Vgm_Emu* this, int count )
{
- this->out_time += count;
-
- // remove from silence and buf first
- {
- long n = min( count, this->silence_count );
- this->silence_count -= n;
- count -= n;
-
- n = min( count, this->buf_remain );
- this->buf_remain -= n;
- count -= n;
- }
-
- if ( count && !this->emu_track_ended_ )
- {
- this->emu_time += count;
- if ( skip_( this, count ) )
- this->emu_track_ended_ = true;
- }
-
- if ( !(this->silence_count | this->buf_remain) ) // caught up to emulator, so update track ended
- this->track_ended |= this->emu_track_ended_;
-
- return 0;
+ require( this->current_track >= 0 ); // start_track() must have been called already
+ return track_skip( &this->track_filter, count );
}
-blargg_err_t skip_( struct Vgm_Emu* this, long count )
+blargg_err_t skip_( void* emu, int count )
{
+ struct Vgm_Emu* this = (struct Vgm_Emu*) emu;
+
// for long skip, mute sound
- const long threshold = 30000;
+ const int threshold = 32768;
if ( count > threshold )
{
int saved_mute = this->mute_mask_;
Sound_mute_voices( this, ~0 );
-
- while ( count > threshold / 2 && !this->emu_track_ended_ )
- {
- RETURN_ERR( play_( this, buf_size, this->buf_ ) );
- count -= buf_size;
- }
-
- Sound_mute_voices( this, saved_mute );
- }
-
- while ( count && !this->emu_track_ended_ )
- {
- long n = buf_size;
- if ( n > count )
- n = count;
- count -= n;
- RETURN_ERR( play_( this, n, this->buf_ ) );
- }
- return 0;
-}
-// Fading
-
-void Track_set_fade( struct Vgm_Emu* this, long start_msec, long length_msec )
-{
- this->fade_step = this->sample_rate * length_msec / (fade_block_size * fade_shift * 1000 / stereo);
- this->fade_start = msec_to_samples( start_msec, this->sample_rate );
-}
-
-// unit / pow( 2.0, (double) x / step )
-static int int_log( blargg_long x, int step, int unit )
-{
- int shift = x / step;
- int fraction = (x - shift * step) * unit / step;
- return ((unit - fraction) + (fraction >> 1)) >> shift;
-}
+ int n = count - threshold/2;
+ n &= ~(2048-1); // round to multiple of 2048
+ count -= n;
+ RETURN_ERR( skippy_( &this->track_filter, n ) );
-static void handle_fade( struct Vgm_Emu* this, long out_count, sample_t* out )
-{
- int i;
- for ( i = 0; i < out_count; i += fade_block_size )
- {
- int const shift = 14;
- int const unit = 1 << shift;
- int gain = int_log( (this->out_time + i - this->fade_start) / fade_block_size,
- this->fade_step, unit );
- if ( gain < (unit >> fade_shift) )
- this->track_ended = this->emu_track_ended_ = true;
-
- sample_t* io = &out [i];
- int count;
- for ( count = min( fade_block_size, out_count - i ); count; --count )
- {
- *io = (sample_t) ((*io * gain) >> shift);
- ++io;
- }
+ Sound_mute_voices( this, saved_mute );
}
-}
-// Silence detection
-
-static void emu_play( struct Vgm_Emu* this, long count, sample_t* out )
-{
- this->emu_time += count;
- if ( !this->emu_track_ended_ ) {
- if ( play_( this, count, out ) )
- this->emu_track_ended_ = true;
- }
- else
- memset( out, 0, count * sizeof *out );
+ return skippy_( &this->track_filter, count );
}
-// number of consecutive silent samples at end
-static long count_silence( sample_t* begin, long size )
-{
- sample_t first = *begin;
- *begin = silence_threshold; // sentinel
- sample_t* p = begin + size;
- while ( (unsigned) (*--p + silence_threshold / 2) <= (unsigned) silence_threshold ) { }
- *begin = first;
- return size - (p - begin);
-}
+// Fading
-// fill internal buffer and check it for silence
-void fill_buf( struct Vgm_Emu* this )
+void Track_set_fade( struct Vgm_Emu* this, int start_msec, int length_msec )
{
- assert( !this->buf_remain );
- if ( !this->emu_track_ended_ )
- {
- emu_play( this, buf_size, this->buf_ );
- long silence = count_silence( this->buf_, buf_size );
- if ( silence < buf_size )
- {
- this->silence_time = this->emu_time - silence;
- this->buf_remain = buf_size;
- return;
- }
- }
- this->silence_count += buf_size;
+ track_set_fade( &this->track_filter, msec_to_samples( start_msec, this->sample_rate ),
+ length_msec * this->sample_rate / (1000 / stereo) );
}
-blargg_err_t Vgm_play( struct Vgm_Emu* this, long out_count, sample_t* out )
+blargg_err_t Vgm_play( struct Vgm_Emu* this, int out_count, sample_t* out )
{
- if ( this->track_ended )
- {
- memset( out, 0, out_count * sizeof *out );
- }
- else
- {
- require( out_count % stereo == 0 );
-
- assert( this->emu_time >= this->out_time );
-
- // prints nifty graph of how far ahead we are when searching for silence
- //debug_printf( "%*s \n", int ((emu_time - out_time) * 7 / sample_rate()), "*" );
-
- long pos = 0;
- if ( this->silence_count )
- {
- // during a run of silence, run emulator at >=2x speed so it gets ahead
- long ahead_time = this->silence_lookahead * (this->out_time + out_count - this->silence_time) + this->silence_time;
- while ( this->emu_time < ahead_time && !(this->buf_remain | this->emu_track_ended_) )
- fill_buf( this );
-
- // fill with silence
- pos = min( this->silence_count, out_count );
- memset( out, 0, pos * sizeof *out );
- this->silence_count -= pos;
-
- if ( this->emu_time - this->silence_time > silence_max * stereo * this->sample_rate )
- {
- this->track_ended = this->emu_track_ended_ = true;
- this->silence_count = 0;
- this->buf_remain = 0;
- }
- }
-
- if ( this->buf_remain )
- {
- // empty silence buf
- long n = min( this->buf_remain, out_count - pos );
- memcpy( &out [pos], this->buf_ + (buf_size - this->buf_remain), n * sizeof *out );
- this->buf_remain -= n;
- pos += n;
- }
-
- // generate remaining samples normally
- long remain = out_count - pos;
- if ( remain )
- {
- emu_play( this, remain, out + pos );
- this->track_ended |= this->emu_track_ended_;
-
- if ( !this->ignore_silence || this->out_time > this->fade_start )
- {
- // check end for a new run of silence
- long silence = count_silence( out + pos, remain );
- if ( silence < remain )
- this->silence_time = this->emu_time - silence;
-
- if ( this->emu_time - this->silence_time >= buf_size )
- fill_buf( this ); // cause silence detection on next play()
- }
- }
-
- if ( this->out_time > this->fade_start )
- handle_fade( this, out_count, out );
- }
- this->out_time += out_count;
- return 0;
+ require( this->current_track >= 0 );
+ require( out_count % stereo == 0 );
+ return track_play( &this->track_filter, out_count, out );
}
diff --git a/apps/codecs/libgme/vgm_emu.h b/apps/codecs/libgme/vgm_emu.h
index 773b850..8c39482 100644
--- a/apps/codecs/libgme/vgm_emu.h
+++ b/apps/codecs/libgme/vgm_emu.h
@@ -1,11 +1,13 @@
// Sega Master System/Mark III, Sega Genesis/Mega Drive, BBC Micro VGM music file emulator
-// Game_Music_Emu 0.5.5
+// Game_Music_Emu 0.6-pre
#ifndef VGM_EMU_H
#define VGM_EMU_H
#include "blargg_common.h"
#include "blargg_source.h"
+
+#include "track_filter.h"
#include "resampler.h"
#include "multi_buffer.h"
#include "ym2413_emu.h"
@@ -17,7 +19,6 @@ typedef int fm_time_t;
enum { fm_time_bits = 12 };
enum { blip_time_bits = 12 };
-enum { buf_size = 2048 };
// VGM header format
enum { header_size = 0x40 };
@@ -46,9 +47,9 @@ enum { gme_max_field = 63 };
struct track_info_t
{
/* times in milliseconds; -1 if unknown */
- long length;
- long intro_length;
- long loop_length;
+ int length;
+ int intro_length;
+ int loop_length;
/* empty string if not available */
char game [64];
@@ -63,11 +64,11 @@ struct track_info_t
// YM2413 sound chip emulator. I can provide one I've modified to work with the library.
struct Vgm_Emu {
int fm_rate;
- long psg_rate;
- long vgm_rate;
+ int psg_rate;
+ int vgm_rate;
bool disable_oversampling;
- long fm_time_offset;
+ int fm_time_offset;
int fm_time_factor;
int blip_time_factor;
@@ -84,47 +85,34 @@ struct Vgm_Emu {
int dac_amp;
int dac_disabled; // -1 if disabled
- struct Blip_Buffer blip_buf;
-
// general
- long clock_rate_;
+ int current_track;
+ int clock_rate_;
unsigned buf_changed_count;
int max_initial_silence;
int voice_count;
+ int const *voice_types;
int mute_mask_;
int tempo;
int gain;
- long sample_rate;
-
- // track-specific
- 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
- volatile bool track_ended;
-
- // fading
- blargg_long fade_start;
- int fade_step;
-
- // silence detection
- int silence_lookahead; // speed to run emulator when looking ahead for silence
- bool ignore_silence;
- long silence_time; // number of samples where most recent silence began
- long silence_count; // number of samples of silence to play before using buf
- long buf_remain; // number of samples left in silence buffer
+ int sample_rate;
// larger items at the end
struct track_info_t info;
- sample_t buf_ [buf_size];
+
+ struct setup_t tfilter;
+ struct Track_Filter track_filter;
struct Ym2612_Emu ym2612;
struct Ym2413_Emu ym2413;
struct Sms_Apu psg;
struct Blip_Synth pcm;
+ struct Blip_Buffer blip_buf;
+
struct Resampler resampler;
- struct Stereo_Buffer buf;
+ struct Multi_Buffer stereo_buf;
};
void Vgm_init( struct Vgm_Emu* this );
@@ -135,7 +123,7 @@ static inline void Vgm_disable_oversampling( struct Vgm_Emu* this, bool disable
// Header for currently loaded file
static inline struct header_t *header( struct Vgm_Emu* this ) { return (struct header_t*) this->file_begin; }
-
+
// Basic functionality (see Gme_File.h for file loading/track info functions)
blargg_err_t Vgm_load_mem( struct Vgm_Emu* this, byte const* new_data, long new_size, bool parse_info );
@@ -144,34 +132,46 @@ blargg_err_t Vgm_load_mem( struct Vgm_Emu* this, byte const* new_data, long new_
static inline bool uses_fm( struct Vgm_Emu* this ) { return Ym2612_enabled( &this->ym2612 ) || Ym2413_enabled( &this->ym2413 ); }
// Set output sample rate. Must be called only once before loading file.
-blargg_err_t Vgm_set_sample_rate( struct Vgm_Emu* this, long sample_rate );
-
+blargg_err_t Vgm_set_sample_rate( struct Vgm_Emu* this, int sample_rate );
+
// Start a track, where 0 is the first track. Also clears warning string.
blargg_err_t Vgm_start_track( struct Vgm_Emu* this );
-
+
// Generate 'count' samples info 'buf'. Output is in stereo. Any emulation
// errors set warning string, and major errors also end track.
-blargg_err_t Vgm_play( struct Vgm_Emu* this, long count, sample_t* buf );
-
+blargg_err_t Vgm_play( struct Vgm_Emu* this, int count, sample_t* buf );
+
// Track status/control
// Number of milliseconds (1000 msec = 1 second) played since beginning of track
-long Track_tell( struct Vgm_Emu* this );
-
+int Track_tell( struct Vgm_Emu* this );
+
// Seek to new time in track. Seeking backwards or far forward can take a while.
-blargg_err_t Track_seek( struct Vgm_Emu* this, long msec );
-
+blargg_err_t Track_seek( struct Vgm_Emu* this, int msec );
+
// Skip n samples
-blargg_err_t Track_skip( struct Vgm_Emu* this, long n );
-
+blargg_err_t Track_skip( struct Vgm_Emu* this, int n );
+
// Set start time and length of track fade out. Once fade ends track_ended() returns
// true. Fade time can be changed while track is playing.
-void Track_set_fade( struct Vgm_Emu* this, long start_msec, long length_msec );
+void Track_set_fade( struct Vgm_Emu* this, int start_msec, int length_msec );
+
+// True if a track has reached its end
+static inline bool Track_ended( struct Vgm_Emu* this )
+{
+ return track_ended( &this->track_filter );
+}
+
+// Disables automatic end-of-track detection and skipping of silence at beginning
+static inline void Track_ignore_silence( struct Vgm_Emu* this, bool disable )
+{
+ this->track_filter.silence_ignored_ = disable;
+}
// Get track length in milliseconds
-static inline long Track_get_length( struct Vgm_Emu* this )
+static inline int Track_get_length( struct Vgm_Emu* this )
{
- long length = this->info.length;
+ int length = this->info.length;
if ( length <= 0 )
{
length = this->info.intro_length + 2 * this->info.loop_length; // intro + 2 loops
@@ -181,20 +181,20 @@ static inline long Track_get_length( struct Vgm_Emu* this )
return length;
}
-
+
// Sound customization
-
+
// Adjust song tempo, where 1.0 = normal, 0.5 = half speed, 2.0 = double speed.
// Track length as returned by track_info() assumes a tempo of 1.0.
void Sound_set_tempo( struct Vgm_Emu* this, int t );
-
+
// Mute/unmute voice i, where voice 0 is first voice
void Sound_mute_voice( struct Vgm_Emu* this, int index, bool mute );
-
+
// Set muting state of all voices at once using a bit mask, where -1 mutes them all,
// 0 unmutes them all, 0x01 mutes just the first voice, etc.
void Sound_mute_voices( struct Vgm_Emu* this, int mask );
-
+
// Change overall output amplitude, where 1.0 results in minimal clamping.
// Must be called before set_sample_rate().
static inline void Sound_set_gain( struct Vgm_Emu* this, int g )
@@ -203,5 +203,4 @@ static inline void Sound_set_gain( struct Vgm_Emu* this, int g )
this->gain = g;
}
-
#endif
diff --git a/apps/codecs/libgme/ym2612_emu.c b/apps/codecs/libgme/ym2612_emu.c
index a1f96e3..ee7947e 100644
--- a/apps/codecs/libgme/ym2612_emu.c
+++ b/apps/codecs/libgme/ym2612_emu.c
@@ -4,7 +4,6 @@
#include "ym2612_emu.h"
-#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>