From f7bd06c4eb4dd151065dc81c6506e2ed33e33744 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 24 Feb 2022 16:16:02 -0500 Subject: [PATCH 1/5] yay ADPCM-A and QSound work again! --- src/engine/engine.cpp | 36 ++++++++++++------------- src/engine/engine.h | 8 +++--- src/engine/platform/ym2610Interface.cpp | 16 ++++++----- src/engine/sample.cpp | 18 +++++++------ src/engine/vgmOps.cpp | 8 +++--- src/gui/gui.cpp | 4 +-- 6 files changed, 47 insertions(+), 43 deletions(-) diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 9dee6ecfe..29c293e86 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -444,46 +444,45 @@ void DivEngine::renderSamples() { sPreview.sample=-1; sPreview.pos=0; + // step 1: render samples for (int i=0; irender(); } - /* - // step 3: allocate ADPCM samples - if (adpcmMem==NULL) adpcmMem=new unsigned char[16777216]; + // step 2: allocate ADPCM-A samples + if (adpcmAMem==NULL) adpcmAMem=new unsigned char[16777216]; size_t memPos=0; for (int i=0; iadpcmRendLength)&0xf00000)) { + int paddedLen=(s->lengthA+255)&(~0xff); + if ((memPos&0xf00000)!=((memPos+paddedLen)&0xf00000)) { memPos=(memPos+0xfffff)&0xf00000; } if (memPos>=16777216) { logW("out of ADPCM memory for sample %d!\n",i); break; } - if (memPos+s->adpcmRendLength>=16777216) { - memcpy(adpcmMem+memPos,s->adpcmRendData,16777216-memPos); + if (memPos+paddedLen>=16777216) { + memcpy(adpcmAMem+memPos,s->dataA,16777216-memPos); logW("out of ADPCM memory for sample %d!\n",i); } else { - memcpy(adpcmMem+memPos,s->adpcmRendData,s->adpcmRendLength); + memcpy(adpcmAMem+memPos,s->dataA,paddedLen); } - s->rendOff=memPos; - memPos+=s->adpcmRendLength; + s->offA=memPos; + memPos+=paddedLen; } - adpcmMemLen=memPos+256; + adpcmAMemLen=memPos+256; // step 4: allocate qsound pcm samples if (qsoundMem==NULL) qsoundMem=new unsigned char[16777216]; - memset(qsoundMem, 0, 16777216); - memPos=0; for (int i=0; irendLength; - if (length > 65536-16) { - length = 65536-16; + int length=s->length8; + if (length>65536-16) { + length=65536-16; } if ((memPos&0xff0000)!=((memPos+length)&0xff0000)) { memPos=(memPos+0xffff)&0xff0000; @@ -494,19 +493,18 @@ void DivEngine::renderSamples() { } if (memPos+length>=16777216) { for (unsigned int i=0; i<16777216-(memPos+length); i++) { - qsoundMem[(memPos + i) ^ 0x8000] = s->rendData[i] >> ((s->depth == 16) ? 8 : 0); + qsoundMem[(memPos+i)^0x8000]=s->data8[i]; } logW("out of QSound PCM memory for sample %d!\n",i); } else { for (int i=0; irendData[i] >> ((s->depth == 16) ? 8 : 0); + qsoundMem[(memPos+i)^0x8000]=s->data8[i]; } } - s->rendOffQsound=memPos ^ 0x8000; + s->offQSound=memPos^0x8000; memPos+=length+16; } qsoundMemLen=memPos+256; - */ } void DivEngine::createNew() { diff --git a/src/engine/engine.h b/src/engine/engine.h index 96594a3ca..c02416996 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -621,8 +621,8 @@ class DivEngine { // terminate the engine. bool quit(); - unsigned char* adpcmMem; - size_t adpcmMemLen; + unsigned char* adpcmAMem; + size_t adpcmAMemLen; unsigned char* adpcmBMem; size_t adpcmBMemLen; unsigned char* qsoundMem; @@ -680,8 +680,8 @@ class DivEngine { totalProcessed(0), oscBuf{NULL,NULL}, oscSize(1), - adpcmMem(NULL), - adpcmMemLen(0), + adpcmAMem(NULL), + adpcmAMemLen(0), adpcmBMem(NULL), adpcmBMemLen(0), qsoundMem(NULL), diff --git a/src/engine/platform/ym2610Interface.cpp b/src/engine/platform/ym2610Interface.cpp index 8d53e48ce..45fb5288b 100644 --- a/src/engine/platform/ym2610Interface.cpp +++ b/src/engine/platform/ym2610Interface.cpp @@ -22,12 +22,16 @@ #include "../engine.h" uint8_t DivYM2610Interface::ymfm_external_read(ymfm::access_class type, uint32_t address) { - //printf("wants to read from %x\n",address); - if (type!=ymfm::ACCESS_ADPCM_A) return 0; - return parent->adpcmMem[address&0xffffff]; - /*if (12*sampleBank+(address>>16)>=parent->song.sampleLen) return 0; - return parent->song.sample[12*sampleBank+(address>>16)]->adpcmRendData[(address&0xffff)];*/ + switch (type) { + case ymfm::ACCESS_ADPCM_A: + return parent->adpcmAMem[address&0xffffff]; + case ymfm::ACCESS_ADPCM_B: + return parent->adpcmBMem[address&0xffffff]; + default: + return 0; + } + return 0; } void DivYM2610Interface::ymfm_external_write(ymfm::access_class type, uint32_t address, uint8_t data) { -} \ No newline at end of file +} diff --git a/src/engine/sample.cpp b/src/engine/sample.cpp index ec0ffad5c..ec6313fff 100644 --- a/src/engine/sample.cpp +++ b/src/engine/sample.cpp @@ -73,6 +73,7 @@ bool DivSample::save(const char* path) { return true; } +// 16-bit memory is padded to 512, to make things easier for ADPCM-A/B. bool DivSample::initInternal(unsigned char d, int count) { switch (d) { case 0: // 1-bit @@ -96,14 +97,14 @@ bool DivSample::initInternal(unsigned char d, int count) { case 5: // ADPCM-A if (dataA!=NULL) delete[] dataA; lengthA=(count+1)/2; - dataA=new unsigned char[lengthA]; - memset(dataA,0,lengthA); + dataA=new unsigned char[(lengthA+255)&(~0xff)]; + memset(dataA,0,(lengthA+255)&(~0xff)); break; case 6: // ADPCM-B if (dataB!=NULL) delete[] dataB; lengthB=(count+1)/2; - dataB=new unsigned char[lengthB]; - memset(dataB,0,lengthB); + dataB=new unsigned char[(lengthB+255)&(~0xff)]; + memset(dataB,0,(lengthB+255)&(~0xff)); break; case 7: // X68000 ADPCM if (dataX68!=NULL) delete[] dataX68; @@ -132,8 +133,8 @@ bool DivSample::initInternal(unsigned char d, int count) { case 16: // 16-bit if (data16!=NULL) delete[] data16; length16=count*2; - data16=new short[count]; - memset(data16,0,count*sizeof(short)); + data16=new short[(count+511)&(~0x1ff)]; + memset(data16,0,((count+511)&(~0x1ff))*sizeof(short)); break; default: return false; @@ -223,13 +224,14 @@ void DivSample::render() { if (!initInternal(4,samples)) return; bs_encode(data16,dataQSoundA,samples); } + // TODO: pad to 256. if (depth!=5) { // ADPCM-A if (!initInternal(5,samples)) return; - yma_encode(data16,dataA,samples); + yma_encode(data16,dataA,(samples+511)&(~0x1ff)); } if (depth!=6) { // ADPCM-B if (!initInternal(6,samples)) return; - ymb_encode(data16,dataB,samples); + ymb_encode(data16,dataB,(samples+511)&(~0x1ff)); } if (depth!=7) { // X68000 ADPCM if (!initInternal(7,samples)) return; diff --git a/src/engine/vgmOps.cpp b/src/engine/vgmOps.cpp index 93a2bcf42..e51217dc4 100644 --- a/src/engine/vgmOps.cpp +++ b/src/engine/vgmOps.cpp @@ -928,14 +928,14 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop) { delete[] pcmMem; } - if (writeADPCM && adpcmMemLen>0) { + if (writeADPCM && adpcmAMemLen>0) { w->writeC(0x67); w->writeC(0x66); w->writeC(0x82); - w->writeI(adpcmMemLen+8); - w->writeI(adpcmMemLen); + w->writeI(adpcmAMemLen+8); + w->writeI(adpcmAMemLen); w->writeI(0); - w->write(adpcmMem,adpcmMemLen); + w->write(adpcmAMem,adpcmAMemLen); } if (writeQSound && qsoundMemLen>0) { diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index ee8d5a954..bc15e5e5f 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -1898,12 +1898,12 @@ void FurnaceGUI::drawStats() { } if (!statsOpen) return; if (ImGui::Begin("Statistics",&statsOpen)) { - String adpcmUsage=fmt::sprintf("%d/16384KB",e->adpcmMemLen/1024); + String adpcmAUsage=fmt::sprintf("%d/16384KB",e->adpcmAMemLen/1024); String adpcmBUsage=fmt::sprintf("%d/16384KB",e->adpcmBMemLen/1024); String qsoundUsage=fmt::sprintf("%d/16384KB",e->qsoundMemLen/1024); ImGui::Text("ADPCM-A"); ImGui::SameLine(); - ImGui::ProgressBar(((float)e->adpcmMemLen)/16777216.0f,ImVec2(-FLT_MIN,0),adpcmUsage.c_str()); + ImGui::ProgressBar(((float)e->adpcmAMemLen)/16777216.0f,ImVec2(-FLT_MIN,0),adpcmAUsage.c_str()); ImGui::Text("ADPCM-B"); ImGui::SameLine(); ImGui::ProgressBar(((float)e->adpcmBMemLen)/16777216.0f,ImVec2(-FLT_MIN,0),adpcmBUsage.c_str()); From e4ad569f8d097d8afef9f1bb47e8d6ea9e724250 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 24 Feb 2022 16:53:20 -0500 Subject: [PATCH 2/5] Lynx: fix stereo --- src/engine/platform/lynx.cpp | 35 +++++++++++++++++++++-------------- src/engine/platform/lynx.h | 4 +++- 2 files changed, 24 insertions(+), 15 deletions(-) diff --git a/src/engine/platform/lynx.cpp b/src/engine/platform/lynx.cpp index eee2b2e22..856950f95 100644 --- a/src/engine/platform/lynx.cpp +++ b/src/engine/platform/lynx.cpp @@ -30,6 +30,7 @@ #define WRITE_CONTROL(ch,v) rWrite(0x25+(ch<<3),(v)) #define WRITE_OTHER(ch,v) rWrite(0x27+(ch<<3),(v)) #define WRITE_ATTEN(ch,v) rWrite((0x40+ch),(v)) +#define WRITE_STEREO(v) rWrite(0x50,(v)) #define CHIP_DIVIDER 64 @@ -88,9 +89,9 @@ const char* regCheatSheetLynx[]={ const char** DivPlatformLynx::getRegisterSheet() { return regCheatSheetLynx; -} - -const char* DivPlatformLynx::getEffectName(unsigned char effect) { +} + +const char* DivPlatformLynx::getEffectName(unsigned char effect) { switch (effect) { case 0x30: case 0x31: case 0x32: case 0x33: @@ -197,7 +198,8 @@ int DivPlatformLynx::dispatch(DivCommand c) { } break; case DIV_CMD_PANNING: - WRITE_ATTEN(c.chan, c.value); + chan[c.chan].pan=((c.value&0x0f)<<4)|((c.value&0xf0)>>4); + WRITE_ATTEN(c.chan,chan[c.chan].pan); break; case DIV_CMD_GET_VOLUME: if (chan[c.chan].std.hasVol) { @@ -261,6 +263,10 @@ void DivPlatformLynx::muteChannel(int ch, bool mute) { if (chan[ch].active) WRITE_VOLUME(ch,(isMuted[ch]?0:(chan[ch].outVol&127))); } +bool DivPlatformLynx::isStereo() { + return true; +} + void DivPlatformLynx::forceIns() { for (int i=0; i<4; i++) { if (chan[i].active) { @@ -272,16 +278,16 @@ void DivPlatformLynx::forceIns() { void* DivPlatformLynx::getChanState(int ch) { return &chan[ch]; -} - -unsigned char* DivPlatformLynx::getRegisterPool() -{ - return const_cast( mikey->getRegisterPool() ); -} - -int DivPlatformLynx::getRegisterPoolSize() -{ - return 4*8+4; +} + +unsigned char* DivPlatformLynx::getRegisterPool() +{ + return const_cast( mikey->getRegisterPool() ); +} + +int DivPlatformLynx::getRegisterPoolSize() +{ + return 4*8+4; } void DivPlatformLynx::reset() { @@ -294,6 +300,7 @@ void DivPlatformLynx::reset() { if (dumpWrites) { addWrite(0xffffffff,0); } + WRITE_STEREO(0); } bool DivPlatformLynx::keyOffAffectsArp(int ch) { diff --git a/src/engine/platform/lynx.h b/src/engine/platform/lynx.h index 8ad2bf2ce..536a874a8 100644 --- a/src/engine/platform/lynx.h +++ b/src/engine/platform/lynx.h @@ -45,7 +45,7 @@ class DivPlatformLynx: public DivDispatch { MikeyFreqDiv fd; MikeyDuty duty; int baseFreq, pitch, note, actualNote, lfsr; - unsigned char ins; + unsigned char ins, pan; bool active, insChanged, freqChanged, keyOn, keyOff, inPorta; signed char vol, outVol; Channel(): @@ -58,6 +58,7 @@ class DivPlatformLynx: public DivDispatch { actualNote(0), lfsr(-1), ins(-1), + pan(0xff), active(false), insChanged(true), freqChanged(false), @@ -81,6 +82,7 @@ class DivPlatformLynx: public DivDispatch { void forceIns(); void tick(); void muteChannel(int ch, bool mute); + bool isStereo(); bool keyOffAffectsArp(int ch); bool keyOffAffectsPorta(int ch); //int getPortaFloor(int ch); From 82c8b97d9c3e50dfa3f0299c3e6e6b4044258b6f Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 24 Feb 2022 16:53:38 -0500 Subject: [PATCH 3/5] Neo Geo: safety checks --- src/engine/engine.h | 10 +++++++++- src/engine/platform/ym2610Interface.cpp | 4 ++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/engine/engine.h b/src/engine/engine.h index c02416996..be04d1cbb 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -627,6 +627,10 @@ class DivEngine { size_t adpcmBMemLen; unsigned char* qsoundMem; size_t qsoundMemLen; + unsigned char* qsoundAMem; + size_t qsoundAMemLen; + unsigned char* dpcmMem; + size_t dpcmMemLen; DivEngine(): output(NULL), @@ -685,6 +689,10 @@ class DivEngine { adpcmBMem(NULL), adpcmBMemLen(0), qsoundMem(NULL), - qsoundMemLen(0) {} + qsoundMemLen(0), + qsoundAMem(NULL), + qsoundAMemLen(0), + dpcmMem(NULL), + dpcmMemLen(0) {} }; #endif diff --git a/src/engine/platform/ym2610Interface.cpp b/src/engine/platform/ym2610Interface.cpp index 45fb5288b..9154b40e7 100644 --- a/src/engine/platform/ym2610Interface.cpp +++ b/src/engine/platform/ym2610Interface.cpp @@ -24,8 +24,12 @@ uint8_t DivYM2610Interface::ymfm_external_read(ymfm::access_class type, uint32_t address) { switch (type) { case ymfm::ACCESS_ADPCM_A: + if (parent->adpcmAMem==NULL) return 0; + if ((address&0xffffff)>=parent->adpcmAMemLen) return 0; return parent->adpcmAMem[address&0xffffff]; case ymfm::ACCESS_ADPCM_B: + if (parent->adpcmBMem==NULL) return 0; + if ((address&0xffffff)>=parent->adpcmBMemLen) return 0; return parent->adpcmBMem[address&0xffffff]; default: return 0; From 4b6d9adcb9b064753c4a30f7f9b894501099e8a0 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 24 Feb 2022 17:56:19 -0500 Subject: [PATCH 4/5] volume changes as of now the master volume is 1.0x (it was 2.0x before) NES has also been amplified. these changes should not affect older songs. --- papers/format.md | 4 ++++ src/engine/engine.h | 4 ++-- src/engine/fileOps.cpp | 15 ++++++++++++++- src/engine/platform/nes.cpp | 5 ++++- src/engine/playback.cpp | 12 ++++++------ src/engine/song.h | 2 ++ src/gui/gui.cpp | 4 ++++ 7 files changed, 36 insertions(+), 10 deletions(-) diff --git a/papers/format.md b/papers/format.md index a61bb6266..1f44f9ac6 100644 --- a/papers/format.md +++ b/papers/format.md @@ -25,6 +25,8 @@ furthermore, an `or reserved` indicates this field is always present, but is res the format versions are: +- 59: Furnace dev59 +- 58: Furnace dev58 - 57: Furnace dev57 - 53: Furnace 0.5.7 @@ -201,6 +203,8 @@ size | description S?? | channel short names | - same as above STR | song comment + 4f | master volume, 1.0f=100% (>=59) + | this is 2.0f for modules before 59 # instrument diff --git a/src/engine/engine.h b/src/engine/engine.h index be04d1cbb..f4324fb6c 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -37,8 +37,8 @@ warnings+=(String("\n")+x); \ } -#define DIV_VERSION "dev58" -#define DIV_ENGINE_VERSION 58 +#define DIV_VERSION "dev59" +#define DIV_ENGINE_VERSION 59 enum DivStatusView { DIV_STATUS_NOTHING=0, diff --git a/src/engine/fileOps.cpp b/src/engine/fileOps.cpp index dd3ff37e2..b8c57c5b3 100644 --- a/src/engine/fileOps.cpp +++ b/src/engine/fileOps.cpp @@ -841,7 +841,12 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) { if (tchans>DIV_MAX_CHANS) tchans=DIV_MAX_CHANS; // system volume - for (int i=0; i<32; i++) ds.systemVol[i]=reader.readC(); + for (int i=0; i<32; i++) { + ds.systemVol[i]=reader.readC(); + if (ds.version<59 && ds.system[i]==DIV_SYSTEM_NES) { + ds.systemVol[i]/=4; + } + } // system panning for (int i=0; i<32; i++) ds.systemPan[i]=reader.readC(); @@ -999,6 +1004,12 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) { ds.notes=reader.readString(); } + if (ds.version>=59) { + ds.masterVol=reader.readF(); + } else { + ds.masterVol=2.0f; + } + // read instruments for (int i=0; iwriteString(song.notes,false); + w->writeF(song.masterVol); + /// INSTRUMENT for (int i=0; iapu.clocked) { nes->apu.clocked=false; } - bufL[i]=(pulse_output(nes)+tnd_output(nes))*30; + int sample=(pulse_output(nes)+tnd_output(nes)-128)<<7; + if (sample>32767) sample=32767; + if (sample<-32768) sample=-32768; + bufL[i]=sample; } } diff --git a/src/engine/playback.cpp b/src/engine/playback.cpp index 6c825cb71..5fb0c4e86 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -1294,17 +1294,17 @@ void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsi } for (int i=0; iisStereo()) { for (size_t j=0; jsong.masterVol,0,3,"%.2fx")) { + if (e->song.masterVol<0) e->song.masterVol=0; + if (e->song.masterVol>3) e->song.masterVol=3; + } for (int i=0; isong.systemLen; i++) { snprintf(id,31,"MixS%d",i); bool doInvert=e->song.systemVol[i]&128; From 28254d4b243235f180c95facc7402af4358c7972 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 24 Feb 2022 18:00:19 -0500 Subject: [PATCH 5/5] SMS: add 1.79MHz option --- src/engine/platform/sms.cpp | 4 +++- src/gui/gui.cpp | 4 ++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/engine/platform/sms.cpp b/src/engine/platform/sms.cpp index 325665003..114d67b05 100644 --- a/src/engine/platform/sms.cpp +++ b/src/engine/platform/sms.cpp @@ -301,7 +301,9 @@ void DivPlatformSMS::poke(std::vector& wlist) { } void DivPlatformSMS::setFlags(unsigned int flags) { - if ((flags&3)==2) { + if ((flags&3)==3) { + chipClock=COLOR_NTSC/2.0; + } else if ((flags&3)==2) { chipClock=4000000; } else if ((flags&3)==1) { chipClock=COLOR_PAL*4.0/5.0; diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 10a7159c2..1a5a13f47 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -4544,6 +4544,10 @@ bool FurnaceGUI::loop() { e->setSysFlags(i,(flags&(~3))|2,restart); updateWindowTitle(); } + if (ImGui::RadioButton("Half NTSC (1.79MHz)",(flags&3)==3)) { + e->setSysFlags(i,(flags&(~3))|3,restart); + updateWindowTitle(); + } ImGui::Text("Chip type:"); if (ImGui::RadioButton("Sega VDP/Master System",((flags>>2)&3)==0)) { e->setSysFlags(i,(flags&(~12))|0,restart);