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] [4.3.8]
* Updated core to 1.56 (with less accurate but faster Blargg audio core) * 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 * Added BS-X BIOS loading
* Memory optimizations to free up more MEM1 for Snes9x * Memory optimizations to free up more MEM1 for Snes9x
* Disable multi pixel format support for a speed boost * Disable multi pixel format support for a speed boost

View File

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

View File

@ -286,19 +286,15 @@ private:
#endif #endif
}; };
#include <assert.h>
inline int SNES_SPC::sample_count() const { return (m.extra_clocks >> 5) * 2; } inline int SNES_SPC::sample_count() const { return (m.extra_clocks >> 5) * 2; }
inline int SNES_SPC::read_port( time_t t, int port ) inline int SNES_SPC::read_port( time_t t, int port )
{ {
assert( (unsigned) port < port_count );
return run_until_( t ) [port]; return run_until_( t ) [port];
} }
inline void SNES_SPC::write_port( time_t t, int port, int data ) inline void SNES_SPC::write_port( time_t t, int port, int data )
{ {
assert( (unsigned) port < port_count );
run_until_( t ) [0x10 + port] = data; run_until_( t ) [0x10 + port] = data;
m.ram.ram [0xF4 + 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 ) 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; m.extra_clocks &= clocks_per_sample - 1;
if ( out ) if ( out )
{ {
@ -352,7 +350,6 @@ void SNES_SPC::save_extra()
blargg_err_t SNES_SPC::play( int count, sample_t* out ) blargg_err_t SNES_SPC::play( int count, sample_t* out )
{ {
require( (count & 1) == 0 ); // must be even
if ( count ) if ( count )
{ {
set_output( out, count ); set_output( out, count );

View File

@ -1204,8 +1204,6 @@ loop:
m.cpu_error = "SPC emulation error"; m.cpu_error = "SPC emulation error";
goto stop; goto stop;
} // switch } // switch
assert( 0 ); // catch any unhandled instructions
} }
out_of_time: out_of_time:
rel_time -= m.cycle_table [*pc]; // undo partial execution of opcode 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 ) void SPC_DSP::set_output( sample_t* out, int size )
{ {
require( (size & 1) == 0 ); // must be even
if ( !out ) if ( !out )
{ {
out = m.extra; out = m.extra;
@ -518,7 +517,6 @@ VOICE_CLOCK( V4 )
if ( (v->brr_offset += 2) >= brr_block_size ) if ( (v->brr_offset += 2) >= brr_block_size )
{ {
// Start decoding next BRR block // Start decoding next BRR block
assert( v->brr_offset == brr_block_size );
v->brr_addr = (v->brr_addr + brr_block_size) & 0xFFFF; v->brr_addr = (v->brr_addr + brr_block_size) & 0xFFFF;
if ( m.t_brr_header & 1 ) 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 ) void SPC_DSP::run( int clocks_remain )
{ {
require( clocks_remain > 0 );
int const phase = m.phase; int const phase = m.phase;
m.phase = (phase + clocks_remain) & 31; m.phase = (phase + clocks_remain) & 31;
switch ( phase ) switch ( phase )

View File

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

View File

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

View File

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

View File

@ -14,25 +14,20 @@ class HermiteResampler : public Resampler
{ {
protected: protected:
double r_step; float r_step;
double r_frac; float r_frac;
int r_left[4], r_right[4]; int r_left[4], r_right[4];
double static inline float
hermite (double mu1, double a, double b, double c, double d) hermite (float mu1, float a, float b, float c, float d)
{ {
const double tension = 0.0; //-1 = low, 0 = normal, 1 = high float mu2, mu3, m0, m1, a0, a1, a2, a3;
const double bias = 0.0; //-1 = left, 0 = even, 1 = right
double mu2, mu3, m0, m1, a0, a1, a2, a3;
mu2 = mu1 * mu1; mu2 = mu1 * mu1;
mu3 = mu2 * mu1; mu3 = mu2 * mu1;
m0 = (b - a) * (1 + bias) * (1 - tension) / 2; m0 = (c - a) * 0.5;
m0 += (c - b) * (1 - bias) * (1 - tension) / 2; m1 = (d - b) * 0.5;
m1 = (c - b) * (1 + bias) * (1 - tension) / 2;
m1 += (d - c) * (1 - bias) * (1 - tension) / 2;
a0 = +2 * mu3 - 3 * mu2 + 1; a0 = +2 * mu3 - 3 * mu2 + 1;
a1 = mu3 - 2 * mu2 + mu1; a1 = mu3 - 2 * mu2 + mu1;
@ -56,7 +51,6 @@ class HermiteResampler : public Resampler
time_ratio (double ratio) time_ratio (double ratio)
{ {
r_step = ratio; r_step = ratio;
clear ();
} }
void void
@ -71,7 +65,15 @@ class HermiteResampler : public Resampler
void void
read (short *data, int num_samples) 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 i_position = start >> 1;
int max_samples = buffer_size >> 1;
short *internal_buffer = (short *) buffer; short *internal_buffer = (short *) buffer;
int o_position = 0; int o_position = 0;
int consumed = 0; int consumed = 0;
@ -80,27 +82,14 @@ class HermiteResampler : public Resampler
{ {
int s_left = internal_buffer[i_position]; int s_left = internal_buffer[i_position];
int s_right = internal_buffer[i_position + 1]; int s_right = internal_buffer[i_position + 1];
int max_samples = buffer_size >> 1; float hermite_val[2];
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;
}
while (r_frac <= 1.0 && o_position < num_samples) 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])); hermite_val[0] = 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[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; o_position += 2;
@ -137,6 +126,12 @@ class HermiteResampler : public Resampler
inline int inline int
avail (void) 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; return (int) floor (((size >> 2) - r_frac) / r_step) * 2;
} }
}; };

View File

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

View File

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