1
0
Fork 0
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:
Andree Buschmann 2011-08-31 19:19:49 +00:00
parent d089e10403
commit 13cbade08a
68 changed files with 4113 additions and 4893 deletions

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -3,4 +3,5 @@ ay_cpu.c
ay_emu.c
blip_buffer.c
multi_buffer.c
track_filter.c
z80_cpu.c

View file

@ -6,3 +6,4 @@ gbs_emu.c
blip_buffer.c
multi_buffer.c
rom_data.c
track_filter.c

View file

@ -5,3 +5,4 @@ hes_emu.c
blip_buffer.c
multi_buffer.c
rom_data.c
track_filter.c

View file

@ -9,3 +9,4 @@ multi_buffer.c
rom_data.c
emu8950.c
emuadpcm.c
track_filter.c

View file

@ -12,3 +12,4 @@ nsfe_info.c
blip_buffer.c
multi_buffer.c
rom_data.c
track_filter.c

View file

@ -4,3 +4,4 @@ z80_cpu.c
blip_buffer.c
multi_buffer.c
rom_data.c
track_filter.c

View file

@ -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

View file

@ -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 );
}
int Track_get_length( struct Ay_Emu* this, int n )
{
int 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;
}
// 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 ( length <= 0 )
length = 120 * 1000; /* 2 minutes */
return length;
}
void Track_set_fade( struct Ay_Emu *this, int start_msec, int length_msec )
{
track_set_fade( &this->track_filter, msec_to_samples( start_msec, this->sample_rate ),
length_msec * this->sample_rate / (1000 / stereo) );
}
blargg_err_t Ay_play( struct Ay_Emu *this, int out_count, sample_t* out )
{
require( this->current_track >= 0 );
require( out_count % stereo == 0 );
return track_play( &this->track_filter, out_count, out );
}
blargg_err_t play_( void *emu, int count, sample_t* out )
{
struct Ay_Emu* this = (struct Ay_Emu*) emu;
if ( !(this->silence_count | this->buf_remain) ) // caught up to emulator, so update track ended
this->track_ended |= this->emu_track_ended_;
return 0;
}
// Fading
void Track_set_fade( struct Ay_Emu *this, long start_msec, long length_msec )
{
this->fade_step = this->sample_rate * length_msec / (fade_block_size * fade_shift * 1000 / stereo);
this->fade_start = msec_to_samples( start_msec, this->sample_rate );
}
// unit / pow( 2.0, (double) x / step )
static int int_log( blargg_long x, int step, int unit )
{
int shift = x / step;
int fraction = (x - shift * step) * unit / step;
return ((unit - fraction) + (fraction >> 1)) >> shift;
}
static void handle_fade( struct Ay_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 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 );

View file

@ -10,14 +10,12 @@
#include "z80_cpu.h"
#include "ay_apu.h"
#include "m3u_playlist.h"
typedef short sample_t;
#include "track_filter.h"
// 64K memory to load code and data into before starting track. Caller
// must parse the AY file.
enum { mem_size = 0x10000 };
enum { ram_addr = 0x4000 }; // where official RAM starts
enum { buf_size = 2048 };
// AY file header
enum { header_size = 0x14 };
@ -62,43 +60,30 @@ struct Ay_Emu {
bool cpc_mode;
// general
int max_initial_silence;
int voice_count;
int const* voice_types;
int mute_mask_;
int tempo;
int gain;
long sample_rate;
int sample_rate;
// track-specific
int current_track;
int track_count;
blargg_long out_time; // number of samples played since start of track
blargg_long emu_time; // number of samples emulator has generated since start of track
volatile bool track_ended;
bool emu_track_ended_; // emulator has reached end of track
// fading
blargg_long fade_start;
int fade_step;
// silence detection
bool ignore_silence;
int silence_lookahead; // speed to run emulator when looking ahead for silence
long silence_time; // number of samples where most recent silence began
long silence_count; // number of samples of silence to play before using buf
long buf_remain; // number of samples left in silence buffer
long clock_rate_;
int clock_rate_;
unsigned buf_changed_count;
// M3u Playlist
struct M3u_Playlist m3u;
// large items
struct setup_t tfilter;
struct Track_Filter track_filter;
struct Ay_Apu apu;
sample_t buf [buf_size];
struct Stereo_Buffer stereo_buf; // NULL if using custom buffer
struct Multi_Buffer stereo_buf; // NULL if using custom buffer
struct Z80_Cpu cpu;
struct mem_t mem;
};
@ -106,46 +91,58 @@ struct Ay_Emu {
// Basic functionality (see Gme_File.h for file loading/track info functions)
void Ay_init( struct Ay_Emu* this );
blargg_err_t Ay_load_mem( struct Ay_Emu* this, byte const in [], int size );
blargg_err_t Ay_load_mem( struct Ay_Emu* this, byte const in [], long size );
// Set output sample rate. Must be called only once before loading file.
blargg_err_t Ay_set_sample_rate( struct Ay_Emu* this, long sample_rate );
blargg_err_t Ay_set_sample_rate( struct Ay_Emu* this, int sample_rate );
// Start a track, where 0 is the first track. Also clears warning string.
blargg_err_t Ay_start_track( struct Ay_Emu* this, int track );
// Generate 'count' samples info 'buf'. Output is in stereo. Any emulation
// errors set warning string, and major errors also end track.
blargg_err_t Ay_play( struct Ay_Emu* this, long count, sample_t* buf );
blargg_err_t Ay_play( struct Ay_Emu* this, int count, sample_t* buf );
// Track status/control
// Number of milliseconds (1000 msec = 1 second) played since beginning of track
long Track_tell( struct Ay_Emu* this );
int Track_tell( struct Ay_Emu* this );
// Seek to new time in track. Seeking backwards or far forward can take a while.
blargg_err_t Track_seek( struct Ay_Emu* this, long msec );
blargg_err_t Track_seek( struct Ay_Emu* this, int msec );
// Skip n samples
blargg_err_t Track_skip( struct Ay_Emu* this, long n );
blargg_err_t Track_skip( struct Ay_Emu* this, int n );
// Set start time and length of track fade out. Once fade ends track_ended() returns
// true. Fade time can be changed while track is playing.
void Track_set_fade( struct Ay_Emu* this, long start_msec, long length_msec );
void Track_set_fade( struct Ay_Emu* this, int start_msec, int length_msec );
// True if a track has reached its end
static inline bool Track_ended( struct Ay_Emu* this )
{
return track_ended( &this->track_filter );
}
// Disables automatic end-of-track detection and skipping of silence at beginning
static inline void Track_ignore_silence( struct Ay_Emu* this, bool disable )
{
this->track_filter.silence_ignored_ = disable;
}
// Get track length in milliseconds
long Track_get_length( struct Ay_Emu* this, int n );
int Track_get_length( struct Ay_Emu* this, int n );
// Sound customization
// Adjust song tempo, where 1.0 = normal, 0.5 = half speed, 2.0 = double speed.
// Track length as returned by track_info() assumes a tempo of 1.0.
void Sound_set_tempo( struct Ay_Emu* this, int t );
// Mute/unmute voice i, where voice 0 is first voice
void Sound_mute_voice( struct Ay_Emu* this, int index, bool mute );
// Set muting state of all voices at once using a bit mask, where -1 mutes them all,
// 0 unmutes them all, 0x01 mutes just the first voice, etc.
void Sound_mute_voices( struct Ay_Emu* this, int mask );
@ -168,5 +165,5 @@ static inline void disable_beeper( struct Ay_Emu *this )
this->beeper_mask = 0;
this->last_beeper = 0;
}
#endif

View file

@ -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.

View file

@ -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

View file

@ -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

View file

@ -2,7 +2,6 @@
#include "blip_buffer.h"
#include <assert.h>
#include <limits.h>
#include <string.h>
#include <stdlib.h>
@ -19,96 +18,78 @@ details. You should have received a copy of the GNU Lesser General Public
License along with this module; if not, write to the Free Software Foundation,
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
#ifdef BLARGG_ENABLE_OPTIMIZER
#include BLARGG_ENABLE_OPTIMIZER
#endif
int const silent_buf_size = 1; // size used for Silent_Blip_Buffer
#include "blargg_source.h"
void Blip_init( struct Blip_Buffer* this )
{
this->factor_ = (blip_ulong)LONG_MAX;
this->offset_ = 0;
this->buffer_size_ = 0;
this->sample_rate_ = 0;
this->reader_accum_ = 0;
this->bass_shift_ = 0;
this->clock_rate_ = 0;
this->bass_freq_ = 16;
this->length_ = 0;
this->factor_ = UINT_MAX/2 + 1;;
this->buffer_center_ = NULL;
this->buffer_size_ = 0;
this->sample_rate_ = 0;
this->bass_shift_ = 0;
this->clock_rate_ = 0;
this->bass_freq_ = 16;
this->length_ = 0;
// assumptions code makes about implementation-defined features
#ifndef NDEBUG
// right shift of negative value preserves sign
buf_t_ i = -0x7FFFFFFE;
assert( (i >> 1) == -0x3FFFFFFF );
// casting to short truncates to 16 bits and sign-extends
i = 0x18000;
assert( (short) i == -0x8000 );
#endif
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)";
}
// start with maximum length that resampled time can represent
long new_size = (ULONG_MAX >> BLIP_BUFFER_ACCURACY) - blip_buffer_extra_ - 64;
if ( msec != blip_max_length )
{
long s = (new_rate * (msec + 1) + 999) / 1000;
if ( s < new_size )
new_size = s;
else
assert( 0 ); // fails if requested buffer length exceeds limit
}
if ( new_size > blip_buffer_max )
return "Out of memory";
this->buffer_size_ = new_size;
assert( this->buffer_size_ != silent_buf_size );
// Limit to maximum size that resampled time can represent
int max_size = (((blip_resampled_time_t) -1) >> BLIP_BUFFER_ACCURACY) -
blip_buffer_extra_ - 64; // TODO: -64 isn't needed
int new_size = (new_rate * (msec + 1) + 999) / 1000;
if ( new_size > max_size )
new_size = max_size;
// Resize buffer
if ( this->buffer_size_ != new_size ) {
this->buffer_center_ = this->buffer_ + BLIP_MAX_QUALITY/2;
this->buffer_size_ = new_size;
}
// update things based on the sample rate
this->sample_rate_ = new_rate;
this->length_ = new_size * 1000 / new_rate - 1;
if ( msec )
assert( this->length_ == msec ); // ensure length is same as that passed in
this->length_ = new_size * 1000 / new_rate - 1;
if ( this->clock_rate_ )
Blip_set_clock_rate( this, this->clock_rate_ );
Blip_bass_freq( this, this->bass_freq_ );
Blip_clear( this, 1 );
Blip_clear( this );
return 0; // success
}
blip_resampled_time_t Blip_clock_rate_factor( struct Blip_Buffer* this, long rate )
blip_resampled_time_t Blip_clock_rate_factor( struct Blip_Buffer* this, int rate )
{
blip_long factor = (blip_long) ( this->sample_rate_ * (1LL << BLIP_BUFFER_ACCURACY) / rate);
int factor = (int) ( this->sample_rate_ * (1LL << BLIP_BUFFER_ACCURACY) / rate);
assert( factor > 0 || !this->sample_rate_ ); // fails if clock/output ratio is too large
return (blip_resampled_time_t) factor;
}
@ -117,119 +98,111 @@ void Blip_bass_freq( struct Blip_Buffer* this, int freq )
{
this->bass_freq_ = freq;
int shift = 31;
if ( freq > 0 )
if ( freq > 0 && this->sample_rate_ )
{
shift = 13;
long f = (freq << 16) / this->sample_rate_;
int f = (freq << 16) / this->sample_rate_;
while ( (f >>= 1) && --shift ) { }
}
this->bass_shift_ = shift;
this->bass_shift_ = shift;
}
void Blip_end_frame( struct Blip_Buffer* this, blip_time_t t )
{
this->offset_ += t * this->factor_;
assert( Blip_samples_avail( this ) <= (long) this->buffer_size_ ); // time outside buffer length
assert( Blip_samples_avail( this ) <= (int) this->buffer_size_ ); // time outside buffer length
}
void Blip_remove_silence( struct Blip_Buffer* this, long count )
int Blip_count_samples( struct Blip_Buffer* this, blip_time_t t )
{
assert( count <= Blip_samples_avail( this ) ); // tried to remove more samples than available
this->offset_ -= (blip_resampled_time_t) count << BLIP_BUFFER_ACCURACY;
blip_resampled_time_t last_sample = Blip_resampled_time( this, t ) >> BLIP_BUFFER_ACCURACY;
blip_resampled_time_t first_sample = this->offset_ >> BLIP_BUFFER_ACCURACY;
return (int) (last_sample - first_sample);
}
long Blip_count_samples( struct Blip_Buffer* this, blip_time_t t )
blip_time_t Blip_count_clocks( struct Blip_Buffer* this, int count )
{
unsigned long last_sample = Blip_resampled_time( this, t ) >> BLIP_BUFFER_ACCURACY;
unsigned long first_sample = this->offset_ >> BLIP_BUFFER_ACCURACY;
return (long) (last_sample - first_sample);
}
blip_time_t Blip_count_clocks( struct Blip_Buffer* this, long count )
{
if ( !this->factor_ )
{
assert( 0 ); // sample rate and clock rates must be set first
return 0;
}
if ( count > this->buffer_size_ )
count = this->buffer_size_;
blip_resampled_time_t time = (blip_resampled_time_t) count << BLIP_BUFFER_ACCURACY;
return (blip_time_t) ((time - this->offset_ + this->factor_ - 1) / this->factor_);
}
void Blip_remove_samples( struct Blip_Buffer* this, long count )
void Blip_remove_samples( struct Blip_Buffer* this, int count )
{
if ( count )
{
Blip_remove_silence( this, count );
// copy remaining samples to beginning and clear old samples
long remain = Blip_samples_avail( this ) + blip_buffer_extra_;
int remain = Blip_samples_avail( this ) + blip_buffer_extra_;
memmove( this->buffer_, this->buffer_ + count, remain * sizeof *this->buffer_ );
memset( this->buffer_ + remain, 0, count * sizeof *this->buffer_ );
}
}
long Blip_read_samples( struct Blip_Buffer* this, blip_sample_t* BLIP_RESTRICT out, long max_samples, int stereo )
int Blip_read_samples( struct Blip_Buffer* this, blip_sample_t out_ [], int max_samples, bool stereo )
{
long count = Blip_samples_avail( this );
int count = Blip_samples_avail( this );
if ( count > max_samples )
count = max_samples;
if ( count )
{
int const bass = BLIP_READER_BASS( *this );
BLIP_READER_BEGIN( reader, *this );
int const bass = this->bass_shift_;
delta_t const* reader = this->buffer_ + count;
int reader_sum = this->reader_accum_;
blip_sample_t* BLARGG_RESTRICT out = out_ + count;
if ( stereo )
out += count;
int offset = -count;
if ( !stereo )
{
blip_long n;
for ( n = count; n; --n )
do
{
blip_long s = BLIP_READER_READ( reader );
if ( (blip_sample_t) s != s )
s = 0x7FFF - (s >> 24);
*out++ = (blip_sample_t) s;
BLIP_READER_NEXT( reader, bass );
int s = reader_sum >> delta_bits;
reader_sum -= reader_sum >> bass;
reader_sum += reader [offset];
BLIP_CLAMP( s, s );
out [offset] = (blip_sample_t) s;
}
while ( ++offset );
}
else
{
blip_long n;
for ( n = count; n; --n )
do
{
blip_long s = BLIP_READER_READ( reader );
if ( (blip_sample_t) s != s )
s = 0x7FFF - (s >> 24);
*out = (blip_sample_t) s;
out += 2;
BLIP_READER_NEXT( reader, bass );
int s = reader_sum >> delta_bits;
reader_sum -= reader_sum >> bass;
reader_sum += reader [offset];
BLIP_CLAMP( s, s );
out [offset * 2] = (blip_sample_t) s;
}
while ( ++offset );
}
BLIP_READER_END( reader, *this );
this->reader_accum_ = reader_sum;
Blip_remove_samples( this, count );
}
return count;
}
void Blip_mix_samples( struct Blip_Buffer* this, blip_sample_t const* in, long count )
void Blip_mix_samples( struct Blip_Buffer* this, blip_sample_t const in [], int count )
{
if ( this->buffer_size_ == silent_buf_size )
{
assert( 0 );
return;
}
buf_t_* out = this->buffer_ + (this->offset_ >> BLIP_BUFFER_ACCURACY) + blip_widest_impulse_ / 2;
delta_t* out = this->buffer_center_ + (this->offset_ >> BLIP_BUFFER_ACCURACY);
int const sample_shift = blip_sample_bits - 16;
int prev = 0;
while ( count-- )
while ( --count >= 0 )
{
blip_long s = (blip_long) *in++ << sample_shift;
int s = *in++ << sample_shift;
*out += s - prev;
prev = s;
++out;
@ -237,40 +210,16 @@ void Blip_mix_samples( struct Blip_Buffer* this, blip_sample_t const* in, long
*out -= prev;
}
void Blip_set_modified( struct Blip_Buffer* this )
{
this->modified_ = 1;
}
int Blip_clear_modified( struct Blip_Buffer* this )
{
int b = this->modified_;
this->modified_ = 0;
return b;
}
blip_resampled_time_t Blip_resampled_duration( struct Blip_Buffer* this, int t )
{
return t * this->factor_;
}
blip_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);
}

