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

# Conflicts:
#	src/engine/fileOps.cpp
#	src/engine/platform/lynx.cpp
#	src/engine/platform/rf5c68.cpp
#	src/engine/platform/su.cpp
#	src/engine/platform/x1_010.h
#	src/engine/platform/ym2610.cpp
#	src/engine/platform/ym2610.h
#	src/engine/platform/ym2610b.cpp
#	src/engine/platform/ym2610b.h
#	src/engine/sysDef.cpp
#	src/gui/insEdit.cpp

Add effect command for ADPCM-A global volume, X1-010 Sample bank slot
This commit is contained in:
cam900 2022-08-27 17:25:48 +09:00
commit 54dbd0690c
148 changed files with 4114 additions and 3610 deletions

View file

@ -49,6 +49,7 @@ const char* aboutLine[]={
"tildearrow",
"BlastBrothers",
"Mahbod Karamoozian",
"nicco1690",
"Raijin",
"",
"-- documentation --",
@ -67,6 +68,7 @@ const char* aboutLine[]={
"AURORA*FIELDS",
"BlueElectric05",
"breakthetargets",
"brickblock369",
"CaptainMalware",
"DeMOSic",
"DevEd",
@ -86,6 +88,7 @@ const char* aboutLine[]={
"nicco1690",
"NikonTeen",
"psdominator",
"Raijin",
"SuperJet Spade",
"TheDuccinator",
"theloredev",

View file

@ -139,6 +139,10 @@ void FurnaceGUI::drawCompatFlags() {
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("if enabled, no checks for the presence of a volume macro will be made.\nthis will cause the last macro value to linger unless a value in the volume column is present.");
}
ImGui::Checkbox("Treat SN76489 periods under 8 as 1",&e->song.snNoLowPeriods);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("when enabled, any SN period under 8 will be written as 1 instead.\nthis replicates DefleMask behavior, but reduces available period range.");
}
ImGui::Text("Pitch linearity:");
if (ImGui::RadioButton("None",e->song.linearPitch==0)) {
@ -189,6 +193,26 @@ void FurnaceGUI::drawCompatFlags() {
ImGui::SetTooltip("select to not reset channels on loop.");
}
ImGui::Text("Cut/delay effect policy:");
if (ImGui::RadioButton("Strict",e->song.delayBehavior==0)) {
e->song.delayBehavior=0;
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("only when time is less than speed (like DefleMask/ProTracker)");
}
if (ImGui::RadioButton("Strict (old)",e->song.delayBehavior==1)) {
e->song.delayBehavior=1;
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("only when time is less than or equal to speed (original buggy behavior)");
}
if (ImGui::RadioButton("Lax",e->song.delayBehavior==2)) {
e->song.delayBehavior=2;
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("no checks (like FamiTracker)");
}
ImGui::Separator();
ImGui::TextWrapped("the following flags are for compatibility with older Furnace versions.");

View file

@ -452,6 +452,12 @@ void FurnaceGUI::drawWaveList() {
if (ImGui::Button(ICON_FA_FOLDER_OPEN "##WaveLoad")) {
doAction(GUI_ACTION_WAVE_LIST_OPEN);
}
if (ImGui::BeginPopupContextItem("WaveOpenOpt")) {
if (ImGui::MenuItem("replace...")) {
doAction((curWave>=0 && curWave<(int)e->song.wave.size())?GUI_ACTION_WAVE_LIST_OPEN_REPLACE:GUI_ACTION_WAVE_LIST_OPEN);
}
ImGui::EndPopup();
}
ImGui::SameLine();
if (ImGui::Button(ICON_FA_FLOPPY_O "##WaveSave")) {
doAction(GUI_ACTION_WAVE_LIST_SAVE);
@ -502,6 +508,19 @@ void FurnaceGUI::drawSampleList() {
if (ImGui::Button(ICON_FA_FOLDER_OPEN "##SampleLoad")) {
doAction(GUI_ACTION_SAMPLE_LIST_OPEN);
}
if (ImGui::BeginPopupContextItem("SampleOpenOpt")) {
if (ImGui::MenuItem("replace...")) {
doAction((curSample>=0 && curSample<(int)e->song.sample.size())?GUI_ACTION_SAMPLE_LIST_OPEN_REPLACE:GUI_ACTION_SAMPLE_LIST_OPEN);
}
ImGui::Separator();
if (ImGui::MenuItem("import raw...")) {
doAction(GUI_ACTION_SAMPLE_LIST_OPEN_RAW);
}
if (ImGui::MenuItem("import raw (replace)...")) {
doAction((curSample>=0 && curSample<(int)e->song.sample.size())?GUI_ACTION_SAMPLE_LIST_OPEN_REPLACE_RAW:GUI_ACTION_SAMPLE_LIST_OPEN_RAW);
}
ImGui::EndPopup();
}
ImGui::SameLine();
if (ImGui::Button(ICON_FA_FLOPPY_O "##SampleSave")) {
doAction(GUI_ACTION_SAMPLE_LIST_SAVE);

View file

@ -238,6 +238,12 @@ void FurnaceGUI::doAction(int what) {
case GUI_ACTION_WINDOW_CHANNELS:
nextWindow=GUI_WINDOW_CHANNELS;
break;
case GUI_ACTION_WINDOW_PAT_MANAGER:
nextWindow=GUI_WINDOW_PAT_MANAGER;
break;
case GUI_ACTION_WINDOW_SYS_MANAGER:
nextWindow=GUI_WINDOW_SYS_MANAGER;
break;
case GUI_ACTION_WINDOW_REGISTER_VIEW:
nextWindow=GUI_WINDOW_REGISTER_VIEW;
break;
@ -322,6 +328,12 @@ void FurnaceGUI::doAction(int what) {
case GUI_WINDOW_CHANNELS:
channelsOpen=false;
break;
case GUI_WINDOW_PAT_MANAGER:
patManagerOpen=false;
break;
case GUI_WINDOW_SYS_MANAGER:
sysManagerOpen=false;
break;
case GUI_WINDOW_REGISTER_VIEW:
regViewOpen=false;
break;
@ -648,6 +660,9 @@ void FurnaceGUI::doAction(int what) {
case GUI_ACTION_WAVE_LIST_OPEN:
openFileDialog(GUI_FILE_WAVE_OPEN);
break;
case GUI_ACTION_WAVE_LIST_OPEN_REPLACE:
openFileDialog(GUI_FILE_WAVE_OPEN_REPLACE);
break;
case GUI_ACTION_WAVE_LIST_SAVE:
if (curWave>=0 && curWave<(int)e->song.wave.size()) openFileDialog(GUI_FILE_WAVE_SAVE);
break;
@ -730,6 +745,15 @@ void FurnaceGUI::doAction(int what) {
case GUI_ACTION_SAMPLE_LIST_OPEN:
openFileDialog(GUI_FILE_SAMPLE_OPEN);
break;
case GUI_ACTION_SAMPLE_LIST_OPEN_REPLACE:
openFileDialog(GUI_FILE_SAMPLE_OPEN_REPLACE);
break;
case GUI_ACTION_SAMPLE_LIST_OPEN_RAW:
openFileDialog(GUI_FILE_SAMPLE_OPEN_RAW);
break;
case GUI_ACTION_SAMPLE_LIST_OPEN_REPLACE_RAW:
openFileDialog(GUI_FILE_SAMPLE_OPEN_REPLACE_RAW);
break;
case GUI_ACTION_SAMPLE_LIST_SAVE:
if (curSample>=0 && curSample<(int)e->song.sample.size()) openFileDialog(GUI_FILE_SAMPLE_SAVE);
break;

View file

@ -24,7 +24,7 @@
#include "actionUtil.h"
const char* noteNameNormal(short note, short octave) {
const char* FurnaceGUI::noteNameNormal(short note, short octave) {
if (note==100) { // note cut
return "OFF";
} else if (note==101) { // note off and envelope release
@ -577,7 +577,7 @@ void FurnaceGUI::doChangeIns(int ins) {
if (!e->curSubSong->chanShow[iCoarse]) continue;
DivPattern* pat=e->curPat[iCoarse].getPattern(e->curOrders->ord[iCoarse][curOrder],true);
for (int j=selStart.y; j<=selEnd.y; j++) {
if (pat->data[j][2]!=-1 || !(pat->data[j][0]==0 && pat->data[j][1]==0)) {
if (pat->data[j][2]!=-1 || !((pat->data[j][0]==0 || pat->data[j][0]==100 || pat->data[j][0]==101 || pat->data[j][0]==102) && pat->data[j][1]==0)) {
pat->data[j][2]=ins;
}
}
@ -616,9 +616,9 @@ void FurnaceGUI::doInterpolate() {
}
} else {
for (int j=selStart.y; j<=selEnd.y; j++) {
if (pat->data[j][0]!=0 && pat->data[j][1]!=0) {
if (pat->data[j][0]!=0 || pat->data[j][1]!=0) {
if (pat->data[j][0]!=100 && pat->data[j][0]!=101 && pat->data[j][0]!=102) {
points.emplace(points.end(),j,pat->data[j][0]+pat->data[j][1]*12);
points.emplace(points.end(),j,pat->data[j][0]+(signed char)pat->data[j][1]*12);
}
}
}

View file

@ -10,7 +10,7 @@ void FurnaceGUI::drawEffectList() {
}
if (!effectListOpen) return;
if (ImGui::Begin("Effect List",&effectListOpen,globalWinFlags)) {
ImGui::Text("System at cursor: %s",e->getSystemName(e->sysOfChan[cursor.xCoarse]));
ImGui::Text("Chip at cursor: %s",e->getSystemName(e->sysOfChan[cursor.xCoarse]));
if (ImGui::BeginTable("effectList",2)) {
ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthFixed);
ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthStretch);

View file

@ -593,11 +593,11 @@ void FurnaceGUI::drawFindReplace() {
i.note=0;
}
if (i.note==130) {
snprintf(tempID,1024,"REL");
snprintf(tempID,1024,"%s##MREL",macroRelLabel);
} else if (i.note==129) {
snprintf(tempID,1024,"===");
snprintf(tempID,1024,"%s##NREL",noteRelLabel);
} else if (i.note==128) {
snprintf(tempID,1024,"OFF");
snprintf(tempID,1024,"%s##NOFF",noteOffLabel);
} else if (i.note>=-60 && i.note<120) {
snprintf(tempID,1024,"%s",noteNames[i.note+60]);
} else {
@ -613,13 +613,13 @@ void FurnaceGUI::drawFindReplace() {
}
}
if (i.noteMode!=GUI_QUERY_RANGE && i.noteMode!=GUI_QUERY_RANGE_NOT) {
if (ImGui::Selectable("OFF",i.note==128)) {
if (ImGui::Selectable(noteOffLabel,i.note==128)) {
i.note=128;
}
if (ImGui::Selectable("===",i.note==129)) {
if (ImGui::Selectable(noteRelLabel,i.note==129)) {
i.note=129;
}
if (ImGui::Selectable("REL",i.note==130)) {
if (ImGui::Selectable(macroRelLabel,i.note==130)) {
i.note=130;
}
}
@ -916,11 +916,11 @@ void FurnaceGUI::drawFindReplace() {
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
if (queryReplaceNoteMode==GUI_QUERY_REPLACE_SET) {
if (queryReplaceNote==130) {
snprintf(tempID,1024,"REL");
snprintf(tempID,1024,"%s##MREL",macroRelLabel);
} else if (queryReplaceNote==129) {
snprintf(tempID,1024,"===");
snprintf(tempID,1024,"%s##NREL",noteRelLabel);
} else if (queryReplaceNote==128) {
snprintf(tempID,1024,"OFF");
snprintf(tempID,1024,"%s##NOFF",noteOffLabel);
} else if (queryReplaceNote>=-60 && queryReplaceNote<120) {
snprintf(tempID,1024,"%s",noteNames[queryReplaceNote+60]);
} else {
@ -934,13 +934,13 @@ void FurnaceGUI::drawFindReplace() {
queryReplaceNote=j-60;
}
}
if (ImGui::Selectable("OFF",queryReplaceNote==128)) {
if (ImGui::Selectable(noteOffLabel,queryReplaceNote==128)) {
queryReplaceNote=128;
}
if (ImGui::Selectable("===",queryReplaceNote==129)) {
if (ImGui::Selectable(noteRelLabel,queryReplaceNote==129)) {
queryReplaceNote=129;
}
if (ImGui::Selectable("REL",queryReplaceNote==130)) {
if (ImGui::Selectable(macroRelLabel,queryReplaceNote==130)) {
queryReplaceNote=130;
}
ImGui::EndCombo();

View file

@ -84,13 +84,13 @@ void FurnaceGUI::bindEngine(DivEngine* eng) {
const char* FurnaceGUI::noteName(short note, short octave) {
if (note==100) {
return "OFF";
return noteOffLabel;
} else if (note==101) { // note off and envelope release
return "===";
return noteRelLabel;
} else if (note==102) { // envelope release only
return "REL";
return macroRelLabel;
} else if (octave==0 && note==0) {
return "...";
return emptyLabel;
} else if (note==0 && octave!=0) {
return "BUG";
}
@ -195,23 +195,30 @@ void FurnaceGUI::decodeKeyMap(std::map<int,int>& map, String source) {
}
}
void FurnaceGUI::encodeMMLStr(String& target, int* macro, int macroLen, int macroLoop, int macroRel, bool hex) {
void FurnaceGUI::encodeMMLStr(String& target, int* macro, int macroLen, int macroLoop, int macroRel, bool hex, bool bit30) {
target="";
char buf[32];
for (int i=0; i<macroLen; i++) {
if (i==macroLoop) target+="| ";
if (i==macroRel) target+="/ ";
if (bit30 && ((macro[i]&0xc0000000)==0x40000000 || (macro[i]&0xc0000000)==0x80000000)) target+="@";
int macroVal=macro[i];
if (macro[i]<0) {
if (!(macroVal&0x40000000)) macroVal|=0x40000000;
} else {
if (macroVal&0x40000000) macroVal&=~0x40000000;
}
if (hex) {
if (i==macroLen-1) {
snprintf(buf,31,"%.2X",macro[i]);
snprintf(buf,31,"%.2X",macroVal);
} else {
snprintf(buf,31,"%.2X ",macro[i]);
snprintf(buf,31,"%.2X ",macroVal);
}
} else {
if (i==macroLen-1) {
snprintf(buf,31,"%d",macro[i]);
snprintf(buf,31,"%d",macroVal);
} else {
snprintf(buf,31,"%d ",macro[i]);
snprintf(buf,31,"%d ",macroVal);
}
}
target+=buf;
@ -276,13 +283,14 @@ void FurnaceGUI::decodeMMLStrW(String& source, int* macro, int& macroLen, int ma
}
}
void FurnaceGUI::decodeMMLStr(String& source, int* macro, unsigned char& macroLen, signed char& macroLoop, int macroMin, int macroMax, signed char& macroRel) {
void FurnaceGUI::decodeMMLStr(String& source, int* macro, unsigned char& macroLen, unsigned char& macroLoop, int macroMin, int macroMax, unsigned char& macroRel, bool bit30) {
int buf=0;
bool negaBuf=false;
bool setBit30=false;
bool hasVal=false;
macroLen=0;
macroLoop=-1;
macroRel=-1;
macroLoop=255;
macroRel=255;
for (char& i: source) {
switch (i) {
case '0': case '1': case '2': case '3': case '4':
@ -297,6 +305,11 @@ void FurnaceGUI::decodeMMLStr(String& source, int* macro, unsigned char& macroLe
negaBuf=true;
}
break;
case '@':
if (bit30) {
setBit30=true;
}
break;
case ' ':
if (hasVal) {
hasVal=false;
@ -304,6 +317,8 @@ void FurnaceGUI::decodeMMLStr(String& source, int* macro, unsigned char& macroLe
negaBuf=false;
if (macro[macroLen]<macroMin) macro[macroLen]=macroMin;
if (macro[macroLen]>macroMax) macro[macroLen]=macroMax;
if (setBit30) macro[macroLen]^=0x40000000;
setBit30=false;
macroLen++;
buf=0;
}
@ -315,10 +330,12 @@ void FurnaceGUI::decodeMMLStr(String& source, int* macro, unsigned char& macroLe
negaBuf=false;
if (macro[macroLen]<macroMin) macro[macroLen]=macroMin;
if (macro[macroLen]>macroMax) macro[macroLen]=macroMax;
if (setBit30) macro[macroLen]^=0x40000000;
setBit30=false;
macroLen++;
buf=0;
}
if (macroLoop==-1) {
if (macroLoop==255) {
macroLoop=macroLen;
}
break;
@ -329,22 +346,26 @@ void FurnaceGUI::decodeMMLStr(String& source, int* macro, unsigned char& macroLe
negaBuf=false;
if (macro[macroLen]<macroMin) macro[macroLen]=macroMin;
if (macro[macroLen]>macroMax) macro[macroLen]=macroMax;
if (setBit30) macro[macroLen]^=0x40000000;
setBit30=false;
macroLen++;
buf=0;
}
if (macroRel==-1) {
if (macroRel==255) {
macroRel=macroLen;
}
break;
}
if (macroLen>=128) break;
if (macroLen>=255) break;
}
if (hasVal && macroLen<128) {
if (hasVal && macroLen<255) {
hasVal=false;
macro[macroLen]=negaBuf?-buf:buf;
negaBuf=false;
if (macro[macroLen]<macroMin) macro[macroLen]=macroMin;
if (macro[macroLen]>macroMax) macro[macroLen]=macroMax;
if (setBit30) macro[macroLen]^=0x40000000;
setBit30=false;
macroLen++;
buf=0;
}
@ -1267,6 +1288,7 @@ void FurnaceGUI::openFileDialog(FurnaceGUIFileDialogs type) {
hasOpened=fileDialog->openLoad(
"Load Instrument",
// TODO supply loadable formats in a dynamic, scalable, "DRY" way.
// thank the author of IGFD for making things impossible
{"all compatible files", "*.fui *.dmp *.tfi *.vgi *.s3i *.sbi *.opli *.opni *.y12 *.bnk *.ff *.gyb *.opm *.wopl *.wopn",
"Furnace instrument", "*.fui",
"DefleMask preset", "*.dmp",
@ -1313,13 +1335,15 @@ void FurnaceGUI::openFileDialog(FurnaceGUIFileDialogs type) {
if (!dirExists(workingDirIns)) workingDirIns=getHomeDir();
hasOpened=fileDialog->openSave(
"Save Instrument",
{"Furnace instrument", "*.fui"},
"Furnace instrument{.fui}",
{"Furnace instrument", "*.fui",
"DefleMask preset", "*.dmp"},
"Furnace instrument{.fui},DefleMask preset{.dmp}",
workingDirIns,
dpiScale
);
break;
case GUI_FILE_WAVE_OPEN:
case GUI_FILE_WAVE_OPEN_REPLACE:
if (!dirExists(workingDirWave)) workingDirWave=getHomeDir();
hasOpened=fileDialog->openLoad(
"Load Wavetable",
@ -1334,13 +1358,16 @@ void FurnaceGUI::openFileDialog(FurnaceGUIFileDialogs type) {
if (!dirExists(workingDirWave)) workingDirWave=getHomeDir();
hasOpened=fileDialog->openSave(
"Save Wavetable",
{"Furnace wavetable", ".fuw"},
"Furnace wavetable{.fuw}",
{"Furnace wavetable", ".fuw",
"DefleMask wavetable", ".dmw",
"raw data", ".raw"},
"Furnace wavetable{.fuw},DefleMask wavetable{.dmw},raw data{.raw}",
workingDirWave,
dpiScale
);
break;
case GUI_FILE_SAMPLE_OPEN:
case GUI_FILE_SAMPLE_OPEN_REPLACE:
if (!dirExists(workingDirSample)) workingDirSample=getHomeDir();
hasOpened=fileDialog->openLoad(
"Load Sample",
@ -1351,6 +1378,17 @@ void FurnaceGUI::openFileDialog(FurnaceGUIFileDialogs type) {
dpiScale
);
break;
case GUI_FILE_SAMPLE_OPEN_RAW:
case GUI_FILE_SAMPLE_OPEN_REPLACE_RAW:
if (!dirExists(workingDirSample)) workingDirSample=getHomeDir();
hasOpened=fileDialog->openLoad(
"Load Raw Sample",
{"all files", ".*"},
".*",
workingDirSample,
dpiScale
);
break;
case GUI_FILE_SAMPLE_SAVE:
if (!dirExists(workingDirSample)) workingDirSample=getHomeDir();
hasOpened=fileDialog->openSave(
@ -1741,8 +1779,35 @@ void FurnaceGUI::showError(String what) {
displayError=true;
}
// what monster did I just create here?
#define B30(tt) (macroDragBit30?((((tt)&0xc0000000)==0x40000000 || ((tt)&0xc0000000)==0x80000000)?0x40000000:0):0)
#define MACRO_DRAG(t) \
if (macroDragBitMode) { \
if (macroDragSettingBit30) { \
if (macroDragLastX!=x || macroDragLastY!=y) { \
macroDragLastX=x; \
macroDragLastY=y; \
if (macroDragInitialValueSet) { \
if (!macroDragInitialValue) { \
if (t[x]&0x80000000) { \
t[x]&=~0x40000000; \
} else { \
t[x]|=0x40000000; \
} \
} else { \
if (t[x]&0x80000000) { \
t[x]|=0x40000000; \
} else { \
t[x]&=~0x40000000; \
} \
} \
} else { \
macroDragInitialValue=(((t[x])&0xc0000000)==0x40000000 || ((t[x])&0xc0000000)==0x80000000); \
macroDragInitialValueSet=true; \
t[x]^=0x40000000; \
} \
} \
} else if (macroDragBitMode) { \
if (macroDragLastX!=x || macroDragLastY!=y) { \
macroDragLastX=x; \
macroDragLastY=y; \
@ -1773,25 +1838,25 @@ void FurnaceGUI::showError(String what) {
} \
if (macroDragMouseMoved) { \
if ((int)round(x-macroDragLineInitial.x)==0) { \
t[x]=macroDragLineInitial.y; \
t[x]=B30(t[x])^(int)(macroDragLineInitial.y); \
} else { \
if ((int)round(x-macroDragLineInitial.x)<0) { \
for (int i=0; i<=(int)round(macroDragLineInitial.x-x); i++) { \
int index=(int)round(x+i); \
if (index<0) continue; \
t[index]=y+(macroDragLineInitial.y-y)*((float)i/(float)(macroDragLineInitial.x-x)); \
t[index]=B30(t[index])^(int)(y+(macroDragLineInitial.y-y)*((float)i/(float)(macroDragLineInitial.x-x))); \
} \
} else { \
for (int i=0; i<=(int)round(x-macroDragLineInitial.x); i++) { \
int index=(int)round(i+macroDragLineInitial.x); \
if (index<0) continue; \
t[index]=macroDragLineInitial.y+(y-macroDragLineInitial.y)*((float)i/(x-macroDragLineInitial.x)); \
t[index]=B30(t[index])^(int)(macroDragLineInitial.y+(y-macroDragLineInitial.y)*((float)i/(x-macroDragLineInitial.x))); \
} \
} \
} \
} \
} else { \
t[x]=y; \
t[x]=B30(t[x])^(y); \
} \
}
@ -1876,20 +1941,6 @@ void FurnaceGUI::processDrags(int dragX, int dragY) {
}
}
#define sysAddOption(x) \
if (ImGui::MenuItem(getSystemName(x))) { \
if (!e->addSystem(x)) { \
showError("cannot add chip! ("+e->getLastError()+")"); \
} \
updateWindowTitle(); \
}
#define sysChangeOption(x,y) \
if (ImGui::MenuItem(getSystemName(y),NULL,e->song.system[x]==y)) { \
e->changeSystem(x,y,preserveChanPos); \
updateWindowTitle(); \
}
#define checkExtension(x) \
String lowerCase=fileName; \
for (char& i: lowerCase) { \
@ -1908,6 +1959,15 @@ void FurnaceGUI::processDrags(int dragX, int dragY) {
fileName+=fallback; \
}
#define checkExtensionTriple(x,y,z,fallback) \
String lowerCase=fileName; \
for (char& i: lowerCase) { \
if (i>='A' && i<='Z') i+='a'-'A'; \
} \
if (lowerCase.size()<4 || (lowerCase.rfind(x)!=lowerCase.size()-4 && lowerCase.rfind(y)!=lowerCase.size()-4 && lowerCase.rfind(z)!=lowerCase.size()-4)) { \
fileName+=fallback; \
}
#define drawOpMask(m) \
ImGui::PushFont(patFont); \
ImGui::PushID("om_" #m); \
@ -2630,6 +2690,8 @@ bool FurnaceGUI::loop() {
case SDL_DROPFILE:
if (ev.drop.file!=NULL) {
std::vector<DivInstrument*> instruments=e->instrumentFromFile(ev.drop.file);
DivWavetable* droppedWave=NULL;
DivSample* droppedSample=NULL;;
if (!instruments.empty()) {
if (!e->getWarnings().empty()) {
showWarning(e->getWarnings(),GUI_WARN_GENERIC);
@ -2639,10 +2701,12 @@ bool FurnaceGUI::loop() {
}
nextWindow=GUI_WINDOW_INS_LIST;
MARK_MODIFIED;
} else if (e->addWaveFromFile(ev.drop.file,false)) {
} else if ((droppedWave=e->waveFromFile(ev.drop.file,false))!=NULL) {
e->addWavePtr(droppedWave);
nextWindow=GUI_WINDOW_WAVE_LIST;
MARK_MODIFIED;
} else if (e->addSampleFromFile(ev.drop.file)!=-1) {
} else if ((droppedSample=e->sampleFromFile(ev.drop.file))!=NULL) {
e->addSamplePtr(droppedSample);
nextWindow=GUI_WINDOW_SAMPLE_LIST;
MARK_MODIFIED;
} else if (modified) {
@ -2973,9 +3037,13 @@ bool FurnaceGUI::loop() {
}
ImGui::Separator();
if (ImGui::BeginMenu("add chip...")) {
for (int j=0; availableSystems[j]; j++) {
if (!settings.hiddenSystems && (availableSystems[j]==DIV_SYSTEM_YMU759 || availableSystems[j]==DIV_SYSTEM_DUMMY)) continue;
sysAddOption((DivSystem)availableSystems[j]);
DivSystem picked=systemPicker();
if (picked!=DIV_SYSTEM_NULL) {
if (!e->addSystem(picked)) {
showError("cannot add chip! ("+e->getLastError()+")");
}
ImGui::CloseCurrentPopup();
updateWindowTitle();
}
ImGui::EndMenu();
}
@ -2992,9 +3060,11 @@ bool FurnaceGUI::loop() {
ImGui::Checkbox("Preserve channel positions",&preserveChanPos);
for (int i=0; i<e->song.systemLen; i++) {
if (ImGui::BeginMenu(fmt::sprintf("%d. %s##_SYSC%d",i+1,getSystemName(e->song.system[i]),i).c_str())) {
for (int j=0; availableSystems[j]; j++) {
if (!settings.hiddenSystems && (availableSystems[j]==DIV_SYSTEM_YMU759 || availableSystems[j]==DIV_SYSTEM_DUMMY)) continue;
sysChangeOption(i,(DivSystem)availableSystems[j]);
DivSystem picked=systemPicker();
if (picked!=DIV_SYSTEM_NULL) {
e->changeSystem(i,picked,preserveChanPos);
updateWindowTitle();
ImGui::CloseCurrentPopup();
}
ImGui::EndMenu();
}
@ -3077,6 +3147,8 @@ bool FurnaceGUI::loop() {
if (ImGui::MenuItem("pattern",BIND_FOR(GUI_ACTION_WINDOW_PATTERN),patternOpen)) patternOpen=!patternOpen;
if (ImGui::MenuItem("mixer",BIND_FOR(GUI_ACTION_WINDOW_MIXER),mixerOpen)) mixerOpen=!mixerOpen;
if (ImGui::MenuItem("channels",BIND_FOR(GUI_ACTION_WINDOW_CHANNELS),channelsOpen)) channelsOpen=!channelsOpen;
if (ImGui::MenuItem("pattern manager",BIND_FOR(GUI_ACTION_WINDOW_PAT_MANAGER),patManagerOpen)) patManagerOpen=!patManagerOpen;
if (ImGui::MenuItem("chip manager",BIND_FOR(GUI_ACTION_WINDOW_SYS_MANAGER),sysManagerOpen)) sysManagerOpen=!sysManagerOpen;
if (ImGui::MenuItem("compatibility flags",BIND_FOR(GUI_ACTION_WINDOW_COMPAT_FLAGS),compatFlagsOpen)) compatFlagsOpen=!compatFlagsOpen;
if (ImGui::MenuItem("song comments",BIND_FOR(GUI_ACTION_WINDOW_NOTES),notesOpen)) notesOpen=!notesOpen;
ImGui::Separator();
@ -3211,6 +3283,8 @@ bool FurnaceGUI::loop() {
drawPiano();
drawNotes();
drawChannels();
drawPatManager();
drawSysManager();
drawRegView();
drawLog();
drawEffectList();
@ -3269,10 +3343,14 @@ bool FurnaceGUI::loop() {
workingDirIns=fileDialog->getPath()+DIR_SEPARATOR_STR;
break;
case GUI_FILE_WAVE_OPEN:
case GUI_FILE_WAVE_OPEN_REPLACE:
case GUI_FILE_WAVE_SAVE:
workingDirWave=fileDialog->getPath()+DIR_SEPARATOR_STR;
break;
case GUI_FILE_SAMPLE_OPEN:
case GUI_FILE_SAMPLE_OPEN_RAW:
case GUI_FILE_SAMPLE_OPEN_REPLACE:
case GUI_FILE_SAMPLE_OPEN_REPLACE_RAW:
case GUI_FILE_SAMPLE_SAVE:
workingDirSample=fileDialog->getPath()+DIR_SEPARATOR_STR;
break;
@ -3344,10 +3422,21 @@ bool FurnaceGUI::loop() {
checkExtension(".wav");
}
if (curFileDialog==GUI_FILE_INS_SAVE) {
checkExtension(".fui");
// we can't tell whether the user chose .fui or .dmp in the system file picker
const char* fallbackExt=(settings.sysFileDialog || ImGuiFileDialog::Instance()->GetCurrentFilter()=="Furnace instrument")?".fui":".dmp";
checkExtensionDual(".fui",".dmp",fallbackExt);
}
if (curFileDialog==GUI_FILE_WAVE_SAVE) {
checkExtension(".fuw");
// same thing here
const char* fallbackExt=".fuw";
if (!settings.sysFileDialog) {
if (ImGuiFileDialog::Instance()->GetCurrentFilter()=="raw data") {
fallbackExt=".raw";
} else if (ImGuiFileDialog::Instance()->GetCurrentFilter()=="DefleMask wavetable") {
fallbackExt=".dmw";
}
}
checkExtensionTriple(".fuw",".dmw",".raw",fallbackExt);
}
if (curFileDialog==GUI_FILE_EXPORT_VGM) {
checkExtension(".vgm");
@ -3430,21 +3519,75 @@ bool FurnaceGUI::loop() {
break;
case GUI_FILE_INS_SAVE:
if (curIns>=0 && curIns<(int)e->song.ins.size()) {
e->song.ins[curIns]->save(copyOfName.c_str());
String lowerCase=fileName;
for (char& i: lowerCase) {
if (i>='A' && i<='Z') i+='a'-'A';
}
if ((lowerCase.size()<4 || lowerCase.rfind(".dmp")!=lowerCase.size()-4)) {
e->song.ins[curIns]->save(copyOfName.c_str());
} else {
if (!e->song.ins[curIns]->saveDMP(copyOfName.c_str())) {
showError("error while saving instrument! make sure your instrument is compatible.");
}
}
}
break;
case GUI_FILE_WAVE_SAVE:
if (curWave>=0 && curWave<(int)e->song.wave.size()) {
e->song.wave[curWave]->save(copyOfName.c_str());
String lowerCase=fileName;
for (char& i: lowerCase) {
if (i>='A' && i<='Z') i+='a'-'A';
}
if (lowerCase.size()<4) {
e->song.wave[curWave]->save(copyOfName.c_str());
} else if (lowerCase.rfind(".dmw")==lowerCase.size()-4) {
e->song.wave[curWave]->saveDMW(copyOfName.c_str());
} else if (lowerCase.rfind(".raw")==lowerCase.size()-4) {
e->song.wave[curWave]->saveRaw(copyOfName.c_str());
} else {
e->song.wave[curWave]->save(copyOfName.c_str());
}
}
break;
case GUI_FILE_SAMPLE_OPEN:
if (e->addSampleFromFile(copyOfName.c_str())==-1) {
case GUI_FILE_SAMPLE_OPEN: {
DivSample* s=e->sampleFromFile(copyOfName.c_str());
if (s==NULL) {
showError(e->getLastError());
} else {
MARK_MODIFIED;
if (e->addSamplePtr(s)==-1) {
showError(e->getLastError());
} else {
MARK_MODIFIED;
}
}
break;
}
case GUI_FILE_SAMPLE_OPEN_REPLACE: {
DivSample* s=e->sampleFromFile(copyOfName.c_str());
if (s==NULL) {
showError(e->getLastError());
} else {
if (curSample>=0 && curSample<(int)e->song.sample.size()) {
e->lockEngine([this,s]() {
// if it crashes here please tell me...
DivSample* oldSample=e->song.sample[curSample];
e->song.sample[curSample]=s;
delete oldSample;
e->renderSamples();
MARK_MODIFIED;
});
} else {
showError("...but you haven't selected a sample!");
delete s;
}
}
break;
}
case GUI_FILE_SAMPLE_OPEN_RAW:
case GUI_FILE_SAMPLE_OPEN_REPLACE_RAW:
pendingRawSample=copyOfName;
displayPendingRawSample=true;
break;
case GUI_FILE_SAMPLE_SAVE:
if (curSample>=0 && curSample<(int)e->song.sample.size()) {
e->song.sample[curSample]->save(copyOfName.c_str());
@ -3508,13 +3651,36 @@ bool FurnaceGUI::loop() {
}
break;
}
case GUI_FILE_WAVE_OPEN:
if (!e->addWaveFromFile(copyOfName.c_str())) {
case GUI_FILE_WAVE_OPEN: {
DivWavetable* wave=e->waveFromFile(copyOfName.c_str());
if (wave==NULL) {
showError("cannot load wavetable! ("+e->getLastError()+")");
} else {
MARK_MODIFIED;
if (e->addWavePtr(wave)==-1) {
showError("cannot load wavetable! ("+e->getLastError()+")");
} else {
MARK_MODIFIED;
}
}
break;
}
case GUI_FILE_WAVE_OPEN_REPLACE: {
DivWavetable* wave=e->waveFromFile(copyOfName.c_str());
if (wave==NULL) {
showError("cannot load wavetable! ("+e->getLastError()+")");
} else {
if (curWave>=0 && curWave<(int)e->song.wave.size()) {
e->lockEngine([this,wave]() {
*e->song.wave[curWave]=*wave;
MARK_MODIFIED;
});
} else {
showError("...but you haven't selected a wavetable!");
}
delete wave;
}
break;
}
case GUI_FILE_EXPORT_VGM: {
SafeWriter* w=e->saveVGM(willExport,vgmExportLoop,vgmExportVersion,vgmExportPatternHints);
if (w!=NULL) {
@ -3641,6 +3807,11 @@ bool FurnaceGUI::loop() {
ImGui::OpenPopup("Select Instrument");
}
if (displayPendingRawSample) {
displayPendingRawSample=false;
ImGui::OpenPopup("Import Raw Sample");
}
if (displayExporting) {
displayExporting=false;
ImGui::OpenPopup("Rendering...");
@ -3982,6 +4153,16 @@ bool FurnaceGUI::loop() {
ImGui::CloseCurrentPopup();
}
break;
case GUI_WARN_SYSTEM_DEL:
if (ImGui::Button("Yes")) {
e->removeSystem(sysToDelete,preserveChanPos);
ImGui::CloseCurrentPopup();
}
ImGui::SameLine();
if (ImGui::Button("No")) {
ImGui::CloseCurrentPopup();
}
break;
case GUI_WARN_GENERIC:
if (ImGui::Button("OK")) {
ImGui::CloseCurrentPopup();
@ -4071,6 +4252,53 @@ bool FurnaceGUI::loop() {
ImGui::EndPopup();
}
if (ImGui::BeginPopupModal("Import Raw Sample",NULL,ImGuiWindowFlags_AlwaysAutoResize)) {
ImGui::Text("Data type:");
for (int i=0; i<DIV_SAMPLE_DEPTH_MAX; i++) {
if (sampleDepths[i]==NULL) continue;
if (ImGui::RadioButton(sampleDepths[i],pendingRawSampleDepth==i)) pendingRawSampleDepth=i;
}
if (pendingRawSampleDepth!=DIV_SAMPLE_DEPTH_8BIT && pendingRawSampleDepth!=DIV_SAMPLE_DEPTH_16BIT) {
pendingRawSampleChannels=1;
}
if (pendingRawSampleDepth!=DIV_SAMPLE_DEPTH_16BIT) {
pendingRawSampleBigEndian=false;
}
ImGui::BeginDisabled(pendingRawSampleDepth!=DIV_SAMPLE_DEPTH_8BIT && pendingRawSampleDepth!=DIV_SAMPLE_DEPTH_16BIT);
ImGui::Text("Channels");
ImGui::SameLine();
if (ImGui::InputInt("##RSChans",&pendingRawSampleChannels)) {
}
ImGui::Text("(will be mixed down to mono)");
ImGui::Checkbox("Unsigned",&pendingRawSampleUnsigned);
ImGui::EndDisabled();
ImGui::BeginDisabled(pendingRawSampleDepth!=DIV_SAMPLE_DEPTH_16BIT);
ImGui::Checkbox("Big endian",&pendingRawSampleBigEndian);
ImGui::EndDisabled();
if (ImGui::Button("OK")) {
DivSample* s=e->sampleFromFileRaw(pendingRawSample.c_str(),(DivSampleDepth)pendingRawSampleDepth,pendingRawSampleChannels,pendingRawSampleBigEndian,pendingRawSampleUnsigned);
if (s==NULL) {
showError(e->getLastError());
} else {
if (e->addSamplePtr(s)==-1) {
showError(e->getLastError());
} else {
MARK_MODIFIED;
}
}
ImGui::CloseCurrentPopup();
}
ImGui::SameLine();
if (ImGui::Button("Cancel")) {
ImGui::CloseCurrentPopup();
}
ImGui::EndPopup();
}
layoutTimeEnd=SDL_GetPerformanceCounter();
// backup trigger
@ -4187,6 +4415,8 @@ bool FurnaceGUI::init() {
pianoOpen=e->getConfBool("pianoOpen",false);
notesOpen=e->getConfBool("notesOpen",false);
channelsOpen=e->getConfBool("channelsOpen",false);
patManagerOpen=e->getConfBool("patManagerOpen",false);
sysManagerOpen=e->getConfBool("sysManagerOpen",false);
regViewOpen=e->getConfBool("regViewOpen",false);
logOpen=e->getConfBool("logOpen",false);
effectListOpen=e->getConfBool("effectListOpen",false);
@ -4429,6 +4659,8 @@ bool FurnaceGUI::finish() {
e->setConf("pianoOpen",pianoOpen);
e->setConf("notesOpen",notesOpen);
e->setConf("channelsOpen",channelsOpen);
e->setConf("patManagerOpen",patManagerOpen);
e->setConf("sysManagerOpen",sysManagerOpen);
e->setConf("regViewOpen",regViewOpen);
e->setConf("logOpen",logOpen);
e->setConf("effectListOpen",effectListOpen);
@ -4521,10 +4753,16 @@ FurnaceGUI::FurnaceGUI():
noteInputPoly(true),
displayPendingIns(false),
pendingInsSingle(false),
displayPendingRawSample(false),
vgmExportVersion(0x171),
drawHalt(10),
macroPointSize(16),
waveEditStyle(0),
curSysSection(NULL),
pendingRawSampleDepth(8),
pendingRawSampleChannels(1),
pendingRawSampleUnsigned(false),
pendingRawSampleBigEndian(false),
globalWinFlags(0),
curFileDialog(GUI_FILE_OPEN),
warnAction(GUI_WARN_OPEN),
@ -4603,6 +4841,8 @@ FurnaceGUI::FurnaceGUI():
subSongsOpen(true),
findOpen(false),
spoilerOpen(false),
patManagerOpen(false),
sysManagerOpen(false),
selecting(false),
selectingFull(false),
dragging(false),
@ -4679,6 +4919,8 @@ FurnaceGUI::FurnaceGUI():
macroDragInitialValueSet(false),
macroDragInitialValue(false),
macroDragChar(false),
macroDragBit30(false),
macroDragSettingBit30(false),
macroDragLineMode(false),
macroDragMouseMoved(false),
macroDragLineInitial(0,0),
@ -4717,6 +4959,8 @@ FurnaceGUI::FurnaceGUI():
eventTimeEnd(0),
eventTimeDelta(0),
chanToMove(-1),
sysToMove(-1),
sysToDelete(-1),
transposeAmount(0),
randomizeMin(0),
randomizeMax(255),
@ -4888,4 +5132,16 @@ FurnaceGUI::FurnaceGUI():
memset(queryReplaceEffectValDo,0,sizeof(bool)*8);
chanOscGrad.bgColor=ImVec4(0.0f,0.0f,0.0f,1.0f);
memset(noteOffLabel,0,32);
memset(noteRelLabel,0,32);
memset(macroRelLabel,0,32);
memset(emptyLabel,0,32);
memset(emptyLabel2,0,32);
strncpy(noteOffLabel,"OFF",32);
strncpy(noteRelLabel,"===",32);
strncpy(macroRelLabel,"REL",32);
strncpy(emptyLabel,"...",32);
strncpy(emptyLabel2,"..",32);
}

View file

@ -208,6 +208,13 @@ enum FurnaceGUIColors {
GUI_COLOR_PATTERN_EFFECT_SYS_SECONDARY,
GUI_COLOR_PATTERN_EFFECT_MISC,
GUI_COLOR_PAT_MANAGER_NULL,
GUI_COLOR_PAT_MANAGER_USED,
GUI_COLOR_PAT_MANAGER_OVERUSED,
GUI_COLOR_PAT_MANAGER_EXTREMELY_OVERUSED,
GUI_COLOR_PAT_MANAGER_COMBO_BREAKER,
GUI_COLOR_PAT_MANAGER_UNUSED,
GUI_COLOR_PIANO_BACKGROUND,
GUI_COLOR_PIANO_KEY_BOTTOM,
GUI_COLOR_PIANO_KEY_TOP,
@ -250,6 +257,8 @@ enum FurnaceGUIWindows {
GUI_WINDOW_PIANO,
GUI_WINDOW_NOTES,
GUI_WINDOW_CHANNELS,
GUI_WINDOW_PAT_MANAGER,
GUI_WINDOW_SYS_MANAGER,
GUI_WINDOW_REGISTER_VIEW,
GUI_WINDOW_LOG,
GUI_WINDOW_EFFECT_LIST,
@ -267,8 +276,12 @@ enum FurnaceGUIFileDialogs {
GUI_FILE_INS_OPEN_REPLACE,
GUI_FILE_INS_SAVE,
GUI_FILE_WAVE_OPEN,
GUI_FILE_WAVE_OPEN_REPLACE,
GUI_FILE_WAVE_SAVE,
GUI_FILE_SAMPLE_OPEN,
GUI_FILE_SAMPLE_OPEN_RAW,
GUI_FILE_SAMPLE_OPEN_REPLACE,
GUI_FILE_SAMPLE_OPEN_REPLACE_RAW,
GUI_FILE_SAMPLE_SAVE,
GUI_FILE_EXPORT_AUDIO_ONE,
GUI_FILE_EXPORT_AUDIO_PER_SYS,
@ -305,6 +318,7 @@ enum FurnaceGUIWarnings {
GUI_WARN_CLOSE_SETTINGS,
GUI_WARN_CLEAR,
GUI_WARN_SUBSONG_DEL,
GUI_WARN_SYSTEM_DEL,
GUI_WARN_GENERIC
};
@ -365,6 +379,8 @@ enum FurnaceGUIActions {
GUI_ACTION_WINDOW_PIANO,
GUI_ACTION_WINDOW_NOTES,
GUI_ACTION_WINDOW_CHANNELS,
GUI_ACTION_WINDOW_PAT_MANAGER,
GUI_ACTION_WINDOW_SYS_MANAGER,
GUI_ACTION_WINDOW_REGISTER_VIEW,
GUI_ACTION_WINDOW_LOG,
GUI_ACTION_WINDOW_EFFECT_LIST,
@ -459,6 +475,7 @@ enum FurnaceGUIActions {
GUI_ACTION_WAVE_LIST_ADD,
GUI_ACTION_WAVE_LIST_DUPLICATE,
GUI_ACTION_WAVE_LIST_OPEN,
GUI_ACTION_WAVE_LIST_OPEN_REPLACE,
GUI_ACTION_WAVE_LIST_SAVE,
GUI_ACTION_WAVE_LIST_MOVE_UP,
GUI_ACTION_WAVE_LIST_MOVE_DOWN,
@ -472,6 +489,9 @@ enum FurnaceGUIActions {
GUI_ACTION_SAMPLE_LIST_ADD,
GUI_ACTION_SAMPLE_LIST_DUPLICATE,
GUI_ACTION_SAMPLE_LIST_OPEN,
GUI_ACTION_SAMPLE_LIST_OPEN_REPLACE,
GUI_ACTION_SAMPLE_LIST_OPEN_RAW,
GUI_ACTION_SAMPLE_LIST_OPEN_REPLACE_RAW,
GUI_ACTION_SAMPLE_LIST_SAVE,
GUI_ACTION_SAMPLE_LIST_MOVE_UP,
GUI_ACTION_SAMPLE_LIST_MOVE_DOWN,
@ -858,10 +878,10 @@ struct FurnaceGUIMacroDesc {
const char* modeName;
ImVec4 color;
unsigned int bitOffset;
bool isBitfield, blockMode;
bool isBitfield, blockMode, bit30;
String (*hoverFunc)(int,float);
FurnaceGUIMacroDesc(const char* name, DivInstrumentMacro* m, int macroMin, int macroMax, float macroHeight, ImVec4 col=ImVec4(1.0f,1.0f,1.0f,1.0f), bool block=false, const char* mName=NULL, String (*hf)(int,float)=NULL, bool bitfield=false, const char** bfVal=NULL, unsigned int bitOff=0):
FurnaceGUIMacroDesc(const char* name, DivInstrumentMacro* m, int macroMin, int macroMax, float macroHeight, ImVec4 col=ImVec4(1.0f,1.0f,1.0f,1.0f), bool block=false, const char* mName=NULL, String (*hf)(int,float)=NULL, bool bitfield=false, const char** bfVal=NULL, unsigned int bitOff=0, bool bit30Special=false):
macro(m),
height(macroHeight),
displayName(name),
@ -871,6 +891,7 @@ struct FurnaceGUIMacroDesc {
bitOffset(bitOff),
isBitfield(bitfield),
blockMode(block),
bit30(bit30Special),
hoverFunc(hf) {
// MSVC -> hell
this->min=macroMin;
@ -956,22 +977,30 @@ class FurnaceGUI {
int sampleTexW, sampleTexH;
bool updateSampleTex;
String workingDir, fileName, clipboard, warnString, errorString, lastError, curFileName, nextFile;
String workingDir, fileName, clipboard, warnString, errorString, lastError, curFileName, nextFile, sysSearchQuery, newSongQuery;
String workingDirSong, workingDirIns, workingDirWave, workingDirSample, workingDirAudioExport;
String workingDirVGMExport, workingDirROMExport, workingDirFont, workingDirColors, workingDirKeybinds;
String workingDirLayout, workingDirROM, workingDirTest;
String mmlString[32];
String mmlStringW;
std::vector<DivSystem> sysSearchResults;
std::vector<FurnaceGUISysDef> newSongSearchResults;
bool quit, warnQuit, willCommit, edit, modified, displayError, displayExporting, vgmExportLoop, vgmExportPatternHints;
bool wantCaptureKeyboard, oldWantCaptureKeyboard, displayMacroMenu;
bool displayNew, fullScreen, preserveChanPos, wantScrollList, noteInputPoly;
bool displayPendingIns, pendingInsSingle;
bool displayPendingIns, pendingInsSingle, displayPendingRawSample;
bool willExport[32];
int vgmExportVersion;
int drawHalt;
int macroPointSize;
int waveEditStyle;
const int* curSysSection;
String pendingRawSample;
int pendingRawSampleDepth, pendingRawSampleChannels;
bool pendingRawSampleUnsigned, pendingRawSampleBigEndian;
ImGuiWindowFlags globalWinFlags;
@ -1013,6 +1042,12 @@ class FurnaceGUI {
ImU32 sysCmd1Grad[256];
ImU32 sysCmd2Grad[256];
char noteOffLabel[32];
char noteRelLabel[32];
char macroRelLabel[32];
char emptyLabel[32];
char emptyLabel2[32];
struct Settings {
int mainFontSize, patFontSize, iconSize;
int audioEngine;
@ -1064,6 +1099,8 @@ class FurnaceGUI {
int roundedMenus;
int loadJapanese;
int loadChinese;
int loadChineseTraditional;
int loadKorean;
int fmLayout;
int sampleLayout;
int waveLayout;
@ -1107,6 +1144,12 @@ class FurnaceGUI {
int unsignedDetune;
int noThreadedInput;
int clampSamples;
int saveUnusedPatterns;
int channelColors;
int channelTextColors;
int channelStyle;
int channelVolStyle;
int channelFeedbackStyle;
unsigned int maxUndoSteps;
String mainFontPath;
String patFontPath;
@ -1115,6 +1158,11 @@ class FurnaceGUI {
String midiOutDevice;
String c163Name;
String initialSysName;
String noteOffLabel;
String noteRelLabel;
String macroRelLabel;
String emptyLabel;
String emptyLabel2;
std::vector<int> initialSys;
Settings():
@ -1170,6 +1218,8 @@ class FurnaceGUI {
roundedMenus(0),
loadJapanese(0),
loadChinese(0),
loadChineseTraditional(0),
loadKorean(0),
fmLayout(0),
sampleLayout(0),
waveLayout(0),
@ -1213,6 +1263,12 @@ class FurnaceGUI {
unsignedDetune(0),
noThreadedInput(0),
clampSamples(0),
saveUnusedPatterns(0),
channelColors(1),
channelTextColors(0),
channelStyle(0),
channelVolStyle(0),
channelFeedbackStyle(1),
maxUndoSteps(100),
mainFontPath(""),
patFontPath(""),
@ -1220,7 +1276,12 @@ class FurnaceGUI {
midiInDevice(""),
midiOutDevice(""),
c163Name(""),
initialSysName("Sega Genesis/Mega Drive") {}
initialSysName("Sega Genesis/Mega Drive"),
noteOffLabel("OFF"),
noteRelLabel("==="),
macroRelLabel("REL"),
emptyLabel("..."),
emptyLabel2("..") {}
} settings;
char finalLayoutPath[4096];
@ -1237,7 +1298,7 @@ class FurnaceGUI {
bool waveListOpen, waveEditOpen, sampleListOpen, sampleEditOpen, aboutOpen, settingsOpen;
bool mixerOpen, debugOpen, inspectorOpen, oscOpen, volMeterOpen, statsOpen, compatFlagsOpen;
bool pianoOpen, notesOpen, channelsOpen, regViewOpen, logOpen, effectListOpen, chanOscOpen;
bool subSongsOpen, findOpen, spoilerOpen;
bool subSongsOpen, findOpen, spoilerOpen, patManagerOpen, sysManagerOpen;
SelectionPoint selStart, selEnd, cursor, cursorDrag, dragStart, dragEnd;
bool selecting, selectingFull, dragging, curNibble, orderNibble, followOrders, followPattern, changeAllOrders, mobileUI;
@ -1352,6 +1413,8 @@ class FurnaceGUI {
bool macroDragInitialValueSet;
bool macroDragInitialValue;
bool macroDragChar;
bool macroDragBit30;
bool macroDragSettingBit30;
bool macroDragLineMode;
bool macroDragMouseMoved;
ImVec2 macroDragLineInitial;
@ -1363,7 +1426,7 @@ class FurnaceGUI {
ImVec2 macroLoopDragStart;
ImVec2 macroLoopDragAreaSize;
signed char* macroLoopDragTarget;
unsigned char* macroLoopDragTarget;
int macroLoopDragLen;
bool macroLoopDragActive;
@ -1383,7 +1446,7 @@ class FurnaceGUI {
int renderTimeBegin, renderTimeEnd, renderTimeDelta;
int eventTimeBegin, eventTimeEnd, eventTimeDelta;
int chanToMove;
int chanToMove, sysToMove, sysToDelete;
ImVec2 patWindowPos, patWindowSize;
@ -1546,6 +1609,8 @@ class FurnaceGUI {
void drawPiano();
void drawNotes();
void drawChannels();
void drawPatManager();
void drawSysManager();
void drawRegView();
void drawAbout();
void drawSettings();
@ -1613,6 +1678,7 @@ class FurnaceGUI {
void doReplace();
void doDrag();
void editOptions(bool topMenu);
DivSystem systemPicker();
void noteInput(int num, int key, int vol=-1);
void valueInput(int num, bool direct=false, int target=-1);
@ -1641,8 +1707,8 @@ class FurnaceGUI {
void applyUISettings(bool updateFonts=true);
void initSystemPresets();
void encodeMMLStr(String& target, int* macro, int macroLen, int macroLoop, int macroRel, bool hex=false);
void decodeMMLStr(String& source, int* macro, unsigned char& macroLen, signed char& macroLoop, int macroMin, int macroMax, signed char& macroRel);
void encodeMMLStr(String& target, int* macro, int macroLen, int macroLoop, int macroRel, bool hex=false, bool bit30=false);
void decodeMMLStr(String& source, int* macro, unsigned char& macroLen, unsigned char& macroLoop, int macroMin, int macroMax, unsigned char& macroRel, bool bit30=false);
void decodeMMLStrW(String& source, int* macro, int& macroLen, int macroMax, bool hex=false);
String encodeKeyMap(std::map<int,int>& map);
@ -1653,6 +1719,7 @@ class FurnaceGUI {
public:
void showWarning(String what, FurnaceGUIWarnings type);
void showError(String what);
const char* noteNameNormal(short note, short octave);
const char* noteName(short note, short octave);
bool decodeNote(const char* what, short& note, short& octave);
void bindEngine(DivEngine* eng);

View file

@ -501,6 +501,8 @@ const FurnaceGUIActionDef guiActions[GUI_ACTION_MAX]={
D("WINDOW_PIANO", "Piano", 0),
D("WINDOW_NOTES", "Song Comments", 0),
D("WINDOW_CHANNELS", "Channels", 0),
D("WINDOW_PAT_MANAGER", "Pattern Manager", 0),
D("WINDOW_SYS_MANAGER", "Chip Manager", 0),
D("WINDOW_REGISTER_VIEW", "Register View", 0),
D("WINDOW_LOG", "Log Viewer", 0),
D("EFFECT_LIST", "Effect List", 0),
@ -595,6 +597,7 @@ const FurnaceGUIActionDef guiActions[GUI_ACTION_MAX]={
D("WAVE_LIST_ADD", "Add", SDLK_INSERT),
D("WAVE_LIST_DUPLICATE", "Duplicate", FURKMOD_CMD|SDLK_d),
D("WAVE_LIST_OPEN", "Open", 0),
D("WAVE_LIST_OPEN_REPLACE", "Open (replace current)", 0),
D("WAVE_LIST_SAVE", "Save", 0),
D("WAVE_LIST_MOVE_UP", "Move up", FURKMOD_SHIFT|SDLK_UP),
D("WAVE_LIST_MOVE_DOWN", "Move down", FURKMOD_SHIFT|SDLK_DOWN),
@ -608,6 +611,9 @@ const FurnaceGUIActionDef guiActions[GUI_ACTION_MAX]={
D("SAMPLE_LIST_ADD", "Add", SDLK_INSERT),
D("SAMPLE_LIST_DUPLICATE", "Duplicate", FURKMOD_CMD|SDLK_d),
D("SAMPLE_LIST_OPEN", "Open", 0),
D("SAMPLE_LIST_OPEN_REPLACE", "Open (replace current)", 0),
D("SAMPLE_LIST_OPEN_RAW", "Import raw data", 0),
D("SAMPLE_LIST_OPEN_REPLACE_RAW", "Import raw data (replace current)", 0),
D("SAMPLE_LIST_SAVE", "Save", 0),
D("SAMPLE_LIST_MOVE_UP", "Move up", FURKMOD_SHIFT|SDLK_UP),
D("SAMPLE_LIST_MOVE_DOWN", "Move down", FURKMOD_SHIFT|SDLK_DOWN),
@ -829,6 +835,13 @@ const FurnaceGUIColorDef guiColors[GUI_COLOR_MAX]={
D(GUI_COLOR_PATTERN_EFFECT_SYS_SECONDARY,"",ImVec4(0.0f,1.0f,0.5f,1.0f)),
D(GUI_COLOR_PATTERN_EFFECT_MISC,"",ImVec4(0.3f,0.3f,1.0f,1.0f)),
D(GUI_COLOR_PAT_MANAGER_NULL,"",ImVec4(0.15f,0.15f,0.15f,1.0f)),
D(GUI_COLOR_PAT_MANAGER_USED,"",ImVec4(0.15f,1.0f,0.15f,1.0f)),
D(GUI_COLOR_PAT_MANAGER_OVERUSED,"",ImVec4(1.0f,1.0f,0.15f,1.0f)),
D(GUI_COLOR_PAT_MANAGER_EXTREMELY_OVERUSED,"",ImVec4(1.0f,0.5f,0.15f,1.0f)),
D(GUI_COLOR_PAT_MANAGER_COMBO_BREAKER,"",ImVec4(1.0f,0.15f,1.0f,1.0f)),
D(GUI_COLOR_PAT_MANAGER_UNUSED,"",ImVec4(1.0f,0.15f,0.15f,1.0f)),
D(GUI_COLOR_PIANO_BACKGROUND,"",ImVec4(0.0f,0.0f,0.0f,1.0f)),
D(GUI_COLOR_PIANO_KEY_BOTTOM,"",ImVec4(1.0f,1.0f,1.0f,1.0f)),
D(GUI_COLOR_PIANO_KEY_TOP,"",ImVec4(0.0f,0.0f,0.0f,1.0f)),
@ -848,7 +861,9 @@ const FurnaceGUIColorDef guiColors[GUI_COLOR_MAX]={
};
#undef D
// define systems.
// define chips here
// all chips
const int availableSystems[]={
DIV_SYSTEM_YM2612,
DIV_SYSTEM_YM2612_EXT,
@ -920,3 +935,115 @@ const int availableSystems[]={
0 // don't remove this last one!
};
// FM
const int chipsFM[]={
DIV_SYSTEM_YM2612,
DIV_SYSTEM_YM2612_EXT,
DIV_SYSTEM_YM2612_FRAC,
DIV_SYSTEM_YM2612_FRAC_EXT,
DIV_SYSTEM_YM2151,
DIV_SYSTEM_YM2610,
DIV_SYSTEM_YM2610_EXT,
DIV_SYSTEM_YM2610_FULL,
DIV_SYSTEM_YM2610_FULL_EXT,
DIV_SYSTEM_YM2610B,
DIV_SYSTEM_YM2610B_EXT,
DIV_SYSTEM_YMU759,
DIV_SYSTEM_OPN,
DIV_SYSTEM_OPN_EXT,
DIV_SYSTEM_PC98,
DIV_SYSTEM_PC98_EXT,
DIV_SYSTEM_OPLL,
DIV_SYSTEM_OPLL_DRUMS,
DIV_SYSTEM_VRC7,
DIV_SYSTEM_OPL,
DIV_SYSTEM_OPL_DRUMS,
DIV_SYSTEM_Y8950,
DIV_SYSTEM_Y8950_DRUMS,
DIV_SYSTEM_OPL2,
DIV_SYSTEM_OPL2_DRUMS,
DIV_SYSTEM_OPL3,
DIV_SYSTEM_OPL3_DRUMS,
DIV_SYSTEM_OPZ,
0 // don't remove this last one!
};
// square
const int chipsSquare[]={
DIV_SYSTEM_SMS,
DIV_SYSTEM_AY8910,
DIV_SYSTEM_PCSPKR,
DIV_SYSTEM_SAA1099,
DIV_SYSTEM_VIC20,
0 // don't remove this last one!
};
// wavetable
const int chipsWave[]={
DIV_SYSTEM_PCE,
DIV_SYSTEM_X1_010,
DIV_SYSTEM_SWAN,
DIV_SYSTEM_BUBSYS_WSG,
DIV_SYSTEM_N163,
DIV_SYSTEM_FDS,
DIV_SYSTEM_SCC,
DIV_SYSTEM_SCC_PLUS,
DIV_SYSTEM_NAMCO,
DIV_SYSTEM_NAMCO_15XX,
DIV_SYSTEM_NAMCO_CUS30,
0 // don't remove this last one!
};
// specialized
const int chipsSpecial[]={
DIV_SYSTEM_GB,
DIV_SYSTEM_NES,
DIV_SYSTEM_C64_8580,
DIV_SYSTEM_C64_6581,
DIV_SYSTEM_SFX_BEEPER,
DIV_SYSTEM_DUMMY,
DIV_SYSTEM_SOUND_UNIT,
DIV_SYSTEM_TIA,
DIV_SYSTEM_AY8930,
DIV_SYSTEM_LYNX,
DIV_SYSTEM_VERA,
DIV_SYSTEM_PET,
DIV_SYSTEM_VRC6,
DIV_SYSTEM_MMC5,
0 // don't remove this last one!
};
// sample
const int chipsSample[]={
DIV_SYSTEM_SEGAPCM,
DIV_SYSTEM_SEGAPCM_COMPAT,
DIV_SYSTEM_AMIGA,
DIV_SYSTEM_QSOUND,
DIV_SYSTEM_X1_010,
DIV_SYSTEM_YMZ280B,
DIV_SYSTEM_MSM6258,
DIV_SYSTEM_MSM6295,
DIV_SYSTEM_RF5C68,
DIV_SYSTEM_PCM_DAC,
0 // don't remove this last one!
};
const int* chipCategories[]={
availableSystems,
chipsFM,
chipsSquare,
chipsWave,
chipsSpecial,
chipsSample,
NULL
};
const char* chipCategoryNames[]={
"All chips",
"FM",
"Square",
"Wavetable",
"Special",
"Sample",
NULL
};

View file

@ -43,7 +43,14 @@ extern const char* insTypes[];
extern const char* sampleLoopModes[];
extern const char* sampleDepths[];
extern const char* resampleStrats[];
extern const char* chipCategoryNames[];
extern const int availableSystems[];
extern const int chipsFM[];
extern const int chipsSquare[];
extern const int chipsWavetable[];
extern const int chipsSpecial[];
extern const int chipsSample[];
extern const int* chipCategories[];
extern const FurnaceGUIActionDef guiActions[];
extern const FurnaceGUIColorDef guiColors[];
extern const int altValues[24];

View file

@ -19,6 +19,7 @@
#include "gui.h"
#include "imgui_internal.h"
#include "../engine/macroInt.h"
#include "IconsFontAwesome4.h"
#include "misc/cpp/imgui_stdlib.h"
#include "guiConst.h"
@ -305,10 +306,14 @@ const char* gbHWSeqCmdTypes[6]={
"Loop until Release"
};
// do not change these!
// anything other than a checkbox will look ugly!
//
// if you really need to, and have a good rationale (and by good I mean a VERY
// good one), please tell me and we'll sort it out.
const char* macroAbsoluteMode="Fixed";
const char* macroRelativeMode="Relative";
const char* macroQSoundMode="QSound";
const char* macroDummyMode="Bug";
String macroHoverNote(int id, float val) {
@ -326,6 +331,11 @@ String macroHoverLoop(int id, float val) {
return "";
}
String macroHoverBit30(int id, float val) {
if (val>0) return "Fixed";
return "Relative";
}
String macroHoverES5506FilterMode(int id, float val) {
String mode="???";
switch (((int)val)&3) {
@ -1187,10 +1197,22 @@ String genericGuide(float value) {
return fmt::sprintf("%d",(int)value);
}
inline int deBit30(const int val) {
if ((val&0xc0000000)==0x40000000 || (val&0xc0000000)==0x80000000) return val^0x40000000;
return val;
}
inline bool enBit30(const int val) {
if ((val&0xc0000000)==0x40000000 || (val&0xc0000000)==0x80000000) return true;
return false;
}
void FurnaceGUI::drawMacros(std::vector<FurnaceGUIMacroDesc>& macros) {
float asFloat[256];
int asInt[256];
float loopIndicator[256];
float bit30Indicator[256];
bool doHighlight[256];
int index=0;
float reservedSpace=(settings.oldMacroVSlider)?(20.0f*dpiScale+ImGui::GetStyle().ItemSpacing.x):ImGui::GetStyle().ScrollbarSize;
@ -1209,14 +1231,14 @@ void FurnaceGUI::drawMacros(std::vector<FurnaceGUIMacroDesc>& macros) {
}
ImGui::TableNextColumn();
float availableWidth=ImGui::GetContentRegionAvail().x-reservedSpace;
int totalFit=MIN(128,availableWidth/MAX(1,macroPointSize*dpiScale));
if (macroDragScroll>128-totalFit) {
macroDragScroll=128-totalFit;
int totalFit=MIN(255,availableWidth/MAX(1,macroPointSize*dpiScale));
if (macroDragScroll>255-totalFit) {
macroDragScroll=255-totalFit;
}
ImGui::SetNextItemWidth(availableWidth);
if (CWSliderInt("##MacroScroll",&macroDragScroll,0,128-totalFit,"")) {
if (CWSliderInt("##MacroScroll",&macroDragScroll,0,255-totalFit,"")) {
if (macroDragScroll<0) macroDragScroll=0;
if (macroDragScroll>128-totalFit) macroDragScroll=128-totalFit;
if (macroDragScroll>255-totalFit) macroDragScroll=255-totalFit;
}
// draw macros
@ -1233,9 +1255,36 @@ void FurnaceGUI::drawMacros(std::vector<FurnaceGUIMacroDesc>& macros) {
}
if (i.macro->open) {
ImGui::SetNextItemWidth(lenAvail);
if (ImGui::InputScalar("##IMacroLen",ImGuiDataType_U8,&i.macro->len,&_ONE,&_THREE)) { MARK_MODIFIED
if (i.macro->len>128) i.macro->len=128;
int macroLen=i.macro->len;
if (ImGui::InputScalar("##IMacroLen",ImGuiDataType_U8,&macroLen,&_ONE,&_THREE)) { MARK_MODIFIED
if (macroLen<0) macroLen=0;
if (macroLen>255) macroLen=255;
i.macro->len=macroLen;
}
if (ImGui::Button(ICON_FA_BAR_CHART "##IMacroType")) {
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Coming soon!");
}
ImGui::SameLine();
ImGui::Button(ICON_FA_ELLIPSIS_H "##IMacroSet");
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Delay/Step Length");
}
if (ImGui::BeginPopupContextItem("IMacroSetP",ImGuiPopupFlags_MouseButtonLeft)) {
if (ImGui::InputScalar("Step Length (ticks)##IMacroSpeed",ImGuiDataType_U8,&i.macro->speed,&_ONE,&_THREE)) {
if (i.macro->speed<1) i.macro->speed=1;
MARK_MODIFIED;
}
if (ImGui::InputScalar("Delay##IMacroDelay",ImGuiDataType_U8,&i.macro->delay,&_ONE,&_THREE)) {
MARK_MODIFIED;
}
ImGui::EndPopup();
}
// do not change this!
// anything other than a checkbox will look ugly!
// if you really need more than two macro modes please tell me.
if (i.modeName!=NULL) {
bool modeVal=i.macro->mode;
String modeName=fmt::sprintf("%s##IMacroMode",i.modeName);
@ -1248,17 +1297,19 @@ void FurnaceGUI::drawMacros(std::vector<FurnaceGUIMacroDesc>& macros) {
// macro area
ImGui::TableNextColumn();
for (int j=0; j<256; j++) {
bit30Indicator[j]=0;
if (j+macroDragScroll>=i.macro->len) {
asFloat[j]=0;
asInt[j]=0;
} else {
asFloat[j]=i.macro->val[j+macroDragScroll];
asInt[j]=i.macro->val[j+macroDragScroll]+i.bitOffset;
asFloat[j]=deBit30(i.macro->val[j+macroDragScroll]);
asInt[j]=deBit30(i.macro->val[j+macroDragScroll])+i.bitOffset;
if (i.bit30) bit30Indicator[j]=enBit30(i.macro->val[j+macroDragScroll]);
}
if (j+macroDragScroll>=i.macro->len || (j+macroDragScroll>i.macro->rel && i.macro->loop<i.macro->rel)) {
loopIndicator[j]=0;
} else {
loopIndicator[j]=((i.macro->loop!=-1 && (j+macroDragScroll)>=i.macro->loop))|((i.macro->rel!=-1 && (j+macroDragScroll)==i.macro->rel)<<1);
loopIndicator[j]=((i.macro->loop!=255 && (j+macroDragScroll)>=i.macro->loop))|((i.macro->rel!=255 && (j+macroDragScroll)==i.macro->rel)<<1);
}
}
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding,ImVec2(0.0f,0.0f));
@ -1278,11 +1329,33 @@ void FurnaceGUI::drawMacros(std::vector<FurnaceGUIMacroDesc>& macros) {
if (i.macro->vZoom>(i.max-i.min)) {
i.macro->vZoom=i.max-i.min;
}
memset(doHighlight,0,256*sizeof(bool));
if (e->isRunning()) for (int j=0; j<e->getTotalChannelCount(); j++) {
DivChannelState* chanState=e->getChanState(j);
if (chanState==NULL) continue;
if (chanState->keyOff) continue;
if (chanState->lastIns!=curIns) continue;
DivMacroInt* macroInt=e->getMacroInt(j);
if (macroInt==NULL) continue;
DivMacroStruct* macroStruct=macroInt->structByName(i.macro->name);
if (macroStruct==NULL) continue;
if (macroStruct->lastPos>i.macro->len) continue;
if (macroStruct->lastPos<macroDragScroll) continue;
if (macroStruct->lastPos>255) continue;
if (!macroStruct->actualHad) continue;
doHighlight[macroStruct->lastPos-macroDragScroll]=true;
}
if (i.isBitfield) {
PlotBitfield("##IMacro",asInt,totalFit,0,i.bitfieldBits,i.max,ImVec2(availableWidth,(i.macro->open)?(i.height*dpiScale):(32.0f*dpiScale)));
PlotBitfield("##IMacro",asInt,totalFit,0,i.bitfieldBits,i.max,ImVec2(availableWidth,(i.macro->open)?(i.height*dpiScale):(32.0f*dpiScale)),sizeof(float),doHighlight);
} else {
PlotCustom("##IMacro",asFloat,totalFit,macroDragScroll,NULL,i.min+i.macro->vScroll,i.min+i.macro->vScroll+i.macro->vZoom,ImVec2(availableWidth,(i.macro->open)?(i.height*dpiScale):(32.0f*dpiScale)),sizeof(float),i.color,i.macro->len-macroDragScroll,i.hoverFunc,i.blockMode,i.macro->open?genericGuide:NULL);
PlotCustom("##IMacro",asFloat,totalFit,macroDragScroll,NULL,i.min+i.macro->vScroll,i.min+i.macro->vScroll+i.macro->vZoom,ImVec2(availableWidth,(i.macro->open)?(i.height*dpiScale):(32.0f*dpiScale)),sizeof(float),i.color,i.macro->len-macroDragScroll,i.hoverFunc,i.blockMode,i.macro->open?genericGuide:NULL,doHighlight);
}
if (i.macro->open && (ImGui::IsItemClicked(ImGuiMouseButton_Left) || ImGui::IsItemClicked(ImGuiMouseButton_Right))) {
macroDragStart=ImGui::GetItemRectMin();
@ -1300,6 +1373,8 @@ void FurnaceGUI::drawMacros(std::vector<FurnaceGUIMacroDesc>& macros) {
macroDragInitialValue=false;
macroDragLen=totalFit;
macroDragActive=true;
macroDragBit30=i.bit30;
macroDragSettingBit30=false;
macroDragTarget=i.macro->val;
macroDragChar=false;
macroDragLineMode=(i.isBitfield)?false:ImGui::IsItemClicked(ImGuiMouseButton_Right);
@ -1367,6 +1442,27 @@ void FurnaceGUI::drawMacros(std::vector<FurnaceGUIMacroDesc>& macros) {
}
}
// bit 30 area
if (i.bit30) {
PlotCustom("##IMacroBit30",bit30Indicator,totalFit,macroDragScroll,NULL,0,1,ImVec2(availableWidth,12.0f*dpiScale),sizeof(float),i.color,i.macro->len-macroDragScroll,&macroHoverBit30);
if (ImGui::IsItemClicked(ImGuiMouseButton_Left)) {
macroDragStart=ImGui::GetItemRectMin();
macroDragAreaSize=ImVec2(availableWidth,12.0f*dpiScale);
macroDragInitialValueSet=false;
macroDragInitialValue=false;
macroDragLen=totalFit;
macroDragActive=true;
macroDragBit30=i.bit30;
macroDragSettingBit30=true;
macroDragTarget=i.macro->val;
macroDragChar=false;
macroDragLineMode=false;
macroDragLineInitial=ImVec2(0,0);
lastMacroDesc=i;
processDrags(ImGui::GetMousePos().x,ImGui::GetMousePos().y);
}
}
// loop area
PlotCustom("##IMacroLoop",loopIndicator,totalFit,macroDragScroll,NULL,0,2,ImVec2(availableWidth,12.0f*dpiScale),sizeof(float),i.color,i.macro->len-macroDragScroll,&macroHoverLoop);
if (ImGui::IsItemClicked(ImGuiMouseButton_Left)) {
@ -1383,18 +1479,18 @@ void FurnaceGUI::drawMacros(std::vector<FurnaceGUIMacroDesc>& macros) {
}
if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) {
if (ImGui::IsKeyDown(ImGuiKey_LeftShift) || ImGui::IsKeyDown(ImGuiKey_RightShift)) {
i.macro->rel=-1;
i.macro->rel=255;
} else {
i.macro->loop=-1;
i.macro->loop=255;
}
}
ImGui::SetNextItemWidth(availableWidth);
String& mmlStr=mmlString[index];
if (ImGui::InputText("##IMacroMML",&mmlStr)) {
decodeMMLStr(mmlStr,i.macro->val,i.macro->len,i.macro->loop,i.min,(i.isBitfield)?((1<<(i.isBitfield?i.max:0))-1):i.max,i.macro->rel);
decodeMMLStr(mmlStr,i.macro->val,i.macro->len,i.macro->loop,i.min,(i.isBitfield)?((1<<(i.isBitfield?i.max:0))-1):i.max,i.macro->rel,i.bit30);
}
if (!ImGui::IsItemActive()) {
encodeMMLStr(mmlStr,i.macro->val,i.macro->len,i.macro->loop,i.macro->rel);
encodeMMLStr(mmlStr,i.macro->val,i.macro->len,i.macro->loop,i.macro->rel,false,i.bit30);
}
}
ImGui::PopStyleVar();
@ -1406,9 +1502,9 @@ void FurnaceGUI::drawMacros(std::vector<FurnaceGUIMacroDesc>& macros) {
ImGui::TableNextColumn();
ImGui::TableNextColumn();
ImGui::SetNextItemWidth(availableWidth);
if (CWSliderInt("##MacroScroll",&macroDragScroll,0,128-totalFit,"")) {
if (CWSliderInt("##MacroScroll",&macroDragScroll,0,255-totalFit,"")) {
if (macroDragScroll<0) macroDragScroll=0;
if (macroDragScroll>128-totalFit) macroDragScroll=128-totalFit;
if (macroDragScroll>255-totalFit) macroDragScroll=255-totalFit;
}
ImGui::EndTable();
}
@ -3002,24 +3098,60 @@ void FurnaceGUI::drawInsEdit() {
P(ImGui::Checkbox("Initialize envelope on every note",&ins->gb.alwaysInit));
ImGui::BeginDisabled(ins->gb.softEnv);
P(CWSliderScalar("Volume",ImGuiDataType_U8,&ins->gb.envVol,&_ZERO,&_FIFTEEN)); rightClickable
P(CWSliderScalar("Envelope Length",ImGuiDataType_U8,&ins->gb.envLen,&_ZERO,&_SEVEN)); rightClickable
P(CWSliderScalar("Sound Length",ImGuiDataType_U8,&ins->gb.soundLen,&_ZERO,&_SIXTY_FOUR,ins->gb.soundLen>63?"Infinity":"%d")); rightClickable
ImGui::Text("Envelope Direction:");
if (ImGui::BeginTable("GBParams",2)) {
ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthStretch,0.6f);
ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthStretch,0.4f);
bool goesUp=ins->gb.envDir;
ImGui::SameLine();
if (ImGui::RadioButton("Up",goesUp)) { PARAMETER
goesUp=true;
ins->gb.envDir=goesUp;
}
ImGui::SameLine();
if (ImGui::RadioButton("Down",!goesUp)) { PARAMETER
goesUp=false;
ins->gb.envDir=goesUp;
}
ImGui::TableNextRow();
ImGui::TableNextColumn();
if (ImGui::BeginTable("GBParamsI",2)) {
ImGui::TableSetupColumn("ci0",ImGuiTableColumnFlags_WidthFixed);
ImGui::TableSetupColumn("ci1",ImGuiTableColumnFlags_WidthStretch);
drawGBEnv(ins->gb.envVol,ins->gb.envLen,ins->gb.soundLen,ins->gb.envDir,ImVec2(ImGui::GetContentRegionAvail().x,100.0f*dpiScale));
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Text("Volume");
ImGui::TableNextColumn();
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
P(CWSliderScalar("##GBVolume",ImGuiDataType_U8,&ins->gb.envVol,&_ZERO,&_FIFTEEN)); rightClickable
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Text("Length");
ImGui::TableNextColumn();
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
P(CWSliderScalar("##GBEnvLen",ImGuiDataType_U8,&ins->gb.envLen,&_ZERO,&_SEVEN)); rightClickable
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Text("Sound Length");
ImGui::TableNextColumn();
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
P(CWSliderScalar("##GBSoundLen",ImGuiDataType_U8,&ins->gb.soundLen,&_ZERO,&_SIXTY_FOUR,ins->gb.soundLen>63?"Infinity":"%d")); rightClickable
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Text("Direction");
ImGui::TableNextColumn();
bool goesUp=ins->gb.envDir;
if (ImGui::RadioButton("Up",goesUp)) { PARAMETER
goesUp=true;
ins->gb.envDir=goesUp;
}
ImGui::SameLine();
if (ImGui::RadioButton("Down",!goesUp)) { PARAMETER
goesUp=false;
ins->gb.envDir=goesUp;
}
ImGui::EndTable();
}
ImGui::TableNextColumn();
drawGBEnv(ins->gb.envVol,ins->gb.envLen,ins->gb.soundLen,ins->gb.envDir,ImVec2(ImGui::GetContentRegionAvail().x,100.0f*dpiScale));
ImGui::EndTable();
}
if (ImGui::BeginChild("HWSeq",ImGui::GetContentRegionAvail(),true,ImGuiWindowFlags_MenuBar)) {
ImGui::BeginMenuBar();
@ -3175,13 +3307,13 @@ void FurnaceGUI::drawInsEdit() {
if (ImGui::Button(ICON_FA_CHEVRON_DOWN "##HWCmdDown")) {
if (i<ins->gb.hwSeqLen-1) {
e->lockEngine([ins,i]() {
ins->gb.hwSeq[i-1].cmd^=ins->gb.hwSeq[i].cmd;
ins->gb.hwSeq[i].cmd^=ins->gb.hwSeq[i-1].cmd;
ins->gb.hwSeq[i-1].cmd^=ins->gb.hwSeq[i].cmd;
ins->gb.hwSeq[i+1].cmd^=ins->gb.hwSeq[i].cmd;
ins->gb.hwSeq[i].cmd^=ins->gb.hwSeq[i+1].cmd;
ins->gb.hwSeq[i+1].cmd^=ins->gb.hwSeq[i].cmd;
ins->gb.hwSeq[i-1].data^=ins->gb.hwSeq[i].data;
ins->gb.hwSeq[i].data^=ins->gb.hwSeq[i-1].data;
ins->gb.hwSeq[i-1].data^=ins->gb.hwSeq[i].data;
ins->gb.hwSeq[i+1].data^=ins->gb.hwSeq[i].data;
ins->gb.hwSeq[i].data^=ins->gb.hwSeq[i+1].data;
ins->gb.hwSeq[i+1].data^=ins->gb.hwSeq[i].data;
});
}
MARK_MODIFIED;
@ -3206,7 +3338,6 @@ void FurnaceGUI::drawInsEdit() {
ins->gb.hwSeqLen++;
}
}
}
ImGui::EndChild();
ImGui::EndDisabled();
@ -4166,7 +4297,7 @@ void FurnaceGUI::drawInsEdit() {
if (volMax>0) {
macroList.push_back(FurnaceGUIMacroDesc(volumeLabel,&ins->std.volMacro,volMin,volMax,160,uiColors[GUI_COLOR_MACRO_VOLUME]));
}
macroList.push_back(FurnaceGUIMacroDesc("Arpeggio",&ins->std.arpMacro,-120,120,160,uiColors[GUI_COLOR_MACRO_PITCH],true,macroAbsoluteMode,ins->std.arpMacro.mode?(&macroHoverNote):NULL));
macroList.push_back(FurnaceGUIMacroDesc("Arpeggio",&ins->std.arpMacro,-120,120,160,uiColors[GUI_COLOR_MACRO_PITCH],true,NULL,NULL,false,NULL,0,true));
if (dutyMax>0) {
if (ins->type==DIV_INS_MIKEY) {
macroList.push_back(FurnaceGUIMacroDesc(dutyLabel,&ins->std.dutyMacro,0,dutyMax,160,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true,mikeyFeedbackBits));
@ -4342,8 +4473,8 @@ void FurnaceGUI::drawInsEdit() {
ImGui::Separator();
if (ImGui::MenuItem("clear")) {
lastMacroDesc.macro->len=0;
lastMacroDesc.macro->loop=-1;
lastMacroDesc.macro->rel=-1;
lastMacroDesc.macro->loop=255;
lastMacroDesc.macro->rel=255;
for (int i=0; i<256; i++) {
lastMacroDesc.macro->val[i]=0;
}
@ -4372,15 +4503,15 @@ void FurnaceGUI::drawInsEdit() {
lastMacroDesc.macro->val[i]=val;
}
if (lastMacroDesc.macro->loop>=0 && lastMacroDesc.macro->loop<lastMacroDesc.macro->len) {
if (lastMacroDesc.macro->loop<lastMacroDesc.macro->len) {
lastMacroDesc.macro->loop+=macroOffX;
} else {
lastMacroDesc.macro->loop=-1;
lastMacroDesc.macro->loop=255;
}
if ((lastMacroDesc.macro->rel+macroOffX)>=0 && (lastMacroDesc.macro->rel+macroOffX)<lastMacroDesc.macro->len) {
lastMacroDesc.macro->rel+=macroOffX;
} else {
lastMacroDesc.macro->rel=-1;
lastMacroDesc.macro->rel=255;
}
ImGui::CloseCurrentPopup();

View file

@ -18,7 +18,8 @@
*/
#include "gui.h"
#include <imgui.h>
#include "misc/cpp/imgui_stdlib.h"
#include <algorithm>
void FurnaceGUI::drawNewSong() {
bool accepted=false;
@ -32,35 +33,76 @@ void FurnaceGUI::drawNewSong() {
avail.y-=ImGui::GetFrameHeightWithSpacing();
if (ImGui::BeginChild("sysPickerC",avail,false,ImGuiWindowFlags_NoScrollWithMouse|ImGuiWindowFlags_NoScrollbar)) {
if (ImGui::BeginTable("sysPicker",2,ImGuiTableFlags_BordersInnerV)) {
ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthFixed,0.0f);
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
if (ImGui::InputTextWithHint("##SysSearch","Search...",&newSongQuery)) {
String lowerCase=newSongQuery;
for (char& i: lowerCase) {
if (i>='A' && i<='Z') i+='a'-'A';
}
auto lastItem=std::remove_if(lowerCase.begin(),lowerCase.end(),[](char c) {
return (c==' ' || c=='_' || c=='-');
});
lowerCase.erase(lastItem,lowerCase.end());
newSongSearchResults.clear();
for (FurnaceGUISysCategory& i: sysCategories) {
for (FurnaceGUISysDef& j: i.systems) {
String lowerCase1=j.name;
for (char& i: lowerCase1) {
if (i>='A' && i<='Z') i+='a'-'A';
}
auto lastItem=std::remove_if(lowerCase1.begin(),lowerCase1.end(),[](char c) {
return (c==' ' || c=='_' || c=='-');
});
lowerCase1.erase(lastItem,lowerCase1.end());
if (lowerCase1.find(lowerCase)!=String::npos) {
newSongSearchResults.push_back(j);
}
}
std::sort(newSongSearchResults.begin(),newSongSearchResults.end(),[](const FurnaceGUISysDef& a, const FurnaceGUISysDef& b) {
return strcmp(a.name,b.name)<0;
});
auto lastItem=std::unique(newSongSearchResults.begin(),newSongSearchResults.end(),[](const FurnaceGUISysDef& a, const FurnaceGUISysDef& b) {
return strcmp(a.name,b.name)==0;
});
newSongSearchResults.erase(lastItem,newSongSearchResults.end());
}
}
if (ImGui::BeginTable("sysPicker",newSongQuery.empty()?2:1,ImGuiTableFlags_BordersInnerV)) {
if (newSongQuery.empty()) {
ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthFixed,0.0f);
}
ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthStretch,0.0f);
ImGui::TableNextRow(ImGuiTableRowFlags_Headers);
ImGui::TableNextColumn();
ImGui::Text("Categories");
ImGui::TableNextColumn();
ImGui::Text("Systems");
if (newSongQuery.empty()) {
ImGui::TableNextRow(ImGuiTableRowFlags_Headers);
ImGui::TableNextColumn();
ImGui::Text("Categories");
ImGui::TableNextColumn();
ImGui::Text("Systems");
}
ImGui::TableNextRow();
// CATEGORIES
ImGui::TableNextColumn();
int index=0;
for (FurnaceGUISysCategory& i: sysCategories) {
if (ImGui::Selectable(i.name,newSongCategory==index,ImGuiSelectableFlags_DontClosePopups)) { \
newSongCategory=index;
if (newSongQuery.empty()) {
ImGui::TableNextColumn();
int index=0;
for (FurnaceGUISysCategory& i: sysCategories) {
if (ImGui::Selectable(i.name,newSongCategory==index,ImGuiSelectableFlags_DontClosePopups)) { \
newSongCategory=index;
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("%s",i.description);
}
index++;
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("%s",i.description);
}
index++;
}
// SYSTEMS
ImGui::TableNextColumn();
if (ImGui::BeginTable("Systems",1,ImGuiTableFlags_BordersInnerV|ImGuiTableFlags_ScrollY)) {
for (FurnaceGUISysDef& i: sysCategories[newSongCategory].systems) {
std::vector<FurnaceGUISysDef>& category=(newSongQuery.empty())?(sysCategories[newSongCategory].systems):(newSongSearchResults);
for (FurnaceGUISysDef& i: category) {
ImGui::TableNextRow();
ImGui::TableNextColumn();
if (ImGui::Selectable(i.name,false,ImGuiSelectableFlags_DontClosePopups)) {

113
src/gui/patManager.cpp Normal file
View file

@ -0,0 +1,113 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "gui.h"
#include "misc/cpp/imgui_stdlib.h"
#include "IconsFontAwesome4.h"
#include <imgui.h>
void FurnaceGUI::drawPatManager() {
if (nextWindow==GUI_WINDOW_PAT_MANAGER) {
patManagerOpen=true;
ImGui::SetNextWindowFocus();
nextWindow=GUI_WINDOW_NOTHING;
}
if (!patManagerOpen) return;
char id[1024];
unsigned char isUsed[256];
bool isNull[256];
if (ImGui::Begin("Pattern Manager",&patManagerOpen,globalWinFlags)) {
ImGui::Text("Global Tasks");
if (ImGui::Button("De-duplicate patterns")) {
e->lockEngine([this]() {
e->curSubSong->optimizePatterns();
});
}
ImGui::SameLine();
if (ImGui::Button("Re-arrange patterns")) {
e->lockEngine([this]() {
e->curSubSong->rearrangePatterns();
});
}
if (ImGui::BeginTable("PatManTable",257,ImGuiTableFlags_ScrollX|ImGuiTableFlags_SizingFixedFit)) {
ImGui::PushFont(patFont);
for (int i=0; i<e->getTotalChannelCount(); i++) {
ImGui::TableNextRow();
memset(isUsed,0,256);
memset(isNull,0,256*sizeof(bool));
for (int j=0; j<e->curSubSong->ordersLen; j++) {
isUsed[e->curSubSong->orders.ord[i][j]]++;
}
for (int j=0; j<256; j++) {
isNull[j]=(e->curSubSong->pat[i].data[j]==NULL);
}
ImGui::TableNextColumn();
ImGui::Text("%s",e->getChannelShortName(i));
ImGui::PushID(1000+i);
for (int k=0; k<256; k++) {
ImGui::TableNextColumn();
snprintf(id,1023,"%.2X",k);
if (isNull[k]) {
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PAT_MANAGER_NULL]);
} else if (isUsed[k]>=e->curSubSong->ordersLen) {
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PAT_MANAGER_COMBO_BREAKER]);
} else if (isUsed[k]>=0.7*(double)e->curSubSong->ordersLen) {
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PAT_MANAGER_EXTREMELY_OVERUSED]);
} else if (isUsed[k]>=0.4*(double)e->curSubSong->ordersLen) {
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PAT_MANAGER_OVERUSED]);
} else if (isUsed[k]) {
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PAT_MANAGER_USED]);
} else {
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PAT_MANAGER_UNUSED]);
}
ImGui::Selectable(id,isUsed[k]);
if (ImGui::IsItemHovered()) {
ImGui::PushFont(mainFont);
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_TEXT]);
if (isNull[k]) {
ImGui::SetTooltip("Pattern %.2X\n- not allocated",k);
} else {
ImGui::SetTooltip("Pattern %.2X\n- use count: %d (%.0f%%)\n\nright-click to erase",k,isUsed[k],100.0*(double)isUsed[k]/(double)e->curSubSong->ordersLen);
}
ImGui::PopStyleColor();
ImGui::PopFont();
}
if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) {
e->lockEngine([this,i,k]() {
delete e->curSubSong->pat[i].data[k];
e->curSubSong->pat[i].data[k]=NULL;
});
}
ImGui::PopStyleColor();
}
ImGui::PopID();
}
ImGui::PopFont();
ImGui::EndTable();
}
}
if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_PAT_MANAGER;
ImGui::End();
}

View file

@ -56,7 +56,7 @@ void FurnaceGUI::popPartBlend() {
// 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];
static char id[64];
bool selectedRow=(i>=sel1.y && i<=sel2.y && !inhibitSel);
ImGui::TableNextRow(0,lineHeight);
ImGui::TableNextColumn();
@ -114,9 +114,9 @@ inline void FurnaceGUI::patternRow(int i, bool isPlaying, float lineHeight, int
ImGui::PushStyleColor(ImGuiCol_Text,rowIndexColor);
if (settings.patRowsBase==1) {
snprintf(id,31," %.2X ##PR_%d",i,i);
snprintf(id,63," %.2X ##PR_%d",i,i);
} else {
snprintf(id,31,"%3d ##PR_%d",i,i);
snprintf(id,63,"%3d ##PR_%d",i,i);
}
ImGui::Selectable(id,false,ImGuiSelectableFlags_NoPadWithHalfSpacing,fourChars);
if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem)) {
@ -151,7 +151,7 @@ inline void FurnaceGUI::patternRow(int i, bool isPlaying, float lineHeight, int
bool cursorVol=(cursor.y==i && cursor.xCoarse==j && cursor.xFine==2 && curWindowLast==GUI_WINDOW_PATTERN);
// note
sprintf(id,"%s##PN_%d_%d",noteName(pat->data[i][0],pat->data[i][1]),i,j);
snprintf(id,63,"%.31s##PN_%d_%d",noteName(pat->data[i][0],pat->data[i][1]),i,j);
if (pat->data[i][0]==0 && pat->data[i][1]==0) {
ImGui::PushStyleColor(ImGuiCol_Text,inactiveColor);
} else {
@ -182,7 +182,7 @@ inline void FurnaceGUI::patternRow(int i, bool isPlaying, float lineHeight, int
// instrument
if (pat->data[i][2]==-1) {
ImGui::PushStyleColor(ImGuiCol_Text,inactiveColor);
sprintf(id,"..##PI_%d_%d",i,j);
snprintf(id,63,"%.31s##PI_%d_%d",emptyLabel2,i,j);
} else {
if (pat->data[i][2]<0 || pat->data[i][2]>=e->song.insLen) {
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_INS_ERROR]);
@ -194,7 +194,7 @@ inline void FurnaceGUI::patternRow(int i, bool isPlaying, float lineHeight, int
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_INS]);
}
}
sprintf(id,"%.2X##PI_%d_%d",pat->data[i][2],i,j);
snprintf(id,63,"%.2X##PI_%d_%d",pat->data[i][2],i,j);
}
ImGui::SameLine(0.0f,0.0f);
if (cursorIns) {
@ -221,13 +221,13 @@ inline void FurnaceGUI::patternRow(int i, bool isPlaying, float lineHeight, int
if (e->curSubSong->chanCollapse[j]<2) {
// volume
if (pat->data[i][3]==-1) {
sprintf(id,"..##PV_%d_%d",i,j);
snprintf(id,63,"%.31s##PV_%d_%d",emptyLabel2,i,j);
ImGui::PushStyleColor(ImGuiCol_Text,inactiveColor);
} else {
int volColor=(pat->data[i][3]*127)/chanVolMax;
if (volColor>127) volColor=127;
if (volColor<0) volColor=0;
sprintf(id,"%.2X##PV_%d_%d",pat->data[i][3],i,j);
snprintf(id,63,"%.2X##PV_%d_%d",pat->data[i][3],i,j);
ImGui::PushStyleColor(ImGuiCol_Text,volColors[volColor]);
}
ImGui::SameLine(0.0f,0.0f);
@ -263,15 +263,15 @@ inline void FurnaceGUI::patternRow(int i, bool isPlaying, float lineHeight, int
// effect
if (pat->data[i][index]==-1) {
sprintf(id,"..##PE%d_%d_%d",k,i,j);
snprintf(id,63,"%.31s##PE%d_%d_%d",emptyLabel2,k,i,j);
ImGui::PushStyleColor(ImGuiCol_Text,inactiveColor);
} else {
if (pat->data[i][index]>0xff) {
sprintf(id,"??##PE%d_%d_%d",k,i,j);
snprintf(id,63,"??##PE%d_%d_%d",k,i,j);
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_INVALID]);
} else {
const unsigned char data=pat->data[i][index];
sprintf(id,"%.2X##PE%d_%d_%d",data,k,i,j);
snprintf(id,63,"%.2X##PE%d_%d_%d",data,k,i,j);
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[fxColors[data]]);
}
}
@ -297,9 +297,9 @@ inline void FurnaceGUI::patternRow(int i, bool isPlaying, float lineHeight, int
// effect value
if (pat->data[i][index+1]==-1) {
sprintf(id,"..##PF%d_%d_%d",k,i,j);
snprintf(id,63,"%.31s##PF%d_%d_%d",emptyLabel2,k,i,j);
} else {
sprintf(id,"%.2X##PF%d_%d_%d",pat->data[i][index+1],k,i,j);
snprintf(id,63,"%.2X##PF%d_%d_%d",pat->data[i][index+1],k,i,j);
}
ImGui::SameLine(0.0f,0.0f);
if (cursorEffectVal) {
@ -701,6 +701,15 @@ void FurnaceGUI::drawPattern() {
if (i.cmd==DIV_CMD_SAMPLE_BANK) continue;
if (i.cmd==DIV_CMD_GET_VOLUME) continue;
if (i.cmd==DIV_ALWAYS_SET_VOLUME) continue;
if (i.cmd==DIV_CMD_HINT_VOLUME ||
i.cmd==DIV_CMD_HINT_PORTA ||
i.cmd==DIV_CMD_HINT_LEGATO ||
i.cmd==DIV_CMD_HINT_VOL_SLIDE ||
i.cmd==DIV_CMD_HINT_ARPEGGIO ||
i.cmd==DIV_CMD_HINT_PITCH ||
i.cmd==DIV_CMD_HINT_VIBRATO ||
i.cmd==DIV_CMD_HINT_VIBRATO_RANGE ||
i.cmd==DIV_CMD_HINT_VIBRATO_SHAPE) continue;
float width=patChanX[i.chan+1]-patChanX[i.chan];
float speedX=0.0f;

View file

@ -183,7 +183,7 @@ void PlotNoLerp(const char* label, const float* values, int values_count, int va
PlotNoLerpEx(ImGuiPlotType_Lines, label, &Plot_ArrayGetter, (void*)&data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size);
}
int PlotBitfieldEx(const char* label, int (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset, const char** overlay_text, int bits, ImVec2 frame_size)
int PlotBitfieldEx(const char* label, int (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset, const char** overlay_text, int bits, ImVec2 frame_size, const bool* values_highlight, ImVec4 highlightColor)
{
ImGuiContext& g = *GImGui;
ImGuiWindow* window = ImGui::GetCurrentWindow();
@ -253,7 +253,11 @@ int PlotBitfieldEx(const char* label, int (*values_getter)(void* data, int idx),
if (pos1.y <= pos0.y - 2.0f)
pos1.y += 1.0f;
if (v1&(1<<o)) {
window->DrawList->AddRectFilled(pos0, pos1, idx_hovered == v1_idx ? col_hovered : col_base);
ImU32 rCol=(idx_hovered == v1_idx ? col_hovered : col_base);
if (values_highlight!=NULL) {
if (values_highlight[v1_idx]) rCol=ImGui::GetColorU32(highlightColor);
}
window->DrawList->AddRectFilled(pos0, pos1, rCol);
}
}
tp0 = tp1;
@ -283,13 +287,13 @@ int PlotBitfieldEx(const char* label, int (*values_getter)(void* data, int idx),
return idx_hovered;
}
void PlotBitfield(const char* label, const int* values, int values_count, int values_offset, const char** overlay_text, int bits, ImVec2 graph_size, int stride)
void PlotBitfield(const char* label, const int* values, int values_count, int values_offset, const char** overlay_text, int bits, ImVec2 graph_size, int stride, const bool* values_highlight, ImVec4 highlightColor)
{
FurnacePlotIntArrayGetterData data(values, stride);
PlotBitfieldEx(label, &Plot_IntArrayGetter, (void*)&data, values_count, values_offset, overlay_text, bits, graph_size);
PlotBitfieldEx(label, &Plot_IntArrayGetter, (void*)&data, values_count, values_offset, overlay_text, bits, graph_size, values_highlight, highlightColor);
}
int PlotCustomEx(ImGuiPlotType plot_type, const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_display_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 frame_size, ImVec4 color, int highlight, std::string (*hoverFunc)(int,float), bool blockMode, std::string (*guideFunc)(float))
int PlotCustomEx(ImGuiPlotType plot_type, const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_display_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 frame_size, ImVec4 color, int highlight, std::string (*hoverFunc)(int,float), bool blockMode, std::string (*guideFunc)(float), const bool* values_highlight, ImVec4 highlightColor)
{
ImGuiContext& g = *GImGui;
ImGuiWindow* window = ImGui::GetCurrentWindow();
@ -413,7 +417,11 @@ int PlotCustomEx(ImGuiPlotType plot_type, const char* label, float (*values_gett
pos0.y-=(inner_bb.Max.y-inner_bb.Min.y)*inv_scale;
//pos1.y+=1.0f;
}
window->DrawList->AddRectFilled(pos0, pos1, idx_hovered == v1_idx ? col_hovered : col_base);
ImU32 rCol=(idx_hovered == v1_idx ? col_hovered : col_base);
if (values_highlight!=NULL) {
if (values_highlight[v1_idx]) rCol=ImGui::GetColorU32(highlightColor);
}
window->DrawList->AddRectFilled(pos0, pos1, rCol);
}
t0 = t1;
@ -451,8 +459,8 @@ int PlotCustomEx(ImGuiPlotType plot_type, const char* label, float (*values_gett
return idx_hovered;
}
void PlotCustom(const char* label, const float* values, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size, int stride, ImVec4 color, int highlight, std::string (*hoverFunc)(int,float), bool blockMode, std::string (*guideFunc)(float))
void PlotCustom(const char* label, const float* values, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size, int stride, ImVec4 color, int highlight, std::string (*hoverFunc)(int,float), bool blockMode, std::string (*guideFunc)(float), const bool* values_highlight, ImVec4 highlightColor)
{
FurnacePlotArrayGetterData data(values, stride);
PlotCustomEx(ImGuiPlotType_Histogram, label, &Plot_ArrayGetter, (void*)&data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size, color, highlight, hoverFunc, blockMode, guideFunc);
PlotCustomEx(ImGuiPlotType_Histogram, label, &Plot_ArrayGetter, (void*)&data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size, color, highlight, hoverFunc, blockMode, guideFunc, values_highlight, highlightColor);
}

View file

@ -21,5 +21,5 @@
#include <string>
void PlotNoLerp(const char* label, const float* values, int values_count, int values_offset = 0, const char* overlay_text = NULL, float scale_min = FLT_MAX, float scale_max = FLT_MAX, ImVec2 graph_size = ImVec2(0, 0), int stride = sizeof(float));
void PlotBitfield(const char* label, const int* values, int values_count, int values_offset = 0, const char** overlay_text = NULL, int bits = 8, ImVec2 graph_size = ImVec2(0, 0), int stride = sizeof(float));
void PlotCustom(const char* label, const float* values, int values_count, int values_offset = 0, const char* overlay_text = NULL, float scale_min = FLT_MAX, float scale_max = FLT_MAX, ImVec2 graph_size = ImVec2(0, 0), int stride = sizeof(float), ImVec4 fgColor = ImVec4(1.0f,1.0f,1.0f,1.0f), int highlight = 0, std::string (*hoverFunc)(int,float) = NULL, bool blockMode=false, std::string (*guideFunc)(float) = NULL);
void PlotBitfield(const char* label, const int* values, int values_count, int values_offset = 0, const char** overlay_text = NULL, int bits = 8, ImVec2 graph_size = ImVec2(0, 0), int stride = sizeof(float), const bool* values_highlight = NULL, ImVec4 highlightColor = ImVec4(1.0f,1.0f,1.0f,1.0f));
void PlotCustom(const char* label, const float* values, int values_count, int values_offset = 0, const char* overlay_text = NULL, float scale_min = FLT_MAX, float scale_max = FLT_MAX, ImVec2 graph_size = ImVec2(0, 0), int stride = sizeof(float), ImVec4 fgColor = ImVec4(1.0f,1.0f,1.0f,1.0f), int highlight = 0, std::string (*hoverFunc)(int,float) = NULL, bool blockMode=false, std::string (*guideFunc)(float) = NULL, const bool* values_highlight = NULL, ImVec4 highlightColor = ImVec4(1.0f,1.0f,1.0f,1.0f));

View file

@ -854,6 +854,41 @@ void FurnaceGUI::initSystemPresets() {
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"MSX + Neotron", {
DIV_SYSTEM_AY8910, 64, 0, 16,
DIV_SYSTEM_YM2610_FULL, 64, 0, 0,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"MSX + Neotron (extended channel 2)", {
DIV_SYSTEM_AY8910, 64, 0, 16,
DIV_SYSTEM_YM2610_FULL_EXT, 64, 0, 0,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"MSX + Neotron (with YM2610B)", {
DIV_SYSTEM_AY8910, 64, 0, 16,
DIV_SYSTEM_YM2610B, 64, 0, 0,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"MSX + Neotron (with YM2610B; extended channel 3)", {
DIV_SYSTEM_AY8910, 64, 0, 16,
DIV_SYSTEM_YM2610B_EXT, 64, 0, 0,
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"MSX + SIMPL", {
DIV_SYSTEM_AY8910, 64, 0, 16,
DIV_SYSTEM_PCM_DAC, 64, 0, 55929|(7<<16), // variable rate, Mono DAC
0
}
));
cat.systems.push_back(FurnaceGUISysDef(
"NEC PC-98 (with PC-9801-26/K)", {
DIV_SYSTEM_OPN, 64, 0, 4, // 3.9936MHz but some compatible card has 4MHz

View file

@ -21,6 +21,7 @@
#include "fonts.h"
#include "../ta-log.h"
#include "../fileutils.h"
#include "../utfutils.h"
#include "util.h"
#include "guiConst.h"
#include "intConst.h"
@ -513,6 +514,11 @@ void FurnaceGUI::drawSettings() {
settings.blankIns=blankInsB;
}
bool saveUnusedPatternsB=settings.saveUnusedPatterns;
if (ImGui::Checkbox("Save unused patterns",&saveUnusedPatternsB)) {
settings.saveUnusedPatterns=saveUnusedPatternsB;
}
ImGui::Text("Note preview behavior:");
if (ImGui::RadioButton("Never##npb0",settings.notePreviewBehavior==0)) {
settings.notePreviewBehavior=0;
@ -1079,6 +1085,41 @@ void FurnaceGUI::drawSettings() {
);
}
bool loadChineseTraditionalB=settings.loadChineseTraditional;
if (ImGui::Checkbox("Display Chinese (Traditional) characters",&loadChineseTraditionalB)) {
settings.loadChineseTraditional=loadChineseTraditionalB;
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip(
"Only toggle this option if you have enough graphics memory.\n"
"This is a temporary solution until dynamic font atlas is implemented in Dear ImGui.\n\n"
"請在確保你有足夠的顯存后再啟動此設定\n"
"這是一個在ImGui實現動態字體加載之前的臨時解決方案"
);
}
bool loadKoreanB=settings.loadKorean;
if (ImGui::Checkbox("Display Korean characters",&loadKoreanB)) {
settings.loadKorean=loadKoreanB;
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip(
"Only toggle this option if you have enough graphics memory.\n"
"This is a temporary solution until dynamic font atlas is implemented in Dear ImGui.\n\n"
"그래픽 메모리가 충분한 경우에만 이 옵션을 선택하십시오.\n"
"이 옵션은 Dear ImGui에 동적 글꼴 아틀라스가 구현될 때까지 임시 솔루션입니다."
);
}
ImGui::Separator();
ImGui::Text("Pattern view labels:");
ImGui::InputTextWithHint("Note off (3-char)","OFF",&settings.noteOffLabel);
ImGui::InputTextWithHint("Note release (3-char)","===",&settings.noteRelLabel);
ImGui::InputTextWithHint("Macro release (3-char)","REL",&settings.macroRelLabel);
ImGui::InputTextWithHint("Empty field (3-char)","...",&settings.emptyLabel);
ImGui::InputTextWithHint("Empty field (2-char)","..",&settings.emptyLabel2);
ImGui::Separator();
ImGui::Text("Orders row number format:");
@ -1207,6 +1248,79 @@ void FurnaceGUI::drawSettings() {
ImGui::Separator();
ImGui::Text("Channel colors:");
if (ImGui::RadioButton("Single##CHC0",settings.channelColors==0)) {
settings.channelColors=0;
}
if (ImGui::RadioButton("Channel type##CHC1",settings.channelColors==1)) {
settings.channelColors=1;
}
if (ImGui::RadioButton("Instrument type##CHC2",settings.channelColors==2)) {
settings.channelColors=2;
}
ImGui::Text("Channel name colors:");
if (ImGui::RadioButton("Single##CTC0",settings.channelColors==0)) {
settings.channelColors=0;
}
if (ImGui::RadioButton("Channel type##CTC1",settings.channelColors==1)) {
settings.channelColors=1;
}
if (ImGui::RadioButton("Instrument type##CTC2",settings.channelColors==2)) {
settings.channelColors=2;
}
ImGui::Text("Channel style:");
if (ImGui::RadioButton("Classic##CHS0",settings.channelStyle==0)) {
settings.channelStyle=0;
}
if (ImGui::RadioButton("Line##CHS1",settings.channelStyle==1)) {
settings.channelStyle=1;
}
if (ImGui::RadioButton("Round##CHS2",settings.channelStyle==2)) {
settings.channelStyle=2;
}
if (ImGui::RadioButton("Split button##CHS3",settings.channelStyle==3)) {
settings.channelStyle=3;
}
if (ImGui::RadioButton("Square border##CH42",settings.channelStyle==4)) {
settings.channelStyle=4;
}
if (ImGui::RadioButton("Round border##CHS5",settings.channelStyle==5)) {
settings.channelStyle=5;
}
ImGui::Text("Channel volume bar:");
if (ImGui::RadioButton("None##CHV0",settings.channelVolStyle==0)) {
settings.channelVolStyle=0;
}
if (ImGui::RadioButton("Simple##CHV1",settings.channelVolStyle==1)) {
settings.channelVolStyle=1;
}
if (ImGui::RadioButton("Stereo##CHV2",settings.channelVolStyle==2)) {
settings.channelVolStyle=2;
}
if (ImGui::RadioButton("Real##CHV3",settings.channelVolStyle==3)) {
settings.channelVolStyle=3;
}
ImGui::Text("Channel feedback style:");
if (ImGui::RadioButton("Off##CHF0",settings.channelFeedbackStyle==0)) {
settings.channelFeedbackStyle=0;
}
if (ImGui::RadioButton("Note##CHF1",settings.channelFeedbackStyle==1)) {
settings.channelFeedbackStyle=1;
}
if (ImGui::RadioButton("Volume##CHF2",settings.channelFeedbackStyle==2)) {
settings.channelFeedbackStyle=2;
}
if (ImGui::RadioButton("Active##CHF3",settings.channelFeedbackStyle==3)) {
settings.channelFeedbackStyle=3;
}
ImGui::Separator();
bool insEditColorizeB=settings.insEditColorize;
if (ImGui::Checkbox("Colorize instrument editor using instrument type",&insEditColorizeB)) {
settings.insEditColorize=insEditColorizeB;
@ -1577,6 +1691,15 @@ void FurnaceGUI::drawSettings() {
UI_COLOR_CONFIG(GUI_COLOR_EE_VALUE,"External command output");
ImGui::TreePop();
}
if (ImGui::TreeNode("Pattern Manager")) {
UI_COLOR_CONFIG(GUI_COLOR_PAT_MANAGER_NULL,"Unallocated");
UI_COLOR_CONFIG(GUI_COLOR_PAT_MANAGER_UNUSED,"Unused");
UI_COLOR_CONFIG(GUI_COLOR_PAT_MANAGER_USED,"Used");
UI_COLOR_CONFIG(GUI_COLOR_PAT_MANAGER_OVERUSED,"Overused");
UI_COLOR_CONFIG(GUI_COLOR_PAT_MANAGER_EXTREMELY_OVERUSED,"Really overused");
UI_COLOR_CONFIG(GUI_COLOR_PAT_MANAGER_COMBO_BREAKER,"Combo Breaker");
ImGui::TreePop();
}
if (ImGui::TreeNode("Piano")) {
UI_COLOR_CONFIG(GUI_COLOR_PIANO_BACKGROUND,"Background");
UI_COLOR_CONFIG(GUI_COLOR_PIANO_KEY_TOP,"Upper key");
@ -2083,6 +2206,8 @@ void FurnaceGUI::syncSettings() {
settings.roundedMenus=e->getConfInt("roundedMenus",0);
settings.loadJapanese=e->getConfInt("loadJapanese",0);
settings.loadChinese=e->getConfInt("loadChinese",0);
settings.loadChineseTraditional=e->getConfInt("loadChineseTraditional",0);
settings.loadKorean=e->getConfInt("loadKorean",0);
settings.fmLayout=e->getConfInt("fmLayout",0);
settings.sampleLayout=e->getConfInt("sampleLayout",0);
settings.waveLayout=e->getConfInt("waveLayout",0);
@ -2127,6 +2252,17 @@ void FurnaceGUI::syncSettings() {
settings.noThreadedInput=e->getConfInt("noThreadedInput",0);
settings.initialSysName=e->getConfString("initialSysName","");
settings.clampSamples=e->getConfInt("clampSamples",0);
settings.noteOffLabel=e->getConfString("noteOffLabel","OFF");
settings.noteRelLabel=e->getConfString("noteRelLabel","===");
settings.macroRelLabel=e->getConfString("macroRelLabel","REL");
settings.emptyLabel=e->getConfString("emptyLabel","...");
settings.emptyLabel2=e->getConfString("emptyLabel2","..");
settings.saveUnusedPatterns=e->getConfInt("saveUnusedPatterns",0);
settings.channelColors=e->getConfInt("channelColors",1);
settings.channelTextColors=e->getConfInt("channelTextColors",0);
settings.channelStyle=e->getConfInt("channelStyle",0);
settings.channelVolStyle=e->getConfInt("channelVolStyle",0);
settings.channelFeedbackStyle=e->getConfInt("channelFeedbackStyle",1);
clampSetting(settings.mainFontSize,2,96);
clampSetting(settings.patFontSize,2,96);
@ -2176,6 +2312,8 @@ void FurnaceGUI::syncSettings() {
clampSetting(settings.roundedMenus,0,1);
clampSetting(settings.loadJapanese,0,1);
clampSetting(settings.loadChinese,0,1);
clampSetting(settings.loadChineseTraditional,0,1);
clampSetting(settings.loadKorean,0,1);
clampSetting(settings.fmLayout,0,6);
clampSetting(settings.susPosition,0,1);
clampSetting(settings.effectCursorDir,0,2);
@ -2215,6 +2353,12 @@ void FurnaceGUI::syncSettings() {
clampSetting(settings.unsignedDetune,0,1);
clampSetting(settings.noThreadedInput,0,1);
clampSetting(settings.clampSamples,0,1);
clampSetting(settings.saveUnusedPatterns,0,1);
clampSetting(settings.channelColors,0,2);
clampSetting(settings.channelTextColors,0,2);
clampSetting(settings.channelStyle,0,5);
clampSetting(settings.channelVolStyle,0,3);
clampSetting(settings.channelFeedbackStyle,0,3);
settings.initialSys=e->decodeSysDesc(e->getConfString("initialSys",""));
if (settings.initialSys.size()<4) {
@ -2309,6 +2453,8 @@ void FurnaceGUI::commitSettings() {
e->setConf("roundedMenus",settings.roundedMenus);
e->setConf("loadJapanese",settings.loadJapanese);
e->setConf("loadChinese",settings.loadChinese);
e->setConf("loadChineseTraditional",settings.loadChineseTraditional);
e->setConf("loadKorean",settings.loadKorean);
e->setConf("fmLayout",settings.fmLayout);
e->setConf("sampleLayout",settings.sampleLayout);
e->setConf("waveLayout",settings.waveLayout);
@ -2354,6 +2500,17 @@ void FurnaceGUI::commitSettings() {
e->setConf("unsignedDetune",settings.unsignedDetune);
e->setConf("noThreadedInput",settings.noThreadedInput);
e->setConf("clampSamples",settings.clampSamples);
e->setConf("noteOffLabel",settings.noteOffLabel);
e->setConf("noteRelLabel",settings.noteRelLabel);
e->setConf("macroRelLabel",settings.macroRelLabel);
e->setConf("emptyLabel",settings.emptyLabel);
e->setConf("emptyLabel2",settings.emptyLabel2);
e->setConf("saveUnusedPatterns",settings.saveUnusedPatterns);
e->setConf("channelColors",settings.channelColors);
e->setConf("channelTextColors",settings.channelTextColors);
e->setConf("channelStyle",settings.channelStyle);
e->setConf("channelVolStyle",settings.channelVolStyle);
e->setConf("channelFeedbackStyle",settings.channelFeedbackStyle);
// colors
for (int i=0; i<GUI_COLOR_MAX; i++) {
@ -2766,6 +2923,20 @@ void FurnaceGUI::popAccentColors() {
#define SYSTEM_PAT_FONT_PATH_3 "/usr/share/fonts/ubuntu/UbuntuMono-R.ttf"
#endif
void setupLabel(const char* lStr, char* label, int len) {
memset(label,0,32);
for (int i=0, p=0; i<len; i++) {
signed char cl;
if (lStr[p]==0) {
strncat(label," ",32);
} else {
decodeUTF8((const unsigned char*)&lStr[p],cl);
memcpy(label+p,lStr+p,cl);
p+=cl;
}
}
}
void FurnaceGUI::applyUISettings(bool updateFonts) {
ImGuiStyle sty;
if (settings.guiColorsBase) {
@ -2774,6 +2945,12 @@ void FurnaceGUI::applyUISettings(bool updateFonts) {
ImGui::StyleColorsDark(&sty);
}
setupLabel(settings.noteOffLabel.c_str(),noteOffLabel,3);
setupLabel(settings.noteRelLabel.c_str(),noteRelLabel,3);
setupLabel(settings.macroRelLabel.c_str(),macroRelLabel,3);
setupLabel(settings.emptyLabel.c_str(),emptyLabel,3);
setupLabel(settings.emptyLabel2.c_str(),emptyLabel2,2);
if (settings.dpiScale>=0.5f) dpiScale=settings.dpiScale;
// colors
@ -2928,6 +3105,12 @@ void FurnaceGUI::applyUISettings(bool updateFonts) {
if (settings.loadChinese) {
range.AddRanges(ImGui::GetIO().Fonts->GetGlyphRangesChineseSimplifiedCommon());
}
if (settings.loadChineseTraditional) {
range.AddRanges(ImGui::GetIO().Fonts->GetGlyphRangesChineseFull());
}
if (settings.loadKorean) {
range.AddRanges(ImGui::GetIO().Fonts->GetGlyphRangesKorean());
}
// I'm terribly sorry
range.UsedChars[0x80>>5]=0;

View file

@ -195,7 +195,7 @@ void FurnaceGUI::drawSongInfo() {
float setHz=tempoView?e->curSubSong->hz*2.5:e->curSubSong->hz;
if (ImGui::InputFloat("##Rate",&setHz,1.0f,1.0f,"%g")) { MARK_MODIFIED
if (tempoView) setHz/=2.5;
if (setHz<10) setHz=10;
if (setHz<1) setHz=1;
if (setHz>999) setHz=999;
e->setSongRate(setHz,setHz<52);
}

View file

@ -135,13 +135,20 @@ void FurnaceGUI::drawSysConf(int chan, DivSystem type, unsigned int& flags, bool
if (ImGui::RadioButton("5.95MHz (PAL)",(flags&3)==1)) {
copyOfFlags=(flags&(~3))|1;
}
ImGui::Text("Chip revision (sample memory):");
if (ImGui::RadioButton("A/B/E (8K)",(flags&16)==0)) {
ImGui::Text("Sample memory:");
if (ImGui::RadioButton("8K (rev A/B/E)",(flags&16)==0)) {
copyOfFlags=(flags&(~16))|0;
}
if (ImGui::RadioButton("D/F (64K)",(flags&16)==16)) {
if (ImGui::RadioButton("64K (rev D/F)",(flags&16)==16)) {
copyOfFlags=(flags&(~16))|16;
}
ImGui::Text("DAC resolution");
if (ImGui::RadioButton("16-bit (rev A/B/D/F)",(flags&32)==0)) {
copyOfFlags=(flags&(~32))|0;
}
if (ImGui::RadioButton("1-bit PDM (rev C/E)",(flags&32)==32)) {
copyOfFlags=(flags&(~32))|32;
}
bool echo=flags&4;
if (ImGui::Checkbox("Enable echo",&echo)) {
copyOfFlags=(flags&(~4))|(echo<<2);

124
src/gui/sysManager.cpp Normal file
View file

@ -0,0 +1,124 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "gui.h"
#include "misc/cpp/imgui_stdlib.h"
#include "IconsFontAwesome4.h"
#include <fmt/printf.h>
#include <imgui.h>
void FurnaceGUI::drawSysManager() {
if (nextWindow==GUI_WINDOW_SYS_MANAGER) {
sysManagerOpen=true;
ImGui::SetNextWindowFocus();
nextWindow=GUI_WINDOW_NOTHING;
}
if (!sysManagerOpen) return;
if (ImGui::Begin("Chip Manager",&sysManagerOpen,globalWinFlags)) {
ImGui::Checkbox("Preserve channel order",&preserveChanPos);
if (ImGui::BeginTable("SystemList",3)) {
ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthFixed);
ImGui::TableSetupColumn("c2",ImGuiTableColumnFlags_WidthStretch);
ImGui::TableSetupColumn("c3",ImGuiTableColumnFlags_WidthFixed);
ImGui::TableNextRow(ImGuiTableRowFlags_Headers);
ImGui::TableNextColumn();
ImGui::TableNextColumn();
ImGui::Text("Name");
ImGui::TableNextColumn();
ImGui::Text("Actions");
for (unsigned char i=0; i<e->song.systemLen; i++) {
ImGui::PushID(i);
ImGui::TableNextRow();
ImGui::TableNextColumn();
if (ImGui::Button(ICON_FA_ARROWS)) {
}
if (ImGui::BeginDragDropSource()) {
sysToMove=i;
ImGui::SetDragDropPayload("FUR_SYS",NULL,0,ImGuiCond_Once);
ImGui::Button(ICON_FA_ARROWS "##SysDrag");
ImGui::EndDragDropSource();
} else if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("(drag to swap chips)");
}
if (ImGui::BeginDragDropTarget()) {
const ImGuiPayload* dragItem=ImGui::AcceptDragDropPayload("FUR_SYS");
if (dragItem!=NULL) {
if (dragItem->IsDataType("FUR_SYS")) {
if (sysToMove!=i && sysToMove>=0) {
e->swapSystem(sysToMove,i,preserveChanPos);
}
sysToMove=-1;
}
}
ImGui::EndDragDropTarget();
}
ImGui::TableNextColumn();
if (ImGui::TreeNode(fmt::sprintf("%d. %s##_SYSM%d",i+1,getSystemName(e->song.system[i]),i).c_str())) {
drawSysConf(i,e->song.system[i],e->song.systemFlags[i],true);
ImGui::TreePop();
}
ImGui::TableNextColumn();
ImGui::Button(ICON_FA_CHEVRON_DOWN "##SysChange");
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Change");
}
if (ImGui::BeginPopupContextItem("SysPickerC",ImGuiPopupFlags_MouseButtonLeft)) {
DivSystem picked=systemPicker();
if (picked!=DIV_SYSTEM_NULL) {
e->changeSystem(i,picked,preserveChanPos);
updateWindowTitle();
ImGui::CloseCurrentPopup();
}
ImGui::EndPopup();
}
ImGui::SameLine();
ImGui::BeginDisabled(e->song.systemLen<=1);
if (ImGui::Button(ICON_FA_TIMES "##SysRemove")) {
sysToDelete=i;
showWarning("Are you sure you want to remove this chip?",GUI_WARN_SYSTEM_DEL);
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Remove");
}
ImGui::EndDisabled();
ImGui::PopID();
}
if (e->song.systemLen<32) {
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::TableNextColumn();
ImGui::Button(ICON_FA_PLUS "##SysAdd");
if (ImGui::BeginPopupContextItem("SysPickerA",ImGuiPopupFlags_MouseButtonLeft)) {
DivSystem picked=systemPicker();
if (picked!=DIV_SYSTEM_NULL) {
if (!e->addSystem(picked)) {
showError("cannot add chip! ("+e->getLastError()+")");
}
updateWindowTitle();
ImGui::CloseCurrentPopup();
}
ImGui::EndPopup();
}
}
ImGui::EndTable();
}
}
if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_SYS_MANAGER;
ImGui::End();
}

97
src/gui/sysPicker.cpp Normal file
View file

@ -0,0 +1,97 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "gui.h"
#include "misc/cpp/imgui_stdlib.h"
#include "IconsFontAwesome4.h"
#include "guiConst.h"
#include <imgui.h>
DivSystem FurnaceGUI::systemPicker() {
DivSystem ret=DIV_SYSTEM_NULL;
DivSystem hoveredSys=DIV_SYSTEM_NULL;
bool reissueSearch=false;
if (curSysSection==NULL) {
curSysSection=availableSystems;
}
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
if (ImGui::InputTextWithHint("##SysSearch","Search...",&sysSearchQuery)) reissueSearch=true;
if (ImGui::BeginTabBar("SysCats")) {
for (int i=0; chipCategories[i]; i++) {
if (ImGui::BeginTabItem(chipCategoryNames[i])) {
if (ImGui::IsItemActive()) {
reissueSearch=true;
}
curSysSection=chipCategories[i];
ImGui::EndTabItem();
}
}
ImGui::EndTabBar();
}
if (reissueSearch) {
String lowerCase=sysSearchQuery;
for (char& i: lowerCase) {
if (i>='A' && i<='Z') i+='a'-'A';
}
sysSearchResults.clear();
for (int j=0; curSysSection[j]; j++) {
String lowerCase1=e->getSystemName((DivSystem)curSysSection[j]);
for (char& i: lowerCase1) {
if (i>='A' && i<='Z') i+='a'-'A';
}
if (lowerCase1.find(lowerCase)!=String::npos) {
sysSearchResults.push_back((DivSystem)curSysSection[j]);
}
}
}
if (ImGui::BeginTable("SysList",1,ImGuiTableFlags_ScrollY,ImVec2(500.0f*dpiScale,200.0*dpiScale))) {
if (sysSearchQuery.empty()) {
// display chip list
for (int j=0; curSysSection[j]; j++) {
ImGui::TableNextRow();
ImGui::TableNextColumn();
if (ImGui::Selectable(e->getSystemName((DivSystem)curSysSection[j]),false,0,ImVec2(500.0f*dpiScale,0.0f))) ret=(DivSystem)curSysSection[j];
if (ImGui::IsItemHovered()) {
hoveredSys=(DivSystem)curSysSection[j];
}
}
} else {
// display search results
for (DivSystem i: sysSearchResults) {
ImGui::TableNextRow();
ImGui::TableNextColumn();
if (ImGui::Selectable(e->getSystemName(i),false,0,ImVec2(500.0f*dpiScale,0.0f))) ret=i;
if (ImGui::IsItemHovered()) {
hoveredSys=i;
}
}
}
ImGui::EndTable();
}
ImGui::Separator();
if (ImGui::BeginChild("SysDesc",ImVec2(0.0f,150.0f*dpiScale),false,ImGuiWindowFlags_NoScrollbar|ImGuiWindowFlags_NoScrollWithMouse)) {
if (hoveredSys!=DIV_SYSTEM_NULL) {
const DivSysDef* sysDef=e->getSystemDef(hoveredSys);
ImGui::TextWrapped("%s",sysDef->description);
}
}
ImGui::EndChild();
return ret;
}

View file

@ -131,9 +131,8 @@ void FurnaceGUI::drawWaveEdit() {
if (curWave>=(int)e->song.wave.size()) curWave=e->song.wave.size()-1;
}
ImGui::SameLine();
// TODO: load replace
if (ImGui::Button(ICON_FA_FOLDER_OPEN "##WELoad")) {
doAction(GUI_ACTION_WAVE_LIST_OPEN);
doAction(GUI_ACTION_WAVE_LIST_OPEN_REPLACE);
}
ImGui::SameLine();
if (ImGui::Button(ICON_FA_FLOPPY_O "##WESave")) {