From 13cbade08a07296d92e7a7d3e20475de0032cba1 Mon Sep 17 00:00:00 2001 From: Andree Buschmann Date: Wed, 31 Aug 2011 19:19:49 +0000 Subject: 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 --- apps/codecs/libgme/hes_apu.c | 306 +++++++++++++++++++++++++------------------ 1 file changed, 181 insertions(+), 125 deletions(-) (limited to 'apps/codecs/libgme/hes_apu.c') diff --git a/apps/codecs/libgme/hes_apu.c b/apps/codecs/libgme/hes_apu.c index 2a831426f5..054b164a9a 100644 --- a/apps/codecs/libgme/hes_apu.c +++ b/apps/codecs/libgme/hes_apu.c @@ -18,8 +18,7 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ enum { center_waves = 1 }; // reduces asymmetry and clamping when starting notes -static void Apu_balance_changed( struct Hes_Apu* this, struct Hes_Osc* osc ); -static void Apu_balance_changed( struct Hes_Apu* this, struct Hes_Osc* osc ) +static void balance_changed( struct Hes_Apu* this, struct Hes_Osc* osc ) { static short const log_table [32] = { // ~1.5 db per step #define ENTRY( factor ) (short) (factor * amp_range / 31.0 + 0.5) @@ -42,27 +41,40 @@ static void Apu_balance_changed( struct Hes_Apu* this, struct Hes_Osc* osc ) int right = vol + (osc->balance << 1 & 0x1E) + (this->balance << 1 & 0x1E); if ( right < 0 ) right = 0; - left = log_table [left ]; - right = log_table [right]; - // optimizing for the common case of being centered also allows easy // panning using Effects_Buffer - osc->outputs [0] = osc->chans [0]; // center - osc->outputs [1] = 0; - if ( left != right ) + + // Separate balance into center volume and additional on either left or right + osc->output [0] = osc->outputs [0]; // center + osc->output [1] = osc->outputs [2]; // right + int base = log_table [left ]; + int side = log_table [right] - base; + if ( side < 0 ) + { + base += side; + side = -side; + osc->output [1] = osc->outputs [1]; // left + } + + // Optimize when output is far left, center, or far right + if ( !base || osc->output [0] == osc->output [1] ) { - osc->outputs [0] = osc->chans [1]; // left - osc->outputs [1] = osc->chans [2]; // right + base += side; + side = 0; + osc->output [0] = osc->output [1]; + osc->output [1] = NULL; + osc->last_amp [1] = 0; } if ( center_waves ) { - osc->last_amp [0] += (left - osc->volume [0]) * 16; - osc->last_amp [1] += (right - osc->volume [1]) * 16; + // TODO: this can leave a non-zero level in a buffer (minor) + osc->last_amp [0] += (base - osc->volume [0]) * 16; + osc->last_amp [1] += (side - osc->volume [1]) * 16; } - osc->volume [0] = left; - osc->volume [1] = right; + osc->volume [0] = base; + osc->volume [1] = side; } void Apu_init( struct Hes_Apu* this ) @@ -71,11 +83,11 @@ void Apu_init( struct Hes_Apu* this ) do { osc--; - osc->outputs [0] = 0; - osc->outputs [1] = 0; - osc->chans [0] = 0; - osc->chans [1] = 0; - osc->chans [2] = 0; + osc->output [0] = NULL; + osc->output [1] = NULL; + osc->outputs [0] = NULL; + osc->outputs [1] = NULL; + osc->outputs [2] = NULL; } while ( osc != this->oscs ); @@ -92,139 +104,183 @@ void Apu_reset( struct Hes_Apu* this ) { osc--; memset( osc, 0, offsetof (struct Hes_Osc,outputs) ); - osc->noise_lfsr = 1; + osc->lfsr = 1; osc->control = 0x40; osc->balance = 0xFF; } while ( osc != this->oscs ); + + // Only last two oscs support noise + this->oscs [osc_count - 2].lfsr = 0x200C3; // equivalent to 1 in Fibonacci LFSR + this->oscs [osc_count - 1].lfsr = 0x200C3; } -void Apu_osc_output( struct Hes_Apu* this, int index, struct Blip_Buffer* center, struct Blip_Buffer* left, struct Blip_Buffer* right ) +void Apu_osc_output( struct Hes_Apu* this, int i, struct Blip_Buffer* center, struct Blip_Buffer* left, struct Blip_Buffer* right ) { - require( (unsigned) index < osc_count ); - this->oscs [index].chans [0] = center; - this->oscs [index].chans [1] = left; - this->oscs [index].chans [2] = right; + // Must be silent (all NULL), mono (left and right NULL), or stereo (none NULL) + require( !center || (center && !left && !right) || (center && left && right) ); + require( (unsigned) i < osc_count ); // fails if you pass invalid osc index - struct Hes_Osc* osc = &this->oscs [osc_count]; - do + if ( !center || !left || !right ) { - osc--; - Apu_balance_changed( this, osc ); + left = center; + right = center; } - while ( osc != this->oscs ); + + struct Hes_Osc* o = &this->oscs [i]; + o->outputs [0] = center; + o->outputs [1] = right; + o->outputs [2] = left; + balance_changed( this, o ); } -void Osc_run_until( struct Hes_Osc* this, struct Blip_Synth* synth_, blip_time_t end_time ) +void run_osc( struct Hes_Osc* o, struct Blip_Synth* syn, blip_time_t end_time ) { - struct Blip_Buffer* const osc_outputs_0 = this->outputs [0]; // cache often-used values - if ( osc_outputs_0 && this->control & 0x80 ) + int vol0 = o->volume [0]; + int vol1 = o->volume [1]; + int dac = o->dac; + + struct Blip_Buffer* out0 = o->output [0]; // cache often-used values + struct Blip_Buffer* out1 = o->output [1]; + if ( !(o->control & 0x80) ) + out0 = NULL; + + if ( out0 ) { - int dac = this->dac; - - int const volume_0 = this->volume [0]; + // Update amplitudes + if ( out1 ) { - int delta = dac * volume_0 - this->last_amp [0]; + int delta = dac * vol1 - o->last_amp [1]; if ( delta ) - Synth_offset( synth_, this->last_time, delta, osc_outputs_0 ); - Blip_set_modified( osc_outputs_0 ); + { + Synth_offset( syn, o->last_time, delta, out1 ); + Blip_set_modified( out1 ); + } } - - struct Blip_Buffer* const osc_outputs_1 = this->outputs [1]; - int const volume_1 = this->volume [1]; - if ( osc_outputs_1 ) + int delta = dac * vol0 - o->last_amp [0]; + if ( delta ) { - int delta = dac * volume_1 - this->last_amp [1]; - if ( delta ) - Synth_offset( synth_, this->last_time, delta, osc_outputs_1 ); - Blip_set_modified( osc_outputs_1 ); + Synth_offset( syn, o->last_time, delta, out0 ); + Blip_set_modified( out0 ); } - blip_time_t time = this->last_time + this->delay; + // Don't generate if silent + if ( !(vol0 | vol1) ) + out0 = NULL; + } + + // Generate noise + int noise = 0; + if ( o->lfsr ) + { + noise = o->noise & 0x80; + + blip_time_t time = o->last_time + o->noise_delay; if ( time < end_time ) { - if ( this->noise & 0x80 ) + int period = (~o->noise & 0x1F) * 128; + if ( !period ) + period = 64; + + if ( noise && out0 ) { - if ( volume_0 | volume_1 ) + unsigned lfsr = o->lfsr; + do { - // noise - int const period = (32 - (this->noise & 0x1F)) * 64; // TODO: correct? - unsigned noise_lfsr = this->noise_lfsr; - do + int new_dac = -(lfsr & 1); + lfsr = (lfsr >> 1) ^ (0x30061 & new_dac); + + int delta = (new_dac &= 0x1F) - dac; + if ( delta ) { - int new_dac = 0x1F & -(noise_lfsr >> 1 & 1); - // Implemented using "Galios configuration" - // TODO: find correct LFSR algorithm - noise_lfsr = (noise_lfsr >> 1) ^ (0xE008 & -(noise_lfsr & 1)); - //noise_lfsr = (noise_lfsr >> 1) ^ (0x6000 & -(noise_lfsr & 1)); - int delta = new_dac - dac; - if ( delta ) - { - dac = new_dac; - Synth_offset( synth_, time, delta * volume_0, osc_outputs_0 ); - if ( osc_outputs_1 ) - Synth_offset( synth_, time, delta * volume_1, osc_outputs_1 ); - } - time += period; + dac = new_dac; + Synth_offset( syn, time, delta * vol0, out0 ); + if ( out1 ) + Synth_offset( syn, time, delta * vol1, out1 ); } - while ( time < end_time ); - - this->noise_lfsr = noise_lfsr; - assert( noise_lfsr ); + time += period; } - } - else if ( !(this->control & 0x40) ) - { - // wave - int phase = (this->phase + 1) & 0x1F; // pre-advance for optimal inner loop - int period = this->period * 2; - if ( period >= 14 && (volume_0 | volume_1) ) + while ( time < end_time ); + + if ( !lfsr ) { - do - { - int new_dac = this->wave [phase]; - phase = (phase + 1) & 0x1F; - int delta = new_dac - dac; - if ( delta ) - { - dac = new_dac; - Synth_offset( synth_, time, delta * volume_0, osc_outputs_0 ); - if ( osc_outputs_1 ) - Synth_offset( synth_, time, delta * volume_1, osc_outputs_1 ); - } - time += period; - } - while ( time < end_time ); + lfsr = 1; + check( false ); } - else + o->lfsr = lfsr; + + Blip_set_modified( out0 ); + if ( out1 ) + Blip_set_modified( out1 ); + } + else + { + // Maintain phase when silent + int count = (end_time - time + period - 1) / period; + time += count * period; + + // not worth it + //while ( count-- ) + // o->lfsr = (o->lfsr >> 1) ^ (0x30061 * (o->lfsr & 1)); + } + } + o->noise_delay = time - end_time; + } + + // Generate wave + blip_time_t time = o->last_time + o->delay; + if ( time < end_time ) + { + int phase = (o->phase + 1) & 0x1F; // pre-advance for optimal inner loop + int period = o->period * 2; + + if ( period >= 14 && out0 && !((o->control & 0x40) | noise) ) + { + do + { + int new_dac = o->wave [phase]; + phase = (phase + 1) & 0x1F; + int delta = new_dac - dac; + if ( delta ) { - if ( !period ) - { - // TODO: Gekisha Boy assumes that period = 0 silences wave - //period = 0x1000 * 2; - period = 1; - //if ( !(volume_0 | volume_1) ) - // dprintf( "Used period 0\n" ); - } - - // maintain phase when silent - blargg_long count = (end_time - time + period - 1) / period; - phase += count; // phase will be masked below - time += count * period; + dac = new_dac; + Synth_offset( syn, time, delta * vol0, out0 ); + if ( out1 ) + Synth_offset( syn, time, delta * vol1, out1 ); } - this->phase = (phase - 1) & 0x1F; // undo pre-advance + time += period; } + while ( time < end_time ); + Blip_set_modified( out0 ); + if ( out1 ) + Blip_set_modified( out1 ); + } + else + { + // Maintain phase when silent + int count = end_time - time; + if ( !period ) + period = 1; + count = (count + period - 1) / period; + + phase += count; // phase will be masked below + time += count * period; } - time -= end_time; - if ( time < 0 ) - time = 0; - this->delay = time; - this->dac = dac; - this->last_amp [0] = dac * volume_0; - this->last_amp [1] = dac * volume_1; + // TODO: Find whether phase increments even when both volumes are zero. + // CAN'T simply check for out0 being non-NULL, since it could be NULL + // if channel is muted in player, but still has non-zero volume. + // City Hunter breaks when this check is removed. + if ( !(o->control & 0x40) && (vol0 | vol1) ) + o->phase = (phase - 1) & 0x1F; // undo pre-advance } - this->last_time = end_time; + o->delay = time - end_time; + check( o->delay >= 0 ); + + o->last_time = end_time; + o->dac = dac; + o->last_amp [0] = dac * vol0; + o->last_amp [1] = dac * vol1; } void Apu_write_data( struct Hes_Apu* this, blip_time_t time, int addr, int data ) @@ -243,8 +299,8 @@ void Apu_write_data( struct Hes_Apu* this, blip_time_t time, int addr, int data do { osc--; - Osc_run_until( osc, &this->synth, time ); - Apu_balance_changed( this, this->oscs ); + run_osc( osc, &this->synth, time ); + balance_changed( this, this->oscs ); } while ( osc != this->oscs ); } @@ -252,7 +308,7 @@ void Apu_write_data( struct Hes_Apu* this, blip_time_t time, int addr, int data else if ( this->latch < osc_count ) { struct Hes_Osc* osc = &this->oscs [this->latch]; - Osc_run_until( osc, &this->synth, time ); + run_osc( osc, &this->synth, time ); switch ( addr ) { case 0x802: @@ -267,12 +323,12 @@ void Apu_write_data( struct Hes_Apu* this, blip_time_t time, int addr, int data if ( osc->control & 0x40 & ~data ) osc->phase = 0; osc->control = data; - Apu_balance_changed( this, osc ); + balance_changed( this, osc ); break; case 0x805: osc->balance = data; - Apu_balance_changed( this, osc ); + balance_changed( this, osc ); break; case 0x806: @@ -289,9 +345,9 @@ void Apu_write_data( struct Hes_Apu* this, blip_time_t time, int addr, int data break; case 0x807: - if ( osc >= &this->oscs [4] ) - osc->noise = data; + osc->noise = data; break; + case 0x809: if ( !(data & 0x80) && (data & 0x03) != 0 ) { dprintf( "HES LFO not supported\n" ); @@ -307,7 +363,7 @@ void Apu_end_frame( struct Hes_Apu* this, blip_time_t end_time ) { osc--; if ( end_time > osc->last_time ) - Osc_run_until( osc, &this->synth, end_time ); + run_osc( osc, &this->synth, end_time ); assert( osc->last_time >= end_time ); osc->last_time -= end_time; } -- cgit v1.2.3