From d16ee7ed4557e9ed89edcedc1a9b169fd6cb1319 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 29 Apr 2022 15:36:03 -0500 Subject: [PATCH 1/9] sysDef oopsie --- src/engine/sysDef.cpp | 43 ++----------------------------------------- 1 file changed, 2 insertions(+), 41 deletions(-) diff --git a/src/engine/sysDef.cpp b/src/engine/sysDef.cpp index 082eb0833..1f7920d7c 100644 --- a/src/engine/sysDef.cpp +++ b/src/engine/sysDef.cpp @@ -312,46 +312,8 @@ DivInstrumentType DivEngine::getPreferInsSecondType(int chan) { } int DivEngine::minVGMVersion(DivSystem which) { - switch (which) { - case DIV_SYSTEM_YM2612: - case DIV_SYSTEM_YM2612_EXT: - case DIV_SYSTEM_SMS: - case DIV_SYSTEM_OPLL: - case DIV_SYSTEM_OPLL_DRUMS: - case DIV_SYSTEM_VRC7: - case DIV_SYSTEM_YM2151: - return 0x150; // due to usage of data blocks - case DIV_SYSTEM_SEGAPCM: - case DIV_SYSTEM_SEGAPCM_COMPAT: - case DIV_SYSTEM_YM2610: - case DIV_SYSTEM_YM2610_EXT: - case DIV_SYSTEM_YM2610_FULL: - case DIV_SYSTEM_YM2610_FULL_EXT: - case DIV_SYSTEM_YM2610B: - case DIV_SYSTEM_YM2610B_EXT: - case DIV_SYSTEM_OPL: - case DIV_SYSTEM_OPL_DRUMS: - case DIV_SYSTEM_OPL2: - case DIV_SYSTEM_OPL2_DRUMS: - case DIV_SYSTEM_OPL3: - case DIV_SYSTEM_OPL3_DRUMS: - case DIV_SYSTEM_AY8910: - case DIV_SYSTEM_AY8930: - return 0x151; - case DIV_SYSTEM_GB: - case DIV_SYSTEM_PCE: - case DIV_SYSTEM_NES: - case DIV_SYSTEM_FDS: - case DIV_SYSTEM_QSOUND: - return 0x161; - case DIV_SYSTEM_SAA1099: - case DIV_SYSTEM_X1_010: - case DIV_SYSTEM_SWAN: - return 0x171; - default: - return 0; - } - return 0; + if (sysDefs[which]==NULL) return 0; + return sysDefs[which]->vgmVersion; } // define systems like: @@ -544,7 +506,6 @@ void DivEngine::registerSystems() { {DIV_INS_PET} ); - // TODO: DIV_INS_SNES sysDefs[DIV_SYSTEM_SNES]=new DivSysDef( "SNES", NULL, 0x87, 0, 8, false, true, 0, false, {"Channel 1", "Channel 2", "Channel 3", "Channel 4", "Channel 5", "Channel 6", "Channel 7", "Channel 8"}, From a8da4f0dba6f63c1661c25647801e1174720a80e Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 29 Apr 2022 17:55:22 -0500 Subject: [PATCH 2/9] GUI: implement sample scroll with mouse wheel --- src/gui/sampleEdit.cpp | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/gui/sampleEdit.cpp b/src/gui/sampleEdit.cpp index 7882e53c4..38a737bdb 100644 --- a/src/gui/sampleEdit.cpp +++ b/src/gui/sampleEdit.cpp @@ -1276,7 +1276,26 @@ void FurnaceGUI::drawSampleEdit() { sampleZoom=100.0/zoomPercent; if (sampleZoom<0.01) sampleZoom=0.01; sampleZoomAuto=false; + int bounds=((int)sample->samples-round(rectSize.x*sampleZoom)); + if (bounds<0) bounds=0; + if (samplePos>bounds) samplePos=bounds; updateSampleTex=true; + } else { + if (wheelY!=0) { + if (!sampleZoomAuto) { + double scrollAmount=MAX(fabs((double)wheelY*sampleZoom*60.0),1.0); + if (wheelY>0) { + samplePos+=scrollAmount; + } else { + samplePos-=scrollAmount; + } + if (samplePos<0) samplePos=0; + int bounds=((int)sample->samples-round(rectSize.x*sampleZoom)); + if (bounds<0) bounds=0; + if (samplePos>bounds) samplePos=bounds; + updateSampleTex=true; + } + } } int posX=-1; From 9c4fed2f4ed71ba03de60780ab52d949e02ded2b Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 29 Apr 2022 22:41:07 -0500 Subject: [PATCH 3/9] GUI: fix sample paste crash --- src/gui/doAction.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/gui/doAction.cpp b/src/gui/doAction.cpp index 28f6cf882..4020dfc7b 100644 --- a/src/gui/doAction.cpp +++ b/src/gui/doAction.cpp @@ -18,6 +18,7 @@ */ #include "gui.h" +#include "../ta-log.h" #include #include @@ -712,6 +713,9 @@ void FurnaceGUI::doAction(int what) { DivSample* sample=e->song.sample[curSample]; sample->prepareUndo(true); int pos=(sampleSelStart==-1 || sampleSelStart==sampleSelEnd)?sample->samples:sampleSelStart; + if (pos>=(int)sample->samples) pos=sample->samples-1; + if (pos<0) pos=0; + logV("paste position: %d",pos); e->lockEngine([this,sample,pos]() { if (!sample->insert(pos,sampleClipboardLen)) { @@ -739,6 +743,8 @@ void FurnaceGUI::doAction(int what) { DivSample* sample=e->song.sample[curSample]; sample->prepareUndo(true); int pos=(sampleSelStart==-1 || sampleSelStart==sampleSelEnd)?0:sampleSelStart; + if (pos>=(int)sample->samples) pos=sample->samples-1; + if (pos<0) pos=0; e->lockEngine([this,sample,pos]() { if (sample->depth==8) { @@ -767,6 +773,8 @@ void FurnaceGUI::doAction(int what) { DivSample* sample=e->song.sample[curSample]; sample->prepareUndo(true); int pos=(sampleSelStart==-1 || sampleSelStart==sampleSelEnd)?0:sampleSelStart; + if (pos>=(int)sample->samples) pos=sample->samples-1; + if (pos<0) pos=0; e->lockEngine([this,sample,pos]() { if (sample->depth==8) { From 2e8634626d17b7e1f3854a14693d777918bfbc5f Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 29 Apr 2022 23:41:14 -0500 Subject: [PATCH 4/9] new panning effects (80/81/82) --- papers/doc/3-pattern/effects.md | 9 ++ src/engine/dispatch.h | 2 +- src/engine/engine.cpp | 10 ++ src/engine/engine.h | 5 +- src/engine/platform/arcade.cpp | 4 +- src/engine/platform/gb.cpp | 8 +- src/engine/platform/genesis.cpp | 4 +- src/engine/platform/genesisext.cpp | 4 +- src/engine/platform/lynx.cpp | 2 +- src/engine/platform/opl.cpp | 4 +- src/engine/platform/pce.cpp | 2 +- src/engine/platform/qsound.cpp | 2 +- src/engine/platform/saa.cpp | 2 +- src/engine/platform/segapcm.cpp | 4 +- src/engine/platform/su.cpp | 2 +- src/engine/platform/swan.cpp | 2 +- src/engine/platform/tx81z.cpp | 4 +- src/engine/platform/vera.cpp | 4 +- src/engine/platform/x1_010.cpp | 5 +- src/engine/platform/ym2610.cpp | 4 +- src/engine/platform/ym2610b.cpp | 4 +- src/engine/platform/ym2610bext.cpp | 4 +- src/engine/platform/ym2610ext.cpp | 4 +- src/engine/playback.cpp | 26 +++- src/gui/effectList.cpp | 22 +-- src/gui/gui.cpp | 22 +-- src/gui/guiConst.cpp | 241 ++++++++++++++++++++++++++++- src/gui/guiConst.h | 3 +- src/gui/pattern.cpp | 31 +--- 29 files changed, 328 insertions(+), 112 deletions(-) diff --git a/papers/doc/3-pattern/effects.md b/papers/doc/3-pattern/effects.md index 773d30373..f72be5013 100644 --- a/papers/doc/3-pattern/effects.md +++ b/papers/doc/3-pattern/effects.md @@ -14,6 +14,15 @@ however, effects are continuous, which means you only need to type it once and t - maximum tremolo depth is -60 volume steps. - `08xy`: set panning. `x` is the left channel and `y` is the right one. - not all systems support this effect. +- `80xx`: set panning (linear). this effect behaves more like other trackers: + - `00` is left. + - `80` is center. + - `FF` is right. + - not all systems support this effect. +- `81xx`: set volume of left channel (from `00` to `FF`). + - not all systems support this effect. +- `82xx`: set volume of right channel (from `00` to `FF`). + - not all systems support this effect. - `09xx`: set speed 1. - `0Axy`: volume slide. - if `x` is 0 then this is a slide down. diff --git a/src/engine/dispatch.h b/src/engine/dispatch.h index a12ccbfe8..21598442b 100644 --- a/src/engine/dispatch.h +++ b/src/engine/dispatch.h @@ -49,7 +49,7 @@ enum DivDispatchCmds { DIV_CMD_GET_VOLMAX, // () -> volMax DIV_CMD_NOTE_PORTA, // (target, speed) -> 2 if target reached DIV_CMD_PITCH, // (pitch) - DIV_CMD_PANNING, // (pan) + DIV_CMD_PANNING, // (left, right) DIV_CMD_LEGATO, // (note) DIV_CMD_PRE_PORTA, // (inPorta, isPortaOrSlide) DIV_CMD_PRE_NOTE, // used in C64 (note) diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index c42f5cc5d..edd7dcc46 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -69,6 +69,12 @@ const char* DivEngine::getEffectDesc(unsigned char effect, int chan, bool notNul return "0Dxx: Jump to next pattern"; case 0x0f: return "0Fxx: Set speed 2"; + case 0x80: + return "80xx: Set panning (00: left; 80: center; FF: right)"; + case 0x81: + return "81xx: Set panning (left channel)"; + case 0x82: + return "82xx: Set panning (right channel)"; case 0xc0: case 0xc1: case 0xc2: case 0xc3: return "Cxxx: Set tick rate (hz)"; case 0xe0: @@ -1052,6 +1058,10 @@ int DivEngine::convertPanSplitToLinear(unsigned int val, unsigned char bits, int return pan*range; } +int DivEngine::convertPanSplitToLinearLR(unsigned char left, unsigned char right, int range) { + return convertPanSplitToLinear((left<<8)|right,8,range); +} + unsigned int DivEngine::convertPanLinearToSplit(int val, unsigned char bits, int range) { if (val<0) val=0; if (val>range) val=range; diff --git a/src/engine/engine.h b/src/engine/engine.h index 9f971d7a8..e3a0f71c2 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -84,7 +84,7 @@ struct DivChannelState { int delayOrder, delayRow, retrigSpeed, retrigTick; int vibratoDepth, vibratoRate, vibratoPos, vibratoDir, vibratoFine; int tremoloDepth, tremoloRate, tremoloPos; - unsigned char arp, arpStage, arpTicks; + unsigned char arp, arpStage, arpTicks, panL, panR; bool doNote, legato, portaStop, keyOn, keyOff, nowYouCanStop, stopOnOff; bool arpYield, delayLocked, inPorta, scheduledSlideReset, shorthandPorta, noteOnInhibit, resetArp; @@ -119,6 +119,8 @@ struct DivChannelState { arp(0), arpStage(-1), arpTicks(1), + panL(255), + panR(255), doNote(false), legato(false), portaStop(false), @@ -477,6 +479,7 @@ class DivEngine { // convert panning formats int convertPanSplitToLinear(unsigned int val, unsigned char bits, int range); + int convertPanSplitToLinearLR(unsigned char left, unsigned char right, int range); unsigned int convertPanLinearToSplit(int val, unsigned char bits, int range); // find song loop position diff --git a/src/engine/platform/arcade.cpp b/src/engine/platform/arcade.cpp index f993a400d..11cff5e94 100644 --- a/src/engine/platform/arcade.cpp +++ b/src/engine/platform/arcade.cpp @@ -543,8 +543,8 @@ int DivPlatformArcade::dispatch(DivCommand c) { chan[c.chan].ins=c.value; break; case DIV_CMD_PANNING: { - chan[c.chan].chVolL=((c.value>>4)>0); - chan[c.chan].chVolR=((c.value&15)>0); + chan[c.chan].chVolL=(c.value>0); + chan[c.chan].chVolR=(c.value2>0); if (isMuted[c.chan]) { rWrite(chanOffs[c.chan]+ADDR_LR_FB_ALG,(chan[c.chan].state.alg&7)|(chan[c.chan].state.fb<<3)); } else { diff --git a/src/engine/platform/gb.cpp b/src/engine/platform/gb.cpp index 0cfdb960c..e55e703ef 100644 --- a/src/engine/platform/gb.cpp +++ b/src/engine/platform/gb.cpp @@ -371,9 +371,11 @@ int DivPlatformGB::dispatch(DivCommand c) { break; case DIV_CMD_PANNING: { lastPan&=~(0x11<0)|(((c.value>>4)>0)<<4); - lastPan|=c.value<0) pan|=0x10; + if (c.value2>0) pan|=0x01; + if (pan==0) pan=0x11; + lastPan|=pan<0)|(((c.value>>4)>0)<<1); + chan[c.chan].pan=(c.value2>0)|((c.value>0)<<1); } rWrite(chanOffs[c.chan]+ADDR_LRAF,(isMuted[c.chan]?0:(chan[c.chan].pan<<6))|(chan[c.chan].state.fms&7)|((chan[c.chan].state.ams&3)<<4)); break; diff --git a/src/engine/platform/genesisext.cpp b/src/engine/platform/genesisext.cpp index e5d964e5d..e67c353d4 100644 --- a/src/engine/platform/genesisext.cpp +++ b/src/engine/platform/genesisext.cpp @@ -107,10 +107,10 @@ int DivPlatformGenesisExt::dispatch(DivCommand c) { opChan[ch].ins=c.value; break; case DIV_CMD_PANNING: { - if (c.value==0) { + if (c.value==0 && c.value2==0) { opChan[ch].pan=3; } else { - opChan[ch].pan=((c.value&15)>0)|(((c.value>>4)>0)<<1); + opChan[ch].pan=(c.value2>0)|((c.value>0)<<1); } if (parent->song.sharedExtStat) { for (int i=0; i<4; i++) { diff --git a/src/engine/platform/lynx.cpp b/src/engine/platform/lynx.cpp index 9a1f4d3d2..18a9b72fa 100644 --- a/src/engine/platform/lynx.cpp +++ b/src/engine/platform/lynx.cpp @@ -259,7 +259,7 @@ int DivPlatformLynx::dispatch(DivCommand c) { } break; case DIV_CMD_PANNING: - chan[c.chan].pan=c.value; + chan[c.chan].pan=(c.value&0xf0)|(c.value2>>4); WRITE_ATTEN(c.chan,chan[c.chan].pan); break; case DIV_CMD_GET_VOLUME: diff --git a/src/engine/platform/opl.cpp b/src/engine/platform/opl.cpp index c127ce774..3e385434e 100644 --- a/src/engine/platform/opl.cpp +++ b/src/engine/platform/opl.cpp @@ -680,10 +680,10 @@ int DivPlatformOPL::dispatch(DivCommand c) { break; case DIV_CMD_PANNING: { if (oplType!=3) break; - if (c.value==0) { + if (c.value==0 && c.value2==0) { chan[c.chan].pan=3; } else { - chan[c.chan].pan=(((c.value&15)>0)<<1)|((c.value>>4)>0); + chan[c.chan].pan=(c.value2>0)|((c.value>0)<<1); } int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2; if (isMuted[c.chan]) { diff --git a/src/engine/platform/pce.cpp b/src/engine/platform/pce.cpp index b09263fc6..a56732bef 100644 --- a/src/engine/platform/pce.cpp +++ b/src/engine/platform/pce.cpp @@ -424,7 +424,7 @@ int DivPlatformPCE::dispatch(DivCommand c) { } break; case DIV_CMD_PANNING: { - chan[c.chan].pan=c.value; + chan[c.chan].pan=(c.value&0xf0)|(c.value2>>4); chWrite(c.chan,0x05,isMuted[c.chan]?0:chan[c.chan].pan); break; } diff --git a/src/engine/platform/qsound.cpp b/src/engine/platform/qsound.cpp index 9dd34589a..f3197f6b4 100644 --- a/src/engine/platform/qsound.cpp +++ b/src/engine/platform/qsound.cpp @@ -441,7 +441,7 @@ int DivPlatformQSound::dispatch(DivCommand c) { return chan[c.chan].outVol; break; case DIV_CMD_PANNING: - chan[c.chan].panning=parent->convertPanSplitToLinear(c.value,4,32); + chan[c.chan].panning=parent->convertPanSplitToLinearLR(c.value,c.value2,32); immWrite(Q1_PAN+c.chan,chan[c.chan].panning+0x110+(chan[c.chan].surround?0:0x30)); break; case DIV_CMD_QSOUND_ECHO_LEVEL: diff --git a/src/engine/platform/saa.cpp b/src/engine/platform/saa.cpp index 4d79d45bd..a083355a6 100644 --- a/src/engine/platform/saa.cpp +++ b/src/engine/platform/saa.cpp @@ -331,7 +331,7 @@ int DivPlatformSAA1099::dispatch(DivCommand c) { break; } case DIV_CMD_PANNING: - chan[c.chan].pan=c.value; + chan[c.chan].pan=(c.value&0xf0)|(c.value2>>4); if (isMuted[c.chan]) { rWrite(c.chan,0); } else { diff --git a/src/engine/platform/segapcm.cpp b/src/engine/platform/segapcm.cpp index 9064fa093..eb506ff36 100644 --- a/src/engine/platform/segapcm.cpp +++ b/src/engine/platform/segapcm.cpp @@ -268,8 +268,8 @@ int DivPlatformSegaPCM::dispatch(DivCommand c) { chan[c.chan].ins=c.value; break; case DIV_CMD_PANNING: { - chan[c.chan].chVolL=(c.value>>4)|(((c.value>>4)>>1)<<4); - chan[c.chan].chVolR=(c.value&15)|(((c.value&15)>>1)<<4); + chan[c.chan].chVolL=c.value>>1; + chan[c.chan].chVolR=c.value2>>1; if (dumpWrites) { addWrite(0x10002+(c.chan<<3),chan[c.chan].chVolL); addWrite(0x10003+(c.chan<<3),chan[c.chan].chVolR); diff --git a/src/engine/platform/su.cpp b/src/engine/platform/su.cpp index 4733e5b24..3a6003cb8 100644 --- a/src/engine/platform/su.cpp +++ b/src/engine/platform/su.cpp @@ -270,7 +270,7 @@ int DivPlatformSoundUnit::dispatch(DivCommand c) { break; } case DIV_CMD_PANNING: { - chan[c.chan].pan=parent->convertPanSplitToLinear(c.value,4,254)-127; + chan[c.chan].pan=parent->convertPanSplitToLinearLR(c.value,c.value2,254)-127; chWrite(c.chan,0x03,chan[c.chan].pan); break; } diff --git a/src/engine/platform/swan.cpp b/src/engine/platform/swan.cpp index 147082924..9ac07f6e6 100644 --- a/src/engine/platform/swan.cpp +++ b/src/engine/platform/swan.cpp @@ -412,7 +412,7 @@ int DivPlatformSwan::dispatch(DivCommand c) { } break; case DIV_CMD_PANNING: { - chan[c.chan].pan=c.value; + chan[c.chan].pan=(c.value&0xf0)|(c.value2>>4); calcAndWriteOutVol(c.chan,chan[c.chan].std.vol.will?chan[c.chan].std.vol.val:15); break; } diff --git a/src/engine/platform/tx81z.cpp b/src/engine/platform/tx81z.cpp index 643b6ebcf..d69dfc875 100644 --- a/src/engine/platform/tx81z.cpp +++ b/src/engine/platform/tx81z.cpp @@ -525,8 +525,8 @@ int DivPlatformTX81Z::dispatch(DivCommand c) { chan[c.chan].ins=c.value; break; case DIV_CMD_PANNING: { - chan[c.chan].chVolL=((c.value>>4)>0); - chan[c.chan].chVolR=((c.value&15)>0); + chan[c.chan].chVolL=(c.value>0); + chan[c.chan].chVolR=(c.value2>0); chan[c.chan].freqChanged=true; /* if (isMuted[c.chan]) { diff --git a/src/engine/platform/vera.cpp b/src/engine/platform/vera.cpp index 9b30abdad..ccb819c9d 100644 --- a/src/engine/platform/vera.cpp +++ b/src/engine/platform/vera.cpp @@ -345,8 +345,8 @@ int DivPlatformVERA::dispatch(DivCommand c) { break; case DIV_CMD_PANNING: { tmp=0; - tmp|=(c.value&0x10)?1:0; - tmp|=(c.value&0x01)?2:0; + tmp|=(c.value>0)?1:0; + tmp|=(c.value2>0)?2:0; chan[c.chan].pan=tmp&3; if (c.chan<16) { rWriteHi(c.chan,2,isMuted[c.chan]?0:chan[c.chan].pan); diff --git a/src/engine/platform/x1_010.cpp b/src/engine/platform/x1_010.cpp index 9d64adfa9..42319a29b 100644 --- a/src/engine/platform/x1_010.cpp +++ b/src/engine/platform/x1_010.cpp @@ -694,8 +694,9 @@ int DivPlatformX1_010::dispatch(DivCommand c) { break; case DIV_CMD_PANNING: { if (!stereo) break; - if (chan[c.chan].pan!=c.value) { - chan[c.chan].pan=c.value; + unsigned char newPan=(c.value&0xf0)|(c.value2>>4); + if (chan[c.chan].pan!=newPan) { + chan[c.chan].pan=newPan; if (!isMuted[c.chan]) { chan[c.chan].envChanged=true; } diff --git a/src/engine/platform/ym2610.cpp b/src/engine/platform/ym2610.cpp index 4fcd53d36..c4ae0ad80 100644 --- a/src/engine/platform/ym2610.cpp +++ b/src/engine/platform/ym2610.cpp @@ -839,10 +839,10 @@ int DivPlatformYM2610::dispatch(DivCommand c) { chan[c.chan].ins=c.value; break; case DIV_CMD_PANNING: { - if (c.value==0) { + if (c.value==0 && c.value2==0) { chan[c.chan].pan=3; } else { - chan[c.chan].pan=((c.value&15)>0)|(((c.value>>4)>0)<<1); + chan[c.chan].pan=(c.value2>0)|((c.value>0)<<1); } if (c.chan>12) { immWrite(0x11,isMuted[c.chan]?0:(chan[c.chan].pan<<6)); diff --git a/src/engine/platform/ym2610b.cpp b/src/engine/platform/ym2610b.cpp index 3ad47b17d..12e32ef8c 100644 --- a/src/engine/platform/ym2610b.cpp +++ b/src/engine/platform/ym2610b.cpp @@ -902,10 +902,10 @@ int DivPlatformYM2610B::dispatch(DivCommand c) { chan[c.chan].ins=c.value; break; case DIV_CMD_PANNING: { - if (c.value==0) { + if (c.value==0 && c.value2==0) { chan[c.chan].pan=3; } else { - chan[c.chan].pan=((c.value&15)>0)|(((c.value>>4)>0)<<1); + chan[c.chan].pan=(c.value2>0)|((c.value>0)<<1); } if (c.chan>14) { immWrite(0x11,isMuted[c.chan]?0:(chan[c.chan].pan<<6)); diff --git a/src/engine/platform/ym2610bext.cpp b/src/engine/platform/ym2610bext.cpp index e930934d0..5eec24ece 100644 --- a/src/engine/platform/ym2610bext.cpp +++ b/src/engine/platform/ym2610bext.cpp @@ -99,10 +99,10 @@ int DivPlatformYM2610BExt::dispatch(DivCommand c) { opChan[ch].ins=c.value; break; case DIV_CMD_PANNING: { - if (c.value==0) { + if (c.value==0 && c.value2==0) { opChan[ch].pan=3; } else { - opChan[ch].pan=((c.value&15)>0)|(((c.value>>4)>0)<<1); + opChan[ch].pan=(c.value2>0)|((c.value>0)<<1); } DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM); if (parent->song.sharedExtStat) { diff --git a/src/engine/platform/ym2610ext.cpp b/src/engine/platform/ym2610ext.cpp index c4cb3d9ca..6ae911061 100644 --- a/src/engine/platform/ym2610ext.cpp +++ b/src/engine/platform/ym2610ext.cpp @@ -99,10 +99,10 @@ int DivPlatformYM2610Ext::dispatch(DivCommand c) { opChan[ch].ins=c.value; break; case DIV_CMD_PANNING: { - if (c.value==0) { + if (c.value==0 && c.value2==0) { opChan[ch].pan=3; } else { - opChan[ch].pan=((c.value&15)>0)|(((c.value>>4)>0)<<1); + opChan[ch].pan=(c.value2>0)|((c.value>0)<<1); } DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM); if (parent->song.sharedExtStat) { diff --git a/src/engine/playback.cpp b/src/engine/playback.cpp index 29899f0d9..cb4d663d1 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -1025,6 +1025,7 @@ void DivEngine::processRow(int i, bool afterDelay) { short lastSlide=-1; bool calledPorta=false; + bool panChanged=false; // effects for (int j=0; j>4)|(effectVal&0xf0); + chan[i].panR=(effectVal&15)|((effectVal&15)<<4); + panChanged=true; + break; + case 0x80: { // panning (linear) + unsigned short pan=convertPanLinearToSplit(effectVal,8,255); + chan[i].panL=pan>>8; + chan[i].panR=pan&0xff; + panChanged=true; + break; + } + case 0x81: // panning left (split 8-bit) + chan[i].panL=effectVal; + panChanged=true; + break; + case 0x82: // panning right (split 8-bit) + chan[i].panR=effectVal; + panChanged=true; break; case 0x01: // ramp up if (song.ignoreDuplicateSlides && (lastSlide==0x01 || lastSlide==0x1337)) break; @@ -1308,6 +1326,10 @@ void DivEngine::processRow(int i, bool afterDelay) { } } + if (panChanged) { + dispatchCmd(DivCommand(DIV_CMD_PANNING,i,chan[i].panL,chan[i].panR)); + } + if (insChanged && (chan[i].inPorta || calledPorta) && song.newInsTriggersInPorta) { dispatchCmd(DivCommand(DIV_CMD_NOTE_ON,i,DIV_NOTE_NULL)); } diff --git a/src/gui/effectList.cpp b/src/gui/effectList.cpp index c64966a43..032b87c33 100644 --- a/src/gui/effectList.cpp +++ b/src/gui/effectList.cpp @@ -32,27 +32,7 @@ void FurnaceGUI::drawEffectList() { ImGui::TableNextRow(); ImGui::TableNextColumn(); ImGui::PushFont(patFont); - if (i<0x10) { - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[fxColors[i]]); - } else if (i<0x20) { - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY]); - } else if (i<0x30) { - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_SYS_SECONDARY]); - } else if (i<0x48) { - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY]); - } else if (i<0x90) { - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_INVALID]); - } else if (i<0xa0) { - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_MISC]); - } else if (i<0xc0) { - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_INVALID]); - } else if (i<0xd0) { - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_SPEED]); - } else if (i<0xe0) { - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_INVALID]); - } else { - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[extFxColors[i-0xe0]]); - } + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[fxColors[i]]); ImGui::Text("%c%c%c%c",name[0],name[1],name[2],name[3]); ImGui::PopStyleColor(); ImGui::PopFont(); diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 74a303542..ee94e8fe8 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -2000,27 +2000,7 @@ void FurnaceGUI::editOptions(bool topMenu) { } else { const unsigned char data=latchEffect; snprintf(id,63,"%.2x##LatchFX",data); - if (data<0x10) { - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[fxColors[data]]); - } else if (data<0x20) { - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY]); - } else if (data<0x30) { - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_SYS_SECONDARY]); - } else if (data<0x48) { - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY]); - } else if (data<0x90) { - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_INVALID]); - } else if (data<0xa0) { - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_MISC]); - } else if (data<0xc0) { - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_INVALID]); - } else if (data<0xd0) { - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_SPEED]); - } else if (data<0xe0) { - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_INVALID]); - } else { - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[extFxColors[data-0xe0]]); - } + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[fxColors[data]]); } if (ImGui::Selectable(id,latchTarget==3,ImGuiSelectableFlags_DontClosePopups)) { diff --git a/src/gui/guiConst.cpp b/src/gui/guiConst.cpp index 357868381..ec157a9eb 100644 --- a/src/gui/guiConst.cpp +++ b/src/gui/guiConst.cpp @@ -142,7 +142,7 @@ const char* resampleStrats[]={ "best possible" }; -const FurnaceGUIColors fxColors[16]={ +const FurnaceGUIColors fxColors[256]={ GUI_COLOR_PATTERN_EFFECT_MISC, // 00 GUI_COLOR_PATTERN_EFFECT_PITCH, // 01 GUI_COLOR_PATTERN_EFFECT_PITCH, // 02 @@ -159,9 +159,242 @@ const FurnaceGUIColors fxColors[16]={ GUI_COLOR_PATTERN_EFFECT_SONG, // 0D GUI_COLOR_PATTERN_EFFECT_INVALID, // 0E GUI_COLOR_PATTERN_EFFECT_SPEED, // 0F -}; -const FurnaceGUIColors extFxColors[32]={ + // 10-1F + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + + // 20-2F + GUI_COLOR_PATTERN_EFFECT_SYS_SECONDARY, + GUI_COLOR_PATTERN_EFFECT_SYS_SECONDARY, + GUI_COLOR_PATTERN_EFFECT_SYS_SECONDARY, + GUI_COLOR_PATTERN_EFFECT_SYS_SECONDARY, + GUI_COLOR_PATTERN_EFFECT_SYS_SECONDARY, + GUI_COLOR_PATTERN_EFFECT_SYS_SECONDARY, + GUI_COLOR_PATTERN_EFFECT_SYS_SECONDARY, + GUI_COLOR_PATTERN_EFFECT_SYS_SECONDARY, + GUI_COLOR_PATTERN_EFFECT_SYS_SECONDARY, + GUI_COLOR_PATTERN_EFFECT_SYS_SECONDARY, + GUI_COLOR_PATTERN_EFFECT_SYS_SECONDARY, + GUI_COLOR_PATTERN_EFFECT_SYS_SECONDARY, + GUI_COLOR_PATTERN_EFFECT_SYS_SECONDARY, + GUI_COLOR_PATTERN_EFFECT_SYS_SECONDARY, + GUI_COLOR_PATTERN_EFFECT_SYS_SECONDARY, + GUI_COLOR_PATTERN_EFFECT_SYS_SECONDARY, + + // 30-3F + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + + // 40-4F + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + + // 50-5F + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + + // 60-6F + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + + // 70-7F + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + + // 80-8F + GUI_COLOR_PATTERN_EFFECT_PANNING, + GUI_COLOR_PATTERN_EFFECT_PANNING, + GUI_COLOR_PATTERN_EFFECT_PANNING, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + + // 90-9F + GUI_COLOR_PATTERN_EFFECT_MISC, + GUI_COLOR_PATTERN_EFFECT_MISC, + GUI_COLOR_PATTERN_EFFECT_MISC, + GUI_COLOR_PATTERN_EFFECT_MISC, + GUI_COLOR_PATTERN_EFFECT_MISC, + GUI_COLOR_PATTERN_EFFECT_MISC, + GUI_COLOR_PATTERN_EFFECT_MISC, + GUI_COLOR_PATTERN_EFFECT_MISC, + GUI_COLOR_PATTERN_EFFECT_MISC, + GUI_COLOR_PATTERN_EFFECT_MISC, + GUI_COLOR_PATTERN_EFFECT_MISC, + GUI_COLOR_PATTERN_EFFECT_MISC, + GUI_COLOR_PATTERN_EFFECT_MISC, + GUI_COLOR_PATTERN_EFFECT_MISC, + GUI_COLOR_PATTERN_EFFECT_MISC, + GUI_COLOR_PATTERN_EFFECT_MISC, + + // A0-AF + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + + // B0-BF + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + + // C0-CF + GUI_COLOR_PATTERN_EFFECT_SPEED, + GUI_COLOR_PATTERN_EFFECT_SPEED, + GUI_COLOR_PATTERN_EFFECT_SPEED, + GUI_COLOR_PATTERN_EFFECT_SPEED, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + + // D0-DF + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + + // E0-FF extended effects GUI_COLOR_PATTERN_EFFECT_MISC, // E0 GUI_COLOR_PATTERN_EFFECT_PITCH, // E1 GUI_COLOR_PATTERN_EFFECT_PITCH, // E2 @@ -193,7 +426,7 @@ const FurnaceGUIColors extFxColors[32]={ GUI_COLOR_PATTERN_EFFECT_INVALID, // FC GUI_COLOR_PATTERN_EFFECT_INVALID, // FD GUI_COLOR_PATTERN_EFFECT_INVALID, // FE - GUI_COLOR_PATTERN_EFFECT_SONG, // FF + GUI_COLOR_PATTERN_EFFECT_SONG // FF }; #define D FurnaceGUIActionDef diff --git a/src/gui/guiConst.h b/src/gui/guiConst.h index c0a8cd6c3..69085c324 100644 --- a/src/gui/guiConst.h +++ b/src/gui/guiConst.h @@ -47,5 +47,4 @@ extern const FurnaceGUIActionDef guiActions[]; extern const FurnaceGUIColorDef guiColors[]; extern const int altValues[24]; extern const int vgmVersions[6]; -extern const FurnaceGUIColors fxColors[16]; -extern const FurnaceGUIColors extFxColors[32]; \ No newline at end of file +extern const FurnaceGUIColors fxColors[256]; \ No newline at end of file diff --git a/src/gui/pattern.cpp b/src/gui/pattern.cpp index 9ba22ef51..61fccb5ae 100644 --- a/src/gui/pattern.cpp +++ b/src/gui/pattern.cpp @@ -235,27 +235,7 @@ inline void FurnaceGUI::patternRow(int i, bool isPlaying, float lineHeight, int } else { const unsigned char data=pat->data[i][index]; sprintf(id,"%.2X##PE%d_%d_%d",data,k,i,j); - if (data<0x10) { - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[fxColors[data]]); - } else if (data<0x20) { - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY]); - } else if (data<0x30) { - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_SYS_SECONDARY]); - } else if (data<0x48) { - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY]); - } else if (data<0x90) { - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_INVALID]); - } else if (data<0xa0) { - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_MISC]); - } else if (data<0xc0) { - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_INVALID]); - } else if (data<0xd0) { - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_SPEED]); - } else if (data<0xe0) { - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_INVALID]); - } else { - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[extFxColors[data-0xe0]]); - } + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[fxColors[data]]); } } ImGui::SameLine(0.0f,0.0f); @@ -721,17 +701,14 @@ void FurnaceGUI::drawPattern() { break; } case DIV_CMD_PANNING: { - if (i.value==0) { - num=0; - break; - } - float ratio=float(((i.value>>4)&15)-(i.value&15))/MAX(((i.value>>4)&15),(i.value&15)); + float ratio=(float)(128-e->convertPanSplitToLinearLR(i.value,i.value2,256))/128.0f; + logV("ratio %f",ratio); speedX=-22.0f*sin(ratio*M_PI*0.5); speedY=-22.0f*cos(ratio*M_PI*0.5); spread=5.0f+fabs(sin(ratio*M_PI*0.5))*7.0f; grav=0.0f; frict=0.96f; - if (((i.value>>4)&15)==(i.value&15)) { + if (i.value==i.value2) { partIcon=ICON_FA_ARROWS_H; } else if (ratio>0) { partIcon=ICON_FA_ARROW_LEFT; From d79e5d77a770288174570e6d0cf09471f154edf9 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 29 Apr 2022 23:53:15 -0500 Subject: [PATCH 5/9] GUI: finally implement "don't scroll when moving c ursor" setting --- TODO.md | 4 +--- src/gui/cursor.cpp | 4 +++- src/gui/gui.cpp | 4 +++- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/TODO.md b/TODO.md index c813fbfe8..a3e829866 100644 --- a/TODO.md +++ b/TODO.md @@ -29,7 +29,6 @@ - try to find out why does VSlider not accept keyboard input - finish lock layout - if macros have release, note off should release them -- add "don't scroll on cursor movement" option - add ability to select entire row when clicking on row number - store edit/followOrders/followPattern state in config - add ability to select a column by double clicking @@ -38,5 +37,4 @@ - settings: OK/Cancel buttons should be always visible - Apply button in settings - better FM chip names (number and codename) -- find and replace -- precise panning effects (80xx linear, 81xx/82xx per-channel) +- find and replace \ No newline at end of file diff --git a/src/gui/cursor.cpp b/src/gui/cursor.cpp index 1fdf64d77..82f4a16a7 100644 --- a/src/gui/cursor.cpp +++ b/src/gui/cursor.cpp @@ -193,7 +193,9 @@ void FurnaceGUI::moveCursor(int x, int y, bool select) { selStart=cursor; } selEnd=cursor; - updateScroll(cursor.y); + if (!settings.cursorMoveNoScroll) { + updateScroll(cursor.y); + } e->setMidiBaseChan(cursor.xCoarse); } diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index ee94e8fe8..e82a16aed 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -2350,7 +2350,9 @@ bool FurnaceGUI::loop() { demandScrollX=true; if (cursor.xCoarse==selStart.xCoarse && cursor.xFine==selStart.xFine && cursor.y==selStart.y && cursor.xCoarse==selEnd.xCoarse && cursor.xFine==selEnd.xFine && cursor.y==selEnd.y) { - updateScroll(cursor.y); + if (!settings.cursorMoveNoScroll) { + updateScroll(cursor.y); + } } } break; From 52c0044bd9f60bdbf60a9a4034580f9e7dcd8d7b Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 30 Apr 2022 01:37:37 -0500 Subject: [PATCH 6/9] GUI: add ability to customize startup system --- TODO.md | 2 - src/engine/engine.cpp | 119 +++++++++++++++++-- src/engine/engine.h | 6 + src/gui/gui.cpp | 2 +- src/gui/gui.h | 3 +- src/gui/settings.cpp | 119 ++++++++++++++++++- src/gui/sysConf.cpp | 270 ++++++++++++++++++++---------------------- 7 files changed, 361 insertions(+), 160 deletions(-) diff --git a/TODO.md b/TODO.md index a3e829866..09f794ab7 100644 --- a/TODO.md +++ b/TODO.md @@ -13,12 +13,10 @@ - maybe YMU759 ADPCM channel - ADPCM chips - more effects for FM param control -- ability to customize startup system - store system presets in new file - Game Boy envelope macro/sequence - option to display chip names instead of "multi-system" on title bar - rewrite the system name detection function anyway -- add nightly.link - scroll instrument/wave/sample list when selecting item - unified data view - volume commands should work on Game Boy diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index edd7dcc46..47d70093b 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -620,6 +620,98 @@ void DivEngine::renderSamples() { x1_010MemLen=memPos+256; } +String DivEngine::encodeSysDesc(std::vector& desc) { + String ret; + if (desc[0]!=0) { + int index=0; + for (size_t i=0; i=32) break; + } + } + return ret; +} + +std::vector DivEngine::decodeSysDesc(String desc) { + std::vector ret; + bool hasVal=false; + bool negative=false; + int val=0; + int curStage=0; + int sysID, sysVol, sysPan, sysFlags; + desc+=' '; // ha + for (char i: desc) { + switch (i) { + case ' ': + if (hasVal) { + if (negative) val=-val; + switch (curStage) { + case 0: + sysID=val; + curStage++; + break; + case 1: + sysVol=val; + curStage++; + break; + case 2: + sysPan=val; + curStage++; + break; + case 3: + sysFlags=val; + + if (systemFromFileFur(sysID)!=0) { + if (sysVol<-128) sysVol=-128; + if (sysVol>127) sysVol=127; + if (sysPan<-128) sysPan=-128; + if (sysPan>127) sysPan=127; + ret.push_back(systemFromFileFur(sysID)); + ret.push_back(sysVol); + ret.push_back(sysPan); + ret.push_back(sysFlags); + } + + curStage=0; + break; + } + hasVal=false; + negative=false; + val=0; + } + break; + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + val=(val*10)+(i-'0'); + hasVal=true; + break; + case '-': + if (!hasVal) negative=true; + break; + } + } + return ret; +} + +void DivEngine::initSongWithDesc(const int* description) { + int chanCount=0; + if (description[0]!=0) { + int index=0; + for (int i=0; description[i]; i+=4) { + song.system[index]=(DivSystem)description[i]; + song.systemVol[index]=description[i+1]; + song.systemPan[index]=description[i+2]; + song.systemFlags[index]=description[i+3]; + index++; + chanCount+=getChannelCount(song.system[index]); + if (chanCount>=63) break; + if (index>=32) break; + } + song.systemLen=index; + } +} + void DivEngine::createNew(const int* description) { quitDispatch(); BUSY_BEGIN; @@ -627,18 +719,7 @@ void DivEngine::createNew(const int* description) { song.unload(); song=DivSong(); if (description!=NULL) { - if (description[0]!=0) { - int index=0; - for (int i=0; description[i]; i+=4) { - song.system[index]=(DivSystem)description[i]; - song.systemVol[index]=description[i+1]; - song.systemPan[index]=description[i+2]; - song.systemFlags[index]=description[i+3]; - index++; - if (index>=32) break; - } - song.systemLen=index; - } + initSongWithDesc(description); } recalcChans(); renderSamples(); @@ -1207,6 +1288,8 @@ void DivEngine::recalcChans() { for (int i=0; i preset=decodeSysDesc(getConfString("initialSys","")); + logI("preset size %ld",preset.size()); + if (preset.size()>0 && (preset.size()&3)==0) { + preset.push_back(0); + initSongWithDesc(preset.data()); + } + hasLoadedSomething=true; + } + // init the rest of engine bool haveAudio=false; if (!initAudioBackend()) { diff --git a/src/engine/engine.h b/src/engine/engine.h index e3a0f71c2..7dd7489cb 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -298,6 +298,7 @@ class DivEngine { bool midiIsDirect; bool lowLatency; bool systemsRegistered; + bool hasLoadedSomething; int softLockCount; int subticks, ticks, curRow, curOrder, remainingLoops, nextSpeed; double divider; @@ -398,6 +399,7 @@ class DivEngine { bool deinitAudioBackend(); void registerSystems(); + void initSongWithDesc(const int* description); void exchangeIns(int one, int two); void swapChannels(int src, int dest); @@ -420,6 +422,9 @@ class DivEngine { DivInstrument* getIns(int index, DivInstrumentType fallbackType=DIV_INS_FM); DivWavetable* getWave(int index); DivSample* getSample(int index); + // parse system setup description + String encodeSysDesc(std::vector& desc); + std::vector decodeSysDesc(String desc); // start fresh void createNew(const int* description); // load a file. @@ -876,6 +881,7 @@ class DivEngine { midiIsDirect(false), lowLatency(false), systemsRegistered(false), + hasLoadedSomething(false), softLockCount(0), subticks(0), ticks(0), diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index e82a16aed..932a7f81b 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -2662,7 +2662,7 @@ bool FurnaceGUI::loop() { if (ImGui::BeginMenu("configure system...")) { for (int i=0; isong.systemLen; i++) { if (ImGui::TreeNode(fmt::sprintf("%d. %s##_SYSP%d",i+1,getSystemName(e->song.system[i]),i).c_str())) { - drawSysConf(i); + drawSysConf(i,e->song.system[i],e->song.systemFlags[i],true); ImGui::TreePop(); } } diff --git a/src/gui/gui.h b/src/gui/gui.h index 31ab566f1..9608e5eb7 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -857,6 +857,7 @@ class FurnaceGUI { String audioDevice; String midiInDevice; String midiOutDevice; + std::vector initialSys; Settings(): mainFontSize(18), @@ -1114,7 +1115,7 @@ class FurnaceGUI { void drawAlgorithm(unsigned char alg, FurnaceGUIFMAlgs algType, const ImVec2& size); void drawFMEnv(unsigned char tl, unsigned char ar, unsigned char dr, unsigned char d2r, unsigned char rr, unsigned char sl, unsigned char sus, unsigned char egt, unsigned char algOrGlobalSus, float maxTl, float maxArDr, const ImVec2& size, unsigned short instType); void drawGBEnv(unsigned char vol, unsigned char len, unsigned char sLen, bool dir, const ImVec2& size); - void drawSysConf(int i); + void drawSysConf(int chan, DivSystem type, unsigned int& flags, bool modifyOnChange); // these ones offer ctrl-wheel fine value changes. bool CWSliderScalar(const char* label, ImGuiDataType data_type, void* p_data, const void* p_min, const void* p_max, const char* format=NULL, ImGuiSliderFlags flags=0); diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index 6f8640871..9eeeeae9d 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -23,6 +23,7 @@ #include "../fileutils.h" #include "util.h" #include "guiConst.h" +#include "intConst.h" #include "ImGuiFileDialog.h" #include "IconsFontAwesome4.h" #include "misc/cpp/imgui_stdlib.h" @@ -219,7 +220,8 @@ void FurnaceGUI::drawSettings() { } if (ImGui::BeginTabBar("settingsTab")) { if (ImGui::BeginTabItem("General")) { - ImGui::Text("Workspace layout"); + ImGui::Text("Workspace layout:"); + ImGui::SameLine(); if (ImGui::Button("Import")) { openFileDialog(GUI_FILE_IMPORT_LAYOUT); } @@ -231,7 +233,108 @@ void FurnaceGUI::drawSettings() { if (ImGui::Button("Reset")) { showWarning("Are you sure you want to reset the workspace layout?",GUI_WARN_RESET_LAYOUT); } + ImGui::Separator(); + + ImGui::Text("Initial system/chips:"); + ImGui::SameLine(); + if (ImGui::Button("Current systems")) { + settings.initialSys.clear(); + for (int i=0; isong.systemLen; i++) { + settings.initialSys.push_back(e->song.system[i]); + settings.initialSys.push_back(e->song.systemVol[i]); + settings.initialSys.push_back(e->song.systemPan[i]); + settings.initialSys.push_back(e->song.systemFlags[i]); + } + } + ImGui::SameLine(); + if (ImGui::Button("Randomize")) { + settings.initialSys.clear(); + int howMany=1+rand()%3; + int totalAvailSys=0; + for (totalAvailSys=0; availableSystems[totalAvailSys]; totalAvailSys++); + if (totalAvailSys>0) { + for (int i=0; i=8) { + settings.initialSys.erase(settings.initialSys.begin()+i,settings.initialSys.begin()+i+4); + i-=4; + } + } + + if (ImGui::Button(ICON_FA_PLUS "##InitSysAdd")) { + settings.initialSys.push_back(DIV_SYSTEM_YM2612); + settings.initialSys.push_back(64); + settings.initialSys.push_back(0); + settings.initialSys.push_back(0); + } + + ImGui::Separator(); + ImGui::Text("Toggle channel solo on:"); if (ImGui::RadioButton("Right-click or double-click##soloA",settings.soloAction==0)) { settings.soloAction=0; @@ -1755,6 +1858,19 @@ void FurnaceGUI::syncSettings() { clampSetting(settings.moveWindowTitle,0,1); clampSetting(settings.hiddenSystems,0,1); + settings.initialSys=e->decodeSysDesc(e->getConfString("initialSys","")); + if (settings.initialSys.size()<4) { + settings.initialSys.clear(); + settings.initialSys.push_back(DIV_SYSTEM_YM2612); + settings.initialSys.push_back(64); + settings.initialSys.push_back(0); + settings.initialSys.push_back(0); + settings.initialSys.push_back(DIV_SYSTEM_SMS); + settings.initialSys.push_back(32); + settings.initialSys.push_back(0); + settings.initialSys.push_back(0); + } + // keybinds for (int i=0; isetConf("eventDelay",settings.eventDelay); e->setConf("moveWindowTitle",settings.moveWindowTitle); e->setConf("hiddenSystems",settings.hiddenSystems); + e->setConf("initialSys",e->encodeSysDesc(settings.initialSys)); // colors for (int i=0; isong.systemFlags[i]; - bool restart=settings.restartOnFlagChange; +void FurnaceGUI::drawSysConf(int chan, DivSystem type, unsigned int& flags, bool modifyOnChange) { + bool restart=settings.restartOnFlagChange && modifyOnChange; bool sysPal=flags&1; - switch (e->song.system[i]) { + unsigned int copyOfFlags=flags; + switch (type) { case DIV_SYSTEM_YM2612: case DIV_SYSTEM_YM2612_EXT: { if (ImGui::RadioButton("NTSC (7.67MHz)",(flags&3)==0)) { - e->setSysFlags(i,(flags&0x80000000)|0,restart); - updateWindowTitle(); + copyOfFlags=(flags&0x80000000)|0; } if (ImGui::RadioButton("PAL (7.61MHz)",(flags&3)==1)) { - e->setSysFlags(i,(flags&0x80000000)|1,restart); - updateWindowTitle(); + copyOfFlags=(flags&0x80000000)|1; } if (ImGui::RadioButton("FM Towns (8MHz)",(flags&3)==2)) { - e->setSysFlags(i,(flags&0x80000000)|2,restart); - updateWindowTitle(); + copyOfFlags=(flags&0x80000000)|2; } if (ImGui::RadioButton("AtGames Genesis (6.13MHz)",(flags&3)==3)) { - e->setSysFlags(i,(flags&0x80000000)|3,restart); - updateWindowTitle(); + copyOfFlags=(flags&0x80000000)|3; } bool ladder=flags&0x80000000; if (ImGui::Checkbox("Enable DAC distortion",&ladder)) { - e->setSysFlags(i,(flags&(~0x80000000))|(ladder?0x80000000:0),restart); - updateWindowTitle(); + copyOfFlags=(flags&(~0x80000000))|(ladder?0x80000000:0); } break; } case DIV_SYSTEM_SMS: { ImGui::Text("Clock rate:"); if (ImGui::RadioButton("NTSC (3.58MHz)",(flags&3)==0)) { - e->setSysFlags(i,(flags&(~3))|0,restart); - updateWindowTitle(); + copyOfFlags=(flags&(~3))|0; + } if (ImGui::RadioButton("PAL (3.55MHz)",(flags&3)==1)) { - e->setSysFlags(i,(flags&(~3))|1,restart); - updateWindowTitle(); + copyOfFlags=(flags&(~3))|1; + } if (ImGui::RadioButton("BBC Micro (4MHz)",(flags&3)==2)) { - e->setSysFlags(i,(flags&(~3))|2,restart); - updateWindowTitle(); + copyOfFlags=(flags&(~3))|2; + } if (ImGui::RadioButton("Half NTSC (1.79MHz)",(flags&3)==3)) { - e->setSysFlags(i,(flags&(~3))|3,restart); - updateWindowTitle(); + copyOfFlags=(flags&(~3))|3; + } ImGui::Text("Chip type:"); if (ImGui::RadioButton("Sega VDP/Master System",((flags>>2)&3)==0)) { - e->setSysFlags(i,(flags&(~12))|0,restart); - updateWindowTitle(); + copyOfFlags=(flags&(~12))|0; + } if (ImGui::RadioButton("TI SN76489",((flags>>2)&3)==1)) { - e->setSysFlags(i,(flags&(~12))|4,restart); - updateWindowTitle(); + copyOfFlags=(flags&(~12))|4; + } if (ImGui::RadioButton("TI SN76489 with Atari-like short noise",((flags>>2)&3)==2)) { - e->setSysFlags(i,(flags&(~12))|8,restart); - updateWindowTitle(); + copyOfFlags=(flags&(~12))|8; + } /*if (ImGui::RadioButton("Game Gear",(flags>>2)==3)) { - e->setSysFlags(i,(flags&3)|12); + copyOfFlags=(flags&3)|12); }*/ bool noPhaseReset=flags&16; if (ImGui::Checkbox("Disable noise period change phase reset",&noPhaseReset)) { - e->setSysFlags(i,(flags&(~16))|(noPhaseReset<<4),restart); - updateWindowTitle(); + copyOfFlags=(flags&(~16))|(noPhaseReset<<4); + } break; } @@ -96,54 +91,54 @@ void FurnaceGUI::drawSysConf(int i) { case DIV_SYSTEM_VRC7: { ImGui::Text("Clock rate:"); if (ImGui::RadioButton("NTSC (3.58MHz)",(flags&15)==0)) { - e->setSysFlags(i,(flags&(~15))|0,restart); - updateWindowTitle(); + copyOfFlags=(flags&(~15))|0; + } if (ImGui::RadioButton("PAL (3.55MHz)",(flags&15)==1)) { - e->setSysFlags(i,(flags&(~15))|1,restart); - updateWindowTitle(); + copyOfFlags=(flags&(~15))|1; + } if (ImGui::RadioButton("BBC Micro (4MHz)",(flags&15)==2)) { - e->setSysFlags(i,(flags&(~15))|2,restart); - updateWindowTitle(); + copyOfFlags=(flags&(~15))|2; + } if (ImGui::RadioButton("Half NTSC (1.79MHz)",(flags&15)==3)) { - e->setSysFlags(i,(flags&(~15))|3,restart); - updateWindowTitle(); + copyOfFlags=(flags&(~15))|3; + } - if (e->song.system[i]!=DIV_SYSTEM_VRC7) { + if (type!=DIV_SYSTEM_VRC7) { ImGui::Text("Patch set:"); if (ImGui::RadioButton("Yamaha YM2413",((flags>>4)&15)==0)) { - e->setSysFlags(i,(flags&(~0xf0))|0,restart); - updateWindowTitle(); + copyOfFlags=(flags&(~0xf0))|0; + } if (ImGui::RadioButton("Yamaha YMF281",((flags>>4)&15)==1)) { - e->setSysFlags(i,(flags&(~0xf0))|0x10,restart); - updateWindowTitle(); + copyOfFlags=(flags&(~0xf0))|0x10; + } if (ImGui::RadioButton("Yamaha YM2423",((flags>>4)&15)==2)) { - e->setSysFlags(i,(flags&(~0xf0))|0x20,restart); - updateWindowTitle(); + copyOfFlags=(flags&(~0xf0))|0x20; + } if (ImGui::RadioButton("Konami VRC7",((flags>>4)&15)==3)) { - e->setSysFlags(i,(flags&(~0xf0))|0x30,restart); - updateWindowTitle(); + copyOfFlags=(flags&(~0xf0))|0x30; + } } break; } case DIV_SYSTEM_YM2151: if (ImGui::RadioButton("NTSC/X16 (3.58MHz)",flags==0)) { - e->setSysFlags(i,0,restart); - updateWindowTitle(); + copyOfFlags=0; + } if (ImGui::RadioButton("PAL (3.55MHz)",flags==1)) { - e->setSysFlags(i,1,restart); - updateWindowTitle(); + copyOfFlags=1; + } if (ImGui::RadioButton("X1/X68000 (4MHz)",flags==2)) { - e->setSysFlags(i,2,restart); - updateWindowTitle(); + copyOfFlags=2; + } break; case DIV_SYSTEM_NES: @@ -151,120 +146,117 @@ void FurnaceGUI::drawSysConf(int i) { case DIV_SYSTEM_FDS: case DIV_SYSTEM_MMC5: if (ImGui::RadioButton("NTSC (1.79MHz)",flags==0)) { - e->setSysFlags(i,0,restart); - updateWindowTitle(); + copyOfFlags=0; + } if (ImGui::RadioButton("PAL (1.67MHz)",flags==1)) { - e->setSysFlags(i,1,restart); - updateWindowTitle(); + copyOfFlags=1; + } if (ImGui::RadioButton("Dendy (1.77MHz)",flags==2)) { - e->setSysFlags(i,2,restart); - updateWindowTitle(); + copyOfFlags=2; + } break; case DIV_SYSTEM_C64_8580: case DIV_SYSTEM_C64_6581: if (ImGui::RadioButton("NTSC (1.02MHz)",flags==0)) { - e->setSysFlags(i,0,restart); - updateWindowTitle(); + copyOfFlags=0; + } if (ImGui::RadioButton("PAL (0.99MHz)",flags==1)) { - e->setSysFlags(i,1,restart); - updateWindowTitle(); + copyOfFlags=1; + } if (ImGui::RadioButton("SSI 2001 (0.89MHz)",flags==2)) { - e->setSysFlags(i,2,restart); - updateWindowTitle(); + copyOfFlags=2; + } break; case DIV_SYSTEM_AY8910: case DIV_SYSTEM_AY8930: { ImGui::Text("Clock rate:"); if (ImGui::RadioButton("1.79MHz (ZX Spectrum NTSC/MSX)",(flags&15)==0)) { - e->setSysFlags(i,(flags&(~15))|0,restart); - updateWindowTitle(); + copyOfFlags=(flags&(~15))|0; + } if (ImGui::RadioButton("1.77MHz (ZX Spectrum)",(flags&15)==1)) { - e->setSysFlags(i,(flags&(~15))|1,restart); - updateWindowTitle(); + copyOfFlags=(flags&(~15))|1; + } if (ImGui::RadioButton("1.75MHz (ZX Spectrum)",(flags&15)==2)) { - e->setSysFlags(i,(flags&(~15))|2,restart); - updateWindowTitle(); + copyOfFlags=(flags&(~15))|2; + } if (ImGui::RadioButton("2MHz (Atari ST/Sharp X1)",(flags&15)==3)) { - e->setSysFlags(i,(flags&(~15))|3,restart); - updateWindowTitle(); + copyOfFlags=(flags&(~15))|3; + } if (ImGui::RadioButton("1.5MHz (Vectrex)",(flags&15)==4)) { - e->setSysFlags(i,(flags&(~15))|4,restart); - updateWindowTitle(); + copyOfFlags=(flags&(~15))|4; + } if (ImGui::RadioButton("1MHz (Amstrad CPC)",(flags&15)==5)) { - e->setSysFlags(i,(flags&(~15))|5,restart); - updateWindowTitle(); + copyOfFlags=(flags&(~15))|5; + } if (ImGui::RadioButton("0.89MHz (Sunsoft 5B)",(flags&15)==6)) { - e->setSysFlags(i,(flags&(~15))|6,restart); - updateWindowTitle(); + copyOfFlags=(flags&(~15))|6; + } if (ImGui::RadioButton("1.67MHz (?)",(flags&15)==7)) { - e->setSysFlags(i,(flags&(~15))|7,restart); - updateWindowTitle(); + copyOfFlags=(flags&(~15))|7; + } if (ImGui::RadioButton("0.83MHz (Sunsoft 5B on PAL)",(flags&15)==8)) { - e->setSysFlags(i,(flags&(~15))|8,restart); - updateWindowTitle(); + copyOfFlags=(flags&(~15))|8; + } if (ImGui::RadioButton("1.10MHz (Gamate/VIC-20 PAL)",(flags&15)==9)) { - e->setSysFlags(i,(flags&(~15))|9,restart); - updateWindowTitle(); + copyOfFlags=(flags&(~15))|9; + } if (ImGui::RadioButton("2^21Hz (Game Boy)",(flags&15)==10)) { - e->setSysFlags(i,(flags&(~15))|10,restart); - updateWindowTitle(); + copyOfFlags=(flags&(~15))|10; + } - if (e->song.system[i]==DIV_SYSTEM_AY8910) { + if (type==DIV_SYSTEM_AY8910) { ImGui::Text("Chip type:"); if (ImGui::RadioButton("AY-3-8910",(flags&0x30)==0)) { - e->setSysFlags(i,(flags&(~0x30))|0,restart); - updateWindowTitle(); + copyOfFlags=(flags&(~0x30))|0; + } if (ImGui::RadioButton("YM2149(F)",(flags&0x30)==16)) { - e->setSysFlags(i,(flags&(~0x30))|16,restart); - updateWindowTitle(); + copyOfFlags=(flags&(~0x30))|16; + } if (ImGui::RadioButton("Sunsoft 5B",(flags&0x30)==32)) { - e->setSysFlags(i,(flags&(~0x30))|32,restart); - updateWindowTitle(); + copyOfFlags=(flags&(~0x30))|32; + } if (ImGui::RadioButton("AY-3-8914",(flags&0x30)==48)) { - e->setSysFlags(i,(flags&(~0x30))|48,restart); - updateWindowTitle(); + copyOfFlags=(flags&(~0x30))|48; + } } bool stereo=flags&0x40; ImGui::BeginDisabled((flags&0x30)==32); if (ImGui::Checkbox("Stereo##_AY_STEREO",&stereo)) { - e->setSysFlags(i,(flags&(~0x40))|(stereo?0x40:0),restart); - updateWindowTitle(); + copyOfFlags=(flags&(~0x40))|(stereo?0x40:0); + } ImGui::EndDisabled(); break; } case DIV_SYSTEM_SAA1099: if (ImGui::RadioButton("SAM Coupé (8MHz)",flags==0)) { - e->setSysFlags(i,0,restart); - updateWindowTitle(); + copyOfFlags=0; } if (ImGui::RadioButton("NTSC (7.15MHz)",flags==1)) { - e->setSysFlags(i,1,restart); - updateWindowTitle(); + copyOfFlags=1; } if (ImGui::RadioButton("PAL (7.09MHz)",flags==2)) { - e->setSysFlags(i,2,restart); - updateWindowTitle(); + copyOfFlags=2; } break; case DIV_SYSTEM_AMIGA: { @@ -273,44 +265,37 @@ void FurnaceGUI::drawSysConf(int i) { if (CWSliderInt("##StereoSep",&stereoSep,0,127)) { if (stereoSep<0) stereoSep=0; if (stereoSep>127) stereoSep=127; - e->setSysFlags(i,(flags&(~0x7f00))|((stereoSep&127)<<8),restart); - updateWindowTitle(); + copyOfFlags=(flags&(~0x7f00))|((stereoSep&127)<<8); } rightClickable if (ImGui::RadioButton("Amiga 500 (OCS)",(flags&2)==0)) { - e->setSysFlags(i,flags&(~2),restart); + copyOfFlags=flags&(~2); } if (ImGui::RadioButton("Amiga 1200 (AGA)",(flags&2)==2)) { - e->setSysFlags(i,(flags&(~2))|2,restart); + copyOfFlags=(flags&(~2))|2; } sysPal=flags&1; if (ImGui::Checkbox("PAL",&sysPal)) { - e->setSysFlags(i,(flags&(~1))|(unsigned int)sysPal,restart); - updateWindowTitle(); + copyOfFlags=(flags&(~1))|(unsigned int)sysPal; } bool bypassLimits=flags&4; if (ImGui::Checkbox("Bypass frequency limits",&bypassLimits)) { - e->setSysFlags(i,(flags&(~4))|(bypassLimits<<2),restart); - updateWindowTitle(); + copyOfFlags=(flags&(~4))|(bypassLimits<<2); } break; } case DIV_SYSTEM_PCSPKR: { ImGui::Text("Speaker type:"); if (ImGui::RadioButton("Unfiltered",(flags&3)==0)) { - e->setSysFlags(i,(flags&(~3))|0,restart); - updateWindowTitle(); + copyOfFlags=(flags&(~3))|0; } if (ImGui::RadioButton("Cone",(flags&3)==1)) { - e->setSysFlags(i,(flags&(~3))|1,restart); - updateWindowTitle(); + copyOfFlags=(flags&(~3))|1; } if (ImGui::RadioButton("Piezo",(flags&3)==2)) { - e->setSysFlags(i,(flags&(~3))|2,restart); - updateWindowTitle(); + copyOfFlags=(flags&(~3))|2; } if (ImGui::RadioButton("Use system beeper (Linux only!)",(flags&3)==3)) { - e->setSysFlags(i,(flags&(~3))|3,restart); - updateWindowTitle(); + copyOfFlags=(flags&(~3))|3; } break; } @@ -320,62 +305,53 @@ void FurnaceGUI::drawSysConf(int i) { if (CWSliderInt("##EchoBufSize",&echoBufSize,0,2725)) { if (echoBufSize<0) echoBufSize=0; if (echoBufSize>2725) echoBufSize=2725; - e->setSysFlags(i,(flags & ~4095) | ((2725 - echoBufSize) & 4095),restart); - updateWindowTitle(); + copyOfFlags=(flags & ~4095) | ((2725 - echoBufSize) & 4095); } rightClickable ImGui::Text("Echo feedback:"); int echoFeedback=(flags>>12)&255; if (CWSliderInt("##EchoFeedback",&echoFeedback,0,255)) { if (echoFeedback<0) echoFeedback=0; if (echoFeedback>255) echoFeedback=255; - e->setSysFlags(i,(flags & ~(255 << 12)) | ((echoFeedback & 255) << 12),restart); - updateWindowTitle(); + copyOfFlags=(flags & ~(255 << 12)) | ((echoFeedback & 255) << 12); } rightClickable break; } case DIV_SYSTEM_X1_010: { ImGui::Text("Clock rate:"); if (ImGui::RadioButton("16MHz (Seta 1)",(flags&15)==0)) { - e->setSysFlags(i,(flags&(~15))|0,restart); - updateWindowTitle(); + copyOfFlags=(flags&(~15))|0; } if (ImGui::RadioButton("16.67MHz (Seta 2)",(flags&15)==1)) { - e->setSysFlags(i,(flags&(~15))|1,restart); - updateWindowTitle(); + copyOfFlags=(flags&(~15))|1; } bool x1_010Stereo=flags&16; if (ImGui::Checkbox("Stereo",&x1_010Stereo)) { - e->setSysFlags(i,(flags&(~16))|(x1_010Stereo<<4),restart); - updateWindowTitle(); + copyOfFlags=(flags&(~16))|(x1_010Stereo<<4); } break; } case DIV_SYSTEM_N163: { ImGui::Text("Clock rate:"); if (ImGui::RadioButton("NTSC (1.79MHz)",(flags&15)==0)) { - e->setSysFlags(i,(flags&(~15))|0,restart); - updateWindowTitle(); + copyOfFlags=(flags&(~15))|0; } if (ImGui::RadioButton("PAL (1.67MHz)",(flags&15)==1)) { - e->setSysFlags(i,(flags&(~15))|1,restart); - updateWindowTitle(); + copyOfFlags=(flags&(~15))|1; } if (ImGui::RadioButton("Dendy (1.77MHz)",(flags&15)==2)) { - e->setSysFlags(i,(flags&(~15))|2,restart); - updateWindowTitle(); + copyOfFlags=(flags&(~15))|2; } ImGui::Text("Initial channel limit:"); int initialChannelLimit=((flags>>4)&7)+1; if (CWSliderInt("##InitialChannelLimit",&initialChannelLimit,1,8)) { if (initialChannelLimit<1) initialChannelLimit=1; if (initialChannelLimit>8) initialChannelLimit=8; - e->setSysFlags(i,(flags & ~(7 << 4)) | (((initialChannelLimit-1) & 7) << 4),restart); - updateWindowTitle(); + copyOfFlags=(flags & ~(7 << 4)) | (((initialChannelLimit-1) & 7) << 4); + } rightClickable bool n163Multiplex=flags&128; if (ImGui::Checkbox("Disable hissing",&n163Multiplex)) { - e->setSysFlags(i,(flags&(~128))|(n163Multiplex<<7),restart); - updateWindowTitle(); + copyOfFlags=(flags&(~128))|(n163Multiplex<<7); } break; } @@ -395,9 +371,17 @@ void FurnaceGUI::drawSysConf(int i) { break; default: if (ImGui::Checkbox("PAL",&sysPal)) { - e->setSysFlags(i,sysPal,restart); - updateWindowTitle(); + copyOfFlags=sysPal; } break; } + + if (copyOfFlags!=flags) { + if (chan>=0) { + e->setSysFlags(chan,copyOfFlags,restart); + updateWindowTitle(); + } else { + flags=copyOfFlags; + } + } } From ba657fe4db68f40685dba5393093de3a94e34b72 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 30 Apr 2022 02:02:55 -0500 Subject: [PATCH 7/9] GUI: add backdrop if system file picker is open --- TODO.md | 1 - src/gui/fileDialog.cpp | 4 ++++ src/gui/fileDialog.h | 1 + src/gui/gui.cpp | 13 +++++++++++++ 4 files changed, 18 insertions(+), 1 deletion(-) diff --git a/TODO.md b/TODO.md index 09f794ab7..18c43c49d 100644 --- a/TODO.md +++ b/TODO.md @@ -13,7 +13,6 @@ - maybe YMU759 ADPCM channel - ADPCM chips - more effects for FM param control -- store system presets in new file - Game Boy envelope macro/sequence - option to display chip names instead of "multi-system" on title bar - rewrite the system name detection function anyway diff --git a/src/gui/fileDialog.cpp b/src/gui/fileDialog.cpp index 3b8fdd46a..e50298c1a 100644 --- a/src/gui/fileDialog.cpp +++ b/src/gui/fileDialog.cpp @@ -95,6 +95,10 @@ bool FurnaceGUIFileDialog::render(const ImVec2& min, const ImVec2& max) { } } +bool FurnaceGUIFileDialog::isOpen() { + return opened; +} + String FurnaceGUIFileDialog::getPath() { if (sysDialog) { if (curPath.size()>1) { diff --git a/src/gui/fileDialog.h b/src/gui/fileDialog.h index 8d3ff787a..5eb67d853 100644 --- a/src/gui/fileDialog.h +++ b/src/gui/fileDialog.h @@ -24,6 +24,7 @@ class FurnaceGUIFileDialog { bool accepted(); void close(); bool render(const ImVec2& min, const ImVec2& max); + bool isOpen(); String getPath(); String getFileName(); explicit FurnaceGUIFileDialog(bool system): diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 932a7f81b..dd37ceae1 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -2882,6 +2882,19 @@ bool FurnaceGUI::loop() { #endif } + if (fileDialog->isOpen() && settings.sysFileDialog) { + ImGui::OpenPopup("System File Dialog Pending"); + } + + if (ImGui::BeginPopupModal("System File Dialog Pending",NULL,ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoBackground|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoMove)) { + if (!fileDialog->isOpen()) { + ImGui::CloseCurrentPopup(); + } + ImDrawList* dl=ImGui::GetForegroundDrawList(); + dl->AddRectFilled(ImVec2(0.0f,0.0f),ImVec2(scrW*dpiScale,scrH*dpiScale),ImGui::ColorConvertFloat4ToU32(uiColors[GUI_COLOR_MODAL_BACKDROP])); + ImGui::EndPopup(); + } + if (fileDialog->render(ImVec2(600.0f*dpiScale,400.0f*dpiScale),ImVec2(scrW*dpiScale,scrH*dpiScale))) { bool openOpen=false; //ImGui::GetIO().ConfigFlags&=~ImGuiConfigFlags_NavEnableKeyboard; From 4197fa44fb0779d1260e26fcc0a82ec4c6915307 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 30 Apr 2022 03:58:30 -0500 Subject: [PATCH 8/9] GUI: prepare for a per-channel oscilloscope? currently supported systems: - Amiga - AY-3-8910 - Dummy System - OPLL - SegaPCM no trigger supported yet! --- CMakeLists.txt | 1 + src/engine/dispatch.h | 19 ++++++ src/engine/engine.cpp | 10 +++ src/engine/engine.h | 6 ++ src/engine/platform/abstract.cpp | 4 ++ src/engine/platform/amiga.cpp | 30 +++++++-- src/engine/platform/amiga.h | 2 + src/engine/platform/ay.cpp | 19 +++++- src/engine/platform/ay.h | 2 + src/engine/platform/dummy.cpp | 22 ++++++- src/engine/platform/dummy.h | 2 + src/engine/platform/opll.cpp | 16 ++++- src/engine/platform/opll.h | 2 + src/engine/platform/segapcm.cpp | 15 +++++ src/engine/platform/segapcm.h | 2 + src/gui/chanOsc.cpp | 101 +++++++++++++++++++++++++++++++ src/gui/doAction.cpp | 6 ++ src/gui/gui.cpp | 9 ++- src/gui/gui.h | 12 +++- src/gui/guiConst.cpp | 3 +- 20 files changed, 269 insertions(+), 14 deletions(-) create mode 100644 src/gui/chanOsc.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 75a3d3350..b757062f2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -389,6 +389,7 @@ src/gui/guiConst.cpp src/gui/about.cpp src/gui/channels.cpp +src/gui/chanOsc.cpp src/gui/compatFlags.cpp src/gui/cursor.cpp src/gui/dataList.cpp diff --git a/src/engine/dispatch.h b/src/engine/dispatch.h index 21598442b..7e5f998b9 100644 --- a/src/engine/dispatch.h +++ b/src/engine/dispatch.h @@ -21,6 +21,7 @@ #define _DISPATCH_H #include +#include #include #define ONE_SEMITONE 2200 @@ -205,6 +206,18 @@ struct DivRegWrite { addr(a), val(v) {} }; +struct DivDispatchOscBuffer { + unsigned int rate; + unsigned short needle; + short data[65536]; + + DivDispatchOscBuffer(): + rate(65536), + needle(0) { + memset(data,0,65536*sizeof(short)); + } +}; + class DivEngine; class DivMacroInt; @@ -268,6 +281,12 @@ class DivDispatch { * @return a pointer, or NULL. */ virtual DivMacroInt* getChanMacroInt(int chan); + + /** + * get an oscilloscope buffer for a channel. + * @return a pointer to a DivDispatchOscBuffer, or NULL if not supported. + */ + virtual DivDispatchOscBuffer* getOscBuffer(int chan); /** * get the register pool of this dispatch. diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 47d70093b..f8f518933 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -986,6 +986,16 @@ unsigned char* DivEngine::getRegisterPool(int sys, int& size, int& depth) { return disCont[sys].dispatch->getRegisterPool(); } +DivMacroInt* DivEngine::getMacroInt(int chan) { + if (chan<0 || chan>=chans) return NULL; + return disCont[dispatchOfChan[chan]].dispatch->getChanMacroInt(dispatchChanOfChan[chan]); +} + +DivDispatchOscBuffer* DivEngine::getOscBuffer(int chan) { + if (chan<0 || chan>=chans) return NULL; + return disCont[dispatchOfChan[chan]].dispatch->getOscBuffer(dispatchChanOfChan[chan]); +} + void DivEngine::enableCommandStream(bool enable) { cmdStreamEnabled=enable; } diff --git a/src/engine/engine.h b/src/engine/engine.h index 7dd7489cb..2ca301c53 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -722,6 +722,12 @@ class DivEngine { // get register pool unsigned char* getRegisterPool(int sys, int& size, int& depth); + // get macro interpreter + DivMacroInt* getMacroInt(int chan); + + // get osc buffer + DivDispatchOscBuffer* getOscBuffer(int chan); + // enable command stream dumping void enableCommandStream(bool enable); diff --git a/src/engine/platform/abstract.cpp b/src/engine/platform/abstract.cpp index 56f2aa3d7..91f61fc62 100644 --- a/src/engine/platform/abstract.cpp +++ b/src/engine/platform/abstract.cpp @@ -33,6 +33,10 @@ DivMacroInt* DivDispatch::getChanMacroInt(int chan) { return NULL; } +DivDispatchOscBuffer* DivDispatch::getOscBuffer(int chan) { + return NULL; +} + unsigned char* DivDispatch::getRegisterPool() { return NULL; } diff --git a/src/engine/platform/amiga.cpp b/src/engine/platform/amiga.cpp index 406b3d406..e78c3a77b 100644 --- a/src/engine/platform/amiga.cpp +++ b/src/engine/platform/amiga.cpp @@ -91,12 +91,15 @@ const char* DivPlatformAmiga::getEffectName(unsigned char effect) { } void DivPlatformAmiga::acquire(short* bufL, short* bufR, size_t start, size_t len) { - static int outL, outR; + static int outL, outR, output; for (size_t h=start; hdata[oscBuf[i]->needle++]=0; + continue; + } if (chan[i].useWave || (chan[i].sample>=0 && chan[i].samplesong.sampleLen)) { chan[i].audSub-=AMIGA_DIVIDER; if (chan[i].audSub<0) { @@ -139,13 +142,17 @@ void DivPlatformAmiga::acquire(short* bufL, short* bufR, size_t start, size_t le } } if (!isMuted[i]) { + output=chan[i].audDat*chan[i].outVol; if (i==0 || i==3) { - outL+=((chan[i].audDat*chan[i].outVol)*sep1)>>7; - outR+=((chan[i].audDat*chan[i].outVol)*sep2)>>7; + outL+=(output*sep1)>>7; + outR+=(output*sep2)>>7; } else { - outL+=((chan[i].audDat*chan[i].outVol)*sep2)>>7; - outR+=((chan[i].audDat*chan[i].outVol)*sep1)>>7; + outL+=(output*sep2)>>7; + outR+=(output*sep1)>>7; } + oscBuf[i]->data[oscBuf[i]->needle++]=output<<2; + } else { + oscBuf[i]->data[oscBuf[i]->needle++]=0; } } filter[0][0]+=(filtConst*(outL-filter[0][0]))>>12; @@ -419,6 +426,10 @@ void* DivPlatformAmiga::getChanState(int ch) { return &chan[ch]; } +DivDispatchOscBuffer* DivPlatformAmiga::getOscBuffer(int ch) { + return oscBuf[ch]; +} + void DivPlatformAmiga::reset() { for (int i=0; i<4; i++) { chan[i]=DivPlatformAmiga::Channel(); @@ -469,6 +480,9 @@ void DivPlatformAmiga::setFlags(unsigned int flags) { chipClock=COLOR_NTSC; } rate=chipClock/AMIGA_DIVIDER; + for (int i=0; i<4; i++) { + oscBuf[i]->rate=rate; + } sep1=((flags>>8)&127)+127; sep2=127-((flags>>8)&127); amigaModel=flags&2; @@ -487,6 +501,7 @@ int DivPlatformAmiga::init(DivEngine* p, int channels, int sugRate, unsigned int dumpWrites=false; skipRegisterWrites=false; for (int i=0; i<4; i++) { + oscBuf[i]=new DivDispatchOscBuffer; isMuted[i]=false; } setFlags(flags); @@ -495,4 +510,7 @@ int DivPlatformAmiga::init(DivEngine* p, int channels, int sugRate, unsigned int } void DivPlatformAmiga::quit() { + for (int i=0; i<4; i++) { + delete oscBuf[i]; + } } diff --git a/src/engine/platform/amiga.h b/src/engine/platform/amiga.h index 0777100d7..539f7830c 100644 --- a/src/engine/platform/amiga.h +++ b/src/engine/platform/amiga.h @@ -74,6 +74,7 @@ class DivPlatformAmiga: public DivDispatch { outVol(64) {} }; Channel chan[4]; + DivDispatchOscBuffer* oscBuf[4]; bool isMuted[4]; bool bypassLimits; bool amigaModel; @@ -91,6 +92,7 @@ class DivPlatformAmiga: public DivDispatch { void acquire(short* bufL, short* bufR, size_t start, size_t len); int dispatch(DivCommand c); void* getChanState(int chan); + DivDispatchOscBuffer* getOscBuffer(int chan); void reset(); void forceIns(); void tick(bool sysTick=true); diff --git a/src/engine/platform/ay.cpp b/src/engine/platform/ay.cpp index 12ea0f64a..37108452e 100644 --- a/src/engine/platform/ay.cpp +++ b/src/engine/platform/ay.cpp @@ -146,6 +146,12 @@ void DivPlatformAY8910::acquire(short* bufL, short* bufR, size_t start, size_t l bufR[i+start]=bufL[i+start]; } } + + for (int ch=0; ch<3; ch++) { + for (size_t i=0; idata[oscBuf[ch]->needle++]=ayBuf[ch][i]; + } + } } void DivPlatformAY8910::updateOutSel(bool immediate) { @@ -500,6 +506,10 @@ void* DivPlatformAY8910::getChanState(int ch) { return &chan[ch]; } +DivDispatchOscBuffer* DivPlatformAY8910::getOscBuffer(int ch) { + return oscBuf[ch]; +} + unsigned char* DivPlatformAY8910::getRegisterPool() { return regPool; } @@ -615,6 +625,9 @@ void DivPlatformAY8910::setFlags(unsigned int flags) { break; } rate=chipClock/8; + for (int i=0; i<3; i++) { + oscBuf[i]->rate=rate; + } if (ay!=NULL) delete ay; switch ((flags>>4)&3) { @@ -650,6 +663,7 @@ int DivPlatformAY8910::init(DivEngine* p, int channels, int sugRate, unsigned in skipRegisterWrites=false; for (int i=0; i<3; i++) { isMuted[i]=false; + oscBuf[i]=new DivDispatchOscBuffer; } ay=NULL; setFlags(flags); @@ -660,6 +674,9 @@ int DivPlatformAY8910::init(DivEngine* p, int channels, int sugRate, unsigned in } void DivPlatformAY8910::quit() { - for (int i=0; i<3; i++) delete[] ayBuf[i]; + for (int i=0; i<3; i++) { + delete oscBuf[i]; + delete[] ayBuf[i]; + } if (ay!=NULL) delete ay; } diff --git a/src/engine/platform/ay.h b/src/engine/platform/ay.h index 2bc4a03d3..b257e3bbc 100644 --- a/src/engine/platform/ay.h +++ b/src/engine/platform/ay.h @@ -56,6 +56,7 @@ class DivPlatformAY8910: public DivDispatch { }; std::queue writes; ay8910_device* ay; + DivDispatchOscBuffer* oscBuf[3]; unsigned char regPool[16]; unsigned char lastBusy; @@ -90,6 +91,7 @@ class DivPlatformAY8910: public DivDispatch { void acquire(short* bufL, short* bufR, size_t start, size_t len); int dispatch(DivCommand c); void* getChanState(int chan); + DivDispatchOscBuffer* getOscBuffer(int chan); unsigned char* getRegisterPool(); int getRegisterPoolSize(); void flushWrites(); diff --git a/src/engine/platform/dummy.cpp b/src/engine/platform/dummy.cpp index 9b972ac18..34d614eb3 100644 --- a/src/engine/platform/dummy.cpp +++ b/src/engine/platform/dummy.cpp @@ -25,12 +25,21 @@ #define CHIP_FREQBASE 2048 void DivPlatformDummy::acquire(short* bufL, short* bufR, size_t start, size_t len) { + int chanOut; for (size_t i=start; i>12; + if (!isMuted[j]) { + chanOut=(((signed short)chan[j].pos)*chan[j].amp*chan[j].vol)>>12; + oscBuf[j]->data[oscBuf[j]->needle++]=chanOut; + out+=chanOut; + } else { + oscBuf[j]->data[oscBuf[j]->needle++]=0; + } chan[j].pos+=chan[j].freq; + } else { + oscBuf[j]->data[oscBuf[j]->needle++]=0; } } if (out<-32768) out=-32768; @@ -61,6 +70,10 @@ void* DivPlatformDummy::getChanState(int ch) { return &chan[ch]; } +DivDispatchOscBuffer* DivPlatformDummy::getOscBuffer(int ch) { + return oscBuf[ch]; +} + int DivPlatformDummy::dispatch(DivCommand c) { switch (c.cmd) { case DIV_CMD_NOTE_ON: @@ -131,6 +144,10 @@ int DivPlatformDummy::init(DivEngine* p, int channels, int sugRate, unsigned int skipRegisterWrites=false; for (int i=0; irate=65536; + } } rate=65536; chipClock=65536; @@ -140,6 +157,9 @@ int DivPlatformDummy::init(DivEngine* p, int channels, int sugRate, unsigned int } void DivPlatformDummy::quit() { + for (int i=0; i=6 && properDrums) || !isMuted[nextOut]) { + oscBuf[nextOut]->data[oscBuf[nextOut]->needle++]=(o[0]+o[1])<<6; os+=(o[0]+o[1]); + } else { + oscBuf[nextOut]->data[oscBuf[nextOut]->needle++]=0; } } os*=50; @@ -731,6 +734,10 @@ void* DivPlatformOPLL::getChanState(int ch) { return &chan[ch]; } +DivDispatchOscBuffer* DivPlatformOPLL::getOscBuffer(int ch) { + return oscBuf[ch]; +} + unsigned char* DivPlatformOPLL::getRegisterPool() { return regPool; } @@ -842,6 +849,9 @@ void DivPlatformOPLL::setFlags(unsigned int flags) { } rate=chipClock/36; patchSet=flags>>4; + for (int i=0; i<11; i++) { + oscBuf[i]->rate=rate/2; + } } int DivPlatformOPLL::init(DivEngine* p, int channels, int sugRate, unsigned int flags) { @@ -851,14 +861,18 @@ int DivPlatformOPLL::init(DivEngine* p, int channels, int sugRate, unsigned int patchSet=0; for (int i=0; i<11; i++) { isMuted[i]=false; + oscBuf[i]=new DivDispatchOscBuffer; } setFlags(flags); reset(); - return 10; + return 11; } void DivPlatformOPLL::quit() { + for (int i=0; i<11; i++) { + delete oscBuf[i]; + } } DivPlatformOPLL::~DivPlatformOPLL() { diff --git a/src/engine/platform/opll.h b/src/engine/platform/opll.h index 96cb1a914..7a06bbb77 100644 --- a/src/engine/platform/opll.h +++ b/src/engine/platform/opll.h @@ -63,6 +63,7 @@ class DivPlatformOPLL: public DivDispatch { }; Channel chan[11]; bool isMuted[11]; + DivDispatchOscBuffer* oscBuf[11]; struct QueuedWrite { unsigned short addr; unsigned char val; @@ -100,6 +101,7 @@ class DivPlatformOPLL: public DivDispatch { void acquire(short* bufL, short* bufR, size_t start, size_t len); int dispatch(DivCommand c); void* getChanState(int chan); + DivDispatchOscBuffer* getOscBuffer(int chan); unsigned char* getRegisterPool(); int getRegisterPoolSize(); void reset(); diff --git a/src/engine/platform/segapcm.cpp b/src/engine/platform/segapcm.cpp index eb506ff36..346daeae1 100644 --- a/src/engine/platform/segapcm.cpp +++ b/src/engine/platform/segapcm.cpp @@ -46,9 +46,11 @@ void DivPlatformSegaPCM::acquire(short* bufL, short* bufR, size_t start, size_t DivSample* s=parent->getSample(chan[i].pcm.sample); if (s->samples<=0) { chan[i].pcm.sample=-1; + oscBuf[i]->data[oscBuf[i]->needle++]=0; continue; } if (!isMuted[i]) { + oscBuf[i]->data[oscBuf[i]->needle++]=s->data8[chan[i].pcm.pos>>8]*(chan[i].chVolL+chan[i].chVolR)>>1; pcmL+=(s->data8[chan[i].pcm.pos>>8]*chan[i].chVolL); pcmR+=(s->data8[chan[i].pcm.pos>>8]*chan[i].chVolR); } @@ -60,6 +62,8 @@ void DivPlatformSegaPCM::acquire(short* bufL, short* bufR, size_t start, size_t chan[i].pcm.sample=-1; } } + } else { + oscBuf[i]->data[oscBuf[i]->needle++]=0; } } @@ -359,6 +363,10 @@ void* DivPlatformSegaPCM::getChanState(int ch) { return &chan[ch]; } +DivDispatchOscBuffer* DivPlatformSegaPCM::getOscBuffer(int ch) { + return oscBuf[ch]; +} + unsigned char* DivPlatformSegaPCM::getRegisterPool() { return regPool; } @@ -408,6 +416,9 @@ void DivPlatformSegaPCM::reset() { void DivPlatformSegaPCM::setFlags(unsigned int flags) { chipClock=8000000.0; rate=31250; + for (int i=0; i<16; i++) { + oscBuf[i]->rate=rate; + } } bool DivPlatformSegaPCM::isStereo() { @@ -420,6 +431,7 @@ int DivPlatformSegaPCM::init(DivEngine* p, int channels, int sugRate, unsigned i skipRegisterWrites=false; for (int i=0; i<16; i++) { isMuted[i]=false; + oscBuf[i]=new DivDispatchOscBuffer; } setFlags(flags); reset(); @@ -428,6 +440,9 @@ int DivPlatformSegaPCM::init(DivEngine* p, int channels, int sugRate, unsigned i } void DivPlatformSegaPCM::quit() { + for (int i=0; i<16; i++) { + delete oscBuf[i]; + } } DivPlatformSegaPCM::~DivPlatformSegaPCM() { diff --git a/src/engine/platform/segapcm.h b/src/engine/platform/segapcm.h index 7ef09892a..32cd22c29 100644 --- a/src/engine/platform/segapcm.h +++ b/src/engine/platform/segapcm.h @@ -49,6 +49,7 @@ class DivPlatformSegaPCM: public DivDispatch { Channel(): freqH(0), freqL(0), freq(0), baseFreq(0), pitch(0), pitch2(0), note(0), ins(-1), active(false), insChanged(true), freqChanged(false), keyOn(false), keyOff(false), inPorta(false), portaPause(false), furnacePCM(false), vol(0), outVol(0), chVolL(127), chVolR(127) {} }; Channel chan[16]; + DivDispatchOscBuffer* oscBuf[16]; struct QueuedWrite { unsigned short addr; unsigned char val; @@ -77,6 +78,7 @@ class DivPlatformSegaPCM: public DivDispatch { void acquire(short* bufL, short* bufR, size_t start, size_t len); int dispatch(DivCommand c); void* getChanState(int chan); + DivDispatchOscBuffer* getOscBuffer(int chan); unsigned char* getRegisterPool(); int getRegisterPoolSize(); void reset(); diff --git a/src/gui/chanOsc.cpp b/src/gui/chanOsc.cpp new file mode 100644 index 000000000..c4924e7ef --- /dev/null +++ b/src/gui/chanOsc.cpp @@ -0,0 +1,101 @@ +/** + * Furnace Tracker - multi-system chiptune tracker + * Copyright (C) 2021-2022 tildearrow and contributors + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "gui.h" +#include "imgui.h" +#include "imgui_internal.h" + +void FurnaceGUI::drawChanOsc() { + if (nextWindow==GUI_WINDOW_CHAN_OSC) { + chanOscOpen=true; + ImGui::SetNextWindowFocus(); + nextWindow=GUI_WINDOW_NOTHING; + } + if (!chanOscOpen) return; + ImGui::SetNextWindowSizeConstraints(ImVec2(64.0f*dpiScale,32.0f*dpiScale),ImVec2(scrW*dpiScale,scrH*dpiScale)); + if (ImGui::Begin("Oscilloscope (per-channel)",&chanOscOpen)) { + if (ImGui::InputInt("Columns",&chanOscCols,1,1)) { + if (chanOscCols<1) chanOscCols=1; + if (chanOscCols>64) chanOscCols=64; + } + + ImGui::PushStyleVar(ImGuiStyleVar_CellPadding,ImVec2(0.0f,0.0f)); + float availY=ImGui::GetContentRegionAvail().y; + if (ImGui::BeginTable("ChanOsc",chanOscCols,ImGuiTableFlags_Borders)) { + int chans=e->getTotalChannelCount(); + int rows=(chans+(chanOscCols-1))/chanOscCols; + ImDrawList* dl=ImGui::GetWindowDrawList(); + ImGuiWindow* window=ImGui::GetCurrentWindow(); + ImVec2 waveform[512]; + + ImGuiStyle& style=ImGui::GetStyle(); + ImU32 color=ImGui::GetColorU32(uiColors[GUI_COLOR_OSC_WAVE]); + + for (int i=0; igetOscBuffer(i); + if (buf==NULL) { + ImGui::Text("Not Available"); + } else { + ImVec2 size=ImGui::GetContentRegionAvail(); + size.y=availY/rows; + + int displaySize=(buf->rate)/30; + + ImVec2 minArea=window->DC.CursorPos; + ImVec2 maxArea=ImVec2( + minArea.x+size.x, + minArea.y+size.y + ); + ImRect rect=ImRect(minArea,maxArea); + ImRect inRect=rect; + inRect.Min.x+=dpiScale; + inRect.Min.y+=dpiScale; + inRect.Max.x-=dpiScale; + inRect.Max.y-=dpiScale; + ImGui::ItemSize(size,style.FramePadding.y); + if (ImGui::ItemAdd(rect,ImGui::GetID("chOscDisplay"))) { + if (!e->isPlaying()) { + for (unsigned short i=0; i<512; i++) { + float x=(float)i/512.0f; + waveform[i]=ImLerp(inRect.Min,inRect.Max,ImVec2(x,0.5f)); + } + } else { + unsigned short needlePos=buf->needle-displaySize; + for (unsigned short i=0; i<512; i++) { + float x=(float)i/512.0f; + float y=(float)buf->data[(unsigned short)(needlePos+(i*displaySize/512))]/65536.0f; + if (y<-0.5f) y=-0.5f; + if (y>0.5f) y=0.5f; + waveform[i]=ImLerp(inRect.Min,inRect.Max,ImVec2(x,0.5f-y)); + } + } + dl->AddPolyline(waveform,512,color,ImDrawFlags_None,dpiScale); + } + } + } + ImGui::EndTable(); + } + ImGui::PopStyleVar(); + } + if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_CHAN_OSC; + ImGui::End(); +} \ No newline at end of file diff --git a/src/gui/doAction.cpp b/src/gui/doAction.cpp index 4020dfc7b..92ce732f9 100644 --- a/src/gui/doAction.cpp +++ b/src/gui/doAction.cpp @@ -217,6 +217,9 @@ void FurnaceGUI::doAction(int what) { case GUI_ACTION_WINDOW_EFFECT_LIST: nextWindow=GUI_WINDOW_EFFECT_LIST; break; + case GUI_ACTION_WINDOW_CHAN_OSC: + nextWindow=GUI_WINDOW_CHAN_OSC; + break; case GUI_ACTION_COLLAPSE_WINDOW: collapseWindow=true; @@ -295,6 +298,9 @@ void FurnaceGUI::doAction(int what) { case GUI_WINDOW_EFFECT_LIST: effectListOpen=false; break; + case GUI_WINDOW_CHAN_OSC: + chanOscOpen=false; + break; default: break; } diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index dd37ceae1..6d63d0f50 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -2757,7 +2757,8 @@ bool FurnaceGUI::loop() { ImGui::Separator(); if (ImGui::MenuItem("play/edit controls",BIND_FOR(GUI_ACTION_WINDOW_EDIT_CONTROLS),editControlsOpen)) editControlsOpen=!editControlsOpen; if (ImGui::MenuItem("piano/input pad",BIND_FOR(GUI_ACTION_WINDOW_PIANO),pianoOpen)) pianoOpen=!pianoOpen; - if (ImGui::MenuItem("oscilloscope",BIND_FOR(GUI_ACTION_WINDOW_OSCILLOSCOPE),oscOpen)) oscOpen=!oscOpen; + if (ImGui::MenuItem("oscilloscope (master)",BIND_FOR(GUI_ACTION_WINDOW_OSCILLOSCOPE),oscOpen)) oscOpen=!oscOpen; + if (ImGui::MenuItem("oscilloscope (per-channel)",BIND_FOR(GUI_ACTION_WINDOW_CHAN_OSC),chanOscOpen)) chanOscOpen=!chanOscOpen; if (ImGui::MenuItem("volume meter",BIND_FOR(GUI_ACTION_WINDOW_VOL_METER),volMeterOpen)) volMeterOpen=!volMeterOpen; if (ImGui::MenuItem("register view",BIND_FOR(GUI_ACTION_WINDOW_REGISTER_VIEW),regViewOpen)) regViewOpen=!regViewOpen; if (ImGui::MenuItem("log viewer",BIND_FOR(GUI_ACTION_WINDOW_LOG),logOpen)) logOpen=!logOpen; @@ -2860,6 +2861,7 @@ bool FurnaceGUI::loop() { readOsc(); drawOsc(); + drawChanOsc(); drawVolMeter(); drawSettings(); drawDebug(); @@ -3556,6 +3558,7 @@ bool FurnaceGUI::init() { settingsOpen=e->getConfBool("settingsOpen",false); mixerOpen=e->getConfBool("mixerOpen",false); oscOpen=e->getConfBool("oscOpen",true); + chanOscOpen=e->getConfBool("chanOscOpen",false); volMeterOpen=e->getConfBool("volMeterOpen",true); statsOpen=e->getConfBool("statsOpen",false); compatFlagsOpen=e->getConfBool("compatFlagsOpen",false); @@ -3731,6 +3734,7 @@ bool FurnaceGUI::finish() { e->setConf("settingsOpen",settingsOpen); e->setConf("mixerOpen",mixerOpen); e->setConf("oscOpen",oscOpen); + e->setConf("chanOscOpen",chanOscOpen); e->setConf("volMeterOpen",volMeterOpen); e->setConf("statsOpen",statsOpen); e->setConf("compatFlagsOpen",compatFlagsOpen); @@ -3851,6 +3855,7 @@ FurnaceGUI::FurnaceGUI(): regViewOpen(false), logOpen(false), effectListOpen(false), + chanOscOpen(false), /* editControlsDocked(false), ordersDocked(false), @@ -3877,6 +3882,7 @@ FurnaceGUI::FurnaceGUI(): regViewDocked(false), logDocked(false), effectListDocked(false), + chanOscDocked(false), */ selecting(false), curNibble(false), @@ -3993,6 +3999,7 @@ FurnaceGUI::FurnaceGUI(): oscTotal(0), oscZoom(0.5f), oscZoomSlider(false), + chanOscCols(3), followLog(true), pianoOctaves(7), pianoOptions(false), diff --git a/src/gui/gui.h b/src/gui/gui.h index 9608e5eb7..cf532eef5 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -230,7 +230,8 @@ enum FurnaceGUIWindows { GUI_WINDOW_CHANNELS, GUI_WINDOW_REGISTER_VIEW, GUI_WINDOW_LOG, - GUI_WINDOW_EFFECT_LIST + GUI_WINDOW_EFFECT_LIST, + GUI_WINDOW_CHAN_OSC }; enum FurnaceGUIFileDialogs { @@ -330,6 +331,7 @@ enum FurnaceGUIActions { GUI_ACTION_WINDOW_REGISTER_VIEW, GUI_ACTION_WINDOW_LOG, GUI_ACTION_WINDOW_EFFECT_LIST, + GUI_ACTION_WINDOW_CHAN_OSC, GUI_ACTION_COLLAPSE_WINDOW, GUI_ACTION_CLOSE_WINDOW, @@ -948,13 +950,13 @@ class FurnaceGUI { bool editControlsOpen, ordersOpen, insListOpen, songInfoOpen, patternOpen, insEditOpen; bool waveListOpen, waveEditOpen, sampleListOpen, sampleEditOpen, aboutOpen, settingsOpen; bool mixerOpen, debugOpen, inspectorOpen, oscOpen, volMeterOpen, statsOpen, compatFlagsOpen; - bool pianoOpen, notesOpen, channelsOpen, regViewOpen, logOpen, effectListOpen; + bool pianoOpen, notesOpen, channelsOpen, regViewOpen, logOpen, effectListOpen, chanOscOpen; /* there ought to be a better way... bool editControlsDocked, ordersDocked, insListDocked, songInfoDocked, patternDocked, insEditDocked; bool waveListDocked, waveEditDocked, sampleListDocked, sampleEditDocked, aboutDocked, settingsDocked; bool mixerDocked, debugDocked, inspectorDocked, oscDocked, volMeterDocked, statsDocked, compatFlagsDocked; - bool pianoDocked, notesDocked, channelsDocked, regViewDocked, logDocked, effectListDocked; + bool pianoDocked, notesDocked, channelsDocked, regViewDocked, logDocked, effectListDocked, chanOscDocked; */ SelectionPoint selStart, selEnd, cursor; @@ -1097,6 +1099,9 @@ class FurnaceGUI { float oscZoom; bool oscZoomSlider; + // per-channel oscilloscope + int chanOscCols; + // visualizer float keyHit[DIV_MAX_CHANS]; int lastIns[DIV_MAX_CHANS]; @@ -1151,6 +1156,7 @@ class FurnaceGUI { void drawSampleEdit(); void drawMixer(); void drawOsc(); + void drawChanOsc(); void drawVolMeter(); void drawStats(); void drawCompatFlags(); diff --git a/src/gui/guiConst.cpp b/src/gui/guiConst.cpp index ec157a9eb..0cc1894ab 100644 --- a/src/gui/guiConst.cpp +++ b/src/gui/guiConst.cpp @@ -475,7 +475,7 @@ const FurnaceGUIActionDef guiActions[GUI_ACTION_MAX]={ D("WINDOW_SETTINGS", "Settings", 0), D("WINDOW_MIXER", "Mixer", 0), D("WINDOW_DEBUG", "Debug Menu", 0), - D("WINDOW_OSCILLOSCOPE", "Oscilloscope", 0), + D("WINDOW_OSCILLOSCOPE", "Oscilloscope (master)", 0), D("WINDOW_VOL_METER", "Volume Meter", 0), D("WINDOW_STATS", "Statistics", 0), D("WINDOW_COMPAT_FLAGS", "Compatibility Flags", 0), @@ -485,6 +485,7 @@ const FurnaceGUIActionDef guiActions[GUI_ACTION_MAX]={ D("WINDOW_REGISTER_VIEW", "Register View", 0), D("WINDOW_LOG", "Log Viewer", 0), D("EFFECT_LIST", "Effect List", 0), + D("WINDOW_CHAN_OSC", "Oscilloscope (per-channel)", 0), D("COLLAPSE_WINDOW", "Collapse/expand current window", 0), D("CLOSE_WINDOW", "Close current window", FURKMOD_SHIFT|SDLK_ESCAPE), From fae3de53a93002a6e5bd787c2564a0b64f5c65f2 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 30 Apr 2022 04:30:33 -0500 Subject: [PATCH 9/9] maybe uninitialized --- src/engine/engine.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index f8f518933..3a0a841d8 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -639,7 +639,10 @@ std::vector DivEngine::decodeSysDesc(String desc) { bool negative=false; int val=0; int curStage=0; - int sysID, sysVol, sysPan, sysFlags; + int sysID=0; + int sysVol=0; + int sysPan=0; + int sysFlags=0; desc+=' '; // ha for (char i: desc) { switch (i) {