View file

@ -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 )
{
return blip_widest_impulse_ / 2;
// 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 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 )
{
return (long) (this->offset_ >> BLIP_BUFFER_ACCURACY);
// Converts clock time since beginning of current time frame to resampled time
static inline blip_resampled_time_t Blip_resampled_time( struct Blip_Buffer* this, blip_time_t t )
{
return t * this->factor_ + this->offset_;
}
// 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))
@ -254,7 +295,7 @@ static inline void Synth_offset_inline( struct Blip_Synth* this, blip_time_t t,
// using Blip_remove_samples().
#define BLIP_READER_END( name, blip_buffer ) \
(void) ((blip_buffer).reader_accum_ = name##_reader_accum)
#define BLIP_READER_ADJ_( name, offset ) (name##_reader_buf += offset)
#define BLIP_READER_NEXT_IDX_( name, bass, idx ) {\
@ -262,18 +303,33 @@ static inline void Synth_offset_inline( struct Blip_Synth* this, blip_time_t t,
name##_reader_accum += name##_reader_buf [(idx)];\
}
#define BLIP_READER_NEXT_RAW_IDX_( name, bass, idx ) {\
name##_reader_accum -= name##_reader_accum >> (bass);\
name##_reader_accum +=\
*(delta_t const*) ((char const*) name##_reader_buf + (idx));\
}
//// BLIP_CLAMP
#if defined (_M_IX86) || defined (_M_IA64) || defined (__i486__) || \
defined (__x86_64__) || defined (__ia64__) || defined (__i386__)
#define BLIP_X86 1
#define BLIP_CLAMP_( in ) in < -0x8000 || 0x7FFF < in
#if ARM_ARCH >= 6
#define BLIP_CLAMP( sample, out ) \
({ \
asm ("ssat %0, #16, %1" \
: "=r" ( out ) : "r"( sample ) ); \
out; \
})
#else
#define BLIP_CLAMP_( in ) (blip_sample_t) in != in
#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

View file

@ -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"

View file

@ -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 );
}

View file

@ -9,15 +9,12 @@
#include "gb_apu.h"
#include "gb_cpu.h"
#include "m3u_playlist.h"
/* typedef uint8_t byte; */
typedef short sample_t;
#include "track_filter.h"
enum { joypad_addr = 0xFF00 };
enum { ram_addr = 0xA000 };
enum { hi_page = 0xFF00 - ram_addr };
enum { io_base = 0xFF00 };
enum { buf_size = 2048 };
// Selects which sound hardware to use. AGB hardware is cleaner than the
// others. Doesn't take effect until next start_track().
@ -59,36 +56,19 @@ struct Gbs_Emu {
blip_time_t next_play;
// Sound
long clock_rate_;
long sample_rate_;
int clock_rate_;
int sample_rate_;
unsigned buf_changed_count;
int voice_count_;
int const* voice_types_;
int mute_mask_;
int gain_;
int tempo_;
// track-specific
byte track_count;
volatile bool track_ended;
int current_track_;
blargg_long out_time; // number of samples played since start of track
blargg_long emu_time; // number of samples emulator has generated since start of track
bool emu_track_ended_; // emulator has reached end of track
// fading
blargg_long fade_start;
int fade_step;
// silence detection
// Disable automatic end-of-track detection and skipping of silence at beginning
bool ignore_silence;
int max_initial_silence;
int mute_mask_;
int silence_lookahead; // speed to run emulator when looking ahead for silence
long silence_time; // number of samples where most recent silence began
long silence_count; // number of samples of silence to play before using buf
long buf_remain; // number of samples left in silence buffer
// Larger items at the end
// Header for currently loaded file
struct header_t header;
@ -96,11 +76,12 @@ struct Gbs_Emu {
// M3u Playlist
struct M3u_Playlist m3u;
struct setup_t tfilter;
struct Track_Filter track_filter;
struct Gb_Apu apu;
struct Gb_Cpu cpu;
struct Stereo_Buffer stereo_buf;
sample_t buf [buf_size];
struct Multi_Buffer stereo_buf;
// rom & ram
struct Rom_Data rom;
@ -116,36 +97,48 @@ void Gbs_init( struct Gbs_Emu* this );
void Gbs_stop( struct Gbs_Emu* this );
// Loads a file from memory
blargg_err_t Gbs_load( struct Gbs_Emu* this, void* data, long size );
blargg_err_t Gbs_load_mem( struct Gbs_Emu* this, void* data, long size );
// Set output sample rate. Must be called only once before loading file.
blargg_err_t Gbs_set_sample_rate( struct Gbs_Emu* this, long sample_rate );
blargg_err_t Gbs_set_sample_rate( struct Gbs_Emu* this, int sample_rate );
// Start a track, where 0 is the first track. Also clears warning string.
blargg_err_t Gbs_start_track( struct Gbs_Emu* this, int );
// Generate 'count' samples info 'buf'. Output is in stereo. Any emulation
// errors set warning string, and major errors also end track.
blargg_err_t Gbs_play( struct Gbs_Emu* this, long count, sample_t* buf );
blargg_err_t Gbs_play( struct Gbs_Emu* this, int count, sample_t* buf );
// Track status/control
// Number of milliseconds (1000 msec = 1 second) played since beginning of track
long Track_tell( struct Gbs_Emu* this );
int Track_tell( struct Gbs_Emu* this );
// Seek to new time in track. Seeking backwards or far forward can take a while.
blargg_err_t Track_seek( struct Gbs_Emu* this, long msec );
blargg_err_t Track_seek( struct Gbs_Emu* this, int msec );
// Skip n samples
blargg_err_t Track_skip( struct Gbs_Emu* this, long n );
blargg_err_t Track_skip( struct Gbs_Emu* this, int n );
// Set start time and length of track fade out. Once fade ends track_ended() returns
// true. Fade time can be changed while track is playing.
void Track_set_fade( struct Gbs_Emu* this, long start_msec, long length_msec );
void Track_set_fade( struct Gbs_Emu* this, int start_msec, int length_msec );
// True if a track has reached its end
static inline bool Track_ended( struct Gbs_Emu* this )
{
return track_ended( &this->track_filter );
}
// Disables automatic end-of-track detection and skipping of silence at beginning
static inline void Track_ignore_silence( struct Gbs_Emu* this, bool disable )
{
this->track_filter.silence_ignored_ = disable;
}
// Get track length in milliseconds
static inline long Track_get_length( struct Gbs_Emu* this, int n )
static inline int Track_get_length( struct Gbs_Emu* this, int n )
{
long length = 120 * 1000; /* 2 minutes */
int length = 120 * 1000; /* 2 minutes */
if ( (this->m3u.size > 0) && (n < this->m3u.size) ) {
struct entry_t* entry = &this->m3u.entries [n];
length = entry->length;
@ -175,19 +168,18 @@ static inline void Sound_set_gain( struct Gbs_Emu* this, int g )
this->gain_ = g;
}
// Emulation (You shouldn't touch these)
blargg_err_t Run_clocks( struct Gbs_Emu* this, blip_time_t duration );
void Set_bank( struct Gbs_Emu* this, int );
void Update_timer( struct Gbs_Emu* this );
blargg_err_t run_clocks( struct Gbs_Emu* this, blip_time_t duration );
void set_bank( struct Gbs_Emu* this, int );
void update_timer( struct Gbs_Emu* this );
// Runs CPU until time becomes >= 0
void Run_cpu( struct Gbs_Emu* this );
void run_cpu( struct Gbs_Emu* this );
// Reads/writes memory and I/O
int Read_mem( struct Gbs_Emu* this, addr_t addr );
void Write_mem( struct Gbs_Emu* this, addr_t addr, int data );
int read_mem( struct Gbs_Emu* this, addr_t addr );
void write_mem( struct Gbs_Emu* this, addr_t addr, int data );
// Current time
static inline blip_time_t Time( struct Gbs_Emu* this )
@ -195,6 +187,6 @@ static inline blip_time_t Time( struct Gbs_Emu* this )
return Cpu_time( &this->cpu ) + this->end_time;
}
void Jsr_then_stop( struct Gbs_Emu* this, byte const [] );
void jsr_then_stop( struct Gbs_Emu* this, byte const [] );
#endif

View file

@ -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 */

View file

@ -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 );
}
void Apu_osc_output( struct Hes_Apu* this, int index, 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;
struct Hes_Osc* osc = &this->oscs [osc_count];
do
{
osc--;
Apu_balance_changed( this, osc );
}
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 Osc_run_until( struct Hes_Osc* this, struct Blip_Synth* synth_, blip_time_t end_time )
void Apu_osc_output( struct Hes_Apu* this, int i, struct Blip_Buffer* center, struct Blip_Buffer* left, struct Blip_Buffer* right )
{
struct Blip_Buffer* const osc_outputs_0 = this->outputs [0]; // cache often-used values
if ( osc_outputs_0 && this->control & 0x80 )
// 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
if ( !center || !left || !right )
{
int dac = this->dac;
int const volume_0 = this->volume [0];
left = center;
right = center;
}
struct Hes_Osc* o = &this->oscs [i];
o->outputs [0] = center;
o->outputs [1] = right;
o->outputs [2] = left;
balance_changed( this, o );
}
void run_osc( struct Hes_Osc* o, struct Blip_Synth* syn, blip_time_t end_time )
{
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 )
{
// 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;
}
// Generate noise
int noise = 0;
if ( o->lfsr )
{
noise = o->noise & 0x80;
blip_time_t time = this->last_time + this->delay;
blip_time_t time = o->last_time + o->noise_delay;
if ( time < end_time )
{
if ( this->noise & 0x80 )
int period = (~o->noise & 0x1F) * 128;
if ( !period )
period = 64;
if ( noise && out0 )
{
if ( volume_0 | volume_1 )
unsigned lfsr = o->lfsr;
do
{
// noise
int const period = (32 - (this->noise & 0x1F)) * 64; // TODO: correct?
unsigned noise_lfsr = this->noise_lfsr;
do
{
int new_dac = 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 new_dac = -(lfsr & 1);
lfsr = (lfsr >> 1) ^ (0x30061 & new_dac);
this->noise_lfsr = noise_lfsr;
assert( noise_lfsr );
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
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
// Maintain phase when silent
int count = (end_time - time + period - 1) / period;
time += count * period;
// not worth it
//while ( count-- )
// o->lfsr = (o->lfsr >> 1) ^ (0x30061 * (o->lfsr & 1));
}
}
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;
}

View file

@ -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 ); }

View file

@ -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

View file

@ -1,56 +1,52 @@
// PC Engine CPU emulator for use with HES music files
// Game_Music_Emu 0.5.2
// Game_Music_Emu 0.6-pre
#ifndef HES_CPU_H
#define HES_CPU_H
#include "blargg_common.h"
#include "blargg_source.h"
typedef blargg_long hes_time_t; // clock cycle count
typedef unsigned hes_addr_t; // 16-bit address
typedef int hes_time_t; // clock cycle count
typedef int hes_addr_t; // 16-bit address
struct Hes_Emu;
enum { future_hes_time = LONG_MAX / 2 + 1 };
enum { page_size = 0x2000 };
enum { page_shift = 13 };
enum { page_count = 8 };
enum { future_time = INT_MAX/2 + 1 };
enum { page_bits = 13 };
enum { page_size = 1 << page_bits };
enum { page_count = 0x10000 / page_size };
// Attempt to execute instruction here results in CPU advancing time to
// lesser of irq_time() and end_time() (or end_time() if IRQs are
// disabled)
enum { idle_addr = 0x1FFF };
// Can read this many bytes past end of a page
enum { cpu_padding = 8 };
enum { irq_inhibit = 0x04 };
enum { irq_inhibit_mask = 0x04 };
enum { idle_addr = 0x1FFF };
// Cpu state
struct state_t {
uint8_t const* code_map [page_count + 1];
struct cpu_state_t {
byte const* code_map [page_count + 1];
hes_time_t base;
blargg_long time;
int time;
};
// Cpu registers
// NOT kept updated during emulation.
struct registers_t {
uint16_t pc;
uint8_t a;
uint8_t x;
uint8_t y;
uint8_t status;
uint8_t sp;
byte a;
byte x;
byte y;
byte flags;
byte sp;
};
struct Hes_Cpu {
struct registers_t r;
hes_time_t irq_time;
hes_time_t end_time;
hes_time_t irq_time_;
hes_time_t end_time_;
struct state_t* state; // points to state_ or a local copy within run()
struct state_t state_;
struct cpu_state_t* cpu_state; // points to state_ or a local copy within run()
struct cpu_state_t cpu_state_;
// page mapping registers
uint8_t mmr [page_count + 1];
@ -58,7 +54,10 @@ struct Hes_Cpu {
};
// Init cpu state
void Cpu_init( struct Hes_Cpu* this );
static inline void Cpu_init( struct Hes_Cpu* this )
{
this->cpu_state = &this->cpu_state_;
}
// Reset hes cpu
void Cpu_reset( struct Hes_Cpu* this );
@ -67,29 +66,67 @@ void Cpu_reset( struct Hes_Cpu* this );
// instructions were encountered.
bool Cpu_run( struct Hes_Emu* this, hes_time_t end_time );
void Cpu_set_mmr( struct Hes_Emu* this, int reg, int bank );
// Time of ning of next instruction to be executed
static inline hes_time_t Cpu_time( struct Hes_Cpu* this )
{
return this->state->time + this->state->base;
return this->cpu_state->time + this->cpu_state->base;
}
static inline void Cpu_set_time( struct Hes_Cpu* this, hes_time_t t ) { this->cpu_state->time = t - this->cpu_state->base; }
static inline void Cpu_adjust_time( struct Hes_Cpu* this, int delta ) { this->cpu_state->time += delta; }
#define HES_CPU_PAGE( addr ) ((unsigned) (addr) >> page_bits)
#ifdef BLARGG_NONPORTABLE
#define HES_CPU_OFFSET( addr ) (addr)
#else
#define HES_CPU_OFFSET( addr ) ((addr) & (page_size - 1))
#endif
static inline uint8_t const* Cpu_get_code( struct Hes_Cpu* this, hes_addr_t addr )
{
return this->state->code_map [addr >> page_shift] + addr
#if !defined (BLARGG_NONPORTABLE)
% (unsigned) page_size
#endif
;
return this->cpu_state_.code_map [HES_CPU_PAGE( addr )] + HES_CPU_OFFSET( addr );
}
static inline 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

View file

@ -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 )

File diff suppressed because it is too large Load diff

View file

@ -21,28 +21,14 @@ int const vdp_mask = 0x02;
int const i_flag_mask = 0x04;
int const unmapped = 0xFF;
long const period_60hz = 262 * 455L; // scanlines * clocks per scanline
int const stereo = 2; // number of channels for stereo
int const silence_max = 6; // seconds
int const silence_threshold = 0x10;
long const fade_block_size = 512;
int const fade_shift = 8; // fade ends with gain at 1.0 / (1 << fade_shift)
int const period_60hz = 262 * 455; // scanlines * clocks per scanline
const char gme_wrong_file_type [] = "Wrong file type for this emulator";
static void clear_track_vars( struct Hes_Emu* this )
{
this->current_track_ = -1;
this->out_time = 0;
this->emu_time = 0;
this->emu_track_ended_ = true;
this->track_ended = true;
this->fade_start = (blargg_long)(LONG_MAX / 2 + 1);
this->fade_step = 1;
this->silence_time = 0;
this->silence_count = 0;
this->buf_remain = 0;
this->current_track_ = -1;
track_stop( &this->track_filter );
}
void Hes_init( struct Hes_Emu* this )
@ -52,15 +38,12 @@ void Hes_init( struct Hes_Emu* this )
this->tempo_ = (int)(FP_ONE_TEMPO);
// defaults
this->max_initial_silence = 2;
this->ignore_silence = false;
// Unload
this->voice_count_ = 0;
clear_track_vars( this );
this->tfilter = *track_get_setup( &this->track_filter );
this->tfilter.max_initial = 2;
this->tfilter.lookahead = 6;
this->track_filter.silence_ignored_ = false;
this->timer.raw_load = 0;
this->silence_lookahead = 6;
Sound_set_gain( this, (int)(FP_ONE_GAIN*1.11) );
Rom_init( &this->rom, 0x2000 );
@ -71,6 +54,11 @@ void Hes_init( struct Hes_Emu* this )
/* Set default track count */
this->track_count = 255;
// clears fields
this->voice_count_ = 0;
this->voice_types_ = 0;
clear_track_vars( this );
}
static blargg_err_t check_hes_header( void const* header )
@ -82,10 +70,12 @@ static blargg_err_t check_hes_header( void const* header )
// Setup
blargg_err_t Hes_load( struct Hes_Emu* this, void* data, long size )
blargg_err_t Hes_load_mem( struct Hes_Emu* this, void* data, long size )
{
// Unload
this->voice_count_ = 0;
this->track_count = 255;
this->m3u.size = 0;
clear_track_vars( this );
assert( offsetof (struct header_t,unused [4]) == header_size );
@ -106,15 +96,15 @@ blargg_err_t Hes_load( struct Hes_Emu* this, void* data, long size )
// many files have bad sizes in the only block, so it's simpler to
// just try to load the damn data as best as possible.
long addr = get_le32( this->header.addr );
/* long rom_size = get_le32( this->header.size ); */
long const rom_max = 0x100000;
if ( addr & ~(rom_max - 1) )
int addr = get_le32( this->header.addr );
/* int rom_size = get_le32( this->header.size ); */
int const rom_max = 0x100000;
if ( (unsigned) addr >= (unsigned) rom_max )
{
/* warning( "Invalid address" ); */
addr &= rom_max - 1;
}
/* if ( (unsigned long) (addr + size) > (unsigned long) rom_max )
/* if ( (unsigned) (addr + size) > (unsigned) rom_max )
warning( "Invalid size" );
if ( rom_size != rom.file_size() )
@ -130,6 +120,10 @@ blargg_err_t Hes_load( struct Hes_Emu* this, void* data, long size )
Rom_set_addr( &this->rom, addr );
this->voice_count_ = osc_count + adpcm_osc_count;
static int const types [osc_count + adpcm_osc_count] = {
wave_type+0, wave_type+1, wave_type+2, wave_type+3, mixed_type+0, mixed_type+1, mixed_type+2
};
this->voice_types_ = types;
Apu_volume( &this->apu, this->gain_ );
Adpcm_volume( &this->adpcm, this->gain_ );
@ -137,21 +131,17 @@ blargg_err_t Hes_load( struct Hes_Emu* this, void* data, long size )
// Setup buffer
this->clock_rate_ = 7159091;
Buffer_clock_rate( &this->stereo_buf, 7159091 );
RETURN_ERR( Buffer_set_channel_count( &this->stereo_buf, this->voice_count_, this->voice_types_ ) );
this->buf_changed_count = Buffer_channels_changed_count( &this->stereo_buf );
Sound_set_tempo( this, this->tempo_ );
Sound_mute_voices( this, this->mute_mask_ );
// Reset track count
this->track_count = 255;
this->m3u.size = 0;
return 0;
}
// Emulation
void recalc_timer_load( struct Hes_Emu* this );
void recalc_timer_load( struct Hes_Emu* this )
{
this->timer.load = this->timer.raw_load * this->timer_base + 1;
@ -159,9 +149,25 @@ void recalc_timer_load( struct Hes_Emu* this )
// Hardware
void irq_changed( struct Hes_Emu* this );
void run_until( struct Hes_Emu* this, hes_time_t present );
void Cpu_write_vdp( struct Hes_Emu* this, int addr, int data )
void run_until( struct Hes_Emu* this, hes_time_t present )
{
while ( this->vdp.next_vbl < present )
this->vdp.next_vbl += this->play_period;
hes_time_t elapsed = present - this->timer.last_time;
if ( elapsed > 0 )
{
if ( this->timer.enabled )
{
this->timer.count -= elapsed;
if ( this->timer.count <= 0 )
this->timer.count += this->timer.load;
}
this->timer.last_time = present;
}
}
void write_vdp( struct Hes_Emu* this, int addr, int data )
{
switch ( addr )
{
@ -178,77 +184,33 @@ void Cpu_write_vdp( struct Hes_Emu* this, int addr, int data )
this->vdp.control = data;
irq_changed( this );
}
else
/* else
{
dprintf( "VDP not supported: $%02X <- $%02X\n", this->vdp.latch, data );
}
dprintf( "VDP not supported: $%02X <- $%02X\n", vdp.latch, data );
} */
break;
case 3:
dprintf( "VDP MSB not supported: $%02X <- $%02X\n", this->vdp.latch, data );
/* dprintf( "VDP MSB not supported: $%02X <- $%02X\n", vdp.latch, data ); */
break;
}
}
int Cpu_done( struct Hes_Emu* this )
{
check( time() >= end_time() ||
(!(r.status & i_flag_mask) && time() >= irq_time()) );
if ( !(this->cpu.r.status & i_flag_mask) )
{
hes_time_t present = Cpu_time( &this->cpu );
if ( this->irq.timer <= present && !(this->irq.disables & timer_mask) )
{
this->timer.fired = true;
this->irq.timer = (hes_time_t)future_hes_time;
irq_changed( this ); // overkill, but not worth writing custom code
#if defined (GME_FRAME_HOOK_DEFINED)
{
unsigned const threshold = period_60hz / 30;
unsigned long elapsed = present - last_frame_hook;
if ( elapsed - period_60hz + threshold / 2 < threshold )
{
last_frame_hook = present;
GME_FRAME_HOOK( this );
}
}
#endif
return 0x0A;
}
if ( this->irq.vdp <= present && !(this->irq.disables & vdp_mask) )
{
// work around for bugs with music not acknowledging VDP
//run_until( present );
//irq.vdp = future_hes_time;
//irq_changed();
#if defined(GME_FRAME_HOOK_DEFINED)
last_frame_hook = present;
GME_FRAME_HOOK( this );
#endif
return 0x08;
}
}
return 0;
}
void Emu_cpu_write( struct Hes_Emu* this, hes_addr_t addr, int data )
void write_mem_( struct Hes_Emu* this, hes_addr_t addr, int data )
{
hes_time_t time = Cpu_time( &this->cpu );
if ( (unsigned) (addr - start_addr) <= end_addr - start_addr )
if ( (unsigned) (addr - apu_io_addr) < apu_io_size )
{
GME_APU_HOOK( this, addr - apu.start_addr, data );
// avoid going way past end when a long block xfer is writing to I/O space
hes_time_t t = min( time, this->cpu.end_time + 8 );
// Avoid going way past end when a long block xfer is writing to I/O space.
// Not a problem for other registers below because they don't write to
// Blip_Buffer.
hes_time_t t = min( time, Cpu_end_time( &this->cpu ) + 8 );
Apu_write_data( &this->apu, t, addr, data );
return;
}
if ( (unsigned) (addr - io_addr) < io_size )
if ( (unsigned) (addr - adpcm_io_addr) < adpcm_io_size )
{
hes_time_t t = min( time, this->cpu.end_time + 6 );
hes_time_t t = min( time, Cpu_end_time( &this->cpu ) + 6 );
Adpcm_write_data( &this->adpcm, t, addr, data );
return;
}
@ -258,7 +220,7 @@ void Emu_cpu_write( struct Hes_Emu* this, hes_addr_t addr, int data )
case 0x0000:
case 0x0002:
case 0x0003:
Cpu_write_vdp( this, addr, data );
write_vdp( this, addr, data );
return;
case 0x0C00: {
@ -282,11 +244,8 @@ void Emu_cpu_write( struct Hes_Emu* this, hes_addr_t addr, int data )
case 0x1402:
run_until( this, time );
this->irq.disables = data;
// flag questionable values
if ( (data & 0xF8) && (data & 0xF8) != 0xF8 ) {
dprintf( "Int mask: $%02X\n", data );
}
/* if ( (data & 0xF8) && (data & 0xF8) != 0xF8 ) // flag questionable values
dprintf( "Int mask: $%02X\n", data ); */
break;
case 0x1403:
@ -305,7 +264,7 @@ void Emu_cpu_write( struct Hes_Emu* this, hes_addr_t addr, int data )
return;
default:
dprintf( "unmapped write $%04X <- $%02X\n", addr, data );
/* dprintf( "unmapped write $%04X <- $%02X\n", addr, data ); */
return;
#endif
}
@ -313,7 +272,7 @@ void Emu_cpu_write( struct Hes_Emu* this, hes_addr_t addr, int data )
irq_changed( this );
}
int Emu_cpu_read( struct Hes_Emu* this, hes_addr_t addr )
int read_mem_( struct Hes_Emu* this, hes_addr_t addr )
{
hes_time_t time = Cpu_time( &this->cpu );
addr &= page_size - 1;
@ -322,21 +281,21 @@ int Emu_cpu_read( struct Hes_Emu* this, hes_addr_t addr )
case 0x0000:
if ( this->irq.vdp > time )
return 0;
this->irq.vdp = (hes_time_t)future_hes_time;
this->irq.vdp = future_time;
run_until( this, time );
irq_changed( this );
return 0x20;
case 0x0002:
/* case 0x0002:
case 0x0003:
dprintf( "VDP read not supported: %d\n", addr );
return 0;
return 0; */
case 0x0C01:
//return timer.enabled; // TODO: remove?
case 0x0C00:
run_until( this, time );
dprintf( "Timer count read\n" );
/* dprintf( "Timer count read\n" ); */
return (unsigned) (this->timer.count - 1) / this->timer_base;
case 0x1402:
@ -349,7 +308,7 @@ int Emu_cpu_read( struct Hes_Emu* this, hes_addr_t addr )
if ( this->irq.vdp <= time ) status |= vdp_mask;
return status;
}
case 0x180A:
case 0x180B:
case 0x180C:
@ -358,71 +317,75 @@ int Emu_cpu_read( struct Hes_Emu* this, hes_addr_t addr )
#ifndef NDEBUG
case 0x1000: // I/O port
// case 0x180C: // CD-ROM
// case 0x180D:
//case 0x180C: // CD-ROM
//case 0x180D:
break;
default:
dprintf( "unmapped read $%04X\n", addr );
/* default:
dprintf( "unmapped read $%04X\n", addr ); */
#endif
}
return unmapped;
}
// see hes_cpu_io.h for core read/write functions
// Emulation
void run_until( struct Hes_Emu* this, hes_time_t present )
{
while ( this->vdp.next_vbl < present )
this->vdp.next_vbl += this->play_period;
hes_time_t elapsed = present - this->timer.last_time;
if ( elapsed > 0 )
{
if ( this->timer.enabled )
{
this->timer.count -= elapsed;
if ( this->timer.count <= 0 )
this->timer.count += this->timer.load;
}
this->timer.last_time = present;
}
}
void irq_changed( struct Hes_Emu* this )
{
hes_time_t present = Cpu_time( &this->cpu );
if ( this->irq.timer > present )
{
this->irq.timer = (hes_time_t)future_hes_time;
this->irq.timer = future_time;
if ( this->timer.enabled && !this->timer.fired )
this->irq.timer = present + this->timer.count;
}
if ( this->irq.vdp > present )
{
this->irq.vdp = (hes_time_t)future_hes_time;
this->irq.vdp = future_time;
if ( this->vdp.control & 0x08 )
this->irq.vdp = this->vdp.next_vbl;
}
hes_time_t time = (hes_time_t)future_hes_time;
hes_time_t time = future_time;
if ( !(this->irq.disables & timer_mask) ) time = this->irq.timer;
if ( !(this->irq.disables & vdp_mask) ) time = min( time, this->irq.vdp );
// Set cpu irq time
this->cpu.state->time += Cpu_update_end_time( &this->cpu, this->cpu.r.status,
this->cpu.end_time, (this->cpu.irq_time = time) );
Cpu_set_irq_time( &this->cpu, time );
}
static void adjust_time( blargg_long* time, hes_time_t delta );
static void adjust_time( blargg_long* time, hes_time_t delta )
int cpu_done( struct Hes_Emu* this )
{
if ( *time < (blargg_long)future_hes_time )
check( Cpu_time( &this->cpu ) >= Cpu_end_time( &this->cpu ) ||
(!(this->cpu.r.flags & i_flag_mask) && Cpu_time( &this->cpu ) >= Cpu_irq_time( &this->cpu )) );
if ( !(this->cpu.r.flags & i_flag_mask) )
{
hes_time_t present = Cpu_time( &this->cpu );
if ( this->irq.timer <= present && !(this->irq.disables & timer_mask) )
{
this->timer.fired = true;
this->irq.timer = future_time;
irq_changed( this ); // overkill, but not worth writing custom code
return 0x0A;
}
if ( this->irq.vdp <= present && !(this->irq.disables & vdp_mask) )
{
// work around for bugs with music not acknowledging VDP
//run_until( present );
//irq.vdp = cpu.future_time;
//irq_changed();
return 0x08;
}
}
return -1;
}
static void adjust_time( hes_time_t* time, hes_time_t delta )
{
if ( *time < future_time )
{
*time -= delta;
if ( *time < 0 )
@ -430,15 +393,13 @@ static void adjust_time( blargg_long* time, hes_time_t delta )
}
}
blargg_err_t run_clocks( struct Hes_Emu* this, blip_time_t* duration_ );
blargg_err_t run_clocks( struct Hes_Emu* this, blip_time_t* duration_ )
blargg_err_t end_frame( struct Hes_Emu* this, hes_time_t duration )
{
blip_time_t duration = *duration_; // cache
/* if ( run_cpu( this, duration ) )
warning( "Emulation error (illegal instruction)" ); */
run_cpu( this, duration );
Cpu_run( this, duration );
/* warning( "Emulation error (illegal instruction)" ); */
check( time() >= duration );
check( Cpu_time( &this->cpu ) >= duration );
//check( time() - duration < 20 ); // Txx instruction could cause going way over
run_until( this, duration );
@ -446,15 +407,7 @@ blargg_err_t run_clocks( struct Hes_Emu* this, blip_time_t* duration_ )
// end time frame
this->timer.last_time -= duration;
this->vdp.next_vbl -= duration;
#if defined (GME_FRAME_HOOK_DEFINED)
last_frame_hook -= *duration;
#endif
// End cpu frame
this->cpu.state_.base -= duration;
if ( this->cpu.irq_time < (hes_time_t)future_hes_time ) this->cpu.irq_time -= duration;
if ( this->cpu.end_time < (hes_time_t)future_hes_time ) this->cpu.end_time -= duration;
Cpu_end_frame( &this->cpu, duration );
adjust_time( &this->irq.timer, duration );
adjust_time( &this->irq.vdp, duration );
Apu_end_frame( &this->apu, duration );
@ -463,24 +416,31 @@ blargg_err_t run_clocks( struct Hes_Emu* this, blip_time_t* duration_ )
return 0;
}
blargg_err_t play_( struct Hes_Emu* this, long count, sample_t* out );
blargg_err_t play_( struct Hes_Emu* this, long count, sample_t* out )
blargg_err_t run_clocks( struct Hes_Emu* this, blip_time_t* duration_ )
{
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
@ -536,7 +497,8 @@ void Sound_mute_voices( struct Hes_Emu* this, int mask )
Apu_osc_output( &this->apu, i, 0, 0, 0 );
}
else
{
{
struct channel_t ch = Buffer_channel( &this->stereo_buf, i );
assert( (ch.center && ch.left && ch.right) ||
(!ch.center && !ch.left && !ch.right) ); // all or nothing
Apu_osc_output( &this->apu, i, ch.center, ch.left, ch.right );
@ -557,7 +519,6 @@ void Sound_set_tempo( struct Hes_Emu* this, int t )
this->tempo_ = t;
}
void fill_buf( struct Hes_Emu* this );
blargg_err_t Hes_start_track( struct Hes_Emu* this, int track )
{
clear_track_vars( this );
@ -572,7 +533,7 @@ blargg_err_t Hes_start_track( struct Hes_Emu* this, int track )
Buffer_clear( &this->stereo_buf );
memset( this->cpu.ram, 0, sizeof this->cpu.ram ); // some HES music relies on zero fill
memset( this->ram, 0, sizeof this->ram ); // some HES music relies on zero fill
memset( this->sgx, 0, sizeof this->sgx );
Apu_reset( &this->apu );
@ -581,12 +542,12 @@ blargg_err_t Hes_start_track( struct Hes_Emu* this, int track )
unsigned i;
for ( i = 0; i < sizeof this->header.banks; i++ )
Cpu_set_mmr( this, i, this->header.banks [i] );
Cpu_set_mmr( this, page_count, 0xFF ); // unmapped beyond end of address space
set_mmr( this, i, this->header.banks [i] );
set_mmr( this, page_count, 0xFF ); // unmapped beyond end of address space
this->irq.disables = timer_mask | vdp_mask;
this->irq.timer = (hes_time_t)future_hes_time;
this->irq.vdp = (hes_time_t)future_hes_time;
this->irq.timer = future_time;
this->irq.vdp = future_time;
this->timer.enabled = false;
this->timer.raw_load= 0x80;
@ -598,280 +559,86 @@ blargg_err_t Hes_start_track( struct Hes_Emu* this, int track )
this->vdp.control = 0;
this->vdp.next_vbl = 0;
this->cpu.ram [0x1FF] = (idle_addr - 1) >> 8;
this->cpu.ram [0x1FE] = (idle_addr - 1) & 0xFF;
this->ram [0x1FF] = (idle_addr - 1) >> 8;
this->ram [0x1FE] = (idle_addr - 1) & 0xFF;
this->cpu.r.sp = 0xFD;
this->cpu.r.pc = get_le16( this->header.init_addr );
this->cpu.r.a = track;
recalc_timer_load( this );
this->last_frame_hook = 0;
this->emu_track_ended_ = false;
this->track_ended = false;
// convert filter times to samples
struct setup_t s = this->tfilter;
s.max_initial *= this->sample_rate_ * stereo;
#ifdef GME_DISABLE_SILENCE_LOOKAHEAD
s.lookahead = 1;
#endif
track_setup( &this->track_filter, &s );
if ( !this->ignore_silence )
{
// play until non-silence or end of track
long end;
for ( end =this-> max_initial_silence * stereo * this->sample_rate_; this->emu_time < end; )
{
fill_buf( this );
if ( this->buf_remain | (int) this->emu_track_ended_ )
break;
}
this->emu_time = this->buf_remain;
this->out_time = 0;
this->silence_time = 0;
this->silence_count = 0;
}
/* return track_ended() ? warning() : 0; */
return 0;
return track_start( &this->track_filter );
}
// Tell/Seek
static blargg_long msec_to_samples( blargg_long msec, long sample_rate )
static int msec_to_samples( int msec, int sample_rate )
{
blargg_long sec = msec / 1000;
int sec = msec / 1000;
msec -= sec * 1000;
return (sec * sample_rate + msec * sample_rate / 1000) * stereo;
}
long Track_tell( struct Hes_Emu* this )
int Track_tell( struct Hes_Emu* this )
{
blargg_long rate = this->sample_rate_ * stereo;
blargg_long sec = this->out_time / rate;
return sec * 1000 + (this->out_time - sec * rate) * 1000 / rate;
int rate = this->sample_rate_ * stereo;
int sec = track_sample_count( &this->track_filter ) / rate;
return sec * 1000 + (track_sample_count( &this->track_filter ) - sec * rate) * 1000 / rate;
}
blargg_err_t Track_seek( struct Hes_Emu* this, long msec )
blargg_err_t Track_seek( struct Hes_Emu* this, int msec )
{
blargg_long time = msec_to_samples( msec, this->sample_rate_ );
if ( time < this->out_time )
RETURN_ERR( Hes_start_track( this, this->current_track_ ) );
return Track_skip( this, time - this->out_time );
int time = msec_to_samples( msec, this->sample_rate_ );
if ( time < track_sample_count( &this->track_filter ) )
RETURN_ERR( Hes_start_track( this, this->current_track_ ) );
return Track_skip( this, time - track_sample_count( &this->track_filter ) );
}
blargg_err_t skip_( struct Hes_Emu* this, long count );
blargg_err_t skip_( struct Hes_Emu* this, long count )
blargg_err_t skip_( void* emu, int count )
{
struct Hes_Emu* this = (struct Hes_Emu*) emu;
// for long skip, mute sound
const long threshold = 30000;
const int threshold = 32768;
if ( count > threshold )
{
int saved_mute = this->mute_mask_;
Sound_mute_voices( this, ~0 );
while ( count > threshold / 2 && !this->emu_track_ended_ )
{
RETURN_ERR( play_( this, buf_size, this->buf ) );
count -= buf_size;
}
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 );
}

View file

@ -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

View file

@ -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;
return track_skip( &this->track_filter, count );
}
void Track_set_fade( struct Kss_Emu* this, int start_msec, int length_msec )
{
track_set_fade( &this->track_filter, msec_to_samples( start_msec, this->sample_rate ),
length_msec * this->sample_rate / (1000 / stereo) );
}
blargg_err_t Kss_play( struct Kss_Emu* this, int out_count, sample_t* out )
{
require( this->current_track >= 0 );
require( out_count % stereo == 0 );
return track_play( &this->track_filter, out_count, out );
}
blargg_err_t play_( void *emu, int count, sample_t* out )
{
struct Kss_Emu* this = (struct Kss_Emu*) emu;
// remove from silence and buf first
{
long n = min( count, this->silence_count );
this->silence_count -= n;
count -= n;
n = min( count, this->buf_remain );
this->buf_remain -= n;
count -= n;
}
if ( count && !this->emu_track_ended_ )
{
this->emu_time += count;
if ( skip_( this, count ) )
this->emu_track_ended_ = true;
}
if ( !(this->silence_count | this->buf_remain) ) // caught up to emulator, so update track ended
this->track_ended |= this->emu_track_ended_;
return 0;
}
// Fading
void Track_set_fade( struct Kss_Emu* this, long start_msec, long length_msec )
{
this->fade_step = this->sample_rate * length_msec / (fade_block_size * fade_shift * 1000 / stereo);
this->fade_start = msec_to_samples( start_msec, this->sample_rate );
}
// unit / pow( 2.0, (double) x / step )
static int int_log( blargg_long x, int step, int unit )
{
int shift = x / step;
int fraction = (x - shift * step) * unit / step;
return ((unit - fraction) + (fraction >> 1)) >> shift;
}
static void handle_fade( struct Kss_Emu *this, long out_count, sample_t* out )
{
int i;
for ( i = 0; i < out_count; i += fade_block_size )
{
int const shift = 14;
int const unit = 1 << shift;
int gain = int_log( (this->out_time + i - this->fade_start) / fade_block_size,
this->fade_step, unit );
if ( gain < (unit >> fade_shift) )
this->track_ended = this->emu_track_ended_ = true;
sample_t* io = &out [i];
int count;
for ( count = min( fade_block_size, out_count - i ); count; --count )
{
*io = (sample_t) ((*io * gain) >> shift);
++io;
}
}
}
// 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;
}

