rewrite lut generation to be a bit more correct; fix looping

master
zetaPRIME 2021-11-10 16:57:00 -05:00
parent 72b5eb3b53
commit 4c6c135617
5 changed files with 41 additions and 16 deletions

4
notes
View File

@ -32,6 +32,10 @@ parameters {
TODO {
immediate frontburner {
... resampling matches modplug-xmms with kaiser window, not modplug's polyphase
rename resamp function?
PLAYBACK BREAKS AFTER FIRST PLAY??? {
doesn't happen in Lovely Storm; only where Capaxitor is used??
resolves itself after project switch

View File

@ -1,6 +1,7 @@
#include "resampler.h"
using namespace Xybrid::NodeLib;
#include <QDebug>
#include <iostream>
#include <array>
@ -34,14 +35,22 @@ const std::array<std::array<double, LUT_TAPS>, LUT_STEPS> Xybrid::NodeLib::resam
double denom = cyl_bessel_i(0, KAISER_BETA);
std::array<std::array<double, LUT_TAPS>, LUT_STEPS> t;
t[0] = {0, 0, 0, 1, 0, 0, 0, 0}; // we already know the ideal integer step
for (size_t step = 1; step < LUT_STEPS; step++) {
double sv = static_cast<double>(step) / LUT_STEPS;
//t[0] = {0, 0, 0, 1, 0, 0, 0, 0}; // we already know the ideal integer step
for (size_t step = 0; step < LUT_STEPS; step++) {
double sv = static_cast<double>(step) / LUT_STEPS; // subvalue (offset of tap position)
for (size_t tap = 0; tap < LUT_TAPS; tap++) {
double x = static_cast<double>(tap) - sv;
t[step][tap] = sinc(x-(LUT_TAPS/2-1)) * (cyl_bessel_i(0, KAISER_BETA * std::sqrt(1 - std::pow(((2 * (x+1)) / (LUT_TAPS)) - 1, 2))) / denom);
double x = static_cast<double>(tap) - sv; // x position of tap;
//double kaiser = cyl_bessel_i(0, KAISER_BETA * std::sqrt(1 - std::pow(((2 * (x+1)) / (LUT_TAPS)) - 1, 2))) / denom; // old kaiser window generation
double kaiser = cyl_bessel_i(0, KAISER_BETA * std::sqrt(1.0 - std::pow( (2.0*x)/(LUT_TAPS-2) - 1.0, 2 ) ) ) / denom; // kaiser window of length LUT_TAPS-1
//double idl = (2.0*PI)/(LUT_TAPS-1);
//double kaiser = 0.40243 - 0.49804 * std::cos(idl * x) + 0.09831 * std::cos(2.0 * idl * x) - 0.00122 * std::cos(3.0 * idl * x); // approximate
t[step][tap] = sinc(x-(LUT_TAPS/2-1)) * std::max(kaiser, 0.0); // sinc function centered on main tap, offset by subvalue, multiplied by window
if (t[step][tap] != t[step][tap]) t[step][tap] = 0; // NaN guard
}
}
/*t[0] = {};
t[0][LUT_HTAPS] = 1;*/
/*for (auto v : t[0]) std::cout << v << ", ";
std::cout << std::endl;*/
return t;
}();

View File

@ -9,10 +9,11 @@
namespace Xybrid::NodeLib {
const constexpr size_t LUT_TAPS = 8;
const constexpr ptrdiff_t LUT_HTAPS = LUT_TAPS/2-1;//static_cast<ptrdiff_t>(LUT_TAPS - (LUT_TAPS+0.5)/2);
const constexpr size_t LUT_STEPS = 1024;
extern const std::array<std::array<double, LUT_TAPS>, LUT_STEPS> resamplerLUT;
inline Data::AudioFrame resamp(Data::Sample* smp, double pos) {
inline Data::AudioFrame resamp(Data::Sample* smp, double pos, double rate [[maybe_unused]]) {
auto loop = smp->loopStart >= 0;
auto len = static_cast<ptrdiff_t>(smp->length());
auto ls = static_cast<ptrdiff_t>(smp->loopStart);
@ -24,13 +25,25 @@ namespace Xybrid::NodeLib {
Data::AudioFrame out(0.0);
auto ii = static_cast<ptrdiff_t>(ip) - 3;
for (uint8_t i = 0; i < 8; i++) {
if (false && rate > 1.0) {
//*
auto ii = static_cast<ptrdiff_t>(ip);
if (loop && ii > le) ii = (ii - ls) % ll + ls;
if (ii >= 0 && ii < len) out += (*smp)[static_cast<size_t>(ii)] * pt[i];
if (ii >= 0 && ii < len) out += (*smp)[static_cast<size_t>(ii)] * (1.0-(pos-ip));
ii++;
if (loop && ii > le) ii = (ii - ls) % ll + ls;
if (ii >= 0 && ii < len) out += (*smp)[static_cast<size_t>(ii)] * (1.0*(pos-ip));//*/
} else {
//*
auto ii = static_cast<ptrdiff_t>(ip) - LUT_HTAPS;
for (size_t i = 0; i < 8; i++) {
if (loop && ii >= le) ii = ((ii - ls) % ll) + ls;
if (ii >= 0 && ii < len) out += (*smp)[static_cast<size_t>(ii)] * pt[i];
ii++;
}//*/
}
return out;
}
}

View File

@ -75,11 +75,6 @@ void Capaxitor::init() {
data.~NoteData(); // destroy
};
/*core.globalParam['Q'] = [](const ParamReader& pr) {
qDebug() << "global recieved" << pr.param() << pr.val();
return true;
};*/
core.processNote = [this](Note& note, AudioPort* p) {
auto& data = *reinterpret_cast<NoteData*>(&note.scratch);
auto smp = this->smp.lock();
@ -97,9 +92,11 @@ void Capaxitor::init() {
// actual sample pos
double sp = data.sampleTime * rate;
auto out = NodeLib::resamp(smp.get(), sp);
auto out = NodeLib::resamp(smp.get(), sp, rate*fr);
(*p)[i] += out.gainBalance(0, note.pan) * note.ampMult();
data.flt += (out - data.flt) * 0.97;
(*p)[i] += data.flt.gainBalance(0, note.pan) * note.ampMult();
data.sampleTime += fr;
}
};

View File

@ -1,6 +1,7 @@
#pragma once
#include "nodelib/instrumentcore.h"
#include "data/audioframe.h"
namespace Xybrid::Data { class Sample; }
namespace Xybrid::Instruments {
@ -9,6 +10,7 @@ namespace Xybrid::Instruments {
struct NoteData {
double sampleTime = 0;
Data::AudioFrame flt;
NoteData() = default;
~NoteData() = default;