update hermite resampler to support dynamic rate control (fixes audio

blips), remove some asserts in APU.
This commit is contained in:
Daryl Borth 2018-08-15 11:49:27 -06:00
parent 1a89c5174a
commit 05a607a8f7
12 changed files with 50 additions and 73 deletions

View File

@ -36,7 +36,7 @@ Wii homebrew is WiiBrew (www.wiibrew.org).
[4.3.8]
* Updated core to 1.56 (with less accurate but faster Blargg audio core)
* Improved audio synchronization - reduced crackling
* Improved audio synchronization with dynamic rate control - fixes audio crackles
* Added BS-X BIOS loading
* Memory optimizations to free up more MEM1 for Snes9x
* Disable multi pixel format support for a speed boost

View File

@ -493,8 +493,8 @@ int SNES_SPC::cpu_read( int addr, rel_time_t time )
}
else // 1%
{
//assert( reg + (r_t0out + 0xF0 - 0x10000) < 0x100 );
result = cpu_read( reg + (r_t0out + 0xF0 - 0x10000), time );
if( reg + (r_t0out + 0xF0 - 0x10000) < 0x100 )
result = cpu_read( reg + (r_t0out + 0xF0 - 0x10000), time );
}
}
}

View File

@ -286,19 +286,15 @@ private:
#endif
};
#include <assert.h>
inline int SNES_SPC::sample_count() const { return (m.extra_clocks >> 5) * 2; }
inline int SNES_SPC::read_port( time_t t, int port )
{
assert( (unsigned) port < port_count );
return run_until_( t ) [port];
}
inline void SNES_SPC::write_port( time_t t, int port, int data )
{
assert( (unsigned) port < port_count );
run_until_( t ) [0x10 + port] = data;
m.ram.ram [0xF4 + port] = data;
}

View File

