Merge branch 'master' of https://github.com/tildearrow/furnace into ymf289b

This commit is contained in:
cam900 2023-01-16 10:17:19 +09:00
commit 371c82338f
18 changed files with 270 additions and 93 deletions

View file

@ -11,7 +11,7 @@ defaults:
shell: bash shell: bash
env: env:
BUILD_TYPE: Debug BUILD_TYPE: Release
jobs: jobs:
build: build:
@ -20,11 +20,11 @@ jobs:
config: config:
- { name: 'Windows MSVC x86', os: windows-latest, compiler: msvc, arch: x86 } - { 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 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', 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_64', os: ubuntu-20.04, compiler: mingw, arch: x86_64 }
- { name: 'macOS x86_64', os: macos-latest, arch: x86_64 } - { name: 'macOS x86_64', os: macos-latest, arch: x86_64 }
- { name: 'macOS ARM', os: macos-latest, arch: arm64 } - { 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 } #- { name: 'Linux ARM', os: ubuntu-18.04, arch: armhf }
fail-fast: false fail-fast: false

Binary file not shown.

Binary file not shown.

View file

@ -442,7 +442,7 @@ class DivEngine {
void processRow(int i, bool afterDelay); void processRow(int i, bool afterDelay);
void nextOrder(); void nextOrder();
void nextRow(); 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. // returns true if end of song.
bool nextTick(bool noAccum=false, bool inhibitLowLat=false); bool nextTick(bool noAccum=false, bool inhibitLowLat=false);
bool perSystemEffect(int ch, unsigned char effect, unsigned char effectVal); bool perSystemEffect(int ch, unsigned char effect, unsigned char effectVal);

View file

@ -586,7 +586,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
} }
} }
if (c.chan>=5 && chan[c.chan].dacMode) { if (c.chan>=5 && chan[c.chan].dacMode) {
if (skipRegisterWrites) break; //if (skipRegisterWrites) break;
if (ins->type==DIV_INS_AMIGA) { // Furnace mode if (ins->type==DIV_INS_AMIGA) { // Furnace mode
if (c.value!=DIV_NOTE_NULL) chan[c.chan].dacSample=ins->amiga.getSample(c.value); 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) { if (chan[c.chan].dacSample<0 || chan[c.chan].dacSample>=parent->song.sampleLen) {
@ -1122,6 +1122,8 @@ void DivPlatformGenesis::forceIns() {
} }
} }
if (chan[5].dacMode) { if (chan[5].dacMode) {
chan[5].dacSample=-1;
chan[6].dacSample=-1;
rWrite(0x2b,0x80); rWrite(0x2b,0x80);
} }
immWrite(0x22,lfoValue); immWrite(0x22,lfoValue);

View file

@ -677,6 +677,8 @@ void DivPlatformGenesisExt::forceIns() {
} }
} }
if (chan[5].dacMode) { if (chan[5].dacMode) {
chan[5].dacSample=-1;
chan[6].dacSample=-1;
rWrite(0x2b,0x80); rWrite(0x2b,0x80);
} }
immWrite(0x22,lfoValue); immWrite(0x22,lfoValue);

View file

@ -1518,7 +1518,7 @@ DivMacroInt* DivPlatformOPL::getChanMacroInt(int ch) {
} }
DivDispatchOscBuffer* DivPlatformOPL::getOscBuffer(int ch) { DivDispatchOscBuffer* DivPlatformOPL::getOscBuffer(int ch) {
if (ch>=18) return NULL; if (ch>=totalChans) return NULL;
if (oplType==3 && ch<12) { if (oplType==3 && ch<12) {
if (chan[ch&(~1)].fourOp) { if (chan[ch&(~1)].fourOp) {
if (ch&1) { if (ch&1) {

View file

@ -17,13 +17,13 @@
void SoundUnit::NextSample(short* l, short* r) { void SoundUnit::NextSample(short* l, short* r) {
// run channels // run channels
for (int i=0; i<8; i++) { 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; fns[i]=0;
continue; continue;
} }
if (chan[i].flags.pcm) { if (chan[i].flags0.pcm) {
ns[i]=pcm[chan[i].pcmpos]; ns[i]=pcm[chan[i].pcmpos];
} else switch (chan[i].flags.shape) { } else switch (chan[i].flags0.shape) {
case 0: case 0:
ns[i]=(((cycle[i]>>15)&127)>chan[i].duty)*127; ns[i]=(((cycle[i]>>15)&127)>chan[i].duty)*127;
break; break;
@ -47,11 +47,11 @@ void SoundUnit::NextSample(short* l, short* r) {
break; break;
} }
if (chan[i].flags.ring) { if (chan[i].flags0.ring) {
ns[i]=(ns[i]*ns[(i+1)&7])>>7; ns[i]=(ns[i]*ns[(i+1)&7])>>7;
} }
if (chan[i].flags.pcm) { if (chan[i].flags0.pcm) {
if (chan[i].freq>0x8000) { if (chan[i].freq>0x8000) {
pcmdec[i]+=0x8000; pcmdec[i]+=0x8000;
} else { } else {
@ -62,18 +62,18 @@ void SoundUnit::NextSample(short* l, short* r) {
if (chan[i].pcmpos<chan[i].pcmbnd) { if (chan[i].pcmpos<chan[i].pcmbnd) {
chan[i].pcmpos++; chan[i].pcmpos++;
if (chan[i].pcmpos==chan[i].pcmbnd) { if (chan[i].pcmpos==chan[i].pcmbnd) {
if (chan[i].flags.pcmloop) { if (chan[i].flags1.pcmloop) {
chan[i].pcmpos=chan[i].pcmrst; chan[i].pcmpos=chan[i].pcmrst;
} }
} }
chan[i].pcmpos&=(pcmSize-1); chan[i].pcmpos&=(pcmSize-1);
} else if (chan[i].flags.pcmloop) { } else if (chan[i].flags1.pcmloop) {
chan[i].pcmpos=chan[i].pcmrst; chan[i].pcmpos=chan[i].pcmrst;
} }
} }
} else { } else {
ocycle[i]=cycle[i]; ocycle[i]=cycle[i];
if (chan[i].flags.shape==5) { if (chan[i].flags0.shape==5) {
switch ((chan[i].duty>>4)&3) { switch ((chan[i].duty>>4)&3) {
case 0: case 0:
cycle[i]+=chan[i].freq*1-(chan[i].freq>>3); 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; cycle[i]+=chan[i].freq;
} }
if ((cycle[i]&0xf80000)!=(ocycle[i]&0xf80000)) { 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); lfsr[i]=(lfsr[i]>>1|(((lfsr[i]) ^ (lfsr[i] >> 2) ^ (lfsr[i] >> 3) ^ (lfsr[i] >> 5) ) & 1)<<31);
} else { } else {
switch ((chan[i].duty>>4)&3) { 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) { if (--rcycle[i]<=0) {
cycle[i]=0; cycle[i]=0;
rcycle[i]=chan[i].restimer; 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); fns[i]=ns[i]*chan[i].vol*(chan[i].flags0.pcm?4:2);
if (chan[i].flags.fmode!=0) { if (chan[i].flags0.fmode!=0) {
int ff=chan[i].cutoff; int ff=chan[i].cutoff;
nslow[i]=nslow[i]+(((ff)*nsband[i])>>16); nslow[i]=nslow[i]+(((ff)*nsband[i])>>16);
nshigh[i]=fns[i]-nslow[i]-(((256-chan[i].reson)*nsband[i])>>8); nshigh[i]=fns[i]-nslow[i]-(((256-chan[i].reson)*nsband[i])>>8);
nsband[i]=(((ff)*nshigh[i])>>16)+nsband[i]; 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; nsL[i]=(fns[i]*SCpantabL[(unsigned char)chan[i].pan])>>8;
nsR[i]=(fns[i]*SCpantabR[(unsigned char)chan[i].pan])>>8; nsR[i]=(fns[i]*SCpantabR[(unsigned char)chan[i].pan])>>8;
oldfreq[i]=chan[i].freq; oldfreq[i]=chan[i].freq;
oldflags[i]=chan[i].flags.flags; if (chan[i].flags1.swvol) {
if (chan[i].flags.swvol) {
if (--swvolt[i]<=0) { if (--swvolt[i]<=0) {
swvolt[i]=chan[i].swvol.speed; swvolt[i]=chan[i].swvol.speed;
if (chan[i].swvol.dir) { 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) { if (--swfreqt[i]<=0) {
swfreqt[i]=chan[i].swfreq.speed; swfreqt[i]=chan[i].swfreq.speed;
if (chan[i].swfreq.dir) { 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) { if (--swcutt[i]<=0) {
swcutt[i]=chan[i].swcut.speed; swcutt[i]=chan[i].swcut.speed;
if (chan[i].swcut.dir) { 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; cycle[i]=0;
rcycle[i]=chan[i].restimer; rcycle[i]=chan[i].restimer;
ocycle[i]=0; ocycle[i]=0;
chan[i].flags.resosc=0; chan[i].flags1.resosc=0;
} }
if (muted[i]) { if (muted[i]) {
nsL[i]=0; nsL[i]=0;
@ -377,7 +376,6 @@ void SoundUnit::Reset() {
swcutt[i]=1; swcutt[i]=1;
lfsr[i]=0xaaaa; lfsr[i]=0xaaaa;
oldfreq[i]=0; oldfreq[i]=0;
oldflags[i]=0;
pcmdec[i]=0; pcmdec[i]=0;
} }
dsCounterL=0; dsCounterL=0;

View file

@ -25,7 +25,6 @@ class SoundUnit {
signed char ilFeedback0; signed char ilFeedback0;
signed char ilFeedback1; signed char ilFeedback1;
unsigned short oldfreq[8]; unsigned short oldfreq[8];
unsigned short oldflags[8];
unsigned int pcmSize; unsigned int pcmSize;
bool dsOut; bool dsOut;
short dsCounterL, dsCounterR; short dsCounterL, dsCounterR;
@ -44,12 +43,17 @@ class SoundUnit {
signed char vol; signed char vol;
signed char pan; signed char pan;
union { union {
unsigned short flags; unsigned char val;
struct { struct {
unsigned char shape: 3; unsigned char shape: 3;
unsigned char pcm: 1; unsigned char pcm: 1;
unsigned char ring: 1; unsigned char ring: 1;
unsigned char fmode: 3; unsigned char fmode: 3;
};
} flags0;
union {
unsigned char val;
struct {
unsigned char resosc: 1; unsigned char resosc: 1;
unsigned char resfilt: 1; unsigned char resfilt: 1;
unsigned char pcmloop: 1; unsigned char pcmloop: 1;
@ -59,7 +63,7 @@ class SoundUnit {
unsigned char swcut: 1; unsigned char swcut: 1;
unsigned char padding: 1; unsigned char padding: 1;
}; };
} flags; } flags1;
unsigned short cutoff; unsigned short cutoff;
unsigned char duty; unsigned char duty;
unsigned char reson; unsigned char reson;

View file

@ -22,6 +22,7 @@
#include <math.h> #include <math.h>
#define rWrite(a,v) if (!skipRegisterWrites) {writes.emplace(a,v); if (dumpWrites) {addWrite(a,v);}} #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 #define CHIP_DIVIDER 32
@ -186,7 +187,7 @@ void DivPlatformSwan::tick(bool sysTick) {
} }
} }
dacRate=((double)chipClock/2)/MAX(1,off*chan[i].freq); 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>2048) chan[i].freq=2048;
if (chan[i].freq<1) chan[i].freq=1; if (chan[i].freq<1) chan[i].freq=1;
@ -217,6 +218,12 @@ void DivPlatformSwan::tick(bool sysTick) {
} }
} }
rWrite(0x10,sndCtrl); 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) { 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 (c.value!=DIV_NOTE_NULL) dacSample=ins->amiga.getSample(c.value);
if (dacSample<0 || dacSample>=parent->song.sampleLen) { if (dacSample<0 || dacSample>=parent->song.sampleLen) {
dacSample=-1; dacSample=-1;
if (dumpWrites) addWrite(0xffff0002,0); if (dumpWrites) postWrite(0xffff0002,0);
break; break;
} else { } else {
if (dumpWrites) { if (dumpWrites) {
addWrite(0xffff0000,dacSample); postWrite(0xffff0000,dacSample);
} }
} }
if (c.value!=DIV_NOTE_NULL) { if (c.value!=DIV_NOTE_NULL) {
@ -260,14 +267,14 @@ int DivPlatformSwan::dispatch(DivCommand c) {
dacSample=12*sampleBank+chan[1].note%12; dacSample=12*sampleBank+chan[1].note%12;
if (dacSample>=parent->song.sampleLen) { if (dacSample>=parent->song.sampleLen) {
dacSample=-1; dacSample=-1;
if (dumpWrites) addWrite(0xffff0002,0); if (dumpWrites) postWrite(0xffff0002,0);
break; break;
} else { } else {
if (dumpWrites) addWrite(0xffff0000,dacSample); if (dumpWrites) postWrite(0xffff0000,dacSample);
} }
dacRate=parent->getSample(dacSample)->rate; dacRate=parent->getSample(dacSample)->rate;
if (dumpWrites) { if (dumpWrites) {
addWrite(0xffff0001,dacRate); postWrite(0xffff0001,dacRate);
} }
chan[1].active=true; chan[1].active=true;
chan[1].keyOn=true; chan[1].keyOn=true;
@ -298,7 +305,7 @@ int DivPlatformSwan::dispatch(DivCommand c) {
case DIV_CMD_NOTE_OFF: case DIV_CMD_NOTE_OFF:
if (c.chan==1&&pcm) { if (c.chan==1&&pcm) {
dacSample=-1; dacSample=-1;
if (dumpWrites) addWrite(0xffff0002,0); if (dumpWrites) postWrite(0xffff0002,0);
pcm=false; pcm=false;
} }
chan[c.chan].active=false; chan[c.chan].active=false;
@ -463,6 +470,7 @@ int DivPlatformSwan::getRegisterPoolSize() {
void DivPlatformSwan::reset() { void DivPlatformSwan::reset() {
while (!writes.empty()) writes.pop(); while (!writes.empty()) writes.pop();
while (!postDACWrites.empty()) postDACWrites.pop();
memset(regPool,0,128); memset(regPool,0,128);
for (int i=0; i<4; i++) { for (int i=0; i<4; i++) {
chan[i]=Channel(); chan[i]=Channel();

View file

@ -51,6 +51,7 @@ class DivPlatformSwan: public DivDispatch {
QueuedWrite(unsigned char a, unsigned char v): addr(a), val(v) {} QueuedWrite(unsigned char a, unsigned char v): addr(a), val(v) {}
}; };
std::queue<QueuedWrite> writes; std::queue<QueuedWrite> writes;
std::queue<DivRegWrite> postDACWrites;
WSwan* ws; WSwan* ws;
void updateWave(int ch); void updateWave(int ch);
friend void putDispatchChip(void*,int); friend void putDispatchChip(void*,int);

View file

@ -136,7 +136,9 @@ float SafeReader::readF() {
memcpy(&ret,&buf[curSeek],4); memcpy(&ret,&buf[curSeek],4);
curSeek+=4; curSeek+=4;
ret=((ret>>24)|((ret&0xff0000)>>8)|((ret&0xff00)<<8)|((ret&0xff)<<24)); 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() { double SafeReader::readD() {
@ -153,7 +155,9 @@ double SafeReader::readD() {
retB[5]=ret[2]; retB[5]=ret[2];
retB[6]=ret[1]; retB[6]=ret[1];
retB[7]=ret[0]; retB[7]=ret[0];
return *((double*)retB); double realRet;
memcpy(&realRet,retB,8);
return realRet;
} }
#else #else
short SafeReader::readS() { short SafeReader::readS() {

View file

@ -827,18 +827,15 @@ bool DivSample::resampleBlep(double r) {
unsigned int posInt=0; unsigned int posInt=0;
double factor=r/(double)rate; double factor=r/(double)rate;
float* sincITable=DivFilterTables::getSincIntegralTable(); 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) { if (depth==DIV_SAMPLE_DEPTH_16BIT) {
memset(data16,0,finalCount*sizeof(short)); memset(data16,0,finalCount*sizeof(short));
for (int i=0; i<finalCount; i++) { for (int i=0; i<finalCount; i++) {
if (posInt<samples) { if (posInt<samples) {
int result=data16[i]+oldData16[posInt]; data16[i]=oldData16[posInt];
if (result<-32768) result=-32768;
if (result>32767) result=32767;
data16[i]=result;
} }
posFrac+=1.0; posFrac+=1.0;
@ -853,28 +850,25 @@ bool DivSample::resampleBlep(double r) {
for (int j=0; j<8; j++) { for (int j=0; j<8; j++) {
if (i-j>0) { if (i-j>0) {
float result=data16[i-j]+t1[j]*-delta; floatData[i-j]+=t1[j]*-delta;
if (result<-32768) result=-32768;
if (result>32767) result=32767;
data16[i-j]=result;
} }
if (i+j+1<finalCount) { if (i+j+1<finalCount) {
float result=data16[i+j+1]+t2[j]*delta; floatData[i+j+1]+=t2[j]*delta;
if (result<-32768) result=-32768;
if (result>32767) result=32767;
data16[i+j+1]=result;
} }
} }
} }
} }
for (int i=0; i<finalCount; i++) {
float result=floatData[i]+data16[i];
if (result<-32768) result=-32768;
if (result>32767) result=32767;
data16[i]=round(result);
}
} else if (depth==DIV_SAMPLE_DEPTH_8BIT) { } else if (depth==DIV_SAMPLE_DEPTH_8BIT) {
memset(data8,0,finalCount); memset(data8,0,finalCount);
for (int i=0; i<finalCount; i++) { for (int i=0; i<finalCount; i++) {
if (posInt<samples) { if (posInt<samples) {
int result=data8[i]+oldData8[posInt]; data8[i]=oldData8[posInt];
if (result<-128) result=-128;
if (result>127) result=127;
data8[i]=result;
} }
posFrac+=1.0; posFrac+=1.0;
@ -889,21 +883,22 @@ bool DivSample::resampleBlep(double r) {
for (int j=0; j<8; j++) { for (int j=0; j<8; j++) {
if (i-j>0) { if (i-j>0) {
float result=data8[i-j]+t1[j]*-delta; floatData[i-j]+=t1[j]*-delta;
if (result<-128) result=-128;
if (result>127) result=127;
data8[i-j]=result;
} }
if (i+j+1<finalCount) { if (i+j+1<finalCount) {
float result=data8[i+j+1]+t2[j]*delta; floatData[i+j+1]+=t2[j]*delta;
if (result<-128) result=-128;
if (result>127) result=127;
data8[i+j+1]=result;
} }
} }
} }
} }
for (int i=0; i<finalCount; i++) {
float result=floatData[i]+data16[i];
if (result<-128) result=-128;
if (result>127) result=127;
data16[i]=round(result);
}
} }
delete[] floatData;
RESAMPLE_END; RESAMPLE_END;
return true; return true;

View file

@ -24,7 +24,7 @@
constexpr int MASTER_CLOCK_PREC=(sizeof(void*)==8)?8:0; 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 baseAddr1=isSecond?0xa0:0x50;
unsigned char baseAddr2=isSecond?0x80:0; unsigned char baseAddr2=isSecond?0x80:0;
unsigned short baseAddr2S=isSecond?0x8000:0; unsigned short baseAddr2S=isSecond?0x8000:0;
@ -541,14 +541,19 @@ void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write
switch (write.addr&0xff) { switch (write.addr&0xff) {
case 0: // play sample case 0: // play sample
if (write.val<song.sampleLen) { if (write.val<song.sampleLen) {
DivSample* sample=song.sample[write.val]; if (playingSample[streamID]!=write.val) {
w->writeC(0x95); pendingFreq[streamID]=write.val;
w->writeC(streamID); } else {
w->writeS(write.val); // sample number DivSample* sample=song.sample[write.val];
w->writeC((sample->getLoopStartPosition(DIV_SAMPLE_DEPTH_8BIT)==0)|(sampleDir[streamID]?0x10:0)); // flags w->writeC(0x95);
if (sample->isLoopable() && !sampleDir[streamID]) { w->writeC(streamID);
loopTimer[streamID]=sample->length8; w->writeS(write.val); // sample number
loopSample[streamID]=write.val; 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; break;
@ -557,11 +562,26 @@ void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write
w->writeC(streamID); w->writeC(streamID);
w->writeI(write.val); w->writeI(write.val);
loopFreq[streamID]=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; break;
case 2: // stop sample case 2: // stop sample
w->writeC(0x94); w->writeC(0x94);
w->writeC(streamID); w->writeC(streamID);
loopSample[streamID]=-1; loopSample[streamID]=-1;
playingSample[streamID]=-1;
pendingFreq[streamID]=-1;
break; break;
case 3: // set sample direction case 3: // set sample direction
sampleDir[streamID]=write.val; sampleDir[streamID]=write.val;
@ -863,16 +883,20 @@ void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write
} }
#define CHIP_VOL(_id,_mult) { \ #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<0.0) _vol=0.0; \
if (_vol>32767.0) _vol=32767.0; \ if (_vol>32767.0) _vol=32767.0; \
chipVolSum+=(unsigned int)(_vol/_mult); \
chipAccounting++; \
chipVol.push_back((_id)|(0x80000000)|(((unsigned int)_vol)<<16)); \ chipVol.push_back((_id)|(0x80000000)|(((unsigned int)_vol)<<16)); \
} }
#define CHIP_VOL_SECOND(_id,_mult) { \ #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<0.0) _vol=0.0; \
if (_vol>32767.0) _vol=32767.0; \ if (_vol>32767.0) _vol=32767.0; \
chipVolSum+=(unsigned int)(_vol/_mult); \
chipAccounting++; \
chipVol.push_back((_id)|(0x80000100)|(((unsigned int)_vol)<<16)); \ 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 hasLynx=0;
int howManyChips=0; int howManyChips=0;
int chipVolSum=0;
int chipAccounting=0;
int loopPos=-1; int loopPos=-1;
int loopTick=-1; int loopTick=-1;
@ -979,7 +1005,9 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
double loopFreq[DIV_MAX_CHANS]; double loopFreq[DIV_MAX_CHANS];
int loopSample[DIV_MAX_CHANS]; int loopSample[DIV_MAX_CHANS];
bool sampleDir[DIV_MAX_CHANS]; bool sampleDir[DIV_MAX_CHANS];
std::vector<unsigned int> chipVol; int pendingFreq[DIV_MAX_CHANS];
int playingSample[DIV_MAX_CHANS];
std::vector<unsigned int> chipVol;
std::vector<DivDelayedWrite> delayedWrites[DIV_MAX_CHIPS]; std::vector<DivDelayedWrite> delayedWrites[DIV_MAX_CHIPS];
std::vector<std::pair<int,DivDelayedWrite>> sortedWrites; std::vector<std::pair<int,DivDelayedWrite>> sortedWrites;
@ -987,6 +1015,8 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
loopTimer[i]=0; loopTimer[i]=0;
loopFreq[i]=0; loopFreq[i]=0;
loopSample[i]=-1; loopSample[i]=-1;
pendingFreq[i]=-1;
playingSample[i]=-1;
sampleDir[i]=false; sampleDir[i]=false;
} }
@ -1016,7 +1046,7 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
case DIV_SYSTEM_SMS: case DIV_SYSTEM_SMS:
if (!hasSN) { if (!hasSN) {
hasSN=disCont[i].dispatch->chipClock; hasSN=disCont[i].dispatch->chipClock;
CHIP_VOL(0,1.0); CHIP_VOL(0,2.0);
willExport[i]=true; willExport[i]=true;
switch (song.systemFlags[i].getInt("chipType",0)) { switch (song.systemFlags[i].getInt("chipType",0)) {
case 1: // real SN case 1: // real SN
@ -1035,7 +1065,7 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
} else if (!(hasSN&0x40000000)) { } else if (!(hasSN&0x40000000)) {
isSecond[i]=true; isSecond[i]=true;
willExport[i]=true; willExport[i]=true;
CHIP_VOL_SECOND(0,1.0); CHIP_VOL_SECOND(0,2.0);
hasSN|=0x40000000; hasSN|=0x40000000;
howManyChips++; howManyChips++;
} }
@ -1043,9 +1073,11 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
case DIV_SYSTEM_GB: case DIV_SYSTEM_GB:
if (!hasGB) { if (!hasGB) {
hasGB=disCont[i].dispatch->chipClock; hasGB=disCont[i].dispatch->chipClock;
CHIP_VOL(19,0.75);
willExport[i]=true; willExport[i]=true;
} else if (!(hasGB&0x40000000)) { } else if (!(hasGB&0x40000000)) {
isSecond[i]=true; isSecond[i]=true;
CHIP_VOL_SECOND(19,0.75);
willExport[i]=true; willExport[i]=true;
hasGB|=0x40000000; hasGB|=0x40000000;
howManyChips++; howManyChips++;
@ -1054,10 +1086,12 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
case DIV_SYSTEM_PCE: case DIV_SYSTEM_PCE:
if (!hasPCE) { if (!hasPCE) {
hasPCE=disCont[i].dispatch->chipClock; hasPCE=disCont[i].dispatch->chipClock;
CHIP_VOL(27,0.98);
willExport[i]=true; willExport[i]=true;
writePCESamples=true; writePCESamples=true;
} else if (!(hasPCE&0x40000000)) { } else if (!(hasPCE&0x40000000)) {
isSecond[i]=true; isSecond[i]=true;
CHIP_VOL(27,0.98);
willExport[i]=true; willExport[i]=true;
hasPCE|=0x40000000; hasPCE|=0x40000000;
howManyChips++; howManyChips++;
@ -1066,10 +1100,12 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
case DIV_SYSTEM_NES: case DIV_SYSTEM_NES:
if (!hasNES) { if (!hasNES) {
hasNES=disCont[i].dispatch->chipClock; hasNES=disCont[i].dispatch->chipClock;
CHIP_VOL(20,1.7);
willExport[i]=true; willExport[i]=true;
writeNESSamples=true; writeNESSamples=true;
} else if (!(hasNES&0x40000000)) { } else if (!(hasNES&0x40000000)) {
isSecond[i]=true; isSecond[i]=true;
CHIP_VOL_SECOND(20,1.7);
willExport[i]=true; willExport[i]=true;
hasNES|=0x40000000; hasNES|=0x40000000;
howManyChips++; howManyChips++;
@ -1079,10 +1115,12 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
case DIV_SYSTEM_SEGAPCM_COMPAT: case DIV_SYSTEM_SEGAPCM_COMPAT:
if (!hasSegaPCM) { if (!hasSegaPCM) {
hasSegaPCM=4000000; hasSegaPCM=4000000;
CHIP_VOL(4,0.67);
willExport[i]=true; willExport[i]=true;
writeSegaPCM=1; writeSegaPCM=1;
} else if (!(hasSegaPCM&0x40000000)) { } else if (!(hasSegaPCM&0x40000000)) {
isSecond[i]=true; isSecond[i]=true;
CHIP_VOL_SECOND(4,0.67);
willExport[i]=true; willExport[i]=true;
writeSegaPCM=2; writeSegaPCM=2;
hasSegaPCM|=0x40000000; hasSegaPCM|=0x40000000;
@ -1092,10 +1130,12 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
case DIV_SYSTEM_X1_010: case DIV_SYSTEM_X1_010:
if (!hasX1) { if (!hasX1) {
hasX1=disCont[i].dispatch->chipClock; hasX1=disCont[i].dispatch->chipClock;
CHIP_VOL(38,0.5);
willExport[i]=true; willExport[i]=true;
writeX1010[0]=disCont[i].dispatch; writeX1010[0]=disCont[i].dispatch;
} else if (!(hasX1&0x40000000)) { } else if (!(hasX1&0x40000000)) {
isSecond[i]=true; isSecond[i]=true;
CHIP_VOL_SECOND(38,0.5);
willExport[i]=true; willExport[i]=true;
writeX1010[1]=disCont[i].dispatch; writeX1010[1]=disCont[i].dispatch;
hasX1|=0x40000000; hasX1|=0x40000000;
@ -1110,10 +1150,14 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
case DIV_SYSTEM_YM2610B_EXT: case DIV_SYSTEM_YM2610B_EXT:
if (!hasOPNB) { if (!hasOPNB) {
hasOPNB=disCont[i].dispatch->chipClock; hasOPNB=disCont[i].dispatch->chipClock;
CHIP_VOL(8,1.0);
CHIP_VOL(0x88,1.25);
willExport[i]=true; willExport[i]=true;
writeADPCM_OPNB[0]=disCont[i].dispatch; writeADPCM_OPNB[0]=disCont[i].dispatch;
} else if (!(hasOPNB&0x40000000)) { } else if (!(hasOPNB&0x40000000)) {
isSecond[i]=true; isSecond[i]=true;
CHIP_VOL_SECOND(8,1.0);
CHIP_VOL_SECOND(0x88,1.25);
willExport[i]=true; willExport[i]=true;
writeADPCM_OPNB[1]=disCont[i].dispatch; writeADPCM_OPNB[1]=disCont[i].dispatch;
hasOPNB|=0x40000000; 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)) { if (hasStereo && song.systemFlags[i].getBool("stereo",false)) {
ayFlags|=0x80; ayFlags|=0x80;
} }
CHIP_VOL(18,1.0);
willExport[i]=true; willExport[i]=true;
} else if (!(hasAY&0x40000000)) { } else if (!(hasAY&0x40000000)) {
isSecond[i]=true; isSecond[i]=true;
CHIP_VOL_SECOND(18,1.0);
willExport[i]=true; willExport[i]=true;
hasAY|=0x40000000; hasAY|=0x40000000;
howManyChips++; howManyChips++;
@ -1170,9 +1216,11 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
case DIV_SYSTEM_SAA1099: case DIV_SYSTEM_SAA1099:
if (!hasSAA) { if (!hasSAA) {
hasSAA=disCont[i].dispatch->chipClock; hasSAA=disCont[i].dispatch->chipClock;
CHIP_VOL(35,1.0);
willExport[i]=true; willExport[i]=true;
} else if (!(hasSAA&0x40000000)) { } else if (!(hasSAA&0x40000000)) {
isSecond[i]=true; isSecond[i]=true;
CHIP_VOL_SECOND(35,1.0);
willExport[i]=true; willExport[i]=true;
hasSAA|=0x40000000; hasSAA|=0x40000000;
howManyChips++; howManyChips++;
@ -1184,10 +1232,12 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
case DIV_SYSTEM_YM2612_DUALPCM_EXT: case DIV_SYSTEM_YM2612_DUALPCM_EXT:
if (!hasOPN2) { if (!hasOPN2) {
hasOPN2=disCont[i].dispatch->chipClock; hasOPN2=disCont[i].dispatch->chipClock;
CHIP_VOL(2,0.8);
willExport[i]=true; willExport[i]=true;
writeDACSamples=true; writeDACSamples=true;
} else if (!(hasOPN2&0x40000000)) { } else if (!(hasOPN2&0x40000000)) {
isSecond[i]=true; isSecond[i]=true;
CHIP_VOL_SECOND(2,0.8);
willExport[i]=true; willExport[i]=true;
hasOPN2|=0x40000000; hasOPN2|=0x40000000;
howManyChips++; howManyChips++;
@ -1196,9 +1246,11 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
case DIV_SYSTEM_YM2151: case DIV_SYSTEM_YM2151:
if (!hasOPM) { if (!hasOPM) {
hasOPM=disCont[i].dispatch->chipClock; hasOPM=disCont[i].dispatch->chipClock;
CHIP_VOL(3,1.0);
willExport[i]=true; willExport[i]=true;
} else if (!(hasOPM&0x40000000)) { } else if (!(hasOPM&0x40000000)) {
isSecond[i]=true; isSecond[i]=true;
CHIP_VOL_SECOND(3,1.0);
willExport[i]=true; willExport[i]=true;
hasOPM|=0x40000000; hasOPM|=0x40000000;
howManyChips++; howManyChips++;
@ -1209,10 +1261,14 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
if (!hasOPN) { if (!hasOPN) {
hasOPN=disCont[i].dispatch->chipClock; hasOPN=disCont[i].dispatch->chipClock;
willExport[i]=true; willExport[i]=true;
CHIP_VOL(6,1.0);
CHIP_VOL(0x86,1.7);
writeDACSamples=true; writeDACSamples=true;
} else if (!(hasOPN&0x40000000)) { } else if (!(hasOPN&0x40000000)) {
isSecond[i]=true; isSecond[i]=true;
willExport[i]=true; willExport[i]=true;
CHIP_VOL_SECOND(6,1.0);
CHIP_VOL_SECOND(0x86,1.7);
hasOPN|=0x40000000; hasOPN|=0x40000000;
howManyChips++; howManyChips++;
} }
@ -1221,10 +1277,14 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
case DIV_SYSTEM_YM2608_EXT: case DIV_SYSTEM_YM2608_EXT:
if (!hasOPNA) { if (!hasOPNA) {
hasOPNA=disCont[i].dispatch->chipClock; hasOPNA=disCont[i].dispatch->chipClock;
CHIP_VOL(7,1.0);
CHIP_VOL(0x87,1.3);
willExport[i]=true; willExport[i]=true;
writeADPCM_OPNA[0]=disCont[i].dispatch; writeADPCM_OPNA[0]=disCont[i].dispatch;
} else if (!(hasOPNA&0x40000000)) { } else if (!(hasOPNA&0x40000000)) {
isSecond[i]=true; isSecond[i]=true;
CHIP_VOL_SECOND(7,1.0);
CHIP_VOL_SECOND(0x87,1.3);
willExport[i]=true; willExport[i]=true;
writeADPCM_OPNA[1]=disCont[i].dispatch; writeADPCM_OPNA[1]=disCont[i].dispatch;
hasOPNA|=0x40000000; hasOPNA|=0x40000000;
@ -1236,9 +1296,11 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
case DIV_SYSTEM_VRC7: case DIV_SYSTEM_VRC7:
if (!hasOPLL) { if (!hasOPLL) {
hasOPLL=disCont[i].dispatch->chipClock; hasOPLL=disCont[i].dispatch->chipClock;
CHIP_VOL(1,1.6);
willExport[i]=true; willExport[i]=true;
} else if (!(hasOPLL&0x40000000)) { } else if (!(hasOPLL&0x40000000)) {
isSecond[i]=true; isSecond[i]=true;
CHIP_VOL_SECOND(1,1.6);
willExport[i]=true; willExport[i]=true;
hasOPLL|=0x40000000; hasOPLL|=0x40000000;
howManyChips++; howManyChips++;
@ -1261,9 +1323,11 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
case DIV_SYSTEM_POKEY: case DIV_SYSTEM_POKEY:
if (!hasPOKEY) { if (!hasPOKEY) {
hasPOKEY=disCont[i].dispatch->chipClock; hasPOKEY=disCont[i].dispatch->chipClock;
CHIP_VOL(30,0.8);
willExport[i]=true; willExport[i]=true;
} else if (!(hasPOKEY&0x40000000)) { } else if (!(hasPOKEY&0x40000000)) {
isSecond[i]=true; isSecond[i]=true;
CHIP_VOL_SECOND(30,0.8);
willExport[i]=true; willExport[i]=true;
hasPOKEY|=0x40000000; hasPOKEY|=0x40000000;
howManyChips++; 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 // However I think it it not necessary because old VGM players will still
// not be able to handle the 64kb sample bank trick // not be able to handle the 64kb sample bank trick
hasQSound=disCont[i].dispatch->chipClock; hasQSound=disCont[i].dispatch->chipClock;
CHIP_VOL(31,1.0);
willExport[i]=true; willExport[i]=true;
writeQSound[0]=disCont[i].dispatch; writeQSound[0]=disCont[i].dispatch;
} else if (!(hasQSound&0x40000000)) { } else if (!(hasQSound&0x40000000)) {
isSecond[i]=true; isSecond[i]=true;
CHIP_VOL_SECOND(31,1.0);
willExport[i]=false; willExport[i]=false;
writeQSound[1]=disCont[i].dispatch; writeQSound[1]=disCont[i].dispatch;
addWarning("dual QSound is not supported by the VGM format"); 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: case DIV_SYSTEM_SWAN:
if (!hasSwan) { if (!hasSwan) {
hasSwan=disCont[i].dispatch->chipClock; hasSwan=disCont[i].dispatch->chipClock;
CHIP_VOL(33,1.0);
willExport[i]=true; willExport[i]=true;
// funny enough, VGM doesn't have support for WSC's sound DMA by design // funny enough, VGM doesn't have support for WSC's sound DMA by design
// so DAC stream it goes // so DAC stream it goes
@ -1305,6 +1372,7 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
writeDACSamples=true; writeDACSamples=true;
} else if (!(hasSwan&0x40000000)) { } else if (!(hasSwan&0x40000000)) {
isSecond[i]=true; isSecond[i]=true;
CHIP_VOL_SECOND(33,1.0);
willExport[i]=true; willExport[i]=true;
hasSwan|=0x40000000; hasSwan|=0x40000000;
howManyChips++; howManyChips++;
@ -1313,9 +1381,11 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
case DIV_SYSTEM_VBOY: case DIV_SYSTEM_VBOY:
if (!hasVSU) { if (!hasVSU) {
hasVSU=disCont[i].dispatch->chipClock; hasVSU=disCont[i].dispatch->chipClock;
CHIP_VOL(34,0.72);
willExport[i]=true; willExport[i]=true;
} else if (!(hasVSU&0x40000000)) { } else if (!(hasVSU&0x40000000)) {
isSecond[i]=true; isSecond[i]=true;
CHIP_VOL_SECOND(34,0.72);
willExport[i]=true; willExport[i]=true;
hasVSU|=0x40000000; hasVSU|=0x40000000;
howManyChips++; howManyChips++;
@ -1325,9 +1395,11 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
case DIV_SYSTEM_OPL_DRUMS: case DIV_SYSTEM_OPL_DRUMS:
if (!hasOPL) { if (!hasOPL) {
hasOPL=disCont[i].dispatch->chipClock; hasOPL=disCont[i].dispatch->chipClock;
CHIP_VOL(9,1.0);
willExport[i]=true; willExport[i]=true;
} else if (!(hasOPL&0x40000000)) { } else if (!(hasOPL&0x40000000)) {
isSecond[i]=true; isSecond[i]=true;
CHIP_VOL_SECOND(9,1.0);
willExport[i]=true; willExport[i]=true;
hasOPL|=0x40000000; hasOPL|=0x40000000;
howManyChips++; howManyChips++;
@ -1337,10 +1409,12 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
case DIV_SYSTEM_Y8950_DRUMS: case DIV_SYSTEM_Y8950_DRUMS:
if (!hasY8950) { if (!hasY8950) {
hasY8950=disCont[i].dispatch->chipClock; hasY8950=disCont[i].dispatch->chipClock;
CHIP_VOL(11,1.0);
willExport[i]=true; willExport[i]=true;
writeADPCM_Y8950[0]=disCont[i].dispatch; writeADPCM_Y8950[0]=disCont[i].dispatch;
} else if (!(hasY8950&0x40000000)) { } else if (!(hasY8950&0x40000000)) {
isSecond[i]=true; isSecond[i]=true;
CHIP_VOL_SECOND(11,1.0);
willExport[i]=true; willExport[i]=true;
writeADPCM_Y8950[1]=disCont[i].dispatch; writeADPCM_Y8950[1]=disCont[i].dispatch;
hasY8950|=0x40000000; hasY8950|=0x40000000;
@ -1351,9 +1425,11 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
case DIV_SYSTEM_OPL2_DRUMS: case DIV_SYSTEM_OPL2_DRUMS:
if (!hasOPL2) { if (!hasOPL2) {
hasOPL2=disCont[i].dispatch->chipClock; hasOPL2=disCont[i].dispatch->chipClock;
CHIP_VOL(10,1.0);
willExport[i]=true; willExport[i]=true;
} else if (!(hasOPL2&0x40000000)) { } else if (!(hasOPL2&0x40000000)) {
isSecond[i]=true; isSecond[i]=true;
CHIP_VOL_SECOND(10,1.0);
willExport[i]=true; willExport[i]=true;
hasOPL2|=0x40000000; hasOPL2|=0x40000000;
howManyChips++; howManyChips++;
@ -1363,9 +1439,11 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
case DIV_SYSTEM_OPL3_DRUMS: case DIV_SYSTEM_OPL3_DRUMS:
if (!hasOPL3) { if (!hasOPL3) {
hasOPL3=disCont[i].dispatch->chipClock; hasOPL3=disCont[i].dispatch->chipClock;
CHIP_VOL(12,1.0);
willExport[i]=true; willExport[i]=true;
} else if (!(hasOPL3&0x40000000)) { } else if (!(hasOPL3&0x40000000)) {
isSecond[i]=true; isSecond[i]=true;
CHIP_VOL_SECOND(12,1.0);
willExport[i]=true; willExport[i]=true;
hasOPL3|=0x40000000; hasOPL3|=0x40000000;
howManyChips++; howManyChips++;
@ -1378,9 +1456,11 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
if (song.system[i]==DIV_SYSTEM_SCC_PLUS) { if (song.system[i]==DIV_SYSTEM_SCC_PLUS) {
hasK051649|=0x80000000; hasK051649|=0x80000000;
} }
CHIP_VOL(25,1.0);
willExport[i]=true; willExport[i]=true;
} else if (!(hasK051649&0x40000000)) { } else if (!(hasK051649&0x40000000)) {
isSecond[i]=true; isSecond[i]=true;
CHIP_VOL_SECOND(25,1.0);
willExport[i]=true; willExport[i]=true;
hasK051649|=0x40000000; hasK051649|=0x40000000;
if (song.system[i]==DIV_SYSTEM_SCC_PLUS) { 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: case DIV_SYSTEM_YMZ280B:
if (!hasZ280) { if (!hasZ280) {
hasZ280=disCont[i].dispatch->chipClock; hasZ280=disCont[i].dispatch->chipClock;
CHIP_VOL(15,0.72);
willExport[i]=true; willExport[i]=true;
writeZ280[0]=disCont[i].dispatch; writeZ280[0]=disCont[i].dispatch;
} else if (!(hasZ280&0x40000000)) { } else if (!(hasZ280&0x40000000)) {
isSecond[i]=true; isSecond[i]=true;
CHIP_VOL_SECOND(15,0.72);
willExport[i]=true; willExport[i]=true;
writeZ280[1]=disCont[i].dispatch; writeZ280[1]=disCont[i].dispatch;
hasZ280|=0x40000000; hasZ280|=0x40000000;
@ -1411,11 +1493,13 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
if (!hasRFC1) { if (!hasRFC1) {
hasRFC1=disCont[i].dispatch->chipClock; hasRFC1=disCont[i].dispatch->chipClock;
isSecond[i]=true; isSecond[i]=true;
CHIP_VOL(16,1.6);
willExport[i]=true; willExport[i]=true;
writeRF5C68[1]=disCont[i].dispatch; writeRF5C68[1]=disCont[i].dispatch;
} }
} else if (!hasRFC) { } else if (!hasRFC) {
hasRFC=disCont[i].dispatch->chipClock; hasRFC=disCont[i].dispatch->chipClock;
CHIP_VOL(5,1.6);
willExport[i]=true; willExport[i]=true;
writeRF5C68[0]=disCont[i].dispatch; writeRF5C68[0]=disCont[i].dispatch;
} }
@ -1423,10 +1507,12 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
case DIV_SYSTEM_MSM6295: case DIV_SYSTEM_MSM6295:
if (!hasOKIM6295) { if (!hasOKIM6295) {
hasOKIM6295=disCont[i].dispatch->chipClock; hasOKIM6295=disCont[i].dispatch->chipClock;
CHIP_VOL(24,1.0);
willExport[i]=true; willExport[i]=true;
writeMSM6295[0]=disCont[i].dispatch; writeMSM6295[0]=disCont[i].dispatch;
} else if (!(hasOKIM6295&0x40000000)) { } else if (!(hasOKIM6295&0x40000000)) {
isSecond[i]=true; isSecond[i]=true;
CHIP_VOL_SECOND(24,1.0);
willExport[i]=true; willExport[i]=true;
writeMSM6295[1]=disCont[i].dispatch; writeMSM6295[1]=disCont[i].dispatch;
hasOKIM6295|=0x40000000; hasOKIM6295|=0x40000000;
@ -1436,10 +1522,12 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
case DIV_SYSTEM_GA20: case DIV_SYSTEM_GA20:
if (!hasGA20) { if (!hasGA20) {
hasGA20=disCont[i].dispatch->chipClock; hasGA20=disCont[i].dispatch->chipClock;
CHIP_VOL(40,0.4);
willExport[i]=true; willExport[i]=true;
writeGA20[0]=disCont[i].dispatch; writeGA20[0]=disCont[i].dispatch;
} else if (!(hasGA20&0x40000000)) { } else if (!(hasGA20&0x40000000)) {
isSecond[i]=true; isSecond[i]=true;
CHIP_VOL_SECOND(40,0.4);
willExport[i]=true; willExport[i]=true;
writeGA20[1]=disCont[i].dispatch; writeGA20[1]=disCont[i].dispatch;
hasGA20|=0x40000000; hasGA20|=0x40000000;
@ -1449,7 +1537,7 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
case DIV_SYSTEM_T6W28: case DIV_SYSTEM_T6W28:
if (!hasSN) { if (!hasSN) {
hasSN=0xc0000000|disCont[i].dispatch->chipClock; hasSN=0xc0000000|disCont[i].dispatch->chipClock;
CHIP_VOL(0,1.0); CHIP_VOL(0,2.0);
snNoiseConfig=3; snNoiseConfig=3;
snNoiseSize=15; snNoiseSize=15;
willExport[i]=true; 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); // OPN
w->writeC(0); // OPNA w->writeC(0); // OPNA
} }
if (version>=0x160) { if (version>=0x160) { // global volume
int calcVolume=32.0*(log(song.masterVol)/log(2.0)); 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<-63) calcVolume=-63;
if (calcVolume>192) calcVolume=192; if (calcVolume>192) calcVolume=192;
w->writeC(calcVolume&0xff); // volume 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<song.systemLen; i++) { for (int i=0; i<song.systemLen; i++) {
std::vector<DivRegWrite>& writes=disCont[i].dispatch->getRegisterWrites(); std::vector<DivRegWrite>& writes=disCont[i].dispatch->getRegisterWrites();
for (DivRegWrite& j: writes) { 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++; writeCount++;
} }
writes.clear(); writes.clear();
@ -2031,7 +2120,7 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
lastOne=i.second.time; lastOne=i.second.time;
} }
// write write // 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++; writeCount++;
} }
sortedWrites.clear(); sortedWrites.clear();
@ -2164,7 +2253,7 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
w->writeI(0); w->writeI(0);
} else { } else {
w->writeI(loopPos-0x1c); w->writeI(loopPos-0x1c);
w->writeI(tickCount-loopTick-1); w->writeI(tickCount-loopTick);
} }
} else { } else {
w->writeI(0); w->writeI(0);

View file

@ -352,6 +352,10 @@ const int detuneUnmap[2][11]={
{0, 0, 0, 3, 4, 5, 6, 7, 2, 1, 0} {0, 0, 0, 3, 4, 5, 6, 7, 2, 1, 0}
}; };
const int kslMap[4]={
0, 2, 1, 3
};
// do not change these! // do not change these!
// anything other than a checkbox will look ugly! // 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) { 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)); P(CWVSliderScalar("##RS",ImVec2(20.0f*dpiScale,sliderHeight),ImGuiDataType_U8,&op.rs,&_ZERO,&_THREE));
} else { } 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) { if (ins->type==DIV_INS_OPZ) {
@ -3226,7 +3234,7 @@ void FurnaceGUI::drawInsEdit() {
break; break;
case DIV_INS_OPL: case DIV_INS_OPL:
case DIV_INS_OPL_DRUMS: case DIV_INS_OPL_DRUMS: {
// waveform // waveform
drawWaveform(op.ws&7,ins->type==DIV_INS_OPZ,ImVec2(waveWidth,waveHeight)); drawWaveform(op.ws&7,ins->type==DIV_INS_OPZ,ImVec2(waveWidth,waveHeight));
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
@ -3268,9 +3276,14 @@ void FurnaceGUI::drawInsEdit() {
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
snprintf(tempID,1024,"%s: %%d",FM_NAME(FM_KSL)); 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; break;
}
case DIV_INS_OPZ: { case DIV_INS_OPZ: {
// waveform // waveform
drawWaveform(op.ws&7,ins->type==DIV_INS_OPZ,ImVec2(waveWidth,waveHeight)); drawWaveform(op.ws&7,ins->type==DIV_INS_OPZ,ImVec2(waveWidth,waveHeight));
@ -3613,7 +3626,11 @@ void FurnaceGUI::drawInsEdit() {
ImGui::TableNextColumn(); ImGui::TableNextColumn();
ImGui::Text("%s",FM_NAME(FM_RS)); ImGui::Text("%s",FM_NAME(FM_RS));
} else { } 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::TableNextColumn();
ImGui::Text("%s",FM_NAME(FM_KSL)); ImGui::Text("%s",FM_NAME(FM_KSL));
} }

View file

@ -24,7 +24,8 @@
#ifdef _WIN32 #ifdef _WIN32
#include <windows.h> #include <windows.h>
typedef HRESULT (*GDFM)(HMONITOR,int,UINT*,UINT*); #include "shellScalingStub.h"
typedef HRESULT (WINAPI *GDFM)(HMONITOR,MONITOR_DPI_TYPE,UINT*,UINT*);
#endif #endif
#ifdef __APPLE__ #ifdef __APPLE__
@ -71,9 +72,9 @@ double getScaleFactor(const char* driverHint) {
return 1.0; return 1.0;
} }
unsigned int dpiX=96; UINT dpiX=96;
unsigned int dpiY=96; UINT dpiY=96;
HRESULT result=ta_GetDpiForMonitor(disp,0,&dpiX,&dpiY); HRESULT result=ta_GetDpiForMonitor(disp,MDT_EFFECTIVE_DPI,&dpiX,&dpiY);
if (result!=S_OK) { if (result!=S_OK) {
logW("GetDpiForMonitor failure (%.8x) - no scaling detection available!",result); logW("GetDpiForMonitor failure (%.8x) - no scaling detection available!",result);

View file

@ -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 */

View file

@ -32,6 +32,10 @@
#include <windows.h> #include <windows.h>
#include <combaseapi.h> #include <combaseapi.h>
#include <shellapi.h> #include <shellapi.h>
#include "gui/shellScalingStub.h"
typedef HRESULT (WINAPI *SPDA)(PROCESS_DPI_AWARENESS);
#else #else
#include <unistd.h> #include <unistd.h>
#endif #endif
@ -339,6 +343,22 @@ void reportError(String what) {
int main(int argc, char** argv) { int main(int argc, char** argv) {
initLog(); initLog();
#ifdef _WIN32 #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); HRESULT coResult=CoInitializeEx(NULL,COINIT_MULTITHREADED);
if (coResult!=S_OK) { if (coResult!=S_OK) {
logE("CoInitializeEx failed!"); logE("CoInitializeEx failed!");