lots of changes

- GUI: ability to load files
- GUI: arrow key navigation
- fix sample volumes
This commit is contained in:
tildearrow 2021-12-15 00:37:27 -05:00
parent 29d7be032a
commit 52087edceb
30 changed files with 377 additions and 37 deletions

View file

@ -22,12 +22,12 @@ if (WIN32)
add_subdirectory(extern/zlib)
set(HAVE_SDL2 SDL2-static)
set(HAVE_Z zlibstatic)
include_directories(extern/imgui extern/imgui/backends extern/zlib ${CMAKE_CURRENT_BINARY_DIR}/extern/zlib extern/fmt/include)
include_directories(extern/imgui extern/imgui/backends extern/igfd extern/zlib ${CMAKE_CURRENT_BINARY_DIR}/extern/zlib extern/fmt/include)
else()
if (BUILD_GUI)
set(SDL_SHARED ON)
add_subdirectory(extern/SDL)
include_directories(extern/SDL/include extern/imgui extern/imgui/backends extern/fmt/include)
include_directories(extern/SDL/include extern/imgui extern/imgui/backends extern/igfd extern/fmt/include)
if (APPLE)
set(HAVE_SDL2 SDL2-static)
else()

View file

@ -117,5 +117,12 @@ class DivDispatch {
* @return the number of channels allocated.
*/
virtual int init(DivEngine* parent, int channels, int sugRate, bool pal);
/**
* quit the DivDispatch.
*/
virtual void quit();
virtual ~DivDispatch();
};
#endif

View file

@ -1023,7 +1023,7 @@ void DivEngine::renderSamples() {
// step 1: render to PCM
unsigned int k=0;
float mult=(float)(s->vol+100)/150.0f;
float mult=(float)(s->vol)/50.0f;
for (double j=0; j<s->length; j+=samplePitches[s->pitch]) {
if (k>=s->rendLength) {
break;
@ -1257,6 +1257,27 @@ void DivEngine::initDispatch() {
void DivEngine::quitDispatch() {
if (dispatch==NULL) return;
isBusy.lock();
dispatch->quit();
delete dispatch;
dispatch=NULL;
chans=0;
playing=false;
speedAB=false;
endOfSong=false;
ticks=0;
cycles=0;
curRow=0;
curOrder=0;
nextSpeed=3;
clockDrift=0;
changeOrd=-1;
changePos=0;
totalTicks=0;
totalCmds=0;
lastCmds=0;
cmdsPerSecond=0;
isBusy.unlock();
}
bool DivEngine::init(String outName) {

View file

@ -90,7 +90,6 @@ class DivEngine {
void processRow(int i, bool afterDelay);
void nextOrder();
void nextRow();
void reset();
// returns true if end of song.
bool nextTick();
bool perSystemEffect(int ch, unsigned char effect, unsigned char effectVal);
@ -113,6 +112,9 @@ class DivEngine {
// stop
void stop();
// reset playback state
void reset();
// get sys channel count
int getChannelCount(DivSystem sys);

View file

@ -24,3 +24,9 @@ bool DivDispatch::keyOffAffectsArp(int ch) {
int DivDispatch::init(DivEngine* p, int channels, int sugRate, bool pal) {
return 0;
}
void DivDispatch::quit() {
}
DivDispatch::~DivDispatch() {
}

View file

@ -377,3 +377,9 @@ int DivPlatformArcade::init(DivEngine* p, int channels, int sugRate, bool pal) {
return 13;
}
void DivPlatformArcade::quit() {
}
DivPlatformArcade::~DivPlatformArcade() {
}

View file

@ -53,5 +53,7 @@ class DivPlatformArcade: public DivDispatch {
void tick();
bool isStereo();
int init(DivEngine* parent, int channels, int sugRate, bool pal);
void quit();
~DivPlatformArcade();
};
#endif

View file

@ -314,3 +314,9 @@ int DivPlatformC64::init(DivEngine* p, int channels, int sugRate, bool pal) {
return 3;
}
void DivPlatformC64::quit() {
}
DivPlatformC64::~DivPlatformC64() {
}

View file

@ -59,6 +59,8 @@ class DivPlatformC64: public DivDispatch {
void tick();
int init(DivEngine* parent, int channels, int sugRate, bool pal);
void setChipModel(bool is6581);
void quit();
~DivPlatformC64();
};
#endif

View file

@ -75,3 +75,9 @@ int DivPlatformDummy::init(DivEngine* p, int channels, int sugRate, bool pal) {
reset();
return channels;
}
void DivPlatformDummy::quit() {
}
DivPlatformDummy::~DivPlatformDummy() {
}

View file

@ -20,4 +20,6 @@ class DivPlatformDummy: public DivDispatch {
void reset();
void tick();
int init(DivEngine* parent, int channels, int sugRate, bool pal);
void quit();
~DivPlatformDummy();
};

View file

@ -295,3 +295,10 @@ int DivPlatformGB::init(DivEngine* p, int channels, int sugRate, bool pal) {
reset();
return 4;
}
void DivPlatformGB::quit() {
delete gb;
}
DivPlatformGB::~DivPlatformGB() {
}

View file

@ -42,6 +42,8 @@ class DivPlatformGB: public DivDispatch {
void tick();
bool isStereo();
int init(DivEngine* parent, int channels, int sugRate, bool pal);
void quit();
~DivPlatformGB();
};
#endif

View file

@ -413,3 +413,10 @@ int DivPlatformGenesis::init(DivEngine* p, int channels, int sugRate, bool pal)
reset();
return 10;
}
void DivPlatformGenesis::quit() {
psg.quit();
}
DivPlatformGenesis::~DivPlatformGenesis() {
}

View file

@ -56,5 +56,7 @@ class DivPlatformGenesis: public DivDispatch {
bool isStereo();
bool keyOffAffectsArp(int ch);
int init(DivEngine* parent, int channels, int sugRate, bool pal);
void quit();
~DivPlatformGenesis();
};
#endif

View file

@ -277,3 +277,10 @@ int DivPlatformGenesisExt::init(DivEngine* parent, int channels, int sugRate, bo
reset();
return 13;
}
void DivPlatformGenesisExt::quit() {
DivPlatformGenesis::quit();
}
DivPlatformGenesisExt::~DivPlatformGenesisExt() {
}

View file

@ -20,4 +20,6 @@ class DivPlatformGenesisExt: public DivPlatformGenesis {
void tick();
bool keyOffAffectsArp(int ch);
int init(DivEngine* parent, int channels, int sugRate, bool pal);
void quit();
~DivPlatformGenesisExt();
};

View file

@ -310,3 +310,9 @@ int DivPlatformNES::init(DivEngine* p, int channels, int sugRate, bool pal) {
reset();
return 5;
}
void DivPlatformNES::quit() {
}
DivPlatformNES::~DivPlatformNES() {
}

View file

@ -48,6 +48,8 @@ class DivPlatformNES: public DivDispatch {
void tick();
bool keyOffAffectsArp(int ch);
int init(DivEngine* parent, int channels, int sugRate, bool pal);
void quit();
~DivPlatformNES();
};
#endif

View file

@ -304,3 +304,10 @@ int DivPlatformPCE::init(DivEngine* p, int channels, int sugRate, bool pal) {
reset();
return 6;
}
void DivPlatformPCE::quit() {
delete pce;
}
DivPlatformPCE::~DivPlatformPCE() {
}

View file

@ -60,6 +60,8 @@ class DivPlatformPCE: public DivDispatch {
bool isStereo();
bool keyOffAffectsArp(int ch);
int init(DivEngine* parent, int channels, int sugRate, bool pal);
void quit();
~DivPlatformPCE();
};
#endif

View file

@ -183,3 +183,10 @@ int DivPlatformSMS::init(DivEngine* p, int channels, int sugRate, bool pal) {
reset();
return 4;
}
void DivPlatformSMS::quit() {
delete sn;
}
DivPlatformSMS::~DivPlatformSMS() {
}

View file

@ -38,6 +38,8 @@ class DivPlatformSMS: public DivDispatch {
void tick();
bool keyOffAffectsArp(int ch);
int init(DivEngine* parent, int channels, int sugRate, bool pal);
void quit();
~DivPlatformSMS();
};
#endif

View file

@ -529,3 +529,10 @@ int DivPlatformYM2610::init(DivEngine* p, int channels, int sugRate, bool pal) {
reset();
return 10;
}
void DivPlatformYM2610::quit() {
delete fm;
}
DivPlatformYM2610::~DivPlatformYM2610() {
}

View file

@ -66,5 +66,7 @@ class DivPlatformYM2610: public DivDispatch {
bool isStereo();
bool keyOffAffectsArp(int ch);
int init(DivEngine* parent, int channels, int sugRate, bool pal);
void quit();
~DivPlatformYM2610();
};
#endif

View file

@ -256,3 +256,10 @@ int DivPlatformYM2610Ext::init(DivEngine* parent, int channels, int sugRate, boo
reset();
return 16;
}
void DivPlatformYM2610Ext::quit() {
DivPlatformYM2610::quit();
}
DivPlatformYM2610Ext::~DivPlatformYM2610Ext() {
}

View file

@ -20,4 +20,6 @@ class DivPlatformYM2610Ext: public DivPlatformYM2610 {
void tick();
bool keyOffAffectsArp(int ch);
int init(DivEngine* parent, int channels, int sugRate, bool pal);
void quit();
~DivPlatformYM2610Ext();
};

View file

@ -778,7 +778,7 @@ bool DivEngine::nextTick() {
}
void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsigned int size) {
if (!playing) {
if (!playing || dispatch==NULL) {
memset(out[0],0,size*sizeof(float));
memset(out[1],0,size*sizeof(float));
return;
@ -821,7 +821,6 @@ void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsi
}
}
}
isBusy.unlock();
totalProcessed=(1+runPos)*got.rate/dispatch->rate;
for (size_t i=0; i<runtotal; i++) {
@ -844,7 +843,10 @@ void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsi
blip_read_samples(bb[1],bbOut[1],size,0);
}
if (out==NULL) return;
if (out==NULL) {
isBusy.unlock();
return;
}
if (dispatch->isStereo()) {
for (size_t i=0; i<size; i++) {
@ -857,4 +859,5 @@ void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsi
out[1][i]=(float)bbOut[0][i]/16384.0;
}
}
isBusy.unlock();
}

View file

@ -1,16 +1,23 @@
#include "gui.h"
#include "SDL_events.h"
#include "SDL_keycode.h"
#include "SDL_render.h"
#include "SDL_video.h"
#include "fonts.h"
#include "../ta-log.h"
#include "imgui.h"
#include "imgui_internal.h"
#include "ImGuiFileDialog.h"
#include "misc/cpp/imgui_stdlib.h"
#include <cstdio>
#include <fmt/printf.h>
#include <stdexcept>
#ifdef _WIN32
#include <windows.h>
#else
#include <unistd.h>
#endif
const int _ZERO=0;
const int _ONE=1;
const int _THREE=3;
@ -601,6 +608,7 @@ void FurnaceGUI::drawPattern() {
} else {
int volColor=(pat->data[i][3]*127)/chanVolMax;
if (volColor>127) volColor=127;
if (volColor<0) volColor=0;
sprintf(id,"%.2X##PV_%d_%d",pat->data[i][3],i,j);
ImGui::PushStyleColor(ImGuiCol_Text,volColors[volColor]);
}
@ -672,12 +680,7 @@ void FurnaceGUI::drawPattern() {
ImGui::PopStyleVar();
ImGui::PopFont();
}
if (ImGui::IsWindowFocused()) {
curWindow=GUI_WINDOW_PATTERN;
} else {
// TODO: what?!
curWindow=GUI_WINDOW_PATTERN;
}
if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_PATTERN;
ImGui::End();
}
@ -721,36 +724,140 @@ void FurnaceGUI::finishSelection() {
selecting=false;
}
void FurnaceGUI::moveCursor(int x, int y) {
finishSelection();
if (x!=0) {
if (x>0) {
for (int i=0; i<x; i++) {
if (++selStart.xFine>=3+e->song.pat[selStart.xCoarse].effectRows*2) {
selStart.xFine=0;
if (++selStart.xCoarse>=e->getChannelCount(e->song.system)) {
selStart.xCoarse=e->getChannelCount(e->song.system)-1;
selStart.xFine=2+e->song.pat[selStart.xCoarse].effectRows*2;
}
}
}
} else {
for (int i=0; i<-x; i++) {
if (--selStart.xFine<0) {
if (--selStart.xCoarse<0) {
selStart.xCoarse=0;
selStart.xFine=0;
} else {
selStart.xFine=2+e->song.pat[selStart.xCoarse].effectRows*2;
}
}
}
}
}
if (y!=0) {
selStart.y+=y;
if (selStart.y<0) selStart.y=0;
if (selStart.y>=e->song.patLen) selStart.y=e->song.patLen-1;
}
selEnd=selStart;
updateScroll(selStart.y);
}
void FurnaceGUI::editAdvance() {
finishSelection();
selStart.y+=editStep;
if (selStart.y>=e->song.patLen) selStart.y=e->song.patLen-1;
selEnd=selStart;
updateScroll(selStart.y);
}
void FurnaceGUI::doDelete() {
finishSelection();
int iCoarse=selStart.xCoarse;
int iFine=selStart.xFine;
int ord=e->getOrder();
for (; iCoarse<=selEnd.xCoarse; iCoarse++) {
DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][ord],true);
for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarse<selEnd.xCoarse || iFine<=selEnd.xFine); iFine++) {
for (int j=selStart.y; j<=selEnd.y; j++) {
//printf("deleting from: %d, %d, %d\n",iCoarse,iFine,j);
if (iFine==0) {
pat->data[j][iFine]=0;
}
pat->data[j][iFine+1]=(iFine<1)?0:-1;
}
}
iFine=0;
}
}
void FurnaceGUI::keyDown(SDL_Event& ev) {
printf("CUR WINDOW: %d\n",curWindow);
switch (curWindow) {
case GUI_WINDOW_PATTERN: {
if (selStart.xFine==0) { // note
try {
int num=12*curOctave+noteKeys.at(ev.key.keysym.sym);
DivPattern* pat=e->song.pat[selStart.xCoarse].getPattern(e->song.orders.ord[selStart.xCoarse][e->getOrder()],true);
switch (ev.key.keysym.sym) {
case SDLK_UP:
moveCursor(0,-1);
break;
case SDLK_DOWN:
moveCursor(0,1);
break;
case SDLK_LEFT:
moveCursor(-1,0);
break;
case SDLK_RIGHT:
moveCursor(1,0);
break;
case SDLK_PAGEUP:
moveCursor(0,-16);
break;
case SDLK_PAGEDOWN:
moveCursor(0,16);
break;
case SDLK_DELETE:
doDelete();
break;
default:
if (selStart.xFine==0) { // note
try {
int num=12*curOctave+noteKeys.at(ev.key.keysym.sym);
DivPattern* pat=e->song.pat[selStart.xCoarse].getPattern(e->song.orders.ord[selStart.xCoarse][e->getOrder()],true);
pat->data[selStart.y][0]=num%12;
pat->data[selStart.y][1]=num/12;
editAdvance();
} catch (std::out_of_range& e) {
}
} else { // value
try {
int num=valueKeys.at(ev.key.keysym.sym);
DivPattern* pat=e->song.pat[selStart.xCoarse].getPattern(e->song.orders.ord[selStart.xCoarse][e->getOrder()],true);
if (pat->data[selStart.y][selStart.xFine+1]==-1) pat->data[selStart.y][selStart.xFine+1]=0;
pat->data[selStart.y][selStart.xFine+1]=((pat->data[selStart.y][selStart.xFine+1]<<4)|num)&0xff;
curNibble=!curNibble;
if (!curNibble) editAdvance();
} catch (std::out_of_range& e) {
}
pat->data[selStart.y][0]=num%12;
pat->data[selStart.y][1]=num/12;
editAdvance();
} catch (std::out_of_range& e) {
}
} else { // value
try {
int num=valueKeys.at(ev.key.keysym.sym);
DivPattern* pat=e->song.pat[selStart.xCoarse].getPattern(e->song.orders.ord[selStart.xCoarse][e->getOrder()],true);
if (pat->data[selStart.y][selStart.xFine+1]==-1) pat->data[selStart.y][selStart.xFine+1]=0;
pat->data[selStart.y][selStart.xFine+1]=((pat->data[selStart.y][selStart.xFine+1]<<4)|num)&0xff;
if (selStart.xFine==1) { // instrument
if (pat->data[selStart.y][selStart.xFine+1]>=(int)e->song.ins.size()) {
pat->data[selStart.y][selStart.xFine+1]=(int)e->song.ins.size()-1;
}
if (e->song.ins.size()<16) {
curNibble=false;
editAdvance();
} else {
curNibble=!curNibble;
if (!curNibble) editAdvance();
}
} else if (selStart.xFine==2) { // volume
pat->data[selStart.y][selStart.xFine+1]&=e->getMaxVolumeChan(selStart.xCoarse);
if (e->getMaxVolumeChan(selStart.xCoarse)<16) {
curNibble=false;
editAdvance();
} else {
curNibble=!curNibble;
if (!curNibble) editAdvance();
}
} else {
curNibble=!curNibble;
if (!curNibble) editAdvance();
}
} catch (std::out_of_range& e) {
}
}
break;
}
break;
}
@ -763,6 +870,57 @@ void FurnaceGUI::keyUp(SDL_Event& ev) {
}
int FurnaceGUI::load(String path) {
if (!path.empty()) {
logI("loading module...\n");
FILE* f=fopen(path.c_str(),"rb");
if (f==NULL) {
perror("error");
return 1;
}
if (fseek(f,0,SEEK_END)<0) {
perror("size error");
fclose(f);
return 1;
}
ssize_t len=ftell(f);
if (len==0x7fffffffffffffff) {
perror("could not get file length");
fclose(f);
return 1;
}
if (len<1) {
if (len==0) {
printf("that file is empty!\n");
} else {
perror("tell error");
}
fclose(f);
return 1;
}
unsigned char* file=new unsigned char[len];
if (fseek(f,0,SEEK_SET)<0) {
perror("size error");
fclose(f);
return 1;
}
if (fread(file,1,(size_t)len,f)!=(size_t)len) {
perror("read error");
fclose(f);
return 1;
}
fclose(f);
e->quitDispatch();
if (!e->load((void*)file,(size_t)len)) {
logE("could not open file!\n");
return 1;
}
e->initDispatch();
e->reset();
}
return 0;
}
bool FurnaceGUI::loop() {
while (!quit) {
SDL_Event ev;
@ -829,7 +987,9 @@ bool FurnaceGUI::loop() {
ImGui::BeginMainMenuBar();
if (ImGui::BeginMenu("file")) {
ImGui::MenuItem("new");
ImGui::MenuItem("open...");
if (ImGui::MenuItem("open...")) {
ImGuiFileDialog::Instance()->OpenDialog("FileDialog","Open File","DefleMask module{.dmf},.*",workingDir);
}
ImGui::Separator();
ImGui::MenuItem("save");
ImGui::MenuItem("save as...");
@ -857,6 +1017,34 @@ bool FurnaceGUI::loop() {
drawInsEdit();
drawPattern();
if (ImGuiFileDialog::Instance()->Display("FileDialog")) {
if (ImGuiFileDialog::Instance()->IsOk()) {
fileName=ImGuiFileDialog::Instance()->GetFilePathName();
if (fileName!="") {
if (isSaving) {
if (fileName.size()<4 || fileName.rfind(".dmf")!=fileName.size()-4) {
fileName+=".dmf";
}
}
String copyOfName=fileName;
if (isSaving) {
printf("saving: %s\n",copyOfName.c_str());
//SaveFile(copyOfName.c_str());
isSaving=false;
} else {
load(copyOfName);
}
}
}
workingDir=ImGuiFileDialog::Instance()->GetCurrentPath();
#ifdef _WIN32
workingDir+='\\';
#else
workingDir+='/';
#endif
ImGuiFileDialog::Instance()->Close();
}
SDL_SetRenderDrawColor(sdlRend,uiColors[GUI_COLOR_BACKGROUND].x*255,
uiColors[GUI_COLOR_BACKGROUND].y*255,
uiColors[GUI_COLOR_BACKGROUND].z*255,
@ -872,6 +1060,17 @@ bool FurnaceGUI::loop() {
bool FurnaceGUI::init() {
float dpiScaleF;
char tempDir[4096];
#ifdef _WIN32
GetCurrentDirectory(4095,tempDir);
workingDir=tempDir;
workingDir+='\\';
#else
getcwd(tempDir,4095);
workingDir=tempDir;
workingDir+='/';
#endif
sdlWin=SDL_CreateWindow("Furnace",SDL_WINDOWPOS_CENTERED,SDL_WINDOWPOS_CENTERED,scrW*dpiScale,scrH*dpiScale,SDL_WINDOW_RESIZABLE|SDL_WINDOW_ALLOW_HIGHDPI);
if (sdlWin==NULL) {
logE("could not open window!\n");
@ -918,6 +1117,7 @@ bool FurnaceGUI::init() {
FurnaceGUI::FurnaceGUI():
e(NULL),
quit(false),
isSaving(false),
scrW(1280),
scrH(800),
dpiScale(1),
@ -964,7 +1164,7 @@ FurnaceGUI::FurnaceGUI():
uiColors[GUI_COLOR_PATTERN_EFFECT_SYS_SECONDARY]=ImVec4(0.0f,1.0f,0.5f,1.0f);
uiColors[GUI_COLOR_PATTERN_EFFECT_MISC]=ImVec4(0.3f,0.3f,1.0f,1.0f);
for (int i=0; i<63; i++) {
for (int i=0; i<64; i++) {
ImVec4 col1=uiColors[GUI_COLOR_PATTERN_VOLUME_MIN];
ImVec4 col2=uiColors[GUI_COLOR_PATTERN_VOLUME_HALF];
ImVec4 col3=uiColors[GUI_COLOR_PATTERN_VOLUME_MAX];

View file

@ -55,7 +55,9 @@ class FurnaceGUI {
SDL_Window* sdlWin;
SDL_Renderer* sdlRend;
bool quit;
String workingDir, fileName;
bool quit, isSaving;
int scrW, scrH;
@ -102,11 +104,15 @@ class FurnaceGUI {
void updateSelection(int xCoarse, int xFine, int y);
void finishSelection();
void moveCursor(int x, int y);
void editAdvance();
void doDelete();
void keyDown(SDL_Event& ev);
void keyUp(SDL_Event& ev);
int load(String path);
public:
const char* noteName(short note, short octave);
void bindEngine(DivEngine* eng);