mirror of
https://github.com/dborth/snes9xgx.git
synced 2024-12-26 19:21: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]
|
[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
|
||||||
|
@ -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 );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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 );
|
||||||
|
@ -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
|
||||||
|
@ -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 )
|
||||||
|
@ -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 ();
|
||||||
|
@ -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];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user