From 09a30570dedbae40e5eb1f7db5d9245d8b69bf02 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 18 Mar 2023 04:22:50 -0500 Subject: [PATCH] GUI: add an FM preview currently for OPN only --- src/gui/dataList.cpp | 3 ++ src/gui/debugWindow.cpp | 4 -- src/gui/doAction.cpp | 7 +++ src/gui/fmPreview.cpp | 84 +++++++++++++++--------------- src/gui/gui.cpp | 8 +++ src/gui/gui.h | 5 +- src/gui/insEdit.cpp | 112 +++++++++++++++++++++++++++------------- 7 files changed, 141 insertions(+), 82 deletions(-) diff --git a/src/gui/dataList.cpp b/src/gui/dataList.cpp index 73197a815..2a37c7e8e 100644 --- a/src/gui/dataList.cpp +++ b/src/gui/dataList.cpp @@ -455,12 +455,14 @@ void FurnaceGUI::drawInsList(bool asChild) { if (ImGui::Selectable(name.c_str(),(i==-1)?(curIns<0 || curIns>=e->song.insLen):(curIns==i))) { curIns=i; wavePreviewInit=true; + updateFMPreview=true; } if (wantScrollList && curIns==i) ImGui::SetScrollHereY(); if (settings.insFocusesPattern && patternOpen && ImGui::IsItemActivated()) { nextWindow=GUI_WINDOW_PATTERN; curIns=i; wavePreviewInit=true; + updateFMPreview=true; } if (ImGui::IsItemHovered() && i>=0 && !mobileUI) { ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_TEXT]); @@ -474,6 +476,7 @@ void FurnaceGUI::drawInsList(bool asChild) { if (i>=0) { if (ImGui::BeginPopupContextItem("InsRightMenu")) { curIns=i; + updateFMPreview=true; ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_TEXT]); if (ImGui::MenuItem("replace...")) { doAction((curIns>=0 && curIns<(int)e->song.ins.size())?GUI_ACTION_INS_LIST_OPEN_REPLACE:GUI_ACTION_INS_LIST_OPEN); diff --git a/src/gui/debugWindow.cpp b/src/gui/debugWindow.cpp index 5042ec3a4..09610bdf8 100644 --- a/src/gui/debugWindow.cpp +++ b/src/gui/debugWindow.cpp @@ -520,10 +520,6 @@ void FurnaceGUI::drawDebug() { ImGui::TreePop(); } if (ImGui::TreeNode("FM Preview")) { - if (ImGui::Button("Generate")) { - DivInstrument* ins=e->getIns(curIns); - if (ins!=NULL) renderFMPreview(ins->fm); - } float asFloat[FM_PREVIEW_SIZE]; for (int i=0; i=(int)e->song.ins.size()) { @@ -134,6 +135,7 @@ void FurnaceGUI::doAction(int what) { } wavePreviewInit=true; wantScrollList=true; + updateFMPreview=true; break; case GUI_ACTION_STEP_UP: if (++editStep>64) editStep=64; @@ -593,6 +595,7 @@ void FurnaceGUI::doAction(int what) { wantScrollList=true; MARK_MODIFIED; wavePreviewInit=true; + updateFMPreview=true; } break; case GUI_ACTION_INS_LIST_DUPLICATE: @@ -606,6 +609,7 @@ void FurnaceGUI::doAction(int what) { wantScrollList=true; MARK_MODIFIED; wavePreviewInit=true; + updateFMPreview=true; } } break; @@ -653,11 +657,13 @@ void FurnaceGUI::doAction(int what) { if (--curIns<0) curIns=0; wantScrollList=true; wavePreviewInit=true; + updateFMPreview=true; break; case GUI_ACTION_INS_LIST_DOWN: if (++curIns>=(int)e->song.ins.size()) curIns=((int)e->song.ins.size())-1; wantScrollList=true; wavePreviewInit=true; + updateFMPreview=true; break; case GUI_ACTION_WAVE_LIST_ADD: @@ -1366,6 +1372,7 @@ void FurnaceGUI::doAction(int what) { nextWindow=GUI_WINDOW_INS_EDIT; MARK_MODIFIED; wavePreviewInit=true; + updateFMPreview=true; } break; } diff --git a/src/gui/fmPreview.cpp b/src/gui/fmPreview.cpp index 8f6dd14fb..eaf449ed9 100644 --- a/src/gui/fmPreview.cpp +++ b/src/gui/fmPreview.cpp @@ -22,61 +22,63 @@ #include "../../extern/opn/ym3438.h" #define FM_WRITE(addr,val) \ - OPN2_Write(&fm,0,(addr)); \ + OPN2_Write((ym3438_t*)fmPreviewOPN,0,(addr)); \ do { \ - OPN2_Clock(&fm,out); \ - } while (fm.write_busy); \ - OPN2_Write(&fm,1,(val)); \ + OPN2_Clock((ym3438_t*)fmPreviewOPN,out); \ + } while (((ym3438_t*)fmPreviewOPN)->write_busy); \ + OPN2_Write((ym3438_t*)fmPreviewOPN,1,(val)); \ do { \ - OPN2_Clock(&fm,out); \ - } while (fm.write_busy); \ + OPN2_Clock((ym3438_t*)fmPreviewOPN,out); \ + } 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) { - ym3438_t fm; + if (fmPreviewOPN==NULL) { + fmPreviewOPN=new ym3438_t; + } short out[2]; int aOut=0; + bool mult0=false; - OPN2_Reset(&fm); - OPN2_SetChipType(&fm,ym3438_mode_opn); + if (pos==0) { + OPN2_Reset((ym3438_t*)fmPreviewOPN); + OPN2_SetChipType((ym3438_t*)fmPreviewOPN,ym3438_mode_opn); - // set params - FM_WRITE(0x50,31); // AR - FM_WRITE(0x54,31); - FM_WRITE(0x58,31); - FM_WRITE(0x5c,31); - FM_WRITE(0x60,0); // DR - FM_WRITE(0x64,0); - FM_WRITE(0x68,0); - FM_WRITE(0x6c,0); - FM_WRITE(0x70,0); // D2R - FM_WRITE(0x74,0); - FM_WRITE(0x78,0); - FM_WRITE(0x7c,0); - FM_WRITE(0x80,0); // SL/RR - FM_WRITE(0x84,0); - FM_WRITE(0x88,0); - FM_WRITE(0x8c,0); - FM_WRITE(0xa4,0x0c); // frequency - FM_WRITE(0xa0,0); - FM_WRITE(0xb0,(params.alg&7)|((params.fb&7)<<3)); // ALG/FB - FM_WRITE(0xb4,0xc0); // pan - FM_WRITE(0x30,params.op[0].mult&15); // MULT - FM_WRITE(0x34,params.op[1].mult&15); - FM_WRITE(0x38,params.op[2].mult&15); - FM_WRITE(0x3c,params.op[3].mult&15); - FM_WRITE(0x40,params.op[0].tl&127); // TL - FM_WRITE(0x44,params.op[1].tl&127); - FM_WRITE(0x48,params.op[2].tl&127); - FM_WRITE(0x4c,params.op[3].tl&127); - FM_WRITE(0x28,0xf0); // key on + // 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*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); + } + 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 + } // render for (int i=0; ich_out[0]; if (aOut<-32768) aOut=-32768; if (aOut>32767) aOut=32767; fmPreview[i]=aOut; diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 95be83891..d1f401d39 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -1175,6 +1175,7 @@ void FurnaceGUI::valueInput(int num, bool direct, int target) { if (settings.absorbInsInput) { curIns=pat->data[cursor.y][target]; wavePreviewInit=true; + updateFMPreview=true; } makeUndo(GUI_UNDO_PATTERN_EDIT); if (direct) { @@ -3404,6 +3405,7 @@ bool FurnaceGUI::loop() { curIns=msg.data[0]; if (curIns>=(int)e->song.ins.size()) curIns=e->song.ins.size()-1; wavePreviewInit=true; + updateFMPreview=true; } break; case TA_MIDI_CONTROL: @@ -4158,6 +4160,7 @@ bool FurnaceGUI::loop() { } else { curIns=prevIns; wavePreviewInit=true; + updateFMPreview=true; } prevIns=-3; } @@ -5155,6 +5158,7 @@ bool FurnaceGUI::loop() { if (i!=DIV_INS_AMIGA) e->song.ins[curIns]->amiga.useSample=true; nextWindow=GUI_WINDOW_INS_EDIT; wavePreviewInit=true; + updateFMPreview=true; } MARK_MODIFIED; } @@ -6016,6 +6020,10 @@ FurnaceGUI::FurnaceGUI(): mobileEditButtonPos(0.7f,0.7f), mobileEditButtonSize(60.0f,60.0f), curSysSection(NULL), + updateFMPreview(true), + fmPreviewOn(false), + fmPreviewPaused(false), + fmPreviewOPN(NULL), pendingRawSampleDepth(8), pendingRawSampleChannels(1), pendingRawSampleUnsigned(false), diff --git a/src/gui/gui.h b/src/gui/gui.h index 8a24857e2..f3f96fb31 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -68,7 +68,7 @@ #define BIND_FOR(x) getKeyName(actionKeys[x],true).c_str() -#define FM_PREVIEW_SIZE 1024 +#define FM_PREVIEW_SIZE 512 // TODO: // - add colors for FM envelope and waveform @@ -1195,6 +1195,8 @@ class FurnaceGUI { const int* curSysSection; DivInstrumentFM opllPreview; short fmPreview[FM_PREVIEW_SIZE]; + bool updateFMPreview, fmPreviewOn, fmPreviewPaused; + void* fmPreviewOPN; String pendingRawSample; int pendingRawSampleDepth, pendingRawSampleChannels; @@ -1890,6 +1892,7 @@ class FurnaceGUI { void drawGBEnv(unsigned char vol, unsigned char len, unsigned char sLen, bool dir, const ImVec2& size); bool drawSysConf(int chan, DivSystem type, DivConfig& flags, bool modifyOnChange); void kvsConfig(DivInstrument* ins); + void drawFMPreview(const ImVec2& size); void renderFMPreview(const DivInstrumentFM& params, int pos=0); // these ones offer ctrl-wheel fine value changes. diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index 1b16e2dc0..abac70c1a 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -1259,9 +1259,10 @@ void FurnaceGUI::drawGBEnv(unsigned char vol, unsigned char len, unsigned char s #define P(x) if (x) { \ MARK_MODIFIED; \ e->notifyInsChange(curIns); \ + updateFMPreview=true; \ } -#define PARAMETER MARK_MODIFIED; e->notifyInsChange(curIns); +#define PARAMETER MARK_MODIFIED; e->notifyInsChange(curIns); updateFMPreview=true; String genericGuide(float value) { return fmt::sprintf("%d",(int)value); @@ -1279,42 +1280,67 @@ inline bool enBit30(const int val) { void FurnaceGUI::kvsConfig(DivInstrument* ins) { - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("(click to configure TL scaling)"); - } - int opCount=4; - if (ins->type==DIV_INS_OPLL) opCount=2; - if (ins->type==DIV_INS_OPL) opCount=(ins->fm.ops==4)?4:2; - if (ImGui::BeginPopupContextItem("IKVSOpt",ImGuiPopupFlags_MouseButtonLeft)) { - ImGui::Text("operator level changes with volume?"); - if (ImGui::BeginTable("KVSTable",4,ImGuiTableFlags_BordersInner)) { - ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthFixed); - ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthStretch); - ImGui::TableSetupColumn("c2",ImGuiTableColumnFlags_WidthFixed); - ImGui::TableSetupColumn("c3",ImGuiTableColumnFlags_WidthStretch); - for (int i=0; i<4; i++) { - int o=(opCount==4)?orderedOps[i]:i; - if (!(i&1)) ImGui::TableNextRow(); - const char* label="AUTO##OPKVS"; - if (ins->fm.op[o].kvs==0) { - label="NO##OPKVS"; - } else if (ins->fm.op[o].kvs==1) { - label="YES##OPKVS"; - } - ImGui::TableNextColumn(); - ImGui::Text("%d",i+1); - ImGui::TableNextColumn(); - ImGui::PushID(o); - if (ImGui::Button(label,ImVec2(ImGui::GetContentRegionAvail().x,0.0f))) { - if (++ins->fm.op[o].kvs>2) ins->fm.op[o].kvs=0; - PARAMETER; - } - ImGui::PopID(); - } - ImGui::EndTable(); + if (ins->type==DIV_INS_FM && fmPreviewOn) { + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("left click to restart\nmiddle click to pause\nright click to see algorithm"); + } + if (ImGui::IsItemClicked(ImGuiMouseButton_Left)) { + updateFMPreview=true; + } + if (ImGui::IsItemClicked(ImGuiMouseButton_Middle)) { + fmPreviewPaused=!fmPreviewPaused; + } + } else { + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("left click to configure TL scaling\nright click to see FM preview"); } - ImGui::EndPopup(); } + if (ImGui::IsItemClicked(ImGuiMouseButton_Right) && ins->type==DIV_INS_FM) { + fmPreviewOn=!fmPreviewOn; + } + if (!fmPreviewOn || ins->type!=DIV_INS_FM) { + int opCount=4; + if (ins->type==DIV_INS_OPLL) opCount=2; + if (ins->type==DIV_INS_OPL) opCount=(ins->fm.ops==4)?4:2; + if (ImGui::BeginPopupContextItem("IKVSOpt",ImGuiPopupFlags_MouseButtonLeft)) { + ImGui::Text("operator level changes with volume?"); + if (ImGui::BeginTable("KVSTable",4,ImGuiTableFlags_BordersInner)) { + ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthFixed); + ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthStretch); + ImGui::TableSetupColumn("c2",ImGuiTableColumnFlags_WidthFixed); + ImGui::TableSetupColumn("c3",ImGuiTableColumnFlags_WidthStretch); + for (int i=0; i<4; i++) { + int o=(opCount==4)?orderedOps[i]:i; + if (!(i&1)) ImGui::TableNextRow(); + const char* label="AUTO##OPKVS"; + if (ins->fm.op[o].kvs==0) { + label="NO##OPKVS"; + } else if (ins->fm.op[o].kvs==1) { + label="YES##OPKVS"; + } + ImGui::TableNextColumn(); + ImGui::Text("%d",i+1); + ImGui::TableNextColumn(); + ImGui::PushID(o); + if (ImGui::Button(label,ImVec2(ImGui::GetContentRegionAvail().x,0.0f))) { + if (++ins->fm.op[o].kvs>2) ins->fm.op[o].kvs=0; + PARAMETER; + } + ImGui::PopID(); + } + ImGui::EndTable(); + } + ImGui::EndPopup(); + } + } +} + +void FurnaceGUI::drawFMPreview(const ImVec2& size) { + float asFloat[FM_PREVIEW_SIZE]; + for (int i=0; isong.ins[curIns]; + if (updateFMPreview) { + renderFMPreview(ins->fm); + updateFMPreview=false; + } if (settings.insEditColorize) { pushAccentColors(uiColors[GUI_COLOR_INSTR_STD+ins->type],uiColors[GUI_COLOR_INSTR_STD+ins->type],uiColors[GUI_COLOR_INSTR_STD+ins->type],ImVec4(0.0f,0.0f,0.0f,0.0f)); } @@ -2167,6 +2198,7 @@ void FurnaceGUI::drawInsEdit() { curIns=i; ins=e->song.ins[curIns]; wavePreviewInit=true; + updateFMPreview=true; } } ImGui::EndCombo(); @@ -2306,7 +2338,15 @@ void FurnaceGUI::drawInsEdit() { P(CWSliderScalar(FM_NAME(FM_ALG),ImGuiDataType_U8,&ins->fm.alg,&_ZERO,&_SEVEN)); rightClickable P(CWSliderScalar(FM_NAME(FM_AMS),ImGuiDataType_U8,&ins->fm.ams,&_ZERO,&_THREE)); rightClickable ImGui::TableNextColumn(); - drawAlgorithm(ins->fm.alg,FM_ALGS_4OP,ImVec2(ImGui::GetContentRegionAvail().x,48.0*dpiScale)); + if (ins->type==DIV_INS_FM && fmPreviewOn) { + drawFMPreview(ImVec2(ImGui::GetContentRegionAvail().x,48.0*dpiScale)); + if (!fmPreviewPaused) { + renderFMPreview(ins->fm,1); + WAKE_UP; + } + } else { + drawAlgorithm(ins->fm.alg,FM_ALGS_4OP,ImVec2(ImGui::GetContentRegionAvail().x,48.0*dpiScale)); + } kvsConfig(ins); break; case DIV_INS_OPZ: