diff --git a/TODO.md b/TODO.md index 5db47ec01..c5e26bd9b 100644 --- a/TODO.md +++ b/TODO.md @@ -37,6 +37,7 @@ - volume commands should work on Game Boy - macro editor menu - refactor sysDef.cpp + - effect/postEffect refactor - add another FM editor layout - try to find out why does VSlider not accept keyboard input - finish lock layout diff --git a/papers/format.md b/papers/format.md index 8d977e84c..feafbe603 100644 --- a/papers/format.md +++ b/papers/format.md @@ -29,6 +29,9 @@ furthermore, an `or reserved` indicates this field is always present, but is res the format versions are: +- 90: Furnace dev90 +- 89: Furnace dev89 +- 88: Furnace dev88 - 87: Furnace dev87 - 86: Furnace dev86 - 85: Furnace dev85 @@ -280,7 +283,8 @@ size | description 1 | new Sega PCM (with macros and proper vol/pan) (>=84) or reserved 1 | weird f-num/block-based chip pitch slides (>=85) or reserved 1 | SN duty macro always resets phase (>=86) or reserved - 20 | reserved + 1 | pitch macro is linear (>=90) or reserved + 19 | reserved ``` # instrument @@ -669,6 +673,8 @@ size | description 1 | parameter 2 1 | parameter 3 1 | parameter 4 + --- | **extra C64 data** (>=89) + 1 | don't test/gate before new note ``` # wavetable diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 2701a4e2c..17183840c 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -140,7 +140,7 @@ void DivEngine::walkSong(int& loopOrder, int& loopRow, int& loopEnd) { for (int j=nextRow; jdata[j][5+(l<<1)]; if (effectVal<0) effectVal=0; if (pat[k]->data[j][4+(l<<1)]==0x0d) { @@ -945,21 +945,28 @@ unsigned short DivEngine::calcBaseFreqFNumBlock(double clock, double divider, in return bf|(block<4095) pitch=4095; - return period? - ((base*(reversePitchTable[pitch]))/whatTheFuck): - (((base*(pitchTable[pitch]))>>10)*whatTheFuck)/1024; + int ret=period? + ((base*(reversePitchTable[pitch]))/whatTheFuck): + (((base*(pitchTable[pitch]))>>10)*whatTheFuck)/1024; + if (!song.pitchMacroIsLinear) { + ret+=period?(-pitch2):pitch2; + } + return ret; } return period? - base-pitch: - base+((pitch*octave)>>1); + base-pitch-pitch2: + base+((pitch*octave)>>1)+pitch2; } void DivEngine::play() { @@ -2103,7 +2110,7 @@ void DivEngine::autoNoteOn(int ch, int ins, int note, int vol) { } do { - if ((ins==-1 || ins>=song.insLen || getChannelType(finalChan)==4 || getPreferInsType(finalChan)==getIns(ins)->type || getIns(ins)->type==DIV_INS_AMIGA) && chan[finalChan].midiNote==-1) { + if ((ins==-1 || ins>=song.insLen || getPreferInsType(finalChan)==getIns(ins)->type || getPreferInsSecondType(finalChan)==getIns(ins)->type) && chan[finalChan].midiNote==-1) { chan[finalChan].midiNote=note; pendingNotes.push(DivNoteEvent(finalChan,ins,note,vol,true)); break; diff --git a/src/engine/engine.h b/src/engine/engine.h index 462ddf9d6..b63693882 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -44,8 +44,8 @@ #define BUSY_BEGIN_SOFT softLocked=true; isBusy.lock(); #define BUSY_END isBusy.unlock(); softLocked=false; -#define DIV_VERSION "dev88" -#define DIV_ENGINE_VERSION 88 +#define DIV_VERSION "dev90" +#define DIV_ENGINE_VERSION 90 // for imports #define DIV_VERSION_MOD 0xff01 @@ -470,7 +470,7 @@ class DivEngine { unsigned short calcBaseFreqFNumBlock(double clock, double divider, int note, int bits); // calculate frequency/period - int calcFreq(int base, int pitch, bool period=false, int octave=0); + int calcFreq(int base, int pitch, bool period=false, int octave=0, int pitch2=0); // find song loop position void walkSong(int& loopOrder, int& loopRow, int& loopEnd); @@ -525,6 +525,9 @@ class DivEngine { // get preferred instrument type DivInstrumentType getPreferInsType(int ch); + // get alternate instrument type + DivInstrumentType getPreferInsSecondType(int ch); + // get song system name const char* getSongSystemName(); diff --git a/src/engine/fileOps.cpp b/src/engine/fileOps.cpp index a50b122fb..33975f41e 100644 --- a/src/engine/fileOps.cpp +++ b/src/engine/fileOps.cpp @@ -637,14 +637,14 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) { for (int i=0; i4 || chan.effectRows<1) { - logE("invalid effect row count %d. are you sure everything is ok?",chan.effectRows); - lastError="file is corrupt or unreadable at effect rows"; + logD("%d fx rows: %d",i,chan.effectCols); + if (chan.effectCols>4 || chan.effectCols<1) { + logE("invalid effect column count %d. are you sure everything is ok?",chan.effectCols); + lastError="file is corrupt or unreadable at effect columns"; delete[] file; return false; } @@ -694,7 +694,7 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) { pat->data[k][3]=(pat->data[k][3]&3)*5; } } - for (int l=0; ldata[k][4+(l<<1)]=reader.readS(); pat->data[k][5+(l<<1)]=reader.readS(); @@ -1011,6 +1011,9 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) { if (ds.version<86) { ds.snDutyReset=true; } + if (ds.version<90) { + ds.pitchMacroIsLinear=false; + } ds.isDMF=false; reader.readS(); // reserved @@ -1291,10 +1294,10 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) { } for (int i=0; i8) { - logE("channel %d has zero or too many effect columns! (%d)",i,ds.pat[i].effectRows); - lastError=fmt::sprintf("channel %d has too many effect columns! (%d)",i,ds.pat[i].effectRows); + ds.pat[i].effectCols=reader.readC(); + if (ds.pat[i].effectCols<1 || ds.pat[i].effectCols>8) { + logE("channel %d has zero or too many effect columns! (%d)",i,ds.pat[i].effectCols); + lastError=fmt::sprintf("channel %d has too many effect columns! (%d)",i,ds.pat[i].effectCols); delete[] file; return false; } @@ -1372,7 +1375,12 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) { } else { reader.readC(); } - for (int i=0; i<20; i++) { + if (ds.version>=90) { + ds.pitchMacroIsLinear=reader.readC(); + } else { + reader.readC(); + } + for (int i=0; i<19; i++) { reader.readC(); } } @@ -1572,7 +1580,7 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) { pat->data[j][1]=reader.readS(); pat->data[j][2]=reader.readS(); pat->data[j][3]=reader.readS(); - for (int k=0; kdata[j][4+(k<<1)]=reader.readS(); pat->data[j][5+(k<<1)]=reader.readS(); } @@ -1973,7 +1981,7 @@ bool DivEngine::loadMod(unsigned char* file, size_t len) { } } } - ds.pat[ch].effectRows=fxCols; + ds.pat[ch].effectCols=fxCols; } ds.pal=false; @@ -1990,7 +1998,7 @@ bool DivEngine::loadMod(unsigned char* file, size_t len) { ds.chanShortName[i]=fmt::sprintf("C%d",i+1); } for(int i=chCount; iwriteC(song.pat[i].effectRows); + w->writeC(song.pat[i].effectCols); } for (int i=0; iwriteC(song.newSegaPCM); w->writeC(song.fbPortaPause); w->writeC(song.snDutyReset); - for (int i=0; i<20; i++) { + w->writeC(song.pitchMacroIsLinear); + for (int i=0; i<19; i++) { w->writeC(0); } @@ -2384,7 +2393,7 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) { w->writeS(pat->data[j][1]); // octave w->writeS(pat->data[j][2]); // instrument w->writeS(pat->data[j][3]); // volume - w->write(&pat->data[j][4],2*song.pat[i>>16].effectRows*2); // effects + w->write(&pat->data[j][4],2*song.pat[i>>16].effectCols*2); // effects } w->writeString(pat->name,false); @@ -2730,7 +2739,7 @@ SafeWriter* DivEngine::saveDMF(unsigned char version) { } for (int i=0; iwriteC(song.pat[i].effectRows); + w->writeC(song.pat[i].effectCols); for (int j=0; jwriteS(pat->data[k][0]); // note w->writeS(pat->data[k][1]); // octave w->writeS(pat->data[k][3]); // volume - w->write(&pat->data[k][4],2*song.pat[i].effectRows*2); // effects + w->write(&pat->data[k][4],2*song.pat[i].effectCols*2); // effects w->writeS(pat->data[k][2]); // instrument } } diff --git a/src/engine/instrument.cpp b/src/engine/instrument.cpp index 818626fd4..fec1a8813 100644 --- a/src/engine/instrument.cpp +++ b/src/engine/instrument.cpp @@ -507,6 +507,9 @@ void DivInstrument::putInsData(SafeWriter* w) { w->writeC(std.ex6Macro.mode); w->writeC(std.ex7Macro.mode); w->writeC(std.ex8Macro.mode); + + // C64 no test + w->writeC(c64.noTest); } DivDataErrors DivInstrument::readInsData(SafeReader& reader, short version) { @@ -1014,6 +1017,11 @@ DivDataErrors DivInstrument::readInsData(SafeReader& reader, short version) { std.ex8Macro.mode=reader.readC(); } + // C64 no test + if (version>=89) { + c64.noTest=reader.readC(); + } + return DIV_DATA_SUCCESS; } diff --git a/src/engine/instrument.h b/src/engine/instrument.h index 63354901f..09efbd6c0 100644 --- a/src/engine/instrument.h +++ b/src/engine/instrument.h @@ -265,7 +265,7 @@ struct DivInstrumentC64 { unsigned char a, d, s, r; unsigned short duty; unsigned char ringMod, oscSync; - bool toFilter, volIsCutoff, initFilter, dutyIsAbs, filterIsAbs; + bool toFilter, volIsCutoff, initFilter, dutyIsAbs, filterIsAbs, noTest; unsigned char res; unsigned short cut; bool hp, lp, bp, ch3off; @@ -287,6 +287,7 @@ struct DivInstrumentC64 { initFilter(false), dutyIsAbs(false), filterIsAbs(false), + noTest(false), res(0), cut(0), hp(false), diff --git a/src/engine/pattern.cpp b/src/engine/pattern.cpp index 8241255b3..4f4663cae 100644 --- a/src/engine/pattern.cpp +++ b/src/engine/pattern.cpp @@ -130,6 +130,6 @@ SafeReader* DivPattern::compile(int len, int fxRows) { } DivChannelData::DivChannelData(): - effectRows(1) { + effectCols(1) { memset(data,0,256*sizeof(void*)); } diff --git a/src/engine/pattern.h b/src/engine/pattern.h index fd0b0d076..4d1070f3b 100644 --- a/src/engine/pattern.h +++ b/src/engine/pattern.h @@ -40,7 +40,7 @@ struct DivPattern { }; struct DivChannelData { - unsigned char effectRows; + unsigned char effectCols; // data goes as follows: data[ROW][TYPE] // TYPE is: // 0: note diff --git a/src/engine/platform/amiga.cpp b/src/engine/platform/amiga.cpp index 55d99af01..ba07269ab 100644 --- a/src/engine/platform/amiga.cpp +++ b/src/engine/platform/amiga.cpp @@ -207,7 +207,7 @@ void DivPlatformAmiga::tick(bool sysTick) { } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { //DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_AMIGA); - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true)+chan[i].std.pitch.val; + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].std.pitch.val); if (chan[i].freq>4095) chan[i].freq=4095; if (chan[i].freq<0) chan[i].freq=0; if (chan[i].keyOn) { diff --git a/src/engine/platform/ay.cpp b/src/engine/platform/ay.cpp index 4ba384832..fc2a36ab6 100644 --- a/src/engine/platform/ay.cpp +++ b/src/engine/platform/ay.cpp @@ -239,7 +239,7 @@ void DivPlatformAY8910::tick(bool sysTick) { if (!chan[i].std.ex3.will) chan[i].autoEnvNum=1; } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true)+chan[i].std.pitch.val; + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].std.pitch.val); if (chan[i].freq>4095) chan[i].freq=4095; if (chan[i].keyOn) { //rWrite(16+i*5+1,((chan[i].duty&3)<<6)|(63-(ins->gb.soundLen&63))); diff --git a/src/engine/platform/ay8930.cpp b/src/engine/platform/ay8930.cpp index 640b495b3..03e99961d 100644 --- a/src/engine/platform/ay8930.cpp +++ b/src/engine/platform/ay8930.cpp @@ -261,7 +261,7 @@ void DivPlatformAY8930::tick(bool sysTick) { immWrite(0x1a,ayNoiseOr); } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true)+chan[i].std.pitch.val; + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].std.pitch.val); if (chan[i].freq>65535) chan[i].freq=65535; if (chan[i].keyOn) { if (chan[i].insChanged) { diff --git a/src/engine/platform/c64.cpp b/src/engine/platform/c64.cpp index 79cfb42ba..3515f0465 100644 --- a/src/engine/platform/c64.cpp +++ b/src/engine/platform/c64.cpp @@ -170,16 +170,17 @@ void DivPlatformC64::tick(bool sysTick) { if (chan[i].testWhen>0) { if (--chan[i].testWhen<1) { if (!chan[i].resetMask && !chan[i].inPorta) { + DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_C64); rWrite(i*7+5,0); rWrite(i*7+6,0); - rWrite(i*7+4,(chan[i].wave<<4)|8|(chan[i].ring<<2)|(chan[i].sync<<1)); + rWrite(i*7+4,(chan[i].wave<<4)|(ins->c64.noTest?0:8)|(chan[i].test<<3)|(chan[i].ring<<2)|(chan[i].sync<<1)); } } } } if (chan[i].std.wave.had) { chan[i].wave=chan[i].std.wave.val; - rWrite(i*7+4,(chan[i].wave<<4)|(chan[i].ring<<2)|(chan[i].sync<<1)|(int)(chan[i].active)); + rWrite(i*7+4,(chan[i].wave<<4)|(chan[i].test<<3)|(chan[i].ring<<2)|(chan[i].sync<<1)|(int)(chan[i].active)); } if (chan[i].std.pitch.had) { chan[i].freqChanged=true; @@ -196,20 +197,25 @@ void DivPlatformC64::tick(bool sysTick) { chan[i].sync=chan[i].std.ex3.val&1; chan[i].ring=chan[i].std.ex3.val&2; chan[i].freqChanged=true; + rWrite(i*7+4,(chan[i].wave<<4)|(chan[i].test<<3)|(chan[i].ring<<2)|(chan[i].sync<<1)|(int)(chan[i].active)); + } + if (chan[i].std.ex4.had) { + chan[i].test=chan[i].std.ex4.val&1; + rWrite(i*7+4,(chan[i].wave<<4)|(chan[i].test<<3)|(chan[i].ring<<2)|(chan[i].sync<<1)|(int)(chan[i].active)); } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,8)+chan[i].std.pitch.val; + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,8,chan[i].std.pitch.val); if (chan[i].freq>0xffff) chan[i].freq=0xffff; if (chan[i].keyOn) { rWrite(i*7+5,(chan[i].attack<<4)|(chan[i].decay)); rWrite(i*7+6,(chan[i].sustain<<4)|(chan[i].release)); - rWrite(i*7+4,(chan[i].wave<<4)|(chan[i].ring<<2)|(chan[i].sync<<1)|1); + rWrite(i*7+4,(chan[i].wave<<4)|(chan[i].test<<3)|(chan[i].ring<<2)|(chan[i].sync<<1)|1); } if (chan[i].keyOff) { rWrite(i*7+5,(chan[i].attack<<4)|(chan[i].decay)); rWrite(i*7+6,(chan[i].sustain<<4)|(chan[i].release)); - rWrite(i*7+4,(chan[i].wave<<4)|(chan[i].ring<<2)|(chan[i].sync<<1)|0); + rWrite(i*7+4,(chan[i].wave<<4)|(chan[i].test<<3)|(chan[i].ring<<2)|(chan[i].sync<<1)|0); } rWrite(i*7,chan[i].freq&0xff); rWrite(i*7+1,chan[i].freq>>8); @@ -231,6 +237,7 @@ int DivPlatformC64::dispatch(DivCommand c) { } chan[c.chan].active=true; chan[c.chan].keyOn=true; + chan[c.chan].test=false; if (chan[c.chan].insChanged || chan[c.chan].resetDuty || ins->std.waveMacro.len>0) { chan[c.chan].duty=ins->c64.duty; rWrite(c.chan*7+2,chan[c.chan].duty&0xff); @@ -335,7 +342,7 @@ int DivPlatformC64::dispatch(DivCommand c) { break; case DIV_CMD_WAVE: chan[c.chan].wave=c.value; - rWrite(c.chan*7+4,(chan[c.chan].wave<<4)|(chan[c.chan].ring<<2)|(chan[c.chan].sync<<1)|(int)(chan[c.chan].active)); + rWrite(c.chan*7+4,(chan[c.chan].wave<<4)|(chan[c.chan].test<<3)|(chan[c.chan].ring<<2)|(chan[c.chan].sync<<1)|(int)(chan[c.chan].active)); break; case DIV_CMD_LEGATO: chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value+((chan[c.chan].std.arp.will && !chan[c.chan].std.arp.mode)?(chan[c.chan].std.arp.val):(0))); @@ -416,11 +423,11 @@ int DivPlatformC64::dispatch(DivCommand c) { break; case 4: chan[c.chan].ring=c.value; - rWrite(c.chan*7+4,(chan[c.chan].wave<<4)|(chan[c.chan].ring<<2)|(chan[c.chan].sync<<1)|(int)(chan[c.chan].active)); + rWrite(c.chan*7+4,(chan[c.chan].wave<<4)|(chan[c.chan].test<<3)|(chan[c.chan].ring<<2)|(chan[c.chan].sync<<1)|(int)(chan[c.chan].active)); break; case 5: chan[c.chan].sync=c.value; - rWrite(c.chan*7+4,(chan[c.chan].wave<<4)|(chan[c.chan].ring<<2)|(chan[c.chan].sync<<1)|(int)(chan[c.chan].active)); + rWrite(c.chan*7+4,(chan[c.chan].wave<<4)|(chan[c.chan].test<<3)|(chan[c.chan].ring<<2)|(chan[c.chan].sync<<1)|(int)(chan[c.chan].active)); break; case 6: filtControl&=7; diff --git a/src/engine/platform/c64.h b/src/engine/platform/c64.h index 8df82051c..bc3cbf98f 100644 --- a/src/engine/platform/c64.h +++ b/src/engine/platform/c64.h @@ -30,7 +30,7 @@ class DivPlatformC64: public DivDispatch { unsigned char sweep, wave, attack, decay, sustain, release; short duty; bool active, insChanged, freqChanged, sweepChanged, keyOn, keyOff, inPorta, filter; - bool resetMask, resetFilter, resetDuty, ring, sync; + bool resetMask, resetFilter, resetDuty, ring, sync, test; signed char vol, outVol; DivMacroInt std; Channel(): @@ -61,6 +61,7 @@ class DivPlatformC64: public DivDispatch { resetDuty(false), ring(false), sync(false), + test(false), vol(15) {} }; Channel chan[3]; diff --git a/src/engine/platform/es5506.cpp b/src/engine/platform/es5506.cpp index ee3f45e87..5659bd49c 100644 --- a/src/engine/platform/es5506.cpp +++ b/src/engine/platform/es5506.cpp @@ -436,7 +436,7 @@ void DivPlatformES5506::tick(bool sysTick) { chan[i].envChanged.changed=0; } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false)+chan[i].std.pitch.val; + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,2,chan[i].std.pitch.val); if (chan[i].freq<0) chan[i].freq=0; if (chan[i].freq>0x1ffff) chan[i].freq=0x1ffff; if (chan[i].keyOn) { diff --git a/src/engine/platform/fds.cpp b/src/engine/platform/fds.cpp index 2c3512137..845e72475 100644 --- a/src/engine/platform/fds.cpp +++ b/src/engine/platform/fds.cpp @@ -177,7 +177,7 @@ void DivPlatformFDS::tick(bool sysTick) { } } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false)+chan[i].std.pitch.val; + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,2,chan[i].std.pitch.val); if (chan[i].freq>4095) chan[i].freq=4095; if (chan[i].freq<0) chan[i].freq=0; if (chan[i].keyOn) { diff --git a/src/engine/platform/gb.cpp b/src/engine/platform/gb.cpp index b9668d58a..ded7b493c 100644 --- a/src/engine/platform/gb.cpp +++ b/src/engine/platform/gb.cpp @@ -227,7 +227,7 @@ void DivPlatformGB::tick(bool sysTick) { if (ntPos>255) ntPos=255; chan[i].freq=noiseTable[ntPos]; } else { - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true)+chan[i].std.pitch.val; + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].std.pitch.val); if (chan[i].freq>2047) chan[i].freq=2047; if (chan[i].freq<0) chan[i].freq=0; } diff --git a/src/engine/platform/genesis.cpp b/src/engine/platform/genesis.cpp index b6d020c27..e4642b32a 100644 --- a/src/engine/platform/genesis.cpp +++ b/src/engine/platform/genesis.cpp @@ -412,7 +412,7 @@ void DivPlatformGenesis::tick(bool sysTick) { for (int i=0; i<6; i++) { if (i==2 && extMode) continue; if (chan[i].freqChanged) { - int fNum=parent->calcFreq(chan[i].baseFreq&0x7ff,chan[i].pitch,false,4)+chan[i].std.pitch.val; + int fNum=parent->calcFreq(chan[i].baseFreq&0x7ff,chan[i].pitch,false,4,chan[i].std.pitch.val); int block=(chan[i].baseFreq&0xf800)>>11; if (fNum<0) fNum=0; if (fNum>2047) { diff --git a/src/engine/platform/genesisext.cpp b/src/engine/platform/genesisext.cpp index 59b77587b..d77857a66 100644 --- a/src/engine/platform/genesisext.cpp +++ b/src/engine/platform/genesisext.cpp @@ -307,7 +307,7 @@ void DivPlatformGenesisExt::tick(bool sysTick) { unsigned char writeMask=2; if (extMode) for (int i=0; i<4; i++) { if (opChan[i].freqChanged) { - int fNum=parent->calcFreq(opChan[i].baseFreq&0x7ff,opChan[i].pitch,false,4)+opChan[i].std.pitch.val; + int fNum=parent->calcFreq(opChan[i].baseFreq&0x7ff,opChan[i].pitch,false,4,opChan[i].std.pitch.val); int block=(opChan[i].baseFreq&0xf800)>>11; if (fNum<0) fNum=0; if (fNum>2047) { diff --git a/src/engine/platform/lynx.cpp b/src/engine/platform/lynx.cpp index 4c688103c..f70198b6e 100644 --- a/src/engine/platform/lynx.cpp +++ b/src/engine/platform/lynx.cpp @@ -195,7 +195,7 @@ void DivPlatformLynx::tick(bool sysTick) { WRITE_OTHER(i, ((chan[i].lfsr&0xf00)>>4)); chan[i].lfsr=-1; } - chan[i].fd=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true)+chan[i].std.pitch.val; + chan[i].fd=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].std.pitch.val); if (chan[i].std.duty.had) { chan[i].duty=chan[i].std.duty.val; WRITE_FEEDBACK(i, chan[i].duty.feedback); diff --git a/src/engine/platform/mmc5.cpp b/src/engine/platform/mmc5.cpp index 8a8c3e8ab..4e70b388a 100644 --- a/src/engine/platform/mmc5.cpp +++ b/src/engine/platform/mmc5.cpp @@ -134,7 +134,7 @@ void DivPlatformMMC5::tick(bool sysTick) { } } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true)-1+chan[i].std.pitch.val; + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].std.pitch.val)-1; if (chan[i].freq>2047) chan[i].freq=2047; if (chan[i].freq<0) chan[i].freq=0; if (chan[i].keyOn) { diff --git a/src/engine/platform/n163.cpp b/src/engine/platform/n163.cpp index a00d4d05d..34515305e 100644 --- a/src/engine/platform/n163.cpp +++ b/src/engine/platform/n163.cpp @@ -347,7 +347,7 @@ void DivPlatformN163::tick(bool sysTick) { chan[i].waveUpdated=false; } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { - chan[i].freq=parent->calcFreq((((chan[i].baseFreq*chan[i].waveLen)*(chanMax+1))/16),chan[i].pitch,false,0)+chan[i].std.pitch.val; + chan[i].freq=parent->calcFreq((((chan[i].baseFreq*chan[i].waveLen)*(chanMax+1))/16),chan[i].pitch,false,0,chan[i].std.pitch.val); if (chan[i].freq<0) chan[i].freq=0; if (chan[i].freq>0x3ffff) chan[i].freq=0x3ffff; if (chan[i].keyOn) { diff --git a/src/engine/platform/nes.cpp b/src/engine/platform/nes.cpp index 475c35be5..ddface2f0 100644 --- a/src/engine/platform/nes.cpp +++ b/src/engine/platform/nes.cpp @@ -109,7 +109,7 @@ void DivPlatformNES::acquire(short* bufL, short* bufR, size_t start, size_t len) if (nes->apu.clocked) { nes->apu.clocked=false; } - int sample=(pulse_output(nes)+tnd_output(nes)); + int sample=(pulse_output(nes)+tnd_output(nes))<<6; if (sample>32767) sample=32767; if (sample<-32768) sample=-32768; bufL[i]=sample; @@ -218,7 +218,7 @@ void DivPlatformNES::tick(bool sysTick) { if (ntPos>252) ntPos=252; chan[i].freq=(parent->song.properNoiseLayout)?(15-(chan[i].baseFreq&15)):(noiseTable[ntPos]); } else { - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true)-1+chan[i].std.pitch.val; + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].std.pitch.val)-1; if (chan[i].freq>2047) chan[i].freq=2047; if (chan[i].freq<0) chan[i].freq=0; } @@ -472,7 +472,7 @@ int DivPlatformNES::getRegisterPoolSize() { } float DivPlatformNES::getPostAmp() { - return 128.0f; + return 2.0f; } void DivPlatformNES::reset() { diff --git a/src/engine/platform/opl.cpp b/src/engine/platform/opl.cpp index fac157da3..1e727fa66 100644 --- a/src/engine/platform/opl.cpp +++ b/src/engine/platform/opl.cpp @@ -417,7 +417,7 @@ void DivPlatformOPL::tick(bool sysTick) { bool updateDrums=false; for (int i=0; icalcFreq(chan[i].baseFreq,chan[i].pitch,false,octave(chan[i].baseFreq)); + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,octave(chan[i].baseFreq),chan[i].std.pitch.val); if (chan[i].freq>131071) chan[i].freq=131071; int freqt=toFreq(chan[i].freq)+chan[i].std.pitch.val; chan[i].freqH=freqt>>8; diff --git a/src/engine/platform/opll.cpp b/src/engine/platform/opll.cpp index a02bc8de2..c2e1342bd 100644 --- a/src/engine/platform/opll.cpp +++ b/src/engine/platform/opll.cpp @@ -261,7 +261,7 @@ void DivPlatformOPLL::tick(bool sysTick) { for (int i=0; i<11; i++) { if (chan[i].freqChanged) { - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,octave(chan[i].baseFreq)); + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,octave(chan[i].baseFreq),chan[i].std.pitch.val); if (chan[i].freq>262143) chan[i].freq=262143; int freqt=toFreq(chan[i].freq)+chan[i].std.pitch.val; chan[i].freqL=freqt&0xff; diff --git a/src/engine/platform/pce.cpp b/src/engine/platform/pce.cpp index 7762b3911..08cb77390 100644 --- a/src/engine/platform/pce.cpp +++ b/src/engine/platform/pce.cpp @@ -217,7 +217,7 @@ void DivPlatformPCE::tick(bool sysTick) { } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { //DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_PCE); - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true)+chan[i].std.pitch.val; + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].std.pitch.val); if (chan[i].furnaceDac) { double off=1.0; if (chan[i].dacSample>=0 && chan[i].dacSamplesong.sampleLen) { diff --git a/src/engine/platform/pcspkr.cpp b/src/engine/platform/pcspkr.cpp index 8a00c5638..1a4b2ee06 100644 --- a/src/engine/platform/pcspkr.cpp +++ b/src/engine/platform/pcspkr.cpp @@ -193,7 +193,7 @@ void DivPlatformPCSpeaker::tick(bool sysTick) { chan[i].freqChanged=true; } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true)-1+chan[i].std.pitch.val; + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].std.pitch.val)-1; if (chan[i].freq<0) chan[i].freq=0; if (chan[i].freq>65535) chan[i].freq=65535; if (chan[i].keyOn) { diff --git a/src/engine/platform/pet.cpp b/src/engine/platform/pet.cpp index 0e83e9d21..eb954a6e1 100644 --- a/src/engine/platform/pet.cpp +++ b/src/engine/platform/pet.cpp @@ -116,7 +116,7 @@ void DivPlatformPET::tick(bool sysTick) { chan.freqChanged=true; } if (chan.freqChanged || chan.keyOn || chan.keyOff) { - chan.freq=parent->calcFreq(chan.baseFreq,chan.pitch,true)+chan.std.pitch.val; + chan.freq=parent->calcFreq(chan.baseFreq,chan.pitch,true,0,chan.std.pitch.val); if (chan.freq>257) chan.freq=257; if (chan.freq<2) chan.freq=2; rWrite(8,chan.freq-2); diff --git a/src/engine/platform/qsound.cpp b/src/engine/platform/qsound.cpp index f54e82d94..179e1317d 100644 --- a/src/engine/platform/qsound.cpp +++ b/src/engine/platform/qsound.cpp @@ -331,7 +331,7 @@ void DivPlatformQSound::tick(bool sysTick) { } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { //DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_AMIGA); - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false)+chan[i].std.pitch.val; + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,2,chan[i].std.pitch.val); if (chan[i].freq>0xffff) chan[i].freq=0xffff; if (chan[i].keyOn) { rWrite(q1_reg_map[Q1V_BANK][i], qsound_bank); diff --git a/src/engine/platform/saa.cpp b/src/engine/platform/saa.cpp index f21c7d5b4..2d4986b58 100644 --- a/src/engine/platform/saa.cpp +++ b/src/engine/platform/saa.cpp @@ -194,7 +194,7 @@ void DivPlatformSAA1099::tick(bool sysTick) { rWrite(0x18+(i/3),saaEnv[i/3]); } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true)+chan[i].std.pitch.val; + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].std.pitch.val); if (chan[i].freq>65535) chan[i].freq=65535; if (chan[i].freq>=32768) { chan[i].freqH=7; diff --git a/src/engine/platform/sms.cpp b/src/engine/platform/sms.cpp index c32548b56..faf5ba7d6 100644 --- a/src/engine/platform/sms.cpp +++ b/src/engine/platform/sms.cpp @@ -106,7 +106,7 @@ void DivPlatformSMS::tick(bool sysTick) { } for (int i=0; i<3; i++) { if (chan[i].freqChanged) { - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true)+chan[i].std.pitch.val; + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].std.pitch.val); if (chan[i].freq>1023) chan[i].freq=1023; if (chan[i].freq<8) chan[i].freq=1; //if (chan[i].actualNote>0x5d) chan[i].freq=0x01; @@ -121,7 +121,7 @@ void DivPlatformSMS::tick(bool sysTick) { } } if (chan[3].freqChanged || updateSNMode) { - chan[3].freq=parent->calcFreq(chan[3].baseFreq,chan[3].pitch,true)+chan[3].std.pitch.val; + chan[3].freq=parent->calcFreq(chan[3].baseFreq,chan[3].pitch,true,0,chan[3].std.pitch.val); if (chan[3].freq>1023) chan[3].freq=1023; if (chan[3].actualNote>0x5d) chan[3].freq=0x01; if (snNoiseMode&2) { // take period from channel 3 diff --git a/src/engine/platform/sound/su.cpp b/src/engine/platform/sound/su.cpp index 69b02a781..a9e6d2430 100644 --- a/src/engine/platform/sound/su.cpp +++ b/src/engine/platform/sound/su.cpp @@ -33,6 +33,10 @@ void SoundUnit::NextSample(short* l, short* r) { ns[i]=((((cycle[i]>>15)&127)>chan[i].duty)*127)^(short)SCtriangle[(cycle[i]>>14)&255]; break; } + + if (chan[i].flags.ring) { + ns[i]=(ns[i]*ns[(i+1)&7])>>7; + } if (chan[i].flags.pcm) { if (chan[i].freq>0x8000) { diff --git a/src/engine/platform/su.cpp b/src/engine/platform/su.cpp index f98b19947..1f47a1629 100644 --- a/src/engine/platform/su.cpp +++ b/src/engine/platform/su.cpp @@ -32,6 +32,62 @@ const char** DivPlatformSoundUnit::getRegisterSheet() { } const char* DivPlatformSoundUnit::getEffectName(unsigned char effect) { + switch (effect) { + case 0x10: + return "10xx: Set waveform (0 to 7)"; + break; + case 0x12: + return "12xx: Set pulse width (0 to 7F)"; + break; + case 0x13: + return "13xx: Set resonance (0 to F)"; + break; + case 0x14: + return "14xx: Set filter mode (bit 0: ring mod; bit 1: low pass; bit 2: band pass; bit 3: high pass)"; + break; + case 0x15: + return "15xx: Set frequency sweep period low byte"; + break; + case 0x16: + return "16xx: Set frequency sweep period high byte"; + break; + case 0x17: + return "17xx: Set volume sweep period low byte"; + break; + case 0x18: + return "18xx: Set volume sweep period high byte"; + break; + case 0x19: + return "19xx: Set cutoff sweep period low byte"; + break; + case 0x1a: + return "1Axx: Set cutoff sweep period high byte"; + break; + case 0x1b: + return "1Bxx: Set frequency sweep boundary"; + break; + case 0x1c: + return "1Cxx: Set volume sweep boundary"; + break; + case 0x1d: + return "1Dxx: Set cutoff sweep boundary"; + break; + case 0x20: + return "20xx: Toggle frequency sweep (bit 0-6: speed; bit 7: direction is up)"; + break; + case 0x21: + return "21xx: Toggle volume sweep (bit 0-4: speed; bit 5: direciton is up; bit 6: loop; bit 7: alternate)"; + break; + case 0x22: + return "22xx: Toggle cutoff sweep (bit 0-6: speed; bit 7: direction is up)"; + break; + case 0x40: case 0x41: case 0x42: case 0x43: + case 0x44: case 0x45: case 0x46: case 0x47: + case 0x48: case 0x49: case 0x4a: case 0x4b: + case 0x4c: case 0x4d: case 0x4e: case 0x4f: + return "4xxx: Set cutoff (0 to FFF)"; + break; + } return NULL; } @@ -50,6 +106,12 @@ void DivPlatformSoundUnit::writeControl(int ch) { chWrite(ch,0x04,(chan[ch].wave&7)|(chan[ch].pcm<<3)|(chan[ch].control<<4)); } +void DivPlatformSoundUnit::writeControlUpper(int ch) { + chWrite(ch,0x05,((int)chan[ch].phaseReset)|(chan[ch].filterPhaseReset<<1)|(chan[ch].pcmLoop<<2)|(chan[ch].timerSync<<3)|(chan[ch].freqSweep<<4)|(chan[ch].volSweep<<5)|(chan[ch].cutSweep<<6)); + chan[ch].phaseReset=false; + chan[ch].filterPhaseReset=false; +} + void DivPlatformSoundUnit::tick(bool sysTick) { for (int i=0; i<8; i++) { chan[i].std.next(); @@ -80,6 +142,10 @@ void DivPlatformSoundUnit::tick(bool sysTick) { chan[i].wave=chan[i].std.wave.val&7; writeControl(i); } + if (chan[i].std.phaseReset.had) { + chan[i].phaseReset=chan[i].std.phaseReset.val; + writeControlUpper(i); + } if (chan[i].std.panL.had) { chan[i].pan=chan[i].std.panL.val; chWrite(i,0x03,chan[i].pan); @@ -88,7 +154,7 @@ void DivPlatformSoundUnit::tick(bool sysTick) { chan[i].freqChanged=true; } if (chan[i].std.ex1.had) { - chan[i].cutoff=chan[i].std.ex1.val; + chan[i].cutoff=chan[i].std.ex1.val&16383; chWrite(i,0x06,chan[i].cutoff&0xff); chWrite(i,0x07,chan[i].cutoff>>8); } @@ -102,7 +168,7 @@ void DivPlatformSoundUnit::tick(bool sysTick) { } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { //DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_SU); - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false)+chan[i].std.pitch.val; + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,2,chan[i].std.pitch.val); chWrite(i,0x00,chan[i].freq&0xff); chWrite(i,0x01,chan[i].freq>>8); if (chan[i].freq>65535) chan[i].freq=65535; diff --git a/src/engine/platform/su.h b/src/engine/platform/su.h index 5baf13f08..6291d4531 100644 --- a/src/engine/platform/su.h +++ b/src/engine/platform/su.h @@ -31,7 +31,8 @@ class DivPlatformSoundUnit: public DivDispatch { int ins, cutoff, res, control; signed char pan; unsigned char duty; - bool active, insChanged, freqChanged, keyOn, keyOff, inPorta, noise, pcm; + bool active, insChanged, freqChanged, keyOn, keyOff, inPorta, noise, pcm, phaseReset, filterPhaseReset; + bool pcmLoop, timerSync, freqSweep, volSweep, cutSweep; signed char vol, outVol, wave; DivMacroInt std; Channel(): @@ -53,9 +54,16 @@ class DivPlatformSoundUnit: public DivDispatch { inPorta(false), noise(false), pcm(false), + phaseReset(false), + filterPhaseReset(false), + pcmLoop(false), + timerSync(false), + freqSweep(false), + volSweep(false), + cutSweep(false), vol(127), outVol(127), - wave(-1) {} + wave(0) {} }; Channel chan[8]; bool isMuted[8]; @@ -74,6 +82,7 @@ class DivPlatformSoundUnit: public DivDispatch { SoundUnit* su; unsigned char regPool[128]; void writeControl(int ch); + void writeControlUpper(int ch); friend void putDispatchChan(void*,int,int); public: diff --git a/src/engine/platform/swan.cpp b/src/engine/platform/swan.cpp index cbcbcfb98..9be2883e2 100644 --- a/src/engine/platform/swan.cpp +++ b/src/engine/platform/swan.cpp @@ -194,7 +194,7 @@ void DivPlatformSwan::tick(bool sysTick) { } } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true)+chan[i].std.pitch.val; + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].std.pitch.val); if (i==1 && pcm && furnaceDac) { double off=1.0; if (dacSample>=0 && dacSamplesong.sampleLen) { diff --git a/src/engine/platform/vera.cpp b/src/engine/platform/vera.cpp index c1b578d29..d579be6ca 100644 --- a/src/engine/platform/vera.cpp +++ b/src/engine/platform/vera.cpp @@ -194,7 +194,7 @@ void DivPlatformVERA::tick(bool sysTick) { chan[i].freqChanged=true; } if (chan[i].freqChanged) { - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,8)+chan[i].std.pitch.val; + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,8,chan[i].std.pitch.val); if (chan[i].freq>65535) chan[i].freq=65535; rWrite(i,0,chan[i].freq&0xff); rWrite(i,1,(chan[i].freq>>8)&0xff); diff --git a/src/engine/platform/vic20.cpp b/src/engine/platform/vic20.cpp index aa787672b..efeb0a206 100644 --- a/src/engine/platform/vic20.cpp +++ b/src/engine/platform/vic20.cpp @@ -123,7 +123,7 @@ void DivPlatformVIC20::tick(bool sysTick) { chan[i].freqChanged=true; } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true)+chan[i].std.pitch.val; + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].std.pitch.val); if (i<3) { chan[i].freq>>=(2-i); } else { diff --git a/src/engine/platform/vrc6.cpp b/src/engine/platform/vrc6.cpp index 6362c0cdc..0d25a9fc9 100644 --- a/src/engine/platform/vrc6.cpp +++ b/src/engine/platform/vrc6.cpp @@ -183,9 +183,9 @@ void DivPlatformVRC6::tick(bool sysTick) { } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { if (i==2) { // sawtooth - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true)-1+chan[i].std.pitch.val; + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].std.pitch.val)-1; } else { // pulse - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true)-1+chan[i].std.pitch.val; + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].std.pitch.val)-1; if (chan[i].furnaceDac) { double off=1.0; if (chan[i].dacSample>=0 && chan[i].dacSamplesong.sampleLen) { diff --git a/src/engine/platform/x1_010.cpp b/src/engine/platform/x1_010.cpp index f1498709f..46db65622 100644 --- a/src/engine/platform/x1_010.cpp +++ b/src/engine/platform/x1_010.cpp @@ -476,7 +476,7 @@ void DivPlatformX1_010::tick(bool sysTick) { chan[i].envChanged=false; } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false)+chan[i].std.pitch.val; + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,2,chan[i].std.pitch.val); if (chan[i].pcm) { if (chan[i].freq<1) chan[i].freq=1; if (chan[i].freq>255) chan[i].freq=255; diff --git a/src/engine/platform/ym2610.cpp b/src/engine/platform/ym2610.cpp index 27d272410..96d43f64a 100644 --- a/src/engine/platform/ym2610.cpp +++ b/src/engine/platform/ym2610.cpp @@ -572,7 +572,7 @@ void DivPlatformYM2610::tick(bool sysTick) { for (int i=0; i<4; i++) { if (i==1 && extMode) continue; if (chan[i].freqChanged) { - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,octave(chan[i].baseFreq)); + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,octave(chan[i].baseFreq),chan[i].std.pitch.val); if (chan[i].freq>262143) chan[i].freq=262143; int freqt=toFreq(chan[i].freq)+chan[i].std.pitch.val; immWrite(chanOffs[i]+ADDR_FREQH,freqt>>8); diff --git a/src/engine/platform/ym2610b.cpp b/src/engine/platform/ym2610b.cpp index 01f4c7cc1..316957fb5 100644 --- a/src/engine/platform/ym2610b.cpp +++ b/src/engine/platform/ym2610b.cpp @@ -635,7 +635,7 @@ void DivPlatformYM2610B::tick(bool sysTick) { for (int i=0; i<6; i++) { if (i==2 && extMode) continue; if (chan[i].freqChanged) { - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,octave(chan[i].baseFreq)); + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,octave(chan[i].baseFreq),chan[i].std.pitch.val); if (chan[i].freq>262143) chan[i].freq=262143; int freqt=toFreq(chan[i].freq)+chan[i].std.pitch.val; immWrite(chanOffs[i]+ADDR_FREQH,freqt>>8); diff --git a/src/engine/playback.cpp b/src/engine/playback.cpp index fef3b4501..cb7e823e1 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -955,7 +955,7 @@ void DivEngine::processRow(int i, bool afterDelay) { int whatRow=afterDelay?chan[i].delayRow:curRow; DivPattern* pat=song.pat[i].getPattern(song.orders.ord[i][whatOrder],false); // pre effects - if (!afterDelay) for (int j=0; jdata[whatRow][4+(j<<1)]; short effectVal=pat->data[whatRow][5+(j<<1)]; @@ -1064,7 +1064,7 @@ void DivEngine::processRow(int i, bool afterDelay) { bool calledPorta=false; // effects - for (int j=0; jdata[whatRow][4+(j<<1)]; short effectVal=pat->data[whatRow][5+(j<<1)]; @@ -1384,7 +1384,7 @@ void DivEngine::processRow(int i, bool afterDelay) { chan[i].noteOnInhibit=false; // post effects - for (int j=0; jdata[whatRow][4+(j<<1)]; short effectVal=pat->data[whatRow][5+(j<<1)]; @@ -1421,7 +1421,7 @@ void DivEngine::nextRow() { snprintf(pb2,4095,"\x1b[0;36m%.2x",pat->data[curRow][2]); strcat(pb3,pb2); } - for (int j=0; jdata[curRow][4+(j<<1)]==-1) { strcat(pb3,"\x1b[m--"); } else { @@ -1495,7 +1495,7 @@ void DivEngine::nextRow() { if (song.oneTickCut) { bool doPrepareCut=true; - for (int j=0; jdata[curRow][4+(j<<1)]==0x03) { doPrepareCut=false; break; diff --git a/src/engine/song.h b/src/engine/song.h index 317ffa9b0..087d8c7b0 100644 --- a/src/engine/song.h +++ b/src/engine/song.h @@ -336,6 +336,7 @@ struct DivSong { bool newSegaPCM; bool fbPortaPause; bool snDutyReset; + bool pitchMacroIsLinear; DivOrders orders; std::vector ins; @@ -441,7 +442,8 @@ struct DivSong { e1e2AlsoTakePriority(false), newSegaPCM(true), fbPortaPause(false), - snDutyReset(false) { + snDutyReset(false), + pitchMacroIsLinear(true) { for (int i=0; i<32; i++) { system[i]=DIV_SYSTEM_NULL; systemVol[i]=64; diff --git a/src/engine/sysDef.cpp b/src/engine/sysDef.cpp index bbf5db2d2..082eb0833 100644 --- a/src/engine/sysDef.cpp +++ b/src/engine/sysDef.cpp @@ -305,6 +305,12 @@ DivInstrumentType DivEngine::getPreferInsType(int chan) { return sysDefs[sysOfChan[chan]]->chanInsType[dispatchChanOfChan[chan]][0]; } +DivInstrumentType DivEngine::getPreferInsSecondType(int chan) { + if (chan<0 || chan>chans) return DIV_INS_NULL; + if (sysDefs[sysOfChan[chan]]==NULL) return DIV_INS_NULL; + return sysDefs[sysOfChan[chan]]->chanInsType[dispatchChanOfChan[chan]][1]; +} + int DivEngine::minVGMVersion(DivSystem which) { switch (which) { case DIV_SYSTEM_YM2612: @@ -365,10 +371,10 @@ void DivEngine::registerSystems() { sysDefs[DIV_SYSTEM_YMU759]=new DivSysDef( "Yamaha YMU759", NULL, 0x01, 0x01, 17, true, false, 0, false, - {"Channel 1", "Channel 2", "Channel 3", "Channel 4", "Channel 5", "Channel 6", "Channel 7", "Channel 8", "Channel 9", "Channel 10", "Channel 11", "Channel 12", "Channel 13", "Channel 14", "Channel 15", "Channel 16", "PCM" }, // name - {"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "PCM" }, // short - {DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_PCM}, // type - {DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM} // ins + {"Channel 1", "Channel 2", "Channel 3", "Channel 4", "Channel 5", "Channel 6", "Channel 7", "Channel 8", "Channel 9", "Channel 10", "Channel 11", "Channel 12", "Channel 13", "Channel 14", "Channel 15", "Channel 16", "PCM" }, // name + {"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "PCM" }, // short + {DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_PCM }, // type + {DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_AMIGA} // ins ); sysDefs[DIV_SYSTEM_GENESIS]=new DivSysDef( diff --git a/src/gui/compatFlags.cpp b/src/gui/compatFlags.cpp index ccaeeb3fe..52f465027 100644 --- a/src/gui/compatFlags.cpp +++ b/src/gui/compatFlags.cpp @@ -121,6 +121,10 @@ void FurnaceGUI::drawCompatFlags() { if (ImGui::IsItemHovered()) { ImGui::SetTooltip("when enabled, duty macro will always reset phase, even if its value hasn't changed."); } + ImGui::Checkbox("Pitch macro is linear",&e->song.pitchMacroIsLinear); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("when enabled, the pitch macro of an instrument is in linear space."); + } ImGui::Text("Loop modality:"); if (ImGui::RadioButton("Reset channels",e->song.loopModality==0)) { diff --git a/src/gui/cursor.cpp b/src/gui/cursor.cpp index 53652ec70..1fdf64d77 100644 --- a/src/gui/cursor.cpp +++ b/src/gui/cursor.cpp @@ -87,7 +87,7 @@ void FurnaceGUI::finishSelection() { selStart.xFine=0; } if (e->song.chanCollapse[selEnd.xCoarse]) { - selEnd.xFine=2+e->song.pat[cursor.xCoarse].effectRows*2; + selEnd.xFine=2+e->song.pat[cursor.xCoarse].effectCols*2; } e->setMidiBaseChan(cursor.xCoarse); @@ -105,7 +105,7 @@ void FurnaceGUI::moveCursor(int x, int y, bool select) { demandScrollX=true; if (x>0) { for (int i=0; i=(e->song.chanCollapse[cursor.xCoarse]?1:(3+e->song.pat[cursor.xCoarse].effectRows*2))) { + if (++cursor.xFine>=(e->song.chanCollapse[cursor.xCoarse]?1:(3+e->song.pat[cursor.xCoarse].effectCols*2))) { cursor.xFine=0; if (++cursor.xCoarse>=lastChannel) { if (settings.wrapHorizontal!=0 && !select) { @@ -113,7 +113,7 @@ void FurnaceGUI::moveCursor(int x, int y, bool select) { if (settings.wrapHorizontal==2) y++; } else { cursor.xCoarse=lastChannel-1; - cursor.xFine=e->song.chanCollapse[cursor.xCoarse]?0:(2+e->song.pat[cursor.xCoarse].effectRows*2); + cursor.xFine=e->song.chanCollapse[cursor.xCoarse]?0:(2+e->song.pat[cursor.xCoarse].effectCols*2); } } else { while (!e->song.chanShow[cursor.xCoarse]) { @@ -129,7 +129,7 @@ void FurnaceGUI::moveCursor(int x, int y, bool select) { if (--cursor.xCoarsesong.pat[cursor.xCoarse].effectRows*2; + cursor.xFine=2+e->song.pat[cursor.xCoarse].effectCols*2; if (settings.wrapHorizontal==2) y--; } else { cursor.xCoarse=firstChannel; @@ -143,7 +143,7 @@ void FurnaceGUI::moveCursor(int x, int y, bool select) { if (e->song.chanCollapse[cursor.xCoarse]) { cursor.xFine=0; } else { - cursor.xFine=2+e->song.pat[cursor.xCoarse].effectRows*2; + cursor.xFine=2+e->song.pat[cursor.xCoarse].effectCols*2; } } } @@ -271,7 +271,7 @@ void FurnaceGUI::moveCursorBottom(bool select) { DETERMINE_LAST; cursor.xCoarse=lastChannel-1; if (cursor.xCoarse<0) cursor.xCoarse=0; - cursor.xFine=2+e->song.pat[cursor.xCoarse].effectRows*2; + cursor.xFine=2+e->song.pat[cursor.xCoarse].effectCols*2; demandScrollX=true; } else { cursor.y=e->song.patLen-1; diff --git a/src/gui/doAction.cpp b/src/gui/doAction.cpp index 49fb43609..14c760bdb 100644 --- a/src/gui/doAction.cpp +++ b/src/gui/doAction.cpp @@ -462,13 +462,13 @@ void FurnaceGUI::doAction(int what) { break; case GUI_ACTION_PAT_INCREASE_COLUMNS: if (cursor.xCoarse<0 || cursor.xCoarse>=e->getTotalChannelCount()) break; - e->song.pat[cursor.xCoarse].effectRows++; - if (e->song.pat[cursor.xCoarse].effectRows>8) e->song.pat[cursor.xCoarse].effectRows=8; + e->song.pat[cursor.xCoarse].effectCols++; + if (e->song.pat[cursor.xCoarse].effectCols>8) e->song.pat[cursor.xCoarse].effectCols=8; break; case GUI_ACTION_PAT_DECREASE_COLUMNS: if (cursor.xCoarse<0 || cursor.xCoarse>=e->getTotalChannelCount()) break; - e->song.pat[cursor.xCoarse].effectRows--; - if (e->song.pat[cursor.xCoarse].effectRows<1) e->song.pat[cursor.xCoarse].effectRows=1; + e->song.pat[cursor.xCoarse].effectCols--; + if (e->song.pat[cursor.xCoarse].effectCols<1) e->song.pat[cursor.xCoarse].effectCols=1; break; case GUI_ACTION_PAT_INTERPOLATE: doInterpolate(); diff --git a/src/gui/editing.cpp b/src/gui/editing.cpp index e5874f91a..8832b7fbb 100644 --- a/src/gui/editing.cpp +++ b/src/gui/editing.cpp @@ -137,12 +137,12 @@ void FurnaceGUI::makeUndo(ActionType action) { void FurnaceGUI::doSelectAll() { finishSelection(); curNibble=false; - if (selStart.xFine==0 && selEnd.xFine==2+e->song.pat[selEnd.xCoarse].effectRows*2) { + if (selStart.xFine==0 && selEnd.xFine==2+e->song.pat[selEnd.xCoarse].effectCols*2) { if (selStart.y==0 && selEnd.y==e->song.patLen-1) { // select entire pattern selStart.xCoarse=0; selStart.xFine=0; selEnd.xCoarse=e->getTotalChannelCount()-1; - selEnd.xFine=2+e->song.pat[selEnd.xCoarse].effectRows*2; + selEnd.xFine=2+e->song.pat[selEnd.xCoarse].effectCols*2; } else { // select entire column selStart.y=0; selEnd.y=e->song.patLen-1; @@ -153,14 +153,14 @@ void FurnaceGUI::doSelectAll() { // find row position for (SelectionPoint i; i.xCoarse!=selStart.xCoarse || i.xFine!=selStart.xFine; selStartX++) { i.xFine++; - if (i.xFine>=3+e->song.pat[i.xCoarse].effectRows*2) { + if (i.xFine>=3+e->song.pat[i.xCoarse].effectCols*2) { i.xFine=0; i.xCoarse++; } } for (SelectionPoint i; i.xCoarse!=selEnd.xCoarse || i.xFine!=selEnd.xFine; selEndX++) { i.xFine++; - if (i.xFine>=3+e->song.pat[i.xCoarse].effectRows*2) { + if (i.xFine>=3+e->song.pat[i.xCoarse].effectCols*2) { i.xFine=0; i.xCoarse++; } @@ -172,7 +172,7 @@ void FurnaceGUI::doSelectAll() { selEnd.y=e->song.patLen-1; } else { // left-right selStart.xFine=0; - selEnd.xFine=2+e->song.pat[selEnd.xCoarse].effectRows*2; + selEnd.xFine=2+e->song.pat[selEnd.xCoarse].effectCols*2; } } } @@ -200,7 +200,7 @@ void FurnaceGUI::doDelete() { for (; iCoarse<=selEnd.xCoarse; iCoarse++) { if (!e->song.chanShow[iCoarse]) continue; DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true); - for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsesong.pat[iCoarse].effectCols*2 && (iCoarsesong.chanShow[iCoarse]) continue; DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true); - for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsesong.pat[iCoarse].effectCols*2 && (iCoarsesong.patLen; j++) { if (jsong.patLen-1) { @@ -269,7 +269,7 @@ void FurnaceGUI::doInsert() { for (; iCoarse<=selEnd.xCoarse; iCoarse++) { if (!e->song.chanShow[iCoarse]) continue; DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true); - for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsesong.pat[iCoarse].effectCols*2 && (iCoarsesong.patLen-1; j>=selStart.y; j--) { if (j==selStart.y) { @@ -301,7 +301,7 @@ void FurnaceGUI::doTranspose(int amount, OperationMask& mask) { for (; iCoarse<=selEnd.xCoarse; iCoarse++) { if (!e->song.chanShow[iCoarse]) continue; DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true); - for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsesong.pat[iCoarse].effectCols*2 && (iCoarsesong.chanShow[iCoarse]) continue; DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true); - for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsesong.pat[iCoarse].effectCols*2 && (iCoarsedata[j][0],pat->data[j][1]); if (cut) { @@ -530,7 +530,7 @@ void FurnaceGUI::doPaste(PasteMode mode) { break; } if (mode!=GUI_PASTE_MODE_MIX_BG || pat->data[j][iFine+1]==-1) { - if (iFine<(3+e->song.pat[iCoarse].effectRows*2)) pat->data[j][iFine+1]=val; + if (iFine<(3+e->song.pat[iCoarse].effectCols*2)) pat->data[j][iFine+1]=val; } } } @@ -589,7 +589,7 @@ void FurnaceGUI::doInterpolate() { for (; iCoarse<=selEnd.xCoarse; iCoarse++) { if (!e->song.chanShow[iCoarse]) continue; DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true); - for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsesong.pat[iCoarse].effectCols*2 && (iCoarsesong.chanShow[iCoarse]) continue; DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true); - for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsesong.pat[iCoarse].effectCols*2 && (iCoarsesong.chanShow[iCoarse]) continue; DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true); - for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsesong.pat[iCoarse].effectCols*2 && (iCoarsesong.chanShow[iCoarse]) continue; DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true); - for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsesong.pat[iCoarse].effectCols*2 && (iCoarsesong.chanShow[iCoarse]) continue; DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true); - for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsesong.pat[iCoarse].effectCols*2 && (iCoarsesong.chanShow[iCoarse]) continue; DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true); - for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsesong.pat[iCoarse].effectCols*2 && (iCoarsesong.chanShow[iCoarse]) continue; DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true); - for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsesong.pat[iCoarse].effectCols*2 && (iCoarsesong.chanShow[iCoarse]) continue; DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true); - for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsesong.pat[iCoarse].effectCols*2 && (iCoarse=(3+(e->song.pat[cursor.xCoarse].effectRows*2))) { + if (++cursor.xFine>=(3+(e->song.pat[cursor.xCoarse].effectCols*2))) { cursor.xFine=3; } } else { @@ -3613,6 +3613,10 @@ bool FurnaceGUI::init() { } if (SDL_GetDisplayUsableBounds(SDL_GetWindowDisplayIndex(sdlWin),&displaySize)==0) { + if (scrW>((displaySize.w/dpiScale)-48) && scrH>((displaySize.h/dpiScale)-64)) { + // maximize + SDL_MaximizeWindow(sdlWin); + } if (scrW>displaySize.w/dpiScale) scrW=(displaySize.w/dpiScale)-32; if (scrH>displaySize.h/dpiScale) scrH=(displaySize.h/dpiScale)-32; if (!fullScreen) { diff --git a/src/gui/gui.h b/src/gui/gui.h index e5ac963b8..3b9911c71 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -825,6 +825,7 @@ class FurnaceGUI { int loadJapanese; int fmLayout; int sampleLayout; + int waveLayout; int susPosition; int effectCursorDir; int cursorPastePos; @@ -903,6 +904,7 @@ class FurnaceGUI { loadJapanese(0), fmLayout(0), sampleLayout(0), + waveLayout(0), susPosition(0), effectCursorDir(1), cursorPastePos(1), diff --git a/src/gui/guiConst.cpp b/src/gui/guiConst.cpp index 18c2fef18..57d63219a 100644 --- a/src/gui/guiConst.cpp +++ b/src/gui/guiConst.cpp @@ -80,11 +80,11 @@ const int vgmVersions[6]={ }; const char* insTypes[DIV_INS_MAX]={ - "Standard", + "Standard (SMS/NES)", "FM (4-operator)", "Game Boy", "C64", - "Amiga/Sample", + "Sample", "PC Engine", "AY-3-8910/SSG", "AY8930", diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index 38174498a..09293a663 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -2465,9 +2465,10 @@ void FurnaceGUI::drawInsEdit() { P(ImGui::Checkbox("Volume Macro is Cutoff Macro",&ins->c64.volIsCutoff)); P(ImGui::Checkbox("Absolute Cutoff Macro",&ins->c64.filterIsAbs)); P(ImGui::Checkbox("Absolute Duty Macro",&ins->c64.dutyIsAbs)); + P(ImGui::Checkbox("Don't test/gate before new note",&ins->c64.noTest)); ImGui::EndTabItem(); } - if (ins->type==DIV_INS_AMIGA || ins->type==DIV_INS_ES5506) if (ImGui::BeginTabItem("Amiga/Sample")) { + if (ins->type==DIV_INS_AMIGA || ins->type==DIV_INS_ES5506) if (ImGui::BeginTabItem("Sample")) { String sName; if (ins->amiga.initSample<0 || ins->amiga.initSample>=e->song.sampleLen) { sName="none selected"; @@ -2920,7 +2921,7 @@ void FurnaceGUI::drawInsEdit() { ex2Max=4095; } if (ins->type==DIV_INS_SU) { - ex1Max=65535; + ex1Max=16383; ex2Max=255; } if (ins->type==DIV_INS_SAA1099) ex1Max=8; @@ -2985,7 +2986,8 @@ void FurnaceGUI::drawInsEdit() { ins->type==DIV_INS_AY || ins->type==DIV_INS_AY8930 || ins->type==DIV_INS_SWAN || - ins->type==DIV_INS_ES5506) { + ins->type==DIV_INS_ES5506 || + ins->type==DIV_INS_SU) { NORMAL_MACRO(ins->std.phaseResetMacro,0,1,"phaseReset","Phase Reset",32,ins->std.phaseResetMacro.open,true,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[16],0,1,NULL,false); } if (ex1Max>0) { @@ -3024,6 +3026,7 @@ void FurnaceGUI::drawInsEdit() { } if (ins->type==DIV_INS_C64) { NORMAL_MACRO(ins->std.ex3Macro,0,2,"ex3","Special",32,ins->std.ex3Macro.open,true,c64SpecialBits,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[6],0,2,NULL,false); + NORMAL_MACRO(ins->std.ex4Macro,0,1,"ex4","Test/Gate",32,ins->std.ex4Macro.open,true,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[7],0,1,NULL,false); } if (ins->type==DIV_INS_AY || ins->type==DIV_INS_AY8930 || ins->type==DIV_INS_X1_010) { NORMAL_MACRO(ins->std.ex3Macro,0,15,"ex3","AutoEnv Num",96,ins->std.ex3Macro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[6],0,15,NULL,false); diff --git a/src/gui/pattern.cpp b/src/gui/pattern.cpp index 3b45cd27b..9ba22ef51 100644 --- a/src/gui/pattern.cpp +++ b/src/gui/pattern.cpp @@ -217,7 +217,7 @@ inline void FurnaceGUI::patternRow(int i, bool isPlaying, float lineHeight, int ImGui::PopStyleColor(); // effects - for (int k=0; ksong.pat[j].effectRows; k++) { + for (int k=0; ksong.pat[j].effectCols; k++) { int index=4+(k<<1); bool selectedEffect=selectedRow && (j32+index-1>=sel1XSum && j32+index-1<=sel2XSum); bool selectedEffectVal=selectedRow && (j32+index>=sel1XSum && j32+index<=sel2XSum); @@ -427,7 +427,7 @@ void FurnaceGUI::drawPattern() { displayTooltip=true; } else { const char* chName=e->getChannelName(i); - size_t chNameLimit=6+4*e->song.pat[i].effectRows; + size_t chNameLimit=6+4*e->song.pat[i].effectCols; if (strlen(chName)>chNameLimit) { String shortChName=chName; shortChName.resize(chNameLimit-3); @@ -524,18 +524,18 @@ void FurnaceGUI::drawPattern() { if (!e->song.chanCollapse[i]) { ImGui::SameLine(); snprintf(chanID,2048,"<##_LCH%d",i); - ImGui::BeginDisabled(e->song.pat[i].effectRows<=1); + ImGui::BeginDisabled(e->song.pat[i].effectCols<=1); if (ImGui::SmallButton(chanID)) { - e->song.pat[i].effectRows--; - if (e->song.pat[i].effectRows<1) e->song.pat[i].effectRows=1; + e->song.pat[i].effectCols--; + if (e->song.pat[i].effectCols<1) e->song.pat[i].effectCols=1; } ImGui::EndDisabled(); ImGui::SameLine(); - ImGui::BeginDisabled(e->song.pat[i].effectRows>=8); + ImGui::BeginDisabled(e->song.pat[i].effectCols>=8); snprintf(chanID,2048,">##_RCH%d",i); if (ImGui::SmallButton(chanID)) { - e->song.pat[i].effectRows++; - if (e->song.pat[i].effectRows>8) e->song.pat[i].effectRows=8; + e->song.pat[i].effectCols++; + if (e->song.pat[i].effectCols>8) e->song.pat[i].effectCols=8; } ImGui::EndDisabled(); } diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index 5630f0747..6f8640871 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -990,6 +990,11 @@ void FurnaceGUI::drawSettings() { ImGui::Separator(); + bool waveLayoutB=settings.waveLayout; + if (ImGui::Checkbox("Use compact wave editor",&waveLayoutB)) { + settings.waveLayout=waveLayoutB; + } + bool sampleLayoutB=settings.sampleLayout; if (ImGui::Checkbox("Use compact sample editor",&sampleLayoutB)) { settings.sampleLayout=sampleLayoutB; @@ -1656,6 +1661,7 @@ void FurnaceGUI::syncSettings() { settings.loadJapanese=e->getConfInt("loadJapanese",0); settings.fmLayout=e->getConfInt("fmLayout",0); settings.sampleLayout=e->getConfInt("sampleLayout",0); + settings.waveLayout=e->getConfInt("waveLayout",0); settings.susPosition=e->getConfInt("susPosition",0); settings.effectCursorDir=e->getConfInt("effectCursorDir",1); settings.cursorPastePos=e->getConfInt("cursorPastePos",1); @@ -1733,6 +1739,7 @@ void FurnaceGUI::syncSettings() { clampSetting(settings.frameBorders,0,1); clampSetting(settings.effectDeletionAltersValue,0,1); clampSetting(settings.sampleLayout,0,1); + clampSetting(settings.waveLayout,0,1); clampSetting(settings.separateFMColors,0,1); clampSetting(settings.insEditColorize,0,1); clampSetting(settings.metroVol,0,200); @@ -1817,6 +1824,7 @@ void FurnaceGUI::commitSettings() { e->setConf("loadJapanese",settings.loadJapanese); e->setConf("fmLayout",settings.fmLayout); e->setConf("sampleLayout",settings.sampleLayout); + e->setConf("waveLayout",settings.waveLayout); e->setConf("susPosition",settings.susPosition); e->setConf("effectCursorDir",settings.effectCursorDir); e->setConf("cursorPastePos",settings.cursorPastePos); diff --git a/src/gui/waveEdit.cpp b/src/gui/waveEdit.cpp index a9efe512e..5c812283e 100644 --- a/src/gui/waveEdit.cpp +++ b/src/gui/waveEdit.cpp @@ -31,7 +31,7 @@ void FurnaceGUI::drawWaveEdit() { } if (!waveEditOpen) return; float wavePreview[256]; - ImGui::SetNextWindowSizeConstraints(ImVec2(450.0f*dpiScale,300.0f*dpiScale),ImVec2(scrW*dpiScale,scrH*dpiScale)); + ImGui::SetNextWindowSizeConstraints(ImVec2(300.0f*dpiScale,300.0f*dpiScale),ImVec2(scrW*dpiScale,scrH*dpiScale)); if (ImGui::Begin("Wavetable Editor",&waveEditOpen,settings.allowEditDocking?0:ImGuiWindowFlags_NoDocking)) { if (curWave<0 || curWave>=(int)e->song.wave.size()) { ImGui::Text("no wavetable selected"); @@ -53,31 +53,34 @@ void FurnaceGUI::drawWaveEdit() { ImGui::SameLine(); DivWavetable* wave=e->song.wave[curWave]; - ImGui::Text("Width"); - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("use a width of:\n- any on Amiga/N163\n- 32 on Game Boy, PC Engine and WonderSwan\n- 64 on FDS\n- 128 on X1-010\nany other widths will be scaled during playback."); - } - ImGui::SameLine(); - ImGui::SetNextItemWidth(128.0f*dpiScale); - if (ImGui::InputInt("##_WTW",&wave->len,1,2)) { - if (wave->len>256) wave->len=256; - if (wave->len<1) wave->len=1; - e->notifyWaveChange(curWave); - if (wavePreviewOn) e->previewWave(curWave,wavePreviewNote); - MARK_MODIFIED; - } - ImGui::SameLine(); - ImGui::Text("Height"); - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("use a height of:\n- 15 for Game Boy, WonderSwan, X1-010 Envelope shape and N163\n- 31 for PC Engine\n- 63 for FDS\n- 255 for X1-010\nany other heights will be scaled during playback."); - } - ImGui::SameLine(); - ImGui::SetNextItemWidth(128.0f*dpiScale); - if (ImGui::InputInt("##_WTH",&wave->max,1,2)) { - if (wave->max>255) wave->max=255; - if (wave->max<1) wave->max=1; - e->notifyWaveChange(curWave); - MARK_MODIFIED; + + if (!settings.waveLayout){ + ImGui::Text("Width"); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("use a width of:\n- any on Amiga/N163\n- 32 on Game Boy, PC Engine and WonderSwan\n- 64 on FDS\n- 128 on X1-010\nany other widths will be scaled during playback."); + } + ImGui::SameLine(); + ImGui::SetNextItemWidth(128.0f*dpiScale); + if (ImGui::InputInt("##_WTW",&wave->len,1,2)) { + if (wave->len>256) wave->len=256; + if (wave->len<1) wave->len=1; + e->notifyWaveChange(curWave); + if (wavePreviewOn) e->previewWave(curWave,wavePreviewNote); + MARK_MODIFIED; + } + ImGui::SameLine(); + ImGui::Text("Height"); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("use a height of:\n- 15 for Game Boy, WonderSwan, X1-010 Envelope shape and N163\n- 31 for PC Engine\n- 63 for FDS\n- 255 for X1-010\nany other heights will be scaled during playback."); + } + ImGui::SameLine(); + ImGui::SetNextItemWidth(128.0f*dpiScale); + if (ImGui::InputInt("##_WTH",&wave->max,1,2)) { + if (wave->max>255) wave->max=255; + if (wave->max<1) wave->max=1; + e->notifyWaveChange(curWave); + MARK_MODIFIED; + } } ImGui::SameLine(); if (ImGui::RadioButton("Dec",!waveHex)) { @@ -87,6 +90,42 @@ void FurnaceGUI::drawWaveEdit() { if (ImGui::RadioButton("Hex",waveHex)) { waveHex=true; } + + if (settings.waveLayout){ + if (ImGui::BeginTable("WaveProps",2,ImGuiTableFlags_SizingStretchSame)) { + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::Text("Width"); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("use a width of:\n- any on Amiga/N163\n- 32 on Game Boy, PC Engine and WonderSwan\n- 64 on FDS\n- 128 on X1-010\nany other widths will be scaled during playback."); + } + ImGui::SameLine(); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + if (ImGui::InputInt("##_WTW",&wave->len,1,2)) { + if (wave->len>256) wave->len=256; + if (wave->len<1) wave->len=1; + e->notifyWaveChange(curWave); + if (wavePreviewOn) e->previewWave(curWave,wavePreviewNote); + MARK_MODIFIED; + } + ImGui::TableNextColumn(); + ImGui::SameLine(); + ImGui::Text("Height"); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("use a height of:\n- 15 for Game Boy, WonderSwan, X1-010 Envelope shape and N163\n- 31 for PC Engine\n- 63 for FDS\n- 255 for X1-010\nany other heights will be scaled during playback."); + } + ImGui::SameLine(); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + if (ImGui::InputInt("##_WTH",&wave->max,1,2)) { + if (wave->max>255) wave->max=255; + if (wave->max<1) wave->max=1; + e->notifyWaveChange(curWave); + MARK_MODIFIED; + } + ImGui::EndTable(); + } + } + for (int i=0; ilen; i++) { if (wave->data[i]>wave->max) wave->data[i]=wave->max; wavePreview[i]=wave->data[i];