Add a consolidated resampler.

This commit is contained in:
Brandon Wright 2019-02-08 20:23:18 -06:00
parent 5b4ca50792
commit 36406a6627
4 changed files with 176 additions and 304 deletions

View File

@ -11,7 +11,7 @@
#include "../msu1.h"
#include "../snapshot.h"
#include "../display.h"
#include "hermite_resampler.h"
#include "resampler.h"
#include "bapu/snes/snes.hpp"
@ -243,14 +243,14 @@ bool8 S9xInitSound(int buffer_ms, int unused2)
if (!spc::resampler)
{
spc::resampler = new HermiteResampler(buffer_size_samples);
spc::resampler = new Resampler(buffer_size_samples);
if (!spc::resampler)
return (FALSE);
}
if (!msu::resampler)
{
msu::resampler = new HermiteResampler(buffer_size_samples * 3 / 2);
msu::resampler = new Resampler(buffer_size_samples * 3 / 2);
if (!msu::resampler)
return (FALSE);
}

View File

@ -1,145 +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"
#include <assert.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 = 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;
}
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;
}
assert((num_samples & 1) == 0); // resampler always processes both stereo samples
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) trunc (((size >> 2) - r_frac) / r_step) * 2;
}
};
#endif /* __HERMITE_RESAMPLER_H */

View File

@ -4,61 +4,193 @@
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>
#include <cstdint>
#include <cmath>
class Resampler : public ring_buffer
struct Resampler
{
public:
virtual void clear (void) = 0;
virtual void time_ratio (double) = 0;
virtual void read (short *, int) = 0;
virtual int avail (void) = 0;
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(int16_t n)
{
return (int16_t)(((int16_t)n != n) ? (n >> 31) ^ 0x7fff : n);
}
virtual ~Resampler ()
static inline int min(int a, int b)
{
return ((a) < (b) ? (a) : (b));
}
inline bool
push (short *src, int num_samples)
static inline float hermite(float mu1, float a, float b, float c, float d)
{
if (max_write () < num_samples)
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];
clear();
}
~Resampler()
{
delete[] buffer;
}
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;
!num_samples || ring_buffer::push ((unsigned char *) src, num_samples << 1);
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 int
space_empty (void) const
inline bool push(int16_t *src, int num_samples)
{
if (space_empty() < num_samples)
return false;
int end = (start + size) % 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;
}
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] = 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;
start += 2;
if (start >= buffer_size)
start -= buffer_size;
size -= 2;
}
}
}
inline int space_empty(void) const
{
return buffer_size - size;
}
inline int
space_filled (void) const
inline int space_filled(void) const
{
return size;
}
inline int
max_write (void) const
inline int avail(void)
{
return space_empty () >> 1;
//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;
return (int)trunc(((size >> 1) - r_frac) / r_step) * 2;
}
inline void
resize (int num_samples)
inline void resize(int num_samples)
{
ring_buffer::resize (num_samples << 1);
delete[] buffer;
buffer_size = num_samples;
buffer = new int16_t[buffer_size];
clear();
}
};
#endif /* __RESAMPLER_H */
#endif /* __NEW_RESAMPLER_H */

View File

@ -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