diff --git a/src/engine/filter.cpp b/src/engine/filter.cpp index 8b0e5a2e..88b38148 100644 --- a/src/engine/filter.cpp +++ b/src/engine/filter.cpp @@ -24,6 +24,7 @@ float* DivFilterTables::cubicTable=NULL; float* DivFilterTables::sincTable=NULL; +float* DivFilterTables::sincTable8=NULL; float* DivFilterTables::sincIntegralTable=NULL; // portions from Schism Tracker (scripts/lutgen.c) @@ -44,7 +45,7 @@ float* DivFilterTables::getCubicTable() { return cubicTable; } -float* DivFilterTables:: getSincTable() { +float* DivFilterTables::getSincTable() { if (sincTable==NULL) { logD("initializing sinc table."); sincTable=new float[65536]; @@ -64,6 +65,26 @@ float* DivFilterTables:: getSincTable() { return sincTable; } +float* DivFilterTables::getSincTable8() { + if (sincTable8==NULL) { + logD("initializing sinc table (8)."); + sincTable8=new float[32768]; + + sincTable8[0]=1.0f; + for (int i=1; i<32768; i++) { + int mapped=((i&8191)<<2)|(i>>13); + double x=(double)i*M_PI/8192.0; + sincTable8[mapped]=sin(x)/x; + } + + for (int i=0; i<32768; i++) { + int mapped=((i&8191)<<2)|(i>>13); + sincTable8[mapped]*=pow(cos(M_PI*(double)i/65536.0),2.0); + } + } + return sincTable8; +} + float* DivFilterTables::getSincIntegralTable() { if (sincIntegralTable==NULL) { logD("initializing sinc integral table."); diff --git a/src/engine/filter.h b/src/engine/filter.h index 974a448e..035c05e6 100644 --- a/src/engine/filter.h +++ b/src/engine/filter.h @@ -21,6 +21,7 @@ class DivFilterTables { public: static float* cubicTable; static float* sincTable; + static float* sincTable8; static float* sincIntegralTable; /** @@ -35,6 +36,12 @@ class DivFilterTables { */ static float* getSincTable(); + /** + * get a 8192x4 one-side sine-windowed sinc table. + * @return the table. + */ + static float* getSincTable8(); + /** * get a 8192x8 one-side sine-windowed sinc integral table. * @return the table. diff --git a/src/engine/platform/pcmdac.cpp b/src/engine/platform/pcmdac.cpp index 803eef7d..d172b38d 100644 --- a/src/engine/platform/pcmdac.cpp +++ b/src/engine/platform/pcmdac.cpp @@ -20,6 +20,7 @@ #define _USE_MATH_DEFINES #include "pcmdac.h" #include "../engine.h" +#include "../filter.h" #include // to ease the driver, freqency register is a 8.16 counter relative to output sample rate @@ -103,7 +104,50 @@ void DivPlatformPCMDAC::acquire(short* bufL, short* bufR, size_t start, size_t l } } if (chan[0].audPos>=0 && chan[0].audPos<(int)s->samples) { - output=s->data16[chan[0].audPos]; + int s_4=((chan[0].audPos-4)>=0)?s->data16[chan[0].audPos-4]:0; + int s_3=((chan[0].audPos-3)>=0)?s->data16[chan[0].audPos-3]:0; + int s_2=((chan[0].audPos-2)>=0)?s->data16[chan[0].audPos-2]:0; + int s_1=((chan[0].audPos-1)>=0)?s->data16[chan[0].audPos-1]:0; + int s0=s->data16[chan[0].audPos]; + int s1=((chan[0].audPos+1)<(int)s->samples)?s->data16[chan[0].audPos+1]:0; + int s2=((chan[0].audPos+2)<(int)s->samples)?s->data16[chan[0].audPos+2]:0; + int s3=((chan[0].audPos+3)<(int)s->samples)?s->data16[chan[0].audPos+3]:0; + switch (interp) { + case 1: // linear + output=s0+((s1-s0)*(chan[0].audSub&0xffff)>>16); + break; + case 2: { // cubic + float* cubicTable=DivFilterTables::getCubicTable(); + float* t=&cubicTable[((chan[0].audSub&0xffff)>>6)<<2]; + float result=(float)s_1*t[0]+(float)s0*t[1]+(float)s1*t[2]+(float)s2*t[3]; + if (result<-32768) result=-32768; + if (result>32767) result=32767; + output=result; + break; + } + case 3: { // sinc + float* sincTable=DivFilterTables::getSincTable8(); + float* t1=&sincTable[(8191-((chan[0].audSub&0xffff)>>3))<<2]; + float* t2=&sincTable[((chan[0].audSub&0xffff)>>3)<<2]; + float result=( + s_4*t2[3]+ + s_3*t2[2]+ + s_2*t2[1]+ + s_1*t2[0]+ + s0*t1[0]+ + s1*t1[1]+ + s2*t1[2]+ + s3*t1[3] + ); + if (result<-32768) result=-32768; + if (result>32767) result=32767; + output=result; + break; + } + default: // none + output=s0; + break; + } } } else { chan[0].sample=-1; @@ -398,6 +442,7 @@ void DivPlatformPCMDAC::setFlags(const DivConfig& flags) { chipClock=rate; outDepth=(flags.getInt("outDepth",15))&15; outStereo=flags.getBool("stereo",true); + interp=flags.getInt("interpolation",0); } int DivPlatformPCMDAC::init(DivEngine* p, int channels, int sugRate, const DivConfig& flags) { diff --git a/src/engine/platform/pcmdac.h b/src/engine/platform/pcmdac.h index dcdb4587..95c01424 100644 --- a/src/engine/platform/pcmdac.h +++ b/src/engine/platform/pcmdac.h @@ -55,6 +55,12 @@ class DivPlatformPCMDAC: public DivDispatch { DivDispatchOscBuffer* oscBuf; bool isMuted; int outDepth; + // valid values: + // - 0: none + // - 1: linear + // - 2: cubic spline + // - 3: sinc + int interp; bool outStereo; friend void putDispatchChip(void*,int); diff --git a/src/gui/sysConf.cpp b/src/gui/sysConf.cpp index c5825c24..b8e95a71 100644 --- a/src/gui/sysConf.cpp +++ b/src/gui/sysConf.cpp @@ -1227,6 +1227,7 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo // default to 44100Hz 16-bit stereo int sampRate=flags.getInt("rate",44100); int bitDepth=flags.getInt("outDepth",15)+1; + int interpolation=flags.getInt("interpolation",0); bool stereo=flags.getBool("stereo",false); ImGui::Text("Output rate:"); @@ -1245,11 +1246,30 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo altered=true; } + ImGui::Text("Interpolation:"); + if (ImGui::RadioButton("None",interpolation==0)) { + interpolation=0; + altered=true; + } + if (ImGui::RadioButton("Linear",interpolation==1)) { + interpolation=1; + altered=true; + } + if (ImGui::RadioButton("Cubic",interpolation==2)) { + interpolation=2; + altered=true; + } + if (ImGui::RadioButton("Sinc",interpolation==3)) { + interpolation=3; + altered=true; + } + if (altered) { e->lockSave([&]() { flags.set("rate",sampRate); flags.set("outDepth",bitDepth-1); flags.set("stereo",stereo); + flags.set("interpolation",interpolation); }); } break;