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>
|
||||
|
||||
struct mInterpData {
|
||||
int16_t (*at)(const void* mInterpData, size_t index);
|
||||
CXX_GUARD_START
|
||||
|
||||
struct mInterpolationData {
|
||||
int16_t (*at)(int index, const void* context);
|
||||
void* context;
|
||||
};
|
||||
|
||||
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 {
|
||||
|
@ -28,4 +31,6 @@ struct mInterpolatorSinc {
|
|||
void mInterpolatorSincInit(struct mInterpolatorSinc* interp, unsigned resolution, unsigned width);
|
||||
void mInterpolatorSincDeinit(struct mInterpolatorSinc* interp);
|
||||
|
||||
CXX_GUARD_END
|
||||
|
||||
#endif
|
||||
|
|
|
@ -13,6 +13,8 @@ set(BASE_SOURCE_FILES
|
|||
|
||||
set(SOURCE_FILES
|
||||
${BASE_SOURCE_FILES}
|
||||
audio-buffer.c
|
||||
audio-resampler.c
|
||||
convolve.c
|
||||
elf-read.c
|
||||
geometry.c
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
|
||||
void mAudioBufferInit(struct mAudioBuffer* buffer, size_t capacity, unsigned channels) {
|
||||
mCircleBufferInit(&buffer->data, capacity * channels * sizeof(int16_t));
|
||||
buffer->channels = capacity;
|
||||
buffer->channels = channels;
|
||||
}
|
||||
|
||||
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,
|
||||
};
|
||||
|
||||
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) {
|
||||
interp->d.interpolate = mInterpolatorSincInterpolate;
|
||||
|
@ -33,6 +33,9 @@ void mInterpolatorSincInit(struct mInterpolatorSinc* interp, unsigned resolution
|
|||
interp->sincLut[0] = 0;
|
||||
interp->windowLut[0] = 1;
|
||||
|
||||
interp->width = width;
|
||||
interp->resolution = resolution;
|
||||
|
||||
unsigned i;
|
||||
for (i = 1; i <= samples; ++i, x += dx, y += dy) {
|
||||
interp->sincLut[i] = x < width ? sin(x) / x : 0.0;
|
||||
|
@ -46,27 +49,27 @@ void mInterpolatorSincDeinit(struct mInterpolatorSinc* interp) {
|
|||
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;
|
||||
ssize_t index = (ssize_t) time;
|
||||
int index = 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 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;
|
||||
int i;
|
||||
for (i = 1 - (int) interp->width; i <= (int) interp->width; ++i) {
|
||||
unsigned window = (i >= 0 ? i : -i) * interp->resolution;
|
||||
if (yShift > window) {
|
||||
window = yShift - window;
|
||||
} else {
|
||||
window -= yShift;
|
||||
}
|
||||
|
||||
unsigned sinc = i * step;
|
||||
unsigned sinc = (i >= 0 ? i : -i) * step;
|
||||
if (xShift > sinc) {
|
||||
sinc = xShift - sinc;
|
||||
} else {
|
||||
|
@ -75,7 +78,7 @@ int16_t mInterpolatorSincInterpolate(const struct mInterpolator* interpolator, c
|
|||
|
||||
kernel = interp->sincLut[sinc] * interp->windowLut[window];
|
||||
kernelSum += kernel;
|
||||
sum += data->at(data, index + i) * kernel;
|
||||
sum += data->at(index + i, data->context) * kernel;
|
||||
}
|
||||
return sum / kernelSum;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue