diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 980062769..9c4b11c46 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -11,7 +11,7 @@ defaults: shell: bash env: - BUILD_TYPE: Debug + BUILD_TYPE: Release jobs: build: @@ -20,11 +20,11 @@ jobs: config: - { name: 'Windows MSVC x86', os: windows-latest, compiler: msvc, arch: x86 } - { name: 'Windows MSVC x86_64', os: windows-latest, compiler: msvc, arch: x86_64 } - ##- { name: 'Windows MinGW x86', os: ubuntu-20.04, compiler: mingw, arch: x86 } - ##- { name: 'Windows MinGW x86_64', os: ubuntu-20.04, compiler: mingw, arch: x86_64 } + - { name: 'Windows MinGW x86', os: ubuntu-20.04, compiler: mingw, arch: x86 } + - { name: 'Windows MinGW x86_64', os: ubuntu-20.04, compiler: mingw, arch: x86_64 } - { name: 'macOS x86_64', os: macos-latest, arch: x86_64 } - { name: 'macOS ARM', os: macos-latest, arch: arm64 } - ##- { name: 'Linux x86_64', os: ubuntu-18.04, arch: x86_64 } + - { name: 'Linux x86_64', os: ubuntu-18.04, arch: x86_64 } #- { name: 'Linux ARM', os: ubuntu-18.04, arch: armhf } fail-fast: false diff --git a/demos/pce/Bad_Dudes_Greased_Lightning.fur b/demos/pce/Bad_Dudes_Greased_Lightning.fur new file mode 100644 index 000000000..98209900e Binary files /dev/null and b/demos/pce/Bad_Dudes_Greased_Lightning.fur differ diff --git a/instruments/OPL/2-OP OPL3 Slap Bass.fui b/instruments/OPL/2-OP OPL3 Slap Bass.fui new file mode 100644 index 000000000..fb8f1c143 Binary files /dev/null and b/instruments/OPL/2-OP OPL3 Slap Bass.fui differ diff --git a/src/engine/engine.h b/src/engine/engine.h index 31edc8c85..ffa97d755 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -442,7 +442,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, 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, bool directStream); // 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/genesis.cpp b/src/engine/platform/genesis.cpp index b547181da..5131afd96 100644 --- a/src/engine/platform/genesis.cpp +++ b/src/engine/platform/genesis.cpp @@ -586,7 +586,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) { } } if (c.chan>=5 && chan[c.chan].dacMode) { - if (skipRegisterWrites) break; + //if (skipRegisterWrites) break; if (ins->type==DIV_INS_AMIGA) { // Furnace mode if (c.value!=DIV_NOTE_NULL) chan[c.chan].dacSample=ins->amiga.getSample(c.value); if (chan[c.chan].dacSample<0 || chan[c.chan].dacSample>=parent->song.sampleLen) { @@ -1122,6 +1122,8 @@ void DivPlatformGenesis::forceIns() { } } if (chan[5].dacMode) { + chan[5].dacSample=-1; + chan[6].dacSample=-1; rWrite(0x2b,0x80); } immWrite(0x22,lfoValue); diff --git a/src/engine/platform/genesisext.cpp b/src/engine/platform/genesisext.cpp index 4eb34bc23..8e1e7d127 100644 --- a/src/engine/platform/genesisext.cpp +++ b/src/engine/platform/genesisext.cpp @@ -677,6 +677,8 @@ void DivPlatformGenesisExt::forceIns() { } } if (chan[5].dacMode) { + chan[5].dacSample=-1; + chan[6].dacSample=-1; rWrite(0x2b,0x80); } immWrite(0x22,lfoValue); diff --git a/src/engine/platform/opl.cpp b/src/engine/platform/opl.cpp index bab5886be..6ac489bfc 100644 --- a/src/engine/platform/opl.cpp +++ b/src/engine/platform/opl.cpp @@ -1518,7 +1518,7 @@ DivMacroInt* DivPlatformOPL::getChanMacroInt(int ch) { } DivDispatchOscBuffer* DivPlatformOPL::getOscBuffer(int ch) { - if (ch>=18) return NULL; + if (ch>=totalChans) return NULL; if (oplType==3 && ch<12) { if (chan[ch&(~1)].fourOp) { if (ch&1) { diff --git a/src/engine/platform/sound/su.cpp b/src/engine/platform/sound/su.cpp index 4d98892cc..f7bb24b31 100644 --- a/src/engine/platform/sound/su.cpp +++ b/src/engine/platform/sound/su.cpp @@ -17,13 +17,13 @@ void SoundUnit::NextSample(short* l, short* r) { // run channels for (int i=0; i<8; i++) { - if (chan[i].vol==0 && !chan[i].flags.swvol) { + if (chan[i].vol==0 && !chan[i].flags1.swvol) { fns[i]=0; continue; } - if (chan[i].flags.pcm) { + if (chan[i].flags0.pcm) { ns[i]=pcm[chan[i].pcmpos]; - } else switch (chan[i].flags.shape) { + } else switch (chan[i].flags0.shape) { case 0: ns[i]=(((cycle[i]>>15)&127)>chan[i].duty)*127; break; @@ -47,11 +47,11 @@ void SoundUnit::NextSample(short* l, short* r) { break; } - if (chan[i].flags.ring) { + if (chan[i].flags0.ring) { ns[i]=(ns[i]*ns[(i+1)&7])>>7; } - if (chan[i].flags.pcm) { + if (chan[i].flags0.pcm) { if (chan[i].freq>0x8000) { pcmdec[i]+=0x8000; } else { @@ -62,18 +62,18 @@ void SoundUnit::NextSample(short* l, short* r) { if (chan[i].pcmpos>4)&3) { case 0: cycle[i]+=chan[i].freq*1-(chan[i].freq>>3); @@ -92,7 +92,7 @@ void SoundUnit::NextSample(short* l, short* r) { cycle[i]+=chan[i].freq; } if ((cycle[i]&0xf80000)!=(ocycle[i]&0xf80000)) { - if (chan[i].flags.shape==4) { + if (chan[i].flags0.shape==4) { lfsr[i]=(lfsr[i]>>1|(((lfsr[i]) ^ (lfsr[i] >> 2) ^ (lfsr[i] >> 3) ^ (lfsr[i] >> 5) ) & 1)<<31); } else { switch ((chan[i].duty>>4)&3) { @@ -114,7 +114,7 @@ void SoundUnit::NextSample(short* l, short* r) { } } } - if (chan[i].flags.restim) { + if (chan[i].flags1.restim) { if (--rcycle[i]<=0) { cycle[i]=0; rcycle[i]=chan[i].restimer; @@ -122,19 +122,18 @@ void SoundUnit::NextSample(short* l, short* r) { } } } - fns[i]=ns[i]*chan[i].vol*(chan[i].flags.pcm?4:2); - if (chan[i].flags.fmode!=0) { + fns[i]=ns[i]*chan[i].vol*(chan[i].flags0.pcm?4:2); + if (chan[i].flags0.fmode!=0) { int ff=chan[i].cutoff; nslow[i]=nslow[i]+(((ff)*nsband[i])>>16); nshigh[i]=fns[i]-nslow[i]-(((256-chan[i].reson)*nsband[i])>>8); nsband[i]=(((ff)*nshigh[i])>>16)+nsband[i]; - fns[i]=(((chan[i].flags.fmode&1)?(nslow[i]):(0))+((chan[i].flags.fmode&2)?(nshigh[i]):(0))+((chan[i].flags.fmode&4)?(nsband[i]):(0))); + fns[i]=(((chan[i].flags0.fmode&1)?(nslow[i]):(0))+((chan[i].flags0.fmode&2)?(nshigh[i]):(0))+((chan[i].flags0.fmode&4)?(nsband[i]):(0))); } nsL[i]=(fns[i]*SCpantabL[(unsigned char)chan[i].pan])>>8; nsR[i]=(fns[i]*SCpantabR[(unsigned char)chan[i].pan])>>8; oldfreq[i]=chan[i].freq; - oldflags[i]=chan[i].flags.flags; - if (chan[i].flags.swvol) { + if (chan[i].flags1.swvol) { if (--swvolt[i]<=0) { swvolt[i]=chan[i].swvol.speed; if (chan[i].swvol.dir) { @@ -174,7 +173,7 @@ void SoundUnit::NextSample(short* l, short* r) { } } } - if (chan[i].flags.swfreq) { + if (chan[i].flags1.swfreq) { if (--swfreqt[i]<=0) { swfreqt[i]=chan[i].swfreq.speed; if (chan[i].swfreq.dir) { @@ -198,7 +197,7 @@ void SoundUnit::NextSample(short* l, short* r) { } } } - if (chan[i].flags.swcut) { + if (chan[i].flags1.swcut) { if (--swcutt[i]<=0) { swcutt[i]=chan[i].swcut.speed; if (chan[i].swcut.dir) { @@ -222,11 +221,11 @@ void SoundUnit::NextSample(short* l, short* r) { } } } - if (chan[i].flags.resosc) { + if (chan[i].flags1.resosc) { cycle[i]=0; rcycle[i]=chan[i].restimer; ocycle[i]=0; - chan[i].flags.resosc=0; + chan[i].flags1.resosc=0; } if (muted[i]) { nsL[i]=0; @@ -377,7 +376,6 @@ void SoundUnit::Reset() { swcutt[i]=1; lfsr[i]=0xaaaa; oldfreq[i]=0; - oldflags[i]=0; pcmdec[i]=0; } dsCounterL=0; diff --git a/src/engine/platform/sound/su.h b/src/engine/platform/sound/su.h index 546acfc9c..528d545b5 100644 --- a/src/engine/platform/sound/su.h +++ b/src/engine/platform/sound/su.h @@ -25,7 +25,6 @@ class SoundUnit { signed char ilFeedback0; signed char ilFeedback1; unsigned short oldfreq[8]; - unsigned short oldflags[8]; unsigned int pcmSize; bool dsOut; short dsCounterL, dsCounterR; @@ -44,12 +43,17 @@ class SoundUnit { signed char vol; signed char pan; union { - unsigned short flags; + unsigned char val; struct { unsigned char shape: 3; unsigned char pcm: 1; unsigned char ring: 1; unsigned char fmode: 3; + }; + } flags0; + union { + unsigned char val; + struct { unsigned char resosc: 1; unsigned char resfilt: 1; unsigned char pcmloop: 1; @@ -59,7 +63,7 @@ class SoundUnit { unsigned char swcut: 1; unsigned char padding: 1; }; - } flags; + } flags1; unsigned short cutoff; unsigned char duty; unsigned char reson; diff --git a/src/engine/platform/swan.cpp b/src/engine/platform/swan.cpp index f5a2dfb9a..001d2957a 100644 --- a/src/engine/platform/swan.cpp +++ b/src/engine/platform/swan.cpp @@ -22,6 +22,7 @@ #include #define rWrite(a,v) if (!skipRegisterWrites) {writes.emplace(a,v); if (dumpWrites) {addWrite(a,v);}} +#define postWrite(a,v) postDACWrites.emplace(a,v); #define CHIP_DIVIDER 32 @@ -186,7 +187,7 @@ void DivPlatformSwan::tick(bool sysTick) { } } dacRate=((double)chipClock/2)/MAX(1,off*chan[i].freq); - if (dumpWrites) addWrite(0xffff0001,dacRate); + if (dumpWrites) postWrite(0xffff0001,dacRate); } if (chan[i].freq>2048) chan[i].freq=2048; if (chan[i].freq<1) chan[i].freq=1; @@ -217,6 +218,12 @@ void DivPlatformSwan::tick(bool sysTick) { } } rWrite(0x10,sndCtrl); + + while (!postDACWrites.empty()) { + const DivRegWrite& w=postDACWrites.back(); + if (dumpWrites) addWrite(w.addr,w.val); + postDACWrites.pop(); + } } int DivPlatformSwan::dispatch(DivCommand c) { @@ -237,11 +244,11 @@ int DivPlatformSwan::dispatch(DivCommand c) { if (c.value!=DIV_NOTE_NULL) dacSample=ins->amiga.getSample(c.value); if (dacSample<0 || dacSample>=parent->song.sampleLen) { dacSample=-1; - if (dumpWrites) addWrite(0xffff0002,0); + if (dumpWrites) postWrite(0xffff0002,0); break; } else { if (dumpWrites) { - addWrite(0xffff0000,dacSample); + postWrite(0xffff0000,dacSample); } } if (c.value!=DIV_NOTE_NULL) { @@ -260,14 +267,14 @@ int DivPlatformSwan::dispatch(DivCommand c) { dacSample=12*sampleBank+chan[1].note%12; if (dacSample>=parent->song.sampleLen) { dacSample=-1; - if (dumpWrites) addWrite(0xffff0002,0); + if (dumpWrites) postWrite(0xffff0002,0); break; } else { - if (dumpWrites) addWrite(0xffff0000,dacSample); + if (dumpWrites) postWrite(0xffff0000,dacSample); } dacRate=parent->getSample(dacSample)->rate; if (dumpWrites) { - addWrite(0xffff0001,dacRate); + postWrite(0xffff0001,dacRate); } chan[1].active=true; chan[1].keyOn=true; @@ -298,7 +305,7 @@ int DivPlatformSwan::dispatch(DivCommand c) { case DIV_CMD_NOTE_OFF: if (c.chan==1&&pcm) { dacSample=-1; - if (dumpWrites) addWrite(0xffff0002,0); + if (dumpWrites) postWrite(0xffff0002,0); pcm=false; } chan[c.chan].active=false; @@ -463,6 +470,7 @@ int DivPlatformSwan::getRegisterPoolSize() { void DivPlatformSwan::reset() { while (!writes.empty()) writes.pop(); + while (!postDACWrites.empty()) postDACWrites.pop(); memset(regPool,0,128); for (int i=0; i<4; i++) { chan[i]=Channel(); diff --git a/src/engine/platform/swan.h b/src/engine/platform/swan.h index 6f129e6dd..10e769fc4 100644 --- a/src/engine/platform/swan.h +++ b/src/engine/platform/swan.h @@ -51,6 +51,7 @@ class DivPlatformSwan: public DivDispatch { QueuedWrite(unsigned char a, unsigned char v): addr(a), val(v) {} }; std::queue writes; + std::queue postDACWrites; WSwan* ws; void updateWave(int ch); friend void putDispatchChip(void*,int); diff --git a/src/engine/safeReader.cpp b/src/engine/safeReader.cpp index 76e337a3d..428c91502 100644 --- a/src/engine/safeReader.cpp +++ b/src/engine/safeReader.cpp @@ -136,7 +136,9 @@ float SafeReader::readF() { memcpy(&ret,&buf[curSeek],4); curSeek+=4; ret=((ret>>24)|((ret&0xff0000)>>8)|((ret&0xff00)<<8)|((ret&0xff)<<24)); - return *((float*)(&ret)); + float realRet; + memcpy(&realRet,&ret,4); + return realRet; } double SafeReader::readD() { @@ -153,7 +155,9 @@ double SafeReader::readD() { retB[5]=ret[2]; retB[6]=ret[1]; retB[7]=ret[0]; - return *((double*)retB); + double realRet; + memcpy(&realRet,retB,8); + return realRet; } #else short SafeReader::readS() { diff --git a/src/engine/sample.cpp b/src/engine/sample.cpp index 08dee993d..5df46fab1 100644 --- a/src/engine/sample.cpp +++ b/src/engine/sample.cpp @@ -827,18 +827,15 @@ bool DivSample::resampleBlep(double r) { unsigned int posInt=0; double factor=r/(double)rate; float* sincITable=DivFilterTables::getSincIntegralTable(); - float s[16]; - memset(s,0,16*sizeof(float)); + float* floatData=new float[finalCount]; + memset(floatData,0,finalCount*sizeof(float)); if (depth==DIV_SAMPLE_DEPTH_16BIT) { memset(data16,0,finalCount*sizeof(short)); for (int i=0; i32767) result=32767; - data16[i]=result; + data16[i]=oldData16[posInt]; } posFrac+=1.0; @@ -853,28 +850,25 @@ bool DivSample::resampleBlep(double r) { for (int j=0; j<8; j++) { if (i-j>0) { - float result=data16[i-j]+t1[j]*-delta; - if (result<-32768) result=-32768; - if (result>32767) result=32767; - data16[i-j]=result; + floatData[i-j]+=t1[j]*-delta; } if (i+j+132767) result=32767; - data16[i+j+1]=result; + floatData[i+j+1]+=t2[j]*delta; } } } } + for (int i=0; i32767) result=32767; + data16[i]=round(result); + } } else if (depth==DIV_SAMPLE_DEPTH_8BIT) { memset(data8,0,finalCount); for (int i=0; i127) result=127; - data8[i]=result; + data8[i]=oldData8[posInt]; } posFrac+=1.0; @@ -889,21 +883,22 @@ bool DivSample::resampleBlep(double r) { for (int j=0; j<8; j++) { if (i-j>0) { - float result=data8[i-j]+t1[j]*-delta; - if (result<-128) result=-128; - if (result>127) result=127; - data8[i-j]=result; + floatData[i-j]+=t1[j]*-delta; } if (i+j+1127) result=127; - data8[i+j+1]=result; + floatData[i+j+1]+=t2[j]*delta; } } } } + for (int i=0; i127) result=127; + data16[i]=round(result); + } } + delete[] floatData; RESAMPLE_END; return true; diff --git a/src/engine/vgmOps.cpp b/src/engine/vgmOps.cpp index 9e4557de3..6ac4c2c10 100644 --- a/src/engine/vgmOps.cpp +++ b/src/engine/vgmOps.cpp @@ -24,7 +24,7 @@ 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, bool directStream) { +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, bool directStream) { unsigned char baseAddr1=isSecond?0xa0:0x50; unsigned char baseAddr2=isSecond?0x80:0; unsigned short baseAddr2S=isSecond?0x8000:0; @@ -541,14 +541,19 @@ void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write switch (write.addr&0xff) { case 0: // play sample if (write.valwriteC(0x95); - w->writeC(streamID); - w->writeS(write.val); // sample number - w->writeC((sample->getLoopStartPosition(DIV_SAMPLE_DEPTH_8BIT)==0)|(sampleDir[streamID]?0x10:0)); // flags - if (sample->isLoopable() && !sampleDir[streamID]) { - loopTimer[streamID]=sample->length8; - loopSample[streamID]=write.val; + if (playingSample[streamID]!=write.val) { + pendingFreq[streamID]=write.val; + } else { + DivSample* sample=song.sample[write.val]; + w->writeC(0x95); + w->writeC(streamID); + w->writeS(write.val); // sample number + w->writeC((sample->getLoopStartPosition(DIV_SAMPLE_DEPTH_8BIT)==0)|(sampleDir[streamID]?0x10:0)); // flags + if (sample->isLoopable() && !sampleDir[streamID]) { + loopTimer[streamID]=sample->length8; + loopSample[streamID]=write.val; + } + playingSample[streamID]=write.val; } } break; @@ -557,11 +562,26 @@ void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write w->writeC(streamID); w->writeI(write.val); loopFreq[streamID]=write.val; + if (pendingFreq[streamID]!=-1) { + DivSample* sample=song.sample[pendingFreq[streamID]]; + w->writeC(0x95); + w->writeC(streamID); + w->writeS(pendingFreq[streamID]); // sample number + w->writeC((sample->getLoopStartPosition(DIV_SAMPLE_DEPTH_8BIT)==0)|(sampleDir[streamID]?0x10:0)); // flags + if (sample->isLoopable() && !sampleDir[streamID]) { + loopTimer[streamID]=sample->length8; + loopSample[streamID]=pendingFreq[streamID]; + } + playingSample[streamID]=pendingFreq[streamID]; + pendingFreq[streamID]=-1; + } break; case 2: // stop sample w->writeC(0x94); w->writeC(streamID); loopSample[streamID]=-1; + playingSample[streamID]=-1; + pendingFreq[streamID]=-1; break; case 3: // set sample direction sampleDir[streamID]=write.val; @@ -863,16 +883,20 @@ void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write } #define CHIP_VOL(_id,_mult) { \ - double _vol=fabs((float)song.systemVol[i])*4.0*_mult; \ + double _vol=fabs((float)song.systemVol[i])*256.0*_mult; \ if (_vol<0.0) _vol=0.0; \ if (_vol>32767.0) _vol=32767.0; \ + chipVolSum+=(unsigned int)(_vol/_mult); \ + chipAccounting++; \ chipVol.push_back((_id)|(0x80000000)|(((unsigned int)_vol)<<16)); \ } #define CHIP_VOL_SECOND(_id,_mult) { \ - double _vol=fabs((float)song.systemVol[i])*4.0*_mult; \ + double _vol=fabs((float)song.systemVol[i])*256.0*_mult; \ if (_vol<0.0) _vol=0.0; \ if (_vol>32767.0) _vol=32767.0; \ + chipVolSum+=(unsigned int)(_vol/_mult); \ + chipAccounting++; \ chipVol.push_back((_id)|(0x80000100)|(((unsigned int)_vol)<<16)); \ } @@ -957,6 +981,8 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p int hasLynx=0; int howManyChips=0; + int chipVolSum=0; + int chipAccounting=0; int loopPos=-1; int loopTick=-1; @@ -979,7 +1005,9 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p double loopFreq[DIV_MAX_CHANS]; int loopSample[DIV_MAX_CHANS]; bool sampleDir[DIV_MAX_CHANS]; - std::vector chipVol; + int pendingFreq[DIV_MAX_CHANS]; + int playingSample[DIV_MAX_CHANS]; + std::vector chipVol; std::vector delayedWrites[DIV_MAX_CHIPS]; std::vector> sortedWrites; @@ -987,6 +1015,8 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p loopTimer[i]=0; loopFreq[i]=0; loopSample[i]=-1; + pendingFreq[i]=-1; + playingSample[i]=-1; sampleDir[i]=false; } @@ -1016,7 +1046,7 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p case DIV_SYSTEM_SMS: if (!hasSN) { hasSN=disCont[i].dispatch->chipClock; - CHIP_VOL(0,1.0); + CHIP_VOL(0,2.0); willExport[i]=true; switch (song.systemFlags[i].getInt("chipType",0)) { case 1: // real SN @@ -1035,7 +1065,7 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p } else if (!(hasSN&0x40000000)) { isSecond[i]=true; willExport[i]=true; - CHIP_VOL_SECOND(0,1.0); + CHIP_VOL_SECOND(0,2.0); hasSN|=0x40000000; howManyChips++; } @@ -1043,9 +1073,11 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p case DIV_SYSTEM_GB: if (!hasGB) { hasGB=disCont[i].dispatch->chipClock; + CHIP_VOL(19,0.75); willExport[i]=true; } else if (!(hasGB&0x40000000)) { isSecond[i]=true; + CHIP_VOL_SECOND(19,0.75); willExport[i]=true; hasGB|=0x40000000; howManyChips++; @@ -1054,10 +1086,12 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p case DIV_SYSTEM_PCE: if (!hasPCE) { hasPCE=disCont[i].dispatch->chipClock; + CHIP_VOL(27,0.98); willExport[i]=true; writePCESamples=true; } else if (!(hasPCE&0x40000000)) { isSecond[i]=true; + CHIP_VOL(27,0.98); willExport[i]=true; hasPCE|=0x40000000; howManyChips++; @@ -1066,10 +1100,12 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p case DIV_SYSTEM_NES: if (!hasNES) { hasNES=disCont[i].dispatch->chipClock; + CHIP_VOL(20,1.7); willExport[i]=true; writeNESSamples=true; } else if (!(hasNES&0x40000000)) { isSecond[i]=true; + CHIP_VOL_SECOND(20,1.7); willExport[i]=true; hasNES|=0x40000000; howManyChips++; @@ -1079,10 +1115,12 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p case DIV_SYSTEM_SEGAPCM_COMPAT: if (!hasSegaPCM) { hasSegaPCM=4000000; + CHIP_VOL(4,0.67); willExport[i]=true; writeSegaPCM=1; } else if (!(hasSegaPCM&0x40000000)) { isSecond[i]=true; + CHIP_VOL_SECOND(4,0.67); willExport[i]=true; writeSegaPCM=2; hasSegaPCM|=0x40000000; @@ -1092,10 +1130,12 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p case DIV_SYSTEM_X1_010: if (!hasX1) { hasX1=disCont[i].dispatch->chipClock; + CHIP_VOL(38,0.5); willExport[i]=true; writeX1010[0]=disCont[i].dispatch; } else if (!(hasX1&0x40000000)) { isSecond[i]=true; + CHIP_VOL_SECOND(38,0.5); willExport[i]=true; writeX1010[1]=disCont[i].dispatch; hasX1|=0x40000000; @@ -1110,10 +1150,14 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p case DIV_SYSTEM_YM2610B_EXT: if (!hasOPNB) { hasOPNB=disCont[i].dispatch->chipClock; + CHIP_VOL(8,1.0); + CHIP_VOL(0x88,1.25); willExport[i]=true; writeADPCM_OPNB[0]=disCont[i].dispatch; } else if (!(hasOPNB&0x40000000)) { isSecond[i]=true; + CHIP_VOL_SECOND(8,1.0); + CHIP_VOL_SECOND(0x88,1.25); willExport[i]=true; writeADPCM_OPNB[1]=disCont[i].dispatch; hasOPNB|=0x40000000; @@ -1158,9 +1202,11 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p if (hasStereo && song.systemFlags[i].getBool("stereo",false)) { ayFlags|=0x80; } + CHIP_VOL(18,1.0); willExport[i]=true; } else if (!(hasAY&0x40000000)) { isSecond[i]=true; + CHIP_VOL_SECOND(18,1.0); willExport[i]=true; hasAY|=0x40000000; howManyChips++; @@ -1170,9 +1216,11 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p case DIV_SYSTEM_SAA1099: if (!hasSAA) { hasSAA=disCont[i].dispatch->chipClock; + CHIP_VOL(35,1.0); willExport[i]=true; } else if (!(hasSAA&0x40000000)) { isSecond[i]=true; + CHIP_VOL_SECOND(35,1.0); willExport[i]=true; hasSAA|=0x40000000; howManyChips++; @@ -1184,10 +1232,12 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p case DIV_SYSTEM_YM2612_DUALPCM_EXT: if (!hasOPN2) { hasOPN2=disCont[i].dispatch->chipClock; + CHIP_VOL(2,0.8); willExport[i]=true; writeDACSamples=true; } else if (!(hasOPN2&0x40000000)) { isSecond[i]=true; + CHIP_VOL_SECOND(2,0.8); willExport[i]=true; hasOPN2|=0x40000000; howManyChips++; @@ -1196,9 +1246,11 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p case DIV_SYSTEM_YM2151: if (!hasOPM) { hasOPM=disCont[i].dispatch->chipClock; + CHIP_VOL(3,1.0); willExport[i]=true; } else if (!(hasOPM&0x40000000)) { isSecond[i]=true; + CHIP_VOL_SECOND(3,1.0); willExport[i]=true; hasOPM|=0x40000000; howManyChips++; @@ -1209,10 +1261,14 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p if (!hasOPN) { hasOPN=disCont[i].dispatch->chipClock; willExport[i]=true; + CHIP_VOL(6,1.0); + CHIP_VOL(0x86,1.7); writeDACSamples=true; } else if (!(hasOPN&0x40000000)) { isSecond[i]=true; willExport[i]=true; + CHIP_VOL_SECOND(6,1.0); + CHIP_VOL_SECOND(0x86,1.7); hasOPN|=0x40000000; howManyChips++; } @@ -1221,10 +1277,14 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p case DIV_SYSTEM_YM2608_EXT: if (!hasOPNA) { hasOPNA=disCont[i].dispatch->chipClock; + CHIP_VOL(7,1.0); + CHIP_VOL(0x87,1.3); willExport[i]=true; writeADPCM_OPNA[0]=disCont[i].dispatch; } else if (!(hasOPNA&0x40000000)) { isSecond[i]=true; + CHIP_VOL_SECOND(7,1.0); + CHIP_VOL_SECOND(0x87,1.3); willExport[i]=true; writeADPCM_OPNA[1]=disCont[i].dispatch; hasOPNA|=0x40000000; @@ -1236,9 +1296,11 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p case DIV_SYSTEM_VRC7: if (!hasOPLL) { hasOPLL=disCont[i].dispatch->chipClock; + CHIP_VOL(1,1.6); willExport[i]=true; } else if (!(hasOPLL&0x40000000)) { isSecond[i]=true; + CHIP_VOL_SECOND(1,1.6); willExport[i]=true; hasOPLL|=0x40000000; howManyChips++; @@ -1261,9 +1323,11 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p case DIV_SYSTEM_POKEY: if (!hasPOKEY) { hasPOKEY=disCont[i].dispatch->chipClock; + CHIP_VOL(30,0.8); willExport[i]=true; } else if (!(hasPOKEY&0x40000000)) { isSecond[i]=true; + CHIP_VOL_SECOND(30,0.8); willExport[i]=true; hasPOKEY|=0x40000000; howManyChips++; @@ -1286,10 +1350,12 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p // However I think it it not necessary because old VGM players will still // not be able to handle the 64kb sample bank trick hasQSound=disCont[i].dispatch->chipClock; + CHIP_VOL(31,1.0); willExport[i]=true; writeQSound[0]=disCont[i].dispatch; } else if (!(hasQSound&0x40000000)) { isSecond[i]=true; + CHIP_VOL_SECOND(31,1.0); willExport[i]=false; writeQSound[1]=disCont[i].dispatch; addWarning("dual QSound is not supported by the VGM format"); @@ -1298,6 +1364,7 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p case DIV_SYSTEM_SWAN: if (!hasSwan) { hasSwan=disCont[i].dispatch->chipClock; + CHIP_VOL(33,1.0); willExport[i]=true; // funny enough, VGM doesn't have support for WSC's sound DMA by design // so DAC stream it goes @@ -1305,6 +1372,7 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p writeDACSamples=true; } else if (!(hasSwan&0x40000000)) { isSecond[i]=true; + CHIP_VOL_SECOND(33,1.0); willExport[i]=true; hasSwan|=0x40000000; howManyChips++; @@ -1313,9 +1381,11 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p case DIV_SYSTEM_VBOY: if (!hasVSU) { hasVSU=disCont[i].dispatch->chipClock; + CHIP_VOL(34,0.72); willExport[i]=true; } else if (!(hasVSU&0x40000000)) { isSecond[i]=true; + CHIP_VOL_SECOND(34,0.72); willExport[i]=true; hasVSU|=0x40000000; howManyChips++; @@ -1325,9 +1395,11 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p case DIV_SYSTEM_OPL_DRUMS: if (!hasOPL) { hasOPL=disCont[i].dispatch->chipClock; + CHIP_VOL(9,1.0); willExport[i]=true; } else if (!(hasOPL&0x40000000)) { isSecond[i]=true; + CHIP_VOL_SECOND(9,1.0); willExport[i]=true; hasOPL|=0x40000000; howManyChips++; @@ -1337,10 +1409,12 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p case DIV_SYSTEM_Y8950_DRUMS: if (!hasY8950) { hasY8950=disCont[i].dispatch->chipClock; + CHIP_VOL(11,1.0); willExport[i]=true; writeADPCM_Y8950[0]=disCont[i].dispatch; } else if (!(hasY8950&0x40000000)) { isSecond[i]=true; + CHIP_VOL_SECOND(11,1.0); willExport[i]=true; writeADPCM_Y8950[1]=disCont[i].dispatch; hasY8950|=0x40000000; @@ -1351,9 +1425,11 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p case DIV_SYSTEM_OPL2_DRUMS: if (!hasOPL2) { hasOPL2=disCont[i].dispatch->chipClock; + CHIP_VOL(10,1.0); willExport[i]=true; } else if (!(hasOPL2&0x40000000)) { isSecond[i]=true; + CHIP_VOL_SECOND(10,1.0); willExport[i]=true; hasOPL2|=0x40000000; howManyChips++; @@ -1363,9 +1439,11 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p case DIV_SYSTEM_OPL3_DRUMS: if (!hasOPL3) { hasOPL3=disCont[i].dispatch->chipClock; + CHIP_VOL(12,1.0); willExport[i]=true; } else if (!(hasOPL3&0x40000000)) { isSecond[i]=true; + CHIP_VOL_SECOND(12,1.0); willExport[i]=true; hasOPL3|=0x40000000; howManyChips++; @@ -1378,9 +1456,11 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p if (song.system[i]==DIV_SYSTEM_SCC_PLUS) { hasK051649|=0x80000000; } + CHIP_VOL(25,1.0); willExport[i]=true; } else if (!(hasK051649&0x40000000)) { isSecond[i]=true; + CHIP_VOL_SECOND(25,1.0); willExport[i]=true; hasK051649|=0x40000000; if (song.system[i]==DIV_SYSTEM_SCC_PLUS) { @@ -1392,10 +1472,12 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p case DIV_SYSTEM_YMZ280B: if (!hasZ280) { hasZ280=disCont[i].dispatch->chipClock; + CHIP_VOL(15,0.72); willExport[i]=true; writeZ280[0]=disCont[i].dispatch; } else if (!(hasZ280&0x40000000)) { isSecond[i]=true; + CHIP_VOL_SECOND(15,0.72); willExport[i]=true; writeZ280[1]=disCont[i].dispatch; hasZ280|=0x40000000; @@ -1411,11 +1493,13 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p if (!hasRFC1) { hasRFC1=disCont[i].dispatch->chipClock; isSecond[i]=true; + CHIP_VOL(16,1.6); willExport[i]=true; writeRF5C68[1]=disCont[i].dispatch; } } else if (!hasRFC) { hasRFC=disCont[i].dispatch->chipClock; + CHIP_VOL(5,1.6); willExport[i]=true; writeRF5C68[0]=disCont[i].dispatch; } @@ -1423,10 +1507,12 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p case DIV_SYSTEM_MSM6295: if (!hasOKIM6295) { hasOKIM6295=disCont[i].dispatch->chipClock; + CHIP_VOL(24,1.0); willExport[i]=true; writeMSM6295[0]=disCont[i].dispatch; } else if (!(hasOKIM6295&0x40000000)) { isSecond[i]=true; + CHIP_VOL_SECOND(24,1.0); willExport[i]=true; writeMSM6295[1]=disCont[i].dispatch; hasOKIM6295|=0x40000000; @@ -1436,10 +1522,12 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p case DIV_SYSTEM_GA20: if (!hasGA20) { hasGA20=disCont[i].dispatch->chipClock; + CHIP_VOL(40,0.4); willExport[i]=true; writeGA20[0]=disCont[i].dispatch; } else if (!(hasGA20&0x40000000)) { isSecond[i]=true; + CHIP_VOL_SECOND(40,0.4); willExport[i]=true; writeGA20[1]=disCont[i].dispatch; hasGA20|=0x40000000; @@ -1449,7 +1537,7 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p case DIV_SYSTEM_T6W28: if (!hasSN) { hasSN=0xc0000000|disCont[i].dispatch->chipClock; - CHIP_VOL(0,1.0); + CHIP_VOL(0,2.0); snNoiseConfig=3; snNoiseSize=15; willExport[i]=true; @@ -1524,8 +1612,9 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p w->writeC(0); // OPN w->writeC(0); // OPNA } - if (version>=0x160) { - int calcVolume=32.0*(log(song.masterVol)/log(2.0)); + if (version>=0x160) { // global volume + double abnormalVol=song.masterVol*(double)chipVolSum/(256.0*MAX(1,chipAccounting)); + int calcVolume=32.0*(log(abnormalVol)/log(2.0)); if (calcVolume<-63) calcVolume=-63; if (calcVolume>192) calcVolume=192; w->writeC(calcVolume&0xff); // volume @@ -1991,7 +2080,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],directStream); + performVGMWrite(w,song.system[i],j,streamIDs[i],loopTimer,loopFreq,loopSample,sampleDir,isSecond[i],pendingFreq,playingSample,directStream); writeCount++; } writes.clear(); @@ -2031,7 +2120,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],directStream); + performVGMWrite(w,song.system[i.first],i.second.write,streamIDs[i.first],loopTimer,loopFreq,loopSample,sampleDir,isSecond[i.first],pendingFreq,playingSample,directStream); writeCount++; } sortedWrites.clear(); @@ -2164,7 +2253,7 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p w->writeI(0); } else { w->writeI(loopPos-0x1c); - w->writeI(tickCount-loopTick-1); + w->writeI(tickCount-loopTick); } } else { w->writeI(0); diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index c72958ed9..7ac353455 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -352,6 +352,10 @@ const int detuneUnmap[2][11]={ {0, 0, 0, 3, 4, 5, 6, 7, 2, 1, 0} }; +const int kslMap[4]={ + 0, 2, 1, 3 +}; + // do not change these! // anything other than a checkbox will look ugly! // @@ -2778,7 +2782,11 @@ void FurnaceGUI::drawInsEdit() { if (ins->type==DIV_INS_FM || ins->type==DIV_INS_OPZ || ins->type==DIV_INS_OPM) { P(CWVSliderScalar("##RS",ImVec2(20.0f*dpiScale,sliderHeight),ImGuiDataType_U8,&op.rs,&_ZERO,&_THREE)); } else { - P(CWVSliderScalar("##KSL",ImVec2(20.0f*dpiScale,sliderHeight),ImGuiDataType_U8,&op.ksl,&_ZERO,&_THREE)); + int ksl=ins->type==DIV_INS_OPLL?op.ksl:kslMap[op.ksl&3]; + if (CWVSliderInt("##KSL",ImVec2(20.0f*dpiScale,sliderHeight),&ksl,0,3)) { + op.ksl=(ins->type==DIV_INS_OPLL?ksl:kslMap[ksl&3]); + PARAMETER; + } } if (ins->type==DIV_INS_OPZ) { @@ -3226,7 +3234,7 @@ void FurnaceGUI::drawInsEdit() { break; case DIV_INS_OPL: - case DIV_INS_OPL_DRUMS: + case DIV_INS_OPL_DRUMS: { // waveform drawWaveform(op.ws&7,ins->type==DIV_INS_OPZ,ImVec2(waveWidth,waveHeight)); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); @@ -3268,9 +3276,14 @@ void FurnaceGUI::drawInsEdit() { ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); snprintf(tempID,1024,"%s: %%d",FM_NAME(FM_KSL)); - P(CWSliderScalar("##KSL",ImGuiDataType_U8,&op.ksl,&_ZERO,&_THREE,tempID)); rightClickable + int ksl=kslMap[op.ksl&3]; + if (CWSliderInt("##KSL",&ksl,0,3,tempID)) { + op.ksl=kslMap[ksl&3]; + PARAMETER; + } rightClickable break; + } case DIV_INS_OPZ: { // waveform drawWaveform(op.ws&7,ins->type==DIV_INS_OPZ,ImVec2(waveWidth,waveHeight)); @@ -3613,7 +3626,11 @@ void FurnaceGUI::drawInsEdit() { ImGui::TableNextColumn(); ImGui::Text("%s",FM_NAME(FM_RS)); } else { - P(CWSliderScalar("##KSL",ImGuiDataType_U8,&op.ksl,&_ZERO,&_THREE)); rightClickable + int ksl=ins->type==DIV_INS_OPLL?op.ksl:kslMap[op.ksl&3]; + if (CWSliderInt("##KSL",&ksl,0,3)) { + op.ksl=(ins->type==DIV_INS_OPLL?ksl:kslMap[ksl&3]); + PARAMETER; + } rightClickable ImGui::TableNextColumn(); ImGui::Text("%s",FM_NAME(FM_KSL)); } diff --git a/src/gui/scaling.cpp b/src/gui/scaling.cpp index 7c42a1e33..1b8cfae35 100644 --- a/src/gui/scaling.cpp +++ b/src/gui/scaling.cpp @@ -24,7 +24,8 @@ #ifdef _WIN32 #include -typedef HRESULT (*GDFM)(HMONITOR,int,UINT*,UINT*); +#include "shellScalingStub.h" +typedef HRESULT (WINAPI *GDFM)(HMONITOR,MONITOR_DPI_TYPE,UINT*,UINT*); #endif #ifdef __APPLE__ @@ -71,9 +72,9 @@ double getScaleFactor(const char* driverHint) { return 1.0; } - unsigned int dpiX=96; - unsigned int dpiY=96; - HRESULT result=ta_GetDpiForMonitor(disp,0,&dpiX,&dpiY); + UINT dpiX=96; + UINT dpiY=96; + HRESULT result=ta_GetDpiForMonitor(disp,MDT_EFFECTIVE_DPI,&dpiX,&dpiY); if (result!=S_OK) { logW("GetDpiForMonitor failure (%.8x) - no scaling detection available!",result); diff --git a/src/gui/shellScalingStub.h b/src/gui/shellScalingStub.h new file mode 100644 index 000000000..88ddf2f7e --- /dev/null +++ b/src/gui/shellScalingStub.h @@ -0,0 +1,36 @@ +/* + * Copyright 2016 Sebastian Lackner + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef __WINE_SHELLSCALINGAPI_H + +typedef enum MONITOR_DPI_TYPE +{ + MDT_EFFECTIVE_DPI = 0, + MDT_ANGULAR_DPI = 1, + MDT_RAW_DPI = 2, + MDT_DEFAULT = MDT_EFFECTIVE_DPI, +} MONITOR_DPI_TYPE; + +typedef enum PROCESS_DPI_AWARENESS +{ + PROCESS_DPI_UNAWARE, + PROCESS_SYSTEM_DPI_AWARE, + PROCESS_PER_MONITOR_DPI_AWARE +} PROCESS_DPI_AWARENESS; + +#endif /* __WINE_SHELLSCALINGAPI_H */ diff --git a/src/main.cpp b/src/main.cpp index 7d797fcfb..ef335a223 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -32,6 +32,10 @@ #include #include #include + +#include "gui/shellScalingStub.h" + +typedef HRESULT (WINAPI *SPDA)(PROCESS_DPI_AWARENESS); #else #include #endif @@ -339,6 +343,22 @@ void reportError(String what) { int main(int argc, char** argv) { initLog(); #ifdef _WIN32 + // set DPI awareness + HMODULE shcore=LoadLibraryW(L"shcore.dll"); + if (shcore!=NULL) { + SPDA ta_SetProcessDpiAwareness=(SPDA)GetProcAddress(shcore,"SetProcessDpiAwareness"); + if (ta_SetProcessDpiAwareness!=NULL) { + HRESULT result=ta_SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE); + if (result!=S_OK) { + // ??? + } + } + if (!FreeLibrary(shcore)) { + // ??? + } + } + + // co initialize ex HRESULT coResult=CoInitializeEx(NULL,COINIT_MULTITHREADED); if (coResult!=S_OK) { logE("CoInitializeEx failed!");