mirror of https://github.com/mgba-emu/mgba.git
Util: Start bringing up better audio resampling
This commit is contained in:
parent
72202544bb
commit
cbd117eb3a
|
@ -0,0 +1,33 @@
|
||||||
|
/* Copyright (c) 2013-2024 Jeffrey Pfau
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
#ifndef M_INTERPOLATOR_H
|
||||||
|
#define M_INTERPOLATOR_H
|
||||||
|
|
||||||
|
#include <mgba-util/common.h>
|
||||||
|
|
||||||
|
struct mSampleBuffer {
|
||||||
|
int16_t* data;
|
||||||
|
size_t samples;
|
||||||
|
int channels;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct mInterpolator {
|
||||||
|
int16_t (*interpolate)(const struct mInterpolator* interp, const struct mSampleBuffer* data, double time, double sampleStep);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct mInterpolatorSinc {
|
||||||
|
struct mInterpolator d;
|
||||||
|
|
||||||
|
unsigned resolution;
|
||||||
|
unsigned width;
|
||||||
|
double* sincLut;
|
||||||
|
double* windowLut;
|
||||||
|
};
|
||||||
|
|
||||||
|
void mInterpolatorSincInit(struct mInterpolatorSinc* interp, unsigned resolution, unsigned width);
|
||||||
|
void mInterpolatorSincDeinit(struct mInterpolatorSinc* interp);
|
||||||
|
|
||||||
|
#endif
|
|
@ -19,6 +19,7 @@ set(SOURCE_FILES
|
||||||
image.c
|
image.c
|
||||||
image/export.c
|
image/export.c
|
||||||
image/png-io.c
|
image/png-io.c
|
||||||
|
interpolator.c
|
||||||
patch.c
|
patch.c
|
||||||
patch-fast.c
|
patch-fast.c
|
||||||
patch-ips.c
|
patch-ips.c
|
||||||
|
|
|
@ -0,0 +1,83 @@
|
||||||
|
/* Copyright (c) 2013-2024 Jeffrey Pfau
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
#include <mgba-util/interpolator.h>
|
||||||
|
|
||||||
|
enum {
|
||||||
|
mSINC_RESOLUTION = 8192,
|
||||||
|
mSINC_WIDTH = 8,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int16_t mInterpolatorSincInterpolate(const struct mInterpolator*, const struct mSampleBuffer* data, double time, double sampleStep);
|
||||||
|
|
||||||
|
void mInterpolatorSincInit(struct mInterpolatorSinc* interp, unsigned resolution, unsigned width) {
|
||||||
|
interp->d.interpolate = mInterpolatorSincInterpolate;
|
||||||
|
|
||||||
|
if (!resolution) {
|
||||||
|
resolution = mSINC_RESOLUTION;
|
||||||
|
}
|
||||||
|
if (!width) {
|
||||||
|
width = mSINC_WIDTH;
|
||||||
|
}
|
||||||
|
unsigned samples = resolution * width;
|
||||||
|
double dy = M_PI / samples;
|
||||||
|
double y = dy;
|
||||||
|
double dx = dy * width;
|
||||||
|
double x = dx;
|
||||||
|
|
||||||
|
interp->sincLut = calloc(samples + 1, sizeof(double));
|
||||||
|
interp->windowLut = calloc(samples + 1, sizeof(double));
|
||||||
|
|
||||||
|
interp->sincLut[0] = 0;
|
||||||
|
interp->windowLut[0] = 1;
|
||||||
|
|
||||||
|
unsigned i;
|
||||||
|
for (i = 1; i <= samples; ++i, x += dx, y += dy) {
|
||||||
|
interp->sincLut[i] = x < width ? sin(x) / x : 0.0;
|
||||||
|
// Three term Nuttall window with continuous first derivative
|
||||||
|
interp->windowLut[i] = 0.40897 + 0.5 * cos(y) + 0.09103 * cos(2 * y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void mInterpolatorSincDeinit(struct mInterpolatorSinc* interp) {
|
||||||
|
free(interp->sincLut);
|
||||||
|
free(interp->windowLut);
|
||||||
|
}
|
||||||
|
|
||||||
|
int16_t mInterpolatorSincInterpolate(const struct mInterpolator* interpolator, const struct mSampleBuffer* data, double time, double sampleStep) {
|
||||||
|
struct mInterpolatorSinc* interp = (struct mInterpolatorSinc*) interpolator;
|
||||||
|
ssize_t index = (ssize_t) time;
|
||||||
|
double subsample = time - floor(time);
|
||||||
|
unsigned step = sampleStep > 1 ? interp->resolution * sampleStep : interp->resolution;
|
||||||
|
unsigned yShift = subsample * step;
|
||||||
|
unsigned xShift = subsample * interp->resolution;
|
||||||
|
double sum = 0.0;
|
||||||
|
double kernelSum = 0.0;
|
||||||
|
double kernel;
|
||||||
|
|
||||||
|
ssize_t i;
|
||||||
|
for (i = 1 - (ssize_t) interp->width; i <= (ssize_t) interp->width; ++i) {
|
||||||
|
unsigned window = i * interp->resolution;
|
||||||
|
if (yShift > window) {
|
||||||
|
window = yShift - window;
|
||||||
|
} else {
|
||||||
|
window -= yShift;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned sinc = i * step;
|
||||||
|
if (xShift > sinc) {
|
||||||
|
sinc = xShift - sinc;
|
||||||
|
} else {
|
||||||
|
sinc -= xShift;
|
||||||
|
}
|
||||||
|
|
||||||
|
kernel = interp->sincLut[sinc] * interp->windowLut[window];
|
||||||
|
kernelSum += kernel;
|
||||||
|
if (index + i >= 0 && index + i < (ssize_t) data->samples) {
|
||||||
|
sum += data->data[(index + i) * data->channels] * kernel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sum / kernelSum;
|
||||||
|
}
|
Loading…
Reference in New Issue