forked from len0rd/rockbox
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
This commit is contained in:
parent
d089e10403
commit
13cbade08a
68 changed files with 4113 additions and 4893 deletions
|
|
@ -74,7 +74,7 @@ enum codec_status codec_run(void)
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((err = Ay_load_mem(&ay_emu, buf, ci->filesize))) {
|
if ((err = Ay_load_mem(&ay_emu, buf, ci->filesize))) {
|
||||||
DEBUGF("AY: Ay_load failed (%s)\n", err);
|
DEBUGF("AY: Ay_load_mem failed (%s)\n", err);
|
||||||
return CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -118,7 +118,7 @@ next_track:
|
||||||
|
|
||||||
/* Generate audio buffer */
|
/* Generate audio buffer */
|
||||||
err = Ay_play(&ay_emu, CHUNK_SIZE, samples);
|
err = Ay_play(&ay_emu, CHUNK_SIZE, samples);
|
||||||
if (err || ay_emu.track_ended) {
|
if (err || Track_ended(&ay_emu)) {
|
||||||
track++;
|
track++;
|
||||||
if (track >= ay_emu.track_count) break;
|
if (track >= ay_emu.track_count) break;
|
||||||
goto next_track;
|
goto next_track;
|
||||||
|
|
|
||||||
|
|
@ -67,8 +67,8 @@ enum codec_status codec_run(void)
|
||||||
return CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((err = Gbs_load(&gbs_emu, buf, ci->filesize))) {
|
if ((err = Gbs_load_mem(&gbs_emu, buf, ci->filesize))) {
|
||||||
DEBUGF("GBS: Gbs_load failed (%s)\n", err);
|
DEBUGF("GBS: Gbs_load_mem failed (%s)\n", err);
|
||||||
return CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -95,7 +95,7 @@ next_track:
|
||||||
|
|
||||||
/* Generate audio buffer */
|
/* Generate audio buffer */
|
||||||
err = Gbs_play(&gbs_emu, CHUNK_SIZE, samples);
|
err = Gbs_play(&gbs_emu, CHUNK_SIZE, samples);
|
||||||
if (err || gbs_emu.track_ended) {
|
if (err || Track_ended(&gbs_emu)) {
|
||||||
track++;
|
track++;
|
||||||
if (track >= gbs_emu.track_count) break;
|
if (track >= gbs_emu.track_count) break;
|
||||||
goto next_track;
|
goto next_track;
|
||||||
|
|
|
||||||
|
|
@ -67,8 +67,8 @@ enum codec_status codec_run(void)
|
||||||
return CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((err = Hes_load(&hes_emu, buf, ci->filesize))) {
|
if ((err = Hes_load_mem(&hes_emu, buf, ci->filesize))) {
|
||||||
DEBUGF("HES: Hes_load failed (%s)\n", err);
|
DEBUGF("HES: Hes_load_mem failed (%s)\n", err);
|
||||||
return CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -95,7 +95,7 @@ next_track:
|
||||||
|
|
||||||
/* Generate audio buffer */
|
/* Generate audio buffer */
|
||||||
err = Hes_play(&hes_emu, CHUNK_SIZE, samples);
|
err = Hes_play(&hes_emu, CHUNK_SIZE, samples);
|
||||||
if (err || hes_emu.track_ended) {
|
if (err || Track_ended(&hes_emu)) {
|
||||||
track++;
|
track++;
|
||||||
if (track >= hes_emu.track_count) break;
|
if (track >= hes_emu.track_count) break;
|
||||||
goto next_track;
|
goto next_track;
|
||||||
|
|
|
||||||
|
|
@ -98,7 +98,7 @@ next_track:
|
||||||
|
|
||||||
/* Generate audio buffer */
|
/* Generate audio buffer */
|
||||||
err = Kss_play(&kss_emu, CHUNK_SIZE, samples);
|
err = Kss_play(&kss_emu, CHUNK_SIZE, samples);
|
||||||
if (err || kss_emu.track_ended) {
|
if (err || Track_ended(&kss_emu)) {
|
||||||
track++;
|
track++;
|
||||||
if (track >= kss_emu.track_count) break;
|
if (track >= kss_emu.track_count) break;
|
||||||
goto next_track;
|
goto next_track;
|
||||||
|
|
|
||||||
|
|
@ -3,4 +3,5 @@ ay_cpu.c
|
||||||
ay_emu.c
|
ay_emu.c
|
||||||
blip_buffer.c
|
blip_buffer.c
|
||||||
multi_buffer.c
|
multi_buffer.c
|
||||||
|
track_filter.c
|
||||||
z80_cpu.c
|
z80_cpu.c
|
||||||
|
|
|
||||||
|
|
@ -6,3 +6,4 @@ gbs_emu.c
|
||||||
blip_buffer.c
|
blip_buffer.c
|
||||||
multi_buffer.c
|
multi_buffer.c
|
||||||
rom_data.c
|
rom_data.c
|
||||||
|
track_filter.c
|
||||||
|
|
|
||||||
|
|
@ -5,3 +5,4 @@ hes_emu.c
|
||||||
blip_buffer.c
|
blip_buffer.c
|
||||||
multi_buffer.c
|
multi_buffer.c
|
||||||
rom_data.c
|
rom_data.c
|
||||||
|
track_filter.c
|
||||||
|
|
|
||||||
|
|
@ -9,3 +9,4 @@ multi_buffer.c
|
||||||
rom_data.c
|
rom_data.c
|
||||||
emu8950.c
|
emu8950.c
|
||||||
emuadpcm.c
|
emuadpcm.c
|
||||||
|
track_filter.c
|
||||||
|
|
|
||||||
|
|
@ -12,3 +12,4 @@ nsfe_info.c
|
||||||
blip_buffer.c
|
blip_buffer.c
|
||||||
multi_buffer.c
|
multi_buffer.c
|
||||||
rom_data.c
|
rom_data.c
|
||||||
|
track_filter.c
|
||||||
|
|
|
||||||
|
|
@ -4,3 +4,4 @@ z80_cpu.c
|
||||||
blip_buffer.c
|
blip_buffer.c
|
||||||
multi_buffer.c
|
multi_buffer.c
|
||||||
rom_data.c
|
rom_data.c
|
||||||
|
track_filter.c
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ multi_buffer.c
|
||||||
resampler.c
|
resampler.c
|
||||||
vgm_emu.c
|
vgm_emu.c
|
||||||
ym2612_emu.c
|
ym2612_emu.c
|
||||||
|
track_filter.c
|
||||||
inflate/bbfuncs.c
|
inflate/bbfuncs.c
|
||||||
inflate/inflate.c
|
inflate/inflate.c
|
||||||
inflate/mallocer.c
|
inflate/mallocer.c
|
||||||
|
|
|
||||||
|
|
@ -17,12 +17,6 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
|
||||||
|
|
||||||
#include "blargg_source.h"
|
#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";
|
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
|
// 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 )
|
static void clear_track_vars( struct Ay_Emu *this )
|
||||||
{
|
{
|
||||||
this->current_track = -1;
|
this->current_track = -1;
|
||||||
this->out_time = 0;
|
track_stop( &this->track_filter );
|
||||||
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 */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Ay_init( struct Ay_Emu *this )
|
void Ay_init( struct Ay_Emu *this )
|
||||||
|
|
@ -59,18 +44,21 @@ void Ay_init( struct Ay_Emu *this )
|
||||||
this->track_count = 0;
|
this->track_count = 0;
|
||||||
|
|
||||||
// defaults
|
// defaults
|
||||||
this->max_initial_silence = 2;
|
this->tfilter = *track_get_setup( &this->track_filter );
|
||||||
this->ignore_silence = false;
|
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;
|
this->beeper_output = NULL;
|
||||||
disable_beeper( this );
|
disable_beeper( this );
|
||||||
|
|
||||||
Ay_apu_init( &this->apu );
|
Ay_apu_init( &this->apu );
|
||||||
Z80_init( &this->cpu );
|
Z80_init( &this->cpu );
|
||||||
|
|
||||||
this->silence_lookahead = 6 ;
|
// clears fields
|
||||||
|
this->voice_count = 0;
|
||||||
|
this->voice_types = 0;
|
||||||
|
clear_track_vars( this );
|
||||||
}
|
}
|
||||||
|
|
||||||
// Track info
|
// Track info
|
||||||
|
|
@ -107,35 +95,22 @@ static blargg_err_t parse_header( byte const in [], int size, struct file_t* out
|
||||||
return 0;
|
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
|
// 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;
|
this->clock_rate_ = rate;
|
||||||
Buffer_clock_rate( &this->stereo_buf, 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 );
|
assert( offsetof (struct header_t,track_info [2]) == header_size );
|
||||||
|
|
||||||
RETURN_ERR( parse_header( in, size, &this->file ) );
|
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" ); */
|
warning( "Unknown file version" ); */
|
||||||
|
|
||||||
this->voice_count = ay_osc_count + 1; // +1 for beeper
|
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);
|
Ay_apu_volume( &this->apu, this->gain);
|
||||||
|
|
||||||
// Setup buffer
|
// Setup buffer
|
||||||
change_clock_rate( this, spectrum_clock );
|
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 );
|
this->buf_changed_count = Buffer_channels_changed_count( &this->stereo_buf );
|
||||||
|
|
||||||
Sound_set_tempo( this, this->tempo );
|
Sound_set_tempo( this, this->tempo );
|
||||||
|
|
||||||
// Remute voices
|
|
||||||
Sound_mute_voices( this, this->mute_mask_ );
|
Sound_mute_voices( this, this->mute_mask_ );
|
||||||
|
|
||||||
this->track_count = this->file.header->max_track + 1;
|
this->track_count = this->file.header->max_track + 1;
|
||||||
this->m3u.size = 0;
|
|
||||||
return 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
|
require( !this->sample_rate ); // sample rate can't be changed once set
|
||||||
Buffer_init( &this->stereo_buf );
|
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 );
|
Buffer_bass_freq( &this->stereo_buf, 160 );
|
||||||
|
|
||||||
this->sample_rate = rate;
|
this->sample_rate = rate;
|
||||||
|
RETURN_ERR( track_init( &this->track_filter, this ) );
|
||||||
|
this->tfilter.max_silence = 6 * stereo * this->sample_rate;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -335,7 +315,7 @@ void Sound_mute_voices( struct Ay_Emu *this, int mask )
|
||||||
}
|
}
|
||||||
else
|
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) ||
|
assert( (ch.center && ch.left && ch.right) ||
|
||||||
(!ch.center && !ch.left && !ch.right) ); // all or nothing
|
(!ch.center && !ch.left && !ch.right) ); // all or nothing
|
||||||
set_voice( this, i, ch.center );
|
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);
|
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 )
|
blargg_err_t Ay_start_track( struct Ay_Emu *this, int track )
|
||||||
{
|
{
|
||||||
clear_track_vars( this );
|
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_addr( &this->apu, 7 );
|
||||||
Ay_apu_write_data( &this->apu, 0, 0x38 );
|
Ay_apu_write_data( &this->apu, 0, 0x38 );
|
||||||
|
|
||||||
this->emu_track_ended_ = false;
|
// convert filter times to samples
|
||||||
this->track_ended = false;
|
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 )
|
return track_start( &this->track_filter );
|
||||||
{
|
|
||||||
// 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tell/Seek
|
// 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;
|
msec -= sec * 1000;
|
||||||
return (sec * sample_rate + msec * sample_rate / 1000) * stereo;
|
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;
|
int rate = this->sample_rate * stereo;
|
||||||
blargg_long sec = this->out_time / rate;
|
int sec = track_sample_count( &this->track_filter ) / rate;
|
||||||
return sec * 1000 + (this->out_time - sec * rate) * 1000 / 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 );
|
int time = msec_to_samples( msec, this->sample_rate );
|
||||||
if ( time < this->out_time )
|
if ( time < track_sample_count( &this->track_filter ) )
|
||||||
RETURN_ERR( Ay_start_track( this, this->current_track ) );
|
RETURN_ERR( Ay_start_track( this, this->current_track ) );
|
||||||
return Track_skip( this, time - this->out_time );
|
return Track_skip( this, time - track_sample_count( &this->track_filter ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
blargg_err_t play_( struct Ay_Emu *this, long count, sample_t* out );
|
blargg_err_t skip_( void *emu, int count )
|
||||||
static blargg_err_t skip_( struct Ay_Emu *this, long count )
|
|
||||||
{
|
{
|
||||||
|
struct Ay_Emu* this = (struct Ay_Emu*) emu;
|
||||||
|
|
||||||
// for long skip, mute sound
|
// for long skip, mute sound
|
||||||
const long threshold = 30000;
|
const int threshold = 32768;
|
||||||
if ( count > threshold )
|
if ( count > threshold )
|
||||||
{
|
{
|
||||||
int saved_mute = this->mute_mask_;
|
int saved_mute = this->mute_mask_;
|
||||||
Sound_mute_voices( this, ~0 );
|
Sound_mute_voices( this, ~0 );
|
||||||
|
|
||||||
while ( count > threshold / 2 && !this->emu_track_ended_ )
|
int n = count - threshold/2;
|
||||||
{
|
n &= ~(2048-1); // round to multiple of 2048
|
||||||
RETURN_ERR( play_( this, buf_size, this->buf ) );
|
count -= n;
|
||||||
count -= buf_size;
|
RETURN_ERR( skippy_( &this->track_filter, n ) );
|
||||||
}
|
|
||||||
|
|
||||||
Sound_mute_voices( this, saved_mute );
|
Sound_mute_voices( this, saved_mute );
|
||||||
}
|
}
|
||||||
|
|
||||||
while ( count && !this->emu_track_ended_ )
|
return skippy_( &this->track_filter, count );
|
||||||
{
|
|
||||||
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 )
|
blargg_err_t Track_skip( struct Ay_Emu *this, int count )
|
||||||
{
|
{
|
||||||
require( this->current_track >= 0 ); // start_track() must have been called already
|
require( this->current_track >= 0 ); // start_track() must have been called already
|
||||||
this->out_time += count;
|
return track_skip( &this->track_filter, count );
|
||||||
|
}
|
||||||
|
|
||||||
// remove from silence and buf first
|
int Track_get_length( struct Ay_Emu* this, int n )
|
||||||
{
|
{
|
||||||
long n = min( count, this->silence_count );
|
int length = 0;
|
||||||
this->silence_count -= n;
|
|
||||||
count -= n;
|
|
||||||
|
|
||||||
n = min( count, this->buf_remain );
|
byte const* track_info = get_data( &this->file, this->file.tracks + n * 4 + 2, 6 );
|
||||||
this->buf_remain -= n;
|
if ( track_info )
|
||||||
count -= n;
|
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 ( count && !this->emu_track_ended_ )
|
if ( length <= 0 )
|
||||||
{
|
length = 120 * 1000; /* 2 minutes */
|
||||||
this->emu_time += count;
|
|
||||||
|
|
||||||
// End track if error
|
return length;
|
||||||
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
|
void Track_set_fade( struct Ay_Emu *this, int start_msec, int length_msec )
|
||||||
|
|
||||||
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);
|
track_set_fade( &this->track_filter, msec_to_samples( start_msec, this->sample_rate ),
|
||||||
this->fade_start = msec_to_samples( start_msec, this->sample_rate );
|
length_msec * this->sample_rate / (1000 / stereo) );
|
||||||
}
|
}
|
||||||
|
|
||||||
// unit / pow( 2.0, (double) x / step )
|
blargg_err_t Ay_play( struct Ay_Emu *this, int out_count, sample_t* out )
|
||||||
static int int_log( blargg_long x, int step, int unit )
|
|
||||||
{
|
{
|
||||||
int shift = x / step;
|
require( this->current_track >= 0 );
|
||||||
int fraction = (x - shift * step) * unit / step;
|
require( out_count % stereo == 0 );
|
||||||
return ((unit - fraction) + (fraction >> 1)) >> shift;
|
return track_play( &this->track_filter, out_count, out );
|
||||||
}
|
}
|
||||||
|
|
||||||
static void handle_fade( struct Ay_Emu *this, long out_count, sample_t* out )
|
blargg_err_t play_( void *emu, int count, sample_t* out )
|
||||||
{
|
{
|
||||||
int i;
|
struct Ay_Emu* this = (struct Ay_Emu*) emu;
|
||||||
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 remain = count;
|
||||||
int count;
|
|
||||||
for ( count = min( fade_block_size, out_count - i ); count; --count )
|
|
||||||
{
|
|
||||||
*io = (sample_t) ((*io * gain) >> shift);
|
|
||||||
++io;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Silence detection
|
|
||||||
|
|
||||||
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 );
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
// fill internal buffer and check it for silence
|
|
||||||
void fill_buf( struct Ay_Emu *this )
|
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
blargg_err_t Ay_play( struct Ay_Emu *this, long 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
blargg_err_t play_( struct Ay_Emu *this, long count, sample_t* out )
|
|
||||||
{
|
|
||||||
long remain = count;
|
|
||||||
while ( remain )
|
while ( remain )
|
||||||
{
|
{
|
||||||
|
Buffer_disable_immediate_removal( &this->stereo_buf );
|
||||||
remain -= Buffer_read_samples( &this->stereo_buf, &out [count - remain], remain );
|
remain -= Buffer_read_samples( &this->stereo_buf, &out [count - remain], remain );
|
||||||
if ( 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_ );
|
Sound_mute_voices( this, this->mute_mask_ );
|
||||||
}
|
}
|
||||||
int msec = Buffer_length( &this->stereo_buf );
|
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 ) );
|
RETURN_ERR( run_clocks( this, &clocks_emulated, msec ) );
|
||||||
assert( clocks_emulated );
|
assert( clocks_emulated );
|
||||||
Buffer_end_frame( &this->stereo_buf, clocks_emulated );
|
Buffer_end_frame( &this->stereo_buf, clocks_emulated );
|
||||||
|
|
|
||||||
|
|
@ -10,14 +10,12 @@
|
||||||
#include "z80_cpu.h"
|
#include "z80_cpu.h"
|
||||||
#include "ay_apu.h"
|
#include "ay_apu.h"
|
||||||
#include "m3u_playlist.h"
|
#include "m3u_playlist.h"
|
||||||
|
#include "track_filter.h"
|
||||||
typedef short sample_t;
|
|
||||||
|
|
||||||
// 64K memory to load code and data into before starting track. Caller
|
// 64K memory to load code and data into before starting track. Caller
|
||||||
// must parse the AY file.
|
// must parse the AY file.
|
||||||
enum { mem_size = 0x10000 };
|
enum { mem_size = 0x10000 };
|
||||||
enum { ram_addr = 0x4000 }; // where official RAM starts
|
enum { ram_addr = 0x4000 }; // where official RAM starts
|
||||||
enum { buf_size = 2048 };
|
|
||||||
|
|
||||||
// AY file header
|
// AY file header
|
||||||
enum { header_size = 0x14 };
|
enum { header_size = 0x14 };
|
||||||
|
|
@ -62,43 +60,30 @@ struct Ay_Emu {
|
||||||
bool cpc_mode;
|
bool cpc_mode;
|
||||||
|
|
||||||
// general
|
// general
|
||||||
int max_initial_silence;
|
|
||||||
int voice_count;
|
int voice_count;
|
||||||
|
int const* voice_types;
|
||||||
int mute_mask_;
|
int mute_mask_;
|
||||||
int tempo;
|
int tempo;
|
||||||
int gain;
|
int gain;
|
||||||
|
|
||||||
long sample_rate;
|
int sample_rate;
|
||||||
|
|
||||||
// track-specific
|
// track-specific
|
||||||
int current_track;
|
int current_track;
|
||||||
int track_count;
|
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
|
int clock_rate_;
|
||||||
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_;
|
|
||||||
unsigned buf_changed_count;
|
unsigned buf_changed_count;
|
||||||
|
|
||||||
// M3u Playlist
|
// M3u Playlist
|
||||||
struct M3u_Playlist m3u;
|
struct M3u_Playlist m3u;
|
||||||
|
|
||||||
// large items
|
// large items
|
||||||
|
struct setup_t tfilter;
|
||||||
|
struct Track_Filter track_filter;
|
||||||
|
|
||||||
struct Ay_Apu apu;
|
struct Ay_Apu apu;
|
||||||
sample_t buf [buf_size];
|
struct Multi_Buffer stereo_buf; // NULL if using custom buffer
|
||||||
struct Stereo_Buffer stereo_buf; // NULL if using custom buffer
|
|
||||||
struct Z80_Cpu cpu;
|
struct Z80_Cpu cpu;
|
||||||
struct mem_t mem;
|
struct mem_t mem;
|
||||||
};
|
};
|
||||||
|
|
@ -106,36 +91,48 @@ struct Ay_Emu {
|
||||||
// Basic functionality (see Gme_File.h for file loading/track info functions)
|
// Basic functionality (see Gme_File.h for file loading/track info functions)
|
||||||
void Ay_init( struct Ay_Emu* this );
|
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.
|
// 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.
|
// 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 );
|
blargg_err_t Ay_start_track( struct Ay_Emu* this, int track );
|
||||||
|
|
||||||
// Generate 'count' samples info 'buf'. Output is in stereo. Any emulation
|
// Generate 'count' samples info 'buf'. Output is in stereo. Any emulation
|
||||||
// errors set warning string, and major errors also end track.
|
// 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
|
// Track status/control
|
||||||
|
|
||||||
// Number of milliseconds (1000 msec = 1 second) played since beginning of track
|
// 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.
|
// 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
|
// 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
|
// 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.
|
// 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
|
// 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
|
// Sound customization
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,12 +7,12 @@
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <assert.h>
|
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
|
|
||||||
#undef BLARGG_COMMON_H
|
#undef BLARGG_COMMON_H
|
||||||
// allow blargg_config.h to #include blargg_common.h
|
// allow blargg_config.h to #include blargg_common.h
|
||||||
#include "blargg_config.h"
|
#include "blargg_config.h"
|
||||||
|
#include "blargg_source.h"
|
||||||
#ifndef BLARGG_COMMON_H
|
#ifndef BLARGG_COMMON_H
|
||||||
#define BLARGG_COMMON_H
|
#define BLARGG_COMMON_H
|
||||||
|
|
||||||
|
|
@ -98,19 +98,13 @@
|
||||||
static bool false = 0;
|
static bool false = 0;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// blargg_long/blargg_ulong = at least 32 bits, int if it's big enough
|
/* My code depends on int being at least 32 bits. Almost everything these days
|
||||||
#include <limits.h>
|
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
|
||||||
#if INT_MAX >= 0x7FFFFFFF
|
everywhere either, because int is often converted to implicitly when doing
|
||||||
typedef int blargg_long;
|
arithmetic on smaller types. */
|
||||||
#else
|
#if UINT_MAX < 0xFFFFFFFF
|
||||||
typedef long blargg_long;
|
#error "int must be at least 32 bits"
|
||||||
#endif
|
|
||||||
|
|
||||||
#if UINT_MAX >= 0xFFFFFFFF
|
|
||||||
typedef unsigned blargg_ulong;
|
|
||||||
#else
|
|
||||||
typedef unsigned long blargg_ulong;
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// int8_t etc.
|
// int8_t etc.
|
||||||
|
|
|
||||||
|
|
@ -64,45 +64,60 @@ static inline void blargg_verify_byte_order( void )
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline unsigned get_le16( void const* p ) {
|
static inline unsigned get_le16( void const* p )
|
||||||
return ((unsigned char const*) p) [1] * 0x100u +
|
{
|
||||||
((unsigned char const*) p) [0];
|
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 +
|
static inline unsigned get_be16( void const* p )
|
||||||
((unsigned char const*) p) [1];
|
{
|
||||||
|
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 +
|
static inline unsigned get_le32( void const* p )
|
||||||
((unsigned char const*) p) [2] * 0x00010000u +
|
{
|
||||||
((unsigned char const*) p) [1] * 0x00000100u +
|
return (unsigned) ((unsigned char const*) p) [3] << 24 |
|
||||||
((unsigned char const*) p) [0];
|
(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 +
|
static inline unsigned get_be32( void const* p )
|
||||||
((unsigned char const*) p) [1] * 0x00010000u +
|
{
|
||||||
((unsigned char const*) p) [2] * 0x00000100u +
|
return (unsigned) ((unsigned char const*) p) [0] << 24 |
|
||||||
((unsigned char const*) p) [3];
|
(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) [1] = (unsigned char) (n >> 8);
|
||||||
((unsigned char*) p) [0] = (unsigned char) n;
|
((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) [0] = (unsigned char) (n >> 8);
|
||||||
((unsigned char*) p) [1] = (unsigned char) n;
|
((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);
|
static inline void set_le32( void* p, unsigned n )
|
||||||
((unsigned char*) p) [2] = (unsigned char) (n >> 16);
|
{
|
||||||
((unsigned char*) p) [1] = (unsigned char) (n >> 8);
|
|
||||||
((unsigned char*) p) [0] = (unsigned char) 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);
|
static inline void set_be32( void* p, unsigned n )
|
||||||
((unsigned char*) p) [1] = (unsigned char) (n >> 16);
|
{
|
||||||
((unsigned char*) p) [2] = (unsigned char) (n >> 8);
|
|
||||||
((unsigned char*) p) [3] = (unsigned char) 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)
|
#if defined(BLARGG_NONPORTABLE)
|
||||||
|
|
@ -132,15 +147,21 @@ static inline void set_be32( void* p, blargg_ulong n ) {
|
||||||
|
|
||||||
#ifndef GET_LE16
|
#ifndef GET_LE16
|
||||||
#define GET_LE16( addr ) get_le16( addr )
|
#define GET_LE16( addr ) get_le16( addr )
|
||||||
#define GET_LE32( addr ) get_le32( addr )
|
|
||||||
#define SET_LE16( addr, data ) set_le16( addr, data )
|
#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 )
|
#define SET_LE32( addr, data ) set_le32( addr, data )
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef GET_BE16
|
#ifndef GET_BE16
|
||||||
#define GET_BE16( addr ) get_be16( addr )
|
#define GET_BE16( addr ) get_be16( addr )
|
||||||
#define GET_BE32( addr ) get_be32( addr )
|
|
||||||
#define SET_BE16( addr, data ) set_be16( addr, data )
|
#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 )
|
#define SET_BE32( addr, data ) set_be32( addr, data )
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,16 +2,13 @@
|
||||||
#ifndef BLARGG_SOURCE_H
|
#ifndef BLARGG_SOURCE_H
|
||||||
#define 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
|
// 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
|
// caller-supplied parameters and operations that are outside the control of the
|
||||||
// module. A failed requirement indicates a bug outside the module.
|
// module. A failed requirement indicates a bug outside the module.
|
||||||
// void require( bool expr );
|
// void require( bool expr );
|
||||||
#if defined(ROCKBOX)
|
#if defined(ROCKBOX)
|
||||||
|
#undef assert
|
||||||
|
#define assert( expr )
|
||||||
#undef require
|
#undef require
|
||||||
#define require( expr )
|
#define require( expr )
|
||||||
#else
|
#else
|
||||||
|
|
@ -36,28 +33,36 @@ static inline void blargg_dprintf_( const char* fmt, ... ) { }
|
||||||
#undef check
|
#undef check
|
||||||
#define check( expr ) ((void) 0)
|
#define check( expr ) ((void) 0)
|
||||||
|
|
||||||
// If expr yields error string, return it from current function, otherwise continue.
|
/* If expr yields non-NULL error string, returns it from current function,
|
||||||
#undef RETURN_ERR
|
otherwise continues normally. */
|
||||||
#define RETURN_ERR( expr ) do { \
|
#undef RETURN_ERR
|
||||||
blargg_err_t blargg_return_err_ = (expr); \
|
#define RETURN_ERR( expr ) \
|
||||||
if ( blargg_return_err_ ) return blargg_return_err_; \
|
do {\
|
||||||
|
blargg_err_t blargg_return_err_ = (expr);\
|
||||||
|
if ( blargg_return_err_ )\
|
||||||
|
return blargg_return_err_;\
|
||||||
} while ( 0 )
|
} while ( 0 )
|
||||||
|
|
||||||
// If ptr is 0, return out of memory error string.
|
/* If ptr is NULL, returns out-of-memory error, otherwise continues normally. */
|
||||||
#undef CHECK_ALLOC
|
#undef CHECK_ALLOC
|
||||||
#define CHECK_ALLOC( ptr ) do { if ( (ptr) == 0 ) return "Out of memory"; } while ( 0 )
|
#define CHECK_ALLOC( ptr ) \
|
||||||
|
do {\
|
||||||
|
if ( !(ptr) )\
|
||||||
|
return "Out of memory";\
|
||||||
|
} while ( 0 )
|
||||||
|
|
||||||
#ifndef max
|
#ifndef max
|
||||||
#define max(a,b) (((a) > (b)) ? (a) : (b))
|
#define max(a,b) (((a) > (b)) ? (a) : (b))
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef min
|
#ifndef min
|
||||||
#define min(a,b) (((a) < (b)) ? (a) : (b))
|
#define min(a,b) (((a) < (b)) ? (a) : (b))
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// TODO: good idea? bad idea?
|
// typedef unsigned char byte;
|
||||||
#undef byte
|
typedef unsigned char blargg_byte;
|
||||||
#define byte byte_
|
#undef byte
|
||||||
typedef unsigned char byte;
|
#define byte blargg_byte
|
||||||
|
|
||||||
// deprecated
|
// deprecated
|
||||||
#define BLARGG_CHECK_ALLOC CHECK_ALLOC
|
#define BLARGG_CHECK_ALLOC CHECK_ALLOC
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
#include "blip_buffer.h"
|
#include "blip_buffer.h"
|
||||||
|
|
||||||
#include <assert.h>
|
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
@ -19,23 +18,18 @@ 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 along with this module; if not, write to the Free Software Foundation,
|
||||||
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
|
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
|
||||||
|
|
||||||
#ifdef BLARGG_ENABLE_OPTIMIZER
|
#include "blargg_source.h"
|
||||||
#include BLARGG_ENABLE_OPTIMIZER
|
|
||||||
#endif
|
|
||||||
|
|
||||||
int const silent_buf_size = 1; // size used for Silent_Blip_Buffer
|
|
||||||
|
|
||||||
void Blip_init( struct Blip_Buffer* this )
|
void Blip_init( struct Blip_Buffer* this )
|
||||||
{
|
{
|
||||||
this->factor_ = (blip_ulong)LONG_MAX;
|
this->factor_ = UINT_MAX/2 + 1;;
|
||||||
this->offset_ = 0;
|
this->buffer_center_ = NULL;
|
||||||
this->buffer_size_ = 0;
|
this->buffer_size_ = 0;
|
||||||
this->sample_rate_ = 0;
|
this->sample_rate_ = 0;
|
||||||
this->reader_accum_ = 0;
|
this->bass_shift_ = 0;
|
||||||
this->bass_shift_ = 0;
|
this->clock_rate_ = 0;
|
||||||
this->clock_rate_ = 0;
|
this->bass_freq_ = 16;
|
||||||
this->bass_freq_ = 16;
|
this->length_ = 0;
|
||||||
this->length_ = 0;
|
|
||||||
|
|
||||||
// assumptions code makes about implementation-defined features
|
// assumptions code makes about implementation-defined features
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
|
|
@ -47,68 +41,55 @@ void Blip_init( struct Blip_Buffer* this )
|
||||||
i = 0x18000;
|
i = 0x18000;
|
||||||
assert( (short) i == -0x8000 );
|
assert( (short) i == -0x8000 );
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
Blip_clear( this );
|
||||||
}
|
}
|
||||||
|
|
||||||
void Blip_stop( struct Blip_Buffer* this )
|
void Blip_clear( struct Blip_Buffer* this )
|
||||||
{
|
{
|
||||||
if ( this->buffer_size_ != silent_buf_size )
|
bool const entire_buffer = true;
|
||||||
free( this->buffer_ );
|
|
||||||
}
|
|
||||||
|
|
||||||
void Blip_clear( struct Blip_Buffer* this, int entire_buffer )
|
this->offset_ = 0;
|
||||||
{
|
|
||||||
this->offset_ = 0;
|
|
||||||
this->reader_accum_ = 0;
|
this->reader_accum_ = 0;
|
||||||
this->modified_ = 0;
|
this->modified = false;
|
||||||
|
|
||||||
if ( this->buffer_ )
|
if ( this->buffer_ )
|
||||||
{
|
{
|
||||||
long count = (entire_buffer ? this->buffer_size_ : Blip_samples_avail( this ));
|
int count = (entire_buffer ? this->buffer_size_ : Blip_samples_avail( this ));
|
||||||
memset( this->buffer_, 0, (count + blip_buffer_extra_) * sizeof (buf_t_) );
|
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 )
|
// Limit to maximum size that resampled time can represent
|
||||||
{
|
int max_size = (((blip_resampled_time_t) -1) >> BLIP_BUFFER_ACCURACY) -
|
||||||
assert( 0 );
|
blip_buffer_extra_ - 64; // TODO: -64 isn't needed
|
||||||
return "Internal (tried to resize Silent_Blip_Buffer)";
|
int new_size = (new_rate * (msec + 1) + 999) / 1000;
|
||||||
}
|
if ( new_size > max_size )
|
||||||
|
new_size = max_size;
|
||||||
|
|
||||||
// start with maximum length that resampled time can represent
|
// Resize buffer
|
||||||
long new_size = (ULONG_MAX >> BLIP_BUFFER_ACCURACY) - blip_buffer_extra_ - 64;
|
if ( this->buffer_size_ != new_size ) {
|
||||||
if ( msec != blip_max_length )
|
this->buffer_center_ = this->buffer_ + BLIP_MAX_QUALITY/2;
|
||||||
{
|
this->buffer_size_ = new_size;
|
||||||
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 );
|
|
||||||
|
|
||||||
// update things based on the sample rate
|
// update things based on the sample rate
|
||||||
this->sample_rate_ = new_rate;
|
this->sample_rate_ = new_rate;
|
||||||
this->length_ = new_size * 1000 / new_rate - 1;
|
this->length_ = new_size * 1000 / new_rate - 1;
|
||||||
if ( msec )
|
|
||||||
assert( this->length_ == msec ); // ensure length is same as that passed in
|
|
||||||
if ( this->clock_rate_ )
|
if ( this->clock_rate_ )
|
||||||
Blip_set_clock_rate( this, this->clock_rate_ );
|
Blip_set_clock_rate( this, this->clock_rate_ );
|
||||||
Blip_bass_freq( this, this->bass_freq_ );
|
Blip_bass_freq( this, this->bass_freq_ );
|
||||||
|
|
||||||
Blip_clear( this, 1 );
|
Blip_clear( this );
|
||||||
|
|
||||||
return 0; // success
|
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
|
assert( factor > 0 || !this->sample_rate_ ); // fails if clock/output ratio is too large
|
||||||
return (blip_resampled_time_t) factor;
|
return (blip_resampled_time_t) factor;
|
||||||
}
|
}
|
||||||
|
|
@ -117,119 +98,111 @@ void Blip_bass_freq( struct Blip_Buffer* this, int freq )
|
||||||
{
|
{
|
||||||
this->bass_freq_ = freq;
|
this->bass_freq_ = freq;
|
||||||
int shift = 31;
|
int shift = 31;
|
||||||
if ( freq > 0 )
|
if ( freq > 0 && this->sample_rate_ )
|
||||||
{
|
{
|
||||||
shift = 13;
|
shift = 13;
|
||||||
long f = (freq << 16) / this->sample_rate_;
|
int f = (freq << 16) / this->sample_rate_;
|
||||||
while ( (f >>= 1) && --shift ) { }
|
while ( (f >>= 1) && --shift ) { }
|
||||||
}
|
}
|
||||||
this->bass_shift_ = shift;
|
this->bass_shift_ = shift;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Blip_end_frame( struct Blip_Buffer* this, blip_time_t t )
|
void Blip_end_frame( struct Blip_Buffer* this, blip_time_t t )
|
||||||
{
|
{
|
||||||
this->offset_ += t * this->factor_;
|
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
|
blip_resampled_time_t last_sample = Blip_resampled_time( this, t ) >> BLIP_BUFFER_ACCURACY;
|
||||||
this->offset_ -= (blip_resampled_time_t) count << 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_ )
|
if ( count > this->buffer_size_ )
|
||||||
count = this->buffer_size_;
|
count = this->buffer_size_;
|
||||||
blip_resampled_time_t time = (blip_resampled_time_t) count << BLIP_BUFFER_ACCURACY;
|
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_);
|
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 )
|
if ( count )
|
||||||
{
|
{
|
||||||
Blip_remove_silence( this, count );
|
Blip_remove_silence( this, count );
|
||||||
|
|
||||||
// copy remaining samples to beginning and clear old samples
|
// 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_ );
|
memmove( this->buffer_, this->buffer_ + count, remain * sizeof *this->buffer_ );
|
||||||
memset( this->buffer_ + remain, 0, count * 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 )
|
if ( count > max_samples )
|
||||||
count = max_samples;
|
count = max_samples;
|
||||||
|
|
||||||
if ( count )
|
if ( count )
|
||||||
{
|
{
|
||||||
int const bass = BLIP_READER_BASS( *this );
|
int const bass = this->bass_shift_;
|
||||||
BLIP_READER_BEGIN( reader, *this );
|
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 )
|
if ( !stereo )
|
||||||
{
|
{
|
||||||
blip_long n;
|
do
|
||||||
for ( n = count; n; --n )
|
|
||||||
{
|
{
|
||||||
blip_long s = BLIP_READER_READ( reader );
|
int s = reader_sum >> delta_bits;
|
||||||
if ( (blip_sample_t) s != s )
|
|
||||||
s = 0x7FFF - (s >> 24);
|
reader_sum -= reader_sum >> bass;
|
||||||
*out++ = (blip_sample_t) s;
|
reader_sum += reader [offset];
|
||||||
BLIP_READER_NEXT( reader, bass );
|
|
||||||
|
BLIP_CLAMP( s, s );
|
||||||
|
out [offset] = (blip_sample_t) s;
|
||||||
}
|
}
|
||||||
|
while ( ++offset );
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
blip_long n;
|
do
|
||||||
for ( n = count; n; --n )
|
|
||||||
{
|
{
|
||||||
blip_long s = BLIP_READER_READ( reader );
|
int s = reader_sum >> delta_bits;
|
||||||
if ( (blip_sample_t) s != s )
|
|
||||||
s = 0x7FFF - (s >> 24);
|
reader_sum -= reader_sum >> bass;
|
||||||
*out = (blip_sample_t) s;
|
reader_sum += reader [offset];
|
||||||
out += 2;
|
|
||||||
BLIP_READER_NEXT( reader, bass );
|
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 );
|
Blip_remove_samples( this, count );
|
||||||
}
|
}
|
||||||
return 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 )
|
delta_t* out = this->buffer_center_ + (this->offset_ >> BLIP_BUFFER_ACCURACY);
|
||||||
{
|
|
||||||
assert( 0 );
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
buf_t_* out = this->buffer_ + (this->offset_ >> BLIP_BUFFER_ACCURACY) + blip_widest_impulse_ / 2;
|
|
||||||
|
|
||||||
int const sample_shift = blip_sample_bits - 16;
|
int const sample_shift = blip_sample_bits - 16;
|
||||||
int prev = 0;
|
int prev = 0;
|
||||||
while ( count-- )
|
while ( --count >= 0 )
|
||||||
{
|
{
|
||||||
blip_long s = (blip_long) *in++ << sample_shift;
|
int s = *in++ << sample_shift;
|
||||||
*out += s - prev;
|
*out += s - prev;
|
||||||
prev = s;
|
prev = s;
|
||||||
++out;
|
++out;
|
||||||
|
|
@ -237,40 +210,16 @@ void Blip_mix_samples( struct Blip_Buffer* this, blip_sample_t const* in, long
|
||||||
*out -= prev;
|
*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_resampled_time_t Blip_resampled_time( struct Blip_Buffer* this, blip_time_t t )
|
|
||||||
{
|
|
||||||
return t * this->factor_ + this->offset_;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Blip_Synth
|
// Blip_Synth
|
||||||
|
|
||||||
|
void volume_unit( struct Blip_Synth* this, int new_unit )
|
||||||
|
{
|
||||||
|
this->delta_factor = (int) (new_unit * (1LL << blip_sample_bits) / FP_ONE_VOLUME);
|
||||||
|
}
|
||||||
|
|
||||||
void Synth_init( struct Blip_Synth* this )
|
void Synth_init( struct Blip_Synth* this )
|
||||||
{
|
{
|
||||||
this->buf = 0;
|
this->buf = 0;
|
||||||
this->last_amp = 0;
|
this->last_amp = 0;
|
||||||
this->delta_factor = 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);
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -4,202 +4,238 @@
|
||||||
#ifndef BLIP_BUFFER_H
|
#ifndef BLIP_BUFFER_H
|
||||||
#define BLIP_BUFFER_H
|
#define BLIP_BUFFER_H
|
||||||
|
|
||||||
#include <assert.h>
|
#include "blargg_common.h"
|
||||||
|
|
||||||
// internal
|
typedef unsigned blip_resampled_time_t;
|
||||||
#include "blargg_common.h"
|
typedef int blip_time_t;
|
||||||
#if INT_MAX >= 0x7FFFFFFF
|
typedef int clocks_t;
|
||||||
typedef int blip_long;
|
|
||||||
typedef unsigned blip_ulong;
|
|
||||||
#else
|
|
||||||
typedef long blip_long;
|
|
||||||
typedef unsigned long blip_ulong;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Time unit at source clock rate
|
// Output samples are 16-bit signed, with a range of -32768 to 32767
|
||||||
typedef blip_long blip_time_t;
|
typedef short blip_sample_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
|
#ifndef BLIP_BUFFER_ACCURACY
|
||||||
#define BLIP_BUFFER_ACCURACY 16
|
#define BLIP_BUFFER_ACCURACY 16
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Number bits in phase offset. Fewer than 6 bits (64 phase offsets) results in
|
// linear interpolation needs 8 bits
|
||||||
// noticeable broadband noise when synthesizing high frequency square waves.
|
|
||||||
// Affects size of Blip_Synth objects since they store the waveform directly.
|
|
||||||
#ifndef BLIP_PHASE_BITS
|
#ifndef BLIP_PHASE_BITS
|
||||||
#define BLIP_PHASE_BITS 8
|
#define BLIP_PHASE_BITS 8
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Output samples are 16-bit signed, with a range of -32768 to 32767
|
static int const blip_res = 1 << BLIP_PHASE_BITS;
|
||||||
typedef short blip_sample_t;
|
static int const blip_buffer_extra_ = BLIP_MAX_QUALITY + 2;
|
||||||
enum { blip_sample_max = 32767 };
|
|
||||||
enum { blip_widest_impulse_ = 16 };
|
// Properties of fixed-point sample position
|
||||||
enum { blip_buffer_extra_ = blip_widest_impulse_ + 2 };
|
typedef unsigned ufixed_t; // unsigned for more range, optimized shifts
|
||||||
enum { blip_res = 1 << BLIP_PHASE_BITS };
|
enum { fixed_bits = BLIP_BUFFER_ACCURACY }; // bits in fraction
|
||||||
enum { blip_max_length = 0 };
|
enum { fixed_unit = 1 << fixed_bits }; // 1.0 samples
|
||||||
enum { blip_default_length = 250 };
|
|
||||||
|
// 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)
|
// Maximun buffer size (48Khz, 50 ms)
|
||||||
enum { blip_buffer_max = 2466 };
|
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 {
|
struct Blip_Buffer {
|
||||||
blip_ulong factor_;
|
unsigned factor_;
|
||||||
blip_resampled_time_t offset_;
|
ufixed_t offset_;
|
||||||
buf_t_ buffer_ [blip_buffer_max];
|
delta_t* buffer_center_;
|
||||||
blip_long buffer_size_;
|
int buffer_size_;
|
||||||
blip_long reader_accum_;
|
int reader_accum_;
|
||||||
int bass_shift_;
|
int bass_shift_;
|
||||||
|
int bass_freq_;
|
||||||
|
int sample_rate_;
|
||||||
|
int clock_rate_;
|
||||||
|
int length_;
|
||||||
|
bool modified;
|
||||||
|
|
||||||
long sample_rate_;
|
delta_t buffer_ [blip_buffer_max];
|
||||||
long clock_rate_;
|
|
||||||
int bass_freq_;
|
|
||||||
int length_;
|
|
||||||
int modified_;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// not documented yet
|
// Blip_Buffer_ implementation
|
||||||
void Blip_set_modified( struct Blip_Buffer* this );
|
static inline ufixed_t to_fixed( struct Blip_Buffer *this, clocks_t t )
|
||||||
int Blip_clear_modified( struct Blip_Buffer* this );
|
{
|
||||||
void Blip_remove_silence( struct Blip_Buffer* this, long count );
|
return t * this->factor_ + this->offset_;
|
||||||
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 );
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Number of samples available for reading with read_samples()
|
||||||
|
static inline int Blip_samples_avail( struct Blip_Buffer* this )
|
||||||
|
{
|
||||||
|
return (int) (this->offset_ >> BLIP_BUFFER_ACCURACY);
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
// Initializes Blip_Buffer structure
|
// Initializes Blip_Buffer structure
|
||||||
void Blip_init( struct Blip_Buffer* this );
|
void Blip_init( struct Blip_Buffer* this );
|
||||||
|
|
||||||
// Stops (clear) Blip_Buffer structure
|
// Sets output sample rate and resizes and clears sample buffer
|
||||||
void Blip_stop( struct Blip_Buffer* this );
|
blargg_err_t Blip_set_sample_rate( struct Blip_Buffer* this, int samples_per_sec, int msec_length );
|
||||||
|
|
||||||
// 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 );
|
|
||||||
|
|
||||||
// Set number of source time units per second
|
|
||||||
static inline void Blip_set_clock_rate( struct Blip_Buffer* this, long cps )
|
|
||||||
{
|
|
||||||
this->factor_ = Blip_clock_rate_factor( this, this->clock_rate_ = cps );
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 );
|
|
||||||
|
|
||||||
// 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 );
|
|
||||||
|
|
||||||
// Additional optional features
|
|
||||||
|
|
||||||
// Current output sample rate
|
// 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_;
|
return this->sample_rate_;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Length of buffer, in milliseconds
|
// Sets number of source time units per second
|
||||||
static inline int Blip_length( struct Blip_Buffer* this )
|
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
|
// 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_;
|
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
|
// Set frequency high-pass filter frequency, where higher values reduce bass more
|
||||||
void Blip_bass_freq( struct Blip_Buffer* this, int frequency );
|
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 )
|
// Low-level features
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
|
||||||
|
// Resampled time (sorry, poor documentation right now)
|
||||||
|
|
||||||
|
// Resampled time is fixed-point, in terms of output samples.
|
||||||
|
|
||||||
|
// Converts clock count to resampled time
|
||||||
|
static inline blip_resampled_time_t Blip_resampled_duration( struct Blip_Buffer* this, int t )
|
||||||
{
|
{
|
||||||
return blip_widest_impulse_ / 2;
|
return t * this->factor_;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove all available samples and clear buffer to silence. If 'entire_buffer' is
|
// Converts clock time since beginning of current time frame to resampled time
|
||||||
// false, just clears out any samples waiting rather than the entire buffer.
|
static inline blip_resampled_time_t Blip_resampled_time( struct Blip_Buffer* this, blip_time_t t )
|
||||||
void Blip_clear( struct Blip_Buffer* this, int entire_buffer );
|
|
||||||
|
|
||||||
// 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);
|
return t * this->factor_ + this->offset_;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove 'count' samples from those waiting to be read
|
|
||||||
void Blip_remove_samples( struct Blip_Buffer* this, long count );
|
|
||||||
|
|
||||||
// Experimental features
|
|
||||||
|
|
||||||
// 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 );
|
|
||||||
|
|
||||||
// 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 );
|
|
||||||
|
|
||||||
// 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
|
// Range specifies the greatest expected change in amplitude. Calculate it
|
||||||
// by finding the difference between the maximum and minimum expected
|
// by finding the difference between the maximum and minimum expected
|
||||||
// amplitudes (max - min).
|
// amplitudes (max - min).
|
||||||
|
|
||||||
|
typedef char coeff_t;
|
||||||
|
|
||||||
struct Blip_Synth {
|
struct Blip_Synth {
|
||||||
struct Blip_Buffer* buf;
|
|
||||||
int last_amp;
|
|
||||||
int delta_factor;
|
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
|
// Initializes Blip_Synth structure
|
||||||
void Synth_init( struct Blip_Synth* this );
|
void Synth_init( struct Blip_Synth* this );
|
||||||
|
|
||||||
// Set overall volume of waveform
|
// Sets volume of amplitude delta unit
|
||||||
void Synth_volume( struct Blip_Synth* this, int v );
|
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
|
// Low-level interface
|
||||||
|
|
||||||
#if defined (__GNUC__) || _MSC_VER >= 1100
|
// (in >> sh & mask) * mul
|
||||||
#define BLIP_RESTRICT __restrict
|
#define BLIP_SH_AND_MUL( in, sh, mask, mul ) \
|
||||||
#else
|
((int) (in) / ((1U << (sh)) / (mul)) & (unsigned) ((mask) * (mul)))
|
||||||
#define BLIP_RESTRICT
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Works directly in terms of fractional output samples. Contact author for more info.
|
// (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. 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,
|
static inline void Synth_offset_resampled( struct Blip_Synth* this, blip_resampled_time_t time,
|
||||||
int delta, struct Blip_Buffer* blip_buf )
|
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
|
int const half_width = 1;
|
||||||
// need for a longer buffer as set by set_sample_rate().
|
|
||||||
assert( (blip_long) (time >> BLIP_BUFFER_ACCURACY) < blip_buf->buffer_size_ );
|
|
||||||
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;
|
delta_t* BLARGG_RESTRICT buf = delta_at( blip_buf, time );
|
||||||
|
delta *= this->delta_factor;
|
||||||
|
|
||||||
|
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.
|
// Kind of crappy, but doing shift after multiply results in overflow.
|
||||||
// Alternate way of delaying multiply by delta_factor results in worse
|
// Alternate way of delaying multiply by delta_factor results in worse
|
||||||
// sub-sample resolution.
|
// 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;
|
left -= right;
|
||||||
right += buf [1];
|
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;
|
int delta = amp - this->last_amp;
|
||||||
this->last_amp = 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
|
// Adds amplitude transition at time t. Delta can be positive or negative.
|
||||||
// rather than the one set with output(). Delta can be positive or negative.
|
// The actual change in amplitude is delta * volume.
|
||||||
// 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
|
|
||||||
static inline void Synth_offset_inline( struct Blip_Synth* this, blip_time_t t, int delta, struct Blip_Buffer* buf )
|
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
|
// Optimized reading from Blip_Buffer, for use in custom sample output
|
||||||
|
|
||||||
// Begin reading from buffer. Name should be unique to the current block.
|
// Begin reading from buffer. Name should be unique to the current block.
|
||||||
#define BLIP_READER_BEGIN( name, blip_buffer ) \
|
#define BLIP_READER_BEGIN( name, blip_buffer ) \
|
||||||
buf_t_* BLIP_RESTRICT name##_reader_buf = (blip_buffer).buffer_;\
|
const delta_t* BLARGG_RESTRICT name##_reader_buf = (blip_buffer).buffer_;\
|
||||||
blip_long name##_reader_accum = (blip_buffer).reader_accum_
|
int name##_reader_accum = (blip_buffer).reader_accum_
|
||||||
|
|
||||||
// Get value to pass to BLIP_READER_NEXT()
|
// Get value to pass to BLIP_READER_NEXT()
|
||||||
#define BLIP_READER_BASS( blip_buffer ) ((blip_buffer).bass_shift_)
|
#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
|
// Current sample
|
||||||
#define BLIP_READER_READ( name ) (name##_reader_accum >> (blip_sample_bits - 16))
|
#define BLIP_READER_READ( name ) (name##_reader_accum >> (blip_sample_bits - 16))
|
||||||
|
|
||||||
|
|
@ -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)];\
|
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
|
//// BLIP_CLAMP
|
||||||
|
|
||||||
#if defined (_M_IX86) || defined (_M_IA64) || defined (__i486__) || \
|
#if ARM_ARCH >= 6
|
||||||
defined (__x86_64__) || defined (__ia64__) || defined (__i386__)
|
#define BLIP_CLAMP( sample, out ) \
|
||||||
#define BLIP_X86 1
|
({ \
|
||||||
#define BLIP_CLAMP_( in ) in < -0x8000 || 0x7FFF < in
|
asm ("ssat %0, #16, %1" \
|
||||||
|
: "=r" ( out ) : "r"( sample ) ); \
|
||||||
|
out; \
|
||||||
|
})
|
||||||
#else
|
#else
|
||||||
#define BLIP_CLAMP_( in ) (blip_sample_t) in != in
|
#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; }
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Clamp sample to blip_sample_t range
|
|
||||||
#define BLIP_CLAMP( sample, out )\
|
|
||||||
{ if ( BLIP_CLAMP_( (sample) ) ) (out) = ((sample) >> 31) ^ 0x7FFF; }
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
|
||||||
#define LOG_MEM( addr, str, data ) data
|
#define LOG_MEM( addr, str, data ) data
|
||||||
#endif
|
#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 );
|
int result = *Cpu_get_code( &this->cpu, addr );
|
||||||
if ( (unsigned) (addr - io_addr) < io_size )
|
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 );
|
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 )
|
if ( (unsigned) (offset - (io_addr - base)) < io_size )
|
||||||
Apu_write_register( &this->apu, Time( this ), offset + base, data & 0xFF );
|
Apu_write_register( &this->apu, Time( this ), offset + base, data & 0xFF );
|
||||||
else if ( (unsigned) (offset - (0xFF06 - base)) < 2 )
|
else if ( (unsigned) (offset - (0xFF06 - base)) < 2 )
|
||||||
Update_timer( this );
|
update_timer( this );
|
||||||
else if ( offset == io_base - base )
|
else if ( offset == io_base - base )
|
||||||
this->ram [base - ram_addr + offset] = 0; // keep joypad return value 0
|
this->ram [base - ram_addr + offset] = 0; // keep joypad return value 0
|
||||||
else
|
else
|
||||||
this->ram [base - ram_addr + offset] = 0xFF;
|
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 );
|
(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;
|
offset -= 0xE000 - ram_addr;
|
||||||
if ( (unsigned) offset < 0x1F80 )
|
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 )
|
else if ( (unsigned) (offset - (0x2000 - ram_addr)) < 0x2000 )
|
||||||
{
|
{
|
||||||
Set_bank( this, data & 0xFF );
|
set_bank( this, data & 0xFF );
|
||||||
}
|
}
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
else if ( unsigned (addr - 0x8000) < 0x2000 || unsigned (addr - 0xE000) < 0x1F00 )
|
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
|
#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 );
|
(void) LOG_MEM( offset + io_base, "<", data );
|
||||||
|
|
||||||
this->ram [io_base - ram_addr + offset] = data;
|
this->ram [io_base - ram_addr + offset] = data;
|
||||||
if ( (unsigned) offset < 0x80 )
|
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 const io_base = 0xFF00;
|
||||||
int result = this->ram [io_base - ram_addr + offset];
|
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 ) );\
|
check( out == Read_mem( emu, addr ) );\
|
||||||
}
|
}
|
||||||
|
|
||||||
#define READ_MEM( emu, addr ) 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 WRITE_MEM( emu, addr, data ) write_mem( emu, addr, data )
|
||||||
|
|
||||||
#define WRITE_IO( emu, addr, data ) Write_io( 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 READ_IO( emu, addr, out ) out = read_io( emu, addr )
|
||||||
|
|
||||||
#define CPU_BEGIN \
|
#define CPU_BEGIN \
|
||||||
void Run_cpu( struct Gbs_Emu* this )\
|
void run_cpu( struct Gbs_Emu* this )\
|
||||||
{ \
|
{ \
|
||||||
struct Gb_Cpu* cpu = &this->cpu;
|
struct Gb_Cpu* cpu = &this->cpu;
|
||||||
#include "gb_cpu_run.h"
|
#include "gb_cpu_run.h"
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@
|
||||||
#include "gbs_emu.h"
|
#include "gbs_emu.h"
|
||||||
|
|
||||||
#include "blargg_endian.h"
|
#include "blargg_endian.h"
|
||||||
#include "blargg_source.h"
|
|
||||||
|
|
||||||
/* Copyright (C) 2003-2006 Shay Green. this module is free software; you
|
/* 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
|
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,
|
License along with this module; if not, write to the Free Software Foundation,
|
||||||
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
|
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";
|
const char gme_wrong_file_type [] = "Wrong file type for this emulator";
|
||||||
|
|
||||||
int const idle_addr = 0xF00D;
|
int const idle_addr = 0xF00D;
|
||||||
int const tempo_unit = 16;
|
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 )
|
static void clear_track_vars( struct Gbs_Emu* this )
|
||||||
{
|
{
|
||||||
this->current_track_ = -1;
|
this->current_track_ = -1;
|
||||||
this->out_time = 0;
|
track_stop( &this->track_filter );
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Gbs_init( struct Gbs_Emu* this )
|
void Gbs_init( struct Gbs_Emu* this )
|
||||||
|
|
@ -50,11 +36,13 @@ void Gbs_init( struct Gbs_Emu* this )
|
||||||
|
|
||||||
// Unload
|
// Unload
|
||||||
this->header.timer_mode = 0;
|
this->header.timer_mode = 0;
|
||||||
clear_track_vars( this );
|
|
||||||
|
|
||||||
this->ignore_silence = false;
|
// defaults
|
||||||
this->silence_lookahead = 6;
|
this->tfilter = *track_get_setup( &this->track_filter );
|
||||||
this->max_initial_silence = 21;
|
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) );
|
Sound_set_gain( this, (int)(FP_ONE_GAIN*1.2) );
|
||||||
|
|
||||||
Rom_init( &this->rom, 0x4000 );
|
Rom_init( &this->rom, 0x4000 );
|
||||||
|
|
@ -67,6 +55,11 @@ void Gbs_init( struct Gbs_Emu* this )
|
||||||
|
|
||||||
// Reduce apu sound clicks?
|
// Reduce apu sound clicks?
|
||||||
Apu_reduce_clicks( &this->apu, true );
|
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 )
|
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
|
// 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
|
// Unload
|
||||||
this->header.timer_mode = 0;
|
this->header.timer_mode = 0;
|
||||||
this->voice_count_ = 0;
|
this->voice_count_ = 0;
|
||||||
|
this->track_count = 0;
|
||||||
this->m3u.size = 0;
|
this->m3u.size = 0;
|
||||||
clear_track_vars( this );
|
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 );
|
Rom_set_addr( &this->rom, load_addr );
|
||||||
|
|
||||||
this->voice_count_ = osc_count;
|
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_ );
|
Apu_volume( &this->apu, this->gain_ );
|
||||||
|
|
||||||
// Change clock rate & setup buffer
|
// Change clock rate & setup buffer
|
||||||
this->clock_rate_ = 4194304;
|
this->clock_rate_ = 4194304;
|
||||||
Buffer_clock_rate( &this->stereo_buf, 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 );
|
this->buf_changed_count = Buffer_channels_changed_count( &this->stereo_buf );
|
||||||
|
|
||||||
// Post load
|
// Post load
|
||||||
Sound_set_tempo( this, this->tempo_ );
|
Sound_set_tempo( this, this->tempo_ );
|
||||||
|
|
||||||
// Remute voices
|
|
||||||
Sound_mute_voices( this, this->mute_mask_ );
|
Sound_mute_voices( this, this->mute_mask_ );
|
||||||
|
|
||||||
// Reset track count
|
// Set track count
|
||||||
this->track_count = this->header.track_count;
|
this->track_count = this->header.track_count;
|
||||||
return 0;
|
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
|
// 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 );
|
addr_t addr = mask_addr( n * this->rom.bank_size, this->rom.mask );
|
||||||
if ( addr == 0 && this->rom.size > this->rom.bank_size )
|
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 ) );
|
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
|
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
|
// Jumps to routine, given pointer to address in file header. Pushes idle_addr
|
||||||
// as return address, NOT old PC.
|
// 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 ) );
|
check( this->cpu.r.sp == get_le16( this->header.stack_ptr ) );
|
||||||
this->cpu.r.pc = get_le16( addr );
|
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 >> 8 );
|
||||||
Write_mem( this, --this->cpu.r.sp, idle_addr );
|
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;
|
this->end_time = end;
|
||||||
Cpu_set_time( &this->cpu, Cpu_time( &this->cpu ) - end );
|
Cpu_set_time( &this->cpu, Cpu_time( &this->cpu ) - end );
|
||||||
while ( true )
|
while ( true )
|
||||||
{
|
{
|
||||||
Run_cpu( this );
|
run_cpu( this );
|
||||||
if ( Cpu_time( &this->cpu ) >= 0 )
|
if ( Cpu_time( &this->cpu ) >= 0 )
|
||||||
break;
|
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 )
|
if ( Cpu_time( &this->cpu ) < this->next_play - this->end_time )
|
||||||
Cpu_set_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;
|
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 )
|
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;
|
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;
|
this->next_play -= end;
|
||||||
if ( this->next_play < 0 ) // happens when play routine takes too long
|
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;
|
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 )
|
while ( remain )
|
||||||
{
|
{
|
||||||
|
Buffer_disable_immediate_removal( &this->stereo_buf );
|
||||||
remain -= Buffer_read_samples( &this->stereo_buf, &out [count - remain], remain );
|
remain -= Buffer_read_samples( &this->stereo_buf, &out [count - remain], remain );
|
||||||
if ( 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_ );
|
Sound_mute_voices( this, this->mute_mask_ );
|
||||||
}
|
}
|
||||||
int msec = Buffer_length( &this->stereo_buf );
|
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 ) );
|
RETURN_ERR( run_clocks( this, clocks_emulated ) );
|
||||||
assert( clocks_emulated );
|
assert( clocks_emulated );
|
||||||
Buffer_end_frame( &this->stereo_buf, 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;
|
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
|
require( !this->sample_rate_ ); // sample rate can't be changed once set
|
||||||
Buffer_init( &this->stereo_buf );
|
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 );
|
Buffer_bass_freq( &this->stereo_buf, 300 );
|
||||||
|
|
||||||
this->sample_rate_ = rate;
|
this->sample_rate_ = rate;
|
||||||
|
RETURN_ERR( track_init( &this->track_filter, this ) );
|
||||||
|
this->tfilter.max_silence = 6 * stereo * this->sample_rate_;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -296,7 +299,7 @@ void Sound_mute_voices( struct Gbs_Emu* this, int mask )
|
||||||
}
|
}
|
||||||
else
|
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) ||
|
assert( (ch.center && ch.left && ch.right) ||
|
||||||
(!ch.center && !ch.left && !ch.right) ); // all or nothing
|
(!ch.center && !ch.left && !ch.right) ); // all or nothing
|
||||||
Apu_set_output( &this->apu, i, ch.center, ch.left, ch.right );
|
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);
|
this->tempo = (int) ((tempo_unit * FP_ONE_TEMPO) / t);
|
||||||
Apu_set_tempo( &this->apu, 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 )
|
blargg_err_t Gbs_start_track( struct Gbs_Emu* this, int track )
|
||||||
{
|
{
|
||||||
clear_track_vars( this );
|
clear_track_vars( this );
|
||||||
|
|
@ -330,7 +332,6 @@ blargg_err_t Gbs_start_track( struct Gbs_Emu* this, int track )
|
||||||
}
|
}
|
||||||
|
|
||||||
this->current_track_ = track;
|
this->current_track_ = track;
|
||||||
|
|
||||||
Buffer_clear( &this->stereo_buf );
|
Buffer_clear( &this->stereo_buf );
|
||||||
|
|
||||||
// Reset APU to state expected by most rips
|
// 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_reset( &this->cpu, this->rom.unmapped );
|
||||||
Cpu_map_code( &this->cpu, ram_addr, 0x10000 - ram_addr, this->ram );
|
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 ) );
|
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->next_play = this->play_period;
|
||||||
this->cpu.r.rp.fa = track;
|
this->cpu.r.rp.fa = track;
|
||||||
this->cpu.r.sp = get_le16( this->header.stack_ptr );
|
this->cpu.r.sp = get_le16( this->header.stack_ptr );
|
||||||
this->cpu_time = 0;
|
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;
|
// convert filter times to samples
|
||||||
this->track_ended = false;
|
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 )
|
return track_start( &this->track_filter );
|
||||||
{
|
|
||||||
// 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Track
|
// 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;
|
msec -= sec * 1000;
|
||||||
return (sec * sample_rate + msec * sample_rate / 1000) * stereo;
|
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;
|
int rate = this->sample_rate_ * stereo;
|
||||||
blargg_long sec = this->out_time / rate;
|
int sec = track_sample_count( &this->track_filter ) / rate;
|
||||||
return sec * 1000 + (this->out_time - sec * rate) * 1000 / 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_ );
|
int time = msec_to_samples( msec, this->sample_rate_ );
|
||||||
if ( time < this->out_time )
|
if ( time < track_sample_count( &this->track_filter ) )
|
||||||
RETURN_ERR( Gbs_start_track( this, this->current_track_ ) );
|
RETURN_ERR( Gbs_start_track( this, this->current_track_ ) );
|
||||||
return Track_skip( this, time - this->out_time );
|
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
|
// for long skip, mute sound
|
||||||
const long threshold = 30000;
|
const int threshold = 32768;
|
||||||
if ( count > threshold )
|
if ( count > threshold )
|
||||||
{
|
{
|
||||||
int saved_mute = this->mute_mask_;
|
int saved_mute = this->mute_mask_;
|
||||||
Sound_mute_voices( this, ~0 );
|
Sound_mute_voices( this, ~0 );
|
||||||
|
|
||||||
while ( count > threshold / 2 && !this->emu_track_ended_ )
|
int n = count - threshold/2;
|
||||||
{
|
n &= ~(2048-1); // round to multiple of 2048
|
||||||
RETURN_ERR( play_( this, buf_size, this->buf ) );
|
count -= n;
|
||||||
count -= buf_size;
|
RETURN_ERR( skippy_( &this->track_filter, n ) );
|
||||||
}
|
|
||||||
|
|
||||||
Sound_mute_voices( this, saved_mute );
|
Sound_mute_voices( this, saved_mute );
|
||||||
}
|
}
|
||||||
|
|
||||||
while ( count && !this->emu_track_ended_ )
|
return skippy_( &this->track_filter, count );
|
||||||
{
|
|
||||||
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 )
|
blargg_err_t Track_skip( struct Gbs_Emu* this, int count )
|
||||||
{
|
{
|
||||||
require( this->current_track_ >= 0 ); // start_track() must have been called already
|
require( this->current_track_ >= 0 ); // start_track() must have been called already
|
||||||
this->out_time += count;
|
return track_skip( &this->track_filter, 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fading
|
void Track_set_fade( struct Gbs_Emu* this, int start_msec, int length_msec )
|
||||||
|
|
||||||
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);
|
track_set_fade( &this->track_filter, msec_to_samples( start_msec, this->sample_rate_ ),
|
||||||
this->fade_start = msec_to_samples( start_msec, this->sample_rate_ );
|
length_msec * this->sample_rate_ / (1000 / stereo) );
|
||||||
}
|
}
|
||||||
|
|
||||||
// unit / pow( 2.0, (double) x / step )
|
blargg_err_t Gbs_play( struct Gbs_Emu* this, int out_count, sample_t* out )
|
||||||
static int int_log( blargg_long x, int step, int unit )
|
|
||||||
{
|
{
|
||||||
int shift = x / step;
|
require( this->current_track_ >= 0 );
|
||||||
int fraction = (x - shift * step) * unit / step;
|
require( out_count % stereo == 0 );
|
||||||
return ((unit - fraction) + (fraction >> 1)) >> shift;
|
return track_play( &this->track_filter, out_count, out );
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 );
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
// fill internal buffer and check it for silence
|
|
||||||
void fill_buf( struct Gbs_Emu* this )
|
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
blargg_err_t Gbs_play( struct Gbs_Emu* this, long 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;
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,15 +9,12 @@
|
||||||
#include "gb_apu.h"
|
#include "gb_apu.h"
|
||||||
#include "gb_cpu.h"
|
#include "gb_cpu.h"
|
||||||
#include "m3u_playlist.h"
|
#include "m3u_playlist.h"
|
||||||
|
#include "track_filter.h"
|
||||||
/* typedef uint8_t byte; */
|
|
||||||
typedef short sample_t;
|
|
||||||
|
|
||||||
enum { joypad_addr = 0xFF00 };
|
enum { joypad_addr = 0xFF00 };
|
||||||
enum { ram_addr = 0xA000 };
|
enum { ram_addr = 0xA000 };
|
||||||
enum { hi_page = 0xFF00 - ram_addr };
|
enum { hi_page = 0xFF00 - ram_addr };
|
||||||
enum { io_base = 0xFF00 };
|
enum { io_base = 0xFF00 };
|
||||||
enum { buf_size = 2048 };
|
|
||||||
|
|
||||||
// Selects which sound hardware to use. AGB hardware is cleaner than the
|
// Selects which sound hardware to use. AGB hardware is cleaner than the
|
||||||
// others. Doesn't take effect until next start_track().
|
// others. Doesn't take effect until next start_track().
|
||||||
|
|
@ -59,35 +56,18 @@ struct Gbs_Emu {
|
||||||
blip_time_t next_play;
|
blip_time_t next_play;
|
||||||
|
|
||||||
// Sound
|
// Sound
|
||||||
long clock_rate_;
|
int clock_rate_;
|
||||||
long sample_rate_;
|
int sample_rate_;
|
||||||
unsigned buf_changed_count;
|
unsigned buf_changed_count;
|
||||||
int voice_count_;
|
int voice_count_;
|
||||||
|
int const* voice_types_;
|
||||||
|
int mute_mask_;
|
||||||
int gain_;
|
int gain_;
|
||||||
int tempo_;
|
int tempo_;
|
||||||
|
|
||||||
// track-specific
|
// track-specific
|
||||||
byte track_count;
|
byte track_count;
|
||||||
volatile bool track_ended;
|
|
||||||
int current_track_;
|
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
|
// Larger items at the end
|
||||||
// Header for currently loaded file
|
// Header for currently loaded file
|
||||||
|
|
@ -96,11 +76,12 @@ struct Gbs_Emu {
|
||||||
// M3u Playlist
|
// M3u Playlist
|
||||||
struct M3u_Playlist m3u;
|
struct M3u_Playlist m3u;
|
||||||
|
|
||||||
|
struct setup_t tfilter;
|
||||||
|
struct Track_Filter track_filter;
|
||||||
|
|
||||||
struct Gb_Apu apu;
|
struct Gb_Apu apu;
|
||||||
struct Gb_Cpu cpu;
|
struct Gb_Cpu cpu;
|
||||||
struct Stereo_Buffer stereo_buf;
|
struct Multi_Buffer stereo_buf;
|
||||||
|
|
||||||
sample_t buf [buf_size];
|
|
||||||
|
|
||||||
// rom & ram
|
// rom & ram
|
||||||
struct Rom_Data rom;
|
struct Rom_Data rom;
|
||||||
|
|
@ -116,36 +97,48 @@ void Gbs_init( struct Gbs_Emu* this );
|
||||||
void Gbs_stop( struct Gbs_Emu* this );
|
void Gbs_stop( struct Gbs_Emu* this );
|
||||||
|
|
||||||
// Loads a file from memory
|
// 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.
|
// 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.
|
// Start a track, where 0 is the first track. Also clears warning string.
|
||||||
blargg_err_t Gbs_start_track( struct Gbs_Emu* this, int );
|
blargg_err_t Gbs_start_track( struct Gbs_Emu* this, int );
|
||||||
|
|
||||||
// Generate 'count' samples info 'buf'. Output is in stereo. Any emulation
|
// Generate 'count' samples info 'buf'. Output is in stereo. Any emulation
|
||||||
// errors set warning string, and major errors also end track.
|
// 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
|
// Track status/control
|
||||||
// Number of milliseconds (1000 msec = 1 second) played since beginning of track
|
// 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.
|
// 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
|
// 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
|
// 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.
|
// 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
|
// 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) ) {
|
if ( (this->m3u.size > 0) && (n < this->m3u.size) ) {
|
||||||
struct entry_t* entry = &this->m3u.entries [n];
|
struct entry_t* entry = &this->m3u.entries [n];
|
||||||
length = entry->length;
|
length = entry->length;
|
||||||
|
|
@ -175,19 +168,18 @@ static inline void Sound_set_gain( struct Gbs_Emu* this, int g )
|
||||||
this->gain_ = g;
|
this->gain_ = g;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Emulation (You shouldn't touch these)
|
// Emulation (You shouldn't touch these)
|
||||||
|
|
||||||
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 );
|
||||||
void Set_bank( struct Gbs_Emu* this, int );
|
void set_bank( struct Gbs_Emu* this, int );
|
||||||
void Update_timer( struct Gbs_Emu* this );
|
void update_timer( struct Gbs_Emu* this );
|
||||||
|
|
||||||
// Runs CPU until time becomes >= 0
|
// 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
|
// Reads/writes memory and I/O
|
||||||
int Read_mem( struct Gbs_Emu* this, addr_t addr );
|
int read_mem( struct Gbs_Emu* this, addr_t addr );
|
||||||
void Write_mem( struct Gbs_Emu* this, addr_t addr, int data );
|
void write_mem( struct Gbs_Emu* this, addr_t addr, int data );
|
||||||
|
|
||||||
// Current time
|
// Current time
|
||||||
static inline blip_time_t Time( struct Gbs_Emu* this )
|
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;
|
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
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -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 */
|
|
||||||
|
|
@ -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
|
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 balance_changed( struct Hes_Apu* this, struct Hes_Osc* osc )
|
||||||
static void Apu_balance_changed( struct Hes_Apu* this, struct Hes_Osc* osc )
|
|
||||||
{
|
{
|
||||||
static short const log_table [32] = { // ~1.5 db per step
|
static short const log_table [32] = { // ~1.5 db per step
|
||||||
#define ENTRY( factor ) (short) (factor * amp_range / 31.0 + 0.5)
|
#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);
|
int right = vol + (osc->balance << 1 & 0x1E) + (this->balance << 1 & 0x1E);
|
||||||
if ( right < 0 ) right = 0;
|
if ( right < 0 ) right = 0;
|
||||||
|
|
||||||
left = log_table [left ];
|
|
||||||
right = log_table [right];
|
|
||||||
|
|
||||||
// optimizing for the common case of being centered also allows easy
|
// optimizing for the common case of being centered also allows easy
|
||||||
// panning using Effects_Buffer
|
// panning using Effects_Buffer
|
||||||
osc->outputs [0] = osc->chans [0]; // center
|
|
||||||
osc->outputs [1] = 0;
|
// Separate balance into center volume and additional on either left or right
|
||||||
if ( left != 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 )
|
||||||
{
|
{
|
||||||
osc->outputs [0] = osc->chans [1]; // left
|
base += side;
|
||||||
osc->outputs [1] = osc->chans [2]; // right
|
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] )
|
||||||
|
{
|
||||||
|
base += side;
|
||||||
|
side = 0;
|
||||||
|
osc->output [0] = osc->output [1];
|
||||||
|
osc->output [1] = NULL;
|
||||||
|
osc->last_amp [1] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( center_waves )
|
if ( center_waves )
|
||||||
{
|
{
|
||||||
osc->last_amp [0] += (left - osc->volume [0]) * 16;
|
// TODO: this can leave a non-zero level in a buffer (minor)
|
||||||
osc->last_amp [1] += (right - osc->volume [1]) * 16;
|
osc->last_amp [0] += (base - osc->volume [0]) * 16;
|
||||||
|
osc->last_amp [1] += (side - osc->volume [1]) * 16;
|
||||||
}
|
}
|
||||||
|
|
||||||
osc->volume [0] = left;
|
osc->volume [0] = base;
|
||||||
osc->volume [1] = right;
|
osc->volume [1] = side;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Apu_init( struct Hes_Apu* this )
|
void Apu_init( struct Hes_Apu* this )
|
||||||
|
|
@ -71,11 +83,11 @@ void Apu_init( struct Hes_Apu* this )
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
osc--;
|
osc--;
|
||||||
osc->outputs [0] = 0;
|
osc->output [0] = NULL;
|
||||||
osc->outputs [1] = 0;
|
osc->output [1] = NULL;
|
||||||
osc->chans [0] = 0;
|
osc->outputs [0] = NULL;
|
||||||
osc->chans [1] = 0;
|
osc->outputs [1] = NULL;
|
||||||
osc->chans [2] = 0;
|
osc->outputs [2] = NULL;
|
||||||
}
|
}
|
||||||
while ( osc != this->oscs );
|
while ( osc != this->oscs );
|
||||||
|
|
||||||
|
|
@ -92,139 +104,183 @@ void Apu_reset( struct Hes_Apu* this )
|
||||||
{
|
{
|
||||||
osc--;
|
osc--;
|
||||||
memset( osc, 0, offsetof (struct Hes_Osc,outputs) );
|
memset( osc, 0, offsetof (struct Hes_Osc,outputs) );
|
||||||
osc->noise_lfsr = 1;
|
osc->lfsr = 1;
|
||||||
osc->control = 0x40;
|
osc->control = 0x40;
|
||||||
osc->balance = 0xFF;
|
osc->balance = 0xFF;
|
||||||
}
|
}
|
||||||
while ( osc != this->oscs );
|
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 );
|
// Must be silent (all NULL), mono (left and right NULL), or stereo (none NULL)
|
||||||
this->oscs [index].chans [0] = center;
|
require( !center || (center && !left && !right) || (center && left && right) );
|
||||||
this->oscs [index].chans [1] = left;
|
require( (unsigned) i < osc_count ); // fails if you pass invalid osc index
|
||||||
this->oscs [index].chans [2] = right;
|
|
||||||
|
|
||||||
struct Hes_Osc* osc = &this->oscs [osc_count];
|
if ( !center || !left || !right )
|
||||||
do
|
|
||||||
{
|
{
|
||||||
osc--;
|
left = center;
|
||||||
Apu_balance_changed( this, osc );
|
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
|
int vol0 = o->volume [0];
|
||||||
if ( osc_outputs_0 && this->control & 0x80 )
|
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;
|
// Update amplitudes
|
||||||
|
if ( out1 )
|
||||||
int const volume_0 = this->volume [0];
|
|
||||||
{
|
{
|
||||||
int delta = dac * volume_0 - this->last_amp [0];
|
int delta = dac * vol1 - o->last_amp [1];
|
||||||
if ( delta )
|
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 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int delta = dac * vol0 - o->last_amp [0];
|
||||||
|
if ( delta )
|
||||||
|
{
|
||||||
|
Synth_offset( syn, o->last_time, delta, out0 );
|
||||||
|
Blip_set_modified( out0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Blip_Buffer* const osc_outputs_1 = this->outputs [1];
|
// Don't generate if silent
|
||||||
int const volume_1 = this->volume [1];
|
if ( !(vol0 | vol1) )
|
||||||
if ( osc_outputs_1 )
|
out0 = NULL;
|
||||||
{
|
}
|
||||||
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 );
|
|
||||||
}
|
|
||||||
|
|
||||||
blip_time_t time = this->last_time + this->delay;
|
// 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 ( time < end_time )
|
||||||
{
|
{
|
||||||
if ( this->noise & 0x80 )
|
int period = (~o->noise & 0x1F) * 128;
|
||||||
{
|
if ( !period )
|
||||||
if ( volume_0 | volume_1 )
|
period = 64;
|
||||||
{
|
|
||||||
// noise
|
|
||||||
int const period = (32 - (this->noise & 0x1F)) * 64; // TODO: correct?
|
|
||||||
unsigned noise_lfsr = this->noise_lfsr;
|
|
||||||
do
|
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
while ( time < end_time );
|
|
||||||
|
|
||||||
this->noise_lfsr = noise_lfsr;
|
if ( noise && out0 )
|
||||||
assert( noise_lfsr );
|
{
|
||||||
|
unsigned lfsr = o->lfsr;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
int new_dac = -(lfsr & 1);
|
||||||
|
lfsr = (lfsr >> 1) ^ (0x30061 & new_dac);
|
||||||
|
|
||||||
|
int delta = (new_dac &= 0x1F) - dac;
|
||||||
|
if ( delta )
|
||||||
|
{
|
||||||
|
dac = new_dac;
|
||||||
|
Synth_offset( syn, time, delta * vol0, out0 );
|
||||||
|
if ( out1 )
|
||||||
|
Synth_offset( syn, time, delta * vol1, out1 );
|
||||||
|
}
|
||||||
|
time += period;
|
||||||
}
|
}
|
||||||
|
while ( time < end_time );
|
||||||
|
|
||||||
|
if ( !lfsr )
|
||||||
|
{
|
||||||
|
lfsr = 1;
|
||||||
|
check( false );
|
||||||
|
}
|
||||||
|
o->lfsr = lfsr;
|
||||||
|
|
||||||
|
Blip_set_modified( out0 );
|
||||||
|
if ( out1 )
|
||||||
|
Blip_set_modified( out1 );
|
||||||
}
|
}
|
||||||
else if ( !(this->control & 0x40) )
|
else
|
||||||
{
|
{
|
||||||
// wave
|
// Maintain phase when silent
|
||||||
int phase = (this->phase + 1) & 0x1F; // pre-advance for optimal inner loop
|
int count = (end_time - time + period - 1) / period;
|
||||||
int period = this->period * 2;
|
time += count * period;
|
||||||
if ( period >= 14 && (volume_0 | volume_1) )
|
|
||||||
{
|
|
||||||
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 );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
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
|
// not worth it
|
||||||
blargg_long count = (end_time - time + period - 1) / period;
|
//while ( count-- )
|
||||||
phase += count; // phase will be masked below
|
// o->lfsr = (o->lfsr >> 1) ^ (0x30061 * (o->lfsr & 1));
|
||||||
time += count * period;
|
|
||||||
}
|
|
||||||
this->phase = (phase - 1) & 0x1F; // undo pre-advance
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
time -= end_time;
|
o->noise_delay = 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;
|
|
||||||
}
|
}
|
||||||
this->last_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 )
|
||||||
|
{
|
||||||
|
dac = new_dac;
|
||||||
|
Synth_offset( syn, time, delta * vol0, out0 );
|
||||||
|
if ( out1 )
|
||||||
|
Synth_offset( syn, time, delta * vol1, out1 );
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
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 )
|
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
|
do
|
||||||
{
|
{
|
||||||
osc--;
|
osc--;
|
||||||
Osc_run_until( osc, &this->synth, time );
|
run_osc( osc, &this->synth, time );
|
||||||
Apu_balance_changed( this, this->oscs );
|
balance_changed( this, this->oscs );
|
||||||
}
|
}
|
||||||
while ( osc != 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 )
|
else if ( this->latch < osc_count )
|
||||||
{
|
{
|
||||||
struct Hes_Osc* osc = &this->oscs [this->latch];
|
struct Hes_Osc* osc = &this->oscs [this->latch];
|
||||||
Osc_run_until( osc, &this->synth, time );
|
run_osc( osc, &this->synth, time );
|
||||||
switch ( addr )
|
switch ( addr )
|
||||||
{
|
{
|
||||||
case 0x802:
|
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 )
|
if ( osc->control & 0x40 & ~data )
|
||||||
osc->phase = 0;
|
osc->phase = 0;
|
||||||
osc->control = data;
|
osc->control = data;
|
||||||
Apu_balance_changed( this, osc );
|
balance_changed( this, osc );
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x805:
|
case 0x805:
|
||||||
osc->balance = data;
|
osc->balance = data;
|
||||||
Apu_balance_changed( this, osc );
|
balance_changed( this, osc );
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x806:
|
case 0x806:
|
||||||
|
|
@ -289,9 +345,9 @@ void Apu_write_data( struct Hes_Apu* this, blip_time_t time, int addr, int data
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x807:
|
case 0x807:
|
||||||
if ( osc >= &this->oscs [4] )
|
osc->noise = data;
|
||||||
osc->noise = data;
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x809:
|
case 0x809:
|
||||||
if ( !(data & 0x80) && (data & 0x03) != 0 ) {
|
if ( !(data & 0x80) && (data & 0x03) != 0 ) {
|
||||||
dprintf( "HES LFO not supported\n" );
|
dprintf( "HES LFO not supported\n" );
|
||||||
|
|
@ -307,7 +363,7 @@ void Apu_end_frame( struct Hes_Apu* this, blip_time_t end_time )
|
||||||
{
|
{
|
||||||
osc--;
|
osc--;
|
||||||
if ( end_time > osc->last_time )
|
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 );
|
assert( osc->last_time >= end_time );
|
||||||
osc->last_time -= end_time;
|
osc->last_time -= end_time;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,40 +5,46 @@
|
||||||
#define HES_APU_H
|
#define HES_APU_H
|
||||||
|
|
||||||
#include "blargg_common.h"
|
#include "blargg_common.h"
|
||||||
|
#include "blargg_source.h"
|
||||||
#include "blip_buffer.h"
|
#include "blip_buffer.h"
|
||||||
|
|
||||||
enum { amp_range = 0x8000 };
|
enum { amp_range = 0x8000 };
|
||||||
enum { osc_count = 6 };
|
enum { osc_count = 6 }; // 0 <= chan < osc_count
|
||||||
enum { start_addr = 0x0800 };
|
|
||||||
enum { end_addr = 0x0809 };
|
// Registers are at io_addr to io_addr+io_size-1
|
||||||
|
enum { apu_io_addr = 0x0800 };
|
||||||
|
enum { apu_io_size = 10 };
|
||||||
|
|
||||||
struct Hes_Osc
|
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];
|
short volume [2];
|
||||||
int last_amp [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];
|
blip_time_t last_time;
|
||||||
struct Blip_Buffer* chans [3];
|
struct Blip_Buffer* output [2];
|
||||||
unsigned noise_lfsr;
|
struct Blip_Buffer* outputs [3];
|
||||||
unsigned char control;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
void Osc_run_until( struct Hes_Osc* this, struct Blip_Synth* synth, blip_time_t );
|
void Osc_run_until( struct Hes_Osc* this, struct Blip_Synth* synth, blip_time_t );
|
||||||
|
|
||||||
struct Hes_Apu {
|
struct Hes_Apu {
|
||||||
struct Hes_Osc oscs [osc_count];
|
|
||||||
|
|
||||||
int latch;
|
int latch;
|
||||||
int balance;
|
int balance;
|
||||||
struct Blip_Synth synth;
|
struct Blip_Synth synth;
|
||||||
|
struct Hes_Osc oscs [osc_count];
|
||||||
};
|
};
|
||||||
|
|
||||||
// Init HES apu sound chip
|
// 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_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 );
|
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 );
|
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 );
|
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 ); }
|
static inline void Apu_volume( struct Hes_Apu* this, int v ) { Synth_volume( &this->synth, (v*9)/5 / osc_count / amp_range ); }
|
||||||
|
|
|
||||||
|
|
@ -12,8 +12,8 @@ enum { adpcm_amp_range = 2048 };
|
||||||
enum { adpcm_osc_count = 1 }; // 0 <= chan < osc_count
|
enum { adpcm_osc_count = 1 }; // 0 <= chan < osc_count
|
||||||
|
|
||||||
// Registers are at io_addr to io_addr+io_size-1
|
// Registers are at io_addr to io_addr+io_size-1
|
||||||
enum { io_addr = 0x1800 };
|
enum { adpcm_io_addr = 0x1800 };
|
||||||
enum { io_size = 0x400 };
|
enum { adpcm_io_size = 0x400 };
|
||||||
|
|
||||||
struct State
|
struct State
|
||||||
{
|
{
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,56 +1,52 @@
|
||||||
// PC Engine CPU emulator for use with HES music files
|
// 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
|
#ifndef HES_CPU_H
|
||||||
#define HES_CPU_H
|
#define HES_CPU_H
|
||||||
|
|
||||||
#include "blargg_common.h"
|
#include "blargg_common.h"
|
||||||
|
#include "blargg_source.h"
|
||||||
|
|
||||||
typedef blargg_long hes_time_t; // clock cycle count
|
typedef int hes_time_t; // clock cycle count
|
||||||
typedef unsigned hes_addr_t; // 16-bit address
|
typedef int hes_addr_t; // 16-bit address
|
||||||
|
|
||||||
struct Hes_Emu;
|
struct Hes_Emu;
|
||||||
|
|
||||||
enum { future_hes_time = LONG_MAX / 2 + 1 };
|
enum { future_time = INT_MAX/2 + 1 };
|
||||||
enum { page_size = 0x2000 };
|
enum { page_bits = 13 };
|
||||||
enum { page_shift = 13 };
|
enum { page_size = 1 << page_bits };
|
||||||
enum { page_count = 8 };
|
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
|
// Can read this many bytes past end of a page
|
||||||
enum { cpu_padding = 8 };
|
enum { cpu_padding = 8 };
|
||||||
enum { irq_inhibit = 0x04 };
|
enum { irq_inhibit_mask = 0x04 };
|
||||||
|
enum { idle_addr = 0x1FFF };
|
||||||
|
|
||||||
// Cpu state
|
// Cpu state
|
||||||
struct state_t {
|
struct cpu_state_t {
|
||||||
uint8_t const* code_map [page_count + 1];
|
byte const* code_map [page_count + 1];
|
||||||
hes_time_t base;
|
hes_time_t base;
|
||||||
blargg_long time;
|
int time;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Cpu registers
|
// NOT kept updated during emulation.
|
||||||
struct registers_t {
|
struct registers_t {
|
||||||
uint16_t pc;
|
uint16_t pc;
|
||||||
uint8_t a;
|
byte a;
|
||||||
uint8_t x;
|
byte x;
|
||||||
uint8_t y;
|
byte y;
|
||||||
uint8_t status;
|
byte flags;
|
||||||
uint8_t sp;
|
byte sp;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Hes_Cpu {
|
struct Hes_Cpu {
|
||||||
struct registers_t r;
|
struct registers_t r;
|
||||||
|
|
||||||
hes_time_t irq_time;
|
hes_time_t irq_time_;
|
||||||
hes_time_t end_time;
|
hes_time_t end_time_;
|
||||||
|
|
||||||
struct state_t* state; // points to state_ or a local copy within run()
|
struct cpu_state_t* cpu_state; // points to state_ or a local copy within run()
|
||||||
struct state_t state_;
|
struct cpu_state_t cpu_state_;
|
||||||
|
|
||||||
// page mapping registers
|
// page mapping registers
|
||||||
uint8_t mmr [page_count + 1];
|
uint8_t mmr [page_count + 1];
|
||||||
|
|
@ -58,7 +54,10 @@ struct Hes_Cpu {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Init cpu state
|
// 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
|
// Reset hes cpu
|
||||||
void Cpu_reset( struct Hes_Cpu* this );
|
void Cpu_reset( struct Hes_Cpu* this );
|
||||||
|
|
@ -67,29 +66,67 @@ void Cpu_reset( struct Hes_Cpu* this );
|
||||||
// instructions were encountered.
|
// instructions were encountered.
|
||||||
bool Cpu_run( struct Hes_Emu* this, hes_time_t end_time );
|
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
|
// Time of ning of next instruction to be executed
|
||||||
static inline hes_time_t Cpu_time( struct Hes_Cpu* this )
|
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 )
|
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
|
return this->cpu_state_.code_map [HES_CPU_PAGE( addr )] + HES_CPU_OFFSET( addr );
|
||||||
#if !defined (BLARGG_NONPORTABLE)
|
|
||||||
% (unsigned) page_size
|
|
||||||
#endif
|
|
||||||
;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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 update_end_time( struct Hes_Cpu* this, hes_time_t end, hes_time_t irq )
|
||||||
{
|
{
|
||||||
if ( irq < t && !(reg_status & irq_inhibit) ) t = irq;
|
if ( end > irq && !(this->r.flags & irq_inhibit_mask) )
|
||||||
int delta = this->state->base - t;
|
end = irq;
|
||||||
this->state->base = t;
|
|
||||||
return delta;
|
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 void Cpu_set_mmr( struct Hes_Cpu* this, int reg, int bank, void const* code )
|
||||||
|
{
|
||||||
|
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
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -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 )
|
|
||||||
1344
apps/codecs/libgme/hes_cpu_run.h
Normal file
1344
apps/codecs/libgme/hes_cpu_run.h
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -21,28 +21,14 @@ int const vdp_mask = 0x02;
|
||||||
int const i_flag_mask = 0x04;
|
int const i_flag_mask = 0x04;
|
||||||
int const unmapped = 0xFF;
|
int const unmapped = 0xFF;
|
||||||
|
|
||||||
long const period_60hz = 262 * 455L; // scanlines * clocks per scanline
|
int const period_60hz = 262 * 455; // 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)
|
|
||||||
|
|
||||||
const char gme_wrong_file_type [] = "Wrong file type for this emulator";
|
const char gme_wrong_file_type [] = "Wrong file type for this emulator";
|
||||||
|
|
||||||
static void clear_track_vars( struct Hes_Emu* this )
|
static void clear_track_vars( struct Hes_Emu* this )
|
||||||
{
|
{
|
||||||
this->current_track_ = -1;
|
this->current_track_ = -1;
|
||||||
this->out_time = 0;
|
track_stop( &this->track_filter );
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Hes_init( struct Hes_Emu* this )
|
void Hes_init( struct Hes_Emu* this )
|
||||||
|
|
@ -52,15 +38,12 @@ void Hes_init( struct Hes_Emu* this )
|
||||||
this->tempo_ = (int)(FP_ONE_TEMPO);
|
this->tempo_ = (int)(FP_ONE_TEMPO);
|
||||||
|
|
||||||
// defaults
|
// defaults
|
||||||
this->max_initial_silence = 2;
|
this->tfilter = *track_get_setup( &this->track_filter );
|
||||||
this->ignore_silence = false;
|
this->tfilter.max_initial = 2;
|
||||||
|
this->tfilter.lookahead = 6;
|
||||||
// Unload
|
this->track_filter.silence_ignored_ = false;
|
||||||
this->voice_count_ = 0;
|
|
||||||
clear_track_vars( this );
|
|
||||||
|
|
||||||
this->timer.raw_load = 0;
|
this->timer.raw_load = 0;
|
||||||
this->silence_lookahead = 6;
|
|
||||||
Sound_set_gain( this, (int)(FP_ONE_GAIN*1.11) );
|
Sound_set_gain( this, (int)(FP_ONE_GAIN*1.11) );
|
||||||
|
|
||||||
Rom_init( &this->rom, 0x2000 );
|
Rom_init( &this->rom, 0x2000 );
|
||||||
|
|
@ -71,6 +54,11 @@ void Hes_init( struct Hes_Emu* this )
|
||||||
|
|
||||||
/* Set default track count */
|
/* Set default track count */
|
||||||
this->track_count = 255;
|
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 )
|
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
|
// 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
|
// Unload
|
||||||
this->voice_count_ = 0;
|
this->voice_count_ = 0;
|
||||||
|
this->track_count = 255;
|
||||||
|
this->m3u.size = 0;
|
||||||
clear_track_vars( this );
|
clear_track_vars( this );
|
||||||
|
|
||||||
assert( offsetof (struct header_t,unused [4]) == header_size );
|
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
|
// 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.
|
// just try to load the damn data as best as possible.
|
||||||
|
|
||||||
long addr = get_le32( this->header.addr );
|
int addr = get_le32( this->header.addr );
|
||||||
/* long rom_size = get_le32( this->header.size ); */
|
/* int rom_size = get_le32( this->header.size ); */
|
||||||
long const rom_max = 0x100000;
|
int const rom_max = 0x100000;
|
||||||
if ( addr & ~(rom_max - 1) )
|
if ( (unsigned) addr >= (unsigned) rom_max )
|
||||||
{
|
{
|
||||||
/* warning( "Invalid address" ); */
|
/* warning( "Invalid address" ); */
|
||||||
addr &= rom_max - 1;
|
addr &= rom_max - 1;
|
||||||
}
|
}
|
||||||
/* if ( (unsigned long) (addr + size) > (unsigned long) rom_max )
|
/* if ( (unsigned) (addr + size) > (unsigned) rom_max )
|
||||||
warning( "Invalid size" );
|
warning( "Invalid size" );
|
||||||
|
|
||||||
if ( rom_size != rom.file_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 );
|
Rom_set_addr( &this->rom, addr );
|
||||||
|
|
||||||
this->voice_count_ = osc_count + adpcm_osc_count;
|
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_ );
|
Apu_volume( &this->apu, this->gain_ );
|
||||||
Adpcm_volume( &this->adpcm, 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
|
// Setup buffer
|
||||||
this->clock_rate_ = 7159091;
|
this->clock_rate_ = 7159091;
|
||||||
Buffer_clock_rate( &this->stereo_buf, 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 );
|
this->buf_changed_count = Buffer_channels_changed_count( &this->stereo_buf );
|
||||||
|
|
||||||
Sound_set_tempo( this, this->tempo_ );
|
Sound_set_tempo( this, this->tempo_ );
|
||||||
Sound_mute_voices( this, this->mute_mask_ );
|
Sound_mute_voices( this, this->mute_mask_ );
|
||||||
|
|
||||||
// Reset track count
|
|
||||||
this->track_count = 255;
|
|
||||||
this->m3u.size = 0;
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Emulation
|
// Emulation
|
||||||
|
|
||||||
void recalc_timer_load( struct Hes_Emu* this );
|
|
||||||
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;
|
this->timer.load = this->timer.raw_load * this->timer_base + 1;
|
||||||
|
|
@ -159,9 +149,25 @@ void recalc_timer_load( struct Hes_Emu* this )
|
||||||
|
|
||||||
// Hardware
|
// Hardware
|
||||||
|
|
||||||
void irq_changed( struct Hes_Emu* this );
|
void run_until( struct Hes_Emu* this, hes_time_t present )
|
||||||
void run_until( struct Hes_Emu* this, hes_time_t present );
|
{
|
||||||
void Cpu_write_vdp( struct Hes_Emu* this, int addr, int data )
|
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 )
|
switch ( addr )
|
||||||
{
|
{
|
||||||
|
|
@ -178,77 +184,33 @@ void Cpu_write_vdp( struct Hes_Emu* this, int addr, int data )
|
||||||
this->vdp.control = data;
|
this->vdp.control = data;
|
||||||
irq_changed( this );
|
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;
|
break;
|
||||||
|
|
||||||
case 3:
|
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;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int Cpu_done( struct Hes_Emu* this )
|
void write_mem_( struct Hes_Emu* this, hes_addr_t addr, int data )
|
||||||
{
|
|
||||||
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 )
|
|
||||||
{
|
{
|
||||||
hes_time_t time = Cpu_time( &this->cpu );
|
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.
|
||||||
// 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
|
||||||
hes_time_t t = min( time, this->cpu.end_time + 8 );
|
// Blip_Buffer.
|
||||||
|
hes_time_t t = min( time, Cpu_end_time( &this->cpu ) + 8 );
|
||||||
Apu_write_data( &this->apu, t, addr, data );
|
Apu_write_data( &this->apu, t, addr, data );
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if ( (unsigned) (addr - adpcm_io_addr) < adpcm_io_size )
|
||||||
if ( (unsigned) (addr - io_addr) < 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 );
|
Adpcm_write_data( &this->adpcm, t, addr, data );
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -258,7 +220,7 @@ void Emu_cpu_write( struct Hes_Emu* this, hes_addr_t addr, int data )
|
||||||
case 0x0000:
|
case 0x0000:
|
||||||
case 0x0002:
|
case 0x0002:
|
||||||
case 0x0003:
|
case 0x0003:
|
||||||
Cpu_write_vdp( this, addr, data );
|
write_vdp( this, addr, data );
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case 0x0C00: {
|
case 0x0C00: {
|
||||||
|
|
@ -282,11 +244,8 @@ void Emu_cpu_write( struct Hes_Emu* this, hes_addr_t addr, int data )
|
||||||
case 0x1402:
|
case 0x1402:
|
||||||
run_until( this, time );
|
run_until( this, time );
|
||||||
this->irq.disables = data;
|
this->irq.disables = data;
|
||||||
|
/* if ( (data & 0xF8) && (data & 0xF8) != 0xF8 ) // flag questionable values
|
||||||
// flag questionable values
|
dprintf( "Int mask: $%02X\n", data ); */
|
||||||
if ( (data & 0xF8) && (data & 0xF8) != 0xF8 ) {
|
|
||||||
dprintf( "Int mask: $%02X\n", data );
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x1403:
|
case 0x1403:
|
||||||
|
|
@ -305,7 +264,7 @@ void Emu_cpu_write( struct Hes_Emu* this, hes_addr_t addr, int data )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
dprintf( "unmapped write $%04X <- $%02X\n", addr, data );
|
/* dprintf( "unmapped write $%04X <- $%02X\n", addr, data ); */
|
||||||
return;
|
return;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
@ -313,7 +272,7 @@ void Emu_cpu_write( struct Hes_Emu* this, hes_addr_t addr, int data )
|
||||||
irq_changed( this );
|
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 );
|
hes_time_t time = Cpu_time( &this->cpu );
|
||||||
addr &= page_size - 1;
|
addr &= page_size - 1;
|
||||||
|
|
@ -322,21 +281,21 @@ int Emu_cpu_read( struct Hes_Emu* this, hes_addr_t addr )
|
||||||
case 0x0000:
|
case 0x0000:
|
||||||
if ( this->irq.vdp > time )
|
if ( this->irq.vdp > time )
|
||||||
return 0;
|
return 0;
|
||||||
this->irq.vdp = (hes_time_t)future_hes_time;
|
this->irq.vdp = future_time;
|
||||||
run_until( this, time );
|
run_until( this, time );
|
||||||
irq_changed( this );
|
irq_changed( this );
|
||||||
return 0x20;
|
return 0x20;
|
||||||
|
|
||||||
case 0x0002:
|
/* case 0x0002:
|
||||||
case 0x0003:
|
case 0x0003:
|
||||||
dprintf( "VDP read not supported: %d\n", addr );
|
dprintf( "VDP read not supported: %d\n", addr );
|
||||||
return 0;
|
return 0; */
|
||||||
|
|
||||||
case 0x0C01:
|
case 0x0C01:
|
||||||
//return timer.enabled; // TODO: remove?
|
//return timer.enabled; // TODO: remove?
|
||||||
case 0x0C00:
|
case 0x0C00:
|
||||||
run_until( this, time );
|
run_until( this, time );
|
||||||
dprintf( "Timer count read\n" );
|
/* dprintf( "Timer count read\n" ); */
|
||||||
return (unsigned) (this->timer.count - 1) / this->timer_base;
|
return (unsigned) (this->timer.count - 1) / this->timer_base;
|
||||||
|
|
||||||
case 0x1402:
|
case 0x1402:
|
||||||
|
|
@ -358,71 +317,75 @@ int Emu_cpu_read( struct Hes_Emu* this, hes_addr_t addr )
|
||||||
|
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
case 0x1000: // I/O port
|
case 0x1000: // I/O port
|
||||||
// case 0x180C: // CD-ROM
|
//case 0x180C: // CD-ROM
|
||||||
// case 0x180D:
|
//case 0x180D:
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
/* default:
|
||||||
dprintf( "unmapped read $%04X\n", addr );
|
dprintf( "unmapped read $%04X\n", addr ); */
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
return unmapped;
|
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 )
|
void irq_changed( struct Hes_Emu* this )
|
||||||
{
|
{
|
||||||
hes_time_t present = Cpu_time( &this->cpu );
|
hes_time_t present = Cpu_time( &this->cpu );
|
||||||
|
|
||||||
if ( this->irq.timer > present )
|
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 )
|
if ( this->timer.enabled && !this->timer.fired )
|
||||||
this->irq.timer = present + this->timer.count;
|
this->irq.timer = present + this->timer.count;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( this->irq.vdp > present )
|
if ( this->irq.vdp > present )
|
||||||
{
|
{
|
||||||
this->irq.vdp = (hes_time_t)future_hes_time;
|
this->irq.vdp = future_time;
|
||||||
if ( this->vdp.control & 0x08 )
|
if ( this->vdp.control & 0x08 )
|
||||||
this->irq.vdp = this->vdp.next_vbl;
|
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 & timer_mask) ) time = this->irq.timer;
|
||||||
if ( !(this->irq.disables & vdp_mask) ) time = min( time, this->irq.vdp );
|
if ( !(this->irq.disables & vdp_mask) ) time = min( time, this->irq.vdp );
|
||||||
|
|
||||||
// Set cpu irq time
|
Cpu_set_irq_time( &this->cpu, time );
|
||||||
this->cpu.state->time += Cpu_update_end_time( &this->cpu, this->cpu.r.status,
|
|
||||||
this->cpu.end_time, (this->cpu.irq_time = time) );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void adjust_time( blargg_long* time, hes_time_t delta );
|
int cpu_done( struct Hes_Emu* this )
|
||||||
static void adjust_time( blargg_long* time, hes_time_t delta )
|
|
||||||
{
|
{
|
||||||
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;
|
*time -= delta;
|
||||||
if ( *time < 0 )
|
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 end_frame( struct Hes_Emu* this, hes_time_t duration )
|
||||||
blargg_err_t run_clocks( struct Hes_Emu* this, blip_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 );
|
check( Cpu_time( &this->cpu ) >= duration );
|
||||||
/* warning( "Emulation error (illegal instruction)" ); */
|
|
||||||
|
|
||||||
check( time() >= duration );
|
|
||||||
//check( time() - duration < 20 ); // Txx instruction could cause going way over
|
//check( time() - duration < 20 ); // Txx instruction could cause going way over
|
||||||
|
|
||||||
run_until( this, duration );
|
run_until( this, duration );
|
||||||
|
|
@ -446,15 +407,7 @@ blargg_err_t run_clocks( struct Hes_Emu* this, blip_time_t* duration_ )
|
||||||
// end time frame
|
// end time frame
|
||||||
this->timer.last_time -= duration;
|
this->timer.last_time -= duration;
|
||||||
this->vdp.next_vbl -= duration;
|
this->vdp.next_vbl -= duration;
|
||||||
#if defined (GME_FRAME_HOOK_DEFINED)
|
Cpu_end_frame( &this->cpu, duration );
|
||||||
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;
|
|
||||||
|
|
||||||
adjust_time( &this->irq.timer, duration );
|
adjust_time( &this->irq.timer, duration );
|
||||||
adjust_time( &this->irq.vdp, duration );
|
adjust_time( &this->irq.vdp, duration );
|
||||||
Apu_end_frame( &this->apu, 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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
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_ )
|
||||||
blargg_err_t play_( struct Hes_Emu* this, long count, sample_t* out )
|
|
||||||
{
|
{
|
||||||
long remain = count;
|
return end_frame( this, *duration_ );
|
||||||
|
}
|
||||||
|
|
||||||
|
blargg_err_t play_( void *emu, int count, sample_t out [] )
|
||||||
|
{
|
||||||
|
struct Hes_Emu* this = (struct Hes_Emu*) emu;
|
||||||
|
|
||||||
|
int remain = count;
|
||||||
while ( remain )
|
while ( remain )
|
||||||
{
|
{
|
||||||
|
Buffer_disable_immediate_removal( &this->stereo_buf );
|
||||||
remain -= Buffer_read_samples( &this->stereo_buf, &out [count - remain], remain );
|
remain -= Buffer_read_samples( &this->stereo_buf, &out [count - remain], remain );
|
||||||
if ( remain )
|
if ( remain )
|
||||||
{
|
{
|
||||||
if ( this->buf_changed_count != Buffer_channels_changed_count( &this->stereo_buf ) )
|
if ( this->buf_changed_count != Buffer_channels_changed_count( &this->stereo_buf ) )
|
||||||
{
|
{
|
||||||
this->buf_changed_count = Buffer_channels_changed_count( &this->stereo_buf );
|
this->buf_changed_count = Buffer_channels_changed_count( &this->stereo_buf );
|
||||||
|
|
||||||
// Remute voices
|
// Remute voices
|
||||||
Sound_mute_voices( this, this->mute_mask_ );
|
Sound_mute_voices( this, this->mute_mask_ );
|
||||||
}
|
}
|
||||||
|
|
||||||
int msec = Buffer_length( &this->stereo_buf );
|
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 ) );
|
RETURN_ERR( run_clocks( this, &clocks_emulated ) );
|
||||||
assert( clocks_emulated );
|
assert( clocks_emulated );
|
||||||
Buffer_end_frame( &this->stereo_buf, 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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Music emu
|
// 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
|
require( !this->sample_rate_ ); // sample rate can't be changed once set
|
||||||
Buffer_init( &this->stereo_buf );
|
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 );
|
Buffer_bass_freq( &this->stereo_buf, 60 );
|
||||||
|
|
||||||
this->sample_rate_ = rate;
|
this->sample_rate_ = rate;
|
||||||
|
RETURN_ERR( track_init( &this->track_filter, this ) );
|
||||||
|
this->tfilter.max_silence = 6 * stereo * this->sample_rate_;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -521,7 +482,7 @@ void Sound_mute_voices( struct Hes_Emu* this, int mask )
|
||||||
this->mute_mask_ = mask;
|
this->mute_mask_ = mask;
|
||||||
|
|
||||||
// Set adpcm voice
|
// 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_ ) )
|
if ( mask & (1 << this->voice_count_ ) )
|
||||||
Adpcm_set_output( &this->adpcm, 0, 0, 0, 0 );
|
Adpcm_set_output( &this->adpcm, 0, 0, 0, 0 );
|
||||||
else
|
else
|
||||||
|
|
@ -537,6 +498,7 @@ void Sound_mute_voices( struct Hes_Emu* this, int mask )
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
struct channel_t ch = Buffer_channel( &this->stereo_buf, i );
|
||||||
assert( (ch.center && ch.left && ch.right) ||
|
assert( (ch.center && ch.left && ch.right) ||
|
||||||
(!ch.center && !ch.left && !ch.right) ); // all or nothing
|
(!ch.center && !ch.left && !ch.right) ); // all or nothing
|
||||||
Apu_osc_output( &this->apu, i, ch.center, ch.left, ch.right );
|
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;
|
this->tempo_ = t;
|
||||||
}
|
}
|
||||||
|
|
||||||
void fill_buf( struct Hes_Emu* this );
|
|
||||||
blargg_err_t Hes_start_track( struct Hes_Emu* this, int track )
|
blargg_err_t Hes_start_track( struct Hes_Emu* this, int track )
|
||||||
{
|
{
|
||||||
clear_track_vars( this );
|
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 );
|
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 );
|
memset( this->sgx, 0, sizeof this->sgx );
|
||||||
|
|
||||||
Apu_reset( &this->apu );
|
Apu_reset( &this->apu );
|
||||||
|
|
@ -581,12 +542,12 @@ blargg_err_t Hes_start_track( struct Hes_Emu* this, int track )
|
||||||
|
|
||||||
unsigned i;
|
unsigned i;
|
||||||
for ( i = 0; i < sizeof this->header.banks; i++ )
|
for ( i = 0; i < sizeof this->header.banks; i++ )
|
||||||
Cpu_set_mmr( this, i, this->header.banks [i] );
|
set_mmr( this, i, this->header.banks [i] );
|
||||||
Cpu_set_mmr( this, page_count, 0xFF ); // unmapped beyond end of address space
|
set_mmr( this, page_count, 0xFF ); // unmapped beyond end of address space
|
||||||
|
|
||||||
this->irq.disables = timer_mask | vdp_mask;
|
this->irq.disables = timer_mask | vdp_mask;
|
||||||
this->irq.timer = (hes_time_t)future_hes_time;
|
this->irq.timer = future_time;
|
||||||
this->irq.vdp = (hes_time_t)future_hes_time;
|
this->irq.vdp = future_time;
|
||||||
|
|
||||||
this->timer.enabled = false;
|
this->timer.enabled = false;
|
||||||
this->timer.raw_load= 0x80;
|
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.control = 0;
|
||||||
this->vdp.next_vbl = 0;
|
this->vdp.next_vbl = 0;
|
||||||
|
|
||||||
this->cpu.ram [0x1FF] = (idle_addr - 1) >> 8;
|
this->ram [0x1FF] = (idle_addr - 1) >> 8;
|
||||||
this->cpu.ram [0x1FE] = (idle_addr - 1) & 0xFF;
|
this->ram [0x1FE] = (idle_addr - 1) & 0xFF;
|
||||||
this->cpu.r.sp = 0xFD;
|
this->cpu.r.sp = 0xFD;
|
||||||
this->cpu.r.pc = get_le16( this->header.init_addr );
|
this->cpu.r.pc = get_le16( this->header.init_addr );
|
||||||
this->cpu.r.a = track;
|
this->cpu.r.a = track;
|
||||||
|
|
||||||
recalc_timer_load( this );
|
recalc_timer_load( this );
|
||||||
this->last_frame_hook = 0;
|
|
||||||
|
|
||||||
this->emu_track_ended_ = false;
|
// convert filter times to samples
|
||||||
this->track_ended = false;
|
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 )
|
return track_start( &this->track_filter );
|
||||||
{
|
|
||||||
// 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tell/Seek
|
// 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;
|
msec -= sec * 1000;
|
||||||
return (sec * sample_rate + msec * sample_rate / 1000) * stereo;
|
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;
|
int rate = this->sample_rate_ * stereo;
|
||||||
blargg_long sec = this->out_time / rate;
|
int sec = track_sample_count( &this->track_filter ) / rate;
|
||||||
return sec * 1000 + (this->out_time - sec * rate) * 1000 / 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_ );
|
int time = msec_to_samples( msec, this->sample_rate_ );
|
||||||
if ( time < this->out_time )
|
if ( time < track_sample_count( &this->track_filter ) )
|
||||||
RETURN_ERR( Hes_start_track( this, this->current_track_ ) );
|
RETURN_ERR( Hes_start_track( this, this->current_track_ ) );
|
||||||
return Track_skip( this, time - this->out_time );
|
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_( void* emu, int count )
|
||||||
blargg_err_t skip_( struct Hes_Emu* this, long count )
|
|
||||||
{
|
{
|
||||||
|
struct Hes_Emu* this = (struct Hes_Emu*) emu;
|
||||||
|
|
||||||
// for long skip, mute sound
|
// for long skip, mute sound
|
||||||
const long threshold = 30000;
|
const int threshold = 32768;
|
||||||
if ( count > threshold )
|
if ( count > threshold )
|
||||||
{
|
{
|
||||||
int saved_mute = this->mute_mask_;
|
int saved_mute = this->mute_mask_;
|
||||||
Sound_mute_voices( this, ~0 );
|
Sound_mute_voices( this, ~0 );
|
||||||
|
|
||||||
while ( count > threshold / 2 && !this->emu_track_ended_ )
|
int n = count - threshold/2;
|
||||||
{
|
n &= ~(2048-1); // round to multiple of 2048
|
||||||
RETURN_ERR( play_( this, buf_size, this->buf ) );
|
count -= n;
|
||||||
count -= buf_size;
|
RETURN_ERR( skippy_( &this->track_filter, n ) );
|
||||||
}
|
|
||||||
|
|
||||||
Sound_mute_voices( this, saved_mute );
|
Sound_mute_voices( this, saved_mute );
|
||||||
}
|
}
|
||||||
|
|
||||||
while ( count && !this->emu_track_ended_ )
|
return skippy_( &this->track_filter, count );
|
||||||
{
|
|
||||||
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 )
|
blargg_err_t Track_skip( struct Hes_Emu* this, int count )
|
||||||
{
|
{
|
||||||
require( this->current_track_ >= 0 ); // start_track() must have been called already
|
require( this->current_track_ >= 0 ); // start_track() must have been called already
|
||||||
this->out_time += count;
|
return track_skip( &this->track_filter, 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Track_set_fade( struct Hes_Emu* this, int start_msec, int length_msec )
|
||||||
|
|
||||||
// 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);
|
track_set_fade( &this->track_filter, msec_to_samples( start_msec, this->sample_rate_ ),
|
||||||
this->fade_start = msec_to_samples( start_msec, this->sample_rate_ );
|
length_msec * this->sample_rate_ / (1000 / stereo) );
|
||||||
}
|
}
|
||||||
|
|
||||||
// unit / pow( 2.0, (double) x / step )
|
blargg_err_t Hes_play( struct Hes_Emu* this, int out_count, sample_t* out )
|
||||||
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;
|
require( this->current_track_ >= 0 );
|
||||||
int fraction = (x - shift * step) * unit / step;
|
require( out_count % stereo == 0 );
|
||||||
return ((unit - fraction) + (fraction >> 1)) >> shift;
|
return track_play( &this->track_filter, out_count, out );
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 );
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 )
|
|
||||||
{
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
// fill internal buffer and check it for silence
|
|
||||||
void fill_buf( struct Hes_Emu* this )
|
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
blargg_err_t Hes_play( struct Hes_Emu* this, long 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;
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,88 +12,67 @@
|
||||||
#include "hes_apu_adpcm.h"
|
#include "hes_apu_adpcm.h"
|
||||||
#include "hes_cpu.h"
|
#include "hes_cpu.h"
|
||||||
#include "m3u_playlist.h"
|
#include "m3u_playlist.h"
|
||||||
|
#include "track_filter.h"
|
||||||
typedef short sample_t;
|
|
||||||
|
|
||||||
enum { buf_size = 2048 };
|
|
||||||
|
|
||||||
// HES file header
|
// HES file header
|
||||||
|
enum { info_offset = 0x20 };
|
||||||
enum { header_size = 0x20 };
|
enum { header_size = 0x20 };
|
||||||
struct header_t
|
struct header_t
|
||||||
{
|
{
|
||||||
byte tag [4];
|
byte tag [4];
|
||||||
byte vers;
|
byte vers;
|
||||||
byte first_track;
|
byte first_track;
|
||||||
byte init_addr [2];
|
byte init_addr [2];
|
||||||
byte banks [8];
|
byte banks [8];
|
||||||
byte data_tag [4];
|
byte data_tag [4];
|
||||||
byte size [4];
|
byte size [4];
|
||||||
byte addr [4];
|
byte addr [4];
|
||||||
byte unused [4];
|
byte unused [4];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
struct timer_t {
|
struct timer_t {
|
||||||
hes_time_t last_time;
|
hes_time_t last_time;
|
||||||
blargg_long count;
|
int count;
|
||||||
blargg_long load;
|
int load;
|
||||||
int raw_load;
|
int raw_load;
|
||||||
byte enabled;
|
byte enabled;
|
||||||
byte fired;
|
byte fired;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct vdp_t {
|
struct vdp_t {
|
||||||
hes_time_t next_vbl;
|
hes_time_t next_vbl;
|
||||||
byte latch;
|
byte latch;
|
||||||
byte control;
|
byte control;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct irq_t {
|
struct irq_t {
|
||||||
hes_time_t timer;
|
hes_time_t timer;
|
||||||
hes_time_t vdp;
|
hes_time_t vdp;
|
||||||
byte disables;
|
byte disables;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
struct Hes_Emu {
|
struct Hes_Emu {
|
||||||
hes_time_t play_period;
|
hes_time_t play_period;
|
||||||
hes_time_t last_frame_hook;
|
|
||||||
int timer_base;
|
int timer_base;
|
||||||
|
|
||||||
struct timer_t timer;
|
struct timer_t timer;
|
||||||
struct vdp_t vdp;
|
struct vdp_t vdp;
|
||||||
struct irq_t irq;
|
struct irq_t irq;
|
||||||
|
|
||||||
// Sound
|
// Sound
|
||||||
long clock_rate_;
|
int clock_rate_;
|
||||||
long sample_rate_;
|
int sample_rate_;
|
||||||
unsigned buf_changed_count;
|
unsigned buf_changed_count;
|
||||||
int voice_count_;
|
int voice_count_;
|
||||||
|
int const* voice_types_;
|
||||||
|
int mute_mask_;
|
||||||
int tempo_;
|
int tempo_;
|
||||||
int gain_;
|
int gain_;
|
||||||
|
|
||||||
// track-specific
|
// track-specific
|
||||||
byte track_count;
|
byte track_count;
|
||||||
volatile bool track_ended;
|
|
||||||
int current_track_;
|
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
|
// Larger files at the end
|
||||||
// Header for currently loaded file
|
// Header for currently loaded file
|
||||||
|
|
@ -102,19 +81,22 @@ struct Hes_Emu {
|
||||||
// M3u Playlist
|
// M3u Playlist
|
||||||
struct M3u_Playlist m3u;
|
struct M3u_Playlist m3u;
|
||||||
|
|
||||||
|
struct setup_t tfilter;
|
||||||
|
struct Track_Filter track_filter;
|
||||||
|
|
||||||
// Hes Cpu
|
// Hes Cpu
|
||||||
byte* write_pages [page_count + 1]; // 0 if unmapped or I/O space
|
|
||||||
struct Hes_Cpu cpu;
|
struct Hes_Cpu cpu;
|
||||||
|
struct Rom_Data rom;
|
||||||
|
|
||||||
struct Hes_Apu apu;
|
struct Hes_Apu apu;
|
||||||
struct Hes_Apu_Adpcm adpcm;
|
struct Hes_Apu_Adpcm adpcm;
|
||||||
|
|
||||||
struct Stereo_Buffer stereo_buf;
|
struct Multi_Buffer stereo_buf;
|
||||||
sample_t buf [buf_size];
|
|
||||||
|
|
||||||
// rom & ram
|
// rom & ram
|
||||||
struct Rom_Data rom;
|
byte* write_pages [page_count + 1]; // 0 if unmapped or I/O space
|
||||||
byte sgx [3 * page_size + cpu_padding];
|
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 );
|
void Hes_stop( struct Hes_Emu* this );
|
||||||
|
|
||||||
// Loads a file from memory
|
// 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.
|
// 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.
|
// Start a track, where 0 is the first track. Also clears warning string.
|
||||||
blargg_err_t Hes_start_track( struct Hes_Emu* this, int );
|
blargg_err_t Hes_start_track( struct Hes_Emu* this, int );
|
||||||
|
|
||||||
// Generate 'count' samples info 'buf'. Output is in stereo. Any emulation
|
// Generate 'count' samples info 'buf'. Output is in stereo. Any emulation
|
||||||
// errors set warning string, and major errors also end track.
|
// 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
|
// Track status/control
|
||||||
// Number of milliseconds (1000 msec = 1 second) played since ning of track
|
// 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.
|
// 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
|
// 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
|
// 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.
|
// 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
|
// 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) ) {
|
if ( (this->m3u.size > 0) && (n < this->m3u.size) ) {
|
||||||
struct entry_t* entry = &this->m3u.entries [n];
|
struct entry_t* entry = &this->m3u.entries [n];
|
||||||
length = entry->length;
|
length = entry->length;
|
||||||
|
|
@ -185,45 +179,17 @@ static inline void Sound_set_gain( struct Hes_Emu* this, int g )
|
||||||
this->gain_ = g;
|
this->gain_ = g;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Emulation (You shouldn't touch these)
|
// Emulation (You shouldn't touch these)
|
||||||
|
|
||||||
int Cpu_read( struct Hes_Emu* this, hes_addr_t );
|
void irq_changed( struct Hes_Emu* this );
|
||||||
void Cpu_write( struct Hes_Emu* this, hes_addr_t, int );
|
void run_until( struct Hes_Emu* this, hes_time_t );
|
||||||
void Cpu_write_vdp( struct Hes_Emu* this, int addr, int data );
|
bool run_cpu( struct Hes_Emu* this, hes_time_t end );
|
||||||
int Cpu_done( struct Hes_Emu* this );
|
int read_mem_( struct Hes_Emu* this, hes_addr_t );
|
||||||
|
int read_mem( struct Hes_Emu* this, hes_addr_t );
|
||||||
int Emu_cpu_read( struct Hes_Emu* this, hes_addr_t );
|
void write_mem_( struct Hes_Emu* this, hes_addr_t, int data );
|
||||||
void Emu_cpu_write( 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 );
|
||||||
static inline byte const* Emu_cpu_set_mmr( struct Hes_Emu* this, int page, int bank )
|
void set_mmr( struct Hes_Emu* this, int reg, int bank );
|
||||||
{
|
int cpu_done( struct Hes_Emu* this );
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -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"
|
#include "kss_emu.h"
|
||||||
|
|
||||||
|
|
@ -17,29 +17,14 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
|
||||||
|
|
||||||
#include "blargg_source.h"
|
#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";
|
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 )
|
static void clear_track_vars( struct Kss_Emu* this )
|
||||||
{
|
{
|
||||||
this->current_track = -1;
|
this->current_track = -1;
|
||||||
this->out_time = 0;
|
track_stop( &this->track_filter );
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static blargg_err_t init_opl_apu( enum opl_type_t type, struct Opl_Apu* out )
|
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;
|
this->chip_flags = 0;
|
||||||
|
|
||||||
// defaults
|
// defaults
|
||||||
this->max_initial_silence = 2;
|
this->tfilter = *track_get_setup( &this->track_filter );
|
||||||
this->silence_lookahead = 6;
|
this->tfilter.max_initial = 2;
|
||||||
this->ignore_silence = false;
|
this->tfilter.lookahead = 6;
|
||||||
|
this->track_filter.silence_ignored_ = false;
|
||||||
this->voice_count = 0;
|
|
||||||
clear_track_vars( this );
|
|
||||||
|
|
||||||
memset( this->unmapped_read, 0xFF, sizeof this->unmapped_read );
|
memset( this->unmapped_read, 0xFF, sizeof this->unmapped_read );
|
||||||
|
|
||||||
// Init all stuff
|
// Init all stuff
|
||||||
Buffer_init( &this->stereo_buffer );
|
Buffer_init( &this->stereo_buf );
|
||||||
|
|
||||||
Z80_init( &this->cpu );
|
Z80_init( &this->cpu );
|
||||||
Rom_init( &this->rom, page_size );
|
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_msxmusic, &this->msx.music );
|
||||||
init_opl_apu( type_msxaudio, &this->msx.audio );
|
init_opl_apu( type_msxaudio, &this->msx.audio );
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
this->voice_count = 0;
|
||||||
|
this->voice_types = 0;
|
||||||
|
clear_track_vars( this );
|
||||||
}
|
}
|
||||||
|
|
||||||
// Track info
|
// Track info
|
||||||
|
|
@ -96,7 +83,7 @@ static blargg_err_t check_kss_header( void const* header )
|
||||||
|
|
||||||
// Setup
|
// Setup
|
||||||
|
|
||||||
static void update_gain( struct Kss_Emu* this )
|
static void update_gain_( struct Kss_Emu* this )
|
||||||
{
|
{
|
||||||
int g = this->gain;
|
int g = this->gain;
|
||||||
if ( msx_music_enabled( this ) || msx_audio_enabled( this )
|
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 );
|
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 )
|
blargg_err_t Kss_load_mem( struct Kss_Emu* this, const void* data, long size )
|
||||||
{
|
{
|
||||||
/* warning( core.warning() ); */
|
/* warning( core.warning() ); */
|
||||||
|
|
@ -176,6 +172,10 @@ blargg_err_t Kss_load_mem( struct Kss_Emu* this, const void* data, long size )
|
||||||
|
|
||||||
// sms.psg
|
// sms.psg
|
||||||
this->voice_count = sms_osc_count;
|
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;
|
this->chip_flags |= sms_psg_flag;
|
||||||
|
|
||||||
// sms.fm
|
// sms.fm
|
||||||
|
|
@ -191,6 +191,10 @@ blargg_err_t Kss_load_mem( struct Kss_Emu* this, const void* data, long size )
|
||||||
|
|
||||||
// msx.psg
|
// msx.psg
|
||||||
this->voice_count = ay_osc_count;
|
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;
|
this->chip_flags |= msx_psg_flag;
|
||||||
|
|
||||||
/* if ( this->header.device_flags & 0x10 )
|
/* 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
|
// msx.scc
|
||||||
this->chip_flags |= msx_scc_flag;
|
this->chip_flags |= msx_scc_flag;
|
||||||
this->voice_count = ay_osc_count + scc_osc_count;
|
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 ( sms_fm_enabled( this ) || msx_music_enabled( this ) || msx_audio_enabled( this ) )
|
||||||
{
|
{
|
||||||
if ( !Opl_supported() )
|
if ( !Opl_supported() )
|
||||||
; /* warning( "FM sound not supported" ); */
|
; /* warning( "FM sound not supported" ); */
|
||||||
else
|
else
|
||||||
this->silence_lookahead = 3; // Opl_Apu is really slow
|
this->tfilter.lookahead = 3; // Opl_Apu is really slow
|
||||||
}
|
}
|
||||||
|
|
||||||
this->clock_rate_ = clock_rate;
|
this->clock_rate_ = clock_rate;
|
||||||
Buffer_clock_rate( &this->stereo_buffer, clock_rate );
|
Buffer_clock_rate( &this->stereo_buf, clock_rate );
|
||||||
this->buf_changed_count = Buffer_channels_changed_count( &this->stereo_buffer );
|
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_set_tempo( this, this->tempo );
|
||||||
Sound_mute_voices( this, this->mute_mask_ );
|
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_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_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_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
|
// MUSIC
|
||||||
|
|
||||||
|
blargg_err_t Kss_set_sample_rate( struct Kss_Emu* this, int rate )
|
||||||
blargg_err_t Kss_set_sample_rate( struct Kss_Emu* this, long rate )
|
|
||||||
{
|
{
|
||||||
require( !this->sample_rate ); // sample rate can't be changed once set
|
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
|
// Set bass frequency
|
||||||
Buffer_bass_freq( &this->stereo_buffer, 180 );
|
Buffer_bass_freq( &this->stereo_buf, 180 );
|
||||||
|
|
||||||
this->sample_rate = rate;
|
this->sample_rate = rate;
|
||||||
|
RETURN_ERR( track_init( &this->track_filter, this ) );
|
||||||
|
this->tfilter.max_silence = 6 * stereo * this->sample_rate;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -501,7 +513,7 @@ void Sound_mute_voices( struct Kss_Emu* this, int mask )
|
||||||
}
|
}
|
||||||
else
|
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) ||
|
assert( (ch.center && ch.left && ch.right) ||
|
||||||
(!ch.center && !ch.left && !ch.right) ); // all or nothing
|
(!ch.center && !ch.left && !ch.right) ); // all or nothing
|
||||||
set_voice( this, i, ch.center, ch.left, ch.right );
|
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);
|
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 )
|
blargg_err_t Kss_start_track( struct Kss_Emu* this, int track )
|
||||||
{
|
{
|
||||||
clear_track_vars( this );
|
clear_track_vars( this );
|
||||||
|
|
@ -536,7 +547,7 @@ blargg_err_t Kss_start_track( struct Kss_Emu* this, int track )
|
||||||
|
|
||||||
this->current_track = 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_psg_enabled( this ) ) Sms_apu_reset( &this->sms.psg, 0, 0 );
|
||||||
if ( sms_fm_enabled( this ) ) Opl_reset( &this->sms.fm );
|
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 );
|
if ( msx_audio_enabled( this ) ) Opl_reset( &this->msx.audio );
|
||||||
|
|
||||||
this->scc_accessed = false;
|
this->scc_accessed = false;
|
||||||
update_gain( this );
|
update_gain_( this );
|
||||||
|
|
||||||
memset( this->ram, 0xC9, 0x4000 );
|
memset( this->ram, 0xC9, 0x4000 );
|
||||||
memset( this->ram + 0x4000, 0, sizeof this->ram - 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;
|
this->gain_updated = false;
|
||||||
jsr( this, this->header.init_addr );
|
jsr( this, this->header.init_addr );
|
||||||
|
|
||||||
this->emu_track_ended_ = false;
|
// convert filter times to samples
|
||||||
this->track_ended = false;
|
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 )
|
return track_start( &this->track_filter );
|
||||||
{
|
|
||||||
// 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tell/Seek
|
// 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;
|
msec -= sec * 1000;
|
||||||
return (sec * sample_rate + msec * sample_rate / 1000) * stereo;
|
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;
|
int rate = this->sample_rate * stereo;
|
||||||
blargg_long sec = this->out_time / rate;
|
int sec = track_sample_count( &this->track_filter ) / rate;
|
||||||
return sec * 1000 + (this->out_time - sec * rate) * 1000 / 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 );
|
int time = msec_to_samples( msec, this->sample_rate );
|
||||||
if ( time < this->out_time )
|
if ( time < track_sample_count( &this->track_filter ) )
|
||||||
RETURN_ERR( Kss_start_track( this, this->current_track ) );
|
RETURN_ERR( Kss_start_track( this, this->current_track ) );
|
||||||
return Track_skip( this, time - this->out_time );
|
return Track_skip( this, time - track_sample_count( &this->track_filter ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
blargg_err_t play_( struct Kss_Emu* this, long count, sample_t* out );
|
blargg_err_t skip_( void *emu, int count )
|
||||||
static blargg_err_t skip_( struct Kss_Emu* this, long count )
|
|
||||||
{
|
{
|
||||||
|
struct Kss_Emu* this = (struct Kss_Emu*) emu;
|
||||||
|
|
||||||
// for long skip, mute sound
|
// for long skip, mute sound
|
||||||
const long threshold = 30000;
|
const int threshold = 32768;
|
||||||
if ( count > threshold )
|
if ( count > threshold )
|
||||||
{
|
{
|
||||||
int saved_mute = this->mute_mask_;
|
int saved_mute = this->mute_mask_;
|
||||||
Sound_mute_voices( this, ~0 );
|
Sound_mute_voices( this, ~0 );
|
||||||
|
|
||||||
while ( count > threshold / 2 && !this->emu_track_ended_ )
|
int n = count - threshold/2;
|
||||||
{
|
n &= ~(2048-1); // round to multiple of 2048
|
||||||
RETURN_ERR( play_( this, buf_size, this->buf ) );
|
count -= n;
|
||||||
count -= buf_size;
|
RETURN_ERR( skippy_( &this->track_filter, n ) );
|
||||||
}
|
|
||||||
|
|
||||||
Sound_mute_voices( this, saved_mute );
|
Sound_mute_voices( this, saved_mute );
|
||||||
}
|
}
|
||||||
|
|
||||||
while ( count && !this->emu_track_ended_ )
|
return skippy_( &this->track_filter, count );
|
||||||
{
|
|
||||||
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 )
|
blargg_err_t Track_skip( struct Kss_Emu* this, int count )
|
||||||
{
|
{
|
||||||
require( this->current_track >= 0 ); // start_track() must have been called already
|
require( this->current_track >= 0 ); // start_track() must have been called already
|
||||||
this->out_time += count;
|
return track_skip( &this->track_filter, 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fading
|
void Track_set_fade( struct Kss_Emu* this, int start_msec, int length_msec )
|
||||||
|
|
||||||
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);
|
track_set_fade( &this->track_filter, msec_to_samples( start_msec, this->sample_rate ),
|
||||||
this->fade_start = msec_to_samples( start_msec, this->sample_rate );
|
length_msec * this->sample_rate / (1000 / stereo) );
|
||||||
}
|
}
|
||||||
|
|
||||||
// unit / pow( 2.0, (double) x / step )
|
blargg_err_t Kss_play( struct Kss_Emu* this, int out_count, sample_t* out )
|
||||||
static int int_log( blargg_long x, int step, int unit )
|
|
||||||
{
|
{
|
||||||
int shift = x / step;
|
require( this->current_track >= 0 );
|
||||||
int fraction = (x - shift * step) * unit / step;
|
require( out_count % stereo == 0 );
|
||||||
return ((unit - fraction) + (fraction >> 1)) >> shift;
|
return track_play( &this->track_filter, out_count, out );
|
||||||
}
|
}
|
||||||
|
|
||||||
static void handle_fade( struct Kss_Emu *this, long out_count, sample_t* out )
|
blargg_err_t play_( void *emu, int count, sample_t* out )
|
||||||
{
|
{
|
||||||
int i;
|
struct Kss_Emu* this = (struct Kss_Emu*) emu;
|
||||||
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 remain = count;
|
||||||
int count;
|
|
||||||
for ( count = min( fade_block_size, out_count - i ); count; --count )
|
|
||||||
{
|
|
||||||
*io = (sample_t) ((*io * gain) >> shift);
|
|
||||||
++io;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 );
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
// fill internal buffer and check it for silence
|
|
||||||
void fill_buf( struct Kss_Emu* this )
|
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
blargg_err_t Kss_play( struct Kss_Emu* this, long 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
blargg_err_t play_( struct Kss_Emu* this, long count, sample_t* out )
|
|
||||||
{
|
|
||||||
long remain = count;
|
|
||||||
while ( remain )
|
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 ( 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_ );
|
Sound_mute_voices( this, this->mute_mask_ );
|
||||||
}
|
}
|
||||||
int msec = Buffer_length( &this->stereo_buffer );
|
int msec = Buffer_length( &this->stereo_buf );
|
||||||
/* blip_time_t clocks_emulated = (blargg_long) msec * clock_rate_ / 1000; */
|
|
||||||
blip_time_t clocks_emulated = msec * this->clock_rate_ / 1000 - 100;
|
blip_time_t clocks_emulated = msec * this->clock_rate_ / 1000 - 100;
|
||||||
RETURN_ERR( run_clocks( this, &clocks_emulated ) );
|
RETURN_ERR( run_clocks( this, &clocks_emulated ) );
|
||||||
assert( clocks_emulated );
|
assert( clocks_emulated );
|
||||||
Buffer_end_frame( &this->stereo_buffer, clocks_emulated );
|
Buffer_end_frame( &this->stereo_buf, clocks_emulated );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,8 +16,8 @@
|
||||||
#include "ay_apu.h"
|
#include "ay_apu.h"
|
||||||
#include "opl_apu.h"
|
#include "opl_apu.h"
|
||||||
#include "m3u_playlist.h"
|
#include "m3u_playlist.h"
|
||||||
|
#include "track_filter.h"
|
||||||
|
|
||||||
typedef short sample_t;
|
|
||||||
typedef int kss_time_t;
|
typedef int kss_time_t;
|
||||||
typedef int kss_addr_t;
|
typedef int kss_addr_t;
|
||||||
typedef struct Z80_Cpu Kss_Cpu;
|
typedef struct Z80_Cpu Kss_Cpu;
|
||||||
|
|
@ -35,7 +35,6 @@ enum {
|
||||||
enum { idle_addr = 0xFFFF };
|
enum { idle_addr = 0xFFFF };
|
||||||
enum { scc_enabled_true = 0xC000 };
|
enum { scc_enabled_true = 0xC000 };
|
||||||
enum { mem_size = 0x10000 };
|
enum { mem_size = 0x10000 };
|
||||||
enum { buf_size = 2048 };
|
|
||||||
|
|
||||||
// KSS file header
|
// KSS file header
|
||||||
enum { header_size = 0x20 };
|
enum { header_size = 0x20 };
|
||||||
|
|
@ -84,8 +83,6 @@ struct Kss_Emu {
|
||||||
bool scc_accessed;
|
bool scc_accessed;
|
||||||
bool gain_updated;
|
bool gain_updated;
|
||||||
|
|
||||||
int track_count;
|
|
||||||
|
|
||||||
unsigned scc_enabled; // 0 or 0xC000
|
unsigned scc_enabled; // 0 or 0xC000
|
||||||
int bank_count;
|
int bank_count;
|
||||||
|
|
||||||
|
|
@ -94,46 +91,32 @@ struct Kss_Emu {
|
||||||
int ay_latch;
|
int ay_latch;
|
||||||
|
|
||||||
// general
|
// general
|
||||||
int max_initial_silence;
|
|
||||||
int voice_count;
|
int voice_count;
|
||||||
|
int const* voice_types;
|
||||||
int mute_mask_;
|
int mute_mask_;
|
||||||
int tempo;
|
int tempo;
|
||||||
int gain;
|
int gain;
|
||||||
|
|
||||||
long sample_rate;
|
int sample_rate;
|
||||||
|
|
||||||
// track-specific
|
// track-specific
|
||||||
|
int track_count;
|
||||||
int current_track;
|
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
|
int clock_rate_;
|
||||||
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_;
|
|
||||||
unsigned buf_changed_count;
|
unsigned buf_changed_count;
|
||||||
|
|
||||||
// M3u Playlist
|
// M3u Playlist
|
||||||
struct M3u_Playlist m3u;
|
struct M3u_Playlist m3u;
|
||||||
|
|
||||||
// large items
|
struct setup_t tfilter;
|
||||||
sample_t buf [buf_size];
|
struct Track_Filter track_filter;
|
||||||
|
|
||||||
struct sms_t sms;
|
struct sms_t sms;
|
||||||
struct msx_t msx;
|
struct msx_t msx;
|
||||||
|
|
||||||
Kss_Cpu cpu;
|
Kss_Cpu cpu;
|
||||||
|
struct Multi_Buffer stereo_buf; // NULL if using custom buffer
|
||||||
struct Rom_Data rom;
|
struct Rom_Data rom;
|
||||||
|
|
||||||
byte unmapped_read [0x100];
|
byte unmapped_read [0x100];
|
||||||
|
|
@ -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 );
|
blargg_err_t end_frame( struct Kss_Emu* this, kss_time_t );
|
||||||
|
|
||||||
// Set output sample rate. Must be called only once before loading file.
|
// 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.
|
// 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 );
|
blargg_err_t Kss_start_track( struct Kss_Emu* this, int track );
|
||||||
|
|
||||||
// Generate 'count' samples info 'buf'. Output is in stereo. Any emulation
|
// Generate 'count' samples info 'buf'. Output is in stereo. Any emulation
|
||||||
// errors set warning string, and major errors also end track.
|
// 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
|
// Track status/control
|
||||||
|
|
||||||
// Number of milliseconds (1000 msec = 1 second) played since beginning of track
|
// 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.
|
// 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
|
// 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
|
// 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.
|
// 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
|
// 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) ) {
|
if ( (this->m3u.size > 0) && (n < this->m3u.size) ) {
|
||||||
struct entry_t* entry = &this->m3u.entries [n];
|
struct entry_t* entry = &this->m3u.entries [n];
|
||||||
|
|
|
||||||
|
|
@ -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"
|
#include "multi_buffer.h"
|
||||||
|
|
||||||
|
|
@ -15,212 +15,272 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
|
||||||
|
|
||||||
#include "blargg_source.h"
|
#include "blargg_source.h"
|
||||||
|
|
||||||
#ifdef BLARGG_ENABLE_OPTIMIZER
|
// Tracked_Blip_Buffer
|
||||||
#include BLARGG_ENABLE_OPTIMIZER
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Stereo_Buffer
|
int const blip_buffer_extra = 32; // TODO: explain why this value
|
||||||
|
|
||||||
void Buffer_init( struct Stereo_Buffer* this )
|
void Tracked_init( struct Tracked_Blip_Buffer* this )
|
||||||
{
|
{
|
||||||
Blip_init( &this->bufs [0] );
|
Blip_init( &this->blip );
|
||||||
Blip_init( &this->bufs [1] );
|
this->last_non_silence = 0;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
blargg_err_t Buffer_set_sample_rate( struct Stereo_Buffer* this, long rate, int msec )
|
void Tracked_clear( struct Tracked_Blip_Buffer* this )
|
||||||
|
{
|
||||||
|
this->last_non_silence = 0;
|
||||||
|
Blip_clear( &this->blip );
|
||||||
|
}
|
||||||
|
|
||||||
|
void Tracked_end_frame( struct Tracked_Blip_Buffer* this, blip_time_t t )
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned Tracked_non_silent( struct Tracked_Blip_Buffer* this )
|
||||||
|
{
|
||||||
|
return this->last_non_silence | unsettled( &this->blip );
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void remove_( struct Tracked_Blip_Buffer* this, int n )
|
||||||
|
{
|
||||||
|
if ( (this->last_non_silence -= n) < 0 )
|
||||||
|
this->last_non_silence = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Tracked_remove_silence( struct Tracked_Blip_Buffer* this, int n )
|
||||||
|
{
|
||||||
|
remove_( this, n );
|
||||||
|
Blip_remove_silence( &this->blip, n );
|
||||||
|
}
|
||||||
|
|
||||||
|
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->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
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void mix_stereo( struct Stereo_Mixer* this, blip_sample_t out_ [], int 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
|
||||||
|
{
|
||||||
|
--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
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Mixer_read_pairs( struct Stereo_Mixer* this, blip_sample_t out [], int 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 );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Multi_Buffer
|
||||||
|
|
||||||
|
void Buffer_init( struct Multi_Buffer* this )
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
blargg_err_t Buffer_set_sample_rate( struct Multi_Buffer* this, int rate, int msec )
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
for ( i = 0; i < buf_count; i++ )
|
for ( i = bufs_size; --i >= 0; )
|
||||||
RETURN_ERR( Blip_set_sample_rate( &this->bufs[i], rate, msec ) );
|
RETURN_ERR( Blip_set_sample_rate( &this->bufs [i].blip, rate, msec ) );
|
||||||
|
|
||||||
this->sample_rate_ = Blip_sample_rate( &this->bufs [0] );
|
this->sample_rate_ = Blip_sample_rate( &this->bufs [0].blip );
|
||||||
this->length_ = Blip_length( &this->bufs [0] );
|
this->length_ = Blip_length( &this->bufs [0].blip );
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Buffer_clock_rate( struct Stereo_Buffer* this, long rate )
|
void Buffer_clock_rate( struct Multi_Buffer* this, int rate )
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
for ( i = 0; i < buf_count; i++ )
|
for ( i = bufs_size; --i >= 0; )
|
||||||
Blip_set_clock_rate( &this->bufs [i], rate );
|
Blip_set_clock_rate( &this->bufs [i].blip, rate );
|
||||||
}
|
}
|
||||||
|
|
||||||
void Buffer_bass_freq( struct Stereo_Buffer* this, int bass )
|
void Buffer_bass_freq( struct Multi_Buffer* this, int bass )
|
||||||
{
|
{
|
||||||
unsigned i;
|
int i;
|
||||||
for ( i = 0; i < buf_count; i++ )
|
for ( i = bufs_size; --i >= 0; )
|
||||||
Blip_bass_freq( &this->bufs [i], bass );
|
Blip_bass_freq( &this->bufs [i].blip, bass );
|
||||||
}
|
}
|
||||||
|
|
||||||
struct channel_t Buffer_channel( struct Stereo_Buffer* this )
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct channel_t Buffer_channel( struct Multi_Buffer* this, int i )
|
||||||
|
{
|
||||||
|
(void) i;
|
||||||
return this->chan;
|
return this->chan;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Buffer_clear( struct Stereo_Buffer* this )
|
void Buffer_clear( struct Multi_Buffer* this )
|
||||||
{
|
{
|
||||||
this->stereo_added = 0;
|
|
||||||
this->was_stereo = false;
|
|
||||||
int i;
|
int i;
|
||||||
for ( i = 0; i < buf_count; i++ )
|
this->mixer.samples_read = 0;
|
||||||
Blip_clear( &this->bufs [i], 1 );
|
for ( i = bufs_size; --i >= 0; )
|
||||||
|
Tracked_clear( &this->bufs [i] );
|
||||||
}
|
}
|
||||||
|
|
||||||
void Buffer_end_frame( struct Stereo_Buffer* this, blip_time_t clock_count )
|
void Buffer_end_frame( struct Multi_Buffer* this, blip_time_t clock_count )
|
||||||
{
|
{
|
||||||
this->stereo_added = 0;
|
int i;
|
||||||
unsigned i;
|
for ( i = bufs_size; --i >= 0; )
|
||||||
for ( i = 0; i < buf_count; i++ )
|
Tracked_end_frame( &this->bufs [i], clock_count );
|
||||||
{
|
|
||||||
this->stereo_added |= Blip_clear_modified( &this->bufs [i] ) << i;
|
|
||||||
Blip_end_frame( &this->bufs [i], clock_count );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
long Buffer_read_samples( struct Stereo_Buffer* this, blip_sample_t* out, long count )
|
int Buffer_read_samples( struct Multi_Buffer* this, blip_sample_t out [], int out_size )
|
||||||
{
|
{
|
||||||
require( !(count & 1) ); // count must be even
|
require( (out_size & 1) == 0 ); // must read an even number of samples
|
||||||
count = (unsigned) count / 2;
|
out_size = min( out_size, Buffer_samples_avail( this ) );
|
||||||
|
|
||||||
long avail = Blip_samples_avail( &this->bufs [0] );
|
int pair_count = (int) (out_size >> 1);
|
||||||
if ( count > avail )
|
if ( pair_count )
|
||||||
count = avail;
|
|
||||||
if ( count )
|
|
||||||
{
|
{
|
||||||
int bufs_used = this->stereo_added | this->was_stereo;
|
Mixer_read_pairs( &this->mixer, out, pair_count );
|
||||||
//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 ( Buffer_samples_avail( this ) <= 0 || this->immediate_removal_ )
|
||||||
if ( !Blip_samples_avail( &this->bufs [0] ) )
|
|
||||||
{
|
{
|
||||||
this->was_stereo = this->stereo_added;
|
int i;
|
||||||
this->stereo_added = 0;
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return count * 2;
|
return out_size;
|
||||||
}
|
|
||||||
|
|
||||||
unsigned Buffer_channels_changed_count( struct Stereo_Buffer* this )
|
|
||||||
{
|
|
||||||
return this->channels_changed_count_;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Buffer_channels_changed( struct Stereo_Buffer* this )
|
|
||||||
{
|
|
||||||
this->channels_changed_count_++;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Buffer_mix_stereo( struct Stereo_Buffer* this, blip_sample_t* out_, blargg_long count )
|
|
||||||
{
|
|
||||||
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] );
|
|
||||||
}
|
|
||||||
|
|
||||||
void Buffer_mix_stereo_no_center( struct Stereo_Buffer* this, blip_sample_t* out_, blargg_long count )
|
|
||||||
{
|
|
||||||
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] );
|
|
||||||
}
|
|
||||||
|
|
||||||
void Buffer_mix_mono( struct Stereo_Buffer* this, blip_sample_t* out_, blargg_long count )
|
|
||||||
{
|
|
||||||
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 )
|
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
BLIP_READER_END( center, this->bufs [0] );
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
// Blip_Buffer 0.4.1
|
||||||
#ifndef MULTI_BUFFER_H
|
#ifndef MULTI_BUFFER_H
|
||||||
|
|
@ -16,57 +16,99 @@ struct channel_t {
|
||||||
|
|
||||||
enum { type_index_mask = 0xFF };
|
enum { type_index_mask = 0xFF };
|
||||||
enum { wave_type = 0x100, noise_type = 0x200, mixed_type = wave_type | noise_type };
|
enum { wave_type = 0x100, noise_type = 0x200, mixed_type = wave_type | noise_type };
|
||||||
enum { buf_count = 3 };
|
enum { stereo = 2 };
|
||||||
|
enum { bufs_size = 3 };
|
||||||
|
|
||||||
struct Stereo_Buffer {
|
// Tracked_Blip_Buffer
|
||||||
struct Blip_Buffer bufs [buf_count];
|
struct Tracked_Blip_Buffer {
|
||||||
struct channel_t chan;
|
struct Blip_Buffer blip;
|
||||||
int stereo_added;
|
int last_non_silence;
|
||||||
int was_stereo;
|
|
||||||
|
|
||||||
unsigned channels_changed_count_;
|
|
||||||
long sample_rate_;
|
|
||||||
int length_;
|
|
||||||
int samples_per_frame_;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Initializes Stereo_Buffer structure
|
void Tracked_init( struct Tracked_Blip_Buffer* this );
|
||||||
void Buffer_init( struct Stereo_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 );
|
||||||
|
|
||||||
blargg_err_t Buffer_set_sample_rate( struct Stereo_Buffer* this, long, int msec );
|
static inline delta_t unsettled( struct Blip_Buffer* this )
|
||||||
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 );
|
|
||||||
|
|
||||||
long Buffer_read_samples( struct Stereo_Buffer* this, blip_sample_t*, long );
|
|
||||||
|
|
||||||
// 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 );
|
|
||||||
|
|
||||||
// Number of samples per output frame (1 = mono, 2 = stereo)
|
|
||||||
static inline int Buffer_samples_per_frame( struct Stereo_Buffer* this )
|
|
||||||
{
|
{
|
||||||
return this->samples_per_frame_;
|
return this->reader_accum_ >> delta_bits;
|
||||||
}
|
}
|
||||||
|
|
||||||
// See Blip_Buffer.h
|
// Stereo Mixer
|
||||||
static inline long Buffer_sample_rate( struct Stereo_Buffer* this )
|
struct Stereo_Mixer {
|
||||||
{
|
struct Tracked_Blip_Buffer* bufs [3];
|
||||||
return this->sample_rate_;
|
int samples_read;
|
||||||
}
|
};
|
||||||
|
|
||||||
// Length of buffer, in milliseconds
|
void Mixer_init( struct Stereo_Mixer* this );
|
||||||
static inline int Buffer_length( struct Stereo_Buffer* 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_;
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
|
||||||
|
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; }
|
||||||
|
|
||||||
|
// Initializes Multi_Buffer structure
|
||||||
|
void Buffer_init( struct Multi_Buffer* this );
|
||||||
|
|
||||||
|
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_;
|
return this->length_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Count of changes to channel configuration. Incremented whenever
|
||||||
|
// a change is made to any of the Blip_Buffers for any channel.
|
||||||
|
static inline unsigned Buffer_channels_changed_count( struct Multi_Buffer* this )
|
||||||
|
{
|
||||||
|
return this->channels_changed_count_;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void Buffer_disable_immediate_removal( struct Multi_Buffer* this )
|
||||||
|
{
|
||||||
|
this->immediate_removal_ = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int Buffer_sample_rate( struct Multi_Buffer* this )
|
||||||
|
{
|
||||||
|
return this->sample_rate_;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int Buffer_samples_avail( struct Multi_Buffer* this )
|
||||||
|
{
|
||||||
|
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
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -39,19 +39,19 @@ void Apu_init( struct Nes_Apu* this )
|
||||||
this->oscs [4] = &this->dmc.osc;
|
this->oscs [4] = &this->dmc.osc;
|
||||||
|
|
||||||
Apu_output( this, NULL );
|
Apu_output( this, NULL );
|
||||||
|
this->dmc.nonlinear = false;
|
||||||
Apu_volume( this, (int)FP_ONE_VOLUME );
|
Apu_volume( this, (int)FP_ONE_VOLUME );
|
||||||
Apu_reset( this, false, 0 );
|
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;
|
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, tnd * 2.752 );
|
||||||
Synth_volume( &this->triangle.synth, 3 * tnd );
|
Synth_volume( &this->noise.synth , tnd * 1.849 );
|
||||||
Synth_volume( &this->noise.synth, 2 * tnd );
|
Synth_volume( &this->dmc.synth , tnd );
|
||||||
Synth_volume( &this->dmc.synth, tnd );
|
|
||||||
|
|
||||||
this->square1 .osc.last_amp = 0;
|
this->square1 .osc.last_amp = 0;
|
||||||
this->square2 .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 )
|
void Apu_volume( struct Nes_Apu* this, int v )
|
||||||
{
|
{
|
||||||
this->dmc.nonlinear = false;
|
if ( !this->dmc.nonlinear )
|
||||||
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->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->noise.synth, (int)((long long)(0.0741 *FP_ONE_VOLUME) * v / amp_range / FP_ONE_VOLUME) );
|
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->dmc.synth, (int)((long long)(0.42545*FP_ONE_VOLUME) * v / 127 / FP_ONE_VOLUME) );
|
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 )
|
void Apu_output( struct Nes_Apu* this, struct Blip_Buffer* buffer )
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
// NES 2A03 APU sound chip emulator
|
// NES 2A03 APU sound chip emulator
|
||||||
|
|
||||||
// Nes_Snd_Emu 0.1.8
|
// Nes_Snd_Emu 0.2.0-pre
|
||||||
#ifndef NES_APU_H
|
#ifndef NES_APU_H
|
||||||
#define NES_APU_H
|
#define NES_APU_H
|
||||||
|
|
||||||
|
|
@ -9,7 +9,7 @@
|
||||||
|
|
||||||
enum { apu_status_addr = 0x4015 };
|
enum { apu_status_addr = 0x4015 };
|
||||||
enum { apu_osc_count = 5 };
|
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_irq_waiting = 0 };
|
||||||
|
|
||||||
enum { apu_io_addr = 0x4000 };
|
enum { apu_io_addr = 0x4000 };
|
||||||
|
|
@ -18,23 +18,15 @@ enum { apu_io_size = 0x18 };
|
||||||
struct apu_state_t;
|
struct apu_state_t;
|
||||||
|
|
||||||
struct Nes_Apu {
|
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;
|
|
||||||
|
|
||||||
int tempo_;
|
int tempo_;
|
||||||
nes_time_t last_time; // has been run until this time in current frame
|
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 earliest_irq_;
|
||||||
nes_time_t next_irq;
|
nes_time_t next_irq;
|
||||||
int frame_period;
|
int frame_period;
|
||||||
int frame_delay; // cycles until frame counter runs next
|
int frame_delay; // cycles until frame counter runs next
|
||||||
int frame; // current frame (0-3)
|
int frame; // current frame (0-3)
|
||||||
|
int osc_enables;
|
||||||
int frame_mode;
|
int frame_mode;
|
||||||
bool irq_flag;
|
bool irq_flag;
|
||||||
|
|
||||||
|
|
@ -42,6 +34,13 @@ struct Nes_Apu {
|
||||||
void* irq_data;
|
void* irq_data;
|
||||||
|
|
||||||
Synth square_synth; // shared by squares
|
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
|
// Init Nes apu
|
||||||
|
|
@ -124,11 +123,13 @@ static inline nes_time_t Dmc_next_read_time( struct Nes_Dmc* this )
|
||||||
if ( this->osc.length_counter == 0 )
|
if ( this->osc.length_counter == 0 )
|
||||||
return apu_no_irq; // not reading
|
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
|
// 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 ); }
|
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 );
|
void Apu_irq_changed( struct Nes_Apu* this );
|
||||||
|
|
||||||
|
// Experimental
|
||||||
|
void Apu_enable_nonlinear_( struct Nes_Apu* this, double sq, double tnd );
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -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 )
|
|
||||||
|
|
@ -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;
|
struct Blip_Buffer* const osc_output = this->oscs [index].output;
|
||||||
if ( !osc_output )
|
if ( !osc_output )
|
||||||
continue;
|
continue;
|
||||||
/* osc_output->set_modified(); */
|
|
||||||
Blip_set_modified( osc_output );
|
|
||||||
|
|
||||||
// check for unsupported mode
|
// check for unsupported mode
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
|
|
@ -92,11 +90,13 @@ void Fme7_run_until( struct Nes_Fme7_Apu* this, blip_time_t end_time )
|
||||||
int amp = volume;
|
int amp = volume;
|
||||||
if ( !this->phases [index] )
|
if ( !this->phases [index] )
|
||||||
amp = 0;
|
amp = 0;
|
||||||
|
|
||||||
{
|
{
|
||||||
int delta = amp - this->oscs [index].last_amp;
|
int delta = amp - this->oscs [index].last_amp;
|
||||||
if ( delta )
|
if ( delta )
|
||||||
{
|
{
|
||||||
this->oscs [index].last_amp = amp;
|
this->oscs [index].last_amp = amp;
|
||||||
|
Blip_set_modified( osc_output );
|
||||||
Synth_offset( &this->synth, this->last_time, delta, 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 )
|
if ( time < end_time )
|
||||||
{
|
{
|
||||||
int delta = amp * 2 - volume;
|
int delta = amp * 2 - volume;
|
||||||
|
Blip_set_modified( osc_output );
|
||||||
if ( volume )
|
if ( volume )
|
||||||
{
|
{
|
||||||
do
|
do
|
||||||
|
|
@ -123,7 +124,7 @@ void Fme7_run_until( struct Nes_Fme7_Apu* this, blip_time_t end_time )
|
||||||
// maintain phase when silent
|
// maintain phase when silent
|
||||||
int count = (end_time - time + period - 1) / period;
|
int count = (end_time - time + period - 1) / period;
|
||||||
this->phases [index] ^= count & 1;
|
this->phases [index] ^= count & 1;
|
||||||
time += (blargg_long) count * period;
|
time += count * period;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
// Sunsoft FME-7 sound emulator
|
// Sunsoft FME-7 sound emulator
|
||||||
|
|
||||||
// Game_Music_Emu 0.5.5
|
// Game_Music_Emu 0.6-pre
|
||||||
#ifndef NES_FME7_APU_H
|
#ifndef NES_FME7_APU_H
|
||||||
#define NES_FME7_APU_H
|
#define NES_FME7_APU_H
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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;
|
struct Blip_Buffer* output = osc->output;
|
||||||
if ( !output )
|
if ( !output )
|
||||||
continue;
|
continue;
|
||||||
/* output->set_modified(); */
|
|
||||||
Blip_set_modified( output );
|
|
||||||
|
|
||||||
blip_resampled_time_t time =
|
blip_resampled_time_t time =
|
||||||
Blip_resampled_time( output, this->last_time ) + osc->delay;
|
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 )
|
if ( !volume )
|
||||||
continue;
|
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 )
|
if ( freq < 64 * active_oscs )
|
||||||
continue; // prevent low frequencies from excessively delaying freq changes
|
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 =
|
blip_resampled_time_t period =
|
||||||
/* output->resampled_duration( 983040 ) / freq * active_oscs; */
|
Blip_resampled_duration( output, lowest_freq_period / 8 ) / freq * 8 * active_oscs;
|
||||||
Blip_resampled_duration( output, 983040 ) / freq * active_oscs;
|
|
||||||
|
|
||||||
int wave_size = 32 - (osc_reg [4] >> 2 & 7) * 4;
|
int wave_size = 32 - (osc_reg [4] >> 2 & 7) * 4;
|
||||||
if ( !wave_size )
|
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 last_amp = osc->last_amp;
|
||||||
int wave_pos = osc->wave_pos;
|
int wave_pos = osc->wave_pos;
|
||||||
|
|
||||||
|
Blip_set_modified( output );
|
||||||
|
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
// read wave sample
|
// read wave sample
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ enum { namco_data_reg_addr = 0x4800 };
|
||||||
enum { namco_reg_count = 0x80 };
|
enum { namco_reg_count = 0x80 };
|
||||||
|
|
||||||
struct Namco_Osc {
|
struct Namco_Osc {
|
||||||
blargg_long delay;
|
int delay;
|
||||||
struct Blip_Buffer* output;
|
struct Blip_Buffer* output;
|
||||||
short last_amp;
|
short last_amp;
|
||||||
short wave_pos;
|
short wave_pos;
|
||||||
|
|
|
||||||
|
|
@ -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;
|
int count = (remain + timer_period - 1) / timer_period;
|
||||||
this->phase = (this->phase + count) & (square_phase_range - 1);
|
this->phase = (this->phase + count) & (square_phase_range - 1);
|
||||||
time += (blargg_long) count * timer_period;
|
time += count * timer_period;
|
||||||
}
|
}
|
||||||
return time;
|
return time;
|
||||||
}
|
}
|
||||||
|
|
@ -107,8 +107,6 @@ void Square_run( struct Nes_Square* this, nes_time_t time, nes_time_t end_time )
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Blip_set_modified( osc->output );
|
|
||||||
|
|
||||||
int offset = period >> (osc->regs [1] & shift_mask);
|
int offset = period >> (osc->regs [1] & shift_mask);
|
||||||
if ( osc->regs [1] & negate_flag )
|
if ( osc->regs [1] & negate_flag )
|
||||||
offset = 0;
|
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 ( volume == 0 || period < 8 || (period + offset) >= 0x800 )
|
||||||
{
|
{
|
||||||
if ( osc->last_amp ) {
|
if ( osc->last_amp ) {
|
||||||
|
Blip_set_modified( osc->output );
|
||||||
Synth_offset( this->synth, time, -osc->last_amp, osc->output );
|
Synth_offset( this->synth, time, -osc->last_amp, osc->output );
|
||||||
osc->last_amp = 0;
|
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 )
|
if ( this->phase < duty )
|
||||||
amp ^= volume;
|
amp ^= volume;
|
||||||
|
|
||||||
|
Blip_set_modified( osc->output );
|
||||||
{
|
{
|
||||||
int delta = Osc_update_amp( osc, amp );
|
int delta = Osc_update_amp( osc, amp );
|
||||||
if ( delta )
|
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;
|
int count = (remain + timer_period - 1) / timer_period;
|
||||||
this->phase = ((unsigned) this->phase + 1 - count) & (Triangle_phase_range * 2 - 1);
|
this->phase = ((unsigned) this->phase + 1 - count) & (Triangle_phase_range * 2 - 1);
|
||||||
this->phase++;
|
this->phase++;
|
||||||
time += (blargg_long) count * timer_period;
|
time += count * timer_period;
|
||||||
}
|
}
|
||||||
return time;
|
return time;
|
||||||
}
|
}
|
||||||
|
|
@ -219,14 +219,15 @@ void Triangle_run( struct Nes_Triangle* this, nes_time_t time, nes_time_t end_ti
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Blip_set_modified( osc->output );
|
|
||||||
|
|
||||||
// to do: track phase when period < 3
|
// to do: track phase when period < 3
|
||||||
// to do: Output 7.5 on dac when period < 2? More accurate, but results in more clicks.
|
// 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 ) );
|
int delta = Osc_update_amp( osc, Triangle_calc_amp( this ) );
|
||||||
if ( delta )
|
if ( delta )
|
||||||
|
{
|
||||||
|
Blip_set_modified( osc->output );
|
||||||
Synth_offset( &this->synth, time, delta, osc->output );
|
Synth_offset( &this->synth, time, delta, osc->output );
|
||||||
|
}
|
||||||
|
|
||||||
time += osc->delay;
|
time += osc->delay;
|
||||||
if ( osc->length_counter == 0 || this->linear_counter == 0 || timer_period < 3 )
|
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;
|
phase -= Triangle_phase_range;
|
||||||
volume = -volume;
|
volume = -volume;
|
||||||
}
|
}
|
||||||
|
Blip_set_modified( osc->output );
|
||||||
|
|
||||||
do {
|
do {
|
||||||
if ( --phase == 0 ) {
|
if ( --phase == 0 ) {
|
||||||
phase = Triangle_phase_range;
|
phase = Triangle_phase_range;
|
||||||
volume = -volume;
|
volume = -volume;
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
Synth_offset_inline( &this->synth, time, volume, output );
|
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;
|
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,
|
0, 24, 48, 71, 94, 118, 141, 163, 186, 209, 231, 253, 275, 297, 319, 340,
|
||||||
15,15,16,17,18,19,20,20,21,22,23,24,24,25,26,27,
|
361, 383, 404, 425, 445, 466, 486, 507, 527, 547, 567, 587, 606, 626, 645, 664,
|
||||||
27,28,29,30,31,31,32,33,33,34,35,36,36,37,38,38,
|
683, 702, 721, 740, 758, 777, 795, 813, 832, 850, 867, 885, 903, 920, 938, 955,
|
||||||
39,40,41,41,42,43,43,44,45,45,46,47,47,48,48,49,
|
972, 989,1006,1023,1040,1056,1073,1089,1105,1122,1138,1154,1170,1185,1201,1217,
|
||||||
50,50,51,52,52,53,53,54,55,55,56,56,57,58,58,59,
|
1232,1248,1263,1278,1293,1308,1323,1338,1353,1368,1382,1397,1411,1425,1440,1454,
|
||||||
59,60,60,61,61,62,63,63,64,64,65,65,66,66,67,67,
|
1468,1482,1496,1510,1523,1537,1551,1564,1578,1591,1604,1618,1631,1644,1657,1670,
|
||||||
68,68,69,70,70,71,71,72,72,73,73,74,74,75,75,75,
|
1683,1695,1708,1721,1733,1746,1758,1771,1783,1795,1807,1819,1831,1843,1855,1867,
|
||||||
76,76,77,77,78,78,79,79,80,80,81,81,82,82,82,83,
|
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 )
|
void Dmc_write_register( struct Nes_Dmc* this, int addr, int data )
|
||||||
{
|
{
|
||||||
if ( addr == 0 )
|
if ( addr == 0 )
|
||||||
|
|
@ -363,14 +375,7 @@ void Dmc_write_register( struct Nes_Dmc* this, int addr, int data )
|
||||||
}
|
}
|
||||||
else if ( addr == 1 )
|
else if ( addr == 1 )
|
||||||
{
|
{
|
||||||
int old_dac = this->dac;
|
|
||||||
this->dac = data & 0x7F;
|
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 )
|
void Dmc_run( struct Nes_Dmc* this, nes_time_t time, nes_time_t end_time )
|
||||||
{
|
{
|
||||||
struct Nes_Osc* osc = &this->osc;
|
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 )
|
if ( !osc->output )
|
||||||
{
|
{
|
||||||
this->silence = true;
|
this->silence = true;
|
||||||
}
|
}
|
||||||
else
|
else if ( delta )
|
||||||
{
|
{
|
||||||
Blip_set_modified( osc->output );
|
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;
|
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;
|
const int period = this->period;
|
||||||
int bits = this->bits;
|
int bits = this->bits;
|
||||||
int dac = this->dac;
|
int dac = this->dac;
|
||||||
|
if ( output )
|
||||||
|
Blip_set_modified( output );
|
||||||
|
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
|
|
@ -444,7 +450,7 @@ void Dmc_run( struct Nes_Dmc* this, nes_time_t time, nes_time_t end_time )
|
||||||
bits >>= 1;
|
bits >>= 1;
|
||||||
if ( (unsigned) (dac + step) <= 0x7F ) {
|
if ( (unsigned) (dac + step) <= 0x7F ) {
|
||||||
dac += step;
|
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 ) {
|
if ( !this->buf_full ) {
|
||||||
this->silence = true;
|
this->silence = true;
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
this->silence = false;
|
this->silence = false;
|
||||||
bits = this->buf;
|
bits = this->buf;
|
||||||
this->buf_full = false;
|
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 );
|
while ( time < end_time );
|
||||||
|
|
||||||
this->dac = dac;
|
this->dac = dac;
|
||||||
osc->last_amp = dac;
|
//osc->last_amp = dac;
|
||||||
this->bits = bits;
|
this->bits = bits;
|
||||||
}
|
}
|
||||||
this->bits_remain = bits_remain;
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Blip_set_modified( osc->output );
|
|
||||||
|
|
||||||
const int volume = Noise_volume( this );
|
const int volume = Noise_volume( this );
|
||||||
int amp = (this->noise & 1) ? volume : 0;
|
int amp = (this->noise & 1) ? volume : 0;
|
||||||
{
|
{
|
||||||
int delta = Osc_update_amp( osc, amp );
|
int delta = Osc_update_amp( osc, amp );
|
||||||
if ( delta )
|
if ( delta )
|
||||||
|
{
|
||||||
|
Blip_set_modified( osc->output );
|
||||||
Synth_offset( &this->synth, time, delta, osc->output );
|
Synth_offset( &this->synth, time, delta, osc->output );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
time += osc->delay;
|
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 noise = this->noise;
|
||||||
int delta = amp * 2 - volume;
|
int delta = amp * 2 - volume;
|
||||||
const int tap = (osc->regs [2] & mode_flag ? 8 : 13);
|
const int tap = (osc->regs [2] & mode_flag ? 8 : 13);
|
||||||
|
Blip_set_modified( osc->output );
|
||||||
|
|
||||||
do {
|
do {
|
||||||
int feedback = (noise << tap) ^ (noise << 14);
|
int feedback = (noise << tap) ^ (noise << 14);
|
||||||
|
|
|
||||||
|
|
@ -144,7 +144,7 @@ struct Nes_Dmc
|
||||||
bool pal_mode;
|
bool pal_mode;
|
||||||
bool nonlinear;
|
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;
|
void* prg_reader_data;
|
||||||
|
|
||||||
struct Nes_Apu* apu;
|
struct Nes_Apu* apu;
|
||||||
|
|
|
||||||
|
|
@ -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;
|
struct Blip_Buffer* output = osc->output;
|
||||||
if ( !output )
|
if ( !output )
|
||||||
return;
|
return;
|
||||||
Blip_set_modified( output );
|
|
||||||
|
|
||||||
int volume = osc->regs [0] & 15;
|
int volume = osc->regs [0] & 15;
|
||||||
if ( !(osc->regs [2] & 0x80) )
|
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 )
|
if ( delta )
|
||||||
{
|
{
|
||||||
osc->last_amp += delta;
|
osc->last_amp += delta;
|
||||||
|
Blip_set_modified( output );
|
||||||
Synth_offset( &this->square_synth, time, delta, output );
|
Synth_offset( &this->square_synth, time, delta, output );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
// Konami VRC6 sound chip emulator
|
// Konami VRC6 sound chip emulator
|
||||||
|
|
||||||
// Nes_Snd_Emu 0.1.8
|
// Nes_Snd_Emu 0.2.0-pre
|
||||||
#ifndef NES_VRC6_APU_H
|
#ifndef NES_VRC6_APU_H
|
||||||
#define NES_VRC6_APU_H
|
#define NES_VRC6_APU_H
|
||||||
|
|
||||||
|
|
@ -14,7 +14,7 @@ enum { vrc6_addr_step = 0x1000 };
|
||||||
|
|
||||||
struct Vrc6_Osc
|
struct Vrc6_Osc
|
||||||
{
|
{
|
||||||
uint8_t regs [vrc6_reg_count];
|
uint8_t regs [3];
|
||||||
struct Blip_Buffer* output;
|
struct Blip_Buffer* output;
|
||||||
int delay;
|
int delay;
|
||||||
int last_amp;
|
int last_amp;
|
||||||
|
|
|
||||||
|
|
@ -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 "nsf_emu.h"
|
||||||
#include "multi_buffer.h"
|
#include "multi_buffer.h"
|
||||||
|
|
@ -19,33 +19,19 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
|
||||||
#include "blargg_source.h"
|
#include "blargg_source.h"
|
||||||
|
|
||||||
const char gme_wrong_file_type [] = "Wrong file type for this emulator";
|
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
|
// number of frames until play interrupts init
|
||||||
int const initial_play_delay = 7; // KikiKaikai needed this to work
|
int const initial_play_delay = 7; // KikiKaikai needed this to work
|
||||||
|
int const bank_size = 0x1000;
|
||||||
int const rom_addr = 0x8000;
|
int const rom_addr = 0x8000;
|
||||||
|
|
||||||
static void clear_track_vars( struct Nsf_Emu* this )
|
static void clear_track_vars( struct Nsf_Emu* this )
|
||||||
{
|
{
|
||||||
this->current_track = -1;
|
this->current_track = -1;
|
||||||
this->out_time = 0;
|
track_stop( &this->track_filter );
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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 );
|
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);
|
this->gain = (int)(FP_ONE_GAIN);
|
||||||
|
|
||||||
// defaults
|
// defaults
|
||||||
this->max_initial_silence = 2;
|
this->tfilter = *track_get_setup( &this->track_filter );
|
||||||
this->ignore_silence = false;
|
this->tfilter.max_initial = 2;
|
||||||
this->voice_count = 0;
|
this->tfilter.lookahead = 6;
|
||||||
|
this->track_filter.silence_ignored_ = false;
|
||||||
|
|
||||||
// Set sound gain
|
// Set sound gain
|
||||||
Sound_set_gain( this, (int)(FP_ONE_GAIN*1.2) );
|
Sound_set_gain( this, (int)(FP_ONE_GAIN*1.2) );
|
||||||
|
|
||||||
// Unload
|
|
||||||
clear_track_vars( this );
|
|
||||||
|
|
||||||
// Init rom
|
// Init rom
|
||||||
Rom_init( &this->rom, 0x1000 );
|
Rom_init( &this->rom, bank_size );
|
||||||
|
|
||||||
// Init & clear nsfe info
|
// Init & clear nsfe info
|
||||||
Info_init( &this->info );
|
Info_init( &this->info );
|
||||||
|
|
@ -78,16 +62,36 @@ void Nsf_init( struct Nsf_Emu* this )
|
||||||
Cpu_init( &this->cpu );
|
Cpu_init( &this->cpu );
|
||||||
Apu_init( &this->apu );
|
Apu_init( &this->apu );
|
||||||
Apu_dmc_reader( &this->apu, pcm_read, this );
|
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
|
// 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 )
|
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" ); **/
|
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;
|
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 );
|
Vrc6_init( &this->vrc6 );
|
||||||
adjusted_gain = (adjusted_gain*3) / 4;
|
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 ) )
|
if ( fme7_enabled( this ) )
|
||||||
|
|
@ -111,7 +118,10 @@ static blargg_err_t init_sound( struct Nsf_Emu* this )
|
||||||
Fme7_init( &this->fme7 );
|
Fme7_init( &this->fme7 );
|
||||||
adjusted_gain = (adjusted_gain*3) / 4;
|
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 ) )
|
if ( mmc5_enabled( this ) )
|
||||||
|
|
@ -119,7 +129,11 @@ static blargg_err_t init_sound( struct Nsf_Emu* this )
|
||||||
Mmc5_init( &this->mmc5 );
|
Mmc5_init( &this->mmc5 );
|
||||||
adjusted_gain = (adjusted_gain*3) / 4;
|
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 ) )
|
if ( fds_enabled( this ) )
|
||||||
|
|
@ -127,7 +141,10 @@ static blargg_err_t init_sound( struct Nsf_Emu* this )
|
||||||
Fds_init( &this->fds );
|
Fds_init( &this->fds );
|
||||||
adjusted_gain = (adjusted_gain*3) / 4;
|
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 ) )
|
if ( namco_enabled( this ) )
|
||||||
|
|
@ -135,22 +152,31 @@ static blargg_err_t init_sound( struct Nsf_Emu* this )
|
||||||
Namco_init( &this->namco );
|
Namco_init( &this->namco );
|
||||||
adjusted_gain = (adjusted_gain*3) / 4;
|
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
|
#ifndef NSF_EMU_NO_VRC7
|
||||||
|
if ( vrc7_enabled( this ) )
|
||||||
|
{
|
||||||
|
|
||||||
Vrc7_init( &this->vrc7 );
|
Vrc7_init( &this->vrc7 );
|
||||||
Vrc7_set_rate( &this->vrc7, this->sample_rate );
|
Vrc7_set_rate( &this->vrc7, this->sample_rate );
|
||||||
#endif
|
|
||||||
|
|
||||||
adjusted_gain = (adjusted_gain*3) / 4;
|
adjusted_gain = (adjusted_gain*3) / 4;
|
||||||
|
|
||||||
this->voice_count += vrc7_osc_count;
|
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 );
|
||||||
|
}
|
||||||
|
|
||||||
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 ( namco_enabled( this ) ) Namco_volume( &this->namco, adjusted_gain );
|
||||||
if ( vrc6_enabled( this ) ) Vrc6_volume( &this->vrc6, adjusted_gain );
|
if ( vrc6_enabled( this ) ) Vrc6_volume( &this->vrc6, adjusted_gain );
|
||||||
if ( fme7_enabled( this ) ) Fme7_volume( &this->fme7, 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;
|
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 )
|
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;
|
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
|
// Unload
|
||||||
Info_unload( &this->info ); // TODO: extremely hacky!
|
Info_unload( &this->info ); // TODO: extremely hacky!
|
||||||
|
|
@ -258,8 +284,7 @@ blargg_err_t Nsf_post_load( struct Nsf_Emu* this )
|
||||||
warning( "Unknown file version" ); */
|
warning( "Unknown file version" ); */
|
||||||
|
|
||||||
// set up data
|
// 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) )
|
/* if ( load_addr < (fds_enabled() ? sram_addr : rom_addr) )
|
||||||
warning( "Load address is too low" ); */
|
warning( "Load address is too low" ); */
|
||||||
|
|
||||||
|
|
@ -277,17 +302,16 @@ blargg_err_t Nsf_post_load( struct Nsf_Emu* this )
|
||||||
|
|
||||||
// Post load
|
// Post load
|
||||||
Sound_set_tempo( this, this->tempo );
|
Sound_set_tempo( this, this->tempo );
|
||||||
|
|
||||||
// Remute voices
|
|
||||||
Sound_mute_voices( this, this->mute_mask_ );
|
Sound_mute_voices( this, this->mute_mask_ );
|
||||||
|
|
||||||
// Set track_count
|
// Set track_count
|
||||||
this->track_count = this->header.track_count;
|
this->track_count = this->header.track_count;
|
||||||
|
|
||||||
// Change clock rate & setup buffer
|
// 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__ );
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -416,11 +440,13 @@ static void set_voice( struct Nsf_Emu* this, int i, struct Blip_Buffer* buf, str
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( vrc7_enabled( this ) && (i -= vrc7_osc_count) < 0 )
|
#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;
|
Vrc7_set_output( &this->vrc7, i + vrc7_osc_count, buf );
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
@ -429,7 +455,7 @@ static void set_voice( struct Nsf_Emu* this, int i, struct Blip_Buffer* buf, str
|
||||||
|
|
||||||
// Music Emu
|
// 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
|
require( !this->sample_rate ); // sample rate can't be changed once set
|
||||||
Buffer_init( &this->stereo_buf );
|
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 );
|
Buffer_bass_freq( &this->stereo_buf, 80 );
|
||||||
|
|
||||||
this->sample_rate = rate;
|
this->sample_rate = rate;
|
||||||
|
RETURN_ERR( track_init( &this->track_filter, this ) );
|
||||||
|
this->tfilter.max_silence = 6 * stereo * this->sample_rate;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -466,7 +494,7 @@ void Sound_mute_voices( struct Nsf_Emu* this, int mask )
|
||||||
}
|
}
|
||||||
else
|
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) ||
|
assert( (ch.center && ch.left && ch.right) ||
|
||||||
(!ch.center && !ch.left && !ch.right) ); // all or nothing
|
(!ch.center && !ch.left && !ch.right) ); // all or nothing
|
||||||
set_voice( this, i, ch.center, ch.left, ch.right );
|
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;
|
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 )
|
void cpu_write( struct Nsf_Emu* this, addr_t addr, int data )
|
||||||
{
|
{
|
||||||
#ifndef NSF_EMU_APU_ONLY
|
#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 )
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -572,7 +581,7 @@ void cpu_write( struct Nsf_Emu* this, addr_t addr, int data )
|
||||||
|
|
||||||
if ( addr == namco_data_reg_addr )
|
if ( addr == namco_data_reg_addr )
|
||||||
{
|
{
|
||||||
Namco_write_data( &this->namco, time, data );
|
Namco_write_data( &this->namco, Cpu_time( &this->cpu ), data );
|
||||||
return;
|
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;
|
int osc = (unsigned) (addr - vrc6_base_addr) / vrc6_addr_step;
|
||||||
if ( (unsigned) osc < vrc6_osc_count && (unsigned) reg < vrc6_reg_count )
|
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;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -597,7 +606,7 @@ void cpu_write( struct Nsf_Emu* this, addr_t addr, int data )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case fme7_data_addr:
|
case fme7_data_addr:
|
||||||
Fme7_write_data( &this->fme7, time, data );
|
Fme7_write_data( &this->fme7, Cpu_time( &this->cpu ), data );
|
||||||
return;
|
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 )
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -625,49 +634,28 @@ void cpu_write( struct Nsf_Emu* this, addr_t addr, int data )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( vrc7_enabled( this) )
|
#ifndef NSF_EMU_NO_VRC7
|
||||||
{
|
if ( vrc7_enabled( this) )
|
||||||
if ( addr == 0x9010 )
|
|
||||||
{
|
{
|
||||||
Vrc7_write_reg( &this->vrc7, data );
|
if ( addr == 0x9010 )
|
||||||
return;
|
{
|
||||||
}
|
Vrc7_write_reg( &this->vrc7, data );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if ( (unsigned) (addr - 0x9028) <= 0x08 )
|
if ( (unsigned) (addr - 0x9028) <= 0x08 )
|
||||||
{
|
{
|
||||||
Vrc7_write_data( &this->vrc7, time, data );
|
Vrc7_write_data( &this->vrc7, Cpu_time( &this->cpu ), data );
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
#endif
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Unmapped_write
|
// 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 )
|
blargg_err_t Nsf_start_track( struct Nsf_Emu* this, int track )
|
||||||
{
|
{
|
||||||
clear_track_vars( this );
|
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 ( vrc6_enabled( this ) ) Vrc6_reset( &this->vrc6 );
|
||||||
if ( fme7_enabled( this ) ) Fme7_reset( &this->fme7 );
|
if ( fme7_enabled( this ) ) Fme7_reset( &this->fme7 );
|
||||||
if ( mmc5_enabled( this ) ) Apu_reset( &this->mmc5.apu, false, 0 );
|
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
|
#endif
|
||||||
|
|
||||||
int speed_flags = 0;
|
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 ) )
|
/* if ( this->cpu.r.pc < get_addr( header.load_addr ) )
|
||||||
warning( "Init address < load address" ); */
|
warning( "Init address < load address" ); */
|
||||||
|
|
||||||
this->emu_track_ended_ = false;
|
// convert filter times to samples
|
||||||
this->track_ended = false;
|
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 )
|
return track_start( &this->track_filter );
|
||||||
{
|
|
||||||
// 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void run_once( struct Nsf_Emu* this, nes_time_t end )
|
void run_once( struct Nsf_Emu* this, nes_time_t end )
|
||||||
|
|
@ -831,253 +809,87 @@ static void end_frame( struct Nsf_Emu* this, nes_time_t end )
|
||||||
if ( mmc5_enabled( this ) ) Apu_end_frame( &this->mmc5.apu, end );
|
if ( mmc5_enabled( this ) ) Apu_end_frame( &this->mmc5.apu, end );
|
||||||
if ( namco_enabled( this ) ) Namco_end_frame( &this->namco, end );
|
if ( namco_enabled( this ) ) Namco_end_frame( &this->namco, end );
|
||||||
if ( vrc6_enabled( this ) ) Vrc6_end_frame( &this->vrc6, 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 );
|
if ( vrc7_enabled( this ) ) Vrc7_end_frame( &this->vrc7, end );
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tell/Seek
|
// 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;
|
msec -= sec * 1000;
|
||||||
return (sec * sample_rate + msec * sample_rate / 1000) * stereo;
|
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;
|
int rate = this->sample_rate * stereo;
|
||||||
blargg_long sec = this->out_time / rate;
|
int sec = track_sample_count( &this->track_filter ) / rate;
|
||||||
return sec * 1000 + (this->out_time - sec * rate) * 1000 / 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 );
|
int time = msec_to_samples( msec, this->sample_rate );
|
||||||
if ( time < this->out_time )
|
if ( time < track_sample_count( &this->track_filter ) )
|
||||||
RETURN_ERR( Nsf_start_track( this, this->current_track ) );
|
RETURN_ERR( Nsf_start_track( this, this->current_track ) );
|
||||||
return Track_skip( this, time - this->out_time );
|
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, int count )
|
||||||
blargg_err_t Track_skip( struct Nsf_Emu* this, long count )
|
|
||||||
{
|
{
|
||||||
require( this->current_track >= 0 ); // start_track() must have been called already
|
require( this->current_track >= 0 ); // start_track() must have been called already
|
||||||
this->out_time += count;
|
return track_skip( &this->track_filter, 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
blargg_err_t play_( struct Nsf_Emu* this, long count, sample_t* out );
|
blargg_err_t skip_( void *emu, int count )
|
||||||
blargg_err_t skip_( struct Nsf_Emu* this, long count )
|
|
||||||
{
|
{
|
||||||
|
struct Nsf_Emu* this = (struct Nsf_Emu*) emu;
|
||||||
|
|
||||||
// for long skip, mute sound
|
// for long skip, mute sound
|
||||||
const long threshold = 30000;
|
const int threshold = 32768;
|
||||||
if ( count > threshold )
|
if ( count > threshold )
|
||||||
{
|
{
|
||||||
int saved_mute = this->mute_mask_;
|
int saved_mute = this->mute_mask_;
|
||||||
Sound_mute_voices( this, ~0 );
|
Sound_mute_voices( this, ~0 );
|
||||||
|
|
||||||
while ( count > threshold / 2 && !this->emu_track_ended_ )
|
int n = count - threshold/2;
|
||||||
{
|
n &= ~(2048-1); // round to multiple of 2048
|
||||||
RETURN_ERR( play_( this, buf_size, this->buf ) );
|
count -= n;
|
||||||
count -= buf_size;
|
RETURN_ERR( skippy_( &this->track_filter, n ) );
|
||||||
}
|
|
||||||
|
|
||||||
Sound_mute_voices( this, saved_mute );
|
Sound_mute_voices( this, saved_mute );
|
||||||
}
|
}
|
||||||
|
|
||||||
while ( count && !this->emu_track_ended_ )
|
return skippy_( &this->track_filter, count );
|
||||||
{
|
|
||||||
long n = buf_size;
|
|
||||||
if ( n > count )
|
|
||||||
n = count;
|
|
||||||
count -= n;
|
|
||||||
RETURN_ERR( play_( this, n, this->buf ) );
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fading
|
// Fading
|
||||||
|
|
||||||
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 )
|
||||||
{
|
{
|
||||||
this->fade_step = this->sample_rate * length_msec / (fade_block_size * fade_shift * 1000 / stereo);
|
track_set_fade( &this->track_filter, msec_to_samples( start_msec, this->sample_rate ),
|
||||||
this->fade_start = msec_to_samples( this->sample_rate, start_msec );
|
length_msec * this->sample_rate / (1000 / stereo) );
|
||||||
}
|
}
|
||||||
|
|
||||||
// unit / pow( 2.0, (double) x / step )
|
blargg_err_t Nsf_play( struct Nsf_Emu* this, int out_count, sample_t* out )
|
||||||
static int int_log( blargg_long x, int step, int unit )
|
|
||||||
{
|
{
|
||||||
int shift = x / step;
|
require( this->current_track >= 0 );
|
||||||
int fraction = (x - shift * step) * unit / step;
|
require( out_count % stereo == 0 );
|
||||||
return ((unit - fraction) + (fraction >> 1)) >> shift;
|
return track_play( &this->track_filter, out_count, out );
|
||||||
}
|
}
|
||||||
|
|
||||||
static void handle_fade( struct Nsf_Emu* this, long out_count, sample_t* out )
|
blargg_err_t play_( void* emu, int count, sample_t* out )
|
||||||
{
|
{
|
||||||
int i;
|
struct Nsf_Emu* this = (struct Nsf_Emu*) emu;
|
||||||
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 remain = count;
|
||||||
int count;
|
|
||||||
for ( count = min( fade_block_size, out_count - i ); count; --count )
|
|
||||||
{
|
|
||||||
*io = (sample_t) ((*io * gain) >> shift);
|
|
||||||
++io;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 );
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
// fill internal buffer and check it for silence
|
|
||||||
void fill_buf( struct Nsf_Emu* this )
|
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
blargg_err_t Nsf_play( struct Nsf_Emu* this, long 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
blargg_err_t play_( struct Nsf_Emu* this, long count, sample_t* out )
|
|
||||||
{
|
|
||||||
long remain = count;
|
|
||||||
while ( remain )
|
while ( remain )
|
||||||
{
|
{
|
||||||
|
Buffer_disable_immediate_removal( &this->stereo_buf );
|
||||||
remain -= Buffer_read_samples( &this->stereo_buf, &out [count - remain], remain );
|
remain -= Buffer_read_samples( &this->stereo_buf, &out [count - remain], remain );
|
||||||
if ( remain )
|
if ( remain )
|
||||||
{
|
{
|
||||||
|
|
@ -1089,7 +901,7 @@ blargg_err_t play_( struct Nsf_Emu* this, long count, sample_t* out )
|
||||||
Sound_mute_voices( this, this->mute_mask_ );
|
Sound_mute_voices( this, this->mute_mask_ );
|
||||||
}
|
}
|
||||||
int msec = Buffer_length( &this->stereo_buf );
|
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 ) );
|
RETURN_ERR( run_clocks( this, &clocks_emulated, msec ) );
|
||||||
assert( clocks_emulated );
|
assert( clocks_emulated );
|
||||||
Buffer_end_frame( &this->stereo_buf, clocks_emulated );
|
Buffer_end_frame( &this->stereo_buf, clocks_emulated );
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@
|
||||||
#include "nes_cpu.h"
|
#include "nes_cpu.h"
|
||||||
#include "nsfe_info.h"
|
#include "nsfe_info.h"
|
||||||
#include "m3u_playlist.h"
|
#include "m3u_playlist.h"
|
||||||
|
#include "track_filter.h"
|
||||||
|
|
||||||
#ifndef NSF_EMU_APU_ONLY
|
#ifndef NSF_EMU_APU_ONLY
|
||||||
#include "nes_namco_apu.h"
|
#include "nes_namco_apu.h"
|
||||||
|
|
@ -17,11 +18,11 @@
|
||||||
#include "nes_fme7_apu.h"
|
#include "nes_fme7_apu.h"
|
||||||
#include "nes_fds_apu.h"
|
#include "nes_fds_apu.h"
|
||||||
#include "nes_mmc5_apu.h"
|
#include "nes_mmc5_apu.h"
|
||||||
#include "nes_vrc7_apu.h"
|
#ifndef NSF_EMU_NO_VRC7
|
||||||
|
#include "nes_vrc7_apu.h"
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
typedef short sample_t;
|
|
||||||
|
|
||||||
// Sound chip flags
|
// Sound chip flags
|
||||||
enum {
|
enum {
|
||||||
vrc6_flag = 1 << 0,
|
vrc6_flag = 1 << 0,
|
||||||
|
|
@ -50,8 +51,7 @@ enum { fdsram_size = 0x6000 };
|
||||||
enum { fdsram_offset = 0x2000 + page_size + 8 };
|
enum { fdsram_offset = 0x2000 + page_size + 8 };
|
||||||
enum { sram_addr = 0x6000 };
|
enum { sram_addr = 0x6000 };
|
||||||
enum { unmapped_size= page_size + 8 };
|
enum { unmapped_size= page_size + 8 };
|
||||||
|
enum { max_voices = 32 };
|
||||||
enum { buf_size = 2048 };
|
|
||||||
|
|
||||||
// NSF file header
|
// NSF file header
|
||||||
enum { header_size = 0x80 };
|
enum { header_size = 0x80 };
|
||||||
|
|
@ -83,36 +83,20 @@ struct Nsf_Emu {
|
||||||
int play_delay;
|
int play_delay;
|
||||||
struct registers_t saved_state; // of interrupted init routine
|
struct registers_t saved_state; // of interrupted init routine
|
||||||
|
|
||||||
int track_count;
|
|
||||||
|
|
||||||
// general
|
// general
|
||||||
int max_initial_silence;
|
|
||||||
int voice_count;
|
int voice_count;
|
||||||
|
int voice_types [32];
|
||||||
int mute_mask_;
|
int mute_mask_;
|
||||||
int tempo;
|
int tempo;
|
||||||
int gain;
|
int gain;
|
||||||
|
|
||||||
long sample_rate;
|
int sample_rate;
|
||||||
|
|
||||||
// track-specific
|
// track-specific
|
||||||
|
int track_count;
|
||||||
int current_track;
|
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
|
int clock_rate__;
|
||||||
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__;
|
|
||||||
unsigned buf_changed_count;
|
unsigned buf_changed_count;
|
||||||
|
|
||||||
// M3u Playlist
|
// M3u Playlist
|
||||||
|
|
@ -127,7 +111,9 @@ struct Nsf_Emu {
|
||||||
struct Nes_Namco_Apu namco;
|
struct Nes_Namco_Apu namco;
|
||||||
struct Nes_Vrc6_Apu vrc6;
|
struct Nes_Vrc6_Apu vrc6;
|
||||||
struct Nes_Fme7_Apu fme7;
|
struct Nes_Fme7_Apu fme7;
|
||||||
struct Nes_Vrc7_Apu vrc7;
|
#ifndef NSF_EMU_NO_VRC7
|
||||||
|
struct Nes_Vrc7_Apu vrc7;
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
struct Nes_Cpu cpu;
|
struct Nes_Cpu cpu;
|
||||||
|
|
@ -136,13 +122,15 @@ struct Nsf_Emu {
|
||||||
// Header for currently loaded file
|
// Header for currently loaded file
|
||||||
struct header_t header;
|
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;
|
struct Rom_Data rom;
|
||||||
|
|
||||||
// Extended nsf info
|
// Extended nsf info
|
||||||
struct Nsfe_Info info;
|
struct Nsfe_Info info;
|
||||||
|
|
||||||
sample_t buf [buf_size];
|
|
||||||
byte high_ram[fdsram_size + fdsram_offset];
|
byte high_ram[fdsram_size + fdsram_offset];
|
||||||
byte low_ram [low_ram_size];
|
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)
|
// Basic functionality (see Gme_File.h for file loading/track info functions)
|
||||||
|
|
||||||
void Nsf_init( struct Nsf_Emu* this );
|
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 );
|
blargg_err_t Nsf_post_load( struct Nsf_Emu* this );
|
||||||
|
|
||||||
// Set output sample rate. Must be called only once before loading file.
|
// 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.
|
// Start a track, where 0 is the first track. Also clears warning string.
|
||||||
blargg_err_t Nsf_start_track( struct Nsf_Emu* this , int );
|
blargg_err_t Nsf_start_track( struct Nsf_Emu* this , int );
|
||||||
|
|
||||||
// Generate 'count' samples info 'buf'. Output is in stereo. Any emulation
|
// Generate 'count' samples info 'buf'. Output is in stereo. Any emulation
|
||||||
// errors set warning string, and major errors also end track.
|
// 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_clear_playlist( struct Nsf_Emu* this );
|
||||||
void Nsf_disable_playlist( struct Nsf_Emu* this, bool b ); // use clear_playlist()
|
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
|
// Track status/control
|
||||||
|
|
||||||
// Number of milliseconds (1000 msec = 1 second) played since beginning of track
|
// 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.
|
// 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
|
// 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
|
// 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.
|
// 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
|
// 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
|
// Sound customization
|
||||||
|
|
||||||
|
|
@ -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]; }
|
static inline byte* unmapped_code( struct Nsf_Emu* this ) { return &this->high_ram [sram_size]; }
|
||||||
|
|
||||||
#ifndef NSF_EMU_APU_ONLY
|
#ifndef NSF_EMU_APU_ONLY
|
||||||
static inline int fds_enabled( struct Nsf_Emu* this ) { return this->header.chip_flags & fds_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; }
|
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; }
|
#ifndef NSF_EMU_NO_VRC7
|
||||||
static inline int mmc5_enabled( struct Nsf_Emu* this ) { return this->header.chip_flags & mmc5_flag; }
|
static inline int vrc7_enabled( struct Nsf_Emu* this ) { return this->header.chip_flags & vrc7_flag; }
|
||||||
static inline int namco_enabled( struct Nsf_Emu* this ) { return this->header.chip_flags & namco_flag; }
|
#endif
|
||||||
static inline int fme7_enabled( struct Nsf_Emu* this ) { return this->header.chip_flags & fme7_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; }
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -154,8 +154,8 @@ blargg_err_t Info_load( struct Nsfe_Info* this, void* data, long size, struct Ns
|
||||||
byte block_header [2] [4];
|
byte block_header [2] [4];
|
||||||
RETURN_ERR( in_read( block_header, sizeof block_header, data, &offset, size ) );
|
RETURN_ERR( in_read( block_header, sizeof block_header, data, &offset, size ) );
|
||||||
|
|
||||||
blargg_long chunk_size = get_le32( block_header [0] );
|
int chunk_size = get_le32( block_header [0] );
|
||||||
blargg_long tag = get_le32( block_header [1] );
|
int tag = get_le32( block_header [1] );
|
||||||
|
|
||||||
switch ( tag )
|
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.track_count = 1;
|
||||||
finfo.first_track = 0;
|
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 ) );
|
(char*) data, &offset, size ) );
|
||||||
|
|
||||||
if ( chunk_size > nsfe_info_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;
|
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) ) {
|
if ( (this->m3u.size > 0) && (n < this->m3u.size) ) {
|
||||||
struct entry_t* entry = &this->m3u.entries [n];
|
struct entry_t* entry = &this->m3u.entries [n];
|
||||||
length = entry->length;
|
length = entry->length;
|
||||||
|
|
|
||||||
|
|
@ -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 );
|
int const bass = BLIP_READER_BASS( *blip_buf );
|
||||||
BLIP_READER_BEGIN( sn, *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;
|
int count = this->sample_buf_size >> 1;
|
||||||
BLIP_READER_ADJ_( sn, count );
|
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* BLARGG_RESTRICT out = (stereo_dsample_t*) out_ + count;
|
||||||
stereo_dsample_t const* BLARGG_RESTRICT in =
|
stereo_dsample_t const* BLARGG_RESTRICT in =
|
||||||
(stereo_dsample_t const*) this->sample_buf + count;
|
(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 );
|
BLIP_READER_END( sn, *blip_buf );
|
||||||
}
|
}
|
||||||
|
|
||||||
sample_t const* resample_( struct Resampler* this, sample_t** out_,
|
dsample_t const* resample_( struct Resampler* this, dsample_t** out_,
|
||||||
sample_t const* out_end, sample_t const in [], int in_size )
|
dsample_t const* out_end, dsample_t const in [], int in_size )
|
||||||
{
|
{
|
||||||
in_size -= write_offset;
|
in_size -= write_offset;
|
||||||
if ( in_size > 0 )
|
if ( in_size > 0 )
|
||||||
{
|
{
|
||||||
sample_t* BLARGG_RESTRICT out = *out_;
|
dsample_t* BLARGG_RESTRICT out = *out_;
|
||||||
sample_t const* const in_end = in + in_size;
|
dsample_t const* const in_end = in + in_size;
|
||||||
|
|
||||||
int const step = this->step;
|
int const step = this->step;
|
||||||
int pos = this->pos;
|
int pos = this->pos;
|
||||||
|
|
@ -135,12 +135,12 @@ sample_t const* resample_( struct Resampler* this, sample_t** out_,
|
||||||
return in;
|
return in;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int resample_wrapper( struct Resampler* this, sample_t out [], int* out_size,
|
static inline int resample_wrapper( struct Resampler* this, dsample_t out [], int* out_size,
|
||||||
sample_t const in [], int in_size )
|
dsample_t const in [], int in_size )
|
||||||
{
|
{
|
||||||
assert( Resampler_rate( this ) );
|
assert( Resampler_rate( this ) );
|
||||||
|
|
||||||
sample_t* out_ = out;
|
dsample_t* out_ = out;
|
||||||
int result = resample_( this, &out_, out + *out_size, in, in_size ) - in;
|
int result = resample_( this, &out_, out + *out_size, in, in_size ) - in;
|
||||||
assert( out_ <= out + *out_size );
|
assert( out_ <= out + *out_size );
|
||||||
assert( result <= in_size );
|
assert( result <= in_size );
|
||||||
|
|
@ -161,7 +161,7 @@ int skip_input( struct Resampler* this, int count )
|
||||||
return 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;
|
int pair_count = this->sample_buf_size >> 1;
|
||||||
blip_time_t blip_time = Blip_count_clocks( blip_buf, pair_count );
|
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 );
|
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
|
// empty extra buffer
|
||||||
int remain = this->sample_buf_size - this->buf_pos;
|
int remain = this->sample_buf_size - this->buf_pos;
|
||||||
|
|
|
||||||
|
|
@ -7,16 +7,15 @@
|
||||||
#include "blargg_common.h"
|
#include "blargg_common.h"
|
||||||
#include "multi_buffer.h"
|
#include "multi_buffer.h"
|
||||||
|
|
||||||
typedef short sample_t;
|
typedef short dsample_t;
|
||||||
|
|
||||||
enum { stereo = 2 };
|
|
||||||
enum { max_buf_size = 3960 };
|
enum { max_buf_size = 3960 };
|
||||||
enum { max_resampler_size = 5942 };
|
enum { max_resampler_size = 5942 };
|
||||||
enum { write_offset = 8 * stereo };
|
enum { write_offset = 8 * stereo };
|
||||||
enum { gain_bits = 14 };
|
enum { gain_bits = 14 };
|
||||||
|
|
||||||
struct Resampler {
|
struct Resampler {
|
||||||
int (*callback)( void*, blip_time_t, int, sample_t* );
|
int (*callback)( void*, blip_time_t, int, dsample_t* );
|
||||||
void* callback_data;
|
void* callback_data;
|
||||||
|
|
||||||
int sample_buffer_size;
|
int sample_buffer_size;
|
||||||
|
|
@ -34,8 +33,8 @@ struct Resampler {
|
||||||
|
|
||||||
int rate_;
|
int rate_;
|
||||||
|
|
||||||
sample_t sample_buf [max_buf_size];
|
dsample_t sample_buf [max_buf_size];
|
||||||
sample_t buf [max_resampler_size]; // Internal resampler
|
dsample_t buf [max_resampler_size]; // Internal resampler
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline void Resampler_init( struct Resampler* this )
|
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 );
|
blargg_err_t Resampler_reset( struct Resampler* this, int max_pairs );
|
||||||
void Resampler_resize( struct Resampler* this, int pairs_per_frame );
|
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 = func;
|
||||||
this->callback_data = user_data;
|
this->callback_data = user_data;
|
||||||
|
|
|
||||||
|
|
@ -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,
|
blargg_err_t Rom_load( struct Rom_Data* this, const void* data, long size,
|
||||||
int header_size, void* header_out, int fill )
|
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->rom_addr = 0;
|
||||||
this->mask = 0;
|
this->mask = 0;
|
||||||
|
|
@ -43,11 +43,11 @@ blargg_err_t Rom_load( struct Rom_Data* this, const void* data, long size,
|
||||||
return 0;
|
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;
|
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 )
|
if ( rounded <= 0 )
|
||||||
{
|
{
|
||||||
rounded = 0;
|
rounded = 0;
|
||||||
|
|
@ -55,7 +55,7 @@ void Rom_set_addr( struct Rom_Data* this, long addr )
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
int shift = 0;
|
int shift = 0;
|
||||||
unsigned long max_addr = (unsigned long) (rounded - 1);
|
unsigned int max_addr = (unsigned int) (rounded - 1);
|
||||||
while ( max_addr >> shift )
|
while ( max_addr >> shift )
|
||||||
shift++;
|
shift++;
|
||||||
this->mask = (1L << shift) - 1;
|
this->mask = (1L << shift) - 1;
|
||||||
|
|
|
||||||
|
|
@ -20,22 +20,22 @@ enum { max_rom_size = 2 * max_pad_size };
|
||||||
|
|
||||||
struct Rom_Data {
|
struct Rom_Data {
|
||||||
byte* file_data;
|
byte* file_data;
|
||||||
blargg_ulong file_size;
|
unsigned file_size;
|
||||||
|
|
||||||
blargg_long rom_addr;
|
int rom_addr;
|
||||||
blargg_long bank_size;
|
int bank_size;
|
||||||
blargg_long rom_size;
|
int rom_size;
|
||||||
blargg_ulong pad_size;
|
unsigned pad_size;
|
||||||
blargg_long mask;
|
int mask;
|
||||||
blargg_long size; // TODO: eliminate
|
int size; // TODO: eliminate
|
||||||
blargg_long rsize_;
|
int rsize_;
|
||||||
|
|
||||||
// Unmapped space
|
// Unmapped space
|
||||||
byte unmapped [max_rom_size];
|
byte unmapped [max_rom_size];
|
||||||
};
|
};
|
||||||
|
|
||||||
// Initialize rom
|
// 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->bank_size = bank_size;
|
||||||
this->pad_size = this->bank_size + pad_extra;
|
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 );
|
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
|
// 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()
|
// 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
|
#ifdef check
|
||||||
check( addr <= mask );
|
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.
|
// 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;
|
unsigned offset = mask_addr( addr, this->mask ) - this->rom_addr;
|
||||||
if ( offset > (blargg_ulong) (this->rsize_ - this->pad_size) )
|
if ( offset > (unsigned) (this->rsize_ - this->pad_size) )
|
||||||
offset = 0; // unmapped
|
offset = 0; // unmapped
|
||||||
|
|
||||||
if ( offset < this->pad_size ) return &this->unmapped [offset];
|
if ( offset < this->pad_size ) return &this->unmapped [offset];
|
||||||
|
|
|
||||||
|
|
@ -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
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
|
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
|
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 */
|
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
|
||||||
|
|
||||||
#include "blargg_source.h"
|
#include "blargg_source.h"
|
||||||
|
|
||||||
int const osc_count = sms_osc_count + fm_apu_osc_count;
|
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";
|
const char gme_wrong_file_type [] = "Wrong file type for this emulator";
|
||||||
|
|
||||||
static void clear_track_vars( struct Sgc_Emu* this )
|
static void clear_track_vars( struct Sgc_Emu* this )
|
||||||
{
|
{
|
||||||
this->current_track = -1;
|
this->current_track = -1;
|
||||||
this->out_time = 0;
|
track_stop( &this->track_filter );
|
||||||
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 */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Sgc_init( struct Sgc_Emu* this )
|
void Sgc_init( struct Sgc_Emu* this )
|
||||||
|
|
@ -48,12 +33,12 @@ void Sgc_init( struct Sgc_Emu* this )
|
||||||
this->mute_mask_ = 0;
|
this->mute_mask_ = 0;
|
||||||
this->tempo = (int)FP_ONE_TEMPO;
|
this->tempo = (int)FP_ONE_TEMPO;
|
||||||
this->gain = (int)FP_ONE_GAIN;
|
this->gain = (int)FP_ONE_GAIN;
|
||||||
this->voice_count = 0;
|
|
||||||
|
|
||||||
// defaults
|
// defaults
|
||||||
this->max_initial_silence = 2;
|
this->tfilter = *track_get_setup( &this->track_filter );
|
||||||
this->silence_lookahead = 6;
|
this->tfilter.max_initial = 2;
|
||||||
this->ignore_silence = false;
|
this->tfilter.lookahead = 6;
|
||||||
|
this->track_filter.silence_ignored_ = false;
|
||||||
|
|
||||||
Sms_apu_init( &this->apu );
|
Sms_apu_init( &this->apu );
|
||||||
Fm_apu_create( &this->fm_apu );
|
Fm_apu_create( &this->fm_apu );
|
||||||
|
|
@ -64,6 +49,8 @@ void Sgc_init( struct Sgc_Emu* this )
|
||||||
Sound_set_gain( this, (int)(FP_ONE_GAIN*1.2) );
|
Sound_set_gain( this, (int)(FP_ONE_GAIN*1.2) );
|
||||||
|
|
||||||
// Unload
|
// Unload
|
||||||
|
this->voice_count = 0;
|
||||||
|
this->voice_types = 0;
|
||||||
clear_track_vars( this );
|
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->m3u.size = 0;
|
||||||
this->track_count = this->header.song_count;
|
this->track_count = this->header.song_count;
|
||||||
this->voice_count = sega_mapping( this ) ? osc_count : sms_osc_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 );
|
Sms_apu_volume( &this->apu, this->gain );
|
||||||
Fm_apu_volume( &this->fm_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
|
// Setup buffer
|
||||||
this->clock_rate_ = clock_rate( this );
|
this->clock_rate_ = clock_rate( this );
|
||||||
Buffer_clock_rate( &this->stereo_buf, 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 );
|
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_ );
|
Sound_mute_voices( this, this->mute_mask_ );
|
||||||
return 0;
|
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
|
require( !this->sample_rate ); // sample rate can't be changed once set
|
||||||
Buffer_init( &this->stereo_buf );
|
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 );
|
Buffer_bass_freq( &this->stereo_buf, 80 );
|
||||||
|
|
||||||
this->sample_rate = rate;
|
this->sample_rate = rate;
|
||||||
|
RETURN_ERR( track_init( &this->track_filter, this ) );
|
||||||
|
this->tfilter.max_silence = 6 * stereo * this->sample_rate;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -286,7 +279,7 @@ void Sound_mute_voices( struct Sgc_Emu* this, int mask )
|
||||||
}
|
}
|
||||||
else
|
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) ||
|
assert( (ch.center && ch.left && ch.right) ||
|
||||||
(!ch.center && !ch.left && !ch.right) ); // all or nothing
|
(!ch.center && !ch.left && !ch.right) ); // all or nothing
|
||||||
Sound_set_voice( this, i, ch.center, ch.left, ch.right );
|
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);
|
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 )
|
blargg_err_t Sgc_start_track( struct Sgc_Emu* this, int track )
|
||||||
{
|
{
|
||||||
clear_track_vars( this );
|
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 );
|
Buffer_clear( &this->stereo_buf );
|
||||||
|
|
||||||
this->emu_track_ended_ = false;
|
// convert filter times to samples
|
||||||
this->track_ended = false;
|
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 )
|
return track_start( &this->track_filter );
|
||||||
{
|
|
||||||
// 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tell/Seek
|
// 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;
|
msec -= sec * 1000;
|
||||||
return (sec * sample_rate + msec * sample_rate / 1000) * stereo;
|
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;
|
int rate = this->sample_rate * stereo;
|
||||||
blargg_long sec = this->out_time / rate;
|
int sec = track_sample_count( &this->track_filter ) / rate;
|
||||||
return sec * 1000 + (this->out_time - sec * rate) * 1000 / 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 );
|
int time = msec_to_samples( msec, this->sample_rate );
|
||||||
if ( time < this->out_time )
|
if ( time < track_sample_count( &this->track_filter ) )
|
||||||
RETURN_ERR( Sgc_start_track( this, this->current_track ) );
|
RETURN_ERR( Sgc_start_track( this, this->current_track ) );
|
||||||
return Track_skip( this, time - this->out_time );
|
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, int count )
|
||||||
blargg_err_t Track_skip( struct Sgc_Emu* this, long count )
|
|
||||||
{
|
{
|
||||||
require( this->current_track >= 0 ); // start_track() must have been called already
|
require( this->current_track >= 0 ); // start_track() must have been called already
|
||||||
this->out_time += count;
|
return track_skip( &this->track_filter, 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
blargg_err_t play_( struct Sgc_Emu* this, long count, sample_t* out );
|
blargg_err_t skip_( void* emu, int count )
|
||||||
blargg_err_t skip_( struct Sgc_Emu* this, long count )
|
|
||||||
{
|
{
|
||||||
|
struct Sgc_Emu* this = (struct Sgc_Emu*) emu;
|
||||||
|
|
||||||
// for long skip, mute sound
|
// for long skip, mute sound
|
||||||
const long threshold = 30000;
|
const int threshold = 32768;
|
||||||
if ( count > threshold )
|
if ( count > threshold )
|
||||||
{
|
{
|
||||||
int saved_mute = this->mute_mask_;
|
int saved_mute = this->mute_mask_;
|
||||||
Sound_mute_voices( this, ~0 );
|
Sound_mute_voices( this, ~0 );
|
||||||
|
|
||||||
while ( count > threshold / 2 && !this->emu_track_ended_ )
|
int n = count - threshold/2;
|
||||||
{
|
n &= ~(2048-1); // round to multiple of 2048
|
||||||
RETURN_ERR( play_( this, buf_size, this->buf ) );
|
count -= n;
|
||||||
count -= buf_size;
|
RETURN_ERR( skippy_( &this->track_filter, n ) );
|
||||||
}
|
|
||||||
|
|
||||||
Sound_mute_voices( this, saved_mute );
|
Sound_mute_voices( this, saved_mute );
|
||||||
}
|
}
|
||||||
|
|
||||||
while ( count && !this->emu_track_ended_ )
|
return skippy_( &this->track_filter, count );
|
||||||
{
|
|
||||||
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, int start_msec, int length_msec )
|
||||||
|
|
||||||
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);
|
track_set_fade( &this->track_filter, msec_to_samples( start_msec, this->sample_rate ),
|
||||||
this->fade_start = msec_to_samples( start_msec, this->sample_rate );
|
length_msec * this->sample_rate / (1000 / stereo) );
|
||||||
}
|
}
|
||||||
|
|
||||||
// unit / pow( 2.0, (double) x / step )
|
blargg_err_t Sgc_play( struct Sgc_Emu* this, int out_count, sample_t* out )
|
||||||
static int int_log( blargg_long x, int step, int unit )
|
|
||||||
{
|
{
|
||||||
int shift = x / step;
|
require( this->current_track >= 0 );
|
||||||
int fraction = (x - shift * step) * unit / step;
|
require( out_count % stereo == 0 );
|
||||||
return ((unit - fraction) + (fraction >> 1)) >> shift;
|
return track_play( &this->track_filter, out_count, out );
|
||||||
}
|
}
|
||||||
|
|
||||||
static void handle_fade( struct Sgc_Emu* this, long out_count, sample_t* out )
|
blargg_err_t play_( void* emu, int count, sample_t out [] )
|
||||||
{
|
{
|
||||||
int i;
|
struct Sgc_Emu* this = (struct Sgc_Emu*) emu;
|
||||||
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 remain = count;
|
||||||
int count;
|
|
||||||
for ( count = min( fade_block_size, out_count - i ); count; --count )
|
|
||||||
{
|
|
||||||
*io = (sample_t) ((*io * gain) >> shift);
|
|
||||||
++io;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Silence detection
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
// fill internal buffer and check it for silence
|
|
||||||
void fill_buf( struct Sgc_Emu* this )
|
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
blargg_err_t Sgc_play( struct Sgc_Emu* this, long 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
blargg_err_t play_( struct Sgc_Emu* this, long count, sample_t* out )
|
|
||||||
{
|
|
||||||
long remain = count;
|
|
||||||
while ( remain )
|
while ( remain )
|
||||||
{
|
{
|
||||||
|
Buffer_disable_immediate_removal( &this->stereo_buf );
|
||||||
remain -= Buffer_read_samples( &this->stereo_buf, &out [count - remain], remain );
|
remain -= Buffer_read_samples( &this->stereo_buf, &out [count - remain], remain );
|
||||||
if ( remain )
|
if ( remain )
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -12,12 +12,10 @@
|
||||||
#include "sms_fm_apu.h"
|
#include "sms_fm_apu.h"
|
||||||
#include "sms_apu.h"
|
#include "sms_apu.h"
|
||||||
#include "m3u_playlist.h"
|
#include "m3u_playlist.h"
|
||||||
|
#include "track_filter.h"
|
||||||
|
|
||||||
typedef short sample_t;
|
|
||||||
typedef struct Z80_Cpu Sgc_Cpu;
|
typedef struct Z80_Cpu Sgc_Cpu;
|
||||||
|
|
||||||
enum { buf_size = 2048 };
|
|
||||||
|
|
||||||
// SGC file header
|
// SGC file header
|
||||||
enum { header_size = 0xA0 };
|
enum { header_size = 0xA0 };
|
||||||
struct header_t
|
struct header_t
|
||||||
|
|
@ -52,7 +50,6 @@ static inline bool valid_tag( struct header_t* h )
|
||||||
|
|
||||||
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;
|
bool fm_accessed;
|
||||||
|
|
||||||
|
|
@ -65,40 +62,28 @@ struct Sgc_Emu {
|
||||||
|
|
||||||
// general
|
// general
|
||||||
int voice_count;
|
int voice_count;
|
||||||
|
int const* voice_types;
|
||||||
int mute_mask_;
|
int mute_mask_;
|
||||||
int tempo;
|
int tempo;
|
||||||
int gain;
|
int gain;
|
||||||
|
|
||||||
long sample_rate;
|
int sample_rate;
|
||||||
|
|
||||||
// track-specific
|
// track-specific
|
||||||
volatile bool track_ended;
|
|
||||||
int current_track;
|
int current_track;
|
||||||
int track_count;
|
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
|
int clock_rate_;
|
||||||
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_;
|
|
||||||
unsigned buf_changed_count;
|
unsigned buf_changed_count;
|
||||||
|
|
||||||
// M3u Playlist
|
// M3u Playlist
|
||||||
struct M3u_Playlist m3u;
|
struct M3u_Playlist m3u;
|
||||||
|
struct header_t header;
|
||||||
|
|
||||||
sample_t buf [buf_size];
|
struct setup_t tfilter;
|
||||||
struct Stereo_Buffer stereo_buf;
|
struct Track_Filter track_filter;
|
||||||
|
|
||||||
|
struct Multi_Buffer stereo_buf;
|
||||||
|
|
||||||
struct Sms_Apu apu;
|
struct Sms_Apu apu;
|
||||||
struct Sms_Fm_Apu fm_apu;
|
struct Sms_Fm_Apu fm_apu;
|
||||||
|
|
@ -106,12 +91,11 @@ struct Sgc_Emu {
|
||||||
Sgc_Cpu cpu;
|
Sgc_Cpu cpu;
|
||||||
|
|
||||||
// large items
|
// large items
|
||||||
struct header_t header;
|
|
||||||
struct Rom_Data rom;
|
struct Rom_Data rom;
|
||||||
byte vectors [page_size + page_padding];
|
byte vectors [page_size + page_padding];
|
||||||
|
byte unmapped_write [0x4000];
|
||||||
byte ram [0x2000 + page_padding];
|
byte ram [0x2000 + page_padding];
|
||||||
byte ram2 [0x4000 + page_padding];
|
byte ram2 [0x4000 + page_padding];
|
||||||
byte unmapped_write [0x4000];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Basic functionality (see Gme_File.h for file loading/track info functions)
|
// Basic functionality (see Gme_File.h for file loading/track info functions)
|
||||||
|
|
@ -126,34 +110,46 @@ static inline int clock_rate( struct Sgc_Emu* this ) { return this->header.rate
|
||||||
static inline void set_coleco_bios( struct Sgc_Emu* this, void* p ) { this->coleco_bios = p; }
|
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.
|
// 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.
|
// 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 );
|
blargg_err_t Sgc_start_track( struct Sgc_Emu* this, int track );
|
||||||
|
|
||||||
// Generate 'count' samples info 'buf'. Output is in stereo. Any emulation
|
// Generate 'count' samples info 'buf'. Output is in stereo. Any emulation
|
||||||
// errors set warning string, and major errors also end track.
|
// 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
|
// Track status/control
|
||||||
|
|
||||||
// Number of milliseconds (1000 msec = 1 second) played since beginning of track
|
// 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.
|
// 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
|
// 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
|
// 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.
|
// 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
|
// 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) ) {
|
if ( (this->m3u.size > 0) && (n < this->m3u.size) ) {
|
||||||
struct entry_t* entry = &this->m3u.entries [n];
|
struct entry_t* entry = &this->m3u.entries [n];
|
||||||
length = entry->length;
|
length = entry->length;
|
||||||
|
|
|
||||||
|
|
@ -158,9 +158,7 @@ static void run_until( struct Sms_Apu* this, blip_time_t end_time )
|
||||||
if ( delta )
|
if ( delta )
|
||||||
{
|
{
|
||||||
osc->last_amp = amp;
|
osc->last_amp = amp;
|
||||||
/* norm_synth.offset( last_time, delta, out ); */
|
|
||||||
Synth_offset( &this->synth, this->last_time, delta, out );
|
Synth_offset( &this->synth, this->last_time, delta, out );
|
||||||
/* out->set_modified(); */
|
|
||||||
Blip_set_modified( out );
|
Blip_set_modified( out );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -200,7 +198,6 @@ static void run_until( struct Sms_Apu* this, blip_time_t end_time )
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
delta = -delta;
|
delta = -delta;
|
||||||
/* norm_synth.offset( time, delta, out ); */
|
|
||||||
Synth_offset( &this->synth, time, delta, out );
|
Synth_offset( &this->synth, time, delta, out );
|
||||||
time += period;
|
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
|
if ( changed & 2 ) // true if bits 0 and 1 differ
|
||||||
{
|
{
|
||||||
delta = -delta;
|
delta = -delta;
|
||||||
/* fast_synth.offset_inline( time, delta, out ); */
|
|
||||||
Synth_offset_inline( &this->synth, time, delta, out );
|
Synth_offset_inline( &this->synth, time, delta, out );
|
||||||
}
|
}
|
||||||
time += period;
|
time += period;
|
||||||
|
|
|
||||||
294
apps/codecs/libgme/track_filter.c
Normal file
294
apps/codecs/libgme/track_filter.c
Normal file
|
|
@ -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;
|
||||||
|
}
|
||||||
90
apps/codecs/libgme/track_filter.h
Normal file
90
apps/codecs/libgme/track_filter.h
Normal file
|
|
@ -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
|
||||||
|
|
@ -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 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)
|
// VGM commands (Spec v1.50)
|
||||||
enum {
|
enum {
|
||||||
cmd_gg_stereo = 0x4F,
|
cmd_gg_stereo = 0x4F,
|
||||||
|
|
@ -53,15 +48,8 @@ enum {
|
||||||
|
|
||||||
static void clear_track_vars( struct Vgm_Emu* this )
|
static void clear_track_vars( struct Vgm_Emu* this )
|
||||||
{
|
{
|
||||||
this->out_time = 0;
|
this->current_track = -1;
|
||||||
this->emu_time = 0;
|
track_stop( &this->track_filter );
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int play_frame( struct Vgm_Emu* this, blip_time_t blip_time, int sample_count, sample_t* buf );
|
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);
|
this->tempo = (int)(FP_ONE_TEMPO);
|
||||||
|
|
||||||
// defaults
|
// defaults
|
||||||
this->max_initial_silence = 2;
|
this->tfilter = *track_get_setup( &this->track_filter );
|
||||||
this->silence_lookahead = 1; // tracks should already be trimmed
|
this->tfilter.max_initial = 2;
|
||||||
this->ignore_silence = false;
|
this->tfilter.lookahead = 1;
|
||||||
|
this->track_filter.silence_ignored_ = false;
|
||||||
|
|
||||||
// Disable oversampling by default
|
// Disable oversampling by default
|
||||||
this->disable_oversampling = true;
|
this->disable_oversampling = true;
|
||||||
|
|
@ -88,7 +77,7 @@ void Vgm_init( struct Vgm_Emu* this )
|
||||||
Sms_apu_init( &this->psg );
|
Sms_apu_init( &this->psg );
|
||||||
Synth_init( &this->pcm );
|
Synth_init( &this->pcm );
|
||||||
|
|
||||||
Buffer_init( &this->buf );
|
Buffer_init( &this->stereo_buf );
|
||||||
Blip_init( &this->blip_buf );
|
Blip_init( &this->blip_buf );
|
||||||
|
|
||||||
// Init fm chips
|
// Init fm chips
|
||||||
|
|
@ -105,6 +94,7 @@ void Vgm_init( struct Vgm_Emu* this )
|
||||||
|
|
||||||
// Unload
|
// Unload
|
||||||
this->voice_count = 0;
|
this->voice_count = 0;
|
||||||
|
this->voice_types = 0;
|
||||||
clear_track_vars( this );
|
clear_track_vars( this );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -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;
|
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 ( remain < gd3_header_size ) return 0;
|
||||||
if ( memcmp( h, "Gd3 ", 4 ) ) return 0;
|
if ( memcmp( h, "Gd3 ", 4 ) ) return 0;
|
||||||
if ( get_le32( h + 4 ) >= 0x200 ) 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 )
|
if ( gd3_size > remain - gd3_header_size )
|
||||||
gd3_size = remain - gd3_header_size;
|
gd3_size = remain - gd3_header_size;
|
||||||
return gd3_size;
|
return gd3_size;
|
||||||
|
|
@ -167,12 +157,12 @@ static byte const* gd3_data( struct Vgm_Emu* this, int* size )
|
||||||
if ( size )
|
if ( size )
|
||||||
*size = 0;
|
*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 )
|
if ( gd3_offset < 0 )
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
byte const* gd3 = this->file_begin + header_size + gd3_offset;
|
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 )
|
if ( !gd3_size )
|
||||||
return 0;
|
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 )
|
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 )
|
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 ) )
|
if ( loop > 0 && get_le32( h->loop_offset ) )
|
||||||
{
|
{
|
||||||
out->loop_length = loop * 10 / 441;
|
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 );
|
Ym2612_enable( &this->ym2612, false );
|
||||||
Ym2413_enable( &this->ym2413, false );
|
Ym2413_enable( &this->ym2413, false );
|
||||||
|
|
||||||
Sound_set_tempo( this, (int)(FP_ONE_TEMPO) );
|
|
||||||
|
|
||||||
this->voice_count = sms_osc_count;
|
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 ) );
|
RETURN_ERR( setup_fm( this ) );
|
||||||
|
|
||||||
// do after FM in case output buffer is changed
|
// do after FM in case output buffer is changed
|
||||||
// setup buffer
|
// setup buffer
|
||||||
this->clock_rate_ = this->psg_rate;
|
this->clock_rate_ = this->psg_rate;
|
||||||
Buffer_clock_rate( &this->buf, this->psg_rate );
|
Buffer_clock_rate( &this->stereo_buf, this->psg_rate );
|
||||||
|
RETURN_ERR( Buffer_set_channel_count( &this->stereo_buf, this->voice_count, this->voice_types ) );
|
||||||
// Setup bass
|
this->buf_changed_count = Buffer_channels_changed_count( &this->stereo_buf );
|
||||||
this->buf_changed_count = Buffer_channels_changed_count( &this->buf );
|
|
||||||
|
|
||||||
// Post load
|
// Post load
|
||||||
Sound_set_tempo( this, this->tempo );
|
Sound_set_tempo( this, this->tempo );
|
||||||
Sound_mute_voices( this, this->mute_mask_ );
|
Sound_mute_voices( this, this->mute_mask_ );
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -362,36 +353,50 @@ blargg_err_t setup_fm( struct Vgm_Emu* this )
|
||||||
// Emulation
|
// Emulation
|
||||||
|
|
||||||
blip_time_t run( struct Vgm_Emu* this, vgm_time_t end_time );
|
blip_time_t run( struct Vgm_Emu* this, vgm_time_t end_time );
|
||||||
|
static blip_time_t run_psg( struct Vgm_Emu* this, int msec )
|
||||||
|
{
|
||||||
|
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 )
|
static blargg_err_t run_clocks( struct Vgm_Emu* this, blip_time_t* time_io, int msec )
|
||||||
{
|
{
|
||||||
*time_io = run( this, msec * this->vgm_rate / 1000 );
|
check_end( this );
|
||||||
Sms_apu_end_frame( &this->psg, *time_io );
|
*time_io = run_psg( this, msec );
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
blargg_err_t play_( void *emu, int count, sample_t out [] )
|
||||||
|
|
||||||
static blargg_err_t play_( struct Vgm_Emu* this, long count, sample_t* out )
|
|
||||||
{
|
{
|
||||||
|
struct Vgm_Emu* this = (struct Vgm_Emu*) emu;
|
||||||
|
|
||||||
if ( !uses_fm( this ) ) {
|
if ( !uses_fm( this ) ) {
|
||||||
long remain = count;
|
int remain = count;
|
||||||
while ( remain )
|
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 ( 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
|
// Remute voices
|
||||||
Sound_mute_voices( this, this->mute_mask_ );
|
Sound_mute_voices( this, this->mute_mask_ );
|
||||||
}
|
}
|
||||||
int msec = Buffer_length( &this->buf );
|
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 ) );
|
RETURN_ERR( run_clocks( this, &clocks_emulated, msec ) );
|
||||||
assert( clocks_emulated );
|
assert( clocks_emulated );
|
||||||
Buffer_end_frame( &this->buf, clocks_emulated );
|
Buffer_end_frame( &this->stereo_buf, clocks_emulated );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -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;
|
vgm_time_t vgm_time = this->vgm_time;
|
||||||
byte const* pos = this->pos;
|
byte const* pos = this->pos;
|
||||||
if ( pos >= this->file_end )
|
/* if ( pos > this->file_end )
|
||||||
{
|
{
|
||||||
this->emu_track_ended_ = true;
|
warning( "Stream lacked end event" );
|
||||||
/* if ( pos > data_end )
|
} */
|
||||||
warning( "Stream lacked end event" ); */
|
|
||||||
}
|
|
||||||
|
|
||||||
while ( vgm_time < end_time && pos < this->file_end )
|
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: {
|
case cmd_data_block: {
|
||||||
check( *pos == cmd_end );
|
check( *pos == cmd_end );
|
||||||
int type = pos [1];
|
int type = pos [1];
|
||||||
long size = get_le32( pos + 2 );
|
int size = get_le32( pos + 2 );
|
||||||
pos += 6;
|
pos += 6;
|
||||||
if ( type == pcm_block_type )
|
if ( type == pcm_block_type )
|
||||||
this->pcm_data = pos;
|
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 [] )
|
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
|
// to do: timing is working mostly by luck
|
||||||
int min_pairs = (unsigned) sample_count / 2;
|
int min_pairs = (unsigned) sample_count / 2;
|
||||||
int vgm_time = (min_pairs << fm_time_bits) / this->fm_time_factor - 1;
|
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
|
// 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
|
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( 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
|
// Set bass frequency
|
||||||
Buffer_bass_freq( &this->buf, 80 );
|
Buffer_bass_freq( &this->stereo_buf, 80 );
|
||||||
|
|
||||||
this->sample_rate = rate;
|
this->sample_rate = rate;
|
||||||
|
RETURN_ERR( track_init( &this->track_filter, this ) );
|
||||||
|
this->tfilter.max_silence = 6 * stereo * this->sample_rate;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -679,7 +686,7 @@ void Sound_mute_voices( struct Vgm_Emu* this, int mask )
|
||||||
}
|
}
|
||||||
else
|
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) ||
|
assert( (ch.center && ch.left && ch.right) ||
|
||||||
(!ch.center && !ch.left && !ch.right) ); // all or nothing
|
(!ch.center && !ch.left && !ch.right) ); // all or nothing
|
||||||
set_voice( this, i, ch.center, ch.left, ch.right );
|
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 )
|
blargg_err_t Vgm_start_track( struct Vgm_Emu* this )
|
||||||
{
|
{
|
||||||
clear_track_vars( this );
|
clear_track_vars( this );
|
||||||
|
|
@ -750,7 +756,7 @@ blargg_err_t Vgm_start_track( struct Vgm_Emu* this )
|
||||||
this->vgm_time = 0;
|
this->vgm_time = 0;
|
||||||
if ( get_le32( header( this )->version ) >= 0x150 )
|
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 );
|
check( data_offset );
|
||||||
if ( data_offset )
|
if ( data_offset )
|
||||||
this->pos += data_offset + offsetof (struct header_t,data_offset) - 0x40;
|
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 ) )
|
if ( Ym2612_enabled( &this->ym2612 ) )
|
||||||
Ym2612_reset( &this->ym2612 );
|
Ym2612_reset( &this->ym2612 );
|
||||||
|
|
||||||
Blip_clear( &this->blip_buf, 1 );
|
Blip_clear( &this->blip_buf );
|
||||||
Resampler_clear( &this->resampler );
|
Resampler_clear( &this->resampler );
|
||||||
}
|
}
|
||||||
|
|
||||||
this->fm_time_offset = 0;
|
this->fm_time_offset = 0;
|
||||||
|
|
||||||
Buffer_clear( &this->buf );
|
Buffer_clear( &this->stereo_buf );
|
||||||
|
|
||||||
this->emu_track_ended_ = false;
|
// convert filter times to samples
|
||||||
this->track_ended = false;
|
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 )
|
return track_start( &this->track_filter );
|
||||||
{
|
|
||||||
// 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tell/Seek
|
// 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;
|
msec -= sec * 1000;
|
||||||
return (sec * sample_rate + msec * sample_rate / 1000) * stereo;
|
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;
|
int rate = this->sample_rate * stereo;
|
||||||
blargg_long sec = this->out_time / rate;
|
int sec = track_sample_count( &this->track_filter ) / rate;
|
||||||
return sec * 1000 + (this->out_time - sec * rate) * 1000 / 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 );
|
int time = msec_to_samples( msec, this->sample_rate );
|
||||||
if ( time < this->out_time )
|
if ( time < track_sample_count( &this->track_filter ) )
|
||||||
RETURN_ERR( Vgm_start_track( this ) );
|
RETURN_ERR( Vgm_start_track( this ) );
|
||||||
return Track_skip( this, time - this->out_time );
|
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, int count )
|
||||||
blargg_err_t Track_skip( struct Vgm_Emu* this, long count )
|
|
||||||
{
|
{
|
||||||
this->out_time += count;
|
require( this->current_track >= 0 ); // start_track() must have been called already
|
||||||
|
return track_skip( &this->track_filter, 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
// for long skip, mute sound
|
||||||
const long threshold = 30000;
|
const int threshold = 32768;
|
||||||
if ( count > threshold )
|
if ( count > threshold )
|
||||||
{
|
{
|
||||||
int saved_mute = this->mute_mask_;
|
int saved_mute = this->mute_mask_;
|
||||||
Sound_mute_voices( this, ~0 );
|
Sound_mute_voices( this, ~0 );
|
||||||
|
|
||||||
while ( count > threshold / 2 && !this->emu_track_ended_ )
|
int n = count - threshold/2;
|
||||||
{
|
n &= ~(2048-1); // round to multiple of 2048
|
||||||
RETURN_ERR( play_( this, buf_size, this->buf_ ) );
|
count -= n;
|
||||||
count -= buf_size;
|
RETURN_ERR( skippy_( &this->track_filter, n ) );
|
||||||
}
|
|
||||||
|
|
||||||
Sound_mute_voices( this, saved_mute );
|
Sound_mute_voices( this, saved_mute );
|
||||||
}
|
}
|
||||||
|
|
||||||
while ( count && !this->emu_track_ended_ )
|
return skippy_( &this->track_filter, count );
|
||||||
{
|
|
||||||
long n = buf_size;
|
|
||||||
if ( n > count )
|
|
||||||
n = count;
|
|
||||||
count -= n;
|
|
||||||
RETURN_ERR( play_( this, n, this->buf_ ) );
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fading
|
// Fading
|
||||||
|
|
||||||
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 )
|
||||||
{
|
{
|
||||||
this->fade_step = this->sample_rate * length_msec / (fade_block_size * fade_shift * 1000 / stereo);
|
track_set_fade( &this->track_filter, msec_to_samples( start_msec, this->sample_rate ),
|
||||||
this->fade_start = msec_to_samples( start_msec, this->sample_rate );
|
length_msec * this->sample_rate / (1000 / stereo) );
|
||||||
}
|
}
|
||||||
|
|
||||||
// unit / pow( 2.0, (double) x / step )
|
blargg_err_t Vgm_play( struct Vgm_Emu* this, int out_count, sample_t* out )
|
||||||
static int int_log( blargg_long x, int step, int unit )
|
|
||||||
{
|
{
|
||||||
int shift = x / step;
|
require( this->current_track >= 0 );
|
||||||
int fraction = (x - shift * step) * unit / step;
|
require( out_count % stereo == 0 );
|
||||||
return ((unit - fraction) + (fraction >> 1)) >> shift;
|
return track_play( &this->track_filter, out_count, out );
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 );
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
// fill internal buffer and check it for silence
|
|
||||||
void fill_buf( struct Vgm_Emu* this )
|
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
blargg_err_t Vgm_play( struct Vgm_Emu* this, long 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;
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,13 @@
|
||||||
// Sega Master System/Mark III, Sega Genesis/Mega Drive, BBC Micro VGM music file emulator
|
// 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
|
#ifndef VGM_EMU_H
|
||||||
#define VGM_EMU_H
|
#define VGM_EMU_H
|
||||||
|
|
||||||
#include "blargg_common.h"
|
#include "blargg_common.h"
|
||||||
#include "blargg_source.h"
|
#include "blargg_source.h"
|
||||||
|
|
||||||
|
#include "track_filter.h"
|
||||||
#include "resampler.h"
|
#include "resampler.h"
|
||||||
#include "multi_buffer.h"
|
#include "multi_buffer.h"
|
||||||
#include "ym2413_emu.h"
|
#include "ym2413_emu.h"
|
||||||
|
|
@ -17,7 +19,6 @@ typedef int fm_time_t;
|
||||||
|
|
||||||
enum { fm_time_bits = 12 };
|
enum { fm_time_bits = 12 };
|
||||||
enum { blip_time_bits = 12 };
|
enum { blip_time_bits = 12 };
|
||||||
enum { buf_size = 2048 };
|
|
||||||
|
|
||||||
// VGM header format
|
// VGM header format
|
||||||
enum { header_size = 0x40 };
|
enum { header_size = 0x40 };
|
||||||
|
|
@ -46,9 +47,9 @@ enum { gme_max_field = 63 };
|
||||||
struct track_info_t
|
struct track_info_t
|
||||||
{
|
{
|
||||||
/* times in milliseconds; -1 if unknown */
|
/* times in milliseconds; -1 if unknown */
|
||||||
long length;
|
int length;
|
||||||
long intro_length;
|
int intro_length;
|
||||||
long loop_length;
|
int loop_length;
|
||||||
|
|
||||||
/* empty string if not available */
|
/* empty string if not available */
|
||||||
char game [64];
|
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.
|
// YM2413 sound chip emulator. I can provide one I've modified to work with the library.
|
||||||
struct Vgm_Emu {
|
struct Vgm_Emu {
|
||||||
int fm_rate;
|
int fm_rate;
|
||||||
long psg_rate;
|
int psg_rate;
|
||||||
long vgm_rate;
|
int vgm_rate;
|
||||||
bool disable_oversampling;
|
bool disable_oversampling;
|
||||||
|
|
||||||
long fm_time_offset;
|
int fm_time_offset;
|
||||||
int fm_time_factor;
|
int fm_time_factor;
|
||||||
|
|
||||||
int blip_time_factor;
|
int blip_time_factor;
|
||||||
|
|
@ -84,47 +85,34 @@ struct Vgm_Emu {
|
||||||
int dac_amp;
|
int dac_amp;
|
||||||
int dac_disabled; // -1 if disabled
|
int dac_disabled; // -1 if disabled
|
||||||
|
|
||||||
struct Blip_Buffer blip_buf;
|
|
||||||
|
|
||||||
// general
|
// general
|
||||||
long clock_rate_;
|
int current_track;
|
||||||
|
int clock_rate_;
|
||||||
unsigned buf_changed_count;
|
unsigned buf_changed_count;
|
||||||
int max_initial_silence;
|
int max_initial_silence;
|
||||||
int voice_count;
|
int voice_count;
|
||||||
|
int const *voice_types;
|
||||||
int mute_mask_;
|
int mute_mask_;
|
||||||
int tempo;
|
int tempo;
|
||||||
int gain;
|
int gain;
|
||||||
|
|
||||||
long sample_rate;
|
int 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
|
|
||||||
|
|
||||||
// larger items at the end
|
// larger items at the end
|
||||||
struct track_info_t info;
|
struct track_info_t info;
|
||||||
sample_t buf_ [buf_size];
|
|
||||||
|
struct setup_t tfilter;
|
||||||
|
struct Track_Filter track_filter;
|
||||||
|
|
||||||
struct Ym2612_Emu ym2612;
|
struct Ym2612_Emu ym2612;
|
||||||
struct Ym2413_Emu ym2413;
|
struct Ym2413_Emu ym2413;
|
||||||
|
|
||||||
struct Sms_Apu psg;
|
struct Sms_Apu psg;
|
||||||
struct Blip_Synth pcm;
|
struct Blip_Synth pcm;
|
||||||
|
struct Blip_Buffer blip_buf;
|
||||||
|
|
||||||
struct Resampler resampler;
|
struct Resampler resampler;
|
||||||
struct Stereo_Buffer buf;
|
struct Multi_Buffer stereo_buf;
|
||||||
};
|
};
|
||||||
|
|
||||||
void Vgm_init( struct Vgm_Emu* this );
|
void Vgm_init( struct Vgm_Emu* this );
|
||||||
|
|
@ -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 ); }
|
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.
|
// 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.
|
// Start a track, where 0 is the first track. Also clears warning string.
|
||||||
blargg_err_t Vgm_start_track( struct Vgm_Emu* this );
|
blargg_err_t Vgm_start_track( struct Vgm_Emu* this );
|
||||||
|
|
||||||
// Generate 'count' samples info 'buf'. Output is in stereo. Any emulation
|
// Generate 'count' samples info 'buf'. Output is in stereo. Any emulation
|
||||||
// errors set warning string, and major errors also end track.
|
// 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
|
// Track status/control
|
||||||
|
|
||||||
// Number of milliseconds (1000 msec = 1 second) played since beginning of track
|
// 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.
|
// 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
|
// 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
|
// 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.
|
// 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
|
// 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 )
|
if ( length <= 0 )
|
||||||
{
|
{
|
||||||
length = this->info.intro_length + 2 * this->info.loop_length; // intro + 2 loops
|
length = this->info.intro_length + 2 * this->info.loop_length; // intro + 2 loops
|
||||||
|
|
@ -203,5 +203,4 @@ static inline void Sound_set_gain( struct Vgm_Emu* this, int g )
|
||||||
this->gain = g;
|
this->gain = g;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@
|
||||||
|
|
||||||
#include "ym2612_emu.h"
|
#include "ym2612_emu.h"
|
||||||
|
|
||||||
#include <assert.h>
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
|
|
|
||||||
|
|
@ -74,8 +74,8 @@ enum codec_status codec_run(void)
|
||||||
return CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((err = Nsf_load(&nsf_emu, buf, ci->filesize))) {
|
if ((err = Nsf_load_mem(&nsf_emu, buf, ci->filesize))) {
|
||||||
DEBUGF("NSF: Nsf_load failed (%s)\n", err);
|
DEBUGF("NSF: Nsf_load_mem failed (%s)\n", err);
|
||||||
return CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -116,7 +116,7 @@ next_track:
|
||||||
|
|
||||||
/* Generate audio buffer */
|
/* Generate audio buffer */
|
||||||
err = Nsf_play(&nsf_emu, CHUNK_SIZE, samples);
|
err = Nsf_play(&nsf_emu, CHUNK_SIZE, samples);
|
||||||
if (err || nsf_emu.track_ended) {
|
if (err || Track_ended(&nsf_emu)) {
|
||||||
track++;
|
track++;
|
||||||
if (track >= nsf_emu.track_count) break;
|
if (track >= nsf_emu.track_count) break;
|
||||||
goto next_track;
|
goto next_track;
|
||||||
|
|
|
||||||
|
|
@ -83,7 +83,7 @@ enum codec_status codec_run(void)
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((err = Sgc_load_mem(&sgc_emu, buf, ci->filesize))) {
|
if ((err = Sgc_load_mem(&sgc_emu, buf, ci->filesize))) {
|
||||||
DEBUGF("SGC: Sgc_load failed (%s)\n", err);
|
DEBUGF("SGC: Sgc_load_mem failed (%s)\n", err);
|
||||||
return CODEC_ERROR;
|
return CODEC_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -110,7 +110,7 @@ next_track:
|
||||||
|
|
||||||
/* Generate audio buffer */
|
/* Generate audio buffer */
|
||||||
err = Sgc_play(&sgc_emu, CHUNK_SIZE, samples);
|
err = Sgc_play(&sgc_emu, CHUNK_SIZE, samples);
|
||||||
if (err || sgc_emu.track_ended) {
|
if (err || Track_ended(&sgc_emu)) {
|
||||||
track++;
|
track++;
|
||||||
if (track >= sgc_emu.track_count) break;
|
if (track >= sgc_emu.track_count) break;
|
||||||
goto next_track;
|
goto next_track;
|
||||||
|
|
|
||||||
|
|
@ -130,7 +130,7 @@ enum codec_status codec_run(void)
|
||||||
|
|
||||||
/* Generate audio buffer */
|
/* Generate audio buffer */
|
||||||
err = Vgm_play(&vgm_emu, CHUNK_SIZE, samples);
|
err = Vgm_play(&vgm_emu, CHUNK_SIZE, samples);
|
||||||
if (err || vgm_emu.track_ended) break;
|
if (err || Track_ended(&vgm_emu)) break;
|
||||||
|
|
||||||
ci->pcmbuf_insert(samples, NULL, CHUNK_SIZE >> 1);
|
ci->pcmbuf_insert(samples, NULL, CHUNK_SIZE >> 1);
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue