diff --git a/CMakeLists.txt b/CMakeLists.txt index 6d1d201ab..c08b8f553 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -397,6 +397,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/TODO.md b/TODO.md index 1dd87e8d8..15ce4fe41 100644 --- a/TODO.md +++ b/TODO.md @@ -19,12 +19,9 @@ - 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 @@ -35,7 +32,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 @@ -44,5 +40,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/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 198777a85..d5843fee3 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 @@ -49,7 +50,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) @@ -214,6 +215,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; @@ -277,6 +290,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 cd4dfa466..ef3abed55 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: @@ -645,6 +651,101 @@ void DivEngine::renderSamples() { es5506MemLen=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=0; + int sysVol=0; + int sysPan=0; + int sysFlags=0; + 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; @@ -652,18 +753,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(); @@ -930,6 +1020,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; } @@ -1083,6 +1183,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; @@ -1231,6 +1335,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 6c8393c68..85e198b44 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), @@ -296,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 +401,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 +424,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. @@ -479,6 +486,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 @@ -716,6 +724,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); @@ -877,6 +891,7 @@ class DivEngine { midiIsDirect(false), lowLatency(false), systemsRegistered(false), + hasLoadedSomething(false), softLockCount(0), subticks(0), ticks(0), 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 34d216983..d50b7d6d4 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/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/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; igetIns(chan[c.chan].ins); - // 08LR, each nibble means volume multipler for each channels // Left volume - const unsigned int lVol=(0xff*((c.value>>4)&0xf))/0xf; - if (chan[c.chan].lVol!=lVol) { - chan[c.chan].lVol=lVol; + if (chan[c.chan].lVol!=(unsigned int)(c.value)) { + chan[c.chan].lVol=c.value; if (!chan[c.chan].std.panL.has) { - chan[c.chan].outLVol=(ins->es5506.lVol*lVol)/0xff; + chan[c.chan].outLVol=(ins->es5506.lVol*c.value)/0xff; if (!isMuted[c.chan]) { chan[c.chan].volChanged.lVol=1; } } } // Right volume - const unsigned int rVol=(0xff*((c.value>>0)&0xf))/0xf; - if (chan[c.chan].rVol!=rVol) { - chan[c.chan].rVol=rVol; + if (chan[c.chan].rVol!=(unsigned int)(c.value2)) { + chan[c.chan].rVol=c.value2; if (!chan[c.chan].std.panR.has) { - chan[c.chan].outRVol=(ins->es5506.rVol*rVol)/0xff; + chan[c.chan].outRVol=(ins->es5506.rVol*c.value2)/0xff; if (!isMuted[c.chan]) { chan[c.chan].volChanged.rVol=1; } 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/opll.cpp b/src/engine/platform/opll.cpp index 54eafcb44..f02ad40b8 100644 --- a/src/engine/platform/opll.cpp +++ b/src/engine/platform/opll.cpp @@ -94,7 +94,10 @@ void DivPlatformOPLL::acquire_nuked(short* bufL, short* bufR, size_t start, size OPLL_Clock(&fm,o); unsigned char nextOut=cycleMapOPLL[fm.cycles]; if ((nextOut>=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/pce.cpp b/src/engine/platform/pce.cpp index 6e249aa36..1ae01fd1c 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 5887dd9ca..eb81dca58 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 ea3a72d0c..7ce320bba 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; } } @@ -268,8 +272,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); @@ -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/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 b2d4fbcb8..e7e08bafd 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 6404553b4..917dd7444 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 2fbd9e09a..d686ef98d 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 717e81913..bfa7415d1 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 57d89be71..b23e9c027 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -1070,6 +1070,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; @@ -1354,6 +1372,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/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"}, 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/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/doAction.cpp b/src/gui/doAction.cpp index 14c760bdb..d31a026e7 100644 --- a/src/gui/doAction.cpp +++ b/src/gui/doAction.cpp @@ -18,6 +18,7 @@ */ #include "gui.h" +#include "../ta-log.h" #include #include @@ -216,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; @@ -294,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; } @@ -714,6 +721,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)) { @@ -741,6 +751,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) { @@ -769,6 +781,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) { 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/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 74a303542..6d63d0f50 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)) { @@ -2370,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; @@ -2680,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(); } } @@ -2775,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; @@ -2878,6 +2861,7 @@ bool FurnaceGUI::loop() { readOsc(); drawOsc(); + drawChanOsc(); drawVolMeter(); drawSettings(); drawDebug(); @@ -2900,6 +2884,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; @@ -3561,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); @@ -3736,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); @@ -3856,6 +3855,7 @@ FurnaceGUI::FurnaceGUI(): regViewOpen(false), logOpen(false), effectListOpen(false), + chanOscOpen(false), /* editControlsDocked(false), ordersDocked(false), @@ -3882,6 +3882,7 @@ FurnaceGUI::FurnaceGUI(): regViewDocked(false), logDocked(false), effectListDocked(false), + chanOscDocked(false), */ selecting(false), curNibble(false), @@ -3998,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 31ab566f1..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, @@ -857,6 +859,7 @@ class FurnaceGUI { String audioDevice; String midiInDevice; String midiOutDevice; + std::vector initialSys; Settings(): mainFontSize(18), @@ -947,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; @@ -1096,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]; @@ -1114,7 +1120,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); @@ -1150,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 57d63219a..4771cf91d 100644 --- a/src/gui/guiConst.cpp +++ b/src/gui/guiConst.cpp @@ -149,7 +149,7 @@ const char* loopMode[DIV_SAMPLE_LOOPMODE_MAX]={ "Pingpong" }; -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 @@ -166,9 +166,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 @@ -200,7 +433,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 @@ -249,7 +482,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), @@ -259,6 +492,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), diff --git a/src/gui/guiConst.h b/src/gui/guiConst.h index d84c17a95..217a5cb70 100644 --- a/src/gui/guiConst.h +++ b/src/gui/guiConst.h @@ -48,5 +48,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; diff --git a/src/gui/sampleEdit.cpp b/src/gui/sampleEdit.cpp index b206f680f..0b12be030 100644 --- a/src/gui/sampleEdit.cpp +++ b/src/gui/sampleEdit.cpp @@ -1341,7 +1341,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; 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("##N163_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; } @@ -406,9 +382,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; + } + } }