Util: Start bringing up new resampler

This commit is contained in:
Vicki Pfau 2024-04-16 01:21:29 -07:00
parent 4bd09bdac2
commit 9fa825e336
6 changed files with 154 additions and 13 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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) {

View File

@ -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;
}

View File

@ -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;
}