mirror of
https://github.com/dborth/snes9xgx.git
synced 2024-11-01 00:15:14 +01:00
Backport and adjusted the Refactored APU Resampler (#821)
This commit is contained in:
parent
6e1a197edd
commit
087ac22029
@ -10,7 +10,7 @@
|
||||
#include "../msu1.h"
|
||||
#include "../snapshot.h"
|
||||
#include "../display.h"
|
||||
#include "hermite_resampler.h"
|
||||
#include "resampler.h"
|
||||
|
||||
#define APU_DEFAULT_INPUT_RATE 32040
|
||||
#define APU_MINIMUM_SAMPLE_COUNT 512
|
||||
@ -62,7 +62,7 @@ namespace spc
|
||||
static uint32 ratio_denominator = APU_DENOMINATOR_NTSC;
|
||||
|
||||
static double dynamic_rate_multiplier = 1.0;
|
||||
}
|
||||
} // namespace spc
|
||||
|
||||
namespace msu
|
||||
{
|
||||
@ -71,7 +71,7 @@ namespace msu
|
||||
static Resampler *resampler = NULL;
|
||||
static int resample_buffer_size = -1;
|
||||
static uint8 *resample_buffer = NULL;
|
||||
}
|
||||
} // namespace msu
|
||||
|
||||
static void EightBitize (uint8 *, int);
|
||||
static void DeStereo (uint8 *, int);
|
||||
@ -359,7 +359,7 @@ bool8 S9xInitSound (int buffer_ms, int lag_ms)
|
||||
arguments. Use 2x in the resampler for buffer leveling with SoundSync */
|
||||
if (!spc::resampler)
|
||||
{
|
||||
spc::resampler = new HermiteResampler(spc::buffer_size >> (Settings.SoundSync ? 0 : 1));
|
||||
spc::resampler = new Resampler(spc::buffer_size >> (Settings.SoundSync ? 0 : 1));
|
||||
if (!spc::resampler)
|
||||
{
|
||||
delete[] spc::landing_buffer;
|
||||
@ -372,7 +372,7 @@ bool8 S9xInitSound (int buffer_ms, int lag_ms)
|
||||
|
||||
if (!msu::resampler)
|
||||
{
|
||||
msu::resampler = new HermiteResampler(msu::buffer_size >> (Settings.SoundSync ? 0 : 1));
|
||||
msu::resampler = new Resampler(msu::buffer_size >> (Settings.SoundSync ? 0 : 1));
|
||||
if (!msu::resampler)
|
||||
{
|
||||
delete[] msu::landing_buffer;
|
||||
|
@ -1,143 +0,0 @@
|
||||
/*****************************************************************************\
|
||||
Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
|
||||
This file is licensed under the Snes9x License.
|
||||
For further information, consult the LICENSE file in the root directory.
|
||||
\*****************************************************************************/
|
||||
|
||||
#ifndef __HERMITE_RESAMPLER_H
|
||||
#define __HERMITE_RESAMPLER_H
|
||||
|
||||
#include "resampler.h"
|
||||
|
||||
#undef CLAMP
|
||||
#undef SHORT_CLAMP
|
||||
#define CLAMP(x, low, high) (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x)))
|
||||
#define SHORT_CLAMP(n) ((short) CLAMP((n), -32768, 32767))
|
||||
|
||||
class HermiteResampler : public Resampler
|
||||
{
|
||||
protected:
|
||||
|
||||
float r_step;
|
||||
float r_frac;
|
||||
int r_left[4], r_right[4];
|
||||
|
||||
static inline float
|
||||
hermite (float mu1, float a, float b, float c, float d)
|
||||
{
|
||||
float mu2, mu3, m0, m1, a0, a1, a2, a3;
|
||||
|
||||
mu2 = mu1 * mu1;
|
||||
mu3 = mu2 * mu1;
|
||||
|
||||
m0 = (c - a) * 0.5;
|
||||
m1 = (d - b) * 0.5;
|
||||
|
||||
a0 = +2 * mu3 - 3 * mu2 + 1;
|
||||
a1 = mu3 - 2 * mu2 + mu1;
|
||||
a2 = mu3 - mu2;
|
||||
a3 = -2 * mu3 + 3 * mu2;
|
||||
|
||||
return (a0 * b) + (a1 * m0) + (a2 * m1) + (a3 * c);
|
||||
}
|
||||
|
||||
public:
|
||||
HermiteResampler (int num_samples) : Resampler (num_samples)
|
||||
{
|
||||
clear ();
|
||||
}
|
||||
|
||||
~HermiteResampler ()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
time_ratio (double ratio)
|
||||
{
|
||||
r_step = ratio;
|
||||
}
|
||||
|
||||
void
|
||||
clear (void)
|
||||
{
|
||||
ring_buffer::clear ();
|
||||
r_frac = 1.0;
|
||||
r_left [0] = r_left [1] = r_left [2] = r_left [3] = 0;
|
||||
r_right[0] = r_right[1] = r_right[2] = r_right[3] = 0;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
while (o_position < num_samples && consumed < buffer_size)
|
||||
{
|
||||
int s_left = internal_buffer[i_position];
|
||||
int s_right = internal_buffer[i_position + 1];
|
||||
float hermite_val[2];
|
||||
|
||||
while (r_frac <= 1.0 && o_position < num_samples)
|
||||
{
|
||||
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;
|
||||
|
||||
r_frac += r_step;
|
||||
}
|
||||
|
||||
if (r_frac > 1.0)
|
||||
{
|
||||
r_left [0] = r_left [1];
|
||||
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_frac -= 1.0;
|
||||
|
||||
i_position += 2;
|
||||
if (i_position >= max_samples)
|
||||
i_position -= max_samples;
|
||||
consumed += 2;
|
||||
}
|
||||
}
|
||||
|
||||
size -= consumed << 1;
|
||||
start += consumed << 1;
|
||||
if (start >= buffer_size)
|
||||
start -= buffer_size;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* __HERMITE_RESAMPLER_H */
|
@ -4,61 +4,216 @@
|
||||
For further information, consult the LICENSE file in the root directory.
|
||||
\*****************************************************************************/
|
||||
|
||||
#ifndef __RESAMPLER_H
|
||||
#define __RESAMPLER_H
|
||||
#ifndef __NEW_RESAMPLER_H
|
||||
#define __NEW_RESAMPLER_H
|
||||
|
||||
#include "ring_buffer.h"
|
||||
#include <cstring>
|
||||
#include <cassert>
|
||||
#if __cplusplus >= 201103L
|
||||
#include <cstdint>
|
||||
#else
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
#include <cmath>
|
||||
|
||||
class Resampler : public ring_buffer
|
||||
class Resampler
|
||||
{
|
||||
public:
|
||||
virtual void clear (void) = 0;
|
||||
virtual void time_ratio (double) = 0;
|
||||
virtual void read (short *, int) = 0;
|
||||
virtual int avail (void) = 0;
|
||||
public:
|
||||
int size;
|
||||
int buffer_size;
|
||||
int start;
|
||||
int16_t *buffer;
|
||||
|
||||
Resampler (int num_samples) : ring_buffer (num_samples << 1)
|
||||
float r_step;
|
||||
float r_frac;
|
||||
int r_left[4], r_right[4];
|
||||
|
||||
static inline int16_t short_clamp(int n)
|
||||
{
|
||||
return (int16_t)(((int16_t)n != n) ? (n >> 31) ^ 0x7fff : n);
|
||||
}
|
||||
|
||||
static inline int min(int a, int b)
|
||||
{
|
||||
return ((a) < (b) ? (a) : (b));
|
||||
}
|
||||
|
||||
static inline float hermite(float mu1, float a, float b, float c, float d)
|
||||
{
|
||||
float mu2, mu3, m0, m1, a0, a1, a2, a3;
|
||||
|
||||
mu2 = mu1 * mu1;
|
||||
mu3 = mu2 * mu1;
|
||||
|
||||
m0 = (c - a) * 0.5;
|
||||
m1 = (d - b) * 0.5;
|
||||
|
||||
a0 = +2 * mu3 - 3 * mu2 + 1;
|
||||
a1 = mu3 - 2 * mu2 + mu1;
|
||||
a2 = mu3 - mu2;
|
||||
a3 = -2 * mu3 + 3 * mu2;
|
||||
|
||||
return (a0 * b) + (a1 * m0) + (a2 * m1) + (a3 * c);
|
||||
}
|
||||
|
||||
Resampler(int num_samples)
|
||||
{
|
||||
this->buffer_size = num_samples;
|
||||
buffer = new int16_t[this->buffer_size];
|
||||
r_step = 1.0;
|
||||
clear();
|
||||
}
|
||||
|
||||
~Resampler()
|
||||
{
|
||||
delete[] buffer;
|
||||
buffer = NULL;
|
||||
}
|
||||
|
||||
inline void time_ratio(double ratio)
|
||||
{
|
||||
r_step = ratio;
|
||||
}
|
||||
|
||||
inline void clear(void)
|
||||
{
|
||||
start = 0;
|
||||
size = 0;
|
||||
memset(buffer, 0, buffer_size * 2);
|
||||
|
||||
r_frac = 0.0;
|
||||
r_left[0] = r_left[1] = r_left[2] = r_left[3] = 0;
|
||||
r_right[0] = r_right[1] = r_right[2] = r_right[3] = 0;
|
||||
}
|
||||
|
||||
inline bool pull(int16_t *dst, int num_samples)
|
||||
{
|
||||
if (space_filled() < num_samples)
|
||||
return false;
|
||||
|
||||
memcpy(dst, buffer + start, min(num_samples, buffer_size - start) * 2);
|
||||
|
||||
if (num_samples > (buffer_size - start))
|
||||
memcpy(dst + (buffer_size - start), buffer, (num_samples - (buffer_size - start)) * 2);
|
||||
|
||||
start = (start + num_samples) % buffer_size;
|
||||
size -= num_samples;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
inline void push_sample(int16_t l, int16_t r)
|
||||
{
|
||||
if (space_empty() >= 2)
|
||||
{
|
||||
int end = start + size;
|
||||
if (end >= buffer_size)
|
||||
end -= buffer_size;
|
||||
buffer[end] = l;
|
||||
buffer[end + 1] = r;
|
||||
size += 2;
|
||||
}
|
||||
}
|
||||
|
||||
inline bool push(int16_t *src, int num_samples)
|
||||
{
|
||||
if (space_empty() < num_samples)
|
||||
return false;
|
||||
|
||||
int end = start + size;
|
||||
if (end > buffer_size)
|
||||
end -= buffer_size;
|
||||
int first_write_size = min(num_samples, buffer_size - end);
|
||||
|
||||
memcpy(buffer + end, src, first_write_size * 2);
|
||||
|
||||
if (num_samples > first_write_size)
|
||||
memcpy(buffer, src + first_write_size, (num_samples - first_write_size) * 2);
|
||||
|
||||
size += num_samples;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void read(int16_t *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)
|
||||
{
|
||||
pull(data, num_samples);
|
||||
return;
|
||||
}
|
||||
|
||||
virtual ~Resampler ()
|
||||
assert((num_samples & 1) == 0); // resampler always processes both stereo samples
|
||||
int o_position = 0;
|
||||
|
||||
while (o_position < num_samples && size > 0)
|
||||
{
|
||||
int s_left = buffer[start];
|
||||
int s_right = buffer[start + 1];
|
||||
int hermite_val[2];
|
||||
|
||||
while (r_frac <= 1.0 && o_position < num_samples)
|
||||
{
|
||||
hermite_val[0] = (int)hermite(r_frac, r_left[0], r_left[1], r_left[2], r_left[3]);
|
||||
hermite_val[1] = (int)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;
|
||||
|
||||
r_frac += r_step;
|
||||
}
|
||||
|
||||
if (r_frac > 1.0)
|
||||
{
|
||||
r_left[0] = r_left[1];
|
||||
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_frac -= 1.0;
|
||||
|
||||
start += 2;
|
||||
if (start >= buffer_size)
|
||||
start -= buffer_size;
|
||||
size -= 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline bool
|
||||
push (short *src, int num_samples)
|
||||
{
|
||||
if (max_write () < num_samples)
|
||||
return false;
|
||||
inline int space_empty(void) const
|
||||
{
|
||||
return buffer_size - size;
|
||||
}
|
||||
|
||||
!num_samples || ring_buffer::push ((unsigned char *) src, num_samples << 1);
|
||||
inline int space_filled(void) const
|
||||
{
|
||||
return size;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
inline int
|
||||
space_empty (void) const
|
||||
{
|
||||
return buffer_size - size;
|
||||
}
|
||||
|
||||
inline int
|
||||
space_filled (void) const
|
||||
{
|
||||
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 size;
|
||||
}
|
||||
|
||||
inline int
|
||||
max_write (void) const
|
||||
{
|
||||
return space_empty () >> 1;
|
||||
}
|
||||
return (int)trunc(((size >> 1) - r_frac) / r_step) * 2;
|
||||
}
|
||||
|
||||
inline void
|
||||
resize (int num_samples)
|
||||
{
|
||||
ring_buffer::resize (num_samples << 1);
|
||||
}
|
||||
void resize(int num_samples)
|
||||
{
|
||||
if (buffer)
|
||||
delete[] buffer;
|
||||
buffer_size = num_samples;
|
||||
buffer = new int16_t[buffer_size];
|
||||
clear();
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* __RESAMPLER_H */
|
||||
#endif /* __NEW_RESAMPLER_H */
|
@ -1,115 +0,0 @@
|
||||
/*****************************************************************************\
|
||||
Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
|
||||
This file is licensed under the Snes9x License.
|
||||
For further information, consult the LICENSE file in the root directory.
|
||||
\*****************************************************************************/
|
||||
|
||||
#ifndef __RING_BUFFER_H
|
||||
#define __RING_BUFFER_H
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#undef MIN
|
||||
#define MIN(a, b) ((a) < (b) ? (a) : (b))
|
||||
|
||||
class ring_buffer
|
||||
{
|
||||
protected:
|
||||
int size;
|
||||
int buffer_size;
|
||||
int start;
|
||||
unsigned char *buffer;
|
||||
|
||||
public:
|
||||
ring_buffer (int buffer_size)
|
||||
{
|
||||
this->buffer_size = buffer_size;
|
||||
buffer = new unsigned char[this->buffer_size];
|
||||
memset (buffer, 0, this->buffer_size);
|
||||
|
||||
size = 0;
|
||||
start = 0;
|
||||
}
|
||||
|
||||
~ring_buffer (void)
|
||||
{
|
||||
delete[] buffer;
|
||||
}
|
||||
|
||||
bool
|
||||
push (unsigned char *src, int bytes)
|
||||
{
|
||||
if (space_empty () < bytes)
|
||||
return false;
|
||||
|
||||
int end = (start + size) % buffer_size;
|
||||
int first_write_size = MIN (bytes, buffer_size - end);
|
||||
|
||||
memcpy (buffer + end, src, first_write_size);
|
||||
|
||||
if (bytes > first_write_size)
|
||||
memcpy (buffer, src + first_write_size, bytes - first_write_size);
|
||||
|
||||
size += bytes;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
pull (unsigned char *dst, int bytes)
|
||||
{
|
||||
if (space_filled () < bytes)
|
||||
return false;
|
||||
|
||||
memcpy (dst, buffer + start, MIN (bytes, buffer_size - start));
|
||||
|
||||
if (bytes > (buffer_size - start))
|
||||
memcpy (dst + (buffer_size - start), buffer, bytes - (buffer_size - start));
|
||||
|
||||
start = (start + bytes) % buffer_size;
|
||||
size -= bytes;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
inline int
|
||||
space_empty (void) const
|
||||
{
|
||||
return buffer_size - size;
|
||||
}
|
||||
|
||||
inline int
|
||||
space_filled (void) const
|
||||
{
|
||||
return size;
|
||||
}
|
||||
|
||||
void
|
||||
clear (void)
|
||||
{
|
||||
start = 0;
|
||||
size = 0;
|
||||
memset (buffer, 0, buffer_size);
|
||||
}
|
||||
|
||||
void
|
||||
resize (int size)
|
||||
{
|
||||
delete[] buffer;
|
||||
buffer_size = size;
|
||||
buffer = new unsigned char[buffer_size];
|
||||
memset (buffer, 0, this->buffer_size);
|
||||
|
||||
this->size = 0;
|
||||
start = 0;
|
||||
}
|
||||
|
||||
inline void
|
||||
cache_silence (void)
|
||||
{
|
||||
clear ();
|
||||
size = buffer_size;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
@ -9,7 +9,7 @@
|
||||
#define _SNES9X_H_
|
||||
|
||||
#ifndef VERSION
|
||||
#define VERSION "1.59"
|
||||
#define VERSION "1.59.2"
|
||||
#endif
|
||||
|
||||
#include "port.h"
|
||||
|
Loading…
Reference in New Issue
Block a user