Merge branch 'master' into preset1
This commit is contained in:
		
						commit
						0c1a8bc001
					
				
							
								
								
									
										7
									
								
								TODO.md
									
									
									
									
									
								
							
							
						
						
									
										7
									
								
								TODO.md
									
									
									
									
									
								
							| 
						 | 
				
			
			@ -1,12 +1,13 @@
 | 
			
		|||
# to-do for 0.6pre1
 | 
			
		||||
 | 
			
		||||
- rewrite the system name detection function anyway
 | 
			
		||||
- add another FM editor layout
 | 
			
		||||
- add ability to move selection by dragging
 | 
			
		||||
- find and replace
 | 
			
		||||
- implement Defle slide bug when using E1xy/E2xy and repeating origin note (requires format change)
 | 
			
		||||
 | 
			
		||||
# to-do for 0.6pre2 (as this requires new data structures)
 | 
			
		||||
 | 
			
		||||
- rewrite the system name detection function anyway
 | 
			
		||||
  - this involves the addition of a new "system" field in the song (which solves the problem)
 | 
			
		||||
  - songs made in older versions will go through old system name detection for compatibility
 | 
			
		||||
- Game Boy envelope macro/sequence
 | 
			
		||||
- volume commands should work on Game Boy
 | 
			
		||||
