diff --git a/demos/multichip/pont of noreturn.fur b/demos/multichip/pont of noreturn.fur new file mode 100644 index 000000000..0f3d34857 Binary files /dev/null and b/demos/multichip/pont of noreturn.fur differ diff --git a/src/audio/sdlAudio.cpp b/src/audio/sdlAudio.cpp index 8f074a5aa..cf464e835 100644 --- a/src/audio/sdlAudio.cpp +++ b/src/audio/sdlAudio.cpp @@ -46,7 +46,9 @@ void* TAAudioSDL::getContext() { bool TAAudioSDL::quit() { if (!initialized) return false; - SDL_CloseAudioDevice(ai); + if (ai!=0) { + SDL_CloseAudioDevice(ai); + } if (running) { running=false; diff --git a/src/audio/sdlAudio.h b/src/audio/sdlAudio.h index b3b0a1843..b9c12fd20 100644 --- a/src/audio/sdlAudio.h +++ b/src/audio/sdlAudio.h @@ -34,5 +34,6 @@ class TAAudioSDL: public TAAudio { std::vector listAudioDevices(); bool init(TAAudioDesc& request, TAAudioDesc& response); TAAudioSDL(): + ai(0), audioSysStarted(false) {} }; diff --git a/src/engine/engine.h b/src/engine/engine.h index e52e4d4b8..c29dc59b2 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -580,7 +580,7 @@ class DivEngine { void processRow(int i, bool afterDelay); void nextOrder(); void nextRow(); - void performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write, int streamOff, double* loopTimer, double* loopFreq, int* loopSample, bool* sampleDir, bool isSecond, int* pendingFreq, int* playingSample, int* setPos, unsigned int* sampleOff8, unsigned int* sampleLen8, size_t bankOffset, bool directStream); + void performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write, int streamOff, double* loopTimer, double* loopFreq, int* loopSample, bool* sampleDir, bool isSecond, int* pendingFreq, int* playingSample, int* setPos, unsigned int* sampleOff8, unsigned int* sampleLen8, size_t bankOffset, bool directStream, bool* sampleStoppable); // returns true if end of song. bool nextTick(bool noAccum=false, bool inhibitLowLat=false); bool perSystemEffect(int ch, unsigned char effect, unsigned char effectVal); diff --git a/src/engine/platform/sms.cpp b/src/engine/platform/sms.cpp index 7be834bda..569862864 100644 --- a/src/engine/platform/sms.cpp +++ b/src/engine/platform/sms.cpp @@ -165,8 +165,12 @@ int DivPlatformSMS::snCalcFreq(int ch) { if (ch==3) CHIP_DIVIDER=noiseDivider; int easyStartingPeriod=16; int easyThreshold=round(128.0*12.0*log((chipClock/(easyStartingPeriod*CHIP_DIVIDER))/(0.0625*parent->song.tuning))/log(2.0))-384+64; - if (parent->song.linearPitch==2 && easyNoise && chan[ch].baseFreq+chan[ch].pitch+chan[ch].pitch2>(easyThreshold)) { - int ret=(((easyStartingPeriod<<7))-(chan[ch].baseFreq+chan[ch].pitch+chan[ch].pitch2-(easyThreshold)))>>7; + int curFreq=chan[ch].baseFreq+chan[ch].pitch+chan[ch].pitch2+(chan[ch].arpOff<<7); + if (chan[ch].fixedArp) { + curFreq=chan[ch].baseNoteOverride<<7; + } + if (parent->song.linearPitch==2 && easyNoise && curFreq>easyThreshold) { + int ret=(((easyStartingPeriod<<7))-(curFreq-(easyThreshold)))>>7; if (ret<0) ret=0; return ret; } diff --git a/src/engine/platform/snes.cpp b/src/engine/platform/snes.cpp index baa055ee6..f877295af 100644 --- a/src/engine/platform/snes.cpp +++ b/src/engine/platform/snes.cpp @@ -840,6 +840,8 @@ void DivPlatformSNES::reset() { memcpy(sampleMem,copyOfSampleMem,65536); dsp.init(sampleMem); dsp.set_output(NULL,0); + dsp.setupInterpolation(!interpolationOff); + memset(regPool,0,128); // this can't be 0 or channel 1 won't play // this can't be 0x100 either as that's used by SPC700 page 1 and the stack @@ -1022,6 +1024,8 @@ void DivPlatformSNES::setFlags(const DivConfig& flags) { initEchoFIR[7]=flags.getInt("echoFilter7",0); initEchoMask=flags.getInt("echoMask",0); + + interpolationOff=flags.getBool("interpolationOff",false); } int DivPlatformSNES::init(DivEngine* p, int channels, int sugRate, const DivConfig& flags) { diff --git a/src/engine/platform/snes.h b/src/engine/platform/snes.h index b61b12f0e..54b6b2540 100644 --- a/src/engine/platform/snes.h +++ b/src/engine/platform/snes.h @@ -69,6 +69,7 @@ class DivPlatformSNES: public DivDispatch { bool writeEcho; bool writeDryVol; bool echoOn; + bool interpolationOff; bool initEchoOn; signed char initEchoVolL; diff --git a/src/engine/platform/sound/snes/SPC_DSP.cpp b/src/engine/platform/sound/snes/SPC_DSP.cpp index 6ae8275bc..25cddd884 100644 --- a/src/engine/platform/sound/snes/SPC_DSP.cpp +++ b/src/engine/platform/sound/snes/SPC_DSP.cpp @@ -135,24 +135,39 @@ static short const gauss [512] = 1299,1300,1300,1301,1302,1302,1303,1303,1303,1304,1304,1304,1304,1304,1305,1305, }; +void SPC_DSP::setupInterpolation(bool interpolate) +{ + for(int i = 0; i < voice_count; i++) + { + m.voices[i].interpolate = interpolate; + } +} + inline int SPC_DSP::interpolate( voice_t const* v ) { // Make pointers into gaussian based on fractional position between samples - int offset = v->interp_pos >> 4 & 0xFF; - short const* fwd = gauss + 255 - offset; - short const* rev = gauss + offset; // mirror left half of gaussian + if(v->interpolate) + { + int offset = v->interp_pos >> 4 & 0xFF; + short const* fwd = gauss + 255 - offset; + short const* rev = gauss + offset; // mirror left half of gaussian - int const* in = &v->buf [(v->interp_pos >> 12) + v->buf_pos]; - int out; - out = (fwd [ 0] * in [0]) >> 11; - out += (fwd [256] * in [1]) >> 11; - out += (rev [256] * in [2]) >> 11; - out = (int16_t) out; - out += (rev [ 0] * in [3]) >> 11; + int const* in = &v->buf [(v->interp_pos >> 12) + v->buf_pos]; + int out; + out = (fwd [ 0] * in [0]) >> 11; + out += (fwd [256] * in [1]) >> 11; + out += (rev [256] * in [2]) >> 11; + out = (int16_t) out; + out += (rev [ 0] * in [3]) >> 11; - CLAMP16( out ); - out &= ~1; - return out; + CLAMP16( out ); + out &= ~1; + return out; + } + else + { + return v->buf [(v->interp_pos >> 12) + v->buf_pos]; //Furnace addition -- no interpolation + } } diff --git a/src/engine/platform/sound/snes/SPC_DSP.h b/src/engine/platform/sound/snes/SPC_DSP.h index 924d9ae2c..6c6ff64ce 100644 --- a/src/engine/platform/sound/snes/SPC_DSP.h +++ b/src/engine/platform/sound/snes/SPC_DSP.h @@ -27,6 +27,9 @@ public: // output buffer could hold. int sample_count() const; + // Furnace addition: disable/enable Gaussian interpolation + void setupInterpolation(bool interpolate); + // Emulation // Resets DSP to power-on state @@ -122,6 +125,7 @@ public: int hidden_env; // used by GAIN mode 7, very obscure quirk uint8_t t_envx_out; sample_t out[2]; // Furnace addition, for per-channel oscilloscope + bool interpolate; // Furnace addition, to disable interpolation }; // Furnace addition, gets a voice diff --git a/src/engine/vgmOps.cpp b/src/engine/vgmOps.cpp index a8a1fab74..9529631a1 100644 --- a/src/engine/vgmOps.cpp +++ b/src/engine/vgmOps.cpp @@ -24,7 +24,9 @@ constexpr int MASTER_CLOCK_PREC=(sizeof(void*)==8)?8:0; -void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write, int streamOff, double* loopTimer, double* loopFreq, int* loopSample, bool* sampleDir, bool isSecond, int* pendingFreq, int* playingSample, int* setPos, unsigned int* sampleOff8, unsigned int* sampleLen8, size_t bankOffset, bool directStream) { +// this function is so long +// may as well make it something else +void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write, int streamOff, double* loopTimer, double* loopFreq, int* loopSample, bool* sampleDir, bool isSecond, int* pendingFreq, int* playingSample, int* setPos, unsigned int* sampleOff8, unsigned int* sampleLen8, size_t bankOffset, bool directStream, bool* sampleStoppable) { unsigned char baseAddr1=isSecond?0xa0:0x50; unsigned char baseAddr2=isSecond?0x80:0; unsigned short baseAddr2S=isSecond?0x8000:0; @@ -647,6 +649,7 @@ void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write logD("writing stream command %x:%x with stream ID %d",write.addr,write.val,streamID); switch (write.addr&0xff) { case 0: // play sample + sampleStoppable[streamID]=true; if (write.val<(unsigned int)song.sampleLen) { if (playingSample[streamID]!=(int)write.val) { pendingFreq[streamID]=write.val; @@ -685,6 +688,7 @@ void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write } break; case 1: { // set sample freq + sampleStoppable[streamID]=true; int realFreq=write.val; if (realFreq<0) realFreq=0; if (realFreq>44100) realFreq=44100; @@ -728,11 +732,14 @@ void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write break; } case 2: // stop sample - w->writeC(0x94); - w->writeC(streamID); - loopSample[streamID]=-1; - playingSample[streamID]=-1; - pendingFreq[streamID]=-1; + if (sampleStoppable[streamID]) { + w->writeC(0x94); + w->writeC(streamID); + loopSample[streamID]=-1; + playingSample[streamID]=-1; + pendingFreq[streamID]=-1; + sampleStoppable[streamID]=false; + } break; case 3: // set sample direction sampleDir[streamID]=write.val; @@ -1224,6 +1231,7 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p bool sampleDir[DIV_MAX_CHANS]; int pendingFreq[DIV_MAX_CHANS]; int playingSample[DIV_MAX_CHANS]; + bool sampleStoppable[DIV_MAX_CHANS]; int setPos[DIV_MAX_CHANS]; std::vector chipVol; std::vector delayedWrites[DIV_MAX_CHIPS]; @@ -1246,6 +1254,7 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p playingSample[i]=-1; setPos[i]=0; sampleDir[i]=false; + sampleStoppable[i]=true; } bool writeDACSamples=false; @@ -2514,7 +2523,7 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p for (int i=0; i& writes=disCont[i].dispatch->getRegisterWrites(); for (DivRegWrite& j: writes) { - performVGMWrite(w,song.system[i],j,streamIDs[i],loopTimer,loopFreq,loopSample,sampleDir,isSecond[i],pendingFreq,playingSample,setPos,sampleOff8,sampleLen8,bankOffset[i],directStream); + performVGMWrite(w,song.system[i],j,streamIDs[i],loopTimer,loopFreq,loopSample,sampleDir,isSecond[i],pendingFreq,playingSample,setPos,sampleOff8,sampleLen8,bankOffset[i],directStream,sampleStoppable); writeCount++; } writes.clear(); @@ -2554,7 +2563,7 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p lastOne=i.second.time; } // write write - performVGMWrite(w,song.system[i.first],i.second.write,streamIDs[i.first],loopTimer,loopFreq,loopSample,sampleDir,isSecond[i.first],pendingFreq,playingSample,setPos,sampleOff8,sampleLen8,bankOffset[i.first],directStream); + performVGMWrite(w,song.system[i.first],i.second.write,streamIDs[i.first],loopTimer,loopFreq,loopSample,sampleDir,isSecond[i.first],pendingFreq,playingSample,setPos,sampleOff8,sampleLen8,bankOffset[i.first],directStream,sampleStoppable); // handle global Furnace commands writeCount++; diff --git a/src/gui/sysConf.cpp b/src/gui/sysConf.cpp index 72229d0d4..caa3891e9 100644 --- a/src/gui/sysConf.cpp +++ b/src/gui/sysConf.cpp @@ -1969,6 +1969,8 @@ bool FurnaceGUI::drawSysConf(int chan, int sysPos, DivSystem type, DivConfig& fl echoFilter[6]=flags.getInt("echoFilter6",0); echoFilter[7]=flags.getInt("echoFilter7",0); + bool interpolationOff=flags.getBool("interpolationOff",false); + ImGui::Text(_("Volume scale:")); if (CWSliderInt(_("Left##VolScaleL"),&vsL,0,127)) { if (vsL<0) vsL=0; @@ -2084,6 +2086,10 @@ bool FurnaceGUI::drawSysConf(int chan, int sysPos, DivSystem type, DivConfig& fl ImGui::Text(_("sum: %d"),filterSum); ImGui::PopStyleColor(); + if (ImGui::Checkbox(_("Disable Gaussian interpolation"),&interpolationOff)) { + altered=true; + } + if (altered) { e->lockSave([&]() { flags.set("volScaleL",127-vsL); @@ -2102,6 +2108,7 @@ bool FurnaceGUI::drawSysConf(int chan, int sysPos, DivSystem type, DivConfig& fl flags.set("echoFilter6",echoFilter[6]); flags.set("echoFilter7",echoFilter[7]); flags.set("echoMask",echoMask); + flags.set("interpolationOff",interpolationOff); }); }