mirror of https://github.com/mgba-emu/mgba.git
Util: Start bringing up new resampler
This commit is contained in:
parent
4bd09bdac2
commit
9fa825e336
|
@ -0,0 +1,37 @@
|
||||||
|
/* 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_AUDIO_RESAMPLER_H
|
||||||
|
#define M_AUDIO_RESAMPLER_H
|
||||||
|
|
||||||
|
#include <mgba-util/common.h>
|
||||||
|
|
||||||
|
CXX_GUARD_START
|
||||||
|
|
||||||
|
#include <mgba-util/interpolator.h>
|
||||||
|
|
||||||
|
struct mAudioBuffer;
|
||||||
|
struct mAudioResampler {
|
||||||
|
struct mAudioBuffer* source;
|
||||||
|
struct mAudioBuffer* destination;
|
||||||
|
double sourceRate;
|
||||||
|
double destRate;
|
||||||
|
double timestamp;
|
||||||
|
double lowWaterMark;
|
||||||
|
double highWaterMark;
|
||||||
|
struct mInterpolatorSinc interp;
|
||||||
|
bool consume;
|
||||||
|
};
|
||||||
|
|
||||||
|
void mAudioResamplerInit(struct mAudioResampler*);
|
||||||
|
void mAudioResamplerDeinit(struct mAudioResampler*);
|
||||||
|
void mAudioResamplerSetSource(struct mAudioResampler*, struct mAudioBuffer* source, double rate, bool consume);
|
||||||
|
void mAudioResamplerSetDestination(struct mAudioResampler*, struct mAudioBuffer* destination, double rate);
|
||||||
|
size_t mAudioResamplerProcess(struct mAudioResampler*);
|
||||||
|
|
||||||
|
CXX_GUARD_END
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
|
@ -8,12 +8,15 @@
|
||||||
|
|
||||||
#include <mgba-util/common.h>
|
#include <mgba-util/common.h>
|
||||||
|
|
||||||
struct mInterpData {
|
CXX_GUARD_START
|
||||||
int16_t (*at)(const void* mInterpData, size_t index);
|
|
||||||
|
struct mInterpolationData {
|
||||||
|
int16_t (*at)(int index, const void* context);
|
||||||
|
void* context;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct mInterpolator {
|
struct mInterpolator {
|
||||||
int16_t (*interpolate)(const struct mInterpolator* interp, const struct mInterpData* data, double time, double sampleStep);
|
int16_t (*interpolate)(const struct mInterpolator* interp, const struct mInterpolationData* data, double time, double sampleStep);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct mInterpolatorSinc {
|
struct mInterpolatorSinc {
|
||||||
|
@ -28,4 +31,6 @@ struct mInterpolatorSinc {
|
||||||
void mInterpolatorSincInit(struct mInterpolatorSinc* interp, unsigned resolution, unsigned width);
|
void mInterpolatorSincInit(struct mInterpolatorSinc* interp, unsigned resolution, unsigned width);
|
||||||
void mInterpolatorSincDeinit(struct mInterpolatorSinc* interp);
|
void mInterpolatorSincDeinit(struct mInterpolatorSinc* interp);
|
||||||
|
|
||||||
|
CXX_GUARD_END
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -13,6 +13,8 @@ set(BASE_SOURCE_FILES
|
||||||
|
|
||||||
set(SOURCE_FILES
|
set(SOURCE_FILES
|
||||||
${BASE_SOURCE_FILES}
|
${BASE_SOURCE_FILES}
|
||||||
|
audio-buffer.c
|
||||||
|
audio-resampler.c
|
||||||
convolve.c
|
convolve.c
|
||||||
elf-read.c
|
elf-read.c
|
||||||
geometry.c
|
geometry.c
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
|
|
||||||
void mAudioBufferInit(struct mAudioBuffer* buffer, size_t capacity, unsigned channels) {
|
void mAudioBufferInit(struct mAudioBuffer* buffer, size_t capacity, unsigned channels) {
|
||||||
mCircleBufferInit(&buffer->data, capacity * channels * sizeof(int16_t));
|
mCircleBufferInit(&buffer->data, capacity * channels * sizeof(int16_t));
|
||||||
buffer->channels = capacity;
|
buffer->channels = channels;
|
||||||
}
|
}
|
||||||
|
|
||||||
void mAudioBufferDeinit(struct mAudioBuffer* buffer) {
|
void mAudioBufferDeinit(struct mAudioBuffer* buffer) {
|
||||||
|
|
|
@ -0,0 +1,94 @@
|
||||||
|
/* 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/audio-resampler.h>
|
||||||
|
|
||||||
|
#include <mgba-util/audio-buffer.h>
|
||||||
|
|
||||||
|
#define MAX_CHANNELS 2
|
||||||
|
|
||||||
|
struct mAudioResamplerData {
|
||||||
|
struct mAudioResampler* resampler;
|
||||||
|
unsigned channel;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int16_t _sampleAt(int index, const void* context) {
|
||||||
|
const struct mAudioResamplerData* data = context;
|
||||||
|
if (index < 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return mAudioBufferPeek(data->resampler->source, data->channel, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
void mAudioResamplerInit(struct mAudioResampler* resampler) {
|
||||||
|
memset(resampler, 0, sizeof(*resampler));
|
||||||
|
mInterpolatorSincInit(&resampler->interp, 0, 0);
|
||||||
|
resampler->lowWaterMark = resampler->interp.width;
|
||||||
|
resampler->highWaterMark = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void mAudioResamplerDeinit(struct mAudioResampler* resampler) {
|
||||||
|
mInterpolatorSincDeinit(&resampler->interp);
|
||||||
|
resampler->source = NULL;
|
||||||
|
resampler->destination = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void mAudioResamplerSetSource(struct mAudioResampler* resampler, struct mAudioBuffer* source, double rate, bool consume) {
|
||||||
|
resampler->source = source;
|
||||||
|
resampler->sourceRate = rate;
|
||||||
|
resampler->consume = consume;
|
||||||
|
}
|
||||||
|
|
||||||
|
void mAudioResamplerSetDestination(struct mAudioResampler* resampler, struct mAudioBuffer* destination, double rate) {
|
||||||
|
resampler->destination = destination;
|
||||||
|
resampler->destRate = rate;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t mAudioResamplerProcess(struct mAudioResampler* resampler) {
|
||||||
|
int16_t sampleBuffer[MAX_CHANNELS] = {0};
|
||||||
|
double timestep = resampler->sourceRate / resampler->destRate;
|
||||||
|
double timestamp = resampler->timestamp;
|
||||||
|
struct mInterpolator* interp = &resampler->interp.d;
|
||||||
|
struct mAudioResamplerData context = {
|
||||||
|
.resampler = resampler,
|
||||||
|
};
|
||||||
|
struct mInterpolationData data = {
|
||||||
|
.at = _sampleAt,
|
||||||
|
.context = &context,
|
||||||
|
};
|
||||||
|
|
||||||
|
size_t read = 0;
|
||||||
|
if (resampler->source->channels > MAX_CHANNELS) {
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
if (timestamp + resampler->highWaterMark >= mAudioBufferAvailable(resampler->source)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (mAudioBufferAvailable(resampler->destination) == mAudioBufferCapacity(resampler->destination)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t channel;
|
||||||
|
for (channel = 0; channel < resampler->source->channels; ++channel) {
|
||||||
|
context.channel = channel;
|
||||||
|
sampleBuffer[channel] = interp->interpolate(interp, &data, timestamp, timestep);
|
||||||
|
}
|
||||||
|
if (!mAudioBufferWrite(resampler->destination, sampleBuffer, 1)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
timestamp += timestep;
|
||||||
|
++read;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resampler->consume && timestamp > resampler->lowWaterMark) {
|
||||||
|
size_t drop = timestamp - resampler->lowWaterMark;
|
||||||
|
drop = mAudioBufferRead(resampler->source, NULL, drop);
|
||||||
|
timestamp -= drop;
|
||||||
|
}
|
||||||
|
resampler->timestamp = timestamp;
|
||||||
|
return read;
|
||||||
|
}
|
|
@ -10,7 +10,7 @@ enum {
|
||||||
mSINC_WIDTH = 8,
|
mSINC_WIDTH = 8,
|
||||||
};
|
};
|
||||||
|
|
||||||
static int16_t mInterpolatorSincInterpolate(const struct mInterpolator*, const struct mInterpData*, double time, double sampleStep);
|
static int16_t mInterpolatorSincInterpolate(const struct mInterpolator*, const struct mInterpolationData*, double time, double sampleStep);
|
||||||
|
|
||||||
void mInterpolatorSincInit(struct mInterpolatorSinc* interp, unsigned resolution, unsigned width) {
|
void mInterpolatorSincInit(struct mInterpolatorSinc* interp, unsigned resolution, unsigned width) {
|
||||||
interp->d.interpolate = mInterpolatorSincInterpolate;
|
interp->d.interpolate = mInterpolatorSincInterpolate;
|
||||||
|
@ -33,6 +33,9 @@ void mInterpolatorSincInit(struct mInterpolatorSinc* interp, unsigned resolution
|
||||||
interp->sincLut[0] = 0;
|
interp->sincLut[0] = 0;
|
||||||
interp->windowLut[0] = 1;
|
interp->windowLut[0] = 1;
|
||||||
|
|
||||||
|
interp->width = width;
|
||||||
|
interp->resolution = resolution;
|
||||||
|
|
||||||
unsigned i;
|
unsigned i;
|
||||||
for (i = 1; i <= samples; ++i, x += dx, y += dy) {
|
for (i = 1; i <= samples; ++i, x += dx, y += dy) {
|
||||||
interp->sincLut[i] = x < width ? sin(x) / x : 0.0;
|
interp->sincLut[i] = x < width ? sin(x) / x : 0.0;
|
||||||
|
@ -46,27 +49,27 @@ void mInterpolatorSincDeinit(struct mInterpolatorSinc* interp) {
|
||||||
free(interp->windowLut);
|
free(interp->windowLut);
|
||||||
}
|
}
|
||||||
|
|
||||||
int16_t mInterpolatorSincInterpolate(const struct mInterpolator* interpolator, const struct mInterpData* data, double time, double sampleStep) {
|
int16_t mInterpolatorSincInterpolate(const struct mInterpolator* interpolator, const struct mInterpolationData* data, double time, double sampleStep) {
|
||||||
struct mInterpolatorSinc* interp = (struct mInterpolatorSinc*) interpolator;
|
struct mInterpolatorSinc* interp = (struct mInterpolatorSinc*) interpolator;
|
||||||
ssize_t index = (ssize_t) time;
|
int index = time;
|
||||||
double subsample = time - floor(time);
|
double subsample = time - floor(time);
|
||||||
unsigned step = sampleStep > 1 ? interp->resolution * sampleStep : interp->resolution;
|
unsigned step = sampleStep < 1 ? interp->resolution * sampleStep : interp->resolution;
|
||||||
unsigned yShift = subsample * step;
|
unsigned yShift = subsample * step;
|
||||||
unsigned xShift = subsample * interp->resolution;
|
unsigned xShift = subsample * interp->resolution;
|
||||||
double sum = 0.0;
|
double sum = 0.0;
|
||||||
double kernelSum = 0.0;
|
double kernelSum = 0.0;
|
||||||
double kernel;
|
double kernel;
|
||||||
|
|
||||||
ssize_t i;
|
int i;
|
||||||
for (i = 1 - (ssize_t) interp->width; i <= (ssize_t) interp->width; ++i) {
|
for (i = 1 - (int) interp->width; i <= (int) interp->width; ++i) {
|
||||||
unsigned window = i * interp->resolution;
|
unsigned window = (i >= 0 ? i : -i) * interp->resolution;
|
||||||
if (yShift > window) {
|
if (yShift > window) {
|
||||||
window = yShift - window;
|
window = yShift - window;
|
||||||
} else {
|
} else {
|
||||||
window -= yShift;
|
window -= yShift;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned sinc = i * step;
|
unsigned sinc = (i >= 0 ? i : -i) * step;
|
||||||
if (xShift > sinc) {
|
if (xShift > sinc) {
|
||||||
sinc = xShift - sinc;
|
sinc = xShift - sinc;
|
||||||
} else {
|
} else {
|
||||||
|
@ -75,7 +78,7 @@ int16_t mInterpolatorSincInterpolate(const struct mInterpolator* interpolator, c
|
||||||
|
|
||||||
kernel = interp->sincLut[sinc] * interp->windowLut[window];
|
kernel = interp->sincLut[sinc] * interp->windowLut[window];
|
||||||
kernelSum += kernel;
|
kernelSum += kernel;
|
||||||
sum += data->at(data, index + i) * kernel;
|
sum += data->at(index + i, data->context) * kernel;
|
||||||
}
|
}
|
||||||
return sum / kernelSum;
|
return sum / kernelSum;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue