Merge branch 'master' into metal

This commit is contained in:
tildearrow 2024-04-11 13:27:39 -05:00
commit f51035e8a0
2980 changed files with 800668 additions and 175667 deletions

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2023 tildearrow and contributors
* Copyright (C) 2021-2024 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -38,102 +38,160 @@ const char* aboutLine[]={
"akumanatt",
"cam900",
"djtuBIG-MaliceX",
"Eknous",
"Kagamiin~",
"laoo",
"LTVA1",
"MooingLemur",
"OPNA2608",
"scratchminer",
"superctr",
"System64",
"",
"-- graphics/UI design --",
"tildearrow",
"BlastBrothers",
"Electric Keet",
"Mahbod Karamoozian",
"nicco1690",
"Raijin",
"",
"-- documentation --",
"tildearrow",
"freq-mod",
"nicco1690",
"DeMOSic",
"brickblock369",
"cam900",
"DeMOSic",
"Electric Keet",
"freq-mod",
"host12prog",
"WindowxDeveloper",
"Lunathir",
"tildearrow",
"",
"-- demo songs --",
"0x5066",
"Abstract 64",
"Aburtos",
"ActualNK358",
"akumanatt",
"AmigaX",
"AquaDoesStuff",
"AURORA*FIELDS",
"Background2982",
"battybeats",
"bbqzzd",
"Bernie",
"BlastBrothers",
"Blaze Weednix",
"BlueElectric05",
"breakthetargets",
"brickblock369",
"Brightonic",
"Burnt Fishy",
"CaptainMalware",
"Clingojam",
"ControlleronaHanger",
"Crisps",
"DeMOSic",
"DevEd",
"Dippy",
"djtuBIG-MaliceX",
"dmKaltsit",
"Dolce",
"dumbut",
"ElectricKeet",
"Eknous",
"Electric Keet",
"EntropyAuthor",
"EpicTyphlosion",
"FΛDE",
"Forte",
"Fragmare",
"freq-mod",
"gtr3qq",
"Heemin",
"Hortus",
"ifrit05",
"iyatemu",
"JayBOB18",
"Jimmy-DS",
"Kagamiin~",
"Kaoru",
"kleeder",
"Korbo",
"jaezu",
"Laggy",
"leejh20",
"LovelyA72",
"LTVA1",
"LunaMoth",
"Lunathir",
"LVintageNerd",
"Mahbod Karamoozian",
"Marisa Kirisame [DJ MasterSpark]",
"Martin Demsky",
"masicbemester",
"MelonadeM",
"Miker",
"nicco1690",
"Molkirill",
"NeoWar",
"Nerreave",
"niffuM",
"<nk>",
"Notakin",
"nwcr",
"NyaongI",
"Pale Moon",
"PeyPey",
"PichuMario",
"Poltvick",
"PotaJoe",
"potatoTeto",
"psxdominator",
"Raijin",
"railzen7",
"Rei8bit",
"RepellantMold",
"RetroCarrot",
"RevvoBolt",
"Rockyfan75000",
"scooblee",
"sheffield^2",
"sillygoose",
"smaybius",
"SnugglyBun",
"Spinning Square Waves",
"src3453",
"SuperJet Spade",
"Supper_E1",
"SwapXFO",
"System64",
"TakuikaNinja",
"tapekeep",
"TapeStone",
"TCORPStudios",
"Teuthida",
"ThaCuber",
"The Blender Fiddler",
"TheDuccinator",
"theloredev",
"The Beesh-Spweesh!",
"TheRealHedgehogSonic",
"tildearrow",
"Uhrwerk Klockwerx",
"tom_atom",
"traumatized",
"Tytanium654",
"Ultraprogramer",
"UserSniper",
"Weeppiko",
"Xan",
"Yuzu4K",
"Zabir",
"Zaxolotl",
"ZoomTen (Zumi)",
"",
"-- additional feedback/fixes --",
"Electric Keet",
"fd",
"GENATARi",
"host12prog",
"jvsTSX",
"Lumigado",
"Lunathir",
"plane",
@ -142,17 +200,26 @@ const char* aboutLine[]={
"powered by:",
"Dear ImGui by Omar Cornut",
"SDL2 by Sam Lantinga",
#ifdef HAVE_FREETYPE
"FreeType",
#endif
"zlib by Jean-loup Gailly",
"and Mark Adler",
"libsndfile by Erik de Castro Lopo",
"Portable File Dialogs by Sam Hocevar",
"Native File Dialog by Frogtoss Games",
"PortAudio",
"Weak-JACK by x42",
"RtMidi by Gary P. Scavone",
"FFTW by Matteo Frigo and Steven G. Johnson",
"backward-cpp by Google",
"adpcm by superctr",
"Nuked-OPL3/OPLL/OPM/OPN2/PSG by Nuke.YKT",
"adpcm-xq by David Bryant",
"Nuked-OPL3/OPLL/OPM/OPN2/PSG by nukeykt",
"YM3812-LLE, YMF262-LLE and YMF276-LLE by nukeykt",
"ESFMu (modified version) by Kagamiin~",
"ymfm by Aaron Giles",
"emu2413 by Digital Sound Antiques",
"MAME SN76496 by Nicola Salmoria",
"MAME AY-3-8910 by Couriersud",
"with AY8930 fixes by Eulous, cam900 and Grauw",
@ -173,25 +240,30 @@ const char* aboutLine[]={
"reSID by Dag Lem",
"reSIDfp by Dag Lem, Antti Lankila",
"and Leandro Nini",
"dSID by DefleMask Team based on jsSID",
"Stella by Stella Team",
"QSound emulator by superctr and Valley Bell",
"VICE VIC-20 sound core by Rami Rasanen and viznut",
"VICE TED sound core by Andreas Boose, Tibor Biczo",
"and Marco van den Heuvel",
"VERA sound core by Frank van den Hoef",
"mzpokeysnd POKEY emulator by Michael Borisov",
"ASAP POKEY emulator by Piotr Fusik",
"ported by laoo to C++",
"K005289 emulator by cam900",
"Namco 163 emulator by cam900",
"Seta X1-010 emulator by cam900",
"Konami VRC6 emulator by cam900",
"Konami SCC emulator by cam900",
"MSM6295 emulator by cam900",
"vgsound_emu (second version, modified version) by cam900",
"SM8521 emulator (modified version) by cam900",
"D65010G031 emulator (modified version) by cam900",
"Namco C140/C219 emulator (modified version) by cam900",
"PowerNoise emulator by scratchminer",
"ep128emu by Istvan Varga",
"NDS sound emulator by cam900",
"",
"greetings to:",
"NEOART Costa Rica",
"Xenium Demoparty",
"all members of Deflers of Noice!",
"",
"copyright © 2021-2023 tildearrow",
"copyright © 2021-2024 tildearrow",
"(and contributors).",
"licensed under GPLv2+! see",
"LICENSE for more information.",
@ -303,6 +375,16 @@ void FurnaceGUI::drawAbout() {
while (aboutSin>=2400) aboutSin-=2400;
if (aboutScroll>(42*dpiScale*aboutCount+canvasH)) aboutScroll=-20*dpiScale;
if (ImGui::IsKeyPressed(ImGuiKey_Space)) {
aboutOpen=false;
if (modified) {
showWarning("Unsaved changes! Save changes before playing?",GUI_WARN_CV);
} else {
cvOpen=true;
cvNotSerious=true;
}
}
WAKE_UP;
}
if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_ABOUT;

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2023 tildearrow and contributors
* Copyright (C) 2021-2024 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2023 tildearrow and contributors
* Copyright (C) 2021-2024 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -41,6 +41,21 @@ const char* chanOscRefs[]={
"Note Trigger"
};
const char* autoColsTypes[]={
"Off",
"Mode 1",
"Mode 2",
"Mode 3"
};
static void _drawOsc(const ImDrawList* drawList, const ImDrawCmd* cmd) {
if (cmd!=NULL) {
if (cmd->UserCallbackData!=NULL) {
((FurnaceGUI*)(((PendingDrawOsc*)cmd->UserCallbackData)->gui))->runPendingDrawOsc((PendingDrawOsc*)cmd->UserCallbackData);
}
}
}
float FurnaceGUI::computeGradPos(int type, int chan) {
switch (type) {
case GUI_OSCREF_NONE:
@ -53,7 +68,7 @@ float FurnaceGUI::computeGradPos(int type, int chan) {
return 1.0f;
break;
case GUI_OSCREF_FREQUENCY:
return chanOscPitch[chan];
return chanOscChan[chan].pitch;
break;
case GUI_OSCREF_VOLUME:
return chanOscVol[chan];
@ -85,20 +100,20 @@ void FurnaceGUI::calcChanOsc() {
if (--tryAgain<0) break;
buf=e->getOscBuffer(tryAgain);
}
if (buf!=NULL && e->curSubSong->chanShow[i]) {
if (buf!=NULL && e->curSubSong->chanShowChanOsc[i]) {
// 30ms should be enough
int displaySize=(float)(buf->rate)*0.03f;
if (e->isRunning()) {
float minLevel=1.0f;
float maxLevel=-1.0f;
short minLevel=32767;
short maxLevel=-32768;
unsigned short needlePos=buf->needle;
needlePos-=displaySize;
for (unsigned short i=0; i<512; i++) {
float y=(float)buf->data[(unsigned short)(needlePos+(i*displaySize/512))]/32768.0f;
short y=buf->data[(unsigned short)(needlePos+(i*displaySize/512))];
if (minLevel>y) minLevel=y;
if (maxLevel<y) maxLevel=y;
}
float estimate=pow(maxLevel-minLevel,0.5f);
float estimate=pow((float)(maxLevel-minLevel)/32768.0f,0.5f);
if (estimate>1.0f) estimate=1.0f;
chanOscVol[i]=MAX(chanOscVol[i]*0.87f,estimate);
}
@ -121,13 +136,14 @@ void FurnaceGUI::drawChanOsc() {
bool centerSettingReset=false;
ImDrawList* dl=ImGui::GetWindowDrawList();
if (chanOscOptions) {
if (ImGui::BeginTable("ChanOscSettings",3)) {
if (ImGui::BeginTable("ChanOscSettings",2)) {
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::AlignTextToFramePadding();
ImGui::Text("Columns");
ImGui::SameLine();
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
if (ImGui::InputInt("##COSColumns",&chanOscCols,1,1)) {
if (ImGui::InputInt("##COSColumns",&chanOscCols,1,3)) {
if (chanOscCols<1) chanOscCols=1;
if (chanOscCols>64) chanOscCols=64;
}
@ -136,16 +152,59 @@ void FurnaceGUI::drawChanOsc() {
ImGui::Text("Size (ms)");
ImGui::SameLine();
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
if (ImGui::InputFloat("##COSWinSize",&chanOscWindowSize,1.0f,1.0f)) {
if (ImGui::InputFloat("##COSWinSize",&chanOscWindowSize,1.0f,10.0f)) {
if (chanOscWindowSize<1.0f) chanOscWindowSize=1.0f;
if (chanOscWindowSize>50.0f) chanOscWindowSize=50.0f;
}
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::AlignTextToFramePadding();
ImGui::Text("Automatic columns");
ImGui::SameLine();
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
const char* previewColType=autoColsTypes[chanOscAutoColsType&3];
if (ImGui::BeginCombo("##AutoCols",previewColType)) {
for (int j=0; j<4; j++) {
const bool isSelected=(chanOscAutoColsType==j);
if (ImGui::Selectable(autoColsTypes[j],isSelected)) chanOscAutoColsType=j;
if (isSelected) ImGui::SetItemDefaultFocus();
}
ImGui::EndCombo();
}
ImGui::TableNextColumn();
if (ImGui::Checkbox("Center waveform",&chanOscWaveCorr)) {
centerSettingReset=true;
}
ImGui::TableNextRow();
ImGui::TableNextColumn();
if (ImGui::Checkbox("Randomize phase on note",&chanOscRandomPhase)) {
}
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::AlignTextToFramePadding();
ImGui::Text("Amplitude");
ImGui::SameLine();
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
if (CWSliderFloat("##COSAmp",&chanOscAmplify,0.0f,2.0f)) {
if (chanOscAmplify<0.0f) chanOscAmplify=0.0f;
if (chanOscAmplify>2.0f) chanOscAmplify=2.0f;
}
ImGui::TableNextColumn();
ImGui::AlignTextToFramePadding();
ImGui::Text("Line size");
ImGui::SameLine();
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
if (CWSliderFloat("##COSLine",&chanOscLineSize,0.25f,16.0f)) {
if (chanOscLineSize<0.25f) chanOscLineSize=0.26f;
if (chanOscLineSize>16.0f) chanOscLineSize=16.0f;
}
ImGui::EndTable();
}
@ -180,7 +239,7 @@ void FurnaceGUI::drawChanOsc() {
ImVec2 gradLeft=ImGui::GetCursorPos();
ImVec2 gradSize=ImVec2(400.0f*dpiScale,400.0f*dpiScale);
ImGui::Image(rend->getTextureID(chanOscGradTex),gradSize);
ImGui::Image(rend->getTextureID(chanOscGradTex),gradSize,ImVec2(0,0),ImVec2(rend->getTextureU(chanOscGradTex),rend->getTextureV(chanOscGradTex)));
ImVec2 gradLeftAbs=ImGui::GetItemRectMin();
if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) {
if (chanOscGrad.points.size()<32) {
@ -233,6 +292,7 @@ void FurnaceGUI::drawChanOsc() {
if (ImGui::ColorPicker4("Color",(float*)&i.color)) {
updateChanOscGradTex=true;
}
ImGui::AlignTextToFramePadding();
ImGui::Text("Distance");
ImGui::SameLine();
float pDist=i.distance*100.0f;
@ -241,6 +301,7 @@ void FurnaceGUI::drawChanOsc() {
updateChanOscGradTex=true;
}
ImGui::AlignTextToFramePadding();
ImGui::Text("Spread");
ImGui::SameLine();
float pSpread=i.spread*100.0f;
@ -249,10 +310,12 @@ void FurnaceGUI::drawChanOsc() {
updateChanOscGradTex=true;
}
pushDestColor();
if (ImGui::Button("Remove")) {
removePoint=index;
ImGui::CloseCurrentPopup();
}
popDestColor();
ImGui::EndPopup();
}
@ -285,6 +348,7 @@ void FurnaceGUI::drawChanOsc() {
ImGui::ColorPicker4("Color",(float*)&chanOscColor);
}
ImGui::AlignTextToFramePadding();
ImGui::Text("Text format:");
ImGui::SameLine();
ImGui::InputText("##TextFormat",&chanOscTextFormat);
@ -296,14 +360,17 @@ void FurnaceGUI::drawChanOsc() {
"- %C: channel short name\n"
"- %d: channel number (starting from 0)\n"
"- %D: channel number (starting from 1)\n"
"- %n: channel note\n"
"- %i: instrument name\n"
"- %I: instrument number (decimal)\n"
"- %x: instrument number (hex)\n"
"- %s: chip name\n"
"- %p: chip part number\n"
"- %S: chip ID\n"
"- %v: volume (decimal)\n"
"- %V: volume (percentage)\n"
"- %b: volume (hex)\n"
"- %l: new line\n"
"- %%: percent sign"
);
ImGui::EndTooltip();
@ -318,26 +385,195 @@ void FurnaceGUI::drawChanOsc() {
} else {
ImGui::PushStyleVar(ImGuiStyleVar_CellPadding,ImVec2(0.0f,0.0f));
float availY=ImGui::GetContentRegionAvail().y;
if (ImGui::BeginTable("ChanOsc",chanOscCols,ImGuiTableFlags_Borders)) {
if (ImGui::BeginTable("ChanOsc",chanOscCols,ImGuiTableFlags_Borders|ImGuiTableFlags_NoClip)) {
std::vector<DivDispatchOscBuffer*> oscBufs;
std::vector<ChanOscStatus*> oscFFTs;
std::vector<int> oscChans;
int chans=e->getTotalChannelCount();
ImGuiWindow* window=ImGui::GetCurrentWindow();
ImVec2 waveform[512];
ImGuiStyle& style=ImGui::GetStyle();
ImVec2 waveform[1024];
// check work thread
if (chanOscWorkPool==NULL) {
logV("creating chan osc work pool");
chanOscWorkPool=new DivWorkPool(settings.chanOscThreads);
}
// fill buffers
for (int i=0; i<chans; i++) {
DivDispatchOscBuffer* buf=e->getOscBuffer(i);
if (buf!=NULL && e->curSubSong->chanShow[i]) {
if (buf!=NULL && e->curSubSong->chanShowChanOsc[i]) {
oscBufs.push_back(buf);
oscFFTs.push_back(&chanOscChan[i]);
oscChans.push_back(i);
}
}
// process
for (size_t i=0; i<oscBufs.size(); i++) {
ChanOscStatus* fft_=oscFFTs[i];
fft_->relatedBuf=oscBufs[i];
fft_->relatedCh=oscChans[i];
if (fft_->relatedBuf!=NULL) {
// prepare
if (centerSettingReset) {
fft_->relatedBuf->readNeedle=fft_->relatedBuf->needle;
}
// check FFT status existence
if (!fft_->ready) {
logD("creating FFT plan for channel %d",fft_->relatedCh);
fft_->inBuf=(double*)fftw_malloc(FURNACE_FFT_SIZE*sizeof(double));
fft_->outBuf=(fftw_complex*)fftw_malloc(FURNACE_FFT_SIZE*sizeof(fftw_complex));
fft_->corrBuf=(double*)fftw_malloc(FURNACE_FFT_SIZE*sizeof(double));
fft_->plan=fftw_plan_dft_r2c_1d(FURNACE_FFT_SIZE,fft_->inBuf,fft_->outBuf,FFTW_ESTIMATE);
fft_->planI=fftw_plan_dft_c2r_1d(FURNACE_FFT_SIZE,fft_->outBuf,fft_->corrBuf,FFTW_ESTIMATE);
if (fft_->plan==NULL) {
logE("failed to create plan!");
} else if (fft_->planI==NULL) {
logE("failed to create inverse plan!");
} else if (fft_->inBuf==NULL || fft_->outBuf==NULL || fft_->corrBuf==NULL) {
logE("failed to create FFT buffers");
} else {
fft_->ready=true;
}
}
if (fft_->ready && e->isRunning()) {
fft_->windowSize=chanOscWindowSize;
fft_->waveCorr=chanOscWaveCorr;
chanOscWorkPool->push([](void* fft_v) {
ChanOscStatus* fft=(ChanOscStatus*)fft_v;
DivDispatchOscBuffer* buf=fft->relatedBuf;
// the STRATEGY
// 1. FFT of windowed signal
// 2. inverse FFT of auto-correlation
// 3. find size of one period
// 4. DFT of the fundamental of ONE PERIOD
// 5. now we can get phase information
//
// I have a feeling this could be simplified to two FFTs or even one...
// if you know how, please tell me
// initialization
double phase=0.0;
int displaySize=(float)(buf->rate)*(fft->windowSize/1000.0f);
fft->loudEnough=false;
fft->needle=buf->needle;
// first FFT
for (int j=0; j<FURNACE_FFT_SIZE; j++) {
fft->inBuf[j]=(double)buf->data[(unsigned short)(fft->needle-displaySize*2+((j*displaySize*2)/(FURNACE_FFT_SIZE)))]/32768.0;
if (fft->inBuf[j]>0.001 || fft->inBuf[j]<-0.001) fft->loudEnough=true;
fft->inBuf[j]*=0.55-0.45*cos(M_PI*(double)j/(double)(FURNACE_FFT_SIZE>>1));
}
// only proceed if not quiet
if (fft->loudEnough) {
fftw_execute(fft->plan);
// auto-correlation and second FFT
for (int j=0; j<FURNACE_FFT_SIZE; j++) {
fft->outBuf[j][0]/=FURNACE_FFT_SIZE;
fft->outBuf[j][1]/=FURNACE_FFT_SIZE;
fft->outBuf[j][0]=fft->outBuf[j][0]*fft->outBuf[j][0]+fft->outBuf[j][1]*fft->outBuf[j][1];
fft->outBuf[j][1]=0;
}
fft->outBuf[0][0]=0;
fft->outBuf[0][1]=0;
fft->outBuf[1][0]=0;
fft->outBuf[1][1]=0;
fftw_execute(fft->planI);
// window
for (int j=0; j<(FURNACE_FFT_SIZE>>1); j++) {
fft->corrBuf[j]*=1.0-((double)j/(double)(FURNACE_FFT_SIZE<<1));
}
// find size of period
double waveLenCandL=DBL_MAX;
double waveLenCandH=DBL_MIN;
fft->waveLen=FURNACE_FFT_SIZE-1;
fft->waveLenBottom=0;
fft->waveLenTop=0;
// find lowest point
for (int j=(FURNACE_FFT_SIZE>>2); j>2; j--) {
if (fft->corrBuf[j]<waveLenCandL) {
waveLenCandL=fft->corrBuf[j];
fft->waveLenBottom=j;
}
}
// find highest point
for (int j=(FURNACE_FFT_SIZE>>1)-1; j>fft->waveLenBottom; j--) {
if (fft->corrBuf[j]>waveLenCandH) {
waveLenCandH=fft->corrBuf[j];
fft->waveLen=j;
}
}
fft->waveLenTop=fft->waveLen;
// did we find the period size?
if (fft->waveLen<(FURNACE_FFT_SIZE-32)) {
// we got pitch
fft->pitch=pow(1.0-(fft->waveLen/(double)(FURNACE_FFT_SIZE>>1)),4.0);
fft->waveLen*=(double)displaySize*2.0/(double)FURNACE_FFT_SIZE;
// DFT of one period (x_1)
double dft[2];
dft[0]=0.0;
dft[1]=0.0;
for (int j=fft->needle-1-(displaySize>>1)-(int)fft->waveLen, k=0; k<fft->waveLen; j++, k++) {
double one=((double)buf->data[j&0xffff]/32768.0);
double two=(double)k*(-2.0*M_PI)/fft->waveLen;
dft[0]+=one*cos(two);
dft[1]+=one*sin(two);
}
// calculate and lock into phase
phase=(0.5+(atan2(dft[1],dft[0])/(2.0*M_PI)));
if (fft->waveCorr) {
fft->needle-=(phase+(fft->phaseOff*2))*fft->waveLen;
}
}
}
fft->needle-=displaySize;
},fft_);
}
}
}
chanOscWorkPool->wait();
// 0: none
// 1: sqrt(chans)
// 2: sqrt(chans+1)
// 3: sqrt(chans)+1
switch (chanOscAutoColsType) {
case 1:
chanOscCols=sqrt(oscChans.size());
break;
case 2:
chanOscCols=sqrt(oscChans.size()+1);
break;
case 3:
chanOscCols=sqrt(oscChans.size())+1;
break;
}
if (chanOscCols<1) chanOscCols=1;
if (chanOscCols>64) chanOscCols=64;
int rows=(oscBufs.size()+(chanOscCols-1))/chanOscCols;
// render
for (size_t i=0; i<oscBufs.size(); i++) {
if (i%chanOscCols==0) ImGui::TableNextRow();
ImGui::TableNextColumn();
@ -351,20 +587,6 @@ void FurnaceGUI::drawChanOsc() {
ImVec2 size=ImGui::GetContentRegionAvail();
size.y=availY/rows;
if (centerSettingReset) {
buf->readNeedle=buf->needle;
}
// check FFT status existence
if (fft->plan==NULL) {
logD("creating FFT plan for channel %d",ch);
fft->inBuf=(double*)fftw_malloc(FURNACE_FFT_SIZE*sizeof(double));
fft->outBuf=(fftw_complex*)fftw_malloc(FURNACE_FFT_SIZE*sizeof(fftw_complex));
fft->plan=fftw_plan_dft_r2c_1d(FURNACE_FFT_SIZE,fft->inBuf,fft->outBuf,FFTW_ESTIMATE);
}
int displaySize=(float)(buf->rate)*(chanOscWindowSize/1000.0f);
ImVec2 minArea=window->DC.CursorPos;
ImVec2 maxArea=ImVec2(
minArea.x+size.x,
@ -379,67 +601,105 @@ void FurnaceGUI::drawChanOsc() {
int precision=inRect.Max.x-inRect.Min.x;
if (precision<1) precision=1;
if (precision>512) precision=512;
if (precision>1024) precision=1024;
ImGui::ItemSize(size,style.FramePadding.y);
if (ImGui::ItemAdd(rect,ImGui::GetID("chOscDisplay"))) {
if (!e->isRunning()) {
for (unsigned short i=0; i<precision; i++) {
float x=(float)i/(float)precision;
waveform[i]=ImLerp(inRect.Min,inRect.Max,ImVec2(x,0.5f));
if (rend->supportsDrawOsc() && settings.shaderOsc) {
memset(fft->oscTex,0,2048*sizeof(float));
} else {
for (unsigned short j=0; j<precision; j++) {
float x=(float)j/(float)precision;
waveform[j]=ImLerp(inRect.Min,inRect.Max,ImVec2(x,0.5f));
}
}
} else {
int displaySize=(float)(buf->rate)*(chanOscWindowSize/1000.0f);
float minLevel=1.0f;
float maxLevel=-1.0f;
float dcOff=0.0f;
unsigned short needlePos=buf->needle;
for (int i=0; i<FURNACE_FFT_SIZE; i++) {
fft->inBuf[i]=(double)buf->data[(unsigned short)(needlePos-displaySize*2+((i*displaySize*2)/FURNACE_FFT_SIZE))]/32768.0;
}
fftw_execute(fft->plan);
// find origin frequency
int point=1;
double candAmp=0.0;
for (unsigned short i=1; i<512; i++) {
fftw_complex& f=fft->outBuf[i];
// AMPLITUDE
double amp=sqrt(pow(f[0],2.0)+pow(f[1],2.0))/pow((double)i,0.8);
if (amp>candAmp) {
point=i;
candAmp=amp;
if (debugFFT) {
// FFT debug code!
double maxavg=0.0;
for (unsigned short j=0; j<(FURNACE_FFT_SIZE>>1); j++) {
if (fabs(fft->corrBuf[j]>maxavg)) {
maxavg=fabs(fft->corrBuf[j]);
}
}
}
if (maxavg>0.0000001) maxavg=0.5/maxavg;
// PHASE
fftw_complex& candPoint=fft->outBuf[point];
double phase=((double)(displaySize*2)/(double)point)*(0.5+(atan2(candPoint[1],candPoint[0])/(M_PI*2)));
if (rend->supportsDrawOsc() && settings.shaderOsc) {
for (unsigned short j=0; j<precision && j<2048; j++) {
float y;
if (j>=precision/2) {
y=fft->inBuf[((j-(precision/2))*FURNACE_FFT_SIZE*2)/(precision)];
} else {
y=fft->corrBuf[(j*FURNACE_FFT_SIZE)/precision]*maxavg;
}
fft->oscTex[j]=y*2.0;
}
} else {
for (unsigned short j=0; j<precision; j++) {
float x=(float)j/(float)precision;
float y;
if (j>=precision/2) {
y=fft->inBuf[((j-(precision/2))*FURNACE_FFT_SIZE*2)/(precision)];
} else {
y=fft->corrBuf[(j*FURNACE_FFT_SIZE)/precision]*maxavg;
}
waveform[j]=ImLerp(inRect.Min,inRect.Max,ImVec2(x,0.5f-y));
}
}
if (fft->loudEnough) {
String cPhase=fmt::sprintf("\n%.1f (b: %d t: %d)",fft->waveLen,fft->waveLenBottom,fft->waveLenTop);
dl->AddText(inRect.Min,0xffffffff,cPhase.c_str());
if (chanOscWaveCorr) {
needlePos-=phase;
}
chanOscPitch[ch]=(float)point/32.0f;
/*
String cPhase=fmt::sprintf("%d cphase: %f vol: %f",point,phase,chanOscVol[ch]);
dl->AddText(inRect.Min,0xffffffff,cPhase.c_str());
*/
dl->AddLine(
ImLerp(inRect.Min,inRect.Max,ImVec2((double)fft->waveLenBottom/(double)FURNACE_FFT_SIZE,0.0)),
ImLerp(inRect.Min,inRect.Max,ImVec2((double)fft->waveLenBottom/(double)FURNACE_FFT_SIZE,1.0)),
0xffffff00
);
dl->AddLine(
ImLerp(inRect.Min,inRect.Max,ImVec2((double)fft->waveLenTop/(double)FURNACE_FFT_SIZE,0.0)),
ImLerp(inRect.Min,inRect.Max,ImVec2((double)fft->waveLenTop/(double)FURNACE_FFT_SIZE,1.0)),
0xff00ff00
);
} else {
if (debugFFT) {
dl->AddText(inRect.Min,0xffffffff,"\nquiet");
}
}
} else {
for (unsigned short j=0; j<precision; j++) {
float y=(float)buf->data[(unsigned short)(fft->needle+(j*displaySize/precision))]/32768.0f;
if (minLevel>y) minLevel=y;
if (maxLevel<y) maxLevel=y;
}
dcOff=(minLevel+maxLevel)*0.5f;
needlePos-=displaySize;
for (unsigned short i=0; i<precision; i++) {
float y=(float)buf->data[(unsigned short)(needlePos+(i*displaySize/precision))]/32768.0f;
if (minLevel>y) minLevel=y;
if (maxLevel<y) maxLevel=y;
}
dcOff=(minLevel+maxLevel)*0.5f;
for (unsigned short i=0; i<precision; i++) {
float x=(float)i/(float)precision;
float y=(float)buf->data[(unsigned short)(needlePos+(i*displaySize/precision))]/32768.0f;
y-=dcOff;
if (y<-0.5f) y=-0.5f;
if (y>0.5f) y=0.5f;
y*=chanOscAmplify;
waveform[i]=ImLerp(inRect.Min,inRect.Max,ImVec2(x,0.5f-y));
if (rend->supportsDrawOsc() && settings.shaderOsc) {
for (unsigned short j=0; j<precision; j++) {
float y=(float)buf->data[(unsigned short)(fft->needle+(j*displaySize/precision))]/32768.0f;
y-=dcOff;
if (y<-0.5f) y=-0.5f;
if (y>0.5f) y=0.5f;
y*=chanOscAmplify*2.0f;
fft->oscTex[j]=y;
}
} else {
for (unsigned short j=0; j<precision; j++) {
float x=(float)j/(float)precision;
float y=(float)buf->data[(unsigned short)(fft->needle+(j*displaySize/precision))]/32768.0f;
y-=dcOff;
if (y<-0.5f) y=-0.5f;
if (y>0.5f) y=0.5f;
y*=chanOscAmplify;
waveform[j]=ImLerp(inRect.Min,inRect.Max,ImVec2(x,0.5f-y));
}
}
}
}
ImU32 color=ImGui::GetColorU32(chanOscColor);
@ -452,17 +712,35 @@ void FurnaceGUI::drawChanOsc() {
color=chanOscGrad.get(xVal,1.0f-yVal);
}
ImGui::PushClipRect(inRect.Min,inRect.Max,false);
dl->AddPolyline(waveform,precision,color,ImDrawFlags_None,dpiScale);
if (rend->supportsDrawOsc() && settings.shaderOsc) {
fft->drawOp.gui=this;
fft->drawOp.data=fft->oscTex;
fft->drawOp.len=precision;
fft->drawOp.pos0=inRect.Min;
fft->drawOp.pos1=inRect.Max;
fft->drawOp.color=ImGui::ColorConvertU32ToFloat4(color);
fft->drawOp.lineSize=dpiScale*chanOscLineSize;
dl->AddCallback(_drawOsc,&fft->drawOp);
dl->AddCallback(ImDrawCallback_ResetRenderState,NULL);
} else {
//ImGui::PushClipRect(inRect.Min,inRect.Max,false);
//ImDrawListFlags prevFlags=dl->Flags;
//dl->Flags&=~(ImDrawListFlags_AntiAliasedLines|ImDrawListFlags_AntiAliasedLinesUseTex);
dl->AddPolyline(waveform,precision,color,ImDrawFlags_None,dpiScale*chanOscLineSize);
//dl->Flags=prevFlags;
//ImGui::PopClipRect();
}
//ImGui::PushClipRect(inRect.Min,inRect.Max,false);
if (!chanOscTextFormat.empty()) {
String text;
bool inFormat=false;
for (char i: chanOscTextFormat) {
for (char j: chanOscTextFormat) {
if (inFormat) {
switch (i) {
switch (j) {
case 'c':
text+=e->getChannelName(ch);
break;
@ -502,38 +780,68 @@ void FurnaceGUI::drawChanOsc() {
text+=e->getSystemName(e->sysOfChan[ch]);
break;
}
case 'p': {
text+=FurnaceGUI::getSystemPartNumber(e->sysOfChan[ch],e->song.systemFlags[e->dispatchOfChan[ch]]);
break;
}
case 'S': {
text+=fmt::sprintf("%d",e->dispatchOfChan[ch]);
break;
}
case 'v':
case 'v': {
DivChannelState* chanState=e->getChanState(ch);
if (chanState==NULL) break;
text+=fmt::sprintf("%d",chanState->volume>>8);
break;
case 'V':
}
case 'V': {
DivChannelState* chanState=e->getChanState(ch);
if (chanState==NULL) break;
int volMax=chanState->volMax>>8;
if (volMax<1) volMax=1;
text+=fmt::sprintf("%d%%",(chanState->volume>>8)/volMax);
break;
case 'b':
}
case 'b': {
DivChannelState* chanState=e->getChanState(ch);
if (chanState==NULL) break;
text+=fmt::sprintf("%.2X",chanState->volume>>8);
break;
}
case 'n': {
DivChannelState* chanState=e->getChanState(ch);
if (chanState==NULL || !(chanState->keyOn)) break;
short tempNote=chanState->note; //all of this conversion is necessary because notes 100-102 are special chars
short noteMod=tempNote%12+12; //also note 0 is a BUG, hence +12 on the note and -1 on the octave
short oct=tempNote/12-1;
text+=fmt::sprintf("%s",noteName(noteMod,oct));
break;
}
case 'l': {
text+='\n';
break;
}
case '%':
text+='%';
break;
default:
text+='%';
text+=i;
text+=j;
break;
}
inFormat=false;
} else {
if (i=='%') {
if (j=='%') {
inFormat=true;
} else {
text+=i;
text+=j;
}
}
}
dl->AddText(ImLerp(inRect.Min,inRect.Max,ImVec2(0.0f,0.0f)),ImGui::GetColorU32(chanOscTextColor),text.c_str());
}
ImGui::PopClipRect();
//ImGui::PopClipRect();
}
}
}
@ -542,6 +850,10 @@ void FurnaceGUI::drawChanOsc() {
if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) {
chanOscOptions=!chanOscOptions;
}
if (ImGui::IsItemHovered() && CHECK_LONG_HOLD) {
NOTIFY_LONG_HOLD;
chanOscOptions=!chanOscOptions;
}
}
ImGui::PopStyleVar();
}

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2023 tildearrow and contributors
* Copyright (C) 2021-2024 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -38,21 +38,39 @@ void FurnaceGUI::drawChannels() {
//ImGui::SetNextWindowSizeConstraints(ImVec2(440.0f*dpiScale,400.0f*dpiScale),ImVec2(canvasW,canvasH));
}
if (ImGui::Begin("Channels",&channelsOpen,globalWinFlags)) {
if (ImGui::BeginTable("ChannelList",3)) {
if (ImGui::BeginTable("ChannelList",5)) {
ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthFixed,0.0);
ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthFixed,0.0);
ImGui::TableSetupColumn("c2",ImGuiTableColumnFlags_WidthStretch,0.0);
ImGui::TableSetupColumn("c3",ImGuiTableColumnFlags_WidthFixed,48.0f*dpiScale);
ImGui::TableSetupColumn("c2",ImGuiTableColumnFlags_WidthFixed,0.0);
ImGui::TableSetupColumn("c3",ImGuiTableColumnFlags_WidthStretch,0.0);
ImGui::TableSetupColumn("c4",ImGuiTableColumnFlags_WidthFixed,48.0f*dpiScale);
ImGui::TableNextRow(ImGuiTableRowFlags_Headers);
ImGui::TableNextColumn();
ImGui::Text("Visible");
ImGui::Text("Pat");
ImGui::TableNextColumn();
ImGui::Text("Osc");
ImGui::TableNextColumn();
ImGui::Text("Swap");
ImGui::TableNextColumn();
ImGui::Text("Name");
for (int i=0; i<e->getTotalChannelCount(); i++) {
ImGui::PushID(i);
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Checkbox("##Visible",&e->curSubSong->chanShow[i]);
ImGui::SameLine();
if (ImGui::Checkbox("##VisiblePat",&e->curSubSong->chanShow[i])) {
MARK_MODIFIED;
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Show in pattern");
}
ImGui::TableNextColumn();
if (ImGui::Checkbox("##VisibleChanOsc",&e->curSubSong->chanShowChanOsc[i])) {
MARK_MODIFIED;
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Show in per-channel oscilloscope");
}
ImGui::TableNextColumn();
if (ImGui::Button(ICON_FA_ARROWS)) {
}
if (ImGui::BeginDragDropSource()) {
@ -69,6 +87,7 @@ void FurnaceGUI::drawChannels() {
if (dragItem->IsDataType("FUR_CHAN")) {
if (chanToMove!=i && chanToMove>=0) {
e->swapChannelsP(chanToMove,i);
MARK_MODIFIED;
}
chanToMove=-1;
}
@ -77,10 +96,14 @@ void FurnaceGUI::drawChannels() {
}
ImGui::TableNextColumn();
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
ImGui::InputTextWithHint("##ChanName",e->getChannelName(i),&e->curSubSong->chanName[i]);
if (ImGui::InputTextWithHint("##ChanName",e->getChannelName(i),&e->curSubSong->chanName[i])) {
MARK_MODIFIED;
}
ImGui::TableNextColumn();
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
ImGui::InputTextWithHint("##ChanShortName",e->getChannelShortName(i),&e->curSubSong->chanShortName[i]);
if (ImGui::InputTextWithHint("##ChanShortName",e->getChannelShortName(i),&e->curSubSong->chanShortName[i])) {
MARK_MODIFIED;
}
ImGui::PopID();
}
ImGui::EndTable();

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2023 tildearrow and contributors
* Copyright (C) 2021-2024 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -29,13 +29,13 @@ void FurnaceGUI::drawClock() {
}
if (!clockOpen) return;
if (ImGui::Begin("Clock",&clockOpen,globalWinFlags)) {
int row=e->getRow();
int row=oldRow;
int elapsedBars=e->getElapsedBars();
int elapsedBeats=e->getElapsedBeats();
bool playing=e->isPlaying();
if (clockShowRow) {
ImGui::PushFont(bigFont);
ImGui::Text("%.3d:%.3d",e->getOrder(),row);
ImGui::Text("%.3d:%.3d",playOrder,row);
ImGui::PopFont();
}
if (clockShowBeat) {

247
src/gui/commandPalette.cpp Normal file
View file

@ -0,0 +1,247 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2024 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "gui.h"
#include "guiConst.h"
#include "commandPalette.h"
#include "misc/cpp/imgui_stdlib.h"
#include <fmt/printf.h>
#include <algorithm>
#include <ctype.h>
#include "../ta-log.h"
static inline bool matchFuzzy(const char* haystack, const char* needle) {
size_t h_i=0; // haystack idx
size_t n_i=0; // needle idx
while (needle[n_i]!='\0') {
for (; std::tolower(haystack[h_i])!=std::tolower(needle[n_i]); h_i++) {
if (haystack[h_i]=='\0')
return false;
}
n_i+=1;
}
return true;
}
void FurnaceGUI::drawPalette() {
bool accepted=false;
if (paletteFirstFrame)
ImGui::SetKeyboardFocusHere();
int width=ImGui::GetContentRegionAvail().x;
ImGui::SetNextItemWidth(width);
const char* hint="Search...";
switch (curPaletteType) {
case CMDPAL_TYPE_RECENT:
hint="Search recent files...";
break;
case CMDPAL_TYPE_INSTRUMENTS:
hint="Search instruments...";
break;
case CMDPAL_TYPE_SAMPLES:
hint="Search samples...";
break;
case CMDPAL_TYPE_INSTRUMENT_CHANGE:
hint="Search instruments (to change to)...";
break;
case CMDPAL_TYPE_ADD_CHIP:
hint="Search chip (to add)...";
break;
}
if (ImGui::InputTextWithHint("##CommandPaletteSearch",hint,&paletteQuery) || paletteFirstFrame) {
paletteSearchResults.clear();
switch (curPaletteType) {
case CMDPAL_TYPE_MAIN:
for (int i=0; i<GUI_ACTION_MAX; i++) {
if (guiActions[i].defaultBind==-1) continue;
if (matchFuzzy(guiActions[i].friendlyName,paletteQuery.c_str())) {
paletteSearchResults.push_back(i);
}
}
break;
case CMDPAL_TYPE_RECENT:
for (int i=0; i<(int)recentFile.size(); i++) {
if (matchFuzzy(recentFile[i].c_str(),paletteQuery.c_str())) {
paletteSearchResults.push_back(i);
}
}
break;
case CMDPAL_TYPE_INSTRUMENTS:
case CMDPAL_TYPE_INSTRUMENT_CHANGE:
if (matchFuzzy("- None -",paletteQuery.c_str())) {
paletteSearchResults.push_back(0);
}
for (int i=0; i<e->song.insLen; i++) {
String s=fmt::sprintf("%02X: %s", i, e->song.ins[i]->name.c_str());
if (matchFuzzy(s.c_str(),paletteQuery.c_str())) {
paletteSearchResults.push_back(i+1); // because over here ins=0 is 'None'
}
}
break;
case CMDPAL_TYPE_SAMPLES:
for (int i=0; i<e->song.sampleLen; i++) {
if (matchFuzzy(e->song.sample[i]->name.c_str(),paletteQuery.c_str())) {
paletteSearchResults.push_back(i);
}
}
break;
case CMDPAL_TYPE_ADD_CHIP:
for (int i=0; availableSystems[i]; i++) {
int ds=availableSystems[i];
const char* sysname=getSystemName((DivSystem)ds);
if (matchFuzzy(sysname,paletteQuery.c_str())) {
paletteSearchResults.push_back(ds);
}
}
break;
default:
logE("invalid command palette type");
ImGui::CloseCurrentPopup();
break;
};
}
ImVec2 avail=ImGui::GetContentRegionAvail();
avail.y-=ImGui::GetFrameHeightWithSpacing();
if (ImGui::BeginChild("CommandPaletteList",avail,false,0)) {
bool navigated=false;
if (ImGui::IsKeyPressed(ImGuiKey_UpArrow) && curPaletteChoice>0) {
curPaletteChoice-=1;
navigated=true;
}
if (ImGui::IsKeyPressed(ImGuiKey_DownArrow)) {
curPaletteChoice+=1;
navigated=true;
}
if (paletteSearchResults.size()>0 && curPaletteChoice<0) {
curPaletteChoice=0;
navigated=true;
}
if (curPaletteChoice>=(int)paletteSearchResults.size()) {
curPaletteChoice=paletteSearchResults.size()-1;
navigated=true;
}
for (int i=0; i<(int)paletteSearchResults.size(); i++) {
bool current=(i==curPaletteChoice);
int id=paletteSearchResults[i];
String s="???";
switch (curPaletteType) {
case CMDPAL_TYPE_MAIN:
s=guiActions[id].friendlyName;
break;
case CMDPAL_TYPE_RECENT:
s=recentFile[id].c_str();
break;
case CMDPAL_TYPE_INSTRUMENTS:
case CMDPAL_TYPE_INSTRUMENT_CHANGE:
if (id==0) {
s="- None -";
} else {
s=fmt::sprintf("%02X: %s", id-1, e->song.ins[id-1]->name.c_str());
}
break;
case CMDPAL_TYPE_SAMPLES:
s=e->song.sample[id]->name.c_str();
break;
case CMDPAL_TYPE_ADD_CHIP:
s=getSystemName((DivSystem)id);
break;
default:
logE("invalid command palette type");
break;
};
if (ImGui::Selectable(s.c_str(),current)) {
curPaletteChoice=i;
accepted=true;
}
if ((navigated || paletteFirstFrame) && current) ImGui::SetScrollHereY();
}
}
ImGui::EndChild();
if (!accepted) {
if (curPaletteChoice>=(int)paletteSearchResults.size()) {
curPaletteChoice=paletteSearchResults.size()-1;
}
accepted=ImGui::IsKeyPressed(ImGuiKey_Enter);
}
if (ImGui::Button("Cancel") || ImGui::IsKeyPressed(ImGuiKey_Escape)) {
ImGui::CloseCurrentPopup();
}
// do not move this to after the resetPalette() calls!
// if they are called before and we're jumping from one palette to the next, the paletteFirstFrame won't be true at the start and the setup will not happen.
paletteFirstFrame=false;
if (accepted) {
if (paletteSearchResults.size()>0) {
int i=paletteSearchResults[curPaletteChoice];
switch (curPaletteType) {
case CMDPAL_TYPE_MAIN:
doAction(i);
break;
case CMDPAL_TYPE_RECENT:
openRecentFile(recentFile[i]);
break;
case CMDPAL_TYPE_INSTRUMENTS:
curIns=i-1;
break;
case CMDPAL_TYPE_SAMPLES:
curSample=i;
break;
case CMDPAL_TYPE_INSTRUMENT_CHANGE:
doChangeIns(i-1);
break;
case CMDPAL_TYPE_ADD_CHIP:
if (i!=DIV_SYSTEM_NULL) {
if (!e->addSystem((DivSystem)i)) {
showError("cannot add chip! ("+e->getLastError()+")");
} else {
MARK_MODIFIED;
}
ImGui::CloseCurrentPopup();
if (e->song.autoSystem) {
autoDetectSystem();
}
updateWindowTitle();
}
break;
default:
logE("invalid command palette type");
break;
};
}
ImGui::CloseCurrentPopup();
}
}

31
src/gui/commandPalette.h Normal file
View file

@ -0,0 +1,31 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2024 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
enum CommandPaletteType {
CMDPAL_TYPE_MAIN = 0,
CMDPAL_TYPE_RECENT,
CMDPAL_TYPE_INSTRUMENTS,
CMDPAL_TYPE_SAMPLES,
CMDPAL_TYPE_INSTRUMENT_CHANGE,
CMDPAL_TYPE_ADD_CHIP,
// a menu to select wavetables is beyond scope (they can't be put as strings)
// TODO: are there more?
CMDPAL_TYPE_MAX,
};

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2023 tildearrow and contributors
* Copyright (C) 2021-2024 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -57,7 +57,7 @@ void FurnaceGUI::drawCompatFlags() {
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("delay arpeggio by one tick on every new note.");
}
ImGui::Checkbox("Broken DAC mode",&e->song.brokenDACMode);
ImGui::Checkbox("Disable DAC when sample ends",&e->song.brokenDACMode);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("when enabled, the DAC in YM2612 will be disabled if there isn't any sample playing.");
}
@ -184,6 +184,18 @@ void FurnaceGUI::drawCompatFlags() {
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("behavior changed in 0.6pre5");
}
ImGui::Checkbox("Pre-note does not take effects into consideration",&e->song.preNoteNoEffect);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("behavior changed in 0.6pre9");
}
ImGui::Checkbox("Disable new NES DPCM features",&e->song.oldDPCM);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("behavior changed in 0.6.1");
}
ImGui::Checkbox("Legacy technical ALWAYS_SET_VOLUME behavior",&e->song.oldAlwaysSetVolume);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("behavior changed in 0.6.1\nthis flag will be removed if I find out that none of the songs break after disabling it.");
}
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem(".mod import")) {
@ -199,17 +211,22 @@ void FurnaceGUI::drawCompatFlags() {
}
if (ImGui::BeginTabItem("Pitch/Playback")) {
ImGui::Text("Pitch linearity:");
ImGui::Indent();
if (ImGui::RadioButton("None",e->song.linearPitch==0)) {
e->song.linearPitch=0;
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("like ProTracker/FamiTracker");
}
if (ImGui::RadioButton("Partial (only 04xy/E5xx)",e->song.linearPitch==1)) {
e->song.linearPitch=1;
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("like DefleMask");
if (e->song.linearPitch==1) {
pushWarningColor(true);
if (ImGui::RadioButton("Partial (only 04xy/E5xx)",e->song.linearPitch==1)) {
e->song.linearPitch=1;
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("like DefleMask\n\nthis pitch linearity mode is deprecated due to:\n- excessive complexity\n- lack of possible optimization\n\nit is recommended to change it now because I will remove this option in the future!");
}
popWarningColor();
}
if (ImGui::RadioButton("Full",e->song.linearPitch==2)) {
e->song.linearPitch=2;
@ -217,6 +234,7 @@ void FurnaceGUI::drawCompatFlags() {
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("like Impulse Tracker");
}
ImGui::Unindent();
if (e->song.linearPitch==2) {
ImGui::SameLine();
@ -228,6 +246,7 @@ void FurnaceGUI::drawCompatFlags() {
}
ImGui::Text("Loop modality:");
ImGui::Indent();
if (ImGui::RadioButton("Reset channels",e->song.loopModality==0)) {
e->song.loopModality=0;
}
@ -246,8 +265,10 @@ void FurnaceGUI::drawCompatFlags() {
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("select to not reset channels on loop.");
}
ImGui::Unindent();
ImGui::Text("Cut/delay effect policy:");
ImGui::Indent();
if (ImGui::RadioButton("Strict",e->song.delayBehavior==0)) {
e->song.delayBehavior=0;
}
@ -266,8 +287,10 @@ void FurnaceGUI::drawCompatFlags() {
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("no checks");
}
ImGui::Unindent();
ImGui::Text("Simultaneous jump (0B+0D) treatment:");
ImGui::Indent();
if (ImGui::RadioButton("Normal",e->song.jumpTreatment==0)) {
e->song.jumpTreatment=0;
}
@ -286,6 +309,7 @@ void FurnaceGUI::drawCompatFlags() {
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("only accept 0Dxx");
}
ImGui::Unindent();
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("Other")) {
@ -306,12 +330,20 @@ void FurnaceGUI::drawCompatFlags() {
}
ImGui::Checkbox("Continuous vibrato",&e->song.continuousVibrato);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("when enabled, vibrato will not be reset on a new note.");
ImGui::SetTooltip("when enabled, vibrato phase/position will not be reset on a new note.");
}
InvCheckbox("Pitch macro is not linear",&e->song.pitchMacroIsLinear);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("when enabled, the pitch macro of an instrument is in frequency/period space.");
}
ImGui::Checkbox("Reset arpeggio effect position on new note",&e->song.resetArpPhaseOnNewNote);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("when enabled, arpeggio effect (00xy) position is reset on a new note.");
}
ImGui::Checkbox("Volume scaling rounds up",&e->song.ceilVolumeScaling);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("when enabled, volume macros round up when applied\nthis prevents volume scaling from causing vol=0, which is silent on some chips\n\nineffective on logarithmic channels");
}
ImGui::EndTabItem();
}
ImGui::EndTabBar();

371
src/gui/csPlayer.cpp Normal file
View file

@ -0,0 +1,371 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2024 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "gui.h"
#include <fmt/printf.h>
#include "imgui.h"
#include "guiConst.h"
// TODO: memory safety
String disasmCmd(unsigned char* buf, size_t bufLen, unsigned int addr) {
if (addr>=bufLen) return "???";
if (buf[addr]<0xb4) {
return fmt::sprintf("note %s",noteNames[buf[addr]]);
} else switch (buf[addr]) {
case 0xb4:
return "note null";
break;
case 0xb5:
return "off";
break;
case 0xb6:
return "offrel";
break;
case 0xb7:
return "mrel";
break;
case 0xb8:
return fmt::sprintf("ins $%.2x",(int)buf[addr+1]);
break;
case 0xbe:
return fmt::sprintf("pan $%x, $%x",(int)buf[addr+1],(int)buf[addr+2]);
break;
case 0xc0:
return fmt::sprintf("preporta $%.2x",(int)buf[addr+1]);
break;
case 0xc2:
return fmt::sprintf("vib %d, %d",(int)buf[addr+1],(int)buf[addr+2]);
break;
case 0xc3:
return fmt::sprintf("vibrange %d",(int)buf[addr+1]);
break;
case 0xc4:
return fmt::sprintf("vibshape %d",(int)buf[addr+1]);
break;
case 0xc5:
return fmt::sprintf("pitch $%.2x",(int)buf[addr+1]);
break;
case 0xc6:
return fmt::sprintf("arp %d, %d",(int)buf[addr+1],(int)buf[addr+2]);
break;
case 0xc7:
return fmt::sprintf("vol $%.2x",(int)buf[addr+1]);
break;
case 0xc8:
return fmt::sprintf("volslide %d",(int)((short)(buf[addr+1]|(buf[addr+2]<<8))));
break;
case 0xc9:
return fmt::sprintf("porta %d, %d",(int)buf[addr+1],(int)buf[addr+2]);
break;
case 0xca:
return fmt::sprintf("legato %d",(int)buf[addr+1]);
break;
case 0xe0: case 0xe1: case 0xe2: case 0xe3:
case 0xe4: case 0xe5: case 0xe6: case 0xe7:
case 0xe8: case 0xe9: case 0xea: case 0xeb:
case 0xec: case 0xed: case 0xee: case 0xef:
return fmt::sprintf("qwait (%d)",(int)(buf[addr]-0xe0));
break;
case 0xfc:
return fmt::sprintf("waits %d",(int)(buf[addr+1]|(buf[addr+2]<<8)));
break;
case 0xfd:
return fmt::sprintf("waitc %d",(int)buf[addr+1]);
break;
case 0xfe:
return "wait 1";
break;
case 0xff:
return "stop";
break;
default:
return "ill";
break;
}
return "TODO";
}
void FurnaceGUI::drawCSPlayer() {
if (nextWindow==GUI_WINDOW_CS_PLAYER) {
csPlayerOpen=true;
ImGui::SetNextWindowFocus();
nextWindow=GUI_WINDOW_NOTHING;
}
if (!csPlayerOpen) return;
if (ImGui::Begin("Command Stream Player",&csPlayerOpen,globalWinFlags)) {
if (ImGui::Button("Load")) {
openFileDialog(GUI_FILE_CMDSTREAM_OPEN);
}
ImGui::SameLine();
if (ImGui::Button("Kill")) {
if (!e->killStream()) {
showError("Kikai wa mou shindeiru!");
}
}
ImGui::SameLine();
if (ImGui::Button("Burn Current Song")) {
SafeWriter* w=e->saveCommand();
if (w!=NULL) {
if (!e->playStream(w->getFinalBuf(),w->size())) {
showError(e->getLastError());
w->finish();
delete w;
} else {
w->disown();
delete w;
}
}
}
DivCSPlayer* cs=e->getStreamPlayer();
if (cs) {
if (ImGui::BeginTabBar("CSOptions")) {
int chans=e->getTotalChannelCount();
if (ImGui::BeginTabItem("Status")) {
if (ImGui::BeginTable("CSStat",12,ImGuiTableFlags_SizingFixedSame|ImGuiTableFlags_ScrollX|ImGuiTableFlags_Borders)) {
ImGui::TableSetupScrollFreeze(1,1);
ImGui::TableNextRow(ImGuiTableRowFlags_Headers);
ImGui::TableNextColumn();
ImGui::Text("channel");
ImGui::TableNextColumn();
ImGui::Text("start");
ImGui::TableNextColumn();
ImGui::Text("PC");
ImGui::TableNextColumn();
ImGui::Text("wait");
ImGui::TableNextColumn();
ImGui::Text("SP");
ImGui::TableNextColumn();
ImGui::Text("note");
ImGui::TableNextColumn();
ImGui::Text("pitch");
ImGui::TableNextColumn();
ImGui::Text("vol");
ImGui::TableNextColumn();
ImGui::Text("vols");
ImGui::TableNextColumn();
ImGui::Text("vib");
ImGui::TableNextColumn();
ImGui::Text("porta");
ImGui::TableNextColumn();
ImGui::Text("arp");
for (int i=0; i<chans; i++) {
DivCSChannelState* state=cs->getChanState(i);
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Text("%d",i);
ImGui::TableNextColumn();
ImGui::Text("$%.4x",state->startPos);
ImGui::TableNextColumn();
ImGui::Text("$%.4x",state->readPos);
ImGui::TableNextColumn();
ImGui::Text("%d/%d",state->waitTicks,state->lastWaitLen);
ImGui::TableNextColumn();
ImGui::Text("%d",state->callStackPos);
ImGui::TableNextColumn();
ImGui::Text("%d",state->note);
ImGui::TableNextColumn();
ImGui::Text("%d",state->pitch);
ImGui::TableNextColumn();
ImGui::Text("$%.4X",state->volume);
ImGui::TableNextColumn();
ImGui::Text("%+d",state->volSpeed);
ImGui::TableNextColumn();
ImGui::Text("%d/%d (%d)",state->vibratoDepth,state->vibratoRate,state->vibratoPos);
ImGui::TableNextColumn();
ImGui::Text("-> %d (%d)",state->portaTarget,state->portaSpeed);
ImGui::TableNextColumn();
ImGui::Text("$%.2X",state->arp);
}
ImGui::EndTable();
}
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("Trace")) {
ImGui::PushFont(patFont);
if (ImGui::BeginTable("CSTrace",chans,ImGuiTableFlags_SizingFixedSame|ImGuiTableFlags_Borders|ImGuiTableFlags_ScrollX)) {
char tempID[32];
for (int i=0; i<chans; i++) {
snprintf(tempID,31,"c%d",i);
ImGui::TableSetupColumn(tempID,ImGuiTableColumnFlags_WidthFixed,200.0*dpiScale);
}
ImGui::TableNextRow(ImGuiTableRowFlags_Headers);
for (int i=0; i<chans; i++) {
DivCSChannelState* state=cs->getChanState(i);
ImGui::TableNextColumn();
ImGui::Text("%d: $%.4x",i,state->readPos);
}
ImGui::TableNextRow();
unsigned char* buf=cs->getData();
size_t bufSize=cs->getDataLen();
for (int i=0; i<chans; i++) {
DivCSChannelState* state=cs->getChanState(i);
ImGui::TableNextColumn();
int maxItems=(ImGui::GetContentRegionAvail().y/MAX(ImGui::GetTextLineHeightWithSpacing(),1.0f));
if (maxItems>=DIV_MAX_CSTRACE) maxItems=DIV_MAX_CSTRACE-1;
int tracePos=state->tracePos;
for (int j=(tracePos-maxItems)&(DIV_MAX_CSTRACE-1); j!=tracePos; j=(j+1)&(DIV_MAX_CSTRACE-1)) {
if (state->trace[j]==0) {
ImGui::TextUnformatted("...");
} else {
String dis=disasmCmd(buf,bufSize,state->trace[j]);
ImGui::Text("%.4x: %s",state->trace[j],dis.c_str());
}
}
}
ImGui::EndTable();
}
ImGui::PopFont();
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("Disassemble")) {
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("Hex")) {
ImGui::PushFont(patFont);
if (ImGui::BeginTable("CSHexPos",chans,ImGuiTableFlags_SizingStretchSame)) {
ImGui::TableNextRow();
for (int i=0; i<chans; i++) {
ImGui::TableNextColumn();
ImGui::Text("%d",i);
}
ImGui::TableNextRow();
for (int i=0; i<chans; i++) {
DivCSChannelState* state=cs->getChanState(i);
ImGui::TableNextColumn();
ImGui::Text("$%.4x",state->readPos);
}
ImGui::EndTable();
}
float oneCharSize=ImGui::CalcTextSize("A").x;
float threeCharSize=ImGui::CalcTextSize("AA").x;
float charViewSize=ImGui::CalcTextSize("0123456789ABCDEF").x;
float fiveCharSize=ImGui::CalcTextSize("AAAAA").x;
if (ImGui::BeginTable("CSHexView",19,ImGuiTableFlags_ScrollY)) {
char charView[17];
ImGui::TableSetupScrollFreeze(1,1);
ImGui::TableSetupColumn("addr",ImGuiTableColumnFlags_WidthFixed,fiveCharSize);
ImGui::TableSetupColumn("d0",ImGuiTableColumnFlags_WidthFixed,threeCharSize);
ImGui::TableSetupColumn("d1",ImGuiTableColumnFlags_WidthFixed,threeCharSize);
ImGui::TableSetupColumn("d2",ImGuiTableColumnFlags_WidthFixed,threeCharSize);
ImGui::TableSetupColumn("d3",ImGuiTableColumnFlags_WidthFixed,threeCharSize);
ImGui::TableSetupColumn("d4",ImGuiTableColumnFlags_WidthFixed,threeCharSize);
ImGui::TableSetupColumn("d5",ImGuiTableColumnFlags_WidthFixed,threeCharSize);
ImGui::TableSetupColumn("d6",ImGuiTableColumnFlags_WidthFixed,threeCharSize);
ImGui::TableSetupColumn("d7",ImGuiTableColumnFlags_WidthFixed,threeCharSize);
ImGui::TableSetupColumn("d8",ImGuiTableColumnFlags_WidthFixed,threeCharSize);
ImGui::TableSetupColumn("d9",ImGuiTableColumnFlags_WidthFixed,threeCharSize);
ImGui::TableSetupColumn("d10",ImGuiTableColumnFlags_WidthFixed,threeCharSize);
ImGui::TableSetupColumn("d11",ImGuiTableColumnFlags_WidthFixed,threeCharSize);
ImGui::TableSetupColumn("d12",ImGuiTableColumnFlags_WidthFixed,threeCharSize);
ImGui::TableSetupColumn("d13",ImGuiTableColumnFlags_WidthFixed,threeCharSize);
ImGui::TableSetupColumn("d14",ImGuiTableColumnFlags_WidthFixed,threeCharSize);
ImGui::TableSetupColumn("d15",ImGuiTableColumnFlags_WidthFixed,threeCharSize);
ImGui::TableSetupColumn("spacer",ImGuiTableColumnFlags_WidthFixed,oneCharSize);
ImGui::TableSetupColumn("char",ImGuiTableColumnFlags_WidthFixed,charViewSize);
// header
ImGui::TableNextRow(ImGuiTableRowFlags_Headers);
ImGui::TableNextColumn();
for (int i=0; i<16; i++) {
ImGui::TableNextColumn();
ImGui::Text("%X",i);
}
// content
unsigned char* buf=cs->getData();
size_t bufSize=cs->getDataLen();
csClipper.Begin((bufSize+15)>>4,ImGui::GetTextLineHeightWithSpacing());
while (csClipper.Step()) {
//std::vector<int> highlightsUnsorted;
std::vector<int> highlights;
int nextHighlight=-1;
int highlightPos=0;
for (int i=0; i<chans; i++) {
DivCSChannelState* state=cs->getChanState(i);
if ((int)state->readPos>=(csClipper.DisplayStart<<4) && (int)state->readPos<=(csClipper.DisplayEnd<<4)) {
highlights.push_back(state->readPos);
}
}
if (!highlights.empty()) nextHighlight=highlights[0];
for (int i=csClipper.DisplayStart; i<csClipper.DisplayEnd; i++) {
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg,ImGui::GetColorU32(ImGuiCol_TableHeaderBg));
ImGui::Text("%.4X",i<<4);
for (int j=0; j<16; j++) {
int pos=(i<<4)|j;
ImGui::TableNextColumn();
if (pos>=(int)bufSize) continue;
if (pos==nextHighlight) {
ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg,ImGui::GetColorU32(ImGuiCol_HeaderActive));
highlightPos++;
if (highlightPos>=(int)highlights.size()) {
nextHighlight=-1;
} else {
nextHighlight=highlights[highlightPos];
}
}
ImGui::Text("%.2X",buf[pos]);
}
ImGui::TableNextColumn();
ImGui::TableNextColumn();
for (int j=0; j<16; j++) {
int pos=(i<<4)|j;
if (pos>=(int)bufSize) {
charView[j]=' ';
} else if (buf[pos]>=0x20 && buf[pos]<=0x7e) {
charView[j]=buf[pos];
} else {
charView[j]='.';
}
}
charView[16]=0;
ImGui::TextUnformatted(charView);
}
}
csClipper.End();
ImGui::EndTable();
}
ImGui::PopFont();
ImGui::EndTabItem();
}
ImGui::EndTabBar();
}
}
}
if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_CS_PLAYER;
ImGui::End();
}

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2023 tildearrow and contributors
* Copyright (C) 2021-2024 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -271,9 +271,15 @@ void FurnaceGUI::moveCursor(int x, int y, bool select) {
if (cursor.y>=e->curSubSong->patLen) {
if (settings.wrapVertical!=0 && !select) {
cursor.y=0;
if (settings.wrapVertical==2) {
if ((!e->isPlaying() || !followPattern) && curOrder<(e->curSubSong->ordersLen-1)) {
setOrder(curOrder+1);
if (settings.wrapVertical>1) {
if (!e->isPlaying() || !followPattern) {
if (curOrder<(e->curSubSong->ordersLen-1)) {
setOrder(curOrder+1);
} else if (settings.wrapVertical==3) {
setOrder(0);
} else {
cursor.y=e->curSubSong->patLen-1;
}
} else {
cursor.y=e->curSubSong->patLen-1;
}
@ -289,9 +295,15 @@ void FurnaceGUI::moveCursor(int x, int y, bool select) {
if (cursor.y<0) {
if (settings.wrapVertical!=0 && !select) {
cursor.y=e->curSubSong->patLen-1;
if (settings.wrapVertical==2) {
if ((!e->isPlaying() || !followPattern) && curOrder>0) {
setOrder(curOrder-1);
if (settings.wrapVertical>1) {
if (!e->isPlaying() || !followPattern) {
if (curOrder>0) {
setOrder(curOrder-1);
} else if (settings.wrapVertical==3) {
setOrder(e->curSubSong->ordersLen-1);
} else {
cursor.y=0;
}
} else {
cursor.y=0;
}
@ -332,6 +344,10 @@ void FurnaceGUI::moveCursorPrevChannel(bool overflow) {
}
e->setMidiBaseChan(cursor.xCoarse);
int xFineMax=(e->curSubSong->chanCollapse[cursor.xCoarse]?(4-e->curSubSong->chanCollapse[cursor.xCoarse]):(3+e->curPat[cursor.xCoarse].effectCols*2));
if (cursor.xFine<0) cursor.xFine=0;
if (cursor.xFine>=xFineMax) cursor.xFine=xFineMax-1;
selStart=cursor;
selEnd=cursor;
demandScrollX=true;
@ -356,13 +372,19 @@ void FurnaceGUI::moveCursorNextChannel(bool overflow) {
}
e->setMidiBaseChan(cursor.xCoarse);
int xFineMax=(e->curSubSong->chanCollapse[cursor.xCoarse]?(4-e->curSubSong->chanCollapse[cursor.xCoarse]):(3+e->curPat[cursor.xCoarse].effectCols*2));
if (cursor.xFine<0) cursor.xFine=0;
if (cursor.xFine>=xFineMax) cursor.xFine=xFineMax-1;
selStart=cursor;
selEnd=cursor;
demandScrollX=true;
}
void FurnaceGUI::moveCursorTop(bool select) {
finishSelection();
if (!select) {
finishSelection();
}
curNibble=false;
if (cursor.y==0) {
DETERMINE_FIRST;
@ -372,16 +394,18 @@ void FurnaceGUI::moveCursorTop(bool select) {
} else {
cursor.y=0;
}
selStart=cursor;
if (!select) {
selEnd=cursor;
selStart=cursor;
}
selEnd=cursor;
e->setMidiBaseChan(cursor.xCoarse);
updateScroll(cursor.y);
}
void FurnaceGUI::moveCursorBottom(bool select) {
finishSelection();
if (!select) {
finishSelection();
}
curNibble=false;
if (cursor.y==e->curSubSong->patLen-1) {
DETERMINE_LAST;

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2023 tildearrow and contributors
* Copyright (C) 2021-2024 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -72,216 +72,30 @@ const char* sampleNote[12]={
void FurnaceGUI::insListItem(int i, int dir, int asset) {
ImGui::PushID(i);
String name=ICON_FA_CIRCLE_O;
String name=(settings.insIconsStyle==0)?"":ICON_FA_CIRCLE_O;
const char* insType="Bug!";
if (i>=0 && i<e->song.insLen) {
DivInstrument* ins=e->song.ins[i];
insType=(ins->type>DIV_INS_MAX)?"Unknown":insTypes[ins->type];
if (ins->type==DIV_INS_N163) insType=settings.c163Name.c_str();
switch (ins->type) {
case DIV_INS_FM:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_FM]);
name=fmt::sprintf(ICON_FA_AREA_CHART "##_INS%d",i);
insType=(ins->type>=DIV_INS_MAX)?"Unknown":insTypes[ins->type][0];
const char** insIcon=NULL;
if (ins->type>=DIV_INS_MAX) {
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_UNKNOWN]);
insIcon=insTypes[DIV_INS_MAX];
} else {
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_STD+ins->type]);
insIcon=insTypes[ins->type];
}
switch (settings.insIconsStyle) {
case 1:
name=fmt::sprintf("%s##_INS%d",insIcon[1],i);
break;
case DIV_INS_STD:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_STD]);
name=fmt::sprintf(ICON_FA_BAR_CHART "##_INS%d",i);
break;
case DIV_INS_GB:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_GB]);
name=fmt::sprintf(ICON_FA_GAMEPAD "##_INS%d",i);
break;
case DIV_INS_C64:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_C64]);
name=fmt::sprintf(ICON_FA_KEYBOARD_O "##_INS%d",i);
break;
case DIV_INS_AMIGA:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_AMIGA]);
name=fmt::sprintf(ICON_FA_VOLUME_UP "##_INS%d",i);
break;
case DIV_INS_PCE:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_PCE]);
name=fmt::sprintf(ICON_FA_ID_BADGE "##_INS%d",i);
break;
case DIV_INS_AY:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_AY]);
name=fmt::sprintf(ICON_FA_BAR_CHART "##_INS%d",i);
break;
case DIV_INS_AY8930:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_AY8930]);
name=fmt::sprintf(ICON_FA_BAR_CHART "##_INS%d",i);
break;
case DIV_INS_TIA:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_TIA]);
name=fmt::sprintf(ICON_FA_BAR_CHART "##_INS%d",i);
break;
case DIV_INS_SAA1099:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_SAA1099]);
name=fmt::sprintf(ICON_FA_BAR_CHART "##_INS%d",i);
break;
case DIV_INS_VIC:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_VIC]);
name=fmt::sprintf(ICON_FA_BAR_CHART "##_INS%d",i);
break;
case DIV_INS_PET:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_PET]);
name=fmt::sprintf(ICON_FA_SQUARE "##_INS%d",i);
break;
case DIV_INS_VRC6:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_VRC6]);
name=fmt::sprintf(ICON_FA_BAR_CHART "##_INS%d",i);
break;
case DIV_INS_VRC6_SAW:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_VRC6_SAW]);
name=fmt::sprintf(ICON_FA_BAR_CHART "##_INS%d",i);
break;
case DIV_INS_OPLL:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_OPLL]);
name=fmt::sprintf(ICON_FA_AREA_CHART "##_INS%d",i);
break;
case DIV_INS_OPL:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_OPL]);
name=fmt::sprintf(ICON_FA_AREA_CHART "##_INS%d",i);
break;
case DIV_INS_FDS:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_FDS]);
name=fmt::sprintf(ICON_FA_FLOPPY_O "##_INS%d",i);
break;
case DIV_INS_VBOY:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_VBOY]);
name=fmt::sprintf(ICON_FA_BINOCULARS "##_INS%d",i);
break;
case DIV_INS_N163:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_N163]);
name=fmt::sprintf(ICON_FA_CALCULATOR "##_INS%d",i);
break;
case DIV_INS_SCC:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_SCC]);
name=fmt::sprintf(ICON_FA_CALCULATOR "##_INS%d",i);
break;
case DIV_INS_OPZ:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_OPZ]);
name=fmt::sprintf(ICON_FA_AREA_CHART "##_INS%d",i);
break;
case DIV_INS_POKEY:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_POKEY]);
name=fmt::sprintf(ICON_FA_BAR_CHART "##_INS%d",i);
break;
case DIV_INS_BEEPER:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_BEEPER]);
name=fmt::sprintf(ICON_FA_SQUARE "##_INS%d",i);
break;
case DIV_INS_SWAN:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_SWAN]);
name=fmt::sprintf(ICON_FA_GAMEPAD "##_INS%d",i);
break;
case DIV_INS_MIKEY:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_MIKEY]);
name=fmt::sprintf(ICON_FA_BAR_CHART "##_INS%d",i);
break;
case DIV_INS_VERA:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_VERA]);
name=fmt::sprintf(ICON_FA_KEYBOARD_O "##_INS%d",i);
break;
case DIV_INS_X1_010:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_X1_010]);
name=fmt::sprintf(ICON_FA_BAR_CHART "##_INS%d",i);
break;
case DIV_INS_ES5506:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_ES5506]);
name=fmt::sprintf(ICON_FA_VOLUME_UP "##_INS%d",i);
break;
case DIV_INS_MULTIPCM:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_MULTIPCM]);
name=fmt::sprintf(ICON_FA_VOLUME_UP "##_INS%d",i);
break;
case DIV_INS_SNES:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_SNES]);
name=fmt::sprintf(ICON_FA_VOLUME_UP "##_INS%d",i);
break;
case DIV_INS_SU:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_SU]);
name=fmt::sprintf(ICON_FA_MICROCHIP "##_INS%d",i);
break;
case DIV_INS_NAMCO:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_NAMCO]);
name=fmt::sprintf(ICON_FA_PIE_CHART "##_INS%d",i);
break;
case DIV_INS_OPL_DRUMS:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_OPL_DRUMS]);
name=fmt::sprintf(ICON_FA_COFFEE "##_INS%d",i);
break;
case DIV_INS_OPM:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_OPM]);
name=fmt::sprintf(ICON_FA_AREA_CHART "##_INS%d",i);
break;
case DIV_INS_NES:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_NES]);
name=fmt::sprintf(ICON_FA_GAMEPAD "##_INS%d",i);
break;
case DIV_INS_MSM6258:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_MSM6258]);
name=fmt::sprintf(ICON_FA_VOLUME_UP "##_INS%d",i);
break;
case DIV_INS_MSM6295:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_MSM6295]);
name=fmt::sprintf(ICON_FA_VOLUME_UP "##_INS%d",i);
break;
case DIV_INS_ADPCMA:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_ADPCMA]);
name=fmt::sprintf(ICON_FA_VOLUME_UP "##_INS%d",i);
break;
case DIV_INS_ADPCMB:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_ADPCMB]);
name=fmt::sprintf(ICON_FA_VOLUME_UP "##_INS%d",i);
break;
case DIV_INS_SEGAPCM:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_SEGAPCM]);
name=fmt::sprintf(ICON_FA_VOLUME_UP "##_INS%d",i);
break;
case DIV_INS_QSOUND:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_QSOUND]);
name=fmt::sprintf(ICON_FA_VOLUME_UP "##_INS%d",i);
break;
case DIV_INS_YMZ280B:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_YMZ280B]);
name=fmt::sprintf(ICON_FA_VOLUME_UP "##_INS%d",i);
break;
case DIV_INS_RF5C68:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_RF5C68]);
name=fmt::sprintf(ICON_FA_VOLUME_UP "##_INS%d",i);
break;
case DIV_INS_MSM5232:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_MSM5232]);
name=fmt::sprintf(ICON_FA_BAR_CHART "##_INS%d",i);
break;
case DIV_INS_T6W28:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_T6W28]);
name=fmt::sprintf(ICON_FA_BAR_CHART "##_INS%d",i);
break;
case DIV_INS_K007232:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_K007232]);
name=fmt::sprintf(ICON_FA_BAR_CHART "##_INS%d",i);
break;
case DIV_INS_GA20:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_GA20]);
name=fmt::sprintf(ICON_FA_BAR_CHART "##_INS%d",i);
break;
case DIV_INS_POKEMINI:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_POKEMINI]);
name=fmt::sprintf(ICON_FA_BAR_CHART "##_INS%d",i);
break;
case DIV_INS_SM8521:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_SM8521]);
name=fmt::sprintf(ICON_FA_GAMEPAD "##_INS%d",i);
break;
case DIV_INS_PV1000:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_PV1000]);
name=fmt::sprintf(ICON_FA_GAMEPAD "##_INS%d",i);
case 2:
name=fmt::sprintf("%s##_INS%d",insIcon[2],i);
break;
default:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_UNKNOWN]);
name=fmt::sprintf(ICON_FA_QUESTION "##_INS%d",i);
name=fmt::sprintf("##_INS%d",i);
break;
}
} else {
@ -291,8 +105,10 @@ void FurnaceGUI::insListItem(int i, int dir, int asset) {
bool insPressed=ImGui::IsItemActivated();
if (insReleased || (!insListDir && insPressed)) {
curIns=i;
wavePreviewInit=true;
updateFMPreview=true;
if (!insReleased || insListDir) {
wavePreviewInit=true;
updateFMPreview=true;
}
lastAssetType=0;
if (settings.insFocusesPattern && patternOpen)
nextWindow=GUI_WINDOW_PATTERN;
@ -317,15 +133,15 @@ void FurnaceGUI::insListItem(int i, int dir, int asset) {
curIns=i;
updateFMPreview=true;
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_TEXT]);
if (ImGui::MenuItem("duplicate")) {
doAction(GUI_ACTION_INS_LIST_DUPLICATE);
}
if (ImGui::MenuItem("replace...")) {
doAction((curIns>=0 && curIns<(int)e->song.ins.size())?GUI_ACTION_INS_LIST_OPEN_REPLACE:GUI_ACTION_INS_LIST_OPEN);
}
if (ImGui::MenuItem("save")) {
doAction(GUI_ACTION_INS_LIST_SAVE);
}
if (ImGui::MenuItem("save (legacy .fui)")) {
doAction(GUI_ACTION_INS_LIST_SAVE_OLD);
}
if (ImGui::MenuItem("save (.dmp)")) {
doAction(GUI_ACTION_INS_LIST_SAVE_DMP);
}
@ -359,10 +175,14 @@ void FurnaceGUI::waveListItem(int i, float* wavePreview, int dir, int asset) {
wavePreview[i]=wave->data[i];
}
if (wave->len>0) wavePreview[wave->len]=wave->data[wave->len-1];
if (ImGui::Selectable(fmt::sprintf("%d##_WAVE%d\n",i,i).c_str(),curWave==i)) {
ImVec2 curPos=ImGui::GetCursorPos();
ImGui::PushStyleVar(ImGuiStyleVar_SelectableTextAlign,ImVec2(0,0.5f));
if (ImGui::Selectable(fmt::sprintf(" %d##_WAVE%d\n",i,i).c_str(),curWave==i,0,ImVec2(0,ImGui::GetFrameHeight()))) {
curWave=i;
lastAssetType=1;
}
ImGui::PopStyleVar();
curPos.x+=ImGui::CalcTextSize("2222").x;
if (wantScrollList && curWave==i) ImGui::SetScrollHereY();
if (ImGui::IsItemHovered()) {
if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) {
@ -375,12 +195,15 @@ void FurnaceGUI::waveListItem(int i, float* wavePreview, int dir, int asset) {
DRAG_TARGET(dir,asset,e->song.waveDir,"FUR_WAVEDIR");
}
ImGui::SameLine();
PlotNoLerp(fmt::sprintf("##_WAVEP%d",i).c_str(),wavePreview,wave->len+1,0,NULL,0,wave->max);
ImGui::SetCursorPos(curPos);
PlotNoLerp(fmt::sprintf("##_WAVEP%d",i).c_str(),wavePreview,wave->len+1,0,NULL,0,wave->max,ImVec2(ImGui::GetContentRegionAvail().x,ImGui::GetFrameHeight()));
}
void FurnaceGUI::sampleListItem(int i, int dir, int asset) {
bool memWarning=false;
ImGui::PushID(i);
DivSample* sample=e->song.sample[i];
for (int j=0; j<e->song.systemLen; j++) {
DivDispatch* dispatch=e->getDispatch(j);
@ -403,13 +226,10 @@ void FurnaceGUI::sampleListItem(int i, int dir, int asset) {
lastAssetType=2;
}
if (ImGui::IsItemHovered() && !mobileUI) {
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_TEXT]);
ImGui::SetTooltip("(legacy bank %d: %s)",i/12,sampleNote[i%12]);
if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) {
sampleEditOpen=true;
nextWindow=GUI_WINDOW_SAMPLE_EDIT;
}
ImGui::PopStyleColor();
}
if (sampleListDir || (settings.unifiedDataView && insListDir)) {
DRAG_SOURCE(dir,asset,"FUR_SDIR");
@ -425,7 +245,35 @@ void FurnaceGUI::sampleListItem(int i, int dir, int asset) {
}
ImGui::PopStyleColor();
}
if (ImGui::BeginPopupContextItem("SampleRightMenu")) {
curSample=i;
samplePos=0;
updateSampleTex=true;
lastAssetType=2;
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_TEXT]);
if (ImGui::MenuItem("make instrument")) {
doAction(GUI_ACTION_SAMPLE_MAKE_INS);
}
if (ImGui::MenuItem("make me a drum kit")) {
doAction(GUI_ACTION_SAMPLE_LIST_MAKE_MAP);
}
if (ImGui::MenuItem("duplicate")) {
doAction(GUI_ACTION_SAMPLE_LIST_DUPLICATE);
}
if (ImGui::MenuItem("replace...")) {
doAction((curSample>=0 && curSample<(int)e->song.sample.size())?GUI_ACTION_SAMPLE_LIST_OPEN_REPLACE:GUI_ACTION_SAMPLE_LIST_OPEN);
}
if (ImGui::MenuItem("save")) {
doAction(GUI_ACTION_SAMPLE_LIST_SAVE);
}
if (ImGui::MenuItem("delete")) {
doAction(GUI_ACTION_SAMPLE_LIST_DELETE);
}
ImGui::PopStyleColor();
ImGui::EndPopup();
}
if (wantScrollList && curSample==i) ImGui::SetScrollHereY();
ImGui::PopID();
}
void FurnaceGUI::drawInsList(bool asChild) {
@ -609,7 +457,7 @@ void FurnaceGUI::drawInsList(bool asChild) {
}
if (!insListDir) {
ImGui::SameLine();
if (ImGui::ArrowButton("InsUp",ImGuiDir_Up)) {
if (ImGui::Button(ICON_FA_ARROW_UP "##InsUp")) {
if (settings.unifiedDataView) {
switch (lastAssetType) {
case 0:
@ -630,7 +478,7 @@ void FurnaceGUI::drawInsList(bool asChild) {
ImGui::SetTooltip("Move up");
}
ImGui::SameLine();
if (ImGui::ArrowButton("InsDown",ImGuiDir_Down)) {
if (ImGui::Button(ICON_FA_ARROW_DOWN "##InsDown")) {
if (settings.unifiedDataView) {
switch (lastAssetType) {
case 0:
@ -692,6 +540,18 @@ void FurnaceGUI::drawInsList(bool asChild) {
ImGui::SetTooltip("New folder");
}
}
if (lastAssetType==2) {
ImGui::SameLine();
if (ImGui::Button(ICON_FA_VOLUME_UP "##PreviewSampleL")) {
doAction(GUI_ACTION_SAMPLE_LIST_PREVIEW);
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Preview (right click to stop)");
}
if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) {
doAction(GUI_ACTION_SAMPLE_LIST_STOP_PREVIEW);
}
}
ImGui::SameLine();
pushDestColor();
if (ImGui::Button(ICON_FA_TIMES "##InsDelete")) {
@ -726,9 +586,11 @@ void FurnaceGUI::drawInsList(bool asChild) {
if (settings.unifiedDataView) {
ImGui::TableNextRow();
ImGui::TableNextColumn();
if (ImGui::Selectable(ICON_FA_TASKS " Instruments",lastAssetType==0)) {
if (ImGui::Selectable(ICON_FA_TASKS "##Instruments",lastAssetType==0)) {
lastAssetType=0;
}
ImGui::SameLine();
ImGui::Text("Instruments");
ImGui::Indent();
}
@ -794,18 +656,22 @@ void FurnaceGUI::drawInsList(bool asChild) {
ImGui::TableNextRow();
ImGui::TableNextColumn();
if (ImGui::Selectable(ICON_FA_AREA_CHART " Wavetables",lastAssetType==1)) {
if (ImGui::Selectable(ICON_FA_AREA_CHART "##Wavetables",lastAssetType==1)) {
lastAssetType=1;
}
ImGui::SameLine();
ImGui::Text("Wavetables");
ImGui::Indent();
actualWaveList();
ImGui::Unindent();
ImGui::TableNextRow();
ImGui::TableNextColumn();
if (ImGui::Selectable(ICON_FA_VOLUME_UP " Samples",lastAssetType==2)) {
if (ImGui::Selectable(ICON_FA_VOLUME_UP "##Samples",lastAssetType==2)) {
lastAssetType=2;
}
ImGui::SameLine();
ImGui::Text("Samples");
ImGui::Indent();
actualSampleList();
ImGui::Unindent();
@ -896,14 +762,14 @@ void FurnaceGUI::drawWaveList(bool asChild) {
}
if (!waveListDir) {
ImGui::SameLine();
if (ImGui::ArrowButton("WaveUp",ImGuiDir_Up)) {
if (ImGui::Button(ICON_FA_ARROW_UP "##WaveUp")) {
doAction(GUI_ACTION_WAVE_LIST_MOVE_UP);
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Move up");
}
ImGui::SameLine();
if (ImGui::ArrowButton("WaveDown",ImGuiDir_Down)) {
if (ImGui::Button(ICON_FA_ARROW_DOWN "##WaveDown")) {
doAction(GUI_ACTION_WAVE_LIST_MOVE_DOWN);
}
if (ImGui::IsItemHovered()) {
@ -1038,14 +904,14 @@ void FurnaceGUI::drawSampleList(bool asChild) {
}
if (!sampleListDir) {
ImGui::SameLine();
if (ImGui::ArrowButton("SampleUp",ImGuiDir_Up)) {
if (ImGui::Button(ICON_FA_ARROW_UP "##SampleUp")) {
doAction(GUI_ACTION_SAMPLE_LIST_MOVE_UP);
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Move up");
}
ImGui::SameLine();
if (ImGui::ArrowButton("SampleDown",ImGuiDir_Down)) {
if (ImGui::Button(ICON_FA_ARROW_DOWN "##SampleDown")) {
doAction(GUI_ACTION_SAMPLE_LIST_MOVE_DOWN);
}
if (ImGui::IsItemHovered()) {
@ -1078,15 +944,11 @@ void FurnaceGUI::drawSampleList(bool asChild) {
doAction(GUI_ACTION_SAMPLE_LIST_PREVIEW);
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Preview");
ImGui::SetTooltip("Preview (right click to stop)");
}
ImGui::SameLine();
if (ImGui::Button(ICON_FA_VOLUME_OFF "##StopSampleL")) {
if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) {
doAction(GUI_ACTION_SAMPLE_LIST_STOP_PREVIEW);
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Stop preview");
}
ImGui::SameLine();
pushDestColor();
if (ImGui::Button(ICON_FA_TIMES "##SampleDelete")) {

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2023 tildearrow and contributors
* Copyright (C) 2021-2024 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -53,6 +53,9 @@
#include "../engine/platform/ga20.h"
#include "../engine/platform/sm8521.h"
#include "../engine/platform/pv1000.h"
#include "../engine/platform/k053260.h"
#include "../engine/platform/c140.h"
#include "../engine/platform/msm6295.h"
#include "../engine/platform/dummy.h"
#define COMMON_CHIP_DEBUG \
@ -61,7 +64,6 @@
#define FM_CHIP_DEBUG \
COMMON_CHIP_DEBUG; \
ImGui::Text("- lastBusy: %d",ch->lastBusy); \
ImGui::Text("- delay: %d",ch->delay);
#define FM_OPN_CHIP_DEBUG \
@ -166,7 +168,6 @@
ImGui::TextColored(ch->hardReset?colorOn:colorOff,">> hardReset"); \
ImGui::TextColored(ch->opMaskChanged?colorOn:colorOff,">> opMaskChanged"); \
ImGui::TextColored(ch->dacMode?colorOn:colorOff,">> DACMode"); \
ImGui::TextColored(ch->dacReady?colorOn:colorOff,">> DACReady"); \
ImGui::TextColored(ch->dacDirection?colorOn:colorOff,">> DACDirection");
#define GENESIS_OPCHAN_DEBUG \
@ -314,9 +315,7 @@ void putDispatchChip(void* data, int type) {
ImGui::Text("> PCEngine");
COMMON_CHIP_DEBUG;
ImGui::Text("- lastPan: %d",ch->lastPan);
ImGui::Text("- cycles: %d",ch->cycles);
ImGui::Text("- curChan: %d",ch->curChan);
ImGui::Text("- delay: %d",ch->delay);
ImGui::Text("- sampleBank: %d",ch->sampleBank);
ImGui::Text("- lfoMode: %d",ch->lfoMode);
ImGui::Text("- lfoSpeed: %d",ch->lfoSpeed);
@ -356,7 +355,6 @@ void putDispatchChip(void* data, int type) {
ImGui::Text("- filtCut: %d",ch->filtCut);
ImGui::Text("- resetTime: %d",ch->resetTime);
COMMON_CHIP_DEBUG_BOOL;
ImGui::TextColored(ch->isFP?colorOn:colorOff,">> IsFP");
break;
}
case DIV_SYSTEM_ARCADE:
@ -381,7 +379,6 @@ void putDispatchChip(void* data, int type) {
ImGui::Text("- pcmR: %d",ch->pcmR);
ImGui::Text("- pcmCycles: %d",ch->pcmCycles);
ImGui::Text("- sampleBank: %d",ch->sampleBank);
ImGui::Text("- lastBusy: %d",ch->lastBusy);
COMMON_CHIP_DEBUG_BOOL;
break;
}
@ -389,7 +386,6 @@ void putDispatchChip(void* data, int type) {
DivPlatformAY8910* ch=(DivPlatformAY8910*)data;
ImGui::Text("> AY-3-8910");
COMMON_CHIP_DEBUG;
ImGui::Text("- lastBusy: %d",ch->lastBusy);
ImGui::Text("- sampleBank: %d",ch->sampleBank);
ImGui::Text("- stereoSep: %d",ch->stereoSep);
ImGui::Text("- delay: %d",ch->delay);
@ -462,8 +458,6 @@ void putDispatchChip(void* data, int type) {
ImGui::Text("- chanMax: %d",ch->chanMax);
ImGui::Text("- loadWave: %d",ch->loadWave);
ImGui::Text("- loadPos: %d",ch->loadPos);
ImGui::Text("- loadLen: %d",ch->loadLen);
ImGui::Text("- loadMode: %d",ch->loadMode);
COMMON_CHIP_DEBUG_BOOL;
ImGui::TextColored(ch->multiplex?colorOn:colorOff,">> Multiplex");
break;
@ -545,6 +539,20 @@ void putDispatchChip(void* data, int type) {
COMMON_CHIP_DEBUG_BOOL;
break;
}
case DIV_SYSTEM_K053260: {
DivPlatformK053260* ch=(DivPlatformK053260*)data;
ImGui::Text("> K053260");
COMMON_CHIP_DEBUG;
COMMON_CHIP_DEBUG_BOOL;
break;
}
case DIV_SYSTEM_C140: {
DivPlatformC140* ch=(DivPlatformC140*)data;
ImGui::Text("> C140");
COMMON_CHIP_DEBUG;
COMMON_CHIP_DEBUG_BOOL;
break;
}
default:
ImGui::Text("Unimplemented chip! Help!");
break;
@ -760,17 +768,6 @@ void putDispatchChan(void* data, int chanNum, int type) {
DivPlatformAY8910::Channel* ch=(DivPlatformAY8910::Channel*)data;
ImGui::Text("> AY-3-8910");
COMMON_CHAN_DEBUG;
ImGui::Text("* psgMode:");
ImGui::Text(" * curr:");
ImGui::Text(" - tone: %d",ch->curPSGMode.tone);
ImGui::Text(" - noise: %d",ch->curPSGMode.noise);
ImGui::Text(" - envelope: %d",ch->curPSGMode.envelope);
ImGui::Text(" - dac: %d",ch->curPSGMode.dac);
ImGui::Text(" * next:");
ImGui::Text(" - tone: %d",ch->nextPSGMode.tone);
ImGui::Text(" - noise: %d",ch->nextPSGMode.noise);
ImGui::Text(" - envelope: %d",ch->nextPSGMode.envelope);
ImGui::Text(" - dac: %d",ch->nextPSGMode.dac);
ImGui::Text("* DAC:");
ImGui::Text(" - sample: %d",ch->dac.sample);
ImGui::Text(" - rate: %d",ch->dac.rate);
@ -788,22 +785,6 @@ void putDispatchChan(void* data, int chanNum, int type) {
ImGui::Text("> AY8930");
COMMON_CHAN_DEBUG;
ImGui::Text("- duty: %d",ch->duty);
ImGui::Text("* envelope:");
ImGui::Text(" - mode: %d",ch->envelope.mode);
ImGui::Text(" - period: %d",ch->envelope.period);
ImGui::Text(" * slide: %d",ch->envelope.slide);
ImGui::Text(" - low: %d",ch->envelope.slideLow);
ImGui::Text("* psgMode:");
ImGui::Text(" * curr:");
ImGui::Text(" - tone: %d",ch->curPSGMode.tone);
ImGui::Text(" - noise: %d",ch->curPSGMode.noise);
ImGui::Text(" - envelope: %d",ch->curPSGMode.envelope);
ImGui::Text(" - dac: %d",ch->curPSGMode.dac);
ImGui::Text(" * next:");
ImGui::Text(" - tone: %d",ch->nextPSGMode.tone);
ImGui::Text(" - noise: %d",ch->nextPSGMode.noise);
ImGui::Text(" - envelope: %d",ch->nextPSGMode.envelope);
ImGui::Text(" - dac: %d",ch->nextPSGMode.dac);
ImGui::Text("* DAC:");
ImGui::Text(" - sample: %d",ch->dac.sample);
ImGui::Text(" - rate: %d",ch->dac.rate);
@ -869,10 +850,6 @@ void putDispatchChan(void* data, int chanNum, int type) {
ImGui::Text("- wavepos: %d",ch->wavePos);
ImGui::Text("- wavelen: %d",ch->waveLen);
ImGui::Text("- wavemode: %d",ch->waveMode);
ImGui::Text("- loadwave: %d",ch->loadWave);
ImGui::Text("- loadpos: %d",ch->loadPos);
ImGui::Text("- loadlen: %d",ch->loadLen);
ImGui::Text("- loadmode: %d",ch->loadMode);
ImGui::Text("- resVol: %.2x",ch->resVol);
COMMON_CHAN_DEBUG_BOOL;
ImGui::TextColored(ch->volumeChanged?colorOn:colorOff,">> VolumeChanged");
@ -1083,6 +1060,37 @@ void putDispatchChan(void* data, int chanNum, int type) {
COMMON_CHAN_DEBUG_BOOL;
break;
}
case DIV_SYSTEM_K053260: {
DivPlatformK053260::Channel* ch=(DivPlatformK053260::Channel*)data;
ImGui::Text("> K053260");
COMMON_CHAN_DEBUG;
ImGui::Text("* Sample: %d",ch->sample);
ImGui::Text(" - pos: %d",ch->audPos);
ImGui::Text("- panning: %d",ch->panning);
ImGui::Text("- macroVolMul: %.2x",ch->macroVolMul);
COMMON_CHAN_DEBUG_BOOL;
ImGui::TextColored(ch->setPos?colorOn:colorOff,">> SetPos");
ImGui::TextColored(ch->reverse?colorOn:colorOff,">> Reverse");
break;
}
case DIV_SYSTEM_C140: {
DivPlatformC140::Channel* ch=(DivPlatformC140::Channel*)data;
ImGui::Text("> C140");
COMMON_CHAN_DEBUG;
ImGui::Text("* Sample: %d",ch->sample);
ImGui::Text(" - pos: %d",ch->audPos);
ImGui::Text("- chPanL: %.2x",ch->chPanL);
ImGui::Text("- chPanR: %.2x",ch->chPanR);
ImGui::Text("- chVolL: %.2x",ch->chVolL);
ImGui::Text("- chVolR: %.2x",ch->chVolR);
ImGui::Text("- macroVolMul: %.2x",ch->macroVolMul);
ImGui::Text("- macroPanMul: %.2x",ch->macroPanMul);
COMMON_CHAN_DEBUG_BOOL;
ImGui::TextColored(ch->volChangedL?colorOn:colorOff,">> VolChangedL");
ImGui::TextColored(ch->volChangedR?colorOn:colorOff,">> VolChangedR");
ImGui::TextColored(ch->setPos?colorOn:colorOff,">> SetPos");
break;
}
default:
ImGui::Text("Unimplemented chip! Help!");
break;

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2023 tildearrow and contributors
* Copyright (C) 2021-2024 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2023 tildearrow and contributors
* Copyright (C) 2021-2024 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -23,7 +23,26 @@
#include "IconsFontAwesome4.h"
#include <SDL_timer.h>
#include <fmt/printf.h>
#include <imgui.h>
#include "imgui.h"
#include "imgui_internal.h"
PendingDrawOsc _debugDo;
static float oscDebugData[2048];
static int oscDebugLen=800;
static int oscDebugHeight=400;
static float oscDebugLineSize=1.0;
static float oscDebugMin=-1.0;
static float oscDebugMax=1.0;
static float oscDebugPower=1.0;
static int oscDebugRepeat=1;
static void _drawOsc(const ImDrawList* drawList, const ImDrawCmd* cmd) {
if (cmd!=NULL) {
if (cmd->UserCallbackData!=NULL) {
((FurnaceGUI*)(((PendingDrawOsc*)cmd->UserCallbackData)->gui))->runPendingDrawOsc((PendingDrawOsc*)cmd->UserCallbackData);
}
}
}
void FurnaceGUI::drawDebug() {
static int bpOrder;
@ -58,7 +77,7 @@ void FurnaceGUI::drawDebug() {
ImGui::SameLine();
if (ImGui::Button("Pattern Advance")) e->haltWhen(DIV_HALT_PATTERN);
if (ImGui::Button("Play Command Stream")) openFileDialog(GUI_FILE_CMDSTREAM_OPEN);
if (ImGui::Button("Play Command Stream")) nextWindow=GUI_WINDOW_CS_PLAYER;
if (ImGui::Button("Panic")) e->syncReset();
ImGui::SameLine();
@ -212,6 +231,7 @@ void FurnaceGUI::drawDebug() {
}
if (ImGui::TreeNode("Oscilloscope Debug")) {
int c=0;
ImGui::Checkbox("FFT debug view",&debugFFT);
for (int i=0; i<e->song.systemLen; i++) {
DivSystem system=e->song.system[i];
if (e->getChannelCount(system)>0) {
@ -308,6 +328,21 @@ void FurnaceGUI::drawDebug() {
}
ImGui::TreePop();
}
if (ImGui::TreeNode("Do Action")) {
char bindID[1024];
for (int j=0; j<GUI_ACTION_MAX; j++) {
if (strcmp(guiActions[j].friendlyName,"")==0) continue;
if (strstr(guiActions[j].friendlyName,"---")==guiActions[j].friendlyName) {
ImGui::TextUnformatted(guiActions[j].friendlyName);
} else {
snprintf(bindID,1024,"%s##DO_%d",guiActions[j].friendlyName,j);
if (ImGui::Button(bindID)) {
doAction(j);
}
}
}
ImGui::TreePop();
}
if (ImGui::TreeNode("Pitch Table Calculator")) {
ImGui::InputDouble("Clock",&ptcClock);
ImGui::InputDouble("Divider/FreqBase",&ptcDivider);
@ -388,6 +423,41 @@ void FurnaceGUI::drawDebug() {
ImGui::Text("System Managed Scale: %d",sysManagedScale);
ImGui::TreePop();
}
if (ImGui::TreeNode("Audio Debug")) {
TAAudioDesc& audioWant=e->getAudioDescWant();
TAAudioDesc& audioGot=e->getAudioDescGot();
ImGui::Text("want:");
ImGui::Text("- name: %s",audioWant.name.c_str());
ImGui::Text("- device name: %s",audioWant.deviceName.c_str());
ImGui::Text("- rate: %f",audioWant.rate);
ImGui::Text("- buffer size: %d",audioWant.bufsize);
ImGui::Text("- fragments: %d",audioWant.fragments);
ImGui::Text("- inputs: %d",audioWant.inChans);
ImGui::Text("- outputs: %d",audioWant.outChans);
ImGui::Text("- format: %d",audioWant.outFormat);
ImGui::Text("got:");
ImGui::Text("- name: %s",audioGot.name.c_str());
ImGui::Text("- device name: %s",audioGot.deviceName.c_str());
ImGui::Text("- rate: %f",audioGot.rate);
ImGui::Text("- buffer size: %d",audioGot.bufsize);
ImGui::Text("- fragments: %d",audioGot.fragments);
ImGui::Text("- inputs: %d",audioGot.inChans);
ImGui::Text("- outputs: %d",audioGot.outChans);
ImGui::Text("- format: %d",audioGot.outFormat);
ImGui::Text("last call to nextBuf(): in %d, out %d, size %d",e->lastNBIns,e->lastNBOuts,e->lastNBSize);
ImGui::TreePop();
}
if (ImGui::TreeNode("MIDI Debug")) {
if (ImGui::Button("Enable Debug (go to log viewer)")) {
e->setMidiDebug(true);
nextWindow=GUI_WINDOW_LOG;
}
ImGui::TreePop();
}
if (ImGui::TreeNode("Visualizer Debug")) {
if (ImGui::BeginTable("visX",3,ImGuiTableFlags_Borders)) {
ImGui::TableNextRow(ImGuiTableRowFlags_Headers);
@ -458,6 +528,7 @@ void FurnaceGUI::drawDebug() {
pgProgram.clear();
}
ImGui::AlignTextToFramePadding();
ImGui::Text("Address");
ImGui::SameLine();
ImGui::SetNextItemWidth(100.0f*dpiScale);
@ -529,6 +600,66 @@ void FurnaceGUI::drawDebug() {
ImGui::PlotLines("##DebugFMPreview",asFloat,FM_PREVIEW_SIZE,0,"Preview",-1.0,1.0,ImVec2(300.0f*dpiScale,150.0f*dpiScale));
ImGui::TreePop();
}
if (ImGui::TreeNode("Recent Files")) {
ImGui::Text("Items: %d - Max: %d",(int)recentFile.size(),settings.maxRecentFile);
ImGui::Text("readPos: %d - writePos: %d",(int)recentFile.readPos,(int)recentFile.writePos);
ImGui::Indent();
for (size_t i=0; i<recentFile.size(); i++) {
ImGui::Text("%d: %s",(int)i,recentFile[i].c_str());
}
ImGui::Unindent();
ImGui::TreePop();
}
if (ImGui::TreeNode("Osc Render Test")) {
ImGui::InputInt("Length",&oscDebugLen);
ImGui::InputInt("Height",&oscDebugHeight);
ImGui::InputFloat("Line Size",&oscDebugLineSize);
ImGui::InputFloat("Min",&oscDebugMin);
ImGui::InputFloat("Max",&oscDebugMax);
ImGui::InputFloat("Power",&oscDebugPower);
ImGui::InputInt("Repeat",&oscDebugRepeat);
if (oscDebugLen<1) oscDebugLen=1;
if (oscDebugLen>2048) oscDebugLen=2048;
if (oscDebugHeight<1) oscDebugHeight=1;
if (oscDebugHeight>2048) oscDebugHeight=2048;
memset(oscDebugData,0,2048*sizeof(float));
for (int i=0; i<oscDebugLen; i++) {
oscDebugData[i]=oscDebugMin+(oscDebugMax-oscDebugMin)*pow((float)((i*oscDebugRepeat)%oscDebugLen)/(float)oscDebugLen,oscDebugPower);
}
if (rend->supportsDrawOsc()) {
ImDrawList* dl=ImGui::GetWindowDrawList();
ImGuiWindow* window=ImGui::GetCurrentWindow();
ImVec2 size=ImVec2(oscDebugLen,oscDebugHeight);
ImVec2 minArea=window->DC.CursorPos;
ImVec2 maxArea=ImVec2(
minArea.x+size.x,
minArea.y+size.y
);
ImRect rect=ImRect(minArea,maxArea);
ImGuiStyle& style=ImGui::GetStyle();
ImGui::ItemSize(size,style.FramePadding.y);
if (ImGui::ItemAdd(rect,ImGui::GetID("debugOsc"))) {
_debugDo.gui=this;
_debugDo.data=oscDebugData;
_debugDo.len=oscDebugLen;
_debugDo.pos0=rect.Min;
_debugDo.pos1=rect.Max;
_debugDo.color=ImVec4(1.0,1.0,1.0,1.0);
_debugDo.lineSize=dpiScale*oscDebugLineSize;
dl->AddCallback(_drawOsc,&_debugDo);
dl->AddCallback(ImDrawCallback_ResetRenderState,NULL);
}
} else {
ImGui::Text("Render Backend does not support osc rendering.");
}
ImGui::TreePop();
}
if (ImGui::TreeNode("User Interface")) {
if (ImGui::Button("Inspect")) {
inspectorOpen=!inspectorOpen;
@ -553,6 +684,7 @@ void FurnaceGUI::drawDebug() {
ImGui::Text("audio: %dµs",lastProcTime);
ImGui::Text("render: %.0fµs",(double)renderTimeDelta/perfFreq);
ImGui::Text("draw: %.0fµs",(double)drawTimeDelta/perfFreq);
ImGui::Text("swap: %.0fµs",(double)swapTimeDelta/perfFreq);
ImGui::Text("layout: %.0fµs",(double)layoutTimeDelta/perfFreq);
ImGui::Text("event: %.0fµs",(double)eventTimeDelta/perfFreq);
ImGui::Separator();

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2023 tildearrow and contributors
* Copyright (C) 2021-2024 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -18,6 +18,7 @@
*/
#include "gui.h"
#include "commandPalette.h"
#include "../ta-log.h"
#include <fmt/printf.h>
#include <imgui.h>
@ -65,6 +66,10 @@ void FurnaceGUI::doAction(int what) {
case GUI_ACTION_SAVE_AS:
openFileDialog(GUI_FILE_SAVE);
break;
case GUI_ACTION_EXPORT:
curExportType=GUI_EXPORT_NONE;
displayExport=true;
break;
case GUI_ACTION_UNDO:
if (curWindow==GUI_WINDOW_SAMPLE_EDIT) {
doUndoSample();
@ -79,6 +84,9 @@ void FurnaceGUI::doAction(int what) {
doRedo();
}
break;
case GUI_ACTION_QUIT:
requestQuit();
break;
case GUI_ACTION_PLAY_TOGGLE:
if (e->isPlaying() && !e->isStepping()) {
stop();
@ -97,6 +105,7 @@ void FurnaceGUI::doAction(int what) {
if (!e->isPlaying()) {
play();
}
e->setRepeatPattern(false);
break;
case GUI_ACTION_PLAY_REPEAT:
play();
@ -111,12 +120,14 @@ void FurnaceGUI::doAction(int what) {
break;
case GUI_ACTION_STEP_ONE:
e->stepOne(cursor.y);
pendingStepUpdate=1;
break;
case GUI_ACTION_OCTAVE_UP:
if (++curOctave>7) {
curOctave=7;
} else {
e->autoNoteOffAll();
failedNoteOn=false;
}
break;
case GUI_ACTION_OCTAVE_DOWN:
@ -124,6 +135,7 @@ void FurnaceGUI::doAction(int what) {
curOctave=-5;
} else {
e->autoNoteOffAll();
failedNoteOn=false;
}
break;
case GUI_ACTION_INS_UP:
@ -182,9 +194,32 @@ void FurnaceGUI::doAction(int what) {
e->syncReset();
break;
case GUI_ACTION_CLEAR:
showWarning("Are you sure you want to clear... (cannot be undone!)",GUI_WARN_CLEAR);
showWarning("Select an option: (cannot be undone!)",GUI_WARN_CLEAR);
break;
case GUI_ACTION_COMMAND_PALETTE:
displayPalette=true;
curPaletteType=CMDPAL_TYPE_MAIN;
break;
case GUI_ACTION_CMDPAL_RECENT:
displayPalette=true;
curPaletteType=CMDPAL_TYPE_RECENT;
break;
case GUI_ACTION_CMDPAL_INSTRUMENTS:
displayPalette=true;
curPaletteType=CMDPAL_TYPE_INSTRUMENTS;
break;
case GUI_ACTION_CMDPAL_SAMPLES:
displayPalette=true;
curPaletteType=CMDPAL_TYPE_SAMPLES;
break;
case GUI_ACTION_CMDPAL_INSTRUMENT_CHANGE:
displayPalette=true;
curPaletteType=CMDPAL_TYPE_INSTRUMENT_CHANGE;
break;
case GUI_ACTION_CMDPAL_ADD_CHIP:
displayPalette=true;
curPaletteType=CMDPAL_TYPE_ADD_CHIP;
break;
case GUI_ACTION_WINDOW_EDIT_CONTROLS:
nextWindow=GUI_WINDOW_EDIT_CONTROLS;
break;
@ -275,6 +310,18 @@ void FurnaceGUI::doAction(int what) {
case GUI_ACTION_WINDOW_GROOVES:
nextWindow=GUI_WINDOW_GROOVES;
break;
case GUI_ACTION_WINDOW_XY_OSC:
nextWindow=GUI_WINDOW_XY_OSC;
break;
case GUI_ACTION_WINDOW_MEMORY:
nextWindow=GUI_WINDOW_MEMORY;
break;
case GUI_ACTION_WINDOW_CS_PLAYER:
nextWindow=GUI_WINDOW_CS_PLAYER;
break;
case GUI_ACTION_WINDOW_USER_PRESETS:
nextWindow=GUI_WINDOW_USER_PRESETS;
break;
case GUI_ACTION_COLLAPSE_WINDOW:
collapseWindow=true;
@ -371,6 +418,17 @@ void FurnaceGUI::doAction(int what) {
case GUI_WINDOW_GROOVES:
groovesOpen=false;
break;
case GUI_WINDOW_XY_OSC:
xyOscOpen=false;
break;
case GUI_WINDOW_MEMORY:
memoryOpen=false;
break;
case GUI_WINDOW_CS_PLAYER:
csPlayerOpen=false;
break;
case GUI_WINDOW_USER_PRESETS:
userPresetsOpen=false;
default:
break;
}
@ -588,14 +646,34 @@ void FurnaceGUI::doAction(int what) {
case GUI_ACTION_PAT_EXPAND_SONG:
doExpandSong(collapseAmount);
break;
case GUI_ACTION_PAT_LATCH: // TODO
case GUI_ACTION_PAT_LATCH: {
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;
break;
case GUI_ACTION_PAT_SCROLL_MODE: // TODO
break;
case GUI_ACTION_PAT_CLEAR_LATCH: // TODO
}
case GUI_ACTION_PAT_CLEAR_LATCH:
latchIns=-2;
latchVol=-1;
latchEffect=-1;
latchEffectVal=-1;
latchTarget=0;
latchNibble=false;
break;
case GUI_ACTION_INS_LIST_ADD:
if (settings.insTypeMenu) {
makeInsTypeList=e->getPossibleInsTypes();
if (makeInsTypeList.size()>1) {
displayInsTypeList=true;
displayInsTypeListMakeInsSample=-1;
break;
}
}
curIns=e->addInstrument(cursor.xCoarse);
if (curIns==-1) {
showError("too many instruments!");
@ -609,6 +687,11 @@ void FurnaceGUI::doAction(int what) {
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;
e->song.ins[curIns]->esfm.op[i].ct=0;
e->song.ins[curIns]->esfm.op[i].dt=0;
e->song.ins[curIns]->esfm.op[i].modIn=0;
e->song.ins[curIns]->esfm.op[i].outLvl=0;
}
}
wantScrollList=true;
@ -641,9 +724,6 @@ void FurnaceGUI::doAction(int what) {
case GUI_ACTION_INS_LIST_SAVE:
if (curIns>=0 && curIns<(int)e->song.ins.size()) openFileDialog(GUI_FILE_INS_SAVE);
break;
case GUI_ACTION_INS_LIST_SAVE_OLD:
if (curIns>=0 && curIns<(int)e->song.ins.size()) openFileDialog(GUI_FILE_INS_SAVE_OLD);
break;
case GUI_ACTION_INS_LIST_SAVE_DMP:
if (curIns>=0 && curIns<(int)e->song.ins.size()) openFileDialog(GUI_FILE_INS_SAVE_DMP);
break;
@ -651,12 +731,14 @@ void FurnaceGUI::doAction(int what) {
if (e->moveInsUp(curIns)) {
curIns--;
wantScrollList=true;
MARK_MODIFIED;
}
break;
case GUI_ACTION_INS_LIST_MOVE_DOWN:
if (e->moveInsDown(curIns)) {
curIns++;
wantScrollList=true;
MARK_MODIFIED;
}
break;
case GUI_ACTION_INS_LIST_DELETE:
@ -687,17 +769,50 @@ void FurnaceGUI::doAction(int what) {
case GUI_ACTION_INS_LIST_DIR_VIEW:
insListDir=!insListDir;
break;
case GUI_ACTION_WAVE_LIST_ADD:
case GUI_ACTION_WAVE_LIST_ADD: {
waveSizeList.clear();
for (int i=0; i<e->song.systemLen; i++) {
const DivSysDef* sysDef=e->getSystemDef(e->song.system[i]);
if (sysDef==NULL) continue;
if (sysDef->waveHeight==0) continue;
if (sysDef->waveWidth==0) {
// add three preset sizes
waveSizeList.push_back(FurnaceGUIWaveSizeEntry(32,sysDef->waveHeight,sysDef->name));
waveSizeList.push_back(FurnaceGUIWaveSizeEntry(64,sysDef->waveHeight,sysDef->name));
waveSizeList.push_back(FurnaceGUIWaveSizeEntry(128,sysDef->waveHeight,sysDef->name));
} else {
waveSizeList.push_back(FurnaceGUIWaveSizeEntry(sysDef->waveWidth,sysDef->waveHeight,sysDef->name));
}
}
int finalWidth=32;
int finalHeight=32;
if (waveSizeList.size()==1) {
finalWidth=waveSizeList[0].width;
finalHeight=waveSizeList[0].height;
} else if (waveSizeList.size()>1) {
displayWaveSizeList=true;
break;
}
curWave=e->addWave();
if (curWave==-1) {
showError("too many wavetables!");
} else {
wantScrollList=true;
e->song.wave[curWave]->len=finalWidth;
e->song.wave[curWave]->max=finalHeight-1;
for (int j=0; j<finalWidth; j++) {
e->song.wave[curWave]->data[j]=(j*finalHeight)/finalWidth;
}
MARK_MODIFIED;
RESET_WAVE_MACRO_ZOOM;
}
break;
}
case GUI_ACTION_WAVE_LIST_DUPLICATE:
if (curWave>=0 && curWave<(int)e->song.wave.size()) {
int prevWave=curWave;
@ -731,12 +846,14 @@ void FurnaceGUI::doAction(int what) {
if (e->moveWaveUp(curWave)) {
curWave--;
wantScrollList=true;
MARK_MODIFIED;
}
break;
case GUI_ACTION_WAVE_LIST_MOVE_DOWN:
if (e->moveWaveDown(curWave)) {
curWave++;
wantScrollList=true;
MARK_MODIFIED;
}
break;
case GUI_ACTION_WAVE_LIST_DELETE:
@ -831,6 +948,7 @@ void FurnaceGUI::doAction(int what) {
curSample--;
wantScrollList=true;
updateSampleTex=true;
MARK_MODIFIED;
}
break;
case GUI_ACTION_SAMPLE_LIST_MOVE_DOWN:
@ -838,6 +956,7 @@ void FurnaceGUI::doAction(int what) {
curSample++;
wantScrollList=true;
updateSampleTex=true;
MARK_MODIFIED;
}
break;
case GUI_ACTION_SAMPLE_LIST_DELETE:
@ -871,6 +990,49 @@ void FurnaceGUI::doAction(int what) {
case GUI_ACTION_SAMPLE_LIST_DIR_VIEW:
sampleListDir=!sampleListDir;
break;
case GUI_ACTION_SAMPLE_LIST_MAKE_MAP: {
// determine instrument type
std::vector<DivInstrumentType> tempTypeList=e->getPossibleInsTypes();
makeInsTypeList.clear();
for (DivInstrumentType& i: tempTypeList) {
if (i==DIV_INS_PCE ||
i==DIV_INS_MSM6258 ||
i==DIV_INS_MSM6295 ||
i==DIV_INS_ADPCMA ||
i==DIV_INS_ADPCMB ||
i==DIV_INS_SEGAPCM ||
i==DIV_INS_QSOUND ||
i==DIV_INS_YMZ280B ||
i==DIV_INS_RF5C68 ||
i==DIV_INS_MULTIPCM ||
i==DIV_INS_MIKEY ||
i==DIV_INS_X1_010 ||
i==DIV_INS_SWAN ||
i==DIV_INS_AY ||
i==DIV_INS_AY8930 ||
i==DIV_INS_VRC6 ||
i==DIV_INS_SU ||
i==DIV_INS_SNES ||
i==DIV_INS_ES5506 ||
i==DIV_INS_K007232 ||
i==DIV_INS_GA20 ||
i==DIV_INS_K053260 ||
i==DIV_INS_C140 ||
i==DIV_INS_C219 ||
i==DIV_INS_NDS) {
makeInsTypeList.push_back(i);
}
}
if (makeInsTypeList.empty()) {
makeInsTypeList.push_back(DIV_INS_AMIGA);
}
displayInsTypeList=true;
displayInsTypeListMakeInsSample=-2;
break;
}
case GUI_ACTION_SAMPLE_SELECT:
if (curSample<0 || curSample>=(int)e->song.sample.size()) break;
@ -901,7 +1063,7 @@ void FurnaceGUI::doAction(int what) {
sample->strip(start,end);
updateSampleTex=true;
e->renderSamples();
e->renderSamples(curSample);
});
sampleSelStart=-1;
sampleSelEnd=-1;
@ -947,7 +1109,7 @@ void FurnaceGUI::doAction(int what) {
memcpy(&(sample->data16[pos]),sampleClipboard,sizeof(short)*sampleClipboardLen);
}
}
e->renderSamples();
e->renderSamples(curSample);
});
sampleSelStart=pos;
sampleSelEnd=pos+sampleClipboardLen;
@ -977,7 +1139,7 @@ void FurnaceGUI::doAction(int what) {
sample->data16[pos+i]=sampleClipboard[i];
}
}
e->renderSamples();
e->renderSamples(curSample);
});
sampleSelStart=pos;
sampleSelEnd=pos+sampleClipboardLen;
@ -1014,7 +1176,7 @@ void FurnaceGUI::doAction(int what) {
sample->data16[pos+i]=val;
}
}
e->renderSamples();
e->renderSamples(curSample);
});
sampleSelStart=pos;
sampleSelEnd=pos+sampleClipboardLen;
@ -1086,7 +1248,7 @@ void FurnaceGUI::doAction(int what) {
updateSampleTex=true;
e->renderSamples();
e->renderSamples(curSample);
});
MARK_MODIFIED;
break;
@ -1117,7 +1279,7 @@ void FurnaceGUI::doAction(int what) {
updateSampleTex=true;
e->renderSamples();
e->renderSamples(curSample);
});
MARK_MODIFIED;
break;
@ -1148,7 +1310,7 @@ void FurnaceGUI::doAction(int what) {
updateSampleTex=true;
e->renderSamples();
e->renderSamples(curSample);
});
MARK_MODIFIED;
break;
@ -1177,7 +1339,7 @@ void FurnaceGUI::doAction(int what) {
updateSampleTex=true;
e->renderSamples();
e->renderSamples(curSample);
});
MARK_MODIFIED;
break;
@ -1193,7 +1355,7 @@ void FurnaceGUI::doAction(int what) {
sample->strip(start,end);
updateSampleTex=true;
e->renderSamples();
e->renderSamples(curSample);
});
sampleSelStart=-1;
sampleSelEnd=-1;
@ -1211,7 +1373,7 @@ void FurnaceGUI::doAction(int what) {
sample->trim(start,end);
updateSampleTex=true;
e->renderSamples();
e->renderSamples(curSample);
});
sampleSelStart=-1;
sampleSelEnd=-1;
@ -1246,7 +1408,7 @@ void FurnaceGUI::doAction(int what) {
updateSampleTex=true;
e->renderSamples();
e->renderSamples(curSample);
});
MARK_MODIFIED;
break;
@ -1267,13 +1429,13 @@ void FurnaceGUI::doAction(int what) {
} else if (sample->depth==DIV_SAMPLE_DEPTH_8BIT) {
for (unsigned int i=start; i<end; i++) {
sample->data8[i]=-sample->data8[i];
if (sample->data16[i]==-128) sample->data16[i]=127;
if (sample->data8[i]==-128) sample->data8[i]=127;
}
}
updateSampleTex=true;
e->renderSamples();
e->renderSamples(curSample);
});
MARK_MODIFIED;
break;
@ -1298,11 +1460,15 @@ void FurnaceGUI::doAction(int what) {
updateSampleTex=true;
e->renderSamples();
e->renderSamples(curSample);
});
MARK_MODIFIED;
break;
}
case GUI_ACTION_SAMPLE_CROSSFADE_LOOP:
if (curSample<0 || curSample>=(int)e->song.sample.size()) break;
openSampleCrossFadeOpt=true;
break;
case GUI_ACTION_SAMPLE_FILTER:
if (curSample<0 || curSample>=(int)e->song.sample.size()) break;
openSampleFilterOpt=true;
@ -1377,7 +1543,13 @@ void FurnaceGUI::doAction(int what) {
i==DIV_INS_SNES ||
i==DIV_INS_ES5506 ||
i==DIV_INS_K007232 ||
i==DIV_INS_GA20) {
i==DIV_INS_GA20 ||
i==DIV_INS_K053260 ||
i==DIV_INS_C140 ||
i==DIV_INS_C219 ||
i==DIV_INS_NDS ||
i==DIV_INS_GBA_DMA ||
i==DIV_INS_GBA_MINMOD) {
makeInsTypeList.push_back(i);
}
}
@ -1421,7 +1593,7 @@ void FurnaceGUI::doAction(int what) {
sample->loop=true;
updateSampleTex=true;
e->renderSamples();
e->renderSamples(curSample);
});
MARK_MODIFIED;
break;
@ -1546,8 +1718,6 @@ void FurnaceGUI::doAction(int what) {
e->deleteOrder(curOrder);
if (curOrder>=e->curSubSong->ordersLen) {
curOrder=e->curSubSong->ordersLen-1;
oldOrder=curOrder;
oldOrder1=curOrder;
e->setOrder(curOrder);
}
makeUndo(GUI_UNDO_CHANGE_ORDER);

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2023 tildearrow and contributors
* Copyright (C) 2021-2024 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -169,7 +169,7 @@ const bool mobileButtonPersist[32]={
};
void FurnaceGUI::drawMobileControls() {
float timeScale=1.0f/(60.0f*ImGui::GetIO().DeltaTime);
float timeScale=60.0*ImGui::GetIO().DeltaTime;
if (dragMobileMenu) {
if (portrait) {
mobileMenuPos=(dragMobileMenuOrigin.y-ImGui::GetMousePos().y)/(canvasH*0.65);
@ -374,7 +374,7 @@ void FurnaceGUI::drawMobileControls() {
if (portrait) ImGui::SameLine();
if (ImGui::Button(ICON_FA_ARROW_DOWN "##StepOne",buttonSize)) {
e->stepOne(cursor.y);
pendingStepUpdate=true;
pendingStepUpdate=1;
}
bool repeatPattern=e->getRepeatPattern();
@ -509,24 +509,8 @@ void FurnaceGUI::drawMobileControls() {
doAction(GUI_ACTION_SAVE_AS);
}
ImGui::Button("1.1+ .dmf");
ImGui::SameLine();
ImGui::Button("Legacy .dmf");
ImGui::SameLine();
if (ImGui::Button("Export Audio")) {
openFileDialog(GUI_FILE_EXPORT_AUDIO_ONE);
}
ImGui::SameLine();
if (ImGui::Button("Export VGM")) {
openFileDialog(GUI_FILE_EXPORT_VGM);
}
if (ImGui::Button("CmdStream")) {
openFileDialog(GUI_FILE_EXPORT_CMDSTREAM_BINARY);
}
ImGui::SameLine();
if (ImGui::Button("CmdStream Text")) {
openFileDialog(GUI_FILE_EXPORT_CMDSTREAM);
if (ImGui::Button("Export")) {
doAction(GUI_ACTION_EXPORT);
}
if (ImGui::Button("Restore Backup")) {
@ -578,9 +562,33 @@ void FurnaceGUI::drawMobileControls() {
if (ImGui::Button("Stats")) {
statsOpen=!statsOpen;
}
ImGui::SameLine();
if (ImGui::Button("Grooves")) {
groovesOpen=!groovesOpen;
}
if (ImGui::Button("Compat Flags")) {
compatFlagsOpen=!compatFlagsOpen;
}
ImGui::SameLine();
if (ImGui::Button("XYOsc")) {
xyOscOpen=!xyOscOpen;
}
ImGui::SameLine();
if (ImGui::Button("Meter")) {
volMeterOpen=!volMeterOpen;
}
ImGui::SameLine();
if (ImGui::Button("Memory")) {
memoryOpen=!memoryOpen;
}
if (ImGui::Button("CV")) {
cvOpen=!cvOpen;
}
ImGui::SameLine();
if (ImGui::Button("Presets")) {
userPresetsOpen=!userPresetsOpen;
}
ImGui::Separator();
@ -619,6 +627,7 @@ void FurnaceGUI::drawMobileControls() {
"Furnace Amiga emulator is working properly by\n"
"comparing it with real Amiga output."
);
ImGui::AlignTextToFramePadding();
ImGui::Text("Directory");
ImGui::SameLine();
ImGui::InputText("##AVDPath",&workingDirROMExport);
@ -664,6 +673,7 @@ void FurnaceGUI::drawEditControls() {
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::AlignTextToFramePadding();
ImGui::Text("Octave");
ImGui::TableNextColumn();
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
@ -671,6 +681,7 @@ void FurnaceGUI::drawEditControls() {
if (curOctave>7) curOctave=7;
if (curOctave<-5) curOctave=-5;
e->autoNoteOffAll();
failedNoteOn=false;
if (settings.insFocusesPattern && !ImGui::IsItemActive() && patternOpen) {
nextWindow=GUI_WINDOW_PATTERN;
@ -679,6 +690,7 @@ void FurnaceGUI::drawEditControls() {
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::AlignTextToFramePadding();
ImGui::Text("Edit Step");
ImGui::TableNextColumn();
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
@ -717,6 +729,7 @@ void FurnaceGUI::drawEditControls() {
e->setMetronome(metro);
}
ImGui::AlignTextToFramePadding();
ImGui::Text("Follow");
ImGui::SameLine();
unimportant(ImGui::Checkbox("Orders",&followOrders));
@ -730,7 +743,7 @@ void FurnaceGUI::drawEditControls() {
ImGui::SameLine();
if (ImGui::Button(ICON_FA_ARROW_DOWN "##StepOne")) {
e->stepOne(cursor.y);
pendingStepUpdate=true;
pendingStepUpdate=1;
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Step one row");
@ -770,7 +783,7 @@ void FurnaceGUI::drawEditControls() {
ImGui::SameLine();
if (ImGui::Button(ICON_FA_ARROW_DOWN "##StepOne")) {
e->stepOne(cursor.y);
pendingStepUpdate=true;
pendingStepUpdate=1;
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Step one row");
@ -816,6 +829,7 @@ void FurnaceGUI::drawEditControls() {
if (curOctave>7) curOctave=7;
if (curOctave<-5) curOctave=-5;
e->autoNoteOffAll();
failedNoteOn=false;
if (settings.insFocusesPattern && !ImGui::IsItemActive() && patternOpen) {
nextWindow=GUI_WINDOW_PATTERN;
@ -875,7 +889,7 @@ void FurnaceGUI::drawEditControls() {
}
if (ImGui::Button(ICON_FA_ARROW_DOWN "##StepOne",buttonSize)) {
e->stepOne(cursor.y);
pendingStepUpdate=true;
pendingStepUpdate=1;
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Step one row");
@ -920,6 +934,7 @@ void FurnaceGUI::drawEditControls() {
if (curOctave>7) curOctave=7;
if (curOctave<-5) curOctave=-5;
e->autoNoteOffAll();
failedNoteOn=false;
if (settings.insFocusesPattern && !ImGui::IsItemActive() && patternOpen) {
nextWindow=GUI_WINDOW_PATTERN;
@ -1009,7 +1024,7 @@ void FurnaceGUI::drawEditControls() {
ImGui::SameLine();
if (ImGui::Button(ICON_FA_ARROW_DOWN "##StepOne")) {
e->stepOne(cursor.y);
pendingStepUpdate=true;
pendingStepUpdate=1;
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Step one row");
@ -1063,6 +1078,7 @@ void FurnaceGUI::drawEditControls() {
if (ImGui::Begin("Edit Controls",&editControlsOpen,globalWinFlags)) {
ImGui::Columns(2);
ImGui::AlignTextToFramePadding();
ImGui::Text("Octave");
ImGui::SameLine();
float cursor=ImGui::GetCursorPosX();
@ -1072,12 +1088,14 @@ void FurnaceGUI::drawEditControls() {
if (curOctave>7) curOctave=7;
if (curOctave<-5) curOctave=-5;
e->autoNoteOffAll();
failedNoteOn=false;
if (settings.insFocusesPattern && !ImGui::IsItemActive() && patternOpen) {
nextWindow=GUI_WINDOW_PATTERN;
}
}
ImGui::AlignTextToFramePadding();
ImGui::Text("Step");
ImGui::SameLine();
ImGui::SetCursorPosX(cursor);

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2023 tildearrow and contributors
* Copyright (C) 2021-2024 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -24,6 +24,17 @@
#include "actionUtil.h"
static const char* modPlugFormatHeaders[]={
"ModPlug Tracker MOD",
"ModPlug Tracker S3M",
"ModPlug Tracker XM",
"ModPlug Tracker XM",
"ModPlug Tracker IT",
"ModPlug Tracker IT",
"ModPlug Tracker MPT",
NULL,
};
const char* FurnaceGUI::noteNameNormal(short note, short octave) {
if (note==100) { // note cut
return "OFF";
@ -41,7 +52,29 @@ const char* FurnaceGUI::noteNameNormal(short note, short octave) {
return noteNames[seek];
}
void FurnaceGUI::prepareUndo(ActionType action) {
void FurnaceGUI::prepareUndo(ActionType action, UndoRegion region) {
if (region.begin.ord==-1) {
region.begin.ord=curOrder;
region.end.ord=curOrder;
region.begin.x=0;
region.end.x=e->getTotalChannelCount()-1;
region.begin.y=0;
region.end.y=e->curSubSong->patLen-1;
} else {
if (region.begin.ord<0) region.begin.ord=0;
if (region.begin.ord>e->curSubSong->ordersLen) region.begin.ord=e->curSubSong->ordersLen;
if (region.end.ord<0) region.end.ord=0;
if (region.end.ord>e->curSubSong->ordersLen) region.end.ord=e->curSubSong->ordersLen;
if (region.begin.x<0) region.begin.x=0;
if (region.begin.x>=e->getTotalChannelCount()) region.begin.x=e->getTotalChannelCount()-1;
if (region.end.x<0) region.end.x=0;
if (region.end.x>=e->getTotalChannelCount()) region.end.x=e->getTotalChannelCount()-1;
if (region.begin.y<0) region.begin.y=0;
if (region.begin.y>=e->curSubSong->patLen) region.begin.y=e->curSubSong->patLen-1;
if (region.end.y<0) region.end.y=0;
if (region.end.y>=e->curSubSong->patLen) region.end.y=e->curSubSong->patLen-1;
}
switch (action) {
case GUI_UNDO_CHANGE_ORDER:
memcpy(&oldOrders,e->curOrders,sizeof(DivOrders));
@ -63,28 +96,67 @@ void FurnaceGUI::prepareUndo(ActionType action) {
case GUI_UNDO_PATTERN_COLLAPSE:
case GUI_UNDO_PATTERN_EXPAND:
case GUI_UNDO_PATTERN_DRAG:
for (int i=0; i<e->getTotalChannelCount(); i++) {
e->curPat[i].getPattern(e->curOrders->ord[i][curOrder],false)->copyOn(oldPat[i]);
for (int h=region.begin.ord; h<=region.end.ord; h++) {
for (int i=region.begin.x; i<=region.end.x; i++) {
unsigned short id=h|(i<<8);
DivPattern* p=NULL;
auto it=oldPatMap.find(id);
if (it==oldPatMap.end()) {
p=oldPatMap[id]=new DivPattern;
//logV("oldPatMap: allocating for %.4x",id);
} else {
p=it->second;
}
e->curPat[i].getPattern(e->curOrders->ord[i][h],false)->copyOn(p);
}
}
break;
case GUI_UNDO_PATTERN_COLLAPSE_SONG:
case GUI_UNDO_PATTERN_EXPAND_SONG: // TODO
case GUI_UNDO_PATTERN_EXPAND_SONG: // this is handled by doCollapseSong/doExpandSong
break;
case GUI_UNDO_REPLACE: // this is handled by doReplace()
break;
}
}
void FurnaceGUI::makeUndo(ActionType action) {
void FurnaceGUI::makeUndo(ActionType action, UndoRegion region) {
bool doPush=false;
bool shallWalk=false;
UndoStep s;
s.type=action;
s.cursor=cursor;
s.selStart=selStart;
s.selEnd=selEnd;
s.order=curOrder;
s.oldOrdersLen=oldOrdersLen;
s.newOrdersLen=e->curSubSong->ordersLen;
s.nibble=curNibble;
size_t subSong=e->getCurrentSubSong();
if (region.begin.ord==-1) {
region.begin.ord=curOrder;
region.end.ord=curOrder;
region.begin.x=0;
region.end.x=e->getTotalChannelCount()-1;
region.begin.y=0;
region.end.y=e->curSubSong->patLen-1;
} else {
if (region.begin.ord<0) region.begin.ord=0;
if (region.begin.ord>e->curSubSong->ordersLen) region.begin.ord=e->curSubSong->ordersLen;
if (region.end.ord<0) region.end.ord=0;
if (region.end.ord>e->curSubSong->ordersLen) region.end.ord=e->curSubSong->ordersLen;
if (region.begin.x<0) region.begin.x=0;
if (region.begin.x>=e->getTotalChannelCount()) region.begin.x=e->getTotalChannelCount()-1;
if (region.end.x<0) region.end.x=0;
if (region.end.x>=e->getTotalChannelCount()) region.end.x=e->getTotalChannelCount()-1;
if (region.begin.y<0) region.begin.y=0;
if (region.begin.y>=e->curSubSong->patLen) region.begin.y=e->curSubSong->patLen-1;
if (region.end.y<0) region.end.y=0;
if (region.end.y>=e->curSubSong->patLen) region.end.y=e->curSubSong->patLen-1;
}
switch (action) {
case GUI_UNDO_CHANGE_ORDER:
for (int i=0; i<DIV_MAX_CHANS; i++) {
@ -94,8 +166,6 @@ void FurnaceGUI::makeUndo(ActionType action) {
}
}
}
s.oldOrdersLen=oldOrdersLen;
s.newOrdersLen=e->curSubSong->ordersLen;
if (oldOrdersLen!=e->curSubSong->ordersLen) {
doPush=true;
}
@ -119,12 +189,43 @@ void FurnaceGUI::makeUndo(ActionType action) {
case GUI_UNDO_PATTERN_COLLAPSE:
case GUI_UNDO_PATTERN_EXPAND:
case GUI_UNDO_PATTERN_DRAG:
for (int i=0; i<e->getTotalChannelCount(); i++) {
DivPattern* p=e->curPat[i].getPattern(e->curOrders->ord[i][curOrder],false);
for (int j=0; j<e->curSubSong->patLen; j++) {
for (int k=0; k<DIV_MAX_COLS; k++) {
if (p->data[j][k]!=oldPat[i]->data[j][k]) {
s.pat.push_back(UndoPatternData(subSong,i,e->curOrders->ord[i][curOrder],j,k,oldPat[i]->data[j][k],p->data[j][k]));
for (int h=region.begin.ord; h<=region.end.ord; h++) {
for (int i=region.begin.x; i<=region.end.x; i++) {
DivPattern* p=e->curPat[i].getPattern(e->curOrders->ord[i][h],false);
DivPattern* op=NULL;
unsigned short id=h|(i<<8);
auto it=oldPatMap.find(id);
if (it==oldPatMap.end()) {
logW("no data in oldPatMap for channel %d!",i);
continue;
} else {
op=it->second;
}
int jBegin=0;
int jEnd=e->curSubSong->patLen-1;
if (h==region.begin.ord) jBegin=region.begin.y;
if (h==region.end.ord) jEnd=region.end.y;
for (int j=jBegin; j<=jEnd; j++) {
for (int k=0; k<DIV_MAX_COLS; k++) {
if (p->data[j][k]!=op->data[j][k]) {
s.pat.push_back(UndoPatternData(subSong,i,e->curOrders->ord[i][h],j,k,op->data[j][k],p->data[j][k]));
if (k>=4) {
if (op->data[j][k&(~1)]==0x0b ||
p->data[j][k&(~1)]==0x0b ||
op->data[j][k&(~1)]==0x0d ||
p->data[j][k&(~1)]==0x0d ||
op->data[j][k&(~1)]==0xff ||
p->data[j][k&(~1)]==0xff) {
shallWalk=true;
}
}
}
}
}
}
@ -134,7 +235,7 @@ void FurnaceGUI::makeUndo(ActionType action) {
}
break;
case GUI_UNDO_PATTERN_COLLAPSE_SONG:
case GUI_UNDO_PATTERN_EXPAND_SONG: // TODO
case GUI_UNDO_PATTERN_EXPAND_SONG: // this is handled by doCollapseSong/doExpandSong
break;
case GUI_UNDO_REPLACE: // this is handled by doReplace()
break;
@ -145,6 +246,15 @@ void FurnaceGUI::makeUndo(ActionType action) {
redoHist.clear();
if (undoHist.size()>settings.maxUndoSteps) undoHist.pop_front();
}
if (shallWalk) {
e->walkSong(loopOrder,loopRow,loopEnd);
}
// garbage collection
for (std::pair<unsigned short,DivPattern*> i: oldPatMap) {
delete i.second;
}
oldPatMap.clear();
}
void FurnaceGUI::doSelectAll() {
@ -433,36 +543,8 @@ String FurnaceGUI::doCopy(bool cut, bool writeClipboard, const SelectionPoint& s
return clipb;
}
void FurnaceGUI::doPaste(PasteMode mode, int arg, bool readClipboard, String clipb) {
if (readClipboard) {
finishSelection();
prepareUndo(GUI_UNDO_PATTERN_PASTE);
char* clipText=SDL_GetClipboardText();
if (clipText!=NULL) {
if (clipText[0]) {
clipboard=clipText;
}
SDL_free(clipText);
}
clipb=clipboard;
}
std::vector<String> data;
String tempS;
for (char i: clipb) {
if (i=='\r') continue;
if (i=='\n') {
data.push_back(tempS);
tempS="";
continue;
}
tempS+=i;
}
data.push_back(tempS);
int startOff=-1;
bool invalidData=false;
if (data.size()<2) return;
if (data[0].find("org.tildearrow.furnace - Pattern Data")!=0) return;
void FurnaceGUI::doPasteFurnace(PasteMode mode, int arg, bool readClipboard, String clipb, std::vector<String> data, int startOff, bool invalidData, UndoRegion ur)
{
if (sscanf(data[1].c_str(),"%d",&startOff)!=1) return;
if (startOff<0) return;
@ -603,7 +685,623 @@ void FurnaceGUI::doPaste(PasteMode mode, int arg, bool readClipboard, String cli
updateScroll(cursor.y);
}
makeUndo(GUI_UNDO_PATTERN_PASTE);
makeUndo(GUI_UNDO_PATTERN_PASTE,ur);
}
}
unsigned int convertEffectMPT_MOD(unsigned char symbol, unsigned int val) {
switch (symbol) {
case '0':
return (0x00<<8)|val;
break;
case '1':
return (0x01<<8)|val;
break;
case '2':
return (0x02<<8)|val;
break;
case '3':
return (0x03<<8)|val;
break;
case '4':
return (0x04<<8)|val;
break;
case '5':
return (0x0a<<8)|val|(0x03<<24); // Axy+300
break;
case '6':
return (0x0a<<8)|val|(0x04<<24); // Axy+400
break;
case '7':
return (0x07<<8)|val;
break;
case '8':
return (0x80<<8)|val;
break;
case '9':
return (0x90<<8)|val;
break;
case 'A':
return (0x0A<<8)|val;
break;
case 'B':
return (0x0B<<8)|val;
break;
case 'C':
return (0x0C<<8)|val; // interpreted as volume later
break;
case 'D': {
unsigned char newParam=(val&0xf)+((val&0xff)>>4)*10; // hex to decimal, Protracker (and XM too!) lol
return (0x0D<<8)|newParam;
break;
}
case 'E': {
switch (val>>4) {
case 1:
return (0xF1<<8)|(val&0xf);
break;
case 2:
return (0xF2<<8)|(val&0xf);
break;
// glissando and vib shape not supported in Furnace
case 5:
return (0xF5<<8)|((val&0xf)<<4);
break;
// pattern loop not supported
case 8:
return (0x80<<8)|((val&0xf)<<4);
break;
case 9:
return (0x0C<<8)|(val&0xf);
break;
case 0xA:
return (0xF3<<8)|(val&0xf);
break;
case 0xB:
return (0xF4<<8)|(val&0xf);
break;
case 0xC:
return (0xFC<<8)|(val&0xf);
break;
case 0xD:
return (0xFD<<8)|(val&0xf);
break;
default:
break;
}
break;
}
case 'F':
if (val<0x20) {
return (0x0F<<8)|val;
} else {
return (0xF0<<8)|val;
}
break;
}
return 0;
}
unsigned int convertEffectMPT_S3M(unsigned char symbol, unsigned int val) {
switch (symbol) {
case 'A':
return (0x09<<8)|val;
break;
case 'B':
return (0x0B<<8)|val;
break;
case 'C':
return (0x0D<<8)|val;
break;
case 'D':
if ((val&0xf0)==0xf0) {
return (0xF4<<8)|(val&0xf);
} else if ((val&0xf)==0xf) {
return (0xF3<<8)|((val&0xf0)>>4);
} else {
return (0x0A<<8)|val;
}
break;
case 'E':
if (val<0xe0) {
return (0x02<<8)|val;
} else if (val>=0xe0 && val<0xf0) {
return (0xF2<<8)|(val&0xf);
} else {
return (0xF2<<8)|((val&0xf)>>1);
}
break;
case 'F':
if (val<0xe0) {
return (0x01<<8)|val;
} else if (val>=0xe0 && val<0xf0) {
return (0xF1<<8)|(val&0xf);
} else {
return (0xF1<<8)|((val&0xf)>>1);
}
break;
case 'G':
return (0x03<<8)|val;
break;
case 'H':
return (0x04<<8)|val;
break;
case 'J':
return (0x00<<8)|val;
break;
case 'K':
return (0x0a<<8)|val|(0x04<<24); // Axy+400
break;
case 'L':
return (0x0a<<8)|val|(0x03<<24); // Axy+300
break;
case 'O':
return (0x90<<8)|val;
break;
case 'Q':
return (0xC0<<8)|(val&0xf);
break;
case 'R':
return (0x07<<8)|(val&0xf);
break;
case 'S': {
switch (val>>4) {
case 2:
return (0xE5<<8)|((val&0xf)<<4);
break;
case 8:
return (0x80<<8)|((val&0xf)<<4);
break;
case 0xC:
return (0xEC<<8)|(val&0xf);
break;
case 0xD:
return (0xED<<8)|(val&0xf);
break;
default:
break;
}
break;
}
case 'T':
return (0xF0<<8)|(val&0xf);
break;
case 'U':
return (0x04<<8)|MAX(1,((val&0xf0)>>6)<<4)|MAX(1,(val&0xf)>>2);
break;
case 'X':
return (0x80<<8)|val;
break;
default:
return 0;
break;
}
return 0;
}
unsigned int convertEffectMPT_XM(unsigned char symbol, unsigned int val) {
if (symbol=='K') {
return (0xEC<<8)|val;
}
return convertEffectMPT_MOD(symbol,val); // for now
}
unsigned int convertEffectMPT_IT(unsigned char symbol, unsigned int val) {
return convertEffectMPT_S3M(symbol,val); // for now
}
unsigned int convertEffectMPT_MPTM(unsigned char symbol, unsigned int val) {
if (symbol==':') {
return (0xED<<8)|((val&0xf0)>>4)|(0xEC<<24)|((((val&0xf0)>>4)+(val&0xf))<<16);
}
return convertEffectMPT_IT(symbol,val);
}
// TODO: fix code style
void FurnaceGUI::doPasteMPT(PasteMode mode, int arg, bool readClipboard, String clipb, std::vector<String> data, int mptFormat, UndoRegion ur)
{
DETERMINE_LAST;
int j=cursor.y;
char note[4];
bool invalidData=false;
memset(note,0,4);
for(size_t i=1; i<data.size() && j<e->curSubSong->patLen; i++)
{
size_t charPos=1;
int iCoarse=cursor.xCoarse;
int iFine=0;
String& line=data[i];
while (charPos<line.size() && iCoarse<lastChannel)
{
DivPattern* pat=e->curPat[iCoarse].getPattern(e->curOrders->ord[iCoarse][curOrder],true);
if (line[charPos]=='|' && charPos != 0) // MPT format starts every pattern line with '|'
{
iCoarse++;
if (iCoarse<lastChannel) while (!e->curSubSong->chanShow[iCoarse])
{
iCoarse++;
if (iCoarse>=lastChannel) break;
}
iFine=0;
charPos++;
continue;
}
if (iFine==0) // note
{
if (charPos>=line.size())
{
invalidData=true;
break;
}
note[0]=line[charPos++];
if (charPos>=line.size()) {
invalidData=true;
break;
}
note[1]=line[charPos++];
if (charPos>=line.size()) {
invalidData=true;
break;
}
note[2]=line[charPos++];
note[3]=0;
if (iFine==0 && !opMaskPaste.note) {
iFine++;
continue;
}
if (strcmp(note,"...")==0 || strcmp(note," ")==0)
{
// do nothing.
}
else
{
if (!(mode==GUI_PASTE_MODE_MIX_BG || mode==GUI_PASTE_MODE_INS_BG) || (pat->data[j][0]==0 && pat->data[j][1]==0))
{
if (!decodeNote(note,pat->data[j][0],pat->data[j][1]))
{
if(strcmp(note, "^^^") == 0)
{
pat->data[j][0]=100;
pat->data[j][1]=0;
}
else if(strcmp(note, "~~~") == 0 || strcmp(note, "===") == 0)
{
pat->data[j][0]=101;
pat->data[j][1]=0;
}
else
{
invalidData=true;
}
break;
}
else
{
pat->data[j][1]--; // MPT is one octave higher...
}
if (mode==GUI_PASTE_MODE_INS_BG || mode==GUI_PASTE_MODE_INS_FG) pat->data[j][2]=arg;
}
}
}
else if (iFine==1) // instrument
{
if (charPos>=line.size())
{
invalidData=true;
break;
}
note[0]=line[charPos++];
if (charPos>=line.size())
{
invalidData=true;
break;
}
note[1]=line[charPos++];
note[2]=0;
if (iFine==1)
{
if (!opMaskPaste.ins || mode==GUI_PASTE_MODE_INS_BG || mode==GUI_PASTE_MODE_INS_FG)
{
iFine++;
continue;
}
}
if (strcmp(note,"..")==0 || strcmp(note," ")==0)
{
if (!(mode==GUI_PASTE_MODE_MIX_BG || mode==GUI_PASTE_MODE_MIX_FG ||
mode==GUI_PASTE_MODE_INS_BG || mode==GUI_PASTE_MODE_INS_FG))
{
pat->data[j][iFine+1]=-1;
}
}
else
{
unsigned int val=0;
if (sscanf(note,"%2d",&val)!=1)
{
invalidData=true;
break;
}
if (!(mode==GUI_PASTE_MODE_MIX_BG || mode==GUI_PASTE_MODE_INS_BG) || pat->data[j][iFine+1]==-1)
{
pat->data[j][iFine+1]=val - 1;
}
}
}
else
{ // volume and effects
if (charPos>=line.size())
{
invalidData=true;
break;
}
note[0]=line[charPos++];
if (charPos>=line.size())
{
invalidData=true;
break;
}
note[1]=line[charPos++];
if (charPos>=line.size())
{
invalidData=true;
break;
}
note[2]=line[charPos++];
note[3]=0;
if (iFine==2)
{
if (!opMaskPaste.vol)
{
iFine++;
continue;
}
}
else if ((iFine&1)==0)
{
if (!opMaskPaste.effectVal)
{
iFine++;
continue;
}
}
else if ((iFine&1)==1)
{
if (!opMaskPaste.effect)
{
iFine++;
continue;
}
}
if (strcmp(note,"...")==0 || strcmp(note," ")==0)
{
if (!(mode==GUI_PASTE_MODE_MIX_BG || mode==GUI_PASTE_MODE_MIX_FG ||
mode==GUI_PASTE_MODE_INS_BG || mode==GUI_PASTE_MODE_INS_FG))
{
pat->data[j][iFine+1]=-1;
}
}
else
{
unsigned int val=0;
unsigned char symbol = '\0';
symbol = note[0];
if(iFine == 2)
{
sscanf(&note[1],"%2d",&val);
}
else
{
sscanf(&note[1],"%2X",&val);
}
if (!(mode==GUI_PASTE_MODE_MIX_BG || mode==GUI_PASTE_MODE_INS_BG) || pat->data[j][iFine+1]==-1)
{
// if (iFine<(3+e->curPat[iCoarse].effectCols*2)) pat->data[j][iFine+1]=val;
if(iFine == 2) // volume
{
switch(symbol)
{
case 'v':
{
pat->data[j][iFine+1]=val;
break;
}
default:
break;
}
}
else // effect
{
unsigned int eff = 0;
if(mptFormat == 0)
{
eff = convertEffectMPT_MOD(symbol, val); // up to 4 effects stored in one variable
if(((eff & 0x0f00) >> 8) == 0x0C) // set volume
{
pat->data[j][iFine]=eff & 0xff;
}
}
if(mptFormat == 1)
{
eff = convertEffectMPT_S3M(symbol, val);
}
if(mptFormat == 2 || mptFormat == 3) // set volume
{
eff = convertEffectMPT_XM(symbol, val);
if(((eff & 0x0f00) >> 8) == 0x0C)
{
pat->data[j][iFine]=eff & 0xff;
}
}
if(mptFormat == 4 || mptFormat == 5)
{
eff = convertEffectMPT_IT(symbol, val);
}
if(mptFormat == 6)
{
eff = convertEffectMPT_MPTM(symbol, val);
}
pat->data[j][iFine+1]=((eff & 0xff00) >> 8);
pat->data[j][iFine+2]=(eff & 0xff);
if(eff > 0xffff)
{
pat->data[j][iFine+3]=((eff & 0xff000000) >> 24);
pat->data[j][iFine+4]=((eff & 0xff0000) >> 16);
}
}
}
}
}
iFine++;
if(charPos >= line.size() - 1)
{
invalidData = false;
break;
}
}
if (invalidData)
{
logW("invalid clipboard data! failed at line %d char %d",i,charPos);
logW("%s",line.c_str());
break;
}
j++;
if (mode==GUI_PASTE_MODE_OVERFLOW && j>=e->curSubSong->patLen && curOrder<e->curSubSong->ordersLen-1)
{
j=0;
curOrder++;
}
if (mode==GUI_PASTE_MODE_FLOOD && i==data.size()-1)
{
i=1;
}
}
if (readClipboard) {
if (settings.cursorPastePos) {
cursor.y=j;
if (cursor.y>=e->curSubSong->patLen) cursor.y=e->curSubSong->patLen-1;
selStart=cursor;
selEnd=cursor;
updateScroll(cursor.y);
}
makeUndo(GUI_UNDO_PATTERN_PASTE,ur);
}
}
void FurnaceGUI::doPaste(PasteMode mode, int arg, bool readClipboard, String clipb) {
if (readClipboard) {
finishSelection();
char* clipText=SDL_GetClipboardText();
if (clipText!=NULL) {
if (clipText[0]) {
clipboard=clipText;
}
SDL_free(clipText);
}
clipb=clipboard;
}
std::vector<String> data;
String tempS;
bool foundString=false;
bool isFurnace=false;
bool isModPlug=false;
int mptFormat=0;
for (char i: clipb) {
if (i=='\r') continue;
if (i=='\n') {
data.push_back(tempS);
tempS="";
continue;
}
tempS+=i;
}
data.push_back(tempS);
int startOff=-1;
bool invalidData=false;
if (data.size()<2) return;
if (data[0].find("org.tildearrow.furnace - Pattern Data")==0) {
foundString=true;
isFurnace=true;
} else {
for (int i=0; modPlugFormatHeaders[i]; i++) {
if (data[0].find(modPlugFormatHeaders[i])==0) {
foundString=true;
isModPlug=true;
mptFormat=i;
break;
}
}
}
if (!foundString) return;
UndoRegion ur;
if (mode==GUI_PASTE_MODE_OVERFLOW) {
int rows=cursor.y;
int firstPattern=curOrder;
int lastPattern=curOrder;
rows+=data.size();
while (rows>=e->curSubSong->patLen) {
lastPattern++;
rows-=e->curSubSong->patLen;
}
ur=UndoRegion(firstPattern,0,0,lastPattern,e->getTotalChannelCount()-1,e->curSubSong->patLen-1);
}
if (readClipboard) {
prepareUndo(GUI_UNDO_PATTERN_PASTE,ur);
}
if (isFurnace) {
doPasteFurnace(mode,arg,readClipboard,clipb,data,startOff,invalidData,ur);
} else if (isModPlug) {
doPasteMPT(mode,arg,readClipboard,clipb,data,mptFormat,ur);
}
}
@ -967,6 +1665,10 @@ void FurnaceGUI::doExpand(int multiplier, const SelectionPoint& sStart, const Se
void FurnaceGUI::doCollapseSong(int divider) {
if (divider<2) return;
if (e->curSubSong->patLen<divider) {
showError("can't collapse any further!");
return;
}
finishSelection();
UndoStep us;
@ -1188,6 +1890,7 @@ void FurnaceGUI::doUndo() {
setOrder(us.order);
}
}
e->walkSong(loopOrder,loopRow,loopEnd);
break;
}
@ -1209,8 +1912,6 @@ void FurnaceGUI::doUndo() {
if (curOrder>=e->curSubSong->ordersLen) {
curOrder=e->curSubSong->ordersLen-1;
oldOrder=curOrder;
oldOrder1=curOrder;
e->setOrder(curOrder);
}
@ -1265,7 +1966,7 @@ void FurnaceGUI::doRedo() {
setOrder(us.order);
}
}
e->walkSong(loopOrder,loopRow,loopEnd);
break;
}
@ -1287,8 +1988,6 @@ void FurnaceGUI::doRedo() {
if (curOrder>=e->curSubSong->ordersLen) {
curOrder=e->curSubSong->ordersLen-1;
oldOrder=curOrder;
oldOrder1=curOrder;
e->setOrder(curOrder);
}

View file

@ -1,6 +1,7 @@
#include "gui.h"
#include "guiConst.h"
#include <imgui.h>
#include "IconsFontAwesome4.h"
void FurnaceGUI::drawEffectList() {
if (nextWindow==GUI_WINDOW_EFFECT_LIST) {
@ -11,7 +12,30 @@ void FurnaceGUI::drawEffectList() {
if (!effectListOpen) return;
ImGui::SetNextWindowSizeConstraints(ImVec2(60.0f*dpiScale,20.0f*dpiScale),ImVec2(canvasW,canvasH));
if (ImGui::Begin("Effect List",&effectListOpen,globalWinFlags)) {
ImGui::Text("Chip at cursor: %s",e->getSystemName(e->sysOfChan[cursor.xCoarse]));
float availB=ImGui::GetContentRegionAvail().x-ImGui::GetFrameHeightWithSpacing();
if (availB>0) {
ImGui::PushTextWrapPos(availB);
ImGui::TextWrapped("Chip at cursor: %s",e->getSystemName(e->sysOfChan[cursor.xCoarse]));
ImGui::PopTextWrapPos();
}
effectSearch.Draw("Search");
ImGui::SameLine();
ImGui::Button(ICON_FA_BARS "##SortEffects");
if (ImGui::BeginPopupContextItem("effectSort",ImGuiPopupFlags_MouseButtonLeft)) {
ImGui::Text("Effect types to show:");
for (int i=1; i<10; i++) {
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[i+GUI_COLOR_PATTERN_EFFECT_INVALID]);
ImGui::Checkbox(fxColorsNames[i],&effectsShow[i]);
ImGui::PopStyleColor();
}
if (ImGui::Button("All")) memset(effectsShow,1,sizeof(bool)*10);
ImGui::SameLine();
if (ImGui::Button("None")) memset(effectsShow,0,sizeof(bool)*10);
ImGui::EndPopup();
}
if (ImGui::BeginTable("effectList",2)) {
ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthFixed);
ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthStretch);
@ -29,7 +53,22 @@ void FurnaceGUI::drawEffectList() {
continue;
}
prevName=name;
if (name!=NULL) {
if (fxColors[i]==GUI_COLOR_PATTERN_EFFECT_PANNING) {
DivDispatch* dispatch=e->getDispatch(e->dispatchOfChan[cursor.xCoarse]);
if (dispatch!=NULL) {
int outputs=dispatch->getOutputCount();
if (outputs<2) {
continue;
}
if (outputs<3) {
if (i>=0x88 && i<=0x8f) {
continue;
}
}
}
}
if (name==NULL) continue;
if (effectSearch.PassFilter(name) && effectsShow[fxColors[i]-GUI_COLOR_PATTERN_EFFECT_INVALID]) {
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::PushFont(patFont);

386
src/gui/exportOptions.cpp Normal file
View file

@ -0,0 +1,386 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2024 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "gui.h"
#include "guiConst.h"
#include "../fileutils.h"
#include "misc/cpp/imgui_stdlib.h"
#include <imgui.h>
void FurnaceGUI::drawExportAudio(bool onWindow) {
exitDisabledTimer=1;
ImGui::RadioButton("one file",&audioExportType,0);
ImGui::RadioButton("multiple files (one per chip)",&audioExportType,1);
ImGui::RadioButton("multiple files (one per channel)",&audioExportType,2);
if (ImGui::InputInt("Loops",&exportLoops,1,2)) {
if (exportLoops<0) exportLoops=0;
}
if (ImGui::InputDouble("Fade out (seconds)",&exportFadeOut,1.0,2.0,"%.1f")) {
if (exportFadeOut<0.0) exportFadeOut=0.0;
}
if (onWindow) {
ImGui::Separator();
if (ImGui::Button("Cancel",ImVec2(200.0f*dpiScale,0))) ImGui::CloseCurrentPopup();
ImGui::SameLine();
}
if (ImGui::Button("Export",ImVec2(200.0f*dpiScale,0))) {
switch (audioExportType) {
case 0:
openFileDialog(GUI_FILE_EXPORT_AUDIO_ONE);
break;
case 1:
openFileDialog(GUI_FILE_EXPORT_AUDIO_PER_SYS);
break;
case 2:
openFileDialog(GUI_FILE_EXPORT_AUDIO_PER_CHANNEL);
break;
}
ImGui::CloseCurrentPopup();
}
}
void FurnaceGUI::drawExportVGM(bool onWindow) {
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];
}
}
ImGui::EndCombo();
}
ImGui::Checkbox("loop",&vgmExportLoop);
if (vgmExportLoop && e->song.loopModality==2) {
ImGui::Text("loop trail:");
ImGui::Indent();
if (ImGui::RadioButton("auto-detect",vgmExportTrailingTicks==-1)) {
vgmExportTrailingTicks=-1;
}
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!");
}
} else {
if (willExport[i]) hasOneAtLeast=true;
}
}
ImGui::Text("select the chip you wish to export, but only up to %d of each type.",(vgmExportVersion>=0x151)?2:1);
if (hasOneAtLeast) {
if (onWindow) {
ImGui::Separator();
if (ImGui::Button("Cancel",ImVec2(200.0f*dpiScale,0))) ImGui::CloseCurrentPopup();
ImGui::SameLine();
}
if (ImGui::Button("Export",ImVec2(200.0f*dpiScale,0))) {
openFileDialog(GUI_FILE_EXPORT_VGM);
ImGui::CloseCurrentPopup();
}
} else {
ImGui::Text("nothing to export");
if (onWindow) {
ImGui::Separator();
if (ImGui::Button("Cancel",ImVec2(400.0f*dpiScale,0))) ImGui::CloseCurrentPopup();
}
}
}
void FurnaceGUI::drawExportZSM(bool onWindow) {
exitDisabledTimer=1;
ImGui::Text("Commander X16 Zsound Music File");
if (ImGui::InputInt("Tick Rate (Hz)",&zsmExportTickRate,1,2)) {
if (zsmExportTickRate<1) zsmExportTickRate=1;
if (zsmExportTickRate>44100) zsmExportTickRate=44100;
}
ImGui::Checkbox("loop",&zsmExportLoop);
ImGui::SameLine();
ImGui::Checkbox("optimize size",&zsmExportOptimize);
if (onWindow) {
ImGui::Separator();
if (ImGui::Button("Cancel",ImVec2(200.0f*dpiScale,0))) ImGui::CloseCurrentPopup();
ImGui::SameLine();
}
if (ImGui::Button("Export",ImVec2(200.0f*dpiScale,0))) {
openFileDialog(GUI_FILE_EXPORT_ZSM);
ImGui::CloseCurrentPopup();
}
}
void FurnaceGUI::drawExportAmigaVal(bool onWindow) {
exitDisabledTimer=1;
ImGui::Text(
"this is NOT ROM export! only use for making sure the\n"
"Furnace Amiga emulator is working properly by\n"
"comparing it with real Amiga output."
);
ImGui::AlignTextToFramePadding();
ImGui::Text("Directory");
ImGui::SameLine();
ImGui::InputText("##AVDPath",&workingDirROMExport);
if (onWindow) {
ImGui::Separator();
if (ImGui::Button("Cancel",ImVec2(200.0f*dpiScale,0))) ImGui::CloseCurrentPopup();
ImGui::SameLine();
}
if (ImGui::Button("Bake Data",ImVec2(200.0f*dpiScale,0))) {
std::vector<DivROMExportOutput> out=e->buildROM(DIV_ROM_AMIGA_VALIDATION);
if (workingDirROMExport.size()>0) {
if (workingDirROMExport[workingDirROMExport.size()-1]!=DIR_SEPARATOR) workingDirROMExport+=DIR_SEPARATOR_STR;
}
for (DivROMExportOutput& i: out) {
String path=workingDirROMExport+i.name;
FILE* outFile=ps_fopen(path.c_str(),"wb");
if (outFile!=NULL) {
fwrite(i.data->getFinalBuf(),1,i.data->size(),outFile);
fclose(outFile);
}
i.data->finish();
delete i.data;
}
showError(fmt::sprintf("Done! Baked %d files.",(int)out.size()));
ImGui::CloseCurrentPopup();
}
}
void FurnaceGUI::drawExportText(bool onWindow) {
exitDisabledTimer=1;
ImGui::Text(
"this option exports the song to a text file.\n"
);
if (onWindow) {
ImGui::Separator();
if (ImGui::Button("Cancel",ImVec2(200.0f*dpiScale,0))) ImGui::CloseCurrentPopup();
ImGui::SameLine();
}
if (ImGui::Button("Export",ImVec2(200.0f*dpiScale,0))) {
openFileDialog(GUI_FILE_EXPORT_TEXT);
ImGui::CloseCurrentPopup();
}
}
void FurnaceGUI::drawExportCommand(bool onWindow) {
exitDisabledTimer=1;
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 (onWindow) {
ImGui::Separator();
if (ImGui::Button("Cancel",ImVec2(200.0f*dpiScale,0))) ImGui::CloseCurrentPopup();
ImGui::SameLine();
}
if (ImGui::Button("Export",ImVec2(200.0f*dpiScale,0))) {
openFileDialog(GUI_FILE_EXPORT_CMDSTREAM);
ImGui::CloseCurrentPopup();
}
}
void FurnaceGUI::drawExportDMF(bool onWindow) {
exitDisabledTimer=1;
ImGui::Text(
"export in DefleMask module format.\n"
"only do it if you really, really need to, or are downgrading an existing .dmf."
);
ImGui::Text("format version:");
ImGui::RadioButton("1.1.3 and higher",&dmfExportVersion,0);
ImGui::RadioButton("1.0/legacy (0.12)",&dmfExportVersion,1);
if (onWindow) {
ImGui::Separator();
if (ImGui::Button("Cancel",ImVec2(200.0f*dpiScale,0))) ImGui::CloseCurrentPopup();
ImGui::SameLine();
}
if (ImGui::Button("Export",ImVec2(200.0f*dpiScale,0))) {
if (dmfExportVersion==1) {
openFileDialog(GUI_FILE_SAVE_DMF_LEGACY);
} else {
openFileDialog(GUI_FILE_SAVE_DMF);
}
ImGui::CloseCurrentPopup();
}
}
void FurnaceGUI::drawExport() {
if (settings.exportOptionsLayout==1 || curExportType==GUI_EXPORT_NONE) {
if (ImGui::BeginTabBar("ExportTypes")) {
if (ImGui::BeginTabItem("Audio")) {
drawExportAudio(true);
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("VGM")) {
drawExportVGM(true);
ImGui::EndTabItem();
}
int numZSMCompat=0;
for (int i=0; i<e->song.systemLen; i++) {
if ((e->song.system[i]==DIV_SYSTEM_VERA) || (e->song.system[i]==DIV_SYSTEM_YM2151)) numZSMCompat++;
}
if (numZSMCompat>0) {
if (ImGui::BeginTabItem("ZSM")) {
drawExportZSM(true);
ImGui::EndTabItem();
}
}
int numAmiga=0;
for (int i=0; i<e->song.systemLen; i++) {
if (e->song.system[i]==DIV_SYSTEM_AMIGA) numAmiga++;
}
if (numAmiga && settings.iCannotWait) {
if (ImGui::BeginTabItem("Amiga Validation")) {
drawExportAmigaVal(true);
ImGui::EndTabItem();
}
}
if (ImGui::BeginTabItem("Text")) {
drawExportText(true);
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("Command Stream")) {
drawExportCommand(true);
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("DMF")) {
drawExportDMF(true);
ImGui::EndTabItem();
}
ImGui::EndTabBar();
}
} else switch (curExportType) {
case GUI_EXPORT_AUDIO:
drawExportAudio(true);
break;
case GUI_EXPORT_VGM:
drawExportVGM(true);
break;
case GUI_EXPORT_ZSM:
drawExportZSM(true);
break;
case GUI_EXPORT_AMIGA_VAL:
drawExportAmigaVal(true);
break;
case GUI_EXPORT_TEXT:
drawExportText(true);
break;
case GUI_EXPORT_CMD_STREAM:
drawExportCommand(true);
break;
case GUI_EXPORT_DMF:
drawExportDMF(true);
break;
default:
ImGui::Text("congratulations! you've unlocked a secret panel.");
if (ImGui::Button("Toggle hidden systems")) {
settings.hiddenSystems=!settings.hiddenSystems;
ImGui::CloseCurrentPopup();
}
if (ImGui::Button("Toggle all instrument types")) {
settings.displayAllInsTypes=!settings.displayAllInsTypes;
ImGui::CloseCurrentPopup();
}
if (ImGui::Button("Set pitch linearity to Partial")) {
e->song.linearPitch=1;
ImGui::CloseCurrentPopup();
}
if (ImGui::Button("Enable multi-threading settings")) {
settings.showPool=1;
ImGui::CloseCurrentPopup();
}
if (ImGui::Button("Set fat to max")) {
ImGuiStyle& sty=ImGui::GetStyle();
sty.FramePadding=ImVec2(20.0f*dpiScale,20.0f*dpiScale);
sty.ItemSpacing=ImVec2(10.0f*dpiScale,10.0f*dpiScale);
sty.ItemInnerSpacing=ImVec2(10.0f*dpiScale,10.0f*dpiScale);
ImGui::CloseCurrentPopup();
}
if (ImGui::Button("Set muscle and fat to zero")) {
ImGuiStyle& sty=ImGui::GetStyle();
sty.FramePadding=ImVec2(0,0);
sty.ItemSpacing=ImVec2(0,0);
sty.ItemInnerSpacing=ImVec2(0,0);
ImGui::CloseCurrentPopup();
}
if (ImGui::Button("Tell tildearrow this must be a mistake")) {
showError("yeah, it's a bug. write a bug report in the GitHub page and tell me how did you get here.");
ImGui::CloseCurrentPopup();
}
break;
}
}

View file

@ -28,7 +28,6 @@ struct NFDState {
}
};
// TODO: filter
void _nfdThread(const NFDState state, std::atomic<bool>* ok, std::vector<String>* result, bool* errorOutput) {
nfdchar_t* out=NULL;
nfdresult_t ret=NFD_CANCEL;
@ -70,14 +69,48 @@ void _nfdThread(const NFDState state, std::atomic<bool>* ok, std::vector<String>
(*errorOutput)=true;
break;
default:
logE("NFD unknown return code %d!\n",ret);
logE("NFD unknown return code %d!\n",(int)ret);
break;
}
(*ok)=true;
}
#endif
bool FurnaceGUIFileDialog::openLoad(String header, std::vector<String> filter, const char* noSysFilter, String path, double dpiScale, FileDialogSelectCallback clickCallback, bool allowMultiple) {
void FurnaceGUIFileDialog::convertFilterList(std::vector<String>& filter) {
memset(noSysFilter,0,4096);
String result;
char sprintfBuf[4096];
for (size_t i=0; (i+1)<filter.size(); i+=2) {
String label=filter[i];
String ext;
if (filter[i+1]=="*") {
ext=".*";
} else for (char j: filter[i+1]) {
switch (j) {
case '*':
break;
case ' ':
ext+=',';
break;
default:
ext+=j;
break;
}
}
if (!result.empty()) result+=',';
// what the heck? fmt::sprintf not working?!
snprintf(sprintfBuf,4095,"%s{%s}",label.c_str(),ext.c_str());
result+=sprintfBuf;
}
strncpy(noSysFilter,result.c_str(),4095);
}
bool FurnaceGUIFileDialog::openLoad(String header, std::vector<String> filter, String path, double dpiScale, FileDialogSelectCallback clickCallback, bool allowMultiple) {
if (opened) return false;
saving=false;
curPath=path;
@ -150,6 +183,8 @@ bool FurnaceGUIFileDialog::openLoad(String header, std::vector<String> filter, c
}
#endif
convertFilterList(filter);
ImGuiFileDialog::Instance()->singleClickSel=mobileUI;
ImGuiFileDialog::Instance()->DpiScale=dpiScale;
ImGuiFileDialog::Instance()->mobileMode=mobileUI;
@ -160,7 +195,7 @@ bool FurnaceGUIFileDialog::openLoad(String header, std::vector<String> filter, c
return true;
}
bool FurnaceGUIFileDialog::openSave(String header, std::vector<String> filter, const char* noSysFilter, String path, double dpiScale) {
bool FurnaceGUIFileDialog::openSave(String header, std::vector<String> filter, String path, double dpiScale) {
if (opened) return false;
#ifdef ANDROID
@ -234,6 +269,8 @@ bool FurnaceGUIFileDialog::openSave(String header, std::vector<String> filter, c
} else {
hasError=false;
convertFilterList(filter);
ImGuiFileDialog::Instance()->singleClickSel=false;
ImGuiFileDialog::Instance()->DpiScale=dpiScale;
ImGuiFileDialog::Instance()->mobileMode=mobileUI;

View file

@ -1,7 +1,7 @@
#include "../ta-utils.h"
#include "imgui.h"
#include <functional>
#include <vector>
#include "../pch.h"
#if defined(_WIN64) || defined(__APPLE__)
#define USE_NFD
@ -31,6 +31,7 @@ class FurnaceGUIFileDialog {
bool opened;
bool saving;
bool hasError;
char noSysFilter[4096];
String curPath;
std::vector<String> fileName;
#ifdef USE_NFD
@ -46,10 +47,12 @@ class FurnaceGUIFileDialog {
pfd::open_file* dialogO;
pfd::save_file* dialogS;
#endif
void convertFilterList(std::vector<String>& filter);
public:
bool mobileUI;
bool openLoad(String header, std::vector<String> filter, const char* noSysFilter, String path, double dpiScale, FileDialogSelectCallback clickCallback=NULL, bool allowMultiple=false);
bool openSave(String header, std::vector<String> filter, const char* noSysFilter, String path, double dpiScale);
bool openLoad(String header, std::vector<String> filter, String path, double dpiScale, FileDialogSelectCallback clickCallback=NULL, bool allowMultiple=false);
bool openSave(String header, std::vector<String> filter, String path, double dpiScale);
bool accepted();
void close();
bool render(const ImVec2& min, const ImVec2& max);

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2023 tildearrow and contributors
* Copyright (C) 2021-2024 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -39,7 +39,7 @@ const char* queryReplaceModes[GUI_QUERY_REPLACE_MAX]={
"set",
"add",
"add (overflow)",
"scale",
"scale %",
"clear"
};
@ -103,6 +103,8 @@ void FurnaceGUI::doFind() {
int lastRow=e->curSubSong->patLen-1;
if (curQueryRangeY==1) {
finishSelection();
firstRow=selStart.y;
lastRow=selEnd.y;
}
@ -595,6 +597,7 @@ void FurnaceGUI::drawFindReplace() {
ImGui::TableSetupColumn("c3",ImGuiTableColumnFlags_WidthStretch,0.25);
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::AlignTextToFramePadding();
ImGui::Text("Note");
ImGui::TableNextColumn();
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
@ -662,153 +665,90 @@ void FurnaceGUI::drawFindReplace() {
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::AlignTextToFramePadding();
ImGui::Text("Ins");
ImGui::TableNextColumn();
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
ImGui::Combo("##ICondition",&i.insMode,queryModes,GUI_QUERY_MAX);
ImGui::TableNextColumn();
if (FIRST_VISIBLE(i.insMode)) {
snprintf(tempID,1024,"%.2X",i.ins);
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
if (ImGui::BeginCombo("II1",tempID)) {
for (int j=0; j<256; j++) {
snprintf(tempID,1024,"%.2X",j);
if (ImGui::Selectable(tempID,i.ins==j)) {
i.ins=j;
}
}
ImGui::EndCombo();
}
ImGui::InputScalar("##II1",ImGuiDataType_U8,&i.ins,NULL,NULL,"%.2X",ImGuiInputTextFlags_CharsHexadecimal);
}
ImGui::TableNextColumn();
if (SECOND_VISIBLE(i.insMode)) {
snprintf(tempID,1024,"%.2X",i.insMax);
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
if (ImGui::BeginCombo("II2",tempID)) {
for (int j=0; j<256; j++) {
snprintf(tempID,1024,"%.2X",j);
if (ImGui::Selectable(tempID,i.insMax==j)) {
i.insMax=j;
}
}
ImGui::EndCombo();
}
ImGui::InputScalar("##II2",ImGuiDataType_U8,&i.insMax,NULL,NULL,"%.2X",ImGuiInputTextFlags_CharsHexadecimal);
}
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::AlignTextToFramePadding();
ImGui::Text("Volume");
ImGui::TableNextColumn();
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
ImGui::Combo("##VCondition",&i.volMode,queryModes,GUI_QUERY_MAX);
ImGui::TableNextColumn();
if (FIRST_VISIBLE(i.volMode)) {
snprintf(tempID,1024,"%.2X",i.vol);
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
if (ImGui::BeginCombo("VV1",tempID)) {
for (int j=0; j<256; j++) {
snprintf(tempID,1024,"%.2X",j);
if (ImGui::Selectable(tempID,i.vol==j)) {
i.vol=j;
}
}
ImGui::EndCombo();
}
ImGui::InputScalar("##VV1",ImGuiDataType_U8,&i.vol,NULL,NULL,"%.2X",ImGuiInputTextFlags_CharsHexadecimal);
}
ImGui::TableNextColumn();
if (SECOND_VISIBLE(i.volMode)) {
snprintf(tempID,1024,"%.2X",i.volMax);
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
if (ImGui::BeginCombo("VV2",tempID)) {
for (int j=0; j<256; j++) {
snprintf(tempID,1024,"%.2X",j);
if (ImGui::Selectable(tempID,i.volMax==j)) {
i.volMax=j;
}
}
ImGui::EndCombo();
}
ImGui::InputScalar("##VV2",ImGuiDataType_U8,&i.volMax,NULL,NULL,"%.2X",ImGuiInputTextFlags_CharsHexadecimal);
}
for (int j=0; j<i.effectCount; j++) {
ImGui::PushID(0x1000+j);
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::AlignTextToFramePadding();
ImGui::Text("Effect");
ImGui::TableNextColumn();
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
ImGui::Combo("##ECondition",&i.effectMode[j],queryModes,GUI_QUERY_MAX);
ImGui::TableNextColumn();
if (FIRST_VISIBLE(i.effectMode[j])) {
snprintf(tempID,1024,"%.2X",i.effect[j]);
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
if (ImGui::BeginCombo("EE1",tempID)) {
for (int k=0; k<256; k++) {
snprintf(tempID,1024,"%.2X",k);
if (ImGui::Selectable(tempID,i.effect[j]==k)) {
i.effect[j]=k;
}
}
ImGui::EndCombo();
}
ImGui::InputScalar("##EE1",ImGuiDataType_U8,&i.effect[j],NULL,NULL,"%.2X",ImGuiInputTextFlags_CharsHexadecimal);
}
ImGui::TableNextColumn();
if (SECOND_VISIBLE(i.effectMode[j])) {
snprintf(tempID,1024,"%.2X",i.effectMax[j]);
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
if (ImGui::BeginCombo("EE2",tempID)) {
for (int k=0; k<256; k++) {
snprintf(tempID,1024,"%.2X",k);
if (ImGui::Selectable(tempID,i.effectMax[j]==k)) {
i.effectMax[j]=k;
}
}
ImGui::EndCombo();
}
ImGui::InputScalar("##EE2",ImGuiDataType_U8,&i.effectMax[j],NULL,NULL,"%.2X",ImGuiInputTextFlags_CharsHexadecimal);
}
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::AlignTextToFramePadding();
ImGui::Text("Value");
ImGui::TableNextColumn();
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
ImGui::Combo("##EVCondition",&i.effectValMode[j],queryModes,GUI_QUERY_MAX);
ImGui::TableNextColumn();
if (FIRST_VISIBLE(i.effectValMode[j])) {
snprintf(tempID,1024,"%.2X",i.effectVal[j]);
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
if (ImGui::BeginCombo("EV1",tempID)) {
for (int k=0; k<256; k++) {
snprintf(tempID,1024,"%.2X",k);
if (ImGui::Selectable(tempID,i.effectVal[j]==k)) {
i.effectVal[j]=k;
}
}
ImGui::EndCombo();
}
ImGui::InputScalar("##EV1",ImGuiDataType_U8,&i.effectVal[j],NULL,NULL,"%.2X",ImGuiInputTextFlags_CharsHexadecimal);
}
ImGui::TableNextColumn();
if (SECOND_VISIBLE(i.effectValMode[j])) {
snprintf(tempID,1024,"%.2X",i.effectValMax[j]);
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
if (ImGui::BeginCombo("EV2",tempID)) {
for (int k=0; k<256; k++) {
snprintf(tempID,1024,"%.2X",k);
if (ImGui::Selectable(tempID,i.effectValMax[j]==k)) {
i.effectValMax[j]=k;
}
}
ImGui::EndCombo();
}
ImGui::InputScalar("##EV2",ImGuiDataType_U8,&i.effectValMax[j],NULL,NULL,"%.2X",ImGuiInputTextFlags_CharsHexadecimal);
}
ImGui::PopID();
}
ImGui::TableNextRow();
ImGui::TableNextColumn();
pushDestColor();
if (ImGui::Button(ICON_FA_MINUS "##DelQuery")) {
eraseIndex=index;
}
popDestColor();
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Delete query");
}
ImGui::TableNextColumn();
if (i.effectCount<8) {
if (ImGui::Button("Add effect")) {
@ -817,9 +757,11 @@ void FurnaceGUI::drawFindReplace() {
}
ImGui::TableNextColumn();
if (i.effectCount>0) {
pushDestColor();
if (ImGui::Button("Remove effect")) {
i.effectCount--;
}
popDestColor();
}
ImGui::EndTable();
}
@ -982,14 +924,14 @@ void FurnaceGUI::drawFindReplace() {
if (queryReplaceIns>255) queryReplaceIns=255;
}
} else if (queryReplaceInsMode==GUI_QUERY_REPLACE_ADD || queryReplaceInsMode==GUI_QUERY_REPLACE_ADD_OVERFLOW) {
if (ImGui::InputInt("##IRValue",&queryReplaceIns,1,12)) {
if (ImGui::InputInt("##IRValue",&queryReplaceIns,1,16)) {
if (queryReplaceIns<-255) queryReplaceIns=-255;
if (queryReplaceIns>255) queryReplaceIns=255;
}
} else if (queryReplaceInsMode==GUI_QUERY_REPLACE_SCALE) {
if (queryReplaceIns<0) queryReplaceIns=0;
if (queryReplaceIns>400) queryReplaceIns=400;
if (ImGui::InputInt("##IRValue",&queryReplaceIns,1,12)) {
if (ImGui::InputInt("##IRValue",&queryReplaceIns,1,10)) {
if (queryReplaceIns<0) queryReplaceIns=0;
if (queryReplaceIns>400) queryReplaceIns=400;
}
@ -1011,14 +953,14 @@ void FurnaceGUI::drawFindReplace() {
if (queryReplaceVol>255) queryReplaceVol=255;
}
} else if (queryReplaceVolMode==GUI_QUERY_REPLACE_ADD || queryReplaceVolMode==GUI_QUERY_REPLACE_ADD_OVERFLOW) {
if (ImGui::InputInt("##VRValue",&queryReplaceVol,1,12)) {
if (ImGui::InputInt("##VRValue",&queryReplaceVol,1,16)) {
if (queryReplaceVol<-255) queryReplaceVol=-255;
if (queryReplaceVol>255) queryReplaceVol=255;
}
} else if (queryReplaceVolMode==GUI_QUERY_REPLACE_SCALE) {
if (queryReplaceVol<0) queryReplaceVol=0;
if (queryReplaceVol>400) queryReplaceVol=400;
if (ImGui::InputInt("##VRValue",&queryReplaceVol,1,12)) {
if (ImGui::InputInt("##VRValue",&queryReplaceVol,1,10)) {
if (queryReplaceVol<0) queryReplaceVol=0;
if (queryReplaceVol>400) queryReplaceVol=400;
}
@ -1042,14 +984,14 @@ void FurnaceGUI::drawFindReplace() {
if (queryReplaceEffect[i]>255) queryReplaceEffect[i]=255;
}
} else if (queryReplaceEffectMode[i]==GUI_QUERY_REPLACE_ADD || queryReplaceEffectMode[i]==GUI_QUERY_REPLACE_ADD_OVERFLOW) {
if (ImGui::InputInt("##ERValue",&queryReplaceEffect[i],1,12)) {
if (ImGui::InputInt("##ERValue",&queryReplaceEffect[i],1,16)) {
if (queryReplaceEffect[i]<-255) queryReplaceEffect[i]=-255;
if (queryReplaceEffect[i]>255) queryReplaceEffect[i]=255;
}
} else if (queryReplaceEffectMode[i]==GUI_QUERY_REPLACE_SCALE) {
if (queryReplaceEffect[i]<0) queryReplaceEffect[i]=0;
if (queryReplaceEffect[i]>400) queryReplaceEffect[i]=400;
if (ImGui::InputInt("##ERValue",&queryReplaceEffect[i],1,12)) {
if (ImGui::InputInt("##ERValue",&queryReplaceEffect[i],1,10)) {
if (queryReplaceEffect[i]<0) queryReplaceEffect[i]=0;
if (queryReplaceEffect[i]>400) queryReplaceEffect[i]=400;
}
@ -1071,14 +1013,14 @@ void FurnaceGUI::drawFindReplace() {
if (queryReplaceEffectVal[i]>255) queryReplaceEffectVal[i]=255;
}
} else if (queryReplaceEffectValMode[i]==GUI_QUERY_REPLACE_ADD || queryReplaceEffectValMode[i]==GUI_QUERY_REPLACE_ADD_OVERFLOW) {
if (ImGui::InputInt("##ERValueV",&queryReplaceEffectVal[i],1,12)) {
if (ImGui::InputInt("##ERValueV",&queryReplaceEffectVal[i],1,16)) {
if (queryReplaceEffectVal[i]<-255) queryReplaceEffectVal[i]=-255;
if (queryReplaceEffectVal[i]>255) queryReplaceEffectVal[i]=255;
}
} else if (queryReplaceEffectValMode[i]==GUI_QUERY_REPLACE_SCALE) {
if (queryReplaceEffectVal[i]<0) queryReplaceEffectVal[i]=0;
if (queryReplaceEffectVal[i]>400) queryReplaceEffectVal[i]=400;
if (ImGui::InputInt("##ERValueV",&queryReplaceEffectVal[i],1,12)) {
if (ImGui::InputInt("##ERValueV",&queryReplaceEffectVal[i],1,10)) {
if (queryReplaceEffectVal[i]<0) queryReplaceEffectVal[i]=0;
if (queryReplaceEffectVal[i]>400) queryReplaceEffectVal[i]=400;
}
@ -1098,9 +1040,11 @@ void FurnaceGUI::drawFindReplace() {
}
ImGui::TableNextColumn();
if (queryReplaceEffectCount>0) {
pushDestColor();
if (ImGui::Button("Remove effect")) {
queryReplaceEffectCount--;
}
popDestColor();
}
ImGui::EndTable();

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2023 tildearrow and contributors
* Copyright (C) 2021-2024 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -20,8 +20,15 @@
#define _USE_MATH_DEFINES
#include "gui.h"
#include "../../extern/opn/ym3438.h"
#include "../../extern/opm/opm.h"
#include "../../extern/opl/opl3.h"
#include "../../extern/ESFMu/esfm.h"
extern "C" {
#include "../../extern/Nuked-OPLL/opll.h"
}
#include "../engine/platform/sound/ymfm/ymfm_opz.h"
#define FM_WRITE(addr,val) \
#define OPN_WRITE(addr,val) \
OPN2_Write((ym3438_t*)fmPreviewOPN,0,(addr)); \
do { \
OPN2_Clock((ym3438_t*)fmPreviewOPN,out); \
@ -29,22 +36,23 @@
OPN2_Write((ym3438_t*)fmPreviewOPN,1,(val)); \
do { \
OPN2_Clock((ym3438_t*)fmPreviewOPN,out); \
} while (((ym3438_t*)fmPreviewOPN)->write_busy); \
} while (((ym3438_t*)fmPreviewOPN)->write_busy);
const unsigned char dtTableFMP[8]={
7,6,5,0,1,2,3,4
};
void FurnaceGUI::renderFMPreview(const DivInstrumentFM& params, int pos) {
void FurnaceGUI::renderFMPreviewOPN(const DivInstrumentFM& params, int pos) {
if (fmPreviewOPN==NULL) {
fmPreviewOPN=new ym3438_t;
pos=0;
}
short out[2];
int aOut=0;
bool mult0=false;
if (pos==0) {
OPN2_Reset((ym3438_t*)fmPreviewOPN);
OPN2_Reset((ym3438_t*)fmPreviewOPN);
OPN2_SetChipType((ym3438_t*)fmPreviewOPN,ym3438_mode_opn);
// set params
@ -57,19 +65,25 @@ void FurnaceGUI::renderFMPreview(const DivInstrumentFM& params, int pos) {
for (int i=0; i<4; i++) {
const DivInstrumentFM::Operator& op=params.op[i];
unsigned short baseAddr=i*4;
FM_WRITE(baseAddr+0x40,op.tl);
FM_WRITE(baseAddr+0x30,(op.mult&15)|(dtTableFMP[op.dt&7]<<4));
FM_WRITE(baseAddr+0x50,(op.ar&31)|(op.rs<<6));
FM_WRITE(baseAddr+0x60,(op.dr&31)|(op.am<<7));
FM_WRITE(baseAddr+0x70,op.d2r&31);
FM_WRITE(baseAddr+0x80,(op.rr&15)|(op.sl<<4));
FM_WRITE(baseAddr+0x90,op.ssgEnv&15);
OPN_WRITE(baseAddr+0x40,op.tl);
OPN_WRITE(baseAddr+0x30,(op.mult&15)|(dtTableFMP[op.dt&7]<<4));
OPN_WRITE(baseAddr+0x50,(op.ar&31)|(op.rs<<6));
OPN_WRITE(baseAddr+0x60,(op.dr&31)|(op.am<<7));
OPN_WRITE(baseAddr+0x70,op.d2r&31);
OPN_WRITE(baseAddr+0x80,(op.rr&15)|(op.sl<<4));
OPN_WRITE(baseAddr+0x90,op.ssgEnv&15);
}
FM_WRITE(0xb0,(params.alg&7)|((params.fb&7)<<3));
FM_WRITE(0xb4,0xc0|(params.fms&7)|((params.ams&3)<<4));
FM_WRITE(0xa4,mult0?0x1c:0x14); // frequency
FM_WRITE(0xa0,0);
FM_WRITE(0x28,0xf0); // key on
OPN_WRITE(0xb0,(params.alg&7)|((params.fb&7)<<3));
OPN_WRITE(0xb4,0xc0|(params.fms&7)|((params.ams&3)<<4));
OPN_WRITE(0xa4,mult0?0x1c:0x14); // frequency
OPN_WRITE(0xa0,0);
OPN_WRITE(
0x28,
(params.op[0].enable?0x10:0)|
(params.op[2].enable?0x20:0)|
(params.op[1].enable?0x40:0)|
(params.op[3].enable?0x80:0)
); // key on
}
// render
@ -84,3 +98,348 @@ void FurnaceGUI::renderFMPreview(const DivInstrumentFM& params, int pos) {
fmPreview[i]=aOut;
}
}
#define OPM_WRITE(addr,val) \
OPM_Write((opm_t*)fmPreviewOPM,0,(addr)); \
do { \
OPM_Clock((opm_t*)fmPreviewOPM,out,NULL,NULL,NULL); \
OPM_Clock((opm_t*)fmPreviewOPM,out,NULL,NULL,NULL); \
} while (((opm_t*)fmPreviewOPM)->write_busy); \
OPM_Write((opm_t*)fmPreviewOPM,1,(val)); \
do { \
OPM_Clock((opm_t*)fmPreviewOPM,out,NULL,NULL,NULL); \
OPM_Clock((opm_t*)fmPreviewOPM,out,NULL,NULL,NULL); \
} while (((opm_t*)fmPreviewOPM)->write_busy);
void FurnaceGUI::renderFMPreviewOPM(const DivInstrumentFM& params, int pos) {
if (fmPreviewOPM==NULL) {
fmPreviewOPM=new opm_t;
pos=0;
}
int out[2];
int aOut=0;
bool mult0=false;
if (pos==0) {
OPM_Reset((opm_t*)fmPreviewOPM);
// set params
for (int i=0; i<4; i++) {
if ((params.op[i].mult&15)==0) {
mult0=true;
break;
}
}
for (int i=0; i<4; i++) {
const DivInstrumentFM::Operator& op=params.op[i];
unsigned short baseAddr=i*8;
OPM_WRITE(baseAddr+0x40,(op.mult&15)|(dtTableFMP[op.dt&7]<<4));
OPM_WRITE(baseAddr+0x60,op.tl);
OPM_WRITE(baseAddr+0x80,(op.ar&31)|(op.rs<<6));
OPM_WRITE(baseAddr+0xa0,(op.dr&31)|(op.am<<7));
OPM_WRITE(baseAddr+0xc0,(op.d2r&31)|(op.dt2<<6));
OPM_WRITE(baseAddr+0xe0,(op.rr&15)|(op.sl<<4));
}
OPM_WRITE(0x20,(params.alg&7)|((params.fb&7)<<3)|0xc0);
OPM_WRITE(0x38,((params.fms&7)<<4)|(params.ams&3));
OPM_WRITE(0x28,mult0?0x39:0x29); // frequency
OPM_WRITE(0x30,0xe6);
OPM_WRITE(
0x08,
(params.op[0].enable?0x08:0)|
(params.op[2].enable?0x10:0)|
(params.op[1].enable?0x20:0)|
(params.op[3].enable?0x40:0)
); // key on
}
// render
for (int i=0; i<FM_PREVIEW_SIZE; i++) {
aOut=0;
for (int j=0; j<32; j++) {
OPM_Clock((opm_t*)fmPreviewOPM,out,NULL,NULL,NULL);
}
aOut+=out[0];
if (aOut<-32768) aOut=-32768;
if (aOut>32767) aOut=32767;
fmPreview[i]=aOut;
}
}
#define OPLL_WRITE(addr,val) \
OPLL_Write((opll_t*)fmPreviewOPLL,0,(addr)); \
for (int _i=0; _i<3; _i++) { \
OPLL_Clock((opll_t*)fmPreviewOPLL,out); \
} \
OPLL_Write((opll_t*)fmPreviewOPLL,1,(val)); \
for (int _i=0; _i<21; _i++) { \
OPLL_Clock((opll_t*)fmPreviewOPLL,out); \
}
void FurnaceGUI::renderFMPreviewOPLL(const DivInstrumentFM& params, int pos) {
if (fmPreviewOPLL==NULL) {
fmPreviewOPLL=new opm_t;
pos=0;
}
int out[2];
int aOut=0;
bool mult0=false;
if (pos==0) {
OPLL_Reset((opll_t*)fmPreviewOPLL,opll_type_ym2413);
// set params
const DivInstrumentFM::Operator& mod=params.op[0];
const DivInstrumentFM::Operator& car=params.op[1];
if (params.opllPreset==0) {
for (int i=0; i<2; i++) {
if ((params.op[i].mult&15)==0) {
mult0=true;
break;
}
}
OPLL_WRITE(0x00,(mod.am<<7)|(mod.vib<<6)|((mod.ssgEnv&8)<<2)|(mod.ksr<<4)|(mod.mult));
OPLL_WRITE(0x01,(car.am<<7)|(car.vib<<6)|((car.ssgEnv&8)<<2)|(car.ksr<<4)|(car.mult));
OPLL_WRITE(0x02,(mod.ksl<<6)|(mod.tl&63));
OPLL_WRITE(0x03,(car.ksl<<6)|((params.fms&1)<<4)|((params.ams&1)<<3)|(params.fb&7));
OPLL_WRITE(0x04,(mod.ar<<4)|(mod.dr));
OPLL_WRITE(0x05,(car.ar<<4)|(car.dr));
OPLL_WRITE(0x06,(mod.sl<<4)|(mod.rr));
OPLL_WRITE(0x07,(car.sl<<4)|(car.rr));
}
OPLL_WRITE(0x10,0);
OPLL_WRITE(0x30,(params.opllPreset<<4)|(car.tl&15));
OPLL_WRITE(0x20,(params.alg?0x20:0)|(mult0?0x15:0x13));
}
// render
for (int i=0; i<FM_PREVIEW_SIZE; i++) {
aOut=0;
for (int j=0; j<36; j++) {
OPLL_Clock((opll_t*)fmPreviewOPLL,out);
aOut+=out[0]<<4;
}
if (aOut<-32768) aOut=-32768;
if (aOut>32767) aOut=32767;
fmPreview[i]=aOut;
}
}
#define OPL_WRITE(addr,val) \
OPL3_WriteReg((opl3_chip*)fmPreviewOPL,(addr),(val)); \
OPL3_Generate4Ch((opl3_chip*)fmPreviewOPL,out);
const unsigned char lPreviewSlots[4]={
0, 3, 8, 11
};
const unsigned char lOpMap[4]={
0, 2, 1, 3
};
void FurnaceGUI::renderFMPreviewOPL(const DivInstrumentFM& params, int pos) {
if (fmPreviewOPL==NULL) {
fmPreviewOPL=new opl3_chip;
pos=0;
}
short out[4];
bool mult0=false;
if (pos==0) {
OPL3_Reset((opl3_chip*)fmPreviewOPL,49716);
// set params
int ops=(params.ops==4)?4:2;
for (int i=0; i<ops; i++) {
if ((params.op[i].mult&15)==0) {
mult0=true;
break;
}
}
OPL_WRITE(0x01,0x20);
OPL_WRITE(0x105,1);
if (ops==4) {
OPL_WRITE(0x104,1);
}
for (int i=0; i<ops; i++) {
const DivInstrumentFM::Operator& op=params.op[(ops==4)?lOpMap[i]:i];
unsigned short baseAddr=lPreviewSlots[i];
OPL_WRITE(baseAddr+0x40,op.tl|(op.ksl<<6));
OPL_WRITE(baseAddr+0x20,(op.am<<7)|(op.vib<<6)|(op.sus<<5)|(op.ksr<<4)|op.mult);
OPL_WRITE(baseAddr+0x60,(op.ar<<4)|op.dr);
OPL_WRITE(baseAddr+0x80,(op.sl<<4)|op.rr);
OPL_WRITE(baseAddr+0xe0,op.ws&7);
}
OPL_WRITE(0xc0,(params.alg&1)|(params.fb<<1)|0x10);
if (ops==4) {
OPL_WRITE(0xc3,((params.alg>>1)&1)|(params.fb<<1)|0x10);
}
OPL_WRITE(0xa0,0);
if (ops==4) {
OPL_WRITE(0xa3,0);
}
OPL_WRITE(0xb0,mult0?0x2a:0x26);
if (ops==4) {
OPL_WRITE(0xb3,mult0?0x2a:0x26);
}
}
// render
for (int i=0; i<FM_PREVIEW_SIZE; i++) {
OPL3_Generate4Ch((opl3_chip*)fmPreviewOPL,out);
OPL3_Generate4Ch((opl3_chip*)fmPreviewOPL,out);
fmPreview[i]=CLAMP(out[0]*2,-32768,32767);
}
}
#define OPZ_WRITE(addr,val) \
((ymfm::ym2414*)fmPreviewOPZ)->write(0,(addr)); \
((ymfm::ym2414*)fmPreviewOPZ)->write(1,(val)); \
((ymfm::ym2414*)fmPreviewOPZ)->generate(&out,1);
void FurnaceGUI::renderFMPreviewOPZ(const DivInstrumentFM& params, int pos) {
if (fmPreviewOPZ==NULL) {
fmPreviewOPZInterface=new ymfm::ymfm_interface();
fmPreviewOPZ=new ymfm::ym2414(*(ymfm::ymfm_interface*)fmPreviewOPZInterface);
pos=0;
}
ymfm::ymfm_output<2> out;
int aOut=0;
bool mult0=false;
if (pos==0) {
((ymfm::ym2414*)fmPreviewOPZ)->reset();
// set params
for (int i=0; i<4; i++) {
if ((params.op[i].mult&15)==0) {
mult0=true;
break;
}
}
for (int i=0; i<4; i++) {
const DivInstrumentFM::Operator& op=params.op[i];
unsigned short baseAddr=i*8;
OPZ_WRITE(baseAddr+0x40,(op.mult&15)|((op.egt?(op.dt&7):dtTableFMP[op.dt&7])<<4));
OPZ_WRITE(baseAddr+0x40,(op.dvb&15)|((op.ws&7)<<4)|0x80);
OPZ_WRITE(baseAddr+0x60,op.tl);
OPZ_WRITE(baseAddr+0x80,(op.ar&31)|(op.egt<<5)|(op.rs<<6));
OPZ_WRITE(baseAddr+0xa0,(op.dr&31)|(op.am<<7));
OPZ_WRITE(baseAddr+0xc0,(op.d2r&31)|(op.dt2<<6));
OPZ_WRITE(baseAddr+0xc0,(op.dam&7)|(op.ksl<<6)|0x20);
OPZ_WRITE(baseAddr+0xe0,(op.rr&15)|(op.sl<<4));
}
OPZ_WRITE(0x38,((params.fms&7)<<4)|(params.ams&3));
OPZ_WRITE(0x38,((params.fms2&7)<<4)|(params.ams2&3)|0x84);
OPZ_WRITE(0x28,mult0?0x39:0x29); // frequency
OPZ_WRITE(0x30,0xe7);
OPZ_WRITE(0x20,(params.alg&7)|((params.fb&7)<<3)|0x40); // key on
}
// render
for (int i=0; i<FM_PREVIEW_SIZE; i++) {
aOut=0;
((ymfm::ym2414*)fmPreviewOPZ)->generate(&out,1);
aOut+=out.data[0];
if (aOut<-32768) aOut=-32768;
if (aOut>32767) aOut=32767;
fmPreview[i]=aOut;
}
}
#define ESFM_WRITE(addr,val) \
ESFM_write_reg_buffered_fast((esfm_chip*)fmPreviewESFM,(addr),(val))
void FurnaceGUI::renderFMPreviewESFM(const DivInstrumentFM& params, const DivInstrumentESFM& esfmParams, int pos) {
if (fmPreviewESFM==NULL) {
fmPreviewESFM=new esfm_chip;
pos=0;
}
short out[4];
bool mult0=false;
if (pos==0) {
ESFM_init((esfm_chip*)fmPreviewESFM,0);
// set native mode
ESFM_WRITE(0x105, 0x80);
// set params
for (int i=0; i<4; i++) {
if ((params.op[i].mult&15)==0) {
mult0=true;
break;
}
}
for (int i=0; i<4; i++) {
const DivInstrumentFM::Operator& op=params.op[i];
const DivInstrumentESFM::Operator& opE=esfmParams.op[i];
unsigned short baseAddr=i*8;
unsigned char freqL, freqH;
if (opE.fixed) {
freqL=opE.dt;
freqH=opE.ct&0x1f;
} else {
// perform detune calculation
int offset=(opE.ct<<7)+opE.dt;
double fbase=(mult0?2048.0:1024.0)*pow(2.0,(float)offset/(128.0*12.0));
int bf=round(fbase);
int block=0;
while (bf>0x3ff) {
bf>>=1;
block++;
}
freqL=bf&0xff;
freqH=((block&7)<<2)|((bf>>8)&3);
}
ESFM_WRITE(baseAddr+0,(op.am<<7)|((op.vib&1)<<6)|((op.sus&1)<<5)|((op.ksr&1)<<4)|(op.mult&0x0f));
ESFM_WRITE(baseAddr+1,(op.ksl<<6)|(op.tl&0x3f));
ESFM_WRITE(baseAddr+2,(op.ar<<4)|(op.dr&0x0f));
ESFM_WRITE(baseAddr+3,(op.sl<<4)|(op.rr&0x0f));
ESFM_WRITE(baseAddr+4,freqL);
ESFM_WRITE(baseAddr+5,(opE.delay<<5)|freqH);
ESFM_WRITE(baseAddr+6,(op.dam<<7)|((op.dvb&1)<<6)|((opE.right&1)<<5)|((opE.left&1)<<4)|((opE.modIn&7)<<1));
ESFM_WRITE(baseAddr+7,(opE.outLvl<<5)|((i==3?esfmParams.noise:0)<<3)|(op.ws&7));
}
}
// note on
ESFM_WRITE(0x240, 1);
// render
for (int i=0; i<FM_PREVIEW_SIZE; i++) {
ESFM_generate((esfm_chip*)fmPreviewESFM,out);
ESFM_generate((esfm_chip*)fmPreviewESFM,out);
fmPreview[i]=CLAMP(out[0]+out[1],-32768,32767);
}
}
void FurnaceGUI::renderFMPreview(const DivInstrument* ins, int pos) {
switch (ins->type) {
case DIV_INS_FM:
renderFMPreviewOPN(ins->fm,pos);
break;
case DIV_INS_OPM:
renderFMPreviewOPM(ins->fm,pos);
break;
case DIV_INS_OPLL:
renderFMPreviewOPLL(ins->fm,pos);
break;
case DIV_INS_OPL:
renderFMPreviewOPL(ins->fm,pos);
break;
case DIV_INS_OPZ:
renderFMPreviewOPZ(ins->fm,pos);
break;
case DIV_INS_ESFM:
renderFMPreviewESFM(ins->fm,ins->esfm,pos);
default:
break;
}
}

File diff suppressed because it is too large Load diff

1237
src/gui/font_furicon.cpp Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,209 +1,501 @@
// papers/proggy-license.txt
// File: 'ProggyClean.ttf' (41208 bytes)
// Exported using binary_to_compressed_c.cpp
#include "fonts.h"
const unsigned int font_proggyClean_compressed_size = 9583;
const unsigned int font_proggyClean_compressed_data[9584/4] =
{
0x0000bc57, 0x00000000, 0xf8a00000, 0x00000400, 0x00010037, 0x000c0000, 0x00030080, 0x2f534f40, 0x74eb8832, 0x01000090, 0x2c158248, 0x616d634e,
0x23120270, 0x03000075, 0x241382a0, 0x74766352, 0x82178220, 0xfc042102, 0x02380482, 0x66796c67, 0x5689af12, 0x04070000, 0x80920000, 0x64616568,
0xd36691d7, 0xcc201b82, 0x36210382, 0x27108268, 0xc3014208, 0x04010000, 0x243b0f82, 0x78746d68, 0x807e008a, 0x98010000, 0x06020000, 0x61636f6c,
0xd8b0738c, 0x82050000, 0x0402291e, 0x7078616d, 0xda00ae01, 0x28201f82, 0x202c1082, 0x656d616e, 0x96bb5925, 0x84990000, 0x9e2c1382, 0x74736f70,
0xef83aca6, 0x249b0000, 0xd22c3382, 0x70657270, 0x12010269, 0xf4040000, 0x08202f82, 0x012ecb84, 0x553c0000, 0x0f5fd5e9, 0x0300f53c, 0x00830008,
0x7767b722, 0x002b3f82, 0xa692bd00, 0xfe0000d7, 0x83800380, 0x21f1826f, 0x00850002, 0x41820120, 0x40fec026, 0x80030000, 0x05821083, 0x07830120,
0x0221038a, 0x24118200, 0x90000101, 0x82798200, 0x00022617, 0x00400008, 0x2009820a, 0x82098276, 0x82002006, 0x9001213b, 0x0223c883, 0x828a02bc,
0x858f2010, 0xc5012507, 0x00023200, 0x04210083, 0x91058309, 0x6c412b03, 0x40007374, 0xac200000, 0x00830008, 0x01000523, 0x834d8380, 0x80032103,
0x012101bf, 0x23b88280, 0x00800000, 0x0b830382, 0x07820120, 0x83800021, 0x88012001, 0x84002009, 0x2005870f, 0x870d8301, 0x2023901b, 0x83199501,
0x82002015, 0x84802000, 0x84238267, 0x88002027, 0x8561882d, 0x21058211, 0x13880000, 0x01800022, 0x05850d85, 0x0f828020, 0x03208384, 0x03200582,
0x47901b84, 0x1b850020, 0x1f821d82, 0x3f831d88, 0x3f410383, 0x84058405, 0x210982cd, 0x09830000, 0x03207789, 0xf38a1384, 0x01203782, 0x13872384,
0x0b88c983, 0x0d898f84, 0x00202982, 0x23900383, 0x87008021, 0x83df8301, 0x86118d03, 0x863f880d, 0x8f35880f, 0x2160820f, 0x04830300, 0x1c220382,
0x05820100, 0x4c000022, 0x09831182, 0x04001c24, 0x11823000, 0x0800082e, 0x00000200, 0xff007f00, 0xffffac20, 0x00220982, 0x09848100, 0xdf216682,
0x843586d5, 0x06012116, 0x04400684, 0xa58120d7, 0x00b127d8, 0x01b88d01, 0x2d8685ff, 0xc100c621, 0xf4be0801, 0x9e011c01, 0x88021402, 0x1403fc02,
0x9c035803, 0x1404de03, 0x50043204, 0xa2046204, 0x66051605, 0x1206bc05, 0xd6067406, 0x7e073807, 0x4e08ec07, 0x96086c08, 0x1009d008, 0x88094a09,
0x800a160a, 0x560b040b, 0x2e0cc80b, 0xea0c820c, 0xa40d5e0d, 0x500eea0d, 0x280f960e, 0x1210b00f, 0xe0107410, 0xb6115211, 0x6e120412, 0x4c13c412,
0xf613ac13, 0xae145814, 0x4015ea14, 0xa6158015, 0x1216b815, 0xc6167e16, 0x8e173417, 0x5618e017, 0xee18ba18, 0x96193619, 0x481ad419, 0xf01a9c1a,
0xc81b5c1b, 0x4c1c041c, 0xea1c961c, 0x921d2a1d, 0x401ed21d, 0xe01e8e1e, 0x761f241f, 0xa61fa61f, 0x01821020, 0x8a202e34, 0xc820b220, 0x74211421,
0xee219821, 0x86226222, 0x01820c23, 0x83238021, 0x23983c01, 0x24d823b0, 0x244a2400, 0x24902468, 0x250625ae, 0x25822560, 0x26f825f8, 0x82aa2658,
0xd8be0801, 0x9a274027, 0x68280a28, 0x0e29a828, 0xb8292029, 0x362af829, 0x602a602a, 0x2a2b022b, 0xac2b5e2b, 0x202ce62b, 0x9a2c342c, 0x5c2d282d,
0xaa2d782d, 0x262ee82d, 0x262fa62e, 0xf42fb62f, 0xc8305e30, 0xb4313e31, 0x9e321e32, 0x82331e33, 0x5c34ee33, 0x3a35ce34, 0xd4358635, 0x72362636,
0x7637e636, 0x3a38d837, 0x1239a638, 0xae397439, 0x9a3a2e3a, 0x7c3b063b, 0x3a3ce83b, 0x223d963c, 0xec3d863d, 0xc63e563e, 0x9a3f2a3f, 0x6a401240,
0x3641d040, 0x0842a241, 0x7a424042, 0xf042b842, 0xcc436243, 0x8a442a44, 0x5845ee44, 0xe245b645, 0xb4465446, 0x7a471447, 0x5448da47, 0x4049c648,
0x15462400, 0x034d0808, 0x0b000700, 0x13000f00, 0x1b001700, 0x23001f00, 0x2b002700, 0x33002f00, 0x3b003700, 0x43003f00, 0x4b004700, 0x53004f00,
0x5b005700, 0x63005f00, 0x6b006700, 0x73006f00, 0x7b007700, 0x83007f00, 0x8b008700, 0x00008f00, 0x15333511, 0x20039631, 0x20178205, 0xd3038221,
0x20739707, 0x25008580, 0x028080fc, 0x05be8080, 0x04204a85, 0x05ce0685, 0x0107002a, 0x02000080, 0x00000400, 0x250d8b41, 0x33350100, 0x03920715,
0x13820320, 0x858d0120, 0x0e8d0320, 0xff260d83, 0x00808000, 0x54820106, 0x04800223, 0x845b8c80, 0x41332059, 0x078b068f, 0x82000121, 0x82fe2039,
0x84802003, 0x83042004, 0x23598a0e, 0x00180000, 0x03210082, 0x42ab9080, 0x73942137, 0x2013bb41, 0x8f978205, 0x2027a39b, 0x20b68801, 0x84b286fd,
0x91c88407, 0x41032011, 0x11a51130, 0x15000027, 0x80ff8000, 0x11af4103, 0x841b0341, 0x8bd983fd, 0x9be99bc9, 0x8343831b, 0x21f1821f, 0xb58300ff,
0x0f84e889, 0xf78a0484, 0x8000ff22, 0x0020eeb3, 0x14200082, 0x2130ef41, 0xeb431300, 0x4133200a, 0xd7410ecb, 0x9a07200b, 0x2027871b, 0x21238221,
0xe7828080, 0xe784fd20, 0xe8848020, 0xfe808022, 0x08880d85, 0xba41fd20, 0x82248205, 0x85eab02a, 0x008022e7, 0x2cd74200, 0x44010021, 0xd34406eb,
0x44312013, 0xcf8b0eef, 0x0d422f8b, 0x82332007, 0x0001212f, 0x8023cf82, 0x83000180, 0x820583de, 0x830682d4, 0x820020d4, 0x82dc850a, 0x20e282e9,
0xb2ff85fe, 0x010327e9, 0x02000380, 0x0f440400, 0x0c634407, 0x68825982, 0x85048021, 0x260a825d, 0x010b0000, 0x4400ff00, 0x2746103f, 0x08d74209,
0x4d440720, 0x0eaf4406, 0xc3441d20, 0x23078406, 0xff800002, 0x04845b83, 0x8d05b241, 0x1781436f, 0x6b8c87a5, 0x1521878e, 0x06474505, 0x01210783,
0x84688c00, 0x8904828e, 0x441e8cf7, 0x0b270cff, 0x80008000, 0x45030003, 0xfb430fab, 0x080f4107, 0x410bf942, 0xd34307e5, 0x070d4207, 0x80800123,
0x205d85fe, 0x849183fe, 0x20128404, 0x82809702, 0x00002217, 0x41839a09, 0x6b4408cf, 0x0733440f, 0x3b460720, 0x82798707, 0x97802052, 0x0000296f,
0xff800004, 0x01800100, 0x0021ef89, 0x0a914625, 0x410a4d41, 0x00250ed4, 0x00050000, 0x056d4280, 0x210a7b46, 0x21481300, 0x46ed8512, 0x00210bd1,
0x89718202, 0x21738877, 0x2b850001, 0x00220582, 0x87450a00, 0x0ddb4606, 0x41079b42, 0x9d420c09, 0x0b09420b, 0x8d820720, 0x9742fc84, 0x42098909,
0x00241e0f, 0x00800014, 0x0b47da82, 0x0833442a, 0x49078d41, 0x2f450f13, 0x42278f17, 0x01200751, 0x22063742, 0x44808001, 0x20450519, 0x88068906,
0x83fe2019, 0x4203202a, 0x1a941a58, 0x00820020, 0xe7a40e20, 0x420ce146, 0x854307e9, 0x0fcb4713, 0xff20a182, 0xfe209b82, 0x0c867f8b, 0x0021aea4,
0x219fa40f, 0x7d41003b, 0x07194214, 0xbf440520, 0x071d4206, 0x6941a590, 0x80802309, 0x028900ff, 0xa9a4b685, 0xc5808021, 0x449b82ab, 0x152007eb,
0x42134d46, 0x61440a15, 0x051e4208, 0x222b0442, 0x47001100, 0xfd412913, 0x17194714, 0x410f5b41, 0x02220773, 0x09428080, 0x21a98208, 0xd4420001,
0x481c840d, 0x00232bc9, 0x42120000, 0xe74c261b, 0x149d4405, 0x07209d87, 0x410db944, 0x14421c81, 0x42fd2005, 0x80410bd2, 0x203d8531, 0x06874100,
0x48256f4a, 0xcb420c95, 0x13934113, 0x44075d44, 0x044c0855, 0x00ff2105, 0xfe228185, 0x45448000, 0x22c5b508, 0x410c0000, 0x7b412087, 0x1bb74514,
0x32429c85, 0x0a574805, 0x21208943, 0x8ba01300, 0x440dfb4e, 0x77431437, 0x245b4113, 0x200fb145, 0x41108ffe, 0x80203562, 0x00200082, 0x46362b42,
0x1742178d, 0x4527830f, 0x0f830b2f, 0x4a138146, 0x802409a1, 0xfe8000ff, 0x94419982, 0x09294320, 0x04000022, 0x49050f4f, 0xcb470a63, 0x48032008,
0x2b48067b, 0x85022008, 0x82638338, 0x00002209, 0x05af4806, 0x900e9f49, 0x84c5873f, 0x214285bd, 0x064900ff, 0x0c894607, 0x00000023, 0x4903820a,
0x714319f3, 0x0749410c, 0x8a07a145, 0x02152507, 0xfe808000, 0x74490386, 0x8080211b, 0x0c276f82, 0x00018000, 0x48028003, 0x2b2315db, 0x43002f00,
0x6f82142f, 0x44011521, 0x93510da7, 0x20e68508, 0x06494d80, 0x8e838020, 0x06821286, 0x124bff20, 0x25f3830c, 0x03800080, 0xe74a0380, 0x207b8715,
0x876b861d, 0x4a152007, 0x07870775, 0xf6876086, 0x8417674a, 0x0a0021f2, 0x431c9743, 0x8d421485, 0x200b830b, 0x06474d03, 0x71828020, 0x04510120,
0x42da8606, 0x1f831882, 0x001a0022, 0xff4d0082, 0x0b0f532c, 0x0d449b94, 0x4e312007, 0x074f12e7, 0x0bf3490b, 0xbb412120, 0x413f820a, 0xef490857,
0x80002313, 0xe2830001, 0x6441fc20, 0x8b802006, 0x00012108, 0xfd201582, 0x492c9b48, 0x802014ff, 0x51084347, 0x0f4327f3, 0x17bf4a14, 0x201b7944,
0x06964201, 0x134ffe20, 0x20d6830b, 0x25d78280, 0xfd800002, 0x05888000, 0x9318dc41, 0x21d282d4, 0xdb481800, 0x0dff542a, 0x45107743, 0xe14813f5,
0x0f034113, 0x83135d45, 0x47b28437, 0xe4510e73, 0x21f58e06, 0x2b8400fd, 0x1041fcac, 0x08db4b0b, 0x421fdb41, 0xdf4b18df, 0x011d210a, 0x420af350,
0x6e8308af, 0xac85cb86, 0x1e461082, 0x82b7a407, 0x411420a3, 0xa34130ab, 0x178f4124, 0x41139741, 0x86410d93, 0x82118511, 0x057243d8, 0x8941d9a4,
0x3093480c, 0x4a13474f, 0xfb5016a9, 0x07ad4108, 0x4a0f9d42, 0xfe200fad, 0x4708aa41, 0x83482dba, 0x288f4d06, 0xb398c3bb, 0x44267b41, 0xb34439d7,
0x0755410f, 0x200ebb45, 0x0f5f4215, 0x20191343, 0x06df5301, 0xf04c0220, 0x2ba64d07, 0x82050841, 0x430020ce, 0xa78f3627, 0x5213ff42, 0x2f970bc1,
0x4305ab55, 0xa084111b, 0x450bac45, 0x5f4238b8, 0x010c2106, 0x0220ed82, 0x441bb344, 0x875010af, 0x0737480f, 0x490c5747, 0x0c840c03, 0x4c204b42,
0x8ba905d7, 0x8b948793, 0x510c0c51, 0xfb4b24b9, 0x1b174107, 0x5709d74c, 0xd1410ca5, 0x079d480f, 0x201ff541, 0x06804780, 0x7d520120, 0x80002205,
0x20a983fe, 0x47bb83fe, 0x1b8409b4, 0x81580220, 0x4e00202c, 0x4f41282f, 0x0eab4f17, 0x57471520, 0x0e0f4808, 0x8221e041, 0x3e1b4a8b, 0x4407175d,
0x1b4b071f, 0x4a0f8b07, 0x174a0703, 0x0ba5411b, 0x430fb141, 0x0120057b, 0xfc20dd82, 0x4a056047, 0xf4850c0c, 0x01221982, 0x02828000, 0x1a5d088b,
0x20094108, 0x8c0e3941, 0x4900200e, 0x7744434f, 0x200b870b, 0x0e4b5a33, 0x2b41f78b, 0x8b138307, 0x0b9f450b, 0x2406f741, 0xfd808001, 0x09475a00,
0x84000121, 0x5980200e, 0x85450e5d, 0x832c8206, 0x4106831e, 0x00213814, 0x28b34810, 0x410c2f4b, 0x5f4a13d7, 0x0b2b4113, 0x6e43a883, 0x11174b05,
0x4b066a45, 0xcc470541, 0x5000202b, 0xcb472f4b, 0x44b59f0f, 0xc5430b5b, 0x0d654907, 0x21065544, 0xd6828080, 0xfe201982, 0x8230ec4a, 0x120025c2,
0x80ff8000, 0x4128d74d, 0x3320408b, 0x410a9f50, 0xdb822793, 0x822bd454, 0x61134b2e, 0x410b214a, 0xad4117c9, 0x0001211f, 0x4206854f, 0x4b430596,
0x06bb5530, 0x2025cf46, 0x0ddd5747, 0x500ea349, 0x0f840fa7, 0x5213c153, 0x634e08d1, 0x0bbe4809, 0x59316e4d, 0x5b50053f, 0x203f6323, 0x5117eb46,
0x94450a63, 0x246e410a, 0x63410020, 0x0bdb5f2f, 0x4233ab44, 0x39480757, 0x112d4a07, 0x7241118f, 0x000e2132, 0x9f286f41, 0x0f8762c3, 0x33350723,
0x094e6415, 0x2010925f, 0x067252fe, 0xd0438020, 0x63a68225, 0x11203a4f, 0x480e6360, 0x5748131f, 0x079b521f, 0x200e2f43, 0x864b8315, 0x113348e7,
0x85084e48, 0x06855008, 0x5880fd21, 0x7c420925, 0x0c414824, 0x37470c86, 0x1b8b422b, 0x5b0a8755, 0x23410c21, 0x0b83420b, 0x5a082047, 0xf482067f,
0xa80b4c47, 0x0c0021cf, 0x20207b42, 0x0fb74100, 0x420b8744, 0xeb43076f, 0x0f6f420b, 0x4261fe20, 0x439aa00c, 0x215034e3, 0x0ff9570f, 0x4b1f2d5d,
0x2d5d0c6f, 0x09634d0b, 0x1f51b8a0, 0x620f200c, 0xaf681e87, 0x24f94d07, 0x4e0f4945, 0xfe200c05, 0x22139742, 0x57048080, 0x23950c20, 0x97601585,
0x4813201f, 0xad620523, 0x200f8f0f, 0x9e638f15, 0x00002181, 0x41342341, 0x0f930f0b, 0x210b4b62, 0x978f0001, 0xfe200f84, 0x8425c863, 0x2704822b,
0x80000a00, 0x00038001, 0x610e9768, 0x834514bb, 0x0bc3430f, 0x2107e357, 0x80848080, 0x4400fe21, 0x2e410983, 0x00002a1a, 0x00000700, 0x800380ff,
0x0fdf5800, 0x59150021, 0xd142163d, 0x0c02410c, 0x01020025, 0x65800300, 0x00240853, 0x1d333501, 0x15220382, 0x35420001, 0x44002008, 0x376406d7,
0x096f6b19, 0x480bc142, 0x8f4908a7, 0x211f8b1f, 0x9e830001, 0x0584fe20, 0x4180fd21, 0x11850910, 0x8d198259, 0x000021d4, 0x5a08275d, 0x275d1983,
0x06d9420e, 0x9f08b36a, 0x0f7d47b5, 0x8d8a2f8b, 0x4c0e0b57, 0xe7410e17, 0x42d18c1a, 0xb351087a, 0x1ac36505, 0x4b4a2f20, 0x0b9f450d, 0x430beb53,
0xa7881015, 0xa5826a83, 0x80200f82, 0x86185a65, 0x4100208e, 0x176c3367, 0x0fe7650b, 0x4a17ad4b, 0x0f4217ed, 0x112e4206, 0x41113a42, 0xf7423169,
0x0cb34737, 0x560f8b46, 0xa75407e5, 0x5f01200f, 0x31590c48, 0x80802106, 0x42268841, 0x0020091e, 0x4207ef64, 0x69461df7, 0x138d4114, 0x820f5145,
0x53802090, 0xff200529, 0xb944b183, 0x417e8505, 0x00202561, 0x15210082, 0x42378200, 0x9b431cc3, 0x004f220d, 0x0dd54253, 0x4213f149, 0x7d41133b,
0x42c9870b, 0x802010f9, 0x420b2c42, 0x8f441138, 0x267c4408, 0x600cb743, 0x8f4109d3, 0x05ab701d, 0x83440020, 0x3521223f, 0x0b794733, 0xfb62fe20,
0x4afd2010, 0xaf410ae7, 0x25ce8525, 0x01080000, 0x7b6b0000, 0x0973710b, 0x82010021, 0x49038375, 0x33420767, 0x052c4212, 0x58464b85, 0x41fe2005,
0x50440c27, 0x000c2209, 0x1cb36b80, 0x9b06df44, 0x0f93566f, 0x52830220, 0xfe216e8d, 0x200f8200, 0x0fb86704, 0xb057238d, 0x050b5305, 0x7217eb47,
0xbd410b6b, 0x0f214610, 0x871f9956, 0x1e91567e, 0x2029b741, 0x20008200, 0x18b7410a, 0x27002322, 0x41095543, 0x0f8f0fb3, 0x41000121, 0x889d111c,
0x14207b82, 0x00200382, 0x73188761, 0x475013a7, 0x6e33200c, 0x234e0ea3, 0x9b138313, 0x08e54d17, 0x9711094e, 0x2ee74311, 0x4908875e, 0xd75d1f1f,
0x19ab5238, 0xa2084d48, 0x63a7a9b3, 0x55450b83, 0x0fd74213, 0x440d814c, 0x4f481673, 0x05714323, 0x13000022, 0x412e1f46, 0xdf493459, 0x21c7550f,
0x8408215f, 0x201d49cb, 0xb1103043, 0x0f0d65d7, 0x452b8d41, 0x594b0f8d, 0x0b004605, 0xb215eb46, 0x000a24d7, 0x47000080, 0x002118cf, 0x06436413,
0x420bd750, 0x2b500743, 0x076a470c, 0x4105c050, 0xd942053f, 0x0d00211a, 0x5f44779c, 0x0ce94805, 0x51558186, 0x14a54c0b, 0x49082b41, 0x0a4b0888,
0x8080261f, 0x0d000000, 0x20048201, 0x1deb6a03, 0x420cb372, 0x07201783, 0x4306854d, 0x8b830c59, 0x59093c74, 0x0020250f, 0x67070f4a, 0x2341160b,
0x00372105, 0x431c515d, 0x554e17ef, 0x0e5d6b05, 0x41115442, 0xb74a1ac1, 0x2243420a, 0x5b4f878f, 0x7507200f, 0x384b086f, 0x09d45409, 0x0020869a,
0x12200082, 0xab460382, 0x10075329, 0x54138346, 0xaf540fbf, 0x1ea75413, 0x9a0c9e54, 0x0f6b44c1, 0x41000021, 0x47412a4f, 0x07374907, 0x5310bf76,
0xff2009b4, 0x9a09a64c, 0x8200208d, 0x34c34500, 0x970fe141, 0x1fd74b0f, 0x440a3850, 0x206411f0, 0x27934609, 0x470c5d41, 0x555c2947, 0x1787540f,
0x6e0f234e, 0x7d540a1b, 0x1d736b08, 0x0026a088, 0x80000e00, 0x9b5200ff, 0x08ef4318, 0x450bff77, 0x1d4d0b83, 0x081f7006, 0xcb691b86, 0x4b022008,
0xc34b0b33, 0x1d0d4a0c, 0x8025a188, 0x0b000000, 0x52a38201, 0xbf7d0873, 0x0c234511, 0x8f0f894a, 0x4101200f, 0x0c880c9d, 0x2b418ea1, 0x06c74128,
0x66181341, 0x7b4c0bb9, 0x0c06630b, 0xfe200c87, 0x9ba10882, 0x27091765, 0x01000008, 0x02800380, 0x48113f4e, 0x29430cf5, 0x09a75a0b, 0x31618020,
0x6d802009, 0x61840e33, 0x8208bf51, 0x0c637d61, 0x7f092379, 0x4f470f4b, 0x1797510c, 0x46076157, 0xf5500fdf, 0x0f616910, 0x1171fe20, 0x82802006,
0x08696908, 0x41127a4c, 0x3f4a15f3, 0x01042607, 0x0200ff00, 0x1cf77700, 0xff204185, 0x00235b8d, 0x43100000, 0x3b22243f, 0x3b4d3f00, 0x0b937709,
0xad42f18f, 0x0b1f420f, 0x51084b43, 0x8020104a, 0xb557ff83, 0x052b7f2a, 0x0280ff22, 0x250beb78, 0x00170013, 0xbf6d2500, 0x07db760e, 0x410e2b7f,
0x00230e4f, 0x49030000, 0x0582055b, 0x07000326, 0x00000b00, 0x580bcd46, 0x00200cdd, 0x57078749, 0x8749160f, 0x0f994f0a, 0x41134761, 0x01200b31,
0xeb796883, 0x0b41500b, 0x0e90b38e, 0x202e7b51, 0x05d95801, 0x41080570, 0x1d530fc9, 0x0b937a0f, 0xaf8eb387, 0xf743b98f, 0x07c74227, 0x80000523,
0x0fcb4503, 0x430ca37b, 0x7782077f, 0x8d0a9947, 0x08af4666, 0xeb798020, 0x6459881e, 0xc3740bbf, 0x0feb6f0b, 0x20072748, 0x052b6102, 0x435e0584,
0x7d088308, 0x03200afd, 0x92109e41, 0x28aa8210, 0x80001500, 0x80030000, 0x0fdb5805, 0x209f4018, 0xa7418d87, 0x0aa3440f, 0x20314961, 0x073a52ff,
0x6108505d, 0x43181051, 0x00223457, 0xe7820500, 0x50028021, 0x81410d33, 0x063d7108, 0xdb41af84, 0x4d888205, 0x00201198, 0x463d835f, 0x152106d7,
0x0a355a33, 0x6917614e, 0x75411f4d, 0x184b8b07, 0x1809c344, 0x21091640, 0x0b828000, 0x42808021, 0x26790519, 0x86058605, 0x2428422d, 0x22123b42,
0x42000080, 0xf587513b, 0x7813677b, 0xaf4d139f, 0x00ff210c, 0x5e0a1d57, 0x3b421546, 0x01032736, 0x02000380, 0x41180480, 0x2f420f07, 0x0c624807,
0x00000025, 0x18000103, 0x83153741, 0x430120c3, 0x042106b2, 0x088d4d00, 0x2f830620, 0x1810434a, 0x18140345, 0x8507fb41, 0x5ee582ea, 0x0023116c,
0x8d000600, 0x053b56af, 0xa6554fa2, 0x0d704608, 0x40180d20, 0x47181a43, 0xd37b07ff, 0x0b79500c, 0x420fd745, 0x47450bd9, 0x8471830a, 0x095a777e,
0x84137542, 0x82002013, 0x2f401800, 0x0007213b, 0x4405e349, 0x0d550ff3, 0x16254c0c, 0x820ffe4a, 0x0400218a, 0x89066f41, 0x106b414f, 0xc84d0120,
0x80802206, 0x0c9a4b03, 0x00100025, 0x68000200, 0x9d8c2473, 0x44134344, 0xf36a0f33, 0x4678860f, 0x1b440a25, 0x41988c0a, 0x80201879, 0x43079b5e,
0x4a18080b, 0x0341190b, 0x1259530c, 0x43251552, 0x908205c8, 0x0cac4018, 0x86000421, 0x0e504aa2, 0x0020b891, 0xfb450082, 0x51132014, 0x8f5205f3,
0x35052108, 0x8505cb59, 0x0f6d4f70, 0x82150021, 0x29af5047, 0x4f004b24, 0x75795300, 0x1b595709, 0x460b6742, 0xbf4b0f0d, 0x5743870b, 0xcb6d1461,
0x08f64505, 0x4e05ab6c, 0x334126c3, 0x0bcb6b0d, 0x1811034d, 0x4111ef4b, 0x814f1ce5, 0x20af8227, 0x07fd7b80, 0x41188e84, 0xef410f33, 0x80802429,
0x410d0000, 0xa34205ab, 0x76b7881c, 0xff500b89, 0x0741430f, 0x20086f4a, 0x209d8200, 0x234c18fd, 0x05d4670a, 0x4509af51, 0x9642078d, 0x189e831d,
0x7c1cc74b, 0xcd4c07b9, 0x0e7c440f, 0x8b7b0320, 0x21108210, 0xc76c8080, 0x03002106, 0x6b23bf41, 0xc549060b, 0x7946180b, 0x0ff7530f, 0x17ad4618,
0x200ecd45, 0x208c83fd, 0x5e0488fe, 0x032009c6, 0x420d044e, 0x0d8f0d7f, 0x00820020, 0x18001021, 0x6d273b45, 0xfd4c0c93, 0xcf451813, 0x0fe5450f,
0x5a47c382, 0x820a8b0a, 0x282b4998, 0x410a8b5b, 0x4b232583, 0x54004f00, 0x978f0ce3, 0x500f1944, 0xa95f1709, 0x0280220b, 0x05ba7080, 0xa1530682,
0x06324c13, 0x91412582, 0x05536e2c, 0x63431020, 0x0f434706, 0x8c11374c, 0x176143d7, 0x4d0f454c, 0xd3680bed, 0x0bee4d17, 0x212b9a41, 0x0f530a00,
0x140d531c, 0x43139143, 0x95610e8d, 0x0f094415, 0x4205fb56, 0x1b4205cf, 0x17015225, 0x5e0c477f, 0xaf6e0aeb, 0x0ff36218, 0x04849a84, 0x0a454218,
0x9c430420, 0x23c6822b, 0x04000102, 0x45091b4b, 0xf05f0955, 0x82802007, 0x421c2023, 0x5218282b, 0x7b53173f, 0x0fe7480c, 0x74173b7f, 0x47751317,
0x634d1807, 0x0f6f430f, 0x24086547, 0xfc808002, 0x0b3c7f80, 0x10840120, 0x188d1282, 0x20096b43, 0x0fc24403, 0x00260faf, 0x0180000b, 0x3f500280,
0x18002019, 0x450b4941, 0xf3530fb9, 0x18002010, 0x8208a551, 0x06234d56, 0xcb58a39b, 0xc3421805, 0x1313461e, 0x0f855018, 0xd34b0120, 0x6cfe2008,
0x574f0885, 0x09204114, 0x07000029, 0x00008000, 0x44028002, 0x01420f57, 0x10c95c10, 0x11184c18, 0x80221185, 0x7f421e00, 0x00732240, 0x09cd4977,
0x6d0b2b42, 0x4f180f8f, 0x8f5a0bcb, 0x9b0f830f, 0x0fb9411f, 0x230b5756, 0x00fd8080, 0x82060745, 0x000121d5, 0x8e0fb277, 0x4a8d4211, 0x24061c53,
0x04000007, 0x12275280, 0x430c954c, 0x80201545, 0x200f764f, 0x20008200, 0x20ce8308, 0x09534f02, 0x660edf64, 0x73731771, 0xe7411807, 0x20a2820c,
0x13b64404, 0x8f5d6682, 0x1d6b4508, 0x0cff4d18, 0x3348c58f, 0x0fc34c07, 0x31558b84, 0x8398820f, 0x17514712, 0x240b0e46, 0x80000a00, 0x093b4502,
0x420f9759, 0xa54c0bf1, 0x0f2b470c, 0x410d314b, 0x2584170c, 0x73b30020, 0xb55fe782, 0x204d8410, 0x08e043fe, 0x4f147e41, 0x022008ab, 0x4b055159,
0x2950068f, 0x00022208, 0x48511880, 0x82002009, 0x00112300, 0x634dff00, 0x24415f27, 0x180f6d43, 0x4d0b5d45, 0x4d5f05ef, 0x01802317, 0x56188000,
0xa7840807, 0xc6450220, 0x21ca8229, 0x4b781a00, 0x3359182c, 0x0cf3470f, 0x180bef46, 0x420b0354, 0xff470b07, 0x4515200a, 0x9758239b, 0x4a80200c,
0xd2410a26, 0x05fb4a08, 0x4b05e241, 0x03200dc9, 0x92290941, 0x00002829, 0x00010900, 0x5b020001, 0x23201363, 0x460d776a, 0xef530fdb, 0x209a890c,
0x13fc4302, 0x00008024, 0xc4820104, 0x08820220, 0x20086b5b, 0x18518700, 0x8408d349, 0x0da449a1, 0x00080024, 0x7b690280, 0x4c438b1a, 0x01220f63,
0x4c878000, 0x5c149c53, 0xfb430868, 0x2f56181e, 0x0ccf7b1b, 0x0f075618, 0x2008e347, 0x14144104, 0x00207f83, 0x00207b82, 0x201adf47, 0x16c35a13,
0x540fdf47, 0x802006c8, 0x5418f185, 0x29430995, 0x00002419, 0x58001600, 0x5720316f, 0x4d051542, 0x4b7b1b03, 0x138f4707, 0xb747b787, 0x4aab8213,
0x058305fc, 0x20115759, 0x82128401, 0x0a0b44e8, 0x46800121, 0xe64210d0, 0x82129312, 0x4bffdffe, 0x3b41171b, 0x9b27870f, 0x808022ff, 0x085c68fe,
0x41800021, 0x01410b20, 0x001a213a, 0x47480082, 0x11374e12, 0x56130b4c, 0xdf4b0c65, 0x0b0f590b, 0x0f574c18, 0x830feb4b, 0x075f480f, 0x480b4755,
0x40490b73, 0x80012206, 0x09d74280, 0x80fe8022, 0x80210e86, 0x056643ff, 0x10820020, 0x420b2646, 0x0b58391a, 0xd74c1808, 0x078b4e22, 0x2007f55f,
0x4b491807, 0x83802017, 0x65aa82a7, 0x3152099e, 0x068b7616, 0x9b431220, 0x09bb742c, 0x500e376c, 0x8342179b, 0x0a4d5d0f, 0x8020a883, 0x180cd349,
0x2016bb4b, 0x14476004, 0x84136c43, 0x08cf7813, 0x4f4c0520, 0x156f420f, 0x20085f42, 0x6fd3be03, 0xd4d30803, 0xa7411420, 0x004b222c, 0x0d3b614f,
0x3f702120, 0x1393410a, 0x8f132745, 0x47421827, 0x41e08209, 0xb05e2bb9, 0x18b7410c, 0x18082647, 0x4107a748, 0xeb8826bf, 0x0ca76018, 0x733ecb41,
0xd0410d83, 0x43ebaf2a, 0x0420067f, 0x721dab4c, 0x472005bb, 0x4105d341, 0x334844cb, 0x20dba408, 0x47d6ac00, 0x034e3aef, 0x0f8f421b, 0x930f134d,
0x3521231f, 0xb7421533, 0x42f5ad0a, 0x1e961eaa, 0x17000022, 0x4c367b50, 0x7d491001, 0x0bf5520f, 0x4c18fda7, 0xb8460c55, 0x83fe2005, 0x00fe25b9,
0x80000180, 0x9e751085, 0x261b5c12, 0x82110341, 0x001123fb, 0x4518fe80, 0xf38c2753, 0x6d134979, 0x295107a7, 0xaf5f180f, 0x0fe3660c, 0x180b6079,
0x2007bd5f, 0x9aab9103, 0x2f4d1811, 0x05002109, 0x44254746, 0x1d200787, 0x450bab75, 0x4f180f57, 0x4f181361, 0x3b831795, 0xeb4b0120, 0x0b734805,
0x84078f48, 0x2e1b47bc, 0x00203383, 0xaf065f45, 0x831520d7, 0x130f51a7, 0x1797bf97, 0x2b47d783, 0x18fe2005, 0x4a18a44f, 0xa64d086d, 0x1ab0410d,
0x6205a258, 0xdbab069f, 0x4f06f778, 0xa963081d, 0x133b670a, 0x8323d141, 0x13195b23, 0x530f5e70, 0xe5ad0824, 0x58001421, 0x1f472b4b, 0x47bf410c,
0x82000121, 0x83fe20cb, 0x07424404, 0x68068243, 0xd7ad0d3d, 0x00010d26, 0x80020000, 0x4a1c6f43, 0x23681081, 0x10a14f13, 0x8a070e57, 0x430a848f,
0x7372243e, 0x4397a205, 0xb56c1021, 0x43978f0f, 0x64180505, 0x99aa0ff2, 0x0e000022, 0x20223341, 0x094b4f37, 0x074a3320, 0x2639410a, 0xfe208e84,
0x8b0e0048, 0x508020a3, 0x9e4308fe, 0x073f4115, 0xe3480420, 0x0c9b5f1b, 0x7c137743, 0x9a95185b, 0x6122b148, 0x979b08df, 0x0fe36c18, 0x48109358,
0x23441375, 0x0ffd5c0b, 0x180fc746, 0x2011d157, 0x07e95702, 0x58180120, 0x18770ac3, 0x51032008, 0x7d4118e3, 0x80802315, 0x3b4c1900, 0xbb5a1830,
0x0ceb6109, 0x5b0b3d42, 0x4f181369, 0x4f180b8d, 0x4f180f75, 0x355a1b81, 0x200d820d, 0x18e483fd, 0x4528854f, 0x89420846, 0x1321411f, 0x44086b60,
0x07421d77, 0x107d4405, 0x4113fd41, 0x5a181bf1, 0x4f180db3, 0x8021128f, 0x20f68280, 0x44a882fe, 0x334d249a, 0x052f6109, 0x1520c3a7, 0xef4eb783,
0x4ec39b1b, 0xc4c90ee7, 0x20060b4d, 0x256f4905, 0x4d0cf761, 0xcf9b1f13, 0xa213d74e, 0x0e1145d4, 0x50135b42, 0xcb4e398f, 0x20d79f27, 0x08865d80,
0x186d5018, 0xa90f7142, 0x067342d7, 0x3f450420, 0x65002021, 0xe3560771, 0x24d38f23, 0x15333531, 0x0eb94d01, 0x451c9f41, 0x384322fb, 0x00092108,
0x19af6b18, 0x6e0c6f5a, 0xbd770bfb, 0x22bb7718, 0x20090f57, 0x25e74204, 0x4207275a, 0xdb5408ef, 0x1769450f, 0x1b1b5518, 0x210b1f57, 0x5e4c8001,
0x55012006, 0x802107f1, 0x0a306a80, 0x45808021, 0x0d850b88, 0x31744f18, 0x1808ec54, 0x2009575b, 0x45ffa505, 0x1b420c73, 0x180f9f0f, 0x4a0cf748,
0x501805b2, 0x00210f40, 0x4d118f80, 0xd6823359, 0x072b5118, 0x314ad7aa, 0x8fc79f08, 0x45d78b1f, 0xfe20058f, 0x23325118, 0x7b54d9b5, 0x9fc38f46,
0x10bb410f, 0x41077b42, 0xc1410faf, 0x27cf441d, 0x46051b4f, 0x04200683, 0x2121d344, 0x8f530043, 0x8fcf9f0e, 0x21df8c1f, 0x50188000, 0x5d180e52,
0xfd201710, 0x4405c341, 0xd68528e3, 0x20071f6b, 0x1b734305, 0x6b080957, 0x7d422b1f, 0x67002006, 0x7f8317b1, 0x2024cb48, 0x08676e00, 0x8749a39b,
0x18132006, 0x410a6370, 0x8f490b47, 0x7e1f8f13, 0x551805c3, 0x4c180915, 0xfe200e2f, 0x244d5d18, 0x270bcf44, 0xff000019, 0x04800380, 0x5f253342,
0xff520df7, 0x13274c18, 0x5542dd93, 0x0776181b, 0xf94a1808, 0x084a4c0c, 0x4308ea5b, 0xde831150, 0x7900fd21, 0x00492c1e, 0x060f4510, 0x17410020,
0x0ce74526, 0x6206b341, 0x1f561083, 0x9d6c181b, 0x08a0500e, 0x112e4118, 0x60000421, 0xbf901202, 0x4408e241, 0xc7ab0513, 0xb40f0950, 0x055943c7,
0x4f18ff20, 0xc9ae1cad, 0x32b34f18, 0x7a180120, 0x3d520a05, 0x53d1b40a, 0x80200813, 0x1b815018, 0x832bf86f, 0x67731847, 0x297f4308, 0x6418d54e,
0x734213f7, 0x056b4b27, 0xdba5fe20, 0x1828aa4e, 0x2031a370, 0x06cb6101, 0x2040ad41, 0x07365300, 0x2558d985, 0x83fe200c, 0x0380211c, 0x542c4743,
0x052006b7, 0x6021df45, 0x897b0707, 0x18d3c010, 0x20090e70, 0x1d5843ff, 0x540a0e44, 0x002126c5, 0x322f7416, 0x636a5720, 0x0f317409, 0x610fe159,
0x294617e7, 0x08555213, 0x2006a75d, 0x6cec84fd, 0xfb5907be, 0x3a317405, 0x83808021, 0x180f20ea, 0x4626434a, 0x531818e3, 0xdb59172d, 0x0cbb460c,
0x2013d859, 0x18b94502, 0x8f46188d, 0x77521842, 0x0a184e38, 0x9585fd20, 0x6a180684, 0xc64507e9, 0x51cbb230, 0xd3440cf3, 0x17ff6a0f, 0x450f5b42,
0x276407c1, 0x4853180a, 0x21ccb010, 0xcf580013, 0x0c15442d, 0x410a1144, 0x1144359d, 0x5cfe2006, 0xa1410a43, 0x2bb64519, 0x2f5b7618, 0xb512b745,
0x0cfd6fd1, 0x42089f59, 0xb8450c70, 0x0000232d, 0x50180900, 0xb9491ae3, 0x0fc37610, 0x01210f83, 0x0f3b4100, 0xa01b2742, 0x0ccd426f, 0x6e8f6f94,
0x9c808021, 0xc7511870, 0x17c74b08, 0x9b147542, 0x44fe2079, 0xd5480c7e, 0x95ef861d, 0x101b597b, 0xf5417594, 0x9f471808, 0x86868d0e, 0x3733491c,
0x690f4d6d, 0x43440b83, 0x1ba94c0b, 0x660cd16b, 0x802008ae, 0x74126448, 0xcb4f38a3, 0x2cb74b0b, 0x47137755, 0xe3971777, 0x1b5d0120, 0x057a4108,
0x6e08664d, 0x17421478, 0x11af4208, 0x850c3f42, 0x08234f0c, 0x4321eb4a, 0xf3451095, 0x0f394e0f, 0x4310eb45, 0xc09707b1, 0x54431782, 0xaec08d1d,
0x0f434dbb, 0x9f0c0b45, 0x0a3b4dbb, 0x4618bdc7, 0x536032eb, 0x17354213, 0x4d134169, 0xc7a30c2f, 0x4e254342, 0x174332cf, 0x43cdae17, 0x6b4706e4,
0x0e16430d, 0x530b5542, 0x2f7c26bb, 0x13075f31, 0x43175342, 0x60181317, 0x6550114e, 0x28624710, 0x58070021, 0x59181683, 0x2d540cf5, 0x05d5660c,
0x20090c7b, 0x0e157e02, 0x8000ff2b, 0x14000080, 0x80ff8000, 0x27137e03, 0x336a4b20, 0x0f817107, 0x13876e18, 0x730f2f7e, 0x2f450b75, 0x6d02200b,
0x6d66094c, 0x4b802009, 0x15820a02, 0x2f45fe20, 0x5e032006, 0x00202fd9, 0x450af741, 0xeb412e0f, 0x0ff3411f, 0x420a8b65, 0xf7410eae, 0x1c664810,
0x540e1145, 0xbfa509f3, 0x42302f58, 0x80200c35, 0xcb066c47, 0x4b1120c1, 0x41492abb, 0x34854110, 0xa7097b72, 0x251545c7, 0x4b2c7f56, 0xc5b40bab,
0x940cd54e, 0x2e6151c8, 0x09f35f18, 0x4b420420, 0x09677121, 0x8f24f357, 0x1b5418e1, 0x08915a1f, 0x3143d894, 0x22541805, 0x1b9b4b0e, 0x8c0d3443,
0x1400240d, 0x18ff8000, 0x582e6387, 0xf99b2b3b, 0x8807a550, 0x17a14790, 0x2184fd20, 0x5758fe20, 0x2354882c, 0x15000080, 0x5e056751, 0x334c2c2f,
0x97c58f0c, 0x1fd7410f, 0x0d4d4018, 0x4114dc41, 0x04470ed6, 0x0dd54128, 0x00820020, 0x02011523, 0x22008700, 0x86480024, 0x0001240a, 0x8682001a,
0x0002240b, 0x866c000e, 0x8a03200b, 0x8a042017, 0x0005220b, 0x22218614, 0x84060000, 0x86012017, 0x8212200f, 0x250b8519, 0x000d0001, 0x0b850031,
0x07000224, 0x0b862600, 0x11000324, 0x0b862d00, 0x238a0420, 0x0a000524, 0x17863e00, 0x17840620, 0x01000324, 0x57820904, 0x0b85a783, 0x0b85a785,
0x0b85a785, 0x22000325, 0x85007a00, 0x85a7850b, 0x85a7850b, 0x22a7850b, 0x82300032, 0x00342201, 0x0805862f, 0x35003131, 0x54207962, 0x74736972,
0x47206e61, 0x6d6d6972, 0x65527265, 0x616c7567, 0x58545472, 0x6f725020, 0x43796767, 0x6e61656c, 0x30325454, 0x822f3430, 0x35313502, 0x79006200,
0x54002000, 0x69007200, 0x74007300, 0x6e006100, 0x47200f82, 0x6d240f84, 0x65006d00, 0x52200982, 0x67240582, 0x6c007500, 0x72201d82, 0x54222b82,
0x23825800, 0x19825020, 0x67006f22, 0x79220182, 0x1b824300, 0x3b846520, 0x1f825420, 0x41000021, 0x1422099b, 0x0b410000, 0x87088206, 0x01012102,
0x78080982, 0x01020101, 0x01040103, 0x01060105, 0x01080107, 0x010a0109, 0x010c010b, 0x010e010d, 0x0110010f, 0x01120111, 0x01140113, 0x01160115,
0x01180117, 0x011a0119, 0x011c011b, 0x011e011d, 0x0020011f, 0x00040003, 0x00060005, 0x00080007, 0x000a0009, 0x000c000b, 0x000e000d, 0x0010000f,
0x00120011, 0x00140013, 0x00160015, 0x00180017, 0x001a0019, 0x001c001b, 0x001e001d, 0x08bb821f, 0x22002142, 0x24002300, 0x26002500, 0x28002700,
0x2a002900, 0x2c002b00, 0x2e002d00, 0x30002f00, 0x32003100, 0x34003300, 0x36003500, 0x38003700, 0x3a003900, 0x3c003b00, 0x3e003d00, 0x40003f00,
0x42004100, 0x4b09f382, 0x00450044, 0x00470046, 0x00490048, 0x004b004a, 0x004d004c, 0x004f004e, 0x00510050, 0x00530052, 0x00550054, 0x00570056,
0x00590058, 0x005b005a, 0x005d005c, 0x005f005e, 0x01610060, 0x01220121, 0x01240123, 0x01260125, 0x01280127, 0x012a0129, 0x012c012b, 0x012e012d,
0x0130012f, 0x01320131, 0x01340133, 0x01360135, 0x01380137, 0x013a0139, 0x013c013b, 0x013e013d, 0x0140013f, 0x00ac0041, 0x008400a3, 0x00bd0085,
0x00e80096, 0x008e0086, 0x009d008b, 0x00a400a9, 0x008a00ef, 0x008300da, 0x00f20093, 0x008d00f3, 0x00880097, 0x00de00c3, 0x009e00f1, 0x00f500aa,
0x00f600f4, 0x00ad00a2, 0x00c700c9, 0x006200ae, 0x00900063, 0x00cb0064, 0x00c80065, 0x00cf00ca, 0x00cd00cc, 0x00e900ce, 0x00d30066, 0x00d100d0,
0x006700af, 0x009100f0, 0x00d400d6, 0x006800d5, 0x00ed00eb, 0x006a0089, 0x006b0069, 0x006c006d, 0x00a0006e, 0x0071006f, 0x00720070, 0x00750073,
0x00760074, 0x00ea0077, 0x007a0078, 0x007b0079, 0x007c007d, 0x00a100b8, 0x007e007f, 0x00810080, 0x00ee00ec, 0x6e750eba, 0x646f6369, 0x78302365,
0x31303030, 0x32200e8d, 0x33200e8d, 0x34200e8d, 0x35200e8d, 0x36200e8d, 0x37200e8d, 0x38200e8d, 0x39200e8d, 0x61200e8d, 0x62200e8d, 0x63200e8d,
0x64200e8d, 0x65200e8d, 0x66200e8d, 0x31210e8c, 0x8d0e8d30, 0x8d3120ef, 0x8d3120ef, 0x8d3120ef, 0x8d3120ef, 0x8d3120ef, 0x8d3120ef, 0x8d3120ef,
0x8d3120ef, 0x8d3120ef, 0x8d3120ef, 0x8d3120ef, 0x8d3120ef, 0x8d3120ef, 0x66312def, 0x6c656406, 0x04657465, 0x6f727545, 0x3820ec8c, 0x3820ec8d,
0x3820ec8d, 0x3820ec8d, 0x3820ec8d, 0x3820ec8d, 0x3820ec8d, 0x3820ec8d, 0x3820ec8d, 0x3820ec8d, 0x3820ec8d, 0x3820ec8d, 0x3820ec8d, 0x3820ec8d,
0x3820ec8d, 0x200ddc41, 0x0ddc4139, 0xef8d3920, 0xef8d3920, 0xef8d3920, 0xef8d3920, 0xef8d3920, 0xef8d3920, 0xef8d3920, 0xef8d3920, 0xef8d3920,
0xef8d3920, 0xef8d3920, 0xef8d3920, 0xef8d3920, 0xef8d3920, 0x00663923, 0x48fa0500, 0x00f762f9,
const unsigned char font_proggyClean_compressed_data[]={
0x78,0xda,0xdd,0x3d,0x69,0x90,0x54,0xc7,0x79,0x5f,0xbf,0xf7,
0x66,0x67,0x17,0x96,0x65,0xd9,0x83,0xfb,0xd8,0x85,0x5d,0xee,
0x63,0x1f,0x03,0xcb,0x0c,0x87,0x58,0x40,0x08,0xac,0x28,0x12,
0x11,0x18,0xe1,0x58,0x91,0xb4,0xc0,0x72,0xd8,0x5c,0x81,0x05,
0x81,0xec,0xc8,0x4f,0x87,0x65,0x19,0xc7,0x32,0x56,0xaa,0x88,
0xa2,0x38,0x2a,0x47,0x71,0x51,0x89,0x8a,0x60,0x25,0xe5,0x52,
0x28,0xc7,0xae,0x4a,0x95,0x13,0x85,0x24,0x8a,0xa3,0xd8,0x92,
0xad,0xd2,0x0f,0x47,0x95,0x1f,0x2e,0x19,0x2b,0x58,0x49,0x64,
0x97,0x7e,0x68,0x67,0xd2,0xef,0xee,0xee,0xd7,0xdd,0xaf,0xbb,
0x67,0xd6,0x3f,0x32,0xbb,0x33,0xef,0x98,0x99,0xf7,0xfa,0x3b,
0xfa,0xbb,0xfb,0x1b,0x40,0x00,0xd0,0x0a,0x1e,0xd8,0x30,0x74,
0xcf,0xee,0x55,0xab,0x9f,0xfe,0xf9,0xe8,0x25,0x00,0xb4,0x13,
0x9f,0xbd,0xfb,0xc0,0xf1,0xe1,0x53,0x56,0xc7,0x82,0xb3,0x00,
0xf6,0xd7,0xf1,0xb9,0x7b,0x0f,0x9c,0x1b,0xed,0x81,0xe0,0xe1,
0x7c,0x84,0x5f,0xac,0xc3,0xc7,0x2e,0x1c,0xea,0xf8,0xe6,0x17,
0xf7,0x02,0x14,0x1d,0x80,0x67,0xbd,0x23,0x23,0xc3,0x07,0x7f,
0xfc,0xd5,0x43,0x3f,0xc4,0xef,0xbd,0x86,0x9f,0x83,0x47,0xf0,
0x89,0xe6,0xad,0xe8,0x7b,0xf8,0xbb,0xf8,0x7d,0xe8,0x3b,0x72,
0x7c,0xf4,0xfc,0x45,0x78,0xd4,0xc3,0xc7,0xcf,0xe1,0xaf,0x37,
0x1d,0x3b,0x79,0x60,0xf8,0xf7,0xcf,0xbc,0xfc,0x16,0x40,0xc1,
0xbf,0x9c,0x73,0x7c,0xf8,0xfc,0x29,0x74,0x0d,0xde,0xc6,0xef,
0x2f,0xc6,0x27,0x7a,0x4e,0x0c,0x1f,0x1f,0xe9,0xff,0xc4,0xdf,
0x5e,0x06,0xf8,0xa3,0x27,0xf1,0xb9,0x17,0x4e,0x9d,0x3c,0x33,
0x7a,0xe5,0xea,0x13,0xbf,0x00,0xf8,0xe3,0x3e,0xfc,0x9d,0x1f,
0x9c,0x3a,0x3d,0x72,0xea,0xa8,0x85,0x3a,0xf0,0x78,0x3e,0xc0,
0x9f,0x6f,0x06,0x1f,0x16,0xfc,0xdc,0xf8,0xf1,0x9f,0xbd,0xf9,
0xe0,0xe4,0x8d,0xbf,0x04,0xbb,0x39,0x18,0xed,0xdf,0x1c,0x7e,
0xf8,0x49,0x7f,0xfb,0xdd,0x67,0xaf,0xfc,0x18,0xa0,0xea,0xd9,
0x9e,0x7f,0x3f,0x0c,0xb1,0x15,0x42,0xe3,0x7f,0xc7,0xf9,0xbb,
0xea,0x10,0x3e,0xe5,0x05,0x6f,0x78,0xc1,0x95,0xc8,0x87,0xe5,
0x9f,0x41,0x08,0x2e,0x41,0x5f,0x74,0xdc,0x0c,0x43,0x30,0x01,
0xef,0x9d,0x83,0xf0,0x26,0xf8,0x5b,0xe8,0x92,0x0f,0x88,0xf5,
0x1d,0xeb,0x22,0x3e,0xfc,0x4a,0xb8,0x45,0xff,0x00,0xab,0xc3,
0xdb,0x38,0x2d,0xc0,0x7d,0x6c,0x39,0x36,0x7a,0x06,0x5f,0x0b,
0x7a,0xae,0x86,0x17,0x2a,0x00,0xf2,0xc2,0x91,0xd8,0x5e,0xfd,
0x7f,0xc8,0x0b,0x40,0xf1,0xe1,0x0a,0xf6,0x91,0xbf,0x1f,0xfc,
0xa5,0x7b,0xf4,0x1f,0xf2,0xc2,0x4f,0x03,0xb9,0xf5,0x32,0xdf,
0xa2,0xdf,0x09,0xef,0x10,0xdf,0x27,0x7a,0x02,0x20,0xc1,0xf7,
0xa3,0x31,0xb1,0x57,0x05,0x7a,0x0c,0x18,0x07,0x3e,0x04,0xc0,
0x19,0x6b,0xf4,0x9e,0x4d,0xc3,0x04,0xc9,0x35,0x82,0xf7,0x03,
0x9a,0xc4,0xef,0xf8,0x58,0x25,0xee,0x6b,0xc7,0xe3,0x8d,0xef,
0x17,0xdc,0x05,0xb1,0x30,0x09,0x8e,0xfd,0x7b,0x22,0x60,0xa1,
0xe0,0x60,0x24,0xef,0xfb,0x5e,0xce,0x5f,0xc8,0xa7,0xe1,0x63,
0x76,0xc4,0x95,0x77,0xe1,0x33,0x28,0x38,0x76,0x60,0x20,0xe0,
0xfb,0xe6,0x80,0x93,0x3f,0x07,0xb5,0x9e,0xab,0xb5,0x5a,0xf0,
0x99,0xc7,0x82,0x3d,0x7c,0x8b,0xff,0x78,0x93,0xe4,0x65,0xd4,
0x04,0xff,0x9f,0x1f,0x8f,0xa9,0x7d,0xec,0xaf,0x00,0x7d,0xf9,
0x3a,0xaa,0x7d,0x3e,0x3a,0x7c,0xb5,0x01,0x7f,0x1f,0xa0,0xd9,
0xe8,0x05,0xab,0xcb,0x7a,0xda,0xfa,0xc8,0xee,0xb2,0xf7,0xd9,
0x5f,0xb3,0x7f,0xe2,0x74,0x39,0xab,0x9d,0x5d,0xce,0x7e,0xe7,
0xc5,0xc2,0xd4,0xc2,0xa1,0xc2,0x77,0x9a,0x3a,0x9a,0x46,0x9b,
0x7e,0x54,0x2c,0x17,0x1f,0x2d,0xbe,0xd7,0x7c,0x77,0xf3,0xb1,
0xe6,0xcb,0xcd,0xaf,0xb7,0xb4,0xb7,0xdc,0xd9,0xf2,0xf4,0x84,
0xa9,0x13,0xbc,0x89,0xce,0xc4,0xbd,0x13,0x6f,0xb4,0xae,0x6c,
0x7d,0xbc,0xf5,0xe6,0xa4,0x07,0x26,0x7d,0x63,0xd2,0xcd,0xb6,
0x5d,0x6d,0x97,0x27,0x2f,0x9e,0xfc,0x72,0x7b,0x47,0xfb,0x68,
0xfb,0x3b,0x53,0xee,0x9d,0xf2,0x4a,0x87,0xd3,0x71,0xa2,0xe3,
0xef,0x3b,0xef,0xea,0xbc,0xda,0xf9,0xab,0xae,0x7d,0x5d,0xd7,
0xba,0x6e,0x76,0x0f,0x75,0x7b,0xdd,0x57,0xba,0xaf,0x4f,0xed,
0x98,0xfa,0xe8,0xd4,0x57,0xa7,0xad,0x99,0xf6,0xcc,0xb4,0x77,
0xa6,0xef,0x9d,0xfe,0xed,0xe9,0xb7,0x66,0x0c,0xce,0xb8,0x3c,
0xe3,0x8d,0x99,0x3b,0x67,0x7e,0x6d,0xe6,0xfb,0xb3,0xee,0x9f,
0x75,0x63,0xb6,0x33,0xfb,0xae,0xd9,0x97,0x67,0xdf,0x9c,0xb3,
0x74,0xce,0xb3,0x73,0x7e,0x30,0x77,0x68,0xee,0x33,0x73,0xdf,
0x99,0xd7,0x37,0xef,0xdc,0xbc,0x2b,0xf3,0xae,0xf4,0xb4,0xe3,
0xbf,0x95,0x3d,0x17,0x7b,0xfe,0xba,0xe7,0x46,0x6f,0x57,0xef,
0x68,0xef,0x73,0xbd,0xb7,0xe6,0xef,0x9f,0xff,0xd4,0x82,0x56,
0xfc,0xe7,0x05,0x7f,0xcf,0x2d,0x78,0x79,0xc1,0x5b,0x7d,0xd0,
0x77,0x67,0xdf,0x91,0xbe,0x4b,0x7d,0xd7,0xfa,0x9b,0xfa,0x1f,
0xea,0x7f,0xbc,0xff,0xc3,0xfe,0x0f,0x17,0xee,0x5b,0xf8,0x12,
0xfe,0x7b,0x6b,0xd1,0xd0,0xa2,0xe7,0x17,0x4f,0x58,0x7c,0x64,
0xf1,0x9f,0x2f,0x69,0x5b,0xd2,0xb3,0xe4,0xfa,0x92,0x0f,0x97,
0x0e,0x2e,0x7d,0x68,0xe9,0x43,0xcb,0xac,0x65,0x4b,0x97,0x3d,
0xb0,0xec,0xea,0xb2,0x9f,0x2e,0xef,0x59,0xbe,0x66,0xf9,0xf3,
0x2b,0x16,0xaf,0xb8,0x7f,0xc5,0xf9,0x15,0x2f,0xad,0x78,0x77,
0xe5,0xc2,0x95,0x57,0x56,0x2d,0x5c,0xf5,0xca,0xaa,0x0f,0x06,
0x1e,0x18,0xb8,0xe1,0xde,0xe6,0x7e,0x6b,0xf5,0xdc,0xd5,0x2f,
0x94,0xe6,0x96,0x1e,0x2f,0xdd,0x5a,0x73,0xff,0x9a,0xef,0xaf,
0x5d,0xbf,0xf6,0xa9,0xb5,0x6f,0x0c,0x2e,0x1c,0x3c,0x3d,0xf8,
0xd3,0x75,0xe7,0xd6,0xbd,0x55,0x5e,0x5f,0xbe,0x52,0xe9,0xa8,
0x8c,0x56,0xae,0xad,0x5f,0xb9,0xfe,0xf9,0x0d,0x4d,0x1b,0x3e,
0xbb,0xe1,0xdd,0x8d,0xeb,0x37,0x5e,0xde,0x34,0x7f,0xd3,0x53,
0x9b,0xde,0xbb,0x6d,0xef,0x6d,0xaf,0x6e,0x5e,0xba,0xf9,0xf9,
0xa1,0x8e,0xa1,0x4f,0x0d,0xbd,0xbe,0x65,0x70,0xcb,0x8b,0x5b,
0x9b,0xb7,0x0e,0x6d,0x7d,0x64,0xeb,0xf5,0xad,0xef,0x6f,0xdb,
0xbf,0xed,0xb5,0xdb,0x97,0xde,0x7e,0xf1,0xf6,0x5b,0xdb,0xf7,
0x6d,0x7f,0x65,0xfb,0x7f,0xde,0xb1,0xe7,0x8e,0x6f,0xed,0xe8,
0xda,0xf1,0xc8,0x8e,0xb7,0x77,0xee,0xd9,0xf9,0xea,0xc7,0x86,
0x7c,0x99,0x1e,0x69,0x05,0x1b,0x8a,0x30,0x11,0x26,0x43,0x27,
0x4c,0x83,0x59,0x30,0x0f,0x16,0xc0,0x22,0x58,0x06,0xab,0xa0,
0x04,0xeb,0x60,0x03,0x6c,0x86,0x6d,0xb0,0x03,0x7e,0x03,0xee,
0x81,0xdd,0x70,0x1f,0x7c,0x12,0x1e,0x84,0x03,0x70,0x18,0x3e,
0x0d,0x27,0xe1,0x0c,0x3c,0x0c,0x9f,0xc1,0x33,0xf0,0x09,0xf8,
0x02,0x7c,0x09,0xbe,0x02,0x30,0x65,0x6d,0xa9,0xdb,0x15,0x3c,
0x0b,0xf8,0xd9,0x3b,0x4e,0x5b,0xd1,0x3d,0xbd,0xf0,0xf1,0x91,
0xe7,0x59,0x75,0xbf,0x86,0x0f,0xc7,0x1b,0x8f,0x07,0x14,0x7d,
0x59,0x6c,0x61,0xd1,0xc6,0xd0,0x02,0x10,0x86,0xa2,0xc8,0x79,
0xda,0xf8,0x89,0xe8,0x8b,0xd8,0xd9,0xeb,0xd6,0x00,0x5f,0xbb,
0x09,0xf9,0x4a,0x1f,0x8f,0x9c,0xba,0x76,0x70,0xe5,0x52,0x84,
0x3f,0x72,0x8b,0xf0,0x77,0xbc,0xaa,0x17,0xbf,0x38,0xc0,0x0c,
0x15,0xa6,0x87,0x7a,0xdf,0xd1,0xe7,0x1b,0xee,0x3d,0x45,0xbc,
0x62,0xf2,0x39,0x94,0x0c,0x7b,0x2c,0x1a,0xee,0x98,0xe4,0x8c,
0x6d,0x4c,0x2e,0xe8,0x06,0xaf,0xe6,0xd9,0xa0,0x85,0x83,0x00,
0xfa,0x82,0x06,0xa4,0x79,0x58,0x28,0x84,0x10,0xd7,0x80,0x06,
0x2b,0x38,0xae,0x01,0x0d,0x77,0x0d,0x8c,0xc0,0xf5,0x49,0xdd,
0xa5,0x4d,0x6f,0xe8,0x24,0xe6,0x66,0x89,0x81,0x82,0xdc,0x2f,
0x4a,0xde,0x2b,0x11,0x73,0xdc,0xc3,0xb6,0x8a,0x37,0x06,0x1e,
0x41,0xc7,0x6a,0xcc,0x97,0x55,0xea,0xf4,0x98,0x6f,0xd6,0xe8,
0x83,0x1a,0xc2,0xe9,0xe9,0xc2,0x89,0x24,0xb2,0xcd,0x65,0xce,
0x95,0x72,0x64,0x57,0x3c,0xf7,0x30,0xd1,0xf0,0xa6,0x0a,0xc1,
0xab,0x17,0x1c,0x8c,0x05,0x07,0x31,0x70,0x63,0x01,0x36,0x22,
0xb0,0x0d,0x20,0xb5,0x91,0xe7,0x3b,0x01,0x89,0x3c,0xa0,0x64,
0x4c,0x24,0x53,0xd2,0x59,0x0f,0x30,0x11,0x41,0x2d,0x2b,0x3f,
0x52,0xbc,0x24,0x7c,0x5d,0x24,0xb6,0xc4,0x73,0x0e,0x22,0xb6,
0x16,0x10,0xfc,0x99,0x3e,0x58,0x29,0xc3,0x1b,0xb7,0xca,0x38,
0xa8,0x7b,0xb1,0x32,0x93,0x1c,0x23,0x82,0x8c,0xa0,0xf4,0x5f,
0x94,0xc6,0x11,0x58,0xd2,0xbe,0x81,0x9a,0x83,0x8f,0x92,0x64,
0x4e,0x97,0xd2,0xf9,0x5b,0x25,0xf8,0x38,0xa2,0x6a,0x35,0x50,
0x3a,0x92,0x31,0xb4,0x48,0xc6,0x90,0xd0,0xb3,0x20,0x90,0x98,
0x45,0x4a,0x52,0x46,0x1b,0x4b,0x78,0x2f,0xc7,0x27,0x59,0xe0,
0x51,0xc5,0xf7,0x82,0x7e,0x06,0xab,0x11,0x36,0x19,0xa2,0x06,
0x6e,0x9d,0x17,0x72,0x1b,0x31,0xce,0x40,0x3a,0xf0,0x35,0x35,
0xa9,0xd1,0xc0,0x0a,0x1c,0xdb,0xf8,0xbe,0xc9,0x3d,0x09,0xca,
0x01,0xf6,0x45,0x43,0x39,0xcc,0xe7,0x09,0x0a,0x13,0xb2,0xad,
0x45,0xcb,0xcd,0xf4,0x25,0x87,0x1f,0x22,0x99,0x01,0x26,0x32,
0x43,0x24,0x37,0x64,0x72,0x90,0x27,0x37,0x50,0xc4,0x37,0x89,
0x64,0x18,0xf3,0x28,0x81,0xe8,0x31,0x32,0xc3,0x48,0x36,0xb6,
0x29,0xc1,0x49,0x69,0x38,0x52,0xc6,0xb3,0xb3,0x90,0xa5,0x7e,
0xa4,0xc5,0xaa,0xe4,0x6d,0xab,0x9e,0xba,0x80,0xc3,0xe3,0x51,
0xa3,0x03,0x17,0xfb,0xf1,0xa8,0xc8,0x67,0x91,0x37,0xca,0x14,
0xd3,0xc1,0x78,0x93,0x7f,0x4f,0x47,0x14,0x37,0x66,0xac,0x2e,
0x21,0xe3,0x72,0xb8,0x22,0x41,0xa5,0x3e,0x13,0xc0,0x14,0x0d,
0x9d,0xc8,0xa5,0xbe,0xaa,0xb5,0x4e,0xcc,0xc4,0x84,0x13,0x42,
0x1d,0x48,0x31,0x75,0x88,0x6d,0x5b,0x8b,0x79,0x3b,0xb4,0xe6,
0x28,0x57,0x3a,0x91,0x5a,0x8d,0x38,0x2f,0xa5,0x00,0x35,0x0b,
0x4d,0x09,0x10,0x03,0x31,0x45,0x03,0x06,0xca,0x2a,0xe1,0xf1,
0xb2,0x8a,0x27,0x85,0x42,0xb6,0x89,0x99,0x9b,0x91,0x2f,0x06,
0x50,0x84,0xf1,0x56,0x39,0x0c,0x5c,0xdc,0xcb,0xe4,0x36,0xab,
0xbe,0x55,0xf9,0x03,0xdf,0x59,0x4f,0x6e,0xe7,0x4a,0x6d,0x2d,
0xdc,0xb2,0x12,0xbb,0x0a,0x0d,0x41,0x71,0xc4,0xef,0x46,0xbc,
0xe2,0x6a,0x78,0xd8,0xa4,0xbc,0x14,0x69,0xa0,0xd4,0x42,0xf5,
0x49,0x52,0xd5,0xe5,0x15,0x27,0xf4,0x87,0x09,0xfb,0x26,0xd1,
0xe5,0x36,0x6d,0xa9,0x26,0x96,0x4b,0x35,0xfe,0x6e,0x53,0x68,
0xaf,0x30,0xb6,0x51,0xe6,0xfb,0x1c,0x4b,0x10,0x8f,0x94,0xbe,
0x58,0x82,0x53,0x3f,0xe6,0x2d,0xb6,0xf9,0xa8,0x19,0xe7,0x32,
0x12,0x9a,0xd8,0xfa,0xd6,0x6f,0x80,0x96,0x18,0x35,0x96,0x8c,
0x8e,0xad,0xbe,0xf8,0xb3,0xf1,0x67,0xf4,0xe6,0x4b,0x40,0x11,
0x79,0x3c,0x64,0x2c,0xbe,0x3d,0x64,0x6c,0x60,0xda,0xb6,0x0a,
0xa2,0xcd,0x02,0x98,0x3b,0x05,0x30,0x32,0x38,0xe8,0x26,0xdc,
0x34,0xff,0x29,0x05,0x39,0xb8,0xa7,0x98,0x77,0x73,0xb5,0x77,
0x12,0x19,0xc9,0x6a,0x6a,0xa9,0x86,0x0e,0xe2,0x24,0x33,0x8d,
0xe3,0x1a,0x7e,0x3c,0x4c,0x3a,0x93,0x5c,0x8e,0x4d,0x27,0x3a,
0xee,0xa5,0xaf,0x33,0x07,0x89,0xad,0x90,0xd8,0x2f,0x4c,0xc3,
0x53,0xe4,0x4e,0xe8,0x32,0x7a,0x66,0x6e,0x22,0x57,0x87,0xaa,
0xe2,0x86,0x3b,0x1f,0x64,0x12,0x45,0x24,0x79,0x90,0x97,0x98,
0x03,0xa9,0x4f,0x1c,0x87,0x3b,0xac,0xc0,0x31,0x4e,0x5e,0x6d,
0x5d,0x9b,0x76,0xba,0xb6,0xbf,0x9f,0xc4,0xb1,0x3a,0x15,0xc6,
0xef,0x1a,0x68,0x5d,0x6a,0x9e,0xf2,0x6d,0x7a,0x06,0xea,0x31,
0xa8,0x8f,0xbe,0x89,0x6d,0xef,0xa9,0xd8,0xf6,0x3c,0x58,0x58,
0x4f,0x9f,0x67,0x49,0xa4,0xfc,0xea,0x25,0x8e,0x9e,0xba,0x76,
0x33,0x8b,0xcd,0x74,0x36,0x20,0xfe,0x2c,0xa6,0x49,0x96,0x0c,
0xe1,0x6b,0x42,0x2b,0x7d,0x37,0x4b,0xdb,0x26,0xd1,0xb0,0x54,
0x33,0x71,0x19,0xbe,0x66,0x48,0xad,0xd5,0x2a,0xc8,0x23,0xcb,
0x39,0xf4,0x52,0xf6,0x71,0xea,0x81,0x41,0x38,0x66,0x7b,0xbc,
0xe5,0x9a,0x68,0x0e,0xf4,0x2a,0xca,0x84,0xec,0x9c,0x88,0xe3,
0x7b,0x16,0xd4,0xe1,0x25,0x18,0xcf,0x93,0x5e,0x4d,0x4f,0x4d,
0x36,0x6f,0x44,0x22,0x8a,0x3f,0x5f,0x2c,0x30,0x0a,0x48,0xb4,
0xfa,0xb1,0x21,0x4b,0x06,0x67,0x86,0x56,0xc5,0x9c,0x58,0x04,
0xca,0x84,0x25,0x15,0x7d,0x89,0xc0,0xaf,0x51,0x1f,0x4b,0x51,
0x75,0x2c,0x54,0x30,0x44,0x6d,0x28,0x7a,0xfe,0xba,0x8c,0xf2,
0x25,0x45,0xbf,0xc6,0xa3,0xe4,0x5e,0xac,0x9e,0xe3,0xf4,0x51,
0xa4,0xb4,0x63,0x89,0xa9,0xe7,0xb7,0x2b,0xfa,0x8b,0x45,0xc9,
0x53,0x60,0xff,0xe6,0xa5,0xcd,0xb2,0xf3,0xaa,0x4e,0xdb,0x70,
0x0a,0x91,0x0f,0x70,0x99,0xfc,0x80,0xab,0x61,0x17,0xf6,0x0a,
0xf6,0xa9,0x3c,0x49,0x60,0x04,0x22,0x89,0x55,0x88,0xff,0xc9,
0x9d,0x24,0xc9,0x59,0xa7,0x91,0xd8,0x00,0x7b,0x8a,0x97,0x3b,
0x29,0x09,0xf6,0xd9,0x9c,0x0a,0x6b,0x3f,0x93,0xfb,0x5e,0x92,
0x40,0x8a,0x25,0x2e,0x02,0x72,0x9b,0x64,0x57,0xd2,0x2c,0x4b,
0xbc,0x31,0xb7,0xac,0xda,0x95,0xf1,0x21,0xcd,0x23,0xa9,0xd8,
0x28,0x28,0x99,0x6e,0x42,0xd3,0xc4,0x8b,0xf3,0x49,0xb6,0x8e,
0x3e,0xd1,0x8a,0x25,0x74,0x1a,0x5a,0xbc,0x05,0x56,0xa7,0x67,
0x63,0x09,0x63,0xa4,0x82,0xd7,0x13,0x23,0xbe,0x8e,0xaf,0x79,
0x66,0xbe,0x8b,0x09,0x2d,0x4a,0x44,0x7c,0x42,0x95,0x26,0xfa,
0x46,0xd6,0xf8,0xdb,0xc4,0xae,0x92,0xf4,0x97,0x53,0xcb,0x5c,
0xfe,0x47,0x50,0xd6,0x61,0x9f,0x31,0xe3,0x66,0xe3,0x14,0x73,
0x90,0x04,0x5e,0x44,0xfb,0x98,0xa4,0xf9,0xa2,0x4f,0xab,0x30,
0x7f,0xa2,0xa2,0x3f,0x72,0xeb,0x67,0x44,0x7a,0x8e,0x67,0xb4,
0x2b,0x3b,0x57,0x9a,0x38,0xee,0x6c,0x50,0x9d,0x8e,0xd8,0x87,
0xca,0x4e,0x13,0x6d,0x94,0x6b,0xe0,0x3b,0x6f,0xcc,0x6c,0x05,
0x46,0x80,0xef,0x44,0x67,0x66,0x52,0xf2,0x55,0xbd,0x2c,0x05,
0xd4,0x55,0x4f,0x33,0x45,0x62,0x13,0xf4,0x2a,0xda,0x15,0xb1,
0xcc,0xe2,0xcd,0xf3,0x08,0xce,0xac,0xcd,0xc0,0xd8,0x15,0x69,
0x69,0x46,0x82,0x8c,0x5f,0x8b,0xde,0xcc,0xf3,0x5d,0x0a,0x8a,
0xd6,0x2b,0x25,0x8c,0xd3,0x80,0x13,0x29,0xb8,0xd5,0xd5,0x66,
0x6b,0x2e,0x3d,0xa5,0x7c,0x57,0xe2,0x79,0xba,0x0c,0xbf,0x55,
0x29,0x5f,0xc5,0x1e,0x97,0x39,0xee,0x4a,0x72,0x31,0x2a,0xd9,
0x52,0xca,0xde,0x26,0xb3,0xa5,0xa9,0x8f,0xa5,0x37,0xa7,0x27,
0xe7,0xd4,0x7f,0x08,0xf3,0xa6,0x45,0x85,0x27,0x1d,0x5d,0xe5,
0x8f,0x20,0xbf,0x38,0x24,0x19,0x6b,0x4e,0x5d,0x42,0x27,0xa7,
0x32,0x86,0xb3,0xed,0xd6,0x1c,0x01,0x98,0xe1,0x48,0x05,0x3f,
0x52,0xef,0x94,0xc8,0x99,0xe8,0xe1,0x08,0x29,0xd6,0x6e,0x94,
0x24,0x19,0x65,0xe4,0x51,0xe5,0x6e,0xa4,0x35,0x22,0x19,0x0c,
0x9e,0xa2,0x45,0x80,0x5a,0x58,0x11,0xcf,0xd4,0x5c,0x76,0xe7,
0x57,0x94,0xb2,0x57,0xb3,0x82,0x4c,0x4d,0x0c,0x4b,0x52,0x89,
0xe4,0x63,0x2a,0x1a,0x04,0x44,0x72,0x4d,0x5c,0x37,0xc4,0xf1,
0x07,0x48,0x3b,0x45,0x27,0xde,0x1c,0x52,0x28,0x89,0x94,0x33,
0x29,0x39,0x4b,0xdd,0x6e,0x89,0x63,0x92,0x9e,0x4e,0x4c,0xb2,
0x28,0x89,0xdf,0x69,0x47,0x5c,0x3d,0xb1,0x8d,0x99,0x58,0x9a,
0xea,0x4c,0xc7,0xe4,0xa2,0x6d,0x8d,0xf8,0x11,0x2f,0xd6,0x27,
0xce,0xea,0x92,0x41,0x6e,0x6b,0x1c,0xf0,0x8c,0x24,0x15,0x35,
0xba,0x78,0xb6,0x48,0xeb,0x91,0x8b,0xe3,0x84,0x75,0xf4,0x31,
0x6d,0xca,0xf7,0xb9,0xb9,0x14,0x35,0x1a,0x8c,0x69,0x10,0x22,
0x93,0x13,0xc9,0xa3,0x07,0x57,0xd3,0x14,0x14,0xec,0x76,0x94,
0xe0,0x3b,0x54,0x86,0x55,0x5d,0x25,0xe3,0xe3,0xb5,0xdb,0x37,
0x57,0x94,0xf0,0xca,0xd4,0x20,0xd7,0xc3,0x2b,0x62,0x7d,0x20,
0x67,0x9c,0x04,0x60,0xcb,0xc4,0x24,0x4c,0xe2,0x10,0x9e,0x6a,
0x1c,0xa2,0x1e,0x19,0xd4,0xab,0x2c,0x7b,0x90,0xde,0x8c,0x00,
0x68,0x0e,0xe2,0xd6,0x3c,0x38,0x02,0xca,0x20,0x89,0x46,0x46,
0x6c,0x86,0xde,0x01,0x81,0xa5,0x12,0xc6,0x4e,0x73,0xec,0x80,
0xdc,0xfb,0xb1,0x54,0xb6,0x80,0xad,0xef,0x0c,0x09,0x2a,0x1c,
0x06,0x39,0xa0,0x76,0x65,0xfa,0x65,0xa8,0xa7,0x1b,0x99,0x26,
0x52,0x8c,0xb2,0x88,0xb4,0x86,0xa1,0xe2,0xd7,0x25,0x08,0xe9,
0x46,0xd5,0x25,0xe4,0x59,0x51,0x3c,0xcb,0x49,0x3e,0x90,0xb4,
0xfe,0x5e,0x63,0x9e,0x53,0x71,0x04,0xd6,0xb7,0xeb,0x35,0xd8,
0x4f,0x6b,0xeb,0x33,0x9e,0xa0,0x68,0xc7,0xd2,0x56,0x14,0x6d,
0x4a,0x7a,0xc2,0x38,0xaf,0xab,0x30,0x97,0xad,0x46,0x8e,0x13,
0x19,0x4a,0x59,0x41,0x35,0xd5,0x98,0x96,0x1a,0x8b,0x6c,0x0a,
0x2d,0xfd,0x60,0x84,0x59,0xfd,0x38,0x2e,0x1b,0xcd,0xd5,0x55,
0x08,0xfa,0x70,0x35,0x4a,0xe7,0x15,0x35,0x74,0x9d,0x01,0x5c,
0x13,0xa4,0x7c,0x15,0x50,0xa7,0xc4,0xc9,0x1f,0x71,0xb3,0xe2,
0xfe,0x40,0x10,0xa4,0xb9,0x71,0xc9,0x60,0x60,0x92,0x02,0x3f,
0xf3,0x70,0x48,0x46,0x52,0xb9,0x5e,0x0a,0x02,0xba,0x0e,0x5d,
0x05,0x29,0x98,0x6f,0x27,0x21,0xc8,0xcd,0x39,0x50,0xf6,0xaf,
0xc4,0xda,0xa2,0xaa,0xa7,0x10,0xf0,0xaa,0x1b,0x6c,0x95,0xd9,
0xa4,0x21,0x9b,0xcc,0x62,0xa1,0x28,0x8f,0xa9,0xd4,0x26,0xbd,
0x02,0x0f,0x29,0xc5,0x37,0x85,0x41,0x4d,0x2b,0x2f,0xd7,0xa2,
0xa3,0xab,0x8c,0xe2,0x97,0x3c,0x9d,0x96,0x13,0xa7,0xa4,0xc2,
0x93,0x96,0x8e,0x52,0xd2,0xc0,0x67,0x89,0xf1,0x01,0x32,0xeb,
0xd7,0xaa,0x74,0x91,0x72,0x28,0x1c,0xad,0x71,0x90,0xe1,0xbd,
0x0d,0xb2,0xeb,0x15,0xb8,0xd2,0xd8,0xac,0x37,0xd7,0xf7,0xa2,
0x38,0xa4,0x28,0x52,0xc3,0xc6,0x1d,0x2d,0xc5,0xf9,0x5e,0xd3,
0xf0,0xff,0x0a,0x7c,0xbb,0x99,0x5d,0x85,0x46,0xd7,0x01,0x33,
0x0b,0x5d,0xf4,0x62,0x8c,0xe0,0xaf,0x87,0xf3,0x6d,0x7c,0x90,
0xaf,0xcb,0x2b,0x2a,0x3e,0x91,0x69,0xc8,0x53,0x19,0x57,0x9d,
0x39,0x38,0x11,0xe0,0xb0,0x9b,0xb3,0x26,0x28,0xdc,0xd1,0x40,
0x95,0xdf,0xad,0x04,0x79,0xdc,0x5a,0x6a,0x8a,0xbf,0x7a,0x39,
0x35,0xb8,0x51,0x75,0x56,0x5c,0x50,0x6b,0x01,0xef,0x06,0x61,
0xfc,0xc6,0x8f,0x26,0xd6,0xb9,0x9e,0x80,0xd5,0x65,0xb2,0xd8,
0x8e,0xb0,0x9a,0xac,0x4a,0xc4,0x15,0x92,0x52,0x32,0xd0,0x9c,
0x9e,0xe0,0x04,0xb1,0x64,0x90,0xae,0xfb,0xf3,0x04,0xeb,0xfe,
0xda,0x95,0x78,0x22,0x53,0xfb,0xa0,0x1a,0x3f,0x21,0x3f,0x97,
0xcc,0xa5,0x4c,0x28,0x45,0x8f,0x4d,0x82,0x91,0x37,0x61,0x98,
0x31,0x97,0x20,0x66,0xed,0x40,0xbf,0xda,0x5a,0xf9,0x2c,0x12,
0xed,0x50,0xc6,0x41,0xb2,0xd6,0x96,0xd4,0x66,0xac,0xdd,0xa3,
0xe0,0x23,0x2b,0xad,0xed,0xe4,0x47,0x9b,0x0c,0x85,0x8d,0x66,
0xec,0x25,0x6f,0x7c,0x2a,0xe3,0x66,0xc6,0xcb,0x8e,0x5c,0x2f,
0xe2,0x12,0xac,0x41,0xb5,0xd9,0xf1,0x53,0x6b,0x74,0x89,0x4c,
0x02,0xa1,0x99,0x1d,0x30,0xaf,0x93,0x66,0xed,0x2b,0x72,0xcd,
0x52,0x49,0x50,0xb7,0x64,0xd1,0x05,0xee,0xe9,0xa4,0x22,0xea,
0x92,0x54,0x53,0xca,0x7e,0x6c,0xd0,0x1f,0x6b,0x41,0x3b,0x36,
0x58,0xca,0x97,0x3b,0xfa,0x95,0x14,0x89,0xf2,0x15,0x57,0x54,
0x18,0xf5,0xd5,0x08,0x68,0xeb,0x5f,0x84,0xb2,0x21,0x12,0x0a,
0x14,0x08,0xdf,0x04,0xa5,0xa3,0xb0,0xb8,0x6b,0x01,0xa0,0xde,
0xda,0xb5,0x92,0x82,0x87,0xa9,0x5a,0x77,0x53,0xe2,0xf6,0x30,
0x21,0x97,0x40,0x10,0x15,0x6b,0xe4,0x39,0xb3,0x62,0x22,0x1a,
0x19,0x0d,0xe7,0x1d,0xd3,0xec,0x71,0xcc,0x3b,0xd5,0x9c,0x34,
0xb2,0x21,0xef,0x84,0x7d,0x10,0xbc,0x4c,0x1f,0x84,0x98,0x5f,
0x3c,0xba,0x0d,0x02,0xfe,0x3c,0xf0,0xfb,0x26,0x14,0x88,0x2c,
0x24,0xf1,0x8d,0xa0,0xef,0x4a,0x46,0x6e,0x2a,0xf4,0x5d,0xe1,
0xae,0x3c,0x0f,0xd6,0xb5,0x19,0xf6,0x71,0xe1,0x5f,0x6f,0x52,
0x4e,0x1f,0x83,0x44,0xdf,0xb0,0xd1,0x56,0x9e,0xfc,0xe6,0xf5,
0x24,0x89,0x9b,0x19,0x00,0xb5,0xb4,0x2e,0x4f,0x56,0x1b,0xf7,
0x0b,0x28,0x46,0x36,0x5e,0x26,0x9f,0x3b,0x25,0x3f,0x9f,0x8b,
0x78,0xf6,0x4f,0x84,0x6f,0x66,0x0d,0x62,0x16,0xc7,0x4c,0xb1,
0x4f,0x98,0xf3,0xf2,0x57,0x2f,0x2a,0xd5,0xb0,0xf0,0x62,0xb5,
0xae,0x44,0x66,0xb0,0xba,0x23,0xab,0x25,0xa8,0xad,0x9a,0xca,
0xd0,0x88,0xcf,0x93,0x38,0x30,0x8e,0x0a,0x91,0x1a,0x21,0xdd,
0x24,0x3c,0x5a,0x03,0x35,0x5e,0xc9,0xca,0xfe,0x4e,0xe6,0xce,
0x85,0x08,0x43,0xe1,0x0d,0x33,0xa2,0xbf,0x5b,0x3b,0xb6,0xbe,
0x3b,0x2b,0xf1,0x79,0xd1,0x0b,0x59,0xbc,0x91,0x27,0xe9,0xb9,
0x11,0x76,0x46,0xec,0xa3,0x44,0x75,0x5a,0x06,0x62,0x5d,0xdf,
0x8e,0x13,0x51,0x59,0xd5,0x03,0xcf,0xc8,0x6f,0x56,0x76,0xab,
0xd3,0x3b,0x94,0x57,0xa1,0x8e,0xce,0xb5,0x8f,0x4b,0xd1,0x98,
0x55,0x2b,0xac,0xe2,0x85,0x92,0x92,0x22,0xab,0x70,0xa4,0xb5,
0xbc,0x54,0x8d,0xbc,0x8f,0x97,0xad,0xe6,0x7b,0xdb,0xfc,0x3b,
0x45,0x75,0xcc,0xa0,0xe8,0x63,0x72,0x25,0x77,0x49,0xa0,0x2b,
0x64,0xdd,0x6a,0x18,0x69,0x9e,0xf6,0x99,0x22,0xfb,0xd6,0xf8,
0xed,0xa5,0x40,0x93,0x1f,0xdb,0x95,0x6b,0xcb,0x95,0x7d,0x44,
0x61,0xe7,0x0e,0x2f,0xeb,0x23,0xea,0xf6,0xed,0xa0,0x72,0x71,
0x9e,0x5e,0x2e,0xae,0x11,0x6b,0x42,0xc9,0x58,0x27,0xa7,0xde,
0x92,0x5e,0x27,0x1a,0x88,0x3a,0x3d,0x97,0x3f,0xf2,0xdb,0x55,
0xed,0x60,0xa5,0x1a,0x46,0x99,0xb7,0x27,0x9a,0x6e,0xa4,0xbb,
0xa7,0xb5,0x56,0x61,0x82,0x34,0x1e,0xc6,0x8d,0x86,0xd9,0xaa,
0x91,0x30,0xd6,0x7a,0xaa,0x01,0x77,0x9d,0x81,0x46,0x0d,0x8f,
0x28,0x02,0x44,0xc5,0xc3,0x4a,0x4c,0x2c,0xb1,0xa0,0x50,0xf3,
0x92,0x46,0x98,0x89,0x40,0xad,0x5e,0xdd,0x0e,0xf8,0xf5,0x6c,
0x4e,0x6a,0x6b,0xa6,0x52,0x35,0xbd,0x96,0xdf,0x89,0xb5,0x9e,
0x35,0x51,0x7e,0xff,0x48,0xe1,0xea,0x4f,0xd9,0xda,0x79,0xd1,
0x5a,0x20,0x97,0x53,0x1f,0x28,0x58,0xe5,0x0a,0x69,0xbe,0x20,
0xae,0x62,0x46,0xd4,0x36,0x79,0x23,0x9d,0x6b,0x75,0x2d,0x8e,
0x0a,0x10,0x36,0xd1,0xa7,0x91,0x64,0x9d,0x20,0x20,0x41,0xc5,
0x9f,0x98,0xea,0xc4,0x32,0x8b,0xbc,0x31,0xc6,0xfd,0x1c,0xa4,
0x3d,0x2c,0x64,0xba,0x81,0xee,0xb1,0x08,0x5e,0x26,0xa1,0x21,
0xeb,0xec,0x50,0x0c,0xd7,0x48,0x66,0x6d,0xf3,0x4e,0x49,0xae,
0x95,0xb8,0x02,0x67,0x52,0xc2,0xdc,0xba,0xf9,0xcf,0xef,0x5f,
0x9a,0xcb,0x83,0x25,0xce,0x7a,0x34,0x56,0x8f,0xca,0xde,0x57,
0xe4,0xc5,0x24,0xca,0x4c,0x96,0xd6,0x33,0x67,0x1a,0xc8,0x8d,
0x69,0x0d,0xac,0xc3,0xeb,0x01,0xab,0xe2,0x33,0x39,0xdc,0x38,
0x3b,0x1e,0x24,0xb7,0xee,0x28,0xb7,0x57,0x22,0xaf,0xf7,0x21,
0x57,0x6c,0x11,0xfd,0xc8,0x6c,0x85,0xfc,0x4c,0x6e,0x27,0x3c,
0x81,0xbd,0x40,0x29,0xa2,0xb8,0x19,0x99,0x40,0x8c,0x92,0xea,
0xc0,0x9f,0x67,0x96,0xb8,0xf6,0x8a,0xe4,0x37,0xca,0x4b,0xe1,
0x56,0xbf,0x92,0xe6,0xb2,0x58,0x86,0x83,0xc9,0x3d,0x79,0x92,
0x86,0x00,0x18,0x72,0xee,0xe8,0xeb,0x88,0x20,0xa3,0x42,0xea,
0x88,0x42,0x52,0x27,0x96,0x54,0x3d,0xfb,0x7e,0x07,0xd4,0x54,
0x7d,0x2c,0xa5,0xfc,0xa5,0xcb,0x99,0x9d,0x25,0xca,0xdf,0x13,
0x56,0x9d,0x10,0x35,0x11,0x63,0xa0,0x12,0x94,0xc8,0xac,0x03,
0xd6,0x59,0x4b,0x28,0xed,0x11,0xc3,0x8b,0x9e,0x94,0x24,0xc7,
0x2a,0xb2,0x99,0x8a,0x4b,0xf0,0x3b,0xa6,0x56,0x45,0x2f,0x76,
0xbd,0xb2,0xa4,0xc5,0xef,0xed,0x6e,0xf1,0xfb,0x15,0x65,0xac,
0x1d,0xde,0x3e,0x93,0x4d,0xb1,0x80,0x7f,0x23,0x07,0xe1,0xf7,
0x2d,0xb2,0x6e,0x1f,0x0a,0x8c,0x57,0x8a,0x38,0x46,0x5a,0x20,
0x9b,0x24,0xb5,0x97,0x05,0x79,0x85,0x6b,0x7c,0x31,0x49,0x5b,
0x48,0xc8,0x9d,0x87,0x48,0xd1,0xee,0xcf,0xc8,0x42,0xb9,0x44,
0x4c,0xeb,0x87,0xbc,0xfc,0xfa,0xa1,0x3c,0xde,0xf1,0xd8,0x2a,
0x85,0xdc,0x0c,0x3f,0x86,0x7b,0xaa,0x41,0xcc,0xf8,0xbe,0x6c,
0xae,0xa4,0x57,0x92,0x3b,0x61,0x67,0x7c,0x81,0x89,0x97,0x91,
0x78,0xb5,0xc8,0x99,0x9f,0xcd,0xab,0x90,0x5d,0x84,0x92,0xb8,
0x8b,0x71,0x6f,0x6f,0x18,0x7f,0xf8,0x55,0x2b,0xd7,0x88,0x8a,
0x7e,0x21,0xfc,0x64,0x59,0x1e,0x50,0x1f,0xa8,0x03,0x03,0x51,
0x7f,0x84,0x42,0x1d,0x72,0x91,0x27,0xd9,0x0b,0x1c,0x39,0x58,
0x10,0xd8,0xfa,0xaa,0x7c,0x01,0x54,0xcb,0xbd,0x48,0xd8,0x54,
0x61,0x5c,0x58,0x83,0xad,0xed,0x93,0xf5,0x35,0xb3,0x39,0xbd,
0xcc,0xb8,0x99,0xfe,0x34,0x7a,0x4a,0xb6,0x89,0x11,0xc6,0xa6,
0xe2,0xb5,0x7f,0x05,0xf5,0x35,0xd4,0x73,0xc8,0xda,0xf0,0x5f,
0x47,0xef,0x2e,0x47,0x39,0x0c,0x48,0xaf,0x67,0xd4,0x80,0x29,
0x18,0x97,0x5d,0x27,0x4c,0x49,0x0d,0xfc,0x78,0x00,0xd5,0xa5,
0x2d,0x43,0x32,0xfa,0xa4,0x11,0x34,0xab,0x26,0x0b,0x61,0x35,
0x80,0xd4,0xa3,0x1e,0x98,0xc2,0x4a,0xd9,0x38,0x0d,0x80,0x35,
0x75,0x61,0xc7,0x11,0xd6,0x78,0xed,0xad,0x46,0x5c,0xa8,0x21,
0xb4,0x04,0x2d,0x3a,0x82,0x16,0x58,0x75,0xe8,0x3b,0x37,0x67,
0xbd,0x76,0x9d,0x7c,0x0b,0x86,0xdc,0xab,0xa7,0xef,0xa6,0x19,
0xc6,0x1e,0xb8,0xb5,0xe8,0xbc,0xd5,0xad,0xae,0x06,0xf4,0xb4,
0x76,0x18,0xf3,0xe8,0xc8,0x50,0xa4,0xc9,0xb2,0xfe,0x90,0xb1,
0xc9,0x1f,0xe6,0x92,0xaa,0x9e,0x4e,0x5f,0x68,0x93,0xce,0x81,
0x05,0xd6,0xa2,0x17,0x75,0x11,0x8c,0xcd,0x55,0xed,0x16,0x2a,
0x71,0xff,0x76,0x4d,0x39,0x44,0xe9,0x47,0xdd,0xce,0x75,0xbc,
0x2c,0x59,0x95,0xcc,0x82,0x32,0x05,0xea,0x9a,0x6a,0x24,0x84,
0xcc,0x0c,0x2e,0x61,0xb6,0xcf,0x04,0xae,0x50,0x57,0x36,0x1c,
0xb2,0xb8,0x3e,0x05,0x0a,0x46,0xbf,0xbd,0x52,0x6a,0x24,0xe5,
0x38,0x99,0x31,0x01,0x8c,0x86,0xf4,0x73,0x3c,0x7d,0xfd,0xd8,
0x48,0xbe,0x94,0x83,0x06,0xfa,0x70,0x4d,0x0a,0xfb,0xf6,0x15,
0xf2,0x6a,0x4e,0x32,0x33,0xac,0x90,0xd7,0x93,0x20,0x93,0xd6,
0x8b,0x5d,0x75,0x15,0xf4,0x2b,0x8f,0x2b,0x33,0x43,0xf2,0xc6,
0x65,0xa5,0x01,0x3c,0xb3,0x91,0x01,0xb4,0x29,0x8c,0x6d,0x9d,
0x98,0xc3,0x15,0x30,0x57,0x83,0x1c,0xec,0xa9,0xe2,0x4f,0x3d,
0x37,0xaf,0x3c,0x3a,0xf1,0xd0,0x54,0x52,0xf2,0xdd,0xda,0xf9,
0xda,0xdd,0xfa,0xdd,0x7d,0x8d,0xba,0xc7,0x26,0x01,0x7e,0x4e,
0xeb,0x58,0xd0,0xd6,0xc8,0x33,0x8c,0xec,0xb1,0xd0,0xff,0x16,
0x5a,0xd6,0x8d,0xec,0xc0,0x47,0x67,0x8d,0xb4,0xbb,0xf0,0x19,
0x98,0xde,0x6c,0x3d,0xba,0x22,0x7e,0x94,0xfc,0xdf,0xbc,0x5e,
0x7c,0xfc,0x16,0x42,0x6c,0xa3,0x21,0x0d,0xfd,0x07,0xda,0x30,
0xe4,0xfa,0xbb,0x32,0x18,0x18,0x3f,0xb7,0x71,0x40,0x18,0xf8,
0xed,0xaa,0xfe,0xad,0x5a,0x7f,0xc4,0xaa,0x0e,0x50,0xea,0x80,
0x19,0xc0,0xa5,0xea,0xcb,0xe6,0xc1,0xc5,0xfa,0xb0,0x0d,0x85,
0xab,0x5d,0xd9,0x77,0x55,0xf2,0x5b,0x73,0x62,0xde,0xea,0xf4,
0x01,0x0d,0x9e,0x93,0xfe,0x56,0x56,0xa7,0xa0,0x6a,0x45,0xb8,
0x7a,0x4f,0x61,0xed,0x5e,0x1a,0x0f,0x76,0x0c,0xfd,0x63,0x5e,
0x9f,0x23,0x9e,0xa4,0x95,0x49,0x66,0x32,0x0e,0x9a,0x78,0x86,
0x49,0xc4,0x97,0x12,0xb4,0xb4,0x20,0x26,0xb2,0x64,0xb6,0xa1,
0x7b,0x58,0x4f,0xcc,0xb1,0x9e,0x9e,0x82,0x28,0xa4,0x8d,0x62,
0x4b,0x41,0xed,0xa8,0xa3,0x71,0xdc,0xb1,0x5e,0xb8,0x12,0x5f,
0x6a,0xbc,0x20,0x6b,0x50,0xec,0xb1,0x6e,0xda,0xc9,0xfb,0xa5,
0x8a,0xe0,0x34,0x8a,0x1d,0x9b,0xc4,0xe3,0xea,0x82,0x0f,0x34,
0x61,0x03,0x4d,0xe6,0x9c,0xa4,0x90,0x83,0x51,0xe2,0x48,0xb6,
0x3e,0x36,0xe2,0x3e,0x90,0x16,0xc8,0xaa,0x39,0x33,0x1a,0x35,
0x9f,0x9d,0x1a,0x1d,0x7c,0xb8,0xf5,0x85,0x5e,0x26,0xdd,0x45,
0x75,0x9e,0xd0,0xaa,0x2d,0x9c,0x11,0xf6,0x8a,0x73,0x3c,0x53,
0xfb,0xda,0xad,0x33,0xc2,0x99,0x57,0x19,0x2a,0xf8,0x15,0x40,
0xc9,0xef,0x55,0x44,0xc9,0x2d,0x07,0x3c,0xf3,0x87,0x41,0xcd,
0x23,0xeb,0xd9,0x9b,0xf5,0xb7,0x4b,0x4a,0x72,0x44,0x3d,0xee,
0x1c,0xd0,0x91,0x0a,0x26,0xb5,0x9b,0x85,0x06,0xc0,0x41,0x44,
0x07,0x1a,0x05,0x89,0x61,0x2f,0x39,0x4e,0xe4,0xc0,0x94,0x32,
0x69,0x34,0x21,0x0f,0x26,0x75,0xa8,0xea,0x88,0x87,0x35,0x10,
0x32,0xb2,0x1b,0x46,0x3e,0x6c,0x3a,0x34,0xd3,0xfc,0x8d,0x42,
0xd4,0x28,0x98,0xf2,0xc1,0xb1,0x4d,0xe8,0x64,0x12,0x77,0x2e,
0x35,0x68,0x4e,0xa1,0x34,0x1f,0x90,0x33,0xb1,0x3c,0x6d,0xeb,
0x01,0xdb,0xf5,0xfa,0xeb,0x85,0xee,0x13,0xaf,0x18,0x52,0xfd,
0x8d,0x15,0x79,0x0d,0x69,0xd5,0x23,0x96,0x06,0x35,0x6a,0xc5,
0x50,0xa8,0xf7,0x26,0x2b,0xf6,0x1f,0xd1,0xee,0x96,0x99,0xf9,
0x2d,0x66,0x6e,0xe7,0xcc,0x9a,0xe2,0xd2,0xc1,0x06,0xea,0x23,
0xe3,0xbe,0x93,0xb5,0x0c,0x14,0x54,0xef,0x49,0x6d,0x31,0x6e,
0xaa,0x93,0xec,0x7a,0x61,0x49,0x06,0xd9,0x40,0x60,0x1a,0xa5,
0x93,0xea,0x82,0xa8,0xaa,0x04,0x94,0x11,0x8d,0xcc,0xe5,0x76,
0x1d,0x7d,0x4e,0xe5,0xe0,0xd8,0xba,0xb0,0xb4,0x48,0x7a,0x1c,
0x66,0x66,0x8a,0xa8,0xbb,0x61,0xb6,0x45,0xa5,0x68,0x10,0xb9,
0xf7,0xa3,0xb8,0x59,0xa9,0x23,0x66,0xce,0x0d,0x93,0x3e,0x8e,
0xd2,0xfa,0x4c,0x96,0xeb,0x64,0x77,0xae,0xf2,0x17,0x09,0xc9,
0xe0,0xf5,0xc4,0xf0,0x96,0x14,0xf0,0xcb,0xbd,0x65,0xb5,0x91,
0x36,0x93,0xcb,0x89,0x84,0xe9,0x76,0x19,0xa6,0x3b,0xae,0xa5,
0x0b,0x3e,0x45,0x2d,0x86,0x0d,0xd6,0x28,0x91,0xbe,0xbc,0x96,
0x8c,0x2c,0x49,0x32,0xc1,0xaa,0xbe,0x7d,0x9a,0x1b,0xad,0xf2,
0x5c,0x4c,0x71,0x8b,0x58,0x95,0x85,0xae,0x7a,0x6b,0xb1,0xa5,
0xfa,0x4b,0x85,0x3e,0x35,0xf0,0x72,0xfa,0x4d,0xaa,0x4b,0x7b,
0xdd,0x75,0xe4,0x85,0x3a,0xc6,0x4e,0xf8,0x4e,0x8d,0x1a,0xbd,
0xc1,0x1a,0xe9,0x3c,0x1d,0xa5,0xf0,0x3b,0xdb,0xfc,0xde,0xf5,
0xe2,0xe9,0xa1,0x02,0x49,0x03,0xe7,0x85,0x26,0x34,0xe4,0xbc,
0x50,0x83,0x47,0x95,0x36,0x9a,0xeb,0x85,0x4b,0x75,0xce,0x0b,
0x05,0x10,0x94,0x55,0x6c,0xb0,0xfe,0x8d,0x67,0x47,0x83,0xa8,
0x92,0x28,0xea,0x41,0x92,0xae,0x41,0xb2,0x98,0x3b,0x05,0x3f,
0x59,0xec,0xcb,0x78,0x8d,0x35,0xe1,0x94,0xbf,0xc5,0xeb,0x33,
0x51,0x10,0xfc,0x56,0x4c,0x81,0xf3,0xfb,0x6c,0xf1,0xe7,0x2d,
0xe2,0xf7,0xcb,0x81,0x69,0xe8,0x10,0xaf,0xb9,0x89,0x3f,0xa0,
0x9d,0xd9,0x26,0xd7,0x89,0x6b,0xca,0x43,0xc3,0x18,0x6d,0xd4,
0x33,0x4d,0xa9,0x0d,0xbe,0x1e,0xdf,0x6a,0xca,0x44,0xc3,0x18,
0x7a,0x28,0x13,0x1b,0x09,0x41,0x5d,0x32,0xd1,0x1c,0x8a,0x3c,
0xad,0xaa,0x1f,0x49,0x32,0x97,0x21,0xa6,0xbc,0xa4,0x04,0x82,
0xad,0x31,0xfe,0xa8,0xe7,0xbf,0x56,0x1c,0xba,0x6e,0xae,0x12,
0xd7,0x35,0x55,0x75,0x5a,0x84,0xea,0x39,0x8d,0x94,0x1d,0x5b,
0xd3,0xf9,0x5d,0xe4,0x7b,0xf4,0x72,0x07,0xf5,0xe4,0x12,0xea,
0xca,0x2b,0x90,0x84,0x8d,0xe9,0x6a,0xd4,0x73,0xab,0x91,0x74,
0x55,0xe6,0x5a,0x06,0x5e,0x6d,0xaa,0x06,0x95,0x9c,0xc8,0x82,
0xf8,0xd1,0x07,0x3b,0xa3,0x3d,0x04,0x33,0xe1,0xf1,0x68,0xdf,
0x82,0x36,0x38,0x16,0xed,0xdb,0xc4,0x79,0x87,0xd8,0x2f,0x84,
0x5d,0x39,0x82,0x47,0x53,0x70,0x1e,0x45,0x47,0x1d,0xf8,0x1d,
0x14,0x5d,0x73,0x12,0xb8,0xd1,0xbe,0x85,0x31,0xbc,0x30,0xda,
0xb7,0xb1,0x74,0x5b,0x11,0xed,0x3b,0xc4,0x67,0x0a,0x30,0x01,
0x6e,0x8b,0xf6,0x9b,0x82,0xf3,0x36,0x20,0xa7,0x25,0x1a,0x67,
0xb8,0x1f,0x8e,0x33,0xdc,0x0f,0xc7,0x19,0xee,0xdb,0x30,0x1f,
0x1e,0x89,0xf6,0x1d,0xe2,0x33,0xe1,0x38,0xc3,0xfd,0x70,0x9c,
0xab,0x61,0x00,0xff,0xad,0xc1,0xd4,0x0d,0x5f,0x5d,0x58,0xbb,
0xff,0x42,0xcf,0x9e,0xd3,0x47,0xcf,0x8c,0x0e,0x9f,0xe8,0xd9,
0x71,0xfa,0xe8,0xf1,0xe3,0x23,0xa7,0xef,0x1d,0x39,0x7c,0xf6,
0xd8,0xf0,0xe9,0x3d,0x7b,0xf6,0xf5,0xec,0x3a,0x7d,0xf2,0xf0,
0xe1,0x0b,0xdb,0x8e,0x8d,0x0c,0x9f,0xd8,0xb3,0x67,0xf5,0xc0,
0xc0,0x9a,0x55,0xf8,0xdf,0x5d,0x0b,0xfb,0xe1,0x02,0xf4,0xc0,
0x1e,0x38,0x0d,0x47,0xe1,0x0c,0x8c,0xc2,0x30,0x9c,0xc0,0xc7,
0x3b,0x82,0xe3,0xe3,0xf8,0x6f,0x04,0xef,0xdd,0x8b,0x5f,0x0f,
0xc3,0x59,0x3c,0xca,0x61,0x7c,0xb4,0x07,0xff,0xed,0xc3,0x9f,
0xd9,0x85,0xf7,0x4f,0xe2,0xf3,0x87,0xf1,0x15,0xb6,0xe1,0xf7,
0x46,0x82,0xef,0xfa,0xef,0xfa,0x78,0x8a,0x1f,0x5d,0x01,0x06,
0x99,0x07,0x42,0xc1,0x8b,0x85,0x6c,0xe4,0xa0,0x02,0x6a,0x42,
0x45,0xd4,0x8c,0x5a,0xd0,0x04,0x34,0x11,0xb5,0xa2,0x49,0xa8,
0x0d,0x4d,0x46,0xed,0x68,0x0a,0xea,0x40,0x9d,0xa8,0x0b,0x75,
0xa3,0xa9,0x68,0x1a,0x9a,0x8e,0x66,0xa0,0x99,0x68,0x16,0x9a,
0x8d,0xe6,0xa0,0xb9,0x68,0x1e,0xea,0x09,0xa6,0x75,0x01,0x63,
0xa3,0x08,0xcd,0xd0,0x82,0x71,0x3e,0x11,0x5a,0x31,0xb6,0xdb,
0x30,0xff,0xb7,0x63,0xca,0x74,0xe0,0x59,0xd0,0x85,0xe7,0xc6,
0x54,0x3c,0x17,0xa6,0xc3,0x0c,0x8c,0xb3,0x59,0x30,0x1b,0xe6,
0xc0,0x5c,0x3c,0x2f,0x7a,0xa0,0x17,0xe3,0x79,0x01,0xa6,0x47,
0x3f,0xa6,0xe6,0x22,0x58,0x0c,0x4b,0x60,0x29,0x9e,0x29,0xcb,
0x31,0x3d,0x57,0x06,0x18,0x75,0x31,0x7e,0x4b,0x18,0xaf,0x6b,
0x61,0x10,0xcf,0x9d,0x32,0x54,0x60,0x3d,0x9e,0x41,0x1b,0x61,
0x13,0xa6,0xec,0x66,0x18,0x82,0x2d,0xb0,0x15,0xc3,0x7c,0x3b,
0x6c,0x87,0x3b,0x30,0xae,0x76,0xc2,0xc7,0xe0,0x4e,0x3c,0xb3,
0xee,0x82,0xdf,0x84,0xbb,0xf1,0xfc,0xda,0x05,0xbf,0x85,0x71,
0xb6,0x1b,0x63,0xe2,0xe3,0xb0,0x17,0xee,0xc3,0xd8,0xfa,0x04,
0xfc,0x36,0x7c,0x12,0xee,0x87,0xdf,0x81,0x07,0xe0,0x41,0x78,
0x08,0x86,0x51,0x2f,0x9a,0x8f,0x16,0xa0,0x3e,0xd4,0x8f,0x16,
0xa2,0x45,0x68,0x31,0x5a,0x82,0x96,0xa2,0x65,0x68,0x39,0x5a,
0x81,0x56,0xa2,0x55,0x68,0x00,0xb9,0x68,0x35,0x2a,0xa1,0x35,
0x68,0x2d,0x1a,0x44,0xeb,0x50,0x19,0x55,0xd0,0x7a,0xb4,0x01,
0x6d,0x44,0x9b,0xd0,0x6d,0x68,0x33,0x1a,0x42,0x5b,0xe0,0x2a,
0xfc,0x19,0x3c,0x09,0x9f,0x87,0xef,0xc2,0x65,0x78,0x17,0x9e,
0x82,0x67,0xe0,0x4b,0xf0,0x27,0xf0,0x17,0xf0,0x0d,0xf8,0x05,
0x5c,0x84,0xb7,0xe1,0x09,0xf8,0x03,0xf8,0x1f,0xf8,0x5f,0xf8,
0x32,0xfc,0x21,0x3c,0x0d,0xdf,0x83,0x9f,0xc0,0x7f,0xc3,0x0b,
0xf0,0x12,0xfc,0x12,0x3e,0x80,0x5f,0xc1,0x8b,0xf0,0x97,0xf0,
0x4f,0xf0,0x8f,0x70,0x0d,0x73,0xc0,0x01,0xb8,0x04,0x07,0xe1,
0x5f,0x30,0x05,0x6f,0xc0,0x3f,0xc3,0xbf,0xc1,0x6b,0xf0,0xaf,
0xf0,0x7d,0xf8,0x19,0x1c,0x82,0x1f,0xc2,0xeb,0xf0,0xef,0xf0,
0x4d,0x4c,0xe3,0xf7,0xe1,0xab,0xf0,0x23,0x78,0x03,0xde,0x84,
0x23,0xf0,0x73,0xf8,0x2f,0xf8,0x22,0x7c,0x0a,0x73,0xc8,0xa7,
0x31,0x87,0x1c,0xc3,0x54,0xff,0x3a,0xe6,0x84,0xdf,0x85,0x53,
0x98,0x23,0xce,0x60,0x3e,0x19,0x85,0x73,0xf0,0x30,0xdc,0x84,
0xf3,0x98,0x97,0x2f,0xc0,0x67,0xe0,0xf7,0xe0,0xb3,0x70,0x1d,
0xfe,0x14,0x3e,0x07,0x8f,0x62,0x1d,0xfa,0x18,0xbc,0x07,0xb7,
0xe0,0xdb,0x6d,0x67,0x4f,0x1c,0x3d,0x70,0xf2,0xe0,0xc8,0x82,
0x81,0xf3,0x03,0x03,0x03,0x2e,0x7d,0xb8,0x9a,0x3e,0x2c,0xd1,
0x87,0x6b,0xe8,0xc3,0xb5,0xf4,0xe1,0x20,0x7d,0xb8,0x8e,0x3e,
0x2c,0xd3,0x87,0x15,0xfa,0x70,0x98,0x3e,0xdc,0x4f,0x1f,0x1e,
0xa0,0x0f,0x0f,0xd2,0x87,0x23,0xf4,0xe1,0x21,0xea,0xd0,0x1d,
0xa0,0x0f,0x69,0x78,0x5d,0x1a,0x5e,0x97,0x86,0xd7,0xa5,0xe1,
0x75,0x69,0x78,0x5d,0x1a,0x5e,0x97,0x86,0xd7,0xa5,0xe1,0x75,
0x69,0x78,0x5d,0x1a,0x5e,0x97,0x86,0xd7,0xa5,0xe1,0x75,0x69,
0x78,0x5d,0x1a,0x5e,0xf7,0x50,0xd3,0xc1,0x91,0x63,0x23,0xa3,
0x23,0xce,0xf6,0xb3,0xa7,0x4f,0x52,0x6f,0x95,0x69,0x60,0xcb,
0x34,0xb0,0x65,0x1a,0xd8,0x32,0x0d,0x6c,0x99,0x06,0xb6,0x4c,
0x03,0x5b,0xa6,0x81,0x2d,0xd3,0xc0,0x96,0x69,0x60,0xcb,0x34,
0xb0,0x65,0x1a,0xd8,0x32,0x0d,0x6c,0x99,0x06,0xb6,0x4c,0x03,
0x5b,0xa6,0x89,0x5b,0xa1,0x89,0x5b,0xa1,0xe1,0xad,0xd0,0xf0,
0x56,0x68,0x78,0x2b,0x34,0xbc,0x15,0x1a,0xde,0x0a,0x0d,0x6f,
0x85,0x86,0xb7,0x42,0xc3,0x5b,0xa1,0xe1,0xad,0xd0,0xf0,0x56,
0x68,0x78,0x2b,0x34,0xbc,0x15,0x1a,0xde,0x0a,0x0d,0x6f,0xe5,
0x10,0xc0,0xff,0x01,0x48,0xf9,0x62,0xf7
};
const unsigned int font_proggyClean_compressed_size=5936;

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2023 tildearrow and contributors
* Copyright (C) 2021-2024 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -22,7 +22,7 @@
#include "fonts.h"
const unsigned int* builtinFont[]={
const unsigned char* builtinFont[]={
font_plexSans_compressed_data,
font_liberationSans_compressed_data,
font_exo_compressed_data,
@ -38,7 +38,7 @@ const unsigned int builtinFontLen[]={
font_unifont_compressed_size
};
const unsigned int* builtinFontM[]={
const unsigned char* builtinFontM[]={
font_plexMono_compressed_data,
font_mononoki_compressed_data,
font_ptMono_compressed_data,

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2023 tildearrow and contributors
* Copyright (C) 2021-2024 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -23,31 +23,33 @@
#ifndef _FONTS_H
#define _FONTS_H
extern const unsigned int font_exo_compressed_size;
extern const unsigned int font_exo_compressed_data[];
extern const unsigned char font_exo_compressed_data[];
extern const unsigned int font_liberationSans_compressed_size;
extern const unsigned int font_liberationSans_compressed_data[];
extern const unsigned char font_liberationSans_compressed_data[];
extern const unsigned int font_mononoki_compressed_size;
extern const unsigned int font_mononoki_compressed_data[];
extern const unsigned char font_mononoki_compressed_data[];
extern const unsigned int font_plexSans_compressed_size;
extern const unsigned int font_plexSans_compressed_data[];
extern const unsigned char font_plexSans_compressed_data[];
extern const unsigned int font_plexMono_compressed_size;
extern const unsigned int font_plexMono_compressed_data[];
extern const unsigned char font_plexMono_compressed_data[];
extern const unsigned int font_proggyClean_compressed_size;
extern const unsigned int font_proggyClean_compressed_data[];
extern const unsigned char font_proggyClean_compressed_data[];
extern const unsigned int font_ptMono_compressed_size;
extern const unsigned int font_ptMono_compressed_data[];
extern const unsigned char font_ptMono_compressed_data[];
extern const unsigned int font_unifont_compressed_size;
extern const unsigned int font_unifont_compressed_data[];
extern const unsigned char font_unifont_compressed_data[];
extern const unsigned int iconFont_compressed_size;
extern const unsigned int iconFont_compressed_data[];
extern const unsigned char iconFont_compressed_data[];
extern const unsigned int furIcons_compressed_size;
extern const unsigned char furIcons_compressed_data[];
extern const unsigned int* builtinFont[];
extern const unsigned char* builtinFont[];
extern const unsigned int builtinFontLen[];
extern const unsigned int* builtinFontM[];
extern const unsigned char* builtinFontM[];
extern const unsigned int builtinFontMLen[];
#endif
// "(Glimmer of hope: the atlas system will be rewritten in the future to make scaling more flexible.)""
// not just that. somebody rewrite it already so I can load these glyphs at run-time and support
// all languages at once, instead of having to load Unifont's 65k+ characters and blow the GPU up in the
// process!
// process!

125
src/gui/fontzlib.cpp Normal file
View file

@ -0,0 +1,125 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2024 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "gui.h"
#include <zlib.h>
#define FONT_READ_SIZE 262144
struct InflateBlock {
unsigned char* buf;
size_t len;
size_t blockSize;
InflateBlock(size_t s) {
buf=new unsigned char[s];
len=s;
blockSize=0;
}
~InflateBlock() {
delete[] buf;
len=0;
}
};
ImFont* FurnaceGUI::addFontZlib(const void* data, size_t len, float size_pixels, const ImFontConfig* font_cfg, const ImWchar* glyph_ranges) {
z_stream zl;
memset(&zl,0,sizeof(z_stream));
logV("addFontZlib...");
zl.avail_in=len;
zl.next_in=(Bytef*)data;
zl.zalloc=NULL;
zl.zfree=NULL;
zl.opaque=NULL;
int nextErr;
nextErr=inflateInit(&zl);
if (nextErr!=Z_OK) {
if (zl.msg==NULL) {
logD("zlib error: unknown! %d",nextErr);
} else {
logD("zlib error: %s",zl.msg);
}
inflateEnd(&zl);
return NULL;
}
std::vector<InflateBlock*> blocks;
while (true) {
InflateBlock* ib=new InflateBlock(FONT_READ_SIZE);
zl.next_out=ib->buf;
zl.avail_out=ib->len;
nextErr=inflate(&zl,Z_SYNC_FLUSH);
if (nextErr!=Z_OK && nextErr!=Z_STREAM_END) {
if (zl.msg==NULL) {
logD("zlib error: unknown error! %d",nextErr);
} else {
logD("zlib inflate: %s",zl.msg);
}
for (InflateBlock* i: blocks) delete i;
blocks.clear();
delete ib;
inflateEnd(&zl);
return NULL;
}
ib->blockSize=ib->len-zl.avail_out;
blocks.push_back(ib);
if (nextErr==Z_STREAM_END) {
break;
}
}
nextErr=inflateEnd(&zl);
if (nextErr!=Z_OK) {
if (zl.msg==NULL) {
logD("zlib end error: unknown error! %d",nextErr);
} else {
logD("zlib end: %s",zl.msg);
}
for (InflateBlock* i: blocks) delete i;
blocks.clear();
return NULL;
}
size_t finalSize=0;
size_t curSeek=0;
for (InflateBlock* i: blocks) {
finalSize+=i->blockSize;
}
if (finalSize<1) {
logD("compressed too small!");
lastError="file too small";
for (InflateBlock* i: blocks) delete i;
blocks.clear();
return NULL;
}
unsigned char* finalData=new unsigned char[finalSize];
for (InflateBlock* i: blocks) {
memcpy(&finalData[curSeek],i->buf,i->blockSize);
curSeek+=i->blockSize;
delete i;
}
blocks.clear();
len=finalSize;
ImFontConfig fontConfig=(font_cfg==NULL)?ImFontConfig():(*font_cfg);
fontConfig.FontDataOwnedByAtlas=true;
return ImGui::GetIO().Fonts->AddFontFromMemoryTTF(finalData,finalSize,size_pixels,&fontConfig,glyph_ranges);
}

302
src/gui/gif_load.h Normal file
View file

@ -0,0 +1,302 @@
#ifndef GIF_LOAD_H
#define GIF_LOAD_H
/** gif_load: A slim, fast and header-only GIF loader written in C.
Original author: hidefromkgb (hidefromkgb@gmail.com)
_________________________________________________________________________
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
_________________________________________________________________________
**/
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h> /** imports uint8_t, uint16_t and uint32_t **/
#ifndef GIF_MGET
#include <stdlib.h>
#define GIF_MGET(m,s,a,c) m = (uint8_t*)realloc((c)? 0 : m, (c)? s : 1UL);
#endif
#ifndef GIF_BIGE
#define GIF_BIGE 0
#endif
#ifndef GIF_EXTR
#define GIF_EXTR static
#endif
#define _GIF_SWAP(h) ((GIF_BIGE)? ((uint16_t)(h << 8) | (h >> 8)) : h)
#pragma pack(push, 1)
struct GIF_WHDR { /** ======== frame writer info: ======== **/
long xdim, ydim, clrs, /** global dimensions, palette size **/
bkgd, tran, /** background index, transparent index **/
intr, mode, /** interlace flag, frame blending mode **/
frxd, fryd, frxo, fryo, /** current frame dimensions and offset **/
time, ifrm, nfrm; /** delay, frame number, frame count **/
uint8_t *bptr; /** frame pixel indices or metadata **/
struct { /** [==== GIF RGB palette element: ====] **/
uint8_t R, G, B; /** [color values - red, green, blue ] **/
} *cpal; /** current palette **/
};
#pragma pack(pop)
enum {GIF_NONE = 0, GIF_CURR = 1, GIF_BKGD = 2, GIF_PREV = 3};
/** [ internal function, do not use ] **/
static long _GIF_SkipChunk(uint8_t **buff, long size) {
long skip;
for (skip = 2, ++size, ++(*buff); ((size -= skip) > 0) && (skip > 1);
*buff += (skip = 1 + **buff));
return size;
}
/** [ internal function, do not use ] **/
static long _GIF_LoadHeader(unsigned gflg, uint8_t **buff, void **rpal,
unsigned fflg, long *size, long flen) {
if (flen && (!(*buff += flen) || ((*size -= flen) <= 0)))
return -2; /** v--[ 0x80: "palette is present" flag ]--, **/
if (flen && (fflg & 0x80)) { /** local palette has priority | **/
*rpal = *buff; /** [ 3L: 3 uint8_t color channels ]--, | **/
*buff += (flen = 2 << (fflg & 7)) * 3L; /** <--| | **/
return ((*size -= flen * 3L) > 0)? flen : -1; /** <--' | **/
} /** no local palette found, checking for the global one | **/
return (gflg & 0x80)? (2 << (gflg & 7)) : 0; /** <-----' **/
}
/** [ internal function, do not use ] **/
static long _GIF_LoadFrame(uint8_t **buff, long *size,
uint8_t *bptr, uint8_t *blen) {
typedef uint16_t GIF_H;
const long GIF_HLEN = sizeof(GIF_H), /** to rid the scope of sizeof **/
GIF_CLEN = 1 << 12; /** code table length: 4096 items **/
GIF_H accu, mask; /** bit accumulator / bit mask **/
long ctbl, iter, /** last code table index / index string iterator **/
prev, curr, /** codes from the stream: previous / current **/
ctsz, ccsz, /** code table bit sizes: min LZW / current **/
bseq, bszc; /** counters: block sequence / bit size **/
uint32_t *code = (uint32_t*)bptr - GIF_CLEN; /** code table pointer **/
/** preparing initial values **/
if ((--(*size) <= GIF_HLEN) || !*++(*buff))
return -4; /** unexpected end of the stream: insufficient size **/
mask = (GIF_H)((1 << (ccsz = (ctsz = *(*buff - 1)) + 1)) - 1);
if ((ctsz < 2) || (ctsz > 8))
return -3; /** min LZW size is out of its nominal [2; 8] bounds **/
if ((ctbl = (1L << ctsz)) != (mask & _GIF_SWAP(*(GIF_H*)(*buff + 1))))
return -2; /** initial code is not equal to min LZW size **/
for (curr = ++ctbl; curr; code[--curr] = 0); /** actual color codes **/
/** getting codes from stream (--size makes up for end-of-stream mark) **/
for (--(*size), bszc = -ccsz, prev = curr = 0;
((*size -= (bseq = *(*buff)++) + 1) >= 0) && bseq; *buff += bseq)
for (; bseq > 0; bseq -= GIF_HLEN, *buff += GIF_HLEN)
for (accu = (GIF_H)(_GIF_SWAP(*(GIF_H*)*buff)
& ((bseq < GIF_HLEN)? ((1U << (8 * bseq)) - 1U) : ~0U)),
curr |= accu << (ccsz + bszc), accu = (GIF_H)(accu >> -bszc),
bszc += 8 * ((bseq < GIF_HLEN)? bseq : GIF_HLEN);
bszc >= 0; bszc -= ccsz, prev = curr, curr = accu,
accu = (GIF_H)(accu >> ccsz))
if (((curr &= mask) & ~1L) == (1L << ctsz)) {
if (~(ctbl = curr + 1) & 1) /** end-of-data code (ED). **/
/** -1: no end-of-stream mark after ED; 1: decoded **/
return (*((*buff += bseq + 1) - 1))? -1 : 1;
mask = (GIF_H)((1 << (ccsz = ctsz + 1)) - 1);
} /** ^- table drop code (TD). TD = 1 << ctsz, ED = TD + 1 **/
else { /** single-pixel (SP) or multi-pixel (MP) code. **/
if (ctbl < GIF_CLEN) { /** is the code table full? **/
if ((ctbl == mask) && (ctbl < GIF_CLEN - 1)) {
mask = (GIF_H)(mask + mask + 1);
ccsz++; /** yes; extending **/
} /** prev = TD? => curr < ctbl = prev **/
code[ctbl] = (uint32_t)prev + (code[prev] & 0xFFF000);
} /** appending SP / MP decoded pixels to the frame **/
prev = (long)code[iter = (ctbl > curr)? curr : prev];
if ((bptr += (prev = (prev >> 12) & 0xFFF)) > blen)
continue; /** skipping pixels above frame capacity **/
for (prev++; (iter &= 0xFFF) >> ctsz;
*bptr-- = (uint8_t)((iter = (long)code[iter]) >> 24));
(bptr += prev)[-prev] = (uint8_t)iter;
if (ctbl < GIF_CLEN) { /** appending the code table **/
if (ctbl == curr)
*bptr++ = (uint8_t)iter;
else if (ctbl < curr)
return -5; /** wrong code in the stream **/
code[ctbl++] += ((uint32_t)iter << 24) + 0x1000;
}
} /** 0: no ED before end-of-stream mark; -4: see above **/
return (++(*size) >= 0)? 0 : -4; /** ^- N.B.: 0 error is recoverable **/
}
/** _________________________________________________________________________
The main loading function. Returns the total number of frames if the data
includes proper GIF ending, and otherwise it returns the number of frames
loaded per current call, multiplied by -1. So, the data may be incomplete
and in this case the function can be called again when more data arrives,
just remember to keep SKIP up to date.
_________________________________________________________________________
DATA: raw data chunk, may be partial
SIZE: size of the data chunk that`s currently present
GWFR: frame writer function, MANDATORY
EAMF: metadata reader function, set to 0 if not needed
ANIM: implementation-specific data (e.g. a structure or a pointer to it)
SKIP: number of frames to skip before resuming
**/
GIF_EXTR long GIF_Load(void *data, long size,
void (*gwfr)(void*, struct GIF_WHDR*),
void (*eamf)(void*, struct GIF_WHDR*),
void *anim, long skip) {
const long GIF_BLEN = (1 << 12) * sizeof(uint32_t);
const uint8_t GIF_EHDM = 0x21, /** extension header mark **/
GIF_FHDM = 0x2C, /** frame header mark **/
GIF_EOFM = 0x3B, /** end-of-file mark **/
GIF_EGCM = 0xF9, /** extension: graphics control mark **/
GIF_EAMM = 0xFF; /** extension: app metadata mark **/
#pragma pack(push, 1)
struct GIF_GHDR { /** ========== GLOBAL GIF HEADER: ========== **/
uint8_t head[6]; /** 'GIF87a' / 'GIF89a' header signature **/
uint16_t xdim, ydim; /** total image width, total image height **/
uint8_t flgs; /** FLAGS:
GlobalPlt bit 7 1: global palette exists
0: local in each frame
ClrRes bit 6-4 bits/channel = ClrRes+1
[reserved] bit 3 0
PixelBits bit 2-0 |Plt| = 2 * 2^PixelBits
**/
uint8_t bkgd, aspr; /** background color index, aspect ratio **/
} *ghdr = (struct GIF_GHDR*)data;
struct GIF_FHDR { /** ======= GIF FRAME MASTER HEADER: ======= **/
uint16_t frxo, fryo; /** offset of this frame in a "full" image **/
uint16_t frxd, fryd; /** frame width, frame height **/
uint8_t flgs; /** FLAGS:
LocalPlt bit 7 1: local palette exists
0: global is used
Interlaced bit 6 1: interlaced frame
0: non-interlaced frame
Sorted bit 5 usually 0
[reserved] bit 4-3 [undefined]
PixelBits bit 2-0 |Plt| = 2 * 2^PixelBits
**/
} *fhdr;
struct GIF_EGCH { /** ==== [EXT] GRAPHICS CONTROL HEADER: ==== **/
uint8_t flgs; /** FLAGS:
[reserved] bit 7-5 [undefined]
BlendMode bit 4-2 000: not set; static GIF
001: leave result as is
010: restore background
011: restore previous
1--: [undefined]
UserInput bit 1 1: show frame till input
0: default; ~99% of GIFs
TransColor bit 0 1: got transparent color
0: frame is fully opaque
**/
uint16_t time; /** delay in GIF time units; 1 unit = 10 ms **/
uint8_t tran; /** transparent color index **/
} *egch = 0;
#pragma pack(pop)
struct GIF_WHDR wtmp, whdr;
long desc, blen;
uint8_t *buff;
memset(&wtmp,0,sizeof(struct GIF_WHDR));
memset(&whdr,0,sizeof(struct GIF_WHDR));
/** checking if the stream is not empty and has a 'GIF8[79]a' signature,
the data has sufficient size and frameskip value is non-negative **/
if (!ghdr || (size <= (long)sizeof(*ghdr)) || (*(buff = ghdr->head) != 71)
|| (buff[1] != 73) || (buff[2] != 70) || (buff[3] != 56) || (skip < 0)
|| ((buff[4] != 55) && (buff[4] != 57)) || (buff[5] != 97) || !gwfr)
return 0;
buff = (uint8_t*)(ghdr + 1) /** skipping the global header and palette **/
+ _GIF_LoadHeader(ghdr->flgs, 0, 0, 0, 0, 0L) * 3L;
if ((size -= buff - (uint8_t*)ghdr) <= 0)
return 0;
whdr.xdim = _GIF_SWAP(ghdr->xdim);
whdr.ydim = _GIF_SWAP(ghdr->ydim);
for (whdr.bptr = buff, whdr.bkgd = ghdr->bkgd, blen = --size;
(blen >= 0) && ((desc = *whdr.bptr++) != GIF_EOFM); /** sic: '>= 0' **/
blen = _GIF_SkipChunk(&whdr.bptr, blen) - 1) /** count all frames **/
if (desc == GIF_FHDM) {
fhdr = (struct GIF_FHDR*)whdr.bptr;
if (_GIF_LoadHeader(ghdr->flgs, &whdr.bptr, (void**)&whdr.cpal,
fhdr->flgs, &blen, sizeof(*fhdr)) <= 0)
break;
whdr.frxd = _GIF_SWAP(fhdr->frxd);
whdr.fryd = _GIF_SWAP(fhdr->fryd);
whdr.frxo = (whdr.frxd > whdr.frxo)? whdr.frxd : whdr.frxo;
whdr.fryo = (whdr.fryd > whdr.fryo)? whdr.fryd : whdr.fryo;
whdr.ifrm++;
}
blen = whdr.frxo * whdr.fryo * (long)sizeof(*whdr.bptr);
GIF_MGET(whdr.bptr, (unsigned long)(blen + GIF_BLEN + 2), anim, 1)
whdr.nfrm = (desc != GIF_EOFM)? -whdr.ifrm : whdr.ifrm;
for (whdr.bptr += GIF_BLEN, whdr.ifrm = -1; blen /** load all frames **/
&& (skip < ((whdr.nfrm < 0)? -whdr.nfrm : whdr.nfrm)) && (size >= 0);
size = (desc != GIF_EOFM)? ((desc != GIF_FHDM) || (skip > whdr.ifrm))?
_GIF_SkipChunk(&buff, size) - 1 : size - 1 : -1)
if ((desc = *buff++) == GIF_FHDM) { /** found a frame **/
whdr.intr = !!((fhdr = (struct GIF_FHDR*)buff)->flgs & 0x40);
*(void**)&whdr.cpal = (void*)(ghdr + 1); /** interlaced? -^ **/
whdr.clrs = _GIF_LoadHeader(ghdr->flgs, &buff, (void**)&whdr.cpal,
fhdr->flgs, &size, sizeof(*fhdr));
if ((skip <= ++whdr.ifrm) && ((whdr.clrs <= 0)
|| (_GIF_LoadFrame(&buff, &size,
whdr.bptr, whdr.bptr + blen) < 0)))
size = -(whdr.ifrm--) - 1; /** failed to load the frame **/
else if (skip <= whdr.ifrm) {
whdr.frxd = _GIF_SWAP(fhdr->frxd);
whdr.fryd = _GIF_SWAP(fhdr->fryd);
whdr.frxo = _GIF_SWAP(fhdr->frxo);
whdr.fryo = _GIF_SWAP(fhdr->fryo);
whdr.time = (egch)? _GIF_SWAP(egch->time) : 0;
whdr.tran = (egch && (egch->flgs & 0x01))? egch->tran : -1;
whdr.time = (egch && (egch->flgs & 0x02))? -whdr.time - 1
: whdr.time;
whdr.mode = (egch && !(egch->flgs & 0x10))?
(egch->flgs & 0x0C) >> 2 : GIF_NONE;
egch = 0;
wtmp = whdr;
gwfr(anim, &wtmp); /** passing the frame to the caller **/
}
}
else if (desc == GIF_EHDM) { /** found an extension **/
if (*buff == GIF_EGCM) /** graphics control ext. **/
egch = (struct GIF_EGCH*)(buff + 1 + 1);
else if ((*buff == GIF_EAMM) && eamf) { /** app metadata ext. **/
wtmp = whdr;
wtmp.bptr = buff + 1 + 1; /** just passing the raw chunk **/
eamf(anim, &wtmp);
}
}
whdr.bptr -= GIF_BLEN; /** for excess pixel codes ----v (here & above) **/
GIF_MGET(whdr.bptr, (unsigned long)(blen + GIF_BLEN + 2), anim, 0)
return (whdr.nfrm < 0)? (skip - whdr.ifrm - 1) : (whdr.ifrm + 1);
}
#undef _GIF_SWAP
#ifdef __cplusplus
}
#endif
#endif /** GIF_LOAD_H **/

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2023 tildearrow and contributors
* Copyright (C) 2021-2024 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2023 tildearrow and contributors
* Copyright (C) 2021-2024 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -47,13 +47,14 @@ void FurnaceGUI::drawGrooves() {
ImGui::TableNextColumn();
ImGui::Text("pattern");
ImGui::TableNextColumn();
ImGui::Text("remove");
// ImGui::Text("remove"); removed because the text clips from the fixed width
int index=0;
for (DivGroovePattern& i: e->song.grooves) {
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::PushFont(patFont);
ImGui::AlignTextToFramePadding();
ImGui::Text("%.2X",index);
ImGui::PopFont();
@ -79,6 +80,7 @@ void FurnaceGUI::drawGrooves() {
ImGui::SetKeyboardFocusHere();
}
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
ImGui::AlignTextToFramePadding();
if (ImGui::InputText(grooveStr.c_str(),&grooveListString)) {
decodeMMLStr(grooveListString,intVersion,intVersionLen,ignoredLoop,1,255,ignoredRel);
if (intVersionLen<1) {
@ -120,10 +122,15 @@ void FurnaceGUI::drawGrooves() {
}
ImGui::TableNextColumn();
pushDestColor();
String grooveID=fmt::sprintf(ICON_FA_TIMES "##GRR%d",index);
if (ImGui::Button(grooveID.c_str())) {
delGroove=index;
}
popDestColor();
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("remove");
}
index++;
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2023 tildearrow and contributors
* Copyright (C) 2021-2024 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -21,6 +21,8 @@
#include "gui.h"
#include "guiConst.h"
#include "../engine/song.h"
#include "IconsFontAwesome4.h"
#include "furIcons.h"
const int opOrder[4]={
0, 2, 1, 3
@ -62,6 +64,42 @@ const char* noteNamesG[180]={
"C-9", "C#9", "D-9", "D#9", "E-9", "F-9", "F#9", "G-9", "G#9", "A-9", "A#9", "H-9"
};
const char* noteNamesF[180]={
"c_5", "dd5", "d_5", "ed5", "e_5", "f_5", "gd5", "g_5", "ad5", "a_5", "bd5", "b_5",
"c_4", "dd4", "d_4", "ed4", "e_4", "f_4", "gd4", "g_4", "ad4", "a_4", "bd4", "b_4",
"c_3", "dd3", "d_3", "ed3", "e_3", "f_3", "gd3", "g_3", "ad3", "a_3", "bd3", "b_3",
"c_2", "dd2", "d_2", "ed2", "e_2", "f_2", "gd2", "g_2", "ad2", "a_2", "bd2", "b_2",
"c_1", "dd1", "d_1", "ed1", "e_1", "f_1", "gd1", "g_1", "ad1", "a_1", "bd1", "b_1",
"C-0", "Db0", "D-0", "Eb0", "E-0", "F-0", "Gb0", "G-0", "Ab0", "A-0", "Bb0", "B-0",
"C-1", "Db1", "D-1", "Eb1", "E-1", "F-1", "Gb1", "G-1", "Ab1", "A-1", "Bb1", "B-1",
"C-2", "Db2", "D-2", "Eb2", "E-2", "F-2", "Gb2", "G-2", "Ab2", "A-2", "Bb2", "B-2",
"C-3", "Db3", "D-3", "Eb3", "E-3", "F-3", "Gb3", "G-3", "Ab3", "A-3", "Bb3", "B-3",
"C-4", "Db4", "D-4", "Eb4", "E-4", "F-4", "Gb4", "G-4", "Ab4", "A-4", "Bb4", "B-4",
"C-5", "Db5", "D-5", "Eb5", "E-5", "F-5", "Gb5", "G-5", "Ab5", "A-5", "Bb5", "B-5",
"C-6", "Db6", "D-6", "Eb6", "E-6", "F-6", "Gb6", "G-6", "Ab6", "A-6", "Bb6", "B-6",
"C-7", "Db7", "D-7", "Eb7", "E-7", "F-7", "Gb7", "G-7", "Ab7", "A-7", "Bb7", "B-7",
"C-8", "Db8", "D-8", "Eb8", "E-8", "F-8", "Gb8", "G-8", "Ab8", "A-8", "Bb8", "B-8",
"C-9", "Db9", "D-9", "Eb9", "E-9", "F-9", "Gb9", "G-9", "Ab9", "A-9", "Bb9", "B-9"
};
const char* noteNamesGF[180]={
"c_5", "dd5", "d_5", "ed5", "e_5", "f_5", "gd5", "g_5", "ad5", "a_5", "b_5", "h_5",
"c_4", "dd4", "d_4", "ed4", "e_4", "f_4", "gd4", "g_4", "ad4", "a_4", "b_4", "h_4",
"c_3", "dd3", "d_3", "ed3", "e_3", "f_3", "gd3", "g_3", "ad3", "a_3", "b_3", "h_3",
"c_2", "dd2", "d_2", "ed2", "e_2", "f_2", "gd2", "g_2", "ad2", "a_2", "b_2", "h_2",
"c_1", "dd1", "d_1", "ed1", "e_1", "f_1", "gd1", "g_1", "ad1", "a_1", "b_1", "h_1",
"C-0", "Db0", "D-0", "Eb0", "E-0", "F-0", "Gb0", "G-0", "Ab0", "A-0", "B-0", "H-0",
"C-1", "Db1", "D-1", "Eb1", "E-1", "F-1", "Gb1", "G-1", "Ab1", "A-1", "B-1", "H-1",
"C-2", "Db2", "D-2", "Eb2", "E-2", "F-2", "Gb2", "G-2", "Ab2", "A-2", "B-2", "H-2",
"C-3", "Db3", "D-3", "Eb3", "E-3", "F-3", "Gb3", "G-3", "Ab3", "A-3", "B-3", "H-3",
"C-4", "Db4", "D-4", "Eb4", "E-4", "F-4", "Gb4", "G-4", "Ab4", "A-4", "B-4", "H-4",
"C-5", "Db5", "D-5", "Eb5", "E-5", "F-5", "Gb5", "G-5", "Ab5", "A-5", "B-5", "H-5",
"C-6", "Db6", "D-6", "Eb6", "E-6", "F-6", "Gb6", "G-6", "Ab6", "A-6", "B-6", "H-6",
"C-7", "Db7", "D-7", "Eb7", "E-7", "F-7", "Gb7", "G-7", "Ab7", "A-7", "B-7", "H-7",
"C-8", "Db8", "D-8", "Eb8", "E-8", "F-8", "Gb8", "G-8", "Ab8", "A-8", "B-8", "H-8",
"C-9", "Db9", "D-9", "Eb9", "E-9", "F-9", "Gb9", "G-9", "Ab9", "A-9", "B-9", "H-9"
};
const char* pitchLabel[11]={
"1/6", "1/5", "1/4", "1/3", "1/2", "1x", "2x", "3x", "4x", "5x", "6x"
};
@ -80,58 +118,71 @@ const int vgmVersions[7]={
0x172
};
const char* insTypes[DIV_INS_MAX+1]={
"SN76489/Sega PSG",
"FM (OPN)",
"Game Boy",
"C64",
"Generic Sample",
"PC Engine",
"AY-3-8910/SSG",
"AY8930",
"TIA",
"SAA1099",
"VIC",
"PET",
"VRC6",
"FM (OPLL)",
"FM (OPL)",
"FDS",
"Virtual Boy",
"Namco 163",
"Konami SCC/Bubble System WSG",
"FM (OPZ)",
"POKEY",
"Beeper",
"WonderSwan",
"Atari Lynx",
"VERA",
"X1-010",
"VRC6 (saw)",
"ES5506",
"MultiPCM",
"SNES",
"Sound Unit",
"Namco WSG",
"OPL (drums)",
"FM (OPM)",
"NES",
"MSM6258",
"MSM6295",
"ADPCM-A",
"ADPCM-B",
"SegaPCM",
"QSound",
"YMZ280B",
"RF5C68",
"MSM5232",
"T6W28",
"K007232",
"GA20",
"Pokémon Mini/QuadTone",
"SM8521",
"PV-1000",
NULL
// name, icon, letter icon
const char* insTypes[DIV_INS_MAX+1][3]={
{"SN76489/Sega PSG",ICON_FA_BAR_CHART,ICON_FUR_INS_STD},
{"FM (OPN)",ICON_FA_AREA_CHART,ICON_FUR_INS_FM},
{"Game Boy",ICON_FA_GAMEPAD,ICON_FUR_INS_GB},
{"C64",ICON_FA_KEYBOARD_O,ICON_FUR_INS_C64},
{"Generic Sample",ICON_FA_VOLUME_UP,ICON_FUR_INS_AMIGA},
{"PC Engine",ICON_FA_ID_BADGE,ICON_FUR_INS_PCE},
{"AY-3-8910/SSG",ICON_FA_BAR_CHART,ICON_FUR_INS_AY},
{"AY8930",ICON_FA_BAR_CHART,ICON_FUR_INS_AY8930},
{"TIA",ICON_FA_BAR_CHART,ICON_FUR_INS_TIA},
{"SAA1099",ICON_FA_BAR_CHART,ICON_FUR_INS_SAA1099},
{"VIC",ICON_FA_BAR_CHART,ICON_FUR_INS_VIC},
{"PET",ICON_FA_SQUARE,ICON_FUR_INS_PET},
{"VRC6",ICON_FA_BAR_CHART,ICON_FUR_INS_VRC6},
{"FM (OPLL)",ICON_FA_AREA_CHART,ICON_FUR_INS_OPLL},
{"FM (OPL)",ICON_FA_AREA_CHART,ICON_FUR_INS_OPL},
{"FDS",ICON_FA_FLOPPY_O,ICON_FUR_INS_FDS},
{"Virtual Boy",ICON_FA_BINOCULARS,ICON_FUR_INS_VBOY},
{"Namco 163",ICON_FA_CALCULATOR,ICON_FUR_INS_N163},
{"Konami SCC/Bubble System WSG",ICON_FA_CALCULATOR,ICON_FUR_INS_SCC},
{"FM (OPZ)",ICON_FA_AREA_CHART,ICON_FUR_INS_OPZ},
{"POKEY",ICON_FA_BAR_CHART,ICON_FUR_INS_POKEY},
{"Beeper",ICON_FA_SQUARE,ICON_FUR_INS_BEEPER},
{"WonderSwan",ICON_FA_GAMEPAD,ICON_FUR_INS_SWAN},
{"Atari Lynx",ICON_FA_BAR_CHART,ICON_FUR_INS_MIKEY},
{"VERA",ICON_FA_KEYBOARD_O,ICON_FUR_INS_VERA},
{"X1-010",ICON_FA_BAR_CHART,ICON_FUR_INS_X1_010},
{"VRC6 (saw)",ICON_FA_BAR_CHART,ICON_FUR_INS_VRC6_SAW},
{"ES5506",ICON_FA_VOLUME_UP,ICON_FUR_INS_ES5506},
{"MultiPCM",ICON_FA_VOLUME_UP,ICON_FUR_INS_MULTIPCM},
{"SNES",ICON_FA_VOLUME_UP,ICON_FUR_INS_SNES},
{"Sound Unit",ICON_FA_MICROCHIP,ICON_FUR_INS_SU},
{"Namco WSG",ICON_FA_PIE_CHART,ICON_FUR_INS_NAMCO},
{"OPL (drums)",ICON_FA_COFFEE,ICON_FUR_INS_OPL_DRUMS},
{"FM (OPM)",ICON_FA_AREA_CHART,ICON_FUR_INS_OPM},
{"NES",ICON_FA_GAMEPAD,ICON_FUR_INS_NES},
{"MSM6258",ICON_FA_VOLUME_UP,ICON_FUR_INS_MSM6258},
{"MSM6295",ICON_FA_VOLUME_UP,ICON_FUR_INS_MSM6295},
{"ADPCM-A",ICON_FA_VOLUME_UP,ICON_FUR_INS_ADPCMA},
{"ADPCM-B",ICON_FA_VOLUME_UP,ICON_FUR_INS_ADPCMB},
{"SegaPCM",ICON_FA_VOLUME_UP,ICON_FUR_INS_SEGAPCM},
{"QSound",ICON_FA_VOLUME_UP,ICON_FUR_INS_QSOUND},
{"YMZ280B",ICON_FA_VOLUME_UP,ICON_FUR_INS_YMZ280B},
{"RF5C68",ICON_FA_VOLUME_UP,ICON_FUR_INS_RF5C68},
{"MSM5232",ICON_FA_BAR_CHART,ICON_FUR_INS_MSM5232},
{"T6W28",ICON_FA_BAR_CHART,ICON_FUR_INS_T6W28},
{"K007232",ICON_FA_BAR_CHART,ICON_FUR_INS_K007232},
{"GA20",ICON_FA_BAR_CHART,ICON_FUR_INS_GA20},
{"Pokémon Mini/QuadTone",ICON_FA_BAR_CHART,ICON_FUR_INS_POKEMINI},
{"SM8521",ICON_FA_GAMEPAD,ICON_FUR_INS_SM8521},
{"PV-1000",ICON_FA_GAMEPAD,ICON_FUR_INS_PV1000},
{"K053260",ICON_FA_BAR_CHART,ICON_FUR_INS_K053260},
{"SCSP",ICON_FA_QUESTION,ICON_FUR_INS_SCSP},
{"TED",ICON_FA_BAR_CHART,ICON_FUR_INS_TED},
{"C140",ICON_FA_VOLUME_UP,ICON_FUR_INS_C140},
{"C219",ICON_FA_VOLUME_UP,ICON_FUR_INS_C219},
{"FM (ESFM)",ICON_FA_AREA_CHART,ICON_FUR_INS_ESFM},
{"PowerNoise (noise)",ICON_FUR_NOISE,ICON_FUR_INS_POWERNOISE},
{"PowerNoise (slope)",ICON_FUR_SAW,ICON_FUR_INS_POWERNOISE_SAW},
{"Dave",ICON_FA_BAR_CHART,ICON_FUR_INS_DAVE},
{"Nintendo DS",ICON_FA_BAR_CHART,ICON_FUR_INS_NDS},
{"GBA DMA",ICON_FA_GAMEPAD,ICON_FUR_INS_GBA_DMA},
{"GBA MinMod",ICON_FA_VOLUME_UP,ICON_FUR_INS_GBA_MINMOD},
{NULL,ICON_FA_QUESTION,ICON_FA_QUESTION}
};
const char* sampleLoopModes[DIV_SAMPLE_LOOP_MAX]={
@ -148,13 +199,13 @@ const char* sampleDepths[DIV_SAMPLE_DEPTH_MAX]={
"QSound ADPCM",
"ADPCM-A",
"ADPCM-B",
NULL,
"K05 ADPCM",
"8-bit PCM",
"BRR",
"VOX",
NULL,
NULL,
NULL,
"8-bit µ-law PCM",
"C219 PCM",
"IMA ADPCM",
NULL,
NULL,
"16-bit PCM"
@ -169,6 +220,19 @@ const char* resampleStrats[]={
"best possible"
};
const char* fxColorsNames[]={
"Invalid",
"Pitch",
"Volume",
"Panning",
"Song",
"Time",
"Speed",
"System (Primary)",
"System (Secondary)",
"Miscellaneous"
};
const FurnaceGUIColors fxColors[256]={
GUI_COLOR_PATTERN_EFFECT_MISC, // 00
GUI_COLOR_PATTERN_EFFECT_PITCH, // 01
@ -428,10 +492,10 @@ const FurnaceGUIColors fxColors[256]={
GUI_COLOR_PATTERN_EFFECT_MISC, // E3
GUI_COLOR_PATTERN_EFFECT_MISC, // E4
GUI_COLOR_PATTERN_EFFECT_PITCH, // E5
GUI_COLOR_PATTERN_EFFECT_INVALID, // E6
GUI_COLOR_PATTERN_EFFECT_INVALID, // E7
GUI_COLOR_PATTERN_EFFECT_INVALID, // E8
GUI_COLOR_PATTERN_EFFECT_INVALID, // E9
GUI_COLOR_PATTERN_EFFECT_MISC, // E6
GUI_COLOR_PATTERN_EFFECT_TIME, // E7
GUI_COLOR_PATTERN_EFFECT_MISC, // E8
GUI_COLOR_PATTERN_EFFECT_MISC, // E9
GUI_COLOR_PATTERN_EFFECT_MISC, // EA
GUI_COLOR_PATTERN_EFFECT_MISC, // EB
GUI_COLOR_PATTERN_EFFECT_TIME, // EC
@ -445,12 +509,12 @@ const FurnaceGUIColors fxColors[256]={
GUI_COLOR_PATTERN_EFFECT_VOLUME, // F4
GUI_COLOR_PATTERN_EFFECT_MISC, // F5
GUI_COLOR_PATTERN_EFFECT_MISC, // F6
GUI_COLOR_PATTERN_EFFECT_INVALID, // F7
GUI_COLOR_PATTERN_EFFECT_MISC, // F7
GUI_COLOR_PATTERN_EFFECT_VOLUME, // F8
GUI_COLOR_PATTERN_EFFECT_VOLUME, // F9
GUI_COLOR_PATTERN_EFFECT_VOLUME, // FA
GUI_COLOR_PATTERN_EFFECT_INVALID, // FB
GUI_COLOR_PATTERN_EFFECT_INVALID, // FC
GUI_COLOR_PATTERN_EFFECT_TIME, // FC
GUI_COLOR_PATTERN_EFFECT_INVALID, // FD
GUI_COLOR_PATTERN_EFFECT_INVALID, // FE
GUI_COLOR_PATTERN_EFFECT_SONG // FF
@ -467,8 +531,14 @@ const FurnaceGUIActionDef guiActions[GUI_ACTION_MAX]={
D("OPEN_BACKUP", "Restore backup", 0),
D("SAVE", "Save file", FURKMOD_CMD|SDLK_s),
D("SAVE_AS", "Save as", FURKMOD_CMD|FURKMOD_SHIFT|SDLK_s),
D("EXPORT", "Export", 0),
D("UNDO", "Undo", FURKMOD_CMD|SDLK_z),
#ifdef __APPLE__
D("REDO", "Redo", FURKMOD_CMD|FURKMOD_SHIFT|SDLK_z),
#else
D("REDO", "Redo", FURKMOD_CMD|SDLK_y),
#endif
D("QUIT", "Exit", 0),
D("PLAY_TOGGLE", "Play/Stop (toggle)", SDLK_RETURN),
D("PLAY", "Play", 0),
D("STOP", "Stop", 0),
@ -492,6 +562,15 @@ const FurnaceGUIActionDef guiActions[GUI_ACTION_MAX]={
D("PANIC", "Panic", SDLK_F12),
D("CLEAR", "Clear song data", 0),
D("COMMAND_PALETTE", "Command Palette", FURKMOD_CMD|SDLK_p),
D("CMDPAL_MIN", "", NOT_AN_ACTION),
D("CMDPAL_RECENT", "Recent files (Palette)", 0),
D("CMDPAL_INSTRUMENTS", "Instruments (Palette)", 0),
D("CMDPAL_SAMPLES", "Samples (Palette)", 0),
D("CMDPAL_INSTRUMENT_CHANGE", "Change instrument (Palette)", 0),
D("CMDPAL_ADD_CHIP", "Add chip (Palette)", 0),
D("CMDPAL_MAX", "", NOT_AN_ACTION),
D("WINDOW_EDIT_CONTROLS", "Edit Controls", 0),
D("WINDOW_ORDERS", "Orders", 0),
D("WINDOW_INS_LIST", "Instrument List", 0),
@ -504,7 +583,11 @@ const FurnaceGUIActionDef guiActions[GUI_ACTION_MAX]={
D("WINDOW_SAMPLE_LIST", "Sample List", 0),
D("WINDOW_SAMPLE_EDIT", "Sample Editor", 0),
D("WINDOW_ABOUT", "About", 0),
#ifdef __APPLE__
D("WINDOW_SETTINGS", "Settings", FURKMOD_CMD|SDLK_COMMA),
#else
D("WINDOW_SETTINGS", "Settings", 0),
#endif
D("WINDOW_MIXER", "Mixer", 0),
D("WINDOW_DEBUG", "Debug Menu", FURKMOD_CMD|FURKMOD_SHIFT|SDLK_d),
D("WINDOW_OSCILLOSCOPE", "Oscilloscope (master)", 0),
@ -518,12 +601,16 @@ const FurnaceGUIActionDef guiActions[GUI_ACTION_MAX]={
D("WINDOW_SYS_MANAGER", "Chip Manager", 0),
D("WINDOW_REGISTER_VIEW", "Register View", 0),
D("WINDOW_LOG", "Log Viewer", 0),
D("EFFECT_LIST", "Effect List", 0),
D("WINDOW_EFFECT_LIST", "Effect List", 0),
D("WINDOW_CHAN_OSC", "Oscilloscope (per-channel)", 0),
D("WINDOW_SUBSONGS", "Subsongs", 0),
D("WINDOW_FIND", "Find/Replace", FURKMOD_CMD|SDLK_f),
D("WINDOW_CLOCK", "Clock", 0),
D("WINDOW_GROOVES", "Grooves", 0),
D("WINDOW_XY_OSC", "Oscilloscope (X-Y)", 0),
D("WINDOW_MEMORY", "Memory Composition", 0),
D("WINDOW_CS_PLAYER", "Command Stream Player", 0),
D("WINDOW_USER_PRESETS", "User Presets", 0),
D("COLLAPSE_WINDOW", "Collapse/expand current window", 0),
D("CLOSE_WINDOW", "Close current window", FURKMOD_SHIFT|SDLK_ESCAPE),
@ -554,8 +641,8 @@ const FurnaceGUIActionDef guiActions[GUI_ACTION_MAX]={
D("PAT_CURSOR_DOWN_ONE", "Move cursor down by one (override Edit Step)", FURKMOD_SHIFT|SDLK_END),
D("PAT_CURSOR_LEFT_CHANNEL", "Move cursor to previous channel", 0),
D("PAT_CURSOR_RIGHT_CHANNEL", "Move cursor to next channel", 0),
D("PAT_CURSOR_NEXT_CHANNEL", "Move cursor to previous channel (overflow)", 0),
D("PAT_CURSOR_PREVIOUS_CHANNEL", "Move cursor to next channel (overflow)", 0),
D("PAT_CURSOR_NEXT_CHANNEL", "Move cursor to next channel (overflow)", 0),
D("PAT_CURSOR_PREVIOUS_CHANNEL", "Move cursor to previous channel (overflow)", 0),
D("PAT_CURSOR_BEGIN", "Move cursor to beginning of pattern", SDLK_HOME),
D("PAT_CURSOR_END", "Move cursor to end of pattern", SDLK_END),
D("PAT_CURSOR_UP_COARSE", "Move cursor up (coarse)", SDLK_PAGEUP),
@ -597,110 +684,111 @@ const FurnaceGUIActionDef guiActions[GUI_ACTION_MAX]={
D("PAT_MAX", "", NOT_AN_ACTION),
D("INS_LIST_MIN", "---Instrument list", NOT_AN_ACTION),
D("INS_LIST_ADD", "Add", SDLK_INSERT),
D("INS_LIST_DUPLICATE", "Duplicate", FURKMOD_CMD|SDLK_d),
D("INS_LIST_OPEN", "Open", 0),
D("INS_LIST_OPEN_REPLACE", "Open (replace current)", 0),
D("INS_LIST_SAVE", "Save", 0),
D("INS_LIST_SAVE_OLD", "Save (legacy .fui)", 0),
D("INS_LIST_SAVE_DMP", "Save (.dmp)", 0),
D("INS_LIST_MOVE_UP", "Move up", FURKMOD_SHIFT|SDLK_UP),
D("INS_LIST_MOVE_DOWN", "Move down", FURKMOD_SHIFT|SDLK_DOWN),
D("INS_LIST_DELETE", "Delete", 0),
D("INS_LIST_EDIT", "Edit", FURKMOD_SHIFT|SDLK_RETURN),
D("INS_LIST_UP", "Cursor up", SDLK_UP),
D("INS_LIST_DOWN", "Cursor down", SDLK_DOWN),
D("INS_LIST_DIR_VIEW", "Toggle folders/standard view", FURKMOD_CMD|SDLK_v),
D("INS_LIST_ADD", "Add instrument", SDLK_INSERT),
D("INS_LIST_DUPLICATE", "Duplicate instrument", FURKMOD_CMD|SDLK_d),
D("INS_LIST_OPEN", "Open instrument", 0),
D("INS_LIST_OPEN_REPLACE", "Open instrument (replace current)", 0),
D("INS_LIST_SAVE", "Save instrument", 0),
D("INS_LIST_SAVE_DMP", "Save instrument (.dmp)", 0),
D("INS_LIST_MOVE_UP", "Move instrument up in list", FURKMOD_SHIFT|SDLK_UP),
D("INS_LIST_MOVE_DOWN", "Move instrument down in list", FURKMOD_SHIFT|SDLK_DOWN),
D("INS_LIST_DELETE", "Delete instrument", 0),
D("INS_LIST_EDIT", "Edit instrument", FURKMOD_SHIFT|SDLK_RETURN),
D("INS_LIST_UP", "Instrument cursor up", SDLK_UP),
D("INS_LIST_DOWN", "Instrument cursor down", SDLK_DOWN),
D("INS_LIST_DIR_VIEW", "Instruments: toggle folders/standard view", FURKMOD_CMD|SDLK_v),
D("INS_LIST_MAX", "", NOT_AN_ACTION),
D("WAVE_LIST_MIN", "---Wavetable list", NOT_AN_ACTION),
D("WAVE_LIST_ADD", "Add", SDLK_INSERT),
D("WAVE_LIST_DUPLICATE", "Duplicate", FURKMOD_CMD|SDLK_d),
D("WAVE_LIST_OPEN", "Open", 0),
D("WAVE_LIST_OPEN_REPLACE", "Open (replace current)", 0),
D("WAVE_LIST_SAVE", "Save", 0),
D("WAVE_LIST_SAVE_DMW", "Save (.dmw)", 0),
D("WAVE_LIST_SAVE_RAW", "Save (raw)", 0),
D("WAVE_LIST_MOVE_UP", "Move up", FURKMOD_SHIFT|SDLK_UP),
D("WAVE_LIST_MOVE_DOWN", "Move down", FURKMOD_SHIFT|SDLK_DOWN),
D("WAVE_LIST_DELETE", "Delete", 0),
D("WAVE_LIST_EDIT", "Edit", FURKMOD_SHIFT|SDLK_RETURN),
D("WAVE_LIST_UP", "Cursor up", SDLK_UP),
D("WAVE_LIST_DOWN", "Cursor down", SDLK_DOWN),
D("WAVE_LIST_DIR_VIEW", "Toggle folders/standard view", FURKMOD_CMD|SDLK_v),
D("WAVE_LIST_ADD", "Add wavetable", SDLK_INSERT),
D("WAVE_LIST_DUPLICATE", "Duplicate wavetable", FURKMOD_CMD|SDLK_d),
D("WAVE_LIST_OPEN", "Open wavetable", 0),
D("WAVE_LIST_OPEN_REPLACE", "Open wavetable (replace current)", 0),
D("WAVE_LIST_SAVE", "Save wavetable", 0),
D("WAVE_LIST_SAVE_DMW", "Save wavetable (.dmw)", 0),
D("WAVE_LIST_SAVE_RAW", "Save wavetable (raw)", 0),
D("WAVE_LIST_MOVE_UP", "Move wavetable up in list", FURKMOD_SHIFT|SDLK_UP),
D("WAVE_LIST_MOVE_DOWN", "Move wavetable down in list", FURKMOD_SHIFT|SDLK_DOWN),
D("WAVE_LIST_DELETE", "Delete wavetable", 0),
D("WAVE_LIST_EDIT", "Edit wavetable", FURKMOD_SHIFT|SDLK_RETURN),
D("WAVE_LIST_UP", "Wavetable cursor up", SDLK_UP),
D("WAVE_LIST_DOWN", "Wavetable cursor down", SDLK_DOWN),
D("WAVE_LIST_DIR_VIEW", "Wavetables: toggle folders/standard view", FURKMOD_CMD|SDLK_v),
D("WAVE_LIST_MAX", "", NOT_AN_ACTION),
D("SAMPLE_LIST_MIN", "---Sample list", NOT_AN_ACTION),
D("SAMPLE_LIST_ADD", "Add", SDLK_INSERT),
D("SAMPLE_LIST_DUPLICATE", "Duplicate", FURKMOD_CMD|SDLK_d),
D("SAMPLE_LIST_OPEN", "Open", 0),
D("SAMPLE_LIST_OPEN_REPLACE", "Open (replace current)", 0),
D("SAMPLE_LIST_OPEN_RAW", "Import raw data", 0),
D("SAMPLE_LIST_OPEN_REPLACE_RAW", "Import raw data (replace current)", 0),
D("SAMPLE_LIST_SAVE", "Save", 0),
D("SAMPLE_LIST_SAVE_RAW", "Save (raw)", 0),
D("SAMPLE_LIST_MOVE_UP", "Move up", FURKMOD_SHIFT|SDLK_UP),
D("SAMPLE_LIST_MOVE_DOWN", "Move down", FURKMOD_SHIFT|SDLK_DOWN),
D("SAMPLE_LIST_DELETE", "Delete", 0),
D("SAMPLE_LIST_EDIT", "Edit", FURKMOD_SHIFT|SDLK_RETURN),
D("SAMPLE_LIST_UP", "Cursor up", SDLK_UP),
D("SAMPLE_LIST_DOWN", "Cursor down", SDLK_DOWN),
D("SAMPLE_LIST_PREVIEW", "Preview", 0),
D("SAMPLE_LIST_STOP_PREVIEW", "Stop preview", 0),
D("SAMPLE_LIST_DIR_VIEW", "Toggle folders/standard view", FURKMOD_CMD|SDLK_v),
D("SAMPLE_LIST_ADD", "Add sample", SDLK_INSERT),
D("SAMPLE_LIST_DUPLICATE", "Duplicate sample", FURKMOD_CMD|SDLK_d),
D("SAMPLE_LIST_OPEN", "Open sample", 0),
D("SAMPLE_LIST_OPEN_REPLACE", "Open sample (replace current)", 0),
D("SAMPLE_LIST_OPEN_RAW", "Import raw sample data", 0),
D("SAMPLE_LIST_OPEN_REPLACE_RAW", "Import raw sample data (replace current)", 0),
D("SAMPLE_LIST_SAVE", "Save sample", 0),
D("SAMPLE_LIST_SAVE_RAW", "Save sample (raw)", 0),
D("SAMPLE_LIST_MOVE_UP", "Move sample up in list", FURKMOD_SHIFT|SDLK_UP),
D("SAMPLE_LIST_MOVE_DOWN", "Move sample down in list", FURKMOD_SHIFT|SDLK_DOWN),
D("SAMPLE_LIST_DELETE", "Delete sample", 0),
D("SAMPLE_LIST_EDIT", "Edit sample", FURKMOD_SHIFT|SDLK_RETURN),
D("SAMPLE_LIST_UP", "Sample cursor up", SDLK_UP),
D("SAMPLE_LIST_DOWN", "Sample cursor down", SDLK_DOWN),
D("SAMPLE_LIST_PREVIEW", "Sample preview", 0),
D("SAMPLE_LIST_STOP_PREVIEW", "Stop sample preview", 0),
D("SAMPLE_LIST_DIR_VIEW", "Samples: Toggle folders/standard view", FURKMOD_CMD|SDLK_v),
D("SAMPLE_LIST_MAKE_MAP", "Samples: Make me a drum kit", 0),
D("SAMPLE_LIST_MAX", "", NOT_AN_ACTION),
D("SAMPLE_MIN", "---Sample editor", NOT_AN_ACTION),
D("SAMPLE_SELECT", "Edit mode: Select", FURKMOD_SHIFT|SDLK_i),
D("SAMPLE_DRAW", "Edit mode: Draw", FURKMOD_SHIFT|SDLK_d),
D("SAMPLE_CUT", "Cut", FURKMOD_CMD|SDLK_x),
D("SAMPLE_COPY", "Copy", FURKMOD_CMD|SDLK_c),
D("SAMPLE_PASTE", "Paste", FURKMOD_CMD|SDLK_v),
D("SAMPLE_PASTE_REPLACE", "Paste replace", FURKMOD_CMD|FURKMOD_SHIFT|SDLK_v),
D("SAMPLE_PASTE_MIX", "Paste mix", FURKMOD_CMD|FURKMOD_ALT|SDLK_v),
D("SAMPLE_SELECT_ALL", "Select all", FURKMOD_CMD|SDLK_a),
D("SAMPLE_RESIZE", "Resize", FURKMOD_CMD|SDLK_r),
D("SAMPLE_RESAMPLE", "Resample", FURKMOD_CMD|SDLK_e),
D("SAMPLE_AMPLIFY", "Amplify", FURKMOD_CMD|SDLK_b),
D("SAMPLE_NORMALIZE", "Normalize", FURKMOD_CMD|SDLK_n),
D("SAMPLE_FADE_IN", "Fade in", FURKMOD_CMD|SDLK_i),
D("SAMPLE_FADE_OUT", "Fade out", FURKMOD_CMD|SDLK_o),
D("SAMPLE_SILENCE", "Apply silence", FURKMOD_SHIFT|SDLK_DELETE),
D("SAMPLE_INSERT", "Insert silence", SDLK_INSERT),
D("SAMPLE_DELETE", "Delete", SDLK_DELETE),
D("SAMPLE_TRIM", "Trim", FURKMOD_CMD|SDLK_DELETE),
D("SAMPLE_REVERSE", "Reverse", FURKMOD_CMD|SDLK_t),
D("SAMPLE_INVERT", "Invert", FURKMOD_CMD|FURKMOD_SHIFT|SDLK_t),
D("SAMPLE_SIGN", "Signed/unsigned exchange", FURKMOD_CMD|SDLK_u),
D("SAMPLE_FILTER", "Apply filter", FURKMOD_CMD|SDLK_f),
D("SAMPLE_PREVIEW", "Preview sample", 0),
D("SAMPLE_STOP_PREVIEW", "Stop sample preview", 0),
D("SAMPLE_ZOOM_IN", "Zoom in", FURKMOD_CMD|SDLK_EQUALS),
D("SAMPLE_ZOOM_OUT", "Zoom out", FURKMOD_CMD|SDLK_MINUS),
D("SAMPLE_ZOOM_AUTO", "Toggle auto-zoom", FURKMOD_CMD|SDLK_0),
D("SAMPLE_MAKE_INS", "Create instrument from sample", 0),
D("SAMPLE_SET_LOOP", "Set loop to selection", FURKMOD_CMD|SDLK_l),
D("SAMPLE_CREATE_WAVE", "Create wavetable from selection", FURKMOD_CMD|SDLK_w),
D("SAMPLE_SELECT", "Sample editor mode: Select", FURKMOD_SHIFT|SDLK_i),
D("SAMPLE_DRAW", "Sample editor mode: Draw", FURKMOD_SHIFT|SDLK_d),
D("SAMPLE_CUT", "Sample editor: Cut", FURKMOD_CMD|SDLK_x),
D("SAMPLE_COPY", "Sample editor: Copy", FURKMOD_CMD|SDLK_c),
D("SAMPLE_PASTE", "Sample editor: Paste", FURKMOD_CMD|SDLK_v),
D("SAMPLE_PASTE_REPLACE", "Sample editor: Paste replace", FURKMOD_CMD|FURKMOD_SHIFT|SDLK_v),
D("SAMPLE_PASTE_MIX", "Sample editor: Paste mix", FURKMOD_CMD|FURKMOD_ALT|SDLK_v),
D("SAMPLE_SELECT_ALL", "Sample editor: Select all", FURKMOD_CMD|SDLK_a),
D("SAMPLE_RESIZE", "Sample editor: Resize", FURKMOD_CMD|SDLK_r),
D("SAMPLE_RESAMPLE", "Sample editor: Resample", FURKMOD_CMD|SDLK_e),
D("SAMPLE_AMPLIFY", "Sample editor: Amplify", FURKMOD_CMD|SDLK_b),
D("SAMPLE_NORMALIZE", "Sample editor: Normalize", FURKMOD_CMD|SDLK_n),
D("SAMPLE_FADE_IN", "Sample editor: Fade in", FURKMOD_CMD|SDLK_i),
D("SAMPLE_FADE_OUT", "Sample editor: Fade out", FURKMOD_CMD|SDLK_o),
D("SAMPLE_SILENCE", "Sample editor: Apply silence", FURKMOD_SHIFT|SDLK_DELETE),
D("SAMPLE_INSERT", "Sample editor: Insert silence", SDLK_INSERT),
D("SAMPLE_DELETE", "Sample editor: Delete", SDLK_DELETE),
D("SAMPLE_TRIM", "Sample editor: Trim", FURKMOD_CMD|SDLK_DELETE),
D("SAMPLE_REVERSE", "Sample editor: Reverse", FURKMOD_CMD|SDLK_t),
D("SAMPLE_INVERT", "Sample editor: Invert", FURKMOD_CMD|FURKMOD_SHIFT|SDLK_t),
D("SAMPLE_SIGN", "Sample editor: Signed/unsigned exchange", FURKMOD_CMD|SDLK_u),
D("SAMPLE_FILTER", "Sample editor: Apply filter", FURKMOD_CMD|SDLK_f),
D("SAMPLE_CROSSFADE_LOOP", "Sample editor: Crossfade loop points", NOT_AN_ACTION),
D("SAMPLE_PREVIEW", "Sample editor: Preview sample", 0),
D("SAMPLE_STOP_PREVIEW", "Sample editor: Stop sample preview", 0),
D("SAMPLE_ZOOM_IN", "Sample editor: Zoom in", FURKMOD_CMD|SDLK_EQUALS),
D("SAMPLE_ZOOM_OUT", "Sample editor: Zoom out", FURKMOD_CMD|SDLK_MINUS),
D("SAMPLE_ZOOM_AUTO", "Sample editor: Toggle auto-zoom", FURKMOD_CMD|SDLK_0),
D("SAMPLE_MAKE_INS", "Sample editor: Create instrument from sample", 0),
D("SAMPLE_SET_LOOP", "Sample editor: Set loop to selection", FURKMOD_CMD|SDLK_l),
D("SAMPLE_CREATE_WAVE", "Sample editor: Create wavetable from selection", FURKMOD_CMD|SDLK_w),
D("SAMPLE_MAX", "", NOT_AN_ACTION),
D("ORDERS_MIN", "---Orders", NOT_AN_ACTION),
D("ORDERS_UP", "Previous order", SDLK_UP),
D("ORDERS_DOWN", "Next order", SDLK_DOWN),
D("ORDERS_LEFT", "Cursor left", SDLK_LEFT),
D("ORDERS_RIGHT", "Cursor right", SDLK_RIGHT),
D("ORDERS_INCREASE", "Increase value", 0),
D("ORDERS_DECREASE", "Decrease value", 0),
D("ORDERS_EDIT_MODE", "Switch edit mode", 0),
D("ORDERS_LINK", "Toggle alter entire row", FURKMOD_CMD|SDLK_l),
D("ORDERS_ADD", "Add", SDLK_INSERT),
D("ORDERS_DUPLICATE", "Duplicate", FURKMOD_CMD|SDLK_d),
D("ORDERS_DEEP_CLONE", "Deep clone", FURKMOD_CMD|FURKMOD_SHIFT|SDLK_d),
D("ORDERS_DUPLICATE_END", "Duplicate to end of song", FURKMOD_CMD|SDLK_e),
D("ORDERS_DEEP_CLONE_END", "Deep clone to end of song", FURKMOD_CMD|FURKMOD_SHIFT|SDLK_e),
D("ORDERS_REMOVE", "Remove", SDLK_DELETE),
D("ORDERS_MOVE_UP", "Move up", FURKMOD_SHIFT|SDLK_UP),
D("ORDERS_MOVE_DOWN", "Move down", FURKMOD_SHIFT|SDLK_DOWN),
D("ORDERS_REPLAY", "Replay", 0),
D("ORDERS_LEFT", "Order cursor left", SDLK_LEFT),
D("ORDERS_RIGHT", "Order cursor right", SDLK_RIGHT),
D("ORDERS_INCREASE", "Increase order value", 0),
D("ORDERS_DECREASE", "Decrease order value", 0),
D("ORDERS_EDIT_MODE", "Switch order edit mode", 0),
D("ORDERS_LINK", "Order: toggle alter entire row", FURKMOD_CMD|SDLK_l),
D("ORDERS_ADD", "Add order", SDLK_INSERT),
D("ORDERS_DUPLICATE", "Duplicate order", FURKMOD_CMD|SDLK_d),
D("ORDERS_DEEP_CLONE", "Deep clone order", FURKMOD_CMD|FURKMOD_SHIFT|SDLK_d),
D("ORDERS_DUPLICATE_END", "Copy current order to end of song", FURKMOD_CMD|SDLK_e),
D("ORDERS_DEEP_CLONE_END", "Deep clone current order to end of song", FURKMOD_CMD|FURKMOD_SHIFT|SDLK_e),
D("ORDERS_REMOVE", "Remove order", SDLK_DELETE),
D("ORDERS_MOVE_UP", "Move order up", FURKMOD_SHIFT|SDLK_UP),
D("ORDERS_MOVE_DOWN", "Move order down", FURKMOD_SHIFT|SDLK_DOWN),
D("ORDERS_REPLAY", "Replay order", 0),
D("ORDERS_MAX", "", NOT_AN_ACTION),
};
#undef D
@ -715,6 +803,7 @@ const FurnaceGUIColorDef guiColors[GUI_COLOR_MAX]={
D(GUI_COLOR_MODAL_BACKDROP,"",ImVec4(0.0f,0.0f,0.0f,0.55f)),
D(GUI_COLOR_HEADER,"",ImVec4(0.2f,0.2f,0.2f,1.0f)),
D(GUI_COLOR_TEXT,"",ImVec4(1.0f,1.0f,1.0f,1.0f)),
D(GUI_COLOR_TEXT_DISABLED,"",ImVec4(0.5f,0.5f,0.5f,1.0f)),
D(GUI_COLOR_ACCENT_PRIMARY,"",ImVec4(0.06f,0.53f,0.98f,1.0f)),
D(GUI_COLOR_ACCENT_SECONDARY,"",ImVec4(0.26f,0.59f,0.98f,1.0f)),
D(GUI_COLOR_TITLE_INACTIVE,"",ImVec4(0.04f,0.04f,0.04f,1.0f)),
@ -738,9 +827,40 @@ const FurnaceGUIColorDef guiColors[GUI_COLOR_MAX]={
D(GUI_COLOR_NAV_HIGHLIGHT,"",ImVec4(0.26f,0.59f,0.98f,1.0f)),
D(GUI_COLOR_NAV_WIN_HIGHLIGHT,"",ImVec4(1.0f,1.0f,1.0f,0.7f)),
D(GUI_COLOR_NAV_WIN_BACKDROP,"",ImVec4(0.8f,0.8f,0.8f,0.2f)),
D(GUI_COLOR_PLOT_LINES,"",ImVec4(0.61f,0.61f,0.61f,1.0f)),
D(GUI_COLOR_PLOT_LINES_HOVER,"",ImVec4(1.00f,0.43f,0.35f,1.00f)),
D(GUI_COLOR_PLOT_HISTOGRAM,"",ImVec4(0.0f,0.9f,1.0f,1.0f)),
D(GUI_COLOR_PLOT_HISTOGRAM_HOVER,"",ImVec4(0.0f,0.9f,1.0f,1.0f)),
D(GUI_COLOR_BUTTON,"",ImVec4(0.085f,0.216f,0.343f,1.0f)),
D(GUI_COLOR_BUTTON_HOVER,"",ImVec4(0.075f,0.287f,0.49f,1.0f)),
D(GUI_COLOR_BUTTON_ACTIVE,"",ImVec4(0.06f,0.53f,0.98f,1.0f)),
D(GUI_COLOR_TAB,"",ImVec4(0.085f,0.216f,0.343f,1.0f)),
D(GUI_COLOR_TAB_HOVER,"",ImVec4(0.165f,0.313f,0.49f,1.0f)),
D(GUI_COLOR_TAB_ACTIVE,"",ImVec4(0.25f,0.47f,0.735f,1.0f)),
D(GUI_COLOR_TAB_UNFOCUSED,"",ImVec4(0.085f,0.216f,0.343f,1.0f)),
D(GUI_COLOR_TAB_UNFOCUSED_ACTIVE,"",ImVec4(0.075f,0.287f,0.49f,1.0f)),
D(GUI_COLOR_IMGUI_HEADER,"",ImVec4(0.083f,0.156f,0.245f,1.0f)),
D(GUI_COLOR_IMGUI_HEADER_HOVER,"",ImVec4(0.165f,0.313f,0.49f,1.0f)),
D(GUI_COLOR_IMGUI_HEADER_ACTIVE,"",ImVec4(0.26f,0.59f,0.98f,1.0f)),
D(GUI_COLOR_RESIZE_GRIP,"",ImVec4(0.083f,0.156f,0.245f,1.0f)),
D(GUI_COLOR_RESIZE_GRIP_HOVER,"",ImVec4(0.165f,0.313f,0.49f,1.0f)),
D(GUI_COLOR_RESIZE_GRIP_ACTIVE,"",ImVec4(0.26f,0.59f,0.98f,1.0f)),
D(GUI_COLOR_WIDGET_BACKGROUND,"",ImVec4(0.083f,0.156f,0.245f,1.0f)),
D(GUI_COLOR_WIDGET_BACKGROUND_HOVER,"",ImVec4(0.165f,0.313f,0.49f,1.0f)),
D(GUI_COLOR_WIDGET_BACKGROUND_ACTIVE,"",ImVec4(0.26f,0.59f,0.98f,1.0f)),
D(GUI_COLOR_SLIDER_GRAB,"",ImVec4(0.06f,0.53f,0.98f,1.0f)),
D(GUI_COLOR_SLIDER_GRAB_ACTIVE,"",ImVec4(0.06f,0.53f,0.98f,1.0f)),
D(GUI_COLOR_TITLE_BACKGROUND_ACTIVE,"",ImVec4(0.085f,0.216f,0.343f,1.0f)),
D(GUI_COLOR_CHECK_MARK,"",ImVec4(0.06f,0.53f,0.98f,1.0f)),
D(GUI_COLOR_TEXT_SELECTION,"",ImVec4(0.165f,0.313f,0.49f,1.0f)),
D(GUI_COLOR_TABLE_ROW_EVEN,"",ImVec4(0.0f,0.0f,0.0f,0.0f)),
D(GUI_COLOR_TABLE_ROW_ODD,"",ImVec4(1.0f,1.0f,1.0f,0.06f)),
D(GUI_COLOR_TOGGLE_OFF,"",ImVec4(0.2f,0.2f,0.2f,1.0f)),
D(GUI_COLOR_TOGGLE_ON,"",ImVec4(0.2f,0.6f,0.2f,1.0f)),
D(GUI_COLOR_EDITING,"",ImVec4(0.2f,0.1f,0.1f,1.0f)),
D(GUI_COLOR_EDITING_CLONE,"",ImVec4(0.2f,0.1f,0.3f,1.0f)),
D(GUI_COLOR_SONG_LOOP,"",ImVec4(0.3f,0.5f,0.8f,0.4f)),
D(GUI_COLOR_DESTRUCTIVE,"",ImVec4(1.0f,0.2f,0.2f,1.0f)),
D(GUI_COLOR_WARNING,"",ImVec4(0.98f,0.98f,0.06f,1.0f)),
@ -764,6 +884,22 @@ const FurnaceGUIColorDef guiColors[GUI_COLOR_MAX]={
D(GUI_COLOR_OSC_BORDER,"",ImVec4(0.4f,0.6f,0.95f,1.0f)),
D(GUI_COLOR_OSC_WAVE,"",ImVec4(0.95f,0.95f,1.0f,1.0f)),
D(GUI_COLOR_OSC_WAVE_PEAK,"",ImVec4(0.95f,0.95f,1.0f,1.0f)),
D(GUI_COLOR_OSC_WAVE_CH0,"",ImVec4(1.0f,1.0f,1.0f,1.0f)),
D(GUI_COLOR_OSC_WAVE_CH1,"",ImVec4(1.0f,0.2f,0.2f,1.0f)),
D(GUI_COLOR_OSC_WAVE_CH2,"",ImVec4(0.1f,0.5f,1.0f,1.0f)),
D(GUI_COLOR_OSC_WAVE_CH3,"",ImVec4(0.5f,0.5f,0.5f,1.0f)),
D(GUI_COLOR_OSC_WAVE_CH4,"",ImVec4(0.7f,0.2f,0.7f,1.0f)),
D(GUI_COLOR_OSC_WAVE_CH5,"",ImVec4(0.2f,1.0f,0.2f,1.0f)),
D(GUI_COLOR_OSC_WAVE_CH6,"",ImVec4(1.0f,1.0f,0.1f,1.0f)),
D(GUI_COLOR_OSC_WAVE_CH7,"",ImVec4(1.0f,0.5f,0.1f,1.0f)),
D(GUI_COLOR_OSC_WAVE_CH8,"",ImVec4(0.9f,1.0f,0.1f,1.0f)),
D(GUI_COLOR_OSC_WAVE_CH9,"",ImVec4(0.8f,1.0f,0.1f,1.0f)),
D(GUI_COLOR_OSC_WAVE_CH10,"",ImVec4(0.7f,1.0f,0.1f,1.0f)),
D(GUI_COLOR_OSC_WAVE_CH11,"",ImVec4(0.6f,1.0f,0.1f,1.0f)),
D(GUI_COLOR_OSC_WAVE_CH12,"",ImVec4(0.5f,1.0f,0.1f,1.0f)),
D(GUI_COLOR_OSC_WAVE_CH13,"",ImVec4(0.4f,1.0f,0.1f,1.0f)),
D(GUI_COLOR_OSC_WAVE_CH14,"",ImVec4(0.3f,1.0f,0.1f,1.0f)),
D(GUI_COLOR_OSC_WAVE_CH15,"",ImVec4(0.2f,1.0f,0.1f,1.0f)),
D(GUI_COLOR_OSC_REF,"",ImVec4(0.3,0.65f,1.0f,0.15f)),
D(GUI_COLOR_OSC_GUIDE,"",ImVec4(0.3,0.65f,1.0f,0.13f)),
@ -851,6 +987,18 @@ const FurnaceGUIColorDef guiColors[GUI_COLOR_MAX]={
D(GUI_COLOR_INSTR_POKEMINI,"",ImVec4(1.0f,1.0f,0.3f,1.0f)),
D(GUI_COLOR_INSTR_SM8521,"",ImVec4(0.5f,0.55f,0.6f,1.0f)),
D(GUI_COLOR_INSTR_PV1000,"",ImVec4(0.4f,0.6f,0.7f,1.0f)),
D(GUI_COLOR_INSTR_K053260,"",ImVec4(1.0f,0.8f,0.1f,1.0f)),
D(GUI_COLOR_INSTR_SCSP,"",ImVec4(0.5f,0.5f,0.5f,1.0f)),
D(GUI_COLOR_INSTR_TED,"",ImVec4(0.7f,0.6f,1.0f,1.0f)),
D(GUI_COLOR_INSTR_C140,"",ImVec4(1.0f,1.0f,0.0f,1.0f)),
D(GUI_COLOR_INSTR_C219,"",ImVec4(1.0f,0.8f,0.0f,1.0f)),
D(GUI_COLOR_INSTR_ESFM,"",ImVec4(0.1f,0.9f,1.0f,1.0f)),
D(GUI_COLOR_INSTR_POWERNOISE,"",ImVec4(1.0f,1.0f,0.8f,1.0f)),
D(GUI_COLOR_INSTR_POWERNOISE_SLOPE,"",ImVec4(1.0f,0.6f,0.3f,1.0f)),
D(GUI_COLOR_INSTR_DAVE,"",ImVec4(0.7f,0.7f,0.8f,1.0f)),
D(GUI_COLOR_INSTR_NDS,"",ImVec4(0.7f,0.7f,0.8f,1.0f)),
D(GUI_COLOR_INSTR_GBA_DMA,"",ImVec4(0.6f,0.4f,1.0f,1.0f)),
D(GUI_COLOR_INSTR_GBA_MINMOD,"",ImVec4(0.5f,0.45f,0.7f,1.0f)),
D(GUI_COLOR_INSTR_UNKNOWN,"",ImVec4(0.3f,0.3f,0.3f,1.0f)),
D(GUI_COLOR_CHANNEL_BG,"",ImVec4(0.4f,0.6f,0.8f,1.0f)),
@ -898,6 +1046,33 @@ const FurnaceGUIColorDef guiColors[GUI_COLOR_MAX]={
D(GUI_COLOR_PATTERN_EFFECT_SYS_SECONDARY,"",ImVec4(0.0f,1.0f,0.5f,1.0f)),
D(GUI_COLOR_PATTERN_EFFECT_MISC,"",ImVec4(0.3f,0.3f,1.0f,1.0f)),
D(GUI_COLOR_PATTERN_STATUS_OFF,"",ImVec4(0.2f,0.2f,0.2f,1.0f)),
D(GUI_COLOR_PATTERN_STATUS_REL,"",ImVec4(0.7f,0.1f,0.1f,1.0f)),
D(GUI_COLOR_PATTERN_STATUS_REL_ON,"",ImVec4(1.0f,0.8f,0.1f,1.0f)),
D(GUI_COLOR_PATTERN_STATUS_ON,"",ImVec4(0.3f,1.0f,0.1f,1.0f)),
D(GUI_COLOR_PATTERN_STATUS_VOLUME,"",ImVec4(0.0f,1.0f,0.0f,1.0f)),
D(GUI_COLOR_PATTERN_STATUS_PITCH,"",ImVec4(1.0f,1.0f,0.0f,1.0f)),
D(GUI_COLOR_PATTERN_STATUS_PANNING,"",ImVec4(0.0f,1.0f,1.0f,1.0f)),
D(GUI_COLOR_PATTERN_STATUS_SYS1,"",ImVec4(0.5f,1.0f,0.0f,1.0f)),
D(GUI_COLOR_PATTERN_STATUS_SYS2,"",ImVec4(0.0f,1.0f,0.5f,1.0f)),
D(GUI_COLOR_PATTERN_STATUS_MIXING,"",ImVec4(1.0f,0.3f,1.0f,1.0f)),
D(GUI_COLOR_PATTERN_STATUS_DSP,"",ImVec4(0.3f,0.6f,1.0f,1.0f)),
D(GUI_COLOR_PATTERN_STATUS_NOTE,"",ImVec4(0.3f,0.3f,1.0f,1.0f)),
D(GUI_COLOR_PATTERN_STATUS_MISC1,"",ImVec4(1.0f,0.5f,0.0f,1.0f)),
D(GUI_COLOR_PATTERN_STATUS_MISC2,"",ImVec4(0.7f,0.5f,1.0f,1.0f)),
D(GUI_COLOR_PATTERN_STATUS_MISC3,"",ImVec4(1.0f,0.1f,0.1f,1.0f)),
D(GUI_COLOR_PATTERN_STATUS_ATTACK,"",ImVec4(1.0f,0.1f,0.1f,1.0f)),
D(GUI_COLOR_PATTERN_STATUS_DECAY,"",ImVec4(0.1f,0.4f,0.5f,1.0f)),
D(GUI_COLOR_PATTERN_STATUS_SUSTAIN,"",ImVec4(0.1f,1.0f,1.0f,1.0f)),
D(GUI_COLOR_PATTERN_STATUS_RELEASE,"",ImVec4(1.0f,0.1f,1.0f,1.0f)),
D(GUI_COLOR_PATTERN_STATUS_DEC_LINEAR,"",ImVec4(0.0f,0.6f,0.7f,1.0f)),
D(GUI_COLOR_PATTERN_STATUS_DEC_EXP,"",ImVec4(1.0f,0.3f,0.0f,1.0f)),
D(GUI_COLOR_PATTERN_STATUS_INC,"",ImVec4(0.1f,0.1f,1.0f,1.0f)),
D(GUI_COLOR_PATTERN_STATUS_BENT,"",ImVec4(1.0f,1.0f,0.1f,1.0f)),
D(GUI_COLOR_PATTERN_STATUS_DIRECT,"",ImVec4(1.0f,0.5f,0.2f,1.0f)),
D(GUI_COLOR_PATTERN_PAIR,"",ImVec4(0.6f,0.8f,1.0f,1.0f)),
D(GUI_COLOR_SAMPLE_BG,"",ImVec4(0.04f,0.13f,0.2f,1.0f)),
D(GUI_COLOR_SAMPLE_FG,"",ImVec4(0.7f,0.7f,0.7f,1.0f)),
D(GUI_COLOR_SAMPLE_LOOP,"",ImVec4(0.1f,0.22f,0.35f,1.0f)),
@ -940,6 +1115,29 @@ const FurnaceGUIColorDef guiColors[GUI_COLOR_MAX]={
D(GUI_COLOR_PATCHBAY_CONNECTION_BG,"",ImVec4(0.1f,0.25f,0.3f,1.0f)),
D(GUI_COLOR_PATCHBAY_CONNECTION_HI,"",ImVec4(0.2f,1.0f,1.0f,1.0f)),
D(GUI_COLOR_MEMORY_BG,"",ImVec4(0.12f,0.12f,0.12f,1.0f)),
D(GUI_COLOR_MEMORY_DATA,"",ImVec4(1.0f,1.0f,1.0f,0.8f)),
D(GUI_COLOR_MEMORY_FREE,"",ImVec4(0.25f,0.25f,0.25f,1.0f)),
D(GUI_COLOR_MEMORY_PADDING,"",ImVec4(0.25f,0.25f,0.25f,1.0f)),
D(GUI_COLOR_MEMORY_RESERVED,"",ImVec4(0.5f,0.5f,0.6f,1.0f)),
D(GUI_COLOR_MEMORY_SAMPLE,"",ImVec4(1.0f,1.0f,0.2f,1.0f)),
D(GUI_COLOR_MEMORY_SAMPLE_ALT1,"",ImVec4(0.5f,1.0f,0.2f,1.0f)),
D(GUI_COLOR_MEMORY_SAMPLE_ALT2,"",ImVec4(0.2f,1.0f,0.2f,1.0f)),
D(GUI_COLOR_MEMORY_SAMPLE_ALT3,"",ImVec4(0.2f,1.0f,0.5f,1.0f)),
D(GUI_COLOR_MEMORY_WAVE_RAM,"",ImVec4(1.0f,0.5f,0.1f,1.0f)),
D(GUI_COLOR_MEMORY_WAVE_STATIC,"",ImVec4(1.0f,0.3f,0.1f,1.0f)),
D(GUI_COLOR_MEMORY_ECHO,"",ImVec4(0.2f,1.0f,1.0f,1.0f)),
D(GUI_COLOR_MEMORY_N163_LOAD,"",ImVec4(1.0f,0.5f,0.1f,1.0f)),
D(GUI_COLOR_MEMORY_N163_PLAY,"",ImVec4(1.0f,1.0f,0.2f,1.0f)),
D(GUI_COLOR_MEMORY_BANK0,"",ImVec4(1.0f,0.1f,0.1f,1.0f)),
D(GUI_COLOR_MEMORY_BANK1,"",ImVec4(1.0f,0.5f,0.1f,1.0f)),
D(GUI_COLOR_MEMORY_BANK2,"",ImVec4(1.0f,1.0f,0.1f,1.0f)),
D(GUI_COLOR_MEMORY_BANK3,"",ImVec4(0.1f,1.0f,0.1f,1.0f)),
D(GUI_COLOR_MEMORY_BANK4,"",ImVec4(0.1f,1.0f,1.0f,1.0f)),
D(GUI_COLOR_MEMORY_BANK5,"",ImVec4(0.1f,0.1f,1.0f,1.0f)),
D(GUI_COLOR_MEMORY_BANK6,"",ImVec4(0.5f,0.1f,1.0f,1.0f)),
D(GUI_COLOR_MEMORY_BANK7,"",ImVec4(1.0f,0.1f,1.0f,1.0f)),
D(GUI_COLOR_LOGLEVEL_ERROR,"",ImVec4(1.0f,0.2f,0.2f,1.0f)),
D(GUI_COLOR_LOGLEVEL_WARNING,"",ImVec4(1.0f,1.0f,0.2f,1.0f)),
D(GUI_COLOR_LOGLEVEL_INFO,"",ImVec4(0.4f,1.0f,0.4f,1.0f)),
@ -1034,8 +1232,19 @@ const int availableSystems[]={
DIV_SYSTEM_GA20,
DIV_SYSTEM_SM8521,
DIV_SYSTEM_PV1000,
DIV_SYSTEM_K053260,
DIV_SYSTEM_TED,
DIV_SYSTEM_C140,
DIV_SYSTEM_C219,
DIV_SYSTEM_GBA_DMA,
DIV_SYSTEM_GBA_MINMOD,
DIV_SYSTEM_PCM_DAC,
DIV_SYSTEM_ESFM,
DIV_SYSTEM_PONG,
DIV_SYSTEM_POWERNOISE,
DIV_SYSTEM_DAVE,
DIV_SYSTEM_NDS,
DIV_SYSTEM_5E01,
0 // don't remove this last one!
};
@ -1070,6 +1279,7 @@ const int chipsFM[]={
DIV_SYSTEM_OPL3,
DIV_SYSTEM_OPL3_DRUMS,
DIV_SYSTEM_OPZ,
DIV_SYSTEM_ESFM,
0 // don't remove this last one!
};
@ -1084,6 +1294,7 @@ const int chipsSquare[]={
DIV_SYSTEM_MSM5232,
DIV_SYSTEM_T6W28,
DIV_SYSTEM_PV1000,
DIV_SYSTEM_TED,
0 // don't remove this last one!
};
@ -1123,6 +1334,10 @@ const int chipsSpecial[]={
DIV_SYSTEM_VRC6,
DIV_SYSTEM_MMC5,
DIV_SYSTEM_SM8521,
DIV_SYSTEM_POWERNOISE,
DIV_SYSTEM_DAVE,
DIV_SYSTEM_NDS,
DIV_SYSTEM_5E01,
0 // don't remove this last one!
};
@ -1142,6 +1357,12 @@ const int chipsSample[]={
DIV_SYSTEM_GA20,
DIV_SYSTEM_PCM_DAC,
DIV_SYSTEM_ES5506,
DIV_SYSTEM_K053260,
DIV_SYSTEM_C140,
DIV_SYSTEM_C219,
DIV_SYSTEM_NDS,
DIV_SYSTEM_GBA_DMA,
DIV_SYSTEM_GBA_MINMOD,
0 // don't remove this last one!
};

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2023 tildearrow and contributors
* Copyright (C) 2021-2024 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -38,8 +38,10 @@ struct FurnaceGUIColorDef {
extern const int opOrder[4];
extern const char* noteNames[180];
extern const char* noteNamesG[180];
extern const char* noteNamesF[180];
extern const char* noteNamesGF[180];
extern const char* pitchLabel[11];
extern const char* insTypes[];
extern const char* insTypes[][3];
extern const char* sampleLoopModes[];
extern const char* sampleDepths[];
extern const char* resampleStrats[];
@ -55,4 +57,6 @@ extern const FurnaceGUIActionDef guiActions[];
extern const FurnaceGUIColorDef guiColors[];
extern const int altValues[24];
extern const int vgmVersions[7];
extern const FurnaceGUIColors fxColors[256];
extern const FurnaceGUIColors fxColors[256];
extern const FurnaceGUIColors fxColorsSort[10];
extern const char* fxColorsNames[10];

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2023 tildearrow and contributors
* Copyright (C) 2021-2024 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -87,6 +87,20 @@ FurnaceGUIImage* FurnaceGUI::getImage(FurnaceGUIImages image) {
logV("%dx%d",ret->width,ret->height);
#ifdef TA_BIG_ENDIAN
if (ret->ch==4) {
size_t total=ret->width*ret->height*ret->ch;
for (size_t i=0; i<total; i+=4) {
ret->data[i]^=ret->data[i|3];
ret->data[i|3]^=ret->data[i];
ret->data[i]^=ret->data[i|3];
ret->data[i|1]^=ret->data[i|2];
ret->data[i|2]^=ret->data[i|1];
ret->data[i|1]^=ret->data[i|2];
}
}
#endif
images[image]=ret;
}

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2023 tildearrow and contributors
* Copyright (C) 2021-2024 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2023 tildearrow and contributors
* Copyright (C) 2021-2024 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -22,10 +22,13 @@
const int _ZERO=0;
const int _ONE=1;
const int _THREE=3;
const int _FOUR=4;
const int _SEVEN=7;
const int _EIGHT=8;
const int _TEN=10;
const int _FIFTEEN=15;
const int _SIXTEEN=16;
const int _TWENTY_FOUR=24;
const int _THIRTY_ONE=31;
const int _SIXTY_FOUR=64;
const int _ONE_HUNDRED=100;
@ -35,5 +38,6 @@ const int _FIVE_HUNDRED_ELEVEN=511;
const int _TWO_THOUSAND_FORTY_SEVEN=2047;
const int _FOUR_THOUSAND_NINETY_FIVE=4095;
const int _SIXTY_FIVE_THOUSAND_FIVE_HUNDRED_THIRTY_FIVE=65535;
const int _MINUS_TWENTY_FOUR=-24;
const int _MINUS_ONE_HUNDRED_TWENTY_SEVEN=-127;
const int _MINUS_ONE_HUNDRED_TWENTY_EIGHT=-128;

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2023 tildearrow and contributors
* Copyright (C) 2021-2024 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -24,10 +24,13 @@
extern const int _ZERO;
extern const int _ONE;
extern const int _THREE;
extern const int _FOUR;
extern const int _SEVEN;
extern const int _EIGHT;
extern const int _TEN;
extern const int _FIFTEEN;
extern const int _SIXTEEN;
extern const int _TWENTY_FOUR;
extern const int _THIRTY_ONE;
extern const int _SIXTY_FOUR;
extern const int _ONE_HUNDRED;
@ -37,5 +40,6 @@ extern const int _FIVE_HUNDRED_ELEVEN;
extern const int _TWO_THOUSAND_FORTY_SEVEN;
extern const int _FOUR_THOUSAND_NINETY_FIVE;
extern const int _SIXTY_FIVE_THOUSAND_FIVE_HUNDRED_THIRTY_FIVE;
extern const int _MINUS_TWENTY_FOUR;
extern const int _MINUS_ONE_HUNDRED_TWENTY_SEVEN;
extern const int _MINUS_ONE_HUNDRED_TWENTY_EIGHT;

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2023 tildearrow and contributors
* Copyright (C) 2021-2024 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -63,10 +63,13 @@ void FurnaceGUI::drawImage(ImDrawList* dl, FurnaceGUIImages image, const ImVec2&
posAbs.y+rectMin.x*sin(rotate)+rectMax.y*cos(rotate)
);
ImVec2 uv0=ImVec2(uvMin.x,uvMin.y);
ImVec2 uv1=ImVec2(uvMax.x,uvMin.y);
ImVec2 uv2=ImVec2(uvMax.x,uvMax.y);
ImVec2 uv3=ImVec2(uvMin.x,uvMax.y);
float uScale=rend->getTextureU(img);
float vScale=rend->getTextureV(img);
ImVec2 uv0=ImVec2(uvMin.x*uScale,uvMin.y*vScale);
ImVec2 uv1=ImVec2(uvMax.x*uScale,uvMin.y*vScale);
ImVec2 uv2=ImVec2(uvMax.x*uScale,uvMax.y*vScale);
ImVec2 uv3=ImVec2(uvMin.x*uScale,uvMax.y*vScale);
ImU32 colorConverted=ImGui::GetColorU32(imgColor);
@ -104,10 +107,7 @@ void FurnaceGUI::endIntroTune() {
void FurnaceGUI::drawIntro(double introTime, bool monitor) {
if (monitor) {
if (introTime<0.0) introTime=0.0;
if (introTime>11.0) introTime=11.0;
if (!introMonOpen) return;
if (introPos<(shortIntro?1.0:11.0)) return;
return;
}
if (introPos<(shortIntro?1.0:11.0) || monitor) {
if (!monitor) {
@ -117,7 +117,7 @@ void FurnaceGUI::drawIntro(double introTime, bool monitor) {
ImGui::SetNextWindowSize(ImVec2(canvasW,canvasH));
if (introPos<0.1) ImGui::SetNextWindowFocus();
}
if (ImGui::Begin(monitor?"IntroMon X":"Intro",monitor?(&introMonOpen):NULL,monitor?globalWinFlags:(ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoDocking|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoBackground))) {
if (ImGui::Begin(monitor?"IntroMon X":"Intro",NULL,monitor?globalWinFlags:(ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoDocking|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoBackground))) {
if (monitor) {
if (ImGui::Button("Preview")) {
introPos=0;

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2023 tildearrow and contributors
* Copyright (C) 2021-2024 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2023 tildearrow and contributors
* Copyright (C) 2021-2024 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2023 tildearrow and contributors
* Copyright (C) 2021-2024 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -17,4 +17,4 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
double getMacDPIScale();
double getMacDPIScale(void* sysWin, unsigned char isUIKit);

View file

@ -20,7 +20,30 @@
#include <Cocoa/Cocoa.h>
#include "macstuff.h"
double getMacDPIScale() {
CGFloat val=[[NSScreen mainScreen] backingScaleFactor];
double getMacDPIScale(void* sysWin, unsigned char isUIKit) {
NSScreen* screen=nil;
if (sysWin!=NULL) {
if (isUIKit) {
return 1.0;
/*
UIWindow* win=(UIWindow*)sysWin;
UIWindowScene* winScene=[win windowScene];
if (winScene!=nil) {
UIScreen* winScreen=[winScene screen];
CGFloat ret=[winScreen scale];
return (double)ret;
}*/
} else {
NSWindow* win=(NSWindow*)sysWin;
screen=[win screen];
}
}
if (screen==nil) {
screen=[NSScreen mainScreen];
}
if (screen==nil) {
return 1.0;
}
CGFloat val=[screen backingScaleFactor];
return (double)val;
}

181
src/gui/memory.cpp Normal file
View file

@ -0,0 +1,181 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2024 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "gui.h"
#include <fmt/printf.h>
#include "imgui.h"
#include "imgui_internal.h"
#define CENTER_TEXT(text) \
ImGui::SetCursorPosX(ImGui::GetCursorPosX()+0.5*(ImGui::GetContentRegionAvail().x-ImGui::CalcTextSize(text).x));
void FurnaceGUI::drawMemory() {
if (nextWindow==GUI_WINDOW_MEMORY) {
memoryOpen=true;
ImGui::SetNextWindowFocus();
nextWindow=GUI_WINDOW_NOTHING;
}
if (!memoryOpen) return;
if (ImGui::Begin("Memory Composition",&memoryOpen,globalWinFlags)) {
ImDrawList* dl=ImGui::GetWindowDrawList();
ImGuiWindow* window=ImGui::GetCurrentWindow();
char tempID[1024];
bool have=false;
for (int i=0; i<e->song.systemLen; i++) {
DivDispatch* dispatch=e->getDispatch(i);
for (int j=0; j<4; j++) {
const DivMemoryComposition* mc=dispatch->getMemCompo(j);
if (mc==NULL) break;
have=true;
ImGui::Text("%s: %s",e->getSystemName(e->song.system[i]),mc->name.c_str());
ImGui::SameLine();
if (mc->capacity>=1024 && settings.memUsageUnit==1) {
ImGui::Text("%dK/%dK (%.1f%%)",(int)mc->used>>10,(int)mc->capacity>>10,100.0*(double)mc->used/(double)mc->capacity);
} else {
ImGui::Text("%d/%d (%.1f%%)",(int)mc->used,(int)mc->capacity,100.0*(double)mc->used/(double)mc->capacity);
}
ImVec2 size=ImVec2(ImGui::GetContentRegionAvail().x,36.0f*dpiScale);
ImVec2 minArea=window->DC.CursorPos;
ImVec2 maxArea=ImVec2(
minArea.x+size.x,
minArea.y+size.y
);
ImRect rect=ImRect(minArea,maxArea);
ImRect dataRect=rect;
ImRect entryRect=rect;
ImGuiStyle& style=ImGui::GetStyle();
ImGui::ItemSize(size,style.FramePadding.y);
snprintf(tempID,1023,"MC%d_%d",i,j);
if (ImGui::ItemAdd(rect,ImGui::GetID(tempID))) {
dl->AddRectFilled(rect.Min,rect.Max,ImGui::GetColorU32(uiColors[GUI_COLOR_MEMORY_BG]));
if (mc->memory!=NULL && mc->waveformView!=DIV_MEMORY_WAVE_NONE) {
dataRect.Max.y-=8.0f*dpiScale;
entryRect.Min.y=entryRect.Max.y-8.0f*dpiScale;
}
int curHover=-1;
int kIndex=0;
if (mc->capacity>0) for (const DivMemoryEntry& k: mc->entries) {
if (k.begin==k.end) {
kIndex++;
continue;
}
ImVec2 pos1=ImLerp(entryRect.Min,entryRect.Max,ImVec2((double)k.begin/(double)mc->capacity,(k.type==DIV_MEMORY_N163_LOAD)?0.5f:0.0f));
ImVec2 pos2=ImLerp(entryRect.Min,entryRect.Max,ImVec2((double)k.end/(double)mc->capacity,(k.type==DIV_MEMORY_N163_PLAY)?0.5f:1.0f));
ImVec2 linePos=pos1;
linePos.y=rect.Max.y;
dl->AddRectFilled(pos1,pos2,ImGui::GetColorU32(uiColors[GUI_COLOR_MEMORY_FREE+(int)k.type]));
dl->AddLine(pos1,linePos,ImGui::GetColorU32(ImGuiCol_Border),dpiScale);
if (ImGui::GetMousePos().x>=pos1.x &&
ImGui::GetMousePos().x<=pos2.x &&
ImGui::GetMousePos().y>=pos1.y &&
ImGui::GetMousePos().y<=pos2.y) {
curHover=kIndex;
}
kIndex++;
}
if (mc->memory!=NULL) {
switch (mc->waveformView) {
case DIV_MEMORY_WAVE_4BIT:
for (int k=0; k<(int)(mc->capacity<<1); k++) {
unsigned char nibble=mc->memory[k>>1];
if (k&1) {
nibble>>=4;
} else {
nibble&=15;
}
ImVec2 pos1=ImLerp(dataRect.Min,dataRect.Max,ImVec2((double)k/(double)(mc->capacity<<1),1.0f-((float)(nibble+1)/16.0f)));
ImVec2 pos2=ImLerp(dataRect.Min,dataRect.Max,ImVec2((double)(k+1)/(double)(mc->capacity<<1),1.0f));
dl->AddRectFilled(pos1,pos2,ImGui::GetColorU32(uiColors[GUI_COLOR_MEMORY_DATA]));
}
break;
case DIV_MEMORY_WAVE_8BIT_SIGNED:
for (int k=0; k<(int)mc->capacity; k++) {
signed char val=(signed char)mc->memory[k];
ImVec2 pos1=ImLerp(dataRect.Min,dataRect.Max,ImVec2((double)k/(double)(mc->capacity),1.0f-((float)(val+129)/256.0f)));
ImVec2 pos2=ImLerp(dataRect.Min,dataRect.Max,ImVec2((double)(k+1)/(double)(mc->capacity),1.0f));
dl->AddRectFilled(pos1,pos2,ImGui::GetColorU32(uiColors[GUI_COLOR_MEMORY_DATA]));
}
break;
default:
break;
}
}
if (ImGui::ItemHoverable(rect,ImGui::GetID(tempID),0)) {
if (curHover>=0 && curHover<(int)mc->entries.size()) {
const DivMemoryEntry& entry=mc->entries[curHover];
if (ImGui::BeginTooltip()) {
switch (entry.type) {
case DIV_MEMORY_SAMPLE:
case DIV_MEMORY_BANK0:
case DIV_MEMORY_BANK1:
case DIV_MEMORY_BANK2:
case DIV_MEMORY_BANK3:
case DIV_MEMORY_BANK4:
case DIV_MEMORY_BANK5:
case DIV_MEMORY_BANK6:
case DIV_MEMORY_BANK7: {
DivSample* sample=e->getSample(entry.asset);
ImGui::Text("%d: %s",curHover,sample->name.c_str());
if ((int)entry.type>=(int)DIV_MEMORY_BANK0) {
ImGui::Text("bank %d",(int)entry.type-(int)DIV_MEMORY_BANK0);
}
if ((entry.end-entry.begin)>=1024 && settings.memUsageUnit==1) {
ImGui::Text("%d-%d ($%x-$%x): %dK ($%x)",(int)entry.begin,(int)entry.end-1,(int)entry.begin,(int)entry.end-1,(int)(entry.end-entry.begin)>>10,(int)(entry.end-entry.begin));
} else {
ImGui::Text("%d-%d ($%x-$%x): %d bytes ($%x)",(int)entry.begin,(int)entry.end-1,(int)entry.begin,(int)entry.end-1,(int)(entry.end-entry.begin),(int)(entry.end-entry.begin));
}
break;
}
default:
ImGui::Text("%d: %s",curHover,entry.name.c_str());
if ((entry.end-entry.begin)>=1024 && settings.memUsageUnit==1) {
ImGui::Text("%d-%d ($%x-$%x): %dK ($%x)",(int)entry.begin,(int)entry.end-1,(int)entry.begin,(int)entry.end-1,(int)(entry.end-entry.begin)>>10,(int)(entry.end-entry.begin));
} else {
ImGui::Text("%d-%d ($%x-$%x): %d bytes ($%x)",(int)entry.begin,(int)entry.end-1,(int)entry.begin,(int)entry.end-1,(int)(entry.end-entry.begin),(int)(entry.end-entry.begin));
}
break;
}
ImGui::EndTooltip();
}
}
}
}
}
}
if (!have) {
ImGui::SetCursorPosY(ImGui::GetCursorPosY()+(ImGui::GetContentRegionAvail().y-ImGui::GetFrameHeight()+ImGui::GetStyle().ItemSpacing.y)*0.5f);
CENTER_TEXT("no chips with memory");
ImGui::Text("no chips with memory");
}
}
if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_MEMORY;
ImGui::End();
}

View file

@ -139,6 +139,7 @@ bool MIDIMap::read(String path) {
UNDERSTAND_OPTION(midiClock) else
UNDERSTAND_OPTION(midiTimeCode) else
UNDERSTAND_OPTION(yamahaFMResponse) else
UNDERSTAND_OPTION(directProgram) else
UNDERSTAND_OPTION(valueInputStyle) else
UNDERSTAND_OPTION(valueInputControlMSB) else
UNDERSTAND_OPTION(valueInputControlLSB) else
@ -205,6 +206,7 @@ bool MIDIMap::write(String path) {
WRITE_OPTION(midiClock);
WRITE_OPTION(midiTimeCode);
WRITE_OPTION(yamahaFMResponse);
WRITE_OPTION(directProgram);
WRITE_OPTION(valueInputStyle);
WRITE_OPTION(valueInputControlMSB);
WRITE_OPTION(valueInputControlLSB);

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2023 tildearrow and contributors
* Copyright (C) 2021-2024 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -105,7 +105,7 @@ bool FurnaceGUI::portSet(String label, unsigned int portSetID, int ins, int outs
bool hovered=false;
bool active=false;
if (visible) {
hovered=ImGui::ItemHoverable(rect,ImGui::GetID(portID.c_str()));
hovered=ImGui::ItemHoverable(rect,ImGui::GetID(portID.c_str()),0);
active=(hovered && ImGui::IsMouseClicked(ImGuiMouseButton_Left));
if (hovered && ImGui::IsMouseClicked(ImGuiMouseButton_Right)) {
@ -244,6 +244,7 @@ void FurnaceGUI::drawMixer() {
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::AlignTextToFramePadding();
ImGui::Text("%d. %s",i+1,getSystemName(e->song.system[i]));
ImGui::TableNextColumn();
if (ImGui::Checkbox("Invert",&doInvert)) {
@ -295,7 +296,7 @@ void FurnaceGUI::drawMixer() {
}
ImGui::EndTabItem();
}
if (!basicMode) if (ImGui::BeginTabItem("Patchbay")) {
if (ImGui::BeginTabItem("Patchbay")) {
std::map<unsigned int,ImVec2> portPos;
if (ImGui::BeginTable("PatchbayOptions",3)) {
@ -337,9 +338,11 @@ void FurnaceGUI::drawMixer() {
if (selectedSubPort>=0) {
portDragActive=true;
ImGui::InhibitInertialScroll();
try {
subPortPos=portPos.at((selectedPortSet<<4)|selectedSubPort);
} catch (std::out_of_range& e) {
auto subPortI=portPos.find((selectedPortSet<<4)|selectedSubPort);
if (subPortI!=portPos.cend()) {
subPortPos=subPortI->second;
} else {
portDragActive=false;
}
}
@ -353,9 +356,10 @@ void FurnaceGUI::drawMixer() {
if (selectedSubPort>=0) {
portDragActive=true;
ImGui::InhibitInertialScroll();
try {
subPortPos=portPos.at((selectedPortSet<<4)|selectedSubPort);
} catch (std::out_of_range& e) {
auto subPortI=portPos.find((selectedPortSet<<4)|selectedSubPort);
if (subPortI!=portPos.cend()) {
subPortPos=subPortI->second;
} else {
portDragActive=false;
}
}
@ -365,9 +369,10 @@ void FurnaceGUI::drawMixer() {
if (selectedSubPort>=0) {
portDragActive=true;
ImGui::InhibitInertialScroll();
try {
subPortPos=portPos.at((selectedPortSet<<4)|selectedSubPort);
} catch (std::out_of_range& e) {
auto subPortI=portPos.find((selectedPortSet<<4)|selectedSubPort);
if (subPortI!=portPos.cend()) {
subPortPos=subPortI->second;
} else {
portDragActive=false;
}
}
@ -380,9 +385,10 @@ void FurnaceGUI::drawMixer() {
if (selectedSubPort>=0) {
portDragActive=true;
ImGui::InhibitInertialScroll();
try {
subPortPos=portPos.at((selectedPortSet<<4)|selectedSubPort);
} catch (std::out_of_range& e) {
auto subPortI=portPos.find((selectedPortSet<<4)|selectedSubPort);
if (subPortI!=portPos.cend()) {
subPortPos=subPortI->second;
} else {
portDragActive=false;
}
}
@ -415,22 +421,24 @@ void FurnaceGUI::drawMixer() {
// draw connections
for (unsigned int i: e->song.patchbay) {
if ((i>>20)==selectedPortSet) continue;
try {
ImVec2 portSrc=portPos.at(i>>16);
ImVec2 portDest=portPos.at(0x10000|(i&0xffff));
auto portSrcI=portPos.find(i>>16);
auto portDestI=portPos.find(0x10000|(i&0xffff));
if (portSrcI!=portPos.cend() && portDestI!=portPos.cend()) {
ImVec2 portSrc=portSrcI->second;
ImVec2 portDest=portDestI->second;
dl->AddLine(portSrc,portDest,ImGui::GetColorU32(uiColors[GUI_COLOR_PATCHBAY_CONNECTION_BG]),2.0f*dpiScale);
} catch (std::out_of_range& e) {
}
}
// foreground
for (unsigned int i: e->song.patchbay) {
if ((i>>20)!=selectedPortSet) continue;
try {
ImVec2 portSrc=portPos.at(i>>16);
ImVec2 portDest=portPos.at(0x10000|(i&0xffff));
auto portSrcI=portPos.find(i>>16);
auto portDestI=portPos.find(0x10000|(i&0xffff));
if (portSrcI!=portPos.cend() && portDestI!=portPos.cend()) {
ImVec2 portSrc=portSrcI->second;
ImVec2 portDest=portDestI->second;
dl->AddLine(portSrc,portDest,ImGui::GetColorU32(uiColors[GUI_COLOR_PATCHBAY_CONNECTION]),2.0f*dpiScale);
} catch (std::out_of_range& e) {
}
}
}

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2023 tildearrow and contributors
* Copyright (C) 2021-2024 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -22,8 +22,101 @@
#include <fmt/printf.h>
#include <algorithm>
String sysDefID;
void FurnaceGUI::drawSysDefs(std::vector<FurnaceGUISysDef>& category, bool& accepted, std::vector<int>& sysDefStack, bool& alreadyHover) {
int index=0;
String sysDefIDLeader="##NS";
for (int i: sysDefStack) {
sysDefIDLeader+=fmt::sprintf("/%d",i);
}
for (FurnaceGUISysDef& i: category) {
bool treeNode=false;
bool isHovered=false;
ImGui::TableNextRow();
ImGui::TableNextColumn();
if (!i.subDefs.empty()) {
if (i.orig.empty()) {
sysDefID=fmt::sprintf("%s%s/%dS",i.name,sysDefIDLeader,index);
} else {
sysDefID=fmt::sprintf("%s/%dS",sysDefIDLeader,index);
}
treeNode=ImGui::TreeNodeEx(sysDefID.c_str(),i.orig.empty()?ImGuiTreeNodeFlags_SpanAvailWidth:0);
ImGui::SameLine();
}
if (!i.orig.empty()) {
sysDefID=fmt::sprintf("%s%s/%d",i.name,sysDefIDLeader,index);
if (ImGui::Selectable(sysDefID.c_str(),false,ImGuiSelectableFlags_DontClosePopups)) {
nextDesc=i.definition;
nextDescName=i.name;
accepted=true;
}
if (ImGui::IsItemHovered()) isHovered=true;
} else if (i.subDefs.empty()) {
ImGui::TextUnformatted(i.name.c_str());
if (ImGui::IsItemHovered()) isHovered=true;
}
if (treeNode) {
sysDefStack.push_back(index);
drawSysDefs(i.subDefs,accepted,sysDefStack,alreadyHover);
sysDefStack.erase(sysDefStack.end()-1);
ImGui::TreePop();
}
if (isHovered && !alreadyHover) {
alreadyHover=true;
if (ImGui::BeginTooltip()) {
std::map<DivSystem,int> chipCounts;
std::vector<DivSystem> chips;
for (FurnaceGUISysDefChip chip: i.orig) {
if (chipCounts.find(chip.sys)==chipCounts.end()) {
chipCounts[chip.sys]=1;
chips.push_back(chip.sys);
} else {
chipCounts[chip.sys]+=1;
}
}
for (size_t chipIndex=0; chipIndex<chips.size(); chipIndex++) {
DivSystem chip=chips[chipIndex];
const DivSysDef* sysDef=e->getSystemDef(chip);
ImGui::PushTextWrapPos(MIN(scrW*dpiScale,400.0f*dpiScale));
ImGui::Text("%s (x%d): ",sysDef->name,chipCounts[chip]);
ImGui::Text("%s",sysDef->description);
ImGui::PopTextWrapPos();
if (chipIndex+1<chips.size()) {
ImGui::Separator();
}
}
ImGui::EndTooltip();
}
}
index++;
}
}
void findInSubs(std::vector<FurnaceGUISysDef>& where, std::vector<FurnaceGUISysDef>& newSongSearchResults, String lowerCase) {
for (FurnaceGUISysDef& j: where) {
if (!j.orig.empty()) {
String lowerCase1=j.name;
for (char& i: lowerCase1) {
if (i>='A' && i<='Z') i+='a'-'A';
}
auto lastItem=std::remove_if(lowerCase1.begin(),lowerCase1.end(),[](char c) {
return (c==' ' || c=='_' || c=='-');
});
lowerCase1.erase(lastItem,lowerCase1.end());
if (lowerCase1.find(lowerCase)!=String::npos) {
newSongSearchResults.push_back(j);
newSongSearchResults[newSongSearchResults.size()-1].subDefs.clear();
}
}
findInSubs(j.subDefs,newSongSearchResults,lowerCase);
}
}
void FurnaceGUI::drawNewSong() {
bool accepted=false;
std::vector<int> sysDefStack;
ImGui::PushFont(bigFont);
ImGui::SetCursorPosX((ImGui::GetContentRegionAvail().x-ImGui::CalcTextSize("Choose a System!").x)*0.5);
@ -49,26 +142,30 @@ void FurnaceGUI::drawNewSong() {
newSongSearchResults.clear();
for (FurnaceGUISysCategory& i: sysCategories) {
for (FurnaceGUISysDef& j: i.systems) {
String lowerCase1=j.name;
for (char& i: lowerCase1) {
if (i>='A' && i<='Z') i+='a'-'A';
}
auto lastItem=std::remove_if(lowerCase1.begin(),lowerCase1.end(),[](char c) {
return (c==' ' || c=='_' || c=='-');
});
lowerCase1.erase(lastItem,lowerCase1.end());
if (lowerCase1.find(lowerCase)!=String::npos) {
newSongSearchResults.push_back(j);
if (!j.orig.empty()) {
String lowerCase1=j.name;
for (char& i: lowerCase1) {
if (i>='A' && i<='Z') i+='a'-'A';
}
auto lastItem=std::remove_if(lowerCase1.begin(),lowerCase1.end(),[](char c) {
return (c==' ' || c=='_' || c=='-');
});
lowerCase1.erase(lastItem,lowerCase1.end());
if (lowerCase1.find(lowerCase)!=String::npos) {
newSongSearchResults.push_back(j);
newSongSearchResults[newSongSearchResults.size()-1].subDefs.clear();
}
}
findInSubs(j.subDefs,newSongSearchResults,lowerCase);
}
std::sort(newSongSearchResults.begin(),newSongSearchResults.end(),[](const FurnaceGUISysDef& a, const FurnaceGUISysDef& b) {
return strcmp(a.name,b.name)<0;
});
auto lastItem=std::unique(newSongSearchResults.begin(),newSongSearchResults.end(),[](const FurnaceGUISysDef& a, const FurnaceGUISysDef& b) {
return strcmp(a.name,b.name)==0;
});
newSongSearchResults.erase(lastItem,newSongSearchResults.end());
}
std::sort(newSongSearchResults.begin(),newSongSearchResults.end(),[](const FurnaceGUISysDef& a, const FurnaceGUISysDef& b) {
return strcmp(a.name.c_str(),b.name.c_str())<0;
});
auto lastItem1=std::unique(newSongSearchResults.begin(),newSongSearchResults.end(),[](const FurnaceGUISysDef& a, const FurnaceGUISysDef& b) {
return a.name==b.name;
});
newSongSearchResults.erase(lastItem1,newSongSearchResults.end());
}
if (ImGui::BeginTable("sysPicker",newSongQuery.empty()?2:1,ImGuiTableFlags_BordersInnerV)) {
if (newSongQuery.empty()) {
@ -91,12 +188,13 @@ void FurnaceGUI::drawNewSong() {
ImGui::TableNextColumn();
int index=0;
for (FurnaceGUISysCategory& i: sysCategories) {
if (ImGui::Selectable(i.name,newSongCategory==index,ImGuiSelectableFlags_DontClosePopups)) { \
if (ImGui::Selectable(i.name,newSongCategory==index,ImGuiSelectableFlags_DontClosePopups)) {
newSongCategory=index;
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("%s",i.description);
}
if (strcmp(i.name,"User")==0) ImGui::Separator();
index++;
}
}
@ -105,14 +203,19 @@ void FurnaceGUI::drawNewSong() {
ImGui::TableNextColumn();
if (ImGui::BeginTable("Systems",1,ImGuiTableFlags_BordersInnerV|ImGuiTableFlags_ScrollY)) {
std::vector<FurnaceGUISysDef>& category=(newSongQuery.empty())?(sysCategories[newSongCategory].systems):(newSongSearchResults);
for (FurnaceGUISysDef& i: category) {
if (category.empty()) {
ImGui::TableNextRow();
ImGui::TableNextColumn();
if (ImGui::Selectable(i.name,false,ImGuiSelectableFlags_DontClosePopups)) {
nextDesc=i.definition;
nextDescName=i.name;
accepted=true;
if (newSongQuery.empty()) {
ImGui::Text("no systems here yet!");
} else {
ImGui::Text("no results");
}
} else {
bool alreadyHover=false;
sysDefStack.push_back(newSongQuery.empty()?newSongCategory:-1);
drawSysDefs(category,accepted,sysDefStack,alreadyHover);
sysDefStack.erase(sysDefStack.end()-1);
}
ImGui::EndTable();
}
@ -124,23 +227,50 @@ void FurnaceGUI::drawNewSong() {
if (ImGui::Button("I'm feeling lucky")) {
if (sysCategories.size()==0) {
showError("no categories available! what in the world.");
ImGui::CloseCurrentPopup();
} else {
FurnaceGUISysCategory* newSystemCat=&sysCategories[rand()%sysCategories.size()];
if (newSystemCat->systems.size()==0) {
int tries=0;
for (tries=0; tries<50; tries++) {
FurnaceGUISysCategory* newSystemCat=&sysCategories[rand()%sysCategories.size()];
if (newSystemCat->systems.empty()) {
continue;
} else {
unsigned int selection=rand()%newSystemCat->systems.size();
if (newSystemCat->systems[selection].orig.empty() && newSystemCat->systems[selection].subDefs.empty()) continue;
if (!newSystemCat->systems[selection].subDefs.empty()) {
if (rand()%2) {
unsigned int subSel=rand()%newSystemCat->systems[selection].subDefs.size();
nextDesc=newSystemCat->systems[selection].subDefs[subSel].definition;
nextDescName=newSystemCat->systems[selection].subDefs[subSel].name;
accepted=true;
} else {
if (newSystemCat->systems[selection].orig.empty()) continue;
nextDesc=newSystemCat->systems[selection].definition;
nextDescName=newSystemCat->systems[selection].name;
accepted=true;
}
} else {
nextDesc=newSystemCat->systems[selection].definition;
nextDescName=newSystemCat->systems[selection].name;
accepted=true;
}
}
if (accepted) break;
}
if (tries>=50) {
showError("it appears you're extremely lucky today!");
ImGui::CloseCurrentPopup();
} else {
unsigned int selection=rand()%newSystemCat->systems.size();
nextDesc=newSystemCat->systems[selection].definition;
nextDescName=newSystemCat->systems[selection].name;
accepted=true;
}
}
}
ImGui::SameLine();
if (ImGui::Button("Cancel")) {
if (ImGui::Button("Cancel") || ImGui::IsKeyPressed(ImGuiKey_Escape)) {
ImGui::CloseCurrentPopup();
}

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2023 tildearrow and contributors
* Copyright (C) 2021-2024 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -102,7 +102,7 @@ void FurnaceGUI::drawOrderButtons() {
int buttonColumns=(settings.orderButtonPos==0)?8:1;
int buttonColumn=0;
while (buttonColumns<8 && ((8/buttonColumns)*ImGui::GetFrameHeightWithSpacing())>ImGui::GetContentRegionAvail().y) {
while (buttonColumns<8 && ((int)(8/buttonColumns)*ImGui::GetFrameHeightWithSpacing())>ImGui::GetContentRegionAvail().y) {
buttonColumns++;
}
@ -115,10 +115,12 @@ void FurnaceGUI::drawOrderButtons() {
}
NEXT_BUTTON;
pushDestColor();
if (ImGui::Button(ICON_FA_MINUS)) { handleUnimportant
// remove this order row
doAction(GUI_ACTION_ORDERS_REMOVE);
}
popDestColor();
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Remove order");
}
@ -162,7 +164,7 @@ void FurnaceGUI::drawOrderButtons() {
doAction(GUI_ACTION_ORDERS_DEEP_CLONE_END);
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Duplicate order at end of song (right-click to deep clone)");
ImGui::SetTooltip("Place copy of current order at end of song (right-click to deep clone)");
}
NEXT_BUTTON;
@ -179,6 +181,10 @@ void FurnaceGUI::drawOrderButtons() {
}
NEXT_BUTTON;
if (orderEditMode==0 && mobileUI) {
orderEditMode=1;
}
const char* orderEditModeLabel="?##OrderEditMode";
if (orderEditMode==3) {
orderEditModeLabel=ICON_FA_ARROWS_V "##OrderEditMode";
@ -191,7 +197,7 @@ void FurnaceGUI::drawOrderButtons() {
}
if (ImGui::Button(orderEditModeLabel)) { handleUnimportant
orderEditMode++;
if (orderEditMode>3) orderEditMode=0;
if (orderEditMode>3) orderEditMode=mobileUI?1:0;
curNibble=false;
}
if (ImGui::IsItemHovered()) {
@ -217,7 +223,7 @@ void FurnaceGUI::drawOrders() {
if (!ordersOpen) return;
if (mobileUI) {
patWindowPos=(portrait?ImVec2(0.0f,(mobileMenuPos*-0.65*canvasH)):ImVec2((0.16*canvasH)+0.5*canvasW*mobileMenuPos,0.0f));
patWindowSize=(portrait?ImVec2(canvasW,canvasH-(0.16*canvasW)):ImVec2(canvasW-(0.16*canvasH),canvasH));
patWindowSize=(portrait?ImVec2(canvasW,canvasH-(0.16*canvasW)-(pianoOpen?(0.4*canvasW):0.0f)):ImVec2(canvasW-(0.16*canvasH),canvasH-(pianoOpen?(0.3*canvasH):0.0f)));
ImGui::SetNextWindowPos(patWindowPos);
ImGui::SetNextWindowSize(patWindowSize);
} else {
@ -257,17 +263,18 @@ void FurnaceGUI::drawOrders() {
}
ImGui::PushFont(patFont);
bool tooSmall=((displayChans+1)>((ImGui::GetContentRegionAvail().x)/(ImGui::CalcTextSize("AA").x+2.0*ImGui::GetStyle().ItemInnerSpacing.x)));
ImGui::PopFont();
float yHeight=ImGui::GetContentRegionAvail().y;
float lineHeight=(ImGui::GetTextLineHeight()+4*dpiScale);
if (e->isPlaying()) {
if (followOrders) {
float nextOrdScroll=(playOrder+1)*lineHeight-((yHeight-(tooSmall?ImGui::GetStyle().ScrollbarSize:0.0f))/2.0f);
if (nextOrdScroll<0.0f) nextOrdScroll=0.0f;
ImGui::SetNextWindowScroll(ImVec2(-1.0f,nextOrdScroll));
}
}
if (ImGui::BeginTable("OrdersTable",1+displayChans,(tooSmall?ImGuiTableFlags_SizingFixedFit:ImGuiTableFlags_SizingStretchSame)|ImGuiTableFlags_ScrollX|ImGuiTableFlags_ScrollY)) {
ImGui::PushFont(patFont);
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing,prevSpacing);
ImGui::TableSetupScrollFreeze(1,1);
float lineHeight=(ImGui::GetTextLineHeight()+4*dpiScale);
if (e->isPlaying()) {
if (followOrders) {
ImGui::SetScrollY((e->getOrder()+1)*lineHeight-(ImGui::GetContentRegionAvail().y/2));
}
}
ImGui::TableNextRow(0,lineHeight);
ImVec2 ra=ImGui::GetContentRegionAvail();
ImGui::TableNextColumn();
@ -280,9 +287,9 @@ void FurnaceGUI::drawOrders() {
ImGui::PopStyleColor();
for (int i=0; i<e->curSubSong->ordersLen; i++) {
ImGui::TableNextRow(0,lineHeight);
if (oldOrder1==i) ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0,ImGui::GetColorU32(uiColors[GUI_COLOR_ORDER_ACTIVE]));
if (playOrder==i && e->isPlaying()) ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0,ImGui::GetColorU32(uiColors[GUI_COLOR_ORDER_ACTIVE]));
ImGui::TableNextColumn();
if ((!followPattern && curOrder==i) || (followPattern && oldOrder1==i)) {
if (curOrder==i) {
// draw a border
ImDrawList* dl=ImGui::GetWindowDrawList();
ImVec2 rBegin=ImGui::GetCursorScreenPos();
@ -319,7 +326,7 @@ void FurnaceGUI::drawOrders() {
//}
ImGui::PushStyleColor(ImGuiCol_Text,(curOrder==i || e->curOrders->ord[j][i]==e->curOrders->ord[j][curOrder])?uiColors[GUI_COLOR_ORDER_SIMILAR]:uiColors[GUI_COLOR_ORDER_INACTIVE]);
if (ImGui::Selectable(selID,settings.ordersCursor?(cursor.xCoarse==j && oldOrder1!=i):false)) {
if (ImGui::Selectable(selID,settings.ordersCursor?(cursor.xCoarse==j && curOrder!=i):false)) {
if (curOrder==i) {
if (orderEditMode==0) {
prepareUndo(GUI_UNDO_CHANGE_ORDER);
@ -391,9 +398,9 @@ void FurnaceGUI::drawOrders() {
}
}
ImGui::PopStyleVar();
ImGui::PopFont();
ImGui::EndTable();
}
ImGui::PopFont();
if (settings.orderButtonPos==2) {
ImGui::TableNextColumn();
@ -408,6 +415,5 @@ void FurnaceGUI::drawOrders() {
}
}
if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_ORDERS;
oldOrder1=e->getOrder();
ImGui::End();
}

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2023 tildearrow and contributors
* Copyright (C) 2021-2024 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -20,6 +20,8 @@
#include "gui.h"
#include "imgui_internal.h"
#include <imgui.h>
#include "../ta-log.h"
#include "../engine/filter.h"
void FurnaceGUI::readOsc() {
int writePos=e->oscWritePos;
@ -47,19 +49,74 @@ void FurnaceGUI::readOsc() {
int winSize=e->getAudioDescGot().rate*(oscWindowSize/1000.0);
int oscReadPos=(writePos-winSize)&0x7fff;
for (int i=0; i<512; i++) {
int pos=(oscReadPos+(i*winSize/512))&0x7fff;
oscValues[i]=0;
for (int j=0; j<e->getAudioDescGot().outChans; j++) {
oscValues[i]+=e->oscBuf[j][pos];
}
oscValues[i]/=e->getAudioDescGot().outChans;
if (oscValues[i]>0.001f || oscValues[i]<-0.001f) {
WAKE_UP;
for (int ch=0; ch<e->getAudioDescGot().outChans; ch++) {
if (oscValues[ch]==NULL) {
oscValues[ch]=new float[2048];
}
memset(oscValues[ch],0,2048*sizeof(float));
float* sincITable=DivFilterTables::getSincIntegralSmallTable();
float posFrac=0.0;
float factor=(float)oscWidth/(float)winSize;
int posInt=oscReadPos-(8.0f/factor);
for (int i=7; i<oscWidth-9; i++) {
oscValues[ch][i]+=e->oscBuf[ch][posInt&0x7fff];
posFrac+=1.0;
while (posFrac>=1.0) {
unsigned int n=((unsigned int)(posFrac*64.0))&63;
posFrac-=factor;
posInt++;
float* t1=&sincITable[(63-n)<<3];
float* t2=&sincITable[n<<3];
float delta=e->oscBuf[ch][posInt&0x7fff]-e->oscBuf[ch][(posInt-1)&0x7fff];
oscValues[ch][i-7]+=t1[7]*-delta;
oscValues[ch][i-6]+=t1[6]*-delta;
oscValues[ch][i-5]+=t1[5]*-delta;
oscValues[ch][i-4]+=t1[4]*-delta;
oscValues[ch][i-3]+=t1[3]*-delta;
oscValues[ch][i-2]+=t1[2]*-delta;
oscValues[ch][i-1]+=t1[1]*-delta;
oscValues[ch][i] +=t1[0]*-delta;
oscValues[ch][i+1]+=t2[0]*delta;
oscValues[ch][i+2]+=t2[1]*delta;
oscValues[ch][i+3]+=t2[2]*delta;
oscValues[ch][i+4]+=t2[3]*delta;
oscValues[ch][i+5]+=t2[4]*delta;
oscValues[ch][i+6]+=t2[5]*delta;
oscValues[ch][i+7]+=t2[6]*delta;
oscValues[ch][i+8]+=t2[7]*delta;
}
}
for (int i=0; i<oscWidth; i++) {
if (oscValues[ch][i]>0.001f || oscValues[ch][i]<-0.001f) {
WAKE_UP;
}
}
}
if (oscValuesAverage==NULL) {
oscValuesAverage=new float[2048];
}
memset(oscValuesAverage,0,2048*sizeof(float));
for (int i=0; i<oscWidth; i++) {
float avg=0;
for (int j=0; j<e->getAudioDescGot().outChans; j++) {
avg+=oscValues[j][i];
}
avg/=e->getAudioDescGot().outChans;
oscValuesAverage[i]=avg*oscZoom*2.0f;
}
/*for (int i=0; i<oscWidth; i++) {
oscValues[i]=(i&1)?0.3:0;
}*/
float peakDecay=0.05f*60.0f*ImGui::GetIO().DeltaTime;
for (int i=0; i<e->getAudioDescGot().outChans; i++) {
peak[i]*=1.0-peakDecay;
@ -82,6 +139,20 @@ void FurnaceGUI::readOsc() {
e->oscReadPos=readPos;
}
PendingDrawOsc _do;
static void _drawOsc(const ImDrawList* drawList, const ImDrawCmd* cmd) {
if (cmd!=NULL) {
if (cmd->UserCallbackData!=NULL) {
((FurnaceGUI*)(((PendingDrawOsc*)cmd->UserCallbackData)->gui))->runPendingDrawOsc((PendingDrawOsc*)cmd->UserCallbackData);
}
}
}
void FurnaceGUI::runPendingDrawOsc(PendingDrawOsc* which) {
rend->drawOsc(which->data,which->len,which->pos0,which->pos1,which->color,ImVec2(canvasW,canvasH),which->lineSize);
}
void FurnaceGUI::drawOsc() {
if (nextWindow==GUI_WINDOW_OSCILLOSCOPE) {
oscOpen=true;
@ -124,7 +195,7 @@ void FurnaceGUI::drawOsc() {
ImDrawList* dl=ImGui::GetWindowDrawList();
ImGuiWindow* window=ImGui::GetCurrentWindow();
ImVec2 waveform[512];
static ImVec2 waveform[2048];
ImVec2 size=ImGui::GetContentRegionAvail();
ImVec2 minArea=window->DC.CursorPos;
@ -145,15 +216,23 @@ void FurnaceGUI::drawOsc() {
ImU32 guideColor=ImGui::GetColorU32(uiColors[GUI_COLOR_OSC_GUIDE]);
ImGui::ItemSize(size,style.FramePadding.y);
if (ImGui::ItemAdd(rect,ImGui::GetID("wsDisplay"))) {
dl->AddRectFilledMultiColor(
inRect.Min,
inRect.Max,
ImGui::GetColorU32(uiColors[GUI_COLOR_OSC_BG1]),
ImGui::GetColorU32(uiColors[GUI_COLOR_OSC_BG2]),
ImGui::GetColorU32(uiColors[GUI_COLOR_OSC_BG4]),
ImGui::GetColorU32(uiColors[GUI_COLOR_OSC_BG3]),
settings.oscRoundedCorners?(8.0f*dpiScale):0.0f
);
if (safeMode || renderBackend==GUI_BACKEND_SOFTWARE) {
dl->AddRectFilled(
inRect.Min,
inRect.Max,
ImGui::GetColorU32(uiColors[GUI_COLOR_OSC_BG4])
);
} else {
dl->AddRectFilledMultiColor(
inRect.Min,
inRect.Max,
ImGui::GetColorU32(uiColors[GUI_COLOR_OSC_BG1]),
ImGui::GetColorU32(uiColors[GUI_COLOR_OSC_BG2]),
ImGui::GetColorU32(uiColors[GUI_COLOR_OSC_BG4]),
ImGui::GetColorU32(uiColors[GUI_COLOR_OSC_BG3]),
settings.oscRoundedCorners?(8.0f*dpiScale):0.0f
);
}
dl->AddLine(
ImLerp(rect.Min,rect.Max,ImVec2(0.0f,0.5f)),
@ -211,24 +290,92 @@ void FurnaceGUI::drawOsc() {
dpiScale
);
for (size_t i=0; i<512; i++) {
float x=(float)i/512.0f;
float y=oscValues[i]*oscZoom;
if (!settings.oscEscapesBoundary) {
if (y<-0.5f) y=-0.5f;
if (y>0.5f) y=0.5f;
oscWidth=round(inRect.Max.x-inRect.Min.x)+24;
if (oscWidth<17) oscWidth=17;
if (oscWidth>2048) oscWidth=2048;
ImDrawListFlags prevFlags=dl->Flags;
if (!settings.oscAntiAlias || safeMode) {
dl->Flags&=~(ImDrawListFlags_AntiAliasedLines|ImDrawListFlags_AntiAliasedLinesUseTex);
}
if ((oscWidth-24)>0) {
if (settings.oscMono) {
if (rend->supportsDrawOsc() && settings.shaderOsc) {
_do.gui=this;
_do.data=&oscValuesAverage[12];
_do.len=oscWidth-24;
_do.pos0=inRect.Min;
_do.pos1=inRect.Max;
_do.color=isClipping?uiColors[GUI_COLOR_OSC_WAVE_PEAK]:uiColors[GUI_COLOR_OSC_WAVE];
_do.lineSize=dpiScale*settings.oscLineSize;
dl->AddCallback(_drawOsc,&_do);
dl->AddCallback(ImDrawCallback_ResetRenderState,NULL);
} else {
for (int i=0; i<oscWidth-24; i++) {
float x=(float)i/(float)(oscWidth-24);
float y=oscValuesAverage[i+12]*0.5f;
if (!settings.oscEscapesBoundary) {
if (y<-0.5f) y=-0.5f;
if (y>0.5f) y=0.5f;
}
waveform[i]=ImLerp(inRect.Min,inRect.Max,ImVec2(x,0.5f-y));
}
if (settings.oscEscapesBoundary) {
dl->PushClipRectFullScreen();
dl->AddPolyline(waveform,oscWidth-24,color,ImDrawFlags_None,dpiScale*settings.oscLineSize);
dl->PopClipRect();
} else {
dl->AddPolyline(waveform,oscWidth-24,color,ImDrawFlags_None,dpiScale*settings.oscLineSize);
}
}
} else {
for (int ch=0; ch<e->getAudioDescGot().outChans; ch++) {
if (!isClipping) {
color=ImGui::GetColorU32(uiColors[GUI_COLOR_OSC_WAVE_CH0+ch]);
}
if (rend->supportsDrawOsc() && settings.shaderOsc) {
_do.gui=this;
_do.data=&oscValues[ch][12];
_do.len=oscWidth-24;
_do.pos0=inRect.Min;
_do.pos1=inRect.Max;
_do.color=isClipping?uiColors[GUI_COLOR_OSC_WAVE_PEAK]:uiColors[GUI_COLOR_OSC_WAVE_CH0+ch];
_do.lineSize=dpiScale*settings.oscLineSize;
dl->AddCallback(_drawOsc,&_do);
dl->AddCallback(ImDrawCallback_ResetRenderState,NULL);
} else {
for (int i=0; i<oscWidth-24; i++) {
float x=(float)i/(float)(oscWidth-24);
float y=oscValues[ch][i+12]*oscZoom;
if (!settings.oscEscapesBoundary) {
if (y<-0.5f) y=-0.5f;
if (y>0.5f) y=0.5f;
}
waveform[i]=ImLerp(inRect.Min,inRect.Max,ImVec2(x,0.5f-y));
}
if (settings.oscEscapesBoundary) {
dl->PushClipRectFullScreen();
dl->AddPolyline(waveform,oscWidth-24,color,ImDrawFlags_None,dpiScale*settings.oscLineSize);
dl->PopClipRect();
} else {
dl->AddPolyline(waveform,oscWidth-24,color,ImDrawFlags_None,dpiScale*settings.oscLineSize);
}
}
}
}
waveform[i]=ImLerp(inRect.Min,inRect.Max,ImVec2(x,0.5f-y));
}
if (settings.oscEscapesBoundary) {
dl->PushClipRectFullScreen();
dl->AddPolyline(waveform,512,color,ImDrawFlags_None,dpiScale);
dl->PopClipRect();
} else {
dl->AddPolyline(waveform,512,color,ImDrawFlags_None,dpiScale);
}
dl->Flags=prevFlags;
if (settings.oscBorder) {
dl->AddRect(inRect.Min,inRect.Max,borderColor,settings.oscRoundedCorners?(8.0f*dpiScale):0.0f,0,1.5f*dpiScale);
dl->AddRect(inRect.Min,inRect.Max,borderColor,(settings.oscRoundedCorners && !(safeMode || renderBackend==GUI_BACKEND_SOFTWARE))?(8.0f*dpiScale):0.0f,0,1.5f*dpiScale);
}
}
if (oscZoomSlider && ImGui::IsItemHovered()) {
@ -243,6 +390,10 @@ void FurnaceGUI::drawOsc() {
if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) {
oscZoomSlider=!oscZoomSlider;
}
if (mobileUI && ImGui::IsItemHovered() && CHECK_LONG_HOLD) {
oscZoomSlider=!oscZoomSlider;
NOTIFY_LONG_HOLD;
}
}
if (settings.oscTakesEntireWindow) {
ImGui::PopStyleVar(3);

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2023 tildearrow and contributors
* Copyright (C) 2021-2024 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -33,18 +33,36 @@ void FurnaceGUI::drawPatManager() {
unsigned char isUsed[DIV_MAX_PATTERNS];
bool isNull[DIV_MAX_PATTERNS];
if (ImGui::Begin("Pattern Manager",&patManagerOpen,globalWinFlags)) {
ImGui::Text("Global Tasks");
ImGui::Text("Global Tasks:");
ImGui::SameLine();
if (ImGui::Button("De-duplicate patterns")) {
e->lockEngine([this]() {
e->curSubSong->optimizePatterns();
});
MARK_MODIFIED;
}
ImGui::SameLine();
if (ImGui::Button("Re-arrange patterns")) {
e->lockEngine([this]() {
e->curSubSong->rearrangePatterns();
});
MARK_MODIFIED;
}
ImGui::SameLine();
if (ImGui::Button("Sort orders")) {
e->lockEngine([this]() {
e->curSubSong->sortOrders();
});
MARK_MODIFIED;
}
ImGui::SameLine();
if (ImGui::Button("Make patterns unique")) {
e->lockEngine([this]() {
e->curSubSong->makePatUnique();
});
MARK_MODIFIED;
}
if (ImGui::BeginTable("PatManTable",257,ImGuiTableFlags_ScrollX|ImGuiTableFlags_SizingFixedFit)) {
@ -98,6 +116,7 @@ void FurnaceGUI::drawPatManager() {
delete e->curSubSong->pat[i].data[k];
e->curSubSong->pat[i].data[k]=NULL;
});
MARK_MODIFIED;
}
ImGui::PopStyleColor();
}

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2023 tildearrow and contributors
* Copyright (C) 2021-2024 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -23,11 +23,23 @@
#include "../ta-log.h"
#include "imgui_internal.h"
#include "IconsFontAwesome4.h"
#include "furIcons.h"
#include "misc/cpp/imgui_stdlib.h"
#include "guiConst.h"
#include "../utfutils.h"
#include <fmt/printf.h>
struct DelayedLabel {
float posCenter, posY;
ImVec2 textSize;
const char* label;
DelayedLabel(float pc, float py, ImVec2 ts, const char* l):
posCenter(pc),
posY(py),
textSize(ts),
label(l) {}
};
inline float randRange(float min, float max) {
return min+((float)rand()/(float)RAND_MAX)*(max-min);
}
@ -90,8 +102,12 @@ inline void FurnaceGUI::patternRow(int i, bool isPlaying, float lineHeight, int
// check overflow highlight
if (settings.overflowHighlight) {
if (edit && cursor.y==i && curWindowLast==GUI_WINDOW_PATTERN) {
ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0,ImGui::GetColorU32(uiColors[GUI_COLOR_EDITING]));
} else if (isPlaying && oldRow==i && ord==e->getOrder()) {
if (editClone && !isPatUnique && secondTimer<0.5) {
ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0,ImGui::GetColorU32(uiColors[GUI_COLOR_EDITING_CLONE]));
} else {
ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0,ImGui::GetColorU32(uiColors[GUI_COLOR_EDITING]));
}
} else if (isPlaying && oldRow==i && ord==playOrder) {
ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0,ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_PLAY_HEAD]));
} else if (e->curSubSong->hilightB>0 && !(i%e->curSubSong->hilightB)) {
ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0,ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_HI_2]));
@ -101,8 +117,12 @@ inline void FurnaceGUI::patternRow(int i, bool isPlaying, float lineHeight, int
} else {
isPushing=true;
if (edit && cursor.y==i && curWindowLast==GUI_WINDOW_PATTERN) {
ImGui::PushStyleColor(ImGuiCol_Header,ImGui::GetColorU32(uiColors[GUI_COLOR_EDITING]));
} else if (isPlaying && oldRow==i && ord==e->getOrder()) {
if (editClone && !isPatUnique && secondTimer<0.5) {
ImGui::PushStyleColor(ImGuiCol_Header,ImGui::GetColorU32(uiColors[GUI_COLOR_EDITING_CLONE]));
} else {
ImGui::PushStyleColor(ImGuiCol_Header,ImGui::GetColorU32(uiColors[GUI_COLOR_EDITING]));
}
} else if (isPlaying && oldRow==i && ord==playOrder) {
ImGui::PushStyleColor(ImGuiCol_Header,ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_PLAY_HEAD]));
} else if (e->curSubSong->hilightB>0 && !(i%e->curSubSong->hilightB)) {
ImGui::PushStyleColor(ImGuiCol_Header,ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_HI_2]));
@ -133,17 +153,20 @@ inline void FurnaceGUI::patternRow(int i, bool isPlaying, float lineHeight, int
}
ImGui::PopStyleColor();
// for each column
int mustSetXOf=0;
for (int j=0; j<chans; j++) {
// check if channel is not hidden
if (!e->curSubSong->chanShow[j]) {
patChanX[j]=ImGui::GetCursorScreenPos().x;
continue;
}
int chanVolMax=e->getMaxVolumeChan(j);
if (chanVolMax<1) chanVolMax=1;
const DivPattern* pat=patCache[j];
ImGui::TableNextColumn();
patChanX[j]=ImGui::GetCursorScreenPos().x;
for (int k=mustSetXOf; k<=j; k++) {
patChanX[k]=ImGui::GetCursorScreenPos().x;
}
mustSetXOf=j+1;
// selection highlight flags
int sel1XSum=sel1.xCoarse*32+sel1.xFine;
@ -358,7 +381,9 @@ inline void FurnaceGUI::patternRow(int i, bool isPlaying, float lineHeight, int
ImGui::PopStyleColor();
}
ImGui::TableNextColumn();
patChanX[chans]=ImGui::GetCursorScreenPos().x;
for (int k=mustSetXOf; k<=chans; k++) {
patChanX[k]=ImGui::GetCursorScreenPos().x;
}
}
void FurnaceGUI::drawPattern() {
@ -371,13 +396,15 @@ void FurnaceGUI::drawPattern() {
if (!patternOpen) return;
bool inhibitMenu=false;
float scrollX=0;
if (e->isPlaying() && followPattern && (!e->isStepping() || pendingStepUpdate)) {
cursor.y=oldRow+((pendingStepUpdate)?1:0);
if (selStart.xCoarse==selEnd.xCoarse && selStart.xFine==selEnd.xFine && selStart.y==selEnd.y && !selecting) {
selStart=cursor;
selEnd=cursor;
if (e->isPlaying() && followPattern) {
if (oldRowChanged || !e->isStepping()) {
if (e->isStepping()) pendingStepUpdate=1;
cursor.y=oldRow;
if (selStart.xCoarse==selEnd.xCoarse && selStart.xFine==selEnd.xFine && selStart.y==selEnd.y && !selecting) {
selStart=cursor;
selEnd=cursor;
}
}
}
demandX=0;
@ -401,6 +428,7 @@ void FurnaceGUI::drawPattern() {
sel1.xFine^=sel2.xFine;
sel2.xFine^=sel1.xFine;
}
ImVec2 origWinPadding=ImGui::GetStyle().WindowPadding;
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding,ImVec2(0.0f,0.0f));
if (mobileUI) {
patWindowPos=(portrait?ImVec2(0.0f,(mobileMenuPos*-0.65*canvasH)+(0.12*canvasW)):ImVec2((0.16*canvasH)+0.5*canvasW*mobileMenuPos,0.0f));
@ -408,20 +436,17 @@ void FurnaceGUI::drawPattern() {
ImGui::SetNextWindowPos(patWindowPos);
ImGui::SetNextWindowSize(patWindowSize);
}
if (ImGui::Begin("Pattern",&patternOpen,globalWinFlags|(settings.avoidRaisingPattern?ImGuiWindowFlags_NoBringToFrontOnFocus:0))) {
if (ImGui::Begin("Pattern",&patternOpen,globalWinFlags|(settings.avoidRaisingPattern?ImGuiWindowFlags_NoBringToFrontOnFocus:0)|(settings.cursorFollowsWheel?ImGuiWindowFlags_NoScrollWithMouse:0))) {
if (!mobileUI) {
patWindowPos=ImGui::GetWindowPos();
patWindowSize=ImGui::GetWindowSize();
}
//char id[32];
ImGui::PushFont(patFont);
int ord=oldOrder;
if (followPattern) {
curOrder=e->getOrder();
}
oldOrder=curOrder;
int ord=curOrder;
int chans=e->getTotalChannelCount();
int displayChans=0;
float chanHeadBottom=0.0f;
const DivPattern* patCache[DIV_MAX_CHANS];
for (int i=0; i<chans; i++) {
if (e->curSubSong->chanShow[i]) displayChans++;
@ -436,23 +461,28 @@ void FurnaceGUI::drawPattern() {
ImGui::SetCursorPosX(ImGui::GetCursorPosX()+centerOff);
}
}
if (ImGui::BeginTable("PatternView",displayChans+2,ImGuiTableFlags_BordersInnerV|ImGuiTableFlags_ScrollX|ImGuiTableFlags_ScrollY|ImGuiTableFlags_NoPadInnerX|ImGuiTableFlags_NoBordersInFrozenArea)) {
if (e->isPlaying() && followPattern && (!e->isStepping() || pendingStepUpdate)) updateScroll(oldRow);
if (--pendingStepUpdate<0) pendingStepUpdate=0;
if (nextScroll>-0.5f) {
ImGui::SetNextWindowScroll(ImVec2(-1.0f,nextScroll));
nextScroll=-1.0f;
nextAddScroll=0.0f;
}
ImDrawList* tdl=NULL;
if (chans<1) {
ImGui::Text("there aren't any channels to show.");
} else if (ImGui::BeginTable("PatternView",displayChans+2,ImGuiTableFlags_BordersInnerV|ImGuiTableFlags_ScrollX|ImGuiTableFlags_ScrollY|ImGuiTableFlags_NoPadInnerX|ImGuiTableFlags_NoBordersInFrozenArea|((settings.cursorFollowsWheel || wheelCalmDown)?ImGuiTableFlags_NoScrollWithMouse:0))) {
ImGui::TableSetupColumn("pos",ImGuiTableColumnFlags_WidthFixed);
char chanID[2048];
float lineHeight=(ImGui::GetTextLineHeight()+2*dpiScale);
int curRow=e->getRow();
if (e->isPlaying() && followPattern && (!e->isStepping() || pendingStepUpdate)) updateScroll(curRow);
pendingStepUpdate=false;
if (nextScroll>-0.5f) {
ImGui::SetScrollY(nextScroll);
nextScroll=-1.0f;
nextAddScroll=0.0f;
}
if (nextAddScroll!=0.0f) {
ImGui::SetScrollY(ImGui::GetScrollY()+nextAddScroll);
nextScroll=-1.0f;
nextAddScroll=0.0f;
}
ImGui::TableSetupScrollFreeze(1,1);
for (int i=0; i<chans; i++) {
if (!e->curSubSong->chanShow[i]) continue;
@ -461,17 +491,11 @@ void FurnaceGUI::drawPattern() {
ImGui::TableNextRow();
ImGui::TableNextColumn();
float lpwStart=ImGui::GetCursorPosX();
if (ImGui::Selectable((extraChannelButtons==2)?" --##ExtraChannelButtons":" ++##ExtraChannelButtons",false,ImGuiSelectableFlags_NoPadWithHalfSpacing,ImVec2(0.0f,lineHeight+1.0f*dpiScale))) {
if (++extraChannelButtons>2) extraChannelButtons=0;
if (ImGui::Selectable(" ++##ExtraChannelButtons",false,ImGuiSelectableFlags_NoPadWithHalfSpacing,ImVec2(0.0f,lineHeight+1.0f*dpiScale))) {
ImGui::OpenPopup("PatternOpt");
}
if (ImGui::IsItemHovered() && !mobileUI) {
if (extraChannelButtons==2) {
ImGui::SetTooltip("Pattern names (click to collapse)\nRight-click for visualizer");
} else if (extraChannelButtons==1) {
ImGui::SetTooltip("Expanded (click for pattern names)\nRight-click for visualizer");
} else {
ImGui::SetTooltip("Compact (click to expand)\nRight-click for visualizer");
}
ImGui::SetTooltip("click for pattern options (effect columns/pattern names/visualizer)");
}
if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) {
fancyPattern=!fancyPattern;
@ -480,6 +504,42 @@ void FurnaceGUI::drawPattern() {
e->getCommandStream(cmdStream);
cmdStream.clear();
}
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding,origWinPadding);
ImGui::PushFont(mainFont);
if (ImGui::BeginPopup("PatternOpt",ImGuiWindowFlags_NoMove|ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings)) {
ImGui::Text("Options:");
ImGui::Indent();
ImGui::Checkbox("Effect columns/collapse",&patExtraButtons);
ImGui::Checkbox("Pattern names",&patChannelNames);
ImGui::Checkbox("Channel group hints",&patChannelPairs);
if (ImGui::Checkbox("Visualizer",&fancyPattern)) {
inhibitMenu=true;
e->enableCommandStream(fancyPattern);
e->getCommandStream(cmdStream);
cmdStream.clear();
}
ImGui::Unindent();
ImGui::Text("Channel status:");
ImGui::Indent();
if (ImGui::RadioButton("No##_PCS0",patChannelHints==0)) {
patChannelHints=0;
}
if (ImGui::RadioButton("Yes##_PCS1",patChannelHints==1)) {
patChannelHints=1;
}
/*
if (ImGui::RadioButton("Regular##_PCS2",patChannelHints==2)) {
patChannelHints=2;
}
if (ImGui::RadioButton("Detailed##_PCS3",patChannelHints==3)) {
patChannelHints=3;
}*/
ImGui::Unindent();
ImGui::EndPopup();
}
ImGui::PopFont();
ImGui::PopStyleVar();
for (int i=0; i<chans; i++) {
if (!e->curSubSong->chanShow[i]) continue;
ImGui::TableNextColumn();
@ -493,6 +553,13 @@ void FurnaceGUI::drawPattern() {
if (e->keyHit[i]) {
keyHit1[i]=1.0f;
if (chanOscRandomPhase) {
chanOscChan[i].phaseOff=(float)rand()/(float)RAND_MAX;
} else {
chanOscChan[i].phaseOff=0.0f;
}
if (settings.channelFeedbackStyle==1) {
keyHit[i]=0.2;
if (!muted) {
@ -615,14 +682,14 @@ void FurnaceGUI::drawPattern() {
minLabelArea.x+=0.5f*(maxLabelArea.x-minLabelArea.x-ImGui::CalcTextSize(chanID).x);
}
if (extraChannelButtons==0 || settings.channelVolStyle!=0) ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing,ImVec2(0.0f,0.0f));
if ((!patExtraButtons && !patChannelNames && !patChannelHints) || settings.channelVolStyle!=0) ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing,ImVec2(0.0f,0.0f));
ImGui::PushID(2048+i);
switch (settings.channelStyle) {
case 0: // classic
ImGui::ItemSize(size,ImGui::GetStyle().FramePadding.y);
if (ImGui::ItemAdd(rect,ImGui::GetID(chanID))) {
bool hovered=ImGui::ItemHoverable(rect,ImGui::GetID(chanID));
bool hovered=ImGui::ItemHoverable(rect,ImGui::GetID(chanID),0);
ImU32 col=(hovered || (mobileUI && ImGui::IsMouseDown(ImGuiMouseButton_Left)))?ImGui::GetColorU32(ImGuiCol_HeaderHovered):ImGui::GetColorU32(ImGuiCol_Header);
dl->AddRectFilled(rect.Min,rect.Max,col);
dl->AddText(ImVec2(minLabelArea.x,rect.Min.y),ImGui::GetColorU32(channelTextColor(i)),chanID);
@ -631,7 +698,7 @@ void FurnaceGUI::drawPattern() {
case 1: { // line
ImGui::ItemSize(size,ImGui::GetStyle().FramePadding.y);
if (ImGui::ItemAdd(rect,ImGui::GetID(chanID))) {
bool hovered=ImGui::ItemHoverable(rect,ImGui::GetID(chanID));
bool hovered=ImGui::ItemHoverable(rect,ImGui::GetID(chanID),0);
ImU32 fadeCol0=ImGui::GetColorU32(ImVec4(
chanHeadBase.x,
chanHeadBase.y,
@ -653,7 +720,7 @@ void FurnaceGUI::drawPattern() {
case 2: { // round
ImGui::ItemSize(size,ImGui::GetStyle().FramePadding.y);
if (ImGui::ItemAdd(rect,ImGui::GetID(chanID))) {
bool hovered=ImGui::ItemHoverable(rect,ImGui::GetID(chanID));
bool hovered=ImGui::ItemHoverable(rect,ImGui::GetID(chanID),0);
ImU32 fadeCol0=ImGui::GetColorU32(ImVec4(
chanHeadBase.x,
chanHeadBase.y,
@ -689,7 +756,7 @@ void FurnaceGUI::drawPattern() {
case 4: { // square border
ImGui::ItemSize(size,ImGui::GetStyle().FramePadding.y);
if (ImGui::ItemAdd(rect,ImGui::GetID(chanID))) {
bool hovered=ImGui::ItemHoverable(rect,ImGui::GetID(chanID));
bool hovered=ImGui::ItemHoverable(rect,ImGui::GetID(chanID),0);
ImU32 fadeCol=ImGui::GetColorU32(ImVec4(
chanHeadBase.x,
chanHeadBase.y,
@ -710,7 +777,7 @@ void FurnaceGUI::drawPattern() {
case 5: { // round border
ImGui::ItemSize(size,ImGui::GetStyle().FramePadding.y);
if (ImGui::ItemAdd(rect,ImGui::GetID(chanID))) {
bool hovered=ImGui::ItemHoverable(rect,ImGui::GetID(chanID));
bool hovered=ImGui::ItemHoverable(rect,ImGui::GetID(chanID),0);
ImU32 fadeCol=ImGui::GetColorU32(ImVec4(
chanHeadBase.x,
chanHeadBase.y,
@ -731,7 +798,7 @@ void FurnaceGUI::drawPattern() {
}
ImGui::PopID();
if (extraChannelButtons==0 || settings.channelVolStyle!=0) ImGui::PopStyleVar();
if ((!patExtraButtons && !patChannelNames && !patChannelHints) || settings.channelVolStyle!=0) ImGui::PopStyleVar();
if (displayTooltip && ImGui::IsItemHovered() && !mobileUI) {
ImGui::SetTooltip("%s",e->getChannelName(i));
@ -799,13 +866,14 @@ void FurnaceGUI::drawPattern() {
if (e->isRunning()) {
DivChannelState* cs=e->getChanState(i);
float stereoPan=(float)(e->convertPanSplitToLinearLR(cs->panL,cs->panR,256)-128)/128.0;
unsigned short chanPan=e->getChanPan(i);
float stereoPan=(float)(e->convertPanSplitToLinear(chanPan,8,256)-128)/128.0;
switch (settings.channelVolStyle) {
case 1: // simple
xRight=((float)(e->getChanState(i)->volume>>8)/(float)e->getMaxVolumeChan(i))*0.9+(keyHit1[i]*0.1f);
xRight=((float)(cs->volume>>8)/(float)e->getMaxVolumeChan(i))*0.9+(keyHit1[i]*0.1f);
break;
case 2: { // stereo
float amount=((float)(e->getChanState(i)->volume>>8)/(float)e->getMaxVolumeChan(i))*0.4+(keyHit1[i]*0.1f);
float amount=((float)(cs->volume>>8)/(float)e->getMaxVolumeChan(i))*0.4+(keyHit1[i]*0.1f);
xRight=0.5+amount*(1.0+MIN(0.0,stereoPan));
xLeft=0.5-amount*(1.0-MAX(0.0,stereoPan));
break;
@ -825,21 +893,11 @@ void FurnaceGUI::drawPattern() {
ImGui::GetColorU32(chanHeadBase)
);
}
keyHit1[i]-=0.2f;
if (keyHit1[i]<0.0f) keyHit1[i]=0.0f;
}
}
// extra buttons
if (extraChannelButtons==2) {
DivPattern* pat=e->curPat[i].getPattern(e->curOrders->ord[i][ord],true);
ImGui::PushFont(mainFont);
snprintf(chanID,2048," %s##PatName%d",pat->name.c_str(),i);
if (ImGui::Selectable(chanID,true,ImGuiSelectableFlags_NoPadWithHalfSpacing,ImVec2(0.0f,lineHeight+1.0f*dpiScale))) {
editStr(&pat->name);
}
ImGui::PopFont();
} else if (extraChannelButtons==1) {
if (patExtraButtons) {
snprintf(chanID,2048,"%c##_HCH%d",e->curSubSong->chanCollapse[i]?'+':'-',i);
ImGui::SetCursorPosX(ImGui::GetCursorPosX()+4.0f*dpiScale);
if (ImGui::SmallButton(chanID)) {
@ -869,6 +927,177 @@ void FurnaceGUI::drawPattern() {
}
ImGui::Spacing();
}
if (patChannelNames) {
DivPattern* pat=e->curPat[i].getPattern(e->curOrders->ord[i][ord],true);
ImGui::PushFont(mainFont);
snprintf(chanID,2048," %s##PatName%d",pat->name.c_str(),i);
if (ImGui::Selectable(chanID,true,ImGuiSelectableFlags_NoPadWithHalfSpacing,ImVec2(0.0f,lineHeight+1.0f*dpiScale))) {
editStr(&pat->name);
}
ImGui::PopFont();
}
if (patChannelHints) {
ImGuiWindow* win=ImGui::GetCurrentWindow();
ImVec2 posMin=win->DC.CursorPos;
ImGui::Dummy(ImVec2(dpiScale,settings.iconSize*dpiScale));
ImVec2 posMax=ImVec2(win->WorkRect.Max.x,win->WorkRect.Max.y);
posMin.y-=ImGui::GetStyle().ItemSpacing.y*0.5;
ImDrawList* dl=ImGui::GetWindowDrawList();
ImVec2 iconPos[6];
DivChannelState* cs=e->getChanState(i);
if (cs!=NULL) {
DivChannelModeHints hints=e->getChanModeHints(i);
if (hints.count>4) hints.count=4;
int hintCount=3+hints.count;
// calculate icon size
for (int i=0; i<hintCount; i++) {
iconPos[i]=ImLerp(posMin,posMax,ImVec2((float)(2*i+1)/(float)(hintCount*2),0.0f));
}
// 1. ON/OFF
ImVec4 onOffColor;
if (cs->keyOn) {
if (cs->releasing) {
onOffColor=uiColors[GUI_COLOR_PATTERN_STATUS_REL_ON];
} else {
onOffColor=uiColors[GUI_COLOR_PATTERN_STATUS_ON];
}
} else {
if (cs->releasing) {
onOffColor=uiColors[GUI_COLOR_PATTERN_STATUS_REL];
} else {
onOffColor=uiColors[GUI_COLOR_PATTERN_STATUS_OFF];
}
}
iconPos[0].x-=mainFont->CalcTextSizeA(mainFont->FontSize,FLT_MAX,0.0f,ICON_FA_SQUARE).x*0.5f;
dl->AddText(mainFont,settings.mainFontSize*dpiScale,iconPos[0],ImGui::GetColorU32(onOffColor),ICON_FA_SQUARE);
// 2. PITCH SLIDE/VIBRATO
ImVec4 pitchColor;
const char* pitchIcon=ICON_FUR_SINE;
if (cs->inPorta) {
pitchIcon=ICON_FA_SHARE;
pitchColor=uiColors[GUI_COLOR_PATTERN_STATUS_PITCH];
} else if (cs->portaSpeed>0) {
if (cs->portaNote>=60) {
pitchIcon=ICON_FA_CHEVRON_UP;
} else {
pitchIcon=ICON_FA_CHEVRON_DOWN;
}
pitchColor=uiColors[GUI_COLOR_PATTERN_STATUS_PITCH];
} else if (cs->vibratoDepth>0) {
pitchIcon=ICON_FUR_SINE;
pitchColor=uiColors[GUI_COLOR_PATTERN_STATUS_PITCH];
} else if (cs->arp) {
pitchIcon=ICON_FA_BARS;
pitchColor=uiColors[GUI_COLOR_PATTERN_STATUS_NOTE];
} else {
pitchColor=uiColors[GUI_COLOR_PATTERN_STATUS_OFF];
}
iconPos[1].x-=mainFont->CalcTextSizeA(mainFont->FontSize,FLT_MAX,0.0f,pitchIcon).x*0.5f;
dl->AddText(mainFont,settings.mainFontSize*dpiScale,iconPos[1],ImGui::GetColorU32(pitchColor),pitchIcon);
// 3. VOLUME
ImVec4 volColor;
const char* volIcon=ICON_FA_MINUS;
if (cs->tremoloDepth>0) {
volIcon=ICON_FUR_SINE;
volColor=uiColors[GUI_COLOR_PATTERN_STATUS_VOLUME];
} else if (cs->volSpeed) {
if (cs->volSpeed>0) {
volIcon=ICON_FA_CHEVRON_UP;
} else {
volIcon=ICON_FA_CHEVRON_DOWN;
}
volColor=uiColors[GUI_COLOR_PATTERN_STATUS_VOLUME];
} else {
volColor=uiColors[GUI_COLOR_PATTERN_STATUS_OFF];
}
iconPos[2].x-=mainFont->CalcTextSizeA(mainFont->FontSize,FLT_MAX,0.0f,volIcon).x*0.5f;
dl->AddText(mainFont,settings.mainFontSize*dpiScale,iconPos[2],ImGui::GetColorU32(volColor),volIcon);
// 4. OTHER
for (int i=0; i<hints.count; i++) {
if (hints.hint[i]==NULL) continue;
ImVec4 hintColor=uiColors[GUI_COLOR_PATTERN_STATUS_OFF];
switch (hints.type[i]) {
case 0:
hintColor=uiColors[GUI_COLOR_PATTERN_STATUS_OFF];
break;
case 1:
hintColor=uiColors[GUI_COLOR_PATTERN_STATUS_VOLUME];
break;
case 2:
hintColor=uiColors[GUI_COLOR_PATTERN_STATUS_PITCH];
break;
case 3:
hintColor=uiColors[GUI_COLOR_PATTERN_STATUS_PANNING];
break;
case 4:
hintColor=uiColors[GUI_COLOR_PATTERN_STATUS_SYS1];
break;
case 5:
hintColor=uiColors[GUI_COLOR_PATTERN_STATUS_SYS2];
break;
case 6:
hintColor=uiColors[GUI_COLOR_PATTERN_STATUS_MIXING];
break;
case 7:
hintColor=uiColors[GUI_COLOR_PATTERN_STATUS_DSP];
break;
case 8:
hintColor=uiColors[GUI_COLOR_PATTERN_STATUS_NOTE];
break;
case 9:
hintColor=uiColors[GUI_COLOR_PATTERN_STATUS_MISC1];
break;
case 10:
hintColor=uiColors[GUI_COLOR_PATTERN_STATUS_MISC2];
break;
case 11:
hintColor=uiColors[GUI_COLOR_PATTERN_STATUS_MISC3];
break;
case 12:
hintColor=uiColors[GUI_COLOR_PATTERN_STATUS_ATTACK];
break;
case 13:
hintColor=uiColors[GUI_COLOR_PATTERN_STATUS_DECAY];
break;
case 14:
hintColor=uiColors[GUI_COLOR_PATTERN_STATUS_SUSTAIN];
break;
case 15:
hintColor=uiColors[GUI_COLOR_PATTERN_STATUS_RELEASE];
break;
case 16:
hintColor=uiColors[GUI_COLOR_PATTERN_STATUS_DEC_LINEAR];
break;
case 17:
hintColor=uiColors[GUI_COLOR_PATTERN_STATUS_DEC_EXP];
break;
case 18:
hintColor=uiColors[GUI_COLOR_PATTERN_STATUS_INC];
break;
case 19:
hintColor=uiColors[GUI_COLOR_PATTERN_STATUS_BENT];
break;
case 20:
hintColor=uiColors[GUI_COLOR_PATTERN_STATUS_DIRECT];
break;
default:
hintColor=uiColors[GUI_COLOR_TEXT];
break;
}
iconPos[i+3].x-=mainFont->CalcTextSizeA(mainFont->FontSize,FLT_MAX,0.0f,hints.hint[i]).x*0.5f;
dl->AddText(mainFont,settings.mainFontSize*dpiScale,iconPos[i+3],ImGui::GetColorU32(hintColor),hints.hint[i]);
}
}
}
chanHeadBottom=ImGui::GetCursorScreenPos().y-ImGui::GetStyle().ItemSpacing.y;
}
ImGui::TableNextColumn();
lastPatternWidth=ImGui::GetCursorPosX()-lpwStart+ImGui::GetStyle().ScrollbarSize;
@ -934,9 +1163,9 @@ void FurnaceGUI::drawPattern() {
ImGui::TableNextColumn();
}
}
ImGui::EndDisabled();
ImGui::PopStyleVar();
oldRow=curRow;
if (demandScrollX) {
int totalDemand=demandX-ImGui::GetScrollX();
if (totalDemand<80) {
@ -946,11 +1175,23 @@ void FurnaceGUI::drawPattern() {
}
demandScrollX=false;
}
scrollX=ImGui::GetScrollX();
// cursor follows wheel
if (settings.cursorFollowsWheel && (!e->isPlaying() || !followPattern) && ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows)) {
if (wheelX!=0 || wheelY!=0) {
int xAmount=wheelX;
int yAmount=(settings.cursorFollowsWheel==2)?wheelY:-wheelY;
if (settings.cursorWheelStep==1) {
xAmount*=MAX(1,editStep);
yAmount*=MAX(1,editStep);
}
moveCursor(xAmount,yAmount,false);
}
}
// overflow changes order
// TODO: this is very unreliable and sometimes it can warp you out of the song
if (settings.scrollChangesOrder && !e->isPlaying() && ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows)) {
if (--wheelCalmDown<0) wheelCalmDown=0;
if (settings.scrollChangesOrder && (!e->isPlaying() || !followPattern) && ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows) && !settings.cursorFollowsWheel && !wheelCalmDown) {
if (wheelY!=0) {
if (wheelY>0) {
if (ImGui::GetScrollY()<=0) {
@ -959,6 +1200,12 @@ void FurnaceGUI::drawPattern() {
setOrder(curOrder-1);
ImGui::SetScrollY(ImGui::GetScrollMaxY());
updateScroll(e->curSubSong->patLen);
wheelCalmDown=2;
} else if (settings.scrollChangesOrder==2) {
setOrder(e->curSubSong->ordersLen-1);
ImGui::SetScrollY(ImGui::GetScrollMaxY());
updateScroll(e->curSubSong->patLen);
wheelCalmDown=2;
}
haveHitBounds=false;
} else {
@ -974,6 +1221,12 @@ void FurnaceGUI::drawPattern() {
setOrder(curOrder+1);
ImGui::SetScrollY(0);
updateScroll(0);
wheelCalmDown=2;
} else if (settings.scrollChangesOrder==2) {
setOrder(0);
ImGui::SetScrollY(0);
updateScroll(0);
wheelCalmDown=2;
}
haveHitBounds=false;
} else {
@ -985,9 +1238,220 @@ void FurnaceGUI::drawPattern() {
}
}
}
// HACK: rendering here would cause the pairs to be drawn behind the pattern for some reason...
// ...so we capture the table's window draw list...
tdl=ImGui::GetWindowDrawList();
ImGui::EndTable();
}
// ...and then use it here
ImGui::PushFont(mainFont);
if (patChannelPairs && e->isRunning() && tdl!=NULL) { // pair hints
float pos=0.0f;
float posCenter=0.0f;
float posMin=FLT_MAX;
float posMax=-FLT_MAX;
ImVec2 textSize;
unsigned int floors[4][4]; // bit array
std::vector<DelayedLabel> delayedLabels;
memset(floors,0,4*4*sizeof(unsigned int));
for (int i=0; i<chans; i++) {
bool isPaired=false;
int numPairs=0;
unsigned int pairMin=i;
unsigned int pairMax=i;
unsigned char curFloor=0;
if (!e->curSubSong->chanShow[i]) {
continue;
}
DivChannelPair pairs=e->getChanPaired(i);
for (int j=0; j<8; j++) {
if (pairs.pairs[j]==-1) continue;
int pairCh=e->dispatchFirstChan[i]+pairs.pairs[j];
if (!e->curSubSong->chanShow[pairCh]) {
continue;
}
isPaired=true;
if ((unsigned int)pairCh<pairMin) pairMin=pairCh;
if ((unsigned int)pairCh>pairMax) pairMax=pairCh;
}
if (!isPaired) continue;
float posY=chanHeadBottom;
// find a free floor
while (curFloor<4) {
bool free=true;
for (unsigned int j=pairMin; j<=pairMax; j++) {
const unsigned int j0=j>>5;
const unsigned int j1=1U<<(j&31);
if (floors[curFloor][j0]&j1) {
free=false;
break;
}
}
if (free) break;
curFloor++;
}
if (curFloor<4) {
// occupy floor
floors[curFloor][pairMin>>5]|=1U<<(pairMin&31);
floors[curFloor][pairMax>>5]|=1U<<(pairMax&31);
}
pos=(patChanX[i+1]+patChanX[i])*0.5;
posCenter=pos;
posMin=pos;
posMax=pos;
numPairs++;
if (pairs.label==NULL) {
textSize=ImGui::CalcTextSize("???");
} else {
textSize=ImGui::CalcTextSize(pairs.label);
}
posY+=(textSize.y+ImGui::GetStyle().ItemSpacing.y)*curFloor;
tdl->AddLine(
ImVec2(pos,chanHeadBottom),
ImVec2(pos,posY+textSize.y),
ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_PAIR]),
2.0f*dpiScale
);
for (int j=0; j<8; j++) {
if (pairs.pairs[j]==-1) continue;
int pairCh=e->dispatchFirstChan[i]+pairs.pairs[j];
if (!e->curSubSong->chanShow[pairCh]) {
continue;
}
pos=(patChanX[pairCh+1]+patChanX[pairCh])*0.5;
posCenter+=pos;
numPairs++;
if (pos<posMin) posMin=pos;
if (pos>posMax) posMax=pos;
tdl->AddLine(
ImVec2(pos,chanHeadBottom),
ImVec2(pos,posY+textSize.y),
ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_PAIR]),
2.0f*dpiScale
);
}
posCenter/=numPairs;
if (pairs.label==NULL) {
tdl->AddLine(
ImVec2(posMin,posY+textSize.y),
ImVec2(posMax,posY+textSize.y),
ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_PAIR]),
2.0f*dpiScale
);
} else {
tdl->AddLine(
ImVec2(posMin,posY+textSize.y),
ImVec2(posCenter-textSize.x*0.5-6.0f*dpiScale,posY+textSize.y),
ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_PAIR]),
2.0f*dpiScale
);
tdl->AddLine(
ImVec2(posCenter+textSize.x*0.5+6.0f*dpiScale,posY+textSize.y),
ImVec2(posMax,posY+textSize.y),
ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_PAIR]),
2.0f*dpiScale
);
delayedLabels.push_back(DelayedLabel(posCenter,posY,textSize,pairs.label));
}
}
for (DelayedLabel& i: delayedLabels) {
ImGui::RenderFrameDrawList(
tdl,
ImVec2(i.posCenter-i.textSize.x*0.5-6.0f*dpiScale,i.posY+i.textSize.y*0.5-3.0f*dpiScale),
ImVec2(i.posCenter+i.textSize.x*0.5+6.0f*dpiScale,i.posY+i.textSize.y*1.5+3.0f*dpiScale),
ImGui::GetColorU32(ImGuiCol_FrameBg),
true,
ImGui::GetStyle().FrameRounding
);
tdl->AddText(
ImVec2(i.posCenter-i.textSize.x*0.5,i.posY+i.textSize.y*0.5),
ImGui::GetColorU32(ImGuiCol_Text),
i.label
);
}
}
// also let's draw a warning if the instrument cannot be previewed
if (tdl!=NULL && failedNoteOn) {
ImVec2 winCenter=ImGui::GetWindowPos()+ImGui::GetWindowSize()*0.5f;
ImGui::PushFont(bigFont);
ImVec2 warnHeadSize=ImGui::CalcTextSize("WARNING!!");
ImGui::PopFont();
ImVec2 warnTextSize1=ImGui::CalcTextSize("this instrument cannot be previewed because");
ImVec2 warnTextSize2=ImGui::CalcTextSize("none of the chips can play it");
ImVec2 warnTextSize3=ImGui::CalcTextSize("your instrument is in peril!! be careful...");
float maxTextSize=warnHeadSize.x;
if (warnTextSize1.x>maxTextSize) maxTextSize=warnTextSize1.x;
if (warnTextSize2.x>maxTextSize) maxTextSize=warnTextSize2.x;
if (warnTextSize3.x>maxTextSize) maxTextSize=warnTextSize3.x;
ImVec2 sumOfAll=ImVec2(
maxTextSize,
warnHeadSize.y+warnTextSize1.y+warnTextSize2.y+warnTextSize3.y
);
ImGui::RenderFrameDrawList(
tdl,
ImVec2(winCenter.x-sumOfAll.x*0.5-ImGui::GetStyle().ItemInnerSpacing.x,winCenter.y-sumOfAll.y*0.5-ImGui::GetStyle().ItemInnerSpacing.y),
ImVec2(winCenter.x+sumOfAll.x*0.5+ImGui::GetStyle().ItemInnerSpacing.x,winCenter.y+sumOfAll.y*0.5+ImGui::GetStyle().ItemInnerSpacing.y),
ImGui::GetColorU32(ImGuiCol_FrameBg),
true,
ImGui::GetStyle().FrameRounding
);
float whereY=winCenter.y-sumOfAll.y*0.5;
tdl->AddText(
bigFont,
MAX(1,40*dpiScale),
ImVec2(winCenter.x-warnHeadSize.x*0.5,whereY),
ImGui::GetColorU32(ImGuiCol_Text),
"WARNING!!"
);
whereY+=warnHeadSize.y;
tdl->AddText(
ImVec2(winCenter.x-warnTextSize1.x*0.5,whereY),
ImGui::GetColorU32(ImGuiCol_Text),
"this instrument cannot be previewed because"
);
whereY+=warnTextSize1.y;
tdl->AddText(
ImVec2(winCenter.x-warnTextSize2.x*0.5,whereY),
ImGui::GetColorU32(ImGuiCol_Text),
"none of the chips can play it"
);
whereY+=warnTextSize2.y;
tdl->AddText(
ImVec2(winCenter.x-warnTextSize3.x*0.5,whereY),
ImGui::GetColorU32(ImGuiCol_Text),
"your instrument is in peril!! be careful..."
);
whereY+=warnTextSize3.y;
}
ImGui::PopFont();
if (fancyPattern) { // visualizer
e->getCommandStream(cmdStream);
ImDrawList* dl=ImGui::GetWindowDrawList();
@ -1008,7 +1472,6 @@ void FurnaceGUI::drawPattern() {
if (i.cmd==DIV_CMD_PRE_NOTE) continue;
if (i.cmd==DIV_CMD_SAMPLE_BANK) continue;
if (i.cmd==DIV_CMD_GET_VOLUME) continue;
if (i.cmd==DIV_ALWAYS_SET_VOLUME) continue;
if (i.cmd==DIV_CMD_HINT_VOLUME ||
i.cmd==DIV_CMD_HINT_PORTA ||
i.cmd==DIV_CMD_HINT_LEGATO ||
@ -1119,6 +1582,8 @@ void FurnaceGUI::drawPattern() {
break;
}
if (!e->curSubSong->chanShow[i.chan]) continue;
for (int j=0; j<num; j++) {
ImVec2 partPos=ImVec2(
off.x+patChanX[i.chan]+fmod(rand(),width),
@ -1175,8 +1640,8 @@ void FurnaceGUI::drawPattern() {
}
if (width>0.1) for (float j=-patChanSlideY[i]; j<ImGui::GetWindowHeight(); j+=width*0.7) {
ImVec2 tMin=ImVec2(off.x+patChanX[i]-scrollX,off.y+j);
ImVec2 tMax=ImVec2(off.x+patChanX[i+1]-scrollX,off.y+j+width*0.6);
ImVec2 tMin=ImVec2(off.x+patChanX[i],off.y+j);
ImVec2 tMax=ImVec2(off.x+patChanX[i+1],off.y+j+width*0.6);
if (ch->portaNote<=ch->note) {
arrowPoints[0]=ImLerp(tMin,tMax,ImVec2(0.1,1.0-0.8));
arrowPoints[1]=ImLerp(tMin,tMax,ImVec2(0.5,1.0-0.0));

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2023 tildearrow and contributors
* Copyright (C) 2021-2024 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -42,7 +42,11 @@ const bool isTopKey[12]={
#define VALUE_DIGIT(x,label) \
if (ImGui::Button(label,buttonSize)) { \
valueInput(x,false); \
if (curWindow==GUI_WINDOW_ORDERS && orderEditMode>0) { \
orderInput(x); \
} else { \
valueInput(x,false); \
} \
}
void FurnaceGUI::drawPiano() {
@ -100,6 +104,7 @@ void FurnaceGUI::drawPiano() {
}
if (ImGui::BeginPopupContextItem("PianoOptions",ImGuiPopupFlags_MouseButtonLeft)) {
ImGui::Text("Key layout:");
ImGui::Indent();
if (ImGui::RadioButton("Automatic",pianoView==PIANO_LAYOUT_AUTOMATIC)) {
pianoView=PIANO_LAYOUT_AUTOMATIC;
}
@ -109,7 +114,9 @@ void FurnaceGUI::drawPiano() {
if (ImGui::RadioButton("Continuous",pianoView==PIANO_LAYOUT_CONTINUOUS)) {
pianoView=PIANO_LAYOUT_CONTINUOUS;
}
ImGui::Unindent();
ImGui::Text("Value input pad:");
ImGui::Indent();
if (ImGui::RadioButton("Disabled",pianoInputPadMode==PIANO_INPUT_PAD_DISABLE)) {
pianoInputPadMode=PIANO_INPUT_PAD_DISABLE;
}
@ -122,6 +129,7 @@ void FurnaceGUI::drawPiano() {
if (ImGui::RadioButton("Split (always visible)",pianoInputPadMode==PIANO_INPUT_PAD_SPLIT_VISIBLE)) {
pianoInputPadMode=PIANO_INPUT_PAD_SPLIT_VISIBLE;
}
ImGui::Unindent();
ImGui::Checkbox("Share play/edit offset/range",&pianoSharePosition);
ImGui::Checkbox("Read-only (can't input notes)",&pianoReadonly);
ImGui::EndPopup();
@ -162,7 +170,7 @@ void FurnaceGUI::drawPiano() {
}
ImGui::TableNextColumn();
if (pianoInputPadMode==PIANO_INPUT_PAD_REPLACE && cursor.xFine>0 && curWindow==GUI_WINDOW_PATTERN) {
if (pianoInputPadMode==PIANO_INPUT_PAD_REPLACE && ((cursor.xFine>0 && curWindow==GUI_WINDOW_PATTERN) || (curWindow==GUI_WINDOW_ORDERS && orderEditMode>0))) {
ImVec2 buttonSize=ImGui::GetContentRegionAvail();
if (ImGui::BeginTable("InputPadP",8,ImGuiTableFlags_SizingFixedSame)) {
ImGui::TableNextRow();
@ -224,7 +232,7 @@ void FurnaceGUI::drawPiano() {
//ImGui::ItemSize(size,ImGui::GetStyle().FramePadding.y);
if (ImGui::ItemAdd(rect,ImGui::GetID("pianoDisplay"))) {
bool canInput=false;
if (!pianoReadonly && ImGui::ItemHoverable(rect,ImGui::GetID("pianoDisplay"))) {
if (!pianoReadonly && ImGui::ItemHoverable(rect,ImGui::GetID("pianoDisplay"),0)) {
canInput=true;
ImGui::InhibitInertialScroll();
}
@ -377,7 +385,6 @@ void FurnaceGUI::drawPiano() {
pianoOptions=!pianoOptions;
}
// TODO: wave and sample preview
// first check released keys
for (int i=0; i<180; i++) {
int note=i-60;
@ -395,6 +402,7 @@ void FurnaceGUI::drawPiano() {
default:
e->synchronized([this,note]() {
e->autoNoteOff(-1,note);
failedNoteOn=false;
});
break;
}
@ -417,10 +425,10 @@ void FurnaceGUI::drawPiano() {
break;
default:
if (sampleMapWaitingInput) {
alterSampleMap(true,note);
alterSampleMap(1,note);
} else {
e->synchronized([this,note]() {
e->autoNoteOn(-1,curIns,note);
if (!e->autoNoteOn(-1,curIns,note)) failedNoteOn=true;
});
if (edit && curWindow!=GUI_WINDOW_INS_LIST && curWindow!=GUI_WINDOW_INS_EDIT) noteInput(note,0);
}
@ -439,9 +447,9 @@ void FurnaceGUI::drawPiano() {
ImGui::End();
// draw input pad if necessary
if (curWindow==GUI_WINDOW_PATTERN && ((pianoInputPadMode==PIANO_INPUT_PAD_SPLIT_AUTO && cursor.xFine>0) || pianoInputPadMode==PIANO_INPUT_PAD_SPLIT_VISIBLE)) {
if ((curWindow==GUI_WINDOW_ORDERS || curWindow==GUI_WINDOW_PATTERN || !mobileUI) && ((pianoInputPadMode==PIANO_INPUT_PAD_SPLIT_AUTO && (cursor.xFine>0 || (curWindow==GUI_WINDOW_ORDERS && orderEditMode>0))) || pianoInputPadMode==PIANO_INPUT_PAD_SPLIT_VISIBLE)) {
if (ImGui::Begin("Input Pad",NULL,ImGuiWindowFlags_NoTitleBar)) {
ImGui::BeginDisabled(cursor.xFine==0);
ImGui::BeginDisabled(cursor.xFine==0 && !(curWindow==GUI_WINDOW_ORDERS && orderEditMode>0));
if (ImGui::BeginTable("InputPad",3,ImGuiTableFlags_Borders)) {
ImGui::TableNextRow();
ImGui::TableNextColumn();

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2023 tildearrow and contributors
* Copyright (C) 2021-2024 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -78,7 +78,7 @@ int PlotNoLerpEx(ImGuiPlotType plot_type, const char* label, float (*values_gett
ImGui::ItemSize(total_bb, style.FramePadding.y);
if (!ImGui::ItemAdd(total_bb, 0, &frame_bb, ImGuiItemFlags_NoInertialScroll))
return -1;
const bool hovered = ImGui::ItemHoverable(frame_bb, id);
const bool hovered = ImGui::ItemHoverable(frame_bb, id, 0);
// Determine scale from values if not specified
if (scale_min == FLT_MAX || scale_max == FLT_MAX)
@ -205,7 +205,7 @@ int PlotBitfieldEx(const char* label, int (*values_getter)(void* data, int idx),
ImGui::ItemSize(total_bb, style.FramePadding.y);
if (!ImGui::ItemAdd(total_bb, 0, &frame_bb, ImGuiItemFlags_NoInertialScroll))
return -1;
const bool hovered = ImGui::ItemHoverable(frame_bb, id);
const bool hovered = ImGui::ItemHoverable(frame_bb, id, 0);
ImGui::RenderFrame(frame_bb.Min, frame_bb.Max, ImGui::GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding);
@ -315,7 +315,7 @@ int PlotCustomEx(ImGuiPlotType plot_type, const char* label, float (*values_gett
ImGui::ItemSize(total_bb, style.FramePadding.y);
if (!ImGui::ItemAdd(total_bb, 0, &frame_bb, ImGuiItemFlags_NoInertialScroll))
return -1;
const bool hovered = ImGui::ItemHoverable(frame_bb, id);
const bool hovered = ImGui::ItemHoverable(frame_bb, id, 0);
// Determine scale from values if not specified
if (scale_min == FLT_MAX || scale_max == FLT_MAX)
@ -394,6 +394,8 @@ int PlotCustomEx(ImGuiPlotType plot_type, const char* label, float (*values_gett
window->DrawList->AddLine(ImLerp(inner_bb.Min,inner_bb.Max,ImVec2(0.0f,histogram_zero_line_t)),ImLerp(inner_bb.Min,inner_bb.Max,ImVec2(1.0f,histogram_zero_line_t)),col_base);
}
ImVec2 chevron[3];
for (int n = 0; n < res_w; n++)
{
const float t1 = t0 + t_step;
@ -421,11 +423,30 @@ int PlotCustomEx(ImGuiPlotType plot_type, const char* label, float (*values_gett
if (values_highlight!=NULL) {
if (values_highlight[v1_idx]) rCol=ImGui::GetColorU32(highlightColor);
}
window->DrawList->AddRectFilled(pos0, pos1, rCol);
if (blockMode) {
if ((int)v0>=(int)(scale_max+0.5)) {
float chScale=(pos1.x-pos0.x)*0.125;
chevron[0]=ImVec2(pos0.x+(pos1.x-pos0.x)*0.25,pos1.y+4.0f*chScale);
chevron[1]=ImVec2(pos0.x+(pos1.x-pos0.x)*0.5,pos1.y+2.0f*chScale);
chevron[2]=ImVec2(pos0.x+(pos1.x-pos0.x)*0.75,pos1.y+4.0f*chScale);
window->DrawList->AddPolyline(chevron, 3, rCol, 0, chScale);
} else if ((int)v0<(int)(scale_min)) {
float chScale=(pos1.x-pos0.x)*0.125;
chevron[0]=ImVec2(pos0.x+(pos1.x-pos0.x)*0.25,pos1.y-4.0f*chScale);
chevron[1]=ImVec2(pos0.x+(pos1.x-pos0.x)*0.5,pos1.y-2.0f*chScale);
chevron[2]=ImVec2(pos0.x+(pos1.x-pos0.x)*0.75,pos1.y-4.0f*chScale);
window->DrawList->AddPolyline(chevron, 3, rCol, 0, chScale);
} else {
window->DrawList->AddRectFilled(pos0, pos1, rCol);
}
} else {
window->DrawList->AddRectFilled(pos0, pos1, rCol);
}
}
t0 = t1;
tp0 = tp1;
v0 = v1;
}
}

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2023 tildearrow and contributors
* Copyright (C) 2021-2024 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -18,7 +18,7 @@
*/
#include "imgui.h"
#include <string>
#include "../pch.h"
void PlotNoLerp(const char* label, const float* values, int values_count, int values_offset = 0, const char* overlay_text = NULL, float scale_min = FLT_MAX, float scale_max = FLT_MAX, ImVec2 graph_size = ImVec2(0, 0), int stride = sizeof(float));
void PlotBitfield(const char* label, const int* values, int values_count, int values_offset = 0, const char** overlay_text = NULL, int bits = 8, ImVec2 graph_size = ImVec2(0, 0), int stride = sizeof(float), const bool* values_highlight = NULL, ImVec4 highlightColor = ImVec4(1.0f,1.0f,1.0f,1.0f));

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2023 tildearrow and contributors
* Copyright (C) 2021-2024 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2023 tildearrow and contributors
* Copyright (C) 2021-2024 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -25,33 +25,70 @@
#ifdef HAVE_RENDER_GL
#include "render/renderGL.h"
#endif
#ifdef HAVE_RENDER_GL1
#include "render/renderGL1.h"
#endif
#ifdef HAVE_RENDER_DX11
#include "render/renderDX11.h"
#endif
#ifdef HAVE_RENDER_METAL
#include "render/renderMetal.h"
#endif
#include "render/renderSoftware.h"
bool FurnaceGUI::initRender() {
if (rend!=NULL) return false;
if (settings.renderBackend=="OpenGL") {
renderBackend=GUI_BACKEND_GL;
logV("requested backend: %s",settings.renderBackend);
if (safeMode) {
renderBackend=GUI_BACKEND_SOFTWARE;
} else if (settings.renderBackend=="OpenGL" || settings.renderBackend=="OpenGL 3.0" || settings.renderBackend=="OpenGL ES 2.0") {
renderBackend=GUI_BACKEND_GL3;
} else if (settings.renderBackend=="OpenGL 2.0") {
renderBackend=GUI_BACKEND_GL2;
} else if (settings.renderBackend=="OpenGL 1.1") {
renderBackend=GUI_BACKEND_GL1;
} else if (settings.renderBackend=="DirectX 11") {
renderBackend=GUI_BACKEND_DX11;
} else if (settings.renderBackend=="DirectX 9") {
renderBackend=GUI_BACKEND_DX9;
} else if (settings.renderBackend=="Metal") {
renderBackend=GUI_BACKEND_METAL;
} else if (settings.renderBackend=="SDL") {
renderBackend=GUI_BACKEND_SDL;
} else if (settings.renderBackend=="Software") {
renderBackend=GUI_BACKEND_SOFTWARE;
} else {
renderBackend=GUI_BACKEND_DEFAULT;
}
switch (renderBackend) {
#ifdef HAVE_RENDER_GL
case GUI_BACKEND_GL:
logI("render backend: OpenGL");
#ifdef USE_GLES
case GUI_BACKEND_GL3:
case GUI_BACKEND_GL2:
logI("render backend: OpenGL ES 2.0");
rend=new FurnaceGUIRenderGL;
((FurnaceGUIRenderGL*)rend)->setVersion(3);
break;
#else
case GUI_BACKEND_GL3:
logI("render backend: OpenGL 3.0");
rend=new FurnaceGUIRenderGL;
((FurnaceGUIRenderGL*)rend)->setVersion(3);
break;
case GUI_BACKEND_GL2:
logI("render backend: OpenGL 2.0");
rend=new FurnaceGUIRenderGL;
((FurnaceGUIRenderGL*)rend)->setVersion(2);
break;
#endif
#endif
#ifdef HAVE_RENDER_GL1
case GUI_BACKEND_GL1:
logI("render backend: OpenGL 1.1");
rend=new FurnaceGUIRenderGL1;
break;
#endif
#ifdef HAVE_RENDER_DX11
@ -60,6 +97,12 @@ bool FurnaceGUI::initRender() {
rend=new FurnaceGUIRenderDX11;
break;
#endif
#ifdef HAVE_RENDER_DX9
case GUI_BACKEND_DX9:
logI("render backend: DirectX 9");
rend=new FurnaceGUIRenderDX9;
break;
#endif
#ifdef HAVE_RENDER_METAL
case GUI_BACKEND_METAL:
logI("render backend: Metal");
@ -72,6 +115,10 @@ bool FurnaceGUI::initRender() {
rend=new FurnaceGUIRenderSDL;
break;
#endif
case GUI_BACKEND_SOFTWARE:
logI("render backend: Software");
rend=new FurnaceGUIRenderSoftware;
break;
default:
logE("invalid render backend!");
return false;

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2023 tildearrow and contributors
* Copyright (C) 2021-2024 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -23,6 +23,14 @@ ImTextureID FurnaceGUIRender::getTextureID(FurnaceGUITexture* which) {
return NULL;
}
float FurnaceGUIRender::getTextureU(FurnaceGUITexture* which) {
return 1.0;
}
float FurnaceGUIRender::getTextureV(FurnaceGUITexture* which) {
return 1.0;
}
bool FurnaceGUIRender::lockTexture(FurnaceGUITexture* which, void** data, int* pitch) {
return false;
}
@ -35,7 +43,7 @@ bool FurnaceGUIRender::updateTexture(FurnaceGUITexture* which, void* data, int p
return false;
}
FurnaceGUITexture* FurnaceGUIRender::createTexture(bool dynamic, int width, int height) {
FurnaceGUITexture* FurnaceGUIRender::createTexture(bool dynamic, int width, int height, bool interpolate) {
return NULL;
}
@ -59,6 +67,10 @@ bool FurnaceGUIRender::newFrame() {
return true;
}
bool FurnaceGUIRender::canVSync() {
return true;
}
void FurnaceGUIRender::createFontsTexture() {
}
@ -71,6 +83,9 @@ void FurnaceGUIRender::renderGUI() {
void FurnaceGUIRender::wipe(float alpha) {
}
void FurnaceGUIRender::drawOsc(float* data, size_t len, ImVec2 pos0, ImVec2 pos1, ImVec4 color, ImVec2 canvasSize, float lineWidth) {
}
void FurnaceGUIRender::present() {
}
@ -78,14 +93,45 @@ bool FurnaceGUIRender::getOutputSize(int& w, int& h) {
return false;
}
bool FurnaceGUIRender::supportsDrawOsc() {
return false;
}
int FurnaceGUIRender::getWindowFlags() {
return 0;
}
int FurnaceGUIRender::getMaxTextureWidth() {
return 0;
}
int FurnaceGUIRender::getMaxTextureHeight() {
return 0;
}
const char* FurnaceGUIRender::getBackendName() {
return "Dummy";
}
const char* FurnaceGUIRender::getVendorName() {
return "N/A";
}
const char* FurnaceGUIRender::getDeviceName() {
return "N/A";
}
const char* FurnaceGUIRender::getAPIVersion() {
return "N/A";
}
void FurnaceGUIRender::setSwapInterval(int swapInterval) {
}
void FurnaceGUIRender::preInit() {
}
bool FurnaceGUIRender::init(SDL_Window* win) {
bool FurnaceGUIRender::init(SDL_Window* win, int swapInterval) {
return false;
}

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2023 tildearrow and contributors
* Copyright (C) 2021-2024 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -22,6 +22,7 @@
#include <SDL_syswm.h>
#include "backends/imgui_impl_dx11.h"
#include "../../ta-log.h"
#include "../../utfutils.h"
typedef HRESULT (__stdcall *D3DCompile_t)(LPCVOID,SIZE_T,LPCSTR,D3D_SHADER_MACRO*,ID3DInclude*,LPCSTR,LPCSTR,UINT,UINT,ID3DBlob**,ID3DBlob*);
@ -199,7 +200,7 @@ bool FurnaceGUIRenderDX11::updateTexture(FurnaceGUITexture* which, void* data, i
return true;
}
FurnaceGUITexture* FurnaceGUIRenderDX11::createTexture(bool dynamic, int width, int height) {
FurnaceGUITexture* FurnaceGUIRenderDX11::createTexture(bool dynamic, int width, int height, bool interpolate) {
D3D11_TEXTURE2D_DESC texDesc;
D3D11_SHADER_RESOURCE_VIEW_DESC viewDesc;
ID3D11Texture2D* tex=NULL;
@ -290,7 +291,11 @@ void FurnaceGUIRenderDX11::clear(ImVec4 color) {
}
bool FurnaceGUIRenderDX11::newFrame() {
ImGui_ImplDX11_NewFrame();
return ImGui_ImplDX11_NewFrame();
}
bool FurnaceGUIRenderDX11::canVSync() {
// TODO: find out how to retrieve VSync status
return true;
}
@ -346,7 +351,7 @@ void FurnaceGUIRenderDX11::wipe(float alpha) {
}
void FurnaceGUIRenderDX11::present() {
HRESULT result=swapchain->Present(1,0);
HRESULT result=swapchain->Present(swapInterval,0);
if (result==DXGI_ERROR_DEVICE_REMOVED || result==DXGI_ERROR_DEVICE_RESET) {
dead=true;
} else if (result!=S_OK && result!=DXGI_STATUS_OCCLUDED) {
@ -364,6 +369,34 @@ int FurnaceGUIRenderDX11::getWindowFlags() {
return 0;
}
int FurnaceGUIRenderDX11::getMaxTextureWidth() {
return maxWidth;
}
int FurnaceGUIRenderDX11::getMaxTextureHeight() {
return maxHeight;
}
const char* FurnaceGUIRenderDX11::getBackendName() {
return "DirectX 11";
}
const char* FurnaceGUIRenderDX11::getVendorName() {
return vendorName.c_str();
}
const char* FurnaceGUIRenderDX11::getDeviceName() {
return deviceName.c_str();
}
const char* FurnaceGUIRenderDX11::getAPIVersion() {
return apiVersion.c_str();
}
void FurnaceGUIRenderDX11::setSwapInterval(int swapInt) {
swapInterval=swapInt;
}
void FurnaceGUIRenderDX11::preInit() {
}
@ -374,7 +407,7 @@ const float wipeVertices[4][4]={
{ 1.0, 1.0, 0.0, 1.0}
};
bool FurnaceGUIRenderDX11::init(SDL_Window* win) {
bool FurnaceGUIRenderDX11::init(SDL_Window* win, int swapInt) {
SDL_SysWMinfo sysWindow;
D3D_FEATURE_LEVEL featureLevel;
@ -385,6 +418,9 @@ bool FurnaceGUIRenderDX11::init(SDL_Window* win) {
}
HWND window=(HWND)sysWindow.info.win.window;
// prepare swapchain
swapInterval=swapInt;
DXGI_SWAP_CHAIN_DESC chainDesc;
memset(&chainDesc,0,sizeof(chainDesc));
chainDesc.BufferDesc.Width=0;
@ -401,12 +437,51 @@ bool FurnaceGUIRenderDX11::init(SDL_Window* win) {
chainDesc.SwapEffect=DXGI_SWAP_EFFECT_DISCARD;
chainDesc.Flags=DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
// initialize
HRESULT result=D3D11CreateDeviceAndSwapChain(NULL,D3D_DRIVER_TYPE_HARDWARE,NULL,0,possibleFeatureLevels,2,D3D11_SDK_VERSION,&chainDesc,&swapchain,&device,&featureLevel,&context);
if (result!=S_OK) {
logE("could not create device and/or swap chain! %.8x",result);
return false;
}
IDXGIDevice* giDevice=NULL;
IDXGIAdapter* adapter=NULL;
result=device->QueryInterface(__uuidof(IDXGIDevice),(void**)&giDevice);
if (result==S_OK) {
result=giDevice->GetAdapter(&adapter);
if (result==S_OK) {
DXGI_ADAPTER_DESC adapterDesc;
result=adapter->GetDesc(&adapterDesc);
if (result!=S_OK) {
logE("could not get adapter info! %.8x",result);
} else {
deviceName=utf16To8(adapterDesc.Description);
vendorName=fmt::sprintf("%.4x:%.4x",adapterDesc.VendorId,adapterDesc.DeviceId);
logV("device: %s",deviceName);
}
} else {
logE("could not get adapter! %.8x",result);
logE("won't be able to get adapter info...");
}
} else {
logE("could not query interface! %.8x",result);
logE("won't be able to get adapter info...");
}
if (featureLevel>=0xb000) {
maxWidth=16384;
maxHeight=16384;
} else if (featureLevel>=0xa000) {
maxWidth=8192;
maxHeight=8192;
} else {
maxWidth=4096;
maxHeight=4096;
}
apiVersion=fmt::sprintf("%d.%d",((int)featureLevel)>>12,((int)featureLevel)>>8);
// https://github.com/ocornut/imgui/pull/638
D3DCompile_t D3DCompile=NULL;
char dllBuffer[20];

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2023 tildearrow and contributors
* Copyright (C) 2021-2024 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -41,7 +41,7 @@ class FurnaceGUIRenderDX11: public FurnaceGUIRender {
ID3D11BlendState* omBlendState;
ID3D11Buffer* quadVertex;
int outW, outH;
int outW, outH, swapInterval;
bool dead;
@ -56,6 +56,9 @@ class FurnaceGUIRenderDX11: public FurnaceGUIRender {
float padding[7];
};
int maxWidth, maxHeight;
String vendorName, deviceName, apiVersion;
bool destroyRenderTarget();
bool createRenderTarget();
@ -64,13 +67,14 @@ class FurnaceGUIRenderDX11: public FurnaceGUIRender {
bool lockTexture(FurnaceGUITexture* which, void** data, int* pitch);
bool unlockTexture(FurnaceGUITexture* which);
bool updateTexture(FurnaceGUITexture* which, void* data, int pitch);
FurnaceGUITexture* createTexture(bool dynamic, int width, int height);
FurnaceGUITexture* createTexture(bool dynamic, int width, int height, bool interpolate=true);
bool destroyTexture(FurnaceGUITexture* which);
void setTextureBlendMode(FurnaceGUITexture* which, FurnaceGUIBlendMode mode);
void setBlendMode(FurnaceGUIBlendMode mode);
void resized(const SDL_Event& ev);
void clear(ImVec4 color);
bool newFrame();
bool canVSync();
void createFontsTexture();
void destroyFontsTexture();
void renderGUI();
@ -78,8 +82,15 @@ class FurnaceGUIRenderDX11: public FurnaceGUIRender {
void present();
bool getOutputSize(int& w, int& h);
int getWindowFlags();
int getMaxTextureWidth();
int getMaxTextureHeight();
const char* getBackendName();
const char* getVendorName();
const char* getDeviceName();
const char* getAPIVersion();
void setSwapInterval(int swapInterval);
void preInit();
bool init(SDL_Window* win);
bool init(SDL_Window* win, int swapInterval);
void initGUI(SDL_Window* win);
void quitGUI();
bool quit();
@ -94,10 +105,13 @@ class FurnaceGUIRenderDX11: public FurnaceGUIRender {
quadVertex(NULL),
outW(0),
outH(0),
swapInterval(1),
dead(false),
sh_wipe_vertex(NULL),
sh_wipe_fragment(NULL),
sh_wipe_inputLayout(NULL),
sh_wipe_uniform(NULL) {
sh_wipe_uniform(NULL),
maxWidth(8192),
maxHeight(8192) {
}
};

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2023 tildearrow and contributors
* Copyright (C) 2021-2024 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -35,6 +35,7 @@ PFNGLBINDBUFFERPROC furBindBuffer=NULL;
PFNGLBUFFERDATAPROC furBufferData=NULL;
PFNGLVERTEXATTRIBPOINTERPROC furVertexAttribPointer=NULL;
PFNGLENABLEVERTEXATTRIBARRAYPROC furEnableVertexAttribArray=NULL;
PFNGLDISABLEVERTEXATTRIBARRAYPROC furDisableVertexAttribArray=NULL;
PFNGLACTIVETEXTUREPROC furActiveTexture=NULL;
PFNGLCREATESHADERPROC furCreateShader=NULL;
@ -45,10 +46,15 @@ PFNGLATTACHSHADERPROC furAttachShader=NULL;
PFNGLBINDATTRIBLOCATIONPROC furBindAttribLocation=NULL;
PFNGLCREATEPROGRAMPROC furCreateProgram=NULL;
PFNGLLINKPROGRAMPROC furLinkProgram=NULL;
PFNGLDELETEPROGRAMPROC furDeleteProgram=NULL;
PFNGLDELETESHADERPROC furDeleteShader=NULL;
PFNGLGETPROGRAMIVPROC furGetProgramiv=NULL;
PFNGLUSEPROGRAMPROC furUseProgram=NULL;
PFNGLGETUNIFORMLOCATIONPROC furGetUniformLocation=NULL;
PFNGLUNIFORM1FPROC furUniform1f=NULL;
PFNGLUNIFORM2FPROC furUniform2f=NULL;
PFNGLUNIFORM1IPROC furUniform1i=NULL;
PFNGLUNIFORM4FVPROC furUniform4fv=NULL;
PFNGLGETSHADERINFOLOGPROC furGetShaderInfoLog=NULL;
#ifndef USE_GLES
@ -68,35 +74,155 @@ class FurnaceGLTexture: public FurnaceGUITexture {
};
#ifdef USE_GLES
const char* sh_wipe_srcV=
const char* sh_wipe_srcV_ES2=
"attribute vec4 fur_position;\n"
"void main() {\n"
" gl_Position=fur_position;\n"
"}\n";
const char* sh_wipe_srcF=
const char* sh_wipe_srcF_ES2=
"uniform float uAlpha;\n"
"void main() {\n"
" gl_FragColor=vec4(0.0,0.0,0.0,uAlpha);\n"
"}\n";
const char* sh_oscRender_srcV=
"attribute vec4 fur_position;\n"
"attribute vec2 fur_texCoord;\n"
"varying vec2 fur_fragCoord;\n"
"void main() {\n"
" gl_Position=fur_position;\n"
" fur_fragCoord=fur_texCoord;\n"
"}\n";
// thank you akumanatt
const char* sh_oscRender_srcF=
"precision highp float;\n"
"uniform vec4 uColor;\n"
"uniform vec2 uResolution;\n"
"uniform float uLineWidth;\n"
"uniform sampler2D oscVal;\n"
"varying vec2 fur_fragCoord;\n"
"const float oneStep=1.0/2048.0;\n"
"void main() {\n"
" float alpha=0.0;\n"
" float xMax=ceil(fur_fragCoord.x+uLineWidth);\n"
" float valmax=-1024.0;\n"
" float valmin=1024.0;\n"
" for (float x=floor(fur_fragCoord.x-uLineWidth); x<=xMax; x+=1.0) {\n"
" float val=texture2D(oscVal,vec2(x*oneStep,1.0)).x;\n"
" if (val>valmax) valmax=val;\n"
" if (val<valmin) valmin=val;\n"
" }\n"
" if ((fur_fragCoord.y-uLineWidth)>valmax*uResolution.y) discard;\n"
" if ((fur_fragCoord.y+uLineWidth)<valmin*uResolution.y) discard;\n"
" float slope=abs(valmax-valmin)*uResolution.y;\n"
" float slopeMul=pow(2.0,ceil(log2(ceil(slope))));\n"
" float slopeDiv=min(1.0,1.0/slopeMul);\n"
" float xRight=ceil(fur_fragCoord.x+uLineWidth);\n"
" for (float x=max(0.0,floor(fur_fragCoord.x-uLineWidth)); x<=xRight; x+=slopeDiv) {\n"
" float val0=texture2D(oscVal,vec2(floor(x)*oneStep,1.0)).x;\n"
" float val1=texture2D(oscVal,vec2(floor(x+1.0)*oneStep,1.0)).x;\n"
" float val=mix(val0,val1,fract(x))*uResolution.y;\n"
" alpha+=clamp(uLineWidth-distance(vec2(fur_fragCoord.x,fur_fragCoord.y),vec2(x,val)),0.0,1.0);\n"
" }\n"
" if (slope>1.0) {\n"
" gl_FragColor = vec4(uColor.xyz,uColor.w*clamp(alpha*(1.0+uResolution.y*pow(slope/uResolution.y,2.0))/(uLineWidth*slopeMul),0.0,1.0));\n"
" } else {\n"
" gl_FragColor = vec4(uColor.xyz,uColor.w*clamp(alpha/uLineWidth,0.0,1.0));\n"
" }\n"
"}\n";
#else
const char* sh_wipe_srcV=
const char* sh_wipe_srcV_130=
"#version 130\n"
"in vec4 fur_position;\n"
"void main() {\n"
" gl_Position=fur_position;\n"
"}\n";
const char* sh_wipe_srcF=
const char* sh_wipe_srcF_130=
"#version 130\n"
"uniform float uAlpha;\n"
"out vec4 fur_FragColor;\n"
"void main() {\n"
" fur_FragColor=vec4(0.0,0.0,0.0,uAlpha);\n"
"}\n";
const char* sh_wipe_srcV_110=
"#version 110\n"
"attribute vec4 fur_position;\n"
"void main() {\n"
" gl_Position=fur_position;\n"
"}\n";
const char* sh_wipe_srcF_110=
"#version 110\n"
"uniform float uAlpha;\n"
"void main() {\n"
" gl_FragColor=vec4(0.0,0.0,0.0,uAlpha);\n"
"}\n";
const char* sh_oscRender_srcV=
"#version 130\n"
"in vec4 fur_position;\n"
"in vec2 fur_texCoord;\n"
"out vec2 fur_fragCoord;\n"
"void main() {\n"
" gl_Position=fur_position;\n"
" fur_fragCoord=fur_texCoord;\n"
"}\n";
// thank you akumanatt
const char* sh_oscRender_srcF=
"#version 130\n"
"uniform vec4 uColor;\n"
"uniform vec2 uResolution;\n"
"uniform float uLineWidth;\n"
"uniform sampler1D oscVal;\n"
"in vec2 fur_fragCoord;\n"
"out vec4 fur_FragColor;\n"
"void main() {\n"
" float alpha=0.0;\n"
" float xMax=ceil(fur_fragCoord.x+uLineWidth);\n"
" float valmax=-1024.0;\n"
" float valmin=1024.0;\n"
" for (float x=floor(fur_fragCoord.x-uLineWidth); x<=xMax; x+=1.0) {\n"
" float val=texelFetch(oscVal,int(x),0).x;\n"
" if (val>valmax) valmax=val;\n"
" if (val<valmin) valmin=val;\n"
" }\n"
" if ((fur_fragCoord.y-uLineWidth)>valmax*uResolution.y) discard;\n"
" if ((fur_fragCoord.y+uLineWidth)<valmin*uResolution.y) discard;\n"
" float slope=abs(valmax-valmin)*uResolution.y;\n"
" float slopeMul=pow(2.0,ceil(log2(ceil(slope))));\n"
" float slopeDiv=min(1.0,1.0/slopeMul);\n"
" float xRight=ceil(fur_fragCoord.x+uLineWidth);\n"
" for (float x=max(0.0,floor(fur_fragCoord.x-uLineWidth)); x<=xRight; x+=slopeDiv) {\n"
" float val0=texelFetch(oscVal,int(x),0).x;\n"
" float val1=texelFetch(oscVal,int(x)+1,0).x;\n"
" float val=mix(val0,val1,fract(x))*uResolution.y;\n"
" alpha+=max(uLineWidth-distance(vec2(fur_fragCoord.x,fur_fragCoord.y),vec2(x,val)),0.0);\n"
" }\n"
" if (slope>1.0) {\n"
" fur_FragColor = vec4(uColor.xyz,uColor.w*clamp(alpha*(1.0+uResolution.y*pow(slope/uResolution.y,2.0))/(uLineWidth*slopeMul),0.0,1.0));\n"
" } else {\n"
" fur_FragColor = vec4(uColor.xyz,uColor.w*clamp(alpha/uLineWidth,0.0,1.0));\n"
" }\n"
"}\n";
#endif
bool FurnaceGUIRenderGL::createShader(const char* vertexS, const char* fragmentS, int& vertex, int& fragment, int& program) {
const char* sh_wipe_attrib[]={
"fur_position",
NULL
};
const char* sh_oscRender_attrib[]={
"fur_position",
"fur_texCoord",
NULL
};
bool FurnaceGUIRenderGL::createShader(const char* vertexS, const char* fragmentS, int& vertex, int& fragment, int& program, const char** attribNames) {
int status;
char infoLog[4096];
int infoLogLen;
@ -126,13 +252,20 @@ bool FurnaceGUIRenderGL::createShader(const char* vertexS, const char* fragmentS
furGetShaderiv(fragment,GL_COMPILE_STATUS,&status);
if (!status) {
logW("failed to compile fragment shader");
furGetShaderInfoLog(fragment,4095,&infoLogLen,infoLog);
infoLog[infoLogLen]=0;
logW("%s",infoLog);
return false;
}
program=furCreateProgram();
furAttachShader(program,vertex);
furAttachShader(program,fragment);
furBindAttribLocation(program,0,"fur_position");
if (attribNames!=NULL) {
for (int i=0; attribNames[i]; i++) {
furBindAttribLocation(program,i,attribNames[i]);
}
}
furLinkProgram(program);
furGetProgramiv(program,GL_LINK_STATUS,&status);
if (!status) {
@ -182,12 +315,17 @@ bool FurnaceGUIRenderGL::updateTexture(FurnaceGUITexture* which, void* data, int
return true;
}
FurnaceGUITexture* FurnaceGUIRenderGL::createTexture(bool dynamic, int width, int height) {
FurnaceGUITexture* FurnaceGUIRenderGL::createTexture(bool dynamic, int width, int height, bool interpolate) {
FurnaceGLTexture* t=new FurnaceGLTexture;
C(glGenTextures(1,&t->id));
C(glBindTexture(GL_TEXTURE_2D,t->id));
C(glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR));
C(glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR));
if (interpolate) {
C(glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR));
C(glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR));
} else {
C(glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST));
C(glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST));
}
C(glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA,width,height,0,GL_RGBA,PIXEL_FORMAT,NULL));
C(furActiveTexture(GL_TEXTURE0));
t->width=width;
@ -233,8 +371,11 @@ void FurnaceGUIRenderGL::clear(ImVec4 color) {
}
bool FurnaceGUIRenderGL::newFrame() {
ImGui_ImplOpenGL3_NewFrame();
return true;
return ImGui_ImplOpenGL3_NewFrame();
}
bool FurnaceGUIRenderGL::canVSync() {
return swapIntervalSet;
}
void FurnaceGUIRenderGL::createFontsTexture() {
@ -279,6 +420,114 @@ void FurnaceGUIRenderGL::wipe(float alpha) {
C(glDrawArrays(GL_TRIANGLE_STRIP,0,4));
}
void FurnaceGUIRenderGL::drawOsc(float* data, size_t len, ImVec2 pos0, ImVec2 pos1, ImVec4 color, ImVec2 canvasSize, float lineWidth) {
if (!sh_oscRender_have) return;
if (!furUseProgram) return;
if (!furUniform4fv) return;
if (!furUniform1f) return;
if (!furUniform2f) return;
if (!furUniform1i) return;
if (len>2048) {
logW("len is %d!",len);
len=2048;
}
memcpy(oscData,data,len*sizeof(float));
if (len<2048) {
oscData[len]=oscData[len-1];
}
int lastArrayBuf=0;
int lastElemArrayBuf=0;
int lastTex=0;
int lastProgram=0;
int lastActiveTex=0;
C(glGetIntegerv(GL_ACTIVE_TEXTURE,&lastActiveTex));
C(furActiveTexture(GL_TEXTURE0));
#ifdef USE_GLES
C(glGetIntegerv(GL_TEXTURE_BINDING_2D,&lastTex));
#else
C(glGetIntegerv(GL_TEXTURE_BINDING_1D,&lastTex));
#endif
#ifdef USE_GLES
C(glBindTexture(GL_TEXTURE_2D,oscDataTex));
C(glTexImage2D(GL_TEXTURE_2D,0,GL_RED_EXT,2048,1,0,GL_RED_EXT,GL_FLOAT,oscData));
#else
C(glBindTexture(GL_TEXTURE_1D,oscDataTex));
C(glTexImage1D(GL_TEXTURE_1D,0,GL_R32F,2048,0,GL_RED,GL_FLOAT,oscData));
#endif
//C(glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA));
//C(glEnable(GL_BLEND));
//float width=fabs(pos1.x-pos0.x);
float height=fabs(pos1.y-pos0.y)*0.5;
pos0.x=(2.0f*pos0.x/canvasSize.x)-1.0f;
pos0.y=1.0f-(2.0f*pos0.y/canvasSize.y);
pos1.x=(2.0f*pos1.x/canvasSize.x)-1.0f;
pos1.y=1.0f-(2.0f*pos1.y/canvasSize.y);
oscVertex[0][0]=pos0.x;
oscVertex[0][1]=pos1.y;
oscVertex[0][2]=0.0f;
oscVertex[0][3]=-height;
oscVertex[1][0]=pos1.x;
oscVertex[1][1]=pos1.y;
oscVertex[1][2]=(float)len;
oscVertex[1][3]=-height;
oscVertex[2][0]=pos0.x;
oscVertex[2][1]=pos0.y;
oscVertex[2][2]=0.0f;
oscVertex[2][3]=height;
oscVertex[3][0]=pos1.x;
oscVertex[3][1]=pos0.y;
oscVertex[3][2]=(float)len;
oscVertex[3][3]=height;
C(glGetIntegerv(GL_ARRAY_BUFFER_BINDING,&lastArrayBuf));
C(glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING,&lastElemArrayBuf));
C(furBindBuffer(GL_ARRAY_BUFFER,oscVertexBuf));
C(furBindBuffer(GL_ELEMENT_ARRAY_BUFFER,0));
C(furBufferData(GL_ARRAY_BUFFER,sizeof(oscVertex),oscVertex,GL_STATIC_DRAW));
C(furVertexAttribPointer(0,2,GL_FLOAT,GL_FALSE,4*sizeof(float),NULL));
C(furVertexAttribPointer(1,2,GL_FLOAT,GL_FALSE,4*sizeof(float),(void*)(2*sizeof(float))));
C(furEnableVertexAttribArray(0));
C(furEnableVertexAttribArray(1));
C(glGetIntegerv(GL_CURRENT_PROGRAM,&lastProgram));
C(furUseProgram(sh_oscRender_program));
C(furUniform4fv(sh_oscRender_uColor,1,(float*)&color));
if (lineWidth<=1.0) {
C(furUniform1f(sh_oscRender_uLineWidth,lineWidth));
} else {
C(furUniform1f(sh_oscRender_uLineWidth,0.5+lineWidth*0.5));
}
C(furUniform2f(sh_oscRender_uResolution,2048.0f,height));
C(furUniform1i(sh_oscRender_oscVal,0));
C(glDrawArrays(GL_TRIANGLE_STRIP,0,4));
C(furDisableVertexAttribArray(1));
// restore state
C(furUseProgram(lastProgram));
C(furBindBuffer(GL_ARRAY_BUFFER,lastArrayBuf));
C(furBindBuffer(GL_ELEMENT_ARRAY_BUFFER,lastElemArrayBuf));
#ifdef USE_GLES
C(glBindTexture(GL_TEXTURE_2D,lastTex));
#else
C(glBindTexture(GL_TEXTURE_1D,lastTex));
#endif
C(furActiveTexture(lastActiveTex));
}
void FurnaceGUIRenderGL::present() {
SDL_GL_SwapWindow(sdlWin);
C(glFlush());
@ -289,26 +538,68 @@ bool FurnaceGUIRenderGL::getOutputSize(int& w, int& h) {
return true;
}
bool FurnaceGUIRenderGL::supportsDrawOsc() {
return sh_oscRender_have;
}
int FurnaceGUIRenderGL::getWindowFlags() {
return SDL_WINDOW_OPENGL;
}
int FurnaceGUIRenderGL::getMaxTextureWidth() {
return maxWidth;
}
int FurnaceGUIRenderGL::getMaxTextureHeight() {
return maxHeight;
}
const char* FurnaceGUIRenderGL::getBackendName() {
return backendName.c_str();
}
const char* FurnaceGUIRenderGL::getVendorName() {
return vendorName.c_str();
}
const char* FurnaceGUIRenderGL::getDeviceName() {
return deviceName.c_str();
}
const char* FurnaceGUIRenderGL::getAPIVersion() {
return apiVersion.c_str();
}
void FurnaceGUIRenderGL::setSwapInterval(int swapInterval) {
SDL_GL_SetSwapInterval(swapInterval);
if (swapInterval>0 && SDL_GL_GetSwapInterval()==0) {
swapIntervalSet=false;
logW("tried to enable VSync but couldn't!");
} else {
swapIntervalSet=true;
}
}
void FurnaceGUIRenderGL::preInit() {
#if defined(USE_GLES)
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, 0);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS,0);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK,SDL_GL_CONTEXT_PROFILE_ES);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION,2);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION,0);
#elif defined(__APPLE__)
// not recommended...
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS,SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK,SDL_GL_CONTEXT_PROFILE_CORE);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION,3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION,2);
if (glVer==2) {
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION,2);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION,0);
} else {
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION,3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION,2);
}
#else
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS,0);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION,3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK,SDL_GL_CONTEXT_PROFILE_CORE);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION,glVer);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION,0);
#endif
@ -333,20 +624,27 @@ void FurnaceGUIRenderGL::preInit() {
logW(_s " not found"); \
}
bool FurnaceGUIRenderGL::init(SDL_Window* win) {
bool FurnaceGUIRenderGL::init(SDL_Window* win, int swapInterval) {
sdlWin=win;
context=SDL_GL_CreateContext(win);
if (context==NULL) {
return false;
}
SDL_GL_MakeCurrent(win,context);
SDL_GL_SetSwapInterval(1);
SDL_GL_SetSwapInterval(swapInterval);
if (swapInterval>0 && SDL_GL_GetSwapInterval()==0) {
swapIntervalSet=false;
logW("tried to enable VSync but couldn't!");
} else {
swapIntervalSet=true;
}
LOAD_PROC_MANDATORY(furGenBuffers,PFNGLGENBUFFERSPROC,"glGenBuffers");
LOAD_PROC_MANDATORY(furBindBuffer,PFNGLBINDBUFFERPROC,"glBindBuffer");
LOAD_PROC_MANDATORY(furBufferData,PFNGLBUFFERDATAPROC,"glBufferData");
LOAD_PROC_MANDATORY(furVertexAttribPointer,PFNGLVERTEXATTRIBPOINTERPROC,"glVertexAttribPointer");
LOAD_PROC_MANDATORY(furEnableVertexAttribArray,PFNGLENABLEVERTEXATTRIBARRAYPROC,"glEnableVertexAttribArray");
LOAD_PROC_MANDATORY(furDisableVertexAttribArray,PFNGLDISABLEVERTEXATTRIBARRAYPROC,"glDisableVertexAttribArray");
LOAD_PROC_MANDATORY(furActiveTexture,PFNGLACTIVETEXTUREPROC,"glActiveTexture");
LOAD_PROC_OPTIONAL(furCreateShader,PFNGLCREATESHADERPROC,"glCreateShader");
@ -359,19 +657,99 @@ bool FurnaceGUIRenderGL::init(SDL_Window* win) {
LOAD_PROC_OPTIONAL(furLinkProgram,PFNGLLINKPROGRAMPROC,"glLinkProgram");
LOAD_PROC_OPTIONAL(furGetProgramiv,PFNGLGETPROGRAMIVPROC,"glGetProgramiv");
LOAD_PROC_OPTIONAL(furUseProgram,PFNGLUSEPROGRAMPROC,"glUseProgram");
LOAD_PROC_OPTIONAL(furDeleteProgram,PFNGLDELETEPROGRAMPROC,"glDeleteProgram");
LOAD_PROC_OPTIONAL(furDeleteShader,PFNGLDELETESHADERPROC,"glDeleteShader");
LOAD_PROC_OPTIONAL(furGetUniformLocation,PFNGLGETUNIFORMLOCATIONPROC,"glGetUniformLocation");
LOAD_PROC_OPTIONAL(furUniform1f,PFNGLUNIFORM1FPROC,"glUniform1f");
LOAD_PROC_OPTIONAL(furUniform2f,PFNGLUNIFORM2FPROC,"glUniform2f");
LOAD_PROC_OPTIONAL(furUniform1i,PFNGLUNIFORM1IPROC,"glUniform1i");
LOAD_PROC_OPTIONAL(furUniform4fv,PFNGLUNIFORM4FVPROC,"glUniform4fv");
LOAD_PROC_OPTIONAL(furGetShaderInfoLog,PFNGLGETSHADERINFOLOGPROC,"glGetShaderInfoLog");
#ifndef USE_GLES
LOAD_PROC_OPTIONAL(furGetGraphicsResetStatusARB,PFNGLGETGRAPHICSRESETSTATUSARBPROC,"glGetGraphicsResetStatusARB");
#else
backendName="OpenGL ES 2.0";
#endif
if (createShader(sh_wipe_srcV,sh_wipe_srcF,sh_wipe_vertex,sh_wipe_fragment,sh_wipe_program)) {
// information
const char* next=(const char*)glGetString(GL_VENDOR);
if (next==NULL) {
vendorName="???";
} else {
vendorName=next;
}
next=(const char*)glGetString(GL_RENDERER);
if (next==NULL) {
deviceName="???";
} else {
deviceName=next;
}
next=(const char*)glGetString(GL_VERSION);
if (next==NULL) {
apiVersion="???";
} else {
apiVersion=next;
}
int maxSize=1024;
glGetIntegerv(GL_MAX_TEXTURE_SIZE,&maxSize);
maxWidth=maxSize;
maxHeight=maxSize;
// texture for osc renderer
if (glVer==3) {
C(glGenTextures(1,&oscDataTex));
#ifdef USE_GLES
C(glBindTexture(GL_TEXTURE_2D,oscDataTex));
C(glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST));
C(glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST));
C(glTexImage2D(GL_TEXTURE_2D,0,GL_RED_EXT,2048,1,0,GL_RED_EXT,GL_FLOAT,NULL));
#else
C(glBindTexture(GL_TEXTURE_1D,oscDataTex));
C(glTexParameteri(GL_TEXTURE_1D,GL_TEXTURE_MIN_FILTER,GL_NEAREST));
C(glTexParameteri(GL_TEXTURE_1D,GL_TEXTURE_MAG_FILTER,GL_NEAREST));
C(glTexImage1D(GL_TEXTURE_1D,0,GL_RED,2048,0,GL_RED,GL_FLOAT,NULL));
#endif
C(furActiveTexture(GL_TEXTURE0));
}
// create shaders
#ifdef USE_GLES
if ((sh_wipe_have=createShader(sh_wipe_srcV_ES2,sh_wipe_srcF_ES2,sh_wipe_vertex,sh_wipe_fragment,sh_wipe_program,sh_wipe_attrib))==true) {
sh_wipe_uAlpha=furGetUniformLocation(sh_wipe_program,"uAlpha");
}
if ((sh_oscRender_have=createShader(sh_oscRender_srcV,sh_oscRender_srcF,sh_oscRender_vertex,sh_oscRender_fragment,sh_oscRender_program,sh_oscRender_attrib))==true) {
sh_oscRender_uColor=furGetUniformLocation(sh_oscRender_program,"uColor");
sh_oscRender_uLineWidth=furGetUniformLocation(sh_oscRender_program,"uLineWidth");
sh_oscRender_uResolution=furGetUniformLocation(sh_oscRender_program,"uResolution");
sh_oscRender_oscVal=furGetUniformLocation(sh_oscRender_program,"oscVal");
}
#else
if (glVer==3) {
if ((sh_wipe_have=createShader(sh_wipe_srcV_130,sh_wipe_srcF_130,sh_wipe_vertex,sh_wipe_fragment,sh_wipe_program,sh_wipe_attrib))==true) {
sh_wipe_uAlpha=furGetUniformLocation(sh_wipe_program,"uAlpha");
}
if ((sh_oscRender_have=createShader(sh_oscRender_srcV,sh_oscRender_srcF,sh_oscRender_vertex,sh_oscRender_fragment,sh_oscRender_program,sh_oscRender_attrib))==true) {
sh_oscRender_uColor=furGetUniformLocation(sh_oscRender_program,"uColor");
sh_oscRender_uLineWidth=furGetUniformLocation(sh_oscRender_program,"uLineWidth");
sh_oscRender_uResolution=furGetUniformLocation(sh_oscRender_program,"uResolution");
sh_oscRender_oscVal=furGetUniformLocation(sh_oscRender_program,"oscVal");
}
} else {
if ((sh_wipe_have=createShader(sh_wipe_srcV_110,sh_wipe_srcF_110,sh_wipe_vertex,sh_wipe_fragment,sh_wipe_program,sh_wipe_attrib))==true) {
sh_wipe_uAlpha=furGetUniformLocation(sh_wipe_program,"uAlpha");
}
sh_oscRender_have=false;
}
#endif
C(furGenBuffers(1,&quadBuf));
C(furGenBuffers(1,&oscVertexBuf));
return true;
}
@ -400,3 +778,14 @@ bool FurnaceGUIRenderGL::isDead() {
return false;
#endif
}
void FurnaceGUIRenderGL::setVersion(unsigned char ver) {
glVer=ver;
if (glVer==3) {
backendName="OpenGL 3.0";
} else if (glVer==2) {
backendName="OpenGL 2.0";
} else {
backendName="OpenGL BUG.REPORT";
}
}

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2023 tildearrow and contributors
* Copyright (C) 2021-2024 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -24,6 +24,10 @@ class FurnaceGUIRenderGL: public FurnaceGUIRender {
SDL_Window* sdlWin;
float quadVertex[4][3];
unsigned int quadBuf;
float oscVertex[4][4];
unsigned int oscVertexBuf;
unsigned int oscDataTex;
float oscData[2048];
// SHADERS //
// -> wipe
@ -31,36 +35,70 @@ class FurnaceGUIRenderGL: public FurnaceGUIRender {
int sh_wipe_fragment;
int sh_wipe_program;
int sh_wipe_uAlpha;
bool sh_wipe_have;
// -> oscRender
int sh_oscRender_vertex;
int sh_oscRender_fragment;
int sh_oscRender_program;
int sh_oscRender_uColor;
int sh_oscRender_uLineWidth;
int sh_oscRender_uResolution;
int sh_oscRender_oscVal;
bool sh_oscRender_have;
bool createShader(const char* vertexS, const char* fragmentS, int& vertex, int& fragment, int& program);
bool swapIntervalSet;
unsigned char glVer;
int maxWidth, maxHeight;
String backendName, vendorName, deviceName, apiVersion;
bool createShader(const char* vertexS, const char* fragmentS, int& vertex, int& fragment, int& program, const char** attribNames);
public:
ImTextureID getTextureID(FurnaceGUITexture* which);
bool lockTexture(FurnaceGUITexture* which, void** data, int* pitch);
bool unlockTexture(FurnaceGUITexture* which);
bool updateTexture(FurnaceGUITexture* which, void* data, int pitch);
FurnaceGUITexture* createTexture(bool dynamic, int width, int height);
FurnaceGUITexture* createTexture(bool dynamic, int width, int height, bool interpolate=true);
bool destroyTexture(FurnaceGUITexture* which);
void setTextureBlendMode(FurnaceGUITexture* which, FurnaceGUIBlendMode mode);
void setBlendMode(FurnaceGUIBlendMode mode);
void clear(ImVec4 color);
bool newFrame();
bool canVSync();
void createFontsTexture();
void destroyFontsTexture();
void renderGUI();
void wipe(float alpha);
void drawOsc(float* data, size_t len, ImVec2 pos0, ImVec2 pos1, ImVec4 color, ImVec2 canvasSize, float lineWidth);
void present();
bool getOutputSize(int& w, int& h);
bool supportsDrawOsc();
int getWindowFlags();
int getMaxTextureWidth();
int getMaxTextureHeight();
const char* getBackendName();
const char* getVendorName();
const char* getDeviceName();
const char* getAPIVersion();
void setSwapInterval(int swapInterval);
void preInit();
bool init(SDL_Window* win);
bool init(SDL_Window* win, int swapInterval);
void initGUI(SDL_Window* win);
void quitGUI();
bool quit();
bool isDead();
void setVersion(unsigned char ver);
FurnaceGUIRenderGL():
context(NULL),
sdlWin(NULL) {
sdlWin(NULL),
swapIntervalSet(true),
glVer(3),
maxWidth(0),
maxHeight(0),
backendName("What?") {
memset(quadVertex,0,4*3*sizeof(float));
memset(oscVertex,0,4*5*sizeof(float));
memset(oscData,0,2048*sizeof(float));
}
};

View file

@ -0,0 +1,337 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2024 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "renderGL1.h"
#include "../../ta-log.h"
#include "SDL_opengl.h"
#include "backends/imgui_impl_opengl2.h"
#include "../engine/bsr.h"
#define C(x) x; if (glGetError()!=GL_NO_ERROR) logW("OpenGL error in %s:%d: " #x,__FILE__,__LINE__);
class FurnaceGL1Texture: public FurnaceGUITexture {
public:
GLuint id;
int width, height, widthReal, heightReal;
unsigned char* lockedData;
FurnaceGL1Texture():
id(0),
width(0),
height(0),
widthReal(0),
heightReal(0),
lockedData(NULL) {}
};
ImTextureID FurnaceGUIRenderGL1::getTextureID(FurnaceGUITexture* which) {
intptr_t ret=((FurnaceGL1Texture*)which)->id;
return (ImTextureID)ret;
}
float FurnaceGUIRenderGL1::getTextureU(FurnaceGUITexture* which) {
FurnaceGL1Texture* t=(FurnaceGL1Texture*)which;
return (float)t->width/(float)t->widthReal;
}
float FurnaceGUIRenderGL1::getTextureV(FurnaceGUITexture* which) {
FurnaceGL1Texture* t=(FurnaceGL1Texture*)which;
return (float)t->height/(float)t->heightReal;
}
bool FurnaceGUIRenderGL1::lockTexture(FurnaceGUITexture* which, void** data, int* pitch) {
FurnaceGL1Texture* t=(FurnaceGL1Texture*)which;
if (t->lockedData!=NULL) return false;
t->lockedData=new unsigned char[t->widthReal*t->heightReal*4];
*data=t->lockedData;
*pitch=t->widthReal*4;
return true;
}
bool FurnaceGUIRenderGL1::unlockTexture(FurnaceGUITexture* which) {
FurnaceGL1Texture* t=(FurnaceGL1Texture*)which;
if (t->lockedData==NULL) return false;
C(glBindTexture(GL_TEXTURE_2D,t->id));
C(glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA,t->widthReal,t->heightReal,0,GL_RGBA,GL_UNSIGNED_BYTE,t->lockedData));
C(glFlush());
delete[] t->lockedData;
t->lockedData=NULL;
return true;
}
bool FurnaceGUIRenderGL1::updateTexture(FurnaceGUITexture* which, void* data, int pitch) {
FurnaceGL1Texture* t=(FurnaceGL1Texture*)which;
if (t->width*4!=pitch) return false;
logV("GL1 updateTexture...");
C(glBindTexture(GL_TEXTURE_2D,t->id));
C(glTexSubImage2D(GL_TEXTURE_2D,0,0,0,t->width,t->height,GL_RGBA,GL_UNSIGNED_BYTE,data));
return true;
}
FurnaceGUITexture* FurnaceGUIRenderGL1::createTexture(bool dynamic, int width, int height, bool interpolate) {
FurnaceGL1Texture* t=new FurnaceGL1Texture;
C(glGenTextures(1,&t->id));
C(glBindTexture(GL_TEXTURE_2D,t->id));
if (interpolate) {
C(glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR));
C(glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR));
} else {
C(glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST));
C(glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST));
}
int widthReal=width;
int heightReal=height;
if ((widthReal&(widthReal-1))!=0) {
widthReal=1<<bsr(width);
}
if ((heightReal&(heightReal-1))!=0) {
heightReal=1<<bsr(height);
}
logV("width: %d (requested)... %d (actual)",width,widthReal);
logV("height: %d (requested)... %d (actual)",height,heightReal);
C(glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA,widthReal,heightReal,0,GL_RGBA,GL_UNSIGNED_BYTE,NULL));
t->width=width;
t->height=height;
t->widthReal=widthReal;
t->heightReal=heightReal;
return t;
}
bool FurnaceGUIRenderGL1::destroyTexture(FurnaceGUITexture* which) {
FurnaceGL1Texture* t=(FurnaceGL1Texture*)which;
C(glDeleteTextures(1,&t->id));
delete t;
return true;
}
void FurnaceGUIRenderGL1::setTextureBlendMode(FurnaceGUITexture* which, FurnaceGUIBlendMode mode) {
}
void FurnaceGUIRenderGL1::setBlendMode(FurnaceGUIBlendMode mode) {
switch (mode) {
case GUI_BLEND_MODE_NONE:
C(glBlendFunc(GL_ONE,GL_ZERO));
C(glDisable(GL_BLEND));
break;
case GUI_BLEND_MODE_BLEND:
C(glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA));
C(glEnable(GL_BLEND));
break;
case GUI_BLEND_MODE_ADD:
C(glBlendFunc(GL_SRC_ALPHA,GL_ONE));
C(glEnable(GL_BLEND));
break;
case GUI_BLEND_MODE_MULTIPLY:
C(glBlendFunc(GL_ZERO,GL_SRC_COLOR));
C(glEnable(GL_BLEND));
break;
}
}
void FurnaceGUIRenderGL1::clear(ImVec4 color) {
SDL_GL_MakeCurrent(sdlWin,context);
C(glClearColor(color.x,color.y,color.z,color.w));
C(glClear(GL_COLOR_BUFFER_BIT));
}
bool FurnaceGUIRenderGL1::newFrame() {
return ImGui_ImplOpenGL2_NewFrame();
}
bool FurnaceGUIRenderGL1::canVSync() {
return swapIntervalSet;
}
void FurnaceGUIRenderGL1::createFontsTexture() {
ImGui_ImplOpenGL2_CreateFontsTexture();
}
void FurnaceGUIRenderGL1::destroyFontsTexture() {
ImGui_ImplOpenGL2_DestroyFontsTexture();
}
void FurnaceGUIRenderGL1::renderGUI() {
ImGui_ImplOpenGL2_RenderDrawData(ImGui::GetDrawData());
}
void FurnaceGUIRenderGL1::wipe(float alpha) {
C(glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA));
C(glEnable(GL_BLEND));
C(glBindTexture(GL_TEXTURE_2D,0));
C(glBegin(GL_TRIANGLE_STRIP));
C(glColor4f(0.0,0.0,0.0,alpha));
C(glNormal3f(0.0f,0.0f,0.0f));
C(glVertex3f(-1.0f,-1.0f,0.0f));
C(glNormal3f(1.0f,0.0f,0.0f));
C(glVertex3f(1.0f,-1.0f,0.0f));
C(glNormal3f(0.0f,1.0f,0.0f));
C(glVertex3f(-1.0f,1.0f,0.0f));
C(glNormal3f(1.0f,1.0f,0.0f));
C(glVertex3f(1.0f,1.0f,0.0f));
C(glEnd());
}
void FurnaceGUIRenderGL1::present() {
SDL_GL_SwapWindow(sdlWin);
C(glFlush());
}
bool FurnaceGUIRenderGL1::getOutputSize(int& w, int& h) {
SDL_GL_GetDrawableSize(sdlWin,&w,&h);
return true;
}
int FurnaceGUIRenderGL1::getWindowFlags() {
return SDL_WINDOW_OPENGL;
}
int FurnaceGUIRenderGL1::getMaxTextureWidth() {
return maxWidth;
}
int FurnaceGUIRenderGL1::getMaxTextureHeight() {
return maxHeight;
}
const char* FurnaceGUIRenderGL1::getBackendName() {
return "OpenGL 1.1";
}
const char* FurnaceGUIRenderGL1::getVendorName() {
return vendorName.c_str();
}
const char* FurnaceGUIRenderGL1::getDeviceName() {
return deviceName.c_str();
}
const char* FurnaceGUIRenderGL1::getAPIVersion() {
return apiVersion.c_str();
}
void FurnaceGUIRenderGL1::setSwapInterval(int swapInterval) {
SDL_GL_SetSwapInterval(swapInterval);
if (swapInterval>0 && SDL_GL_GetSwapInterval()==0) {
swapIntervalSet=false;
logW("tried to enable VSync but couldn't!");
} else {
swapIntervalSet=true;
}
}
void FurnaceGUIRenderGL1::preInit() {
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS,0);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK,0);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION,1);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION,1);
SDL_GL_SetAttribute(SDL_GL_RED_SIZE,8);
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE,8);
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE,8);
SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE,0);
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER,1);
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE,24);
}
#define LOAD_PROC_MANDATORY(_v,_t,_s) \
_v=(_t)SDL_GL_GetProcAddress(_s); \
if (!_v) { \
logE(_s " not found"); \
return false; \
}
#define LOAD_PROC_OPTIONAL(_v,_t,_s) \
_v=(_t)SDL_GL_GetProcAddress(_s); \
if (!_v) { \
logW(_s " not found"); \
}
bool FurnaceGUIRenderGL1::init(SDL_Window* win, int swapInterval) {
sdlWin=win;
context=SDL_GL_CreateContext(win);
if (context==NULL) {
return false;
}
SDL_GL_MakeCurrent(win,context);
SDL_GL_SetSwapInterval(swapInterval);
if (swapInterval>0 && SDL_GL_GetSwapInterval()==0) {
swapIntervalSet=false;
logW("tried to enable VSync but couldn't!");
} else {
swapIntervalSet=true;
}
const char* next=(const char*)glGetString(GL_VENDOR);
if (next==NULL) {
vendorName="???";
} else {
vendorName=next;
}
next=(const char*)glGetString(GL_RENDERER);
if (next==NULL) {
deviceName="???";
} else {
deviceName=next;
}
next=(const char*)glGetString(GL_VERSION);
if (next==NULL) {
apiVersion="???";
} else {
apiVersion=next;
}
int maxSize=1024;
glGetIntegerv(GL_MAX_TEXTURE_SIZE,&maxSize);
maxWidth=maxSize;
maxHeight=maxSize;
return true;
}
void FurnaceGUIRenderGL1::initGUI(SDL_Window* win) {
ImGui_ImplSDL2_InitForOpenGL(win,context);
ImGui_ImplOpenGL2_Init();
}
bool FurnaceGUIRenderGL1::quit() {
if (context==NULL) return false;
SDL_GL_DeleteContext(context);
context=NULL;
return true;
}
void FurnaceGUIRenderGL1::quitGUI() {
ImGui_ImplOpenGL2_Shutdown();
}
// sadly, OpenGL 1.1 doesn't have the ability to recover from death...
bool FurnaceGUIRenderGL1::isDead() {
return false;
}

View file

@ -0,0 +1,72 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2024 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "../gui.h"
class FurnaceGUIRenderGL1: public FurnaceGUIRender {
SDL_GLContext context;
SDL_Window* sdlWin;
bool swapIntervalSet;
int maxWidth, maxHeight;
String vendorName, deviceName, apiVersion;
public:
ImTextureID getTextureID(FurnaceGUITexture* which);
float getTextureU(FurnaceGUITexture* which);
float getTextureV(FurnaceGUITexture* which);
bool lockTexture(FurnaceGUITexture* which, void** data, int* pitch);
bool unlockTexture(FurnaceGUITexture* which);
bool updateTexture(FurnaceGUITexture* which, void* data, int pitch);
FurnaceGUITexture* createTexture(bool dynamic, int width, int height, bool interpolate=true);
bool destroyTexture(FurnaceGUITexture* which);
void setTextureBlendMode(FurnaceGUITexture* which, FurnaceGUIBlendMode mode);
void setBlendMode(FurnaceGUIBlendMode mode);
void clear(ImVec4 color);
bool newFrame();
bool canVSync();
void createFontsTexture();
void destroyFontsTexture();
void renderGUI();
void wipe(float alpha);
void present();
bool getOutputSize(int& w, int& h);
int getWindowFlags();
int getMaxTextureWidth();
int getMaxTextureHeight();
const char* getBackendName();
const char* getVendorName();
const char* getDeviceName();
const char* getAPIVersion();
void setSwapInterval(int swapInterval);
void preInit();
bool init(SDL_Window* win, int swapInterval);
void initGUI(SDL_Window* win);
void quitGUI();
bool quit();
bool isDead();
FurnaceGUIRenderGL1():
context(NULL),
sdlWin(NULL),
swapIntervalSet(true),
maxWidth(0),
maxHeight(0) {
}
};

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2023 tildearrow and contributors
* Copyright (C) 2021-2024 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -19,6 +19,7 @@
#include "renderSDL.h"
#include "backends/imgui_impl_sdlrenderer2.h"
#include "../../ta-log.h"
class FurnaceSDLTexture: public FurnaceGUITexture {
public:
@ -48,8 +49,10 @@ bool FurnaceGUIRenderSDL::updateTexture(FurnaceGUITexture* which, void* data, in
return SDL_UpdateTexture(t->tex,NULL,data,pitch)==0;
}
FurnaceGUITexture* FurnaceGUIRenderSDL::createTexture(bool dynamic, int width, int height) {
FurnaceGUITexture* FurnaceGUIRenderSDL::createTexture(bool dynamic, int width, int height, bool interpolate) {
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY,interpolate?"1":"0");
SDL_Texture* t=SDL_CreateTexture(sdlRend,SDL_PIXELFORMAT_ABGR8888,dynamic?SDL_TEXTUREACCESS_STREAMING:SDL_TEXTUREACCESS_STATIC,width,height);
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY,"1");
if (t==NULL) return NULL;
FurnaceSDLTexture* ret=new FurnaceSDLTexture;
@ -109,6 +112,10 @@ bool FurnaceGUIRenderSDL::newFrame() {
return ImGui_ImplSDLRenderer2_NewFrame();
}
bool FurnaceGUIRenderSDL::canVSync() {
return swapIntervalSet;
}
void FurnaceGUIRenderSDL::createFontsTexture() {
ImGui_ImplSDLRenderer2_CreateFontsTexture();
}
@ -139,11 +146,62 @@ int FurnaceGUIRenderSDL::getWindowFlags() {
return 0;
}
int FurnaceGUIRenderSDL::getMaxTextureWidth() {
if (!hasInfo) return 2048;
return renderInfo.max_texture_width;
}
int FurnaceGUIRenderSDL::getMaxTextureHeight() {
if (!hasInfo) return 2048;
return renderInfo.max_texture_height;
}
const char* FurnaceGUIRenderSDL::getBackendName() {
return "SDL Renderer";
}
const char* FurnaceGUIRenderSDL::getVendorName() {
return "SDL";
}
const char* FurnaceGUIRenderSDL::getDeviceName() {
if (!hasInfo) return "???";
return renderInfo.name;
}
const char* FurnaceGUIRenderSDL::getAPIVersion() {
return "N/A";
}
void FurnaceGUIRenderSDL::setSwapInterval(int swapInterval) {
if (SDL_RenderSetVSync(sdlRend,(swapInterval>=0)?1:0)!=0) {
swapIntervalSet=false;
logW("tried to enable VSync but couldn't!");
} else {
swapIntervalSet=true;
}
}
void FurnaceGUIRenderSDL::preInit() {
}
bool FurnaceGUIRenderSDL::init(SDL_Window* win) {
sdlRend=SDL_CreateRenderer(win,-1,SDL_RENDERER_ACCELERATED|SDL_RENDERER_PRESENTVSYNC|SDL_RENDERER_TARGETTEXTURE);
bool FurnaceGUIRenderSDL::init(SDL_Window* win, int swapInterval) {
logV("creating SDL renderer...");
sdlRend=SDL_CreateRenderer(win,-1,SDL_RENDERER_ACCELERATED|((swapInterval>0)?SDL_RENDERER_PRESENTVSYNC:0)|SDL_RENDERER_TARGETTEXTURE);
if (sdlRend==NULL) return false;
if (SDL_GetRendererInfo(sdlRend,&renderInfo)==0) {
hasInfo=true;
} else {
logE("could not get renderer info! %s",SDL_GetError());
hasInfo=false;
}
if (SDL_RenderSetVSync(sdlRend,(swapInterval>=0)?1:0)!=0) {
swapIntervalSet=false;
logW("tried to enable VSync but couldn't!");
} else {
swapIntervalSet=true;
}
logV("(post creation)");
return (sdlRend!=NULL);
}

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2023 tildearrow and contributors
* Copyright (C) 2021-2024 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -21,17 +21,21 @@
class FurnaceGUIRenderSDL: public FurnaceGUIRender {
SDL_Renderer* sdlRend;
SDL_RendererInfo renderInfo;
bool hasInfo;
bool swapIntervalSet;
public:
ImTextureID getTextureID(FurnaceGUITexture* which);
bool lockTexture(FurnaceGUITexture* which, void** data, int* pitch);
bool unlockTexture(FurnaceGUITexture* which);
bool updateTexture(FurnaceGUITexture* which, void* data, int pitch);
FurnaceGUITexture* createTexture(bool dynamic, int width, int height);
FurnaceGUITexture* createTexture(bool dynamic, int width, int height, bool interpolate=true);
bool destroyTexture(FurnaceGUITexture* which);
void setTextureBlendMode(FurnaceGUITexture* which, FurnaceGUIBlendMode mode);
void setBlendMode(FurnaceGUIBlendMode mode);
void clear(ImVec4 color);
bool newFrame();
bool canVSync();
void createFontsTexture();
void destroyFontsTexture();
void renderGUI();
@ -39,11 +43,20 @@ class FurnaceGUIRenderSDL: public FurnaceGUIRender {
void present();
bool getOutputSize(int& w, int& h);
int getWindowFlags();
int getMaxTextureWidth();
int getMaxTextureHeight();
const char* getBackendName();
const char* getVendorName();
const char* getDeviceName();
const char* getAPIVersion();
void setSwapInterval(int swapInterval);
void preInit();
bool init(SDL_Window* win);
bool init(SDL_Window* win, int swapInterval);
void initGUI(SDL_Window* win);
void quitGUI();
bool quit();
FurnaceGUIRenderSDL():
sdlRend(NULL) {}
};
sdlRend(NULL),
hasInfo(false),
swapIntervalSet(true) {}
};

View file

@ -0,0 +1,183 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2024 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "renderSoftware.h"
#include "imgui_sw.hpp"
#include "../../ta-log.h"
class FurnaceSoftwareTexture: public FurnaceGUITexture {
public:
SWTexture* tex;
FurnaceSoftwareTexture():
tex(NULL) {}
};
ImTextureID FurnaceGUIRenderSoftware::getTextureID(FurnaceGUITexture* which) {
FurnaceSoftwareTexture* t=(FurnaceSoftwareTexture*)which;
return t->tex;
}
bool FurnaceGUIRenderSoftware::lockTexture(FurnaceGUITexture* which, void** data, int* pitch) {
FurnaceSoftwareTexture* t=(FurnaceSoftwareTexture*)which;
if (!t->tex->managed) return false;
*data=t->tex->pixels;
*pitch=t->tex->width*(t->tex->isAlpha?1:4);
return true;
}
bool FurnaceGUIRenderSoftware::unlockTexture(FurnaceGUITexture* which) {
return true;
}
bool FurnaceGUIRenderSoftware::updateTexture(FurnaceGUITexture* which, void* data, int pitch) {
FurnaceSoftwareTexture* t=(FurnaceSoftwareTexture*)which;
if (!t->tex->managed) return false;
memcpy(t->tex->pixels,data,pitch*t->tex->height);
return true;
}
FurnaceGUITexture* FurnaceGUIRenderSoftware::createTexture(bool dynamic, int width, int height, bool interpolate) {
FurnaceSoftwareTexture* ret=new FurnaceSoftwareTexture;
ret->tex=new SWTexture(width,height);
return ret;
}
bool FurnaceGUIRenderSoftware::destroyTexture(FurnaceGUITexture* which) {
FurnaceSoftwareTexture* t=(FurnaceSoftwareTexture*)which;
delete t->tex;
delete t;
return true;
}
void FurnaceGUIRenderSoftware::setTextureBlendMode(FurnaceGUITexture* which, FurnaceGUIBlendMode mode) {
// TODO
}
void FurnaceGUIRenderSoftware::setBlendMode(FurnaceGUIBlendMode mode) {
// TODO
}
void FurnaceGUIRenderSoftware::clear(ImVec4 color) {
SDL_Surface* surf=SDL_GetWindowSurface(sdlWin);
if (!surf) return;
ImU32 clearToWhat=ImGui::ColorConvertFloat4ToU32(color);
clearToWhat=(clearToWhat&0xff00ff00)|((clearToWhat&0xff)<<16)|((clearToWhat&0xff0000)>>16);
bool mustLock=SDL_MUSTLOCK(surf);
if (mustLock) {
if (SDL_LockSurface(surf)!=0) return;
}
unsigned int* pixels=(unsigned int*)surf->pixels;
for (size_t total=surf->w*surf->h; total; total--) {
*(pixels++)=clearToWhat;
}
if (mustLock) {
SDL_UnlockSurface(surf);
}
}
bool FurnaceGUIRenderSoftware::newFrame() {
return ImGui_ImplSW_NewFrame();
}
bool FurnaceGUIRenderSoftware::canVSync() {
return false;
}
void FurnaceGUIRenderSoftware::createFontsTexture() {
ImGui_ImplSW_CreateFontsTexture();
}
void FurnaceGUIRenderSoftware::destroyFontsTexture() {
ImGui_ImplSW_DestroyFontsTexture();
}
void FurnaceGUIRenderSoftware::renderGUI() {
ImGui_ImplSW_RenderDrawData(ImGui::GetDrawData());
}
void FurnaceGUIRenderSoftware::wipe(float alpha) {
// TODO
}
void FurnaceGUIRenderSoftware::present() {
SDL_UpdateWindowSurface(sdlWin);
}
bool FurnaceGUIRenderSoftware::getOutputSize(int& w, int& h) {
SDL_Surface* surf=SDL_GetWindowSurface(sdlWin);
if (surf==NULL) return false;
w=surf->w;
h=surf->h;
return true;
}
int FurnaceGUIRenderSoftware::getWindowFlags() {
return 0;
}
int FurnaceGUIRenderSoftware::getMaxTextureWidth() {
return 16384;
}
int FurnaceGUIRenderSoftware::getMaxTextureHeight() {
return 16384;
}
const char* FurnaceGUIRenderSoftware::getBackendName() {
return "Software";
}
const char* FurnaceGUIRenderSoftware::getVendorName() {
return "emilk, JesusKrists and tildearrow";
}
const char* FurnaceGUIRenderSoftware::getDeviceName() {
return "imgui_sw Software Renderer";
}
const char* FurnaceGUIRenderSoftware::getAPIVersion() {
return "N/A";
}
void FurnaceGUIRenderSoftware::setSwapInterval(int swapInterval) {
}
void FurnaceGUIRenderSoftware::preInit() {
}
bool FurnaceGUIRenderSoftware::init(SDL_Window* win, int swapInterval) {
sdlWin=win;
return true;
}
void FurnaceGUIRenderSoftware::initGUI(SDL_Window* win) {
// hack
ImGui_ImplSDL2_InitForMetal(win);
ImGui_ImplSW_Init(win);
}
void FurnaceGUIRenderSoftware::quitGUI() {
ImGui_ImplSW_Shutdown();
}
bool FurnaceGUIRenderSoftware::quit() {
return true;
}

View file

@ -0,0 +1,57 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2024 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "../gui.h"
class FurnaceGUIRenderSoftware: public FurnaceGUIRender {
SDL_Window* sdlWin;
public:
ImTextureID getTextureID(FurnaceGUITexture* which);
bool lockTexture(FurnaceGUITexture* which, void** data, int* pitch);
bool unlockTexture(FurnaceGUITexture* which);
bool updateTexture(FurnaceGUITexture* which, void* data, int pitch);
FurnaceGUITexture* createTexture(bool dynamic, int width, int height, bool interpolate=true);
bool destroyTexture(FurnaceGUITexture* which);
void setTextureBlendMode(FurnaceGUITexture* which, FurnaceGUIBlendMode mode);
void setBlendMode(FurnaceGUIBlendMode mode);
void clear(ImVec4 color);
bool newFrame();
bool canVSync();
void createFontsTexture();
void destroyFontsTexture();
void renderGUI();
void wipe(float alpha);
void present();
bool getOutputSize(int& w, int& h);
int getWindowFlags();
int getMaxTextureWidth();
int getMaxTextureHeight();
const char* getBackendName();
const char* getVendorName();
const char* getDeviceName();
const char* getAPIVersion();
void setSwapInterval(int swapInterval);
void preInit();
bool init(SDL_Window* win, int swapInterval);
void initGUI(SDL_Window* win);
void quitGUI();
bool quit();
FurnaceGUIRenderSoftware():
sdlWin(NULL) {}
};

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2023 tildearrow and contributors
* Copyright (C) 2021-2024 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -24,6 +24,7 @@
#include <math.h>
#include "../ta-log.h"
#include "IconsFontAwesome4.h"
#include "furIcons.h"
#include "misc/cpp/imgui_stdlib.h"
#include <fmt/printf.h>
#include "guiConst.h"
@ -49,6 +50,27 @@ const double timeMultipliers[13]={
_x+=_text; \
}
#define MAX_RATE(_name,_x) \
if (e->isPreviewingSample()) { \
if ((int)e->getSamplePreviewRate()>(int)(_x)) { \
SAMPLE_WARN(warnRate,fmt::sprintf("%s: maximum sample rate is %d",_name,(int)(_x))); \
} \
}
#define MIN_RATE(_name,_x) \
if (e->isPreviewingSample()) { \
if ((int)e->getSamplePreviewRate()<(int)(_x)) { \
SAMPLE_WARN(warnRate,fmt::sprintf("%s: minimum sample rate is %d",_name,(int)(_x))); \
} \
}
#define EXACT_RATE(_name,_x) \
if (e->isPreviewingSample()) { \
if ((int)e->getSamplePreviewRate()!=(int)(_x)) { \
SAMPLE_WARN(warnRate,fmt::sprintf("%s: sample rate must be %d",_name,(int)(_x))); \
} \
}
void FurnaceGUI::drawSampleEdit() {
if (nextWindow==GUI_WINDOW_SAMPLE_EDIT) {
sampleEditOpen=true;
@ -173,7 +195,8 @@ void FurnaceGUI::drawSampleEdit() {
ImGui::Separator();
String warnLoop, warnLoopMode, warnLoopPos;
String warnLength;
String warnLoopStart, warnLoopEnd;
String warnLength, warnRate;
bool isChipVisible[DIV_MAX_CHIPS];
bool isTypeVisible[DIV_MAX_SAMPLE_TYPE];
@ -185,13 +208,30 @@ void FurnaceGUI::drawSampleEdit() {
memset(isMemWarning,0,DIV_MAX_CHIPS*DIV_MAX_SAMPLE_TYPE*sizeof(bool));
for (int i=0; i<e->song.systemLen; i++) {
DivDispatch* dispatch=e->getDispatch(i);
// warnings
switch (e->song.system[i]) {
case DIV_SYSTEM_SNES:
if (sample->loop) {
if (sample->loopStart&15 || sample->loopEnd&15) {
SAMPLE_WARN(warnLoopPos,"SNES: loop must be a multiple of 16");
if (sample->loopStart&15) {
int tryWith=(sample->loopStart+8)&(~15);
if (tryWith>(int)sample->samples) tryWith-=16;
String alignHint=fmt::sprintf("SNES: loop start must be a multiple of 16 (try with %d)",tryWith);
SAMPLE_WARN(warnLoopStart,alignHint);
}
if (sample->loopEnd&15) {
int tryWith=(sample->loopEnd+8)&(~15);
if (tryWith>(int)sample->samples) tryWith-=16;
String alignHint=fmt::sprintf("SNES: loop end must be a multiple of 16 (try with %d)",tryWith);
SAMPLE_WARN(warnLoopEnd,alignHint);
}
}
if (sample->samples&15) {
SAMPLE_WARN(warnLength,"SNES: sample length will be padded to multiple of 16");
}
if (dispatch!=NULL) {
MAX_RATE("SNES",dispatch->chipClock/8.0);
}
break;
case DIV_SYSTEM_QSOUND:
@ -226,13 +266,19 @@ void FurnaceGUI::drawSampleEdit() {
if (sample->loop) {
SAMPLE_WARN(warnLoop,"GA20: samples can't loop");
}
if (dispatch!=NULL) {
MIN_RATE("GA20",dispatch->chipClock/1024);
}
break;
case DIV_SYSTEM_YM2608:
case DIV_SYSTEM_YM2608_EXT:
case DIV_SYSTEM_YM2608_CSM:
if (sample->loop) {
if (sample->loopStart!=0 || sample->loopEnd!=(int)(sample->samples)) {
SAMPLE_WARN(warnLoopPos,"YM2608: loop point ignored on ADPCM-B (may only loop entire sample)");
SAMPLE_WARN(warnLoopPos,"YM2608: loop point ignored on ADPCM (may only loop entire sample)");
}
if (sample->samples&511) {
SAMPLE_WARN(warnLength,"YM2608: sample length will be padded to multiple of 512");
}
}
break;
@ -246,26 +292,106 @@ void FurnaceGUI::drawSampleEdit() {
if (sample->loopStart!=0 || sample->loopEnd!=(int)(sample->samples)) {
SAMPLE_WARN(warnLoopPos,"YM2610: loop point ignored on ADPCM-B (may only loop entire sample)");
}
if (sample->samples&511) {
SAMPLE_WARN(warnLength,"YM2610: sample length will be padded to multiple of 512");
}
}
if (sample->samples>2097152) {
SAMPLE_WARN(warnLength,"YM2610: maximum ADPCM-A sample length is 2097152");
}
if (dispatch!=NULL) {
EXACT_RATE("YM2610 (ADPCM-A)",dispatch->chipClock/432);
}
break;
case DIV_SYSTEM_Y8950:
if (sample->loop) {
if (sample->loopStart!=0 || sample->loopEnd!=(int)(sample->samples)) {
SAMPLE_WARN(warnLoopPos,"Y8950: loop point ignored on ADPCM (may only loop entire sample)");
}
if (sample->samples&511) {
SAMPLE_WARN(warnLength,"Y8950: sample length will be padded to multiple of 512");
}
}
break;
case DIV_SYSTEM_AMIGA:
if (sample->loop) {
if (sample->loopStart&1 || sample->loopEnd&1) {
SAMPLE_WARN(warnLoopPos,"Amiga: loop must be a multiple of 2");
if (sample->loopStart&1) {
SAMPLE_WARN(warnLoopStart,"Amiga: loop start must be a multiple of 2");
}
if (sample->loopEnd&1) {
SAMPLE_WARN(warnLoopEnd,"Amiga: loop end must be a multiple of 2");
}
}
if (sample->samples>131070) {
SAMPLE_WARN(warnLength,"Amiga: maximum sample length is 131070");
}
if (dispatch!=NULL) {
MAX_RATE("Amiga",31250.0);
}
break;
case DIV_SYSTEM_SEGAPCM:
case DIV_SYSTEM_SEGAPCM_COMPAT:
if (sample->samples>65280) {
SAMPLE_WARN(warnLength,"SegaPCM: maximum sample length is 65280");
}
if (dispatch!=NULL) {
MAX_RATE("SegaPCM",dispatch->chipClock/256);
}
break;
case DIV_SYSTEM_K053260:
if (sample->loop) {
if (sample->loopStart!=0 || sample->loopEnd!=(int)(sample->samples)) {
SAMPLE_WARN(warnLoopPos,"K053260: loop point ignored (may only loop entire sample)");
}
}
if (sample->samples>65535) {
SAMPLE_WARN(warnLength,"K053260: maximum sample length is 65535");
}
break;
case DIV_SYSTEM_C140:
if (sample->samples>65535) {
SAMPLE_WARN(warnLength,"C140: maximum sample length is 65535");
}
if (dispatch!=NULL) {
MAX_RATE("C140",dispatch->rate);
}
break;
case DIV_SYSTEM_C219:
if (sample->loop) {
if (sample->loopStart&1) {
SAMPLE_WARN(warnLoopStart,"C219: loop start must be a multiple of 2");
}
if (sample->loopEnd&1) {
SAMPLE_WARN(warnLoopEnd,"C219: loop end must be a multiple of 2");
}
}
if (sample->samples>131072) {
SAMPLE_WARN(warnLength,"C219: maximum sample length is 131072");
}
if (dispatch!=NULL) {
MAX_RATE("C219",dispatch->rate);
}
break;
case DIV_SYSTEM_MSM6295:
if (sample->loop) {
SAMPLE_WARN(warnLoop,"MSM6295: samples can't loop");
}
if (sample->samples>129024) {
SAMPLE_WARN(warnLength,"MSM6295: maximum bankswitched sample length is 129024");
}
break;
case DIV_SYSTEM_GBA_DMA:
if (sample->loop) {
if (sample->loopStart&3) {
SAMPLE_WARN(warnLoopStart,"GBA DMA: loop start must be a multiple of 4");
}
if ((sample->loopEnd-sample->loopStart)&15) {
SAMPLE_WARN(warnLoopEnd,"GBA DMA: loop length must be a multiple of 16");
}
}
if (sample->samples&15) {
SAMPLE_WARN(warnLength,"GBA DMA: sample length will be padded to multiple of 16");
}
break;
default:
break;
@ -281,7 +407,6 @@ void FurnaceGUI::drawSampleEdit() {
}
// chips grid
DivDispatch* dispatch=e->getDispatch(i);
if (dispatch==NULL) continue;
for (int j=0; j<DIV_MAX_SAMPLE_TYPE; j++) {
@ -326,7 +451,8 @@ void FurnaceGUI::drawSampleEdit() {
ImGui::TableNextColumn();
bool doLoop=(sample->loop);
pushWarningColor(!warnLoop.empty());
if (ImGui::Checkbox("Loop",&doLoop)) { MARK_MODIFIED
String loopCheckboxName=(doLoop && (sample->loopEnd-sample->loopStart)>0)?fmt::sprintf("Loop (length: %d)##Loop",sample->loopEnd-sample->loopStart):String("Loop");
if (ImGui::Checkbox(loopCheckboxName.c_str(),&doLoop)) { MARK_MODIFIED
if (doLoop) {
sample->loop=true;
if (sample->loopStart<0) {
@ -343,7 +469,7 @@ void FurnaceGUI::drawSampleEdit() {
}
updateSampleTex=true;
if (e->getSampleFormatMask()&(1U<<DIV_SAMPLE_DEPTH_BRR)) {
e->renderSamplesP();
e->renderSamplesP(curSample);
}
}
popWarningColor();
@ -362,6 +488,7 @@ void FurnaceGUI::drawSampleEdit() {
if (sampleInfo) {
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::AlignTextToFramePadding();
ImGui::Text("Type");
ImGui::SameLine();
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
@ -371,8 +498,8 @@ void FurnaceGUI::drawSampleEdit() {
if (ImGui::Selectable(sampleDepths[i])) {
sample->prepareUndo(true);
e->lockEngine([this,sample,i]() {
sample->convert((DivSampleDepth)i);
e->renderSamples();
sample->convert((DivSampleDepth)i,e->getSampleFormatMask());
e->renderSamples(curSample);
});
updateSampleTex=true;
MARK_MODIFIED;
@ -393,7 +520,7 @@ void FurnaceGUI::drawSampleEdit() {
if (ImGui::Checkbox("BRR emphasis",&be)) {
sample->prepareUndo(true);
sample->brrEmphasis=be;
e->renderSamplesP();
e->renderSamplesP(curSample);
updateSampleTex=true;
MARK_MODIFIED;
}
@ -410,7 +537,7 @@ void FurnaceGUI::drawSampleEdit() {
if (ImGui::Checkbox("8-bit dither",&di)) {
sample->prepareUndo(true);
sample->dither=di;
e->renderSamplesP();
e->renderSamplesP(curSample);
updateSampleTex=true;
MARK_MODIFIED;
}
@ -435,6 +562,7 @@ void FurnaceGUI::drawSampleEdit() {
bool coarseChanged=false;
ImGui::TableNextColumn();
ImGui::AlignTextToFramePadding();
ImGui::Text("Hz");
ImGui::SameLine();
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
@ -449,6 +577,7 @@ void FurnaceGUI::drawSampleEdit() {
}
}
ImGui::AlignTextToFramePadding();
ImGui::Text("Note");
ImGui::SameLine();
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
@ -460,6 +589,7 @@ void FurnaceGUI::drawSampleEdit() {
sampleNoteCoarse=i;
coarseChanged=true;
}
if (i==sampleNoteCoarse) ImGui::SetItemDefaultFocus();
}
ImGui::EndCombo();
} else if (ImGui::IsItemHovered()) {
@ -491,6 +621,7 @@ void FurnaceGUI::drawSampleEdit() {
}
}
ImGui::AlignTextToFramePadding();
ImGui::Text("Fine");
ImGui::SameLine();
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
@ -527,6 +658,7 @@ void FurnaceGUI::drawSampleEdit() {
ImGui::TableNextColumn();
ImGui::BeginDisabled(!(doLoop || keepLoopAlive));
keepLoopAlive=false;
ImGui::AlignTextToFramePadding();
ImGui::Text("Mode");
ImGui::SameLine();
pushWarningColor(!warnLoopMode.empty());
@ -537,7 +669,7 @@ void FurnaceGUI::drawSampleEdit() {
if (ImGui::Selectable(sampleLoopModes[i])) {
sample->prepareUndo(true);
sample->loopMode=(DivSampleLoopMode)i;
e->renderSamplesP();
e->renderSamplesP(curSample);
updateSampleTex=true;
MARK_MODIFIED;
}
@ -549,7 +681,8 @@ void FurnaceGUI::drawSampleEdit() {
}
popWarningColor();
pushWarningColor(!warnLoopPos.empty());
pushWarningColor(!warnLoopPos.empty() || !warnLoopStart.empty());
ImGui::AlignTextToFramePadding();
ImGui::Text("Start");
ImGui::SameLine();
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
@ -562,19 +695,30 @@ void FurnaceGUI::drawSampleEdit() {
}
updateSampleTex=true;
if (e->getSampleFormatMask()&(1U<<DIV_SAMPLE_DEPTH_BRR)) {
e->renderSamplesP();
e->renderSamplesP(curSample);
}
}
if (ImGui::IsItemActive()) {
keepLoopAlive=true;
}
if (ImGui::IsItemHovered() && (!warnLoopPos.empty() || sample->depth==DIV_SAMPLE_DEPTH_BRR)) {
if (sample->depth==DIV_SAMPLE_DEPTH_BRR) {
SAMPLE_WARN(warnLoopPos,"changing the loop in a BRR sample may result in glitches!");
if (ImGui::IsItemHovered() && (!warnLoopPos.empty() || !warnLoopStart.empty() || sample->depth==DIV_SAMPLE_DEPTH_BRR)) {
if (ImGui::BeginTooltip()) {
if (sample->depth==DIV_SAMPLE_DEPTH_BRR) {
ImGui::Text("changing the loop in a BRR sample may result in glitches!");
}
if (!warnLoopStart.empty()) {
ImGui::Text("%s",warnLoopStart.c_str());
}
if (!warnLoopPos.empty()) {
ImGui::Text("%s",warnLoopPos.c_str());
}
ImGui::EndTooltip();
}
ImGui::SetTooltip("%s",warnLoopPos.c_str());
}
popWarningColor();
pushWarningColor(!warnLoopPos.empty() || !warnLoopEnd.empty());
ImGui::AlignTextToFramePadding();
ImGui::Text("End");
ImGui::SameLine();
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
@ -587,17 +731,25 @@ void FurnaceGUI::drawSampleEdit() {
}
updateSampleTex=true;
if (e->getSampleFormatMask()&(1U<<DIV_SAMPLE_DEPTH_BRR)) {
e->renderSamplesP();
e->renderSamplesP(curSample);
}
}
if (ImGui::IsItemActive()) {
keepLoopAlive=true;
}
if (ImGui::IsItemHovered() && (!warnLoopPos.empty() || sample->depth==DIV_SAMPLE_DEPTH_BRR)) {
if (sample->depth==DIV_SAMPLE_DEPTH_BRR) {
SAMPLE_WARN(warnLoopPos,"changing the loop in a BRR sample may result in glitches!");
if (ImGui::IsItemHovered() && (!warnLoopPos.empty() || !warnLoopEnd.empty() || sample->depth==DIV_SAMPLE_DEPTH_BRR)) {
if (ImGui::BeginTooltip()) {
if (sample->depth==DIV_SAMPLE_DEPTH_BRR) {
ImGui::Text("changing the loop in a BRR sample may result in glitches!");
}
if (!warnLoopEnd.empty()) {
ImGui::Text("%s",warnLoopEnd.c_str());
}
if (!warnLoopPos.empty()) {
ImGui::Text("%s",warnLoopPos.c_str());
}
ImGui::EndTooltip();
}
ImGui::SetTooltip("%s",warnLoopPos.c_str());
}
popWarningColor();
ImGui::EndDisabled();
@ -656,7 +808,7 @@ void FurnaceGUI::drawSampleEdit() {
ImGui::PushStyleColor(ImGuiCol_CheckMark,baseColor);
if (ImGui::Checkbox(id,&sample->renderOn[i][j])) {
e->renderSamplesP();
e->renderSamplesP(curSample);
}
ImGui::PopStyleColor(4);
@ -699,12 +851,6 @@ void FurnaceGUI::drawSampleEdit() {
ImGui::EndTable();
}
/*
if (ImGui::Button("Apply")) {
e->renderSamplesP();
}
ImGui::SameLine();
*/
ImGui::Separator();
pushToggleColors(!sampleDragMode);
@ -728,7 +874,7 @@ void FurnaceGUI::drawSampleEdit() {
sameLineMaybe();
ImGui::Dummy(ImVec2(4.0*dpiScale,dpiScale));
sameLineMaybe();
ImGui::Button(ICON_FA_ARROWS_H "##SResize");
ImGui::Button(ICON_FUR_SAMPLE_RESIZE "##SResize");
if (ImGui::IsItemClicked()) {
resizeSize=sample->samples;
}
@ -750,7 +896,7 @@ void FurnaceGUI::drawSampleEdit() {
if (!sample->resize(resizeSize)) {
showError("couldn't resize! make sure your sample is 8 or 16-bit.");
}
e->renderSamples();
e->renderSamples(curSample);
});
updateSampleTex=true;
sampleSelStart=-1;
@ -763,7 +909,7 @@ void FurnaceGUI::drawSampleEdit() {
resizeSize=sample->samples;
}
sameLineMaybe();
ImGui::Button(ICON_FA_EXPAND "##SResample");
ImGui::Button(ICON_FUR_SAMPLE_RESAMPLE "##SResample");
if (ImGui::IsItemClicked()) {
resampleTarget=targetRate;
}
@ -805,7 +951,7 @@ void FurnaceGUI::drawSampleEdit() {
if (!sample->resample(targetRate,resampleTarget,resampleStrat)) {
showError("couldn't resample! make sure your sample is 8 or 16-bit.");
}
e->renderSamples();
e->renderSamples(curSample);
});
updateSampleTex=true;
sampleSelStart=-1;
@ -876,7 +1022,7 @@ void FurnaceGUI::drawSampleEdit() {
updateSampleTex=true;
e->renderSamples();
e->renderSamples(curSample);
});
MARK_MODIFIED;
ImGui::CloseCurrentPopup();
@ -884,28 +1030,28 @@ void FurnaceGUI::drawSampleEdit() {
ImGui::EndPopup();
}
sameLineMaybe();
if (ImGui::Button(ICON_FA_ARROWS_V "##SNormalize")) {
if (ImGui::Button(ICON_FUR_SAMPLE_NORMALIZE "##SNormalize")) {
doAction(GUI_ACTION_SAMPLE_NORMALIZE);
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Normalize");
}
sameLineMaybe();
if (ImGui::Button(ICON_FA_ARROW_UP "##SFadeIn")) {
if (ImGui::Button(ICON_FUR_SAMPLE_FADEIN "##SFadeIn")) {
doAction(GUI_ACTION_SAMPLE_FADE_IN);
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Fade in");
}
sameLineMaybe();
if (ImGui::Button(ICON_FA_ARROW_DOWN "##SFadeOut")) {
if (ImGui::Button(ICON_FUR_SAMPLE_FADEOUT "##SFadeOut")) {
doAction(GUI_ACTION_SAMPLE_FADE_OUT);
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Fade out");
}
sameLineMaybe();
ImGui::Button(ICON_FA_ADJUST "##SInsertSilence");
ImGui::Button(ICON_FUR_SAMPLE_INSERT_SILENCE "##SInsertSilence");
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Insert silence");
}
@ -925,7 +1071,7 @@ void FurnaceGUI::drawSampleEdit() {
if (!sample->insert(pos,silenceSize)) {
showError("couldn't insert! make sure your sample is 8 or 16-bit.");
}
e->renderSamples();
e->renderSamples(curSample);
});
updateSampleTex=true;
sampleSelStart=pos;
@ -936,7 +1082,7 @@ void FurnaceGUI::drawSampleEdit() {
ImGui::EndPopup();
}
sameLineMaybe();
if (ImGui::Button(ICON_FA_ERASER "##SSilence")) {
if (ImGui::Button(ICON_FUR_SAMPLE_APPLY_SILENCE "##SSilence")) {
doAction(GUI_ACTION_SAMPLE_SILENCE);
}
if (ImGui::IsItemHovered()) {
@ -959,28 +1105,28 @@ void FurnaceGUI::drawSampleEdit() {
ImGui::SameLine();
ImGui::Dummy(ImVec2(4.0*dpiScale,dpiScale));
sameLineMaybe();
if (ImGui::Button(ICON_FA_BACKWARD "##SReverse")) {
if (ImGui::Button(ICON_FUR_SAMPLE_REVERSE "##SReverse")) {
doAction(GUI_ACTION_SAMPLE_REVERSE);
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Reverse");
}
sameLineMaybe();
if (ImGui::Button(ICON_FA_SORT_AMOUNT_ASC "##SInvert")) {
if (ImGui::Button(ICON_FUR_SAMPLE_INVERT "##SInvert")) {
doAction(GUI_ACTION_SAMPLE_INVERT);
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Invert");
}
sameLineMaybe();
if (ImGui::Button(ICON_FA_LEVEL_DOWN "##SSign")) {
if (ImGui::Button(ICON_FUR_SAMPLE_SIGN "##SSign")) {
doAction(GUI_ACTION_SAMPLE_SIGN);
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Signed/unsigned exchange");
}
sameLineMaybe();
ImGui::Button(ICON_FA_INDUSTRY "##SFilter");
ImGui::Button(ICON_FUR_SAMPLE_FILTER "##SFilter");
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Apply filter");
}
@ -994,11 +1140,11 @@ void FurnaceGUI::drawSampleEdit() {
float highP=sampleFilterH*100.0f;
float resP=sampleFilterRes*100.0f;
ImGui::Text("Cutoff:");
if (ImGui::InputFloat("From",&sampleFilterCutStart,1.0f,100.0f,"%.0f")) {
if (ImGui::InputFloat("From",&sampleFilterCutStart,10.0f,1000.0f,"%.0f")) {
if (sampleFilterCutStart<0.0) sampleFilterCutStart=0.0;
if (sampleFilterCutStart>sample->rate*0.5) sampleFilterCutStart=sample->rate*0.5;
}
if (ImGui::InputFloat("To",&sampleFilterCutEnd,1.0f,100.0f,"%.0f")) {
if (ImGui::InputFloat("To",&sampleFilterCutEnd,10.0f,1000.0f,"%.0f")) {
if (sampleFilterCutEnd<0.0) sampleFilterCutEnd=0.0;
if (sampleFilterCutEnd>sample->rate*0.5) sampleFilterCutEnd=sample->rate*0.5;
}
@ -1008,6 +1154,7 @@ void FurnaceGUI::drawSampleEdit() {
if (sampleFilterRes<0.0f) sampleFilterRes=0.0f;
if (sampleFilterRes>0.99f) sampleFilterRes=0.99f;
}
ImGui::AlignTextToFramePadding();
ImGui::Text("Power");
ImGui::SameLine();
if (ImGui::RadioButton("1x",sampleFilterPower==1)) {
@ -1085,7 +1232,7 @@ void FurnaceGUI::drawSampleEdit() {
updateSampleTex=true;
e->renderSamples();
e->renderSamples(curSample);
});
MARK_MODIFIED;
ImGui::CloseCurrentPopup();
@ -1096,6 +1243,74 @@ void FurnaceGUI::drawSampleEdit() {
ImGui::SameLine();
ImGui::Dummy(ImVec2(4.0*dpiScale,dpiScale));
sameLineMaybe();
ImGui::Button(ICON_FUR_CROSSFADE "##CrossFade");
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Crossfade loop points");
}
if (openSampleCrossFadeOpt) {
openSampleCrossFadeOpt=false;
ImGui::OpenPopup("SCrossFadeOpt");
}
if (ImGui::BeginPopupContextItem("SCrossFadeOpt",ImGuiPopupFlags_MouseButtonLeft)) {
if (sampleCrossFadeLoopLength>sample->loopStart) sampleCrossFadeLoopLength=sample->loopStart;
if (sampleCrossFadeLoopLength>(sample->loopEnd-sample->loopStart)) sampleCrossFadeLoopLength=sample->loopEnd-sample->loopStart;
if (ImGui::SliderInt("Number of samples",&sampleCrossFadeLoopLength,0,100000)) {
if (sampleCrossFadeLoopLength<0) sampleCrossFadeLoopLength=0;
if (sampleCrossFadeLoopLength>sample->loopStart) sampleCrossFadeLoopLength=sample->loopStart;
if (sampleCrossFadeLoopLength>(sample->loopEnd-sample->loopStart)) sampleCrossFadeLoopLength=sample->loopEnd-sample->loopStart;
if (sampleCrossFadeLoopLength>100000) sampleCrossFadeLoopLength=100000;
}
if (ImGui::SliderInt("Linear <-> Equal power",&sampleCrossFadeLoopLaw,0,100)) {
if (sampleCrossFadeLoopLaw<0) sampleCrossFadeLoopLaw=0;
if (sampleCrossFadeLoopLaw>100) sampleCrossFadeLoopLaw=100;
}
if (ImGui::Button("Apply")) {
if (sampleCrossFadeLoopLength>sample->loopStart) {
showError("Crossfade: length would go out of bounds. Aborted...");
ImGui::CloseCurrentPopup();
} else if (sampleCrossFadeLoopLength>(sample->loopEnd-sample->loopStart)) {
showError("Crossfade: length would overflow loopStart. Try a smaller random value.");
ImGui::CloseCurrentPopup();
} else {
sample->prepareUndo(true);
e->lockEngine([this,sample] {
SAMPLE_OP_BEGIN;
double l=1.0/(double)sampleCrossFadeLoopLength;
double evar=1.0-sampleCrossFadeLoopLaw/200.0;
if (sample->depth==DIV_SAMPLE_DEPTH_8BIT) {
unsigned int crossFadeInput=sample->loopStart-sampleCrossFadeLoopLength;
unsigned int crossFadeOutput=sample->loopEnd-sampleCrossFadeLoopLength;
for (int i=0; i<sampleCrossFadeLoopLength; i++) {
double f1=pow(i*l,evar);
double f2=pow((sampleCrossFadeLoopLength-i)*l,evar);
signed char out=(signed char)(((double)sample->data8[crossFadeInput])*f1+((double)sample->data8[crossFadeOutput])*f2);
sample->data8[crossFadeOutput]=out;
crossFadeInput++;
crossFadeOutput++;
}
} else if (sample->depth==DIV_SAMPLE_DEPTH_16BIT) {
unsigned int crossFadeInput=sample->loopStart-sampleCrossFadeLoopLength;
unsigned int crossFadeOutput=sample->loopEnd-sampleCrossFadeLoopLength;
for (int i=0; i<sampleCrossFadeLoopLength; i++) {
double f1=std::pow(i*l,evar);
double f2=std::pow((sampleCrossFadeLoopLength-i)*l,evar);
short out=(short)(((double)sample->data16[crossFadeInput])*f1+((double)sample->data16[crossFadeOutput])*f2);
sample->data16[crossFadeOutput]=out;
crossFadeInput++;
crossFadeOutput++;
}
}
updateSampleTex=true;
e->renderSamples(curSample);
});
MARK_MODIFIED;
ImGui::CloseCurrentPopup();
}
}
ImGui::EndPopup();
}
ImGui::SameLine();
if (ImGui::Button(ICON_FA_PLAY "##PreviewSample")) {
e->previewSample(curSample);
}
@ -1120,6 +1335,7 @@ void FurnaceGUI::drawSampleEdit() {
sameLineMaybe(ImGui::CalcTextSize("Zoom").x+150.0f*dpiScale+ImGui::CalcTextSize("100%").x);
double zoomPercent=100.0/sampleZoom;
bool checkZoomLimit=false;
ImGui::AlignTextToFramePadding();
ImGui::Text("Zoom");
ImGui::SameLine();
ImGui::SetNextItemWidth(150.0f*dpiScale);
@ -1373,7 +1589,7 @@ void FurnaceGUI::drawSampleEdit() {
updateSampleTex=false;
}
ImGui::ImageButton(rend->getTextureID(sampleTex),avail,ImVec2(0,0),ImVec2(1,1),0);
ImGui::ImageButton(rend->getTextureID(sampleTex),avail,ImVec2(0,0),ImVec2(rend->getTextureU(sampleTex),rend->getTextureV(sampleTex)),0);
ImVec2 rectMin=ImGui::GetItemRectMin();
ImVec2 rectMax=ImGui::GetItemRectMax();
@ -1591,14 +1807,14 @@ void FurnaceGUI::drawSampleEdit() {
posX=samplePos+pos.x*sampleZoom;
if (posX>(int)sample->samples) posX=-1;
}
posY=(0.5-pos.y/rectSize.y)*((sample->depth==DIV_SAMPLE_DEPTH_8BIT)?255:32767);
posY=(0.5-pos.y/rectSize.y)*((sample->depth==DIV_SAMPLE_DEPTH_8BIT)?255:65535);
if (posX>=0) {
statusBar2=fmt::sprintf("(%d, %d)",posX,posY);
}
}
dl->PushClipRect(rectMin,rectMax);
if (e->isPreviewingSample()) {
if (e->isPreviewingSample() && e->getSamplePreviewSample()==curSample) {
if (!statusBar2.empty()) {
statusBar2+=" | ";
}
@ -1733,7 +1949,16 @@ void FurnaceGUI::drawSampleEdit() {
ImGui::TableNextColumn();
ImGui::TextUnformatted(statusBar.c_str());
ImGui::TableNextColumn();
ImGui::TextUnformatted(statusBar2.c_str());
if (!warnRate.empty()) {
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_WARNING]);
ImGui::TextUnformatted(statusBar2.c_str());
ImGui::PopStyleColor();
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("%s",warnRate.c_str());
}
} else {
ImGui::TextUnformatted(statusBar2.c_str());
}
ImGui::TableNextColumn();
if (!warnLength.empty()) {
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_WARNING]);
@ -1762,7 +1987,7 @@ void FurnaceGUI::doUndoSample() {
DivSample* sample=e->song.sample[curSample];
e->lockEngine([this,sample]() {
if (sample->undo()==2) {
e->renderSamples();
e->renderSamples(curSample);
updateSampleTex=true;
}
});
@ -1774,7 +1999,7 @@ void FurnaceGUI::doRedoSample() {
DivSample* sample=e->song.sample[curSample];
e->lockEngine([this,sample]() {
if (sample->redo()==2) {
e->renderSamples();
e->renderSamples(curSample);
updateSampleTex=true;
}
});

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2023 tildearrow and contributors
* Copyright (C) 2021-2024 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2023 tildearrow and contributors
* Copyright (C) 2021-2024 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -21,6 +21,7 @@
#include "scaling.h"
#include "../ta-log.h"
#include <SDL.h>
#include <SDL_syswm.h>
#ifdef _WIN32
#include <windows.h>
@ -42,14 +43,23 @@ typedef int (*XDS)(void*);
typedef int (*XDW)(void*,int);
#endif
double getScaleFactor(const char* driverHint) {
double getScaleFactor(const char* driverHint, void* windowHint) {
double ret=1.0;
// Windows
#ifdef _WIN32
POINT nullPoint;
nullPoint.x=-1;
nullPoint.y=-1;
if (windowHint!=NULL) {
int px=0;
int py=0;
SDL_GetWindowPosition((SDL_Window*)windowHint,&px,&py);
nullPoint.x=px;
nullPoint.y=py;
} else {
nullPoint.x=-1;
nullPoint.y=-1;
}
HMONITOR disp=MonitorFromPoint(nullPoint,MONITOR_DEFAULTTOPRIMARY);
if (disp==NULL) {
@ -93,12 +103,34 @@ double getScaleFactor(const char* driverHint) {
return ret;
#endif
// macOS - backingScaleFactor
// macOS
#ifdef __APPLE__
if (driverHint==NULL) {
return getMacDPIScale();
} else if (strcmp(driverHint,"cocoa")==0 || strcmp(driverHint,"uikit")==0) {
return getMacDPIScale();
return getMacDPIScale(NULL,false);
} else if (strcmp(driverHint,"cocoa")==0) {
#ifdef SDL_VIDEO_DRIVER_COCOA
void* nsWindow=NULL;
SDL_SysWMinfo wmInfo;
if (windowHint!=NULL) {
SDL_VERSION(&wmInfo.version)
if (SDL_GetWindowWMInfo((SDL_Window*)windowHint,&wmInfo)==SDL_TRUE) {
nsWindow=wmInfo.info.cocoa.window;
}
}
return getMacDPIScale(nsWindow,false);
#endif
} else if (strcmp(driverHint,"uikit")==0) {
#ifdef SDL_VIDEO_DRIVER_UIKIT
void* uiWindow=NULL;
SDL_SysWMinfo wmInfo;
if (windowHint!=NULL) {
SDL_VERSION(&wmInfo.version)
if (SDL_GetWindowWMInfo((SDL_Window*)windowHint,&wmInfo)==SDL_TRUE) {
uiWindow=wmInfo.info.uikit.window;
}
}
return getMacDPIScale(uiWindow,true);
#endif
}
#endif

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2023 tildearrow and contributors
* Copyright (C) 2021-2024 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -17,4 +17,4 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
double getScaleFactor(const char* driverHint);
double getScaleFactor(const char* driverHint, void* windowHint);

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2023 tildearrow and contributors
* Copyright (C) 2021-2024 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -36,6 +36,7 @@ void FurnaceGUI::drawSongInfo(bool asChild) {
ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthStretch,0.0);
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::AlignTextToFramePadding();
ImGui::Text("Name");
ImGui::TableNextColumn();
float avail=ImGui::GetContentRegionAvail().x;
@ -43,22 +44,9 @@ void FurnaceGUI::drawSongInfo(bool asChild) {
if (ImGui::InputText("##Name",&e->song.name,ImGuiInputTextFlags_UndoRedo)) { MARK_MODIFIED
updateWindowTitle();
}
if (e->song.insLen==1) {
unsigned int checker=0x11111111;
unsigned int checker1=0;
DivInstrument* ins=e->getIns(0);
if (ins->name.size()==15 && e->curSubSong->ordersLen==8) {
for (int i=0; i<15; i++) {
checker^=ins->name[i]<<i;
checker1+=ins->name[i];
checker=(checker>>1|(((checker)^(checker>>2)^(checker>>3)^(checker>>5))&1)<<31);
checker1<<=1;
}
if (checker==0x5ec4497d && checker1==0x6347ee) nonLatchNibble=true;
}
}
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::AlignTextToFramePadding();
ImGui::Text("Author");
ImGui::TableNextColumn();
ImGui::SetNextItemWidth(avail);
@ -68,56 +56,53 @@ void FurnaceGUI::drawSongInfo(bool asChild) {
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::AlignTextToFramePadding();
ImGui::Text("Album");
ImGui::TableNextColumn();
ImGui::SetNextItemWidth(avail);
if (ImGui::InputText("##Category",&e->song.category,ImGuiInputTextFlags_UndoRedo)) {
MARK_MODIFIED;
}
if (!basicMode) {
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Text("System");
ImGui::TableNextColumn();
ImGui::SetNextItemWidth(MAX(16.0f*dpiScale,avail-autoButtonSize-ImGui::GetStyle().ItemSpacing.x));
if (ImGui::InputText("##SystemName",&e->song.systemName,ImGuiInputTextFlags_UndoRedo)) {
MARK_MODIFIED;
updateWindowTitle();
e->song.autoSystem=false;
}
ImGui::SameLine();
pushToggleColors(e->song.autoSystem);
if (ImGui::Button("Auto")) {
e->song.autoSystem=!e->song.autoSystem;
if (e->song.autoSystem) {
autoDetectSystem();
updateWindowTitle();
}
MARK_MODIFIED;
}
popToggleColors();
autoButtonSize=ImGui::GetItemRectSize().x;
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::AlignTextToFramePadding();
ImGui::Text("System");
ImGui::TableNextColumn();
ImGui::SetNextItemWidth(MAX(16.0f*dpiScale,avail-autoButtonSize-ImGui::GetStyle().ItemSpacing.x));
if (ImGui::InputText("##SystemName",&e->song.systemName,ImGuiInputTextFlags_UndoRedo)) {
MARK_MODIFIED;
updateWindowTitle();
e->song.autoSystem=false;
}
ImGui::SameLine();
pushToggleColors(e->song.autoSystem);
if (ImGui::Button("Auto")) {
e->song.autoSystem=!e->song.autoSystem;
if (e->song.autoSystem) {
autoDetectSystem();
updateWindowTitle();
}
MARK_MODIFIED;
}
popToggleColors();
autoButtonSize=ImGui::GetItemRectSize().x;
ImGui::EndTable();
}
if (basicMode) {
if (e->song.tuning<435.8 || e->song.tuning>444) {
ImGui::TextWrapped("Tuning changed - disable Basic Mode to edit.");
}
} else if (ImGui::BeginTable("OtherProps",2,ImGuiTableFlags_SizingStretchProp)) {
if (ImGui::BeginTable("OtherProps",2,ImGuiTableFlags_SizingStretchProp)) {
ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthFixed,0.0);
ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthStretch,0.0);
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::AlignTextToFramePadding();
ImGui::Text("Tuning (A-4)");
ImGui::TableNextColumn();
float tune=e->song.tuning;
float avail=ImGui::GetContentRegionAvail().x;
ImGui::SetNextItemWidth(avail);
if (ImGui::InputFloat("##Tuning",&tune,1.0f,3.0f,"%g")) { MARK_MODIFIED
if (ImGui::InputFloat("##Tuning",&tune,1.0f,10.0f,"%g")) { MARK_MODIFIED
if (tune<220.0f) tune=220.0f;
if (tune>880.0f) tune=880.0f;
e->song.tuning=tune;

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2023 tildearrow and contributors
* Copyright (C) 2021-2024 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2023 tildearrow and contributors
* Copyright (C) 2021-2024 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -37,6 +37,7 @@ void FurnaceGUI::drawSpeed(bool asChild) {
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::AlignTextToFramePadding();
if (ImGui::SmallButton(tempoView?"Base Tempo##TempoOrHz":"Tick Rate##TempoOrHz")) {
tempoView=!tempoView;
}
@ -52,7 +53,7 @@ void FurnaceGUI::drawSpeed(bool asChild) {
float halfAvail=(avail-ImGui::GetStyle().ItemSpacing.x)*0.5;
ImGui::SetNextItemWidth(halfAvail);
float setHz=tempoView?e->curSubSong->hz*2.5:e->curSubSong->hz;
if (ImGui::InputFloat("##Rate",&setHz,1.0f,1.0f,"%g")) { MARK_MODIFIED
if (ImGui::InputFloat("##Rate",&setHz,1.0f,10.0f,"%g")) { MARK_MODIFIED
if (tempoView) setHz/=2.5;
if (setHz<1) setHz=1;
if (setHz>999) setHz=999;
@ -74,6 +75,7 @@ void FurnaceGUI::drawSpeed(bool asChild) {
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::AlignTextToFramePadding();
if (keepGrooveAlive || e->curSubSong->speeds.len>2) {
if (ImGui::SmallButton("Groove")) {
e->lockEngine([this]() {
@ -160,55 +162,58 @@ void FurnaceGUI::drawSpeed(bool asChild) {
}
}
if (!basicMode) {
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Text("Virtual Tempo");
ImGui::TableNextColumn();
ImGui::SetNextItemWidth(halfAvail);
if (ImGui::InputScalar("##VTempoN",ImGuiDataType_S16,&e->curSubSong->virtualTempoN,&_ONE,&_THREE)) { MARK_MODIFIED
if (e->curSubSong->virtualTempoN<1) e->curSubSong->virtualTempoN=1;
if (e->curSubSong->virtualTempoN>255) e->curSubSong->virtualTempoN=255;
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Numerator");
}
ImGui::SameLine();
ImGui::SetNextItemWidth(halfAvail);
if (ImGui::InputScalar("##VTempoD",ImGuiDataType_S16,&e->curSubSong->virtualTempoD,&_ONE,&_THREE)) { MARK_MODIFIED
if (e->curSubSong->virtualTempoD<1) e->curSubSong->virtualTempoD=1;
if (e->curSubSong->virtualTempoD>255) e->curSubSong->virtualTempoD=255;
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Denominator (set to base tempo)");
}
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Text("Divider");
ImGui::TableNextColumn();
ImGui::SetNextItemWidth(halfAvail);
unsigned char realTB=e->curSubSong->timeBase+1;
if (ImGui::InputScalar("##TimeBase",ImGuiDataType_U8,&realTB,&_ONE,&_THREE)) { MARK_MODIFIED
if (realTB<1) realTB=1;
if (realTB>16) realTB=16;
e->curSubSong->timeBase=realTB-1;
}
ImGui::SameLine();
ImGui::Text("%.2f BPM",calcBPM(e->curSubSong->speeds,e->curSubSong->hz,e->curSubSong->virtualTempoN,e->curSubSong->virtualTempoD));
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::AlignTextToFramePadding();
ImGui::Text("Virtual Tempo");
ImGui::TableNextColumn();
ImGui::SetNextItemWidth(halfAvail);
if (ImGui::InputScalar("##VTempoN",ImGuiDataType_S16,&e->curSubSong->virtualTempoN,&_ONE,&_TEN)) { MARK_MODIFIED
if (e->curSubSong->virtualTempoN<1) e->curSubSong->virtualTempoN=1;
if (e->curSubSong->virtualTempoN>255) e->curSubSong->virtualTempoN=255;
e->virtualTempoChanged();
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Numerator");
}
ImGui::SameLine();
ImGui::SetNextItemWidth(halfAvail);
if (ImGui::InputScalar("##VTempoD",ImGuiDataType_S16,&e->curSubSong->virtualTempoD,&_ONE,&_TEN)) { MARK_MODIFIED
if (e->curSubSong->virtualTempoD<1) e->curSubSong->virtualTempoD=1;
if (e->curSubSong->virtualTempoD>255) e->curSubSong->virtualTempoD=255;
e->virtualTempoChanged();
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Denominator (set to base tempo)");
}
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::AlignTextToFramePadding();
ImGui::Text("Divider");
ImGui::TableNextColumn();
ImGui::SetNextItemWidth(halfAvail);
unsigned char realTB=e->curSubSong->timeBase+1;
if (ImGui::InputScalar("##TimeBase",ImGuiDataType_U8,&realTB,&_ONE,&_THREE)) { MARK_MODIFIED
if (realTB<1) realTB=1;
if (realTB>16) realTB=16;
e->curSubSong->timeBase=realTB-1;
}
ImGui::SameLine();
ImGui::Text("%.2f BPM",calcBPM(e->curSubSong->speeds,e->curSubSong->hz,e->curSubSong->virtualTempoN,e->curSubSong->virtualTempoD));
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::AlignTextToFramePadding();
ImGui::Text("Highlight");
ImGui::TableNextColumn();
ImGui::SetNextItemWidth(halfAvail);
if (ImGui::InputScalar("##Highlight1",ImGuiDataType_U8,&e->curSubSong->hilightA,&_ONE,&_THREE)) {
if (ImGui::InputScalar("##Highlight1",ImGuiDataType_U8,&e->curSubSong->hilightA,&_ONE,&_FOUR)) {
MARK_MODIFIED;
}
ImGui::SameLine();
ImGui::SetNextItemWidth(halfAvail);
if (ImGui::InputScalar("##Highlight2",ImGuiDataType_U8,&e->curSubSong->hilightB,&_ONE,&_THREE)) {
if (ImGui::InputScalar("##Highlight2",ImGuiDataType_U8,&e->curSubSong->hilightB,&_ONE,&_FOUR)) {
MARK_MODIFIED;
}
ImGui::EndTable();
@ -222,31 +227,31 @@ void FurnaceGUI::drawSpeed(bool asChild) {
ImGui::TableSetupColumn("c2",ImGuiTableColumnFlags_WidthStretch,0.0);
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::AlignTextToFramePadding();
ImGui::Text("Pattern Length");
ImGui::TableNextColumn();
float avail=ImGui::GetContentRegionAvail().x;
ImGui::SetNextItemWidth(avail);
int patLen=e->curSubSong->patLen;
if (ImGui::InputInt("##PatLength",&patLen,1,3)) { MARK_MODIFIED
if (ImGui::InputInt("##PatLength",&patLen,1,16)) { MARK_MODIFIED
if (patLen<1) patLen=1;
if (patLen>DIV_MAX_PATTERNS) patLen=DIV_MAX_PATTERNS;
e->curSubSong->patLen=patLen;
}
if (!basicMode) {
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Text("Song Length");
ImGui::TableNextColumn();
ImGui::SetNextItemWidth(avail);
int ordLen=e->curSubSong->ordersLen;
if (ImGui::InputInt("##OrdLength",&ordLen,1,3)) { MARK_MODIFIED
if (ordLen<1) ordLen=1;
if (ordLen>DIV_MAX_PATTERNS) ordLen=DIV_MAX_PATTERNS;
e->curSubSong->ordersLen=ordLen;
if (curOrder>=ordLen) {
setOrder(ordLen-1);
}
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::AlignTextToFramePadding();
ImGui::Text("Song Length");
ImGui::TableNextColumn();
ImGui::SetNextItemWidth(avail);
int ordLen=e->curSubSong->ordersLen;
if (ImGui::InputInt("##OrdLength",&ordLen,1,4)) { MARK_MODIFIED
if (ordLen<1) ordLen=1;
if (ordLen>DIV_MAX_PATTERNS) ordLen=DIV_MAX_PATTERNS;
e->curSubSong->ordersLen=ordLen;
if (curOrder>=ordLen) {
setOrder(ordLen-1);
}
}

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2023 tildearrow and contributors
* Copyright (C) 2021-2024 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2023 tildearrow and contributors
* Copyright (C) 2021-2024 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -32,26 +32,10 @@ void FurnaceGUI::drawStats() {
size_t lastProcTime=e->processTime;
double maxGot=1000000000.0*(double)e->getAudioDescGot().bufsize/(double)e->getAudioDescGot().rate;
String procStr=fmt::sprintf("%.1f%%",100.0*((double)lastProcTime/(double)maxGot));
ImGui::AlignTextToFramePadding();
ImGui::Text("Audio load");
ImGui::SameLine();
ImGui::ProgressBar((double)lastProcTime/maxGot,ImVec2(-FLT_MIN,0),procStr.c_str());
ImGui::Separator();
for (int i=0; i<e->song.systemLen; i++) {
DivDispatch* dispatch=e->getDispatch(i);
for (int j=0; dispatch!=NULL && dispatch->getSampleMemCapacity(j)>0; j++) {
size_t capacity=dispatch->getSampleMemCapacity(j);
size_t usage=dispatch->getSampleMemUsage(j);
String usageStr;
if (settings.memUsageUnit==1) {
usageStr=fmt::sprintf("%d/%dKB",usage/1024,capacity/1024);
} else {
usageStr=fmt::sprintf("%d/%d",usage,capacity);
}
ImGui::Text("%s [%d]", e->getSystemName(e->song.system[i]), j);
ImGui::SameLine();
ImGui::ProgressBar(((float)usage)/((float)capacity),ImVec2(-FLT_MIN,0),usageStr.c_str());
}
}
}
if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_STATS;
ImGui::End();

View file

@ -38,8 +38,6 @@ void FurnaceGUI::drawSubSongs(bool asChild) {
if (ImGui::Selectable(id,i==e->getCurrentSubSong())) {
e->changeSongP(i);
updateScroll(0);
oldOrder=0;
oldOrder1=0;
oldRow=0;
cursor.xCoarse=0;
cursor.xFine=0;
@ -76,8 +74,6 @@ void FurnaceGUI::drawSubSongs(bool asChild) {
} else {
e->changeSongP(e->song.subsong.size()-1);
updateScroll(0);
oldOrder=0;
oldOrder1=0;
oldRow=0;
cursor.xCoarse=0;
cursor.xFine=0;
@ -98,8 +94,6 @@ void FurnaceGUI::drawSubSongs(bool asChild) {
} else {
e->changeSongP(e->song.subsong.size()-1);
updateScroll(0);
oldOrder=0;
oldOrder1=0;
oldRow=0;
cursor.xCoarse=0;
cursor.xFine=0;
@ -127,6 +121,7 @@ void FurnaceGUI::drawSubSongs(bool asChild) {
ImGui::SetTooltip("Remove");
}
ImGui::AlignTextToFramePadding();
ImGui::Text("Name");
ImGui::SameLine();
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);

File diff suppressed because it is too large Load diff

View file

@ -60,8 +60,7 @@ bool FurnaceGUI::parseSysEx(unsigned char* data, size_t len) {
op.rs=reader.readC();
reader.readC(); // EBS - ignore
op.am=reader.readC();
// TODO: don't ignore after I add KVS to Furnace
reader.readC(); // KVS - ignore
op.kvs=(reader.readC()>2)?1:0;
op.tl=3+((99-reader.readC())*124)/99;
unsigned char freq=reader.readC();
logV("OP%d freq: %d",i,freq);

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2023 tildearrow and contributors
* Copyright (C) 2021-2024 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -40,6 +40,10 @@ void FurnaceGUI::drawSysManager() {
}
if (ImGui::Begin("Chip Manager",&sysManagerOpen,globalWinFlags)) {
ImGui::Checkbox("Preserve channel order",&preserveChanPos);
ImGui::SameLine();
ImGui::Checkbox("Clone channel data",&sysDupCloneChannels);
ImGui::SameLine();
ImGui::Checkbox("Clone at end",&sysDupEnd);
if (ImGui::BeginTable("SystemList",3)) {
ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthFixed);
ImGui::TableSetupColumn("c2",ImGuiTableColumnFlags_WidthStretch);
@ -79,33 +83,43 @@ void FurnaceGUI::drawSysManager() {
}
ImGui::TableNextColumn();
if (ImGui::TreeNode(fmt::sprintf("%d. %s##_SYSM%d",i+1,getSystemName(e->song.system[i]),i).c_str())) {
drawSysConf(i,e->song.system[i],e->song.systemFlags[i],true);
drawSysConf(i,i,e->song.system[i],e->song.systemFlags[i],true);
ImGui::TreePop();
}
ImGui::TableNextColumn();
ImGui::Button(ICON_FA_CHEVRON_DOWN "##SysChange");
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Change");
if (ImGui::Button("Clone##SysDup")) {
if (!e->duplicateSystem(i,sysDupCloneChannels,sysDupEnd)) {
showError("cannot clone chip! ("+e->getLastError()+")");
} else {
MARK_MODIFIED;
}
}
ImGui::SameLine();
ImGui::Button("Change##SysChange");
if (ImGui::BeginPopupContextItem("SysPickerC",ImGuiPopupFlags_MouseButtonLeft)) {
DivSystem picked=systemPicker();
if (picked!=DIV_SYSTEM_NULL) {
e->changeSystem(i,picked,preserveChanPos);
MARK_MODIFIED;
if (e->song.autoSystem) {
autoDetectSystem();
if (e->changeSystem(i,picked,preserveChanPos)) {
MARK_MODIFIED;
if (e->song.autoSystem) {
autoDetectSystem();
}
updateWindowTitle();
} else {
showError("cannot change chip! ("+e->getLastError()+")");
}
updateWindowTitle();
ImGui::CloseCurrentPopup();
}
ImGui::EndPopup();
}
ImGui::SameLine();
ImGui::BeginDisabled(e->song.systemLen<=1);
pushDestColor();
if (ImGui::Button(ICON_FA_TIMES "##SysRemove")) {
sysToDelete=i;
showWarning("Are you sure you want to remove this chip?",GUI_WARN_SYSTEM_DEL);
}
popDestColor();
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Remove");
}

286
src/gui/sysPartNumber.cpp Normal file
View file

@ -0,0 +1,286 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2024 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "gui.h"
const char* FurnaceGUI::getSystemPartNumber(DivSystem sys, DivConfig& flags) {
switch (sys) {
case DIV_SYSTEM_YMU759:
return "YMU759";
break;
case DIV_SYSTEM_SMS:{
int chipType=flags.getInt("chipType",0);
if (chipType==4) {
return "SN76489A";
} else if (chipType==5) {
return "SN76496";
} else if (chipType==6) {
return "8496";
} else if (chipType==7) {
return "PSSJ";//not part number
} else if (chipType==8) {
return "SN94624";
} else if (chipType==9) {
return "SN76494";
} else {
return "SN76489";
}
break;
}
case DIV_SYSTEM_PCE:{
int chipType=flags.getInt("chipType",0);
if (chipType==1) {
return "HuC6280A";
} else {
return "HuC6280";
}
}
case DIV_SYSTEM_NES:
return "2A03";
break;
case DIV_SYSTEM_C64_6581:
return "6581";
break;
case DIV_SYSTEM_C64_8580:
return "8580";
break;
case DIV_SYSTEM_Y8950:
case DIV_SYSTEM_Y8950_DRUMS:
return "Y8950";
break;
case DIV_SYSTEM_AY8910:{
int chipType=flags.getInt("chipType",0);
if (chipType==1) {
return "YM2149(F)";
} else if (chipType==2) {
return "5B";
} else if (chipType==3) {
return "AY-3-8914";
} else {
return "AY-3-8910";
}
break;
}
case DIV_SYSTEM_YM2151:
return "YM2151";
break;
case DIV_SYSTEM_YM2612:
case DIV_SYSTEM_YM2612_CSM:
case DIV_SYSTEM_YM2612_DUALPCM:
case DIV_SYSTEM_YM2612_DUALPCM_EXT:
case DIV_SYSTEM_YM2612_EXT:{
int chipType=0;
if (flags.has("chipType")) {
chipType=flags.getInt("chipType",0);
} else {
chipType=flags.getBool("ladderEffect",0)?1:0;
}
if (chipType==0) {
return "YM3438";
} else if (chipType==2) {
return "YMF276";
} else {
return "YM2612";
}
break;
}
case DIV_SYSTEM_TIA:
return "TIA";
break;
case DIV_SYSTEM_SAA1099:
return "SAA1099";
break;
case DIV_SYSTEM_AY8930:
return "AY8930";
break;
case DIV_SYSTEM_VIC20:
return "VIC";
break;
case DIV_SYSTEM_PET:
return "PET";
break;
case DIV_SYSTEM_VRC6:
return "VRC6";
break;
case DIV_SYSTEM_FDS:
return "FDS";
break;
case DIV_SYSTEM_MMC5:
return "MMC5";
break;
case DIV_SYSTEM_N163:
return "N163";
break;
case DIV_SYSTEM_YM2203:
case DIV_SYSTEM_YM2203_EXT:
case DIV_SYSTEM_YM2203_CSM:
return "YM2203";
break;
case DIV_SYSTEM_YM2608:
case DIV_SYSTEM_YM2608_CSM:
case DIV_SYSTEM_YM2608_EXT:
return "YM2608";
break;
case DIV_SYSTEM_OPLL:
case DIV_SYSTEM_OPLL_DRUMS:{
int patchSet=flags.getInt("patchSet",0);
if (patchSet==1) {
return "YMF281";
} else if (patchSet==2) {
return "YM2423";
} else if (patchSet==3) {
return "VRC7";
} else {
return "YM2413";
}
break;
}
case DIV_SYSTEM_OPL2:
case DIV_SYSTEM_OPL2_DRUMS:
return "YM3812";
break;
case DIV_SYSTEM_OPL3:
case DIV_SYSTEM_OPL3_DRUMS:{
int chipType=flags.getInt("chipType",0);
if (chipType==1) {
return "YMF289B";
} else {
return "YMF262";
}
break;
}
case DIV_SYSTEM_OPL4:
case DIV_SYSTEM_OPL4_DRUMS:
return "YMF278";
break;
case DIV_SYSTEM_MULTIPCM:
return "YMW258-F";
break;
case DIV_SYSTEM_RF5C68:{
int chipType=flags.getInt("chipType",0);
if (chipType==1) {
return "RF5C164";
} else {
return "RF5C68";
}
break;
}
case DIV_SYSTEM_OPZ:
return "YM2414";
break;
case DIV_SYSTEM_SEGAPCM:
case DIV_SYSTEM_SEGAPCM_COMPAT:
return "SegaPCM";
break;
case DIV_SYSTEM_VRC7:
return "VRC7";
break;
case DIV_SYSTEM_YM2610B:
case DIV_SYSTEM_YM2610B_CSM:
case DIV_SYSTEM_YM2610B_EXT:
return "YM2610B";
break;
case DIV_SYSTEM_SFX_BEEPER:
case DIV_SYSTEM_SFX_BEEPER_QUADTONE:
return "ZXS Beeper";
break;
case DIV_SYSTEM_SCC:
return "SCC";
break;
case DIV_SYSTEM_YM2610:
case DIV_SYSTEM_YM2610_CSM:
case DIV_SYSTEM_YM2610_EXT:
case DIV_SYSTEM_YM2610_FULL:
case DIV_SYSTEM_YM2610_FULL_EXT:
return "YM2610";
break;
case DIV_SYSTEM_OPL:
case DIV_SYSTEM_OPL_DRUMS:
return "YM3526";
break;
case DIV_SYSTEM_QSOUND:
return "QSound";
break;
case DIV_SYSTEM_X1_010:
return "X1-010";
break;
case DIV_SYSTEM_BUBSYS_WSG:
return "Konami WSG";
break;
case DIV_SYSTEM_ES5506:
return "ES5506";
break;
case DIV_SYSTEM_SCC_PLUS:
return "SCC+";
break;
case DIV_SYSTEM_SOUND_UNIT:
return "TSU";
break;
case DIV_SYSTEM_MSM6295:
return "MSM6295";
break;
case DIV_SYSTEM_MSM6258:
return "MSM6258";
break;
case DIV_SYSTEM_YMZ280B:
return "YMZ280B";
break;
case DIV_SYSTEM_NAMCO_15XX:
return "C15";
break;
case DIV_SYSTEM_NAMCO_CUS30:
return "C30";
break;
case DIV_SYSTEM_MSM5232:
return "MSM5232";
break;
case DIV_SYSTEM_K007232:
return "K007232";
break;
case DIV_SYSTEM_GA20:
return "GA20";
break;
case DIV_SYSTEM_PCM_DAC:
return "DAC";
break;
case DIV_SYSTEM_SM8521:
return "SM8521";
break;
case DIV_SYSTEM_PV1000:
return "PV-1000";
break;
case DIV_SYSTEM_K053260:
return "K053260";
break;
case DIV_SYSTEM_TED:
return "TED";
break;
case DIV_SYSTEM_C140:
return "C140";
break;
case DIV_SYSTEM_C219:
return "C219";
break;
case DIV_SYSTEM_ESFM:
return "ES1xxx";
default:
return FurnaceGUI::getSystemName(sys);
break;
}
}

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2023 tildearrow and contributors
* Copyright (C) 2021-2024 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

1501
src/gui/tileData.h Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2023 tildearrow and contributors
* Copyright (C) 2021-2024 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2023 tildearrow and contributors
* Copyright (C) 2021-2024 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

View file

@ -1,6 +1,6 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2023 tildearrow and contributors
* Copyright (C) 2021-2024 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by

Some files were not shown because too many files have changed in this diff Show more