@ -292,8 +292,6 @@ void SNES_SPC::reset_buf()
void SNES_SPC::set_output( sample_t* out, int size )
{
require( (size & 1) == 0 ); // size must be even
m.extra_clocks &= clocks_per_sample - 1;
if ( out )
{
@ -352,7 +350,6 @@ void SNES_SPC::save_extra()
blargg_err_t SNES_SPC::play( int count, sample_t* out )
{
require( (count & 1) == 0 ); // must be even
if ( count )
{
set_output( out, count );

View File

@ -1204,8 +1204,6 @@ loop:
m.cpu_error = "SPC emulation error";
goto stop;
} // switch
assert( 0 ); // catch any unhandled instructions
}
out_of_time:
rel_time -= m.cycle_table [*pc]; // undo partial execution of opcode

View File

@ -74,7 +74,6 @@ static BOOST::uint8_t const initial_regs [SPC_DSP::register_count] =
void SPC_DSP::set_output( sample_t* out, int size )
{
require( (size & 1) == 0 ); // must be even
if ( !out )
{
out = m.extra;
@ -518,7 +517,6 @@ VOICE_CLOCK( V4 )
if ( (v->brr_offset += 2) >= brr_block_size )
{
// Start decoding next BRR block
assert( v->brr_offset == brr_block_size );
v->brr_addr = (v->brr_addr + brr_block_size) & 0xFFFF;
if ( m.t_brr_header & 1 )
{
@ -804,8 +802,6 @@ PHASE(31) V(V4,0) V(V1,2)\
void SPC_DSP::run( int clocks_remain )
{
require( clocks_remain > 0 );
int const phase = m.phase;
m.phase = (phase + clocks_remain) & 31;
switch ( phase )

View File

@ -352,8 +352,6 @@ bool8 S9xMixSamples (uint8 *buffer, int sample_count)
for (int32 i = 0; i < sample_count; ++i)
*((int16*)(dest+(i * 2))) += *((int16*)(msu::resample_buffer+(i * 2)));
}
else // should never occur
assert(0);
}
}
else
@ -394,8 +392,8 @@ int S9xGetSampleCount (void)
#ifdef GEKKO
void S9xIncreaseDynamicRateMultiplier ()
{
if(spc::dynamic_rate_multiplier != 1.001) {
spc::dynamic_rate_multiplier = 1.001;
if(spc::dynamic_rate_multiplier != 1.005) {
spc::dynamic_rate_multiplier = 1.005;
UpdatePlaybackRate();
}
}
@ -415,13 +413,16 @@ void S9xFinalizeSamples (void)
if (!Settings.Mute)
{
if(!spc::sound_in_sync) {
S9xIncreaseDynamicRateMultiplier();
}
drop_current_msu1_samples = FALSE;
if (!spc::resampler->push((short *) spc::landing_buffer, spc_core->sample_count()))
{
/* We weren't able to process the entire buffer. Potential overrun. */
spc::sound_in_sync = FALSE;
S9xIncreaseDynamicRateMultiplier();
if (Settings.SoundSync && !Settings.TurboMode)
return;
@ -442,7 +443,6 @@ void S9xFinalizeSamples (void)
if (!drop_current_msu1_samples && !msu::resampler->push((short *)msu::landing_buffer, S9xMSU1Samples()))
{
// should not occur, msu buffer is larger and we drop msu samples if spc buffer overruns
assert(0);
}
}
@ -451,10 +451,8 @@ void S9xFinalizeSamples (void)
else
if (spc::resampler->space_empty() >= spc::resampler->space_filled())
spc::sound_in_sync = TRUE;
else {
S9xIncreaseDynamicRateMultiplier ();
else
spc::sound_in_sync = FALSE;
}
if(spc::sound_in_sync) {
S9xResetDynamicRateMultiplier ();

View File

@ -58,7 +58,6 @@ public:
void clear() { void* p = begin_; begin_ = 0; size_ = 0; free( p ); }
T& operator [] ( size_t n ) const
{
assert( n <= size_ ); // <= to allow past-the-end value
return begin_ [n];
}
};

View File

@ -57,10 +57,8 @@ inline void blargg_verify_byte_order()
#ifndef NDEBUG
#if BLARGG_BIG_ENDIAN
volatile int i = 1;
assert( *(volatile char*) &i == 0 );
#elif BLARGG_LITTLE_ENDIAN
volatile int i = 1;
assert( *(volatile char*) &i != 0 );
#endif
#endif
}

View File

@ -14,25 +14,20 @@ class HermiteResampler : public Resampler
{
protected:
double r_step;
double r_frac;
int r_left[4], r_right[4];
float r_step;
float r_frac;
int r_left[4], r_right[4];
double
hermite (double mu1, double a, double b, double c, double d)
static inline float
hermite (float mu1, float a, float b, float c, float d)
{
const double tension = 0.0; //-1 = low, 0 = normal, 1 = high
const double bias = 0.0; //-1 = left, 0 = even, 1 = right
double mu2, mu3, m0, m1, a0, a1, a2, a3;
float mu2, mu3, m0, m1, a0, a1, a2, a3;
mu2 = mu1 * mu1;
mu3 = mu2 * mu1;
m0 = (b - a) * (1 + bias) * (1 - tension) / 2;
m0 += (c - b) * (1 - bias) * (1 - tension) / 2;
m1 = (c - b) * (1 + bias) * (1 - tension) / 2;
m1 += (d - c) * (1 - bias) * (1 - tension) / 2;
m0 = (c - a) * 0.5;
m1 = (d - b) * 0.5;
a0 = +2 * mu3 - 3 * mu2 + 1;
a1 = mu3 - 2 * mu2 + mu1;
@ -56,7 +51,6 @@ class HermiteResampler : public Resampler
time_ratio (double ratio)
{
r_step = ratio;
clear ();
}
void
@ -71,7 +65,15 @@ class HermiteResampler : public Resampler
void
read (short *data, int num_samples)
{
//If we are outputting the exact same ratio as the input, pull directly from the input buffer
if (r_step == 1.0)
{
ring_buffer::pull((unsigned char*)data, num_samples * sizeof(short));
return;
}
int i_position = start >> 1;
int max_samples = buffer_size >> 1;
short *internal_buffer = (short *) buffer;
int o_position = 0;
int consumed = 0;
@ -80,27 +82,14 @@ class HermiteResampler : public Resampler
{
int s_left = internal_buffer[i_position];
int s_right = internal_buffer[i_position + 1];
int max_samples = buffer_size >> 1;
const double margin_of_error = 1.0e-10;
if (fabs(r_step - 1.0) < margin_of_error)
{
data[o_position] = (short) s_left;
data[o_position + 1] = (short) s_right;
o_position += 2;
i_position += 2;
if (i_position >= max_samples)
i_position -= max_samples;
consumed += 2;
continue;
}
float hermite_val[2];
while (r_frac <= 1.0 && o_position < num_samples)
{
data[o_position] = SHORT_CLAMP (hermite (r_frac, r_left [0], r_left [1], r_left [2], r_left [3]));
data[o_position + 1] = SHORT_CLAMP (hermite (r_frac, r_right[0], r_right[1], r_right[2], r_right[3]));
hermite_val[0] = hermite (r_frac, r_left [0], r_left [1], r_left [2], r_left [3]);
hermite_val[1] = hermite (r_frac, r_right[0], r_right[1], r_right[2], r_right[3]);
data[o_position] = SHORT_CLAMP (hermite_val[0]);
data[o_position + 1] = SHORT_CLAMP (hermite_val[1]);
o_position += 2;
@ -137,6 +126,12 @@ class HermiteResampler : public Resampler
inline int
avail (void)
{
//If we are outputting the exact same ratio as the input, find out directly from the input buffer
if (r_step == 1.0)
{
return (ring_buffer::space_filled() + sizeof(short) - 1) / sizeof(short);
}
return (int) floor (((size >> 2) - r_frac) / r_step) * 2;
}
};

View File

@ -33,19 +33,19 @@ class Resampler : public ring_buffer
}
inline int
space_empty (void)
space_empty (void) const
{
return buffer_size - size;
}
inline int
space_filled (void)
space_filled (void) const
{
return size;
}
inline int
max_write (void)
max_write (void) const
{
return space_empty () >> 1;
}

View File

@ -69,13 +69,13 @@ public:
}
inline int
space_empty (void)
space_empty (void) const
{
return buffer_size - size;
}
inline int
space_filled (void)
space_filled (void) const
{
return size;
}
@ -96,7 +96,7 @@ public:
buffer = new unsigned char[buffer_size];
memset (buffer, 0, this->buffer_size);
size = 0;
this->size = 0;
start = 0;
}