mirror of
https://github.com/tildearrow/furnace.git
synced 2024-11-30 08:23:01 +00:00
GUI: macro editor (kind of) and order view
This commit is contained in:
parent
dcbb41bc2f
commit
4b32599237
4 changed files with 233 additions and 10 deletions
|
@ -104,7 +104,7 @@ unsigned char systemToFile(DivSystem val) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int getChannelCount(DivSystem sys) {
|
int DivEngine::getChannelCount(DivSystem sys) {
|
||||||
switch (sys) {
|
switch (sys) {
|
||||||
case DIV_SYSTEM_NULL:
|
case DIV_SYSTEM_NULL:
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -132,6 +132,78 @@ int getChannelCount(DivSystem sys) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool DivEngine::isFMSystem(DivSystem sys) {
|
||||||
|
return (sys==DIV_SYSTEM_GENESIS ||
|
||||||
|
sys==DIV_SYSTEM_GENESIS_EXT ||
|
||||||
|
sys==DIV_SYSTEM_ARCADE ||
|
||||||
|
sys==DIV_SYSTEM_YM2610 ||
|
||||||
|
sys==DIV_SYSTEM_YM2610_EXT ||
|
||||||
|
sys==DIV_SYSTEM_YMU759);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* chanShortNames[11][17]={
|
||||||
|
{"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "PCM"}, // YMU759
|
||||||
|
{"F1", "F2", "F3", "F4", "F5", "F6", "S1", "S2", "S3", "NO"}, // Genesis
|
||||||
|
{"F1", "F2", "O1", "O2", "O3", "O4", "F4", "F5", "F6", "S1", "S2", "S3", "S4"}, // Genesis (extended channel 3)
|
||||||
|
{"S1", "S2", "S3", "NO"}, // SMS
|
||||||
|
{"S1", "S2", "WA", "NO"}, // GB
|
||||||
|
{"CH1", "CH2", "CH3", "CH4", "CH5", "CH6"}, // PCE
|
||||||
|
{"S1", "S2", "TR", "NO", "PCM"}, // NES
|
||||||
|
{"CH1", "CH2", "CH3"}, // C64
|
||||||
|
{"F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "P1", "P2", "P3", "P4", "P5"}, // Arcade
|
||||||
|
{"F1", "F2", "F3", "F4", "S1", "S2", "S3", "P1", "P2", "P3", "P4", "P5", "P6"}, // YM2610
|
||||||
|
{"F1", "O1", "O2", "O3", "O4", "F3", "F4", "S1", "S2", "S3", "P1", "P2", "P3", "P4", "P5", "P6"}, // YM2610 (extended channel 2)
|
||||||
|
};
|
||||||
|
|
||||||
|
const char* DivEngine::getChannelShortName(int chan) {
|
||||||
|
switch (song.system) {
|
||||||
|
case DIV_SYSTEM_NULL: case DIV_SYSTEM_YMU759:
|
||||||
|
return chanShortNames[0][chan];
|
||||||
|
break;
|
||||||
|
case DIV_SYSTEM_GENESIS:
|
||||||
|
return chanShortNames[1][chan];
|
||||||
|
break;
|
||||||
|
case DIV_SYSTEM_GENESIS_EXT:
|
||||||
|
return chanShortNames[2][chan];
|
||||||
|
break;
|
||||||
|
case DIV_SYSTEM_SMS:
|
||||||
|
return chanShortNames[3][chan];
|
||||||
|
break;
|
||||||
|
case DIV_SYSTEM_GB:
|
||||||
|
return chanShortNames[4][chan];
|
||||||
|
break;
|
||||||
|
case DIV_SYSTEM_PCE:
|
||||||
|
return chanShortNames[5][chan];
|
||||||
|
break;
|
||||||
|
case DIV_SYSTEM_NES:
|
||||||
|
return chanShortNames[6][chan];
|
||||||
|
break;
|
||||||
|
case DIV_SYSTEM_C64_6581: case DIV_SYSTEM_C64_8580:
|
||||||
|
return chanShortNames[7][chan];
|
||||||
|
break;
|
||||||
|
case DIV_SYSTEM_ARCADE:
|
||||||
|
return chanShortNames[8][chan];
|
||||||
|
break;
|
||||||
|
case DIV_SYSTEM_YM2610:
|
||||||
|
return chanShortNames[9][chan];
|
||||||
|
break;
|
||||||
|
case DIV_SYSTEM_YM2610_EXT:
|
||||||
|
return chanShortNames[10][chan];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return "??";
|
||||||
|
}
|
||||||
|
|
||||||
|
int DivEngine::getMaxVolume() {
|
||||||
|
switch (song.system) {
|
||||||
|
case DIV_SYSTEM_PCE:
|
||||||
|
return 31;
|
||||||
|
default:
|
||||||
|
return 15;
|
||||||
|
}
|
||||||
|
return 127;
|
||||||
|
}
|
||||||
|
|
||||||
bool DivEngine::load(void* f, size_t slen) {
|
bool DivEngine::load(void* f, size_t slen) {
|
||||||
unsigned char* file;
|
unsigned char* file;
|
||||||
size_t len;
|
size_t len;
|
||||||
|
@ -350,12 +422,7 @@ bool DivEngine::load(void* f, size_t slen) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ins->mode) { // FM
|
if (ins->mode) { // FM
|
||||||
if (ds.system!=DIV_SYSTEM_GENESIS &&
|
if (!isFMSystem(ds.system)) {
|
||||||
ds.system!=DIV_SYSTEM_GENESIS_EXT &&
|
|
||||||
ds.system!=DIV_SYSTEM_ARCADE &&
|
|
||||||
ds.system!=DIV_SYSTEM_YM2610 &&
|
|
||||||
ds.system!=DIV_SYSTEM_YM2610_EXT &&
|
|
||||||
ds.system!=DIV_SYSTEM_YMU759) {
|
|
||||||
logE("FM instrument in non-FM system. oopsie?\n");
|
logE("FM instrument in non-FM system. oopsie?\n");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -996,6 +1063,7 @@ void DivEngine::play() {
|
||||||
curRow=0;
|
curRow=0;
|
||||||
clockDrift=0;
|
clockDrift=0;
|
||||||
cycles=0;
|
cycles=0;
|
||||||
|
speedAB=false;
|
||||||
playing=true;
|
playing=true;
|
||||||
isBusy.unlock();
|
isBusy.unlock();
|
||||||
}
|
}
|
||||||
|
@ -1028,6 +1096,7 @@ void DivEngine::setOrder(unsigned char order) {
|
||||||
curRow=0;
|
curRow=0;
|
||||||
clockDrift=0;
|
clockDrift=0;
|
||||||
cycles=0;
|
cycles=0;
|
||||||
|
speedAB=false;
|
||||||
}
|
}
|
||||||
isBusy.unlock();
|
isBusy.unlock();
|
||||||
}
|
}
|
||||||
|
|
|
@ -113,6 +113,18 @@ class DivEngine {
|
||||||
// stop
|
// stop
|
||||||
void stop();
|
void stop();
|
||||||
|
|
||||||
|
// get sys channel count
|
||||||
|
int getChannelCount(DivSystem sys);
|
||||||
|
|
||||||
|
// is FM system
|
||||||
|
bool isFMSystem(DivSystem sys);
|
||||||
|
|
||||||
|
// get channel short name
|
||||||
|
const char* getChannelShortName(int chan);
|
||||||
|
|
||||||
|
// get max STD volume
|
||||||
|
int getMaxVolume();
|
||||||
|
|
||||||
// get current order
|
// get current order
|
||||||
unsigned char getOrder();
|
unsigned char getOrder();
|
||||||
|
|
||||||
|
|
133
src/gui/gui.cpp
133
src/gui/gui.cpp
|
@ -1,11 +1,15 @@
|
||||||
#include "gui.h"
|
#include "gui.h"
|
||||||
|
#include "SDL_events.h"
|
||||||
|
#include "SDL_video.h"
|
||||||
#include "fonts.h"
|
#include "fonts.h"
|
||||||
#include "../ta-log.h"
|
#include "../ta-log.h"
|
||||||
#include "imgui.h"
|
#include "imgui.h"
|
||||||
#include "misc/cpp/imgui_stdlib.h"
|
#include "misc/cpp/imgui_stdlib.h"
|
||||||
|
#include <cstdio>
|
||||||
#include <fmt/printf.h>
|
#include <fmt/printf.h>
|
||||||
|
|
||||||
const int _ZERO=0;
|
const int _ZERO=0;
|
||||||
|
const int _ONE=1;
|
||||||
const int _THREE=3;
|
const int _THREE=3;
|
||||||
const int _SEVEN=7;
|
const int _SEVEN=7;
|
||||||
const int _FIFTEEN=15;;
|
const int _FIFTEEN=15;;
|
||||||
|
@ -26,6 +30,23 @@ bool FurnaceGUI::loop() {
|
||||||
while (SDL_PollEvent(&ev)) {
|
while (SDL_PollEvent(&ev)) {
|
||||||
ImGui_ImplSDL2_ProcessEvent(&ev);
|
ImGui_ImplSDL2_ProcessEvent(&ev);
|
||||||
switch (ev.type) {
|
switch (ev.type) {
|
||||||
|
case SDL_MOUSEMOTION:
|
||||||
|
if (macroDragActive) {
|
||||||
|
// do macro drag here!
|
||||||
|
if (macroDragLen>0) {
|
||||||
|
int x=(ev.motion.x-macroDragStart.x)*macroDragLen/macroDragAreaSize.x;
|
||||||
|
if (x<0) x=0;
|
||||||
|
if (x>=macroDragLen) x=macroDragLen-1;
|
||||||
|
int y=round(macroDragMax-((ev.motion.y-macroDragStart.y)*(double(macroDragMax-macroDragMin)/(double)macroDragAreaSize.y)));
|
||||||
|
if (y>macroDragMax) y=macroDragMax;
|
||||||
|
if (y<macroDragMin) y=macroDragMin;
|
||||||
|
macroDragTarget[x]=y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SDL_MOUSEBUTTONUP:
|
||||||
|
macroDragActive=false;
|
||||||
|
break;
|
||||||
case SDL_QUIT:
|
case SDL_QUIT:
|
||||||
quit=true;
|
quit=true;
|
||||||
return true;
|
return true;
|
||||||
|
@ -68,6 +89,39 @@ bool FurnaceGUI::loop() {
|
||||||
}
|
}
|
||||||
ImGui::End();
|
ImGui::End();
|
||||||
|
|
||||||
|
char selID[16];
|
||||||
|
if (ImGui::Begin("Orders")) {
|
||||||
|
ImGui::BeginTable("OrdersTable",1+e->getChannelCount(e->song.system));
|
||||||
|
ImGui::TableNextRow();
|
||||||
|
ImGui::TableNextColumn();
|
||||||
|
for (int i=0; i<e->getChannelCount(e->song.system); i++) {
|
||||||
|
ImGui::TableNextColumn();
|
||||||
|
ImGui::Text("%s",e->getChannelShortName(i));
|
||||||
|
}
|
||||||
|
for (int i=0; i<e->song.ordersLen; i++) {
|
||||||
|
ImGui::TableNextRow();
|
||||||
|
if (e->getOrder()==i) ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0,0x40ffffff);
|
||||||
|
ImGui::TableNextColumn();
|
||||||
|
snprintf(selID,16,"%.2x##O_S%.2x",i,i);
|
||||||
|
if (ImGui::Selectable(selID)) {
|
||||||
|
e->setOrder(i);
|
||||||
|
}
|
||||||
|
for (int j=0; j<e->getChannelCount(e->song.system); j++) {
|
||||||
|
ImGui::TableNextColumn();
|
||||||
|
snprintf(selID,16,"%.2x##O_%.2x_%.2x",e->song.orders.ord[j][i],j,i);
|
||||||
|
if (ImGui::Selectable(selID)) {
|
||||||
|
if (e->song.orders.ord[j][i]<0x7f) e->song.orders.ord[j][i]++;
|
||||||
|
}
|
||||||
|
if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) {
|
||||||
|
if (e->song.orders.ord[j][i]>0) e->song.orders.ord[j][i]--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::EndTable();
|
||||||
|
}
|
||||||
|
ImGui::End();
|
||||||
|
|
||||||
if (ImGui::Begin("Instruments")) {
|
if (ImGui::Begin("Instruments")) {
|
||||||
for (int i=0; i<e->song.ins.size(); i++) {
|
for (int i=0; i<e->song.ins.size(); i++) {
|
||||||
DivInstrument* ins=e->song.ins[i];
|
DivInstrument* ins=e->song.ins[i];
|
||||||
|
@ -84,7 +138,7 @@ bool FurnaceGUI::loop() {
|
||||||
} else {
|
} else {
|
||||||
DivInstrument* ins=e->song.ins[curIns];
|
DivInstrument* ins=e->song.ins[curIns];
|
||||||
ImGui::InputText("Name",&ins->name);
|
ImGui::InputText("Name",&ins->name);
|
||||||
ImGui::Checkbox("FM",&ins->mode);
|
if (e->isFMSystem(e->song.system)) ImGui::Checkbox("FM",&ins->mode);
|
||||||
|
|
||||||
if (ins->mode) { // FM
|
if (ins->mode) { // FM
|
||||||
ImGui::SliderScalar("Algorithm",ImGuiDataType_U8,&ins->fm.alg,&_ZERO,&_SEVEN);
|
ImGui::SliderScalar("Algorithm",ImGuiDataType_U8,&ins->fm.alg,&_ZERO,&_SEVEN);
|
||||||
|
@ -123,10 +177,72 @@ bool FurnaceGUI::loop() {
|
||||||
ImGui::PopID();
|
ImGui::PopID();
|
||||||
}
|
}
|
||||||
} else { // STD
|
} else { // STD
|
||||||
|
float asFloat[128];
|
||||||
|
float loopIndicator[128];
|
||||||
|
|
||||||
|
// volume macro
|
||||||
|
ImGui::Separator();
|
||||||
|
ImGui::Text("Volume Macro");
|
||||||
|
for (int i=0; i<ins->std.volMacroLen; i++) {
|
||||||
|
asFloat[i]=ins->std.volMacro[i];
|
||||||
|
loopIndicator[i]=(ins->std.volMacroLoop!=-1 && i>=ins->std.volMacroLoop);
|
||||||
|
}
|
||||||
|
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding,ImVec2(0.0f,0.0f));
|
||||||
|
int volMax=e->getMaxVolume();
|
||||||
|
ImGui::PlotHistogram("##IVolMacro",asFloat,ins->std.volMacroLen,0,NULL,0,volMax,ImVec2(400.0f*dpiScale,200.0f*dpiScale));
|
||||||
|
if (ImGui::IsItemClicked(ImGuiMouseButton_Left)) {
|
||||||
|
macroDragStart=ImGui::GetItemRectMin();
|
||||||
|
macroDragAreaSize=ImVec2(400.0f*dpiScale,200.0f*dpiScale);
|
||||||
|
macroDragMin=0;
|
||||||
|
macroDragMax=volMax;
|
||||||
|
macroDragLen=ins->std.volMacroLen;
|
||||||
|
macroDragActive=true;
|
||||||
|
macroDragTarget=ins->std.volMacro;
|
||||||
|
printf("Start.\n");
|
||||||
|
}
|
||||||
|
ImGui::PlotHistogram("##IVolMacroLoop",loopIndicator,ins->std.volMacroLen,0,NULL,0,1,ImVec2(400.0f*dpiScale,16.0f*dpiScale));
|
||||||
|
if (ImGui::IsItemClicked(ImGuiMouseButton_Left)) {
|
||||||
|
macroLoopDragStart=ImGui::GetItemRectMin();
|
||||||
|
macroLoopDragAreaSize=ImVec2(400.0f*dpiScale,16.0f*dpiScale);
|
||||||
|
macroLoopDragLen=ins->std.volMacroLen;
|
||||||
|
macroLoopDragTarget=&ins->std.volMacroLoop;
|
||||||
|
macroLoopDragActive=true;
|
||||||
|
}
|
||||||
|
ImGui::PopStyleVar();
|
||||||
|
if (ImGui::InputScalar("Length##IVolMacroL",ImGuiDataType_U8,&ins->std.volMacroLen,&_ONE,&_THREE)) {
|
||||||
|
if (ins->std.volMacroLen>127) ins->std.volMacroLen=127;
|
||||||
|
}
|
||||||
|
|
||||||
|
// arp macro
|
||||||
|
ImGui::Separator();
|
||||||
|
ImGui::Text("Arpeggio Macro");
|
||||||
|
for (int i=0; i<ins->std.arpMacroLen; i++) {
|
||||||
|
asFloat[i]=ins->std.arpMacro[i];
|
||||||
|
}
|
||||||
|
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding,ImVec2(0.0f,0.0f));
|
||||||
|
ImGui::PlotHistogram("##IArpMacro",asFloat,ins->std.arpMacroLen,0,NULL,-92,82,ImVec2(400.0f*dpiScale,200.0f*dpiScale));
|
||||||
|
if (ImGui::IsItemClicked(ImGuiMouseButton_Left)) {
|
||||||
|
macroDragStart=ImGui::GetItemRectMin();
|
||||||
|
macroDragAreaSize=ImVec2(400.0f*dpiScale,200.0f*dpiScale);
|
||||||
|
macroDragMin=-92;
|
||||||
|
macroDragMax=82;
|
||||||
|
macroDragLen=ins->std.arpMacroLen;
|
||||||
|
macroDragActive=true;
|
||||||
|
macroDragTarget=ins->std.arpMacro;
|
||||||
|
printf("Start.\n");
|
||||||
|
}
|
||||||
|
ImGui::PopStyleVar();
|
||||||
|
if (ImGui::InputScalar("Length##IArpMacroL",ImGuiDataType_U8,&ins->std.arpMacroLen,&_ONE,&_THREE)) {
|
||||||
|
if (ins->std.arpMacroLen>127) ins->std.arpMacroLen=127;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
ImGui::End();
|
||||||
|
|
||||||
|
if (ImGui::Begin("Pattern")) {
|
||||||
|
ImGui::Text("TODO");
|
||||||
|
}
|
||||||
ImGui::End();
|
ImGui::End();
|
||||||
|
|
||||||
SDL_RenderClear(sdlRend);
|
SDL_RenderClear(sdlRend);
|
||||||
|
@ -138,11 +254,17 @@ bool FurnaceGUI::loop() {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FurnaceGUI::init() {
|
bool FurnaceGUI::init() {
|
||||||
|
float dpiScaleF;
|
||||||
|
|
||||||
sdlWin=SDL_CreateWindow("Furnace",SDL_WINDOWPOS_CENTERED,SDL_WINDOWPOS_CENTERED,scrW*dpiScale,scrH*dpiScale,SDL_WINDOW_RESIZABLE|SDL_WINDOW_ALLOW_HIGHDPI);
|
sdlWin=SDL_CreateWindow("Furnace",SDL_WINDOWPOS_CENTERED,SDL_WINDOWPOS_CENTERED,scrW*dpiScale,scrH*dpiScale,SDL_WINDOW_RESIZABLE|SDL_WINDOW_ALLOW_HIGHDPI);
|
||||||
if (sdlWin==NULL) {
|
if (sdlWin==NULL) {
|
||||||
logE("could not open window!\n");
|
logE("could not open window!\n");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
SDL_GetDisplayDPI(SDL_GetWindowDisplayIndex(sdlWin),&dpiScaleF,NULL,NULL);
|
||||||
|
dpiScale=round(dpiScaleF/96.0f);
|
||||||
|
if (dpiScale<1) dpiScale=1;
|
||||||
|
SDL_SetWindowSize(sdlWin,scrW*dpiScale,scrH*dpiScale);
|
||||||
|
|
||||||
sdlRend=SDL_CreateRenderer(sdlWin,-1,SDL_RENDERER_ACCELERATED|SDL_RENDERER_PRESENTVSYNC|SDL_RENDERER_TARGETTEXTURE);
|
sdlRend=SDL_CreateRenderer(sdlWin,-1,SDL_RENDERER_ACCELERATED|SDL_RENDERER_PRESENTVSYNC|SDL_RENDERER_TARGETTEXTURE);
|
||||||
|
|
||||||
|
@ -178,5 +300,12 @@ FurnaceGUI::FurnaceGUI():
|
||||||
scrH(800),
|
scrH(800),
|
||||||
dpiScale(1),
|
dpiScale(1),
|
||||||
curIns(0),
|
curIns(0),
|
||||||
curOctave(3) {
|
curOctave(3),
|
||||||
|
macroDragStart(0,0),
|
||||||
|
macroDragAreaSize(0,0),
|
||||||
|
macroDragTarget(NULL),
|
||||||
|
macroDragLen(0),
|
||||||
|
macroDragMin(0),
|
||||||
|
macroDragMax(0),
|
||||||
|
macroDragActive(false) {
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,19 @@ class FurnaceGUI {
|
||||||
|
|
||||||
int curIns, curOctave;
|
int curIns, curOctave;
|
||||||
|
|
||||||
|
ImVec2 macroDragStart;
|
||||||
|
ImVec2 macroDragAreaSize;
|
||||||
|
int* macroDragTarget;
|
||||||
|
int macroDragLen;
|
||||||
|
int macroDragMin, macroDragMax;
|
||||||
|
bool macroDragActive;
|
||||||
|
|
||||||
|
ImVec2 macroLoopDragStart;
|
||||||
|
ImVec2 macroLoopDragAreaSize;
|
||||||
|
signed char* macroLoopDragTarget;
|
||||||
|
int macroLoopDragLen;
|
||||||
|
bool macroLoopDragActive;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void bindEngine(DivEngine* eng);
|
void bindEngine(DivEngine* eng);
|
||||||
bool loop();
|
bool loop();
|
||||||
|
|
Loading…
Reference in a new issue