innovative™️ dual-LUT system. because it sounds better for some reason.
parent
a78d41b134
commit
60df49db69
21
notes
21
notes
|
@ -34,16 +34,15 @@ 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
|
||||
does NOT break preview mode??
|
||||
seems to be only with the Maybe sample??? idk
|
||||
fiddling with things, figured out much better sounding cases {
|
||||
rate<1.0, omit window
|
||||
rate>1.0, omit window AND USE HALF RATIO SINC???
|
||||
rate near 1.0, windowed is probably best
|
||||
|
||||
triple-lut this?? fade to windowed near 1.0, pick one of the other two for main
|
||||
maybe only double, windowed with a=5.5 sounds good for rate<1.0
|
||||
}
|
||||
|
||||
|
||||
distortion effect
|
||||
single-selection sampler
|
||||
|
||||
|
@ -60,6 +59,12 @@ TODO {
|
|||
|
||||
|
||||
bugs to fix {
|
||||
PLAYBACK BREAKS AFTER FIRST PLAY??? {
|
||||
doesn't happen in Lovely Storm; only where Capaxitor is used??
|
||||
resolves itself after project switch
|
||||
does NOT break preview mode??
|
||||
seems to be only with the Maybe sample??? idk
|
||||
}
|
||||
playback after stopping immediately after a note in the first pattern played sometimes skips that note
|
||||
things can apparently be hooked up cyclically, which completely breaks the queue
|
||||
|
||||
|
|
|
@ -17,12 +17,13 @@ using std::cyl_bessel_i;
|
|||
namespace {
|
||||
const constexpr double PI = 3.141592653589793238462643383279502884197169399375105820974;
|
||||
|
||||
const constexpr double KAISER_ALPHA = 7.5;
|
||||
// 5.5 for downrate
|
||||
const constexpr double KAISER_ALPHA = 5.5; //7.5;
|
||||
const constexpr double KAISER_BETA = PI * KAISER_ALPHA;
|
||||
|
||||
inline constexpr double sinc(double x) {
|
||||
if (x == 0) return 1;
|
||||
double px = x * PI;
|
||||
double px = x * PI/1;
|
||||
return std::sin(px) / px;
|
||||
}
|
||||
}
|
||||
|
@ -30,22 +31,31 @@ namespace {
|
|||
|
||||
|
||||
// generate
|
||||
const std::array<std::array<double, LUT_TAPS>, LUT_STEPS> Xybrid::NodeLib::resamplerLUT = [] {
|
||||
const std::array<std::array<std::array<double, LUT_TAPS>, 2>, LUT_STEPS> Xybrid::NodeLib::resamplerLUT = [] {
|
||||
|
||||
double denom = cyl_bessel_i(0, KAISER_BETA);
|
||||
|
||||
std::array<std::array<double, LUT_TAPS>, LUT_STEPS> t;
|
||||
std::array<std::array<std::array<double, LUT_TAPS>, 2>, LUT_STEPS> t;
|
||||
//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; // x position of tap;
|
||||
double sx = x-LUT_HTAPS;
|
||||
double kaiser = cyl_bessel_i(0, KAISER_BETA * std::sqrt(1.0 - std::pow( ( (2.0*(x+1))/(LUT_TAPS) ) - 1.0, 2 ) ) ) / denom; // original 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; // by-the-book 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
|
||||
//double kaiser = 1.0; // omit windowing
|
||||
//kaiser = std::max(kaiser, 0.0);
|
||||
t[step][0][tap] = sinc(sx) * kaiser; // sinc function centered on main tap, offset by subvalue, multiplied by window
|
||||
|
||||
// uprate table
|
||||
t[step][1][tap] = sinc(sx*0.5) * 0.5;// * kaiser; // for some reason this sounds cleaner (nearly identical to modplug polyphase)
|
||||
|
||||
// NaN guards
|
||||
if (t[step][0][tap] != t[step][0][tap]) t[step][0][tap] = 0;
|
||||
if (t[step][1][tap] != t[step][1][tap]) t[step][1][tap] = 0;
|
||||
}
|
||||
}
|
||||
/*t[0] = {};
|
||||
|
|
|
@ -11,7 +11,7 @@ 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;
|
||||
extern const std::array<std::array<std::array<double, LUT_TAPS>, 2>, LUT_STEPS> resamplerLUT;
|
||||
|
||||
inline Data::AudioFrame resamp(Data::Sample* smp, double pos, double rate [[maybe_unused]]) {
|
||||
auto loop = smp->loopStart >= 0;
|
||||
|
@ -21,7 +21,7 @@ namespace Xybrid::NodeLib {
|
|||
auto ll = le - ls;
|
||||
|
||||
double ip = std::floor(pos);
|
||||
auto& pt = NodeLib::resamplerLUT[static_cast<size_t>((pos - ip)*NodeLib::LUT_STEPS) % NodeLib::LUT_STEPS];
|
||||
auto& pt = NodeLib::resamplerLUT[static_cast<size_t>((pos - ip)*NodeLib::LUT_STEPS) % NodeLib::LUT_STEPS][rate > 1.0 ? 1 : 0];
|
||||
|
||||
Data::AudioFrame out(0.0);
|
||||
|
||||
|
@ -34,13 +34,12 @@ namespace Xybrid::NodeLib {
|
|||
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++;
|
||||
}//*/
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -108,7 +108,7 @@ void TestSynth::process() {
|
|||
auto ii = static_cast<ptrdiff_t>(ip);
|
||||
for (size_t i = 0; i < 8; i++) {
|
||||
auto si = ii+static_cast<ptrdiff_t>(i);
|
||||
if (si >= 0 && si < static_cast<ptrdiff_t>(smp->length())) out += (*smp)[static_cast<size_t>(si)] * pt[i];
|
||||
//if (si >= 0 && si < static_cast<ptrdiff_t>(smp->length())) out += (*smp)[static_cast<size_t>(si)] * pt[i];
|
||||
}
|
||||
(*p)[s] = out;
|
||||
|
||||
|
|
|
@ -30,6 +30,7 @@ using namespace Xybrid::UI;
|
|||
#include "util/strings.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <iostream>
|
||||
|
||||
#include <QDebug>
|
||||
#include <QCborMap>
|
||||
|
@ -93,6 +94,7 @@ void BeatPad::init() {
|
|||
if (!smp) return core.deleteNote(note);
|
||||
|
||||
double rate = static_cast<double>(smp->sampleRate) / static_cast<double>(audioEngine->curSampleRate());
|
||||
//std::cout << "rate: " << rate << std::endl;
|
||||
auto start = data.config->start;
|
||||
if (start < 0) start = 0;
|
||||
auto end = data.config->end;
|
||||
|
|
Loading…
Reference in New Issue