furnace/src/gui/gui.cpp

7459 lines
232 KiB
C++
Raw Normal View History

#define _USE_MATH_DEFINES
// OK, sorry for inserting the define here but I'm so tired of this extension
2022-02-15 03:12:20 +00:00
/**
* Furnace Tracker - multi-system chiptune tracker
2023-01-20 00:18:40 +00:00
* Copyright (C) 2021-2023 tildearrow and contributors
2022-02-15 03:12:20 +00:00
*
* 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.
*/
2022-09-08 06:49:36 +00:00
// I hate you clangd extension!
// how about you DON'T insert random headers before this freaking important
// define!!!!!!
2021-12-11 07:10:09 +00:00
#include "gui.h"
#include "util.h"
2021-12-11 08:11:40 +00:00
#include "../ta-log.h"
#include "../fileutils.h"
2021-12-11 08:11:40 +00:00
#include "imgui.h"
#include "imgui_internal.h"
#include "ImGuiFileDialog.h"
2021-12-21 05:30:55 +00:00
#include "IconsFontAwesome4.h"
2021-12-11 22:41:32 +00:00
#include "misc/cpp/imgui_stdlib.h"
#include "plot_nolerp.h"
2022-02-17 18:08:17 +00:00
#include "guiConst.h"
#include "intConst.h"
#include "scaling.h"
2023-02-23 10:56:48 +00:00
#include "introTune.h"
2022-01-18 02:08:14 +00:00
#include <stdint.h>
#include <zlib.h>
2021-12-11 21:44:02 +00:00
#include <fmt/printf.h>
2021-12-14 22:45:37 +00:00
#include <stdexcept>
2021-12-11 07:10:09 +00:00
#ifdef _WIN32
#include <windows.h>
#include <shlobj.h>
#include <shlwapi.h>
#include "../utfutils.h"
2021-12-19 08:16:24 +00:00
#define LAYOUT_INI "\\layout.ini"
2023-04-06 09:22:43 +00:00
#define BACKUPS_DIR "\\backups"
#else
2023-04-06 09:22:43 +00:00
#include <sys/types.h>
#include <dirent.h>
#include <unistd.h>
#include <pwd.h>
#include <sys/stat.h>
2021-12-19 08:16:24 +00:00
#define LAYOUT_INI "/layout.ini"
2023-04-06 09:22:43 +00:00
#define BACKUPS_DIR "/backups"
#endif
2022-05-11 21:09:23 +00:00
#ifdef IS_MOBILE
#define MOBILE_UI_DEFAULT true
#else
#define MOBILE_UI_DEFAULT false
#endif
#include "actionUtil.h"
bool Particle::update(float frameTime) {
pos.x+=speed.x*frameTime;
pos.y+=speed.y*frameTime;
speed.x*=1.0-((1.0-friction)*frameTime);
speed.y*=1.0-((1.0-friction)*frameTime);
speed.y+=gravity*frameTime;
life-=lifeSpeed*frameTime;
2022-02-15 23:52:12 +00:00
return (life>0);
}
2023-08-10 07:42:42 +00:00
void FurnaceGUI::centerNextWindow(const char* name, float w, float h) {
if (ImGui::IsPopupOpen(name)) {
if (settings.centerPopup) {
ImGui::SetNextWindowPos(ImVec2(w*0.5,h*0.5),ImGuiCond_Always,ImVec2(0.5,0.5));
}
}
2023-07-12 13:16:39 +00:00
}
2021-12-11 07:10:09 +00:00
void FurnaceGUI::bindEngine(DivEngine* eng) {
e=eng;
2022-05-21 23:36:15 +00:00
wavePreview.setEngine(e);
2021-12-11 07:10:09 +00:00
}
2021-12-13 07:03:36 +00:00
const char* FurnaceGUI::noteName(short note, short octave) {
if (note==100) {
return noteOffLabel;
} else if (note==101) { // note off and envelope release
return noteRelLabel;
} else if (note==102) { // envelope release only
return macroRelLabel;
2021-12-13 07:03:36 +00:00
} else if (octave==0 && note==0) {
return emptyLabel;
2022-02-20 04:11:20 +00:00
} else if (note==0 && octave!=0) {
return "BUG";
2021-12-13 07:03:36 +00:00
}
2022-01-18 21:55:32 +00:00
int seek=(note+(signed char)octave*12)+60;
if (seek<0 || seek>=180) {
return "???";
}
2023-07-24 19:51:41 +00:00
if (settings.flatNotes) {
2023-07-22 16:44:02 +00:00
if (settings.germanNotation) return noteNamesGF[seek];
return noteNamesF[seek];
}
2022-02-03 19:40:09 +00:00
if (settings.germanNotation) return noteNamesG[seek];
2021-12-13 07:03:36 +00:00
return noteNames[seek];
}
2021-12-19 07:12:19 +00:00
bool FurnaceGUI::decodeNote(const char* what, short& note, short& octave) {
if (strlen(what)!=3) return false;
if (strcmp(what,"...")==0) {
note=0;
octave=0;
return true;
}
if (strcmp(what,"???")==0) {
note=0;
octave=0;
return true;
}
if (strcmp(what,"OFF")==0) {
note=100;
octave=0;
return true;
}
if (strcmp(what,"===")==0) {
note=101;
octave=0;
return true;
}
if (strcmp(what,"REL")==0) {
note=102;
octave=0;
return true;
}
for (int i=0; i<180; i++) {
2021-12-19 07:12:19 +00:00
if (strcmp(what,noteNames[i])==0) {
if ((i%12)==0) {
note=12;
2022-01-19 09:32:40 +00:00
octave=(unsigned char)((i/12)-6);
2021-12-19 07:12:19 +00:00
} else {
note=i%12;
2022-01-19 09:32:40 +00:00
octave=(unsigned char)((i/12)-5);
2021-12-19 07:12:19 +00:00
}
return true;
}
}
return false;
}
String FurnaceGUI::encodeKeyMap(std::map<int,int>& map) {
String ret;
for (std::map<int,int>::value_type& i: map) {
2022-02-21 23:01:41 +00:00
ret+=fmt::sprintf("%d:%d;",i.first,i.second);
}
return ret;
}
void FurnaceGUI::decodeKeyMap(std::map<int,int>& map, String source) {
map.clear();
bool inValue=false;
bool negateKey=false;
bool negateValue=false;
int key=0;
int val=0;
for (char& i: source) {
switch (i) {
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
if (inValue) {
val*=10;
val+=i-'0';
} else {
key*=10;
key+=i-'0';
}
break;
case '-':
if (inValue) {
negateValue=true;
} else {
negateKey=true;
}
break;
case ':':
inValue=true;
break;
case ';':
if (inValue) {
map[negateKey?-key:key]=negateValue?-val:val;
}
key=0;
val=0;
inValue=false;
negateKey=false;
negateValue=false;
break;
}
}
}
void FurnaceGUI::encodeMMLStr(String& target, int* macro, int macroLen, int macroLoop, int macroRel, bool hex, bool bit30) {
target="";
char buf[32];
for (int i=0; i<macroLen; i++) {
if (i==macroLoop) target+="| ";
if (i==macroRel) target+="/ ";
if (bit30 && ((macro[i]&0xc0000000)==0x40000000 || (macro[i]&0xc0000000)==0x80000000)) target+="@";
int macroVal=macro[i];
if (macro[i]<0) {
if (!(macroVal&0x40000000)) macroVal|=0x40000000;
} else {
if (macroVal&0x40000000) macroVal&=~0x40000000;
}
if (hex) {
if (i==macroLen-1) {
snprintf(buf,31,"%.2X",macroVal);
} else {
snprintf(buf,31,"%.2X ",macroVal);
}
} else {
if (i==macroLen-1) {
snprintf(buf,31,"%d",macroVal);
} else {
snprintf(buf,31,"%d ",macroVal);
}
}
target+=buf;
}
}
2022-09-11 03:35:21 +00:00
void FurnaceGUI::decodeMMLStrW(String& source, int* macro, int& macroLen, int macroMin, int macroMax, bool hex) {
int buf=0;
bool negaBuf=false;
bool hasVal=false;
macroLen=0;
for (char& i: source) {
switch (i) {
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
hasVal=true;
buf*=hex?16:10;
buf+=i-'0';
break;
case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
if (hex) {
hasVal=true;
buf*=16;
buf+=10+i-'A';
}
break;
case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
if (hex) {
hasVal=true;
buf*=16;
buf+=10+i-'a';
}
break;
case '-':
if (!hasVal) {
hasVal=true;
negaBuf=true;
}
break;
case ' ':
if (hasVal) {
hasVal=false;
macro[macroLen]=negaBuf?-buf:buf;
2022-09-11 03:35:21 +00:00
negaBuf=false;
if (macro[macroLen]<macroMin) macro[macroLen]=macroMin;
if (macro[macroLen]>macroMax) macro[macroLen]=macroMax;
macroLen++;
buf=0;
}
break;
}
if (macroLen>=256) break;
}
if (hasVal && macroLen<256) {
hasVal=false;
macro[macroLen]=negaBuf?-buf:buf;
2022-09-11 03:35:21 +00:00
negaBuf=false;
if (macro[macroLen]<macroMin) macro[macroLen]=macroMin;
if (macro[macroLen]>macroMax) macro[macroLen]=macroMax;
macroLen++;
buf=0;
}
}
void FurnaceGUI::decodeMMLStr(String& source, int* macro, unsigned char& macroLen, unsigned char& macroLoop, int macroMin, int macroMax, unsigned char& macroRel, bool bit30) {
int buf=0;
bool negaBuf=false;
bool setBit30=false;
bool hasVal=false;
macroLen=0;
macroLoop=255;
macroRel=255;
for (char& i: source) {
switch (i) {
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
hasVal=true;
buf*=10;
buf+=i-'0';
break;
case '-':
if (!hasVal) {
hasVal=true;
negaBuf=true;
}
break;
case '@':
if (bit30) {
setBit30=true;
}
break;
case ' ':
if (hasVal) {
hasVal=false;
macro[macroLen]=negaBuf?-buf:buf;
negaBuf=false;
if (macro[macroLen]<macroMin) macro[macroLen]=macroMin;
if (macro[macroLen]>macroMax) macro[macroLen]=macroMax;
if (setBit30) macro[macroLen]^=0x40000000;
setBit30=false;
macroLen++;
buf=0;
}
break;
case '|':
if (hasVal) {
hasVal=false;
macro[macroLen]=negaBuf?-buf:buf;
negaBuf=false;
if (macro[macroLen]<macroMin) macro[macroLen]=macroMin;
if (macro[macroLen]>macroMax) macro[macroLen]=macroMax;
if (setBit30) macro[macroLen]^=0x40000000;
setBit30=false;
macroLen++;
buf=0;
}
if (macroLoop==255) {
macroLoop=macroLen;
}
break;
case '/':
if (hasVal) {
hasVal=false;
macro[macroLen]=negaBuf?-buf:buf;
negaBuf=false;
if (macro[macroLen]<macroMin) macro[macroLen]=macroMin;
if (macro[macroLen]>macroMax) macro[macroLen]=macroMax;
if (setBit30) macro[macroLen]^=0x40000000;
setBit30=false;
macroLen++;
buf=0;
}
if (macroRel==255) {
macroRel=macroLen;
}
break;
}
if (macroLen>=255) break;
}
if (hasVal && macroLen<255) {
hasVal=false;
macro[macroLen]=negaBuf?-buf:buf;
negaBuf=false;
if (macro[macroLen]<macroMin) macro[macroLen]=macroMin;
if (macro[macroLen]>macroMax) macro[macroLen]=macroMax;
if (setBit30) macro[macroLen]^=0x40000000;
setBit30=false;
macroLen++;
buf=0;
}
}
#define CW_ADDITION(T) \
if (p_min!=NULL && p_max!=NULL) { \
if (*((T*)p_min)>*((T*)p_max)) { \
if (wheelY<0) { \
if ((*((T*)p_data)-wheelY)>*((T*)p_min)) { \
*((T*)p_data)=*((T*)p_min); \
} else { \
*((T*)p_data)-=wheelY; \
} \
} else { \
if ((*((T*)p_data)-wheelY)<*((T*)p_max)) { \
*((T*)p_data)=*((T*)p_max); \
} else { \
*((T*)p_data)-=wheelY; \
} \
} \
} else { \
if (wheelY>0) { \
if ((*((T*)p_data)+wheelY)>*((T*)p_max)) { \
*((T*)p_data)=*((T*)p_max); \
} else { \
*((T*)p_data)+=wheelY; \
} \
} else { \
if ((*((T*)p_data)+wheelY)<*((T*)p_min)) { \
*((T*)p_data)=*((T*)p_min); \
} else { \
*((T*)p_data)+=wheelY; \
} \
} \
} \
}
bool FurnaceGUI::CWSliderScalar(const char* label, ImGuiDataType data_type, void* p_data, const void* p_min, const void* p_max, const char* format, ImGuiSliderFlags flags) {
2023-08-31 06:52:11 +00:00
flags^=ImGuiSliderFlags_AlwaysClamp;
if (ImGui::SliderScalar(label,data_type,p_data,p_min,p_max,format,flags)) {
return true;
}
if (ImGui::IsItemHovered() && ctrlWheeling) {
switch (data_type) {
case ImGuiDataType_U8:
CW_ADDITION(unsigned char);
break;
case ImGuiDataType_S8:
CW_ADDITION(signed char);
break;
case ImGuiDataType_U16:
CW_ADDITION(unsigned short);
break;
case ImGuiDataType_S16:
CW_ADDITION(short);
break;
case ImGuiDataType_U32:
CW_ADDITION(unsigned int);
break;
case ImGuiDataType_S32:
CW_ADDITION(int);
break;
case ImGuiDataType_Float:
CW_ADDITION(float);
break;
case ImGuiDataType_Double:
CW_ADDITION(double);
break;
}
return true;
}
return false;
}
bool FurnaceGUI::CWVSliderScalar(const char* label, const ImVec2& size, ImGuiDataType data_type, void* p_data, const void* p_min, const void* p_max, const char* format, ImGuiSliderFlags flags) {
2023-08-31 06:52:11 +00:00
flags^=ImGuiSliderFlags_AlwaysClamp;
if (ImGui::VSliderScalar(label,size,data_type,p_data,p_min,p_max,format,flags)) {
return true;
}
if (ImGui::IsItemHovered() && ctrlWheeling) {
switch (data_type) {
case ImGuiDataType_U8:
CW_ADDITION(unsigned char);
break;
case ImGuiDataType_S8:
CW_ADDITION(signed char);
break;
case ImGuiDataType_U16:
CW_ADDITION(unsigned short);
break;
case ImGuiDataType_S16:
CW_ADDITION(short);
break;
case ImGuiDataType_U32:
CW_ADDITION(unsigned int);
break;
case ImGuiDataType_S32:
CW_ADDITION(int);
break;
case ImGuiDataType_Float:
CW_ADDITION(float);
break;
case ImGuiDataType_Double:
CW_ADDITION(double);
break;
}
return true;
}
return false;
}
bool FurnaceGUI::CWSliderInt(const char* label, int* v, int v_min, int v_max, const char* format, ImGuiSliderFlags flags) {
return CWSliderScalar(label,ImGuiDataType_S32,v,&v_min,&v_max,format,flags);
}
bool FurnaceGUI::CWSliderFloat(const char* label, float* v, float v_min, float v_max, const char* format, ImGuiSliderFlags flags) {
return CWSliderScalar(label,ImGuiDataType_Float,v,&v_min,&v_max,format,flags);
}
bool FurnaceGUI::CWVSliderInt(const char* label, const ImVec2& size, int* v, int v_min, int v_max, const char* format, ImGuiSliderFlags flags) {
return CWVSliderScalar(label,size,ImGuiDataType_S32,v,&v_min,&v_max,format,flags);
}
bool FurnaceGUI::InvCheckbox(const char* label, bool* value) {
bool t=!(*value);
if (ImGui::Checkbox(label,&t)) {
*value=!t;
return true;
}
return false;
}
void FurnaceGUI::sameLineMaybe(float width) {
if (width<0.0f) width=ImGui::GetFrameHeight();
ImGui::SameLine();
if (ImGui::GetContentRegionAvail().x<width) ImGui::NewLine();
}
2022-01-28 08:17:35 +00:00
const char* FurnaceGUI::getSystemName(DivSystem which) {
/*
2022-01-28 08:17:35 +00:00
if (settings.chipNames) {
return e->getSystemChips(which);
}
*/
2022-01-28 08:17:35 +00:00
return e->getSystemName(which);
}
2021-12-13 22:09:46 +00:00
void FurnaceGUI::updateScroll(int amount) {
2022-01-22 08:15:43 +00:00
float lineHeight=(patFont->FontSize+2*dpiScale);
2021-12-13 22:09:46 +00:00
nextScroll=lineHeight*amount;
2022-04-13 23:16:55 +00:00
haveHitBounds=false;
2021-12-13 22:09:46 +00:00
}
void FurnaceGUI::addScroll(int amount) {
2022-01-22 08:15:43 +00:00
float lineHeight=(patFont->FontSize+2*dpiScale);
nextAddScroll=lineHeight*amount;
2022-04-13 23:16:55 +00:00
haveHitBounds=false;
}
void FurnaceGUI::setFileName(String name) {
#ifdef _WIN32
wchar_t ret[4096];
WString ws=utf8To16(name.c_str());
int index=0;
for (wchar_t& i: ws) {
ret[index++]=i;
if (index>=4095) break;
}
ret[index]=0;
2023-04-06 21:24:44 +00:00
backupLock.lock();
if (GetFullPathNameW(ws.c_str(),4095,ret,NULL)==0) {
curFileName=name;
} else {
curFileName=utf16To8(ret);
}
2023-04-06 21:24:44 +00:00
backupLock.unlock();
#else
char ret[4096];
2023-04-06 21:24:44 +00:00
backupLock.lock();
if (realpath(name.c_str(),ret)==NULL) {
curFileName=name;
} else {
curFileName=ret;
}
2023-04-06 21:24:44 +00:00
backupLock.unlock();
#endif
updateWindowTitle();
2022-09-10 23:53:27 +00:00
pushRecentFile(curFileName);
2023-02-19 05:08:37 +00:00
if (settings.alwaysPlayIntro==2) shortIntro=true;
}
2021-12-15 22:32:08 +00:00
void FurnaceGUI::updateWindowTitle() {
String title;
switch (settings.titleBarInfo) {
case 0:
title="Furnace";
break;
case 1:
if (e->song.name.empty()) {
title="Furnace";
} else {
title=fmt::sprintf("%s - Furnace",e->song.name);
}
break;
case 2:
if (curFileName.empty()) {
title="Furnace";
} else {
String shortName;
size_t pos=curFileName.rfind(DIR_SEPARATOR);
if (pos==String::npos) {
shortName=curFileName;
} else {
shortName=curFileName.substr(pos+1);
}
title=fmt::sprintf("%s - Furnace",shortName);
}
break;
case 3:
if (curFileName.empty()) {
title="Furnace";
} else {
title=fmt::sprintf("%s - Furnace",curFileName);
}
break;
2021-12-15 22:32:08 +00:00
}
if (settings.titleBarSys) {
if (e->song.systemName!="") {
title+=fmt::sprintf(" (%s)",e->song.systemName);
}
}
if (sdlWin!=NULL) SDL_SetWindowTitle(sdlWin,title.c_str());
2021-12-15 22:32:08 +00:00
}
2022-09-22 00:27:42 +00:00
void FurnaceGUI::autoDetectSystem() {
std::map<DivSystem,int> sysCountMap;
std::map<DivSystem,DivConfig> sysConfMap;
2022-09-22 00:27:42 +00:00
for (int i=0; i<e->song.systemLen; i++) {
auto it=sysCountMap.find(e->song.system[i]);
if (it==sysCountMap.cend()) {
2022-09-22 00:27:42 +00:00
sysCountMap[e->song.system[i]]=1;
} else {
it->second++;
2022-09-22 00:27:42 +00:00
}
sysConfMap[e->song.system[i]]=e->song.systemFlags[i];
2022-09-22 00:27:42 +00:00
}
logV("sysCountMap:");
for (std::pair<DivSystem,int> k: sysCountMap) {
logV("%s: %d",e->getSystemName(k.first),k.second);
}
bool isMatch=false;
std::map<DivSystem,int> defCountMap;
std::map<DivSystem,DivConfig> defConfMap;
2022-09-22 00:27:42 +00:00
for (FurnaceGUISysCategory& i: sysCategories) {
for (FurnaceGUISysDef& j: i.systems) {
defCountMap.clear();
defConfMap.clear();
for (FurnaceGUISysDefChip& k: j.orig) {
auto it=defCountMap.find(k.sys);
if (it==defCountMap.cend()) {
defCountMap[k.sys]=1;
} else {
it->second++;
2022-09-22 00:27:42 +00:00
}
DivConfig dc;
dc.loadFromMemory(k.flags);
defConfMap[k.sys]=dc;
2022-09-22 00:27:42 +00:00
}
if (defCountMap.size()!=sysCountMap.size()) continue;
isMatch=true;
2022-12-29 04:43:58 +00:00
/*logV("trying on defCountMap: %s",j.name);
2022-09-22 00:27:42 +00:00
for (std::pair<DivSystem,int> k: defCountMap) {
logV("- %s: %d",e->getSystemName(k.first),k.second);
2022-12-29 04:43:58 +00:00
}*/
2022-09-22 00:27:42 +00:00
for (std::pair<DivSystem,int> k: defCountMap) {
auto countI=sysCountMap.find(k.first);
if (countI==sysCountMap.cend()) {
isMatch=false;
break;
} else if (countI->second!=k.second) {
isMatch=false;
break;
}
auto confI=sysConfMap.find(k.first);
if (confI==sysConfMap.cend()) {
isMatch=false;
break;
}
DivConfig& sysDC=confI->second;
auto defConfI=defConfMap.find(k.first);
if (defConfI==defConfMap.cend()) {
isMatch=false;
break;
}
for (std::pair<String,String> l: defConfI->second.configMap()) {
if (!sysDC.has(l.first)) {
2022-09-22 00:27:42 +00:00
isMatch=false;
break;
}
if (sysDC.getString(l.first,"")!=l.second) {
isMatch=false;
break;
}
2022-09-22 00:27:42 +00:00
}
if (!isMatch) break;
2022-09-22 00:27:42 +00:00
}
if (isMatch) {
logV("match found!");
e->song.systemName=j.name;
break;
}
}
if (isMatch) break;
}
if (!isMatch) {
bool isFirst=true;
e->song.systemName="";
for (std::pair<DivSystem,int> k: sysCountMap) {
if (!isFirst) e->song.systemName+=" + ";
if (k.second>1) {
e->song.systemName+=fmt::sprintf("%d×",k.second);
}
2023-07-20 21:50:19 +00:00
e->song.systemName+=e->getSystemName(k.first);
2022-09-22 00:27:42 +00:00
isFirst=false;
}
}
}
2022-09-18 08:51:10 +00:00
ImVec4 FurnaceGUI::channelColor(int ch) {
switch (settings.channelColors) {
case 0:
return uiColors[GUI_COLOR_CHANNEL_BG];
break;
case 1:
return uiColors[GUI_COLOR_CHANNEL_FM+e->getChannelType(ch)];
break;
case 2:
return uiColors[GUI_COLOR_INSTR_STD+e->getPreferInsType(ch)];
break;
}
// invalid
return uiColors[GUI_COLOR_TEXT];
}
ImVec4 FurnaceGUI::channelTextColor(int ch) {
switch (settings.channelTextColors) {
case 0:
return uiColors[GUI_COLOR_CHANNEL_FG];
break;
case 1:
return uiColors[GUI_COLOR_CHANNEL_FM+e->getChannelType(ch)];
break;
case 2:
return uiColors[GUI_COLOR_INSTR_STD+e->getPreferInsType(ch)];
break;
}
// invalid
return uiColors[GUI_COLOR_TEXT];
}
2021-12-19 21:01:24 +00:00
const char* defaultLayout="[Window][DockSpaceViewport_11111111]\n\
Pos=0,24\n\
2022-09-22 09:27:18 +00:00
Size=1280,776\n\
2021-12-19 21:01:24 +00:00
Collapsed=0\n\
\n\
[Window][Debug##Default]\n\
2023-02-24 09:26:17 +00:00
Pos=54,43\n\
2021-12-19 21:01:24 +00:00
Size=400,400\n\
Collapsed=0\n\
\n\
[Window][Play/Edit Controls]\n\
2022-03-14 22:40:22 +00:00
Pos=181,208\n\
Size=45,409\n\
2021-12-19 21:01:24 +00:00
Collapsed=0\n\
\n\
[Window][Song Information]\n\
2022-03-14 22:40:22 +00:00
Pos=978,24\n\
2022-09-22 09:27:18 +00:00
Size=302,179\n\
2021-12-19 21:01:24 +00:00
Collapsed=0\n\
2022-09-22 09:27:18 +00:00
DockId=0x0000000F,0\n\
2021-12-19 21:01:24 +00:00
\n\
[Window][Orders]\n\
Pos=0,24\n\
2022-03-14 22:40:22 +00:00
Size=345,217\n\
2021-12-19 21:01:24 +00:00
Collapsed=0\n\
2022-03-14 22:40:22 +00:00
DockId=0x00000007,0\n\
2021-12-19 21:01:24 +00:00
\n\
[Window][Instruments]\n\
2022-03-14 22:40:22 +00:00
Pos=653,24\n\
Size=323,217\n\
2021-12-19 21:01:24 +00:00
Collapsed=0\n\
2022-09-22 09:27:18 +00:00
DockId=0x00000006,0\n\
2021-12-19 21:01:24 +00:00
\n\
[Window][Wavetables]\n\
2022-03-14 22:40:22 +00:00
Pos=653,24\n\
Size=323,217\n\
2021-12-19 21:01:24 +00:00
Collapsed=0\n\
2022-03-14 22:40:22 +00:00
DockId=0x00000006,1\n\
2021-12-19 21:01:24 +00:00
\n\
[Window][Samples]\n\
2022-03-14 22:40:22 +00:00
Pos=653,24\n\
Size=323,217\n\
2021-12-19 21:01:24 +00:00
Collapsed=0\n\
2022-09-22 09:27:18 +00:00
DockId=0x00000006,2\n\
2021-12-19 21:01:24 +00:00
\n\
[Window][Pattern]\n\
2022-03-14 22:40:22 +00:00
Pos=0,243\n\
2023-02-24 09:26:17 +00:00
Size=939,557\n\
2021-12-19 21:01:24 +00:00
Collapsed=0\n\
2023-02-24 09:26:17 +00:00
DockId=0x00000017,0\n\
2021-12-19 21:01:24 +00:00
\n\
2022-01-30 23:17:46 +00:00
[Window][Instrument Editor]\n\
2023-02-24 09:26:17 +00:00
Pos=229,126\n\
Size=856,652\n\
2022-01-30 23:17:46 +00:00
Collapsed=0\n\
\n\
[Window][Warning]\n\
2022-03-14 22:40:22 +00:00
Pos=481,338\n\
2022-09-22 09:27:18 +00:00
Size=264,86\n\
2022-01-30 23:17:46 +00:00
Collapsed=0\n\
\n\
[Window][Sample Editor]\n\
2023-02-24 09:26:17 +00:00
Pos=47,216\n\
Size=1075,525\n\
2022-01-30 23:17:46 +00:00
Collapsed=0\n\
\n\
[Window][About Furnace]\n\
Size=1280,755\n\
Collapsed=0\n\
\n\
[Window][Save File##FileDialog]\n\
Pos=340,177\n\
Size=600,400\n\
Collapsed=0\n\
\n\
2022-03-14 22:40:22 +00:00
[Window][Wavetable Editor]\n\
Pos=253,295\n\
Size=748,378\n\
2022-01-30 23:17:46 +00:00
Collapsed=0\n\
\n\
2022-03-14 22:40:22 +00:00
[Window][Settings]\n\
Pos=655,224\n\
2023-02-24 09:26:17 +00:00
Size=1280,941\n\
2022-03-14 22:40:22 +00:00
Collapsed=0\n\
\n\
[Window][Error]\n\
Pos=491,342\n\
Size=514,71\n\
Collapsed=0\n\
\n\
[Window][Mixer]\n\
2022-09-22 09:27:18 +00:00
Pos=429,198\n\
Size=453,355\n\
2022-03-14 22:40:22 +00:00
Collapsed=0\n\
\n\
[Window][Oscilloscope]\n\
Pos=347,94\n\
Size=304,105\n\
Collapsed=0\n\
DockId=0x0000000E,0\n\
\n\
[Window][Volume Meter]\n\
Pos=1248,243\n\
2022-09-22 09:27:18 +00:00
Size=32,557\n\
2022-03-14 22:40:22 +00:00
Collapsed=0\n\
DockId=0x0000000C,0\n\
\n\
[Window][Debug]\n\
Pos=113,148\n\
Size=945,473\n\
Collapsed=0\n\
\n\
[Window][Load Sample##FileDialog]\n\
Pos=40,0\n\
Size=1200,755\n\
Collapsed=0\n\
\n\
[Window][Open File##FileDialog]\n\
Pos=250,143\n\
Size=779,469\n\
Collapsed=0\n\
\n\
[Window][Export Audio##FileDialog]\n\
Pos=339,177\n\
Size=601,400\n\
Collapsed=0\n\
\n\
[Window][Rendering...]\n\
Pos=585,342\n\
Size=114,71\n\
2022-01-30 23:17:46 +00:00
Collapsed=0\n\
2021-12-19 21:01:24 +00:00
\n\
2022-03-14 22:40:22 +00:00
[Window][Export VGM##FileDialog]\n\
2022-01-30 23:17:46 +00:00
Pos=340,177\n\
Size=600,400\n\
Collapsed=0\n\
\n\
2022-03-14 22:40:22 +00:00
[Window][Warning##Save FileFileDialogOverWriteDialog]\n\
Pos=390,351\n\
Size=500,71\n\
2022-01-30 23:17:46 +00:00
Collapsed=0\n\
\n\
2022-03-14 22:40:22 +00:00
[Window][Statistics]\n\
2023-02-24 09:26:17 +00:00
Pos=0,581\n\
Size=1246,219\n\
2022-01-30 23:17:46 +00:00
Collapsed=0\n\
2023-02-24 09:26:17 +00:00
DockId=0x00000016,0\n\
2022-01-30 23:17:46 +00:00
\n\
2022-03-14 22:40:22 +00:00
[Window][Warning##Export VGMFileDialogOverWriteDialog]\n\
Pos=390,351\n\
Size=500,71\n\
Collapsed=0\n\
\n\
[Window][Compatibility Flags]\n\
2023-02-24 09:26:17 +00:00
Pos=388,132\n\
Size=580,641\n\
2022-03-14 22:40:22 +00:00
Collapsed=0\n\
\n\
[Window][Song Comments]\n\
Pos=60,60\n\
Size=395,171\n\
Collapsed=0\n\
\n\
[Window][Warning##Export AudioFileDialogOverWriteDialog]\n\
Pos=381,351\n\
Size=500,71\n\
Collapsed=0\n\
\n\
[Window][Select Font##FileDialog]\n\
2022-01-30 23:17:46 +00:00
Pos=340,177\n\
Size=600,400\n\
Collapsed=0\n\
\n\
2022-03-14 22:40:22 +00:00
[Window][Channels]\n\
2022-01-30 23:17:46 +00:00
Pos=60,60\n\
2022-03-14 22:40:22 +00:00
Size=368,449\n\
2022-01-30 23:17:46 +00:00
Collapsed=0\n\
\n\
2022-03-14 22:40:22 +00:00
[Window][Register View]\n\
2022-09-22 09:27:18 +00:00
Pos=829,243\n\
Size=417,557\n\
2022-01-30 23:17:46 +00:00
Collapsed=0\n\
2022-09-22 09:27:18 +00:00
DockId=0x00000014,0\n\
2022-01-30 23:17:46 +00:00
\n\
2022-03-14 22:40:22 +00:00
[Window][New Song]\n\
Pos=267,110\n\
Size=746,534\n\
2022-01-30 23:17:46 +00:00
Collapsed=0\n\
\n\
2022-03-14 22:40:22 +00:00
[Window][Edit Controls]\n\
Pos=347,24\n\
Size=304,68\n\
Collapsed=0\n\
DockId=0x0000000D,0\n\
\n\
[Window][Play Controls]\n\
Pos=347,201\n\
Size=304,40\n\
2022-01-30 23:17:46 +00:00
Collapsed=0\n\
2022-03-14 22:40:22 +00:00
DockId=0x0000000A,0\n\
2022-01-30 23:17:46 +00:00
\n\
2022-09-22 09:27:18 +00:00
[Window][Subsongs]\n\
2023-02-24 09:26:17 +00:00
Pos=978,24\n\
Size=302,217\n\
2022-09-22 09:27:18 +00:00
Collapsed=0\n\
2023-02-24 09:26:17 +00:00
DockId=0x00000010,1\n\
2022-09-22 09:27:18 +00:00
\n\
[Window][Oscilloscope (per-channel)]\n\
Pos=1095,243\n\
Size=151,557\n\
Collapsed=0\n\
DockId=0x00000012,0\n\
\n\
[Window][Piano]\n\
Pos=177,669\n\
Size=922,118\n\
Collapsed=0\n\
\n\
[Window][Log Viewer]\n\
Pos=60,60\n\
Size=541,637\n\
Collapsed=0\n\
\n\
[Window][Pattern Manager]\n\
Pos=60,60\n\
Size=1099,366\n\
Collapsed=0\n\
\n\
[Window][Chip Manager]\n\
Pos=60,60\n\
Size=490,407\n\
Collapsed=0\n\
\n\
2023-02-24 09:26:17 +00:00
[Window][Speed]\n\
Pos=978,24\n\
Size=302,217\n\
Collapsed=0\n\
DockId=0x00000010,2\n\
\n\
[Window][Song Info##Song Information]\n\
Pos=978,24\n\
Size=302,217\n\
Collapsed=0\n\
DockId=0x00000010,0\n\
\n\
[Window][Effect List]\n\
Pos=941,243\n\
Size=305,557\n\
Collapsed=0\n\
DockId=0x00000018,0\n\
\n\
[Window][Intro]\n\
Pos=0,0\n\
Size=2560,1600\n\
Collapsed=0\n\
\n\
[Window][Welcome]\n\
Pos=944,666\n\
Size=672,268\n\
Collapsed=0\n\
\n\
[Window][Grooves]\n\
Pos=416,314\n\
Size=463,250\n\
Collapsed=0\n\
\n\
[Window][Clock]\n\
Pos=60,60\n\
Size=145,184\n\
Collapsed=0\n\
\n\
2022-01-30 23:17:46 +00:00
[Docking][Data]\n\
2022-09-22 09:27:18 +00:00
DockSpace ID=0x8B93E3BD Window=0xA787BDB4 Pos=0,24 Size=1280,776 Split=Y Selected=0x6C01C512\n\
2022-03-14 22:40:22 +00:00
DockNode ID=0x00000001 Parent=0x8B93E3BD SizeRef=1280,217 Split=X Selected=0xF3094A52\n\
DockNode ID=0x00000003 Parent=0x00000001 SizeRef=976,231 Split=X Selected=0x65CC51DC\n\
DockNode ID=0x00000007 Parent=0x00000003 SizeRef=345,231 HiddenTabBar=1 Selected=0x8F5BFC9A\n\
DockNode ID=0x00000008 Parent=0x00000003 SizeRef=629,231 Split=X Selected=0xD2AD486B\n\
DockNode ID=0x00000005 Parent=0x00000008 SizeRef=304,406 Split=Y Selected=0x6D682373\n\
DockNode ID=0x00000009 Parent=0x00000005 SizeRef=292,175 Split=Y Selected=0x6D682373\n\
DockNode ID=0x0000000D Parent=0x00000009 SizeRef=292,68 HiddenTabBar=1 Selected=0xE57B1A9D\n\
DockNode ID=0x0000000E Parent=0x00000009 SizeRef=292,105 HiddenTabBar=1 Selected=0x6D682373\n\
DockNode ID=0x0000000A Parent=0x00000005 SizeRef=292,40 HiddenTabBar=1 Selected=0x0DE44CFF\n\
2023-02-24 09:26:17 +00:00
DockNode ID=0x00000006 Parent=0x00000008 SizeRef=323,406 Selected=0xB75D68C7\n\
2022-09-22 09:27:18 +00:00
DockNode ID=0x00000004 Parent=0x00000001 SizeRef=302,231 Split=Y Selected=0x60B9D088\n\
DockNode ID=0x0000000F Parent=0x00000004 SizeRef=302,179 Selected=0x60B9D088\n\
2023-02-24 09:26:17 +00:00
DockNode ID=0x00000010 Parent=0x00000004 SizeRef=302,36 Selected=0x82BEE2E5\n\
2022-03-14 22:40:22 +00:00
DockNode ID=0x00000002 Parent=0x8B93E3BD SizeRef=1280,512 Split=X Selected=0x6C01C512\n\
2022-09-22 09:27:18 +00:00
DockNode ID=0x0000000B Parent=0x00000002 SizeRef=1246,503 Split=X Selected=0xB9ADD0D5\n\
DockNode ID=0x00000011 Parent=0x0000000B SizeRef=1093,557 Split=X Selected=0xB9ADD0D5\n\
2023-02-24 09:26:17 +00:00
DockNode ID=0x00000013 Parent=0x00000011 SizeRef=827,557 Split=Y Selected=0xB9ADD0D5\n\
DockNode ID=0x00000015 Parent=0x00000013 SizeRef=1246,336 Split=X Selected=0xB9ADD0D5\n\
DockNode ID=0x00000017 Parent=0x00000015 SizeRef=939,557 CentralNode=1 HiddenTabBar=1 Selected=0xB9ADD0D5\n\
DockNode ID=0x00000018 Parent=0x00000015 SizeRef=305,557 Selected=0xB94874DD\n\
DockNode ID=0x00000016 Parent=0x00000013 SizeRef=1246,219 Selected=0xAD8E88F2\n\
2022-09-22 09:27:18 +00:00
DockNode ID=0x00000014 Parent=0x00000011 SizeRef=417,557 Selected=0x425428FB\n\
DockNode ID=0x00000012 Parent=0x0000000B SizeRef=151,557 HiddenTabBar=1 Selected=0x4C07BC58\n\
DockNode ID=0x0000000C Parent=0x00000002 SizeRef=32,503 HiddenTabBar=1 Selected=0x644DA2C1\n";
2021-12-19 21:01:24 +00:00
void FurnaceGUI::prepareLayout() {
FILE* check;
check=ps_fopen(finalLayoutPath,"r");
2021-12-19 21:01:24 +00:00
if (check!=NULL) {
fclose(check);
return;
}
// copy initial layout
logI("loading default layout.");
check=ps_fopen(finalLayoutPath,"w");
2021-12-19 21:01:24 +00:00
if (check==NULL) {
logW("could not write default layout!");
2021-12-19 21:01:24 +00:00
return;
}
fwrite(defaultLayout,1,strlen(defaultLayout),check);
fclose(check);
}
2023-02-05 07:56:39 +00:00
float FurnaceGUI::calcBPM(const DivGroovePattern& speeds, float hz, int vN, int vD) {
float hl=e->curSubSong->hilightA;
2022-03-16 04:30:15 +00:00
if (hl<=0.0f) hl=4.0f;
float timeBase=e->curSubSong->timeBase+1;
2023-02-05 07:56:39 +00:00
float speedSum=0;
for (int i=0; i<MIN(16,speeds.len); i++) {
speedSum+=speeds.val[i];
}
speedSum/=MAX(1,speeds.len);
2022-03-16 04:30:15 +00:00
if (timeBase<1.0f) timeBase=1.0f;
if (speedSum<1.0f) speedSum=1.0f;
2022-05-18 05:05:25 +00:00
if (vD<1) vD=1;
2023-02-05 07:56:39 +00:00
return (60.0f*hz/(timeBase*hl*speedSum))*(float)vN/(float)vD;
2022-03-16 04:30:15 +00:00
}
void FurnaceGUI::play(int row) {
2023-01-19 00:22:43 +00:00
memset(chanOscVol,0,DIV_MAX_CHANS*sizeof(float));
memset(chanOscPitch,0,DIV_MAX_CHANS*sizeof(float));
memset(chanOscBright,0,DIV_MAX_CHANS*sizeof(float));
e->walkSong(loopOrder,loopRow,loopEnd);
memset(lastIns,-1,sizeof(int)*DIV_MAX_CHANS);
if (!followPattern) e->setOrder(curOrder);
if (row>0) {
e->playToRow(row);
} else {
e->play();
}
curNibble=false;
orderNibble=false;
activeNotes.clear();
}
void FurnaceGUI::setOrder(unsigned char order, bool forced) {
curOrder=order;
if (followPattern || forced) {
e->setOrder(order);
}
}
void FurnaceGUI::stop() {
bool wasPlaying=e->isPlaying();
e->walkSong(loopOrder,loopRow,loopEnd);
e->stop();
curNibble=false;
orderNibble=false;
if (followPattern && wasPlaying) {
nextScroll=-1.0f;
nextAddScroll=0.0f;
cursor.y=e->getRow();
if (selStart.xCoarse==selEnd.xCoarse && selStart.xFine==selEnd.xFine && selStart.y==selEnd.y && !selecting) {
selStart=cursor;
selEnd=cursor;
}
updateScroll(cursor.y);
}
2022-01-27 22:49:00 +00:00
}
void FurnaceGUI::previewNote(int refChan, int note, bool autoNote) {
e->setMidiBaseChan(refChan);
e->synchronized([this,note]() {
e->autoNoteOn(-1,curIns,note);
});
2022-01-29 23:56:08 +00:00
}
void FurnaceGUI::stopPreviewNote(SDL_Scancode scancode, bool autoNote) {
auto it=noteKeys.find(scancode);
if (it!=noteKeys.cend()) {
int key=it->second;
int num=12*curOctave+key;
if (num<-60) num=-60; // C-(-5)
if (num>119) num=119; // B-9
if (key==100) return;
if (key==101) return;
if (key==102) return;
e->synchronized([this,num]() {
e->autoNoteOff(-1,num);
});
}
}
2022-03-31 20:25:58 +00:00
void FurnaceGUI::noteInput(int num, int key, int vol) {
DivPattern* pat=e->curPat[cursor.xCoarse].getPattern(e->curOrders->ord[cursor.xCoarse][curOrder],true);
bool removeIns=false;
2022-05-26 05:24:21 +00:00
2022-03-30 20:44:27 +00:00
prepareUndo(GUI_UNDO_PATTERN_EDIT);
if (key==GUI_NOTE_OFF) { // note off
2022-03-30 20:44:27 +00:00
pat->data[cursor.y][0]=100;
pat->data[cursor.y][1]=0;
removeIns=true;
} else if (key==GUI_NOTE_OFF_RELEASE) { // note off + env release
2022-03-30 20:44:27 +00:00
pat->data[cursor.y][0]=101;
pat->data[cursor.y][1]=0;
removeIns=true;
} else if (key==GUI_NOTE_RELEASE) { // env release only
2022-03-30 20:44:27 +00:00
pat->data[cursor.y][0]=102;
pat->data[cursor.y][1]=0;
removeIns=true;
2022-03-30 20:44:27 +00:00
} else {
pat->data[cursor.y][0]=num%12;
pat->data[cursor.y][1]=num/12;
if (pat->data[cursor.y][0]==0) {
pat->data[cursor.y][0]=12;
pat->data[cursor.y][1]--;
}
pat->data[cursor.y][1]=(unsigned char)pat->data[cursor.y][1];
if (latchIns==-2) {
if (curIns>=(int)e->song.ins.size()) curIns=-1;
if (curIns>=0) {
pat->data[cursor.y][2]=curIns;
}
2022-03-30 20:44:27 +00:00
} else if (latchIns!=-1 && !e->song.ins.empty()) {
pat->data[cursor.y][2]=MIN(((int)e->song.ins.size())-1,latchIns);
}
2022-03-31 20:25:58 +00:00
int maxVol=e->getMaxVolumeChan(cursor.xCoarse);
2022-03-30 20:44:27 +00:00
if (latchVol!=-1) {
pat->data[cursor.y][3]=MIN(maxVol,latchVol);
2022-03-31 20:25:58 +00:00
} else if (vol!=-1) {
pat->data[cursor.y][3]=(vol*maxVol)/127;
2022-03-30 20:44:27 +00:00
}
if (latchEffect!=-1) pat->data[cursor.y][4]=latchEffect;
if (latchEffectVal!=-1) pat->data[cursor.y][5]=latchEffectVal;
}
if (removeIns) {
if (settings.removeInsOff) {
pat->data[cursor.y][2]=-1;
}
if (settings.removeVolOff) {
pat->data[cursor.y][3]=-1;
}
}
2022-03-30 20:44:27 +00:00
makeUndo(GUI_UNDO_PATTERN_EDIT);
editAdvance();
curNibble=false;
}
2022-04-01 06:50:01 +00:00
void FurnaceGUI::valueInput(int num, bool direct, int target) {
DivPattern* pat=e->curPat[cursor.xCoarse].getPattern(e->curOrders->ord[cursor.xCoarse][curOrder],true);
2022-04-01 06:50:01 +00:00
prepareUndo(GUI_UNDO_PATTERN_EDIT);
if (target==-1) target=cursor.xFine+1;
if (direct) {
pat->data[cursor.y][target]=num&0xff;
} else {
if (pat->data[cursor.y][target]==-1) pat->data[cursor.y][target]=0;
if (!settings.pushNibble && !curNibble) {
pat->data[cursor.y][target]=num;
} else {
pat->data[cursor.y][target]=((pat->data[cursor.y][target]<<4)|num)&0xff;
}
2022-04-01 06:50:01 +00:00
}
if (cursor.xFine==1) { // instrument
if (pat->data[cursor.y][target]>=(int)e->song.ins.size()) {
pat->data[cursor.y][target]&=0x0f;
if (pat->data[cursor.y][target]>=(int)e->song.ins.size()) {
pat->data[cursor.y][target]=(int)e->song.ins.size()-1;
}
}
if (settings.absorbInsInput) {
curIns=pat->data[cursor.y][target];
2022-05-21 23:36:15 +00:00
wavePreviewInit=true;
updateFMPreview=true;
}
2022-04-01 06:50:01 +00:00
makeUndo(GUI_UNDO_PATTERN_EDIT);
if (direct) {
curNibble=false;
} else {
if (e->song.ins.size()<16) {
curNibble=false;
editAdvance();
} else {
curNibble=!curNibble;
if (!curNibble) editAdvance();
}
}
} else if (cursor.xFine==2) {
if (curNibble) {
if (pat->data[cursor.y][target]>e->getMaxVolumeChan(cursor.xCoarse)) pat->data[cursor.y][target]=e->getMaxVolumeChan(cursor.xCoarse);
} else {
pat->data[cursor.y][target]&=15;
}
makeUndo(GUI_UNDO_PATTERN_EDIT);
if (direct) {
curNibble=false;
} else {
if (e->getMaxVolumeChan(cursor.xCoarse)<16) {
curNibble=false;
editAdvance();
} else {
curNibble=!curNibble;
if (!curNibble) editAdvance();
}
}
} else {
makeUndo(GUI_UNDO_PATTERN_EDIT);
if (direct) {
curNibble=false;
} else {
curNibble=!curNibble;
if (!curNibble) {
if (!settings.effectCursorDir) {
editAdvance();
} else {
2022-04-08 07:16:57 +00:00
if (settings.effectCursorDir==2) {
if (++cursor.xFine>=(3+(e->curPat[cursor.xCoarse].effectCols*2))) {
2022-04-08 07:16:57 +00:00
cursor.xFine=3;
}
} else {
2022-04-08 07:16:57 +00:00
if (cursor.xFine&1) {
cursor.xFine++;
} else {
editAdvance();
cursor.xFine--;
}
}
}
}
2022-04-01 06:50:01 +00:00
}
}
}
#define changeLatch(x) \
if (x<0) x=0; \
if (!latchNibble && !settings.pushNibble) x=0; \
x=(x<<4)|num; \
latchNibble=!latchNibble; \
if (!latchNibble) { \
if (++latchTarget>4) latchTarget=0; \
}
void FurnaceGUI::keyDown(SDL_Event& ev) {
if (introPos<11.0 && !shortIntro) return;
if (ImGuiFileDialog::Instance()->IsOpened()) return;
2022-02-12 07:14:25 +00:00
if (aboutOpen) return;
int mapped=ev.key.keysym.sym;
if (ev.key.keysym.mod&KMOD_CTRL) {
mapped|=FURKMOD_CTRL;
}
if (ev.key.keysym.mod&KMOD_ALT) {
mapped|=FURKMOD_ALT;
}
if (ev.key.keysym.mod&KMOD_GUI) {
mapped|=FURKMOD_META;
}
if (ev.key.keysym.mod&KMOD_SHIFT) {
mapped|=FURKMOD_SHIFT;
}
if (bindSetActive) {
if (!ev.key.repeat) {
switch (ev.key.keysym.sym) {
case SDLK_LCTRL: case SDLK_RCTRL:
case SDLK_LALT: case SDLK_RALT:
case SDLK_LGUI: case SDLK_RGUI:
case SDLK_LSHIFT: case SDLK_RSHIFT:
bindSetPending=false;
actionKeys[bindSetTarget]=(mapped&(~FURK_MASK))|0xffffff;
break;
default:
actionKeys[bindSetTarget]=mapped;
bindSetActive=false;
bindSetPending=false;
bindSetTarget=0;
bindSetPrevValue=0;
parseKeybinds();
break;
}
}
return;
}
if (latchTarget) {
if (mapped==SDLK_DELETE || mapped==SDLK_BACKSPACE) {
switch (latchTarget) {
case 1:
latchIns=-1;
break;
case 2:
latchVol=-1;
break;
case 3:
latchEffect=-1;
break;
case 4:
latchEffectVal=-1;
break;
}
} else {
auto it=valueKeys.find(ev.key.keysym.sym);
if (it!=valueKeys.cend()) {
int num=it->second;
switch (latchTarget) {
case 1: // instrument
changeLatch(latchIns);
break;
case 2: // volume
changeLatch(latchVol);
break;
case 3: // effect
changeLatch(latchEffect);
break;
case 4: // effect value
changeLatch(latchEffectVal);
break;
}
}
}
return;
}
if (sampleMapWaitingInput) {
if (sampleMapColumn==1) {
// TODO: map?
if (ev.key.keysym.scancode==SDL_SCANCODE_DELETE) {
alterSampleMap(true,-1);
return;
}
auto it=noteKeys.find(ev.key.keysym.scancode);
if (it!=noteKeys.cend()) {
int key=it->second;
int num=12*curOctave+key;
if (num<-60) num=-60; // C-(-5)
if (num>119) num=119; // B-9
alterSampleMap(true,num);
return;
}
} else {
// TODO: map?
if (ev.key.keysym.scancode==SDL_SCANCODE_DELETE) {
alterSampleMap(false,-1);
return;
}
auto it=valueKeys.find(ev.key.keysym.sym);
if (it!=valueKeys.cend()) {
int num=it->second;
if (num<10) {
alterSampleMap(false,num);
return;
}
}
}
}
// PER-WINDOW KEYS
2021-12-14 22:45:37 +00:00
switch (curWindow) {
case GUI_WINDOW_PATTERN: {
auto actionI=actionMapPat.find(mapped);
if (actionI!=actionMapPat.cend()) {
int action=actionI->second;
if (action>0) {
doAction(action);
return;
}
}
// pattern input otherwise
if (mapped&(FURKMOD_ALT|FURKMOD_CTRL|FURKMOD_META|FURKMOD_SHIFT)) break;
if (!ev.key.repeat) {
if (cursor.xFine==0) { // note
auto it=noteKeys.find(ev.key.keysym.scancode);
if (it!=noteKeys.cend()) {
int key=it->second;
int num=12*curOctave+key;
if (num<-60) num=-60; // C-(-5)
if (num>119) num=119; // B-9
if (edit) {
2022-03-30 20:44:27 +00:00
noteInput(num,key);
2022-03-30 23:18:39 +00:00
}
}
} else if (edit) { // value
auto it=valueKeys.find(ev.key.keysym.sym);
if (it!=valueKeys.cend()) {
int num=it->second;
2022-04-01 06:50:01 +00:00
valueInput(num);
}
}
}
break;
}
case GUI_WINDOW_ORDERS: {
auto actionI=actionMapOrders.find(mapped);
if (actionI!=actionMapOrders.cend()) {
int action=actionI->second;
if (action>0) {
doAction(action);
return;
}
}
// order input otherwise
if (mapped&(FURKMOD_ALT|FURKMOD_CTRL|FURKMOD_META|FURKMOD_SHIFT)) break;
2022-01-20 07:11:03 +00:00
if (orderEditMode!=0) {
auto it=valueKeys.find(ev.key.keysym.sym);
if (it!=valueKeys.cend()) {
int num=it->second;
2022-01-20 07:11:03 +00:00
if (orderCursor>=0 && orderCursor<e->getTotalChannelCount()) {
prepareUndo(GUI_UNDO_CHANGE_ORDER);
e->lockSave([this,num]() {
if (!curNibble && !settings.pushNibble) e->curOrders->ord[orderCursor][curOrder]=0;
e->curOrders->ord[orderCursor][curOrder]=((e->curOrders->ord[orderCursor][curOrder]<<4)|num);
});
MARK_MODIFIED;
curNibble=!curNibble;
2022-01-20 07:11:03 +00:00
if (orderEditMode==2 || orderEditMode==3) {
if (!curNibble) {
if (orderEditMode==2) {
orderCursor++;
if (orderCursor>=e->getTotalChannelCount()) orderCursor=0;
} else if (orderEditMode==3) {
if (curOrder<e->curSubSong->ordersLen-1) {
setOrder(curOrder+1);
2022-01-20 07:11:03 +00:00
}
}
}
}
2022-01-27 06:04:26 +00:00
e->walkSong(loopOrder,loopRow,loopEnd);
makeUndo(GUI_UNDO_CHANGE_ORDER);
2022-01-20 07:11:03 +00:00
}
}
}
break;
}
case GUI_WINDOW_SAMPLE_EDIT: {
auto actionI=actionMapSample.find(mapped);
if (actionI!=actionMapSample.cend()) {
int action=actionI->second;
2022-03-22 09:54:01 +00:00
if (action>0) {
doAction(action);
return;
}
}
break;
}
case GUI_WINDOW_INS_LIST: {
auto actionI=actionMapInsList.find(mapped);
if (actionI!=actionMapInsList.cend()) {
int action=actionI->second;
if (action>0) {
doAction(action);
return;
2021-12-19 07:12:19 +00:00
}
2021-12-14 22:45:37 +00:00
}
break;
}
case GUI_WINDOW_WAVE_LIST: {
auto actionI=actionMapWaveList.find(mapped);
if (actionI!=actionMapWaveList.cend()) {
int action=actionI->second;
if (action>0) {
doAction(action);
return;
}
}
break;
}
case GUI_WINDOW_SAMPLE_LIST: {
auto actionI=actionMapSampleList.find(mapped);
if (actionI!=actionMapSampleList.cend()) {
int action=actionI->second;
if (action>0) {
doAction(action);
return;
}
}
break;
}
default:
break;
}
// GLOBAL KEYS
auto actionI=actionMapGlobal.find(mapped);
if (actionI!=actionMapGlobal.cend()) {
int action=actionI->second;
if (action>0) {
doAction(action);
return;
2021-12-14 22:45:37 +00:00
}
}
2021-12-14 22:45:37 +00:00
}
void FurnaceGUI::keyUp(SDL_Event& ev) {
// nothing for now
2021-12-14 22:45:37 +00:00
}
2023-04-06 09:22:43 +00:00
bool dirExists(String s) {
return dirExists(s.c_str());
}
void FurnaceGUI::openFileDialog(FurnaceGUIFileDialogs type) {
bool hasOpened=false;
switch (type) {
case GUI_FILE_OPEN:
if (!dirExists(workingDirSong)) workingDirSong=getHomeDir();
hasOpened=fileDialog->openLoad(
"Open File",
2022-09-28 06:15:50 +00:00
{"compatible files", "*.fur *.dmf *.mod *.fc13 *.fc14 *.smod *.fc",
2023-01-28 23:36:43 +00:00
"all files", "*"},
workingDirSong,
dpiScale
);
break;
2023-04-06 20:16:52 +00:00
case GUI_FILE_OPEN_BACKUP:
if (!dirExists(backupPath)) {
showError("no backups made yet!");
break;
}
hasOpened=fileDialog->openLoad(
"Restore Backup",
{"Furnace song", "*.fur"},
2023-04-06 20:51:12 +00:00
backupPath+String(DIR_SEPARATOR_STR),
2023-04-06 20:16:52 +00:00
dpiScale
);
break;
case GUI_FILE_SAVE:
if (!dirExists(workingDirSong)) workingDirSong=getHomeDir();
hasOpened=fileDialog->openSave(
"Save File",
{"Furnace song", "*.fur"},
workingDirSong,
dpiScale
);
break;
case GUI_FILE_SAVE_DMF:
if (!dirExists(workingDirSong)) workingDirSong=getHomeDir();
hasOpened=fileDialog->openSave(
"Save File",
{"DefleMask 1.1.3 module", "*.dmf"},
workingDirSong,
dpiScale
);
break;
case GUI_FILE_SAVE_DMF_LEGACY:
if (!dirExists(workingDirSong)) workingDirSong=getHomeDir();
hasOpened=fileDialog->openSave(
"Save File",
{"DefleMask 1.0/legacy module", "*.dmf"},
workingDirSong,
dpiScale
);
break;
2022-01-19 08:15:20 +00:00
case GUI_FILE_INS_OPEN:
2022-05-05 03:55:11 +00:00
case GUI_FILE_INS_OPEN_REPLACE:
2022-04-26 06:07:28 +00:00
prevIns=-3;
2022-05-05 03:55:11 +00:00
if (prevInsData!=NULL) {
delete prevInsData;
prevInsData=NULL;
}
prevInsData=new DivInstrument;
*prevInsData=*e->getIns(curIns);
if (!dirExists(workingDirIns)) workingDirIns=getHomeDir();
hasOpened=fileDialog->openLoad(
"Load Instrument",
2022-05-17 01:49:31 +00:00
{"all compatible files", "*.fui *.dmp *.tfi *.vgi *.s3i *.sbi *.opli *.opni *.y12 *.bnk *.ff *.gyb *.opm *.wopl *.wopn",
"Furnace instrument", "*.fui",
"DefleMask preset", "*.dmp",
"TFM Music Maker instrument", "*.tfi",
"VGM Music Maker instrument", "*.vgi",
"Scream Tracker 3 instrument", "*.s3i",
"SoundBlaster instrument", "*.sbi",
"Wohlstand OPL instrument", "*.opli",
"Wohlstand OPN instrument", "*.opni",
"Gens KMod patch dump", "*.y12",
"BNK file (AdLib)", "*.bnk",
"FF preset bank", "*.ff",
"2612edit GYB preset bank", "*.gyb",
"VOPM preset bank", "*.opm",
"Wohlstand WOPL bank", "*.wopl",
"Wohlstand WOPN bank", "*.wopn",
2023-01-28 23:36:43 +00:00
"all files", "*"},
workingDirIns,
dpiScale,
[this](const char* path) {
int sampleCountBefore=e->song.sampleLen;
std::vector<DivInstrument*> instruments=e->instrumentFromFile(path,false);
if (!instruments.empty()) {
if (e->song.sampleLen!=sampleCountBefore) {
e->renderSamplesP();
}
2022-05-05 03:55:11 +00:00
if (curFileDialog==GUI_FILE_INS_OPEN_REPLACE) {
if (prevIns==-3) {
prevIns=curIns;
}
if (prevIns>=0 && prevIns<=(int)e->song.ins.size()) {
*e->song.ins[prevIns]=*instruments[0];
}
} else {
e->loadTempIns(instruments[0]);
if (curIns!=-2) {
prevIns=curIns;
}
curIns=-2;
2022-04-26 06:07:28 +00:00
}
}
for (DivInstrument* i: instruments) delete i;
2022-10-28 08:44:17 +00:00
},
(type==GUI_FILE_INS_OPEN)
);
2022-01-19 08:15:20 +00:00
break;
case GUI_FILE_INS_SAVE:
if (!dirExists(workingDirIns)) workingDirIns=getHomeDir();
hasOpened=fileDialog->openSave(
"Save Instrument",
{"Furnace instrument", "*.fui"},
workingDirIns,
dpiScale
);
break;
case GUI_FILE_INS_SAVE_DMP:
if (!dirExists(workingDirIns)) workingDirIns=getHomeDir();
hasOpened=fileDialog->openSave(
"Save Instrument",
{"DefleMask preset", "*.dmp"},
workingDirIns,
dpiScale
);
2022-01-19 08:15:20 +00:00
break;
case GUI_FILE_WAVE_OPEN:
case GUI_FILE_WAVE_OPEN_REPLACE:
if (!dirExists(workingDirWave)) workingDirWave=getHomeDir();
hasOpened=fileDialog->openLoad(
"Load Wavetable",
{"compatible files", "*.fuw *.dmw",
2023-01-28 23:36:43 +00:00
"all files", "*"},
workingDirWave,
2022-10-28 09:11:27 +00:00
dpiScale,
NULL, // TODO
(type==GUI_FILE_WAVE_OPEN)
);
2022-01-19 08:15:20 +00:00
break;
case GUI_FILE_WAVE_SAVE:
if (!dirExists(workingDirWave)) workingDirWave=getHomeDir();
hasOpened=fileDialog->openSave(
"Save Wavetable",
{"Furnace wavetable", ".fuw"},
workingDirWave,
dpiScale
);
break;
case GUI_FILE_WAVE_SAVE_DMW:
if (!dirExists(workingDirWave)) workingDirWave=getHomeDir();
hasOpened=fileDialog->openSave(
"Save Wavetable",
{"DefleMask wavetable", ".dmw"},
workingDirWave,
dpiScale
);
break;
case GUI_FILE_WAVE_SAVE_RAW:
if (!dirExists(workingDirWave)) workingDirWave=getHomeDir();
hasOpened=fileDialog->openSave(
"Save Wavetable",
{"raw data", ".raw"},
workingDirWave,
dpiScale
);
2022-01-19 08:15:20 +00:00
break;
case GUI_FILE_SAMPLE_OPEN:
case GUI_FILE_SAMPLE_OPEN_REPLACE:
if (!dirExists(workingDirSample)) workingDirSample=getHomeDir();
hasOpened=fileDialog->openLoad(
"Load Sample",
2022-09-25 06:20:08 +00:00
{"compatible files", "*.wav *.dmc *.brr",
2023-01-28 23:36:43 +00:00
"all files", "*"},
workingDirSample,
2022-10-28 09:11:27 +00:00
dpiScale,
NULL, // TODO
(type==GUI_FILE_SAMPLE_OPEN)
);
break;
case GUI_FILE_SAMPLE_OPEN_RAW:
case GUI_FILE_SAMPLE_OPEN_REPLACE_RAW:
if (!dirExists(workingDirSample)) workingDirSample=getHomeDir();
hasOpened=fileDialog->openLoad(
"Load Raw Sample",
2023-01-28 23:36:43 +00:00
{"all files", "*"},
workingDirSample,
dpiScale
);
break;
case GUI_FILE_SAMPLE_SAVE:
if (!dirExists(workingDirSample)) workingDirSample=getHomeDir();
hasOpened=fileDialog->openSave(
"Save Sample",
{"Wave file", "*.wav"},
workingDirSample,
dpiScale
);
break;
2023-05-02 08:57:25 +00:00
case GUI_FILE_SAMPLE_SAVE_RAW:
if (!dirExists(workingDirSample)) workingDirSample=getHomeDir();
hasOpened=fileDialog->openSave(
"Save Raw Sample",
2023-05-02 08:57:25 +00:00
{"all files", "*"},
workingDirSample,
dpiScale
);
break;
case GUI_FILE_EXPORT_AUDIO_ONE:
if (!dirExists(workingDirAudioExport)) workingDirAudioExport=getHomeDir();
hasOpened=fileDialog->openSave(
"Export Audio",
{"Wave file", "*.wav"},
workingDirAudioExport,
dpiScale
);
break;
case GUI_FILE_EXPORT_AUDIO_PER_SYS:
if (!dirExists(workingDirAudioExport)) workingDirAudioExport=getHomeDir();
hasOpened=fileDialog->openSave(
"Export Audio",
{"Wave file", "*.wav"},
workingDirAudioExport,
dpiScale
);
break;
case GUI_FILE_EXPORT_AUDIO_PER_CHANNEL:
if (!dirExists(workingDirAudioExport)) workingDirAudioExport=getHomeDir();
hasOpened=fileDialog->openSave(
"Export Audio",
{"Wave file", "*.wav"},
workingDirAudioExport,
dpiScale
);
break;
case GUI_FILE_EXPORT_VGM:
if (!dirExists(workingDirVGMExport)) workingDirVGMExport=getHomeDir();
hasOpened=fileDialog->openSave(
"Export VGM",
{"VGM file", "*.vgm"},
workingDirVGMExport,
dpiScale
);
break;
2022-05-26 05:24:21 +00:00
case GUI_FILE_EXPORT_ZSM:
if (!dirExists(workingDirZSMExport)) workingDirZSMExport=getHomeDir();
hasOpened=fileDialog->openSave(
"Export ZSM",
{"ZSM file", "*.zsm"},
workingDirZSMExport,
dpiScale
);
break;
2022-08-04 05:51:47 +00:00
case GUI_FILE_EXPORT_CMDSTREAM:
if (!dirExists(workingDirROMExport)) workingDirROMExport=getHomeDir();
hasOpened=fileDialog->openSave(
"Export Command Stream",
{"text file", "*.txt"},
workingDirROMExport,
dpiScale
);
break;
case GUI_FILE_EXPORT_CMDSTREAM_BINARY:
if (!dirExists(workingDirROMExport)) workingDirROMExport=getHomeDir();
hasOpened=fileDialog->openSave(
"Export Command Stream",
{"binary file", "*.bin"},
2022-08-04 05:51:47 +00:00
workingDirROMExport,
dpiScale
);
break;
case GUI_FILE_EXPORT_ROM:
showError("Coming soon!");
break;
case GUI_FILE_LOAD_MAIN_FONT:
if (!dirExists(workingDirFont)) workingDirFont=getHomeDir();
hasOpened=fileDialog->openLoad(
"Select Font",
{"compatible files", "*.ttf *.otf *.ttc"},
workingDirFont,
dpiScale
);
break;
case GUI_FILE_LOAD_HEAD_FONT:
2023-08-02 20:48:07 +00:00
if (!dirExists(workingDirFont)) workingDirFont=getHomeDir();
hasOpened=fileDialog->openLoad(
"Select Font",
{"compatible files", "*.ttf *.otf *.ttc"},
workingDirFont,
dpiScale
);
break;
case GUI_FILE_LOAD_PAT_FONT:
if (!dirExists(workingDirFont)) workingDirFont=getHomeDir();
hasOpened=fileDialog->openLoad(
"Select Font",
{"compatible files", "*.ttf *.otf *.ttc"},
workingDirFont,
dpiScale
);
break;
case GUI_FILE_IMPORT_COLORS:
if (!dirExists(workingDirColors)) workingDirColors=getHomeDir();
hasOpened=fileDialog->openLoad(
"Select Color File",
2022-04-06 19:17:29 +00:00
{"configuration files", "*.cfgc"},
workingDirColors,
dpiScale
);
break;
case GUI_FILE_IMPORT_KEYBINDS:
if (!dirExists(workingDirKeybinds)) workingDirKeybinds=getHomeDir();
hasOpened=fileDialog->openLoad(
"Select Keybind File",
2022-04-06 19:17:29 +00:00
{"configuration files", "*.cfgk"},
workingDirKeybinds,
dpiScale
);
break;
case GUI_FILE_IMPORT_LAYOUT:
if (!dirExists(workingDirKeybinds)) workingDirKeybinds=getHomeDir();
hasOpened=fileDialog->openLoad(
"Select Layout File",
{".ini files", "*.ini"},
workingDirKeybinds,
dpiScale
);
break;
case GUI_FILE_EXPORT_COLORS:
if (!dirExists(workingDirColors)) workingDirColors=getHomeDir();
hasOpened=fileDialog->openSave(
"Export Colors",
2022-04-06 19:17:29 +00:00
{"configuration files", "*.cfgc"},
workingDirColors,
dpiScale
);
break;
case GUI_FILE_EXPORT_KEYBINDS:
if (!dirExists(workingDirKeybinds)) workingDirKeybinds=getHomeDir();
hasOpened=fileDialog->openSave(
"Export Keybinds",
2022-04-06 19:17:29 +00:00
{"configuration files", "*.cfgk"},
workingDirKeybinds,
dpiScale
);
break;
case GUI_FILE_EXPORT_LAYOUT:
if (!dirExists(workingDirKeybinds)) workingDirKeybinds=getHomeDir();
hasOpened=fileDialog->openSave(
"Export Layout",
{".ini files", "*.ini"},
workingDirKeybinds,
dpiScale
);
break;
case GUI_FILE_YRW801_ROM_OPEN:
case GUI_FILE_TG100_ROM_OPEN:
case GUI_FILE_MU5_ROM_OPEN:
if (!dirExists(workingDirSample)) workingDirSample=getHomeDir();
hasOpened=fileDialog->openLoad(
"Load ROM",
{"compatible files", "*.rom *.bin",
2023-01-28 23:36:43 +00:00
"all files", "*"},
workingDirROM,
dpiScale
);
break;
case GUI_FILE_CMDSTREAM_OPEN:
if (!dirExists(workingDirROM)) workingDirROM=getHomeDir();
hasOpened=fileDialog->openLoad(
"Play Command Stream",
{"command stream", "*.bin",
"all files", "*"},
workingDirROM,
dpiScale
);
break;
case GUI_FILE_TEST_OPEN:
if (!dirExists(workingDirTest)) workingDirTest=getHomeDir();
hasOpened=fileDialog->openLoad(
"Open Test",
{"compatible files", "*.fur *.dmf *.mod",
"another option", "*.wav *.ttf",
2023-01-28 23:36:43 +00:00
"all files", "*"},
workingDirTest,
2022-10-17 00:40:14 +00:00
dpiScale,
[](const char* path) {
if (path!=NULL) {
logI("Callback Result: %s",path);
} else {
logI("Callback Result: NULL");
}
}
);
break;
case GUI_FILE_TEST_OPEN_MULTI:
if (!dirExists(workingDirTest)) workingDirTest=getHomeDir();
hasOpened=fileDialog->openLoad(
"Open Test (Multi)",
{"compatible files", "*.fur *.dmf *.mod",
"another option", "*.wav *.ttf",
2023-01-28 23:36:43 +00:00
"all files", "*"},
workingDirTest,
dpiScale,
2022-10-17 00:40:14 +00:00
[](const char* path) {
if (path!=NULL) {
logI("Callback Result: %s",path);
} else {
logI("Callback Result: NULL");
}
},
true
);
break;
case GUI_FILE_TEST_SAVE:
if (!dirExists(workingDirTest)) workingDirTest=getHomeDir();
hasOpened=fileDialog->openSave(
"Save Test",
{"Furnace song", "*.fur",
"DefleMask module", "*.dmf"},
workingDirTest,
dpiScale
);
break;
}
if (hasOpened) curFileDialog=type;
//ImGui::GetIO().ConfigFlags|=ImGuiConfigFlags_NavEnableKeyboard;
}
int FurnaceGUI::save(String path, int dmfVersion) {
SafeWriter* w;
logD("saving file...");
if (dmfVersion) {
2022-08-28 06:03:23 +00:00
if (dmfVersion<24) dmfVersion=24;
w=e->saveDMF(dmfVersion);
} else {
2023-05-26 06:29:49 +00:00
w=e->saveFur(false,settings.newPatternFormat);
}
if (w==NULL) {
lastError=e->getLastError();
logE("couldn't save! %s",lastError);
return 3;
}
logV("opening file for writing...");
FILE* outFile=ps_fopen(path.c_str(),"wb");
2022-01-14 05:34:22 +00:00
if (outFile==NULL) {
lastError=strerror(errno);
logE("couldn't save! %s",lastError);
2022-01-14 05:34:22 +00:00
w->finish();
return 1;
}
if (settings.compress) {
unsigned char zbuf[131072];
int ret;
z_stream zl;
memset(&zl,0,sizeof(z_stream));
ret=deflateInit(&zl,Z_DEFAULT_COMPRESSION);
if (ret!=Z_OK) {
logE("zlib error!");
lastError="compression error";
fclose(outFile);
w->finish();
return 2;
}
zl.avail_in=w->size();
zl.next_in=w->getFinalBuf();
while (zl.avail_in>0) {
zl.avail_out=131072;
zl.next_out=zbuf;
if ((ret=deflate(&zl,Z_NO_FLUSH))==Z_STREAM_ERROR) {
logE("zlib stream error!");
lastError="zlib stream error";
deflateEnd(&zl);
fclose(outFile);
w->finish();
return 2;
}
size_t amount=131072-zl.avail_out;
if (amount>0) {
if (fwrite(zbuf,1,amount,outFile)!=amount) {
logE("did not write entirely: %s!",strerror(errno));
lastError=strerror(errno);
deflateEnd(&zl);
fclose(outFile);
w->finish();
return 1;
}
}
}
zl.avail_out=131072;
zl.next_out=zbuf;
if ((ret=deflate(&zl,Z_FINISH))==Z_STREAM_ERROR) {
logE("zlib finish stream error!");
lastError="zlib finish stream error";
deflateEnd(&zl);
fclose(outFile);
w->finish();
return 2;
}
if (131072-zl.avail_out>0) {
if (fwrite(zbuf,1,131072-zl.avail_out,outFile)!=(131072-zl.avail_out)) {
logE("did not write entirely: %s!",strerror(errno));
lastError=strerror(errno);
deflateEnd(&zl);
fclose(outFile);
w->finish();
return 1;
}
}
deflateEnd(&zl);
} else {
if (fwrite(w->getFinalBuf(),1,w->size(),outFile)!=w->size()) {
logE("did not write entirely: %s!",strerror(errno));
lastError=strerror(errno);
fclose(outFile);
w->finish();
return 1;
}
}
fclose(outFile);
w->finish();
2023-04-06 21:24:44 +00:00
backupLock.lock();
2021-12-30 23:25:55 +00:00
curFileName=path;
2023-04-06 21:24:44 +00:00
backupLock.unlock();
2021-12-30 23:25:55 +00:00
modified=false;
updateWindowTitle();
2022-01-29 06:22:32 +00:00
if (!e->getWarnings().empty()) {
showWarning(e->getWarnings(),GUI_WARN_GENERIC);
}
2022-09-10 23:53:27 +00:00
pushRecentFile(path);
pushRecentSys(path.c_str());
logD("save complete.");
return 0;
}
int FurnaceGUI::load(String path) {
2023-08-05 19:43:29 +00:00
bool wasPlaying=e->isPlaying();
if (!path.empty()) {
logI("loading module...");
FILE* f=ps_fopen(path.c_str(),"rb");
if (f==NULL) {
perror("error");
lastError=strerror(errno);
return 1;
}
if (fseek(f,0,SEEK_END)<0) {
perror("size error");
lastError=fmt::sprintf("on seek: %s",strerror(errno));
fclose(f);
return 1;
}
ssize_t len=ftell(f);
2022-01-18 02:08:14 +00:00
if (len==(SIZE_MAX>>1)) {
perror("could not get file length");
lastError=fmt::sprintf("on pre tell: %s",strerror(errno));
fclose(f);
return 1;
}
if (len<1) {
if (len==0) {
logE("that file is empty!");
lastError="file is empty";
} else {
perror("tell error");
lastError=fmt::sprintf("on tell: %s",strerror(errno));
}
fclose(f);
return 1;
}
if (fseek(f,0,SEEK_SET)<0) {
perror("size error");
lastError=fmt::sprintf("on get size: %s",strerror(errno));
fclose(f);
return 1;
}
2022-04-15 06:30:21 +00:00
unsigned char* file=new unsigned char[len];
if (fread(file,1,(size_t)len,f)!=(size_t)len) {
perror("read error");
lastError=fmt::sprintf("on read: %s",strerror(errno));
fclose(f);
2021-12-16 07:21:43 +00:00
delete[] file;
return 1;
}
fclose(f);
2021-12-16 07:21:43 +00:00
if (!e->load(file,(size_t)len)) {
lastError=e->getLastError();
logE("could not open file!");
return 1;
}
}
2023-04-06 21:24:44 +00:00
backupLock.lock();
2021-12-30 23:25:55 +00:00
curFileName=path;
2023-04-06 21:24:44 +00:00
backupLock.unlock();
2021-12-30 23:25:55 +00:00
modified=false;
2022-01-20 07:11:03 +00:00
curNibble=false;
orderNibble=false;
orderCursor=-1;
curOrder=0;
oldRow=0;
samplePos=0;
updateSampleTex=true;
2022-01-20 07:11:03 +00:00
selStart=SelectionPoint();
selEnd=SelectionPoint();
cursor=SelectionPoint();
lastError="everything OK";
2021-12-26 23:05:18 +00:00
undoHist.clear();
redoHist.clear();
2021-12-15 22:32:08 +00:00
updateWindowTitle();
updateScroll(0);
2022-01-29 06:22:32 +00:00
if (!e->getWarnings().empty()) {
showWarning(e->getWarnings(),GUI_WARN_GENERIC);
}
2022-09-10 23:53:27 +00:00
pushRecentFile(path);
2023-08-05 19:43:29 +00:00
// do not auto-play a backup
if (path.find(backupPath)!=0) {
if (settings.playOnLoad==2 || (settings.playOnLoad==1 && wasPlaying)) {
play();
}
}
return 0;
}
2022-09-10 23:53:27 +00:00
void FurnaceGUI::pushRecentFile(String path) {
if (path.empty()) return;
2023-04-06 09:22:43 +00:00
if (path.find(backupPath)==0) return;
2022-09-10 23:53:27 +00:00
for (int i=0; i<(int)recentFile.size(); i++) {
if (recentFile[i]==path) {
recentFile.erase(recentFile.begin()+i);
i--;
}
}
recentFile.push_front(path);
while (!recentFile.empty() && (int)recentFile.size()>settings.maxRecentFile) {
recentFile.pop_back();
}
}
void FurnaceGUI::pushRecentSys(const char* path) {
#ifdef _WIN32
WString widePath=utf8To16(path);
SHAddToRecentDocs(SHARD_PATHW,widePath.c_str());
#endif
}
2023-04-06 09:22:43 +00:00
void FurnaceGUI::delFirstBackup(String name) {
std::vector<String> listOfFiles;
#ifdef _WIN32
2023-04-06 11:30:43 +00:00
String findPath=backupPath+String(DIR_SEPARATOR_STR)+name+String("*.fur");
WIN32_FIND_DATAW next;
HANDLE backDir=FindFirstFileW(utf8To16(findPath.c_str()).c_str(),&next);
if (backDir!=INVALID_HANDLE_VALUE) {
do {
listOfFiles.push_back(utf16To8(next.cFileName));
2023-04-06 20:07:11 +00:00
} while (FindNextFileW(backDir,&next)!=0);
2023-04-06 11:30:43 +00:00
FindClose(backDir);
}
2023-04-06 09:22:43 +00:00
#else
DIR* backDir=opendir(backupPath.c_str());
if (backDir==NULL) {
logW("could not open backups dir!");
return;
}
while (true) {
struct dirent* next=readdir(backDir);
if (next==NULL) break;
if (strstr(next->d_name,name.c_str())!=next->d_name) continue;
listOfFiles.push_back(String(next->d_name));
}
closedir(backDir);
#endif
std::sort(listOfFiles.begin(),listOfFiles.end(),[](const String& a, const String& b) -> bool {
return strcmp(a.c_str(),b.c_str())<0;
});
2023-04-06 11:15:47 +00:00
int totalDelete=((int)listOfFiles.size())-5;
for (int i=0; i<totalDelete; i++) {
String toDelete=backupPath+String(DIR_SEPARATOR_STR)+listOfFiles[i];
deleteFile(toDelete.c_str());
}
2023-04-06 09:22:43 +00:00
}
int FurnaceGUI::loadStream(String path) {
if (!path.empty()) {
logI("loading stream...");
FILE* f=ps_fopen(path.c_str(),"rb");
if (f==NULL) {
perror("error");
lastError=strerror(errno);
return 1;
}
if (fseek(f,0,SEEK_END)<0) {
perror("size error");
lastError=fmt::sprintf("on seek: %s",strerror(errno));
fclose(f);
return 1;
}
ssize_t len=ftell(f);
if (len==(SIZE_MAX>>1)) {
perror("could not get file length");
lastError=fmt::sprintf("on pre tell: %s",strerror(errno));
fclose(f);
return 1;
}
if (len<1) {
if (len==0) {
logE("that file is empty!");
lastError="file is empty";
} else {
perror("tell error");
lastError=fmt::sprintf("on tell: %s",strerror(errno));
}
fclose(f);
return 1;
}
if (fseek(f,0,SEEK_SET)<0) {
perror("size error");
lastError=fmt::sprintf("on get size: %s",strerror(errno));
fclose(f);
return 1;
}
unsigned char* file=new unsigned char[len];
if (fread(file,1,(size_t)len,f)!=(size_t)len) {
perror("read error");
lastError=fmt::sprintf("on read: %s",strerror(errno));
fclose(f);
delete[] file;
return 1;
}
fclose(f);
if (!e->playStream(file,(size_t)len)) {
lastError=e->getLastError();
logE("could not open file!");
return 1;
}
}
return 0;
}
void FurnaceGUI::exportAudio(String path, DivAudioExportModes mode) {
2022-06-06 08:05:55 +00:00
e->saveAudio(path.c_str(),exportLoops+1,mode,exportFadeOut);
displayExporting=true;
}
2023-05-16 18:41:08 +00:00
void FurnaceGUI::editStr(String* which) {
editString=which;
displayEditString=true;
}
2021-12-30 23:52:36 +00:00
void FurnaceGUI::showWarning(String what, FurnaceGUIWarnings type) {
warnString=what;
warnAction=type;
warnQuit=true;
}
void FurnaceGUI::showError(String what) {
errorString=what;
2022-01-09 21:36:47 +00:00
displayError=true;
}
2022-10-02 21:12:02 +00:00
String FurnaceGUI::getLastError() {
return lastError;
}
// what monster did I just create here?
#define B30(tt) (macroDragBit30?((((tt)&0xc0000000)==0x40000000 || ((tt)&0xc0000000)==0x80000000)?0x40000000:0):0)
#define MACRO_DRAG(t) \
2022-08-23 08:57:21 +00:00
if (macroDragSettingBit30) { \
if (macroDragLastX!=x || macroDragLastY!=y) { \
macroDragLastX=x; \
macroDragLastY=y; \
if (macroDragInitialValueSet) { \
if (!macroDragInitialValue) { \
if (t[x]&0x80000000) { \
t[x]&=~0x40000000; \
} else { \
t[x]|=0x40000000; \
} \
} else { \
if (t[x]&0x80000000) { \
t[x]|=0x40000000; \
} else { \
t[x]&=~0x40000000; \
} \
} \
} else { \
macroDragInitialValue=(((t[x])&0xc0000000)==0x40000000 || ((t[x])&0xc0000000)==0x80000000); \
macroDragInitialValueSet=true; \
t[x]^=0x40000000; \
} \
} \
} else if (macroDragBitMode) { \
if (macroDragLastX!=x || macroDragLastY!=y) { \
macroDragLastX=x; \
macroDragLastY=y; \
if (macroDragInitialValueSet) { \
if (macroDragInitialValue) { \
t[x]=(((t[x]+macroDragBitOff)&((1<<macroDragMax)-1))&(~(1<<y)))-macroDragBitOff; \
} else { \
t[x]=(((t[x]+macroDragBitOff)&((1<<macroDragMax)-1))|(1<<y))-macroDragBitOff; \
} \
} else { \
macroDragInitialValue=(((t[x]+macroDragBitOff)&((1<<macroDragMax)-1))&(1<<y)); \
macroDragInitialValueSet=true; \
t[x]=(((t[x]+macroDragBitOff)&((1<<macroDragMax)-1))^(1<<y))-macroDragBitOff; \
} \
t[x]&=(1<<macroDragMax)-1; \
} \
} else { \
2022-04-13 04:03:20 +00:00
if (macroDragLineMode) { \
if (!macroDragInitialValueSet) { \
macroDragLineInitial=ImVec2(x,y); \
macroDragLineInitialV=ImVec2(dragX,dragY); \
2022-04-13 04:03:20 +00:00
macroDragInitialValueSet=true; \
2022-05-08 23:32:16 +00:00
macroDragMouseMoved=false; \
} else if (!macroDragMouseMoved) { \
if ((pow(dragX-macroDragLineInitialV.x,2.0)+pow(dragY-macroDragLineInitialV.y,2.0))>=16.0f) { \
macroDragMouseMoved=true; \
} \
2022-04-13 04:03:20 +00:00
} \
2022-05-08 23:32:16 +00:00
if (macroDragMouseMoved) { \
if ((int)round(x-macroDragLineInitial.x)==0) { \
t[x]=B30(t[x])^(int)(macroDragLineInitial.y); \
2022-04-13 04:03:20 +00:00
} else { \
2022-05-08 23:32:16 +00:00
if ((int)round(x-macroDragLineInitial.x)<0) { \
for (int i=0; i<=(int)round(macroDragLineInitial.x-x); i++) { \
int index=(int)round(x+i); \
if (index<0) continue; \
t[index]=B30(t[index])^(int)(y+(macroDragLineInitial.y-y)*((float)i/(float)(macroDragLineInitial.x-x))); \
2022-05-08 23:32:16 +00:00
} \
} else { \
for (int i=0; i<=(int)round(x-macroDragLineInitial.x); i++) { \
int index=(int)round(i+macroDragLineInitial.x); \
if (index<0) continue; \
t[index]=B30(t[index])^(int)(macroDragLineInitial.y+(y-macroDragLineInitial.y)*((float)i/(x-macroDragLineInitial.x))); \
2022-05-08 23:32:16 +00:00
} \
2022-04-13 04:03:20 +00:00
} \
} \
} \
} else { \
t[x]=B30(t[x])^(y); \
2022-04-13 04:03:20 +00:00
} \
}
void FurnaceGUI::processDrags(int dragX, int dragY) {
if (macroDragActive) {
if (macroDragLen>0) {
2022-02-15 22:22:09 +00:00
int x=((dragX-macroDragStart.x)*macroDragLen/MAX(1,macroDragAreaSize.x));
if (x<0) x=0;
if (x>=macroDragLen) x=macroDragLen-1;
2022-01-21 22:00:28 +00:00
x+=macroDragScroll;
int y;
if (macroDragBitMode) {
2022-02-15 22:22:09 +00:00
y=(int)(macroDragMax-((dragY-macroDragStart.y)*(double(macroDragMax-macroDragMin)/(double)MAX(1,macroDragAreaSize.y))));
} else {
2022-02-15 22:22:09 +00:00
y=round(macroDragMax-((dragY-macroDragStart.y)*(double(macroDragMax-macroDragMin)/(double)MAX(1,macroDragAreaSize.y))));
}
if (y>macroDragMax) y=macroDragMax;
if (y<macroDragMin) y=macroDragMin;
if (macroDragChar) {
MACRO_DRAG(macroDragCTarget);
} else {
MACRO_DRAG(macroDragTarget);
}
}
}
if (macroLoopDragActive) {
if (macroLoopDragLen>0) {
2022-02-15 22:22:09 +00:00
int x=(dragX-macroLoopDragStart.x)*macroLoopDragLen/MAX(1,macroLoopDragAreaSize.x);
if (x<0) x=0;
if (x>=macroLoopDragLen) {
x=-1;
} else {
x+=macroDragScroll;
}
*macroLoopDragTarget=x;
}
}
if (waveDragActive) {
if (waveDragLen>0) {
2022-02-15 22:22:09 +00:00
int x=(dragX-waveDragStart.x)*waveDragLen/MAX(1,waveDragAreaSize.x);
if (x<0) x=0;
if (x>=waveDragLen) x=waveDragLen-1;
2022-02-15 22:22:09 +00:00
int y=round(waveDragMax-((dragY-waveDragStart.y)*(double(waveDragMax-waveDragMin)/(double)MAX(1,waveDragAreaSize.y))));
if (y>waveDragMax) y=waveDragMax;
if (y<waveDragMin) y=waveDragMin;
waveDragTarget[x]=y;
notifyWaveChange=true;
MARK_MODIFIED;
}
}
2022-03-20 23:25:48 +00:00
if (sampleDragActive) {
int x=samplePos+round(double(dragX-sampleDragStart.x)*sampleZoom);
int x1=samplePos+round(double(dragX-sampleDragStart.x+1)*sampleZoom);
2022-03-20 23:25:48 +00:00
if (x<0) x=0;
if (sampleDragMode) {
if (x>=(int)sampleDragLen) x=sampleDragLen-1;
} else {
if (x>(int)sampleDragLen) x=sampleDragLen;
}
2022-03-20 23:25:48 +00:00
if (x1<0) x1=0;
if (x1>=(int)sampleDragLen) x1=sampleDragLen-1;
double y=0.5-double(dragY-sampleDragStart.y)/sampleDragAreaSize.y;
if (sampleDragMode) { // draw
2022-09-30 22:47:17 +00:00
if (sampleDragTarget) {
if (sampleDrag16) {
int val=y*65536;
if (val<-32768) val=-32768;
if (val>32767) val=32767;
for (int i=x; i<=x1; i++) ((short*)sampleDragTarget)[i]=val;
} else {
int val=y*256;
if (val<-128) val=-128;
if (val>127) val=127;
for (int i=x; i<=x1; i++) ((signed char*)sampleDragTarget)[i]=val;
}
updateSampleTex=true;
2022-03-20 23:25:48 +00:00
}
} else { // select
if (sampleSelStart<0) {
sampleSelStart=x;
}
sampleSelEnd=x;
}
}
2022-11-30 22:20:04 +00:00
if (orderScrollLocked) {
2022-11-30 22:37:48 +00:00
if (fabs(orderScrollRealOrigin.x-dragX)>2.0f*dpiScale || fabs(orderScrollRealOrigin.y-dragY)>2.0f*dpiScale) orderScrollTolerance=false;
2022-11-30 22:20:04 +00:00
orderScroll=(orderScrollSlideOrigin-dragX)/(40.0*dpiScale);
if (orderScroll<0.0f) orderScroll=0.0f;
if (orderScroll>(float)e->curSubSong->ordersLen-1) orderScroll=e->curSubSong->ordersLen-1;
}
}
2022-01-19 10:10:06 +00:00
#define checkExtension(x) \
String lowerCase=fileName; \
for (char& i: lowerCase) { \
if (i>='A' && i<='Z') i+='a'-'A'; \
} \
if (lowerCase.size()<strlen(x) || lowerCase.rfind(x)!=lowerCase.size()-strlen(x)) { \
2022-01-19 10:10:06 +00:00
fileName+=x; \
}
#define checkExtensionDual(x,y,fallback) \
String lowerCase=fileName; \
for (char& i: lowerCase) { \
if (i>='A' && i<='Z') i+='a'-'A'; \
} \
if (lowerCase.size()<4 || (lowerCase.rfind(x)!=lowerCase.size()-4 && lowerCase.rfind(y)!=lowerCase.size()-4)) { \
fileName+=fallback; \
}
#define checkExtensionTriple(x,y,z,fallback) \
String lowerCase=fileName; \
for (char& i: lowerCase) { \
if (i>='A' && i<='Z') i+='a'-'A'; \
} \
if (lowerCase.size()<4 || (lowerCase.rfind(x)!=lowerCase.size()-4 && lowerCase.rfind(y)!=lowerCase.size()-4 && lowerCase.rfind(z)!=lowerCase.size()-4)) { \
fileName+=fallback; \
}
#define drawOpMask(m) \
ImGui::PushFont(patFont); \
ImGui::PushID("om_" #m); \
if (ImGui::BeginTable("opMaskTable",5,ImGuiTableFlags_Borders|ImGuiTableFlags_SizingFixedFit|ImGuiTableFlags_NoHostExtendX)) { \
ImGui::TableNextRow(); \
ImGui::TableNextColumn(); \
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_ACTIVE]); \
if (ImGui::Selectable(m.note?"C-4##opMaskNote":"---##opMaskNote",m.note,ImGuiSelectableFlags_DontClosePopups)) { \
m.note=!m.note; \
} \
ImGui::PopStyleColor(); \
ImGui::TableNextColumn(); \
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_INS]); \
if (ImGui::Selectable(m.ins?"01##opMaskIns":"--##opMaskIns",m.ins,ImGuiSelectableFlags_DontClosePopups)) { \
m.ins=!m.ins; \
} \
ImGui::PopStyleColor(); \
ImGui::TableNextColumn(); \
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_VOLUME_MAX]); \
if (ImGui::Selectable(m.vol?"7F##opMaskVol":"--##opMaskVol",m.vol,ImGuiSelectableFlags_DontClosePopups)) { \
m.vol=!m.vol; \
} \
ImGui::PopStyleColor(); \
ImGui::TableNextColumn(); \
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_PITCH]); \
if (ImGui::Selectable(m.effect?"04##opMaskEffect":"--##opMaskEffect",m.effect,ImGuiSelectableFlags_DontClosePopups)) { \
m.effect=!m.effect; \
} \
ImGui::TableNextColumn(); \
if (ImGui::Selectable(m.effectVal?"72##opMaskEffectVal":"--##opMaskEffectVal",m.effectVal,ImGuiSelectableFlags_DontClosePopups)) { \
m.effectVal=!m.effectVal; \
} \
ImGui::PopStyleColor(); \
ImGui::EndTable(); \
} \
ImGui::PopID(); \
ImGui::PopFont();
void FurnaceGUI::editOptions(bool topMenu) {
char id[4096];
2022-04-21 23:10:59 +00:00
editOptsVisible=true;
2022-06-06 10:03:19 +00:00
2023-01-30 20:58:59 +00:00
if (ImGui::MenuItem("cut",BIND_FOR(GUI_ACTION_PAT_CUT))) doCopy(true,true,selStart,selEnd);
if (ImGui::MenuItem("copy",BIND_FOR(GUI_ACTION_PAT_COPY))) doCopy(false,true,selStart,selEnd);
if (ImGui::MenuItem("paste",BIND_FOR(GUI_ACTION_PAT_PASTE))) doPaste();
2023-02-06 23:52:51 +00:00
if (!basicMode) if (ImGui::BeginMenu("paste special...")) {
if (ImGui::MenuItem("paste mix",BIND_FOR(GUI_ACTION_PAT_PASTE_MIX))) doPaste(GUI_PASTE_MODE_MIX_FG);
if (ImGui::MenuItem("paste mix (background)",BIND_FOR(GUI_ACTION_PAT_PASTE_MIX_BG))) doPaste(GUI_PASTE_MODE_MIX_BG);
if (ImGui::BeginMenu("paste with ins (foreground)")) {
if (e->song.ins.empty()) {
ImGui::Text("no instruments available");
}
for (size_t i=0; i<e->song.ins.size(); i++) {
snprintf(id,4095,"%.2X: %s",(int)i,e->song.ins[i]->name.c_str());
if (ImGui::MenuItem(id)) {
doPaste(GUI_PASTE_MODE_INS_FG,i);
}
}
ImGui::EndMenu();
}
if (ImGui::BeginMenu("paste with ins (background)")) {
if (e->song.ins.empty()) {
ImGui::Text("no instruments available");
}
for (size_t i=0; i<e->song.ins.size(); i++) {
snprintf(id,4095,"%.2X: %s",(int)i,e->song.ins[i]->name.c_str());
if (ImGui::MenuItem(id)) {
doPaste(GUI_PASTE_MODE_INS_BG,i);
}
}
ImGui::EndMenu();
}
if (ImGui::MenuItem("paste flood",BIND_FOR(GUI_ACTION_PAT_PASTE_FLOOD))) doPaste(GUI_PASTE_MODE_FLOOD);
if (ImGui::MenuItem("paste overflow",BIND_FOR(GUI_ACTION_PAT_PASTE_OVERFLOW))) doPaste(GUI_PASTE_MODE_OVERFLOW);
ImGui::EndMenu();
}
if (ImGui::MenuItem("delete",BIND_FOR(GUI_ACTION_PAT_DELETE))) doDelete();
if (topMenu) {
if (ImGui::MenuItem("select all",BIND_FOR(GUI_ACTION_PAT_SELECT_ALL))) doSelectAll();
}
ImGui::Separator();
2023-02-06 23:52:51 +00:00
if (!basicMode) {
if (ImGui::BeginMenu("operation mask...")) {
drawOpMask(opMaskDelete);
ImGui::SameLine();
ImGui::Text("delete");
2023-02-06 23:52:51 +00:00
drawOpMask(opMaskPullDelete);
ImGui::SameLine();
ImGui::Text("pull delete");
2023-02-06 23:52:51 +00:00
drawOpMask(opMaskInsert);
ImGui::SameLine();
ImGui::Text("insert");
2023-02-06 23:52:51 +00:00
drawOpMask(opMaskPaste);
ImGui::SameLine();
ImGui::Text("paste");
2023-02-06 23:52:51 +00:00
drawOpMask(opMaskTransposeNote);
ImGui::SameLine();
ImGui::Text("transpose (note)");
2023-02-06 23:52:51 +00:00
drawOpMask(opMaskTransposeValue);
ImGui::SameLine();
ImGui::Text("transpose (value)");
2023-02-06 23:52:51 +00:00
drawOpMask(opMaskInterpolate);
ImGui::SameLine();
ImGui::Text("interpolate");
2023-02-06 23:52:51 +00:00
drawOpMask(opMaskFade);
ImGui::SameLine();
ImGui::Text("fade");
2023-02-06 23:52:51 +00:00
drawOpMask(opMaskInvertVal);
ImGui::SameLine();
ImGui::Text("invert values");
2023-02-06 23:52:51 +00:00
drawOpMask(opMaskScale);
ImGui::SameLine();
ImGui::Text("scale");
2023-02-06 23:52:51 +00:00
drawOpMask(opMaskRandomize);
ImGui::SameLine();
ImGui::Text("randomize");
2023-02-06 23:52:51 +00:00
drawOpMask(opMaskFlip);
ImGui::SameLine();
ImGui::Text("flip");
2023-02-06 23:52:51 +00:00
drawOpMask(opMaskCollapseExpand);
ImGui::SameLine();
ImGui::Text("collapse/expand");
2023-02-06 23:52:51 +00:00
ImGui::EndMenu();
2022-04-21 23:10:59 +00:00
}
2023-02-06 23:52:51 +00:00
ImGui::Text("input latch");
ImGui::PushFont(patFont);
if (ImGui::BeginTable("inputLatchTable",5,ImGuiTableFlags_Borders|ImGuiTableFlags_SizingFixedFit|ImGuiTableFlags_NoHostExtendX)) {
static char id[64];
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_ACTIVE]);
ImGui::Text("C-4");
ImGui::PopStyleColor();
2023-02-06 23:52:51 +00:00
ImGui::TableNextColumn();
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_INS]);
if (latchIns==-2) {
strcpy(id,"&&##LatchIns");
} else if (latchIns==-1) {
strcpy(id,"..##LatchIns");
} else {
snprintf(id,63,"%.2x##LatchIns",latchIns&0xff);
}
if (ImGui::Selectable(id,latchTarget==1,ImGuiSelectableFlags_DontClosePopups)) {
latchTarget=1;
latchNibble=false;
}
if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) {
latchIns=-2;
}
if (ImGui::IsItemHovered()) {
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_TEXT]);
ImGui::SetTooltip("&&: selected instrument\n..: no instrument");
ImGui::PopStyleColor();
}
ImGui::PopStyleColor();
ImGui::TableNextColumn();
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_VOLUME_MAX]);
if (latchVol==-1) {
strcpy(id,"..##LatchVol");
} else {
snprintf(id,63,"%.2x##LatchVol",latchVol&0xff);
}
if (ImGui::Selectable(id,latchTarget==2,ImGuiSelectableFlags_DontClosePopups)) {
latchTarget=2;
latchNibble=false;
}
if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) {
latchVol=-1;
}
ImGui::PopStyleColor();
ImGui::TableNextColumn();
if (latchEffect==-1) {
strcpy(id,"..##LatchFX");
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_INACTIVE]);
} else {
const unsigned char data=latchEffect;
snprintf(id,63,"%.2x##LatchFX",data);
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[fxColors[data]]);
}
if (ImGui::Selectable(id,latchTarget==3,ImGuiSelectableFlags_DontClosePopups)) {
latchTarget=3;
latchNibble=false;
}
if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) {
latchEffect=-1;
}
ImGui::TableNextColumn();
if (latchEffectVal==-1) {
strcpy(id,"..##LatchFXV");
} else {
snprintf(id,63,"%.2x##LatchFXV",latchEffectVal&0xff);
}
if (ImGui::Selectable(id,latchTarget==4,ImGuiSelectableFlags_DontClosePopups)) {
latchTarget=4;
latchNibble=false;
}
if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) {
latchEffectVal=-1;
}
ImGui::PopStyleColor();
ImGui::EndTable();
2022-04-21 23:10:59 +00:00
}
2023-02-06 23:52:51 +00:00
ImGui::PopFont();
ImGui::SameLine();
if (ImGui::Button("Set")) {
DivPattern* pat=e->curPat[cursor.xCoarse].getPattern(e->curOrders->ord[cursor.xCoarse][curOrder],true);
latchIns=pat->data[cursor.y][2];
latchVol=pat->data[cursor.y][3];
latchEffect=pat->data[cursor.y][4];
latchEffectVal=pat->data[cursor.y][5];
latchTarget=0;
latchNibble=false;
}
2023-02-06 23:52:51 +00:00
ImGui::SameLine();
if (ImGui::Button("Reset")) {
latchIns=-2;
latchVol=-1;
latchEffect=-1;
latchEffectVal=-1;
2023-02-06 23:52:51 +00:00
latchTarget=0;
latchNibble=false;
2022-04-21 23:10:59 +00:00
}
2023-02-06 23:52:51 +00:00
ImGui::Separator();
}
if (ImGui::MenuItem("note up",BIND_FOR(GUI_ACTION_PAT_NOTE_UP))) doTranspose(1,opMaskTransposeNote);
if (ImGui::MenuItem("note down",BIND_FOR(GUI_ACTION_PAT_NOTE_DOWN))) doTranspose(-1,opMaskTransposeNote);
if (ImGui::MenuItem("octave up",BIND_FOR(GUI_ACTION_PAT_OCTAVE_UP))) doTranspose(12,opMaskTransposeNote);
if (ImGui::MenuItem("octave down",BIND_FOR(GUI_ACTION_PAT_OCTAVE_DOWN))) doTranspose(-12,opMaskTransposeNote);
ImGui::Separator();
if (ImGui::MenuItem("values up",BIND_FOR(GUI_ACTION_PAT_VALUE_UP))) doTranspose(1,opMaskTransposeValue);
if (ImGui::MenuItem("values down",BIND_FOR(GUI_ACTION_PAT_VALUE_DOWN))) doTranspose(-1,opMaskTransposeValue);
if (ImGui::MenuItem("values up (+16)",BIND_FOR(GUI_ACTION_PAT_VALUE_UP_COARSE))) doTranspose(16,opMaskTransposeValue);
if (ImGui::MenuItem("values down (-16)",BIND_FOR(GUI_ACTION_PAT_VALUE_DOWN_COARSE))) doTranspose(-16,opMaskTransposeValue);
ImGui::Separator();
ImGui::AlignTextToFramePadding();
ImGui::Text("transpose");
ImGui::SameLine();
ImGui::SetNextItemWidth(120.0f*dpiScale);
if (ImGui::InputInt("##TransposeAmount",&transposeAmount,1,1)) {
if (transposeAmount<-96) transposeAmount=-96;
if (transposeAmount>96) transposeAmount=96;
}
ImGui::SameLine();
if (ImGui::Button("Notes")) {
doTranspose(transposeAmount,opMaskTransposeNote);
ImGui::CloseCurrentPopup();
}
ImGui::SameLine();
if (ImGui::Button("Values")) {
doTranspose(transposeAmount,opMaskTransposeValue);
ImGui::CloseCurrentPopup();
}
2022-05-26 05:24:21 +00:00
ImGui::Separator();
if (ImGui::MenuItem("interpolate",BIND_FOR(GUI_ACTION_PAT_INTERPOLATE))) doInterpolate();
if (ImGui::BeginMenu("change instrument...")) {
if (e->song.ins.empty()) {
ImGui::Text("no instruments available");
}
for (size_t i=0; i<e->song.ins.size(); i++) {
snprintf(id,4095,"%.2X: %s",(int)i,e->song.ins[i]->name.c_str());
if (ImGui::MenuItem(id)) {
doChangeIns(i);
}
}
ImGui::EndMenu();
}
2023-02-06 23:52:51 +00:00
if (!basicMode) {
if (ImGui::BeginMenu("gradient/fade...")) {
if (ImGui::InputInt("Start",&fadeMin,1,1)) {
if (fadeMin<0) fadeMin=0;
if (fadeMode) {
if (fadeMin>15) fadeMin=15;
} else {
if (fadeMin>255) fadeMin=255;
}
2022-03-12 08:04:34 +00:00
}
2023-02-06 23:52:51 +00:00
if (ImGui::InputInt("End",&fadeMax,1,1)) {
if (fadeMax<0) fadeMax=0;
if (fadeMode) {
if (fadeMax>15) fadeMax=15;
} else {
if (fadeMax>255) fadeMax=255;
}
2022-03-12 08:04:34 +00:00
}
2023-02-06 23:52:51 +00:00
if (ImGui::Checkbox("Nibble mode",&fadeMode)) {
if (fadeMode) {
if (fadeMin>15) fadeMin=15;
if (fadeMax>15) fadeMax=15;
} else {
if (fadeMin>255) fadeMin=255;
if (fadeMax>255) fadeMax=255;
}
2022-03-12 08:04:34 +00:00
}
2023-02-06 23:52:51 +00:00
if (ImGui::Button("Go ahead")) {
doFade(fadeMin,fadeMax,fadeMode);
ImGui::CloseCurrentPopup();
2022-03-12 08:40:56 +00:00
}
2023-02-06 23:52:51 +00:00
ImGui::EndMenu();
}
2023-02-06 23:52:51 +00:00
if (ImGui::BeginMenu("scale...")) {
if (ImGui::InputFloat("##ScaleMax",&scaleMax,1,1,"%.1f%%")) {
if (scaleMax<0.0f) scaleMax=0.0f;
if (scaleMax>25600.0f) scaleMax=25600.0f;
2022-03-12 08:40:56 +00:00
}
2023-02-06 23:52:51 +00:00
if (ImGui::Button("Scale")) {
doScale(scaleMax);
ImGui::CloseCurrentPopup();
2022-03-12 08:40:56 +00:00
}
2023-02-06 23:52:51 +00:00
ImGui::EndMenu();
}
2023-02-06 23:52:51 +00:00
if (ImGui::BeginMenu("randomize...")) {
if (ImGui::InputInt("Minimum",&randomizeMin,1,1)) {
if (randomizeMin<0) randomizeMin=0;
if (randomMode) {
if (randomizeMin>15) randomizeMin=15;
} else {
if (randomizeMin>255) randomizeMin=255;
}
if (randomizeMin>randomizeMax) randomizeMin=randomizeMax;
}
if (ImGui::InputInt("Maximum",&randomizeMax,1,1)) {
if (randomizeMax<0) randomizeMax=0;
if (randomizeMax<randomizeMin) randomizeMax=randomizeMin;
if (randomMode) {
if (randomizeMax>15) randomizeMax=15;
} else {
if (randomizeMax>255) randomizeMax=255;
}
}
if (ImGui::Checkbox("Nibble mode",&randomMode)) {
if (randomMode) {
if (randomizeMin>15) randomizeMin=15;
if (randomizeMax>15) randomizeMax=15;
} else {
if (randomizeMin>255) randomizeMin=255;
if (randomizeMax>255) randomizeMax=255;
}
}
// TODO: add an option to set effect to specific value?
if (ImGui::Button("Randomize")) {
doRandomize(randomizeMin,randomizeMax,randomMode);
ImGui::CloseCurrentPopup();
}
ImGui::EndMenu();
}
2023-02-06 23:52:51 +00:00
if (ImGui::MenuItem("invert values",BIND_FOR(GUI_ACTION_PAT_INVERT_VALUES))) doInvertValues();
2023-02-06 23:52:51 +00:00
ImGui::Separator();
2023-02-06 23:52:51 +00:00
if (ImGui::MenuItem("flip selection",BIND_FOR(GUI_ACTION_PAT_FLIP_SELECTION))) doFlip();
ImGui::SetNextItemWidth(120.0f*dpiScale);
if (ImGui::InputInt("collapse/expand amount##CollapseAmount",&collapseAmount,1,1)) {
if (collapseAmount<2) collapseAmount=2;
if (collapseAmount>256) collapseAmount=256;
}
if (ImGui::MenuItem("collapse",BIND_FOR(GUI_ACTION_PAT_COLLAPSE_ROWS))) doCollapse(collapseAmount,selStart,selEnd);
if (ImGui::MenuItem("expand",BIND_FOR(GUI_ACTION_PAT_EXPAND_ROWS))) doExpand(collapseAmount,selStart,selEnd);
2023-04-27 06:23:54 +00:00
if (topMenu) {
ImGui::Separator();
if (ImGui::MenuItem("collapse pattern",BIND_FOR(GUI_ACTION_PAT_COLLAPSE_PAT))) doAction(GUI_ACTION_PAT_COLLAPSE_PAT);
if (ImGui::MenuItem("expand pattern",BIND_FOR(GUI_ACTION_PAT_EXPAND_PAT))) doAction(GUI_ACTION_PAT_EXPAND_PAT);
}
}
if (topMenu) {
ImGui::Separator();
if (ImGui::MenuItem("collapse song",BIND_FOR(GUI_ACTION_PAT_COLLAPSE_SONG))) doAction(GUI_ACTION_PAT_COLLAPSE_SONG);
if (ImGui::MenuItem("expand song",BIND_FOR(GUI_ACTION_PAT_EXPAND_SONG))) doAction(GUI_ACTION_PAT_EXPAND_SONG);
}
if (!basicMode) {
2023-02-06 23:52:51 +00:00
if (topMenu) {
ImGui::Separator();
if (ImGui::MenuItem("find/replace",BIND_FOR(GUI_ACTION_WINDOW_FIND),findOpen)) {
if (findOpen) {
findOpen=false;
} else {
nextWindow=GUI_WINDOW_FIND;
}
2022-06-06 10:03:19 +00:00
}
}
}
}
2022-05-18 23:42:59 +00:00
void FurnaceGUI::toggleMobileUI(bool enable, bool force) {
if (mobileUI!=enable || force) {
if (!mobileUI && enable) {
if (!ImGui::SaveIniSettingsToDisk(finalLayoutPath,true)) {
2023-04-04 21:01:45 +00:00
reportError(fmt::sprintf("could NOT save layout! %s",strerror(errno)));
}
2022-05-26 05:24:21 +00:00
}
2022-05-18 23:42:59 +00:00
mobileUI=enable;
if (mobileUI) {
ImGui::GetIO().IniFilename=NULL;
2022-12-01 00:42:51 +00:00
ImGui::GetIO().ConfigFlags|=ImGuiConfigFlags_InertialScrollEnable;
2022-12-03 05:51:57 +00:00
ImGui::GetIO().ConfigFlags|=ImGuiConfigFlags_NoHoverColors;
fileDialog->mobileUI=true;
2022-05-18 23:42:59 +00:00
} else {
2022-09-08 18:16:57 +00:00
ImGui::GetIO().IniFilename=NULL;
if (!ImGui::LoadIniSettingsFromDisk(finalLayoutPath,true)) {
2023-04-04 21:01:45 +00:00
reportError(fmt::sprintf("could NOT load layout! %s",strerror(errno)));
ImGui::LoadIniSettingsFromMemory(defaultLayout);
2023-04-04 21:01:45 +00:00
}
2022-12-01 00:42:51 +00:00
ImGui::GetIO().ConfigFlags&=~ImGuiConfigFlags_InertialScrollEnable;
2022-12-03 05:51:57 +00:00
ImGui::GetIO().ConfigFlags&=~ImGuiConfigFlags_NoHoverColors;
fileDialog->mobileUI=false;
2022-05-18 23:42:59 +00:00
}
2022-05-26 05:24:21 +00:00
}
2022-05-18 23:42:59 +00:00
}
void FurnaceGUI::pushToggleColors(bool status) {
ImVec4 toggleColor=status?uiColors[GUI_COLOR_TOGGLE_ON]:uiColors[GUI_COLOR_TOGGLE_OFF];
ImGui::PushStyleColor(ImGuiCol_Button,toggleColor);
2022-12-03 05:51:57 +00:00
if (!mobileUI) {
if (settings.guiColorsBase) {
toggleColor.x*=0.8f;
toggleColor.y*=0.8f;
toggleColor.z*=0.8f;
} else {
toggleColor.x=CLAMP(toggleColor.x*1.3f,0.0f,1.0f);
toggleColor.y=CLAMP(toggleColor.y*1.3f,0.0f,1.0f);
toggleColor.z=CLAMP(toggleColor.z*1.3f,0.0f,1.0f);
}
}
ImGui::PushStyleColor(ImGuiCol_ButtonHovered,toggleColor);
if (settings.guiColorsBase) {
toggleColor.x*=0.8f;
toggleColor.y*=0.8f;
toggleColor.z*=0.8f;
} else {
toggleColor.x=CLAMP(toggleColor.x*1.5f,0.0f,1.0f);
toggleColor.y=CLAMP(toggleColor.y*1.5f,0.0f,1.0f);
toggleColor.z=CLAMP(toggleColor.z*1.5f,0.0f,1.0f);
}
ImGui::PushStyleColor(ImGuiCol_ButtonActive,toggleColor);
}
void FurnaceGUI::popToggleColors() {
ImGui::PopStyleColor(3);
}
int _processEvent(void* instance, SDL_Event* event) {
return ((FurnaceGUI*)instance)->processEvent(event);
}
#if SDL_VERSION_ATLEAST(2,0,17)
#define VALID_MODS KMOD_NUM|KMOD_CAPS|KMOD_SCROLL
#else
#define VALID_MODS KMOD_NUM|KMOD_CAPS
#endif
int FurnaceGUI::processEvent(SDL_Event* ev) {
if (introPos<11.0 && !shortIntro) return 1;
2022-05-17 17:46:52 +00:00
#ifdef IS_MOBILE
2022-11-10 21:53:18 +00:00
if (ev->type==SDL_APP_TERMINATING) {
// TODO: save last song state here
} else if (ev->type==SDL_APP_WILLENTERBACKGROUND) {
commitState();
e->saveConf();
2022-05-17 17:46:52 +00:00
}
#endif
if (ev->type==SDL_KEYDOWN) {
if (!ev->key.repeat && latchTarget==0 && !wantCaptureKeyboard && !sampleMapWaitingInput && (ev->key.keysym.mod&(~(VALID_MODS)))==0) {
if (settings.notePreviewBehavior==0) return 1;
switch (curWindow) {
case GUI_WINDOW_SAMPLE_EDIT:
case GUI_WINDOW_SAMPLE_LIST: {
auto it=noteKeys.find(ev->key.keysym.scancode);
if (it!=noteKeys.cend()) {
int key=it->second;
int num=12*curOctave+key;
if (key!=100 && key!=101 && key!=102) {
int pStart=-1;
int pEnd=-1;
if (curWindow==GUI_WINDOW_SAMPLE_EDIT) {
if (sampleSelStart!=sampleSelEnd) {
pStart=sampleSelStart;
pEnd=sampleSelEnd;
if (pStart>pEnd) {
pStart^=pEnd;
pEnd^=pStart;
pStart^=pEnd;
}
}
}
e->previewSample(curSample,num,pStart,pEnd);
samplePreviewOn=true;
samplePreviewKey=ev->key.keysym.scancode;
samplePreviewNote=num;
}
}
break;
}
case GUI_WINDOW_WAVE_LIST:
case GUI_WINDOW_WAVE_EDIT: {
auto it=noteKeys.find(ev->key.keysym.scancode);
if (it!=noteKeys.cend()) {
int key=it->second;
int num=12*curOctave+key;
if (key!=100 && key!=101 && key!=102) {
e->previewWave(curWave,num);
wavePreviewOn=true;
wavePreviewKey=ev->key.keysym.scancode;
wavePreviewNote=num;
}
}
break;
}
case GUI_WINDOW_ORDERS: // ignore here
break;
case GUI_WINDOW_PATTERN:
if (settings.notePreviewBehavior==1) {
if (cursor.xFine!=0) break;
} else if (settings.notePreviewBehavior==2) {
if (edit && cursor.xFine!=0) break;
}
// fall-through
default: {
auto it=noteKeys.find(ev->key.keysym.scancode);
if (it!=noteKeys.cend()) {
int key=it->second;
int num=12*curOctave+key;
if (num<-60) num=-60; // C-(-5)
if (num>119) num=119; // B-9
if (key!=100 && key!=101 && key!=102) {
previewNote(cursor.xCoarse,num);
}
}
break;
}
}
}
} else if (ev->type==SDL_KEYUP) {
stopPreviewNote(ev->key.keysym.scancode,true);
if (wavePreviewOn) {
if (ev->key.keysym.scancode==wavePreviewKey) {
wavePreviewOn=false;
e->stopWavePreview();
}
}
if (samplePreviewOn) {
if (ev->key.keysym.scancode==samplePreviewKey) {
samplePreviewOn=false;
e->stopSamplePreview();
}
}
}
return 1;
}
2022-05-17 17:46:52 +00:00
#define FIND_POINT(p,pid) \
for (TouchPoint& i: activePoints) { \
if (i.id==pid) { \
p=&i; \
} \
}
void FurnaceGUI::processPoint(SDL_Event& ev) {
switch (ev.type) {
case SDL_MOUSEMOTION: {
TouchPoint* point=NULL;
FIND_POINT(point,-1);
if (point!=NULL) {
point->x=(double)ev.motion.x*((double)canvasW/(double)scrW);
point->y=(double)ev.motion.y*((double)canvasH/(double)scrH);
2022-05-17 17:46:52 +00:00
}
break;
}
case SDL_MOUSEBUTTONDOWN: {
if (ev.button.button!=SDL_BUTTON_LEFT) break;
2022-05-17 17:46:52 +00:00
for (size_t i=0; i<activePoints.size(); i++) {
TouchPoint& point=activePoints[i];
if (point.id==-1) {
releasedPoints.push_back(point);
activePoints.erase(activePoints.begin()+i);
break;
}
}
TouchPoint newPoint(ev.button.x,ev.button.y);
#ifdef __APPLE__
2022-05-17 20:48:37 +00:00
newPoint.x*=dpiScale;
newPoint.y*=dpiScale;
2022-05-17 17:46:52 +00:00
#endif
activePoints.push_back(newPoint);
pressedPoints.push_back(newPoint);
break;
}
case SDL_MOUSEBUTTONUP: {
if (ev.button.button!=SDL_BUTTON_LEFT) break;
2022-05-17 17:46:52 +00:00
for (size_t i=0; i<activePoints.size(); i++) {
TouchPoint& point=activePoints[i];
if (point.id==-1) {
releasedPoints.push_back(point);
activePoints.erase(activePoints.begin()+i);
break;
}
}
break;
}
case SDL_FINGERMOTION: {
TouchPoint* point=NULL;
FIND_POINT(point,ev.tfinger.fingerId);
if (point!=NULL) {
2022-09-09 23:53:20 +00:00
float prevX=point->x;
float prevY=point->y;
point->x=ev.tfinger.x*canvasW;
point->y=ev.tfinger.y*canvasH;
2022-05-17 17:46:52 +00:00
point->z=ev.tfinger.pressure;
2022-05-27 21:10:37 +00:00
if (point->id==0) {
ImGui::GetIO().AddMousePosEvent(point->x,point->y);
2022-09-09 23:53:20 +00:00
pointMotion(point->x,point->y,point->x-prevX,point->y-prevY);
2022-05-27 21:10:37 +00:00
}
2022-05-17 17:46:52 +00:00
}
break;
}
case SDL_FINGERDOWN: {
for (size_t i=0; i<activePoints.size(); i++) {
TouchPoint& point=activePoints[i];
if (point.id==ev.tfinger.fingerId) {
releasedPoints.push_back(point);
activePoints.erase(activePoints.begin()+i);
break;
}
}
TouchPoint newPoint(ev.tfinger.fingerId,ev.tfinger.x*canvasW,ev.tfinger.y*canvasH,ev.tfinger.pressure);
2022-05-17 17:46:52 +00:00
activePoints.push_back(newPoint);
pressedPoints.push_back(newPoint);
2022-05-27 21:10:37 +00:00
if (newPoint.id==0) {
ImGui::GetIO().AddMousePosEvent(newPoint.x,newPoint.y);
ImGui::GetIO().AddMouseButtonEvent(ImGuiMouseButton_Left,true);
2022-09-09 23:53:20 +00:00
pointDown(newPoint.x,newPoint.y,0);
2022-05-27 21:10:37 +00:00
}
2022-05-17 17:46:52 +00:00
break;
}
case SDL_FINGERUP: {
for (size_t i=0; i<activePoints.size(); i++) {
TouchPoint& point=activePoints[i];
if (point.id==ev.tfinger.fingerId) {
2022-05-27 21:10:37 +00:00
if (point.id==0) {
ImGui::GetIO().AddMouseButtonEvent(ImGuiMouseButton_Left,false);
2022-09-09 00:15:19 +00:00
//ImGui::GetIO().AddMousePosEvent(-FLT_MAX,-FLT_MAX);
2022-09-09 23:53:20 +00:00
pointUp(point.x,point.y,0);
2022-05-27 21:10:37 +00:00
}
2022-09-09 23:53:20 +00:00
releasedPoints.push_back(point);
activePoints.erase(activePoints.begin()+i);
2022-05-17 17:46:52 +00:00
break;
}
}
break;
}
}
}
2022-09-09 23:53:20 +00:00
void FurnaceGUI::pointDown(int x, int y, int button) {
aboutOpen=false;
if (bindSetActive) {
bindSetActive=false;
bindSetPending=false;
actionKeys[bindSetTarget]=bindSetPrevValue;
bindSetTarget=0;
bindSetPrevValue=0;
}
if (introPos<11.0 && !shortIntro) {
2023-02-19 05:08:37 +00:00
introSkipDo=true;
}
2022-09-09 23:53:20 +00:00
}
void FurnaceGUI::pointUp(int x, int y, int button) {
2022-09-30 22:47:17 +00:00
if (macroDragActive || macroLoopDragActive || waveDragActive || (sampleDragActive && sampleDragMode && sampleDragTarget)) {
2022-09-09 23:53:20 +00:00
MARK_MODIFIED;
}
if (macroDragActive && macroDragLineMode && !macroDragMouseMoved) {
displayMacroMenu=true;
}
macroDragActive=false;
macroDragBitMode=false;
macroDragInitialValue=false;
macroDragInitialValueSet=false;
macroDragLastX=-1;
macroDragLastY=-1;
macroLoopDragActive=false;
waveDragActive=false;
if (introPos<11.0 && introSkip<0.5 && !shortIntro) {
2023-02-19 05:08:37 +00:00
introSkipDo=false;
}
2022-09-09 23:53:20 +00:00
if (sampleDragActive) {
logD("stopping sample drag");
if (sampleDragMode) {
e->renderSamplesP();
} else {
if (sampleSelStart>sampleSelEnd) {
sampleSelStart^=sampleSelEnd;
sampleSelEnd^=sampleSelStart;
sampleSelStart^=sampleSelEnd;
}
}
}
sampleDragActive=false;
2022-11-30 22:20:04 +00:00
if (orderScrollLocked) {
int targetOrder=round(orderScroll);
2022-11-30 22:37:48 +00:00
if (orderScrollTolerance) {
2022-12-03 06:13:13 +00:00
targetOrder=round(orderScroll+(orderScrollRealOrigin.x-((float)canvasW/2.0f))/(40.0f*dpiScale));
2022-11-30 22:37:48 +00:00
}
2022-11-30 22:20:04 +00:00
if (targetOrder<0) targetOrder=0;
if (targetOrder>e->curSubSong->ordersLen-1) targetOrder=e->curSubSong->ordersLen-1;
if (curOrder!=targetOrder) setOrder(targetOrder);
}
orderScrollLocked=false;
2022-11-30 22:37:48 +00:00
orderScrollTolerance=false;
2022-12-04 22:19:21 +00:00
if (dragMobileMenu) {
dragMobileMenu=false;
if (mobileMenuOpen) {
mobileMenuOpen=(mobileMenuPos>=0.85f);
} else {
mobileMenuOpen=(mobileMenuPos>=0.15f);
}
}
2022-12-07 03:40:23 +00:00
if (dragMobileEditButton) {
dragMobileEditButton=false;
}
2022-09-09 23:53:20 +00:00
}
void FurnaceGUI::pointMotion(int x, int y, int xrel, int yrel) {
if (selecting) {
// detect whether we have to scroll
if (y<patWindowPos.y+2.0f*dpiScale) {
addScroll(-1);
}
if (y>patWindowPos.y+patWindowSize.y-2.0f*dpiScale) {
addScroll(1);
}
}
2022-11-30 22:20:04 +00:00
if (macroDragActive || macroLoopDragActive || waveDragActive || sampleDragActive || orderScrollLocked) {
2022-09-09 23:53:20 +00:00
int distance=fabs((double)xrel);
if (distance<1) distance=1;
float start=x-xrel;
float end=x;
float startY=y-yrel;
float endY=y;
for (int i=0; i<=distance; i++) {
float fraction=(float)i/(float)distance;
float x=start+(end-start)*fraction;
float y=startY+(endY-startY)*fraction;
processDrags(x,y);
}
}
}
// how many pixels should be visible at least at x/y dir
#define OOB_PIXELS_SAFETY 25
bool FurnaceGUI::detectOutOfBoundsWindow(SDL_Rect& failing) {
2022-09-08 22:04:38 +00:00
int count=SDL_GetNumVideoDisplays();
if (count<1) {
logW("bounds check: error: %s",SDL_GetError());
return false;
}
SDL_Rect rect;
2022-09-08 22:04:38 +00:00
for (int i=0; i<count; i++) {
if (SDL_GetDisplayUsableBounds(i,&rect)!=0) {
logW("bounds check: error in display %d: %s",i,SDL_GetError());
continue;
}
2022-09-08 22:04:38 +00:00
bool xbound=((rect.x+OOB_PIXELS_SAFETY)<=(scrX+scrW)) && ((rect.x+rect.w-OOB_PIXELS_SAFETY)>=scrX);
bool ybound=((rect.y+OOB_PIXELS_SAFETY)<=(scrY+scrH)) && ((rect.y+rect.h-OOB_PIXELS_SAFETY)>=scrY);
logD("bounds check: display %d is at %dx%dx%dx%d: %s%s",i,rect.x+OOB_PIXELS_SAFETY,rect.y+OOB_PIXELS_SAFETY,rect.x+rect.w-OOB_PIXELS_SAFETY,rect.y+rect.h-OOB_PIXELS_SAFETY,xbound?"x":"",ybound?"y":"");
2022-09-08 22:04:38 +00:00
if (xbound && ybound) {
return true;
}
}
2023-05-01 06:36:47 +00:00
failing=rect;
return false;
}
2023-04-14 00:43:48 +00:00
#define DECLARE_METRIC(_n) \
int __perfM##_n;
#define MEASURE_BEGIN(_n) \
__perfM##_n=SDL_GetPerformanceCounter();
#define MEASURE_END(_n) \
if (perfMetricsLen<64) { \
perfMetrics[perfMetricsLen++]=FurnaceGUIPerfMetric(#_n,SDL_GetPerformanceCounter()-__perfM##_n); \
}
#define MEASURE(_n,_x) \
MEASURE_BEGIN(_n) \
_x; \
MEASURE_END(_n)
2021-12-14 09:45:44 +00:00
bool FurnaceGUI::loop() {
2023-04-14 00:43:48 +00:00
DECLARE_METRIC(calcChanOsc)
DECLARE_METRIC(mobileControls)
DECLARE_METRIC(mobileOrderSel)
DECLARE_METRIC(subSongs)
DECLARE_METRIC(findReplace)
DECLARE_METRIC(spoiler)
DECLARE_METRIC(pattern)
DECLARE_METRIC(editControls)
DECLARE_METRIC(speed)
DECLARE_METRIC(grooves)
DECLARE_METRIC(songInfo)
DECLARE_METRIC(orders)
DECLARE_METRIC(intro)
DECLARE_METRIC(sampleList)
DECLARE_METRIC(sampleEdit)
DECLARE_METRIC(waveList)
DECLARE_METRIC(waveEdit)
DECLARE_METRIC(insList)
DECLARE_METRIC(insEdit)
DECLARE_METRIC(mixer)
DECLARE_METRIC(readOsc)
DECLARE_METRIC(osc)
DECLARE_METRIC(chanOsc)
DECLARE_METRIC(volMeter)
DECLARE_METRIC(settings)
DECLARE_METRIC(debug)
DECLARE_METRIC(stats)
DECLARE_METRIC(compatFlags)
DECLARE_METRIC(piano)
DECLARE_METRIC(notes)
DECLARE_METRIC(channels)
DECLARE_METRIC(patManager)
DECLARE_METRIC(sysManager)
DECLARE_METRIC(clock)
DECLARE_METRIC(regView)
DECLARE_METRIC(log)
DECLARE_METRIC(effectList)
DECLARE_METRIC(popup)
2022-11-10 21:53:18 +00:00
#ifdef IS_MOBILE
bool doThreadedInput=true;
#else
bool doThreadedInput=!settings.noThreadedInput;
2022-11-10 21:53:18 +00:00
#endif
if (doThreadedInput) {
logD("key input: event filter");
SDL_SetEventFilter(_processEvent,this);
} else {
logD("key input: main thread");
}
2021-12-14 09:45:44 +00:00
while (!quit) {
SDL_Event ev;
2022-04-16 23:35:25 +00:00
if (e->isPlaying()) {
WAKE_UP;
}
if (--drawHalt<=0) {
drawHalt=0;
if (settings.powerSave) SDL_WaitEventTimeout(NULL,500);
}
2023-04-14 00:43:48 +00:00
memcpy(perfMetricsLast,perfMetrics,64*sizeof(FurnaceGUIPerfMetric));
perfMetricsLastLen=perfMetricsLen;
perfMetricsLen=0;
2022-05-27 05:19:10 +00:00
eventTimeBegin=SDL_GetPerformanceCounter();
2022-09-09 23:53:20 +00:00
bool updateWindow=false;
if (injectBackUp) {
ImGui::GetIO().AddKeyEvent(ImGuiKey_Backspace,false);
injectBackUp=false;
}
2021-12-14 09:45:44 +00:00
while (SDL_PollEvent(&ev)) {
2022-04-16 23:35:25 +00:00
WAKE_UP;
2021-12-14 09:45:44 +00:00
ImGui_ImplSDL2_ProcessEvent(&ev);
2022-05-17 17:46:52 +00:00
processPoint(ev);
if (!doThreadedInput) processEvent(&ev);
2021-12-14 09:45:44 +00:00
switch (ev.type) {
case SDL_MOUSEMOTION: {
int motionX=(double)ev.motion.x*((double)canvasW/(double)scrW);
int motionY=(double)ev.motion.y*((double)canvasH/(double)scrH);
int motionXrel=(double)ev.motion.xrel*((double)canvasW/(double)scrW);
int motionYrel=(double)ev.motion.yrel*((double)canvasH/(double)scrH);
2022-09-09 23:53:20 +00:00
pointMotion(motionX,motionY,motionXrel,motionYrel);
2021-12-14 09:45:44 +00:00
break;
}
2021-12-14 09:45:44 +00:00
case SDL_MOUSEBUTTONUP:
2022-09-09 23:53:20 +00:00
pointUp(ev.button.x,ev.button.y,ev.button.button);
2021-12-14 09:45:44 +00:00
break;
2021-12-19 04:03:50 +00:00
case SDL_MOUSEBUTTONDOWN:
2022-09-09 23:53:20 +00:00
pointDown(ev.button.x,ev.button.y,ev.button.button);
2021-12-19 04:03:50 +00:00
break;
case SDL_MOUSEWHEEL:
wheelX+=ev.wheel.x;
wheelY+=ev.wheel.y;
break;
2021-12-14 09:45:44 +00:00
case SDL_WINDOWEVENT:
switch (ev.window.event) {
case SDL_WINDOWEVENT_RESIZED:
scrW=ev.window.data1;
scrH=ev.window.data2;
2022-09-08 22:04:38 +00:00
portrait=(scrW<scrH);
2022-09-09 00:15:19 +00:00
logV("portrait: %d (%dx%d)",portrait,scrW,scrH);
logD("window resized to %dx%d",scrW,scrH);
updateWindow=true;
2023-06-13 00:43:26 +00:00
rend->resized(ev);
break;
case SDL_WINDOWEVENT_MOVED:
scrX=ev.window.data1;
scrY=ev.window.data2;
updateWindow=true;
shallDetectScale=2;
2022-12-24 03:36:14 +00:00
logV("window moved to %dx%d",scrX,scrY);
break;
case SDL_WINDOWEVENT_SIZE_CHANGED:
logV("window size changed to %dx%d",ev.window.data1,ev.window.data2);
break;
case SDL_WINDOWEVENT_MINIMIZED:
logV("window minimized");
break;
case SDL_WINDOWEVENT_MAXIMIZED:
scrMax=true;
updateWindow=true;
2022-12-24 03:36:14 +00:00
logV("window maximized");
break;
case SDL_WINDOWEVENT_RESTORED:
scrMax=false;
updateWindow=true;
2022-12-24 03:36:14 +00:00
logV("window restored");
break;
case SDL_WINDOWEVENT_SHOWN:
logV("window shown");
break;
case SDL_WINDOWEVENT_HIDDEN:
logV("window hidden");
break;
case SDL_WINDOWEVENT_EXPOSED:
logV("window exposed");
2021-12-14 09:45:44 +00:00
break;
}
break;
2023-07-02 10:41:51 +00:00
#if SDL_VERSION_ATLEAST(2,0,4)
case SDL_RENDER_DEVICE_RESET:
killGraphics=true;
break;
#endif
#if SDL_VERSION_ATLEAST(2,0,17)
case SDL_DISPLAYEVENT: {
switch (ev.display.event) {
2023-01-17 22:57:42 +00:00
case SDL_DISPLAYEVENT_CONNECTED:
logD("display %d connected!",ev.display.display);
updateWindow=true;
shallDetectScale=16;
2023-01-17 22:57:42 +00:00
break;
case SDL_DISPLAYEVENT_DISCONNECTED:
logD("display %d disconnected!",ev.display.display);
updateWindow=true;
shallDetectScale=16;
2023-01-17 22:57:42 +00:00
break;
case SDL_DISPLAYEVENT_ORIENTATION:
logD("display oriented to %d",ev.display.data1);
updateWindow=true;
break;
}
break;
}
#endif
2021-12-14 22:45:37 +00:00
case SDL_KEYDOWN:
if (!ImGui::GetIO().WantCaptureKeyboard) {
keyDown(ev);
}
#ifdef IS_MOBILE
injectBackUp=true;
#endif
2021-12-14 22:45:37 +00:00
break;
case SDL_KEYUP:
// for now
2021-12-14 22:45:37 +00:00
break;
case SDL_DROPFILE:
if (ev.drop.file!=NULL) {
int sampleCountBefore=e->song.sampleLen;
std::vector<DivInstrument*> instruments=e->instrumentFromFile(ev.drop.file);
DivWavetable* droppedWave=NULL;
2023-06-11 00:22:44 +00:00
DivSample* droppedSample=NULL;
if (!instruments.empty()) {
if (e->song.sampleLen!=sampleCountBefore) {
e->renderSamplesP();
}
if (!e->getWarnings().empty()) {
showWarning(e->getWarnings(),GUI_WARN_GENERIC);
}
for (DivInstrument* i: instruments) {
e->addInstrumentPtr(i);
}
nextWindow=GUI_WINDOW_INS_LIST;
MARK_MODIFIED;
} else if ((droppedWave=e->waveFromFile(ev.drop.file,false))!=NULL) {
e->addWavePtr(droppedWave);
nextWindow=GUI_WINDOW_WAVE_LIST;
MARK_MODIFIED;
} else if ((droppedSample=e->sampleFromFile(ev.drop.file))!=NULL) {
e->addSamplePtr(droppedSample);
nextWindow=GUI_WINDOW_SAMPLE_LIST;
MARK_MODIFIED;
} else if (modified) {
nextFile=ev.drop.file;
showWarning("Unsaved changes! Save changes before opening file?",GUI_WARN_OPEN_DROP);
} else {
if (load(ev.drop.file)>0) {
showError(fmt::sprintf("Error while loading file! (%s)",lastError));
}
}
SDL_free(ev.drop.file);
}
break;
2021-12-14 09:45:44 +00:00
case SDL_QUIT:
2021-12-30 23:52:36 +00:00
if (modified) {
showWarning("Unsaved changes! Save changes before quitting?",GUI_WARN_QUIT);
2021-12-30 23:52:36 +00:00
} else {
quit=true;
return true;
}
2021-12-14 09:45:44 +00:00
break;
2021-12-13 07:03:36 +00:00
}
}
// update config x/y/w/h values based on scrMax state
2022-09-08 22:04:38 +00:00
if (updateWindow) {
logV("updateWindow is true");
if (!scrMax && !fullScreen) {
logV("updating scrConf");
scrConfX=scrX;
scrConfY=scrY;
scrConfW=scrW;
scrConfH=scrH;
}
2022-10-24 19:57:12 +00:00
}
// update canvas size as well
if (!rend->getOutputSize(canvasW,canvasH)) {
logW("loop: error while getting output size!");
2022-10-24 19:57:12 +00:00
} else {
//logV("updateWindow: canvas size %dx%d",canvasW,canvasH);
// and therefore window size
2022-12-24 03:36:14 +00:00
int prevScrW=scrW;
int prevScrH=scrH;
2022-10-24 20:18:35 +00:00
SDL_GetWindowSize(sdlWin,&scrW,&scrH);
2022-12-24 03:36:14 +00:00
if (prevScrW!=scrW || prevScrH!=scrH) {
logV("size change 2: %dx%d (from %dx%d)",scrW,scrH,prevScrW,prevScrH);
}
ImGui::GetIO().InputScale=(float)canvasW/(float)scrW;
}
wantCaptureKeyboard=ImGui::GetIO().WantTextInput;
2022-05-11 21:09:23 +00:00
if (wantCaptureKeyboard!=oldWantCaptureKeyboard) {
oldWantCaptureKeyboard=wantCaptureKeyboard;
if (wantCaptureKeyboard) {
SDL_StartTextInput();
} else {
SDL_StopTextInput();
}
}
if (wantCaptureKeyboard) {
WAKE_UP;
}
if (ImGui::GetIO().IsSomethingHappening) {
WAKE_UP;
}
if (ImGui::GetIO().MouseDown[0] || ImGui::GetIO().MouseDown[1] || ImGui::GetIO().MouseDown[2] || ImGui::GetIO().MouseDown[3] || ImGui::GetIO().MouseDown[4]) {
WAKE_UP;
}
2022-05-26 05:24:21 +00:00
2022-03-28 23:19:47 +00:00
while (true) {
midiLock.lock();
if (midiQueue.empty()) {
midiLock.unlock();
break;
}
TAMidiMessage msg=midiQueue.front();
midiLock.unlock();
if (msg.type==TA_MIDI_SYSEX) {
unsigned char* data=msg.sysExData.get();
for (size_t i=0; i<msg.sysExLen; i++) {
if ((i&15)==0) printf("\n");
printf("%.2x ",data[i]);
}
printf("\n");
if (!parseSysEx(data,msg.sysExLen)) {
logW("error while parsing SysEx data!");
}
}
2022-03-28 23:19:47 +00:00
// parse message here
2022-03-31 23:39:01 +00:00
if (learning!=-1) {
if (learning>=0 && learning<(int)midiMap.binds.size()) {
midiMap.binds[learning].type=msg.type>>4;
midiMap.binds[learning].channel=msg.type&15;
midiMap.binds[learning].data1=msg.data[0];
2022-04-01 06:50:01 +00:00
switch (msg.type&0xf0) {
2022-03-31 23:39:01 +00:00
case TA_MIDI_NOTE_OFF:
case TA_MIDI_NOTE_ON:
case TA_MIDI_AFTERTOUCH:
case TA_MIDI_PITCH_BEND:
case TA_MIDI_CONTROL:
midiMap.binds[learning].data2=msg.data[1];
break;
default:
midiMap.binds[learning].data2=128;
break;
2022-03-30 20:44:27 +00:00
}
2022-03-31 23:39:01 +00:00
}
learning=-1;
} else {
int action=midiMap.at(msg);
if (action!=0) {
doAction(action);
} else switch (msg.type&0xf0) {
case TA_MIDI_NOTE_ON:
2022-04-01 06:50:01 +00:00
if (midiMap.valueInputStyle==0 || midiMap.valueInputStyle>3 || cursor.xFine==0) {
if (midiMap.noteInput && edit && msg.data[1]!=0) {
noteInput(
msg.data[0]-12,
0,
midiMap.volInput?((int)(pow((double)msg.data[1]/127.0,midiMap.volExp)*127.0)):-1
);
}
} else {
if (edit && msg.data[1]!=0) {
switch (midiMap.valueInputStyle) {
case 1: {
int val=msg.data[0]%24;
if (val<16) {
valueInput(val);
}
break;
}
case 2:
valueInput(msg.data[0]&15);
break;
case 3:
int val=altValues[msg.data[0]%24];
if (val>=0) {
valueInput(val);
}
break;
}
}
2022-03-31 23:39:01 +00:00
}
break;
case TA_MIDI_PROGRAM:
if (midiMap.programChange) {
curIns=msg.data[0];
if (curIns>=(int)e->song.ins.size()) curIns=e->song.ins.size()-1;
2022-05-21 23:36:15 +00:00
wavePreviewInit=true;
updateFMPreview=true;
2022-03-31 23:39:01 +00:00
}
break;
2022-04-01 06:50:01 +00:00
case TA_MIDI_CONTROL:
bool gchanged=false;
if (msg.data[0]==midiMap.valueInputControlMSB) {
midiMap.valueInputCurMSB=msg.data[1];
gchanged=true;
}
if (msg.data[0]==midiMap.valueInputControlLSB) {
midiMap.valueInputCurLSB=msg.data[1];
gchanged=true;
}
if (msg.data[0]==midiMap.valueInputControlSingle) {
midiMap.valueInputCurSingle=msg.data[1];
gchanged=true;
}
if (gchanged && cursor.xFine>0) {
switch (midiMap.valueInputStyle) {
case 4: // dual CC
valueInput(((midiMap.valueInputCurMSB>>3)<<4)|(midiMap.valueInputCurLSB>>3),true);
break;
case 5: // 14-bit
valueInput((midiMap.valueInputCurMSB<<1)|(midiMap.valueInputCurLSB>>6),true);
break;
case 6: // single CC
valueInput((midiMap.valueInputCurSingle*255)/127,true);
break;
}
}
for (int i=0; i<18; i++) {
bool changed=false;
if (midiMap.valueInputSpecificStyle[i]!=0) {
if (msg.data[0]==midiMap.valueInputSpecificMSB[i]) {
changed=true;
midiMap.valueInputCurMSBS[i]=msg.data[1];
}
if (msg.data[0]==midiMap.valueInputSpecificLSB[i]) {
changed=true;
midiMap.valueInputCurLSBS[i]=msg.data[1];
}
if (msg.data[0]==midiMap.valueInputSpecificSingle[i]) {
changed=true;
midiMap.valueInputCurSingleS[i]=msg.data[1];
}
if (changed) switch (midiMap.valueInputStyle) {
case 1: // dual CC
valueInput(((midiMap.valueInputCurMSBS[i]>>3)<<4)|(midiMap.valueInputCurLSBS[i]>>3),true,i+2);
break;
case 2: // 14-bit
valueInput((midiMap.valueInputCurMSBS[i]<<1)|(midiMap.valueInputCurLSBS[i]>>6),true,i+2);
break;
case 3: // single CC
valueInput((midiMap.valueInputCurSingleS[i]*255)/127,true,i+2);
break;
}
}
}
break;
2022-03-31 23:39:01 +00:00
}
2022-03-30 20:44:27 +00:00
}
2022-03-28 23:19:47 +00:00
midiLock.lock();
midiQueue.pop();
midiLock.unlock();
}
2022-05-27 05:19:10 +00:00
if (notifyWaveChange) {
notifyWaveChange=false;
e->notifyWaveChange(curWave);
}
2022-05-27 05:19:10 +00:00
eventTimeEnd=SDL_GetPerformanceCounter();
2023-01-23 21:21:58 +00:00
if (SDL_GetWindowFlags(sdlWin)&SDL_WINDOW_MINIMIZED) {
2023-01-23 21:40:56 +00:00
SDL_Delay(30);
drawHalt=0;
2023-01-23 21:21:58 +00:00
continue;
}
2023-02-24 05:38:32 +00:00
if (firstFrame) {
if (!tutorial.introPlayed || settings.alwaysPlayIntro==3 || (settings.alwaysPlayIntro==2 && curFileName.empty())) {
unsigned char* introTemp=new unsigned char[intro_fur_len];
memcpy(introTemp,intro_fur,intro_fur_len);
e->load(introTemp,intro_fur_len);
}
}
if (!e->isRunning()) {
activeNotes.clear();
memset(chanOscVol,0,DIV_MAX_CHANS*sizeof(float));
memset(chanOscPitch,0,DIV_MAX_CHANS*sizeof(float));
memset(chanOscBright,0,DIV_MAX_CHANS*sizeof(float));
e->synchronized([this]() {
for (int i=0; i<e->getTotalChannelCount(); i++) {
DivDispatchOscBuffer* buf=e->getOscBuffer(i);
2023-04-06 07:20:48 +00:00
if (buf!=NULL) {
buf->needle=0;
buf->readNeedle=0;
}
}
});
}
2023-07-02 05:09:39 +00:00
// recover from dead graphics
if (rend->isDead() || killGraphics) {
killGraphics=false;
logW("graphics are dead! restarting...");
if (sampleTex!=NULL) {
rend->destroyTexture(sampleTex);
sampleTex=NULL;
}
if (chanOscGradTex!=NULL) {
rend->destroyTexture(chanOscGradTex);
chanOscGradTex=NULL;
}
for (auto& i: images) {
if (i.second->tex!=NULL) {
rend->destroyTexture(i.second->tex);
i.second->tex=NULL;
}
}
commitState();
rend->quitGUI();
rend->quit();
ImGui_ImplSDL2_Shutdown();
int initAttempts=0;
SDL_Delay(500);
logD("starting render backend...");
while (++initAttempts<=5) {
if (rend->init(sdlWin)) {
break;
}
SDL_Delay(1000);
logV("trying again...");
}
if (initAttempts>5) {
reportError("can't keep going without graphics! Furnace will quit now.");
quit=true;
break;
}
rend->clear(ImVec4(0.0,0.0,0.0,1.0));
rend->present();
logD("preparing user interface...");
rend->initGUI(sdlWin);
logD("building font...");
if (!ImGui::GetIO().Fonts->Build()) {
logE("error while building font atlas!");
showError("error while loading fonts! please check your settings.");
ImGui::GetIO().Fonts->Clear();
mainFont=ImGui::GetIO().Fonts->AddFontDefault();
patFont=mainFont;
2023-07-10 19:38:26 +00:00
bigFont=mainFont;
headFont=mainFont;
2023-07-02 05:09:39 +00:00
if (rend) rend->destroyFontsTexture();
if (!ImGui::GetIO().Fonts->Build()) {
logE("error again while building font atlas!");
}
}
firstFrame=true;
mustClear=2;
initialScreenWipe=1.0f;
continue;
}
bool fontsFailed=false;
2022-05-27 05:19:10 +00:00
layoutTimeBegin=SDL_GetPerformanceCounter();
if (!rend->newFrame()) {
fontsFailed=true;
}
2021-12-14 09:45:44 +00:00
ImGui_ImplSDL2_NewFrame(sdlWin);
ImGui::NewFrame();
curWindowLast=curWindow;
2021-12-14 22:45:37 +00:00
curWindow=GUI_WINDOW_NOTHING;
2022-04-21 23:10:59 +00:00
editOptsVisible=false;
2021-12-14 22:45:37 +00:00
2022-05-18 23:42:59 +00:00
if (!mobileUI) {
ImGui::BeginMainMenuBar();
2023-08-10 03:47:06 +00:00
if (ImGui::BeginMenu(settings.capitalMenuBar?"File":"file")) {
2023-04-02 21:39:06 +00:00
if (ImGui::MenuItem("new...",BIND_FOR(GUI_ACTION_NEW))) {
2022-05-18 23:42:59 +00:00
if (modified) {
showWarning("Unsaved changes! Save changes before creating a new song?",GUI_WARN_NEW);
} else {
displayNew=true;
2021-12-30 23:25:55 +00:00
}
}
2022-05-18 23:42:59 +00:00
if (ImGui::MenuItem("open...",BIND_FOR(GUI_ACTION_OPEN))) {
if (modified) {
showWarning("Unsaved changes! Save changes before opening another file?",GUI_WARN_OPEN);
} else {
openFileDialog(GUI_FILE_OPEN);
}
}
2022-09-10 23:53:27 +00:00
if (ImGui::BeginMenu("open recent")) {
exitDisabledTimer=1;
2022-09-10 23:53:27 +00:00
for (int i=0; i<(int)recentFile.size(); i++) {
String item=recentFile[i];
if (ImGui::MenuItem(item.c_str())) {
if (modified) {
nextFile=item;
showWarning("Unsaved changes! Save changes before opening file?",GUI_WARN_OPEN_DROP);
} else {
recentFile.erase(recentFile.begin()+i);
i--;
if (load(item)>0) {
showError(fmt::sprintf("Error while loading file! (%s)",lastError));
}
}
}
}
if (recentFile.empty()) {
ImGui::Text("nothing here yet");
} else {
ImGui::Separator();
if (ImGui::MenuItem("clear history")) {
showWarning("Are you sure you want to clear the recent file list?",GUI_WARN_CLEAR_HISTORY);
}
2022-09-10 23:53:27 +00:00
}
ImGui::EndMenu();
}
2022-05-18 23:42:59 +00:00
ImGui::Separator();
if (ImGui::MenuItem("save",BIND_FOR(GUI_ACTION_SAVE))) {
2023-04-06 09:22:43 +00:00
if (curFileName=="" || (curFileName.find(backupPath)==0) || e->song.version>=0xff00) {
2022-05-18 23:42:59 +00:00
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));
}
}
}
2022-05-18 23:42:59 +00:00
if (ImGui::MenuItem("save as...",BIND_FOR(GUI_ACTION_SAVE_AS))) {
openFileDialog(GUI_FILE_SAVE);
}
2022-09-04 07:27:00 +00:00
if (ImGui::MenuItem("save as .dmf (1.1.3+)...")) {
openFileDialog(GUI_FILE_SAVE_DMF);
}
2022-09-04 07:27:00 +00:00
if (ImGui::MenuItem("save as .dmf (1.0/legacy)...")) {
2022-05-18 23:42:59 +00:00
openFileDialog(GUI_FILE_SAVE_DMF_LEGACY);
}
2022-05-18 23:42:59 +00:00
ImGui::Separator();
if (ImGui::BeginMenu("export audio...")) {
exitDisabledTimer=1;
2022-05-18 23:42:59 +00:00
if (ImGui::MenuItem("one file")) {
openFileDialog(GUI_FILE_EXPORT_AUDIO_ONE);
}
if (ImGui::MenuItem("multiple files (one per chip)")) {
openFileDialog(GUI_FILE_EXPORT_AUDIO_PER_SYS);
2022-05-18 23:42:59 +00:00
}
if (ImGui::MenuItem("multiple files (one per channel)")) {
openFileDialog(GUI_FILE_EXPORT_AUDIO_PER_CHANNEL);
}
2022-05-18 23:42:59 +00:00
if (ImGui::InputInt("Loops",&exportLoops,1,2)) {
if (exportLoops<0) exportLoops=0;
}
2022-06-06 08:05:55 +00:00
if (ImGui::InputDouble("Fade out (seconds)",&exportFadeOut,1.0,2.0,"%.1f")) {
if (exportFadeOut<0.0) exportFadeOut=0.0;
}
2022-05-18 23:42:59 +00:00
ImGui::EndMenu();
}
2022-05-18 23:42:59 +00:00
if (ImGui::BeginMenu("export VGM...")) {
exitDisabledTimer=1;
ImGui::Text("settings:");
if (ImGui::BeginCombo("format version",fmt::sprintf("%d.%.2x",vgmExportVersion>>8,vgmExportVersion&0xff).c_str())) {
for (int i=0; i<7; i++) {
if (ImGui::Selectable(fmt::sprintf("%d.%.2x",vgmVersions[i]>>8,vgmVersions[i]&0xff).c_str(),vgmExportVersion==vgmVersions[i])) {
vgmExportVersion=vgmVersions[i];
2022-05-18 23:42:59 +00:00
}
}
ImGui::EndCombo();
}
ImGui::Checkbox("loop",&vgmExportLoop);
if (vgmExportLoop && e->song.loopModality==2) {
2023-04-15 21:08:38 +00:00
ImGui::Text("loop trail:");
ImGui::Indent();
if (ImGui::RadioButton("auto-detect",vgmExportTrailingTicks==-1)) {
vgmExportTrailingTicks=-1;
}
2023-04-15 21:08:38 +00:00
if (ImGui::RadioButton("add one loop",vgmExportTrailingTicks==-2)) {
vgmExportTrailingTicks=-2;
}
if (ImGui::RadioButton("custom",vgmExportTrailingTicks>=0)) {
vgmExportTrailingTicks=0;
}
if (vgmExportTrailingTicks>=0) {
ImGui::SameLine();
if (ImGui::InputInt("##TrailTicks",&vgmExportTrailingTicks,1,100)) {
if (vgmExportTrailingTicks<0) vgmExportTrailingTicks=0;
}
}
ImGui::Unindent();
}
ImGui::Checkbox("add pattern change hints",&vgmExportPatternHints);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip(
"inserts data blocks on pattern changes.\n"
"useful if you are writing a playback routine.\n\n"
"the format of a pattern change data block is:\n"
"67 66 FE ll ll ll ll 01 oo rr pp pp pp ...\n"
"- ll: length, a 32-bit little-endian number\n"
"- oo: order\n"
"- rr: initial row (a 0Dxx effect is able to select a different row)\n"
"- pp: pattern index (one per channel)\n\n"
"pattern indexes are ordered as they appear in the song."
);
}
ImGui::Checkbox("direct stream mode",&vgmExportDirectStream);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip(
"required for DualPCM and MSM6258 export.\n\n"
"allows for volume/direction changes when playing samples,\n"
"at the cost of a massive increase in file size."
);
}
ImGui::Text("chips to export:");
bool hasOneAtLeast=false;
for (int i=0; i<e->song.systemLen; i++) {
int minVersion=e->minVGMVersion(e->song.system[i]);
ImGui::BeginDisabled(minVersion>vgmExportVersion || minVersion==0);
ImGui::Checkbox(fmt::sprintf("%d. %s##_SYSV%d",i+1,getSystemName(e->song.system[i]),i).c_str(),&willExport[i]);
ImGui::EndDisabled();
if (minVersion>vgmExportVersion) {
if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) {
ImGui::SetTooltip("this chip is only available in VGM %d.%.2x and higher!",minVersion>>8,minVersion&0xff);
}
} else if (minVersion==0) {
if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) {
ImGui::SetTooltip("this chip is not supported by the VGM format!");
2022-05-18 23:42:59 +00:00
}
} else {
if (willExport[i]) hasOneAtLeast=true;
}
}
ImGui::Text("select the chip you wish to export,");
ImGui::Text("but only up to %d of each type.",(vgmExportVersion>=0x151)?2:1);
if (hasOneAtLeast) {
if (ImGui::MenuItem("click to export")) {
openFileDialog(GUI_FILE_EXPORT_VGM);
}
} else {
ImGui::Text("nothing to export");
}
2022-05-18 23:42:59 +00:00
ImGui::EndMenu();
}
2022-05-26 05:24:21 +00:00
int numZSMCompat=0;
for (int i=0; i<e->song.systemLen; i++) {
if ((e->song.system[i] == DIV_SYSTEM_VERA) || (e->song.system[i] == DIV_SYSTEM_YM2151)) numZSMCompat++;
}
if (numZSMCompat > 0) {
if (ImGui::BeginMenu("export ZSM...")) {
exitDisabledTimer=1;
2023-03-13 09:20:54 +00:00
ImGui::Text("Commander X16 Zsound Music File");
if (ImGui::InputInt("Tick Rate (Hz)",&zsmExportTickRate,1,2)) {
if (zsmExportTickRate<1) zsmExportTickRate=1;
if (zsmExportTickRate>44100) zsmExportTickRate=44100;
}
ImGui::Checkbox("loop",&zsmExportLoop);
ImGui::SameLine();
2023-08-11 20:03:37 +00:00
ImGui::Checkbox("optimize size",&zsmExportOptimize);
ImGui::SameLine();
2023-03-13 09:20:54 +00:00
if (ImGui::Button("Begin Export")) {
openFileDialog(GUI_FILE_EXPORT_ZSM);
ImGui::CloseCurrentPopup();
}
ImGui::EndMenu();
}
}
int numAmiga=0;
for (int i=0; i<e->song.systemLen; i++) {
if (e->song.system[i]==DIV_SYSTEM_AMIGA) numAmiga++;
}
if (numAmiga && settings.iCannotWait) {
if (ImGui::BeginMenu("export Amiga validation data...")) {
exitDisabledTimer=1;
2023-03-13 09:20:54 +00:00
ImGui::Text(
"this is NOT ROM export! only use for making sure the\n"
"Furnace Amiga emulator is working properly by\n"
"comparing it with real Amiga output."
);
ImGui::AlignTextToFramePadding();
2023-03-13 09:20:54 +00:00
ImGui::Text("Directory");
ImGui::SameLine();
ImGui::InputText("##AVDPath",&workingDirROMExport);
if (ImGui::Button("Bake Data")) {
2023-03-14 01:01:01 +00:00
std::vector<DivROMExportOutput> out=e->buildROM(DIV_ROM_AMIGA_VALIDATION);
2023-03-14 06:27:45 +00:00
if (workingDirROMExport.size()>0) {
if (workingDirROMExport[workingDirROMExport.size()-1]!=DIR_SEPARATOR) workingDirROMExport+=DIR_SEPARATOR_STR;
}
for (DivROMExportOutput& i: out) {
String path=workingDirROMExport+i.name;
FILE* outFile=ps_fopen(path.c_str(),"wb");
if (outFile!=NULL) {
fwrite(i.data->getFinalBuf(),1,i.data->size(),outFile);
fclose(outFile);
}
i.data->finish();
delete i.data;
}
showError(fmt::sprintf("Done! Baked %d files.",(int)out.size()));
2023-03-13 09:20:54 +00:00
ImGui::CloseCurrentPopup();
}
ImGui::EndMenu();
2022-05-26 05:24:21 +00:00
}
}
if (ImGui::BeginMenu("export command stream...")) {
exitDisabledTimer=1;
2022-08-04 05:51:47 +00:00
ImGui::Text(
"this option exports a text or binary file which\n"
"contains a dump of the internal command stream\n"
"produced when playing the song.\n\n"
"technical/development use only!"
);
if (ImGui::Button("export (binary)")) {
openFileDialog(GUI_FILE_EXPORT_CMDSTREAM_BINARY);
}
if (ImGui::Button("export (text)")) {
2022-08-04 05:51:47 +00:00
openFileDialog(GUI_FILE_EXPORT_CMDSTREAM);
}
ImGui::EndMenu();
}
2023-02-24 08:47:53 +00:00
ImGui::Separator();
if (!settings.classicChipOptions) {
if (ImGui::MenuItem("manage chips")) {
nextWindow=GUI_WINDOW_SYS_MANAGER;
}
} else {
if (ImGui::BeginMenu("add chip...")) {
exitDisabledTimer=1;
DivSystem picked=systemPicker();
if (picked!=DIV_SYSTEM_NULL) {
if (!e->addSystem(picked)) {
showError("cannot add chip! ("+e->getLastError()+")");
} else {
MARK_MODIFIED;
}
ImGui::CloseCurrentPopup();
if (e->song.autoSystem) {
autoDetectSystem();
}
updateWindowTitle();
2022-05-18 23:42:59 +00:00
}
ImGui::EndMenu();
2022-01-28 23:12:56 +00:00
}
if (ImGui::BeginMenu("configure chip...")) {
exitDisabledTimer=1;
for (int i=0; i<e->song.systemLen; i++) {
if (ImGui::TreeNode(fmt::sprintf("%d. %s##_SYSP%d",i+1,getSystemName(e->song.system[i]),i).c_str())) {
drawSysConf(i,e->song.system[i],e->song.systemFlags[i],true,true);
ImGui::TreePop();
}
}
ImGui::EndMenu();
2022-01-09 21:36:47 +00:00
}
if (ImGui::BeginMenu("change chip...")) {
exitDisabledTimer=1;
ImGui::Checkbox("Preserve channel positions",&preserveChanPos);
for (int i=0; i<e->song.systemLen; i++) {
if (ImGui::BeginMenu(fmt::sprintf("%d. %s##_SYSC%d",i+1,getSystemName(e->song.system[i]),i).c_str())) {
DivSystem picked=systemPicker();
if (picked!=DIV_SYSTEM_NULL) {
e->changeSystem(i,picked,preserveChanPos);
MARK_MODIFIED;
if (e->song.autoSystem) {
autoDetectSystem();
}
updateWindowTitle();
ImGui::CloseCurrentPopup();
2023-02-06 23:52:51 +00:00
}
ImGui::EndMenu();
2022-09-22 00:27:42 +00:00
}
2022-01-09 21:36:47 +00:00
}
ImGui::EndMenu();
2022-01-09 21:36:47 +00:00
}
if (ImGui::BeginMenu("remove chip...")) {
exitDisabledTimer=1;
ImGui::Checkbox("Preserve channel positions",&preserveChanPos);
for (int i=0; i<e->song.systemLen; i++) {
if (ImGui::MenuItem(fmt::sprintf("%d. %s##_SYSR%d",i+1,getSystemName(e->song.system[i]),i).c_str())) {
if (!e->removeSystem(i,preserveChanPos)) {
showError("cannot remove chip! ("+e->getLastError()+")");
} else {
MARK_MODIFIED;
}
if (e->song.autoSystem) {
autoDetectSystem();
updateWindowTitle();
}
2023-02-24 08:47:53 +00:00
}
}
ImGui::EndMenu();
2023-02-24 08:47:53 +00:00
}
2022-05-18 23:42:59 +00:00
}
ImGui::BeginDisabled(exitDisabledTimer);
2022-05-18 23:42:59 +00:00
ImGui::Separator();
if (ImGui::MenuItem("restore backup",BIND_FOR(GUI_ACTION_OPEN_BACKUP))) {
doAction(GUI_ACTION_OPEN_BACKUP);
}
ImGui::Separator();
if (ImGui::MenuItem("exit")) {
if (modified) {
showWarning("Unsaved changes! Save before quitting?",GUI_WARN_QUIT);
} else {
quit=true;
}
2022-01-09 21:36:47 +00:00
}
ImGui::EndDisabled();
2021-12-18 03:14:41 +00:00
ImGui::EndMenu();
} else {
exitDisabledTimer=0;
2021-12-18 03:14:41 +00:00
}
2023-08-10 03:47:06 +00:00
if (ImGui::BeginMenu(settings.capitalMenuBar?"Edit":"edit")) {
2022-06-06 23:04:19 +00:00
ImGui::Text("...");
ImGui::Separator();
2022-05-18 23:42:59 +00:00
if (ImGui::MenuItem("undo",BIND_FOR(GUI_ACTION_UNDO))) doUndo();
if (ImGui::MenuItem("redo",BIND_FOR(GUI_ACTION_REDO))) doRedo();
ImGui::Separator();
editOptions(true);
ImGui::Separator();
if (ImGui::MenuItem("clear...")) {
2022-12-11 17:36:41 +00:00
doAction(GUI_ACTION_CLEAR);
2022-05-18 23:42:59 +00:00
}
ImGui::EndMenu();
2022-03-21 19:32:33 +00:00
}
2023-08-10 03:47:06 +00:00
if (ImGui::BeginMenu(settings.capitalMenuBar?"Settings":"settings")) {
2022-05-18 23:42:59 +00:00
#ifndef IS_MOBILE
if (ImGui::MenuItem("full screen",BIND_FOR(GUI_ACTION_FULLSCREEN),fullScreen)) {
doAction(GUI_ACTION_FULLSCREEN);
}
#endif
if (ImGui::MenuItem("lock layout",NULL,lockLayout)) {
2022-05-18 23:42:59 +00:00
lockLayout=!lockLayout;
2021-12-30 23:52:36 +00:00
}
2023-02-06 23:52:51 +00:00
if (ImGui::MenuItem("basic mode",NULL,basicMode)) {
if (basicMode) {
showWarning("prepare to unlock the full power of Furnace!",GUI_WARN_BASIC_MODE);
} else {
showWarning("are you sure? this will disable several things.",GUI_WARN_BASIC_MODE);
}
}
2022-05-18 23:42:59 +00:00
if (ImGui::MenuItem("visualizer",NULL,fancyPattern)) {
fancyPattern=!fancyPattern;
e->enableCommandStream(fancyPattern);
e->getCommandStream(cmdStream);
cmdStream.clear();
}
if (ImGui::MenuItem("reset layout")) {
showWarning("Are you sure you want to reset the workspace layout?",GUI_WARN_RESET_LAYOUT);
}
2022-09-10 00:23:53 +00:00
#ifdef IS_MOBILE
if (ImGui::MenuItem("switch to mobile view")) {
toggleMobileUI(!mobileUI);
}
#endif
2022-05-18 23:42:59 +00:00
if (ImGui::MenuItem("settings...",BIND_FOR(GUI_ACTION_WINDOW_SETTINGS))) {
syncSettings();
settingsOpen=true;
}
ImGui::EndMenu();
2021-12-14 09:45:44 +00:00
}
2023-08-10 03:47:06 +00:00
if (ImGui::BeginMenu(settings.capitalMenuBar?"Window":"window")) {
2022-05-18 23:42:59 +00:00
if (ImGui::MenuItem("song information",BIND_FOR(GUI_ACTION_WINDOW_SONG_INFO),songInfoOpen)) songInfoOpen=!songInfoOpen;
if (ImGui::MenuItem("subsongs",BIND_FOR(GUI_ACTION_WINDOW_SUBSONGS),subSongsOpen)) subSongsOpen=!subSongsOpen;
if (ImGui::MenuItem("speed",BIND_FOR(GUI_ACTION_WINDOW_SPEED),speedOpen)) speedOpen=!speedOpen;
2022-05-29 07:59:11 +00:00
if (settings.unifiedDataView) {
if (ImGui::MenuItem("assets",BIND_FOR(GUI_ACTION_WINDOW_INS_LIST),insListOpen)) insListOpen=!insListOpen;
} else {
if (ImGui::MenuItem("instruments",BIND_FOR(GUI_ACTION_WINDOW_INS_LIST),insListOpen)) insListOpen=!insListOpen;
if (ImGui::MenuItem("wavetables",BIND_FOR(GUI_ACTION_WINDOW_WAVE_LIST),waveListOpen)) waveListOpen=!waveListOpen;
if (ImGui::MenuItem("samples",BIND_FOR(GUI_ACTION_WINDOW_SAMPLE_LIST),sampleListOpen)) sampleListOpen=!sampleListOpen;
}
2022-05-18 23:42:59 +00:00
if (ImGui::MenuItem("orders",BIND_FOR(GUI_ACTION_WINDOW_ORDERS),ordersOpen)) ordersOpen=!ordersOpen;
if (ImGui::MenuItem("pattern",BIND_FOR(GUI_ACTION_WINDOW_PATTERN),patternOpen)) patternOpen=!patternOpen;
2023-02-24 08:47:53 +00:00
if (ImGui::MenuItem("mixer",BIND_FOR(GUI_ACTION_WINDOW_MIXER),mixerOpen)) mixerOpen=!mixerOpen;
2023-02-06 23:52:51 +00:00
if (!basicMode) {
if (ImGui::MenuItem("grooves",BIND_FOR(GUI_ACTION_WINDOW_GROOVES),groovesOpen)) groovesOpen=!groovesOpen;
if (ImGui::MenuItem("channels",BIND_FOR(GUI_ACTION_WINDOW_CHANNELS),channelsOpen)) channelsOpen=!channelsOpen;
2023-02-24 08:47:53 +00:00
}
if (ImGui::MenuItem("pattern manager",BIND_FOR(GUI_ACTION_WINDOW_PAT_MANAGER),patManagerOpen)) patManagerOpen=!patManagerOpen;
if (!basicMode) {
2023-02-06 23:52:51 +00:00
if (ImGui::MenuItem("chip manager",BIND_FOR(GUI_ACTION_WINDOW_SYS_MANAGER),sysManagerOpen)) sysManagerOpen=!sysManagerOpen;
if (ImGui::MenuItem("compatibility flags",BIND_FOR(GUI_ACTION_WINDOW_COMPAT_FLAGS),compatFlagsOpen)) compatFlagsOpen=!compatFlagsOpen;
}
2022-05-18 23:42:59 +00:00
if (ImGui::MenuItem("song comments",BIND_FOR(GUI_ACTION_WINDOW_NOTES),notesOpen)) notesOpen=!notesOpen;
ImGui::Separator();
if (ImGui::MenuItem("instrument editor",BIND_FOR(GUI_ACTION_WINDOW_INS_EDIT),insEditOpen)) insEditOpen=!insEditOpen;
if (ImGui::MenuItem("wavetable editor",BIND_FOR(GUI_ACTION_WINDOW_WAVE_EDIT),waveEditOpen)) waveEditOpen=!waveEditOpen;
if (ImGui::MenuItem("sample editor",BIND_FOR(GUI_ACTION_WINDOW_SAMPLE_EDIT),sampleEditOpen)) sampleEditOpen=!sampleEditOpen;
ImGui::Separator();
if (ImGui::MenuItem("play/edit controls",BIND_FOR(GUI_ACTION_WINDOW_EDIT_CONTROLS),editControlsOpen)) editControlsOpen=!editControlsOpen;
if (ImGui::MenuItem("piano/input pad",BIND_FOR(GUI_ACTION_WINDOW_PIANO),pianoOpen)) pianoOpen=!pianoOpen;
if (ImGui::MenuItem("oscilloscope (master)",BIND_FOR(GUI_ACTION_WINDOW_OSCILLOSCOPE),oscOpen)) oscOpen=!oscOpen;
if (ImGui::MenuItem("oscilloscope (per-channel)",BIND_FOR(GUI_ACTION_WINDOW_CHAN_OSC),chanOscOpen)) chanOscOpen=!chanOscOpen;
if (ImGui::MenuItem("volume meter",BIND_FOR(GUI_ACTION_WINDOW_VOL_METER),volMeterOpen)) volMeterOpen=!volMeterOpen;
2022-11-10 06:26:59 +00:00
if (ImGui::MenuItem("clock",BIND_FOR(GUI_ACTION_WINDOW_CLOCK),clockOpen)) clockOpen=!clockOpen;
2022-05-18 23:42:59 +00:00
if (ImGui::MenuItem("register view",BIND_FOR(GUI_ACTION_WINDOW_REGISTER_VIEW),regViewOpen)) regViewOpen=!regViewOpen;
if (ImGui::MenuItem("log viewer",BIND_FOR(GUI_ACTION_WINDOW_LOG),logOpen)) logOpen=!logOpen;
if (ImGui::MenuItem("statistics",BIND_FOR(GUI_ACTION_WINDOW_STATS),statsOpen)) statsOpen=!statsOpen;
2022-06-20 20:20:02 +00:00
if (spoilerOpen) if (ImGui::MenuItem("spoiler",NULL,spoilerOpen)) spoilerOpen=!spoilerOpen;
2022-08-16 16:24:18 +00:00
2022-05-18 23:42:59 +00:00
ImGui::EndMenu();
2022-04-24 21:57:58 +00:00
}
2023-08-10 03:47:06 +00:00
if (ImGui::BeginMenu(settings.capitalMenuBar?"Help":"help")) {
2022-05-18 23:42:59 +00:00
if (ImGui::MenuItem("effect list",BIND_FOR(GUI_ACTION_WINDOW_EFFECT_LIST),effectListOpen)) effectListOpen=!effectListOpen;
if (ImGui::MenuItem("debug menu",BIND_FOR(GUI_ACTION_WINDOW_DEBUG))) debugOpen=!debugOpen;
2023-08-30 18:12:25 +00:00
if (ImGui::MenuItem("inspector")) inspectorOpen=!inspectorOpen;
if (ImGui::MenuItem("panic",BIND_FOR(GUI_ACTION_PANIC))) e->syncReset();
2022-05-18 23:42:59 +00:00
if (ImGui::MenuItem("about...",BIND_FOR(GUI_ACTION_WINDOW_ABOUT))) {
aboutOpen=true;
aboutScroll=0;
}
ImGui::EndMenu();
2022-04-17 06:54:42 +00:00
}
2022-05-18 23:42:59 +00:00
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PLAYBACK_STAT]);
if (e->isPlaying()) {
int totalTicks=e->getTotalTicks();
int totalSeconds=e->getTotalSeconds();
2023-02-05 07:56:39 +00:00
String info;
DivGroovePattern gp=e->getSpeeds();
if (gp.len==2) {
info=fmt::sprintf("| Speed %d:%d",gp.val[0],gp.val[1]);
} else if (gp.len==1) {
info=fmt::sprintf("| Speed %d",gp.val[0]);
} else {
info="| Groove";
}
info+=fmt::sprintf(" @ %gHz (%g BPM) | Order %d/%d | Row %d/%d | %d:%.2d:%.2d.%.2d",e->getCurHz(),calcBPM(e->getSpeeds(),e->getCurHz(),e->curSubSong->virtualTempoN,e->curSubSong->virtualTempoD),e->getOrder(),e->curSubSong->ordersLen,e->getRow(),e->curSubSong->patLen,totalSeconds/3600,(totalSeconds/60)%60,totalSeconds%60,totalTicks/10000);
ImGui::TextUnformatted(info.c_str());
2022-05-18 23:42:59 +00:00
} else {
bool hasInfo=false;
String info;
if (cursor.xCoarse>=0 && cursor.xCoarse<e->getTotalChannelCount()) {
DivPattern* p=e->curPat[cursor.xCoarse].getPattern(e->curOrders->ord[cursor.xCoarse][curOrder],false);
if (cursor.xFine>=0) switch (cursor.xFine) {
case 0: // note
if (p->data[cursor.y][0]>0) {
if (p->data[cursor.y][0]==100) {
info=fmt::sprintf("Note off (cut)");
} else if (p->data[cursor.y][0]==101) {
info=fmt::sprintf("Note off (release)");
} else if (p->data[cursor.y][0]==102) {
info=fmt::sprintf("Macro release only");
} else {
info=fmt::sprintf("Note on: %s",noteName(p->data[cursor.y][0],p->data[cursor.y][1]));
}
hasInfo=true;
2022-02-15 06:46:03 +00:00
}
2022-05-18 23:42:59 +00:00
break;
case 1: // instrument
if (p->data[cursor.y][2]>-1) {
if (p->data[cursor.y][2]>=(int)e->song.ins.size()) {
info=fmt::sprintf("Ins %d: <invalid>",p->data[cursor.y][2]);
} else {
DivInstrument* ins=e->getIns(p->data[cursor.y][2]);
info=fmt::sprintf("Ins %d: %s",p->data[cursor.y][2],ins->name);
}
hasInfo=true;
2022-02-15 06:46:03 +00:00
}
2022-05-18 23:42:59 +00:00
break;
case 2: // volume
if (p->data[cursor.y][3]>-1) {
int maxVol=e->getMaxVolumeChan(cursor.xCoarse);
if (maxVol<1 || p->data[cursor.y][3]>maxVol) {
info=fmt::sprintf("Set volume: %d (%.2X, INVALID!)",p->data[cursor.y][3],p->data[cursor.y][3]);
} else {
info=fmt::sprintf("Set volume: %d (%.2X, %d%%)",p->data[cursor.y][3],p->data[cursor.y][3],(p->data[cursor.y][3]*100)/maxVol);
}
hasInfo=true;
2022-02-15 06:46:03 +00:00
}
2022-05-18 23:42:59 +00:00
break;
default: // effect
int actualCursor=((cursor.xFine+1)&(~1));
if (p->data[cursor.y][actualCursor]>-1) {
info=e->getEffectDesc(p->data[cursor.y][actualCursor],cursor.xCoarse,true);
hasInfo=true;
}
break;
}
}
if (hasInfo && (settings.statusDisplay==0 || settings.statusDisplay==2)) {
ImGui::Text("| %s",info.c_str());
} else if (settings.statusDisplay==1 || settings.statusDisplay==2) {
if (curFileName!="") ImGui::Text("| %s",curFileName.c_str());
2022-02-15 06:46:03 +00:00
}
}
2022-05-18 23:42:59 +00:00
ImGui::PopStyleColor();
if (modified) {
ImGui::Text("| modified");
}
ImGui::EndMainMenuBar();
}
2023-04-14 00:43:48 +00:00
MEASURE(calcChanOsc,calcChanOsc());
2022-09-20 07:32:23 +00:00
if (followPattern) {
curOrder=e->getOrder();
}
2022-05-18 23:42:59 +00:00
if (mobileUI) {
globalWinFlags=ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoBringToFrontOnFocus;
2022-09-09 00:15:19 +00:00
//globalWinFlags=ImGuiWindowFlags_NoTitleBar;
2022-09-09 04:20:33 +00:00
// scene handling goes here!
2023-04-14 00:43:48 +00:00
MEASURE(mobileControls,drawMobileControls());
2022-09-09 20:31:29 +00:00
switch (mobScene) {
case GUI_SCENE_ORDERS:
ordersOpen=true;
2022-09-09 23:53:20 +00:00
curWindow=GUI_WINDOW_ORDERS;
2023-04-14 00:43:48 +00:00
MEASURE(orders,drawOrders());
2022-09-09 20:31:29 +00:00
break;
case GUI_SCENE_INSTRUMENT:
insEditOpen=true;
2022-09-09 23:53:20 +00:00
curWindow=GUI_WINDOW_INS_EDIT;
2023-04-14 00:43:48 +00:00
MEASURE(insEdit,drawInsEdit());
MEASURE(piano,drawPiano());
2022-09-09 20:31:29 +00:00
break;
case GUI_SCENE_WAVETABLE:
waveEditOpen=true;
2022-09-09 23:53:20 +00:00
curWindow=GUI_WINDOW_WAVE_EDIT;
2023-04-14 00:43:48 +00:00
MEASURE(waveEdit,drawWaveEdit());
MEASURE(piano,drawPiano());
2022-09-09 20:31:29 +00:00
break;
case GUI_SCENE_SAMPLE:
sampleEditOpen=true;
2022-09-09 23:53:20 +00:00
curWindow=GUI_WINDOW_SAMPLE_EDIT;
2023-04-14 00:43:48 +00:00
MEASURE(sampleEdit,drawSampleEdit());
MEASURE(piano,drawPiano());
2022-09-09 20:31:29 +00:00
break;
2022-12-01 23:41:04 +00:00
case GUI_SCENE_CHANNELS:
channelsOpen=true;
curWindow=GUI_WINDOW_CHANNELS;
2023-04-14 00:43:48 +00:00
MEASURE(channels,drawChannels());
2022-12-01 23:41:04 +00:00
break;
2022-11-29 21:09:50 +00:00
case GUI_SCENE_CHIPS:
sysManagerOpen=true;
curWindow=GUI_WINDOW_SYS_MANAGER;
2023-04-14 00:43:48 +00:00
MEASURE(sysManager,drawSysManager());
2022-11-29 21:09:50 +00:00
break;
2023-01-07 21:26:36 +00:00
case GUI_SCENE_MIXER:
mixerOpen=true;
curWindow=GUI_WINDOW_MIXER;
2023-04-14 00:43:48 +00:00
MEASURE(mixer,drawMixer());
2023-01-07 21:26:36 +00:00
break;
2022-09-10 00:23:53 +00:00
default:
patternOpen=true;
curWindow=GUI_WINDOW_PATTERN;
2023-04-14 00:43:48 +00:00
MEASURE(pattern,drawPattern());
MEASURE(piano,drawPiano());
MEASURE(mobileOrderSel,drawMobileOrderSel());
2022-12-11 18:34:58 +00:00
globalWinFlags=0;
2023-04-14 00:43:48 +00:00
MEASURE(findReplace,drawFindReplace());
2022-09-10 00:23:53 +00:00
break;
2022-09-09 20:31:29 +00:00
}
globalWinFlags=0;
2023-04-14 00:43:48 +00:00
MEASURE(settings,drawSettings());
MEASURE(debug,drawDebug());
MEASURE(log,drawLog());
MEASURE(compatFlags,drawCompatFlags());
MEASURE(stats,drawStats());
2023-06-06 21:24:15 +00:00
MEASURE(chanOsc,drawChanOsc());
2023-08-29 21:43:37 +00:00
MEASURE(regView,drawRegView());
2022-05-18 23:42:59 +00:00
} else {
2022-05-24 07:38:10 +00:00
globalWinFlags=0;
ImGui::DockSpaceOverViewport(NULL,lockLayout?(ImGuiDockNodeFlags_NoWindowMenuButton|ImGuiDockNodeFlags_NoMove|ImGuiDockNodeFlags_NoResize|ImGuiDockNodeFlags_NoCloseButton|ImGuiDockNodeFlags_NoDocking|ImGuiDockNodeFlags_NoDockingSplitMe|ImGuiDockNodeFlags_NoDockingSplitOther):0);
2022-05-19 21:35:00 +00:00
2023-04-14 00:43:48 +00:00
MEASURE(subSongs,drawSubSongs());
MEASURE(findReplace,drawFindReplace());
MEASURE(spoiler,drawSpoiler());
MEASURE(pattern,drawPattern());
MEASURE(editControls,drawEditControls());
MEASURE(speed,drawSpeed());
if (!basicMode) {
MEASURE(grooves,drawGrooves());
}
MEASURE(songInfo,drawSongInfo());
MEASURE(orders,drawOrders());
MEASURE(sampleList,drawSampleList());
MEASURE(sampleEdit,drawSampleEdit());
MEASURE(waveList,drawWaveList());
MEASURE(waveEdit,drawWaveEdit());
MEASURE(insList,drawInsList());
MEASURE(insEdit,drawInsEdit());
MEASURE(mixer,drawMixer());
MEASURE(readOsc,readOsc());
MEASURE(osc,drawOsc());
MEASURE(chanOsc,drawChanOsc());
MEASURE(volMeter,drawVolMeter());
MEASURE(settings,drawSettings());
MEASURE(debug,drawDebug());
MEASURE(stats,drawStats());
2023-02-06 23:52:51 +00:00
if (!basicMode) {
2023-04-14 00:43:48 +00:00
MEASURE(compatFlags,drawCompatFlags());
2023-02-24 08:47:53 +00:00
}
2023-04-14 00:43:48 +00:00
MEASURE(piano,drawPiano());
MEASURE(notes,drawNotes());
2023-02-24 08:47:53 +00:00
if (!basicMode) {
2023-04-14 00:43:48 +00:00
MEASURE(channels,drawChannels());
2023-02-06 23:52:51 +00:00
}
2023-04-14 00:43:48 +00:00
MEASURE(patManager,drawPatManager());
if (!basicMode) {
MEASURE(sysManager,drawSysManager());
}
MEASURE(clock,drawClock());
MEASURE(regView,drawRegView());
MEASURE(log,drawLog());
MEASURE(effectList,drawEffectList());
2021-12-30 23:25:55 +00:00
}
// release selection if mouse released
if (ImGui::IsMouseReleased(ImGuiMouseButton_Left) && selecting) {
if (!selectingFull) cursor=selEnd;
finishSelection();
if (!mobileUI) {
demandScrollX=true;
if (cursor.xCoarse==selStart.xCoarse && cursor.xFine==selStart.xFine && cursor.y==selStart.y &&
cursor.xCoarse==selEnd.xCoarse && cursor.xFine==selEnd.xFine && cursor.y==selEnd.y) {
if (!settings.cursorMoveNoScroll) {
updateScroll(cursor.y);
}
}
}
2021-12-30 23:25:55 +00:00
}
for (int i=0; i<e->getTotalChannelCount(); i++) {
keyHit1[i]-=0.2f;
if (keyHit1[i]<0.0f) keyHit1[i]=0.0f;
}
2023-02-26 09:21:27 +00:00
activateTutorial(GUI_TUTORIAL_OVERVIEW);
if (inspectorOpen) ImGui::ShowMetricsWindow(&inspectorOpen);
if (firstFrame) {
firstFrame=false;
2022-09-09 20:31:29 +00:00
#ifdef IS_MOBILE
SDL_GetWindowSize(sdlWin,&scrW,&scrH);
portrait=(scrW<scrH);
logV("portrait: %d (%dx%d)",portrait,scrW,scrH);
rend->getOutputSize(canvasW,canvasH);
2022-09-09 20:31:29 +00:00
#endif
if (patternOpen) nextWindow=GUI_WINDOW_PATTERN;
#ifdef __APPLE__
SDL_RaiseWindow(sdlWin);
#endif
}
#ifndef NFD_NON_THREADED
if (fileDialog->isOpen() && settings.sysFileDialog) {
ImGui::OpenPopup("System File Dialog Pending");
}
if (ImGui::BeginPopupModal("System File Dialog Pending",NULL,ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoBackground|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoMove)) {
if (!fileDialog->isOpen()) {
ImGui::CloseCurrentPopup();
}
ImDrawList* dl=ImGui::GetForegroundDrawList();
dl->AddRectFilled(ImVec2(0.0f,0.0f),ImVec2(canvasW,canvasH),ImGui::ColorConvertFloat4ToU32(uiColors[GUI_COLOR_MODAL_BACKDROP]));
ImGui::EndPopup();
}
#endif
if (fileDialog->render(mobileUI?ImVec2(canvasW-(portrait?0:(60.0*dpiScale)),canvasH-60.0*dpiScale):ImVec2(600.0f*dpiScale,400.0f*dpiScale),ImVec2(canvasW-((mobileUI && !portrait)?(60.0*dpiScale):0),canvasH-(mobileUI?(60.0*dpiScale):0)))) {
bool openOpen=false;
//ImGui::GetIO().ConfigFlags&=~ImGuiConfigFlags_NavEnableKeyboard;
2022-05-05 03:55:11 +00:00
if ((curFileDialog==GUI_FILE_INS_OPEN || curFileDialog==GUI_FILE_INS_OPEN_REPLACE) && prevIns!=-3) {
if (curFileDialog==GUI_FILE_INS_OPEN_REPLACE) {
if (prevInsData!=NULL) {
if (prevIns>=0 && prevIns<(int)e->song.ins.size()) {
*e->song.ins[prevIns]=*prevInsData;
}
}
} else {
curIns=prevIns;
2022-05-21 23:36:15 +00:00
wavePreviewInit=true;
updateFMPreview=true;
2022-05-05 03:55:11 +00:00
}
prevIns=-3;
}
switch (curFileDialog) {
case GUI_FILE_OPEN:
case GUI_FILE_SAVE:
case GUI_FILE_SAVE_DMF:
case GUI_FILE_SAVE_DMF_LEGACY:
workingDirSong=fileDialog->getPath()+DIR_SEPARATOR_STR;
break;
case GUI_FILE_INS_OPEN:
2022-05-05 03:55:11 +00:00
case GUI_FILE_INS_OPEN_REPLACE:
case GUI_FILE_INS_SAVE:
case GUI_FILE_INS_SAVE_DMP:
workingDirIns=fileDialog->getPath()+DIR_SEPARATOR_STR;
break;
case GUI_FILE_WAVE_OPEN:
case GUI_FILE_WAVE_OPEN_REPLACE:
case GUI_FILE_WAVE_SAVE:
case GUI_FILE_WAVE_SAVE_DMW:
case GUI_FILE_WAVE_SAVE_RAW:
workingDirWave=fileDialog->getPath()+DIR_SEPARATOR_STR;
break;
case GUI_FILE_SAMPLE_OPEN:
case GUI_FILE_SAMPLE_OPEN_RAW:
case GUI_FILE_SAMPLE_OPEN_REPLACE:
case GUI_FILE_SAMPLE_OPEN_REPLACE_RAW:
case GUI_FILE_SAMPLE_SAVE:
2023-05-02 08:57:25 +00:00
case GUI_FILE_SAMPLE_SAVE_RAW:
workingDirSample=fileDialog->getPath()+DIR_SEPARATOR_STR;
break;
case GUI_FILE_EXPORT_AUDIO_ONE:
case GUI_FILE_EXPORT_AUDIO_PER_SYS:
case GUI_FILE_EXPORT_AUDIO_PER_CHANNEL:
workingDirAudioExport=fileDialog->getPath()+DIR_SEPARATOR_STR;
break;
case GUI_FILE_EXPORT_VGM:
workingDirVGMExport=fileDialog->getPath()+DIR_SEPARATOR_STR;
break;
2022-05-26 05:24:21 +00:00
case GUI_FILE_EXPORT_ZSM:
workingDirZSMExport=fileDialog->getPath()+DIR_SEPARATOR_STR;
break;
2022-08-04 05:51:47 +00:00
case GUI_FILE_EXPORT_ROM:
case GUI_FILE_EXPORT_CMDSTREAM:
case GUI_FILE_EXPORT_CMDSTREAM_BINARY:
2022-08-04 05:51:47 +00:00
workingDirROMExport=fileDialog->getPath()+DIR_SEPARATOR_STR;
break;
case GUI_FILE_LOAD_MAIN_FONT:
case GUI_FILE_LOAD_HEAD_FONT:
case GUI_FILE_LOAD_PAT_FONT:
workingDirFont=fileDialog->getPath()+DIR_SEPARATOR_STR;
break;
case GUI_FILE_IMPORT_COLORS:
case GUI_FILE_EXPORT_COLORS:
workingDirColors=fileDialog->getPath()+DIR_SEPARATOR_STR;
break;
case GUI_FILE_IMPORT_KEYBINDS:
case GUI_FILE_EXPORT_KEYBINDS:
workingDirKeybinds=fileDialog->getPath()+DIR_SEPARATOR_STR;
break;
case GUI_FILE_IMPORT_LAYOUT:
case GUI_FILE_EXPORT_LAYOUT:
workingDirLayout=fileDialog->getPath()+DIR_SEPARATOR_STR;
break;
case GUI_FILE_YRW801_ROM_OPEN:
case GUI_FILE_TG100_ROM_OPEN:
case GUI_FILE_MU5_ROM_OPEN:
workingDirROM=fileDialog->getPath()+DIR_SEPARATOR_STR;
break;
case GUI_FILE_CMDSTREAM_OPEN:
workingDirROM=fileDialog->getPath()+DIR_SEPARATOR_STR;
break;
case GUI_FILE_TEST_OPEN:
case GUI_FILE_TEST_OPEN_MULTI:
case GUI_FILE_TEST_SAVE:
workingDirTest=fileDialog->getPath()+DIR_SEPARATOR_STR;
break;
2023-04-06 20:16:52 +00:00
case GUI_FILE_OPEN_BACKUP:
break;
}
2022-07-14 06:59:55 +00:00
if (fileDialog->isError()) {
#if defined(_WIN32) || defined(__APPLE__)
showError("there was an error in the file dialog! you may want to report this issue to:\nhttps://github.com/tildearrow/furnace/issues\ncheck the Log Viewer (window > log viewer) for more information.\n\nfor now please disable the system file picker in Settings > General.");
#else
#ifdef ANDROID
showError("can't do anything without Storage permissions!");
2022-07-14 06:59:55 +00:00
#else
showError("Zenity/KDialog not available!\nplease install one of these, or disable the system file picker in Settings > General.");
#endif
2022-07-14 06:59:55 +00:00
#endif
}
if (fileDialog->accepted()) {
if (fileDialog->getFileName().empty()) {
fileName="";
} else {
fileName=fileDialog->getFileName()[0];
}
if (fileName!="") {
if (curFileDialog==GUI_FILE_SAVE) {
checkExtension(".fur");
}
if (curFileDialog==GUI_FILE_SAVE_DMF) {
checkExtension(".dmf");
}
if (curFileDialog==GUI_FILE_SAVE_DMF_LEGACY) {
checkExtension(".dmf");
}
2022-01-19 10:10:06 +00:00
if (curFileDialog==GUI_FILE_SAMPLE_SAVE ||
curFileDialog==GUI_FILE_EXPORT_AUDIO_ONE ||
curFileDialog==GUI_FILE_EXPORT_AUDIO_PER_SYS ||
curFileDialog==GUI_FILE_EXPORT_AUDIO_PER_CHANNEL) {
checkExtension(".wav");
}
if (curFileDialog==GUI_FILE_INS_SAVE) {
checkExtension(".fui");
}
if (curFileDialog==GUI_FILE_INS_SAVE_DMP) {
checkExtension(".dmp");
}
if (curFileDialog==GUI_FILE_WAVE_SAVE) {
checkExtension(".fuw");
}
if (curFileDialog==GUI_FILE_WAVE_SAVE_DMW) {
checkExtension(".dmw");
}
if (curFileDialog==GUI_FILE_WAVE_SAVE_RAW) {
checkExtension(".raw");
}
if (curFileDialog==GUI_FILE_EXPORT_VGM) {
checkExtension(".vgm");
}
2022-05-26 05:24:21 +00:00
if (curFileDialog==GUI_FILE_EXPORT_ZSM) {
checkExtension(".zsm");
}
2022-08-04 05:51:47 +00:00
if (curFileDialog==GUI_FILE_EXPORT_CMDSTREAM) {
checkExtension(".txt");
}
if (curFileDialog==GUI_FILE_EXPORT_CMDSTREAM_BINARY) {
checkExtension(".bin");
2022-08-04 05:51:47 +00:00
}
if (curFileDialog==GUI_FILE_EXPORT_COLORS) {
checkExtension(".cfgc");
}
if (curFileDialog==GUI_FILE_EXPORT_KEYBINDS) {
checkExtension(".cfgk");
}
if (curFileDialog==GUI_FILE_EXPORT_LAYOUT) {
checkExtension(".ini");
}
String copyOfName=fileName;
switch (curFileDialog) {
case GUI_FILE_OPEN:
2023-04-06 20:16:52 +00:00
case GUI_FILE_OPEN_BACKUP:
if (load(copyOfName)>0) {
showError(fmt::sprintf("Error while loading file! (%s)",lastError));
}
break;
case GUI_FILE_SAVE: {
bool saveWasSuccessful=true;
if (save(copyOfName,0)>0) {
showError(fmt::sprintf("Error while saving file! (%s)",lastError));
saveWasSuccessful=false;
}
if (saveWasSuccessful && postWarnAction!=GUI_WARN_GENERIC) {
switch (postWarnAction) {
case GUI_WARN_QUIT:
quit=true;
break;
case GUI_WARN_NEW:
displayNew=true;
break;
case GUI_WARN_OPEN:
openOpen=true;
break;
case GUI_WARN_OPEN_DROP:
if (load(nextFile)>0) {
showError(fmt::sprintf("Error while loading file! (%s)",lastError));
}
nextFile="";
break;
case GUI_WARN_OPEN_BACKUP:
2023-04-06 20:16:52 +00:00
openFileDialog(GUI_FILE_OPEN_BACKUP);
break;
default:
break;
}
postWarnAction=GUI_WARN_GENERIC;
} else if (postWarnAction==GUI_WARN_OPEN_DROP) {
nextFile="";
}
break;
}
case GUI_FILE_SAVE_DMF:
logD("saving: %s",copyOfName.c_str());
if (save(copyOfName,26)>0) {
showError(fmt::sprintf("Error while saving file! (%s)",lastError));
}
break;
case GUI_FILE_SAVE_DMF_LEGACY:
logD("saving: %s",copyOfName.c_str());
if (save(copyOfName,24)>0) {
showError(fmt::sprintf("Error while saving file! (%s)",lastError));
}
break;
2022-01-19 08:28:29 +00:00
case GUI_FILE_INS_SAVE:
if (curIns>=0 && curIns<(int)e->song.ins.size()) {
if (e->song.ins[curIns]->save(copyOfName.c_str(),false,&e->song)) {
pushRecentSys(copyOfName.c_str());
}
2022-11-21 00:19:24 +00:00
}
break;
case GUI_FILE_INS_SAVE_DMP:
if (curIns>=0 && curIns<(int)e->song.ins.size()) {
if (!e->song.ins[curIns]->saveDMP(copyOfName.c_str())) {
showError("error while saving instrument! make sure your instrument is compatible.");
} else {
pushRecentSys(copyOfName.c_str());
}
2022-01-19 08:28:29 +00:00
}
break;
2022-01-19 10:10:06 +00:00
case GUI_FILE_WAVE_SAVE:
if (curWave>=0 && curWave<(int)e->song.wave.size()) {
if (e->song.wave[curWave]->save(copyOfName.c_str())) {
pushRecentSys(copyOfName.c_str());
}
}
break;
case GUI_FILE_WAVE_SAVE_DMW:
if (curWave>=0 && curWave<(int)e->song.wave.size()) {
if (e->song.wave[curWave]->saveDMW(copyOfName.c_str())) {
pushRecentSys(copyOfName.c_str());
}
}
break;
case GUI_FILE_WAVE_SAVE_RAW:
if (curWave>=0 && curWave<(int)e->song.wave.size()) {
if (e->song.wave[curWave]->saveRaw(copyOfName.c_str())) {
pushRecentSys(copyOfName.c_str());
}
2022-01-19 10:10:06 +00:00
}
break;
case GUI_FILE_SAMPLE_OPEN: {
2022-10-28 09:11:27 +00:00
String errs="there were some errors while loading wavetables:\n";
bool warn=false;
for (String i: fileDialog->getFileName()) {
DivSample* s=e->sampleFromFile(i.c_str());
if (s==NULL) {
if (fileDialog->getFileName().size()>1) {
warn=true;
errs+=fmt::sprintf("- %s: %s\n",i,e->getLastError());
} else {
showError(e->getLastError());
}
} else {
2022-10-28 09:11:27 +00:00
if (e->addSamplePtr(s)==-1) {
if (fileDialog->getFileName().size()>1) {
warn=true;
errs+=fmt::sprintf("- %s: %s\n",i,e->getLastError());
} else {
showError(e->getLastError());
}
} else {
MARK_MODIFIED;
}
}
2022-01-19 10:10:06 +00:00
}
2022-10-28 09:11:27 +00:00
if (warn) {
showWarning(errs,GUI_WARN_GENERIC);
}
2022-01-19 10:10:06 +00:00
break;
}
case GUI_FILE_SAMPLE_OPEN_REPLACE: {
DivSample* s=e->sampleFromFile(copyOfName.c_str());
if (s==NULL) {
2022-03-23 21:39:08 +00:00
showError(e->getLastError());
} else {
if (curSample>=0 && curSample<(int)e->song.sample.size()) {
e->lockEngine([this,s]() {
// if it crashes here please tell me...
DivSample* oldSample=e->song.sample[curSample];
e->song.sample[curSample]=s;
delete oldSample;
e->renderSamples();
MARK_MODIFIED;
});
} else {
showError("...but you haven't selected a sample!");
delete s;
}
2022-03-23 21:39:08 +00:00
}
break;
}
case GUI_FILE_SAMPLE_OPEN_RAW:
case GUI_FILE_SAMPLE_OPEN_REPLACE_RAW:
pendingRawSample=copyOfName;
2023-07-17 21:31:55 +00:00
pendingRawSampleReplace=(curFileDialog==GUI_FILE_SAMPLE_OPEN_REPLACE_RAW);
displayPendingRawSample=true;
break;
case GUI_FILE_SAMPLE_SAVE:
2021-12-18 06:03:59 +00:00
if (curSample>=0 && curSample<(int)e->song.sample.size()) {
2023-05-02 08:57:25 +00:00
if (!e->song.sample[curSample]->save(copyOfName.c_str())) {
showError("could not save sample! open Log Viewer for more information.");
} else {
pushRecentSys(copyOfName.c_str());
2023-05-02 08:57:25 +00:00
}
}
break;
case GUI_FILE_SAMPLE_SAVE_RAW:
if (curSample>=0 && curSample<(int)e->song.sample.size()) {
if (!e->song.sample[curSample]->saveRaw(copyOfName.c_str())) {
showError("could not save sample! open Log Viewer for more information.");
} else {
pushRecentSys(copyOfName.c_str());
2023-05-02 08:57:25 +00:00
}
2021-12-18 06:03:59 +00:00
}
break;
case GUI_FILE_EXPORT_AUDIO_ONE:
exportAudio(copyOfName,DIV_EXPORT_MODE_ONE);
break;
case GUI_FILE_EXPORT_AUDIO_PER_SYS:
exportAudio(copyOfName,DIV_EXPORT_MODE_MANY_SYS);
break;
case GUI_FILE_EXPORT_AUDIO_PER_CHANNEL:
exportAudio(copyOfName,DIV_EXPORT_MODE_MANY_CHAN);
break;
case GUI_FILE_INS_OPEN: {
2022-10-28 08:44:17 +00:00
std::vector<DivInstrument*> instruments;
bool ask=false;
bool warn=false;
String warns="there were some warnings/errors while loading instruments:\n";
int sampleCountBefore=e->song.sampleLen;
2022-10-28 08:44:17 +00:00
for (String i: fileDialog->getFileName()) {
std::vector<DivInstrument*> insTemp=e->instrumentFromFile(i.c_str());
if (insTemp.empty()) {
warn=true;
warns+=fmt::sprintf("> %s: cannot load instrument! (%s)\n",i,e->getLastError());
} else if (!e->getWarnings().empty()) {
warn=true;
warns+=fmt::sprintf("> %s:\n%s\n",i,e->getWarnings());
}
2022-10-28 08:44:17 +00:00
if (insTemp.size()>1) ask=true;
for (DivInstrument* j: insTemp) {
instruments.push_back(j);
}
}
if (e->song.sampleLen!=sampleCountBefore) {
e->renderSamplesP();
}
2022-10-28 08:44:17 +00:00
if (warn) {
if (instruments.empty()) {
if (fileDialog->getFileName().size()>1) {
showError(warns);
} else {
showError("cannot load instrument! ("+e->getLastError()+")");
}
} else {
showWarning(warns,GUI_WARN_GENERIC);
}
} else if (instruments.empty()) {
showError("congratulations! you managed to load nothing.\nyou are entitled to a bug report.");
}
if (!instruments.empty()) {
if (ask) { // ask which instruments to load
for (DivInstrument* i: instruments) {
pendingIns.push_back(std::make_pair(i,false));
}
displayPendingIns=true;
pendingInsSingle=false;
} else { // load the only instrument
for (DivInstrument* i: instruments) {
e->addInstrumentPtr(i);
}
}
2022-01-29 06:22:32 +00:00
}
2022-01-21 22:59:48 +00:00
break;
}
2022-05-05 03:55:11 +00:00
case GUI_FILE_INS_OPEN_REPLACE: {
int sampleCountBefore=e->song.sampleLen;
2022-05-05 03:55:11 +00:00
std::vector<DivInstrument*> instruments=e->instrumentFromFile(copyOfName.c_str());
if (!instruments.empty()) {
if (e->song.sampleLen!=sampleCountBefore) {
e->renderSamplesP();
}
2022-05-05 03:55:11 +00:00
if (!e->getWarnings().empty()) {
showWarning(e->getWarnings(),GUI_WARN_GENERIC);
}
if (instruments.size()>1) { // ask which instrument
for (DivInstrument* i: instruments) {
pendingIns.push_back(std::make_pair(i,false));
}
displayPendingIns=true;
pendingInsSingle=true;
} else { // replace with the only instrument
if (curIns>=0 && curIns<(int)e->song.ins.size()) {
*e->song.ins[curIns]=*instruments[0];
} else {
showError("...but you haven't selected an instrument!");
}
for (DivInstrument* i: instruments) {
delete i;
}
2022-05-05 03:55:11 +00:00
}
} else {
showError("cannot load instrument! ("+e->getLastError()+")");
}
break;
}
case GUI_FILE_WAVE_OPEN: {
2022-10-28 09:11:27 +00:00
String errs="there were some errors while loading wavetables:\n";
bool warn=false;
for (String i: fileDialog->getFileName()) {
DivWavetable* wave=e->waveFromFile(i.c_str());
if (wave==NULL) {
if (fileDialog->getFileName().size()>1) {
warn=true;
errs+=fmt::sprintf("- %s: %s\n",i,e->getLastError());
} else {
showError("cannot load wavetable! ("+e->getLastError()+")");
}
} else {
2022-10-28 09:11:27 +00:00
if (e->addWavePtr(wave)==-1) {
if (fileDialog->getFileName().size()>1) {
warn=true;
errs+=fmt::sprintf("- %s: %s\n",i,e->getLastError());
} else {
showError("cannot load wavetable! ("+e->getLastError()+")");
}
} else {
MARK_MODIFIED;
RESET_WAVE_MACRO_ZOOM;
}
}
2022-05-14 22:51:05 +00:00
}
2022-10-28 09:11:27 +00:00
if (warn) {
showWarning(errs,GUI_WARN_GENERIC);
}
2022-01-21 22:59:48 +00:00
break;
}
case GUI_FILE_WAVE_OPEN_REPLACE: {
DivWavetable* wave=e->waveFromFile(copyOfName.c_str());
if (wave==NULL) {
2022-05-14 22:51:05 +00:00
showError("cannot load wavetable! ("+e->getLastError()+")");
} else {
if (curWave>=0 && curWave<(int)e->song.wave.size()) {
e->lockEngine([this,wave]() {
*e->song.wave[curWave]=*wave;
MARK_MODIFIED;
});
} else {
showError("...but you haven't selected a wavetable!");
}
delete wave;
2022-05-14 22:51:05 +00:00
}
2022-01-21 22:59:48 +00:00
break;
}
case GUI_FILE_EXPORT_VGM: {
SafeWriter* w=e->saveVGM(willExport,vgmExportLoop,vgmExportVersion,vgmExportPatternHints,vgmExportDirectStream,vgmExportTrailingTicks);
if (w!=NULL) {
2022-04-06 07:46:09 +00:00
FILE* f=ps_fopen(copyOfName.c_str(),"wb");
if (f!=NULL) {
fwrite(w->getFinalBuf(),1,w->size(),f);
fclose(f);
pushRecentSys(copyOfName.c_str());
} else {
showError("could not open file!");
}
w->finish();
delete w;
2022-01-30 22:18:10 +00:00
if (!e->getWarnings().empty()) {
showWarning(e->getWarnings(),GUI_WARN_GENERIC);
}
} else {
showError(fmt::sprintf("could not write VGM! (%s)",e->getLastError()));
}
break;
}
2022-05-26 05:24:21 +00:00
case GUI_FILE_EXPORT_ZSM: {
2023-08-11 20:03:37 +00:00
SafeWriter* w=e->saveZSM(zsmExportTickRate,zsmExportLoop,zsmExportOptimize);
2022-05-26 05:24:21 +00:00
if (w!=NULL) {
FILE* f=ps_fopen(copyOfName.c_str(),"wb");
if (f!=NULL) {
fwrite(w->getFinalBuf(),1,w->size(),f);
fclose(f);
pushRecentSys(copyOfName.c_str());
2022-05-26 05:24:21 +00:00
} else {
showError("could not open file!");
}
w->finish();
delete w;
if (!e->getWarnings().empty()) {
showWarning(e->getWarnings(),GUI_WARN_GENERIC);
}
} else {
showError(fmt::sprintf("Could not write ZSM! (%s)",e->getLastError()));
}
break;
}
case GUI_FILE_EXPORT_ROM:
showError("Coming soon!");
break;
case GUI_FILE_EXPORT_CMDSTREAM:
case GUI_FILE_EXPORT_CMDSTREAM_BINARY: {
bool isBinary=(curFileDialog==GUI_FILE_EXPORT_CMDSTREAM_BINARY);
2022-08-04 05:51:47 +00:00
SafeWriter* w=e->saveCommand(isBinary);
if (w!=NULL) {
FILE* f=ps_fopen(copyOfName.c_str(),"wb");
if (f!=NULL) {
fwrite(w->getFinalBuf(),1,w->size(),f);
fclose(f);
pushRecentSys(copyOfName.c_str());
2022-08-04 05:51:47 +00:00
} else {
showError("could not open file!");
}
w->finish();
delete w;
if (!e->getWarnings().empty()) {
showWarning(e->getWarnings(),GUI_WARN_GENERIC);
}
} else {
showError(fmt::sprintf("could not write command stream! (%s)",e->getLastError()));
}
break;
}
case GUI_FILE_LOAD_MAIN_FONT:
settings.mainFontPath=copyOfName;
break;
case GUI_FILE_LOAD_HEAD_FONT:
settings.headFontPath=copyOfName;
2023-08-02 20:48:07 +00:00
break;
case GUI_FILE_LOAD_PAT_FONT:
settings.patFontPath=copyOfName;
break;
case GUI_FILE_IMPORT_COLORS:
importColors(copyOfName);
break;
case GUI_FILE_IMPORT_KEYBINDS:
importKeybinds(copyOfName);
break;
case GUI_FILE_IMPORT_LAYOUT:
importLayout(copyOfName);
break;
case GUI_FILE_EXPORT_COLORS:
exportColors(copyOfName);
break;
case GUI_FILE_EXPORT_KEYBINDS:
exportKeybinds(copyOfName);
break;
case GUI_FILE_EXPORT_LAYOUT:
exportLayout(copyOfName);
break;
case GUI_FILE_YRW801_ROM_OPEN:
settings.yrw801Path=copyOfName;
break;
case GUI_FILE_TG100_ROM_OPEN:
settings.tg100Path=copyOfName;
break;
case GUI_FILE_MU5_ROM_OPEN:
settings.mu5Path=copyOfName;
break;
case GUI_FILE_CMDSTREAM_OPEN:
if (loadStream(copyOfName)>0) {
showError(fmt::sprintf("Error while loading file! (%s)",lastError));
}
break;
case GUI_FILE_TEST_OPEN:
showWarning(fmt::sprintf("You opened: %s",copyOfName),GUI_WARN_GENERIC);
break;
case GUI_FILE_TEST_OPEN_MULTI: {
String msg="You opened:";
for (String i: fileDialog->getFileName()) {
msg+=fmt::sprintf("\n- %s",i);
}
showWarning(msg,GUI_WARN_GENERIC);
break;
}
case GUI_FILE_TEST_SAVE:
showWarning(fmt::sprintf("You saved: %s",copyOfName),GUI_WARN_GENERIC);
break;
}
curFileDialog=GUI_FILE_OPEN;
}
}
fileDialog->close();
postWarnAction=GUI_WARN_GENERIC;
if (openOpen) {
openFileDialog(GUI_FILE_OPEN);
}
}
if (warnQuit && introPos>=11.0) {
2021-12-30 23:52:36 +00:00
warnQuit=false;
ImGui::OpenPopup("Warning");
}
if (displayError && introPos>=11.0) {
2022-01-09 21:36:47 +00:00
displayError=false;
ImGui::OpenPopup("Error");
}
if (displayPendingIns) {
displayPendingIns=false;
ImGui::OpenPopup("Select Instrument");
}
if (displayPendingRawSample) {
displayPendingRawSample=false;
ImGui::OpenPopup("Import Raw Sample");
}
if (displayInsTypeList) {
displayInsTypeList=false;
ImGui::OpenPopup("InsTypeList");
}
if (displayExporting) {
displayExporting=false;
ImGui::OpenPopup("Rendering...");
}
2022-03-01 22:19:52 +00:00
if (displayNew) {
2023-04-03 14:11:26 +00:00
newSongQuery="";
newSongFirstFrame=true;
2022-03-01 22:19:52 +00:00
displayNew=false;
if (settings.newSongBehavior==1) {
e->createNewFromDefaults();
undoHist.clear();
redoHist.clear();
curFileName="";
modified=false;
curNibble=false;
orderNibble=false;
orderCursor=-1;
samplePos=0;
updateSampleTex=true;
selStart=SelectionPoint();
selEnd=SelectionPoint();
cursor=SelectionPoint();
updateWindowTitle();
} else {
ImGui::OpenPopup("New Song");
}
2022-03-01 22:19:52 +00:00
}
2023-05-16 18:41:08 +00:00
if (displayEditString) {
ImGui::OpenPopup("EditString");
}
2022-02-12 07:14:25 +00:00
if (nextWindow==GUI_WINDOW_ABOUT) {
aboutOpen=true;
nextWindow=GUI_WINDOW_NOTHING;
}
2021-12-19 04:03:50 +00:00
if (aboutOpen) drawAbout();
2023-04-14 00:43:48 +00:00
MEASURE_BEGIN(popup);
2023-08-10 07:42:42 +00:00
centerNextWindow("Rendering...",canvasW,canvasH);
if (ImGui::BeginPopupModal("Rendering...",NULL,ImGuiWindowFlags_AlwaysAutoResize)) {
ImGui::Text("Please wait...");
if (ImGui::Button("Abort")) {
if (e->haltAudioFile()) {
ImGui::CloseCurrentPopup();
}
}
if (!e->isExporting()) {
2023-08-21 19:14:09 +00:00
e->finishAudioFile();
ImGui::CloseCurrentPopup();
}
ImGui::EndPopup();
}
drawTutorial();
2023-02-24 09:19:39 +00:00
2022-11-13 21:57:47 +00:00
ImVec2 newSongMinSize=mobileUI?ImVec2(canvasW-(portrait?0:(60.0*dpiScale)),canvasH-60.0*dpiScale):ImVec2(400.0f*dpiScale,200.0f*dpiScale);
ImVec2 newSongMaxSize=ImVec2(canvasW-((mobileUI && !portrait)?(60.0*dpiScale):0),canvasH-(mobileUI?(60.0*dpiScale):0));
ImGui::SetNextWindowSizeConstraints(newSongMinSize,newSongMaxSize);
if (ImGui::BeginPopupModal("New Song",NULL,ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoScrollWithMouse|ImGuiWindowFlags_NoScrollbar)) {
ImGui::SetWindowPos(ImVec2(((canvasW)-ImGui::GetWindowSize().x)*0.5,((canvasH)-ImGui::GetWindowSize().y)*0.5));
2022-11-13 21:57:47 +00:00
if (ImGui::GetWindowSize().x<newSongMinSize.x || ImGui::GetWindowSize().y<newSongMinSize.y) {
ImGui::SetWindowSize(newSongMinSize,ImGuiCond_Always);
}
2022-03-01 22:19:52 +00:00
drawNewSong();
ImGui::EndPopup();
}
2023-08-10 07:42:42 +00:00
centerNextWindow("Error",canvasW,canvasH);
if (ImGui::BeginPopupModal("Error",NULL,ImGuiWindowFlags_AlwaysAutoResize)) {
ImGui::Text("%s",errorString.c_str());
if (ImGui::Button("OK")) {
ImGui::CloseCurrentPopup();
}
ImGui::EndPopup();
}
2023-08-10 07:42:42 +00:00
centerNextWindow("Warning",canvasW,canvasH);
2021-12-30 23:52:36 +00:00
if (ImGui::BeginPopupModal("Warning",NULL,ImGuiWindowFlags_AlwaysAutoResize)) {
ImGui::Text("%s",warnString.c_str());
switch (warnAction) {
case GUI_WARN_QUIT:
if (ImGui::Button("Yes")) {
ImGui::CloseCurrentPopup();
2023-04-06 09:22:43 +00:00
if (curFileName=="" || curFileName.find(backupPath)==0 || e->song.version>=0xff00) {
openFileDialog(GUI_FILE_SAVE);
postWarnAction=GUI_WARN_QUIT;
} else {
if (save(curFileName,e->song.isDMF?e->song.version:0)>0) {
showError(fmt::sprintf("Error while saving file! (%s)",lastError));
} else {
quit=true;
}
}
}
ImGui::SameLine();
if (ImGui::Button("No")) {
ImGui::CloseCurrentPopup();
quit=true;
}
ImGui::SameLine();
2023-07-11 14:14:35 +00:00
if (ImGui::Button("Cancel") || ImGui::IsKeyPressed(ImGuiKey_Escape)) {
ImGui::CloseCurrentPopup();
}
break;
case GUI_WARN_NEW:
if (ImGui::Button("Yes")) {
ImGui::CloseCurrentPopup();
2023-04-06 09:22:43 +00:00
if (curFileName=="" || curFileName.find(backupPath)==0 || e->song.version>=0xff00) {
openFileDialog(GUI_FILE_SAVE);
postWarnAction=GUI_WARN_NEW;
} else {
if (save(curFileName,e->song.isDMF?e->song.version:0)>0) {
showError(fmt::sprintf("Error while saving file! (%s)",lastError));
} else {
displayNew=true;
}
}
}
ImGui::SameLine();
if (ImGui::Button("No")) {
ImGui::CloseCurrentPopup();
displayNew=true;
}
ImGui::SameLine();
2023-07-11 14:14:35 +00:00
if (ImGui::Button("Cancel") || ImGui::IsKeyPressed(ImGuiKey_Escape)) {
ImGui::CloseCurrentPopup();
}
break;
case GUI_WARN_OPEN:
if (ImGui::Button("Yes")) {
ImGui::CloseCurrentPopup();
2023-04-06 09:22:43 +00:00
if (curFileName=="" || curFileName.find(backupPath)==0 || e->song.version>=0xff00) {
openFileDialog(GUI_FILE_SAVE);
postWarnAction=GUI_WARN_OPEN;
} else {
if (save(curFileName,e->song.isDMF?e->song.version:0)>0) {
showError(fmt::sprintf("Error while saving file! (%s)",lastError));
} else {
openFileDialog(GUI_FILE_OPEN);
}
}
}
ImGui::SameLine();
if (ImGui::Button("No")) {
ImGui::CloseCurrentPopup();
openFileDialog(GUI_FILE_OPEN);
}
ImGui::SameLine();
2023-07-11 14:14:35 +00:00
if (ImGui::Button("Cancel") || ImGui::IsKeyPressed(ImGuiKey_Escape)) {
ImGui::CloseCurrentPopup();
}
break;
case GUI_WARN_OPEN_BACKUP:
if (ImGui::Button("Yes")) {
ImGui::CloseCurrentPopup();
2023-04-06 09:22:43 +00:00
if (curFileName=="" || curFileName.find(backupPath)==0 || e->song.version>=0xff00) {
openFileDialog(GUI_FILE_SAVE);
postWarnAction=GUI_WARN_OPEN_BACKUP;
} else {
if (save(curFileName,e->song.isDMF?e->song.version:0)>0) {
showError(fmt::sprintf("Error while saving file! (%s)",lastError));
} else {
2023-04-06 20:16:52 +00:00
openFileDialog(GUI_FILE_OPEN_BACKUP);
}
}
}
ImGui::SameLine();
if (ImGui::Button("No")) {
ImGui::CloseCurrentPopup();
2023-04-06 20:16:52 +00:00
openFileDialog(GUI_FILE_OPEN_BACKUP);
}
ImGui::SameLine();
2023-07-11 14:14:35 +00:00
if (ImGui::Button("Cancel") || ImGui::IsKeyPressed(ImGuiKey_Escape)) {
ImGui::CloseCurrentPopup();
}
break;
case GUI_WARN_OPEN_DROP:
if (ImGui::Button("Yes")) {
ImGui::CloseCurrentPopup();
2023-04-06 09:22:43 +00:00
if (curFileName=="" || curFileName.find(backupPath)==0 || e->song.version>=0xff00) {
openFileDialog(GUI_FILE_SAVE);
postWarnAction=GUI_WARN_OPEN_DROP;
} else {
if (save(curFileName,e->song.isDMF?e->song.version:0)>0) {
showError(fmt::sprintf("Error while saving file! (%s)",lastError));
nextFile="";
} else {
if (load(nextFile)>0) {
showError(fmt::sprintf("Error while loading file! (%s)",lastError));
}
nextFile="";
}
}
}
ImGui::SameLine();
if (ImGui::Button("No")) {
ImGui::CloseCurrentPopup();
if (load(nextFile)>0) {
showError(fmt::sprintf("Error while loading file! (%s)",lastError));
}
nextFile="";
}
ImGui::SameLine();
2023-07-11 14:14:35 +00:00
if (ImGui::Button("Cancel") || ImGui::IsKeyPressed(ImGuiKey_Escape)) {
ImGui::CloseCurrentPopup();
nextFile="";
}
break;
case GUI_WARN_RESET_LAYOUT:
if (ImGui::Button("Yes")) {
ImGui::CloseCurrentPopup();
2022-05-18 23:42:59 +00:00
if (!mobileUI) {
ImGui::LoadIniSettingsFromMemory(defaultLayout);
if (!ImGui::SaveIniSettingsToDisk(finalLayoutPath,true)) {
2023-04-04 21:01:45 +00:00
reportError(fmt::sprintf("could NOT save layout! %s",strerror(errno)));
}
2022-05-18 23:42:59 +00:00
}
}
ImGui::SameLine();
if (ImGui::Button("No")) {
ImGui::CloseCurrentPopup();
}
break;
case GUI_WARN_RESET_KEYBINDS:
if (ImGui::Button("Yes")) {
ImGui::CloseCurrentPopup();
resetKeybinds();
}
ImGui::SameLine();
if (ImGui::Button("No")) {
ImGui::CloseCurrentPopup();
}
break;
case GUI_WARN_RESET_COLORS:
if (ImGui::Button("Yes")) {
ImGui::CloseCurrentPopup();
resetColors();
2022-04-11 07:53:21 +00:00
applyUISettings(false);
}
ImGui::SameLine();
if (ImGui::Button("No")) {
ImGui::CloseCurrentPopup();
}
break;
case GUI_WARN_CLOSE_SETTINGS:
if (ImGui::Button("Yes")) {
ImGui::CloseCurrentPopup();
settingsOpen=false;
willCommit=true;
}
ImGui::SameLine();
if (ImGui::Button("No")) {
ImGui::CloseCurrentPopup();
settingsOpen=false;
syncSettings();
}
ImGui::SameLine();
2023-07-11 14:14:35 +00:00
if (ImGui::Button("Cancel") || ImGui::IsKeyPressed(ImGuiKey_Escape)) {
ImGui::CloseCurrentPopup();
}
break;
2022-04-24 21:57:58 +00:00
case GUI_WARN_CLEAR:
if (ImGui::Button("All subsongs")) {
stop();
2022-05-17 06:42:21 +00:00
e->clearSubSongs();
curOrder=0;
oldOrder=0;
oldOrder1=0;
MARK_MODIFIED;
ImGui::CloseCurrentPopup();
}
ImGui::SameLine();
if (ImGui::Button("Current subsong")) {
2022-04-24 22:39:18 +00:00
stop();
e->lockEngine([this]() {
e->curSubSong->clearData();
2022-04-24 22:39:18 +00:00
});
e->setOrder(0);
curOrder=0;
oldOrder=0;
oldOrder1=0;
MARK_MODIFIED;
2022-04-24 21:57:58 +00:00
ImGui::CloseCurrentPopup();
}
ImGui::SameLine();
2022-05-17 06:46:34 +00:00
if (ImGui::Button("Orders")) {
stop();
e->lockEngine([this]() {
memset(e->curOrders->ord,0,DIV_MAX_CHANS*DIV_MAX_PATTERNS);
2022-05-17 06:46:34 +00:00
e->curSubSong->ordersLen=1;
});
e->setOrder(0);
curOrder=0;
oldOrder=0;
oldOrder1=0;
MARK_MODIFIED;
ImGui::CloseCurrentPopup();
}
ImGui::SameLine();
2022-04-24 21:57:58 +00:00
if (ImGui::Button("Pattern")) {
2022-04-24 22:39:18 +00:00
stop();
e->lockEngine([this]() {
for (int i=0; i<e->getTotalChannelCount(); i++) {
DivPattern* pat=e->curPat[i].getPattern(e->curOrders->ord[i][curOrder],true);
memset(pat->data,-1,DIV_MAX_ROWS*DIV_MAX_COLS*sizeof(short));
for (int j=0; j<DIV_MAX_ROWS; j++) {
2022-04-24 22:39:18 +00:00
pat->data[j][0]=0;
pat->data[j][1]=0;
}
}
});
MARK_MODIFIED;
2022-04-24 21:57:58 +00:00
ImGui::CloseCurrentPopup();
}
ImGui::SameLine();
if (ImGui::Button("Instruments")) {
2022-04-24 22:39:18 +00:00
stop();
e->lockEngine([this]() {
e->song.clearInstruments();
});
curIns=-1;
MARK_MODIFIED;
2022-04-24 21:57:58 +00:00
ImGui::CloseCurrentPopup();
}
ImGui::SameLine();
if (ImGui::Button("Wavetables")) {
2022-04-24 22:39:18 +00:00
stop();
e->lockEngine([this]() {
e->song.clearWavetables();
});
curWave=0;
MARK_MODIFIED;
2022-04-24 21:57:58 +00:00
ImGui::CloseCurrentPopup();
}
ImGui::SameLine();
if (ImGui::Button("Samples")) {
2022-04-24 22:39:18 +00:00
stop();
e->lockEngine([this]() {
e->song.clearSamples();
});
curSample=0;
2022-04-24 21:57:58 +00:00
ImGui::CloseCurrentPopup();
}
2023-07-11 14:14:35 +00:00
if (ImGui::Button("Wait! What am I doing? Cancel!") || ImGui::IsKeyPressed(ImGuiKey_Escape)) {
2022-04-24 21:57:58 +00:00
ImGui::CloseCurrentPopup();
}
break;
case GUI_WARN_SUBSONG_DEL:
if (ImGui::Button("Yes")) {
if (e->removeSubSong(e->getCurrentSubSong())) {
undoHist.clear();
redoHist.clear();
updateScroll(0);
oldOrder=0;
oldOrder1=0;
oldRow=0;
cursor.xCoarse=0;
cursor.xFine=0;
cursor.y=0;
selStart=cursor;
selEnd=cursor;
curOrder=0;
MARK_MODIFIED;
}
ImGui::CloseCurrentPopup();
}
ImGui::SameLine();
if (ImGui::Button("No")) {
ImGui::CloseCurrentPopup();
}
break;
2022-08-26 23:51:17 +00:00
case GUI_WARN_SYSTEM_DEL:
if (ImGui::Button("Yes")) {
e->removeSystem(sysToDelete,preserveChanPos);
2022-09-22 00:27:42 +00:00
if (e->song.autoSystem) {
autoDetectSystem();
updateWindowTitle();
2022-10-16 23:28:42 +00:00
MARK_MODIFIED;
2022-09-22 00:27:42 +00:00
}
2022-08-26 23:51:17 +00:00
ImGui::CloseCurrentPopup();
}
ImGui::SameLine();
if (ImGui::Button("No")) {
ImGui::CloseCurrentPopup();
}
break;
case GUI_WARN_CLEAR_HISTORY:
if (ImGui::Button("Yes")) {
recentFile.clear();
ImGui::CloseCurrentPopup();
}
ImGui::SameLine();
if (ImGui::Button("No")) {
ImGui::CloseCurrentPopup();
}
break;
2023-02-06 23:52:51 +00:00
case GUI_WARN_BASIC_MODE:
if (ImGui::Button("Yes")) {
basicMode=!basicMode;
ImGui::CloseCurrentPopup();
}
ImGui::SameLine();
if (ImGui::Button("No")) {
ImGui::CloseCurrentPopup();
}
break;
case GUI_WARN_GENERIC:
if (ImGui::Button("OK")) {
ImGui::CloseCurrentPopup();
}
break;
2021-12-30 23:52:36 +00:00
}
ImGui::EndPopup();
}
if (ImGui::BeginPopup("InsTypeList",ImGuiWindowFlags_NoMove|ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings)) {
char temp[1024];
for (DivInstrumentType& i: makeInsTypeList) {
2023-08-15 01:02:10 +00:00
strncpy(temp,insTypes[i][0],1023);
if (ImGui::MenuItem(temp)) {
// create ins
curIns=e->addInstrument(-1,i);
if (curIns==-1) {
showError("too many instruments!");
} else {
if (displayInsTypeListMakeInsSample>=0 && displayInsTypeListMakeInsSample<(int)e->song.sample.size()) {
e->song.ins[curIns]->type=i;
e->song.ins[curIns]->name=e->song.sample[displayInsTypeListMakeInsSample]->name;
e->song.ins[curIns]->amiga.initSample=displayInsTypeListMakeInsSample;
if (i!=DIV_INS_AMIGA) e->song.ins[curIns]->amiga.useSample=true;
nextWindow=GUI_WINDOW_INS_EDIT;
wavePreviewInit=true;
updateFMPreview=true;
}
2023-08-09 21:06:38 +00:00
if (settings.blankIns) {
e->song.ins[curIns]->fm.fb=0;
for (int i=0; i<4; i++) {
e->song.ins[curIns]->fm.op[i]=DivInstrumentFM::Operator();
e->song.ins[curIns]->fm.op[i].ar=31;
e->song.ins[curIns]->fm.op[i].dr=31;
e->song.ins[curIns]->fm.op[i].rr=15;
e->song.ins[curIns]->fm.op[i].tl=127;
e->song.ins[curIns]->fm.op[i].dt=3;
}
}
MARK_MODIFIED;
}
}
}
ImGui::EndPopup();
}
// TODO:
// - multiple selection
// - replace instrument
2023-08-10 07:42:42 +00:00
centerNextWindow("Select Instrument",canvasW,canvasH);
if (ImGui::BeginPopupModal("Select Instrument",NULL,ImGuiWindowFlags_AlwaysAutoResize)) {
bool quitPlease=false;
if (pendingInsSingle) {
ImGui::Text("this is an instrument bank! select which one to use:");
} else {
ImGui::AlignTextToFramePadding();
ImGui::Text("this is an instrument bank! select which ones to load:");
ImGui::SameLine();
if (ImGui::Button("All")) {
for (std::pair<DivInstrument*,bool>& i: pendingIns) {
i.second=true;
}
}
ImGui::SameLine();
if (ImGui::Button("None")) {
for (std::pair<DivInstrument*,bool>& i: pendingIns) {
i.second=false;
}
}
}
bool anySelected=false;
float sizeY=ImGui::GetFrameHeightWithSpacing()*pendingIns.size();
if (sizeY>(canvasH-180.0*dpiScale)) {
sizeY=canvasH-180.0*dpiScale;
if (sizeY<60.0*dpiScale) sizeY=60.0*dpiScale;
}
if (ImGui::BeginTable("PendingInsList",1,ImGuiTableFlags_ScrollY,ImVec2(0.0f,sizeY))) {
for (size_t i=0; i<pendingIns.size(); i++) {
ImGui::TableNextRow();
ImGui::TableNextColumn();
String id=fmt::sprintf("%d: %s",(int)i,pendingIns[i].first->name);
if (pendingInsSingle) {
if (ImGui::Selectable(id.c_str())) {
pendingIns[i].second=true;
quitPlease=true;
}
} else {
ImGui::Checkbox(id.c_str(),&pendingIns[i].second);
}
if (pendingIns[i].second) anySelected=true;
}
ImGui::EndTable();
}
if (!pendingInsSingle) {
ImGui::BeginDisabled(!anySelected);
if (ImGui::Button("OK")) {
quitPlease=true;
}
ImGui::EndDisabled();
ImGui::SameLine();
}
2023-07-11 14:14:35 +00:00
if (ImGui::Button("Cancel") || ImGui::IsKeyPressed(ImGuiKey_Escape)) {
for (std::pair<DivInstrument*,bool>& i: pendingIns) {
i.second=false;
}
quitPlease=true;
}
if (quitPlease) {
ImGui::CloseCurrentPopup();
for (std::pair<DivInstrument*,bool>& i: pendingIns) {
if (!i.second || pendingInsSingle) {
if (i.second) {
if (curIns>=0 && curIns<(int)e->song.ins.size()) {
*e->song.ins[curIns]=*i.first;
} else {
showError("...but you haven't selected an instrument!");
}
}
delete i.first;
} else {
e->addInstrumentPtr(i.first);
}
}
pendingIns.clear();
}
ImGui::EndPopup();
}
2023-08-10 07:42:42 +00:00
centerNextWindow("Import Raw Sample",canvasW,canvasH);
if (ImGui::BeginPopupModal("Import Raw Sample",NULL,ImGuiWindowFlags_AlwaysAutoResize)) {
2022-08-13 10:50:36 +00:00
ImGui::Text("Data type:");
for (int i=0; i<DIV_SAMPLE_DEPTH_MAX; i++) {
if (sampleDepths[i]==NULL) continue;
if (ImGui::RadioButton(sampleDepths[i],pendingRawSampleDepth==i)) pendingRawSampleDepth=i;
}
if (pendingRawSampleDepth!=DIV_SAMPLE_DEPTH_8BIT && pendingRawSampleDepth!=DIV_SAMPLE_DEPTH_16BIT) {
pendingRawSampleChannels=1;
}
2022-08-13 10:50:36 +00:00
if (pendingRawSampleDepth!=DIV_SAMPLE_DEPTH_16BIT) {
pendingRawSampleBigEndian=false;
}
2023-08-19 23:01:12 +00:00
if (pendingRawSampleDepth==DIV_SAMPLE_DEPTH_8BIT || pendingRawSampleDepth==DIV_SAMPLE_DEPTH_16BIT) {
ImGui::AlignTextToFramePadding();
ImGui::Text("Channels");
ImGui::SameLine();
ImGui::SetNextItemWidth(120.0f*dpiScale);
if (ImGui::InputInt("##RSChans",&pendingRawSampleChannels)) {
}
ImGui::Text("(will be mixed down to mono)");
ImGui::Checkbox("Unsigned",&pendingRawSampleUnsigned);
}
if (pendingRawSampleDepth==DIV_SAMPLE_DEPTH_16BIT) {
ImGui::Checkbox("Big endian",&pendingRawSampleBigEndian);
}
2023-08-19 23:01:12 +00:00
if (pendingRawSampleDepth==DIV_SAMPLE_DEPTH_YMZ_ADPCM ||
pendingRawSampleDepth==DIV_SAMPLE_DEPTH_QSOUND_ADPCM ||
pendingRawSampleDepth==DIV_SAMPLE_DEPTH_ADPCM_A ||
pendingRawSampleDepth==DIV_SAMPLE_DEPTH_ADPCM_B ||
pendingRawSampleDepth==DIV_SAMPLE_DEPTH_VOX) {
ImGui::Checkbox("Swap nibbles",&pendingRawSampleSwapNibbles);
}
if (pendingRawSampleDepth==DIV_SAMPLE_DEPTH_MULAW) {
ImGui::Text("Encoding:");
ImGui::Indent();
if (ImGui::RadioButton("G.711",pendingRawSampleSwapNibbles==0)) {
pendingRawSampleSwapNibbles=0;
}
if (ImGui::RadioButton("Namco",pendingRawSampleSwapNibbles==1)) {
pendingRawSampleSwapNibbles=1;
}
ImGui::Unindent();
}
2023-08-19 23:01:12 +00:00
if (pendingRawSampleDepth==DIV_SAMPLE_DEPTH_1BIT ||
pendingRawSampleDepth==DIV_SAMPLE_DEPTH_1BIT_DPCM) {
ImGui::Checkbox("Reverse bit order",&pendingRawSampleSwapNibbles);
}
2022-08-13 10:50:36 +00:00
if (ImGui::Button("OK")) {
DivSample* s=e->sampleFromFileRaw(pendingRawSample.c_str(),(DivSampleDepth)pendingRawSampleDepth,pendingRawSampleChannels,pendingRawSampleBigEndian,pendingRawSampleUnsigned,pendingRawSampleSwapNibbles);
2022-08-13 10:50:36 +00:00
if (s==NULL) {
showError(e->getLastError());
} else {
2023-07-17 21:31:55 +00:00
if (pendingRawSampleReplace) {
if (curSample>=0 && curSample<(int)e->song.sample.size()) {
e->lockEngine([this,s]() {
// if it crashes here please tell me...
DivSample* oldSample=e->song.sample[curSample];
e->song.sample[curSample]=s;
delete oldSample;
e->renderSamples();
MARK_MODIFIED;
});
} else {
showError("...but you haven't selected a sample!");
delete s;
}
2022-08-13 10:50:36 +00:00
} else {
2023-07-17 21:31:55 +00:00
if (e->addSamplePtr(s)==-1) {
showError(e->getLastError());
} else {
MARK_MODIFIED;
}
2022-08-13 10:50:36 +00:00
}
}
ImGui::CloseCurrentPopup();
}
ImGui::SameLine();
2023-07-11 14:14:35 +00:00
if (ImGui::Button("Cancel") || ImGui::IsKeyPressed(ImGuiKey_Escape)) {
ImGui::CloseCurrentPopup();
}
ImGui::EndPopup();
}
2023-05-16 18:41:08 +00:00
if (ImGui::BeginPopup("EditString",ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings)) {
if (editString==NULL) {
ImGui::Text("Error! No string provided!");
} else {
if (displayEditString) {
ImGui::SetItemDefaultFocus();
ImGui::SetKeyboardFocusHere();
}
ImGui::InputText("##StringVal",editString);
}
displayEditString=false;
ImGui::SameLine();
if (ImGui::Button("OK") || ImGui::IsKeyPressed(ImGuiKey_Enter,false)) {
editString=NULL;
ImGui::CloseCurrentPopup();
}
ImGui::EndPopup();
} else {
editString=NULL;
}
2023-04-14 00:43:48 +00:00
MEASURE_END(popup);
2023-02-19 05:08:37 +00:00
if (!tutorial.introPlayed || settings.alwaysPlayIntro!=0) {
2023-04-14 00:43:48 +00:00
MEASURE_BEGIN(intro);
initialScreenWipe=0;
2023-02-19 05:08:37 +00:00
if (settings.alwaysPlayIntro==1) {
shortIntro=true;
}
2023-02-19 20:58:56 +00:00
drawIntro(introPos);
2023-04-14 00:43:48 +00:00
MEASURE_END(intro);
2023-02-19 05:08:37 +00:00
} else {
introPos=12.0;
2023-02-15 21:25:35 +00:00
}
#ifdef DIV_UNSTABLE
{
ImDrawList* dl=ImGui::GetForegroundDrawList();
2023-07-10 09:32:56 +00:00
ImVec2 markPos=ImVec2(canvasW-ImGui::CalcTextSize(DIV_VERSION).x-6.0*dpiScale,4.0*dpiScale);
ImVec4 markColor=uiColors[GUI_COLOR_TEXT];
markColor.w=0.67f;
dl->AddText(markPos,ImGui::ColorConvertFloat4ToU32(markColor),DIV_VERSION);
}
#endif
2022-05-27 05:19:10 +00:00
layoutTimeEnd=SDL_GetPerformanceCounter();
2022-03-21 19:11:28 +00:00
// backup trigger
if (modified) {
if (backupTimer>0) {
2022-05-16 08:09:59 +00:00
backupTimer=(backupTimer-ImGui::GetIO().DeltaTime);
2022-03-21 19:11:28 +00:00
if (backupTimer<=0) {
backupTask=std::async(std::launch::async,[this]() -> bool {
2023-04-06 21:24:44 +00:00
backupLock.lock();
logV("backupPath: %s",backupPath);
logV("curFileName: %s",curFileName);
2023-04-06 09:22:43 +00:00
if (curFileName.find(backupPath)==0) {
logD("backup file open. not saving backup.");
backupTimer=30.0;
2023-04-06 21:24:44 +00:00
backupLock.unlock();
2022-03-21 19:11:28 +00:00
return true;
}
2023-04-06 09:22:43 +00:00
if (!dirExists(backupPath.c_str())) {
if (!makeDir(backupPath.c_str())) {
logW("could not create backup directory!");
backupTimer=30.0;
2023-04-06 21:24:44 +00:00
backupLock.unlock();
2023-04-06 09:22:43 +00:00
return false;
}
}
logD("saving backup...");
2023-05-26 06:29:49 +00:00
SafeWriter* w=e->saveFur(true,true);
logV("writing file...");
2022-05-26 05:24:21 +00:00
2022-03-21 19:11:28 +00:00
if (w!=NULL) {
2023-04-06 09:22:43 +00:00
size_t sepPos=curFileName.rfind(DIR_SEPARATOR);
String backupPreBaseName;
2023-04-06 09:22:43 +00:00
String backupBaseName;
String backupFileName;
if (sepPos==String::npos) {
backupPreBaseName=curFileName;
2023-04-06 09:22:43 +00:00
} else {
backupPreBaseName=curFileName.substr(sepPos+1);
2023-04-06 09:22:43 +00:00
}
size_t dotPos=backupPreBaseName.rfind('.');
2023-04-06 09:22:43 +00:00
if (dotPos!=String::npos) {
backupPreBaseName=backupPreBaseName.substr(0,dotPos);
2023-04-06 09:22:43 +00:00
}
for (char i: backupPreBaseName) {
if (backupBaseName.size()>=48) break;
if ((i>='0' && i<='9') || (i>='A' && i<='Z') || (i>='a' && i<='z') || i=='_' || i=='-' || i==' ') backupBaseName+=i;
}
if (backupBaseName.empty()) backupBaseName="untitled";
2023-04-06 09:22:43 +00:00
backupFileName=backupBaseName;
time_t curTime=time(NULL);
struct tm curTM;
#ifdef _WIN32
struct tm* tempTM=localtime(&curTime);
if (tempTM==NULL) {
backupFileName+="-unknownTime.fur";
} else {
curTM=*tempTM;
backupFileName+=fmt::sprintf("-%d%.2d%.2d-%.2d%.2d%.2d.fur",curTM.tm_year+1900,curTM.tm_mon+1,curTM.tm_mday,curTM.tm_hour,curTM.tm_min,curTM.tm_sec);
}
#else
if (localtime_r(&curTime,&curTM)==NULL) {
backupFileName+="-unknownTime.fur";
} else {
backupFileName+=fmt::sprintf("-%d%.2d%.2d-%.2d%.2d%.2d.fur",curTM.tm_year+1900,curTM.tm_mon+1,curTM.tm_mday,curTM.tm_hour,curTM.tm_min,curTM.tm_sec);
}
#endif
String finalPath=backupPath+String(DIR_SEPARATOR_STR)+backupFileName;
FILE* outFile=ps_fopen(finalPath.c_str(),"wb");
2022-03-21 19:11:28 +00:00
if (outFile!=NULL) {
if (fwrite(w->getFinalBuf(),1,w->size(),outFile)!=w->size()) {
logW("did not write backup entirely: %s!",strerror(errno));
2022-03-21 19:11:28 +00:00
}
2022-03-22 02:47:27 +00:00
fclose(outFile);
2022-03-21 19:11:28 +00:00
} else {
logW("could not save backup: %s!",strerror(errno));
2022-03-21 19:11:28 +00:00
}
w->finish();
2023-04-06 09:22:43 +00:00
// delete previous backup if there are too many
delFirstBackup(backupBaseName);
2022-03-21 19:11:28 +00:00
}
logD("backup saved.");
2022-03-21 19:11:28 +00:00
backupTimer=30.0;
2023-04-06 21:24:44 +00:00
backupLock.unlock();
2022-03-21 19:11:28 +00:00
return true;
});
}
}
}
sampleMapWaitingInput=(curWindow==GUI_WINDOW_INS_EDIT && sampleMapFocused);
2022-11-06 07:06:51 +00:00
curWindowThreadSafe=curWindow;
2022-03-21 19:11:28 +00:00
if (curWindow!=curWindowLast) {
int curWindowCat=0;
int lastWindowCat=0;
switch (curWindow) {
case GUI_WINDOW_WAVE_LIST:
case GUI_WINDOW_WAVE_EDIT:
curWindowCat=1;
break;
case GUI_WINDOW_SAMPLE_LIST:
case GUI_WINDOW_SAMPLE_EDIT:
curWindowCat=2;
break;
default:
curWindowCat=0;
break;
}
switch (curWindowLast) {
case GUI_WINDOW_WAVE_LIST:
case GUI_WINDOW_WAVE_EDIT:
lastWindowCat=1;
break;
case GUI_WINDOW_SAMPLE_LIST:
case GUI_WINDOW_SAMPLE_EDIT:
lastWindowCat=2;
break;
default:
lastWindowCat=0;
break;
}
if (curWindowCat!=lastWindowCat) {
switch (lastWindowCat) {
case 0:
e->autoNoteOffAll();
break;
case 1:
e->stopWavePreview();
break;
case 2:
e->stopSamplePreview();
break;
}
}
}
2023-06-08 09:15:57 +00:00
if (!settings.renderClearPos) {
rend->clear(uiColors[GUI_COLOR_BACKGROUND]);
}
2022-05-27 05:19:10 +00:00
renderTimeBegin=SDL_GetPerformanceCounter();
2021-12-11 08:11:40 +00:00
ImGui::Render();
2022-05-27 05:19:10 +00:00
renderTimeEnd=SDL_GetPerformanceCounter();
2023-06-12 20:58:16 +00:00
drawTimeBegin=SDL_GetPerformanceCounter();
rend->renderGUI();
if (mustClear) {
rend->clear(ImVec4(0,0,0,0));
mustClear--;
} else {
if (initialScreenWipe>0.0f && !settings.disableFadeIn) {
WAKE_UP;
initialScreenWipe-=ImGui::GetIO().DeltaTime*5.0f;
if (initialScreenWipe>0.0f) {
rend->wipe(pow(initialScreenWipe,2.0f));
}
}
}
2023-06-12 20:58:16 +00:00
drawTimeEnd=SDL_GetPerformanceCounter();
2023-06-13 00:43:26 +00:00
rend->present();
2023-06-08 09:15:57 +00:00
if (settings.renderClearPos) {
rend->clear(uiColors[GUI_COLOR_BACKGROUND]);
}
2021-12-20 03:51:02 +00:00
2022-05-27 05:19:10 +00:00
layoutTimeDelta=layoutTimeEnd-layoutTimeBegin;
renderTimeDelta=renderTimeEnd-renderTimeBegin;
2023-06-12 20:58:16 +00:00
drawTimeDelta=drawTimeEnd-drawTimeBegin;
2022-05-27 05:19:10 +00:00
eventTimeDelta=eventTimeEnd-eventTimeBegin;
2023-01-14 19:07:50 +00:00
soloTimeout-=ImGui::GetIO().DeltaTime;
if (soloTimeout<0) {
soloTimeout=0;
} else {
WAKE_UP;
}
if (!ImGui::IsMouseDown(ImGuiMouseButton_Left)) {
exitDisabledTimer=0;
}
wheelX=0;
wheelY=0;
wantScrollList=false;
2022-05-17 17:46:52 +00:00
pressedPoints.clear();
releasedPoints.clear();
2021-12-20 03:51:02 +00:00
if (willCommit) {
commitSettings();
willCommit=false;
}
if (shallDetectScale) {
if (--shallDetectScale<1) {
if (settings.dpiScale<0.5f) {
const char* videoBackend=SDL_GetCurrentVideoDriver();
double newScale=getScaleFactor(videoBackend,sdlWin);
if (newScale<0.1f) {
logW("scale what?");
newScale=1.0f;
}
if (newScale!=dpiScale) {
logD("auto UI scale changed (%f != %f) - applying settings...",newScale,dpiScale);
ImGui::GetIO().Fonts->Clear();
applyUISettings();
if (rend) rend->destroyFontsTexture();
if (!ImGui::GetIO().Fonts->Build()) {
logE("error while building font atlas!");
showError("error while loading fonts! please check your settings.");
ImGui::GetIO().Fonts->Clear();
mainFont=ImGui::GetIO().Fonts->AddFontDefault();
patFont=mainFont;
bigFont=mainFont;
headFont=mainFont;
if (rend) rend->destroyFontsTexture();
if (!ImGui::GetIO().Fonts->Build()) {
logE("error again while building font atlas!");
} else {
rend->createFontsTexture();
}
} else {
rend->createFontsTexture();
}
}
}
}
}
if (fontsFailed) {
showError("it appears I couldn't load these fonts. any setting you can check?");
logE("couldn't load fonts");
ImGui::GetIO().Fonts->Clear();
mainFont=ImGui::GetIO().Fonts->AddFontDefault();
patFont=mainFont;
bigFont=mainFont;
headFont=mainFont;
if (rend) rend->destroyFontsTexture();
if (!ImGui::GetIO().Fonts->Build()) {
logE("error again while building font atlas!");
} else {
rend->createFontsTexture();
}
}
2022-04-21 23:10:59 +00:00
if (!editOptsVisible) {
latchTarget=0;
latchNibble=false;
2022-04-21 23:10:59 +00:00
}
if (SDL_GetWindowFlags(sdlWin)&SDL_WINDOW_MINIMIZED) {
SDL_Delay(100);
}
2021-12-11 08:11:40 +00:00
}
return false;
}
bool FurnaceGUI::init() {
logI("initializing GUI.");
String homeDir=getHomeDir();
workingDir=e->getConfString("lastDir",homeDir);
workingDirSong=e->getConfString("lastDirSong",workingDir);
workingDirIns=e->getConfString("lastDirIns",workingDir);
workingDirWave=e->getConfString("lastDirWave",workingDir);
workingDirSample=e->getConfString("lastDirSample",workingDir);
workingDirAudioExport=e->getConfString("lastDirAudioExport",workingDir);
workingDirVGMExport=e->getConfString("lastDirVGMExport",workingDir);
2022-05-26 05:24:21 +00:00
workingDirZSMExport=e->getConfString("lastDirZSMExport",workingDir);
2022-08-04 05:51:47 +00:00
workingDirROMExport=e->getConfString("lastDirROMExport",workingDir);
workingDirFont=e->getConfString("lastDirFont",workingDir);
workingDirColors=e->getConfString("lastDirColors",workingDir);
workingDirKeybinds=e->getConfString("lastDirKeybinds",workingDir);
workingDirLayout=e->getConfString("lastDirLayout",workingDir);
workingDirTest=e->getConfString("lastDirTest",workingDir);
2022-01-29 06:54:30 +00:00
editControlsOpen=e->getConfBool("editControlsOpen",true);
ordersOpen=e->getConfBool("ordersOpen",true);
insListOpen=e->getConfBool("insListOpen",true);
songInfoOpen=e->getConfBool("songInfoOpen",true);
patternOpen=e->getConfBool("patternOpen",true);
insEditOpen=e->getConfBool("insEditOpen",false);
waveListOpen=e->getConfBool("waveListOpen",true);
waveEditOpen=e->getConfBool("waveEditOpen",false);
sampleListOpen=e->getConfBool("sampleListOpen",true);
sampleEditOpen=e->getConfBool("sampleEditOpen",false);
settingsOpen=e->getConfBool("settingsOpen",false);
mixerOpen=e->getConfBool("mixerOpen",false);
oscOpen=e->getConfBool("oscOpen",true);
chanOscOpen=e->getConfBool("chanOscOpen",false);
2022-01-29 23:56:08 +00:00
volMeterOpen=e->getConfBool("volMeterOpen",true);
statsOpen=e->getConfBool("statsOpen",false);
compatFlagsOpen=e->getConfBool("compatFlagsOpen",false);
2022-12-11 18:34:58 +00:00
#ifdef IS_MOBILE
pianoOpen=e->getConfBool("pianoOpen",true);
#else
pianoOpen=e->getConfBool("pianoOpen",false);
#endif
notesOpen=e->getConfBool("notesOpen",false);
channelsOpen=e->getConfBool("channelsOpen",false);
patManagerOpen=e->getConfBool("patManagerOpen",false);
2022-08-19 09:41:45 +00:00
sysManagerOpen=e->getConfBool("sysManagerOpen",false);
2022-11-10 06:26:59 +00:00
clockOpen=e->getConfBool("clockOpen",false);
speedOpen=e->getConfBool("speedOpen",true);
2023-02-05 07:56:39 +00:00
groovesOpen=e->getConfBool("groovesOpen",false);
regViewOpen=e->getConfBool("regViewOpen",false);
logOpen=e->getConfBool("logOpen",false);
2023-02-24 09:26:17 +00:00
effectListOpen=e->getConfBool("effectListOpen",true);
subSongsOpen=e->getConfBool("subSongsOpen",true);
findOpen=e->getConfBool("findOpen",false);
2022-06-20 20:20:02 +00:00
spoilerOpen=e->getConfBool("spoilerOpen",false);
2022-01-29 06:54:30 +00:00
2023-02-06 23:52:51 +00:00
if (e->hasConf("lastDir")) {
basicMode=e->getConfBool("basicMode",false);
} else {
basicMode=true;
}
2023-05-15 06:36:02 +00:00
insListDir=e->getConfBool("insListDir",false);
waveListDir=e->getConfBool("waveListDir",false);
sampleListDir=e->getConfBool("sampleListDir",false);
tempoView=e->getConfBool("tempoView",true);
waveHex=e->getConfBool("waveHex",false);
2022-09-11 03:35:21 +00:00
waveSigned=e->getConfBool("waveSigned",false);
2022-07-21 08:14:52 +00:00
waveGenVisible=e->getConfBool("waveGenVisible",false);
2022-07-21 07:49:19 +00:00
waveEditStyle=e->getConfInt("waveEditStyle",0);
extraChannelButtons=e->getConfInt("extraChannelButtons",0);
lockLayout=e->getConfBool("lockLayout",false);
2022-05-11 21:09:23 +00:00
#ifdef IS_MOBILE
fullScreen=true;
#else
2022-04-17 06:54:42 +00:00
fullScreen=e->getConfBool("fullScreen",false);
2022-05-11 21:09:23 +00:00
#endif
mobileUI=e->getConfBool("mobileUI",MOBILE_UI_DEFAULT);
edit=e->getConfBool("edit",false);
followOrders=e->getConfBool("followOrders",true);
followPattern=e->getConfBool("followPattern",true);
2022-06-03 23:05:07 +00:00
noteInputPoly=e->getConfBool("noteInputPoly",true);
exportLoops=e->getConfInt("exportLoops",0);
if (exportLoops<0) exportLoops=0;
exportFadeOut=e->getConfDouble("exportFadeOut",0.0);
if (exportFadeOut<0.0) exportFadeOut=0.0;
orderEditMode=e->getConfInt("orderEditMode",0);
if (orderEditMode<0) orderEditMode=0;
if (orderEditMode>3) orderEditMode=3;
oscZoom=e->getConfFloat("oscZoom",0.5f);
2022-05-31 03:22:53 +00:00
oscZoomSlider=e->getConfBool("oscZoomSlider",false);
oscWindowSize=e->getConfFloat("oscWindowSize",20.0f);
2022-05-27 06:13:33 +00:00
pianoOctaves=e->getConfInt("pianoOctaves",pianoOctaves);
pianoOctavesEdit=e->getConfInt("pianoOctavesEdit",pianoOctavesEdit);
pianoOptions=e->getConfBool("pianoOptions",pianoOptions);
pianoSharePosition=e->getConfBool("pianoSharePosition",pianoSharePosition);
pianoOptionsSet=e->getConfBool("pianoOptionsSet",pianoOptionsSet);
2023-05-24 15:47:20 +00:00
pianoReadonly=e->getConfBool("pianoReadonly",false);
2022-05-27 06:13:33 +00:00
pianoOffset=e->getConfInt("pianoOffset",pianoOffset);
pianoOffsetEdit=e->getConfInt("pianoOffsetEdit",pianoOffsetEdit);
pianoView=e->getConfInt("pianoView",pianoView);
pianoInputPadMode=e->getConfInt("pianoInputPadMode",pianoInputPadMode);
2023-09-03 09:22:00 +00:00
chanOscCols=e->getConfInt("chanOscCols",3);
chanOscAutoColsType=e->getConfInt("chanOscAutoColsType",0);
2022-06-23 09:02:41 +00:00
chanOscColorX=e->getConfInt("chanOscColorX",GUI_OSCREF_CENTER);
chanOscColorY=e->getConfInt("chanOscColorY",GUI_OSCREF_CENTER);
2023-06-18 09:27:22 +00:00
chanOscTextX=e->getConfFloat("chanOscTextX",0.0f);
chanOscTextY=e->getConfFloat("chanOscTextY",0.0f);
chanOscAmplify=e->getConfFloat("chanOscAmplify",0.95f);
2022-06-23 09:02:41 +00:00
chanOscWindowSize=e->getConfFloat("chanOscWindowSize",20.0f);
chanOscWaveCorr=e->getConfBool("chanOscWaveCorr",true);
chanOscOptions=e->getConfBool("chanOscOptions",false);
2023-06-18 09:27:22 +00:00
chanOscNormalize=e->getConfBool("chanOscNormalize",false);
chanOscTextFormat=e->getConfString("chanOscTextFormat","%c");
2022-06-23 09:02:41 +00:00
chanOscColor.x=e->getConfFloat("chanOscColorR",1.0f);
chanOscColor.y=e->getConfFloat("chanOscColorG",1.0f);
chanOscColor.z=e->getConfFloat("chanOscColorB",1.0f);
chanOscColor.w=e->getConfFloat("chanOscColorA",1.0f);
2023-06-18 09:27:22 +00:00
chanOscTextColor.x=e->getConfFloat("chanOscTextColorR",1.0f);
chanOscTextColor.y=e->getConfFloat("chanOscTextColorG",1.0f);
chanOscTextColor.z=e->getConfFloat("chanOscTextColorB",1.0f);
chanOscTextColor.w=e->getConfFloat("chanOscTextColorA",0.75f);
2022-06-23 09:02:41 +00:00
chanOscUseGrad=e->getConfBool("chanOscUseGrad",false);
chanOscGrad.fromString(e->getConfString("chanOscGrad",""));
2022-06-24 08:17:40 +00:00
chanOscGrad.render();
2022-06-23 09:02:41 +00:00
2022-01-16 22:25:43 +00:00
syncSettings();
2023-02-19 05:08:37 +00:00
syncTutorial();
if (!settings.persistFadeOut) {
exportLoops=settings.exportLoops;
exportFadeOut=settings.exportFadeOut;
}
2022-09-10 23:53:27 +00:00
for (int i=0; i<settings.maxRecentFile; i++) {
String r=e->getConfString(fmt::sprintf("recentFile%d",i),"");
if (!r.empty()) {
recentFile.push_back(r);
}
}
2022-05-14 07:38:38 +00:00
initSystemPresets();
2023-02-25 23:04:17 +00:00
initTutorial();
2022-05-14 07:38:38 +00:00
2022-06-03 23:05:07 +00:00
e->setAutoNotePoly(noteInputPoly);
2022-10-19 20:33:20 +00:00
SDL_SetHint(SDL_HINT_VIDEO_ALLOW_SCREENSAVER,"1");
#if SDL_VERSION_ATLEAST(2,0,17)
2022-10-19 20:33:20 +00:00
SDL_SetHint(SDL_HINT_MOUSE_TOUCH_EVENTS,"0");
#endif
2022-10-19 20:33:20 +00:00
SDL_SetHint(SDL_HINT_TOUCH_MOUSE_EVENTS,"0");
// don't disable compositing on KWin
#if SDL_VERSION_ATLEAST(2,0,22)
logV("setting window type to NORMAL.");
2022-10-19 20:33:20 +00:00
SDL_SetHint(SDL_HINT_X11_WINDOW_TYPE,"_NET_WM_WINDOW_TYPE_NORMAL");
#endif
// initialize SDL
logD("initializing video...");
2023-06-02 19:11:47 +00:00
if (SDL_Init(SDL_INIT_VIDEO)!=0) {
logE("could not initialize video! %s",SDL_GetError());
return false;
}
#ifdef IS_MOBILE
logD("initializing haptic...");
2023-06-02 19:11:47 +00:00
if (SDL_Init(SDL_INIT_HAPTIC)!=0) {
logW("could not initialize haptic! %s",SDL_GetError());
}
#endif
2022-10-19 20:33:20 +00:00
const char* videoBackend=SDL_GetCurrentVideoDriver();
if (videoBackend!=NULL) {
logV("video backend: %s",videoBackend);
2022-10-19 20:33:20 +00:00
if (strcmp(videoBackend,"wayland")==0 ||
strcmp(videoBackend,"cocoa")==0 ||
strcmp(videoBackend,"uikit")==0) {
sysManagedScale=true;
logV("scaling managed by system.");
} else {
logV("scaling managed by application.");
2022-10-19 20:33:20 +00:00
}
} else {
logV("could not get video backend name!");
2022-10-19 20:33:20 +00:00
}
// get scale factor
if (settings.dpiScale>=0.5f) {
logD("setting UI scale factor from config (%f).",settings.dpiScale);
dpiScale=settings.dpiScale;
} else {
logD("auto-detecting UI scale factor.");
dpiScale=getScaleFactor(videoBackend,sdlWin);
logD("scale factor: %f",dpiScale);
2023-02-03 20:31:02 +00:00
if (dpiScale<0.1f) {
logW("scale what?");
dpiScale=1.0f;
}
}
2022-10-19 20:33:20 +00:00
#if !(defined(__APPLE__) || defined(_WIN32))
2022-10-19 20:33:20 +00:00
// get the icon (on macOS and Windows the icon is bundled with the app)
2023-02-15 23:32:31 +00:00
const FurnaceGUIImage* furIcon=getImage(GUI_IMAGE_ICON);
SDL_Surface* icon=NULL;
2023-02-15 23:59:49 +00:00
if (furIcon!=NULL) {
2023-02-16 03:04:14 +00:00
icon=SDL_CreateRGBSurfaceFrom(furIcon->data,furIcon->width,furIcon->height,32,256*4,0xff,0xff00,0xff0000,0xff000000);
2023-02-15 23:59:49 +00:00
} else {
logE("furIcon is NULL!");
}
2022-01-07 08:29:56 +00:00
#endif
2022-09-09 20:31:29 +00:00
#ifdef IS_MOBILE
scrW=960;
scrH=540;
scrX=0;
scrY=0;
#else
scrW=scrConfW=e->getConfInt("lastWindowWidth",1280);
scrH=scrConfH=e->getConfInt("lastWindowHeight",800);
scrX=scrConfX=e->getConfInt("lastWindowX",SDL_WINDOWPOS_CENTERED);
scrY=scrConfY=e->getConfInt("lastWindowY",SDL_WINDOWPOS_CENTERED);
scrMax=e->getConfBool("lastWindowMax",false);
2022-09-09 20:31:29 +00:00
#endif
2022-09-08 22:04:38 +00:00
portrait=(scrW<scrH);
2022-09-09 00:15:19 +00:00
logV("portrait: %d (%dx%d)",portrait,scrW,scrH);
2022-01-29 22:27:51 +00:00
// if old config, scale size as it was stored unscaled before
if (e->getConfInt("configVersion",0)<122 && !sysManagedScale) {
logD("scaling window size to scale factor because configVersion is not present.");
scrW*=dpiScale;
scrH*=dpiScale;
}
// predict the canvas size
if (sysManagedScale) {
canvasW=scrW*dpiScale;
canvasH=scrH*dpiScale;
} else {
canvasW=scrW;
canvasH=scrH;
}
#ifndef IS_MOBILE
SDL_Rect displaySize;
#endif
2022-09-09 20:31:29 +00:00
#ifndef IS_MOBILE
// if window would spawn out of bounds, force it to be get default position
SDL_Rect bounds;
if (!detectOutOfBoundsWindow(bounds)) {
2022-09-08 22:04:38 +00:00
scrMax=false;
scrX=scrConfX=SDL_WINDOWPOS_CENTERED;
scrY=scrConfY=SDL_WINDOWPOS_CENTERED;
// make sure our window isn't big
2023-05-01 06:36:47 +00:00
/*if (bounds.w<scrW) {
logD("resizing width because it does not fit");
scrW=bounds.w-OOB_PIXELS_SAFETY*2;
if (scrW<200) scrW=200;
}
if (bounds.h<scrH) {
logD("resizing height because it does not fit");
scrH=bounds.h-OOB_PIXELS_SAFETY*2;
if (scrH<100) scrH=100;
2023-05-01 06:36:47 +00:00
}*/
}
2022-09-09 20:31:29 +00:00
#endif
logV("window size: %dx%d",scrW,scrH);
if (!initRender()) {
if (settings.renderBackend!="SDL") {
settings.renderBackend="SDL";
e->setConf("renderBackend","SDL");
e->saveConf();
lastError=fmt::sprintf("could not init renderer!\r\nthe render backend has been set to a safe value. please restart Furnace.");
} else {
lastError=fmt::sprintf("could not init renderer! %s",SDL_GetError());
if (!settings.renderDriver.empty()) {
settings.renderDriver="";
e->setConf("renderDriver","");
e->saveConf();
lastError+=fmt::sprintf("\r\nthe render driver has been set to a safe value. please restart Furnace.");
}
}
return false;
}
rend->preInit();
2023-06-05 04:55:57 +00:00
logD("creating window...");
sdlWin=SDL_CreateWindow("Furnace",scrX,scrY,scrW,scrH,SDL_WINDOW_RESIZABLE|SDL_WINDOW_ALLOW_HIGHDPI|(scrMax?SDL_WINDOW_MAXIMIZED:0)|(fullScreen?SDL_WINDOW_FULLSCREEN_DESKTOP:0)|rend->getWindowFlags());
2021-12-11 08:11:40 +00:00
if (sdlWin==NULL) {
2022-10-02 21:12:02 +00:00
lastError=fmt::sprintf("could not open window! %s",SDL_GetError());
2021-12-11 08:11:40 +00:00
return false;
}
2022-01-07 08:29:56 +00:00
#ifndef IS_MOBILE
if (SDL_GetDisplayUsableBounds(SDL_GetWindowDisplayIndex(sdlWin),&displaySize)==0) {
bool mustChange=false;
if (scrW>((displaySize.w)-48) && scrH>((displaySize.h)-64)) {
// maximize
SDL_MaximizeWindow(sdlWin);
logD("maximizing as it doesn't fit (%dx%d+%d+%d).",displaySize.w,displaySize.h,displaySize.x,displaySize.y);
2022-04-17 06:54:42 +00:00
}
if (scrW>displaySize.w) {
scrW=(displaySize.w)-32;
mustChange=true;
}
if (scrH>displaySize.h) {
scrH=(displaySize.h)-32;
mustChange=true;
}
if (mustChange) {
2022-09-08 22:04:38 +00:00
portrait=(scrW<scrH);
2022-09-09 00:15:19 +00:00
logV("portrait: %d (%dx%d)",portrait,scrW,scrH);
2022-04-17 06:54:42 +00:00
if (!fullScreen) {
logD("setting window size to %dx%d as it goes off bounds (%dx%d+%d+%d).",scrW,scrH,displaySize.w,displaySize.h,displaySize.x,displaySize.y);
SDL_SetWindowSize(sdlWin,scrW,scrH);
2022-04-17 06:54:42 +00:00
}
2022-02-15 22:42:56 +00:00
}
}
#endif
2022-05-17 17:46:52 +00:00
#ifdef IS_MOBILE
SDL_GetWindowSize(sdlWin,&scrW,&scrH);
2022-09-09 00:15:19 +00:00
portrait=(scrW<scrH);
logV("portrait: %d (%dx%d)",portrait,scrW,scrH);
2022-05-17 17:46:52 +00:00
#endif
#if !(defined(__APPLE__) || defined(_WIN32))
2022-01-07 08:29:56 +00:00
if (icon!=NULL) {
SDL_SetWindowIcon(sdlWin,icon);
SDL_FreeSurface(icon);
} else {
logW("could not create icon!");
2022-01-07 08:29:56 +00:00
}
#endif
2023-07-07 02:14:25 +00:00
int numDriversA=SDL_GetNumAudioDrivers();
if (numDriversA<0) {
logW("could not list audio drivers! %s",SDL_GetError());
} else {
for (int i=0; i<numDriversA; i++) {
const char* r=SDL_GetAudioDriver(i);
if (r==NULL) continue;
if (strcmp(r,"disk")==0) continue;
if (strcmp(r,"dummy")==0) continue;
availAudioDrivers.push_back(String(r));
}
}
int numDrivers=SDL_GetNumRenderDrivers();
if (numDrivers<0) {
logW("could not list render drivers! %s",SDL_GetError());
} else {
SDL_RendererInfo ri;
2023-08-28 08:02:35 +00:00
logV("available render drivers:");
for (int i=0; i<numDrivers; i++) {
int r=SDL_GetRenderDriverInfo(i,&ri);
if (r!=0) continue;
availRenderDrivers.push_back(String(ri.name));
2023-08-28 08:02:35 +00:00
logV("- %s",ri.name);
}
}
if (!settings.renderDriver.empty()) {
SDL_SetHint(SDL_HINT_RENDER_DRIVER,settings.renderDriver.c_str());
}
2023-06-05 04:55:57 +00:00
logD("starting render backend...");
if (!rend->init(sdlWin)) {
2023-08-28 08:02:35 +00:00
logE("it failed...");
if (settings.renderBackend!="SDL") {
settings.renderBackend="SDL";
e->setConf("renderBackend","SDL");
e->saveConf();
lastError=fmt::sprintf("could not init renderer!\r\nthe render backend has been set to a safe value. please restart Furnace.");
} else {
lastError=fmt::sprintf("could not init renderer! %s",SDL_GetError());
if (!settings.renderDriver.empty()) {
settings.renderDriver="";
e->setConf("renderDriver","");
e->saveConf();
lastError+=fmt::sprintf("\r\nthe render driver has been set to a safe value. please restart Furnace.");
}
}
2021-12-11 08:11:40 +00:00
return false;
}
2023-08-28 08:02:35 +00:00
logV("render backend started");
2021-12-11 08:11:40 +00:00
// try acquiring the canvas size
if (!rend->getOutputSize(canvasW,canvasH)) {
logW("could not get renderer output size!");
} else {
logV("canvas size: %dx%d",canvasW,canvasH);
}
// special consideration for Wayland
if (settings.dpiScale<0.5f) {
if (strcmp(videoBackend,"wayland")==0) {
int realW=scrW;
int realH=scrH;
SDL_GetWindowSize(sdlWin,&realW,&realH);
if (realW<1) {
2023-06-02 19:06:25 +00:00
logW("screen width is zero!\n");
dpiScale=1.0;
} else {
dpiScale=(double)canvasW/(double)realW;
logV("we're on Wayland... scaling factor: %f",dpiScale);
2023-06-02 19:06:25 +00:00
}
}
}
2023-06-05 04:55:57 +00:00
updateWindowTitle();
rend->clear(ImVec4(0.0,0.0,0.0,1.0));
rend->present();
logD("preparing user interface...");
2023-07-02 05:09:39 +00:00
IMGUI_CHECKVERSION();
ImGui::CreateContext();
rend->initGUI(sdlWin);
2021-12-11 08:11:40 +00:00
applyUISettings();
2021-12-11 08:11:40 +00:00
2023-06-05 04:55:57 +00:00
logD("building font...");
if (!ImGui::GetIO().Fonts->Build()) {
logE("error while building font atlas!");
showError("error while loading fonts! please check your settings.");
ImGui::GetIO().Fonts->Clear();
mainFont=ImGui::GetIO().Fonts->AddFontDefault();
patFont=mainFont;
2023-07-10 19:38:26 +00:00
bigFont=mainFont;
2023-08-03 06:20:59 +00:00
headFont=mainFont;
if (rend) rend->destroyFontsTexture();
if (!ImGui::GetIO().Fonts->Build()) {
logE("error again while building font atlas!");
}
}
2023-06-05 04:55:57 +00:00
logD("preparing layout...");
2021-12-19 21:01:24 +00:00
strncpy(finalLayoutPath,(e->getConfigPath()+String(LAYOUT_INI)).c_str(),4095);
backupPath=e->getConfigPath();
if (backupPath.size()>0) {
if (backupPath[backupPath.size()-1]==DIR_SEPARATOR) backupPath.resize(backupPath.size()-1);
}
backupPath+=String(BACKUPS_DIR);
2021-12-19 21:01:24 +00:00
prepareLayout();
2021-12-19 08:16:24 +00:00
2021-12-14 01:55:40 +00:00
ImGui::GetIO().ConfigFlags|=ImGuiConfigFlags_DockingEnable;
2022-05-18 23:42:59 +00:00
toggleMobileUI(mobileUI,true);
2021-12-14 01:55:40 +00:00
2022-01-08 06:57:37 +00:00
for (int i=0; i<DIV_MAX_CHANS; i++) {
2021-12-26 23:05:18 +00:00
oldPat[i]=new DivPattern;
}
2022-02-08 07:11:21 +00:00
firstFrame=true;
2022-03-28 23:19:47 +00:00
// TODO: MIDI mapping time!
e->setMidiCallback([this](const TAMidiMessage& msg) -> int {
if (introPos<11.0) return -2;
2022-03-28 23:19:47 +00:00
midiLock.lock();
midiQueue.push(msg);
midiLock.unlock();
e->setMidiBaseChan(cursor.xCoarse);
if (msg.type==TA_MIDI_SYSEX) return -2;
2022-04-01 06:50:01 +00:00
if (midiMap.valueInputStyle!=0 && cursor.xFine!=0 && edit) return -2;
if (!midiMap.noteInput) return -2;
2022-03-31 23:39:01 +00:00
if (learning!=-1) return -2;
if (midiMap.at(msg)) return -2;
2022-11-06 07:06:51 +00:00
if (curWindowThreadSafe==GUI_WINDOW_WAVE_EDIT || curWindowThreadSafe==GUI_WINDOW_WAVE_LIST) {
if ((msg.type&0xf0)==TA_MIDI_NOTE_ON) {
e->previewWaveNoLock(curWave,msg.data[0]-12);
wavePreviewNote=msg.data[0]-12;
} else if ((msg.type&0xf0)==TA_MIDI_NOTE_OFF) {
if (wavePreviewNote==msg.data[0]-12) {
e->stopWavePreviewNoLock();
}
}
return -2;
}
if (curWindowThreadSafe==GUI_WINDOW_SAMPLE_EDIT || curWindowThreadSafe==GUI_WINDOW_SAMPLE_LIST) {
if ((msg.type&0xf0)==TA_MIDI_NOTE_ON) {
e->previewSampleNoLock(curSample,msg.data[0]-12);
samplePreviewNote=msg.data[0]-12;
} else if ((msg.type&0xf0)==TA_MIDI_NOTE_OFF) {
if (samplePreviewNote==msg.data[0]-12) {
e->stopSamplePreviewNoLock();
}
}
return -2;
}
2022-03-28 23:19:47 +00:00
return curIns;
2022-03-28 20:24:09 +00:00
});
2023-06-05 04:55:57 +00:00
#ifdef IS_MOBILE
2022-12-02 21:52:47 +00:00
vibrator=SDL_HapticOpen(0);
if (vibrator==NULL) {
logD("could not open vibration device: %s",SDL_GetError());
} else {
if (SDL_HapticRumbleInit(vibrator)==0) {
vibratorAvailable=true;
} else {
logD("vibration not available: %s",SDL_GetError());
}
}
2023-06-05 04:55:57 +00:00
#endif
2022-12-02 21:52:47 +00:00
2023-09-05 09:38:57 +00:00
cpuCores=SDL_GetCPUCount();
if (cpuCores<1) cpuCores=1;
2023-06-05 04:55:57 +00:00
logI("done!");
2021-12-11 08:11:40 +00:00
return true;
2021-12-19 21:01:24 +00:00
}
void FurnaceGUI::commitState() {
2022-05-18 23:42:59 +00:00
if (!mobileUI) {
if (!ImGui::SaveIniSettingsToDisk(finalLayoutPath,true)) {
2023-04-04 21:01:45 +00:00
reportError(fmt::sprintf("could NOT save layout! %s",strerror(errno)));
}
2022-05-18 23:42:59 +00:00
}
2021-12-26 23:05:18 +00:00
e->setConf("configVersion",(int)DIV_ENGINE_VERSION);
e->setConf("lastDir",workingDir);
e->setConf("lastDirSong",workingDirSong);
e->setConf("lastDirIns",workingDirIns);
e->setConf("lastDirWave",workingDirWave);
e->setConf("lastDirSample",workingDirSample);
e->setConf("lastDirAudioExport",workingDirAudioExport);
e->setConf("lastDirVGMExport",workingDirVGMExport);
2022-05-26 05:24:21 +00:00
e->setConf("lastDirZSMExport",workingDirZSMExport);
2022-08-04 05:51:47 +00:00
e->setConf("lastDirROMExport",workingDirROMExport);
e->setConf("lastDirFont",workingDirFont);
e->setConf("lastDirColors",workingDirColors);
e->setConf("lastDirKeybinds",workingDirKeybinds);
e->setConf("lastDirLayout",workingDirLayout);
e->setConf("lastDirTest",workingDirTest);
2022-01-29 06:54:30 +00:00
// commit last open windows
e->setConf("editControlsOpen",editControlsOpen);
e->setConf("ordersOpen",ordersOpen);
e->setConf("insListOpen",insListOpen);
e->setConf("songInfoOpen",songInfoOpen);
e->setConf("patternOpen",patternOpen);
e->setConf("insEditOpen",insEditOpen);
e->setConf("waveListOpen",waveListOpen);
e->setConf("waveEditOpen",waveEditOpen);
e->setConf("sampleListOpen",sampleListOpen);
e->setConf("sampleEditOpen",sampleEditOpen);
e->setConf("settingsOpen",settingsOpen);
e->setConf("mixerOpen",mixerOpen);
e->setConf("oscOpen",oscOpen);
e->setConf("chanOscOpen",chanOscOpen);
2022-01-29 23:56:08 +00:00
e->setConf("volMeterOpen",volMeterOpen);
e->setConf("statsOpen",statsOpen);
e->setConf("compatFlagsOpen",compatFlagsOpen);
e->setConf("pianoOpen",pianoOpen);
e->setConf("notesOpen",notesOpen);
e->setConf("channelsOpen",channelsOpen);
e->setConf("patManagerOpen",patManagerOpen);
2022-08-19 09:41:45 +00:00
e->setConf("sysManagerOpen",sysManagerOpen);
2022-11-10 06:26:59 +00:00
e->setConf("clockOpen",clockOpen);
e->setConf("speedOpen",speedOpen);
2023-02-05 07:56:39 +00:00
e->setConf("groovesOpen",groovesOpen);
e->setConf("regViewOpen",regViewOpen);
e->setConf("logOpen",logOpen);
2022-04-19 23:44:05 +00:00
e->setConf("effectListOpen",effectListOpen);
e->setConf("subSongsOpen",subSongsOpen);
e->setConf("findOpen",findOpen);
2022-06-20 20:20:02 +00:00
e->setConf("spoilerOpen",spoilerOpen);
2023-02-06 23:52:51 +00:00
e->setConf("basicMode",basicMode);
2022-01-29 06:54:30 +00:00
2023-05-15 06:36:02 +00:00
// commit dir state
e->setConf("insListDir",insListDir);
e->setConf("waveListDir",waveListDir);
e->setConf("sampleListDir",sampleListDir);
2022-01-29 22:27:51 +00:00
// commit last window size
e->setConf("lastWindowWidth",scrConfW);
e->setConf("lastWindowHeight",scrConfH);
e->setConf("lastWindowX",settings.saveWindowPos?scrConfX:(int)SDL_WINDOWPOS_CENTERED);
e->setConf("lastWindowY",settings.saveWindowPos?scrConfY:(int)SDL_WINDOWPOS_CENTERED);
e->setConf("lastWindowMax",scrMax);
2022-01-29 22:27:51 +00:00
e->setConf("tempoView",tempoView);
e->setConf("waveHex",waveHex);
2022-09-11 03:35:21 +00:00
e->setConf("waveSigned",waveSigned);
2022-07-21 08:14:52 +00:00
e->setConf("waveGenVisible",waveGenVisible);
2022-07-21 07:49:19 +00:00
e->setConf("waveEditStyle",waveEditStyle);
e->setConf("extraChannelButtons",extraChannelButtons);
e->setConf("lockLayout",lockLayout);
2022-04-17 06:54:42 +00:00
e->setConf("fullScreen",fullScreen);
e->setConf("mobileUI",mobileUI);
e->setConf("edit",edit);
e->setConf("followOrders",followOrders);
e->setConf("followPattern",followPattern);
e->setConf("orderEditMode",orderEditMode);
2022-06-03 23:05:07 +00:00
e->setConf("noteInputPoly",noteInputPoly);
if (settings.persistFadeOut) {
e->setConf("exportLoops",exportLoops);
e->setConf("exportFadeOut",exportFadeOut);
}
// commit oscilloscope state
e->setConf("oscZoom",oscZoom);
2022-05-31 03:22:53 +00:00
e->setConf("oscZoomSlider",oscZoomSlider);
e->setConf("oscWindowSize",oscWindowSize);
2022-05-27 06:13:33 +00:00
// commit piano state
e->setConf("pianoOctaves",pianoOctaves);
e->setConf("pianoOctavesEdit",pianoOctavesEdit);
e->setConf("pianoOptions",pianoOptions);
e->setConf("pianoSharePosition",pianoSharePosition);
e->setConf("pianoOptionsSet",pianoOptionsSet);
2023-05-24 15:47:20 +00:00
e->setConf("pianoReadonly",pianoReadonly);
2022-05-27 06:13:33 +00:00
e->setConf("pianoOffset",pianoOffset);
e->setConf("pianoOffsetEdit",pianoOffsetEdit);
e->setConf("pianoView",pianoView);
e->setConf("pianoInputPadMode",pianoInputPadMode);
2022-06-23 09:02:41 +00:00
// commit per-chan osc state
e->setConf("chanOscCols",chanOscCols);
e->setConf("chanOscAutoColsType",chanOscAutoColsType);
2022-06-23 09:02:41 +00:00
e->setConf("chanOscColorX",chanOscColorX);
e->setConf("chanOscColorY",chanOscColorY);
2023-06-18 09:27:22 +00:00
e->setConf("chanOscTextX",chanOscTextX);
e->setConf("chanOscTextY",chanOscTextY);
e->setConf("chanOscAmplify",chanOscAmplify);
2022-06-23 09:02:41 +00:00
e->setConf("chanOscWindowSize",chanOscWindowSize);
e->setConf("chanOscWaveCorr",chanOscWaveCorr);
e->setConf("chanOscOptions",chanOscOptions);
2023-06-18 09:27:22 +00:00
e->setConf("chanOscNormalize",chanOscNormalize);
e->setConf("chanOscTextFormat",chanOscTextFormat);
2022-06-23 09:02:41 +00:00
e->setConf("chanOscColorR",chanOscColor.x);
e->setConf("chanOscColorG",chanOscColor.y);
e->setConf("chanOscColorB",chanOscColor.z);
e->setConf("chanOscColorA",chanOscColor.w);
2023-06-18 09:27:22 +00:00
e->setConf("chanOscTextColorR",chanOscTextColor.x);
e->setConf("chanOscTextColorG",chanOscTextColor.y);
e->setConf("chanOscTextColorB",chanOscTextColor.z);
e->setConf("chanOscTextColorA",chanOscTextColor.w);
2022-06-23 09:02:41 +00:00
e->setConf("chanOscUseGrad",chanOscUseGrad);
e->setConf("chanOscGrad",chanOscGrad.toString());
2022-09-10 23:53:27 +00:00
// commit recent files
for (int i=0; i<30; i++) {
String key=fmt::sprintf("recentFile%d",i);
if (i>=settings.maxRecentFile || i>=(int)recentFile.size()) {
e->setConf(key,"");
} else {
e->setConf(key,recentFile[i]);
}
}
}
bool FurnaceGUI::finish() {
commitState();
rend->quitGUI();
ImGui_ImplSDL2_Shutdown();
quitRender();
2023-07-02 05:09:39 +00:00
ImGui::DestroyContext();
SDL_DestroyWindow(sdlWin);
2022-09-10 23:53:27 +00:00
2022-12-02 21:52:47 +00:00
if (vibrator) {
SDL_HapticClose(vibrator);
}
2023-08-05 08:26:36 +00:00
for (int i=0; i<DIV_MAX_OUTPUTS; i++) {
if (oscValues[i]) {
delete[] oscValues[i];
oscValues[i]=NULL;
}
}
2022-01-08 06:57:37 +00:00
for (int i=0; i<DIV_MAX_CHANS; i++) {
2021-12-26 23:05:18 +00:00
delete oldPat[i];
}
2022-03-21 19:11:28 +00:00
if (backupTask.valid()) {
backupTask.get();
}
2023-09-05 09:38:57 +00:00
if (chanOscWorkPool!=NULL) {
delete chanOscWorkPool;
}
2021-12-19 21:01:24 +00:00
return true;
2021-12-11 08:11:40 +00:00
}
void FurnaceGUI::requestQuit() {
quit=true;
}
2021-12-11 07:10:09 +00:00
FurnaceGUI::FurnaceGUI():
2021-12-11 08:11:40 +00:00
e(NULL),
renderBackend(GUI_BACKEND_SDL),
rend(NULL),
sdlWin(NULL),
2022-12-02 21:52:47 +00:00
vibrator(NULL),
vibratorAvailable(false),
2022-03-17 08:43:02 +00:00
sampleTex(NULL),
sampleTexW(0),
sampleTexH(0),
updateSampleTex(true),
2021-12-11 08:11:40 +00:00
quit(false),
2021-12-30 23:52:36 +00:00
warnQuit(false),
2021-12-20 03:51:02 +00:00
willCommit(false),
2021-12-28 23:23:57 +00:00
edit(false),
2021-12-30 23:25:55 +00:00
modified(false),
2022-01-09 21:36:47 +00:00
displayError(false),
displayExporting(false),
vgmExportLoop(true),
2022-05-26 05:24:21 +00:00
zsmExportLoop(true),
2023-08-11 20:03:37 +00:00
zsmExportOptimize(true),
vgmExportPatternHints(false),
vgmExportDirectStream(false),
displayInsTypeList(false),
2022-09-08 22:04:38 +00:00
portrait(false),
injectBackUp(false),
2022-09-09 00:15:19 +00:00
mobileMenuOpen(false),
warnColorPushed(false),
wantCaptureKeyboard(false),
2022-05-11 21:09:23 +00:00
oldWantCaptureKeyboard(false),
2022-05-08 23:32:16 +00:00
displayMacroMenu(false),
2022-03-01 22:19:52 +00:00
displayNew(false),
2022-04-17 06:54:42 +00:00
fullScreen(false),
2022-04-28 08:36:15 +00:00
preserveChanPos(false),
wantScrollList(false),
2022-06-03 23:05:07 +00:00
noteInputPoly(true),
notifyWaveChange(false),
displayPendingIns(false),
pendingInsSingle(false),
displayPendingRawSample(false),
snesFilterHex(false),
modTableHex(false),
2023-05-16 18:41:08 +00:00
displayEditString(false),
2022-12-08 23:04:18 +00:00
mobileEdit(false),
2023-07-02 05:09:39 +00:00
killGraphics(false),
2023-08-31 04:14:47 +00:00
audioEngineChanged(false),
settingsChanged(false),
2023-09-03 09:22:00 +00:00
debugFFT(false),
vgmExportVersion(0x171),
vgmExportTrailingTicks(-1),
2022-04-16 23:35:25 +00:00
drawHalt(10),
2022-05-26 05:24:21 +00:00
zsmExportTickRate(60),
2022-04-29 10:39:18 +00:00
macroPointSize(16),
2022-07-21 07:49:19 +00:00
waveEditStyle(0),
displayInsTypeListMakeInsSample(-1),
2022-12-11 17:36:41 +00:00
mobileEditPage(0),
2023-08-22 07:30:33 +00:00
wheelCalmDown(0),
shallDetectScale(0),
2022-09-09 00:15:19 +00:00
mobileMenuPos(0.0f),
2022-12-08 20:41:10 +00:00
autoButtonSize(0.0f),
2022-12-08 23:04:18 +00:00
mobileEditAnim(0.0f),
mobileEditButtonPos(0.7f,0.7f),
mobileEditButtonSize(60.0f,60.0f),
2022-08-27 04:35:16 +00:00
curSysSection(NULL),
updateFMPreview(true),
fmPreviewOn(false),
fmPreviewPaused(false),
fmPreviewOPN(NULL),
fmPreviewOPM(NULL),
fmPreviewOPL(NULL),
fmPreviewOPLL(NULL),
fmPreviewOPZ(NULL),
2023-08-22 03:41:35 +00:00
fmPreviewOPZInterface(NULL),
2023-05-16 18:41:08 +00:00
editString(NULL),
2022-08-13 11:25:11 +00:00
pendingRawSampleDepth(8),
pendingRawSampleChannels(1),
pendingRawSampleUnsigned(false),
pendingRawSampleBigEndian(false),
pendingRawSampleSwapNibbles(false),
2023-07-17 21:31:55 +00:00
pendingRawSampleReplace(false),
2022-05-19 21:35:00 +00:00
globalWinFlags(0),
curFileDialog(GUI_FILE_OPEN),
2021-12-30 23:52:36 +00:00
warnAction(GUI_WARN_OPEN),
postWarnAction(GUI_WARN_GENERIC),
2022-09-09 04:20:33 +00:00
mobScene(GUI_SCENE_PATTERN),
fileDialog(NULL),
2021-12-11 08:11:40 +00:00
scrW(1280),
scrH(800),
2022-09-08 06:37:16 +00:00
scrConfW(1280),
scrConfH(800),
canvasW(1280),
canvasH(800),
2022-09-08 06:37:16 +00:00
scrX(SDL_WINDOWPOS_CENTERED),
scrY(SDL_WINDOWPOS_CENTERED),
scrConfX(SDL_WINDOWPOS_CENTERED),
scrConfY(SDL_WINDOWPOS_CENTERED),
scrMax(false),
2022-10-19 20:33:20 +00:00
sysManagedScale(false),
2021-12-11 21:44:02 +00:00
dpiScale(1),
2021-12-19 04:03:50 +00:00
aboutScroll(0),
aboutSin(0),
aboutHue(0.0f),
2022-03-21 19:32:33 +00:00
backupTimer(15.0),
2022-03-31 23:39:01 +00:00
learning(-1),
mainFont(NULL),
iconFont(NULL),
2023-08-15 01:02:10 +00:00
furIconFont(NULL),
patFont(NULL),
bigFont(NULL),
headFont(NULL),
fontRange(NULL),
2022-05-05 03:55:11 +00:00
prevInsData(NULL),
2021-12-11 21:44:02 +00:00
curIns(0),
curWave(0),
curSample(0),
curOctave(3),
curOrder(0),
2022-04-26 06:07:28 +00:00
prevIns(0),
2021-12-13 22:09:46 +00:00
oldRow(0),
oldOrder(0),
oldOrder1(0),
2021-12-14 22:45:37 +00:00
editStep(1),
exportLoops(0),
soloChan(-1),
orderEditMode(0),
2022-01-20 07:11:03 +00:00
orderCursor(-1),
2022-01-27 05:29:16 +00:00
loopOrder(-1),
loopRow(-1),
loopEnd(-1),
2022-01-29 23:56:08 +00:00
isClipping(0),
2022-02-21 08:05:00 +00:00
extraChannelButtons(0),
2022-03-02 07:22:51 +00:00
newSongCategory(0),
2022-04-21 23:10:59 +00:00
latchTarget(0),
wheelX(0),
wheelY(0),
dragSourceX(0),
dragSourceY(0),
dragDestinationX(0),
dragDestinationY(0),
2022-11-10 06:26:59 +00:00
oldBeat(-1),
oldBar(-1),
2023-02-05 07:56:39 +00:00
curGroove(-1),
exitDisabledTimer(0),
soloTimeout(0.0f),
2022-06-06 08:05:55 +00:00
exportFadeOut(5.0),
2021-12-15 22:32:08 +00:00
editControlsOpen(true),
2021-12-14 09:45:44 +00:00
ordersOpen(true),
insListOpen(true),
songInfoOpen(true),
patternOpen(true),
insEditOpen(false),
waveListOpen(true),
waveEditOpen(false),
sampleListOpen(true),
sampleEditOpen(false),
2021-12-19 04:03:50 +00:00
aboutOpen(false),
2021-12-20 03:51:02 +00:00
settingsOpen(false),
mixerOpen(false),
2022-01-27 05:29:16 +00:00
debugOpen(false),
inspectorOpen(false),
2022-01-27 22:49:00 +00:00
oscOpen(true),
2022-01-29 23:56:08 +00:00
volMeterOpen(true),
statsOpen(false),
compatFlagsOpen(false),
pianoOpen(false),
notesOpen(false),
channelsOpen(false),
regViewOpen(false),
logOpen(false),
2022-04-19 23:44:05 +00:00
effectListOpen(false),
chanOscOpen(false),
subSongsOpen(true),
2022-06-06 10:03:19 +00:00
findOpen(false),
2022-06-20 20:20:02 +00:00
spoilerOpen(false),
patManagerOpen(false),
2022-08-19 09:41:45 +00:00
sysManagerOpen(false),
2022-11-10 06:26:59 +00:00
clockOpen(false),
speedOpen(true),
2023-02-05 07:56:39 +00:00
groovesOpen(false),
2023-02-06 23:52:51 +00:00
basicMode(true),
2023-05-15 06:36:02 +00:00
shortIntro(false),
insListDir(false),
waveListDir(false),
sampleListDir(false),
2022-11-10 06:26:59 +00:00
clockShowReal(true),
clockShowRow(true),
clockShowBeat(true),
clockShowMetro(true),
clockShowTime(true),
2021-12-14 22:45:37 +00:00
selecting(false),
selectingFull(false),
dragging(false),
2021-12-14 22:45:37 +00:00
curNibble(false),
2022-01-20 07:11:03 +00:00
orderNibble(false),
followOrders(true),
followPattern(true),
2021-12-22 21:22:47 +00:00
changeAllOrders(false),
mobileUI(MOBILE_UI_DEFAULT),
collapseWindow(false),
demandScrollX(false),
2022-02-15 18:38:59 +00:00
fancyPattern(false),
firstFrame(true),
tempoView(true),
waveHex(false),
2022-09-11 03:35:21 +00:00
waveSigned(false),
2022-07-21 08:14:52 +00:00
waveGenVisible(false),
lockLayout(false),
2022-04-21 23:10:59 +00:00
editOptsVisible(false),
latchNibble(false),
nonLatchNibble(false),
2022-09-30 23:59:56 +00:00
keepLoopAlive(false),
2023-02-05 07:56:39 +00:00
keepGrooveAlive(false),
2022-11-30 22:20:04 +00:00
orderScrollLocked(false),
2022-11-30 22:37:48 +00:00
orderScrollTolerance(false),
2022-12-04 22:19:21 +00:00
dragMobileMenu(false),
2022-12-07 03:40:23 +00:00
dragMobileEditButton(false),
2023-02-05 07:56:39 +00:00
wantGrooveListFocus(false),
2023-05-17 00:00:05 +00:00
lastAssetType(0),
2021-12-14 22:45:37 +00:00
curWindow(GUI_WINDOW_NOTHING),
nextWindow(GUI_WINDOW_NOTHING),
curWindowLast(GUI_WINDOW_NOTHING),
2022-11-06 07:06:51 +00:00
curWindowThreadSafe(GUI_WINDOW_NOTHING),
lastPatternWidth(0.0f),
2022-12-02 21:52:47 +00:00
longThreshold(0.48f),
2022-12-07 03:40:23 +00:00
buttonLongThreshold(0.20f),
latchNote(-1),
latchIns(-2),
latchVol(-1),
latchEffect(-1),
latchEffectVal(-1),
2022-05-21 23:36:15 +00:00
wavePreviewLen(32),
wavePreviewHeight(255),
wavePreviewInit(true),
wavePreviewPaused(false),
2022-06-09 23:52:38 +00:00
pgSys(0),
pgAddr(0),
pgVal(0),
curQueryRangeX(false),
curQueryBackwards(false),
curQueryRangeXMin(0), curQueryRangeXMax(0),
curQueryRangeY(0),
curQueryEffectPos(0),
queryReplaceEffectCount(0),
2023-02-13 23:33:25 +00:00
queryReplaceEffectPos(1),
2022-06-09 23:52:38 +00:00
queryReplaceNoteMode(0),
queryReplaceInsMode(0),
queryReplaceVolMode(0),
queryReplaceNote(0),
queryReplaceIns(0),
queryReplaceVol(0),
queryReplaceNoteDo(false),
queryReplaceInsDo(false),
queryReplaceVolDo(false),
2022-06-11 07:14:30 +00:00
queryViewingResults(false),
wavePreviewOn(false),
wavePreviewKey((SDL_Scancode)0),
wavePreviewNote(0),
2022-01-20 21:51:31 +00:00
samplePreviewOn(false),
samplePreviewKey((SDL_Scancode)0),
samplePreviewNote(0),
2023-04-11 00:49:14 +00:00
sampleMapSelStart(-1),
sampleMapSelEnd(-1),
sampleMapDigit(0),
sampleMapColumn(0),
sampleMapFocused(false),
sampleMapWaitingInput(false),
macroDragStart(0,0),
macroDragAreaSize(0,0),
macroDragCTarget(NULL),
macroDragTarget(NULL),
macroDragLen(0),
macroDragMin(0),
macroDragMax(0),
macroDragLastX(-1),
macroDragLastY(-1),
macroDragBitOff(0),
2022-01-21 22:00:28 +00:00
macroDragScroll(0),
macroDragBitMode(false),
macroDragInitialValueSet(false),
macroDragInitialValue(false),
macroDragChar(false),
macroDragBit30(false),
2022-08-23 08:57:21 +00:00
macroDragSettingBit30(false),
2022-04-13 04:03:20 +00:00
macroDragLineMode(false),
2022-05-08 23:32:16 +00:00
macroDragMouseMoved(false),
2022-04-13 04:03:20 +00:00
macroDragLineInitial(0,0),
macroDragLineInitialV(0,0),
2021-12-13 22:09:46 +00:00
macroDragActive(false),
2022-05-08 23:32:16 +00:00
lastMacroDesc(NULL,NULL,0,0,0.0f),
macroOffX(0),
macroOffY(0),
macroScaleX(100.0f),
macroScaleY(100.0f),
macroRandMin(0),
macroRandMax(0),
macroLoopDragStart(0,0),
macroLoopDragAreaSize(0,0),
macroLoopDragTarget(NULL),
macroLoopDragLen(0),
macroLoopDragActive(false),
waveDragStart(0,0),
waveDragAreaSize(0,0),
waveDragTarget(NULL),
waveDragLen(0),
waveDragMin(0),
waveDragMax(0),
waveDragActive(false),
bindSetTarget(0),
bindSetPrevValue(0),
bindSetActive(false),
bindSetPending(false),
nextScroll(-1.0f),
2021-12-26 23:05:18 +00:00
nextAddScroll(0.0f),
2022-11-30 22:20:04 +00:00
orderScroll(0.0f),
orderScrollSlideOrigin(0.0f),
2022-11-30 22:37:48 +00:00
orderScrollRealOrigin(0.0f,0.0f),
2022-12-04 22:19:21 +00:00
dragMobileMenuOrigin(0.0f,0.0f),
2022-05-27 05:19:10 +00:00
layoutTimeBegin(0),
layoutTimeEnd(0),
layoutTimeDelta(0),
renderTimeBegin(0),
renderTimeEnd(0),
renderTimeDelta(0),
2023-06-12 20:58:16 +00:00
drawTimeBegin(0),
drawTimeEnd(0),
drawTimeDelta(0),
2022-05-27 05:19:10 +00:00
eventTimeBegin(0),
eventTimeEnd(0),
eventTimeDelta(0),
2023-04-14 00:43:48 +00:00
perfMetricsLen(0),
2022-05-27 06:04:12 +00:00
chanToMove(-1),
2022-08-26 23:51:17 +00:00
sysToMove(-1),
sysToDelete(-1),
2022-09-11 04:20:22 +00:00
opToMove(-1),
assetToMove(-1),
dirToMove(-1),
transposeAmount(0),
randomizeMin(0),
randomizeMax(255),
2022-03-12 08:04:34 +00:00
fadeMin(0),
fadeMax(255),
collapseAmount(2),
scaleMax(100.0f),
2022-03-12 08:04:34 +00:00
fadeMode(false),
2022-03-12 08:40:56 +00:00
randomMode(false),
2022-04-13 23:16:55 +00:00
haveHitBounds(false),
2023-07-15 00:24:57 +00:00
pendingStepUpdate(0),
2022-03-17 23:08:59 +00:00
oldOrdersLen(0),
sampleZoom(1.0),
prevSampleZoom(1.0),
minSampleZoom(1.0),
2022-03-19 08:42:44 +00:00
samplePos(0),
resizeSize(1024),
2022-03-22 22:01:06 +00:00
silenceSize(1024),
2022-03-19 08:42:44 +00:00
resampleTarget(32000),
resampleStrat(5),
amplifyVol(100.0),
sampleSelStart(-1),
sampleSelEnd(-1),
sampleInfo(true),
sampleCompatRate(false),
2022-03-19 08:42:44 +00:00
sampleDragActive(false),
2022-03-19 21:14:11 +00:00
sampleDragMode(false),
sampleDrag16(false),
sampleZoomAuto(true),
sampleSelTarget(0),
2022-03-19 21:14:11 +00:00
sampleDragTarget(NULL),
sampleDragStart(0,0),
sampleDragAreaSize(0,0),
sampleDragLen(0),
sampleFilterL(1.0f),
sampleFilterB(0.0f),
sampleFilterH(0.0f),
sampleFilterRes(0.25f),
sampleFilterCutStart(16000.0f),
sampleFilterCutEnd(100.0f),
sampleFilterPower(1),
sampleClipboard(NULL),
2022-03-22 09:54:01 +00:00
sampleClipboardLen(0),
openSampleResizeOpt(false),
openSampleResampleOpt(false),
openSampleAmplifyOpt(false),
2022-03-22 22:01:06 +00:00
openSampleSilenceOpt(false),
openSampleFilterOpt(false),
selectedPortSet(0x1fff),
selectedSubPort(-1),
hoveredPortSet(0x1fff),
hoveredSubPort(-1),
portDragActive(false),
2023-01-10 20:58:15 +00:00
displayHiddenPorts(false),
2023-01-11 00:09:26 +00:00
displayInternalPorts(false),
subPortPos(0.0f,0.0f),
oscTotal(0),
oscWidth(512),
oscZoom(0.5f),
oscWindowSize(20.0f),
oscInput(0.0f),
oscInput1(0.0f),
oscZoomSlider(false),
chanOscCols(3),
chanOscAutoColsType(0),
2022-06-22 20:10:53 +00:00
chanOscColorX(GUI_OSCREF_CENTER),
chanOscColorY(GUI_OSCREF_CENTER),
chanOscWindowSize(20.0f),
2023-06-18 09:27:22 +00:00
chanOscTextX(0.0f),
chanOscTextY(0.0f),
chanOscAmplify(0.95f),
chanOscWaveCorr(true),
chanOscOptions(false),
updateChanOscGradTex(true),
2022-06-22 20:10:53 +00:00
chanOscUseGrad(false),
2023-06-18 09:27:22 +00:00
chanOscNormalize(false),
chanOscTextFormat("%c"),
2022-06-22 20:10:53 +00:00
chanOscColor(1.0f,1.0f,1.0f,1.0f),
2023-06-18 09:27:22 +00:00
chanOscTextColor(1.0f,1.0f,1.0f,0.75f),
chanOscGrad(64,64),
chanOscGradTex(NULL),
2023-09-05 09:38:57 +00:00
chanOscWorkPool(NULL),
2022-04-13 08:24:49 +00:00
followLog(true),
2022-05-17 20:20:56 +00:00
#ifdef IS_MOBILE
pianoOctaves(7),
pianoOctavesEdit(2),
pianoOptions(true),
pianoSharePosition(false),
2022-05-17 23:55:44 +00:00
pianoOptionsSet(false),
2023-05-24 15:47:20 +00:00
pianoReadonly(false),
2022-05-17 20:20:56 +00:00
pianoOffset(6),
pianoOffsetEdit(9),
pianoView(PIANO_LAYOUT_AUTOMATIC),
pianoInputPadMode(PIANO_INPUT_PAD_SPLIT_AUTO),
2022-05-17 20:20:56 +00:00
#else
2022-04-13 08:24:49 +00:00
pianoOctaves(7),
2022-05-17 17:46:52 +00:00
pianoOctavesEdit(4),
2022-04-13 08:24:49 +00:00
pianoOptions(false),
2022-05-17 20:20:56 +00:00
pianoSharePosition(true),
2023-05-24 15:47:20 +00:00
pianoReadonly(false),
pianoOffset(6),
2022-05-17 17:46:52 +00:00
pianoOffsetEdit(6),
pianoView(PIANO_LAYOUT_STANDARD),
pianoInputPadMode(PIANO_INPUT_PAD_DISABLE),
2022-05-17 20:20:56 +00:00
#endif
2022-07-26 23:23:01 +00:00
hasACED(false),
waveGenBaseShape(0),
2022-10-04 09:43:14 +00:00
waveInterpolation(0),
2022-07-27 07:23:29 +00:00
waveGenDuty(0.5f),
waveGenPower(1),
waveGenInvertPoint(1.0f),
waveGenScaleX(32),
2023-02-06 09:02:29 +00:00
waveGenScaleY(32),
waveGenOffsetX(0),
waveGenOffsetY(0),
waveGenSmooth(1),
waveGenAmplify(1.0f),
2023-02-15 21:25:35 +00:00
waveGenFM(false),
introPos(0.0),
2023-02-19 05:08:37 +00:00
introSkip(0.0),
2023-02-19 21:32:05 +00:00
monitorPos(0.0),
mustClear(2),
2023-02-19 05:08:37 +00:00
initialScreenWipe(1.0f),
2023-02-25 23:04:17 +00:00
introSkipDo(false),
introStopped(false),
2023-02-25 23:04:17 +00:00
curTutorial(-1),
curTutorialStep(0) {
2021-12-14 22:45:37 +00:00
// value keys
valueKeys[SDLK_0]=0;
valueKeys[SDLK_1]=1;
valueKeys[SDLK_2]=2;
valueKeys[SDLK_3]=3;
valueKeys[SDLK_4]=4;
valueKeys[SDLK_5]=5;
valueKeys[SDLK_6]=6;
valueKeys[SDLK_7]=7;
valueKeys[SDLK_8]=8;
valueKeys[SDLK_9]=9;
valueKeys[SDLK_a]=10;
valueKeys[SDLK_b]=11;
valueKeys[SDLK_c]=12;
valueKeys[SDLK_d]=13;
valueKeys[SDLK_e]=14;
valueKeys[SDLK_f]=15;
valueKeys[SDLK_KP_0]=0;
valueKeys[SDLK_KP_1]=1;
valueKeys[SDLK_KP_2]=2;
valueKeys[SDLK_KP_3]=3;
valueKeys[SDLK_KP_4]=4;
valueKeys[SDLK_KP_5]=5;
valueKeys[SDLK_KP_6]=6;
valueKeys[SDLK_KP_7]=7;
valueKeys[SDLK_KP_8]=8;
valueKeys[SDLK_KP_9]=9;
2022-03-01 22:19:52 +00:00
memset(willExport,1,DIV_MAX_CHIPS*sizeof(bool));
2022-01-29 23:56:08 +00:00
memset(peak,0,DIV_MAX_OUTPUTS*sizeof(float));
2022-02-10 22:44:27 +00:00
opMaskTransposeNote.note=true;
opMaskTransposeNote.ins=false;
opMaskTransposeNote.vol=false;
opMaskTransposeNote.effect=false;
opMaskTransposeNote.effectVal=false;
opMaskTransposeValue.note=false;
opMaskTransposeValue.ins=true;
opMaskTransposeValue.vol=true;
opMaskTransposeValue.effect=false;
opMaskTransposeValue.effectVal=true;
2022-02-10 22:44:27 +00:00
memset(actionKeys,0,GUI_ACTION_MAX*sizeof(int));
2022-02-15 18:38:59 +00:00
memset(patChanX,0,sizeof(float)*(DIV_MAX_CHANS+1));
memset(patChanSlideY,0,sizeof(float)*(DIV_MAX_CHANS+1));
2022-02-22 05:13:32 +00:00
memset(lastIns,-1,sizeof(int)*DIV_MAX_CHANS);
2023-08-05 08:26:36 +00:00
memset(oscValues,0,sizeof(void*)*DIV_MAX_OUTPUTS);
memset(chanOscLP0,0,sizeof(float)*DIV_MAX_CHANS);
memset(chanOscLP1,0,sizeof(float)*DIV_MAX_CHANS);
2022-06-22 20:10:53 +00:00
memset(chanOscVol,0,sizeof(float)*DIV_MAX_CHANS);
memset(chanOscPitch,0,sizeof(float)*DIV_MAX_CHANS);
memset(chanOscBright,0,sizeof(float)*DIV_MAX_CHANS);
memset(lastCorrPos,0,sizeof(short)*DIV_MAX_CHANS);
memset(acedData,0,23);
2022-05-17 20:20:56 +00:00
2022-07-27 06:20:26 +00:00
memset(waveGenAmp,0,sizeof(float)*16);
memset(waveGenPhase,0,sizeof(float)*16);
waveGenTL[0]=0.0f;
waveGenTL[1]=0.0f;
waveGenTL[2]=0.0f;
waveGenTL[3]=1.0f;
fmWaveform[0]=0;
fmWaveform[1]=0;
fmWaveform[2]=0;
fmWaveform[3]=0;
waveGenMult[0]=1;
waveGenMult[1]=1;
waveGenMult[2]=1;
waveGenMult[3]=1;
memset(waveGenFB,0,sizeof(int)*4);
memset(waveGenFMCon0,0,sizeof(bool)*5);
memset(waveGenFMCon1,0,sizeof(bool)*5);
memset(waveGenFMCon2,0,sizeof(bool)*5);
memset(waveGenFMCon3, 0, sizeof(bool) * 5);
memset(waveGenFMCon4,0,sizeof(bool)*5);
2022-07-27 06:20:26 +00:00
waveGenAmp[0]=1.0f;
waveGenFMCon0[0]=false;
waveGenFMCon1[0]= true;
waveGenFMCon2[1]= true;
waveGenFMCon3[2] = true;
waveGenFMCon4[0]= false;
waveGenFMCon0[4] = false;
waveGenFMCon1[4] = false;
waveGenFMCon2[4] = false;
waveGenFMCon3[4] = true;
2022-07-27 06:20:26 +00:00
memset(keyHit,0,sizeof(float)*DIV_MAX_CHANS);
memset(keyHit1,0,sizeof(float)*DIV_MAX_CHANS);
2022-05-17 20:20:56 +00:00
memset(pianoKeyHit,0,sizeof(float)*180);
memset(pianoKeyPressed,0,sizeof(bool)*180);
2022-06-09 23:52:38 +00:00
memset(queryReplaceEffectMode,0,sizeof(int)*8);
memset(queryReplaceEffectValMode,0,sizeof(int)*8);
memset(queryReplaceEffect,0,sizeof(int)*8);
memset(queryReplaceEffectVal,0,sizeof(int)*8);
memset(queryReplaceEffectDo,0,sizeof(bool)*8);
memset(queryReplaceEffectValDo,0,sizeof(bool)*8);
chanOscGrad.bgColor=ImVec4(0.0f,0.0f,0.0f,1.0f);
memset(noteOffLabel,0,32);
memset(noteRelLabel,0,32);
memset(macroRelLabel,0,32);
memset(emptyLabel,0,32);
memset(emptyLabel2,0,32);
//effect sorting
memset(effectsShow,1,sizeof(bool)*10);
2022-08-13 23:16:24 +00:00
strncpy(noteOffLabel,"OFF",32);
strncpy(noteRelLabel,"===",32);
strncpy(macroRelLabel,"REL",32);
strncpy(emptyLabel,"...",32);
strncpy(emptyLabel2,"..",32);
2022-03-28 20:24:09 +00:00
}