View file

@ -16,8 +16,8 @@
#include "ay_apu.h"
#include "opl_apu.h"
#include "m3u_playlist.h"
#include "track_filter.h"
typedef short sample_t;
typedef int kss_time_t;
typedef int kss_addr_t;
typedef struct Z80_Cpu Kss_Cpu;
@ -35,7 +35,6 @@ enum {
enum { idle_addr = 0xFFFF };
enum { scc_enabled_true = 0xC000 };
enum { mem_size = 0x10000 };
enum { buf_size = 2048 };
// KSS file header
enum { header_size = 0x20 };
@ -53,7 +52,7 @@ struct header_t
byte bank_mode;
byte extra_header;
byte device_flags;
// KSSX extended data, if extra_header==0x10
byte data_size [4];
byte unused [4];
@ -69,7 +68,7 @@ struct sms_t {
struct Sms_Apu psg;
struct Opl_Apu fm;
};
struct msx_t {
struct Ay_Apu psg;
struct Scc_Apu scc;
@ -84,8 +83,6 @@ struct Kss_Emu {
bool scc_accessed;
bool gain_updated;
int track_count;
unsigned scc_enabled; // 0 or 0xC000
int bank_count;
@ -94,48 +91,34 @@ struct Kss_Emu {
int ay_latch;
// general
int max_initial_silence;
int voice_count;
int const* voice_types;
int mute_mask_;
int tempo;
int gain;
long sample_rate;
int sample_rate;
// track-specific
int track_count;
int current_track;
blargg_long out_time; // number of samples played since start of track
blargg_long emu_time; // number of samples emulator has generated since start of track
bool emu_track_ended_; // emulator has reached end of track
volatile bool track_ended;
// fading
blargg_long fade_start;
int fade_step;
// silence detection
int silence_lookahead; // speed to run emulator when looking ahead for silence
bool ignore_silence;
long silence_time; // number of samples where most recent silence began
long silence_count; // number of samples of silence to play before using buf
long buf_remain; // number of samples left in silence buffer
struct Stereo_Buffer stereo_buffer; // NULL if using custom buffer
long clock_rate_;
int clock_rate_;
unsigned buf_changed_count;
// M3u Playlist
// M3u Playlist
struct M3u_Playlist m3u;
// large items
sample_t buf [buf_size];
struct setup_t tfilter;
struct Track_Filter track_filter;
struct sms_t sms;
struct msx_t msx;
Kss_Cpu cpu;
struct Multi_Buffer stereo_buf; // NULL if using custom buffer
struct Rom_Data rom;
byte unmapped_read [0x100];
byte unmapped_write [page_size];
byte ram [mem_size + cpu_padding];
@ -148,34 +131,46 @@ blargg_err_t Kss_load_mem( struct Kss_Emu* this, const void* data, long size );
blargg_err_t end_frame( struct Kss_Emu* this, kss_time_t );
// Set output sample rate. Must be called only once before loading file.
blargg_err_t Kss_set_sample_rate( struct Kss_Emu* this, long sample_rate );
blargg_err_t Kss_set_sample_rate( struct Kss_Emu* this, int sample_rate );
// Start a track, where 0 is the first track. Also clears warning string.
blargg_err_t Kss_start_track( struct Kss_Emu* this, int track );
// Generate 'count' samples info 'buf'. Output is in stereo. Any emulation
// errors set warning string, and major errors also end track.
blargg_err_t Kss_play( struct Kss_Emu* this, long count, sample_t* buf );
blargg_err_t Kss_play( struct Kss_Emu* this, int count, sample_t* buf );
// Track status/control
// Number of milliseconds (1000 msec = 1 second) played since beginning of track
long Track_tell( struct Kss_Emu* this );
int Track_tell( struct Kss_Emu* this );
// Seek to new time in track. Seeking backwards or far forward can take a while.
blargg_err_t Track_seek( struct Kss_Emu* this, long msec );
blargg_err_t Track_seek( struct Kss_Emu* this, int msec );
// Skip n samples
blargg_err_t Track_skip( struct Kss_Emu* this, long n );
blargg_err_t Track_skip( struct Kss_Emu* this, int n );
// Set start time and length of track fade out. Once fade ends track_ended() returns
// true. Fade time can be changed while track is playing.
void Track_set_fade( struct Kss_Emu* this, long start_msec, long length_msec );
void Track_set_fade( struct Kss_Emu* this, int start_msec, int length_msec );
// True if a track has reached its end
static inline bool Track_ended( struct Kss_Emu* this )
{
return track_ended( &this->track_filter );
}
// Disables automatic end-of-track detection and skipping of silence at beginning
static inline void Track_ignore_silence( struct Kss_Emu* this, bool disable )
{
this->track_filter.silence_ignored_ = disable;
}
// Get track length in milliseconds
static inline long Track_get_length( struct Kss_Emu* this, int n )
static inline int Track_get_length( struct Kss_Emu* this, int n )
{
long length = 0;
int length = 0;
if ( (this->m3u.size > 0) && (n < this->m3u.size) ) {
struct entry_t* entry = &this->m3u.entries [n];

View file

@ -34,7 +34,7 @@ void Scc_reset( struct Scc_Apu* this );
// Set overall volume, where 1.0 is normal
void Scc_volume( struct Scc_Apu* this, int v );
static inline void Scc_set_output( struct Scc_Apu* this, int index, struct Blip_Buffer* b )
{
assert( (unsigned) index < scc_osc_count );

View file

@ -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
void Buffer_init( struct Stereo_Buffer* this )
int const blip_buffer_extra = 32; // TODO: explain why this value
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 ) );
this->sample_rate_ = Blip_sample_rate( &this->bufs [0] );
this->length_ = Blip_length( &this->bufs [0] );
for ( i = bufs_size; --i >= 0; )
RETURN_ERR( Blip_set_sample_rate( &this->bufs [i].blip, rate, msec ) );
this->sample_rate_ = Blip_sample_rate( &this->bufs [0].blip );
this->length_ = Blip_length( &this->bufs [0].blip );
return 0;
}
void Buffer_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;
long avail = Blip_samples_avail( &this->bufs [0] );
if ( count > avail )
count = avail;
if ( count )
require( (out_size & 1) == 0 ); // must read an even number of samples
out_size = min( out_size, Buffer_samples_avail( this ) );
int pair_count = (int) (out_size >> 1);
if ( pair_count )
{
int bufs_used = this->stereo_added | this->was_stereo;
//dprintf( "%X\n", bufs_used );
if ( bufs_used <= 1 )
Mixer_read_pairs( &this->mixer, out, pair_count );
if ( Buffer_samples_avail( this ) <= 0 || this->immediate_removal_ )
{
Buffer_mix_mono( this, out, count );
Blip_remove_samples( &this->bufs [0], count );
Blip_remove_silence( &this->bufs [1], count );
Blip_remove_silence( &this->bufs [2], count );
}
else if ( bufs_used & 1 )
{
Buffer_mix_stereo( this, out, count );
Blip_remove_samples( &this->bufs [0], count );
Blip_remove_samples( &this->bufs [1], count );
Blip_remove_samples( &this->bufs [2], count );
}
else
{
Buffer_mix_stereo_no_center( this, out, count );
Blip_remove_silence( &this->bufs [0], count );
Blip_remove_samples( &this->bufs [1], count );
Blip_remove_samples( &this->bufs [2], count );
}
// to do: this might miss opportunities for optimization
if ( !Blip_samples_avail( &this->bufs [0] ) )
{
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;
}

View file

@ -1,4 +1,4 @@
// Multi-channel sound buffer interface, and basic mono and stereo buffers
// Multi-channel sound buffer interface, stereo and effects buffers
// Blip_Buffer 0.4.1
#ifndef MULTI_BUFFER_H
@ -16,57 +16,99 @@ struct channel_t {
enum { type_index_mask = 0xFF };
enum { wave_type = 0x100, noise_type = 0x200, mixed_type = wave_type | noise_type };
enum { buf_count = 3 };
struct Stereo_Buffer {
struct Blip_Buffer bufs [buf_count];
struct channel_t chan;
int stereo_added;
int was_stereo;
unsigned channels_changed_count_;
long sample_rate_;
int length_;
int samples_per_frame_;
enum { stereo = 2 };
enum { bufs_size = 3 };
// 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

View file

@ -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 )

View file

@ -1,6 +1,6 @@
// NES 2A03 APU sound chip emulator
// Nes_Snd_Emu 0.1.8
// Nes_Snd_Emu 0.2.0-pre
#ifndef NES_APU_H
#define NES_APU_H
@ -9,7 +9,7 @@
enum { apu_status_addr = 0x4015 };
enum { apu_osc_count = 5 };
enum { apu_no_irq = INT_MAX / 2 + 1 };
enum { apu_no_irq = INT_MAX/2 + 1 };
enum { apu_irq_waiting = 0 };
enum { apu_io_addr = 0x4000 };
@ -17,24 +17,16 @@ enum { apu_io_size = 0x18 };
struct apu_state_t;
struct Nes_Apu {
nes_time_t last_dmc_time;
int osc_enables;
struct Nes_Osc* oscs [apu_osc_count];
struct Nes_Square square1;
struct Nes_Square square2;
struct Nes_Noise noise;
struct Nes_Triangle triangle;
struct Nes_Dmc dmc;
struct Nes_Apu {
int tempo_;
nes_time_t last_time; // has been run until this time in current frame
nes_time_t last_dmc_time;
nes_time_t earliest_irq_;
nes_time_t next_irq;
int frame_period;
int frame_delay; // cycles until frame counter runs next
int frame; // current frame (0-3)
int osc_enables;
int frame_mode;
bool irq_flag;
@ -42,6 +34,13 @@ struct Nes_Apu {
void* irq_data;
Synth square_synth; // shared by squares
struct Nes_Osc* oscs [apu_osc_count];
struct Nes_Square square1;
struct Nes_Square square2;
struct Nes_Noise noise;
struct Nes_Triangle triangle;
struct Nes_Dmc dmc;
};
// Init Nes apu
@ -49,22 +48,22 @@ void Apu_init( struct Nes_Apu* this );
// Set buffer to generate all sound into, or disable sound if NULL
void Apu_output( struct Nes_Apu* this, struct Blip_Buffer* );
// All time values are the number of cpu clock cycles relative to the
// beginning of the current time frame. Before resetting the cpu clock
// count, call end_frame( last_cpu_time ).
// Write to register (0x4000-0x4017, except 0x4014 and 0x4016)
void Apu_write_register( struct Nes_Apu* this, nes_time_t, addr_t, int data );
// Read from status register at 0x4015
int Apu_read_status( struct Nes_Apu* this, nes_time_t );
// Run all oscillators up to specified time, end current time frame, then
// start a new time frame at time 0. Time frames have no effect on emulation
// and each can be whatever length is convenient.
void Apu_end_frame( struct Nes_Apu* this, nes_time_t );
// Additional optional features (can be ignored without any problem)
// Reset internal frame counter, registers, and all oscillators.
@ -72,13 +71,13 @@ void Apu_end_frame( struct Nes_Apu* this, nes_time_t );
// Set the DMC oscillator's initial DAC value to initial_dmc_dac without
// any audible click.
void Apu_reset( struct Nes_Apu* this, bool pal_mode, int initial_dmc_dac );
// Adjust frame period
void Apu_set_tempo( struct Nes_Apu* this, int );
// Set overall volume (default is 1.0)
void Apu_volume( struct Nes_Apu* this, int );
// Run DMC until specified time, so that any DMC memory reads can be
// accounted for (i.e. inserting cpu wait states).
void Apu_run_until( struct Nes_Apu* this, nes_time_t );
@ -124,11 +123,13 @@ static inline nes_time_t Dmc_next_read_time( struct Nes_Dmc* this )
if ( this->osc.length_counter == 0 )
return apu_no_irq; // not reading
return this->apu->last_dmc_time + this->osc.delay + (long) (this->bits_remain - 1) * this->period;
return this->apu->last_dmc_time + this->osc.delay + (this->bits_remain - 1) * this->period;
}
// Time when next DMC memory read will occur
static inline nes_time_t Apu_next_dmc_read_time( struct Nes_Apu* this ) { return Dmc_next_read_time( &this->dmc ); }
void Apu_irq_changed( struct Nes_Apu* this );
// Experimental
void Apu_enable_nonlinear_( struct Nes_Apu* this, double sq, double tnd );
#endif

View file

@ -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 )

View file

@ -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;
}
}

View file

@ -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

View file

@ -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

View file

@ -15,13 +15,13 @@ enum { namco_data_reg_addr = 0x4800 };
enum { namco_reg_count = 0x80 };
struct Namco_Osc {
blargg_long delay;
int delay;
struct Blip_Buffer* output;
short last_amp;
short wave_pos;
};
struct Nes_Namco_Apu {
struct Nes_Namco_Apu {
struct Namco_Osc oscs [namco_osc_count];
blip_time_t last_time;

View file

@ -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);

View file

@ -46,7 +46,7 @@ enum { shift_mask = 0x07 };
enum { square_phase_range = 8 };
typedef struct Blip_Synth Synth;
struct Nes_Square
{
struct Nes_Osc osc;
@ -54,12 +54,12 @@ struct Nes_Square
int env_delay;
int phase;
int sweep_delay;
Synth* synth; // shared between squares
};
static inline void Square_set_synth( struct Nes_Square* this, Synth* s ) { this->synth = s; }
void Square_clock_sweep( struct Nes_Square* this, int adjust );
void Square_run( struct Nes_Square* this, nes_time_t, nes_time_t );
@ -73,15 +73,15 @@ static inline void Square_reset( struct Nes_Square* this )
void Square_clock_envelope( struct Nes_Square* this );
int Square_volume( struct Nes_Square* this );
// Nes_Triangle
enum { Triangle_phase_range = 16 };
struct Nes_Triangle
{
struct Nes_Osc osc;
int phase;
int linear_counter;
struct Blip_Synth synth;
@ -123,11 +123,11 @@ static inline void Noise_reset( struct Nes_Noise* this )
// Nes_Dmc
enum { loop_flag = 0x40 };
struct Nes_Dmc
{
struct Nes_Osc osc;
int address; // address of next byte to read
int period;
int buf;
@ -144,7 +144,7 @@ struct Nes_Dmc
bool pal_mode;
bool nonlinear;
int (*prg_reader)( void*, addr_t ); // needs to be initialized to prg read function
int (*prg_reader)( void*, int ); // needs to be initialized to prg read function
void* prg_reader_data;
struct Nes_Apu* apu;

View file

@ -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 );
}

View file

@ -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;

View file

@ -1,4 +1,4 @@
// Game_Music_Emu 0.5.5. http://www.slack.net/~ant/
// Game_Music_Emu 0.6-pre. http://www.slack.net/~ant/
#include "nsf_emu.h"
#include "multi_buffer.h"
@ -19,33 +19,19 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
#include "blargg_source.h"
const char gme_wrong_file_type [] = "Wrong file type for this emulator";
long const clock_divisor = 12;
int const stereo = 2; // number of channels for stereo
int const silence_max = 6; // seconds
int const silence_threshold = 0x10;
long const fade_block_size = 512;
int const fade_shift = 8; // fade ends with gain at 1.0 / (1 << fade_shift)
// number of frames until play interrupts init
int const initial_play_delay = 7; // KikiKaikai needed this to work
int const bank_size = 0x1000;
int const rom_addr = 0x8000;
static void clear_track_vars( struct Nsf_Emu* this )
{
this->current_track = -1;
this->out_time = 0;
this->emu_time = 0;
this->emu_track_ended_ = true;
this->track_ended = true;
this->fade_start = INT_MAX / 2 + 1;
this->fade_step = 1;
this->silence_time = 0;
this->silence_count = 0;
this->buf_remain = 0;
track_stop( &this->track_filter );
}
static int pcm_read( void* emu, addr_t addr )
static int pcm_read( void* emu, int addr )
{
return *Cpu_get_code( &((struct Nsf_Emu*) emu)->cpu, addr );
}
@ -58,18 +44,16 @@ void Nsf_init( struct Nsf_Emu* this )
this->gain = (int)(FP_ONE_GAIN);
// defaults
this->max_initial_silence = 2;
this->ignore_silence = false;
this->voice_count = 0;
this->tfilter = *track_get_setup( &this->track_filter );
this->tfilter.max_initial = 2;
this->tfilter.lookahead = 6;
this->track_filter.silence_ignored_ = false;
// Set sound gain
Sound_set_gain( this, (int)(FP_ONE_GAIN*1.2) );
// Unload
clear_track_vars( this );
// Init rom
Rom_init( &this->rom, 0x1000 );
Rom_init( &this->rom, bank_size );
// Init & clear nsfe info
Info_init( &this->info );
@ -78,16 +62,36 @@ void Nsf_init( struct Nsf_Emu* this )
Cpu_init( &this->cpu );
Apu_init( &this->apu );
Apu_dmc_reader( &this->apu, pcm_read, this );
// Unload
this->voice_count = 0;
memset( this->voice_types, 0, sizeof this->voice_types );
clear_track_vars( this );
}
// Setup
static void append_voices( struct Nsf_Emu* this, int const types [], int count )
{
assert( this->voice_count + count < max_voices );
int i;
for ( i = 0; i < count; i++ ) {
this->voice_types [this->voice_count + i] = types [i];
}
this->voice_count += count;
}
static blargg_err_t init_sound( struct Nsf_Emu* this )
{
/* if ( header_.chip_flags & ~(fds_flag | namco_flag | vrc6_flag | fme7_flag) )
/* if ( header_.chip_flags & ~(fds_flag | namco_flag | vrc6_flag | fme7_flag) )
warning( "Uses unsupported audio expansion hardware" ); **/
this->voice_count = apu_osc_count;
{
static int const types [apu_osc_count] = {
wave_type+1, wave_type+2, mixed_type+1, noise_type+0, mixed_type+1
};
append_voices( this, types, apu_osc_count );
}
int adjusted_gain = (this->gain * 4) / 3;
@ -103,7 +107,10 @@ static blargg_err_t init_sound( struct Nsf_Emu* this )
Vrc6_init( &this->vrc6 );
adjusted_gain = (adjusted_gain*3) / 4;
this->voice_count += vrc6_osc_count;
static int const types [vrc6_osc_count] = {
wave_type+3, wave_type+4, wave_type+5,
};
append_voices( this, types, vrc6_osc_count );
}
if ( fme7_enabled( this ) )
@ -111,7 +118,10 @@ static blargg_err_t init_sound( struct Nsf_Emu* this )
Fme7_init( &this->fme7 );
adjusted_gain = (adjusted_gain*3) / 4;
this->voice_count += fme7_osc_count;
static int const types [fme7_osc_count] = {
wave_type+3, wave_type+4, wave_type+5,
};
append_voices( this, types, fme7_osc_count );
}
if ( mmc5_enabled( this ) )
@ -119,7 +129,11 @@ static blargg_err_t init_sound( struct Nsf_Emu* this )
Mmc5_init( &this->mmc5 );
adjusted_gain = (adjusted_gain*3) / 4;
this->voice_count += mmc5_osc_count;
static int const types [mmc5_osc_count] = {
wave_type+3, wave_type+4, mixed_type+2
};
append_voices( this, types, mmc5_osc_count );
}
if ( fds_enabled( this ) )
@ -127,7 +141,10 @@ static blargg_err_t init_sound( struct Nsf_Emu* this )
Fds_init( &this->fds );
adjusted_gain = (adjusted_gain*3) / 4;
this->voice_count += fds_osc_count ;
static int const types [fds_osc_count] = {
wave_type+0
};
append_voices( this, types, fds_osc_count );
}
if ( namco_enabled( this ) )
@ -135,22 +152,31 @@ static blargg_err_t init_sound( struct Nsf_Emu* this )
Namco_init( &this->namco );
adjusted_gain = (adjusted_gain*3) / 4;
this->voice_count += namco_osc_count;
static int const types [namco_osc_count] = {
wave_type+3, wave_type+4, wave_type+5, wave_type+ 6,
wave_type+7, wave_type+8, wave_type+9, wave_type+10,
};
append_voices( this, types, namco_osc_count );
}
if ( vrc7_enabled( this ) )
{
#ifndef NSF_EMU_NO_VRC7
if ( vrc7_enabled( this ) )
{
Vrc7_init( &this->vrc7 );
Vrc7_set_rate( &this->vrc7, this->sample_rate );
#endif
adjusted_gain = (adjusted_gain*3) / 4;
adjusted_gain = (adjusted_gain*3) / 4;
static int const types [vrc7_osc_count] = {
wave_type+3, wave_type+4, wave_type+5, wave_type+6,
wave_type+7, wave_type+8
};
append_voices( this, types, vrc7_osc_count );
}
this->voice_count += vrc7_osc_count;
}
if ( vrc7_enabled( this ) ) Vrc7_volume( &this->vrc7, adjusted_gain );
if ( vrc7_enabled( this ) ) Vrc7_volume( &this->vrc7, adjusted_gain );
#endif
if ( namco_enabled( this ) ) Namco_volume( &this->namco, adjusted_gain );
if ( vrc6_enabled( this ) ) Vrc6_volume( &this->vrc6, adjusted_gain );
if ( fme7_enabled( this ) ) Fme7_volume( &this->fme7, adjusted_gain );
@ -179,9 +205,9 @@ static bool pal_only( struct header_t* this )
return (this->speed_flags & 3) == 1;
}
static long clock_rate( struct header_t* this )
static int clock_rate( struct header_t* this )
{
return pal_only( this ) ? (long)1662607.125 : (long)1789772.727272727;
return pal_only( this ) ? (int)1662607.125 : (int)1789772.727272727;
}
static int play_period( struct header_t* this )
@ -227,7 +253,7 @@ static blargg_err_t check_nsf_header( struct header_t* h )
return 0;
}
blargg_err_t Nsf_load( struct Nsf_Emu* this, void* data, long size )
blargg_err_t Nsf_load_mem( struct Nsf_Emu* this, void* data, long size )
{
// Unload
Info_unload( &this->info ); // TODO: extremely hacky!
@ -258,11 +284,10 @@ blargg_err_t Nsf_post_load( struct Nsf_Emu* this )
warning( "Unknown file version" ); */
// set up data
addr_t load_addr = get_le16( this->header.load_addr );
addr_t load_addr = get_addr( this->header.load_addr );
/* if ( load_addr < (fds_enabled() ? sram_addr : rom_addr) )
warning( "Load address is too low" ); */
Rom_set_addr( &this->rom, load_addr % this->rom.bank_size );
/* if ( header_.vers != 1 )
@ -277,17 +302,16 @@ blargg_err_t Nsf_post_load( struct Nsf_Emu* this )
// Post load
Sound_set_tempo( this, this->tempo );
// Remute voices
Sound_mute_voices( this, this->mute_mask_ );
// Set track_count
this->track_count = this->header.track_count;
// Change clock rate & setup buffer
this->clock_rate__ = (long) clock_rate( &this->header );
this->clock_rate__ = clock_rate( &this->header );
Buffer_clock_rate( &this->stereo_buf, this->clock_rate__ );
this->buf_changed_count = Buffer_channels_changed_count( &this->stereo_buf );
RETURN_ERR( Buffer_set_channel_count( &this->stereo_buf, this->voice_count, this->voice_types ) );
this->buf_changed_count = Buffer_channels_changed_count( &this->stereo_buf );
return 0;
}
@ -416,11 +440,13 @@ static void set_voice( struct Nsf_Emu* this, int i, struct Blip_Buffer* buf, str
return;
}
if ( vrc7_enabled( this ) && (i -= vrc7_osc_count) < 0 )
{
Vrc7_set_output( &this->vrc7, i + vrc7_osc_count, buf );
return;
}
#ifndef NSF_EMU_NO_VRC7
if ( vrc7_enabled( this ) && (i -= vrc7_osc_count) < 0 )
{
Vrc7_set_output( &this->vrc7, i + vrc7_osc_count, buf );
return;
}
#endif
}
#endif
}
@ -429,7 +455,7 @@ static void set_voice( struct Nsf_Emu* this, int i, struct Blip_Buffer* buf, str
// Music Emu
blargg_err_t Nsf_set_sample_rate( struct Nsf_Emu* this, long rate )
blargg_err_t Nsf_set_sample_rate( struct Nsf_Emu* this, int rate )
{
require( !this->sample_rate ); // sample rate can't be changed once set
Buffer_init( &this->stereo_buf );
@ -439,6 +465,8 @@ blargg_err_t Nsf_set_sample_rate( struct Nsf_Emu* this, long rate )
Buffer_bass_freq( &this->stereo_buf, 80 );
this->sample_rate = rate;
RETURN_ERR( track_init( &this->track_filter, this ) );
this->tfilter.max_silence = 6 * stereo * this->sample_rate;
return 0;
}
@ -466,7 +494,7 @@ void Sound_mute_voices( struct Nsf_Emu* this, int mask )
}
else
{
struct channel_t ch = Buffer_channel( &this->stereo_buf );
struct channel_t ch = Buffer_channel( &this->stereo_buf, i );
assert( (ch.center && ch.left && ch.right) ||
(!ch.center && !ch.left && !ch.right) ); // all or nothing
set_voice( this, i, ch.center, ch.left, ch.right );
@ -533,32 +561,13 @@ int cpu_read( struct Nsf_Emu* this, addr_t addr )
return addr >> 8;
}
#if 0 /* function currently unused */
static int unmapped_read( struct Nsf_Emu* this, addr_t addr )
{
(void) this;
switch ( addr )
{
case 0x2002:
case 0x4016:
case 0x4017:
return addr >> 8;
}
// Unmapped read
return addr >> 8;
}
#endif
void cpu_write( struct Nsf_Emu* this, addr_t addr, int data )
{
#ifndef NSF_EMU_APU_ONLY
{
nes_time_t time = Cpu_time( &this->cpu );
if ( fds_enabled( this) && (unsigned) (addr - fds_io_addr) < fds_io_size )
{
Fds_write( &this->fds, time, addr, data );
Fds_write( &this->fds, Cpu_time( &this->cpu ), addr, data );
return;
}
@ -572,7 +581,7 @@ void cpu_write( struct Nsf_Emu* this, addr_t addr, int data )
if ( addr == namco_data_reg_addr )
{
Namco_write_data( &this->namco, time, data );
Namco_write_data( &this->namco, Cpu_time( &this->cpu ), data );
return;
}
}
@ -583,7 +592,7 @@ void cpu_write( struct Nsf_Emu* this, addr_t addr, int data )
int osc = (unsigned) (addr - vrc6_base_addr) / vrc6_addr_step;
if ( (unsigned) osc < vrc6_osc_count && (unsigned) reg < vrc6_reg_count )
{
Vrc6_write_osc( &this->vrc6, time, osc, reg, data );
Vrc6_write_osc( &this->vrc6, Cpu_time( &this->cpu ), osc, reg, data );
return;
}
}
@ -597,7 +606,7 @@ void cpu_write( struct Nsf_Emu* this, addr_t addr, int data )
return;
case fme7_data_addr:
Fme7_write_data( &this->fme7, time, data );
Fme7_write_data( &this->fme7, Cpu_time( &this->cpu ), data );
return;
}
}
@ -606,7 +615,7 @@ void cpu_write( struct Nsf_Emu* this, addr_t addr, int data )
{
if ( (unsigned) (addr - mmc5_regs_addr) < mmc5_regs_size )
{
Mmc5_write_register( &this->mmc5, time, addr, data );
Mmc5_write_register( &this->mmc5, Cpu_time( &this->cpu ), addr, data );
return;
}
@ -625,49 +634,28 @@ void cpu_write( struct Nsf_Emu* this, addr_t addr, int data )
}
}
if ( vrc7_enabled( this) )
{
if ( addr == 0x9010 )
#ifndef NSF_EMU_NO_VRC7
if ( vrc7_enabled( this) )
{
Vrc7_write_reg( &this->vrc7, data );
return;
}
if ( addr == 0x9010 )
{
Vrc7_write_reg( &this->vrc7, data );
return;
}
if ( (unsigned) (addr - 0x9028) <= 0x08 )
{
Vrc7_write_data( &this->vrc7, time, data );
return;
if ( (unsigned) (addr - 0x9028) <= 0x08 )
{
Vrc7_write_data( &this->vrc7, Cpu_time( &this->cpu ), data );
return;
}
}
}
#endif
}
#endif
// Unmapped_write
}
#if 0 /* function currently unused */
static void unmapped_write( struct Nsf_Emu* this, addr_t addr, int data )
{
(void) data;
switch ( addr )
{
case 0x8000: // some write to $8000 and $8001 repeatedly
case 0x8001:
case 0x4800: // probably namco sound mistakenly turned on in MCK
case 0xF800:
case 0xFFF8: // memory mapper?
return;
}
if ( mmc5_enabled( this ) && addr == 0x5115 ) return;
// FDS memory
if ( fds_enabled( this ) && (unsigned) (addr - 0x8000) < 0x6000 ) return;
}
#endif
void fill_buf( struct Nsf_Emu* this );
blargg_err_t Nsf_start_track( struct Nsf_Emu* this, int track )
{
clear_track_vars( this );
@ -695,7 +683,9 @@ blargg_err_t Nsf_start_track( struct Nsf_Emu* this, int track )
if ( vrc6_enabled( this ) ) Vrc6_reset( &this->vrc6 );
if ( fme7_enabled( this ) ) Fme7_reset( &this->fme7 );
if ( mmc5_enabled( this ) ) Apu_reset( &this->mmc5.apu, false, 0 );
if ( vrc7_enabled( this ) ) Vrc7_reset( &this->vrc7 );
#ifndef NSF_EMU_NO_VRC7
if ( vrc7_enabled( this ) ) Vrc7_reset( &this->vrc7 );
#endif
#endif
int speed_flags = 0;
@ -728,27 +718,15 @@ blargg_err_t Nsf_start_track( struct Nsf_Emu* this, int track )
/* if ( this->cpu.r.pc < get_addr( header.load_addr ) )
warning( "Init address < load address" ); */
this->emu_track_ended_ = false;
this->track_ended = false;
// convert filter times to samples
struct setup_t s = this->tfilter;
s.max_initial *= this->sample_rate * stereo;
#ifdef GME_DISABLE_SILENCE_LOOKAHEAD
s.lookahead = 1;
#endif
track_setup( &this->track_filter, &s );
if ( !this->ignore_silence )
{
// play until non-silence or end of track
long end;
for ( end = this->max_initial_silence * stereo * this->sample_rate; this->emu_time < end; )
{
fill_buf( this );
if ( this->buf_remain | (int) this->emu_track_ended_ )
break;
}
this->emu_time = this->buf_remain;
this->out_time = 0;
this->silence_time = 0;
this->silence_count = 0;
}
/* return track_ended() ? warning() : 0; */
return 0;
return track_start( &this->track_filter );
}
void run_once( struct Nsf_Emu* this, nes_time_t end )
@ -831,265 +809,99 @@ static void end_frame( struct Nsf_Emu* this, nes_time_t end )
if ( mmc5_enabled( this ) ) Apu_end_frame( &this->mmc5.apu, end );
if ( namco_enabled( this ) ) Namco_end_frame( &this->namco, end );
if ( vrc6_enabled( this ) ) Vrc6_end_frame( &this->vrc6, end );
#ifndef NSF_EMU_NO_VRC7
if ( vrc7_enabled( this ) ) Vrc7_end_frame( &this->vrc7, end );
#endif
#endif
}
// Tell/Seek
static blargg_long msec_to_samples( long sample_rate, blargg_long msec )
static int msec_to_samples( int msec, int sample_rate )
{
blargg_long sec = msec / 1000;
int sec = msec / 1000;
msec -= sec * 1000;
return (sec * sample_rate + msec * sample_rate / 1000) * stereo;
}
long Track_tell( struct Nsf_Emu* this )
int Track_tell( struct Nsf_Emu* this )
{
blargg_long rate = this->sample_rate * stereo;
blargg_long sec = this->out_time / rate;
return sec * 1000 + (this->out_time - sec * rate) * 1000 / rate;
int rate = this->sample_rate * stereo;
int sec = track_sample_count( &this->track_filter ) / rate;
return sec * 1000 + (track_sample_count( &this->track_filter ) - sec * rate) * 1000 / rate;
}
blargg_err_t Track_seek( struct Nsf_Emu* this, long msec )
blargg_err_t Track_seek( struct Nsf_Emu* this, int msec )
{
blargg_long time = msec_to_samples( this->sample_rate, msec );
if ( time < this->out_time )
RETURN_ERR( Nsf_start_track( this, this->current_track ) );
return Track_skip( this, time - this->out_time );
int time = msec_to_samples( msec, this->sample_rate );
if ( time < track_sample_count( &this->track_filter ) )
RETURN_ERR( Nsf_start_track( this, this->current_track ) );
return Track_skip( this, time - track_sample_count( &this->track_filter ) );
}
blargg_err_t skip_( struct Nsf_Emu* this, long count );
blargg_err_t Track_skip( struct Nsf_Emu* this, long count )
blargg_err_t Track_skip( struct Nsf_Emu* this, int count )
{
require( this->current_track >= 0 ); // start_track() must have been called already
this->out_time += count;
// remove from silence and buf first
{
long n = min( count, this->silence_count );
this->silence_count -= n;
count -= n;
n = min( count, this->buf_remain );
this->buf_remain -= n;
count -= n;
}
if ( count && !this->emu_track_ended_ )
{
this->emu_time += count;
// End track if error
if ( skip_( this, count ) )
this->emu_track_ended_ = true;
}
if ( !(this->silence_count | this->buf_remain) ) // caught up to emulator, so update track ended
this->track_ended |= this->emu_track_ended_;
return 0;
return track_skip( &this->track_filter, count );
}
blargg_err_t play_( struct Nsf_Emu* this, long count, sample_t* out );
blargg_err_t skip_( struct Nsf_Emu* this, long count )
blargg_err_t skip_( void *emu, int count )
{
struct Nsf_Emu* this = (struct Nsf_Emu*) emu;
// for long skip, mute sound
const long threshold = 30000;
const int threshold = 32768;
if ( count > threshold )
{
int saved_mute = this->mute_mask_;
Sound_mute_voices( this, ~0 );
while ( count > threshold / 2 && !this->emu_track_ended_ )
{
RETURN_ERR( play_( this, buf_size, this->buf ) );
count -= buf_size;
}
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;
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;
struct Nsf_Emu* this = (struct Nsf_Emu*) emu;
int remain = count;
while ( remain )
{
Buffer_disable_immediate_removal( &this->stereo_buf );
remain -= Buffer_read_samples( &this->stereo_buf, &out [count - remain], remain );
if ( remain )
{
if ( this->buf_changed_count != Buffer_channels_changed_count( &this->stereo_buf ) )
{
this->buf_changed_count = Buffer_channels_changed_count( &this->stereo_buf );
// Remute voices
Sound_mute_voices( this, this->mute_mask_ );
}
int msec = Buffer_length( &this->stereo_buf );
blip_time_t clocks_emulated = (blargg_long) msec * this->clock_rate__ / 1000 - 100;
blip_time_t clocks_emulated = msec * this->clock_rate__ / 1000 - 100;
RETURN_ERR( run_clocks( this, &clocks_emulated, msec ) );
assert( clocks_emulated );
Buffer_end_frame( &this->stereo_buf, clocks_emulated );

View file

@ -10,6 +10,7 @@
#include "nes_cpu.h"
#include "nsfe_info.h"
#include "m3u_playlist.h"
#include "track_filter.h"
#ifndef NSF_EMU_APU_ONLY
#include "nes_namco_apu.h"
@ -17,11 +18,11 @@
#include "nes_fme7_apu.h"
#include "nes_fds_apu.h"
#include "nes_mmc5_apu.h"
#include "nes_vrc7_apu.h"
#ifndef NSF_EMU_NO_VRC7
#include "nes_vrc7_apu.h"
#endif
#endif
typedef short sample_t;
// Sound chip flags
enum {
vrc6_flag = 1 << 0,
@ -50,8 +51,7 @@ enum { fdsram_size = 0x6000 };
enum { fdsram_offset = 0x2000 + page_size + 8 };
enum { sram_addr = 0x6000 };
enum { unmapped_size= page_size + 8 };
enum { buf_size = 2048 };
enum { max_voices = 32 };
// NSF file header
enum { header_size = 0x80 };
@ -82,37 +82,21 @@ struct Nsf_Emu {
int play_extra;
int play_delay;
struct registers_t saved_state; // of interrupted init routine
int track_count;
// general
int max_initial_silence;
int voice_count;
int voice_types [32];
int mute_mask_;
int tempo;
int gain;
long sample_rate;
int sample_rate;
// track-specific
int track_count;
int current_track;
blargg_long out_time; // number of samples played since start of track
blargg_long emu_time; // number of samples emulator has generated since start of track
bool emu_track_ended_; // emulator has reached end of track
volatile bool track_ended;
// fading
blargg_long fade_start;
int fade_step;
// silence detection
int silence_lookahead; // speed to run emulator when looking ahead for silence
bool ignore_silence;
long silence_time; // number of samples where most recent silence began
long silence_count; // number of samples of silence to play before using buf
long buf_remain; // number of samples left in silence buffer
long clock_rate__;
int clock_rate__;
unsigned buf_changed_count;
// M3u Playlist
@ -127,7 +111,9 @@ struct Nsf_Emu {
struct Nes_Namco_Apu namco;
struct Nes_Vrc6_Apu vrc6;
struct Nes_Fme7_Apu fme7;
struct Nes_Vrc7_Apu vrc7;
#ifndef NSF_EMU_NO_VRC7
struct Nes_Vrc7_Apu vrc7;
#endif
#endif
struct Nes_Cpu cpu;
@ -136,13 +122,15 @@ struct Nsf_Emu {
// Header for currently loaded file
struct header_t header;
struct Stereo_Buffer stereo_buf;
struct setup_t tfilter;
struct Track_Filter track_filter;
struct Multi_Buffer stereo_buf;
struct Rom_Data rom;
// Extended nsf info
struct Nsfe_Info info;
sample_t buf [buf_size];
byte high_ram[fdsram_size + fdsram_offset];
byte low_ram [low_ram_size];
};
@ -150,18 +138,18 @@ struct Nsf_Emu {
// Basic functionality (see Gme_File.h for file loading/track info functions)
void Nsf_init( struct Nsf_Emu* this );
blargg_err_t Nsf_load( struct Nsf_Emu* this, void* data, long size );
blargg_err_t Nsf_load_mem( struct Nsf_Emu* this, void* data, long size );
blargg_err_t Nsf_post_load( struct Nsf_Emu* this );
// Set output sample rate. Must be called only once before loading file.
blargg_err_t Nsf_set_sample_rate( struct Nsf_Emu* this, long sample_rate );
blargg_err_t Nsf_set_sample_rate( struct Nsf_Emu* this, int sample_rate );
// Start a track, where 0 is the first track. Also clears warning string.
blargg_err_t Nsf_start_track( struct Nsf_Emu* this , int );
// Generate 'count' samples info 'buf'. Output is in stereo. Any emulation
// errors set warning string, and major errors also end track.
blargg_err_t Nsf_play( struct Nsf_Emu* this, long count, sample_t* buf );
blargg_err_t Nsf_play( struct Nsf_Emu* this, int count, sample_t* buf );
void Nsf_clear_playlist( struct Nsf_Emu* this );
void Nsf_disable_playlist( struct Nsf_Emu* this, bool b ); // use clear_playlist()
@ -169,20 +157,32 @@ void Nsf_disable_playlist( struct Nsf_Emu* this, bool b ); // use clear_playlist
// Track status/control
// Number of milliseconds (1000 msec = 1 second) played since beginning of track
long Track_tell( struct Nsf_Emu* this );
int Track_tell( struct Nsf_Emu* this );
// Seek to new time in track. Seeking backwards or far forward can take a while.
blargg_err_t Track_seek( struct Nsf_Emu* this, long msec );
blargg_err_t Track_seek( struct Nsf_Emu* this, int msec );
// Skip n samples
blargg_err_t Track_skip( struct Nsf_Emu* this, long n );
blargg_err_t Track_skip( struct Nsf_Emu* this, int n );
// Set start time and length of track fade out. Once fade ends track_ended() returns
// true. Fade time can be changed while track is playing.
void Track_set_fade( struct Nsf_Emu* this, long start_msec, long length_msec );
void Track_set_fade( struct Nsf_Emu* this, int start_msec, int length_msec );
// True if a track has reached its end
static inline bool Track_ended( struct Nsf_Emu* this )
{
return track_ended( &this->track_filter );
}
// Disables automatic end-of-track detection and skipping of silence at beginning
static inline void Track_ignore_silence( struct Nsf_Emu* this, bool disable )
{
this->track_filter.silence_ignored_ = disable;
}
// Get track length in milliseconds
long Track_length( struct Nsf_Emu* this, int n );
int Track_length( struct Nsf_Emu* this, int n );
// Sound customization
@ -217,28 +217,28 @@ bool run_cpu_until( struct Nsf_Emu* this, nes_time_t end );
// Sets clocks between calls to play routine to p + 1/2 clock
static inline void set_play_period( struct Nsf_Emu* this, int p ) { this->play_period = p; }
// Time play routine will next be called
static inline nes_time_t play_time( struct Nsf_Emu* this ) { return this->next_play; }
// Emulates to at least time t. Might emulate a few clocks extra.
void run_until( struct Nsf_Emu* this, nes_time_t t );
// Runs cpu to at least time t and returns false, or returns true
// if it encounters illegal instruction (halt).
bool run_cpu_until( struct Nsf_Emu* this, nes_time_t t );
// cpu calls through to these to access memory (except instructions)
int read_mem( struct Nsf_Emu* this, addr_t );
void write_mem( struct Nsf_Emu* this, addr_t, int );
// Address of play routine
static inline addr_t play_addr( struct Nsf_Emu* this ) { return get_addr( this->header.play_addr ); }
// Same as run_until, except emulation stops for any event (routine returned,
// play routine called, illegal instruction).
void run_once( struct Nsf_Emu* this, nes_time_t );
// Reads byte as cpu would when executing code. Only works for RAM/ROM,
// NOT I/O like sound chips.
int read_code( struct Nsf_Emu* this, addr_t addr );
@ -248,12 +248,14 @@ static inline byte* sram( struct Nsf_Emu* this ) { return this->high_
static inline byte* unmapped_code( struct Nsf_Emu* this ) { return &this->high_ram [sram_size]; }
#ifndef NSF_EMU_APU_ONLY
static inline int fds_enabled( struct Nsf_Emu* this ) { return this->header.chip_flags & fds_flag; }
static inline int vrc6_enabled( struct Nsf_Emu* this ) { return this->header.chip_flags & vrc6_flag; }
static inline int vrc7_enabled( struct Nsf_Emu* this ) { return this->header.chip_flags & vrc7_flag; }
static inline int mmc5_enabled( struct Nsf_Emu* this ) { return this->header.chip_flags & mmc5_flag; }
static inline int namco_enabled( struct Nsf_Emu* this ) { return this->header.chip_flags & namco_flag; }
static inline int fme7_enabled( struct Nsf_Emu* this ) { return this->header.chip_flags & fme7_flag; }
static inline int fds_enabled( struct Nsf_Emu* this ) { return this->header.chip_flags & fds_flag; }
static inline int vrc6_enabled( struct Nsf_Emu* this ) { return this->header.chip_flags & vrc6_flag; }
#ifndef NSF_EMU_NO_VRC7
static inline int vrc7_enabled( struct Nsf_Emu* this ) { return this->header.chip_flags & vrc7_flag; }
#endif
static inline int mmc5_enabled( struct Nsf_Emu* this ) { return this->header.chip_flags & mmc5_flag; }
static inline int namco_enabled( struct Nsf_Emu* this ) { return this->header.chip_flags & namco_flag; }
static inline int fme7_enabled( struct Nsf_Emu* this ) { return this->header.chip_flags & fme7_flag; }
#endif
#endif

View file

@ -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;

View file

@ -37,10 +37,10 @@ struct Opl_Apu {
blargg_err_t Opl_init( struct Opl_Apu* this, long clock, long rate, blip_time_t period, enum opl_type_t type );
void Opl_shutdown( struct Opl_Apu* this );
void Opl_reset( struct Opl_Apu* this );
static inline void Opl_volume( struct Opl_Apu* this, int v ) { Synth_volume( &this->synth, v / (4096 * 6) ); }
static inline void Opl_osc_output( struct Opl_Apu* this, int i, struct Blip_Buffer* buf )
{
#if defined(ROCKBOX)

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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];

View file

@ -10,34 +10,19 @@ module is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
details. You should have received a copy of the GNU Lesser General Public
License along with this module; if not, write to the Free Software Foundation,
License aint with this module; if not, write to the Free Software Foundation,
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
#include "blargg_source.h"
int const osc_count = sms_osc_count + fm_apu_osc_count;
int const stereo = 2; // number of channels for stereo
int const silence_max = 6; // seconds
int const silence_threshold = 0x10;
long const fade_block_size = 512;
int const fade_shift = 8; // fade ends with gain at 1.0 / (1 << fade_shift)
const char gme_wrong_file_type [] = "Wrong file type for this emulator";
static void clear_track_vars( struct Sgc_Emu* this )
{
this->current_track = -1;
this->out_time = 0;
this->emu_time = 0;
this->emu_track_ended_ = true;
this->track_ended = true;
this->fade_start = INT_MAX / 2 + 1;
this->fade_step = 1;
this->silence_time = 0;
this->silence_count = 0;
this->buf_remain = 0;
/* warning(); // clear warning */
track_stop( &this->track_filter );
}
void Sgc_init( struct Sgc_Emu* this )
@ -48,12 +33,12 @@ void Sgc_init( struct Sgc_Emu* this )
this->mute_mask_ = 0;
this->tempo = (int)FP_ONE_TEMPO;
this->gain = (int)FP_ONE_GAIN;
this->voice_count = 0;
// defaults
this->max_initial_silence = 2;
this->silence_lookahead = 6;
this->ignore_silence = false;
this->tfilter = *track_get_setup( &this->track_filter );
this->tfilter.max_initial = 2;
this->tfilter.lookahead = 6;
this->track_filter.silence_ignored_ = false;
Sms_apu_init( &this->apu );
Fm_apu_create( &this->fm_apu );
@ -62,8 +47,10 @@ void Sgc_init( struct Sgc_Emu* this )
Z80_init( &this->cpu );
Sound_set_gain( this, (int)(FP_ONE_GAIN*1.2) );
// Unload
this->voice_count = 0;
this->voice_types = 0;
clear_track_vars( this );
}
@ -95,6 +82,10 @@ blargg_err_t Sgc_load_mem( struct Sgc_Emu* this, const void* data, long size )
this->m3u.size = 0;
this->track_count = this->header.song_count;
this->voice_count = sega_mapping( this ) ? osc_count : sms_osc_count;
static int const types [osc_count + 1] = {
wave_type+1, wave_type+2, wave_type+3, mixed_type+1, mixed_type+2
};
this->voice_types = types;
Sms_apu_volume( &this->apu, this->gain );
Fm_apu_volume( &this->fm_apu, this->gain );
@ -102,10 +93,10 @@ blargg_err_t Sgc_load_mem( struct Sgc_Emu* this, const void* data, long size )
// Setup buffer
this->clock_rate_ = clock_rate( this );
Buffer_clock_rate( &this->stereo_buf, clock_rate( this ) );
RETURN_ERR( Buffer_set_channel_count( &this->stereo_buf, this->voice_count, this->voice_types ) );
this->buf_changed_count = Buffer_channels_changed_count( &this->stereo_buf );
Sound_set_tempo( this, this->tempo );
// Remute voices
Sound_set_tempo( this, this->tempo );
Sound_mute_voices( this, this->mute_mask_ );
return 0;
}
@ -249,7 +240,7 @@ void cpu_write( struct Sgc_Emu* this, addr_t addr, int data )
}
}
blargg_err_t Sgc_set_sample_rate( struct Sgc_Emu* this, long rate )
blargg_err_t Sgc_set_sample_rate( struct Sgc_Emu* this, int rate )
{
require( !this->sample_rate ); // sample rate can't be changed once set
Buffer_init( &this->stereo_buf );
@ -259,6 +250,8 @@ blargg_err_t Sgc_set_sample_rate( struct Sgc_Emu* this, long rate )
Buffer_bass_freq( &this->stereo_buf, 80 );
this->sample_rate = rate;
RETURN_ERR( track_init( &this->track_filter, this ) );
this->tfilter.max_silence = 6 * stereo * this->sample_rate;
return 0;
}
@ -286,7 +279,7 @@ void Sound_mute_voices( struct Sgc_Emu* this, int mask )
}
else
{
struct channel_t ch = Buffer_channel( &this->stereo_buf );
struct channel_t ch = Buffer_channel( &this->stereo_buf, i );
assert( (ch.center && ch.left && ch.right) ||
(!ch.center && !ch.left && !ch.right) ); // all or nothing
Sound_set_voice( this, i, ch.center, ch.left, ch.right );
@ -306,7 +299,6 @@ void Sound_set_tempo( struct Sgc_Emu* this, int t )
this->play_period = (int) ((clock_rate( this ) * FP_ONE_TEMPO) / (this->header.rate ? 50 : 60) / t);
}
void fill_buf( struct Sgc_Emu* this );
blargg_err_t Sgc_start_track( struct Sgc_Emu* this, int track )
{
clear_track_vars( this );
@ -383,275 +375,90 @@ blargg_err_t Sgc_start_track( struct Sgc_Emu* this, int track )
Buffer_clear( &this->stereo_buf );
this->emu_track_ended_ = false;
this->track_ended = false;
if ( !this->ignore_silence )
{
// play until non-silence or end of track
long end;
for ( end = this->max_initial_silence * stereo * this->sample_rate; this->emu_time < end; )
{
fill_buf( this );
if ( this->buf_remain | (int) this->emu_track_ended_ )
break;
}
this->emu_time = this->buf_remain;
this->out_time = 0;
this->silence_time = 0;
this->silence_count = 0;
}
/* return track_ended() ? warning() : 0; */
return 0;
// convert filter times to samples
struct setup_t s = this->tfilter;
s.max_initial *= this->sample_rate * stereo;
#ifdef GME_DISABLE_SILENCE_LOOKAHEAD
s.lookahead = 1;
#endif
track_setup( &this->track_filter, &s );
return track_start( &this->track_filter );
}
// Tell/Seek
static blargg_long msec_to_samples( blargg_long msec, long sample_rate )
static int msec_to_samples( int msec, int sample_rate )
{
blargg_long sec = msec / 1000;
int sec = msec / 1000;
msec -= sec * 1000;
return (sec * sample_rate + msec * sample_rate / 1000) * stereo;
}
long Track_tell( struct Sgc_Emu* this )
int Track_tell( struct Sgc_Emu* this )
{
blargg_long rate = this->sample_rate * stereo;
blargg_long sec = this->out_time / rate;
return sec * 1000 + (this->out_time - sec * rate) * 1000 / rate;
int rate = this->sample_rate * stereo;
int sec = track_sample_count( &this->track_filter ) / rate;
return sec * 1000 + (track_sample_count( &this->track_filter ) - sec * rate) * 1000 / rate;
}
blargg_err_t Track_seek( struct Sgc_Emu* this, long msec )
blargg_err_t Track_seek( struct Sgc_Emu* this, int msec )
{
blargg_long time = msec_to_samples( msec, this->sample_rate );
if ( time < this->out_time )
RETURN_ERR( Sgc_start_track( this, this->current_track ) );
return Track_skip( this, time - this->out_time );
int time = msec_to_samples( msec, this->sample_rate );
if ( time < track_sample_count( &this->track_filter ) )
RETURN_ERR( Sgc_start_track( this, this->current_track ) );
return Track_skip( this, time - track_sample_count( &this->track_filter ) );
}
blargg_err_t skip_( struct Sgc_Emu* this, long count );
blargg_err_t Track_skip( struct Sgc_Emu* this, long count )
blargg_err_t Track_skip( struct Sgc_Emu* this, int count )
{
require( this->current_track >= 0 ); // start_track() must have been called already
this->out_time += count;
// remove from silence and buf first
{
long n = min( count, this->silence_count );
this->silence_count -= n;
count -= n;
n = min( count, this->buf_remain );
this->buf_remain -= n;
count -= n;
}
if ( count && !this->emu_track_ended_ )
{
this->emu_time += count;
// End track if error
if ( skip_( this, count ) ) {
this->emu_track_ended_ = true;
}
}
if ( !(this->silence_count | this->buf_remain) ) // caught up to emulator, so update track ended
this->track_ended |= this->emu_track_ended_;
return 0;
return track_skip( &this->track_filter, count );
}
blargg_err_t play_( struct Sgc_Emu* this, long count, sample_t* out );
blargg_err_t skip_( struct Sgc_Emu* this, long count )
blargg_err_t skip_( void* emu, int count )
{
struct Sgc_Emu* this = (struct Sgc_Emu*) emu;
// for long skip, mute sound
const long threshold = 30000;
const int threshold = 32768;
if ( count > threshold )
{
int saved_mute = this->mute_mask_;
Sound_mute_voices( this, ~0 );
while ( count > threshold / 2 && !this->emu_track_ended_ )
{
RETURN_ERR( play_( this, buf_size, this->buf ) );
count -= buf_size;
}
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 );
}
return skippy_( &this->track_filter, count );
}
void Track_set_fade( struct Sgc_Emu* this, int start_msec, int length_msec )
{
track_set_fade( &this->track_filter, msec_to_samples( start_msec, this->sample_rate ),
length_msec * this->sample_rate / (1000 / stereo) );
}
blargg_err_t Sgc_play( struct Sgc_Emu* this, int out_count, sample_t* out )
{
require( this->current_track >= 0 );
require( out_count % stereo == 0 );
return track_play( &this->track_filter, out_count, out );
}
blargg_err_t play_( void* emu, int count, sample_t out [] )
{
struct Sgc_Emu* this = (struct Sgc_Emu*) emu;
while ( count && !this->emu_track_ended_ )
{
long n = buf_size;
if ( n > count )
n = count;
count -= n;
RETURN_ERR( play_( this, n, this->buf ) );
}
return 0;
}
// Fading
void Track_set_fade( struct Sgc_Emu* this, long start_msec, long length_msec )
{
this->fade_step = this->sample_rate * length_msec / (fade_block_size * fade_shift * 1000 / stereo);
this->fade_start = msec_to_samples( start_msec, this->sample_rate );
}
// unit / pow( 2.0, (double) x / step )
static int int_log( blargg_long x, int step, int unit )
{
int shift = x / step;
int fraction = (x - shift * step) * unit / step;
return ((unit - fraction) + (fraction >> 1)) >> shift;
}
static void handle_fade( struct Sgc_Emu* this, long out_count, sample_t* out )
{
int i;
for ( i = 0; i < out_count; i += fade_block_size )
{
int const shift = 14;
int const unit = 1 << shift;
int gain = int_log( (this->out_time + i - this->fade_start) / fade_block_size,
this->fade_step, unit );
if ( gain < (unit >> fade_shift) )
this->track_ended = this->emu_track_ended_ = true;
sample_t* io = &out [i];
int count;
for ( count = min( fade_block_size, out_count - i ); count; --count )
{
*io = (sample_t) ((*io * gain) >> shift);
++io;
}
}
}
// Silence detection
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 )
{

View file

@ -12,16 +12,14 @@
#include "sms_fm_apu.h"
#include "sms_apu.h"
#include "m3u_playlist.h"
#include "track_filter.h"
typedef short sample_t;
typedef struct Z80_Cpu Sgc_Cpu;
enum { buf_size = 2048 };
// SGC file header
enum { header_size = 0xA0 };
struct header_t
{
{
char tag [4]; // "SGC\x1A"
byte vers; // 0x01
byte rate; // 0=NTSC 1=PAL
@ -48,12 +46,11 @@ struct header_t
static inline bool valid_tag( struct header_t* h )
{
return 0 == memcmp( h->tag, "SGC\x1A", 4 );
}
}
static inline int effect_count( struct header_t* h ) { return h->last_effect ? h->last_effect - h->first_effect + 1 : 0; }
struct Sgc_Emu {
struct Sgc_Emu {
bool fm_accessed;
cpu_time_t play_period;
@ -65,40 +62,28 @@ struct Sgc_Emu {
// general
int voice_count;
int const* voice_types;
int mute_mask_;
int tempo;
int gain;
long sample_rate;
int sample_rate;
// track-specific
volatile bool track_ended;
int current_track;
int track_count;
blargg_long out_time; // number of samples played since start of track
blargg_long emu_time; // number of samples emulator has generated since start of track
bool emu_track_ended_; // emulator has reached end of track
// fading
blargg_long fade_start;
int fade_step;
// silence detection
bool ignore_silence;
int max_initial_silence;
int silence_lookahead; // speed to run emulator when looking ahead for silence
long silence_time; // number of samples where most recent silence began
long silence_count; // number of samples of silence to play before using buf
long buf_remain; // number of samples left in silence buffer
long clock_rate_;
int clock_rate_;
unsigned buf_changed_count;
// M3u Playlist
struct M3u_Playlist m3u;
struct header_t header;
sample_t buf [buf_size];
struct Stereo_Buffer stereo_buf;
struct setup_t tfilter;
struct Track_Filter track_filter;
struct Multi_Buffer stereo_buf;
struct Sms_Apu apu;
struct Sms_Fm_Apu fm_apu;
@ -106,12 +91,11 @@ struct Sgc_Emu {
Sgc_Cpu cpu;
// large items
struct header_t header;
struct Rom_Data rom;
byte vectors [page_size + page_padding];
byte unmapped_write [0x4000];
byte ram [0x2000 + page_padding];
byte ram2 [0x4000 + page_padding];
byte unmapped_write [0x4000];
};
// Basic functionality (see Gme_File.h for file loading/track info functions)
@ -119,41 +103,53 @@ struct Sgc_Emu {
void Sgc_init( struct Sgc_Emu* this );
blargg_err_t Sgc_load_mem( struct Sgc_Emu* this, const void* data, long size );
static inline int clock_rate( struct Sgc_Emu* this ) { return this->header.rate ? 3546893 : 3579545; }
// 0x2000 bytes
static inline void set_coleco_bios( struct Sgc_Emu* this, void* p ) { this->coleco_bios = p; }
// Set output sample rate. Must be called only once before loading file.
blargg_err_t Sgc_set_sample_rate( struct Sgc_Emu* this, long sample_rate );
blargg_err_t Sgc_set_sample_rate( struct Sgc_Emu* this, int sample_rate );
// Start a track, where 0 is the first track. Also clears warning string.
blargg_err_t Sgc_start_track( struct Sgc_Emu* this, int track );
// Generate 'count' samples info 'buf'. Output is in stereo. Any emulation
// errors set warning string, and major errors also end track.
blargg_err_t Sgc_play( struct Sgc_Emu* this, long count, sample_t* buf );
blargg_err_t Sgc_play( struct Sgc_Emu* this, int count, sample_t* buf );
// Track status/control
// Number of milliseconds (1000 msec = 1 second) played since beginning of track
long Track_tell( struct Sgc_Emu* this );
int Track_tell( struct Sgc_Emu* this );
// Seek to new time in track. Seeking backwards or far forward can take a while.
blargg_err_t Track_seek( struct Sgc_Emu* this, long msec );
blargg_err_t Track_seek( struct Sgc_Emu* this, int msec );
// Skip n samples
blargg_err_t Track_skip( struct Sgc_Emu* this, long n );
blargg_err_t Track_skip( struct Sgc_Emu* this, int n );
// Set start time and length of track fade out. Once fade ends track_ended() returns
// true. Fade time can be changed while track is playing.
void Track_set_fade( struct Sgc_Emu* this, long start_msec, long length_msec );
void Track_set_fade( struct Sgc_Emu* this, int start_msec, int length_msec );
// True if a track has reached its end
static inline bool Track_ended( struct Sgc_Emu* this )
{
return track_ended( &this->track_filter );
}
// Disables automatic end-of-track detection and skipping of silence at beginning
static inline void Track_ignore_silence( struct Sgc_Emu* this, bool disable )
{
this->track_filter.silence_ignored_ = disable;
}
// Get track length in milliseconds
static inline long Track_get_length( struct Sgc_Emu* this, int n )
static inline int Track_get_length( struct Sgc_Emu* this, int n )
{
long length = 120 * 1000; /* 2 minutes */
int length = 120 * 1000; /* 2 minutes */
if ( (this->m3u.size > 0) && (n < this->m3u.size) ) {
struct entry_t* entry = &this->m3u.entries [n];
length = entry->length;

View file

@ -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;

View file

@ -44,10 +44,10 @@ void Sms_apu_set_output( struct Sms_Apu* this, int i, struct Blip_Buffer* center
// Emulates to time t, then writes data to Game Gear left/right assignment byte
void Sms_apu_write_ggstereo( struct Sms_Apu* this, blip_time_t t, int data );
// Emulates to time t, then writes data
void Sms_apu_write_data( struct Sms_Apu* this, blip_time_t t, int data );
// Emulates to time t, then subtracts t from the current time.
// OK if previous write call had time slightly after t.
void Sms_apu_end_frame( struct Sms_Apu* this, blip_time_t t );

View file

@ -25,19 +25,19 @@ void Fm_apu_create( struct Sms_Fm_Apu* this );
static inline bool Fm_apu_supported( void ) { return Ym2413_supported(); }
blargg_err_t Fm_apu_init( struct Sms_Fm_Apu* this, int clock_rate, int sample_rate );
static inline void Fm_apu_set_output( struct Sms_Fm_Apu* this, struct Blip_Buffer* b )
{
this->output_ = b;
}
static inline void Fm_apu_volume( struct Sms_Fm_Apu* this, int v ) { Synth_volume( &this->synth, (v*2) / 5 / 4096 ); }
void Fm_apu_reset( struct Sms_Fm_Apu* this );
static inline void Fm_apu_write_addr( struct Sms_Fm_Apu* this, int data ) { this->addr = data; }
void Fm_apu_write_data( struct Sms_Fm_Apu* this, blip_time_t, int data );
void Fm_apu_end_frame( struct Sms_Fm_Apu* this, blip_time_t t );
#endif

View 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;
}

View 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

View file

@ -23,11 +23,6 @@ const char* const gme_wrong_file_type = "Wrong file type for this emulator";
int const fm_gain = 3; // FM emulators are internally quieter to avoid 16-bit overflow
int const silence_max = 6; // seconds
int const silence_threshold = 0x10;
long const fade_block_size = 512;
int const fade_shift = 8; // fade ends with gain at 1.0 / (1 << fade_shift)
// VGM commands (Spec v1.50)
enum {
cmd_gg_stereo = 0x4F,
@ -53,15 +48,8 @@ enum {
static void clear_track_vars( struct Vgm_Emu* this )
{
this->out_time = 0;
this->emu_time = 0;
this->emu_track_ended_ = true;
this->track_ended = true;
this->fade_start = INT_MAX / 2 + 1;
this->fade_step = 1;
this->silence_time = 0;
this->silence_count = 0;
this->buf_remain = 0;
this->current_track = -1;
track_stop( &this->track_filter );
}
int play_frame( struct Vgm_Emu* this, blip_time_t blip_time, int sample_count, sample_t* buf );
@ -77,9 +65,10 @@ void Vgm_init( struct Vgm_Emu* this )
this->tempo = (int)(FP_ONE_TEMPO);
// defaults
this->max_initial_silence = 2;
this->silence_lookahead = 1; // tracks should already be trimmed
this->ignore_silence = false;
this->tfilter = *track_get_setup( &this->track_filter );
this->tfilter.max_initial = 2;
this->tfilter.lookahead = 1;
this->track_filter.silence_ignored_ = false;
// Disable oversampling by default
this->disable_oversampling = true;
@ -88,10 +77,10 @@ void Vgm_init( struct Vgm_Emu* this )
Sms_apu_init( &this->psg );
Synth_init( &this->pcm );
Buffer_init( &this->buf );
Buffer_init( &this->stereo_buf );
Blip_init( &this->blip_buf );
// Init fm chips
// Init fm chips
Ym2413_init( &this->ym2413 );
Ym2612_init( &this->ym2612 );
@ -105,7 +94,8 @@ void Vgm_init( struct Vgm_Emu* this )
// Unload
this->voice_count = 0;
clear_track_vars( this );
this->voice_types = 0;
clear_track_vars( this );
}
// Track info
@ -150,13 +140,13 @@ static void parse_gd3( byte const* in, byte const* end, struct track_info_t* out
int const gd3_header_size = 12;
static long check_gd3_header( byte const* h, long remain )
static int check_gd3_header( byte const* h, int remain )
{
if ( remain < gd3_header_size ) return 0;
if ( memcmp( h, "Gd3 ", 4 ) ) return 0;
if ( get_le32( h + 4 ) >= 0x200 ) return 0;
long gd3_size = get_le32( h + 8 );
int gd3_size = get_le32( h + 8 );
if ( gd3_size > remain - gd3_header_size )
gd3_size = remain - gd3_header_size;
return gd3_size;
@ -167,12 +157,12 @@ static byte const* gd3_data( struct Vgm_Emu* this, int* size )
if ( size )
*size = 0;
long gd3_offset = get_le32( header( this )->gd3_offset ) - 0x2C;
int gd3_offset = get_le32( header( this )->gd3_offset ) - 0x2C;
if ( gd3_offset < 0 )
return 0;
byte const* gd3 = this->file_begin + header_size + gd3_offset;
long gd3_size = check_gd3_header( gd3, this->file_end - gd3 );
int gd3_size = check_gd3_header( gd3, this->file_end - gd3 );
if ( !gd3_size )
return 0;
@ -184,10 +174,10 @@ static byte const* gd3_data( struct Vgm_Emu* this, int* size )
static void get_vgm_length( struct header_t const* h, struct track_info_t* out )
{
long length = get_le32( h->track_duration ) * 10 / 441;
int length = get_le32( h->track_duration ) * 10 / 441;
if ( length > 0 )
{
long loop = get_le32( h->loop_duration );
int loop = get_le32( h->loop_duration );
if ( loop > 0 && get_le32( h->loop_offset ) )
{
out->loop_length = loop * 10 / 441;
@ -285,24 +275,25 @@ blargg_err_t Vgm_load_mem( struct Vgm_Emu* this, byte const* new_data, long new_
Ym2612_enable( &this->ym2612, false );
Ym2413_enable( &this->ym2413, false );
Sound_set_tempo( this, (int)(FP_ONE_TEMPO) );
this->voice_count = sms_osc_count;
static int const types [8] = {
wave_type+1, wave_type+2, wave_type+3, noise_type+1,
0, 0, 0, 0
};
this->voice_types = types;
RETURN_ERR( setup_fm( this ) );
// do after FM in case output buffer is changed
// setup buffer
this->clock_rate_ = this->psg_rate;
Buffer_clock_rate( &this->buf, this->psg_rate );
// Setup bass
this->buf_changed_count = Buffer_channels_changed_count( &this->buf );
Buffer_clock_rate( &this->stereo_buf, this->psg_rate );
RETURN_ERR( Buffer_set_channel_count( &this->stereo_buf, this->voice_count, this->voice_types ) );
this->buf_changed_count = Buffer_channels_changed_count( &this->stereo_buf );
// Post load
Sound_set_tempo( this, this->tempo );
Sound_mute_voices( this, this->mute_mask_ );
return 0;
}
@ -362,42 +353,56 @@ blargg_err_t setup_fm( struct Vgm_Emu* this )
// Emulation
blip_time_t run( struct Vgm_Emu* this, vgm_time_t end_time );
static 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 );
}
}
return 0;
}
Resampler_play( &this->resampler, count, out, &this->blip_buf );
return 0;
}
@ -414,7 +419,7 @@ static inline int command_len( int command )
check( len != 1 );
return len;
}
static inline fm_time_t to_fm_time( struct Vgm_Emu* this, vgm_time_t t )
{
return (t * this->fm_time_factor + this->fm_time_offset) >> fm_time_bits;
@ -443,12 +448,10 @@ blip_time_t run( struct Vgm_Emu* this, vgm_time_t end_time )
{
vgm_time_t vgm_time = this->vgm_time;
byte const* pos = this->pos;
if ( pos >= this->file_end )
/* if ( pos > this->file_end )
{
this->emu_track_ended_ = true;
/* if ( pos > data_end )
warning( "Stream lacked end event" ); */
}
warning( "Stream lacked end event" );
} */
while ( vgm_time < end_time && pos < this->file_end )
{
@ -517,7 +520,7 @@ blip_time_t run( struct Vgm_Emu* this, vgm_time_t end_time )
case cmd_data_block: {
check( *pos == cmd_end );
int type = pos [1];
long size = get_le32( pos + 2 );
int size = get_le32( pos + 2 );
pos += 6;
if ( type == pcm_block_type )
this->pcm_data = pos;
@ -564,6 +567,8 @@ blip_time_t run( struct Vgm_Emu* this, vgm_time_t end_time )
int play_frame( struct Vgm_Emu* this, blip_time_t blip_time, int sample_count, blip_sample_t out [] )
{
check_end( this);
// to do: timing is working mostly by luck
int min_pairs = (unsigned) sample_count / 2;
int vgm_time = (min_pairs << fm_time_bits) / this->fm_time_factor - 1;
@ -642,16 +647,18 @@ void update_fm_rates( struct Vgm_Emu* this, int* ym2413_rate, int* ym2612_rate )
// Music Emu
blargg_err_t Vgm_set_sample_rate( struct Vgm_Emu* this, long rate )
blargg_err_t Vgm_set_sample_rate( struct Vgm_Emu* this, int rate )
{
require( !this->sample_rate ); // sample rate can't be changed once set
RETURN_ERR( Blip_set_sample_rate( &this->blip_buf, rate, 1000 / 30 ) );
RETURN_ERR( Buffer_set_sample_rate( &this->buf, rate, 1000 / 20 ) );
RETURN_ERR( Buffer_set_sample_rate( &this->stereo_buf, rate, 1000 / 20 ) );
// Set bass frequency
Buffer_bass_freq( &this->buf, 80 );
Buffer_bass_freq( &this->stereo_buf, 80 );
this->sample_rate = rate;
RETURN_ERR( track_init( &this->track_filter, this ) );
this->tfilter.max_silence = 6 * stereo * this->sample_rate;
return 0;
}
@ -679,7 +686,7 @@ void Sound_mute_voices( struct Vgm_Emu* this, int mask )
}
else
{
struct channel_t ch = Buffer_channel( &this->buf );
struct channel_t ch = Buffer_channel( &this->stereo_buf, i );
assert( (ch.center && ch.left && ch.right) ||
(!ch.center && !ch.left && !ch.right) ); // all or nothing
set_voice( this, i, ch.center, ch.left, ch.right );
@ -735,7 +742,6 @@ void Sound_set_tempo( struct Vgm_Emu* this, int t )
}
}
void fill_buf( struct Vgm_Emu *this );
blargg_err_t Vgm_start_track( struct Vgm_Emu* this )
{
clear_track_vars( this );
@ -750,7 +756,7 @@ blargg_err_t Vgm_start_track( struct Vgm_Emu* this )
this->vgm_time = 0;
if ( get_le32( header( this )->version ) >= 0x150 )
{
long data_offset = get_le32( header( this )->data_offset );
int data_offset = get_le32( header( this )->data_offset );
check( data_offset );
if ( data_offset )
this->pos += data_offset + offsetof (struct header_t,data_offset) - 0x40;
@ -764,266 +770,88 @@ blargg_err_t Vgm_start_track( struct Vgm_Emu* this )
if ( Ym2612_enabled( &this->ym2612 ) )
Ym2612_reset( &this->ym2612 );
Blip_clear( &this->blip_buf, 1 );
Blip_clear( &this->blip_buf );
Resampler_clear( &this->resampler );
}
this->fm_time_offset = 0;
Buffer_clear( &this->buf );
Buffer_clear( &this->stereo_buf );
this->emu_track_ended_ = false;
this->track_ended = false;
// convert filter times to samples
struct setup_t s = this->tfilter;
s.max_initial *= this->sample_rate * stereo;
#ifdef GME_DISABLE_SILENCE_LOOKAHEAD
s.lookahead = 1;
#endif
track_setup( &this->track_filter, &s );
if ( !this->ignore_silence )
{
// play until non-silence or end of track
long end;
for ( end = this->max_initial_silence * stereo * this->sample_rate; this->emu_time < end; )
{
fill_buf( this );
if ( this->buf_remain | (int) this->emu_track_ended_ )
break;
}
this->emu_time = this->buf_remain;
this->out_time = 0;
this->silence_time = 0;
this->silence_count = 0;
}
/* return track_ended() ? warning() : 0; */
return 0;
return track_start( &this->track_filter );
}
// Tell/Seek
static blargg_long msec_to_samples( blargg_long msec, long sample_rate )
static int msec_to_samples( int msec, int sample_rate )
{
blargg_long sec = msec / 1000;
int sec = msec / 1000;
msec -= sec * 1000;
return (sec * sample_rate + msec * sample_rate / 1000) * stereo;
}
long Track_tell( struct Vgm_Emu* this )
int Track_tell( struct Vgm_Emu* this )
{
blargg_long rate = this->sample_rate * stereo;
blargg_long sec = this->out_time / rate;
return sec * 1000 + (this->out_time - sec * rate) * 1000 / rate;
int rate = this->sample_rate * stereo;
int sec = track_sample_count( &this->track_filter ) / rate;
return sec * 1000 + (track_sample_count( &this->track_filter ) - sec * rate) * 1000 / rate;
}
blargg_err_t Track_seek( struct Vgm_Emu* this, long msec )
blargg_err_t Track_seek( struct Vgm_Emu* this, int msec )
{
blargg_long time = msec_to_samples( msec, this->sample_rate );
if ( time < this->out_time )
RETURN_ERR( Vgm_start_track( this ) );
return Track_skip( this, time - this->out_time );
int time = msec_to_samples( msec, this->sample_rate );
if ( time < track_sample_count( &this->track_filter ) )
RETURN_ERR( Vgm_start_track( this ) );
return Track_skip( this, time - track_sample_count( &this->track_filter ) );
}
blargg_err_t skip_( struct Vgm_Emu* this, long count );
blargg_err_t Track_skip( struct Vgm_Emu* this, long count )
blargg_err_t Track_skip( struct Vgm_Emu* this, int count )
{
this->out_time += count;
// remove from silence and buf first
{
long n = min( count, this->silence_count );
this->silence_count -= n;
count -= n;
n = min( count, this->buf_remain );
this->buf_remain -= n;
count -= n;
}
if ( count && !this->emu_track_ended_ )
{
this->emu_time += count;
if ( skip_( this, count ) )
this->emu_track_ended_ = true;
}
if ( !(this->silence_count | this->buf_remain) ) // caught up to emulator, so update track ended
this->track_ended |= this->emu_track_ended_;
return 0;
require( this->current_track >= 0 ); // start_track() must have been called already
return track_skip( &this->track_filter, count );
}
blargg_err_t skip_( struct Vgm_Emu* this, long count )
blargg_err_t skip_( void* emu, int count )
{
struct Vgm_Emu* this = (struct Vgm_Emu*) emu;
// for long skip, mute sound
const long threshold = 30000;
const int threshold = 32768;
if ( count > threshold )
{
int saved_mute = this->mute_mask_;
Sound_mute_voices( this, ~0 );
while ( count > threshold / 2 && !this->emu_track_ended_ )
{
RETURN_ERR( play_( this, buf_size, this->buf_ ) );
count -= buf_size;
}
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 );
}

View file

@ -1,11 +1,13 @@
// Sega Master System/Mark III, Sega Genesis/Mega Drive, BBC Micro VGM music file emulator
// Game_Music_Emu 0.5.5
// Game_Music_Emu 0.6-pre
#ifndef VGM_EMU_H
#define VGM_EMU_H
#include "blargg_common.h"
#include "blargg_source.h"
#include "track_filter.h"
#include "resampler.h"
#include "multi_buffer.h"
#include "ym2413_emu.h"
@ -17,7 +19,6 @@ typedef int fm_time_t;
enum { fm_time_bits = 12 };
enum { blip_time_bits = 12 };
enum { buf_size = 2048 };
// VGM header format
enum { header_size = 0x40 };
@ -46,9 +47,9 @@ enum { gme_max_field = 63 };
struct track_info_t
{
/* times in milliseconds; -1 if unknown */
long length;
long intro_length;
long loop_length;
int length;
int intro_length;
int loop_length;
/* empty string if not available */
char game [64];
@ -63,11 +64,11 @@ struct track_info_t
// YM2413 sound chip emulator. I can provide one I've modified to work with the library.
struct Vgm_Emu {
int fm_rate;
long psg_rate;
long vgm_rate;
int psg_rate;
int vgm_rate;
bool disable_oversampling;
long fm_time_offset;
int fm_time_offset;
int fm_time_factor;
int blip_time_factor;
@ -84,47 +85,34 @@ struct Vgm_Emu {
int dac_amp;
int dac_disabled; // -1 if disabled
struct Blip_Buffer blip_buf;
// general
long clock_rate_;
int current_track;
int clock_rate_;
unsigned buf_changed_count;
int max_initial_silence;
int voice_count;
int const *voice_types;
int mute_mask_;
int tempo;
int gain;
long sample_rate;
// track-specific
blargg_long out_time; // number of samples played since start of track
blargg_long emu_time; // number of samples emulator has generated since start of track
bool emu_track_ended_; // emulator has reached end of track
volatile bool track_ended;
// fading
blargg_long fade_start;
int fade_step;
// silence detection
int silence_lookahead; // speed to run emulator when looking ahead for silence
bool ignore_silence;
long silence_time; // number of samples where most recent silence began
long silence_count; // number of samples of silence to play before using buf
long buf_remain; // number of samples left in silence buffer
int sample_rate;
// larger items at the end
struct track_info_t info;
sample_t buf_ [buf_size];
struct setup_t tfilter;
struct Track_Filter track_filter;
struct Ym2612_Emu ym2612;
struct Ym2413_Emu ym2413;
struct Sms_Apu psg;
struct Blip_Synth pcm;
struct Blip_Buffer blip_buf;
struct Resampler resampler;
struct Stereo_Buffer buf;
struct Multi_Buffer stereo_buf;
};
void Vgm_init( struct Vgm_Emu* this );
@ -135,7 +123,7 @@ static inline void Vgm_disable_oversampling( struct Vgm_Emu* this, bool disable
// Header for currently loaded file
static inline struct header_t *header( struct Vgm_Emu* this ) { return (struct header_t*) this->file_begin; }
// Basic functionality (see Gme_File.h for file loading/track info functions)
blargg_err_t Vgm_load_mem( struct Vgm_Emu* this, byte const* new_data, long new_size, bool parse_info );
@ -144,34 +132,46 @@ blargg_err_t Vgm_load_mem( struct Vgm_Emu* this, byte const* new_data, long new_
static inline bool uses_fm( struct Vgm_Emu* this ) { return Ym2612_enabled( &this->ym2612 ) || Ym2413_enabled( &this->ym2413 ); }
// Set output sample rate. Must be called only once before loading file.
blargg_err_t Vgm_set_sample_rate( struct Vgm_Emu* this, long sample_rate );
blargg_err_t Vgm_set_sample_rate( struct Vgm_Emu* this, int sample_rate );
// Start a track, where 0 is the first track. Also clears warning string.
blargg_err_t Vgm_start_track( struct Vgm_Emu* this );
// Generate 'count' samples info 'buf'. Output is in stereo. Any emulation
// errors set warning string, and major errors also end track.
blargg_err_t Vgm_play( struct Vgm_Emu* this, long count, sample_t* buf );
blargg_err_t Vgm_play( struct Vgm_Emu* this, int count, sample_t* buf );
// Track status/control
// Number of milliseconds (1000 msec = 1 second) played since beginning of track
long Track_tell( struct Vgm_Emu* this );
int Track_tell( struct Vgm_Emu* this );
// Seek to new time in track. Seeking backwards or far forward can take a while.
blargg_err_t Track_seek( struct Vgm_Emu* this, long msec );
blargg_err_t Track_seek( struct Vgm_Emu* this, int msec );
// Skip n samples
blargg_err_t Track_skip( struct Vgm_Emu* this, long n );
blargg_err_t Track_skip( struct Vgm_Emu* this, int n );
// Set start time and length of track fade out. Once fade ends track_ended() returns
// true. Fade time can be changed while track is playing.
void Track_set_fade( struct Vgm_Emu* this, long start_msec, long length_msec );
void Track_set_fade( struct Vgm_Emu* this, int start_msec, int length_msec );
// True if a track has reached its end
static inline bool Track_ended( struct Vgm_Emu* this )
{
return track_ended( &this->track_filter );
}
// Disables automatic end-of-track detection and skipping of silence at beginning
static inline void Track_ignore_silence( struct Vgm_Emu* this, bool disable )
{
this->track_filter.silence_ignored_ = disable;
}
// Get track length in milliseconds
static inline long Track_get_length( struct Vgm_Emu* this )
static inline int Track_get_length( struct Vgm_Emu* this )
{
long length = this->info.length;
int length = this->info.length;
if ( length <= 0 )
{
length = this->info.intro_length + 2 * this->info.loop_length; // intro + 2 loops
@ -181,20 +181,20 @@ static inline long Track_get_length( struct Vgm_Emu* this )
return length;
}
// Sound customization
// Adjust song tempo, where 1.0 = normal, 0.5 = half speed, 2.0 = double speed.
// Track length as returned by track_info() assumes a tempo of 1.0.
void Sound_set_tempo( struct Vgm_Emu* this, int t );
// Mute/unmute voice i, where voice 0 is first voice
void Sound_mute_voice( struct Vgm_Emu* this, int index, bool mute );
// Set muting state of all voices at once using a bit mask, where -1 mutes them all,
// 0 unmutes them all, 0x01 mutes just the first voice, etc.
void Sound_mute_voices( struct Vgm_Emu* this, int mask );
// Change overall output amplitude, where 1.0 results in minimal clamping.
// Must be called before set_sample_rate().
static inline void Sound_set_gain( struct Vgm_Emu* this, int g )
@ -203,5 +203,4 @@ static inline void Sound_set_gain( struct Vgm_Emu* this, int g )
this->gain = g;
}
#endif

View file

@ -4,7 +4,6 @@
#include "ym2612_emu.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>

View file

@ -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;

View file

@ -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;

View file

@ -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);