From efb89f7f94025542119071db2bb9975bfea7c890 Mon Sep 17 00:00:00 2001 From: LTVA1 <87536432+LTVA1@users.noreply.github.com> Date: Sat, 24 Aug 2024 20:12:20 +0300 Subject: [PATCH] yeah --- src/engine/platform/nes.cpp | 52 ++++++++++++++++++++++++++++++++++--- src/engine/platform/nes.h | 8 ++++++ src/gui/sampleEdit.cpp | 15 +++++++++-- 3 files changed, 70 insertions(+), 5 deletions(-) diff --git a/src/engine/platform/nes.cpp b/src/engine/platform/nes.cpp index 4101b9f61..e48a581cc 100644 --- a/src/engine/platform/nes.cpp +++ b/src/engine/platform/nes.cpp @@ -28,7 +28,7 @@ struct _nla_table nla_table; #define CHIP_DIVIDER 16 -#define rWrite(a,v) if (!skipRegisterWrites) {doWrite(a,v); regPool[(a)&0x7f]=v; if (dumpWrites) {addWrite(a,v);} } +#define rWrite(a,v) if (!skipRegisterWrites) {writes.push(QueuedWrite((a),v)); if (dumpWrites) {addWrite((a),v);} } const char* regCheatSheetNES[]={ "S0Volume", "4000", @@ -86,10 +86,10 @@ void DivPlatformNES::doWrite(unsigned short addr, unsigned char data) { unsigned char next=((unsigned char)s->data8[dacPos]+0x80)>>1; \ if (dacAntiClickOn && dacAntiClickapu.odd_cycle=!nes->apu.odd_cycle; @@ -134,6 +142,14 @@ void DivPlatformNES::acquire_NSFPlay(short** buf, size_t len) { int out2[2]; for (size_t i=0; iTick(8); nes2_NP->TickFrameSequence(8); @@ -400,6 +416,35 @@ void DivPlatformNES::tick(bool sysTick) { logV("switching bank to %d",dpcmBank); if (dumpWrites) addWrite(0xffff0004,dpcmBank); } + //sample custom loop point... + + DivSample* lsamp = parent->getSample(dacSample); + + //how it works: + //when the initial sample info is written (see above) and playback is launched, + //the parameters (start point in memory and length) are locked until sample end + //is reached. + + //thus, if we write new data after just several APU clock cycles, it will be used only when + //sample finishes one full loop. + + //thus we can write sample's loop point as "start address" and sample's looped part length + //as "full sample length". + + //APU will play full sample once and then repeatedly cycle through the looped part. + + //sources: + //https://www.nesdev.org/wiki/APU_DMC + //https://www.youtube.com/watch?v=vB4P8x2Am6Y + + if(lsamp->loopEnd > lsamp->loopStart && goingToLoop) + { + int loop_start_addr = (sampleOffDPCM[dacSample] + lsamp->loopStart) / 8; + int loop_len = (lsamp->loopEnd - lsamp->loopStart) / 8; + + rWrite(0x4012,(loop_start_addr >> 6)&0xff); + rWrite(0x4013,(loop_len >> 4)&0xff); + } } } else { if (nextDPCMFreq>=0) { @@ -790,6 +835,7 @@ float DivPlatformNES::getPostAmp() { } void DivPlatformNES::reset() { + while (!writes.empty()) writes.pop(); for (int i=0; i<5; i++) { chan[i]=DivPlatformNES::Channel(); chan[i].std.setEngine(parent); diff --git a/src/engine/platform/nes.h b/src/engine/platform/nes.h index cccbe0b18..21e2779e5 100644 --- a/src/engine/platform/nes.h +++ b/src/engine/platform/nes.h @@ -24,6 +24,7 @@ #include "sound/nes_nsfplay/nes_apu.h" #include "sound/nes_nsfplay/5e01_apu.h" +#include "../../fixedQueue.h" class DivPlatformNES: public DivDispatch { struct Channel: public SharedChannel { @@ -44,6 +45,13 @@ class DivPlatformNES: public DivDispatch { Channel chan[5]; DivDispatchOscBuffer* oscBuf[5]; bool isMuted[5]; + struct QueuedWrite { + unsigned short addr; + unsigned char val; + QueuedWrite(): addr(0), val(0) {} + QueuedWrite(unsigned short a, unsigned char v): addr(a), val(v) {} + }; + FixedQueue writes; int dacPeriod, dacRate, dpcmPos; unsigned int dacPos, dacAntiClick; int dacSample; diff --git a/src/gui/sampleEdit.cpp b/src/gui/sampleEdit.cpp index 88337e39c..08c0a7438 100644 --- a/src/gui/sampleEdit.cpp +++ b/src/gui/sampleEdit.cpp @@ -254,15 +254,26 @@ void FurnaceGUI::drawSampleEdit() { } break; case DIV_SYSTEM_NES: + { if (sample->loop) { - if (sample->loopStart!=0 || sample->loopEnd!=(int)(sample->samples)) { - SAMPLE_WARN(warnLoopPos,_("NES: loop point ignored on DPCM (may only loop entire sample)")); + if (sample->loopStart&511) { + int tryWith=(sample->loopStart)&(~511); + if (tryWith>(int)sample->samples) tryWith-=512; + String alignHint=fmt::sprintf(_("NES: loop start must be a multiple of 512 (try with %d)"),tryWith); + SAMPLE_WARN(warnLoopStart,alignHint); + } + if ((sample->loopEnd)&127) { + int tryWith=(sample->loopEnd + 1)&(~127); //+1 bc of how sample length is treated: https://www.nesdev.org/wiki/APU_DMC + if (tryWith>(int)sample->samples) tryWith-=128; + String alignHint=fmt::sprintf(_("NES: loop end must be a multiple of 128 (try with %d)"),tryWith); + SAMPLE_WARN(warnLoopEnd,alignHint); } } if (sample->samples>32648) { SAMPLE_WARN(warnLength,_("NES: maximum DPCM sample length is 32648")); } break; + } case DIV_SYSTEM_X1_010: if (sample->loop) { SAMPLE_WARN(warnLoop,_("X1-010: samples can't loop"));