From 7e5c27c5b7eb835db861e52e2503989056cba708 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 21 Mar 2022 17:34:43 -0500 Subject: [PATCH] GUI: massive code split gui.cpp now less than 3000 lines --- CMakeLists.txt | 19 + src/gui/about.cpp | 211 ++ src/gui/actionUtil.h | 21 + src/gui/channels.cpp | 53 + src/gui/compatFlags.cpp | 136 ++ src/gui/cursor.cpp | 285 +++ src/gui/dataList.cpp | 19 + src/gui/debugWindow.cpp | 250 ++ src/gui/doAction.cpp | 687 ++++++ src/gui/editControls.cpp | 330 +++ src/gui/editing.cpp | 1015 ++++++++ src/gui/gui.cpp | 4802 +------------------------------------- src/gui/gui.h | 7 + src/gui/guiConst.cpp | 46 +- src/gui/guiConst.h | 3 +- src/gui/mixer.cpp | 59 + src/gui/newSong.cpp | 90 + src/gui/osc.cpp | 47 + src/gui/piano.cpp | 46 + src/gui/presets.cpp | 639 +++++ src/gui/regView.cpp | 71 + src/gui/settings.cpp | 397 ++++ src/gui/songInfo.cpp | 173 ++ src/gui/songNotes.cpp | 37 + src/gui/stats.cpp | 50 + src/gui/sysConf.cpp | 371 +++ src/gui/volMeter.cpp | 111 + src/gui/waveEdit.cpp | 19 + 28 files changed, 5201 insertions(+), 4793 deletions(-) create mode 100644 src/gui/about.cpp create mode 100644 src/gui/actionUtil.h create mode 100644 src/gui/channels.cpp create mode 100644 src/gui/compatFlags.cpp create mode 100644 src/gui/cursor.cpp create mode 100644 src/gui/debugWindow.cpp create mode 100644 src/gui/doAction.cpp create mode 100644 src/gui/editControls.cpp create mode 100644 src/gui/editing.cpp create mode 100644 src/gui/mixer.cpp create mode 100644 src/gui/newSong.cpp create mode 100644 src/gui/osc.cpp create mode 100644 src/gui/piano.cpp create mode 100644 src/gui/presets.cpp create mode 100644 src/gui/regView.cpp create mode 100644 src/gui/songInfo.cpp create mode 100644 src/gui/songNotes.cpp create mode 100644 src/gui/stats.cpp create mode 100644 src/gui/sysConf.cpp create mode 100644 src/gui/volMeter.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 8f51dc9e0..f09c88a64 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -357,14 +357,33 @@ src/gui/fileDialog.cpp src/gui/intConst.cpp src/gui/guiConst.cpp +src/gui/about.cpp +src/gui/channels.cpp +src/gui/compatFlags.cpp +src/gui/cursor.cpp src/gui/dataList.cpp +src/gui/debugWindow.cpp +src/gui/doAction.cpp +src/gui/editing.cpp +src/gui/editControls.cpp src/gui/insEdit.cpp +src/gui/mixer.cpp +src/gui/newSong.cpp src/gui/orders.cpp +src/gui/osc.cpp src/gui/pattern.cpp +src/gui/piano.cpp +src/gui/presets.cpp +src/gui/regView.cpp src/gui/sampleEdit.cpp src/gui/settings.cpp +src/gui/songInfo.cpp +src/gui/songNotes.cpp +src/gui/stats.cpp +src/gui/sysConf.cpp src/gui/util.cpp src/gui/waveEdit.cpp +src/gui/volMeter.cpp src/gui/gui.cpp ) diff --git a/src/gui/about.cpp b/src/gui/about.cpp new file mode 100644 index 000000000..cd5888c61 --- /dev/null +++ b/src/gui/about.cpp @@ -0,0 +1,211 @@ +/** + * 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" + +const char* aboutLine[]={ + "tildearrow", + "is proud to present", + "", + ("Furnace " DIV_VERSION), + "", + "the free software chiptune tracker,", + "compatible with DefleMask modules.", + "", + "zero disassembly.", + "just clean-room design,", + "time and dedication.", + "", + "> CREDITS <", + "", + "-- program --", + "tildearrow", + "akumanatt", + "cam900", + "djtuBIG-MaliceX", + "laoo", + "superctr", + "", + "-- graphics/UI design --", + "tildearrow", + "BlastBrothers", + "", + "-- documentation --", + "tildearrow", + "freq-mod", + "nicco1690", + "DeMOSic", + "cam900", + "", + "-- demo songs --", + "0x5066", + "ActualNK358", + "breakthetargets", + "CaptainMalware", + "kleeder", + "Mahbod Karamoozian", + "nicco1690", + "NikonTeen", + "SuperJet Spade", + "TheDuccinator", + "TheRealHedgehogSonic", + "tildearrow", + "Ultraprogramer", + "", + "-- additional feedback/fixes --", + "fd", + "OPNA2608", + "plane", + "TheEssem", + "", + "powered by:", + "Dear ImGui by Omar Cornut", + "SDL2 by Sam Lantinga", + "zlib by Jean-loup Gailly", + "and Mark Adler", + "libsndfile by Erik de Castro Lopo", + "Nuked-OPM & Nuked-OPN2 by Nuke.YKT", + "ymfm by Aaron Giles", + "MAME SN76496 by Nicola Salmoria", + "MAME AY-3-8910 by Couriersud", + "with AY8930 fixes by Eulous", + "MAME SAA1099 by Juergen Buchmueller and Manuel Abadia", + "SAASound", + "SameBoy by Lior Halphon", + "Mednafen PCE", + "puNES by FHorse", + "reSID by Dag Lem", + "Stella by Stella Team", + "QSound emulator by Ian Karlsson and Valley Bell", + "", + "greetings to:", + "Delek", + "fd", + "ILLUMIDARO", + "all members of Deflers of Noice!", + "", + "copyright © 2021-2022 tildearrow", + "(and contributors).", + "licensed under GPLv2+! see", + "LICENSE for more information.", + "", + "help Furnace grow:", + "https://github.com/tildearrow/furnace", + "", + "contact tildearrow at:", + "https://tildearrow.org/?p=contact", + "", + "disclaimer:", + "despite the fact this program works", + "with the .dmf file format, it is NOT", + "affiliated with Delek or DefleMask in", + "any way, nor it is a replacement for", + "the original program.", + "", + "it also comes with ABSOLUTELY NO WARRANTY.", + "", + "thanks to all contributors/bug reporters!" +}; + +const size_t aboutCount=sizeof(aboutLine)/sizeof(aboutLine[0]); + +void FurnaceGUI::drawAbout() { + // do stuff + if (ImGui::Begin("About Furnace",NULL,ImGuiWindowFlags_Modal|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoDocking|ImGuiWindowFlags_NoTitleBar)) { + ImGui::SetWindowPos(ImVec2(0,0)); + ImGui::SetWindowSize(ImVec2(scrW*dpiScale,scrH*dpiScale)); + ImGui::PushFont(bigFont); + ImDrawList* dl=ImGui::GetWindowDrawList(); + float r=0; + float g=0; + float b=0; + float peakMix=settings.partyTime?((peak[0]+peak[1])*0.5):0.3; + ImGui::ColorConvertHSVtoRGB(aboutHue,1.0,0.25+MIN(0.75f,peakMix*0.75f),r,g,b); + dl->AddRectFilled(ImVec2(0,0),ImVec2(scrW*dpiScale,scrH*dpiScale),0xff000000); + bool skip=false; + bool skip2=false; + for (int i=(-80-sin(double(aboutSin)*2*M_PI/120.0)*80.0)*2; iAddRectFilled(ImVec2(i*dpiScale,j*dpiScale),ImVec2((i+160)*dpiScale,(j+160)*dpiScale),ImGui::GetColorU32(ImVec4(r*0.25,g*0.25,b*0.25,1.0))); + } + } + + skip=false; + skip2=false; + for (int i=(-80-cos(double(aboutSin)*2*M_PI/120.0)*80.0)*2; iAddRectFilled(ImVec2(i*dpiScale,j*dpiScale),ImVec2((i+160)*dpiScale,(j+160)*dpiScale),ImGui::GetColorU32(ImVec4(r*0.5,g*0.5,b*0.5,1.0))); + } + } + + skip=false; + skip2=false; + for (int i=(-160+fmod(aboutSin*2,160))*2; iAddRectFilled(ImVec2(i*dpiScale,j*dpiScale),ImVec2((i+160)*dpiScale,(j+160)*dpiScale),ImGui::GetColorU32(ImVec4(r*0.75,g*0.75,b*0.75,1.0))); + } + } + + for (size_t i=0; iscrH*dpiScale) continue; + dl->AddText(bigFont,bigFont->FontSize, + ImVec2(posX+dpiScale,posY+dpiScale), + 0xff000000,aboutLine[i]); + dl->AddText(bigFont,bigFont->FontSize, + ImVec2(posX+dpiScale,posY-dpiScale), + 0xff000000,aboutLine[i]); + dl->AddText(bigFont,bigFont->FontSize, + ImVec2(posX-dpiScale,posY+dpiScale), + 0xff000000,aboutLine[i]); + dl->AddText(bigFont,bigFont->FontSize, + ImVec2(posX-dpiScale,posY-dpiScale), + 0xff000000,aboutLine[i]); + dl->AddText(bigFont,bigFont->FontSize, + ImVec2(posX,posY), + 0xffffffff,aboutLine[i]); + } + ImGui::PopFont(); + + float timeScale=60.0f*ImGui::GetIO().DeltaTime; + + aboutHue+=(0.001+peakMix*0.004)*timeScale; + aboutScroll+=(2+(peakMix>0.78)*3)*timeScale; + aboutSin+=(1+(peakMix>0.75)*2)*timeScale; + + while (aboutHue>1) aboutHue--; + while (aboutSin>=2400) aboutSin-=2400; + if (aboutScroll>(42*aboutCount+scrH)) aboutScroll=-20; + } + if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_ABOUT; + ImGui::End(); +} \ No newline at end of file diff --git a/src/gui/actionUtil.h b/src/gui/actionUtil.h new file mode 100644 index 000000000..38ed9a5cd --- /dev/null +++ b/src/gui/actionUtil.h @@ -0,0 +1,21 @@ +#define DETERMINE_FIRST \ + int firstChannel=0; \ + for (int i=0; igetTotalChannelCount(); i++) { \ + if (e->song.chanShow[i]) { \ + firstChannel=i; \ + break; \ + } \ + } \ + +#define DETERMINE_LAST \ + int lastChannel=0; \ + for (int i=e->getTotalChannelCount()-1; i>=0; i--) { \ + if (e->song.chanShow[i]) { \ + lastChannel=i+1; \ + break; \ + } \ + } + +#define DETERMINE_FIRST_LAST \ + DETERMINE_FIRST \ + DETERMINE_LAST \ No newline at end of file diff --git a/src/gui/channels.cpp b/src/gui/channels.cpp new file mode 100644 index 000000000..5b2a7e0c6 --- /dev/null +++ b/src/gui/channels.cpp @@ -0,0 +1,53 @@ +/** + * 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" + +void FurnaceGUI::drawChannels() { + if (nextWindow==GUI_WINDOW_CHANNELS) { + channelsOpen=true; + ImGui::SetNextWindowFocus(); + nextWindow=GUI_WINDOW_NOTHING; + } + if (!channelsOpen) return; + if (ImGui::Begin("Channels",&channelsOpen)) { + if (ImGui::BeginTable("ChannelList",3)) { + ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthFixed,0.0); + ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthStretch,0.0); + ImGui::TableSetupColumn("c2",ImGuiTableColumnFlags_WidthFixed,48.0f*dpiScale); + for (int i=0; igetTotalChannelCount(); i++) { + ImGui::PushID(i); + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::Checkbox("##Visible",&e->song.chanShow[i]); + ImGui::TableNextColumn(); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + ImGui::InputTextWithHint("##ChanName",e->getChannelName(i),&e->song.chanName[i]); + ImGui::TableNextColumn(); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + ImGui::InputTextWithHint("##ChanShortName",e->getChannelShortName(i),&e->song.chanShortName[i]); + ImGui::PopID(); + } + ImGui::EndTable(); + } + } + if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_CHANNELS; + ImGui::End(); +} \ No newline at end of file diff --git a/src/gui/compatFlags.cpp b/src/gui/compatFlags.cpp new file mode 100644 index 000000000..46424fc52 --- /dev/null +++ b/src/gui/compatFlags.cpp @@ -0,0 +1,136 @@ +/** + * 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" + +void FurnaceGUI::drawCompatFlags() { + if (nextWindow==GUI_WINDOW_COMPAT_FLAGS) { + compatFlagsOpen=true; + ImGui::SetNextWindowFocus(); + nextWindow=GUI_WINDOW_NOTHING; + } + if (!compatFlagsOpen) return; + if (ImGui::Begin("Compatibility Flags",&compatFlagsOpen)) { + ImGui::TextWrapped("these flags are designed to provide better DefleMask/older Furnace compatibility."); + ImGui::Checkbox("Limit slide range",&e->song.limitSlides); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("when enabled, slides are limited to a compatible range.\nmay cause problems with slides in negative octaves."); + } + ImGui::Checkbox("Linear pitch control",&e->song.linearPitch); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("linear pitch:\n- slides work in frequency/period space\n- E5xx and 04xx effects work in tonality space\nnon-linear pitch:\n- slides work in frequency/period space\n- E5xx and 04xx effects work on frequency/period space"); + } + ImGui::Checkbox("Proper noise layout on NES and PC Engine",&e->song.properNoiseLayout); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("use a proper noise channel note mapping (0-15) instead of a rather unusual compatible one.\nunlocks all noise frequencies on PC Engine."); + } + ImGui::Checkbox("Game Boy instrument duty is wave volume",&e->song.waveDutyIsVol); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("if enabled, an instrument with duty macro in the wave channel will be mapped to wavetable volume."); + } + + ImGui::Checkbox("Restart macro on portamento",&e->song.resetMacroOnPorta); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("when enabled, a portamento effect will reset the channel's macro if used in combination with a note."); + } + ImGui::Checkbox("Legacy volume slides",&e->song.legacyVolumeSlides); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("simulate glitchy volume slide behavior by silently overflowing the volume when the slide goes below 0."); + } + ImGui::Checkbox("Compatible arpeggio",&e->song.compatibleArpeggio); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("delay arpeggio by one tick on every new note."); + } + ImGui::Checkbox("Reset slides after note off",&e->song.noteOffResetsSlides); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("when enabled, note off will reset the channel's slide effect."); + } + ImGui::Checkbox("Reset portamento after reaching target",&e->song.targetResetsSlides); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("when enabled, the slide effect is disabled after it reaches its target."); + } + ImGui::Checkbox("Ignore duplicate slide effects",&e->song.ignoreDuplicateSlides); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("if this is on, only the first slide of a row in a channel will be considered."); + } + ImGui::Checkbox("Continuous vibrato",&e->song.continuousVibrato); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("when enabled, vibrato will not be reset on a new note."); + } + ImGui::Checkbox("Broken DAC mode",&e->song.brokenDACMode); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("when enabled, the DAC in YM2612 will be disabled if there isn't any sample playing."); + } + ImGui::Checkbox("Auto-insert one tick gap between notes",&e->song.oneTickCut); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("when enabled, a one-tick note cut will be inserted between non-legato/non-portamento notes.\nthis simulates the behavior of some Amiga/SNES music engines."); + } + + ImGui::Text("Loop modality:"); + if (ImGui::RadioButton("Reset channels",e->song.loopModality==0)) { + e->song.loopModality=0; + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("select to reset channels on loop. may trigger a voltage click on every loop!"); + } + if (ImGui::RadioButton("Soft reset channels",e->song.loopModality==1)) { + e->song.loopModality=1; + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("select to turn channels off on loop."); + } + if (ImGui::RadioButton("Do nothing",e->song.loopModality==2)) { + e->song.loopModality=2; + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("select to not reset channels on loop."); + } + + ImGui::Separator(); + + ImGui::TextWrapped("the following flags are for compatibility with older Furnace versions."); + + ImGui::Checkbox("Arpeggio inhibits non-porta slides",&e->song.arpNonPorta); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("behavior changed in 0.5.5"); + } + ImGui::Checkbox("Wack FM algorithm macro",&e->song.algMacroBehavior); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("behavior changed in 0.5.5"); + } + ImGui::Checkbox("Broken shortcut slides (E1xy/E2xy)",&e->song.brokenShortcutSlides); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("behavior changed in 0.5.7"); + } + ImGui::Checkbox("Stop portamento on note off",&e->song.stopPortaOnNoteOff); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("behavior changed in 0.6"); + } + ImGui::Checkbox("Allow instrument change during slides",&e->song.newInsTriggersInPorta); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("behavior changed in 0.6"); + } + ImGui::Checkbox("Reset note to base on arpeggio stop",&e->song.arp0Reset); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("behavior changed in 0.6"); + } + } + if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_COMPAT_FLAGS; + ImGui::End(); +} \ No newline at end of file diff --git a/src/gui/cursor.cpp b/src/gui/cursor.cpp new file mode 100644 index 000000000..c783856a7 --- /dev/null +++ b/src/gui/cursor.cpp @@ -0,0 +1,285 @@ +/** + * 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 "actionUtil.h" + +void FurnaceGUI::startSelection(int xCoarse, int xFine, int y) { + if (xCoarse!=selStart.xCoarse || xFine!=selStart.xFine || y!=selStart.y) { + curNibble=false; + } + cursor.xCoarse=xCoarse; + cursor.xFine=xFine; + cursor.y=y; + selStart.xCoarse=xCoarse; + selStart.xFine=xFine; + selStart.y=y; + selEnd.xCoarse=xCoarse; + selEnd.xFine=xFine; + selEnd.y=y; + selecting=true; +} + +void FurnaceGUI::updateSelection(int xCoarse, int xFine, int y) { + if (!selecting) return; + selEnd.xCoarse=xCoarse; + selEnd.xFine=xFine; + selEnd.y=y; +} + +void FurnaceGUI::finishSelection() { + // swap points if needed + if (selEnd.ygetTotalChannelCount(); + + if (selStart.xCoarse<0) selStart.xCoarse=0; + if (selStart.xCoarse>=chanCount) selStart.xCoarse=chanCount-1; + if (selStart.y<0) selStart.y=0; + if (selStart.y>=e->song.patLen) selStart.y=e->song.patLen-1; + if (selEnd.xCoarse<0) selEnd.xCoarse=0; + if (selEnd.xCoarse>=chanCount) selEnd.xCoarse=chanCount-1; + if (selEnd.y<0) selEnd.y=0; + if (selEnd.y>=e->song.patLen) selEnd.y=e->song.patLen-1; + if (cursor.xCoarse<0) cursor.xCoarse=0; + if (cursor.xCoarse>=chanCount) cursor.xCoarse=chanCount-1; + if (cursor.y<0) cursor.y=0; + if (cursor.y>=e->song.patLen) cursor.y=e->song.patLen-1; + + if (e->song.chanCollapse[selEnd.xCoarse]) { + selStart.xFine=0; + } + if (e->song.chanCollapse[selEnd.xCoarse]) { + selEnd.xFine=2+e->song.pat[cursor.xCoarse].effectRows*2; + } +} + +void FurnaceGUI::moveCursor(int x, int y, bool select) { + if (!select) { + finishSelection(); + } + + DETERMINE_FIRST_LAST; + + curNibble=false; + if (x!=0) { + demandScrollX=true; + if (x>0) { + for (int i=0; i=(e->song.chanCollapse[cursor.xCoarse]?1:(3+e->song.pat[cursor.xCoarse].effectRows*2))) { + cursor.xFine=0; + if (++cursor.xCoarse>=lastChannel) { + if (settings.wrapHorizontal!=0 && !select) { + cursor.xCoarse=firstChannel; + if (settings.wrapHorizontal==2) y++; + } else { + cursor.xCoarse=lastChannel-1; + cursor.xFine=e->song.chanCollapse[cursor.xCoarse]?0:(2+e->song.pat[cursor.xCoarse].effectRows*2); + } + } else { + while (!e->song.chanShow[cursor.xCoarse]) { + cursor.xCoarse++; + if (cursor.xCoarse>=e->getTotalChannelCount()) break; + } + } + } + } + } else { + for (int i=0; i<-x; i++) { + if (--cursor.xFine<0) { + if (--cursor.xCoarsesong.pat[cursor.xCoarse].effectRows*2; + if (settings.wrapHorizontal==2) y--; + } else { + cursor.xCoarse=firstChannel; + cursor.xFine=0; + } + } else { + while (!e->song.chanShow[cursor.xCoarse]) { + cursor.xCoarse--; + if (cursor.xCoarse<0) break; + } + if (e->song.chanCollapse[cursor.xCoarse]) { + cursor.xFine=0; + } else { + cursor.xFine=2+e->song.pat[cursor.xCoarse].effectRows*2; + } + } + } + } + } + } + if (y!=0) { + if (y>0) { + for (int i=0; i=e->song.patLen) { + if (settings.wrapVertical!=0 && !select) { + cursor.y=0; + if (settings.wrapVertical==2) { + if (!e->isPlaying() && e->getOrder()<(e->song.ordersLen-1)) { + e->setOrder(e->getOrder()+1); + } else { + cursor.y=e->song.patLen-1; + } + } + } else { + cursor.y=e->song.patLen-1; + } + } + } + } else { + for (int i=0; i<-y; i++) { + cursor.y--; + if (cursor.y<0) { + if (settings.wrapVertical!=0 && !select) { + cursor.y=e->song.patLen-1; + if (settings.wrapVertical==2) { + if (!e->isPlaying() && e->getOrder()>0) { + e->setOrder(e->getOrder()-1); + } else { + cursor.y=0; + } + } + } else { + cursor.y=0; + } + } + } + } + } + if (!select) { + selStart=cursor; + } + selEnd=cursor; + updateScroll(cursor.y); +} + +void FurnaceGUI::moveCursorPrevChannel(bool overflow) { + finishSelection(); + curNibble=false; + + DETERMINE_FIRST_LAST; + + do { + cursor.xCoarse--; + if (cursor.xCoarse<0) break; + } while (!e->song.chanShow[cursor.xCoarse]); + if (cursor.xCoarse=e->getTotalChannelCount()) break; + } while (!e->song.chanShow[cursor.xCoarse]); + if (cursor.xCoarse>=lastChannel) { + if (overflow) { + cursor.xCoarse=firstChannel; + } else { + cursor.xCoarse=lastChannel-1; + } + } + + selStart=cursor; + selEnd=cursor; + demandScrollX=true; +} + +void FurnaceGUI::moveCursorTop(bool select) { + finishSelection(); + curNibble=false; + if (cursor.y==0) { + DETERMINE_FIRST; + cursor.xCoarse=firstChannel; + cursor.xFine=0; + demandScrollX=true; + } else { + cursor.y=0; + } + selStart=cursor; + if (!select) { + selEnd=cursor; + } + updateScroll(cursor.y); +} + +void FurnaceGUI::moveCursorBottom(bool select) { + finishSelection(); + curNibble=false; + if (cursor.y==e->song.patLen-1) { + DETERMINE_LAST; + cursor.xCoarse=lastChannel-1; + cursor.xFine=2+e->song.pat[cursor.xCoarse].effectRows*2; + demandScrollX=true; + } else { + cursor.y=e->song.patLen-1; + } + if (!select) { + selStart=cursor; + } + selEnd=cursor; + updateScroll(cursor.y); +} + +void FurnaceGUI::editAdvance() { + finishSelection(); + cursor.y+=editStep; + if (cursor.y>=e->song.patLen) cursor.y=e->song.patLen-1; + selStart=cursor; + selEnd=cursor; + updateScroll(cursor.y); +} \ No newline at end of file diff --git a/src/gui/dataList.cpp b/src/gui/dataList.cpp index f32ffda77..d6c88a78e 100644 --- a/src/gui/dataList.cpp +++ b/src/gui/dataList.cpp @@ -1,3 +1,22 @@ +/** + * 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 "IconsFontAwesome4.h" #include "misc/cpp/imgui_stdlib.h" diff --git a/src/gui/debugWindow.cpp b/src/gui/debugWindow.cpp new file mode 100644 index 000000000..31830681f --- /dev/null +++ b/src/gui/debugWindow.cpp @@ -0,0 +1,250 @@ +/** + * 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 "debug.h" +#include "IconsFontAwesome4.h" +#include + +void FurnaceGUI::drawDebug() { + static int bpOrder; + static int bpRow; + static int bpTick; + static bool bpOn; + if (nextWindow==GUI_WINDOW_DEBUG) { + debugOpen=true; + ImGui::SetNextWindowFocus(); + nextWindow=GUI_WINDOW_NOTHING; + } + if (!debugOpen) return; + ImGui::SetNextWindowSizeConstraints(ImVec2(400.0f*dpiScale,200.0f*dpiScale),ImVec2(scrW*dpiScale,scrH*dpiScale)); + if (ImGui::Begin("Debug",&debugOpen,ImGuiWindowFlags_NoDocking)) { + ImGui::Text("NOTE: use with caution."); + if (ImGui::TreeNode("Debug Controls")) { + if (e->isHalted()) { + if (ImGui::Button("Resume")) e->resume(); + } else { + if (ImGui::Button("Pause")) e->halt(); + } + ImGui::SameLine(); + if (ImGui::Button("Frame Advance")) e->haltWhen(DIV_HALT_TICK); + ImGui::SameLine(); + if (ImGui::Button("Row Advance")) e->haltWhen(DIV_HALT_ROW); + ImGui::SameLine(); + if (ImGui::Button("Pattern Advance")) e->haltWhen(DIV_HALT_PATTERN); + + if (ImGui::Button("Panic")) e->syncReset(); + ImGui::SameLine(); + if (ImGui::Button("Abort")) { + abort(); + } + ImGui::TreePop(); + } + if (ImGui::TreeNode("Breakpoint")) { + ImGui::InputInt("Order",&bpOrder); + ImGui::InputInt("Row",&bpRow); + ImGui::InputInt("Tick",&bpTick); + ImGui::Checkbox("Enable",&bpOn); + ImGui::TreePop(); + } + if (ImGui::TreeNode("Dispatch Status")) { + ImGui::Text("for best results set latency to minimum or use the Frame Advance button."); + ImGui::Columns(e->getTotalChannelCount()); + for (int i=0; igetTotalChannelCount(); i++) { + void* ch=e->getDispatchChanState(i); + ImGui::TextColored(uiColors[GUI_COLOR_ACCENT_PRIMARY],"Ch. %d: %d, %d",i,e->dispatchOfChan[i],e->dispatchChanOfChan[i]); + if (ch==NULL) { + ImGui::Text("NULL"); + } else { + putDispatchChan(ch,e->dispatchChanOfChan[i],e->sysOfChan[i]); + } + ImGui::NextColumn(); + } + ImGui::Columns(); + ImGui::TreePop(); + } + if (ImGui::TreeNode("Playback Status")) { + ImGui::Text("for best results set latency to minimum or use the Frame Advance button."); + ImGui::Columns(e->getTotalChannelCount()); + for (int i=0; igetTotalChannelCount(); i++) { + DivChannelState* ch=e->getChanState(i); + ImGui::TextColored(uiColors[GUI_COLOR_ACCENT_PRIMARY],"Channel %d:",i); + if (ch==NULL) { + ImGui::Text("NULL"); + } else { + ImGui::Text("* General:"); + ImGui::Text("- note = %d",ch->note); + ImGui::Text("- oldNote = %d",ch->oldNote); + ImGui::Text("- pitch = %d",ch->pitch); + ImGui::Text("- portaSpeed = %d",ch->portaSpeed); + ImGui::Text("- portaNote = %d",ch->portaNote); + ImGui::Text("- volume = %.4x",ch->volume); + ImGui::Text("- volSpeed = %d",ch->volSpeed); + ImGui::Text("- cut = %d",ch->cut); + ImGui::Text("- rowDelay = %d",ch->rowDelay); + ImGui::Text("- volMax = %.4x",ch->volMax); + ImGui::Text("- delayOrder = %d",ch->delayOrder); + ImGui::Text("- delayRow = %d",ch->delayRow); + ImGui::Text("- retrigSpeed = %d",ch->retrigSpeed); + ImGui::Text("- retrigTick = %d",ch->retrigTick); + ImGui::PushStyleColor(ImGuiCol_Text,(ch->vibratoDepth>0)?uiColors[GUI_COLOR_MACRO_VOLUME]:uiColors[GUI_COLOR_TEXT]); + ImGui::Text("* Vibrato:"); + ImGui::Text("- depth = %d",ch->vibratoDepth); + ImGui::Text("- rate = %d",ch->vibratoRate); + ImGui::Text("- pos = %d",ch->vibratoPos); + ImGui::Text("- dir = %d",ch->vibratoDir); + ImGui::Text("- fine = %d",ch->vibratoFine); + ImGui::PopStyleColor(); + ImGui::PushStyleColor(ImGuiCol_Text,(ch->tremoloDepth>0)?uiColors[GUI_COLOR_MACRO_VOLUME]:uiColors[GUI_COLOR_TEXT]); + ImGui::Text("* Tremolo:"); + ImGui::Text("- depth = %d",ch->tremoloDepth); + ImGui::Text("- rate = %d",ch->tremoloRate); + ImGui::Text("- pos = %d",ch->tremoloPos); + ImGui::PopStyleColor(); + ImGui::PushStyleColor(ImGuiCol_Text,(ch->arp>0)?uiColors[GUI_COLOR_MACRO_VOLUME]:uiColors[GUI_COLOR_TEXT]); + ImGui::Text("* Arpeggio:"); + ImGui::Text("- arp = %.2X",ch->arp); + ImGui::Text("- stage = %d",ch->arpStage); + ImGui::Text("- ticks = %d",ch->arpTicks); + ImGui::PopStyleColor(); + ImGui::Text("* Miscellaneous:"); + ImGui::TextColored(ch->doNote?uiColors[GUI_COLOR_MACRO_VOLUME]:uiColors[GUI_COLOR_HEADER],">> Do Note"); + ImGui::TextColored(ch->legato?uiColors[GUI_COLOR_MACRO_VOLUME]:uiColors[GUI_COLOR_HEADER],">> Legato"); + ImGui::TextColored(ch->portaStop?uiColors[GUI_COLOR_MACRO_VOLUME]:uiColors[GUI_COLOR_HEADER],">> PortaStop"); + ImGui::TextColored(ch->keyOn?uiColors[GUI_COLOR_MACRO_VOLUME]:uiColors[GUI_COLOR_HEADER],">> Key On"); + ImGui::TextColored(ch->keyOff?uiColors[GUI_COLOR_MACRO_VOLUME]:uiColors[GUI_COLOR_HEADER],">> Key Off"); + ImGui::TextColored(ch->nowYouCanStop?uiColors[GUI_COLOR_MACRO_VOLUME]:uiColors[GUI_COLOR_HEADER],">> NowYouCanStop"); + ImGui::TextColored(ch->stopOnOff?uiColors[GUI_COLOR_MACRO_VOLUME]:uiColors[GUI_COLOR_HEADER],">> Stop on Off"); + ImGui::TextColored(ch->arpYield?uiColors[GUI_COLOR_MACRO_VOLUME]:uiColors[GUI_COLOR_HEADER],">> Arp Yield"); + ImGui::TextColored(ch->delayLocked?uiColors[GUI_COLOR_MACRO_VOLUME]:uiColors[GUI_COLOR_HEADER],">> DelayLocked"); + ImGui::TextColored(ch->inPorta?uiColors[GUI_COLOR_MACRO_VOLUME]:uiColors[GUI_COLOR_HEADER],">> InPorta"); + ImGui::TextColored(ch->scheduledSlideReset?uiColors[GUI_COLOR_MACRO_VOLUME]:uiColors[GUI_COLOR_HEADER],">> SchedSlide"); + } + ImGui::NextColumn(); + } + ImGui::Columns(); + ImGui::TreePop(); + } + if (ImGui::TreeNode("Playground")) { + if (pgSys<0 || pgSys>=e->song.systemLen) pgSys=0; + if (ImGui::BeginCombo("System",fmt::sprintf("%d. %s",pgSys+1,e->getSystemName(e->song.system[pgSys])).c_str())) { + for (int i=0; isong.systemLen; i++) { + if (ImGui::Selectable(fmt::sprintf("%d. %s",i+1,e->getSystemName(e->song.system[i])).c_str())) { + pgSys=i; + break; + } + } + ImGui::EndCombo(); + } + ImGui::Text("Program"); + if (pgProgram.empty()) { + ImGui::Text("-nothing here-"); + } else { + char id[32]; + for (size_t index=0; indexpoke(pgSys,pgProgram); + } + ImGui::SameLine(); + if (ImGui::Button("Clear")) { + pgProgram.clear(); + } + + ImGui::Text("Address"); + ImGui::SameLine(); + ImGui::SetNextItemWidth(100.0f*dpiScale); + ImGui::InputInt("##PAddress",&pgAddr,0,0,ImGuiInputTextFlags_CharsHexadecimal); + ImGui::SameLine(); + ImGui::Text("Value"); + ImGui::SameLine(); + ImGui::SetNextItemWidth(100.0f*dpiScale); + ImGui::InputInt("##PValue",&pgVal,0,0,ImGuiInputTextFlags_CharsHexadecimal); + ImGui::SameLine(); + if (ImGui::Button("Write")) { + e->poke(pgSys,pgAddr,pgVal); + } + ImGui::SameLine(); + if (ImGui::Button("Add")) { + pgProgram.push_back(DivRegWrite(pgAddr,pgVal)); + } + if (ImGui::TreeNode("Register Cheatsheet")) { + const char** sheet=e->getRegisterSheet(pgSys); + if (sheet==NULL) { + ImGui::Text("no cheatsheet available for this system."); + } else { + if (ImGui::BeginTable("RegisterSheet",2,ImGuiTableFlags_SizingFixedSame)) { + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::Text("Name"); + ImGui::TableNextColumn(); + ImGui::Text("Address"); + for (int i=0; sheet[i]!=NULL; i+=2) { + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::Text("%s",sheet[i]); + ImGui::TableNextColumn(); + ImGui::Text("$%s",sheet[i+1]); + } + ImGui::EndTable(); + } + } + ImGui::TreePop(); + } + ImGui::TreePop(); + } + if (ImGui::TreeNode("User Interface")) { + if (ImGui::Button("Inspect")) { + inspectorOpen=!inspectorOpen; + } + ImGui::TreePop(); + } + if (ImGui::TreeNode("Settings")) { + if (ImGui::Button("Sync")) syncSettings(); + ImGui::SameLine(); + if (ImGui::Button("Commit")) commitSettings(); + ImGui::SameLine(); + if (ImGui::Button("Force Load")) e->loadConf(); + ImGui::SameLine(); + if (ImGui::Button("Force Save")) e->saveConf(); + ImGui::TreePop(); + } + ImGui::Text("Song format version %d",e->song.version); + ImGui::Text("Furnace version " DIV_VERSION " (%d)",DIV_ENGINE_VERSION); + } + if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_DEBUG; + ImGui::End(); +} \ No newline at end of file diff --git a/src/gui/doAction.cpp b/src/gui/doAction.cpp new file mode 100644 index 000000000..903f6edf0 --- /dev/null +++ b/src/gui/doAction.cpp @@ -0,0 +1,687 @@ +/** + * 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 + +#include "actionUtil.h" + +void FurnaceGUI::doAction(int what) { + switch (what) { + case GUI_ACTION_OPEN: + if (modified) { + showWarning("Unsaved changes! Are you sure?",GUI_WARN_OPEN); + } else { + openFileDialog(GUI_FILE_OPEN); + } + break; + case GUI_ACTION_OPEN_BACKUP: + if (modified) { + showWarning("Unsaved changes! Are you sure?",GUI_WARN_OPEN_BACKUP); + } else { + if (load(backupPath)>0) { + showError("No backup available! (or unable to open it)"); + } + } + break; + case GUI_ACTION_SAVE: + if (curFileName=="" || curFileName==backupPath || e->song.version>=0xff00) { + openFileDialog(GUI_FILE_SAVE); + } else { + if (save(curFileName,e->song.isDMF?e->song.version:0)>0) { + showError(fmt::sprintf("Error while saving file! (%s)",lastError)); + } + } + break; + case GUI_ACTION_SAVE_AS: + openFileDialog(GUI_FILE_SAVE); + break; + case GUI_ACTION_UNDO: + doUndo(); + break; + case GUI_ACTION_REDO: + doRedo(); + break; + case GUI_ACTION_PLAY_TOGGLE: + if (e->isPlaying() && !e->isStepping()) { + stop(); + } else { + play(); + } + break; + case GUI_ACTION_PLAY: + play(); + break; + case GUI_ACTION_STOP: + stop(); + break; + case GUI_ACTION_PLAY_REPEAT: + play(); + e->setRepeatPattern(true); + break; + case GUI_ACTION_PLAY_CURSOR: + if (e->isPlaying() && !e->isStepping()) { + stop(); + } else { + play(cursor.y); + } + break; + case GUI_ACTION_STEP_ONE: + e->stepOne(cursor.y); + break; + case GUI_ACTION_OCTAVE_UP: + if (++curOctave>7) { + curOctave=7; + } else { + for (size_t i=0; inoteOff(activeNotes[i].chan); + } + activeNotes.clear(); + } + break; + case GUI_ACTION_OCTAVE_DOWN: + if (--curOctave<-5) { + curOctave=-5; + } else { + for (size_t i=0; inoteOff(activeNotes[i].chan); + } + activeNotes.clear(); + } + break; + case GUI_ACTION_INS_UP: + if (--curIns<-1) curIns=-1; + break; + case GUI_ACTION_INS_DOWN: + if (++curIns>=(int)e->song.ins.size()) curIns=((int)e->song.ins.size())-1; + break; + case GUI_ACTION_STEP_UP: + if (++editStep>64) editStep=64; + break; + case GUI_ACTION_STEP_DOWN: + if (--editStep<0) editStep=0; + break; + case GUI_ACTION_TOGGLE_EDIT: + edit=!edit; + break; + case GUI_ACTION_METRONOME: + e->setMetronome(!e->getMetronome()); + break; + case GUI_ACTION_REPEAT_PATTERN: + e->setRepeatPattern(!e->getRepeatPattern()); + break; + case GUI_ACTION_FOLLOW_ORDERS: + followOrders=!followOrders; + break; + case GUI_ACTION_FOLLOW_PATTERN: + followPattern=!followPattern; + break; + case GUI_ACTION_PANIC: + e->syncReset(); + break; + + case GUI_ACTION_WINDOW_EDIT_CONTROLS: + nextWindow=GUI_WINDOW_EDIT_CONTROLS; + break; + case GUI_ACTION_WINDOW_ORDERS: + nextWindow=GUI_WINDOW_ORDERS; + break; + case GUI_ACTION_WINDOW_INS_LIST: + nextWindow=GUI_WINDOW_INS_LIST; + break; + case GUI_ACTION_WINDOW_INS_EDIT: + nextWindow=GUI_WINDOW_INS_EDIT; + break; + case GUI_ACTION_WINDOW_SONG_INFO: + nextWindow=GUI_WINDOW_SONG_INFO; + break; + case GUI_ACTION_WINDOW_PATTERN: + nextWindow=GUI_WINDOW_PATTERN; + break; + case GUI_ACTION_WINDOW_WAVE_LIST: + nextWindow=GUI_WINDOW_WAVE_LIST; + break; + case GUI_ACTION_WINDOW_WAVE_EDIT: + nextWindow=GUI_WINDOW_WAVE_EDIT; + break; + case GUI_ACTION_WINDOW_SAMPLE_LIST: + nextWindow=GUI_WINDOW_SAMPLE_LIST; + break; + case GUI_ACTION_WINDOW_SAMPLE_EDIT: + nextWindow=GUI_WINDOW_SAMPLE_EDIT; + break; + case GUI_ACTION_WINDOW_ABOUT: + nextWindow=GUI_WINDOW_ABOUT; + break; + case GUI_ACTION_WINDOW_SETTINGS: + nextWindow=GUI_WINDOW_SETTINGS; + break; + case GUI_ACTION_WINDOW_MIXER: + nextWindow=GUI_WINDOW_MIXER; + break; + case GUI_ACTION_WINDOW_DEBUG: + nextWindow=GUI_WINDOW_DEBUG; + break; + case GUI_ACTION_WINDOW_OSCILLOSCOPE: + nextWindow=GUI_WINDOW_OSCILLOSCOPE; + break; + case GUI_ACTION_WINDOW_VOL_METER: + nextWindow=GUI_WINDOW_VOL_METER; + break; + case GUI_ACTION_WINDOW_STATS: + nextWindow=GUI_WINDOW_STATS; + break; + case GUI_ACTION_WINDOW_COMPAT_FLAGS: + nextWindow=GUI_WINDOW_COMPAT_FLAGS; + break; + case GUI_ACTION_WINDOW_PIANO: + nextWindow=GUI_WINDOW_PIANO; + break; + case GUI_ACTION_WINDOW_NOTES: + nextWindow=GUI_WINDOW_NOTES; + break; + case GUI_ACTION_WINDOW_CHANNELS: + nextWindow=GUI_WINDOW_CHANNELS; + break; + case GUI_ACTION_WINDOW_REGISTER_VIEW: + nextWindow=GUI_WINDOW_REGISTER_VIEW; + break; + + case GUI_ACTION_COLLAPSE_WINDOW: + collapseWindow=true; + break; + case GUI_ACTION_CLOSE_WINDOW: + switch (curWindow) { + case GUI_WINDOW_EDIT_CONTROLS: + editControlsOpen=false; + break; + case GUI_WINDOW_SONG_INFO: + songInfoOpen=false; + break; + case GUI_WINDOW_ORDERS: + ordersOpen=false; + break; + case GUI_WINDOW_INS_LIST: + insListOpen=false; + break; + case GUI_WINDOW_PATTERN: + patternOpen=false; + break; + case GUI_WINDOW_INS_EDIT: + insEditOpen=false; + break; + case GUI_WINDOW_WAVE_LIST: + waveListOpen=false; + break; + case GUI_WINDOW_WAVE_EDIT: + waveEditOpen=false; + break; + case GUI_WINDOW_SAMPLE_LIST: + sampleListOpen=false; + break; + case GUI_WINDOW_SAMPLE_EDIT: + sampleEditOpen=false; + break; + case GUI_WINDOW_MIXER: + mixerOpen=false; + break; + case GUI_WINDOW_ABOUT: + aboutOpen=false; + break; + case GUI_WINDOW_SETTINGS: + settingsOpen=false; + break; + case GUI_WINDOW_DEBUG: + debugOpen=false; + break; + case GUI_WINDOW_OSCILLOSCOPE: + oscOpen=false; + break; + case GUI_WINDOW_VOL_METER: + volMeterOpen=false; + break; + case GUI_WINDOW_STATS: + statsOpen=false; + break; + case GUI_WINDOW_COMPAT_FLAGS: + compatFlagsOpen=false; + break; + case GUI_WINDOW_PIANO: + pianoOpen=false; + break; + case GUI_WINDOW_NOTES: + notesOpen=false; + break; + case GUI_WINDOW_CHANNELS: + channelsOpen=false; + break; + case GUI_WINDOW_REGISTER_VIEW: + regViewOpen=false; + break; + default: + break; + } + curWindow=GUI_WINDOW_NOTHING; + break; + + case GUI_ACTION_PAT_NOTE_UP: + doTranspose(1); + break; + case GUI_ACTION_PAT_NOTE_DOWN: + doTranspose(-1); + break; + case GUI_ACTION_PAT_OCTAVE_UP: + doTranspose(12); + break; + case GUI_ACTION_PAT_OCTAVE_DOWN: + doTranspose(-12); + break; + case GUI_ACTION_PAT_SELECT_ALL: + doSelectAll(); + break; + case GUI_ACTION_PAT_CUT: + doCopy(true); + break; + case GUI_ACTION_PAT_COPY: + doCopy(false); + break; + case GUI_ACTION_PAT_PASTE: + doPaste(); + break; + case GUI_ACTION_PAT_CURSOR_UP: + moveCursor(0,-MAX(1,settings.scrollStep?editStep:1),false); + break; + case GUI_ACTION_PAT_CURSOR_DOWN: + moveCursor(0,MAX(1,settings.scrollStep?editStep:1),false); + break; + case GUI_ACTION_PAT_CURSOR_LEFT: + moveCursor(-1,0,false); + break; + case GUI_ACTION_PAT_CURSOR_RIGHT: + moveCursor(1,0,false); + break; + case GUI_ACTION_PAT_CURSOR_UP_ONE: + moveCursor(0,-1,false); + break; + case GUI_ACTION_PAT_CURSOR_DOWN_ONE: + moveCursor(0,1,false); + break; + case GUI_ACTION_PAT_CURSOR_LEFT_CHANNEL: + moveCursorPrevChannel(false); + break; + case GUI_ACTION_PAT_CURSOR_RIGHT_CHANNEL: + moveCursorNextChannel(false); + break; + case GUI_ACTION_PAT_CURSOR_NEXT_CHANNEL: + moveCursorNextChannel(true); + break; + case GUI_ACTION_PAT_CURSOR_PREVIOUS_CHANNEL: + moveCursorPrevChannel(true); + break; + case GUI_ACTION_PAT_CURSOR_BEGIN: + moveCursorTop(false); + break; + case GUI_ACTION_PAT_CURSOR_END: + moveCursorBottom(false); + break; + case GUI_ACTION_PAT_CURSOR_UP_COARSE: + moveCursor(0,-16,false); + break; + case GUI_ACTION_PAT_CURSOR_DOWN_COARSE: + moveCursor(0,16,false); + break; + case GUI_ACTION_PAT_SELECTION_UP: + moveCursor(0,-MAX(1,settings.scrollStep?editStep:1),true); + break; + case GUI_ACTION_PAT_SELECTION_DOWN: + moveCursor(0,MAX(1,settings.scrollStep?editStep:1),true); + break; + case GUI_ACTION_PAT_SELECTION_LEFT: + moveCursor(-1,0,true); + break; + case GUI_ACTION_PAT_SELECTION_RIGHT: + moveCursor(1,0,true); + break; + case GUI_ACTION_PAT_SELECTION_UP_ONE: + moveCursor(0,-1,true); + break; + case GUI_ACTION_PAT_SELECTION_DOWN_ONE: + moveCursor(0,1,true); + break; + case GUI_ACTION_PAT_SELECTION_BEGIN: + moveCursorTop(true); + break; + case GUI_ACTION_PAT_SELECTION_END: + moveCursorBottom(true); + break; + case GUI_ACTION_PAT_SELECTION_UP_COARSE: + moveCursor(0,-16,true); + break; + case GUI_ACTION_PAT_SELECTION_DOWN_COARSE: + moveCursor(0,16,true); + break; + case GUI_ACTION_PAT_DELETE: + doDelete(); + if (settings.stepOnDelete) { + moveCursor(0,editStep,false); + } + break; + case GUI_ACTION_PAT_PULL_DELETE: + doPullDelete(); + break; + case GUI_ACTION_PAT_INSERT: + doInsert(); + if (settings.stepOnInsert) { + moveCursor(0,editStep,false); + } + break; + case GUI_ACTION_PAT_MUTE_CURSOR: + if (cursor.xCoarse<0 || cursor.xCoarse>=e->getTotalChannelCount()) break; + e->toggleMute(cursor.xCoarse); + break; + case GUI_ACTION_PAT_SOLO_CURSOR: + if (cursor.xCoarse<0 || cursor.xCoarse>=e->getTotalChannelCount()) break; + e->toggleSolo(cursor.xCoarse); + break; + case GUI_ACTION_PAT_UNMUTE_ALL: + e->unmuteAll(); + break; + case GUI_ACTION_PAT_NEXT_ORDER: + if (e->getOrder()song.ordersLen-1) { + e->setOrder(e->getOrder()+1); + } + break; + case GUI_ACTION_PAT_PREV_ORDER: + if (e->getOrder()>0) { + e->setOrder(e->getOrder()-1); + } + break; + case GUI_ACTION_PAT_COLLAPSE: + if (cursor.xCoarse<0 || cursor.xCoarse>=e->getTotalChannelCount()) break; + e->song.chanCollapse[cursor.xCoarse]=!e->song.chanCollapse[cursor.xCoarse]; + break; + case GUI_ACTION_PAT_INCREASE_COLUMNS: + if (cursor.xCoarse<0 || cursor.xCoarse>=e->getTotalChannelCount()) break; + e->song.pat[cursor.xCoarse].effectRows++; + if (e->song.pat[cursor.xCoarse].effectRows>8) e->song.pat[cursor.xCoarse].effectRows=8; + break; + case GUI_ACTION_PAT_DECREASE_COLUMNS: + if (cursor.xCoarse<0 || cursor.xCoarse>=e->getTotalChannelCount()) break; + e->song.pat[cursor.xCoarse].effectRows--; + if (e->song.pat[cursor.xCoarse].effectRows<1) e->song.pat[cursor.xCoarse].effectRows=1; + break; + case GUI_ACTION_PAT_INTERPOLATE: + doInterpolate(); + break; + case GUI_ACTION_PAT_INVERT_VALUES: + doInvertValues(); + break; + case GUI_ACTION_PAT_FLIP_SELECTION: + doFlip(); + break; + case GUI_ACTION_PAT_COLLAPSE_ROWS: + doCollapse(2); + break; + case GUI_ACTION_PAT_EXPAND_ROWS: + doExpand(2); + break; + case GUI_ACTION_PAT_COLLAPSE_PAT: // TODO + break; + case GUI_ACTION_PAT_EXPAND_PAT: // TODO + break; + case GUI_ACTION_PAT_COLLAPSE_SONG: // TODO + break; + case GUI_ACTION_PAT_EXPAND_SONG: // TODO + break; + case GUI_ACTION_PAT_LATCH: // TODO + break; + + case GUI_ACTION_INS_LIST_ADD: + curIns=e->addInstrument(cursor.xCoarse); + MARK_MODIFIED; + break; + case GUI_ACTION_INS_LIST_DUPLICATE: + if (curIns>=0 && curIns<(int)e->song.ins.size()) { + int prevIns=curIns; + curIns=e->addInstrument(cursor.xCoarse); + (*e->song.ins[curIns])=(*e->song.ins[prevIns]); + MARK_MODIFIED; + } + break; + case GUI_ACTION_INS_LIST_OPEN: + openFileDialog(GUI_FILE_INS_OPEN); + break; + case GUI_ACTION_INS_LIST_SAVE: + if (curIns>=0 && curIns<(int)e->song.ins.size()) openFileDialog(GUI_FILE_INS_SAVE); + break; + case GUI_ACTION_INS_LIST_MOVE_UP: + if (e->moveInsUp(curIns)) curIns--; + break; + case GUI_ACTION_INS_LIST_MOVE_DOWN: + if (e->moveInsDown(curIns)) curIns++; + break; + case GUI_ACTION_INS_LIST_DELETE: + if (curIns>=0 && curIns<(int)e->song.ins.size()) { + e->delInstrument(curIns); + MARK_MODIFIED; + if (curIns>=(int)e->song.ins.size()) { + curIns--; + } + } + break; + case GUI_ACTION_INS_LIST_EDIT: + insEditOpen=true; + break; + case GUI_ACTION_INS_LIST_UP: + if (--curIns<0) curIns=0; + break; + case GUI_ACTION_INS_LIST_DOWN: + if (++curIns>=(int)e->song.ins.size()) curIns=((int)e->song.ins.size())-1; + break; + + case GUI_ACTION_WAVE_LIST_ADD: + curWave=e->addWave(); + MARK_MODIFIED; + break; + case GUI_ACTION_WAVE_LIST_DUPLICATE: + if (curWave>=0 && curWave<(int)e->song.wave.size()) { + int prevWave=curWave; + curWave=e->addWave(); + (*e->song.wave[curWave])=(*e->song.wave[prevWave]); + MARK_MODIFIED; + } + break; + case GUI_ACTION_WAVE_LIST_OPEN: + openFileDialog(GUI_FILE_WAVE_OPEN); + break; + case GUI_ACTION_WAVE_LIST_SAVE: + if (curWave>=0 && curWave<(int)e->song.wave.size()) openFileDialog(GUI_FILE_WAVE_SAVE); + break; + case GUI_ACTION_WAVE_LIST_MOVE_UP: + if (e->moveWaveUp(curWave)) curWave--; + break; + case GUI_ACTION_WAVE_LIST_MOVE_DOWN: + if (e->moveWaveDown(curWave)) curWave++; + break; + case GUI_ACTION_WAVE_LIST_DELETE: + if (curWave>=0 && curWave<(int)e->song.wave.size()) { + e->delWave(curWave); + MARK_MODIFIED; + if (curWave>=(int)e->song.wave.size()) { + curWave--; + } + } + break; + case GUI_ACTION_WAVE_LIST_EDIT: + waveEditOpen=true; + break; + case GUI_ACTION_WAVE_LIST_UP: + if (--curWave<0) curWave=0; + break; + case GUI_ACTION_WAVE_LIST_DOWN: + if (++curWave>=(int)e->song.wave.size()) curWave=((int)e->song.wave.size())-1; + break; + + case GUI_ACTION_SAMPLE_LIST_ADD: + curSample=e->addSample(); + MARK_MODIFIED; + break; + case GUI_ACTION_SAMPLE_LIST_OPEN: + openFileDialog(GUI_FILE_SAMPLE_OPEN); + break; + case GUI_ACTION_SAMPLE_LIST_SAVE: + if (curSample>=0 && curSample<(int)e->song.sample.size()) openFileDialog(GUI_FILE_SAMPLE_SAVE); + break; + case GUI_ACTION_SAMPLE_LIST_MOVE_UP: + if (e->moveSampleUp(curSample)) curSample--; + break; + case GUI_ACTION_SAMPLE_LIST_MOVE_DOWN: + if (e->moveSampleDown(curSample)) curSample++; + break; + case GUI_ACTION_SAMPLE_LIST_DELETE: + e->delSample(curSample); + MARK_MODIFIED; + if (curSample>=(int)e->song.sample.size()) { + curSample--; + } + break; + case GUI_ACTION_SAMPLE_LIST_EDIT: + sampleEditOpen=true; + break; + case GUI_ACTION_SAMPLE_LIST_UP: + if (--curSample<0) curSample=0; + break; + case GUI_ACTION_SAMPLE_LIST_DOWN: + if (++curSample>=(int)e->song.sample.size()) curSample=((int)e->song.sample.size())-1; + break; + case GUI_ACTION_SAMPLE_LIST_PREVIEW: + e->previewSample(curSample); + break; + case GUI_ACTION_SAMPLE_LIST_STOP_PREVIEW: + e->stopSamplePreview(); + break; + + case GUI_ACTION_ORDERS_UP: + if (e->getOrder()>0) { + e->setOrder(e->getOrder()-1); + } + break; + case GUI_ACTION_ORDERS_DOWN: + if (e->getOrder()song.ordersLen-1) { + e->setOrder(e->getOrder()+1); + } + break; + case GUI_ACTION_ORDERS_LEFT: { + DETERMINE_FIRST; + + do { + orderCursor--; + if (orderCursorsong.chanShow[orderCursor]); + break; + } + case GUI_ACTION_ORDERS_RIGHT: { + DETERMINE_LAST; + + do { + orderCursor++; + if (orderCursor>=lastChannel) { + orderCursor=lastChannel-1; + break; + } + } while (!e->song.chanShow[orderCursor]); + break; + } + case GUI_ACTION_ORDERS_INCREASE: { + if (orderCursor<0 || orderCursor>=e->getTotalChannelCount()) break; + int curOrder=e->getOrder(); + if (e->song.orders.ord[orderCursor][curOrder]<0x7f) { + e->song.orders.ord[orderCursor][curOrder]++; + } + break; + } + case GUI_ACTION_ORDERS_DECREASE: { + if (orderCursor<0 || orderCursor>=e->getTotalChannelCount()) break; + int curOrder=e->getOrder(); + if (e->song.orders.ord[orderCursor][curOrder]>0) { + e->song.orders.ord[orderCursor][curOrder]--; + } + break; + } + case GUI_ACTION_ORDERS_EDIT_MODE: + orderEditMode++; + if (orderEditMode>3) orderEditMode=0; + break; + case GUI_ACTION_ORDERS_LINK: + changeAllOrders=!changeAllOrders; + break; + case GUI_ACTION_ORDERS_ADD: + prepareUndo(GUI_UNDO_CHANGE_ORDER); + e->addOrder(false,false); + makeUndo(GUI_UNDO_CHANGE_ORDER); + break; + case GUI_ACTION_ORDERS_DUPLICATE: + prepareUndo(GUI_UNDO_CHANGE_ORDER); + e->addOrder(true,false); + makeUndo(GUI_UNDO_CHANGE_ORDER); + break; + case GUI_ACTION_ORDERS_DEEP_CLONE: + prepareUndo(GUI_UNDO_CHANGE_ORDER); + e->deepCloneOrder(false); + makeUndo(GUI_UNDO_CHANGE_ORDER); + if (!e->getWarnings().empty()) { + showWarning(e->getWarnings(),GUI_WARN_GENERIC); + } + break; + case GUI_ACTION_ORDERS_DUPLICATE_END: + prepareUndo(GUI_UNDO_CHANGE_ORDER); + e->addOrder(true,true); + makeUndo(GUI_UNDO_CHANGE_ORDER); + break; + case GUI_ACTION_ORDERS_DEEP_CLONE_END: + prepareUndo(GUI_UNDO_CHANGE_ORDER); + e->deepCloneOrder(true); + makeUndo(GUI_UNDO_CHANGE_ORDER); + if (!e->getWarnings().empty()) { + showWarning(e->getWarnings(),GUI_WARN_GENERIC); + } + break; + case GUI_ACTION_ORDERS_REMOVE: + prepareUndo(GUI_UNDO_CHANGE_ORDER); + e->deleteOrder(); + makeUndo(GUI_UNDO_CHANGE_ORDER); + break; + case GUI_ACTION_ORDERS_MOVE_UP: + prepareUndo(GUI_UNDO_CHANGE_ORDER); + e->moveOrderUp(); + makeUndo(GUI_UNDO_CHANGE_ORDER); + break; + case GUI_ACTION_ORDERS_MOVE_DOWN: + prepareUndo(GUI_UNDO_CHANGE_ORDER); + e->moveOrderDown(); + makeUndo(GUI_UNDO_CHANGE_ORDER); + break; + case GUI_ACTION_ORDERS_REPLAY: + e->setOrder(e->getOrder()); + break; + } +} \ No newline at end of file diff --git a/src/gui/editControls.cpp b/src/gui/editControls.cpp new file mode 100644 index 000000000..68f0fee2d --- /dev/null +++ b/src/gui/editControls.cpp @@ -0,0 +1,330 @@ +/** + * 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 "IconsFontAwesome4.h" + +void FurnaceGUI::drawEditControls() { + if (nextWindow==GUI_WINDOW_EDIT_CONTROLS) { + editControlsOpen=true; + ImGui::SetNextWindowFocus(); + nextWindow=GUI_WINDOW_NOTHING; + } + if (!editControlsOpen) return; + switch (settings.controlLayout) { + case 0: // classic + if (ImGui::Begin("Play/Edit Controls",&editControlsOpen)) { + ImGui::Text("Octave"); + ImGui::SameLine(); + if (ImGui::InputInt("##Octave",&curOctave,1,1)) { + if (curOctave>7) curOctave=7; + if (curOctave<-5) curOctave=-5; + for (size_t i=0; inoteOff(activeNotes[i].chan); + } + activeNotes.clear(); + + if (settings.insFocusesPattern && !ImGui::IsItemActive() && patternOpen) { + nextWindow=GUI_WINDOW_PATTERN; + } + } + + ImGui::Text("Edit Step"); + ImGui::SameLine(); + if (ImGui::InputInt("##EditStep",&editStep,1,1)) { + if (editStep>=e->song.patLen) editStep=e->song.patLen-1; + if (editStep<0) editStep=0; + + if (settings.insFocusesPattern && !ImGui::IsItemActive() && patternOpen) { + nextWindow=GUI_WINDOW_PATTERN; + } + } + + if (ImGui::Button(ICON_FA_PLAY "##Play")) { + play(); + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_STOP "##Stop")) { + stop(); + } + ImGui::SameLine(); + ImGui::Checkbox("Edit",&edit); + ImGui::SameLine(); + bool metro=e->getMetronome(); + if (ImGui::Checkbox("Metronome",&metro)) { + e->setMetronome(metro); + } + + ImGui::Text("Follow"); + ImGui::SameLine(); + unimportant(ImGui::Checkbox("Orders",&followOrders)); + ImGui::SameLine(); + unimportant(ImGui::Checkbox("Pattern",&followPattern)); + + bool repeatPattern=e->getRepeatPattern(); + if (ImGui::Checkbox("Repeat pattern",&repeatPattern)) { + e->setRepeatPattern(repeatPattern); + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_ARROW_DOWN "##StepOne")) { + e->stepOne(cursor.y); + } + } + if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_EDIT_CONTROLS; + ImGui::End(); + break; + case 1: // compact + if (ImGui::Begin("Play/Edit Controls",&editControlsOpen,ImGuiWindowFlags_NoScrollbar|ImGuiWindowFlags_NoScrollWithMouse)) { + if (ImGui::Button(ICON_FA_STOP "##Stop")) { + stop(); + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_PLAY "##Play")) { + play(); + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_ARROW_DOWN "##StepOne")) { + e->stepOne(cursor.y); + } + + ImGui::SameLine(); + bool repeatPattern=e->getRepeatPattern(); + ImGui::PushStyleColor(ImGuiCol_Button,ImVec4(0.2f,(repeatPattern)?0.6f:0.2f,0.2f,1.0f)); + if (ImGui::Button(ICON_FA_REPEAT "##RepeatPattern")) { + e->setRepeatPattern(!repeatPattern); + } + ImGui::PopStyleColor(); + + ImGui::SameLine(); + ImGui::PushStyleColor(ImGuiCol_Button,ImVec4(0.2f,(edit)?0.6f:0.2f,0.2f,1.0f)); + if (ImGui::Button(ICON_FA_CIRCLE "##Edit")) { + edit=!edit; + } + ImGui::PopStyleColor(); + + ImGui::SameLine(); + bool metro=e->getMetronome(); + ImGui::PushStyleColor(ImGuiCol_Button,ImVec4(0.2f,(metro)?0.6f:0.2f,0.2f,1.0f)); + if (ImGui::Button(ICON_FA_BELL_O "##Metronome")) { + e->setMetronome(!metro); + } + ImGui::PopStyleColor(); + + ImGui::SameLine(); + ImGui::Text("Octave"); + ImGui::SameLine(); + ImGui::SetNextItemWidth(96.0f*dpiScale); + if (ImGui::InputInt("##Octave",&curOctave,1,1)) { + if (curOctave>7) curOctave=7; + if (curOctave<-5) curOctave=-5; + for (size_t i=0; inoteOff(activeNotes[i].chan); + } + activeNotes.clear(); + + if (settings.insFocusesPattern && !ImGui::IsItemActive() && patternOpen) { + nextWindow=GUI_WINDOW_PATTERN; + } + } + + ImGui::SameLine(); + ImGui::Text("Edit Step"); + ImGui::SameLine(); + ImGui::SetNextItemWidth(96.0f*dpiScale); + if (ImGui::InputInt("##EditStep",&editStep,1,1)) { + if (editStep>=e->song.patLen) editStep=e->song.patLen-1; + if (editStep<0) editStep=0; + + if (settings.insFocusesPattern && !ImGui::IsItemActive() && patternOpen) { + nextWindow=GUI_WINDOW_PATTERN; + } + } + + ImGui::SameLine(); + ImGui::Text("Follow"); + ImGui::SameLine(); + unimportant(ImGui::Checkbox("Orders",&followOrders)); + ImGui::SameLine(); + unimportant(ImGui::Checkbox("Pattern",&followPattern)); + } + if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_EDIT_CONTROLS; + ImGui::End(); + break; + case 2: // compact vertical + if (ImGui::Begin("Play/Edit Controls",&editControlsOpen,ImGuiWindowFlags_NoScrollbar|ImGuiWindowFlags_NoScrollWithMouse)) { + if (ImGui::Button(ICON_FA_PLAY "##Play")) { + play(); + } + if (ImGui::Button(ICON_FA_STOP "##Stop")) { + stop(); + } + if (ImGui::Button(ICON_FA_ARROW_DOWN "##StepOne")) { + e->stepOne(cursor.y); + } + + bool repeatPattern=e->getRepeatPattern(); + ImGui::PushStyleColor(ImGuiCol_Button,ImVec4(0.2f,(repeatPattern)?0.6f:0.2f,0.2f,1.0f)); + if (ImGui::Button(ICON_FA_REPEAT "##RepeatPattern")) { + e->setRepeatPattern(!repeatPattern); + } + ImGui::PopStyleColor(); + + ImGui::PushStyleColor(ImGuiCol_Button,ImVec4(0.2f,(edit)?0.6f:0.2f,0.2f,1.0f)); + if (ImGui::Button(ICON_FA_CIRCLE "##Edit")) { + edit=!edit; + } + ImGui::PopStyleColor(); + + bool metro=e->getMetronome(); + ImGui::PushStyleColor(ImGuiCol_Button,ImVec4(0.2f,(metro)?0.6f:0.2f,0.2f,1.0f)); + if (ImGui::Button(ICON_FA_BELL_O "##Metronome")) { + e->setMetronome(!metro); + } + ImGui::PopStyleColor(); + + ImGui::Text("Oct."); + 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; + for (size_t i=0; inoteOff(activeNotes[i].chan); + } + activeNotes.clear(); + + if (settings.insFocusesPattern && !ImGui::IsItemActive() && patternOpen) { + nextWindow=GUI_WINDOW_PATTERN; + } + } + + ImGui::Text("Step"); + ImGui::SetNextItemWidth(avail); + if (ImGui::InputInt("##EditStep",&editStep,0,0)) { + if (editStep>=e->song.patLen) editStep=e->song.patLen-1; + if (editStep<0) editStep=0; + + if (settings.insFocusesPattern && !ImGui::IsItemActive() && patternOpen) { + nextWindow=GUI_WINDOW_PATTERN; + } + } + + ImGui::Text("Foll."); + ImGui::PushStyleColor(ImGuiCol_Button,ImVec4(0.2f,(followOrders)?0.6f:0.2f,0.2f,1.0f)); + if (ImGui::SmallButton("Ord##FollowOrders")) { handleUnimportant + followOrders=!followOrders; + } + ImGui::PopStyleColor(); + ImGui::PushStyleColor(ImGuiCol_Button,ImVec4(0.2f,(followPattern)?0.6f:0.2f,0.2f,1.0f)); + if (ImGui::SmallButton("Pat##FollowPattern")) { handleUnimportant + followPattern=!followPattern; + } + ImGui::PopStyleColor(); + } + if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_EDIT_CONTROLS; + ImGui::End(); + break; + case 3: // split + if (ImGui::Begin("Play Controls",&editControlsOpen,ImGuiWindowFlags_NoScrollbar|ImGuiWindowFlags_NoScrollWithMouse)) { + if (e->isPlaying()) { + if (ImGui::Button(ICON_FA_STOP "##Stop")) { + stop(); + } + } else { + if (ImGui::Button(ICON_FA_PLAY "##Play")) { + play(); + } + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_PLAY_CIRCLE "##PlayAgain")) { + play(); + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_ARROW_DOWN "##StepOne")) { + e->stepOne(cursor.y); + } + + ImGui::SameLine(); + ImGui::PushStyleColor(ImGuiCol_Button,ImVec4(0.2f,(edit)?0.6f:0.2f,0.2f,1.0f)); + if (ImGui::Button(ICON_FA_CIRCLE "##Edit")) { + edit=!edit; + } + ImGui::PopStyleColor(); + + bool metro=e->getMetronome(); + ImGui::SameLine(); + ImGui::PushStyleColor(ImGuiCol_Button,ImVec4(0.2f,(metro)?0.6f:0.2f,0.2f,1.0f)); + if (ImGui::Button(ICON_FA_BELL_O "##Metronome")) { + e->setMetronome(!metro); + } + ImGui::PopStyleColor(); + + ImGui::SameLine(); + bool repeatPattern=e->getRepeatPattern(); + ImGui::PushStyleColor(ImGuiCol_Button,ImVec4(0.2f,(repeatPattern)?0.6f:0.2f,0.2f,1.0f)); + if (ImGui::Button(ICON_FA_REPEAT "##RepeatPattern")) { + e->setRepeatPattern(!repeatPattern); + } + ImGui::PopStyleColor(); + } + if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_EDIT_CONTROLS; + ImGui::End(); + + if (ImGui::Begin("Edit Controls",&editControlsOpen)) { + ImGui::Columns(2); + ImGui::Text("Octave"); + ImGui::SameLine(); + float cursor=ImGui::GetCursorPosX(); + 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; + for (size_t i=0; inoteOff(activeNotes[i].chan); + } + activeNotes.clear(); + + if (settings.insFocusesPattern && !ImGui::IsItemActive() && patternOpen) { + nextWindow=GUI_WINDOW_PATTERN; + } + } + + ImGui::Text("Step"); + ImGui::SameLine(); + ImGui::SetCursorPosX(cursor); + ImGui::SetNextItemWidth(avail); + if (ImGui::InputInt("##EditStep",&editStep,1,1)) { + if (editStep>=e->song.patLen) editStep=e->song.patLen-1; + if (editStep<0) editStep=0; + + if (settings.insFocusesPattern && !ImGui::IsItemActive() && patternOpen) { + nextWindow=GUI_WINDOW_PATTERN; + } + } + ImGui::NextColumn(); + + unimportant(ImGui::Checkbox("Follow orders",&followOrders)); + unimportant(ImGui::Checkbox("Follow pattern",&followPattern)); + } + if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_EDIT_CONTROLS; + ImGui::End(); + break; + } +} \ No newline at end of file diff --git a/src/gui/editing.cpp b/src/gui/editing.cpp new file mode 100644 index 000000000..59c1fc342 --- /dev/null +++ b/src/gui/editing.cpp @@ -0,0 +1,1015 @@ +/** + * 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 "../ta-log.h" +#include "guiConst.h" +#include + +#include "actionUtil.h" + +const char* noteNameNormal(short note, short octave) { + if (note==100) { // note cut + return "OFF"; + } else if (note==101) { // note off and envelope release + return "==="; + } else if (note==102) { // envelope release only + return "REL"; + } else if (octave==0 && note==0) { + return "..."; + } + int seek=(note+(signed char)octave*12)+60; + if (seek<0 || seek>=180) { + return "???"; + } + return noteNames[seek]; +} + +void FurnaceGUI::prepareUndo(ActionType action) { + int order=e->getOrder(); + switch (action) { + case GUI_UNDO_CHANGE_ORDER: + oldOrders=e->song.orders; + oldOrdersLen=e->song.ordersLen; + break; + case GUI_UNDO_PATTERN_EDIT: + case GUI_UNDO_PATTERN_DELETE: + case GUI_UNDO_PATTERN_PULL: + case GUI_UNDO_PATTERN_PUSH: + case GUI_UNDO_PATTERN_CUT: + case GUI_UNDO_PATTERN_PASTE: + case GUI_UNDO_PATTERN_CHANGE_INS: + case GUI_UNDO_PATTERN_INTERPOLATE: + case GUI_UNDO_PATTERN_FADE: + case GUI_UNDO_PATTERN_SCALE: + case GUI_UNDO_PATTERN_RANDOMIZE: + case GUI_UNDO_PATTERN_INVERT_VAL: + case GUI_UNDO_PATTERN_FLIP: + case GUI_UNDO_PATTERN_COLLAPSE: + case GUI_UNDO_PATTERN_EXPAND: + for (int i=0; igetTotalChannelCount(); i++) { + e->song.pat[i].getPattern(e->song.orders.ord[i][order],false)->copyOn(oldPat[i]); + } + break; + } +} + +void FurnaceGUI::makeUndo(ActionType action) { + bool doPush=false; + UndoStep s; + s.type=action; + s.cursor=cursor; + s.selStart=selStart; + s.selEnd=selEnd; + int order=e->getOrder(); + s.order=order; + s.nibble=curNibble; + switch (action) { + case GUI_UNDO_CHANGE_ORDER: + for (int i=0; isong.orders.ord[i][j]) { + s.ord.push_back(UndoOrderData(i,j,oldOrders.ord[i][j],e->song.orders.ord[i][j])); + } + } + } + s.oldOrdersLen=oldOrdersLen; + s.newOrdersLen=e->song.ordersLen; + if (oldOrdersLen!=e->song.ordersLen) { + doPush=true; + } + if (!s.ord.empty()) { + doPush=true; + } + break; + case GUI_UNDO_PATTERN_EDIT: + case GUI_UNDO_PATTERN_DELETE: + case GUI_UNDO_PATTERN_PULL: + case GUI_UNDO_PATTERN_PUSH: + case GUI_UNDO_PATTERN_CUT: + case GUI_UNDO_PATTERN_PASTE: + case GUI_UNDO_PATTERN_CHANGE_INS: + case GUI_UNDO_PATTERN_INTERPOLATE: + case GUI_UNDO_PATTERN_FADE: + case GUI_UNDO_PATTERN_SCALE: + case GUI_UNDO_PATTERN_RANDOMIZE: + case GUI_UNDO_PATTERN_INVERT_VAL: + case GUI_UNDO_PATTERN_FLIP: + case GUI_UNDO_PATTERN_COLLAPSE: + case GUI_UNDO_PATTERN_EXPAND: + for (int i=0; igetTotalChannelCount(); i++) { + DivPattern* p=e->song.pat[i].getPattern(e->song.orders.ord[i][order],false); + for (int j=0; jsong.patLen; j++) { + for (int k=0; k<32; k++) { + if (p->data[j][k]!=oldPat[i]->data[j][k]) { + s.pat.push_back(UndoPatternData(i,e->song.orders.ord[i][order],j,k,oldPat[i]->data[j][k],p->data[j][k])); + } + } + } + } + if (!s.pat.empty()) { + doPush=true; + } + break; + } + if (doPush) { + MARK_MODIFIED; + undoHist.push_back(s); + redoHist.clear(); + if (undoHist.size()>settings.maxUndoSteps) undoHist.pop_front(); + } +} + +void FurnaceGUI::doSelectAll() { + finishSelection(); + curNibble=false; + if (selStart.xFine==0 && selEnd.xFine==2+e->song.pat[selEnd.xCoarse].effectRows*2) { + if (selStart.y==0 && selEnd.y==e->song.patLen-1) { // select entire pattern + selStart.xCoarse=0; + selStart.xFine=0; + selEnd.xCoarse=e->getTotalChannelCount()-1; + selEnd.xFine=2+e->song.pat[selEnd.xCoarse].effectRows*2; + } else { // select entire column + selStart.y=0; + selEnd.y=e->song.patLen-1; + } + } else { + int selStartX=0; + int selEndX=0; + // find row position + for (SelectionPoint i; i.xCoarse!=selStart.xCoarse || i.xFine!=selStart.xFine; selStartX++) { + i.xFine++; + if (i.xFine>=3+e->song.pat[i.xCoarse].effectRows*2) { + i.xFine=0; + i.xCoarse++; + } + } + for (SelectionPoint i; i.xCoarse!=selEnd.xCoarse || i.xFine!=selEnd.xFine; selEndX++) { + i.xFine++; + if (i.xFine>=3+e->song.pat[i.xCoarse].effectRows*2) { + i.xFine=0; + i.xCoarse++; + } + } + + float aspect=float(selEndX-selStartX+1)/float(selEnd.y-selStart.y+1); + if (aspect<=1.0f && !(selStart.y==0 && selEnd.y==e->song.patLen-1)) { // up-down + selStart.y=0; + selEnd.y=e->song.patLen-1; + } else { // left-right + selStart.xFine=0; + selEnd.xFine=2+e->song.pat[selEnd.xCoarse].effectRows*2; + } + } +} + +#define maskOut(x) \ + if (x==0) { \ + if (!opMaskNote) continue; \ + } else if (x==1) { \ + if (!opMaskIns) continue; \ + } else if (x==2) { \ + if (!opMaskVol) continue; \ + } else if (((x)&1)==0) { \ + if (!opMaskEffectVal) continue; \ + } else if (((x)&1)==1) { \ + if (!opMaskEffect) continue; \ + } + +void FurnaceGUI::doDelete() { + finishSelection(); + prepareUndo(GUI_UNDO_PATTERN_DELETE); + curNibble=false; + + int iCoarse=selStart.xCoarse; + int iFine=selStart.xFine; + int ord=e->getOrder(); + for (; iCoarse<=selEnd.xCoarse; iCoarse++) { + if (!e->song.chanShow[iCoarse]) continue; + DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][ord],true); + for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsedata[j][iFine]=0; + if (selStart.y==selEnd.y) pat->data[j][2]=-1; + } + pat->data[j][iFine+1]=(iFine<1)?0:-1; + } + } + iFine=0; + } + + makeUndo(GUI_UNDO_PATTERN_DELETE); +} + +void FurnaceGUI::doPullDelete() { + finishSelection(); + prepareUndo(GUI_UNDO_PATTERN_PULL); + curNibble=false; + + if (settings.pullDeleteBehavior) { + if (--selStart.y<0) selStart.y=0; + if (--selEnd.y<0) selEnd.y=0; + if (--cursor.y<0) cursor.y=0; + updateScroll(cursor.y); + } + + int iCoarse=selStart.xCoarse; + int iFine=selStart.xFine; + int ord=e->getOrder(); + for (; iCoarse<=selEnd.xCoarse; iCoarse++) { + if (!e->song.chanShow[iCoarse]) continue; + DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][ord],true); + for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsesong.patLen; j++) { + if (jsong.patLen-1) { + if (iFine==0) { + pat->data[j][iFine]=pat->data[j+1][iFine]; + } + pat->data[j][iFine+1]=pat->data[j+1][iFine+1]; + } else { + if (iFine==0) { + pat->data[j][iFine]=0; + } + pat->data[j][iFine+1]=(iFine<1)?0:-1; + } + } + } + iFine=0; + } + + makeUndo(GUI_UNDO_PATTERN_PULL); +} + +void FurnaceGUI::doInsert() { + finishSelection(); + prepareUndo(GUI_UNDO_PATTERN_PUSH); + curNibble=false; + + int iCoarse=selStart.xCoarse; + int iFine=selStart.xFine; + int ord=e->getOrder(); + for (; iCoarse<=selEnd.xCoarse; iCoarse++) { + if (!e->song.chanShow[iCoarse]) continue; + DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][ord],true); + for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsesong.patLen-1; j>=selStart.y; j--) { + if (j==selStart.y) { + if (iFine==0) { + pat->data[j][iFine]=0; + } + pat->data[j][iFine+1]=(iFine<1)?0:-1; + } else { + if (iFine==0) { + pat->data[j][iFine]=pat->data[j-1][iFine]; + } + pat->data[j][iFine+1]=pat->data[j-1][iFine+1]; + } + } + } + iFine=0; + } + + makeUndo(GUI_UNDO_PATTERN_PUSH); +} + +void FurnaceGUI::doTranspose(int amount) { + finishSelection(); + prepareUndo(GUI_UNDO_PATTERN_DELETE); + curNibble=false; + + int iCoarse=selStart.xCoarse; + int iFine=selStart.xFine; + int ord=e->getOrder(); + for (; iCoarse<=selEnd.xCoarse; iCoarse++) { + if (!e->song.chanShow[iCoarse]) continue; + DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][ord],true); + for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsedata[j][0]; + int origOctave=(signed char)pat->data[j][1]; + if (origNote!=0 && origNote!=100 && origNote!=101 && origNote!=102) { + origNote+=amount; + while (origNote>12) { + origNote-=12; + origOctave++; + } + while (origNote<1) { + origNote+=12; + origOctave--; + } + if (origOctave==9 && origNote>11) { + origNote=11; + origOctave=9; + } + if (origOctave>9) { + origNote=11; + origOctave=9; + } + if (origOctave<-5) { + origNote=1; + origOctave=-5; + } + pat->data[j][0]=origNote; + pat->data[j][1]=(unsigned char)origOctave; + } + } else { + int top=255; + if (iFine==1) { + if (e->song.ins.empty()) continue; + top=e->song.ins.size()-1; + } else if (iFine==2) { // volume + top=e->getMaxVolumeChan(iCoarse); + } + if (pat->data[j][iFine+1]==-1) continue; + pat->data[j][iFine+1]=MIN(top,MAX(0,pat->data[j][iFine+1]+amount)); + } + } + } + iFine=0; + } + + makeUndo(GUI_UNDO_PATTERN_DELETE); +} + +void FurnaceGUI::doCopy(bool cut) { + finishSelection(); + if (cut) { + curNibble=false; + prepareUndo(GUI_UNDO_PATTERN_CUT); + } + clipboard=fmt::sprintf("org.tildearrow.furnace - Pattern Data (%d)\n%d",DIV_ENGINE_VERSION,selStart.xFine); + + for (int j=selStart.y; j<=selEnd.y; j++) { + int iCoarse=selStart.xCoarse; + int iFine=selStart.xFine; + if (iFine>3 && !(iFine&1)) { + iFine--; + } + int ord=e->getOrder(); + clipboard+='\n'; + for (; iCoarse<=selEnd.xCoarse; iCoarse++) { + if (!e->song.chanShow[iCoarse]) continue; + DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][ord],true); + for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsedata[j][0],pat->data[j][1]); + if (cut) { + pat->data[j][0]=0; + pat->data[j][1]=0; + } + } else { + if (pat->data[j][iFine+1]==-1) { + clipboard+=".."; + } else { + clipboard+=fmt::sprintf("%.2X",pat->data[j][iFine+1]); + } + if (cut) { + pat->data[j][iFine+1]=-1; + } + } + } + clipboard+='|'; + iFine=0; + } + } + SDL_SetClipboardText(clipboard.c_str()); + + if (cut) { + makeUndo(GUI_UNDO_PATTERN_CUT); + } +} + +void FurnaceGUI::doPaste(PasteMode mode) { + finishSelection(); + prepareUndo(GUI_UNDO_PATTERN_PASTE); + char* clipText=SDL_GetClipboardText(); + if (clipText!=NULL) { + if (clipText[0]) { + clipboard=clipText; + } + SDL_free(clipText); + } + std::vector data; + String tempS; + for (char i: clipboard) { + if (i=='\r') continue; + if (i=='\n') { + data.push_back(tempS); + tempS=""; + continue; + } + tempS+=i; + } + data.push_back(tempS); + + int startOff=-1; + bool invalidData=false; + if (data.size()<2) return; + if (data[0]!=fmt::sprintf("org.tildearrow.furnace - Pattern Data (%d)",DIV_ENGINE_VERSION)) return; + if (sscanf(data[1].c_str(),"%d",&startOff)!=1) return; + if (startOff<0) return; + + DETERMINE_LAST; + + int j=cursor.y; + char note[4]; + int ord=e->getOrder(); + for (size_t i=2; isong.patLen; i++) { + size_t charPos=0; + int iCoarse=cursor.xCoarse; + int iFine=(startOff>2 && cursor.xFine>2)?(((cursor.xFine-1)&(~1))|1):startOff; + + String& line=data[i]; + + while (charPossong.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][ord],true); + if (line[charPos]=='|') { + iCoarse++; + if (iCoarsesong.chanShow[iCoarse]) { + iCoarse++; + if (iCoarse>=lastChannel) break; + } + iFine=0; + charPos++; + continue; + } + if (iFine==0) { + if (charPos>=line.size()) { + invalidData=true; + break; + } + note[0]=line[charPos++]; + if (charPos>=line.size()) { + invalidData=true; + break; + } + note[1]=line[charPos++]; + if (charPos>=line.size()) { + invalidData=true; + break; + } + note[2]=line[charPos++]; + note[3]=0; + + if (iFine==0 && !opMaskNote) { + iFine++; + continue; + } + + if ((mode==GUI_PASTE_MODE_MIX_BG || mode==GUI_PASTE_MODE_MIX_FG) && strcmp(note,"...")==0) { + // do nothing. + } else { + if (mode!=GUI_PASTE_MODE_MIX_BG || (pat->data[j][0]==0 && pat->data[j][1]==0)) { + if (!decodeNote(note,pat->data[j][0],pat->data[j][1])) { + invalidData=true; + break; + } + } + } + } else { + if (charPos>=line.size()) { + invalidData=true; + break; + } + note[0]=line[charPos++]; + if (charPos>=line.size()) { + invalidData=true; + break; + } + note[1]=line[charPos++]; + note[2]=0; + + if (iFine==1) { + if (!opMaskIns) { + iFine++; + continue; + } + } else if (iFine==2) { + if (!opMaskVol) { + iFine++; + continue; + } + } else if ((iFine&1)==0) { + if (!opMaskEffectVal) { + iFine++; + continue; + } + } else if ((iFine&1)==1) { + if (!opMaskEffect) { + iFine++; + continue; + } + } + + if (strcmp(note,"..")==0) { + if (!(mode==GUI_PASTE_MODE_MIX_BG || mode==GUI_PASTE_MODE_MIX_FG)) { + pat->data[j][iFine+1]=-1; + } + } else { + unsigned int val=0; + if (sscanf(note,"%2X",&val)!=1) { + invalidData=true; + break; + } + if (mode!=GUI_PASTE_MODE_MIX_BG || pat->data[j][iFine+1]==-1) { + if (iFine<(3+e->song.pat[iCoarse].effectRows*2)) pat->data[j][iFine+1]=val; + } + } + } + iFine++; + } + + if (invalidData) { + logW("invalid clipboard data! failed at line %d char %d\n",i,charPos); + logW("%s\n",line.c_str()); + break; + } + j++; + if (mode==GUI_PASTE_MODE_OVERFLOW && j>=e->song.patLen && ordsong.ordersLen-1) { + j=0; + ord++; + } + + if (mode==GUI_PASTE_MODE_FLOOD && i==data.size()-1) { + i=1; + } + } + + makeUndo(GUI_UNDO_PATTERN_PASTE); +} + +void FurnaceGUI::doChangeIns(int ins) { + finishSelection(); + prepareUndo(GUI_UNDO_PATTERN_CHANGE_INS); + + int iCoarse=selStart.xCoarse; + int ord=e->getOrder(); + for (; iCoarse<=selEnd.xCoarse; iCoarse++) { + if (!e->song.chanShow[iCoarse]) continue; + DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][ord],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)) { + pat->data[j][2]=ins; + } + } + } + + makeUndo(GUI_UNDO_PATTERN_CHANGE_INS); +} + +void FurnaceGUI::doInterpolate() { + finishSelection(); + prepareUndo(GUI_UNDO_PATTERN_INTERPOLATE); + + std::vector> points; + int iCoarse=selStart.xCoarse; + int iFine=selStart.xFine; + int ord=e->getOrder(); + for (; iCoarse<=selEnd.xCoarse; iCoarse++) { + if (!e->song.chanShow[iCoarse]) continue; + DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][ord],true); + for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsedata[j][iFine+1]!=-1) { + points.emplace(points.end(),j,pat->data[j][iFine+1]); + } + } + + if (points.size()>1) for (size_t j=0; j& curPoint=points[j]; + std::pair& nextPoint=points[j+1]; + double distance=nextPoint.first-curPoint.first; + for (int k=0; k<(nextPoint.first-curPoint.first); k++) { + pat->data[k+curPoint.first][iFine+1]=curPoint.second+((nextPoint.second-curPoint.second)*(double)k/distance); + } + } + } 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]!=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); + } + } + } + + if (points.size()>1) for (size_t j=0; j& curPoint=points[j]; + std::pair& nextPoint=points[j+1]; + double distance=nextPoint.first-curPoint.first; + for (int k=0; k<(nextPoint.first-curPoint.first); k++) { + int val=curPoint.second+((nextPoint.second-curPoint.second)*(double)k/distance); + pat->data[k+curPoint.first][0]=val%12; + pat->data[k+curPoint.first][1]=val/12; + if (pat->data[k+curPoint.first][0]==0) { + pat->data[k+curPoint.first][0]=12; + pat->data[k+curPoint.first][1]--; + } + pat->data[k+curPoint.first][1]&=255; + } + } + } + } + iFine=0; + } + + makeUndo(GUI_UNDO_PATTERN_INTERPOLATE); +} + +void FurnaceGUI::doFade(int p0, int p1, bool mode) { + finishSelection(); + prepareUndo(GUI_UNDO_PATTERN_FADE); + + int iCoarse=selStart.xCoarse; + int iFine=selStart.xFine; + int ord=e->getOrder(); + for (; iCoarse<=selEnd.xCoarse; iCoarse++) { + if (!e->song.chanShow[iCoarse]) continue; + DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][ord],true); + for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsesong.ins.empty()) continue; + absoluteTop=e->song.ins.size()-1; + } else if (iFine==2) { // volume + absoluteTop=e->getMaxVolumeChan(iCoarse); + } + if (selEnd.y-selStart.y<1) continue; + for (int j=selStart.y; j<=selEnd.y; j++) { + double fraction=double(j-selStart.y)/double(selEnd.y-selStart.y); + int value=p0+double(p1-p0)*fraction; + if (mode) { // nibble + value&=15; + pat->data[j][iFine+1]=MIN(absoluteTop,value|(value<<4)); + } else { // byte + pat->data[j][iFine+1]=MIN(absoluteTop,value); + } + } + } + } + iFine=0; + } + + makeUndo(GUI_UNDO_PATTERN_FADE); +} + +void FurnaceGUI::doInvertValues() { + finishSelection(); + prepareUndo(GUI_UNDO_PATTERN_INVERT_VAL); + + int iCoarse=selStart.xCoarse; + int iFine=selStart.xFine; + int ord=e->getOrder(); + for (; iCoarse<=selEnd.xCoarse; iCoarse++) { + if (!e->song.chanShow[iCoarse]) continue; + DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][ord],true); + for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsesong.ins.empty()) continue; + top=e->song.ins.size()-1; + } else if (iFine==2) { // volume + top=e->getMaxVolumeChan(iCoarse); + } + for (int j=selStart.y; j<=selEnd.y; j++) { + if (pat->data[j][iFine+1]==-1) continue; + pat->data[j][iFine+1]=top-pat->data[j][iFine+1]; + } + } + } + iFine=0; + } + + makeUndo(GUI_UNDO_PATTERN_INVERT_VAL); +} + +void FurnaceGUI::doScale(float top) { + finishSelection(); + prepareUndo(GUI_UNDO_PATTERN_SCALE); + + int iCoarse=selStart.xCoarse; + int iFine=selStart.xFine; + int ord=e->getOrder(); + for (; iCoarse<=selEnd.xCoarse; iCoarse++) { + if (!e->song.chanShow[iCoarse]) continue; + DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][ord],true); + for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsesong.ins.empty()) continue; + absoluteTop=e->song.ins.size()-1; + } else if (iFine==2) { // volume + absoluteTop=e->getMaxVolumeChan(iCoarse); + } + for (int j=selStart.y; j<=selEnd.y; j++) { + if (pat->data[j][iFine+1]==-1) continue; + pat->data[j][iFine+1]=MIN(absoluteTop,(double)pat->data[j][iFine+1]*(top/100.0f)); + } + } + } + iFine=0; + } + + makeUndo(GUI_UNDO_PATTERN_SCALE); +} + +void FurnaceGUI::doRandomize(int bottom, int top, bool mode) { + finishSelection(); + prepareUndo(GUI_UNDO_PATTERN_RANDOMIZE); + + int iCoarse=selStart.xCoarse; + int iFine=selStart.xFine; + int ord=e->getOrder(); + for (; iCoarse<=selEnd.xCoarse; iCoarse++) { + if (!e->song.chanShow[iCoarse]) continue; + DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][ord],true); + for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsesong.ins.empty()) continue; + absoluteTop=e->song.ins.size()-1; + } else if (iFine==2) { // volume + absoluteTop=e->getMaxVolumeChan(iCoarse); + } + for (int j=selStart.y; j<=selEnd.y; j++) { + int value=0; + int value2=0; + if (top-bottom<=0) { + value=MIN(absoluteTop,bottom); + value2=MIN(absoluteTop,bottom); + } else { + value=MIN(absoluteTop,bottom+(rand()%(top-bottom+1))); + value2=MIN(absoluteTop,bottom+(rand()%(top-bottom+1))); + } + if (mode) { + value&=15; + value2&=15; + pat->data[j][iFine+1]=value|(value2<<4); + } else { + pat->data[j][iFine+1]=value; + } + } + } + } + iFine=0; + } + + makeUndo(GUI_UNDO_PATTERN_RANDOMIZE); +} + +void FurnaceGUI::doFlip() { + finishSelection(); + prepareUndo(GUI_UNDO_PATTERN_FLIP); + + DivPattern patBuffer; + int iCoarse=selStart.xCoarse; + int iFine=selStart.xFine; + int ord=e->getOrder(); + for (; iCoarse<=selEnd.xCoarse; iCoarse++) { + if (!e->song.chanShow[iCoarse]) continue; + DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][ord],true); + for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsedata[j][0]; + } + patBuffer.data[j][iFine+1]=pat->data[j][iFine+1]; + } + for (int j=selStart.y; j<=selEnd.y; j++) { + if (iFine==0) { + pat->data[j][0]=patBuffer.data[selEnd.y-j+selStart.y][0]; + } + pat->data[j][iFine+1]=patBuffer.data[selEnd.y-j+selStart.y][iFine+1]; + } + } + iFine=0; + } + + makeUndo(GUI_UNDO_PATTERN_FLIP); +} + +void FurnaceGUI::doCollapse(int divider) { + finishSelection(); + prepareUndo(GUI_UNDO_PATTERN_COLLAPSE); + + DivPattern patBuffer; + int iCoarse=selStart.xCoarse; + int iFine=selStart.xFine; + int ord=e->getOrder(); + for (; iCoarse<=selEnd.xCoarse; iCoarse++) { + if (!e->song.chanShow[iCoarse]) continue; + DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][ord],true); + for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsedata[j][0]; + } + patBuffer.data[j][iFine+1]=pat->data[j][iFine+1]; + } + for (int j=0; j<=selEnd.y-selStart.y; j++) { + if (j*divider>=selEnd.y-selStart.y) { + if (iFine==0) { + pat->data[j+selStart.y][0]=0; + pat->data[j+selStart.y][1]=0; + } else { + pat->data[j+selStart.y][iFine+1]=-1; + } + } else { + if (iFine==0) { + pat->data[j+selStart.y][0]=patBuffer.data[j*divider+selStart.y][0]; + } + pat->data[j+selStart.y][iFine+1]=patBuffer.data[j*divider+selStart.y][iFine+1]; + + if (iFine==0) { + for (int k=1; k=selEnd.y-selStart.y) break; + if (!(pat->data[j+selStart.y][0]==0 && pat->data[j+selStart.y][1]==0)) break; + pat->data[j+selStart.y][0]=patBuffer.data[j*divider+selStart.y+k][0]; + pat->data[j+selStart.y][1]=patBuffer.data[j*divider+selStart.y+k][1]; + } + } else { + for (int k=1; k=selEnd.y-selStart.y) break; + if (pat->data[j+selStart.y][iFine+1]!=-1) break; + pat->data[j+selStart.y][iFine+1]=patBuffer.data[j*divider+selStart.y+k][iFine+1]; + } + } + } + } + } + iFine=0; + } + + makeUndo(GUI_UNDO_PATTERN_COLLAPSE); +} + +void FurnaceGUI::doExpand(int multiplier) { + if (multiplier<1) return; + + finishSelection(); + prepareUndo(GUI_UNDO_PATTERN_EXPAND); + + DivPattern patBuffer; + int iCoarse=selStart.xCoarse; + int iFine=selStart.xFine; + int ord=e->getOrder(); + for (; iCoarse<=selEnd.xCoarse; iCoarse++) { + if (!e->song.chanShow[iCoarse]) continue; + DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][ord],true); + for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsedata[j][0]; + } + patBuffer.data[j][iFine+1]=pat->data[j][iFine+1]; + } + for (int j=0; j<=(selEnd.y-selStart.y)*multiplier; j++) { + if ((j+selStart.y)>=e->song.patLen) break; + if ((j%multiplier)!=0) { + if (iFine==0) { + pat->data[j+selStart.y][0]=0; + pat->data[j+selStart.y][1]=0; + } else { + pat->data[j+selStart.y][iFine+1]=-1; + } + continue; + } + if (iFine==0) { + pat->data[j+selStart.y][0]=patBuffer.data[j/multiplier+selStart.y][0]; + } + pat->data[j+selStart.y][iFine+1]=patBuffer.data[j/multiplier+selStart.y][iFine+1]; + } + } + iFine=0; + } + + makeUndo(GUI_UNDO_PATTERN_EXPAND); +} + +void FurnaceGUI::doUndo() { + if (undoHist.empty()) return; + UndoStep& us=undoHist.back(); + redoHist.push_back(us); + MARK_MODIFIED; + + switch (us.type) { + case GUI_UNDO_CHANGE_ORDER: + e->song.ordersLen=us.oldOrdersLen; + for (UndoOrderData& i: us.ord) { + e->song.orders.ord[i.chan][i.ord]=i.oldVal; + } + break; + case GUI_UNDO_PATTERN_EDIT: + case GUI_UNDO_PATTERN_DELETE: + case GUI_UNDO_PATTERN_PULL: + case GUI_UNDO_PATTERN_PUSH: + case GUI_UNDO_PATTERN_CUT: + case GUI_UNDO_PATTERN_PASTE: + case GUI_UNDO_PATTERN_CHANGE_INS: + case GUI_UNDO_PATTERN_INTERPOLATE: + case GUI_UNDO_PATTERN_FADE: + case GUI_UNDO_PATTERN_SCALE: + case GUI_UNDO_PATTERN_RANDOMIZE: + case GUI_UNDO_PATTERN_INVERT_VAL: + case GUI_UNDO_PATTERN_FLIP: + case GUI_UNDO_PATTERN_COLLAPSE: + case GUI_UNDO_PATTERN_EXPAND: + for (UndoPatternData& i: us.pat) { + DivPattern* p=e->song.pat[i.chan].getPattern(i.pat,true); + p->data[i.row][i.col]=i.oldVal; + } + if (!e->isPlaying()) { + cursor=us.cursor; + selStart=us.selStart; + selEnd=us.selEnd; + curNibble=us.nibble; + updateScroll(cursor.y); + e->setOrder(us.order); + } + break; + } + + undoHist.pop_back(); +} + +void FurnaceGUI::doRedo() { + if (redoHist.empty()) return; + UndoStep& us=redoHist.back(); + undoHist.push_back(us); + MARK_MODIFIED; + + switch (us.type) { + case GUI_UNDO_CHANGE_ORDER: + e->song.ordersLen=us.newOrdersLen; + for (UndoOrderData& i: us.ord) { + e->song.orders.ord[i.chan][i.ord]=i.newVal; + } + break; + case GUI_UNDO_PATTERN_EDIT: + case GUI_UNDO_PATTERN_DELETE: + case GUI_UNDO_PATTERN_PULL: + case GUI_UNDO_PATTERN_PUSH: + case GUI_UNDO_PATTERN_CUT: + case GUI_UNDO_PATTERN_PASTE: + case GUI_UNDO_PATTERN_CHANGE_INS: + case GUI_UNDO_PATTERN_INTERPOLATE: + case GUI_UNDO_PATTERN_FADE: + case GUI_UNDO_PATTERN_SCALE: + case GUI_UNDO_PATTERN_RANDOMIZE: + case GUI_UNDO_PATTERN_INVERT_VAL: + case GUI_UNDO_PATTERN_FLIP: + case GUI_UNDO_PATTERN_COLLAPSE: + case GUI_UNDO_PATTERN_EXPAND: + for (UndoPatternData& i: us.pat) { + DivPattern* p=e->song.pat[i.chan].getPattern(i.pat,true); + p->data[i.row][i.col]=i.newVal; + } + if (!e->isPlaying()) { + cursor=us.cursor; + selStart=us.selStart; + selEnd=us.selEnd; + curNibble=us.nibble; + updateScroll(cursor.y); + e->setOrder(us.order); + } + + break; + } + + redoHist.pop_back(); +} \ No newline at end of file diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index f3ae5a7f3..2ba2131cc 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -20,15 +20,12 @@ #define _USE_MATH_DEFINES #include "gui.h" #include "util.h" -#include "debug.h" -#include "fonts.h" #include "icon.h" #include "../ta-log.h" #include "../fileutils.h" #include "imgui.h" #include "imgui_impl_sdl.h" #include "imgui_impl_sdlrenderer.h" -#include "imgui_internal.h" #include "ImGuiFileDialog.h" #include "IconsFontAwesome4.h" #include "misc/cpp/imgui_stdlib.h" @@ -61,6 +58,8 @@ extern "C" { #define BACKUP_FUR "/backup.fur" #endif +#include "actionUtil.h" + bool Particle::update(float frameTime) { pos.x+=speed.x*frameTime; pos.y+=speed.y*frameTime; @@ -75,23 +74,6 @@ void FurnaceGUI::bindEngine(DivEngine* eng) { e=eng; } -const char* noteNameNormal(short note, short octave) { - if (note==100) { // note cut - return "OFF"; - } else if (note==101) { // note off and envelope release - return "==="; - } else if (note==102) { // envelope release only - return "REL"; - } else if (octave==0 && note==0) { - return "..."; - } - int seek=(note+(signed char)octave*12)+60; - if (seek<0 || seek>=180) { - return "???"; - } - return noteNames[seek]; -} - const char* FurnaceGUI::noteName(short note, short octave) { if (note==100) { return "OFF"; @@ -678,2636 +660,6 @@ float FurnaceGUI::calcBPM(int s1, int s2, float hz) { return 120.0f*hz/(timeBase*hl*speedSum); } -void FurnaceGUI::drawEditControls() { - if (nextWindow==GUI_WINDOW_EDIT_CONTROLS) { - editControlsOpen=true; - ImGui::SetNextWindowFocus(); - nextWindow=GUI_WINDOW_NOTHING; - } - if (!editControlsOpen) return; - switch (settings.controlLayout) { - case 0: // classic - if (ImGui::Begin("Play/Edit Controls",&editControlsOpen)) { - ImGui::Text("Octave"); - ImGui::SameLine(); - if (ImGui::InputInt("##Octave",&curOctave,1,1)) { - if (curOctave>7) curOctave=7; - if (curOctave<-5) curOctave=-5; - for (size_t i=0; inoteOff(activeNotes[i].chan); - } - activeNotes.clear(); - - if (settings.insFocusesPattern && !ImGui::IsItemActive() && patternOpen) { - nextWindow=GUI_WINDOW_PATTERN; - } - } - - ImGui::Text("Edit Step"); - ImGui::SameLine(); - if (ImGui::InputInt("##EditStep",&editStep,1,1)) { - if (editStep>=e->song.patLen) editStep=e->song.patLen-1; - if (editStep<0) editStep=0; - - if (settings.insFocusesPattern && !ImGui::IsItemActive() && patternOpen) { - nextWindow=GUI_WINDOW_PATTERN; - } - } - - if (ImGui::Button(ICON_FA_PLAY "##Play")) { - play(); - } - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_STOP "##Stop")) { - stop(); - } - ImGui::SameLine(); - ImGui::Checkbox("Edit",&edit); - ImGui::SameLine(); - bool metro=e->getMetronome(); - if (ImGui::Checkbox("Metronome",&metro)) { - e->setMetronome(metro); - } - - ImGui::Text("Follow"); - ImGui::SameLine(); - unimportant(ImGui::Checkbox("Orders",&followOrders)); - ImGui::SameLine(); - unimportant(ImGui::Checkbox("Pattern",&followPattern)); - - bool repeatPattern=e->getRepeatPattern(); - if (ImGui::Checkbox("Repeat pattern",&repeatPattern)) { - e->setRepeatPattern(repeatPattern); - } - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_ARROW_DOWN "##StepOne")) { - e->stepOne(cursor.y); - } - } - if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_EDIT_CONTROLS; - ImGui::End(); - break; - case 1: // compact - if (ImGui::Begin("Play/Edit Controls",&editControlsOpen,ImGuiWindowFlags_NoScrollbar|ImGuiWindowFlags_NoScrollWithMouse)) { - if (ImGui::Button(ICON_FA_STOP "##Stop")) { - stop(); - } - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_PLAY "##Play")) { - play(); - } - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_ARROW_DOWN "##StepOne")) { - e->stepOne(cursor.y); - } - - ImGui::SameLine(); - bool repeatPattern=e->getRepeatPattern(); - ImGui::PushStyleColor(ImGuiCol_Button,ImVec4(0.2f,(repeatPattern)?0.6f:0.2f,0.2f,1.0f)); - if (ImGui::Button(ICON_FA_REPEAT "##RepeatPattern")) { - e->setRepeatPattern(!repeatPattern); - } - ImGui::PopStyleColor(); - - ImGui::SameLine(); - ImGui::PushStyleColor(ImGuiCol_Button,ImVec4(0.2f,(edit)?0.6f:0.2f,0.2f,1.0f)); - if (ImGui::Button(ICON_FA_CIRCLE "##Edit")) { - edit=!edit; - } - ImGui::PopStyleColor(); - - ImGui::SameLine(); - bool metro=e->getMetronome(); - ImGui::PushStyleColor(ImGuiCol_Button,ImVec4(0.2f,(metro)?0.6f:0.2f,0.2f,1.0f)); - if (ImGui::Button(ICON_FA_BELL_O "##Metronome")) { - e->setMetronome(!metro); - } - ImGui::PopStyleColor(); - - ImGui::SameLine(); - ImGui::Text("Octave"); - ImGui::SameLine(); - ImGui::SetNextItemWidth(96.0f*dpiScale); - if (ImGui::InputInt("##Octave",&curOctave,1,1)) { - if (curOctave>7) curOctave=7; - if (curOctave<-5) curOctave=-5; - for (size_t i=0; inoteOff(activeNotes[i].chan); - } - activeNotes.clear(); - - if (settings.insFocusesPattern && !ImGui::IsItemActive() && patternOpen) { - nextWindow=GUI_WINDOW_PATTERN; - } - } - - ImGui::SameLine(); - ImGui::Text("Edit Step"); - ImGui::SameLine(); - ImGui::SetNextItemWidth(96.0f*dpiScale); - if (ImGui::InputInt("##EditStep",&editStep,1,1)) { - if (editStep>=e->song.patLen) editStep=e->song.patLen-1; - if (editStep<0) editStep=0; - - if (settings.insFocusesPattern && !ImGui::IsItemActive() && patternOpen) { - nextWindow=GUI_WINDOW_PATTERN; - } - } - - ImGui::SameLine(); - ImGui::Text("Follow"); - ImGui::SameLine(); - unimportant(ImGui::Checkbox("Orders",&followOrders)); - ImGui::SameLine(); - unimportant(ImGui::Checkbox("Pattern",&followPattern)); - } - if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_EDIT_CONTROLS; - ImGui::End(); - break; - case 2: // compact vertical - if (ImGui::Begin("Play/Edit Controls",&editControlsOpen,ImGuiWindowFlags_NoScrollbar|ImGuiWindowFlags_NoScrollWithMouse)) { - if (ImGui::Button(ICON_FA_PLAY "##Play")) { - play(); - } - if (ImGui::Button(ICON_FA_STOP "##Stop")) { - stop(); - } - if (ImGui::Button(ICON_FA_ARROW_DOWN "##StepOne")) { - e->stepOne(cursor.y); - } - - bool repeatPattern=e->getRepeatPattern(); - ImGui::PushStyleColor(ImGuiCol_Button,ImVec4(0.2f,(repeatPattern)?0.6f:0.2f,0.2f,1.0f)); - if (ImGui::Button(ICON_FA_REPEAT "##RepeatPattern")) { - e->setRepeatPattern(!repeatPattern); - } - ImGui::PopStyleColor(); - - ImGui::PushStyleColor(ImGuiCol_Button,ImVec4(0.2f,(edit)?0.6f:0.2f,0.2f,1.0f)); - if (ImGui::Button(ICON_FA_CIRCLE "##Edit")) { - edit=!edit; - } - ImGui::PopStyleColor(); - - bool metro=e->getMetronome(); - ImGui::PushStyleColor(ImGuiCol_Button,ImVec4(0.2f,(metro)?0.6f:0.2f,0.2f,1.0f)); - if (ImGui::Button(ICON_FA_BELL_O "##Metronome")) { - e->setMetronome(!metro); - } - ImGui::PopStyleColor(); - - ImGui::Text("Oct."); - 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; - for (size_t i=0; inoteOff(activeNotes[i].chan); - } - activeNotes.clear(); - - if (settings.insFocusesPattern && !ImGui::IsItemActive() && patternOpen) { - nextWindow=GUI_WINDOW_PATTERN; - } - } - - ImGui::Text("Step"); - ImGui::SetNextItemWidth(avail); - if (ImGui::InputInt("##EditStep",&editStep,0,0)) { - if (editStep>=e->song.patLen) editStep=e->song.patLen-1; - if (editStep<0) editStep=0; - - if (settings.insFocusesPattern && !ImGui::IsItemActive() && patternOpen) { - nextWindow=GUI_WINDOW_PATTERN; - } - } - - ImGui::Text("Foll."); - ImGui::PushStyleColor(ImGuiCol_Button,ImVec4(0.2f,(followOrders)?0.6f:0.2f,0.2f,1.0f)); - if (ImGui::SmallButton("Ord##FollowOrders")) { handleUnimportant - followOrders=!followOrders; - } - ImGui::PopStyleColor(); - ImGui::PushStyleColor(ImGuiCol_Button,ImVec4(0.2f,(followPattern)?0.6f:0.2f,0.2f,1.0f)); - if (ImGui::SmallButton("Pat##FollowPattern")) { handleUnimportant - followPattern=!followPattern; - } - ImGui::PopStyleColor(); - } - if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_EDIT_CONTROLS; - ImGui::End(); - break; - case 3: // split - if (ImGui::Begin("Play Controls",&editControlsOpen,ImGuiWindowFlags_NoScrollbar|ImGuiWindowFlags_NoScrollWithMouse)) { - if (e->isPlaying()) { - if (ImGui::Button(ICON_FA_STOP "##Stop")) { - stop(); - } - } else { - if (ImGui::Button(ICON_FA_PLAY "##Play")) { - play(); - } - } - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_PLAY_CIRCLE "##PlayAgain")) { - play(); - } - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_ARROW_DOWN "##StepOne")) { - e->stepOne(cursor.y); - } - - ImGui::SameLine(); - ImGui::PushStyleColor(ImGuiCol_Button,ImVec4(0.2f,(edit)?0.6f:0.2f,0.2f,1.0f)); - if (ImGui::Button(ICON_FA_CIRCLE "##Edit")) { - edit=!edit; - } - ImGui::PopStyleColor(); - - bool metro=e->getMetronome(); - ImGui::SameLine(); - ImGui::PushStyleColor(ImGuiCol_Button,ImVec4(0.2f,(metro)?0.6f:0.2f,0.2f,1.0f)); - if (ImGui::Button(ICON_FA_BELL_O "##Metronome")) { - e->setMetronome(!metro); - } - ImGui::PopStyleColor(); - - ImGui::SameLine(); - bool repeatPattern=e->getRepeatPattern(); - ImGui::PushStyleColor(ImGuiCol_Button,ImVec4(0.2f,(repeatPattern)?0.6f:0.2f,0.2f,1.0f)); - if (ImGui::Button(ICON_FA_REPEAT "##RepeatPattern")) { - e->setRepeatPattern(!repeatPattern); - } - ImGui::PopStyleColor(); - } - if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_EDIT_CONTROLS; - ImGui::End(); - - if (ImGui::Begin("Edit Controls",&editControlsOpen)) { - ImGui::Columns(2); - ImGui::Text("Octave"); - ImGui::SameLine(); - float cursor=ImGui::GetCursorPosX(); - 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; - for (size_t i=0; inoteOff(activeNotes[i].chan); - } - activeNotes.clear(); - - if (settings.insFocusesPattern && !ImGui::IsItemActive() && patternOpen) { - nextWindow=GUI_WINDOW_PATTERN; - } - } - - ImGui::Text("Step"); - ImGui::SameLine(); - ImGui::SetCursorPosX(cursor); - ImGui::SetNextItemWidth(avail); - if (ImGui::InputInt("##EditStep",&editStep,1,1)) { - if (editStep>=e->song.patLen) editStep=e->song.patLen-1; - if (editStep<0) editStep=0; - - if (settings.insFocusesPattern && !ImGui::IsItemActive() && patternOpen) { - nextWindow=GUI_WINDOW_PATTERN; - } - } - ImGui::NextColumn(); - - unimportant(ImGui::Checkbox("Follow orders",&followOrders)); - unimportant(ImGui::Checkbox("Follow pattern",&followPattern)); - } - if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_EDIT_CONTROLS; - ImGui::End(); - break; - } -} - -void FurnaceGUI::drawSongInfo() { - if (nextWindow==GUI_WINDOW_SONG_INFO) { - songInfoOpen=true; - ImGui::SetNextWindowFocus(); - nextWindow=GUI_WINDOW_NOTHING; - } - if (!songInfoOpen) return; - if (ImGui::Begin("Song Information",&songInfoOpen)) { - if (ImGui::BeginTable("NameAuthor",2,ImGuiTableFlags_SizingStretchProp)) { - ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthFixed,0.0); - ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthStretch,0.0); - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - ImGui::Text("Name"); - ImGui::TableNextColumn(); - float avail=ImGui::GetContentRegionAvail().x; - ImGui::SetNextItemWidth(avail); - if (ImGui::InputText("##Name",&e->song.name)) { MARK_MODIFIED - updateWindowTitle(); - } - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - ImGui::Text("Author"); - ImGui::TableNextColumn(); - ImGui::SetNextItemWidth(avail); - if (ImGui::InputText("##Author",&e->song.author)) { - MARK_MODIFIED; - } - ImGui::EndTable(); - } - - if (ImGui::BeginTable("OtherProps",3,ImGuiTableFlags_SizingStretchProp)) { - ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthFixed,0.0); - ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthStretch,0.0); - ImGui::TableSetupColumn("c2",ImGuiTableColumnFlags_WidthStretch,0.0); - - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - ImGui::Text("TimeBase"); - ImGui::TableNextColumn(); - float avail=ImGui::GetContentRegionAvail().x; - ImGui::SetNextItemWidth(avail); - unsigned char realTB=e->song.timeBase+1; - if (ImGui::InputScalar("##TimeBase",ImGuiDataType_U8,&realTB,&_ONE,&_THREE)) { MARK_MODIFIED - if (realTB<1) realTB=1; - if (realTB>16) realTB=16; - e->song.timeBase=realTB-1; - } - ImGui::TableNextColumn(); - ImGui::Text("%.2f BPM",calcBPM(e->song.speed1,e->song.speed2,e->song.hz)); - - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - ImGui::Text("Speed"); - ImGui::TableNextColumn(); - ImGui::SetNextItemWidth(avail); - if (ImGui::InputScalar("##Speed1",ImGuiDataType_U8,&e->song.speed1,&_ONE,&_THREE)) { MARK_MODIFIED - if (e->song.speed1<1) e->song.speed1=1; - if (e->isPlaying()) play(); - } - ImGui::TableNextColumn(); - ImGui::SetNextItemWidth(avail); - if (ImGui::InputScalar("##Speed2",ImGuiDataType_U8,&e->song.speed2,&_ONE,&_THREE)) { MARK_MODIFIED - if (e->song.speed2<1) e->song.speed2=1; - if (e->isPlaying()) play(); - } - - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - ImGui::Text("Highlight"); - ImGui::TableNextColumn(); - ImGui::SetNextItemWidth(avail); - if (ImGui::InputScalar("##Highlight1",ImGuiDataType_U8,&e->song.hilightA,&_ONE,&_THREE)) { - MARK_MODIFIED; - } - ImGui::TableNextColumn(); - ImGui::SetNextItemWidth(avail); - if (ImGui::InputScalar("##Highlight2",ImGuiDataType_U8,&e->song.hilightB,&_ONE,&_THREE)) { - MARK_MODIFIED; - } - - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - ImGui::Text("Pattern Length"); - ImGui::TableNextColumn(); - ImGui::SetNextItemWidth(avail); - int patLen=e->song.patLen; - if (ImGui::InputInt("##PatLength",&patLen,1,3)) { MARK_MODIFIED - if (patLen<1) patLen=1; - if (patLen>256) patLen=256; - e->song.patLen=patLen; - } - - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - ImGui::Text("Song Length"); - ImGui::TableNextColumn(); - ImGui::SetNextItemWidth(avail); - int ordLen=e->song.ordersLen; - if (ImGui::InputInt("##OrdLength",&ordLen,1,3)) { MARK_MODIFIED - if (ordLen<1) ordLen=1; - if (ordLen>127) ordLen=127; - e->song.ordersLen=ordLen; - } - - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - if (ImGui::Selectable(tempoView?"Base Tempo##TempoOrHz":"Tick Rate##TempoOrHz")) { - tempoView=!tempoView; - } - ImGui::TableNextColumn(); - ImGui::SetNextItemWidth(avail); - float setHz=tempoView?e->song.hz*2.5:e->song.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>999) setHz=999; - e->setSongRate(setHz,setHz<52); - } - if (tempoView) { - ImGui::TableNextColumn(); - ImGui::Text("= %gHz",e->song.hz); - } else { - if (e->song.hz>=49.98 && e->song.hz<=50.02) { - ImGui::TableNextColumn(); - ImGui::Text("PAL"); - } - if (e->song.hz>=59.9 && e->song.hz<=60.11) { - ImGui::TableNextColumn(); - ImGui::Text("NTSC"); - } - } - - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - ImGui::Text("Tuning (A-4)"); - ImGui::TableNextColumn(); - float tune=e->song.tuning; - ImGui::SetNextItemWidth(avail); - if (ImGui::InputFloat("##Tuning",&tune,1.0f,3.0f,"%g")) { MARK_MODIFIED - if (tune<220.0f) tune=220.0f; - if (tune>880.0f) tune=880.0f; - e->song.tuning=tune; - } - ImGui::EndTable(); - } - } - if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_SONG_INFO; - ImGui::End(); -} - -void FurnaceGUI::drawMixer() { - if (nextWindow==GUI_WINDOW_MIXER) { - mixerOpen=true; - ImGui::SetNextWindowFocus(); - nextWindow=GUI_WINDOW_NOTHING; - } - if (!mixerOpen) return; - ImGui::SetNextWindowSizeConstraints(ImVec2(400.0f*dpiScale,200.0f*dpiScale),ImVec2(scrW*dpiScale,scrH*dpiScale)); - if (ImGui::Begin("Mixer",&mixerOpen,settings.allowEditDocking?0:ImGuiWindowFlags_NoDocking)) { - char id[32]; - if (ImGui::SliderFloat("Master Volume",&e->song.masterVol,0,3,"%.2fx")) { - if (e->song.masterVol<0) e->song.masterVol=0; - if (e->song.masterVol>3) e->song.masterVol=3; - } rightClickable - for (int i=0; isong.systemLen; i++) { - snprintf(id,31,"MixS%d",i); - bool doInvert=e->song.systemVol[i]&128; - signed char vol=e->song.systemVol[i]&127; - ImGui::PushID(id); - ImGui::Text("%d. %s",i+1,getSystemName(e->song.system[i])); - ImGui::SameLine(ImGui::GetWindowWidth()-(82.0f*dpiScale)); - if (ImGui::Checkbox("Invert",&doInvert)) { - e->song.systemVol[i]^=128; - } - ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x-(50.0f*dpiScale)); - if (ImGui::SliderScalar("Volume",ImGuiDataType_S8,&vol,&_ZERO,&_ONE_HUNDRED_TWENTY_SEVEN)) { - e->song.systemVol[i]=(e->song.systemVol[i]&128)|vol; - } rightClickable - ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x-(50.0f*dpiScale)); - ImGui::SliderScalar("Panning",ImGuiDataType_S8,&e->song.systemPan[i],&_MINUS_ONE_HUNDRED_TWENTY_SEVEN,&_ONE_HUNDRED_TWENTY_SEVEN); rightClickable - - ImGui::PopID(); - } - } - if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_MIXER; - ImGui::End(); -} - -void FurnaceGUI::drawOsc() { - if (nextWindow==GUI_WINDOW_OSCILLOSCOPE) { - oscOpen=true; - ImGui::SetNextWindowFocus(); - nextWindow=GUI_WINDOW_NOTHING; - } - if (!oscOpen) return; - ImGui::SetNextWindowSizeConstraints(ImVec2(64.0f*dpiScale,32.0f*dpiScale),ImVec2(scrW*dpiScale,scrH*dpiScale)); - ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding,ImVec2(0,0)); - ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing,ImVec2(0,0)); - ImGui::PushStyleVar(ImGuiStyleVar_ItemInnerSpacing,ImVec2(0,0)); - if (ImGui::Begin("Oscilloscope",&oscOpen)) { - float values[512]; - for (int i=0; i<512; i++) { - int pos=i*e->oscSize/512; - values[i]=(e->oscBuf[0][pos]+e->oscBuf[1][pos])*0.5f; - } - //ImGui::SetCursorPos(ImVec2(0,0)); - ImGui::BeginDisabled(); - ImGui::PlotLines("##SingleOsc",values,512,0,NULL,-1.0f,1.0f,ImGui::GetContentRegionAvail()); - ImGui::EndDisabled(); - } - ImGui::PopStyleVar(3); - if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_OSCILLOSCOPE; - ImGui::End(); -} - -void FurnaceGUI::drawVolMeter() { - if (nextWindow==GUI_WINDOW_VOL_METER) { - volMeterOpen=true; - ImGui::SetNextWindowFocus(); - nextWindow=GUI_WINDOW_NOTHING; - } - if (!volMeterOpen) return; - if (--isClipping<0) isClipping=0; - ImGui::SetNextWindowSizeConstraints(ImVec2(6.0f*dpiScale,6.0f*dpiScale),ImVec2(scrW*dpiScale,scrH*dpiScale)); - ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding,ImVec2(0,0)); - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding,ImVec2(0,0)); - ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing,ImVec2(0,0)); - ImGui::PushStyleVar(ImGuiStyleVar_ItemInnerSpacing,ImVec2(0,0)); - if (ImGui::Begin("Volume Meter",&volMeterOpen)) { - ImDrawList* dl=ImGui::GetWindowDrawList(); - bool aspectRatio=(ImGui::GetWindowSize().x/ImGui::GetWindowSize().y)>1.0; - - ImVec2 minArea=ImVec2( - ImGui::GetWindowPos().x+ImGui::GetCursorPos().x, - ImGui::GetWindowPos().y+ImGui::GetCursorPos().y - ); - ImVec2 maxArea=ImVec2( - ImGui::GetWindowPos().x+ImGui::GetCursorPos().x+ImGui::GetContentRegionAvail().x, - ImGui::GetWindowPos().y+ImGui::GetCursorPos().y+ImGui::GetContentRegionAvail().y - ); - ImRect rect=ImRect(minArea,maxArea); - ImGuiStyle& style=ImGui::GetStyle(); - ImGui::ItemSize(ImVec2(4.0f,4.0f),style.FramePadding.y); - ImU32 lowColor=ImGui::GetColorU32(uiColors[GUI_COLOR_VOLMETER_LOW]); - float peakDecay=0.05f*60.0f*ImGui::GetIO().DeltaTime; - if (ImGui::ItemAdd(rect,ImGui::GetID("volMeter"))) { - ImGui::RenderFrame(rect.Min,rect.Max,ImGui::GetColorU32(ImGuiCol_FrameBg),true,style.FrameRounding); - for (int i=0; i<2; i++) { - peak[i]*=1.0-peakDecay; - if (peak[i]<0.0001) peak[i]=0.0; - for (int j=0; joscSize; j++) { - if (fabs(e->oscBuf[i][j])>peak[i]) { - peak[i]=fabs(e->oscBuf[i][j]); - } - } - float logPeak=(20*log10(peak[i])/36.0); - if (logPeak==NAN) logPeak=0.0; - if (logPeak<-1.0) logPeak=-1.0; - if (logPeak>0.0) { - isClipping=8; - logPeak=0.0; - } - logPeak+=1.0; - ImU32 highColor=ImGui::GetColorU32( - ImLerp(uiColors[GUI_COLOR_VOLMETER_LOW],uiColors[GUI_COLOR_VOLMETER_HIGH],logPeak) - ); - ImRect s; - if (aspectRatio) { - s=ImRect( - ImLerp(rect.Min,rect.Max,ImVec2(0,float(i)*0.5)), - ImLerp(rect.Min,rect.Max,ImVec2(logPeak,float(i+1)*0.5)) - ); - if (i==0) s.Max.y-=dpiScale; - if (isClipping) { - dl->AddRectFilled(s.Min,s.Max,ImGui::GetColorU32(uiColors[GUI_COLOR_VOLMETER_PEAK])); - } else { - dl->AddRectFilledMultiColor(s.Min,s.Max,lowColor,highColor,highColor,lowColor); - } - } else { - s=ImRect( - ImLerp(rect.Min,rect.Max,ImVec2(float(i)*0.5,1.0-logPeak)), - ImLerp(rect.Min,rect.Max,ImVec2(float(i+1)*0.5,1.0)) - ); - if (i==0) s.Max.x-=dpiScale; - if (isClipping) { - dl->AddRectFilled(s.Min,s.Max,ImGui::GetColorU32(uiColors[GUI_COLOR_VOLMETER_PEAK])); - } else { - dl->AddRectFilledMultiColor(s.Min,s.Max,highColor,highColor,lowColor,lowColor); - } - } - } - if (ImGui::IsItemHovered()) { - if (aspectRatio) { - ImGui::SetTooltip("%.1fdB",36*((ImGui::GetMousePos().x-ImGui::GetItemRectMin().x)/(rect.Max.x-rect.Min.x)-1.0)); - } else { - ImGui::SetTooltip("%.1fdB",-(36+36*((ImGui::GetMousePos().y-ImGui::GetItemRectMin().y)/(rect.Max.y-rect.Min.y)-1.0))); - } - } - } - } - ImGui::PopStyleVar(4); - if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_VOL_METER; - ImGui::End(); -} - -const char* aboutLine[]={ - "tildearrow", - "is proud to present", - "", - ("Furnace " DIV_VERSION), - "", - "the free software chiptune tracker,", - "compatible with DefleMask modules.", - "", - "zero disassembly.", - "just clean-room design,", - "time and dedication.", - "", - "> CREDITS <", - "", - "-- program --", - "tildearrow", - "akumanatt", - "cam900", - "djtuBIG-MaliceX", - "laoo", - "superctr", - "", - "-- graphics/UI design --", - "tildearrow", - "BlastBrothers", - "", - "-- documentation --", - "tildearrow", - "freq-mod", - "nicco1690", - "DeMOSic", - "cam900", - "", - "-- demo songs --", - "0x5066", - "ActualNK358", - "breakthetargets", - "CaptainMalware", - "kleeder", - "Mahbod Karamoozian", - "nicco1690", - "NikonTeen", - "SuperJet Spade", - "TheDuccinator", - "TheRealHedgehogSonic", - "tildearrow", - "Ultraprogramer", - "", - "-- additional feedback/fixes --", - "fd", - "OPNA2608", - "plane", - "TheEssem", - "", - "powered by:", - "Dear ImGui by Omar Cornut", - "SDL2 by Sam Lantinga", - "zlib by Jean-loup Gailly", - "and Mark Adler", - "libsndfile by Erik de Castro Lopo", - "Nuked-OPM & Nuked-OPN2 by Nuke.YKT", - "ymfm by Aaron Giles", - "MAME SN76496 by Nicola Salmoria", - "MAME AY-3-8910 by Couriersud", - "with AY8930 fixes by Eulous", - "MAME SAA1099 by Juergen Buchmueller and Manuel Abadia", - "SAASound", - "SameBoy by Lior Halphon", - "Mednafen PCE", - "puNES by FHorse", - "reSID by Dag Lem", - "Stella by Stella Team", - "QSound emulator by Ian Karlsson and Valley Bell", - "", - "greetings to:", - "Delek", - "fd", - "ILLUMIDARO", - "all members of Deflers of Noice!", - "", - "copyright © 2021-2022 tildearrow", - "(and contributors).", - "licensed under GPLv2+! see", - "LICENSE for more information.", - "", - "help Furnace grow:", - "https://github.com/tildearrow/furnace", - "", - "contact tildearrow at:", - "https://tildearrow.org/?p=contact", - "", - "disclaimer:", - "despite the fact this program works", - "with the .dmf file format, it is NOT", - "affiliated with Delek or DefleMask in", - "any way, nor it is a replacement for", - "the original program.", - "", - "it also comes with ABSOLUTELY NO WARRANTY.", - "", - "thanks to all contributors/bug reporters!" -}; - -const size_t aboutCount = sizeof(aboutLine)/sizeof(aboutLine[0]); - -void FurnaceGUI::drawAbout() { - // do stuff - if (ImGui::Begin("About Furnace",NULL,ImGuiWindowFlags_Modal|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoDocking|ImGuiWindowFlags_NoTitleBar)) { - ImGui::SetWindowPos(ImVec2(0,0)); - ImGui::SetWindowSize(ImVec2(scrW*dpiScale,scrH*dpiScale)); - ImGui::PushFont(bigFont); - ImDrawList* dl=ImGui::GetWindowDrawList(); - float r=0; - float g=0; - float b=0; - float peakMix=settings.partyTime?((peak[0]+peak[1])*0.5):0.3; - ImGui::ColorConvertHSVtoRGB(aboutHue,1.0,0.25+MIN(0.75f,peakMix*0.75f),r,g,b); - dl->AddRectFilled(ImVec2(0,0),ImVec2(scrW*dpiScale,scrH*dpiScale),0xff000000); - bool skip=false; - bool skip2=false; - for (int i=(-80-sin(double(aboutSin)*2*M_PI/120.0)*80.0)*2; iAddRectFilled(ImVec2(i*dpiScale,j*dpiScale),ImVec2((i+160)*dpiScale,(j+160)*dpiScale),ImGui::GetColorU32(ImVec4(r*0.25,g*0.25,b*0.25,1.0))); - } - } - - skip=false; - skip2=false; - for (int i=(-80-cos(double(aboutSin)*2*M_PI/120.0)*80.0)*2; iAddRectFilled(ImVec2(i*dpiScale,j*dpiScale),ImVec2((i+160)*dpiScale,(j+160)*dpiScale),ImGui::GetColorU32(ImVec4(r*0.5,g*0.5,b*0.5,1.0))); - } - } - - skip=false; - skip2=false; - for (int i=(-160+fmod(aboutSin*2,160))*2; iAddRectFilled(ImVec2(i*dpiScale,j*dpiScale),ImVec2((i+160)*dpiScale,(j+160)*dpiScale),ImGui::GetColorU32(ImVec4(r*0.75,g*0.75,b*0.75,1.0))); - } - } - - for (size_t i=0; iscrH*dpiScale) continue; - dl->AddText(bigFont,bigFont->FontSize, - ImVec2(posX+dpiScale,posY+dpiScale), - 0xff000000,aboutLine[i]); - dl->AddText(bigFont,bigFont->FontSize, - ImVec2(posX+dpiScale,posY-dpiScale), - 0xff000000,aboutLine[i]); - dl->AddText(bigFont,bigFont->FontSize, - ImVec2(posX-dpiScale,posY+dpiScale), - 0xff000000,aboutLine[i]); - dl->AddText(bigFont,bigFont->FontSize, - ImVec2(posX-dpiScale,posY-dpiScale), - 0xff000000,aboutLine[i]); - dl->AddText(bigFont,bigFont->FontSize, - ImVec2(posX,posY), - 0xffffffff,aboutLine[i]); - } - ImGui::PopFont(); - - float timeScale=60.0f*ImGui::GetIO().DeltaTime; - - aboutHue+=(0.001+peakMix*0.004)*timeScale; - aboutScroll+=(2+(peakMix>0.78)*3)*timeScale; - aboutSin+=(1+(peakMix>0.75)*2)*timeScale; - - while (aboutHue>1) aboutHue--; - while (aboutSin>=2400) aboutSin-=2400; - if (aboutScroll>(42*aboutCount+scrH)) aboutScroll=-20; - } - if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_ABOUT; - ImGui::End(); -} - -void FurnaceGUI::drawDebug() { - static int bpOrder; - static int bpRow; - static int bpTick; - static bool bpOn; - if (nextWindow==GUI_WINDOW_DEBUG) { - debugOpen=true; - ImGui::SetNextWindowFocus(); - nextWindow=GUI_WINDOW_NOTHING; - } - if (!debugOpen) return; - ImGui::SetNextWindowSizeConstraints(ImVec2(400.0f*dpiScale,200.0f*dpiScale),ImVec2(scrW*dpiScale,scrH*dpiScale)); - if (ImGui::Begin("Debug",&debugOpen,ImGuiWindowFlags_NoDocking)) { - ImGui::Text("NOTE: use with caution."); - if (ImGui::TreeNode("Debug Controls")) { - if (e->isHalted()) { - if (ImGui::Button("Resume")) e->resume(); - } else { - if (ImGui::Button("Pause")) e->halt(); - } - ImGui::SameLine(); - if (ImGui::Button("Frame Advance")) e->haltWhen(DIV_HALT_TICK); - ImGui::SameLine(); - if (ImGui::Button("Row Advance")) e->haltWhen(DIV_HALT_ROW); - ImGui::SameLine(); - if (ImGui::Button("Pattern Advance")) e->haltWhen(DIV_HALT_PATTERN); - - if (ImGui::Button("Panic")) e->syncReset(); - ImGui::SameLine(); - if (ImGui::Button("Abort")) { - abort(); - } - ImGui::TreePop(); - } - if (ImGui::TreeNode("Breakpoint")) { - ImGui::InputInt("Order",&bpOrder); - ImGui::InputInt("Row",&bpRow); - ImGui::InputInt("Tick",&bpTick); - ImGui::Checkbox("Enable",&bpOn); - ImGui::TreePop(); - } - if (ImGui::TreeNode("Dispatch Status")) { - ImGui::Text("for best results set latency to minimum or use the Frame Advance button."); - ImGui::Columns(e->getTotalChannelCount()); - for (int i=0; igetTotalChannelCount(); i++) { - void* ch=e->getDispatchChanState(i); - ImGui::TextColored(uiColors[GUI_COLOR_ACCENT_PRIMARY],"Ch. %d: %d, %d",i,e->dispatchOfChan[i],e->dispatchChanOfChan[i]); - if (ch==NULL) { - ImGui::Text("NULL"); - } else { - putDispatchChan(ch,e->dispatchChanOfChan[i],e->sysOfChan[i]); - } - ImGui::NextColumn(); - } - ImGui::Columns(); - ImGui::TreePop(); - } - if (ImGui::TreeNode("Playback Status")) { - ImGui::Text("for best results set latency to minimum or use the Frame Advance button."); - ImGui::Columns(e->getTotalChannelCount()); - for (int i=0; igetTotalChannelCount(); i++) { - DivChannelState* ch=e->getChanState(i); - ImGui::TextColored(uiColors[GUI_COLOR_ACCENT_PRIMARY],"Channel %d:",i); - if (ch==NULL) { - ImGui::Text("NULL"); - } else { - ImGui::Text("* General:"); - ImGui::Text("- note = %d",ch->note); - ImGui::Text("- oldNote = %d",ch->oldNote); - ImGui::Text("- pitch = %d",ch->pitch); - ImGui::Text("- portaSpeed = %d",ch->portaSpeed); - ImGui::Text("- portaNote = %d",ch->portaNote); - ImGui::Text("- volume = %.4x",ch->volume); - ImGui::Text("- volSpeed = %d",ch->volSpeed); - ImGui::Text("- cut = %d",ch->cut); - ImGui::Text("- rowDelay = %d",ch->rowDelay); - ImGui::Text("- volMax = %.4x",ch->volMax); - ImGui::Text("- delayOrder = %d",ch->delayOrder); - ImGui::Text("- delayRow = %d",ch->delayRow); - ImGui::Text("- retrigSpeed = %d",ch->retrigSpeed); - ImGui::Text("- retrigTick = %d",ch->retrigTick); - ImGui::PushStyleColor(ImGuiCol_Text,(ch->vibratoDepth>0)?uiColors[GUI_COLOR_MACRO_VOLUME]:uiColors[GUI_COLOR_TEXT]); - ImGui::Text("* Vibrato:"); - ImGui::Text("- depth = %d",ch->vibratoDepth); - ImGui::Text("- rate = %d",ch->vibratoRate); - ImGui::Text("- pos = %d",ch->vibratoPos); - ImGui::Text("- dir = %d",ch->vibratoDir); - ImGui::Text("- fine = %d",ch->vibratoFine); - ImGui::PopStyleColor(); - ImGui::PushStyleColor(ImGuiCol_Text,(ch->tremoloDepth>0)?uiColors[GUI_COLOR_MACRO_VOLUME]:uiColors[GUI_COLOR_TEXT]); - ImGui::Text("* Tremolo:"); - ImGui::Text("- depth = %d",ch->tremoloDepth); - ImGui::Text("- rate = %d",ch->tremoloRate); - ImGui::Text("- pos = %d",ch->tremoloPos); - ImGui::PopStyleColor(); - ImGui::PushStyleColor(ImGuiCol_Text,(ch->arp>0)?uiColors[GUI_COLOR_MACRO_VOLUME]:uiColors[GUI_COLOR_TEXT]); - ImGui::Text("* Arpeggio:"); - ImGui::Text("- arp = %.2X",ch->arp); - ImGui::Text("- stage = %d",ch->arpStage); - ImGui::Text("- ticks = %d",ch->arpTicks); - ImGui::PopStyleColor(); - ImGui::Text("* Miscellaneous:"); - ImGui::TextColored(ch->doNote?uiColors[GUI_COLOR_MACRO_VOLUME]:uiColors[GUI_COLOR_HEADER],">> Do Note"); - ImGui::TextColored(ch->legato?uiColors[GUI_COLOR_MACRO_VOLUME]:uiColors[GUI_COLOR_HEADER],">> Legato"); - ImGui::TextColored(ch->portaStop?uiColors[GUI_COLOR_MACRO_VOLUME]:uiColors[GUI_COLOR_HEADER],">> PortaStop"); - ImGui::TextColored(ch->keyOn?uiColors[GUI_COLOR_MACRO_VOLUME]:uiColors[GUI_COLOR_HEADER],">> Key On"); - ImGui::TextColored(ch->keyOff?uiColors[GUI_COLOR_MACRO_VOLUME]:uiColors[GUI_COLOR_HEADER],">> Key Off"); - ImGui::TextColored(ch->nowYouCanStop?uiColors[GUI_COLOR_MACRO_VOLUME]:uiColors[GUI_COLOR_HEADER],">> NowYouCanStop"); - ImGui::TextColored(ch->stopOnOff?uiColors[GUI_COLOR_MACRO_VOLUME]:uiColors[GUI_COLOR_HEADER],">> Stop on Off"); - ImGui::TextColored(ch->arpYield?uiColors[GUI_COLOR_MACRO_VOLUME]:uiColors[GUI_COLOR_HEADER],">> Arp Yield"); - ImGui::TextColored(ch->delayLocked?uiColors[GUI_COLOR_MACRO_VOLUME]:uiColors[GUI_COLOR_HEADER],">> DelayLocked"); - ImGui::TextColored(ch->inPorta?uiColors[GUI_COLOR_MACRO_VOLUME]:uiColors[GUI_COLOR_HEADER],">> InPorta"); - ImGui::TextColored(ch->scheduledSlideReset?uiColors[GUI_COLOR_MACRO_VOLUME]:uiColors[GUI_COLOR_HEADER],">> SchedSlide"); - } - ImGui::NextColumn(); - } - ImGui::Columns(); - ImGui::TreePop(); - } - if (ImGui::TreeNode("Playground")) { - if (pgSys<0 || pgSys>=e->song.systemLen) pgSys=0; - if (ImGui::BeginCombo("System",fmt::sprintf("%d. %s",pgSys+1,e->getSystemName(e->song.system[pgSys])).c_str())) { - for (int i=0; isong.systemLen; i++) { - if (ImGui::Selectable(fmt::sprintf("%d. %s",i+1,e->getSystemName(e->song.system[i])).c_str())) { - pgSys=i; - break; - } - } - ImGui::EndCombo(); - } - ImGui::Text("Program"); - if (pgProgram.empty()) { - ImGui::Text("-nothing here-"); - } else { - char id[32]; - for (size_t index=0; indexpoke(pgSys,pgProgram); - } - ImGui::SameLine(); - if (ImGui::Button("Clear")) { - pgProgram.clear(); - } - - ImGui::Text("Address"); - ImGui::SameLine(); - ImGui::SetNextItemWidth(100.0f*dpiScale); - ImGui::InputInt("##PAddress",&pgAddr,0,0,ImGuiInputTextFlags_CharsHexadecimal); - ImGui::SameLine(); - ImGui::Text("Value"); - ImGui::SameLine(); - ImGui::SetNextItemWidth(100.0f*dpiScale); - ImGui::InputInt("##PValue",&pgVal,0,0,ImGuiInputTextFlags_CharsHexadecimal); - ImGui::SameLine(); - if (ImGui::Button("Write")) { - e->poke(pgSys,pgAddr,pgVal); - } - ImGui::SameLine(); - if (ImGui::Button("Add")) { - pgProgram.push_back(DivRegWrite(pgAddr,pgVal)); - } - if (ImGui::TreeNode("Register Cheatsheet")) { - const char** sheet=e->getRegisterSheet(pgSys); - if (sheet==NULL) { - ImGui::Text("no cheatsheet available for this system."); - } else { - if (ImGui::BeginTable("RegisterSheet",2,ImGuiTableFlags_SizingFixedSame)) { - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - ImGui::Text("Name"); - ImGui::TableNextColumn(); - ImGui::Text("Address"); - for (int i=0; sheet[i]!=NULL; i+=2) { - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - ImGui::Text("%s",sheet[i]); - ImGui::TableNextColumn(); - ImGui::Text("$%s",sheet[i+1]); - } - ImGui::EndTable(); - } - } - ImGui::TreePop(); - } - ImGui::TreePop(); - } - if (ImGui::TreeNode("User Interface")) { - if (ImGui::Button("Inspect")) { - inspectorOpen=!inspectorOpen; - } - ImGui::TreePop(); - } - if (ImGui::TreeNode("Settings")) { - if (ImGui::Button("Sync")) syncSettings(); - ImGui::SameLine(); - if (ImGui::Button("Commit")) commitSettings(); - ImGui::SameLine(); - if (ImGui::Button("Force Load")) e->loadConf(); - ImGui::SameLine(); - if (ImGui::Button("Force Save")) e->saveConf(); - ImGui::TreePop(); - } - ImGui::Text("Song format version %d",e->song.version); - ImGui::Text("Furnace version " DIV_VERSION " (%d)",DIV_ENGINE_VERSION); - } - if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_DEBUG; - ImGui::End(); -} - -void FurnaceGUI::drawNewSong() { - bool accepted=false; - - ImGui::PushFont(bigFont); - ImGui::SetCursorPosX((ImGui::GetContentRegionAvail().x-ImGui::CalcTextSize("Choose a System!").x)*0.5); - ImGui::Text("Choose a System!"); - ImGui::PopFont(); - - if (ImGui::BeginTable("sysPicker",2)) { - 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"); - - ImGui::TableNextRow(); - - // CATEGORIES - ImGui::TableNextColumn(); - int index=0; - for (FurnaceGUISysCategory& i: sysCategories) { - if (ImGui::Selectable(i.name,newSongCategory==index,ImGuiSelectableFlags_DontClosePopups)) { \ - newSongCategory=index; - } - index++; - } - - // SYSTEMS - ImGui::TableNextColumn(); - if (ImGui::BeginTable("Systems",1,ImGuiTableFlags_BordersInnerV|ImGuiTableFlags_ScrollY)) { - for (FurnaceGUISysDef& i: sysCategories[newSongCategory].systems) { - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - if (ImGui::Selectable(i.name,false,ImGuiSelectableFlags_DontClosePopups)) { - nextDesc=i.definition.data(); - accepted=true; - } - } - ImGui::EndTable(); - } - - ImGui::EndTable(); - } - - if (ImGui::Button("Cancel")) { - ImGui::CloseCurrentPopup(); - } - - if (accepted) { - e->createNew(nextDesc); - undoHist.clear(); - redoHist.clear(); - curFileName=""; - modified=false; - curNibble=false; - orderNibble=false; - orderCursor=-1; - samplePos=0; - updateSampleTex=true; - selStart=SelectionPoint(); - selEnd=SelectionPoint(); - cursor=SelectionPoint(); - updateWindowTitle(); - ImGui::CloseCurrentPopup(); - } -} - -void FurnaceGUI::drawStats() { - if (nextWindow==GUI_WINDOW_STATS) { - statsOpen=true; - ImGui::SetNextWindowFocus(); - nextWindow=GUI_WINDOW_NOTHING; - } - if (!statsOpen) return; - if (ImGui::Begin("Statistics",&statsOpen)) { - String adpcmAUsage=fmt::sprintf("%d/16384KB",e->adpcmAMemLen/1024); - String adpcmBUsage=fmt::sprintf("%d/16384KB",e->adpcmBMemLen/1024); - String qsoundUsage=fmt::sprintf("%d/16384KB",e->qsoundMemLen/1024); - String x1_010Usage=fmt::sprintf("%d/1024KB",e->x1_010MemLen/1024); - ImGui::Text("ADPCM-A"); - ImGui::SameLine(); - ImGui::ProgressBar(((float)e->adpcmAMemLen)/16777216.0f,ImVec2(-FLT_MIN,0),adpcmAUsage.c_str()); - ImGui::Text("ADPCM-B"); - ImGui::SameLine(); - ImGui::ProgressBar(((float)e->adpcmBMemLen)/16777216.0f,ImVec2(-FLT_MIN,0),adpcmBUsage.c_str()); - ImGui::Text("QSound"); - ImGui::SameLine(); - ImGui::ProgressBar(((float)e->qsoundMemLen)/16777216.0f,ImVec2(-FLT_MIN,0),qsoundUsage.c_str()); - ImGui::Text("X1-010"); - ImGui::SameLine(); - ImGui::ProgressBar(((float)e->x1_010MemLen)/1048576.0f,ImVec2(-FLT_MIN,0),x1_010Usage.c_str()); - } - if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_STATS; - ImGui::End(); -} - -void FurnaceGUI::drawCompatFlags() { - if (nextWindow==GUI_WINDOW_COMPAT_FLAGS) { - compatFlagsOpen=true; - ImGui::SetNextWindowFocus(); - nextWindow=GUI_WINDOW_NOTHING; - } - if (!compatFlagsOpen) return; - if (ImGui::Begin("Compatibility Flags",&compatFlagsOpen)) { - ImGui::TextWrapped("these flags are designed to provide better DefleMask/older Furnace compatibility."); - ImGui::Checkbox("Limit slide range",&e->song.limitSlides); - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("when enabled, slides are limited to a compatible range.\nmay cause problems with slides in negative octaves."); - } - ImGui::Checkbox("Linear pitch control",&e->song.linearPitch); - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("linear pitch:\n- slides work in frequency/period space\n- E5xx and 04xx effects work in tonality space\nnon-linear pitch:\n- slides work in frequency/period space\n- E5xx and 04xx effects work on frequency/period space"); - } - ImGui::Checkbox("Proper noise layout on NES and PC Engine",&e->song.properNoiseLayout); - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("use a proper noise channel note mapping (0-15) instead of a rather unusual compatible one.\nunlocks all noise frequencies on PC Engine."); - } - ImGui::Checkbox("Game Boy instrument duty is wave volume",&e->song.waveDutyIsVol); - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("if enabled, an instrument with duty macro in the wave channel will be mapped to wavetable volume."); - } - - ImGui::Checkbox("Restart macro on portamento",&e->song.resetMacroOnPorta); - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("when enabled, a portamento effect will reset the channel's macro if used in combination with a note."); - } - ImGui::Checkbox("Legacy volume slides",&e->song.legacyVolumeSlides); - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("simulate glitchy volume slide behavior by silently overflowing the volume when the slide goes below 0."); - } - ImGui::Checkbox("Compatible arpeggio",&e->song.compatibleArpeggio); - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("delay arpeggio by one tick on every new note."); - } - ImGui::Checkbox("Reset slides after note off",&e->song.noteOffResetsSlides); - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("when enabled, note off will reset the channel's slide effect."); - } - ImGui::Checkbox("Reset portamento after reaching target",&e->song.targetResetsSlides); - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("when enabled, the slide effect is disabled after it reaches its target."); - } - ImGui::Checkbox("Ignore duplicate slide effects",&e->song.ignoreDuplicateSlides); - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("if this is on, only the first slide of a row in a channel will be considered."); - } - ImGui::Checkbox("Continuous vibrato",&e->song.continuousVibrato); - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("when enabled, vibrato will not be reset on a new note."); - } - ImGui::Checkbox("Broken DAC mode",&e->song.brokenDACMode); - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("when enabled, the DAC in YM2612 will be disabled if there isn't any sample playing."); - } - ImGui::Checkbox("Auto-insert one tick gap between notes",&e->song.oneTickCut); - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("when enabled, a one-tick note cut will be inserted between non-legato/non-portamento notes.\nthis simulates the behavior of some Amiga/SNES music engines."); - } - - ImGui::Text("Loop modality:"); - if (ImGui::RadioButton("Reset channels",e->song.loopModality==0)) { - e->song.loopModality=0; - } - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("select to reset channels on loop. may trigger a voltage click on every loop!"); - } - if (ImGui::RadioButton("Soft reset channels",e->song.loopModality==1)) { - e->song.loopModality=1; - } - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("select to turn channels off on loop."); - } - if (ImGui::RadioButton("Do nothing",e->song.loopModality==2)) { - e->song.loopModality=2; - } - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("select to not reset channels on loop."); - } - - ImGui::Separator(); - - ImGui::TextWrapped("the following flags are for compatibility with older Furnace versions."); - - ImGui::Checkbox("Arpeggio inhibits non-porta slides",&e->song.arpNonPorta); - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("behavior changed in 0.5.5"); - } - ImGui::Checkbox("Wack FM algorithm macro",&e->song.algMacroBehavior); - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("behavior changed in 0.5.5"); - } - ImGui::Checkbox("Broken shortcut slides (E1xy/E2xy)",&e->song.brokenShortcutSlides); - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("behavior changed in 0.5.7"); - } - ImGui::Checkbox("Stop portamento on note off",&e->song.stopPortaOnNoteOff); - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("behavior changed in 0.6"); - } - ImGui::Checkbox("Allow instrument change during slides",&e->song.newInsTriggersInPorta); - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("behavior changed in 0.6"); - } - ImGui::Checkbox("Reset note to base on arpeggio stop",&e->song.arp0Reset); - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("behavior changed in 0.6"); - } - } - if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_COMPAT_FLAGS; - ImGui::End(); -} - -void FurnaceGUI::drawPiano() { - if (nextWindow==GUI_WINDOW_PIANO) { - pianoOpen=true; - ImGui::SetNextWindowFocus(); - nextWindow=GUI_WINDOW_NOTHING; - } - if (!pianoOpen) return; - if (ImGui::Begin("Piano",&pianoOpen)) { - for (int i=0; igetTotalChannelCount(); i++) { - DivChannelState* cs=e->getChanState(i); - if (cs->keyOn) { - const char* noteName=NULL; - if (cs->note<-60 || cs->note>120) { - noteName="???"; - } else { - noteName=noteNames[cs->note+60]; - } - ImGui::Text("%d: %s",i,noteName); - } - } - } - if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_PIANO; - ImGui::End(); -} - -// NOTE: please don't ask me to enable text wrap. -// Dear ImGui doesn't have that feature. D: -void FurnaceGUI::drawNotes() { - if (nextWindow==GUI_WINDOW_NOTES) { - notesOpen=true; - ImGui::SetNextWindowFocus(); - nextWindow=GUI_WINDOW_NOTHING; - } - if (!notesOpen) return; - if (ImGui::Begin("Song Comments",¬esOpen)) { - ImGui::InputTextMultiline("##SongNotes",&e->song.notes,ImGui::GetContentRegionAvail()); - } - if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_NOTES; - ImGui::End(); -} - -void FurnaceGUI::drawChannels() { - if (nextWindow==GUI_WINDOW_CHANNELS) { - channelsOpen=true; - ImGui::SetNextWindowFocus(); - nextWindow=GUI_WINDOW_NOTHING; - } - if (!channelsOpen) return; - if (ImGui::Begin("Channels",&channelsOpen)) { - if (ImGui::BeginTable("ChannelList",3)) { - ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthFixed,0.0); - ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthStretch,0.0); - ImGui::TableSetupColumn("c2",ImGuiTableColumnFlags_WidthFixed,48.0f*dpiScale); - for (int i=0; igetTotalChannelCount(); i++) { - ImGui::PushID(i); - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - ImGui::Checkbox("##Visible",&e->song.chanShow[i]); - ImGui::TableNextColumn(); - ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); - ImGui::InputTextWithHint("##ChanName",e->getChannelName(i),&e->song.chanName[i]); - ImGui::TableNextColumn(); - ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); - ImGui::InputTextWithHint("##ChanShortName",e->getChannelShortName(i),&e->song.chanShortName[i]); - ImGui::PopID(); - } - ImGui::EndTable(); - } - } - if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_CHANNELS; - ImGui::End(); -} - -void FurnaceGUI::drawRegView() { - if (nextWindow==GUI_WINDOW_REGISTER_VIEW) { - channelsOpen=true; - ImGui::SetNextWindowFocus(); - nextWindow=GUI_WINDOW_NOTHING; - } - if (!regViewOpen) return; - if (ImGui::Begin("Register View",®ViewOpen)) { - for (int i=0; isong.systemLen; i++) { - ImGui::Text("%d. %s",i+1,getSystemName(e->song.system[i])); - int size=0; - int depth=8; - unsigned char* regPool=e->getRegisterPool(i,size,depth); - unsigned short* regPoolW=(unsigned short*)regPool; - if (regPool==NULL) { - ImGui::Text("- no register pool available"); - } else { - ImGui::PushFont(patFont); - if (ImGui::BeginTable("Memory",17)) { - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - for (int i=0; i<16; i++) { - ImGui::TableNextColumn(); - ImGui::TextColored(uiColors[GUI_COLOR_PATTERN_ROW_INDEX]," %X",i); - } - for (int i=0; i<=((size-1)>>4); i++) { - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - ImGui::TextColored(uiColors[GUI_COLOR_PATTERN_ROW_INDEX],"%.2X",i*16); - for (int j=0; j<16; j++) { - ImGui::TableNextColumn(); - if (i*16+j>=size) continue; - if (depth == 8) { - ImGui::Text("%.2x",regPool[i*16+j]); - } else if (depth == 16) { - ImGui::Text("%.4x",regPoolW[i*16+j]); - } else { - ImGui::Text("??"); - } - } - } - ImGui::EndTable(); - } - ImGui::PopFont(); - } - } - } - if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_REGISTER_VIEW; - ImGui::End(); -} - -void FurnaceGUI::startSelection(int xCoarse, int xFine, int y) { - if (xCoarse!=selStart.xCoarse || xFine!=selStart.xFine || y!=selStart.y) { - curNibble=false; - } - cursor.xCoarse=xCoarse; - cursor.xFine=xFine; - cursor.y=y; - selStart.xCoarse=xCoarse; - selStart.xFine=xFine; - selStart.y=y; - selEnd.xCoarse=xCoarse; - selEnd.xFine=xFine; - selEnd.y=y; - selecting=true; -} - -void FurnaceGUI::updateSelection(int xCoarse, int xFine, int y) { - if (!selecting) return; - selEnd.xCoarse=xCoarse; - selEnd.xFine=xFine; - selEnd.y=y; -} - -void FurnaceGUI::finishSelection() { - // swap points if needed - if (selEnd.ygetTotalChannelCount(); - - if (selStart.xCoarse<0) selStart.xCoarse=0; - if (selStart.xCoarse>=chanCount) selStart.xCoarse=chanCount-1; - if (selStart.y<0) selStart.y=0; - if (selStart.y>=e->song.patLen) selStart.y=e->song.patLen-1; - if (selEnd.xCoarse<0) selEnd.xCoarse=0; - if (selEnd.xCoarse>=chanCount) selEnd.xCoarse=chanCount-1; - if (selEnd.y<0) selEnd.y=0; - if (selEnd.y>=e->song.patLen) selEnd.y=e->song.patLen-1; - if (cursor.xCoarse<0) cursor.xCoarse=0; - if (cursor.xCoarse>=chanCount) cursor.xCoarse=chanCount-1; - if (cursor.y<0) cursor.y=0; - if (cursor.y>=e->song.patLen) cursor.y=e->song.patLen-1; - - if (e->song.chanCollapse[selEnd.xCoarse]) { - selStart.xFine=0; - } - if (e->song.chanCollapse[selEnd.xCoarse]) { - selEnd.xFine=2+e->song.pat[cursor.xCoarse].effectRows*2; - } -} - -#define DETERMINE_FIRST \ - int firstChannel=0; \ - for (int i=0; igetTotalChannelCount(); i++) { \ - if (e->song.chanShow[i]) { \ - firstChannel=i; \ - break; \ - } \ - } \ - -#define DETERMINE_LAST \ - int lastChannel=0; \ - for (int i=e->getTotalChannelCount()-1; i>=0; i--) { \ - if (e->song.chanShow[i]) { \ - lastChannel=i+1; \ - break; \ - } \ - } - -#define DETERMINE_FIRST_LAST \ - DETERMINE_FIRST \ - DETERMINE_LAST - -void FurnaceGUI::moveCursor(int x, int y, bool select) { - if (!select) { - finishSelection(); - } - - DETERMINE_FIRST_LAST; - - curNibble=false; - if (x!=0) { - demandScrollX=true; - if (x>0) { - for (int i=0; i=(e->song.chanCollapse[cursor.xCoarse]?1:(3+e->song.pat[cursor.xCoarse].effectRows*2))) { - cursor.xFine=0; - if (++cursor.xCoarse>=lastChannel) { - if (settings.wrapHorizontal!=0 && !select) { - cursor.xCoarse=firstChannel; - if (settings.wrapHorizontal==2) y++; - } else { - cursor.xCoarse=lastChannel-1; - cursor.xFine=e->song.chanCollapse[cursor.xCoarse]?0:(2+e->song.pat[cursor.xCoarse].effectRows*2); - } - } else { - while (!e->song.chanShow[cursor.xCoarse]) { - cursor.xCoarse++; - if (cursor.xCoarse>=e->getTotalChannelCount()) break; - } - } - } - } - } else { - for (int i=0; i<-x; i++) { - if (--cursor.xFine<0) { - if (--cursor.xCoarsesong.pat[cursor.xCoarse].effectRows*2; - if (settings.wrapHorizontal==2) y--; - } else { - cursor.xCoarse=firstChannel; - cursor.xFine=0; - } - } else { - while (!e->song.chanShow[cursor.xCoarse]) { - cursor.xCoarse--; - if (cursor.xCoarse<0) break; - } - if (e->song.chanCollapse[cursor.xCoarse]) { - cursor.xFine=0; - } else { - cursor.xFine=2+e->song.pat[cursor.xCoarse].effectRows*2; - } - } - } - } - } - } - if (y!=0) { - if (y>0) { - for (int i=0; i=e->song.patLen) { - if (settings.wrapVertical!=0 && !select) { - cursor.y=0; - if (settings.wrapVertical==2) { - if (!e->isPlaying() && e->getOrder()<(e->song.ordersLen-1)) { - e->setOrder(e->getOrder()+1); - } else { - cursor.y=e->song.patLen-1; - } - } - } else { - cursor.y=e->song.patLen-1; - } - } - } - } else { - for (int i=0; i<-y; i++) { - cursor.y--; - if (cursor.y<0) { - if (settings.wrapVertical!=0 && !select) { - cursor.y=e->song.patLen-1; - if (settings.wrapVertical==2) { - if (!e->isPlaying() && e->getOrder()>0) { - e->setOrder(e->getOrder()-1); - } else { - cursor.y=0; - } - } - } else { - cursor.y=0; - } - } - } - } - } - if (!select) { - selStart=cursor; - } - selEnd=cursor; - updateScroll(cursor.y); -} - -void FurnaceGUI::moveCursorPrevChannel(bool overflow) { - finishSelection(); - curNibble=false; - - DETERMINE_FIRST_LAST; - - do { - cursor.xCoarse--; - if (cursor.xCoarse<0) break; - } while (!e->song.chanShow[cursor.xCoarse]); - if (cursor.xCoarse=e->getTotalChannelCount()) break; - } while (!e->song.chanShow[cursor.xCoarse]); - if (cursor.xCoarse>=lastChannel) { - if (overflow) { - cursor.xCoarse=firstChannel; - } else { - cursor.xCoarse=lastChannel-1; - } - } - - selStart=cursor; - selEnd=cursor; - demandScrollX=true; -} - -void FurnaceGUI::moveCursorTop(bool select) { - finishSelection(); - curNibble=false; - if (cursor.y==0) { - DETERMINE_FIRST; - cursor.xCoarse=firstChannel; - cursor.xFine=0; - demandScrollX=true; - } else { - cursor.y=0; - } - selStart=cursor; - if (!select) { - selEnd=cursor; - } - updateScroll(cursor.y); -} - -void FurnaceGUI::moveCursorBottom(bool select) { - finishSelection(); - curNibble=false; - if (cursor.y==e->song.patLen-1) { - DETERMINE_LAST; - cursor.xCoarse=lastChannel-1; - cursor.xFine=2+e->song.pat[cursor.xCoarse].effectRows*2; - demandScrollX=true; - } else { - cursor.y=e->song.patLen-1; - } - if (!select) { - selStart=cursor; - } - selEnd=cursor; - updateScroll(cursor.y); -} - -void FurnaceGUI::editAdvance() { - finishSelection(); - cursor.y+=editStep; - if (cursor.y>=e->song.patLen) cursor.y=e->song.patLen-1; - selStart=cursor; - selEnd=cursor; - updateScroll(cursor.y); -} - -void FurnaceGUI::prepareUndo(ActionType action) { - int order=e->getOrder(); - switch (action) { - case GUI_UNDO_CHANGE_ORDER: - oldOrders=e->song.orders; - oldOrdersLen=e->song.ordersLen; - break; - case GUI_UNDO_PATTERN_EDIT: - case GUI_UNDO_PATTERN_DELETE: - case GUI_UNDO_PATTERN_PULL: - case GUI_UNDO_PATTERN_PUSH: - case GUI_UNDO_PATTERN_CUT: - case GUI_UNDO_PATTERN_PASTE: - case GUI_UNDO_PATTERN_CHANGE_INS: - case GUI_UNDO_PATTERN_INTERPOLATE: - case GUI_UNDO_PATTERN_FADE: - case GUI_UNDO_PATTERN_SCALE: - case GUI_UNDO_PATTERN_RANDOMIZE: - case GUI_UNDO_PATTERN_INVERT_VAL: - case GUI_UNDO_PATTERN_FLIP: - case GUI_UNDO_PATTERN_COLLAPSE: - case GUI_UNDO_PATTERN_EXPAND: - for (int i=0; igetTotalChannelCount(); i++) { - e->song.pat[i].getPattern(e->song.orders.ord[i][order],false)->copyOn(oldPat[i]); - } - break; - } -} - -void FurnaceGUI::makeUndo(ActionType action) { - bool doPush=false; - UndoStep s; - s.type=action; - s.cursor=cursor; - s.selStart=selStart; - s.selEnd=selEnd; - int order=e->getOrder(); - s.order=order; - s.nibble=curNibble; - switch (action) { - case GUI_UNDO_CHANGE_ORDER: - for (int i=0; isong.orders.ord[i][j]) { - s.ord.push_back(UndoOrderData(i,j,oldOrders.ord[i][j],e->song.orders.ord[i][j])); - } - } - } - s.oldOrdersLen=oldOrdersLen; - s.newOrdersLen=e->song.ordersLen; - if (oldOrdersLen!=e->song.ordersLen) { - doPush=true; - } - if (!s.ord.empty()) { - doPush=true; - } - break; - case GUI_UNDO_PATTERN_EDIT: - case GUI_UNDO_PATTERN_DELETE: - case GUI_UNDO_PATTERN_PULL: - case GUI_UNDO_PATTERN_PUSH: - case GUI_UNDO_PATTERN_CUT: - case GUI_UNDO_PATTERN_PASTE: - case GUI_UNDO_PATTERN_CHANGE_INS: - case GUI_UNDO_PATTERN_INTERPOLATE: - case GUI_UNDO_PATTERN_FADE: - case GUI_UNDO_PATTERN_SCALE: - case GUI_UNDO_PATTERN_RANDOMIZE: - case GUI_UNDO_PATTERN_INVERT_VAL: - case GUI_UNDO_PATTERN_FLIP: - case GUI_UNDO_PATTERN_COLLAPSE: - case GUI_UNDO_PATTERN_EXPAND: - for (int i=0; igetTotalChannelCount(); i++) { - DivPattern* p=e->song.pat[i].getPattern(e->song.orders.ord[i][order],false); - for (int j=0; jsong.patLen; j++) { - for (int k=0; k<32; k++) { - if (p->data[j][k]!=oldPat[i]->data[j][k]) { - s.pat.push_back(UndoPatternData(i,e->song.orders.ord[i][order],j,k,oldPat[i]->data[j][k],p->data[j][k])); - } - } - } - } - if (!s.pat.empty()) { - doPush=true; - } - break; - } - if (doPush) { - MARK_MODIFIED; - undoHist.push_back(s); - redoHist.clear(); - if (undoHist.size()>settings.maxUndoSteps) undoHist.pop_front(); - } -} - -void FurnaceGUI::doSelectAll() { - finishSelection(); - curNibble=false; - if (selStart.xFine==0 && selEnd.xFine==2+e->song.pat[selEnd.xCoarse].effectRows*2) { - if (selStart.y==0 && selEnd.y==e->song.patLen-1) { // select entire pattern - selStart.xCoarse=0; - selStart.xFine=0; - selEnd.xCoarse=e->getTotalChannelCount()-1; - selEnd.xFine=2+e->song.pat[selEnd.xCoarse].effectRows*2; - } else { // select entire column - selStart.y=0; - selEnd.y=e->song.patLen-1; - } - } else { - int selStartX=0; - int selEndX=0; - // find row position - for (SelectionPoint i; i.xCoarse!=selStart.xCoarse || i.xFine!=selStart.xFine; selStartX++) { - i.xFine++; - if (i.xFine>=3+e->song.pat[i.xCoarse].effectRows*2) { - i.xFine=0; - i.xCoarse++; - } - } - for (SelectionPoint i; i.xCoarse!=selEnd.xCoarse || i.xFine!=selEnd.xFine; selEndX++) { - i.xFine++; - if (i.xFine>=3+e->song.pat[i.xCoarse].effectRows*2) { - i.xFine=0; - i.xCoarse++; - } - } - - float aspect=float(selEndX-selStartX+1)/float(selEnd.y-selStart.y+1); - if (aspect<=1.0f && !(selStart.y==0 && selEnd.y==e->song.patLen-1)) { // up-down - selStart.y=0; - selEnd.y=e->song.patLen-1; - } else { // left-right - selStart.xFine=0; - selEnd.xFine=2+e->song.pat[selEnd.xCoarse].effectRows*2; - } - } -} - -#define maskOut(x) \ - if (x==0) { \ - if (!opMaskNote) continue; \ - } else if (x==1) { \ - if (!opMaskIns) continue; \ - } else if (x==2) { \ - if (!opMaskVol) continue; \ - } else if (((x)&1)==0) { \ - if (!opMaskEffectVal) continue; \ - } else if (((x)&1)==1) { \ - if (!opMaskEffect) continue; \ - } - -void FurnaceGUI::doDelete() { - finishSelection(); - prepareUndo(GUI_UNDO_PATTERN_DELETE); - curNibble=false; - - int iCoarse=selStart.xCoarse; - int iFine=selStart.xFine; - int ord=e->getOrder(); - for (; iCoarse<=selEnd.xCoarse; iCoarse++) { - if (!e->song.chanShow[iCoarse]) continue; - DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][ord],true); - for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsedata[j][iFine]=0; - if (selStart.y==selEnd.y) pat->data[j][2]=-1; - } - pat->data[j][iFine+1]=(iFine<1)?0:-1; - } - } - iFine=0; - } - - makeUndo(GUI_UNDO_PATTERN_DELETE); -} - -void FurnaceGUI::doPullDelete() { - finishSelection(); - prepareUndo(GUI_UNDO_PATTERN_PULL); - curNibble=false; - - if (settings.pullDeleteBehavior) { - if (--selStart.y<0) selStart.y=0; - if (--selEnd.y<0) selEnd.y=0; - if (--cursor.y<0) cursor.y=0; - updateScroll(cursor.y); - } - - int iCoarse=selStart.xCoarse; - int iFine=selStart.xFine; - int ord=e->getOrder(); - for (; iCoarse<=selEnd.xCoarse; iCoarse++) { - if (!e->song.chanShow[iCoarse]) continue; - DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][ord],true); - for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsesong.patLen; j++) { - if (jsong.patLen-1) { - if (iFine==0) { - pat->data[j][iFine]=pat->data[j+1][iFine]; - } - pat->data[j][iFine+1]=pat->data[j+1][iFine+1]; - } else { - if (iFine==0) { - pat->data[j][iFine]=0; - } - pat->data[j][iFine+1]=(iFine<1)?0:-1; - } - } - } - iFine=0; - } - - makeUndo(GUI_UNDO_PATTERN_PULL); -} - -void FurnaceGUI::doInsert() { - finishSelection(); - prepareUndo(GUI_UNDO_PATTERN_PUSH); - curNibble=false; - - int iCoarse=selStart.xCoarse; - int iFine=selStart.xFine; - int ord=e->getOrder(); - for (; iCoarse<=selEnd.xCoarse; iCoarse++) { - if (!e->song.chanShow[iCoarse]) continue; - DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][ord],true); - for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsesong.patLen-1; j>=selStart.y; j--) { - if (j==selStart.y) { - if (iFine==0) { - pat->data[j][iFine]=0; - } - pat->data[j][iFine+1]=(iFine<1)?0:-1; - } else { - if (iFine==0) { - pat->data[j][iFine]=pat->data[j-1][iFine]; - } - pat->data[j][iFine+1]=pat->data[j-1][iFine+1]; - } - } - } - iFine=0; - } - - makeUndo(GUI_UNDO_PATTERN_PUSH); -} - -void FurnaceGUI::doTranspose(int amount) { - finishSelection(); - prepareUndo(GUI_UNDO_PATTERN_DELETE); - curNibble=false; - - int iCoarse=selStart.xCoarse; - int iFine=selStart.xFine; - int ord=e->getOrder(); - for (; iCoarse<=selEnd.xCoarse; iCoarse++) { - if (!e->song.chanShow[iCoarse]) continue; - DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][ord],true); - for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsedata[j][0]; - int origOctave=(signed char)pat->data[j][1]; - if (origNote!=0 && origNote!=100 && origNote!=101 && origNote!=102) { - origNote+=amount; - while (origNote>12) { - origNote-=12; - origOctave++; - } - while (origNote<1) { - origNote+=12; - origOctave--; - } - if (origOctave==9 && origNote>11) { - origNote=11; - origOctave=9; - } - if (origOctave>9) { - origNote=11; - origOctave=9; - } - if (origOctave<-5) { - origNote=1; - origOctave=-5; - } - pat->data[j][0]=origNote; - pat->data[j][1]=(unsigned char)origOctave; - } - } else { - int top=255; - if (iFine==1) { - if (e->song.ins.empty()) continue; - top=e->song.ins.size()-1; - } else if (iFine==2) { // volume - top=e->getMaxVolumeChan(iCoarse); - } - if (pat->data[j][iFine+1]==-1) continue; - pat->data[j][iFine+1]=MIN(top,MAX(0,pat->data[j][iFine+1]+amount)); - } - } - } - iFine=0; - } - - makeUndo(GUI_UNDO_PATTERN_DELETE); -} - -void FurnaceGUI::doCopy(bool cut) { - finishSelection(); - if (cut) { - curNibble=false; - prepareUndo(GUI_UNDO_PATTERN_CUT); - } - clipboard=fmt::sprintf("org.tildearrow.furnace - Pattern Data (%d)\n%d",DIV_ENGINE_VERSION,selStart.xFine); - - for (int j=selStart.y; j<=selEnd.y; j++) { - int iCoarse=selStart.xCoarse; - int iFine=selStart.xFine; - if (iFine>3 && !(iFine&1)) { - iFine--; - } - int ord=e->getOrder(); - clipboard+='\n'; - for (; iCoarse<=selEnd.xCoarse; iCoarse++) { - if (!e->song.chanShow[iCoarse]) continue; - DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][ord],true); - for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsedata[j][0],pat->data[j][1]); - if (cut) { - pat->data[j][0]=0; - pat->data[j][1]=0; - } - } else { - if (pat->data[j][iFine+1]==-1) { - clipboard+=".."; - } else { - clipboard+=fmt::sprintf("%.2X",pat->data[j][iFine+1]); - } - if (cut) { - pat->data[j][iFine+1]=-1; - } - } - } - clipboard+='|'; - iFine=0; - } - } - SDL_SetClipboardText(clipboard.c_str()); - - if (cut) { - makeUndo(GUI_UNDO_PATTERN_CUT); - } -} - -void FurnaceGUI::doPaste(PasteMode mode) { - finishSelection(); - prepareUndo(GUI_UNDO_PATTERN_PASTE); - char* clipText=SDL_GetClipboardText(); - if (clipText!=NULL) { - if (clipText[0]) { - clipboard=clipText; - } - SDL_free(clipText); - } - std::vector data; - String tempS; - for (char i: clipboard) { - if (i=='\r') continue; - if (i=='\n') { - data.push_back(tempS); - tempS=""; - continue; - } - tempS+=i; - } - data.push_back(tempS); - - int startOff=-1; - bool invalidData=false; - if (data.size()<2) return; - if (data[0]!=fmt::sprintf("org.tildearrow.furnace - Pattern Data (%d)",DIV_ENGINE_VERSION)) return; - if (sscanf(data[1].c_str(),"%d",&startOff)!=1) return; - if (startOff<0) return; - - DETERMINE_LAST; - - int j=cursor.y; - char note[4]; - int ord=e->getOrder(); - for (size_t i=2; isong.patLen; i++) { - size_t charPos=0; - int iCoarse=cursor.xCoarse; - int iFine=(startOff>2 && cursor.xFine>2)?(((cursor.xFine-1)&(~1))|1):startOff; - - String& line=data[i]; - - while (charPossong.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][ord],true); - if (line[charPos]=='|') { - iCoarse++; - if (iCoarsesong.chanShow[iCoarse]) { - iCoarse++; - if (iCoarse>=lastChannel) break; - } - iFine=0; - charPos++; - continue; - } - if (iFine==0) { - if (charPos>=line.size()) { - invalidData=true; - break; - } - note[0]=line[charPos++]; - if (charPos>=line.size()) { - invalidData=true; - break; - } - note[1]=line[charPos++]; - if (charPos>=line.size()) { - invalidData=true; - break; - } - note[2]=line[charPos++]; - note[3]=0; - - if (iFine==0 && !opMaskNote) { - iFine++; - continue; - } - - if ((mode==GUI_PASTE_MODE_MIX_BG || mode==GUI_PASTE_MODE_MIX_FG) && strcmp(note,"...")==0) { - // do nothing. - } else { - if (mode!=GUI_PASTE_MODE_MIX_BG || (pat->data[j][0]==0 && pat->data[j][1]==0)) { - if (!decodeNote(note,pat->data[j][0],pat->data[j][1])) { - invalidData=true; - break; - } - } - } - } else { - if (charPos>=line.size()) { - invalidData=true; - break; - } - note[0]=line[charPos++]; - if (charPos>=line.size()) { - invalidData=true; - break; - } - note[1]=line[charPos++]; - note[2]=0; - - if (iFine==1) { - if (!opMaskIns) { - iFine++; - continue; - } - } else if (iFine==2) { - if (!opMaskVol) { - iFine++; - continue; - } - } else if ((iFine&1)==0) { - if (!opMaskEffectVal) { - iFine++; - continue; - } - } else if ((iFine&1)==1) { - if (!opMaskEffect) { - iFine++; - continue; - } - } - - if (strcmp(note,"..")==0) { - if (!(mode==GUI_PASTE_MODE_MIX_BG || mode==GUI_PASTE_MODE_MIX_FG)) { - pat->data[j][iFine+1]=-1; - } - } else { - unsigned int val=0; - if (sscanf(note,"%2X",&val)!=1) { - invalidData=true; - break; - } - if (mode!=GUI_PASTE_MODE_MIX_BG || pat->data[j][iFine+1]==-1) { - if (iFine<(3+e->song.pat[iCoarse].effectRows*2)) pat->data[j][iFine+1]=val; - } - } - } - iFine++; - } - - if (invalidData) { - logW("invalid clipboard data! failed at line %d char %d\n",i,charPos); - logW("%s\n",line.c_str()); - break; - } - j++; - if (mode==GUI_PASTE_MODE_OVERFLOW && j>=e->song.patLen && ordsong.ordersLen-1) { - j=0; - ord++; - } - - if (mode==GUI_PASTE_MODE_FLOOD && i==data.size()-1) { - i=1; - } - } - - makeUndo(GUI_UNDO_PATTERN_PASTE); -} - -void FurnaceGUI::doChangeIns(int ins) { - finishSelection(); - prepareUndo(GUI_UNDO_PATTERN_CHANGE_INS); - - int iCoarse=selStart.xCoarse; - int ord=e->getOrder(); - for (; iCoarse<=selEnd.xCoarse; iCoarse++) { - if (!e->song.chanShow[iCoarse]) continue; - DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][ord],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)) { - pat->data[j][2]=ins; - } - } - } - - makeUndo(GUI_UNDO_PATTERN_CHANGE_INS); -} - -void FurnaceGUI::doInterpolate() { - finishSelection(); - prepareUndo(GUI_UNDO_PATTERN_INTERPOLATE); - - std::vector> points; - int iCoarse=selStart.xCoarse; - int iFine=selStart.xFine; - int ord=e->getOrder(); - for (; iCoarse<=selEnd.xCoarse; iCoarse++) { - if (!e->song.chanShow[iCoarse]) continue; - DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][ord],true); - for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsedata[j][iFine+1]!=-1) { - points.emplace(points.end(),j,pat->data[j][iFine+1]); - } - } - - if (points.size()>1) for (size_t j=0; j& curPoint=points[j]; - std::pair& nextPoint=points[j+1]; - double distance=nextPoint.first-curPoint.first; - for (int k=0; k<(nextPoint.first-curPoint.first); k++) { - pat->data[k+curPoint.first][iFine+1]=curPoint.second+((nextPoint.second-curPoint.second)*(double)k/distance); - } - } - } 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]!=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); - } - } - } - - if (points.size()>1) for (size_t j=0; j& curPoint=points[j]; - std::pair& nextPoint=points[j+1]; - double distance=nextPoint.first-curPoint.first; - for (int k=0; k<(nextPoint.first-curPoint.first); k++) { - int val=curPoint.second+((nextPoint.second-curPoint.second)*(double)k/distance); - pat->data[k+curPoint.first][0]=val%12; - pat->data[k+curPoint.first][1]=val/12; - if (pat->data[k+curPoint.first][0]==0) { - pat->data[k+curPoint.first][0]=12; - pat->data[k+curPoint.first][1]--; - } - pat->data[k+curPoint.first][1]&=255; - } - } - } - } - iFine=0; - } - - makeUndo(GUI_UNDO_PATTERN_INTERPOLATE); -} - -void FurnaceGUI::doFade(int p0, int p1, bool mode) { - finishSelection(); - prepareUndo(GUI_UNDO_PATTERN_FADE); - - int iCoarse=selStart.xCoarse; - int iFine=selStart.xFine; - int ord=e->getOrder(); - for (; iCoarse<=selEnd.xCoarse; iCoarse++) { - if (!e->song.chanShow[iCoarse]) continue; - DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][ord],true); - for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsesong.ins.empty()) continue; - absoluteTop=e->song.ins.size()-1; - } else if (iFine==2) { // volume - absoluteTop=e->getMaxVolumeChan(iCoarse); - } - if (selEnd.y-selStart.y<1) continue; - for (int j=selStart.y; j<=selEnd.y; j++) { - double fraction=double(j-selStart.y)/double(selEnd.y-selStart.y); - int value=p0+double(p1-p0)*fraction; - if (mode) { // nibble - value&=15; - pat->data[j][iFine+1]=MIN(absoluteTop,value|(value<<4)); - } else { // byte - pat->data[j][iFine+1]=MIN(absoluteTop,value); - } - } - } - } - iFine=0; - } - - makeUndo(GUI_UNDO_PATTERN_FADE); -} - -void FurnaceGUI::doInvertValues() { - finishSelection(); - prepareUndo(GUI_UNDO_PATTERN_INVERT_VAL); - - int iCoarse=selStart.xCoarse; - int iFine=selStart.xFine; - int ord=e->getOrder(); - for (; iCoarse<=selEnd.xCoarse; iCoarse++) { - if (!e->song.chanShow[iCoarse]) continue; - DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][ord],true); - for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsesong.ins.empty()) continue; - top=e->song.ins.size()-1; - } else if (iFine==2) { // volume - top=e->getMaxVolumeChan(iCoarse); - } - for (int j=selStart.y; j<=selEnd.y; j++) { - if (pat->data[j][iFine+1]==-1) continue; - pat->data[j][iFine+1]=top-pat->data[j][iFine+1]; - } - } - } - iFine=0; - } - - makeUndo(GUI_UNDO_PATTERN_INVERT_VAL); -} - -void FurnaceGUI::doScale(float top) { - finishSelection(); - prepareUndo(GUI_UNDO_PATTERN_SCALE); - - int iCoarse=selStart.xCoarse; - int iFine=selStart.xFine; - int ord=e->getOrder(); - for (; iCoarse<=selEnd.xCoarse; iCoarse++) { - if (!e->song.chanShow[iCoarse]) continue; - DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][ord],true); - for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsesong.ins.empty()) continue; - absoluteTop=e->song.ins.size()-1; - } else if (iFine==2) { // volume - absoluteTop=e->getMaxVolumeChan(iCoarse); - } - for (int j=selStart.y; j<=selEnd.y; j++) { - if (pat->data[j][iFine+1]==-1) continue; - pat->data[j][iFine+1]=MIN(absoluteTop,(double)pat->data[j][iFine+1]*(top/100.0f)); - } - } - } - iFine=0; - } - - makeUndo(GUI_UNDO_PATTERN_SCALE); -} - -void FurnaceGUI::doRandomize(int bottom, int top, bool mode) { - finishSelection(); - prepareUndo(GUI_UNDO_PATTERN_RANDOMIZE); - - int iCoarse=selStart.xCoarse; - int iFine=selStart.xFine; - int ord=e->getOrder(); - for (; iCoarse<=selEnd.xCoarse; iCoarse++) { - if (!e->song.chanShow[iCoarse]) continue; - DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][ord],true); - for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsesong.ins.empty()) continue; - absoluteTop=e->song.ins.size()-1; - } else if (iFine==2) { // volume - absoluteTop=e->getMaxVolumeChan(iCoarse); - } - for (int j=selStart.y; j<=selEnd.y; j++) { - int value=0; - int value2=0; - if (top-bottom<=0) { - value=MIN(absoluteTop,bottom); - value2=MIN(absoluteTop,bottom); - } else { - value=MIN(absoluteTop,bottom+(rand()%(top-bottom+1))); - value2=MIN(absoluteTop,bottom+(rand()%(top-bottom+1))); - } - if (mode) { - value&=15; - value2&=15; - pat->data[j][iFine+1]=value|(value2<<4); - } else { - pat->data[j][iFine+1]=value; - } - } - } - } - iFine=0; - } - - makeUndo(GUI_UNDO_PATTERN_RANDOMIZE); -} - -void FurnaceGUI::doFlip() { - finishSelection(); - prepareUndo(GUI_UNDO_PATTERN_FLIP); - - DivPattern patBuffer; - int iCoarse=selStart.xCoarse; - int iFine=selStart.xFine; - int ord=e->getOrder(); - for (; iCoarse<=selEnd.xCoarse; iCoarse++) { - if (!e->song.chanShow[iCoarse]) continue; - DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][ord],true); - for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsedata[j][0]; - } - patBuffer.data[j][iFine+1]=pat->data[j][iFine+1]; - } - for (int j=selStart.y; j<=selEnd.y; j++) { - if (iFine==0) { - pat->data[j][0]=patBuffer.data[selEnd.y-j+selStart.y][0]; - } - pat->data[j][iFine+1]=patBuffer.data[selEnd.y-j+selStart.y][iFine+1]; - } - } - iFine=0; - } - - makeUndo(GUI_UNDO_PATTERN_FLIP); -} - -void FurnaceGUI::doCollapse(int divider) { - finishSelection(); - prepareUndo(GUI_UNDO_PATTERN_COLLAPSE); - - DivPattern patBuffer; - int iCoarse=selStart.xCoarse; - int iFine=selStart.xFine; - int ord=e->getOrder(); - for (; iCoarse<=selEnd.xCoarse; iCoarse++) { - if (!e->song.chanShow[iCoarse]) continue; - DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][ord],true); - for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsedata[j][0]; - } - patBuffer.data[j][iFine+1]=pat->data[j][iFine+1]; - } - for (int j=0; j<=selEnd.y-selStart.y; j++) { - if (j*divider>=selEnd.y-selStart.y) { - if (iFine==0) { - pat->data[j+selStart.y][0]=0; - pat->data[j+selStart.y][1]=0; - } else { - pat->data[j+selStart.y][iFine+1]=-1; - } - } else { - if (iFine==0) { - pat->data[j+selStart.y][0]=patBuffer.data[j*divider+selStart.y][0]; - } - pat->data[j+selStart.y][iFine+1]=patBuffer.data[j*divider+selStart.y][iFine+1]; - - if (iFine==0) { - for (int k=1; k=selEnd.y-selStart.y) break; - if (!(pat->data[j+selStart.y][0]==0 && pat->data[j+selStart.y][1]==0)) break; - pat->data[j+selStart.y][0]=patBuffer.data[j*divider+selStart.y+k][0]; - pat->data[j+selStart.y][1]=patBuffer.data[j*divider+selStart.y+k][1]; - } - } else { - for (int k=1; k=selEnd.y-selStart.y) break; - if (pat->data[j+selStart.y][iFine+1]!=-1) break; - pat->data[j+selStart.y][iFine+1]=patBuffer.data[j*divider+selStart.y+k][iFine+1]; - } - } - } - } - } - iFine=0; - } - - makeUndo(GUI_UNDO_PATTERN_COLLAPSE); -} - -void FurnaceGUI::doExpand(int multiplier) { - if (multiplier<1) return; - - finishSelection(); - prepareUndo(GUI_UNDO_PATTERN_EXPAND); - - DivPattern patBuffer; - int iCoarse=selStart.xCoarse; - int iFine=selStart.xFine; - int ord=e->getOrder(); - for (; iCoarse<=selEnd.xCoarse; iCoarse++) { - if (!e->song.chanShow[iCoarse]) continue; - DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][ord],true); - for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsedata[j][0]; - } - patBuffer.data[j][iFine+1]=pat->data[j][iFine+1]; - } - for (int j=0; j<=(selEnd.y-selStart.y)*multiplier; j++) { - if ((j+selStart.y)>=e->song.patLen) break; - if ((j%multiplier)!=0) { - if (iFine==0) { - pat->data[j+selStart.y][0]=0; - pat->data[j+selStart.y][1]=0; - } else { - pat->data[j+selStart.y][iFine+1]=-1; - } - continue; - } - if (iFine==0) { - pat->data[j+selStart.y][0]=patBuffer.data[j/multiplier+selStart.y][0]; - } - pat->data[j+selStart.y][iFine+1]=patBuffer.data[j/multiplier+selStart.y][iFine+1]; - } - } - iFine=0; - } - - makeUndo(GUI_UNDO_PATTERN_EXPAND); -} - -void FurnaceGUI::doUndo() { - if (undoHist.empty()) return; - UndoStep& us=undoHist.back(); - redoHist.push_back(us); - MARK_MODIFIED; - - switch (us.type) { - case GUI_UNDO_CHANGE_ORDER: - e->song.ordersLen=us.oldOrdersLen; - for (UndoOrderData& i: us.ord) { - e->song.orders.ord[i.chan][i.ord]=i.oldVal; - } - break; - case GUI_UNDO_PATTERN_EDIT: - case GUI_UNDO_PATTERN_DELETE: - case GUI_UNDO_PATTERN_PULL: - case GUI_UNDO_PATTERN_PUSH: - case GUI_UNDO_PATTERN_CUT: - case GUI_UNDO_PATTERN_PASTE: - case GUI_UNDO_PATTERN_CHANGE_INS: - case GUI_UNDO_PATTERN_INTERPOLATE: - case GUI_UNDO_PATTERN_FADE: - case GUI_UNDO_PATTERN_SCALE: - case GUI_UNDO_PATTERN_RANDOMIZE: - case GUI_UNDO_PATTERN_INVERT_VAL: - case GUI_UNDO_PATTERN_FLIP: - case GUI_UNDO_PATTERN_COLLAPSE: - case GUI_UNDO_PATTERN_EXPAND: - for (UndoPatternData& i: us.pat) { - DivPattern* p=e->song.pat[i.chan].getPattern(i.pat,true); - p->data[i.row][i.col]=i.oldVal; - } - if (!e->isPlaying()) { - cursor=us.cursor; - selStart=us.selStart; - selEnd=us.selEnd; - curNibble=us.nibble; - updateScroll(cursor.y); - e->setOrder(us.order); - } - break; - } - - undoHist.pop_back(); -} - -void FurnaceGUI::doRedo() { - if (redoHist.empty()) return; - UndoStep& us=redoHist.back(); - undoHist.push_back(us); - MARK_MODIFIED; - - switch (us.type) { - case GUI_UNDO_CHANGE_ORDER: - e->song.ordersLen=us.newOrdersLen; - for (UndoOrderData& i: us.ord) { - e->song.orders.ord[i.chan][i.ord]=i.newVal; - } - break; - case GUI_UNDO_PATTERN_EDIT: - case GUI_UNDO_PATTERN_DELETE: - case GUI_UNDO_PATTERN_PULL: - case GUI_UNDO_PATTERN_PUSH: - case GUI_UNDO_PATTERN_CUT: - case GUI_UNDO_PATTERN_PASTE: - case GUI_UNDO_PATTERN_CHANGE_INS: - case GUI_UNDO_PATTERN_INTERPOLATE: - case GUI_UNDO_PATTERN_FADE: - case GUI_UNDO_PATTERN_SCALE: - case GUI_UNDO_PATTERN_RANDOMIZE: - case GUI_UNDO_PATTERN_INVERT_VAL: - case GUI_UNDO_PATTERN_FLIP: - case GUI_UNDO_PATTERN_COLLAPSE: - case GUI_UNDO_PATTERN_EXPAND: - for (UndoPatternData& i: us.pat) { - DivPattern* p=e->song.pat[i.chan].getPattern(i.pat,true); - p->data[i.row][i.col]=i.newVal; - } - if (!e->isPlaying()) { - cursor=us.cursor; - selStart=us.selStart; - selEnd=us.selEnd; - curNibble=us.nibble; - updateScroll(cursor.y); - e->setOrder(us.order); - } - - break; - } - - redoHist.pop_back(); -} - void FurnaceGUI::play(int row) { e->walkSong(loopOrder,loopRow,loopEnd); memset(lastIns,-1,sizeof(int)*DIV_MAX_CHANS); @@ -3373,670 +725,6 @@ void FurnaceGUI::stopPreviewNote(SDL_Scancode scancode) { } } -void FurnaceGUI::doAction(int what) { - switch (what) { - case GUI_ACTION_OPEN: - if (modified) { - showWarning("Unsaved changes! Are you sure?",GUI_WARN_OPEN); - } else { - openFileDialog(GUI_FILE_OPEN); - } - break; - case GUI_ACTION_OPEN_BACKUP: - if (modified) { - showWarning("Unsaved changes! Are you sure?",GUI_WARN_OPEN_BACKUP); - } else { - if (load(backupPath)>0) { - showError("No backup available! (or unable to open it)"); - } - } - break; - case GUI_ACTION_SAVE: - if (curFileName=="" || curFileName==backupPath || e->song.version>=0xff00) { - openFileDialog(GUI_FILE_SAVE); - } else { - if (save(curFileName,e->song.isDMF?e->song.version:0)>0) { - showError(fmt::sprintf("Error while saving file! (%s)",lastError)); - } - } - break; - case GUI_ACTION_SAVE_AS: - openFileDialog(GUI_FILE_SAVE); - break; - case GUI_ACTION_UNDO: - doUndo(); - break; - case GUI_ACTION_REDO: - doRedo(); - break; - case GUI_ACTION_PLAY_TOGGLE: - if (e->isPlaying() && !e->isStepping()) { - stop(); - } else { - play(); - } - break; - case GUI_ACTION_PLAY: - play(); - break; - case GUI_ACTION_STOP: - stop(); - break; - case GUI_ACTION_PLAY_REPEAT: - play(); - e->setRepeatPattern(true); - break; - case GUI_ACTION_PLAY_CURSOR: - if (e->isPlaying() && !e->isStepping()) { - stop(); - } else { - play(cursor.y); - } - break; - case GUI_ACTION_STEP_ONE: - e->stepOne(cursor.y); - break; - case GUI_ACTION_OCTAVE_UP: - if (++curOctave>7) { - curOctave=7; - } else { - for (size_t i=0; inoteOff(activeNotes[i].chan); - } - activeNotes.clear(); - } - break; - case GUI_ACTION_OCTAVE_DOWN: - if (--curOctave<-5) { - curOctave=-5; - } else { - for (size_t i=0; inoteOff(activeNotes[i].chan); - } - activeNotes.clear(); - } - break; - case GUI_ACTION_INS_UP: - if (--curIns<-1) curIns=-1; - break; - case GUI_ACTION_INS_DOWN: - if (++curIns>=(int)e->song.ins.size()) curIns=((int)e->song.ins.size())-1; - break; - case GUI_ACTION_STEP_UP: - if (++editStep>64) editStep=64; - break; - case GUI_ACTION_STEP_DOWN: - if (--editStep<0) editStep=0; - break; - case GUI_ACTION_TOGGLE_EDIT: - edit=!edit; - break; - case GUI_ACTION_METRONOME: - e->setMetronome(!e->getMetronome()); - break; - case GUI_ACTION_REPEAT_PATTERN: - e->setRepeatPattern(!e->getRepeatPattern()); - break; - case GUI_ACTION_FOLLOW_ORDERS: - followOrders=!followOrders; - break; - case GUI_ACTION_FOLLOW_PATTERN: - followPattern=!followPattern; - break; - case GUI_ACTION_PANIC: - e->syncReset(); - break; - - case GUI_ACTION_WINDOW_EDIT_CONTROLS: - nextWindow=GUI_WINDOW_EDIT_CONTROLS; - break; - case GUI_ACTION_WINDOW_ORDERS: - nextWindow=GUI_WINDOW_ORDERS; - break; - case GUI_ACTION_WINDOW_INS_LIST: - nextWindow=GUI_WINDOW_INS_LIST; - break; - case GUI_ACTION_WINDOW_INS_EDIT: - nextWindow=GUI_WINDOW_INS_EDIT; - break; - case GUI_ACTION_WINDOW_SONG_INFO: - nextWindow=GUI_WINDOW_SONG_INFO; - break; - case GUI_ACTION_WINDOW_PATTERN: - nextWindow=GUI_WINDOW_PATTERN; - break; - case GUI_ACTION_WINDOW_WAVE_LIST: - nextWindow=GUI_WINDOW_WAVE_LIST; - break; - case GUI_ACTION_WINDOW_WAVE_EDIT: - nextWindow=GUI_WINDOW_WAVE_EDIT; - break; - case GUI_ACTION_WINDOW_SAMPLE_LIST: - nextWindow=GUI_WINDOW_SAMPLE_LIST; - break; - case GUI_ACTION_WINDOW_SAMPLE_EDIT: - nextWindow=GUI_WINDOW_SAMPLE_EDIT; - break; - case GUI_ACTION_WINDOW_ABOUT: - nextWindow=GUI_WINDOW_ABOUT; - break; - case GUI_ACTION_WINDOW_SETTINGS: - nextWindow=GUI_WINDOW_SETTINGS; - break; - case GUI_ACTION_WINDOW_MIXER: - nextWindow=GUI_WINDOW_MIXER; - break; - case GUI_ACTION_WINDOW_DEBUG: - nextWindow=GUI_WINDOW_DEBUG; - break; - case GUI_ACTION_WINDOW_OSCILLOSCOPE: - nextWindow=GUI_WINDOW_OSCILLOSCOPE; - break; - case GUI_ACTION_WINDOW_VOL_METER: - nextWindow=GUI_WINDOW_VOL_METER; - break; - case GUI_ACTION_WINDOW_STATS: - nextWindow=GUI_WINDOW_STATS; - break; - case GUI_ACTION_WINDOW_COMPAT_FLAGS: - nextWindow=GUI_WINDOW_COMPAT_FLAGS; - break; - case GUI_ACTION_WINDOW_PIANO: - nextWindow=GUI_WINDOW_PIANO; - break; - case GUI_ACTION_WINDOW_NOTES: - nextWindow=GUI_WINDOW_NOTES; - break; - case GUI_ACTION_WINDOW_CHANNELS: - nextWindow=GUI_WINDOW_CHANNELS; - break; - case GUI_ACTION_WINDOW_REGISTER_VIEW: - nextWindow=GUI_WINDOW_REGISTER_VIEW; - break; - - case GUI_ACTION_COLLAPSE_WINDOW: - collapseWindow=true; - break; - case GUI_ACTION_CLOSE_WINDOW: - switch (curWindow) { - case GUI_WINDOW_EDIT_CONTROLS: - editControlsOpen=false; - break; - case GUI_WINDOW_SONG_INFO: - songInfoOpen=false; - break; - case GUI_WINDOW_ORDERS: - ordersOpen=false; - break; - case GUI_WINDOW_INS_LIST: - insListOpen=false; - break; - case GUI_WINDOW_PATTERN: - patternOpen=false; - break; - case GUI_WINDOW_INS_EDIT: - insEditOpen=false; - break; - case GUI_WINDOW_WAVE_LIST: - waveListOpen=false; - break; - case GUI_WINDOW_WAVE_EDIT: - waveEditOpen=false; - break; - case GUI_WINDOW_SAMPLE_LIST: - sampleListOpen=false; - break; - case GUI_WINDOW_SAMPLE_EDIT: - sampleEditOpen=false; - break; - case GUI_WINDOW_MIXER: - mixerOpen=false; - break; - case GUI_WINDOW_ABOUT: - aboutOpen=false; - break; - case GUI_WINDOW_SETTINGS: - settingsOpen=false; - break; - case GUI_WINDOW_DEBUG: - debugOpen=false; - break; - case GUI_WINDOW_OSCILLOSCOPE: - oscOpen=false; - break; - case GUI_WINDOW_VOL_METER: - volMeterOpen=false; - break; - case GUI_WINDOW_STATS: - statsOpen=false; - break; - case GUI_WINDOW_COMPAT_FLAGS: - compatFlagsOpen=false; - break; - case GUI_WINDOW_PIANO: - pianoOpen=false; - break; - case GUI_WINDOW_NOTES: - notesOpen=false; - break; - case GUI_WINDOW_CHANNELS: - channelsOpen=false; - break; - case GUI_WINDOW_REGISTER_VIEW: - regViewOpen=false; - break; - default: - break; - } - curWindow=GUI_WINDOW_NOTHING; - break; - - case GUI_ACTION_PAT_NOTE_UP: - doTranspose(1); - break; - case GUI_ACTION_PAT_NOTE_DOWN: - doTranspose(-1); - break; - case GUI_ACTION_PAT_OCTAVE_UP: - doTranspose(12); - break; - case GUI_ACTION_PAT_OCTAVE_DOWN: - doTranspose(-12); - break; - case GUI_ACTION_PAT_SELECT_ALL: - doSelectAll(); - break; - case GUI_ACTION_PAT_CUT: - doCopy(true); - break; - case GUI_ACTION_PAT_COPY: - doCopy(false); - break; - case GUI_ACTION_PAT_PASTE: - doPaste(); - break; - case GUI_ACTION_PAT_CURSOR_UP: - moveCursor(0,-MAX(1,settings.scrollStep?editStep:1),false); - break; - case GUI_ACTION_PAT_CURSOR_DOWN: - moveCursor(0,MAX(1,settings.scrollStep?editStep:1),false); - break; - case GUI_ACTION_PAT_CURSOR_LEFT: - moveCursor(-1,0,false); - break; - case GUI_ACTION_PAT_CURSOR_RIGHT: - moveCursor(1,0,false); - break; - case GUI_ACTION_PAT_CURSOR_UP_ONE: - moveCursor(0,-1,false); - break; - case GUI_ACTION_PAT_CURSOR_DOWN_ONE: - moveCursor(0,1,false); - break; - case GUI_ACTION_PAT_CURSOR_LEFT_CHANNEL: - moveCursorPrevChannel(false); - break; - case GUI_ACTION_PAT_CURSOR_RIGHT_CHANNEL: - moveCursorNextChannel(false); - break; - case GUI_ACTION_PAT_CURSOR_NEXT_CHANNEL: - moveCursorNextChannel(true); - break; - case GUI_ACTION_PAT_CURSOR_PREVIOUS_CHANNEL: - moveCursorPrevChannel(true); - break; - case GUI_ACTION_PAT_CURSOR_BEGIN: - moveCursorTop(false); - break; - case GUI_ACTION_PAT_CURSOR_END: - moveCursorBottom(false); - break; - case GUI_ACTION_PAT_CURSOR_UP_COARSE: - moveCursor(0,-16,false); - break; - case GUI_ACTION_PAT_CURSOR_DOWN_COARSE: - moveCursor(0,16,false); - break; - case GUI_ACTION_PAT_SELECTION_UP: - moveCursor(0,-MAX(1,settings.scrollStep?editStep:1),true); - break; - case GUI_ACTION_PAT_SELECTION_DOWN: - moveCursor(0,MAX(1,settings.scrollStep?editStep:1),true); - break; - case GUI_ACTION_PAT_SELECTION_LEFT: - moveCursor(-1,0,true); - break; - case GUI_ACTION_PAT_SELECTION_RIGHT: - moveCursor(1,0,true); - break; - case GUI_ACTION_PAT_SELECTION_UP_ONE: - moveCursor(0,-1,true); - break; - case GUI_ACTION_PAT_SELECTION_DOWN_ONE: - moveCursor(0,1,true); - break; - case GUI_ACTION_PAT_SELECTION_BEGIN: - moveCursorTop(true); - break; - case GUI_ACTION_PAT_SELECTION_END: - moveCursorBottom(true); - break; - case GUI_ACTION_PAT_SELECTION_UP_COARSE: - moveCursor(0,-16,true); - break; - case GUI_ACTION_PAT_SELECTION_DOWN_COARSE: - moveCursor(0,16,true); - break; - case GUI_ACTION_PAT_DELETE: - doDelete(); - if (settings.stepOnDelete) { - moveCursor(0,editStep,false); - } - break; - case GUI_ACTION_PAT_PULL_DELETE: - doPullDelete(); - break; - case GUI_ACTION_PAT_INSERT: - doInsert(); - if (settings.stepOnInsert) { - moveCursor(0,editStep,false); - } - break; - case GUI_ACTION_PAT_MUTE_CURSOR: - if (cursor.xCoarse<0 || cursor.xCoarse>=e->getTotalChannelCount()) break; - e->toggleMute(cursor.xCoarse); - break; - case GUI_ACTION_PAT_SOLO_CURSOR: - if (cursor.xCoarse<0 || cursor.xCoarse>=e->getTotalChannelCount()) break; - e->toggleSolo(cursor.xCoarse); - break; - case GUI_ACTION_PAT_UNMUTE_ALL: - e->unmuteAll(); - break; - case GUI_ACTION_PAT_NEXT_ORDER: - if (e->getOrder()song.ordersLen-1) { - e->setOrder(e->getOrder()+1); - } - break; - case GUI_ACTION_PAT_PREV_ORDER: - if (e->getOrder()>0) { - e->setOrder(e->getOrder()-1); - } - break; - case GUI_ACTION_PAT_COLLAPSE: - if (cursor.xCoarse<0 || cursor.xCoarse>=e->getTotalChannelCount()) break; - e->song.chanCollapse[cursor.xCoarse]=!e->song.chanCollapse[cursor.xCoarse]; - break; - case GUI_ACTION_PAT_INCREASE_COLUMNS: - if (cursor.xCoarse<0 || cursor.xCoarse>=e->getTotalChannelCount()) break; - e->song.pat[cursor.xCoarse].effectRows++; - if (e->song.pat[cursor.xCoarse].effectRows>8) e->song.pat[cursor.xCoarse].effectRows=8; - break; - case GUI_ACTION_PAT_DECREASE_COLUMNS: - if (cursor.xCoarse<0 || cursor.xCoarse>=e->getTotalChannelCount()) break; - e->song.pat[cursor.xCoarse].effectRows--; - if (e->song.pat[cursor.xCoarse].effectRows<1) e->song.pat[cursor.xCoarse].effectRows=1; - break; - case GUI_ACTION_PAT_INTERPOLATE: - doInterpolate(); - break; - case GUI_ACTION_PAT_INVERT_VALUES: - doInvertValues(); - break; - case GUI_ACTION_PAT_FLIP_SELECTION: - doFlip(); - break; - case GUI_ACTION_PAT_COLLAPSE_ROWS: - doCollapse(2); - break; - case GUI_ACTION_PAT_EXPAND_ROWS: - doExpand(2); - break; - case GUI_ACTION_PAT_COLLAPSE_PAT: // TODO - break; - case GUI_ACTION_PAT_EXPAND_PAT: // TODO - break; - case GUI_ACTION_PAT_COLLAPSE_SONG: // TODO - break; - case GUI_ACTION_PAT_EXPAND_SONG: // TODO - break; - case GUI_ACTION_PAT_LATCH: // TODO - break; - - case GUI_ACTION_INS_LIST_ADD: - curIns=e->addInstrument(cursor.xCoarse); - MARK_MODIFIED; - break; - case GUI_ACTION_INS_LIST_DUPLICATE: - if (curIns>=0 && curIns<(int)e->song.ins.size()) { - int prevIns=curIns; - curIns=e->addInstrument(cursor.xCoarse); - (*e->song.ins[curIns])=(*e->song.ins[prevIns]); - MARK_MODIFIED; - } - break; - case GUI_ACTION_INS_LIST_OPEN: - openFileDialog(GUI_FILE_INS_OPEN); - break; - case GUI_ACTION_INS_LIST_SAVE: - if (curIns>=0 && curIns<(int)e->song.ins.size()) openFileDialog(GUI_FILE_INS_SAVE); - break; - case GUI_ACTION_INS_LIST_MOVE_UP: - if (e->moveInsUp(curIns)) curIns--; - break; - case GUI_ACTION_INS_LIST_MOVE_DOWN: - if (e->moveInsDown(curIns)) curIns++; - break; - case GUI_ACTION_INS_LIST_DELETE: - if (curIns>=0 && curIns<(int)e->song.ins.size()) { - e->delInstrument(curIns); - MARK_MODIFIED; - if (curIns>=(int)e->song.ins.size()) { - curIns--; - } - } - break; - case GUI_ACTION_INS_LIST_EDIT: - insEditOpen=true; - break; - case GUI_ACTION_INS_LIST_UP: - if (--curIns<0) curIns=0; - break; - case GUI_ACTION_INS_LIST_DOWN: - if (++curIns>=(int)e->song.ins.size()) curIns=((int)e->song.ins.size())-1; - break; - - case GUI_ACTION_WAVE_LIST_ADD: - curWave=e->addWave(); - MARK_MODIFIED; - break; - case GUI_ACTION_WAVE_LIST_DUPLICATE: - if (curWave>=0 && curWave<(int)e->song.wave.size()) { - int prevWave=curWave; - curWave=e->addWave(); - (*e->song.wave[curWave])=(*e->song.wave[prevWave]); - MARK_MODIFIED; - } - break; - case GUI_ACTION_WAVE_LIST_OPEN: - openFileDialog(GUI_FILE_WAVE_OPEN); - break; - case GUI_ACTION_WAVE_LIST_SAVE: - if (curWave>=0 && curWave<(int)e->song.wave.size()) openFileDialog(GUI_FILE_WAVE_SAVE); - break; - case GUI_ACTION_WAVE_LIST_MOVE_UP: - if (e->moveWaveUp(curWave)) curWave--; - break; - case GUI_ACTION_WAVE_LIST_MOVE_DOWN: - if (e->moveWaveDown(curWave)) curWave++; - break; - case GUI_ACTION_WAVE_LIST_DELETE: - if (curWave>=0 && curWave<(int)e->song.wave.size()) { - e->delWave(curWave); - MARK_MODIFIED; - if (curWave>=(int)e->song.wave.size()) { - curWave--; - } - } - break; - case GUI_ACTION_WAVE_LIST_EDIT: - waveEditOpen=true; - break; - case GUI_ACTION_WAVE_LIST_UP: - if (--curWave<0) curWave=0; - break; - case GUI_ACTION_WAVE_LIST_DOWN: - if (++curWave>=(int)e->song.wave.size()) curWave=((int)e->song.wave.size())-1; - break; - - case GUI_ACTION_SAMPLE_LIST_ADD: - curSample=e->addSample(); - MARK_MODIFIED; - break; - case GUI_ACTION_SAMPLE_LIST_OPEN: - openFileDialog(GUI_FILE_SAMPLE_OPEN); - break; - case GUI_ACTION_SAMPLE_LIST_SAVE: - if (curSample>=0 && curSample<(int)e->song.sample.size()) openFileDialog(GUI_FILE_SAMPLE_SAVE); - break; - case GUI_ACTION_SAMPLE_LIST_MOVE_UP: - if (e->moveSampleUp(curSample)) curSample--; - break; - case GUI_ACTION_SAMPLE_LIST_MOVE_DOWN: - if (e->moveSampleDown(curSample)) curSample++; - break; - case GUI_ACTION_SAMPLE_LIST_DELETE: - e->delSample(curSample); - MARK_MODIFIED; - if (curSample>=(int)e->song.sample.size()) { - curSample--; - } - break; - case GUI_ACTION_SAMPLE_LIST_EDIT: - sampleEditOpen=true; - break; - case GUI_ACTION_SAMPLE_LIST_UP: - if (--curSample<0) curSample=0; - break; - case GUI_ACTION_SAMPLE_LIST_DOWN: - if (++curSample>=(int)e->song.sample.size()) curSample=((int)e->song.sample.size())-1; - break; - case GUI_ACTION_SAMPLE_LIST_PREVIEW: - e->previewSample(curSample); - break; - case GUI_ACTION_SAMPLE_LIST_STOP_PREVIEW: - e->stopSamplePreview(); - break; - - case GUI_ACTION_ORDERS_UP: - if (e->getOrder()>0) { - e->setOrder(e->getOrder()-1); - } - break; - case GUI_ACTION_ORDERS_DOWN: - if (e->getOrder()song.ordersLen-1) { - e->setOrder(e->getOrder()+1); - } - break; - case GUI_ACTION_ORDERS_LEFT: { - DETERMINE_FIRST; - - do { - orderCursor--; - if (orderCursorsong.chanShow[orderCursor]); - break; - } - case GUI_ACTION_ORDERS_RIGHT: { - DETERMINE_LAST; - - do { - orderCursor++; - if (orderCursor>=lastChannel) { - orderCursor=lastChannel-1; - break; - } - } while (!e->song.chanShow[orderCursor]); - break; - } - case GUI_ACTION_ORDERS_INCREASE: { - if (orderCursor<0 || orderCursor>=e->getTotalChannelCount()) break; - int curOrder=e->getOrder(); - if (e->song.orders.ord[orderCursor][curOrder]<0x7f) { - e->song.orders.ord[orderCursor][curOrder]++; - } - break; - } - case GUI_ACTION_ORDERS_DECREASE: { - if (orderCursor<0 || orderCursor>=e->getTotalChannelCount()) break; - int curOrder=e->getOrder(); - if (e->song.orders.ord[orderCursor][curOrder]>0) { - e->song.orders.ord[orderCursor][curOrder]--; - } - break; - } - case GUI_ACTION_ORDERS_EDIT_MODE: - orderEditMode++; - if (orderEditMode>3) orderEditMode=0; - break; - case GUI_ACTION_ORDERS_LINK: - changeAllOrders=!changeAllOrders; - break; - case GUI_ACTION_ORDERS_ADD: - prepareUndo(GUI_UNDO_CHANGE_ORDER); - e->addOrder(false,false); - makeUndo(GUI_UNDO_CHANGE_ORDER); - break; - case GUI_ACTION_ORDERS_DUPLICATE: - prepareUndo(GUI_UNDO_CHANGE_ORDER); - e->addOrder(true,false); - makeUndo(GUI_UNDO_CHANGE_ORDER); - break; - case GUI_ACTION_ORDERS_DEEP_CLONE: - prepareUndo(GUI_UNDO_CHANGE_ORDER); - e->deepCloneOrder(false); - makeUndo(GUI_UNDO_CHANGE_ORDER); - if (!e->getWarnings().empty()) { - showWarning(e->getWarnings(),GUI_WARN_GENERIC); - } - break; - case GUI_ACTION_ORDERS_DUPLICATE_END: - prepareUndo(GUI_UNDO_CHANGE_ORDER); - e->addOrder(true,true); - makeUndo(GUI_UNDO_CHANGE_ORDER); - break; - case GUI_ACTION_ORDERS_DEEP_CLONE_END: - prepareUndo(GUI_UNDO_CHANGE_ORDER); - e->deepCloneOrder(true); - makeUndo(GUI_UNDO_CHANGE_ORDER); - if (!e->getWarnings().empty()) { - showWarning(e->getWarnings(),GUI_WARN_GENERIC); - } - break; - case GUI_ACTION_ORDERS_REMOVE: - prepareUndo(GUI_UNDO_CHANGE_ORDER); - e->deleteOrder(); - makeUndo(GUI_UNDO_CHANGE_ORDER); - break; - case GUI_ACTION_ORDERS_MOVE_UP: - prepareUndo(GUI_UNDO_CHANGE_ORDER); - e->moveOrderUp(); - makeUndo(GUI_UNDO_CHANGE_ORDER); - break; - case GUI_ACTION_ORDERS_MOVE_DOWN: - prepareUndo(GUI_UNDO_CHANGE_ORDER); - e->moveOrderDown(); - makeUndo(GUI_UNDO_CHANGE_ORDER); - break; - case GUI_ACTION_ORDERS_REPLAY: - e->setOrder(e->getOrder()); - break; - } -} - void FurnaceGUI::keyDown(SDL_Event& ev) { if (ImGuiFileDialog::Instance()->IsOpened()) return; if (aboutOpen) return; @@ -5262,398 +1950,15 @@ bool FurnaceGUI::loop() { } ImGui::Separator(); if (ImGui::BeginMenu("add system...")) { - sysAddOption(DIV_SYSTEM_YM2612); - sysAddOption(DIV_SYSTEM_YM2612_EXT); - sysAddOption(DIV_SYSTEM_SMS); - sysAddOption(DIV_SYSTEM_GB); - sysAddOption(DIV_SYSTEM_PCE); - sysAddOption(DIV_SYSTEM_NES); - sysAddOption(DIV_SYSTEM_C64_8580); - sysAddOption(DIV_SYSTEM_C64_6581); - sysAddOption(DIV_SYSTEM_YM2151); - sysAddOption(DIV_SYSTEM_SEGAPCM); - sysAddOption(DIV_SYSTEM_SEGAPCM_COMPAT); - sysAddOption(DIV_SYSTEM_YM2610); - sysAddOption(DIV_SYSTEM_YM2610_EXT); - sysAddOption(DIV_SYSTEM_YM2610_FULL); - sysAddOption(DIV_SYSTEM_YM2610_FULL_EXT); - sysAddOption(DIV_SYSTEM_YM2610B); - sysAddOption(DIV_SYSTEM_YM2610B_EXT); - sysAddOption(DIV_SYSTEM_AY8910); - sysAddOption(DIV_SYSTEM_AMIGA); - sysAddOption(DIV_SYSTEM_PCSPKR); - sysAddOption(DIV_SYSTEM_OPLL); - sysAddOption(DIV_SYSTEM_OPLL_DRUMS); - sysAddOption(DIV_SYSTEM_VRC7); - sysAddOption(DIV_SYSTEM_OPL); - sysAddOption(DIV_SYSTEM_OPL_DRUMS); - sysAddOption(DIV_SYSTEM_OPL2); - sysAddOption(DIV_SYSTEM_OPL2_DRUMS); - sysAddOption(DIV_SYSTEM_OPL3); - sysAddOption(DIV_SYSTEM_OPL3_DRUMS); - sysAddOption(DIV_SYSTEM_TIA); - sysAddOption(DIV_SYSTEM_SAA1099); - sysAddOption(DIV_SYSTEM_AY8930); - sysAddOption(DIV_SYSTEM_LYNX); - sysAddOption(DIV_SYSTEM_QSOUND); - sysAddOption(DIV_SYSTEM_X1_010); - sysAddOption(DIV_SYSTEM_SWAN); - sysAddOption(DIV_SYSTEM_VERA); - sysAddOption(DIV_SYSTEM_BUBSYS_WSG); - sysAddOption(DIV_SYSTEM_PET); + for (int j=0; availableSystems[j]; j++) { + sysAddOption((DivSystem)availableSystems[j]); + } ImGui::EndMenu(); } if (ImGui::BeginMenu("configure system...")) { for (int i=0; isong.systemLen; i++) { if (ImGui::TreeNode(fmt::sprintf("%d. %s##_SYSP%d",i+1,getSystemName(e->song.system[i]),i).c_str())) { - unsigned int flags=e->song.systemFlags[i]; - bool restart=settings.restartOnFlagChange; - bool sysPal=flags&1; - switch (e->song.system[i]) { - case DIV_SYSTEM_YM2612: - case DIV_SYSTEM_YM2612_EXT: { - if (ImGui::RadioButton("NTSC (7.67MHz)",(flags&3)==0)) { - e->setSysFlags(i,(flags&0x80000000)|0,restart); - updateWindowTitle(); - } - if (ImGui::RadioButton("PAL (7.61MHz)",(flags&3)==1)) { - e->setSysFlags(i,(flags&0x80000000)|1,restart); - updateWindowTitle(); - } - if (ImGui::RadioButton("FM Towns (8MHz)",(flags&3)==2)) { - e->setSysFlags(i,(flags&0x80000000)|2,restart); - updateWindowTitle(); - } - if (ImGui::RadioButton("AtGames Genesis (6.13MHz)",(flags&3)==3)) { - e->setSysFlags(i,(flags&0x80000000)|3,restart); - updateWindowTitle(); - } - bool ladder=flags&0x80000000; - if (ImGui::Checkbox("Enable DAC distortion",&ladder)) { - e->setSysFlags(i,(flags&(~0x80000000))|(ladder?0x80000000:0),restart); - updateWindowTitle(); - } - break; - } - case DIV_SYSTEM_SMS: { - ImGui::Text("Clock rate:"); - if (ImGui::RadioButton("NTSC (3.58MHz)",(flags&3)==0)) { - e->setSysFlags(i,(flags&(~3))|0,restart); - updateWindowTitle(); - } - if (ImGui::RadioButton("PAL (3.55MHz)",(flags&3)==1)) { - e->setSysFlags(i,(flags&(~3))|1,restart); - updateWindowTitle(); - } - if (ImGui::RadioButton("BBC Micro (4MHz)",(flags&3)==2)) { - e->setSysFlags(i,(flags&(~3))|2,restart); - updateWindowTitle(); - } - if (ImGui::RadioButton("Half NTSC (1.79MHz)",(flags&3)==3)) { - e->setSysFlags(i,(flags&(~3))|3,restart); - updateWindowTitle(); - } - ImGui::Text("Chip type:"); - if (ImGui::RadioButton("Sega VDP/Master System",((flags>>2)&3)==0)) { - e->setSysFlags(i,(flags&(~12))|0,restart); - updateWindowTitle(); - } - if (ImGui::RadioButton("TI SN76489",((flags>>2)&3)==1)) { - e->setSysFlags(i,(flags&(~12))|4,restart); - updateWindowTitle(); - } - if (ImGui::RadioButton("TI SN76489 with Atari-like short noise",((flags>>2)&3)==2)) { - e->setSysFlags(i,(flags&(~12))|8,restart); - updateWindowTitle(); - } - /*if (ImGui::RadioButton("Game Gear",(flags>>2)==3)) { - e->setSysFlags(i,(flags&3)|12); - }*/ - - bool noPhaseReset=flags&16; - if (ImGui::Checkbox("Disable noise period change phase reset",&noPhaseReset)) { - e->setSysFlags(i,(flags&(~16))|(noPhaseReset<<4),restart); - updateWindowTitle(); - } - break; - } - case DIV_SYSTEM_OPLL: - case DIV_SYSTEM_OPLL_DRUMS: - case DIV_SYSTEM_VRC7: { - ImGui::Text("Clock rate:"); - if (ImGui::RadioButton("NTSC (3.58MHz)",(flags&15)==0)) { - e->setSysFlags(i,(flags&(~15))|0,restart); - updateWindowTitle(); - } - if (ImGui::RadioButton("PAL (3.55MHz)",(flags&15)==1)) { - e->setSysFlags(i,(flags&(~15))|1,restart); - updateWindowTitle(); - } - if (ImGui::RadioButton("BBC Micro (4MHz)",(flags&15)==2)) { - e->setSysFlags(i,(flags&(~15))|2,restart); - updateWindowTitle(); - } - if (ImGui::RadioButton("Half NTSC (1.79MHz)",(flags&15)==3)) { - e->setSysFlags(i,(flags&(~15))|3,restart); - updateWindowTitle(); - } - if (e->song.system[i]!=DIV_SYSTEM_VRC7) { - ImGui::Text("Patch set:"); - if (ImGui::RadioButton("Yamaha YM2413",((flags>>4)&15)==0)) { - e->setSysFlags(i,(flags&(~0xf0))|0,restart); - updateWindowTitle(); - } - if (ImGui::RadioButton("Yamaha YMF281",((flags>>4)&15)==1)) { - e->setSysFlags(i,(flags&(~0xf0))|0x10,restart); - updateWindowTitle(); - } - if (ImGui::RadioButton("Yamaha YM2423",((flags>>4)&15)==2)) { - e->setSysFlags(i,(flags&(~0xf0))|0x20,restart); - updateWindowTitle(); - } - if (ImGui::RadioButton("Konami VRC7",((flags>>4)&15)==3)) { - e->setSysFlags(i,(flags&(~0xf0))|0x30,restart); - updateWindowTitle(); - } - } - break; - } - case DIV_SYSTEM_YM2151: - if (ImGui::RadioButton("NTSC/X16 (3.58MHz)",flags==0)) { - e->setSysFlags(i,0,restart); - updateWindowTitle(); - } - if (ImGui::RadioButton("PAL (3.55MHz)",flags==1)) { - e->setSysFlags(i,1,restart); - updateWindowTitle(); - } - if (ImGui::RadioButton("X1/X68000 (4MHz)",flags==2)) { - e->setSysFlags(i,2,restart); - updateWindowTitle(); - } - break; - case DIV_SYSTEM_NES: - if (ImGui::RadioButton("NTSC (1.79MHz)",flags==0)) { - e->setSysFlags(i,0,restart); - updateWindowTitle(); - } - if (ImGui::RadioButton("PAL (1.67MHz)",flags==1)) { - e->setSysFlags(i,1,restart); - updateWindowTitle(); - } - if (ImGui::RadioButton("Dendy (1.77MHz)",flags==2)) { - e->setSysFlags(i,2,restart); - updateWindowTitle(); - } - break; - case DIV_SYSTEM_C64_8580: - case DIV_SYSTEM_C64_6581: - if (ImGui::RadioButton("NTSC (1.02MHz)",flags==0)) { - e->setSysFlags(i,0,restart); - updateWindowTitle(); - } - if (ImGui::RadioButton("PAL (0.99MHz)",flags==1)) { - e->setSysFlags(i,1,restart); - updateWindowTitle(); - } - if (ImGui::RadioButton("SSI 2001 (0.89MHz)",flags==2)) { - e->setSysFlags(i,2,restart); - updateWindowTitle(); - } - break; - case DIV_SYSTEM_AY8910: - case DIV_SYSTEM_AY8930: { - ImGui::Text("Clock rate:"); - if (ImGui::RadioButton("1.79MHz (ZX Spectrum NTSC/MSX)",(flags&15)==0)) { - e->setSysFlags(i,(flags&(~15))|0,restart); - updateWindowTitle(); - } - if (ImGui::RadioButton("1.77MHz (ZX Spectrum)",(flags&15)==1)) { - e->setSysFlags(i,(flags&(~15))|1,restart); - updateWindowTitle(); - } - if (ImGui::RadioButton("1.75MHz (ZX Spectrum)",(flags&15)==2)) { - e->setSysFlags(i,(flags&(~15))|2,restart); - updateWindowTitle(); - } - if (ImGui::RadioButton("2MHz (Atari ST/Sharp X1)",(flags&15)==3)) { - e->setSysFlags(i,(flags&(~15))|3,restart); - updateWindowTitle(); - } - if (ImGui::RadioButton("1.5MHz (Vectrex)",(flags&15)==4)) { - e->setSysFlags(i,(flags&(~15))|4,restart); - updateWindowTitle(); - } - if (ImGui::RadioButton("1MHz (Amstrad CPC)",(flags&15)==5)) { - e->setSysFlags(i,(flags&(~15))|5,restart); - updateWindowTitle(); - } - if (ImGui::RadioButton("0.89MHz (Sunsoft 5B)",(flags&15)==6)) { - e->setSysFlags(i,(flags&(~15))|6,restart); - updateWindowTitle(); - } - if (ImGui::RadioButton("1.67MHz (?)",(flags&15)==7)) { - e->setSysFlags(i,(flags&(~15))|7,restart); - updateWindowTitle(); - } - if (ImGui::RadioButton("0.83MHz (Sunsoft 5B on PAL)",(flags&15)==8)) { - e->setSysFlags(i,(flags&(~15))|8,restart); - updateWindowTitle(); - } - if (ImGui::RadioButton("1.10MHz (Gamate/VIC-20 PAL)",(flags&15)==9)) { - e->setSysFlags(i,(flags&(~15))|9,restart); - updateWindowTitle(); - } - if (ImGui::RadioButton("2^21Hz (Game Boy)",(flags&15)==10)) { - e->setSysFlags(i,(flags&(~15))|10,restart); - updateWindowTitle(); - } - if (e->song.system[i]==DIV_SYSTEM_AY8910) { - ImGui::Text("Chip type:"); - if (ImGui::RadioButton("AY-3-8910",(flags&0x30)==0)) { - e->setSysFlags(i,(flags&(~0x30))|0,restart); - updateWindowTitle(); - } - if (ImGui::RadioButton("YM2149(F)",(flags&0x30)==16)) { - e->setSysFlags(i,(flags&(~0x30))|16,restart); - updateWindowTitle(); - } - if (ImGui::RadioButton("Sunsoft 5B",(flags&0x30)==32)) { - e->setSysFlags(i,(flags&(~0x30))|32,restart); - updateWindowTitle(); - } - if (ImGui::RadioButton("AY-3-8914",(flags&0x30)==48)) { - e->setSysFlags(i,(flags&(~0x30))|48,restart); - updateWindowTitle(); - } - } - bool stereo=flags&0x40; - ImGui::BeginDisabled((flags&0x30)==32); - if (ImGui::Checkbox("Stereo##_AY_STEREO",&stereo)) { - e->setSysFlags(i,(flags&(~0x40))|(stereo?0x40:0),restart); - updateWindowTitle(); - } - ImGui::EndDisabled(); - break; - } - case DIV_SYSTEM_SAA1099: - if (ImGui::RadioButton("SAM Coupé (8MHz)",flags==0)) { - e->setSysFlags(i,0,restart); - updateWindowTitle(); - } - if (ImGui::RadioButton("NTSC (7.15MHz)",flags==1)) { - e->setSysFlags(i,1,restart); - updateWindowTitle(); - } - if (ImGui::RadioButton("PAL (7.09MHz)",flags==2)) { - e->setSysFlags(i,2,restart); - updateWindowTitle(); - } - break; - case DIV_SYSTEM_AMIGA: { - ImGui::Text("Stereo separation:"); - int stereoSep=(flags>>8)&127; - if (ImGui::SliderInt("##StereoSep",&stereoSep,0,127)) { - if (stereoSep<0) stereoSep=0; - if (stereoSep>127) stereoSep=127; - e->setSysFlags(i,(flags&(~0x7f00))|((stereoSep&127)<<8),restart); - updateWindowTitle(); - } rightClickable - if (ImGui::RadioButton("Amiga 500 (OCS)",(flags&2)==0)) { - e->setSysFlags(i,flags&(~2),restart); - } - if (ImGui::RadioButton("Amiga 1200 (AGA)",(flags&2)==2)) { - e->setSysFlags(i,(flags&(~2))|2,restart); - } - sysPal=flags&1; - if (ImGui::Checkbox("PAL",&sysPal)) { - e->setSysFlags(i,(flags&(~1))|sysPal,restart); - updateWindowTitle(); - } - bool bypassLimits=flags&4; - if (ImGui::Checkbox("Bypass frequency limits",&bypassLimits)) { - e->setSysFlags(i,(flags&(~4))|(bypassLimits<<2),restart); - updateWindowTitle(); - } - break; - } - case DIV_SYSTEM_PCSPKR: { - ImGui::Text("Speaker type:"); - if (ImGui::RadioButton("Unfiltered",(flags&3)==0)) { - e->setSysFlags(i,(flags&(~3))|0,restart); - updateWindowTitle(); - } - if (ImGui::RadioButton("Cone",(flags&3)==1)) { - e->setSysFlags(i,(flags&(~3))|1,restart); - updateWindowTitle(); - } - if (ImGui::RadioButton("Piezo",(flags&3)==2)) { - e->setSysFlags(i,(flags&(~3))|2,restart); - updateWindowTitle(); - } - if (ImGui::RadioButton("Use system beeper (Linux only!)",(flags&3)==3)) { - e->setSysFlags(i,(flags&(~3))|3,restart); - updateWindowTitle(); - } - break; - } - case DIV_SYSTEM_QSOUND: { - ImGui::Text("Echo delay:"); - int echoBufSize=2725 - (flags & 4095); - if (ImGui::SliderInt("##EchoBufSize",&echoBufSize,0,2725)) { - if (echoBufSize<0) echoBufSize=0; - if (echoBufSize>2725) echoBufSize=2725; - e->setSysFlags(i,(flags & ~4095) | ((2725 - echoBufSize) & 4095),restart); - updateWindowTitle(); - } rightClickable - ImGui::Text("Echo feedback:"); - int echoFeedback=(flags>>12)&255; - if (ImGui::SliderInt("##EchoFeedback",&echoFeedback,0,255)) { - if (echoFeedback<0) echoFeedback=0; - if (echoFeedback>255) echoFeedback=255; - e->setSysFlags(i,(flags & ~(255 << 12)) | ((echoFeedback & 255) << 12),restart); - updateWindowTitle(); - } rightClickable - break; - } - case DIV_SYSTEM_X1_010: { - ImGui::Text("Clock rate:"); - if (ImGui::RadioButton("16MHz (Seta 1)",(flags&15)==0)) { - e->setSysFlags(i,(flags&(~15))|0,restart); - updateWindowTitle(); - } - if (ImGui::RadioButton("16.67MHz (Seta 2)",(flags&15)==1)) { - e->setSysFlags(i,(flags&(~15))|1,restart); - updateWindowTitle(); - } - bool x1_010Stereo=flags&16; - if (ImGui::Checkbox("Stereo",&x1_010Stereo)) { - e->setSysFlags(i,(flags&(~16))|(x1_010Stereo<<4),restart); - updateWindowTitle(); - } - break; - } - case DIV_SYSTEM_GB: - case DIV_SYSTEM_SWAN: - case DIV_SYSTEM_VERA: - case DIV_SYSTEM_BUBSYS_WSG: - case DIV_SYSTEM_YM2610: - case DIV_SYSTEM_YM2610_EXT: - case DIV_SYSTEM_YM2610_FULL: - case DIV_SYSTEM_YM2610_FULL_EXT: - case DIV_SYSTEM_YM2610B: - case DIV_SYSTEM_YM2610B_EXT: - case DIV_SYSTEM_YMU759: - case DIV_SYSTEM_PET: - ImGui::Text("nothing to configure"); - break; - default: - if (ImGui::Checkbox("PAL",&sysPal)) { - e->setSysFlags(i,sysPal,restart); - updateWindowTitle(); - } - break; - } + drawSysConf(i); ImGui::TreePop(); } } @@ -5662,45 +1967,9 @@ bool FurnaceGUI::loop() { if (ImGui::BeginMenu("change system...")) { for (int i=0; isong.systemLen; i++) { if (ImGui::BeginMenu(fmt::sprintf("%d. %s##_SYSC%d",i+1,getSystemName(e->song.system[i]),i).c_str())) { - sysChangeOption(i,DIV_SYSTEM_YM2612); - sysChangeOption(i,DIV_SYSTEM_YM2612_EXT); - sysChangeOption(i,DIV_SYSTEM_SMS); - sysChangeOption(i,DIV_SYSTEM_GB); - sysChangeOption(i,DIV_SYSTEM_PCE); - sysChangeOption(i,DIV_SYSTEM_NES); - sysChangeOption(i,DIV_SYSTEM_C64_8580); - sysChangeOption(i,DIV_SYSTEM_C64_6581); - sysChangeOption(i,DIV_SYSTEM_YM2151); - sysChangeOption(i,DIV_SYSTEM_SEGAPCM); - sysChangeOption(i,DIV_SYSTEM_SEGAPCM_COMPAT); - sysChangeOption(i,DIV_SYSTEM_YM2610); - sysChangeOption(i,DIV_SYSTEM_YM2610_EXT); - sysChangeOption(i,DIV_SYSTEM_YM2610_FULL); - sysChangeOption(i,DIV_SYSTEM_YM2610_FULL_EXT); - sysChangeOption(i,DIV_SYSTEM_YM2610B); - sysChangeOption(i,DIV_SYSTEM_YM2610B_EXT); - sysChangeOption(i,DIV_SYSTEM_AY8910); - sysChangeOption(i,DIV_SYSTEM_AMIGA); - sysChangeOption(i,DIV_SYSTEM_PCSPKR); - sysChangeOption(i,DIV_SYSTEM_OPLL); - sysChangeOption(i,DIV_SYSTEM_OPLL_DRUMS); - sysChangeOption(i,DIV_SYSTEM_VRC7); - sysChangeOption(i,DIV_SYSTEM_OPL); - sysChangeOption(i,DIV_SYSTEM_OPL_DRUMS); - sysChangeOption(i,DIV_SYSTEM_OPL2); - sysChangeOption(i,DIV_SYSTEM_OPL2_DRUMS); - sysChangeOption(i,DIV_SYSTEM_OPL3); - sysChangeOption(i,DIV_SYSTEM_OPL3_DRUMS); - sysChangeOption(i,DIV_SYSTEM_TIA); - sysChangeOption(i,DIV_SYSTEM_SAA1099); - sysChangeOption(i,DIV_SYSTEM_AY8930); - sysChangeOption(i,DIV_SYSTEM_LYNX); - sysChangeOption(i,DIV_SYSTEM_QSOUND); - sysChangeOption(i,DIV_SYSTEM_X1_010); - sysChangeOption(i,DIV_SYSTEM_SWAN); - sysChangeOption(i,DIV_SYSTEM_VERA); - sysChangeOption(i,DIV_SYSTEM_BUBSYS_WSG); - sysChangeOption(i,DIV_SYSTEM_PET); + for (int j=0; availableSystems[j]; j++) { + sysChangeOption(i,(DivSystem)availableSystems[j]); + } ImGui::EndMenu(); } } @@ -6204,379 +2473,6 @@ bool FurnaceGUI::loop() { return false; } -void FurnaceGUI::parseKeybinds() { - actionMapGlobal.clear(); - actionMapPat.clear(); - actionMapInsList.clear(); - actionMapWaveList.clear(); - actionMapSampleList.clear(); - actionMapOrders.clear(); - - for (int i=GUI_ACTION_GLOBAL_MIN+1; igetConfInt(#target,ImGui::GetColorU32(def))); - -#ifdef _WIN32 -#define SYSTEM_FONT_PATH_1 "C:\\Windows\\Fonts\\segoeui.ttf" -#define SYSTEM_FONT_PATH_2 "C:\\Windows\\Fonts\\tahoma.ttf" -// TODO! -#define SYSTEM_FONT_PATH_3 "C:\\Windows\\Fonts\\tahoma.ttf" -// TODO! -#define SYSTEM_PAT_FONT_PATH_1 "C:\\Windows\\Fonts\\consola.ttf" -#define SYSTEM_PAT_FONT_PATH_2 "C:\\Windows\\Fonts\\cour.ttf" -// GOOD LUCK WITH THIS ONE - UNTESTED -#define SYSTEM_PAT_FONT_PATH_3 "C:\\Windows\\Fonts\\vgasys.fon" -#elif defined(__APPLE__) -#define SYSTEM_FONT_PATH_1 "/System/Library/Fonts/SFAANS.ttf" -#define SYSTEM_FONT_PATH_2 "/System/Library/Fonts/Helvetica.ttc" -#define SYSTEM_FONT_PATH_3 "/System/Library/Fonts/Helvetica.dfont" -#define SYSTEM_PAT_FONT_PATH_1 "/System/Library/Fonts/SFNSMono.ttf" -#define SYSTEM_PAT_FONT_PATH_2 "/System/Library/Fonts/Courier New.ttf" -#define SYSTEM_PAT_FONT_PATH_3 "/System/Library/Fonts/Courier New.ttf" -#else -#define SYSTEM_FONT_PATH_1 "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf" -#define SYSTEM_FONT_PATH_2 "/usr/share/fonts/TTF/DejaVuSans.ttf" -#define SYSTEM_FONT_PATH_3 "/usr/share/fonts/ubuntu/Ubuntu-R.ttf" -#define SYSTEM_PAT_FONT_PATH_1 "/usr/share/fonts/truetype/dejavu/DejaVuSansMono.ttf" -#define SYSTEM_PAT_FONT_PATH_2 "/usr/share/fonts/TTF/DejaVuSansMono.ttf" -#define SYSTEM_PAT_FONT_PATH_3 "/usr/share/fonts/ubuntu/UbuntuMono-R.ttf" -#endif - -void FurnaceGUI::applyUISettings() { - ImGuiStyle sty; - if (settings.guiColorsBase) { - ImGui::StyleColorsLight(&sty); - } else { - ImGui::StyleColorsDark(&sty); - } - - if (settings.dpiScale>=0.5f) dpiScale=settings.dpiScale; - - GET_UI_COLOR(GUI_COLOR_BACKGROUND,ImVec4(0.1f,0.1f,0.1f,1.0f)); - GET_UI_COLOR(GUI_COLOR_FRAME_BACKGROUND,ImVec4(0.0f,0.0f,0.0f,0.85f)); - GET_UI_COLOR(GUI_COLOR_MODAL_BACKDROP,ImVec4(0.0f,0.0f,0.0f,0.55f)); - GET_UI_COLOR(GUI_COLOR_HEADER,ImVec4(0.2f,0.2f,0.2f,1.0f)); - GET_UI_COLOR(GUI_COLOR_TEXT,ImVec4(1.0f,1.0f,1.0f,1.0f)); - GET_UI_COLOR(GUI_COLOR_ACCENT_PRIMARY,ImVec4(0.06f,0.53f,0.98f,1.0f)); - GET_UI_COLOR(GUI_COLOR_ACCENT_SECONDARY,ImVec4(0.26f,0.59f,0.98f,1.0f)); - GET_UI_COLOR(GUI_COLOR_EDITING,ImVec4(0.2f,0.1f,0.1f,1.0f)); - GET_UI_COLOR(GUI_COLOR_SONG_LOOP,ImVec4(0.3f,0.5f,0.8f,0.4f)); - GET_UI_COLOR(GUI_COLOR_VOLMETER_LOW,ImVec4(0.2f,0.6f,0.2f,1.0f)); - GET_UI_COLOR(GUI_COLOR_VOLMETER_HIGH,ImVec4(1.0f,0.9f,0.2f,1.0f)); - GET_UI_COLOR(GUI_COLOR_VOLMETER_PEAK,ImVec4(1.0f,0.1f,0.1f,1.0f)); - GET_UI_COLOR(GUI_COLOR_MACRO_VOLUME,ImVec4(0.2f,1.0f,0.0f,1.0f)); - GET_UI_COLOR(GUI_COLOR_MACRO_PITCH,ImVec4(1.0f,0.8f,0.0f,1.0f)); - GET_UI_COLOR(GUI_COLOR_MACRO_OTHER,ImVec4(0.0f,0.9f,1.0f,1.0f)); - GET_UI_COLOR(GUI_COLOR_MACRO_WAVE,ImVec4(1.0f,0.4f,0.0f,1.0f)); - GET_UI_COLOR(GUI_COLOR_INSTR_FM,ImVec4(0.6f,0.9f,1.0f,1.0f)); - GET_UI_COLOR(GUI_COLOR_INSTR_STD,ImVec4(0.6f,1.0f,0.5f,1.0f)); - GET_UI_COLOR(GUI_COLOR_INSTR_GB,ImVec4(1.0f,1.0f,0.5f,1.0f)); - GET_UI_COLOR(GUI_COLOR_INSTR_C64,ImVec4(0.85f,0.8f,1.0f,1.0f)); - GET_UI_COLOR(GUI_COLOR_INSTR_AMIGA,ImVec4(1.0f,0.5f,0.5f,1.0f)); - GET_UI_COLOR(GUI_COLOR_INSTR_PCE,ImVec4(1.0f,0.8f,0.5f,1.0f)); - GET_UI_COLOR(GUI_COLOR_INSTR_AY,ImVec4(1.0f,0.5f,1.0f,1.0f)); - GET_UI_COLOR(GUI_COLOR_INSTR_AY8930,ImVec4(0.7f,0.5f,1.0f,1.0f)); - GET_UI_COLOR(GUI_COLOR_INSTR_TIA,ImVec4(1.0f,0.6f,0.4f,1.0f)); - GET_UI_COLOR(GUI_COLOR_INSTR_SAA1099,ImVec4(0.3f,0.3f,1.0f,1.0f)); - GET_UI_COLOR(GUI_COLOR_INSTR_VIC,ImVec4(0.2f,1.0f,0.6f,1.0f)); - GET_UI_COLOR(GUI_COLOR_INSTR_PET,ImVec4(1.0f,1.0f,0.8f,1.0f)); - GET_UI_COLOR(GUI_COLOR_INSTR_VRC6,ImVec4(1.0f,0.9f,0.5f,1.0f)); - GET_UI_COLOR(GUI_COLOR_INSTR_OPLL,ImVec4(0.6f,0.7f,1.0f,1.0f)); - GET_UI_COLOR(GUI_COLOR_INSTR_OPL,ImVec4(0.3f,1.0f,0.9f,1.0f)); - GET_UI_COLOR(GUI_COLOR_INSTR_FDS,ImVec4(0.8f,0.5f,1.0f,1.0f)); - GET_UI_COLOR(GUI_COLOR_INSTR_VBOY,ImVec4(1.0f,0.1f,0.1f,1.0f)); - GET_UI_COLOR(GUI_COLOR_INSTR_N163,ImVec4(1.0f,0.4f,0.1f,1.0f)); - GET_UI_COLOR(GUI_COLOR_INSTR_SCC,ImVec4(0.7f,1.0f,0.3f,1.0f)); - GET_UI_COLOR(GUI_COLOR_INSTR_OPZ,ImVec4(0.2f,0.8f,1.0f,1.0f)); - GET_UI_COLOR(GUI_COLOR_INSTR_POKEY,ImVec4(0.5f,1.0f,0.3f,1.0f)); - GET_UI_COLOR(GUI_COLOR_INSTR_BEEPER,ImVec4(0.0f,1.0f,0.0f,1.0f)); - GET_UI_COLOR(GUI_COLOR_INSTR_SWAN,ImVec4(0.3f,0.5f,1.0f,1.0f)); - GET_UI_COLOR(GUI_COLOR_INSTR_MIKEY,ImVec4(0.5f,1.0f,0.3f,1.0f)); - GET_UI_COLOR(GUI_COLOR_INSTR_VERA,ImVec4(0.4f,0.6f,1.0f,1.0f)); - GET_UI_COLOR(GUI_COLOR_INSTR_X1_010,ImVec4(0.3f,0.5f,1.0f,1.0f)); - GET_UI_COLOR(GUI_COLOR_INSTR_UNKNOWN,ImVec4(0.3f,0.3f,0.3f,1.0f)); - GET_UI_COLOR(GUI_COLOR_CHANNEL_FM,ImVec4(0.2f,0.8f,1.0f,1.0f)); - GET_UI_COLOR(GUI_COLOR_CHANNEL_PULSE,ImVec4(0.4f,1.0f,0.2f,1.0f)); - GET_UI_COLOR(GUI_COLOR_CHANNEL_NOISE,ImVec4(0.8f,0.8f,0.8f,1.0f)); - GET_UI_COLOR(GUI_COLOR_CHANNEL_PCM,ImVec4(1.0f,0.9f,0.2f,1.0f)); - GET_UI_COLOR(GUI_COLOR_CHANNEL_WAVE,ImVec4(1.0f,0.5f,0.2f,1.0f)); - GET_UI_COLOR(GUI_COLOR_CHANNEL_OP,ImVec4(0.2f,0.4f,1.0f,1.0f)); - GET_UI_COLOR(GUI_COLOR_CHANNEL_MUTED,ImVec4(0.5f,0.5f,0.5f,1.0f)); - GET_UI_COLOR(GUI_COLOR_PATTERN_CURSOR,ImVec4(0.1f,0.3f,0.5f,1.0f)); - GET_UI_COLOR(GUI_COLOR_PATTERN_CURSOR_HOVER,ImVec4(0.2f,0.4f,0.6f,1.0f)); - GET_UI_COLOR(GUI_COLOR_PATTERN_CURSOR_ACTIVE,ImVec4(0.2f,0.5f,0.7f,1.0f)); - GET_UI_COLOR(GUI_COLOR_PATTERN_SELECTION,ImVec4(0.15f,0.15f,0.2f,1.0f)); - GET_UI_COLOR(GUI_COLOR_PATTERN_SELECTION_HOVER,ImVec4(0.2f,0.2f,0.3f,1.0f)); - GET_UI_COLOR(GUI_COLOR_PATTERN_SELECTION_ACTIVE,ImVec4(0.4f,0.4f,0.5f,1.0f)); - GET_UI_COLOR(GUI_COLOR_PATTERN_HI_1,ImVec4(0.6f,0.6f,0.6f,0.2f)); - GET_UI_COLOR(GUI_COLOR_PATTERN_HI_2,ImVec4(0.5f,0.8f,1.0f,0.2f)); - GET_UI_COLOR(GUI_COLOR_PATTERN_ROW_INDEX,ImVec4(0.5f,0.8f,1.0f,1.0f)); - GET_UI_COLOR(GUI_COLOR_PATTERN_ACTIVE,ImVec4(1.0f,1.0f,1.0f,1.0f)); - GET_UI_COLOR(GUI_COLOR_PATTERN_INACTIVE,ImVec4(0.5f,0.5f,0.5f,1.0f)); - GET_UI_COLOR(GUI_COLOR_PATTERN_INS,ImVec4(0.4f,0.7f,1.0f,1.0f)); - GET_UI_COLOR(GUI_COLOR_PATTERN_VOLUME_MIN,ImVec4(0.0f,0.5f,0.0f,1.0f)); - GET_UI_COLOR(GUI_COLOR_PATTERN_VOLUME_HALF,ImVec4(0.0f,0.75f,0.0f,1.0f)); - GET_UI_COLOR(GUI_COLOR_PATTERN_VOLUME_MAX,ImVec4(0.0f,1.0f,0.0f,1.0f)); - GET_UI_COLOR(GUI_COLOR_PATTERN_EFFECT_INVALID,ImVec4(1.0f,0.0f,0.0f,1.0f)); - GET_UI_COLOR(GUI_COLOR_PATTERN_EFFECT_PITCH,ImVec4(1.0f,1.0f,0.0f,1.0f)); - GET_UI_COLOR(GUI_COLOR_PATTERN_EFFECT_VOLUME,ImVec4(0.0f,1.0f,0.0f,1.0f)); - GET_UI_COLOR(GUI_COLOR_PATTERN_EFFECT_PANNING,ImVec4(0.0f,1.0f,1.0f,1.0f)); - GET_UI_COLOR(GUI_COLOR_PATTERN_EFFECT_SONG,ImVec4(1.0f,0.0f,0.0f,1.0f)); - GET_UI_COLOR(GUI_COLOR_PATTERN_EFFECT_TIME,ImVec4(0.5f,0.0f,1.0f,1.0f)); - GET_UI_COLOR(GUI_COLOR_PATTERN_EFFECT_SPEED,ImVec4(1.0f,0.0f,1.0f,1.0f)); - GET_UI_COLOR(GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY,ImVec4(0.5f,1.0f,0.0f,1.0f)); - GET_UI_COLOR(GUI_COLOR_PATTERN_EFFECT_SYS_SECONDARY,ImVec4(0.0f,1.0f,0.5f,1.0f)); - GET_UI_COLOR(GUI_COLOR_PATTERN_EFFECT_MISC,ImVec4(0.3f,0.3f,1.0f,1.0f)); - GET_UI_COLOR(GUI_COLOR_EE_VALUE,ImVec4(0.0f,1.0f,1.0f,1.0f)); - GET_UI_COLOR(GUI_COLOR_PLAYBACK_STAT,ImVec4(0.6f,0.6f,0.6f,1.0f)); - - for (int i=0; i<64; i++) { - ImVec4 col1=uiColors[GUI_COLOR_PATTERN_VOLUME_MIN]; - ImVec4 col2=uiColors[GUI_COLOR_PATTERN_VOLUME_HALF]; - ImVec4 col3=uiColors[GUI_COLOR_PATTERN_VOLUME_MAX]; - volColors[i]=ImVec4(col1.x+((col2.x-col1.x)*float(i)/64.0f), - col1.y+((col2.y-col1.y)*float(i)/64.0f), - col1.z+((col2.z-col1.z)*float(i)/64.0f), - 1.0f); - volColors[i+64]=ImVec4(col2.x+((col3.x-col2.x)*float(i)/64.0f), - col2.y+((col3.y-col2.y)*float(i)/64.0f), - col2.z+((col3.z-col2.z)*float(i)/64.0f), - 1.0f); - } - - float hue, sat, val; - - ImVec4 primaryActive=uiColors[GUI_COLOR_ACCENT_PRIMARY]; - ImVec4 primaryHover, primary; - primaryHover.w=primaryActive.w; - primary.w=primaryActive.w; - ImGui::ColorConvertRGBtoHSV(primaryActive.x,primaryActive.y,primaryActive.z,hue,sat,val); - if (settings.guiColorsBase) { - primary=primaryActive; - ImGui::ColorConvertHSVtoRGB(hue,sat*0.9,val*0.9,primaryHover.x,primaryHover.y,primaryHover.z); - ImGui::ColorConvertHSVtoRGB(hue,sat,val*0.5,primaryActive.x,primaryActive.y,primaryActive.z); - } else { - ImGui::ColorConvertHSVtoRGB(hue,sat*0.9,val*0.5,primaryHover.x,primaryHover.y,primaryHover.z); - ImGui::ColorConvertHSVtoRGB(hue,sat*0.8,val*0.35,primary.x,primary.y,primary.z); - } - - ImVec4 secondaryActive=uiColors[GUI_COLOR_ACCENT_SECONDARY]; - ImVec4 secondaryHover, secondary, secondarySemiActive; - secondarySemiActive.w=secondaryActive.w; - secondaryHover.w=secondaryActive.w; - secondary.w=secondaryActive.w; - ImGui::ColorConvertRGBtoHSV(secondaryActive.x,secondaryActive.y,secondaryActive.z,hue,sat,val); - if (settings.guiColorsBase) { - secondary=secondaryActive; - ImGui::ColorConvertHSVtoRGB(hue,sat*0.9,val*0.7,secondarySemiActive.x,secondarySemiActive.y,secondarySemiActive.z); - ImGui::ColorConvertHSVtoRGB(hue,sat*0.9,val*0.9,secondaryHover.x,secondaryHover.y,secondaryHover.z); - ImGui::ColorConvertHSVtoRGB(hue,sat,val*0.5,secondaryActive.x,secondaryActive.y,secondaryActive.z); - } else { - ImGui::ColorConvertHSVtoRGB(hue,sat*0.9,val*0.75,secondarySemiActive.x,secondarySemiActive.y,secondarySemiActive.z); - ImGui::ColorConvertHSVtoRGB(hue,sat*0.9,val*0.5,secondaryHover.x,secondaryHover.y,secondaryHover.z); - ImGui::ColorConvertHSVtoRGB(hue,sat*0.9,val*0.25,secondary.x,secondary.y,secondary.z); - } - - - sty.Colors[ImGuiCol_WindowBg]=uiColors[GUI_COLOR_FRAME_BACKGROUND]; - sty.Colors[ImGuiCol_ModalWindowDimBg]=uiColors[GUI_COLOR_MODAL_BACKDROP]; - sty.Colors[ImGuiCol_Text]=uiColors[GUI_COLOR_TEXT]; - - sty.Colors[ImGuiCol_Button]=primary; - sty.Colors[ImGuiCol_ButtonHovered]=primaryHover; - sty.Colors[ImGuiCol_ButtonActive]=primaryActive; - sty.Colors[ImGuiCol_Tab]=primary; - sty.Colors[ImGuiCol_TabHovered]=secondaryHover; - sty.Colors[ImGuiCol_TabActive]=secondarySemiActive; - sty.Colors[ImGuiCol_TabUnfocused]=primary; - sty.Colors[ImGuiCol_TabUnfocusedActive]=primaryHover; - sty.Colors[ImGuiCol_Header]=secondary; - sty.Colors[ImGuiCol_HeaderHovered]=secondaryHover; - sty.Colors[ImGuiCol_HeaderActive]=secondaryActive; - sty.Colors[ImGuiCol_ResizeGrip]=secondary; - sty.Colors[ImGuiCol_ResizeGripHovered]=secondaryHover; - sty.Colors[ImGuiCol_ResizeGripActive]=secondaryActive; - sty.Colors[ImGuiCol_FrameBg]=secondary; - sty.Colors[ImGuiCol_FrameBgHovered]=secondaryHover; - sty.Colors[ImGuiCol_FrameBgActive]=secondaryActive; - sty.Colors[ImGuiCol_SliderGrab]=primaryActive; - sty.Colors[ImGuiCol_SliderGrabActive]=primaryActive; - sty.Colors[ImGuiCol_TitleBgActive]=primary; - sty.Colors[ImGuiCol_CheckMark]=primaryActive; - sty.Colors[ImGuiCol_TextSelectedBg]=secondaryHover; - sty.Colors[ImGuiCol_PlotHistogram]=uiColors[GUI_COLOR_MACRO_OTHER]; - sty.Colors[ImGuiCol_PlotHistogramHovered]=uiColors[GUI_COLOR_MACRO_OTHER]; - - if (settings.roundedWindows) sty.WindowRounding=8.0f; - if (settings.roundedButtons) { - sty.FrameRounding=6.0f; - sty.GrabRounding=6.0f; - } - if (settings.roundedMenus) sty.PopupRounding=8.0f; - - sty.ScaleAllSizes(dpiScale); - - ImGui::GetStyle()=sty; - - for (int i=0; i<256; i++) { - ImVec4& base=uiColors[GUI_COLOR_PATTERN_EFFECT_PITCH]; - pitchGrad[i]=ImGui::GetColorU32(ImVec4(base.x,base.y,base.z,((float)i/255.0f)*base.w)); - } - for (int i=0; i<256; i++) { - ImVec4& base=uiColors[GUI_COLOR_PATTERN_ACTIVE]; - noteGrad[i]=ImGui::GetColorU32(ImVec4(base.x,base.y,base.z,((float)i/255.0f)*base.w)); - } - for (int i=0; i<256; i++) { - ImVec4& base=uiColors[GUI_COLOR_PATTERN_EFFECT_PANNING]; - panGrad[i]=ImGui::GetColorU32(ImVec4(base.x,base.y,base.z,((float)i/255.0f)*base.w)); - } - for (int i=0; i<256; i++) { - ImVec4& base=uiColors[GUI_COLOR_PATTERN_INS]; - insGrad[i]=ImGui::GetColorU32(ImVec4(base.x,base.y,base.z,((float)i/255.0f)*base.w)); - } - for (int i=0; i<256; i++) { - ImVec4& base=volColors[i/2]; - volGrad[i]=ImGui::GetColorU32(ImVec4(base.x,base.y,base.z,((float)i/255.0f)*base.w)); - } - for (int i=0; i<256; i++) { - ImVec4& base=uiColors[GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY]; - sysCmd1Grad[i]=ImGui::GetColorU32(ImVec4(base.x,base.y,base.z,((float)i/255.0f)*base.w)); - } - for (int i=0; i<256; i++) { - ImVec4& base=uiColors[GUI_COLOR_PATTERN_EFFECT_SYS_SECONDARY]; - sysCmd2Grad[i]=ImGui::GetColorU32(ImVec4(base.x,base.y,base.z,((float)i/255.0f)*base.w)); - } - - // set to 800 for now due to problems with unifont - static const ImWchar loadEverything[]={0x20,0x800,0}; - - if (settings.mainFont<0 || settings.mainFont>6) settings.mainFont=0; - if (settings.patFont<0 || settings.patFont>6) settings.patFont=0; - - if (settings.mainFont==6 && settings.mainFontPath.empty()) { - logW("UI font path is empty! reverting to default font\n"); - settings.mainFont=0; - } - if (settings.patFont==6 && settings.patFontPath.empty()) { - logW("pattern font path is empty! reverting to default font\n"); - settings.patFont=0; - } - - if (settings.mainFont==6) { // custom font - if ((mainFont=ImGui::GetIO().Fonts->AddFontFromFileTTF(settings.mainFontPath.c_str(),e->getConfInt("mainFontSize",18)*dpiScale,NULL,loadEverything))==NULL) { - logW("could not load UI font! reverting to default font\n"); - settings.mainFont=0; - if ((mainFont=ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF(builtinFont[settings.mainFont],builtinFontLen[settings.mainFont],e->getConfInt("mainFontSize",18)*dpiScale,NULL,loadEverything))==NULL) { - logE("could not load UI font! falling back to Proggy Clean.\n"); - mainFont=ImGui::GetIO().Fonts->AddFontDefault(); - } - } - } else if (settings.mainFont==5) { // system font - if ((mainFont=ImGui::GetIO().Fonts->AddFontFromFileTTF(SYSTEM_FONT_PATH_1,e->getConfInt("mainFontSize",18)*dpiScale,NULL,loadEverything))==NULL) { - if ((mainFont=ImGui::GetIO().Fonts->AddFontFromFileTTF(SYSTEM_FONT_PATH_2,e->getConfInt("mainFontSize",18)*dpiScale,NULL,loadEverything))==NULL) { - if ((mainFont=ImGui::GetIO().Fonts->AddFontFromFileTTF(SYSTEM_FONT_PATH_3,e->getConfInt("mainFontSize",18)*dpiScale,NULL,loadEverything))==NULL) { - logW("could not load UI font! reverting to default font\n"); - settings.mainFont=0; - if ((mainFont=ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF(builtinFont[settings.mainFont],builtinFontLen[settings.mainFont],e->getConfInt("mainFontSize",18)*dpiScale,NULL,loadEverything))==NULL) { - logE("could not load UI font! falling back to Proggy Clean.\n"); - mainFont=ImGui::GetIO().Fonts->AddFontDefault(); - } - } - } - } - } else { - if ((mainFont=ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF(builtinFont[settings.mainFont],builtinFontLen[settings.mainFont],e->getConfInt("mainFontSize",18)*dpiScale,NULL,loadEverything))==NULL) { - logE("could not load UI font! falling back to Proggy Clean.\n"); - mainFont=ImGui::GetIO().Fonts->AddFontDefault(); - } - } - - ImFontConfig fc; - fc.MergeMode=true; - fc.GlyphMinAdvanceX=e->getConfInt("iconSize",16)*dpiScale; - static const ImWchar fontRange[]={ICON_MIN_FA,ICON_MAX_FA,0}; - if ((iconFont=ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF(iconFont_compressed_data,iconFont_compressed_size,e->getConfInt("iconSize",16)*dpiScale,&fc,fontRange))==NULL) { - logE("could not load icon font!\n"); - } - if (settings.mainFontSize==settings.patFontSize && settings.patFont<5 && builtinFontM[settings.patFont]==builtinFont[settings.mainFont]) { - logD("using main font for pat font.\n"); - patFont=mainFont; - } else { - if (settings.patFont==6) { // custom font - if ((patFont=ImGui::GetIO().Fonts->AddFontFromFileTTF(settings.patFontPath.c_str(),e->getConfInt("patFontSize",18)*dpiScale,NULL,loadEverything))==NULL) { - logW("could not load pattern font! reverting to default font\n"); - settings.patFont=0; - if ((patFont=ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF(builtinFontM[settings.patFont],builtinFontMLen[settings.patFont],e->getConfInt("patFontSize",18)*dpiScale,NULL,loadEverything))==NULL) { - logE("could not load pattern font! falling back to Proggy Clean.\n"); - patFont=ImGui::GetIO().Fonts->AddFontDefault(); - } - } - } else if (settings.patFont==5) { // system font - if ((patFont=ImGui::GetIO().Fonts->AddFontFromFileTTF(SYSTEM_PAT_FONT_PATH_1,e->getConfInt("patFontSize",18)*dpiScale,NULL,loadEverything))==NULL) { - if ((patFont=ImGui::GetIO().Fonts->AddFontFromFileTTF(SYSTEM_PAT_FONT_PATH_2,e->getConfInt("patFontSize",18)*dpiScale,NULL,loadEverything))==NULL) { - if ((patFont=ImGui::GetIO().Fonts->AddFontFromFileTTF(SYSTEM_PAT_FONT_PATH_3,e->getConfInt("patFontSize",18)*dpiScale,NULL,loadEverything))==NULL) { - logW("could not load pattern font! reverting to default font\n"); - settings.patFont=0; - if ((patFont=ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF(builtinFontM[settings.patFont],builtinFontMLen[settings.patFont],e->getConfInt("patFontSize",18)*dpiScale,NULL,loadEverything))==NULL) { - logE("could not load pattern font! falling back to Proggy Clean.\n"); - patFont=ImGui::GetIO().Fonts->AddFontDefault(); - } - } - } - } - } else { - if ((patFont=ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF(builtinFontM[settings.patFont],builtinFontMLen[settings.patFont],e->getConfInt("patFontSize",18)*dpiScale,NULL,loadEverything))==NULL) { - logE("could not load pattern font!\n"); - patFont=ImGui::GetIO().Fonts->AddFontDefault(); - } - } - } - if ((bigFont=ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF(font_plexSans_compressed_data,font_plexSans_compressed_size,40*dpiScale))==NULL) { - logE("could not load big UI font!\n"); - } - - if (fileDialog!=NULL) delete fileDialog; - fileDialog=new FurnaceGUIFileDialog(settings.sysFileDialog); -} - bool FurnaceGUI::init() { #ifndef __APPLE__ float dpiScaleF; @@ -6707,28 +2603,6 @@ bool FurnaceGUI::init() { ImGui::GetIO().IniFilename=finalLayoutPath; ImGui::LoadIniSettingsFromDisk(finalLayoutPath); - // TODO: allow changing these colors. - ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByTypeDir,"",ImVec4(0.0f,1.0f,1.0f,1.0f),ICON_FA_FOLDER_O); - ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByTypeFile,"",ImVec4(0.7f,0.7f,0.7f,1.0f),ICON_FA_FILE_O); - ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".fur",ImVec4(0.5f,1.0f,0.5f,1.0f),ICON_FA_FILE); - ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".fui",ImVec4(1.0f,0.5f,0.5f,1.0f),ICON_FA_FILE); - ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".fuw",ImVec4(1.0f,0.75f,0.5f,1.0f),ICON_FA_FILE); - ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".dmf",ImVec4(0.5f,1.0f,0.5f,1.0f),ICON_FA_FILE); - ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".dmp",ImVec4(1.0f,0.5f,0.5f,1.0f),ICON_FA_FILE); - ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".dmw",ImVec4(1.0f,0.75f,0.5f,1.0f),ICON_FA_FILE); - ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".wav",ImVec4(1.0f,1.0f,0.5f,1.0f),ICON_FA_FILE_AUDIO_O); - ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".vgm",ImVec4(1.0f,1.0f,0.5f,1.0f),ICON_FA_FILE_AUDIO_O); - ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".ttf",ImVec4(0.3f,1.0f,0.6f,1.0f),ICON_FA_FONT); - ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".otf",ImVec4(0.3f,1.0f,0.6f,1.0f),ICON_FA_FONT); - ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".ttc",ImVec4(0.3f,1.0f,0.6f,1.0f),ICON_FA_FONT); - - ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".tfi",ImVec4(1.0f,0.5f,0.5f,1.0f),ICON_FA_FILE); - ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".vgi",ImVec4(1.0f,0.5f,0.5f,1.0f),ICON_FA_FILE); - ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".s3i",ImVec4(1.0f,0.5f,0.5f,1.0f),ICON_FA_FILE); - ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".sbi",ImVec4(1.0f,0.5f,0.5f,1.0f),ICON_FA_FILE); - ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".fti",ImVec4(1.0f,0.5f,0.5f,1.0f),ICON_FA_FILE); - ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".bti",ImVec4(1.0f,0.5f,0.5f,1.0f),ICON_FA_FILE); - updateWindowTitle(); for (int i=0; isong.masterVol,0,3,"%.2fx")) { + if (e->song.masterVol<0) e->song.masterVol=0; + if (e->song.masterVol>3) e->song.masterVol=3; + } rightClickable + for (int i=0; isong.systemLen; i++) { + snprintf(id,31,"MixS%d",i); + bool doInvert=e->song.systemVol[i]&128; + signed char vol=e->song.systemVol[i]&127; + ImGui::PushID(id); + ImGui::Text("%d. %s",i+1,getSystemName(e->song.system[i])); + ImGui::SameLine(ImGui::GetWindowWidth()-(82.0f*dpiScale)); + if (ImGui::Checkbox("Invert",&doInvert)) { + e->song.systemVol[i]^=128; + } + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x-(50.0f*dpiScale)); + if (ImGui::SliderScalar("Volume",ImGuiDataType_S8,&vol,&_ZERO,&_ONE_HUNDRED_TWENTY_SEVEN)) { + e->song.systemVol[i]=(e->song.systemVol[i]&128)|vol; + } rightClickable + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x-(50.0f*dpiScale)); + ImGui::SliderScalar("Panning",ImGuiDataType_S8,&e->song.systemPan[i],&_MINUS_ONE_HUNDRED_TWENTY_SEVEN,&_ONE_HUNDRED_TWENTY_SEVEN); rightClickable + + ImGui::PopID(); + } + } + if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_MIXER; + ImGui::End(); +} \ No newline at end of file diff --git a/src/gui/newSong.cpp b/src/gui/newSong.cpp new file mode 100644 index 000000000..d6188b3e5 --- /dev/null +++ b/src/gui/newSong.cpp @@ -0,0 +1,90 @@ +/** + * 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" + +void FurnaceGUI::drawNewSong() { + bool accepted=false; + + ImGui::PushFont(bigFont); + ImGui::SetCursorPosX((ImGui::GetContentRegionAvail().x-ImGui::CalcTextSize("Choose a System!").x)*0.5); + ImGui::Text("Choose a System!"); + ImGui::PopFont(); + + if (ImGui::BeginTable("sysPicker",2)) { + 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"); + + ImGui::TableNextRow(); + + // CATEGORIES + ImGui::TableNextColumn(); + int index=0; + for (FurnaceGUISysCategory& i: sysCategories) { + if (ImGui::Selectable(i.name,newSongCategory==index,ImGuiSelectableFlags_DontClosePopups)) { \ + newSongCategory=index; + } + index++; + } + + // SYSTEMS + ImGui::TableNextColumn(); + if (ImGui::BeginTable("Systems",1,ImGuiTableFlags_BordersInnerV|ImGuiTableFlags_ScrollY)) { + for (FurnaceGUISysDef& i: sysCategories[newSongCategory].systems) { + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + if (ImGui::Selectable(i.name,false,ImGuiSelectableFlags_DontClosePopups)) { + nextDesc=i.definition.data(); + accepted=true; + } + } + ImGui::EndTable(); + } + + ImGui::EndTable(); + } + + if (ImGui::Button("Cancel")) { + ImGui::CloseCurrentPopup(); + } + + if (accepted) { + e->createNew(nextDesc); + undoHist.clear(); + redoHist.clear(); + curFileName=""; + modified=false; + curNibble=false; + orderNibble=false; + orderCursor=-1; + samplePos=0; + updateSampleTex=true; + selStart=SelectionPoint(); + selEnd=SelectionPoint(); + cursor=SelectionPoint(); + updateWindowTitle(); + ImGui::CloseCurrentPopup(); + } +} \ No newline at end of file diff --git a/src/gui/osc.cpp b/src/gui/osc.cpp new file mode 100644 index 000000000..0bdcdf8a0 --- /dev/null +++ b/src/gui/osc.cpp @@ -0,0 +1,47 @@ +/** + * 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" + +void FurnaceGUI::drawOsc() { + if (nextWindow==GUI_WINDOW_OSCILLOSCOPE) { + oscOpen=true; + ImGui::SetNextWindowFocus(); + nextWindow=GUI_WINDOW_NOTHING; + } + if (!oscOpen) return; + ImGui::SetNextWindowSizeConstraints(ImVec2(64.0f*dpiScale,32.0f*dpiScale),ImVec2(scrW*dpiScale,scrH*dpiScale)); + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding,ImVec2(0,0)); + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing,ImVec2(0,0)); + ImGui::PushStyleVar(ImGuiStyleVar_ItemInnerSpacing,ImVec2(0,0)); + if (ImGui::Begin("Oscilloscope",&oscOpen)) { + float values[512]; + for (int i=0; i<512; i++) { + int pos=i*e->oscSize/512; + values[i]=(e->oscBuf[0][pos]+e->oscBuf[1][pos])*0.5f; + } + //ImGui::SetCursorPos(ImVec2(0,0)); + ImGui::BeginDisabled(); + ImGui::PlotLines("##SingleOsc",values,512,0,NULL,-1.0f,1.0f,ImGui::GetContentRegionAvail()); + ImGui::EndDisabled(); + } + ImGui::PopStyleVar(3); + if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_OSCILLOSCOPE; + ImGui::End(); +} \ No newline at end of file diff --git a/src/gui/piano.cpp b/src/gui/piano.cpp new file mode 100644 index 000000000..d3a3d35ef --- /dev/null +++ b/src/gui/piano.cpp @@ -0,0 +1,46 @@ +/** + * 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 "guiConst.h" + +void FurnaceGUI::drawPiano() { + if (nextWindow==GUI_WINDOW_PIANO) { + pianoOpen=true; + ImGui::SetNextWindowFocus(); + nextWindow=GUI_WINDOW_NOTHING; + } + if (!pianoOpen) return; + if (ImGui::Begin("Piano",&pianoOpen)) { + for (int i=0; igetTotalChannelCount(); i++) { + DivChannelState* cs=e->getChanState(i); + if (cs->keyOn) { + const char* noteName=NULL; + if (cs->note<-60 || cs->note>120) { + noteName="???"; + } else { + noteName=noteNames[cs->note+60]; + } + ImGui::Text("%d: %s",i,noteName); + } + } + } + if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_PIANO; + ImGui::End(); +} \ No newline at end of file diff --git a/src/gui/presets.cpp b/src/gui/presets.cpp new file mode 100644 index 000000000..0ab7c2ba6 --- /dev/null +++ b/src/gui/presets.cpp @@ -0,0 +1,639 @@ +/** + * 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" + +// add system configurations here. +// every entry is written in the following format: +// cat.systems.push_back(FurnaceGUISysDef( +// "System Name", { +// DIV_SYSTEM_???, Volume, Panning, Flags, +// DIV_SYSTEM_???, Volume, Panning, Flags, +// ... +// 0 +// } +// )); + +void FurnaceGUI::initSystemPresets() { + FurnaceGUISysCategory cat; + + cat=FurnaceGUISysCategory("FM"); + cat.systems.push_back(FurnaceGUISysDef( + "Yamaha YM2612", { + DIV_SYSTEM_YM2612, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Yamaha YM2612 (extended channel 3)", { + DIV_SYSTEM_YM2612_EXT, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Yamaha YM2151", { + DIV_SYSTEM_YM2151, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Yamaha YM2610", { + DIV_SYSTEM_YM2610_FULL, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Yamaha YM2610 (extended channel 2)", { + DIV_SYSTEM_YM2610_FULL_EXT, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Yamaha YM2610B", { + DIV_SYSTEM_YM2610B, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Yamaha YM2610B (extended channel 3)", { + DIV_SYSTEM_YM2610B_EXT, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Yamaha YM2413", { + DIV_SYSTEM_OPLL, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Yamaha YM2413 (drums mode)", { + DIV_SYSTEM_OPLL_DRUMS, 64, 0, 0, + 0 + } + )); + sysCategories.push_back(cat); + + cat=FurnaceGUISysCategory("Square"); + cat.systems.push_back(FurnaceGUISysDef( + "TI SN76489", { + DIV_SYSTEM_SMS, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "AY-3-8910", { + DIV_SYSTEM_AY8910, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Philips SAA1099", { + DIV_SYSTEM_SAA1099, 64, 0, 0, + 0 + } + )); + sysCategories.push_back(cat); + + cat=FurnaceGUISysCategory("Sample"); + cat.systems.push_back(FurnaceGUISysDef( + "Amiga", { + DIV_SYSTEM_AMIGA, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "SegaPCM", { + DIV_SYSTEM_SEGAPCM, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Capcom QSound", { + DIV_SYSTEM_QSOUND, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Seta/Allumer X1-010", { + DIV_SYSTEM_X1_010, 64, 0, 0, + 0 + } + )); + sysCategories.push_back(cat); + + cat=FurnaceGUISysCategory("Game consoles"); + cat.systems.push_back(FurnaceGUISysDef( + "Sega Genesis", { + DIV_SYSTEM_YM2612, 64, 0, 0, + DIV_SYSTEM_SMS, 24, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Sega Genesis (extended channel 3)", { + DIV_SYSTEM_YM2612_EXT, 64, 0, 0, + DIV_SYSTEM_SMS, 24, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Sega Master System", { + DIV_SYSTEM_SMS, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Sega Master System (with FM expansion)", { + DIV_SYSTEM_SMS, 64, 0, 0, + DIV_SYSTEM_OPLL, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Sega Master System (with FM expansion in drums mode)", { + DIV_SYSTEM_SMS, 64, 0, 0, + DIV_SYSTEM_OPLL_DRUMS, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Game Boy", { + DIV_SYSTEM_GB, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "NEC PC Engine/TurboGrafx-16", { + DIV_SYSTEM_PCE, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "NES", { + DIV_SYSTEM_NES, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "NES with Konami VRC7", { + DIV_SYSTEM_NES, 64, 0, 0, + DIV_SYSTEM_VRC7, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "NES with Sunsoft 5B", { + DIV_SYSTEM_NES, 64, 0, 0, + DIV_SYSTEM_AY8910, 64, 0, 38, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Mattel Intellivision", { + DIV_SYSTEM_AY8910, 64, 0, 48, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Vectrex", { + DIV_SYSTEM_AY8910, 64, 0, 4, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Neo Geo AES", { + DIV_SYSTEM_YM2610_FULL, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Neo Geo AES (extended channel 2)", { + DIV_SYSTEM_YM2610_FULL_EXT, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Atari 2600/7800", { + DIV_SYSTEM_TIA, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Atari Lynx", { + DIV_SYSTEM_LYNX, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "WonderSwan", { + DIV_SYSTEM_SWAN, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Gamate", { + DIV_SYSTEM_AY8910, 64, 0, 73, + 0 + } + )); + sysCategories.push_back(cat); + + cat=FurnaceGUISysCategory("Computers"); + cat.systems.push_back(FurnaceGUISysDef( + "Commodore PET", { + DIV_SYSTEM_PET, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Commodore VIC-20", { + DIV_SYSTEM_VIC20, 64, 0, 1, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Commodore 64 (6581 SID)", { + DIV_SYSTEM_C64_6581, 64, 0, 1, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Commodore 64 (8580 SID)", { + DIV_SYSTEM_C64_8580, 64, 0, 1, + 0 + } + )); + /* + cat.systems.push_back(FurnaceGUISysDef( + "Commodore 64 (6581 SID + Sound Expander)", { + DIV_SYSTEM_OPL, 64, 0, 0, + DIV_SYSTEM_C64_6581, 64, 0, 1, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Commodore 64 (6581 SID + Sound Expander with drums mode)", { + DIV_SYSTEM_OPL_DRUMS, 64, 0, 0, + DIV_SYSTEM_C64_6581, 64, 0, 1, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Commodore 64 (8580 SID + Sound Expander)", { + DIV_SYSTEM_OPL, 64, 0, 0, + DIV_SYSTEM_C64_8580, 64, 0, 1, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Commodore 64 (8580 SID + Sound Expander with drums mode)", { + DIV_SYSTEM_OPL_DRUMS, 64, 0, 0, + DIV_SYSTEM_C64_8580, 64, 0, 1, + 0 + } + ));*/ + cat.systems.push_back(FurnaceGUISysDef( + "Amiga", { + DIV_SYSTEM_AMIGA, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "MSX", { + DIV_SYSTEM_AY8910, 64, 0, 16, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "MSX + SFG-01", { + DIV_SYSTEM_YM2151, 64, 0, 0, + DIV_SYSTEM_AY8910, 64, 0, 16, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "MSX + MSX-MUSIC", { + DIV_SYSTEM_OPLL, 64, 0, 0, + DIV_SYSTEM_AY8910, 64, 0, 16, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "MSX + MSX-MUSIC (drums mode)", { + DIV_SYSTEM_OPLL_DRUMS, 64, 0, 0, + DIV_SYSTEM_AY8910, 64, 0, 16, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "ZX Spectrum (48K)", { + DIV_SYSTEM_AY8910, 64, 0, 2, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "ZX Spectrum (128K)", { + DIV_SYSTEM_AY8910, 64, 0, 1, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Amstrad CPC", { + DIV_SYSTEM_AY8910, 64, 0, 5, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "SAM Coupé", { + DIV_SYSTEM_SAA1099, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "BBC Micro", { + DIV_SYSTEM_SMS, 64, 0, 6, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "PC (barebones)", { + DIV_SYSTEM_PCSPKR, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "PC + Covox Sound Master", { + DIV_SYSTEM_AY8930, 64, 0, 3, + DIV_SYSTEM_PCSPKR, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "PC + SSI 2001", { + DIV_SYSTEM_C64_6581, 64, 0, 2, + DIV_SYSTEM_PCSPKR, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "PC + Game Blaster", { + DIV_SYSTEM_SAA1099, 64, -127, 1, + DIV_SYSTEM_SAA1099, 64, 127, 1, + DIV_SYSTEM_PCSPKR, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "PC + AdLib/Sound Blaster", { + DIV_SYSTEM_OPL2, 64, 0, 0, + DIV_SYSTEM_PCSPKR, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "PC + AdLib/Sound Blaster (drums mode)", { + DIV_SYSTEM_OPL2_DRUMS, 64, 0, 0, + DIV_SYSTEM_PCSPKR, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "PC + Sound Blaster w/Game Blaster Compatible", { + DIV_SYSTEM_OPL2, 64, 0, 0, + DIV_SYSTEM_SAA1099, 64, -127, 1, + DIV_SYSTEM_SAA1099, 64, 127, 1, + DIV_SYSTEM_PCSPKR, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "PC + Sound Blaster w/Game Blaster Compatible (drums mode)", { + DIV_SYSTEM_OPL2_DRUMS, 64, 0, 0, + DIV_SYSTEM_SAA1099, 64, -127, 1, + DIV_SYSTEM_SAA1099, 64, 127, 1, + DIV_SYSTEM_PCSPKR, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "PC + Sound Blaster Pro 2", { + DIV_SYSTEM_OPL3, 64, 0, 0, + DIV_SYSTEM_PCSPKR, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "PC + Sound Blaster Pro 2 (drums mode)", { + DIV_SYSTEM_OPL3_DRUMS, 64, 0, 0, + DIV_SYSTEM_PCSPKR, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Sharp X1", { + DIV_SYSTEM_AY8910, 64, 0, 3, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Sharp X1 + FM Addon", { + DIV_SYSTEM_AY8910, 64, 0, 3, + DIV_SYSTEM_YM2151, 64, 0, 2, + 0 + } + )); + /* + cat.systems.push_back(FurnaceGUISysDef( + "Sharp X68000", { + DIV_SYSTEM_YM2151, 64, 0, 2, + DIV_SYSTEM_MSM6258, 64, 0, 0, + 0 + } + ));*/ + cat.systems.push_back(FurnaceGUISysDef( + "Commander X16", { + DIV_SYSTEM_YM2151, 64, 0, 0, + DIV_SYSTEM_VERA, 64, 0, 0, + 0 + } + )); + sysCategories.push_back(cat); + + cat=FurnaceGUISysCategory("Arcade systems"); + cat.systems.push_back(FurnaceGUISysDef( + "Bally Midway MCR", { + DIV_SYSTEM_AY8910, 64, 0, 0, + DIV_SYSTEM_AY8910, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Sega Kyugo", { + DIV_SYSTEM_AY8910, 64, 0, 4, + DIV_SYSTEM_AY8910, 64, 0, 4, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Sega OutRun/X Board", { + DIV_SYSTEM_YM2151, 64, 0, 2, + DIV_SYSTEM_SEGAPCM, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Neo Geo MVS", { + DIV_SYSTEM_YM2610_FULL, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Neo Geo MVS (extended channel 2)", { + DIV_SYSTEM_YM2610_FULL_EXT, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Taito Arcade", { + DIV_SYSTEM_YM2610B, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Taito Arcade (extended channel 3)", { + DIV_SYSTEM_YM2610B_EXT, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Capcom CPS-2 (QSound)", { + DIV_SYSTEM_QSOUND, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Seta 1", { + DIV_SYSTEM_X1_010, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Seta 2", { + DIV_SYSTEM_X1_010, 64, 0, 1, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Konami Bubble System", { + DIV_SYSTEM_AY8910, 64, 0, 0, + DIV_SYSTEM_AY8910, 64, 0, 0, + DIV_SYSTEM_BUBSYS_WSG, 64, 0, 0, + // VLM5030 exists but not used for music at all + 0 + } + )); + sysCategories.push_back(cat); + + cat=FurnaceGUISysCategory("DefleMask-compatible"); + cat.systems.push_back(FurnaceGUISysDef( + "Sega Genesis", { + DIV_SYSTEM_YM2612, 64, 0, 0, + DIV_SYSTEM_SMS, 24, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Sega Genesis (extended channel 3)", { + DIV_SYSTEM_YM2612_EXT, 64, 0, 0, + DIV_SYSTEM_SMS, 24, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Sega Master System", { + DIV_SYSTEM_SMS, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Sega Master System (with FM expansion)", { + DIV_SYSTEM_SMS, 64, 0, 0, + DIV_SYSTEM_OPLL, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Game Boy", { + DIV_SYSTEM_GB, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "NEC PC Engine/TurboGrafx-16", { + DIV_SYSTEM_PCE, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "NES", { + DIV_SYSTEM_NES, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "NES with Konami VRC7", { + DIV_SYSTEM_NES, 64, 0, 0, + DIV_SYSTEM_VRC7, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Commodore 64 (6581 SID)", { + DIV_SYSTEM_C64_6581, 64, 0, 1, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Commodore 64 (8580 SID)", { + DIV_SYSTEM_C64_8580, 64, 0, 1, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Arcade (YM2151 and SegaPCM)", { + DIV_SYSTEM_YM2151, 64, 0, 0, + DIV_SYSTEM_SEGAPCM_COMPAT, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Neo Geo CD", { + DIV_SYSTEM_YM2610, 64, 0, 0, + 0 + } + )); + cat.systems.push_back(FurnaceGUISysDef( + "Neo Geo CD (extended channel 2)", { + DIV_SYSTEM_YM2610_EXT, 64, 0, 0, + 0 + } + )); + sysCategories.push_back(cat); +} diff --git a/src/gui/regView.cpp b/src/gui/regView.cpp new file mode 100644 index 000000000..9755e4a83 --- /dev/null +++ b/src/gui/regView.cpp @@ -0,0 +1,71 @@ +/** + * 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" + +void FurnaceGUI::drawRegView() { + if (nextWindow==GUI_WINDOW_REGISTER_VIEW) { + channelsOpen=true; + ImGui::SetNextWindowFocus(); + nextWindow=GUI_WINDOW_NOTHING; + } + if (!regViewOpen) return; + if (ImGui::Begin("Register View",®ViewOpen)) { + for (int i=0; isong.systemLen; i++) { + ImGui::Text("%d. %s",i+1,getSystemName(e->song.system[i])); + int size=0; + int depth=8; + unsigned char* regPool=e->getRegisterPool(i,size,depth); + unsigned short* regPoolW=(unsigned short*)regPool; + if (regPool==NULL) { + ImGui::Text("- no register pool available"); + } else { + ImGui::PushFont(patFont); + if (ImGui::BeginTable("Memory",17)) { + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + for (int i=0; i<16; i++) { + ImGui::TableNextColumn(); + ImGui::TextColored(uiColors[GUI_COLOR_PATTERN_ROW_INDEX]," %X",i); + } + for (int i=0; i<=((size-1)>>4); i++) { + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::TextColored(uiColors[GUI_COLOR_PATTERN_ROW_INDEX],"%.2X",i*16); + for (int j=0; j<16; j++) { + ImGui::TableNextColumn(); + if (i*16+j>=size) continue; + if (depth == 8) { + ImGui::Text("%.2x",regPool[i*16+j]); + } else if (depth == 16) { + ImGui::Text("%.4x",regPoolW[i*16+j]); + } else { + ImGui::Text("??"); + } + } + } + ImGui::EndTable(); + } + ImGui::PopFont(); + } + } + } + if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_REGISTER_VIEW; + ImGui::End(); +} \ No newline at end of file diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index ed17a1e3b..a3fd347d6 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -18,8 +18,10 @@ */ #include "gui.h" +#include "fonts.h" #include "../ta-log.h" #include "util.h" +#include "ImGuiFileDialog.h" #include "IconsFontAwesome4.h" #include "misc/cpp/imgui_stdlib.h" #include @@ -1425,3 +1427,398 @@ void FurnaceGUI::commitSettings() { } } } + +void FurnaceGUI::parseKeybinds() { + actionMapGlobal.clear(); + actionMapPat.clear(); + actionMapInsList.clear(); + actionMapWaveList.clear(); + actionMapSampleList.clear(); + actionMapOrders.clear(); + + for (int i=GUI_ACTION_GLOBAL_MIN+1; igetConfInt(#target,ImGui::GetColorU32(def))); + +#ifdef _WIN32 +#define SYSTEM_FONT_PATH_1 "C:\\Windows\\Fonts\\segoeui.ttf" +#define SYSTEM_FONT_PATH_2 "C:\\Windows\\Fonts\\tahoma.ttf" +// TODO! +#define SYSTEM_FONT_PATH_3 "C:\\Windows\\Fonts\\tahoma.ttf" +// TODO! +#define SYSTEM_PAT_FONT_PATH_1 "C:\\Windows\\Fonts\\consola.ttf" +#define SYSTEM_PAT_FONT_PATH_2 "C:\\Windows\\Fonts\\cour.ttf" +// GOOD LUCK WITH THIS ONE - UNTESTED +#define SYSTEM_PAT_FONT_PATH_3 "C:\\Windows\\Fonts\\vgasys.fon" +#elif defined(__APPLE__) +#define SYSTEM_FONT_PATH_1 "/System/Library/Fonts/SFAANS.ttf" +#define SYSTEM_FONT_PATH_2 "/System/Library/Fonts/Helvetica.ttc" +#define SYSTEM_FONT_PATH_3 "/System/Library/Fonts/Helvetica.dfont" +#define SYSTEM_PAT_FONT_PATH_1 "/System/Library/Fonts/SFNSMono.ttf" +#define SYSTEM_PAT_FONT_PATH_2 "/System/Library/Fonts/Courier New.ttf" +#define SYSTEM_PAT_FONT_PATH_3 "/System/Library/Fonts/Courier New.ttf" +#else +#define SYSTEM_FONT_PATH_1 "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf" +#define SYSTEM_FONT_PATH_2 "/usr/share/fonts/TTF/DejaVuSans.ttf" +#define SYSTEM_FONT_PATH_3 "/usr/share/fonts/ubuntu/Ubuntu-R.ttf" +#define SYSTEM_PAT_FONT_PATH_1 "/usr/share/fonts/truetype/dejavu/DejaVuSansMono.ttf" +#define SYSTEM_PAT_FONT_PATH_2 "/usr/share/fonts/TTF/DejaVuSansMono.ttf" +#define SYSTEM_PAT_FONT_PATH_3 "/usr/share/fonts/ubuntu/UbuntuMono-R.ttf" +#endif + +void FurnaceGUI::applyUISettings() { + ImGuiStyle sty; + if (settings.guiColorsBase) { + ImGui::StyleColorsLight(&sty); + } else { + ImGui::StyleColorsDark(&sty); + } + + if (settings.dpiScale>=0.5f) dpiScale=settings.dpiScale; + + GET_UI_COLOR(GUI_COLOR_BACKGROUND,ImVec4(0.1f,0.1f,0.1f,1.0f)); + GET_UI_COLOR(GUI_COLOR_FRAME_BACKGROUND,ImVec4(0.0f,0.0f,0.0f,0.85f)); + GET_UI_COLOR(GUI_COLOR_MODAL_BACKDROP,ImVec4(0.0f,0.0f,0.0f,0.55f)); + GET_UI_COLOR(GUI_COLOR_HEADER,ImVec4(0.2f,0.2f,0.2f,1.0f)); + GET_UI_COLOR(GUI_COLOR_TEXT,ImVec4(1.0f,1.0f,1.0f,1.0f)); + GET_UI_COLOR(GUI_COLOR_ACCENT_PRIMARY,ImVec4(0.06f,0.53f,0.98f,1.0f)); + GET_UI_COLOR(GUI_COLOR_ACCENT_SECONDARY,ImVec4(0.26f,0.59f,0.98f,1.0f)); + GET_UI_COLOR(GUI_COLOR_EDITING,ImVec4(0.2f,0.1f,0.1f,1.0f)); + GET_UI_COLOR(GUI_COLOR_SONG_LOOP,ImVec4(0.3f,0.5f,0.8f,0.4f)); + GET_UI_COLOR(GUI_COLOR_VOLMETER_LOW,ImVec4(0.2f,0.6f,0.2f,1.0f)); + GET_UI_COLOR(GUI_COLOR_VOLMETER_HIGH,ImVec4(1.0f,0.9f,0.2f,1.0f)); + GET_UI_COLOR(GUI_COLOR_VOLMETER_PEAK,ImVec4(1.0f,0.1f,0.1f,1.0f)); + GET_UI_COLOR(GUI_COLOR_MACRO_VOLUME,ImVec4(0.2f,1.0f,0.0f,1.0f)); + GET_UI_COLOR(GUI_COLOR_MACRO_PITCH,ImVec4(1.0f,0.8f,0.0f,1.0f)); + GET_UI_COLOR(GUI_COLOR_MACRO_OTHER,ImVec4(0.0f,0.9f,1.0f,1.0f)); + GET_UI_COLOR(GUI_COLOR_MACRO_WAVE,ImVec4(1.0f,0.4f,0.0f,1.0f)); + GET_UI_COLOR(GUI_COLOR_INSTR_FM,ImVec4(0.6f,0.9f,1.0f,1.0f)); + GET_UI_COLOR(GUI_COLOR_INSTR_STD,ImVec4(0.6f,1.0f,0.5f,1.0f)); + GET_UI_COLOR(GUI_COLOR_INSTR_GB,ImVec4(1.0f,1.0f,0.5f,1.0f)); + GET_UI_COLOR(GUI_COLOR_INSTR_C64,ImVec4(0.85f,0.8f,1.0f,1.0f)); + GET_UI_COLOR(GUI_COLOR_INSTR_AMIGA,ImVec4(1.0f,0.5f,0.5f,1.0f)); + GET_UI_COLOR(GUI_COLOR_INSTR_PCE,ImVec4(1.0f,0.8f,0.5f,1.0f)); + GET_UI_COLOR(GUI_COLOR_INSTR_AY,ImVec4(1.0f,0.5f,1.0f,1.0f)); + GET_UI_COLOR(GUI_COLOR_INSTR_AY8930,ImVec4(0.7f,0.5f,1.0f,1.0f)); + GET_UI_COLOR(GUI_COLOR_INSTR_TIA,ImVec4(1.0f,0.6f,0.4f,1.0f)); + GET_UI_COLOR(GUI_COLOR_INSTR_SAA1099,ImVec4(0.3f,0.3f,1.0f,1.0f)); + GET_UI_COLOR(GUI_COLOR_INSTR_VIC,ImVec4(0.2f,1.0f,0.6f,1.0f)); + GET_UI_COLOR(GUI_COLOR_INSTR_PET,ImVec4(1.0f,1.0f,0.8f,1.0f)); + GET_UI_COLOR(GUI_COLOR_INSTR_VRC6,ImVec4(1.0f,0.9f,0.5f,1.0f)); + GET_UI_COLOR(GUI_COLOR_INSTR_OPLL,ImVec4(0.6f,0.7f,1.0f,1.0f)); + GET_UI_COLOR(GUI_COLOR_INSTR_OPL,ImVec4(0.3f,1.0f,0.9f,1.0f)); + GET_UI_COLOR(GUI_COLOR_INSTR_FDS,ImVec4(0.8f,0.5f,1.0f,1.0f)); + GET_UI_COLOR(GUI_COLOR_INSTR_VBOY,ImVec4(1.0f,0.1f,0.1f,1.0f)); + GET_UI_COLOR(GUI_COLOR_INSTR_N163,ImVec4(1.0f,0.4f,0.1f,1.0f)); + GET_UI_COLOR(GUI_COLOR_INSTR_SCC,ImVec4(0.7f,1.0f,0.3f,1.0f)); + GET_UI_COLOR(GUI_COLOR_INSTR_OPZ,ImVec4(0.2f,0.8f,1.0f,1.0f)); + GET_UI_COLOR(GUI_COLOR_INSTR_POKEY,ImVec4(0.5f,1.0f,0.3f,1.0f)); + GET_UI_COLOR(GUI_COLOR_INSTR_BEEPER,ImVec4(0.0f,1.0f,0.0f,1.0f)); + GET_UI_COLOR(GUI_COLOR_INSTR_SWAN,ImVec4(0.3f,0.5f,1.0f,1.0f)); + GET_UI_COLOR(GUI_COLOR_INSTR_MIKEY,ImVec4(0.5f,1.0f,0.3f,1.0f)); + GET_UI_COLOR(GUI_COLOR_INSTR_VERA,ImVec4(0.4f,0.6f,1.0f,1.0f)); + GET_UI_COLOR(GUI_COLOR_INSTR_X1_010,ImVec4(0.3f,0.5f,1.0f,1.0f)); + GET_UI_COLOR(GUI_COLOR_INSTR_UNKNOWN,ImVec4(0.3f,0.3f,0.3f,1.0f)); + GET_UI_COLOR(GUI_COLOR_CHANNEL_FM,ImVec4(0.2f,0.8f,1.0f,1.0f)); + GET_UI_COLOR(GUI_COLOR_CHANNEL_PULSE,ImVec4(0.4f,1.0f,0.2f,1.0f)); + GET_UI_COLOR(GUI_COLOR_CHANNEL_NOISE,ImVec4(0.8f,0.8f,0.8f,1.0f)); + GET_UI_COLOR(GUI_COLOR_CHANNEL_PCM,ImVec4(1.0f,0.9f,0.2f,1.0f)); + GET_UI_COLOR(GUI_COLOR_CHANNEL_WAVE,ImVec4(1.0f,0.5f,0.2f,1.0f)); + GET_UI_COLOR(GUI_COLOR_CHANNEL_OP,ImVec4(0.2f,0.4f,1.0f,1.0f)); + GET_UI_COLOR(GUI_COLOR_CHANNEL_MUTED,ImVec4(0.5f,0.5f,0.5f,1.0f)); + GET_UI_COLOR(GUI_COLOR_PATTERN_CURSOR,ImVec4(0.1f,0.3f,0.5f,1.0f)); + GET_UI_COLOR(GUI_COLOR_PATTERN_CURSOR_HOVER,ImVec4(0.2f,0.4f,0.6f,1.0f)); + GET_UI_COLOR(GUI_COLOR_PATTERN_CURSOR_ACTIVE,ImVec4(0.2f,0.5f,0.7f,1.0f)); + GET_UI_COLOR(GUI_COLOR_PATTERN_SELECTION,ImVec4(0.15f,0.15f,0.2f,1.0f)); + GET_UI_COLOR(GUI_COLOR_PATTERN_SELECTION_HOVER,ImVec4(0.2f,0.2f,0.3f,1.0f)); + GET_UI_COLOR(GUI_COLOR_PATTERN_SELECTION_ACTIVE,ImVec4(0.4f,0.4f,0.5f,1.0f)); + GET_UI_COLOR(GUI_COLOR_PATTERN_HI_1,ImVec4(0.6f,0.6f,0.6f,0.2f)); + GET_UI_COLOR(GUI_COLOR_PATTERN_HI_2,ImVec4(0.5f,0.8f,1.0f,0.2f)); + GET_UI_COLOR(GUI_COLOR_PATTERN_ROW_INDEX,ImVec4(0.5f,0.8f,1.0f,1.0f)); + GET_UI_COLOR(GUI_COLOR_PATTERN_ACTIVE,ImVec4(1.0f,1.0f,1.0f,1.0f)); + GET_UI_COLOR(GUI_COLOR_PATTERN_INACTIVE,ImVec4(0.5f,0.5f,0.5f,1.0f)); + GET_UI_COLOR(GUI_COLOR_PATTERN_INS,ImVec4(0.4f,0.7f,1.0f,1.0f)); + GET_UI_COLOR(GUI_COLOR_PATTERN_VOLUME_MIN,ImVec4(0.0f,0.5f,0.0f,1.0f)); + GET_UI_COLOR(GUI_COLOR_PATTERN_VOLUME_HALF,ImVec4(0.0f,0.75f,0.0f,1.0f)); + GET_UI_COLOR(GUI_COLOR_PATTERN_VOLUME_MAX,ImVec4(0.0f,1.0f,0.0f,1.0f)); + GET_UI_COLOR(GUI_COLOR_PATTERN_EFFECT_INVALID,ImVec4(1.0f,0.0f,0.0f,1.0f)); + GET_UI_COLOR(GUI_COLOR_PATTERN_EFFECT_PITCH,ImVec4(1.0f,1.0f,0.0f,1.0f)); + GET_UI_COLOR(GUI_COLOR_PATTERN_EFFECT_VOLUME,ImVec4(0.0f,1.0f,0.0f,1.0f)); + GET_UI_COLOR(GUI_COLOR_PATTERN_EFFECT_PANNING,ImVec4(0.0f,1.0f,1.0f,1.0f)); + GET_UI_COLOR(GUI_COLOR_PATTERN_EFFECT_SONG,ImVec4(1.0f,0.0f,0.0f,1.0f)); + GET_UI_COLOR(GUI_COLOR_PATTERN_EFFECT_TIME,ImVec4(0.5f,0.0f,1.0f,1.0f)); + GET_UI_COLOR(GUI_COLOR_PATTERN_EFFECT_SPEED,ImVec4(1.0f,0.0f,1.0f,1.0f)); + GET_UI_COLOR(GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY,ImVec4(0.5f,1.0f,0.0f,1.0f)); + GET_UI_COLOR(GUI_COLOR_PATTERN_EFFECT_SYS_SECONDARY,ImVec4(0.0f,1.0f,0.5f,1.0f)); + GET_UI_COLOR(GUI_COLOR_PATTERN_EFFECT_MISC,ImVec4(0.3f,0.3f,1.0f,1.0f)); + GET_UI_COLOR(GUI_COLOR_EE_VALUE,ImVec4(0.0f,1.0f,1.0f,1.0f)); + GET_UI_COLOR(GUI_COLOR_PLAYBACK_STAT,ImVec4(0.6f,0.6f,0.6f,1.0f)); + + for (int i=0; i<64; i++) { + ImVec4 col1=uiColors[GUI_COLOR_PATTERN_VOLUME_MIN]; + ImVec4 col2=uiColors[GUI_COLOR_PATTERN_VOLUME_HALF]; + ImVec4 col3=uiColors[GUI_COLOR_PATTERN_VOLUME_MAX]; + volColors[i]=ImVec4(col1.x+((col2.x-col1.x)*float(i)/64.0f), + col1.y+((col2.y-col1.y)*float(i)/64.0f), + col1.z+((col2.z-col1.z)*float(i)/64.0f), + 1.0f); + volColors[i+64]=ImVec4(col2.x+((col3.x-col2.x)*float(i)/64.0f), + col2.y+((col3.y-col2.y)*float(i)/64.0f), + col2.z+((col3.z-col2.z)*float(i)/64.0f), + 1.0f); + } + + float hue, sat, val; + + ImVec4 primaryActive=uiColors[GUI_COLOR_ACCENT_PRIMARY]; + ImVec4 primaryHover, primary; + primaryHover.w=primaryActive.w; + primary.w=primaryActive.w; + ImGui::ColorConvertRGBtoHSV(primaryActive.x,primaryActive.y,primaryActive.z,hue,sat,val); + if (settings.guiColorsBase) { + primary=primaryActive; + ImGui::ColorConvertHSVtoRGB(hue,sat*0.9,val*0.9,primaryHover.x,primaryHover.y,primaryHover.z); + ImGui::ColorConvertHSVtoRGB(hue,sat,val*0.5,primaryActive.x,primaryActive.y,primaryActive.z); + } else { + ImGui::ColorConvertHSVtoRGB(hue,sat*0.9,val*0.5,primaryHover.x,primaryHover.y,primaryHover.z); + ImGui::ColorConvertHSVtoRGB(hue,sat*0.8,val*0.35,primary.x,primary.y,primary.z); + } + + ImVec4 secondaryActive=uiColors[GUI_COLOR_ACCENT_SECONDARY]; + ImVec4 secondaryHover, secondary, secondarySemiActive; + secondarySemiActive.w=secondaryActive.w; + secondaryHover.w=secondaryActive.w; + secondary.w=secondaryActive.w; + ImGui::ColorConvertRGBtoHSV(secondaryActive.x,secondaryActive.y,secondaryActive.z,hue,sat,val); + if (settings.guiColorsBase) { + secondary=secondaryActive; + ImGui::ColorConvertHSVtoRGB(hue,sat*0.9,val*0.7,secondarySemiActive.x,secondarySemiActive.y,secondarySemiActive.z); + ImGui::ColorConvertHSVtoRGB(hue,sat*0.9,val*0.9,secondaryHover.x,secondaryHover.y,secondaryHover.z); + ImGui::ColorConvertHSVtoRGB(hue,sat,val*0.5,secondaryActive.x,secondaryActive.y,secondaryActive.z); + } else { + ImGui::ColorConvertHSVtoRGB(hue,sat*0.9,val*0.75,secondarySemiActive.x,secondarySemiActive.y,secondarySemiActive.z); + ImGui::ColorConvertHSVtoRGB(hue,sat*0.9,val*0.5,secondaryHover.x,secondaryHover.y,secondaryHover.z); + ImGui::ColorConvertHSVtoRGB(hue,sat*0.9,val*0.25,secondary.x,secondary.y,secondary.z); + } + + + sty.Colors[ImGuiCol_WindowBg]=uiColors[GUI_COLOR_FRAME_BACKGROUND]; + sty.Colors[ImGuiCol_ModalWindowDimBg]=uiColors[GUI_COLOR_MODAL_BACKDROP]; + sty.Colors[ImGuiCol_Text]=uiColors[GUI_COLOR_TEXT]; + + sty.Colors[ImGuiCol_Button]=primary; + sty.Colors[ImGuiCol_ButtonHovered]=primaryHover; + sty.Colors[ImGuiCol_ButtonActive]=primaryActive; + sty.Colors[ImGuiCol_Tab]=primary; + sty.Colors[ImGuiCol_TabHovered]=secondaryHover; + sty.Colors[ImGuiCol_TabActive]=secondarySemiActive; + sty.Colors[ImGuiCol_TabUnfocused]=primary; + sty.Colors[ImGuiCol_TabUnfocusedActive]=primaryHover; + sty.Colors[ImGuiCol_Header]=secondary; + sty.Colors[ImGuiCol_HeaderHovered]=secondaryHover; + sty.Colors[ImGuiCol_HeaderActive]=secondaryActive; + sty.Colors[ImGuiCol_ResizeGrip]=secondary; + sty.Colors[ImGuiCol_ResizeGripHovered]=secondaryHover; + sty.Colors[ImGuiCol_ResizeGripActive]=secondaryActive; + sty.Colors[ImGuiCol_FrameBg]=secondary; + sty.Colors[ImGuiCol_FrameBgHovered]=secondaryHover; + sty.Colors[ImGuiCol_FrameBgActive]=secondaryActive; + sty.Colors[ImGuiCol_SliderGrab]=primaryActive; + sty.Colors[ImGuiCol_SliderGrabActive]=primaryActive; + sty.Colors[ImGuiCol_TitleBgActive]=primary; + sty.Colors[ImGuiCol_CheckMark]=primaryActive; + sty.Colors[ImGuiCol_TextSelectedBg]=secondaryHover; + sty.Colors[ImGuiCol_PlotHistogram]=uiColors[GUI_COLOR_MACRO_OTHER]; + sty.Colors[ImGuiCol_PlotHistogramHovered]=uiColors[GUI_COLOR_MACRO_OTHER]; + + if (settings.roundedWindows) sty.WindowRounding=8.0f; + if (settings.roundedButtons) { + sty.FrameRounding=6.0f; + sty.GrabRounding=6.0f; + } + if (settings.roundedMenus) sty.PopupRounding=8.0f; + + sty.ScaleAllSizes(dpiScale); + + ImGui::GetStyle()=sty; + + for (int i=0; i<256; i++) { + ImVec4& base=uiColors[GUI_COLOR_PATTERN_EFFECT_PITCH]; + pitchGrad[i]=ImGui::GetColorU32(ImVec4(base.x,base.y,base.z,((float)i/255.0f)*base.w)); + } + for (int i=0; i<256; i++) { + ImVec4& base=uiColors[GUI_COLOR_PATTERN_ACTIVE]; + noteGrad[i]=ImGui::GetColorU32(ImVec4(base.x,base.y,base.z,((float)i/255.0f)*base.w)); + } + for (int i=0; i<256; i++) { + ImVec4& base=uiColors[GUI_COLOR_PATTERN_EFFECT_PANNING]; + panGrad[i]=ImGui::GetColorU32(ImVec4(base.x,base.y,base.z,((float)i/255.0f)*base.w)); + } + for (int i=0; i<256; i++) { + ImVec4& base=uiColors[GUI_COLOR_PATTERN_INS]; + insGrad[i]=ImGui::GetColorU32(ImVec4(base.x,base.y,base.z,((float)i/255.0f)*base.w)); + } + for (int i=0; i<256; i++) { + ImVec4& base=volColors[i/2]; + volGrad[i]=ImGui::GetColorU32(ImVec4(base.x,base.y,base.z,((float)i/255.0f)*base.w)); + } + for (int i=0; i<256; i++) { + ImVec4& base=uiColors[GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY]; + sysCmd1Grad[i]=ImGui::GetColorU32(ImVec4(base.x,base.y,base.z,((float)i/255.0f)*base.w)); + } + for (int i=0; i<256; i++) { + ImVec4& base=uiColors[GUI_COLOR_PATTERN_EFFECT_SYS_SECONDARY]; + sysCmd2Grad[i]=ImGui::GetColorU32(ImVec4(base.x,base.y,base.z,((float)i/255.0f)*base.w)); + } + + // set to 800 for now due to problems with unifont + static const ImWchar loadEverything[]={0x20,0x800,0}; + + if (settings.mainFont<0 || settings.mainFont>6) settings.mainFont=0; + if (settings.patFont<0 || settings.patFont>6) settings.patFont=0; + + if (settings.mainFont==6 && settings.mainFontPath.empty()) { + logW("UI font path is empty! reverting to default font\n"); + settings.mainFont=0; + } + if (settings.patFont==6 && settings.patFontPath.empty()) { + logW("pattern font path is empty! reverting to default font\n"); + settings.patFont=0; + } + + if (settings.mainFont==6) { // custom font + if ((mainFont=ImGui::GetIO().Fonts->AddFontFromFileTTF(settings.mainFontPath.c_str(),e->getConfInt("mainFontSize",18)*dpiScale,NULL,loadEverything))==NULL) { + logW("could not load UI font! reverting to default font\n"); + settings.mainFont=0; + if ((mainFont=ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF(builtinFont[settings.mainFont],builtinFontLen[settings.mainFont],e->getConfInt("mainFontSize",18)*dpiScale,NULL,loadEverything))==NULL) { + logE("could not load UI font! falling back to Proggy Clean.\n"); + mainFont=ImGui::GetIO().Fonts->AddFontDefault(); + } + } + } else if (settings.mainFont==5) { // system font + if ((mainFont=ImGui::GetIO().Fonts->AddFontFromFileTTF(SYSTEM_FONT_PATH_1,e->getConfInt("mainFontSize",18)*dpiScale,NULL,loadEverything))==NULL) { + if ((mainFont=ImGui::GetIO().Fonts->AddFontFromFileTTF(SYSTEM_FONT_PATH_2,e->getConfInt("mainFontSize",18)*dpiScale,NULL,loadEverything))==NULL) { + if ((mainFont=ImGui::GetIO().Fonts->AddFontFromFileTTF(SYSTEM_FONT_PATH_3,e->getConfInt("mainFontSize",18)*dpiScale,NULL,loadEverything))==NULL) { + logW("could not load UI font! reverting to default font\n"); + settings.mainFont=0; + if ((mainFont=ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF(builtinFont[settings.mainFont],builtinFontLen[settings.mainFont],e->getConfInt("mainFontSize",18)*dpiScale,NULL,loadEverything))==NULL) { + logE("could not load UI font! falling back to Proggy Clean.\n"); + mainFont=ImGui::GetIO().Fonts->AddFontDefault(); + } + } + } + } + } else { + if ((mainFont=ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF(builtinFont[settings.mainFont],builtinFontLen[settings.mainFont],e->getConfInt("mainFontSize",18)*dpiScale,NULL,loadEverything))==NULL) { + logE("could not load UI font! falling back to Proggy Clean.\n"); + mainFont=ImGui::GetIO().Fonts->AddFontDefault(); + } + } + + ImFontConfig fc; + fc.MergeMode=true; + fc.GlyphMinAdvanceX=e->getConfInt("iconSize",16)*dpiScale; + static const ImWchar fontRange[]={ICON_MIN_FA,ICON_MAX_FA,0}; + if ((iconFont=ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF(iconFont_compressed_data,iconFont_compressed_size,e->getConfInt("iconSize",16)*dpiScale,&fc,fontRange))==NULL) { + logE("could not load icon font!\n"); + } + if (settings.mainFontSize==settings.patFontSize && settings.patFont<5 && builtinFontM[settings.patFont]==builtinFont[settings.mainFont]) { + logD("using main font for pat font.\n"); + patFont=mainFont; + } else { + if (settings.patFont==6) { // custom font + if ((patFont=ImGui::GetIO().Fonts->AddFontFromFileTTF(settings.patFontPath.c_str(),e->getConfInt("patFontSize",18)*dpiScale,NULL,loadEverything))==NULL) { + logW("could not load pattern font! reverting to default font\n"); + settings.patFont=0; + if ((patFont=ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF(builtinFontM[settings.patFont],builtinFontMLen[settings.patFont],e->getConfInt("patFontSize",18)*dpiScale,NULL,loadEverything))==NULL) { + logE("could not load pattern font! falling back to Proggy Clean.\n"); + patFont=ImGui::GetIO().Fonts->AddFontDefault(); + } + } + } else if (settings.patFont==5) { // system font + if ((patFont=ImGui::GetIO().Fonts->AddFontFromFileTTF(SYSTEM_PAT_FONT_PATH_1,e->getConfInt("patFontSize",18)*dpiScale,NULL,loadEverything))==NULL) { + if ((patFont=ImGui::GetIO().Fonts->AddFontFromFileTTF(SYSTEM_PAT_FONT_PATH_2,e->getConfInt("patFontSize",18)*dpiScale,NULL,loadEverything))==NULL) { + if ((patFont=ImGui::GetIO().Fonts->AddFontFromFileTTF(SYSTEM_PAT_FONT_PATH_3,e->getConfInt("patFontSize",18)*dpiScale,NULL,loadEverything))==NULL) { + logW("could not load pattern font! reverting to default font\n"); + settings.patFont=0; + if ((patFont=ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF(builtinFontM[settings.patFont],builtinFontMLen[settings.patFont],e->getConfInt("patFontSize",18)*dpiScale,NULL,loadEverything))==NULL) { + logE("could not load pattern font! falling back to Proggy Clean.\n"); + patFont=ImGui::GetIO().Fonts->AddFontDefault(); + } + } + } + } + } else { + if ((patFont=ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF(builtinFontM[settings.patFont],builtinFontMLen[settings.patFont],e->getConfInt("patFontSize",18)*dpiScale,NULL,loadEverything))==NULL) { + logE("could not load pattern font!\n"); + patFont=ImGui::GetIO().Fonts->AddFontDefault(); + } + } + } + if ((bigFont=ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF(font_plexSans_compressed_data,font_plexSans_compressed_size,40*dpiScale))==NULL) { + logE("could not load big UI font!\n"); + } + + // TODO: allow changing these colors. + ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByTypeDir,"",ImVec4(0.0f,1.0f,1.0f,1.0f),ICON_FA_FOLDER_O); + ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByTypeFile,"",ImVec4(0.7f,0.7f,0.7f,1.0f),ICON_FA_FILE_O); + ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".fur",ImVec4(0.5f,1.0f,0.5f,1.0f),ICON_FA_FILE); + ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".fui",ImVec4(1.0f,0.5f,0.5f,1.0f),ICON_FA_FILE); + ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".fuw",ImVec4(1.0f,0.75f,0.5f,1.0f),ICON_FA_FILE); + ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".dmf",ImVec4(0.5f,1.0f,0.5f,1.0f),ICON_FA_FILE); + ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".dmp",ImVec4(1.0f,0.5f,0.5f,1.0f),ICON_FA_FILE); + ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".dmw",ImVec4(1.0f,0.75f,0.5f,1.0f),ICON_FA_FILE); + ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".wav",ImVec4(1.0f,1.0f,0.5f,1.0f),ICON_FA_FILE_AUDIO_O); + ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".vgm",ImVec4(1.0f,1.0f,0.5f,1.0f),ICON_FA_FILE_AUDIO_O); + ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".ttf",ImVec4(0.3f,1.0f,0.6f,1.0f),ICON_FA_FONT); + ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".otf",ImVec4(0.3f,1.0f,0.6f,1.0f),ICON_FA_FONT); + ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".ttc",ImVec4(0.3f,1.0f,0.6f,1.0f),ICON_FA_FONT); + + ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".tfi",ImVec4(1.0f,0.5f,0.5f,1.0f),ICON_FA_FILE); + ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".vgi",ImVec4(1.0f,0.5f,0.5f,1.0f),ICON_FA_FILE); + ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".s3i",ImVec4(1.0f,0.5f,0.5f,1.0f),ICON_FA_FILE); + ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".sbi",ImVec4(1.0f,0.5f,0.5f,1.0f),ICON_FA_FILE); + ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".fti",ImVec4(1.0f,0.5f,0.5f,1.0f),ICON_FA_FILE); + ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".bti",ImVec4(1.0f,0.5f,0.5f,1.0f),ICON_FA_FILE); + + if (fileDialog!=NULL) delete fileDialog; + fileDialog=new FurnaceGUIFileDialog(settings.sysFileDialog); +} \ No newline at end of file diff --git a/src/gui/songInfo.cpp b/src/gui/songInfo.cpp new file mode 100644 index 000000000..e9caf5af9 --- /dev/null +++ b/src/gui/songInfo.cpp @@ -0,0 +1,173 @@ +/** + * 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 "intConst.h" + +void FurnaceGUI::drawSongInfo() { + if (nextWindow==GUI_WINDOW_SONG_INFO) { + songInfoOpen=true; + ImGui::SetNextWindowFocus(); + nextWindow=GUI_WINDOW_NOTHING; + } + if (!songInfoOpen) return; + if (ImGui::Begin("Song Information",&songInfoOpen)) { + if (ImGui::BeginTable("NameAuthor",2,ImGuiTableFlags_SizingStretchProp)) { + ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthFixed,0.0); + ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthStretch,0.0); + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::Text("Name"); + ImGui::TableNextColumn(); + float avail=ImGui::GetContentRegionAvail().x; + ImGui::SetNextItemWidth(avail); + if (ImGui::InputText("##Name",&e->song.name)) { MARK_MODIFIED + updateWindowTitle(); + } + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::Text("Author"); + ImGui::TableNextColumn(); + ImGui::SetNextItemWidth(avail); + if (ImGui::InputText("##Author",&e->song.author)) { + MARK_MODIFIED; + } + ImGui::EndTable(); + } + + if (ImGui::BeginTable("OtherProps",3,ImGuiTableFlags_SizingStretchProp)) { + ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthFixed,0.0); + ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthStretch,0.0); + ImGui::TableSetupColumn("c2",ImGuiTableColumnFlags_WidthStretch,0.0); + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::Text("TimeBase"); + ImGui::TableNextColumn(); + float avail=ImGui::GetContentRegionAvail().x; + ImGui::SetNextItemWidth(avail); + unsigned char realTB=e->song.timeBase+1; + if (ImGui::InputScalar("##TimeBase",ImGuiDataType_U8,&realTB,&_ONE,&_THREE)) { MARK_MODIFIED + if (realTB<1) realTB=1; + if (realTB>16) realTB=16; + e->song.timeBase=realTB-1; + } + ImGui::TableNextColumn(); + ImGui::Text("%.2f BPM",calcBPM(e->song.speed1,e->song.speed2,e->song.hz)); + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::Text("Speed"); + ImGui::TableNextColumn(); + ImGui::SetNextItemWidth(avail); + if (ImGui::InputScalar("##Speed1",ImGuiDataType_U8,&e->song.speed1,&_ONE,&_THREE)) { MARK_MODIFIED + if (e->song.speed1<1) e->song.speed1=1; + if (e->isPlaying()) play(); + } + ImGui::TableNextColumn(); + ImGui::SetNextItemWidth(avail); + if (ImGui::InputScalar("##Speed2",ImGuiDataType_U8,&e->song.speed2,&_ONE,&_THREE)) { MARK_MODIFIED + if (e->song.speed2<1) e->song.speed2=1; + if (e->isPlaying()) play(); + } + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::Text("Highlight"); + ImGui::TableNextColumn(); + ImGui::SetNextItemWidth(avail); + if (ImGui::InputScalar("##Highlight1",ImGuiDataType_U8,&e->song.hilightA,&_ONE,&_THREE)) { + MARK_MODIFIED; + } + ImGui::TableNextColumn(); + ImGui::SetNextItemWidth(avail); + if (ImGui::InputScalar("##Highlight2",ImGuiDataType_U8,&e->song.hilightB,&_ONE,&_THREE)) { + MARK_MODIFIED; + } + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::Text("Pattern Length"); + ImGui::TableNextColumn(); + ImGui::SetNextItemWidth(avail); + int patLen=e->song.patLen; + if (ImGui::InputInt("##PatLength",&patLen,1,3)) { MARK_MODIFIED + if (patLen<1) patLen=1; + if (patLen>256) patLen=256; + e->song.patLen=patLen; + } + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::Text("Song Length"); + ImGui::TableNextColumn(); + ImGui::SetNextItemWidth(avail); + int ordLen=e->song.ordersLen; + if (ImGui::InputInt("##OrdLength",&ordLen,1,3)) { MARK_MODIFIED + if (ordLen<1) ordLen=1; + if (ordLen>127) ordLen=127; + e->song.ordersLen=ordLen; + } + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + if (ImGui::Selectable(tempoView?"Base Tempo##TempoOrHz":"Tick Rate##TempoOrHz")) { + tempoView=!tempoView; + } + ImGui::TableNextColumn(); + ImGui::SetNextItemWidth(avail); + float setHz=tempoView?e->song.hz*2.5:e->song.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>999) setHz=999; + e->setSongRate(setHz,setHz<52); + } + if (tempoView) { + ImGui::TableNextColumn(); + ImGui::Text("= %gHz",e->song.hz); + } else { + if (e->song.hz>=49.98 && e->song.hz<=50.02) { + ImGui::TableNextColumn(); + ImGui::Text("PAL"); + } + if (e->song.hz>=59.9 && e->song.hz<=60.11) { + ImGui::TableNextColumn(); + ImGui::Text("NTSC"); + } + } + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::Text("Tuning (A-4)"); + ImGui::TableNextColumn(); + float tune=e->song.tuning; + ImGui::SetNextItemWidth(avail); + if (ImGui::InputFloat("##Tuning",&tune,1.0f,3.0f,"%g")) { MARK_MODIFIED + if (tune<220.0f) tune=220.0f; + if (tune>880.0f) tune=880.0f; + e->song.tuning=tune; + } + ImGui::EndTable(); + } + } + if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_SONG_INFO; + ImGui::End(); +} \ No newline at end of file diff --git a/src/gui/songNotes.cpp b/src/gui/songNotes.cpp new file mode 100644 index 000000000..7c7cbdaa9 --- /dev/null +++ b/src/gui/songNotes.cpp @@ -0,0 +1,37 @@ +/** + * 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" + +// NOTE: please don't ask me to enable text wrap. +// Dear ImGui doesn't have that feature. D: +void FurnaceGUI::drawNotes() { + if (nextWindow==GUI_WINDOW_NOTES) { + notesOpen=true; + ImGui::SetNextWindowFocus(); + nextWindow=GUI_WINDOW_NOTHING; + } + if (!notesOpen) return; + if (ImGui::Begin("Song Comments",¬esOpen)) { + ImGui::InputTextMultiline("##SongNotes",&e->song.notes,ImGui::GetContentRegionAvail()); + } + if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_NOTES; + ImGui::End(); +} \ No newline at end of file diff --git a/src/gui/stats.cpp b/src/gui/stats.cpp new file mode 100644 index 000000000..274be2eda --- /dev/null +++ b/src/gui/stats.cpp @@ -0,0 +1,50 @@ +/** + * 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 + +void FurnaceGUI::drawStats() { + if (nextWindow==GUI_WINDOW_STATS) { + statsOpen=true; + ImGui::SetNextWindowFocus(); + nextWindow=GUI_WINDOW_NOTHING; + } + if (!statsOpen) return; + if (ImGui::Begin("Statistics",&statsOpen)) { + String adpcmAUsage=fmt::sprintf("%d/16384KB",e->adpcmAMemLen/1024); + String adpcmBUsage=fmt::sprintf("%d/16384KB",e->adpcmBMemLen/1024); + String qsoundUsage=fmt::sprintf("%d/16384KB",e->qsoundMemLen/1024); + String x1_010Usage=fmt::sprintf("%d/1024KB",e->x1_010MemLen/1024); + ImGui::Text("ADPCM-A"); + ImGui::SameLine(); + ImGui::ProgressBar(((float)e->adpcmAMemLen)/16777216.0f,ImVec2(-FLT_MIN,0),adpcmAUsage.c_str()); + ImGui::Text("ADPCM-B"); + ImGui::SameLine(); + ImGui::ProgressBar(((float)e->adpcmBMemLen)/16777216.0f,ImVec2(-FLT_MIN,0),adpcmBUsage.c_str()); + ImGui::Text("QSound"); + ImGui::SameLine(); + ImGui::ProgressBar(((float)e->qsoundMemLen)/16777216.0f,ImVec2(-FLT_MIN,0),qsoundUsage.c_str()); + ImGui::Text("X1-010"); + ImGui::SameLine(); + ImGui::ProgressBar(((float)e->x1_010MemLen)/1048576.0f,ImVec2(-FLT_MIN,0),x1_010Usage.c_str()); + } + if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_STATS; + ImGui::End(); +} \ No newline at end of file diff --git a/src/gui/sysConf.cpp b/src/gui/sysConf.cpp new file mode 100644 index 000000000..b61245f2c --- /dev/null +++ b/src/gui/sysConf.cpp @@ -0,0 +1,371 @@ +/** + * 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" + +void FurnaceGUI::drawSysConf(int i) { + unsigned int flags=e->song.systemFlags[i]; + bool restart=settings.restartOnFlagChange; + bool sysPal=flags&1; + switch (e->song.system[i]) { + case DIV_SYSTEM_YM2612: + case DIV_SYSTEM_YM2612_EXT: { + if (ImGui::RadioButton("NTSC (7.67MHz)",(flags&3)==0)) { + e->setSysFlags(i,(flags&0x80000000)|0,restart); + updateWindowTitle(); + } + if (ImGui::RadioButton("PAL (7.61MHz)",(flags&3)==1)) { + e->setSysFlags(i,(flags&0x80000000)|1,restart); + updateWindowTitle(); + } + if (ImGui::RadioButton("FM Towns (8MHz)",(flags&3)==2)) { + e->setSysFlags(i,(flags&0x80000000)|2,restart); + updateWindowTitle(); + } + if (ImGui::RadioButton("AtGames Genesis (6.13MHz)",(flags&3)==3)) { + e->setSysFlags(i,(flags&0x80000000)|3,restart); + updateWindowTitle(); + } + bool ladder=flags&0x80000000; + if (ImGui::Checkbox("Enable DAC distortion",&ladder)) { + e->setSysFlags(i,(flags&(~0x80000000))|(ladder?0x80000000:0),restart); + updateWindowTitle(); + } + break; + } + case DIV_SYSTEM_SMS: { + ImGui::Text("Clock rate:"); + if (ImGui::RadioButton("NTSC (3.58MHz)",(flags&3)==0)) { + e->setSysFlags(i,(flags&(~3))|0,restart); + updateWindowTitle(); + } + if (ImGui::RadioButton("PAL (3.55MHz)",(flags&3)==1)) { + e->setSysFlags(i,(flags&(~3))|1,restart); + updateWindowTitle(); + } + if (ImGui::RadioButton("BBC Micro (4MHz)",(flags&3)==2)) { + e->setSysFlags(i,(flags&(~3))|2,restart); + updateWindowTitle(); + } + if (ImGui::RadioButton("Half NTSC (1.79MHz)",(flags&3)==3)) { + e->setSysFlags(i,(flags&(~3))|3,restart); + updateWindowTitle(); + } + ImGui::Text("Chip type:"); + if (ImGui::RadioButton("Sega VDP/Master System",((flags>>2)&3)==0)) { + e->setSysFlags(i,(flags&(~12))|0,restart); + updateWindowTitle(); + } + if (ImGui::RadioButton("TI SN76489",((flags>>2)&3)==1)) { + e->setSysFlags(i,(flags&(~12))|4,restart); + updateWindowTitle(); + } + if (ImGui::RadioButton("TI SN76489 with Atari-like short noise",((flags>>2)&3)==2)) { + e->setSysFlags(i,(flags&(~12))|8,restart); + updateWindowTitle(); + } + /*if (ImGui::RadioButton("Game Gear",(flags>>2)==3)) { + e->setSysFlags(i,(flags&3)|12); + }*/ + + bool noPhaseReset=flags&16; + if (ImGui::Checkbox("Disable noise period change phase reset",&noPhaseReset)) { + e->setSysFlags(i,(flags&(~16))|(noPhaseReset<<4),restart); + updateWindowTitle(); + } + break; + } + case DIV_SYSTEM_OPLL: + case DIV_SYSTEM_OPLL_DRUMS: + case DIV_SYSTEM_VRC7: { + ImGui::Text("Clock rate:"); + if (ImGui::RadioButton("NTSC (3.58MHz)",(flags&15)==0)) { + e->setSysFlags(i,(flags&(~15))|0,restart); + updateWindowTitle(); + } + if (ImGui::RadioButton("PAL (3.55MHz)",(flags&15)==1)) { + e->setSysFlags(i,(flags&(~15))|1,restart); + updateWindowTitle(); + } + if (ImGui::RadioButton("BBC Micro (4MHz)",(flags&15)==2)) { + e->setSysFlags(i,(flags&(~15))|2,restart); + updateWindowTitle(); + } + if (ImGui::RadioButton("Half NTSC (1.79MHz)",(flags&15)==3)) { + e->setSysFlags(i,(flags&(~15))|3,restart); + updateWindowTitle(); + } + if (e->song.system[i]!=DIV_SYSTEM_VRC7) { + ImGui::Text("Patch set:"); + if (ImGui::RadioButton("Yamaha YM2413",((flags>>4)&15)==0)) { + e->setSysFlags(i,(flags&(~0xf0))|0,restart); + updateWindowTitle(); + } + if (ImGui::RadioButton("Yamaha YMF281",((flags>>4)&15)==1)) { + e->setSysFlags(i,(flags&(~0xf0))|0x10,restart); + updateWindowTitle(); + } + if (ImGui::RadioButton("Yamaha YM2423",((flags>>4)&15)==2)) { + e->setSysFlags(i,(flags&(~0xf0))|0x20,restart); + updateWindowTitle(); + } + if (ImGui::RadioButton("Konami VRC7",((flags>>4)&15)==3)) { + e->setSysFlags(i,(flags&(~0xf0))|0x30,restart); + updateWindowTitle(); + } + } + break; + } + case DIV_SYSTEM_YM2151: + if (ImGui::RadioButton("NTSC/X16 (3.58MHz)",flags==0)) { + e->setSysFlags(i,0,restart); + updateWindowTitle(); + } + if (ImGui::RadioButton("PAL (3.55MHz)",flags==1)) { + e->setSysFlags(i,1,restart); + updateWindowTitle(); + } + if (ImGui::RadioButton("X1/X68000 (4MHz)",flags==2)) { + e->setSysFlags(i,2,restart); + updateWindowTitle(); + } + break; + case DIV_SYSTEM_NES: + if (ImGui::RadioButton("NTSC (1.79MHz)",flags==0)) { + e->setSysFlags(i,0,restart); + updateWindowTitle(); + } + if (ImGui::RadioButton("PAL (1.67MHz)",flags==1)) { + e->setSysFlags(i,1,restart); + updateWindowTitle(); + } + if (ImGui::RadioButton("Dendy (1.77MHz)",flags==2)) { + e->setSysFlags(i,2,restart); + updateWindowTitle(); + } + break; + case DIV_SYSTEM_C64_8580: + case DIV_SYSTEM_C64_6581: + if (ImGui::RadioButton("NTSC (1.02MHz)",flags==0)) { + e->setSysFlags(i,0,restart); + updateWindowTitle(); + } + if (ImGui::RadioButton("PAL (0.99MHz)",flags==1)) { + e->setSysFlags(i,1,restart); + updateWindowTitle(); + } + if (ImGui::RadioButton("SSI 2001 (0.89MHz)",flags==2)) { + e->setSysFlags(i,2,restart); + updateWindowTitle(); + } + break; + case DIV_SYSTEM_AY8910: + case DIV_SYSTEM_AY8930: { + ImGui::Text("Clock rate:"); + if (ImGui::RadioButton("1.79MHz (ZX Spectrum NTSC/MSX)",(flags&15)==0)) { + e->setSysFlags(i,(flags&(~15))|0,restart); + updateWindowTitle(); + } + if (ImGui::RadioButton("1.77MHz (ZX Spectrum)",(flags&15)==1)) { + e->setSysFlags(i,(flags&(~15))|1,restart); + updateWindowTitle(); + } + if (ImGui::RadioButton("1.75MHz (ZX Spectrum)",(flags&15)==2)) { + e->setSysFlags(i,(flags&(~15))|2,restart); + updateWindowTitle(); + } + if (ImGui::RadioButton("2MHz (Atari ST/Sharp X1)",(flags&15)==3)) { + e->setSysFlags(i,(flags&(~15))|3,restart); + updateWindowTitle(); + } + if (ImGui::RadioButton("1.5MHz (Vectrex)",(flags&15)==4)) { + e->setSysFlags(i,(flags&(~15))|4,restart); + updateWindowTitle(); + } + if (ImGui::RadioButton("1MHz (Amstrad CPC)",(flags&15)==5)) { + e->setSysFlags(i,(flags&(~15))|5,restart); + updateWindowTitle(); + } + if (ImGui::RadioButton("0.89MHz (Sunsoft 5B)",(flags&15)==6)) { + e->setSysFlags(i,(flags&(~15))|6,restart); + updateWindowTitle(); + } + if (ImGui::RadioButton("1.67MHz (?)",(flags&15)==7)) { + e->setSysFlags(i,(flags&(~15))|7,restart); + updateWindowTitle(); + } + if (ImGui::RadioButton("0.83MHz (Sunsoft 5B on PAL)",(flags&15)==8)) { + e->setSysFlags(i,(flags&(~15))|8,restart); + updateWindowTitle(); + } + if (ImGui::RadioButton("1.10MHz (Gamate/VIC-20 PAL)",(flags&15)==9)) { + e->setSysFlags(i,(flags&(~15))|9,restart); + updateWindowTitle(); + } + if (ImGui::RadioButton("2^21Hz (Game Boy)",(flags&15)==10)) { + e->setSysFlags(i,(flags&(~15))|10,restart); + updateWindowTitle(); + } + if (e->song.system[i]==DIV_SYSTEM_AY8910) { + ImGui::Text("Chip type:"); + if (ImGui::RadioButton("AY-3-8910",(flags&0x30)==0)) { + e->setSysFlags(i,(flags&(~0x30))|0,restart); + updateWindowTitle(); + } + if (ImGui::RadioButton("YM2149(F)",(flags&0x30)==16)) { + e->setSysFlags(i,(flags&(~0x30))|16,restart); + updateWindowTitle(); + } + if (ImGui::RadioButton("Sunsoft 5B",(flags&0x30)==32)) { + e->setSysFlags(i,(flags&(~0x30))|32,restart); + updateWindowTitle(); + } + if (ImGui::RadioButton("AY-3-8914",(flags&0x30)==48)) { + e->setSysFlags(i,(flags&(~0x30))|48,restart); + updateWindowTitle(); + } + } + bool stereo=flags&0x40; + ImGui::BeginDisabled((flags&0x30)==32); + if (ImGui::Checkbox("Stereo##_AY_STEREO",&stereo)) { + e->setSysFlags(i,(flags&(~0x40))|(stereo?0x40:0),restart); + updateWindowTitle(); + } + ImGui::EndDisabled(); + break; + } + case DIV_SYSTEM_SAA1099: + if (ImGui::RadioButton("SAM Coupé (8MHz)",flags==0)) { + e->setSysFlags(i,0,restart); + updateWindowTitle(); + } + if (ImGui::RadioButton("NTSC (7.15MHz)",flags==1)) { + e->setSysFlags(i,1,restart); + updateWindowTitle(); + } + if (ImGui::RadioButton("PAL (7.09MHz)",flags==2)) { + e->setSysFlags(i,2,restart); + updateWindowTitle(); + } + break; + case DIV_SYSTEM_AMIGA: { + ImGui::Text("Stereo separation:"); + int stereoSep=(flags>>8)&127; + if (ImGui::SliderInt("##StereoSep",&stereoSep,0,127)) { + if (stereoSep<0) stereoSep=0; + if (stereoSep>127) stereoSep=127; + e->setSysFlags(i,(flags&(~0x7f00))|((stereoSep&127)<<8),restart); + updateWindowTitle(); + } rightClickable + if (ImGui::RadioButton("Amiga 500 (OCS)",(flags&2)==0)) { + e->setSysFlags(i,flags&(~2),restart); + } + if (ImGui::RadioButton("Amiga 1200 (AGA)",(flags&2)==2)) { + e->setSysFlags(i,(flags&(~2))|2,restart); + } + sysPal=flags&1; + if (ImGui::Checkbox("PAL",&sysPal)) { + e->setSysFlags(i,(flags&(~1))|sysPal,restart); + updateWindowTitle(); + } + bool bypassLimits=flags&4; + if (ImGui::Checkbox("Bypass frequency limits",&bypassLimits)) { + e->setSysFlags(i,(flags&(~4))|(bypassLimits<<2),restart); + updateWindowTitle(); + } + break; + } + case DIV_SYSTEM_PCSPKR: { + ImGui::Text("Speaker type:"); + if (ImGui::RadioButton("Unfiltered",(flags&3)==0)) { + e->setSysFlags(i,(flags&(~3))|0,restart); + updateWindowTitle(); + } + if (ImGui::RadioButton("Cone",(flags&3)==1)) { + e->setSysFlags(i,(flags&(~3))|1,restart); + updateWindowTitle(); + } + if (ImGui::RadioButton("Piezo",(flags&3)==2)) { + e->setSysFlags(i,(flags&(~3))|2,restart); + updateWindowTitle(); + } + if (ImGui::RadioButton("Use system beeper (Linux only!)",(flags&3)==3)) { + e->setSysFlags(i,(flags&(~3))|3,restart); + updateWindowTitle(); + } + break; + } + case DIV_SYSTEM_QSOUND: { + ImGui::Text("Echo delay:"); + int echoBufSize=2725 - (flags & 4095); + if (ImGui::SliderInt("##EchoBufSize",&echoBufSize,0,2725)) { + if (echoBufSize<0) echoBufSize=0; + if (echoBufSize>2725) echoBufSize=2725; + e->setSysFlags(i,(flags & ~4095) | ((2725 - echoBufSize) & 4095),restart); + updateWindowTitle(); + } rightClickable + ImGui::Text("Echo feedback:"); + int echoFeedback=(flags>>12)&255; + if (ImGui::SliderInt("##EchoFeedback",&echoFeedback,0,255)) { + if (echoFeedback<0) echoFeedback=0; + if (echoFeedback>255) echoFeedback=255; + e->setSysFlags(i,(flags & ~(255 << 12)) | ((echoFeedback & 255) << 12),restart); + updateWindowTitle(); + } rightClickable + break; + } + case DIV_SYSTEM_X1_010: { + ImGui::Text("Clock rate:"); + if (ImGui::RadioButton("16MHz (Seta 1)",(flags&15)==0)) { + e->setSysFlags(i,(flags&(~15))|0,restart); + updateWindowTitle(); + } + if (ImGui::RadioButton("16.67MHz (Seta 2)",(flags&15)==1)) { + e->setSysFlags(i,(flags&(~15))|1,restart); + updateWindowTitle(); + } + bool x1_010Stereo=flags&16; + if (ImGui::Checkbox("Stereo",&x1_010Stereo)) { + e->setSysFlags(i,(flags&(~16))|(x1_010Stereo<<4),restart); + updateWindowTitle(); + } + break; + } + case DIV_SYSTEM_GB: + case DIV_SYSTEM_SWAN: + case DIV_SYSTEM_VERA: + case DIV_SYSTEM_BUBSYS_WSG: + case DIV_SYSTEM_YM2610: + case DIV_SYSTEM_YM2610_EXT: + case DIV_SYSTEM_YM2610_FULL: + case DIV_SYSTEM_YM2610_FULL_EXT: + case DIV_SYSTEM_YM2610B: + case DIV_SYSTEM_YM2610B_EXT: + case DIV_SYSTEM_YMU759: + case DIV_SYSTEM_PET: + ImGui::Text("nothing to configure"); + break; + default: + if (ImGui::Checkbox("PAL",&sysPal)) { + e->setSysFlags(i,sysPal,restart); + updateWindowTitle(); + } + break; + } +} \ No newline at end of file diff --git a/src/gui/volMeter.cpp b/src/gui/volMeter.cpp new file mode 100644 index 000000000..29f62c969 --- /dev/null +++ b/src/gui/volMeter.cpp @@ -0,0 +1,111 @@ +/** + * Furnace Tracker - multi-system chiptune tracker + * Copyright (C) 2021-2022 tildearrow and contributors + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "gui.h" +#include "imgui_internal.h" + +void FurnaceGUI::drawVolMeter() { + if (nextWindow==GUI_WINDOW_VOL_METER) { + volMeterOpen=true; + ImGui::SetNextWindowFocus(); + nextWindow=GUI_WINDOW_NOTHING; + } + if (!volMeterOpen) return; + if (--isClipping<0) isClipping=0; + ImGui::SetNextWindowSizeConstraints(ImVec2(6.0f*dpiScale,6.0f*dpiScale),ImVec2(scrW*dpiScale,scrH*dpiScale)); + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding,ImVec2(0,0)); + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding,ImVec2(0,0)); + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing,ImVec2(0,0)); + ImGui::PushStyleVar(ImGuiStyleVar_ItemInnerSpacing,ImVec2(0,0)); + if (ImGui::Begin("Volume Meter",&volMeterOpen)) { + ImDrawList* dl=ImGui::GetWindowDrawList(); + bool aspectRatio=(ImGui::GetWindowSize().x/ImGui::GetWindowSize().y)>1.0; + + ImVec2 minArea=ImVec2( + ImGui::GetWindowPos().x+ImGui::GetCursorPos().x, + ImGui::GetWindowPos().y+ImGui::GetCursorPos().y + ); + ImVec2 maxArea=ImVec2( + ImGui::GetWindowPos().x+ImGui::GetCursorPos().x+ImGui::GetContentRegionAvail().x, + ImGui::GetWindowPos().y+ImGui::GetCursorPos().y+ImGui::GetContentRegionAvail().y + ); + ImRect rect=ImRect(minArea,maxArea); + ImGuiStyle& style=ImGui::GetStyle(); + ImGui::ItemSize(ImVec2(4.0f,4.0f),style.FramePadding.y); + ImU32 lowColor=ImGui::GetColorU32(uiColors[GUI_COLOR_VOLMETER_LOW]); + float peakDecay=0.05f*60.0f*ImGui::GetIO().DeltaTime; + if (ImGui::ItemAdd(rect,ImGui::GetID("volMeter"))) { + ImGui::RenderFrame(rect.Min,rect.Max,ImGui::GetColorU32(ImGuiCol_FrameBg),true,style.FrameRounding); + for (int i=0; i<2; i++) { + peak[i]*=1.0-peakDecay; + if (peak[i]<0.0001) peak[i]=0.0; + for (int j=0; joscSize; j++) { + if (fabs(e->oscBuf[i][j])>peak[i]) { + peak[i]=fabs(e->oscBuf[i][j]); + } + } + float logPeak=(20*log10(peak[i])/36.0); + if (logPeak==NAN) logPeak=0.0; + if (logPeak<-1.0) logPeak=-1.0; + if (logPeak>0.0) { + isClipping=8; + logPeak=0.0; + } + logPeak+=1.0; + ImU32 highColor=ImGui::GetColorU32( + ImLerp(uiColors[GUI_COLOR_VOLMETER_LOW],uiColors[GUI_COLOR_VOLMETER_HIGH],logPeak) + ); + ImRect s; + if (aspectRatio) { + s=ImRect( + ImLerp(rect.Min,rect.Max,ImVec2(0,float(i)*0.5)), + ImLerp(rect.Min,rect.Max,ImVec2(logPeak,float(i+1)*0.5)) + ); + if (i==0) s.Max.y-=dpiScale; + if (isClipping) { + dl->AddRectFilled(s.Min,s.Max,ImGui::GetColorU32(uiColors[GUI_COLOR_VOLMETER_PEAK])); + } else { + dl->AddRectFilledMultiColor(s.Min,s.Max,lowColor,highColor,highColor,lowColor); + } + } else { + s=ImRect( + ImLerp(rect.Min,rect.Max,ImVec2(float(i)*0.5,1.0-logPeak)), + ImLerp(rect.Min,rect.Max,ImVec2(float(i+1)*0.5,1.0)) + ); + if (i==0) s.Max.x-=dpiScale; + if (isClipping) { + dl->AddRectFilled(s.Min,s.Max,ImGui::GetColorU32(uiColors[GUI_COLOR_VOLMETER_PEAK])); + } else { + dl->AddRectFilledMultiColor(s.Min,s.Max,highColor,highColor,lowColor,lowColor); + } + } + } + if (ImGui::IsItemHovered()) { + if (aspectRatio) { + ImGui::SetTooltip("%.1fdB",36*((ImGui::GetMousePos().x-ImGui::GetItemRectMin().x)/(rect.Max.x-rect.Min.x)-1.0)); + } else { + ImGui::SetTooltip("%.1fdB",-(36+36*((ImGui::GetMousePos().y-ImGui::GetItemRectMin().y)/(rect.Max.y-rect.Min.y)-1.0))); + } + } + } + } + ImGui::PopStyleVar(4); + if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_VOL_METER; + ImGui::End(); +} \ No newline at end of file diff --git a/src/gui/waveEdit.cpp b/src/gui/waveEdit.cpp index 09ec95b1c..3fba43f70 100644 --- a/src/gui/waveEdit.cpp +++ b/src/gui/waveEdit.cpp @@ -1,3 +1,22 @@ +/** + * 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 "plot_nolerp.h" #include "misc/cpp/imgui_stdlib.h"