- ability to customize `OFF`, `===` and `REL`
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										
											BIN
										
									
								
								demos/E1M4OPL2.fur
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								demos/E1M4OPL2.fur
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								demos/FDS TEST.fur
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								demos/FDS TEST.fur
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								demos/Fake Gameboy.fur
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								demos/Fake Gameboy.fur
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								demos/Funky_Bubbles_OPL3.fur
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								demos/Funky_Bubbles_OPL3.fur
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								demos/Fusion.fur
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								demos/Fusion.fur
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								demos/c64 ring test.fur
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								demos/c64 ring test.fur
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								demos/game boy thing.fur
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								demos/game boy thing.fur
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								demos/going_up_a_step_at_time.fur
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								demos/going_up_a_step_at_time.fur
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								demos/the_erfngjt.fur
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								demos/the_erfngjt.fur
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								demos/thick bass test.fur
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								demos/thick bass test.fur
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										8
									
								
								extern/igfd/ImGuiFileDialog.cpp
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										8
									
								
								extern/igfd/ImGuiFileDialog.cpp
									
									
									
									
										vendored
									
									
								
							| 
						 | 
				
			
			@ -1287,8 +1287,6 @@ namespace IGFD
 | 
			
		|||
				std::sort(prFileList.begin(), prFileList.end(),
 | 
			
		||||
					[](const std::shared_ptr<FileInfos>& a, const std::shared_ptr<FileInfos>& b) -> bool
 | 
			
		||||
					{
 | 
			
		||||
            if (a==NULL || b==NULL)
 | 
			
		||||
              return false;
 | 
			
		||||
						if (!a.use_count() || !b.use_count())
 | 
			
		||||
							return false;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1760,7 +1758,7 @@ namespace IGFD
 | 
			
		|||
			struct stat statInfos = {};
 | 
			
		||||
			char timebuf[100];
 | 
			
		||||
			int result = stat(fpn.c_str(), &statInfos);
 | 
			
		||||
			if (!result)
 | 
			
		||||
			if (result!=-1)
 | 
			
		||||
			{
 | 
			
		||||
				if (vInfos->fileType != 'd')
 | 
			
		||||
				{
 | 
			
		||||
| 
						 | 
				
			
			@ -1781,6 +1779,10 @@ namespace IGFD
 | 
			
		|||
				{
 | 
			
		||||
					vInfos->fileModifDate = std::string(timebuf, len);
 | 
			
		||||
				}
 | 
			
		||||
			} else {
 | 
			
		||||
        vInfos->fileSize=0;
 | 
			
		||||
        vInfos->formatedFileSize = prFormatFileSize(vInfos->fileSize);
 | 
			
		||||
        vInfos->fileModifDate="???";
 | 
			
		||||
      }
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -21,6 +21,26 @@
 | 
			
		|||
#include "../ta-log.h"
 | 
			
		||||
#include "taAudio.h"
 | 
			
		||||
 | 
			
		||||
String sanitizePortName(const String& name) {
 | 
			
		||||
#if defined(_WIN32)
 | 
			
		||||
  // remove port number
 | 
			
		||||
  size_t namePos=name.rfind(' ');
 | 
			
		||||
  if (namePos!=String::npos) {
 | 
			
		||||
    return name.substr(0,namePos);
 | 
			
		||||
  }
 | 
			
		||||
  return name;
 | 
			
		||||
#elif defined(__linux__)
 | 
			
		||||
  // remove port location
 | 
			
		||||
  size_t namePos=name.rfind(' ');
 | 
			
		||||
  if (namePos!=String::npos) {
 | 
			
		||||
    return name.substr(0,namePos);
 | 
			
		||||
  }
 | 
			
		||||
  return name;
 | 
			
		||||
#else
 | 
			
		||||
  return name;
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// --- IN ---
 | 
			
		||||
 | 
			
		||||
bool TAMidiInRtMidi::gather() {
 | 
			
		||||
| 
						 | 
				
			
			@ -56,7 +76,7 @@ std::vector<String> TAMidiInRtMidi::listDevices() {
 | 
			
		|||
    unsigned int count=port->getPortCount();
 | 
			
		||||
    logD("got port count.");
 | 
			
		||||
    for (unsigned int i=0; i<count; i++) {
 | 
			
		||||
      String name=port->getPortName(i);
 | 
			
		||||
      String name=sanitizePortName(port->getPortName(i));
 | 
			
		||||
      if (name!="") ret.push_back(name);
 | 
			
		||||
    }
 | 
			
		||||
  } catch (RtMidiError& e) {
 | 
			
		||||
| 
						 | 
				
			
			@ -75,8 +95,12 @@ bool TAMidiInRtMidi::openDevice(String name) {
 | 
			
		|||
  try {
 | 
			
		||||
    bool portOpen=false;
 | 
			
		||||
    unsigned int count=port->getPortCount();
 | 
			
		||||
    logD("finding port %s...",name);
 | 
			
		||||
    for (unsigned int i=0; i<count; i++) {
 | 
			
		||||
      if (port->getPortName(i)==name) {
 | 
			
		||||
      String portName=sanitizePortName(port->getPortName(i));
 | 
			
		||||
      logV("- %d: %s",i,portName);
 | 
			
		||||
      if (portName==name) {
 | 
			
		||||
        logD("opening port %d...",i);
 | 
			
		||||
        port->openPort(i);
 | 
			
		||||
        portOpen=true;
 | 
			
		||||
        break;
 | 
			
		||||
| 
						 | 
				
			
			@ -184,8 +208,12 @@ bool TAMidiOutRtMidi::openDevice(String name) {
 | 
			
		|||
  try {
 | 
			
		||||
    bool portOpen=false;
 | 
			
		||||
    unsigned int count=port->getPortCount();
 | 
			
		||||
    logD("finding port %s...",name);
 | 
			
		||||
    for (unsigned int i=0; i<count; i++) {
 | 
			
		||||
      if (port->getPortName(i)==name) {
 | 
			
		||||
      String portName=sanitizePortName(port->getPortName(i));
 | 
			
		||||
      logV("- %d: %s",i,portName);
 | 
			
		||||
      if (portName==name) {
 | 
			
		||||
        logD("opening port %d...",i);
 | 
			
		||||
        port->openPort(i);
 | 
			
		||||
        portOpen=true;
 | 
			
		||||
        break;
 | 
			
		||||
| 
						 | 
				
			
			@ -222,7 +250,7 @@ std::vector<String> TAMidiOutRtMidi::listDevices() {
 | 
			
		|||
  try {
 | 
			
		||||
    unsigned int count=port->getPortCount();
 | 
			
		||||
    for (unsigned int i=0; i<count; i++) {
 | 
			
		||||
      String name=port->getPortName(i);
 | 
			
		||||
      String name=sanitizePortName(port->getPortName(i));
 | 
			
		||||
      if (name!="") ret.push_back(name);
 | 
			
		||||
    }
 | 
			
		||||
  } catch (RtMidiError& e) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -83,7 +83,7 @@ struct DivChannelState {
 | 
			
		|||
  int note, oldNote, lastIns, pitch, portaSpeed, portaNote;
 | 
			
		||||
  int volume, volSpeed, cut, rowDelay, volMax;
 | 
			
		||||
  int delayOrder, delayRow, retrigSpeed, retrigTick;
 | 
			
		||||
  int vibratoDepth, vibratoRate, vibratoPos, vibratoDir, vibratoFine;
 | 
			
		||||
  int vibratoDepth, vibratoRate, vibratoPos, vibratoPosGiant, vibratoDir, vibratoFine;
 | 
			
		||||
  int tremoloDepth, tremoloRate, tremoloPos;
 | 
			
		||||
  unsigned char arp, arpStage, arpTicks, panL, panR;
 | 
			
		||||
  bool doNote, legato, portaStop, keyOn, keyOff, nowYouCanStop, stopOnOff;
 | 
			
		||||
| 
						 | 
				
			
			@ -112,6 +112,7 @@ struct DivChannelState {
 | 
			
		|||
    vibratoDepth(0),
 | 
			
		||||
    vibratoRate(0),
 | 
			
		||||
    vibratoPos(0),
 | 
			
		||||
    vibratoPosGiant(0),
 | 
			
		||||
    vibratoDir(0),
 | 
			
		||||
    vibratoFine(15),
 | 
			
		||||
    tremoloDepth(0),
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -306,7 +306,7 @@ void DivPlatformBubSysWSG::reset() {
 | 
			
		|||
  for (int i=0; i<2; i++) {
 | 
			
		||||
    chan[i]=DivPlatformBubSysWSG::Channel();
 | 
			
		||||
    chan[i].std.setEngine(parent);
 | 
			
		||||
    chan[i].ws.setEngine(parent);
 | 
			
		||||
    chan[i].ws.setEngine(parent,8);
 | 
			
		||||
    chan[i].ws.init(NULL,32,15,false);
 | 
			
		||||
  }
 | 
			
		||||
  if (dumpWrites) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -380,7 +380,7 @@ void DivPlatformMSM6258::setFlags(unsigned int flags) {
 | 
			
		|||
      chipClock=4000000;
 | 
			
		||||
      break;
 | 
			
		||||
  }
 | 
			
		||||
  rate=chipClock/128;
 | 
			
		||||
  rate=chipClock/256;
 | 
			
		||||
  for (int i=0; i<1; i++) {
 | 
			
		||||
    oscBuf[i]->rate=rate;
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -682,6 +682,9 @@ void DivPlatformOPL::muteChannel(int ch, bool mute) {
 | 
			
		|||
    fm.channel[outChanMap[ch]].muted=mute;
 | 
			
		||||
  }
 | 
			
		||||
  int ops=(slots[3][ch]!=255 && chan[ch].state.ops==4 && oplType==3)?4:2;
 | 
			
		||||
  if (ch&1 && ch<12) {
 | 
			
		||||
    if (chan[ch-1].fourOp) return;
 | 
			
		||||
  }
 | 
			
		||||
  chan[ch].fourOp=(ops==4);
 | 
			
		||||
  update4OpMask=true;
 | 
			
		||||
  for (int i=0; i<ops; i++) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -332,7 +332,7 @@ void DivPlatformSCC::reset() {
 | 
			
		|||
  for (int i=0; i<5; i++) {
 | 
			
		||||
    chan[i]=DivPlatformSCC::Channel();
 | 
			
		||||
    chan[i].std.setEngine(parent);
 | 
			
		||||
    chan[i].ws.setEngine(parent);
 | 
			
		||||
    chan[i].ws.setEngine(parent,128);
 | 
			
		||||
    chan[i].ws.init(NULL,32,255,false);
 | 
			
		||||
    chan[i].vol=15;
 | 
			
		||||
    chan[i].outVol=15;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -347,7 +347,7 @@ int DivPlatformSwan::dispatch(DivCommand c) {
 | 
			
		|||
    case DIV_CMD_VOLUME:
 | 
			
		||||
      if (chan[c.chan].vol!=c.value) {
 | 
			
		||||
        chan[c.chan].vol=c.value;
 | 
			
		||||
        if (!chan[c.chan].std.vol.had) {
 | 
			
		||||
        if (!chan[c.chan].std.vol.has) {
 | 
			
		||||
          calcAndWriteOutVol(c.chan,15);
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -195,7 +195,7 @@ int DivPlatformVIC20::dispatch(DivCommand c) {
 | 
			
		|||
    case DIV_CMD_VOLUME:
 | 
			
		||||
      if (chan[c.chan].vol!=c.value) {
 | 
			
		||||
        chan[c.chan].vol=c.value;
 | 
			
		||||
        if (!chan[c.chan].std.vol.had) {
 | 
			
		||||
        if (!chan[c.chan].std.vol.has) {
 | 
			
		||||
          calcAndWriteOutVol(c.chan,15);
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -988,6 +988,10 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) {
 | 
			
		|||
        if (chan[i].vibratoDepth>0) {
 | 
			
		||||
          chan[i].vibratoPos+=chan[i].vibratoRate;
 | 
			
		||||
          if (chan[i].vibratoPos>=64) chan[i].vibratoPos-=64;
 | 
			
		||||
 | 
			
		||||
          chan[i].vibratoPosGiant+=chan[i].vibratoRate;
 | 
			
		||||
          if (chan[i].vibratoPos>=512) chan[i].vibratoPos-=512;
 | 
			
		||||
 | 
			
		||||
          switch (chan[i].vibratoDir) {
 | 
			
		||||
            case 1: // up
 | 
			
		||||
              dispatchCmd(DivCommand(DIV_CMD_PITCH,i,chan[i].pitch+(MAX(0,(chan[i].vibratoDepth*vibTable[chan[i].vibratoPos]*chan[i].vibratoFine)>>4)/15)));
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -243,8 +243,13 @@ void DivWaveSynth::changeWave2(int num) {
 | 
			
		|||
  first=true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void DivWaveSynth::setEngine(DivEngine* engine) {
 | 
			
		||||
void DivWaveSynth::setEngine(DivEngine* engine, int waveFloor) {
 | 
			
		||||
  e=engine;
 | 
			
		||||
  memset(wave1,waveFloor,256);
 | 
			
		||||
  memset(wave2,waveFloor,256);
 | 
			
		||||
  for (int i=0; i<256; i++) {
 | 
			
		||||
    output[i]=waveFloor;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void DivWaveSynth::init(DivInstrument* which, int w, int h, bool insChanged) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -70,7 +70,7 @@ class DivWaveSynth {
 | 
			
		|||
     * @param insChanged whether the instrument has changed.
 | 
			
		||||
     */
 | 
			
		||||
    void init(DivInstrument* which, int width, int height, bool insChanged=false);
 | 
			
		||||
    void setEngine(DivEngine* engine);
 | 
			
		||||
    void setEngine(DivEngine* engine, int waveFloor=0);
 | 
			
		||||
    DivWaveSynth():
 | 
			
		||||
      e(NULL),
 | 
			
		||||
      pos(0),
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -66,6 +66,8 @@ void FurnaceGUI::prepareUndo(ActionType action) {
 | 
			
		|||
        e->curPat[i].getPattern(e->curOrders->ord[i][curOrder],false)->copyOn(oldPat[i]);
 | 
			
		||||
      }
 | 
			
		||||
      break;
 | 
			
		||||
    case GUI_UNDO_REPLACE: // this is handled by doReplace()
 | 
			
		||||
      break;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -126,6 +128,8 @@ void FurnaceGUI::makeUndo(ActionType action) {
 | 
			
		|||
        doPush=true;
 | 
			
		||||
      }
 | 
			
		||||
      break;
 | 
			
		||||
    case GUI_UNDO_REPLACE: // this is handled by doReplace()
 | 
			
		||||
      break;
 | 
			
		||||
  }
 | 
			
		||||
  if (doPush) {
 | 
			
		||||
    MARK_MODIFIED;
 | 
			
		||||
| 
						 | 
				
			
			@ -943,6 +947,7 @@ void FurnaceGUI::doUndo() {
 | 
			
		|||
    case GUI_UNDO_PATTERN_FLIP:
 | 
			
		||||
    case GUI_UNDO_PATTERN_COLLAPSE:
 | 
			
		||||
    case GUI_UNDO_PATTERN_EXPAND:
 | 
			
		||||
    case GUI_UNDO_REPLACE:
 | 
			
		||||
      for (UndoPatternData& i: us.pat) {
 | 
			
		||||
        e->changeSongP(i.subSong);
 | 
			
		||||
        DivPattern* p=e->curPat[i.chan].getPattern(i.pat,true);
 | 
			
		||||
| 
						 | 
				
			
			@ -991,6 +996,7 @@ void FurnaceGUI::doRedo() {
 | 
			
		|||
    case GUI_UNDO_PATTERN_FLIP:
 | 
			
		||||
    case GUI_UNDO_PATTERN_COLLAPSE:
 | 
			
		||||
    case GUI_UNDO_PATTERN_EXPAND:
 | 
			
		||||
    case GUI_UNDO_REPLACE:
 | 
			
		||||
      for (UndoPatternData& i: us.pat) {
 | 
			
		||||
        e->changeSongP(i.subSong);
 | 
			
		||||
        DivPattern* p=e->curPat[i.chan].getPattern(i.pat,true);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,6 +3,8 @@
 | 
			
		|||
#include "IconsFontAwesome4.h"
 | 
			
		||||
#include "misc/cpp/imgui_stdlib.h"
 | 
			
		||||
#include "guiConst.h"
 | 
			
		||||
#include "intConst.h"
 | 
			
		||||
#include "../ta-log.h"
 | 
			
		||||
 | 
			
		||||
const char* queryModes[GUI_QUERY_MAX]={
 | 
			
		||||
  "ignore",
 | 
			
		||||
| 
						 | 
				
			
			@ -17,9 +19,398 @@ const char* queryModes[GUI_QUERY_MAX]={
 | 
			
		|||
const char* queryReplaceModes[GUI_QUERY_REPLACE_MAX]={
 | 
			
		||||
  "set",
 | 
			
		||||
  "add",
 | 
			
		||||
  "add (overflow)",
 | 
			
		||||
  "clear"
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
int queryNote(int note, int octave) {
 | 
			
		||||
  if (note==100) {
 | 
			
		||||
    return 128;
 | 
			
		||||
  } else if (note==101) { // note off and envelope release
 | 
			
		||||
    return 129;
 | 
			
		||||
  } else if (note==102) { // envelope release only
 | 
			
		||||
    return 130;
 | 
			
		||||
  } else if (octave==0 && note==0) {
 | 
			
		||||
    return -61;
 | 
			
		||||
  } else if (note==0 && octave!=0) {
 | 
			
		||||
    return -61; // bug note?
 | 
			
		||||
  }
 | 
			
		||||
  int seek=(note+(signed char)octave*12);
 | 
			
		||||
  if (seek<-60 || seek>=120) {
 | 
			
		||||
    return -61; // out of range note
 | 
			
		||||
  }
 | 
			
		||||
  return seek;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool checkCondition(int mode, int arg, int argMax, int val, bool noteMode=false) {
 | 
			
		||||
  switch (mode) {
 | 
			
		||||
    case GUI_QUERY_IGNORE:
 | 
			
		||||
      return true;
 | 
			
		||||
      break;
 | 
			
		||||
    case GUI_QUERY_MATCH:
 | 
			
		||||
      return (val==arg);
 | 
			
		||||
      break;
 | 
			
		||||
    case GUI_QUERY_MATCH_NOT:
 | 
			
		||||
      return (val!=-1 && val!=arg);
 | 
			
		||||
      break;
 | 
			
		||||
    case GUI_QUERY_RANGE:
 | 
			
		||||
      return (val>=arg && val<=argMax);
 | 
			
		||||
      break;
 | 
			
		||||
    case GUI_QUERY_RANGE_NOT:
 | 
			
		||||
      return (val!=-1 && (val<arg || val>argMax) && (!noteMode || val<120));
 | 
			
		||||
      break;
 | 
			
		||||
    case GUI_QUERY_ANY:
 | 
			
		||||
      return (val!=-1);
 | 
			
		||||
      break;
 | 
			
		||||
    case GUI_QUERY_NONE:
 | 
			
		||||
      return (val==-1);
 | 
			
		||||
      break;
 | 
			
		||||
  }
 | 
			
		||||
  return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void FurnaceGUI::doFind() {
 | 
			
		||||
  int firstOrder=0;
 | 
			
		||||
  int lastOrder=e->curSubSong->ordersLen-1;
 | 
			
		||||
 | 
			
		||||
  if (curQueryRangeY==1 || curQueryRangeY==2) {
 | 
			
		||||
    firstOrder=curOrder;
 | 
			
		||||
    lastOrder=curOrder;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  int firstRow=0;
 | 
			
		||||
  int lastRow=e->curSubSong->patLen-1;
 | 
			
		||||
 | 
			
		||||
  if (curQueryRangeY==1) {
 | 
			
		||||
    firstRow=selStart.y;
 | 
			
		||||
    lastRow=selEnd.y;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  int firstChan=0;
 | 
			
		||||
  int lastChan=e->getTotalChannelCount()-1;
 | 
			
		||||
 | 
			
		||||
  if (curQueryRangeX) {
 | 
			
		||||
    firstChan=curQueryRangeXMin;
 | 
			
		||||
    lastChan=curQueryRangeXMax;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  curQueryResults.clear();
 | 
			
		||||
 | 
			
		||||
  for (int i=firstOrder; i<=lastOrder; i++) {
 | 
			
		||||
    for (int j=firstRow; j<=lastRow; j++) {
 | 
			
		||||
      for (int k=firstChan; k<=lastChan; k++) {
 | 
			
		||||
        DivPattern* p=e->curPat[k].getPattern(e->curOrders->ord[k][i],false);
 | 
			
		||||
        bool matched=false;
 | 
			
		||||
        for (FurnaceGUIFindQuery& l: curQuery) {
 | 
			
		||||
          if (matched) break;
 | 
			
		||||
 | 
			
		||||
          if (!checkCondition(l.noteMode,l.note,l.noteMax,queryNote(p->data[j][0],p->data[j][1]),true)) continue;
 | 
			
		||||
          if (!checkCondition(l.insMode,l.ins,l.insMax,p->data[j][2])) continue;
 | 
			
		||||
          if (!checkCondition(l.volMode,l.vol,l.volMax,p->data[j][3])) continue;
 | 
			
		||||
 | 
			
		||||
          if (l.effectCount>0) {
 | 
			
		||||
            bool notMatched=false;
 | 
			
		||||
            switch (curQueryEffectPos) {
 | 
			
		||||
              case 0: // no
 | 
			
		||||
                for (int m=0; m<l.effectCount; m++) {
 | 
			
		||||
                  bool allGood=false;
 | 
			
		||||
                  for (int n=0; n<e->curPat[k].effectCols; n++) {
 | 
			
		||||
                    if (!checkCondition(l.effectMode[m],l.effect[m],l.effectMax[m],p->data[j][4+n*2])) continue;
 | 
			
		||||
                    if (!checkCondition(l.effectValMode[m],l.effectVal[m],l.effectValMax[m],p->data[j][5+n*2])) continue;
 | 
			
		||||
                    allGood=true;
 | 
			
		||||
                    break;
 | 
			
		||||
                  }
 | 
			
		||||
                  if (!allGood) {
 | 
			
		||||
                    notMatched=true;
 | 
			
		||||
                    break;
 | 
			
		||||
                  }
 | 
			
		||||
                }
 | 
			
		||||
                break;
 | 
			
		||||
              case 1: { // lax
 | 
			
		||||
                // locate first effect
 | 
			
		||||
                int posOfFirst=-1;
 | 
			
		||||
                for (int m=0; m<e->curPat[k].effectCols; m++) {
 | 
			
		||||
                  if (!checkCondition(l.effectMode[0],l.effect[0],l.effectMax[0],p->data[j][4+m*2])) continue;
 | 
			
		||||
                  if (!checkCondition(l.effectValMode[0],l.effectVal[0],l.effectValMax[0],p->data[j][5+m*2])) continue;
 | 
			
		||||
                  posOfFirst=m;
 | 
			
		||||
                  break;
 | 
			
		||||
                }
 | 
			
		||||
                if (posOfFirst<0) {
 | 
			
		||||
                  notMatched=true;
 | 
			
		||||
                  break;
 | 
			
		||||
                }
 | 
			
		||||
                // make sure we aren't too far to the right
 | 
			
		||||
                if ((posOfFirst+l.effectCount)>e->curPat[k].effectCols) {
 | 
			
		||||
                  notMatched=true;
 | 
			
		||||
                  break;
 | 
			
		||||
                }
 | 
			
		||||
                // search from first effect location
 | 
			
		||||
                for (int m=0; m<l.effectCount; m++) {
 | 
			
		||||
                  if (!checkCondition(l.effectMode[m],l.effect[m],l.effectMax[m],p->data[j][4+(m+posOfFirst)*2])) {
 | 
			
		||||
                    notMatched=true;
 | 
			
		||||
                    break;
 | 
			
		||||
                  }
 | 
			
		||||
                  if (!checkCondition(l.effectValMode[m],l.effectVal[m],l.effectValMax[m],p->data[j][5+(m+posOfFirst)*2])) {
 | 
			
		||||
                    notMatched=true;
 | 
			
		||||
                    break;
 | 
			
		||||
                  }
 | 
			
		||||
                }
 | 
			
		||||
                break;
 | 
			
		||||
              }
 | 
			
		||||
              case 2: // strict
 | 
			
		||||
                int effectMax=l.effectCount;
 | 
			
		||||
                if (effectMax>e->curPat[k].effectCols) {
 | 
			
		||||
                  notMatched=true;
 | 
			
		||||
                } else {
 | 
			
		||||
                  for (int m=0; m<effectMax; m++) {
 | 
			
		||||
                    if (!checkCondition(l.effectMode[m],l.effect[m],l.effectMax[m],p->data[j][4+m*2])) {
 | 
			
		||||
                      notMatched=true;
 | 
			
		||||
                      break;
 | 
			
		||||
                    }
 | 
			
		||||
                    if (!checkCondition(l.effectValMode[m],l.effectVal[m],l.effectValMax[m],p->data[j][5+m*2])) {
 | 
			
		||||
                      notMatched=true;
 | 
			
		||||
                      break;
 | 
			
		||||
                    }
 | 
			
		||||
                  }
 | 
			
		||||
                }
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            if (notMatched) continue;
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          matched=true;
 | 
			
		||||
        }
 | 
			
		||||
        if (matched) {
 | 
			
		||||
          curQueryResults.push_back(FurnaceGUIQueryResult(e->getCurrentSubSong(),i,k,j));
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  queryViewingResults=true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* issues with the find and replace function:
 | 
			
		||||
   - doesn't mark the module as modified
 | 
			
		||||
   - can't undo
 | 
			
		||||
   - replace notes to anything starting from C-0 to lower notes will have an octave higher, so set it to replace to C-0 it will becom C-1, b_1 will become B-0 and so on
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
void FurnaceGUI::doReplace() {
 | 
			
		||||
  doFind();
 | 
			
		||||
  queryViewingResults=false;
 | 
			
		||||
 | 
			
		||||
  bool* touched[DIV_MAX_CHANS];
 | 
			
		||||
  memset(touched,0,DIV_MAX_CHANS*sizeof(bool*));
 | 
			
		||||
 | 
			
		||||
  for (FurnaceGUIQueryResult& i: curQueryResults) {
 | 
			
		||||
    int patIndex=e->song.subsong[i.subsong]->orders.ord[i.x][i.order];
 | 
			
		||||
    DivPattern* p=e->song.subsong[i.subsong]->pat[i.x].getPattern(patIndex,true);
 | 
			
		||||
    if (touched[i.x]==NULL) {
 | 
			
		||||
      touched[i.x]=new bool[256*256];
 | 
			
		||||
      memset(touched[i.x],0,256*256*sizeof(bool));
 | 
			
		||||
    }
 | 
			
		||||
    if (touched[i.x][(patIndex<<8)|i.y]) continue;
 | 
			
		||||
    touched[i.x][(patIndex<<8)|i.y]=true;
 | 
			
		||||
    if (queryReplaceNoteDo) {
 | 
			
		||||
      switch (queryReplaceNoteMode) {
 | 
			
		||||
        case GUI_QUERY_REPLACE_SET:
 | 
			
		||||
          if (queryReplaceNote==130) { // macro release
 | 
			
		||||
            p->data[i.y][0]=102;
 | 
			
		||||
            p->data[i.y][1]=0;
 | 
			
		||||
          } else if (queryReplaceNote==129) { // note release
 | 
			
		||||
            p->data[i.y][0]=101;
 | 
			
		||||
            p->data[i.y][1]=0;
 | 
			
		||||
          } else if (queryReplaceNote==128) { // note off
 | 
			
		||||
            p->data[i.y][0]=100;
 | 
			
		||||
            p->data[i.y][1]=0;
 | 
			
		||||
          } else if (queryReplaceNote>=-60 && queryReplaceNote<120) { // note
 | 
			
		||||
            p->data[i.y][0]=(queryReplaceNote+60)%12;
 | 
			
		||||
            if (p->data[i.y][0]==0) p->data[i.y][0]=12;
 | 
			
		||||
            p->data[i.y][1]=(unsigned char)((queryReplaceNote-1)/12);
 | 
			
		||||
          } else { // invalid
 | 
			
		||||
            p->data[i.y][0]=0;
 | 
			
		||||
            p->data[i.y][1]=0;
 | 
			
		||||
          }
 | 
			
		||||
          break;
 | 
			
		||||
        case GUI_QUERY_REPLACE_ADD:
 | 
			
		||||
          if (p->data[i.y][0]<100) {
 | 
			
		||||
            int note=queryNote(p->data[i.y][0],p->data[i.y][1]);
 | 
			
		||||
            if (note>=-60 && note<120) {
 | 
			
		||||
              note+=queryReplaceNote;
 | 
			
		||||
              if (note<-60) note=-60;
 | 
			
		||||
              if (note>119) note=119;
 | 
			
		||||
 | 
			
		||||
              p->data[i.y][0]=(note+60)%12;
 | 
			
		||||
              if (p->data[i.y][0]==0) p->data[i.y][0]=12;
 | 
			
		||||
              p->data[i.y][1]=(unsigned char)((note-1)/12);
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
          break;
 | 
			
		||||
        case GUI_QUERY_REPLACE_ADD_OVERFLOW:
 | 
			
		||||
          if (p->data[i.y][0]<100) {
 | 
			
		||||
            int note=queryNote(p->data[i.y][0],p->data[i.y][1]);
 | 
			
		||||
            if (note>=-60 && note<120) {
 | 
			
		||||
              note+=queryReplaceNote;
 | 
			
		||||
              if (note<-60) {
 | 
			
		||||
                while (note<-60) note+=180;
 | 
			
		||||
              } else if (note>119) {
 | 
			
		||||
                while (note>119) note-=180;
 | 
			
		||||
              }
 | 
			
		||||
 | 
			
		||||
              p->data[i.y][0]=(note+60)%12;
 | 
			
		||||
              if (p->data[i.y][0]==0) p->data[i.y][0]=12;
 | 
			
		||||
              p->data[i.y][1]=(unsigned char)((note-1)/12);
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
          break;
 | 
			
		||||
        case GUI_QUERY_REPLACE_CLEAR:
 | 
			
		||||
          p->data[i.y][0]=0;
 | 
			
		||||
          p->data[i.y][1]=0;
 | 
			
		||||
          break;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (queryReplaceInsDo) {
 | 
			
		||||
      switch (queryReplaceInsMode) {
 | 
			
		||||
        case GUI_QUERY_REPLACE_SET:
 | 
			
		||||
          p->data[i.y][2]=queryReplaceIns;
 | 
			
		||||
          break;
 | 
			
		||||
        case GUI_QUERY_REPLACE_ADD:
 | 
			
		||||
          if (p->data[i.y][2]>=0) {
 | 
			
		||||
            p->data[i.y][2]+=queryReplaceIns;
 | 
			
		||||
            if (p->data[i.y][2]<0) p->data[i.y][2]=0;
 | 
			
		||||
            if (p->data[i.y][2]>255) p->data[i.y][2]=255;
 | 
			
		||||
          }
 | 
			
		||||
          break;
 | 
			
		||||
        case GUI_QUERY_REPLACE_ADD_OVERFLOW:
 | 
			
		||||
          if (p->data[i.y][2]>=0) p->data[i.y][2]=(p->data[i.y][2]+queryReplaceIns)&0xff;
 | 
			
		||||
          break;
 | 
			
		||||
        case GUI_QUERY_REPLACE_CLEAR:
 | 
			
		||||
          p->data[i.y][2]=-1;
 | 
			
		||||
          break;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (queryReplaceVolDo) {
 | 
			
		||||
      switch (queryReplaceVolMode) {
 | 
			
		||||
        case GUI_QUERY_REPLACE_SET:
 | 
			
		||||
          p->data[i.y][3]=queryReplaceVol;
 | 
			
		||||
          break;
 | 
			
		||||
        case GUI_QUERY_REPLACE_ADD:
 | 
			
		||||
          if (p->data[i.y][3]>=0) {
 | 
			
		||||
            p->data[i.y][3]+=queryReplaceVol;
 | 
			
		||||
            if (p->data[i.y][3]<0) p->data[i.y][3]=0;
 | 
			
		||||
            if (p->data[i.y][3]>255) p->data[i.y][3]=255;
 | 
			
		||||
          }
 | 
			
		||||
          break;
 | 
			
		||||
        case GUI_QUERY_REPLACE_ADD_OVERFLOW:
 | 
			
		||||
          if (p->data[i.y][3]>=0) p->data[i.y][3]=(p->data[i.y][3]+queryReplaceVol)&0xff;
 | 
			
		||||
          break;
 | 
			
		||||
        case GUI_QUERY_REPLACE_CLEAR:
 | 
			
		||||
          p->data[i.y][3]=-1;
 | 
			
		||||
          break;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    signed char effectOrder[8];
 | 
			
		||||
    memset(effectOrder,-1,8);
 | 
			
		||||
 | 
			
		||||
    switch (queryReplaceEffectPos) {
 | 
			
		||||
      case 0: // clear
 | 
			
		||||
        for (int j=0; j<e->song.subsong[i.subsong]->pat[i.x].effectCols; j++) {
 | 
			
		||||
          effectOrder[j]=j;
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
      case 1: { // replace matches
 | 
			
		||||
        int placementIndex=0;
 | 
			
		||||
        for (int j=0; j<e->song.subsong[i.subsong]->pat[i.x].effectCols; j++) {
 | 
			
		||||
          if (p->data[i.y][4+j*2]!=-1 || p->data[i.y][5+j*2]!=-1) {
 | 
			
		||||
            effectOrder[placementIndex++]=j;
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
      case 2: { // replace matches then free spaces
 | 
			
		||||
        int placementIndex=0;
 | 
			
		||||
        for (int j=0; j<e->song.subsong[i.subsong]->pat[i.x].effectCols; j++) {
 | 
			
		||||
          if (p->data[i.y][4+j*2]!=-1 || p->data[i.y][5+j*2]!=-1) {
 | 
			
		||||
            effectOrder[placementIndex++]=j;
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
        for (int j=0; j<e->song.subsong[i.subsong]->pat[i.x].effectCols; j++) {
 | 
			
		||||
          if (p->data[i.y][4+j*2]==-1 && p->data[i.y][5+j*2]==-1) {
 | 
			
		||||
            effectOrder[placementIndex++]=j;
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
      case 3: { // insert in free spaces
 | 
			
		||||
        int placementIndex=0;
 | 
			
		||||
        for (int j=0; j<e->song.subsong[i.subsong]->pat[i.x].effectCols; j++) {
 | 
			
		||||
          if (p->data[i.y][4+j*2]==-1 && p->data[i.y][5+j*2]==-1) {
 | 
			
		||||
            effectOrder[placementIndex++]=j;
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for (int j=0; j<queryReplaceEffectCount; j++) {
 | 
			
		||||
      signed char pos=effectOrder[j];
 | 
			
		||||
      if (pos==-1) continue;
 | 
			
		||||
      if (queryReplaceEffectDo[j]) {
 | 
			
		||||
        switch (queryReplaceEffectMode[j]) {
 | 
			
		||||
          case GUI_QUERY_REPLACE_SET:
 | 
			
		||||
            p->data[i.y][4+pos*2]=queryReplaceEffect[j];
 | 
			
		||||
            break;
 | 
			
		||||
          case GUI_QUERY_REPLACE_ADD:
 | 
			
		||||
            if (p->data[i.y][4+pos*2]>=0) {
 | 
			
		||||
              p->data[i.y][4+pos*2]+=queryReplaceEffect[j];
 | 
			
		||||
              if (p->data[i.y][4+pos*2]<0) p->data[i.y][4+pos*2]=0;
 | 
			
		||||
              if (p->data[i.y][4+pos*2]>255) p->data[i.y][4+pos*2]=255;
 | 
			
		||||
            }
 | 
			
		||||
            break;
 | 
			
		||||
          case GUI_QUERY_REPLACE_ADD_OVERFLOW:
 | 
			
		||||
            if (p->data[i.y][4+pos*2]>=0) p->data[i.y][4+pos*2]=(p->data[i.y][4+pos*2]+queryReplaceEffect[j])&0xff;
 | 
			
		||||
            break;
 | 
			
		||||
          case GUI_QUERY_REPLACE_CLEAR:
 | 
			
		||||
            p->data[i.y][4+pos*2]=-1;
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if (queryReplaceEffectValDo[j]) {
 | 
			
		||||
        switch (queryReplaceEffectValMode[j]) {
 | 
			
		||||
          case GUI_QUERY_REPLACE_SET:
 | 
			
		||||
            p->data[i.y][5+pos*2]=queryReplaceEffectVal[j];
 | 
			
		||||
            break;
 | 
			
		||||
          case GUI_QUERY_REPLACE_ADD:
 | 
			
		||||
            if (p->data[i.y][5+pos*2]>=0) {
 | 
			
		||||
              p->data[i.y][5+pos*2]+=queryReplaceEffectVal[j];
 | 
			
		||||
              if (p->data[i.y][5+pos*2]<0) p->data[i.y][5+pos*2]=0;
 | 
			
		||||
              if (p->data[i.y][5+pos*2]>255) p->data[i.y][5+pos*2]=255;
 | 
			
		||||
            }
 | 
			
		||||
            break;
 | 
			
		||||
          case GUI_QUERY_REPLACE_ADD_OVERFLOW:
 | 
			
		||||
            if (p->data[i.y][5+pos*2]>=0) p->data[i.y][5+pos*2]=(p->data[i.y][5+pos*2]+queryReplaceEffectVal[j])&0xff;
 | 
			
		||||
            break;
 | 
			
		||||
          case GUI_QUERY_REPLACE_CLEAR:
 | 
			
		||||
            p->data[i.y][5+pos*2]=-1;
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  for (int i=0; i<DIV_MAX_CHANS; i++) {
 | 
			
		||||
    if (touched[i]!=NULL) delete[] touched[i];
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (!curQueryResults.empty()) {
 | 
			
		||||
    MARK_MODIFIED;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define FIRST_VISIBLE(x) (x==GUI_QUERY_MATCH || x==GUI_QUERY_MATCH_NOT || x==GUI_QUERY_RANGE || x==GUI_QUERY_RANGE_NOT)
 | 
			
		||||
#define SECOND_VISIBLE(x) (x==GUI_QUERY_RANGE || x==GUI_QUERY_RANGE_NOT)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -38,13 +429,85 @@ void FurnaceGUI::drawFindReplace() {
 | 
			
		|||
    int index=0;
 | 
			
		||||
    int eraseIndex=-1;
 | 
			
		||||
    char tempID[1024];
 | 
			
		||||
    if (ImGui::BeginTabBar("FindOrReplace")) {
 | 
			
		||||
      if (ImGui::BeginTabItem("Find")) {
 | 
			
		||||
        if (queryViewingResults) {
 | 
			
		||||
          if (!curQueryResults.empty()) {
 | 
			
		||||
            ImVec2 avail=ImGui::GetContentRegionAvail();
 | 
			
		||||
            avail.y-=ImGui::GetFrameHeightWithSpacing();
 | 
			
		||||
            if (ImGui::BeginTable("FindResults",4,ImGuiTableFlags_Borders|ImGuiTableFlags_ScrollY,avail)) {
 | 
			
		||||
              ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthFixed,ImGui::CalcTextSize("order").x);
 | 
			
		||||
              ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthFixed,ImGui::CalcTextSize("row").x);
 | 
			
		||||
              ImGui::TableSetupColumn("c2",ImGuiTableColumnFlags_WidthStretch);
 | 
			
		||||
              ImGui::TableSetupColumn("c3",ImGuiTableColumnFlags_WidthFixed);
 | 
			
		||||
 | 
			
		||||
              ImGui::TableSetupScrollFreeze(0,1);
 | 
			
		||||
 | 
			
		||||
              ImGui::TableNextRow(ImGuiTableRowFlags_Headers);
 | 
			
		||||
              ImGui::TableNextColumn();
 | 
			
		||||
              ImGui::Text("order");
 | 
			
		||||
              ImGui::TableNextColumn();
 | 
			
		||||
              ImGui::Text("row");
 | 
			
		||||
              ImGui::TableNextColumn();
 | 
			
		||||
              ImGui::Text("channel");
 | 
			
		||||
              ImGui::TableNextColumn();
 | 
			
		||||
              ImGui::Text("go");
 | 
			
		||||
 | 
			
		||||
              int index=0;
 | 
			
		||||
              for (FurnaceGUIQueryResult& i: curQueryResults) {
 | 
			
		||||
                ImGui::TableNextRow();
 | 
			
		||||
                ImGui::TableNextColumn();
 | 
			
		||||
                if (settings.orderRowsBase==1) {
 | 
			
		||||
                  ImGui::Text("%.2X",i.order);
 | 
			
		||||
                } else {
 | 
			
		||||
                  ImGui::Text("%d",i.order);
 | 
			
		||||
                }
 | 
			
		||||
                ImGui::TableNextColumn();
 | 
			
		||||
                if (settings.patRowsBase==1) {
 | 
			
		||||
                  ImGui::Text("%.2X",i.y);
 | 
			
		||||
                } else {
 | 
			
		||||
                  ImGui::Text("%d",i.y);
 | 
			
		||||
                }
 | 
			
		||||
                ImGui::TableNextColumn();
 | 
			
		||||
                ImGui::Text("%d (%s)",i.x+1,e->getChannelName(i.x));
 | 
			
		||||
                if (ImGui::TableNextColumn()) {
 | 
			
		||||
                  snprintf(tempID,1024,ICON_FA_CHEVRON_RIGHT "##_FR%d",index);
 | 
			
		||||
                  if (ImGui::Selectable(tempID)) {
 | 
			
		||||
                    e->changeSongP(i.subsong);
 | 
			
		||||
                    if (e->isPlaying()) {
 | 
			
		||||
                      followPattern=false;
 | 
			
		||||
                    } else {
 | 
			
		||||
                      e->setOrder(i.order);
 | 
			
		||||
                    }
 | 
			
		||||
                    curOrder=i.order;
 | 
			
		||||
                    cursor.xCoarse=i.x;
 | 
			
		||||
                    cursor.xFine=0;
 | 
			
		||||
                    cursor.y=i.y;
 | 
			
		||||
                    selStart=cursor;
 | 
			
		||||
                    selEnd=cursor;
 | 
			
		||||
                    demandScrollX=true;
 | 
			
		||||
                    updateScroll(cursor.y);
 | 
			
		||||
                    nextWindow=GUI_WINDOW_PATTERN;
 | 
			
		||||
                  }
 | 
			
		||||
                }
 | 
			
		||||
                index++;
 | 
			
		||||
              }
 | 
			
		||||
              ImGui::EndTable();
 | 
			
		||||
            }
 | 
			
		||||
          } else {
 | 
			
		||||
            ImGui::Text("no matches found!");
 | 
			
		||||
          }
 | 
			
		||||
          if (ImGui::Button("Back")) {
 | 
			
		||||
            queryViewingResults=false;
 | 
			
		||||
          }
 | 
			
		||||
        } else {
 | 
			
		||||
          for (FurnaceGUIFindQuery& i: curQuery) {
 | 
			
		||||
            ImGui::PushID(index+0x100);
 | 
			
		||||
            if (ImGui::BeginTable("FindRep",4,ImGuiTableFlags_BordersOuter)) {
 | 
			
		||||
              ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthFixed);
 | 
			
		||||
              ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthStretch,0.5);
 | 
			
		||||
              ImGui::TableSetupColumn("c2",ImGuiTableColumnFlags_WidthStretch,0.25);
 | 
			
		||||
              ImGui::TableSetupColumn("c3",ImGuiTableColumnFlags_WidthStretch,0.25);
 | 
			
		||||
        ImGui::PushID(index);
 | 
			
		||||
              ImGui::TableNextRow();
 | 
			
		||||
              ImGui::TableNextColumn();
 | 
			
		||||
              ImGui::Text("Note");
 | 
			
		||||
| 
						 | 
				
			
			@ -273,15 +736,11 @@ void FurnaceGUI::drawFindReplace() {
 | 
			
		|||
                  i.effectCount--;
 | 
			
		||||
                }
 | 
			
		||||
              }
 | 
			
		||||
        ImGui::PopID();
 | 
			
		||||
              ImGui::EndTable();
 | 
			
		||||
            }
 | 
			
		||||
            ImGui::PopID();
 | 
			
		||||
            index++;
 | 
			
		||||
          }
 | 
			
		||||
    if (ImGui::Button("Find")) {
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
    ImGui::SameLine();
 | 
			
		||||
          if (eraseIndex>=0) {
 | 
			
		||||
            curQuery.erase(curQuery.begin()+eraseIndex);
 | 
			
		||||
          }
 | 
			
		||||
| 
						 | 
				
			
			@ -289,7 +748,11 @@ void FurnaceGUI::drawFindReplace() {
 | 
			
		|||
            curQuery.push_back(FurnaceGUIFindQuery());
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
    if (ImGui::BeginTable("QueryLimits",2)) {
 | 
			
		||||
          if (ImGui::BeginTable("QueryLimits",3)) {
 | 
			
		||||
            ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthFixed);
 | 
			
		||||
            ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthStretch,0.5f);
 | 
			
		||||
            ImGui::TableSetupColumn("c2",ImGuiTableColumnFlags_WidthStretch,0.5f);
 | 
			
		||||
 | 
			
		||||
            ImGui::TableNextRow();
 | 
			
		||||
            ImGui::TableNextColumn();
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -332,6 +795,7 @@ void FurnaceGUI::drawFindReplace() {
 | 
			
		|||
            }
 | 
			
		||||
            ImGui::EndDisabled();
 | 
			
		||||
 | 
			
		||||
            ImGui::TableNextColumn();
 | 
			
		||||
            ImGui::Text("Match effect position:");
 | 
			
		||||
 | 
			
		||||
            if (ImGui::RadioButton("No",curQueryEffectPos==0)) {
 | 
			
		||||
| 
						 | 
				
			
			@ -353,26 +817,199 @@ void FurnaceGUI::drawFindReplace() {
 | 
			
		|||
              ImGui::SetTooltip("match effects only if they appear exactly as specified.");
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
      ImGui::TableNextRow();
 | 
			
		||||
      ImGui::TableNextColumn();
 | 
			
		||||
      ImGui::Checkbox("From start",&curQueryFromStart);
 | 
			
		||||
      ImGui::TableNextColumn();
 | 
			
		||||
      ImGui::Checkbox("Backwards",&curQueryBackwards);
 | 
			
		||||
 | 
			
		||||
            ImGui::EndTable();
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
    if (ImGui::TreeNode("Replace")) {
 | 
			
		||||
      if (ImGui::BeginTable("QueryReplace",3)) {
 | 
			
		||||
          if (ImGui::Button("Find")) {
 | 
			
		||||
            doFind();
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
        ImGui::EndTabItem();
 | 
			
		||||
      }
 | 
			
		||||
      if (ImGui::BeginTabItem("Replace")) {
 | 
			
		||||
        if (ImGui::BeginTable("QueryReplace",3,ImGuiTableFlags_BordersOuter)) {
 | 
			
		||||
          ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthFixed);
 | 
			
		||||
          ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthStretch,0.5);
 | 
			
		||||
          ImGui::TableSetupColumn("c2",ImGuiTableColumnFlags_WidthStretch,0.5);
 | 
			
		||||
 | 
			
		||||
          ImGui::TableNextRow();
 | 
			
		||||
          ImGui::TableNextColumn();
 | 
			
		||||
        ImGui::Text("...");
 | 
			
		||||
          ImGui::Checkbox("Note",&queryReplaceNoteDo);
 | 
			
		||||
          ImGui::TableNextColumn();
 | 
			
		||||
          ImGui::BeginDisabled(!queryReplaceNoteDo);
 | 
			
		||||
          ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
 | 
			
		||||
          ImGui::Combo("##NRMode",&queryReplaceNoteMode,queryReplaceModes,GUI_QUERY_REPLACE_MAX);
 | 
			
		||||
          ImGui::TableNextColumn();
 | 
			
		||||
          ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
 | 
			
		||||
          if (queryReplaceNoteMode==GUI_QUERY_REPLACE_SET) {
 | 
			
		||||
            if (queryReplaceNote==130) {
 | 
			
		||||
              snprintf(tempID,1024,"REL");
 | 
			
		||||
            } else if (queryReplaceNote==129) {
 | 
			
		||||
              snprintf(tempID,1024,"===");
 | 
			
		||||
            } else if (queryReplaceNote==128) {
 | 
			
		||||
              snprintf(tempID,1024,"OFF");
 | 
			
		||||
            } else if (queryReplaceNote>=-60 && queryReplaceNote<120) {
 | 
			
		||||
              snprintf(tempID,1024,"%s",noteNames[queryReplaceNote+60]);
 | 
			
		||||
            } else {
 | 
			
		||||
              snprintf(tempID,1024,"???");
 | 
			
		||||
              queryReplaceNote=0;
 | 
			
		||||
            }
 | 
			
		||||
            if (ImGui::BeginCombo("##NRValueC",tempID)) {
 | 
			
		||||
              for (int j=0; j<180; j++) {
 | 
			
		||||
                snprintf(tempID,1024,"%s",noteNames[j]);
 | 
			
		||||
                if (ImGui::Selectable(tempID,queryReplaceNote==(j-60))) {
 | 
			
		||||
                  queryReplaceNote=j-60;
 | 
			
		||||
                }
 | 
			
		||||
              }
 | 
			
		||||
              if (ImGui::Selectable("OFF",queryReplaceNote==128)) {
 | 
			
		||||
                queryReplaceNote=128;
 | 
			
		||||
              }
 | 
			
		||||
              if (ImGui::Selectable("===",queryReplaceNote==129)) {
 | 
			
		||||
                queryReplaceNote=129;
 | 
			
		||||
              }
 | 
			
		||||
              if (ImGui::Selectable("REL",queryReplaceNote==130)) {
 | 
			
		||||
                queryReplaceNote=130;
 | 
			
		||||
              }
 | 
			
		||||
              ImGui::EndCombo();
 | 
			
		||||
            }
 | 
			
		||||
          } else if (queryReplaceNoteMode==GUI_QUERY_REPLACE_ADD || queryReplaceNoteMode==GUI_QUERY_REPLACE_ADD_OVERFLOW) {
 | 
			
		||||
            if (ImGui::InputInt("##NRValue",&queryReplaceNote,1,12)) {
 | 
			
		||||
              if (queryReplaceNote<-180) queryReplaceNote=-180;
 | 
			
		||||
              if (queryReplaceNote>180) queryReplaceNote=180;
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
          ImGui::EndDisabled();
 | 
			
		||||
 | 
			
		||||
          ImGui::TableNextRow();
 | 
			
		||||
          ImGui::TableNextColumn();
 | 
			
		||||
          ImGui::Checkbox("Ins",&queryReplaceInsDo);
 | 
			
		||||
          ImGui::TableNextColumn();
 | 
			
		||||
          ImGui::BeginDisabled(!queryReplaceInsDo);
 | 
			
		||||
          ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
 | 
			
		||||
          ImGui::Combo("##IRMode",&queryReplaceInsMode,queryReplaceModes,GUI_QUERY_REPLACE_MAX);
 | 
			
		||||
          ImGui::TableNextColumn();
 | 
			
		||||
          ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
 | 
			
		||||
          if (queryReplaceInsMode==GUI_QUERY_REPLACE_SET) {
 | 
			
		||||
            if (ImGui::InputScalar("##IRValueH",ImGuiDataType_S32,&queryReplaceIns,&_ONE,&_SIXTEEN,"%.2X",ImGuiInputTextFlags_CharsHexadecimal)) {
 | 
			
		||||
              if (queryReplaceIns<0) queryReplaceIns=0;
 | 
			
		||||
              if (queryReplaceIns>255) queryReplaceIns=255;
 | 
			
		||||
            }
 | 
			
		||||
          } else if (queryReplaceInsMode==GUI_QUERY_REPLACE_ADD || queryReplaceInsMode==GUI_QUERY_REPLACE_ADD_OVERFLOW) {
 | 
			
		||||
            if (ImGui::InputInt("##IRValue",&queryReplaceIns,1,12)) {
 | 
			
		||||
              if (queryReplaceIns<-255) queryReplaceIns=-255;
 | 
			
		||||
              if (queryReplaceIns>255) queryReplaceIns=255;
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
          ImGui::EndDisabled();
 | 
			
		||||
 | 
			
		||||
          ImGui::TableNextRow();
 | 
			
		||||
          ImGui::TableNextColumn();
 | 
			
		||||
          ImGui::Checkbox("Volume",&queryReplaceVolDo);
 | 
			
		||||
          ImGui::TableNextColumn();
 | 
			
		||||
          ImGui::BeginDisabled(!queryReplaceVolDo);
 | 
			
		||||
          ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
 | 
			
		||||
          ImGui::Combo("##VRMode",&queryReplaceVolMode,queryReplaceModes,GUI_QUERY_REPLACE_MAX);
 | 
			
		||||
          ImGui::TableNextColumn();
 | 
			
		||||
          ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
 | 
			
		||||
          if (queryReplaceVolMode==GUI_QUERY_REPLACE_SET) {
 | 
			
		||||
            if (ImGui::InputScalar("##VRValueH",ImGuiDataType_S32,&queryReplaceVol,&_ONE,&_SIXTEEN,"%.2X",ImGuiInputTextFlags_CharsHexadecimal)) {
 | 
			
		||||
              if (queryReplaceVol<0) queryReplaceVol=0;
 | 
			
		||||
              if (queryReplaceVol>255) queryReplaceVol=255;
 | 
			
		||||
            }
 | 
			
		||||
          } else if (queryReplaceVolMode==GUI_QUERY_REPLACE_ADD || queryReplaceVolMode==GUI_QUERY_REPLACE_ADD_OVERFLOW) {
 | 
			
		||||
            if (ImGui::InputInt("##VRValue",&queryReplaceVol,1,12)) {
 | 
			
		||||
              if (queryReplaceVol<-255) queryReplaceVol=-255;
 | 
			
		||||
              if (queryReplaceVol>255) queryReplaceVol=255;
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
          ImGui::EndDisabled();
 | 
			
		||||
 | 
			
		||||
          for (int i=0; i<queryReplaceEffectCount; i++) {
 | 
			
		||||
            ImGui::PushID(0x100+i);
 | 
			
		||||
            ImGui::TableNextRow();
 | 
			
		||||
            ImGui::TableNextColumn();
 | 
			
		||||
            ImGui::Checkbox("Effect",&queryReplaceEffectDo[i]);
 | 
			
		||||
            ImGui::TableNextColumn();
 | 
			
		||||
            ImGui::BeginDisabled(!queryReplaceEffectDo[i]);
 | 
			
		||||
            ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
 | 
			
		||||
            ImGui::Combo("##ERMode",&queryReplaceEffectMode[i],queryReplaceModes,GUI_QUERY_REPLACE_MAX);
 | 
			
		||||
            ImGui::TableNextColumn();
 | 
			
		||||
            ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
 | 
			
		||||
            if (queryReplaceEffectMode[i]==GUI_QUERY_REPLACE_SET) {
 | 
			
		||||
              if (ImGui::InputScalar("##ERValueH",ImGuiDataType_S32,&queryReplaceEffect[i],&_ONE,&_SIXTEEN,"%.2X",ImGuiInputTextFlags_CharsHexadecimal)) {
 | 
			
		||||
                if (queryReplaceEffect[i]<0) queryReplaceEffect[i]=0;
 | 
			
		||||
                if (queryReplaceEffect[i]>255) queryReplaceEffect[i]=255;
 | 
			
		||||
              }
 | 
			
		||||
            } else if (queryReplaceEffectMode[i]==GUI_QUERY_REPLACE_ADD || queryReplaceEffectMode[i]==GUI_QUERY_REPLACE_ADD_OVERFLOW) {
 | 
			
		||||
              if (ImGui::InputInt("##ERValue",&queryReplaceEffect[i],1,12)) {
 | 
			
		||||
                if (queryReplaceEffect[i]<-255) queryReplaceEffect[i]=-255;
 | 
			
		||||
                if (queryReplaceEffect[i]>255) queryReplaceEffect[i]=255;
 | 
			
		||||
              }
 | 
			
		||||
            }
 | 
			
		||||
            ImGui::EndDisabled();
 | 
			
		||||
 | 
			
		||||
            ImGui::TableNextRow();
 | 
			
		||||
            ImGui::TableNextColumn();
 | 
			
		||||
            ImGui::Checkbox("Value",&queryReplaceEffectValDo[i]);
 | 
			
		||||
            ImGui::TableNextColumn();
 | 
			
		||||
            ImGui::BeginDisabled(!queryReplaceEffectValDo[i]);
 | 
			
		||||
            ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
 | 
			
		||||
            ImGui::Combo("##ERMode",&queryReplaceEffectValMode[i],queryReplaceModes,GUI_QUERY_REPLACE_MAX);
 | 
			
		||||
            ImGui::TableNextColumn();
 | 
			
		||||
            ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
 | 
			
		||||
            if (queryReplaceEffectValMode[i]==GUI_QUERY_REPLACE_SET) {
 | 
			
		||||
              if (ImGui::InputScalar("##ERValueH",ImGuiDataType_S32,&queryReplaceEffectVal[i],&_ONE,&_SIXTEEN,"%.2X",ImGuiInputTextFlags_CharsHexadecimal)) {
 | 
			
		||||
                if (queryReplaceEffectVal[i]<0) queryReplaceEffectVal[i]=0;
 | 
			
		||||
                if (queryReplaceEffectVal[i]>255) queryReplaceEffectVal[i]=255;
 | 
			
		||||
              }
 | 
			
		||||
            } else if (queryReplaceEffectValMode[i]==GUI_QUERY_REPLACE_ADD || queryReplaceEffectValMode[i]==GUI_QUERY_REPLACE_ADD_OVERFLOW) {
 | 
			
		||||
              if (ImGui::InputInt("##ERValue",&queryReplaceEffectVal[i],1,12)) {
 | 
			
		||||
                if (queryReplaceEffectVal[i]<-255) queryReplaceEffectVal[i]=-255;
 | 
			
		||||
                if (queryReplaceEffectVal[i]>255) queryReplaceEffectVal[i]=255;
 | 
			
		||||
              }
 | 
			
		||||
            }
 | 
			
		||||
            ImGui::EndDisabled();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            ImGui::PopID();
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          ImGui::TableNextRow();
 | 
			
		||||
          ImGui::TableNextColumn();
 | 
			
		||||
          ImGui::TableNextColumn();
 | 
			
		||||
          if (queryReplaceEffectCount<8) {
 | 
			
		||||
            if (ImGui::Button("Add effect")) {
 | 
			
		||||
              queryReplaceEffectCount++;
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
          ImGui::TableNextColumn();
 | 
			
		||||
          if (queryReplaceEffectCount>0) {
 | 
			
		||||
            if (ImGui::Button("Remove effect")) {
 | 
			
		||||
              queryReplaceEffectCount--;
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          ImGui::EndTable();
 | 
			
		||||
        }
 | 
			
		||||
        ImGui::Text("Effect replace mode:");
 | 
			
		||||
        if (ImGui::RadioButton("Clear effects",queryReplaceEffectPos==0)) {
 | 
			
		||||
          queryReplaceEffectPos=0;
 | 
			
		||||
        }
 | 
			
		||||
        if (ImGui::RadioButton("Replace matches only",queryReplaceEffectPos==1)) {
 | 
			
		||||
          queryReplaceEffectPos=1;
 | 
			
		||||
        }
 | 
			
		||||
        if (ImGui::RadioButton("Replace matches, then free spaces",queryReplaceEffectPos==2)) {
 | 
			
		||||
          queryReplaceEffectPos=2;
 | 
			
		||||
        }
 | 
			
		||||
        if (ImGui::RadioButton("Insert in free spaces",queryReplaceEffectPos==3)) {
 | 
			
		||||
          queryReplaceEffectPos=3;
 | 
			
		||||
        }
 | 
			
		||||
        if (ImGui::Button("Replace##QueryReplace")) {
 | 
			
		||||
        // TODO
 | 
			
		||||
          doReplace();
 | 
			
		||||
        }
 | 
			
		||||
      ImGui::TreePop();
 | 
			
		||||
        ImGui::EndTabItem();
 | 
			
		||||
      }
 | 
			
		||||
      ImGui::EndTabBar();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_FIND;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4105,6 +4105,7 @@ bool FurnaceGUI::init() {
 | 
			
		|||
 | 
			
		||||
#ifndef __APPLE__
 | 
			
		||||
  if (settings.dpiScale<0.5f) {
 | 
			
		||||
    // TODO: replace with a function to actually detect the display scaling factor as it's unreliable.
 | 
			
		||||
    SDL_GetDisplayDPI(SDL_GetWindowDisplayIndex(sdlWin),&dpiScaleF,NULL,NULL);
 | 
			
		||||
    dpiScale=round(dpiScaleF/96.0f);
 | 
			
		||||
    if (dpiScale<1) dpiScale=1;
 | 
			
		||||
| 
						 | 
				
			
			@ -4431,6 +4432,26 @@ FurnaceGUI::FurnaceGUI():
 | 
			
		|||
  wavePreviewLen(32),
 | 
			
		||||
  wavePreviewHeight(255),
 | 
			
		||||
  wavePreviewInit(true),
 | 
			
		||||
  pgSys(0),
 | 
			
		||||
  pgAddr(0),
 | 
			
		||||
  pgVal(0),
 | 
			
		||||
  curQueryRangeX(false),
 | 
			
		||||
  curQueryBackwards(false),
 | 
			
		||||
  curQueryRangeXMin(0), curQueryRangeXMax(0),
 | 
			
		||||
  curQueryRangeY(0),
 | 
			
		||||
  curQueryEffectPos(0),
 | 
			
		||||
  queryReplaceEffectCount(0),
 | 
			
		||||
  queryReplaceEffectPos(0),
 | 
			
		||||
  queryReplaceNoteMode(0),
 | 
			
		||||
  queryReplaceInsMode(0),
 | 
			
		||||
  queryReplaceVolMode(0),
 | 
			
		||||
  queryReplaceNote(0),
 | 
			
		||||
  queryReplaceIns(0),
 | 
			
		||||
  queryReplaceVol(0),
 | 
			
		||||
  queryReplaceNoteDo(false),
 | 
			
		||||
  queryReplaceInsDo(false),
 | 
			
		||||
  queryReplaceVolDo(false),
 | 
			
		||||
  queryViewingResults(false),
 | 
			
		||||
  wavePreviewOn(false),
 | 
			
		||||
  wavePreviewKey((SDL_Scancode)0),
 | 
			
		||||
  wavePreviewNote(0),
 | 
			
		||||
| 
						 | 
				
			
			@ -4624,4 +4645,11 @@ FurnaceGUI::FurnaceGUI():
 | 
			
		|||
 | 
			
		||||
  memset(pianoKeyHit,0,sizeof(float)*180);
 | 
			
		||||
  memset(pianoKeyPressed,0,sizeof(bool)*180);
 | 
			
		||||
 | 
			
		||||
  memset(queryReplaceEffectMode,0,sizeof(int)*8);
 | 
			
		||||
  memset(queryReplaceEffectValMode,0,sizeof(int)*8);
 | 
			
		||||
  memset(queryReplaceEffect,0,sizeof(int)*8);
 | 
			
		||||
  memset(queryReplaceEffectVal,0,sizeof(int)*8);
 | 
			
		||||
  memset(queryReplaceEffectDo,0,sizeof(bool)*8);
 | 
			
		||||
  memset(queryReplaceEffectValDo,0,sizeof(bool)*8);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -566,7 +566,8 @@ enum ActionType {
 | 
			
		|||
  GUI_UNDO_PATTERN_INVERT_VAL,
 | 
			
		||||
  GUI_UNDO_PATTERN_FLIP,
 | 
			
		||||
  GUI_UNDO_PATTERN_COLLAPSE,
 | 
			
		||||
  GUI_UNDO_PATTERN_EXPAND
 | 
			
		||||
  GUI_UNDO_PATTERN_EXPAND,
 | 
			
		||||
  GUI_UNDO_REPLACE
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct UndoPatternData {
 | 
			
		||||
| 
						 | 
				
			
			@ -818,6 +819,7 @@ enum FurnaceGUIFindQueryModes {
 | 
			
		|||
enum FurnaceGUIFindQueryReplaceModes {
 | 
			
		||||
  GUI_QUERY_REPLACE_SET=0,
 | 
			
		||||
  GUI_QUERY_REPLACE_ADD,
 | 
			
		||||
  GUI_QUERY_REPLACE_ADD_OVERFLOW,
 | 
			
		||||
  GUI_QUERY_REPLACE_CLEAR,
 | 
			
		||||
 | 
			
		||||
  GUI_QUERY_REPLACE_MAX
 | 
			
		||||
| 
						 | 
				
			
			@ -855,6 +857,20 @@ struct FurnaceGUIFindQuery {
 | 
			
		|||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct FurnaceGUIQueryResult {
 | 
			
		||||
  int subsong, order, x, y;
 | 
			
		||||
  FurnaceGUIQueryResult():
 | 
			
		||||
    subsong(0),
 | 
			
		||||
    order(0),
 | 
			
		||||
    x(0),
 | 
			
		||||
    y(0) {}
 | 
			
		||||
  FurnaceGUIQueryResult(int ss, int o, int xPos, int yPos):
 | 
			
		||||
    subsong(ss),
 | 
			
		||||
    order(o),
 | 
			
		||||
    x(xPos),
 | 
			
		||||
    y(yPos) {}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class FurnaceGUI {
 | 
			
		||||
  DivEngine* e;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1170,11 +1186,31 @@ class FurnaceGUI {
 | 
			
		|||
  int pgSys, pgAddr, pgVal;
 | 
			
		||||
 | 
			
		||||
  std::vector<FurnaceGUIFindQuery> curQuery;
 | 
			
		||||
  bool curQueryRangeX, curQueryFromStart, curQueryBackwards;
 | 
			
		||||
  std::vector<FurnaceGUIQueryResult> curQueryResults;
 | 
			
		||||
  bool curQueryRangeX, curQueryBackwards;
 | 
			
		||||
  int curQueryRangeXMin, curQueryRangeXMax;
 | 
			
		||||
  int curQueryRangeY;
 | 
			
		||||
  int curQueryEffectPos;
 | 
			
		||||
 | 
			
		||||
  int queryReplaceEffectCount;
 | 
			
		||||
  int queryReplaceEffectPos;
 | 
			
		||||
  int queryReplaceNoteMode;
 | 
			
		||||
  int queryReplaceInsMode;
 | 
			
		||||
  int queryReplaceVolMode;
 | 
			
		||||
  int queryReplaceEffectMode[8];
 | 
			
		||||
  int queryReplaceEffectValMode[8];
 | 
			
		||||
  int queryReplaceNote;
 | 
			
		||||
  int queryReplaceIns;
 | 
			
		||||
  int queryReplaceVol;
 | 
			
		||||
  int queryReplaceEffect[8];
 | 
			
		||||
  int queryReplaceEffectVal[8];
 | 
			
		||||
  bool queryReplaceNoteDo;
 | 
			
		||||
  bool queryReplaceInsDo;
 | 
			
		||||
  bool queryReplaceVolDo;
 | 
			
		||||
  bool queryReplaceEffectDo[8];
 | 
			
		||||
  bool queryReplaceEffectValDo[8];
 | 
			
		||||
  bool queryViewingResults;
 | 
			
		||||
 | 
			
		||||
  struct ActiveNote {
 | 
			
		||||
    int chan;
 | 
			
		||||
    int note;
 | 
			
		||||
| 
						 | 
				
			
			@ -1456,6 +1492,8 @@ class FurnaceGUI {
 | 
			
		|||
  void doExpand(int multiplier);
 | 
			
		||||
  void doUndo();
 | 
			
		||||
  void doRedo();
 | 
			
		||||
  void doFind();
 | 
			
		||||
  void doReplace();
 | 
			
		||||
  void editOptions(bool topMenu);
 | 
			
		||||
  void noteInput(int num, int key, int vol=-1);
 | 
			
		||||
  void valueInput(int num, bool direct=false, int target=-1);
 | 
			
		||||
| 
						 | 
				
			
			@ -1502,6 +1540,8 @@ class FurnaceGUI {
 | 
			
		|||
    void addScroll(int amount);
 | 
			
		||||
    void setFileName(String name);
 | 
			
		||||
    void runBackupThread();
 | 
			
		||||
    void pushPartBlend();
 | 
			
		||||
    void popPartBlend();
 | 
			
		||||
    int processEvent(SDL_Event* ev);
 | 
			
		||||
    bool loop();
 | 
			
		||||
    bool finish();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1345,6 +1345,9 @@ void FurnaceGUI::drawMacros(std::vector<FurnaceGUIMacroDesc>& macros) {
 | 
			
		|||
#define CENTER_VSLIDER \
 | 
			
		||||
  ImGui::SetCursorPosX(ImGui::GetCursorPosX()+0.5f*ImGui::GetContentRegionAvail().x-10.0f*dpiScale);
 | 
			
		||||
 | 
			
		||||
#define CENTER_TEXT_20(text) \
 | 
			
		||||
  ImGui::SetCursorPosX(ImGui::GetCursorPosX()+0.5*(20.0f*dpiScale-ImGui::CalcTextSize(text).x));
 | 
			
		||||
 | 
			
		||||
void FurnaceGUI::drawInsEdit() {
 | 
			
		||||
  if (nextWindow==GUI_WINDOW_INS_EDIT) {
 | 
			
		||||
    insEditOpen=true;
 | 
			
		||||
| 
						 | 
				
			
			@ -2016,7 +2019,452 @@ void FurnaceGUI::drawInsEdit() {
 | 
			
		|||
 | 
			
		||||
                  ImGui::EndTable();
 | 
			
		||||
                }
 | 
			
		||||
              } else if (settings.fmLayout>=4 && settings.fmLayout<=6) { // alternate
 | 
			
		||||
                int columns=2;
 | 
			
		||||
                switch (settings.fmLayout) {
 | 
			
		||||
                  case 4: // 2x2
 | 
			
		||||
                    columns=2;
 | 
			
		||||
                    break;
 | 
			
		||||
                  case 5: // 1x4
 | 
			
		||||
                    columns=1;
 | 
			
		||||
                    break;
 | 
			
		||||
                  case 6: // 4x1
 | 
			
		||||
                    columns=opCount;
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
                char tempID[1024];
 | 
			
		||||
                ImVec2 oldPadding=ImGui::GetStyle().CellPadding;
 | 
			
		||||
                ImGui::PushStyleVar(ImGuiStyleVar_CellPadding,ImVec2(8.0f*dpiScale,4.0f*dpiScale));
 | 
			
		||||
                if (ImGui::BeginTable("KGE93BSIEO3NOWBDJZBA",columns,ImGuiTableFlags_SizingStretchSame|ImGuiTableFlags_BordersInner)) {
 | 
			
		||||
                  for (int i=0; i<opCount; i++) {
 | 
			
		||||
                    DivInstrumentFM::Operator& op=ins->fm.op[(opCount==4 && ins->type!=DIV_INS_OPL_DRUMS)?opOrder[i]:i];
 | 
			
		||||
                    if ((settings.fmLayout!=6 && ((i+1)&1)) || i==0 || settings.fmLayout==5) ImGui::TableNextRow();
 | 
			
		||||
                    ImGui::TableNextColumn();
 | 
			
		||||
                    ImGui::PushID(fmt::sprintf("op%d",i).c_str());
 | 
			
		||||
 | 
			
		||||
                    // push colors
 | 
			
		||||
                    if (settings.separateFMColors) {
 | 
			
		||||
                      bool mod=true;
 | 
			
		||||
                      if (ins->type==DIV_INS_OPL_DRUMS) {
 | 
			
		||||
                        mod=false;
 | 
			
		||||
                      } else if (opCount==4) {
 | 
			
		||||
                        if (ins->type==DIV_INS_OPL) {
 | 
			
		||||
                          if (opIsOutputOPL[ins->fm.alg&3][i]) mod=false;
 | 
			
		||||
                        } else {
 | 
			
		||||
                          if (opIsOutput[ins->fm.alg&7][i]) mod=false;
 | 
			
		||||
                        }
 | 
			
		||||
                      } else {
 | 
			
		||||
                        if (i==1 || (ins->type==DIV_INS_OPL && (ins->fm.alg&1))) mod=false;
 | 
			
		||||
                      }
 | 
			
		||||
                      if (mod) {
 | 
			
		||||
                        pushAccentColors(
 | 
			
		||||
                          uiColors[GUI_COLOR_FM_PRIMARY_MOD],
 | 
			
		||||
                          uiColors[GUI_COLOR_FM_SECONDARY_MOD],
 | 
			
		||||
                          uiColors[GUI_COLOR_FM_BORDER_MOD],
 | 
			
		||||
                          uiColors[GUI_COLOR_FM_BORDER_SHADOW_MOD]
 | 
			
		||||
                        );
 | 
			
		||||
                      } else {
 | 
			
		||||
                        pushAccentColors(
 | 
			
		||||
                          uiColors[GUI_COLOR_FM_PRIMARY_CAR],
 | 
			
		||||
                          uiColors[GUI_COLOR_FM_SECONDARY_CAR],
 | 
			
		||||
                          uiColors[GUI_COLOR_FM_BORDER_CAR],
 | 
			
		||||
                          uiColors[GUI_COLOR_FM_BORDER_SHADOW_CAR]
 | 
			
		||||
                        );
 | 
			
		||||
                      }
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    ImGui::Dummy(ImVec2(dpiScale,dpiScale));
 | 
			
		||||
                    if (ins->type==DIV_INS_OPL_DRUMS) {
 | 
			
		||||
                      snprintf(tempID,1024,"%s",oplDrumNames[i]);
 | 
			
		||||
                    } else if (ins->type==DIV_INS_OPL && ins->fm.opllPreset==16) {
 | 
			
		||||
                      if (i==1) {
 | 
			
		||||
                        snprintf(tempID,1024,"Envelope 2 (kick only)");
 | 
			
		||||
                      } else {
 | 
			
		||||
                        snprintf(tempID,1024,"Envelope");
 | 
			
		||||
                      }
 | 
			
		||||
                    } else {
 | 
			
		||||
                      snprintf(tempID,1024,"Operator %d",i+1);
 | 
			
		||||
                    }
 | 
			
		||||
                    CENTER_TEXT(tempID);
 | 
			
		||||
                    ImGui::TextUnformatted(tempID);
 | 
			
		||||
 | 
			
		||||
                    float sliderHeight=200.0f*dpiScale;
 | 
			
		||||
                    float waveWidth=140.0*dpiScale;
 | 
			
		||||
                    float waveHeight=sliderHeight-ImGui::GetFrameHeightWithSpacing()*(ins->type==DIV_INS_OPLL?4.5f:5.5f);
 | 
			
		||||
 | 
			
		||||
                    int maxTl=127;
 | 
			
		||||
                    if (ins->type==DIV_INS_OPLL) {
 | 
			
		||||
                      if (i==1) {
 | 
			
		||||
                        maxTl=15;
 | 
			
		||||
                      } else {
 | 
			
		||||
                        maxTl=63;
 | 
			
		||||
                      }
 | 
			
		||||
                    }
 | 
			
		||||
                    if (ins->type==DIV_INS_OPL || ins->type==DIV_INS_OPL_DRUMS) {
 | 
			
		||||
                      maxTl=63;
 | 
			
		||||
                    }
 | 
			
		||||
                    int maxArDr=(ins->type==DIV_INS_FM || ins->type==DIV_INS_OPZ)?31:15;
 | 
			
		||||
 | 
			
		||||
                    bool ssgOn=op.ssgEnv&8;
 | 
			
		||||
                    bool ksrOn=op.ksr;
 | 
			
		||||
                    bool vibOn=op.vib;
 | 
			
		||||
                    bool egtOn=op.egt;
 | 
			
		||||
                    bool susOn=op.sus; // yawn
 | 
			
		||||
                    unsigned char ssgEnv=op.ssgEnv&7;
 | 
			
		||||
 | 
			
		||||
                    ImGui::PushStyleVar(ImGuiStyleVar_CellPadding,oldPadding);
 | 
			
		||||
                    if (ImGui::BeginTable("opParams",4,ImGuiTableFlags_BordersInnerV)) {
 | 
			
		||||
                      ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthFixed);
 | 
			
		||||
                      ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthFixed,waveWidth);
 | 
			
		||||
                      ImGui::TableSetupColumn("c2",ImGuiTableColumnFlags_WidthStretch);
 | 
			
		||||
                      ImGui::TableSetupColumn("c3",ImGuiTableColumnFlags_WidthFixed);
 | 
			
		||||
 | 
			
		||||
                      ImGui::TableNextRow();
 | 
			
		||||
                      ImGui::TableNextColumn();
 | 
			
		||||
                      float textY=ImGui::GetCursorPosY();
 | 
			
		||||
                      CENTER_TEXT_20(FM_SHORT_NAME(FM_AR));
 | 
			
		||||
                      ImGui::TextUnformatted(FM_SHORT_NAME(FM_AR));
 | 
			
		||||
                      ImGui::TableNextColumn();
 | 
			
		||||
                      if (ins->type==DIV_INS_FM) {
 | 
			
		||||
                        ImGui::Text("SSG-EG");
 | 
			
		||||
                      } else {
 | 
			
		||||
                        ImGui::Text("Waveform");
 | 
			
		||||
                      }
 | 
			
		||||
                      ImGui::TableNextColumn();
 | 
			
		||||
                      ImGui::Text("Envelope");
 | 
			
		||||
                      ImGui::TableNextColumn();
 | 
			
		||||
                      CENTER_TEXT(FM_SHORT_NAME(FM_TL));
 | 
			
		||||
                      ImGui::Text("TL");
 | 
			
		||||
 | 
			
		||||
                      // A/D/S/R
 | 
			
		||||
                      ImGui::TableNextColumn();
 | 
			
		||||
 | 
			
		||||
                      op.ar&=maxArDr;
 | 
			
		||||
                      P(CWVSliderScalar("##AR",ImVec2(20.0f*dpiScale,sliderHeight),ImGuiDataType_U8,&op.ar,&maxArDr,&_ZERO));
 | 
			
		||||
 | 
			
		||||
                      ImGui::SameLine();
 | 
			
		||||
                      op.dr&=maxArDr;
 | 
			
		||||
                      float textX_DR=ImGui::GetCursorPosX();
 | 
			
		||||
                      P(CWVSliderScalar("##DR",ImVec2(20.0f*dpiScale,sliderHeight),ImGuiDataType_U8,&op.dr,&maxArDr,&_ZERO));
 | 
			
		||||
 | 
			
		||||
                      float textX_SL=0.0f;
 | 
			
		||||
                      if (settings.susPosition==0) {
 | 
			
		||||
                        ImGui::SameLine();
 | 
			
		||||
                        op.sl&=15;
 | 
			
		||||
                        textX_SL=ImGui::GetCursorPosX();
 | 
			
		||||
                        P(CWVSliderScalar("##SL",ImVec2(20.0f*dpiScale,sliderHeight),ImGuiDataType_U8,&op.sl,&_FIFTEEN,&_ZERO));
 | 
			
		||||
                      }
 | 
			
		||||
 | 
			
		||||
                      float textX_D2R=0.0f;
 | 
			
		||||
                      if (ins->type==DIV_INS_FM || ins->type==DIV_INS_OPZ) {
 | 
			
		||||
                        ImGui::SameLine();
 | 
			
		||||
                        op.d2r&=31;
 | 
			
		||||
                        textX_D2R=ImGui::GetCursorPosX();
 | 
			
		||||
                        P(CWVSliderScalar("##D2R",ImVec2(20.0f*dpiScale,sliderHeight),ImGuiDataType_U8,&op.d2r,&_THIRTY_ONE,&_ZERO));
 | 
			
		||||
                      }
 | 
			
		||||
 | 
			
		||||
                      ImGui::SameLine();
 | 
			
		||||
                      op.rr&=15;
 | 
			
		||||
                      float textX_RR=ImGui::GetCursorPosX();
 | 
			
		||||
                      P(CWVSliderScalar("##RR",ImVec2(20.0f*dpiScale,sliderHeight),ImGuiDataType_U8,&op.rr,&_FIFTEEN,&_ZERO));
 | 
			
		||||
 | 
			
		||||
                      if (settings.susPosition==1) {
 | 
			
		||||
                        ImGui::SameLine();
 | 
			
		||||
                        op.sl&=15;
 | 
			
		||||
                        textX_SL=ImGui::GetCursorPosX();
 | 
			
		||||
                        P(CWVSliderScalar("##SL",ImVec2(20.0f*dpiScale,sliderHeight),ImGuiDataType_U8,&op.sl,&_FIFTEEN,&_ZERO));
 | 
			
		||||
                      }
 | 
			
		||||
 | 
			
		||||
                      ImVec2 prevCurPos=ImGui::GetCursorPos();
 | 
			
		||||
                      
 | 
			
		||||
                      // labels
 | 
			
		||||
                      ImGui::SetCursorPos(ImVec2(textX_DR,textY));
 | 
			
		||||
                      CENTER_TEXT_20(FM_SHORT_NAME(FM_DR));
 | 
			
		||||
                      ImGui::TextUnformatted(FM_SHORT_NAME(FM_DR));
 | 
			
		||||
 | 
			
		||||
                      ImGui::SetCursorPos(ImVec2(textX_SL,textY));
 | 
			
		||||
                      CENTER_TEXT_20(FM_SHORT_NAME(FM_SL));
 | 
			
		||||
                      ImGui::TextUnformatted(FM_SHORT_NAME(FM_SL));
 | 
			
		||||
 | 
			
		||||
                      ImGui::SetCursorPos(ImVec2(textX_RR,textY));
 | 
			
		||||
                      CENTER_TEXT_20(FM_SHORT_NAME(FM_RR));
 | 
			
		||||
                      ImGui::TextUnformatted(FM_SHORT_NAME(FM_RR));
 | 
			
		||||
 | 
			
		||||
                      if (ins->type==DIV_INS_FM || ins->type==DIV_INS_OPZ) {
 | 
			
		||||
                        ImGui::SetCursorPos(ImVec2(textX_D2R,textY));
 | 
			
		||||
                        CENTER_TEXT_20(FM_SHORT_NAME(FM_D2R));
 | 
			
		||||
                        ImGui::TextUnformatted(FM_SHORT_NAME(FM_D2R));
 | 
			
		||||
                      }
 | 
			
		||||
 | 
			
		||||
                      ImGui::SetCursorPos(prevCurPos);
 | 
			
		||||
                      
 | 
			
		||||
                      ImGui::TableNextColumn();
 | 
			
		||||
                      switch (ins->type) {
 | 
			
		||||
                        case DIV_INS_FM: {
 | 
			
		||||
                          // SSG
 | 
			
		||||
                          ImGui::BeginDisabled(!ssgOn);
 | 
			
		||||
                          drawSSGEnv(op.ssgEnv&7,ImVec2(waveWidth,waveHeight));
 | 
			
		||||
                          ImGui::EndDisabled();
 | 
			
		||||
                          if (ImGui::Checkbox("##SSGOn",&ssgOn)) { PARAMETER
 | 
			
		||||
                            op.ssgEnv=(op.ssgEnv&7)|(ssgOn<<3);
 | 
			
		||||
                          }
 | 
			
		||||
                          if (ImGui::IsItemHovered()) {
 | 
			
		||||
                            ImGui::SetTooltip("Only for OPN family chips");
 | 
			
		||||
                          }
 | 
			
		||||
 | 
			
		||||
                          ImGui::SameLine();
 | 
			
		||||
                          ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
 | 
			
		||||
                          if (CWSliderScalar("##SSG",ImGuiDataType_U8,&ssgEnv,&_ZERO,&_SEVEN,ssgEnvTypes[ssgEnv])) { PARAMETER
 | 
			
		||||
                            op.ssgEnv=(op.ssgEnv&8)|(ssgEnv&7);
 | 
			
		||||
                          }
 | 
			
		||||
                          
 | 
			
		||||
                          // params
 | 
			
		||||
                          ImGui::Separator();
 | 
			
		||||
                          ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
 | 
			
		||||
                          snprintf(tempID,1024,"%s: %%d",FM_NAME(FM_MULT));
 | 
			
		||||
                          P(CWSliderScalar("##MULT",ImGuiDataType_U8,&op.mult,&_ZERO,&_FIFTEEN,tempID)); rightClickable
 | 
			
		||||
 | 
			
		||||
                          int detune=(op.dt&7)-3;
 | 
			
		||||
                          ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
 | 
			
		||||
                          snprintf(tempID,1024,"%s: %%d",FM_NAME(FM_DT));
 | 
			
		||||
                          if (CWSliderInt("##DT",&detune,-3,4,tempID)) { PARAMETER
 | 
			
		||||
                            op.dt=detune+3;
 | 
			
		||||
                          } rightClickable
 | 
			
		||||
 | 
			
		||||
                          ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
 | 
			
		||||
                          snprintf(tempID,1024,"%s: %%d",FM_NAME(FM_DT2));
 | 
			
		||||
                          P(CWSliderScalar("##DT2",ImGuiDataType_U8,&op.dt2,&_ZERO,&_THREE,tempID)); rightClickable
 | 
			
		||||
                          if (ImGui::IsItemHovered()) {
 | 
			
		||||
                            ImGui::SetTooltip("Only on YM2151 (OPM)");
 | 
			
		||||
                          }
 | 
			
		||||
 | 
			
		||||
                          ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
 | 
			
		||||
                          snprintf(tempID,1024,"%s: %%d",FM_NAME(FM_RS));
 | 
			
		||||
                          P(CWSliderScalar("##RS",ImGuiDataType_U8,&op.rs,&_ZERO,&_THREE,tempID)); rightClickable
 | 
			
		||||
 | 
			
		||||
                          break;
 | 
			
		||||
                        }
 | 
			
		||||
                        case DIV_INS_OPLL:
 | 
			
		||||
                          // waveform
 | 
			
		||||
                          drawWaveform(i==0?(ins->fm.ams&1):(ins->fm.fms&1),ins->type==DIV_INS_OPZ,ImVec2(waveWidth,waveHeight));
 | 
			
		||||
 | 
			
		||||
                          // params
 | 
			
		||||
                          ImGui::Separator();
 | 
			
		||||
                          if (ImGui::BeginTable("FMParamsInner",2)) {
 | 
			
		||||
                            ImGui::TableNextRow();
 | 
			
		||||
                            ImGui::TableNextColumn();
 | 
			
		||||
                            bool amOn=op.am;
 | 
			
		||||
                            if (ImGui::Checkbox(FM_NAME(FM_AM),&amOn)) { PARAMETER
 | 
			
		||||
                              op.am=amOn;
 | 
			
		||||
                            }
 | 
			
		||||
                            ImGui::TableNextColumn();
 | 
			
		||||
                            if (ImGui::Checkbox(FM_NAME(FM_KSR),&ksrOn)) { PARAMETER
 | 
			
		||||
                              op.ksr=ksrOn;
 | 
			
		||||
                            }
 | 
			
		||||
 | 
			
		||||
                            ImGui::TableNextRow();
 | 
			
		||||
                            ImGui::TableNextColumn();
 | 
			
		||||
                            if (ImGui::Checkbox(FM_NAME(FM_VIB),&vibOn)) { PARAMETER
 | 
			
		||||
                              op.vib=vibOn;
 | 
			
		||||
                            }
 | 
			
		||||
                            ImGui::TableNextColumn();
 | 
			
		||||
                            if (ImGui::Checkbox(FM_NAME(FM_EGS),&ssgOn)) { PARAMETER
 | 
			
		||||
                              op.ssgEnv=(op.ssgEnv&7)|(ssgOn<<3);
 | 
			
		||||
                            }
 | 
			
		||||
                            
 | 
			
		||||
                            ImGui::EndTable();
 | 
			
		||||
                          }
 | 
			
		||||
 | 
			
		||||
                          ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
 | 
			
		||||
                          snprintf(tempID,1024,"%s: %%d",FM_NAME(FM_MULT));
 | 
			
		||||
                          P(CWSliderScalar("##MULT",ImGuiDataType_U8,&op.mult,&_ZERO,&_FIFTEEN,tempID)); rightClickable
 | 
			
		||||
 | 
			
		||||
                          ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
 | 
			
		||||
                          snprintf(tempID,1024,"%s: %%d",FM_NAME(FM_KSL));
 | 
			
		||||
                          P(CWSliderScalar("##KSL",ImGuiDataType_U8,&op.ksl,&_ZERO,&_THREE,tempID)); rightClickable
 | 
			
		||||
 | 
			
		||||
                          break;
 | 
			
		||||
                        case DIV_INS_OPL:
 | 
			
		||||
                          // waveform
 | 
			
		||||
                          drawWaveform(op.ws&7,ins->type==DIV_INS_OPZ,ImVec2(waveWidth,waveHeight));
 | 
			
		||||
                          ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
 | 
			
		||||
                          P(CWSliderScalar("##WS",ImGuiDataType_U8,&op.ws,&_ZERO,&_SEVEN,(ins->type==DIV_INS_OPZ)?opzWaveforms[op.ws&7]:(settings.oplStandardWaveNames?oplWaveformsStandard[op.ws&7]:oplWaveforms[op.ws&7]))); rightClickable
 | 
			
		||||
                          if ((ins->type==DIV_INS_OPL || ins->type==DIV_INS_OPL_DRUMS) && ImGui::IsItemHovered()) {
 | 
			
		||||
                            ImGui::SetTooltip("OPL2/3 only (last 4 waveforms are OPL3 only)");
 | 
			
		||||
                          }
 | 
			
		||||
 | 
			
		||||
                          // params
 | 
			
		||||
                          ImGui::Separator();
 | 
			
		||||
                          if (ImGui::BeginTable("FMParamsInner",2)) {
 | 
			
		||||
                            ImGui::TableNextRow();
 | 
			
		||||
                            ImGui::TableNextColumn();
 | 
			
		||||
                            bool amOn=op.am;
 | 
			
		||||
                            if (ImGui::Checkbox(FM_NAME(FM_AM),&amOn)) { PARAMETER
 | 
			
		||||
                              op.am=amOn;
 | 
			
		||||
                            }
 | 
			
		||||
                            ImGui::TableNextColumn();
 | 
			
		||||
                            if (ImGui::Checkbox(FM_NAME(FM_KSR),&ksrOn)) { PARAMETER
 | 
			
		||||
                              op.ksr=ksrOn;
 | 
			
		||||
                            }
 | 
			
		||||
 | 
			
		||||
                            ImGui::TableNextRow();
 | 
			
		||||
                            ImGui::TableNextColumn();
 | 
			
		||||
                            if (ImGui::Checkbox(FM_NAME(FM_VIB),&vibOn)) { PARAMETER
 | 
			
		||||
                              op.vib=vibOn;
 | 
			
		||||
                            }
 | 
			
		||||
                            ImGui::TableNextColumn();
 | 
			
		||||
                            if (ImGui::Checkbox(FM_NAME(FM_SUS),&susOn)) { PARAMETER
 | 
			
		||||
                              op.sus=susOn;
 | 
			
		||||
                            }
 | 
			
		||||
                            
 | 
			
		||||
                            ImGui::EndTable();
 | 
			
		||||
                          }
 | 
			
		||||
 | 
			
		||||
                          ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
 | 
			
		||||
                          snprintf(tempID,1024,"%s: %%d",FM_NAME(FM_MULT));
 | 
			
		||||
                          P(CWSliderScalar("##MULT",ImGuiDataType_U8,&op.mult,&_ZERO,&_FIFTEEN,tempID)); rightClickable
 | 
			
		||||
 | 
			
		||||
                          ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
 | 
			
		||||
                          snprintf(tempID,1024,"%s: %%d",FM_NAME(FM_KSL));
 | 
			
		||||
                          P(CWSliderScalar("##KSL",ImGuiDataType_U8,&op.ksl,&_ZERO,&_THREE,tempID)); rightClickable
 | 
			
		||||
 | 
			
		||||
                          break;
 | 
			
		||||
                        case DIV_INS_OPZ: {
 | 
			
		||||
                          // waveform
 | 
			
		||||
                          drawWaveform(op.ws&7,ins->type==DIV_INS_OPZ,ImVec2(waveWidth,waveHeight));
 | 
			
		||||
                          ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
 | 
			
		||||
                          P(CWSliderScalar("##WS",ImGuiDataType_U8,&op.ws,&_ZERO,&_SEVEN,(ins->type==DIV_INS_OPZ)?opzWaveforms[op.ws&7]:(settings.oplStandardWaveNames?oplWaveformsStandard[op.ws&7]:oplWaveforms[op.ws&7]))); rightClickable
 | 
			
		||||
                          if ((ins->type==DIV_INS_OPL || ins->type==DIV_INS_OPL_DRUMS) && ImGui::IsItemHovered()) {
 | 
			
		||||
                            ImGui::SetTooltip("OPL2/3 only (last 4 waveforms are OPL3 only)");
 | 
			
		||||
                          }
 | 
			
		||||
 | 
			
		||||
                          // params
 | 
			
		||||
                          ImGui::Separator();
 | 
			
		||||
                          if (egtOn) {
 | 
			
		||||
                            int block=op.dt;
 | 
			
		||||
                            int freqNum=(op.mult<<4)|(op.dvb&15);
 | 
			
		||||
                            ImGui::Text("Block");
 | 
			
		||||
                            ImGui::SameLine();
 | 
			
		||||
                            ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
 | 
			
		||||
                            ImVec2 cursorAlign=ImGui::GetCursorPos();
 | 
			
		||||
                            if (ImGui::InputInt("##Block",&block,1,1)) {
 | 
			
		||||
                              if (block<0) block=0;
 | 
			
		||||
                              if (block>7) block=7;
 | 
			
		||||
                              op.dt=block;
 | 
			
		||||
                            }
 | 
			
		||||
                            
 | 
			
		||||
                            ImGui::Text("Freq");
 | 
			
		||||
                            ImGui::SameLine();
 | 
			
		||||
                            ImGui::SetCursorPos(ImVec2(cursorAlign.x,ImGui::GetCursorPosY()));
 | 
			
		||||
                            ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
 | 
			
		||||
                            if (ImGui::InputInt("##FreqNum",&freqNum,1,16)) {
 | 
			
		||||
                              if (freqNum<0) freqNum=0;
 | 
			
		||||
                              if (freqNum>255) freqNum=255;
 | 
			
		||||
                              op.mult=freqNum>>4;
 | 
			
		||||
                              op.dvb=freqNum&15;
 | 
			
		||||
                            }
 | 
			
		||||
                          } else {
 | 
			
		||||
                            ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
 | 
			
		||||
                            snprintf(tempID,1024,"%s: %%d",FM_NAME(FM_MULT));
 | 
			
		||||
                            P(CWSliderScalar("##MULT",ImGuiDataType_U8,&op.mult,&_ZERO,&_FIFTEEN,tempID)); rightClickable
 | 
			
		||||
 | 
			
		||||
                            int detune=(op.dt&7)-3;
 | 
			
		||||
                            ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
 | 
			
		||||
                            snprintf(tempID,1024,"%s: %%d",FM_NAME(FM_DT));
 | 
			
		||||
                            if (CWSliderInt("##DT",&detune,-3,4,tempID)) { PARAMETER
 | 
			
		||||
                              op.dt=detune+3;
 | 
			
		||||
                            } rightClickable
 | 
			
		||||
                          }
 | 
			
		||||
 | 
			
		||||
                          ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
 | 
			
		||||
                          snprintf(tempID,1024,"%s: %%d",FM_NAME(FM_DT2));
 | 
			
		||||
                          P(CWSliderScalar("##DT2",ImGuiDataType_U8,&op.dt2,&_ZERO,&_THREE,tempID)); rightClickable
 | 
			
		||||
                          if (ImGui::IsItemHovered()) {
 | 
			
		||||
                            ImGui::SetTooltip("Only on YM2151 (OPM)");
 | 
			
		||||
                          }
 | 
			
		||||
 | 
			
		||||
                          ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
 | 
			
		||||
                          snprintf(tempID,1024,"%s: %%d",FM_NAME(FM_RS));
 | 
			
		||||
                          P(CWSliderScalar("##RS",ImGuiDataType_U8,&op.rs,&_ZERO,&_THREE,tempID)); rightClickable
 | 
			
		||||
                          break;
 | 
			
		||||
                        }
 | 
			
		||||
                        default:
 | 
			
		||||
                          break;
 | 
			
		||||
                      }
 | 
			
		||||
 | 
			
		||||
                      ImGui::TableNextColumn();
 | 
			
		||||
                      float envHeight=sliderHeight;//-ImGui::GetStyle().ItemSpacing.y*2.0f;
 | 
			
		||||
                      if (ins->type==DIV_INS_OPZ) {
 | 
			
		||||
                        envHeight-=ImGui::GetFrameHeightWithSpacing()*2.0f;
 | 
			
		||||
                      }
 | 
			
		||||
                      drawFMEnv(op.tl&maxTl,op.ar&maxArDr,op.dr&maxArDr,(ins->type==DIV_INS_OPL || ins->type==DIV_INS_OPL_DRUMS || ins->type==DIV_INS_OPLL)?((op.rr&15)*2):op.d2r&31,op.rr&15,op.sl&15,op.sus,op.ssgEnv&8,ins->fm.alg,maxTl,maxArDr,ImVec2(ImGui::GetContentRegionAvail().x,envHeight),ins->type);
 | 
			
		||||
 | 
			
		||||
                      if (ins->type==DIV_INS_OPZ) {
 | 
			
		||||
                        ImGui::Separator();
 | 
			
		||||
                        if (ImGui::BeginTable("FMParamsInnerOPZ",2)) {
 | 
			
		||||
                          ImGui::TableNextRow();
 | 
			
		||||
                          ImGui::TableNextColumn();
 | 
			
		||||
                          if (!egtOn) {
 | 
			
		||||
                            ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
 | 
			
		||||
                            snprintf(tempID,1024,"%s: %%d",FM_NAME(FM_FINE));
 | 
			
		||||
                            P(CWSliderScalar("##FINE",ImGuiDataType_U8,&op.dvb,&_ZERO,&_FIFTEEN,tempID)); rightClickable
 | 
			
		||||
                          }
 | 
			
		||||
 | 
			
		||||
                          ImGui::TableNextColumn();
 | 
			
		||||
                          bool amOn=op.am;
 | 
			
		||||
                          if (ImGui::Checkbox(FM_NAME(FM_AM),&amOn)) { PARAMETER
 | 
			
		||||
                            op.am=amOn;
 | 
			
		||||
                          }
 | 
			
		||||
                          ImGui::SameLine();
 | 
			
		||||
                          if (ImGui::Checkbox("Fixed",&egtOn)) { PARAMETER
 | 
			
		||||
                            op.egt=egtOn;
 | 
			
		||||
                          }
 | 
			
		||||
 | 
			
		||||
                          ImGui::TableNextRow();
 | 
			
		||||
                          ImGui::TableNextColumn();
 | 
			
		||||
                          ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
 | 
			
		||||
                          snprintf(tempID,1024,"%s: %%d",FM_NAME(FM_EGSHIFT));
 | 
			
		||||
                          P(CWSliderScalar("##EGShift",ImGuiDataType_U8,&op.ksl,&_ZERO,&_THREE,tempID)); rightClickable
 | 
			
		||||
 | 
			
		||||
                          ImGui::TableNextColumn();
 | 
			
		||||
                          ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
 | 
			
		||||
                          snprintf(tempID,1024,"%s: %%d",FM_NAME(FM_REV));
 | 
			
		||||
                          P(CWSliderScalar("##REV",ImGuiDataType_U8,&op.dam,&_ZERO,&_SEVEN,tempID)); rightClickable
 | 
			
		||||
 | 
			
		||||
                          ImGui::TableNextColumn();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
                          ImGui::EndTable();
 | 
			
		||||
                        }
 | 
			
		||||
                      }
 | 
			
		||||
 | 
			
		||||
                      ImGui::TableNextColumn();
 | 
			
		||||
                      op.tl&=maxTl;
 | 
			
		||||
                      P(CWVSliderScalar("##TL",ImVec2(ImGui::GetFrameHeight(),sliderHeight-(ins->type==DIV_INS_FM?(ImGui::GetFrameHeightWithSpacing()+ImGui::CalcTextSize(FM_SHORT_NAME(FM_AM)).y+ImGui::GetStyle().ItemSpacing.y):0.0f)),ImGuiDataType_U8,&op.tl,&maxTl,&_ZERO));
 | 
			
		||||
 | 
			
		||||
                      if (ins->type==DIV_INS_FM) {
 | 
			
		||||
                        CENTER_TEXT(FM_SHORT_NAME(FM_AM));
 | 
			
		||||
                        ImGui::TextUnformatted(FM_SHORT_NAME(FM_AM));
 | 
			
		||||
                        bool amOn=op.am;
 | 
			
		||||
                        if (ImGui::Checkbox("##AM",&amOn)) { PARAMETER
 | 
			
		||||
                          op.am=amOn;
 | 
			
		||||
                        }
 | 
			
		||||
                      }
 | 
			
		||||
 | 
			
		||||
                      ImGui::EndTable();
 | 
			
		||||
                    }
 | 
			
		||||
                    ImGui::PopStyleVar();
 | 
			
		||||
 | 
			
		||||
                    if (settings.separateFMColors) {
 | 
			
		||||
                      popAccentColors();
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    ImGui::PopID();
 | 
			
		||||
                  }
 | 
			
		||||
                  ImGui::EndTable();
 | 
			
		||||
                }
 | 
			
		||||
                ImGui::PopStyleVar();
 | 
			
		||||
              } else { // classic
 | 
			
		||||
                int columns=2;
 | 
			
		||||
                switch (settings.fmLayout) {
 | 
			
		||||
                  case 1: // 2x2
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -25,6 +25,7 @@ const int _THREE=3;
 | 
			
		|||
const int _SEVEN=7;
 | 
			
		||||
const int _TEN=10;
 | 
			
		||||
const int _FIFTEEN=15;
 | 
			
		||||
const int _SIXTEEN=16;
 | 
			
		||||
const int _THIRTY_ONE=31;
 | 
			
		||||
const int _SIXTY_FOUR=64;
 | 
			
		||||
const int _ONE_HUNDRED=100;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -27,6 +27,7 @@ extern const int _THREE;
 | 
			
		|||
extern const int _SEVEN;
 | 
			
		||||
extern const int _TEN;
 | 
			
		||||
extern const int _FIFTEEN;
 | 
			
		||||
extern const int _SIXTEEN;
 | 
			
		||||
extern const int _THIRTY_ONE;
 | 
			
		||||
extern const int _SIXTY_FOUR;
 | 
			
		||||
extern const int _ONE_HUNDRED;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -17,7 +17,6 @@
 | 
			
		|||
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <imgui.h>
 | 
			
		||||
#define _USE_MATH_DEFINES
 | 
			
		||||
#include "gui.h"
 | 
			
		||||
#include "../ta-log.h"
 | 
			
		||||
| 
						 | 
				
			
			@ -31,6 +30,30 @@ inline float randRange(float min, float max) {
 | 
			
		|||
  return min+((float)rand()/(float)RAND_MAX)*(max-min);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void _pushPartBlend(const ImDrawList* drawList, const ImDrawCmd* cmd) {
 | 
			
		||||
  if (cmd!=NULL) {
 | 
			
		||||
    if (cmd->UserCallbackData!=NULL) {
 | 
			
		||||
      ((FurnaceGUI*)cmd->UserCallbackData)->pushPartBlend();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void _popPartBlend(const ImDrawList* drawList, const ImDrawCmd* cmd) {
 | 
			
		||||
  if (cmd!=NULL) {
 | 
			
		||||
    if (cmd->UserCallbackData!=NULL) {
 | 
			
		||||
      ((FurnaceGUI*)cmd->UserCallbackData)->popPartBlend();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void FurnaceGUI::pushPartBlend() {
 | 
			
		||||
  SDL_SetRenderDrawBlendMode(sdlRend,SDL_BLENDMODE_ADD);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void FurnaceGUI::popPartBlend() {
 | 
			
		||||
  SDL_SetRenderDrawBlendMode(sdlRend,SDL_BLENDMODE_BLEND);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// draw a pattern row
 | 
			
		||||
inline void FurnaceGUI::patternRow(int i, bool isPlaying, float lineHeight, int chans, int ord, const DivPattern** patCache, bool inhibitSel) {
 | 
			
		||||
  static char id[32];
 | 
			
		||||
| 
						 | 
				
			
			@ -692,11 +715,14 @@ void FurnaceGUI::drawPattern() {
 | 
			
		|||
        ImU32* color=noteGrad;
 | 
			
		||||
 | 
			
		||||
        switch (i.cmd) {
 | 
			
		||||
          case DIV_CMD_NOTE_ON:
 | 
			
		||||
          case DIV_CMD_NOTE_ON: {
 | 
			
		||||
            float strength=CLAMP(i.value,0,119);
 | 
			
		||||
            partIcon=ICON_FA_ASTERISK;
 | 
			
		||||
            life=96.0f;
 | 
			
		||||
            life=80.0f+((i.value==DIV_NOTE_NULL)?0.0f:(strength*0.3f));
 | 
			
		||||
            lifeSpeed=3.0f;
 | 
			
		||||
            num=6+(strength/16);
 | 
			
		||||
            break;
 | 
			
		||||
          }
 | 
			
		||||
          case DIV_CMD_LEGATO:
 | 
			
		||||
            partIcon=ICON_FA_COG;
 | 
			
		||||
            color=insGrad;
 | 
			
		||||
| 
						 | 
				
			
			@ -794,7 +820,7 @@ void FurnaceGUI::drawPattern() {
 | 
			
		|||
 | 
			
		||||
      float frameTime=ImGui::GetIO().DeltaTime*60.0f;
 | 
			
		||||
 | 
			
		||||
      // note slides
 | 
			
		||||
      // note slides and vibrato
 | 
			
		||||
      ImVec2 arrowPoints[7];
 | 
			
		||||
      if (e->isPlaying()) for (int i=0; i<chans; i++) {
 | 
			
		||||
        if (!e->curSubSong->chanShow[i]) continue;
 | 
			
		||||
| 
						 | 
				
			
			@ -849,11 +875,30 @@ void FurnaceGUI::drawPattern() {
 | 
			
		|||
            }
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
        if (ch->vibratoDepth>0) {
 | 
			
		||||
          ImVec4 col=uiColors[GUI_COLOR_PATTERN_EFFECT_PITCH];
 | 
			
		||||
          col.w*=0.2;
 | 
			
		||||
          float width=patChanX[i+1]-patChanX[i];
 | 
			
		||||
 | 
			
		||||
          particles.push_back(Particle(
 | 
			
		||||
            pitchGrad,
 | 
			
		||||
            ICON_FA_GLASS,
 | 
			
		||||
            off.x+patChanX[i]+(width*0.5+0.5*sin(M_PI*(float)ch->vibratoPosGiant/64.0f)*width)-scrollX,
 | 
			
		||||
            off.y+(ImGui::GetWindowHeight()*0.5f)+randRange(0,patFont->FontSize),
 | 
			
		||||
            randRange(-4.0f,4.0f),
 | 
			
		||||
            2.0f*(3.0f+(rand()%5)+ch->vibratoRate),
 | 
			
		||||
            0.4f,
 | 
			
		||||
            1.0f,
 | 
			
		||||
            128.0f,
 | 
			
		||||
            4.0f
 | 
			
		||||
          ));
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      // particle simulation
 | 
			
		||||
      ImDrawList* fdl=ImGui::GetForegroundDrawList();
 | 
			
		||||
      if (!particles.empty()) WAKE_UP;
 | 
			
		||||
      fdl->AddCallback(_pushPartBlend,this);
 | 
			
		||||
      for (size_t i=0; i<particles.size(); i++) {
 | 
			
		||||
        Particle& part=particles[i];
 | 
			
		||||
        if (part.update(frameTime)) {
 | 
			
		||||
| 
						 | 
				
			
			@ -870,6 +915,7 @@ void FurnaceGUI::drawPattern() {
 | 
			
		|||
          i--;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      fdl->AddCallback(_popPartBlend,this);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ImGui::PopStyleColor(3);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1117,6 +1117,15 @@ void FurnaceGUI::drawSettings() {
 | 
			
		|||
          if (ImGui::RadioButton("Compact (4x1)##fml3",settings.fmLayout==3)) {
 | 
			
		||||
            settings.fmLayout=3;
 | 
			
		||||
          }
 | 
			
		||||
          if (ImGui::RadioButton("Alternate (2x2)##fml4",settings.fmLayout==4)) {
 | 
			
		||||
            settings.fmLayout=4;
 | 
			
		||||
          }
 | 
			
		||||
          if (ImGui::RadioButton("Alternate (1x4)##fml5",settings.fmLayout==5)) {
 | 
			
		||||
            settings.fmLayout=5;
 | 
			
		||||
          }
 | 
			
		||||
          if (ImGui::RadioButton("Alternate (4x1)##fml5",settings.fmLayout==6)) {
 | 
			
		||||
            settings.fmLayout=6;
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          ImGui::Text("Position of Sustain in FM editor:");
 | 
			
		||||
          if (ImGui::RadioButton("Between Decay and Sustain Rate##susp0",settings.susPosition==0)) {
 | 
			
		||||
| 
						 | 
				
			
			@ -2077,7 +2086,7 @@ void FurnaceGUI::syncSettings() {
 | 
			
		|||
  clampSetting(settings.roundedMenus,0,1);
 | 
			
		||||
  clampSetting(settings.loadJapanese,0,1);
 | 
			
		||||
  clampSetting(settings.loadChinese,0,1);
 | 
			
		||||
  clampSetting(settings.fmLayout,0,3);
 | 
			
		||||
  clampSetting(settings.fmLayout,0,6);
 | 
			
		||||
  clampSetting(settings.susPosition,0,1);
 | 
			
		||||
  clampSetting(settings.effectCursorDir,0,2);
 | 
			
		||||
  clampSetting(settings.cursorPastePos,0,1);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										13
									
								
								src/main.cpp
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								src/main.cpp
									
									
									
									
									
								
							| 
						 | 
				
			
			@ -30,6 +30,7 @@
 | 
			
		|||
#ifdef _WIN32
 | 
			
		||||
#define WIN32_LEAN_AND_MEAN
 | 
			
		||||
#include <windows.h>
 | 
			
		||||
#include <combaseapi.h>
 | 
			
		||||
#include <shellapi.h>
 | 
			
		||||
#else
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
| 
						 | 
				
			
			@ -244,6 +245,12 @@ void initParams() {
 | 
			
		|||
// TODO: add crash log
 | 
			
		||||
int main(int argc, char** argv) {
 | 
			
		||||
  initLog();
 | 
			
		||||
#ifdef _WIN32
 | 
			
		||||
  HRESULT coResult=CoInitializeEx(NULL,COINIT_MULTITHREADED);
 | 
			
		||||
  if (coResult!=S_OK) {
 | 
			
		||||
    logE("CoInitializeEx failed!");
 | 
			
		||||
  }
 | 
			
		||||
#endif
 | 
			
		||||
#if !(defined(__APPLE__) || defined(_WIN32) || defined(ANDROID))
 | 
			
		||||
  // workaround for Wayland HiDPI issue
 | 
			
		||||
  if (getenv("SDL_VIDEODRIVER")==NULL) {
 | 
			
		||||
| 
						 | 
				
			
			@ -447,6 +454,12 @@ int main(int argc, char** argv) {
 | 
			
		|||
 | 
			
		||||
  logI("stopping engine.");
 | 
			
		||||
  e.quit();
 | 
			
		||||
 | 
			
		||||
#ifdef _WIN32
 | 
			
		||||
  if (coResult==S_OK || coResult==S_FALSE) {
 | 
			
		||||
    CoUninitialize();
 | 
			
		||||
  }
 | 
			
		||||
#endif
 | 
			
		||||
  return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue