snes9x/apu/resampler.h

225 lines
5.6 KiB
C
Raw Normal View History

2018-11-15 23:45:29 +00:00
/*****************************************************************************\
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.
\*****************************************************************************/
2010-09-25 15:46:12 +00:00
2019-02-09 02:23:18 +00:00
#ifndef __NEW_RESAMPLER_H
#define __NEW_RESAMPLER_H
2010-09-25 15:46:12 +00:00
2019-02-09 02:23:18 +00:00
#include <cstring>
#include <cassert>
#include <cstdint>
#include <cmath>
2010-09-25 15:46:12 +00:00
2019-02-09 18:21:38 +00:00
class Resampler
2010-09-25 15:46:12 +00:00
{
2019-02-09 18:21:38 +00:00
public:
volatile int end;
2019-02-09 02:23:18 +00:00
int buffer_size;
volatile int start;
2019-02-09 02:23:18 +00:00
int16_t *buffer;
2019-02-09 02:23:18 +00:00
float r_step;
float r_frac;
int r_left[4], r_right[4];
2019-02-09 18:03:34 +00:00
static inline int16_t short_clamp(int n)
2019-02-09 02:23:18 +00:00
{
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);
}
2019-05-14 20:34:25 +00:00
Resampler()
{
this->buffer_size = 0;
buffer = NULL;
r_step = 1.0;
}
2019-02-09 02:23:18 +00:00
Resampler(int num_samples)
{
buffer = NULL;
resize(num_samples);
2019-02-11 18:32:17 +00:00
r_step = 1.0;
2019-02-09 02:23:18 +00:00
}
~Resampler()
{
delete[] buffer;
2019-02-11 18:32:17 +00:00
buffer = NULL;
2019-02-09 02:23:18 +00:00
}
inline void time_ratio(double ratio)
{
r_step = ratio;
}
inline void clear(void)
{
2019-05-14 20:34:25 +00:00
if (!buffer)
return;
2019-02-09 02:23:18 +00:00
start = 0;
end = 0;
2019-02-09 02:23:18 +00:00
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;
int first_block_size = buffer_size - start;
2019-02-09 02:23:18 +00:00
memcpy(dst, buffer + start, min(num_samples, first_block_size) * 2);
if (num_samples > first_block_size)
memcpy(dst + first_block_size, buffer, (num_samples - first_block_size) * 2);
2019-02-09 02:23:18 +00:00
start = (start + num_samples) % buffer_size;
return true;
}
2019-02-09 17:40:42 +00:00
inline void push_sample(int16_t l, int16_t r)
2019-02-09 02:32:42 +00:00
{
if (space_empty() >= 2)
{
buffer[end] = l;
buffer[end + 1] = r;
end = (end + 2) % buffer_size;
2019-02-09 02:32:42 +00:00
}
}
2019-02-09 02:23:18 +00:00
inline bool push(int16_t *src, int num_samples)
{
if (space_empty() < num_samples)
return false;
2010-09-25 15:46:12 +00:00
int first_block_size = min(num_samples, buffer_size - end);
2019-02-09 02:23:18 +00:00
memcpy(buffer + end, src, first_block_size * 2);
2019-02-09 02:23:18 +00:00
if (num_samples > first_block_size)
memcpy(buffer, src + first_block_size, (num_samples - first_block_size) * 2);
2019-02-09 02:23:18 +00:00
end = (end + num_samples) % buffer_size;
2019-02-09 02:23:18 +00:00
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)
2010-09-25 15:46:12 +00:00
{
2019-02-09 02:23:18 +00:00
pull(data, num_samples);
return;
2010-09-25 15:46:12 +00:00
}
2019-02-09 02:23:18 +00:00
assert((num_samples & 1) == 0); // resampler always processes both stereo samples
int o_position = 0;
while (o_position < num_samples && space_filled() >= 2)
2010-09-25 15:46:12 +00:00
{
2019-02-09 02:23:18 +00:00
int s_left = buffer[start];
int s_right = buffer[start + 1];
int hermite_val[2];
2010-09-25 15:46:12 +00:00
2019-02-09 02:23:18 +00:00
while (r_frac <= 1.0 && o_position < num_samples)
{
2019-02-11 16:12:02 +00:00
hermite_val[0] = (int)hermite(r_frac, (float)r_left[0], (float)r_left[1], (float)r_left[2], (float)r_left[3]);
hermite_val[1] = (int)hermite(r_frac, (float)r_right[0], (float)r_right[1], (float)r_right[2], (float)r_right[3]);
2019-02-09 02:23:18 +00:00
data[o_position] = short_clamp(hermite_val[0]);
data[o_position + 1] = short_clamp(hermite_val[1]);
2010-09-25 15:46:12 +00:00
2019-02-09 02:23:18 +00:00
o_position += 2;
2010-09-25 15:46:12 +00:00
2019-02-09 02:23:18 +00:00
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;
}
2010-09-25 16:46:06 +00:00
}
2019-02-09 02:23:18 +00:00
}
2019-02-09 02:23:18 +00:00
inline int space_empty(void) const
{
return buffer_size - 2 - space_filled();
2019-02-09 02:23:18 +00:00
}
inline int space_filled(void) const
{
int size = end - start;
if (size < 0)
size += buffer_size;
2019-02-09 02:23:18 +00:00
return size;
}
inline int avail(void)
{
int size = space_filled();
2019-02-09 02:23:18 +00:00
//If we are outputting the exact same ratio as the input, find out directly from the input buffer
if (r_step == 1.0)
2010-09-25 16:46:06 +00:00
return size;
2019-02-09 02:23:18 +00:00
return (int)trunc(((size >> 1) - r_frac) / r_step) * 2;
}
2010-09-25 15:46:12 +00:00
2019-02-09 17:40:42 +00:00
void resize(int num_samples)
2019-02-09 02:23:18 +00:00
{
2019-02-09 17:40:42 +00:00
if (buffer)
delete[] buffer;
// Only allow even buffer sizes
if (num_samples & 1)
num_samples++;
2019-02-09 02:23:18 +00:00
buffer_size = num_samples;
buffer = new int16_t[buffer_size];
clear();
}
2010-09-25 15:46:12 +00:00
};
2019-02-09 02:23:18 +00:00
#endif /* __NEW_RESAMPLER_H */