Merge branch 'master' into master
This commit is contained in:
commit
eb95024fb9
321 changed files with 78416 additions and 69816 deletions
|
|
@ -35,6 +35,7 @@ const char* aboutLine[]={
|
|||
_N("-- program --"),
|
||||
"tildearrow",
|
||||
_N("A M 4 N (intro tune)"),
|
||||
"Adam Lederer",
|
||||
"akumanatt",
|
||||
"cam900",
|
||||
"djtuBIG-MaliceX",
|
||||
|
|
@ -75,7 +76,7 @@ const char* aboutLine[]={
|
|||
"Polski: freq-mod, PoznańskiSzybkowiec",
|
||||
"Português (Brasil): Kagamiin~",
|
||||
"Русский: Background2982, LTVA",
|
||||
"Slovenčina: Mr. Hassium",
|
||||
"Slovenčina: Wegfrei",
|
||||
"Svenska: RevvoBolt",
|
||||
"ไทย: akumanatt",
|
||||
"",
|
||||
|
|
@ -180,6 +181,7 @@ const char* aboutLine[]={
|
|||
"Slightly Large NC",
|
||||
"smaybius",
|
||||
"SnugglyBun",
|
||||
"Someone64",
|
||||
"Spinning Square Waves",
|
||||
"src3453",
|
||||
"SuperJet Spade",
|
||||
|
|
@ -205,6 +207,7 @@ const char* aboutLine[]={
|
|||
"Ultraprogramer",
|
||||
"UserSniper",
|
||||
"Weeppiko",
|
||||
"Wegfrei",
|
||||
"Xan",
|
||||
"Yuzu4K",
|
||||
"Zabir",
|
||||
|
|
@ -299,6 +302,7 @@ const char* aboutLine[]={
|
|||
_N("NDS sound emulator by cam900"),
|
||||
"",
|
||||
_N("greetings to:"),
|
||||
"floxy!",
|
||||
"NEOART Costa Rica",
|
||||
"Xenium Demoparty",
|
||||
"@party",
|
||||
|
|
|
|||
|
|
@ -161,6 +161,8 @@ void FurnaceGUI::drawCSPlayer() {
|
|||
ImGui::TableNextColumn();
|
||||
ImGui::Text(_("vols"));
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text(_("volst"));
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text(_("vib"));
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text(_("porta"));
|
||||
|
|
@ -189,6 +191,8 @@ void FurnaceGUI::drawCSPlayer() {
|
|||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%+d",state->volSpeed);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%+d",state->volSpeedTarget);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%d/%d (%d)",state->vibratoDepth,state->vibratoRate,state->vibratoPos);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("-> %d (%d)",state->portaTarget,state->portaSpeed);
|
||||
|
|
|
|||
|
|
@ -69,6 +69,9 @@ void FurnaceGUI::startSelection(int xCoarse, int xFine, int y, bool fullRow) {
|
|||
selStart.y=y;
|
||||
selEnd.y=y;
|
||||
} else {
|
||||
if (xCoarse!=cursor.xCoarse || y!=cursor.y) {
|
||||
makeCursorUndo();
|
||||
}
|
||||
cursor.xCoarse=xCoarse;
|
||||
cursor.xFine=xFine;
|
||||
cursor.y=y;
|
||||
|
|
@ -208,6 +211,9 @@ void FurnaceGUI::finishSelection() {
|
|||
}
|
||||
|
||||
void FurnaceGUI::moveCursor(int x, int y, bool select) {
|
||||
if (y>=editStepCoarse || y<=-editStepCoarse || x<=-5 || x>=5 ) {
|
||||
makeCursorUndo();
|
||||
}
|
||||
if (!select) {
|
||||
finishSelection();
|
||||
}
|
||||
|
|
@ -326,6 +332,7 @@ void FurnaceGUI::moveCursor(int x, int y, bool select) {
|
|||
}
|
||||
|
||||
void FurnaceGUI::moveCursorPrevChannel(bool overflow) {
|
||||
makeCursorUndo();
|
||||
finishSelection();
|
||||
curNibble=false;
|
||||
|
||||
|
|
@ -354,6 +361,7 @@ void FurnaceGUI::moveCursorPrevChannel(bool overflow) {
|
|||
}
|
||||
|
||||
void FurnaceGUI::moveCursorNextChannel(bool overflow) {
|
||||
makeCursorUndo();
|
||||
finishSelection();
|
||||
curNibble=false;
|
||||
|
||||
|
|
@ -382,6 +390,7 @@ void FurnaceGUI::moveCursorNextChannel(bool overflow) {
|
|||
}
|
||||
|
||||
void FurnaceGUI::moveCursorTop(bool select) {
|
||||
makeCursorUndo();
|
||||
if (!select) {
|
||||
finishSelection();
|
||||
}
|
||||
|
|
@ -403,6 +412,7 @@ void FurnaceGUI::moveCursorTop(bool select) {
|
|||
}
|
||||
|
||||
void FurnaceGUI::moveCursorBottom(bool select) {
|
||||
makeCursorUndo();
|
||||
if (!select) {
|
||||
finishSelection();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,6 +36,8 @@ static float oscDebugMax=1.0;
|
|||
static float oscDebugPower=1.0;
|
||||
static int oscDebugRepeat=1;
|
||||
static int numApples=1;
|
||||
static int getGainChan=0;
|
||||
static int getGainVol=0;
|
||||
|
||||
static void _drawOsc(const ImDrawList* drawList, const ImDrawCmd* cmd) {
|
||||
if (cmd!=NULL) {
|
||||
|
|
@ -143,6 +145,7 @@ void FurnaceGUI::drawDebug() {
|
|||
ImGui::Text("- portaNote = %d",ch->portaNote);
|
||||
ImGui::Text("- volume = %.4x",ch->volume);
|
||||
ImGui::Text("- volSpeed = %d",ch->volSpeed);
|
||||
ImGui::Text("- volSpeedTarget = %d",ch->volSpeedTarget);
|
||||
ImGui::Text("- cut = %d",ch->cut);
|
||||
ImGui::Text("- rowDelay = %d",ch->rowDelay);
|
||||
ImGui::Text("- volMax = %.4x",ch->volMax);
|
||||
|
|
@ -721,6 +724,32 @@ void FurnaceGUI::drawDebug() {
|
|||
ImGui::TreePop();
|
||||
}
|
||||
#endif
|
||||
if (ImGui::TreeNode("Get Gain Test")) {
|
||||
float realVol=e->getGain(getGainChan,getGainVol);
|
||||
ImGui::InputInt("Chan",&getGainChan);
|
||||
ImGui::InputInt("Vol",&getGainVol);
|
||||
ImGui::Text("result: %.0f%%",realVol*100.0f);
|
||||
ImGui::TreePop();
|
||||
}
|
||||
if (ImGui::TreeNode("Cursor Undo Debug")) {
|
||||
auto DrawSpot=[&](const CursorJumpPoint& spot) {
|
||||
ImGui::Text("[%d:%d] <%d:%d, %d>", spot.subSong, spot.order, spot.point.xCoarse, spot.point.xFine, spot.point.y);
|
||||
};
|
||||
if (ImGui::BeginChild("##CursorUndoDebugChild", ImVec2(0, 300), true)) {
|
||||
if (ImGui::BeginTable("##CursorUndoDebug", 2, ImGuiTableFlags_Borders|ImGuiTableFlags_SizingStretchSame)) {
|
||||
for (size_t row=0; row<MAX(cursorUndoHist.size(),cursorRedoHist.size()); ++row) {
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
if (row<cursorUndoHist.size()) DrawSpot(cursorUndoHist[cursorUndoHist.size()-row-1]);
|
||||
ImGui::TableNextColumn();
|
||||
if (row<cursorRedoHist.size()) DrawSpot(cursorRedoHist[cursorRedoHist.size()-row-1]);
|
||||
}
|
||||
ImGui::EndTable();
|
||||
}
|
||||
}
|
||||
ImGui::EndChild();
|
||||
ImGui::TreePop();
|
||||
}
|
||||
if (ImGui::TreeNode("User Interface")) {
|
||||
if (ImGui::Button("Inspect")) {
|
||||
inspectorOpen=!inspectorOpen;
|
||||
|
|
|
|||
|
|
@ -73,6 +73,8 @@ void FurnaceGUI::doAction(int what) {
|
|||
case GUI_ACTION_UNDO:
|
||||
if (curWindow==GUI_WINDOW_SAMPLE_EDIT) {
|
||||
doUndoSample();
|
||||
} else if (curWindow==GUI_WINDOW_INS_EDIT) {
|
||||
doUndoInstrument();
|
||||
} else {
|
||||
doUndo();
|
||||
}
|
||||
|
|
@ -80,6 +82,8 @@ void FurnaceGUI::doAction(int what) {
|
|||
case GUI_ACTION_REDO:
|
||||
if (curWindow==GUI_WINDOW_SAMPLE_EDIT) {
|
||||
doRedoSample();
|
||||
} else if (curWindow==GUI_WINDOW_INS_EDIT) {
|
||||
doRedoInstrument();
|
||||
} else {
|
||||
doRedo();
|
||||
}
|
||||
|
|
@ -123,16 +127,16 @@ void FurnaceGUI::doAction(int what) {
|
|||
pendingStepUpdate=1;
|
||||
break;
|
||||
case GUI_ACTION_OCTAVE_UP:
|
||||
if (++curOctave>7) {
|
||||
curOctave=7;
|
||||
if (++curOctave>GUI_EDIT_OCTAVE_MAX) {
|
||||
curOctave=GUI_EDIT_OCTAVE_MAX;
|
||||
} else {
|
||||
e->autoNoteOffAll();
|
||||
failedNoteOn=false;
|
||||
}
|
||||
break;
|
||||
case GUI_ACTION_OCTAVE_DOWN:
|
||||
if (--curOctave<-5) {
|
||||
curOctave=-5;
|
||||
if (--curOctave<GUI_EDIT_OCTAVE_MIN) {
|
||||
curOctave=GUI_EDIT_OCTAVE_MIN;
|
||||
} else {
|
||||
e->autoNoteOffAll();
|
||||
failedNoteOn=false;
|
||||
|
|
@ -676,6 +680,15 @@ void FurnaceGUI::doAction(int what) {
|
|||
latchTarget=0;
|
||||
latchNibble=false;
|
||||
break;
|
||||
case GUI_ACTION_PAT_ABSORB_INSTRUMENT:
|
||||
doAbsorbInstrument();
|
||||
break;
|
||||
case GUI_ACTION_PAT_CURSOR_UNDO:
|
||||
doCursorUndo();
|
||||
break;
|
||||
case GUI_ACTION_PAT_CURSOR_REDO:
|
||||
doCursorRedo();
|
||||
break;
|
||||
|
||||
case GUI_ACTION_INS_LIST_ADD:
|
||||
if (settings.insTypeMenu) {
|
||||
|
|
|
|||
|
|
@ -130,7 +130,7 @@ const bool mobileButtonPersist[32]={
|
|||
// page 1
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
|
|
@ -508,7 +508,7 @@ void FurnaceGUI::drawMobileControls() {
|
|||
mobileMenuOpen=false;
|
||||
doAction(GUI_ACTION_SAVE_AS);
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button(_("Export"))) {
|
||||
doAction(GUI_ACTION_EXPORT);
|
||||
}
|
||||
|
|
@ -533,6 +533,10 @@ void FurnaceGUI::drawMobileControls() {
|
|||
drawSpeed(true);
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
if (ImGui::BeginTabItem(_("Comments"))) {
|
||||
drawNotes(true);
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
ImGui::EndTabBar();
|
||||
}
|
||||
break;
|
||||
|
|
@ -619,41 +623,6 @@ void FurnaceGUI::drawMobileControls() {
|
|||
if (ImGui::Button(_("Switch to Desktop Mode"))) {
|
||||
toggleMobileUI(!mobileUI);
|
||||
}
|
||||
|
||||
int numAmiga=0;
|
||||
for (int i=0; i<e->song.systemLen; i++) {
|
||||
if (e->song.system[i]==DIV_SYSTEM_AMIGA) numAmiga++;
|
||||
}
|
||||
|
||||
if (numAmiga) {
|
||||
ImGui::Text(_(
|
||||
"this is NOT ROM export! only use for making sure the\n"
|
||||
"Furnace Amiga emulator is working properly by\n"
|
||||
"comparing it with real Amiga output."
|
||||
));
|
||||
ImGui::AlignTextToFramePadding();
|
||||
ImGui::Text(_("Directory"));
|
||||
ImGui::SameLine();
|
||||
ImGui::InputText("##AVDPath",&workingDirROMExport);
|
||||
if (ImGui::Button(_("Bake Data"))) {
|
||||
std::vector<DivROMExportOutput> out=e->buildROM(DIV_ROM_AMIGA_VALIDATION);
|
||||
if (workingDirROMExport.size()>0) {
|
||||
if (workingDirROMExport[workingDirROMExport.size()-1]!=DIR_SEPARATOR) workingDirROMExport+=DIR_SEPARATOR_STR;
|
||||
}
|
||||
for (DivROMExportOutput& i: out) {
|
||||
String path=workingDirROMExport+i.name;
|
||||
FILE* outFile=ps_fopen(path.c_str(),"wb");
|
||||
if (outFile!=NULL) {
|
||||
fwrite(i.data->getFinalBuf(),1,i.data->size(),outFile);
|
||||
fclose(outFile);
|
||||
}
|
||||
i.data->finish();
|
||||
delete i.data;
|
||||
}
|
||||
showError(fmt::sprintf(_("Done! Baked %d files."),(int)out.size()));
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -682,8 +651,8 @@ void FurnaceGUI::drawEditControls() {
|
|||
ImGui::TableNextColumn();
|
||||
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
|
||||
if (ImGui::InputInt("##Octave",&curOctave,1,1)) {
|
||||
if (curOctave>7) curOctave=7;
|
||||
if (curOctave<-5) curOctave=-5;
|
||||
if (curOctave>GUI_EDIT_OCTAVE_MAX) curOctave=GUI_EDIT_OCTAVE_MAX;
|
||||
if (curOctave<GUI_EDIT_OCTAVE_MIN) curOctave=GUI_EDIT_OCTAVE_MIN;
|
||||
e->autoNoteOffAll();
|
||||
failedNoteOn=false;
|
||||
|
||||
|
|
@ -843,8 +812,8 @@ void FurnaceGUI::drawEditControls() {
|
|||
ImGui::SameLine();
|
||||
ImGui::SetNextItemWidth(96.0f*dpiScale);
|
||||
if (ImGui::InputInt("##Octave",&curOctave,1,1)) {
|
||||
if (curOctave>7) curOctave=7;
|
||||
if (curOctave<-5) curOctave=-5;
|
||||
if (curOctave>GUI_EDIT_OCTAVE_MAX) curOctave=GUI_EDIT_OCTAVE_MAX;
|
||||
if (curOctave<GUI_EDIT_OCTAVE_MIN) curOctave=GUI_EDIT_OCTAVE_MIN;
|
||||
e->autoNoteOffAll();
|
||||
failedNoteOn=false;
|
||||
|
||||
|
|
@ -961,8 +930,8 @@ void FurnaceGUI::drawEditControls() {
|
|||
float avail=ImGui::GetContentRegionAvail().x;
|
||||
ImGui::SetNextItemWidth(avail);
|
||||
if (ImGui::InputInt("##Octave",&curOctave,0,0)) {
|
||||
if (curOctave>7) curOctave=7;
|
||||
if (curOctave<-5) curOctave=-5;
|
||||
if (curOctave>GUI_EDIT_OCTAVE_MAX) curOctave=GUI_EDIT_OCTAVE_MAX;
|
||||
if (curOctave<GUI_EDIT_OCTAVE_MIN) curOctave=GUI_EDIT_OCTAVE_MIN;
|
||||
e->autoNoteOffAll();
|
||||
failedNoteOn=false;
|
||||
|
||||
|
|
@ -1128,8 +1097,8 @@ void FurnaceGUI::drawEditControls() {
|
|||
float avail=ImGui::GetContentRegionAvail().x;
|
||||
ImGui::SetNextItemWidth(avail);
|
||||
if (ImGui::InputInt("##Octave",&curOctave,1,1)) {
|
||||
if (curOctave>7) curOctave=7;
|
||||
if (curOctave<-5) curOctave=-5;
|
||||
if (curOctave>GUI_EDIT_OCTAVE_MAX) curOctave=GUI_EDIT_OCTAVE_MAX;
|
||||
if (curOctave<GUI_EDIT_OCTAVE_MIN) curOctave=GUI_EDIT_OCTAVE_MIN;
|
||||
e->autoNoteOffAll();
|
||||
failedNoteOn=false;
|
||||
|
||||
|
|
|
|||
|
|
@ -678,6 +678,7 @@ void FurnaceGUI::doPasteFurnace(PasteMode mode, int arg, bool readClipboard, Str
|
|||
|
||||
if (readClipboard) {
|
||||
if (settings.cursorPastePos) {
|
||||
makeCursorUndo();
|
||||
cursor.y=j;
|
||||
if (cursor.y>=e->curSubSong->patLen) cursor.y=e->curSubSong->patLen-1;
|
||||
selStart=cursor;
|
||||
|
|
@ -1220,6 +1221,7 @@ void FurnaceGUI::doPasteMPT(PasteMode mode, int arg, bool readClipboard, String
|
|||
|
||||
if (readClipboard) {
|
||||
if (settings.cursorPastePos) {
|
||||
makeCursorUndo();
|
||||
cursor.y=j;
|
||||
if (cursor.y>=e->curSubSong->patLen) cursor.y=e->curSubSong->patLen-1;
|
||||
selStart=cursor;
|
||||
|
|
@ -1823,6 +1825,55 @@ void FurnaceGUI::doExpandSong(int multiplier) {
|
|||
if (e->isPlaying()) e->play();
|
||||
}
|
||||
|
||||
void FurnaceGUI::doAbsorbInstrument() {
|
||||
bool foundIns=false;
|
||||
bool foundOctave=false;
|
||||
auto foundAll = [&]() { return foundIns && foundOctave; };
|
||||
|
||||
// search this order and all prior until we find all the data we need
|
||||
int orderIdx=curOrder;
|
||||
for (; orderIdx>=0 && !foundAll(); orderIdx--) {
|
||||
DivPattern* pat=e->curPat[cursor.xCoarse].getPattern(e->curOrders->ord[cursor.xCoarse][orderIdx],false);
|
||||
if (!pat) continue;
|
||||
|
||||
// start on current row when searching current order, but start from end when searching
|
||||
// prior orders.
|
||||
int searchStartRow=orderIdx==curOrder ? cursor.y : e->curSubSong->patLen-1;
|
||||
for (int i=searchStartRow; i>=0 && !foundAll(); i--) {
|
||||
|
||||
// absorb most recent instrument
|
||||
if (!foundIns && pat->data[i][2] >= 0) {
|
||||
foundIns=true;
|
||||
curIns=pat->data[i][2];
|
||||
}
|
||||
|
||||
// absorb most recent octave (i.e. set curOctave such that the "main row" (QWERTY) of
|
||||
// notes will result in an octave number equal to the previous note). make sure to
|
||||
// skip "special note values" like OFF/REL/=== and "none", since there won't be valid
|
||||
// octave values
|
||||
unsigned char note=pat->data[i][0];
|
||||
if (!foundOctave && note!=0 && note!=100 && note!=101 && note!=102) {
|
||||
foundOctave=true;
|
||||
|
||||
// decode octave data (was signed cast to unsigned char)
|
||||
int octave=pat->data[i][1];
|
||||
if (octave>128) octave-=256;
|
||||
|
||||
// @NOTE the special handling when note==12, which is really an octave above what's
|
||||
// stored in the octave data. without this handling, if you press Q, then
|
||||
// "ABSORB_INSTRUMENT", then Q again, you'd get a different octave!
|
||||
if (pat->data[i][0]==12) octave++;
|
||||
curOctave=CLAMP(octave-1, GUI_EDIT_OCTAVE_MIN, GUI_EDIT_OCTAVE_MAX);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if no instrument has been set at this point, the only way to match it is to use "none"
|
||||
if (!foundIns) curIns=-1;
|
||||
|
||||
logD("doAbsorbInstrument -- searched %d orders", curOrder-orderIdx);
|
||||
}
|
||||
|
||||
void FurnaceGUI::doDrag() {
|
||||
int len=dragEnd.xCoarse-dragStart.xCoarse+1;
|
||||
|
||||
|
|
@ -2012,3 +2063,52 @@ void FurnaceGUI::doRedo() {
|
|||
|
||||
redoHist.pop_back();
|
||||
}
|
||||
|
||||
CursorJumpPoint FurnaceGUI::getCurrentCursorJumpPoint() {
|
||||
return CursorJumpPoint(cursor, curOrder, e->getCurrentSubSong());
|
||||
}
|
||||
|
||||
void FurnaceGUI::applyCursorJumpPoint(const CursorJumpPoint& spot) {
|
||||
cursor=spot.point;
|
||||
curOrder=MIN(e->curSubSong->ordersLen-1, spot.order);
|
||||
e->setOrder(curOrder);
|
||||
e->changeSongP(spot.subSong);
|
||||
if (!settings.cursorMoveNoScroll) {
|
||||
updateScroll(cursor.y);
|
||||
}
|
||||
}
|
||||
|
||||
void FurnaceGUI::makeCursorUndo() {
|
||||
CursorJumpPoint spot = getCurrentCursorJumpPoint();
|
||||
if (!cursorUndoHist.empty() && spot == cursorUndoHist.back()) return;
|
||||
|
||||
if (cursorUndoHist.size()>=settings.maxUndoSteps) cursorUndoHist.pop_front();
|
||||
cursorUndoHist.push_back(spot);
|
||||
|
||||
// redo history no longer relevant, we've changed timeline
|
||||
cursorRedoHist.clear();
|
||||
}
|
||||
|
||||
void FurnaceGUI::doCursorUndo() {
|
||||
if (cursorUndoHist.empty()) return;
|
||||
|
||||
// allow returning to current spot
|
||||
if (cursorRedoHist.size()>=settings.maxUndoSteps) cursorRedoHist.pop_front();
|
||||
cursorRedoHist.push_back(getCurrentCursorJumpPoint());
|
||||
|
||||
// apply spot
|
||||
applyCursorJumpPoint(cursorUndoHist.back());
|
||||
cursorUndoHist.pop_back();
|
||||
}
|
||||
|
||||
void FurnaceGUI::doCursorRedo() {
|
||||
if (cursorRedoHist.empty()) return;
|
||||
|
||||
// allow returning to current spot
|
||||
if (cursorUndoHist.size()>=settings.maxUndoSteps) cursorUndoHist.pop_front();
|
||||
cursorUndoHist.push_back(getCurrentCursorJumpPoint());
|
||||
|
||||
// apply spot
|
||||
applyCursorJumpPoint(cursorRedoHist.back());
|
||||
cursorRedoHist.pop_back();
|
||||
}
|
||||
|
|
@ -239,111 +239,126 @@ void FurnaceGUI::drawExportVGM(bool onWindow) {
|
|||
}
|
||||
}
|
||||
|
||||
void FurnaceGUI::drawExportZSM(bool onWindow) {
|
||||
void FurnaceGUI::drawExportROM(bool onWindow) {
|
||||
exitDisabledTimer=1;
|
||||
|
||||
ImGui::Text(_("Commander X16 Zsound Music File"));
|
||||
if (ImGui::InputInt(_("Tick Rate (Hz)"),&zsmExportTickRate,1,2)) {
|
||||
if (zsmExportTickRate<1) zsmExportTickRate=1;
|
||||
if (zsmExportTickRate>44100) zsmExportTickRate=44100;
|
||||
const DivROMExportDef* def=e->getROMExportDef(romTarget);
|
||||
|
||||
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
|
||||
if (ImGui::BeginCombo("##ROMTarget",def==NULL?"<select one>":def->name)) {
|
||||
for (int i=0; i<DIV_ROM_MAX; i++) {
|
||||
const DivROMExportDef* newDef=e->getROMExportDef((DivROMExportOptions)i);
|
||||
if (newDef!=NULL) {
|
||||
if (romExportAvail[i]) {
|
||||
if (ImGui::Selectable(newDef->name)) {
|
||||
romTarget=(DivROMExportOptions)i;
|
||||
romMultiFile=newDef->multiOutput;
|
||||
romConfig=DivConfig();
|
||||
if (newDef->fileExt==NULL) {
|
||||
romFilterName="";
|
||||
romFilterExt="";
|
||||
} else {
|
||||
romFilterName=newDef->fileType;
|
||||
romFilterExt=newDef->fileExt;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
ImGui::Checkbox(_("loop"),&zsmExportLoop);
|
||||
ImGui::SameLine();
|
||||
ImGui::Checkbox(_("optimize size"),&zsmExportOptimize);
|
||||
|
||||
if (def!=NULL) {
|
||||
ImGui::Text("by %s",def->author);
|
||||
|
||||
ImGui::TextWrapped("%s",def->description);
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
bool altered=false;
|
||||
|
||||
switch (romTarget) {
|
||||
case DIV_ROM_TIUNA: {
|
||||
String asmBaseLabel=romConfig.getString("baseLabel","song");
|
||||
int firstBankSize=romConfig.getInt("firstBankSize",3072);
|
||||
int otherBankSize=romConfig.getInt("otherBankSize",4096-48);
|
||||
int sysToExport=romConfig.getInt("sysToExport",-1);
|
||||
|
||||
// TODO; validate label
|
||||
if (ImGui::InputText(_("base song label name"),&asmBaseLabel)) {
|
||||
altered=true;
|
||||
}
|
||||
if (ImGui::InputInt(_("max size in first bank"),&firstBankSize,1,100)) {
|
||||
if (firstBankSize<0) firstBankSize=0;
|
||||
if (firstBankSize>4096) firstBankSize=4096;
|
||||
altered=true;
|
||||
}
|
||||
if (ImGui::InputInt(_("max size in other banks"),&otherBankSize,1,100)) {
|
||||
if (otherBankSize<16) otherBankSize=16;
|
||||
if (otherBankSize>4096) otherBankSize=4096;
|
||||
altered=true;
|
||||
}
|
||||
|
||||
ImGui::Text(_("chip to export:"));
|
||||
for (int i=0; i<e->song.systemLen; i++) {
|
||||
DivSystem sys=e->song.system[i];
|
||||
bool isTIA=(sys==DIV_SYSTEM_TIA);
|
||||
ImGui::BeginDisabled(!isTIA);
|
||||
if (ImGui::RadioButton(fmt::sprintf("%d. %s##_SYSV%d",i+1,getSystemName(e->song.system[i]),i).c_str(),sysToExport==i)) {
|
||||
sysToExport=i;
|
||||
altered=true;
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
}
|
||||
if (altered) {
|
||||
romConfig.set("baseLabel",asmBaseLabel);
|
||||
romConfig.set("firstBankSize",firstBankSize);
|
||||
romConfig.set("otherBankSize",otherBankSize);
|
||||
romConfig.set("sysToExport",sysToExport);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_ROM_ZSM: {
|
||||
int zsmExportTickRate=romConfig.getInt("zsmrate",60);
|
||||
bool zsmExportLoop=romConfig.getBool("loop",true);
|
||||
bool zsmExportOptimize=romConfig.getBool("optimize",true);
|
||||
|
||||
if (ImGui::InputInt(_("Tick Rate (Hz)"),&zsmExportTickRate,1,2)) {
|
||||
if (zsmExportTickRate<1) zsmExportTickRate=1;
|
||||
if (zsmExportTickRate>44100) zsmExportTickRate=44100;
|
||||
altered=true;
|
||||
}
|
||||
if (ImGui::Checkbox(_("loop"),&zsmExportLoop)) {
|
||||
altered=true;
|
||||
}
|
||||
if (ImGui::Checkbox(_("optimize size"),&zsmExportOptimize)) {
|
||||
altered=true;
|
||||
}
|
||||
if (altered) {
|
||||
romConfig.set("zsmrate",zsmExportTickRate);
|
||||
romConfig.set("loop",zsmExportLoop);
|
||||
romConfig.set("optimize",zsmExportOptimize);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_ROM_ABSTRACT:
|
||||
ImGui::TextWrapped("%s",_("select a target from the menu at the top of this dialog."));
|
||||
break;
|
||||
default:
|
||||
ImGui::TextWrapped("%s",_("this export method doesn't offer any options."));
|
||||
break;
|
||||
}
|
||||
/*
|
||||
*/
|
||||
|
||||
if (onWindow) {
|
||||
ImGui::Separator();
|
||||
if (ImGui::Button(_("Cancel"),ImVec2(200.0f*dpiScale,0))) ImGui::CloseCurrentPopup();
|
||||
ImGui::SameLine();
|
||||
}
|
||||
if (ImGui::Button(_("Export"),ImVec2(200.0f*dpiScale,0))) {
|
||||
openFileDialog(GUI_FILE_EXPORT_ZSM);
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
}
|
||||
|
||||
void FurnaceGUI::drawExportTiuna(bool onWindow) {
|
||||
exitDisabledTimer=1;
|
||||
|
||||
ImGui::Text(_("for use with TIunA driver. outputs asm source."));
|
||||
ImGui::InputText(_("base song label name"),&asmBaseLabel); // TODO: validate label
|
||||
if (ImGui::InputInt(_("max size in first bank"),&tiunaFirstBankSize,1,100)) {
|
||||
if (tiunaFirstBankSize<0) tiunaFirstBankSize=0;
|
||||
if (tiunaFirstBankSize>4096) tiunaFirstBankSize=4096;
|
||||
}
|
||||
if (ImGui::InputInt(_("max size in other banks"),&tiunaOtherBankSize,1,100)) {
|
||||
if (tiunaOtherBankSize<16) tiunaOtherBankSize=16;
|
||||
if (tiunaOtherBankSize>4096) tiunaOtherBankSize=4096;
|
||||
}
|
||||
|
||||
ImGui::Text(_("chips to export:"));
|
||||
int selected=0;
|
||||
for (int i=0; i<e->song.systemLen; i++) {
|
||||
DivSystem sys=e->song.system[i];
|
||||
bool isTIA=sys==DIV_SYSTEM_TIA;
|
||||
ImGui::BeginDisabled((!isTIA) || (selected>=1));
|
||||
ImGui::Checkbox(fmt::sprintf("%d. %s##_SYSV%d",i+1,getSystemName(e->song.system[i]),i).c_str(),&willExport[i]);
|
||||
ImGui::EndDisabled();
|
||||
if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) {
|
||||
if (!isTIA) {
|
||||
ImGui::SetTooltip(_("this chip is not supported by the file format!"));
|
||||
} else if (selected>=1) {
|
||||
ImGui::SetTooltip(_("only one Atari TIA is supported!"));
|
||||
}
|
||||
}
|
||||
if (isTIA && willExport[i]) selected++;
|
||||
}
|
||||
if (selected>0) {
|
||||
if (onWindow) {
|
||||
ImGui::Separator();
|
||||
if (ImGui::Button(_("Cancel"),ImVec2(200.0f*dpiScale,0))) ImGui::CloseCurrentPopup();
|
||||
ImGui::SameLine();
|
||||
}
|
||||
if (ImGui::Button(_("Export"),ImVec2(200.0f*dpiScale,0))) {
|
||||
openFileDialog(GUI_FILE_EXPORT_TIUNA);
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
} else {
|
||||
ImGui::Text(_("nothing to export"));
|
||||
if (onWindow) {
|
||||
ImGui::Separator();
|
||||
if (ImGui::Button(_("Cancel"),ImVec2(400.0f*dpiScale,0))) ImGui::CloseCurrentPopup();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FurnaceGUI::drawExportAmigaVal(bool onWindow) {
|
||||
exitDisabledTimer=1;
|
||||
|
||||
ImGui::Text(_(
|
||||
"this is NOT ROM export! only use for making sure the\n"
|
||||
"Furnace Amiga emulator is working properly by\n"
|
||||
"comparing it with real Amiga output."
|
||||
));
|
||||
ImGui::AlignTextToFramePadding();
|
||||
ImGui::Text(_("Directory"));
|
||||
ImGui::SameLine();
|
||||
ImGui::InputText("##AVDPath",&workingDirROMExport);
|
||||
if (onWindow) {
|
||||
ImGui::Separator();
|
||||
if (ImGui::Button(_("Cancel"),ImVec2(200.0f*dpiScale,0))) ImGui::CloseCurrentPopup();
|
||||
ImGui::SameLine();
|
||||
}
|
||||
if (ImGui::Button(_("Bake Data"),ImVec2(200.0f*dpiScale,0))) {
|
||||
std::vector<DivROMExportOutput> out=e->buildROM(DIV_ROM_AMIGA_VALIDATION);
|
||||
if (workingDirROMExport.size()>0) {
|
||||
if (workingDirROMExport[workingDirROMExport.size()-1]!=DIR_SEPARATOR) workingDirROMExport+=DIR_SEPARATOR_STR;
|
||||
}
|
||||
for (DivROMExportOutput& i: out) {
|
||||
String path=workingDirROMExport+i.name;
|
||||
FILE* outFile=ps_fopen(path.c_str(),"wb");
|
||||
if (outFile!=NULL) {
|
||||
fwrite(i.data->getFinalBuf(),1,i.data->size(),outFile);
|
||||
fclose(outFile);
|
||||
}
|
||||
i.data->finish();
|
||||
delete i.data;
|
||||
}
|
||||
showError(fmt::sprintf(_("Done! Baked %d files."),(int)out.size()));
|
||||
openFileDialog(GUI_FILE_EXPORT_ROM);
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
}
|
||||
|
|
@ -424,36 +439,9 @@ void FurnaceGUI::drawExport() {
|
|||
drawExportVGM(true);
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
int numZSMCompat=0;
|
||||
for (int i=0; i<e->song.systemLen; i++) {
|
||||
if ((e->song.system[i]==DIV_SYSTEM_VERA) || (e->song.system[i]==DIV_SYSTEM_YM2151)) numZSMCompat++;
|
||||
}
|
||||
if (numZSMCompat>0) {
|
||||
if (ImGui::BeginTabItem(_("ZSM"))) {
|
||||
drawExportZSM(true);
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
}
|
||||
bool hasTiunaCompat=false;
|
||||
for (int i=0; i<e->song.systemLen; i++) {
|
||||
if (e->song.system[i]==DIV_SYSTEM_TIA) {
|
||||
hasTiunaCompat=true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (hasTiunaCompat) {
|
||||
if (ImGui::BeginTabItem("TIunA")) {
|
||||
drawExportTiuna(true);
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
}
|
||||
int numAmiga=0;
|
||||
for (int i=0; i<e->song.systemLen; i++) {
|
||||
if (e->song.system[i]==DIV_SYSTEM_AMIGA) numAmiga++;
|
||||
}
|
||||
if (numAmiga && settings.iCannotWait) {
|
||||
if (ImGui::BeginTabItem(_("Amiga Validation"))) {
|
||||
drawExportAmigaVal(true);
|
||||
if (romExportExists) {
|
||||
if (ImGui::BeginTabItem(_("ROM"))) {
|
||||
drawExportROM(true);
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
}
|
||||
|
|
@ -478,14 +466,8 @@ void FurnaceGUI::drawExport() {
|
|||
case GUI_EXPORT_VGM:
|
||||
drawExportVGM(true);
|
||||
break;
|
||||
case GUI_EXPORT_ZSM:
|
||||
drawExportZSM(true);
|
||||
break;
|
||||
case GUI_EXPORT_TIUNA:
|
||||
drawExportTiuna(true);
|
||||
break;
|
||||
case GUI_EXPORT_AMIGA_VAL:
|
||||
drawExportAmigaVal(true);
|
||||
case GUI_EXPORT_ROM:
|
||||
drawExportROM(true);
|
||||
break;
|
||||
case GUI_EXPORT_TEXT:
|
||||
drawExportText(true);
|
||||
|
|
|
|||
|
|
@ -560,6 +560,7 @@ void FurnaceGUI::drawFindReplace() {
|
|||
if (ImGui::TableNextColumn()) {
|
||||
snprintf(tempID,1024,ICON_FA_CHEVRON_RIGHT "##_FR%d",index);
|
||||
if (ImGui::Selectable(tempID)) {
|
||||
makeCursorUndo();
|
||||
e->changeSongP(i.subsong);
|
||||
if (e->isPlaying()) {
|
||||
followPattern=false;
|
||||
|
|
|
|||
824
src/gui/gui.cpp
824
src/gui/gui.cpp
File diff suppressed because it is too large
Load diff
|
|
@ -137,6 +137,9 @@ enum FurnaceGUIRenderBackend {
|
|||
#define ngettext momo_ngettext
|
||||
#endif
|
||||
|
||||
#define GUI_EDIT_OCTAVE_MIN -5
|
||||
#define GUI_EDIT_OCTAVE_MAX 7
|
||||
|
||||
// TODO:
|
||||
// - add colors for FM envelope and waveform
|
||||
// - maybe add "alternate" color for FM modulators/carriers (a bit difficult)
|
||||
|
|
@ -600,8 +603,6 @@ enum FurnaceGUIFileDialogs {
|
|||
GUI_FILE_EXPORT_AUDIO_PER_SYS,
|
||||
GUI_FILE_EXPORT_AUDIO_PER_CHANNEL,
|
||||
GUI_FILE_EXPORT_VGM,
|
||||
GUI_FILE_EXPORT_ZSM,
|
||||
GUI_FILE_EXPORT_TIUNA,
|
||||
GUI_FILE_EXPORT_CMDSTREAM,
|
||||
GUI_FILE_EXPORT_TEXT,
|
||||
GUI_FILE_EXPORT_ROM,
|
||||
|
|
@ -653,10 +654,8 @@ enum FurnaceGUIExportTypes {
|
|||
|
||||
GUI_EXPORT_AUDIO=0,
|
||||
GUI_EXPORT_VGM,
|
||||
GUI_EXPORT_ZSM,
|
||||
GUI_EXPORT_TIUNA,
|
||||
GUI_EXPORT_ROM,
|
||||
GUI_EXPORT_CMD_STREAM,
|
||||
GUI_EXPORT_AMIGA_VAL,
|
||||
GUI_EXPORT_TEXT,
|
||||
GUI_EXPORT_DMF
|
||||
};
|
||||
|
|
@ -820,6 +819,9 @@ enum FurnaceGUIActions {
|
|||
GUI_ACTION_PAT_LATCH,
|
||||
GUI_ACTION_PAT_SCROLL_MODE,
|
||||
GUI_ACTION_PAT_CLEAR_LATCH,
|
||||
GUI_ACTION_PAT_ABSORB_INSTRUMENT,
|
||||
GUI_ACTION_PAT_CURSOR_UNDO,
|
||||
GUI_ACTION_PAT_CURSOR_REDO,
|
||||
GUI_ACTION_PAT_MAX,
|
||||
|
||||
GUI_ACTION_INS_LIST_MIN,
|
||||
|
|
@ -1105,6 +1107,22 @@ struct UndoStep {
|
|||
newPatLen(0) {}
|
||||
};
|
||||
|
||||
struct CursorJumpPoint {
|
||||
SelectionPoint point;
|
||||
int order;
|
||||
int subSong;
|
||||
CursorJumpPoint(const SelectionPoint& p, int o, int ss):
|
||||
point(p), order(o), subSong(ss) {}
|
||||
CursorJumpPoint():
|
||||
point(), order(0), subSong(0) {}
|
||||
bool operator== (const CursorJumpPoint& spot) {
|
||||
return point.xCoarse==spot.point.xCoarse && point.xFine==spot.point.xFine && point.y==spot.point.y && order==spot.order && subSong==spot.subSong;
|
||||
}
|
||||
bool operator!= (const CursorJumpPoint& spot) {
|
||||
return !(*this == spot);
|
||||
}
|
||||
};
|
||||
|
||||
// -1 = any
|
||||
struct MIDIBind {
|
||||
int type, channel, data1, data2;
|
||||
|
|
@ -1595,9 +1613,9 @@ class FurnaceGUI {
|
|||
int sampleTexW, sampleTexH;
|
||||
bool updateSampleTex;
|
||||
|
||||
String workingDir, fileName, clipboard, warnString, errorString, lastError, curFileName, nextFile, sysSearchQuery, newSongQuery, paletteQuery;
|
||||
String workingDir, fileName, clipboard, warnString, errorString, lastError, curFileName, nextFile, sysSearchQuery, newSongQuery, paletteQuery, sampleBankSearchQuery;
|
||||
String workingDirSong, workingDirIns, workingDirWave, workingDirSample, workingDirAudioExport;
|
||||
String workingDirVGMExport, workingDirZSMExport, workingDirROMExport;
|
||||
String workingDirVGMExport, workingDirROMExport;
|
||||
String workingDirFont, workingDirColors, workingDirKeybinds;
|
||||
String workingDirLayout, workingDirROM, workingDirTest;
|
||||
String workingDirConfig;
|
||||
|
|
@ -1607,6 +1625,7 @@ class FurnaceGUI {
|
|||
String folderString;
|
||||
|
||||
std::vector<DivSystem> sysSearchResults;
|
||||
std::vector<std::pair<DivSample*,bool>> sampleBankSearchResults;
|
||||
std::vector<FurnaceGUISysDef> newSongSearchResults;
|
||||
std::vector<int> paletteSearchResults;
|
||||
FixedQueue<String,32> recentFile;
|
||||
|
|
@ -1615,13 +1634,15 @@ class FurnaceGUI {
|
|||
std::vector<String> availRenderDrivers;
|
||||
std::vector<String> availAudioDrivers;
|
||||
|
||||
bool quit, warnQuit, willCommit, edit, editClone, isPatUnique, modified, displayError, displayExporting, vgmExportLoop, zsmExportLoop, zsmExportOptimize, vgmExportPatternHints;
|
||||
bool quit, warnQuit, willCommit, edit, editClone, isPatUnique, modified, displayError, displayExporting, vgmExportLoop, vgmExportPatternHints;
|
||||
bool vgmExportDirectStream, displayInsTypeList, displayWaveSizeList;
|
||||
bool portrait, injectBackUp, mobileMenuOpen, warnColorPushed;
|
||||
bool wantCaptureKeyboard, oldWantCaptureKeyboard, displayMacroMenu;
|
||||
bool displayNew, displayExport, displayPalette, fullScreen, preserveChanPos, sysDupCloneChannels, sysDupEnd, noteInputPoly, notifyWaveChange;
|
||||
bool wantScrollListIns, wantScrollListWave, wantScrollListSample;
|
||||
bool displayPendingIns, pendingInsSingle, displayPendingRawSample, snesFilterHex, modTableHex, displayEditString;
|
||||
bool displayPendingSamples, replacePendingSample;
|
||||
bool displayExportingROM;
|
||||
bool changeCoarse;
|
||||
bool mobileEdit;
|
||||
bool killGraphics;
|
||||
|
|
@ -1634,10 +1655,6 @@ class FurnaceGUI {
|
|||
int vgmExportTrailingTicks;
|
||||
int cvHiScore;
|
||||
int drawHalt;
|
||||
int zsmExportTickRate;
|
||||
String asmBaseLabel;
|
||||
int tiunaFirstBankSize;
|
||||
int tiunaOtherBankSize;
|
||||
int macroPointSize;
|
||||
int waveEditStyle;
|
||||
int displayInsTypeListMakeInsSample;
|
||||
|
|
@ -1726,6 +1743,16 @@ class FurnaceGUI {
|
|||
char emptyLabel[32];
|
||||
char emptyLabel2[32];
|
||||
|
||||
std::vector<int> songOrdersLengths; // lengths of all orders (for drawing song export progress)
|
||||
int songLength; // length of all the song in rows
|
||||
int songLoopedSectionLength; // length of looped part of the song
|
||||
int songFadeoutSectionLength; // length of fading part of the song
|
||||
bool songHasSongEndCommand; // song has "Song end" command (FFxx)
|
||||
int lengthOfOneFile; // length of one rendering pass. song length times num of loops + fadeout
|
||||
int totalLength; // total length of render (lengthOfOneFile times num of files for per-channel export)
|
||||
float curProgress;
|
||||
int totalFiles;
|
||||
|
||||
struct Settings {
|
||||
bool settingsChanged;
|
||||
int mainFontSize, patFontSize, headFontSize, iconSize;
|
||||
|
|
@ -1963,6 +1990,7 @@ class FurnaceGUI {
|
|||
unsigned int maxUndoSteps;
|
||||
float vibrationStrength;
|
||||
int vibrationLength;
|
||||
int s3mOPL3;
|
||||
String mainFontPath;
|
||||
String headFontPath;
|
||||
String patFontPath;
|
||||
|
|
@ -2219,6 +2247,7 @@ class FurnaceGUI {
|
|||
maxUndoSteps(100),
|
||||
vibrationStrength(0.5f),
|
||||
vibrationLength(20),
|
||||
s3mOPL3(1),
|
||||
mainFontPath(""),
|
||||
headFontPath(""),
|
||||
patFontPath(""),
|
||||
|
|
@ -2260,6 +2289,9 @@ class FurnaceGUI {
|
|||
std::vector<ImWchar> localeExtraRanges;
|
||||
|
||||
DivInstrument* prevInsData;
|
||||
DivInstrument cachedCurIns;
|
||||
DivInstrument* cachedCurInsPtr;
|
||||
bool insEditMayBeDirty;
|
||||
|
||||
unsigned char* pendingLayoutImport;
|
||||
size_t pendingLayoutImportLen;
|
||||
|
|
@ -2375,6 +2407,7 @@ class FurnaceGUI {
|
|||
std::vector<DivCommand> cmdStream;
|
||||
std::vector<Particle> particles;
|
||||
std::vector<std::pair<DivInstrument*,bool>> pendingIns;
|
||||
std::vector<std::pair<DivSample*,bool>> pendingSamples;
|
||||
|
||||
std::vector<FurnaceGUISysCategory> sysCategories;
|
||||
|
||||
|
|
@ -2487,6 +2520,8 @@ class FurnaceGUI {
|
|||
std::map<unsigned short,DivPattern*> oldPatMap;
|
||||
FixedQueue<UndoStep,256> undoHist;
|
||||
FixedQueue<UndoStep,256> redoHist;
|
||||
FixedQueue<CursorJumpPoint,256> cursorUndoHist;
|
||||
FixedQueue<CursorJumpPoint,256> cursorRedoHist;
|
||||
|
||||
// sample editor specific
|
||||
double sampleZoom;
|
||||
|
|
@ -2681,6 +2716,17 @@ class FurnaceGUI {
|
|||
int dmfExportVersion;
|
||||
FurnaceGUIExportTypes curExportType;
|
||||
|
||||
// ROM export specific
|
||||
DivROMExportOptions romTarget;
|
||||
DivConfig romConfig;
|
||||
bool romMultiFile;
|
||||
bool romExportSave;
|
||||
String romFilterName, romFilterExt;
|
||||
String romExportPath;
|
||||
DivROMExport* pendingExport;
|
||||
bool romExportAvail[DIV_ROM_MAX];
|
||||
bool romExportExists;
|
||||
|
||||
// user presets window
|
||||
std::vector<int> selectedUserPreset;
|
||||
|
||||
|
|
@ -2688,9 +2734,7 @@ class FurnaceGUI {
|
|||
|
||||
void drawExportAudio(bool onWindow=false);
|
||||
void drawExportVGM(bool onWindow=false);
|
||||
void drawExportZSM(bool onWindow=false);
|
||||
void drawExportTiuna(bool onWindow=false);
|
||||
void drawExportAmigaVal(bool onWindow=false);
|
||||
void drawExportROM(bool onWindow=false);
|
||||
void drawExportText(bool onWindow=false);
|
||||
void drawExportCommand(bool onWindow=false);
|
||||
void drawExportDMF(bool onWindow=false);
|
||||
|
|
@ -2730,6 +2774,7 @@ class FurnaceGUI {
|
|||
bool portSet(String label, unsigned int portSetID, int ins, int outs, int activeIns, int activeOuts, int& clickedPort, std::map<unsigned int,ImVec2>& portPos);
|
||||
|
||||
void updateWindowTitle();
|
||||
void updateROMExportAvail();
|
||||
void autoDetectSystem();
|
||||
void autoDetectSystemIter(std::vector<FurnaceGUISysDef>& category, bool& isMatch, std::map<DivSystem,int>& defCountMap, std::map<DivSystem,DivConfig>& defConfMap, std::map<DivSystem,int>& sysCountMap, std::map<DivSystem,DivConfig>& sysConfMap);
|
||||
void prepareLayout();
|
||||
|
|
@ -2809,7 +2854,7 @@ class FurnaceGUI {
|
|||
void drawMemory();
|
||||
void drawCompatFlags();
|
||||
void drawPiano();
|
||||
void drawNotes();
|
||||
void drawNotes(bool asChild=false);
|
||||
void drawChannels();
|
||||
void drawPatManager();
|
||||
void drawSysManager();
|
||||
|
|
@ -2899,22 +2944,33 @@ class FurnaceGUI {
|
|||
void doExpand(int multiplier, const SelectionPoint& sStart, const SelectionPoint& sEnd);
|
||||
void doCollapseSong(int divider);
|
||||
void doExpandSong(int multiplier);
|
||||
void doAbsorbInstrument();
|
||||
void doUndo();
|
||||
void doRedo();
|
||||
void doFind();
|
||||
void doReplace();
|
||||
void doDrag();
|
||||
void editOptions(bool topMenu);
|
||||
DivSystem systemPicker();
|
||||
DivSystem systemPicker(bool fullWidth);
|
||||
void noteInput(int num, int key, int vol=-1);
|
||||
void valueInput(int num, bool direct=false, int target=-1);
|
||||
void orderInput(int num);
|
||||
|
||||
void doGenerateWave();
|
||||
|
||||
CursorJumpPoint getCurrentCursorJumpPoint();
|
||||
void applyCursorJumpPoint(const CursorJumpPoint& spot);
|
||||
void makeCursorUndo();
|
||||
void doCursorUndo();
|
||||
void doCursorRedo();
|
||||
|
||||
void doUndoSample();
|
||||
void doRedoSample();
|
||||
|
||||
void checkRecordInstrumentUndoStep();
|
||||
void doUndoInstrument();
|
||||
void doRedoInstrument();
|
||||
|
||||
void play(int row=0);
|
||||
void setOrder(unsigned char order, bool forced=false);
|
||||
void stop();
|
||||
|
|
|
|||
|
|
@ -475,8 +475,8 @@ const FurnaceGUIColors fxColors[256]={
|
|||
GUI_COLOR_PATTERN_EFFECT_INVALID,
|
||||
GUI_COLOR_PATTERN_EFFECT_INVALID,
|
||||
GUI_COLOR_PATTERN_EFFECT_INVALID,
|
||||
GUI_COLOR_PATTERN_EFFECT_INVALID,
|
||||
GUI_COLOR_PATTERN_EFFECT_INVALID,
|
||||
GUI_COLOR_PATTERN_EFFECT_VOLUME,
|
||||
GUI_COLOR_PATTERN_EFFECT_VOLUME,
|
||||
GUI_COLOR_PATTERN_EFFECT_INVALID,
|
||||
GUI_COLOR_PATTERN_EFFECT_INVALID,
|
||||
GUI_COLOR_PATTERN_EFFECT_INVALID,
|
||||
|
|
@ -689,6 +689,9 @@ const FurnaceGUIActionDef guiActions[GUI_ACTION_MAX]={
|
|||
D("PAT_LATCH", _N("Set note input latch"), 0),
|
||||
D("PAT_SCROLL_MODE", _N("Change mobile scroll mode"), 0),
|
||||
D("PAT_CLEAR_LATCH", _N("Clear note input latch"), 0),
|
||||
D("PAT_ABSORB_INSTRUMENT", _N("Absorb instrument/octave from status at cursor"), 0),
|
||||
D("PAT_CURSOR_UNDO", _N("Return cursor to previous jump point"), 0),
|
||||
D("PAT_CURSOR_REDO", _N("Reverse recent cursor undo"), 0),
|
||||
D("PAT_MAX", "", NOT_AN_ACTION),
|
||||
|
||||
D("INS_LIST_MIN", _N("---Instrument list"), NOT_AN_ACTION),
|
||||
|
|
|
|||
|
|
@ -5251,6 +5251,7 @@ void FurnaceGUI::drawInsEdit() {
|
|||
ImGui::SetNextWindowSizeConstraints(ImVec2(440.0f*dpiScale,400.0f*dpiScale),ImVec2(canvasW,canvasH));
|
||||
}
|
||||
if (ImGui::Begin("Instrument Editor",&insEditOpen,globalWinFlags|(settings.allowEditDocking?0:ImGuiWindowFlags_NoDocking),_("Instrument Editor"))) {
|
||||
DivInstrument* ins=NULL;
|
||||
if (curIns==-2) {
|
||||
ImGui::SetCursorPosY(ImGui::GetCursorPosY()+(ImGui::GetContentRegionAvail().y-ImGui::GetFrameHeightWithSpacing()+ImGui::GetStyle().ItemSpacing.y)*0.5f);
|
||||
CENTER_TEXT(_("waiting..."));
|
||||
|
|
@ -5278,6 +5279,7 @@ void FurnaceGUI::drawInsEdit() {
|
|||
curIns=i;
|
||||
wavePreviewInit=true;
|
||||
updateFMPreview=true;
|
||||
ins = e->song.ins[curIns];
|
||||
}
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
|
|
@ -5300,7 +5302,7 @@ void FurnaceGUI::drawInsEdit() {
|
|||
ImGui::EndTable();
|
||||
}
|
||||
} else {
|
||||
DivInstrument* ins=e->song.ins[curIns];
|
||||
ins=e->song.ins[curIns];
|
||||
if (updateFMPreview) {
|
||||
renderFMPreview(ins);
|
||||
updateFMPreview=false;
|
||||
|
|
@ -7185,6 +7187,8 @@ void FurnaceGUI::drawInsEdit() {
|
|||
macroList.push_back(FurnaceGUIMacroDesc(_("Noise"),&ins->std.dutyMacro,0,8,160,uiColors[GUI_COLOR_MACRO_NOISE]));
|
||||
}
|
||||
macroList.push_back(FurnaceGUIMacroDesc(_("Waveform"),&ins->std.waveMacro,0,waveCount,160,uiColors[GUI_COLOR_MACRO_WAVE],false,NULL,NULL,false,NULL));
|
||||
macroList.push_back(FurnaceGUIMacroDesc(_("Panning (left)"),&ins->std.panLMacro,0,15,46,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL));
|
||||
macroList.push_back(FurnaceGUIMacroDesc(_("Panning (right)"),&ins->std.panRMacro,0,15,46,uiColors[GUI_COLOR_MACRO_OTHER]));
|
||||
macroList.push_back(FurnaceGUIMacroDesc(_("Pitch"),&ins->std.pitchMacro,-2048,2047,160,uiColors[GUI_COLOR_MACRO_PITCH],true,macroRelativeMode));
|
||||
macroList.push_back(FurnaceGUIMacroDesc(_("Phase Reset"),&ins->std.phaseResetMacro,0,1,32,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true));
|
||||
break;
|
||||
|
|
@ -7585,6 +7589,10 @@ void FurnaceGUI::drawInsEdit() {
|
|||
macroList.push_back(FurnaceGUIMacroDesc(_("Timer Num"),&ins->std.ex8Macro,0,15,64,uiColors[GUI_COLOR_MACRO_OTHER]));
|
||||
macroList.push_back(FurnaceGUIMacroDesc(_("Timer Den"),&ins->std.fmsMacro,0,15,64,uiColors[GUI_COLOR_MACRO_OTHER]));
|
||||
macroList.push_back(FurnaceGUIMacroDesc(_("PWM Boundary"),&ins->std.amsMacro,0,15,64,uiColors[GUI_COLOR_MACRO_OTHER]));
|
||||
// workaround, because the gui will not set
|
||||
// zoom or scroll if we're not in macros tab
|
||||
ins->std.ex7Macro.vZoom=128;
|
||||
ins->std.ex7Macro.vScroll=2048-64;
|
||||
drawMacros(macroList,macroEditStateMacros);
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
|
|
@ -7751,6 +7759,63 @@ void FurnaceGUI::drawInsEdit() {
|
|||
ImGui::EndPopup();
|
||||
}
|
||||
}
|
||||
|
||||
if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_INS_EDIT;
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
void FurnaceGUI::checkRecordInstrumentUndoStep() {
|
||||
if (insEditOpen && curIns>=0 && curIns<(int)e->song.ins.size()) {
|
||||
DivInstrument* ins=e->song.ins[curIns];
|
||||
|
||||
// invalidate cachedCurIns/any possible changes if the cachedCurIns was referencing a different
|
||||
// instrument altgoether
|
||||
bool insChanged=ins!=cachedCurInsPtr;
|
||||
if (insChanged) {
|
||||
insEditMayBeDirty=false;
|
||||
cachedCurInsPtr=ins;
|
||||
cachedCurIns=*ins;
|
||||
}
|
||||
|
||||
cachedCurInsPtr=ins;
|
||||
|
||||
// check against the last cached to see if diff -- note that modifications to instruments
|
||||
// happen outside drawInsEdit (e.g. cursor inputs are processed and can directly modify
|
||||
// macro data). but don't check until we think the user input is complete.
|
||||
bool delayDiff=ImGui::IsMouseDown(ImGuiMouseButton_Left) || ImGui::IsMouseDown(ImGuiMouseButton_Right) || ImGui::GetIO().WantCaptureKeyboard;
|
||||
if (!delayDiff && insEditMayBeDirty) {
|
||||
bool hasChange=ins->recordUndoStepIfChanged(e->processTime, &cachedCurIns);
|
||||
if (hasChange) {
|
||||
cachedCurIns=*ins;
|
||||
}
|
||||
insEditMayBeDirty=false;
|
||||
}
|
||||
} else {
|
||||
cachedCurInsPtr=NULL;
|
||||
insEditMayBeDirty=false;
|
||||
}
|
||||
}
|
||||
|
||||
void FurnaceGUI::doUndoInstrument() {
|
||||
if (!insEditOpen) return;
|
||||
if (curIns<0 || curIns>=(int)e->song.ins.size()) return;
|
||||
DivInstrument* ins=e->song.ins[curIns];
|
||||
// is locking the engine necessary? copied from doUndoSample
|
||||
e->lockEngine([this,ins]() {
|
||||
ins->undo();
|
||||
cachedCurInsPtr=ins;
|
||||
cachedCurIns=*ins;
|
||||
});
|
||||
}
|
||||
|
||||
void FurnaceGUI::doRedoInstrument() {
|
||||
if (!insEditOpen) return;
|
||||
if (curIns<0 || curIns>=(int)e->song.ins.size()) return;
|
||||
DivInstrument* ins=e->song.ins[curIns];
|
||||
// is locking the engine necessary? copied from doRedoSample
|
||||
e->lockEngine([this,ins]() {
|
||||
ins->redo();
|
||||
cachedCurInsPtr=ins;
|
||||
cachedCurIns=*ins;
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -289,6 +289,7 @@ void FurnaceGUI::drawNewSong() {
|
|||
selEnd=SelectionPoint();
|
||||
cursor=SelectionPoint();
|
||||
updateWindowTitle();
|
||||
updateROMExportAvail();
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1500,6 +1500,7 @@ void FurnaceGUI::drawPattern() {
|
|||
i.cmd==DIV_CMD_HINT_PORTA ||
|
||||
i.cmd==DIV_CMD_HINT_LEGATO ||
|
||||
i.cmd==DIV_CMD_HINT_VOL_SLIDE ||
|
||||
i.cmd==DIV_CMD_HINT_VOL_SLIDE_TARGET ||
|
||||
i.cmd==DIV_CMD_HINT_ARPEGGIO ||
|
||||
i.cmd==DIV_CMD_HINT_PITCH ||
|
||||
i.cmd==DIV_CMD_HINT_VIBRATO ||
|
||||
|
|
|
|||
|
|
@ -26,6 +26,8 @@
|
|||
#include "imgui.h"
|
||||
#include "imgui_internal.h"
|
||||
|
||||
#include "../ta-utils.h"
|
||||
|
||||
struct FurnacePlotArrayGetterData
|
||||
{
|
||||
const float* Values;
|
||||
|
|
@ -270,12 +272,13 @@ int PlotBitfieldEx(const char* label, int (*values_getter)(void* data, int idx),
|
|||
float lineHeight=ImGui::GetTextLineHeight()/2.0;
|
||||
for (int i=0; i<bits && overlay_text[i]; i++) {
|
||||
ImGui::PushStyleColor(ImGuiCol_Text,ImVec4(0,0,0,1.0f));
|
||||
ImGui::RenderTextClipped(ImVec2(frame_bb.Min.x-1, frame_bb.Min.y-lineHeight-1), ImVec2(frame_bb.Max.x-1,frame_bb.Max.y+lineHeight-1), overlay_text[i], NULL, NULL, ImVec2(0.0f, (0.5+double(bits-1-i))/double(bits)));
|
||||
ImGui::RenderTextClipped(ImVec2(frame_bb.Min.x-1, frame_bb.Min.y-lineHeight+1), ImVec2(frame_bb.Max.x-1,frame_bb.Max.y+lineHeight+1), overlay_text[i], NULL, NULL, ImVec2(0.0f, (0.5+double(bits-1-i))/double(bits)));
|
||||
ImGui::RenderTextClipped(ImVec2(frame_bb.Min.x+1, frame_bb.Min.y-lineHeight-1), ImVec2(frame_bb.Max.x+1,frame_bb.Max.y+lineHeight-1), overlay_text[i], NULL, NULL, ImVec2(0.0f, (0.5+double(bits-1-i))/double(bits)));
|
||||
ImGui::RenderTextClipped(ImVec2(frame_bb.Min.x+1, frame_bb.Min.y-lineHeight+1), ImVec2(frame_bb.Max.x+1,frame_bb.Max.y+lineHeight+1), overlay_text[i], NULL, NULL, ImVec2(0.0f, (0.5+double(bits-1-i))/double(bits)));
|
||||
const char* text=_(overlay_text[i]);
|
||||
ImGui::RenderTextClipped(ImVec2(frame_bb.Min.x-1, frame_bb.Min.y-lineHeight-1), ImVec2(frame_bb.Max.x-1,frame_bb.Max.y+lineHeight-1), text, NULL, NULL, ImVec2(0.0f, (0.5+double(bits-1-i))/double(bits)));
|
||||
ImGui::RenderTextClipped(ImVec2(frame_bb.Min.x-1, frame_bb.Min.y-lineHeight+1), ImVec2(frame_bb.Max.x-1,frame_bb.Max.y+lineHeight+1), text, NULL, NULL, ImVec2(0.0f, (0.5+double(bits-1-i))/double(bits)));
|
||||
ImGui::RenderTextClipped(ImVec2(frame_bb.Min.x+1, frame_bb.Min.y-lineHeight-1), ImVec2(frame_bb.Max.x+1,frame_bb.Max.y+lineHeight-1), text, NULL, NULL, ImVec2(0.0f, (0.5+double(bits-1-i))/double(bits)));
|
||||
ImGui::RenderTextClipped(ImVec2(frame_bb.Min.x+1, frame_bb.Min.y-lineHeight+1), ImVec2(frame_bb.Max.x+1,frame_bb.Max.y+lineHeight+1), text, NULL, NULL, ImVec2(0.0f, (0.5+double(bits-1-i))/double(bits)));
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::RenderTextClipped(ImVec2(frame_bb.Min.x, frame_bb.Min.y-lineHeight), ImVec2(frame_bb.Max.x,frame_bb.Max.y+lineHeight), overlay_text[i], NULL, NULL, ImVec2(0.0f, (0.5+double(bits-1-i))/double(bits)));
|
||||
ImGui::RenderTextClipped(ImVec2(frame_bb.Min.x, frame_bb.Min.y-lineHeight), ImVec2(frame_bb.Max.x,frame_bb.Max.y+lineHeight), text, NULL, NULL, ImVec2(0.0f, (0.5+double(bits-1-i))/double(bits)));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1208,6 +1208,41 @@ void FurnaceGUI::initSystemPresets() {
|
|||
CH(DIV_SYSTEM_PCSPKR, 1.0f, 0, "")
|
||||
}
|
||||
);
|
||||
SUB_ENTRY(
|
||||
"Sega TeraDrive", {
|
||||
CH(DIV_SYSTEM_YM2612, 1.0f, 0, ""),
|
||||
CH(DIV_SYSTEM_SMS, 0.5f, 0, ""),
|
||||
CH(DIV_SYSTEM_PCSPKR, 1.0f, 0, "")
|
||||
}
|
||||
);
|
||||
SUB_SUB_ENTRY(
|
||||
"Sega TeraDrive (extended channel 3)", {
|
||||
CH(DIV_SYSTEM_YM2612_EXT, 1.0f, 0, ""),
|
||||
CH(DIV_SYSTEM_SMS, 0.5f, 0, ""),
|
||||
CH(DIV_SYSTEM_PCSPKR, 1.0f, 0, "")
|
||||
}
|
||||
);
|
||||
SUB_SUB_ENTRY(
|
||||
"Sega TeraDrive (CSM)", {
|
||||
CH(DIV_SYSTEM_YM2612_CSM, 1.0f, 0, ""),
|
||||
CH(DIV_SYSTEM_SMS, 0.5f, 0, ""),
|
||||
CH(DIV_SYSTEM_PCSPKR, 1.0f, 0, "")
|
||||
}
|
||||
);
|
||||
SUB_SUB_ENTRY(
|
||||
"Sega TeraDrive (DualPCM)", {
|
||||
CH(DIV_SYSTEM_YM2612_DUALPCM, 1.0f, 0, ""),
|
||||
CH(DIV_SYSTEM_SMS, 0.5f, 0, ""),
|
||||
CH(DIV_SYSTEM_PCSPKR, 1.0f, 0, "")
|
||||
}
|
||||
);
|
||||
SUB_SUB_ENTRY(
|
||||
"Sega TeraDrive (DualPCM, extended channel 3)", {
|
||||
CH(DIV_SYSTEM_YM2612_DUALPCM_EXT, 1.0f, 0, ""),
|
||||
CH(DIV_SYSTEM_SMS, 0.5f, 0, ""),
|
||||
CH(DIV_SYSTEM_PCSPKR, 1.0f, 0, "")
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"Sharp X1", {
|
||||
CH(DIV_SYSTEM_AY8910, 1.0f, 0, "clockSel=3")
|
||||
|
|
|
|||
|
|
@ -253,16 +253,27 @@ void FurnaceGUI::drawSampleEdit() {
|
|||
SAMPLE_WARN(warnLength,"QSound: maximum sample length is 65535");
|
||||
}
|
||||
break;
|
||||
case DIV_SYSTEM_NES:
|
||||
case DIV_SYSTEM_NES: {
|
||||
if (sample->loop) {
|
||||
if (sample->loopStart!=0 || sample->loopEnd!=(int)(sample->samples)) {
|
||||
SAMPLE_WARN(warnLoopPos,_("NES: loop point ignored on DPCM (may only loop entire sample)"));
|
||||
if (sample->loopStart&511) {
|
||||
int tryWith=(sample->loopStart)&(~511);
|
||||
if (tryWith>(int)sample->samples) tryWith-=512;
|
||||
String alignHint=fmt::sprintf(_("NES: loop start must be a multiple of 512 (try with %d)"),tryWith);
|
||||
SAMPLE_WARN(warnLoopStart,alignHint);
|
||||
}
|
||||
if ((sample->loopEnd-8)&127) {
|
||||
int tryWith=(sample->loopEnd-8)&(~127);
|
||||
if (tryWith>(int)sample->samples) tryWith-=128;
|
||||
tryWith+=8; // +1 bc of how sample length is treated: https://www.nesdev.org/wiki/APU_DMC
|
||||
String alignHint=fmt::sprintf(_("NES: loop end must be a multiple of 128 (try with %d)"),tryWith);
|
||||
SAMPLE_WARN(warnLoopEnd,alignHint);
|
||||
}
|
||||
}
|
||||
if (sample->samples>32648) {
|
||||
SAMPLE_WARN(warnLength,_("NES: maximum DPCM sample length is 32648"));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_SYSTEM_X1_010:
|
||||
if (sample->loop) {
|
||||
SAMPLE_WARN(warnLoop,_("X1-010: samples can't loop"));
|
||||
|
|
|
|||
|
|
@ -1084,15 +1084,19 @@ void FurnaceGUI::drawSettings() {
|
|||
ImGui::PushID(i);
|
||||
|
||||
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x-ImGui::CalcTextSize(_("Invert")).x-ImGui::GetFrameHeightWithSpacing()*2.0-ImGui::GetStyle().ItemSpacing.x*2.0);
|
||||
if (ImGui::BeginCombo("##System",getSystemName(sysID))) {
|
||||
for (int j=0; availableSystems[j]; j++) {
|
||||
if (ImGui::Selectable(getSystemName((DivSystem)availableSystems[j]),sysID==availableSystems[j])) {
|
||||
sysID=(DivSystem)availableSystems[j];
|
||||
settings.initialSys.set(fmt::sprintf("id%d",i),(int)e->systemToFileFur(sysID));
|
||||
settings.initialSys.set(fmt::sprintf("flags%d",i),"");
|
||||
settingsChanged=true;
|
||||
}
|
||||
if (ImGui::BeginCombo("##System",getSystemName(sysID),ImGuiComboFlags_HeightLargest)) {
|
||||
|
||||
sysID=systemPicker(true);
|
||||
|
||||
if (sysID!=DIV_SYSTEM_NULL)
|
||||
{
|
||||
settings.initialSys.set(fmt::sprintf("id%d",i),(int)e->systemToFileFur(sysID));
|
||||
settings.initialSys.set(fmt::sprintf("flags%d",i),"");
|
||||
settingsChanged=true;
|
||||
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
|
||||
|
|
@ -1254,6 +1258,14 @@ void FurnaceGUI::drawSettings() {
|
|||
}
|
||||
popDestColor();
|
||||
|
||||
// SUBSECTION IMPORT
|
||||
CONFIG_SUBSECTION(_("Import"));
|
||||
bool s3mOPL3B=settings.s3mOPL3;
|
||||
if (ImGui::Checkbox(_("Use OPL3 instead of OPL2 for S3M import"),&s3mOPL3B)) {
|
||||
settings.s3mOPL3=s3mOPL3B;
|
||||
settingsChanged=true;
|
||||
}
|
||||
|
||||
END_SECTION;
|
||||
}
|
||||
CONFIG_SECTION(_("Audio")) {
|
||||
|
|
@ -2415,6 +2427,9 @@ void FurnaceGUI::drawSettings() {
|
|||
UI_KEYBIND_CONFIG(GUI_ACTION_PAT_EXPAND_SONG);
|
||||
UI_KEYBIND_CONFIG(GUI_ACTION_PAT_LATCH);
|
||||
UI_KEYBIND_CONFIG(GUI_ACTION_PAT_CLEAR_LATCH);
|
||||
UI_KEYBIND_CONFIG(GUI_ACTION_PAT_ABSORB_INSTRUMENT);
|
||||
UI_KEYBIND_CONFIG(GUI_ACTION_PAT_CURSOR_UNDO);
|
||||
UI_KEYBIND_CONFIG(GUI_ACTION_PAT_CURSOR_REDO);
|
||||
|
||||
KEYBIND_CONFIG_END;
|
||||
ImGui::TreePop();
|
||||
|
|
@ -4747,6 +4762,8 @@ void FurnaceGUI::readConfig(DivConfig& conf, FurnaceGUISettingGroups groups) {
|
|||
settings.vibrationStrength=conf.getFloat("vibrationStrength",0.5f);
|
||||
settings.vibrationLength=conf.getInt("vibrationLength",20);
|
||||
|
||||
settings.s3mOPL3=conf.getInt("s3mOPL3",1);
|
||||
|
||||
settings.backupEnable=conf.getInt("backupEnable",1);
|
||||
settings.backupInterval=conf.getInt("backupInterval",30);
|
||||
settings.backupMaxCopies=conf.getInt("backupMaxCopies",5);
|
||||
|
|
@ -5259,6 +5276,7 @@ void FurnaceGUI::readConfig(DivConfig& conf, FurnaceGUISettingGroups groups) {
|
|||
clampSetting(settings.backupMaxCopies,1,100);
|
||||
clampSetting(settings.autoFillSave,0,1);
|
||||
clampSetting(settings.autoMacroStepSize,0,1);
|
||||
clampSetting(settings.s3mOPL3,0,1);
|
||||
|
||||
if (settings.exportLoops<0.0) settings.exportLoops=0.0;
|
||||
if (settings.exportFadeOut<0.0) settings.exportFadeOut=0.0;
|
||||
|
|
@ -5332,6 +5350,8 @@ void FurnaceGUI::writeConfig(DivConfig& conf, FurnaceGUISettingGroups groups) {
|
|||
conf.set("vibrationStrength",settings.vibrationStrength);
|
||||
conf.set("vibrationLength",settings.vibrationLength);
|
||||
|
||||
conf.set("s3mOPL3",settings.s3mOPL3);
|
||||
|
||||
conf.set("backupEnable",settings.backupEnable);
|
||||
conf.set("backupInterval",settings.backupInterval);
|
||||
conf.set("backupMaxCopies",settings.backupMaxCopies);
|
||||
|
|
|
|||
|
|
@ -22,18 +22,23 @@
|
|||
|
||||
// NOTE: please don't ask me to enable text wrap.
|
||||
// Dear ImGui doesn't have that feature. D:
|
||||
void FurnaceGUI::drawNotes() {
|
||||
void FurnaceGUI::drawNotes(bool asChild) {
|
||||
if (nextWindow==GUI_WINDOW_NOTES) {
|
||||
notesOpen=true;
|
||||
ImGui::SetNextWindowFocus();
|
||||
nextWindow=GUI_WINDOW_NOTHING;
|
||||
}
|
||||
if (!notesOpen) return;
|
||||
if (ImGui::Begin("Song Comments",¬esOpen,globalWinFlags,_("Song Comments"))) {
|
||||
if (!notesOpen && !asChild) return;
|
||||
bool began=asChild?ImGui::BeginChild("Song Info##Song Information"):ImGui::Begin("Song Comments",¬esOpen,globalWinFlags,_("Song Comments"));
|
||||
if (began) {
|
||||
if (ImGui::InputTextMultiline("##SongNotes",&e->song.notes,ImGui::GetContentRegionAvail(),ImGuiInputTextFlags_UndoRedo)) {
|
||||
MARK_MODIFIED;
|
||||
}
|
||||
}
|
||||
if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_NOTES;
|
||||
ImGui::End();
|
||||
if (!asChild && ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_NOTES;
|
||||
if (asChild) {
|
||||
ImGui::EndChild();
|
||||
} else {
|
||||
ImGui::End();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ void FurnaceGUI::drawSubSongs(bool asChild) {
|
|||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
if (ImGui::Selectable(id,i==e->getCurrentSubSong())) {
|
||||
makeCursorUndo();
|
||||
e->changeSongP(i);
|
||||
updateScroll(0);
|
||||
oldRow=0;
|
||||
|
|
@ -72,6 +73,7 @@ void FurnaceGUI::drawSubSongs(bool asChild) {
|
|||
if (!e->addSubSong()) {
|
||||
showError(_("too many subsongs!"));
|
||||
} else {
|
||||
makeCursorUndo();
|
||||
e->changeSongP(e->song.subsong.size()-1);
|
||||
updateScroll(0);
|
||||
oldRow=0;
|
||||
|
|
@ -92,6 +94,7 @@ void FurnaceGUI::drawSubSongs(bool asChild) {
|
|||
if (!e->duplicateSubSong(e->getCurrentSubSong())) {
|
||||
showError(_("too many subsongs!"));
|
||||
} else {
|
||||
makeCursorUndo();
|
||||
e->changeSongP(e->song.subsong.size()-1);
|
||||
updateScroll(0);
|
||||
oldRow=0;
|
||||
|
|
|
|||
|
|
@ -1969,6 +1969,9 @@ bool FurnaceGUI::drawSysConf(int chan, int sysPos, DivSystem type, DivConfig& fl
|
|||
echoFilter[6]=flags.getInt("echoFilter6",0);
|
||||
echoFilter[7]=flags.getInt("echoFilter7",0);
|
||||
|
||||
bool interpolationOff=flags.getBool("interpolationOff",false);
|
||||
bool antiClick=flags.getBool("antiClick",true);
|
||||
|
||||
ImGui::Text(_("Volume scale:"));
|
||||
if (CWSliderInt(_("Left##VolScaleL"),&vsL,0,127)) {
|
||||
if (vsL<0) vsL=0;
|
||||
|
|
@ -2084,6 +2087,14 @@ bool FurnaceGUI::drawSysConf(int chan, int sysPos, DivSystem type, DivConfig& fl
|
|||
ImGui::Text(_("sum: %d"),filterSum);
|
||||
ImGui::PopStyleColor();
|
||||
|
||||
if (ImGui::Checkbox(_("Disable Gaussian interpolation"),&interpolationOff)) {
|
||||
altered=true;
|
||||
}
|
||||
|
||||
if (ImGui::Checkbox(_("Anti-click"),&antiClick)) {
|
||||
altered=true;
|
||||
}
|
||||
|
||||
if (altered) {
|
||||
e->lockSave([&]() {
|
||||
flags.set("volScaleL",127-vsL);
|
||||
|
|
@ -2102,6 +2113,8 @@ bool FurnaceGUI::drawSysConf(int chan, int sysPos, DivSystem type, DivConfig& fl
|
|||
flags.set("echoFilter6",echoFilter[6]);
|
||||
flags.set("echoFilter7",echoFilter[7]);
|
||||
flags.set("echoMask",echoMask);
|
||||
flags.set("interpolationOff",interpolationOff);
|
||||
flags.set("antiClick",antiClick);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -2505,7 +2518,7 @@ bool FurnaceGUI::drawSysConf(int chan, int sysPos, DivSystem type, DivConfig& fl
|
|||
break;
|
||||
}
|
||||
case DIV_SYSTEM_VERA: {
|
||||
int chipType=flags.getInt("chipType",1);
|
||||
int chipType=flags.getInt("chipType",2);
|
||||
|
||||
ImGui::Text(_("Chip revision:"));
|
||||
ImGui::Indent();
|
||||
|
|
@ -2517,6 +2530,10 @@ bool FurnaceGUI::drawSysConf(int chan, int sysPos, DivSystem type, DivConfig& fl
|
|||
chipType=1;
|
||||
altered=true;
|
||||
}
|
||||
if (ImGui::RadioButton(_("V 47.0.2 (Tri/Saw PW XOR)"),chipType==2)) {
|
||||
chipType=2;
|
||||
altered=true;
|
||||
}
|
||||
ImGui::Unindent();
|
||||
|
||||
if (altered) {
|
||||
|
|
|
|||
|
|
@ -91,13 +91,18 @@ void FurnaceGUI::drawSysManager() {
|
|||
if (!e->duplicateSystem(i,sysDupCloneChannels,sysDupEnd)) {
|
||||
showError(fmt::sprintf(_("cannot clone chip! (%s)"),e->getLastError()));
|
||||
} else {
|
||||
if (e->song.autoSystem) {
|
||||
autoDetectSystem();
|
||||
updateWindowTitle();
|
||||
}
|
||||
updateROMExportAvail();
|
||||
MARK_MODIFIED;
|
||||
}
|
||||
}
|
||||
ImGui::SameLine();
|
||||
ImGui::Button(_("Change##SysChange"));
|
||||
if (ImGui::BeginPopupContextItem("SysPickerC",ImGuiPopupFlags_MouseButtonLeft)) {
|
||||
DivSystem picked=systemPicker();
|
||||
DivSystem picked=systemPicker(false);
|
||||
if (picked!=DIV_SYSTEM_NULL) {
|
||||
if (e->changeSystem(i,picked,preserveChanPos)) {
|
||||
MARK_MODIFIED;
|
||||
|
|
@ -105,6 +110,7 @@ void FurnaceGUI::drawSysManager() {
|
|||
autoDetectSystem();
|
||||
}
|
||||
updateWindowTitle();
|
||||
updateROMExportAvail();
|
||||
} else {
|
||||
showError(fmt::sprintf(_("cannot change chip! (%s)"),e->getLastError()));
|
||||
}
|
||||
|
|
@ -132,7 +138,7 @@ void FurnaceGUI::drawSysManager() {
|
|||
ImGui::TableNextColumn();
|
||||
ImGui::Button(ICON_FA_PLUS "##SysAdd");
|
||||
if (ImGui::BeginPopupContextItem("SysPickerA",ImGuiPopupFlags_MouseButtonLeft)) {
|
||||
DivSystem picked=systemPicker();
|
||||
DivSystem picked=systemPicker(false);
|
||||
if (picked!=DIV_SYSTEM_NULL) {
|
||||
if (!e->addSystem(picked)) {
|
||||
showError(fmt::sprintf(_("cannot add chip! (%s)"),e->getLastError()));
|
||||
|
|
@ -143,6 +149,7 @@ void FurnaceGUI::drawSysManager() {
|
|||
autoDetectSystem();
|
||||
}
|
||||
updateWindowTitle();
|
||||
updateROMExportAvail();
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@
|
|||
#include "guiConst.h"
|
||||
#include <imgui.h>
|
||||
|
||||
DivSystem FurnaceGUI::systemPicker() {
|
||||
DivSystem FurnaceGUI::systemPicker(bool fullWidth) {
|
||||
DivSystem ret=DIV_SYSTEM_NULL;
|
||||
DivSystem hoveredSys=DIV_SYSTEM_NULL;
|
||||
bool reissueSearch=false;
|
||||
|
|
@ -61,7 +61,7 @@ DivSystem FurnaceGUI::systemPicker() {
|
|||
}
|
||||
}
|
||||
}
|
||||
if (ImGui::BeginTable("SysList",1,ImGuiTableFlags_ScrollY,ImVec2(500.0f*dpiScale,200.0*dpiScale))) {
|
||||
if (ImGui::BeginTable("SysList",1,ImGuiTableFlags_ScrollY,ImVec2(fullWidth ? ImGui::GetContentRegionAvail().x : 500.0f*dpiScale,200.0f*dpiScale))) {
|
||||
if (sysSearchQuery.empty()) {
|
||||
// display chip list
|
||||
for (int j=0; curSysSection[j]; j++) {
|
||||
|
|
|
|||
|
|
@ -392,7 +392,7 @@ void FurnaceGUI::drawUserPresets() {
|
|||
tempID=fmt::sprintf("%s##USystem",getSystemName(chip.sys));
|
||||
ImGui::Button(tempID.c_str(),ImVec2(ImGui::GetContentRegionAvail().x-ImGui::CalcTextSize(_("Invert")).x-ImGui::GetFrameHeightWithSpacing()*2.0-ImGui::GetStyle().ItemSpacing.x*2.0,0));
|
||||
if (ImGui::BeginPopupContextItem("SysPickerCU",ImGuiPopupFlags_MouseButtonLeft)) {
|
||||
DivSystem picked=systemPicker();
|
||||
DivSystem picked=systemPicker(false);
|
||||
if (picked!=DIV_SYSTEM_NULL) {
|
||||
chip.sys=picked;
|
||||
mustBake=true;
|
||||
|
|
@ -456,7 +456,7 @@ void FurnaceGUI::drawUserPresets() {
|
|||
|
||||
ImGui::Button(ICON_FA_PLUS "##SysAddU");
|
||||
if (ImGui::BeginPopupContextItem("SysPickerU",ImGuiPopupFlags_MouseButtonLeft)) {
|
||||
DivSystem picked=systemPicker();
|
||||
DivSystem picked=systemPicker(false);
|
||||
if (picked!=DIV_SYSTEM_NULL) {
|
||||
preset->orig.push_back(FurnaceGUISysDefChip(picked,1.0f,0.0f,""));
|
||||
mustBake=true;
|
||||
|
|
@ -475,7 +475,8 @@ void FurnaceGUI::drawUserPresets() {
|
|||
ImGui::SetTooltip(_(
|
||||
"insert additional settings in `option=value` format.\n"
|
||||
"available options:\n"
|
||||
"- tickRate"
|
||||
"- tickRate \n"
|
||||
"- chanMask \n"
|
||||
));
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue