mirror of
https://github.com/dborth/snes9xgx.git
synced 2024-12-25 18:51:50 +01:00
update hermite resampler to support dynamic rate control (fixes audio
blips), remove some asserts in APU.
This commit is contained in:
parent
1a89c5174a
commit
05a607a8f7
@ -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
|
||||
|
@ -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 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 );
|
||||
|
@ -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
|
||||
|
@ -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 )
|
||||
|
@ -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 ();
|
||||
|
@ -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];
|
||||
}
|
||||
};
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
@ -113,14 +102,14 @@ class HermiteResampler : public Resampler
|
||||
r_left [1] = r_left [2];
|
||||
r_left [2] = r_left [3];
|
||||
r_left [3] = s_left;
|
||||
|
||||
|
||||
r_right[0] = r_right[1];
|
||||
r_right[1] = r_right[2];
|
||||
r_right[2] = r_right[3];
|
||||
r_right[3] = s_right;
|
||||
|
||||
r_right[3] = s_right;
|
||||
|
||||
r_frac -= 1.0;
|
||||
|
||||
|
||||
i_position += 2;
|
||||
if (i_position >= max_samples)
|
||||
i_position -= max_samples;
|
||||
@ -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;
|
||||
}
|
||||
};
|
||||
|
@ -12,7 +12,7 @@ class Resampler : public ring_buffer
|
||||
virtual void time_ratio (double) = 0;
|
||||
virtual void read (short *, int) = 0;
|
||||
virtual int avail (void) = 0;
|
||||
|
||||
|
||||
Resampler (int num_samples) : ring_buffer (num_samples << 1)
|
||||
{
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user