RetroArch/audio/sinc.c

315 lines
8.6 KiB
C
Raw Normal View History

2012-04-21 21:13:50 +00:00
/* RetroArch - A frontend for libretro.
2012-02-23 22:53:18 +00:00
* Copyright (C) 2010-2012 - Hans-Kristian Arntzen
*
2012-04-21 21:13:50 +00:00
* RetroArch is free software: you can redistribute it and/or modify it under the terms
2012-02-23 22:53:18 +00:00
* of the GNU General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
2012-04-21 21:13:50 +00:00
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
2012-02-23 22:53:18 +00:00
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
2012-04-21 21:31:57 +00:00
* You should have received a copy of the GNU General Public License along with RetroArch.
2012-02-23 22:53:18 +00:00
* If not, see <http://www.gnu.org/licenses/>.
*/
// Bog-standard windowed SINC implementation.
// Only suitable as an upsampler, as there is no low-pass filter stage.
#include "resampler.h"
#include <math.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
2012-02-25 00:47:23 +00:00
#ifndef RESAMPLER_TEST
#include "../general.h"
#else
2012-04-21 21:25:32 +00:00
#define RARCH_LOG(...)
2012-02-25 00:47:23 +00:00
#endif
2012-02-23 22:53:18 +00:00
#if __SSE__
#include <xmmintrin.h>
#endif
#define PHASE_BITS 8
2012-07-06 15:36:37 +00:00
#define SUBPHASE_BITS 15
2012-02-23 22:53:18 +00:00
#define PHASES (1 << PHASE_BITS)
#define PHASES_SHIFT (SUBPHASE_BITS)
#define SUBPHASES (1 << SUBPHASE_BITS)
#define SUBPHASES_SHIFT 0
#define SUBPHASES_MASK ((1 << SUBPHASE_BITS) - 1)
#define PHASES_WRAP (1 << (PHASE_BITS + SUBPHASE_BITS))
#define FRAMES_SHIFT (PHASE_BITS + SUBPHASE_BITS)
2012-05-08 10:11:56 +00:00
#define SIDELOBES 8
2012-02-24 20:01:29 +00:00
#define TAPS (SIDELOBES * 2)
2012-05-08 10:11:56 +00:00
#define CUTOFF 1.0
2012-02-24 20:01:29 +00:00
#define PHASE_INDEX 0
#define DELTA_INDEX 1
2012-02-23 22:53:18 +00:00
2012-04-21 21:25:32 +00:00
struct rarch_resampler
2012-02-23 22:53:18 +00:00
{
2012-07-06 15:36:37 +00:00
sample_t phase_table[PHASES][2][TAPS];
sample_t buffer_l[2 * TAPS];
sample_t buffer_r[2 * TAPS];
2012-02-28 21:58:19 +00:00
unsigned ptr;
2012-02-23 22:53:18 +00:00
uint32_t time;
};
static inline double sinc(double val)
{
if (fabs(val) < 0.00001)
return 1.0;
else
return sin(val) / val;
}
2012-02-25 15:45:06 +00:00
static inline double lanzcos(double index)
2012-02-23 22:53:18 +00:00
{
2012-02-25 15:45:06 +00:00
return sinc(index);
2012-02-23 22:53:18 +00:00
}
2012-04-21 21:25:32 +00:00
static void init_sinc_table(rarch_resampler_t *resamp)
2012-02-23 22:53:18 +00:00
{
2012-02-24 20:01:29 +00:00
// Sinc phases: [..., p + 3, p + 2, p + 1, p + 0, p - 1, p - 2, p - 3, p - 4, ...]
for (int i = 0; i < PHASES; i++)
2012-02-23 22:53:18 +00:00
{
2012-02-25 15:33:33 +00:00
for (int j = 0; j < TAPS; j++)
2012-02-23 22:53:18 +00:00
{
2012-02-24 20:01:29 +00:00
double p = (double)i / PHASES;
double sinc_phase = M_PI * (p + (SIDELOBES - 1 - j));
2012-07-06 15:36:37 +00:00
float val = CUTOFF * sinc(CUTOFF * sinc_phase) * lanzcos(sinc_phase / SIDELOBES);
#ifdef HAVE_FIXED_POINT
resamp->phase_table[i][PHASE_INDEX][j] = (int16_t)(val * 0x7fff);
#else
resamp->phase_table[i][PHASE_INDEX][j] = val;
#endif
2012-02-23 22:53:18 +00:00
}
}
// Optimize linear interpolation.
2012-02-24 20:01:29 +00:00
for (int i = 0; i < PHASES - 1; i++)
{
2012-02-25 15:33:33 +00:00
for (int j = 0; j < TAPS; j++)
2012-02-24 20:01:29 +00:00
{
2012-07-06 15:36:37 +00:00
#ifdef HAVE_FIXED_POINT
resamp->phase_table[i][DELTA_INDEX][j] =
(resamp->phase_table[i + 1][PHASE_INDEX][j] - resamp->phase_table[i][PHASE_INDEX][j]);
#else
2012-02-24 20:01:29 +00:00
resamp->phase_table[i][DELTA_INDEX][j] =
(resamp->phase_table[i + 1][PHASE_INDEX][j] - resamp->phase_table[i][PHASE_INDEX][j]) / SUBPHASES;
2012-07-06 15:36:37 +00:00
#endif
2012-02-24 20:01:29 +00:00
}
}
2012-02-25 15:33:33 +00:00
// Interpolation between [PHASES - 1] => [PHASES]
for (int j = 0; j < TAPS; j++)
{
double p = 1.0;
double sinc_phase = M_PI * (p + (SIDELOBES - 1 - j));
2012-02-26 18:16:46 +00:00
double phase = CUTOFF * sinc(CUTOFF * sinc_phase) * lanzcos(sinc_phase / SIDELOBES);
2012-02-25 15:33:33 +00:00
2012-07-06 15:36:37 +00:00
#ifdef HAVE_FIXED_POINT
int16_t result = 0x7fff * phase - resamp->phase_table[PHASES - 1][PHASE_INDEX][j];
#else
2012-02-25 15:33:33 +00:00
float result = (phase - resamp->phase_table[PHASES - 1][PHASE_INDEX][j]) / SUBPHASES;
2012-07-06 15:36:37 +00:00
#endif
2012-02-25 15:33:33 +00:00
resamp->phase_table[PHASES - 1][DELTA_INDEX][j] = result;
}
2012-02-23 22:53:18 +00:00
}
2012-02-25 21:37:40 +00:00
// No memalign() for us on Win32 ...
2012-07-06 15:36:37 +00:00
static void *aligned_alloc__(size_t boundary, size_t size)
2012-02-25 21:37:40 +00:00
{
void *ptr = malloc(boundary + size + sizeof(uintptr_t));
if (!ptr)
return NULL;
uintptr_t addr = ((uintptr_t)ptr + sizeof(uintptr_t) + boundary) & ~(boundary - 1);
2012-07-06 15:36:37 +00:00
void **place = (void**)addr;
place[-1] = ptr;
2012-02-25 21:37:40 +00:00
return (void*)addr;
}
2012-07-06 15:36:37 +00:00
static void aligned_free__(void *ptr)
2012-02-25 21:37:40 +00:00
{
void **p = (void**)ptr;
free(p[-1]);
}
2012-04-21 21:25:32 +00:00
rarch_resampler_t *resampler_new(void)
2012-02-23 22:53:18 +00:00
{
2012-07-06 15:36:37 +00:00
rarch_resampler_t *re = (rarch_resampler_t*)aligned_alloc__(16, sizeof(*re));
2012-02-23 22:53:18 +00:00
if (!re)
return NULL;
memset(re, 0, sizeof(*re));
init_sinc_table(re);
2012-02-24 20:01:29 +00:00
2012-07-06 15:36:37 +00:00
#ifdef HAVE_FIXED_POINT
RARCH_LOG("Sinc resampler [Fixed]\n");
#elif __SSE__
2012-04-21 21:25:32 +00:00
RARCH_LOG("Sinc resampler [SSE]\n");
2012-02-24 20:01:29 +00:00
#else
2012-04-21 21:25:32 +00:00
RARCH_LOG("Sinc resampler [C]\n");
2012-02-24 20:01:29 +00:00
#endif
2012-02-23 22:53:18 +00:00
return re;
}
2012-07-06 15:36:37 +00:00
#ifdef HAVE_FIXED_POINT
static inline int16_t saturate(int32_t val)
{
if (val > 0x7fff)
return 0x7fff;
else if (val < -0x8000)
return -0x8000;
else
return val;
}
static void process_sinc(rarch_resampler_t *resamp, int16_t *out_buffer)
{
int32_t sum_l = 0;
int32_t sum_r = 0;
const int16_t *buffer_l = resamp->buffer_l + resamp->ptr;
const int16_t *buffer_r = resamp->buffer_r + resamp->ptr;
unsigned phase = resamp->time >> PHASES_SHIFT;
unsigned delta = (resamp->time >> SUBPHASES_SHIFT) & SUBPHASES_MASK;
const int16_t *phase_table = resamp->phase_table[phase][PHASE_INDEX];
const int16_t *delta_table = resamp->phase_table[phase][DELTA_INDEX];
for (unsigned i = 0; i < TAPS; i++)
{
int16_t sinc_val = phase_table[i] + ((delta * delta_table[i] + 0x4000) >> 15);
sum_l += (buffer_l[i] * sinc_val + 0x4000) >> 15;
sum_r += (buffer_r[i] * sinc_val + 0x4000) >> 15;
}
out_buffer[0] = saturate(sum_l);
out_buffer[1] = saturate(sum_r);
}
#elif __SSE__
2012-04-21 21:25:32 +00:00
static void process_sinc(rarch_resampler_t *resamp, float *out_buffer)
2012-02-23 22:53:18 +00:00
{
__m128 sum_l = _mm_setzero_ps();
__m128 sum_r = _mm_setzero_ps();
2012-02-28 21:58:19 +00:00
const float *buffer_l = resamp->buffer_l + resamp->ptr;
const float *buffer_r = resamp->buffer_r + resamp->ptr;
2012-02-23 22:53:18 +00:00
unsigned phase = resamp->time >> PHASES_SHIFT;
unsigned delta = (resamp->time >> SUBPHASES_SHIFT) & SUBPHASES_MASK;
2012-02-24 20:01:29 +00:00
__m128 delta_f = _mm_set1_ps(delta);
2012-02-23 22:53:18 +00:00
2012-02-24 20:01:29 +00:00
const float *phase_table = resamp->phase_table[phase][PHASE_INDEX];
const float *delta_table = resamp->phase_table[phase][DELTA_INDEX];
2012-02-23 22:53:18 +00:00
2012-02-24 20:01:29 +00:00
for (unsigned i = 0; i < TAPS; i += 4)
2012-02-23 22:53:18 +00:00
{
2012-02-28 21:58:19 +00:00
__m128 buf_l = _mm_loadu_ps(buffer_l + i);
__m128 buf_r = _mm_loadu_ps(buffer_r + i);
2012-02-23 22:53:18 +00:00
2012-02-24 20:01:29 +00:00
__m128 phases = _mm_load_ps(phase_table + i);
__m128 deltas = _mm_load_ps(delta_table + i);
2012-02-23 22:53:18 +00:00
2012-02-24 20:01:29 +00:00
__m128 sinc = _mm_add_ps(phases, _mm_mul_ps(deltas, delta_f));
2012-02-23 22:53:18 +00:00
2012-02-24 20:01:29 +00:00
sum_l = _mm_add_ps(sum_l, _mm_mul_ps(buf_l, sinc));
sum_r = _mm_add_ps(sum_r, _mm_mul_ps(buf_r, sinc));
2012-02-23 22:53:18 +00:00
}
2012-02-25 20:47:40 +00:00
// Them annoying shuffles :V
// sum_l = { l3, l2, l1, l0 }
// sum_r = { r3, r2, r1, r0 }
__m128 sum = _mm_add_ps(_mm_shuffle_ps(sum_l, sum_r, _MM_SHUFFLE(1, 0, 1, 0)),
_mm_shuffle_ps(sum_l, sum_r, _MM_SHUFFLE(3, 2, 3, 2)));
// sum = { r1, r0, l1, l0 } + { r3, r2, l3, l2 }
// sum = { R1, R0, L1, L0 }
2012-02-28 21:58:19 +00:00
sum = _mm_add_ps(_mm_shuffle_ps(sum, sum, _MM_SHUFFLE(3, 3, 1, 1)), sum);
2012-02-25 20:47:40 +00:00
// sum = {R1, R1, L1, L1 } + { R1, R0, L1, L0 }
// sum = { X, R, X, L }
// Store L
_mm_store_ss(out_buffer + 0, sum);
// movehl { X, R, X, L } == { X, R, X, R }
_mm_store_ss(out_buffer + 1, _mm_movehl_ps(sum, sum));
2012-02-23 22:53:18 +00:00
}
#else // Plain ol' C99
2012-04-21 21:25:32 +00:00
static void process_sinc(rarch_resampler_t *resamp, float *out_buffer)
2012-02-23 22:53:18 +00:00
{
float sum_l = 0.0f;
float sum_r = 0.0f;
2012-02-28 21:58:19 +00:00
const float *buffer_l = resamp->buffer_l + resamp->ptr;
const float *buffer_r = resamp->buffer_r + resamp->ptr;
2012-02-23 22:53:18 +00:00
unsigned phase = resamp->time >> PHASES_SHIFT;
unsigned delta = (resamp->time >> SUBPHASES_SHIFT) & SUBPHASES_MASK;
2012-02-24 20:01:29 +00:00
float delta_f = (float)delta;
2012-02-23 22:53:18 +00:00
2012-02-24 20:01:29 +00:00
const float *phase_table = resamp->phase_table[phase][PHASE_INDEX];
const float *delta_table = resamp->phase_table[phase][DELTA_INDEX];
2012-02-23 22:53:18 +00:00
2012-02-24 20:01:29 +00:00
for (unsigned i = 0; i < TAPS; i++)
2012-02-23 22:53:18 +00:00
{
2012-02-24 20:01:29 +00:00
float sinc_val = phase_table[i] + delta_f * delta_table[i];
sum_l += buffer_l[i] * sinc_val;
sum_r += buffer_r[i] * sinc_val;
2012-02-23 22:53:18 +00:00
}
out_buffer[0] = sum_l;
out_buffer[1] = sum_r;
}
#endif
2012-04-21 21:25:32 +00:00
void resampler_process(rarch_resampler_t *re, struct resampler_data *data)
2012-02-23 22:53:18 +00:00
{
uint32_t ratio = PHASES_WRAP / data->ratio;
2012-07-06 15:36:37 +00:00
const sample_t *input = data->data_in;
sample_t *output = data->data_out;
size_t frames = data->input_frames;
size_t out_frames = 0;
2012-02-23 22:53:18 +00:00
while (frames)
{
process_sinc(re, output);
output += 2;
out_frames++;
re->time += ratio;
while (re->time >= PHASES_WRAP)
{
2012-02-28 21:58:19 +00:00
re->buffer_l[re->ptr + TAPS] = re->buffer_l[re->ptr] = *input++;
re->buffer_r[re->ptr + TAPS] = re->buffer_r[re->ptr] = *input++;
re->ptr = (re->ptr + 1) & (TAPS - 1);
2012-02-23 22:53:18 +00:00
re->time -= PHASES_WRAP;
frames--;
}
}
data->output_frames = out_frames;
}
2012-04-21 21:25:32 +00:00
void resampler_free(rarch_resampler_t *re)
2012-02-23 22:53:18 +00:00
{
2012-07-06 15:36:37 +00:00
aligned_free__(re);
2012-02-23 22:53:18 +00:00
}