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))) {
|
||||
DEBUGF("AY: Ay_load failed (%s)\n", err);
|
||||
DEBUGF("AY: Ay_load_mem failed (%s)\n", err);
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
|
||||
|
@ -118,7 +118,7 @@ next_track:
|
|||
|
||||
/* Generate audio buffer */
|
||||
err = Ay_play(&ay_emu, CHUNK_SIZE, samples);
|
||||
if (err || ay_emu.track_ended) {
|
||||
if (err || Track_ended(&ay_emu)) {
|
||||
track++;
|
||||
if (track >= ay_emu.track_count) break;
|
||||
goto next_track;
|
||||
|
|
|
@ -67,8 +67,8 @@ enum codec_status codec_run(void)
|
|||
return CODEC_ERROR;
|
||||
}
|
||||
|
||||
if ((err = Gbs_load(&gbs_emu, buf, ci->filesize))) {
|
||||
DEBUGF("GBS: Gbs_load failed (%s)\n", err);
|
||||
if ((err = Gbs_load_mem(&gbs_emu, buf, ci->filesize))) {
|
||||
DEBUGF("GBS: Gbs_load_mem failed (%s)\n", err);
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
|
||||
|
@ -95,7 +95,7 @@ next_track:
|
|||
|
||||
/* Generate audio buffer */
|
||||
err = Gbs_play(&gbs_emu, CHUNK_SIZE, samples);
|
||||
if (err || gbs_emu.track_ended) {
|
||||
if (err || Track_ended(&gbs_emu)) {
|
||||
track++;
|
||||
if (track >= gbs_emu.track_count) break;
|
||||
goto next_track;
|
||||
|
|
|
@ -67,8 +67,8 @@ enum codec_status codec_run(void)
|
|||
return CODEC_ERROR;
|
||||
}
|
||||
|
||||
if ((err = Hes_load(&hes_emu, buf, ci->filesize))) {
|
||||
DEBUGF("HES: Hes_load failed (%s)\n", err);
|
||||
if ((err = Hes_load_mem(&hes_emu, buf, ci->filesize))) {
|
||||
DEBUGF("HES: Hes_load_mem failed (%s)\n", err);
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
|
||||
|
@ -95,7 +95,7 @@ next_track:
|
|||
|
||||
/* Generate audio buffer */
|
||||
err = Hes_play(&hes_emu, CHUNK_SIZE, samples);
|
||||
if (err || hes_emu.track_ended) {
|
||||
if (err || Track_ended(&hes_emu)) {
|
||||
track++;
|
||||
if (track >= hes_emu.track_count) break;
|
||||
goto next_track;
|
||||
|
|
|
@ -98,7 +98,7 @@ next_track:
|
|||
|
||||
/* Generate audio buffer */
|
||||
err = Kss_play(&kss_emu, CHUNK_SIZE, samples);
|
||||
if (err || kss_emu.track_ended) {
|
||||
if (err || Track_ended(&kss_emu)) {
|
||||
track++;
|
||||
if (track >= kss_emu.track_count) break;
|
||||
goto next_track;
|
||||
|
|
|
@ -3,4 +3,5 @@ ay_cpu.c
|
|||
ay_emu.c
|
||||
blip_buffer.c
|
||||
multi_buffer.c
|
||||
track_filter.c
|
||||
z80_cpu.c
|
||||
|
|
|
@ -6,3 +6,4 @@ gbs_emu.c
|
|||
blip_buffer.c
|
||||
multi_buffer.c
|
||||
rom_data.c
|
||||
track_filter.c
|
||||
|
|
|
@ -5,3 +5,4 @@ hes_emu.c
|
|||
blip_buffer.c
|
||||
multi_buffer.c
|
||||
rom_data.c
|
||||
track_filter.c
|
||||
|
|
|
@ -9,3 +9,4 @@ multi_buffer.c
|
|||
rom_data.c
|
||||
emu8950.c
|
||||
emuadpcm.c
|
||||
track_filter.c
|
||||
|
|
|
@ -12,3 +12,4 @@ nsfe_info.c
|
|||
blip_buffer.c
|
||||
multi_buffer.c
|
||||
rom_data.c
|
||||
track_filter.c
|
||||
|
|
|
@ -4,3 +4,4 @@ z80_cpu.c
|
|||
blip_buffer.c
|
||||
multi_buffer.c
|
||||
rom_data.c
|
||||
track_filter.c
|
||||
|
|
|
@ -3,6 +3,7 @@ multi_buffer.c
|
|||
resampler.c
|
||||
vgm_emu.c
|
||||
ym2612_emu.c
|
||||
track_filter.c
|
||||
inflate/bbfuncs.c
|
||||
inflate/inflate.c
|
||||
inflate/mallocer.c
|
||||
|
|
|
@ -17,12 +17,6 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
|
|||
|
||||
#include "blargg_source.h"
|
||||
|
||||
int const stereo = 2; // number of channels for stereo
|
||||
int const silence_max = 6; // seconds
|
||||
int const silence_threshold = 0x10;
|
||||
long const fade_block_size = 512;
|
||||
int const fade_shift = 8; // fade ends with gain at 1.0 / (1 << fade_shift)
|
||||
|
||||
const char* const gme_wrong_file_type = "Wrong file type for this emulator";
|
||||
|
||||
// TODO: probably don't need detailed errors as to why file is corrupt
|
||||
|
@ -38,16 +32,7 @@ int const cpc_clock = 2000000;
|
|||
static void clear_track_vars( struct Ay_Emu *this )
|
||||
{
|
||||
this->current_track = -1;
|
||||
this->out_time = 0;
|
||||
this->emu_time = 0;
|
||||
this->emu_track_ended_ = true;
|
||||
this->track_ended = true;
|
||||
this->fade_start = INT_MAX / 2 + 1;
|
||||
this->fade_step = 1;
|
||||
this->silence_time = 0;
|
||||
this->silence_count = 0;
|
||||
this->buf_remain = 0;
|
||||
/* warning(); // clear warning */
|
||||
track_stop( &this->track_filter );
|
||||
}
|
||||
|
||||
void Ay_init( struct Ay_Emu *this )
|
||||
|
@ -59,18 +44,21 @@ void Ay_init( struct Ay_Emu *this )
|
|||
this->track_count = 0;
|
||||
|
||||
// defaults
|
||||
this->max_initial_silence = 2;
|
||||
this->ignore_silence = false;
|
||||
this->tfilter = *track_get_setup( &this->track_filter );
|
||||
this->tfilter.max_initial = 2;
|
||||
this->tfilter.lookahead = 6;
|
||||
this->track_filter.silence_ignored_ = false;
|
||||
|
||||
this->voice_count = 0;
|
||||
clear_track_vars( this );
|
||||
this->beeper_output = NULL;
|
||||
disable_beeper( this );
|
||||
|
||||
Ay_apu_init( &this->apu );
|
||||
Z80_init( &this->cpu );
|
||||
|
||||
this->silence_lookahead = 6 ;
|
||||
// clears fields
|
||||
this->voice_count = 0;
|
||||
this->voice_types = 0;
|
||||
clear_track_vars( this );
|
||||
}
|
||||
|
||||
// Track info
|
||||
|
@ -107,35 +95,22 @@ static blargg_err_t parse_header( byte const in [], int size, struct file_t* out
|
|||
return 0;
|
||||
}
|
||||
|
||||
long Track_get_length( struct Ay_Emu* this, int n )
|
||||
{
|
||||
long length = 0;
|
||||
|
||||
byte const* track_info = get_data( &this->file, this->file.tracks + n * 4 + 2, 6 );
|
||||
if ( track_info )
|
||||
length = get_be16( track_info + 4 ) * (1000 / 50); // frames to msec
|
||||
|
||||
if ( (this->m3u.size > 0) && (n < this->m3u.size) ) {
|
||||
struct entry_t* entry = &this->m3u.entries [n];
|
||||
length = entry->length;
|
||||
}
|
||||
|
||||
if ( length <= 0 )
|
||||
length = 120 * 1000; /* 2 minutes */
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
// Setup
|
||||
|
||||
static void change_clock_rate( struct Ay_Emu *this, long rate )
|
||||
static void change_clock_rate( struct Ay_Emu *this, int rate )
|
||||
{
|
||||
this->clock_rate_ = rate;
|
||||
Buffer_clock_rate( &this->stereo_buf, rate );
|
||||
}
|
||||
|
||||
blargg_err_t Ay_load_mem( struct Ay_Emu *this, byte const in [], int size )
|
||||
blargg_err_t Ay_load_mem( struct Ay_Emu *this, byte const in [], long size )
|
||||
{
|
||||
// Unload
|
||||
this->voice_count = 0;
|
||||
this->track_count = 0;
|
||||
this->m3u.size = 0;
|
||||
clear_track_vars( this );
|
||||
|
||||
assert( offsetof (struct header_t,track_info [2]) == header_size );
|
||||
|
||||
RETURN_ERR( parse_header( in, size, &this->file ) );
|
||||
|
@ -144,19 +119,22 @@ blargg_err_t Ay_load_mem( struct Ay_Emu *this, byte const in [], int size )
|
|||
warning( "Unknown file version" ); */
|
||||
|
||||
this->voice_count = ay_osc_count + 1; // +1 for beeper
|
||||
static int const types [ay_osc_count + 1] = {
|
||||
wave_type+0, wave_type+1, wave_type+2, mixed_type+1
|
||||
};
|
||||
this->voice_types = types;
|
||||
|
||||
Ay_apu_volume( &this->apu, this->gain);
|
||||
|
||||
// Setup buffer
|
||||
change_clock_rate( this, spectrum_clock );
|
||||
RETURN_ERR( Buffer_set_channel_count( &this->stereo_buf, this->voice_count, this->voice_types ) );
|
||||
this->buf_changed_count = Buffer_channels_changed_count( &this->stereo_buf );
|
||||
|
||||
Sound_set_tempo( this, this->tempo );
|
||||
|
||||
// Remute voices
|
||||
Sound_mute_voices( this, this->mute_mask_ );
|
||||
|
||||
this->track_count = this->file.header->max_track + 1;
|
||||
this->m3u.size = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -298,7 +276,7 @@ enable_cpc:
|
|||
}
|
||||
}
|
||||
|
||||
blargg_err_t Ay_set_sample_rate( struct Ay_Emu *this, long rate )
|
||||
blargg_err_t Ay_set_sample_rate( struct Ay_Emu *this, int rate )
|
||||
{
|
||||
require( !this->sample_rate ); // sample rate can't be changed once set
|
||||
Buffer_init( &this->stereo_buf );
|
||||
|
@ -308,6 +286,8 @@ blargg_err_t Ay_set_sample_rate( struct Ay_Emu *this, long rate )
|
|||
Buffer_bass_freq( &this->stereo_buf, 160 );
|
||||
|
||||
this->sample_rate = rate;
|
||||
RETURN_ERR( track_init( &this->track_filter, this ) );
|
||||
this->tfilter.max_silence = 6 * stereo * this->sample_rate;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -335,7 +315,7 @@ void Sound_mute_voices( struct Ay_Emu *this, int mask )
|
|||
}
|
||||
else
|
||||
{
|
||||
struct channel_t ch = Buffer_channel( &this->stereo_buf );
|
||||
struct channel_t ch = Buffer_channel( &this->stereo_buf, i );
|
||||
assert( (ch.center && ch.left && ch.right) ||
|
||||
(!ch.center && !ch.left && !ch.right) ); // all or nothing
|
||||
set_voice( this, i, ch.center );
|
||||
|
@ -359,7 +339,6 @@ void Sound_set_tempo( struct Ay_Emu *this, int t )
|
|||
this->play_period = (blip_time_t) ((p * FP_ONE_TEMPO) / t);
|
||||
}
|
||||
|
||||
void fill_buf( struct Ay_Emu *this );;
|
||||
blargg_err_t Ay_start_track( struct Ay_Emu *this, int track )
|
||||
{
|
||||
clear_track_vars( this );
|
||||
|
@ -496,272 +475,109 @@ blargg_err_t Ay_start_track( struct Ay_Emu *this, int track )
|
|||
Ay_apu_write_addr( &this->apu, 7 );
|
||||
Ay_apu_write_data( &this->apu, 0, 0x38 );
|
||||
|
||||
this->emu_track_ended_ = false;
|
||||
this->track_ended = false;
|
||||
// convert filter times to samples
|
||||
struct setup_t s = this->tfilter;
|
||||
s.max_initial *= this->sample_rate * stereo;
|
||||
#ifdef GME_DISABLE_SILENCE_LOOKAHEAD
|
||||
s.lookahead = 1;
|
||||
#endif
|
||||
track_setup( &this->track_filter, &s );
|
||||
|
||||
if ( !this->ignore_silence )
|
||||
{
|
||||
// play until non-silence or end of track
|
||||
long end;
|
||||
for ( end = this->max_initial_silence * stereo * this->sample_rate; this->emu_time < end; )
|
||||
{
|
||||
fill_buf( this );
|
||||
if ( this->buf_remain | (int) this->emu_track_ended_ )
|
||||
break;
|
||||
}
|
||||
|
||||
this->emu_time = this->buf_remain;
|
||||
this->out_time = 0;
|
||||
this->silence_time = 0;
|
||||
this->silence_count = 0;
|
||||
}
|
||||
/* return track_ended() ? warning() : 0; */
|
||||
return 0;
|
||||
return track_start( &this->track_filter );
|
||||
}
|
||||
|
||||
// Tell/Seek
|
||||
|
||||
static blargg_long msec_to_samples( blargg_long msec, long sample_rate )
|
||||
static int msec_to_samples( int msec, int sample_rate )
|
||||
{
|
||||
blargg_long sec = msec / 1000;
|
||||
int sec = msec / 1000;
|
||||
msec -= sec * 1000;
|
||||
return (sec * sample_rate + msec * sample_rate / 1000) * stereo;
|
||||
}
|
||||
|
||||
long Track_tell( struct Ay_Emu *this )
|
||||
int Track_tell( struct Ay_Emu *this )
|
||||
{
|
||||
blargg_long rate = this->sample_rate * stereo;
|
||||
blargg_long sec = this->out_time / rate;
|
||||
return sec * 1000 + (this->out_time - sec * rate) * 1000 / rate;
|
||||
int rate = this->sample_rate * stereo;
|
||||
int sec = track_sample_count( &this->track_filter ) / rate;
|
||||
return sec * 1000 + (track_sample_count( &this->track_filter ) - sec * rate) * 1000 / rate;
|
||||
}
|
||||
|
||||
blargg_err_t Track_seek( struct Ay_Emu *this, long msec )
|
||||
blargg_err_t Track_seek( struct Ay_Emu *this, int msec )
|
||||
{
|
||||
blargg_long time = msec_to_samples( msec, this->sample_rate );
|
||||
if ( time < this->out_time )
|
||||
RETURN_ERR( Ay_start_track( this, this->current_track ) );
|
||||
return Track_skip( this, time - this->out_time );
|
||||
int time = msec_to_samples( msec, this->sample_rate );
|
||||
if ( time < track_sample_count( &this->track_filter ) )
|
||||
RETURN_ERR( Ay_start_track( this, this->current_track ) );
|
||||
return Track_skip( this, time - track_sample_count( &this->track_filter ) );
|
||||
}
|
||||
|
||||
blargg_err_t play_( struct Ay_Emu *this, long count, sample_t* out );
|
||||
static blargg_err_t skip_( struct Ay_Emu *this, long count )
|
||||
blargg_err_t skip_( void *emu, int count )
|
||||
{
|
||||
struct Ay_Emu* this = (struct Ay_Emu*) emu;
|
||||
|
||||
// for long skip, mute sound
|
||||
const long threshold = 30000;
|
||||
const int threshold = 32768;
|
||||
if ( count > threshold )
|
||||
{
|
||||
int saved_mute = this->mute_mask_;
|
||||
Sound_mute_voices( this, ~0 );
|
||||
|
||||
while ( count > threshold / 2 && !this->emu_track_ended_ )
|
||||
{
|
||||
RETURN_ERR( play_( this, buf_size, this->buf ) );
|
||||
count -= buf_size;
|
||||
}
|
||||
int n = count - threshold/2;
|
||||
n &= ~(2048-1); // round to multiple of 2048
|
||||
count -= n;
|
||||
RETURN_ERR( skippy_( &this->track_filter, n ) );
|
||||
|
||||
Sound_mute_voices( this, saved_mute );
|
||||
}
|
||||
|
||||
while ( count && !this->emu_track_ended_ )
|
||||
{
|
||||
long n = buf_size;
|
||||
if ( n > count )
|
||||
n = count;
|
||||
count -= n;
|
||||
RETURN_ERR( play_( this, n, this->buf ) );
|
||||
}
|
||||
return 0;
|
||||
return skippy_( &this->track_filter, count );
|
||||
}
|
||||
|
||||
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
|
||||
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;
|
||||
int Track_get_length( struct Ay_Emu* this, int n )
|
||||
{
|
||||
int length = 0;
|
||||
|
||||
n = min( count, this->buf_remain );
|
||||
this->buf_remain -= n;
|
||||
count -= n;
|
||||
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 ( count && !this->emu_track_ended_ )
|
||||
{
|
||||
this->emu_time += count;
|
||||
if ( length <= 0 )
|
||||
length = 120 * 1000; /* 2 minutes */
|
||||
|
||||
// End track if error
|
||||
if ( skip_( this, count ) )
|
||||
this->emu_track_ended_ = true;
|
||||
}
|
||||
|
||||
if ( !(this->silence_count | this->buf_remain) ) // caught up to emulator, so update track ended
|
||||
this->track_ended |= this->emu_track_ended_;
|
||||
|
||||
return 0;
|
||||
return length;
|
||||
}
|
||||
|
||||
// Fading
|
||||
|
||||
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 )
|
||||
{
|
||||
this->fade_step = this->sample_rate * length_msec / (fade_block_size * fade_shift * 1000 / stereo);
|
||||
this->fade_start = msec_to_samples( start_msec, this->sample_rate );
|
||||
track_set_fade( &this->track_filter, msec_to_samples( start_msec, this->sample_rate ),
|
||||
length_msec * this->sample_rate / (1000 / stereo) );
|
||||
}
|
||||
|
||||
// unit / pow( 2.0, (double) x / step )
|
||||
static int int_log( blargg_long x, int step, int unit )
|
||||
blargg_err_t Ay_play( struct Ay_Emu *this, int out_count, sample_t* out )
|
||||
{
|
||||
int shift = x / step;
|
||||
int fraction = (x - shift * step) * unit / step;
|
||||
return ((unit - fraction) + (fraction >> 1)) >> shift;
|
||||
require( this->current_track >= 0 );
|
||||
require( out_count % stereo == 0 );
|
||||
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;
|
||||
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;
|
||||
struct Ay_Emu* this = (struct Ay_Emu*) emu;
|
||||
|
||||
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 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;
|
||||
int remain = count;
|
||||
while ( remain )
|
||||
{
|
||||
Buffer_disable_immediate_removal( &this->stereo_buf );
|
||||
remain -= Buffer_read_samples( &this->stereo_buf, &out [count - remain], remain );
|
||||
if ( remain )
|
||||
{
|
||||
|
@ -773,7 +589,7 @@ blargg_err_t play_( struct Ay_Emu *this, long count, sample_t* out )
|
|||
Sound_mute_voices( this, this->mute_mask_ );
|
||||
}
|
||||
int msec = Buffer_length( &this->stereo_buf );
|
||||
blip_time_t clocks_emulated = (blargg_long) msec * this->clock_rate_ / 1000 - 100;
|
||||
blip_time_t clocks_emulated = msec * this->clock_rate_ / 1000 - 100;
|
||||
RETURN_ERR( run_clocks( this, &clocks_emulated, msec ) );
|
||||
assert( clocks_emulated );
|
||||
Buffer_end_frame( &this->stereo_buf, clocks_emulated );
|
||||
|
|
|
@ -10,14 +10,12 @@
|
|||
#include "z80_cpu.h"
|
||||
#include "ay_apu.h"
|
||||
#include "m3u_playlist.h"
|
||||
|
||||
typedef short sample_t;
|
||||
#include "track_filter.h"
|
||||
|
||||
// 64K memory to load code and data into before starting track. Caller
|
||||
// must parse the AY file.
|
||||
enum { mem_size = 0x10000 };
|
||||
enum { ram_addr = 0x4000 }; // where official RAM starts
|
||||
enum { buf_size = 2048 };
|
||||
|
||||
// AY file header
|
||||
enum { header_size = 0x14 };
|
||||
|
@ -62,43 +60,30 @@ struct Ay_Emu {
|
|||
bool cpc_mode;
|
||||
|
||||
// general
|
||||
int max_initial_silence;
|
||||
int voice_count;
|
||||
int const* voice_types;
|
||||
int mute_mask_;
|
||||
int tempo;
|
||||
int gain;
|
||||
|
||||
long sample_rate;
|
||||
int sample_rate;
|
||||
|
||||
// track-specific
|
||||
int current_track;
|
||||
int track_count;
|
||||
blargg_long out_time; // number of samples played since start of track
|
||||
blargg_long emu_time; // number of samples emulator has generated since start of track
|
||||
volatile bool track_ended;
|
||||
bool emu_track_ended_; // emulator has reached end of track
|
||||
|
||||
// fading
|
||||
blargg_long fade_start;
|
||||
int fade_step;
|
||||
|
||||
// silence detection
|
||||
bool ignore_silence;
|
||||
int silence_lookahead; // speed to run emulator when looking ahead for silence
|
||||
long silence_time; // number of samples where most recent silence began
|
||||
long silence_count; // number of samples of silence to play before using buf
|
||||
long buf_remain; // number of samples left in silence buffer
|
||||
|
||||
long clock_rate_;
|
||||
int clock_rate_;
|
||||
unsigned buf_changed_count;
|
||||
|
||||
// M3u Playlist
|
||||
struct M3u_Playlist m3u;
|
||||
|
||||
// large items
|
||||
struct setup_t tfilter;
|
||||
struct Track_Filter track_filter;
|
||||
|
||||
struct Ay_Apu apu;
|
||||
sample_t buf [buf_size];
|
||||
struct Stereo_Buffer stereo_buf; // NULL if using custom buffer
|
||||
struct Multi_Buffer stereo_buf; // NULL if using custom buffer
|
||||
struct Z80_Cpu cpu;
|
||||
struct mem_t mem;
|
||||
};
|
||||
|
@ -106,36 +91,48 @@ struct Ay_Emu {
|
|||
// Basic functionality (see Gme_File.h for file loading/track info functions)
|
||||
void Ay_init( struct Ay_Emu* this );
|
||||
|
||||
blargg_err_t Ay_load_mem( struct Ay_Emu* this, byte const in [], int size );
|
||||
blargg_err_t Ay_load_mem( struct Ay_Emu* this, byte const in [], long size );
|
||||
|
||||
// Set output sample rate. Must be called only once before loading file.
|
||||
blargg_err_t Ay_set_sample_rate( struct Ay_Emu* this, long sample_rate );
|
||||
blargg_err_t Ay_set_sample_rate( struct Ay_Emu* this, int sample_rate );
|
||||
|
||||
// Start a track, where 0 is the first track. Also clears warning string.
|
||||
blargg_err_t Ay_start_track( struct Ay_Emu* this, int track );
|
||||
|
||||
// Generate 'count' samples info 'buf'. Output is in stereo. Any emulation
|
||||
// errors set warning string, and major errors also end track.
|
||||
blargg_err_t Ay_play( struct Ay_Emu* this, long count, sample_t* buf );
|
||||
blargg_err_t Ay_play( struct Ay_Emu* this, int count, sample_t* buf );
|
||||
|
||||
|
||||
// Track status/control
|
||||
|
||||
// Number of milliseconds (1000 msec = 1 second) played since beginning of track
|
||||
long Track_tell( struct Ay_Emu* this );
|
||||
int Track_tell( struct Ay_Emu* this );
|
||||
|
||||
// Seek to new time in track. Seeking backwards or far forward can take a while.
|
||||
blargg_err_t Track_seek( struct Ay_Emu* this, long msec );
|
||||
blargg_err_t Track_seek( struct Ay_Emu* this, int msec );
|
||||
|
||||
// Skip n samples
|
||||
blargg_err_t Track_skip( struct Ay_Emu* this, long n );
|
||||
blargg_err_t Track_skip( struct Ay_Emu* this, int n );
|
||||
|
||||
// Set start time and length of track fade out. Once fade ends track_ended() returns
|
||||
// true. Fade time can be changed while track is playing.
|
||||
void Track_set_fade( struct Ay_Emu* this, long start_msec, long length_msec );
|
||||
void Track_set_fade( struct Ay_Emu* this, int start_msec, int length_msec );
|
||||
|
||||
// True if a track has reached its end
|
||||
static inline bool Track_ended( struct Ay_Emu* this )
|
||||
{
|
||||
return track_ended( &this->track_filter );
|
||||
}
|
||||
|
||||
// Disables automatic end-of-track detection and skipping of silence at beginning
|
||||
static inline void Track_ignore_silence( struct Ay_Emu* this, bool disable )
|
||||
{
|
||||
this->track_filter.silence_ignored_ = disable;
|
||||
}
|
||||
|
||||
// Get track length in milliseconds
|
||||
long Track_get_length( struct Ay_Emu* this, int n );
|
||||
int Track_get_length( struct Ay_Emu* this, int n );
|
||||
|
||||
// Sound customization
|
||||
|
||||
|
|
|
@ -7,12 +7,12 @@
|
|||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <limits.h>
|
||||
|
||||
#undef BLARGG_COMMON_H
|
||||
// allow blargg_config.h to #include blargg_common.h
|
||||
#include "blargg_config.h"
|
||||
#include "blargg_source.h"
|
||||
#ifndef BLARGG_COMMON_H
|
||||
#define BLARGG_COMMON_H
|
||||
|
||||
|
@ -98,19 +98,13 @@
|
|||
static bool false = 0;
|
||||
#endif
|
||||
|
||||
// blargg_long/blargg_ulong = at least 32 bits, int if it's big enough
|
||||
#include <limits.h>
|
||||
|
||||
#if INT_MAX >= 0x7FFFFFFF
|
||||
typedef int blargg_long;
|
||||
#else
|
||||
typedef long blargg_long;
|
||||
#endif
|
||||
|
||||
#if UINT_MAX >= 0xFFFFFFFF
|
||||
typedef unsigned blargg_ulong;
|
||||
#else
|
||||
typedef unsigned long blargg_ulong;
|
||||
/* My code depends on int being at least 32 bits. Almost everything these days
|
||||
uses at least 32-bit ints, so it's hard to even find a system with 16-bit ints
|
||||
to test with. The issue can't be gotten around by using a suitable blargg_int
|
||||
everywhere either, because int is often converted to implicitly when doing
|
||||
arithmetic on smaller types. */
|
||||
#if UINT_MAX < 0xFFFFFFFF
|
||||
#error "int must be at least 32 bits"
|
||||
#endif
|
||||
|
||||
// int8_t etc.
|
||||
|
|
|
@ -64,45 +64,60 @@ static inline void blargg_verify_byte_order( void )
|
|||
#endif
|
||||
}
|
||||
|
||||
static inline unsigned get_le16( void const* p ) {
|
||||
return ((unsigned char const*) p) [1] * 0x100u +
|
||||
((unsigned char const*) p) [0];
|
||||
static inline unsigned get_le16( void const* p )
|
||||
{
|
||||
return (unsigned) ((unsigned char const*) p) [1] << 8 |
|
||||
(unsigned) ((unsigned char const*) p) [0];
|
||||
}
|
||||
static inline unsigned get_be16( void const* p ) {
|
||||
return ((unsigned char const*) p) [0] * 0x100u +
|
||||
((unsigned char const*) p) [1];
|
||||
|
||||
static inline unsigned get_be16( void const* p )
|
||||
{
|
||||
return (unsigned) ((unsigned char const*) p) [0] << 8 |
|
||||
(unsigned) ((unsigned char const*) p) [1];
|
||||
}
|
||||
static inline blargg_ulong get_le32( void const* p ) {
|
||||
return ((unsigned char const*) p) [3] * 0x01000000u +
|
||||
((unsigned char const*) p) [2] * 0x00010000u +
|
||||
((unsigned char const*) p) [1] * 0x00000100u +
|
||||
((unsigned char const*) p) [0];
|
||||
|
||||
static inline unsigned get_le32( void const* p )
|
||||
{
|
||||
return (unsigned) ((unsigned char const*) p) [3] << 24 |
|
||||
(unsigned) ((unsigned char const*) p) [2] << 16 |
|
||||
(unsigned) ((unsigned char const*) p) [1] << 8 |
|
||||
(unsigned) ((unsigned char const*) p) [0];
|
||||
}
|
||||
static inline blargg_ulong get_be32( void const* p ) {
|
||||
return ((unsigned char const*) p) [0] * 0x01000000u +
|
||||
((unsigned char const*) p) [1] * 0x00010000u +
|
||||
((unsigned char const*) p) [2] * 0x00000100u +
|
||||
((unsigned char const*) p) [3];
|
||||
|
||||
static inline unsigned get_be32( void const* p )
|
||||
{
|
||||
return (unsigned) ((unsigned char const*) p) [0] << 24 |
|
||||
(unsigned) ((unsigned char const*) p) [1] << 16 |
|
||||
(unsigned) ((unsigned char const*) p) [2] << 8 |
|
||||
(unsigned) ((unsigned char const*) p) [3];
|
||||
}
|
||||
static inline void set_le16( void* p, unsigned n ) {
|
||||
|
||||
static inline void set_le16( void* p, unsigned n )
|
||||
{
|
||||
((unsigned char*) p) [1] = (unsigned char) (n >> 8);
|
||||
((unsigned char*) p) [0] = (unsigned char) n;
|
||||
}
|
||||
static inline void set_be16( void* p, unsigned n ) {
|
||||
|
||||
static inline void set_be16( void* p, unsigned n )
|
||||
{
|
||||
((unsigned char*) p) [0] = (unsigned char) (n >> 8);
|
||||
((unsigned char*) p) [1] = (unsigned char) n;
|
||||
}
|
||||
static inline void set_le32( void* p, blargg_ulong n ) {
|
||||
((unsigned char*) p) [3] = (unsigned char) (n >> 24);
|
||||
((unsigned char*) p) [2] = (unsigned char) (n >> 16);
|
||||
((unsigned char*) p) [1] = (unsigned char) (n >> 8);
|
||||
|
||||
static inline void set_le32( void* p, unsigned n )
|
||||
{
|
||||
((unsigned char*) p) [0] = (unsigned char) n;
|
||||
((unsigned char*) p) [1] = (unsigned char) (n >> 8);
|
||||
((unsigned char*) p) [2] = (unsigned char) (n >> 16);
|
||||
((unsigned char*) p) [3] = (unsigned char) (n >> 24);
|
||||
}
|
||||
static inline void set_be32( void* p, blargg_ulong n ) {
|
||||
((unsigned char*) p) [0] = (unsigned char) (n >> 24);
|
||||
((unsigned char*) p) [1] = (unsigned char) (n >> 16);
|
||||
((unsigned char*) p) [2] = (unsigned char) (n >> 8);
|
||||
|
||||
static inline void set_be32( void* p, unsigned n )
|
||||
{
|
||||
((unsigned char*) p) [3] = (unsigned char) n;
|
||||
((unsigned char*) p) [2] = (unsigned char) (n >> 8);
|
||||
((unsigned char*) p) [1] = (unsigned char) (n >> 16);
|
||||
((unsigned char*) p) [0] = (unsigned char) (n >> 24);
|
||||
}
|
||||
|
||||
#if defined(BLARGG_NONPORTABLE)
|
||||
|
@ -132,15 +147,21 @@ static inline void set_be32( void* p, blargg_ulong n ) {
|
|||
|
||||
#ifndef GET_LE16
|
||||
#define GET_LE16( addr ) get_le16( addr )
|
||||
#define GET_LE32( addr ) get_le32( addr )
|
||||
#define SET_LE16( addr, data ) set_le16( addr, data )
|
||||
#endif
|
||||
|
||||
#ifndef GET_LE32
|
||||
#define GET_LE32( addr ) get_le32( addr )
|
||||
#define SET_LE32( addr, data ) set_le32( addr, data )
|
||||
#endif
|
||||
|
||||
#ifndef GET_BE16
|
||||
#define GET_BE16( addr ) get_be16( addr )
|
||||
#define GET_BE32( addr ) get_be32( addr )
|
||||
#define SET_BE16( addr, data ) set_be16( addr, data )
|
||||
#endif
|
||||
|
||||
#ifndef GET_BE32
|
||||
#define GET_BE32( addr ) get_be32( addr )
|
||||
#define SET_BE32( addr, data ) set_be32( addr, data )
|
||||
#endif
|
||||
|
||||
|
|
|
@ -2,16 +2,13 @@
|
|||
#ifndef BLARGG_SOURCE_H
|
||||
#define BLARGG_SOURCE_H
|
||||
|
||||
// If debugging is enabled, abort program if expr is false. Meant for checking
|
||||
// internal state and consistency. A failed assertion indicates a bug in the module.
|
||||
// void assert( bool expr );
|
||||
#include <assert.h>
|
||||
|
||||
// If debugging is enabled and expr is false, abort program. Meant for checking
|
||||
// caller-supplied parameters and operations that are outside the control of the
|
||||
// module. A failed requirement indicates a bug outside the module.
|
||||
// void require( bool expr );
|
||||
#if defined(ROCKBOX)
|
||||
#undef assert
|
||||
#define assert( expr )
|
||||
#undef require
|
||||
#define require( expr )
|
||||
#else
|
||||
|
@ -36,28 +33,36 @@ static inline void blargg_dprintf_( const char* fmt, ... ) { }
|
|||
#undef check
|
||||
#define check( expr ) ((void) 0)
|
||||
|
||||
// If expr yields error string, return it from current function, otherwise continue.
|
||||
#undef RETURN_ERR
|
||||
#define RETURN_ERR( expr ) do { \
|
||||
blargg_err_t blargg_return_err_ = (expr); \
|
||||
if ( blargg_return_err_ ) return blargg_return_err_; \
|
||||
/* If expr yields non-NULL error string, returns it from current function,
|
||||
otherwise continues normally. */
|
||||
#undef RETURN_ERR
|
||||
#define RETURN_ERR( expr ) \
|
||||
do {\
|
||||
blargg_err_t blargg_return_err_ = (expr);\
|
||||
if ( blargg_return_err_ )\
|
||||
return blargg_return_err_;\
|
||||
} while ( 0 )
|
||||
|
||||
// If ptr is 0, return out of memory error string.
|
||||
#undef CHECK_ALLOC
|
||||
#define CHECK_ALLOC( ptr ) do { if ( (ptr) == 0 ) return "Out of memory"; } while ( 0 )
|
||||
/* If ptr is NULL, returns out-of-memory error, otherwise continues normally. */
|
||||
#undef CHECK_ALLOC
|
||||
#define CHECK_ALLOC( ptr ) \
|
||||
do {\
|
||||
if ( !(ptr) )\
|
||||
return "Out of memory";\
|
||||
} while ( 0 )
|
||||
|
||||
#ifndef max
|
||||
#define max(a,b) (((a) > (b)) ? (a) : (b))
|
||||
#endif
|
||||
|
||||
#ifndef min
|
||||
#define min(a,b) (((a) < (b)) ? (a) : (b))
|
||||
#endif
|
||||
|
||||
// TODO: good idea? bad idea?
|
||||
#undef byte
|
||||
#define byte byte_
|
||||
typedef unsigned char byte;
|
||||
// typedef unsigned char byte;
|
||||
typedef unsigned char blargg_byte;
|
||||
#undef byte
|
||||
#define byte blargg_byte
|
||||
|
||||
// deprecated
|
||||
#define BLARGG_CHECK_ALLOC CHECK_ALLOC
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
#include "blip_buffer.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <limits.h>
|
||||
#include <string.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,
|
||||
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
|
||||
|
||||
#ifdef BLARGG_ENABLE_OPTIMIZER
|
||||
#include BLARGG_ENABLE_OPTIMIZER
|
||||
#endif
|
||||
|
||||
int const silent_buf_size = 1; // size used for Silent_Blip_Buffer
|
||||
#include "blargg_source.h"
|
||||
|
||||
void Blip_init( struct Blip_Buffer* this )
|
||||
{
|
||||
this->factor_ = (blip_ulong)LONG_MAX;
|
||||
this->offset_ = 0;
|
||||
this->buffer_size_ = 0;
|
||||
this->sample_rate_ = 0;
|
||||
this->reader_accum_ = 0;
|
||||
this->bass_shift_ = 0;
|
||||
this->clock_rate_ = 0;
|
||||
this->bass_freq_ = 16;
|
||||
this->length_ = 0;
|
||||
this->factor_ = UINT_MAX/2 + 1;;
|
||||
this->buffer_center_ = NULL;
|
||||
this->buffer_size_ = 0;
|
||||
this->sample_rate_ = 0;
|
||||
this->bass_shift_ = 0;
|
||||
this->clock_rate_ = 0;
|
||||
this->bass_freq_ = 16;
|
||||
this->length_ = 0;
|
||||
|
||||
// assumptions code makes about implementation-defined features
|
||||
#ifndef NDEBUG
|
||||
|
@ -47,68 +41,55 @@ void Blip_init( struct Blip_Buffer* this )
|
|||
i = 0x18000;
|
||||
assert( (short) i == -0x8000 );
|
||||
#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 )
|
||||
free( this->buffer_ );
|
||||
}
|
||||
bool const entire_buffer = true;
|
||||
|
||||
void Blip_clear( struct Blip_Buffer* this, int entire_buffer )
|
||||
{
|
||||
this->offset_ = 0;
|
||||
this->offset_ = 0;
|
||||
this->reader_accum_ = 0;
|
||||
this->modified_ = 0;
|
||||
this->modified = false;
|
||||
|
||||
if ( this->buffer_ )
|
||||
{
|
||||
long count = (entire_buffer ? this->buffer_size_ : Blip_samples_avail( this ));
|
||||
memset( this->buffer_, 0, (count + blip_buffer_extra_) * sizeof (buf_t_) );
|
||||
int count = (entire_buffer ? this->buffer_size_ : Blip_samples_avail( this ));
|
||||
memset( this->buffer_, 0, (count + blip_buffer_extra_) * sizeof (delta_t) );
|
||||
}
|
||||
}
|
||||
|
||||
blargg_err_t Blip_set_sample_rate( struct Blip_Buffer* this, long new_rate, int msec )
|
||||
blargg_err_t Blip_set_sample_rate( struct Blip_Buffer* this, int new_rate, int msec )
|
||||
{
|
||||
if ( this->buffer_size_ == silent_buf_size )
|
||||
{
|
||||
assert( 0 );
|
||||
return "Internal (tried to resize Silent_Blip_Buffer)";
|
||||
}
|
||||
// Limit to maximum size that resampled time can represent
|
||||
int max_size = (((blip_resampled_time_t) -1) >> BLIP_BUFFER_ACCURACY) -
|
||||
blip_buffer_extra_ - 64; // TODO: -64 isn't needed
|
||||
int new_size = (new_rate * (msec + 1) + 999) / 1000;
|
||||
if ( new_size > max_size )
|
||||
new_size = max_size;
|
||||
|
||||
// start with maximum length that resampled time can represent
|
||||
long new_size = (ULONG_MAX >> BLIP_BUFFER_ACCURACY) - blip_buffer_extra_ - 64;
|
||||
if ( msec != blip_max_length )
|
||||
{
|
||||
long s = (new_rate * (msec + 1) + 999) / 1000;
|
||||
if ( s < new_size )
|
||||
new_size = s;
|
||||
else
|
||||
assert( 0 ); // fails if requested buffer length exceeds limit
|
||||
}
|
||||
|
||||
if ( new_size > blip_buffer_max )
|
||||
return "Out of memory";
|
||||
|
||||
this->buffer_size_ = new_size;
|
||||
assert( this->buffer_size_ != silent_buf_size );
|
||||
// Resize buffer
|
||||
if ( this->buffer_size_ != new_size ) {
|
||||
this->buffer_center_ = this->buffer_ + BLIP_MAX_QUALITY/2;
|
||||
this->buffer_size_ = new_size;
|
||||
}
|
||||
|
||||
// update things based on the sample rate
|
||||
this->sample_rate_ = new_rate;
|
||||
this->length_ = new_size * 1000 / new_rate - 1;
|
||||
if ( msec )
|
||||
assert( this->length_ == msec ); // ensure length is same as that passed in
|
||||
this->length_ = new_size * 1000 / new_rate - 1;
|
||||
if ( this->clock_rate_ )
|
||||
Blip_set_clock_rate( this, this->clock_rate_ );
|
||||
Blip_bass_freq( this, this->bass_freq_ );
|
||||
|
||||
Blip_clear( this, 1 );
|
||||
Blip_clear( this );
|
||||
|
||||
return 0; // success
|
||||
}
|
||||
|
||||
blip_resampled_time_t Blip_clock_rate_factor( struct Blip_Buffer* this, long rate )
|
||||
blip_resampled_time_t Blip_clock_rate_factor( struct Blip_Buffer* this, int rate )
|
||||
{
|
||||
blip_long factor = (blip_long) ( this->sample_rate_ * (1LL << BLIP_BUFFER_ACCURACY) / rate);
|
||||
int factor = (int) ( this->sample_rate_ * (1LL << BLIP_BUFFER_ACCURACY) / rate);
|
||||
assert( factor > 0 || !this->sample_rate_ ); // fails if clock/output ratio is too large
|
||||
return (blip_resampled_time_t) factor;
|
||||
}
|
||||
|
@ -117,119 +98,111 @@ void Blip_bass_freq( struct Blip_Buffer* this, int freq )
|
|||
{
|
||||
this->bass_freq_ = freq;
|
||||
int shift = 31;
|
||||
if ( freq > 0 )
|
||||
if ( freq > 0 && this->sample_rate_ )
|
||||
{
|
||||
shift = 13;
|
||||
long f = (freq << 16) / this->sample_rate_;
|
||||
int f = (freq << 16) / this->sample_rate_;
|
||||
while ( (f >>= 1) && --shift ) { }
|
||||
}
|
||||
this->bass_shift_ = shift;
|
||||
this->bass_shift_ = shift;
|
||||
}
|
||||
|
||||
void Blip_end_frame( struct Blip_Buffer* this, blip_time_t t )
|
||||
{
|
||||
this->offset_ += t * this->factor_;
|
||||
assert( Blip_samples_avail( this ) <= (long) this->buffer_size_ ); // time outside buffer length
|
||||
assert( Blip_samples_avail( this ) <= (int) this->buffer_size_ ); // time outside buffer length
|
||||
}
|
||||
|
||||
void Blip_remove_silence( struct Blip_Buffer* this, long count )
|
||||
int Blip_count_samples( struct Blip_Buffer* this, blip_time_t t )
|
||||
{
|
||||
assert( count <= Blip_samples_avail( this ) ); // tried to remove more samples than available
|
||||
this->offset_ -= (blip_resampled_time_t) count << BLIP_BUFFER_ACCURACY;
|
||||
blip_resampled_time_t last_sample = Blip_resampled_time( this, t ) >> BLIP_BUFFER_ACCURACY;
|
||||
blip_resampled_time_t first_sample = this->offset_ >> BLIP_BUFFER_ACCURACY;
|
||||
return (int) (last_sample - first_sample);
|
||||
}
|
||||
|
||||
long Blip_count_samples( struct Blip_Buffer* this, blip_time_t t )
|
||||
blip_time_t Blip_count_clocks( struct Blip_Buffer* this, int count )
|
||||
{
|
||||
unsigned long last_sample = Blip_resampled_time( this, t ) >> BLIP_BUFFER_ACCURACY;
|
||||
unsigned long first_sample = this->offset_ >> BLIP_BUFFER_ACCURACY;
|
||||
return (long) (last_sample - first_sample);
|
||||
}
|
||||
|
||||
blip_time_t Blip_count_clocks( struct Blip_Buffer* this, long count )
|
||||
{
|
||||
if ( !this->factor_ )
|
||||
{
|
||||
assert( 0 ); // sample rate and clock rates must be set first
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ( count > this->buffer_size_ )
|
||||
count = this->buffer_size_;
|
||||
blip_resampled_time_t time = (blip_resampled_time_t) count << BLIP_BUFFER_ACCURACY;
|
||||
return (blip_time_t) ((time - this->offset_ + this->factor_ - 1) / this->factor_);
|
||||
}
|
||||
|
||||
void Blip_remove_samples( struct Blip_Buffer* this, long count )
|
||||
void Blip_remove_samples( struct Blip_Buffer* this, int count )
|
||||
{
|
||||
if ( count )
|
||||
{
|
||||
Blip_remove_silence( this, count );
|
||||
|
||||
// copy remaining samples to beginning and clear old samples
|
||||
long remain = Blip_samples_avail( this ) + blip_buffer_extra_;
|
||||
int remain = Blip_samples_avail( this ) + blip_buffer_extra_;
|
||||
memmove( this->buffer_, this->buffer_ + count, remain * sizeof *this->buffer_ );
|
||||
memset( this->buffer_ + remain, 0, count * sizeof *this->buffer_ );
|
||||
}
|
||||
}
|
||||
|
||||
long Blip_read_samples( struct Blip_Buffer* this, blip_sample_t* BLIP_RESTRICT out, long max_samples, int stereo )
|
||||
int Blip_read_samples( struct Blip_Buffer* this, blip_sample_t out_ [], int max_samples, bool stereo )
|
||||
{
|
||||
long count = Blip_samples_avail( this );
|
||||
int count = Blip_samples_avail( this );
|
||||
if ( count > max_samples )
|
||||
count = max_samples;
|
||||
|
||||
if ( count )
|
||||
{
|
||||
int const bass = BLIP_READER_BASS( *this );
|
||||
BLIP_READER_BEGIN( reader, *this );
|
||||
int const bass = this->bass_shift_;
|
||||
delta_t const* reader = this->buffer_ + count;
|
||||
int reader_sum = this->reader_accum_;
|
||||
|
||||
blip_sample_t* BLARGG_RESTRICT out = out_ + count;
|
||||
if ( stereo )
|
||||
out += count;
|
||||
int offset = -count;
|
||||
|
||||
if ( !stereo )
|
||||
{
|
||||
blip_long n;
|
||||
for ( n = count; n; --n )
|
||||
do
|
||||
{
|
||||
blip_long s = BLIP_READER_READ( reader );
|
||||
if ( (blip_sample_t) s != s )
|
||||
s = 0x7FFF - (s >> 24);
|
||||
*out++ = (blip_sample_t) s;
|
||||
BLIP_READER_NEXT( reader, bass );
|
||||
int s = reader_sum >> delta_bits;
|
||||
|
||||
reader_sum -= reader_sum >> bass;
|
||||
reader_sum += reader [offset];
|
||||
|
||||
BLIP_CLAMP( s, s );
|
||||
out [offset] = (blip_sample_t) s;
|
||||
}
|
||||
while ( ++offset );
|
||||
}
|
||||
else
|
||||
{
|
||||
blip_long n;
|
||||
for ( n = count; n; --n )
|
||||
do
|
||||
{
|
||||
blip_long s = BLIP_READER_READ( reader );
|
||||
if ( (blip_sample_t) s != s )
|
||||
s = 0x7FFF - (s >> 24);
|
||||
*out = (blip_sample_t) s;
|
||||
out += 2;
|
||||
BLIP_READER_NEXT( reader, bass );
|
||||
int s = reader_sum >> delta_bits;
|
||||
|
||||
reader_sum -= reader_sum >> bass;
|
||||
reader_sum += reader [offset];
|
||||
|
||||
BLIP_CLAMP( s, s );
|
||||
out [offset * 2] = (blip_sample_t) s;
|
||||
}
|
||||
while ( ++offset );
|
||||
}
|
||||
BLIP_READER_END( reader, *this );
|
||||
|
||||
this->reader_accum_ = reader_sum;
|
||||
|
||||
Blip_remove_samples( this, count );
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
void Blip_mix_samples( struct Blip_Buffer* this, blip_sample_t const* in, long count )
|
||||
void Blip_mix_samples( struct Blip_Buffer* this, blip_sample_t const in [], int count )
|
||||
{
|
||||
if ( this->buffer_size_ == silent_buf_size )
|
||||
{
|
||||
assert( 0 );
|
||||
return;
|
||||
}
|
||||
|
||||
buf_t_* out = this->buffer_ + (this->offset_ >> BLIP_BUFFER_ACCURACY) + blip_widest_impulse_ / 2;
|
||||
delta_t* out = this->buffer_center_ + (this->offset_ >> BLIP_BUFFER_ACCURACY);
|
||||
|
||||
int const sample_shift = blip_sample_bits - 16;
|
||||
int prev = 0;
|
||||
while ( count-- )
|
||||
while ( --count >= 0 )
|
||||
{
|
||||
blip_long s = (blip_long) *in++ << sample_shift;
|
||||
int s = *in++ << sample_shift;
|
||||
*out += s - prev;
|
||||
prev = s;
|
||||
++out;
|
||||
|
@ -237,40 +210,16 @@ void Blip_mix_samples( struct Blip_Buffer* this, blip_sample_t const* in, long
|
|||
*out -= prev;
|
||||
}
|
||||
|
||||
void Blip_set_modified( struct Blip_Buffer* this )
|
||||
{
|
||||
this->modified_ = 1;
|
||||
}
|
||||
|
||||
int Blip_clear_modified( struct Blip_Buffer* this )
|
||||
{
|
||||
int b = this->modified_;
|
||||
this->modified_ = 0;
|
||||
return b;
|
||||
}
|
||||
|
||||
blip_resampled_time_t Blip_resampled_duration( struct Blip_Buffer* this, int t )
|
||||
{
|
||||
return t * this->factor_;
|
||||
}
|
||||
|
||||
blip_resampled_time_t Blip_resampled_time( struct Blip_Buffer* this, blip_time_t t )
|
||||
{
|
||||
return t * this->factor_ + this->offset_;
|
||||
}
|
||||
|
||||
|
||||
// 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 )
|
||||
{
|
||||
this->buf = 0;
|
||||
this->last_amp = 0;
|
||||
this->delta_factor = 0;
|
||||
}
|
||||
|
||||
// Set overall volume of waveform
|
||||
void Synth_volume( struct Blip_Synth* this, int v )
|
||||
{
|
||||
this->delta_factor = (int) (v * (1LL << blip_sample_bits) / FP_ONE_VOLUME);
|
||||
}
|
||||
|
|
|
@ -4,202 +4,238 @@
|
|||
#ifndef BLIP_BUFFER_H
|
||||
#define BLIP_BUFFER_H
|
||||
|
||||
#include <assert.h>
|
||||
#include "blargg_common.h"
|
||||
|
||||
// internal
|
||||
#include "blargg_common.h"
|
||||
#if INT_MAX >= 0x7FFFFFFF
|
||||
typedef int blip_long;
|
||||
typedef unsigned blip_ulong;
|
||||
#else
|
||||
typedef long blip_long;
|
||||
typedef unsigned long blip_ulong;
|
||||
#endif
|
||||
typedef unsigned blip_resampled_time_t;
|
||||
typedef int blip_time_t;
|
||||
typedef int clocks_t;
|
||||
|
||||
// Time unit at source clock rate
|
||||
typedef blip_long blip_time_t;
|
||||
// Output samples are 16-bit signed, with a range of -32768 to 32767
|
||||
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
|
||||
#define BLIP_BUFFER_ACCURACY 16
|
||||
#endif
|
||||
|
||||
// Number bits in phase offset. Fewer than 6 bits (64 phase offsets) results in
|
||||
// noticeable broadband noise when synthesizing high frequency square waves.
|
||||
// Affects size of Blip_Synth objects since they store the waveform directly.
|
||||
// linear interpolation needs 8 bits
|
||||
#ifndef BLIP_PHASE_BITS
|
||||
#define BLIP_PHASE_BITS 8
|
||||
#endif
|
||||
|
||||
// Output samples are 16-bit signed, with a range of -32768 to 32767
|
||||
typedef short blip_sample_t;
|
||||
enum { blip_sample_max = 32767 };
|
||||
enum { blip_widest_impulse_ = 16 };
|
||||
enum { blip_buffer_extra_ = blip_widest_impulse_ + 2 };
|
||||
enum { blip_res = 1 << BLIP_PHASE_BITS };
|
||||
enum { blip_max_length = 0 };
|
||||
enum { blip_default_length = 250 };
|
||||
static int const blip_res = 1 << BLIP_PHASE_BITS;
|
||||
static int const blip_buffer_extra_ = BLIP_MAX_QUALITY + 2;
|
||||
|
||||
// Properties of fixed-point sample position
|
||||
typedef unsigned ufixed_t; // unsigned for more range, optimized shifts
|
||||
enum { fixed_bits = BLIP_BUFFER_ACCURACY }; // bits in fraction
|
||||
enum { fixed_unit = 1 << fixed_bits }; // 1.0 samples
|
||||
|
||||
// Deltas in buffer are fixed-point with this many fraction bits.
|
||||
// Less than 16 for extra range.
|
||||
enum { delta_bits = 14 };
|
||||
|
||||
// Pointer to first committed delta sample
|
||||
typedef int delta_t;
|
||||
|
||||
// Maximun buffer size (48Khz, 50 ms)
|
||||
enum { blip_buffer_max = 2466 };
|
||||
enum { blip_sample_bits = 30 };
|
||||
|
||||
typedef blip_time_t buf_t_;
|
||||
/* typedef const char* blargg_err_t; */
|
||||
typedef blip_ulong blip_resampled_time_t;
|
||||
|
||||
struct Blip_Buffer {
|
||||
blip_ulong factor_;
|
||||
blip_resampled_time_t offset_;
|
||||
buf_t_ buffer_ [blip_buffer_max];
|
||||
blip_long buffer_size_;
|
||||
blip_long reader_accum_;
|
||||
int bass_shift_;
|
||||
unsigned factor_;
|
||||
ufixed_t offset_;
|
||||
delta_t* buffer_center_;
|
||||
int buffer_size_;
|
||||
int reader_accum_;
|
||||
int bass_shift_;
|
||||
int bass_freq_;
|
||||
int sample_rate_;
|
||||
int clock_rate_;
|
||||
int length_;
|
||||
bool modified;
|
||||
|
||||
long sample_rate_;
|
||||
long clock_rate_;
|
||||
int bass_freq_;
|
||||
int length_;
|
||||
int modified_;
|
||||
delta_t buffer_ [blip_buffer_max];
|
||||
};
|
||||
|
||||
// not documented yet
|
||||
void Blip_set_modified( struct Blip_Buffer* this );
|
||||
int Blip_clear_modified( struct Blip_Buffer* this );
|
||||
void Blip_remove_silence( struct Blip_Buffer* this, long count );
|
||||
blip_resampled_time_t Blip_resampled_duration( struct Blip_Buffer* this, int t );
|
||||
blip_resampled_time_t Blip_resampled_time( struct Blip_Buffer* this, blip_time_t t );
|
||||
blip_resampled_time_t Blip_clock_rate_factor( struct Blip_Buffer* this, long clock_rate );
|
||||
// Blip_Buffer_ implementation
|
||||
static inline ufixed_t to_fixed( struct Blip_Buffer *this, clocks_t t )
|
||||
{
|
||||
return t * this->factor_ + this->offset_;
|
||||
}
|
||||
|
||||
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
|
||||
void Blip_init( struct Blip_Buffer* this );
|
||||
|
||||
// Stops (clear) Blip_Buffer structure
|
||||
void Blip_stop( struct Blip_Buffer* this );
|
||||
|
||||
// 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
|
||||
// Sets output sample rate and resizes and clears sample buffer
|
||||
blargg_err_t Blip_set_sample_rate( struct Blip_Buffer* this, int samples_per_sec, int msec_length );
|
||||
|
||||
// Current output sample rate
|
||||
static inline long Blip_sample_rate( struct Blip_Buffer* this )
|
||||
static inline int Blip_sample_rate( struct Blip_Buffer* this )
|
||||
{
|
||||
return this->sample_rate_;
|
||||
}
|
||||
|
||||
// Length of buffer, in milliseconds
|
||||
static inline int Blip_length( struct Blip_Buffer* this )
|
||||
// Sets number of source time units per second
|
||||
blip_resampled_time_t Blip_clock_rate_factor( struct Blip_Buffer* this, int clock_rate );
|
||||
static inline void Blip_set_clock_rate( struct Blip_Buffer* this, int clocks_per_sec )
|
||||
{
|
||||
return this->length_;
|
||||
this->factor_ = Blip_clock_rate_factor( this, this->clock_rate_ = clocks_per_sec );
|
||||
}
|
||||
|
||||
// Number of source time units per second
|
||||
static inline long Blip_clock_rate( struct Blip_Buffer* this )
|
||||
static inline int Blip_clock_rate( struct Blip_Buffer* this )
|
||||
{
|
||||
return this->clock_rate_;
|
||||
}
|
||||
|
||||
static inline int Blip_length( struct Blip_Buffer* this )
|
||||
{
|
||||
return this->length_;
|
||||
}
|
||||
|
||||
// Clears buffer and removes all samples
|
||||
void Blip_clear( struct Blip_Buffer* this );
|
||||
|
||||
// Use Blip_Synth to add waveform to buffer
|
||||
|
||||
// Resamples to time t, then subtracts t from current time. Appends result of resampling
|
||||
// to buffer for reading.
|
||||
void Blip_end_frame( struct Blip_Buffer* this, blip_time_t time ) ICODE_ATTR;
|
||||
|
||||
|
||||
// Reads at most n samples to out [0 to n-1] and returns number actually read. If stereo
|
||||
// is true, writes to out [0], out [2], out [4] etc. instead.
|
||||
int Blip_read_samples( struct Blip_Buffer* this, blip_sample_t out [], int n, bool stereo ) ICODE_ATTR;
|
||||
|
||||
|
||||
// More features
|
||||
|
||||
// Sets flag that tells some Multi_Buffer types that sound was added to buffer,
|
||||
// so they know that it needs to be mixed in. Only needs to be called once
|
||||
// per time frame that sound was added. Not needed if not using Multi_Buffer.
|
||||
static inline void Blip_set_modified( struct Blip_Buffer* this ) { this->modified = true; }
|
||||
|
||||
// Set frequency high-pass filter frequency, where higher values reduce bass more
|
||||
void Blip_bass_freq( struct Blip_Buffer* this, int frequency );
|
||||
|
||||
// Number of samples delay from synthesis to samples read out
|
||||
static inline int Blip_output_latency( void )
|
||||
|
||||
// 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
|
||||
// false, just clears out any samples waiting rather than the entire buffer.
|
||||
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 )
|
||||
// Converts clock time since beginning of current time frame to resampled time
|
||||
static inline blip_resampled_time_t Blip_resampled_time( struct Blip_Buffer* this, blip_time_t t )
|
||||
{
|
||||
return (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
|
||||
// by finding the difference between the maximum and minimum expected
|
||||
// amplitudes (max - min).
|
||||
|
||||
typedef char coeff_t;
|
||||
|
||||
struct Blip_Synth {
|
||||
struct Blip_Buffer* buf;
|
||||
int last_amp;
|
||||
int delta_factor;
|
||||
int last_amp;
|
||||
struct Blip_Buffer* buf;
|
||||
};
|
||||
|
||||
// Blip_Synth_
|
||||
void volume_unit( struct Blip_Synth* this, int new_unit );
|
||||
|
||||
// Initializes Blip_Synth structure
|
||||
void Synth_init( struct Blip_Synth* this );
|
||||
|
||||
// Set overall volume of waveform
|
||||
void Synth_volume( struct Blip_Synth* this, int v );
|
||||
// Sets volume of amplitude delta unit
|
||||
static inline void Synth_volume( struct Blip_Synth* this, int v )
|
||||
{
|
||||
volume_unit( this, v ); // new_unit = 1 / range * v
|
||||
}
|
||||
|
||||
// Get/set Blip_Buffer used for output
|
||||
const struct Blip_Buffer* Synth_output( struct Blip_Synth* this );
|
||||
|
||||
// Low-level interface
|
||||
|
||||
#if defined (__GNUC__) || _MSC_VER >= 1100
|
||||
#define BLIP_RESTRICT __restrict
|
||||
#else
|
||||
#define BLIP_RESTRICT
|
||||
#endif
|
||||
// (in >> sh & mask) * mul
|
||||
#define BLIP_SH_AND_MUL( in, sh, mask, mul ) \
|
||||
((int) (in) / ((1U << (sh)) / (mul)) & (unsigned) ((mask) * (mul)))
|
||||
|
||||
// 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,
|
||||
int delta, struct Blip_Buffer* blip_buf )
|
||||
{
|
||||
// Fails if time is beyond end of Blip_Buffer, due to a bug in caller code or the
|
||||
// need for a longer buffer as set by set_sample_rate().
|
||||
assert( (blip_long) (time >> BLIP_BUFFER_ACCURACY) < blip_buf->buffer_size_ );
|
||||
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));
|
||||
int const half_width = 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.
|
||||
// Alternate way of delaying multiply by delta_factor results in worse
|
||||
// sub-sample resolution.
|
||||
blip_long right = (delta >> BLIP_PHASE_BITS) * phase;
|
||||
int right = (delta >> BLIP_PHASE_BITS) * phase;
|
||||
#ifdef BLIP_BUFFER_NOINTERP
|
||||
// TODO: remove? (just a hack to see how it sounds)
|
||||
right = 0;
|
||||
#endif
|
||||
left -= right;
|
||||
right += buf [1];
|
||||
|
||||
|
@ -213,33 +249,38 @@ static inline void Synth_update( struct Blip_Synth* this, blip_time_t t, int amp
|
|||
{
|
||||
int delta = amp - this->last_amp;
|
||||
this->last_amp = amp;
|
||||
Synth_offset_resampled( this, t * this->buf->factor_ + this->buf->offset_, delta, this->buf );
|
||||
Synth_offset_resampled( this, to_fixed(this->buf, t), delta, this->buf );
|
||||
}
|
||||
|
||||
// Add an amplitude transition of specified delta, optionally into specified buffer
|
||||
// rather than the one set with output(). Delta can be positive or negative.
|
||||
// The actual change in amplitude is delta * (volume / range)
|
||||
static inline void Synth_offset( struct Blip_Synth* this, blip_time_t t, int delta, struct Blip_Buffer* buf )
|
||||
{
|
||||
Synth_offset_resampled( this, t * buf->factor_ + buf->offset_, delta, buf );
|
||||
}
|
||||
|
||||
// Same as offset(), except code is inlined for higher performance
|
||||
// Adds amplitude transition at time t. Delta can be positive or negative.
|
||||
// The actual change in amplitude is delta * volume.
|
||||
static inline void Synth_offset_inline( struct Blip_Synth* this, blip_time_t t, int delta, struct Blip_Buffer* buf )
|
||||
{
|
||||
Synth_offset_resampled( this, t * buf->factor_ + buf->offset_, delta, buf );
|
||||
Synth_offset_resampled( this, to_fixed(buf, t), delta, buf );
|
||||
}
|
||||
|
||||
#define Synth_offset( synth, time, delta, buf ) Synth_offset_inline( synth, time, delta, buf )
|
||||
|
||||
// Number of bits in raw sample that covers normal output range. Less than 32 bits to give
|
||||
// extra amplitude range. That is,
|
||||
// +1 << (blip_sample_bits-1) = +1.0
|
||||
// -1 << (blip_sample_bits-1) = -1.0
|
||||
static int const blip_sample_bits = 30;
|
||||
|
||||
// Optimized reading from Blip_Buffer, for use in custom sample output
|
||||
|
||||
// Begin reading from buffer. Name should be unique to the current block.
|
||||
#define BLIP_READER_BEGIN( name, blip_buffer ) \
|
||||
buf_t_* BLIP_RESTRICT name##_reader_buf = (blip_buffer).buffer_;\
|
||||
blip_long name##_reader_accum = (blip_buffer).reader_accum_
|
||||
const delta_t* BLARGG_RESTRICT name##_reader_buf = (blip_buffer).buffer_;\
|
||||
int name##_reader_accum = (blip_buffer).reader_accum_
|
||||
|
||||
// Get value to pass to BLIP_READER_NEXT()
|
||||
#define BLIP_READER_BASS( blip_buffer ) ((blip_buffer).bass_shift_)
|
||||
|
||||
// Constant value to use instead of BLIP_READER_BASS(), for slightly more optimal
|
||||
// code at the cost of having no bass_freq() functionality
|
||||
static int const blip_reader_default_bass = 9;
|
||||
|
||||
// Current sample
|
||||
#define BLIP_READER_READ( name ) (name##_reader_accum >> (blip_sample_bits - 16))
|
||||
|
||||
|
@ -262,18 +303,33 @@ static inline void Synth_offset_inline( struct Blip_Synth* this, blip_time_t t,
|
|||
name##_reader_accum += name##_reader_buf [(idx)];\
|
||||
}
|
||||
|
||||
#define BLIP_READER_NEXT_RAW_IDX_( name, bass, idx ) {\
|
||||
name##_reader_accum -= name##_reader_accum >> (bass);\
|
||||
name##_reader_accum +=\
|
||||
*(delta_t const*) ((char const*) name##_reader_buf + (idx));\
|
||||
}
|
||||
|
||||
//// BLIP_CLAMP
|
||||
|
||||
#if defined (_M_IX86) || defined (_M_IA64) || defined (__i486__) || \
|
||||
defined (__x86_64__) || defined (__ia64__) || defined (__i386__)
|
||||
#define BLIP_X86 1
|
||||
#define BLIP_CLAMP_( in ) in < -0x8000 || 0x7FFF < in
|
||||
#if ARM_ARCH >= 6
|
||||
#define BLIP_CLAMP( sample, out ) \
|
||||
({ \
|
||||
asm ("ssat %0, #16, %1" \
|
||||
: "=r" ( out ) : "r"( sample ) ); \
|
||||
out; \
|
||||
})
|
||||
#else
|
||||
#define BLIP_CLAMP_( in ) (blip_sample_t) in != in
|
||||
#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
|
||||
|
||||
// Clamp sample to blip_sample_t range
|
||||
#define BLIP_CLAMP( sample, out )\
|
||||
{ if ( BLIP_CLAMP_( (sample) ) ) (out) = ((sample) >> 31) ^ 0x7FFF; }
|
||||
|
||||
#endif
|
||||
|
|
|
@ -20,7 +20,7 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
|
|||
#define LOG_MEM( addr, str, data ) data
|
||||
#endif
|
||||
|
||||
int Read_mem( struct Gbs_Emu* this, addr_t addr )
|
||||
int read_mem( struct Gbs_Emu* this, addr_t addr )
|
||||
{
|
||||
int result = *Cpu_get_code( &this->cpu, addr );
|
||||
if ( (unsigned) (addr - io_addr) < io_size )
|
||||
|
@ -29,19 +29,19 @@ int Read_mem( struct Gbs_Emu* this, addr_t addr )
|
|||
return LOG_MEM( addr, ">", result );
|
||||
}
|
||||
|
||||
static inline void Write_io_inline( struct Gbs_Emu* this, int offset, int data, int base )
|
||||
static inline void write_io_inline( struct Gbs_Emu* this, int offset, int data, int base )
|
||||
{
|
||||
if ( (unsigned) (offset - (io_addr - base)) < io_size )
|
||||
Apu_write_register( &this->apu, Time( this ), offset + base, data & 0xFF );
|
||||
else if ( (unsigned) (offset - (0xFF06 - base)) < 2 )
|
||||
Update_timer( this );
|
||||
update_timer( this );
|
||||
else if ( offset == io_base - base )
|
||||
this->ram [base - ram_addr + offset] = 0; // keep joypad return value 0
|
||||
else
|
||||
this->ram [base - ram_addr + offset] = 0xFF;
|
||||
}
|
||||
|
||||
void Write_mem( struct Gbs_Emu* this, addr_t addr, int data )
|
||||
void write_mem( struct Gbs_Emu* this, addr_t addr, int data )
|
||||
{
|
||||
(void) LOG_MEM( addr, "<", data );
|
||||
|
||||
|
@ -52,11 +52,11 @@ void Write_mem( struct Gbs_Emu* this, addr_t addr, int data )
|
|||
|
||||
offset -= 0xE000 - ram_addr;
|
||||
if ( (unsigned) offset < 0x1F80 )
|
||||
Write_io_inline( this, offset, data, 0xE000 );
|
||||
write_io_inline( this, offset, data, 0xE000 );
|
||||
}
|
||||
else if ( (unsigned) (offset - (0x2000 - ram_addr)) < 0x2000 )
|
||||
{
|
||||
Set_bank( this, data & 0xFF );
|
||||
set_bank( this, data & 0xFF );
|
||||
}
|
||||
#ifndef NDEBUG
|
||||
else if ( unsigned (addr - 0x8000) < 0x2000 || unsigned (addr - 0xE000) < 0x1F00 )
|
||||
|
@ -66,21 +66,21 @@ void Write_mem( struct Gbs_Emu* this, addr_t addr, int data )
|
|||
#endif
|
||||
}
|
||||
|
||||
static void Write_io_( struct Gbs_Emu* this, int offset, int data )
|
||||
static void write_io_( struct Gbs_Emu* this, int offset, int data )
|
||||
{
|
||||
Write_io_inline( this, offset, data, io_base );
|
||||
write_io_inline( this, offset, data, io_base );
|
||||
}
|
||||
|
||||
static inline void Write_io( struct Gbs_Emu* this, int offset, int data )
|
||||
static inline void write_io( struct Gbs_Emu* this, int offset, int data )
|
||||
{
|
||||
(void) LOG_MEM( offset + io_base, "<", data );
|
||||
|
||||
this->ram [io_base - ram_addr + offset] = data;
|
||||
if ( (unsigned) offset < 0x80 )
|
||||
Write_io_( this, offset, data );
|
||||
write_io_( this, offset, data );
|
||||
}
|
||||
|
||||
static int Read_io( struct Gbs_Emu* this, int offset )
|
||||
static int read_io( struct Gbs_Emu* this, int offset )
|
||||
{
|
||||
int const io_base = 0xFF00;
|
||||
int result = this->ram [io_base - ram_addr + offset];
|
||||
|
@ -106,14 +106,14 @@ static int Read_io( struct Gbs_Emu* this, int offset )
|
|||
check( out == Read_mem( emu, addr ) );\
|
||||
}
|
||||
|
||||
#define READ_MEM( emu, addr ) Read_mem( emu, addr )
|
||||
#define WRITE_MEM( emu, addr, data ) Write_mem( emu, addr, data )
|
||||
#define READ_MEM( emu, addr ) read_mem( emu, addr )
|
||||
#define WRITE_MEM( emu, addr, data ) write_mem( emu, addr, data )
|
||||
|
||||
#define WRITE_IO( emu, addr, data ) Write_io( emu, addr, data )
|
||||
#define READ_IO( emu, addr, out ) out = Read_io( emu, addr )
|
||||
#define WRITE_IO( emu, addr, data ) write_io( emu, addr, data )
|
||||
#define READ_IO( emu, addr, out ) out = read_io( emu, addr )
|
||||
|
||||
#define CPU_BEGIN \
|
||||
void Run_cpu( struct Gbs_Emu* this )\
|
||||
void run_cpu( struct Gbs_Emu* this )\
|
||||
{ \
|
||||
struct Gb_Cpu* cpu = &this->cpu;
|
||||
#include "gb_cpu_run.h"
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
#include "gbs_emu.h"
|
||||
|
||||
#include "blargg_endian.h"
|
||||
#include "blargg_source.h"
|
||||
|
||||
/* Copyright (C) 2003-2006 Shay Green. this module is free software; you
|
||||
can redistribute it and/or modify it under the terms of the GNU Lesser
|
||||
|
@ -16,30 +15,17 @@ details. You should have received a copy of the GNU Lesser General Public
|
|||
License along with this module; if not, write to the Free Software Foundation,
|
||||
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
|
||||
|
||||
#include "blargg_source.h"
|
||||
|
||||
const char gme_wrong_file_type [] = "Wrong file type for this emulator";
|
||||
|
||||
int const idle_addr = 0xF00D;
|
||||
int const tempo_unit = 16;
|
||||
|
||||
int const stereo = 2; // number of channels for stereo
|
||||
int const silence_max = 6; // seconds
|
||||
int const silence_threshold = 0x10;
|
||||
long const fade_block_size = 512;
|
||||
int const fade_shift = 8; // fade ends with gain at 1.0 / (1 << fade_shift)
|
||||
|
||||
static void clear_track_vars( struct Gbs_Emu* this )
|
||||
{
|
||||
this->current_track_ = -1;
|
||||
this->out_time = 0;
|
||||
this->emu_time = 0;
|
||||
this->emu_track_ended_ = true;
|
||||
this->track_ended = true;
|
||||
this->fade_start = (blargg_long)(LONG_MAX / 2 + 1);
|
||||
this->fade_step = 1;
|
||||
this->silence_time = 0;
|
||||
this->silence_count = 0;
|
||||
this->buf_remain = 0;
|
||||
this->current_track_ = -1;
|
||||
track_stop( &this->track_filter );
|
||||
}
|
||||
|
||||
void Gbs_init( struct Gbs_Emu* this )
|
||||
|
@ -50,11 +36,13 @@ void Gbs_init( struct Gbs_Emu* this )
|
|||
|
||||
// Unload
|
||||
this->header.timer_mode = 0;
|
||||
clear_track_vars( this );
|
||||
|
||||
this->ignore_silence = false;
|
||||
this->silence_lookahead = 6;
|
||||
this->max_initial_silence = 21;
|
||||
// defaults
|
||||
this->tfilter = *track_get_setup( &this->track_filter );
|
||||
this->tfilter.max_initial = 21;
|
||||
this->tfilter.lookahead = 6;
|
||||
this->track_filter.silence_ignored_ = false;
|
||||
|
||||
Sound_set_gain( this, (int)(FP_ONE_GAIN*1.2) );
|
||||
|
||||
Rom_init( &this->rom, 0x4000 );
|
||||
|
@ -67,6 +55,11 @@ void Gbs_init( struct Gbs_Emu* this )
|
|||
|
||||
// Reduce apu sound clicks?
|
||||
Apu_reduce_clicks( &this->apu, true );
|
||||
|
||||
// clears fields
|
||||
this->voice_count_ = 0;
|
||||
this->voice_types_ = 0;
|
||||
clear_track_vars( this );
|
||||
}
|
||||
|
||||
static blargg_err_t check_gbs_header( void const* header )
|
||||
|
@ -78,11 +71,12 @@ static blargg_err_t check_gbs_header( void const* header )
|
|||
|
||||
// Setup
|
||||
|
||||
blargg_err_t Gbs_load( struct Gbs_Emu* this, void* data, long size )
|
||||
blargg_err_t Gbs_load_mem( struct Gbs_Emu* this, void* data, long size )
|
||||
{
|
||||
// Unload
|
||||
this->header.timer_mode = 0;
|
||||
this->voice_count_ = 0;
|
||||
this->track_count = 0;
|
||||
this->m3u.size = 0;
|
||||
clear_track_vars( this );
|
||||
|
||||
|
@ -112,20 +106,24 @@ blargg_err_t Gbs_load( struct Gbs_Emu* this, void* data, long size )
|
|||
Rom_set_addr( &this->rom, load_addr );
|
||||
|
||||
this->voice_count_ = osc_count;
|
||||
static int const types [osc_count] = {
|
||||
wave_type+1, wave_type+2, wave_type+3, mixed_type+1
|
||||
};
|
||||
this->voice_types_ = types;
|
||||
|
||||
Apu_volume( &this->apu, this->gain_ );
|
||||
|
||||
// Change clock rate & setup buffer
|
||||
this->clock_rate_ = 4194304;
|
||||
Buffer_clock_rate( &this->stereo_buf, 4194304 );
|
||||
RETURN_ERR( Buffer_set_channel_count( &this->stereo_buf, this->voice_count_, this->voice_types_ ) );
|
||||
this->buf_changed_count = Buffer_channels_changed_count( &this->stereo_buf );
|
||||
|
||||
// Post load
|
||||
Sound_set_tempo( this, this->tempo_ );
|
||||
|
||||
// Remute voices
|
||||
Sound_mute_voices( this, this->mute_mask_ );
|
||||
|
||||
// Reset track count
|
||||
// Set track count
|
||||
this->track_count = this->header.track_count;
|
||||
return 0;
|
||||
}
|
||||
|
@ -134,7 +132,7 @@ blargg_err_t Gbs_load( struct Gbs_Emu* this, void* data, long size )
|
|||
|
||||
// see gb_cpu_io.h for read/write functions
|
||||
|
||||
void Set_bank( struct Gbs_Emu* this, int n )
|
||||
void set_bank( struct Gbs_Emu* this, int n )
|
||||
{
|
||||
addr_t addr = mask_addr( n * this->rom.bank_size, this->rom.mask );
|
||||
if ( addr == 0 && this->rom.size > this->rom.bank_size )
|
||||
|
@ -142,7 +140,7 @@ void Set_bank( struct Gbs_Emu* this, int n )
|
|||
Cpu_map_code( &this->cpu, this->rom.bank_size, this->rom.bank_size, Rom_at_addr( &this->rom, addr ) );
|
||||
}
|
||||
|
||||
void Update_timer( struct Gbs_Emu* this )
|
||||
void update_timer( struct Gbs_Emu* this )
|
||||
{
|
||||
this->play_period = 70224 / tempo_unit; /// 59.73 Hz
|
||||
|
||||
|
@ -161,21 +159,21 @@ void Update_timer( struct Gbs_Emu* this )
|
|||
|
||||
// Jumps to routine, given pointer to address in file header. Pushes idle_addr
|
||||
// as return address, NOT old PC.
|
||||
void Jsr_then_stop( struct Gbs_Emu* this, byte const addr [] )
|
||||
void jsr_then_stop( struct Gbs_Emu* this, byte const addr [] )
|
||||
{
|
||||
check( this->cpu.r.sp == get_le16( this->header.stack_ptr ) );
|
||||
this->cpu.r.pc = get_le16( addr );
|
||||
Write_mem( this, --this->cpu.r.sp, idle_addr >> 8 );
|
||||
Write_mem( this, --this->cpu.r.sp, idle_addr );
|
||||
write_mem( this, --this->cpu.r.sp, idle_addr >> 8 );
|
||||
write_mem( this, --this->cpu.r.sp, idle_addr );
|
||||
}
|
||||
|
||||
static blargg_err_t Run_until( struct Gbs_Emu* this, int end )
|
||||
static blargg_err_t run_until( struct Gbs_Emu* this, int end )
|
||||
{
|
||||
this->end_time = end;
|
||||
Cpu_set_time( &this->cpu, Cpu_time( &this->cpu ) - end );
|
||||
while ( true )
|
||||
{
|
||||
Run_cpu( this );
|
||||
run_cpu( this );
|
||||
if ( Cpu_time( &this->cpu ) >= 0 )
|
||||
break;
|
||||
|
||||
|
@ -190,7 +188,7 @@ static blargg_err_t Run_until( struct Gbs_Emu* this, int end )
|
|||
if ( Cpu_time( &this->cpu ) < this->next_play - this->end_time )
|
||||
Cpu_set_time( &this->cpu, this->next_play - this->end_time );
|
||||
this->next_play += this->play_period;
|
||||
Jsr_then_stop( this, this->header.play_addr );
|
||||
jsr_then_stop( this, this->header.play_addr );
|
||||
}
|
||||
else if ( this->cpu.r.pc > 0xFFFF )
|
||||
{
|
||||
|
@ -208,9 +206,9 @@ static blargg_err_t Run_until( struct Gbs_Emu* this, int end )
|
|||
return 0;
|
||||
}
|
||||
|
||||
static blargg_err_t End_frame( struct Gbs_Emu* this, int end )
|
||||
static blargg_err_t end_frame( struct Gbs_Emu* this, int end )
|
||||
{
|
||||
RETURN_ERR( Run_until( this, end ) );
|
||||
RETURN_ERR( run_until( this, end ) );
|
||||
|
||||
this->next_play -= end;
|
||||
if ( this->next_play < 0 ) // happens when play routine takes too long
|
||||
|
@ -226,16 +224,19 @@ static blargg_err_t End_frame( struct Gbs_Emu* this, int end )
|
|||
return 0;
|
||||
}
|
||||
|
||||
blargg_err_t Run_clocks( struct Gbs_Emu* this, blip_time_t duration )
|
||||
blargg_err_t run_clocks( struct Gbs_Emu* this, blip_time_t duration )
|
||||
{
|
||||
return End_frame( this, duration );
|
||||
return end_frame( this, duration );
|
||||
}
|
||||
|
||||
static blargg_err_t play_( struct Gbs_Emu* this, long count, sample_t* out )
|
||||
blargg_err_t play_( void* emu, int count, sample_t* out )
|
||||
{
|
||||
long remain = count;
|
||||
struct Gbs_Emu* this = (struct Gbs_Emu*) emu;
|
||||
|
||||
int remain = count;
|
||||
while ( remain )
|
||||
{
|
||||
Buffer_disable_immediate_removal( &this->stereo_buf );
|
||||
remain -= Buffer_read_samples( &this->stereo_buf, &out [count - remain], remain );
|
||||
if ( remain )
|
||||
{
|
||||
|
@ -247,8 +248,8 @@ static blargg_err_t play_( struct Gbs_Emu* this, long count, sample_t* out )
|
|||
Sound_mute_voices( this, this->mute_mask_ );
|
||||
}
|
||||
int msec = Buffer_length( &this->stereo_buf );
|
||||
blip_time_t clocks_emulated = (blargg_long) msec * this->clock_rate_ / 1000;
|
||||
RETURN_ERR( Run_clocks( this, clocks_emulated ) );
|
||||
blip_time_t clocks_emulated = msec * this->clock_rate_ / 1000 - 100;
|
||||
RETURN_ERR( run_clocks( this, clocks_emulated ) );
|
||||
assert( clocks_emulated );
|
||||
Buffer_end_frame( &this->stereo_buf, clocks_emulated );
|
||||
}
|
||||
|
@ -256,7 +257,7 @@ static blargg_err_t play_( struct Gbs_Emu* this, long count, sample_t* out )
|
|||
return 0;
|
||||
}
|
||||
|
||||
blargg_err_t Gbs_set_sample_rate( struct Gbs_Emu* this, long rate )
|
||||
blargg_err_t Gbs_set_sample_rate( struct Gbs_Emu* this, int rate )
|
||||
{
|
||||
require( !this->sample_rate_ ); // sample rate can't be changed once set
|
||||
Buffer_init( &this->stereo_buf );
|
||||
|
@ -266,6 +267,8 @@ blargg_err_t Gbs_set_sample_rate( struct Gbs_Emu* this, long rate )
|
|||
Buffer_bass_freq( &this->stereo_buf, 300 );
|
||||
|
||||
this->sample_rate_ = rate;
|
||||
RETURN_ERR( track_init( &this->track_filter, this ) );
|
||||
this->tfilter.max_silence = 6 * stereo * this->sample_rate_;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -296,7 +299,7 @@ void Sound_mute_voices( struct Gbs_Emu* this, int mask )
|
|||
}
|
||||
else
|
||||
{
|
||||
struct channel_t ch = Buffer_channel( &this->stereo_buf );
|
||||
struct channel_t ch = Buffer_channel( &this->stereo_buf, i );
|
||||
assert( (ch.center && ch.left && ch.right) ||
|
||||
(!ch.center && !ch.left && !ch.right) ); // all or nothing
|
||||
Apu_set_output( &this->apu, i, ch.center, ch.left, ch.right );
|
||||
|
@ -315,10 +318,9 @@ void Sound_set_tempo( struct Gbs_Emu* this, int t )
|
|||
|
||||
this->tempo = (int) ((tempo_unit * FP_ONE_TEMPO) / t);
|
||||
Apu_set_tempo( &this->apu, t );
|
||||
Update_timer( this );
|
||||
update_timer( this );
|
||||
}
|
||||
|
||||
void fill_buf( struct Gbs_Emu* this );
|
||||
blargg_err_t Gbs_start_track( struct Gbs_Emu* this, int track )
|
||||
{
|
||||
clear_track_vars( this );
|
||||
|
@ -330,7 +332,6 @@ blargg_err_t Gbs_start_track( struct Gbs_Emu* this, int track )
|
|||
}
|
||||
|
||||
this->current_track_ = track;
|
||||
|
||||
Buffer_clear( &this->stereo_buf );
|
||||
|
||||
// Reset APU to state expected by most rips
|
||||
|
@ -364,268 +365,88 @@ blargg_err_t Gbs_start_track( struct Gbs_Emu* this, int track )
|
|||
Cpu_reset( &this->cpu, this->rom.unmapped );
|
||||
Cpu_map_code( &this->cpu, ram_addr, 0x10000 - ram_addr, this->ram );
|
||||
Cpu_map_code( &this->cpu, 0, this->rom.bank_size, Rom_at_addr( &this->rom, 0 ) );
|
||||
Set_bank( this, this->rom.size > this->rom.bank_size );
|
||||
set_bank( this, this->rom.size > this->rom.bank_size );
|
||||
|
||||
Update_timer( this );
|
||||
update_timer( this );
|
||||
this->next_play = this->play_period;
|
||||
this->cpu.r.rp.fa = track;
|
||||
this->cpu.r.sp = get_le16( this->header.stack_ptr );
|
||||
this->cpu_time = 0;
|
||||
Jsr_then_stop( this, this->header.init_addr );
|
||||
jsr_then_stop( this, this->header.init_addr );
|
||||
|
||||
this->emu_track_ended_ = false;
|
||||
this->track_ended = false;
|
||||
// convert filter times to samples
|
||||
struct setup_t s = this->tfilter;
|
||||
s.max_initial *= this->sample_rate_ * stereo;
|
||||
#ifdef GME_DISABLE_SILENCE_LOOKAHEAD
|
||||
s.lookahead = 1;
|
||||
#endif
|
||||
track_setup( &this->track_filter, &s );
|
||||
|
||||
if ( !this->ignore_silence )
|
||||
{
|
||||
// play until non-silence or end of track
|
||||
long end;
|
||||
for ( end = this->max_initial_silence * stereo * this->sample_rate_; this->emu_time < end; )
|
||||
{
|
||||
fill_buf( this );
|
||||
if ( this->buf_remain | (int) this->emu_track_ended_ )
|
||||
break;
|
||||
}
|
||||
|
||||
this->emu_time = this->buf_remain;
|
||||
this->out_time = 0;
|
||||
this->silence_time = 0;
|
||||
this->silence_count = 0;
|
||||
}
|
||||
/* return track_ended() ? warning() : 0; */
|
||||
return 0;
|
||||
return track_start( &this->track_filter );
|
||||
}
|
||||
|
||||
|
||||
// Track
|
||||
|
||||
static blargg_long msec_to_samples( blargg_long msec, long sample_rate )
|
||||
static int msec_to_samples( int msec, int sample_rate )
|
||||
{
|
||||
blargg_long sec = msec / 1000;
|
||||
int sec = msec / 1000;
|
||||
msec -= sec * 1000;
|
||||
return (sec * sample_rate + msec * sample_rate / 1000) * stereo;
|
||||
}
|
||||
|
||||
long Track_tell( struct Gbs_Emu* this )
|
||||
int Track_tell( struct Gbs_Emu* this )
|
||||
{
|
||||
blargg_long rate = this->sample_rate_ * stereo;
|
||||
blargg_long sec = this->out_time / rate;
|
||||
return sec * 1000 + (this->out_time - sec * rate) * 1000 / rate;
|
||||
int rate = this->sample_rate_ * stereo;
|
||||
int sec = track_sample_count( &this->track_filter ) / rate;
|
||||
return sec * 1000 + (track_sample_count( &this->track_filter ) - sec * rate) * 1000 / rate;
|
||||
}
|
||||
|
||||
blargg_err_t Track_seek( struct Gbs_Emu* this, long msec )
|
||||
blargg_err_t Track_seek( struct Gbs_Emu* this, int msec )
|
||||
{
|
||||
blargg_long time = msec_to_samples( msec, this->sample_rate_ );
|
||||
if ( time < this->out_time )
|
||||
RETURN_ERR( Gbs_start_track( this, this->current_track_ ) );
|
||||
return Track_skip( this, time - this->out_time );
|
||||
int time = msec_to_samples( msec, this->sample_rate_ );
|
||||
if ( time < track_sample_count( &this->track_filter ) )
|
||||
RETURN_ERR( Gbs_start_track( this, this->current_track_ ) );
|
||||
return Track_skip( this, time - track_sample_count( &this->track_filter ) );
|
||||
}
|
||||
|
||||
static blargg_err_t skip_( struct Gbs_Emu* this, long count )
|
||||
blargg_err_t skip_( void* emu, int count )
|
||||
{
|
||||
struct Gbs_Emu* this = (struct Gbs_Emu*) emu;
|
||||
|
||||
// for long skip, mute sound
|
||||
const long threshold = 30000;
|
||||
const int threshold = 32768;
|
||||
if ( count > threshold )
|
||||
{
|
||||
int saved_mute = this->mute_mask_;
|
||||
Sound_mute_voices( this, ~0 );
|
||||
|
||||
while ( count > threshold / 2 && !this->emu_track_ended_ )
|
||||
{
|
||||
RETURN_ERR( play_( this, buf_size, this->buf ) );
|
||||
count -= buf_size;
|
||||
}
|
||||
int n = count - threshold/2;
|
||||
n &= ~(2048-1); // round to multiple of 2048
|
||||
count -= n;
|
||||
RETURN_ERR( skippy_( &this->track_filter, n ) );
|
||||
|
||||
Sound_mute_voices( this, saved_mute );
|
||||
}
|
||||
|
||||
while ( count && !this->emu_track_ended_ )
|
||||
{
|
||||
long n = buf_size;
|
||||
if ( n > count )
|
||||
n = count;
|
||||
count -= n;
|
||||
RETURN_ERR( play_( this, n, this->buf ) );
|
||||
}
|
||||
return 0;
|
||||
return skippy_( &this->track_filter, count );
|
||||
}
|
||||
|
||||
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
|
||||
this->out_time += count;
|
||||
|
||||
// remove from silence and buf first
|
||||
{
|
||||
long n = min( count, this->silence_count );
|
||||
this->silence_count -= n;
|
||||
count -= n;
|
||||
|
||||
n = min( count, this->buf_remain );
|
||||
this->buf_remain -= n;
|
||||
count -= n;
|
||||
}
|
||||
|
||||
if ( count && !this->emu_track_ended_ )
|
||||
{
|
||||
this->emu_time += count;
|
||||
// End track if error
|
||||
if ( skip_( this, count ) )
|
||||
this->emu_track_ended_ = true;
|
||||
}
|
||||
|
||||
if ( !(this->silence_count | this->buf_remain) ) // caught up to emulator, so update track ended
|
||||
this->track_ended |= this->emu_track_ended_;
|
||||
|
||||
return 0;
|
||||
return track_skip( &this->track_filter, count );
|
||||
}
|
||||
|
||||
// Fading
|
||||
|
||||
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 )
|
||||
{
|
||||
this->fade_step = this->sample_rate_ * length_msec / (fade_block_size * fade_shift * 1000 / stereo);
|
||||
this->fade_start = msec_to_samples( start_msec, this->sample_rate_ );
|
||||
track_set_fade( &this->track_filter, msec_to_samples( start_msec, this->sample_rate_ ),
|
||||
length_msec * this->sample_rate_ / (1000 / stereo) );
|
||||
}
|
||||
|
||||
// unit / pow( 2.0, (double) x / step )
|
||||
static int int_log( blargg_long x, int step, int unit )
|
||||
blargg_err_t Gbs_play( struct Gbs_Emu* this, int out_count, sample_t* out )
|
||||
{
|
||||
int shift = x / step;
|
||||
int fraction = (x - shift * step) * unit / step;
|
||||
return ((unit - fraction) + (fraction >> 1)) >> shift;
|
||||
}
|
||||
|
||||
static void handle_fade( struct Gbs_Emu* this, long out_count, sample_t* out )
|
||||
{
|
||||
int i;
|
||||
for ( i = 0; i < out_count; i += fade_block_size )
|
||||
{
|
||||
int const shift = 14;
|
||||
int const unit = 1 << shift;
|
||||
int gain = int_log( (this->out_time + i - this->fade_start) / fade_block_size,
|
||||
this->fade_step, unit );
|
||||
if ( gain < (unit >> fade_shift) )
|
||||
this->track_ended = this->emu_track_ended_ = true;
|
||||
|
||||
sample_t* io = &out [i];
|
||||
int count;
|
||||
for ( count = min( fade_block_size, out_count - i ); count; --count )
|
||||
{
|
||||
*io = (sample_t) ((*io * gain) >> shift);
|
||||
++io;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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;
|
||||
require( this->current_track_ >= 0 );
|
||||
require( out_count % stereo == 0 );
|
||||
return track_play( &this->track_filter, out_count, out );
|
||||
}
|
||||
|
|
|
@ -9,15 +9,12 @@
|
|||
#include "gb_apu.h"
|
||||
#include "gb_cpu.h"
|
||||
#include "m3u_playlist.h"
|
||||
|
||||
/* typedef uint8_t byte; */
|
||||
typedef short sample_t;
|
||||
#include "track_filter.h"
|
||||
|
||||
enum { joypad_addr = 0xFF00 };
|
||||
enum { ram_addr = 0xA000 };
|
||||
enum { hi_page = 0xFF00 - ram_addr };
|
||||
enum { io_base = 0xFF00 };
|
||||
enum { buf_size = 2048 };
|
||||
|
||||
// Selects which sound hardware to use. AGB hardware is cleaner than the
|
||||
// others. Doesn't take effect until next start_track().
|
||||
|
@ -59,35 +56,18 @@ struct Gbs_Emu {
|
|||
blip_time_t next_play;
|
||||
|
||||
// Sound
|
||||
long clock_rate_;
|
||||
long sample_rate_;
|
||||
int clock_rate_;
|
||||
int sample_rate_;
|
||||
unsigned buf_changed_count;
|
||||
int voice_count_;
|
||||
int const* voice_types_;
|
||||
int mute_mask_;
|
||||
int gain_;
|
||||
int tempo_;
|
||||
|
||||
// track-specific
|
||||
byte track_count;
|
||||
volatile bool track_ended;
|
||||
int current_track_;
|
||||
blargg_long out_time; // number of samples played since start of track
|
||||
blargg_long emu_time; // number of samples emulator has generated since start of track
|
||||
bool emu_track_ended_; // emulator has reached end of track
|
||||
|
||||
// fading
|
||||
blargg_long fade_start;
|
||||
int fade_step;
|
||||
|
||||
// silence detection
|
||||
// Disable automatic end-of-track detection and skipping of silence at beginning
|
||||
bool ignore_silence;
|
||||
|
||||
int max_initial_silence;
|
||||
int mute_mask_;
|
||||
int silence_lookahead; // speed to run emulator when looking ahead for silence
|
||||
long silence_time; // number of samples where most recent silence began
|
||||
long silence_count; // number of samples of silence to play before using buf
|
||||
long buf_remain; // number of samples left in silence buffer
|
||||
|
||||
// Larger items at the end
|
||||
// Header for currently loaded file
|
||||
|
@ -96,11 +76,12 @@ struct Gbs_Emu {
|
|||
// M3u Playlist
|
||||
struct M3u_Playlist m3u;
|
||||
|
||||
struct setup_t tfilter;
|
||||
struct Track_Filter track_filter;
|
||||
|
||||
struct Gb_Apu apu;
|
||||
struct Gb_Cpu cpu;
|
||||
struct Stereo_Buffer stereo_buf;
|
||||
|
||||
sample_t buf [buf_size];
|
||||
struct Multi_Buffer stereo_buf;
|
||||
|
||||
// rom & ram
|
||||
struct Rom_Data rom;
|
||||
|
@ -116,36 +97,48 @@ void Gbs_init( struct Gbs_Emu* this );
|
|||
void Gbs_stop( struct Gbs_Emu* this );
|
||||
|
||||
// Loads a file from memory
|
||||
blargg_err_t Gbs_load( struct Gbs_Emu* this, void* data, long size );
|
||||
blargg_err_t Gbs_load_mem( struct Gbs_Emu* this, void* data, long size );
|
||||
|
||||
// Set output sample rate. Must be called only once before loading file.
|
||||
blargg_err_t Gbs_set_sample_rate( struct Gbs_Emu* this, long sample_rate );
|
||||
blargg_err_t Gbs_set_sample_rate( struct Gbs_Emu* this, int sample_rate );
|
||||
|
||||
// Start a track, where 0 is the first track. Also clears warning string.
|
||||
blargg_err_t Gbs_start_track( struct Gbs_Emu* this, int );
|
||||
|
||||
// Generate 'count' samples info 'buf'. Output is in stereo. Any emulation
|
||||
// errors set warning string, and major errors also end track.
|
||||
blargg_err_t Gbs_play( struct Gbs_Emu* this, long count, sample_t* buf );
|
||||
blargg_err_t Gbs_play( struct Gbs_Emu* this, int count, sample_t* buf );
|
||||
|
||||
// Track status/control
|
||||
// Number of milliseconds (1000 msec = 1 second) played since beginning of track
|
||||
long Track_tell( struct Gbs_Emu* this );
|
||||
int Track_tell( struct Gbs_Emu* this );
|
||||
|
||||
// Seek to new time in track. Seeking backwards or far forward can take a while.
|
||||
blargg_err_t Track_seek( struct Gbs_Emu* this, long msec );
|
||||
blargg_err_t Track_seek( struct Gbs_Emu* this, int msec );
|
||||
|
||||
// Skip n samples
|
||||
blargg_err_t Track_skip( struct Gbs_Emu* this, long n );
|
||||
blargg_err_t Track_skip( struct Gbs_Emu* this, int n );
|
||||
|
||||
// Set start time and length of track fade out. Once fade ends track_ended() returns
|
||||
// true. Fade time can be changed while track is playing.
|
||||
void Track_set_fade( struct Gbs_Emu* this, long start_msec, long length_msec );
|
||||
void Track_set_fade( struct Gbs_Emu* this, int start_msec, int length_msec );
|
||||
|
||||
// True if a track has reached its end
|
||||
static inline bool Track_ended( struct Gbs_Emu* this )
|
||||
{
|
||||
return track_ended( &this->track_filter );
|
||||
}
|
||||
|
||||
// Disables automatic end-of-track detection and skipping of silence at beginning
|
||||
static inline void Track_ignore_silence( struct Gbs_Emu* this, bool disable )
|
||||
{
|
||||
this->track_filter.silence_ignored_ = disable;
|
||||
}
|
||||
|
||||
// Get track length in milliseconds
|
||||
static inline long Track_get_length( struct Gbs_Emu* this, int n )
|
||||
static inline int Track_get_length( struct Gbs_Emu* this, int n )
|
||||
{
|
||||
long length = 120 * 1000; /* 2 minutes */
|
||||
int length = 120 * 1000; /* 2 minutes */
|
||||
if ( (this->m3u.size > 0) && (n < this->m3u.size) ) {
|
||||
struct entry_t* entry = &this->m3u.entries [n];
|
||||
length = entry->length;
|
||||
|
@ -175,19 +168,18 @@ static inline void Sound_set_gain( struct Gbs_Emu* this, int g )
|
|||
this->gain_ = g;
|
||||
}
|
||||
|
||||
|
||||
// Emulation (You shouldn't touch these)
|
||||
|
||||
blargg_err_t Run_clocks( struct Gbs_Emu* this, blip_time_t duration );
|
||||
void Set_bank( struct Gbs_Emu* this, int );
|
||||
void Update_timer( struct Gbs_Emu* this );
|
||||
blargg_err_t run_clocks( struct Gbs_Emu* this, blip_time_t duration );
|
||||
void set_bank( struct Gbs_Emu* this, int );
|
||||
void update_timer( struct Gbs_Emu* this );
|
||||
|
||||
// Runs CPU until time becomes >= 0
|
||||
void Run_cpu( struct Gbs_Emu* this );
|
||||
void run_cpu( struct Gbs_Emu* this );
|
||||
|
||||
// Reads/writes memory and I/O
|
||||
int Read_mem( struct Gbs_Emu* this, addr_t addr );
|
||||
void Write_mem( struct Gbs_Emu* this, addr_t addr, int data );
|
||||
int read_mem( struct Gbs_Emu* this, addr_t addr );
|
||||
void write_mem( struct Gbs_Emu* this, addr_t addr, int data );
|
||||
|
||||
// Current time
|
||||
static inline blip_time_t Time( struct Gbs_Emu* this )
|
||||
|
@ -195,6 +187,6 @@ static inline blip_time_t Time( struct Gbs_Emu* this )
|
|||
return Cpu_time( &this->cpu ) + this->end_time;
|
||||
}
|
||||
|
||||
void Jsr_then_stop( struct Gbs_Emu* this, byte const [] );
|
||||
void jsr_then_stop( struct Gbs_Emu* this, byte const [] );
|
||||
|
||||
#endif
|
||||
|
|
|
@ -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
|
||||
|
||||
static void Apu_balance_changed( struct Hes_Apu* this, struct Hes_Osc* osc );
|
||||
static void Apu_balance_changed( struct Hes_Apu* this, struct Hes_Osc* osc )
|
||||
static void balance_changed( struct Hes_Apu* this, struct Hes_Osc* osc )
|
||||
{
|
||||
static short const log_table [32] = { // ~1.5 db per step
|
||||
#define ENTRY( factor ) (short) (factor * amp_range / 31.0 + 0.5)
|
||||
|
@ -42,27 +41,40 @@ static void Apu_balance_changed( struct Hes_Apu* this, struct Hes_Osc* osc )
|
|||
int right = vol + (osc->balance << 1 & 0x1E) + (this->balance << 1 & 0x1E);
|
||||
if ( right < 0 ) right = 0;
|
||||
|
||||
left = log_table [left ];
|
||||
right = log_table [right];
|
||||
|
||||
// optimizing for the common case of being centered also allows easy
|
||||
// panning using Effects_Buffer
|
||||
osc->outputs [0] = osc->chans [0]; // center
|
||||
osc->outputs [1] = 0;
|
||||
if ( left != right )
|
||||
|
||||
// Separate balance into center volume and additional on either left or right
|
||||
osc->output [0] = osc->outputs [0]; // center
|
||||
osc->output [1] = osc->outputs [2]; // right
|
||||
int base = log_table [left ];
|
||||
int side = log_table [right] - base;
|
||||
if ( side < 0 )
|
||||
{
|
||||
osc->outputs [0] = osc->chans [1]; // left
|
||||
osc->outputs [1] = osc->chans [2]; // right
|
||||
base += side;
|
||||
side = -side;
|
||||
osc->output [1] = osc->outputs [1]; // left
|
||||
}
|
||||
|
||||
// Optimize when output is far left, center, or far right
|
||||
if ( !base || osc->output [0] == osc->output [1] )
|
||||
{
|
||||
base += side;
|
||||
side = 0;
|
||||
osc->output [0] = osc->output [1];
|
||||
osc->output [1] = NULL;
|
||||
osc->last_amp [1] = 0;
|
||||
}
|
||||
|
||||
if ( center_waves )
|
||||
{
|
||||
osc->last_amp [0] += (left - osc->volume [0]) * 16;
|
||||
osc->last_amp [1] += (right - osc->volume [1]) * 16;
|
||||
// TODO: this can leave a non-zero level in a buffer (minor)
|
||||
osc->last_amp [0] += (base - osc->volume [0]) * 16;
|
||||
osc->last_amp [1] += (side - osc->volume [1]) * 16;
|
||||
}
|
||||
|
||||
osc->volume [0] = left;
|
||||
osc->volume [1] = right;
|
||||
osc->volume [0] = base;
|
||||
osc->volume [1] = side;
|
||||
}
|
||||
|
||||
void Apu_init( struct Hes_Apu* this )
|
||||
|
@ -71,11 +83,11 @@ void Apu_init( struct Hes_Apu* this )
|
|||
do
|
||||
{
|
||||
osc--;
|
||||
osc->outputs [0] = 0;
|
||||
osc->outputs [1] = 0;
|
||||
osc->chans [0] = 0;
|
||||
osc->chans [1] = 0;
|
||||
osc->chans [2] = 0;
|
||||
osc->output [0] = NULL;
|
||||
osc->output [1] = NULL;
|
||||
osc->outputs [0] = NULL;
|
||||
osc->outputs [1] = NULL;
|
||||
osc->outputs [2] = NULL;
|
||||
}
|
||||
while ( osc != this->oscs );
|
||||
|
||||
|
@ -92,139 +104,183 @@ void Apu_reset( struct Hes_Apu* this )
|
|||
{
|
||||
osc--;
|
||||
memset( osc, 0, offsetof (struct Hes_Osc,outputs) );
|
||||
osc->noise_lfsr = 1;
|
||||
osc->lfsr = 1;
|
||||
osc->control = 0x40;
|
||||
osc->balance = 0xFF;
|
||||
}
|
||||
while ( osc != this->oscs );
|
||||
|
||||
// Only last two oscs support noise
|
||||
this->oscs [osc_count - 2].lfsr = 0x200C3; // equivalent to 1 in Fibonacci LFSR
|
||||
this->oscs [osc_count - 1].lfsr = 0x200C3;
|
||||
}
|
||||
|
||||
void Apu_osc_output( struct Hes_Apu* this, int index, struct Blip_Buffer* center, struct Blip_Buffer* left, struct Blip_Buffer* right )
|
||||
void Apu_osc_output( struct Hes_Apu* this, int i, struct Blip_Buffer* center, struct Blip_Buffer* left, struct Blip_Buffer* right )
|
||||
{
|
||||
require( (unsigned) index < osc_count );
|
||||
this->oscs [index].chans [0] = center;
|
||||
this->oscs [index].chans [1] = left;
|
||||
this->oscs [index].chans [2] = right;
|
||||
// Must be silent (all NULL), mono (left and right NULL), or stereo (none NULL)
|
||||
require( !center || (center && !left && !right) || (center && left && right) );
|
||||
require( (unsigned) i < osc_count ); // fails if you pass invalid osc index
|
||||
|
||||
struct Hes_Osc* osc = &this->oscs [osc_count];
|
||||
do
|
||||
if ( !center || !left || !right )
|
||||
{
|
||||
osc--;
|
||||
Apu_balance_changed( this, osc );
|
||||
left = center;
|
||||
right = center;
|
||||
}
|
||||
while ( osc != this->oscs );
|
||||
|
||||
struct Hes_Osc* o = &this->oscs [i];
|
||||
o->outputs [0] = center;
|
||||
o->outputs [1] = right;
|
||||
o->outputs [2] = left;
|
||||
balance_changed( this, o );
|
||||
}
|
||||
|
||||
void Osc_run_until( struct Hes_Osc* this, struct Blip_Synth* synth_, blip_time_t end_time )
|
||||
void run_osc( struct Hes_Osc* o, struct Blip_Synth* syn, blip_time_t end_time )
|
||||
{
|
||||
struct Blip_Buffer* const osc_outputs_0 = this->outputs [0]; // cache often-used values
|
||||
if ( osc_outputs_0 && this->control & 0x80 )
|
||||
int vol0 = o->volume [0];
|
||||
int vol1 = o->volume [1];
|
||||
int dac = o->dac;
|
||||
|
||||
struct Blip_Buffer* out0 = o->output [0]; // cache often-used values
|
||||
struct Blip_Buffer* out1 = o->output [1];
|
||||
if ( !(o->control & 0x80) )
|
||||
out0 = NULL;
|
||||
|
||||
if ( out0 )
|
||||
{
|
||||
int dac = this->dac;
|
||||
|
||||
int const volume_0 = this->volume [0];
|
||||
// Update amplitudes
|
||||
if ( out1 )
|
||||
{
|
||||
int delta = dac * volume_0 - this->last_amp [0];
|
||||
int delta = dac * vol1 - o->last_amp [1];
|
||||
if ( delta )
|
||||
Synth_offset( synth_, this->last_time, delta, osc_outputs_0 );
|
||||
Blip_set_modified( osc_outputs_0 );
|
||||
{
|
||||
Synth_offset( syn, o->last_time, delta, out1 );
|
||||
Blip_set_modified( out1 );
|
||||
}
|
||||
}
|
||||
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];
|
||||
int const volume_1 = this->volume [1];
|
||||
if ( osc_outputs_1 )
|
||||
{
|
||||
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 );
|
||||
}
|
||||
// Don't generate if silent
|
||||
if ( !(vol0 | vol1) )
|
||||
out0 = NULL;
|
||||
}
|
||||
|
||||
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 ( this->noise & 0x80 )
|
||||
{
|
||||
if ( volume_0 | volume_1 )
|
||||
{
|
||||
// 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 );
|
||||
int period = (~o->noise & 0x1F) * 128;
|
||||
if ( !period )
|
||||
period = 64;
|
||||
|
||||
this->noise_lfsr = noise_lfsr;
|
||||
assert( noise_lfsr );
|
||||
if ( noise && out0 )
|
||||
{
|
||||
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
|
||||
int phase = (this->phase + 1) & 0x1F; // pre-advance for optimal inner loop
|
||||
int period = this->period * 2;
|
||||
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
|
||||
int count = (end_time - time + period - 1) / period;
|
||||
time += count * period;
|
||||
|
||||
// maintain phase when silent
|
||||
blargg_long count = (end_time - time + period - 1) / period;
|
||||
phase += count; // phase will be masked below
|
||||
time += count * period;
|
||||
}
|
||||
this->phase = (phase - 1) & 0x1F; // undo pre-advance
|
||||
// not worth it
|
||||
//while ( count-- )
|
||||
// o->lfsr = (o->lfsr >> 1) ^ (0x30061 * (o->lfsr & 1));
|
||||
}
|
||||
}
|
||||
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;
|
||||
o->noise_delay = time - end_time;
|
||||
}
|
||||
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 )
|
||||
|
@ -243,8 +299,8 @@ void Apu_write_data( struct Hes_Apu* this, blip_time_t time, int addr, int data
|
|||
do
|
||||
{
|
||||
osc--;
|
||||
Osc_run_until( osc, &this->synth, time );
|
||||
Apu_balance_changed( this, this->oscs );
|
||||
run_osc( osc, &this->synth, time );
|
||||
balance_changed( this, this->oscs );
|
||||
}
|
||||
while ( osc != this->oscs );
|
||||
}
|
||||
|
@ -252,7 +308,7 @@ void Apu_write_data( struct Hes_Apu* this, blip_time_t time, int addr, int data
|
|||
else if ( this->latch < osc_count )
|
||||
{
|
||||
struct Hes_Osc* osc = &this->oscs [this->latch];
|
||||
Osc_run_until( osc, &this->synth, time );
|
||||
run_osc( osc, &this->synth, time );
|
||||
switch ( addr )
|
||||
{
|
||||
case 0x802:
|
||||
|
@ -267,12 +323,12 @@ void Apu_write_data( struct Hes_Apu* this, blip_time_t time, int addr, int data
|
|||
if ( osc->control & 0x40 & ~data )
|
||||
osc->phase = 0;
|
||||
osc->control = data;
|
||||
Apu_balance_changed( this, osc );
|
||||
balance_changed( this, osc );
|
||||
break;
|
||||
|
||||
case 0x805:
|
||||
osc->balance = data;
|
||||
Apu_balance_changed( this, osc );
|
||||
balance_changed( this, osc );
|
||||
break;
|
||||
|
||||
case 0x806:
|
||||
|
@ -289,9 +345,9 @@ void Apu_write_data( struct Hes_Apu* this, blip_time_t time, int addr, int data
|
|||
break;
|
||||
|
||||
case 0x807:
|
||||
if ( osc >= &this->oscs [4] )
|
||||
osc->noise = data;
|
||||
osc->noise = data;
|
||||
break;
|
||||
|
||||
case 0x809:
|
||||
if ( !(data & 0x80) && (data & 0x03) != 0 ) {
|
||||
dprintf( "HES LFO not supported\n" );
|
||||
|
@ -307,7 +363,7 @@ void Apu_end_frame( struct Hes_Apu* this, blip_time_t end_time )
|
|||
{
|
||||
osc--;
|
||||
if ( end_time > osc->last_time )
|
||||
Osc_run_until( osc, &this->synth, end_time );
|
||||
run_osc( osc, &this->synth, end_time );
|
||||
assert( osc->last_time >= end_time );
|
||||
osc->last_time -= end_time;
|
||||
}
|
||||
|
|
|
@ -5,40 +5,46 @@
|
|||
#define HES_APU_H
|
||||
|
||||
#include "blargg_common.h"
|
||||
#include "blargg_source.h"
|
||||
#include "blip_buffer.h"
|
||||
|
||||
enum { amp_range = 0x8000 };
|
||||
enum { osc_count = 6 };
|
||||
enum { start_addr = 0x0800 };
|
||||
enum { end_addr = 0x0809 };
|
||||
enum { osc_count = 6 }; // 0 <= chan < osc_count
|
||||
|
||||
// Registers are at io_addr to io_addr+io_size-1
|
||||
enum { apu_io_addr = 0x0800 };
|
||||
enum { apu_io_size = 10 };
|
||||
|
||||
struct Hes_Osc
|
||||
{
|
||||
unsigned char wave [32];
|
||||
byte wave [32];
|
||||
int delay;
|
||||
int period;
|
||||
int phase;
|
||||
|
||||
int noise_delay;
|
||||
byte noise;
|
||||
unsigned lfsr;
|
||||
|
||||
byte control;
|
||||
byte balance;
|
||||
byte dac;
|
||||
short volume [2];
|
||||
int last_amp [2];
|
||||
int delay;
|
||||
int period;
|
||||
unsigned char noise;
|
||||
unsigned char phase;
|
||||
unsigned char balance;
|
||||
unsigned char dac;
|
||||
blip_time_t last_time;
|
||||
|
||||
struct Blip_Buffer* outputs [2];
|
||||
struct Blip_Buffer* chans [3];
|
||||
unsigned noise_lfsr;
|
||||
unsigned char control;
|
||||
blip_time_t last_time;
|
||||
struct Blip_Buffer* output [2];
|
||||
struct Blip_Buffer* outputs [3];
|
||||
};
|
||||
|
||||
void Osc_run_until( struct Hes_Osc* this, struct Blip_Synth* synth, blip_time_t );
|
||||
|
||||
struct Hes_Apu {
|
||||
struct Hes_Osc oscs [osc_count];
|
||||
|
||||
int latch;
|
||||
int balance;
|
||||
struct Blip_Synth synth;
|
||||
struct Hes_Osc oscs [osc_count];
|
||||
};
|
||||
|
||||
// Init HES apu sound chip
|
||||
|
@ -48,7 +54,12 @@ void Apu_init( struct Hes_Apu* this );
|
|||
void Apu_reset( struct Hes_Apu* this );
|
||||
|
||||
void Apu_osc_output( struct Hes_Apu* this, int index, struct Blip_Buffer* center, struct Blip_Buffer* left, struct Blip_Buffer* right );
|
||||
|
||||
// Emulates to time t, then writes data to addr
|
||||
void Apu_write_data( struct Hes_Apu* this, blip_time_t, int addr, int data );
|
||||
|
||||
// Emulates to time t, then subtracts t from the current time.
|
||||
// OK if previous write call had time slightly after t.
|
||||
void Apu_end_frame( struct Hes_Apu* this, blip_time_t );
|
||||
|
||||
static inline void Apu_volume( struct Hes_Apu* this, int v ) { Synth_volume( &this->synth, (v*9)/5 / osc_count / amp_range ); }
|
||||
|
|
|
@ -12,8 +12,8 @@ enum { adpcm_amp_range = 2048 };
|
|||
enum { adpcm_osc_count = 1 }; // 0 <= chan < osc_count
|
||||
|
||||
// Registers are at io_addr to io_addr+io_size-1
|
||||
enum { io_addr = 0x1800 };
|
||||
enum { io_size = 0x400 };
|
||||
enum { adpcm_io_addr = 0x1800 };
|
||||
enum { adpcm_io_size = 0x400 };
|
||||
|
||||
struct State
|
||||
{
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,56 +1,52 @@
|
|||
// PC Engine CPU emulator for use with HES music files
|
||||
|
||||
// Game_Music_Emu 0.5.2
|
||||
// Game_Music_Emu 0.6-pre
|
||||
#ifndef HES_CPU_H
|
||||
#define HES_CPU_H
|
||||
|
||||
#include "blargg_common.h"
|
||||
#include "blargg_source.h"
|
||||
|
||||
typedef blargg_long hes_time_t; // clock cycle count
|
||||
typedef unsigned hes_addr_t; // 16-bit address
|
||||
typedef int hes_time_t; // clock cycle count
|
||||
typedef int hes_addr_t; // 16-bit address
|
||||
|
||||
struct Hes_Emu;
|
||||
|
||||
enum { future_hes_time = LONG_MAX / 2 + 1 };
|
||||
enum { page_size = 0x2000 };
|
||||
enum { page_shift = 13 };
|
||||
enum { page_count = 8 };
|
||||
|
||||
// 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 };
|
||||
enum { future_time = INT_MAX/2 + 1 };
|
||||
enum { page_bits = 13 };
|
||||
enum { page_size = 1 << page_bits };
|
||||
enum { page_count = 0x10000 / page_size };
|
||||
|
||||
// Can read this many bytes past end of a page
|
||||
enum { cpu_padding = 8 };
|
||||
enum { irq_inhibit = 0x04 };
|
||||
|
||||
enum { irq_inhibit_mask = 0x04 };
|
||||
enum { idle_addr = 0x1FFF };
|
||||
|
||||
// Cpu state
|
||||
struct state_t {
|
||||
uint8_t const* code_map [page_count + 1];
|
||||
struct cpu_state_t {
|
||||
byte const* code_map [page_count + 1];
|
||||
hes_time_t base;
|
||||
blargg_long time;
|
||||
int time;
|
||||
};
|
||||
|
||||
// Cpu registers
|
||||
// NOT kept updated during emulation.
|
||||
struct registers_t {
|
||||
uint16_t pc;
|
||||
uint8_t a;
|
||||
uint8_t x;
|
||||
uint8_t y;
|
||||
uint8_t status;
|
||||
uint8_t sp;
|
||||
byte a;
|
||||
byte x;
|
||||
byte y;
|
||||
byte flags;
|
||||
byte sp;
|
||||
};
|
||||
|
||||
struct Hes_Cpu {
|
||||
struct registers_t r;
|
||||
|
||||
hes_time_t irq_time;
|
||||
hes_time_t end_time;
|
||||
hes_time_t irq_time_;
|
||||
hes_time_t end_time_;
|
||||
|
||||
struct state_t* state; // points to state_ or a local copy within run()
|
||||
struct state_t state_;
|
||||
struct cpu_state_t* cpu_state; // points to state_ or a local copy within run()
|
||||
struct cpu_state_t cpu_state_;
|
||||
|
||||
// page mapping registers
|
||||
uint8_t mmr [page_count + 1];
|
||||
|
@ -58,7 +54,10 @@ struct Hes_Cpu {
|
|||
};
|
||||
|
||||
// Init cpu state
|
||||
void Cpu_init( struct Hes_Cpu* this );
|
||||
static inline void Cpu_init( struct Hes_Cpu* this )
|
||||
{
|
||||
this->cpu_state = &this->cpu_state_;
|
||||
}
|
||||
|
||||
// Reset hes cpu
|
||||
void Cpu_reset( struct Hes_Cpu* this );
|
||||
|
@ -67,29 +66,67 @@ void Cpu_reset( struct Hes_Cpu* this );
|
|||
// instructions were encountered.
|
||||
bool Cpu_run( struct Hes_Emu* this, hes_time_t end_time );
|
||||
|
||||
void Cpu_set_mmr( struct Hes_Emu* this, int reg, int bank );
|
||||
|
||||
// Time of ning of next instruction to be executed
|
||||
static inline hes_time_t Cpu_time( struct Hes_Cpu* this )
|
||||
{
|
||||
return this->state->time + this->state->base;
|
||||
return this->cpu_state->time + this->cpu_state->base;
|
||||
}
|
||||
|
||||
static inline void Cpu_set_time( struct Hes_Cpu* this, hes_time_t t ) { this->cpu_state->time = t - this->cpu_state->base; }
|
||||
static inline void Cpu_adjust_time( struct Hes_Cpu* this, int delta ) { this->cpu_state->time += delta; }
|
||||
|
||||
#define HES_CPU_PAGE( addr ) ((unsigned) (addr) >> page_bits)
|
||||
|
||||
#ifdef BLARGG_NONPORTABLE
|
||||
#define HES_CPU_OFFSET( addr ) (addr)
|
||||
#else
|
||||
#define HES_CPU_OFFSET( addr ) ((addr) & (page_size - 1))
|
||||
#endif
|
||||
|
||||
static inline uint8_t const* Cpu_get_code( struct Hes_Cpu* this, hes_addr_t addr )
|
||||
{
|
||||
return this->state->code_map [addr >> page_shift] + addr
|
||||
#if !defined (BLARGG_NONPORTABLE)
|
||||
% (unsigned) page_size
|
||||
#endif
|
||||
;
|
||||
return this->cpu_state_.code_map [HES_CPU_PAGE( addr )] + HES_CPU_OFFSET( addr );
|
||||
}
|
||||
|
||||
static inline 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;
|
||||
int delta = this->state->base - t;
|
||||
this->state->base = t;
|
||||
return delta;
|
||||
if ( end > irq && !(this->r.flags & irq_inhibit_mask) )
|
||||
end = irq;
|
||||
|
||||
this->cpu_state->time += this->cpu_state->base - end;
|
||||
this->cpu_state->base = end;
|
||||
}
|
||||
|
||||
static inline hes_time_t Cpu_end_time( struct Hes_Cpu* this ) { return this->end_time_; }
|
||||
|
||||
static inline void Cpu_set_irq_time( struct Hes_Cpu* this, hes_time_t t )
|
||||
{
|
||||
this->irq_time_ = t;
|
||||
update_end_time( this, this->end_time_, t );
|
||||
}
|
||||
|
||||
static inline void Cpu_set_end_time( struct Hes_Cpu* this, hes_time_t t )
|
||||
{
|
||||
this->end_time_ = t;
|
||||
update_end_time( this, t, this->irq_time_ );
|
||||
}
|
||||
|
||||
static inline void Cpu_end_frame( struct Hes_Cpu* this, hes_time_t t )
|
||||
{
|
||||
assert( this->cpu_state == &this->cpu_state_ );
|
||||
this->cpu_state_.base -= t;
|
||||
if ( this->irq_time_ < future_time ) this->irq_time_ -= t;
|
||||
if ( this->end_time_ < future_time ) this->end_time_ -= t;
|
||||
}
|
||||
|
||||
static inline 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
|
||||
|
|
|
@ -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 unmapped = 0xFF;
|
||||
|
||||
long const period_60hz = 262 * 455L; // scanlines * clocks per scanline
|
||||
|
||||
int const stereo = 2; // number of channels for stereo
|
||||
int const silence_max = 6; // seconds
|
||||
int const silence_threshold = 0x10;
|
||||
long const fade_block_size = 512;
|
||||
int const fade_shift = 8; // fade ends with gain at 1.0 / (1 << fade_shift)
|
||||
int const period_60hz = 262 * 455; // scanlines * clocks per scanline
|
||||
|
||||
const char gme_wrong_file_type [] = "Wrong file type for this emulator";
|
||||
|
||||
static void clear_track_vars( struct Hes_Emu* this )
|
||||
{
|
||||
this->current_track_ = -1;
|
||||
this->out_time = 0;
|
||||
this->emu_time = 0;
|
||||
this->emu_track_ended_ = true;
|
||||
this->track_ended = true;
|
||||
this->fade_start = (blargg_long)(LONG_MAX / 2 + 1);
|
||||
this->fade_step = 1;
|
||||
this->silence_time = 0;
|
||||
this->silence_count = 0;
|
||||
this->buf_remain = 0;
|
||||
this->current_track_ = -1;
|
||||
track_stop( &this->track_filter );
|
||||
}
|
||||
|
||||
void Hes_init( struct Hes_Emu* this )
|
||||
|
@ -52,15 +38,12 @@ void Hes_init( struct Hes_Emu* this )
|
|||
this->tempo_ = (int)(FP_ONE_TEMPO);
|
||||
|
||||
// defaults
|
||||
this->max_initial_silence = 2;
|
||||
this->ignore_silence = false;
|
||||
|
||||
// Unload
|
||||
this->voice_count_ = 0;
|
||||
clear_track_vars( this );
|
||||
this->tfilter = *track_get_setup( &this->track_filter );
|
||||
this->tfilter.max_initial = 2;
|
||||
this->tfilter.lookahead = 6;
|
||||
this->track_filter.silence_ignored_ = false;
|
||||
|
||||
this->timer.raw_load = 0;
|
||||
this->silence_lookahead = 6;
|
||||
Sound_set_gain( this, (int)(FP_ONE_GAIN*1.11) );
|
||||
|
||||
Rom_init( &this->rom, 0x2000 );
|
||||
|
@ -71,6 +54,11 @@ void Hes_init( struct Hes_Emu* this )
|
|||
|
||||
/* Set default track count */
|
||||
this->track_count = 255;
|
||||
|
||||
// clears fields
|
||||
this->voice_count_ = 0;
|
||||
this->voice_types_ = 0;
|
||||
clear_track_vars( this );
|
||||
}
|
||||
|
||||
static blargg_err_t check_hes_header( void const* header )
|
||||
|
@ -82,10 +70,12 @@ static blargg_err_t check_hes_header( void const* header )
|
|||
|
||||
// Setup
|
||||
|
||||
blargg_err_t Hes_load( struct Hes_Emu* this, void* data, long size )
|
||||
blargg_err_t Hes_load_mem( struct Hes_Emu* this, void* data, long size )
|
||||
{
|
||||
// Unload
|
||||
this->voice_count_ = 0;
|
||||
this->track_count = 255;
|
||||
this->m3u.size = 0;
|
||||
clear_track_vars( this );
|
||||
|
||||
assert( offsetof (struct header_t,unused [4]) == header_size );
|
||||
|
@ -106,15 +96,15 @@ blargg_err_t Hes_load( struct Hes_Emu* this, void* data, long size )
|
|||
// many files have bad sizes in the only block, so it's simpler to
|
||||
// just try to load the damn data as best as possible.
|
||||
|
||||
long addr = get_le32( this->header.addr );
|
||||
/* long rom_size = get_le32( this->header.size ); */
|
||||
long const rom_max = 0x100000;
|
||||
if ( addr & ~(rom_max - 1) )
|
||||
int addr = get_le32( this->header.addr );
|
||||
/* int rom_size = get_le32( this->header.size ); */
|
||||
int const rom_max = 0x100000;
|
||||
if ( (unsigned) addr >= (unsigned) rom_max )
|
||||
{
|
||||
/* warning( "Invalid address" ); */
|
||||
addr &= rom_max - 1;
|
||||
}
|
||||
/* if ( (unsigned long) (addr + size) > (unsigned long) rom_max )
|
||||
/* if ( (unsigned) (addr + size) > (unsigned) rom_max )
|
||||
warning( "Invalid size" );
|
||||
|
||||
if ( rom_size != rom.file_size() )
|
||||
|
@ -130,6 +120,10 @@ blargg_err_t Hes_load( struct Hes_Emu* this, void* data, long size )
|
|||
Rom_set_addr( &this->rom, addr );
|
||||
|
||||
this->voice_count_ = osc_count + adpcm_osc_count;
|
||||
static int const types [osc_count + adpcm_osc_count] = {
|
||||
wave_type+0, wave_type+1, wave_type+2, wave_type+3, mixed_type+0, mixed_type+1, mixed_type+2
|
||||
};
|
||||
this->voice_types_ = types;
|
||||
|
||||
Apu_volume( &this->apu, this->gain_ );
|
||||
Adpcm_volume( &this->adpcm, this->gain_ );
|
||||
|
@ -137,21 +131,17 @@ blargg_err_t Hes_load( struct Hes_Emu* this, void* data, long size )
|
|||
// Setup buffer
|
||||
this->clock_rate_ = 7159091;
|
||||
Buffer_clock_rate( &this->stereo_buf, 7159091 );
|
||||
RETURN_ERR( Buffer_set_channel_count( &this->stereo_buf, this->voice_count_, this->voice_types_ ) );
|
||||
this->buf_changed_count = Buffer_channels_changed_count( &this->stereo_buf );
|
||||
|
||||
Sound_set_tempo( this, this->tempo_ );
|
||||
Sound_mute_voices( this, this->mute_mask_ );
|
||||
|
||||
// Reset track count
|
||||
this->track_count = 255;
|
||||
this->m3u.size = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// Emulation
|
||||
|
||||
void recalc_timer_load( struct Hes_Emu* this );
|
||||
void recalc_timer_load( struct Hes_Emu* this )
|
||||
{
|
||||
this->timer.load = this->timer.raw_load * this->timer_base + 1;
|
||||
|
@ -159,9 +149,25 @@ void recalc_timer_load( struct Hes_Emu* this )
|
|||
|
||||
// Hardware
|
||||
|
||||
void irq_changed( struct Hes_Emu* this );
|
||||
void run_until( struct Hes_Emu* this, hes_time_t present );
|
||||
void Cpu_write_vdp( struct Hes_Emu* this, int addr, int data )
|
||||
void run_until( struct Hes_Emu* this, hes_time_t present )
|
||||
{
|
||||
while ( this->vdp.next_vbl < present )
|
||||
this->vdp.next_vbl += this->play_period;
|
||||
|
||||
hes_time_t elapsed = present - this->timer.last_time;
|
||||
if ( elapsed > 0 )
|
||||
{
|
||||
if ( this->timer.enabled )
|
||||
{
|
||||
this->timer.count -= elapsed;
|
||||
if ( this->timer.count <= 0 )
|
||||
this->timer.count += this->timer.load;
|
||||
}
|
||||
this->timer.last_time = present;
|
||||
}
|
||||
}
|
||||
|
||||
void write_vdp( struct Hes_Emu* this, int addr, int data )
|
||||
{
|
||||
switch ( addr )
|
||||
{
|
||||
|
@ -178,77 +184,33 @@ void Cpu_write_vdp( struct Hes_Emu* this, int addr, int data )
|
|||
this->vdp.control = data;
|
||||
irq_changed( this );
|
||||
}
|
||||
else
|
||||
/* else
|
||||
{
|
||||
dprintf( "VDP not supported: $%02X <- $%02X\n", this->vdp.latch, data );
|
||||
}
|
||||
dprintf( "VDP not supported: $%02X <- $%02X\n", vdp.latch, data );
|
||||
} */
|
||||
break;
|
||||
|
||||
case 3:
|
||||
dprintf( "VDP MSB not supported: $%02X <- $%02X\n", this->vdp.latch, data );
|
||||
/* dprintf( "VDP MSB not supported: $%02X <- $%02X\n", vdp.latch, data ); */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int Cpu_done( struct Hes_Emu* this )
|
||||
{
|
||||
check( time() >= end_time() ||
|
||||
(!(r.status & i_flag_mask) && time() >= irq_time()) );
|
||||
|
||||
if ( !(this->cpu.r.status & i_flag_mask) )
|
||||
{
|
||||
hes_time_t present = Cpu_time( &this->cpu );
|
||||
|
||||
if ( this->irq.timer <= present && !(this->irq.disables & timer_mask) )
|
||||
{
|
||||
this->timer.fired = true;
|
||||
this->irq.timer = (hes_time_t)future_hes_time;
|
||||
irq_changed( this ); // overkill, but not worth writing custom code
|
||||
#if defined (GME_FRAME_HOOK_DEFINED)
|
||||
{
|
||||
unsigned const threshold = period_60hz / 30;
|
||||
unsigned long elapsed = present - last_frame_hook;
|
||||
if ( elapsed - period_60hz + threshold / 2 < threshold )
|
||||
{
|
||||
last_frame_hook = present;
|
||||
GME_FRAME_HOOK( this );
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return 0x0A;
|
||||
}
|
||||
|
||||
if ( this->irq.vdp <= present && !(this->irq.disables & vdp_mask) )
|
||||
{
|
||||
// work around for bugs with music not acknowledging VDP
|
||||
//run_until( present );
|
||||
//irq.vdp = future_hes_time;
|
||||
//irq_changed();
|
||||
#if defined(GME_FRAME_HOOK_DEFINED)
|
||||
last_frame_hook = present;
|
||||
GME_FRAME_HOOK( this );
|
||||
#endif
|
||||
return 0x08;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Emu_cpu_write( struct Hes_Emu* this, hes_addr_t addr, int data )
|
||||
void write_mem_( struct Hes_Emu* this, hes_addr_t addr, int data )
|
||||
{
|
||||
hes_time_t time = Cpu_time( &this->cpu );
|
||||
if ( (unsigned) (addr - start_addr) <= end_addr - start_addr )
|
||||
if ( (unsigned) (addr - apu_io_addr) < apu_io_size )
|
||||
{
|
||||
GME_APU_HOOK( this, addr - apu.start_addr, data );
|
||||
// avoid going way past end when a long block xfer is writing to I/O space
|
||||
hes_time_t t = min( time, this->cpu.end_time + 8 );
|
||||
// Avoid going way past end when a long block xfer is writing to I/O space.
|
||||
// Not a problem for other registers below because they don't write to
|
||||
// Blip_Buffer.
|
||||
hes_time_t t = min( time, Cpu_end_time( &this->cpu ) + 8 );
|
||||
Apu_write_data( &this->apu, t, addr, data );
|
||||
return;
|
||||
}
|
||||
|
||||
if ( (unsigned) (addr - io_addr) < io_size )
|
||||
if ( (unsigned) (addr - adpcm_io_addr) < adpcm_io_size )
|
||||
{
|
||||
hes_time_t t = min( time, this->cpu.end_time + 6 );
|
||||
hes_time_t t = min( time, Cpu_end_time( &this->cpu ) + 6 );
|
||||
Adpcm_write_data( &this->adpcm, t, addr, data );
|
||||
return;
|
||||
}
|
||||
|
@ -258,7 +220,7 @@ void Emu_cpu_write( struct Hes_Emu* this, hes_addr_t addr, int data )
|
|||
case 0x0000:
|
||||
case 0x0002:
|
||||
case 0x0003:
|
||||
Cpu_write_vdp( this, addr, data );
|
||||
write_vdp( this, addr, data );
|
||||
return;
|
||||
|
||||
case 0x0C00: {
|
||||
|
@ -282,11 +244,8 @@ void Emu_cpu_write( struct Hes_Emu* this, hes_addr_t addr, int data )
|
|||
case 0x1402:
|
||||
run_until( this, time );
|
||||
this->irq.disables = data;
|
||||
|
||||
// flag questionable values
|
||||
if ( (data & 0xF8) && (data & 0xF8) != 0xF8 ) {
|
||||
dprintf( "Int mask: $%02X\n", data );
|
||||
}
|
||||
/* if ( (data & 0xF8) && (data & 0xF8) != 0xF8 ) // flag questionable values
|
||||
dprintf( "Int mask: $%02X\n", data ); */
|
||||
break;
|
||||
|
||||
case 0x1403:
|
||||
|
@ -305,7 +264,7 @@ void Emu_cpu_write( struct Hes_Emu* this, hes_addr_t addr, int data )
|
|||
return;
|
||||
|
||||
default:
|
||||
dprintf( "unmapped write $%04X <- $%02X\n", addr, data );
|
||||
/* dprintf( "unmapped write $%04X <- $%02X\n", addr, data ); */
|
||||
return;
|
||||
#endif
|
||||
}
|
||||
|
@ -313,7 +272,7 @@ void Emu_cpu_write( struct Hes_Emu* this, hes_addr_t addr, int data )
|
|||
irq_changed( this );
|
||||
}
|
||||
|
||||
int Emu_cpu_read( struct Hes_Emu* this, hes_addr_t addr )
|
||||
int read_mem_( struct Hes_Emu* this, hes_addr_t addr )
|
||||
{
|
||||
hes_time_t time = Cpu_time( &this->cpu );
|
||||
addr &= page_size - 1;
|
||||
|
@ -322,21 +281,21 @@ int Emu_cpu_read( struct Hes_Emu* this, hes_addr_t addr )
|
|||
case 0x0000:
|
||||
if ( this->irq.vdp > time )
|
||||
return 0;
|
||||
this->irq.vdp = (hes_time_t)future_hes_time;
|
||||
this->irq.vdp = future_time;
|
||||
run_until( this, time );
|
||||
irq_changed( this );
|
||||
return 0x20;
|
||||
|
||||
case 0x0002:
|
||||
/* case 0x0002:
|
||||
case 0x0003:
|
||||
dprintf( "VDP read not supported: %d\n", addr );
|
||||
return 0;
|
||||
return 0; */
|
||||
|
||||
case 0x0C01:
|
||||
//return timer.enabled; // TODO: remove?
|
||||
case 0x0C00:
|
||||
run_until( this, time );
|
||||
dprintf( "Timer count read\n" );
|
||||
/* dprintf( "Timer count read\n" ); */
|
||||
return (unsigned) (this->timer.count - 1) / this->timer_base;
|
||||
|
||||
case 0x1402:
|
||||
|
@ -358,71 +317,75 @@ int Emu_cpu_read( struct Hes_Emu* this, hes_addr_t addr )
|
|||
|
||||
#ifndef NDEBUG
|
||||
case 0x1000: // I/O port
|
||||
// case 0x180C: // CD-ROM
|
||||
// case 0x180D:
|
||||
//case 0x180C: // CD-ROM
|
||||
//case 0x180D:
|
||||
break;
|
||||
|
||||
default:
|
||||
dprintf( "unmapped read $%04X\n", addr );
|
||||
/* default:
|
||||
dprintf( "unmapped read $%04X\n", addr ); */
|
||||
#endif
|
||||
}
|
||||
|
||||
return unmapped;
|
||||
}
|
||||
|
||||
// see hes_cpu_io.h for core read/write functions
|
||||
|
||||
// Emulation
|
||||
|
||||
void run_until( struct Hes_Emu* this, hes_time_t present )
|
||||
{
|
||||
while ( this->vdp.next_vbl < present )
|
||||
this->vdp.next_vbl += this->play_period;
|
||||
|
||||
hes_time_t elapsed = present - this->timer.last_time;
|
||||
if ( elapsed > 0 )
|
||||
{
|
||||
if ( this->timer.enabled )
|
||||
{
|
||||
this->timer.count -= elapsed;
|
||||
if ( this->timer.count <= 0 )
|
||||
this->timer.count += this->timer.load;
|
||||
}
|
||||
this->timer.last_time = present;
|
||||
}
|
||||
}
|
||||
|
||||
void irq_changed( struct Hes_Emu* this )
|
||||
{
|
||||
hes_time_t present = Cpu_time( &this->cpu );
|
||||
|
||||
if ( this->irq.timer > present )
|
||||
{
|
||||
this->irq.timer = (hes_time_t)future_hes_time;
|
||||
this->irq.timer = future_time;
|
||||
if ( this->timer.enabled && !this->timer.fired )
|
||||
this->irq.timer = present + this->timer.count;
|
||||
}
|
||||
|
||||
if ( this->irq.vdp > present )
|
||||
{
|
||||
this->irq.vdp = (hes_time_t)future_hes_time;
|
||||
this->irq.vdp = future_time;
|
||||
if ( this->vdp.control & 0x08 )
|
||||
this->irq.vdp = this->vdp.next_vbl;
|
||||
}
|
||||
|
||||
hes_time_t time = (hes_time_t)future_hes_time;
|
||||
hes_time_t time = future_time;
|
||||
if ( !(this->irq.disables & timer_mask) ) time = this->irq.timer;
|
||||
if ( !(this->irq.disables & vdp_mask) ) time = min( time, this->irq.vdp );
|
||||
|
||||
// Set cpu irq time
|
||||
this->cpu.state->time += Cpu_update_end_time( &this->cpu, this->cpu.r.status,
|
||||
this->cpu.end_time, (this->cpu.irq_time = time) );
|
||||
Cpu_set_irq_time( &this->cpu, time );
|
||||
}
|
||||
|
||||
static void adjust_time( blargg_long* time, hes_time_t delta );
|
||||
static void adjust_time( blargg_long* time, hes_time_t delta )
|
||||
int cpu_done( struct Hes_Emu* this )
|
||||
{
|
||||
if ( *time < (blargg_long)future_hes_time )
|
||||
check( Cpu_time( &this->cpu ) >= Cpu_end_time( &this->cpu ) ||
|
||||
(!(this->cpu.r.flags & i_flag_mask) && Cpu_time( &this->cpu ) >= Cpu_irq_time( &this->cpu )) );
|
||||
|
||||
if ( !(this->cpu.r.flags & i_flag_mask) )
|
||||
{
|
||||
hes_time_t present = Cpu_time( &this->cpu );
|
||||
|
||||
if ( this->irq.timer <= present && !(this->irq.disables & timer_mask) )
|
||||
{
|
||||
this->timer.fired = true;
|
||||
this->irq.timer = future_time;
|
||||
irq_changed( this ); // overkill, but not worth writing custom code
|
||||
return 0x0A;
|
||||
}
|
||||
|
||||
if ( this->irq.vdp <= present && !(this->irq.disables & vdp_mask) )
|
||||
{
|
||||
// work around for bugs with music not acknowledging VDP
|
||||
//run_until( present );
|
||||
//irq.vdp = cpu.future_time;
|
||||
//irq_changed();
|
||||
return 0x08;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void adjust_time( hes_time_t* time, hes_time_t delta )
|
||||
{
|
||||
if ( *time < future_time )
|
||||
{
|
||||
*time -= delta;
|
||||
if ( *time < 0 )
|
||||
|
@ -430,15 +393,13 @@ static void adjust_time( blargg_long* time, hes_time_t delta )
|
|||
}
|
||||
}
|
||||
|
||||
blargg_err_t run_clocks( struct Hes_Emu* this, blip_time_t* duration_ );
|
||||
blargg_err_t run_clocks( struct Hes_Emu* this, blip_time_t* duration_ )
|
||||
blargg_err_t end_frame( struct Hes_Emu* this, hes_time_t duration )
|
||||
{
|
||||
blip_time_t duration = *duration_; // cache
|
||||
/* if ( run_cpu( this, duration ) )
|
||||
warning( "Emulation error (illegal instruction)" ); */
|
||||
run_cpu( this, duration );
|
||||
|
||||
Cpu_run( this, duration );
|
||||
/* warning( "Emulation error (illegal instruction)" ); */
|
||||
|
||||
check( time() >= duration );
|
||||
check( Cpu_time( &this->cpu ) >= duration );
|
||||
//check( time() - duration < 20 ); // Txx instruction could cause going way over
|
||||
|
||||
run_until( this, duration );
|
||||
|
@ -446,15 +407,7 @@ blargg_err_t run_clocks( struct Hes_Emu* this, blip_time_t* duration_ )
|
|||
// end time frame
|
||||
this->timer.last_time -= duration;
|
||||
this->vdp.next_vbl -= duration;
|
||||
#if defined (GME_FRAME_HOOK_DEFINED)
|
||||
last_frame_hook -= *duration;
|
||||
#endif
|
||||
|
||||
// End cpu frame
|
||||
this->cpu.state_.base -= duration;
|
||||
if ( this->cpu.irq_time < (hes_time_t)future_hes_time ) this->cpu.irq_time -= duration;
|
||||
if ( this->cpu.end_time < (hes_time_t)future_hes_time ) this->cpu.end_time -= duration;
|
||||
|
||||
Cpu_end_frame( &this->cpu, duration );
|
||||
adjust_time( &this->irq.timer, duration );
|
||||
adjust_time( &this->irq.vdp, duration );
|
||||
Apu_end_frame( &this->apu, duration );
|
||||
|
@ -463,24 +416,31 @@ blargg_err_t run_clocks( struct Hes_Emu* this, blip_time_t* duration_ )
|
|||
return 0;
|
||||
}
|
||||
|
||||
blargg_err_t play_( struct Hes_Emu* this, long count, sample_t* out );
|
||||
blargg_err_t play_( struct Hes_Emu* this, long count, sample_t* out )
|
||||
blargg_err_t run_clocks( struct Hes_Emu* this, blip_time_t* duration_ )
|
||||
{
|
||||
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 )
|
||||
{
|
||||
Buffer_disable_immediate_removal( &this->stereo_buf );
|
||||
remain -= Buffer_read_samples( &this->stereo_buf, &out [count - remain], remain );
|
||||
if ( remain )
|
||||
{
|
||||
if ( this->buf_changed_count != Buffer_channels_changed_count( &this->stereo_buf ) )
|
||||
{
|
||||
this->buf_changed_count = Buffer_channels_changed_count( &this->stereo_buf );
|
||||
|
||||
// Remute voices
|
||||
Sound_mute_voices( this, this->mute_mask_ );
|
||||
}
|
||||
|
||||
int msec = Buffer_length( &this->stereo_buf );
|
||||
blip_time_t clocks_emulated = (blargg_long) msec * this->clock_rate_ / 1000;
|
||||
blip_time_t clocks_emulated = msec * this->clock_rate_ / 1000 - 100;
|
||||
RETURN_ERR( run_clocks( this, &clocks_emulated ) );
|
||||
assert( clocks_emulated );
|
||||
Buffer_end_frame( &this->stereo_buf, clocks_emulated );
|
||||
|
@ -489,10 +449,9 @@ blargg_err_t play_( struct Hes_Emu* this, long count, sample_t* out )
|
|||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// Music emu
|
||||
|
||||
blargg_err_t Hes_set_sample_rate( struct Hes_Emu* this, long rate )
|
||||
blargg_err_t Hes_set_sample_rate( struct Hes_Emu* this, int rate )
|
||||
{
|
||||
require( !this->sample_rate_ ); // sample rate can't be changed once set
|
||||
Buffer_init( &this->stereo_buf );
|
||||
|
@ -502,6 +461,8 @@ blargg_err_t Hes_set_sample_rate( struct Hes_Emu* this, long rate )
|
|||
Buffer_bass_freq( &this->stereo_buf, 60 );
|
||||
|
||||
this->sample_rate_ = rate;
|
||||
RETURN_ERR( track_init( &this->track_filter, this ) );
|
||||
this->tfilter.max_silence = 6 * stereo * this->sample_rate_;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -521,7 +482,7 @@ void Sound_mute_voices( struct Hes_Emu* this, int mask )
|
|||
this->mute_mask_ = mask;
|
||||
|
||||
// Set adpcm voice
|
||||
struct channel_t ch = Buffer_channel( &this->stereo_buf );
|
||||
struct channel_t ch = Buffer_channel( &this->stereo_buf, this->voice_count_ );
|
||||
if ( mask & (1 << this->voice_count_ ) )
|
||||
Adpcm_set_output( &this->adpcm, 0, 0, 0, 0 );
|
||||
else
|
||||
|
@ -537,6 +498,7 @@ void Sound_mute_voices( struct Hes_Emu* this, int mask )
|
|||
}
|
||||
else
|
||||
{
|
||||
struct channel_t ch = Buffer_channel( &this->stereo_buf, i );
|
||||
assert( (ch.center && ch.left && ch.right) ||
|
||||
(!ch.center && !ch.left && !ch.right) ); // all or nothing
|
||||
Apu_osc_output( &this->apu, i, ch.center, ch.left, ch.right );
|
||||
|
@ -557,7 +519,6 @@ void Sound_set_tempo( struct Hes_Emu* this, int t )
|
|||
this->tempo_ = t;
|
||||
}
|
||||
|
||||
void fill_buf( struct Hes_Emu* this );
|
||||
blargg_err_t Hes_start_track( struct Hes_Emu* this, int track )
|
||||
{
|
||||
clear_track_vars( this );
|
||||
|
@ -572,7 +533,7 @@ blargg_err_t Hes_start_track( struct Hes_Emu* this, int track )
|
|||
|
||||
Buffer_clear( &this->stereo_buf );
|
||||
|
||||
memset( this->cpu.ram, 0, sizeof this->cpu.ram ); // some HES music relies on zero fill
|
||||
memset( this->ram, 0, sizeof this->ram ); // some HES music relies on zero fill
|
||||
memset( this->sgx, 0, sizeof this->sgx );
|
||||
|
||||
Apu_reset( &this->apu );
|
||||
|
@ -581,12 +542,12 @@ blargg_err_t Hes_start_track( struct Hes_Emu* this, int track )
|
|||
|
||||
unsigned i;
|
||||
for ( i = 0; i < sizeof this->header.banks; i++ )
|
||||
Cpu_set_mmr( this, i, this->header.banks [i] );
|
||||
Cpu_set_mmr( this, page_count, 0xFF ); // unmapped beyond end of address space
|
||||
set_mmr( this, i, this->header.banks [i] );
|
||||
set_mmr( this, page_count, 0xFF ); // unmapped beyond end of address space
|
||||
|
||||
this->irq.disables = timer_mask | vdp_mask;
|
||||
this->irq.timer = (hes_time_t)future_hes_time;
|
||||
this->irq.vdp = (hes_time_t)future_hes_time;
|
||||
this->irq.timer = future_time;
|
||||
this->irq.vdp = future_time;
|
||||
|
||||
this->timer.enabled = false;
|
||||
this->timer.raw_load= 0x80;
|
||||
|
@ -598,280 +559,86 @@ blargg_err_t Hes_start_track( struct Hes_Emu* this, int track )
|
|||
this->vdp.control = 0;
|
||||
this->vdp.next_vbl = 0;
|
||||
|
||||
this->cpu.ram [0x1FF] = (idle_addr - 1) >> 8;
|
||||
this->cpu.ram [0x1FE] = (idle_addr - 1) & 0xFF;
|
||||
this->ram [0x1FF] = (idle_addr - 1) >> 8;
|
||||
this->ram [0x1FE] = (idle_addr - 1) & 0xFF;
|
||||
this->cpu.r.sp = 0xFD;
|
||||
this->cpu.r.pc = get_le16( this->header.init_addr );
|
||||
this->cpu.r.a = track;
|
||||
|
||||
recalc_timer_load( this );
|
||||
this->last_frame_hook = 0;
|
||||
|
||||
this->emu_track_ended_ = false;
|
||||
this->track_ended = false;
|
||||
// convert filter times to samples
|
||||
struct setup_t s = this->tfilter;
|
||||
s.max_initial *= this->sample_rate_ * stereo;
|
||||
#ifdef GME_DISABLE_SILENCE_LOOKAHEAD
|
||||
s.lookahead = 1;
|
||||
#endif
|
||||
track_setup( &this->track_filter, &s );
|
||||
|
||||
if ( !this->ignore_silence )
|
||||
{
|
||||
// play until non-silence or end of track
|
||||
long end;
|
||||
for ( end =this-> max_initial_silence * stereo * this->sample_rate_; this->emu_time < end; )
|
||||
{
|
||||
fill_buf( this );
|
||||
if ( this->buf_remain | (int) this->emu_track_ended_ )
|
||||
break;
|
||||
}
|
||||
|
||||
this->emu_time = this->buf_remain;
|
||||
this->out_time = 0;
|
||||
this->silence_time = 0;
|
||||
this->silence_count = 0;
|
||||
}
|
||||
/* return track_ended() ? warning() : 0; */
|
||||
return 0;
|
||||
return track_start( &this->track_filter );
|
||||
}
|
||||
|
||||
// Tell/Seek
|
||||
|
||||
static blargg_long msec_to_samples( blargg_long msec, long sample_rate )
|
||||
static int msec_to_samples( int msec, int sample_rate )
|
||||
{
|
||||
blargg_long sec = msec / 1000;
|
||||
int sec = msec / 1000;
|
||||
msec -= sec * 1000;
|
||||
return (sec * sample_rate + msec * sample_rate / 1000) * stereo;
|
||||
}
|
||||
|
||||
long Track_tell( struct Hes_Emu* this )
|
||||
int Track_tell( struct Hes_Emu* this )
|
||||
{
|
||||
blargg_long rate = this->sample_rate_ * stereo;
|
||||
blargg_long sec = this->out_time / rate;
|
||||
return sec * 1000 + (this->out_time - sec * rate) * 1000 / rate;
|
||||
int rate = this->sample_rate_ * stereo;
|
||||
int sec = track_sample_count( &this->track_filter ) / rate;
|
||||
return sec * 1000 + (track_sample_count( &this->track_filter ) - sec * rate) * 1000 / rate;
|
||||
}
|
||||
|
||||
blargg_err_t Track_seek( struct Hes_Emu* this, long msec )
|
||||
blargg_err_t Track_seek( struct Hes_Emu* this, int msec )
|
||||
{
|
||||
blargg_long time = msec_to_samples( msec, this->sample_rate_ );
|
||||
if ( time < this->out_time )
|
||||
RETURN_ERR( Hes_start_track( this, this->current_track_ ) );
|
||||
return Track_skip( this, time - this->out_time );
|
||||
int time = msec_to_samples( msec, this->sample_rate_ );
|
||||
if ( time < track_sample_count( &this->track_filter ) )
|
||||
RETURN_ERR( Hes_start_track( this, this->current_track_ ) );
|
||||
return Track_skip( this, time - track_sample_count( &this->track_filter ) );
|
||||
}
|
||||
|
||||
blargg_err_t skip_( struct Hes_Emu* this, long count );
|
||||
blargg_err_t skip_( struct Hes_Emu* this, long count )
|
||||
blargg_err_t skip_( void* emu, int count )
|
||||
{
|
||||
struct Hes_Emu* this = (struct Hes_Emu*) emu;
|
||||
|
||||
// for long skip, mute sound
|
||||
const long threshold = 30000;
|
||||
const int threshold = 32768;
|
||||
if ( count > threshold )
|
||||
{
|
||||
int saved_mute = this->mute_mask_;
|
||||
Sound_mute_voices( this, ~0 );
|
||||
|
||||
while ( count > threshold / 2 && !this->emu_track_ended_ )
|
||||
{
|
||||
RETURN_ERR( play_( this, buf_size, this->buf ) );
|
||||
count -= buf_size;
|
||||
}
|
||||
int n = count - threshold/2;
|
||||
n &= ~(2048-1); // round to multiple of 2048
|
||||
count -= n;
|
||||
RETURN_ERR( skippy_( &this->track_filter, n ) );
|
||||
|
||||
Sound_mute_voices( this, saved_mute );
|
||||
}
|
||||
|
||||
while ( count && !this->emu_track_ended_ )
|
||||
{
|
||||
long n = buf_size;
|
||||
if ( n > count )
|
||||
n = count;
|
||||
count -= n;
|
||||
RETURN_ERR( play_( this, n, this->buf ) );
|
||||
}
|
||||
return 0;
|
||||
return skippy_( &this->track_filter, count );
|
||||
}
|
||||
|
||||
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
|
||||
this->out_time += count;
|
||||
|
||||
// remove from silence and buf first
|
||||
{
|
||||
long n = min( count, this->silence_count );
|
||||
this->silence_count -= n;
|
||||
count -= n;
|
||||
|
||||
n = min( count, this->buf_remain );
|
||||
this->buf_remain -= n;
|
||||
count -= n;
|
||||
}
|
||||
|
||||
if ( count && !this->emu_track_ended_ )
|
||||
{
|
||||
this->emu_time += count;
|
||||
|
||||
// End track if error
|
||||
if ( skip_( this, count ) )
|
||||
this->emu_track_ended_ = true;
|
||||
}
|
||||
|
||||
if ( !(this->silence_count | this->buf_remain) ) // caught up to emulator, so update track ended
|
||||
this->track_ended |= this->emu_track_ended_;
|
||||
|
||||
return 0;
|
||||
return track_skip( &this->track_filter, count );
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Fading
|
||||
|
||||
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 )
|
||||
{
|
||||
this->fade_step = this->sample_rate_ * length_msec / (fade_block_size * fade_shift * 1000 / stereo);
|
||||
this->fade_start = msec_to_samples( start_msec, this->sample_rate_ );
|
||||
track_set_fade( &this->track_filter, msec_to_samples( start_msec, this->sample_rate_ ),
|
||||
length_msec * this->sample_rate_ / (1000 / stereo) );
|
||||
}
|
||||
|
||||
// unit / pow( 2.0, (double) x / step )
|
||||
static int int_log( blargg_long x, int step, int unit );
|
||||
static int int_log( blargg_long x, int step, int unit )
|
||||
blargg_err_t Hes_play( struct Hes_Emu* this, int out_count, sample_t* out )
|
||||
{
|
||||
int shift = x / step;
|
||||
int fraction = (x - shift * step) * unit / step;
|
||||
return ((unit - fraction) + (fraction >> 1)) >> shift;
|
||||
}
|
||||
|
||||
void handle_fade( struct Hes_Emu* this, long out_count, sample_t* out );
|
||||
void handle_fade( struct Hes_Emu* this, long out_count, sample_t* out )
|
||||
{
|
||||
int i;
|
||||
for ( i = 0; i < out_count; i += fade_block_size )
|
||||
{
|
||||
int const shift = 14;
|
||||
int const unit = 1 << shift;
|
||||
int gain = int_log( (this->out_time + i - this->fade_start) / fade_block_size,
|
||||
this->fade_step, unit );
|
||||
if ( gain < (unit >> fade_shift) )
|
||||
this->track_ended = this->emu_track_ended_ = true;
|
||||
|
||||
sample_t* io = &out [i];
|
||||
int count;
|
||||
for ( count = min( fade_block_size, out_count - i ); count; --count )
|
||||
{
|
||||
*io = (sample_t) ((*io * gain) >> shift);
|
||||
++io;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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;
|
||||
require( this->current_track_ >= 0 );
|
||||
require( out_count % stereo == 0 );
|
||||
return track_play( &this->track_filter, out_count, out );
|
||||
}
|
||||
|
|
|
@ -12,88 +12,67 @@
|
|||
#include "hes_apu_adpcm.h"
|
||||
#include "hes_cpu.h"
|
||||
#include "m3u_playlist.h"
|
||||
|
||||
typedef short sample_t;
|
||||
|
||||
enum { buf_size = 2048 };
|
||||
#include "track_filter.h"
|
||||
|
||||
// HES file header
|
||||
enum { info_offset = 0x20 };
|
||||
enum { header_size = 0x20 };
|
||||
struct header_t
|
||||
{
|
||||
byte tag [4];
|
||||
byte tag [4];
|
||||
byte vers;
|
||||
byte first_track;
|
||||
byte init_addr [2];
|
||||
byte banks [8];
|
||||
byte data_tag [4];
|
||||
byte size [4];
|
||||
byte addr [4];
|
||||
byte unused [4];
|
||||
byte banks [8];
|
||||
byte data_tag [4];
|
||||
byte size [4];
|
||||
byte addr [4];
|
||||
byte unused [4];
|
||||
};
|
||||
|
||||
|
||||
struct timer_t {
|
||||
hes_time_t last_time;
|
||||
blargg_long count;
|
||||
blargg_long load;
|
||||
int raw_load;
|
||||
byte enabled;
|
||||
byte fired;
|
||||
int count;
|
||||
int load;
|
||||
int raw_load;
|
||||
byte enabled;
|
||||
byte fired;
|
||||
};
|
||||
|
||||
struct vdp_t {
|
||||
hes_time_t next_vbl;
|
||||
byte latch;
|
||||
byte control;
|
||||
byte latch;
|
||||
byte control;
|
||||
};
|
||||
|
||||
struct irq_t {
|
||||
hes_time_t timer;
|
||||
hes_time_t vdp;
|
||||
byte disables;
|
||||
byte disables;
|
||||
};
|
||||
|
||||
|
||||
struct Hes_Emu {
|
||||
hes_time_t play_period;
|
||||
hes_time_t last_frame_hook;
|
||||
int timer_base;
|
||||
|
||||
struct timer_t timer;
|
||||
struct vdp_t vdp;
|
||||
struct irq_t irq;
|
||||
struct vdp_t vdp;
|
||||
struct irq_t irq;
|
||||
|
||||
// Sound
|
||||
long clock_rate_;
|
||||
long sample_rate_;
|
||||
int clock_rate_;
|
||||
int sample_rate_;
|
||||
unsigned buf_changed_count;
|
||||
int voice_count_;
|
||||
int const* voice_types_;
|
||||
int mute_mask_;
|
||||
int tempo_;
|
||||
int gain_;
|
||||
|
||||
// track-specific
|
||||
byte track_count;
|
||||
volatile bool track_ended;
|
||||
int current_track_;
|
||||
blargg_long out_time; // number of samples played since start of track
|
||||
blargg_long emu_time; // number of samples emulator has generated since start of track
|
||||
bool emu_track_ended_; // emulator has reached end of track
|
||||
|
||||
// fading
|
||||
blargg_long fade_start;
|
||||
int fade_step;
|
||||
|
||||
// silence detection
|
||||
// Disable automatic end-of-track detection and skipping of silence at beginning
|
||||
bool ignore_silence;
|
||||
|
||||
int max_initial_silence;
|
||||
int mute_mask_;
|
||||
int silence_lookahead; // speed to run emulator when looking ahead for silence
|
||||
long silence_time; // number of samples where most recent silence began
|
||||
long silence_count; // number of samples of silence to play before using buf
|
||||
long buf_remain; // number of samples left in silence buffer
|
||||
|
||||
// Larger files at the end
|
||||
// Header for currently loaded file
|
||||
|
@ -102,19 +81,22 @@ struct Hes_Emu {
|
|||
// M3u Playlist
|
||||
struct M3u_Playlist m3u;
|
||||
|
||||
struct setup_t tfilter;
|
||||
struct Track_Filter track_filter;
|
||||
|
||||
// Hes Cpu
|
||||
byte* write_pages [page_count + 1]; // 0 if unmapped or I/O space
|
||||
struct Hes_Cpu cpu;
|
||||
struct Rom_Data rom;
|
||||
|
||||
struct Hes_Apu apu;
|
||||
struct Hes_Apu_Adpcm adpcm;
|
||||
|
||||
struct Stereo_Buffer stereo_buf;
|
||||
sample_t buf [buf_size];
|
||||
struct Multi_Buffer stereo_buf;
|
||||
|
||||
// rom & ram
|
||||
struct Rom_Data rom;
|
||||
byte sgx [3 * page_size + cpu_padding];
|
||||
byte* write_pages [page_count + 1]; // 0 if unmapped or I/O space
|
||||
byte ram [page_size];
|
||||
byte sgx [3 * page_size + cpu_padding];
|
||||
};
|
||||
|
||||
|
||||
|
@ -126,36 +108,48 @@ void Hes_init( struct Hes_Emu* this );
|
|||
void Hes_stop( struct Hes_Emu* this );
|
||||
|
||||
// Loads a file from memory
|
||||
blargg_err_t Hes_load( struct Hes_Emu* this, void* data, long size );
|
||||
blargg_err_t Hes_load_mem( struct Hes_Emu* this, void* data, long size );
|
||||
|
||||
// Set output sample rate. Must be called only once before loading file.
|
||||
blargg_err_t Hes_set_sample_rate( struct Hes_Emu* this, long sample_rate );
|
||||
blargg_err_t Hes_set_sample_rate( struct Hes_Emu* this, int sample_rate );
|
||||
|
||||
// Start a track, where 0 is the first track. Also clears warning string.
|
||||
blargg_err_t Hes_start_track( struct Hes_Emu* this, int );
|
||||
|
||||
// Generate 'count' samples info 'buf'. Output is in stereo. Any emulation
|
||||
// errors set warning string, and major errors also end track.
|
||||
blargg_err_t Hes_play( struct Hes_Emu* this, long count, sample_t* buf );
|
||||
blargg_err_t Hes_play( struct Hes_Emu* this, int count, sample_t* buf );
|
||||
|
||||
// Track status/control
|
||||
// Number of milliseconds (1000 msec = 1 second) played since ning of track
|
||||
long Track_tell( struct Hes_Emu* this );
|
||||
int Track_tell( struct Hes_Emu* this );
|
||||
|
||||
// Seek to new time in track. Seeking backwards or far forward can take a while.
|
||||
blargg_err_t Track_seek( struct Hes_Emu* this, long msec );
|
||||
blargg_err_t Track_seek( struct Hes_Emu* this, int msec );
|
||||
|
||||
// Skip n samples
|
||||
blargg_err_t Track_skip( struct Hes_Emu* this, long n );
|
||||
blargg_err_t Track_skip( struct Hes_Emu* this, int n );
|
||||
|
||||
// Set start time and length of track fade out. Once fade ends track_ended() returns
|
||||
// true. Fade time can be changed while track is playing.
|
||||
void Track_set_fade( struct Hes_Emu* this, long start_msec, long length_msec );
|
||||
void Track_set_fade( struct Hes_Emu* this, int start_msec, int length_msec );
|
||||
|
||||
// True if a track has reached its end
|
||||
static inline bool Track_ended( struct Hes_Emu* this )
|
||||
{
|
||||
return track_ended( &this->track_filter );
|
||||
}
|
||||
|
||||
// Disables automatic end-of-track detection and skipping of silence at beginning
|
||||
static inline void Track_ignore_silence( struct Hes_Emu* this, bool disable )
|
||||
{
|
||||
this->track_filter.silence_ignored_ = disable;
|
||||
}
|
||||
|
||||
// Get track length in milliseconds
|
||||
static inline long Track_get_length( struct Hes_Emu* this, int n )
|
||||
static inline int Track_get_length( struct Hes_Emu* this, int n )
|
||||
{
|
||||
long length = 120 * 1000; /* 2 minutes */
|
||||
int length = 120 * 1000; /* 2 minutes */
|
||||
if ( (this->m3u.size > 0) && (n < this->m3u.size) ) {
|
||||
struct entry_t* entry = &this->m3u.entries [n];
|
||||
length = entry->length;
|
||||
|
@ -185,45 +179,17 @@ static inline void Sound_set_gain( struct Hes_Emu* this, int g )
|
|||
this->gain_ = g;
|
||||
}
|
||||
|
||||
|
||||
// Emulation (You shouldn't touch these)
|
||||
|
||||
int Cpu_read( struct Hes_Emu* this, hes_addr_t );
|
||||
void Cpu_write( struct Hes_Emu* this, hes_addr_t, int );
|
||||
void Cpu_write_vdp( struct Hes_Emu* this, int addr, int data );
|
||||
int Cpu_done( struct Hes_Emu* this );
|
||||
|
||||
int Emu_cpu_read( struct Hes_Emu* this, hes_addr_t );
|
||||
void Emu_cpu_write( struct Hes_Emu* this, hes_addr_t, int data );
|
||||
|
||||
static inline byte const* Emu_cpu_set_mmr( struct Hes_Emu* this, int page, int bank )
|
||||
{
|
||||
this->write_pages [page] = 0;
|
||||
if ( bank < 0x80 )
|
||||
return Rom_at_addr( &this->rom, bank * (blargg_long) page_size );
|
||||
|
||||
byte* data = 0;
|
||||
switch ( bank )
|
||||
{
|
||||
case 0xF8:
|
||||
data = this->cpu.ram;
|
||||
break;
|
||||
|
||||
case 0xF9:
|
||||
case 0xFA:
|
||||
case 0xFB:
|
||||
data = &this->sgx [(bank - 0xF9) * page_size];
|
||||
break;
|
||||
|
||||
default:
|
||||
if ( bank != 0xFF ) {
|
||||
dprintf( "Unmapped bank $%02X\n", bank );
|
||||
}
|
||||
return this->rom.unmapped;
|
||||
}
|
||||
|
||||
this->write_pages [page] = data;
|
||||
return data;
|
||||
}
|
||||
void irq_changed( struct Hes_Emu* this );
|
||||
void run_until( struct Hes_Emu* this, hes_time_t );
|
||||
bool run_cpu( struct Hes_Emu* this, hes_time_t end );
|
||||
int read_mem_( struct Hes_Emu* this, hes_addr_t );
|
||||
int read_mem( struct Hes_Emu* this, hes_addr_t );
|
||||
void write_mem_( struct Hes_Emu* this, hes_addr_t, int data );
|
||||
void write_mem( struct Hes_Emu* this, hes_addr_t, int );
|
||||
void write_vdp( struct Hes_Emu* this, int addr, int data );
|
||||
void set_mmr( struct Hes_Emu* this, int reg, int bank );
|
||||
int cpu_done( struct Hes_Emu* this );
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Game_Music_Emu 0.5.5. http://www.slack.net/~ant/
|
||||
// Game_Music_Emu 0.6-pre. http://www.slack.net/~ant/
|
||||
|
||||
#include "kss_emu.h"
|
||||
|
||||
|
@ -17,29 +17,14 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
|
|||
|
||||
#include "blargg_source.h"
|
||||
|
||||
long const clock_rate = 3579545;
|
||||
int const clock_rate = 3579545;
|
||||
|
||||
const char gme_wrong_file_type [] = "Wrong file type for this emulator";
|
||||
|
||||
int const stereo = 2; // number of channels for stereo
|
||||
int const silence_max = 6; // seconds
|
||||
int const silence_threshold = 0x10;
|
||||
long const fade_block_size = 512;
|
||||
int const fade_shift = 8; // fade ends with gain at 1.0 / (1 << fade_shift)
|
||||
|
||||
static void clear_track_vars( struct Kss_Emu* this )
|
||||
{
|
||||
this->current_track = -1;
|
||||
this->out_time = 0;
|
||||
this->emu_time = 0;
|
||||
this->emu_track_ended_ = true;
|
||||
this->track_ended = true;
|
||||
this->fade_start = INT_MAX / 2 + 1;
|
||||
this->fade_step = 1;
|
||||
this->silence_time = 0;
|
||||
this->silence_count = 0;
|
||||
this->buf_remain = 0;
|
||||
// warning(); // clear warning
|
||||
this->current_track = -1;
|
||||
track_stop( &this->track_filter );
|
||||
}
|
||||
|
||||
static blargg_err_t init_opl_apu( enum opl_type_t type, struct Opl_Apu* out )
|
||||
|
@ -58,17 +43,15 @@ void Kss_init( struct Kss_Emu* this )
|
|||
this->chip_flags = 0;
|
||||
|
||||
// defaults
|
||||
this->max_initial_silence = 2;
|
||||
this->silence_lookahead = 6;
|
||||
this->ignore_silence = false;
|
||||
|
||||
this->voice_count = 0;
|
||||
clear_track_vars( this );
|
||||
this->tfilter = *track_get_setup( &this->track_filter );
|
||||
this->tfilter.max_initial = 2;
|
||||
this->tfilter.lookahead = 6;
|
||||
this->track_filter.silence_ignored_ = false;
|
||||
|
||||
memset( this->unmapped_read, 0xFF, sizeof this->unmapped_read );
|
||||
|
||||
// Init all stuff
|
||||
Buffer_init( &this->stereo_buffer );
|
||||
Buffer_init( &this->stereo_buf );
|
||||
|
||||
Z80_init( &this->cpu );
|
||||
Rom_init( &this->rom, page_size );
|
||||
|
@ -83,6 +66,10 @@ void Kss_init( struct Kss_Emu* this )
|
|||
init_opl_apu( type_msxmusic, &this->msx.music );
|
||||
init_opl_apu( type_msxaudio, &this->msx.audio );
|
||||
#endif
|
||||
|
||||
this->voice_count = 0;
|
||||
this->voice_types = 0;
|
||||
clear_track_vars( this );
|
||||
}
|
||||
|
||||
// Track info
|
||||
|
@ -96,7 +83,7 @@ static blargg_err_t check_kss_header( void const* header )
|
|||
|
||||
// Setup
|
||||
|
||||
static void update_gain( struct Kss_Emu* this )
|
||||
static void update_gain_( struct Kss_Emu* this )
|
||||
{
|
||||
int g = this->gain;
|
||||
if ( msx_music_enabled( this ) || msx_audio_enabled( this )
|
||||
|
@ -118,6 +105,15 @@ static void update_gain( struct Kss_Emu* this )
|
|||
if ( msx_audio_enabled( this ) ) Opl_volume( &this->msx.audio, g );
|
||||
}
|
||||
|
||||
static void update_gain( struct Kss_Emu* this )
|
||||
{
|
||||
if ( this->scc_accessed )
|
||||
{
|
||||
/* dprintf( "SCC accessed\n" ); */
|
||||
update_gain_( this );
|
||||
}
|
||||
}
|
||||
|
||||
blargg_err_t Kss_load_mem( struct Kss_Emu* this, const void* data, long size )
|
||||
{
|
||||
/* warning( core.warning() ); */
|
||||
|
@ -176,6 +172,10 @@ blargg_err_t Kss_load_mem( struct Kss_Emu* this, const void* data, long size )
|
|||
|
||||
// sms.psg
|
||||
this->voice_count = sms_osc_count;
|
||||
static int const types [sms_osc_count + opl_osc_count] = {
|
||||
wave_type+1, wave_type+3, wave_type+2, mixed_type+1, wave_type+0
|
||||
};
|
||||
this->voice_types = types;
|
||||
this->chip_flags |= sms_psg_flag;
|
||||
|
||||
// sms.fm
|
||||
|
@ -191,6 +191,10 @@ blargg_err_t Kss_load_mem( struct Kss_Emu* this, const void* data, long size )
|
|||
|
||||
// msx.psg
|
||||
this->voice_count = ay_osc_count;
|
||||
static int const types [ay_osc_count + opl_osc_count] = {
|
||||
wave_type+1, wave_type+3, wave_type+2, wave_type+0
|
||||
};
|
||||
this->voice_types = types;
|
||||
this->chip_flags |= msx_psg_flag;
|
||||
|
||||
/* if ( this->header.device_flags & 0x10 )
|
||||
|
@ -220,21 +224,27 @@ blargg_err_t Kss_load_mem( struct Kss_Emu* this, const void* data, long size )
|
|||
// msx.scc
|
||||
this->chip_flags |= msx_scc_flag;
|
||||
this->voice_count = ay_osc_count + scc_osc_count;
|
||||
static int const types [ay_osc_count + scc_osc_count] = {
|
||||
wave_type+1, wave_type+3, wave_type+2,
|
||||
wave_type+0, wave_type+4, wave_type+5, wave_type+6, wave_type+7,
|
||||
};
|
||||
this->voice_types = types;
|
||||
}
|
||||
}
|
||||
|
||||
this->silence_lookahead = 6;
|
||||
this->tfilter.lookahead = 6;
|
||||
if ( sms_fm_enabled( this ) || msx_music_enabled( this ) || msx_audio_enabled( this ) )
|
||||
{
|
||||
if ( !Opl_supported() )
|
||||
; /* warning( "FM sound not supported" ); */
|
||||
else
|
||||
this->silence_lookahead = 3; // Opl_Apu is really slow
|
||||
this->tfilter.lookahead = 3; // Opl_Apu is really slow
|
||||
}
|
||||
|
||||
this->clock_rate_ = clock_rate;
|
||||
Buffer_clock_rate( &this->stereo_buffer, clock_rate );
|
||||
this->buf_changed_count = Buffer_channels_changed_count( &this->stereo_buffer );
|
||||
Buffer_clock_rate( &this->stereo_buf, clock_rate );
|
||||
RETURN_ERR( Buffer_set_channel_count( &this->stereo_buf, this->voice_count, this->voice_types ) );
|
||||
this->buf_changed_count = Buffer_channels_changed_count( &this->stereo_buf );
|
||||
|
||||
Sound_set_tempo( this, this->tempo );
|
||||
Sound_mute_voices( this, this->mute_mask_ );
|
||||
|
@ -265,8 +275,8 @@ static void set_voice( struct Kss_Emu* this, int i, struct Blip_Buffer* center,
|
|||
}
|
||||
|
||||
if ( msx_scc_enabled( this ) && i < scc_osc_count ) Scc_set_output( &this->msx.scc, i, center );
|
||||
if ( msx_music_enabled( this ) && i < opl_osc_count ) Opl_set_output( &this->msx.music, center );
|
||||
if ( msx_audio_enabled( this ) && i < opl_osc_count ) Opl_set_output( &this->msx.audio, center );
|
||||
if ( msx_music_enabled( this ) && i < opl_osc_count ) Opl_set_output( &this->msx.music, center );
|
||||
if ( msx_audio_enabled( this ) && i < opl_osc_count ) Opl_set_output( &this->msx.audio, center );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -465,15 +475,17 @@ blargg_err_t end_frame( struct Kss_Emu* this, kss_time_t end )
|
|||
|
||||
// MUSIC
|
||||
|
||||
|
||||
blargg_err_t Kss_set_sample_rate( struct Kss_Emu* this, long rate )
|
||||
blargg_err_t Kss_set_sample_rate( struct Kss_Emu* this, int rate )
|
||||
{
|
||||
require( !this->sample_rate ); // sample rate can't be changed once set
|
||||
RETURN_ERR( Buffer_set_sample_rate( &this->stereo_buffer, rate, 1000 / 20 ) );
|
||||
RETURN_ERR( Buffer_set_sample_rate( &this->stereo_buf, rate, 1000 / 20 ) );
|
||||
|
||||
// Set bass frequency
|
||||
Buffer_bass_freq( &this->stereo_buffer, 180 );
|
||||
Buffer_bass_freq( &this->stereo_buf, 180 );
|
||||
|
||||
this->sample_rate = rate;
|
||||
RETURN_ERR( track_init( &this->track_filter, this ) );
|
||||
this->tfilter.max_silence = 6 * stereo * this->sample_rate;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -501,7 +513,7 @@ void Sound_mute_voices( struct Kss_Emu* this, int mask )
|
|||
}
|
||||
else
|
||||
{
|
||||
struct channel_t ch = Buffer_channel( &this->stereo_buffer );
|
||||
struct channel_t ch = Buffer_channel( &this->stereo_buf, i );
|
||||
assert( (ch.center && ch.left && ch.right) ||
|
||||
(!ch.center && !ch.left && !ch.right) ); // all or nothing
|
||||
set_voice( this, i, ch.center, ch.left, ch.right );
|
||||
|
@ -523,7 +535,6 @@ void Sound_set_tempo( struct Kss_Emu* this, int t )
|
|||
this->play_period = (blip_time_t) ((period * FP_ONE_TEMPO) / t);
|
||||
}
|
||||
|
||||
void fill_buf( struct Kss_Emu* this );
|
||||
blargg_err_t Kss_start_track( struct Kss_Emu* this, int track )
|
||||
{
|
||||
clear_track_vars( this );
|
||||
|
@ -536,7 +547,7 @@ blargg_err_t Kss_start_track( struct Kss_Emu* this, int track )
|
|||
|
||||
this->current_track = track;
|
||||
|
||||
Buffer_clear( &this->stereo_buffer );
|
||||
Buffer_clear( &this->stereo_buf );
|
||||
|
||||
if ( sms_psg_enabled( this ) ) Sms_apu_reset( &this->sms.psg, 0, 0 );
|
||||
if ( sms_fm_enabled( this ) ) Opl_reset( &this->sms.fm );
|
||||
|
@ -546,7 +557,7 @@ blargg_err_t Kss_start_track( struct Kss_Emu* this, int track )
|
|||
if ( msx_audio_enabled( this ) ) Opl_reset( &this->msx.audio );
|
||||
|
||||
this->scc_accessed = false;
|
||||
update_gain( this );
|
||||
update_gain_( this );
|
||||
|
||||
memset( this->ram, 0xC9, 0x4000 );
|
||||
memset( this->ram + 0x4000, 0, sizeof this->ram - 0x4000 );
|
||||
|
@ -598,286 +609,106 @@ blargg_err_t Kss_start_track( struct Kss_Emu* this, int track )
|
|||
this->gain_updated = false;
|
||||
jsr( this, this->header.init_addr );
|
||||
|
||||
this->emu_track_ended_ = false;
|
||||
this->track_ended = false;
|
||||
// convert filter times to samples
|
||||
struct setup_t s = this->tfilter;
|
||||
s.max_initial *= this->sample_rate * stereo;
|
||||
#ifdef GME_DISABLE_SILENCE_LOOKAHEAD
|
||||
s.lookahead = 1;
|
||||
#endif
|
||||
track_setup( &this->track_filter, &s );
|
||||
|
||||
if ( !this->ignore_silence )
|
||||
{
|
||||
// play until non-silence or end of track
|
||||
long end;
|
||||
for ( end = this->max_initial_silence * stereo * this->sample_rate; this->emu_time < end; )
|
||||
{
|
||||
fill_buf( this );
|
||||
if ( this->buf_remain | (int) this->emu_track_ended_ )
|
||||
break;
|
||||
}
|
||||
|
||||
this->emu_time = this->buf_remain;
|
||||
this->out_time = 0;
|
||||
this->silence_time = 0;
|
||||
this->silence_count = 0;
|
||||
}
|
||||
/* return track_ended() ? warning() : 0; */
|
||||
return 0;
|
||||
return track_start( &this->track_filter );
|
||||
}
|
||||
|
||||
// Tell/Seek
|
||||
|
||||
static blargg_long msec_to_samples( blargg_long msec, long sample_rate )
|
||||
static int msec_to_samples( int msec, int sample_rate )
|
||||
{
|
||||
blargg_long sec = msec / 1000;
|
||||
int sec = msec / 1000;
|
||||
msec -= sec * 1000;
|
||||
return (sec * sample_rate + msec * sample_rate / 1000) * stereo;
|
||||
}
|
||||
|
||||
long Track_tell( struct Kss_Emu* this )
|
||||
int Track_tell( struct Kss_Emu* this )
|
||||
{
|
||||
blargg_long rate = this->sample_rate * stereo;
|
||||
blargg_long sec = this->out_time / rate;
|
||||
return sec * 1000 + (this->out_time - sec * rate) * 1000 / rate;
|
||||
int rate = this->sample_rate * stereo;
|
||||
int sec = track_sample_count( &this->track_filter ) / rate;
|
||||
return sec * 1000 + (track_sample_count( &this->track_filter ) - sec * rate) * 1000 / rate;
|
||||
}
|
||||
|
||||
blargg_err_t Track_seek( struct Kss_Emu* this, long msec )
|
||||
blargg_err_t Track_seek( struct Kss_Emu* this, int msec )
|
||||
{
|
||||
blargg_long time = msec_to_samples( msec, this->sample_rate );
|
||||
if ( time < this->out_time )
|
||||
RETURN_ERR( Kss_start_track( this, this->current_track ) );
|
||||
return Track_skip( this, time - this->out_time );
|
||||
int time = msec_to_samples( msec, this->sample_rate );
|
||||
if ( time < track_sample_count( &this->track_filter ) )
|
||||
RETURN_ERR( Kss_start_track( this, this->current_track ) );
|
||||
return Track_skip( this, time - track_sample_count( &this->track_filter ) );
|
||||
}
|
||||
|
||||
blargg_err_t play_( struct Kss_Emu* this, long count, sample_t* out );
|
||||
static blargg_err_t skip_( struct Kss_Emu* this, long count )
|
||||
blargg_err_t skip_( void *emu, int count )
|
||||
{
|
||||
struct Kss_Emu* this = (struct Kss_Emu*) emu;
|
||||
|
||||
// for long skip, mute sound
|
||||
const long threshold = 30000;
|
||||
const int threshold = 32768;
|
||||
if ( count > threshold )
|
||||
{
|
||||
int saved_mute = this->mute_mask_;
|
||||
Sound_mute_voices( this, ~0 );
|
||||
|
||||
while ( count > threshold / 2 && !this->emu_track_ended_ )
|
||||
{
|
||||
RETURN_ERR( play_( this, buf_size, this->buf ) );
|
||||
count -= buf_size;
|
||||
}
|
||||
int n = count - threshold/2;
|
||||
n &= ~(2048-1); // round to multiple of 2048
|
||||
count -= n;
|
||||
RETURN_ERR( skippy_( &this->track_filter, n ) );
|
||||
|
||||
Sound_mute_voices( this, saved_mute );
|
||||
}
|
||||
|
||||
while ( count && !this->emu_track_ended_ )
|
||||
{
|
||||
long n = buf_size;
|
||||
if ( n > count )
|
||||
n = count;
|
||||
count -= n;
|
||||
RETURN_ERR( play_( this, n, this->buf ) );
|
||||
}
|
||||
return 0;
|
||||
return skippy_( &this->track_filter, count );
|
||||
}
|
||||
|
||||
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
|
||||
this->out_time += count;
|
||||
|
||||
// remove from silence and buf first
|
||||
{
|
||||
long n = min( count, this->silence_count );
|
||||
this->silence_count -= n;
|
||||
count -= n;
|
||||
|
||||
n = min( count, this->buf_remain );
|
||||
this->buf_remain -= n;
|
||||
count -= n;
|
||||
}
|
||||
|
||||
if ( count && !this->emu_track_ended_ )
|
||||
{
|
||||
this->emu_time += count;
|
||||
if ( skip_( this, count ) )
|
||||
this->emu_track_ended_ = true;
|
||||
}
|
||||
|
||||
if ( !(this->silence_count | this->buf_remain) ) // caught up to emulator, so update track ended
|
||||
this->track_ended |= this->emu_track_ended_;
|
||||
|
||||
return 0;
|
||||
return track_skip( &this->track_filter, count );
|
||||
}
|
||||
|
||||
// Fading
|
||||
|
||||
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 )
|
||||
{
|
||||
this->fade_step = this->sample_rate * length_msec / (fade_block_size * fade_shift * 1000 / stereo);
|
||||
this->fade_start = msec_to_samples( start_msec, this->sample_rate );
|
||||
track_set_fade( &this->track_filter, msec_to_samples( start_msec, this->sample_rate ),
|
||||
length_msec * this->sample_rate / (1000 / stereo) );
|
||||
}
|
||||
|
||||
// unit / pow( 2.0, (double) x / step )
|
||||
static int int_log( blargg_long x, int step, int unit )
|
||||
blargg_err_t Kss_play( struct Kss_Emu* this, int out_count, sample_t* out )
|
||||
{
|
||||
int shift = x / step;
|
||||
int fraction = (x - shift * step) * unit / step;
|
||||
return ((unit - fraction) + (fraction >> 1)) >> shift;
|
||||
require( this->current_track >= 0 );
|
||||
require( out_count % stereo == 0 );
|
||||
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;
|
||||
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;
|
||||
struct Kss_Emu* this = (struct Kss_Emu*) emu;
|
||||
|
||||
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 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;
|
||||
int remain = count;
|
||||
while ( remain )
|
||||
{
|
||||
remain -= Buffer_read_samples( &this->stereo_buffer, &out [count - remain], remain );
|
||||
Buffer_disable_immediate_removal( &this->stereo_buf );
|
||||
remain -= Buffer_read_samples( &this->stereo_buf, &out [count - remain], remain );
|
||||
if ( remain )
|
||||
{
|
||||
if ( this->buf_changed_count != Buffer_channels_changed_count( &this->stereo_buffer ) )
|
||||
if ( this->buf_changed_count != Buffer_channels_changed_count( &this->stereo_buf ) )
|
||||
{
|
||||
this->buf_changed_count = Buffer_channels_changed_count( &this->stereo_buffer );
|
||||
this->buf_changed_count = Buffer_channels_changed_count( &this->stereo_buf );
|
||||
|
||||
// Remute voices
|
||||
Sound_mute_voices( this, this->mute_mask_ );
|
||||
}
|
||||
int msec = Buffer_length( &this->stereo_buffer );
|
||||
/* blip_time_t clocks_emulated = (blargg_long) msec * clock_rate_ / 1000; */
|
||||
int msec = Buffer_length( &this->stereo_buf );
|
||||
blip_time_t clocks_emulated = msec * this->clock_rate_ / 1000 - 100;
|
||||
RETURN_ERR( run_clocks( this, &clocks_emulated ) );
|
||||
assert( clocks_emulated );
|
||||
Buffer_end_frame( &this->stereo_buffer, clocks_emulated );
|
||||
Buffer_end_frame( &this->stereo_buf, clocks_emulated );
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -16,8 +16,8 @@
|
|||
#include "ay_apu.h"
|
||||
#include "opl_apu.h"
|
||||
#include "m3u_playlist.h"
|
||||
#include "track_filter.h"
|
||||
|
||||
typedef short sample_t;
|
||||
typedef int kss_time_t;
|
||||
typedef int kss_addr_t;
|
||||
typedef struct Z80_Cpu Kss_Cpu;
|
||||
|
@ -35,7 +35,6 @@ enum {
|
|||
enum { idle_addr = 0xFFFF };
|
||||
enum { scc_enabled_true = 0xC000 };
|
||||
enum { mem_size = 0x10000 };
|
||||
enum { buf_size = 2048 };
|
||||
|
||||
// KSS file header
|
||||
enum { header_size = 0x20 };
|
||||
|
@ -84,8 +83,6 @@ struct Kss_Emu {
|
|||
bool scc_accessed;
|
||||
bool gain_updated;
|
||||
|
||||
int track_count;
|
||||
|
||||
unsigned scc_enabled; // 0 or 0xC000
|
||||
int bank_count;
|
||||
|
||||
|
@ -94,46 +91,32 @@ struct Kss_Emu {
|
|||
int ay_latch;
|
||||
|
||||
// general
|
||||
int max_initial_silence;
|
||||
int voice_count;
|
||||
int const* voice_types;
|
||||
int mute_mask_;
|
||||
int tempo;
|
||||
int gain;
|
||||
|
||||
long sample_rate;
|
||||
int sample_rate;
|
||||
|
||||
// track-specific
|
||||
int track_count;
|
||||
int current_track;
|
||||
blargg_long out_time; // number of samples played since start of track
|
||||
blargg_long emu_time; // number of samples emulator has generated since start of track
|
||||
bool emu_track_ended_; // emulator has reached end of track
|
||||
volatile bool track_ended;
|
||||
|
||||
// fading
|
||||
blargg_long fade_start;
|
||||
int fade_step;
|
||||
|
||||
// silence detection
|
||||
int silence_lookahead; // speed to run emulator when looking ahead for silence
|
||||
bool ignore_silence;
|
||||
long silence_time; // number of samples where most recent silence began
|
||||
long silence_count; // number of samples of silence to play before using buf
|
||||
long buf_remain; // number of samples left in silence buffer
|
||||
|
||||
struct Stereo_Buffer stereo_buffer; // NULL if using custom buffer
|
||||
long clock_rate_;
|
||||
int clock_rate_;
|
||||
unsigned buf_changed_count;
|
||||
|
||||
// M3u Playlist
|
||||
// M3u Playlist
|
||||
struct M3u_Playlist m3u;
|
||||
|
||||
// large items
|
||||
sample_t buf [buf_size];
|
||||
struct setup_t tfilter;
|
||||
struct Track_Filter track_filter;
|
||||
|
||||
struct sms_t sms;
|
||||
struct msx_t msx;
|
||||
|
||||
Kss_Cpu cpu;
|
||||
struct Multi_Buffer stereo_buf; // NULL if using custom buffer
|
||||
struct Rom_Data rom;
|
||||
|
||||
byte unmapped_read [0x100];
|
||||
|
@ -148,34 +131,46 @@ blargg_err_t Kss_load_mem( struct Kss_Emu* this, const void* data, long size );
|
|||
blargg_err_t end_frame( struct Kss_Emu* this, kss_time_t );
|
||||
|
||||
// Set output sample rate. Must be called only once before loading file.
|
||||
blargg_err_t Kss_set_sample_rate( struct Kss_Emu* this, long sample_rate );
|
||||
blargg_err_t Kss_set_sample_rate( struct Kss_Emu* this, int sample_rate );
|
||||
|
||||
// Start a track, where 0 is the first track. Also clears warning string.
|
||||
blargg_err_t Kss_start_track( struct Kss_Emu* this, int track );
|
||||
|
||||
// Generate 'count' samples info 'buf'. Output is in stereo. Any emulation
|
||||
// errors set warning string, and major errors also end track.
|
||||
blargg_err_t Kss_play( struct Kss_Emu* this, long count, sample_t* buf );
|
||||
blargg_err_t Kss_play( struct Kss_Emu* this, int count, sample_t* buf );
|
||||
|
||||
// Track status/control
|
||||
|
||||
// Number of milliseconds (1000 msec = 1 second) played since beginning of track
|
||||
long Track_tell( struct Kss_Emu* this );
|
||||
int Track_tell( struct Kss_Emu* this );
|
||||
|
||||
// Seek to new time in track. Seeking backwards or far forward can take a while.
|
||||
blargg_err_t Track_seek( struct Kss_Emu* this, long msec );
|
||||
blargg_err_t Track_seek( struct Kss_Emu* this, int msec );
|
||||
|
||||
// Skip n samples
|
||||
blargg_err_t Track_skip( struct Kss_Emu* this, long n );
|
||||
blargg_err_t Track_skip( struct Kss_Emu* this, int n );
|
||||
|
||||
// Set start time and length of track fade out. Once fade ends track_ended() returns
|
||||
// true. Fade time can be changed while track is playing.
|
||||
void Track_set_fade( struct Kss_Emu* this, long start_msec, long length_msec );
|
||||
void Track_set_fade( struct Kss_Emu* this, int start_msec, int length_msec );
|
||||
|
||||
// True if a track has reached its end
|
||||
static inline bool Track_ended( struct Kss_Emu* this )
|
||||
{
|
||||
return track_ended( &this->track_filter );
|
||||
}
|
||||
|
||||
// Disables automatic end-of-track detection and skipping of silence at beginning
|
||||
static inline void Track_ignore_silence( struct Kss_Emu* this, bool disable )
|
||||
{
|
||||
this->track_filter.silence_ignored_ = disable;
|
||||
}
|
||||
|
||||
// Get track length in milliseconds
|
||||
static inline long Track_get_length( struct Kss_Emu* this, int n )
|
||||
static inline int Track_get_length( struct Kss_Emu* this, int n )
|
||||
{
|
||||
long length = 0;
|
||||
int length = 0;
|
||||
|
||||
if ( (this->m3u.size > 0) && (n < this->m3u.size) ) {
|
||||
struct entry_t* entry = &this->m3u.entries [n];
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Blip_Buffer 0.4.1. http://www.slack.net/~ant/
|
||||
// Multi_Buffer 0.4.1. http://www.slack.net/~ant/
|
||||
|
||||
#include "multi_buffer.h"
|
||||
|
||||
|
@ -15,212 +15,272 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
|
|||
|
||||
#include "blargg_source.h"
|
||||
|
||||
#ifdef BLARGG_ENABLE_OPTIMIZER
|
||||
#include BLARGG_ENABLE_OPTIMIZER
|
||||
#endif
|
||||
// Tracked_Blip_Buffer
|
||||
|
||||
// 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->bufs [1] );
|
||||
Blip_init( &this->bufs [2] );
|
||||
|
||||
this->chan.center = &this->bufs [0];
|
||||
this->chan.left = &this->bufs [1];
|
||||
this->chan.right = &this->bufs [2];
|
||||
|
||||
this->length_ = 0;
|
||||
this->sample_rate_ = 0;
|
||||
this->channels_changed_count_ = 1;
|
||||
this->samples_per_frame_ = 2;
|
||||
Blip_init( &this->blip );
|
||||
this->last_non_silence = 0;
|
||||
}
|
||||
|
||||
blargg_err_t Buffer_set_sample_rate( struct Stereo_Buffer* this, long rate, int msec )
|
||||
void Tracked_clear( struct Tracked_Blip_Buffer* this )
|
||||
{
|
||||
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;
|
||||
for ( i = 0; i < buf_count; i++ )
|
||||
RETURN_ERR( Blip_set_sample_rate( &this->bufs[i], rate, msec ) );
|
||||
for ( i = bufs_size; --i >= 0; )
|
||||
RETURN_ERR( Blip_set_sample_rate( &this->bufs [i].blip, rate, msec ) );
|
||||
|
||||
this->sample_rate_ = Blip_sample_rate( &this->bufs [0] );
|
||||
this->length_ = Blip_length( &this->bufs [0] );
|
||||
this->sample_rate_ = Blip_sample_rate( &this->bufs [0].blip );
|
||||
this->length_ = Blip_length( &this->bufs [0].blip );
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Buffer_clock_rate( struct Stereo_Buffer* this, long rate )
|
||||
void Buffer_clock_rate( struct Multi_Buffer* this, int rate )
|
||||
{
|
||||
int i;
|
||||
for ( i = 0; i < buf_count; i++ )
|
||||
Blip_set_clock_rate( &this->bufs [i], rate );
|
||||
for ( i = bufs_size; --i >= 0; )
|
||||
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;
|
||||
for ( i = 0; i < buf_count; i++ )
|
||||
Blip_bass_freq( &this->bufs [i], bass );
|
||||
int i;
|
||||
for ( i = bufs_size; --i >= 0; )
|
||||
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;
|
||||
}
|
||||
|
||||
void Buffer_clear( struct Stereo_Buffer* this )
|
||||
void Buffer_clear( struct Multi_Buffer* this )
|
||||
{
|
||||
this->stereo_added = 0;
|
||||
this->was_stereo = false;
|
||||
int i;
|
||||
for ( i = 0; i < buf_count; i++ )
|
||||
Blip_clear( &this->bufs [i], 1 );
|
||||
this->mixer.samples_read = 0;
|
||||
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;
|
||||
unsigned i;
|
||||
for ( i = 0; i < buf_count; i++ )
|
||||
{
|
||||
this->stereo_added |= Blip_clear_modified( &this->bufs [i] ) << i;
|
||||
Blip_end_frame( &this->bufs [i], clock_count );
|
||||
}
|
||||
int i;
|
||||
for ( i = bufs_size; --i >= 0; )
|
||||
Tracked_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
|
||||
count = (unsigned) count / 2;
|
||||
require( (out_size & 1) == 0 ); // must read an even number of samples
|
||||
out_size = min( out_size, Buffer_samples_avail( this ) );
|
||||
|
||||
long avail = Blip_samples_avail( &this->bufs [0] );
|
||||
if ( count > avail )
|
||||
count = avail;
|
||||
if ( count )
|
||||
int pair_count = (int) (out_size >> 1);
|
||||
if ( pair_count )
|
||||
{
|
||||
int bufs_used = this->stereo_added | this->was_stereo;
|
||||
//dprintf( "%X\n", bufs_used );
|
||||
if ( bufs_used <= 1 )
|
||||
{
|
||||
Buffer_mix_mono( this, out, count );
|
||||
Blip_remove_samples( &this->bufs [0], count );
|
||||
Blip_remove_silence( &this->bufs [1], count );
|
||||
Blip_remove_silence( &this->bufs [2], count );
|
||||
}
|
||||
else if ( bufs_used & 1 )
|
||||
{
|
||||
Buffer_mix_stereo( this, out, count );
|
||||
Blip_remove_samples( &this->bufs [0], count );
|
||||
Blip_remove_samples( &this->bufs [1], count );
|
||||
Blip_remove_samples( &this->bufs [2], count );
|
||||
}
|
||||
else
|
||||
{
|
||||
Buffer_mix_stereo_no_center( this, out, count );
|
||||
Blip_remove_silence( &this->bufs [0], count );
|
||||
Blip_remove_samples( &this->bufs [1], count );
|
||||
Blip_remove_samples( &this->bufs [2], count );
|
||||
}
|
||||
Mixer_read_pairs( &this->mixer, out, pair_count );
|
||||
|
||||
// to do: this might miss opportunities for optimization
|
||||
if ( !Blip_samples_avail( &this->bufs [0] ) )
|
||||
if ( Buffer_samples_avail( this ) <= 0 || this->immediate_removal_ )
|
||||
{
|
||||
this->was_stereo = this->stereo_added;
|
||||
this->stereo_added = 0;
|
||||
int i;
|
||||
for ( i = bufs_size; --i >= 0; )
|
||||
{
|
||||
buf_t* b = &this->bufs [i];
|
||||
// TODO: might miss non-silence settling since it checks END of last read
|
||||
if ( !Tracked_non_silent( b ) )
|
||||
Tracked_remove_silence( b, this->mixer.samples_read );
|
||||
else
|
||||
Tracked_remove_samples( b, this->mixer.samples_read );
|
||||
}
|
||||
this->mixer.samples_read = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return count * 2;
|
||||
}
|
||||
|
||||
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] );
|
||||
return out_size;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Multi-channel sound buffer interface, and basic mono and stereo buffers
|
||||
// Multi-channel sound buffer interface, stereo and effects buffers
|
||||
|
||||
// Blip_Buffer 0.4.1
|
||||
#ifndef MULTI_BUFFER_H
|
||||
|
@ -16,57 +16,99 @@ struct channel_t {
|
|||
|
||||
enum { type_index_mask = 0xFF };
|
||||
enum { wave_type = 0x100, noise_type = 0x200, mixed_type = wave_type | noise_type };
|
||||
enum { buf_count = 3 };
|
||||
enum { stereo = 2 };
|
||||
enum { bufs_size = 3 };
|
||||
|
||||
struct Stereo_Buffer {
|
||||
struct Blip_Buffer bufs [buf_count];
|
||||
struct channel_t chan;
|
||||
int stereo_added;
|
||||
int was_stereo;
|
||||
|
||||
unsigned channels_changed_count_;
|
||||
long sample_rate_;
|
||||
int length_;
|
||||
int samples_per_frame_;
|
||||
// Tracked_Blip_Buffer
|
||||
struct Tracked_Blip_Buffer {
|
||||
struct Blip_Buffer blip;
|
||||
int last_non_silence;
|
||||
};
|
||||
|
||||
// Initializes Stereo_Buffer structure
|
||||
void Buffer_init( struct Stereo_Buffer* this );
|
||||
void Tracked_init( struct Tracked_Blip_Buffer* this );
|
||||
unsigned Tracked_non_silent( struct Tracked_Blip_Buffer* this );
|
||||
void Tracked_remove_all_samples( struct Tracked_Blip_Buffer * this );
|
||||
int Tracked_read_samples( struct Tracked_Blip_Buffer* this, blip_sample_t [], int );
|
||||
void Tracked_remove_silence( struct Tracked_Blip_Buffer* this, int );
|
||||
void Tracked_remove_samples( struct Tracked_Blip_Buffer* this, int );
|
||||
void Tracked_clear( struct Tracked_Blip_Buffer* this );
|
||||
void Tracked_end_frame( struct Tracked_Blip_Buffer* this, blip_time_t );
|
||||
|
||||
blargg_err_t Buffer_set_sample_rate( struct Stereo_Buffer* this, long, int msec );
|
||||
void Buffer_clock_rate( struct Stereo_Buffer* this, long );
|
||||
void Buffer_bass_freq( struct Stereo_Buffer* this, int );
|
||||
void Buffer_clear( struct Stereo_Buffer* this );
|
||||
struct channel_t Buffer_channel( struct Stereo_Buffer* this );
|
||||
void Buffer_end_frame( struct Stereo_Buffer* this, blip_time_t );
|
||||
|
||||
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 )
|
||||
static inline delta_t unsettled( struct Blip_Buffer* this )
|
||||
{
|
||||
return this->samples_per_frame_;
|
||||
return this->reader_accum_ >> delta_bits;
|
||||
}
|
||||
|
||||
// See Blip_Buffer.h
|
||||
static inline long Buffer_sample_rate( struct Stereo_Buffer* this )
|
||||
{
|
||||
return this->sample_rate_;
|
||||
}
|
||||
// Stereo Mixer
|
||||
struct Stereo_Mixer {
|
||||
struct Tracked_Blip_Buffer* bufs [3];
|
||||
int samples_read;
|
||||
};
|
||||
|
||||
// Length of buffer, in milliseconds
|
||||
static inline int Buffer_length( struct Stereo_Buffer* this )
|
||||
void Mixer_init( struct Stereo_Mixer* this );
|
||||
void Mixer_read_pairs( struct Stereo_Mixer* this, blip_sample_t out [], int count );
|
||||
|
||||
typedef struct Tracked_Blip_Buffer buf_t;
|
||||
|
||||
// Multi_Buffer
|
||||
struct Multi_Buffer {
|
||||
unsigned channels_changed_count_;
|
||||
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_;
|
||||
}
|
||||
|
||||
// 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
|
||||
|
|
|
@ -39,19 +39,19 @@ void Apu_init( struct Nes_Apu* this )
|
|||
this->oscs [4] = &this->dmc.osc;
|
||||
|
||||
Apu_output( this, NULL );
|
||||
this->dmc.nonlinear = false;
|
||||
Apu_volume( this, (int)FP_ONE_VOLUME );
|
||||
Apu_reset( this, false, 0 );
|
||||
}
|
||||
|
||||
void Apu_enable_nonlinear( struct Nes_Apu* this, int v )
|
||||
void Apu_enable_nonlinear_( struct Nes_Apu* this, double sq, double tnd )
|
||||
{
|
||||
this->dmc.nonlinear = true;
|
||||
Synth_volume( &this->square_synth, (int)((long long)(1.3 * 0.25751258 / 0.742467605 * 0.25 * FP_ONE_VOLUME) / amp_range * v) );
|
||||
Synth_volume( &this->square_synth, (int)((long long)(sq * FP_ONE_VOLUME) / amp_range) );
|
||||
|
||||
const int tnd = (int)(0.48 / 202 * 0.75 * FP_ONE_VOLUME);
|
||||
Synth_volume( &this->triangle.synth, 3 * tnd );
|
||||
Synth_volume( &this->noise.synth, 2 * tnd );
|
||||
Synth_volume( &this->dmc.synth, tnd );
|
||||
Synth_volume( &this->triangle.synth, tnd * 2.752 );
|
||||
Synth_volume( &this->noise.synth , tnd * 1.849 );
|
||||
Synth_volume( &this->dmc.synth , tnd );
|
||||
|
||||
this->square1 .osc.last_amp = 0;
|
||||
this->square2 .osc.last_amp = 0;
|
||||
|
@ -62,11 +62,13 @@ void Apu_enable_nonlinear( struct Nes_Apu* this, int v )
|
|||
|
||||
void Apu_volume( struct Nes_Apu* this, int v )
|
||||
{
|
||||
this->dmc.nonlinear = false;
|
||||
Synth_volume( &this->square_synth, (int)((long long)(0.1128 *FP_ONE_VOLUME) * v / amp_range / FP_ONE_VOLUME) );
|
||||
Synth_volume( &this->triangle.synth,(int)((long long)(0.12765*FP_ONE_VOLUME) * v / amp_range / FP_ONE_VOLUME) );
|
||||
Synth_volume( &this->noise.synth, (int)((long long)(0.0741 *FP_ONE_VOLUME) * v / amp_range / FP_ONE_VOLUME) );
|
||||
Synth_volume( &this->dmc.synth, (int)((long long)(0.42545*FP_ONE_VOLUME) * v / 127 / FP_ONE_VOLUME) );
|
||||
if ( !this->dmc.nonlinear )
|
||||
{
|
||||
Synth_volume( &this->square_synth, (int)((long long)((0.125 * (1.0 /1.11)) * FP_ONE_VOLUME) * v / amp_range / FP_ONE_VOLUME) ); // was 0.1128 1.108
|
||||
Synth_volume( &this->triangle.synth,(int)((long long)((0.150 * (1.0 /1.11)) * FP_ONE_VOLUME) * v / amp_range / FP_ONE_VOLUME) ); // was 0.12765 1.175
|
||||
Synth_volume( &this->noise.synth, (int)((long long)((0.095 * (1.0 /1.11)) * FP_ONE_VOLUME) * v / amp_range / FP_ONE_VOLUME) ); // was 0.0741 1.282
|
||||
Synth_volume( &this->dmc.synth, (int)((long long)((0.450 * (1.0 /1.11)) * FP_ONE_VOLUME) * v / 2048 / FP_ONE_VOLUME) ); // was 0.42545 1.058
|
||||
}
|
||||
}
|
||||
|
||||
void Apu_output( struct Nes_Apu* this, struct Blip_Buffer* buffer )
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// NES 2A03 APU sound chip emulator
|
||||
|
||||
// Nes_Snd_Emu 0.1.8
|
||||
// Nes_Snd_Emu 0.2.0-pre
|
||||
#ifndef NES_APU_H
|
||||
#define NES_APU_H
|
||||
|
||||
|
@ -9,7 +9,7 @@
|
|||
|
||||
enum { apu_status_addr = 0x4015 };
|
||||
enum { apu_osc_count = 5 };
|
||||
enum { apu_no_irq = INT_MAX / 2 + 1 };
|
||||
enum { apu_no_irq = INT_MAX/2 + 1 };
|
||||
enum { apu_irq_waiting = 0 };
|
||||
|
||||
enum { apu_io_addr = 0x4000 };
|
||||
|
@ -18,23 +18,15 @@ enum { apu_io_size = 0x18 };
|
|||
struct apu_state_t;
|
||||
|
||||
struct Nes_Apu {
|
||||
nes_time_t last_dmc_time;
|
||||
int osc_enables;
|
||||
|
||||
struct Nes_Osc* oscs [apu_osc_count];
|
||||
struct Nes_Square square1;
|
||||
struct Nes_Square square2;
|
||||
struct Nes_Noise noise;
|
||||
struct Nes_Triangle triangle;
|
||||
struct Nes_Dmc dmc;
|
||||
|
||||
int tempo_;
|
||||
nes_time_t last_time; // has been run until this time in current frame
|
||||
nes_time_t last_dmc_time;
|
||||
nes_time_t earliest_irq_;
|
||||
nes_time_t next_irq;
|
||||
int frame_period;
|
||||
int frame_delay; // cycles until frame counter runs next
|
||||
int frame; // current frame (0-3)
|
||||
int osc_enables;
|
||||
int frame_mode;
|
||||
bool irq_flag;
|
||||
|
||||
|
@ -42,6 +34,13 @@ struct Nes_Apu {
|
|||
void* irq_data;
|
||||
|
||||
Synth square_synth; // shared by squares
|
||||
|
||||
struct Nes_Osc* oscs [apu_osc_count];
|
||||
struct Nes_Square square1;
|
||||
struct Nes_Square square2;
|
||||
struct Nes_Noise noise;
|
||||
struct Nes_Triangle triangle;
|
||||
struct Nes_Dmc dmc;
|
||||
};
|
||||
|
||||
// Init Nes apu
|
||||
|
@ -124,11 +123,13 @@ static inline nes_time_t Dmc_next_read_time( struct Nes_Dmc* this )
|
|||
if ( this->osc.length_counter == 0 )
|
||||
return apu_no_irq; // not reading
|
||||
|
||||
return this->apu->last_dmc_time + this->osc.delay + (long) (this->bits_remain - 1) * this->period;
|
||||
return this->apu->last_dmc_time + this->osc.delay + (this->bits_remain - 1) * this->period;
|
||||
}
|
||||
|
||||
// Time when next DMC memory read will occur
|
||||
static inline nes_time_t Apu_next_dmc_read_time( struct Nes_Apu* this ) { return Dmc_next_read_time( &this->dmc ); }
|
||||
void Apu_irq_changed( struct Nes_Apu* this );
|
||||
|
||||
// Experimental
|
||||
void Apu_enable_nonlinear_( struct Nes_Apu* this, double sq, double tnd );
|
||||
#endif
|
||||
|
|
|
@ -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;
|
||||
if ( !osc_output )
|
||||
continue;
|
||||
/* osc_output->set_modified(); */
|
||||
Blip_set_modified( osc_output );
|
||||
|
||||
// check for unsupported mode
|
||||
#ifndef NDEBUG
|
||||
|
@ -92,11 +90,13 @@ void Fme7_run_until( struct Nes_Fme7_Apu* this, blip_time_t end_time )
|
|||
int amp = volume;
|
||||
if ( !this->phases [index] )
|
||||
amp = 0;
|
||||
|
||||
{
|
||||
int delta = amp - this->oscs [index].last_amp;
|
||||
if ( delta )
|
||||
{
|
||||
this->oscs [index].last_amp = amp;
|
||||
Blip_set_modified( osc_output );
|
||||
Synth_offset( &this->synth, this->last_time, delta, osc_output );
|
||||
}
|
||||
}
|
||||
|
@ -105,6 +105,7 @@ void Fme7_run_until( struct Nes_Fme7_Apu* this, blip_time_t end_time )
|
|||
if ( time < end_time )
|
||||
{
|
||||
int delta = amp * 2 - volume;
|
||||
Blip_set_modified( osc_output );
|
||||
if ( volume )
|
||||
{
|
||||
do
|
||||
|
@ -123,7 +124,7 @@ void Fme7_run_until( struct Nes_Fme7_Apu* this, blip_time_t end_time )
|
|||
// maintain phase when silent
|
||||
int count = (end_time - time + period - 1) / period;
|
||||
this->phases [index] ^= count & 1;
|
||||
time += (blargg_long) count * period;
|
||||
time += count * period;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// Sunsoft FME-7 sound emulator
|
||||
|
||||
// Game_Music_Emu 0.5.5
|
||||
// Game_Music_Emu 0.6-pre
|
||||
#ifndef NES_FME7_APU_H
|
||||
#define NES_FME7_APU_H
|
||||
|
||||
|
|
|
@ -68,8 +68,6 @@ void Namco_run_until( struct Nes_Namco_Apu* this, blip_time_t nes_end_time )
|
|||
struct Blip_Buffer* output = osc->output;
|
||||
if ( !output )
|
||||
continue;
|
||||
/* output->set_modified(); */
|
||||
Blip_set_modified( output );
|
||||
|
||||
blip_resampled_time_t time =
|
||||
Blip_resampled_time( output, this->last_time ) + osc->delay;
|
||||
|
@ -85,12 +83,17 @@ void Namco_run_until( struct Nes_Namco_Apu* this, blip_time_t nes_end_time )
|
|||
if ( !volume )
|
||||
continue;
|
||||
|
||||
blargg_long freq = (osc_reg [4] & 3) * 0x10000 + osc_reg [2] * 0x100L + osc_reg [0];
|
||||
int freq = (osc_reg [4] & 3) * 0x10000 + osc_reg [2] * 0x100L + osc_reg [0];
|
||||
if ( freq < 64 * active_oscs )
|
||||
continue; // prevent low frequencies from excessively delaying freq changes
|
||||
|
||||
int const master_clock_divider = 12; // NES time derived via divider of master clock
|
||||
int const n106_divider = 45; // N106 then divides master clock by this
|
||||
int const max_freq = 0x3FFFF;
|
||||
int const lowest_freq_period = (max_freq + 1) * n106_divider / master_clock_divider;
|
||||
// divide by 8 to avoid overflow
|
||||
blip_resampled_time_t period =
|
||||
/* output->resampled_duration( 983040 ) / freq * active_oscs; */
|
||||
Blip_resampled_duration( output, 983040 ) / freq * active_oscs;
|
||||
Blip_resampled_duration( output, lowest_freq_period / 8 ) / freq * 8 * active_oscs;
|
||||
|
||||
int wave_size = 32 - (osc_reg [4] >> 2 & 7) * 4;
|
||||
if ( !wave_size )
|
||||
|
@ -99,6 +102,8 @@ void Namco_run_until( struct Nes_Namco_Apu* this, blip_time_t nes_end_time )
|
|||
int last_amp = osc->last_amp;
|
||||
int wave_pos = osc->wave_pos;
|
||||
|
||||
Blip_set_modified( output );
|
||||
|
||||
do
|
||||
{
|
||||
// read wave sample
|
||||
|
|
|
@ -15,7 +15,7 @@ enum { namco_data_reg_addr = 0x4800 };
|
|||
enum { namco_reg_count = 0x80 };
|
||||
|
||||
struct Namco_Osc {
|
||||
blargg_long delay;
|
||||
int delay;
|
||||
struct Blip_Buffer* output;
|
||||
short last_amp;
|
||||
short wave_pos;
|
||||
|
|
|
@ -90,7 +90,7 @@ static inline nes_time_t Square_maintain_phase( struct Nes_Square* this, nes_tim
|
|||
{
|
||||
int count = (remain + timer_period - 1) / timer_period;
|
||||
this->phase = (this->phase + count) & (square_phase_range - 1);
|
||||
time += (blargg_long) count * timer_period;
|
||||
time += count * timer_period;
|
||||
}
|
||||
return time;
|
||||
}
|
||||
|
@ -107,8 +107,6 @@ void Square_run( struct Nes_Square* this, nes_time_t time, nes_time_t end_time )
|
|||
return;
|
||||
}
|
||||
|
||||
Blip_set_modified( osc->output );
|
||||
|
||||
int offset = period >> (osc->regs [1] & shift_mask);
|
||||
if ( osc->regs [1] & negate_flag )
|
||||
offset = 0;
|
||||
|
@ -117,6 +115,7 @@ void Square_run( struct Nes_Square* this, nes_time_t time, nes_time_t end_time )
|
|||
if ( volume == 0 || period < 8 || (period + offset) >= 0x800 )
|
||||
{
|
||||
if ( osc->last_amp ) {
|
||||
Blip_set_modified( osc->output );
|
||||
Synth_offset( this->synth, time, -osc->last_amp, osc->output );
|
||||
osc->last_amp = 0;
|
||||
}
|
||||
|
@ -137,6 +136,7 @@ void Square_run( struct Nes_Square* this, nes_time_t time, nes_time_t end_time )
|
|||
if ( this->phase < duty )
|
||||
amp ^= volume;
|
||||
|
||||
Blip_set_modified( osc->output );
|
||||
{
|
||||
int delta = Osc_update_amp( osc, amp );
|
||||
if ( delta )
|
||||
|
@ -201,7 +201,7 @@ static inline nes_time_t Triangle_maintain_phase( struct Nes_Triangle* this, nes
|
|||
int count = (remain + timer_period - 1) / timer_period;
|
||||
this->phase = ((unsigned) this->phase + 1 - count) & (Triangle_phase_range * 2 - 1);
|
||||
this->phase++;
|
||||
time += (blargg_long) count * timer_period;
|
||||
time += count * timer_period;
|
||||
}
|
||||
return time;
|
||||
}
|
||||
|
@ -219,14 +219,15 @@ void Triangle_run( struct Nes_Triangle* this, nes_time_t time, nes_time_t end_ti
|
|||
return;
|
||||
}
|
||||
|
||||
Blip_set_modified( osc->output );
|
||||
|
||||
// to do: track phase when period < 3
|
||||
// to do: Output 7.5 on dac when period < 2? More accurate, but results in more clicks.
|
||||
|
||||
int delta = Osc_update_amp( osc, Triangle_calc_amp( this ) );
|
||||
if ( delta )
|
||||
{
|
||||
Blip_set_modified( osc->output );
|
||||
Synth_offset( &this->synth, time, delta, osc->output );
|
||||
}
|
||||
|
||||
time += osc->delay;
|
||||
if ( osc->length_counter == 0 || this->linear_counter == 0 || timer_period < 3 )
|
||||
|
@ -243,13 +244,15 @@ void Triangle_run( struct Nes_Triangle* this, nes_time_t time, nes_time_t end_ti
|
|||
phase -= Triangle_phase_range;
|
||||
volume = -volume;
|
||||
}
|
||||
Blip_set_modified( osc->output );
|
||||
|
||||
do {
|
||||
if ( --phase == 0 ) {
|
||||
phase = Triangle_phase_range;
|
||||
volume = -volume;
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
Synth_offset_inline( &this->synth, time, volume, output );
|
||||
}
|
||||
|
||||
|
@ -340,18 +343,27 @@ static inline void Dmc_reload_sample( struct Nes_Dmc* this )
|
|||
this->osc.length_counter = this->osc.regs [3] * 0x10 + 1;
|
||||
}
|
||||
|
||||
static byte const dac_table [128] =
|
||||
static int const dmc_table [128] =
|
||||
{
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 7, 8, 9,10,11,12,13,14,
|
||||
15,15,16,17,18,19,20,20,21,22,23,24,24,25,26,27,
|
||||
27,28,29,30,31,31,32,33,33,34,35,36,36,37,38,38,
|
||||
39,40,41,41,42,43,43,44,45,45,46,47,47,48,48,49,
|
||||
50,50,51,52,52,53,53,54,55,55,56,56,57,58,58,59,
|
||||
59,60,60,61,61,62,63,63,64,64,65,65,66,66,67,67,
|
||||
68,68,69,70,70,71,71,72,72,73,73,74,74,75,75,75,
|
||||
76,76,77,77,78,78,79,79,80,80,81,81,82,82,82,83,
|
||||
0, 24, 48, 71, 94, 118, 141, 163, 186, 209, 231, 253, 275, 297, 319, 340,
|
||||
361, 383, 404, 425, 445, 466, 486, 507, 527, 547, 567, 587, 606, 626, 645, 664,
|
||||
683, 702, 721, 740, 758, 777, 795, 813, 832, 850, 867, 885, 903, 920, 938, 955,
|
||||
972, 989,1006,1023,1040,1056,1073,1089,1105,1122,1138,1154,1170,1185,1201,1217,
|
||||
1232,1248,1263,1278,1293,1308,1323,1338,1353,1368,1382,1397,1411,1425,1440,1454,
|
||||
1468,1482,1496,1510,1523,1537,1551,1564,1578,1591,1604,1618,1631,1644,1657,1670,
|
||||
1683,1695,1708,1721,1733,1746,1758,1771,1783,1795,1807,1819,1831,1843,1855,1867,
|
||||
1879,1890,1902,1914,1925,1937,1948,1959,1971,1982,1993,2004,2015,2026,2037,2048,
|
||||
};
|
||||
|
||||
static inline int update_amp_nonlinear( struct Nes_Dmc* this, int in )
|
||||
{
|
||||
if ( !this->nonlinear )
|
||||
in = dmc_table [in];
|
||||
int delta = in - this->osc.last_amp;
|
||||
this->osc.last_amp = in;
|
||||
return delta;
|
||||
}
|
||||
|
||||
void Dmc_write_register( struct Nes_Dmc* this, int addr, int data )
|
||||
{
|
||||
if ( addr == 0 )
|
||||
|
@ -363,14 +375,7 @@ void Dmc_write_register( struct Nes_Dmc* this, int addr, int data )
|
|||
}
|
||||
else if ( addr == 1 )
|
||||
{
|
||||
int old_dac = this->dac;
|
||||
this->dac = data & 0x7F;
|
||||
|
||||
// adjust last_amp so that "pop" amplitude will be properly non-linear
|
||||
// with respect to change in dac
|
||||
int faked_nonlinear = this->dac - (dac_table [this->dac] - dac_table [old_dac]);
|
||||
if ( !this->nonlinear )
|
||||
this->osc.last_amp = faked_nonlinear;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -407,16 +412,15 @@ void Dmc_fill_buffer( struct Nes_Dmc* this )
|
|||
void Dmc_run( struct Nes_Dmc* this, nes_time_t time, nes_time_t end_time )
|
||||
{
|
||||
struct Nes_Osc* osc = &this->osc;
|
||||
int delta = Osc_update_amp( osc, this->dac );
|
||||
int delta = update_amp_nonlinear( this, this->dac );
|
||||
if ( !osc->output )
|
||||
{
|
||||
this->silence = true;
|
||||
}
|
||||
else
|
||||
else if ( delta )
|
||||
{
|
||||
Blip_set_modified( osc->output );
|
||||
if ( delta )
|
||||
Synth_offset( &this->synth, time, delta, osc->output );
|
||||
Synth_offset( &this->synth, time, delta, osc->output );
|
||||
}
|
||||
|
||||
time += osc->delay;
|
||||
|
@ -435,6 +439,8 @@ void Dmc_run( struct Nes_Dmc* this, nes_time_t time, nes_time_t end_time )
|
|||
const int period = this->period;
|
||||
int bits = this->bits;
|
||||
int dac = this->dac;
|
||||
if ( output )
|
||||
Blip_set_modified( output );
|
||||
|
||||
do
|
||||
{
|
||||
|
@ -444,7 +450,7 @@ void Dmc_run( struct Nes_Dmc* this, nes_time_t time, nes_time_t end_time )
|
|||
bits >>= 1;
|
||||
if ( (unsigned) (dac + step) <= 0x7F ) {
|
||||
dac += step;
|
||||
Synth_offset_inline( &this->synth, time, step, output );
|
||||
Synth_offset_inline( &this->synth, time, update_amp_nonlinear( this, dac ), output );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -456,7 +462,8 @@ void Dmc_run( struct Nes_Dmc* this, nes_time_t time, nes_time_t end_time )
|
|||
if ( !this->buf_full ) {
|
||||
this->silence = true;
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
this->silence = false;
|
||||
bits = this->buf;
|
||||
this->buf_full = false;
|
||||
|
@ -469,7 +476,7 @@ void Dmc_run( struct Nes_Dmc* this, nes_time_t time, nes_time_t end_time )
|
|||
while ( time < end_time );
|
||||
|
||||
this->dac = dac;
|
||||
osc->last_amp = dac;
|
||||
//osc->last_amp = dac;
|
||||
this->bits = bits;
|
||||
}
|
||||
this->bits_remain = bits_remain;
|
||||
|
@ -519,14 +526,15 @@ void Noise_run( struct Nes_Noise* this, nes_time_t time, nes_time_t end_time )
|
|||
return;
|
||||
}
|
||||
|
||||
Blip_set_modified( osc->output );
|
||||
|
||||
const int volume = Noise_volume( this );
|
||||
int amp = (this->noise & 1) ? volume : 0;
|
||||
{
|
||||
int delta = Osc_update_amp( osc, amp );
|
||||
if ( delta )
|
||||
{
|
||||
Blip_set_modified( osc->output );
|
||||
Synth_offset( &this->synth, time, delta, osc->output );
|
||||
}
|
||||
}
|
||||
|
||||
time += osc->delay;
|
||||
|
@ -557,6 +565,7 @@ void Noise_run( struct Nes_Noise* this, nes_time_t time, nes_time_t end_time )
|
|||
int noise = this->noise;
|
||||
int delta = amp * 2 - volume;
|
||||
const int tap = (osc->regs [2] & mode_flag ? 8 : 13);
|
||||
Blip_set_modified( osc->output );
|
||||
|
||||
do {
|
||||
int feedback = (noise << tap) ^ (noise << 14);
|
||||
|
|
|
@ -144,7 +144,7 @@ struct Nes_Dmc
|
|||
bool pal_mode;
|
||||
bool nonlinear;
|
||||
|
||||
int (*prg_reader)( void*, addr_t ); // needs to be initialized to prg read function
|
||||
int (*prg_reader)( void*, int ); // needs to be initialized to prg read function
|
||||
void* prg_reader_data;
|
||||
|
||||
struct Nes_Apu* apu;
|
||||
|
|
|
@ -83,7 +83,6 @@ void run_square( struct Nes_Vrc6_Apu* this, struct Vrc6_Osc* osc, blip_time_t en
|
|||
struct Blip_Buffer* output = osc->output;
|
||||
if ( !output )
|
||||
return;
|
||||
Blip_set_modified( output );
|
||||
|
||||
int volume = osc->regs [0] & 15;
|
||||
if ( !(osc->regs [2] & 0x80) )
|
||||
|
@ -96,6 +95,7 @@ void run_square( struct Nes_Vrc6_Apu* this, struct Vrc6_Osc* osc, blip_time_t en
|
|||
if ( delta )
|
||||
{
|
||||
osc->last_amp += delta;
|
||||
Blip_set_modified( output );
|
||||
Synth_offset( &this->square_synth, time, delta, output );
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// Konami VRC6 sound chip emulator
|
||||
|
||||
// Nes_Snd_Emu 0.1.8
|
||||
// Nes_Snd_Emu 0.2.0-pre
|
||||
#ifndef NES_VRC6_APU_H
|
||||
#define NES_VRC6_APU_H
|
||||
|
||||
|
@ -14,7 +14,7 @@ enum { vrc6_addr_step = 0x1000 };
|
|||
|
||||
struct Vrc6_Osc
|
||||
{
|
||||
uint8_t regs [vrc6_reg_count];
|
||||
uint8_t regs [3];
|
||||
struct Blip_Buffer* output;
|
||||
int delay;
|
||||
int last_amp;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Game_Music_Emu 0.5.5. http://www.slack.net/~ant/
|
||||
// Game_Music_Emu 0.6-pre. http://www.slack.net/~ant/
|
||||
|
||||
#include "nsf_emu.h"
|
||||
#include "multi_buffer.h"
|
||||
|
@ -19,33 +19,19 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
|
|||
#include "blargg_source.h"
|
||||
|
||||
const char gme_wrong_file_type [] = "Wrong file type for this emulator";
|
||||
long const clock_divisor = 12;
|
||||
|
||||
int const stereo = 2; // number of channels for stereo
|
||||
int const silence_max = 6; // seconds
|
||||
int const silence_threshold = 0x10;
|
||||
long const fade_block_size = 512;
|
||||
int const fade_shift = 8; // fade ends with gain at 1.0 / (1 << fade_shift)
|
||||
|
||||
// number of frames until play interrupts init
|
||||
int const initial_play_delay = 7; // KikiKaikai needed this to work
|
||||
int const bank_size = 0x1000;
|
||||
int const rom_addr = 0x8000;
|
||||
|
||||
static void clear_track_vars( struct Nsf_Emu* this )
|
||||
{
|
||||
this->current_track = -1;
|
||||
this->out_time = 0;
|
||||
this->emu_time = 0;
|
||||
this->emu_track_ended_ = true;
|
||||
this->track_ended = true;
|
||||
this->fade_start = INT_MAX / 2 + 1;
|
||||
this->fade_step = 1;
|
||||
this->silence_time = 0;
|
||||
this->silence_count = 0;
|
||||
this->buf_remain = 0;
|
||||
track_stop( &this->track_filter );
|
||||
}
|
||||
|
||||
static int pcm_read( void* emu, addr_t addr )
|
||||
static int pcm_read( void* emu, int addr )
|
||||
{
|
||||
return *Cpu_get_code( &((struct Nsf_Emu*) emu)->cpu, addr );
|
||||
}
|
||||
|
@ -58,18 +44,16 @@ void Nsf_init( struct Nsf_Emu* this )
|
|||
this->gain = (int)(FP_ONE_GAIN);
|
||||
|
||||
// defaults
|
||||
this->max_initial_silence = 2;
|
||||
this->ignore_silence = false;
|
||||
this->voice_count = 0;
|
||||
this->tfilter = *track_get_setup( &this->track_filter );
|
||||
this->tfilter.max_initial = 2;
|
||||
this->tfilter.lookahead = 6;
|
||||
this->track_filter.silence_ignored_ = false;
|
||||
|
||||
// Set sound gain
|
||||
Sound_set_gain( this, (int)(FP_ONE_GAIN*1.2) );
|
||||
|
||||
// Unload
|
||||
clear_track_vars( this );
|
||||
|
||||
// Init rom
|
||||
Rom_init( &this->rom, 0x1000 );
|
||||
Rom_init( &this->rom, bank_size );
|
||||
|
||||
// Init & clear nsfe info
|
||||
Info_init( &this->info );
|
||||
|
@ -78,16 +62,36 @@ void Nsf_init( struct Nsf_Emu* this )
|
|||
Cpu_init( &this->cpu );
|
||||
Apu_init( &this->apu );
|
||||
Apu_dmc_reader( &this->apu, pcm_read, this );
|
||||
|
||||
// Unload
|
||||
this->voice_count = 0;
|
||||
memset( this->voice_types, 0, sizeof this->voice_types );
|
||||
clear_track_vars( this );
|
||||
}
|
||||
|
||||
// Setup
|
||||
|
||||
static void append_voices( struct Nsf_Emu* this, int const types [], int count )
|
||||
{
|
||||
assert( this->voice_count + count < max_voices );
|
||||
int i;
|
||||
for ( i = 0; i < count; i++ ) {
|
||||
this->voice_types [this->voice_count + i] = types [i];
|
||||
}
|
||||
this->voice_count += count;
|
||||
}
|
||||
|
||||
static blargg_err_t init_sound( struct Nsf_Emu* this )
|
||||
{
|
||||
/* if ( header_.chip_flags & ~(fds_flag | namco_flag | vrc6_flag | fme7_flag) )
|
||||
/* if ( header_.chip_flags & ~(fds_flag | namco_flag | vrc6_flag | fme7_flag) )
|
||||
warning( "Uses unsupported audio expansion hardware" ); **/
|
||||
|
||||
this->voice_count = apu_osc_count;
|
||||
{
|
||||
static int const types [apu_osc_count] = {
|
||||
wave_type+1, wave_type+2, mixed_type+1, noise_type+0, mixed_type+1
|
||||
};
|
||||
append_voices( this, types, apu_osc_count );
|
||||
}
|
||||
|
||||
int adjusted_gain = (this->gain * 4) / 3;
|
||||
|
||||
|
@ -103,7 +107,10 @@ static blargg_err_t init_sound( struct Nsf_Emu* this )
|
|||
Vrc6_init( &this->vrc6 );
|
||||
adjusted_gain = (adjusted_gain*3) / 4;
|
||||
|
||||
this->voice_count += vrc6_osc_count;
|
||||
static int const types [vrc6_osc_count] = {
|
||||
wave_type+3, wave_type+4, wave_type+5,
|
||||
};
|
||||
append_voices( this, types, vrc6_osc_count );
|
||||
}
|
||||
|
||||
if ( fme7_enabled( this ) )
|
||||
|
@ -111,7 +118,10 @@ static blargg_err_t init_sound( struct Nsf_Emu* this )
|
|||
Fme7_init( &this->fme7 );
|
||||
adjusted_gain = (adjusted_gain*3) / 4;
|
||||
|
||||
this->voice_count += fme7_osc_count;
|
||||
static int const types [fme7_osc_count] = {
|
||||
wave_type+3, wave_type+4, wave_type+5,
|
||||
};
|
||||
append_voices( this, types, fme7_osc_count );
|
||||
}
|
||||
|
||||
if ( mmc5_enabled( this ) )
|
||||
|
@ -119,7 +129,11 @@ static blargg_err_t init_sound( struct Nsf_Emu* this )
|
|||
Mmc5_init( &this->mmc5 );
|
||||
adjusted_gain = (adjusted_gain*3) / 4;
|
||||
|
||||
this->voice_count += mmc5_osc_count;
|
||||
|
||||
static int const types [mmc5_osc_count] = {
|
||||
wave_type+3, wave_type+4, mixed_type+2
|
||||
};
|
||||
append_voices( this, types, mmc5_osc_count );
|
||||
}
|
||||
|
||||
if ( fds_enabled( this ) )
|
||||
|
@ -127,7 +141,10 @@ static blargg_err_t init_sound( struct Nsf_Emu* this )
|
|||
Fds_init( &this->fds );
|
||||
adjusted_gain = (adjusted_gain*3) / 4;
|
||||
|
||||
this->voice_count += fds_osc_count ;
|
||||
static int const types [fds_osc_count] = {
|
||||
wave_type+0
|
||||
};
|
||||
append_voices( this, types, fds_osc_count );
|
||||
}
|
||||
|
||||
if ( namco_enabled( this ) )
|
||||
|
@ -135,22 +152,31 @@ static blargg_err_t init_sound( struct Nsf_Emu* this )
|
|||
Namco_init( &this->namco );
|
||||
adjusted_gain = (adjusted_gain*3) / 4;
|
||||
|
||||
this->voice_count += namco_osc_count;
|
||||
static int const types [namco_osc_count] = {
|
||||
wave_type+3, wave_type+4, wave_type+5, wave_type+ 6,
|
||||
wave_type+7, wave_type+8, wave_type+9, wave_type+10,
|
||||
};
|
||||
append_voices( this, types, namco_osc_count );
|
||||
}
|
||||
|
||||
if ( vrc7_enabled( this ) )
|
||||
{
|
||||
#ifndef NSF_EMU_NO_VRC7
|
||||
if ( vrc7_enabled( this ) )
|
||||
{
|
||||
|
||||
Vrc7_init( &this->vrc7 );
|
||||
Vrc7_set_rate( &this->vrc7, this->sample_rate );
|
||||
#endif
|
||||
|
||||
adjusted_gain = (adjusted_gain*3) / 4;
|
||||
adjusted_gain = (adjusted_gain*3) / 4;
|
||||
|
||||
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 ( vrc6_enabled( this ) ) Vrc6_volume( &this->vrc6, adjusted_gain );
|
||||
if ( fme7_enabled( this ) ) Fme7_volume( &this->fme7, adjusted_gain );
|
||||
|
@ -179,9 +205,9 @@ static bool pal_only( struct header_t* this )
|
|||
return (this->speed_flags & 3) == 1;
|
||||
}
|
||||
|
||||
static long clock_rate( struct header_t* this )
|
||||
static int clock_rate( struct header_t* this )
|
||||
{
|
||||
return pal_only( this ) ? (long)1662607.125 : (long)1789772.727272727;
|
||||
return pal_only( this ) ? (int)1662607.125 : (int)1789772.727272727;
|
||||
}
|
||||
|
||||
static int play_period( struct header_t* this )
|
||||
|
@ -227,7 +253,7 @@ static blargg_err_t check_nsf_header( struct header_t* h )
|
|||
return 0;
|
||||
}
|
||||
|
||||
blargg_err_t Nsf_load( struct Nsf_Emu* this, void* data, long size )
|
||||
blargg_err_t Nsf_load_mem( struct Nsf_Emu* this, void* data, long size )
|
||||
{
|
||||
// Unload
|
||||
Info_unload( &this->info ); // TODO: extremely hacky!
|
||||
|
@ -258,8 +284,7 @@ blargg_err_t Nsf_post_load( struct Nsf_Emu* this )
|
|||
warning( "Unknown file version" ); */
|
||||
|
||||
// set up data
|
||||
addr_t load_addr = get_le16( this->header.load_addr );
|
||||
|
||||
addr_t load_addr = get_addr( this->header.load_addr );
|
||||
/* if ( load_addr < (fds_enabled() ? sram_addr : rom_addr) )
|
||||
warning( "Load address is too low" ); */
|
||||
|
||||
|
@ -277,17 +302,16 @@ blargg_err_t Nsf_post_load( struct Nsf_Emu* this )
|
|||
|
||||
// Post load
|
||||
Sound_set_tempo( this, this->tempo );
|
||||
|
||||
// Remute voices
|
||||
Sound_mute_voices( this, this->mute_mask_ );
|
||||
|
||||
// Set track_count
|
||||
this->track_count = this->header.track_count;
|
||||
|
||||
// Change clock rate & setup buffer
|
||||
this->clock_rate__ = (long) clock_rate( &this->header );
|
||||
this->clock_rate__ = clock_rate( &this->header );
|
||||
Buffer_clock_rate( &this->stereo_buf, this->clock_rate__ );
|
||||
this->buf_changed_count = Buffer_channels_changed_count( &this->stereo_buf );
|
||||
RETURN_ERR( Buffer_set_channel_count( &this->stereo_buf, this->voice_count, this->voice_types ) );
|
||||
this->buf_changed_count = Buffer_channels_changed_count( &this->stereo_buf );
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -416,11 +440,13 @@ static void set_voice( struct Nsf_Emu* this, int i, struct Blip_Buffer* buf, str
|
|||
return;
|
||||
}
|
||||
|
||||
if ( vrc7_enabled( this ) && (i -= vrc7_osc_count) < 0 )
|
||||
{
|
||||
Vrc7_set_output( &this->vrc7, i + vrc7_osc_count, buf );
|
||||
return;
|
||||
}
|
||||
#ifndef NSF_EMU_NO_VRC7
|
||||
if ( vrc7_enabled( this ) && (i -= vrc7_osc_count) < 0 )
|
||||
{
|
||||
Vrc7_set_output( &this->vrc7, i + vrc7_osc_count, buf );
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
@ -429,7 +455,7 @@ static void set_voice( struct Nsf_Emu* this, int i, struct Blip_Buffer* buf, str
|
|||
|
||||
// Music Emu
|
||||
|
||||
blargg_err_t Nsf_set_sample_rate( struct Nsf_Emu* this, long rate )
|
||||
blargg_err_t Nsf_set_sample_rate( struct Nsf_Emu* this, int rate )
|
||||
{
|
||||
require( !this->sample_rate ); // sample rate can't be changed once set
|
||||
Buffer_init( &this->stereo_buf );
|
||||
|
@ -439,6 +465,8 @@ blargg_err_t Nsf_set_sample_rate( struct Nsf_Emu* this, long rate )
|
|||
Buffer_bass_freq( &this->stereo_buf, 80 );
|
||||
|
||||
this->sample_rate = rate;
|
||||
RETURN_ERR( track_init( &this->track_filter, this ) );
|
||||
this->tfilter.max_silence = 6 * stereo * this->sample_rate;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -466,7 +494,7 @@ void Sound_mute_voices( struct Nsf_Emu* this, int mask )
|
|||
}
|
||||
else
|
||||
{
|
||||
struct channel_t ch = Buffer_channel( &this->stereo_buf );
|
||||
struct channel_t ch = Buffer_channel( &this->stereo_buf, i );
|
||||
assert( (ch.center && ch.left && ch.right) ||
|
||||
(!ch.center && !ch.left && !ch.right) ); // all or nothing
|
||||
set_voice( this, i, ch.center, ch.left, ch.right );
|
||||
|
@ -533,32 +561,13 @@ int cpu_read( struct Nsf_Emu* this, addr_t addr )
|
|||
return addr >> 8;
|
||||
}
|
||||
|
||||
#if 0 /* function currently unused */
|
||||
static int unmapped_read( struct Nsf_Emu* this, addr_t addr )
|
||||
{
|
||||
(void) this;
|
||||
|
||||
switch ( addr )
|
||||
{
|
||||
case 0x2002:
|
||||
case 0x4016:
|
||||
case 0x4017:
|
||||
return addr >> 8;
|
||||
}
|
||||
|
||||
// Unmapped read
|
||||
return addr >> 8;
|
||||
}
|
||||
#endif
|
||||
|
||||
void cpu_write( struct Nsf_Emu* this, addr_t addr, int data )
|
||||
{
|
||||
#ifndef NSF_EMU_APU_ONLY
|
||||
{
|
||||
nes_time_t time = Cpu_time( &this->cpu );
|
||||
if ( fds_enabled( this) && (unsigned) (addr - fds_io_addr) < fds_io_size )
|
||||
{
|
||||
Fds_write( &this->fds, time, addr, data );
|
||||
Fds_write( &this->fds, Cpu_time( &this->cpu ), addr, data );
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -572,7 +581,7 @@ void cpu_write( struct Nsf_Emu* this, addr_t addr, int data )
|
|||
|
||||
if ( addr == namco_data_reg_addr )
|
||||
{
|
||||
Namco_write_data( &this->namco, time, data );
|
||||
Namco_write_data( &this->namco, Cpu_time( &this->cpu ), data );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -583,7 +592,7 @@ void cpu_write( struct Nsf_Emu* this, addr_t addr, int data )
|
|||
int osc = (unsigned) (addr - vrc6_base_addr) / vrc6_addr_step;
|
||||
if ( (unsigned) osc < vrc6_osc_count && (unsigned) reg < vrc6_reg_count )
|
||||
{
|
||||
Vrc6_write_osc( &this->vrc6, time, osc, reg, data );
|
||||
Vrc6_write_osc( &this->vrc6, Cpu_time( &this->cpu ), osc, reg, data );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -597,7 +606,7 @@ void cpu_write( struct Nsf_Emu* this, addr_t addr, int data )
|
|||
return;
|
||||
|
||||
case fme7_data_addr:
|
||||
Fme7_write_data( &this->fme7, time, data );
|
||||
Fme7_write_data( &this->fme7, Cpu_time( &this->cpu ), data );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -606,7 +615,7 @@ void cpu_write( struct Nsf_Emu* this, addr_t addr, int data )
|
|||
{
|
||||
if ( (unsigned) (addr - mmc5_regs_addr) < mmc5_regs_size )
|
||||
{
|
||||
Mmc5_write_register( &this->mmc5, time, addr, data );
|
||||
Mmc5_write_register( &this->mmc5, Cpu_time( &this->cpu ), addr, data );
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -625,49 +634,28 @@ void cpu_write( struct Nsf_Emu* this, addr_t addr, int data )
|
|||
}
|
||||
}
|
||||
|
||||
if ( vrc7_enabled( this) )
|
||||
{
|
||||
if ( addr == 0x9010 )
|
||||
#ifndef NSF_EMU_NO_VRC7
|
||||
if ( vrc7_enabled( this) )
|
||||
{
|
||||
Vrc7_write_reg( &this->vrc7, data );
|
||||
return;
|
||||
}
|
||||
if ( addr == 0x9010 )
|
||||
{
|
||||
Vrc7_write_reg( &this->vrc7, data );
|
||||
return;
|
||||
}
|
||||
|
||||
if ( (unsigned) (addr - 0x9028) <= 0x08 )
|
||||
{
|
||||
Vrc7_write_data( &this->vrc7, time, data );
|
||||
return;
|
||||
if ( (unsigned) (addr - 0x9028) <= 0x08 )
|
||||
{
|
||||
Vrc7_write_data( &this->vrc7, Cpu_time( &this->cpu ), data );
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
// Unmapped_write
|
||||
}
|
||||
|
||||
#if 0 /* function currently unused */
|
||||
static void unmapped_write( struct Nsf_Emu* this, addr_t addr, int data )
|
||||
{
|
||||
(void) data;
|
||||
|
||||
switch ( addr )
|
||||
{
|
||||
case 0x8000: // some write to $8000 and $8001 repeatedly
|
||||
case 0x8001:
|
||||
case 0x4800: // probably namco sound mistakenly turned on in MCK
|
||||
case 0xF800:
|
||||
case 0xFFF8: // memory mapper?
|
||||
return;
|
||||
}
|
||||
|
||||
if ( mmc5_enabled( this ) && addr == 0x5115 ) return;
|
||||
|
||||
// FDS memory
|
||||
if ( fds_enabled( this ) && (unsigned) (addr - 0x8000) < 0x6000 ) return;
|
||||
}
|
||||
#endif
|
||||
|
||||
void fill_buf( struct Nsf_Emu* this );
|
||||
blargg_err_t Nsf_start_track( struct Nsf_Emu* this, int track )
|
||||
{
|
||||
clear_track_vars( this );
|
||||
|
@ -695,7 +683,9 @@ blargg_err_t Nsf_start_track( struct Nsf_Emu* this, int track )
|
|||
if ( vrc6_enabled( this ) ) Vrc6_reset( &this->vrc6 );
|
||||
if ( fme7_enabled( this ) ) Fme7_reset( &this->fme7 );
|
||||
if ( mmc5_enabled( this ) ) Apu_reset( &this->mmc5.apu, false, 0 );
|
||||
if ( vrc7_enabled( this ) ) Vrc7_reset( &this->vrc7 );
|
||||
#ifndef NSF_EMU_NO_VRC7
|
||||
if ( vrc7_enabled( this ) ) Vrc7_reset( &this->vrc7 );
|
||||
#endif
|
||||
#endif
|
||||
|
||||
int speed_flags = 0;
|
||||
|
@ -728,27 +718,15 @@ blargg_err_t Nsf_start_track( struct Nsf_Emu* this, int track )
|
|||
/* if ( this->cpu.r.pc < get_addr( header.load_addr ) )
|
||||
warning( "Init address < load address" ); */
|
||||
|
||||
this->emu_track_ended_ = false;
|
||||
this->track_ended = false;
|
||||
// convert filter times to samples
|
||||
struct setup_t s = this->tfilter;
|
||||
s.max_initial *= this->sample_rate * stereo;
|
||||
#ifdef GME_DISABLE_SILENCE_LOOKAHEAD
|
||||
s.lookahead = 1;
|
||||
#endif
|
||||
track_setup( &this->track_filter, &s );
|
||||
|
||||
if ( !this->ignore_silence )
|
||||
{
|
||||
// play until non-silence or end of track
|
||||
long end;
|
||||
for ( end = this->max_initial_silence * stereo * this->sample_rate; this->emu_time < end; )
|
||||
{
|
||||
fill_buf( this );
|
||||
if ( this->buf_remain | (int) this->emu_track_ended_ )
|
||||
break;
|
||||
}
|
||||
|
||||
this->emu_time = this->buf_remain;
|
||||
this->out_time = 0;
|
||||
this->silence_time = 0;
|
||||
this->silence_count = 0;
|
||||
}
|
||||
/* return track_ended() ? warning() : 0; */
|
||||
return 0;
|
||||
return track_start( &this->track_filter );
|
||||
}
|
||||
|
||||
void run_once( struct Nsf_Emu* this, nes_time_t end )
|
||||
|
@ -831,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 ( namco_enabled( this ) ) Namco_end_frame( &this->namco, end );
|
||||
if ( vrc6_enabled( this ) ) Vrc6_end_frame( &this->vrc6, end );
|
||||
#ifndef NSF_EMU_NO_VRC7
|
||||
if ( vrc7_enabled( this ) ) Vrc7_end_frame( &this->vrc7, end );
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
// Tell/Seek
|
||||
|
||||
static blargg_long msec_to_samples( long sample_rate, blargg_long msec )
|
||||
static int msec_to_samples( int msec, int sample_rate )
|
||||
{
|
||||
blargg_long sec = msec / 1000;
|
||||
int sec = msec / 1000;
|
||||
msec -= sec * 1000;
|
||||
return (sec * sample_rate + msec * sample_rate / 1000) * stereo;
|
||||
}
|
||||
|
||||
long Track_tell( struct Nsf_Emu* this )
|
||||
int Track_tell( struct Nsf_Emu* this )
|
||||
{
|
||||
blargg_long rate = this->sample_rate * stereo;
|
||||
blargg_long sec = this->out_time / rate;
|
||||
return sec * 1000 + (this->out_time - sec * rate) * 1000 / rate;
|
||||
int rate = this->sample_rate * stereo;
|
||||
int sec = track_sample_count( &this->track_filter ) / rate;
|
||||
return sec * 1000 + (track_sample_count( &this->track_filter ) - sec * rate) * 1000 / rate;
|
||||
}
|
||||
|
||||
blargg_err_t Track_seek( struct Nsf_Emu* this, long msec )
|
||||
blargg_err_t Track_seek( struct Nsf_Emu* this, int msec )
|
||||
{
|
||||
blargg_long time = msec_to_samples( this->sample_rate, msec );
|
||||
if ( time < this->out_time )
|
||||
RETURN_ERR( Nsf_start_track( this, this->current_track ) );
|
||||
return Track_skip( this, time - this->out_time );
|
||||
int time = msec_to_samples( msec, this->sample_rate );
|
||||
if ( time < track_sample_count( &this->track_filter ) )
|
||||
RETURN_ERR( Nsf_start_track( this, this->current_track ) );
|
||||
return Track_skip( this, time - track_sample_count( &this->track_filter ) );
|
||||
}
|
||||
|
||||
blargg_err_t skip_( struct Nsf_Emu* this, long count );
|
||||
blargg_err_t Track_skip( struct Nsf_Emu* this, long count )
|
||||
blargg_err_t Track_skip( struct Nsf_Emu* this, int count )
|
||||
{
|
||||
require( this->current_track >= 0 ); // start_track() must have been called already
|
||||
this->out_time += count;
|
||||
|
||||
// remove from silence and buf first
|
||||
{
|
||||
long n = min( count, this->silence_count );
|
||||
this->silence_count -= n;
|
||||
count -= n;
|
||||
|
||||
n = min( count, this->buf_remain );
|
||||
this->buf_remain -= n;
|
||||
count -= n;
|
||||
}
|
||||
|
||||
if ( count && !this->emu_track_ended_ )
|
||||
{
|
||||
this->emu_time += count;
|
||||
// End track if error
|
||||
if ( skip_( this, count ) )
|
||||
this->emu_track_ended_ = true;
|
||||
}
|
||||
|
||||
if ( !(this->silence_count | this->buf_remain) ) // caught up to emulator, so update track ended
|
||||
this->track_ended |= this->emu_track_ended_;
|
||||
|
||||
return 0;
|
||||
return track_skip( &this->track_filter, count );
|
||||
}
|
||||
|
||||
blargg_err_t play_( struct Nsf_Emu* this, long count, sample_t* out );
|
||||
blargg_err_t skip_( struct Nsf_Emu* this, long count )
|
||||
blargg_err_t skip_( void *emu, int count )
|
||||
{
|
||||
struct Nsf_Emu* this = (struct Nsf_Emu*) emu;
|
||||
|
||||
// for long skip, mute sound
|
||||
const long threshold = 30000;
|
||||
const int threshold = 32768;
|
||||
if ( count > threshold )
|
||||
{
|
||||
int saved_mute = this->mute_mask_;
|
||||
Sound_mute_voices( this, ~0 );
|
||||
|
||||
while ( count > threshold / 2 && !this->emu_track_ended_ )
|
||||
{
|
||||
RETURN_ERR( play_( this, buf_size, this->buf ) );
|
||||
count -= buf_size;
|
||||
}
|
||||
int n = count - threshold/2;
|
||||
n &= ~(2048-1); // round to multiple of 2048
|
||||
count -= n;
|
||||
RETURN_ERR( skippy_( &this->track_filter, n ) );
|
||||
|
||||
Sound_mute_voices( this, saved_mute );
|
||||
}
|
||||
|
||||
while ( count && !this->emu_track_ended_ )
|
||||
{
|
||||
long n = buf_size;
|
||||
if ( n > count )
|
||||
n = count;
|
||||
count -= n;
|
||||
RETURN_ERR( play_( this, n, this->buf ) );
|
||||
}
|
||||
return 0;
|
||||
return skippy_( &this->track_filter, count );
|
||||
}
|
||||
|
||||
// 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);
|
||||
this->fade_start = msec_to_samples( this->sample_rate, start_msec );
|
||||
track_set_fade( &this->track_filter, msec_to_samples( start_msec, this->sample_rate ),
|
||||
length_msec * this->sample_rate / (1000 / stereo) );
|
||||
}
|
||||
|
||||
// unit / pow( 2.0, (double) x / step )
|
||||
static int int_log( blargg_long x, int step, int unit )
|
||||
blargg_err_t Nsf_play( struct Nsf_Emu* this, int out_count, sample_t* out )
|
||||
{
|
||||
int shift = x / step;
|
||||
int fraction = (x - shift * step) * unit / step;
|
||||
return ((unit - fraction) + (fraction >> 1)) >> shift;
|
||||
require( this->current_track >= 0 );
|
||||
require( out_count % stereo == 0 );
|
||||
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;
|
||||
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;
|
||||
struct Nsf_Emu* this = (struct Nsf_Emu*) emu;
|
||||
|
||||
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 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;
|
||||
int remain = count;
|
||||
while ( remain )
|
||||
{
|
||||
Buffer_disable_immediate_removal( &this->stereo_buf );
|
||||
remain -= Buffer_read_samples( &this->stereo_buf, &out [count - remain], remain );
|
||||
if ( remain )
|
||||
{
|
||||
|
@ -1089,7 +901,7 @@ blargg_err_t play_( struct Nsf_Emu* this, long count, sample_t* out )
|
|||
Sound_mute_voices( this, this->mute_mask_ );
|
||||
}
|
||||
int msec = Buffer_length( &this->stereo_buf );
|
||||
blip_time_t clocks_emulated = (blargg_long) msec * this->clock_rate__ / 1000 - 100;
|
||||
blip_time_t clocks_emulated = msec * this->clock_rate__ / 1000 - 100;
|
||||
RETURN_ERR( run_clocks( this, &clocks_emulated, msec ) );
|
||||
assert( clocks_emulated );
|
||||
Buffer_end_frame( &this->stereo_buf, clocks_emulated );
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include "nes_cpu.h"
|
||||
#include "nsfe_info.h"
|
||||
#include "m3u_playlist.h"
|
||||
#include "track_filter.h"
|
||||
|
||||
#ifndef NSF_EMU_APU_ONLY
|
||||
#include "nes_namco_apu.h"
|
||||
|
@ -17,11 +18,11 @@
|
|||
#include "nes_fme7_apu.h"
|
||||
#include "nes_fds_apu.h"
|
||||
#include "nes_mmc5_apu.h"
|
||||
#include "nes_vrc7_apu.h"
|
||||
#ifndef NSF_EMU_NO_VRC7
|
||||
#include "nes_vrc7_apu.h"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
typedef short sample_t;
|
||||
|
||||
// Sound chip flags
|
||||
enum {
|
||||
vrc6_flag = 1 << 0,
|
||||
|
@ -50,8 +51,7 @@ enum { fdsram_size = 0x6000 };
|
|||
enum { fdsram_offset = 0x2000 + page_size + 8 };
|
||||
enum { sram_addr = 0x6000 };
|
||||
enum { unmapped_size= page_size + 8 };
|
||||
|
||||
enum { buf_size = 2048 };
|
||||
enum { max_voices = 32 };
|
||||
|
||||
// NSF file header
|
||||
enum { header_size = 0x80 };
|
||||
|
@ -83,36 +83,20 @@ struct Nsf_Emu {
|
|||
int play_delay;
|
||||
struct registers_t saved_state; // of interrupted init routine
|
||||
|
||||
int track_count;
|
||||
|
||||
// general
|
||||
int max_initial_silence;
|
||||
int voice_count;
|
||||
int voice_types [32];
|
||||
int mute_mask_;
|
||||
int tempo;
|
||||
int gain;
|
||||
|
||||
long sample_rate;
|
||||
int sample_rate;
|
||||
|
||||
// track-specific
|
||||
int track_count;
|
||||
int current_track;
|
||||
blargg_long out_time; // number of samples played since start of track
|
||||
blargg_long emu_time; // number of samples emulator has generated since start of track
|
||||
bool emu_track_ended_; // emulator has reached end of track
|
||||
volatile bool track_ended;
|
||||
|
||||
// fading
|
||||
blargg_long fade_start;
|
||||
int fade_step;
|
||||
|
||||
// silence detection
|
||||
int silence_lookahead; // speed to run emulator when looking ahead for silence
|
||||
bool ignore_silence;
|
||||
long silence_time; // number of samples where most recent silence began
|
||||
long silence_count; // number of samples of silence to play before using buf
|
||||
long buf_remain; // number of samples left in silence buffer
|
||||
|
||||
long clock_rate__;
|
||||
int clock_rate__;
|
||||
unsigned buf_changed_count;
|
||||
|
||||
// M3u Playlist
|
||||
|
@ -127,7 +111,9 @@ struct Nsf_Emu {
|
|||
struct Nes_Namco_Apu namco;
|
||||
struct Nes_Vrc6_Apu vrc6;
|
||||
struct Nes_Fme7_Apu fme7;
|
||||
struct Nes_Vrc7_Apu vrc7;
|
||||
#ifndef NSF_EMU_NO_VRC7
|
||||
struct Nes_Vrc7_Apu vrc7;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
struct Nes_Cpu cpu;
|
||||
|
@ -136,13 +122,15 @@ struct Nsf_Emu {
|
|||
// Header for currently loaded file
|
||||
struct header_t header;
|
||||
|
||||
struct Stereo_Buffer stereo_buf;
|
||||
struct setup_t tfilter;
|
||||
struct Track_Filter track_filter;
|
||||
|
||||
struct Multi_Buffer stereo_buf;
|
||||
struct Rom_Data rom;
|
||||
|
||||
// Extended nsf info
|
||||
struct Nsfe_Info info;
|
||||
|
||||
sample_t buf [buf_size];
|
||||
byte high_ram[fdsram_size + fdsram_offset];
|
||||
byte low_ram [low_ram_size];
|
||||
};
|
||||
|
@ -150,18 +138,18 @@ struct Nsf_Emu {
|
|||
// Basic functionality (see Gme_File.h for file loading/track info functions)
|
||||
|
||||
void Nsf_init( struct Nsf_Emu* this );
|
||||
blargg_err_t Nsf_load( struct Nsf_Emu* this, void* data, long size );
|
||||
blargg_err_t Nsf_load_mem( struct Nsf_Emu* this, void* data, long size );
|
||||
blargg_err_t Nsf_post_load( struct Nsf_Emu* this );
|
||||
|
||||
// Set output sample rate. Must be called only once before loading file.
|
||||
blargg_err_t Nsf_set_sample_rate( struct Nsf_Emu* this, long sample_rate );
|
||||
blargg_err_t Nsf_set_sample_rate( struct Nsf_Emu* this, int sample_rate );
|
||||
|
||||
// Start a track, where 0 is the first track. Also clears warning string.
|
||||
blargg_err_t Nsf_start_track( struct Nsf_Emu* this , int );
|
||||
|
||||
// Generate 'count' samples info 'buf'. Output is in stereo. Any emulation
|
||||
// errors set warning string, and major errors also end track.
|
||||
blargg_err_t Nsf_play( struct Nsf_Emu* this, long count, sample_t* buf );
|
||||
blargg_err_t Nsf_play( struct Nsf_Emu* this, int count, sample_t* buf );
|
||||
|
||||
void Nsf_clear_playlist( struct Nsf_Emu* this );
|
||||
void Nsf_disable_playlist( struct Nsf_Emu* this, bool b ); // use clear_playlist()
|
||||
|
@ -169,20 +157,32 @@ void Nsf_disable_playlist( struct Nsf_Emu* this, bool b ); // use clear_playlist
|
|||
// Track status/control
|
||||
|
||||
// Number of milliseconds (1000 msec = 1 second) played since beginning of track
|
||||
long Track_tell( struct Nsf_Emu* this );
|
||||
int Track_tell( struct Nsf_Emu* this );
|
||||
|
||||
// Seek to new time in track. Seeking backwards or far forward can take a while.
|
||||
blargg_err_t Track_seek( struct Nsf_Emu* this, long msec );
|
||||
blargg_err_t Track_seek( struct Nsf_Emu* this, int msec );
|
||||
|
||||
// Skip n samples
|
||||
blargg_err_t Track_skip( struct Nsf_Emu* this, long n );
|
||||
blargg_err_t Track_skip( struct Nsf_Emu* this, int n );
|
||||
|
||||
// Set start time and length of track fade out. Once fade ends track_ended() returns
|
||||
// true. Fade time can be changed while track is playing.
|
||||
void Track_set_fade( struct Nsf_Emu* this, long start_msec, long length_msec );
|
||||
void Track_set_fade( struct Nsf_Emu* this, int start_msec, int length_msec );
|
||||
|
||||
// True if a track has reached its end
|
||||
static inline bool Track_ended( struct Nsf_Emu* this )
|
||||
{
|
||||
return track_ended( &this->track_filter );
|
||||
}
|
||||
|
||||
// Disables automatic end-of-track detection and skipping of silence at beginning
|
||||
static inline void Track_ignore_silence( struct Nsf_Emu* this, bool disable )
|
||||
{
|
||||
this->track_filter.silence_ignored_ = disable;
|
||||
}
|
||||
|
||||
// Get track length in milliseconds
|
||||
long Track_length( struct Nsf_Emu* this, int n );
|
||||
int Track_length( struct Nsf_Emu* this, int n );
|
||||
|
||||
// Sound customization
|
||||
|
||||
|
@ -248,12 +248,14 @@ static inline byte* sram( struct Nsf_Emu* this ) { return this->high_
|
|||
static inline byte* unmapped_code( struct Nsf_Emu* this ) { return &this->high_ram [sram_size]; }
|
||||
|
||||
#ifndef NSF_EMU_APU_ONLY
|
||||
static inline int fds_enabled( struct Nsf_Emu* this ) { return this->header.chip_flags & fds_flag; }
|
||||
static inline int vrc6_enabled( struct Nsf_Emu* this ) { return this->header.chip_flags & vrc6_flag; }
|
||||
static inline int vrc7_enabled( struct Nsf_Emu* this ) { return this->header.chip_flags & vrc7_flag; }
|
||||
static inline int mmc5_enabled( struct Nsf_Emu* this ) { return this->header.chip_flags & mmc5_flag; }
|
||||
static inline int namco_enabled( struct Nsf_Emu* this ) { return this->header.chip_flags & namco_flag; }
|
||||
static inline int fme7_enabled( struct Nsf_Emu* this ) { return this->header.chip_flags & fme7_flag; }
|
||||
static inline int fds_enabled( struct Nsf_Emu* this ) { return this->header.chip_flags & fds_flag; }
|
||||
static inline int vrc6_enabled( struct Nsf_Emu* this ) { return this->header.chip_flags & vrc6_flag; }
|
||||
#ifndef NSF_EMU_NO_VRC7
|
||||
static inline int vrc7_enabled( struct Nsf_Emu* this ) { return this->header.chip_flags & vrc7_flag; }
|
||||
#endif
|
||||
static inline int mmc5_enabled( struct Nsf_Emu* this ) { return this->header.chip_flags & mmc5_flag; }
|
||||
static inline int namco_enabled( struct Nsf_Emu* this ) { return this->header.chip_flags & namco_flag; }
|
||||
static inline int fme7_enabled( struct Nsf_Emu* this ) { return this->header.chip_flags & fme7_flag; }
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
|
@ -154,8 +154,8 @@ blargg_err_t Info_load( struct Nsfe_Info* this, void* data, long size, struct Ns
|
|||
byte block_header [2] [4];
|
||||
RETURN_ERR( in_read( block_header, sizeof block_header, data, &offset, size ) );
|
||||
|
||||
blargg_long chunk_size = get_le32( block_header [0] );
|
||||
blargg_long tag = get_le32( block_header [1] );
|
||||
int chunk_size = get_le32( block_header [0] );
|
||||
int tag = get_le32( block_header [1] );
|
||||
|
||||
switch ( tag )
|
||||
{
|
||||
|
@ -168,7 +168,7 @@ blargg_err_t Info_load( struct Nsfe_Info* this, void* data, long size, struct Ns
|
|||
finfo.track_count = 1;
|
||||
finfo.first_track = 0;
|
||||
|
||||
RETURN_ERR( in_read( &finfo, min( chunk_size, (blargg_long) nsfe_info_size ),
|
||||
RETURN_ERR( in_read( &finfo, min( chunk_size, nsfe_info_size ),
|
||||
(char*) data, &offset, size ) );
|
||||
|
||||
if ( chunk_size > nsfe_info_size )
|
||||
|
@ -248,9 +248,9 @@ blargg_err_t Info_load( struct Nsfe_Info* this, void* data, long size, struct Ns
|
|||
return 0;
|
||||
}
|
||||
|
||||
long Track_length( struct Nsf_Emu* this, int n )
|
||||
int Track_length( struct Nsf_Emu* this, int n )
|
||||
{
|
||||
long length = 0;
|
||||
int length = 0;
|
||||
if ( (this->m3u.size > 0) && (n < this->m3u.size) ) {
|
||||
struct entry_t* entry = &this->m3u.entries [n];
|
||||
length = entry->length;
|
||||
|
|
|
@ -64,7 +64,7 @@ void Resampler_resize( struct Resampler* this, int pairs )
|
|||
}
|
||||
}
|
||||
|
||||
void mix_samples( struct Resampler* this, struct Blip_Buffer* blip_buf, sample_t out_ [] )
|
||||
void mix_samples( struct Resampler* this, struct Blip_Buffer* blip_buf, dsample_t out_ [] )
|
||||
{
|
||||
int const bass = BLIP_READER_BASS( *blip_buf );
|
||||
BLIP_READER_BEGIN( sn, *blip_buf );
|
||||
|
@ -72,7 +72,7 @@ void mix_samples( struct Resampler* this, struct Blip_Buffer* blip_buf, sample_t
|
|||
int count = this->sample_buf_size >> 1;
|
||||
BLIP_READER_ADJ_( sn, count );
|
||||
|
||||
typedef sample_t stereo_dsample_t [2];
|
||||
typedef dsample_t stereo_dsample_t [2];
|
||||
stereo_dsample_t* BLARGG_RESTRICT out = (stereo_dsample_t*) out_ + count;
|
||||
stereo_dsample_t const* BLARGG_RESTRICT in =
|
||||
(stereo_dsample_t const*) this->sample_buf + count;
|
||||
|
@ -97,14 +97,14 @@ void mix_samples( struct Resampler* this, struct Blip_Buffer* blip_buf, sample_t
|
|||
BLIP_READER_END( sn, *blip_buf );
|
||||
}
|
||||
|
||||
sample_t const* resample_( struct Resampler* this, sample_t** out_,
|
||||
sample_t const* out_end, sample_t const in [], int in_size )
|
||||
dsample_t const* resample_( struct Resampler* this, dsample_t** out_,
|
||||
dsample_t const* out_end, dsample_t const in [], int in_size )
|
||||
{
|
||||
in_size -= write_offset;
|
||||
if ( in_size > 0 )
|
||||
{
|
||||
sample_t* BLARGG_RESTRICT out = *out_;
|
||||
sample_t const* const in_end = in + in_size;
|
||||
dsample_t* BLARGG_RESTRICT out = *out_;
|
||||
dsample_t const* const in_end = in + in_size;
|
||||
|
||||
int const step = this->step;
|
||||
int pos = this->pos;
|
||||
|
@ -135,12 +135,12 @@ sample_t const* resample_( struct Resampler* this, sample_t** out_,
|
|||
return in;
|
||||
}
|
||||
|
||||
static inline int resample_wrapper( struct Resampler* this, sample_t out [], int* out_size,
|
||||
sample_t const in [], int in_size )
|
||||
static inline int resample_wrapper( struct Resampler* this, dsample_t out [], int* out_size,
|
||||
dsample_t const in [], int in_size )
|
||||
{
|
||||
assert( Resampler_rate( this ) );
|
||||
|
||||
sample_t* out_ = out;
|
||||
dsample_t* out_ = out;
|
||||
int result = resample_( this, &out_, out + *out_size, in, in_size ) - in;
|
||||
assert( out_ <= out + *out_size );
|
||||
assert( result <= in_size );
|
||||
|
@ -161,7 +161,7 @@ int skip_input( struct Resampler* this, int count )
|
|||
return count;
|
||||
}
|
||||
|
||||
void play_frame_( struct Resampler* this, struct Blip_Buffer* blip_buf, sample_t* out )
|
||||
void play_frame_( struct Resampler* this, struct Blip_Buffer* blip_buf, dsample_t* out )
|
||||
{
|
||||
int pair_count = this->sample_buf_size >> 1;
|
||||
blip_time_t blip_time = Blip_count_clocks( blip_buf, pair_count );
|
||||
|
@ -185,7 +185,7 @@ void play_frame_( struct Resampler* this, struct Blip_Buffer* blip_buf, sample_t
|
|||
Blip_remove_samples( blip_buf, pair_count );
|
||||
}
|
||||
|
||||
void Resampler_play( struct Resampler* this, int count, sample_t* out, struct Blip_Buffer* blip_buf )
|
||||
void Resampler_play( struct Resampler* this, int count, dsample_t* out, struct Blip_Buffer* blip_buf )
|
||||
{
|
||||
// empty extra buffer
|
||||
int remain = this->sample_buf_size - this->buf_pos;
|
||||
|
|
|
@ -7,16 +7,15 @@
|
|||
#include "blargg_common.h"
|
||||
#include "multi_buffer.h"
|
||||
|
||||
typedef short sample_t;
|
||||
typedef short dsample_t;
|
||||
|
||||
enum { stereo = 2 };
|
||||
enum { max_buf_size = 3960 };
|
||||
enum { max_resampler_size = 5942 };
|
||||
enum { write_offset = 8 * stereo };
|
||||
enum { gain_bits = 14 };
|
||||
|
||||
struct Resampler {
|
||||
int (*callback)( void*, blip_time_t, int, sample_t* );
|
||||
int (*callback)( void*, blip_time_t, int, dsample_t* );
|
||||
void* callback_data;
|
||||
|
||||
int sample_buffer_size;
|
||||
|
@ -34,8 +33,8 @@ struct Resampler {
|
|||
|
||||
int rate_;
|
||||
|
||||
sample_t sample_buf [max_buf_size];
|
||||
sample_t buf [max_resampler_size]; // Internal resampler
|
||||
dsample_t sample_buf [max_buf_size];
|
||||
dsample_t buf [max_resampler_size]; // Internal resampler
|
||||
};
|
||||
|
||||
static inline void Resampler_init( struct Resampler* this )
|
||||
|
@ -50,9 +49,9 @@ static inline void Resampler_init( struct Resampler* this )
|
|||
|
||||
blargg_err_t Resampler_reset( struct Resampler* this, int max_pairs );
|
||||
void Resampler_resize( struct Resampler* this, int pairs_per_frame );
|
||||
void Resampler_play( struct Resampler* this, int count, sample_t* out, struct Blip_Buffer* );
|
||||
void Resampler_play( struct Resampler* this, int count, dsample_t* out, struct Blip_Buffer* );
|
||||
|
||||
static inline void Resampler_set_callback(struct Resampler* this, int (*func)( void*, blip_time_t, int, sample_t* ), void* user_data )
|
||||
static inline void Resampler_set_callback(struct Resampler* this, int (*func)( void*, blip_time_t, int, dsample_t* ), void* user_data )
|
||||
{
|
||||
this->callback = func;
|
||||
this->callback_data = user_data;
|
||||
|
|
|
@ -21,7 +21,7 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
|
|||
blargg_err_t Rom_load( struct Rom_Data* this, const void* data, long size,
|
||||
int header_size, void* header_out, int fill )
|
||||
{
|
||||
long file_offset = this->pad_size;
|
||||
int file_offset = this->pad_size;
|
||||
|
||||
this->rom_addr = 0;
|
||||
this->mask = 0;
|
||||
|
@ -43,11 +43,11 @@ blargg_err_t Rom_load( struct Rom_Data* this, const void* data, long size,
|
|||
return 0;
|
||||
}
|
||||
|
||||
void Rom_set_addr( struct Rom_Data* this, long addr )
|
||||
void Rom_set_addr( struct Rom_Data* this, int addr )
|
||||
{
|
||||
this->rom_addr = addr - this->bank_size - pad_extra;
|
||||
|
||||
long rounded = (addr + this->file_size + this->bank_size - 1) / this->bank_size * this->bank_size;
|
||||
int rounded = (addr + this->file_size + this->bank_size - 1) / this->bank_size * this->bank_size;
|
||||
if ( rounded <= 0 )
|
||||
{
|
||||
rounded = 0;
|
||||
|
@ -55,7 +55,7 @@ void Rom_set_addr( struct Rom_Data* this, long addr )
|
|||
else
|
||||
{
|
||||
int shift = 0;
|
||||
unsigned long max_addr = (unsigned long) (rounded - 1);
|
||||
unsigned int max_addr = (unsigned int) (rounded - 1);
|
||||
while ( max_addr >> shift )
|
||||
shift++;
|
||||
this->mask = (1L << shift) - 1;
|
||||
|
|
|
@ -20,22 +20,22 @@ enum { max_rom_size = 2 * max_pad_size };
|
|||
|
||||
struct Rom_Data {
|
||||
byte* file_data;
|
||||
blargg_ulong file_size;
|
||||
unsigned file_size;
|
||||
|
||||
blargg_long rom_addr;
|
||||
blargg_long bank_size;
|
||||
blargg_long rom_size;
|
||||
blargg_ulong pad_size;
|
||||
blargg_long mask;
|
||||
blargg_long size; // TODO: eliminate
|
||||
blargg_long rsize_;
|
||||
int rom_addr;
|
||||
int bank_size;
|
||||
int rom_size;
|
||||
unsigned pad_size;
|
||||
int mask;
|
||||
int size; // TODO: eliminate
|
||||
int rsize_;
|
||||
|
||||
// Unmapped space
|
||||
byte unmapped [max_rom_size];
|
||||
};
|
||||
|
||||
// Initialize rom
|
||||
static inline void Rom_init( struct Rom_Data* this, blargg_long bank_size )
|
||||
static inline void Rom_init( struct Rom_Data* this, int bank_size )
|
||||
{
|
||||
this->bank_size = bank_size;
|
||||
this->pad_size = this->bank_size + pad_extra;
|
||||
|
@ -47,10 +47,10 @@ static inline void Rom_init( struct Rom_Data* this, blargg_long bank_size )
|
|||
blargg_err_t Rom_load( struct Rom_Data* this, const void* data, long size, int header_size, void* header_out, int fill );
|
||||
|
||||
// Set address that file data should start at
|
||||
void Rom_set_addr( struct Rom_Data* this, long addr );
|
||||
void Rom_set_addr( struct Rom_Data* this, int addr );
|
||||
|
||||
// Mask address to nearest power of two greater than size()
|
||||
static inline blargg_long mask_addr( blargg_long addr, blargg_long mask )
|
||||
static inline int mask_addr( int addr, int mask )
|
||||
{
|
||||
#ifdef check
|
||||
check( addr <= mask );
|
||||
|
@ -59,10 +59,10 @@ static inline blargg_long mask_addr( blargg_long addr, blargg_long mask )
|
|||
}
|
||||
|
||||
// Pointer to page starting at addr. Returns unmapped() if outside data.
|
||||
static inline byte* Rom_at_addr( struct Rom_Data* this, blargg_long addr )
|
||||
static inline byte* Rom_at_addr( struct Rom_Data* this, int addr )
|
||||
{
|
||||
blargg_ulong offset = mask_addr( addr, this->mask ) - this->rom_addr;
|
||||
if ( offset > (blargg_ulong) (this->rsize_ - this->pad_size) )
|
||||
unsigned offset = mask_addr( addr, this->mask ) - this->rom_addr;
|
||||
if ( offset > (unsigned) (this->rsize_ - this->pad_size) )
|
||||
offset = 0; // unmapped
|
||||
|
||||
if ( offset < this->pad_size ) return &this->unmapped [offset];
|
||||
|
|
|
@ -10,34 +10,19 @@ module is distributed in the hope that it will be useful, but WITHOUT ANY
|
|||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
|
||||
details. You should have received a copy of the GNU Lesser General Public
|
||||
License along with this module; if not, write to the Free Software Foundation,
|
||||
License aint with this module; if not, write to the Free Software Foundation,
|
||||
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
|
||||
|
||||
#include "blargg_source.h"
|
||||
|
||||
int const osc_count = sms_osc_count + fm_apu_osc_count;
|
||||
|
||||
int const stereo = 2; // number of channels for stereo
|
||||
int const silence_max = 6; // seconds
|
||||
int const silence_threshold = 0x10;
|
||||
long const fade_block_size = 512;
|
||||
int const fade_shift = 8; // fade ends with gain at 1.0 / (1 << fade_shift)
|
||||
|
||||
const char gme_wrong_file_type [] = "Wrong file type for this emulator";
|
||||
|
||||
static void clear_track_vars( struct Sgc_Emu* this )
|
||||
{
|
||||
this->current_track = -1;
|
||||
this->out_time = 0;
|
||||
this->emu_time = 0;
|
||||
this->emu_track_ended_ = true;
|
||||
this->track_ended = true;
|
||||
this->fade_start = INT_MAX / 2 + 1;
|
||||
this->fade_step = 1;
|
||||
this->silence_time = 0;
|
||||
this->silence_count = 0;
|
||||
this->buf_remain = 0;
|
||||
/* warning(); // clear warning */
|
||||
track_stop( &this->track_filter );
|
||||
}
|
||||
|
||||
void Sgc_init( struct Sgc_Emu* this )
|
||||
|
@ -48,12 +33,12 @@ void Sgc_init( struct Sgc_Emu* this )
|
|||
this->mute_mask_ = 0;
|
||||
this->tempo = (int)FP_ONE_TEMPO;
|
||||
this->gain = (int)FP_ONE_GAIN;
|
||||
this->voice_count = 0;
|
||||
|
||||
// defaults
|
||||
this->max_initial_silence = 2;
|
||||
this->silence_lookahead = 6;
|
||||
this->ignore_silence = false;
|
||||
this->tfilter = *track_get_setup( &this->track_filter );
|
||||
this->tfilter.max_initial = 2;
|
||||
this->tfilter.lookahead = 6;
|
||||
this->track_filter.silence_ignored_ = false;
|
||||
|
||||
Sms_apu_init( &this->apu );
|
||||
Fm_apu_create( &this->fm_apu );
|
||||
|
@ -64,6 +49,8 @@ void Sgc_init( struct Sgc_Emu* this )
|
|||
Sound_set_gain( this, (int)(FP_ONE_GAIN*1.2) );
|
||||
|
||||
// Unload
|
||||
this->voice_count = 0;
|
||||
this->voice_types = 0;
|
||||
clear_track_vars( this );
|
||||
}
|
||||
|
||||
|
@ -95,6 +82,10 @@ blargg_err_t Sgc_load_mem( struct Sgc_Emu* this, const void* data, long size )
|
|||
this->m3u.size = 0;
|
||||
this->track_count = this->header.song_count;
|
||||
this->voice_count = sega_mapping( this ) ? osc_count : sms_osc_count;
|
||||
static int const types [osc_count + 1] = {
|
||||
wave_type+1, wave_type+2, wave_type+3, mixed_type+1, mixed_type+2
|
||||
};
|
||||
this->voice_types = types;
|
||||
|
||||
Sms_apu_volume( &this->apu, this->gain );
|
||||
Fm_apu_volume( &this->fm_apu, this->gain );
|
||||
|
@ -102,10 +93,10 @@ blargg_err_t Sgc_load_mem( struct Sgc_Emu* this, const void* data, long size )
|
|||
// Setup buffer
|
||||
this->clock_rate_ = clock_rate( this );
|
||||
Buffer_clock_rate( &this->stereo_buf, clock_rate( this ) );
|
||||
RETURN_ERR( Buffer_set_channel_count( &this->stereo_buf, this->voice_count, this->voice_types ) );
|
||||
this->buf_changed_count = Buffer_channels_changed_count( &this->stereo_buf );
|
||||
Sound_set_tempo( this, this->tempo );
|
||||
|
||||
// Remute voices
|
||||
Sound_set_tempo( this, this->tempo );
|
||||
Sound_mute_voices( this, this->mute_mask_ );
|
||||
return 0;
|
||||
}
|
||||
|
@ -249,7 +240,7 @@ void cpu_write( struct Sgc_Emu* this, addr_t addr, int data )
|
|||
}
|
||||
}
|
||||
|
||||
blargg_err_t Sgc_set_sample_rate( struct Sgc_Emu* this, long rate )
|
||||
blargg_err_t Sgc_set_sample_rate( struct Sgc_Emu* this, int rate )
|
||||
{
|
||||
require( !this->sample_rate ); // sample rate can't be changed once set
|
||||
Buffer_init( &this->stereo_buf );
|
||||
|
@ -259,6 +250,8 @@ blargg_err_t Sgc_set_sample_rate( struct Sgc_Emu* this, long rate )
|
|||
Buffer_bass_freq( &this->stereo_buf, 80 );
|
||||
|
||||
this->sample_rate = rate;
|
||||
RETURN_ERR( track_init( &this->track_filter, this ) );
|
||||
this->tfilter.max_silence = 6 * stereo * this->sample_rate;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -286,7 +279,7 @@ void Sound_mute_voices( struct Sgc_Emu* this, int mask )
|
|||
}
|
||||
else
|
||||
{
|
||||
struct channel_t ch = Buffer_channel( &this->stereo_buf );
|
||||
struct channel_t ch = Buffer_channel( &this->stereo_buf, i );
|
||||
assert( (ch.center && ch.left && ch.right) ||
|
||||
(!ch.center && !ch.left && !ch.right) ); // all or nothing
|
||||
Sound_set_voice( this, i, ch.center, ch.left, ch.right );
|
||||
|
@ -306,7 +299,6 @@ void Sound_set_tempo( struct Sgc_Emu* this, int t )
|
|||
this->play_period = (int) ((clock_rate( this ) * FP_ONE_TEMPO) / (this->header.rate ? 50 : 60) / t);
|
||||
}
|
||||
|
||||
void fill_buf( struct Sgc_Emu* this );
|
||||
blargg_err_t Sgc_start_track( struct Sgc_Emu* this, int track )
|
||||
{
|
||||
clear_track_vars( this );
|
||||
|
@ -383,275 +375,90 @@ blargg_err_t Sgc_start_track( struct Sgc_Emu* this, int track )
|
|||
|
||||
Buffer_clear( &this->stereo_buf );
|
||||
|
||||
this->emu_track_ended_ = false;
|
||||
this->track_ended = false;
|
||||
// convert filter times to samples
|
||||
struct setup_t s = this->tfilter;
|
||||
s.max_initial *= this->sample_rate * stereo;
|
||||
#ifdef GME_DISABLE_SILENCE_LOOKAHEAD
|
||||
s.lookahead = 1;
|
||||
#endif
|
||||
track_setup( &this->track_filter, &s );
|
||||
|
||||
if ( !this->ignore_silence )
|
||||
{
|
||||
// play until non-silence or end of track
|
||||
long end;
|
||||
for ( end = this->max_initial_silence * stereo * this->sample_rate; this->emu_time < end; )
|
||||
{
|
||||
fill_buf( this );
|
||||
if ( this->buf_remain | (int) this->emu_track_ended_ )
|
||||
break;
|
||||
}
|
||||
|
||||
this->emu_time = this->buf_remain;
|
||||
this->out_time = 0;
|
||||
this->silence_time = 0;
|
||||
this->silence_count = 0;
|
||||
}
|
||||
/* return track_ended() ? warning() : 0; */
|
||||
return 0;
|
||||
return track_start( &this->track_filter );
|
||||
}
|
||||
|
||||
// Tell/Seek
|
||||
|
||||
static blargg_long msec_to_samples( blargg_long msec, long sample_rate )
|
||||
static int msec_to_samples( int msec, int sample_rate )
|
||||
{
|
||||
blargg_long sec = msec / 1000;
|
||||
int sec = msec / 1000;
|
||||
msec -= sec * 1000;
|
||||
return (sec * sample_rate + msec * sample_rate / 1000) * stereo;
|
||||
}
|
||||
|
||||
long Track_tell( struct Sgc_Emu* this )
|
||||
int Track_tell( struct Sgc_Emu* this )
|
||||
{
|
||||
blargg_long rate = this->sample_rate * stereo;
|
||||
blargg_long sec = this->out_time / rate;
|
||||
return sec * 1000 + (this->out_time - sec * rate) * 1000 / rate;
|
||||
int rate = this->sample_rate * stereo;
|
||||
int sec = track_sample_count( &this->track_filter ) / rate;
|
||||
return sec * 1000 + (track_sample_count( &this->track_filter ) - sec * rate) * 1000 / rate;
|
||||
}
|
||||
|
||||
blargg_err_t Track_seek( struct Sgc_Emu* this, long msec )
|
||||
blargg_err_t Track_seek( struct Sgc_Emu* this, int msec )
|
||||
{
|
||||
blargg_long time = msec_to_samples( msec, this->sample_rate );
|
||||
if ( time < this->out_time )
|
||||
RETURN_ERR( Sgc_start_track( this, this->current_track ) );
|
||||
return Track_skip( this, time - this->out_time );
|
||||
int time = msec_to_samples( msec, this->sample_rate );
|
||||
if ( time < track_sample_count( &this->track_filter ) )
|
||||
RETURN_ERR( Sgc_start_track( this, this->current_track ) );
|
||||
return Track_skip( this, time - track_sample_count( &this->track_filter ) );
|
||||
}
|
||||
|
||||
blargg_err_t skip_( struct Sgc_Emu* this, long count );
|
||||
blargg_err_t Track_skip( struct Sgc_Emu* this, long count )
|
||||
blargg_err_t Track_skip( struct Sgc_Emu* this, int count )
|
||||
{
|
||||
require( this->current_track >= 0 ); // start_track() must have been called already
|
||||
this->out_time += count;
|
||||
|
||||
// remove from silence and buf first
|
||||
{
|
||||
long n = min( count, this->silence_count );
|
||||
this->silence_count -= n;
|
||||
count -= n;
|
||||
|
||||
n = min( count, this->buf_remain );
|
||||
this->buf_remain -= n;
|
||||
count -= n;
|
||||
}
|
||||
|
||||
if ( count && !this->emu_track_ended_ )
|
||||
{
|
||||
this->emu_time += count;
|
||||
|
||||
// End track if error
|
||||
if ( skip_( this, count ) ) {
|
||||
this->emu_track_ended_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ( !(this->silence_count | this->buf_remain) ) // caught up to emulator, so update track ended
|
||||
this->track_ended |= this->emu_track_ended_;
|
||||
|
||||
return 0;
|
||||
return track_skip( &this->track_filter, count );
|
||||
}
|
||||
|
||||
blargg_err_t play_( struct Sgc_Emu* this, long count, sample_t* out );
|
||||
blargg_err_t skip_( struct Sgc_Emu* this, long count )
|
||||
blargg_err_t skip_( void* emu, int count )
|
||||
{
|
||||
struct Sgc_Emu* this = (struct Sgc_Emu*) emu;
|
||||
|
||||
// for long skip, mute sound
|
||||
const long threshold = 30000;
|
||||
const int threshold = 32768;
|
||||
if ( count > threshold )
|
||||
{
|
||||
int saved_mute = this->mute_mask_;
|
||||
Sound_mute_voices( this, ~0 );
|
||||
|
||||
while ( count > threshold / 2 && !this->emu_track_ended_ )
|
||||
{
|
||||
RETURN_ERR( play_( this, buf_size, this->buf ) );
|
||||
count -= buf_size;
|
||||
}
|
||||
int n = count - threshold/2;
|
||||
n &= ~(2048-1); // round to multiple of 2048
|
||||
count -= n;
|
||||
RETURN_ERR( skippy_( &this->track_filter, n ) );
|
||||
|
||||
Sound_mute_voices( this, saved_mute );
|
||||
}
|
||||
|
||||
while ( count && !this->emu_track_ended_ )
|
||||
{
|
||||
long n = buf_size;
|
||||
if ( n > count )
|
||||
n = count;
|
||||
count -= n;
|
||||
RETURN_ERR( play_( this, n, this->buf ) );
|
||||
}
|
||||
return 0;
|
||||
return skippy_( &this->track_filter, count );
|
||||
}
|
||||
|
||||
// Fading
|
||||
|
||||
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 )
|
||||
{
|
||||
this->fade_step = this->sample_rate * length_msec / (fade_block_size * fade_shift * 1000 / stereo);
|
||||
this->fade_start = msec_to_samples( start_msec, this->sample_rate );
|
||||
track_set_fade( &this->track_filter, msec_to_samples( start_msec, this->sample_rate ),
|
||||
length_msec * this->sample_rate / (1000 / stereo) );
|
||||
}
|
||||
|
||||
// unit / pow( 2.0, (double) x / step )
|
||||
static int int_log( blargg_long x, int step, int unit )
|
||||
blargg_err_t Sgc_play( struct Sgc_Emu* this, int out_count, sample_t* out )
|
||||
{
|
||||
int shift = x / step;
|
||||
int fraction = (x - shift * step) * unit / step;
|
||||
return ((unit - fraction) + (fraction >> 1)) >> shift;
|
||||
require( this->current_track >= 0 );
|
||||
require( out_count % stereo == 0 );
|
||||
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;
|
||||
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;
|
||||
struct Sgc_Emu* this = (struct Sgc_Emu*) emu;
|
||||
|
||||
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 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;
|
||||
int remain = count;
|
||||
while ( remain )
|
||||
{
|
||||
Buffer_disable_immediate_removal( &this->stereo_buf );
|
||||
remain -= Buffer_read_samples( &this->stereo_buf, &out [count - remain], remain );
|
||||
if ( remain )
|
||||
{
|
||||
|
|
|
@ -12,12 +12,10 @@
|
|||
#include "sms_fm_apu.h"
|
||||
#include "sms_apu.h"
|
||||
#include "m3u_playlist.h"
|
||||
#include "track_filter.h"
|
||||
|
||||
typedef short sample_t;
|
||||
typedef struct Z80_Cpu Sgc_Cpu;
|
||||
|
||||
enum { buf_size = 2048 };
|
||||
|
||||
// SGC file header
|
||||
enum { header_size = 0xA0 };
|
||||
struct header_t
|
||||
|
@ -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; }
|
||||
|
||||
|
||||
struct Sgc_Emu {
|
||||
bool fm_accessed;
|
||||
|
||||
|
@ -65,40 +62,28 @@ struct Sgc_Emu {
|
|||
|
||||
// general
|
||||
int voice_count;
|
||||
int const* voice_types;
|
||||
int mute_mask_;
|
||||
int tempo;
|
||||
int gain;
|
||||
|
||||
long sample_rate;
|
||||
int sample_rate;
|
||||
|
||||
// track-specific
|
||||
volatile bool track_ended;
|
||||
int current_track;
|
||||
int track_count;
|
||||
blargg_long out_time; // number of samples played since start of track
|
||||
blargg_long emu_time; // number of samples emulator has generated since start of track
|
||||
bool emu_track_ended_; // emulator has reached end of track
|
||||
|
||||
// fading
|
||||
blargg_long fade_start;
|
||||
int fade_step;
|
||||
|
||||
// silence detection
|
||||
bool ignore_silence;
|
||||
int max_initial_silence;
|
||||
int silence_lookahead; // speed to run emulator when looking ahead for silence
|
||||
long silence_time; // number of samples where most recent silence began
|
||||
long silence_count; // number of samples of silence to play before using buf
|
||||
long buf_remain; // number of samples left in silence buffer
|
||||
|
||||
long clock_rate_;
|
||||
int clock_rate_;
|
||||
unsigned buf_changed_count;
|
||||
|
||||
// M3u Playlist
|
||||
struct M3u_Playlist m3u;
|
||||
struct header_t header;
|
||||
|
||||
sample_t buf [buf_size];
|
||||
struct Stereo_Buffer stereo_buf;
|
||||
struct setup_t tfilter;
|
||||
struct Track_Filter track_filter;
|
||||
|
||||
struct Multi_Buffer stereo_buf;
|
||||
|
||||
struct Sms_Apu apu;
|
||||
struct Sms_Fm_Apu fm_apu;
|
||||
|
@ -106,12 +91,11 @@ struct Sgc_Emu {
|
|||
Sgc_Cpu cpu;
|
||||
|
||||
// large items
|
||||
struct header_t header;
|
||||
struct Rom_Data rom;
|
||||
byte vectors [page_size + page_padding];
|
||||
byte unmapped_write [0x4000];
|
||||
byte ram [0x2000 + page_padding];
|
||||
byte ram2 [0x4000 + page_padding];
|
||||
byte unmapped_write [0x4000];
|
||||
};
|
||||
|
||||
// Basic functionality (see Gme_File.h for file loading/track info functions)
|
||||
|
@ -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; }
|
||||
|
||||
// Set output sample rate. Must be called only once before loading file.
|
||||
blargg_err_t Sgc_set_sample_rate( struct Sgc_Emu* this, long sample_rate );
|
||||
blargg_err_t Sgc_set_sample_rate( struct Sgc_Emu* this, int sample_rate );
|
||||
|
||||
// Start a track, where 0 is the first track. Also clears warning string.
|
||||
blargg_err_t Sgc_start_track( struct Sgc_Emu* this, int track );
|
||||
|
||||
// Generate 'count' samples info 'buf'. Output is in stereo. Any emulation
|
||||
// errors set warning string, and major errors also end track.
|
||||
blargg_err_t Sgc_play( struct Sgc_Emu* this, long count, sample_t* buf );
|
||||
blargg_err_t Sgc_play( struct Sgc_Emu* this, int count, sample_t* buf );
|
||||
|
||||
// Track status/control
|
||||
|
||||
// Number of milliseconds (1000 msec = 1 second) played since beginning of track
|
||||
long Track_tell( struct Sgc_Emu* this );
|
||||
int Track_tell( struct Sgc_Emu* this );
|
||||
|
||||
// Seek to new time in track. Seeking backwards or far forward can take a while.
|
||||
blargg_err_t Track_seek( struct Sgc_Emu* this, long msec );
|
||||
blargg_err_t Track_seek( struct Sgc_Emu* this, int msec );
|
||||
|
||||
// Skip n samples
|
||||
blargg_err_t Track_skip( struct Sgc_Emu* this, long n );
|
||||
blargg_err_t Track_skip( struct Sgc_Emu* this, int n );
|
||||
|
||||
// Set start time and length of track fade out. Once fade ends track_ended() returns
|
||||
// true. Fade time can be changed while track is playing.
|
||||
void Track_set_fade( struct Sgc_Emu* this, long start_msec, long length_msec );
|
||||
void Track_set_fade( struct Sgc_Emu* this, int start_msec, int length_msec );
|
||||
|
||||
// True if a track has reached its end
|
||||
static inline bool Track_ended( struct Sgc_Emu* this )
|
||||
{
|
||||
return track_ended( &this->track_filter );
|
||||
}
|
||||
|
||||
// Disables automatic end-of-track detection and skipping of silence at beginning
|
||||
static inline void Track_ignore_silence( struct Sgc_Emu* this, bool disable )
|
||||
{
|
||||
this->track_filter.silence_ignored_ = disable;
|
||||
}
|
||||
|
||||
// Get track length in milliseconds
|
||||
static inline long Track_get_length( struct Sgc_Emu* this, int n )
|
||||
static inline int Track_get_length( struct Sgc_Emu* this, int n )
|
||||
{
|
||||
long length = 120 * 1000; /* 2 minutes */
|
||||
int length = 120 * 1000; /* 2 minutes */
|
||||
if ( (this->m3u.size > 0) && (n < this->m3u.size) ) {
|
||||
struct entry_t* entry = &this->m3u.entries [n];
|
||||
length = entry->length;
|
||||
|
|
|
@ -158,9 +158,7 @@ static void run_until( struct Sms_Apu* this, blip_time_t end_time )
|
|||
if ( delta )
|
||||
{
|
||||
osc->last_amp = amp;
|
||||
/* norm_synth.offset( last_time, delta, out ); */
|
||||
Synth_offset( &this->synth, this->last_time, delta, out );
|
||||
/* out->set_modified(); */
|
||||
Blip_set_modified( out );
|
||||
}
|
||||
}
|
||||
|
@ -200,7 +198,6 @@ static void run_until( struct Sms_Apu* this, blip_time_t end_time )
|
|||
do
|
||||
{
|
||||
delta = -delta;
|
||||
/* norm_synth.offset( time, delta, out ); */
|
||||
Synth_offset( &this->synth, time, delta, out );
|
||||
time += period;
|
||||
}
|
||||
|
@ -218,7 +215,6 @@ static void run_until( struct Sms_Apu* this, blip_time_t end_time )
|
|||
if ( changed & 2 ) // true if bits 0 and 1 differ
|
||||
{
|
||||
delta = -delta;
|
||||
/* fast_synth.offset_inline( time, delta, out ); */
|
||||
Synth_offset_inline( &this->synth, time, delta, out );
|
||||
}
|
||||
time += period;
|
||||
|
|
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 silence_max = 6; // seconds
|
||||
int const silence_threshold = 0x10;
|
||||
long const fade_block_size = 512;
|
||||
int const fade_shift = 8; // fade ends with gain at 1.0 / (1 << fade_shift)
|
||||
|
||||
// VGM commands (Spec v1.50)
|
||||
enum {
|
||||
cmd_gg_stereo = 0x4F,
|
||||
|
@ -53,15 +48,8 @@ enum {
|
|||
|
||||
static void clear_track_vars( struct Vgm_Emu* this )
|
||||
{
|
||||
this->out_time = 0;
|
||||
this->emu_time = 0;
|
||||
this->emu_track_ended_ = true;
|
||||
this->track_ended = true;
|
||||
this->fade_start = INT_MAX / 2 + 1;
|
||||
this->fade_step = 1;
|
||||
this->silence_time = 0;
|
||||
this->silence_count = 0;
|
||||
this->buf_remain = 0;
|
||||
this->current_track = -1;
|
||||
track_stop( &this->track_filter );
|
||||
}
|
||||
|
||||
int play_frame( struct Vgm_Emu* this, blip_time_t blip_time, int sample_count, sample_t* buf );
|
||||
|
@ -77,9 +65,10 @@ void Vgm_init( struct Vgm_Emu* this )
|
|||
this->tempo = (int)(FP_ONE_TEMPO);
|
||||
|
||||
// defaults
|
||||
this->max_initial_silence = 2;
|
||||
this->silence_lookahead = 1; // tracks should already be trimmed
|
||||
this->ignore_silence = false;
|
||||
this->tfilter = *track_get_setup( &this->track_filter );
|
||||
this->tfilter.max_initial = 2;
|
||||
this->tfilter.lookahead = 1;
|
||||
this->track_filter.silence_ignored_ = false;
|
||||
|
||||
// Disable oversampling by default
|
||||
this->disable_oversampling = true;
|
||||
|
@ -88,7 +77,7 @@ void Vgm_init( struct Vgm_Emu* this )
|
|||
Sms_apu_init( &this->psg );
|
||||
Synth_init( &this->pcm );
|
||||
|
||||
Buffer_init( &this->buf );
|
||||
Buffer_init( &this->stereo_buf );
|
||||
Blip_init( &this->blip_buf );
|
||||
|
||||
// Init fm chips
|
||||
|
@ -105,6 +94,7 @@ void Vgm_init( struct Vgm_Emu* this )
|
|||
|
||||
// Unload
|
||||
this->voice_count = 0;
|
||||
this->voice_types = 0;
|
||||
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;
|
||||
|
||||
static long check_gd3_header( byte const* h, long remain )
|
||||
static int check_gd3_header( byte const* h, int remain )
|
||||
{
|
||||
if ( remain < gd3_header_size ) return 0;
|
||||
if ( memcmp( h, "Gd3 ", 4 ) ) return 0;
|
||||
if ( get_le32( h + 4 ) >= 0x200 ) return 0;
|
||||
|
||||
long gd3_size = get_le32( h + 8 );
|
||||
int gd3_size = get_le32( h + 8 );
|
||||
if ( gd3_size > remain - gd3_header_size )
|
||||
gd3_size = remain - gd3_header_size;
|
||||
return gd3_size;
|
||||
|
@ -167,12 +157,12 @@ static byte const* gd3_data( struct Vgm_Emu* this, int* size )
|
|||
if ( size )
|
||||
*size = 0;
|
||||
|
||||
long gd3_offset = get_le32( header( this )->gd3_offset ) - 0x2C;
|
||||
int gd3_offset = get_le32( header( this )->gd3_offset ) - 0x2C;
|
||||
if ( gd3_offset < 0 )
|
||||
return 0;
|
||||
|
||||
byte const* gd3 = this->file_begin + header_size + gd3_offset;
|
||||
long gd3_size = check_gd3_header( gd3, this->file_end - gd3 );
|
||||
int gd3_size = check_gd3_header( gd3, this->file_end - gd3 );
|
||||
if ( !gd3_size )
|
||||
return 0;
|
||||
|
||||
|
@ -184,10 +174,10 @@ static byte const* gd3_data( struct Vgm_Emu* this, int* size )
|
|||
|
||||
static void get_vgm_length( struct header_t const* h, struct track_info_t* out )
|
||||
{
|
||||
long length = get_le32( h->track_duration ) * 10 / 441;
|
||||
int length = get_le32( h->track_duration ) * 10 / 441;
|
||||
if ( length > 0 )
|
||||
{
|
||||
long loop = get_le32( h->loop_duration );
|
||||
int loop = get_le32( h->loop_duration );
|
||||
if ( loop > 0 && get_le32( h->loop_offset ) )
|
||||
{
|
||||
out->loop_length = loop * 10 / 441;
|
||||
|
@ -285,24 +275,25 @@ blargg_err_t Vgm_load_mem( struct Vgm_Emu* this, byte const* new_data, long new_
|
|||
Ym2612_enable( &this->ym2612, false );
|
||||
Ym2413_enable( &this->ym2413, false );
|
||||
|
||||
Sound_set_tempo( this, (int)(FP_ONE_TEMPO) );
|
||||
|
||||
this->voice_count = sms_osc_count;
|
||||
static int const types [8] = {
|
||||
wave_type+1, wave_type+2, wave_type+3, noise_type+1,
|
||||
0, 0, 0, 0
|
||||
};
|
||||
this->voice_types = types;
|
||||
|
||||
RETURN_ERR( setup_fm( this ) );
|
||||
|
||||
// do after FM in case output buffer is changed
|
||||
// setup buffer
|
||||
this->clock_rate_ = this->psg_rate;
|
||||
Buffer_clock_rate( &this->buf, this->psg_rate );
|
||||
|
||||
// Setup bass
|
||||
this->buf_changed_count = Buffer_channels_changed_count( &this->buf );
|
||||
Buffer_clock_rate( &this->stereo_buf, this->psg_rate );
|
||||
RETURN_ERR( Buffer_set_channel_count( &this->stereo_buf, this->voice_count, this->voice_types ) );
|
||||
this->buf_changed_count = Buffer_channels_changed_count( &this->stereo_buf );
|
||||
|
||||
// Post load
|
||||
Sound_set_tempo( this, this->tempo );
|
||||
Sound_mute_voices( this, this->mute_mask_ );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -362,36 +353,50 @@ blargg_err_t setup_fm( struct Vgm_Emu* this )
|
|||
// Emulation
|
||||
|
||||
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 )
|
||||
{
|
||||
*time_io = run( this, msec * this->vgm_rate / 1000 );
|
||||
Sms_apu_end_frame( &this->psg, *time_io );
|
||||
check_end( this );
|
||||
*time_io = run_psg( this, msec );
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static blargg_err_t play_( struct Vgm_Emu* this, long count, sample_t* out )
|
||||
blargg_err_t play_( void *emu, int count, sample_t out [] )
|
||||
{
|
||||
struct Vgm_Emu* this = (struct Vgm_Emu*) emu;
|
||||
|
||||
if ( !uses_fm( this ) ) {
|
||||
long remain = count;
|
||||
int remain = count;
|
||||
while ( remain )
|
||||
{
|
||||
remain -= Buffer_read_samples( &this->buf, &out [count - remain], remain );
|
||||
Buffer_disable_immediate_removal( &this->stereo_buf );
|
||||
remain -= Buffer_read_samples( &this->stereo_buf, &out [count - remain], remain );
|
||||
if ( remain )
|
||||
{
|
||||
if ( this->buf_changed_count != Buffer_channels_changed_count( &this->buf ) )
|
||||
if ( this->buf_changed_count != Buffer_channels_changed_count( &this->stereo_buf ) )
|
||||
{
|
||||
this->buf_changed_count = Buffer_channels_changed_count( &this->buf );
|
||||
this->buf_changed_count = Buffer_channels_changed_count( &this->stereo_buf );
|
||||
|
||||
// Remute voices
|
||||
Sound_mute_voices( this, this->mute_mask_ );
|
||||
}
|
||||
int msec = Buffer_length( &this->buf );
|
||||
blip_time_t clocks_emulated = (blargg_long) msec * this->clock_rate_ / 1000 - 100;
|
||||
int msec = Buffer_length( &this->stereo_buf );
|
||||
blip_time_t clocks_emulated = msec * this->clock_rate_ / 1000 - 100;
|
||||
RETURN_ERR( run_clocks( this, &clocks_emulated, msec ) );
|
||||
assert( clocks_emulated );
|
||||
Buffer_end_frame( &this->buf, clocks_emulated );
|
||||
Buffer_end_frame( &this->stereo_buf, clocks_emulated );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -443,12 +448,10 @@ blip_time_t run( struct Vgm_Emu* this, vgm_time_t end_time )
|
|||
{
|
||||
vgm_time_t vgm_time = this->vgm_time;
|
||||
byte const* pos = this->pos;
|
||||
if ( pos >= this->file_end )
|
||||
/* if ( pos > this->file_end )
|
||||
{
|
||||
this->emu_track_ended_ = true;
|
||||
/* if ( pos > data_end )
|
||||
warning( "Stream lacked end event" ); */
|
||||
}
|
||||
warning( "Stream lacked end event" );
|
||||
} */
|
||||
|
||||
while ( vgm_time < end_time && pos < this->file_end )
|
||||
{
|
||||
|
@ -517,7 +520,7 @@ blip_time_t run( struct Vgm_Emu* this, vgm_time_t end_time )
|
|||
case cmd_data_block: {
|
||||
check( *pos == cmd_end );
|
||||
int type = pos [1];
|
||||
long size = get_le32( pos + 2 );
|
||||
int size = get_le32( pos + 2 );
|
||||
pos += 6;
|
||||
if ( type == pcm_block_type )
|
||||
this->pcm_data = pos;
|
||||
|
@ -564,6 +567,8 @@ blip_time_t run( struct Vgm_Emu* this, vgm_time_t end_time )
|
|||
|
||||
int play_frame( struct Vgm_Emu* this, blip_time_t blip_time, int sample_count, blip_sample_t out [] )
|
||||
{
|
||||
check_end( this);
|
||||
|
||||
// to do: timing is working mostly by luck
|
||||
int min_pairs = (unsigned) sample_count / 2;
|
||||
int vgm_time = (min_pairs << fm_time_bits) / this->fm_time_factor - 1;
|
||||
|
@ -642,16 +647,18 @@ void update_fm_rates( struct Vgm_Emu* this, int* ym2413_rate, int* ym2612_rate )
|
|||
|
||||
// Music Emu
|
||||
|
||||
blargg_err_t Vgm_set_sample_rate( struct Vgm_Emu* this, long rate )
|
||||
blargg_err_t Vgm_set_sample_rate( struct Vgm_Emu* this, int rate )
|
||||
{
|
||||
require( !this->sample_rate ); // sample rate can't be changed once set
|
||||
RETURN_ERR( Blip_set_sample_rate( &this->blip_buf, rate, 1000 / 30 ) );
|
||||
RETURN_ERR( Buffer_set_sample_rate( &this->buf, rate, 1000 / 20 ) );
|
||||
RETURN_ERR( Buffer_set_sample_rate( &this->stereo_buf, rate, 1000 / 20 ) );
|
||||
|
||||
// Set bass frequency
|
||||
Buffer_bass_freq( &this->buf, 80 );
|
||||
Buffer_bass_freq( &this->stereo_buf, 80 );
|
||||
|
||||
this->sample_rate = rate;
|
||||
RETURN_ERR( track_init( &this->track_filter, this ) );
|
||||
this->tfilter.max_silence = 6 * stereo * this->sample_rate;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -679,7 +686,7 @@ void Sound_mute_voices( struct Vgm_Emu* this, int mask )
|
|||
}
|
||||
else
|
||||
{
|
||||
struct channel_t ch = Buffer_channel( &this->buf );
|
||||
struct channel_t ch = Buffer_channel( &this->stereo_buf, i );
|
||||
assert( (ch.center && ch.left && ch.right) ||
|
||||
(!ch.center && !ch.left && !ch.right) ); // all or nothing
|
||||
set_voice( this, i, ch.center, ch.left, ch.right );
|
||||
|
@ -735,7 +742,6 @@ void Sound_set_tempo( struct Vgm_Emu* this, int t )
|
|||
}
|
||||
}
|
||||
|
||||
void fill_buf( struct Vgm_Emu *this );
|
||||
blargg_err_t Vgm_start_track( struct Vgm_Emu* this )
|
||||
{
|
||||
clear_track_vars( this );
|
||||
|
@ -750,7 +756,7 @@ blargg_err_t Vgm_start_track( struct Vgm_Emu* this )
|
|||
this->vgm_time = 0;
|
||||
if ( get_le32( header( this )->version ) >= 0x150 )
|
||||
{
|
||||
long data_offset = get_le32( header( this )->data_offset );
|
||||
int data_offset = get_le32( header( this )->data_offset );
|
||||
check( data_offset );
|
||||
if ( data_offset )
|
||||
this->pos += data_offset + offsetof (struct header_t,data_offset) - 0x40;
|
||||
|
@ -764,266 +770,88 @@ blargg_err_t Vgm_start_track( struct Vgm_Emu* this )
|
|||
if ( Ym2612_enabled( &this->ym2612 ) )
|
||||
Ym2612_reset( &this->ym2612 );
|
||||
|
||||
Blip_clear( &this->blip_buf, 1 );
|
||||
Blip_clear( &this->blip_buf );
|
||||
Resampler_clear( &this->resampler );
|
||||
}
|
||||
|
||||
this->fm_time_offset = 0;
|
||||
|
||||
Buffer_clear( &this->buf );
|
||||
Buffer_clear( &this->stereo_buf );
|
||||
|
||||
this->emu_track_ended_ = false;
|
||||
this->track_ended = false;
|
||||
// convert filter times to samples
|
||||
struct setup_t s = this->tfilter;
|
||||
s.max_initial *= this->sample_rate * stereo;
|
||||
#ifdef GME_DISABLE_SILENCE_LOOKAHEAD
|
||||
s.lookahead = 1;
|
||||
#endif
|
||||
track_setup( &this->track_filter, &s );
|
||||
|
||||
if ( !this->ignore_silence )
|
||||
{
|
||||
// play until non-silence or end of track
|
||||
long end;
|
||||
for ( end = this->max_initial_silence * stereo * this->sample_rate; this->emu_time < end; )
|
||||
{
|
||||
fill_buf( this );
|
||||
if ( this->buf_remain | (int) this->emu_track_ended_ )
|
||||
break;
|
||||
}
|
||||
|
||||
this->emu_time = this->buf_remain;
|
||||
this->out_time = 0;
|
||||
this->silence_time = 0;
|
||||
this->silence_count = 0;
|
||||
}
|
||||
/* return track_ended() ? warning() : 0; */
|
||||
return 0;
|
||||
return track_start( &this->track_filter );
|
||||
}
|
||||
|
||||
// Tell/Seek
|
||||
|
||||
static blargg_long msec_to_samples( blargg_long msec, long sample_rate )
|
||||
static int msec_to_samples( int msec, int sample_rate )
|
||||
{
|
||||
blargg_long sec = msec / 1000;
|
||||
int sec = msec / 1000;
|
||||
msec -= sec * 1000;
|
||||
return (sec * sample_rate + msec * sample_rate / 1000) * stereo;
|
||||
}
|
||||
|
||||
long Track_tell( struct Vgm_Emu* this )
|
||||
int Track_tell( struct Vgm_Emu* this )
|
||||
{
|
||||
blargg_long rate = this->sample_rate * stereo;
|
||||
blargg_long sec = this->out_time / rate;
|
||||
return sec * 1000 + (this->out_time - sec * rate) * 1000 / rate;
|
||||
int rate = this->sample_rate * stereo;
|
||||
int sec = track_sample_count( &this->track_filter ) / rate;
|
||||
return sec * 1000 + (track_sample_count( &this->track_filter ) - sec * rate) * 1000 / rate;
|
||||
}
|
||||
|
||||
blargg_err_t Track_seek( struct Vgm_Emu* this, long msec )
|
||||
blargg_err_t Track_seek( struct Vgm_Emu* this, int msec )
|
||||
{
|
||||
blargg_long time = msec_to_samples( msec, this->sample_rate );
|
||||
if ( time < this->out_time )
|
||||
RETURN_ERR( Vgm_start_track( this ) );
|
||||
return Track_skip( this, time - this->out_time );
|
||||
int time = msec_to_samples( msec, this->sample_rate );
|
||||
if ( time < track_sample_count( &this->track_filter ) )
|
||||
RETURN_ERR( Vgm_start_track( this ) );
|
||||
return Track_skip( this, time - track_sample_count( &this->track_filter ) );
|
||||
}
|
||||
|
||||
blargg_err_t skip_( struct Vgm_Emu* this, long count );
|
||||
blargg_err_t Track_skip( struct Vgm_Emu* this, long count )
|
||||
blargg_err_t Track_skip( struct Vgm_Emu* this, int count )
|
||||
{
|
||||
this->out_time += count;
|
||||
|
||||
// remove from silence and buf first
|
||||
{
|
||||
long n = min( count, this->silence_count );
|
||||
this->silence_count -= n;
|
||||
count -= n;
|
||||
|
||||
n = min( count, this->buf_remain );
|
||||
this->buf_remain -= n;
|
||||
count -= n;
|
||||
}
|
||||
|
||||
if ( count && !this->emu_track_ended_ )
|
||||
{
|
||||
this->emu_time += count;
|
||||
if ( skip_( this, count ) )
|
||||
this->emu_track_ended_ = true;
|
||||
}
|
||||
|
||||
if ( !(this->silence_count | this->buf_remain) ) // caught up to emulator, so update track ended
|
||||
this->track_ended |= this->emu_track_ended_;
|
||||
|
||||
return 0;
|
||||
require( this->current_track >= 0 ); // start_track() must have been called already
|
||||
return track_skip( &this->track_filter, count );
|
||||
}
|
||||
|
||||
blargg_err_t skip_( struct Vgm_Emu* this, long count )
|
||||
blargg_err_t skip_( void* emu, int count )
|
||||
{
|
||||
struct Vgm_Emu* this = (struct Vgm_Emu*) emu;
|
||||
|
||||
// for long skip, mute sound
|
||||
const long threshold = 30000;
|
||||
const int threshold = 32768;
|
||||
if ( count > threshold )
|
||||
{
|
||||
int saved_mute = this->mute_mask_;
|
||||
Sound_mute_voices( this, ~0 );
|
||||
|
||||
while ( count > threshold / 2 && !this->emu_track_ended_ )
|
||||
{
|
||||
RETURN_ERR( play_( this, buf_size, this->buf_ ) );
|
||||
count -= buf_size;
|
||||
}
|
||||
int n = count - threshold/2;
|
||||
n &= ~(2048-1); // round to multiple of 2048
|
||||
count -= n;
|
||||
RETURN_ERR( skippy_( &this->track_filter, n ) );
|
||||
|
||||
Sound_mute_voices( this, saved_mute );
|
||||
}
|
||||
|
||||
while ( count && !this->emu_track_ended_ )
|
||||
{
|
||||
long n = buf_size;
|
||||
if ( n > count )
|
||||
n = count;
|
||||
count -= n;
|
||||
RETURN_ERR( play_( this, n, this->buf_ ) );
|
||||
}
|
||||
return 0;
|
||||
return skippy_( &this->track_filter, count );
|
||||
}
|
||||
|
||||
// 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);
|
||||
this->fade_start = msec_to_samples( start_msec, this->sample_rate );
|
||||
track_set_fade( &this->track_filter, msec_to_samples( start_msec, this->sample_rate ),
|
||||
length_msec * this->sample_rate / (1000 / stereo) );
|
||||
}
|
||||
|
||||
// unit / pow( 2.0, (double) x / step )
|
||||
static int int_log( blargg_long x, int step, int unit )
|
||||
blargg_err_t Vgm_play( struct Vgm_Emu* this, int out_count, sample_t* out )
|
||||
{
|
||||
int shift = x / step;
|
||||
int fraction = (x - shift * step) * unit / step;
|
||||
return ((unit - fraction) + (fraction >> 1)) >> shift;
|
||||
}
|
||||
|
||||
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;
|
||||
require( this->current_track >= 0 );
|
||||
require( out_count % stereo == 0 );
|
||||
return track_play( &this->track_filter, out_count, out );
|
||||
}
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
// Sega Master System/Mark III, Sega Genesis/Mega Drive, BBC Micro VGM music file emulator
|
||||
|
||||
// Game_Music_Emu 0.5.5
|
||||
// Game_Music_Emu 0.6-pre
|
||||
#ifndef VGM_EMU_H
|
||||
#define VGM_EMU_H
|
||||
|
||||
#include "blargg_common.h"
|
||||
#include "blargg_source.h"
|
||||
|
||||
#include "track_filter.h"
|
||||
#include "resampler.h"
|
||||
#include "multi_buffer.h"
|
||||
#include "ym2413_emu.h"
|
||||
|
@ -17,7 +19,6 @@ typedef int fm_time_t;
|
|||
|
||||
enum { fm_time_bits = 12 };
|
||||
enum { blip_time_bits = 12 };
|
||||
enum { buf_size = 2048 };
|
||||
|
||||
// VGM header format
|
||||
enum { header_size = 0x40 };
|
||||
|
@ -46,9 +47,9 @@ enum { gme_max_field = 63 };
|
|||
struct track_info_t
|
||||
{
|
||||
/* times in milliseconds; -1 if unknown */
|
||||
long length;
|
||||
long intro_length;
|
||||
long loop_length;
|
||||
int length;
|
||||
int intro_length;
|
||||
int loop_length;
|
||||
|
||||
/* empty string if not available */
|
||||
char game [64];
|
||||
|
@ -63,11 +64,11 @@ struct track_info_t
|
|||
// YM2413 sound chip emulator. I can provide one I've modified to work with the library.
|
||||
struct Vgm_Emu {
|
||||
int fm_rate;
|
||||
long psg_rate;
|
||||
long vgm_rate;
|
||||
int psg_rate;
|
||||
int vgm_rate;
|
||||
bool disable_oversampling;
|
||||
|
||||
long fm_time_offset;
|
||||
int fm_time_offset;
|
||||
int fm_time_factor;
|
||||
|
||||
int blip_time_factor;
|
||||
|
@ -84,47 +85,34 @@ struct Vgm_Emu {
|
|||
int dac_amp;
|
||||
int dac_disabled; // -1 if disabled
|
||||
|
||||
struct Blip_Buffer blip_buf;
|
||||
|
||||
// general
|
||||
long clock_rate_;
|
||||
int current_track;
|
||||
int clock_rate_;
|
||||
unsigned buf_changed_count;
|
||||
int max_initial_silence;
|
||||
int voice_count;
|
||||
int const *voice_types;
|
||||
int mute_mask_;
|
||||
int tempo;
|
||||
int gain;
|
||||
|
||||
long sample_rate;
|
||||
|
||||
// track-specific
|
||||
blargg_long out_time; // number of samples played since start of track
|
||||
blargg_long emu_time; // number of samples emulator has generated since start of track
|
||||
bool emu_track_ended_; // emulator has reached end of track
|
||||
volatile bool track_ended;
|
||||
|
||||
// fading
|
||||
blargg_long fade_start;
|
||||
int fade_step;
|
||||
|
||||
// silence detection
|
||||
int silence_lookahead; // speed to run emulator when looking ahead for silence
|
||||
bool ignore_silence;
|
||||
long silence_time; // number of samples where most recent silence began
|
||||
long silence_count; // number of samples of silence to play before using buf
|
||||
long buf_remain; // number of samples left in silence buffer
|
||||
int sample_rate;
|
||||
|
||||
// larger items at the end
|
||||
struct track_info_t info;
|
||||
sample_t buf_ [buf_size];
|
||||
|
||||
struct setup_t tfilter;
|
||||
struct Track_Filter track_filter;
|
||||
|
||||
struct Ym2612_Emu ym2612;
|
||||
struct Ym2413_Emu ym2413;
|
||||
|
||||
struct Sms_Apu psg;
|
||||
struct Blip_Synth pcm;
|
||||
struct Blip_Buffer blip_buf;
|
||||
|
||||
struct Resampler resampler;
|
||||
struct Stereo_Buffer buf;
|
||||
struct Multi_Buffer stereo_buf;
|
||||
};
|
||||
|
||||
void Vgm_init( struct Vgm_Emu* this );
|
||||
|
@ -144,34 +132,46 @@ blargg_err_t Vgm_load_mem( struct Vgm_Emu* this, byte const* new_data, long new_
|
|||
static inline bool uses_fm( struct Vgm_Emu* this ) { return Ym2612_enabled( &this->ym2612 ) || Ym2413_enabled( &this->ym2413 ); }
|
||||
|
||||
// Set output sample rate. Must be called only once before loading file.
|
||||
blargg_err_t Vgm_set_sample_rate( struct Vgm_Emu* this, long sample_rate );
|
||||
blargg_err_t Vgm_set_sample_rate( struct Vgm_Emu* this, int sample_rate );
|
||||
|
||||
// Start a track, where 0 is the first track. Also clears warning string.
|
||||
blargg_err_t Vgm_start_track( struct Vgm_Emu* this );
|
||||
|
||||
// Generate 'count' samples info 'buf'. Output is in stereo. Any emulation
|
||||
// errors set warning string, and major errors also end track.
|
||||
blargg_err_t Vgm_play( struct Vgm_Emu* this, long count, sample_t* buf );
|
||||
blargg_err_t Vgm_play( struct Vgm_Emu* this, int count, sample_t* buf );
|
||||
|
||||
// Track status/control
|
||||
|
||||
// Number of milliseconds (1000 msec = 1 second) played since beginning of track
|
||||
long Track_tell( struct Vgm_Emu* this );
|
||||
int Track_tell( struct Vgm_Emu* this );
|
||||
|
||||
// Seek to new time in track. Seeking backwards or far forward can take a while.
|
||||
blargg_err_t Track_seek( struct Vgm_Emu* this, long msec );
|
||||
blargg_err_t Track_seek( struct Vgm_Emu* this, int msec );
|
||||
|
||||
// Skip n samples
|
||||
blargg_err_t Track_skip( struct Vgm_Emu* this, long n );
|
||||
blargg_err_t Track_skip( struct Vgm_Emu* this, int n );
|
||||
|
||||
// Set start time and length of track fade out. Once fade ends track_ended() returns
|
||||
// true. Fade time can be changed while track is playing.
|
||||
void Track_set_fade( struct Vgm_Emu* this, long start_msec, long length_msec );
|
||||
void Track_set_fade( struct Vgm_Emu* this, int start_msec, int length_msec );
|
||||
|
||||
// True if a track has reached its end
|
||||
static inline bool Track_ended( struct Vgm_Emu* this )
|
||||
{
|
||||
return track_ended( &this->track_filter );
|
||||
}
|
||||
|
||||
// Disables automatic end-of-track detection and skipping of silence at beginning
|
||||
static inline void Track_ignore_silence( struct Vgm_Emu* this, bool disable )
|
||||
{
|
||||
this->track_filter.silence_ignored_ = disable;
|
||||
}
|
||||
|
||||
// Get track length in milliseconds
|
||||
static inline long Track_get_length( struct Vgm_Emu* this )
|
||||
static inline int Track_get_length( struct Vgm_Emu* this )
|
||||
{
|
||||
long length = this->info.length;
|
||||
int length = this->info.length;
|
||||
if ( length <= 0 )
|
||||
{
|
||||
length = this->info.intro_length + 2 * this->info.loop_length; // intro + 2 loops
|
||||
|
@ -203,5 +203,4 @@ static inline void Sound_set_gain( struct Vgm_Emu* this, int g )
|
|||
this->gain = g;
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
|
||||
#include "ym2612_emu.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
|
|
|
@ -74,8 +74,8 @@ enum codec_status codec_run(void)
|
|||
return CODEC_ERROR;
|
||||
}
|
||||
|
||||
if ((err = Nsf_load(&nsf_emu, buf, ci->filesize))) {
|
||||
DEBUGF("NSF: Nsf_load failed (%s)\n", err);
|
||||
if ((err = Nsf_load_mem(&nsf_emu, buf, ci->filesize))) {
|
||||
DEBUGF("NSF: Nsf_load_mem failed (%s)\n", err);
|
||||
return CODEC_ERROR;
|
||||
}
|
||||
|
||||
|
@ -116,7 +116,7 @@ next_track:
|
|||
|
||||
/* Generate audio buffer */
|
||||
err = Nsf_play(&nsf_emu, CHUNK_SIZE, samples);
|
||||
if (err || nsf_emu.track_ended) {
|
||||
if (err || Track_ended(&nsf_emu)) {
|
||||
track++;
|
||||
if (track >= nsf_emu.track_count) break;
|
||||
goto next_track;
|
||||
|
|
|
@ -83,7 +83,7 @@ enum codec_status codec_run(void)
|
|||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -110,7 +110,7 @@ next_track:
|
|||
|
||||
/* Generate audio buffer */
|
||||
err = Sgc_play(&sgc_emu, CHUNK_SIZE, samples);
|
||||
if (err || sgc_emu.track_ended) {
|
||||
if (err || Track_ended(&sgc_emu)) {
|
||||
track++;
|
||||
if (track >= sgc_emu.track_count) break;
|
||||
goto next_track;
|
||||
|
|
|
@ -130,7 +130,7 @@ enum codec_status codec_run(void)
|
|||
|
||||
/* Generate audio buffer */
|
||||
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);
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue