This commit is contained in:
LTVA1 2024-08-24 20:12:20 +03:00 committed by tildearrow
parent 332b449f0e
commit efb89f7f94
3 changed files with 70 additions and 5 deletions

View file

@ -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 && dacAntiClick<next) { \
dacAntiClick+=8; \
rWrite(0x4011,dacAntiClick); \
doWrite(0x4011,dacAntiClick); \
} else { \
dacAntiClickOn=false; \
rWrite(0x4011,next); \
doWrite(0x4011,next); \
} \
} \
dacPos++; \
@ -109,6 +109,14 @@ void DivPlatformNES::acquire_puNES(short** buf, size_t len) {
for (size_t i=0; i<len; i++) {
doPCM;
if (!writes.empty())
{
QueuedWrite w=writes.front();
doWrite(w.addr,w.val);
regPool[w.addr&0x1f]=w.val;
writes.pop();
}
apu_tick(nes,NULL);
nes->apu.odd_cycle=!nes->apu.odd_cycle;
if (nes->apu.clocked) {
@ -135,6 +143,14 @@ void DivPlatformNES::acquire_NSFPlay(short** buf, size_t len) {
for (size_t i=0; i<len; i++) {
doPCM;
if (!writes.empty())
{
QueuedWrite w=writes.front();
doWrite(w.addr,w.val);
regPool[w.addr&0x1f]=w.val;
writes.pop();
}
nes1_NP->Tick(8);
nes2_NP->TickFrameSequence(8);
nes2_NP->Tick(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);

View file

@ -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<signed char> {
@ -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<QueuedWrite,128> writes;
int dacPeriod, dacRate, dpcmPos;
unsigned int dacPos, dacAntiClick;
int dacSample;

View file

@ -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"));