From 8a2bf3ae3945211e4d8f5c0f281ff8282e01dbf6 Mon Sep 17 00:00:00 2001 From: BlastBrothers Date: Fri, 8 Apr 2022 00:01:42 -0400 Subject: [PATCH 001/342] Thin sample editor --- src/gui/sampleEdit.cpp | 194 ++++++++++++++++++++--------------------- 1 file changed, 97 insertions(+), 97 deletions(-) diff --git a/src/gui/sampleEdit.cpp b/src/gui/sampleEdit.cpp index b976ab92f..cd9f76b7e 100644 --- a/src/gui/sampleEdit.cpp +++ b/src/gui/sampleEdit.cpp @@ -53,7 +53,7 @@ void FurnaceGUI::drawSampleEdit() { MARK_MODIFIED; } - if (ImGui::BeginTable("SampleProps",4,ImGuiTableFlags_SizingStretchSame)) { + if (ImGui::BeginTable("SampleProps",2,ImGuiTableFlags_SizingStretchSame)) { ImGui::TableNextRow(); ImGui::TableNextColumn(); ImGui::Text("Type"); @@ -82,6 +82,7 @@ void FurnaceGUI::drawSampleEdit() { if (sample->rate>96000) sample->rate=96000; } + ImGui::TableNextRow(); ImGui::TableNextColumn(); ImGui::Text("C-4 (Hz)"); ImGui::SameLine(); @@ -142,7 +143,7 @@ void FurnaceGUI::drawSampleEdit() { ImGui::SetTooltip("Edit mode: Draw"); } ImGui::SameLine(); - ImGui::Dummy(ImVec2(4.0*dpiScale,dpiScale)); + ImGui::Dummy(ImVec2(7.0*dpiScale,dpiScale)); ImGui::SameLine(); ImGui::Button(ICON_FA_ARROWS_H "##SResize"); if (ImGui::IsItemClicked()) { @@ -234,23 +235,7 @@ void FurnaceGUI::drawSampleEdit() { resampleTarget=sample->rate; } ImGui::SameLine(); - ImGui::Dummy(ImVec2(4.0*dpiScale,dpiScale)); - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_UNDO "##SUndo")) { - doUndoSample(); - } - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("Undo"); - } - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_REPEAT "##SRedo")) { - doRedoSample(); - } - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("Redo"); - } - ImGui::SameLine(); - ImGui::Dummy(ImVec2(4.0*dpiScale,dpiScale)); + ImGui::Dummy(ImVec2(0.5*dpiScale,dpiScale)); ImGui::SameLine(); ImGui::Button(ICON_FA_VOLUME_UP "##SAmplify"); if (ImGui::IsItemHovered()) { @@ -321,80 +306,7 @@ void FurnaceGUI::drawSampleEdit() { ImGui::SetTooltip("Fade out"); } ImGui::SameLine(); - ImGui::Button(ICON_FA_ADJUST "##SInsertSilence"); - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("Insert silence"); - } - if (openSampleSilenceOpt) { - openSampleSilenceOpt=false; - ImGui::OpenPopup("SSilenceOpt"); - } - if (ImGui::BeginPopupContextItem("SSilenceOpt",ImGuiPopupFlags_MouseButtonLeft)) { - if (ImGui::InputInt("Samples",&silenceSize,1,64)) { - if (silenceSize<0) silenceSize=0; - if (silenceSize>16777215) silenceSize=16777215; - } - if (ImGui::Button("Resize")) { - int pos=(sampleSelStart==-1 || sampleSelStart==sampleSelEnd)?sample->samples:sampleSelStart; - sample->prepareUndo(true); - e->lockEngine([this,sample,pos]() { - if (!sample->insert(pos,silenceSize)) { - showError("couldn't insert! make sure your sample is 8 or 16-bit."); - } - e->renderSamples(); - }); - updateSampleTex=true; - sampleSelStart=pos; - sampleSelEnd=pos+silenceSize; - MARK_MODIFIED; - ImGui::CloseCurrentPopup(); - } - ImGui::EndPopup(); - } - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_ERASER "##SSilence")) { - doAction(GUI_ACTION_SAMPLE_SILENCE); - } - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("Apply silence"); - } - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_TIMES "##SDelete")) { - doAction(GUI_ACTION_SAMPLE_DELETE); - } - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("Delete"); - } - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_CROP "##STrim")) { - doAction(GUI_ACTION_SAMPLE_TRIM); - } - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("Trim"); - } - ImGui::SameLine(); - ImGui::Dummy(ImVec2(4.0*dpiScale,dpiScale)); - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_BACKWARD "##SReverse")) { - doAction(GUI_ACTION_SAMPLE_REVERSE); - } - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("Reverse"); - } - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_SORT_AMOUNT_ASC "##SInvert")) { - doAction(GUI_ACTION_SAMPLE_INVERT); - } - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("Invert"); - } - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_LEVEL_DOWN "##SSign")) { - doAction(GUI_ACTION_SAMPLE_SIGN); - } - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("Signed/unsigned exchange"); - } + ImGui::Dummy(ImVec2(0.5*dpiScale,dpiScale)); ImGui::SameLine(); ImGui::Button(ICON_FA_INDUSTRY "##SFilter"); if (ImGui::IsItemHovered()) { @@ -508,23 +420,111 @@ void FurnaceGUI::drawSampleEdit() { } ImGui::EndPopup(); } + + if (ImGui::Button(ICON_FA_UNDO "##SUndo")) { + doUndoSample(); + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Undo"); + } ImGui::SameLine(); - ImGui::Dummy(ImVec2(4.0*dpiScale,dpiScale)); + if (ImGui::Button(ICON_FA_REPEAT "##SRedo")) { + doRedoSample(); + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Redo"); + } ImGui::SameLine(); - if (ImGui::Button(ICON_FA_VOLUME_UP "##PreviewSample")) { + ImGui::Dummy(ImVec2(7.0*dpiScale,dpiScale)); + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_BACKWARD "##SReverse")) { + doAction(GUI_ACTION_SAMPLE_REVERSE); + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Reverse"); + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_SORT_AMOUNT_ASC "##SInvert")) { + doAction(GUI_ACTION_SAMPLE_INVERT); + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Invert"); + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_LEVEL_DOWN "##SSign")) { + doAction(GUI_ACTION_SAMPLE_SIGN); + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Signed/unsigned exchange"); + } + ImGui::SameLine(); + ImGui::Dummy(ImVec2(7.0*dpiScale,dpiScale)); + ImGui::SameLine(); + ImGui::Button(ICON_FA_ADJUST "##SInsertSilence"); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Insert silence"); + } + if (openSampleSilenceOpt) { + openSampleSilenceOpt=false; + ImGui::OpenPopup("SSilenceOpt"); + } + if (ImGui::BeginPopupContextItem("SSilenceOpt",ImGuiPopupFlags_MouseButtonLeft)) { + if (ImGui::InputInt("Samples",&silenceSize,1,64)) { + if (silenceSize<0) silenceSize=0; + if (silenceSize>16777215) silenceSize=16777215; + } + if (ImGui::Button("Resize")) { + int pos=(sampleSelStart==-1 || sampleSelStart==sampleSelEnd)?sample->samples:sampleSelStart; + sample->prepareUndo(true); + e->lockEngine([this,sample,pos]() { + if (!sample->insert(pos,silenceSize)) { + showError("couldn't insert! make sure your sample is 8 or 16-bit."); + } + e->renderSamples(); + }); + updateSampleTex=true; + sampleSelStart=pos; + sampleSelEnd=pos+silenceSize; + MARK_MODIFIED; + ImGui::CloseCurrentPopup(); + } + ImGui::EndPopup(); + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_ERASER "##SSilence")) { + doAction(GUI_ACTION_SAMPLE_SILENCE); + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Apply silence"); + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_TIMES "##SDelete")) { + doAction(GUI_ACTION_SAMPLE_DELETE); + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Delete"); + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_CROP "##STrim")) { + doAction(GUI_ACTION_SAMPLE_TRIM); + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Trim"); + } + + if (ImGui::Button(ICON_FA_PLAY "##PreviewSample")) { e->previewSample(curSample); } if (ImGui::IsItemHovered()) { ImGui::SetTooltip("Preview sample"); } ImGui::SameLine(); - if (ImGui::Button(ICON_FA_VOLUME_OFF "##StopSample")) { + if (ImGui::Button(ICON_FA_STOP "##StopSample")) { e->stopSamplePreview(); } if (ImGui::IsItemHovered()) { ImGui::SetTooltip("Stop sample preview"); } - ImGui::SameLine(); double zoomPercent=100.0/sampleZoom; ImGui::Text("Zoom"); From 1151db50912cb17491937e84459454fbb369576f Mon Sep 17 00:00:00 2001 From: BlastBrothers Date: Fri, 8 Apr 2022 10:23:51 -0400 Subject: [PATCH 002/342] Sample preview has maximum y height relative to x --- src/gui/sampleEdit.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/gui/sampleEdit.cpp b/src/gui/sampleEdit.cpp index cd9f76b7e..f4c1e8bbc 100644 --- a/src/gui/sampleEdit.cpp +++ b/src/gui/sampleEdit.cpp @@ -554,8 +554,14 @@ void FurnaceGUI::drawSampleEdit() { ImGui::Separator(); - ImVec2 avail=ImGui::GetContentRegionAvail(); + ImVec2 avail=ImGui::GetContentRegionAvail(); // graph size determined here + if (ImGui::GetContentRegionAvail().y > (ImGui::GetContentRegionAvail().x / 2.0f)) { + avail=ImVec2(ImGui::GetContentRegionAvail().x,ImGui::GetContentRegionAvail().x / 2.0f); + } avail.y-=ImGui::GetFontSize()+ImGui::GetStyle().ItemSpacing.y+ImGui::GetStyle().ScrollbarSize; + if (avail.y < 1.0){ //Prevents crash + avail.y = 1.0; + } int availX=avail.x; int availY=avail.y; From 052a0923fee89c60787d438ca74d63abf693396b Mon Sep 17 00:00:00 2001 From: BlastBrothers Date: Fri, 8 Apr 2022 10:48:42 -0400 Subject: [PATCH 003/342] Rearrange buttons --- src/gui/sampleEdit.cpp | 148 +++++++++++++++++++++-------------------- 1 file changed, 76 insertions(+), 72 deletions(-) diff --git a/src/gui/sampleEdit.cpp b/src/gui/sampleEdit.cpp index f4c1e8bbc..d83505dfc 100644 --- a/src/gui/sampleEdit.cpp +++ b/src/gui/sampleEdit.cpp @@ -237,77 +237,6 @@ void FurnaceGUI::drawSampleEdit() { ImGui::SameLine(); ImGui::Dummy(ImVec2(0.5*dpiScale,dpiScale)); ImGui::SameLine(); - ImGui::Button(ICON_FA_VOLUME_UP "##SAmplify"); - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("Amplify"); - } - if (openSampleAmplifyOpt) { - openSampleAmplifyOpt=false; - ImGui::OpenPopup("SAmplifyOpt"); - } - if (ImGui::BeginPopupContextItem("SAmplifyOpt",ImGuiPopupFlags_MouseButtonLeft)) { - ImGui::Text("Volume"); - if (ImGui::InputFloat("##SRVolume",&lifyVol,10.0,50.0,"%g%%")) { - if (amplifyVol<0) amplifyVol=0; - if (amplifyVol>10000) amplifyVol=10000; - } - ImGui::SameLine(); - ImGui::Text("(%.1fdB)",20.0*log10(amplifyVol/100.0f)); - if (ImGui::Button("Apply")) { - sample->prepareUndo(true); - e->lockEngine([this,sample]() { - SAMPLE_OP_BEGIN; - float vol=amplifyVol/100.0f; - - if (sample->depth==16) { - for (unsigned int i=start; idata16[i]*vol; - if (val<-32768) val=-32768; - if (val>32767) val=32767; - sample->data16[i]=val; - } - } else if (sample->depth==8) { - for (unsigned int i=start; idata8[i]*vol; - if (val<-128) val=-128; - if (val>127) val=127; - sample->data8[i]=val; - } - } - - updateSampleTex=true; - - e->renderSamples(); - }); - MARK_MODIFIED; - ImGui::CloseCurrentPopup(); - } - ImGui::EndPopup(); - } - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_ARROWS_V "##SNormalize")) { - doAction(GUI_ACTION_SAMPLE_NORMALIZE); - } - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("Normalize"); - } - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_ARROW_UP "##SFadeIn")) { - doAction(GUI_ACTION_SAMPLE_FADE_IN); - } - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("Fade in"); - } - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_ARROW_DOWN "##SFadeOut")) { - doAction(GUI_ACTION_SAMPLE_FADE_OUT); - } - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("Fade out"); - } - ImGui::SameLine(); - ImGui::Dummy(ImVec2(0.5*dpiScale,dpiScale)); - ImGui::SameLine(); ImGui::Button(ICON_FA_INDUSTRY "##SFilter"); if (ImGui::IsItemHovered()) { ImGui::SetTooltip("Apply filter"); @@ -420,6 +349,79 @@ void FurnaceGUI::drawSampleEdit() { } ImGui::EndPopup(); } + ImGui::SameLine(); + ImGui::Dummy(ImVec2(0.5*dpiScale,dpiScale)); + ImGui::SameLine(); + + + ImGui::Button(ICON_FA_VOLUME_UP "##SAmplify"); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Amplify"); + } + if (openSampleAmplifyOpt) { + openSampleAmplifyOpt=false; + ImGui::OpenPopup("SAmplifyOpt"); + } + if (ImGui::BeginPopupContextItem("SAmplifyOpt",ImGuiPopupFlags_MouseButtonLeft)) { + ImGui::Text("Volume"); + if (ImGui::InputFloat("##SRVolume",&lifyVol,10.0,50.0,"%g%%")) { + if (amplifyVol<0) amplifyVol=0; + if (amplifyVol>10000) amplifyVol=10000; + } + ImGui::SameLine(); + ImGui::Text("(%.1fdB)",20.0*log10(amplifyVol/100.0f)); + if (ImGui::Button("Apply")) { + sample->prepareUndo(true); + e->lockEngine([this,sample]() { + SAMPLE_OP_BEGIN; + float vol=amplifyVol/100.0f; + + if (sample->depth==16) { + for (unsigned int i=start; idata16[i]*vol; + if (val<-32768) val=-32768; + if (val>32767) val=32767; + sample->data16[i]=val; + } + } else if (sample->depth==8) { + for (unsigned int i=start; idata8[i]*vol; + if (val<-128) val=-128; + if (val>127) val=127; + sample->data8[i]=val; + } + } + + updateSampleTex=true; + + e->renderSamples(); + }); + MARK_MODIFIED; + ImGui::CloseCurrentPopup(); + } + ImGui::EndPopup(); + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_ARROWS_V "##SNormalize")) { + doAction(GUI_ACTION_SAMPLE_NORMALIZE); + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Normalize"); + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_ARROW_UP "##SFadeIn")) { + doAction(GUI_ACTION_SAMPLE_FADE_IN); + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Fade in"); + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_ARROW_DOWN "##SFadeOut")) { + doAction(GUI_ACTION_SAMPLE_FADE_OUT); + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Fade out"); + } if (ImGui::Button(ICON_FA_UNDO "##SUndo")) { doUndoSample(); @@ -451,6 +453,8 @@ void FurnaceGUI::drawSampleEdit() { ImGui::SetTooltip("Invert"); } ImGui::SameLine(); + ImGui::Dummy(ImVec2(0.5*dpiScale,dpiScale)); + ImGui::SameLine(); if (ImGui::Button(ICON_FA_LEVEL_DOWN "##SSign")) { doAction(GUI_ACTION_SAMPLE_SIGN); } @@ -458,7 +462,7 @@ void FurnaceGUI::drawSampleEdit() { ImGui::SetTooltip("Signed/unsigned exchange"); } ImGui::SameLine(); - ImGui::Dummy(ImVec2(7.0*dpiScale,dpiScale)); + ImGui::Dummy(ImVec2(0.5*dpiScale,dpiScale)); ImGui::SameLine(); ImGui::Button(ICON_FA_ADJUST "##SInsertSilence"); if (ImGui::IsItemHovered()) { From 91d8c48542cbbd9b39856103b3ff581c8e9d5f0b Mon Sep 17 00:00:00 2001 From: OPNA2608 Date: Sun, 10 Apr 2022 14:16:33 +0200 Subject: [PATCH 004/342] Add FF bank import --- src/engine/engine.h | 1 + src/engine/fileOpsIns.cpp | 81 ++++++++++++++++++++++++++++++++++++++- src/gui/gui.cpp | 4 +- src/gui/settings.cpp | 1 + 4 files changed, 84 insertions(+), 3 deletions(-) diff --git a/src/engine/engine.h b/src/engine/engine.h index 68ec0ce5b..3f4f59b37 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -284,6 +284,7 @@ class DivEngine { void loadS3I(SafeReader& reader, std::vector& ret, String& stripPath); void loadSBI(SafeReader& reader, std::vector& ret, String& stripPath); void loadOPM(SafeReader& reader, std::vector& ret, String& stripPath); + void loadFF(SafeReader& reader, std::vector& ret, String& stripPath); bool initAudioBackend(); bool deinitAudioBackend(); diff --git a/src/engine/fileOpsIns.cpp b/src/engine/fileOpsIns.cpp index 6689c5547..49b40aadf 100644 --- a/src/engine/fileOpsIns.cpp +++ b/src/engine/fileOpsIns.cpp @@ -30,7 +30,8 @@ enum DivInsFormats { DIV_INSFORMAT_BTI, DIV_INSFORMAT_S3I, DIV_INSFORMAT_SBI, - DIV_INSFORMAT_OPM + DIV_INSFORMAT_OPM, + DIV_INSFORMAT_FF, }; void DivEngine::loadDMP(SafeReader& reader, std::vector& ret, String& stripPath) { @@ -631,6 +632,79 @@ void DivEngine::loadSBI(SafeReader& reader, std::vector& ret, St ret.push_back(ins); } +void DivEngine::loadFF(SafeReader& reader, std::vector& ret, String& stripPath) { + DivInstrument* insList[256]; + memset(insList,0,256*sizeof(void*)); + int readCount = 0; + size_t insCount = reader.size(); + insCount = (insCount >> 5) + (((insCount % 0x20) > 0) ? 1 : 0); + if (insCount > 256) insCount = 256; + uint8_t buf; + try { + reader.seek(0, SEEK_SET); + for (unsigned int i = 0; i < insCount; ++i) { + insList[i] = new DivInstrument; + DivInstrument* ins = insList[i]; + + ins->type = DIV_INS_FM; + DivInstrumentFM::Operator op; + + for (unsigned int j = 0; j < 4; j++) { + buf = reader.readC(); + ins->fm.op[j].mult = buf & 0xf; + // detune needs extra translation from register to furnace format + const int dtNative = (buf >> 4) & 0x7; + ins->fm.op[j].dt = (dtNative >= 4) ? (7 - dtNative) : (dtNative + 3); + ins->fm.op[j].ssgEnv = (buf >> 4) & 0x8; + } + for (unsigned int j = 0; j < 4; j++) { + buf = reader.readC(); + ins->fm.op[j].tl = buf & 0x7f; + ins->fm.op[j].ssgEnv |= (buf >> 5) & 0x4; + } + for (unsigned int j = 0; j < 4; j++) { + buf = reader.readC(); + ins->fm.op[j].ar = buf & 0x1f; + ins->fm.op[j].rs = buf >> 6; + } + for (unsigned int j = 0; j < 4; j++) { + buf = reader.readC(); + ins->fm.op[j].dr = buf & 0x1f; + ins->fm.op[j].ssgEnv |= (buf >> 5) & 0x3; + ins->fm.op[j].am = buf >> 7; + } + for (unsigned int j = 0; j < 4; j++) { + buf = reader.readC(); + ins->fm.op[j].d2r = buf & 0x1f; + } + for (unsigned int j = 0; j < 4; j++) { + buf = reader.readC(); + ins->fm.op[j].rr = buf & 0xf; + ins->fm.op[j].sl = buf >> 4; + } + + buf = reader.readC(); + ins->fm.alg = buf & 0x7; + ins->fm.fb = (buf >> 3) & 0x7; + + // FIXME This is encoded in Shift-JIS + ins->name = reader.readString(7); + ++readCount; + } + } catch (EndOfFileException& e) { + lastError = "premature end of file"; + logE("premature end of file!\n"); + for (int i = readCount; i >= 0; --i) { + delete insList[i]; + } + return; + } + + for (unsigned int i = 0; i < insCount; ++i) { + ret.push_back(insList[i]); + } +} + void DivEngine::loadOPM(SafeReader& reader, std::vector& ret, String& stripPath) { DivInstrument* ins[128]; memset(ins,0,128*sizeof(void*)); @@ -771,6 +845,8 @@ std::vector DivEngine::instrumentFromFile(const char* path) { format=DIV_INSFORMAT_SBI; } else if (extS==String(".opm")) { format=DIV_INSFORMAT_OPM; + } else if (extS==String(".ff")) { + format=DIV_INSFORMAT_FF; } } @@ -797,6 +873,9 @@ std::vector DivEngine::instrumentFromFile(const char* path) { case DIV_INSFORMAT_SBI: loadSBI(reader,ret,stripPath); break; + case DIV_INSFORMAT_FF: + loadFF(reader,ret,stripPath); + break; } if (reader.tell()openLoad( "Load Instrument", - {"compatible files", "*.fui *.dmp *.tfi *.vgi *.s3i *.sbi", + {"compatible files", "*.fui *.dmp *.tfi *.vgi *.s3i *.sbi *.ff", "all files", ".*"}, - "compatible files{.fui,.dmp,.tfi,.vgi,.s3i,.sbi},.*", + "compatible files{.fui,.dmp,.tfi,.vgi,.s3i,.sbi,.ff},.*", workingDirIns, dpiScale ); diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index 316e3ef51..91627863f 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -2214,6 +2214,7 @@ void FurnaceGUI::applyUISettings() { ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".sbi",uiColors[GUI_COLOR_FILE_INSTR],ICON_FA_FILE); ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".fti",uiColors[GUI_COLOR_FILE_INSTR],ICON_FA_FILE); ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".bti",uiColors[GUI_COLOR_FILE_INSTR],ICON_FA_FILE); + ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".ff",uiColors[GUI_COLOR_FILE_INSTR],ICON_FA_FILE); if (fileDialog!=NULL) delete fileDialog; fileDialog=new FurnaceGUIFileDialog(settings.sysFileDialog); From a454afcd19568b251a3e320d32b39ce629a7bfea Mon Sep 17 00:00:00 2001 From: cam900 Date: Mon, 11 Apr 2022 21:08:58 +0900 Subject: [PATCH 005/342] Fix possible instrument loading fix --- src/engine/instrument.cpp | 48 +++++++++++++++++++++++++++++---------- 1 file changed, 36 insertions(+), 12 deletions(-) diff --git a/src/engine/instrument.cpp b/src/engine/instrument.cpp index 68896a45e..ee2e08dd4 100644 --- a/src/engine/instrument.cpp +++ b/src/engine/instrument.cpp @@ -698,18 +698,42 @@ DivDataErrors DivInstrument::readInsData(SafeReader& reader, short version) { for (int i=0; i<4; i++) { DivInstrumentSTD::OpMacro& op=std.opMacros[i]; - reader.read(op.amMacro.val,op.amMacro.len); - reader.read(op.arMacro.val,op.arMacro.len); - reader.read(op.drMacro.val,op.drMacro.len); - reader.read(op.multMacro.val,op.multMacro.len); - reader.read(op.rrMacro.val,op.rrMacro.len); - reader.read(op.slMacro.val,op.slMacro.len); - reader.read(op.tlMacro.val,op.tlMacro.len); - reader.read(op.dt2Macro.val,op.dt2Macro.len); - reader.read(op.rsMacro.val,op.rsMacro.len); - reader.read(op.dtMacro.val,op.dtMacro.len); - reader.read(op.d2rMacro.val,op.d2rMacro.len); - reader.read(op.ssgMacro.val,op.ssgMacro.len); + for (int j=0; j Date: Mon, 11 Apr 2022 21:15:52 +0900 Subject: [PATCH 006/342] Fix possibly breaks instrument loading --- src/engine/instrument.cpp | 134 ++++++++++++++++++++++++++++++++++---- 1 file changed, 122 insertions(+), 12 deletions(-) diff --git a/src/engine/instrument.cpp b/src/engine/instrument.cpp index ee2e08dd4..3501b34a6 100644 --- a/src/engine/instrument.cpp +++ b/src/engine/instrument.cpp @@ -233,43 +233,44 @@ void DivInstrument::putInsData(SafeWriter* w) { w->writeC(op.ssgMacro.open); } + // FM macro low 8 bits for (int i=0; i<4; i++) { DivInstrumentSTD::OpMacro& op=std.opMacros[i]; for (int j=0; jwriteC(op.amMacro.val[j]); + w->writeC(op.amMacro.val[j]&0xff); } for (int j=0; jwriteC(op.arMacro.val[j]); + w->writeC(op.arMacro.val[j]&0xff); } for (int j=0; jwriteC(op.drMacro.val[j]); + w->writeC(op.drMacro.val[j]&0xff); } for (int j=0; jwriteC(op.multMacro.val[j]); + w->writeC(op.multMacro.val[j]&0xff); } for (int j=0; jwriteC(op.rrMacro.val[j]); + w->writeC(op.rrMacro.val[j]&0xff); } for (int j=0; jwriteC(op.slMacro.val[j]); + w->writeC(op.slMacro.val[j]&0xff); } for (int j=0; jwriteC(op.tlMacro.val[j]); + w->writeC(op.tlMacro.val[j]&0xff); } for (int j=0; jwriteC(op.dt2Macro.val[j]); + w->writeC(op.dt2Macro.val[j]&0xff); } for (int j=0; jwriteC(op.rsMacro.val[j]); + w->writeC(op.rsMacro.val[j]&0xff); } for (int j=0; jwriteC(op.dtMacro.val[j]); + w->writeC(op.dtMacro.val[j]&0xff); } for (int j=0; jwriteC(op.d2rMacro.val[j]); + w->writeC(op.d2rMacro.val[j]&0xff); } for (int j=0; jwriteC(op.ssgMacro.val[j]); + w->writeC(op.ssgMacro.val[j]&0xff); } } @@ -480,6 +481,59 @@ void DivInstrument::putInsData(SafeWriter* w) { w->writeC(ws.param2); w->writeC(ws.param3); w->writeC(ws.param4); + + // FM macro high 24 bits + for (int i=0; i<4; i++) { + DivInstrumentSTD::OpMacro& op=std.opMacros[i]; + for (int j=0; jwriteC((op.amMacro.val[j]>>8)&0xff); + w->writeS((op.amMacro.val[j]>>16)&0xffff); + } + for (int j=0; jwriteC((op.arMacro.val[j]>>8)&0xff); + w->writeS((op.arMacro.val[j]>>16)&0xffff); + } + for (int j=0; jwriteC((op.drMacro.val[j]>>8)&0xff); + w->writeS((op.drMacro.val[j]>>16)&0xffff); + } + for (int j=0; jwriteC((op.multMacro.val[j]>>8)&0xff); + w->writeS((op.multMacro.val[j]>>16)&0xffff); + } + for (int j=0; jwriteC((op.rrMacro.val[j]>>8)&0xff); + w->writeS((op.rrMacro.val[j]>>16)&0xffff); + } + for (int j=0; jwriteC((op.slMacro.val[j]>>8)&0xff); + w->writeS((op.slMacro.val[j]>>16)&0xffff); + } + for (int j=0; jwriteC((op.tlMacro.val[j]>>8)&0xff); + w->writeS((op.tlMacro.val[j]>>16)&0xffff); + } + for (int j=0; jwriteC((op.dt2Macro.val[j]>>8)&0xff); + w->writeS((op.dt2Macro.val[j]>>16)&0xffff); + } + for (int j=0; jwriteC((op.rsMacro.val[j]>>8)&0xff); + w->writeS((op.rsMacro.val[j]>>16)&0xffff); + } + for (int j=0; jwriteC((op.dtMacro.val[j]>>8)&0xff); + w->writeS((op.dtMacro.val[j]>>16)&0xffff); + } + for (int j=0; jwriteC((op.d2rMacro.val[j]>>8)&0xff); + w->writeS((op.d2rMacro.val[j]>>16)&0xffff); + } + for (int j=0; jwriteC((op.ssgMacro.val[j]>>8)&0xff); + w->writeS((op.ssgMacro.val[j]>>16)&0xffff); + } + } } DivDataErrors DivInstrument::readInsData(SafeReader& reader, short version) { @@ -696,6 +750,7 @@ DivDataErrors DivInstrument::readInsData(SafeReader& reader, short version) { op.ssgMacro.open=reader.readC(); } + // FM macro low 8 bits for (int i=0; i<4; i++) { DivInstrumentSTD::OpMacro& op=std.opMacros[i]; for (int j=0; j=81) { + for (int i=0; i<4; i++) { + DivInstrumentSTD::OpMacro& op=std.opMacros[i]; + for (int j=0; j Date: Mon, 11 Apr 2022 12:15:11 -0400 Subject: [PATCH 007/342] Pass sus to drawFMEnv- but is this the right var? --- src/gui/gui.h | 2 +- src/gui/insEdit.cpp | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/gui/gui.h b/src/gui/gui.h index 7cc66c847..b3bf1af12 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -1016,7 +1016,7 @@ class FurnaceGUI { void drawSSGEnv(unsigned char type, const ImVec2& size); void drawWaveform(unsigned char type, bool opz, const ImVec2& size); void drawAlgorithm(unsigned char alg, FurnaceGUIFMAlgs algType, const ImVec2& size); - void drawFMEnv(unsigned char tl, unsigned char ar, unsigned char dr, unsigned char d2r, unsigned char rr, unsigned char sl, float maxTl, float maxArDr, const ImVec2& size); + void drawFMEnv(unsigned char tl, unsigned char ar, unsigned char dr, unsigned char d2r, unsigned char rr, unsigned char sl, unsigned char sus, float maxTl, float maxArDr, const ImVec2& size); void drawSysConf(int i); // these ones offer ctrl-wheel fine value changes. diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index 3e1416128..05df40ad6 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -901,7 +901,7 @@ void FurnaceGUI::drawAlgorithm(unsigned char alg, FurnaceGUIFMAlgs algType, cons } } -void FurnaceGUI::drawFMEnv(unsigned char tl, unsigned char ar, unsigned char dr, unsigned char d2r, unsigned char rr, unsigned char sl, float maxTl, float maxArDr, const ImVec2& size) { +void FurnaceGUI::drawFMEnv(unsigned char tl, unsigned char ar, unsigned char dr, unsigned char d2r, unsigned char rr, unsigned char sl, unsigned char sus, float maxTl, float maxArDr, const ImVec2& size) { ImDrawList* dl=ImGui::GetWindowDrawList(); ImGuiWindow* window=ImGui::GetCurrentWindow(); @@ -1806,7 +1806,7 @@ void FurnaceGUI::drawInsEdit() { } ImGui::TableNextColumn(); - drawFMEnv(op.tl&maxTl,op.ar&maxArDr,op.dr&maxArDr,(ins->type==DIV_INS_OPL || ins->type==DIV_INS_OPLL)?((op.rr&15)*2):op.d2r&31,op.rr&15,op.sl&15,maxTl,maxArDr,ImVec2(ImGui::GetContentRegionAvail().x,sliderHeight)); + drawFMEnv(op.tl&maxTl,op.ar&maxArDr,op.dr&maxArDr,(ins->type==DIV_INS_OPL || ins->type==DIV_INS_OPLL)?((op.rr&15)*2):op.d2r&31,op.rr&15,op.sl&15,ins->fm.op->sus,maxTl,maxArDr,ImVec2(ImGui::GetContentRegionAvail().x,sliderHeight)); ImGui::PopID(); } @@ -1889,7 +1889,7 @@ void FurnaceGUI::drawInsEdit() { } //52.0 controls vert scaling; default 96 - drawFMEnv(op.tl&maxTl,op.ar&maxArDr,op.dr&maxArDr,(ins->type==DIV_INS_OPL || ins->type==DIV_INS_OPLL)?((op.rr&15)*2):op.d2r&31,op.rr&15,op.sl&15,maxTl,maxArDr,ImVec2(ImGui::GetContentRegionAvail().x,52.0*dpiScale)); + drawFMEnv(op.tl&maxTl,op.ar&maxArDr,op.dr&maxArDr,(ins->type==DIV_INS_OPL || ins->type==DIV_INS_OPLL)?((op.rr&15)*2):op.d2r&31,op.rr&15,op.sl&15,ins->fm.op->sus,maxTl,maxArDr,ImVec2(ImGui::GetContentRegionAvail().x,52.0*dpiScale)); //P(CWSliderScalar(FM_NAME(FM_AR),ImGuiDataType_U8,&op.ar,&_ZERO,&_THIRTY_ONE)); rightClickable if (ImGui::BeginTable("opParams",2,ImGuiTableFlags_SizingStretchProp)) { ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthStretch,0.0); \ From 1abfa0644be06efc466b764e65f35ae6ee47bd71 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 11 Apr 2022 12:49:43 -0500 Subject: [PATCH 008/342] oops --- src/log.cpp | 62 ----------------------------------------------------- 1 file changed, 62 deletions(-) diff --git a/src/log.cpp b/src/log.cpp index 7f7fae8a9..a90b7f1e0 100644 --- a/src/log.cpp +++ b/src/log.cpp @@ -27,68 +27,6 @@ LogEntry logEntries[TA_LOG_SIZE]; static constexpr unsigned int TA_LOG_MASK=TA_LOG_SIZE-1; -int logV(const char* format, ...) { - va_list va; - int ret; - if (logLevel Date: Mon, 11 Apr 2022 13:55:20 -0400 Subject: [PATCH 009/342] Works for OPLx --- src/gui/insEdit.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index 05df40ad6..bb699522e 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -951,7 +951,7 @@ void FurnaceGUI::drawFMEnv(unsigned char tl, unsigned char ar, unsigned char dr, //addAALine(dl,pos3,posSLineVEnd,colorS); //draw vert. line through sustain level addAALine(dl,pos1,pos2,color); //A addAALine(dl,pos2,posDecayRate0Pt,color); //Line from A to end of graph - } else if (d2r==0.0) { //if D2R = 0, the envelope stays at the sustain level forever + } else if (d2r==0.0 || sus==1.0) { //if D2R = 0, the envelope stays at the sustain level forever dl->AddTriangleFilled(posRStart,posREnd,pos1,colorS); //draw release as shaded triangle behind everything addAALine(dl,pos3,posSLineHEnd,colorS); //draw horiz line through sustain level addAALine(dl,pos3,posSLineVEnd,colorS); //draw vert. line through sustain level @@ -1806,7 +1806,7 @@ void FurnaceGUI::drawInsEdit() { } ImGui::TableNextColumn(); - drawFMEnv(op.tl&maxTl,op.ar&maxArDr,op.dr&maxArDr,(ins->type==DIV_INS_OPL || ins->type==DIV_INS_OPLL)?((op.rr&15)*2):op.d2r&31,op.rr&15,op.sl&15,ins->fm.op->sus,maxTl,maxArDr,ImVec2(ImGui::GetContentRegionAvail().x,sliderHeight)); + drawFMEnv(op.tl&maxTl,op.ar&maxArDr,op.dr&maxArDr,(ins->type==DIV_INS_OPL || ins->type==DIV_INS_OPLL)?((op.rr&15)*2):op.d2r&31,op.rr&15,op.sl&15,op.sus,maxTl,maxArDr,ImVec2(ImGui::GetContentRegionAvail().x,sliderHeight)); ImGui::PopID(); } @@ -1889,7 +1889,7 @@ void FurnaceGUI::drawInsEdit() { } //52.0 controls vert scaling; default 96 - drawFMEnv(op.tl&maxTl,op.ar&maxArDr,op.dr&maxArDr,(ins->type==DIV_INS_OPL || ins->type==DIV_INS_OPLL)?((op.rr&15)*2):op.d2r&31,op.rr&15,op.sl&15,ins->fm.op->sus,maxTl,maxArDr,ImVec2(ImGui::GetContentRegionAvail().x,52.0*dpiScale)); + drawFMEnv(op.tl&maxTl,op.ar&maxArDr,op.dr&maxArDr,(ins->type==DIV_INS_OPL || ins->type==DIV_INS_OPLL)?((op.rr&15)*2):op.d2r&31,op.rr&15,op.sl&15,op.sus,maxTl,maxArDr,ImVec2(ImGui::GetContentRegionAvail().x,52.0*dpiScale)); //P(CWSliderScalar(FM_NAME(FM_AR),ImGuiDataType_U8,&op.ar,&_ZERO,&_THIRTY_ONE)); rightClickable if (ImGui::BeginTable("opParams",2,ImGuiTableFlags_SizingStretchProp)) { ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthStretch,0.0); \ From 1adc7753be533be5ef9bd1de52f90cf9c338a220 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 11 Apr 2022 13:08:12 -0500 Subject: [PATCH 010/342] second attempt --- src/log.cpp | 2 +- src/ta-log.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/log.cpp b/src/log.cpp index a90b7f1e0..89602b422 100644 --- a/src/log.cpp +++ b/src/log.cpp @@ -27,7 +27,7 @@ LogEntry logEntries[TA_LOG_SIZE]; static constexpr unsigned int TA_LOG_MASK=TA_LOG_SIZE-1; -int writeLog(int level, const char* msg, fmt::printf_args& args) { +int writeLog(int level, const char* msg, fmt::printf_args args) { time_t thisMakesNoSense=time(NULL); int pos=logPosition; logPosition=(logPosition+1)&TA_LOG_MASK; diff --git a/src/ta-log.h b/src/ta-log.h index dd26dd94f..6a61fa321 100644 --- a/src/ta-log.h +++ b/src/ta-log.h @@ -49,7 +49,7 @@ struct LogEntry { ready(false) {} }; -int writeLog(int level, const char* msg, fmt::printf_args& args); +int writeLog(int level, const char* msg, fmt::printf_args args); extern LogEntry logEntries[TA_LOG_SIZE]; From b5472fb52e05089dfe4f9de4497f449aefc9dd2a Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 11 Apr 2022 13:36:02 -0500 Subject: [PATCH 011/342] third attempt --- src/ta-log.h | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/src/ta-log.h b/src/ta-log.h index 6a61fa321..27580e5e7 100644 --- a/src/ta-log.h +++ b/src/ta-log.h @@ -54,28 +54,23 @@ int writeLog(int level, const char* msg, fmt::printf_args args); extern LogEntry logEntries[TA_LOG_SIZE]; template int logV(const char* msg, const T&... args) { - fmt::printf_args a=fmt::make_printf_args(args...); - return writeLog(LOGLEVEL_TRACE,msg,a); + return writeLog(LOGLEVEL_TRACE,msg,fmt::make_printf_args(args...)); } template int logD(const char* msg, const T&... args) { - fmt::printf_args a=fmt::make_printf_args(args...); - return writeLog(LOGLEVEL_DEBUG,msg,a); + return writeLog(LOGLEVEL_DEBUG,msg,fmt::make_printf_args(args...)); } template int logI(const char* msg, const T&... args) { - fmt::printf_args a=fmt::make_printf_args(args...); - return writeLog(LOGLEVEL_INFO,msg,a); + return writeLog(LOGLEVEL_INFO,msg,fmt::make_printf_args(args...)); } template int logW(const char* msg, const T&... args) { - fmt::printf_args a=fmt::make_printf_args(args...); - return writeLog(LOGLEVEL_WARN,msg,a); + return writeLog(LOGLEVEL_WARN,msg,fmt::make_printf_args(args...)); } template int logE(const char* msg, const T&... args) { - fmt::printf_args a=fmt::make_printf_args(args...); - return writeLog(LOGLEVEL_ERROR,msg,a); + return writeLog(LOGLEVEL_ERROR,msg,fmt::make_printf_args(args...)); } void initLog(); From 83baae92d9a9ba4371aa9a6a8ad0a777aff60562 Mon Sep 17 00:00:00 2001 From: BlastBrothers Date: Mon, 11 Apr 2022 17:26:40 -0400 Subject: [PATCH 012/342] Now it works ofr OPLL too, plus global sus --- src/gui/gui.h | 2 +- src/gui/insEdit.cpp | 12 ++++++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/gui/gui.h b/src/gui/gui.h index b3bf1af12..631d52a6d 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -1016,7 +1016,7 @@ class FurnaceGUI { void drawSSGEnv(unsigned char type, const ImVec2& size); void drawWaveform(unsigned char type, bool opz, const ImVec2& size); void drawAlgorithm(unsigned char alg, FurnaceGUIFMAlgs algType, const ImVec2& size); - void drawFMEnv(unsigned char tl, unsigned char ar, unsigned char dr, unsigned char d2r, unsigned char rr, unsigned char sl, unsigned char sus, float maxTl, float maxArDr, const ImVec2& size); + void drawFMEnv(unsigned char tl, unsigned char ar, unsigned char dr, unsigned char d2r, unsigned char rr, unsigned char sl, unsigned char sus, unsigned char egt, unsigned char algOrGlobalSus, float maxTl, float maxArDr, const ImVec2& size, unsigned short instType); void drawSysConf(int i); // these ones offer ctrl-wheel fine value changes. diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index bb699522e..73ae54c46 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -901,7 +901,7 @@ void FurnaceGUI::drawAlgorithm(unsigned char alg, FurnaceGUIFMAlgs algType, cons } } -void FurnaceGUI::drawFMEnv(unsigned char tl, unsigned char ar, unsigned char dr, unsigned char d2r, unsigned char rr, unsigned char sl, unsigned char sus, float maxTl, float maxArDr, const ImVec2& size) { +void FurnaceGUI::drawFMEnv(unsigned char tl, unsigned char ar, unsigned char dr, unsigned char d2r, unsigned char rr, unsigned char sl, unsigned char sus, unsigned char egt, unsigned char algOrGlobalSus, float maxTl, float maxArDr, const ImVec2& size, unsigned short instType) { ImDrawList* dl=ImGui::GetWindowDrawList(); ImGuiWindow* window=ImGui::GetCurrentWindow(); @@ -918,6 +918,10 @@ void FurnaceGUI::drawFMEnv(unsigned char tl, unsigned char ar, unsigned char dr, if (ImGui::ItemAdd(rect,ImGui::GetID("alg"))) { ImGui::RenderFrame(rect.Min,rect.Max,ImGui::GetColorU32(ImGuiCol_FrameBg),true,style.FrameRounding); + //Adjust for OPLL global sustain setting + if (instType==DIV_INS_OPLL && algOrGlobalSus==1.0){ + rr = 5.0; + } //calculate x positions float arPos=float(maxArDr-ar)/maxArDr; //peak of AR, start of DR float drPos=arPos+((sl/15.0)*(float(maxArDr-dr)/maxArDr)); //end of DR, start of D2R @@ -951,7 +955,7 @@ void FurnaceGUI::drawFMEnv(unsigned char tl, unsigned char ar, unsigned char dr, //addAALine(dl,pos3,posSLineVEnd,colorS); //draw vert. line through sustain level addAALine(dl,pos1,pos2,color); //A addAALine(dl,pos2,posDecayRate0Pt,color); //Line from A to end of graph - } else if (d2r==0.0 || sus==1.0) { //if D2R = 0, the envelope stays at the sustain level forever + } else if (d2r==0.0 || (instType==DIV_INS_OPL && sus==1.0) || (instType==DIV_INS_OPLL && egt!=0.0)) { //envelope stays at the sustain level forever dl->AddTriangleFilled(posRStart,posREnd,pos1,colorS); //draw release as shaded triangle behind everything addAALine(dl,pos3,posSLineHEnd,colorS); //draw horiz line through sustain level addAALine(dl,pos3,posSLineVEnd,colorS); //draw vert. line through sustain level @@ -1806,7 +1810,7 @@ void FurnaceGUI::drawInsEdit() { } ImGui::TableNextColumn(); - drawFMEnv(op.tl&maxTl,op.ar&maxArDr,op.dr&maxArDr,(ins->type==DIV_INS_OPL || ins->type==DIV_INS_OPLL)?((op.rr&15)*2):op.d2r&31,op.rr&15,op.sl&15,op.sus,maxTl,maxArDr,ImVec2(ImGui::GetContentRegionAvail().x,sliderHeight)); + drawFMEnv(op.tl&maxTl,op.ar&maxArDr,op.dr&maxArDr,(ins->type==DIV_INS_OPL || ins->type==DIV_INS_OPLL)?((op.rr&15)*2):op.d2r&31,op.rr&15,op.sl&15,op.sus,op.ssgEnv&8,ins->fm.alg,maxTl,maxArDr,ImVec2(ImGui::GetContentRegionAvail().x,sliderHeight),ins->type); ImGui::PopID(); } @@ -1889,7 +1893,7 @@ void FurnaceGUI::drawInsEdit() { } //52.0 controls vert scaling; default 96 - drawFMEnv(op.tl&maxTl,op.ar&maxArDr,op.dr&maxArDr,(ins->type==DIV_INS_OPL || ins->type==DIV_INS_OPLL)?((op.rr&15)*2):op.d2r&31,op.rr&15,op.sl&15,op.sus,maxTl,maxArDr,ImVec2(ImGui::GetContentRegionAvail().x,52.0*dpiScale)); + drawFMEnv(op.tl&maxTl,op.ar&maxArDr,op.dr&maxArDr,(ins->type==DIV_INS_OPL || ins->type==DIV_INS_OPLL)?((op.rr&15)*2):op.d2r&31,op.rr&15,op.sl&15,op.sus,op.ssgEnv&8,ins->fm.alg,maxTl,maxArDr,ImVec2(ImGui::GetContentRegionAvail().x,52.0*dpiScale),ins->type); //P(CWSliderScalar(FM_NAME(FM_AR),ImGuiDataType_U8,&op.ar,&_ZERO,&_THIRTY_ONE)); rightClickable if (ImGui::BeginTable("opParams",2,ImGuiTableFlags_SizingStretchProp)) { ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthStretch,0.0); \ From 8407a7d3a4c3a02b33ee9d2259387d69578d3e5c Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 11 Apr 2022 18:12:05 -0500 Subject: [PATCH 013/342] add several TODO comments self-note: check this one after you come back! --- src/engine/engine.cpp | 1 + src/engine/waveSynth.cpp | 4 ++++ src/gui/editControls.cpp | 3 +++ src/gui/gui.h | 3 +++ src/gui/insEdit.cpp | 2 ++ src/gui/osc.cpp | 7 +++++++ src/gui/pattern.cpp | 1 + src/gui/piano.cpp | 1 + src/gui/sampleEdit.cpp | 6 ++++++ src/gui/settings.cpp | 4 ++++ 10 files changed, 32 insertions(+) diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 14577b40e..1c5d4dd2e 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -414,6 +414,7 @@ void DivEngine::runExportThread() { } } +// TODO: properly export ExtCh songs in per-channel mode bool DivEngine::saveAudio(const char* path, int loops, DivAudioExportModes mode) { exportPath=path; exportMode=mode; diff --git a/src/engine/waveSynth.cpp b/src/engine/waveSynth.cpp index 4d776ba4c..e38ee228e 100644 --- a/src/engine/waveSynth.cpp +++ b/src/engine/waveSynth.cpp @@ -2,6 +2,10 @@ #include "engine.h" #include "instrument.h" +// TODO: +// - add missing license header +// - implement waveSynth on Amiga and N163 + bool DivWaveSynth::activeChanged() { if (activeChangedB) { activeChangedB=false; diff --git a/src/gui/editControls.cpp b/src/gui/editControls.cpp index 5e8d72574..8d9480242 100644 --- a/src/gui/editControls.cpp +++ b/src/gui/editControls.cpp @@ -20,6 +20,9 @@ #include "gui.h" #include "IconsFontAwesome4.h" +// TODO: +// - "play from start" button/action +// - fix behavior of play buttons void FurnaceGUI::drawEditControls() { if (nextWindow==GUI_WINDOW_EDIT_CONTROLS) { editControlsOpen=true; diff --git a/src/gui/gui.h b/src/gui/gui.h index 7cc66c847..53b2f1715 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -46,6 +46,9 @@ #define BIND_FOR(x) getKeyName(actionKeys[x],true).c_str() +// TODO: +// - add colors for FM envelope and waveform +// - maybe add "alternate" color for FM modulators/carriers (a bit difficult) enum FurnaceGUIColors { GUI_COLOR_BACKGROUND=0, GUI_COLOR_FRAME_BACKGROUND, diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index 3e1416128..f53db474c 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -1253,6 +1253,8 @@ if (ImGui::BeginTable("MacroSpace",2)) { \ #define CENTER_VSLIDER \ ImGui::SetCursorPosX(ImGui::GetCursorPosX()+0.5f*ImGui::GetContentRegionAvail().x-10.0f*dpiScale); +// TODO: +// - add right click line draw in macro editor void FurnaceGUI::drawInsEdit() { if (nextWindow==GUI_WINDOW_INS_EDIT) { insEditOpen=true; diff --git a/src/gui/osc.cpp b/src/gui/osc.cpp index c0ce978cf..e43dee1e0 100644 --- a/src/gui/osc.cpp +++ b/src/gui/osc.cpp @@ -22,6 +22,10 @@ #include #include +// TODO: +// - potentially move oscilloscope seek position to the end, and read the last samples +// - this allows for setting up the window size +// - reduce initial latency (it's too high) void FurnaceGUI::readOsc() { int writePos=e->oscWritePos; int readPos=e->oscReadPos; @@ -65,6 +69,9 @@ void FurnaceGUI::readOsc() { e->oscReadPos=readPos; } +// TODO: +// - draw reference level +// - draw guidelines void FurnaceGUI::drawOsc() { if (nextWindow==GUI_WINDOW_OSCILLOSCOPE) { oscOpen=true; diff --git a/src/gui/pattern.cpp b/src/gui/pattern.cpp index 0decef970..de0e5a61f 100644 --- a/src/gui/pattern.cpp +++ b/src/gui/pattern.cpp @@ -408,6 +408,7 @@ void FurnaceGUI::drawPattern() { patWindowSize=ImGui::GetWindowSize(); //char id[32]; ImGui::PushFont(patFont); + // TODO: separate GUI curOrder from engine curOrder int ord=e->isPlaying()?oldOrder:e->getOrder(); oldOrder=e->getOrder(); int chans=e->getTotalChannelCount(); diff --git a/src/gui/piano.cpp b/src/gui/piano.cpp index d3a3d35ef..aa18996f3 100644 --- a/src/gui/piano.cpp +++ b/src/gui/piano.cpp @@ -20,6 +20,7 @@ #include "gui.h" #include "guiConst.h" +// TODO: actually implement a piano! void FurnaceGUI::drawPiano() { if (nextWindow==GUI_WINDOW_PIANO) { pianoOpen=true; diff --git a/src/gui/sampleEdit.cpp b/src/gui/sampleEdit.cpp index 04af4cab3..34cb23d5f 100644 --- a/src/gui/sampleEdit.cpp +++ b/src/gui/sampleEdit.cpp @@ -28,6 +28,12 @@ #include "sampleUtil.h" #include "util.h" +// TODO: +// - resample should change C-4 note +// - clicking on waveform should give this window focus +// - add "create instrument using this sample" option +// - .dmc loading +// - duplicate sample void FurnaceGUI::drawSampleEdit() { if (nextWindow==GUI_WINDOW_SAMPLE_EDIT) { sampleEditOpen=true; diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index 87b798572..d8070dc65 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -198,6 +198,10 @@ struct MappedInput { scan(s), val(v) {} }; +// TODO: +// - add metronome volume setting +// - maybe add metronome sound setting +// - add a "close" button to this window void FurnaceGUI::drawSettings() { if (nextWindow==GUI_WINDOW_SETTINGS) { settingsOpen=true; From af2b19ae622e7a5fff8fd4bd3640a17dc391a8dd Mon Sep 17 00:00:00 2001 From: BlastBrothers Date: Mon, 11 Apr 2022 20:15:03 -0400 Subject: [PATCH 014/342] Revert "Sample preview has maximum y height relative to x" This reverts commit 1151db50912cb17491937e84459454fbb369576f. --- src/gui/sampleEdit.cpp | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/gui/sampleEdit.cpp b/src/gui/sampleEdit.cpp index a4c744b19..bbd946819 100644 --- a/src/gui/sampleEdit.cpp +++ b/src/gui/sampleEdit.cpp @@ -564,14 +564,8 @@ void FurnaceGUI::drawSampleEdit() { ImGui::Separator(); - ImVec2 avail=ImGui::GetContentRegionAvail(); // graph size determined here - if (ImGui::GetContentRegionAvail().y > (ImGui::GetContentRegionAvail().x / 2.0f)) { - avail=ImVec2(ImGui::GetContentRegionAvail().x,ImGui::GetContentRegionAvail().x / 2.0f); - } + ImVec2 avail=ImGui::GetContentRegionAvail(); avail.y-=ImGui::GetFontSize()+ImGui::GetStyle().ItemSpacing.y+ImGui::GetStyle().ScrollbarSize; - if (avail.y < 1.0){ //Prevents crash - avail.y = 1.0; - } int availX=avail.x; int availY=avail.y; From 61715fd8b0be63aa9c4768a408e048d27b9acf27 Mon Sep 17 00:00:00 2001 From: BlastBrothers Date: Mon, 11 Apr 2022 20:15:46 -0400 Subject: [PATCH 015/342] Revert "Rearrange buttons" This reverts commit 052a0923fee89c60787d438ca74d63abf693396b. --- src/gui/sampleEdit.cpp | 148 ++++++++++++++++++++--------------------- 1 file changed, 72 insertions(+), 76 deletions(-) diff --git a/src/gui/sampleEdit.cpp b/src/gui/sampleEdit.cpp index bbd946819..4065e074a 100644 --- a/src/gui/sampleEdit.cpp +++ b/src/gui/sampleEdit.cpp @@ -243,6 +243,77 @@ void FurnaceGUI::drawSampleEdit() { ImGui::SameLine(); ImGui::Dummy(ImVec2(0.5*dpiScale,dpiScale)); ImGui::SameLine(); + ImGui::Button(ICON_FA_VOLUME_UP "##SAmplify"); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Amplify"); + } + if (openSampleAmplifyOpt) { + openSampleAmplifyOpt=false; + ImGui::OpenPopup("SAmplifyOpt"); + } + if (ImGui::BeginPopupContextItem("SAmplifyOpt",ImGuiPopupFlags_MouseButtonLeft)) { + ImGui::Text("Volume"); + if (ImGui::InputFloat("##SRVolume",&lifyVol,10.0,50.0,"%g%%")) { + if (amplifyVol<0) amplifyVol=0; + if (amplifyVol>10000) amplifyVol=10000; + } + ImGui::SameLine(); + ImGui::Text("(%.1fdB)",20.0*log10(amplifyVol/100.0f)); + if (ImGui::Button("Apply")) { + sample->prepareUndo(true); + e->lockEngine([this,sample]() { + SAMPLE_OP_BEGIN; + float vol=amplifyVol/100.0f; + + if (sample->depth==16) { + for (unsigned int i=start; idata16[i]*vol; + if (val<-32768) val=-32768; + if (val>32767) val=32767; + sample->data16[i]=val; + } + } else if (sample->depth==8) { + for (unsigned int i=start; idata8[i]*vol; + if (val<-128) val=-128; + if (val>127) val=127; + sample->data8[i]=val; + } + } + + updateSampleTex=true; + + e->renderSamples(); + }); + MARK_MODIFIED; + ImGui::CloseCurrentPopup(); + } + ImGui::EndPopup(); + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_ARROWS_V "##SNormalize")) { + doAction(GUI_ACTION_SAMPLE_NORMALIZE); + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Normalize"); + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_ARROW_UP "##SFadeIn")) { + doAction(GUI_ACTION_SAMPLE_FADE_IN); + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Fade in"); + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_ARROW_DOWN "##SFadeOut")) { + doAction(GUI_ACTION_SAMPLE_FADE_OUT); + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Fade out"); + } + ImGui::SameLine(); + ImGui::Dummy(ImVec2(0.5*dpiScale,dpiScale)); + ImGui::SameLine(); ImGui::Button(ICON_FA_INDUSTRY "##SFilter"); if (ImGui::IsItemHovered()) { ImGui::SetTooltip("Apply filter"); @@ -355,79 +426,6 @@ void FurnaceGUI::drawSampleEdit() { } ImGui::EndPopup(); } - ImGui::SameLine(); - ImGui::Dummy(ImVec2(0.5*dpiScale,dpiScale)); - ImGui::SameLine(); - - - ImGui::Button(ICON_FA_VOLUME_UP "##SAmplify"); - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("Amplify"); - } - if (openSampleAmplifyOpt) { - openSampleAmplifyOpt=false; - ImGui::OpenPopup("SAmplifyOpt"); - } - if (ImGui::BeginPopupContextItem("SAmplifyOpt",ImGuiPopupFlags_MouseButtonLeft)) { - ImGui::Text("Volume"); - if (ImGui::InputFloat("##SRVolume",&lifyVol,10.0,50.0,"%g%%")) { - if (amplifyVol<0) amplifyVol=0; - if (amplifyVol>10000) amplifyVol=10000; - } - ImGui::SameLine(); - ImGui::Text("(%.1fdB)",20.0*log10(amplifyVol/100.0f)); - if (ImGui::Button("Apply")) { - sample->prepareUndo(true); - e->lockEngine([this,sample]() { - SAMPLE_OP_BEGIN; - float vol=amplifyVol/100.0f; - - if (sample->depth==16) { - for (unsigned int i=start; idata16[i]*vol; - if (val<-32768) val=-32768; - if (val>32767) val=32767; - sample->data16[i]=val; - } - } else if (sample->depth==8) { - for (unsigned int i=start; idata8[i]*vol; - if (val<-128) val=-128; - if (val>127) val=127; - sample->data8[i]=val; - } - } - - updateSampleTex=true; - - e->renderSamples(); - }); - MARK_MODIFIED; - ImGui::CloseCurrentPopup(); - } - ImGui::EndPopup(); - } - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_ARROWS_V "##SNormalize")) { - doAction(GUI_ACTION_SAMPLE_NORMALIZE); - } - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("Normalize"); - } - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_ARROW_UP "##SFadeIn")) { - doAction(GUI_ACTION_SAMPLE_FADE_IN); - } - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("Fade in"); - } - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_ARROW_DOWN "##SFadeOut")) { - doAction(GUI_ACTION_SAMPLE_FADE_OUT); - } - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("Fade out"); - } if (ImGui::Button(ICON_FA_UNDO "##SUndo")) { doUndoSample(); @@ -459,8 +457,6 @@ void FurnaceGUI::drawSampleEdit() { ImGui::SetTooltip("Invert"); } ImGui::SameLine(); - ImGui::Dummy(ImVec2(0.5*dpiScale,dpiScale)); - ImGui::SameLine(); if (ImGui::Button(ICON_FA_LEVEL_DOWN "##SSign")) { doAction(GUI_ACTION_SAMPLE_SIGN); } @@ -468,7 +464,7 @@ void FurnaceGUI::drawSampleEdit() { ImGui::SetTooltip("Signed/unsigned exchange"); } ImGui::SameLine(); - ImGui::Dummy(ImVec2(0.5*dpiScale,dpiScale)); + ImGui::Dummy(ImVec2(7.0*dpiScale,dpiScale)); ImGui::SameLine(); ImGui::Button(ICON_FA_ADJUST "##SInsertSilence"); if (ImGui::IsItemHovered()) { From f6381771a9708ce1b6c5f1c162e40c44da03a504 Mon Sep 17 00:00:00 2001 From: BlastBrothers Date: Mon, 11 Apr 2022 20:16:51 -0400 Subject: [PATCH 016/342] Revert "Thin sample editor" This reverts commit 8a2bf3ae3945211e4d8f5c0f281ff8282e01dbf6. --- src/gui/sampleEdit.cpp | 194 ++++++++++++++++++++--------------------- 1 file changed, 97 insertions(+), 97 deletions(-) diff --git a/src/gui/sampleEdit.cpp b/src/gui/sampleEdit.cpp index 4065e074a..34cb23d5f 100644 --- a/src/gui/sampleEdit.cpp +++ b/src/gui/sampleEdit.cpp @@ -59,7 +59,7 @@ void FurnaceGUI::drawSampleEdit() { MARK_MODIFIED; } - if (ImGui::BeginTable("SampleProps",2,ImGuiTableFlags_SizingStretchSame)) { + if (ImGui::BeginTable("SampleProps",4,ImGuiTableFlags_SizingStretchSame)) { ImGui::TableNextRow(); ImGui::TableNextColumn(); ImGui::Text("Type"); @@ -88,7 +88,6 @@ void FurnaceGUI::drawSampleEdit() { if (sample->rate>96000) sample->rate=96000; } - ImGui::TableNextRow(); ImGui::TableNextColumn(); ImGui::Text("C-4 (Hz)"); ImGui::SameLine(); @@ -149,7 +148,7 @@ void FurnaceGUI::drawSampleEdit() { ImGui::SetTooltip("Edit mode: Draw"); } ImGui::SameLine(); - ImGui::Dummy(ImVec2(7.0*dpiScale,dpiScale)); + ImGui::Dummy(ImVec2(4.0*dpiScale,dpiScale)); ImGui::SameLine(); ImGui::Button(ICON_FA_ARROWS_H "##SResize"); if (ImGui::IsItemClicked()) { @@ -241,7 +240,23 @@ void FurnaceGUI::drawSampleEdit() { resampleTarget=sample->rate; } ImGui::SameLine(); - ImGui::Dummy(ImVec2(0.5*dpiScale,dpiScale)); + ImGui::Dummy(ImVec2(4.0*dpiScale,dpiScale)); + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_UNDO "##SUndo")) { + doUndoSample(); + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Undo"); + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_REPEAT "##SRedo")) { + doRedoSample(); + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Redo"); + } + ImGui::SameLine(); + ImGui::Dummy(ImVec2(4.0*dpiScale,dpiScale)); ImGui::SameLine(); ImGui::Button(ICON_FA_VOLUME_UP "##SAmplify"); if (ImGui::IsItemHovered()) { @@ -312,7 +327,80 @@ void FurnaceGUI::drawSampleEdit() { ImGui::SetTooltip("Fade out"); } ImGui::SameLine(); - ImGui::Dummy(ImVec2(0.5*dpiScale,dpiScale)); + ImGui::Button(ICON_FA_ADJUST "##SInsertSilence"); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Insert silence"); + } + if (openSampleSilenceOpt) { + openSampleSilenceOpt=false; + ImGui::OpenPopup("SSilenceOpt"); + } + if (ImGui::BeginPopupContextItem("SSilenceOpt",ImGuiPopupFlags_MouseButtonLeft)) { + if (ImGui::InputInt("Samples",&silenceSize,1,64)) { + if (silenceSize<0) silenceSize=0; + if (silenceSize>16777215) silenceSize=16777215; + } + if (ImGui::Button("Resize")) { + int pos=(sampleSelStart==-1 || sampleSelStart==sampleSelEnd)?sample->samples:sampleSelStart; + sample->prepareUndo(true); + e->lockEngine([this,sample,pos]() { + if (!sample->insert(pos,silenceSize)) { + showError("couldn't insert! make sure your sample is 8 or 16-bit."); + } + e->renderSamples(); + }); + updateSampleTex=true; + sampleSelStart=pos; + sampleSelEnd=pos+silenceSize; + MARK_MODIFIED; + ImGui::CloseCurrentPopup(); + } + ImGui::EndPopup(); + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_ERASER "##SSilence")) { + doAction(GUI_ACTION_SAMPLE_SILENCE); + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Apply silence"); + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_TIMES "##SDelete")) { + doAction(GUI_ACTION_SAMPLE_DELETE); + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Delete"); + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_CROP "##STrim")) { + doAction(GUI_ACTION_SAMPLE_TRIM); + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Trim"); + } + ImGui::SameLine(); + ImGui::Dummy(ImVec2(4.0*dpiScale,dpiScale)); + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_BACKWARD "##SReverse")) { + doAction(GUI_ACTION_SAMPLE_REVERSE); + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Reverse"); + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_SORT_AMOUNT_ASC "##SInvert")) { + doAction(GUI_ACTION_SAMPLE_INVERT); + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Invert"); + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_LEVEL_DOWN "##SSign")) { + doAction(GUI_ACTION_SAMPLE_SIGN); + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Signed/unsigned exchange"); + } ImGui::SameLine(); ImGui::Button(ICON_FA_INDUSTRY "##SFilter"); if (ImGui::IsItemHovered()) { @@ -426,111 +514,23 @@ void FurnaceGUI::drawSampleEdit() { } ImGui::EndPopup(); } - - if (ImGui::Button(ICON_FA_UNDO "##SUndo")) { - doUndoSample(); - } - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("Undo"); - } ImGui::SameLine(); - if (ImGui::Button(ICON_FA_REPEAT "##SRedo")) { - doRedoSample(); - } - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("Redo"); - } + ImGui::Dummy(ImVec2(4.0*dpiScale,dpiScale)); ImGui::SameLine(); - ImGui::Dummy(ImVec2(7.0*dpiScale,dpiScale)); - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_BACKWARD "##SReverse")) { - doAction(GUI_ACTION_SAMPLE_REVERSE); - } - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("Reverse"); - } - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_SORT_AMOUNT_ASC "##SInvert")) { - doAction(GUI_ACTION_SAMPLE_INVERT); - } - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("Invert"); - } - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_LEVEL_DOWN "##SSign")) { - doAction(GUI_ACTION_SAMPLE_SIGN); - } - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("Signed/unsigned exchange"); - } - ImGui::SameLine(); - ImGui::Dummy(ImVec2(7.0*dpiScale,dpiScale)); - ImGui::SameLine(); - ImGui::Button(ICON_FA_ADJUST "##SInsertSilence"); - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("Insert silence"); - } - if (openSampleSilenceOpt) { - openSampleSilenceOpt=false; - ImGui::OpenPopup("SSilenceOpt"); - } - if (ImGui::BeginPopupContextItem("SSilenceOpt",ImGuiPopupFlags_MouseButtonLeft)) { - if (ImGui::InputInt("Samples",&silenceSize,1,64)) { - if (silenceSize<0) silenceSize=0; - if (silenceSize>16777215) silenceSize=16777215; - } - if (ImGui::Button("Resize")) { - int pos=(sampleSelStart==-1 || sampleSelStart==sampleSelEnd)?sample->samples:sampleSelStart; - sample->prepareUndo(true); - e->lockEngine([this,sample,pos]() { - if (!sample->insert(pos,silenceSize)) { - showError("couldn't insert! make sure your sample is 8 or 16-bit."); - } - e->renderSamples(); - }); - updateSampleTex=true; - sampleSelStart=pos; - sampleSelEnd=pos+silenceSize; - MARK_MODIFIED; - ImGui::CloseCurrentPopup(); - } - ImGui::EndPopup(); - } - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_ERASER "##SSilence")) { - doAction(GUI_ACTION_SAMPLE_SILENCE); - } - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("Apply silence"); - } - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_TIMES "##SDelete")) { - doAction(GUI_ACTION_SAMPLE_DELETE); - } - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("Delete"); - } - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_CROP "##STrim")) { - doAction(GUI_ACTION_SAMPLE_TRIM); - } - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("Trim"); - } - - if (ImGui::Button(ICON_FA_PLAY "##PreviewSample")) { + if (ImGui::Button(ICON_FA_VOLUME_UP "##PreviewSample")) { e->previewSample(curSample); } if (ImGui::IsItemHovered()) { ImGui::SetTooltip("Preview sample"); } ImGui::SameLine(); - if (ImGui::Button(ICON_FA_STOP "##StopSample")) { + if (ImGui::Button(ICON_FA_VOLUME_OFF "##StopSample")) { e->stopSamplePreview(); } if (ImGui::IsItemHovered()) { ImGui::SetTooltip("Stop sample preview"); } + ImGui::SameLine(); double zoomPercent=100.0/sampleZoom; ImGui::Text("Zoom"); From f362b1288c1af739e427d58ce2f4faef5633d99b Mon Sep 17 00:00:00 2001 From: BlastBrothers Date: Mon, 11 Apr 2022 21:27:23 -0400 Subject: [PATCH 017/342] Add (currently useless) menu option ofr compact editor --- src/gui/gui.h | 2 ++ src/gui/settings.cpp | 9 +++++++++ 2 files changed, 11 insertions(+) diff --git a/src/gui/gui.h b/src/gui/gui.h index 53b2f1715..41d3590ed 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -775,6 +775,7 @@ class FurnaceGUI { int roundedMenus; int loadJapanese; int fmLayout; + int sampleLayout; int susPosition; int effectCursorDir; int cursorPastePos; @@ -838,6 +839,7 @@ class FurnaceGUI { roundedMenus(0), loadJapanese(0), fmLayout(0), + sampleLayout(0), susPosition(0), effectCursorDir(1), cursorPastePos(1), diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index d8070dc65..9c64f404e 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -832,6 +832,8 @@ void FurnaceGUI::drawSettings() { settings.susPosition=1; } + ImGui::Separator(); + bool macroViewB=settings.macroView; if (ImGui::Checkbox("Classic macro view (standard macros only; deprecated!)",¯oViewB)) { settings.macroView=macroViewB; @@ -878,6 +880,11 @@ void FurnaceGUI::drawSettings() { ImGui::Separator(); + bool sampleLayoutB=settings.sampleLayout; + if (ImGui::Checkbox("Use compact sample editor",&sampleLayoutB)) { + settings.sampleLayout=sampleLayoutB; + } + bool roundedWindowsB=settings.roundedWindows; if (ImGui::Checkbox("Rounded window corners",&roundedWindowsB)) { settings.roundedWindows=roundedWindowsB; @@ -1502,6 +1509,7 @@ void FurnaceGUI::syncSettings() { settings.roundedMenus=e->getConfInt("roundedMenus",0); settings.loadJapanese=e->getConfInt("loadJapanese",0); settings.fmLayout=e->getConfInt("fmLayout",0); + settings.sampleLayout=e->getConfInt("sampleLayout",0); settings.susPosition=e->getConfInt("susPosition",0); settings.effectCursorDir=e->getConfInt("effectCursorDir",1); settings.cursorPastePos=e->getConfInt("cursorPastePos",1); @@ -1632,6 +1640,7 @@ void FurnaceGUI::commitSettings() { e->setConf("roundedMenus",settings.roundedMenus); e->setConf("loadJapanese",settings.loadJapanese); e->setConf("fmLayout",settings.fmLayout); + e->setConf("sampleLayout",settings.sampleLayout); e->setConf("susPosition",settings.susPosition); e->setConf("effectCursorDir",settings.effectCursorDir); e->setConf("cursorPastePos",settings.cursorPastePos); From 93b87cc65d7a179ca7fb7892d98555f53ba6fb64 Mon Sep 17 00:00:00 2001 From: BlastBrothers Date: Mon, 11 Apr 2022 21:51:43 -0400 Subject: [PATCH 018/342] Sample preview can only be so tall now --- src/gui/sampleEdit.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/gui/sampleEdit.cpp b/src/gui/sampleEdit.cpp index 34cb23d5f..dbe5c2cba 100644 --- a/src/gui/sampleEdit.cpp +++ b/src/gui/sampleEdit.cpp @@ -561,7 +561,14 @@ void FurnaceGUI::drawSampleEdit() { ImGui::Separator(); ImVec2 avail=ImGui::GetContentRegionAvail(); + if(ImGui::GetContentRegionAvail().y > (ImGui::GetContentRegionAvail().x / 2.0f)) { + avail=ImVec2(ImGui::GetContentRegionAvail().x,ImGui::GetContentRegionAvail().x / 2.0f); + } avail.y-=ImGui::GetFontSize()+ImGui::GetStyle().ItemSpacing.y+ImGui::GetStyle().ScrollbarSize; + if (avail.y < 1.0){ //Prevents crash + avail.y = 1.0; + } + int availX=avail.x; int availY=avail.y; From 0771cdcdae12647a9c12e6ce3aadc8f922c83803 Mon Sep 17 00:00:00 2001 From: BlastBrothers Date: Mon, 11 Apr 2022 22:14:20 -0400 Subject: [PATCH 019/342] Add in the alternate editor as an option --- src/gui/sampleEdit.cpp | 776 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 775 insertions(+), 1 deletion(-) diff --git a/src/gui/sampleEdit.cpp b/src/gui/sampleEdit.cpp index dbe5c2cba..13e759c74 100644 --- a/src/gui/sampleEdit.cpp +++ b/src/gui/sampleEdit.cpp @@ -44,7 +44,7 @@ void FurnaceGUI::drawSampleEdit() { if (ImGui::Begin("Sample Editor",&sampleEditOpen,settings.allowEditDocking?0:ImGuiWindowFlags_NoDocking)) { if (curSample<0 || curSample>=(int)e->song.sample.size()) { ImGui::Text("no sample selected"); - } else { + } else if (!settings.sampleLayout) { DivSample* sample=e->song.sample[curSample]; String sampleType="Invalid"; if (sample->depth<17) { @@ -856,6 +856,780 @@ void FurnaceGUI::drawSampleEdit() { ImGui::Text("- none"); }*/ } + else { + DivSample* sample=e->song.sample[curSample]; + String sampleType="Invalid"; + if (sample->depth<17) { + if (sampleDepths[sample->depth]!=NULL) { + sampleType=sampleDepths[sample->depth]; + } + } + ImGui::Text("Name"); + ImGui::SameLine(); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + if (ImGui::InputText("##SampleName",&sample->name)) { + MARK_MODIFIED; + } + + if (ImGui::BeginTable("SampleProps",2,ImGuiTableFlags_SizingStretchSame)) { + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::Text("Type"); + ImGui::SameLine(); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + if (ImGui::BeginCombo("##SampleType",sampleType.c_str())) { + for (int i=0; i<17; i++) { + if (sampleDepths[i]==NULL) continue; + if (ImGui::Selectable(sampleDepths[i])) { + sample->prepareUndo(true); + sample->depth=i; + e->renderSamplesP(); + updateSampleTex=true; + MARK_MODIFIED; + } + } + ImGui::EndCombo(); + } + + ImGui::TableNextColumn(); + ImGui::Text("Rate (Hz)"); + ImGui::SameLine(); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + if (ImGui::InputInt("##SampleRate",&sample->rate,10,200)) { MARK_MODIFIED + if (sample->rate<100) sample->rate=100; + if (sample->rate>96000) sample->rate=96000; + } + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::Text("C-4 (Hz)"); + ImGui::SameLine(); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + if (ImGui::InputInt("##SampleCenter",&sample->centerRate,10,200)) { MARK_MODIFIED + if (sample->centerRate<100) sample->centerRate=100; + if (sample->centerRate>65535) sample->centerRate=65535; + } + + ImGui::TableNextColumn(); + bool doLoop=(sample->loopStart>=0); + if (ImGui::Checkbox("Loop",&doLoop)) { MARK_MODIFIED + if (doLoop) { + sample->loopStart=0; + } else { + sample->loopStart=-1; + } + updateSampleTex=true; + } + if (doLoop) { + ImGui::SameLine(); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + if (ImGui::InputInt("##LoopPosition",&sample->loopStart,1,10)) { MARK_MODIFIED + if (sample->loopStart<0 || sample->loopStart>=(int)sample->samples) { + sample->loopStart=0; + } + updateSampleTex=true; + } + } + ImGui::EndTable(); + } + + /* + if (ImGui::Button("Apply")) { + e->renderSamplesP(); + } + ImGui::SameLine(); + */ + ImGui::Separator(); + + ImGui::BeginDisabled(sample->depth!=8 && sample->depth!=16); + + ImGui::PushStyleColor(ImGuiCol_Button,TOGGLE_COLOR(!sampleDragMode)); + if (ImGui::Button(ICON_FA_I_CURSOR "##SSelect")) { + sampleDragMode=false; + } + ImGui::PopStyleColor(); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Edit mode: Select"); + } + ImGui::SameLine(); + ImGui::PushStyleColor(ImGuiCol_Button,TOGGLE_COLOR(sampleDragMode)); + if (ImGui::Button(ICON_FA_PENCIL "##SDraw")) { + sampleDragMode=true; + } + ImGui::PopStyleColor(); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Edit mode: Draw"); + } + ImGui::SameLine(); + ImGui::Dummy(ImVec2(7.0*dpiScale,dpiScale)); + ImGui::SameLine(); + ImGui::Button(ICON_FA_ARROWS_H "##SResize"); + if (ImGui::IsItemClicked()) { + resizeSize=sample->samples; + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Resize"); + } + if (openSampleResizeOpt) { + openSampleResizeOpt=false; + ImGui::OpenPopup("SResizeOpt"); + } + if (ImGui::BeginPopupContextItem("SResizeOpt",ImGuiPopupFlags_MouseButtonLeft)) { + if (ImGui::InputInt("Samples",&resizeSize,1,64)) { + if (resizeSize<0) resizeSize=0; + if (resizeSize>16777215) resizeSize=16777215; + } + if (ImGui::Button("Resize")) { + sample->prepareUndo(true); + e->lockEngine([this,sample]() { + if (!sample->resize(resizeSize)) { + showError("couldn't resize! make sure your sample is 8 or 16-bit."); + } + e->renderSamples(); + }); + updateSampleTex=true; + sampleSelStart=-1; + sampleSelEnd=-1; + MARK_MODIFIED; + ImGui::CloseCurrentPopup(); + } + ImGui::EndPopup(); + } else { + resizeSize=sample->samples; + } + ImGui::SameLine(); + ImGui::Button(ICON_FA_EXPAND "##SResample"); + if (ImGui::IsItemClicked()) { + resampleTarget=sample->rate; + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Resample"); + } + if (openSampleResampleOpt) { + openSampleResampleOpt=false; + ImGui::OpenPopup("SResampleOpt"); + } + if (ImGui::BeginPopupContextItem("SResampleOpt",ImGuiPopupFlags_MouseButtonLeft)) { + ImGui::Text("Rate"); + if (ImGui::InputDouble("##SRRate",&resampleTarget,1.0,50.0,"%g")) { + if (resampleTarget<0) resampleTarget=0; + if (resampleTarget>96000) resampleTarget=96000; + } + ImGui::SameLine(); + if (ImGui::Button("0.5x")) { + resampleTarget*=0.5; + } + ImGui::SameLine(); + if (ImGui::Button("==")) { + resampleTarget=sample->rate; + } + ImGui::SameLine(); + if (ImGui::Button("2.0x")) { + resampleTarget*=2.0; + } + double factor=resampleTarget/(double)sample->rate; + if (ImGui::InputDouble("Factor",&factor,0.125,0.5,"%g")) { + resampleTarget=(double)sample->rate*factor; + if (resampleTarget<0) resampleTarget=0; + if (resampleTarget>96000) resampleTarget=96000; + } + ImGui::Combo("Filter",&resampleStrat,resampleStrats,6); + if (ImGui::Button("Resample")) { + sample->prepareUndo(true); + e->lockEngine([this,sample]() { + if (!sample->resample(resampleTarget,resampleStrat)) { + showError("couldn't resample! make sure your sample is 8 or 16-bit."); + } + e->renderSamples(); + }); + updateSampleTex=true; + sampleSelStart=-1; + sampleSelEnd=-1; + MARK_MODIFIED; + ImGui::CloseCurrentPopup(); + } + ImGui::EndPopup(); + } else { + resampleTarget=sample->rate; + } + ImGui::SameLine(); + ImGui::Dummy(ImVec2(0.5*dpiScale,dpiScale)); + ImGui::SameLine(); + ImGui::Button(ICON_FA_INDUSTRY "##SFilter"); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Apply filter"); + } + if (openSampleFilterOpt) { + openSampleFilterOpt=false; + ImGui::OpenPopup("SFilterOpt"); + } + if (ImGui::BeginPopupContextItem("SFilterOpt",ImGuiPopupFlags_MouseButtonLeft)) { + float lowP=sampleFilterL*100.0f; + float bandP=sampleFilterB*100.0f; + float highP=sampleFilterH*100.0f; + float resP=sampleFilterRes*100.0f; + ImGui::Text("Cutoff:"); + if (ImGui::SliderFloat("From",&sampleFilterCutStart,0.0f,sample->rate*0.5,"%.0fHz")) { + if (sampleFilterCutStart<0.0) sampleFilterCutStart=0.0; + if (sampleFilterCutStart>sample->rate*0.5) sampleFilterCutStart=sample->rate*0.5; + } + if (ImGui::SliderFloat("To",&sampleFilterCutEnd,0.0f,sample->rate*0.5,"%.0fHz")) { + if (sampleFilterCutEnd<0.0) sampleFilterCutEnd=0.0; + if (sampleFilterCutEnd>sample->rate*0.5) sampleFilterCutEnd=sample->rate*0.5; + } + ImGui::Separator(); + if (ImGui::SliderFloat("Resonance",&resP,0.0f,99.0f,"%.1f%%")) { + sampleFilterRes=resP/100.0f; + if (sampleFilterRes<0.0f) sampleFilterRes=0.0f; + if (sampleFilterRes>0.99f) sampleFilterRes=0.99f; + } + ImGui::Text("Power"); + ImGui::SameLine(); + if (ImGui::RadioButton("1x",sampleFilterPower==1)) { + sampleFilterPower=1; + } + ImGui::SameLine(); + if (ImGui::RadioButton("2x",sampleFilterPower==2)) { + sampleFilterPower=2; + } + ImGui::SameLine(); + if (ImGui::RadioButton("3x",sampleFilterPower==3)) { + sampleFilterPower=3; + } + ImGui::Separator(); + if (ImGui::SliderFloat("Low-pass",&lowP,0.0f,100.0f,"%.1f%%")) { + sampleFilterL=lowP/100.0f; + if (sampleFilterL<0.0f) sampleFilterL=0.0f; + if (sampleFilterL>1.0f) sampleFilterL=1.0f; + } + if (ImGui::SliderFloat("Band-pass",&bandP,0.0f,100.0f,"%.1f%%")) { + sampleFilterB=bandP/100.0f; + if (sampleFilterB<0.0f) sampleFilterB=0.0f; + if (sampleFilterB>1.0f) sampleFilterB=1.0f; + } + if (ImGui::SliderFloat("High-pass",&highP,0.0f,100.0f,"%.1f%%")) { + sampleFilterH=highP/100.0f; + if (sampleFilterH<0.0f) sampleFilterH=0.0f; + if (sampleFilterH>1.0f) sampleFilterH=1.0f; + } + + if (ImGui::Button("Apply")) { + sample->prepareUndo(true); + e->lockEngine([this,sample]() { + SAMPLE_OP_BEGIN; + float res=1.0-pow(sampleFilterRes,0.5f); + float low=0; + float band=0; + float high=0; + + double power=(sampleFilterCutStart>sampleFilterCutEnd)?0.5:2.0; + + if (sample->depth==16) { + for (unsigned int i=start; irate))*M_PI); + + for (int j=0; jdata16[i])-low-(res*band); + band=cut*high+band; + } + + float val=low*sampleFilterL+band*sampleFilterB+high*sampleFilterH; + if (val<-32768) val=-32768; + if (val>32767) val=32767; + sample->data16[i]=val; + } + } else if (sample->depth==8) { + for (unsigned int i=start; irate))*M_PI); + + for (int j=0; jdata8[i])-low-(res*band); + band=cut*high+band; + } + + float val=low*sampleFilterL+band*sampleFilterB+high*sampleFilterH; + if (val<-128) val=-128; + if (val>127) val=127; + sample->data8[i]=val; + } + } + + updateSampleTex=true; + + e->renderSamples(); + }); + MARK_MODIFIED; + ImGui::CloseCurrentPopup(); + } + ImGui::EndPopup(); + } + ImGui::SameLine(); + ImGui::Dummy(ImVec2(0.5*dpiScale,dpiScale)); + ImGui::SameLine(); + + + ImGui::Button(ICON_FA_VOLUME_UP "##SAmplify"); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Amplify"); + } + if (openSampleAmplifyOpt) { + openSampleAmplifyOpt=false; + ImGui::OpenPopup("SAmplifyOpt"); + } + if (ImGui::BeginPopupContextItem("SAmplifyOpt",ImGuiPopupFlags_MouseButtonLeft)) { + ImGui::Text("Volume"); + if (ImGui::InputFloat("##SRVolume",&lifyVol,10.0,50.0,"%g%%")) { + if (amplifyVol<0) amplifyVol=0; + if (amplifyVol>10000) amplifyVol=10000; + } + ImGui::SameLine(); + ImGui::Text("(%.1fdB)",20.0*log10(amplifyVol/100.0f)); + if (ImGui::Button("Apply")) { + sample->prepareUndo(true); + e->lockEngine([this,sample]() { + SAMPLE_OP_BEGIN; + float vol=amplifyVol/100.0f; + + if (sample->depth==16) { + for (unsigned int i=start; idata16[i]*vol; + if (val<-32768) val=-32768; + if (val>32767) val=32767; + sample->data16[i]=val; + } + } else if (sample->depth==8) { + for (unsigned int i=start; idata8[i]*vol; + if (val<-128) val=-128; + if (val>127) val=127; + sample->data8[i]=val; + } + } + + updateSampleTex=true; + + e->renderSamples(); + }); + MARK_MODIFIED; + ImGui::CloseCurrentPopup(); + } + ImGui::EndPopup(); + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_ARROWS_V "##SNormalize")) { + doAction(GUI_ACTION_SAMPLE_NORMALIZE); + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Normalize"); + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_ARROW_UP "##SFadeIn")) { + doAction(GUI_ACTION_SAMPLE_FADE_IN); + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Fade in"); + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_ARROW_DOWN "##SFadeOut")) { + doAction(GUI_ACTION_SAMPLE_FADE_OUT); + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Fade out"); + } + + if (ImGui::Button(ICON_FA_UNDO "##SUndo")) { + doUndoSample(); + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Undo"); + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_REPEAT "##SRedo")) { + doRedoSample(); + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Redo"); + } + ImGui::SameLine(); + ImGui::Dummy(ImVec2(7.0*dpiScale,dpiScale)); + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_BACKWARD "##SReverse")) { + doAction(GUI_ACTION_SAMPLE_REVERSE); + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Reverse"); + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_SORT_AMOUNT_ASC "##SInvert")) { + doAction(GUI_ACTION_SAMPLE_INVERT); + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Invert"); + } + ImGui::SameLine(); + ImGui::Dummy(ImVec2(0.5*dpiScale,dpiScale)); + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_LEVEL_DOWN "##SSign")) { + doAction(GUI_ACTION_SAMPLE_SIGN); + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Signed/unsigned exchange"); + } + ImGui::SameLine(); + ImGui::Dummy(ImVec2(0.5*dpiScale,dpiScale)); + ImGui::SameLine(); + ImGui::Button(ICON_FA_ADJUST "##SInsertSilence"); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Insert silence"); + } + if (openSampleSilenceOpt) { + openSampleSilenceOpt=false; + ImGui::OpenPopup("SSilenceOpt"); + } + if (ImGui::BeginPopupContextItem("SSilenceOpt",ImGuiPopupFlags_MouseButtonLeft)) { + if (ImGui::InputInt("Samples",&silenceSize,1,64)) { + if (silenceSize<0) silenceSize=0; + if (silenceSize>16777215) silenceSize=16777215; + } + if (ImGui::Button("Resize")) { + int pos=(sampleSelStart==-1 || sampleSelStart==sampleSelEnd)?sample->samples:sampleSelStart; + sample->prepareUndo(true); + e->lockEngine([this,sample,pos]() { + if (!sample->insert(pos,silenceSize)) { + showError("couldn't insert! make sure your sample is 8 or 16-bit."); + } + e->renderSamples(); + }); + updateSampleTex=true; + sampleSelStart=pos; + sampleSelEnd=pos+silenceSize; + MARK_MODIFIED; + ImGui::CloseCurrentPopup(); + } + ImGui::EndPopup(); + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_ERASER "##SSilence")) { + doAction(GUI_ACTION_SAMPLE_SILENCE); + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Apply silence"); + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_TIMES "##SDelete")) { + doAction(GUI_ACTION_SAMPLE_DELETE); + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Delete"); + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_CROP "##STrim")) { + doAction(GUI_ACTION_SAMPLE_TRIM); + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Trim"); + } + + if (ImGui::Button(ICON_FA_PLAY "##PreviewSample")) { + e->previewSample(curSample); + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Preview sample"); + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_STOP "##StopSample")) { + e->stopSamplePreview(); + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Stop sample preview"); + } + ImGui::SameLine(); + double zoomPercent=100.0/sampleZoom; + ImGui::Text("Zoom"); + ImGui::SameLine(); + ImGui::SetNextItemWidth(150.0f*dpiScale); + if (ImGui::InputDouble("##SZoom",&zoomPercent,5.0,20.0,"%g%%")) { + if (zoomPercent>10000.0) zoomPercent=10000.0; + if (zoomPercent<1.0) zoomPercent=1.0; + sampleZoom=100.0/zoomPercent; + if (sampleZoom<0.01) sampleZoom=0.01; + sampleZoomAuto=false; + updateSampleTex=true; + } + ImGui::SameLine(); + if (sampleZoomAuto) { + if (ImGui::Button("100%")) { + sampleZoom=1.0; + sampleZoomAuto=false; + updateSampleTex=true; + } + } else { + if (ImGui::Button("Auto")) { + sampleZoomAuto=true; + updateSampleTex=true; + } + } + + ImGui::Separator(); + + ImVec2 avail=ImGui::GetContentRegionAvail(); // graph size determined here + if (ImGui::GetContentRegionAvail().y > (ImGui::GetContentRegionAvail().x / 2.0f)) { + avail=ImVec2(ImGui::GetContentRegionAvail().x,ImGui::GetContentRegionAvail().x / 2.0f); + } + avail.y-=ImGui::GetFontSize()+ImGui::GetStyle().ItemSpacing.y+ImGui::GetStyle().ScrollbarSize; + if (avail.y < 1.0){ //Prevents crash + avail.y = 1.0; + } + int availX=avail.x; + int availY=avail.y; + + + if (sampleZoomAuto) { + samplePos=0; + if (sample->samples<1 || avail.x<=0) { + sampleZoom=1.0; + } else { + sampleZoom=(double)sample->samples/avail.x; + } + if (sampleZoom!=prevSampleZoom) { + prevSampleZoom=sampleZoom; + updateSampleTex=true; + } + } + + if (sampleTex==NULL || sampleTexW!=avail.x || sampleTexH!=avail.y) { + if (sampleTex!=NULL) { + SDL_DestroyTexture(sampleTex); + sampleTex=NULL; + } + if (avail.x>=1 && avail.y>=1) { + logD("recreating sample texture."); + sampleTex=SDL_CreateTexture(sdlRend,SDL_PIXELFORMAT_ABGR8888,SDL_TEXTUREACCESS_STREAMING,avail.x,avail.y); + sampleTexW=avail.x; + sampleTexH=avail.y; + if (sampleTex==NULL) { + logE("error while creating sample texture! %s",SDL_GetError()); + } else { + updateSampleTex=true; + } + } + } + + if (sampleTex!=NULL) { + if (updateSampleTex) { + unsigned int* data=NULL; + int pitch=0; + logD("updating sample texture."); + if (SDL_LockTexture(sampleTex,NULL,(void**)&data,&pitch)!=0) { + logE("error while locking sample texture! %s",SDL_GetError()); + } else { + ImU32 bgColor=ImGui::GetColorU32(ImGuiCol_FrameBg); + ImU32 bgColorLoop=ImAlphaBlendColors(bgColor,ImGui::GetColorU32(ImGuiCol_FrameBgHovered,0.5)); + ImU32 lineColor=ImGui::GetColorU32(ImGuiCol_PlotLines); + ImU32 centerLineColor=ImAlphaBlendColors(bgColor,ImGui::GetColorU32(ImGuiCol_PlotLines,0.25)); + for (int i=0; iloopStart>=0 && sample->loopStart<(int)sample->samples && ((j+samplePos)*sampleZoom)>sample->loopStart) { + data[i*availX+j]=bgColorLoop; + } else { + data[i*availX+j]=bgColor; + } + } + } + if (availY>0) { + for (int i=availX*(availY>>1); i>1)); i++) { + data[i]=centerLineColor; + } + } + unsigned int xCoarse=samplePos; + unsigned int xFine=0; + unsigned int xAdvanceCoarse=sampleZoom; + unsigned int xAdvanceFine=fmod(sampleZoom,1.0)*16777216; + for (unsigned int i=0; i<(unsigned int)availX; i++) { + if (xCoarse>=sample->samples) break; + int y1, y2; + int totalAdvance=0; + if (sample->depth==8) { + y1=((unsigned char)sample->data8[xCoarse]^0x80)*availY/256; + } else { + y1=((unsigned short)sample->data16[xCoarse]^0x8000)*availY/65536; + } + xFine+=xAdvanceFine; + if (xFine>=16777216) { + xFine-=16777216; + totalAdvance++; + } + totalAdvance+=xAdvanceCoarse; + if (xCoarse>=sample->samples) break; + do { + if (sample->depth==8) { + y2=((unsigned char)sample->data8[xCoarse]^0x80)*availY/256; + } else { + y2=((unsigned short)sample->data16[xCoarse]^0x8000)*availY/65536; + } + if (y1>y2) { + y2^=y1; + y1^=y2; + y2^=y1; + } + if (y1<0) y1=0; + if (y1>=availY) y1=availY-1; + if (y2<0) y2=0; + if (y2>=availY) y2=availY-1; + for (int j=y1; j<=y2; j++) { + data[i+availX*(availY-j-1)]=lineColor; + } + if (totalAdvance>0) xCoarse++; + } while ((totalAdvance--)>0); + } + SDL_UnlockTexture(sampleTex); + } + updateSampleTex=false; + } + + ImGui::ImageButton(sampleTex,avail,ImVec2(0,0),ImVec2(1,1),0); + + ImVec2 rectMin=ImGui::GetItemRectMin(); + ImVec2 rectMax=ImGui::GetItemRectMax(); + ImVec2 rectSize=ImGui::GetItemRectSize(); + + if (ImGui::IsItemClicked()) { + if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) { + sampleDragActive=false; + sampleSelStart=0; + sampleSelEnd=sample->samples; + } else { + if (sample->samples>0 && (sample->depth==16 || sample->depth==8)) { + sampleDragStart=rectMin; + sampleDragAreaSize=rectSize; + sampleDrag16=(sample->depth==16); + sampleDragTarget=(sample->depth==16)?((void*)sample->data16):((void*)sample->data8); + sampleDragLen=sample->samples; + sampleDragActive=true; + sampleSelStart=-1; + sampleSelEnd=-1; + if (sampleDragMode) sample->prepareUndo(true); + processDrags(ImGui::GetMousePos().x,ImGui::GetMousePos().y); + } + } + } + + if (!sampleDragMode && ImGui::IsItemClicked(ImGuiMouseButton_Right)) { + ImGui::OpenPopup("SRightClick"); + } + + if (ImGui::BeginPopup("SRightClick",ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_AlwaysAutoResize)) { + if (ImGui::MenuItem("cut",BIND_FOR(GUI_ACTION_SAMPLE_CUT))) { + doAction(GUI_ACTION_SAMPLE_CUT); + } + if (ImGui::MenuItem("copy",BIND_FOR(GUI_ACTION_SAMPLE_COPY))) { + doAction(GUI_ACTION_SAMPLE_COPY); + } + if (ImGui::MenuItem("paste",BIND_FOR(GUI_ACTION_SAMPLE_PASTE))) { + doAction(GUI_ACTION_SAMPLE_PASTE); + } + if (ImGui::MenuItem("paste (replace)",BIND_FOR(GUI_ACTION_SAMPLE_PASTE_REPLACE))) { + doAction(GUI_ACTION_SAMPLE_PASTE_REPLACE); + } + if (ImGui::MenuItem("paste (mix)",BIND_FOR(GUI_ACTION_SAMPLE_PASTE_MIX))) { + doAction(GUI_ACTION_SAMPLE_PASTE_MIX); + } + if (ImGui::MenuItem("select all",BIND_FOR(GUI_ACTION_SAMPLE_SELECT_ALL))) { + doAction(GUI_ACTION_SAMPLE_SELECT_ALL); + } + ImGui::EndPopup(); + } + + String statusBar=sampleDragMode?"Draw":"Select"; + bool drawSelection=false; + + if (!sampleDragMode) { + if (sampleSelStart>=0 && sampleSelEnd>=0) { + int start=sampleSelStart; + int end=sampleSelEnd; + if (start>end) { + start^=end; + end^=start; + start^=end; + } + statusBar+=fmt::sprintf(" (%d-%d)",start,end); + drawSelection=true; + } + } + + if (ImGui::IsItemHovered()) { + int posX=-1; + int posY=0; + ImVec2 pos=ImGui::GetMousePos(); + pos.x-=rectMin.x; + pos.y-=rectMin.y; + + if (sampleZoom>0) { + posX=samplePos+pos.x*sampleZoom; + if (posX>(int)sample->samples) posX=-1; + } + posY=(0.5-pos.y/rectSize.y)*((sample->depth==8)?255:32767); + if (posX>=0) { + statusBar+=fmt::sprintf(" | (%d, %d)",posX,posY); + } + } + + if (drawSelection) { + int start=sampleSelStart; + int end=sampleSelEnd; + if (start>end) { + start^=end; + end^=start; + start^=end; + } + ImDrawList* dl=ImGui::GetWindowDrawList(); + ImVec2 p1=rectMin; + p1.x+=(start-samplePos)/sampleZoom; + + ImVec2 p2=ImVec2(rectMin.x+(end-samplePos)/sampleZoom,rectMax.y); + ImVec4 boundColor=uiColors[GUI_COLOR_ACCENT_PRIMARY]; + ImVec4 selColor=uiColors[GUI_COLOR_ACCENT_SECONDARY]; + boundColor.w*=0.5; + selColor.w*=0.25; + + if (p1.xrectMax.x) p1.x=rectMax.x; + + if (p2.xrectMax.x) p2.x=rectMax.x; + + dl->AddRectFilled(p1,p2,ImGui::GetColorU32(selColor)); + dl->AddLine(ImVec2(p1.x,p1.y),ImVec2(p1.x,p2.y),ImGui::GetColorU32(boundColor)); + if (start!=end) { + dl->AddLine(ImVec2(p2.x,p1.y),ImVec2(p2.x,p2.y),ImGui::GetColorU32(boundColor)); + } + } + + ImS64 scrollV=samplePos; + ImS64 availV=round(rectSize.x*sampleZoom); + ImS64 contentsV=MAX(sample->samples,MAX(availV,1)); + + if (ImGui::ScrollbarEx(ImRect(ImVec2(rectMin.x,rectMax.y),ImVec2(rectMax.x,rectMax.y+ImGui::GetStyle().ScrollbarSize)),ImGui::GetID("sampleScroll"),ImGuiAxis_X,&scrollV,availV,contentsV,0)) { + if (!sampleZoomAuto && samplePos!=scrollV) { + samplePos=scrollV; + updateSampleTex=true; + } + } + + if (sample->depth!=8 && sample->depth!=16) { + statusBar="Non-8/16-bit samples cannot be edited without prior conversion."; + } + + ImGui::EndDisabled(); + + ImGui::SetCursorPosY(ImGui::GetCursorPosY()+ImGui::GetStyle().ScrollbarSize); + ImGui::Text("%s",statusBar.c_str()); + } + } } if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_SAMPLE_EDIT; ImGui::End(); From ef5953cda5e124e4d3fff7a9f372c10730d4df28 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 11 Apr 2022 22:37:34 -0500 Subject: [PATCH 020/342] don't store the upper 24 bits of FM macro values those are unnecessary (no FM parameter ever goes above 255) --- src/engine/instrument.cpp | 108 -------------------------------------- 1 file changed, 108 deletions(-) diff --git a/src/engine/instrument.cpp b/src/engine/instrument.cpp index 3501b34a6..7dbf5b3fd 100644 --- a/src/engine/instrument.cpp +++ b/src/engine/instrument.cpp @@ -233,7 +233,6 @@ void DivInstrument::putInsData(SafeWriter* w) { w->writeC(op.ssgMacro.open); } - // FM macro low 8 bits for (int i=0; i<4; i++) { DivInstrumentSTD::OpMacro& op=std.opMacros[i]; for (int j=0; jwriteC(ws.param2); w->writeC(ws.param3); w->writeC(ws.param4); - - // FM macro high 24 bits - for (int i=0; i<4; i++) { - DivInstrumentSTD::OpMacro& op=std.opMacros[i]; - for (int j=0; jwriteC((op.amMacro.val[j]>>8)&0xff); - w->writeS((op.amMacro.val[j]>>16)&0xffff); - } - for (int j=0; jwriteC((op.arMacro.val[j]>>8)&0xff); - w->writeS((op.arMacro.val[j]>>16)&0xffff); - } - for (int j=0; jwriteC((op.drMacro.val[j]>>8)&0xff); - w->writeS((op.drMacro.val[j]>>16)&0xffff); - } - for (int j=0; jwriteC((op.multMacro.val[j]>>8)&0xff); - w->writeS((op.multMacro.val[j]>>16)&0xffff); - } - for (int j=0; jwriteC((op.rrMacro.val[j]>>8)&0xff); - w->writeS((op.rrMacro.val[j]>>16)&0xffff); - } - for (int j=0; jwriteC((op.slMacro.val[j]>>8)&0xff); - w->writeS((op.slMacro.val[j]>>16)&0xffff); - } - for (int j=0; jwriteC((op.tlMacro.val[j]>>8)&0xff); - w->writeS((op.tlMacro.val[j]>>16)&0xffff); - } - for (int j=0; jwriteC((op.dt2Macro.val[j]>>8)&0xff); - w->writeS((op.dt2Macro.val[j]>>16)&0xffff); - } - for (int j=0; jwriteC((op.rsMacro.val[j]>>8)&0xff); - w->writeS((op.rsMacro.val[j]>>16)&0xffff); - } - for (int j=0; jwriteC((op.dtMacro.val[j]>>8)&0xff); - w->writeS((op.dtMacro.val[j]>>16)&0xffff); - } - for (int j=0; jwriteC((op.d2rMacro.val[j]>>8)&0xff); - w->writeS((op.d2rMacro.val[j]>>16)&0xffff); - } - for (int j=0; jwriteC((op.ssgMacro.val[j]>>8)&0xff); - w->writeS((op.ssgMacro.val[j]>>16)&0xffff); - } - } } DivDataErrors DivInstrument::readInsData(SafeReader& reader, short version) { @@ -1001,60 +947,6 @@ DivDataErrors DivInstrument::readInsData(SafeReader& reader, short version) { ws.param4=reader.readC(); } - // FM macro high 24 bits - if (version>=81) { - for (int i=0; i<4; i++) { - DivInstrumentSTD::OpMacro& op=std.opMacros[i]; - for (int j=0; j Date: Tue, 12 Apr 2022 00:17:34 -0500 Subject: [PATCH 021/342] N163: implement wave synth I hope this does not break any songs --- src/engine/platform/n163.cpp | 70 ++++++++++++++++++++++++------------ src/engine/platform/n163.h | 4 ++- src/engine/waveSynth.cpp | 9 +++++ src/engine/waveSynth.h | 5 +++ src/gui/guiConst.cpp | 4 +-- src/gui/osc.cpp | 55 +++++++++++++++++++++++++--- 6 files changed, 118 insertions(+), 29 deletions(-) diff --git a/src/engine/platform/n163.cpp b/src/engine/platform/n163.cpp index 7c9188d49..b7234a4ba 100644 --- a/src/engine/platform/n163.cpp +++ b/src/engine/platform/n163.cpp @@ -171,29 +171,43 @@ void DivPlatformN163::acquire(short* bufL, short* bufR, size_t start, size_t len } } -void DivPlatformN163::updateWave(int wave, int pos, int len) { +void DivPlatformN163::updateWave(int ch, int wave, int pos, int len) { len&=0xfc; // 4 nibble boundary - DivWavetable* wt=parent->getWave(wave); - for (int i=0; i=((0x78-(chanMax<<3))<<1)) { // avoid conflict with channel register area - break; - } - unsigned char mask=(addr&1)?0xf0:0x0f; - if (wt->max<1 || wt->len<1) { - rWriteMask(addr>>1,0,mask); - } else { - int data=wt->data[i*wt->len/len]*15/wt->max; - if (data<0) data=0; - if (data>15) data=15; + if (wave<0) { + // load from wave synth + for (int i=0; i=((0x78-(chanMax<<3))<<1)) { // avoid conflict with channel register area + break; + } + unsigned char mask=(addr&1)?0xf0:0x0f; + int data=chan[ch].ws.output[i]; rWriteMask(addr>>1,(addr&1)?(data<<4):(data&0xf),mask); } + } else { + // load from custom + DivWavetable* wt=parent->getWave(wave); + for (int i=0; i=((0x78-(chanMax<<3))<<1)) { // avoid conflict with channel register area + break; + } + unsigned char mask=(addr&1)?0xf0:0x0f; + if (wt->max<1 || wt->len<1) { + rWriteMask(addr>>1,0,mask); + } else { + int data=wt->data[i*wt->len/len]*15/wt->max; + if (data<0) data=0; + if (data>15) data=15; + rWriteMask(addr>>1,(addr&1)?(data<<4):(data&0xf),mask); + } + } } } void DivPlatformN163::updateWaveCh(int ch) { if (ch<=chanMax) { - updateWave(chan[ch].wave,chan[ch].wavePos,chan[ch].waveLen); + updateWave(ch,-1,chan[ch].wavePos,chan[ch].waveLen); if (chan[ch].active && !isMuted[ch]) { chan[ch].volumeChanged=true; } @@ -241,6 +255,7 @@ void DivPlatformN163::tick() { if (chan[i].std.wave.had) { if (chan[i].wave!=chan[i].std.wave.val) { chan[i].wave=chan[i].std.wave.val; + chan[i].ws.changeWave1(chan[i].wave); if (chan[i].waveMode&0x2) { chan[i].waveUpdated=true; } @@ -249,6 +264,7 @@ void DivPlatformN163::tick() { if (chan[i].std.ex1.had) { if (chan[i].waveLen!=(chan[i].std.ex1.val&0xfc)) { chan[i].waveLen=chan[i].std.ex1.val&0xfc; + chan[i].ws.setWidth(chan[i].waveLen); if (chan[i].waveMode&0x2) { chan[i].waveUpdated=true; } @@ -275,7 +291,7 @@ void DivPlatformN163::tick() { if (chan[i].loadWave!=chan[i].std.ex3.val) { chan[i].loadWave=chan[i].std.ex3.val; if (chan[i].loadMode&0x2) { - updateWave(chan[i].loadWave,chan[i].loadPos,chan[i].loadLen&0xfc); + updateWave(i,chan[i].loadWave,chan[i].loadPos,chan[i].loadLen&0xfc); } } } @@ -296,7 +312,7 @@ void DivPlatformN163::tick() { if ((chan[i].loadMode&0x1)!=(chan[i].std.fms.val&0x1)) { // load now chan[i].loadMode=(chan[i].loadMode&~0x1)|(chan[i].std.fms.val&0x1); if (chan[i].loadMode&0x1) { // rising edge - updateWave(chan[i].loadWave,chan[i].loadPos,chan[i].loadLen&0xfc); + updateWave(i,chan[i].loadWave,chan[i].loadPos,chan[i].loadLen&0xfc); } } } @@ -315,6 +331,11 @@ void DivPlatformN163::tick() { } chan[i].waveChanged=false; } + if (chan[i].active) { + if (chan[i].ws.tick()) { + chan[i].waveUpdated=true; + } + } if (chan[i].waveUpdated) { updateWaveCh(i); if (chan[i].active) { @@ -353,11 +374,12 @@ int DivPlatformN163::dispatch(DivCommand c) { DivInstrument* ins=parent->getIns(chan[c.chan].ins); if (chan[c.chan].insChanged) { chan[c.chan].wave=ins->n163.wave; + chan[c.chan].ws.changeWave1(chan[c.chan].wave); chan[c.chan].wavePos=ins->n163.wavePos; chan[c.chan].waveLen=ins->n163.waveLen; chan[c.chan].waveMode=ins->n163.waveMode; chan[c.chan].waveChanged=true; - if (chan[c.chan].waveMode&0x3) { + if (chan[c.chan].waveMode&0x3 || ins->ws.enabled) { chan[c.chan].waveUpdated=true; } chan[c.chan].insChanged=false; @@ -374,6 +396,7 @@ int DivPlatformN163::dispatch(DivCommand c) { chan[c.chan].volumeChanged=true; } chan[c.chan].std.init(ins); + chan[c.chan].ws.init(ins,chan[c.chan].waveLen,15,chan[c.chan].insChanged); break; } case DIV_CMD_NOTE_OFF: @@ -472,7 +495,7 @@ int DivPlatformN163::dispatch(DivCommand c) { case DIV_CMD_N163_WAVE_LOAD: chan[c.chan].loadWave=c.value; if (chan[c.chan].loadMode&0x2) { // load when every waveform changes - updateWave(chan[c.chan].loadWave,chan[c.chan].loadPos,chan[c.chan].loadLen); + updateWave(c.chan,chan[c.chan].loadWave,chan[c.chan].loadPos,chan[c.chan].loadLen); } break; case DIV_CMD_N163_WAVE_LOADPOS: @@ -484,13 +507,13 @@ int DivPlatformN163::dispatch(DivCommand c) { case DIV_CMD_N163_WAVE_LOADMODE: chan[c.chan].loadMode=c.value&0x3; if (chan[c.chan].loadMode&0x1) { // load now - updateWave(chan[c.chan].loadWave,chan[c.chan].loadPos,chan[c.chan].loadLen); + updateWave(c.chan,chan[c.chan].loadWave,chan[c.chan].loadPos,chan[c.chan].loadLen); } break; case DIV_CMD_N163_GLOBAL_WAVE_LOAD: loadWave=c.value; if (loadMode&0x2) { // load when every waveform changes - updateWave(loadWave,loadPos,loadLen); + updateWave(c.chan,loadWave,loadPos,loadLen); } break; case DIV_CMD_N163_GLOBAL_WAVE_LOADPOS: @@ -502,7 +525,7 @@ int DivPlatformN163::dispatch(DivCommand c) { case DIV_CMD_N163_GLOBAL_WAVE_LOADMODE: loadMode=c.value&0x3; if (loadMode&0x3) { // load now - updateWave(loadWave,loadPos,loadLen); + updateWave(c.chan,loadWave,loadPos,loadLen); } break; case DIV_CMD_N163_CHANNEL_LIMIT: @@ -562,6 +585,7 @@ void DivPlatformN163::notifyWaveChange(int wave) { for (int i=0; i<8; i++) { if (chan[i].wave==wave) { if (chan[i].waveMode&0x2) { + chan[i].ws.changeWave1(wave); chan[i].waveUpdated=true; } } @@ -601,6 +625,8 @@ void DivPlatformN163::reset() { while (!writes.empty()) writes.pop(); for (int i=0; i<8; i++) { chan[i]=DivPlatformN163::Channel(); + chan[i].ws.setEngine(parent); + chan[i].ws.init(NULL,32,15,false); } n163.reset(); diff --git a/src/engine/platform/n163.h b/src/engine/platform/n163.h index 70f842adf..2f68d6c9d 100644 --- a/src/engine/platform/n163.h +++ b/src/engine/platform/n163.h @@ -23,6 +23,7 @@ #include "../dispatch.h" #include #include "../macroInt.h" +#include "../waveSynth.h" #include "sound/n163/n163.hpp" class DivPlatformN163: public DivDispatch { @@ -35,6 +36,7 @@ class DivPlatformN163: public DivDispatch { bool active, insChanged, freqChanged, volumeChanged, waveChanged, waveUpdated, keyOn, keyOff, inPorta; signed char vol, outVol, resVol; DivMacroInt std; + DivWaveSynth ws; Channel(): freq(0), baseFreq(0), @@ -79,7 +81,7 @@ class DivPlatformN163: public DivDispatch { n163_core n163; unsigned char regPool[128]; - void updateWave(int wave, int pos, int len); + void updateWave(int ch, int wave, int pos, int len); void updateWaveCh(int ch); friend void putDispatchChan(void*,int,int); diff --git a/src/engine/waveSynth.cpp b/src/engine/waveSynth.cpp index e38ee228e..ce93105d4 100644 --- a/src/engine/waveSynth.cpp +++ b/src/engine/waveSynth.cpp @@ -18,6 +18,7 @@ bool DivWaveSynth::tick() { bool updated=first; first=false; if (!state.enabled) return updated; + if (width<1) return false; if (--divCounter<=0) { // run effect @@ -84,8 +85,15 @@ bool DivWaveSynth::tick() { return updated; } +void DivWaveSynth::setWidth(int val) { + width=val; + if (width<0) width=0; + if (width>256) width=256; +} + void DivWaveSynth::changeWave1(int num) { DivWavetable* w1=e->getWave(num); + if (width<1) return; for (int i=0; imax<1 || w1->len<1) { wave1[i]=0; @@ -103,6 +111,7 @@ void DivWaveSynth::changeWave1(int num) { void DivWaveSynth::changeWave2(int num) { DivWavetable* w2=e->getWave(num); + if (width<1) return; for (int i=0; imax<1 || w2->len<1) { wave2[i]=0; diff --git a/src/engine/waveSynth.h b/src/engine/waveSynth.h index ccd8b23de..ecd6f8a86 100644 --- a/src/engine/waveSynth.h +++ b/src/engine/waveSynth.h @@ -47,6 +47,11 @@ class DivWaveSynth { * @return whether the wave has changed. */ bool tick(); + /** + * set the wave width. + * @param value the width. + */ + void setWidth(int val); /** * change the first wave. * @param num wavetable number. diff --git a/src/gui/guiConst.cpp b/src/gui/guiConst.cpp index 1b292638c..bc5345aa5 100644 --- a/src/gui/guiConst.cpp +++ b/src/gui/guiConst.cpp @@ -385,8 +385,8 @@ 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_REF,"",ImVec4(0.3f,0.3f,0.3f,1.0f)), - D(GUI_COLOR_OSC_GUIDE,"",ImVec4(0.3,0.3f,0.3f,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)), D(GUI_COLOR_VOLMETER_LOW,"",ImVec4(0.2f,0.6f,0.2f,1.0f)), D(GUI_COLOR_VOLMETER_HIGH,"",ImVec4(1.0f,0.9f,0.2f,1.0f)), diff --git a/src/gui/osc.cpp b/src/gui/osc.cpp index e43dee1e0..c7089a991 100644 --- a/src/gui/osc.cpp +++ b/src/gui/osc.cpp @@ -69,9 +69,6 @@ void FurnaceGUI::readOsc() { e->oscReadPos=readPos; } -// TODO: -// - draw reference level -// - draw guidelines void FurnaceGUI::drawOsc() { if (nextWindow==GUI_WINDOW_OSCILLOSCOPE) { oscOpen=true; @@ -115,6 +112,7 @@ void FurnaceGUI::drawOsc() { ImU32 color=ImGui::GetColorU32(isClipping?uiColors[GUI_COLOR_OSC_WAVE_PEAK]:uiColors[GUI_COLOR_OSC_WAVE]); ImU32 borderColor=ImGui::GetColorU32(uiColors[GUI_COLOR_OSC_BORDER]); ImU32 refColor=ImGui::GetColorU32(uiColors[GUI_COLOR_OSC_REF]); + ImU32 guideColor=ImGui::GetColorU32(uiColors[GUI_COLOR_OSC_GUIDE]); ImGui::ItemSize(size,style.FramePadding.y); if (ImGui::ItemAdd(rect,ImGui::GetID("wsDisplay"))) { // https://github.com/ocornut/imgui/issues/3710 @@ -162,11 +160,60 @@ void FurnaceGUI::drawOsc() { dl->AddLine( ImLerp(rect.Min,rect.Max,ImVec2(0.0f,0.5f)), - ImLerp(rect.Min,rect.Max,ImVec2(0.0f,0.5f)), + ImLerp(rect.Min,rect.Max,ImVec2(1.0f,0.5f)), refColor, dpiScale ); + dl->AddLine( + ImLerp(rect.Min,rect.Max,ImVec2(0.48f,0.125f)), + ImLerp(rect.Min,rect.Max,ImVec2(0.52f,0.125f)), + guideColor, + dpiScale + ); + + dl->AddLine( + ImLerp(rect.Min,rect.Max,ImVec2(0.47f,0.25f)), + ImLerp(rect.Min,rect.Max,ImVec2(0.53f,0.25f)), + guideColor, + dpiScale + ); + + dl->AddLine( + ImLerp(rect.Min,rect.Max,ImVec2(0.45f,0.375f)), + ImLerp(rect.Min,rect.Max,ImVec2(0.55f,0.375f)), + guideColor, + dpiScale + ); + + dl->AddLine( + ImLerp(rect.Min,rect.Max,ImVec2(0.45f,0.625f)), + ImLerp(rect.Min,rect.Max,ImVec2(0.55f,0.625f)), + guideColor, + dpiScale + ); + + dl->AddLine( + ImLerp(rect.Min,rect.Max,ImVec2(0.47f,0.75f)), + ImLerp(rect.Min,rect.Max,ImVec2(0.53f,0.75f)), + guideColor, + dpiScale + ); + + dl->AddLine( + ImLerp(rect.Min,rect.Max,ImVec2(0.48f,0.875f)), + ImLerp(rect.Min,rect.Max,ImVec2(0.52f,0.875f)), + guideColor, + dpiScale + ); + + dl->AddLine( + ImLerp(rect.Min,rect.Max,ImVec2(0.5f,0.08f)), + ImLerp(rect.Min,rect.Max,ImVec2(0.5f,0.92f)), + guideColor, + dpiScale + ); + for (size_t i=0; i<512; i++) { float x=(float)i/512.0f; float y=oscValues[i]*oscZoom; From 014e86d3d19db8dff729d23881cd05650767587c Mon Sep 17 00:00:00 2001 From: tildearrow Date: Tue, 12 Apr 2022 00:18:29 -0500 Subject: [PATCH 022/342] add missing license headers --- src/engine/waveSynth.cpp | 21 ++++++++++++++++++++- src/gui/log.cpp | 19 +++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/src/engine/waveSynth.cpp b/src/engine/waveSynth.cpp index ce93105d4..ae42a44e3 100644 --- a/src/engine/waveSynth.cpp +++ b/src/engine/waveSynth.cpp @@ -1,10 +1,29 @@ +/** + * Furnace Tracker - multi-system chiptune tracker + * Copyright (C) 2021-2022 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 "waveSynth.h" #include "engine.h" #include "instrument.h" // TODO: // - add missing license header -// - implement waveSynth on Amiga and N163 +// - implement waveSynth on Amiga bool DivWaveSynth::activeChanged() { if (activeChangedB) { diff --git a/src/gui/log.cpp b/src/gui/log.cpp index c6c4e2a86..7dc6bf8d8 100644 --- a/src/gui/log.cpp +++ b/src/gui/log.cpp @@ -1,3 +1,22 @@ +/** + * Furnace Tracker - multi-system chiptune tracker + * Copyright (C) 2021-2022 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 "../ta-log.h" #include From 24209c7853949bab00776f14549698ab39a63158 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Tue, 12 Apr 2022 01:19:00 -0500 Subject: [PATCH 023/342] dev82 - amiga wavetables closes #16 (yay!) --- papers/doc/7-systems/amiga.md | 6 +- papers/format.md | 8 ++- src/engine/engine.h | 4 +- src/engine/instrument.cpp | 13 +++- src/engine/instrument.h | 6 +- src/engine/platform/amiga.cpp | 111 ++++++++++++++++++++++------------ src/engine/platform/amiga.h | 4 +- src/gui/insEdit.cpp | 14 ++++- 8 files changed, 119 insertions(+), 47 deletions(-) diff --git a/papers/doc/7-systems/amiga.md b/papers/doc/7-systems/amiga.md index 97c1151f6..cbc73f7ae 100644 --- a/papers/doc/7-systems/amiga.md +++ b/papers/doc/7-systems/amiga.md @@ -6,8 +6,10 @@ in this very computer music trackers were born... # effects -- `10xx`: toggle low-pass filter. `0` turns it off and `1` turns it on. +- `10xx`: change wave. + - only works when "Mode" is set to "Wavetable" in the instrument. - `11xx`: toggle amplitude modulation with the next channel. - does not work on the last channel. - `12xx`: toggle period (frequency) modulation with the next channel. - - does not work on the last channel. \ No newline at end of file + - does not work on the last channel. +- `13xx`: toggle low-pass filter. `0` turns it off and `1` turns it on. \ No newline at end of file diff --git a/papers/format.md b/papers/format.md index ce1685c63..87e53ebfe 100644 --- a/papers/format.md +++ b/papers/format.md @@ -29,6 +29,8 @@ furthermore, an `or reserved` indicates this field is always present, but is res the format versions are: +- 82: Furnace dev82 +- 81: Furnace dev81 - 80: Furnace dev80 - 79: Furnace dev79 - 78: Furnace dev78 @@ -342,7 +344,11 @@ size | description 1 | filter macro is absolute --- | **Amiga instrument data** 2 | initial sample - 14 | reserved + 1 | mode (>=82) or reserved + | - 0: sample + | - 1: wavetable + 1 | wavetable length (-1) (>=82) or reserved + 12 | reserved --- | **standard instrument data** 4 | volume macro length 4 | arp macro length diff --git a/src/engine/engine.h b/src/engine/engine.h index 89f6ff732..32e89fd46 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -42,8 +42,8 @@ #define BUSY_BEGIN_SOFT softLocked=true; isBusy.lock(); #define BUSY_END isBusy.unlock(); softLocked=false; -#define DIV_VERSION "dev81" -#define DIV_ENGINE_VERSION 81 +#define DIV_VERSION "dev82" +#define DIV_ENGINE_VERSION 82 // for imports #define DIV_VERSION_MOD 0xff01 diff --git a/src/engine/instrument.cpp b/src/engine/instrument.cpp index 7dbf5b3fd..460fee9e2 100644 --- a/src/engine/instrument.cpp +++ b/src/engine/instrument.cpp @@ -106,7 +106,9 @@ void DivInstrument::putInsData(SafeWriter* w) { // Amiga w->writeS(amiga.initSample); - for (int j=0; j<14; j++) { // reserved + w->writeC(amiga.useWave); + w->writeC(amiga.waveLen); + for (int j=0; j<12; j++) { // reserved w->writeC(0); } @@ -571,8 +573,15 @@ DivDataErrors DivInstrument::readInsData(SafeReader& reader, short version) { // Amiga amiga.initSample=reader.readS(); + if (version>=82) { + amiga.useWave=reader.readC(); + amiga.waveLen=(unsigned char)reader.readC(); + } else { + reader.readC(); + reader.readC(); + } // reserved - for (int k=0; k<14; k++) reader.readC(); + for (int k=0; k<12; k++) reader.readC(); // standard std.volMacro.len=reader.readI(); diff --git a/src/engine/instrument.h b/src/engine/instrument.h index 605241c71..80df6ab49 100644 --- a/src/engine/instrument.h +++ b/src/engine/instrument.h @@ -293,12 +293,16 @@ struct DivInstrumentC64 { struct DivInstrumentAmiga { short initSample; bool useNoteMap; + bool useWave; + unsigned char waveLen; int noteFreq[120]; short noteMap[120]; DivInstrumentAmiga(): initSample(0), - useNoteMap(false) { + useNoteMap(false), + useWave(false), + waveLen(31) { memset(noteMap,-1,120*sizeof(short)); memset(noteFreq,0,120*sizeof(int)); } diff --git a/src/engine/platform/amiga.cpp b/src/engine/platform/amiga.cpp index 458c9329d..e7b57457e 100644 --- a/src/engine/platform/amiga.cpp +++ b/src/engine/platform/amiga.cpp @@ -79,35 +79,45 @@ const char* DivPlatformAmiga::getEffectName(unsigned char effect) { return NULL; } +#define writeAudDat(x) \ + chan[i].audDat=x; \ + if (i<3 && chan[i].useV) { \ + chan[i+1].outVol=(unsigned char)chan[i].audDat^0x80; \ + if (chan[i+1].outVol>64) chan[i+1].outVol=64; \ + } \ + if (i<3 && chan[i].useP) { \ + chan[i+1].freq=(unsigned char)chan[i].audDat^0x80; \ + if (chan[i+1].freq=0 && chan[i].samplesong.sampleLen) { + if (chan[i].useWave || (chan[i].sample>=0 && chan[i].samplesong.sampleLen)) { chan[i].audSub-=AMIGA_DIVIDER; if (chan[i].audSub<0) { - DivSample* s=parent->getSample(chan[i].sample); - if (s->samples>0) { - chan[i].audDat=s->data8[chan[i].audPos++]; - if (i<3 && chan[i].useV) { - chan[i+1].outVol=(unsigned char)chan[i].audDat^0x80; - if (chan[i+1].outVol>64) chan[i+1].outVol=64; - } - if (i<3 && chan[i].useP) { - chan[i+1].freq=(unsigned char)chan[i].audDat^0x80; - if (chan[i+1].freq=s->samples || chan[i].audPos>=131071) { - if (s->loopStart>=0 && s->loopStart<(int)s->samples) { - chan[i].audPos=s->loopStart; - } else { - chan[i].sample=-1; - } + if (chan[i].useWave) { + writeAudDat(chan[i].ws.output[chan[i].audPos++]^0x80); + if (chan[i].audPos>=(unsigned int)(chan[i].audLen<<1)) { + chan[i].audPos=0; } } else { - chan[i].sample=-1; + DivSample* s=parent->getSample(chan[i].sample); + if (s->samples>0) { + writeAudDat(s->data8[chan[i].audPos++]); + if (chan[i].audPos>=s->samples || chan[i].audPos>=131071) { + if (s->loopStart>=0 && s->loopStart<(int)s->samples) { + chan[i].audPos=s->loopStart; + } else { + chan[i].sample=-1; + } + } + } else { + chan[i].sample=-1; + } } /*if (chan[i].freq<124) { if (++chan[i].busClock>=512) { @@ -151,7 +161,7 @@ void DivPlatformAmiga::tick() { chan[i].outVol=((chan[i].vol%65)*MIN(64,chan[i].std.vol.val))>>6; } double off=1.0; - if (chan[i].sample>=0 && chan[i].samplesong.sampleLen) { + if (!chan[i].useWave && chan[i].sample>=0 && chan[i].samplesong.sampleLen) { DivSample* s=parent->getSample(chan[i].sample); if (s->centerRate<1) { off=1.0; @@ -174,21 +184,21 @@ void DivPlatformAmiga::tick() { chan[i].freqChanged=true; } } - if (chan[i].std.wave.had) { - if (chan[i].wave!=chan[i].std.wave.val) { + if (chan[i].useWave && chan[i].std.wave.had) { + if (chan[i].wave!=chan[i].std.wave.val || chan[i].ws.activeChanged()) { chan[i].wave=chan[i].std.wave.val; + chan[i].ws.changeWave1(chan[i].wave); if (!chan[i].keyOff) chan[i].keyOn=true; } } + if (chan[i].useWave && chan[i].active) { + chan[i].ws.tick(); + } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { //DivInstrument* ins=parent->getIns(chan[i].ins); chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true); if (chan[i].freq>4095) chan[i].freq=4095; - if (chan[i].note>0x5d) chan[i].freq=0x01; if (chan[i].keyOn) { - if (chan[i].wave<0) { - chan[i].wave=0; - } } if (chan[i].keyOff) { } @@ -203,20 +213,33 @@ int DivPlatformAmiga::dispatch(DivCommand c) { switch (c.cmd) { case DIV_CMD_NOTE_ON: { DivInstrument* ins=parent->getIns(chan[c.chan].ins); - chan[c.chan].sample=ins->amiga.initSample; double off=1.0; - if (chan[c.chan].sample>=0 && chan[c.chan].samplesong.sampleLen) { - DivSample* s=parent->getSample(chan[c.chan].sample); - if (s->centerRate<1) { - off=1.0; - } else { - off=8363.0/(double)s->centerRate; + if (ins->amiga.useWave) { + chan[c.chan].useWave=true; + chan[c.chan].audLen=(ins->amiga.waveLen+1)>>1; + if (chan[c.chan].insChanged) { + if (chan[c.chan].wave<0) { + chan[c.chan].wave=0; + chan[c.chan].ws.setWidth(chan[c.chan].audLen<<1); + chan[c.chan].ws.changeWave1(chan[c.chan].wave); + } + } + } else { + chan[c.chan].sample=ins->amiga.initSample; + chan[c.chan].useWave=false; + if (chan[c.chan].sample>=0 && chan[c.chan].samplesong.sampleLen) { + DivSample* s=parent->getSample(chan[c.chan].sample); + if (s->centerRate<1) { + off=1.0; + } else { + off=8363.0/(double)s->centerRate; + } } } if (c.value!=DIV_NOTE_NULL) { chan[c.chan].baseFreq=round(off*NOTE_PERIODIC_NOROUND(c.value)); } - if (chan[c.chan].sample<0 || chan[c.chan].sample>=parent->song.sampleLen) { + if (chan[c.chan].useWave || chan[c.chan].sample<0 || chan[c.chan].sample>=parent->song.sampleLen) { chan[c.chan].sample=-1; } if (chan[c.chan].setPos) { @@ -232,6 +255,10 @@ int DivPlatformAmiga::dispatch(DivCommand c) { chan[c.chan].active=true; chan[c.chan].keyOn=true; chan[c.chan].std.init(ins); + if (chan[c.chan].useWave) { + chan[c.chan].ws.init(ins,chan[c.chan].audLen<<1,255,chan[c.chan].insChanged); + } + chan[c.chan].insChanged=false; break; } case DIV_CMD_NOTE_OFF: @@ -247,6 +274,7 @@ int DivPlatformAmiga::dispatch(DivCommand c) { case DIV_CMD_INSTRUMENT: if (chan[c.chan].ins!=c.value || c.value2==1) { chan[c.chan].ins=c.value; + chan[c.chan].insChanged=true; } break; case DIV_CMD_VOLUME: @@ -268,14 +296,16 @@ int DivPlatformAmiga::dispatch(DivCommand c) { chan[c.chan].freqChanged=true; break; case DIV_CMD_WAVE: + if (!chan[c.chan].useWave) break; chan[c.chan].wave=c.value; chan[c.chan].keyOn=true; + chan[c.chan].ws.changeWave1(chan[c.chan].wave); break; case DIV_CMD_NOTE_PORTA: { DivInstrument* ins=parent->getIns(chan[c.chan].ins); chan[c.chan].sample=ins->amiga.initSample; double off=1.0; - if (chan[c.chan].sample>=0 && chan[c.chan].samplesong.sampleLen) { + if (!chan[c.chan].useWave && chan[c.chan].sample>=0 && chan[c.chan].samplesong.sampleLen) { DivSample* s=parent->getSample(chan[c.chan].sample); if (s->centerRate<1) { off=1.0; @@ -307,7 +337,7 @@ int DivPlatformAmiga::dispatch(DivCommand c) { } case DIV_CMD_LEGATO: { double off=1.0; - if (chan[c.chan].sample>=0 && chan[c.chan].samplesong.sampleLen) { + if (!chan[c.chan].useWave && chan[c.chan].sample>=0 && chan[c.chan].samplesong.sampleLen) { DivSample* s=parent->getSample(chan[c.chan].sample); if (s->centerRate<1) { off=1.0; @@ -327,6 +357,7 @@ int DivPlatformAmiga::dispatch(DivCommand c) { chan[c.chan].inPorta=c.value; break; case DIV_CMD_SAMPLE_POS: + if (chan[c.chan].useWave) break; chan[c.chan].audPos=c.value; chan[c.chan].setPos=true; break; @@ -373,6 +404,8 @@ void* DivPlatformAmiga::getChanState(int ch) { void DivPlatformAmiga::reset() { for (int i=0; i<4; i++) { chan[i]=DivPlatformAmiga::Channel(); + chan[i].ws.setEngine(parent); + chan[i].ws.init(NULL,32,255); filter[0][i]=0; filter[1][i]=0; } @@ -397,7 +430,11 @@ void DivPlatformAmiga::notifyInsChange(int ins) { } void DivPlatformAmiga::notifyWaveChange(int wave) { - // TODO when wavetables are added + for (int i=0; i<4; i++) { + if (chan[i].useWave && chan[i].wave==wave) { + chan[i].ws.changeWave1(wave); + } + } } void DivPlatformAmiga::notifyInsDeletion(void* ins) { diff --git a/src/engine/platform/amiga.h b/src/engine/platform/amiga.h index b5f13701d..8fbe8fec8 100644 --- a/src/engine/platform/amiga.h +++ b/src/engine/platform/amiga.h @@ -23,6 +23,7 @@ #include "../dispatch.h" #include #include "../macroInt.h" +#include "../waveSynth.h" class DivPlatformAmiga: public DivDispatch { struct Channel { @@ -39,6 +40,7 @@ class DivPlatformAmiga: public DivDispatch { bool active, insChanged, freqChanged, keyOn, keyOff, inPorta, useWave, setPos, useV, useP; signed char vol, outVol; DivMacroInt std; + DivWaveSynth ws; Channel(): freq(0), baseFreq(0), @@ -49,7 +51,7 @@ class DivPlatformAmiga: public DivDispatch { audSub(0), audDat(0), sample(-1), - wave(0), + wave(-1), ins(-1), busClock(0), note(0), diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index e9e69084f..4380764de 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -2264,6 +2264,17 @@ void FurnaceGUI::drawInsEdit() { } ImGui::EndCombo(); } + P(ImGui::Checkbox("Use wavetable (Amiga only)",&ins->amiga.useWave)); + if (ins->amiga.useWave) { + int len=ins->amiga.waveLen+1; + if (ImGui::InputInt("Width",&len,2,16)) { + if (len<2) len=2; + if (len>256) len=256; + ins->amiga.waveLen=(len&(~1))-1; + PARAMETER + } + } + ImGui::BeginDisabled(ins->amiga.useWave); P(ImGui::Checkbox("Use sample map (does not work yet!)",&ins->amiga.useNoteMap)); if (ins->amiga.useNoteMap) { if (ImGui::BeginTable("NoteMap",3,ImGuiTableFlags_ScrollY|ImGuiTableFlags_Borders|ImGuiTableFlags_SizingStretchSame)) { @@ -2317,6 +2328,7 @@ void FurnaceGUI::drawInsEdit() { ImGui::EndTable(); } } + ImGui::EndDisabled(); ImGui::EndTabItem(); } if (ins->type==DIV_INS_N163) if (ImGui::BeginTabItem("Namco 163")) { @@ -2383,7 +2395,7 @@ void FurnaceGUI::drawInsEdit() { ImGui::EndTabItem(); } if (ins->type==DIV_INS_GB || - ins->type==DIV_INS_AMIGA || + (ins->type==DIV_INS_AMIGA && ins->amiga.useWave) || ins->type==DIV_INS_X1_010 || ins->type==DIV_INS_N163 || ins->type==DIV_INS_FDS || From 463090717bd78240e24cfa52e8b7958d2fd7d646 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Tue, 12 Apr 2022 01:26:14 -0500 Subject: [PATCH 024/342] Amiga: what the hell? --- src/engine/platform/amiga.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/engine/platform/amiga.cpp b/src/engine/platform/amiga.cpp index e7b57457e..3da16eef4 100644 --- a/src/engine/platform/amiga.cpp +++ b/src/engine/platform/amiga.cpp @@ -96,6 +96,7 @@ void DivPlatformAmiga::acquire(short* bufL, short* bufR, size_t start, size_t le outL=0; outR=0; for (int i=0; i<4; i++) { + if (!chan[i].active) continue; if (chan[i].useWave || (chan[i].sample>=0 && chan[i].samplesong.sampleLen)) { chan[i].audSub-=AMIGA_DIVIDER; if (chan[i].audSub<0) { @@ -345,7 +346,7 @@ int DivPlatformAmiga::dispatch(DivCommand c) { off=8363.0/(double)s->centerRate; } } - chan[c.chan].baseFreq=round(off*NOTE_PERIODIC_NOROUND(c.value+((chan[c.chan].std.arp.will && !chan[c.chan].std.arp.mode)?(chan[c.chan].std.arp.val-12):(0)))); + chan[c.chan].baseFreq=round(off*NOTE_PERIODIC_NOROUND(c.value+((chan[c.chan].std.arp.will && !chan[c.chan].std.arp.mode)?(chan[c.chan].std.arp.val):(0)))); chan[c.chan].freqChanged=true; chan[c.chan].note=c.value; break; From 9baa26125346a1366f5f216e0a5ed51aea4d9d0a Mon Sep 17 00:00:00 2001 From: tildearrow Date: Tue, 12 Apr 2022 01:51:15 -0500 Subject: [PATCH 025/342] GUI: reduce initial oscilloscope latency --- src/gui/osc.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/gui/osc.cpp b/src/gui/osc.cpp index c7089a991..0119232d4 100644 --- a/src/gui/osc.cpp +++ b/src/gui/osc.cpp @@ -19,18 +19,18 @@ #include "gui.h" #include "imgui_internal.h" -#include -#include // TODO: // - potentially move oscilloscope seek position to the end, and read the last samples // - this allows for setting up the window size -// - reduce initial latency (it's too high) void FurnaceGUI::readOsc() { int writePos=e->oscWritePos; int readPos=e->oscReadPos; int avail=0; int total=0; + if (firstFrame) { + readPos=writePos; + } if (writePos>=readPos) { avail=writePos-readPos; } else { From 3d94aef14baf2bda9d5c010c35a0b3d0f5ecb164 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Tue, 12 Apr 2022 01:51:45 -0500 Subject: [PATCH 026/342] WaveSynth: remove to-do --- src/engine/waveSynth.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/engine/waveSynth.cpp b/src/engine/waveSynth.cpp index ae42a44e3..2b9946ee2 100644 --- a/src/engine/waveSynth.cpp +++ b/src/engine/waveSynth.cpp @@ -21,10 +21,6 @@ #include "engine.h" #include "instrument.h" -// TODO: -// - add missing license header -// - implement waveSynth on Amiga - bool DivWaveSynth::activeChanged() { if (activeChangedB) { activeChangedB=false; From 455cccdef0bea4b5a97dd19987453be0f26df4cd Mon Sep 17 00:00:00 2001 From: tildearrow Date: Tue, 12 Apr 2022 02:15:12 -0500 Subject: [PATCH 027/342] GUI: sample improvements - resample change C-4 note - add duplicate option --- src/engine/sample.cpp | 1 + src/gui/dataList.cpp | 4 ++++ src/gui/doAction.cpp | 23 +++++++++++++++++++++++ src/gui/sampleEdit.cpp | 2 -- 4 files changed, 28 insertions(+), 2 deletions(-) diff --git a/src/engine/sample.cpp b/src/engine/sample.cpp index 93e3ece6d..bd34daa5b 100644 --- a/src/engine/sample.cpp +++ b/src/engine/sample.cpp @@ -323,6 +323,7 @@ bool DivSample::insert(unsigned int pos, unsigned int length) { #define RESAMPLE_END \ if (loopStart>=0) loopStart=(double)loopStart*(r/(double)rate); \ + centerRate=(int)((double)centerRate*(r/(double)rate)); \ rate=r; \ samples=finalCount; \ if (depth==16) { \ diff --git a/src/gui/dataList.cpp b/src/gui/dataList.cpp index 9557cc57d..8a02c40e3 100644 --- a/src/gui/dataList.cpp +++ b/src/gui/dataList.cpp @@ -290,6 +290,10 @@ void FurnaceGUI::drawSampleList() { doAction(GUI_ACTION_SAMPLE_LIST_ADD); } ImGui::SameLine(); + if (ImGui::Button(ICON_FA_FILES_O "##SampleClone")) { + doAction(GUI_ACTION_SAMPLE_LIST_DUPLICATE); + } + ImGui::SameLine(); if (ImGui::Button(ICON_FA_FOLDER_OPEN "##SampleLoad")) { doAction(GUI_ACTION_SAMPLE_LIST_OPEN); } diff --git a/src/gui/doAction.cpp b/src/gui/doAction.cpp index a1b974e36..26fbd39f2 100644 --- a/src/gui/doAction.cpp +++ b/src/gui/doAction.cpp @@ -567,6 +567,29 @@ void FurnaceGUI::doAction(int what) { curSample=e->addSample(); MARK_MODIFIED; break; + case GUI_ACTION_SAMPLE_LIST_DUPLICATE: + if (curSample>=0 && curSample<(int)e->song.sample.size()) { + DivSample* prevSample=e->getSample(curSample); + curSample=e->addSample(); + e->lockEngine([this,prevSample]() { + DivSample* sample=e->getSample(curSample); + if (sample!=NULL) { + sample->rate=prevSample->rate; + sample->centerRate=prevSample->centerRate; + sample->name=prevSample->name; + sample->loopStart=prevSample->loopStart; + sample->depth=prevSample->depth; + if (sample->init(prevSample->samples)) { + if (prevSample->getCurBuf()!=NULL) { + memcpy(sample->getCurBuf(),prevSample->getCurBuf(),prevSample->getCurBufLen()); + } + } + } + e->renderSamples(); + }); + MARK_MODIFIED; + } + break; case GUI_ACTION_SAMPLE_LIST_OPEN: openFileDialog(GUI_FILE_SAMPLE_OPEN); break; diff --git a/src/gui/sampleEdit.cpp b/src/gui/sampleEdit.cpp index 34cb23d5f..4ff8761eb 100644 --- a/src/gui/sampleEdit.cpp +++ b/src/gui/sampleEdit.cpp @@ -29,11 +29,9 @@ #include "util.h" // TODO: -// - resample should change C-4 note // - clicking on waveform should give this window focus // - add "create instrument using this sample" option // - .dmc loading -// - duplicate sample void FurnaceGUI::drawSampleEdit() { if (nextWindow==GUI_WINDOW_SAMPLE_EDIT) { sampleEditOpen=true; From ffef40c098e6c9db1fd81ebf4596e99b548eb304 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Tue, 12 Apr 2022 02:24:42 -0500 Subject: [PATCH 028/342] GUI: clampSetting() for sample layout --- src/gui/settings.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index 9c64f404e..64900282e 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -1572,6 +1572,7 @@ void FurnaceGUI::syncSettings() { clampSetting(settings.titleBarSys,0,1); clampSetting(settings.frameBorders,0,1); clampSetting(settings.effectDeletionAltersValue,0,1); + clampSetting(settings.sampleLayout,0,1); // keybinds for (int i=0; i Date: Tue, 12 Apr 2022 02:39:03 -0500 Subject: [PATCH 029/342] GUI: deduplicate a lot of code --- src/gui/sampleEdit.cpp | 2202 +++++++++++++++++----------------------- 1 file changed, 949 insertions(+), 1253 deletions(-) diff --git a/src/gui/sampleEdit.cpp b/src/gui/sampleEdit.cpp index 569200c83..cf7f01764 100644 --- a/src/gui/sampleEdit.cpp +++ b/src/gui/sampleEdit.cpp @@ -42,7 +42,7 @@ void FurnaceGUI::drawSampleEdit() { if (ImGui::Begin("Sample Editor",&sampleEditOpen,settings.allowEditDocking?0:ImGuiWindowFlags_NoDocking)) { if (curSample<0 || curSample>=(int)e->song.sample.size()) { ImGui::Text("no sample selected"); - } else if (!settings.sampleLayout) { + } else { DivSample* sample=e->song.sample[curSample]; String sampleType="Invalid"; if (sample->depth<17) { @@ -50,1342 +50,1038 @@ void FurnaceGUI::drawSampleEdit() { sampleType=sampleDepths[sample->depth]; } } - ImGui::Text("Name"); - ImGui::SameLine(); - ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); - if (ImGui::InputText("##SampleName",&sample->name)) { - MARK_MODIFIED; - } - - if (ImGui::BeginTable("SampleProps",4,ImGuiTableFlags_SizingStretchSame)) { - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - ImGui::Text("Type"); + if (!settings.sampleLayout) { + ImGui::Text("Name"); ImGui::SameLine(); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); - if (ImGui::BeginCombo("##SampleType",sampleType.c_str())) { - for (int i=0; i<17; i++) { - if (sampleDepths[i]==NULL) continue; - if (ImGui::Selectable(sampleDepths[i])) { - sample->prepareUndo(true); - sample->depth=i; - e->renderSamplesP(); - updateSampleTex=true; - MARK_MODIFIED; - } - } - ImGui::EndCombo(); + if (ImGui::InputText("##SampleName",&sample->name)) { + MARK_MODIFIED; } - ImGui::TableNextColumn(); - ImGui::Text("Rate (Hz)"); - ImGui::SameLine(); - ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); - if (ImGui::InputInt("##SampleRate",&sample->rate,10,200)) { MARK_MODIFIED - if (sample->rate<100) sample->rate=100; - if (sample->rate>96000) sample->rate=96000; - } - - ImGui::TableNextColumn(); - ImGui::Text("C-4 (Hz)"); - ImGui::SameLine(); - ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); - if (ImGui::InputInt("##SampleCenter",&sample->centerRate,10,200)) { MARK_MODIFIED - if (sample->centerRate<100) sample->centerRate=100; - if (sample->centerRate>65535) sample->centerRate=65535; - } - - ImGui::TableNextColumn(); - bool doLoop=(sample->loopStart>=0); - if (ImGui::Checkbox("Loop",&doLoop)) { MARK_MODIFIED - if (doLoop) { - sample->loopStart=0; - } else { - sample->loopStart=-1; - } - updateSampleTex=true; - } - if (doLoop) { + if (ImGui::BeginTable("SampleProps",4,ImGuiTableFlags_SizingStretchSame)) { + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::Text("Type"); ImGui::SameLine(); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); - if (ImGui::InputInt("##LoopPosition",&sample->loopStart,1,10)) { MARK_MODIFIED - if (sample->loopStart<0 || sample->loopStart>=(int)sample->samples) { + if (ImGui::BeginCombo("##SampleType",sampleType.c_str())) { + for (int i=0; i<17; i++) { + if (sampleDepths[i]==NULL) continue; + if (ImGui::Selectable(sampleDepths[i])) { + sample->prepareUndo(true); + sample->depth=i; + e->renderSamplesP(); + updateSampleTex=true; + MARK_MODIFIED; + } + } + ImGui::EndCombo(); + } + + ImGui::TableNextColumn(); + ImGui::Text("Rate (Hz)"); + ImGui::SameLine(); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + if (ImGui::InputInt("##SampleRate",&sample->rate,10,200)) { MARK_MODIFIED + if (sample->rate<100) sample->rate=100; + if (sample->rate>96000) sample->rate=96000; + } + + ImGui::TableNextColumn(); + ImGui::Text("C-4 (Hz)"); + ImGui::SameLine(); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + if (ImGui::InputInt("##SampleCenter",&sample->centerRate,10,200)) { MARK_MODIFIED + if (sample->centerRate<100) sample->centerRate=100; + if (sample->centerRate>65535) sample->centerRate=65535; + } + + ImGui::TableNextColumn(); + bool doLoop=(sample->loopStart>=0); + if (ImGui::Checkbox("Loop",&doLoop)) { MARK_MODIFIED + if (doLoop) { sample->loopStart=0; + } else { + sample->loopStart=-1; } updateSampleTex=true; } - } - ImGui::EndTable(); - } - - /* - if (ImGui::Button("Apply")) { - e->renderSamplesP(); - } - ImGui::SameLine(); - */ - ImGui::Separator(); - - ImGui::BeginDisabled(sample->depth!=8 && sample->depth!=16); - - ImGui::PushStyleColor(ImGuiCol_Button,TOGGLE_COLOR(!sampleDragMode)); - if (ImGui::Button(ICON_FA_I_CURSOR "##SSelect")) { - sampleDragMode=false; - } - ImGui::PopStyleColor(); - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("Edit mode: Select"); - } - ImGui::SameLine(); - ImGui::PushStyleColor(ImGuiCol_Button,TOGGLE_COLOR(sampleDragMode)); - if (ImGui::Button(ICON_FA_PENCIL "##SDraw")) { - sampleDragMode=true; - } - ImGui::PopStyleColor(); - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("Edit mode: Draw"); - } - ImGui::SameLine(); - ImGui::Dummy(ImVec2(4.0*dpiScale,dpiScale)); - ImGui::SameLine(); - ImGui::Button(ICON_FA_ARROWS_H "##SResize"); - if (ImGui::IsItemClicked()) { - resizeSize=sample->samples; - } - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("Resize"); - } - if (openSampleResizeOpt) { - openSampleResizeOpt=false; - ImGui::OpenPopup("SResizeOpt"); - } - if (ImGui::BeginPopupContextItem("SResizeOpt",ImGuiPopupFlags_MouseButtonLeft)) { - if (ImGui::InputInt("Samples",&resizeSize,1,64)) { - if (resizeSize<0) resizeSize=0; - if (resizeSize>16777215) resizeSize=16777215; - } - if (ImGui::Button("Resize")) { - sample->prepareUndo(true); - e->lockEngine([this,sample]() { - if (!sample->resize(resizeSize)) { - showError("couldn't resize! make sure your sample is 8 or 16-bit."); + if (doLoop) { + ImGui::SameLine(); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + if (ImGui::InputInt("##LoopPosition",&sample->loopStart,1,10)) { MARK_MODIFIED + if (sample->loopStart<0 || sample->loopStart>=(int)sample->samples) { + sample->loopStart=0; + } + updateSampleTex=true; } - e->renderSamples(); - }); - updateSampleTex=true; - sampleSelStart=-1; - sampleSelEnd=-1; - MARK_MODIFIED; - ImGui::CloseCurrentPopup(); + } + ImGui::EndTable(); } - ImGui::EndPopup(); - } else { - resizeSize=sample->samples; - } - ImGui::SameLine(); - ImGui::Button(ICON_FA_EXPAND "##SResample"); - if (ImGui::IsItemClicked()) { - resampleTarget=sample->rate; - } - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("Resample"); - } - if (openSampleResampleOpt) { - openSampleResampleOpt=false; - ImGui::OpenPopup("SResampleOpt"); - } - if (ImGui::BeginPopupContextItem("SResampleOpt",ImGuiPopupFlags_MouseButtonLeft)) { - ImGui::Text("Rate"); - if (ImGui::InputDouble("##SRRate",&resampleTarget,1.0,50.0,"%g")) { - if (resampleTarget<0) resampleTarget=0; - if (resampleTarget>96000) resampleTarget=96000; + + /* + if (ImGui::Button("Apply")) { + e->renderSamplesP(); } ImGui::SameLine(); - if (ImGui::Button("0.5x")) { - resampleTarget*=0.5; + */ + ImGui::Separator(); + + ImGui::BeginDisabled(sample->depth!=8 && sample->depth!=16); + + ImGui::PushStyleColor(ImGuiCol_Button,TOGGLE_COLOR(!sampleDragMode)); + if (ImGui::Button(ICON_FA_I_CURSOR "##SSelect")) { + sampleDragMode=false; + } + ImGui::PopStyleColor(); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Edit mode: Select"); } ImGui::SameLine(); - if (ImGui::Button("==")) { + ImGui::PushStyleColor(ImGuiCol_Button,TOGGLE_COLOR(sampleDragMode)); + if (ImGui::Button(ICON_FA_PENCIL "##SDraw")) { + sampleDragMode=true; + } + ImGui::PopStyleColor(); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Edit mode: Draw"); + } + ImGui::SameLine(); + ImGui::Dummy(ImVec2(4.0*dpiScale,dpiScale)); + ImGui::SameLine(); + ImGui::Button(ICON_FA_ARROWS_H "##SResize"); + if (ImGui::IsItemClicked()) { + resizeSize=sample->samples; + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Resize"); + } + if (openSampleResizeOpt) { + openSampleResizeOpt=false; + ImGui::OpenPopup("SResizeOpt"); + } + if (ImGui::BeginPopupContextItem("SResizeOpt",ImGuiPopupFlags_MouseButtonLeft)) { + if (ImGui::InputInt("Samples",&resizeSize,1,64)) { + if (resizeSize<0) resizeSize=0; + if (resizeSize>16777215) resizeSize=16777215; + } + if (ImGui::Button("Resize")) { + sample->prepareUndo(true); + e->lockEngine([this,sample]() { + if (!sample->resize(resizeSize)) { + showError("couldn't resize! make sure your sample is 8 or 16-bit."); + } + e->renderSamples(); + }); + updateSampleTex=true; + sampleSelStart=-1; + sampleSelEnd=-1; + MARK_MODIFIED; + ImGui::CloseCurrentPopup(); + } + ImGui::EndPopup(); + } else { + resizeSize=sample->samples; + } + ImGui::SameLine(); + ImGui::Button(ICON_FA_EXPAND "##SResample"); + if (ImGui::IsItemClicked()) { + resampleTarget=sample->rate; + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Resample"); + } + if (openSampleResampleOpt) { + openSampleResampleOpt=false; + ImGui::OpenPopup("SResampleOpt"); + } + if (ImGui::BeginPopupContextItem("SResampleOpt",ImGuiPopupFlags_MouseButtonLeft)) { + ImGui::Text("Rate"); + if (ImGui::InputDouble("##SRRate",&resampleTarget,1.0,50.0,"%g")) { + if (resampleTarget<0) resampleTarget=0; + if (resampleTarget>96000) resampleTarget=96000; + } + ImGui::SameLine(); + if (ImGui::Button("0.5x")) { + resampleTarget*=0.5; + } + ImGui::SameLine(); + if (ImGui::Button("==")) { + resampleTarget=sample->rate; + } + ImGui::SameLine(); + if (ImGui::Button("2.0x")) { + resampleTarget*=2.0; + } + double factor=resampleTarget/(double)sample->rate; + if (ImGui::InputDouble("Factor",&factor,0.125,0.5,"%g")) { + resampleTarget=(double)sample->rate*factor; + if (resampleTarget<0) resampleTarget=0; + if (resampleTarget>96000) resampleTarget=96000; + } + ImGui::Combo("Filter",&resampleStrat,resampleStrats,6); + if (ImGui::Button("Resample")) { + sample->prepareUndo(true); + e->lockEngine([this,sample]() { + if (!sample->resample(resampleTarget,resampleStrat)) { + showError("couldn't resample! make sure your sample is 8 or 16-bit."); + } + e->renderSamples(); + }); + updateSampleTex=true; + sampleSelStart=-1; + sampleSelEnd=-1; + MARK_MODIFIED; + ImGui::CloseCurrentPopup(); + } + ImGui::EndPopup(); + } else { resampleTarget=sample->rate; } ImGui::SameLine(); - if (ImGui::Button("2.0x")) { - resampleTarget*=2.0; + ImGui::Dummy(ImVec2(4.0*dpiScale,dpiScale)); + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_UNDO "##SUndo")) { + doUndoSample(); } - double factor=resampleTarget/(double)sample->rate; - if (ImGui::InputDouble("Factor",&factor,0.125,0.5,"%g")) { - resampleTarget=(double)sample->rate*factor; - if (resampleTarget<0) resampleTarget=0; - if (resampleTarget>96000) resampleTarget=96000; - } - ImGui::Combo("Filter",&resampleStrat,resampleStrats,6); - if (ImGui::Button("Resample")) { - sample->prepareUndo(true); - e->lockEngine([this,sample]() { - if (!sample->resample(resampleTarget,resampleStrat)) { - showError("couldn't resample! make sure your sample is 8 or 16-bit."); - } - e->renderSamples(); - }); - updateSampleTex=true; - sampleSelStart=-1; - sampleSelEnd=-1; - MARK_MODIFIED; - ImGui::CloseCurrentPopup(); - } - ImGui::EndPopup(); - } else { - resampleTarget=sample->rate; - } - ImGui::SameLine(); - ImGui::Dummy(ImVec2(4.0*dpiScale,dpiScale)); - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_UNDO "##SUndo")) { - doUndoSample(); - } - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("Undo"); - } - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_REPEAT "##SRedo")) { - doRedoSample(); - } - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("Redo"); - } - ImGui::SameLine(); - ImGui::Dummy(ImVec2(4.0*dpiScale,dpiScale)); - ImGui::SameLine(); - ImGui::Button(ICON_FA_VOLUME_UP "##SAmplify"); - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("Amplify"); - } - if (openSampleAmplifyOpt) { - openSampleAmplifyOpt=false; - ImGui::OpenPopup("SAmplifyOpt"); - } - if (ImGui::BeginPopupContextItem("SAmplifyOpt",ImGuiPopupFlags_MouseButtonLeft)) { - ImGui::Text("Volume"); - if (ImGui::InputFloat("##SRVolume",&lifyVol,10.0,50.0,"%g%%")) { - if (amplifyVol<0) amplifyVol=0; - if (amplifyVol>10000) amplifyVol=10000; + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Undo"); } ImGui::SameLine(); - ImGui::Text("(%.1fdB)",20.0*log10(amplifyVol/100.0f)); - if (ImGui::Button("Apply")) { - sample->prepareUndo(true); - e->lockEngine([this,sample]() { - SAMPLE_OP_BEGIN; - float vol=amplifyVol/100.0f; - - if (sample->depth==16) { - for (unsigned int i=start; idata16[i]*vol; - if (val<-32768) val=-32768; - if (val>32767) val=32767; - sample->data16[i]=val; - } - } else if (sample->depth==8) { - for (unsigned int i=start; idata8[i]*vol; - if (val<-128) val=-128; - if (val>127) val=127; - sample->data8[i]=val; - } - } - - updateSampleTex=true; - - e->renderSamples(); - }); - MARK_MODIFIED; - ImGui::CloseCurrentPopup(); + if (ImGui::Button(ICON_FA_REPEAT "##SRedo")) { + doRedoSample(); } - ImGui::EndPopup(); - } - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_ARROWS_V "##SNormalize")) { - doAction(GUI_ACTION_SAMPLE_NORMALIZE); - } - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("Normalize"); - } - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_ARROW_UP "##SFadeIn")) { - doAction(GUI_ACTION_SAMPLE_FADE_IN); - } - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("Fade in"); - } - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_ARROW_DOWN "##SFadeOut")) { - doAction(GUI_ACTION_SAMPLE_FADE_OUT); - } - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("Fade out"); - } - ImGui::SameLine(); - ImGui::Button(ICON_FA_ADJUST "##SInsertSilence"); - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("Insert silence"); - } - if (openSampleSilenceOpt) { - openSampleSilenceOpt=false; - ImGui::OpenPopup("SSilenceOpt"); - } - if (ImGui::BeginPopupContextItem("SSilenceOpt",ImGuiPopupFlags_MouseButtonLeft)) { - if (ImGui::InputInt("Samples",&silenceSize,1,64)) { - if (silenceSize<0) silenceSize=0; - if (silenceSize>16777215) silenceSize=16777215; - } - if (ImGui::Button("Resize")) { - int pos=(sampleSelStart==-1 || sampleSelStart==sampleSelEnd)?sample->samples:sampleSelStart; - sample->prepareUndo(true); - e->lockEngine([this,sample,pos]() { - if (!sample->insert(pos,silenceSize)) { - showError("couldn't insert! make sure your sample is 8 or 16-bit."); - } - e->renderSamples(); - }); - updateSampleTex=true; - sampleSelStart=pos; - sampleSelEnd=pos+silenceSize; - MARK_MODIFIED; - ImGui::CloseCurrentPopup(); - } - ImGui::EndPopup(); - } - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_ERASER "##SSilence")) { - doAction(GUI_ACTION_SAMPLE_SILENCE); - } - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("Apply silence"); - } - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_TIMES "##SDelete")) { - doAction(GUI_ACTION_SAMPLE_DELETE); - } - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("Delete"); - } - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_CROP "##STrim")) { - doAction(GUI_ACTION_SAMPLE_TRIM); - } - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("Trim"); - } - ImGui::SameLine(); - ImGui::Dummy(ImVec2(4.0*dpiScale,dpiScale)); - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_BACKWARD "##SReverse")) { - doAction(GUI_ACTION_SAMPLE_REVERSE); - } - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("Reverse"); - } - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_SORT_AMOUNT_ASC "##SInvert")) { - doAction(GUI_ACTION_SAMPLE_INVERT); - } - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("Invert"); - } - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_LEVEL_DOWN "##SSign")) { - doAction(GUI_ACTION_SAMPLE_SIGN); - } - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("Signed/unsigned exchange"); - } - ImGui::SameLine(); - ImGui::Button(ICON_FA_INDUSTRY "##SFilter"); - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("Apply filter"); - } - if (openSampleFilterOpt) { - openSampleFilterOpt=false; - ImGui::OpenPopup("SFilterOpt"); - } - if (ImGui::BeginPopupContextItem("SFilterOpt",ImGuiPopupFlags_MouseButtonLeft)) { - float lowP=sampleFilterL*100.0f; - float bandP=sampleFilterB*100.0f; - float highP=sampleFilterH*100.0f; - float resP=sampleFilterRes*100.0f; - ImGui::Text("Cutoff:"); - if (ImGui::SliderFloat("From",&sampleFilterCutStart,0.0f,sample->rate*0.5,"%.0fHz")) { - if (sampleFilterCutStart<0.0) sampleFilterCutStart=0.0; - if (sampleFilterCutStart>sample->rate*0.5) sampleFilterCutStart=sample->rate*0.5; - } - if (ImGui::SliderFloat("To",&sampleFilterCutEnd,0.0f,sample->rate*0.5,"%.0fHz")) { - if (sampleFilterCutEnd<0.0) sampleFilterCutEnd=0.0; - if (sampleFilterCutEnd>sample->rate*0.5) sampleFilterCutEnd=sample->rate*0.5; - } - ImGui::Separator(); - if (ImGui::SliderFloat("Resonance",&resP,0.0f,99.0f,"%.1f%%")) { - sampleFilterRes=resP/100.0f; - if (sampleFilterRes<0.0f) sampleFilterRes=0.0f; - if (sampleFilterRes>0.99f) sampleFilterRes=0.99f; - } - ImGui::Text("Power"); - ImGui::SameLine(); - if (ImGui::RadioButton("1x",sampleFilterPower==1)) { - sampleFilterPower=1; + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Redo"); } ImGui::SameLine(); - if (ImGui::RadioButton("2x",sampleFilterPower==2)) { - sampleFilterPower=2; - } + ImGui::Dummy(ImVec2(4.0*dpiScale,dpiScale)); ImGui::SameLine(); - if (ImGui::RadioButton("3x",sampleFilterPower==3)) { - sampleFilterPower=3; + ImGui::Button(ICON_FA_VOLUME_UP "##SAmplify"); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Amplify"); } - ImGui::Separator(); - if (ImGui::SliderFloat("Low-pass",&lowP,0.0f,100.0f,"%.1f%%")) { - sampleFilterL=lowP/100.0f; - if (sampleFilterL<0.0f) sampleFilterL=0.0f; - if (sampleFilterL>1.0f) sampleFilterL=1.0f; + if (openSampleAmplifyOpt) { + openSampleAmplifyOpt=false; + ImGui::OpenPopup("SAmplifyOpt"); } - if (ImGui::SliderFloat("Band-pass",&bandP,0.0f,100.0f,"%.1f%%")) { - sampleFilterB=bandP/100.0f; - if (sampleFilterB<0.0f) sampleFilterB=0.0f; - if (sampleFilterB>1.0f) sampleFilterB=1.0f; - } - if (ImGui::SliderFloat("High-pass",&highP,0.0f,100.0f,"%.1f%%")) { - sampleFilterH=highP/100.0f; - if (sampleFilterH<0.0f) sampleFilterH=0.0f; - if (sampleFilterH>1.0f) sampleFilterH=1.0f; - } - - if (ImGui::Button("Apply")) { - sample->prepareUndo(true); - e->lockEngine([this,sample]() { - SAMPLE_OP_BEGIN; - float res=1.0-pow(sampleFilterRes,0.5f); - float low=0; - float band=0; - float high=0; - - double power=(sampleFilterCutStart>sampleFilterCutEnd)?0.5:2.0; - - if (sample->depth==16) { - for (unsigned int i=start; irate))*M_PI); - - for (int j=0; jdata16[i])-low-(res*band); - band=cut*high+band; - } - - float val=low*sampleFilterL+band*sampleFilterB+high*sampleFilterH; - if (val<-32768) val=-32768; - if (val>32767) val=32767; - sample->data16[i]=val; - } - } else if (sample->depth==8) { - for (unsigned int i=start; irate))*M_PI); - - for (int j=0; jdata8[i])-low-(res*band); - band=cut*high+band; - } - - float val=low*sampleFilterL+band*sampleFilterB+high*sampleFilterH; - if (val<-128) val=-128; - if (val>127) val=127; - sample->data8[i]=val; - } - } - - updateSampleTex=true; - - e->renderSamples(); - }); - MARK_MODIFIED; - ImGui::CloseCurrentPopup(); - } - ImGui::EndPopup(); - } - ImGui::SameLine(); - ImGui::Dummy(ImVec2(4.0*dpiScale,dpiScale)); - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_VOLUME_UP "##PreviewSample")) { - e->previewSample(curSample); - } - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("Preview sample"); - } - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_VOLUME_OFF "##StopSample")) { - e->stopSamplePreview(); - } - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("Stop sample preview"); - } - - ImGui::SameLine(); - double zoomPercent=100.0/sampleZoom; - ImGui::Text("Zoom"); - ImGui::SameLine(); - ImGui::SetNextItemWidth(150.0f*dpiScale); - if (ImGui::InputDouble("##SZoom",&zoomPercent,5.0,20.0,"%g%%")) { - if (zoomPercent>10000.0) zoomPercent=10000.0; - if (zoomPercent<1.0) zoomPercent=1.0; - sampleZoom=100.0/zoomPercent; - if (sampleZoom<0.01) sampleZoom=0.01; - sampleZoomAuto=false; - updateSampleTex=true; - } - ImGui::SameLine(); - if (sampleZoomAuto) { - if (ImGui::Button("100%")) { - sampleZoom=1.0; - sampleZoomAuto=false; - updateSampleTex=true; - } - } else { - if (ImGui::Button("Auto")) { - sampleZoomAuto=true; - updateSampleTex=true; - } - } - - ImGui::Separator(); - - ImVec2 avail=ImGui::GetContentRegionAvail(); - if(ImGui::GetContentRegionAvail().y > (ImGui::GetContentRegionAvail().x / 2.0f)) { - avail=ImVec2(ImGui::GetContentRegionAvail().x,ImGui::GetContentRegionAvail().x / 2.0f); - } - avail.y-=ImGui::GetFontSize()+ImGui::GetStyle().ItemSpacing.y+ImGui::GetStyle().ScrollbarSize; - if (avail.y < 1.0){ //Prevents crash - avail.y = 1.0; - } - - int availX=avail.x; - int availY=avail.y; - - - if (sampleZoomAuto) { - samplePos=0; - if (sample->samples<1 || avail.x<=0) { - sampleZoom=1.0; - } else { - sampleZoom=(double)sample->samples/avail.x; - } - if (sampleZoom!=prevSampleZoom) { - prevSampleZoom=sampleZoom; - updateSampleTex=true; - } - } - - if (sampleTex==NULL || sampleTexW!=avail.x || sampleTexH!=avail.y) { - if (sampleTex!=NULL) { - SDL_DestroyTexture(sampleTex); - sampleTex=NULL; - } - if (avail.x>=1 && avail.y>=1) { - logD("recreating sample texture."); - sampleTex=SDL_CreateTexture(sdlRend,SDL_PIXELFORMAT_ABGR8888,SDL_TEXTUREACCESS_STREAMING,avail.x,avail.y); - sampleTexW=avail.x; - sampleTexH=avail.y; - if (sampleTex==NULL) { - logE("error while creating sample texture! %s",SDL_GetError()); - } else { - updateSampleTex=true; + if (ImGui::BeginPopupContextItem("SAmplifyOpt",ImGuiPopupFlags_MouseButtonLeft)) { + ImGui::Text("Volume"); + if (ImGui::InputFloat("##SRVolume",&lifyVol,10.0,50.0,"%g%%")) { + if (amplifyVol<0) amplifyVol=0; + if (amplifyVol>10000) amplifyVol=10000; } - } - } + ImGui::SameLine(); + ImGui::Text("(%.1fdB)",20.0*log10(amplifyVol/100.0f)); + if (ImGui::Button("Apply")) { + sample->prepareUndo(true); + e->lockEngine([this,sample]() { + SAMPLE_OP_BEGIN; + float vol=amplifyVol/100.0f; - if (sampleTex!=NULL) { - if (updateSampleTex) { - unsigned int* data=NULL; - int pitch=0; - logD("updating sample texture."); - if (SDL_LockTexture(sampleTex,NULL,(void**)&data,&pitch)!=0) { - logE("error while locking sample texture! %s",SDL_GetError()); - } else { - ImU32 bgColor=ImGui::GetColorU32(ImGuiCol_FrameBg); - ImU32 bgColorLoop=ImAlphaBlendColors(bgColor,ImGui::GetColorU32(ImGuiCol_FrameBgHovered,0.5)); - ImU32 lineColor=ImGui::GetColorU32(ImGuiCol_PlotLines); - ImU32 centerLineColor=ImAlphaBlendColors(bgColor,ImGui::GetColorU32(ImGuiCol_PlotLines,0.25)); - for (int i=0; iloopStart>=0 && sample->loopStart<(int)sample->samples && ((j+samplePos)*sampleZoom)>sample->loopStart) { - data[i*availX+j]=bgColorLoop; - } else { - data[i*availX+j]=bgColor; + if (sample->depth==16) { + for (unsigned int i=start; idata16[i]*vol; + if (val<-32768) val=-32768; + if (val>32767) val=32767; + sample->data16[i]=val; + } + } else if (sample->depth==8) { + for (unsigned int i=start; idata8[i]*vol; + if (val<-128) val=-128; + if (val>127) val=127; + sample->data8[i]=val; } } - } - if (availY>0) { - for (int i=availX*(availY>>1); i>1)); i++) { - data[i]=centerLineColor; - } - } - unsigned int xCoarse=samplePos; - unsigned int xFine=0; - unsigned int xAdvanceCoarse=sampleZoom; - unsigned int xAdvanceFine=fmod(sampleZoom,1.0)*16777216; - for (unsigned int i=0; i<(unsigned int)availX; i++) { - if (xCoarse>=sample->samples) break; - int y1, y2; - int totalAdvance=0; - if (sample->depth==8) { - y1=((unsigned char)sample->data8[xCoarse]^0x80)*availY/256; - } else { - y1=((unsigned short)sample->data16[xCoarse]^0x8000)*availY/65536; - } - xFine+=xAdvanceFine; - if (xFine>=16777216) { - xFine-=16777216; - totalAdvance++; - } - totalAdvance+=xAdvanceCoarse; - if (xCoarse>=sample->samples) break; - do { - if (sample->depth==8) { - y2=((unsigned char)sample->data8[xCoarse]^0x80)*availY/256; - } else { - y2=((unsigned short)sample->data16[xCoarse]^0x8000)*availY/65536; - } - if (y1>y2) { - y2^=y1; - y1^=y2; - y2^=y1; - } - if (y1<0) y1=0; - if (y1>=availY) y1=availY-1; - if (y2<0) y2=0; - if (y2>=availY) y2=availY-1; - for (int j=y1; j<=y2; j++) { - data[i+availX*(availY-j-1)]=lineColor; - } - if (totalAdvance>0) xCoarse++; - } while ((totalAdvance--)>0); - } - SDL_UnlockTexture(sampleTex); - } - updateSampleTex=false; - } - ImGui::ImageButton(sampleTex,avail,ImVec2(0,0),ImVec2(1,1),0); + updateSampleTex=true; - ImVec2 rectMin=ImGui::GetItemRectMin(); - ImVec2 rectMax=ImGui::GetItemRectMax(); - ImVec2 rectSize=ImGui::GetItemRectSize(); - - if (ImGui::IsItemClicked()) { - if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) { - sampleDragActive=false; - sampleSelStart=0; - sampleSelEnd=sample->samples; - } else { - if (sample->samples>0 && (sample->depth==16 || sample->depth==8)) { - sampleDragStart=rectMin; - sampleDragAreaSize=rectSize; - sampleDrag16=(sample->depth==16); - sampleDragTarget=(sample->depth==16)?((void*)sample->data16):((void*)sample->data8); - sampleDragLen=sample->samples; - sampleDragActive=true; - sampleSelStart=-1; - sampleSelEnd=-1; - if (sampleDragMode) sample->prepareUndo(true); - processDrags(ImGui::GetMousePos().x,ImGui::GetMousePos().y); - } - } - } - - if (!sampleDragMode && ImGui::IsItemClicked(ImGuiMouseButton_Right)) { - ImGui::OpenPopup("SRightClick"); - } - - if (ImGui::BeginPopup("SRightClick",ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_AlwaysAutoResize)) { - if (ImGui::MenuItem("cut",BIND_FOR(GUI_ACTION_SAMPLE_CUT))) { - doAction(GUI_ACTION_SAMPLE_CUT); - } - if (ImGui::MenuItem("copy",BIND_FOR(GUI_ACTION_SAMPLE_COPY))) { - doAction(GUI_ACTION_SAMPLE_COPY); - } - if (ImGui::MenuItem("paste",BIND_FOR(GUI_ACTION_SAMPLE_PASTE))) { - doAction(GUI_ACTION_SAMPLE_PASTE); - } - if (ImGui::MenuItem("paste (replace)",BIND_FOR(GUI_ACTION_SAMPLE_PASTE_REPLACE))) { - doAction(GUI_ACTION_SAMPLE_PASTE_REPLACE); - } - if (ImGui::MenuItem("paste (mix)",BIND_FOR(GUI_ACTION_SAMPLE_PASTE_MIX))) { - doAction(GUI_ACTION_SAMPLE_PASTE_MIX); - } - if (ImGui::MenuItem("select all",BIND_FOR(GUI_ACTION_SAMPLE_SELECT_ALL))) { - doAction(GUI_ACTION_SAMPLE_SELECT_ALL); + e->renderSamples(); + }); + MARK_MODIFIED; + ImGui::CloseCurrentPopup(); } ImGui::EndPopup(); } - - String statusBar=sampleDragMode?"Draw":"Select"; - bool drawSelection=false; - - if (!sampleDragMode) { - if (sampleSelStart>=0 && sampleSelEnd>=0) { - int start=sampleSelStart; - int end=sampleSelEnd; - if (start>end) { - start^=end; - end^=start; - start^=end; - } - statusBar+=fmt::sprintf(" (%d-%d)",start,end); - drawSelection=true; - } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_ARROWS_V "##SNormalize")) { + doAction(GUI_ACTION_SAMPLE_NORMALIZE); } - if (ImGui::IsItemHovered()) { - int posX=-1; - int posY=0; - ImVec2 pos=ImGui::GetMousePos(); - pos.x-=rectMin.x; - pos.y-=rectMin.y; - - if (sampleZoom>0) { - posX=samplePos+pos.x*sampleZoom; - if (posX>(int)sample->samples) posX=-1; - } - posY=(0.5-pos.y/rectSize.y)*((sample->depth==8)?255:32767); - if (posX>=0) { - statusBar+=fmt::sprintf(" | (%d, %d)",posX,posY); - } + ImGui::SetTooltip("Normalize"); } - - if (drawSelection) { - int start=sampleSelStart; - int end=sampleSelEnd; - if (start>end) { - start^=end; - end^=start; - start^=end; - } - ImDrawList* dl=ImGui::GetWindowDrawList(); - ImVec2 p1=rectMin; - p1.x+=(start-samplePos)/sampleZoom; - - ImVec2 p2=ImVec2(rectMin.x+(end-samplePos)/sampleZoom,rectMax.y); - ImVec4 boundColor=uiColors[GUI_COLOR_ACCENT_PRIMARY]; - ImVec4 selColor=uiColors[GUI_COLOR_ACCENT_SECONDARY]; - boundColor.w*=0.5; - selColor.w*=0.25; - - if (p1.xrectMax.x) p1.x=rectMax.x; - - if (p2.xrectMax.x) p2.x=rectMax.x; - - dl->AddRectFilled(p1,p2,ImGui::GetColorU32(selColor)); - dl->AddLine(ImVec2(p1.x,p1.y),ImVec2(p1.x,p2.y),ImGui::GetColorU32(boundColor)); - if (start!=end) { - dl->AddLine(ImVec2(p2.x,p1.y),ImVec2(p2.x,p2.y),ImGui::GetColorU32(boundColor)); - } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_ARROW_UP "##SFadeIn")) { + doAction(GUI_ACTION_SAMPLE_FADE_IN); } - - ImS64 scrollV=samplePos; - ImS64 availV=round(rectSize.x*sampleZoom); - ImS64 contentsV=MAX(sample->samples,MAX(availV,1)); - - if (ImGui::ScrollbarEx(ImRect(ImVec2(rectMin.x,rectMax.y),ImVec2(rectMax.x,rectMax.y+ImGui::GetStyle().ScrollbarSize)),ImGui::GetID("sampleScroll"),ImGuiAxis_X,&scrollV,availV,contentsV,0)) { - if (!sampleZoomAuto && samplePos!=scrollV) { - samplePos=scrollV; + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Fade in"); + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_ARROW_DOWN "##SFadeOut")) { + doAction(GUI_ACTION_SAMPLE_FADE_OUT); + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Fade out"); + } + ImGui::SameLine(); + ImGui::Button(ICON_FA_ADJUST "##SInsertSilence"); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Insert silence"); + } + if (openSampleSilenceOpt) { + openSampleSilenceOpt=false; + ImGui::OpenPopup("SSilenceOpt"); + } + if (ImGui::BeginPopupContextItem("SSilenceOpt",ImGuiPopupFlags_MouseButtonLeft)) { + if (ImGui::InputInt("Samples",&silenceSize,1,64)) { + if (silenceSize<0) silenceSize=0; + if (silenceSize>16777215) silenceSize=16777215; + } + if (ImGui::Button("Resize")) { + int pos=(sampleSelStart==-1 || sampleSelStart==sampleSelEnd)?sample->samples:sampleSelStart; + sample->prepareUndo(true); + e->lockEngine([this,sample,pos]() { + if (!sample->insert(pos,silenceSize)) { + showError("couldn't insert! make sure your sample is 8 or 16-bit."); + } + e->renderSamples(); + }); updateSampleTex=true; + sampleSelStart=pos; + sampleSelEnd=pos+silenceSize; + MARK_MODIFIED; + ImGui::CloseCurrentPopup(); } + ImGui::EndPopup(); } - - if (sample->depth!=8 && sample->depth!=16) { - statusBar="Non-8/16-bit samples cannot be edited without prior conversion."; - } - - ImGui::EndDisabled(); - - ImGui::SetCursorPosY(ImGui::GetCursorPosY()+ImGui::GetStyle().ScrollbarSize); - ImGui::Text("%s",statusBar.c_str()); - } - - /* - bool considerations=false; - ImGui::Text("notes:"); - if (sample->loopStart>=0) { - considerations=true; - ImGui::Text("- sample won't loop on Neo Geo ADPCM-A and X1-010"); - if (sample->loopStart&1) { - ImGui::Text("- sample loop start will be aligned to the nearest even sample on Amiga"); - } - if (sample->loopStart>0) { - ImGui::Text("- sample loop start will be ignored on Neo Geo ADPCM-B"); - } - } - if (sample->samples&1) { - considerations=true; - ImGui::Text("- sample length will be aligned to the nearest even sample on Amiga"); - } - if (sample->samples&511) { - considerations=true; - ImGui::Text("- sample length will be aligned and padded to 512 sample units on Neo Geo ADPCM."); - } - if (sample->samples&4095) { - considerations=true; - ImGui::Text("- sample length will be aligned and padded to 4096 sample units on X1-010."); - } - if (sample->samples>65535) { - considerations=true; - ImGui::Text("- maximum sample length on Sega PCM and QSound is 65536 samples"); - } - if (sample->samples>131071) { - considerations=true; - ImGui::Text("- maximum sample length on X1-010 is 131072 samples"); - } - if (sample->samples>2097151) { - considerations=true; - ImGui::Text("- maximum sample length on Neo Geo ADPCM is 2097152 samples"); - } - if (!considerations) { - ImGui::Text("- none"); - }*/ - } - else { - DivSample* sample=e->song.sample[curSample]; - String sampleType="Invalid"; - if (sample->depth<17) { - if (sampleDepths[sample->depth]!=NULL) { - sampleType=sampleDepths[sample->depth]; - } - } - ImGui::Text("Name"); - ImGui::SameLine(); - ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); - if (ImGui::InputText("##SampleName",&sample->name)) { - MARK_MODIFIED; - } - - if (ImGui::BeginTable("SampleProps",2,ImGuiTableFlags_SizingStretchSame)) { - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - ImGui::Text("Type"); ImGui::SameLine(); - ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); - if (ImGui::BeginCombo("##SampleType",sampleType.c_str())) { - for (int i=0; i<17; i++) { - if (sampleDepths[i]==NULL) continue; - if (ImGui::Selectable(sampleDepths[i])) { - sample->prepareUndo(true); - sample->depth=i; - e->renderSamplesP(); - updateSampleTex=true; - MARK_MODIFIED; - } + if (ImGui::Button(ICON_FA_ERASER "##SSilence")) { + doAction(GUI_ACTION_SAMPLE_SILENCE); + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Apply silence"); + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_TIMES "##SDelete")) { + doAction(GUI_ACTION_SAMPLE_DELETE); + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Delete"); + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_CROP "##STrim")) { + doAction(GUI_ACTION_SAMPLE_TRIM); + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Trim"); + } + ImGui::SameLine(); + ImGui::Dummy(ImVec2(4.0*dpiScale,dpiScale)); + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_BACKWARD "##SReverse")) { + doAction(GUI_ACTION_SAMPLE_REVERSE); + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Reverse"); + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_SORT_AMOUNT_ASC "##SInvert")) { + doAction(GUI_ACTION_SAMPLE_INVERT); + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Invert"); + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_LEVEL_DOWN "##SSign")) { + doAction(GUI_ACTION_SAMPLE_SIGN); + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Signed/unsigned exchange"); + } + ImGui::SameLine(); + ImGui::Button(ICON_FA_INDUSTRY "##SFilter"); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Apply filter"); + } + if (openSampleFilterOpt) { + openSampleFilterOpt=false; + ImGui::OpenPopup("SFilterOpt"); + } + if (ImGui::BeginPopupContextItem("SFilterOpt",ImGuiPopupFlags_MouseButtonLeft)) { + float lowP=sampleFilterL*100.0f; + float bandP=sampleFilterB*100.0f; + float highP=sampleFilterH*100.0f; + float resP=sampleFilterRes*100.0f; + ImGui::Text("Cutoff:"); + if (ImGui::SliderFloat("From",&sampleFilterCutStart,0.0f,sample->rate*0.5,"%.0fHz")) { + if (sampleFilterCutStart<0.0) sampleFilterCutStart=0.0; + if (sampleFilterCutStart>sample->rate*0.5) sampleFilterCutStart=sample->rate*0.5; } - ImGui::EndCombo(); - } - - ImGui::TableNextColumn(); - ImGui::Text("Rate (Hz)"); - ImGui::SameLine(); - ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); - if (ImGui::InputInt("##SampleRate",&sample->rate,10,200)) { MARK_MODIFIED - if (sample->rate<100) sample->rate=100; - if (sample->rate>96000) sample->rate=96000; - } - - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - ImGui::Text("C-4 (Hz)"); - ImGui::SameLine(); - ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); - if (ImGui::InputInt("##SampleCenter",&sample->centerRate,10,200)) { MARK_MODIFIED - if (sample->centerRate<100) sample->centerRate=100; - if (sample->centerRate>65535) sample->centerRate=65535; - } - - ImGui::TableNextColumn(); - bool doLoop=(sample->loopStart>=0); - if (ImGui::Checkbox("Loop",&doLoop)) { MARK_MODIFIED - if (doLoop) { - sample->loopStart=0; - } else { - sample->loopStart=-1; + if (ImGui::SliderFloat("To",&sampleFilterCutEnd,0.0f,sample->rate*0.5,"%.0fHz")) { + if (sampleFilterCutEnd<0.0) sampleFilterCutEnd=0.0; + if (sampleFilterCutEnd>sample->rate*0.5) sampleFilterCutEnd=sample->rate*0.5; } - updateSampleTex=true; - } - if (doLoop) { + ImGui::Separator(); + if (ImGui::SliderFloat("Resonance",&resP,0.0f,99.0f,"%.1f%%")) { + sampleFilterRes=resP/100.0f; + if (sampleFilterRes<0.0f) sampleFilterRes=0.0f; + if (sampleFilterRes>0.99f) sampleFilterRes=0.99f; + } + ImGui::Text("Power"); ImGui::SameLine(); - ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); - if (ImGui::InputInt("##LoopPosition",&sample->loopStart,1,10)) { MARK_MODIFIED - if (sample->loopStart<0 || sample->loopStart>=(int)sample->samples) { - sample->loopStart=0; - } - updateSampleTex=true; + if (ImGui::RadioButton("1x",sampleFilterPower==1)) { + sampleFilterPower=1; + } + ImGui::SameLine(); + if (ImGui::RadioButton("2x",sampleFilterPower==2)) { + sampleFilterPower=2; + } + ImGui::SameLine(); + if (ImGui::RadioButton("3x",sampleFilterPower==3)) { + sampleFilterPower=3; + } + ImGui::Separator(); + if (ImGui::SliderFloat("Low-pass",&lowP,0.0f,100.0f,"%.1f%%")) { + sampleFilterL=lowP/100.0f; + if (sampleFilterL<0.0f) sampleFilterL=0.0f; + if (sampleFilterL>1.0f) sampleFilterL=1.0f; + } + if (ImGui::SliderFloat("Band-pass",&bandP,0.0f,100.0f,"%.1f%%")) { + sampleFilterB=bandP/100.0f; + if (sampleFilterB<0.0f) sampleFilterB=0.0f; + if (sampleFilterB>1.0f) sampleFilterB=1.0f; + } + if (ImGui::SliderFloat("High-pass",&highP,0.0f,100.0f,"%.1f%%")) { + sampleFilterH=highP/100.0f; + if (sampleFilterH<0.0f) sampleFilterH=0.0f; + if (sampleFilterH>1.0f) sampleFilterH=1.0f; } - } - ImGui::EndTable(); - } - /* - if (ImGui::Button("Apply")) { - e->renderSamplesP(); - } - ImGui::SameLine(); - */ - ImGui::Separator(); + if (ImGui::Button("Apply")) { + sample->prepareUndo(true); + e->lockEngine([this,sample]() { + SAMPLE_OP_BEGIN; + float res=1.0-pow(sampleFilterRes,0.5f); + float low=0; + float band=0; + float high=0; - ImGui::BeginDisabled(sample->depth!=8 && sample->depth!=16); + double power=(sampleFilterCutStart>sampleFilterCutEnd)?0.5:2.0; - ImGui::PushStyleColor(ImGuiCol_Button,TOGGLE_COLOR(!sampleDragMode)); - if (ImGui::Button(ICON_FA_I_CURSOR "##SSelect")) { - sampleDragMode=false; - } - ImGui::PopStyleColor(); - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("Edit mode: Select"); - } - ImGui::SameLine(); - ImGui::PushStyleColor(ImGuiCol_Button,TOGGLE_COLOR(sampleDragMode)); - if (ImGui::Button(ICON_FA_PENCIL "##SDraw")) { - sampleDragMode=true; - } - ImGui::PopStyleColor(); - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("Edit mode: Draw"); - } - ImGui::SameLine(); - ImGui::Dummy(ImVec2(7.0*dpiScale,dpiScale)); - ImGui::SameLine(); - ImGui::Button(ICON_FA_ARROWS_H "##SResize"); - if (ImGui::IsItemClicked()) { - resizeSize=sample->samples; - } - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("Resize"); - } - if (openSampleResizeOpt) { - openSampleResizeOpt=false; - ImGui::OpenPopup("SResizeOpt"); - } - if (ImGui::BeginPopupContextItem("SResizeOpt",ImGuiPopupFlags_MouseButtonLeft)) { - if (ImGui::InputInt("Samples",&resizeSize,1,64)) { - if (resizeSize<0) resizeSize=0; - if (resizeSize>16777215) resizeSize=16777215; - } - if (ImGui::Button("Resize")) { - sample->prepareUndo(true); - e->lockEngine([this,sample]() { - if (!sample->resize(resizeSize)) { - showError("couldn't resize! make sure your sample is 8 or 16-bit."); - } - e->renderSamples(); - }); - updateSampleTex=true; - sampleSelStart=-1; - sampleSelEnd=-1; - MARK_MODIFIED; - ImGui::CloseCurrentPopup(); - } - ImGui::EndPopup(); - } else { - resizeSize=sample->samples; - } - ImGui::SameLine(); - ImGui::Button(ICON_FA_EXPAND "##SResample"); - if (ImGui::IsItemClicked()) { - resampleTarget=sample->rate; - } - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("Resample"); - } - if (openSampleResampleOpt) { - openSampleResampleOpt=false; - ImGui::OpenPopup("SResampleOpt"); - } - if (ImGui::BeginPopupContextItem("SResampleOpt",ImGuiPopupFlags_MouseButtonLeft)) { - ImGui::Text("Rate"); - if (ImGui::InputDouble("##SRRate",&resampleTarget,1.0,50.0,"%g")) { - if (resampleTarget<0) resampleTarget=0; - if (resampleTarget>96000) resampleTarget=96000; - } - ImGui::SameLine(); - if (ImGui::Button("0.5x")) { - resampleTarget*=0.5; - } - ImGui::SameLine(); - if (ImGui::Button("==")) { - resampleTarget=sample->rate; - } - ImGui::SameLine(); - if (ImGui::Button("2.0x")) { - resampleTarget*=2.0; - } - double factor=resampleTarget/(double)sample->rate; - if (ImGui::InputDouble("Factor",&factor,0.125,0.5,"%g")) { - resampleTarget=(double)sample->rate*factor; - if (resampleTarget<0) resampleTarget=0; - if (resampleTarget>96000) resampleTarget=96000; - } - ImGui::Combo("Filter",&resampleStrat,resampleStrats,6); - if (ImGui::Button("Resample")) { - sample->prepareUndo(true); - e->lockEngine([this,sample]() { - if (!sample->resample(resampleTarget,resampleStrat)) { - showError("couldn't resample! make sure your sample is 8 or 16-bit."); - } - e->renderSamples(); - }); - updateSampleTex=true; - sampleSelStart=-1; - sampleSelEnd=-1; - MARK_MODIFIED; - ImGui::CloseCurrentPopup(); - } - ImGui::EndPopup(); - } else { - resampleTarget=sample->rate; - } - ImGui::SameLine(); - ImGui::Dummy(ImVec2(0.5*dpiScale,dpiScale)); - ImGui::SameLine(); - ImGui::Button(ICON_FA_INDUSTRY "##SFilter"); - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("Apply filter"); - } - if (openSampleFilterOpt) { - openSampleFilterOpt=false; - ImGui::OpenPopup("SFilterOpt"); - } - if (ImGui::BeginPopupContextItem("SFilterOpt",ImGuiPopupFlags_MouseButtonLeft)) { - float lowP=sampleFilterL*100.0f; - float bandP=sampleFilterB*100.0f; - float highP=sampleFilterH*100.0f; - float resP=sampleFilterRes*100.0f; - ImGui::Text("Cutoff:"); - if (ImGui::SliderFloat("From",&sampleFilterCutStart,0.0f,sample->rate*0.5,"%.0fHz")) { - if (sampleFilterCutStart<0.0) sampleFilterCutStart=0.0; - if (sampleFilterCutStart>sample->rate*0.5) sampleFilterCutStart=sample->rate*0.5; - } - if (ImGui::SliderFloat("To",&sampleFilterCutEnd,0.0f,sample->rate*0.5,"%.0fHz")) { - if (sampleFilterCutEnd<0.0) sampleFilterCutEnd=0.0; - if (sampleFilterCutEnd>sample->rate*0.5) sampleFilterCutEnd=sample->rate*0.5; - } - ImGui::Separator(); - if (ImGui::SliderFloat("Resonance",&resP,0.0f,99.0f,"%.1f%%")) { - sampleFilterRes=resP/100.0f; - if (sampleFilterRes<0.0f) sampleFilterRes=0.0f; - if (sampleFilterRes>0.99f) sampleFilterRes=0.99f; - } - ImGui::Text("Power"); - ImGui::SameLine(); - if (ImGui::RadioButton("1x",sampleFilterPower==1)) { - sampleFilterPower=1; - } - ImGui::SameLine(); - if (ImGui::RadioButton("2x",sampleFilterPower==2)) { - sampleFilterPower=2; - } - ImGui::SameLine(); - if (ImGui::RadioButton("3x",sampleFilterPower==3)) { - sampleFilterPower=3; - } - ImGui::Separator(); - if (ImGui::SliderFloat("Low-pass",&lowP,0.0f,100.0f,"%.1f%%")) { - sampleFilterL=lowP/100.0f; - if (sampleFilterL<0.0f) sampleFilterL=0.0f; - if (sampleFilterL>1.0f) sampleFilterL=1.0f; - } - if (ImGui::SliderFloat("Band-pass",&bandP,0.0f,100.0f,"%.1f%%")) { - sampleFilterB=bandP/100.0f; - if (sampleFilterB<0.0f) sampleFilterB=0.0f; - if (sampleFilterB>1.0f) sampleFilterB=1.0f; - } - if (ImGui::SliderFloat("High-pass",&highP,0.0f,100.0f,"%.1f%%")) { - sampleFilterH=highP/100.0f; - if (sampleFilterH<0.0f) sampleFilterH=0.0f; - if (sampleFilterH>1.0f) sampleFilterH=1.0f; - } + if (sample->depth==16) { + for (unsigned int i=start; irate))*M_PI); - if (ImGui::Button("Apply")) { - sample->prepareUndo(true); - e->lockEngine([this,sample]() { - SAMPLE_OP_BEGIN; - float res=1.0-pow(sampleFilterRes,0.5f); - float low=0; - float band=0; - float high=0; + for (int j=0; jdata16[i])-low-(res*band); + band=cut*high+band; + } - double power=(sampleFilterCutStart>sampleFilterCutEnd)?0.5:2.0; - - if (sample->depth==16) { - for (unsigned int i=start; irate))*M_PI); - - for (int j=0; jdata16[i])-low-(res*band); - band=cut*high+band; + float val=low*sampleFilterL+band*sampleFilterB+high*sampleFilterH; + if (val<-32768) val=-32768; + if (val>32767) val=32767; + sample->data16[i]=val; } + } else if (sample->depth==8) { + for (unsigned int i=start; irate))*M_PI); - float val=low*sampleFilterL+band*sampleFilterB+high*sampleFilterH; - if (val<-32768) val=-32768; - if (val>32767) val=32767; - sample->data16[i]=val; - } - } else if (sample->depth==8) { - for (unsigned int i=start; irate))*M_PI); + for (int j=0; jdata8[i])-low-(res*band); + band=cut*high+band; + } - for (int j=0; jdata8[i])-low-(res*band); - band=cut*high+band; + float val=low*sampleFilterL+band*sampleFilterB+high*sampleFilterH; + if (val<-128) val=-128; + if (val>127) val=127; + sample->data8[i]=val; } - - float val=low*sampleFilterL+band*sampleFilterB+high*sampleFilterH; - if (val<-128) val=-128; - if (val>127) val=127; - sample->data8[i]=val; } - } - updateSampleTex=true; + updateSampleTex=true; - e->renderSamples(); - }); - MARK_MODIFIED; - ImGui::CloseCurrentPopup(); - } - ImGui::EndPopup(); - } - ImGui::SameLine(); - ImGui::Dummy(ImVec2(0.5*dpiScale,dpiScale)); - ImGui::SameLine(); - - - ImGui::Button(ICON_FA_VOLUME_UP "##SAmplify"); - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("Amplify"); - } - if (openSampleAmplifyOpt) { - openSampleAmplifyOpt=false; - ImGui::OpenPopup("SAmplifyOpt"); - } - if (ImGui::BeginPopupContextItem("SAmplifyOpt",ImGuiPopupFlags_MouseButtonLeft)) { - ImGui::Text("Volume"); - if (ImGui::InputFloat("##SRVolume",&lifyVol,10.0,50.0,"%g%%")) { - if (amplifyVol<0) amplifyVol=0; - if (amplifyVol>10000) amplifyVol=10000; + e->renderSamples(); + }); + MARK_MODIFIED; + ImGui::CloseCurrentPopup(); + } + ImGui::EndPopup(); } ImGui::SameLine(); - ImGui::Text("(%.1fdB)",20.0*log10(amplifyVol/100.0f)); - if (ImGui::Button("Apply")) { - sample->prepareUndo(true); - e->lockEngine([this,sample]() { - SAMPLE_OP_BEGIN; - float vol=amplifyVol/100.0f; - - if (sample->depth==16) { - for (unsigned int i=start; idata16[i]*vol; - if (val<-32768) val=-32768; - if (val>32767) val=32767; - sample->data16[i]=val; - } - } else if (sample->depth==8) { - for (unsigned int i=start; idata8[i]*vol; - if (val<-128) val=-128; - if (val>127) val=127; - sample->data8[i]=val; - } - } - - updateSampleTex=true; - - e->renderSamples(); - }); - MARK_MODIFIED; - ImGui::CloseCurrentPopup(); + ImGui::Dummy(ImVec2(4.0*dpiScale,dpiScale)); + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_VOLUME_UP "##PreviewSample")) { + e->previewSample(curSample); } - ImGui::EndPopup(); - } - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_ARROWS_V "##SNormalize")) { - doAction(GUI_ACTION_SAMPLE_NORMALIZE); - } - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("Normalize"); - } - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_ARROW_UP "##SFadeIn")) { - doAction(GUI_ACTION_SAMPLE_FADE_IN); - } - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("Fade in"); - } - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_ARROW_DOWN "##SFadeOut")) { - doAction(GUI_ACTION_SAMPLE_FADE_OUT); - } - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("Fade out"); - } - - if (ImGui::Button(ICON_FA_UNDO "##SUndo")) { - doUndoSample(); - } - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("Undo"); - } - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_REPEAT "##SRedo")) { - doRedoSample(); - } - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("Redo"); - } - ImGui::SameLine(); - ImGui::Dummy(ImVec2(7.0*dpiScale,dpiScale)); - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_BACKWARD "##SReverse")) { - doAction(GUI_ACTION_SAMPLE_REVERSE); - } - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("Reverse"); - } - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_SORT_AMOUNT_ASC "##SInvert")) { - doAction(GUI_ACTION_SAMPLE_INVERT); - } - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("Invert"); - } - ImGui::SameLine(); - ImGui::Dummy(ImVec2(0.5*dpiScale,dpiScale)); - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_LEVEL_DOWN "##SSign")) { - doAction(GUI_ACTION_SAMPLE_SIGN); - } - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("Signed/unsigned exchange"); - } - ImGui::SameLine(); - ImGui::Dummy(ImVec2(0.5*dpiScale,dpiScale)); - ImGui::SameLine(); - ImGui::Button(ICON_FA_ADJUST "##SInsertSilence"); - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("Insert silence"); - } - if (openSampleSilenceOpt) { - openSampleSilenceOpt=false; - ImGui::OpenPopup("SSilenceOpt"); - } - if (ImGui::BeginPopupContextItem("SSilenceOpt",ImGuiPopupFlags_MouseButtonLeft)) { - if (ImGui::InputInt("Samples",&silenceSize,1,64)) { - if (silenceSize<0) silenceSize=0; - if (silenceSize>16777215) silenceSize=16777215; + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Preview sample"); } - if (ImGui::Button("Resize")) { - int pos=(sampleSelStart==-1 || sampleSelStart==sampleSelEnd)?sample->samples:sampleSelStart; - sample->prepareUndo(true); - e->lockEngine([this,sample,pos]() { - if (!sample->insert(pos,silenceSize)) { - showError("couldn't insert! make sure your sample is 8 or 16-bit."); - } - e->renderSamples(); - }); - updateSampleTex=true; - sampleSelStart=pos; - sampleSelEnd=pos+silenceSize; - MARK_MODIFIED; - ImGui::CloseCurrentPopup(); + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_VOLUME_OFF "##StopSample")) { + e->stopSamplePreview(); + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Stop sample preview"); } - ImGui::EndPopup(); - } - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_ERASER "##SSilence")) { - doAction(GUI_ACTION_SAMPLE_SILENCE); - } - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("Apply silence"); - } - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_TIMES "##SDelete")) { - doAction(GUI_ACTION_SAMPLE_DELETE); - } - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("Delete"); - } - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_CROP "##STrim")) { - doAction(GUI_ACTION_SAMPLE_TRIM); - } - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("Trim"); - } - if (ImGui::Button(ICON_FA_PLAY "##PreviewSample")) { - e->previewSample(curSample); - } - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("Preview sample"); - } - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_STOP "##StopSample")) { - e->stopSamplePreview(); - } - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("Stop sample preview"); - } - ImGui::SameLine(); - double zoomPercent=100.0/sampleZoom; - ImGui::Text("Zoom"); - ImGui::SameLine(); - ImGui::SetNextItemWidth(150.0f*dpiScale); - if (ImGui::InputDouble("##SZoom",&zoomPercent,5.0,20.0,"%g%%")) { - if (zoomPercent>10000.0) zoomPercent=10000.0; - if (zoomPercent<1.0) zoomPercent=1.0; - sampleZoom=100.0/zoomPercent; - if (sampleZoom<0.01) sampleZoom=0.01; - sampleZoomAuto=false; - updateSampleTex=true; - } - ImGui::SameLine(); - if (sampleZoomAuto) { - if (ImGui::Button("100%")) { - sampleZoom=1.0; + ImGui::SameLine(); + double zoomPercent=100.0/sampleZoom; + ImGui::Text("Zoom"); + ImGui::SameLine(); + ImGui::SetNextItemWidth(150.0f*dpiScale); + if (ImGui::InputDouble("##SZoom",&zoomPercent,5.0,20.0,"%g%%")) { + if (zoomPercent>10000.0) zoomPercent=10000.0; + if (zoomPercent<1.0) zoomPercent=1.0; + sampleZoom=100.0/zoomPercent; + if (sampleZoom<0.01) sampleZoom=0.01; sampleZoomAuto=false; updateSampleTex=true; } + ImGui::SameLine(); + if (sampleZoomAuto) { + if (ImGui::Button("100%")) { + sampleZoom=1.0; + sampleZoomAuto=false; + updateSampleTex=true; + } + } else { + if (ImGui::Button("Auto")) { + sampleZoomAuto=true; + updateSampleTex=true; + } + } } else { - if (ImGui::Button("Auto")) { - sampleZoomAuto=true; + ImGui::Text("Name"); + ImGui::SameLine(); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + if (ImGui::InputText("##SampleName",&sample->name)) { + MARK_MODIFIED; + } + + if (ImGui::BeginTable("SampleProps",2,ImGuiTableFlags_SizingStretchSame)) { + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::Text("Type"); + ImGui::SameLine(); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + if (ImGui::BeginCombo("##SampleType",sampleType.c_str())) { + for (int i=0; i<17; i++) { + if (sampleDepths[i]==NULL) continue; + if (ImGui::Selectable(sampleDepths[i])) { + sample->prepareUndo(true); + sample->depth=i; + e->renderSamplesP(); + updateSampleTex=true; + MARK_MODIFIED; + } + } + ImGui::EndCombo(); + } + + ImGui::TableNextColumn(); + ImGui::Text("Rate (Hz)"); + ImGui::SameLine(); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + if (ImGui::InputInt("##SampleRate",&sample->rate,10,200)) { MARK_MODIFIED + if (sample->rate<100) sample->rate=100; + if (sample->rate>96000) sample->rate=96000; + } + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::Text("C-4 (Hz)"); + ImGui::SameLine(); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + if (ImGui::InputInt("##SampleCenter",&sample->centerRate,10,200)) { MARK_MODIFIED + if (sample->centerRate<100) sample->centerRate=100; + if (sample->centerRate>65535) sample->centerRate=65535; + } + + ImGui::TableNextColumn(); + bool doLoop=(sample->loopStart>=0); + if (ImGui::Checkbox("Loop",&doLoop)) { MARK_MODIFIED + if (doLoop) { + sample->loopStart=0; + } else { + sample->loopStart=-1; + } + updateSampleTex=true; + } + if (doLoop) { + ImGui::SameLine(); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + if (ImGui::InputInt("##LoopPosition",&sample->loopStart,1,10)) { MARK_MODIFIED + if (sample->loopStart<0 || sample->loopStart>=(int)sample->samples) { + sample->loopStart=0; + } + updateSampleTex=true; + } + } + ImGui::EndTable(); + } + + /* + if (ImGui::Button("Apply")) { + e->renderSamplesP(); + } + ImGui::SameLine(); + */ + ImGui::Separator(); + + ImGui::BeginDisabled(sample->depth!=8 && sample->depth!=16); + + ImGui::PushStyleColor(ImGuiCol_Button,TOGGLE_COLOR(!sampleDragMode)); + if (ImGui::Button(ICON_FA_I_CURSOR "##SSelect")) { + sampleDragMode=false; + } + ImGui::PopStyleColor(); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Edit mode: Select"); + } + ImGui::SameLine(); + ImGui::PushStyleColor(ImGuiCol_Button,TOGGLE_COLOR(sampleDragMode)); + if (ImGui::Button(ICON_FA_PENCIL "##SDraw")) { + sampleDragMode=true; + } + ImGui::PopStyleColor(); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Edit mode: Draw"); + } + ImGui::SameLine(); + ImGui::Dummy(ImVec2(7.0*dpiScale,dpiScale)); + ImGui::SameLine(); + ImGui::Button(ICON_FA_ARROWS_H "##SResize"); + if (ImGui::IsItemClicked()) { + resizeSize=sample->samples; + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Resize"); + } + if (openSampleResizeOpt) { + openSampleResizeOpt=false; + ImGui::OpenPopup("SResizeOpt"); + } + if (ImGui::BeginPopupContextItem("SResizeOpt",ImGuiPopupFlags_MouseButtonLeft)) { + if (ImGui::InputInt("Samples",&resizeSize,1,64)) { + if (resizeSize<0) resizeSize=0; + if (resizeSize>16777215) resizeSize=16777215; + } + if (ImGui::Button("Resize")) { + sample->prepareUndo(true); + e->lockEngine([this,sample]() { + if (!sample->resize(resizeSize)) { + showError("couldn't resize! make sure your sample is 8 or 16-bit."); + } + e->renderSamples(); + }); + updateSampleTex=true; + sampleSelStart=-1; + sampleSelEnd=-1; + MARK_MODIFIED; + ImGui::CloseCurrentPopup(); + } + ImGui::EndPopup(); + } else { + resizeSize=sample->samples; + } + ImGui::SameLine(); + ImGui::Button(ICON_FA_EXPAND "##SResample"); + if (ImGui::IsItemClicked()) { + resampleTarget=sample->rate; + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Resample"); + } + if (openSampleResampleOpt) { + openSampleResampleOpt=false; + ImGui::OpenPopup("SResampleOpt"); + } + if (ImGui::BeginPopupContextItem("SResampleOpt",ImGuiPopupFlags_MouseButtonLeft)) { + ImGui::Text("Rate"); + if (ImGui::InputDouble("##SRRate",&resampleTarget,1.0,50.0,"%g")) { + if (resampleTarget<0) resampleTarget=0; + if (resampleTarget>96000) resampleTarget=96000; + } + ImGui::SameLine(); + if (ImGui::Button("0.5x")) { + resampleTarget*=0.5; + } + ImGui::SameLine(); + if (ImGui::Button("==")) { + resampleTarget=sample->rate; + } + ImGui::SameLine(); + if (ImGui::Button("2.0x")) { + resampleTarget*=2.0; + } + double factor=resampleTarget/(double)sample->rate; + if (ImGui::InputDouble("Factor",&factor,0.125,0.5,"%g")) { + resampleTarget=(double)sample->rate*factor; + if (resampleTarget<0) resampleTarget=0; + if (resampleTarget>96000) resampleTarget=96000; + } + ImGui::Combo("Filter",&resampleStrat,resampleStrats,6); + if (ImGui::Button("Resample")) { + sample->prepareUndo(true); + e->lockEngine([this,sample]() { + if (!sample->resample(resampleTarget,resampleStrat)) { + showError("couldn't resample! make sure your sample is 8 or 16-bit."); + } + e->renderSamples(); + }); + updateSampleTex=true; + sampleSelStart=-1; + sampleSelEnd=-1; + MARK_MODIFIED; + ImGui::CloseCurrentPopup(); + } + ImGui::EndPopup(); + } else { + resampleTarget=sample->rate; + } + ImGui::SameLine(); + ImGui::Dummy(ImVec2(0.5*dpiScale,dpiScale)); + ImGui::SameLine(); + ImGui::Button(ICON_FA_INDUSTRY "##SFilter"); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Apply filter"); + } + if (openSampleFilterOpt) { + openSampleFilterOpt=false; + ImGui::OpenPopup("SFilterOpt"); + } + if (ImGui::BeginPopupContextItem("SFilterOpt",ImGuiPopupFlags_MouseButtonLeft)) { + float lowP=sampleFilterL*100.0f; + float bandP=sampleFilterB*100.0f; + float highP=sampleFilterH*100.0f; + float resP=sampleFilterRes*100.0f; + ImGui::Text("Cutoff:"); + if (ImGui::SliderFloat("From",&sampleFilterCutStart,0.0f,sample->rate*0.5,"%.0fHz")) { + if (sampleFilterCutStart<0.0) sampleFilterCutStart=0.0; + if (sampleFilterCutStart>sample->rate*0.5) sampleFilterCutStart=sample->rate*0.5; + } + if (ImGui::SliderFloat("To",&sampleFilterCutEnd,0.0f,sample->rate*0.5,"%.0fHz")) { + if (sampleFilterCutEnd<0.0) sampleFilterCutEnd=0.0; + if (sampleFilterCutEnd>sample->rate*0.5) sampleFilterCutEnd=sample->rate*0.5; + } + ImGui::Separator(); + if (ImGui::SliderFloat("Resonance",&resP,0.0f,99.0f,"%.1f%%")) { + sampleFilterRes=resP/100.0f; + if (sampleFilterRes<0.0f) sampleFilterRes=0.0f; + if (sampleFilterRes>0.99f) sampleFilterRes=0.99f; + } + ImGui::Text("Power"); + ImGui::SameLine(); + if (ImGui::RadioButton("1x",sampleFilterPower==1)) { + sampleFilterPower=1; + } + ImGui::SameLine(); + if (ImGui::RadioButton("2x",sampleFilterPower==2)) { + sampleFilterPower=2; + } + ImGui::SameLine(); + if (ImGui::RadioButton("3x",sampleFilterPower==3)) { + sampleFilterPower=3; + } + ImGui::Separator(); + if (ImGui::SliderFloat("Low-pass",&lowP,0.0f,100.0f,"%.1f%%")) { + sampleFilterL=lowP/100.0f; + if (sampleFilterL<0.0f) sampleFilterL=0.0f; + if (sampleFilterL>1.0f) sampleFilterL=1.0f; + } + if (ImGui::SliderFloat("Band-pass",&bandP,0.0f,100.0f,"%.1f%%")) { + sampleFilterB=bandP/100.0f; + if (sampleFilterB<0.0f) sampleFilterB=0.0f; + if (sampleFilterB>1.0f) sampleFilterB=1.0f; + } + if (ImGui::SliderFloat("High-pass",&highP,0.0f,100.0f,"%.1f%%")) { + sampleFilterH=highP/100.0f; + if (sampleFilterH<0.0f) sampleFilterH=0.0f; + if (sampleFilterH>1.0f) sampleFilterH=1.0f; + } + + if (ImGui::Button("Apply")) { + sample->prepareUndo(true); + e->lockEngine([this,sample]() { + SAMPLE_OP_BEGIN; + float res=1.0-pow(sampleFilterRes,0.5f); + float low=0; + float band=0; + float high=0; + + double power=(sampleFilterCutStart>sampleFilterCutEnd)?0.5:2.0; + + if (sample->depth==16) { + for (unsigned int i=start; irate))*M_PI); + + for (int j=0; jdata16[i])-low-(res*band); + band=cut*high+band; + } + + float val=low*sampleFilterL+band*sampleFilterB+high*sampleFilterH; + if (val<-32768) val=-32768; + if (val>32767) val=32767; + sample->data16[i]=val; + } + } else if (sample->depth==8) { + for (unsigned int i=start; irate))*M_PI); + + for (int j=0; jdata8[i])-low-(res*band); + band=cut*high+band; + } + + float val=low*sampleFilterL+band*sampleFilterB+high*sampleFilterH; + if (val<-128) val=-128; + if (val>127) val=127; + sample->data8[i]=val; + } + } + + updateSampleTex=true; + + e->renderSamples(); + }); + MARK_MODIFIED; + ImGui::CloseCurrentPopup(); + } + ImGui::EndPopup(); + } + ImGui::SameLine(); + ImGui::Dummy(ImVec2(0.5*dpiScale,dpiScale)); + ImGui::SameLine(); + + + ImGui::Button(ICON_FA_VOLUME_UP "##SAmplify"); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Amplify"); + } + if (openSampleAmplifyOpt) { + openSampleAmplifyOpt=false; + ImGui::OpenPopup("SAmplifyOpt"); + } + if (ImGui::BeginPopupContextItem("SAmplifyOpt",ImGuiPopupFlags_MouseButtonLeft)) { + ImGui::Text("Volume"); + if (ImGui::InputFloat("##SRVolume",&lifyVol,10.0,50.0,"%g%%")) { + if (amplifyVol<0) amplifyVol=0; + if (amplifyVol>10000) amplifyVol=10000; + } + ImGui::SameLine(); + ImGui::Text("(%.1fdB)",20.0*log10(amplifyVol/100.0f)); + if (ImGui::Button("Apply")) { + sample->prepareUndo(true); + e->lockEngine([this,sample]() { + SAMPLE_OP_BEGIN; + float vol=amplifyVol/100.0f; + + if (sample->depth==16) { + for (unsigned int i=start; idata16[i]*vol; + if (val<-32768) val=-32768; + if (val>32767) val=32767; + sample->data16[i]=val; + } + } else if (sample->depth==8) { + for (unsigned int i=start; idata8[i]*vol; + if (val<-128) val=-128; + if (val>127) val=127; + sample->data8[i]=val; + } + } + + updateSampleTex=true; + + e->renderSamples(); + }); + MARK_MODIFIED; + ImGui::CloseCurrentPopup(); + } + ImGui::EndPopup(); + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_ARROWS_V "##SNormalize")) { + doAction(GUI_ACTION_SAMPLE_NORMALIZE); + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Normalize"); + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_ARROW_UP "##SFadeIn")) { + doAction(GUI_ACTION_SAMPLE_FADE_IN); + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Fade in"); + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_ARROW_DOWN "##SFadeOut")) { + doAction(GUI_ACTION_SAMPLE_FADE_OUT); + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Fade out"); + } + + if (ImGui::Button(ICON_FA_UNDO "##SUndo")) { + doUndoSample(); + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Undo"); + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_REPEAT "##SRedo")) { + doRedoSample(); + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Redo"); + } + ImGui::SameLine(); + ImGui::Dummy(ImVec2(7.0*dpiScale,dpiScale)); + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_BACKWARD "##SReverse")) { + doAction(GUI_ACTION_SAMPLE_REVERSE); + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Reverse"); + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_SORT_AMOUNT_ASC "##SInvert")) { + doAction(GUI_ACTION_SAMPLE_INVERT); + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Invert"); + } + ImGui::SameLine(); + ImGui::Dummy(ImVec2(0.5*dpiScale,dpiScale)); + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_LEVEL_DOWN "##SSign")) { + doAction(GUI_ACTION_SAMPLE_SIGN); + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Signed/unsigned exchange"); + } + ImGui::SameLine(); + ImGui::Dummy(ImVec2(0.5*dpiScale,dpiScale)); + ImGui::SameLine(); + ImGui::Button(ICON_FA_ADJUST "##SInsertSilence"); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Insert silence"); + } + if (openSampleSilenceOpt) { + openSampleSilenceOpt=false; + ImGui::OpenPopup("SSilenceOpt"); + } + if (ImGui::BeginPopupContextItem("SSilenceOpt",ImGuiPopupFlags_MouseButtonLeft)) { + if (ImGui::InputInt("Samples",&silenceSize,1,64)) { + if (silenceSize<0) silenceSize=0; + if (silenceSize>16777215) silenceSize=16777215; + } + if (ImGui::Button("Resize")) { + int pos=(sampleSelStart==-1 || sampleSelStart==sampleSelEnd)?sample->samples:sampleSelStart; + sample->prepareUndo(true); + e->lockEngine([this,sample,pos]() { + if (!sample->insert(pos,silenceSize)) { + showError("couldn't insert! make sure your sample is 8 or 16-bit."); + } + e->renderSamples(); + }); + updateSampleTex=true; + sampleSelStart=pos; + sampleSelEnd=pos+silenceSize; + MARK_MODIFIED; + ImGui::CloseCurrentPopup(); + } + ImGui::EndPopup(); + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_ERASER "##SSilence")) { + doAction(GUI_ACTION_SAMPLE_SILENCE); + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Apply silence"); + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_TIMES "##SDelete")) { + doAction(GUI_ACTION_SAMPLE_DELETE); + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Delete"); + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_CROP "##STrim")) { + doAction(GUI_ACTION_SAMPLE_TRIM); + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Trim"); + } + + if (ImGui::Button(ICON_FA_PLAY "##PreviewSample")) { + e->previewSample(curSample); + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Preview sample"); + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_STOP "##StopSample")) { + e->stopSamplePreview(); + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Stop sample preview"); + } + ImGui::SameLine(); + double zoomPercent=100.0/sampleZoom; + ImGui::Text("Zoom"); + ImGui::SameLine(); + ImGui::SetNextItemWidth(150.0f*dpiScale); + if (ImGui::InputDouble("##SZoom",&zoomPercent,5.0,20.0,"%g%%")) { + if (zoomPercent>10000.0) zoomPercent=10000.0; + if (zoomPercent<1.0) zoomPercent=1.0; + sampleZoom=100.0/zoomPercent; + if (sampleZoom<0.01) sampleZoom=0.01; + sampleZoomAuto=false; updateSampleTex=true; } + ImGui::SameLine(); + if (sampleZoomAuto) { + if (ImGui::Button("100%")) { + sampleZoom=1.0; + sampleZoomAuto=false; + updateSampleTex=true; + } + } else { + if (ImGui::Button("Auto")) { + sampleZoomAuto=true; + updateSampleTex=true; + } + } } ImGui::Separator(); ImVec2 avail=ImGui::GetContentRegionAvail(); // graph size determined here - if (ImGui::GetContentRegionAvail().y > (ImGui::GetContentRegionAvail().x / 2.0f)) { - avail=ImVec2(ImGui::GetContentRegionAvail().x,ImGui::GetContentRegionAvail().x / 2.0f); + if (ImGui::GetContentRegionAvail().y>(ImGui::GetContentRegionAvail().x*0.5f)) { + avail=ImVec2(ImGui::GetContentRegionAvail().x,ImGui::GetContentRegionAvail().x*0.5f); } avail.y-=ImGui::GetFontSize()+ImGui::GetStyle().ItemSpacing.y+ImGui::GetStyle().ScrollbarSize; - if (avail.y < 1.0){ //Prevents crash - avail.y = 1.0; + if (avail.y<1.0) { // Prevents crash + avail.y=1.0; } + int availX=avail.x; int availY=avail.y; - if (sampleZoomAuto) { samplePos=0; if (sample->samples<1 || avail.x<=0) { From 3b0be2c42a2adf59c6856b18c5e8fc84072061cd Mon Sep 17 00:00:00 2001 From: tildearrow Date: Tue, 12 Apr 2022 02:46:51 -0500 Subject: [PATCH 030/342] GUI: focus sample editor when clicking on waveform --- src/gui/sampleEdit.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/sampleEdit.cpp b/src/gui/sampleEdit.cpp index cf7f01764..07aeef8a3 100644 --- a/src/gui/sampleEdit.cpp +++ b/src/gui/sampleEdit.cpp @@ -29,7 +29,6 @@ #include "util.h" // TODO: -// - clicking on waveform should give this window focus // - add "create instrument using this sample" option // - .dmc loading void FurnaceGUI::drawSampleEdit() { @@ -1192,6 +1191,7 @@ void FurnaceGUI::drawSampleEdit() { ImVec2 rectSize=ImGui::GetItemRectSize(); if (ImGui::IsItemClicked()) { + nextWindow=GUI_WINDOW_SAMPLE_EDIT; if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) { sampleDragActive=false; sampleSelStart=0; From dae7a9a0997e2f4400b53b8419744d772a5dee42 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Tue, 12 Apr 2022 03:00:57 -0500 Subject: [PATCH 031/342] GUI: add "Create instrument from sample" option --- src/gui/doAction.cpp | 11 +++++++++++ src/gui/gui.h | 1 + src/gui/guiConst.cpp | 1 + src/gui/sampleEdit.cpp | 15 ++++++++++++++- src/gui/settings.cpp | 1 + 5 files changed, 28 insertions(+), 1 deletion(-) diff --git a/src/gui/doAction.cpp b/src/gui/doAction.cpp index 26fbd39f2..0fb6b0626 100644 --- a/src/gui/doAction.cpp +++ b/src/gui/doAction.cpp @@ -1083,6 +1083,17 @@ void FurnaceGUI::doAction(int what) { updateSampleTex=true; } break; + case GUI_ACTION_SAMPLE_MAKE_INS: { + if (curSample<0 || curSample>=(int)e->song.sample.size()) break; + DivSample* sample=e->song.sample[curSample]; + curIns=e->addInstrument(cursor.xCoarse); + e->song.ins[curIns]->type=DIV_INS_AMIGA; + e->song.ins[curIns]->name=sample->name; + e->song.ins[curIns]->amiga.initSample=curSample; + nextWindow=GUI_WINDOW_INS_EDIT; + MARK_MODIFIED; + break; + } case GUI_ACTION_ORDERS_UP: if (e->getOrder()>0) { diff --git a/src/gui/gui.h b/src/gui/gui.h index 6c1b984c6..73fc4d87d 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -436,6 +436,7 @@ enum FurnaceGUIActions { GUI_ACTION_SAMPLE_ZOOM_IN, GUI_ACTION_SAMPLE_ZOOM_OUT, GUI_ACTION_SAMPLE_ZOOM_AUTO, + GUI_ACTION_SAMPLE_MAKE_INS, GUI_ACTION_SAMPLE_MAX, GUI_ACTION_ORDERS_MIN, diff --git a/src/gui/guiConst.cpp b/src/gui/guiConst.cpp index bc5345aa5..e826f8cc8 100644 --- a/src/gui/guiConst.cpp +++ b/src/gui/guiConst.cpp @@ -327,6 +327,7 @@ const FurnaceGUIActionDef guiActions[GUI_ACTION_MAX]={ 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_MAX", "", NOT_AN_ACTION), D("ORDERS_MIN", "---Orders", NOT_AN_ACTION), diff --git a/src/gui/sampleEdit.cpp b/src/gui/sampleEdit.cpp index 07aeef8a3..a84c852f6 100644 --- a/src/gui/sampleEdit.cpp +++ b/src/gui/sampleEdit.cpp @@ -29,7 +29,6 @@ #include "util.h" // TODO: -// - add "create instrument using this sample" option // - .dmc loading void FurnaceGUI::drawSampleEdit() { if (nextWindow==GUI_WINDOW_SAMPLE_EDIT) { @@ -528,6 +527,13 @@ void FurnaceGUI::drawSampleEdit() { if (ImGui::IsItemHovered()) { ImGui::SetTooltip("Stop sample preview"); } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_UPLOAD "##MakeIns")) { + doAction(GUI_ACTION_SAMPLE_MAKE_INS); + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Create instrument from sample"); + } ImGui::SameLine(); double zoomPercent=100.0/sampleZoom; @@ -1040,6 +1046,13 @@ void FurnaceGUI::drawSampleEdit() { ImGui::SetTooltip("Stop sample preview"); } ImGui::SameLine(); + if (ImGui::Button(ICON_FA_UPLOAD "##MakeIns")) { + doAction(GUI_ACTION_SAMPLE_MAKE_INS); + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Create instrument from sample"); + } + ImGui::SameLine(); double zoomPercent=100.0/sampleZoom; ImGui::Text("Zoom"); ImGui::SameLine(); diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index ad122058c..8bb7addd8 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -1427,6 +1427,7 @@ void FurnaceGUI::drawSettings() { UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_ZOOM_IN); UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_ZOOM_OUT); UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_ZOOM_AUTO); + UI_KEYBIND_CONFIG(GUI_ACTION_SAMPLE_MAKE_INS); KEYBIND_CONFIG_END; ImGui::TreePop(); From a20861559b6afdaa0bd689cc1659d29d30918c89 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Tue, 12 Apr 2022 03:34:53 -0500 Subject: [PATCH 032/342] implement .dmc loading --- src/engine/engine.cpp | 99 +++++++++++++++++++++++++++++++++++++++--- src/gui/gui.cpp | 4 +- src/gui/sampleEdit.cpp | 2 - src/gui/settings.cpp | 1 + 4 files changed, 95 insertions(+), 11 deletions(-) diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 1c5d4dd2e..b8b18cb40 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -1469,6 +1469,97 @@ int DivEngine::addSample() { int DivEngine::addSampleFromFile(const char* path) { BUSY_BEGIN; + warnings=""; + + const char* pathRedux=strrchr(path,DIR_SEPARATOR); + if (pathRedux==NULL) { + pathRedux=path; + } else { + pathRedux++; + } + String stripPath; + const char* pathReduxEnd=strrchr(pathRedux,'.'); + if (pathReduxEnd==NULL) { + stripPath=pathRedux; + } else { + for (const char* i=pathRedux; i!=pathReduxEnd && (*i); i++) { + stripPath+=*i; + } + } + + const char* ext=strrchr(path,'.'); + if (ext!=NULL) { + String extS; + for (; *ext; ext++) { + char i=*ext; + if (i>='A' && i<='Z') { + i+='a'-'A'; + } + extS+=i; + } + if (extS==String(".dmc")) { // read as .dmc + size_t len=0; + DivSample* sample=new DivSample; + int sampleCount=(int)song.sample.size(); + sample->name=stripPath; + + FILE* f=fopen(path,"rb"); + if (f==NULL) { + BUSY_END; + lastError=fmt::sprintf("could not open file! (%s)",strerror(errno)); + delete sample; + return -1; + } + + if (fseek(f,0,SEEK_END)<0) { + fclose(f); + BUSY_END; + lastError=fmt::sprintf("could not get file length! (%s)",strerror(errno)); + delete sample; + return -1; + } + + len=ftell(f); + + if (len==0) { + fclose(f); + BUSY_END; + lastError="file is empty!"; + delete sample; + return -1; + } + + if (fseek(f,0,SEEK_SET)<0) { + fclose(f); + BUSY_END; + lastError=fmt::sprintf("could not seek to beginning of file! (%s)",strerror(errno)); + delete sample; + return -1; + } + + sample->rate=33144; + sample->centerRate=33144; + sample->depth=1; + sample->init(len*8); + + if (fread(sample->dataDPCM,1,len,f)==0) { + fclose(f); + BUSY_END; + lastError=fmt::sprintf("could not read file! (%s)",strerror(errno)); + delete sample; + return -1; + } + + saveLock.lock(); + song.sample.push_back(sample); + song.sampleLen=sampleCount+1; + saveLock.unlock(); + renderSamples(); + BUSY_END; + return sampleCount; + } + } + SF_INFO si; SNDFILE* f=sf_open(path,SFM_READ,&si); if (f==NULL) { @@ -1493,13 +1584,7 @@ int DivEngine::addSampleFromFile(const char* path) { } DivSample* sample=new DivSample; int sampleCount=(int)song.sample.size(); - const char* sName=strrchr(path,DIR_SEPARATOR); - if (sName==NULL) { - sName=path; - } else { - sName++; - } - sample->name=sName; + sample->name=stripPath; int index=0; if ((si.format&SF_FORMAT_SUBMASK)==SF_FORMAT_PCM_U8) { diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index a3cf11ba4..f577691f5 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -1368,9 +1368,9 @@ void FurnaceGUI::openFileDialog(FurnaceGUIFileDialogs type) { if (!dirExists(workingDirSample)) workingDirSample=getHomeDir(); hasOpened=fileDialog->openLoad( "Load Sample", - {"Wave file", "*.wav", + {"compatible files", "*.wav *.dmc", "all files", ".*"}, - "Wave file{.wav},.*", + "compatible files{.wav,.dmc},.*", workingDirSample, dpiScale ); diff --git a/src/gui/sampleEdit.cpp b/src/gui/sampleEdit.cpp index a84c852f6..9919c069f 100644 --- a/src/gui/sampleEdit.cpp +++ b/src/gui/sampleEdit.cpp @@ -28,8 +28,6 @@ #include "sampleUtil.h" #include "util.h" -// TODO: -// - .dmc loading void FurnaceGUI::drawSampleEdit() { if (nextWindow==GUI_WINDOW_SAMPLE_EDIT) { sampleEditOpen=true; diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index 8bb7addd8..aa6dcb503 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -2257,6 +2257,7 @@ void FurnaceGUI::applyUISettings(bool updateFonts) { ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".dmp",uiColors[GUI_COLOR_FILE_INSTR],ICON_FA_FILE); ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".dmw",uiColors[GUI_COLOR_FILE_WAVE],ICON_FA_FILE); ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".wav",uiColors[GUI_COLOR_FILE_AUDIO],ICON_FA_FILE_AUDIO_O); + ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".dmc",uiColors[GUI_COLOR_FILE_AUDIO],ICON_FA_FILE_AUDIO_O); ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".vgm",uiColors[GUI_COLOR_FILE_VGM],ICON_FA_FILE_AUDIO_O); ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".ttf",uiColors[GUI_COLOR_FILE_FONT],ICON_FA_FONT); ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".otf",uiColors[GUI_COLOR_FILE_FONT],ICON_FA_FONT); From 40451cc039d7c96fc8203e3120b14556a71cb6e8 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Tue, 12 Apr 2022 04:36:45 -0500 Subject: [PATCH 033/342] GUI: update credits --- src/gui/about.cpp | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/src/gui/about.cpp b/src/gui/about.cpp index 386b01b0c..c85843239 100644 --- a/src/gui/about.cpp +++ b/src/gui/about.cpp @@ -27,7 +27,7 @@ const char* aboutLine[]={ "", ("Furnace " DIV_VERSION), "", - "the free software chiptune tracker,", + "the free software multi-system chiptune tracker,", "compatible with DefleMask modules.", "", "zero disassembly.", @@ -42,6 +42,7 @@ const char* aboutLine[]={ "cam900", "djtuBIG-MaliceX", "laoo", + "OPNA2608", "superctr", "", "-- graphics/UI design --", @@ -73,7 +74,6 @@ const char* aboutLine[]={ "", "-- additional feedback/fixes --", "fd", - "OPNA2608", "plane", "TheEssem", "", @@ -83,25 +83,30 @@ const char* aboutLine[]={ "zlib by Jean-loup Gailly", "and Mark Adler", "libsndfile by Erik de Castro Lopo", - "Nuked-OPM & Nuked-OPN2 by Nuke.YKT", + "Portable File Dialogs by Sam Hocevar", + "RtMidi by Gary P. Scavone", + "adpcm by superctr", + "Nuked-OPL3/OPLL/OPM/OPN2 by Nuke.YKT", "ymfm by Aaron Giles", "MAME SN76496 by Nicola Salmoria", "MAME AY-3-8910 by Couriersud", "with AY8930 fixes by Eulous", "MAME SAA1099 by Juergen Buchmueller and Manuel Abadia", - "SAASound", + "SAASound by Dave Hooper and Simon Owen", "SameBoy by Lior Halphon", - "Mednafen PCE", - "puNES by FHorse", + "Mednafen PCE and WonderSwan audio cores", + "puNES (NES, MMC5 and FDS) by FHorse", "reSID by Dag Lem", "Stella by Stella Team", "QSound emulator by Ian Karlsson and Valley Bell", + "VICE VIC-20 sound core by Rami Rasanen and viznut", + "VERA sound core by Frank van den Hoef", + "K005289 emulator by cam900", + "Namco 163 emulator by cam900", + "Seta X1-010 emulator by cam900", + "Konami VRC6 emulator by cam900", "", - "greetings to:", - "Delek", - "fd", - "ILLUMIDARO", - "all members of Deflers of Noice!", + "greetings to all members of Deflers of Noice!", "", "copyright © 2021-2022 tildearrow", "(and contributors).", From b69b456ef8492386eeaa06060699852345227738 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Tue, 12 Apr 2022 14:57:55 -0500 Subject: [PATCH 034/342] update format.md --- papers/format.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/papers/format.md b/papers/format.md index 87e53ebfe..2af015af9 100644 --- a/papers/format.md +++ b/papers/format.md @@ -169,7 +169,7 @@ size | description | - 0x8f: OPL (YM3526) - 9 channels | - 0x90: OPL2 (YM3812) - 9 channels | - 0x91: OPL3 (YMF262) - 18 channels - | - 0x92: MultiPCM - 24 channels + | - 0x92: MultiPCM - 28 channels | - 0x93: Intel 8253 (beeper) - 1 channel | - 0x94: POKEY - 4 channels | - 0x95: RF5C68 - 8 channels @@ -197,7 +197,10 @@ size | description | - 0xab: MSM6258 - 1 channel | - 0xac: Commander X16 (VERA) - 17 channels | - 0xad: Bubble System WSG - 2 channels + | - 0xae: OPL4 (YMF278B) - 42 channels + | - 0xaf: OPL4 drums (YMF278B) - 44 channels | - 0xb0: Seta/Allumer X1-010 - 16 channels + | - 0xb1: Ensoniq ES5506 - 32 channels | - 0xde: YM2610B extended - 19 channels | - 0xe0: QSound - 19 channels | - (compound!) means that the system is composed of two or more chips, From bd49daad4882696e2a5f194d990f5b8547024212 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Tue, 12 Apr 2022 15:06:11 -0500 Subject: [PATCH 035/342] another format.md update --- papers/format.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/papers/format.md b/papers/format.md index 2af015af9..7bdf2677e 100644 --- a/papers/format.md +++ b/papers/format.md @@ -201,6 +201,8 @@ size | description | - 0xaf: OPL4 drums (YMF278B) - 44 channels | - 0xb0: Seta/Allumer X1-010 - 16 channels | - 0xb1: Ensoniq ES5506 - 32 channels + | - 0xb2: Yamaha Y8950 - 10 channels + | - 0xb3: Yamaha Y8950 drums - 12 channels | - 0xde: YM2610B extended - 19 channels | - 0xe0: QSound - 19 channels | - (compound!) means that the system is composed of two or more chips, From 17d06de55c7652eb8996344c7eec77e1a81c5eaf Mon Sep 17 00:00:00 2001 From: tildearrow Date: Tue, 12 Apr 2022 15:13:02 -0500 Subject: [PATCH 036/342] allocate SCC+ channel ID --- papers/format.md | 1 + 1 file changed, 1 insertion(+) diff --git a/papers/format.md b/papers/format.md index 7bdf2677e..e8f076176 100644 --- a/papers/format.md +++ b/papers/format.md @@ -203,6 +203,7 @@ size | description | - 0xb1: Ensoniq ES5506 - 32 channels | - 0xb2: Yamaha Y8950 - 10 channels | - 0xb3: Yamaha Y8950 drums - 12 channels + | - 0xb4: Konami SCC+ - 5 channels | - 0xde: YM2610B extended - 19 channels | - 0xe0: QSound - 19 channels | - (compound!) means that the system is composed of two or more chips, From bc3e64726e1274cc5306ef60877f18de8d7ae2c6 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Tue, 12 Apr 2022 15:38:40 -0500 Subject: [PATCH 037/342] prepare for "select instruments to load" dialog --- src/gui/gui.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/gui/gui.h b/src/gui/gui.h index 73fc4d87d..174039d0d 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -918,6 +918,7 @@ class FurnaceGUI { std::vector activeNotes; std::vector cmdStream; std::vector particles; + std::vector pendingIns; std::vector sysCategories; From 8702effbb975b0b93b4ef8119fd2f8ed3d01dd35 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Tue, 12 Apr 2022 15:39:01 -0500 Subject: [PATCH 038/342] prepare for storing other details --- src/engine/song.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/engine/song.h b/src/engine/song.h index 75e5a03e9..009b40980 100644 --- a/src/engine/song.h +++ b/src/engine/song.h @@ -263,8 +263,15 @@ struct DivSong { String name, author; // legacy song information + // those will be stored in .fur and mapped to VGM as: + // category -> game name + // writer -> ripper + // createdDate -> year String carrier, composer, vendor, category, writer, arranger, copyright, manGroup, manInfo, createdDate, revisionDate; + // more VGM specific stuff + String nameJ, authorJ, categoryJ; + // other things String chanName[DIV_MAX_CHANS]; String chanShortName[DIV_MAX_CHANS]; From 25c5c97c47cbf1360b210df76433969e93cd2ede Mon Sep 17 00:00:00 2001 From: tildearrow Date: Tue, 12 Apr 2022 16:12:32 -0500 Subject: [PATCH 039/342] GUI: add YM2414 to system presets --- src/gui/presets.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/gui/presets.cpp b/src/gui/presets.cpp index e7b08081c..5dd3b693d 100644 --- a/src/gui/presets.cpp +++ b/src/gui/presets.cpp @@ -88,6 +88,12 @@ void FurnaceGUI::initSystemPresets() { 0 } )); + cat.systems.push_back(FurnaceGUISysDef( + "Yamaha YM2414", { + DIV_SYSTEM_OPZ, 64, 0, 0, + 0 + } + )); cat.systems.push_back(FurnaceGUISysDef( "Yamaha YM3526", { DIV_SYSTEM_OPL, 64, 0, 0, From 8facb430817a3cbad18e6428a3d75f71ed35150d Mon Sep 17 00:00:00 2001 From: tildearrow Date: Tue, 12 Apr 2022 17:01:41 -0500 Subject: [PATCH 040/342] GUI: change behavior of play buttons --- src/gui/editControls.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/gui/editControls.cpp b/src/gui/editControls.cpp index 8d9480242..71c6a94d8 100644 --- a/src/gui/editControls.cpp +++ b/src/gui/editControls.cpp @@ -20,9 +20,6 @@ #include "gui.h" #include "IconsFontAwesome4.h" -// TODO: -// - "play from start" button/action -// - fix behavior of play buttons void FurnaceGUI::drawEditControls() { if (nextWindow==GUI_WINDOW_EDIT_CONTROLS) { editControlsOpen=true; @@ -259,11 +256,17 @@ void FurnaceGUI::drawEditControls() { ImGui::PopStyleColor(); } else { if (ImGui::Button(ICON_FA_PLAY "##Play")) { - play(); + play(oldRow); } } ImGui::SameLine(); if (ImGui::Button(ICON_FA_PLAY_CIRCLE "##PlayAgain")) { + e->setRepeatPattern(false); + play(); + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_STEP_FORWARD "##PlayRepeat")) { + e->setRepeatPattern(true); play(); } ImGui::SameLine(); From 152d05dbfc59bcb723b7913bb425a0880ebb059a Mon Sep 17 00:00:00 2001 From: tildearrow Date: Tue, 12 Apr 2022 18:26:25 -0500 Subject: [PATCH 041/342] GUI: add "separate car/mod colors" to FM editor and other improvements --- src/gui/gui.h | 24 +++ src/gui/guiConst.cpp | 19 +++ src/gui/insEdit.cpp | 345 +++++++++++++++++++++++++++---------------- src/gui/settings.cpp | 98 ++++++++++++ 4 files changed, 357 insertions(+), 129 deletions(-) diff --git a/src/gui/gui.h b/src/gui/gui.h index 174039d0d..7c0b04fcd 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -93,6 +93,25 @@ enum FurnaceGUIColors { GUI_COLOR_ORDER_SIMILAR, GUI_COLOR_ORDER_INACTIVE, + GUI_COLOR_FM_ALG_BG, + GUI_COLOR_FM_ALG_LINE, + GUI_COLOR_FM_MOD, + GUI_COLOR_FM_PRIMARY_MOD, + GUI_COLOR_FM_SECONDARY_MOD, + GUI_COLOR_FM_BORDER_MOD, + GUI_COLOR_FM_BORDER_SHADOW_MOD, + GUI_COLOR_FM_CAR, + GUI_COLOR_FM_PRIMARY_CAR, + GUI_COLOR_FM_SECONDARY_CAR, + GUI_COLOR_FM_BORDER_CAR, + GUI_COLOR_FM_BORDER_SHADOW_CAR, + + GUI_COLOR_FM_ENVELOPE, + GUI_COLOR_FM_ENVELOPE_SUS_GUIDE, + GUI_COLOR_FM_ENVELOPE_RELEASE, + GUI_COLOR_FM_SSG, + GUI_COLOR_FM_WAVE, + GUI_COLOR_MACRO_VOLUME, GUI_COLOR_MACRO_PITCH, GUI_COLOR_MACRO_OTHER, @@ -787,6 +806,7 @@ class FurnaceGUI { int oscRoundedCorners; int oscTakesEntireWindow; int oscBorder; + int separateFMColors; unsigned int maxUndoSteps; String mainFontPath; String patFontPath; @@ -851,6 +871,7 @@ class FurnaceGUI { oscRoundedCorners(1), oscTakesEntireWindow(0), oscBorder(1), + separateFMColors(0), maxUndoSteps(100), mainFontPath(""), patFontPath(""), @@ -1038,6 +1059,9 @@ class FurnaceGUI { void readOsc(); + void pushAccentColors(const ImVec4& one, const ImVec4& two, const ImVec4& border, const ImVec4& borderShadow); + void popAccentColors(); + float calcBPM(int s1, int s2, float hz); void patternRow(int i, bool isPlaying, float lineHeight, int chans, int ord, const DivPattern** patCache); diff --git a/src/gui/guiConst.cpp b/src/gui/guiConst.cpp index e826f8cc8..8f48e63af 100644 --- a/src/gui/guiConst.cpp +++ b/src/gui/guiConst.cpp @@ -398,6 +398,25 @@ const FurnaceGUIColorDef guiColors[GUI_COLOR_MAX]={ D(GUI_COLOR_ORDER_SIMILAR,"",ImVec4(0.5f,1.0f,1.0f,1.0f)), D(GUI_COLOR_ORDER_INACTIVE,"",ImVec4(1.0f,1.0f,1.0f,1.0f)), + D(GUI_COLOR_FM_ALG_BG,"",ImVec4(0.12f,0.12f,0.12f,1.0f)), + D(GUI_COLOR_FM_ALG_LINE,"",ImVec4(1.0f,1.0f,1.0f,0.33f)), + D(GUI_COLOR_FM_MOD,"",ImVec4(1.0f,1.0f,1.0f,1.0f)), + D(GUI_COLOR_FM_PRIMARY_MOD,"",ImVec4(0.06f,0.53f,0.98f,1.0f)), + D(GUI_COLOR_FM_SECONDARY_MOD,"",ImVec4(0.06f,0.53f,0.98f,1.0f)), + D(GUI_COLOR_FM_BORDER_MOD,"",ImVec4(0.43f,0.43f,0.5f,0.5f)), + D(GUI_COLOR_FM_BORDER_SHADOW_MOD,"",ImVec4(0.0f,0.0f,0.0f,0.0f)), + D(GUI_COLOR_FM_CAR,"",ImVec4(0.5f,0.8f,1.0f,1.0f)), + D(GUI_COLOR_FM_PRIMARY_CAR,"",ImVec4(0.06f,0.8f,0.98f,1.0f)), + D(GUI_COLOR_FM_SECONDARY_CAR,"",ImVec4(0.06f,0.8f,0.98f,1.0f)), + D(GUI_COLOR_FM_BORDER_CAR,"",ImVec4(0.43f,0.5f,0.5f,0.5f)), + D(GUI_COLOR_FM_BORDER_SHADOW_CAR,"",ImVec4(0.0f,0.0f,0.0f,0.0f)), + + D(GUI_COLOR_FM_ENVELOPE,"",ImVec4(1.0f,1.0f,1.0f,1.0f)), + D(GUI_COLOR_FM_ENVELOPE_SUS_GUIDE,"",ImVec4(0.3f,0.5f,0.8f,0.4f)), + D(GUI_COLOR_FM_ENVELOPE_RELEASE,"",ImVec4(0.3f,0.5f,0.8f,0.4f)), + D(GUI_COLOR_FM_SSG,"",ImVec4(1.0f,1.0f,1.0f,1.0f)), + D(GUI_COLOR_FM_WAVE,"",ImVec4(1.0f,1.0f,1.0f,1.0f)), + D(GUI_COLOR_MACRO_VOLUME,"",ImVec4(0.2f,1.0f,0.0f,1.0f)), D(GUI_COLOR_MACRO_PITCH,"",ImVec4(1.0f,0.8f,0.0f,1.0f)), D(GUI_COLOR_MACRO_OTHER,"",ImVec4(0.0f,0.9f,1.0f,1.0f)), diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index 4380764de..fa836c9ff 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -71,6 +71,24 @@ const char* opzWaveforms[8]={ "Sine", "Triangle", "Cut Sine", "Cut Triangle", "Squished Sine", "Squished Triangle", "Squished AbsSine", "Squished AbsTriangle" }; +const bool opIsOutput[8][4]={ + {false,false,false,true}, + {false,false,false,true}, + {false,false,false,true}, + {false,false,false,true}, + {false,true,false,true}, + {false,true,true,true}, + {false,true,true,true}, + {true,true,true,true} +}; + +const bool opIsOutputOPL[4][4]={ + {false,false,false,true}, + {true,false,false,true}, + {false,true,false,true}, + {true,false,true,true} +}; + enum FMParams { FM_ALG=0, FM_FB=1, @@ -239,7 +257,7 @@ void FurnaceGUI::drawSSGEnv(unsigned char type, const ImVec2& size) { ); ImRect rect=ImRect(minArea,maxArea); ImGuiStyle& style=ImGui::GetStyle(); - ImU32 color=ImGui::GetColorU32(uiColors[GUI_COLOR_TEXT]); + ImU32 color=ImGui::GetColorU32(uiColors[GUI_COLOR_FM_SSG]); ImGui::ItemSize(size,style.FramePadding.y); if (ImGui::ItemAdd(rect,ImGui::GetID("ssgEnvDisplay"))) { ImGui::RenderFrame(rect.Min,rect.Max,ImGui::GetColorU32(ImGuiCol_FrameBg),true,style.FrameRounding); @@ -352,7 +370,7 @@ void FurnaceGUI::drawWaveform(unsigned char type, bool opz, const ImVec2& size) ); ImRect rect=ImRect(minArea,maxArea); ImGuiStyle& style=ImGui::GetStyle(); - ImU32 color=ImGui::GetColorU32(uiColors[GUI_COLOR_TEXT]); + ImU32 color=ImGui::GetColorU32(uiColors[GUI_COLOR_FM_WAVE]); ImGui::ItemSize(size,style.FramePadding.y); if (ImGui::ItemAdd(rect,ImGui::GetID("wsDisplay"))) { ImGui::RenderFrame(rect.Min,rect.Max,ImGui::GetColorU32(ImGuiCol_FrameBg),true,style.FrameRounding); @@ -492,11 +510,12 @@ void FurnaceGUI::drawAlgorithm(unsigned char alg, FurnaceGUIFMAlgs algType, cons ); ImRect rect=ImRect(minArea,maxArea); ImGuiStyle& style=ImGui::GetStyle(); - ImU32 color=ImGui::GetColorU32(uiColors[GUI_COLOR_TEXT]); - ImU32 colorL=ImGui::GetColorU32(ImVec4(uiColors[GUI_COLOR_TEXT].x,uiColors[GUI_COLOR_TEXT].y,uiColors[GUI_COLOR_TEXT].z,uiColors[GUI_COLOR_TEXT].w*0.33)); + ImU32 colorM=ImGui::GetColorU32(uiColors[GUI_COLOR_FM_MOD]); + ImU32 colorC=ImGui::GetColorU32(uiColors[GUI_COLOR_FM_CAR]); + ImU32 colorL=ImGui::GetColorU32(uiColors[GUI_COLOR_FM_ALG_LINE]); ImGui::ItemSize(size,style.FramePadding.y); if (ImGui::ItemAdd(rect,ImGui::GetID("alg"))) { - ImGui::RenderFrame(rect.Min,rect.Max,ImGui::GetColorU32(ImGuiCol_FrameBg),true,style.FrameRounding); + ImGui::RenderFrame(rect.Min,rect.Max,ImGui::GetColorU32(uiColors[GUI_COLOR_FM_ALG_BG]),true,style.FrameRounding); const float circleRadius=6.0f*dpiScale+1.0f; switch (algType) { case FM_ALGS_4OP: @@ -506,14 +525,14 @@ void FurnaceGUI::drawAlgorithm(unsigned char alg, FurnaceGUIFMAlgs algType, cons ImVec2 pos2=ImLerp(rect.Min,rect.Max,ImVec2(0.4,0.5)); ImVec2 pos3=ImLerp(rect.Min,rect.Max,ImVec2(0.6,0.5)); ImVec2 pos4=ImLerp(rect.Min,rect.Max,ImVec2(0.8,0.5)); - dl->AddCircleFilled(pos1,4.0f*dpiScale+1.0f,color); - dl->AddCircle(pos1,6.0f*dpiScale+1.0f,color); + dl->AddCircleFilled(pos1,4.0f*dpiScale+1.0f,colorM); + dl->AddCircle(pos1,6.0f*dpiScale+1.0f,colorM); addAALine(dl,pos1,pos2,colorL); - dl->AddCircleFilled(pos2,4.0f*dpiScale+1.0f,color); + dl->AddCircleFilled(pos2,4.0f*dpiScale+1.0f,colorM); addAALine(dl,pos2,pos3,colorL); - dl->AddCircleFilled(pos3,4.0f*dpiScale+1.0f,color); + dl->AddCircleFilled(pos3,4.0f*dpiScale+1.0f,colorM); addAALine(dl,pos3,pos4,colorL); - dl->AddCircleFilled(pos4,4.0f*dpiScale+1.0f,color); + dl->AddCircleFilled(pos4,4.0f*dpiScale+1.0f,colorC); pos1.x-=ImGui::CalcTextSize("1").x*0.5; pos2.x-=ImGui::CalcTextSize("2").x*0.5; @@ -523,10 +542,10 @@ void FurnaceGUI::drawAlgorithm(unsigned char alg, FurnaceGUIFMAlgs algType, cons pos2.y-=ImGui::CalcTextSize("2").y+circleRadius; pos3.y-=ImGui::CalcTextSize("3").y+circleRadius; pos4.y-=ImGui::CalcTextSize("4").y+circleRadius; - dl->AddText(pos1,color,"1"); - dl->AddText(pos2,color,"2"); - dl->AddText(pos3,color,"3"); - dl->AddText(pos4,color,"4"); + dl->AddText(pos1,colorM,"1"); + dl->AddText(pos2,colorM,"2"); + dl->AddText(pos3,colorM,"3"); + dl->AddText(pos4,colorC,"4"); break; } case 1: { // (1+2) > 3 > 4 @@ -534,14 +553,14 @@ void FurnaceGUI::drawAlgorithm(unsigned char alg, FurnaceGUIFMAlgs algType, cons ImVec2 pos2=ImLerp(rect.Min,rect.Max,ImVec2(0.25,0.7)); ImVec2 pos3=ImLerp(rect.Min,rect.Max,ImVec2(0.5,0.5)); ImVec2 pos4=ImLerp(rect.Min,rect.Max,ImVec2(0.75,0.5)); - dl->AddCircleFilled(pos1,4.0f*dpiScale+1.0f,color); - dl->AddCircle(pos1,6.0f*dpiScale+1.0f,color); + dl->AddCircleFilled(pos1,4.0f*dpiScale+1.0f,colorM); + dl->AddCircle(pos1,6.0f*dpiScale+1.0f,colorM); addAALine(dl,pos1,pos3,colorL); - dl->AddCircleFilled(pos2,4.0f*dpiScale+1.0f,color); + dl->AddCircleFilled(pos2,4.0f*dpiScale+1.0f,colorM); addAALine(dl,pos2,pos3,colorL); - dl->AddCircleFilled(pos3,4.0f*dpiScale+1.0f,color); + dl->AddCircleFilled(pos3,4.0f*dpiScale+1.0f,colorM); addAALine(dl,pos3,pos4,colorL); - dl->AddCircleFilled(pos4,4.0f*dpiScale+1.0f,color); + dl->AddCircleFilled(pos4,4.0f*dpiScale+1.0f,colorC); pos2.x-=ImGui::CalcTextSize("2").x+circleRadius+3.0*dpiScale; pos1.x=pos2.x; @@ -551,10 +570,10 @@ void FurnaceGUI::drawAlgorithm(unsigned char alg, FurnaceGUIFMAlgs algType, cons pos2.y-=ImGui::CalcTextSize("2").y*0.5; pos3.y-=ImGui::CalcTextSize("3").y+circleRadius; pos4.y-=ImGui::CalcTextSize("4").y+circleRadius; - dl->AddText(pos1,color,"1"); - dl->AddText(pos2,color,"2"); - dl->AddText(pos3,color,"3"); - dl->AddText(pos4,color,"4"); + dl->AddText(pos1,colorM,"1"); + dl->AddText(pos2,colorM,"2"); + dl->AddText(pos3,colorM,"3"); + dl->AddText(pos4,colorC,"4"); break; } case 2: { // 1+(2>3) > 4 @@ -562,14 +581,14 @@ void FurnaceGUI::drawAlgorithm(unsigned char alg, FurnaceGUIFMAlgs algType, cons ImVec2 pos2=ImLerp(rect.Min,rect.Max,ImVec2(0.25,0.7)); ImVec2 pos3=ImLerp(rect.Min,rect.Max,ImVec2(0.5,0.7)); ImVec2 pos4=ImLerp(rect.Min,rect.Max,ImVec2(0.75,0.5)); - dl->AddCircleFilled(pos1,4.0f*dpiScale+1.0f,color); - dl->AddCircle(pos1,6.0f*dpiScale+1.0f,color); + dl->AddCircleFilled(pos1,4.0f*dpiScale+1.0f,colorM); + dl->AddCircle(pos1,6.0f*dpiScale+1.0f,colorM); addAALine(dl,pos1,pos4,colorL); - dl->AddCircleFilled(pos2,4.0f*dpiScale+1.0f,color); + dl->AddCircleFilled(pos2,4.0f*dpiScale+1.0f,colorM); addAALine(dl,pos2,pos3,colorL); - dl->AddCircleFilled(pos3,4.0f*dpiScale+1.0f,color); + dl->AddCircleFilled(pos3,4.0f*dpiScale+1.0f,colorM); addAALine(dl,pos3,pos4,colorL); - dl->AddCircleFilled(pos4,4.0f*dpiScale+1.0f,color); + dl->AddCircleFilled(pos4,4.0f*dpiScale+1.0f,colorC); pos1.x-=ImGui::CalcTextSize("2").x+circleRadius+3.0*dpiScale; pos2.x-=ImGui::CalcTextSize("2").x+circleRadius+3.0*dpiScale; @@ -579,10 +598,10 @@ void FurnaceGUI::drawAlgorithm(unsigned char alg, FurnaceGUIFMAlgs algType, cons pos2.y-=ImGui::CalcTextSize("2").y*0.5; pos3.y-=ImGui::CalcTextSize("3").y*0.5; pos4.y-=ImGui::CalcTextSize("4").y+circleRadius; - dl->AddText(pos1,color,"1"); - dl->AddText(pos2,color,"2"); - dl->AddText(pos3,color,"3"); - dl->AddText(pos4,color,"4"); + dl->AddText(pos1,colorM,"1"); + dl->AddText(pos2,colorM,"2"); + dl->AddText(pos3,colorM,"3"); + dl->AddText(pos4,colorC,"4"); break; } case 3: { // (1>2)+3 > 4 @@ -590,14 +609,14 @@ void FurnaceGUI::drawAlgorithm(unsigned char alg, FurnaceGUIFMAlgs algType, cons ImVec2 pos2=ImLerp(rect.Min,rect.Max,ImVec2(0.5,0.3)); ImVec2 pos3=ImLerp(rect.Min,rect.Max,ImVec2(0.5,0.7)); ImVec2 pos4=ImLerp(rect.Min,rect.Max,ImVec2(0.75,0.5)); - dl->AddCircleFilled(pos1,4.0f*dpiScale+1.0f,color); - dl->AddCircle(pos1,6.0f*dpiScale+1.0f,color); + dl->AddCircleFilled(pos1,4.0f*dpiScale+1.0f,colorM); + dl->AddCircle(pos1,6.0f*dpiScale+1.0f,colorM); addAALine(dl,pos1,pos2,colorL); - dl->AddCircleFilled(pos2,4.0f*dpiScale+1.0f,color); + dl->AddCircleFilled(pos2,4.0f*dpiScale+1.0f,colorM); addAALine(dl,pos2,pos4,colorL); - dl->AddCircleFilled(pos3,4.0f*dpiScale+1.0f,color); + dl->AddCircleFilled(pos3,4.0f*dpiScale+1.0f,colorM); addAALine(dl,pos3,pos4,colorL); - dl->AddCircleFilled(pos4,4.0f*dpiScale+1.0f,color); + dl->AddCircleFilled(pos4,4.0f*dpiScale+1.0f,colorC); pos1.x-=ImGui::CalcTextSize("2").x+circleRadius+3.0*dpiScale; pos2.x-=ImGui::CalcTextSize("2").x+circleRadius+3.0*dpiScale; @@ -607,10 +626,10 @@ void FurnaceGUI::drawAlgorithm(unsigned char alg, FurnaceGUIFMAlgs algType, cons pos2.y-=ImGui::CalcTextSize("2").y*0.5; pos3.y-=ImGui::CalcTextSize("3").y*0.5; pos4.y-=ImGui::CalcTextSize("4").y+circleRadius; - dl->AddText(pos1,color,"1"); - dl->AddText(pos2,color,"2"); - dl->AddText(pos3,color,"3"); - dl->AddText(pos4,color,"4"); + dl->AddText(pos1,colorM,"1"); + dl->AddText(pos2,colorM,"2"); + dl->AddText(pos3,colorM,"3"); + dl->AddText(pos4,colorC,"4"); break; } case 4: { // (1>2) + (3>4) @@ -619,13 +638,13 @@ void FurnaceGUI::drawAlgorithm(unsigned char alg, FurnaceGUIFMAlgs algType, cons ImVec2 pos3=ImLerp(rect.Min,rect.Max,ImVec2(0.25,0.7)); ImVec2 pos4=ImLerp(rect.Min,rect.Max,ImVec2(0.5,0.7)); ImVec2 pos5=ImLerp(rect.Min,rect.Max,ImVec2(0.75,0.5)); - dl->AddCircleFilled(pos1,4.0f*dpiScale+1.0f,color); - dl->AddCircle(pos1,6.0f*dpiScale+1.0f,color); + dl->AddCircleFilled(pos1,4.0f*dpiScale+1.0f,colorM); + dl->AddCircle(pos1,6.0f*dpiScale+1.0f,colorM); addAALine(dl,pos1,pos2,colorL); - dl->AddCircleFilled(pos2,4.0f*dpiScale+1.0f,color); - dl->AddCircleFilled(pos3,4.0f*dpiScale+1.0f,color); + dl->AddCircleFilled(pos2,4.0f*dpiScale+1.0f,colorC); + dl->AddCircleFilled(pos3,4.0f*dpiScale+1.0f,colorM); addAALine(dl,pos3,pos4,colorL); - dl->AddCircleFilled(pos4,4.0f*dpiScale+1.0f,color); + dl->AddCircleFilled(pos4,4.0f*dpiScale+1.0f,colorC); addAALine(dl,pos2,pos5,colorL); addAALine(dl,pos4,pos5,colorL); @@ -637,10 +656,10 @@ void FurnaceGUI::drawAlgorithm(unsigned char alg, FurnaceGUIFMAlgs algType, cons pos2.y-=ImGui::CalcTextSize("2").y*0.5; pos3.y-=ImGui::CalcTextSize("3").y*0.5; pos4.y-=ImGui::CalcTextSize("4").y*0.5; - dl->AddText(pos1,color,"1"); - dl->AddText(pos2,color,"2"); - dl->AddText(pos3,color,"3"); - dl->AddText(pos4,color,"4"); + dl->AddText(pos1,colorM,"1"); + dl->AddText(pos2,colorC,"2"); + dl->AddText(pos3,colorM,"3"); + dl->AddText(pos4,colorC,"4"); break; } case 5: { // 1 > (2+3+4) @@ -649,14 +668,14 @@ void FurnaceGUI::drawAlgorithm(unsigned char alg, FurnaceGUIFMAlgs algType, cons ImVec2 pos3=ImLerp(rect.Min,rect.Max,ImVec2(0.5,0.5)); ImVec2 pos4=ImLerp(rect.Min,rect.Max,ImVec2(0.5,0.75)); ImVec2 pos5=ImLerp(rect.Min,rect.Max,ImVec2(0.75,0.5)); - dl->AddCircleFilled(pos1,4.0f*dpiScale+1.0f,color); - dl->AddCircle(pos1,6.0f*dpiScale+1.0f,color); + dl->AddCircleFilled(pos1,4.0f*dpiScale+1.0f,colorM); + dl->AddCircle(pos1,6.0f*dpiScale+1.0f,colorM); addAALine(dl,pos1,pos2,colorL); addAALine(dl,pos1,pos3,colorL); addAALine(dl,pos1,pos4,colorL); - dl->AddCircleFilled(pos2,4.0f*dpiScale+1.0f,color); - dl->AddCircleFilled(pos3,4.0f*dpiScale+1.0f,color); - dl->AddCircleFilled(pos4,4.0f*dpiScale+1.0f,color); + dl->AddCircleFilled(pos2,4.0f*dpiScale+1.0f,colorC); + dl->AddCircleFilled(pos3,4.0f*dpiScale+1.0f,colorC); + dl->AddCircleFilled(pos4,4.0f*dpiScale+1.0f,colorC); addAALine(dl,pos2,pos5,colorL); addAALine(dl,pos3,pos5,colorL); addAALine(dl,pos4,pos5,colorL); @@ -669,10 +688,10 @@ void FurnaceGUI::drawAlgorithm(unsigned char alg, FurnaceGUIFMAlgs algType, cons pos2.y-=ImGui::CalcTextSize("2").y*0.5; pos3.y-=ImGui::CalcTextSize("3").y*0.5; pos4.y-=ImGui::CalcTextSize("4").y*0.5; - dl->AddText(pos1,color,"1"); - dl->AddText(pos2,color,"2"); - dl->AddText(pos3,color,"3"); - dl->AddText(pos4,color,"4"); + dl->AddText(pos1,colorM,"1"); + dl->AddText(pos2,colorC,"2"); + dl->AddText(pos3,colorC,"3"); + dl->AddText(pos4,colorC,"4"); break; } case 6: { // (1>2) + 3 + 4 @@ -681,12 +700,12 @@ void FurnaceGUI::drawAlgorithm(unsigned char alg, FurnaceGUIFMAlgs algType, cons ImVec2 pos3=ImLerp(rect.Min,rect.Max,ImVec2(0.5,0.5)); ImVec2 pos4=ImLerp(rect.Min,rect.Max,ImVec2(0.5,0.75)); ImVec2 pos5=ImLerp(rect.Min,rect.Max,ImVec2(0.75,0.5)); - dl->AddCircleFilled(pos1,4.0f*dpiScale+1.0f,color); - dl->AddCircle(pos1,6.0f*dpiScale+1.0f,color); + dl->AddCircleFilled(pos1,4.0f*dpiScale+1.0f,colorM); + dl->AddCircle(pos1,6.0f*dpiScale+1.0f,colorM); addAALine(dl,pos1,pos2,colorL); - dl->AddCircleFilled(pos2,4.0f*dpiScale+1.0f,color); - dl->AddCircleFilled(pos3,4.0f*dpiScale+1.0f,color); - dl->AddCircleFilled(pos4,4.0f*dpiScale+1.0f,color); + dl->AddCircleFilled(pos2,4.0f*dpiScale+1.0f,colorC); + dl->AddCircleFilled(pos3,4.0f*dpiScale+1.0f,colorC); + dl->AddCircleFilled(pos4,4.0f*dpiScale+1.0f,colorC); addAALine(dl,pos2,pos5,colorL); addAALine(dl,pos3,pos5,colorL); addAALine(dl,pos4,pos5,colorL); @@ -699,10 +718,10 @@ void FurnaceGUI::drawAlgorithm(unsigned char alg, FurnaceGUIFMAlgs algType, cons pos2.y-=ImGui::CalcTextSize("2").y*0.5; pos3.y-=ImGui::CalcTextSize("3").y*0.5; pos4.y-=ImGui::CalcTextSize("4").y*0.5; - dl->AddText(pos1,color,"1"); - dl->AddText(pos2,color,"2"); - dl->AddText(pos3,color,"3"); - dl->AddText(pos4,color,"4"); + dl->AddText(pos1,colorM,"1"); + dl->AddText(pos2,colorC,"2"); + dl->AddText(pos3,colorC,"3"); + dl->AddText(pos4,colorC,"4"); break; } case 7: { // 1 + 2 + 3 + 4 @@ -711,11 +730,11 @@ void FurnaceGUI::drawAlgorithm(unsigned char alg, FurnaceGUIFMAlgs algType, cons ImVec2 pos3=ImLerp(rect.Min,rect.Max,ImVec2(0.45,0.6)); ImVec2 pos4=ImLerp(rect.Min,rect.Max,ImVec2(0.55,0.8)); ImVec2 pos5=ImLerp(rect.Min,rect.Max,ImVec2(0.75,0.5)); - dl->AddCircleFilled(pos1,4.0f*dpiScale+1.0f,color); - dl->AddCircle(pos1,6.0f*dpiScale+1.0f,color); - dl->AddCircleFilled(pos2,4.0f*dpiScale+1.0f,color); - dl->AddCircleFilled(pos3,4.0f*dpiScale+1.0f,color); - dl->AddCircleFilled(pos4,4.0f*dpiScale+1.0f,color); + dl->AddCircleFilled(pos1,4.0f*dpiScale+1.0f,colorC); + dl->AddCircle(pos1,6.0f*dpiScale+1.0f,colorC); + dl->AddCircleFilled(pos2,4.0f*dpiScale+1.0f,colorC); + dl->AddCircleFilled(pos3,4.0f*dpiScale+1.0f,colorC); + dl->AddCircleFilled(pos4,4.0f*dpiScale+1.0f,colorC); addAALine(dl,pos1,pos5,colorL); addAALine(dl,pos2,pos5,colorL); addAALine(dl,pos3,pos5,colorL); @@ -729,10 +748,10 @@ void FurnaceGUI::drawAlgorithm(unsigned char alg, FurnaceGUIFMAlgs algType, cons pos2.y-=ImGui::CalcTextSize("2").y*0.5; pos3.y-=ImGui::CalcTextSize("3").y*0.5; pos4.y-=ImGui::CalcTextSize("4").y*0.5; - dl->AddText(pos1,color,"1"); - dl->AddText(pos2,color,"2"); - dl->AddText(pos3,color,"3"); - dl->AddText(pos4,color,"4"); + dl->AddText(pos1,colorC,"1"); + dl->AddText(pos2,colorC,"2"); + dl->AddText(pos3,colorC,"3"); + dl->AddText(pos4,colorC,"4"); break; } } @@ -742,32 +761,32 @@ void FurnaceGUI::drawAlgorithm(unsigned char alg, FurnaceGUIFMAlgs algType, cons case 0: { // 1 > 2 ImVec2 pos1=ImLerp(rect.Min,rect.Max,ImVec2(0.33,0.5)); ImVec2 pos2=ImLerp(rect.Min,rect.Max,ImVec2(0.67,0.5)); - dl->AddCircleFilled(pos1,4.0f*dpiScale+1.0f,color); - dl->AddCircle(pos1,6.0f*dpiScale+1.0f,color); + dl->AddCircleFilled(pos1,4.0f*dpiScale+1.0f,colorM); + dl->AddCircle(pos1,6.0f*dpiScale+1.0f,colorM); addAALine(dl,pos1,pos2,colorL); - dl->AddCircleFilled(pos2,4.0f*dpiScale+1.0f,color); + dl->AddCircleFilled(pos2,4.0f*dpiScale+1.0f,colorC); pos1.x-=ImGui::CalcTextSize("2").x+circleRadius+3.0*dpiScale; pos2.x+=circleRadius+3.0*dpiScale; pos1.y-=ImGui::CalcTextSize("1").y*0.5; pos2.y-=ImGui::CalcTextSize("2").y*0.5; - dl->AddText(pos1,color,"1"); - dl->AddText(pos2,color,"2"); + dl->AddText(pos1,colorM,"1"); + dl->AddText(pos2,colorC,"2"); break; } case 1: { // 1 + 2 ImVec2 pos1=ImLerp(rect.Min,rect.Max,ImVec2(0.33,0.5)); ImVec2 pos2=ImLerp(rect.Min,rect.Max,ImVec2(0.67,0.5)); - dl->AddCircleFilled(pos1,4.0f*dpiScale+1.0f,color); - dl->AddCircle(pos1,6.0f*dpiScale+1.0f,color); - dl->AddCircleFilled(pos2,4.0f*dpiScale+1.0f,color); + dl->AddCircleFilled(pos1,4.0f*dpiScale+1.0f,colorC); + dl->AddCircle(pos1,6.0f*dpiScale+1.0f,colorC); + dl->AddCircleFilled(pos2,4.0f*dpiScale+1.0f,colorC); pos1.x-=ImGui::CalcTextSize("2").x+circleRadius+3.0*dpiScale; pos2.x+=circleRadius+3.0*dpiScale; pos1.y-=ImGui::CalcTextSize("1").y*0.5; pos2.y-=ImGui::CalcTextSize("2").y*0.5; - dl->AddText(pos1,color,"1"); - dl->AddText(pos2,color,"2"); + dl->AddText(pos1,colorC,"1"); + dl->AddText(pos2,colorC,"2"); break; } } @@ -779,14 +798,14 @@ void FurnaceGUI::drawAlgorithm(unsigned char alg, FurnaceGUIFMAlgs algType, cons ImVec2 pos2=ImLerp(rect.Min,rect.Max,ImVec2(0.4,0.5)); ImVec2 pos3=ImLerp(rect.Min,rect.Max,ImVec2(0.6,0.5)); ImVec2 pos4=ImLerp(rect.Min,rect.Max,ImVec2(0.8,0.5)); - dl->AddCircleFilled(pos1,4.0f*dpiScale+1.0f,color); - dl->AddCircle(pos1,6.0f*dpiScale+1.0f,color); + dl->AddCircleFilled(pos1,4.0f*dpiScale+1.0f,colorM); + dl->AddCircle(pos1,6.0f*dpiScale+1.0f,colorM); addAALine(dl,pos1,pos2,colorL); - dl->AddCircleFilled(pos2,4.0f*dpiScale+1.0f,color); + dl->AddCircleFilled(pos2,4.0f*dpiScale+1.0f,colorM); addAALine(dl,pos2,pos3,colorL); - dl->AddCircleFilled(pos3,4.0f*dpiScale+1.0f,color); + dl->AddCircleFilled(pos3,4.0f*dpiScale+1.0f,colorM); addAALine(dl,pos3,pos4,colorL); - dl->AddCircleFilled(pos4,4.0f*dpiScale+1.0f,color); + dl->AddCircleFilled(pos4,4.0f*dpiScale+1.0f,colorC); pos1.x-=ImGui::CalcTextSize("1").x*0.5; pos2.x-=ImGui::CalcTextSize("2").x*0.5; @@ -796,10 +815,10 @@ void FurnaceGUI::drawAlgorithm(unsigned char alg, FurnaceGUIFMAlgs algType, cons pos2.y-=ImGui::CalcTextSize("2").y+circleRadius; pos3.y-=ImGui::CalcTextSize("3").y+circleRadius; pos4.y-=ImGui::CalcTextSize("4").y+circleRadius; - dl->AddText(pos1,color,"1"); - dl->AddText(pos2,color,"2"); - dl->AddText(pos3,color,"3"); - dl->AddText(pos4,color,"4"); + dl->AddText(pos1,colorM,"1"); + dl->AddText(pos2,colorM,"2"); + dl->AddText(pos3,colorM,"3"); + dl->AddText(pos4,colorC,"4"); break; } case 1: { // 1 + (2 > 3 > 4) @@ -808,14 +827,14 @@ void FurnaceGUI::drawAlgorithm(unsigned char alg, FurnaceGUIFMAlgs algType, cons ImVec2 pos3=ImLerp(rect.Min,rect.Max,ImVec2(0.4,0.7)); ImVec2 pos4=ImLerp(rect.Min,rect.Max,ImVec2(0.6,0.7)); ImVec2 pos5=ImLerp(rect.Min,rect.Max,ImVec2(0.8,0.7)); - dl->AddCircleFilled(pos1,4.0f*dpiScale+1.0f,color); - dl->AddCircle(pos1,6.0f*dpiScale+1.0f,color); + dl->AddCircleFilled(pos1,4.0f*dpiScale+1.0f,colorC); + dl->AddCircle(pos1,6.0f*dpiScale+1.0f,colorC); addAALine(dl,pos1,pos5,colorL); - dl->AddCircleFilled(pos2,4.0f*dpiScale+1.0f,color); + dl->AddCircleFilled(pos2,4.0f*dpiScale+1.0f,colorM); addAALine(dl,pos2,pos3,colorL); - dl->AddCircleFilled(pos3,4.0f*dpiScale+1.0f,color); + dl->AddCircleFilled(pos3,4.0f*dpiScale+1.0f,colorM); addAALine(dl,pos3,pos4,colorL); - dl->AddCircleFilled(pos4,4.0f*dpiScale+1.0f,color); + dl->AddCircleFilled(pos4,4.0f*dpiScale+1.0f,colorC); addAALine(dl,pos4,pos5,colorL); @@ -827,10 +846,10 @@ void FurnaceGUI::drawAlgorithm(unsigned char alg, FurnaceGUIFMAlgs algType, cons pos2.y-=ImGui::CalcTextSize("2").y*0.5; pos3.y-=ImGui::CalcTextSize("3").y*0.5; pos4.y-=ImGui::CalcTextSize("4").y+circleRadius; - dl->AddText(pos1,color,"1"); - dl->AddText(pos2,color,"2"); - dl->AddText(pos3,color,"3"); - dl->AddText(pos4,color,"4"); + dl->AddText(pos1,colorC,"1"); + dl->AddText(pos2,colorM,"2"); + dl->AddText(pos3,colorM,"3"); + dl->AddText(pos4,colorC,"4"); break; } case 2: { // (1>2) + (3>4) @@ -839,13 +858,13 @@ void FurnaceGUI::drawAlgorithm(unsigned char alg, FurnaceGUIFMAlgs algType, cons ImVec2 pos3=ImLerp(rect.Min,rect.Max,ImVec2(0.25,0.7)); ImVec2 pos4=ImLerp(rect.Min,rect.Max,ImVec2(0.5,0.7)); ImVec2 pos5=ImLerp(rect.Min,rect.Max,ImVec2(0.75,0.5)); - dl->AddCircleFilled(pos1,4.0f*dpiScale+1.0f,color); - dl->AddCircle(pos1,6.0f*dpiScale+1.0f,color); + dl->AddCircleFilled(pos1,4.0f*dpiScale+1.0f,colorM); + dl->AddCircle(pos1,6.0f*dpiScale+1.0f,colorM); addAALine(dl,pos1,pos2,colorL); - dl->AddCircleFilled(pos2,4.0f*dpiScale+1.0f,color); - dl->AddCircleFilled(pos3,4.0f*dpiScale+1.0f,color); + dl->AddCircleFilled(pos2,4.0f*dpiScale+1.0f,colorC); + dl->AddCircleFilled(pos3,4.0f*dpiScale+1.0f,colorM); addAALine(dl,pos3,pos4,colorL); - dl->AddCircleFilled(pos4,4.0f*dpiScale+1.0f,color); + dl->AddCircleFilled(pos4,4.0f*dpiScale+1.0f,colorC); addAALine(dl,pos2,pos5,colorL); addAALine(dl,pos4,pos5,colorL); @@ -857,10 +876,10 @@ void FurnaceGUI::drawAlgorithm(unsigned char alg, FurnaceGUIFMAlgs algType, cons pos2.y-=ImGui::CalcTextSize("2").y*0.5; pos3.y-=ImGui::CalcTextSize("3").y*0.5; pos4.y-=ImGui::CalcTextSize("4").y*0.5; - dl->AddText(pos1,color,"1"); - dl->AddText(pos2,color,"2"); - dl->AddText(pos3,color,"3"); - dl->AddText(pos4,color,"4"); + dl->AddText(pos1,colorM,"1"); + dl->AddText(pos2,colorC,"2"); + dl->AddText(pos3,colorM,"3"); + dl->AddText(pos4,colorC,"4"); break; } case 3: { // 1 + (2 > 3) + 4 @@ -869,12 +888,12 @@ void FurnaceGUI::drawAlgorithm(unsigned char alg, FurnaceGUIFMAlgs algType, cons ImVec2 pos3=ImLerp(rect.Min,rect.Max,ImVec2(0.5,0.5)); ImVec2 pos4=ImLerp(rect.Min,rect.Max,ImVec2(0.5,0.75)); ImVec2 pos5=ImLerp(rect.Min,rect.Max,ImVec2(0.75,0.5)); - dl->AddCircleFilled(pos1,4.0f*dpiScale+1.0f,color); - dl->AddCircle(pos1,6.0f*dpiScale+1.0f,color); + dl->AddCircleFilled(pos1,4.0f*dpiScale+1.0f,colorC); + dl->AddCircle(pos1,6.0f*dpiScale+1.0f,colorC); addAALine(dl,pos2,pos3,colorL); - dl->AddCircleFilled(pos2,4.0f*dpiScale+1.0f,color); - dl->AddCircleFilled(pos3,4.0f*dpiScale+1.0f,color); - dl->AddCircleFilled(pos4,4.0f*dpiScale+1.0f,color); + dl->AddCircleFilled(pos2,4.0f*dpiScale+1.0f,colorM); + dl->AddCircleFilled(pos3,4.0f*dpiScale+1.0f,colorC); + dl->AddCircleFilled(pos4,4.0f*dpiScale+1.0f,colorC); addAALine(dl,pos1,pos5,colorL); addAALine(dl,pos3,pos5,colorL); addAALine(dl,pos4,pos5,colorL); @@ -887,10 +906,10 @@ void FurnaceGUI::drawAlgorithm(unsigned char alg, FurnaceGUIFMAlgs algType, cons pos2.y-=ImGui::CalcTextSize("2").y*0.5; pos3.y-=ImGui::CalcTextSize("3").y*0.5; pos4.y-=ImGui::CalcTextSize("4").y*0.5; - dl->AddText(pos1,color,"1"); - dl->AddText(pos2,color,"2"); - dl->AddText(pos3,color,"3"); - dl->AddText(pos4,color,"4"); + dl->AddText(pos1,colorC,"1"); + dl->AddText(pos2,colorM,"2"); + dl->AddText(pos3,colorC,"3"); + dl->AddText(pos4,colorC,"4"); break; } } @@ -912,8 +931,9 @@ void FurnaceGUI::drawFMEnv(unsigned char tl, unsigned char ar, unsigned char dr, ); ImRect rect=ImRect(minArea,maxArea); ImGuiStyle& style=ImGui::GetStyle(); - ImU32 color=ImGui::GetColorU32(uiColors[GUI_COLOR_TEXT]); - ImU32 colorS=ImGui::GetColorU32(uiColors[GUI_COLOR_SONG_LOOP]); //Relsease triangle and sustain horiz/vert line color + ImU32 color=ImGui::GetColorU32(uiColors[GUI_COLOR_FM_ENVELOPE]); + ImU32 colorR=ImGui::GetColorU32(uiColors[GUI_COLOR_FM_ENVELOPE_RELEASE]); // Relsease triangle + ImU32 colorS=ImGui::GetColorU32(uiColors[GUI_COLOR_FM_ENVELOPE_SUS_GUIDE]); // Sustain horiz/vert line color ImGui::ItemSize(size,style.FramePadding.y); if (ImGui::ItemAdd(rect,ImGui::GetID("alg"))) { ImGui::RenderFrame(rect.Min,rect.Max,ImGui::GetColorU32(ImGuiCol_FrameBg),true,style.FrameRounding); @@ -957,15 +977,15 @@ void FurnaceGUI::drawFMEnv(unsigned char tl, unsigned char ar, unsigned char dr, addAALine(dl,pos2,posDecayRate0Pt,color); //Line from A to end of graph } else if (d2r==0.0 || (instType==DIV_INS_OPL && sus==1.0) || (instType==DIV_INS_OPLL && egt!=0.0)) { //envelope stays at the sustain level forever dl->AddTriangleFilled(posRStart,posREnd,pos1,colorS); //draw release as shaded triangle behind everything - addAALine(dl,pos3,posSLineHEnd,colorS); //draw horiz line through sustain level - addAALine(dl,pos3,posSLineVEnd,colorS); //draw vert. line through sustain level + addAALine(dl,pos3,posSLineHEnd,colorR); //draw horiz line through sustain level + addAALine(dl,pos3,posSLineVEnd,colorR); //draw vert. line through sustain level addAALine(dl,pos1,pos2,color); //A addAALine(dl,pos2,pos3,color); //D addAALine(dl,pos3,posDecay2Rate0Pt,color); //Line from D to end of graph } else { //draw graph normally dl->AddTriangleFilled(posRStart,posREnd,pos1,colorS); //draw release as shaded triangle behind everything - addAALine(dl,pos3,posSLineHEnd,colorS); //draw horiz line through sustain level - addAALine(dl,pos3,posSLineVEnd,colorS); //draw vert. line through sustain level + addAALine(dl,pos3,posSLineHEnd,colorR); //draw horiz line through sustain level + addAALine(dl,pos3,posSLineVEnd,colorR); //draw vert. line through sustain level addAALine(dl,pos1,pos2,color); //A addAALine(dl,pos2,pos3,color); //D addAALine(dl,pos3,pos4,color); //D2 @@ -1594,6 +1614,35 @@ void FurnaceGUI::drawInsEdit() { ImGui::TableNextRow(); ImGui::TableNextColumn(); + // push colors + if (settings.separateFMColors) { + bool mod=true; + if (opCount==4) { + if (ins->type==DIV_INS_OPL) { + if (opIsOutputOPL[ins->fm.alg&3][i]) mod=false; + } else { + if (opIsOutput[ins->fm.alg&7][i]) mod=false; + } + } else { + if (i==1 || (ins->type==DIV_INS_OPL && (ins->fm.alg&1))) mod=false; + } + if (mod) { + pushAccentColors( + uiColors[GUI_COLOR_FM_PRIMARY_MOD], + uiColors[GUI_COLOR_FM_SECONDARY_MOD], + uiColors[GUI_COLOR_FM_BORDER_MOD], + uiColors[GUI_COLOR_FM_BORDER_SHADOW_MOD] + ); + } else { + pushAccentColors( + uiColors[GUI_COLOR_FM_PRIMARY_CAR], + uiColors[GUI_COLOR_FM_SECONDARY_CAR], + uiColors[GUI_COLOR_FM_BORDER_CAR], + uiColors[GUI_COLOR_FM_BORDER_SHADOW_CAR] + ); + } + } + if (i==0) sliderHeight=(ImGui::GetContentRegionAvail().y/opCount)-ImGui::GetStyle().ItemSpacing.y; ImGui::PushID(fmt::sprintf("op%d",i).c_str()); @@ -1814,6 +1863,10 @@ void FurnaceGUI::drawInsEdit() { ImGui::TableNextColumn(); drawFMEnv(op.tl&maxTl,op.ar&maxArDr,op.dr&maxArDr,(ins->type==DIV_INS_OPL || ins->type==DIV_INS_OPLL)?((op.rr&15)*2):op.d2r&31,op.rr&15,op.sl&15,op.sus,op.ssgEnv&8,ins->fm.alg,maxTl,maxArDr,ImVec2(ImGui::GetContentRegionAvail().x,sliderHeight),ins->type); + if (settings.separateFMColors) { + popAccentColors(); + } + ImGui::PopID(); } @@ -1839,6 +1892,36 @@ void FurnaceGUI::drawInsEdit() { ImGui::TableNextColumn(); ImGui::Separator(); ImGui::PushID(fmt::sprintf("op%d",i).c_str()); + + // push colors + if (settings.separateFMColors) { + bool mod=true; + if (opCount==4) { + if (ins->type==DIV_INS_OPL) { + if (opIsOutputOPL[ins->fm.alg&3][i]) mod=false; + } else { + if (opIsOutput[ins->fm.alg&7][i]) mod=false; + } + } else { + if (i==1 || (ins->type==DIV_INS_OPL && (ins->fm.alg&1))) mod=false; + } + if (mod) { + pushAccentColors( + uiColors[GUI_COLOR_FM_PRIMARY_MOD], + uiColors[GUI_COLOR_FM_SECONDARY_MOD], + uiColors[GUI_COLOR_FM_BORDER_MOD], + uiColors[GUI_COLOR_FM_BORDER_SHADOW_MOD] + ); + } else { + pushAccentColors( + uiColors[GUI_COLOR_FM_PRIMARY_CAR], + uiColors[GUI_COLOR_FM_SECONDARY_CAR], + uiColors[GUI_COLOR_FM_BORDER_CAR], + uiColors[GUI_COLOR_FM_BORDER_SHADOW_CAR] + ); + } + } + ImGui::Dummy(ImVec2(dpiScale,dpiScale)); if (ins->type==DIV_INS_OPL && ins->fm.opllPreset==16) { if (i==1) { @@ -2043,6 +2126,10 @@ void FurnaceGUI::drawInsEdit() { } } + if (settings.separateFMColors) { + popAccentColors(); + } + ImGui::PopID(); } ImGui::EndTable(); diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index aa6dcb503..421c8f21b 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -834,6 +834,11 @@ void FurnaceGUI::drawSettings() { ImGui::Separator(); + bool separateFMColorsB=settings.separateFMColors; + if (ImGui::Checkbox("Use separate colors for carriers/modulators in FM editor",&separateFMColorsB)) { + settings.separateFMColors=separateFMColorsB; + } + bool macroViewB=settings.macroView; if (ImGui::Checkbox("Classic macro view (standard macros only; deprecated!)",¯oViewB)) { settings.macroView=macroViewB; @@ -999,6 +1004,32 @@ void FurnaceGUI::drawSettings() { UI_COLOR_CONFIG(GUI_COLOR_ORDER_INACTIVE,"Inactive patterns"); ImGui::TreePop(); } + if (ImGui::TreeNode("FM Editor")) { + UI_COLOR_CONFIG(GUI_COLOR_FM_ALG_BG,"Algorithm background"); + UI_COLOR_CONFIG(GUI_COLOR_FM_ALG_LINE,"Algorithm lines"); + UI_COLOR_CONFIG(GUI_COLOR_FM_MOD,"Modulator"); + UI_COLOR_CONFIG(GUI_COLOR_FM_CAR,"Carrier"); + + UI_COLOR_CONFIG(GUI_COLOR_FM_ENVELOPE,"Envelope"); + UI_COLOR_CONFIG(GUI_COLOR_FM_ENVELOPE_SUS_GUIDE,"Sustain guide"); + UI_COLOR_CONFIG(GUI_COLOR_FM_ENVELOPE_RELEASE,"Release"); + UI_COLOR_CONFIG(GUI_COLOR_FM_SSG,"SSG-EG"); + UI_COLOR_CONFIG(GUI_COLOR_FM_WAVE,"Waveform"); + + ImGui::TextWrapped("(the following colors only apply when \"Use separate colors for carriers/modulators in FM editor\" is on!)"); + + UI_COLOR_CONFIG(GUI_COLOR_FM_PRIMARY_MOD,"Mod. accent (primary)"); + UI_COLOR_CONFIG(GUI_COLOR_FM_SECONDARY_MOD,"Mod. accent (secondary)"); + UI_COLOR_CONFIG(GUI_COLOR_FM_BORDER_MOD,"Mod. border"); + UI_COLOR_CONFIG(GUI_COLOR_FM_BORDER_SHADOW_MOD,"Mod. border shadow"); + + UI_COLOR_CONFIG(GUI_COLOR_FM_PRIMARY_CAR,"Car. accent (primary"); + UI_COLOR_CONFIG(GUI_COLOR_FM_SECONDARY_CAR,"Car. accent (secondary)"); + UI_COLOR_CONFIG(GUI_COLOR_FM_BORDER_CAR,"Car. border"); + UI_COLOR_CONFIG(GUI_COLOR_FM_BORDER_SHADOW_CAR,"Car. border shadow"); + + ImGui::TreePop(); + } if (ImGui::TreeNode("Macro Editor")) { UI_COLOR_CONFIG(GUI_COLOR_MACRO_VOLUME,"Volume"); UI_COLOR_CONFIG(GUI_COLOR_MACRO_PITCH,"Pitch"); @@ -1521,6 +1552,7 @@ void FurnaceGUI::syncSettings() { settings.oscRoundedCorners=e->getConfInt("oscRoundedCorners",1); settings.oscTakesEntireWindow=e->getConfInt("oscTakesEntireWindow",0); settings.oscBorder=e->getConfInt("oscBorder",1); + settings.separateFMColors=e->getConfInt("separateFMColors",0); clampSetting(settings.mainFontSize,2,96); clampSetting(settings.patFontSize,2,96); @@ -1574,6 +1606,7 @@ void FurnaceGUI::syncSettings() { clampSetting(settings.frameBorders,0,1); clampSetting(settings.effectDeletionAltersValue,0,1); clampSetting(settings.sampleLayout,0,1); + clampSetting(settings.separateFMColors,0,1); // keybinds for (int i=0; isetConf("oscRoundedCorners",settings.oscRoundedCorners); e->setConf("oscTakesEntireWindow",settings.oscTakesEntireWindow); e->setConf("oscBorder",settings.oscBorder); + e->setConf("separateFMColors",settings.separateFMColors); // colors for (int i=0; i Date: Tue, 12 Apr 2022 21:30:12 -0500 Subject: [PATCH 042/342] GUI: add Colorize instrument editor using instrume nt type option --- src/gui/gui.h | 6 ++++-- src/gui/guiConst.cpp | 4 ++-- src/gui/insEdit.cpp | 6 ++++++ src/gui/settings.cpp | 8 ++++++++ 4 files changed, 20 insertions(+), 4 deletions(-) diff --git a/src/gui/gui.h b/src/gui/gui.h index 7c0b04fcd..8bfd9f8ae 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -117,8 +117,8 @@ enum FurnaceGUIColors { GUI_COLOR_MACRO_OTHER, GUI_COLOR_MACRO_WAVE, - GUI_COLOR_INSTR_FM, GUI_COLOR_INSTR_STD, + GUI_COLOR_INSTR_FM, GUI_COLOR_INSTR_GB, GUI_COLOR_INSTR_C64, GUI_COLOR_INSTR_AMIGA, @@ -130,7 +130,6 @@ enum FurnaceGUIColors { GUI_COLOR_INSTR_VIC, GUI_COLOR_INSTR_PET, GUI_COLOR_INSTR_VRC6, - GUI_COLOR_INSTR_VRC6_SAW, GUI_COLOR_INSTR_OPLL, GUI_COLOR_INSTR_OPL, GUI_COLOR_INSTR_FDS, @@ -144,6 +143,7 @@ enum FurnaceGUIColors { GUI_COLOR_INSTR_MIKEY, GUI_COLOR_INSTR_VERA, GUI_COLOR_INSTR_X1_010, + GUI_COLOR_INSTR_VRC6_SAW, GUI_COLOR_INSTR_UNKNOWN, GUI_COLOR_CHANNEL_FM, @@ -807,6 +807,7 @@ class FurnaceGUI { int oscTakesEntireWindow; int oscBorder; int separateFMColors; + int insEditColorize; unsigned int maxUndoSteps; String mainFontPath; String patFontPath; @@ -872,6 +873,7 @@ class FurnaceGUI { oscTakesEntireWindow(0), oscBorder(1), separateFMColors(0), + insEditColorize(0), maxUndoSteps(100), mainFontPath(""), patFontPath(""), diff --git a/src/gui/guiConst.cpp b/src/gui/guiConst.cpp index 8f48e63af..481ce6f20 100644 --- a/src/gui/guiConst.cpp +++ b/src/gui/guiConst.cpp @@ -422,8 +422,8 @@ const FurnaceGUIColorDef guiColors[GUI_COLOR_MAX]={ D(GUI_COLOR_MACRO_OTHER,"",ImVec4(0.0f,0.9f,1.0f,1.0f)), D(GUI_COLOR_MACRO_WAVE,"",ImVec4(1.0f,0.4f,0.0f,1.0f)), - D(GUI_COLOR_INSTR_FM,"",ImVec4(0.6f,0.9f,1.0f,1.0f)), D(GUI_COLOR_INSTR_STD,"",ImVec4(0.6f,1.0f,0.5f,1.0f)), + D(GUI_COLOR_INSTR_FM,"",ImVec4(0.6f,0.9f,1.0f,1.0f)), D(GUI_COLOR_INSTR_GB,"",ImVec4(1.0f,1.0f,0.5f,1.0f)), D(GUI_COLOR_INSTR_C64,"",ImVec4(0.85f,0.8f,1.0f,1.0f)), D(GUI_COLOR_INSTR_AMIGA,"",ImVec4(1.0f,0.5f,0.5f,1.0f)), @@ -435,7 +435,6 @@ const FurnaceGUIColorDef guiColors[GUI_COLOR_MAX]={ D(GUI_COLOR_INSTR_VIC,"",ImVec4(0.2f,1.0f,0.6f,1.0f)), D(GUI_COLOR_INSTR_PET,"",ImVec4(1.0f,1.0f,0.8f,1.0f)), D(GUI_COLOR_INSTR_VRC6,"",ImVec4(1.0f,0.9f,0.5f,1.0f)), - D(GUI_COLOR_INSTR_VRC6_SAW,"",ImVec4(0.8f,0.3f,0.0f,1.0f)), D(GUI_COLOR_INSTR_OPLL,"",ImVec4(0.6f,0.7f,1.0f,1.0f)), D(GUI_COLOR_INSTR_OPL,"",ImVec4(0.3f,1.0f,0.9f,1.0f)), D(GUI_COLOR_INSTR_FDS,"",ImVec4(0.8f,0.5f,1.0f,1.0f)), @@ -449,6 +448,7 @@ const FurnaceGUIColorDef guiColors[GUI_COLOR_MAX]={ D(GUI_COLOR_INSTR_MIKEY,"",ImVec4(0.5f,1.0f,0.3f,1.0f)), D(GUI_COLOR_INSTR_VERA,"",ImVec4(0.4f,0.6f,1.0f,1.0f)), D(GUI_COLOR_INSTR_X1_010,"",ImVec4(0.3f,0.5f,1.0f,1.0f)), + D(GUI_COLOR_INSTR_VRC6_SAW,"",ImVec4(0.8f,0.3f,0.0f,1.0f)), D(GUI_COLOR_INSTR_UNKNOWN,"",ImVec4(0.3f,0.3f,0.3f,1.0f)), D(GUI_COLOR_CHANNEL_FM,"",ImVec4(0.2f,0.8f,1.0f,1.0f)), diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index fa836c9ff..d7ad60113 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -1292,6 +1292,9 @@ void FurnaceGUI::drawInsEdit() { ImGui::Text("no instrument selected"); } else { DivInstrument* ins=e->song.ins[curIns]; + 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)); + } if (ImGui::BeginTable("InsProp",3)) { ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthFixed); ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthFixed); @@ -3050,6 +3053,9 @@ void FurnaceGUI::drawInsEdit() { } ImGui::EndTabBar(); } + if (settings.insEditColorize) { + popAccentColors(); + } } } if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_INS_EDIT; diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index 421c8f21b..f1da3717d 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -834,6 +834,11 @@ void FurnaceGUI::drawSettings() { ImGui::Separator(); + bool insEditColorizeB=settings.insEditColorize; + if (ImGui::Checkbox("Colorize instrument editor using instrument type",&insEditColorizeB)) { + settings.insEditColorize=insEditColorizeB; + } + bool separateFMColorsB=settings.separateFMColors; if (ImGui::Checkbox("Use separate colors for carriers/modulators in FM editor",&separateFMColorsB)) { settings.separateFMColors=separateFMColorsB; @@ -1553,6 +1558,7 @@ void FurnaceGUI::syncSettings() { settings.oscTakesEntireWindow=e->getConfInt("oscTakesEntireWindow",0); settings.oscBorder=e->getConfInt("oscBorder",1); settings.separateFMColors=e->getConfInt("separateFMColors",0); + settings.insEditColorize=e->getConfInt("insEditColorize",0); clampSetting(settings.mainFontSize,2,96); clampSetting(settings.patFontSize,2,96); @@ -1607,6 +1613,7 @@ void FurnaceGUI::syncSettings() { clampSetting(settings.effectDeletionAltersValue,0,1); clampSetting(settings.sampleLayout,0,1); clampSetting(settings.separateFMColors,0,1); + clampSetting(settings.insEditColorize,0,1); // keybinds for (int i=0; isetConf("oscTakesEntireWindow",settings.oscTakesEntireWindow); e->setConf("oscBorder",settings.oscBorder); e->setConf("separateFMColors",settings.separateFMColors); + e->setConf("insEditColorize",settings.insEditColorize); // colors for (int i=0; i Date: Tue, 12 Apr 2022 21:30:35 -0500 Subject: [PATCH 043/342] SMS: fix note slides on upper notes hopefully this doesn't break anything --- src/engine/platform/sms.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/engine/platform/sms.cpp b/src/engine/platform/sms.cpp index f4bbc46e1..2189eba29 100644 --- a/src/engine/platform/sms.cpp +++ b/src/engine/platform/sms.cpp @@ -96,7 +96,8 @@ void DivPlatformSMS::tick() { if (chan[i].freqChanged) { chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true); if (chan[i].freq>1023) chan[i].freq=1023; - if (chan[i].actualNote>0x5d) chan[i].freq=0x01; + if (chan[i].freq<8) chan[i].freq=1; + //if (chan[i].actualNote>0x5d) chan[i].freq=0x01; rWrite(0x80|i<<5|(chan[i].freq&15)); rWrite(chan[i].freq>>4); // what? From ff75c7fe019b49a7e64491f39b4662cc99965929 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Tue, 12 Apr 2022 23:03:20 -0500 Subject: [PATCH 044/342] GUI: implement macro line drawing --- src/gui/gui.cpp | 28 +++++++++++++++++++++++- src/gui/gui.h | 3 ++- src/gui/insEdit.cpp | 52 +++++++++++++++------------------------------ 3 files changed, 46 insertions(+), 37 deletions(-) diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index f577691f5..8924748e2 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -1721,7 +1721,31 @@ void FurnaceGUI::showError(String what) { t[x]&=(1<127) macro.len=127; \ } \ if (macroMode) { \ - String modeName; \ - if (macro.mode>macroModeMax) { \ - modeName="none selected"; \ - } else { \ - modeName=displayModeName[macro.mode]; \ - } \ - if (ImGui::BeginCombo("TODO: Improve##IMacroMode_" macroName,modeName.c_str())) { \ - String id; \ - for (unsigned int i=0; i<=macroModeMax; i++) { \ - id=fmt::sprintf("%d: %s",i,displayModeName[i]); \ - if (ImGui::Selectable(id.c_str(),macro.mode==i)) { PARAMETER \ - macro.mode=i; \ - } \ - } \ - ImGui::EndCombo(); \ + bool modeVal=macro.mode; \ + if (ImGui::Checkbox("Relative##IMacroMode_" macroName,&modeVal)) { \ + macro.mode=modeVal; \ } \ } \ } \ @@ -1055,7 +1043,7 @@ void FurnaceGUI::drawFMEnv(unsigned char tl, unsigned char ar, unsigned char dr, } else { \ PlotCustom("##IMacro_" macroName,asFloat,totalFit,macroDragScroll,NULL,macroDispMin+macroMin,macroHeight+macroDispMin,ImVec2(availableWidth,(displayLoop)?(displayHeight*dpiScale):(32.0f*dpiScale)),sizeof(float),macroColor,macro.len-macroDragScroll,hoverFunc,blockMode); \ } \ - if (displayLoop && ImGui::IsItemClicked(ImGuiMouseButton_Left)) { \ + if (displayLoop && (ImGui::IsItemClicked(ImGuiMouseButton_Left) || ImGui::IsItemClicked(ImGuiMouseButton_Right))) { \ macroDragStart=ImGui::GetItemRectMin(); \ macroDragAreaSize=ImVec2(availableWidth,displayHeight*dpiScale); \ macroDragMin=macroMin; \ @@ -1068,6 +1056,8 @@ void FurnaceGUI::drawFMEnv(unsigned char tl, unsigned char ar, unsigned char dr, macroDragActive=true; \ macroDragTarget=macro.val; \ macroDragChar=false; \ + macroDragLineMode=(bitfield)?false:ImGui::IsItemClicked(ImGuiMouseButton_Right); \ + macroDragLineInitial=ImVec2(0,0); \ processDrags(ImGui::GetMousePos().x,ImGui::GetMousePos().y); \ } \ if (displayLoop) { \ @@ -1119,21 +1109,9 @@ void FurnaceGUI::drawFMEnv(unsigned char tl, unsigned char ar, unsigned char dr, if (macro.len>127) macro.len=127; \ } \ if (macroMode) { \ - String modeName; \ - if (macro.mode>macroModeMax) { \ - modeName="none selected"; \ - } else { \ - modeName=displayModeName[macro.mode]; \ - } \ - if (ImGui::BeginCombo("TODO: Improve##IOPMacroMode_" macroName,modeName.c_str())) { \ - String id; \ - for (unsigned int i=0; i<=macroModeMax; i++) { \ - id=fmt::sprintf("%d: %s",i,displayModeName[i]); \ - if (ImGui::Selectable(id.c_str(),macro.mode==i)) { PARAMETER \ - macro.mode=i; \ - } \ - } \ - ImGui::EndCombo(); \ + bool modeVal=macro.mode; \ + if (ImGui::Checkbox("Relative##IOPMacroMode_" macroName,&modeVal)) { \ + macro.mode=modeVal; \ } \ } \ } \ @@ -1159,7 +1137,7 @@ void FurnaceGUI::drawFMEnv(unsigned char tl, unsigned char ar, unsigned char dr, } else { \ PlotCustom("##IOPMacro_" #op macroName,asFloat,totalFit,macroDragScroll,NULL,0,macroHeight,ImVec2(availableWidth,displayLoop?(displayHeight*dpiScale):(24*dpiScale)),sizeof(float),uiColors[GUI_COLOR_MACRO_OTHER],macro.len-macroDragScroll); \ } \ - if (displayLoop && ImGui::IsItemClicked(ImGuiMouseButton_Left)) { \ + if (displayLoop && (ImGui::IsItemClicked(ImGuiMouseButton_Left) || ImGui::IsItemClicked(ImGuiMouseButton_Right))) { \ macroDragStart=ImGui::GetItemRectMin(); \ macroDragAreaSize=ImVec2(availableWidth,displayHeight*dpiScale); \ macroDragMin=0; \ @@ -1172,6 +1150,8 @@ void FurnaceGUI::drawFMEnv(unsigned char tl, unsigned char ar, unsigned char dr, macroDragActive=true; \ macroDragTarget=macro.val; \ macroDragChar=false; \ + macroDragLineMode=(bitfield)?false:ImGui::IsItemClicked(ImGuiMouseButton_Right); \ + macroDragLineInitial=ImVec2(0,0); \ processDrags(ImGui::GetMousePos().x,ImGui::GetMousePos().y); \ } \ if (displayLoop) { \ @@ -2480,7 +2460,9 @@ void FurnaceGUI::drawInsEdit() { macroDragActive=true; macroDragCTarget=(unsigned char*)ins->fds.modTable; macroDragChar=true; - processDrags(ImGui::GetMousePos().x,ImGui::GetMousePos().y); \ + macroDragLineMode=false; + macroDragLineInitial=ImVec2(0,0); + processDrags(ImGui::GetMousePos().x,ImGui::GetMousePos().y); } ImGui::EndTabItem(); } @@ -2742,7 +2724,7 @@ void FurnaceGUI::drawInsEdit() { if (ins->type==DIV_INS_MIKEY) { NORMAL_MACRO(ins->std.dutyMacro,0,dutyMax,"duty",dutyLabel,160,ins->std.dutyMacro.open,true,mikeyFeedbackBits,false,NULL,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[2],0,dutyMax,NULL,false); } else if (ins->type==DIV_INS_C64) { - NORMAL_MACRO(ins->std.dutyMacro,0,dutyMax,"duty",dutyLabel,160,ins->std.dutyMacro.open,false,NULL,false,NULL,0,0,0,true,1,macroAbsoluteMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[2],0,dutyMax,NULL,false); + NORMAL_MACRO(ins->std.dutyMacro,0,dutyMax,"duty",dutyLabel,160,ins->std.dutyMacro.open,false,NULL,false,NULL,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[2],0,dutyMax,NULL,false); } else { NORMAL_MACRO(ins->std.dutyMacro,0,dutyMax,"duty",dutyLabel,160,ins->std.dutyMacro.open,false,NULL,false,NULL,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[2],0,dutyMax,NULL,false); } @@ -2799,7 +2781,7 @@ void FurnaceGUI::drawInsEdit() { } MACRO_END; - } else { // classic view + } else { // classic view (TODO: possibly remove) // volume macro ImGui::Separator(); if (ins->type==DIV_INS_C64 && ins->c64.volIsCutoff) { From aab7cf25125b338ebe9b6620aabec2240eb2b6a1 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Tue, 12 Apr 2022 23:03:24 -0500 Subject: [PATCH 045/342] oops --- src/gui/insEdit.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index 9527542bb..c4a3126ed 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -1257,8 +1257,6 @@ if (ImGui::BeginTable("MacroSpace",2)) { \ #define CENTER_VSLIDER \ ImGui::SetCursorPosX(ImGui::GetCursorPosX()+0.5f*ImGui::GetContentRegionAvail().x-10.0f*dpiScale); -// TODO: -// - add right click line draw in macro editor void FurnaceGUI::drawInsEdit() { if (nextWindow==GUI_WINDOW_INS_EDIT) { insEditOpen=true; From fbf7f9304e0563143ce0c47be8b8f4f0253b6539 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 13 Apr 2022 00:34:00 -0500 Subject: [PATCH 046/342] dev83 - yet another E1xy/E2xy fix and 17xx fix in ExtCh --- papers/format.md | 5 ++++- src/engine/engine.h | 4 ++-- src/engine/fileOps.cpp | 15 +++++++++++++-- src/engine/platform/genesisext.cpp | 14 +++++++++++++- src/engine/playback.cpp | 2 ++ src/engine/song.h | 6 +++++- src/gui/compatFlags.cpp | 8 ++++++++ 7 files changed, 47 insertions(+), 7 deletions(-) diff --git a/papers/format.md b/papers/format.md index e8f076176..2f4037a06 100644 --- a/papers/format.md +++ b/papers/format.md @@ -29,6 +29,7 @@ furthermore, an `or reserved` indicates this field is always present, but is res the format versions are: +- 83: Furnace dev83 - 82: Furnace dev82 - 81: Furnace dev81 - 80: Furnace dev80 @@ -266,7 +267,9 @@ size | description 1 | buggy portamento after slide (>=72) or reserved 1 | new ins affects envelope (Game Boy) (>=72) or reserved 1 | ExtCh channel state is shared (>=78) or reserved - 25 | reserved + 1 | ignore DAC mode change outside of intended channel (>=83) or reserved + 1 | E1xx and E2xx also take priority over Slide00 (>=83) or reserved + 23 | reserved ``` # instrument diff --git a/src/engine/engine.h b/src/engine/engine.h index d11c54709..6b93666bf 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -42,8 +42,8 @@ #define BUSY_BEGIN_SOFT softLocked=true; isBusy.lock(); #define BUSY_END isBusy.unlock(); softLocked=false; -#define DIV_VERSION "dev82" -#define DIV_ENGINE_VERSION 82 +#define DIV_VERSION "dev83" +#define DIV_ENGINE_VERSION 83 // for imports #define DIV_VERSION_MOD 0xff01 diff --git a/src/engine/fileOps.cpp b/src/engine/fileOps.cpp index 356e40e7e..6e91ac4aa 100644 --- a/src/engine/fileOps.cpp +++ b/src/engine/fileOps.cpp @@ -154,6 +154,8 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) { ds.ignoreJumpAtEnd=true; ds.buggyPortaAfterSlide=true; ds.gbInsAffectsEnvelope=true; + ds.ignoreDACModeOutsideIntendedChannel=false; + ds.e1e2AlsoTakePriority=true; // 1.1 compat flags if (ds.version>24) { @@ -1315,7 +1317,14 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) { } else { reader.readC(); } - for (int i=0; i<25; i++) { + if (ds.version>=82) { + ds.ignoreDACModeOutsideIntendedChannel=reader.readC(); + ds.e1e2AlsoTakePriority=reader.readC(); + } else { + reader.readC(); + reader.readC(); + } + for (int i=0; i<23; i++) { reader.readC(); } } @@ -2219,7 +2228,9 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) { w->writeC(song.buggyPortaAfterSlide); w->writeC(song.gbInsAffectsEnvelope); w->writeC(song.sharedExtStat); - for (int i=0; i<25; i++) { + w->writeC(song.ignoreDACModeOutsideIntendedChannel); + w->writeC(song.e1e2AlsoTakePriority); + for (int i=0; i<23; i++) { w->writeC(0); } diff --git a/src/engine/platform/genesisext.cpp b/src/engine/platform/genesisext.cpp index 60f877b7e..e8ddbdc79 100644 --- a/src/engine/platform/genesisext.cpp +++ b/src/engine/platform/genesisext.cpp @@ -156,9 +156,21 @@ int DivPlatformGenesisExt::dispatch(DivCommand c) { break; } case DIV_CMD_SAMPLE_MODE: { - // ignored on extended channel 3 mode. + // not ignored actually! + if (!parent->song.ignoreDACModeOutsideIntendedChannel) { + dacMode=c.value; + rWrite(0x2b,c.value<<7); + } break; } + case DIV_CMD_SAMPLE_BANK: + if (!parent->song.ignoreDACModeOutsideIntendedChannel) { + sampleBank=c.value; + if (sampleBank>(parent->song.sample.size()/12)) { + sampleBank=parent->song.sample.size()/12; + } + } + break; case DIV_CMD_LEGATO: { opChan[ch].baseFreq=NOTE_FREQUENCY(c.value); opChan[ch].freqChanged=true; diff --git a/src/engine/playback.cpp b/src/engine/playback.cpp index 97047b64d..05cb890c5 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -1174,6 +1174,7 @@ void DivEngine::processRow(int i, bool afterDelay) { chan[i].inPorta=true; chan[i].shorthandPorta=true; if (!song.brokenShortcutSlides) dispatchCmd(DivCommand(DIV_CMD_PRE_PORTA,i,true,0)); + if (song.e1e2AlsoTakePriority) lastSlide=0x1337; // ... } else { chan[i].inPorta=false; if (!song.brokenShortcutSlides) dispatchCmd(DivCommand(DIV_CMD_PRE_PORTA,i,false,0)); @@ -1190,6 +1191,7 @@ void DivEngine::processRow(int i, bool afterDelay) { chan[i].inPorta=true; chan[i].shorthandPorta=true; if (!song.brokenShortcutSlides) dispatchCmd(DivCommand(DIV_CMD_PRE_PORTA,i,true,0)); + if (song.e1e2AlsoTakePriority) lastSlide=0x1337; // ... } else { chan[i].inPorta=false; if (!song.brokenShortcutSlides) dispatchCmd(DivCommand(DIV_CMD_PRE_PORTA,i,false,0)); diff --git a/src/engine/song.h b/src/engine/song.h index 009b40980..6cda7c533 100644 --- a/src/engine/song.h +++ b/src/engine/song.h @@ -321,6 +321,8 @@ struct DivSong { bool buggyPortaAfterSlide; bool gbInsAffectsEnvelope; bool sharedExtStat; + bool ignoreDACModeOutsideIntendedChannel; + bool e1e2AlsoTakePriority; DivOrders orders; std::vector ins; @@ -400,7 +402,9 @@ struct DivSong { ignoreJumpAtEnd(false), buggyPortaAfterSlide(false), gbInsAffectsEnvelope(true), - sharedExtStat(true) { + sharedExtStat(true), + ignoreDACModeOutsideIntendedChannel(false), + e1e2AlsoTakePriority(false) { for (int i=0; i<32; i++) { system[i]=DIV_SYSTEM_NULL; systemVol[i]=64; diff --git a/src/gui/compatFlags.cpp b/src/gui/compatFlags.cpp index 15a900b2d..8a45741f8 100644 --- a/src/gui/compatFlags.cpp +++ b/src/gui/compatFlags.cpp @@ -105,6 +105,14 @@ void FurnaceGUI::drawCompatFlags() { if (ImGui::IsItemHovered()) { ImGui::SetTooltip("if this is on, an instrument change will also affect the envelope."); } + ImGui::Checkbox("Ignore DAC mode change outside of intended channel in ExtCh mode",&e->song.ignoreDACModeOutsideIntendedChannel); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("if this is on, 17xx has no effect on the operator channels in YM2612."); + } + ImGui::Checkbox("E1xy/E2xy also take priority over slide stops",&e->song.e1e2AlsoTakePriority); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("does this make any sense by now?"); + } ImGui::Text("Loop modality:"); if (ImGui::RadioButton("Reset channels",e->song.loopModality==0)) { From fd723763f601d5d69eb3460a2e3f920e200f1099 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 13 Apr 2022 00:35:38 -0500 Subject: [PATCH 047/342] fix serious issue! --- src/engine/fileOps.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/engine/fileOps.cpp b/src/engine/fileOps.cpp index 6e91ac4aa..e4e7f0541 100644 --- a/src/engine/fileOps.cpp +++ b/src/engine/fileOps.cpp @@ -980,6 +980,10 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) { if (ds.version<78) { ds.sharedExtStat=false; } + if (ds.version<83) { + ds.ignoreDACModeOutsideIntendedChannel=true; + ds.e1e2AlsoTakePriority=false; + } ds.isDMF=false; reader.readS(); // reserved @@ -1317,7 +1321,7 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) { } else { reader.readC(); } - if (ds.version>=82) { + if (ds.version>=83) { ds.ignoreDACModeOutsideIntendedChannel=reader.readC(); ds.e1e2AlsoTakePriority=reader.readC(); } else { From 75f99cfb098ebe6fc1670e9c1be56a5d4cab10cd Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 13 Apr 2022 00:37:48 -0500 Subject: [PATCH 048/342] update credits --- src/gui/about.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/gui/about.cpp b/src/gui/about.cpp index c85843239..c15f3268c 100644 --- a/src/gui/about.cpp +++ b/src/gui/about.cpp @@ -74,6 +74,7 @@ const char* aboutLine[]={ "", "-- additional feedback/fixes --", "fd", + "GENATARi", "plane", "TheEssem", "", From 2cd454a59dc3de3915fac29895fbede4847081e2 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 13 Apr 2022 02:09:59 -0500 Subject: [PATCH 049/342] major warning dialog changes it makes more sense now. closes #321 --- src/gui/doAction.cpp | 4 +- src/gui/gui.cpp | 249 +++++++++++++++++++++++++++++++++++++------ src/gui/gui.h | 2 + src/gui/settings.cpp | 7 +- 4 files changed, 225 insertions(+), 37 deletions(-) diff --git a/src/gui/doAction.cpp b/src/gui/doAction.cpp index 0fb6b0626..c05455bf3 100644 --- a/src/gui/doAction.cpp +++ b/src/gui/doAction.cpp @@ -28,14 +28,14 @@ void FurnaceGUI::doAction(int what) { switch (what) { case GUI_ACTION_OPEN: if (modified) { - showWarning("Unsaved changes! Are you sure?",GUI_WARN_OPEN); + showWarning("Unsaved changes! Save changes before opening another file?",GUI_WARN_OPEN); } else { openFileDialog(GUI_FILE_OPEN); } break; case GUI_ACTION_OPEN_BACKUP: if (modified) { - showWarning("Unsaved changes! Are you sure?",GUI_WARN_OPEN_BACKUP); + showWarning("Unsaved changes! Save changes before opening backup?",GUI_WARN_OPEN_BACKUP); } else { if (load(backupPath)>0) { showError("No backup available! (or unable to open it)"); diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 8924748e2..dfa066f36 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -2177,7 +2177,7 @@ bool FurnaceGUI::loop() { if (ev.drop.file!=NULL) { if (modified) { nextFile=ev.drop.file; - showWarning("Unsaved changes! Are you sure?",GUI_WARN_OPEN_DROP); + showWarning("Unsaved changes! Save changes before opening file?",GUI_WARN_OPEN_DROP); } else { if (load(ev.drop.file)>0) { showError(fmt::sprintf("Error while loading file! (%s)",lastError)); @@ -2188,7 +2188,7 @@ bool FurnaceGUI::loop() { break; case SDL_QUIT: if (modified) { - showWarning("Unsaved changes! Are you sure you want to quit?",GUI_WARN_QUIT); + showWarning("Unsaved changes! Save changes before quitting?",GUI_WARN_QUIT); } else { quit=true; return true; @@ -2345,14 +2345,14 @@ bool FurnaceGUI::loop() { if (ImGui::BeginMenu("file")) { if (ImGui::MenuItem("new...")) { if (modified) { - showWarning("Unsaved changes! Are you sure?",GUI_WARN_NEW); + showWarning("Unsaved changes! Save changes before creating a new song?",GUI_WARN_NEW); } else { displayNew=true; } } if (ImGui::MenuItem("open...",BIND_FOR(GUI_ACTION_OPEN))) { if (modified) { - showWarning("Unsaved changes! Are you sure?",GUI_WARN_OPEN); + showWarning("Unsaved changes! Save changes before opening another file?",GUI_WARN_OPEN); } else { openFileDialog(GUI_FILE_OPEN); } @@ -2474,7 +2474,7 @@ bool FurnaceGUI::loop() { ImGui::Separator(); if (ImGui::MenuItem("exit")) { if (modified) { - showWarning("Unsaved changes! Are you sure you want to quit?",GUI_WARN_QUIT); + showWarning("Unsaved changes! Save before quitting?",GUI_WARN_QUIT); } else { quit=true; } @@ -2651,6 +2651,7 @@ bool FurnaceGUI::loop() { } if (fileDialog->render(ImVec2(600.0f*dpiScale,400.0f*dpiScale),ImVec2(scrW*dpiScale,scrH*dpiScale))) { + bool openOpen=false; //ImGui::GetIO().ConfigFlags&=~ImGuiConfigFlags_NavEnableKeyboard; switch (curFileDialog) { case GUI_FILE_OPEN: @@ -2744,15 +2745,47 @@ bool FurnaceGUI::loop() { for (char& i: lowerCase) { if (i>='A' && i<='Z') i+='a'-'A'; } + bool saveWasSuccessful=true; if ((lowerCase.size()<4 || lowerCase.rfind(".dmf")!=lowerCase.size()-4)) { if (save(copyOfName,0)>0) { showError(fmt::sprintf("Error while saving file! (%s)",lastError)); + saveWasSuccessful=false; } } else { if (save(copyOfName,26)>0) { showError(fmt::sprintf("Error while saving file! (%s)",lastError)); + saveWasSuccessful=false; } } + if (saveWasSuccessful && postWarnAction!=GUI_WARN_GENERIC) { + switch (postWarnAction) { + case GUI_WARN_QUIT: + quit=true; + break; + case GUI_WARN_NEW: + displayNew=true; + break; + case GUI_WARN_OPEN: + openOpen=true; + break; + case GUI_WARN_OPEN_DROP: + if (load(nextFile)>0) { + showError(fmt::sprintf("Error while loading file! (%s)",lastError)); + } + nextFile=""; + break; + case GUI_WARN_OPEN_BACKUP: + if (load(backupPath)>0) { + showError("No backup available! (or unable to open it)"); + } + break; + default: + break; + } + postWarnAction=GUI_WARN_GENERIC; + } else if (postWarnAction==GUI_WARN_OPEN_DROP) { + nextFile=""; + } break; } case GUI_FILE_SAVE_DMF_LEGACY: @@ -2862,6 +2895,11 @@ bool FurnaceGUI::loop() { } } fileDialog->close(); + postWarnAction=GUI_WARN_GENERIC; + + if (openOpen) { + openFileDialog(GUI_FILE_OPEN); + } } if (warnQuit) { @@ -2920,49 +2958,193 @@ bool FurnaceGUI::loop() { if (ImGui::BeginPopupModal("Warning",NULL,ImGuiWindowFlags_AlwaysAutoResize)) { ImGui::Text("%s",warnString.c_str()); - if (ImGui::Button(warnAction==GUI_WARN_GENERIC?"OK":"Yes")) { - ImGui::CloseCurrentPopup(); - switch (warnAction) { - case GUI_WARN_QUIT: + switch (warnAction) { + case GUI_WARN_QUIT: + if (ImGui::Button("Yes")) { + ImGui::CloseCurrentPopup(); + if (curFileName=="" || curFileName==backupPath || e->song.version>=0xff00) { + openFileDialog(GUI_FILE_SAVE); + postWarnAction=GUI_WARN_QUIT; + } else { + if (save(curFileName,e->song.isDMF?e->song.version:0)>0) { + showError(fmt::sprintf("Error while saving file! (%s)",lastError)); + } else { + quit=true; + } + } + } + ImGui::SameLine(); + if (ImGui::Button("No")) { + ImGui::CloseCurrentPopup(); quit=true; - break; - case GUI_WARN_NEW: + } + ImGui::SameLine(); + if (ImGui::Button("Cancel")) { + ImGui::CloseCurrentPopup(); + } + break; + case GUI_WARN_NEW: + if (ImGui::Button("Yes")) { + ImGui::CloseCurrentPopup(); + if (curFileName=="" || curFileName==backupPath || e->song.version>=0xff00) { + openFileDialog(GUI_FILE_SAVE); + postWarnAction=GUI_WARN_NEW; + } else { + if (save(curFileName,e->song.isDMF?e->song.version:0)>0) { + showError(fmt::sprintf("Error while saving file! (%s)",lastError)); + } else { + displayNew=true; + } + } + } + ImGui::SameLine(); + if (ImGui::Button("No")) { + ImGui::CloseCurrentPopup(); displayNew=true; - break; - case GUI_WARN_OPEN: + } + ImGui::SameLine(); + if (ImGui::Button("Cancel")) { + ImGui::CloseCurrentPopup(); + } + break; + case GUI_WARN_OPEN: + if (ImGui::Button("Yes")) { + ImGui::CloseCurrentPopup(); + if (curFileName=="" || curFileName==backupPath || e->song.version>=0xff00) { + openFileDialog(GUI_FILE_SAVE); + postWarnAction=GUI_WARN_OPEN; + } else { + if (save(curFileName,e->song.isDMF?e->song.version:0)>0) { + showError(fmt::sprintf("Error while saving file! (%s)",lastError)); + } else { + openFileDialog(GUI_FILE_OPEN); + } + } + } + ImGui::SameLine(); + if (ImGui::Button("No")) { + ImGui::CloseCurrentPopup(); openFileDialog(GUI_FILE_OPEN); - break; - case GUI_WARN_OPEN_BACKUP: + } + ImGui::SameLine(); + if (ImGui::Button("Cancel")) { + ImGui::CloseCurrentPopup(); + } + break; + case GUI_WARN_OPEN_BACKUP: + if (ImGui::Button("Yes")) { + ImGui::CloseCurrentPopup(); + if (curFileName=="" || curFileName==backupPath || e->song.version>=0xff00) { + openFileDialog(GUI_FILE_SAVE); + postWarnAction=GUI_WARN_OPEN_BACKUP; + } else { + if (save(curFileName,e->song.isDMF?e->song.version:0)>0) { + showError(fmt::sprintf("Error while saving file! (%s)",lastError)); + } else { + if (load(backupPath)>0) { + showError("No backup available! (or unable to open it)"); + } + } + } + } + ImGui::SameLine(); + if (ImGui::Button("No")) { + ImGui::CloseCurrentPopup(); if (load(backupPath)>0) { showError("No backup available! (or unable to open it)"); } - break; - case GUI_WARN_OPEN_DROP: + } + ImGui::SameLine(); + if (ImGui::Button("Cancel")) { + ImGui::CloseCurrentPopup(); + } + break; + case GUI_WARN_OPEN_DROP: + if (ImGui::Button("Yes")) { + ImGui::CloseCurrentPopup(); + if (curFileName=="" || curFileName==backupPath || e->song.version>=0xff00) { + openFileDialog(GUI_FILE_SAVE); + postWarnAction=GUI_WARN_OPEN_DROP; + } else { + if (save(curFileName,e->song.isDMF?e->song.version:0)>0) { + showError(fmt::sprintf("Error while saving file! (%s)",lastError)); + nextFile=""; + } else { + if (load(nextFile)>0) { + showError(fmt::sprintf("Error while loading file! (%s)",lastError)); + } + nextFile=""; + } + } + } + ImGui::SameLine(); + if (ImGui::Button("No")) { + ImGui::CloseCurrentPopup(); if (load(nextFile)>0) { showError(fmt::sprintf("Error while loading file! (%s)",lastError)); } nextFile=""; - break; - case GUI_WARN_RESET_LAYOUT: + } + ImGui::SameLine(); + if (ImGui::Button("Cancel")) { + ImGui::CloseCurrentPopup(); + nextFile=""; + } + break; + case GUI_WARN_RESET_LAYOUT: + if (ImGui::Button("Yes")) { + ImGui::CloseCurrentPopup(); ImGui::LoadIniSettingsFromMemory(defaultLayout); ImGui::SaveIniSettingsToDisk(finalLayoutPath); - break; - case GUI_WARN_RESET_KEYBINDS: + } + ImGui::SameLine(); + if (ImGui::Button("No")) { + ImGui::CloseCurrentPopup(); + } + break; + case GUI_WARN_RESET_KEYBINDS: + if (ImGui::Button("Yes")) { + ImGui::CloseCurrentPopup(); resetKeybinds(); - break; - case GUI_WARN_RESET_COLORS: + } + ImGui::SameLine(); + if (ImGui::Button("No")) { + ImGui::CloseCurrentPopup(); + } + break; + case GUI_WARN_RESET_COLORS: + if (ImGui::Button("Yes")) { + ImGui::CloseCurrentPopup(); resetColors(); applyUISettings(false); - break; - case GUI_WARN_GENERIC: - break; - } - } - if (warnAction!=GUI_WARN_GENERIC) { - ImGui::SameLine(); - if (ImGui::Button("No")) { - ImGui::CloseCurrentPopup(); - } + } + ImGui::SameLine(); + if (ImGui::Button("No")) { + ImGui::CloseCurrentPopup(); + } + break; + case GUI_WARN_CLOSE_SETTINGS: + if (ImGui::Button("Yes")) { + ImGui::CloseCurrentPopup(); + settingsOpen=false; + willCommit=true; + } + ImGui::SameLine(); + if (ImGui::Button("No")) { + ImGui::CloseCurrentPopup(); + settingsOpen=false; + syncSettings(); + } + ImGui::SameLine(); + if (ImGui::Button("Cancel")) { + ImGui::CloseCurrentPopup(); + } + break; + case GUI_WARN_GENERIC: + if (ImGui::Button("OK")) { + ImGui::CloseCurrentPopup(); + } + break; } ImGui::EndPopup(); } @@ -3268,6 +3450,7 @@ FurnaceGUI::FurnaceGUI(): vgmExportVersion(0x171), curFileDialog(GUI_FILE_OPEN), warnAction(GUI_WARN_OPEN), + postWarnAction(GUI_WARN_GENERIC), fileDialog(NULL), scrW(1280), scrH(800), diff --git a/src/gui/gui.h b/src/gui/gui.h index b2a5e46e9..ad20d8e7d 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -261,6 +261,7 @@ enum FurnaceGUIWarnings { GUI_WARN_RESET_LAYOUT, GUI_WARN_RESET_COLORS, GUI_WARN_RESET_KEYBINDS, + GUI_WARN_CLOSE_SETTINGS, GUI_WARN_GENERIC }; @@ -714,6 +715,7 @@ class FurnaceGUI { FurnaceGUIFileDialogs curFileDialog; FurnaceGUIWarnings warnAction; + FurnaceGUIWarnings postWarnAction; FurnaceGUIFileDialog* fileDialog; diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index f1da3717d..b823fb88e 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -201,7 +201,6 @@ struct MappedInput { // TODO: // - add metronome volume setting // - maybe add metronome sound setting -// - add a "close" button to this window void FurnaceGUI::drawSettings() { if (nextWindow==GUI_WINDOW_SETTINGS) { settingsOpen=true; @@ -209,7 +208,11 @@ void FurnaceGUI::drawSettings() { nextWindow=GUI_WINDOW_NOTHING; } if (!settingsOpen) return; - if (ImGui::Begin("Settings",NULL,ImGuiWindowFlags_NoDocking)) { + if (ImGui::Begin("Settings",&settingsOpen,ImGuiWindowFlags_NoDocking)) { + if (!settingsOpen) { + settingsOpen=true; + showWarning("Do you want to save your settings?",GUI_WARN_CLOSE_SETTINGS); + } if (ImGui::BeginTabBar("settingsTab")) { if (ImGui::BeginTabItem("General")) { ImGui::Text("Workspace layout"); From 2e2fafd878ab61dde0ace5e3467a9bc5419a60cf Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 13 Apr 2022 02:29:07 -0500 Subject: [PATCH 050/342] GUI: add metronome volume setting --- src/engine/engine.cpp | 7 +++++++ src/engine/engine.h | 5 +++++ src/engine/playback.cpp | 4 ++-- src/gui/gui.h | 2 ++ src/gui/settings.cpp | 15 ++++++++++++--- 5 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index b8b18cb40..d24700fe9 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -2025,6 +2025,10 @@ void DivEngine::setMetronome(bool enable) { metroAmp=0; } +void DivEngine::setMetronomeVol(float vol) { + metroVol=vol; +} + void DivEngine::setConsoleMode(bool enable) { consoleMode=enable; } @@ -2196,6 +2200,9 @@ bool DivEngine::initAudioBackend() { lowQuality=getConfInt("audioQuality",0); forceMono=getConfInt("forceMono",0); + metroVol=(float)(getConfInt("metroVol",100))/100.0f; + if (metroVol<0.0f) metroVol=0.0f; + if (metroVol>2.0f) metroVol=2.0f; switch (audioEngine) { case DIV_AUDIO_JACK: diff --git a/src/engine/engine.h b/src/engine/engine.h index 6b93666bf..bde102a27 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -252,6 +252,7 @@ class DivEngine { size_t metroTickLen; float metroFreq, metroPos; float metroAmp; + float metroVol; size_t totalProcessed; @@ -622,6 +623,9 @@ class DivEngine { // set metronome void setMetronome(bool enable); + // set metronome volume (1.0 = 100%) + void setMetronomeVol(float vol); + // halt now void halt(); @@ -772,6 +776,7 @@ class DivEngine { metroFreq(0), metroPos(0), metroAmp(0.0f), + metroVol(1.0f), totalProcessed(0), oscBuf{NULL,NULL}, oscSize(1), diff --git a/src/engine/playback.cpp b/src/engine/playback.cpp index 05cb890c5..240209943 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -1925,8 +1925,8 @@ void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsi metroAmp=0.7f; } if (metroAmp>0.0f) { - out[0][i]+=(sin(metroPos*2*M_PI))*metroAmp; - out[1][i]+=(sin(metroPos*2*M_PI))*metroAmp; + out[0][i]+=(sin(metroPos*2*M_PI))*metroAmp*metroVol; + out[1][i]+=(sin(metroPos*2*M_PI))*metroAmp*metroVol; } metroAmp-=0.0003f; if (metroAmp<0.0f) metroAmp=0.0f; diff --git a/src/gui/gui.h b/src/gui/gui.h index ad20d8e7d..84292825f 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -810,6 +810,7 @@ class FurnaceGUI { int oscBorder; int separateFMColors; int insEditColorize; + int metroVol; unsigned int maxUndoSteps; String mainFontPath; String patFontPath; @@ -876,6 +877,7 @@ class FurnaceGUI { oscBorder(1), separateFMColors(0), insEditColorize(0), + metroVol(100), maxUndoSteps(100), mainFontPath(""), patFontPath(""), diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index b823fb88e..7dc6478e0 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -198,9 +198,6 @@ struct MappedInput { scan(s), val(v) {} }; -// TODO: -// - add metronome volume setting -// - maybe add metronome sound setting void FurnaceGUI::drawSettings() { if (nextWindow==GUI_WINDOW_SETTINGS) { settingsOpen=true; @@ -385,6 +382,14 @@ void FurnaceGUI::drawSettings() { ImGui::SameLine(); ImGui::Combo("##Quality",&settings.audioQuality,audioQualities,2); + ImGui::Text("Metronome volume"); + ImGui::SameLine(); + if (ImGui::SliderInt("##MetroVol",&settings.metroVol,0,200,"%d%%")) { + if (settings.metroVol<0) settings.metroVol=0; + if (settings.metroVol>200) settings.metroVol=200; + e->setMetronomeVol(((float)settings.metroVol)/100.0f); + } + bool forceMonoB=settings.forceMono; if (ImGui::Checkbox("Force mono audio",&forceMonoB)) { settings.forceMono=forceMonoB; @@ -1562,6 +1567,7 @@ void FurnaceGUI::syncSettings() { settings.oscBorder=e->getConfInt("oscBorder",1); settings.separateFMColors=e->getConfInt("separateFMColors",0); settings.insEditColorize=e->getConfInt("insEditColorize",0); + settings.metroVol=e->getConfInt("metroVol",100); clampSetting(settings.mainFontSize,2,96); clampSetting(settings.patFontSize,2,96); @@ -1617,6 +1623,7 @@ void FurnaceGUI::syncSettings() { clampSetting(settings.sampleLayout,0,1); clampSetting(settings.separateFMColors,0,1); clampSetting(settings.insEditColorize,0,1); + clampSetting(settings.metroVol,0,200); // keybinds for (int i=0; isetMidiDirect(midiMap.directChannel); + e->setMetronomeVol(((float)settings.metroVol)/100.0f); } void FurnaceGUI::commitSettings() { @@ -1698,6 +1706,7 @@ void FurnaceGUI::commitSettings() { e->setConf("oscBorder",settings.oscBorder); e->setConf("separateFMColors",settings.separateFMColors); e->setConf("insEditColorize",settings.insEditColorize); + e->setConf("metroVol",settings.metroVol); // colors for (int i=0; i Date: Wed, 13 Apr 2022 03:24:49 -0500 Subject: [PATCH 051/342] GUI: partially add a piano --- src/gui/gui.cpp | 5 ++- src/gui/gui.h | 6 +++ src/gui/pattern.cpp | 6 +++ src/gui/piano.cpp | 92 ++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 106 insertions(+), 3 deletions(-) diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index dfa066f36..9787a4da6 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -3650,7 +3650,10 @@ FurnaceGUI::FurnaceGUI(): oscTotal(0), oscZoom(0.5f), oscZoomSlider(false), - followLog(true) { + followLog(true), + pianoOctaves(7), + pianoOptions(false), + pianoOffset(6) { // value keys valueKeys[SDLK_0]=0; valueKeys[SDLK_1]=1; diff --git a/src/gui/gui.h b/src/gui/gui.h index 84292825f..4b9ca05ad 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -1048,6 +1048,12 @@ class FurnaceGUI { // log window bool followLog; + // piano + int pianoOctaves; + bool pianoOptions; + float pianoKeyHit[180]; + int pianoOffset; + void drawSSGEnv(unsigned char type, const ImVec2& size); void drawWaveform(unsigned char type, bool opz, const ImVec2& size); void drawAlgorithm(unsigned char alg, FurnaceGUIFMAlgs algType, const ImVec2& size); diff --git a/src/gui/pattern.cpp b/src/gui/pattern.cpp index de0e5a61f..166815145 100644 --- a/src/gui/pattern.cpp +++ b/src/gui/pattern.cpp @@ -494,6 +494,12 @@ void FurnaceGUI::drawPattern() { ImVec4 chanHeadHover=chanHead; if (e->keyHit[i]) { keyHit[i]=0.2; + if (!muted) { + int note=e->getChanState(i)->note+60; + if (note>=0 && note<180) { + pianoKeyHit[note]=1.0; + } + } e->keyHit[i]=false; } if (settings.guiColorsBase) { diff --git a/src/gui/piano.cpp b/src/gui/piano.cpp index aa18996f3..a4f0c64cb 100644 --- a/src/gui/piano.cpp +++ b/src/gui/piano.cpp @@ -19,6 +19,20 @@ #include "gui.h" #include "guiConst.h" +#include "imgui.h" +#include "imgui_internal.h" + +const float topKeyStarts[5]={ + 0.9f/7.0f, 2.1f/7.0f, 3.9f/7.0f, 5.0f/7.0f, 6.1f/7.0f +}; + +const int topKeyNotes[5]={ + 1, 3, 6, 8, 10 +}; + +const int bottomKeyNotes[7]={ + 0, 2, 4, 5, 7, 9, 11 +}; // TODO: actually implement a piano! void FurnaceGUI::drawPiano() { @@ -28,7 +42,81 @@ void FurnaceGUI::drawPiano() { nextWindow=GUI_WINDOW_NOTHING; } if (!pianoOpen) return; - if (ImGui::Begin("Piano",&pianoOpen)) { + if (ImGui::Begin("Piano",&pianoOpen,(pianoOptions)?0:ImGuiWindowFlags_NoTitleBar)) { + if (ImGui::BeginTable("PianoLayout",pianoOptions?2:1,ImGuiTableFlags_BordersInnerV)) { + if (pianoOptions) { + ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthFixed); + } + ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthStretch); + + ImGui::TableNextRow(); + if (pianoOptions) { + ImGui::TableNextColumn(); + ImGui::Button("Options"); + } + + ImGui::TableNextColumn(); + ImGuiWindow* window=ImGui::GetCurrentWindow(); + ImVec2 size=ImGui::GetContentRegionAvail(); + ImDrawList* dl=ImGui::GetWindowDrawList(); + + ImVec2 minArea=window->DC.CursorPos; + ImVec2 maxArea=ImVec2( + minArea.x+size.x, + minArea.y+size.y + ); + ImRect rect=ImRect(minArea,maxArea); + + // render piano + //ImGui::ItemSize(size,ImGui::GetStyle().FramePadding.y); + if (ImGui::ItemAdd(rect,ImGui::GetID("pianoDisplay"))) { + int bottomNotes=7*pianoOctaves; + for (int i=0; i=180) continue; + float pkh=pianoKeyHit[note]*0.5; + ImVec4 color=ImVec4(1.0f-pkh,1.0f-pkh,1.0f-pkh,1.0f); + ImVec2 p0=ImLerp(rect.Min,rect.Max,ImVec2((float)i/bottomNotes,0.0f)); + ImVec2 p1=ImLerp(rect.Min,rect.Max,ImVec2((float)(i+1)/bottomNotes,1.0f)); + p1.x-=dpiScale; + dl->AddRectFilled(p0,p1,ImGui::ColorConvertFloat4ToU32(color)); + } + + for (int i=0; i=180) continue; + float pkh=pianoKeyHit[note]*0.5; + ImVec4 color=ImVec4(pkh,pkh,pkh,1.0f); + ImVec2 p0=ImLerp(op0,op1,ImVec2(topKeyStarts[j]-0.05f,0.0f)); + ImVec2 p1=ImLerp(op0,op1,ImVec2(topKeyStarts[j]+0.05f,0.64f)); + dl->AddRectFilled(p0,p1,0xff000000); + p0.x+=dpiScale; + p1.x-=dpiScale; + p1.y-=dpiScale; + dl->AddRectFilled(p0,p1,ImGui::ColorConvertFloat4ToU32(color)); + } + } + + const float reduction=ImGui::GetIO().DeltaTime*60.0f*0.12; + for (int i=0; i<180; i++) { + pianoKeyHit[i]-=reduction; + if (pianoKeyHit[i]<0) pianoKeyHit[i]=0; + } + } + + if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) { + pianoOptions=!pianoOptions; + } + + ImGui::EndTable(); + } + /* for (int i=0; igetTotalChannelCount(); i++) { DivChannelState* cs=e->getChanState(i); if (cs->keyOn) { @@ -40,7 +128,7 @@ void FurnaceGUI::drawPiano() { } ImGui::Text("%d: %s",i,noteName); } - } + }*/ } if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_PIANO; ImGui::End(); From 720f046f28fda0c14fae146e82d42de26892d344 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 13 Apr 2022 17:42:55 -0500 Subject: [PATCH 052/342] GUI: implement no-nibble-push value input --- src/gui/gui.cpp | 6 +++++- src/gui/gui.h | 2 ++ src/gui/guiConst.cpp | 4 ++-- src/gui/settings.cpp | 8 ++++++++ 4 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 9787a4da6..fec3b6ed7 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -967,7 +967,11 @@ void FurnaceGUI::valueInput(int num, bool direct, int target) { pat->data[cursor.y][target]=num&0xff; } else { if (pat->data[cursor.y][target]==-1) pat->data[cursor.y][target]=0; - pat->data[cursor.y][target]=((pat->data[cursor.y][target]<<4)|num)&0xff; + if (!settings.pushNibble && !curNibble) { + pat->data[cursor.y][target]=num; + } else { + pat->data[cursor.y][target]=((pat->data[cursor.y][target]<<4)|num)&0xff; + } } if (cursor.xFine==1) { // instrument if (pat->data[cursor.y][target]>=(int)e->song.ins.size()) { diff --git a/src/gui/gui.h b/src/gui/gui.h index 4b9ca05ad..d9a15162d 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -811,6 +811,7 @@ class FurnaceGUI { int separateFMColors; int insEditColorize; int metroVol; + int pushNibble; unsigned int maxUndoSteps; String mainFontPath; String patFontPath; @@ -878,6 +879,7 @@ class FurnaceGUI { separateFMColors(0), insEditColorize(0), metroVol(100), + pushNibble(0), maxUndoSteps(100), mainFontPath(""), patFontPath(""), diff --git a/src/gui/guiConst.cpp b/src/gui/guiConst.cpp index 481ce6f20..10b004421 100644 --- a/src/gui/guiConst.cpp +++ b/src/gui/guiConst.cpp @@ -454,8 +454,8 @@ const FurnaceGUIColorDef guiColors[GUI_COLOR_MAX]={ D(GUI_COLOR_CHANNEL_FM,"",ImVec4(0.2f,0.8f,1.0f,1.0f)), D(GUI_COLOR_CHANNEL_PULSE,"",ImVec4(0.4f,1.0f,0.2f,1.0f)), D(GUI_COLOR_CHANNEL_NOISE,"",ImVec4(0.8f,0.8f,0.8f,1.0f)), - D(GUI_COLOR_CHANNEL_WAVE,"",ImVec4(1.0f,0.9f,0.2f,1.0f)), - D(GUI_COLOR_CHANNEL_PCM,"",ImVec4(1.0f,0.5f,0.2f,1.0f)), + D(GUI_COLOR_CHANNEL_WAVE,"",ImVec4(1.0f,0.5f,0.2f,1.0f)), + D(GUI_COLOR_CHANNEL_PCM,"",ImVec4(1.0f,0.9f,0.2f,1.0f)), D(GUI_COLOR_CHANNEL_OP,"",ImVec4(0.2f,0.4f,1.0f,1.0f)), D(GUI_COLOR_CHANNEL_MUTED,"",ImVec4(0.5f,0.5f,0.5f,1.0f)), diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index 7dc6478e0..8804a6125 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -236,6 +236,11 @@ void FurnaceGUI::drawSettings() { settings.soloAction=2; } + bool pushNibbleB=settings.pushNibble; + if (ImGui::Checkbox("Push value when overwriting instead of clearing it",&pushNibbleB)) { + settings.pushNibble=pushNibbleB; + } + bool pullDeleteBehaviorB=settings.pullDeleteBehavior; if (ImGui::Checkbox("Move cursor up on backspace-delete",&pullDeleteBehaviorB)) { settings.pullDeleteBehavior=pullDeleteBehaviorB; @@ -1568,6 +1573,7 @@ void FurnaceGUI::syncSettings() { settings.separateFMColors=e->getConfInt("separateFMColors",0); settings.insEditColorize=e->getConfInt("insEditColorize",0); settings.metroVol=e->getConfInt("metroVol",100); + settings.pushNibble=e->getConfInt("pushNibble",0); clampSetting(settings.mainFontSize,2,96); clampSetting(settings.patFontSize,2,96); @@ -1624,6 +1630,7 @@ void FurnaceGUI::syncSettings() { clampSetting(settings.separateFMColors,0,1); clampSetting(settings.insEditColorize,0,1); clampSetting(settings.metroVol,0,200); + clampSetting(settings.pushNibble,0,1); // keybinds for (int i=0; isetConf("separateFMColors",settings.separateFMColors); e->setConf("insEditColorize",settings.insEditColorize); e->setConf("metroVol",settings.metroVol); + e->setConf("pushNibble",settings.pushNibble); // colors for (int i=0; i Date: Wed, 13 Apr 2022 18:16:55 -0500 Subject: [PATCH 053/342] GUI: unreliable version of #63 --- src/gui/gui.cpp | 3 +++ src/gui/gui.h | 2 +- src/gui/pattern.cpp | 40 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 44 insertions(+), 1 deletion(-) diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index fec3b6ed7..a63585e6b 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -514,11 +514,13 @@ const char* FurnaceGUI::getSystemName(DivSystem which) { void FurnaceGUI::updateScroll(int amount) { float lineHeight=(patFont->FontSize+2*dpiScale); nextScroll=lineHeight*amount; + haveHitBounds=false; } void FurnaceGUI::addScroll(int amount) { float lineHeight=(patFont->FontSize+2*dpiScale); nextAddScroll=lineHeight*amount; + haveHitBounds=false; } void FurnaceGUI::setFileName(String name) { @@ -3618,6 +3620,7 @@ FurnaceGUI::FurnaceGUI(): scaleMax(100.0f), fadeMode(false), randomMode(false), + haveHitBounds(false), oldOrdersLen(0), sampleZoom(1.0), prevSampleZoom(1.0), diff --git a/src/gui/gui.h b/src/gui/gui.h index d9a15162d..c1a98c076 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -1009,7 +1009,7 @@ class FurnaceGUI { int dummyRows, demandX; int transposeAmount, randomizeMin, randomizeMax, fadeMin, fadeMax; float scaleMax; - bool fadeMode, randomMode; + bool fadeMode, randomMode, haveHitBounds; int oldOrdersLen; DivOrders oldOrders; diff --git a/src/gui/pattern.cpp b/src/gui/pattern.cpp index 166815145..fe471f15c 100644 --- a/src/gui/pattern.cpp +++ b/src/gui/pattern.cpp @@ -17,6 +17,7 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#include #define _USE_MATH_DEFINES #include "gui.h" #include "../ta-log.h" @@ -652,6 +653,45 @@ void FurnaceGUI::drawPattern() { demandScrollX=false; } scrollX=ImGui::GetScrollX(); + + // overflow changes order + if (!e->isPlaying()) { + if (wheelY!=0) { + if (wheelY>0) { + if (ImGui::GetScrollY()<=0) { + logV("scroll %d",wheelY); + if (haveHitBounds) { + if (e->getOrder()>0) { + e->setOrder(e->getOrder()-1); + ImGui::SetScrollY(ImGui::GetScrollMaxY()); + updateScroll(e->song.patLen); + } + haveHitBounds=false; + } else { + haveHitBounds=true; + } + } else { + haveHitBounds=false; + } + } else { + if (ImGui::GetScrollY()>=ImGui::GetScrollMaxY()) { + logV("scroll %d",wheelY); + if (haveHitBounds) { + if (e->getOrder()song.ordersLen) { + e->setOrder(e->getOrder()+1); + ImGui::SetScrollY(0); + updateScroll(0); + } + haveHitBounds=false; + } else { + haveHitBounds=true; + } + } else { + haveHitBounds=false; + } + } + } + } ImGui::EndTable(); } From 0e2ecaf6be0f9f841944b5a4bcb1ed0fdcb00998 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 14 Apr 2022 01:25:21 -0500 Subject: [PATCH 054/342] add setting to disable scrollChangesOrder it's kinda unreliable at the moment --- src/gui/gui.h | 2 ++ src/gui/pattern.cpp | 7 +++---- src/gui/settings.cpp | 8 ++++++++ 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/gui/gui.h b/src/gui/gui.h index c1a98c076..7583412d8 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -812,6 +812,7 @@ class FurnaceGUI { int insEditColorize; int metroVol; int pushNibble; + int scrollChangesOrder; unsigned int maxUndoSteps; String mainFontPath; String patFontPath; @@ -880,6 +881,7 @@ class FurnaceGUI { insEditColorize(0), metroVol(100), pushNibble(0), + scrollChangesOrder(0), maxUndoSteps(100), mainFontPath(""), patFontPath(""), diff --git a/src/gui/pattern.cpp b/src/gui/pattern.cpp index fe471f15c..d4fb939cd 100644 --- a/src/gui/pattern.cpp +++ b/src/gui/pattern.cpp @@ -655,11 +655,11 @@ void FurnaceGUI::drawPattern() { scrollX=ImGui::GetScrollX(); // overflow changes order - if (!e->isPlaying()) { + // TODO: this is very unreliable and sometimes it can warp you out of the song + if (settings.scrollChangesOrder && !e->isPlaying()) { if (wheelY!=0) { if (wheelY>0) { if (ImGui::GetScrollY()<=0) { - logV("scroll %d",wheelY); if (haveHitBounds) { if (e->getOrder()>0) { e->setOrder(e->getOrder()-1); @@ -675,9 +675,8 @@ void FurnaceGUI::drawPattern() { } } else { if (ImGui::GetScrollY()>=ImGui::GetScrollMaxY()) { - logV("scroll %d",wheelY); if (haveHitBounds) { - if (e->getOrder()song.ordersLen) { + if (e->getOrder()<(e->song.ordersLen-1)) { e->setOrder(e->getOrder()+1); ImGui::SetScrollY(0); updateScroll(0); diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index 8804a6125..f07d028ec 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -256,6 +256,11 @@ void FurnaceGUI::drawSettings() { settings.effectDeletionAltersValue=effectDeletionAltersValueB; } + bool scrollChangesOrderB=settings.scrollChangesOrder; + if (ImGui::Checkbox("Change order when scrolling outside of pattern bounds",&scrollChangesOrderB)) { + settings.scrollChangesOrder=scrollChangesOrderB; + } + bool stepOnInsertB=settings.stepOnInsert; if (ImGui::Checkbox("Move cursor by edit step on insert (push)",&stepOnInsertB)) { settings.stepOnInsert=stepOnInsertB; @@ -1574,6 +1579,7 @@ void FurnaceGUI::syncSettings() { settings.insEditColorize=e->getConfInt("insEditColorize",0); settings.metroVol=e->getConfInt("metroVol",100); settings.pushNibble=e->getConfInt("pushNibble",0); + settings.scrollChangesOrder=e->getConfInt("scrollChangesOrder",0); clampSetting(settings.mainFontSize,2,96); clampSetting(settings.patFontSize,2,96); @@ -1631,6 +1637,7 @@ void FurnaceGUI::syncSettings() { clampSetting(settings.insEditColorize,0,1); clampSetting(settings.metroVol,0,200); clampSetting(settings.pushNibble,0,1); + clampSetting(settings.scrollChangesOrder,0,1); // keybinds for (int i=0; isetConf("insEditColorize",settings.insEditColorize); e->setConf("metroVol",settings.metroVol); e->setConf("pushNibble",settings.pushNibble); + e->setConf("scrollChangesOrder",settings.scrollChangesOrder); // colors for (int i=0; i Date: Thu, 14 Apr 2022 01:51:21 -0500 Subject: [PATCH 055/342] VERA: 4x the volume --- src/engine/platform/vera.cpp | 4 ++++ src/engine/platform/vera.h | 1 + 2 files changed, 5 insertions(+) diff --git a/src/engine/platform/vera.cpp b/src/engine/platform/vera.cpp index 77f5c8050..68c214ddb 100644 --- a/src/engine/platform/vera.cpp +++ b/src/engine/platform/vera.cpp @@ -372,6 +372,10 @@ void DivPlatformVERA::muteChannel(int ch, bool mute) { } } +float DivPlatformVERA::getPostAmp() { + return 4.0f; +} + bool DivPlatformVERA::isStereo() { return true; } diff --git a/src/engine/platform/vera.h b/src/engine/platform/vera.h index a3773cccb..2b47f99a7 100644 --- a/src/engine/platform/vera.h +++ b/src/engine/platform/vera.h @@ -66,6 +66,7 @@ class DivPlatformVERA: public DivDispatch { void tick(); void muteChannel(int ch, bool mute); void notifyInsDeletion(void* ins); + float getPostAmp(); bool isStereo(); void poke(unsigned int addr, unsigned short val); void poke(std::vector& wlist); From 8ce8e87922cc7a1679218d01cad57e51dc79992e Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 14 Apr 2022 02:04:18 -0500 Subject: [PATCH 056/342] VERA: halve PCM volume this should be good --- src/engine/platform/vera.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/engine/platform/vera.cpp b/src/engine/platform/vera.cpp index 68c214ddb..593c1a0c7 100644 --- a/src/engine/platform/vera.cpp +++ b/src/engine/platform/vera.cpp @@ -114,8 +114,8 @@ void DivPlatformVERA::acquire(short* bufL, short* bufR, size_t start, size_t len psg_render(psg,buf[0],buf[1],curLen); pcm_render(pcm,buf[2],buf[3],curLen); for (int i=0; i>1))/2); + bufR[pos]=(short)(((int)buf[1][i]+(buf[3][i]>>1))/2); pos++; } len-=curLen; From cc1933dcbb256a20f6eab3f62d4534bdbfe13214 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 14 Apr 2022 02:16:08 -0500 Subject: [PATCH 057/342] export ExtCh op channels as a group in per-channel audio export --- src/engine/engine.cpp | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index d24700fe9..94f5d5b34 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -358,6 +358,14 @@ void DivEngine::runExportThread() { for (int j=0; jmuteChannel(dispatchChanOfChan[j],isMuted[j]); } @@ -385,6 +393,15 @@ void DivEngine::runExportThread() { if (sf_close(sf)!=0) { logE("could not close audio file!"); } + + if (getChannelType(i)==5) { + i++; + while (true) { + if (++i>=chans) break; + if (getChannelType(i)!=5) break; + } + i--; + } } exporting=false; @@ -414,7 +431,6 @@ void DivEngine::runExportThread() { } } -// TODO: properly export ExtCh songs in per-channel mode bool DivEngine::saveAudio(const char* path, int loops, DivAudioExportModes mode) { exportPath=path; exportMode=mode; From 9795bca2cadf8461ad05417957964fadf6aeb1e0 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 14 Apr 2022 02:26:47 -0500 Subject: [PATCH 058/342] fix audio export not aborting in per-channel mode --- src/engine/engine.cpp | 16 ++++++++++++++++ src/engine/engine.h | 2 ++ 2 files changed, 18 insertions(+) diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 94f5d5b34..ee4b4b20e 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -402,6 +402,8 @@ void DivEngine::runExportThread() { } i--; } + + if (stopExport) break; } exporting=false; @@ -429,12 +431,25 @@ void DivEngine::runExportThread() { break; } } + stopExport=false; } bool DivEngine::saveAudio(const char* path, int loops, DivAudioExportModes mode) { exportPath=path; exportMode=mode; + if (exportMode!=DIV_EXPORT_MODE_ONE) { + // remove extension + String lowerCase=exportPath; + for (char& i: lowerCase) { + if (i>='A' && i<='Z') i+='a'-'A'; + } + size_t extPos=lowerCase.rfind(".wav"); + if (extPos!=String::npos) { + exportPath=exportPath.substr(0,extPos); + } + } exporting=true; + stopExport=false; stop(); repeatPattern=false; setOrder(0); @@ -450,6 +465,7 @@ void DivEngine::waitAudioFile() { } bool DivEngine::haltAudioFile() { + stopExport=true; stop(); return true; } diff --git a/src/engine/engine.h b/src/engine/engine.h index bde102a27..3d5327565 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -194,6 +194,7 @@ class DivEngine { bool repeatPattern; bool metronome; bool exporting; + bool stopExport; bool halted; bool forceMono; bool cmdStreamEnabled; @@ -736,6 +737,7 @@ class DivEngine { repeatPattern(false), metronome(false), exporting(false), + stopExport(false), halted(false), forceMono(false), cmdStreamEnabled(false), From 55e085b148a615eb6e168c33b72dd4867b0d1e4f Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 14 Apr 2022 02:58:29 -0500 Subject: [PATCH 059/342] GUI: separate current order from engine order fixes #105 --- src/gui/cursor.cpp | 8 +++--- src/gui/doAction.cpp | 20 ++++++-------- src/gui/editing.cpp | 65 ++++++++++++++++---------------------------- src/gui/gui.cpp | 20 ++++++++++---- src/gui/gui.h | 3 +- src/gui/orders.cpp | 9 +++--- src/gui/pattern.cpp | 20 ++++++++------ src/gui/songInfo.cpp | 4 +-- 8 files changed, 70 insertions(+), 79 deletions(-) diff --git a/src/gui/cursor.cpp b/src/gui/cursor.cpp index 421b6d486..53652ec70 100644 --- a/src/gui/cursor.cpp +++ b/src/gui/cursor.cpp @@ -158,8 +158,8 @@ void FurnaceGUI::moveCursor(int x, int y, bool select) { if (settings.wrapVertical!=0 && !select) { cursor.y=0; if (settings.wrapVertical==2) { - if (!e->isPlaying() && e->getOrder()<(e->song.ordersLen-1)) { - e->setOrder(e->getOrder()+1); + if ((!e->isPlaying() || !followPattern) && curOrder<(e->song.ordersLen-1)) { + setOrder(curOrder+1); } else { cursor.y=e->song.patLen-1; } @@ -176,8 +176,8 @@ void FurnaceGUI::moveCursor(int x, int y, bool select) { if (settings.wrapVertical!=0 && !select) { cursor.y=e->song.patLen-1; if (settings.wrapVertical==2) { - if (!e->isPlaying() && e->getOrder()>0) { - e->setOrder(e->getOrder()-1); + if ((!e->isPlaying() || !followPattern) && curOrder>0) { + setOrder(curOrder-1); } else { cursor.y=0; } diff --git a/src/gui/doAction.cpp b/src/gui/doAction.cpp index c05455bf3..808d57a0c 100644 --- a/src/gui/doAction.cpp +++ b/src/gui/doAction.cpp @@ -428,13 +428,13 @@ void FurnaceGUI::doAction(int what) { e->unmuteAll(); break; case GUI_ACTION_PAT_NEXT_ORDER: - if (e->getOrder()song.ordersLen-1) { - e->setOrder(e->getOrder()+1); + if (curOrdersong.ordersLen-1) { + setOrder(curOrder+1); } break; case GUI_ACTION_PAT_PREV_ORDER: - if (e->getOrder()>0) { - e->setOrder(e->getOrder()-1); + if (curOrder>0) { + setOrder(curOrder-1); } break; case GUI_ACTION_PAT_COLLAPSE: @@ -1096,13 +1096,13 @@ void FurnaceGUI::doAction(int what) { } case GUI_ACTION_ORDERS_UP: - if (e->getOrder()>0) { - e->setOrder(e->getOrder()-1); + if (curOrder>0) { + setOrder(curOrder-1); } break; case GUI_ACTION_ORDERS_DOWN: - if (e->getOrder()song.ordersLen-1) { - e->setOrder(e->getOrder()+1); + if (curOrdersong.ordersLen-1) { + setOrder(curOrder+1); } break; case GUI_ACTION_ORDERS_LEFT: { @@ -1131,7 +1131,6 @@ void FurnaceGUI::doAction(int what) { } case GUI_ACTION_ORDERS_INCREASE: { if (orderCursor<0 || orderCursor>=e->getTotalChannelCount()) break; - int curOrder=e->getOrder(); if (e->song.orders.ord[orderCursor][curOrder]<0x7f) { e->song.orders.ord[orderCursor][curOrder]++; } @@ -1139,7 +1138,6 @@ void FurnaceGUI::doAction(int what) { } case GUI_ACTION_ORDERS_DECREASE: { if (orderCursor<0 || orderCursor>=e->getTotalChannelCount()) break; - int curOrder=e->getOrder(); if (e->song.orders.ord[orderCursor][curOrder]>0) { e->song.orders.ord[orderCursor][curOrder]--; } @@ -1199,7 +1197,7 @@ void FurnaceGUI::doAction(int what) { makeUndo(GUI_UNDO_CHANGE_ORDER); break; case GUI_ACTION_ORDERS_REPLAY: - e->setOrder(e->getOrder()); + setOrder(curOrder); break; } } \ No newline at end of file diff --git a/src/gui/editing.cpp b/src/gui/editing.cpp index 96f827f32..bd17e20ab 100644 --- a/src/gui/editing.cpp +++ b/src/gui/editing.cpp @@ -42,7 +42,6 @@ const char* noteNameNormal(short note, short octave) { } void FurnaceGUI::prepareUndo(ActionType action) { - int order=e->getOrder(); switch (action) { case GUI_UNDO_CHANGE_ORDER: oldOrders=e->song.orders; @@ -64,7 +63,7 @@ void FurnaceGUI::prepareUndo(ActionType action) { case GUI_UNDO_PATTERN_COLLAPSE: case GUI_UNDO_PATTERN_EXPAND: for (int i=0; igetTotalChannelCount(); i++) { - e->song.pat[i].getPattern(e->song.orders.ord[i][order],false)->copyOn(oldPat[i]); + e->song.pat[i].getPattern(e->song.orders.ord[i][curOrder],false)->copyOn(oldPat[i]); } break; } @@ -77,8 +76,7 @@ void FurnaceGUI::makeUndo(ActionType action) { s.cursor=cursor; s.selStart=selStart; s.selEnd=selEnd; - int order=e->getOrder(); - s.order=order; + s.order=curOrder; s.nibble=curNibble; switch (action) { case GUI_UNDO_CHANGE_ORDER: @@ -114,11 +112,11 @@ void FurnaceGUI::makeUndo(ActionType action) { case GUI_UNDO_PATTERN_COLLAPSE: case GUI_UNDO_PATTERN_EXPAND: for (int i=0; igetTotalChannelCount(); i++) { - DivPattern* p=e->song.pat[i].getPattern(e->song.orders.ord[i][order],false); + DivPattern* p=e->song.pat[i].getPattern(e->song.orders.ord[i][curOrder],false); for (int j=0; jsong.patLen; j++) { for (int k=0; k<32; k++) { if (p->data[j][k]!=oldPat[i]->data[j][k]) { - s.pat.push_back(UndoPatternData(i,e->song.orders.ord[i][order],j,k,oldPat[i]->data[j][k],p->data[j][k])); + s.pat.push_back(UndoPatternData(i,e->song.orders.ord[i][curOrder],j,k,oldPat[i]->data[j][k],p->data[j][k])); } } } @@ -199,10 +197,9 @@ void FurnaceGUI::doDelete() { int iCoarse=selStart.xCoarse; int iFine=selStart.xFine; - int ord=e->getOrder(); for (; iCoarse<=selEnd.xCoarse; iCoarse++) { if (!e->song.chanShow[iCoarse]) continue; - DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][ord],true); + DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true); for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsegetOrder(); for (; iCoarse<=selEnd.xCoarse; iCoarse++) { if (!e->song.chanShow[iCoarse]) continue; - DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][ord],true); + DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true); for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsesong.patLen; j++) { @@ -270,10 +266,9 @@ void FurnaceGUI::doInsert() { int iCoarse=selStart.xCoarse; int iFine=selStart.xFine; - int ord=e->getOrder(); for (; iCoarse<=selEnd.xCoarse; iCoarse++) { if (!e->song.chanShow[iCoarse]) continue; - DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][ord],true); + DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true); for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsesong.patLen-1; j>=selStart.y; j--) { @@ -303,10 +298,9 @@ void FurnaceGUI::doTranspose(int amount) { int iCoarse=selStart.xCoarse; int iFine=selStart.xFine; - int ord=e->getOrder(); for (; iCoarse<=selEnd.xCoarse; iCoarse++) { if (!e->song.chanShow[iCoarse]) continue; - DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][ord],true); + DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true); for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarse3 && !(iFine&1)) { iFine--; } - int ord=e->getOrder(); clipboard+='\n'; for (; iCoarse<=selEnd.xCoarse; iCoarse++) { if (!e->song.chanShow[iCoarse]) continue; - DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][ord],true); + DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true); for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsedata[j][0],pat->data[j][1]); @@ -439,7 +432,6 @@ void FurnaceGUI::doPaste(PasteMode mode) { int j=cursor.y; char note[4]; - int ord=e->getOrder(); for (size_t i=2; isong.patLen; i++) { size_t charPos=0; int iCoarse=cursor.xCoarse; @@ -448,7 +440,7 @@ void FurnaceGUI::doPaste(PasteMode mode) { String& line=data[i]; while (charPossong.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][ord],true); + DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true); if (line[charPos]=='|') { iCoarse++; if (iCoarsesong.chanShow[iCoarse]) { @@ -551,9 +543,9 @@ void FurnaceGUI::doPaste(PasteMode mode) { break; } j++; - if (mode==GUI_PASTE_MODE_OVERFLOW && j>=e->song.patLen && ordsong.ordersLen-1) { + if (mode==GUI_PASTE_MODE_OVERFLOW && j>=e->song.patLen && curOrdersong.ordersLen-1) { j=0; - ord++; + curOrder++; } if (mode==GUI_PASTE_MODE_FLOOD && i==data.size()-1) { @@ -573,10 +565,9 @@ void FurnaceGUI::doChangeIns(int ins) { prepareUndo(GUI_UNDO_PATTERN_CHANGE_INS); int iCoarse=selStart.xCoarse; - int ord=e->getOrder(); for (; iCoarse<=selEnd.xCoarse; iCoarse++) { if (!e->song.chanShow[iCoarse]) continue; - DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][ord],true); + DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true); for (int j=selStart.y; j<=selEnd.y; j++) { if (pat->data[j][2]!=-1 || !(pat->data[j][0]==0 && pat->data[j][1]==0)) { pat->data[j][2]=ins; @@ -594,10 +585,9 @@ void FurnaceGUI::doInterpolate() { std::vector> points; int iCoarse=selStart.xCoarse; int iFine=selStart.xFine; - int ord=e->getOrder(); for (; iCoarse<=selEnd.xCoarse; iCoarse++) { if (!e->song.chanShow[iCoarse]) continue; - DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][ord],true); + DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true); for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsegetOrder(); for (; iCoarse<=selEnd.xCoarse; iCoarse++) { if (!e->song.chanShow[iCoarse]) continue; - DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][ord],true); + DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true); for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsegetOrder(); for (; iCoarse<=selEnd.xCoarse; iCoarse++) { if (!e->song.chanShow[iCoarse]) continue; - DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][ord],true); + DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true); for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsegetOrder(); for (; iCoarse<=selEnd.xCoarse; iCoarse++) { if (!e->song.chanShow[iCoarse]) continue; - DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][ord],true); + DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true); for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsegetOrder(); for (; iCoarse<=selEnd.xCoarse; iCoarse++) { if (!e->song.chanShow[iCoarse]) continue; - DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][ord],true); + DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true); for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsegetOrder(); for (; iCoarse<=selEnd.xCoarse; iCoarse++) { if (!e->song.chanShow[iCoarse]) continue; - DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][ord],true); + DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true); for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsegetOrder(); for (; iCoarse<=selEnd.xCoarse; iCoarse++) { if (!e->song.chanShow[iCoarse]) continue; - DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][ord],true); + DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true); for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsegetOrder(); for (; iCoarse<=selEnd.xCoarse; iCoarse++) { if (!e->song.chanShow[iCoarse]) continue; - DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][ord],true); + DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true); for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsesong.pat[i.chan].getPattern(i.pat,true); p->data[i.row][i.col]=i.oldVal; } - if (!e->isPlaying()) { + if (!e->isPlaying() || !followPattern) { cursor=us.cursor; selStart=us.selStart; selEnd=us.selEnd; curNibble=us.nibble; updateScroll(cursor.y); - e->setOrder(us.order); + setOrder(us.order); } break; } @@ -1013,7 +996,7 @@ void FurnaceGUI::doRedo() { selEnd=us.selEnd; curNibble=us.nibble; updateScroll(cursor.y); - e->setOrder(us.order); + setOrder(us.order); } break; diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index a63585e6b..54034c078 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -841,6 +841,7 @@ float FurnaceGUI::calcBPM(int s1, int s2, float hz) { void FurnaceGUI::play(int row) { e->walkSong(loopOrder,loopRow,loopEnd); memset(lastIns,-1,sizeof(int)*DIV_MAX_CHANS); + if (!followPattern) e->setOrder(curOrder); if (row>0) { e->playToRow(row); } else { @@ -851,6 +852,13 @@ void FurnaceGUI::play(int row) { activeNotes.clear(); } +void FurnaceGUI::setOrder(unsigned char order, bool forced) { + curOrder=order; + if (followPattern || forced) { + e->setOrder(order); + } +} + void FurnaceGUI::stop() { e->walkSong(loopOrder,loopRow,loopEnd); e->stop(); @@ -921,7 +929,7 @@ void FurnaceGUI::stopPreviewNote(SDL_Scancode scancode, bool autoNote) { } void FurnaceGUI::noteInput(int num, int key, int vol) { - DivPattern* pat=e->song.pat[cursor.xCoarse].getPattern(e->song.orders.ord[cursor.xCoarse][e->getOrder()],true); + DivPattern* pat=e->song.pat[cursor.xCoarse].getPattern(e->song.orders.ord[cursor.xCoarse][curOrder],true); prepareUndo(GUI_UNDO_PATTERN_EDIT); @@ -962,7 +970,7 @@ void FurnaceGUI::noteInput(int num, int key, int vol) { } void FurnaceGUI::valueInput(int num, bool direct, int target) { - DivPattern* pat=e->song.pat[cursor.xCoarse].getPattern(e->song.orders.ord[cursor.xCoarse][e->getOrder()],true); + DivPattern* pat=e->song.pat[cursor.xCoarse].getPattern(e->song.orders.ord[cursor.xCoarse][curOrder],true); prepareUndo(GUI_UNDO_PATTERN_EDIT); if (target==-1) target=cursor.xFine+1; if (direct) { @@ -1135,8 +1143,7 @@ void FurnaceGUI::keyDown(SDL_Event& ev) { try { int num=valueKeys.at(ev.key.keysym.sym); if (orderCursor>=0 && orderCursorgetTotalChannelCount()) { - int curOrder=e->getOrder(); - e->lockSave([this,curOrder,num]() { + e->lockSave([this,num]() { e->song.orders.ord[orderCursor][curOrder]=((e->song.orders.ord[orderCursor][curOrder]<<4)|num); }); if (orderEditMode==2 || orderEditMode==3) { @@ -1147,7 +1154,7 @@ void FurnaceGUI::keyDown(SDL_Event& ev) { if (orderCursor>=e->getTotalChannelCount()) orderCursor=0; } else if (orderEditMode==3) { if (curOrdersong.ordersLen-1) { - e->setOrder(curOrder+1); + setOrder(curOrder+1); } } } @@ -2559,7 +2566,7 @@ bool FurnaceGUI::loop() { bool hasInfo=false; String info; if (cursor.xCoarse>=0 && cursor.xCoarsegetTotalChannelCount()) { - DivPattern* p=e->song.pat[cursor.xCoarse].getPattern(e->song.orders.ord[cursor.xCoarse][e->getOrder()],false); + DivPattern* p=e->song.pat[cursor.xCoarse].getPattern(e->song.orders.ord[cursor.xCoarse][curOrder],false); if (cursor.xFine>=0) switch (cursor.xFine) { case 0: // note if (p->data[cursor.y][0]>0) { @@ -3475,6 +3482,7 @@ FurnaceGUI::FurnaceGUI(): curWave(0), curSample(0), curOctave(3), + curOrder(0), oldRow(0), oldOrder(0), oldOrder1(0), diff --git a/src/gui/gui.h b/src/gui/gui.h index 7583412d8..07f7b5261 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -892,7 +892,7 @@ class FurnaceGUI { char finalLayoutPath[4096]; - int curIns, curWave, curSample, curOctave, oldRow, oldOrder, oldOrder1, editStep, exportLoops, soloChan, soloTimeout, orderEditMode, orderCursor; + int curIns, curWave, curSample, curOctave, curOrder, oldRow, oldOrder, oldOrder1, editStep, exportLoops, soloChan, soloTimeout, orderEditMode, orderCursor; int loopOrder, loopRow, loopEnd, isClipping, extraChannelButtons, patNameTarget, newSongCategory; int wheelX, wheelY; @@ -1167,6 +1167,7 @@ class FurnaceGUI { void doRedoSample(); void play(int row=0); + void setOrder(unsigned char order, bool forced=false); void stop(); void previewNote(int refChan, int note, bool autoNote=false); diff --git a/src/gui/orders.cpp b/src/gui/orders.cpp index 945f551d7..2311a3980 100644 --- a/src/gui/orders.cpp +++ b/src/gui/orders.cpp @@ -47,10 +47,9 @@ void FurnaceGUI::drawOrders() { ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing,prevSpacing); ImGui::TableSetupScrollFreeze(1,1); float lineHeight=(ImGui::GetTextLineHeight()+4*dpiScale); - int curOrder=e->getOrder(); if (e->isPlaying()) { if (followOrders) { - ImGui::SetScrollY((curOrder+1)*lineHeight-(ImGui::GetContentRegionAvail().y/2)); + ImGui::SetScrollY((e->getOrder()+1)*lineHeight-(ImGui::GetContentRegionAvail().y/2)); } } ImGui::TableNextRow(0,lineHeight); @@ -75,7 +74,7 @@ void FurnaceGUI::drawOrders() { snprintf(selID,4096,"%d##O_S%.2x",i,i); } if (ImGui::Selectable(selID)) { - e->setOrder(i); + setOrder(i); curNibble=false; orderCursor=-1; @@ -115,7 +114,7 @@ void FurnaceGUI::drawOrders() { curNibble=false; } } else { - e->setOrder(i); + setOrder(i); e->walkSong(loopOrder,loopRow,loopEnd); if (orderEditMode!=0) { orderCursor=j; @@ -151,7 +150,7 @@ void FurnaceGUI::drawOrders() { curNibble=false; } } else { - e->setOrder(i); + setOrder(i); e->walkSong(loopOrder,loopRow,loopEnd); if (orderEditMode!=0) { orderCursor=j; diff --git a/src/gui/pattern.cpp b/src/gui/pattern.cpp index d4fb939cd..b2d01743f 100644 --- a/src/gui/pattern.cpp +++ b/src/gui/pattern.cpp @@ -120,7 +120,7 @@ inline void FurnaceGUI::patternRow(int i, bool isPlaying, float lineHeight, int if (settings.overflowHighlight) { if (edit && cursor.y==i) { ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0,ImGui::GetColorU32(uiColors[GUI_COLOR_EDITING])); - } else if (isPlaying && oldRow==i) { + } else if (isPlaying && oldRow==i && ord==e->getOrder()) { ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0,ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_PLAY_HEAD])); } else if (e->song.hilightB>0 && !(i%e->song.hilightB)) { ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0,ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_HI_2])); @@ -131,7 +131,7 @@ inline void FurnaceGUI::patternRow(int i, bool isPlaying, float lineHeight, int isPushing=true; if (edit && cursor.y==i) { ImGui::PushStyleColor(ImGuiCol_Header,ImGui::GetColorU32(uiColors[GUI_COLOR_EDITING])); - } else if (isPlaying && oldRow==i) { + } else if (isPlaying && oldRow==i && ord==e->getOrder()) { ImGui::PushStyleColor(ImGuiCol_Header,ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_PLAY_HEAD])); } else if (e->song.hilightB>0 && !(i%e->song.hilightB)) { ImGui::PushStyleColor(ImGuiCol_Header,ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_HI_2])); @@ -409,9 +409,11 @@ void FurnaceGUI::drawPattern() { patWindowSize=ImGui::GetWindowSize(); //char id[32]; ImGui::PushFont(patFont); - // TODO: separate GUI curOrder from engine curOrder - int ord=e->isPlaying()?oldOrder:e->getOrder(); - oldOrder=e->getOrder(); + int ord=oldOrder; + if (followPattern) { + curOrder=e->getOrder(); + } + oldOrder=curOrder; int chans=e->getTotalChannelCount(); int displayChans=0; const DivPattern* patCache[DIV_MAX_CHANS]; @@ -661,8 +663,8 @@ void FurnaceGUI::drawPattern() { if (wheelY>0) { if (ImGui::GetScrollY()<=0) { if (haveHitBounds) { - if (e->getOrder()>0) { - e->setOrder(e->getOrder()-1); + if (curOrder>0) { + setOrder(curOrder-1); ImGui::SetScrollY(ImGui::GetScrollMaxY()); updateScroll(e->song.patLen); } @@ -676,8 +678,8 @@ void FurnaceGUI::drawPattern() { } else { if (ImGui::GetScrollY()>=ImGui::GetScrollMaxY()) { if (haveHitBounds) { - if (e->getOrder()<(e->song.ordersLen-1)) { - e->setOrder(e->getOrder()+1); + if (curOrder<(e->song.ordersLen-1)) { + setOrder(curOrder+1); ImGui::SetScrollY(0); updateScroll(0); } diff --git a/src/gui/songInfo.cpp b/src/gui/songInfo.cpp index a2f788c35..e225b9557 100644 --- a/src/gui/songInfo.cpp +++ b/src/gui/songInfo.cpp @@ -124,8 +124,8 @@ void FurnaceGUI::drawSongInfo() { if (ordLen<1) ordLen=1; if (ordLen>256) ordLen=256; e->song.ordersLen=ordLen; - if (e->getOrder()>=ordLen) { - e->setOrder(ordLen-1); + if (curOrder>=ordLen) { + setOrder(ordLen-1); } } From 11516d7923eee9ef79bb5a2e46fe6e9747ec24b1 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 14 Apr 2022 03:05:58 -0500 Subject: [PATCH 060/342] GUI: allow cursor movement when stepping --- src/gui/editControls.cpp | 4 ++++ src/gui/gui.cpp | 1 + src/gui/gui.h | 2 +- src/gui/pattern.cpp | 5 +++-- 4 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/gui/editControls.cpp b/src/gui/editControls.cpp index 71c6a94d8..5098a73de 100644 --- a/src/gui/editControls.cpp +++ b/src/gui/editControls.cpp @@ -86,6 +86,7 @@ void FurnaceGUI::drawEditControls() { ImGui::SameLine(); if (ImGui::Button(ICON_FA_ARROW_DOWN "##StepOne")) { e->stepOne(cursor.y); + pendingStepUpdate=true; } } if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_EDIT_CONTROLS; @@ -105,6 +106,7 @@ void FurnaceGUI::drawEditControls() { ImGui::SameLine(); if (ImGui::Button(ICON_FA_ARROW_DOWN "##StepOne")) { e->stepOne(cursor.y); + pendingStepUpdate=true; } ImGui::SameLine(); @@ -182,6 +184,7 @@ void FurnaceGUI::drawEditControls() { } if (ImGui::Button(ICON_FA_ARROW_DOWN "##StepOne")) { e->stepOne(cursor.y); + pendingStepUpdate=true; } bool repeatPattern=e->getRepeatPattern(); @@ -272,6 +275,7 @@ void FurnaceGUI::drawEditControls() { ImGui::SameLine(); if (ImGui::Button(ICON_FA_ARROW_DOWN "##StepOne")) { e->stepOne(cursor.y); + pendingStepUpdate=true; } ImGui::SameLine(); diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 54034c078..0dc80a780 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -3629,6 +3629,7 @@ FurnaceGUI::FurnaceGUI(): fadeMode(false), randomMode(false), haveHitBounds(false), + pendingStepUpdate(false), oldOrdersLen(0), sampleZoom(1.0), prevSampleZoom(1.0), diff --git a/src/gui/gui.h b/src/gui/gui.h index 07f7b5261..9167c9940 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -1011,7 +1011,7 @@ class FurnaceGUI { int dummyRows, demandX; int transposeAmount, randomizeMin, randomizeMax, fadeMin, fadeMax; float scaleMax; - bool fadeMode, randomMode, haveHitBounds; + bool fadeMode, randomMode, haveHitBounds, pendingStepUpdate; int oldOrdersLen; DivOrders oldOrders; diff --git a/src/gui/pattern.cpp b/src/gui/pattern.cpp index b2d01743f..271469c6c 100644 --- a/src/gui/pattern.cpp +++ b/src/gui/pattern.cpp @@ -380,7 +380,7 @@ void FurnaceGUI::drawPattern() { bool inhibitMenu=false; float scrollX=0; - if (e->isPlaying() && followPattern) cursor.y=oldRow; + if (e->isPlaying() && followPattern && (!e->isStepping() || pendingStepUpdate)) cursor.y=oldRow+((pendingStepUpdate)?1:0); demandX=0; sel1=selStart; sel2=selEnd; @@ -429,7 +429,8 @@ void FurnaceGUI::drawPattern() { char chanID[2048]; float lineHeight=(ImGui::GetTextLineHeight()+2*dpiScale); int curRow=e->getRow(); - if (e->isPlaying() && followPattern) updateScroll(curRow); + if (e->isPlaying() && followPattern && (!e->isStepping() || pendingStepUpdate)) updateScroll(curRow); + pendingStepUpdate=false; if (nextScroll>-0.5f) { ImGui::SetScrollY(nextScroll); nextScroll=-1.0f; From 5eb27178841ffbfdbe90f637477222ca4ea61085 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 14 Apr 2022 13:54:47 -0500 Subject: [PATCH 061/342] GUI: slightly tweak note preview algorithm issue #338 --- src/gui/gui.cpp | 56 +++++++++---------------------------------------- 1 file changed, 10 insertions(+), 46 deletions(-) diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 0dc80a780..d42710204 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -868,37 +868,13 @@ void FurnaceGUI::stop() { } void FurnaceGUI::previewNote(int refChan, int note, bool autoNote) { - if (autoNote) { - e->setMidiBaseChan(refChan); - e->synchronized([this,note]() { - e->autoNoteOn(-1,curIns,note); - }); - return; - } - - bool chanBusy[DIV_MAX_CHANS]; - memset(chanBusy,0,DIV_MAX_CHANS*sizeof(bool)); - for (ActiveNote& i: activeNotes) { - if (i.chan<0 || i.chan>=DIV_MAX_CHANS) continue; - chanBusy[i.chan]=true; - } - int chanCount=e->getTotalChannelCount(); - int i=refChan; - do { - if (!chanBusy[i]) { - e->noteOn(i,curIns,note); - activeNotes.push_back(ActiveNote(i,note)); - //printf("PUSHING: %d NOTE %d\n",i,note); - return; - } - i++; - if (i>=chanCount) i=0; - } while (i!=refChan); - //printf("FAILED TO FIND CHANNEL!\n"); + e->setMidiBaseChan(refChan); + e->synchronized([this,note]() { + e->autoNoteOn(-1,curIns,note); + }); } void FurnaceGUI::stopPreviewNote(SDL_Scancode scancode, bool autoNote) { - if (activeNotes.empty() && !autoNote) return; try { int key=noteKeys.at(scancode); int num=12*curOctave+key; @@ -909,21 +885,9 @@ void FurnaceGUI::stopPreviewNote(SDL_Scancode scancode, bool autoNote) { if (key==101) return; if (key==102) return; - if (autoNote) { - e->synchronized([this,num]() { - e->autoNoteOff(-1,num); - }); - return; - } - - for (size_t i=0; inoteOff(activeNotes[i].chan); - //printf("REMOVING %d\n",activeNotes[i].chan); - activeNotes.erase(activeNotes.begin()+i); - break; - } - } + e->synchronized([this,num]() { + e->autoNoteOff(-1,num); + }); } catch (std::out_of_range& e) { } } @@ -1230,7 +1194,7 @@ void FurnaceGUI::keyDown(SDL_Event& ev) { int key=noteKeys.at(ev.key.keysym.scancode); int num=12*curOctave+key; if (key!=100 && key!=101 && key!=102) { - previewNote(cursor.xCoarse,num,true); + previewNote(cursor.xCoarse,num); } } catch (std::out_of_range& e) { } @@ -1274,7 +1238,7 @@ void FurnaceGUI::keyDown(SDL_Event& ev) { } void FurnaceGUI::keyUp(SDL_Event& ev) { - stopPreviewNote(ev.key.keysym.scancode,curWindow!=GUI_WINDOW_PATTERN); + stopPreviewNote(ev.key.keysym.scancode,true); if (wavePreviewOn) { if (ev.key.keysym.scancode==wavePreviewKey) { wavePreviewOn=false; @@ -2171,7 +2135,7 @@ bool FurnaceGUI::loop() { if (!ImGui::GetIO().WantCaptureKeyboard) { keyUp(ev); } else { - stopPreviewNote(ev.key.keysym.scancode); + stopPreviewNote(ev.key.keysym.scancode,true); if (wavePreviewOn) { if (ev.key.keysym.scancode==wavePreviewKey) { wavePreviewOn=false; From f032e71b3ac01d1258bbc5c806c0ec14bcfb2349 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 14 Apr 2022 14:05:13 -0500 Subject: [PATCH 062/342] add TODO.md --- TODO.md | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 TODO.md diff --git a/TODO.md b/TODO.md new file mode 100644 index 000000000..3762895b9 --- /dev/null +++ b/TODO.md @@ -0,0 +1,36 @@ +# to-do for 0.6pre1 + +- panning macro +- pitch macro +- phase reset macro +- piano/input pad + - note input via piano + - input pad + - settings +- OPN system +- OPNA system +- ZX beeper system +- Y8950 system +- SCC/SCC+ system +- maybe YMU759 ADPCM channel +- ADPCM chips +- more effects for FM param control +- ability to customize startup system +- store system presets in new file +- Game Boy envelope macro/sequence +- option to display chip names instead of "multi-system" on title bar +- rewrite the system name detection function anyway +- add nightly.link +- scroll instrument/wave/sample list when selecting item +- "absorb" mode for instrument input - when this happens, current instrument is set to the input value +- unified data view +- separate "transpose note" and "transpose value" - see next point +- volume commands should work on Game Boy +- macro editor menu +- refactor sysDef.cpp +- add another FM editor layout +- try to find out why does VSlider not accept keyboard input +- finish lock layout +- note input latch! and separate edit masks +- if macros have release, note off should release them +- NSFPlay core for NES From de440d55ffd631b7f70d6c2f0cbea4de3c8b3faf Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 14 Apr 2022 14:09:42 -0500 Subject: [PATCH 063/342] one more task --- TODO.md | 1 + 1 file changed, 1 insertion(+) diff --git a/TODO.md b/TODO.md index 3762895b9..419b9b8eb 100644 --- a/TODO.md +++ b/TODO.md @@ -7,6 +7,7 @@ - note input via piano - input pad - settings +- RF5C68 system - OPN system - OPNA system - ZX beeper system From 8ff694eb132ccbb5389d393c234e7b45feff8255 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 14 Apr 2022 16:20:10 -0500 Subject: [PATCH 064/342] GUI: fix MIDI input settings crash --- src/gui/settings.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index f07d028ec..b31a62ffc 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -591,7 +591,11 @@ void FurnaceGUI::drawSettings() { if (bind.data1==128) { snprintf(bindID,1024,"Any"); } else { - snprintf(bindID,1024,"%d (0x%.2X, %s)",bind.data1,bind.data1,noteNames[bind.data1+60]); + const char* nName="???"; + if ((bind.data1+60)>0 && (bind.data1+60)<180) { + nName=noteNames[bind.data1+60]; + } + snprintf(bindID,1024,"%d (0x%.2X, %s)",bind.data1,bind.data1,nName); } ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); if (ImGui::BeginCombo("##BValue1",bindID)) { @@ -599,7 +603,11 @@ void FurnaceGUI::drawSettings() { bind.data1=128; } for (int j=0; j<128; j++) { - snprintf(bindID,1024,"%d (0x%.2X, %s)##BV1_%d",j,j,noteNames[j+60],j); + const char* nName="???"; + if ((j+60)>0 && (j+60)<180) { + nName=noteNames[j+60]; + } + snprintf(bindID,1024,"%d (0x%.2X, %s)##BV1_%d",j,j,nName,j); if (ImGui::Selectable(bindID,bind.data1==j)) { bind.data1=j; } From 68445435f925f69a84a4d5243be4767f9b395a9a Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 14 Apr 2022 18:25:59 -0500 Subject: [PATCH 065/342] fix several misc issues --- src/audio/taAudio.h | 5 +++-- src/engine/engine.h | 18 +++++++++++++++++- src/engine/instrument.cpp | 6 ++---- src/engine/instrument.h | 2 +- src/engine/sample.h | 1 + src/gui/fileDialog.h | 2 +- src/gui/gui.h | 3 +++ src/ta-log.h | 4 +++- src/ta-utils.h | 2 +- 9 files changed, 32 insertions(+), 11 deletions(-) diff --git a/src/audio/taAudio.h b/src/audio/taAudio.h index c1ef519dd..818122bf9 100644 --- a/src/audio/taAudio.h +++ b/src/audio/taAudio.h @@ -25,13 +25,13 @@ struct SampleRateChangeEvent { double rate; - SampleRateChangeEvent(double r): + explicit SampleRateChangeEvent(double r): rate(r) {} }; struct BufferSizeChangeEvent { unsigned int bufsize; - BufferSizeChangeEvent(unsigned int bs): + explicit BufferSizeChangeEvent(unsigned int bs): bufsize(bs) {} }; @@ -183,6 +183,7 @@ class TAAudio { inBufs(NULL), outBufs(NULL), audioProcCallback(NULL), + audioProcCallbackUser(NULL), sampleRateChanged(NULL), bufferSizeChanged(NULL), midiIn(NULL), diff --git a/src/engine/engine.h b/src/engine/engine.h index 3d5327565..0613d3edd 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -763,16 +763,21 @@ class DivEngine { totalCmds(0), lastCmds(0), cmdsPerSecond(0), + globalPitch(0), extValue(0), speed1(3), speed2(3), view(DIV_STATUS_NOTHING), haltOn(DIV_HALT_NONE), audioEngine(DIV_AUDIO_NULL), + exportMode(DIV_EXPORT_MODE_ONE), midiBaseChan(0), + samp_bb(NULL), samp_bbInLen(0), samp_temp(0), samp_prevSample(0), + samp_bbIn(NULL), + samp_bbOut(NULL), metroTick(NULL), metroTickLen(0), metroFreq(0), @@ -793,6 +798,17 @@ class DivEngine { qsoundAMem(NULL), qsoundAMemLen(0), dpcmMem(NULL), - dpcmMemLen(0) {} + dpcmMemLen(0), + x1_010Mem(NULL), + x1_010MemLen(0) { + memset(isMuted,0,DIV_MAX_CHANS*sizeof(bool)); + memset(keyHit,0,DIV_MAX_CHANS*sizeof(bool)); + memset(dispatchChanOfChan,0,DIV_MAX_CHANS*sizeof(int)); + memset(dispatchOfChan,0,DIV_MAX_CHANS*sizeof(int)); + memset(sysOfChan,0,DIV_MAX_CHANS*sizeof(int)); + memset(vibTable,0,64*sizeof(short)); + memset(reversePitchTable,0,4096*sizeof(int)); + memset(pitchTable,0,4096*sizeof(int)); + } }; #endif diff --git a/src/engine/instrument.cpp b/src/engine/instrument.cpp index 460fee9e2..b5ed40890 100644 --- a/src/engine/instrument.cpp +++ b/src/engine/instrument.cpp @@ -876,8 +876,8 @@ DivDataErrors DivInstrument::readInsData(SafeReader& reader, short version) { reader.readC(); // reserved } - // more macros if (version>=76) { + // more macros std.panLMacro.len=reader.readI(); std.panRMacro.len=reader.readI(); std.phaseResetMacro.len=reader.readI(); @@ -922,10 +922,8 @@ DivDataErrors DivInstrument::readInsData(SafeReader& reader, short version) { reader.read(std.ex6Macro.val,4*std.ex6Macro.len); reader.read(std.ex7Macro.val,4*std.ex7Macro.len); reader.read(std.ex8Macro.val,4*std.ex8Macro.len); - } - // FDS - if (version>=76) { + // FDS fds.modSpeed=reader.readI(); fds.modDepth=reader.readI(); fds.initModTableWithFirstWave=reader.readC(); diff --git a/src/engine/instrument.h b/src/engine/instrument.h index 80df6ab49..d9e943866 100644 --- a/src/engine/instrument.h +++ b/src/engine/instrument.h @@ -161,7 +161,7 @@ struct DivInstrumentMacro { unsigned char len; signed char loop; signed char rel; - DivInstrumentMacro(String n, bool initOpen=false): + explicit DivInstrumentMacro(const String& n, bool initOpen=false): name(n), mode(0), open(initOpen), diff --git a/src/engine/sample.h b/src/engine/sample.h index 1a9e53d32..ae0c06dae 100644 --- a/src/engine/sample.h +++ b/src/engine/sample.h @@ -246,6 +246,7 @@ struct DivSample { offVOX(0), offSegaPCM(0), offQSound(0), + offX1_010(0), samples(0) {} ~DivSample(); }; diff --git a/src/gui/fileDialog.h b/src/gui/fileDialog.h index b7f21abf0..0dcd82a6d 100644 --- a/src/gui/fileDialog.h +++ b/src/gui/fileDialog.h @@ -23,7 +23,7 @@ class FurnaceGUIFileDialog { bool render(const ImVec2& min, const ImVec2& max); String getPath(); String getFileName(); - FurnaceGUIFileDialog(bool system): + explicit FurnaceGUIFileDialog(bool system): sysDialog(system), opened(false), saving(false), diff --git a/src/gui/gui.h b/src/gui/gui.h index 9167c9940..4711ce17b 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -644,6 +644,9 @@ struct MIDIMap { midiClock(false), midiTimeCode(false), valueInputStyle(1), + valueInputControlMSB(0), + valueInputControlLSB(0), + valueInputControlSingle(0), volExp(1.0f), valueInputCurMSB(0), valueInputCurLSB(0), diff --git a/src/ta-log.h b/src/ta-log.h index 27580e5e7..f7921c710 100644 --- a/src/ta-log.h +++ b/src/ta-log.h @@ -46,7 +46,9 @@ struct LogEntry { bool ready; LogEntry(): loglevel(0), - ready(false) {} + ready(false) { + memset(&time,0,sizeof(struct tm)); + } }; int writeLog(int level, const char* msg, fmt::printf_args args); diff --git a/src/ta-utils.h b/src/ta-utils.h index 98b184f31..014f65f6c 100644 --- a/src/ta-utils.h +++ b/src/ta-utils.h @@ -50,7 +50,7 @@ struct TAParam { String desc; bool value; bool (*func)(String); - TAParam(String sn, String n, bool v, bool (*f)(String), String vn, String d): + TAParam(const String& sn, const String& n, bool v, bool (*f)(String), const String& vn, const String& d): shortName(sn), name(n), valName(vn), From 95580d9f1aba235c8f2b6c7342afd997b16c11c4 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 14 Apr 2022 18:26:06 -0500 Subject: [PATCH 066/342] update to-do list --- TODO.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/TODO.md b/TODO.md index 419b9b8eb..1cef798f3 100644 --- a/TODO.md +++ b/TODO.md @@ -34,4 +34,9 @@ - finish lock layout - note input latch! and separate edit masks - if macros have release, note off should release them +- add "don't scroll on cursor movement" option +- add ability to select entire row when clicking on row number +- store edit/followOrders/followPattern state in config +- add ability to select a column by double clicking +- add ability to move selection by dragging - NSFPlay core for NES From be483470efad394ef871d2540d65d66595dffba4 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 14 Apr 2022 21:57:39 -0500 Subject: [PATCH 067/342] GUI: add "Use standard OPL waveform names" option --- src/gui/gui.h | 4 ++++ src/gui/insEdit.cpp | 8 ++++++-- src/gui/settings.cpp | 16 ++++++++++++++++ 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/src/gui/gui.h b/src/gui/gui.h index 4711ce17b..136b666d7 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -816,6 +816,8 @@ class FurnaceGUI { int metroVol; int pushNibble; int scrollChangesOrder; + int oplStandardWaveNames; + int cursorMoveNoScroll; unsigned int maxUndoSteps; String mainFontPath; String patFontPath; @@ -885,6 +887,8 @@ class FurnaceGUI { metroVol(100), pushNibble(0), scrollChangesOrder(0), + oplStandardWaveNames(0), + cursorMoveNoScroll(0), maxUndoSteps(100), mainFontPath(""), patFontPath(""), diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index c4a3126ed..1f84fea10 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -67,6 +67,10 @@ const char* oplWaveforms[8]={ "Sine", "Half Sine", "Absolute Sine", "Quarter Sine", "Squished Sine", "Squished AbsSine", "Square", "Derived Square" }; +const char* oplWaveformsStandard[8]={ + "Sine", "Half Sine", "Absolute Sine", "Pulse Sine", "Sine (Even Periods)", "AbsSine (Even Periods)", "Square", "Derived Square" +}; + const char* opzWaveforms[8]={ "Sine", "Triangle", "Cut Sine", "Cut Triangle", "Squished Sine", "Squished Triangle", "Squished AbsSine", "Squished AbsTriangle" }; @@ -1832,7 +1836,7 @@ void FurnaceGUI::drawInsEdit() { drawWaveform(op.ws&7,ins->type==DIV_INS_OPZ,ImVec2(ImGui::GetContentRegionAvail().x,sliderHeight-ImGui::GetFrameHeightWithSpacing())); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); - P(CWSliderScalar("##WS",ImGuiDataType_U8,&op.ws,&_ZERO,&_SEVEN,(ins->type==DIV_INS_OPZ)?opzWaveforms[op.ws&7]:oplWaveforms[op.ws&7])); rightClickable + P(CWSliderScalar("##WS",ImGuiDataType_U8,&op.ws,&_ZERO,&_SEVEN,(ins->type==DIV_INS_OPZ)?opzWaveforms[op.ws&7]:(settings.oplStandardWaveNames?oplWaveformsStandard[op.ws&7]:oplWaveforms[op.ws&7]))); rightClickable if (ins->type==DIV_INS_OPL && ImGui::IsItemHovered()) { ImGui::SetTooltip("OPL2/3 only (last 4 waveforms are OPL3 only)"); } @@ -2086,7 +2090,7 @@ void FurnaceGUI::drawInsEdit() { ImGui::TableNextRow(); ImGui::TableNextColumn(); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); - P(CWSliderScalar("##WS",ImGuiDataType_U8,&op.ws,&_ZERO,&_SEVEN,(ins->type==DIV_INS_OPZ)?opzWaveforms[op.ws&7]:oplWaveforms[op.ws&7])); rightClickable + P(CWSliderScalar("##WS",ImGuiDataType_U8,&op.ws,&_ZERO,&_SEVEN,(ins->type==DIV_INS_OPZ)?opzWaveforms[op.ws&7]:(settings.oplStandardWaveNames?oplWaveformsStandard[op.ws&7]:oplWaveforms[op.ws&7]))); rightClickable if (ins->type==DIV_INS_OPL && ImGui::IsItemHovered()) { ImGui::SetTooltip("OPL2/3 only (last 4 waveforms are OPL3 only)"); } diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index b31a62ffc..bb7e76cec 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -271,6 +271,11 @@ void FurnaceGUI::drawSettings() { settings.cursorPastePos=cursorPastePosB; } + bool cursorMoveNoScrollB=settings.cursorMoveNoScroll; + if (ImGui::Checkbox("Don't scroll when moving cursor",&cursorMoveNoScrollB)) { + settings.cursorMoveNoScroll=cursorMoveNoScrollB; + } + bool allowEditDockingB=settings.allowEditDocking; if (ImGui::Checkbox("Allow docking editors",&allowEditDockingB)) { settings.allowEditDocking=allowEditDockingB; @@ -885,6 +890,11 @@ void FurnaceGUI::drawSettings() { settings.chipNames=chipNamesB; } + bool oplStandardWaveNamesB=settings.oplStandardWaveNames; + if (ImGui::Checkbox("Use standard OPL waveform names",&oplStandardWaveNamesB)) { + settings.oplStandardWaveNames=oplStandardWaveNamesB; + } + bool overflowHighlightB=settings.overflowHighlight; if (ImGui::Checkbox("Overflow pattern highlights",&overflowHighlightB)) { settings.overflowHighlight=overflowHighlightB; @@ -1588,6 +1598,8 @@ void FurnaceGUI::syncSettings() { settings.metroVol=e->getConfInt("metroVol",100); settings.pushNibble=e->getConfInt("pushNibble",0); settings.scrollChangesOrder=e->getConfInt("scrollChangesOrder",0); + settings.oplStandardWaveNames=e->getConfInt("oplStandardWaveNames",0); + settings.cursorMoveNoScroll=e->getConfInt("cursorMoveNoScroll",0); clampSetting(settings.mainFontSize,2,96); clampSetting(settings.patFontSize,2,96); @@ -1646,6 +1658,8 @@ void FurnaceGUI::syncSettings() { clampSetting(settings.metroVol,0,200); clampSetting(settings.pushNibble,0,1); clampSetting(settings.scrollChangesOrder,0,1); + clampSetting(settings.oplStandardWaveNames,0,1); + clampSetting(settings.cursorMoveNoScroll,0,1); // keybinds for (int i=0; isetConf("metroVol",settings.metroVol); e->setConf("pushNibble",settings.pushNibble); e->setConf("scrollChangesOrder",settings.scrollChangesOrder); + e->setConf("oplStandardWaveNames",settings.oplStandardWaveNames); + e->setConf("cursorMoveNoScroll",settings.cursorMoveNoScroll); // colors for (int i=0; i Date: Thu, 14 Apr 2022 23:22:11 -0500 Subject: [PATCH 068/342] GUI: fix another cppcheck warning --- src/gui/settings.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index bb7e76cec..666ba3b62 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -1951,14 +1951,13 @@ bool FurnaceGUI::importLayout(String path) { fclose(f); return false; } - unsigned char* file=new unsigned char[len]; if (fseek(f,0,SEEK_SET)<0) { perror("size error"); lastError=fmt::sprintf("on get size: %s",strerror(errno)); fclose(f); - delete[] file; return false; } + unsigned char* file=new unsigned char[len]; if (fread(file,1,(size_t)len,f)!=(size_t)len) { perror("read error"); lastError=fmt::sprintf("on read: %s",strerror(errno)); From 51bc1119e88bb6e6a2192df82f35fd58e4539b24 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 14 Apr 2022 23:22:25 -0500 Subject: [PATCH 069/342] hopefully fix oscilloscope line being out of bound --- src/gui/osc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/osc.cpp b/src/gui/osc.cpp index 0119232d4..266dfa3bf 100644 --- a/src/gui/osc.cpp +++ b/src/gui/osc.cpp @@ -219,7 +219,7 @@ void FurnaceGUI::drawOsc() { float y=oscValues[i]*oscZoom; if (y<-0.5f) y=-0.5f; if (y>0.5f) y=0.5f; - waveform[i]=ImLerp(rect.Min,rect.Max,ImVec2(x,0.5f-y)); + waveform[i]=ImLerp(inRect.Min,inRect.Max,ImVec2(x,0.5f-y)); } dl->AddPolyline(waveform,512,color,ImDrawFlags_None,dpiScale); if (settings.oscBorder) { From 3e15a066f8c661a676d854823c7dee5e247efcfa Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 15 Apr 2022 00:45:55 -0500 Subject: [PATCH 070/342] Soundtracker .mod loading support this required a re-organization of DivEngine::load() but it works eventually we'll be able to load other file formats this way --- src/engine/fileOps.cpp | 122 +++++++++++++++++++++++++++-------------- src/gui/gui.h | 3 +- 2 files changed, 83 insertions(+), 42 deletions(-) diff --git a/src/engine/fileOps.cpp b/src/engine/fileOps.cpp index e4e7f0541..5eaed546f 100644 --- a/src/engine/fileOps.cpp +++ b/src/engine/fileOps.cpp @@ -42,6 +42,12 @@ struct InflateBlock { } }; +struct NotZlibException { + int what; + NotZlibException(int w): + what(w) {} +}; + static double samplePitches[11]={ 0.1666666666, 0.2, 0.25, 0.333333333, 0.5, 1, @@ -1598,7 +1604,10 @@ bool DivEngine::loadMod(unsigned char* file, size_t len) { logD("couldn't seek to 1080"); throw EndOfFileException(&reader,reader.tell()); } - reader.read(magic,4); + if (reader.read(magic,4)!=4) { + logD("the magic isn't complete"); + throw EndOfFileException(&reader,reader.tell()); + } if (memcmp(magic,"M.K.",4)==0 || memcmp(magic,"M!K!",4)==0 || memcmp(magic,"M&K!",4)==0) { logD("detected a ProTracker module"); chCount=4; @@ -1619,20 +1628,26 @@ bool DivEngine::loadMod(unsigned char* file, size_t len) { logD("detected a Fast/TakeTracker module"); chCount=((magic[0]-'0')*10)+(magic[1]-'0'); } else { - // TODO: Soundtracker MOD? insCount=15; - throw InvalidHeaderException(); + logD("possibly a Soundtracker module"); + chCount=4; } // song name - reader.seek(0,SEEK_SET); + if (!reader.seek(0,SEEK_SET)) { + logD("couldn't seek to 0"); + throw EndOfFileException(&reader,reader.tell()); + } ds.name=reader.readString(20); + logI("%s",ds.name); // samples + logD("reading samples... (%d)",insCount); for (int i=0; idepth=8; sample->name=reader.readString(22); + logD("%d: %s",i+1,sample->name); int slen=((unsigned short)reader.readS_BE())*2; sampLens[i]=slen; if (slen==2) slen=0; @@ -1649,7 +1664,7 @@ bool DivEngine::loadMod(unsigned char* file, size_t len) { loopStart=0; loopLen=0; } - if(loopLen>=2) { + if (loopLen>=2) { if (loopEndloopStart=loopStart; } @@ -1660,7 +1675,21 @@ bool DivEngine::loadMod(unsigned char* file, size_t len) { // orders ds.ordersLen=ordCount=reader.readC(); - reader.readC(); // restart position, unused + if (ds.ordersLen<1 || ds.ordersLen>127) { + logD("invalid order count!"); + throw EndOfFileException(&reader,reader.tell()); + } + unsigned char restartPos=reader.readC(); // restart position, unused + logD("restart position byte: %.2x",restartPos); + if (insCount==15) { + if (restartPos>0x60 && restartPos<0x80) { + logD("detected a Soundtracker module"); + } else { + logD("no Soundtracker signature found"); + throw EndOfFileException(&reader,reader.tell()); + } + } + int patMax=0; for (int i=0; i<128; i++) { unsigned char pat=reader.readC(); @@ -1670,8 +1699,17 @@ bool DivEngine::loadMod(unsigned char* file, size_t len) { } } - // TODO: maybe change if this is a Soundtracker module? - reader.seek(1084,SEEK_SET); + if (insCount==15) { + if (!reader.seek(600,SEEK_SET)) { + logD("couldn't seek to 600"); + throw EndOfFileException(&reader,reader.tell()); + } + } else { + if (!reader.seek(1084,SEEK_SET)) { + logD("couldn't seek to 1084"); + throw EndOfFileException(&reader,reader.tell()); + } + } // patterns ds.patLen=64; @@ -1742,8 +1780,12 @@ bool DivEngine::loadMod(unsigned char* file, size_t len) { // samples size_t pos=reader.tell(); - for (int i=0; i<31; i++) { - reader.seek(pos,SEEK_SET); + logD("reading sample data..."); + for (int i=0; idata8,ds.sample[i]->samples); pos+=sampLens[i]; } @@ -1944,18 +1986,10 @@ bool DivEngine::load(unsigned char* f, size_t slen) { delete[] f; return false; } - if (memcmp(f,DIV_DMF_MAGIC,16)!=0 && memcmp(f,DIV_FUR_MAGIC,16)!=0) { - // try loading as a .mod first before trying to decompress - // TODO: move to a different location? - logD("loading as .mod..."); - if (loadMod(f,slen)) { - delete[] f; - return true; - } - - lastError="not a .mod song"; - logD("loading as zlib..."); - // try zlib + + // step 1: try loading as a zlib-compressed file + logD("trying zlib..."); + try { z_stream zl; memset(&zl,0,sizeof(z_stream)); @@ -1969,14 +2003,13 @@ bool DivEngine::load(unsigned char* f, size_t slen) { nextErr=inflateInit(&zl); if (nextErr!=Z_OK) { if (zl.msg==NULL) { - logE("zlib error: unknown! %d",nextErr); + logD("zlib error: unknown! %d",nextErr); } else { - logE("zlib error: %s",zl.msg); + logD("zlib error: %s",zl.msg); } inflateEnd(&zl); - delete[] f; - lastError="not a .dmf song"; - return false; + lastError="not a .dmf/.fur song"; + throw NotZlibException(0); } std::vector blocks; @@ -1988,18 +2021,17 @@ bool DivEngine::load(unsigned char* f, size_t slen) { nextErr=inflate(&zl,Z_SYNC_FLUSH); if (nextErr!=Z_OK && nextErr!=Z_STREAM_END) { if (zl.msg==NULL) { - logE("zlib error: unknown error! %d",nextErr); + logD("zlib error: unknown error! %d",nextErr); lastError="unknown decompression error"; } else { - logE("zlib inflate: %s",zl.msg); + logD("zlib inflate: %s",zl.msg); lastError=fmt::sprintf("decompression error: %s",zl.msg); } for (InflateBlock* i: blocks) delete i; blocks.clear(); delete ib; inflateEnd(&zl); - delete[] f; - return false; + throw NotZlibException(0); } ib->blockSize=ib->len-zl.avail_out; blocks.push_back(ib); @@ -2010,16 +2042,15 @@ bool DivEngine::load(unsigned char* f, size_t slen) { nextErr=inflateEnd(&zl); if (nextErr!=Z_OK) { if (zl.msg==NULL) { - logE("zlib end error: unknown error! %d",nextErr); + logD("zlib end error: unknown error! %d",nextErr); lastError="unknown decompression finish error"; } else { - logE("zlib end: %s",zl.msg); + logD("zlib end: %s",zl.msg); lastError=fmt::sprintf("decompression finish error: %s",zl.msg); } for (InflateBlock* i: blocks) delete i; blocks.clear(); - delete[] f; - return false; + throw NotZlibException(0); } size_t finalSize=0; @@ -2028,12 +2059,11 @@ bool DivEngine::load(unsigned char* f, size_t slen) { finalSize+=i->blockSize; } if (finalSize<1) { - logE("compressed too small!"); + logD("compressed too small!"); lastError="file too small"; for (InflateBlock* i: blocks) delete i; blocks.clear(); - delete[] f; - return false; + throw NotZlibException(0); } file=new unsigned char[finalSize]; for (InflateBlock* i: blocks) { @@ -2044,16 +2074,26 @@ bool DivEngine::load(unsigned char* f, size_t slen) { blocks.clear(); len=finalSize; delete[] f; - } else { - logD("loading as uncompressed"); - file=(unsigned char*)f; + } catch (NotZlibException& e) { + logD("not zlib. loading as raw..."); + file=f; len=slen; } + + // step 2: try loading as .fur or .dmf if (memcmp(file,DIV_DMF_MAGIC,16)==0) { return loadDMF(file,len); } else if (memcmp(file,DIV_FUR_MAGIC,16)==0) { return loadFur(file,len); } + + // step 3: try loading as .mod + if (loadMod(f,slen)) { + delete[] f; + return true; + } + + // step 4: not a valid file logE("not a valid module!"); lastError="not a compatible song"; delete[] file; diff --git a/src/gui/gui.h b/src/gui/gui.h index 136b666d7..934f09540 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -490,7 +490,8 @@ enum PasteMode { GUI_PASTE_MODE_OVERFLOW }; -#define FURKMOD_CTRL (1<<31) +// 1<<31 +#define FURKMOD_CTRL (-2147483648) #define FURKMOD_SHIFT (1<<29) #define FURKMOD_META (1<<28) #define FURKMOD_ALT (1<<27) From b77b9e61ecd3a0ef0ae08d219b0aaa72731ae155 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 15 Apr 2022 00:47:25 -0500 Subject: [PATCH 071/342] the ugliest way to fix klisje paa klisje --- src/engine/fileOps.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/engine/fileOps.cpp b/src/engine/fileOps.cpp index 5eaed546f..183ff81a1 100644 --- a/src/engine/fileOps.cpp +++ b/src/engine/fileOps.cpp @@ -1876,8 +1876,8 @@ bool DivEngine::loadMod(unsigned char* file, size_t len) { break; case 15: // set speed // TODO: somehow handle VBlank tunes - // TODO: klisje is still broken, perhaps because there wasn't tempo set back then? - if (fxVal>0x20) { + // TODO: i am so sorry + if (fxVal>0x20 && ds.name!="klisje paa klisje") { writeFxCol(0xf0,fxVal); } else { writeFxCol(0x09,fxVal); From 397ab43ffd25bb213f3517518b74c8f18630507e Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 15 Apr 2022 01:20:09 -0500 Subject: [PATCH 072/342] C64: better muting strategy now directly inside reSID rather than in the DivDispatch fixes #285 --- src/engine/platform/c64.cpp | 24 ++++++++++++++---------- src/engine/platform/c64.h | 1 + src/engine/platform/sound/c64/sid.cc | 17 +++++++++++++++-- src/engine/platform/sound/c64/sid.h | 3 +++ 4 files changed, 33 insertions(+), 12 deletions(-) diff --git a/src/engine/platform/c64.cpp b/src/engine/platform/c64.cpp index b7521f035..f8b014c8a 100644 --- a/src/engine/platform/c64.cpp +++ b/src/engine/platform/c64.cpp @@ -168,16 +168,16 @@ void DivPlatformC64::tick() { } if (chan[i].testWhen>0) { if (--chan[i].testWhen<1) { - if (!chan[i].resetMask && !isMuted[i]) { + if (!chan[i].resetMask) { rWrite(i*7+5,0); rWrite(i*7+6,0); - rWrite(i*7+4,(isMuted[i]?8:(chan[i].wave<<4))|8|(chan[i].ring<<2)|(chan[i].sync<<1)); + rWrite(i*7+4,(chan[i].wave<<4)|8|(chan[i].ring<<2)|(chan[i].sync<<1)); } } } if (chan[i].std.wave.had) { chan[i].wave=chan[i].std.wave.val; - rWrite(i*7+4,(isMuted[i]?8:(chan[i].wave<<4))|(chan[i].ring<<2)|(chan[i].sync<<1)|(int)(chan[i].active)); + rWrite(i*7+4,(chan[i].wave<<4)|(chan[i].ring<<2)|(chan[i].sync<<1)|(int)(chan[i].active)); } if (chan[i].std.ex1.had) { filtControl=chan[i].std.ex1.val&15; @@ -199,12 +199,12 @@ void DivPlatformC64::tick() { if (chan[i].keyOn) { rWrite(i*7+5,(chan[i].attack<<4)|(chan[i].decay)); rWrite(i*7+6,(chan[i].sustain<<4)|(chan[i].release)); - rWrite(i*7+4,(isMuted[i]?8:(chan[i].wave<<4))|(chan[i].ring<<2)|(chan[i].sync<<1)|1); + rWrite(i*7+4,(chan[i].wave<<4)|(chan[i].ring<<2)|(chan[i].sync<<1)|1); } - if (chan[i].keyOff && !isMuted[i]) { + if (chan[i].keyOff) { rWrite(i*7+5,(chan[i].attack<<4)|(chan[i].decay)); rWrite(i*7+6,(chan[i].sustain<<4)|(chan[i].release)); - rWrite(i*7+4,(isMuted[i]?8:(chan[i].wave<<4))|(chan[i].ring<<2)|(chan[i].sync<<1)|0); + rWrite(i*7+4,(chan[i].wave<<4)|(chan[i].ring<<2)|(chan[i].sync<<1)|0); } rWrite(i*7,chan[i].freq&0xff); rWrite(i*7+1,chan[i].freq>>8); @@ -330,7 +330,7 @@ int DivPlatformC64::dispatch(DivCommand c) { break; case DIV_CMD_WAVE: chan[c.chan].wave=c.value; - rWrite(c.chan*7+4,(isMuted[c.chan]?8:(chan[c.chan].wave<<4))|(chan[c.chan].ring<<2)|(chan[c.chan].sync<<1)|(int)(chan[c.chan].active)); + rWrite(c.chan*7+4,(chan[c.chan].wave<<4)|(chan[c.chan].ring<<2)|(chan[c.chan].sync<<1)|(int)(chan[c.chan].active)); break; case DIV_CMD_LEGATO: chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value+((chan[c.chan].std.arp.will && !chan[c.chan].std.arp.mode)?(chan[c.chan].std.arp.val):(0))); @@ -411,11 +411,11 @@ int DivPlatformC64::dispatch(DivCommand c) { break; case 4: chan[c.chan].ring=c.value; - rWrite(c.chan*7+4,(isMuted[c.chan]?8:(chan[c.chan].wave<<4))|(chan[c.chan].ring<<2)|(chan[c.chan].sync<<1)|(int)(chan[c.chan].active)); + rWrite(c.chan*7+4,(chan[c.chan].wave<<4)|(chan[c.chan].ring<<2)|(chan[c.chan].sync<<1)|(int)(chan[c.chan].active)); break; case 5: chan[c.chan].sync=c.value; - rWrite(c.chan*7+4,(isMuted[c.chan]?8:(chan[c.chan].wave<<4))|(chan[c.chan].ring<<2)|(chan[c.chan].sync<<1)|(int)(chan[c.chan].active)); + rWrite(c.chan*7+4,(chan[c.chan].wave<<4)|(chan[c.chan].ring<<2)|(chan[c.chan].sync<<1)|(int)(chan[c.chan].active)); break; case 6: filtControl&=7; @@ -434,7 +434,7 @@ int DivPlatformC64::dispatch(DivCommand c) { void DivPlatformC64::muteChannel(int ch, bool mute) { isMuted[ch]=mute; - rWrite(ch*7+4,(isMuted[ch]?8:(chan[ch].wave<<4))|(chan[ch].ring<<2)|(chan[ch].sync<<1)|(int)(chan[ch].active)); + sid.set_is_muted(ch,mute); } void DivPlatformC64::forceIns() { @@ -475,6 +475,10 @@ int DivPlatformC64::getRegisterPoolSize() { return 32; } +bool DivPlatformC64::getDCOffRequired() { + return true; +} + void DivPlatformC64::reset() { for (int i=0; i<3; i++) { chan[i]=DivPlatformC64::Channel(); diff --git a/src/engine/platform/c64.h b/src/engine/platform/c64.h index 9045e3332..e288940e5 100644 --- a/src/engine/platform/c64.h +++ b/src/engine/platform/c64.h @@ -87,6 +87,7 @@ class DivPlatformC64: public DivDispatch { void muteChannel(int ch, bool mute); void setFlags(unsigned int flags); void notifyInsChange(int ins); + bool getDCOffRequired(); void notifyInsDeletion(void* ins); void poke(unsigned int addr, unsigned short val); void poke(std::vector& wlist); diff --git a/src/engine/platform/sound/c64/sid.cc b/src/engine/platform/sound/c64/sid.cc index 73d5c2bb3..d33a0d350 100644 --- a/src/engine/platform/sound/c64/sid.cc +++ b/src/engine/platform/sound/c64/sid.cc @@ -18,6 +18,7 @@ // --------------------------------------------------------------------------- #include "sid.h" +#include #include // ---------------------------------------------------------------------------- @@ -39,6 +40,10 @@ SID::SID() bus_value_ttl = 0; ext_in = 0; + + isMuted[0]=false; + isMuted[1]=false; + isMuted[2]=false; } @@ -51,6 +56,14 @@ SID::~SID() delete[] fir; } +// ---------------------------------------------------------------------------- +// Mute/unmute channel. +// ---------------------------------------------------------------------------- +void SID::set_is_muted(int ch, bool val) { + if (ch<0 || ch>2) return; + isMuted[ch]=val; +} + // ---------------------------------------------------------------------------- // Set chip model. @@ -626,7 +639,7 @@ void SID::clock() } // Clock filter. - filter.clock(voice[0].output(), voice[1].output(), voice[2].output(), ext_in); + filter.clock(isMuted[0]?0:voice[0].output(), isMuted[1]?0:voice[1].output(), isMuted[2]?0:voice[2].output(), ext_in); // Clock external filter. extfilt.clock(filter.output()); @@ -706,7 +719,7 @@ void SID::clock(cycle_count delta_t) // Clock filter. filter.clock(delta_t, - voice[0].output(), voice[1].output(), voice[2].output(), ext_in); + isMuted[0]?0:voice[0].output(), isMuted[1]?0:voice[1].output(), isMuted[2]?0:voice[2].output(), ext_in); // Clock external filter. extfilt.clock(delta_t, filter.output()); diff --git a/src/engine/platform/sound/c64/sid.h b/src/engine/platform/sound/c64/sid.h index 3292cd8fc..6dbe6c599 100644 --- a/src/engine/platform/sound/c64/sid.h +++ b/src/engine/platform/sound/c64/sid.h @@ -32,6 +32,7 @@ public: SID(); ~SID(); + void set_is_muted(int ch, bool val); void set_chip_model(chip_model model); void enable_filter(bool enable); void enable_external_filter(bool enable); @@ -102,6 +103,8 @@ protected: Potentiometer potx; Potentiometer poty; + bool isMuted[3]; + reg8 bus_value; cycle_count bus_value_ttl; From a530934671de0c60cbfca038edd8698bb4bc2250 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 15 Apr 2022 01:26:44 -0500 Subject: [PATCH 073/342] fix retrigger issue in sample instruments --- src/engine/platform/genesis.cpp | 6 ++++-- src/engine/platform/nes.cpp | 2 +- src/engine/platform/segapcm.cpp | 6 ++++-- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/engine/platform/genesis.cpp b/src/engine/platform/genesis.cpp index e85bc7688..f00a521bc 100644 --- a/src/engine/platform/genesis.cpp +++ b/src/engine/platform/genesis.cpp @@ -493,8 +493,10 @@ int DivPlatformGenesis::dispatch(DivCommand c) { } dacPos=0; dacPeriod=0; - chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value); - chan[c.chan].freqChanged=true; + if (c.value!=DIV_NOTE_NULL) { + chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value); + chan[c.chan].freqChanged=true; + } chan[c.chan].furnaceDac=true; } else { // compatible mode if (c.value!=DIV_NOTE_NULL) { diff --git a/src/engine/platform/nes.cpp b/src/engine/platform/nes.cpp index 9f56e4e56..e99a3e29e 100644 --- a/src/engine/platform/nes.cpp +++ b/src/engine/platform/nes.cpp @@ -274,8 +274,8 @@ int DivPlatformNES::dispatch(DivCommand c) { } dacPos=0; dacPeriod=0; - chan[c.chan].baseFreq=parent->song.tuning*pow(2.0f,((float)(c.value+3)/12.0f)); if (c.value!=DIV_NOTE_NULL) { + chan[c.chan].baseFreq=parent->song.tuning*pow(2.0f,((float)(c.value+3)/12.0f)); chan[c.chan].freqChanged=true; chan[c.chan].note=c.value; } diff --git a/src/engine/platform/segapcm.cpp b/src/engine/platform/segapcm.cpp index b87ee9a86..84ec00c37 100644 --- a/src/engine/platform/segapcm.cpp +++ b/src/engine/platform/segapcm.cpp @@ -142,8 +142,10 @@ int DivPlatformSegaPCM::dispatch(DivCommand c) { break; } chan[c.chan].pcm.pos=0; - chan[c.chan].baseFreq=(c.value<<6); - chan[c.chan].freqChanged=true; + if (c.value!=DIV_NOTE_NULL) { + chan[c.chan].baseFreq=(c.value<<6); + chan[c.chan].freqChanged=true; + } chan[c.chan].furnacePCM=true; if (dumpWrites) { // Sega PCM writes DivSample* s=parent->getSample(chan[c.chan].pcm.sample); From f27942d77d0e7a23145721da79e8e58665f3fb91 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 15 Apr 2022 01:27:09 -0500 Subject: [PATCH 074/342] update to-do list --- TODO.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/TODO.md b/TODO.md index 1cef798f3..22efa331b 100644 --- a/TODO.md +++ b/TODO.md @@ -19,6 +19,7 @@ - ability to customize startup system - store system presets in new file - Game Boy envelope macro/sequence +- Game Boy envelope view - option to display chip names instead of "multi-system" on title bar - rewrite the system name detection function anyway - add nightly.link @@ -40,3 +41,7 @@ - add ability to select a column by double clicking - add ability to move selection by dragging - NSFPlay core for NES +- option for standard waveform names in OPL +- settings: OK/Cancel buttons should be always visible +- Apply button in settings +- better FM chip names (number and codename) From 600c38543b469f6f68543c3c493277128fd27f82 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 15 Apr 2022 01:29:29 -0500 Subject: [PATCH 075/342] what is valid then --- src/gui/gui.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/gui/gui.h b/src/gui/gui.h index 934f09540..5843a3c14 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -490,8 +490,7 @@ enum PasteMode { GUI_PASTE_MODE_OVERFLOW }; -// 1<<31 -#define FURKMOD_CTRL (-2147483648) +#define FURKMOD_CTRL (1U<<31) #define FURKMOD_SHIFT (1<<29) #define FURKMOD_META (1<<28) #define FURKMOD_ALT (1<<27) From f9e9c6c179a701b441d1d9f5e83dadfd26eea884 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 15 Apr 2022 01:30:21 -0500 Subject: [PATCH 076/342] huh --- src/gui/gui.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index d42710204..d8384f9a1 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -1621,14 +1621,13 @@ int FurnaceGUI::load(String path) { fclose(f); return 1; } - unsigned char* file=new unsigned char[len]; if (fseek(f,0,SEEK_SET)<0) { perror("size error"); lastError=fmt::sprintf("on get size: %s",strerror(errno)); fclose(f); - delete[] file; return 1; } + unsigned char* file=new unsigned char[len]; if (fread(file,1,(size_t)len,f)!=(size_t)len) { perror("read error"); lastError=fmt::sprintf("on read: %s",strerror(errno)); From c76e719371115e31a5fc98b285a3be48dfa3aa54 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 15 Apr 2022 02:51:38 -0500 Subject: [PATCH 077/342] GUI: prepare for more macros --- src/gui/gui.cpp | 3 +- src/gui/gui.h | 3 +- src/gui/insEdit.cpp | 116 ++++++++++++++++++++++++++++---------------- 3 files changed, 77 insertions(+), 45 deletions(-) diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index d8384f9a1..ba83b8654 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -3546,7 +3546,8 @@ FurnaceGUI::FurnaceGUI(): samplePreviewOn(false), samplePreviewKey((SDL_Scancode)0), samplePreviewNote(0), - arpMacroScroll(0), + arpMacroScroll(-12), + pitchMacroScroll(-80), macroDragStart(0,0), macroDragAreaSize(0,0), macroDragCTarget(NULL), diff --git a/src/gui/gui.h b/src/gui/gui.h index 5843a3c14..40d0b9c44 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -708,7 +708,7 @@ class FurnaceGUI { String workingDir, fileName, clipboard, warnString, errorString, lastError, curFileName, nextFile; String workingDirSong, workingDirIns, workingDirWave, workingDirSample, workingDirAudioExport, workingDirVGMExport, workingDirFont, workingDirColors, workingDirKeybinds, workingDirLayout; - String mmlString[13]; + String mmlString[17]; String mmlStringW; bool quit, warnQuit, willCommit, edit, modified, displayError, displayExporting, vgmExportLoop; @@ -974,6 +974,7 @@ class FurnaceGUI { std::map valueKeys; int arpMacroScroll; + int pitchMacroScroll; ImVec2 macroDragStart; ImVec2 macroDragAreaSize; diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index 1f84fea10..f91e4c405 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -1005,7 +1005,7 @@ void FurnaceGUI::drawFMEnv(unsigned char tl, unsigned char ar, unsigned char dr, #define PARAMETER MARK_MODIFIED; e->notifyInsChange(curIns); -#define NORMAL_MACRO(macro,macroMin,macroHeight,macroName,displayName,displayHeight,displayLoop,bitfield,bfVal,drawSlider,sliderVal,sliderLow,macroDispMin,bitOff,macroMode,macroModeMax,displayModeName,macroColor,mmlStr,macroAMin,macroAMax,hoverFunc,blockMode) \ +#define NORMAL_MACRO(macro,macroMin,macroHeight,macroName,displayName,displayHeight,displayLoop,bitfield,bfVal,drawSlider,sliderVal,sliderLow,sliderHigh,macroDispMin,bitOff,macroMode,macroModeMax,displayModeName,macroColor,mmlStr,macroAMin,macroAMax,hoverFunc,blockMode) \ ImGui::TableNextRow(); \ ImGui::TableNextColumn(); \ ImGui::Text("%s",displayName); \ @@ -1067,7 +1067,7 @@ void FurnaceGUI::drawFMEnv(unsigned char tl, unsigned char ar, unsigned char dr, if (displayLoop) { \ if (drawSlider) { \ ImGui::SameLine(); \ - CWVSliderInt("##IArpMacroPos",ImVec2(20.0f*dpiScale,displayHeight*dpiScale),sliderVal,sliderLow,70); \ + CWVSliderInt("##IMacroPos_" macroName,ImVec2(20.0f*dpiScale,displayHeight*dpiScale),sliderVal,sliderLow,sliderHigh); \ } \ PlotCustom("##IMacroLoop_" macroName,loopIndicator,totalFit,macroDragScroll,NULL,0,2,ImVec2(availableWidth,12.0f*dpiScale),sizeof(float),macroColor,macro.len-macroDragScroll,¯oHoverLoop); \ if (ImGui::IsItemClicked(ImGuiMouseButton_Left)) { \ @@ -2126,31 +2126,31 @@ void FurnaceGUI::drawInsEdit() { if (ImGui::BeginTabItem("FM Macros")) { MACRO_BEGIN(0); if (ins->type==DIV_INS_OPLL) { - NORMAL_MACRO(ins->std.algMacro,0,1,"alg",FM_NAME(FM_SUS),32,ins->std.algMacro.open,true,NULL,false,NULL,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[0],0,1,NULL,false); - NORMAL_MACRO(ins->std.fbMacro,0,7,"fb",FM_NAME(FM_FB),96,ins->std.fbMacro.open,false,NULL,false,NULL,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[1],0,7,NULL,false); - NORMAL_MACRO(ins->std.fmsMacro,0,1,"fms",FM_NAME(FM_DC),32,ins->std.fmsMacro.open,true,NULL,false,NULL,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[2],0,1,NULL,false); - NORMAL_MACRO(ins->std.amsMacro,0,1,"ams",FM_NAME(FM_DM),32,ins->std.amsMacro.open,true,NULL,false,NULL,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[3],0,1,NULL,false); + NORMAL_MACRO(ins->std.algMacro,0,1,"alg",FM_NAME(FM_SUS),32,ins->std.algMacro.open,true,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[0],0,1,NULL,false); + NORMAL_MACRO(ins->std.fbMacro,0,7,"fb",FM_NAME(FM_FB),96,ins->std.fbMacro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[1],0,7,NULL,false); + NORMAL_MACRO(ins->std.fmsMacro,0,1,"fms",FM_NAME(FM_DC),32,ins->std.fmsMacro.open,true,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[2],0,1,NULL,false); + NORMAL_MACRO(ins->std.amsMacro,0,1,"ams",FM_NAME(FM_DM),32,ins->std.amsMacro.open,true,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[3],0,1,NULL,false); } else { - NORMAL_MACRO(ins->std.algMacro,0,7,"alg",FM_NAME(FM_ALG),96,ins->std.algMacro.open,false,NULL,false,NULL,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[0],0,7,NULL,false); - NORMAL_MACRO(ins->std.fbMacro,0,7,"fb",FM_NAME(FM_FB),96,ins->std.fbMacro.open,false,NULL,false,NULL,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[1],0,7,NULL,false); + NORMAL_MACRO(ins->std.algMacro,0,7,"alg",FM_NAME(FM_ALG),96,ins->std.algMacro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[0],0,7,NULL,false); + NORMAL_MACRO(ins->std.fbMacro,0,7,"fb",FM_NAME(FM_FB),96,ins->std.fbMacro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[1],0,7,NULL,false); if (ins->type!=DIV_INS_OPL) { if (ins->type==DIV_INS_OPZ) { // TODO: FMS2/AMS2 macros - NORMAL_MACRO(ins->std.fmsMacro,0,7,"fms",FM_NAME(FM_FMS),96,ins->std.fmsMacro.open,false,NULL,false,NULL,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[2],0,7,NULL,false); - NORMAL_MACRO(ins->std.amsMacro,0,3,"ams",FM_NAME(FM_AMS),48,ins->std.amsMacro.open,false,NULL,false,NULL,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[4],0,3,NULL,false); + NORMAL_MACRO(ins->std.fmsMacro,0,7,"fms",FM_NAME(FM_FMS),96,ins->std.fmsMacro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[2],0,7,NULL,false); + NORMAL_MACRO(ins->std.amsMacro,0,3,"ams",FM_NAME(FM_AMS),48,ins->std.amsMacro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[4],0,3,NULL,false); } else { - NORMAL_MACRO(ins->std.fmsMacro,0,7,"fms",FM_NAME(FM_FMS),96,ins->std.fmsMacro.open,false,NULL,false,NULL,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[2],0,7,NULL,false); - NORMAL_MACRO(ins->std.amsMacro,0,3,"ams",FM_NAME(FM_AMS),48,ins->std.amsMacro.open,false,NULL,false,NULL,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[3],0,3,NULL,false); + NORMAL_MACRO(ins->std.fmsMacro,0,7,"fms",FM_NAME(FM_FMS),96,ins->std.fmsMacro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[2],0,7,NULL,false); + NORMAL_MACRO(ins->std.amsMacro,0,3,"ams",FM_NAME(FM_AMS),48,ins->std.amsMacro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[3],0,3,NULL,false); } } } if (ins->type==DIV_INS_FM || ins->type==DIV_INS_OPZ) { - NORMAL_MACRO(ins->std.ex1Macro,0,127,"ex1","AM Depth",128,ins->std.ex1Macro.open,false,NULL,false,NULL,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[6],0,127,NULL,false); - NORMAL_MACRO(ins->std.ex2Macro,0,127,"ex2","PM Depth",128,ins->std.ex2Macro.open,false,NULL,false,NULL,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[7],0,127,NULL,false); - NORMAL_MACRO(ins->std.ex3Macro,0,255,"ex3","LFO Speed",128,ins->std.ex3Macro.open,false,NULL,false,NULL,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[8],0,255,NULL,false); - NORMAL_MACRO(ins->std.waveMacro,0,3,"wave","LFO Shape",48,ins->std.waveMacro.open,false,NULL,false,NULL,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_WAVE],mmlString[9],0,3,¯oLFOWaves,false); - NORMAL_MACRO(ins->std.ex4Macro,0,4,"ex4","OpMask",128,ins->std.ex4Macro.open,true,fmOperatorBits,false,NULL,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[10],0,4,NULL,false); + NORMAL_MACRO(ins->std.ex1Macro,0,127,"ex1","AM Depth",128,ins->std.ex1Macro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[6],0,127,NULL,false); + NORMAL_MACRO(ins->std.ex2Macro,0,127,"ex2","PM Depth",128,ins->std.ex2Macro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[7],0,127,NULL,false); + NORMAL_MACRO(ins->std.ex3Macro,0,255,"ex3","LFO Speed",128,ins->std.ex3Macro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[8],0,255,NULL,false); + NORMAL_MACRO(ins->std.waveMacro,0,3,"wave","LFO Shape",48,ins->std.waveMacro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_WAVE],mmlString[9],0,3,¯oLFOWaves,false); + NORMAL_MACRO(ins->std.ex4Macro,0,4,"ex4","OpMask",128,ins->std.ex4Macro.open,true,fmOperatorBits,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[10],0,4,NULL,false); } MACRO_END; ImGui::EndTabItem(); @@ -2716,70 +2716,100 @@ void FurnaceGUI::drawInsEdit() { } if (ins->type==DIV_INS_SAA1099) ex1Max=8; + int panMax=0; + if (ins->type==DIV_INS_FM || ins->type==DIV_INS_OPL || ins->type==DIV_INS_GB || ins->type==DIV_INS_OPZ || ins->type==DIV_INS_VERA) { + panMax=1; + } + if (ins->type==DIV_INS_AMIGA) { + panMax=127; + } + if (ins->type==DIV_INS_X1_010 || ins->type==DIV_INS_PCE) { + panMax=15; + } + if (settings.macroView==0) { // modern view MACRO_BEGIN(28*dpiScale); if (volMax>0) { - NORMAL_MACRO(ins->std.volMacro,volMin,volMax,"vol",volumeLabel,160,ins->std.volMacro.open,false,NULL,false,NULL,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_VOLUME],mmlString[0],volMin,volMax,NULL,false); + NORMAL_MACRO(ins->std.volMacro,volMin,volMax,"vol",volumeLabel,160,ins->std.volMacro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_VOLUME],mmlString[0],volMin,volMax,NULL,false); } - NORMAL_MACRO(ins->std.arpMacro,arpMacroScroll,arpMacroScroll+24,"arp","Arpeggio",160,ins->std.arpMacro.open,false,NULL,true,&arpMacroScroll,(arpMode?-60:-80),0,0,true,1,macroAbsoluteMode,uiColors[GUI_COLOR_MACRO_PITCH],mmlString[1],-92,94,(ins->std.arpMacro.mode?(¯oHoverNote):NULL),true); + NORMAL_MACRO(ins->std.arpMacro,arpMacroScroll,arpMacroScroll+24,"arp","Arpeggio",160,ins->std.arpMacro.open,false,NULL,true,&arpMacroScroll,(arpMode?-60:-80),70,0,0,true,1,macroAbsoluteMode,uiColors[GUI_COLOR_MACRO_PITCH],mmlString[1],-92,94,(ins->std.arpMacro.mode?(¯oHoverNote):NULL),true); if (dutyMax>0) { if (ins->type==DIV_INS_MIKEY) { - NORMAL_MACRO(ins->std.dutyMacro,0,dutyMax,"duty",dutyLabel,160,ins->std.dutyMacro.open,true,mikeyFeedbackBits,false,NULL,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[2],0,dutyMax,NULL,false); + NORMAL_MACRO(ins->std.dutyMacro,0,dutyMax,"duty",dutyLabel,160,ins->std.dutyMacro.open,true,mikeyFeedbackBits,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[2],0,dutyMax,NULL,false); } else if (ins->type==DIV_INS_C64) { - NORMAL_MACRO(ins->std.dutyMacro,0,dutyMax,"duty",dutyLabel,160,ins->std.dutyMacro.open,false,NULL,false,NULL,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[2],0,dutyMax,NULL,false); + NORMAL_MACRO(ins->std.dutyMacro,0,dutyMax,"duty",dutyLabel,160,ins->std.dutyMacro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[2],0,dutyMax,NULL,false); } else { - NORMAL_MACRO(ins->std.dutyMacro,0,dutyMax,"duty",dutyLabel,160,ins->std.dutyMacro.open,false,NULL,false,NULL,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[2],0,dutyMax,NULL,false); + NORMAL_MACRO(ins->std.dutyMacro,0,dutyMax,"duty",dutyLabel,160,ins->std.dutyMacro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[2],0,dutyMax,NULL,false); } } if (waveMax>0) { - NORMAL_MACRO(ins->std.waveMacro,0,waveMax,"wave",waveLabel,(bitMode && ins->type!=DIV_INS_PET)?64:160,ins->std.waveMacro.open,bitMode,waveNames,false,NULL,0,0,((ins->type==DIV_INS_AY || ins->type==DIV_INS_AY8930)?1:0),false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_WAVE],mmlString[3],0,waveMax,NULL,false); + NORMAL_MACRO(ins->std.waveMacro,0,waveMax,"wave",waveLabel,(bitMode && ins->type!=DIV_INS_PET)?64:160,ins->std.waveMacro.open,bitMode,waveNames,false,NULL,0,0,0,((ins->type==DIV_INS_AY || ins->type==DIV_INS_AY8930)?1:0),false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_WAVE],mmlString[3],0,waveMax,NULL,false); + } + if (panMax>0) { + NORMAL_MACRO(ins->std.panLMacro,0,panMax,"panL","Panning (left)",(31+panMax),ins->std.panLMacro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[13],0,panMax,NULL,false); + NORMAL_MACRO(ins->std.panRMacro,0,panMax,"panR","Panning (right)",(31+panMax),ins->std.panRMacro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[14],0,panMax,NULL,false); + } + NORMAL_MACRO(ins->std.pitchMacro,pitchMacroScroll,pitchMacroScroll+160,"pitch","Pitch",160,ins->std.pitchMacro.open,false,NULL,true,&pitchMacroScroll,-2048,2047,0,0,true,1,macroAbsoluteMode,uiColors[GUI_COLOR_MACRO_PITCH],mmlString[15],-2048,2047,NULL,!ins->std.pitchMacro.mode); + if (ins->type==DIV_INS_FM || + ins->type==DIV_INS_STD || + ins->type==DIV_INS_OPL || + ins->type==DIV_INS_OPZ || + ins->type==DIV_INS_PCE || + ins->type==DIV_INS_GB || + ins->type==DIV_INS_AMIGA || + ins->type==DIV_INS_OPLL || + ins->type==DIV_INS_FDS || + ins->type==DIV_INS_AY || + ins->type==DIV_INS_AY8930 || + ins->type==DIV_INS_SWAN) { + NORMAL_MACRO(ins->std.phaseResetMacro,0,1,"phaseReset","Phase Reset",32,ins->std.phaseResetMacro.open,true,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[16],0,1,NULL,false); } if (ex1Max>0) { if (ins->type==DIV_INS_C64) { - NORMAL_MACRO(ins->std.ex1Macro,0,ex1Max,"ex1","Filter Mode",64,ins->std.ex1Macro.open,true,filtModeBits,false,NULL,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[4],0,ex1Max,NULL,false); + NORMAL_MACRO(ins->std.ex1Macro,0,ex1Max,"ex1","Filter Mode",64,ins->std.ex1Macro.open,true,filtModeBits,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[4],0,ex1Max,NULL,false); } else if (ins->type==DIV_INS_SAA1099) { - NORMAL_MACRO(ins->std.ex1Macro,0,ex1Max,"ex1","Envelope",160,ins->std.ex1Macro.open,true,saaEnvBits,false,NULL,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[4],0,ex1Max,NULL,false); + NORMAL_MACRO(ins->std.ex1Macro,0,ex1Max,"ex1","Envelope",160,ins->std.ex1Macro.open,true,saaEnvBits,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[4],0,ex1Max,NULL,false); } else if (ins->type==DIV_INS_X1_010) { - NORMAL_MACRO(ins->std.ex1Macro,0,ex1Max,"ex1","Envelope Mode",160,ins->std.ex1Macro.open,true,x1_010EnvBits,false,NULL,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[4],0,ex1Max,NULL,false); + NORMAL_MACRO(ins->std.ex1Macro,0,ex1Max,"ex1","Envelope Mode",160,ins->std.ex1Macro.open,true,x1_010EnvBits,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[4],0,ex1Max,NULL,false); } else if (ins->type==DIV_INS_N163) { - NORMAL_MACRO(ins->std.ex1Macro,0,ex1Max,"ex1","Waveform len.",160,ins->std.ex1Macro.open,false,NULL,false,NULL,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[4],0,ex1Max,NULL,false); + NORMAL_MACRO(ins->std.ex1Macro,0,ex1Max,"ex1","Waveform len.",160,ins->std.ex1Macro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[4],0,ex1Max,NULL,false); } else if (ins->type==DIV_INS_FDS) { - NORMAL_MACRO(ins->std.ex1Macro,0,ex1Max,"ex1","Mod Depth",160,ins->std.ex1Macro.open,false,NULL,false,NULL,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[4],0,ex1Max,NULL,false); + NORMAL_MACRO(ins->std.ex1Macro,0,ex1Max,"ex1","Mod Depth",160,ins->std.ex1Macro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[4],0,ex1Max,NULL,false); } else { - NORMAL_MACRO(ins->std.ex1Macro,0,ex1Max,"ex1","Duty",160,ins->std.ex1Macro.open,false,NULL,false,NULL,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[4],0,ex1Max,NULL,false); + NORMAL_MACRO(ins->std.ex1Macro,0,ex1Max,"ex1","Duty",160,ins->std.ex1Macro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[4],0,ex1Max,NULL,false); } } if (ex2Max>0) { if (ins->type==DIV_INS_C64) { - NORMAL_MACRO(ins->std.ex2Macro,0,ex2Max,"ex2","Resonance",64,ins->std.ex2Macro.open,false,NULL,false,NULL,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[5],0,ex2Max,NULL,false); + NORMAL_MACRO(ins->std.ex2Macro,0,ex2Max,"ex2","Resonance",64,ins->std.ex2Macro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[5],0,ex2Max,NULL,false); } else if (ins->type==DIV_INS_N163) { - NORMAL_MACRO(ins->std.ex2Macro,0,ex2Max,"ex2","Waveform update",64,ins->std.ex2Macro.open,true,n163UpdateBits,false,NULL,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[5],0,ex2Max,NULL,false); + NORMAL_MACRO(ins->std.ex2Macro,0,ex2Max,"ex2","Waveform update",64,ins->std.ex2Macro.open,true,n163UpdateBits,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[5],0,ex2Max,NULL,false); } else if (ins->type==DIV_INS_FDS) { - NORMAL_MACRO(ins->std.ex2Macro,0,ex2Max,"ex2","Mod Speed",160,ins->std.ex2Macro.open,false,NULL,false,NULL,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[5],0,ex2Max,NULL,false); + NORMAL_MACRO(ins->std.ex2Macro,0,ex2Max,"ex2","Mod Speed",160,ins->std.ex2Macro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[5],0,ex2Max,NULL,false); } else { - NORMAL_MACRO(ins->std.ex2Macro,0,ex2Max,"ex2","Envelope",ex2Bit?64:160,ins->std.ex2Macro.open,ex2Bit,ayEnvBits,false,NULL,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[5],0,ex2Max,NULL,false); + NORMAL_MACRO(ins->std.ex2Macro,0,ex2Max,"ex2","Envelope",ex2Bit?64:160,ins->std.ex2Macro.open,ex2Bit,ayEnvBits,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[5],0,ex2Max,NULL,false); } } if (ins->type==DIV_INS_C64) { - NORMAL_MACRO(ins->std.ex3Macro,0,2,"ex3","Special",32,ins->std.ex3Macro.open,true,c64SpecialBits,false,NULL,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[6],0,2,NULL,false); + NORMAL_MACRO(ins->std.ex3Macro,0,2,"ex3","Special",32,ins->std.ex3Macro.open,true,c64SpecialBits,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[6],0,2,NULL,false); } if (ins->type==DIV_INS_AY || ins->type==DIV_INS_AY8930 || ins->type==DIV_INS_X1_010) { - NORMAL_MACRO(ins->std.ex3Macro,0,15,"ex3","AutoEnv Num",96,ins->std.ex3Macro.open,false,NULL,false,NULL,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[6],0,15,NULL,false); - NORMAL_MACRO(ins->std.algMacro,0,15,"alg","AutoEnv Den",96,ins->std.algMacro.open,false,NULL,false,NULL,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[7],0,15,NULL,false); + NORMAL_MACRO(ins->std.ex3Macro,0,15,"ex3","AutoEnv Num",96,ins->std.ex3Macro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[6],0,15,NULL,false); + NORMAL_MACRO(ins->std.algMacro,0,15,"alg","AutoEnv Den",96,ins->std.algMacro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[7],0,15,NULL,false); } if (ins->type==DIV_INS_AY8930) { // oh my i am running out of macros - NORMAL_MACRO(ins->std.fbMacro,0,8,"fb","Noise AND Mask",96,ins->std.fbMacro.open,true,NULL,false,NULL,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[8],0,8,NULL,false); - NORMAL_MACRO(ins->std.fmsMacro,0,8,"fms","Noise OR Mask",96,ins->std.fmsMacro.open,true,NULL,false,NULL,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[9],0,8,NULL,false); + NORMAL_MACRO(ins->std.fbMacro,0,8,"fb","Noise AND Mask",96,ins->std.fbMacro.open,true,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[8],0,8,NULL,false); + NORMAL_MACRO(ins->std.fmsMacro,0,8,"fms","Noise OR Mask",96,ins->std.fmsMacro.open,true,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[9],0,8,NULL,false); } if (ins->type==DIV_INS_N163) { - NORMAL_MACRO(ins->std.ex3Macro,0,255,"ex3","Waveform to Load",160,ins->std.ex3Macro.open,false,NULL,false,NULL,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[6],0,255,NULL,false); - NORMAL_MACRO(ins->std.algMacro,0,255,"alg","Wave pos. to Load",160,ins->std.algMacro.open,false,NULL,false,NULL,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[7],0,255,NULL,false); - NORMAL_MACRO(ins->std.fbMacro,0,252,"fb","Wave len. to Load",160,ins->std.fbMacro.open,false,NULL,false,NULL,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[8],0,252,NULL,false); - NORMAL_MACRO(ins->std.fmsMacro,0,2,"fms","Waveform load",64,ins->std.fmsMacro.open,true,n163UpdateBits,false,NULL,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[9],0,2,NULL,false); + NORMAL_MACRO(ins->std.ex3Macro,0,255,"ex3","Waveform to Load",160,ins->std.ex3Macro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[6],0,255,NULL,false); + NORMAL_MACRO(ins->std.algMacro,0,255,"alg","Wave pos. to Load",160,ins->std.algMacro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[7],0,255,NULL,false); + NORMAL_MACRO(ins->std.fbMacro,0,252,"fb","Wave len. to Load",160,ins->std.fbMacro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[8],0,252,NULL,false); + NORMAL_MACRO(ins->std.fmsMacro,0,2,"fms","Waveform load",64,ins->std.fmsMacro.open,true,n163UpdateBits,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[9],0,2,NULL,false); } if (ins->type==DIV_INS_FDS) { - NORMAL_MACRO(ins->std.ex3Macro,0,127,"ex3","Mod Position",160,ins->std.ex3Macro.open,false,NULL,false,NULL,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[6],0,2,NULL,false); + NORMAL_MACRO(ins->std.ex3Macro,0,127,"ex3","Mod Position",160,ins->std.ex3Macro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[6],0,2,NULL,false); } MACRO_END; From c743a9e96a46a1ef2e2bc3d88ad2f14063b49ba4 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 15 Apr 2022 03:16:14 -0500 Subject: [PATCH 078/342] implement phase reset macro on supported systems --- src/engine/platform/amiga.cpp | 5 +++++ src/engine/platform/arcade.cpp | 6 ++++++ src/engine/platform/ay.cpp | 6 ++++++ src/engine/platform/ay8930.cpp | 6 ++++++ src/engine/platform/gb.cpp | 5 +++++ src/engine/platform/genesis.cpp | 6 ++++++ src/engine/platform/mmc5.cpp | 6 ++++++ src/engine/platform/nes.cpp | 6 ++++++ src/engine/platform/opl.cpp | 6 ++++++ src/engine/platform/opll.cpp | 6 ++++++ src/engine/platform/pce.cpp | 2 +- src/engine/platform/sms.cpp | 17 ++++++++++++----- src/engine/platform/tx81z.cpp | 6 ++++++ src/engine/platform/ym2610.cpp | 6 ++++++ src/engine/platform/ym2610b.cpp | 6 ++++++ src/gui/insEdit.cpp | 1 - 16 files changed, 89 insertions(+), 7 deletions(-) diff --git a/src/engine/platform/amiga.cpp b/src/engine/platform/amiga.cpp index 3da16eef4..ff938e9b8 100644 --- a/src/engine/platform/amiga.cpp +++ b/src/engine/platform/amiga.cpp @@ -195,6 +195,11 @@ void DivPlatformAmiga::tick() { if (chan[i].useWave && chan[i].active) { chan[i].ws.tick(); } + if (chan[i].std.phaseReset.had) { + if (chan[i].std.phaseReset.val==1) { + chan[i].audPos=0; + } + } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { //DivInstrument* ins=parent->getIns(chan[i].ins); chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true); diff --git a/src/engine/platform/arcade.cpp b/src/engine/platform/arcade.cpp index 55172394c..061f40c67 100644 --- a/src/engine/platform/arcade.cpp +++ b/src/engine/platform/arcade.cpp @@ -264,6 +264,12 @@ void DivPlatformArcade::tick() { rWrite(0x1b,chan[i].std.wave.val&3); } + if (chan[i].std.phaseReset.had) { + if (chan[i].std.phaseReset.val==1) { + chan[i].keyOn=true; + } + } + if (chan[i].std.ex1.had) { amDepth=chan[i].std.ex1.val; immWrite(0x19,amDepth); diff --git a/src/engine/platform/ay.cpp b/src/engine/platform/ay.cpp index 727a14408..a33902efc 100644 --- a/src/engine/platform/ay.cpp +++ b/src/engine/platform/ay.cpp @@ -215,6 +215,12 @@ void DivPlatformAY8910::tick() { rWrite(0x08+i,(chan[i].outVol&15)|((chan[i].psgMode&4)<<2)); } } + if (chan[i].std.phaseReset.had) { + if (chan[i].std.phaseReset.val==1) { + oldWrites[0x08+i]=-1; + oldWrites[0x0d]=-1; + } + } if (chan[i].std.ex2.had) { ayEnvMode=chan[i].std.ex2.val; rWrite(0x0d,ayEnvMode); diff --git a/src/engine/platform/ay8930.cpp b/src/engine/platform/ay8930.cpp index e9af64fc7..c32957d71 100644 --- a/src/engine/platform/ay8930.cpp +++ b/src/engine/platform/ay8930.cpp @@ -226,6 +226,12 @@ void DivPlatformAY8930::tick() { rWrite(0x08+i,(chan[i].outVol&31)|((chan[i].psgMode&4)<<3)); } } + if (chan[i].std.phaseReset.had) { + if (chan[i].std.phaseReset.val==1) { + oldWrites[0x08+i]=-1; + oldWrites[regMode[i]]=-1; + } + } if (chan[i].std.ex1.had) { // duty rWrite(0x16+i,chan[i].std.ex1.val); } diff --git a/src/engine/platform/gb.cpp b/src/engine/platform/gb.cpp index 13eac1f32..56a8fa45e 100644 --- a/src/engine/platform/gb.cpp +++ b/src/engine/platform/gb.cpp @@ -192,6 +192,11 @@ void DivPlatformGB::tick() { if (!chan[i].keyOff) chan[i].keyOn=true; } } + if (chan[i].std.phaseReset.had) { + if (chan[i].std.phaseReset.val==1) { + chan[i].keyOn=true; + } + } if (i==2) { if (chan[i].active) { if (ws.tick()) { diff --git a/src/engine/platform/genesis.cpp b/src/engine/platform/genesis.cpp index f00a521bc..03288a37e 100644 --- a/src/engine/platform/genesis.cpp +++ b/src/engine/platform/genesis.cpp @@ -258,6 +258,12 @@ void DivPlatformGenesis::tick() { } } + if (chan[i].std.phaseReset.had) { + if (chan[i].std.phaseReset.val==1) { + chan[i].keyOn=true; + } + } + if (chan[i].std.alg.had) { chan[i].state.alg=chan[i].std.alg.val; rWrite(chanOffs[i]+ADDR_FB_ALG,(chan[i].state.alg&7)|(chan[i].state.fb<<3)); diff --git a/src/engine/platform/mmc5.cpp b/src/engine/platform/mmc5.cpp index 79d9fc61e..28706ce6e 100644 --- a/src/engine/platform/mmc5.cpp +++ b/src/engine/platform/mmc5.cpp @@ -123,6 +123,12 @@ void DivPlatformMMC5::tick() { chan[i].duty=chan[i].std.duty.val; rWrite(0x5000+i*4,0x30|chan[i].outVol|((chan[i].duty&3)<<6)); } + if (chan[i].std.phaseReset.had) { + if (chan[i].std.phaseReset.val==1) { + chan[i].freqChanged=true; + chan[i].prevFreq=-1; + } + } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true)-1; if (chan[i].freq>2047) chan[i].freq=2047; diff --git a/src/engine/platform/nes.cpp b/src/engine/platform/nes.cpp index e99a3e29e..89467a88f 100644 --- a/src/engine/platform/nes.cpp +++ b/src/engine/platform/nes.cpp @@ -201,6 +201,12 @@ void DivPlatformNES::tick() { //rWrite(16+i*5,chan[i].sweep); } } + if (i<2) if (chan[i].std.phaseReset.had) { + if (chan[i].std.phaseReset.val==1) { + chan[i].freqChanged=true; + chan[i].prevFreq=-1; + } + } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { if (i==3) { // noise int ntPos=chan[i].baseFreq; diff --git a/src/engine/platform/opl.cpp b/src/engine/platform/opl.cpp index 3e222eec1..9f77350c5 100644 --- a/src/engine/platform/opl.cpp +++ b/src/engine/platform/opl.cpp @@ -269,6 +269,12 @@ void DivPlatformOPL::tick() { } } + if (chan[i].std.phaseReset.had) { + if (chan[i].std.phaseReset.val==1) { + chan[i].keyOn=true; + } + } + if (chan[i].std.alg.had) { chan[i].state.alg=chan[i].std.alg.val; } diff --git a/src/engine/platform/opll.cpp b/src/engine/platform/opll.cpp index ee07463c1..617972764 100644 --- a/src/engine/platform/opll.cpp +++ b/src/engine/platform/opll.cpp @@ -145,6 +145,12 @@ void DivPlatformOPLL::tick() { } } + if (chan[i].std.phaseReset.had) { + if (chan[i].std.phaseReset.val==1) { + chan[i].keyOn=true; + } + } + if (chan[i].state.opllPreset==0) { if (chan[i].std.alg.had) { // SUS chan[i].state.alg=chan[i].std.alg.val; diff --git a/src/engine/platform/pce.cpp b/src/engine/platform/pce.cpp index 0789c804d..7a601ca59 100644 --- a/src/engine/platform/pce.cpp +++ b/src/engine/platform/pce.cpp @@ -197,7 +197,7 @@ void DivPlatformPCE::tick() { } } if (chan[i].active) { - if (chan[i].ws.tick()) { + if (chan[i].ws.tick() || (chan[i].std.phaseReset.had && chan[i].std.phaseReset.val==1)) { updateWave(i); } } diff --git a/src/engine/platform/sms.cpp b/src/engine/platform/sms.cpp index 2189eba29..528901f0f 100644 --- a/src/engine/platform/sms.cpp +++ b/src/engine/platform/sms.cpp @@ -84,12 +84,19 @@ void DivPlatformSMS::tick() { chan[i].freqChanged=true; } } - if (i==3) if (chan[i].std.duty.had) { - snNoiseMode=chan[i].std.duty.val; - if (chan[i].std.duty.val<2) { - chan[3].freqChanged=false; + if (i==3) { + if (chan[i].std.duty.had) { + snNoiseMode=chan[i].std.duty.val; + if (chan[i].std.duty.val<2) { + chan[3].freqChanged=false; + } + updateSNMode=true; + } + if (chan[i].std.phaseReset.had) { + if (chan[i].std.phaseReset.val==1) { + updateSNMode=true; + } } - updateSNMode=true; } } for (int i=0; i<3; i++) { diff --git a/src/engine/platform/tx81z.cpp b/src/engine/platform/tx81z.cpp index 7ea634b26..8cbfa039b 100644 --- a/src/engine/platform/tx81z.cpp +++ b/src/engine/platform/tx81z.cpp @@ -228,6 +228,12 @@ void DivPlatformTX81Z::tick() { rWrite(0x1b,chan[i].std.wave.val&3); } + if (chan[i].std.phaseReset.had) { + if (chan[i].std.phaseReset.val==1) { + chan[i].keyOn=true; + } + } + if (chan[i].std.ex1.had) { amDepth=chan[i].std.ex1.val; immWrite(0x19,amDepth); diff --git a/src/engine/platform/ym2610.cpp b/src/engine/platform/ym2610.cpp index dd7220fa5..3098c935b 100644 --- a/src/engine/platform/ym2610.cpp +++ b/src/engine/platform/ym2610.cpp @@ -408,6 +408,12 @@ void DivPlatformYM2610::tick() { } } + if (chan[i].std.phaseReset.had) { + if (chan[i].std.phaseReset.val==1) { + chan[i].keyOn=true; + } + } + if (chan[i].std.alg.had) { chan[i].state.alg=chan[i].std.alg.val; rWrite(chanOffs[i]+ADDR_FB_ALG,(chan[i].state.alg&7)|(chan[i].state.fb<<3)); diff --git a/src/engine/platform/ym2610b.cpp b/src/engine/platform/ym2610b.cpp index c51f41198..b28852e43 100644 --- a/src/engine/platform/ym2610b.cpp +++ b/src/engine/platform/ym2610b.cpp @@ -472,6 +472,12 @@ void DivPlatformYM2610B::tick() { } } + if (chan[i].std.phaseReset.had) { + if (chan[i].std.phaseReset.val==1) { + chan[i].keyOn=true; + } + } + if (chan[i].std.alg.had) { chan[i].state.alg=chan[i].std.alg.val; rWrite(chanOffs[i]+ADDR_FB_ALG,(chan[i].state.alg&7)|(chan[i].state.fb<<3)); diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index f91e4c405..50f93de2e 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -2758,7 +2758,6 @@ void FurnaceGUI::drawInsEdit() { ins->type==DIV_INS_GB || ins->type==DIV_INS_AMIGA || ins->type==DIV_INS_OPLL || - ins->type==DIV_INS_FDS || ins->type==DIV_INS_AY || ins->type==DIV_INS_AY8930 || ins->type==DIV_INS_SWAN) { From d47a59f5f01c9451aa22b8709b1083c78010f292 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 15 Apr 2022 03:37:16 -0500 Subject: [PATCH 079/342] implement pitch macro TODO: test! --- TODO.md | 2 -- src/engine/macroInt.h | 2 ++ src/engine/platform/amiga.cpp | 6 +++++- src/engine/platform/arcade.cpp | 2 +- src/engine/platform/ay.cpp | 2 +- src/engine/platform/ay8930.cpp | 2 +- src/engine/platform/bubsyswsg.cpp | 2 +- src/engine/platform/c64.cpp | 2 +- src/engine/platform/fds.cpp | 4 ++-- src/engine/platform/gb.cpp | 3 ++- src/engine/platform/genesis.cpp | 2 +- src/engine/platform/lynx.cpp | 2 +- src/engine/platform/mmc5.cpp | 2 +- src/engine/platform/n163.cpp | 2 +- src/engine/platform/nes.cpp | 2 +- src/engine/platform/opl.cpp | 2 +- src/engine/platform/opll.cpp | 2 +- src/engine/platform/pce.cpp | 2 +- src/engine/platform/pcspkr.cpp | 2 +- src/engine/platform/pet.cpp | 2 +- src/engine/platform/qsound.cpp | 2 +- src/engine/platform/saa.cpp | 2 +- src/engine/platform/segapcm.cpp | 2 +- src/engine/platform/sms.cpp | 4 ++-- src/engine/platform/swan.cpp | 2 +- src/engine/platform/tia.cpp | 6 +++--- src/engine/platform/tx81z.cpp | 2 +- src/engine/platform/vera.cpp | 4 ++-- src/engine/platform/vic20.cpp | 2 +- src/engine/platform/vrc6.cpp | 4 ++-- src/engine/platform/x1_010.cpp | 2 +- src/engine/platform/ym2610.cpp | 2 +- src/engine/platform/ym2610b.cpp | 2 +- 33 files changed, 44 insertions(+), 39 deletions(-) diff --git a/TODO.md b/TODO.md index 22efa331b..d9817937e 100644 --- a/TODO.md +++ b/TODO.md @@ -2,7 +2,6 @@ - panning macro - pitch macro -- phase reset macro - piano/input pad - note input via piano - input pad @@ -41,7 +40,6 @@ - add ability to select a column by double clicking - add ability to move selection by dragging - NSFPlay core for NES -- option for standard waveform names in OPL - settings: OK/Cancel buttons should be always visible - Apply button in settings - better FM chip names (number and codename) diff --git a/src/engine/macroInt.h b/src/engine/macroInt.h index 3c26eb659..5dc30aa06 100644 --- a/src/engine/macroInt.h +++ b/src/engine/macroInt.h @@ -31,6 +31,8 @@ struct DivMacroStruct { void init() { pos=mode=0; has=had=will=false; + // TODO: test whether this breaks anything? + val=0; } void prepare(DivInstrumentMacro& source) { has=had=will=true; diff --git a/src/engine/platform/amiga.cpp b/src/engine/platform/amiga.cpp index ff938e9b8..d0b1f4417 100644 --- a/src/engine/platform/amiga.cpp +++ b/src/engine/platform/amiga.cpp @@ -195,6 +195,9 @@ void DivPlatformAmiga::tick() { if (chan[i].useWave && chan[i].active) { chan[i].ws.tick(); } + if (chan[i].std.pitch.had) { + chan[i].freqChanged=true; + } if (chan[i].std.phaseReset.had) { if (chan[i].std.phaseReset.val==1) { chan[i].audPos=0; @@ -202,8 +205,9 @@ void DivPlatformAmiga::tick() { } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { //DivInstrument* ins=parent->getIns(chan[i].ins); - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true); + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true)+chan[i].std.pitch.val; if (chan[i].freq>4095) chan[i].freq=4095; + if (chan[i].freq<0) chan[i].freq=0; if (chan[i].keyOn) { } if (chan[i].keyOff) { diff --git a/src/engine/platform/arcade.cpp b/src/engine/platform/arcade.cpp index 061f40c67..3885619b1 100644 --- a/src/engine/platform/arcade.cpp +++ b/src/engine/platform/arcade.cpp @@ -406,7 +406,7 @@ void DivPlatformArcade::tick() { for (int i=0; i<8; i++) { if (chan[i].freqChanged) { - chan[i].freq=chan[i].baseFreq+(chan[i].pitch>>1)-64; + chan[i].freq=chan[i].baseFreq+(chan[i].pitch>>1)-64+chan[i].std.pitch.val; if (chan[i].freq<0) chan[i].freq=0; if (chan[i].freq>=(95<<6)) chan[i].freq=(95<<6)-1; immWrite(i+0x28,hScale(chan[i].freq>>6)); diff --git a/src/engine/platform/ay.cpp b/src/engine/platform/ay.cpp index a33902efc..f096b1e6c 100644 --- a/src/engine/platform/ay.cpp +++ b/src/engine/platform/ay.cpp @@ -236,7 +236,7 @@ void DivPlatformAY8910::tick() { if (!chan[i].std.ex3.will) chan[i].autoEnvNum=1; } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true); + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true)+chan[i].std.pitch.val; if (chan[i].freq>4095) chan[i].freq=4095; if (chan[i].keyOn) { //rWrite(16+i*5+1,((chan[i].duty&3)<<6)|(63-(ins->gb.soundLen&63))); diff --git a/src/engine/platform/ay8930.cpp b/src/engine/platform/ay8930.cpp index c32957d71..8237876b9 100644 --- a/src/engine/platform/ay8930.cpp +++ b/src/engine/platform/ay8930.cpp @@ -258,7 +258,7 @@ void DivPlatformAY8930::tick() { immWrite(0x1a,ayNoiseOr); } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true); + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true)+chan[i].std.pitch.val; if (chan[i].freq>65535) chan[i].freq=65535; if (chan[i].keyOn) { if (chan[i].insChanged) { diff --git a/src/engine/platform/bubsyswsg.cpp b/src/engine/platform/bubsyswsg.cpp index 712496c1c..2c2bebbd3 100644 --- a/src/engine/platform/bubsyswsg.cpp +++ b/src/engine/platform/bubsyswsg.cpp @@ -117,7 +117,7 @@ void DivPlatformBubSysWSG::tick() { } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { //DivInstrument* ins=parent->getIns(chan[i].ins); - chan[i].freq=0x1000-parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true); + chan[i].freq=0x1000-parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true)+chan[i].std.pitch.val; if (chan[i].freq<0) chan[i].freq=0; if (chan[i].freq>4095) chan[i].freq=4095; k005289->load(i,chan[i].freq); diff --git a/src/engine/platform/c64.cpp b/src/engine/platform/c64.cpp index f8b014c8a..d230c1402 100644 --- a/src/engine/platform/c64.cpp +++ b/src/engine/platform/c64.cpp @@ -194,7 +194,7 @@ void DivPlatformC64::tick() { } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,8); + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,8)+chan[i].std.pitch.val; if (chan[i].freq>0xffff) chan[i].freq=0xffff; if (chan[i].keyOn) { rWrite(i*7+5,(chan[i].attack<<4)|(chan[i].decay)); diff --git a/src/engine/platform/fds.cpp b/src/engine/platform/fds.cpp index 9960a0d74..e039e38a6 100644 --- a/src/engine/platform/fds.cpp +++ b/src/engine/platform/fds.cpp @@ -184,7 +184,7 @@ void DivPlatformFDS::tick() { } } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false); + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false)+chan[i].std.pitch.val; if (chan[i].freq>4095) chan[i].freq=4095; if (chan[i].freq<0) chan[i].freq=0; if (chan[i].keyOn) { @@ -439,7 +439,7 @@ void DivPlatformFDS::setFlags(unsigned int flags) { } void DivPlatformFDS::notifyInsDeletion(void* ins) { - for (int i=0; i<5; i++) { + for (int i=0; i<1; i++) { chan[i].std.notifyInsDeletion((DivInstrument*)ins); } } diff --git a/src/engine/platform/gb.cpp b/src/engine/platform/gb.cpp index 56a8fa45e..a84173189 100644 --- a/src/engine/platform/gb.cpp +++ b/src/engine/platform/gb.cpp @@ -219,8 +219,9 @@ void DivPlatformGB::tick() { if (ntPos>255) ntPos=255; chan[i].freq=noiseTable[ntPos]; } else { - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true); + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true)+chan[i].std.pitch.val; if (chan[i].freq>2047) chan[i].freq=2047; + if (chan[i].freq<0) chan[i].freq=0; } if (chan[i].keyOn) { if (i==2) { // wave diff --git a/src/engine/platform/genesis.cpp b/src/engine/platform/genesis.cpp index 03288a37e..aacc31243 100644 --- a/src/engine/platform/genesis.cpp +++ b/src/engine/platform/genesis.cpp @@ -387,7 +387,7 @@ void DivPlatformGenesis::tick() { if (chan[i].freqChanged) { chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,octave(chan[i].baseFreq)); if (chan[i].freq>262143) chan[i].freq=262143; - int freqt=toFreq(chan[i].freq); + int freqt=toFreq(chan[i].freq)+chan[i].std.pitch.val; immWrite(chanOffs[i]+ADDR_FREQH,freqt>>8); immWrite(chanOffs[i]+ADDR_FREQ,freqt&0xff); if (chan[i].furnaceDac && dacMode) { diff --git a/src/engine/platform/lynx.cpp b/src/engine/platform/lynx.cpp index 70e7bc413..9ef121594 100644 --- a/src/engine/platform/lynx.cpp +++ b/src/engine/platform/lynx.cpp @@ -177,7 +177,7 @@ void DivPlatformLynx::tick() { WRITE_OTHER(i, ((chan[i].lfsr&0xf00)>>4)); chan[i].lfsr=-1; } - chan[i].fd=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true); + chan[i].fd=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true)+chan[i].std.pitch.val; if (chan[i].std.duty.had) { chan[i].duty=chan[i].std.duty.val; WRITE_FEEDBACK(i, chan[i].duty.feedback); diff --git a/src/engine/platform/mmc5.cpp b/src/engine/platform/mmc5.cpp index 28706ce6e..e23e242bd 100644 --- a/src/engine/platform/mmc5.cpp +++ b/src/engine/platform/mmc5.cpp @@ -130,7 +130,7 @@ void DivPlatformMMC5::tick() { } } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true)-1; + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true)-1+chan[i].std.pitch.val; if (chan[i].freq>2047) chan[i].freq=2047; if (chan[i].freq<0) chan[i].freq=0; if (chan[i].keyOn) { diff --git a/src/engine/platform/n163.cpp b/src/engine/platform/n163.cpp index b7234a4ba..5de55ef0b 100644 --- a/src/engine/platform/n163.cpp +++ b/src/engine/platform/n163.cpp @@ -344,7 +344,7 @@ void DivPlatformN163::tick() { chan[i].waveUpdated=false; } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { - chan[i].freq=parent->calcFreq((((chan[i].baseFreq*chan[i].waveLen)*(chanMax+1))/16),chan[i].pitch,false,0); + chan[i].freq=parent->calcFreq((((chan[i].baseFreq*chan[i].waveLen)*(chanMax+1))/16),chan[i].pitch,false,0)+chan[i].std.pitch.val; if (chan[i].freq<0) chan[i].freq=0; if (chan[i].freq>0x3ffff) chan[i].freq=0x3ffff; if (chan[i].keyOn) { diff --git a/src/engine/platform/nes.cpp b/src/engine/platform/nes.cpp index 89467a88f..532f9f23c 100644 --- a/src/engine/platform/nes.cpp +++ b/src/engine/platform/nes.cpp @@ -214,7 +214,7 @@ void DivPlatformNES::tick() { if (ntPos>252) ntPos=252; chan[i].freq=(parent->song.properNoiseLayout)?(15-(chan[i].baseFreq&15)):(noiseTable[ntPos]); } else { - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true)-1; + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true)-1+chan[i].std.pitch.val; if (chan[i].freq>2047) chan[i].freq=2047; if (chan[i].freq<0) chan[i].freq=0; } diff --git a/src/engine/platform/opl.cpp b/src/engine/platform/opl.cpp index 9f77350c5..006afc472 100644 --- a/src/engine/platform/opl.cpp +++ b/src/engine/platform/opl.cpp @@ -411,7 +411,7 @@ void DivPlatformOPL::tick() { if (chan[i].freqChanged) { chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,octave(chan[i].baseFreq)); if (chan[i].freq>131071) chan[i].freq=131071; - int freqt=toFreq(chan[i].freq); + int freqt=toFreq(chan[i].freq)+chan[i].std.pitch.val; chan[i].freqH=freqt>>8; chan[i].freqL=freqt&0xff; immWrite(chanMap[i]+ADDR_FREQ,chan[i].freqL); diff --git a/src/engine/platform/opll.cpp b/src/engine/platform/opll.cpp index 617972764..479fce455 100644 --- a/src/engine/platform/opll.cpp +++ b/src/engine/platform/opll.cpp @@ -259,7 +259,7 @@ void DivPlatformOPLL::tick() { if (chan[i].freqChanged) { chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,octave(chan[i].baseFreq)); if (chan[i].freq>262143) chan[i].freq=262143; - int freqt=toFreq(chan[i].freq); + int freqt=toFreq(chan[i].freq)+chan[i].std.pitch.val; chan[i].freqL=freqt&0xff; if (i>=6 && properDrums) { immWrite(0x10+drumSlot[i],freqt&0xff); diff --git a/src/engine/platform/pce.cpp b/src/engine/platform/pce.cpp index 7a601ca59..f41e32008 100644 --- a/src/engine/platform/pce.cpp +++ b/src/engine/platform/pce.cpp @@ -203,7 +203,7 @@ void DivPlatformPCE::tick() { } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { //DivInstrument* ins=parent->getIns(chan[i].ins); - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true); + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true)+chan[i].std.pitch.val; if (chan[i].furnaceDac) { double off=1.0; if (chan[i].dacSample>=0 && chan[i].dacSamplesong.sampleLen) { diff --git a/src/engine/platform/pcspkr.cpp b/src/engine/platform/pcspkr.cpp index ee6ba7ef8..c20687300 100644 --- a/src/engine/platform/pcspkr.cpp +++ b/src/engine/platform/pcspkr.cpp @@ -187,7 +187,7 @@ void DivPlatformPCSpeaker::tick() { } } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true)-1; + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true)-1+chan[i].std.pitch.val; if (chan[i].freq<0) chan[i].freq=0; if (chan[i].freq>65535) chan[i].freq=65535; if (chan[i].keyOn) { diff --git a/src/engine/platform/pet.cpp b/src/engine/platform/pet.cpp index e46277e0a..22eee4578 100644 --- a/src/engine/platform/pet.cpp +++ b/src/engine/platform/pet.cpp @@ -113,7 +113,7 @@ void DivPlatformPET::tick() { } } if (chan.freqChanged || chan.keyOn || chan.keyOff) { - chan.freq=parent->calcFreq(chan.baseFreq,chan.pitch,true); + chan.freq=parent->calcFreq(chan.baseFreq,chan.pitch,true)+chan.std.pitch.val; if (chan.freq>257) chan.freq=257; if (chan.freq<2) chan.freq=2; rWrite(8,chan.freq-2); diff --git a/src/engine/platform/qsound.cpp b/src/engine/platform/qsound.cpp index 0fd25ec72..ef443e5e6 100644 --- a/src/engine/platform/qsound.cpp +++ b/src/engine/platform/qsound.cpp @@ -328,7 +328,7 @@ void DivPlatformQSound::tick() { } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { //DivInstrument* ins=parent->getIns(chan[i].ins); - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false); + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false)+chan[i].std.pitch.val; if (chan[i].freq>0xffff) chan[i].freq=0xffff; if (chan[i].keyOn) { rWrite(q1_reg_map[Q1V_BANK][i], qsound_bank); diff --git a/src/engine/platform/saa.cpp b/src/engine/platform/saa.cpp index 708fbc7ab..4a9e1d580 100644 --- a/src/engine/platform/saa.cpp +++ b/src/engine/platform/saa.cpp @@ -171,7 +171,7 @@ void DivPlatformSAA1099::tick() { rWrite(0x18+(i/3),saaEnv[i/3]); } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true); + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true)+chan[i].std.pitch.val; if (chan[i].freq>65535) chan[i].freq=65535; if (chan[i].freq>=32768) { chan[i].freqH=7; diff --git a/src/engine/platform/segapcm.cpp b/src/engine/platform/segapcm.cpp index 84ec00c37..2abdcd463 100644 --- a/src/engine/platform/segapcm.cpp +++ b/src/engine/platform/segapcm.cpp @@ -113,7 +113,7 @@ void DivPlatformSegaPCM::tick() { DivSample* s=parent->getSample(chan[i].pcm.sample); off=(double)s->centerRate/8363.0; } - chan[i].pcm.freq=MIN(255,(15625+(off*parent->song.tuning*pow(2.0,double(chan[i].freq+256)/(64.0*12.0)))*255)/31250); + chan[i].pcm.freq=MIN(255,(15625+(off*parent->song.tuning*pow(2.0,double(chan[i].freq+256)/(64.0*12.0)))*255)/31250)+chan[i].std.pitch.val; if (dumpWrites) { addWrite(0x10007+(i<<3),chan[i].pcm.freq); } diff --git a/src/engine/platform/sms.cpp b/src/engine/platform/sms.cpp index 528901f0f..60361dd83 100644 --- a/src/engine/platform/sms.cpp +++ b/src/engine/platform/sms.cpp @@ -101,7 +101,7 @@ void DivPlatformSMS::tick() { } for (int i=0; i<3; i++) { if (chan[i].freqChanged) { - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true); + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true)+chan[i].std.pitch.val; if (chan[i].freq>1023) chan[i].freq=1023; if (chan[i].freq<8) chan[i].freq=1; //if (chan[i].actualNote>0x5d) chan[i].freq=0x01; @@ -117,7 +117,7 @@ void DivPlatformSMS::tick() { } if (chan[3].freqChanged || updateSNMode) { // seems arbitrary huh? - chan[3].freq=parent->calcFreq(chan[3].baseFreq,chan[3].pitch-1-(isRealSN?127:0),true); + chan[3].freq=parent->calcFreq(chan[3].baseFreq,chan[3].pitch-1-(isRealSN?127:0),true)+chan[3].std.pitch.val; if (chan[3].freq>1023) chan[3].freq=1023; if (chan[3].actualNote>0x5d) chan[3].freq=0x01; if (snNoiseMode&2) { // take period from channel 3 diff --git a/src/engine/platform/swan.cpp b/src/engine/platform/swan.cpp index 802f27b86..563606a94 100644 --- a/src/engine/platform/swan.cpp +++ b/src/engine/platform/swan.cpp @@ -180,7 +180,7 @@ void DivPlatformSwan::tick() { } } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true); + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true)+chan[i].std.pitch.val; if (i==1 && pcm && furnaceDac) { double off=1.0; if (dacSample>=0 && dacSamplesong.sampleLen) { diff --git a/src/engine/platform/tia.cpp b/src/engine/platform/tia.cpp index e06c5c74f..1e6f84879 100644 --- a/src/engine/platform/tia.cpp +++ b/src/engine/platform/tia.cpp @@ -124,14 +124,14 @@ void DivPlatformTIA::tick() { } chan[i].insChanged=false; } - chan[i].freq=dealWithFreq(chan[i].shape,chan[i].baseFreq,chan[i].pitch); + chan[i].freq=dealWithFreq(chan[i].shape,chan[i].baseFreq,chan[i].pitch)+chan[i].std.pitch.val; if ((chan[i].shape==4 || chan[i].shape==5) && !(chan[i].baseFreq&0x80000000 && ((chan[i].baseFreq&0x7fffffff)<32))) { if (chan[i].baseFreq<39*256) { rWrite(0x15+i,6); - chan[i].freq=dealWithFreq(6,chan[i].baseFreq,chan[i].pitch); + chan[i].freq=dealWithFreq(6,chan[i].baseFreq,chan[i].pitch)+chan[i].std.pitch.val; } else if (chan[i].baseFreq<59*256) { rWrite(0x15+i,12); - chan[i].freq=dealWithFreq(12,chan[i].baseFreq,chan[i].pitch); + chan[i].freq=dealWithFreq(12,chan[i].baseFreq,chan[i].pitch)+chan[i].std.pitch.val; } else { rWrite(0x15+i,chan[i].shape); } diff --git a/src/engine/platform/tx81z.cpp b/src/engine/platform/tx81z.cpp index 8cbfa039b..d50830e71 100644 --- a/src/engine/platform/tx81z.cpp +++ b/src/engine/platform/tx81z.cpp @@ -387,7 +387,7 @@ void DivPlatformTX81Z::tick() { for (int i=0; i<8; i++) { if (chan[i].freqChanged) { - chan[i].freq=chan[i].baseFreq+(chan[i].pitch>>1)-64; + chan[i].freq=chan[i].baseFreq+(chan[i].pitch>>1)-64+chan[i].std.pitch.val; if (chan[i].freq<0) chan[i].freq=0; if (chan[i].freq>=(95<<6)) chan[i].freq=(95<<6)-1; immWrite(i+0x28,hScale(chan[i].freq>>6)); diff --git a/src/engine/platform/vera.cpp b/src/engine/platform/vera.cpp index 593c1a0c7..0c0b3f71e 100644 --- a/src/engine/platform/vera.cpp +++ b/src/engine/platform/vera.cpp @@ -184,7 +184,7 @@ void DivPlatformVERA::tick() { rWriteHi(i,3,chan[i].std.wave.val); } if (chan[i].freqChanged) { - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,8); + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,8)+chan[i].std.pitch.val; if (chan[i].freq>65535) chan[i].freq=65535; rWrite(i,0,chan[i].freq&0xff); rWrite(i,1,(chan[i].freq>>8)&0xff); @@ -213,7 +213,7 @@ void DivPlatformVERA::tick() { } } if (chan[16].freqChanged) { - chan[16].freq=parent->calcFreq(chan[16].baseFreq,chan[16].pitch,false,8); + chan[16].freq=parent->calcFreq(chan[16].baseFreq,chan[16].pitch,false,8)+chan[16].std.pitch.val; if (chan[16].freq>128) chan[16].freq=128; rWritePCMRate(chan[16].freq&0xff); chan[16].freqChanged=false; diff --git a/src/engine/platform/vic20.cpp b/src/engine/platform/vic20.cpp index b45fecfc3..29ce7106b 100644 --- a/src/engine/platform/vic20.cpp +++ b/src/engine/platform/vic20.cpp @@ -120,7 +120,7 @@ void DivPlatformVIC20::tick() { } } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true); + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true)+chan[i].std.pitch.val; if (i<3) { chan[i].freq>>=(2-i); } else { diff --git a/src/engine/platform/vrc6.cpp b/src/engine/platform/vrc6.cpp index 31694a62c..0342bd67c 100644 --- a/src/engine/platform/vrc6.cpp +++ b/src/engine/platform/vrc6.cpp @@ -180,9 +180,9 @@ void DivPlatformVRC6::tick() { } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { if (i==2) { // sawtooth - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true)-1; + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true)-1+chan[i].std.pitch.val; } else { // pulse - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true)-1; + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true)-1+chan[i].std.pitch.val; if (chan[i].furnaceDac) { double off=1.0; if (chan[i].dacSample>=0 && chan[i].dacSamplesong.sampleLen) { diff --git a/src/engine/platform/x1_010.cpp b/src/engine/platform/x1_010.cpp index 124e0ace6..02632b874 100644 --- a/src/engine/platform/x1_010.cpp +++ b/src/engine/platform/x1_010.cpp @@ -463,7 +463,7 @@ void DivPlatformX1_010::tick() { chan[i].envChanged=false; } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false); + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false)+chan[i].std.pitch.val; if (chan[i].pcm) { if (chan[i].freq<1) chan[i].freq=1; if (chan[i].freq>255) chan[i].freq=255; diff --git a/src/engine/platform/ym2610.cpp b/src/engine/platform/ym2610.cpp index 3098c935b..c52b34281 100644 --- a/src/engine/platform/ym2610.cpp +++ b/src/engine/platform/ym2610.cpp @@ -565,7 +565,7 @@ void DivPlatformYM2610::tick() { if (chan[i].freqChanged) { chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,octave(chan[i].baseFreq)); if (chan[i].freq>262143) chan[i].freq=262143; - int freqt=toFreq(chan[i].freq); + int freqt=toFreq(chan[i].freq)+chan[i].std.pitch.val; immWrite(chanOffs[i]+ADDR_FREQH,freqt>>8); immWrite(chanOffs[i]+ADDR_FREQ,freqt&0xff); chan[i].freqChanged=false; diff --git a/src/engine/platform/ym2610b.cpp b/src/engine/platform/ym2610b.cpp index b28852e43..ade5a9d80 100644 --- a/src/engine/platform/ym2610b.cpp +++ b/src/engine/platform/ym2610b.cpp @@ -628,7 +628,7 @@ void DivPlatformYM2610B::tick() { if (chan[i].freqChanged) { chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,octave(chan[i].baseFreq)); if (chan[i].freq>262143) chan[i].freq=262143; - int freqt=toFreq(chan[i].freq); + int freqt=toFreq(chan[i].freq)+chan[i].std.pitch.val; immWrite(chanOffs[i]+ADDR_FREQH,freqt>>8); immWrite(chanOffs[i]+ADDR_FREQ,freqt&0xff); chan[i].freqChanged=false; From 96715ed88c3722db9a653d4617d9716982a1deff Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 15 Apr 2022 05:37:23 -0500 Subject: [PATCH 080/342] add experimental low-latency mode do not use (yet)! --- src/engine/engine.cpp | 1 + src/engine/engine.h | 7 +- src/engine/macroInt.cpp | 18 ++- src/engine/macroInt.h | 12 ++ src/engine/platform/amiga.cpp | 1 + src/engine/platform/arcade.cpp | 1 + src/engine/platform/ay.cpp | 1 + src/engine/platform/ay8930.cpp | 1 + src/engine/platform/bubsyswsg.cpp | 1 + src/engine/platform/c64.cpp | 1 + src/engine/platform/fds.cpp | 1 + src/engine/platform/gb.cpp | 1 + src/engine/platform/genesis.cpp | 1 + src/engine/platform/lynx.cpp | 6 +- src/engine/platform/mmc5.cpp | 1 + src/engine/platform/n163.cpp | 1 + src/engine/platform/nes.cpp | 1 + src/engine/platform/opl.cpp | 1 + src/engine/platform/opll.cpp | 1 + src/engine/platform/pce.cpp | 1 + src/engine/platform/pcspkr.cpp | 1 + src/engine/platform/pet.cpp | 1 + src/engine/platform/qsound.cpp | 1 + src/engine/platform/saa.cpp | 1 + src/engine/platform/segapcm.cpp | 1 + src/engine/platform/sms.cpp | 1 + src/engine/platform/swan.cpp | 1 + src/engine/platform/tia.cpp | 1 + src/engine/platform/tx81z.cpp | 1 + src/engine/platform/vera.cpp | 1 + src/engine/platform/vic20.cpp | 1 + src/engine/platform/vrc6.cpp | 1 + src/engine/platform/x1_010.cpp | 1 + src/engine/platform/ym2610.cpp | 1 + src/engine/platform/ym2610b.cpp | 1 + src/engine/playback.cpp | 234 +++++++++++++++--------------- src/gui/gui.h | 2 + src/gui/settings.cpp | 11 ++ 38 files changed, 201 insertions(+), 120 deletions(-) diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index ee4b4b20e..81d7947a7 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -2232,6 +2232,7 @@ bool DivEngine::initAudioBackend() { lowQuality=getConfInt("audioQuality",0); forceMono=getConfInt("forceMono",0); + lowLatency=getConfInt("lowLatency",0); metroVol=(float)(getConfInt("metroVol",100))/100.0f; if (metroVol<0.0f) metroVol=0.0f; if (metroVol>2.0f) metroVol=2.0f; diff --git a/src/engine/engine.h b/src/engine/engine.h index 0613d3edd..6bcaa38c9 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -202,8 +202,9 @@ class DivEngine { bool firstTick; bool skipping; bool midiIsDirect; + bool lowLatency; int softLockCount; - int ticks, curRow, curOrder, remainingLoops, nextSpeed; + int subticks, ticks, curRow, curOrder, remainingLoops, nextSpeed; double divider; int cycles; double clockDrift; @@ -303,6 +304,7 @@ class DivEngine { float* oscBuf[2]; float oscSize; int oscReadPos, oscWritePos; + int tickMult; void runExportThread(); void nextBuf(float** in, float** out, int inChans, int outChans, unsigned int size); @@ -745,7 +747,9 @@ class DivEngine { firstTick(false), skipping(false), midiIsDirect(false), + lowLatency(false), softLockCount(0), + subticks(0), ticks(0), curRow(0), curOrder(0), @@ -789,6 +793,7 @@ class DivEngine { oscSize(1), oscReadPos(0), oscWritePos(0), + tickMult(1), adpcmAMem(NULL), adpcmAMemLen(0), adpcmBMem(NULL), diff --git a/src/engine/macroInt.cpp b/src/engine/macroInt.cpp index 21ca93a1a..ae6caf5df 100644 --- a/src/engine/macroInt.cpp +++ b/src/engine/macroInt.cpp @@ -19,6 +19,7 @@ #include "macroInt.h" #include "instrument.h" +#include "engine.h" void DivMacroStruct::doMacro(DivInstrumentMacro& source, bool released) { if (finished) { @@ -51,9 +52,16 @@ void DivMacroInt::next() { if (ins==NULL) return; // run macros // TODO: potentially get rid of list to avoid allocations - for (size_t i=0; idoMacro(*macroSource[i],released); + if (--subTick<=0) { + if (e==NULL) { + subTick=1; + } else { + subTick=e->tickMult; + } + for (size_t i=0; idoMacro(*macroSource[i],released); + } } } } @@ -62,6 +70,10 @@ void DivMacroInt::release() { released=true; } +void DivMacroInt::setEngine(DivEngine* eng) { + e=eng; +} + #define ADD_MACRO(m,s) \ macroList[macroListLen]=&m; \ macroSource[macroListLen++]=&s; diff --git a/src/engine/macroInt.h b/src/engine/macroInt.h index 5dc30aa06..886ecbe23 100644 --- a/src/engine/macroInt.h +++ b/src/engine/macroInt.h @@ -22,6 +22,8 @@ #include "instrument.h" +class DivEngine; + struct DivMacroStruct { int pos; int val; @@ -49,10 +51,12 @@ struct DivMacroStruct { }; class DivMacroInt { + DivEngine* e; DivInstrument* ins; DivMacroStruct* macroList[128]; DivInstrumentMacro* macroSource[128]; size_t macroListLen; + int subTick; bool released; public: // common macro @@ -102,6 +106,12 @@ class DivMacroInt { */ void next(); + /** + * set the engine. + * @param the engine + */ + void setEngine(DivEngine* eng); + /** * initialize the macro interpreter. * @param which an instrument, or NULL. @@ -115,8 +125,10 @@ class DivMacroInt { void notifyInsDeletion(DivInstrument* which); DivMacroInt(): + e(NULL), ins(NULL), macroListLen(0), + subTick(0), released(false), vol(), arp(), diff --git a/src/engine/platform/amiga.cpp b/src/engine/platform/amiga.cpp index d0b1f4417..b8419d197 100644 --- a/src/engine/platform/amiga.cpp +++ b/src/engine/platform/amiga.cpp @@ -414,6 +414,7 @@ void* DivPlatformAmiga::getChanState(int ch) { void DivPlatformAmiga::reset() { for (int i=0; i<4; i++) { chan[i]=DivPlatformAmiga::Channel(); + chan[i].std.setEngine(parent); chan[i].ws.setEngine(parent); chan[i].ws.init(NULL,32,255); filter[0][i]=0; diff --git a/src/engine/platform/arcade.cpp b/src/engine/platform/arcade.cpp index 3885619b1..a7ab53ec0 100644 --- a/src/engine/platform/arcade.cpp +++ b/src/engine/platform/arcade.cpp @@ -735,6 +735,7 @@ void DivPlatformArcade::reset() { } for (int i=0; i<8; i++) { chan[i]=DivPlatformArcade::Channel(); + chan[i].std.setEngine(parent); chan[i].vol=0x7f; chan[i].outVol=0x7f; } diff --git a/src/engine/platform/ay.cpp b/src/engine/platform/ay.cpp index f096b1e6c..e8a61b9ae 100644 --- a/src/engine/platform/ay.cpp +++ b/src/engine/platform/ay.cpp @@ -513,6 +513,7 @@ void DivPlatformAY8910::reset() { memset(regPool,0,16); for (int i=0; i<3; i++) { chan[i]=DivPlatformAY8910::Channel(); + chan[i].std.setEngine(parent); chan[i].vol=0x0f; } if (dumpWrites) { diff --git a/src/engine/platform/ay8930.cpp b/src/engine/platform/ay8930.cpp index 8237876b9..9f684a048 100644 --- a/src/engine/platform/ay8930.cpp +++ b/src/engine/platform/ay8930.cpp @@ -528,6 +528,7 @@ void DivPlatformAY8930::reset() { memset(regPool,0,32); for (int i=0; i<3; i++) { chan[i]=DivPlatformAY8930::Channel(); + chan[i].std.setEngine(parent); chan[i].vol=31; ayEnvPeriod[i]=0; ayEnvMode[i]=0; diff --git a/src/engine/platform/bubsyswsg.cpp b/src/engine/platform/bubsyswsg.cpp index 2c2bebbd3..4a80bf414 100644 --- a/src/engine/platform/bubsyswsg.cpp +++ b/src/engine/platform/bubsyswsg.cpp @@ -274,6 +274,7 @@ void DivPlatformBubSysWSG::reset() { memset(regPool,0,4*2); for (int i=0; i<2; i++) { chan[i]=DivPlatformBubSysWSG::Channel(); + chan[i].std.setEngine(parent); chan[i].ws.setEngine(parent); chan[i].ws.init(NULL,32,15,false); } diff --git a/src/engine/platform/c64.cpp b/src/engine/platform/c64.cpp index d230c1402..db5ab46a6 100644 --- a/src/engine/platform/c64.cpp +++ b/src/engine/platform/c64.cpp @@ -482,6 +482,7 @@ bool DivPlatformC64::getDCOffRequired() { void DivPlatformC64::reset() { for (int i=0; i<3; i++) { chan[i]=DivPlatformC64::Channel(); + chan[i].std.setEngine(parent); } sid.reset(); diff --git a/src/engine/platform/fds.cpp b/src/engine/platform/fds.cpp index e039e38a6..aefa37541 100644 --- a/src/engine/platform/fds.cpp +++ b/src/engine/platform/fds.cpp @@ -408,6 +408,7 @@ int DivPlatformFDS::getRegisterPoolSize() { void DivPlatformFDS::reset() { for (int i=0; i<1; i++) { chan[i]=DivPlatformFDS::Channel(); + chan[i].std.setEngine(parent); } ws.setEngine(parent); ws.init(NULL,64,63,false); diff --git a/src/engine/platform/gb.cpp b/src/engine/platform/gb.cpp index a84173189..8791e6d4c 100644 --- a/src/engine/platform/gb.cpp +++ b/src/engine/platform/gb.cpp @@ -424,6 +424,7 @@ int DivPlatformGB::getRegisterPoolSize() { void DivPlatformGB::reset() { for (int i=0; i<4; i++) { chan[i]=DivPlatformGB::Channel(); + chan[i].std.setEngine(parent); } ws.setEngine(parent); ws.init(NULL,32,15,false); diff --git a/src/engine/platform/genesis.cpp b/src/engine/platform/genesis.cpp index aacc31243..7a9a1d51a 100644 --- a/src/engine/platform/genesis.cpp +++ b/src/engine/platform/genesis.cpp @@ -827,6 +827,7 @@ void DivPlatformGenesis::reset() { } for (int i=0; i<10; i++) { chan[i]=DivPlatformGenesis::Channel(); + chan[i].std.setEngine(parent); chan[i].vol=0x7f; chan[i].outVol=0x7f; } diff --git a/src/engine/platform/lynx.cpp b/src/engine/platform/lynx.cpp index 9ef121594..6120233af 100644 --- a/src/engine/platform/lynx.cpp +++ b/src/engine/platform/lynx.cpp @@ -329,11 +329,11 @@ int DivPlatformLynx::getRegisterPoolSize() } void DivPlatformLynx::reset() { - - mikey = std::make_unique( rate ); + mikey=std::make_unique(rate); for (int i=0; i<4; i++) { - chan[i]= DivPlatformLynx::Channel(); + chan[i]=DivPlatformLynx::Channel(); + chan[i].std.setEngine(parent); } if (dumpWrites) { addWrite(0xffffffff,0); diff --git a/src/engine/platform/mmc5.cpp b/src/engine/platform/mmc5.cpp index e23e242bd..a9f9bb796 100644 --- a/src/engine/platform/mmc5.cpp +++ b/src/engine/platform/mmc5.cpp @@ -348,6 +348,7 @@ float DivPlatformMMC5::getPostAmp() { void DivPlatformMMC5::reset() { for (int i=0; i<3; i++) { chan[i]=DivPlatformMMC5::Channel(); + chan[i].std.setEngine(parent); } if (dumpWrites) { addWrite(0xffffffff,0); diff --git a/src/engine/platform/n163.cpp b/src/engine/platform/n163.cpp index 5de55ef0b..a66157d9e 100644 --- a/src/engine/platform/n163.cpp +++ b/src/engine/platform/n163.cpp @@ -625,6 +625,7 @@ void DivPlatformN163::reset() { while (!writes.empty()) writes.pop(); for (int i=0; i<8; i++) { chan[i]=DivPlatformN163::Channel(); + chan[i].std.setEngine(parent); chan[i].ws.setEngine(parent); chan[i].ws.init(NULL,32,15,false); } diff --git a/src/engine/platform/nes.cpp b/src/engine/platform/nes.cpp index 532f9f23c..4c65ce4f2 100644 --- a/src/engine/platform/nes.cpp +++ b/src/engine/platform/nes.cpp @@ -474,6 +474,7 @@ float DivPlatformNES::getPostAmp() { void DivPlatformNES::reset() { for (int i=0; i<5; i++) { chan[i]=DivPlatformNES::Channel(); + chan[i].std.setEngine(parent); } if (dumpWrites) { addWrite(0xffffffff,0); diff --git a/src/engine/platform/opl.cpp b/src/engine/platform/opl.cpp index 006afc472..add3815c5 100644 --- a/src/engine/platform/opl.cpp +++ b/src/engine/platform/opl.cpp @@ -934,6 +934,7 @@ void DivPlatformOPL::reset() { for (int i=0; ireset(); for (int i=0; i<14; i++) { chan[i]=DivPlatformYM2610::Channel(); + chan[i].std.setEngine(parent); } for (int i=0; i<4; i++) { chan[i].vol=0x7f; diff --git a/src/engine/platform/ym2610b.cpp b/src/engine/platform/ym2610b.cpp index ade5a9d80..130eb29a2 100644 --- a/src/engine/platform/ym2610b.cpp +++ b/src/engine/platform/ym2610b.cpp @@ -1131,6 +1131,7 @@ void DivPlatformYM2610B::reset() { fm->reset(); for (int i=0; i<16; i++) { chan[i]=DivPlatformYM2610B::Channel(); + chan[i].std.setEngine(parent); } for (int i=0; i<6; i++) { chan[i].vol=0x7f; diff --git a/src/engine/playback.cpp b/src/engine/playback.cpp index 240209943..ea2213305 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -1472,8 +1472,13 @@ void DivEngine::nextRow() { bool DivEngine::nextTick(bool noAccum) { bool ret=false; if (divider<10) divider=10; + + if (lowLatency) { + tickMult=1000/divider; + if (tickMult<1) tickMult=1; + } - cycles=got.rate*pow(2,MASTER_CLOCK_PREC)/divider; + cycles=got.rate*pow(2,MASTER_CLOCK_PREC)/(divider*tickMult); clockDrift+=fmod(got.rate*pow(2,MASTER_CLOCK_PREC),(double)divider); if (clockDrift>=divider) { clockDrift-=divider; @@ -1499,130 +1504,133 @@ bool DivEngine::nextTick(bool noAccum) { } if (!freelance) { - if (stepPlay!=1) if (--ticks<=0) { - ret=endOfSong; - if (endOfSong) { - if (song.loopModality!=2) { - playSub(true); + if (--subticks<=0) { + subticks=tickMult; + if (stepPlay!=1) if (--ticks<=0) { + ret=endOfSong; + if (endOfSong) { + if (song.loopModality!=2) { + playSub(true); + } } + endOfSong=false; + if (stepPlay==2) stepPlay=1; + nextRow(); } - endOfSong=false; - if (stepPlay==2) stepPlay=1; - nextRow(); - } - // process stuff - for (int i=0; i0) { - if (--chan[i].rowDelay==0) { - processRow(i,true); + // process stuff + for (int i=0; i0) { + if (--chan[i].rowDelay==0) { + processRow(i,true); + } } - } - if (chan[i].retrigSpeed) { - if (--chan[i].retrigTick<0) { - chan[i].retrigTick=chan[i].retrigSpeed-1; - dispatchCmd(DivCommand(DIV_CMD_NOTE_ON,i,DIV_NOTE_NULL)); - keyHit[i]=true; + if (chan[i].retrigSpeed) { + if (--chan[i].retrigTick<0) { + chan[i].retrigTick=chan[i].retrigSpeed-1; + dispatchCmd(DivCommand(DIV_CMD_NOTE_ON,i,DIV_NOTE_NULL)); + keyHit[i]=true; + } } - } - if (!song.noSlidesOnFirstTick || !firstTick) { - if (chan[i].volSpeed!=0) { - chan[i].volume=(chan[i].volume&0xff)|(dispatchCmd(DivCommand(DIV_CMD_GET_VOLUME,i))<<8); - chan[i].volume+=chan[i].volSpeed; - if (chan[i].volume>chan[i].volMax) { - chan[i].volume=chan[i].volMax; - chan[i].volSpeed=0; - dispatchCmd(DivCommand(DIV_CMD_VOLUME,i,chan[i].volume>>8)); - } else if (chan[i].volume<0) { - chan[i].volSpeed=0; - if (song.legacyVolumeSlides) { - chan[i].volume=chan[i].volMax+1; + if (!song.noSlidesOnFirstTick || !firstTick) { + if (chan[i].volSpeed!=0) { + chan[i].volume=(chan[i].volume&0xff)|(dispatchCmd(DivCommand(DIV_CMD_GET_VOLUME,i))<<8); + chan[i].volume+=chan[i].volSpeed; + if (chan[i].volume>chan[i].volMax) { + chan[i].volume=chan[i].volMax; + chan[i].volSpeed=0; + dispatchCmd(DivCommand(DIV_CMD_VOLUME,i,chan[i].volume>>8)); + } else if (chan[i].volume<0) { + chan[i].volSpeed=0; + if (song.legacyVolumeSlides) { + chan[i].volume=chan[i].volMax+1; + } else { + chan[i].volume=0; + } + dispatchCmd(DivCommand(DIV_CMD_VOLUME,i,chan[i].volume>>8)); } else { - chan[i].volume=0; + dispatchCmd(DivCommand(DIV_CMD_VOLUME,i,chan[i].volume>>8)); } - dispatchCmd(DivCommand(DIV_CMD_VOLUME,i,chan[i].volume>>8)); - } else { - dispatchCmd(DivCommand(DIV_CMD_VOLUME,i,chan[i].volume>>8)); } } - } - if (chan[i].vibratoDepth>0) { - chan[i].vibratoPos+=chan[i].vibratoRate; - if (chan[i].vibratoPos>=64) chan[i].vibratoPos-=64; - switch (chan[i].vibratoDir) { - case 1: // up - dispatchCmd(DivCommand(DIV_CMD_PITCH,i,chan[i].pitch+(MAX(0,(chan[i].vibratoDepth*vibTable[chan[i].vibratoPos]*chan[i].vibratoFine)>>4)/15))); - break; - case 2: // down - dispatchCmd(DivCommand(DIV_CMD_PITCH,i,chan[i].pitch+(MIN(0,(chan[i].vibratoDepth*vibTable[chan[i].vibratoPos]*chan[i].vibratoFine)>>4)/15))); - break; - default: // both - dispatchCmd(DivCommand(DIV_CMD_PITCH,i,chan[i].pitch+(((chan[i].vibratoDepth*vibTable[chan[i].vibratoPos]*chan[i].vibratoFine)>>4)/15))); - break; - } - } - if (!song.noSlidesOnFirstTick || !firstTick) { - if ((chan[i].keyOn || chan[i].keyOff) && chan[i].portaSpeed>0) { - if (dispatchCmd(DivCommand(DIV_CMD_NOTE_PORTA,i,chan[i].portaSpeed,chan[i].portaNote))==2 && chan[i].portaStop && song.targetResetsSlides) { - chan[i].portaSpeed=0; - chan[i].oldNote=chan[i].note; - chan[i].note=chan[i].portaNote; - chan[i].inPorta=false; - dispatchCmd(DivCommand(DIV_CMD_LEGATO,i,chan[i].note)); + if (chan[i].vibratoDepth>0) { + chan[i].vibratoPos+=chan[i].vibratoRate; + if (chan[i].vibratoPos>=64) chan[i].vibratoPos-=64; + switch (chan[i].vibratoDir) { + case 1: // up + dispatchCmd(DivCommand(DIV_CMD_PITCH,i,chan[i].pitch+(MAX(0,(chan[i].vibratoDepth*vibTable[chan[i].vibratoPos]*chan[i].vibratoFine)>>4)/15))); + break; + case 2: // down + dispatchCmd(DivCommand(DIV_CMD_PITCH,i,chan[i].pitch+(MIN(0,(chan[i].vibratoDepth*vibTable[chan[i].vibratoPos]*chan[i].vibratoFine)>>4)/15))); + break; + default: // both + dispatchCmd(DivCommand(DIV_CMD_PITCH,i,chan[i].pitch+(((chan[i].vibratoDepth*vibTable[chan[i].vibratoPos]*chan[i].vibratoFine)>>4)/15))); + break; } } - } - if (chan[i].cut>0) { - if (--chan[i].cut<1) { - chan[i].oldNote=chan[i].note; - //chan[i].note=-1; - if (chan[i].inPorta && song.noteOffResetsSlides) { - chan[i].keyOff=true; - chan[i].keyOn=false; - if (chan[i].stopOnOff) { - chan[i].portaNote=-1; - chan[i].portaSpeed=-1; - chan[i].stopOnOff=false; - } - if (disCont[dispatchOfChan[i]].dispatch->keyOffAffectsPorta(dispatchChanOfChan[i])) { - chan[i].portaNote=-1; - chan[i].portaSpeed=-1; - /*if (i==2 && sysOfChan[i]==DIV_SYSTEM_SMS) { - chan[i+1].portaNote=-1; - chan[i+1].portaSpeed=-1; - }*/ - } - dispatchCmd(DivCommand(DIV_CMD_PRE_PORTA,i,false,0)); - chan[i].scheduledSlideReset=true; - } - dispatchCmd(DivCommand(DIV_CMD_NOTE_OFF,i)); - } - } - if (chan[i].resetArp) { - dispatchCmd(DivCommand(DIV_CMD_LEGATO,i,chan[i].note)); - chan[i].resetArp=false; - } - if (song.rowResetsArpPos && firstTick) { - chan[i].arpStage=-1; - } - if (chan[i].arp!=0 && !chan[i].arpYield && chan[i].portaSpeed<1) { - if (--chan[i].arpTicks<1) { - chan[i].arpTicks=song.arpLen; - chan[i].arpStage++; - if (chan[i].arpStage>2) chan[i].arpStage=0; - switch (chan[i].arpStage) { - case 0: + if (!song.noSlidesOnFirstTick || !firstTick) { + if ((chan[i].keyOn || chan[i].keyOff) && chan[i].portaSpeed>0) { + if (dispatchCmd(DivCommand(DIV_CMD_NOTE_PORTA,i,chan[i].portaSpeed,chan[i].portaNote))==2 && chan[i].portaStop && song.targetResetsSlides) { + chan[i].portaSpeed=0; + chan[i].oldNote=chan[i].note; + chan[i].note=chan[i].portaNote; + chan[i].inPorta=false; dispatchCmd(DivCommand(DIV_CMD_LEGATO,i,chan[i].note)); - break; - case 1: - dispatchCmd(DivCommand(DIV_CMD_LEGATO,i,chan[i].note+(chan[i].arp>>4))); - break; - case 2: - dispatchCmd(DivCommand(DIV_CMD_LEGATO,i,chan[i].note+(chan[i].arp&15))); - break; + } } } - } else { - chan[i].arpYield=false; + if (chan[i].cut>0) { + if (--chan[i].cut<1) { + chan[i].oldNote=chan[i].note; + //chan[i].note=-1; + if (chan[i].inPorta && song.noteOffResetsSlides) { + chan[i].keyOff=true; + chan[i].keyOn=false; + if (chan[i].stopOnOff) { + chan[i].portaNote=-1; + chan[i].portaSpeed=-1; + chan[i].stopOnOff=false; + } + if (disCont[dispatchOfChan[i]].dispatch->keyOffAffectsPorta(dispatchChanOfChan[i])) { + chan[i].portaNote=-1; + chan[i].portaSpeed=-1; + /*if (i==2 && sysOfChan[i]==DIV_SYSTEM_SMS) { + chan[i+1].portaNote=-1; + chan[i+1].portaSpeed=-1; + }*/ + } + dispatchCmd(DivCommand(DIV_CMD_PRE_PORTA,i,false,0)); + chan[i].scheduledSlideReset=true; + } + dispatchCmd(DivCommand(DIV_CMD_NOTE_OFF,i)); + } + } + if (chan[i].resetArp) { + dispatchCmd(DivCommand(DIV_CMD_LEGATO,i,chan[i].note)); + chan[i].resetArp=false; + } + if (song.rowResetsArpPos && firstTick) { + chan[i].arpStage=-1; + } + if (chan[i].arp!=0 && !chan[i].arpYield && chan[i].portaSpeed<1) { + if (--chan[i].arpTicks<1) { + chan[i].arpTicks=song.arpLen; + chan[i].arpStage++; + if (chan[i].arpStage>2) chan[i].arpStage=0; + switch (chan[i].arpStage) { + case 0: + dispatchCmd(DivCommand(DIV_CMD_LEGATO,i,chan[i].note)); + break; + case 1: + dispatchCmd(DivCommand(DIV_CMD_LEGATO,i,chan[i].note+(chan[i].arp>>4))); + break; + case 2: + dispatchCmd(DivCommand(DIV_CMD_LEGATO,i,chan[i].note+(chan[i].arp&15))); + break; + } + } + } else { + chan[i].arpYield=false; + } } } } @@ -1636,7 +1644,7 @@ bool DivEngine::nextTick(bool noAccum) { if (stepPlay!=1) { if (!noAccum) { totalTicksR++; - totalTicks+=1000000/divider; + totalTicks+=1000000/(divider*tickMult); } if (totalTicks>=1000000) { totalTicks-=1000000; diff --git a/src/gui/gui.h b/src/gui/gui.h index 40d0b9c44..67d084a47 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -818,6 +818,7 @@ class FurnaceGUI { int scrollChangesOrder; int oplStandardWaveNames; int cursorMoveNoScroll; + int lowLatency; unsigned int maxUndoSteps; String mainFontPath; String patFontPath; @@ -889,6 +890,7 @@ class FurnaceGUI { scrollChangesOrder(0), oplStandardWaveNames(0), cursorMoveNoScroll(0), + lowLatency(0), maxUndoSteps(100), mainFontPath(""), patFontPath(""), diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index 666ba3b62..f4d62a81d 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -405,6 +405,14 @@ void FurnaceGUI::drawSettings() { e->setMetronomeVol(((float)settings.metroVol)/100.0f); } + bool lowLatencyB=settings.lowLatency; + if (ImGui::Checkbox("Low-latency mode (experimental!)",&lowLatencyB)) { + settings.lowLatency=lowLatencyB; + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("reduces latency by running the engine faster than the tick rate.\nuseful for live playback/jam mode.\n\nwarning: experimental! may produce glitches.\nonly enable if your buffer size is small (10ms or less)."); + } + bool forceMonoB=settings.forceMono; if (ImGui::Checkbox("Force mono audio",&forceMonoB)) { settings.forceMono=forceMonoB; @@ -1600,6 +1608,7 @@ void FurnaceGUI::syncSettings() { settings.scrollChangesOrder=e->getConfInt("scrollChangesOrder",0); settings.oplStandardWaveNames=e->getConfInt("oplStandardWaveNames",0); settings.cursorMoveNoScroll=e->getConfInt("cursorMoveNoScroll",0); + settings.lowLatency=e->getConfInt("lowLatency",0); clampSetting(settings.mainFontSize,2,96); clampSetting(settings.patFontSize,2,96); @@ -1660,6 +1669,7 @@ void FurnaceGUI::syncSettings() { clampSetting(settings.scrollChangesOrder,0,1); clampSetting(settings.oplStandardWaveNames,0,1); clampSetting(settings.cursorMoveNoScroll,0,1); + clampSetting(settings.lowLatency,0,1); // keybinds for (int i=0; isetConf("scrollChangesOrder",settings.scrollChangesOrder); e->setConf("oplStandardWaveNames",settings.oplStandardWaveNames); e->setConf("cursorMoveNoScroll",settings.cursorMoveNoScroll); + e->setConf("lowLatency",settings.lowLatency); // colors for (int i=0; i Date: Tue, 12 Apr 2022 21:07:21 +1000 Subject: [PATCH 081/342] SBI fixes: - Support 2OP\x1A header (used in some third party implementations) - Include 2op pair for Freq Monster 801 6op (4+2op) patches. - Fall back to filename if no internal patch name found. --- src/engine/fileOpsIns.cpp | 71 +++++++++++++++++++++++++++++++++++---- 1 file changed, 65 insertions(+), 6 deletions(-) diff --git a/src/engine/fileOpsIns.cpp b/src/engine/fileOpsIns.cpp index af0c06d83..b8978f2a6 100644 --- a/src/engine/fileOpsIns.cpp +++ b/src/engine/fileOpsIns.cpp @@ -447,6 +447,8 @@ void DivEngine::loadS3I(SafeReader& reader, std::vector& ret, St logE("S3I PCM samples currently not supported."); } ins->name = reader.readString(28); + ins->name = (ins->name[0] == '\0') ? stripPath : ins->name; + int s3i_signature = reader.readI(); if (s3i_signature != 0x49524353) { @@ -470,12 +472,13 @@ void DivEngine::loadSBI(SafeReader& reader, std::vector& ret, St int sbi_header = reader.readI(); // SBI header determines format - bool is_2op = (sbi_header == 0x1A494253); // SBI\x1A + bool is_2op = (sbi_header == 0x1A494253 || sbi_header == 0x1A504F32); // SBI\x1A or 2OP\x1A bool is_4op = (sbi_header == 0x1A504F34); // 4OP\x1A bool is_6op = (sbi_header == 0x1A504F36); // 6OP\x1A - Freq Monster 801-specific // 32-byte null terminated instrument name - ins->name = reader.readString(32); + String patchName = reader.readString(32); + patchName = (patchName.length() == 0) ? stripPath : patchName; // 2op SBI uint8_t sbi_Mcharacteristics = reader.readC(); @@ -502,11 +505,13 @@ void DivEngine::loadSBI(SafeReader& reader, std::vector& ret, St uint8_t sbi_M4wave; uint8_t sbi_C4wave; uint8_t sbi_4opConnect; - + if (is_2op) { DivInstrumentFM::Operator& opM = ins->fm.op[0]; DivInstrumentFM::Operator& opC = ins->fm.op[1]; ins->fm.ops = 2; + ins->name = patchName; + opM.mult = sbi_Mcharacteristics & 0xF; opM.ksr = ((sbi_Mcharacteristics >> 4) & 0x1); opM.sus = ((sbi_Mcharacteristics >> 5) & 0x1); @@ -538,6 +543,7 @@ void DivEngine::loadSBI(SafeReader& reader, std::vector& ret, St // Ignore rest of file - rest is 'reserved padding'. reader.seek(0, SEEK_END); + ret.push_back(ins); } if (is_4op || is_6op) { @@ -549,6 +555,7 @@ void DivEngine::loadSBI(SafeReader& reader, std::vector& ret, St DivInstrumentFM::Operator& opM4 = ins->fm.op[1]; DivInstrumentFM::Operator& opC4 = ins->fm.op[3]; ins->fm.ops = 4; + ins->name = patchName; sbi_M4characteristics = reader.readC(); sbi_C4characteristics = reader.readC(); @@ -617,19 +624,71 @@ void DivEngine::loadSBI(SafeReader& reader, std::vector& ret, St opC4.sl = ((sbi_C4eg_SR >> 4) & 0xF); opC4.ws = sbi_C4wave; + // Freq Monster 801 SBIs use a 4op+2op layout + if (is_6op) { + ins->name = ins->name + " (4op)"; + ret.push_back(ins); + + ins = new DivInstrument; + DivInstrumentFM::Operator& opM6 = ins->fm.op[0]; + DivInstrumentFM::Operator& opC6 = ins->fm.op[1]; + ins->type = DIV_INS_OPL; + ins->fm.ops = 2; + ins->name = patchName + " (2op)"; + + sbi_Mcharacteristics = reader.readC(); + sbi_Ccharacteristics = reader.readC(); + sbi_Mscaling_output = reader.readC(); + sbi_Cscaling_output = reader.readC(); + sbi_Meg_AD = reader.readC(); + sbi_Ceg_AD = reader.readC(); + sbi_Meg_SR = reader.readC(); + sbi_Ceg_SR = reader.readC(); + sbi_Mwave = reader.readC(); + sbi_Cwave = reader.readC(); + sbi_FeedConnect = reader.readC(); + + opM6.mult = sbi_Mcharacteristics & 0xF; + opM6.ksr = ((sbi_Mcharacteristics >> 4) & 0x1); + opM6.sus = ((sbi_Mcharacteristics >> 5) & 0x1); + opM6.vib = ((sbi_Mcharacteristics >> 6) & 0x1); + opM6.am = ((sbi_Mcharacteristics >> 7) & 0x1); + opM6.tl = sbi_Mscaling_output & 0x3F; + opM6.ksl = ((sbi_Mscaling_output >> 6) & 0x3); + opM6.ar = ((sbi_Meg_AD >> 4) & 0xF); + opM6.dr = (sbi_Meg_AD & 0xF); + opM6.rr = (sbi_Meg_SR & 0xF); + opM6.sl = ((sbi_Meg_SR >> 4) & 0xF); + opM6.ws = sbi_Mwave; + + ins->fm.alg = (sbi_FeedConnect & 0x1); + ins->fm.fb = ((sbi_FeedConnect >> 1) & 0x7); + + opC6.mult = sbi_Ccharacteristics & 0xF; + opC6.ksr = ((sbi_Ccharacteristics >> 4) & 0x1); + opC6.sus = ((sbi_Ccharacteristics >> 5) & 0x1); + opC6.vib = ((sbi_Ccharacteristics >> 6) & 0x1); + opC6.am = ((sbi_Ccharacteristics >> 7) & 0x1); + opC6.tl = sbi_Cscaling_output & 0x3F; + opC6.ksl = ((sbi_Cscaling_output >> 6) & 0x3); + opC6.ar = ((sbi_Ceg_AD >> 4) & 0xF); + opC6.dr = (sbi_Ceg_AD & 0xF); + opC6.rr = (sbi_Ceg_SR & 0xF); + opC6.sl = ((sbi_Ceg_SR >> 4) & 0xF); + opC6.ws = sbi_Cwave; + } + // Ignore rest of file once we've read in all we need. // Note: Freq Monster 801 adds a ton of other additional fields irrelevant to chip registers. reader.seek(0, SEEK_END); + ret.push_back(ins); } } catch (EndOfFileException& e) { lastError = "premature end of file"; logE("premature end of file!"); delete ins; - return; } - - ret.push_back(ins); } void DivEngine::loadFF(SafeReader& reader, std::vector& ret, String& stripPath) { From 29ed73a1577de8f136623e07f206519935216840 Mon Sep 17 00:00:00 2001 From: James Alan Nguyen Date: Thu, 14 Apr 2022 21:33:20 +1000 Subject: [PATCH 082/342] Start BNK --- src/engine/engine.h | 1 + src/engine/fileOpsIns.cpp | 30 ++++++++++++++++++++++++++---- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/src/engine/engine.h b/src/engine/engine.h index 6bcaa38c9..d7860f37f 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -287,6 +287,7 @@ class DivEngine { void loadVGI(SafeReader& reader, std::vector& ret, String& stripPath); void loadS3I(SafeReader& reader, std::vector& ret, String& stripPath); void loadSBI(SafeReader& reader, std::vector& ret, String& stripPath); + void loadBNK(SafeReader& reader, std::vector& ret, String& stripPath); void loadOPM(SafeReader& reader, std::vector& ret, String& stripPath); void loadFF(SafeReader& reader, std::vector& ret, String& stripPath); diff --git a/src/engine/fileOpsIns.cpp b/src/engine/fileOpsIns.cpp index b8978f2a6..9be3f02cf 100644 --- a/src/engine/fileOpsIns.cpp +++ b/src/engine/fileOpsIns.cpp @@ -30,6 +30,7 @@ enum DivInsFormats { DIV_INSFORMAT_BTI, DIV_INSFORMAT_S3I, DIV_INSFORMAT_SBI, + DIV_INSFORMAT_BNK, DIV_INSFORMAT_OPM, DIV_INSFORMAT_FF, }; @@ -447,7 +448,7 @@ void DivEngine::loadS3I(SafeReader& reader, std::vector& ret, St logE("S3I PCM samples currently not supported."); } ins->name = reader.readString(28); - ins->name = (ins->name[0] == '\0') ? stripPath : ins->name; + ins->name = (ins->name.length() == 0) ? stripPath : ins->name; int s3i_signature = reader.readI(); @@ -544,9 +545,8 @@ void DivEngine::loadSBI(SafeReader& reader, std::vector& ret, St // Ignore rest of file - rest is 'reserved padding'. reader.seek(0, SEEK_END); ret.push_back(ins); - } - if (is_4op || is_6op) { + } else if (is_4op || is_6op) { // Operator placement is different so need to place in correct registers. // Note: 6op is an unofficial extension of 4op SBIs by Darron Broad (Freq Monster 801). // We'll only use the 4op portion here for pure OPL3. @@ -624,8 +624,9 @@ void DivEngine::loadSBI(SafeReader& reader, std::vector& ret, St opC4.sl = ((sbi_C4eg_SR >> 4) & 0xF); opC4.ws = sbi_C4wave; - // Freq Monster 801 SBIs use a 4op+2op layout if (is_6op) { + // Freq Monster 801 6op SBIs use a 4+2op layout + // Save the 4op portion before reading the 2op part ins->name = ins->name + " (4op)"; ret.push_back(ins); @@ -680,6 +681,7 @@ void DivEngine::loadSBI(SafeReader& reader, std::vector& ret, St // Ignore rest of file once we've read in all we need. // Note: Freq Monster 801 adds a ton of other additional fields irrelevant to chip registers. + // If instrument transpose is ever supported, we can read it in maybe? reader.seek(0, SEEK_END); ret.push_back(ins); } @@ -690,6 +692,22 @@ void DivEngine::loadSBI(SafeReader& reader, std::vector& ret, St delete ins; } } +void DivEngine::loadBNK(SafeReader& reader, std::vector& ret, String& stripPath) { + DivInstrument* insList[256]; + memset(insList, 0, 256 * sizeof(void*)); + + // First distinguish between GEMS BNK and Adlib BNK + bool is_gems = false; // TODO + bool is_adlib = true; // TODO + + if (is_gems) { + logE("GEMS BNK currently not supported."); + + } else if (is_adlib) { + // TODO + } +} + void DivEngine::loadFF(SafeReader& reader, std::vector& ret, String& stripPath) { DivInstrument* insList[256]; @@ -902,6 +920,8 @@ std::vector DivEngine::instrumentFromFile(const char* path) { format=DIV_INSFORMAT_S3I; } else if (extS==String(".sbi")) { format=DIV_INSFORMAT_SBI; + } else if (ext5==String(".bnk")) { + format=DIV_INSFORMAT_BNK; } else if (extS==String(".opm")) { format=DIV_INSFORMAT_OPM; } else if (extS==String(".ff")) { @@ -932,6 +952,8 @@ std::vector DivEngine::instrumentFromFile(const char* path) { case DIV_INSFORMAT_SBI: loadSBI(reader,ret,stripPath); break; + case DIV_INSFORMAT_BNK: + loadBNK(reader, ret, stripPath); case DIV_INSFORMAT_FF: loadFF(reader,ret,stripPath); break; From 7e692eb0735d13072ff83c3d76e9a6504903972c Mon Sep 17 00:00:00 2001 From: James Alan Nguyen Date: Fri, 15 Apr 2022 00:32:55 +1000 Subject: [PATCH 083/342] Checkpoint: BNK presets now loading and also refactoring duplicated operations. --- src/engine/fileOpsIns.cpp | 378 +++++++++++++++++++++----------------- 1 file changed, 207 insertions(+), 171 deletions(-) diff --git a/src/engine/fileOpsIns.cpp b/src/engine/fileOpsIns.cpp index 9be3f02cf..7d2dda373 100644 --- a/src/engine/fileOpsIns.cpp +++ b/src/engine/fileOpsIns.cpp @@ -391,6 +391,9 @@ void DivEngine::loadS3I(SafeReader& reader, std::vector& ret, St if (s3i_type >= 2) { ins->type = DIV_INS_OPL; + if (s3i_type > 2 && s3i_type <= 7) { + ins->fm.opllPreset = (uint8_t)(1<<4); // Flag as Drum preset. + } // skip internal filename - we'll use the long name description reader.seek(12, SEEK_CUR); @@ -481,72 +484,89 @@ void DivEngine::loadSBI(SafeReader& reader, std::vector& ret, St String patchName = reader.readString(32); patchName = (patchName.length() == 0) ? stripPath : patchName; - // 2op SBI - uint8_t sbi_Mcharacteristics = reader.readC(); - uint8_t sbi_Ccharacteristics = reader.readC(); - uint8_t sbi_Mscaling_output = reader.readC(); - uint8_t sbi_Cscaling_output = reader.readC(); - uint8_t sbi_Meg_AD = reader.readC(); - uint8_t sbi_Ceg_AD = reader.readC(); - uint8_t sbi_Meg_SR = reader.readC(); - uint8_t sbi_Ceg_SR = reader.readC(); - uint8_t sbi_Mwave = reader.readC(); - uint8_t sbi_Cwave = reader.readC(); - uint8_t sbi_FeedConnect = reader.readC(); + typedef struct { + uint8_t Mcharacteristics, + Ccharacteristics, + Mscaling_output, + Cscaling_output, + Meg_AD, + Ceg_AD, + Meg_SR, + Ceg_SR, + Mwave, + Cwave, + FeedConnect; + } sbi_t; - // 4op SBI - uint8_t sbi_M4characteristics; - uint8_t sbi_C4characteristics; - uint8_t sbi_M4scaling_output; - uint8_t sbi_C4scaling_output; - uint8_t sbi_M4eg_AD; - uint8_t sbi_C4eg_AD; - uint8_t sbi_M4eg_SR; - uint8_t sbi_C4eg_SR; - uint8_t sbi_M4wave; - uint8_t sbi_C4wave; - uint8_t sbi_4opConnect; + auto readSbiOpData = [](sbi_t& sbi, SafeReader& reader) { + sbi.Mcharacteristics = reader.readC(); + sbi.Ccharacteristics = reader.readC(); + sbi.Mscaling_output = reader.readC(); + sbi.Cscaling_output = reader.readC(); + sbi.Meg_AD = reader.readC(); + sbi.Ceg_AD = reader.readC(); + sbi.Meg_SR = reader.readC(); + sbi.Ceg_SR = reader.readC(); + sbi.Mwave = reader.readC(); + sbi.Cwave = reader.readC(); + sbi.FeedConnect = reader.readC(); + }; + + auto writeOp = [](sbi_t& sbi, DivInstrumentFM::Operator& opM, DivInstrumentFM::Operator& opC) { + opM.mult = sbi.Mcharacteristics & 0xF; + opM.ksr = ((sbi.Mcharacteristics >> 4) & 0x1); + opM.sus = ((sbi.Mcharacteristics >> 5) & 0x1); + opM.vib = ((sbi.Mcharacteristics >> 6) & 0x1); + opM.am = ((sbi.Mcharacteristics >> 7) & 0x1); + opM.tl = sbi.Mscaling_output & 0x3F; + opM.ksl = ((sbi.Mscaling_output >> 6) & 0x3); + opM.ar = ((sbi.Meg_AD >> 4) & 0xF); + opM.dr = (sbi.Meg_AD & 0xF); + opM.rr = (sbi.Meg_SR & 0xF); + opM.sl = ((sbi.Meg_SR >> 4) & 0xF); + opM.ws = sbi.Mwave; + + opC.mult = sbi.Ccharacteristics & 0xF; + opC.ksr = ((sbi.Ccharacteristics >> 4) & 0x1); + opC.sus = ((sbi.Ccharacteristics >> 5) & 0x1); + opC.vib = ((sbi.Ccharacteristics >> 6) & 0x1); + opC.am = ((sbi.Ccharacteristics >> 7) & 0x1); + opC.tl = sbi.Cscaling_output & 0x3F; + opC.ksl = ((sbi.Cscaling_output >> 6) & 0x3); + opC.ar = ((sbi.Ceg_AD >> 4) & 0xF); + opC.dr = (sbi.Ceg_AD & 0xF); + opC.rr = (sbi.Ceg_SR & 0xF); + opC.sl = ((sbi.Ceg_SR >> 4) & 0xF); + opC.ws = sbi.Cwave; + }; + + sbi_t sbi_op12; // 2op (+6op portion) + sbi_t sbi_op34; // 4op + + readSbiOpData(sbi_op12, reader); if (is_2op) { DivInstrumentFM::Operator& opM = ins->fm.op[0]; DivInstrumentFM::Operator& opC = ins->fm.op[1]; ins->fm.ops = 2; ins->name = patchName; + writeOp(sbi_op12, opM, opC); + ins->fm.alg = (sbi_op12.FeedConnect & 0x1); + ins->fm.fb = ((sbi_op12.FeedConnect >> 1) & 0x7); - opM.mult = sbi_Mcharacteristics & 0xF; - opM.ksr = ((sbi_Mcharacteristics >> 4) & 0x1); - opM.sus = ((sbi_Mcharacteristics >> 5) & 0x1); - opM.vib = ((sbi_Mcharacteristics >> 6) & 0x1); - opM.am = ((sbi_Mcharacteristics >> 7) & 0x1); - opM.tl = sbi_Mscaling_output & 0x3F; - opM.ksl = ((sbi_Mscaling_output >> 6) & 0x3); - opM.ar = ((sbi_Meg_AD >> 4) & 0xF); - opM.dr = (sbi_Meg_AD & 0xF); - opM.rr = (sbi_Meg_SR & 0xF); - opM.sl = ((sbi_Meg_SR >> 4) & 0xF); - opM.ws = sbi_Mwave; - - ins->fm.alg = (sbi_FeedConnect & 0x1); - ins->fm.fb = ((sbi_FeedConnect >> 1) & 0x7); - - opC.mult = sbi_Ccharacteristics & 0xF; - opC.ksr = ((sbi_Ccharacteristics >> 4) & 0x1); - opC.sus = ((sbi_Ccharacteristics >> 5) & 0x1); - opC.vib = ((sbi_Ccharacteristics >> 6) & 0x1); - opC.am = ((sbi_Ccharacteristics >> 7) & 0x1); - opC.tl = sbi_Cscaling_output & 0x3F; - opC.ksl = ((sbi_Cscaling_output >> 6) & 0x3); - opC.ar = ((sbi_Ceg_AD >> 4) & 0xF); - opC.dr = (sbi_Ceg_AD & 0xF); - opC.rr = (sbi_Ceg_SR & 0xF); - opC.sl = ((sbi_Ceg_SR >> 4) & 0xF); - opC.ws = sbi_Cwave; + // SBTimbre extensions + uint8_t perc_voc = reader.readC(); + if (perc_voc >= 6) { + ins->fm.opllPreset = (uint8_t)(1 << 4); + } // Ignore rest of file - rest is 'reserved padding'. - reader.seek(0, SEEK_END); + reader.seek(4, SEEK_CUR); ret.push_back(ins); } else if (is_4op || is_6op) { + readSbiOpData(sbi_op34, reader); + // Operator placement is different so need to place in correct registers. // Note: 6op is an unofficial extension of 4op SBIs by Darron Broad (Freq Monster 801). // We'll only use the 4op portion here for pure OPL3. @@ -556,127 +576,28 @@ void DivEngine::loadSBI(SafeReader& reader, std::vector& ret, St DivInstrumentFM::Operator& opC4 = ins->fm.op[3]; ins->fm.ops = 4; ins->name = patchName; - - sbi_M4characteristics = reader.readC(); - sbi_C4characteristics = reader.readC(); - sbi_M4scaling_output = reader.readC(); - sbi_C4scaling_output = reader.readC(); - sbi_M4eg_AD = reader.readC(); - sbi_C4eg_AD = reader.readC(); - sbi_M4eg_SR = reader.readC(); - sbi_C4eg_SR = reader.readC(); - sbi_M4wave = reader.readC(); - sbi_C4wave = reader.readC(); - sbi_4opConnect = reader.readC(); - - ins->fm.alg = (sbi_FeedConnect & 0x1) | ((sbi_4opConnect & 0x1) << 1); - ins->fm.fb = ((sbi_FeedConnect >> 1) & 0x7); - - opM.mult = sbi_Mcharacteristics & 0xF; - opM.ksr = ((sbi_Mcharacteristics >> 4) & 0x1); - opM.sus = ((sbi_Mcharacteristics >> 5) & 0x1); - opM.vib = ((sbi_Mcharacteristics >> 6) & 0x1); - opM.am = ((sbi_Mcharacteristics >> 7) & 0x1); - opM.tl = sbi_Mscaling_output & 0x3F; - opM.ksl = ((sbi_Mscaling_output >> 6) & 0x3); - opM.ar = ((sbi_Meg_AD >> 4) & 0xF); - opM.dr = (sbi_Meg_AD & 0xF); - opM.rr = (sbi_Meg_SR & 0xF); - opM.sl = ((sbi_Meg_SR >> 4) & 0xF); - opM.ws = sbi_Mwave; - - opC.mult = sbi_Ccharacteristics & 0xF; - opC.ksr = ((sbi_Ccharacteristics >> 4) & 0x1); - opC.sus = ((sbi_Ccharacteristics >> 5) & 0x1); - opC.vib = ((sbi_Ccharacteristics >> 6) & 0x1); - opC.am = ((sbi_Ccharacteristics >> 7) & 0x1); - opC.tl = sbi_Cscaling_output & 0x3F; - opC.ksl = ((sbi_Cscaling_output >> 6) & 0x3); - opC.ar = ((sbi_Ceg_AD >> 4) & 0xF); - opC.dr = (sbi_Ceg_AD & 0xF); - opC.rr = (sbi_Ceg_SR & 0xF); - opC.sl = ((sbi_Ceg_SR >> 4) & 0xF); - opC.ws = sbi_Cwave; - - opM4.mult = sbi_M4characteristics & 0xF; - opM4.ksr = ((sbi_M4characteristics >> 4) & 0x1); - opM4.sus = ((sbi_M4characteristics >> 5) & 0x1); - opM4.vib = ((sbi_M4characteristics >> 6) & 0x1); - opM4.am = ((sbi_M4characteristics >> 7) & 0x1); - opM4.tl = sbi_M4scaling_output & 0x3F; - opM4.ksl = ((sbi_M4scaling_output >> 6) & 0x3); - opM4.ar = ((sbi_M4eg_AD >> 4) & 0xF); - opM4.dr = (sbi_M4eg_AD & 0xF); - opM4.rr = (sbi_M4eg_SR & 0xF); - opM4.sl = ((sbi_M4eg_SR >> 4) & 0xF); - opM4.ws = sbi_M4wave; - - opC4.mult = sbi_C4characteristics & 0xF; - opC4.ksr = ((sbi_C4characteristics >> 4) & 0x1); - opC4.sus = ((sbi_C4characteristics >> 5) & 0x1); - opC4.vib = ((sbi_C4characteristics >> 6) & 0x1); - opC4.am = ((sbi_C4characteristics >> 7) & 0x1); - opC4.tl = sbi_C4scaling_output & 0x3F; - opC4.ksl = ((sbi_C4scaling_output >> 6) & 0x3); - opC4.ar = ((sbi_C4eg_AD >> 4) & 0xF); - opC4.dr = (sbi_C4eg_AD & 0xF); - opC4.rr = (sbi_C4eg_SR & 0xF); - opC4.sl = ((sbi_C4eg_SR >> 4) & 0xF); - opC4.ws = sbi_C4wave; + ins->fm.alg = (sbi_op12.FeedConnect & 0x1) | ((sbi_op34.FeedConnect & 0x1) << 1); + ins->fm.fb = ((sbi_op34.FeedConnect >> 1) & 0x7); + writeOp(sbi_op12, opM, opC); + writeOp(sbi_op34, opM4, opC4); if (is_6op) { // Freq Monster 801 6op SBIs use a 4+2op layout // Save the 4op portion before reading the 2op part - ins->name = ins->name + " (4op)"; + ins->name = fmt::format("{0} (4op)", ins->name); ret.push_back(ins); + readSbiOpData(sbi_op12, reader); + ins = new DivInstrument; DivInstrumentFM::Operator& opM6 = ins->fm.op[0]; DivInstrumentFM::Operator& opC6 = ins->fm.op[1]; ins->type = DIV_INS_OPL; ins->fm.ops = 2; - ins->name = patchName + " (2op)"; - - sbi_Mcharacteristics = reader.readC(); - sbi_Ccharacteristics = reader.readC(); - sbi_Mscaling_output = reader.readC(); - sbi_Cscaling_output = reader.readC(); - sbi_Meg_AD = reader.readC(); - sbi_Ceg_AD = reader.readC(); - sbi_Meg_SR = reader.readC(); - sbi_Ceg_SR = reader.readC(); - sbi_Mwave = reader.readC(); - sbi_Cwave = reader.readC(); - sbi_FeedConnect = reader.readC(); - - opM6.mult = sbi_Mcharacteristics & 0xF; - opM6.ksr = ((sbi_Mcharacteristics >> 4) & 0x1); - opM6.sus = ((sbi_Mcharacteristics >> 5) & 0x1); - opM6.vib = ((sbi_Mcharacteristics >> 6) & 0x1); - opM6.am = ((sbi_Mcharacteristics >> 7) & 0x1); - opM6.tl = sbi_Mscaling_output & 0x3F; - opM6.ksl = ((sbi_Mscaling_output >> 6) & 0x3); - opM6.ar = ((sbi_Meg_AD >> 4) & 0xF); - opM6.dr = (sbi_Meg_AD & 0xF); - opM6.rr = (sbi_Meg_SR & 0xF); - opM6.sl = ((sbi_Meg_SR >> 4) & 0xF); - opM6.ws = sbi_Mwave; - - ins->fm.alg = (sbi_FeedConnect & 0x1); - ins->fm.fb = ((sbi_FeedConnect >> 1) & 0x7); - - opC6.mult = sbi_Ccharacteristics & 0xF; - opC6.ksr = ((sbi_Ccharacteristics >> 4) & 0x1); - opC6.sus = ((sbi_Ccharacteristics >> 5) & 0x1); - opC6.vib = ((sbi_Ccharacteristics >> 6) & 0x1); - opC6.am = ((sbi_Ccharacteristics >> 7) & 0x1); - opC6.tl = sbi_Cscaling_output & 0x3F; - opC6.ksl = ((sbi_Cscaling_output >> 6) & 0x3); - opC6.ar = ((sbi_Ceg_AD >> 4) & 0xF); - opC6.dr = (sbi_Ceg_AD & 0xF); - opC6.rr = (sbi_Ceg_SR & 0xF); - opC6.sl = ((sbi_Ceg_SR >> 4) & 0xF); - opC6.ws = sbi_Cwave; + ins->name = fmt::format("{0} (2op)", patchName); + writeOp(sbi_op12, opM6, opC6); + ins->fm.alg = (sbi_op12.FeedConnect & 0x1); + ins->fm.fb = ((sbi_op12.FeedConnect >> 1) & 0x7); } // Ignore rest of file once we've read in all we need. @@ -694,18 +615,133 @@ void DivEngine::loadSBI(SafeReader& reader, std::vector& ret, St } void DivEngine::loadBNK(SafeReader& reader, std::vector& ret, String& stripPath) { DivInstrument* insList[256]; + String instNames[256]; memset(insList, 0, 256 * sizeof(void*)); + reader.seek(0, SEEK_SET); // First distinguish between GEMS BNK and Adlib BNK - bool is_gems = false; // TODO - bool is_adlib = true; // TODO + uint64_t header = reader.readL(); + bool is_adlib = ((header>>8) == 0x2d42494c444100L); + + if (is_adlib) { + // Caveat: Technically Adlib BNK can hold up to 0xFFFF instruments, + // but Furnace only can handle up to 0xFF. + + typedef struct { + uint8_t ksl, + multiple, + feedback, // op1 only + attack, + sustain, + eg, + decay, + releaseRate, + totalLevel, + am, + vib, + ksr, + con; // op1 only + } bnkop_t; - if (is_gems) { - logE("GEMS BNK currently not supported."); + typedef struct { + uint8_t mode, // version + percVoice; // perc + bnkop_t op[2]; + uint8_t wave0, // wave op1 + wave1; // wave op2 + } bnktimbre_t; - } else if (is_adlib) { - // TODO - } + int readCount = 0; + + try { + // Seek to BNK patch names + reader.seek(0x0c, SEEK_SET); + int name_offset = reader.readI(); + reader.seek(0x10, SEEK_SET); + int data_offset = reader.readI(); + + reader.seek(name_offset, SEEK_SET); + + while (readCount < 256 && reader.tell() < data_offset) { + reader.seek(3, SEEK_CUR); + instNames[readCount] = reader.readString(9); + ++readCount; + } + + if (readCount >= 256) { + logW("BNK exceeds 256 presets. Only first 256 will be imported.\n"); + } + + // Seek to BNK data + reader.seek(data_offset, SEEK_SET); + + // Read until EOF + for (int i = 0; i < readCount && i < 256; ++i) { + try { + bnktimbre_t timbre; + insList[i] = new DivInstrument; + auto& ins = insList[i]; + + ins->type = DIV_INS_OPL; + + timbre.mode = reader.readC(); + timbre.percVoice = reader.readC(); + if (timbre.mode == 1) { + ins->fm.opllPreset = (uint8_t)(1<<4); + } + ins->fm.op[0].ksl = reader.readC(); + ins->fm.op[0].mult = reader.readC(); + ins->fm.fb = reader.readC(); + ins->fm.op[0].ar = reader.readC(); + ins->fm.op[0].sl = reader.readC(); + ins->fm.op[0].ksr = reader.readC(); + ins->fm.op[0].dr = reader.readC(); + ins->fm.op[0].rr = reader.readC(); + ins->fm.op[0].tl = reader.readC(); + ins->fm.op[0].am = reader.readC(); + ins->fm.op[0].vib = reader.readC(); + ins->fm.op[0].ksr = reader.readC(); + ins->fm.alg = (reader.readC() == 0) ? 1 : 0; + + ins->fm.op[1].ksl = reader.readC(); + ins->fm.op[1].mult = reader.readC(); + reader.readC(); // skip + ins->fm.op[1].ar = reader.readC(); + ins->fm.op[1].sl = reader.readC(); + ins->fm.op[1].ksr = reader.readC(); + ins->fm.op[1].dr = reader.readC(); + ins->fm.op[1].rr = reader.readC(); + ins->fm.op[1].tl = reader.readC(); + ins->fm.op[1].am = reader.readC(); + ins->fm.op[1].vib = reader.readC(); + ins->fm.op[1].ksr = reader.readC(); + reader.readC(); // skip + + ins->fm.op[0].ws = reader.readC(); + ins->fm.op[1].ws = reader.readC(); + ins->name = instNames[i].length() > 0 ? instNames[i] : fmt::format("{0}[{1}]", stripPath, i); + + ret.push_back(insList[i]); + } catch (EndOfFileException& e) { + // Reached end of BNK data + delete insList[i]; + break; + } + } + } catch (EndOfFileException& e) { + lastError = "premature end of file"; + logE("premature end of file!\n"); + for (int i = readCount; i >= 0; --i) { + delete insList[i]; + } + return; + } + + } else { + // assume GEMS BNK for now. + lastError = "GEMS BNK currently not supported.\n"; + logE("GEMS BNK currently not supported.\n"); + } } @@ -920,7 +956,7 @@ std::vector DivEngine::instrumentFromFile(const char* path) { format=DIV_INSFORMAT_S3I; } else if (extS==String(".sbi")) { format=DIV_INSFORMAT_SBI; - } else if (ext5==String(".bnk")) { + } else if (extS==String(".bnk")) { format=DIV_INSFORMAT_BNK; } else if (extS==String(".opm")) { format=DIV_INSFORMAT_OPM; From c431add35b4b42a4b14fc2e999b8178c973397ff Mon Sep 17 00:00:00 2001 From: James Alan Nguyen Date: Fri, 15 Apr 2022 00:42:05 +1000 Subject: [PATCH 084/342] BNK - Fix sustain flag --- src/engine/fileOpsIns.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/engine/fileOpsIns.cpp b/src/engine/fileOpsIns.cpp index 7d2dda373..1338b7965 100644 --- a/src/engine/fileOpsIns.cpp +++ b/src/engine/fileOpsIns.cpp @@ -694,7 +694,7 @@ void DivEngine::loadBNK(SafeReader& reader, std::vector& ret, St ins->fm.fb = reader.readC(); ins->fm.op[0].ar = reader.readC(); ins->fm.op[0].sl = reader.readC(); - ins->fm.op[0].ksr = reader.readC(); + ins->fm.op[0].sus = (reader.readC() != 0) ? 1 : 0; ins->fm.op[0].dr = reader.readC(); ins->fm.op[0].rr = reader.readC(); ins->fm.op[0].tl = reader.readC(); @@ -708,7 +708,7 @@ void DivEngine::loadBNK(SafeReader& reader, std::vector& ret, St reader.readC(); // skip ins->fm.op[1].ar = reader.readC(); ins->fm.op[1].sl = reader.readC(); - ins->fm.op[1].ksr = reader.readC(); + ins->fm.op[1].sus = (reader.readC() != 0) ? 1 : 0; ins->fm.op[1].dr = reader.readC(); ins->fm.op[1].rr = reader.readC(); ins->fm.op[1].tl = reader.readC(); From 1985546d0b23059aaca88b5e3018c1d98e033c9b Mon Sep 17 00:00:00 2001 From: James Alan Nguyen Date: Fri, 15 Apr 2022 00:46:26 +1000 Subject: [PATCH 085/342] Add file extension to picker --- src/gui/gui.cpp | 4 ++-- src/gui/settings.cpp | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index ba83b8654..bdee56563 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -1303,9 +1303,9 @@ void FurnaceGUI::openFileDialog(FurnaceGUIFileDialogs type) { if (!dirExists(workingDirIns)) workingDirIns=getHomeDir(); hasOpened=fileDialog->openLoad( "Load Instrument", - {"compatible files", "*.fui *.dmp *.tfi *.vgi *.s3i *.sbi *.ff", + {"compatible files", "*.fui *.dmp *.tfi *.vgi *.s3i *.sbi *.bnk *.ff", "all files", ".*"}, - "compatible files{.fui,.dmp,.tfi,.vgi,.s3i,.sbi,.ff},.*", + "compatible files{.fui,.dmp,.tfi,.vgi,.s3i,.sbi,.bnk,.ff},.*", workingDirIns, dpiScale ); diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index f4d62a81d..d9495f976 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -2437,6 +2437,7 @@ void FurnaceGUI::applyUISettings(bool updateFonts) { ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".vgi",uiColors[GUI_COLOR_FILE_INSTR],ICON_FA_FILE); ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".s3i",uiColors[GUI_COLOR_FILE_INSTR],ICON_FA_FILE); ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".sbi",uiColors[GUI_COLOR_FILE_INSTR],ICON_FA_FILE); + ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".bnk",uiColors[GUI_COLOR_FILE_INSTR],ICON_FA_FILE); ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".fti",uiColors[GUI_COLOR_FILE_INSTR],ICON_FA_FILE); ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".bti",uiColors[GUI_COLOR_FILE_INSTR],ICON_FA_FILE); ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".ff",uiColors[GUI_COLOR_FILE_INSTR],ICON_FA_FILE); From c3b393004d4915187b23cd87e258bc7bd80db05a Mon Sep 17 00:00:00 2001 From: James Alan Nguyen Date: Fri, 15 Apr 2022 00:57:24 +1000 Subject: [PATCH 086/342] Fix for -Werror o/ o/ o/ --- buildme.bat | 6 ++++++ src/engine/fileOpsIns.cpp | 5 +++-- 2 files changed, 9 insertions(+), 2 deletions(-) create mode 100644 buildme.bat diff --git a/buildme.bat b/buildme.bat new file mode 100644 index 000000000..239d4fe06 --- /dev/null +++ b/buildme.bat @@ -0,0 +1,6 @@ +@echo off +git pull +git submodule update --init --recursive +cmake -Bbuild +cmake --build build --target ALL_BUILD --config Release --parallel 12 +copy build\Release\furnace.exe \Apps\Audio\Furnace\ diff --git a/src/engine/fileOpsIns.cpp b/src/engine/fileOpsIns.cpp index 1338b7965..4556e5a8b 100644 --- a/src/engine/fileOpsIns.cpp +++ b/src/engine/fileOpsIns.cpp @@ -656,9 +656,9 @@ void DivEngine::loadBNK(SafeReader& reader, std::vector& ret, St try { // Seek to BNK patch names reader.seek(0x0c, SEEK_SET); - int name_offset = reader.readI(); + uint32_t name_offset = reader.readI(); reader.seek(0x10, SEEK_SET); - int data_offset = reader.readI(); + uint32_t data_offset = reader.readI(); reader.seek(name_offset, SEEK_SET); @@ -990,6 +990,7 @@ std::vector DivEngine::instrumentFromFile(const char* path) { break; case DIV_INSFORMAT_BNK: loadBNK(reader, ret, stripPath); + break; case DIV_INSFORMAT_FF: loadFF(reader,ret,stripPath); break; From 2dc80e8d8ed859f1fa046b21b1faf498eed3940a Mon Sep 17 00:00:00 2001 From: James Alan Nguyen Date: Fri, 15 Apr 2022 01:07:11 +1000 Subject: [PATCH 087/342] Batch file wasn't supposed to be there --- buildme.bat | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 buildme.bat diff --git a/buildme.bat b/buildme.bat deleted file mode 100644 index 239d4fe06..000000000 --- a/buildme.bat +++ /dev/null @@ -1,6 +0,0 @@ -@echo off -git pull -git submodule update --init --recursive -cmake -Bbuild -cmake --build build --target ALL_BUILD --config Release --parallel 12 -copy build\Release\furnace.exe \Apps\Audio\Furnace\ From c3ca175e4635b683975416746fa6e06e6af044f9 Mon Sep 17 00:00:00 2001 From: James Alan Nguyen Date: Fri, 15 Apr 2022 11:57:57 +1000 Subject: [PATCH 088/342] Nitpicky cruft cleanup. --- src/engine/fileOpsIns.cpp | 125 +++++++++++++++++++------------------- 1 file changed, 62 insertions(+), 63 deletions(-) diff --git a/src/engine/fileOpsIns.cpp b/src/engine/fileOpsIns.cpp index 4556e5a8b..2391f2a1a 100644 --- a/src/engine/fileOpsIns.cpp +++ b/src/engine/fileOpsIns.cpp @@ -448,7 +448,8 @@ void DivEngine::loadS3I(SafeReader& reader, std::vector& ret, St // Skip more stuff we don't need reader.seek(21, SEEK_CUR); } else { - logE("S3I PCM samples currently not supported."); + lastError = "S3I PCM samples currently not supported."; + logE("S3I PCM samples currently not supported.\n"); } ins->name = reader.readString(28); ins->name = (ins->name.length() == 0) ? stripPath : ins->name; @@ -456,7 +457,8 @@ void DivEngine::loadS3I(SafeReader& reader, std::vector& ret, St int s3i_signature = reader.readI(); if (s3i_signature != 0x49524353) { - logW("S3I signature invalid."); + lastError = "S3I signature invalid."; + logW("S3I signature invalid.\n"); }; } catch (EndOfFileException& e) { lastError = "premature end of file"; @@ -622,7 +624,8 @@ void DivEngine::loadBNK(SafeReader& reader, std::vector& ret, St // First distinguish between GEMS BNK and Adlib BNK uint64_t header = reader.readL(); bool is_adlib = ((header>>8) == 0x2d42494c444100L); - + int readCount = 0; + if (is_adlib) { // Caveat: Technically Adlib BNK can hold up to 0xFFFF instruments, // but Furnace only can handle up to 0xFF. @@ -644,90 +647,82 @@ void DivEngine::loadBNK(SafeReader& reader, std::vector& ret, St } bnkop_t; typedef struct { - uint8_t mode, // version - percVoice; // perc + uint8_t mode, + percVoice; bnkop_t op[2]; - uint8_t wave0, // wave op1 - wave1; // wave op2 + uint8_t wave0, + wave1; } bnktimbre_t; - int readCount = 0; - try { - // Seek to BNK patch names reader.seek(0x0c, SEEK_SET); uint32_t name_offset = reader.readI(); reader.seek(0x10, SEEK_SET); uint32_t data_offset = reader.readI(); + // Seek to BNK patch names reader.seek(name_offset, SEEK_SET); - - while (readCount < 256 && reader.tell() < data_offset) { + while (reader.tell() < data_offset) { + if (readCount >= 256) { + lastError = "BNK exceeds 256 presets. Only first 256 will be imported."; + logW("BNK exceeds 256 presets. Only first 256 will be imported.\n"); + break; + } reader.seek(3, SEEK_CUR); instNames[readCount] = reader.readString(9); ++readCount; } - if (readCount >= 256) { - logW("BNK exceeds 256 presets. Only first 256 will be imported.\n"); - } - // Seek to BNK data reader.seek(data_offset, SEEK_SET); // Read until EOF for (int i = 0; i < readCount && i < 256; ++i) { - try { - bnktimbre_t timbre; - insList[i] = new DivInstrument; - auto& ins = insList[i]; + bnktimbre_t timbre; + insList[i] = new DivInstrument; + auto& ins = insList[i]; - ins->type = DIV_INS_OPL; + ins->type = DIV_INS_OPL; - timbre.mode = reader.readC(); - timbre.percVoice = reader.readC(); - if (timbre.mode == 1) { - ins->fm.opllPreset = (uint8_t)(1<<4); - } - ins->fm.op[0].ksl = reader.readC(); - ins->fm.op[0].mult = reader.readC(); - ins->fm.fb = reader.readC(); - ins->fm.op[0].ar = reader.readC(); - ins->fm.op[0].sl = reader.readC(); - ins->fm.op[0].sus = (reader.readC() != 0) ? 1 : 0; - ins->fm.op[0].dr = reader.readC(); - ins->fm.op[0].rr = reader.readC(); - ins->fm.op[0].tl = reader.readC(); - ins->fm.op[0].am = reader.readC(); - ins->fm.op[0].vib = reader.readC(); - ins->fm.op[0].ksr = reader.readC(); - ins->fm.alg = (reader.readC() == 0) ? 1 : 0; - - ins->fm.op[1].ksl = reader.readC(); - ins->fm.op[1].mult = reader.readC(); - reader.readC(); // skip - ins->fm.op[1].ar = reader.readC(); - ins->fm.op[1].sl = reader.readC(); - ins->fm.op[1].sus = (reader.readC() != 0) ? 1 : 0; - ins->fm.op[1].dr = reader.readC(); - ins->fm.op[1].rr = reader.readC(); - ins->fm.op[1].tl = reader.readC(); - ins->fm.op[1].am = reader.readC(); - ins->fm.op[1].vib = reader.readC(); - ins->fm.op[1].ksr = reader.readC(); - reader.readC(); // skip - - ins->fm.op[0].ws = reader.readC(); - ins->fm.op[1].ws = reader.readC(); - ins->name = instNames[i].length() > 0 ? instNames[i] : fmt::format("{0}[{1}]", stripPath, i); - - ret.push_back(insList[i]); - } catch (EndOfFileException& e) { - // Reached end of BNK data - delete insList[i]; - break; + timbre.mode = reader.readC(); + timbre.percVoice = reader.readC(); + if (timbre.mode == 1) { + ins->fm.opllPreset = (uint8_t)(1<<4); } + ins->fm.op[0].ksl = reader.readC(); + ins->fm.op[0].mult = reader.readC(); + ins->fm.fb = reader.readC(); + ins->fm.op[0].ar = reader.readC(); + ins->fm.op[0].sl = reader.readC(); + ins->fm.op[0].sus = (reader.readC() != 0) ? 1 : 0; + ins->fm.op[0].dr = reader.readC(); + ins->fm.op[0].rr = reader.readC(); + ins->fm.op[0].tl = reader.readC(); + ins->fm.op[0].am = reader.readC(); + ins->fm.op[0].vib = reader.readC(); + ins->fm.op[0].ksr = reader.readC(); + ins->fm.alg = (reader.readC() == 0) ? 1 : 0; + + ins->fm.op[1].ksl = reader.readC(); + ins->fm.op[1].mult = reader.readC(); + reader.readC(); // skip + ins->fm.op[1].ar = reader.readC(); + ins->fm.op[1].sl = reader.readC(); + ins->fm.op[1].sus = (reader.readC() != 0) ? 1 : 0; + ins->fm.op[1].dr = reader.readC(); + ins->fm.op[1].rr = reader.readC(); + ins->fm.op[1].tl = reader.readC(); + ins->fm.op[1].am = reader.readC(); + ins->fm.op[1].vib = reader.readC(); + ins->fm.op[1].ksr = reader.readC(); + reader.readC(); // skip + + ins->fm.op[0].ws = reader.readC(); + ins->fm.op[1].ws = reader.readC(); + ins->name = instNames[i].length() > 0 ? instNames[i] : fmt::format("{0}[{1}]", stripPath, i); } + reader.seek(0, SEEK_END); + } catch (EndOfFileException& e) { lastError = "premature end of file"; logE("premature end of file!\n"); @@ -742,6 +737,10 @@ void DivEngine::loadBNK(SafeReader& reader, std::vector& ret, St lastError = "GEMS BNK currently not supported.\n"; logE("GEMS BNK currently not supported.\n"); } + + for (int i = 0; i < readCount; ++i) { + ret.push_back(insList[i]); + } } From eb70086234de1949c43c90efeeaa4216584099b5 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 15 Apr 2022 14:38:13 -0500 Subject: [PATCH 089/342] sample 8-bit blep resample fix --- src/engine/sample.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/engine/sample.cpp b/src/engine/sample.cpp index bd34daa5b..3aec450b0 100644 --- a/src/engine/sample.cpp +++ b/src/engine/sample.cpp @@ -587,8 +587,8 @@ bool DivSample::resampleSinc(double r) { result+=s[j]*t2[7-j]; result+=s[8+j]*t1[j]; } - if (result<-32768) result=-32768; - if (result>32767) result=32767; + if (result<-128) result=-128; + if (result>127) result=127; if (i>=8) { data8[i-8]=result; } From 45460df96d24c87fea90e58fe3d48a235c23e9f1 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 15 Apr 2022 14:38:25 -0500 Subject: [PATCH 090/342] improve low-latency mode strategy --- src/engine/engine.cpp | 2 ++ src/engine/macroInt.cpp | 25 ++++++++++++++++--------- src/engine/macroInt.h | 11 ++++++----- src/engine/playback.cpp | 2 +- 4 files changed, 25 insertions(+), 15 deletions(-) diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 81d7947a7..6257ff537 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -2237,6 +2237,8 @@ bool DivEngine::initAudioBackend() { if (metroVol<0.0f) metroVol=0.0f; if (metroVol>2.0f) metroVol=2.0f; + if (lowLatency) logI("using low latency mode."); + switch (audioEngine) { case DIV_AUDIO_JACK: #ifndef HAVE_JACK diff --git a/src/engine/macroInt.cpp b/src/engine/macroInt.cpp index ae6caf5df..00fee080e 100644 --- a/src/engine/macroInt.cpp +++ b/src/engine/macroInt.cpp @@ -21,14 +21,19 @@ #include "instrument.h" #include "engine.h" -void DivMacroStruct::doMacro(DivInstrumentMacro& source, bool released) { +void DivMacroStruct::doMacro(DivInstrumentMacro& source, bool released, bool tick) { + if (!tick) { + had=false; + return; + } if (finished) { finished=false; } - if (had!=has) { + if (actualHad!=has) { finished=true; } - had=has; + actualHad=has; + had=actualHad; if (has) { val=source.val[pos++]; if (source.rel>=0 && pos>source.rel && !released) { @@ -52,17 +57,18 @@ void DivMacroInt::next() { if (ins==NULL) return; // run macros // TODO: potentially get rid of list to avoid allocations - if (--subTick<=0) { + subTick--; + for (size_t i=0; idoMacro(*macroSource[i],released,subTick==0); + } + } + if (subTick<=0) { if (e==NULL) { subTick=1; } else { subTick=e->tickMult; } - for (size_t i=0; idoMacro(*macroSource[i],released); - } - } } } @@ -85,6 +91,7 @@ void DivMacroInt::init(DivInstrument* which) { if (macroList[i]!=NULL) macroList[i]->init(); } macroListLen=0; + subTick=1; released=false; diff --git a/src/engine/macroInt.h b/src/engine/macroInt.h index 886ecbe23..f1cd29a7e 100644 --- a/src/engine/macroInt.h +++ b/src/engine/macroInt.h @@ -27,17 +27,17 @@ class DivEngine; struct DivMacroStruct { int pos; int val; - bool has, had, finished, will; + bool has, had, actualHad, finished, will; unsigned int mode; - void doMacro(DivInstrumentMacro& source, bool released); + void doMacro(DivInstrumentMacro& source, bool released, bool tick); void init() { pos=mode=0; - has=had=will=false; + has=had=actualHad=will=false; // TODO: test whether this breaks anything? val=0; } void prepare(DivInstrumentMacro& source) { - has=had=will=true; + has=had=actualHad=will=true; mode=source.mode; } DivMacroStruct(): @@ -45,6 +45,7 @@ struct DivMacroStruct { val(0), has(false), had(false), + actualHad(false), finished(false), will(false), mode(0) {} @@ -128,7 +129,7 @@ class DivMacroInt { e(NULL), ins(NULL), macroListLen(0), - subTick(0), + subTick(1), released(false), vol(), arp(), diff --git a/src/engine/playback.cpp b/src/engine/playback.cpp index ea2213305..fe42c73af 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -1654,7 +1654,7 @@ bool DivEngine::nextTick(bool noAccum) { } } - if (consoleMode) fprintf(stderr,"\x1b[2K> %d:%.2d:%.2d.%.2d %.2x/%.2x:%.3d/%.3d %4dcmd/s\x1b[G",totalSeconds/3600,(totalSeconds/60)%60,totalSeconds%60,totalTicks/10000,curOrder,song.ordersLen,curRow,song.patLen,cmdsPerSecond); + if (consoleMode && subticks<=1) fprintf(stderr,"\x1b[2K> %d:%.2d:%.2d.%.2d %.2x/%.2x:%.3d/%.3d %4dcmd/s\x1b[G",totalSeconds/3600,(totalSeconds/60)%60,totalSeconds%60,totalTicks/10000,curOrder,song.ordersLen,curRow,song.patLen,cmdsPerSecond); } if (haltOn==DIV_HALT_TICK) halted=true; From fd3d57b1cba942ded63d532c5db4f0ca3d378ace Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 15 Apr 2022 15:01:11 -0500 Subject: [PATCH 091/342] even more improvements to low-latency mode --- src/engine/dispatch.h | 3 ++- src/engine/platform/abstract.cpp | 2 +- src/engine/platform/amiga.cpp | 2 +- src/engine/platform/amiga.h | 2 +- src/engine/platform/arcade.cpp | 2 +- src/engine/platform/arcade.h | 2 +- src/engine/platform/ay.cpp | 2 +- src/engine/platform/ay.h | 2 +- src/engine/platform/ay8930.cpp | 2 +- src/engine/platform/ay8930.h | 2 +- src/engine/platform/bubsyswsg.cpp | 2 +- src/engine/platform/bubsyswsg.h | 2 +- src/engine/platform/c64.cpp | 16 +++++++++------- src/engine/platform/c64.h | 2 +- src/engine/platform/dummy.cpp | 10 ++++++---- src/engine/platform/dummy.h | 2 +- src/engine/platform/fds.cpp | 2 +- src/engine/platform/fds.h | 2 +- src/engine/platform/gb.cpp | 2 +- src/engine/platform/gb.h | 2 +- src/engine/platform/genesis.cpp | 2 +- src/engine/platform/genesis.h | 2 +- src/engine/platform/genesisext.cpp | 4 ++-- src/engine/platform/genesisext.h | 2 +- src/engine/platform/lynx.cpp | 2 +- src/engine/platform/lynx.h | 2 +- src/engine/platform/mmc5.cpp | 2 +- src/engine/platform/mmc5.h | 2 +- src/engine/platform/n163.cpp | 2 +- src/engine/platform/n163.h | 2 +- src/engine/platform/nes.cpp | 2 +- src/engine/platform/nes.h | 2 +- src/engine/platform/opl.cpp | 2 +- src/engine/platform/opl.h | 2 +- src/engine/platform/opll.cpp | 2 +- src/engine/platform/opll.h | 2 +- src/engine/platform/pce.cpp | 2 +- src/engine/platform/pce.h | 2 +- src/engine/platform/pcspkr.cpp | 2 +- src/engine/platform/pcspkr.h | 2 +- src/engine/platform/pet.cpp | 2 +- src/engine/platform/pet.h | 2 +- src/engine/platform/qsound.cpp | 2 +- src/engine/platform/qsound.h | 2 +- src/engine/platform/saa.cpp | 2 +- src/engine/platform/saa.h | 2 +- src/engine/platform/segapcm.cpp | 2 +- src/engine/platform/segapcm.h | 2 +- src/engine/platform/sms.cpp | 2 +- src/engine/platform/sms.h | 2 +- src/engine/platform/swan.cpp | 2 +- src/engine/platform/swan.h | 2 +- src/engine/platform/tia.cpp | 2 +- src/engine/platform/tia.h | 2 +- src/engine/platform/tx81z.cpp | 2 +- src/engine/platform/tx81z.h | 2 +- src/engine/platform/vera.cpp | 2 +- src/engine/platform/vera.h | 2 +- src/engine/platform/vic20.cpp | 2 +- src/engine/platform/vic20.h | 2 +- src/engine/platform/vrc6.cpp | 2 +- src/engine/platform/vrc6.h | 2 +- src/engine/platform/x1_010.cpp | 2 +- src/engine/platform/x1_010.h | 2 +- src/engine/platform/ym2610.cpp | 4 ++-- src/engine/platform/ym2610.h | 2 +- src/engine/platform/ym2610b.cpp | 4 ++-- src/engine/platform/ym2610b.h | 2 +- src/engine/platform/ym2610bext.cpp | 4 ++-- src/engine/platform/ym2610bext.h | 2 +- src/engine/platform/ym2610ext.cpp | 4 ++-- src/engine/platform/ym2610ext.h | 2 +- src/engine/playback.cpp | 2 +- src/engine/waveSynth.cpp | 4 ++++ src/engine/waveSynth.h | 3 ++- 75 files changed, 98 insertions(+), 88 deletions(-) diff --git a/src/engine/dispatch.h b/src/engine/dispatch.h index 37e36a2bf..c35ff919c 100644 --- a/src/engine/dispatch.h +++ b/src/engine/dispatch.h @@ -245,8 +245,9 @@ class DivDispatch { /** * ticks this dispatch. + * @param sysTick whether the engine has ticked (if not then this may be a sub-tick used in low-latency mode). */ - virtual void tick(); + virtual void tick(bool sysTick=true); /** * get the state of a channel. diff --git a/src/engine/platform/abstract.cpp b/src/engine/platform/abstract.cpp index b860f6808..343838e73 100644 --- a/src/engine/platform/abstract.cpp +++ b/src/engine/platform/abstract.cpp @@ -22,7 +22,7 @@ void DivDispatch::acquire(short* bufL, short* bufR, size_t start, size_t len) { } -void DivDispatch::tick() { +void DivDispatch::tick(bool sysTick) { } void* DivDispatch::getChanState(int chan) { diff --git a/src/engine/platform/amiga.cpp b/src/engine/platform/amiga.cpp index b8419d197..104e66d45 100644 --- a/src/engine/platform/amiga.cpp +++ b/src/engine/platform/amiga.cpp @@ -155,7 +155,7 @@ void DivPlatformAmiga::acquire(short* bufL, short* bufR, size_t start, size_t le } } -void DivPlatformAmiga::tick() { +void DivPlatformAmiga::tick(bool sysTick) { for (int i=0; i<4; i++) { chan[i].std.next(); if (chan[i].std.vol.had) { diff --git a/src/engine/platform/amiga.h b/src/engine/platform/amiga.h index 8fbe8fec8..07c44ee7c 100644 --- a/src/engine/platform/amiga.h +++ b/src/engine/platform/amiga.h @@ -88,7 +88,7 @@ class DivPlatformAmiga: public DivDispatch { void* getChanState(int chan); void reset(); void forceIns(); - void tick(); + void tick(bool sysTick=true); void muteChannel(int ch, bool mute); bool isStereo(); bool keyOffAffectsArp(int ch); diff --git a/src/engine/platform/arcade.cpp b/src/engine/platform/arcade.cpp index a7ab53ec0..0bcb3b990 100644 --- a/src/engine/platform/arcade.cpp +++ b/src/engine/platform/arcade.cpp @@ -219,7 +219,7 @@ inline int hScale(int note) { return ((note/12)<<4)+(noteMap[note%12]); } -void DivPlatformArcade::tick() { +void DivPlatformArcade::tick(bool sysTick) { for (int i=0; i<8; i++) { chan[i].std.next(); diff --git a/src/engine/platform/arcade.h b/src/engine/platform/arcade.h index a6ec82c94..41af49e33 100644 --- a/src/engine/platform/arcade.h +++ b/src/engine/platform/arcade.h @@ -107,7 +107,7 @@ class DivPlatformArcade: public DivDispatch { int getRegisterPoolSize(); void reset(); void forceIns(); - void tick(); + void tick(bool sysTick=true); void muteChannel(int ch, bool mute); void notifyInsChange(int ins); void setFlags(unsigned int flags); diff --git a/src/engine/platform/ay.cpp b/src/engine/platform/ay.cpp index e8a61b9ae..d3db4ba8b 100644 --- a/src/engine/platform/ay.cpp +++ b/src/engine/platform/ay.cpp @@ -172,7 +172,7 @@ void DivPlatformAY8910::updateOutSel(bool immediate) { } } -void DivPlatformAY8910::tick() { +void DivPlatformAY8910::tick(bool sysTick) { // PSG for (int i=0; i<3; i++) { chan[i].std.next(); diff --git a/src/engine/platform/ay.h b/src/engine/platform/ay.h index b1a3ea127..a78f21bc8 100644 --- a/src/engine/platform/ay.h +++ b/src/engine/platform/ay.h @@ -90,7 +90,7 @@ class DivPlatformAY8910: public DivDispatch { void flushWrites(); void reset(); void forceIns(); - void tick(); + void tick(bool sysTick=true); void muteChannel(int ch, bool mute); void setFlags(unsigned int flags); bool isStereo(); diff --git a/src/engine/platform/ay8930.cpp b/src/engine/platform/ay8930.cpp index 9f684a048..9fa39131c 100644 --- a/src/engine/platform/ay8930.cpp +++ b/src/engine/platform/ay8930.cpp @@ -187,7 +187,7 @@ const unsigned char regMode[3]={ 0x0d, 0x14, 0x15 }; -void DivPlatformAY8930::tick() { +void DivPlatformAY8930::tick(bool sysTick) { // PSG for (int i=0; i<3; i++) { chan[i].std.next(); diff --git a/src/engine/platform/ay8930.h b/src/engine/platform/ay8930.h index 6bd51bdcd..adbe0be87 100644 --- a/src/engine/platform/ay8930.h +++ b/src/engine/platform/ay8930.h @@ -78,7 +78,7 @@ class DivPlatformAY8930: public DivDispatch { int getRegisterPoolSize(); void reset(); void forceIns(); - void tick(); + void tick(bool sysTick=true); void muteChannel(int ch, bool mute); void setFlags(unsigned int flags); bool isStereo(); diff --git a/src/engine/platform/bubsyswsg.cpp b/src/engine/platform/bubsyswsg.cpp index 4a80bf414..91f50d461 100644 --- a/src/engine/platform/bubsyswsg.cpp +++ b/src/engine/platform/bubsyswsg.cpp @@ -81,7 +81,7 @@ void DivPlatformBubSysWSG::updateWave(int ch) { } } -void DivPlatformBubSysWSG::tick() { +void DivPlatformBubSysWSG::tick(bool sysTick) { for (int i=0; i<2; i++) { chan[i].std.next(); if (chan[i].std.vol.had) { diff --git a/src/engine/platform/bubsyswsg.h b/src/engine/platform/bubsyswsg.h index 6c0e2261e..2efcc4d98 100644 --- a/src/engine/platform/bubsyswsg.h +++ b/src/engine/platform/bubsyswsg.h @@ -67,7 +67,7 @@ class DivPlatformBubSysWSG: public DivDispatch { int getRegisterPoolDepth(); void reset(); void forceIns(); - void tick(); + void tick(bool sysTick=true); void muteChannel(int ch, bool mute); bool isStereo(); bool keyOffAffectsArp(int ch); diff --git a/src/engine/platform/c64.cpp b/src/engine/platform/c64.cpp index db5ab46a6..4c3cc32e4 100644 --- a/src/engine/platform/c64.cpp +++ b/src/engine/platform/c64.cpp @@ -122,7 +122,7 @@ void DivPlatformC64::updateFilter() { rWrite(0x18,(filtControl<<4)|vol); } -void DivPlatformC64::tick() { +void DivPlatformC64::tick(bool sysTick) { for (int i=0; i<3; i++) { chan[i].std.next(); if (chan[i].std.vol.had) { @@ -166,12 +166,14 @@ void DivPlatformC64::tick() { rWrite(i*7+2,chan[i].duty&0xff); rWrite(i*7+3,chan[i].duty>>8); } - if (chan[i].testWhen>0) { - if (--chan[i].testWhen<1) { - if (!chan[i].resetMask) { - rWrite(i*7+5,0); - rWrite(i*7+6,0); - rWrite(i*7+4,(chan[i].wave<<4)|8|(chan[i].ring<<2)|(chan[i].sync<<1)); + if (sysTick) { + if (chan[i].testWhen>0) { + if (--chan[i].testWhen<1) { + if (!chan[i].resetMask) { + rWrite(i*7+5,0); + rWrite(i*7+6,0); + rWrite(i*7+4,(chan[i].wave<<4)|8|(chan[i].ring<<2)|(chan[i].sync<<1)); + } } } } diff --git a/src/engine/platform/c64.h b/src/engine/platform/c64.h index e288940e5..f77e24b97 100644 --- a/src/engine/platform/c64.h +++ b/src/engine/platform/c64.h @@ -83,7 +83,7 @@ class DivPlatformC64: public DivDispatch { int getRegisterPoolSize(); void reset(); void forceIns(); - void tick(); + void tick(bool sysTick=true); void muteChannel(int ch, bool mute); void setFlags(unsigned int flags); void notifyInsChange(int ins); diff --git a/src/engine/platform/dummy.cpp b/src/engine/platform/dummy.cpp index 308b83722..e751e3b6f 100644 --- a/src/engine/platform/dummy.cpp +++ b/src/engine/platform/dummy.cpp @@ -38,10 +38,12 @@ void DivPlatformDummy::muteChannel(int ch, bool mute) { isMuted[ch]=mute; } -void DivPlatformDummy::tick() { +void DivPlatformDummy::tick(bool sysTick) { for (unsigned char i=0; isampleAudio( bufL + start, bufR + start, len ); } -void DivPlatformLynx::tick() { +void DivPlatformLynx::tick(bool sysTick) { for (int i=0; i<4; i++) { chan[i].std.next(); if (chan[i].std.vol.had) { diff --git a/src/engine/platform/lynx.h b/src/engine/platform/lynx.h index 536a874a8..61f6e67df 100644 --- a/src/engine/platform/lynx.h +++ b/src/engine/platform/lynx.h @@ -80,7 +80,7 @@ class DivPlatformLynx: public DivDispatch { int getRegisterPoolSize(); void reset(); void forceIns(); - void tick(); + void tick(bool sysTick=true); void muteChannel(int ch, bool mute); bool isStereo(); bool keyOffAffectsArp(int ch); diff --git a/src/engine/platform/mmc5.cpp b/src/engine/platform/mmc5.cpp index a9f9bb796..3c7f3dcab 100644 --- a/src/engine/platform/mmc5.cpp +++ b/src/engine/platform/mmc5.cpp @@ -95,7 +95,7 @@ void DivPlatformMMC5::acquire(short* bufL, short* bufR, size_t start, size_t len } } -void DivPlatformMMC5::tick() { +void DivPlatformMMC5::tick(bool sysTick) { for (int i=0; i<2; i++) { chan[i].std.next(); if (chan[i].std.vol.had) { diff --git a/src/engine/platform/mmc5.h b/src/engine/platform/mmc5.h index 6b364d5ad..02ca06e84 100644 --- a/src/engine/platform/mmc5.h +++ b/src/engine/platform/mmc5.h @@ -71,7 +71,7 @@ class DivPlatformMMC5: public DivDispatch { int getRegisterPoolSize(); void reset(); void forceIns(); - void tick(); + void tick(bool sysTick=true); void muteChannel(int ch, bool mute); bool keyOffAffectsArp(int ch); float getPostAmp(); diff --git a/src/engine/platform/n163.cpp b/src/engine/platform/n163.cpp index a66157d9e..6a1eb60e0 100644 --- a/src/engine/platform/n163.cpp +++ b/src/engine/platform/n163.cpp @@ -214,7 +214,7 @@ void DivPlatformN163::updateWaveCh(int ch) { } } -void DivPlatformN163::tick() { +void DivPlatformN163::tick(bool sysTick) { for (int i=0; i<=chanMax; i++) { chan[i].std.next(); if (chan[i].std.vol.had) { diff --git a/src/engine/platform/n163.h b/src/engine/platform/n163.h index 2f68d6c9d..38ebeace3 100644 --- a/src/engine/platform/n163.h +++ b/src/engine/platform/n163.h @@ -93,7 +93,7 @@ class DivPlatformN163: public DivDispatch { int getRegisterPoolSize(); void reset(); void forceIns(); - void tick(); + void tick(bool sysTick=true); void muteChannel(int ch, bool mute); void setFlags(unsigned int flags); void notifyWaveChange(int wave); diff --git a/src/engine/platform/nes.cpp b/src/engine/platform/nes.cpp index 4c65ce4f2..7e291446f 100644 --- a/src/engine/platform/nes.cpp +++ b/src/engine/platform/nes.cpp @@ -140,7 +140,7 @@ static unsigned char noiseTable[253]={ 15 }; -void DivPlatformNES::tick() { +void DivPlatformNES::tick(bool sysTick) { for (int i=0; i<4; i++) { chan[i].std.next(); if (chan[i].std.vol.had) { diff --git a/src/engine/platform/nes.h b/src/engine/platform/nes.h index 1dbb4411b..2cf2a8a15 100644 --- a/src/engine/platform/nes.h +++ b/src/engine/platform/nes.h @@ -72,7 +72,7 @@ class DivPlatformNES: public DivDispatch { int getRegisterPoolSize(); void reset(); void forceIns(); - void tick(); + void tick(bool sysTick=true); void muteChannel(int ch, bool mute); bool keyOffAffectsArp(int ch); float getPostAmp(); diff --git a/src/engine/platform/opl.cpp b/src/engine/platform/opl.cpp index add3815c5..e73ca41e4 100644 --- a/src/engine/platform/opl.cpp +++ b/src/engine/platform/opl.cpp @@ -228,7 +228,7 @@ void DivPlatformOPL::acquire(short* bufL, short* bufR, size_t start, size_t len) //} } -void DivPlatformOPL::tick() { +void DivPlatformOPL::tick(bool sysTick) { for (int i=0; i>4))/15)|(((vol*(pan&15))/15)<<4); } -void DivPlatformSAA1099::tick() { +void DivPlatformSAA1099::tick(bool sysTick) { for (int i=0; i<6; i++) { chan[i].std.next(); if (chan[i].std.vol.had) { diff --git a/src/engine/platform/saa.h b/src/engine/platform/saa.h index 8df44f616..0542ff513 100644 --- a/src/engine/platform/saa.h +++ b/src/engine/platform/saa.h @@ -90,7 +90,7 @@ class DivPlatformSAA1099: public DivDispatch { int getRegisterPoolSize(); void reset(); void forceIns(); - void tick(); + void tick(bool sysTick=true); void muteChannel(int ch, bool mute); void setCore(DivSAACores core); void setFlags(unsigned int flags); diff --git a/src/engine/platform/segapcm.cpp b/src/engine/platform/segapcm.cpp index b2a8b3db8..97ff900ae 100644 --- a/src/engine/platform/segapcm.cpp +++ b/src/engine/platform/segapcm.cpp @@ -76,7 +76,7 @@ void DivPlatformSegaPCM::acquire(short* bufL, short* bufR, size_t start, size_t } } -void DivPlatformSegaPCM::tick() { +void DivPlatformSegaPCM::tick(bool sysTick) { for (int i=0; i<16; i++) { chan[i].std.next(); diff --git a/src/engine/platform/segapcm.h b/src/engine/platform/segapcm.h index b3c84cd3b..4b05c160c 100644 --- a/src/engine/platform/segapcm.h +++ b/src/engine/platform/segapcm.h @@ -78,7 +78,7 @@ class DivPlatformSegaPCM: public DivDispatch { int getRegisterPoolSize(); void reset(); void forceIns(); - void tick(); + void tick(bool sysTick=true); void muteChannel(int ch, bool mute); void notifyInsChange(int ins); void setFlags(unsigned int flags); diff --git a/src/engine/platform/sms.cpp b/src/engine/platform/sms.cpp index a0f8abdda..acd2c0726 100644 --- a/src/engine/platform/sms.cpp +++ b/src/engine/platform/sms.cpp @@ -53,7 +53,7 @@ int DivPlatformSMS::acquireOne() { return v; } -void DivPlatformSMS::tick() { +void DivPlatformSMS::tick(bool sysTick) { for (int i=0; i<4; i++) { chan[i].std.next(); if (chan[i].std.vol.had) { diff --git a/src/engine/platform/sms.h b/src/engine/platform/sms.h index b49a5e276..0aeb9e81a 100644 --- a/src/engine/platform/sms.h +++ b/src/engine/platform/sms.h @@ -63,7 +63,7 @@ class DivPlatformSMS: public DivDispatch { void* getChanState(int chan); void reset(); void forceIns(); - void tick(); + void tick(bool sysTick=true); void muteChannel(int ch, bool mute); bool keyOffAffectsArp(int ch); bool keyOffAffectsPorta(int ch); diff --git a/src/engine/platform/swan.cpp b/src/engine/platform/swan.cpp index 30be897be..99d819b1f 100644 --- a/src/engine/platform/swan.cpp +++ b/src/engine/platform/swan.cpp @@ -141,7 +141,7 @@ void DivPlatformSwan::writeOutVol(int ch) { } } -void DivPlatformSwan::tick() { +void DivPlatformSwan::tick(bool sysTick) { unsigned char sndCtrl=(pcm?0x20:0)|(sweep?0x40:0)|((noise>0)?0x80:0); for (int i=0; i<4; i++) { chan[i].std.next(); diff --git a/src/engine/platform/swan.h b/src/engine/platform/swan.h index 610884b00..0f1643c53 100644 --- a/src/engine/platform/swan.h +++ b/src/engine/platform/swan.h @@ -77,7 +77,7 @@ class DivPlatformSwan: public DivDispatch { int getRegisterPoolSize(); void reset(); void forceIns(); - void tick(); + void tick(bool sysTick=true); void muteChannel(int ch, bool mute); void notifyWaveChange(int wave); void notifyInsDeletion(void* ins); diff --git a/src/engine/platform/tia.cpp b/src/engine/platform/tia.cpp index 474cd00e3..dc6c607fe 100644 --- a/src/engine/platform/tia.cpp +++ b/src/engine/platform/tia.cpp @@ -84,7 +84,7 @@ unsigned char DivPlatformTIA::dealWithFreq(unsigned char shape, int base, int pi return 0; } -void DivPlatformTIA::tick() { +void DivPlatformTIA::tick(bool sysTick) { for (int i=0; i<2; i++) { chan[i].std.next(); if (chan[i].std.vol.had) { diff --git a/src/engine/platform/tia.h b/src/engine/platform/tia.h index ea149ec34..3ec3c7f34 100644 --- a/src/engine/platform/tia.h +++ b/src/engine/platform/tia.h @@ -51,7 +51,7 @@ class DivPlatformTIA: public DivDispatch { int getRegisterPoolSize(); void reset(); void forceIns(); - void tick(); + void tick(bool sysTick=true); void muteChannel(int ch, bool mute); void setFlags(unsigned int flags); bool isStereo(); diff --git a/src/engine/platform/tx81z.cpp b/src/engine/platform/tx81z.cpp index 34b975dd0..6f71be755 100644 --- a/src/engine/platform/tx81z.cpp +++ b/src/engine/platform/tx81z.cpp @@ -183,7 +183,7 @@ inline int hScale(int note) { return ((note/12)<<4)+(noteMap[note%12]); } -void DivPlatformTX81Z::tick() { +void DivPlatformTX81Z::tick(bool sysTick) { for (int i=0; i<8; i++) { chan[i].std.next(); diff --git a/src/engine/platform/tx81z.h b/src/engine/platform/tx81z.h index 363d5c234..c61b4bfe7 100644 --- a/src/engine/platform/tx81z.h +++ b/src/engine/platform/tx81z.h @@ -102,7 +102,7 @@ class DivPlatformTX81Z: public DivDispatch { int getRegisterPoolSize(); void reset(); void forceIns(); - void tick(); + void tick(bool sysTick=true); void muteChannel(int ch, bool mute); void notifyInsChange(int ins); void setFlags(unsigned int flags); diff --git a/src/engine/platform/vera.cpp b/src/engine/platform/vera.cpp index 7e887be80..31d52569a 100644 --- a/src/engine/platform/vera.cpp +++ b/src/engine/platform/vera.cpp @@ -156,7 +156,7 @@ int DivPlatformVERA::calcNoteFreq(int ch, int note) { } } -void DivPlatformVERA::tick() { +void DivPlatformVERA::tick(bool sysTick) { for (int i=0; i<16; i++) { chan[i].std.next(); if (chan[i].std.vol.had) { diff --git a/src/engine/platform/vera.h b/src/engine/platform/vera.h index 2b47f99a7..ec6dcde11 100644 --- a/src/engine/platform/vera.h +++ b/src/engine/platform/vera.h @@ -63,7 +63,7 @@ class DivPlatformVERA: public DivDispatch { unsigned char* getRegisterPool(); int getRegisterPoolSize(); void reset(); - void tick(); + void tick(bool sysTick=true); void muteChannel(int ch, bool mute); void notifyInsDeletion(void* ins); float getPostAmp(); diff --git a/src/engine/platform/vic20.cpp b/src/engine/platform/vic20.cpp index b3062a630..7817cd839 100644 --- a/src/engine/platform/vic20.cpp +++ b/src/engine/platform/vic20.cpp @@ -91,7 +91,7 @@ void DivPlatformVIC20::writeOutVol(int ch) { } } -void DivPlatformVIC20::tick() { +void DivPlatformVIC20::tick(bool sysTick) { for (int i=0; i<4; i++) { chan[i].std.next(); if (chan[i].std.vol.had) { diff --git a/src/engine/platform/vic20.h b/src/engine/platform/vic20.h index 1c4d384b7..7948c5ef2 100644 --- a/src/engine/platform/vic20.h +++ b/src/engine/platform/vic20.h @@ -66,7 +66,7 @@ class DivPlatformVIC20: public DivDispatch { int getRegisterPoolSize(); void reset(); void forceIns(); - void tick(); + void tick(bool sysTick=true); void muteChannel(int ch, bool mute); void setFlags(unsigned int flags); void notifyInsDeletion(void* ins); diff --git a/src/engine/platform/vrc6.cpp b/src/engine/platform/vrc6.cpp index b126deb6e..048b42cdb 100644 --- a/src/engine/platform/vrc6.cpp +++ b/src/engine/platform/vrc6.cpp @@ -135,7 +135,7 @@ void DivPlatformVRC6::acquire(short* bufL, short* bufR, size_t start, size_t len } } -void DivPlatformVRC6::tick() { +void DivPlatformVRC6::tick(bool sysTick) { for (int i=0; i<3; i++) { // 16 for pulse; 14 for saw int CHIP_DIVIDER=(i==2)?14:16; diff --git a/src/engine/platform/vrc6.h b/src/engine/platform/vrc6.h index 05cd89415..d28265a47 100644 --- a/src/engine/platform/vrc6.h +++ b/src/engine/platform/vrc6.h @@ -82,7 +82,7 @@ class DivPlatformVRC6: public DivDispatch { int getRegisterPoolSize(); void reset(); void forceIns(); - void tick(); + void tick(bool sysTick=true); void muteChannel(int ch, bool mute); bool keyOffAffectsArp(int ch); void setFlags(unsigned int flags); diff --git a/src/engine/platform/x1_010.cpp b/src/engine/platform/x1_010.cpp index 1bbd2a809..df6682adb 100644 --- a/src/engine/platform/x1_010.cpp +++ b/src/engine/platform/x1_010.cpp @@ -336,7 +336,7 @@ void DivPlatformX1_010::updateEnvelope(int ch) { } } -void DivPlatformX1_010::tick() { +void DivPlatformX1_010::tick(bool sysTick) { for (int i=0; i<16; i++) { chan[i].std.next(); if (chan[i].std.vol.had) { diff --git a/src/engine/platform/x1_010.h b/src/engine/platform/x1_010.h index 2bfb78cc9..bee546473 100644 --- a/src/engine/platform/x1_010.h +++ b/src/engine/platform/x1_010.h @@ -126,7 +126,7 @@ class DivPlatformX1_010: public DivDispatch { int getRegisterPoolSize(); void reset(); void forceIns(); - void tick(); + void tick(bool sysTick=true); void muteChannel(int ch, bool mute); bool isStereo(); bool keyOffAffectsArp(int ch); diff --git a/src/engine/platform/ym2610.cpp b/src/engine/platform/ym2610.cpp index a1f5ea658..3d6c8ed29 100644 --- a/src/engine/platform/ym2610.cpp +++ b/src/engine/platform/ym2610.cpp @@ -365,9 +365,9 @@ void DivPlatformYM2610::acquire(short* bufL, short* bufR, size_t start, size_t l } } -void DivPlatformYM2610::tick() { +void DivPlatformYM2610::tick(bool sysTick) { // PSG - ay->tick(); + ay->tick(sysTick); ay->flushWrites(); for (DivRegWrite& i: ay->getRegisterWrites()) { immWrite(i.addr&15,i.val); diff --git a/src/engine/platform/ym2610.h b/src/engine/platform/ym2610.h index 2fd525b08..e75e24d79 100644 --- a/src/engine/platform/ym2610.h +++ b/src/engine/platform/ym2610.h @@ -116,7 +116,7 @@ class DivPlatformYM2610: public DivDispatch { int getRegisterPoolSize(); void reset(); void forceIns(); - void tick(); + void tick(bool sysTick=true); void muteChannel(int ch, bool mute); bool isStereo(); bool keyOffAffectsArp(int ch); diff --git a/src/engine/platform/ym2610b.cpp b/src/engine/platform/ym2610b.cpp index 130eb29a2..aeadeae47 100644 --- a/src/engine/platform/ym2610b.cpp +++ b/src/engine/platform/ym2610b.cpp @@ -429,9 +429,9 @@ void DivPlatformYM2610B::acquire(short* bufL, short* bufR, size_t start, size_t } } -void DivPlatformYM2610B::tick() { +void DivPlatformYM2610B::tick(bool sysTick) { // PSG - ay->tick(); + ay->tick(sysTick); ay->flushWrites(); for (DivRegWrite& i: ay->getRegisterWrites()) { immWrite(i.addr&15,i.val); diff --git a/src/engine/platform/ym2610b.h b/src/engine/platform/ym2610b.h index d6b616c50..6d46ecb26 100644 --- a/src/engine/platform/ym2610b.h +++ b/src/engine/platform/ym2610b.h @@ -107,7 +107,7 @@ class DivPlatformYM2610B: public DivDispatch { int getRegisterPoolSize(); void reset(); void forceIns(); - void tick(); + void tick(bool sysTick=true); void muteChannel(int ch, bool mute); bool isStereo(); bool keyOffAffectsArp(int ch); diff --git a/src/engine/platform/ym2610bext.cpp b/src/engine/platform/ym2610bext.cpp index 03e9f8699..496a308e0 100644 --- a/src/engine/platform/ym2610bext.cpp +++ b/src/engine/platform/ym2610bext.cpp @@ -212,7 +212,7 @@ static int opChanOffsH[4]={ 0xad, 0xae, 0xac, 0xa6 }; -void DivPlatformYM2610BExt::tick() { +void DivPlatformYM2610BExt::tick(bool sysTick) { if (extMode) { bool writeSomething=false; unsigned char writeMask=2; @@ -229,7 +229,7 @@ void DivPlatformYM2610BExt::tick() { } } - DivPlatformYM2610B::tick(); + DivPlatformYM2610B::tick(sysTick); bool writeNoteOn=false; unsigned char writeMask=2; diff --git a/src/engine/platform/ym2610bext.h b/src/engine/platform/ym2610bext.h index 25ca59196..e908916e3 100644 --- a/src/engine/platform/ym2610bext.h +++ b/src/engine/platform/ym2610bext.h @@ -41,7 +41,7 @@ class DivPlatformYM2610BExt: public DivPlatformYM2610B { void* getChanState(int chan); void reset(); void forceIns(); - void tick(); + void tick(bool sysTick=true); void muteChannel(int ch, bool mute); bool keyOffAffectsArp(int ch); void notifyInsChange(int ins); diff --git a/src/engine/platform/ym2610ext.cpp b/src/engine/platform/ym2610ext.cpp index 40294733d..dc4625080 100644 --- a/src/engine/platform/ym2610ext.cpp +++ b/src/engine/platform/ym2610ext.cpp @@ -212,7 +212,7 @@ static int opChanOffsH[4]={ 0xad, 0xae, 0xac, 0xa6 }; -void DivPlatformYM2610Ext::tick() { +void DivPlatformYM2610Ext::tick(bool sysTick) { if (extMode) { bool writeSomething=false; unsigned char writeMask=2; @@ -229,7 +229,7 @@ void DivPlatformYM2610Ext::tick() { } } - DivPlatformYM2610::tick(); + DivPlatformYM2610::tick(sysTick); bool writeNoteOn=false; unsigned char writeMask=2; diff --git a/src/engine/platform/ym2610ext.h b/src/engine/platform/ym2610ext.h index 37c9e328d..9fa44d0b1 100644 --- a/src/engine/platform/ym2610ext.h +++ b/src/engine/platform/ym2610ext.h @@ -41,7 +41,7 @@ class DivPlatformYM2610Ext: public DivPlatformYM2610 { void* getChanState(int chan); void reset(); void forceIns(); - void tick(); + void tick(bool sysTick=true); void muteChannel(int ch, bool mute); bool keyOffAffectsArp(int ch); void notifyInsChange(int ins); diff --git a/src/engine/playback.cpp b/src/engine/playback.cpp index fe42c73af..0f3789635 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -1638,7 +1638,7 @@ bool DivEngine::nextTick(bool noAccum) { firstTick=false; // system tick - for (int i=0; itick(); + for (int i=0; itick(subticks==tickMult); if (!freelance) { if (stepPlay!=1) { diff --git a/src/engine/waveSynth.cpp b/src/engine/waveSynth.cpp index 2b9946ee2..7bae30d9a 100644 --- a/src/engine/waveSynth.cpp +++ b/src/engine/waveSynth.cpp @@ -30,8 +30,11 @@ bool DivWaveSynth::activeChanged() { } bool DivWaveSynth::tick() { + if (--subDivCounter>0) return false; + bool updated=first; first=false; + subDivCounter=e->tickMult; if (!state.enabled) return updated; if (width<1) return false; @@ -167,6 +170,7 @@ void DivWaveSynth::init(DivInstrument* which, int w, int h, bool insChanged) { pos=0; stage=0; divCounter=1+state.rateDivider; + subDivCounter=0; first=true; changeWave1(state.wave1); diff --git a/src/engine/waveSynth.h b/src/engine/waveSynth.h index ecd6f8a86..f5c89e3ac 100644 --- a/src/engine/waveSynth.h +++ b/src/engine/waveSynth.h @@ -28,7 +28,7 @@ class DivEngine; class DivWaveSynth { DivEngine* e; DivInstrumentWaveSynth state; - int pos, stage, divCounter, width, height; + int pos, stage, divCounter, width, height, subDivCounter; bool first, activeChangedB; unsigned char wave1[256]; unsigned char wave2[256]; @@ -78,6 +78,7 @@ class DivWaveSynth { divCounter(0), width(32), height(31), + subDivCounter(0), first(false), activeChangedB(false) { memset(wave1,0,256); From 55d821de6e99eb574cfcdf96fa917b871cc2d0e2 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 15 Apr 2022 16:00:21 -0500 Subject: [PATCH 092/342] potential to-do for Windows --- src/main.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main.cpp b/src/main.cpp index f8c749f7a..8f41b10cb 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -239,6 +239,7 @@ void initParams() { params.push_back(TAParam("W","warranty",false,pWarranty,"","view warranty disclaimer.")); } +// TODO: CoInitializeEx on Windows? int main(int argc, char** argv) { initLog(); #if !(defined(__APPLE__) || defined(_WIN32)) From 98b9bd32b94b350b1fbca1bc558ddafb2b604f5b Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 15 Apr 2022 16:10:57 -0500 Subject: [PATCH 093/342] static_assert() on cmdName --- src/engine/playback.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/engine/playback.cpp b/src/engine/playback.cpp index 0f3789635..2b22abd24 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -40,7 +40,7 @@ const char* notes[12]={ }; // update this when adding new commands. -const char* cmdName[DIV_CMD_MAX]={ +const char* cmdName[]={ "NOTE_ON", "NOTE_OFF", "NOTE_OFF_ENV", @@ -144,6 +144,7 @@ const char* cmdName[DIV_CMD_MAX]={ "N163_WAVE_LOAD", "N163_WAVE_LOADPOS", "N163_WAVE_LOADLEN", + "N163_WAVE_LOADMODE", "N163_CHANNEL_LIMIT", "N163_GLOBAL_WAVE_LOAD", "N163_GLOBAL_WAVE_LOADPOS", @@ -153,6 +154,8 @@ const char* cmdName[DIV_CMD_MAX]={ "ALWAYS_SET_VOLUME" }; +static_assert((sizeof(cmdName)/sizeof(void*))==DIV_CMD_MAX,"update cmdName!"); + const char* formatNote(unsigned char note, unsigned char octave) { static char ret[4]; if (note==100) { From a8201fa535174df56e80d805101fc715e349ef1d Mon Sep 17 00:00:00 2001 From: James Alan Nguyen Date: Sat, 16 Apr 2022 09:34:12 +1000 Subject: [PATCH 094/342] Address review comments --- src/engine/fileOpsIns.cpp | 242 ++++++++++++++++++-------------------- 1 file changed, 117 insertions(+), 125 deletions(-) diff --git a/src/engine/fileOpsIns.cpp b/src/engine/fileOpsIns.cpp index 2391f2a1a..b1e5b3974 100644 --- a/src/engine/fileOpsIns.cpp +++ b/src/engine/fileOpsIns.cpp @@ -35,6 +35,61 @@ enum DivInsFormats { DIV_INSFORMAT_FF, }; +// Patch data structures + +// SBI and some other OPL containers +struct sbi_t { + uint8_t Mcharacteristics, + Ccharacteristics, + Mscaling_output, + Cscaling_output, + Meg_AD, + Ceg_AD, + Meg_SR, + Ceg_SR, + Mwave, + Cwave, + FeedConnect; +}; + +// Adlib Visual Composer BNK +struct bnkop_t { + uint8_t ksl, + multiple, + feedback, // op1 only + attack, + sustain, + eg, + decay, + releaseRate, + totalLevel, + am, + vib, + ksr, + con; // op1 only +}; +struct bnktimbre_t { + uint8_t mode, + percVoice; + bnkop_t op[2]; + uint8_t wave0, + wave1; +}; + +auto readSbiOpData = [](sbi_t& sbi, SafeReader& reader) { + sbi.Mcharacteristics = reader.readC(); + sbi.Ccharacteristics = reader.readC(); + sbi.Mscaling_output = reader.readC(); + sbi.Cscaling_output = reader.readC(); + sbi.Meg_AD = reader.readC(); + sbi.Ceg_AD = reader.readC(); + sbi.Meg_SR = reader.readC(); + sbi.Ceg_SR = reader.readC(); + sbi.Mwave = reader.readC(); + sbi.Cwave = reader.readC(); + sbi.FeedConnect = reader.readC(); +}; + void DivEngine::loadDMP(SafeReader& reader, std::vector& ret, String& stripPath) { DivInstrument* ins=new DivInstrument; // this is a ridiculous mess @@ -46,7 +101,7 @@ void DivEngine::loadDMP(SafeReader& reader, std::vector& ret, St logD(".dmp version %d",version); } catch (EndOfFileException& e) { lastError="premature end of file"; - logE("premature end of file!"); + logE(lastError.c_str()); delete ins; return; } @@ -105,7 +160,7 @@ void DivEngine::loadDMP(SafeReader& reader, std::vector& ret, St } } catch (EndOfFileException& e) { lastError="premature end of file"; - logE("premature end of file!"); + logE(lastError.c_str()); delete ins; return; } @@ -297,7 +352,7 @@ void DivEngine::loadDMP(SafeReader& reader, std::vector& ret, St } } catch (EndOfFileException& e) { lastError="premature end of file"; - logE("premature end of file!"); + logE(lastError.c_str()); delete ins; return; } @@ -332,7 +387,7 @@ void DivEngine::loadTFI(SafeReader& reader, std::vector& ret, St } } catch (EndOfFileException& e) { lastError="premature end of file"; - logE("premature end of file!"); + logE(lastError.c_str()); delete ins; return; } @@ -374,7 +429,7 @@ void DivEngine::loadVGI(SafeReader& reader, std::vector& ret, St } } catch (EndOfFileException& e) { lastError="premature end of file"; - logE("premature end of file!"); + logE(lastError.c_str()); delete ins; return; } @@ -400,56 +455,48 @@ void DivEngine::loadS3I(SafeReader& reader, std::vector& ret, St // skip reserved bytes reader.seek(3, SEEK_CUR); - // 12-byte opl value - uint8_t s3i_Mcharacteristics = reader.readC(); - uint8_t s3i_Ccharacteristics = reader.readC(); - uint8_t s3i_Mscaling_output = reader.readC(); - uint8_t s3i_Cscaling_output = reader.readC(); - uint8_t s3i_Meg_AD = reader.readC(); - uint8_t s3i_Ceg_AD = reader.readC(); - uint8_t s3i_Meg_SR = reader.readC(); - uint8_t s3i_Ceg_SR = reader.readC(); - uint8_t s3i_Mwave = reader.readC(); - uint8_t s3i_Cwave = reader.readC(); - uint8_t s3i_FeedConnect = reader.readC(); + // 12-byte opl value - identical to SBI format + sbi_t s3i; + + readSbiOpData(s3i, reader); DivInstrumentFM::Operator& opM = ins->fm.op[0]; DivInstrumentFM::Operator& opC = ins->fm.op[1]; ins->fm.ops = 2; - opM.mult = s3i_Mcharacteristics & 0xF; - opM.ksr = ((s3i_Mcharacteristics >> 4) & 0x1); - opM.sus = ((s3i_Mcharacteristics >> 5) & 0x1); - opM.vib = ((s3i_Mcharacteristics >> 6) & 0x1); - opM.am = ((s3i_Mcharacteristics >> 7) & 0x1); - opM.tl = s3i_Mscaling_output & 0x3F; - opM.ksl = ((s3i_Mscaling_output >> 6) & 0x3); - opM.ar = ((s3i_Meg_AD >> 4) & 0xF); - opM.dr = (s3i_Meg_AD & 0xF); - opM.rr = (s3i_Meg_SR & 0xF); - opM.sl = ((s3i_Meg_SR >> 4) & 0xF); - opM.ws = s3i_Mwave; + opM.mult = s3i.Mcharacteristics & 0xF; + opM.ksr = ((s3i.Mcharacteristics >> 4) & 0x1); + opM.sus = ((s3i.Mcharacteristics >> 5) & 0x1); + opM.vib = ((s3i.Mcharacteristics >> 6) & 0x1); + opM.am = ((s3i.Mcharacteristics >> 7) & 0x1); + opM.tl = s3i.Mscaling_output & 0x3F; + opM.ksl = ((s3i.Mscaling_output >> 6) & 0x3); + opM.ar = ((s3i.Meg_AD >> 4) & 0xF); + opM.dr = (s3i.Meg_AD & 0xF); + opM.rr = (s3i.Meg_SR & 0xF); + opM.sl = ((s3i.Meg_SR >> 4) & 0xF); + opM.ws = s3i.Mwave; - ins->fm.alg = (s3i_FeedConnect & 0x1); - ins->fm.fb = ((s3i_FeedConnect >> 1) & 0x7); + ins->fm.alg = (s3i.FeedConnect & 0x1); + ins->fm.fb = ((s3i.FeedConnect >> 1) & 0x7); - opC.mult = s3i_Ccharacteristics & 0xF; - opC.ksr = ((s3i_Ccharacteristics >> 4) & 0x1); - opC.sus = ((s3i_Ccharacteristics >> 5) & 0x1); - opC.vib = ((s3i_Ccharacteristics >> 6) & 0x1); - opC.am = ((s3i_Ccharacteristics >> 7) & 0x1); - opC.tl = s3i_Cscaling_output & 0x3F; - opC.ksl = ((s3i_Cscaling_output >> 6) & 0x3); - opC.ar = ((s3i_Ceg_AD >> 4) & 0xF); - opC.dr = (s3i_Ceg_AD & 0xF); - opC.rr = (s3i_Ceg_SR & 0xF); - opC.sl = ((s3i_Ceg_SR >> 4) & 0xF); - opC.ws = s3i_Cwave; + opC.mult = s3i.Ccharacteristics & 0xF; + opC.ksr = ((s3i.Ccharacteristics >> 4) & 0x1); + opC.sus = ((s3i.Ccharacteristics >> 5) & 0x1); + opC.vib = ((s3i.Ccharacteristics >> 6) & 0x1); + opC.am = ((s3i.Ccharacteristics >> 7) & 0x1); + opC.tl = s3i.Cscaling_output & 0x3F; + opC.ksl = ((s3i.Cscaling_output >> 6) & 0x3); + opC.ar = ((s3i.Ceg_AD >> 4) & 0xF); + opC.dr = (s3i.Ceg_AD & 0xF); + opC.rr = (s3i.Ceg_SR & 0xF); + opC.sl = ((s3i.Ceg_SR >> 4) & 0xF); + opC.ws = s3i.Cwave; // Skip more stuff we don't need reader.seek(21, SEEK_CUR); } else { lastError = "S3I PCM samples currently not supported."; - logE("S3I PCM samples currently not supported.\n"); + logE(lastError.c_str()); } ins->name = reader.readString(28); ins->name = (ins->name.length() == 0) ? stripPath : ins->name; @@ -457,12 +504,12 @@ void DivEngine::loadS3I(SafeReader& reader, std::vector& ret, St int s3i_signature = reader.readI(); if (s3i_signature != 0x49524353) { - lastError = "S3I signature invalid."; - logW("S3I signature invalid.\n"); + warnings = "S3I signature invalid."; + logW(lastError.c_str()); }; } catch (EndOfFileException& e) { lastError = "premature end of file"; - logE("premature end of file!"); + logE(lastError.c_str()); delete ins; return; } @@ -486,34 +533,6 @@ void DivEngine::loadSBI(SafeReader& reader, std::vector& ret, St String patchName = reader.readString(32); patchName = (patchName.length() == 0) ? stripPath : patchName; - typedef struct { - uint8_t Mcharacteristics, - Ccharacteristics, - Mscaling_output, - Cscaling_output, - Meg_AD, - Ceg_AD, - Meg_SR, - Ceg_SR, - Mwave, - Cwave, - FeedConnect; - } sbi_t; - - auto readSbiOpData = [](sbi_t& sbi, SafeReader& reader) { - sbi.Mcharacteristics = reader.readC(); - sbi.Ccharacteristics = reader.readC(); - sbi.Mscaling_output = reader.readC(); - sbi.Cscaling_output = reader.readC(); - sbi.Meg_AD = reader.readC(); - sbi.Ceg_AD = reader.readC(); - sbi.Meg_SR = reader.readC(); - sbi.Ceg_SR = reader.readC(); - sbi.Mwave = reader.readC(); - sbi.Cwave = reader.readC(); - sbi.FeedConnect = reader.readC(); - }; - auto writeOp = [](sbi_t& sbi, DivInstrumentFM::Operator& opM, DivInstrumentFM::Operator& opC) { opM.mult = sbi.Mcharacteristics & 0xF; opM.ksr = ((sbi.Mcharacteristics >> 4) & 0x1); @@ -611,49 +630,22 @@ void DivEngine::loadSBI(SafeReader& reader, std::vector& ret, St } catch (EndOfFileException& e) { lastError = "premature end of file"; - logE("premature end of file!"); + logE(lastError.c_str()); delete ins; } } void DivEngine::loadBNK(SafeReader& reader, std::vector& ret, String& stripPath) { - DivInstrument* insList[256]; - String instNames[256]; - memset(insList, 0, 256 * sizeof(void*)); + std::vector insList; + std::vector instNames; reader.seek(0, SEEK_SET); // First distinguish between GEMS BNK and Adlib BNK uint64_t header = reader.readL(); bool is_adlib = ((header>>8) == 0x2d42494c444100L); + bool is_failed = false; int readCount = 0; if (is_adlib) { - // Caveat: Technically Adlib BNK can hold up to 0xFFFF instruments, - // but Furnace only can handle up to 0xFF. - - typedef struct { - uint8_t ksl, - multiple, - feedback, // op1 only - attack, - sustain, - eg, - decay, - releaseRate, - totalLevel, - am, - vib, - ksr, - con; // op1 only - } bnkop_t; - - typedef struct { - uint8_t mode, - percVoice; - bnkop_t op[2]; - uint8_t wave0, - wave1; - } bnktimbre_t; - try { reader.seek(0x0c, SEEK_SET); uint32_t name_offset = reader.readI(); @@ -663,13 +655,8 @@ void DivEngine::loadBNK(SafeReader& reader, std::vector& ret, St // Seek to BNK patch names reader.seek(name_offset, SEEK_SET); while (reader.tell() < data_offset) { - if (readCount >= 256) { - lastError = "BNK exceeds 256 presets. Only first 256 will be imported."; - logW("BNK exceeds 256 presets. Only first 256 will be imported.\n"); - break; - } reader.seek(3, SEEK_CUR); - instNames[readCount] = reader.readString(9); + instNames.push_back(new String(reader.readString(9))); ++readCount; } @@ -677,9 +664,9 @@ void DivEngine::loadBNK(SafeReader& reader, std::vector& ret, St reader.seek(data_offset, SEEK_SET); // Read until EOF - for (int i = 0; i < readCount && i < 256; ++i) { + for (int i = 0; i < readCount; ++i) { bnktimbre_t timbre; - insList[i] = new DivInstrument; + insList.push_back(new DivInstrument); auto& ins = insList[i]; ins->type = DIV_INS_OPL; @@ -719,31 +706,36 @@ void DivEngine::loadBNK(SafeReader& reader, std::vector& ret, St ins->fm.op[0].ws = reader.readC(); ins->fm.op[1].ws = reader.readC(); - ins->name = instNames[i].length() > 0 ? instNames[i] : fmt::format("{0}[{1}]", stripPath, i); + ins->name = instNames[i]->length() > 0 ? (*instNames[i]) : fmt::format("{0}[{1}]", stripPath, i); } reader.seek(0, SEEK_END); } catch (EndOfFileException& e) { lastError = "premature end of file"; - logE("premature end of file!\n"); + logE(lastError.c_str()); for (int i = readCount; i >= 0; --i) { delete insList[i]; } - return; + is_failed = true; } } else { // assume GEMS BNK for now. - lastError = "GEMS BNK currently not supported.\n"; - logE("GEMS BNK currently not supported.\n"); + lastError = "GEMS BNK currently not supported."; + logE(lastError.c_str()); } - for (int i = 0; i < readCount; ++i) { - ret.push_back(insList[i]); + if (!is_failed) { + for (int i = 0; i < readCount; ++i) { + ret.push_back(insList[i]); + } + } + + for (auto& name : instNames) { + delete name; } } - void DivEngine::loadFF(SafeReader& reader, std::vector& ret, String& stripPath) { DivInstrument* insList[256]; memset(insList,0,256*sizeof(void*)); @@ -805,7 +797,7 @@ void DivEngine::loadFF(SafeReader& reader, std::vector& ret, Str } } catch (EndOfFileException& e) { lastError = "premature end of file"; - logE("premature end of file!\n"); + logE(lastError.c_str()); for (int i = readCount; i >= 0; --i) { delete insList[i]; } @@ -826,7 +818,7 @@ void DivEngine::loadOPM(SafeReader& reader, std::vector& ret, St } catch (EndOfFileException& e) { lastError="premature end of file"; - logE("premature end of file!"); + logE(lastError.c_str()); return; } } @@ -924,7 +916,7 @@ std::vector DivEngine::instrumentFromFile(const char* path) { } } catch (EndOfFileException& e) { lastError="premature end of file"; - logE("premature end of file!"); + logE(lastError.c_str()); delete ins; delete[] buf; return ret; From b48a2368bed445e11da1265600bf17f71a8456d2 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 15 Apr 2022 22:22:47 -0500 Subject: [PATCH 095/342] more low-latency mode work playSub() runs at normal tick rate --- src/engine/engine.cpp | 1 + src/engine/playback.cpp | 10 +++-- src/gui/gui.cpp | 91 +++++++++++++++++++---------------------- src/gui/gui.h | 1 + 4 files changed, 51 insertions(+), 52 deletions(-) diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 6257ff537..bb53fd2a3 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -853,6 +853,7 @@ void DivEngine::playSub(bool preserveDrift, int goalRow) { } if (!preserveDrift) { ticks=1; + subticks=1; } skipping=false; cmdStream.clear(); diff --git a/src/engine/playback.cpp b/src/engine/playback.cpp index 2b22abd24..bd8b2b8af 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -1476,15 +1476,17 @@ bool DivEngine::nextTick(bool noAccum) { bool ret=false; if (divider<10) divider=10; - if (lowLatency) { + if (lowLatency && !skipping) { tickMult=1000/divider; if (tickMult<1) tickMult=1; + } else { + tickMult=1; } cycles=got.rate*pow(2,MASTER_CLOCK_PREC)/(divider*tickMult); - clockDrift+=fmod(got.rate*pow(2,MASTER_CLOCK_PREC),(double)divider); - if (clockDrift>=divider) { - clockDrift-=divider; + clockDrift+=fmod(got.rate*pow(2,MASTER_CLOCK_PREC),(double)(divider*tickMult)); + if (clockDrift>=(divider*tickMult)) { + clockDrift-=(divider*tickMult); cycles++; } diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index ba83b8654..4fcd60ddd 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -1078,9 +1078,6 @@ void FurnaceGUI::keyDown(SDL_Event& ev) { if (edit) { noteInput(num,key); } - if (key!=100 && key!=101 && key!=102) { - previewNote(cursor.xCoarse,num); - } } catch (std::out_of_range& e) { } } else if (edit) { // value @@ -1184,22 +1181,8 @@ void FurnaceGUI::keyDown(SDL_Event& ev) { } // PER-WINDOW PREVIEW KEYS + // TODO: move this to new event handler switch (curWindow) { - case GUI_WINDOW_INS_EDIT: - case GUI_WINDOW_INS_LIST: - case GUI_WINDOW_EDIT_CONTROLS: - case GUI_WINDOW_SONG_INFO: - if (!ev.key.repeat) { - try { - int key=noteKeys.at(ev.key.keysym.scancode); - int num=12*curOctave+key; - if (key!=100 && key!=101 && key!=102) { - previewNote(cursor.xCoarse,num); - } - } catch (std::out_of_range& e) { - } - } - break; case GUI_WINDOW_SAMPLE_EDIT: case GUI_WINDOW_SAMPLE_LIST: if (!ev.key.repeat) { @@ -1238,19 +1221,7 @@ void FurnaceGUI::keyDown(SDL_Event& ev) { } void FurnaceGUI::keyUp(SDL_Event& ev) { - stopPreviewNote(ev.key.keysym.scancode,true); - if (wavePreviewOn) { - if (ev.key.keysym.scancode==wavePreviewKey) { - wavePreviewOn=false; - e->stopWavePreview(); - } - } - if (samplePreviewOn) { - if (ev.key.keysym.scancode==samplePreviewKey) { - samplePreviewOn=false; - e->stopSamplePreview(); - } - } + // nothing for now } bool dirExists(String what) { @@ -2021,7 +1992,47 @@ void FurnaceGUI::editOptions(bool topMenu) { } } +int _processEvent(void* instance, SDL_Event* event) { + return ((FurnaceGUI*)instance)->processEvent(event); +} + +int FurnaceGUI::processEvent(SDL_Event* ev) { + if (ev->type==SDL_KEYDOWN) { + if (!ev->key.repeat) { + try { + int key=noteKeys.at(ev->key.keysym.scancode); + int num=12*curOctave+key; + + if (num<-60) num=-60; // C-(-5) + if (num>119) num=119; // B-9 + + if (key!=100 && key!=101 && key!=102) { + previewNote(cursor.xCoarse,num); + } + } catch (std::out_of_range& e) { + } + } + } else if (ev->type==SDL_KEYUP) { + stopPreviewNote(ev->key.keysym.scancode,true); + if (wavePreviewOn) { + if (ev->key.keysym.scancode==wavePreviewKey) { + wavePreviewOn=false; + e->stopWavePreview(); + } + } + if (samplePreviewOn) { + if (ev->key.keysym.scancode==samplePreviewKey) { + samplePreviewOn=false; + e->stopSamplePreview(); + } + } + } + return 1; +} + bool FurnaceGUI::loop() { + SDL_SetEventFilter(_processEvent,this); + while (!quit) { SDL_Event ev; while (SDL_PollEvent(&ev)) { @@ -2131,23 +2142,7 @@ bool FurnaceGUI::loop() { } break; case SDL_KEYUP: - if (!ImGui::GetIO().WantCaptureKeyboard) { - keyUp(ev); - } else { - stopPreviewNote(ev.key.keysym.scancode,true); - if (wavePreviewOn) { - if (ev.key.keysym.scancode==wavePreviewKey) { - wavePreviewOn=false; - e->stopWavePreview(); - } - } - if (samplePreviewOn) { - if (ev.key.keysym.scancode==samplePreviewKey) { - samplePreviewOn=false; - e->stopSamplePreview(); - } - } - } + // for now break; case SDL_DROPFILE: if (ev.drop.file!=NULL) { diff --git a/src/gui/gui.h b/src/gui/gui.h index 67d084a47..5983057b5 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -1215,6 +1215,7 @@ class FurnaceGUI { void addScroll(int amount); void setFileName(String name); void runBackupThread(); + int processEvent(SDL_Event* ev); bool loop(); bool finish(); bool init(); From 6b294933bca61f11b955ab47a402fa1d946b6732 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 15 Apr 2022 22:27:44 -0500 Subject: [PATCH 096/342] VGM export: fix it under low-latency mode --- src/engine/engine.h | 2 +- src/engine/playback.cpp | 4 ++-- src/engine/vgmOps.cpp | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/engine/engine.h b/src/engine/engine.h index 6bcaa38c9..6c1129e9d 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -271,7 +271,7 @@ class DivEngine { void nextRow(); void performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write, int streamOff, double* loopTimer, double* loopFreq, int* loopSample, bool isSecond); // returns true if end of song. - bool nextTick(bool noAccum=false); + bool nextTick(bool noAccum=false, bool inhibitLowLat=false); bool perSystemEffect(int ch, unsigned char effect, unsigned char effectVal); bool perSystemPostEffect(int ch, unsigned char effect, unsigned char effectVal); void recalcChans(); diff --git a/src/engine/playback.cpp b/src/engine/playback.cpp index bd8b2b8af..38c7309d7 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -1472,11 +1472,11 @@ void DivEngine::nextRow() { firstTick=true; } -bool DivEngine::nextTick(bool noAccum) { +bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) { bool ret=false; if (divider<10) divider=10; - if (lowLatency && !skipping) { + if (lowLatency && !skipping && !inhibitLowLat) { tickMult=1000/divider; if (tickMult<1) tickMult=1; } else { diff --git a/src/engine/vgmOps.cpp b/src/engine/vgmOps.cpp index b20813489..efac0e482 100644 --- a/src/engine/vgmOps.cpp +++ b/src/engine/vgmOps.cpp @@ -1384,7 +1384,7 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version) { writeLoop=true; } } - if (nextTick() || !playing) { + if (nextTick(false,true) || !playing) { done=true; if (!loop) { for (int i=0; i Date: Fri, 15 Apr 2022 23:47:39 -0500 Subject: [PATCH 097/342] GUI: move all preview actions to callback --- src/gui/gui.cpp | 96 ++++++++++++++++++++++++------------------------- src/gui/gui.h | 2 +- 2 files changed, 48 insertions(+), 50 deletions(-) diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 4fcd60ddd..0488fd539 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -1179,45 +1179,6 @@ void FurnaceGUI::keyDown(SDL_Event& ev) { } } catch (std::out_of_range& e) { } - - // PER-WINDOW PREVIEW KEYS - // TODO: move this to new event handler - switch (curWindow) { - case GUI_WINDOW_SAMPLE_EDIT: - case GUI_WINDOW_SAMPLE_LIST: - if (!ev.key.repeat) { - try { - int key=noteKeys.at(ev.key.keysym.scancode); - int num=12*curOctave+key; - if (key!=100 && key!=101 && key!=102) { - e->previewSample(curSample,num); - samplePreviewOn=true; - samplePreviewKey=ev.key.keysym.scancode; - samplePreviewNote=num; - } - } catch (std::out_of_range& e) { - } - } - break; - case GUI_WINDOW_WAVE_LIST: - case GUI_WINDOW_WAVE_EDIT: - if (!ev.key.repeat) { - try { - int key=noteKeys.at(ev.key.keysym.scancode); - int num=12*curOctave+key; - if (key!=100 && key!=101 && key!=102) { - e->previewWave(curWave,num); - wavePreviewOn=true; - wavePreviewKey=ev.key.keysym.scancode; - wavePreviewNote=num; - } - } catch (std::out_of_range& e) { - } - } - break; - default: - break; - } } void FurnaceGUI::keyUp(SDL_Event& ev) { @@ -1998,18 +1959,52 @@ int _processEvent(void* instance, SDL_Event* event) { int FurnaceGUI::processEvent(SDL_Event* ev) { if (ev->type==SDL_KEYDOWN) { - if (!ev->key.repeat) { - try { - int key=noteKeys.at(ev->key.keysym.scancode); - int num=12*curOctave+key; + if (!ev->key.repeat && !wantCaptureKeyboard) { + switch (curWindow) { + case GUI_WINDOW_SAMPLE_EDIT: + case GUI_WINDOW_SAMPLE_LIST: + try { + int key=noteKeys.at(ev->key.keysym.scancode); + int num=12*curOctave+key; + if (key!=100 && key!=101 && key!=102) { + e->previewSample(curSample,num); + samplePreviewOn=true; + samplePreviewKey=ev->key.keysym.scancode; + samplePreviewNote=num; + } + } catch (std::out_of_range& e) { + } + break; + case GUI_WINDOW_WAVE_LIST: + case GUI_WINDOW_WAVE_EDIT: + try { + int key=noteKeys.at(ev->key.keysym.scancode); + int num=12*curOctave+key; + if (key!=100 && key!=101 && key!=102) { + e->previewWave(curWave,num); + wavePreviewOn=true; + wavePreviewKey=ev->key.keysym.scancode; + wavePreviewNote=num; + } + } catch (std::out_of_range& e) { + } + break; + case GUI_WINDOW_ORDERS: // ignore here + break; + default: + try { + int key=noteKeys.at(ev->key.keysym.scancode); + int num=12*curOctave+key; - if (num<-60) num=-60; // C-(-5) - if (num>119) num=119; // B-9 + if (num<-60) num=-60; // C-(-5) + if (num>119) num=119; // B-9 - if (key!=100 && key!=101 && key!=102) { - previewNote(cursor.xCoarse,num); - } - } catch (std::out_of_range& e) { + if (key!=100 && key!=101 && key!=102) { + previewNote(cursor.xCoarse,num); + } + } catch (std::out_of_range& e) { + } + break; } } } else if (ev->type==SDL_KEYUP) { @@ -2167,6 +2162,8 @@ bool FurnaceGUI::loop() { break; } } + + wantCaptureKeyboard=ImGui::GetIO().WantCaptureKeyboard; while (true) { midiLock.lock(); @@ -3417,6 +3414,7 @@ FurnaceGUI::FurnaceGUI(): displayError(false), displayExporting(false), vgmExportLoop(true), + wantCaptureKeyboard(false), displayNew(false), vgmExportVersion(0x171), curFileDialog(GUI_FILE_OPEN), diff --git a/src/gui/gui.h b/src/gui/gui.h index 5983057b5..eaab6df43 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -711,7 +711,7 @@ class FurnaceGUI { String mmlString[17]; String mmlStringW; - bool quit, warnQuit, willCommit, edit, modified, displayError, displayExporting, vgmExportLoop; + bool quit, warnQuit, willCommit, edit, modified, displayError, displayExporting, vgmExportLoop, wantCaptureKeyboard; bool displayNew; bool willExport[32]; int vgmExportVersion; From d0c76e020cef2d05011a7fb24176f014ece475fa Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 16 Apr 2022 00:10:52 -0500 Subject: [PATCH 098/342] GUI: more note preview fixes --- src/engine/engine.cpp | 14 ++++++++++++++ src/engine/engine.h | 1 + src/gui/doAction.cpp | 10 ++-------- src/gui/editControls.cpp | 20 ++++---------------- src/gui/gui.cpp | 2 +- 5 files changed, 22 insertions(+), 25 deletions(-) diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index bb53fd2a3..04029c46b 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -1997,6 +1997,20 @@ void DivEngine::autoNoteOff(int ch, int note, int vol) { } } +void DivEngine::autoNoteOffAll() { + if (!playing) { + reset(); + freelance=true; + playing=true; + } + for (int i=0; i7) { curOctave=7; } else { - for (size_t i=0; inoteOff(activeNotes[i].chan); - } - activeNotes.clear(); + e->autoNoteOffAll(); } break; case GUI_ACTION_OCTAVE_DOWN: if (--curOctave<-5) { curOctave=-5; } else { - for (size_t i=0; inoteOff(activeNotes[i].chan); - } - activeNotes.clear(); + e->autoNoteOffAll(); } break; case GUI_ACTION_INS_UP: diff --git a/src/gui/editControls.cpp b/src/gui/editControls.cpp index 5098a73de..722c38f2a 100644 --- a/src/gui/editControls.cpp +++ b/src/gui/editControls.cpp @@ -35,10 +35,7 @@ void FurnaceGUI::drawEditControls() { if (ImGui::InputInt("##Octave",&curOctave,1,1)) { if (curOctave>7) curOctave=7; if (curOctave<-5) curOctave=-5; - for (size_t i=0; inoteOff(activeNotes[i].chan); - } - activeNotes.clear(); + e->autoNoteOffAll(); if (settings.insFocusesPattern && !ImGui::IsItemActive() && patternOpen) { nextWindow=GUI_WINDOW_PATTERN; @@ -139,10 +136,7 @@ void FurnaceGUI::drawEditControls() { if (ImGui::InputInt("##Octave",&curOctave,1,1)) { if (curOctave>7) curOctave=7; if (curOctave<-5) curOctave=-5; - for (size_t i=0; inoteOff(activeNotes[i].chan); - } - activeNotes.clear(); + e->autoNoteOffAll(); if (settings.insFocusesPattern && !ImGui::IsItemActive() && patternOpen) { nextWindow=GUI_WINDOW_PATTERN; @@ -213,10 +207,7 @@ void FurnaceGUI::drawEditControls() { if (ImGui::InputInt("##Octave",&curOctave,0,0)) { if (curOctave>7) curOctave=7; if (curOctave<-5) curOctave=-5; - for (size_t i=0; inoteOff(activeNotes[i].chan); - } - activeNotes.clear(); + e->autoNoteOffAll(); if (settings.insFocusesPattern && !ImGui::IsItemActive() && patternOpen) { nextWindow=GUI_WINDOW_PATTERN; @@ -314,10 +305,7 @@ void FurnaceGUI::drawEditControls() { if (ImGui::InputInt("##Octave",&curOctave,1,1)) { if (curOctave>7) curOctave=7; if (curOctave<-5) curOctave=-5; - for (size_t i=0; inoteOff(activeNotes[i].chan); - } - activeNotes.clear(); + e->autoNoteOffAll(); if (settings.insFocusesPattern && !ImGui::IsItemActive() && patternOpen) { nextWindow=GUI_WINDOW_PATTERN; diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 0488fd539..b37726059 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -1959,7 +1959,7 @@ int _processEvent(void* instance, SDL_Event* event) { int FurnaceGUI::processEvent(SDL_Event* ev) { if (ev->type==SDL_KEYDOWN) { - if (!ev->key.repeat && !wantCaptureKeyboard) { + if (!ev->key.repeat && !wantCaptureKeyboard && (ev->key.keysym.mod&(~(KMOD_NUM|KMOD_CAPS|KMOD_SCROLL)))==0) { switch (curWindow) { case GUI_WINDOW_SAMPLE_EDIT: case GUI_WINDOW_SAMPLE_LIST: From af41e56acced0ae6a68d94aa5074fda7bd9849a8 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 16 Apr 2022 00:39:03 -0500 Subject: [PATCH 099/342] Lynx: set freqChanged to false! --- src/engine/platform/lynx.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/engine/platform/lynx.cpp b/src/engine/platform/lynx.cpp index 307d2059a..a62f0d458 100644 --- a/src/engine/platform/lynx.cpp +++ b/src/engine/platform/lynx.cpp @@ -184,8 +184,8 @@ void DivPlatformLynx::tick(bool sysTick) { } WRITE_CONTROL(i, (chan[i].fd.clockDivider|0x18|chan[i].duty.int_feedback7)); WRITE_BACKUP( i, chan[i].fd.backup ); - } - else if (chan[i].std.duty.had) { + chan[i].freqChanged=false; + } else if (chan[i].std.duty.had) { chan[i].duty = chan[i].std.duty.val; WRITE_FEEDBACK(i, chan[i].duty.feedback); WRITE_CONTROL(i, (chan[i].fd.clockDivider|0x18|chan[i].duty.int_feedback7)); From b4ac5c7e6a28169c1c2d30dda242ac28becf0e3d Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 16 Apr 2022 01:39:40 -0500 Subject: [PATCH 100/342] implement pitch macro --- src/engine/platform/arcade.cpp | 4 ++++ src/engine/platform/ay.cpp | 3 +++ src/engine/platform/ay8930.cpp | 3 +++ src/engine/platform/bubsyswsg.cpp | 3 +++ src/engine/platform/c64.cpp | 3 +++ src/engine/platform/fds.cpp | 19 ++++++------------- src/engine/platform/gb.cpp | 3 +++ src/engine/platform/genesis.cpp | 4 ++++ src/engine/platform/lynx.cpp | 4 ++++ src/engine/platform/mmc5.cpp | 3 +++ src/engine/platform/n163.cpp | 3 +++ src/engine/platform/nes.cpp | 3 +++ src/engine/platform/opl.cpp | 4 ++++ src/engine/platform/opll.cpp | 4 ++++ src/engine/platform/pce.cpp | 3 +++ src/engine/platform/pcspkr.cpp | 3 +++ src/engine/platform/pet.cpp | 3 +++ src/engine/platform/qsound.cpp | 3 +++ src/engine/platform/saa.cpp | 3 +++ src/engine/platform/segapcm.cpp | 4 ++++ src/engine/platform/sms.cpp | 3 +++ src/engine/platform/swan.cpp | 3 +++ src/engine/platform/tia.cpp | 3 +++ src/engine/platform/tx81z.cpp | 4 ++++ src/engine/platform/vera.cpp | 3 +++ src/engine/platform/vic20.cpp | 3 +++ src/engine/platform/vrc6.cpp | 3 +++ src/engine/platform/x1_010.cpp | 3 +++ src/engine/platform/ym2610.cpp | 4 ++++ src/engine/platform/ym2610b.cpp | 4 ++++ 30 files changed, 102 insertions(+), 13 deletions(-) diff --git a/src/engine/platform/arcade.cpp b/src/engine/platform/arcade.cpp index 0bcb3b990..2935bad06 100644 --- a/src/engine/platform/arcade.cpp +++ b/src/engine/platform/arcade.cpp @@ -264,6 +264,10 @@ void DivPlatformArcade::tick(bool sysTick) { rWrite(0x1b,chan[i].std.wave.val&3); } + if (chan[i].std.pitch.had) { + chan[i].freqChanged=true; + } + if (chan[i].std.phaseReset.had) { if (chan[i].std.phaseReset.val==1) { chan[i].keyOn=true; diff --git a/src/engine/platform/ay.cpp b/src/engine/platform/ay.cpp index d3db4ba8b..25f49e950 100644 --- a/src/engine/platform/ay.cpp +++ b/src/engine/platform/ay.cpp @@ -215,6 +215,9 @@ void DivPlatformAY8910::tick(bool sysTick) { rWrite(0x08+i,(chan[i].outVol&15)|((chan[i].psgMode&4)<<2)); } } + if (chan[i].std.pitch.had) { + chan[i].freqChanged=true; + } if (chan[i].std.phaseReset.had) { if (chan[i].std.phaseReset.val==1) { oldWrites[0x08+i]=-1; diff --git a/src/engine/platform/ay8930.cpp b/src/engine/platform/ay8930.cpp index 9fa39131c..e4fa30dc4 100644 --- a/src/engine/platform/ay8930.cpp +++ b/src/engine/platform/ay8930.cpp @@ -226,6 +226,9 @@ void DivPlatformAY8930::tick(bool sysTick) { rWrite(0x08+i,(chan[i].outVol&31)|((chan[i].psgMode&4)<<3)); } } + if (chan[i].std.pitch.had) { + chan[i].freqChanged=true; + } if (chan[i].std.phaseReset.had) { if (chan[i].std.phaseReset.val==1) { oldWrites[0x08+i]=-1; diff --git a/src/engine/platform/bubsyswsg.cpp b/src/engine/platform/bubsyswsg.cpp index 91f50d461..569e82f1c 100644 --- a/src/engine/platform/bubsyswsg.cpp +++ b/src/engine/platform/bubsyswsg.cpp @@ -110,6 +110,9 @@ void DivPlatformBubSysWSG::tick(bool sysTick) { if (!chan[i].keyOff) chan[i].keyOn=true; } } + if (chan[i].std.pitch.had) { + chan[i].freqChanged=true; + } if (chan[i].active) { if (chan[i].ws.tick()) { updateWave(i); diff --git a/src/engine/platform/c64.cpp b/src/engine/platform/c64.cpp index 4c3cc32e4..12ec3f5f9 100644 --- a/src/engine/platform/c64.cpp +++ b/src/engine/platform/c64.cpp @@ -181,6 +181,9 @@ void DivPlatformC64::tick(bool sysTick) { chan[i].wave=chan[i].std.wave.val; rWrite(i*7+4,(chan[i].wave<<4)|(chan[i].ring<<2)|(chan[i].sync<<1)|(int)(chan[i].active)); } + if (chan[i].std.pitch.had) { + chan[i].freqChanged=true; + } if (chan[i].std.ex1.had) { filtControl=chan[i].std.ex1.val&15; updateFilter(); diff --git a/src/engine/platform/fds.cpp b/src/engine/platform/fds.cpp index 16e24f130..c581c516e 100644 --- a/src/engine/platform/fds.cpp +++ b/src/engine/platform/fds.cpp @@ -107,21 +107,11 @@ void DivPlatformFDS::tick(bool sysTick) { rWrite(0x4080,0x80|chan[i].outVol); } if (chan[i].std.arp.had) { - if (i==3) { // noise + if (!chan[i].inPorta) { if (chan[i].std.arp.mode) { - chan[i].baseFreq=chan[i].std.arp.val; + chan[i].baseFreq=NOTE_FREQUENCY(chan[i].std.arp.val); } else { - chan[i].baseFreq=chan[i].note+chan[i].std.arp.val; - } - if (chan[i].baseFreq>255) chan[i].baseFreq=255; - if (chan[i].baseFreq<0) chan[i].baseFreq=0; - } else { - if (!chan[i].inPorta) { - if (chan[i].std.arp.mode) { - chan[i].baseFreq=NOTE_FREQUENCY(chan[i].std.arp.val); - } else { - chan[i].baseFreq=NOTE_FREQUENCY(chan[i].note+chan[i].std.arp.val); - } + chan[i].baseFreq=NOTE_FREQUENCY(chan[i].note+chan[i].std.arp.val); } } chan[i].freqChanged=true; @@ -155,6 +145,9 @@ void DivPlatformFDS::tick(bool sysTick) { //if (!chan[i].keyOff) chan[i].keyOn=true; } } + if (chan[i].std.pitch.had) { + chan[i].freqChanged=true; + } if (chan[i].active) { if (ws.tick()) { updateWave(); diff --git a/src/engine/platform/gb.cpp b/src/engine/platform/gb.cpp index c28478a3a..d3bb28e5e 100644 --- a/src/engine/platform/gb.cpp +++ b/src/engine/platform/gb.cpp @@ -192,6 +192,9 @@ void DivPlatformGB::tick(bool sysTick) { if (!chan[i].keyOff) chan[i].keyOn=true; } } + if (chan[i].std.pitch.had) { + chan[i].freqChanged=true; + } if (chan[i].std.phaseReset.had) { if (chan[i].std.phaseReset.val==1) { chan[i].keyOn=true; diff --git a/src/engine/platform/genesis.cpp b/src/engine/platform/genesis.cpp index bcca663ec..1385c62cf 100644 --- a/src/engine/platform/genesis.cpp +++ b/src/engine/platform/genesis.cpp @@ -258,6 +258,10 @@ void DivPlatformGenesis::tick(bool sysTick) { } } + if (chan[i].std.pitch.had) { + chan[i].freqChanged=true; + } + if (chan[i].std.phaseReset.had) { if (chan[i].std.phaseReset.val==1) { chan[i].keyOn=true; diff --git a/src/engine/platform/lynx.cpp b/src/engine/platform/lynx.cpp index a62f0d458..16d3f15ea 100644 --- a/src/engine/platform/lynx.cpp +++ b/src/engine/platform/lynx.cpp @@ -171,6 +171,10 @@ void DivPlatformLynx::tick(bool sysTick) { } } + if (chan[i].std.pitch.had) { + chan[i].freqChanged=true; + } + if (chan[i].freqChanged) { if (chan[i].lfsr >= 0) { WRITE_LFSR(i, (chan[i].lfsr&0xff)); diff --git a/src/engine/platform/mmc5.cpp b/src/engine/platform/mmc5.cpp index 3c7f3dcab..696ae6d21 100644 --- a/src/engine/platform/mmc5.cpp +++ b/src/engine/platform/mmc5.cpp @@ -123,6 +123,9 @@ void DivPlatformMMC5::tick(bool sysTick) { chan[i].duty=chan[i].std.duty.val; rWrite(0x5000+i*4,0x30|chan[i].outVol|((chan[i].duty&3)<<6)); } + if (chan[i].std.pitch.had) { + chan[i].freqChanged=true; + } if (chan[i].std.phaseReset.had) { if (chan[i].std.phaseReset.val==1) { chan[i].freqChanged=true; diff --git a/src/engine/platform/n163.cpp b/src/engine/platform/n163.cpp index 6a1eb60e0..a9634fa95 100644 --- a/src/engine/platform/n163.cpp +++ b/src/engine/platform/n163.cpp @@ -261,6 +261,9 @@ void DivPlatformN163::tick(bool sysTick) { } } } + if (chan[i].std.pitch.had) { + chan[i].freqChanged=true; + } if (chan[i].std.ex1.had) { if (chan[i].waveLen!=(chan[i].std.ex1.val&0xfc)) { chan[i].waveLen=chan[i].std.ex1.val&0xfc; diff --git a/src/engine/platform/nes.cpp b/src/engine/platform/nes.cpp index 7e291446f..e4bfbca9b 100644 --- a/src/engine/platform/nes.cpp +++ b/src/engine/platform/nes.cpp @@ -195,6 +195,9 @@ void DivPlatformNES::tick(bool sysTick) { chan[i].freqChanged=true; } } + if (chan[i].std.pitch.had) { + chan[i].freqChanged=true; + } if (chan[i].sweepChanged) { chan[i].sweepChanged=false; if (i==0) { diff --git a/src/engine/platform/opl.cpp b/src/engine/platform/opl.cpp index e73ca41e4..7050e0007 100644 --- a/src/engine/platform/opl.cpp +++ b/src/engine/platform/opl.cpp @@ -269,6 +269,10 @@ void DivPlatformOPL::tick(bool sysTick) { } } + if (chan[i].std.pitch.had) { + chan[i].freqChanged=true; + } + if (chan[i].std.phaseReset.had) { if (chan[i].std.phaseReset.val==1) { chan[i].keyOn=true; diff --git a/src/engine/platform/opll.cpp b/src/engine/platform/opll.cpp index cb6ceba83..d94d1e26d 100644 --- a/src/engine/platform/opll.cpp +++ b/src/engine/platform/opll.cpp @@ -145,6 +145,10 @@ void DivPlatformOPLL::tick(bool sysTick) { } } + if (chan[i].std.pitch.had) { + chan[i].freqChanged=true; + } + if (chan[i].std.phaseReset.had) { if (chan[i].std.phaseReset.val==1) { chan[i].keyOn=true; diff --git a/src/engine/platform/pce.cpp b/src/engine/platform/pce.cpp index cff5e534a..2c248db09 100644 --- a/src/engine/platform/pce.cpp +++ b/src/engine/platform/pce.cpp @@ -196,6 +196,9 @@ void DivPlatformPCE::tick(bool sysTick) { if (!chan[i].keyOff) chan[i].keyOn=true; } } + if (chan[i].std.pitch.had) { + chan[i].freqChanged=true; + } if (chan[i].active) { if (chan[i].ws.tick() || (chan[i].std.phaseReset.had && chan[i].std.phaseReset.val==1)) { updateWave(i); diff --git a/src/engine/platform/pcspkr.cpp b/src/engine/platform/pcspkr.cpp index 8e1705f50..e7062d213 100644 --- a/src/engine/platform/pcspkr.cpp +++ b/src/engine/platform/pcspkr.cpp @@ -186,6 +186,9 @@ void DivPlatformPCSpeaker::tick(bool sysTick) { chan[i].freqChanged=true; } } + if (chan[i].std.pitch.had) { + chan[i].freqChanged=true; + } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true)-1+chan[i].std.pitch.val; if (chan[i].freq<0) chan[i].freq=0; diff --git a/src/engine/platform/pet.cpp b/src/engine/platform/pet.cpp index 0fe4523db..47bb2e3d4 100644 --- a/src/engine/platform/pet.cpp +++ b/src/engine/platform/pet.cpp @@ -112,6 +112,9 @@ void DivPlatformPET::tick(bool sysTick) { rWrite(10,chan.wave); } } + if (chan.std.pitch.had) { + chan.freqChanged=true; + } if (chan.freqChanged || chan.keyOn || chan.keyOff) { chan.freq=parent->calcFreq(chan.baseFreq,chan.pitch,true)+chan.std.pitch.val; if (chan.freq>257) chan.freq=257; diff --git a/src/engine/platform/qsound.cpp b/src/engine/platform/qsound.cpp index 601ea0ea9..338870109 100644 --- a/src/engine/platform/qsound.cpp +++ b/src/engine/platform/qsound.cpp @@ -326,6 +326,9 @@ void DivPlatformQSound::tick(bool sysTick) { chan[i].freqChanged=true; } } + if (chan[i].std.pitch.had) { + chan[i].freqChanged=true; + } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { //DivInstrument* ins=parent->getIns(chan[i].ins); chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false)+chan[i].std.pitch.val; diff --git a/src/engine/platform/saa.cpp b/src/engine/platform/saa.cpp index ca91800c2..0e06c3388 100644 --- a/src/engine/platform/saa.cpp +++ b/src/engine/platform/saa.cpp @@ -166,6 +166,9 @@ void DivPlatformSAA1099::tick(bool sysTick) { if (chan[i].std.wave.had) { chan[i].psgMode=chan[i].std.wave.val&3; } + if (chan[i].std.pitch.had) { + chan[i].freqChanged=true; + } if (chan[i].std.ex1.had) { saaEnv[i/3]=chan[i].std.ex1.val; rWrite(0x18+(i/3),saaEnv[i/3]); diff --git a/src/engine/platform/segapcm.cpp b/src/engine/platform/segapcm.cpp index 97ff900ae..79d7179a2 100644 --- a/src/engine/platform/segapcm.cpp +++ b/src/engine/platform/segapcm.cpp @@ -99,6 +99,10 @@ void DivPlatformSegaPCM::tick(bool sysTick) { chan[i].freqChanged=true; } } + + if (chan[i].std.pitch.had) { + chan[i].freqChanged=true; + } /*if (chan[i].keyOn || chan[i].keyOff) { chan[i].keyOff=false; }*/ diff --git a/src/engine/platform/sms.cpp b/src/engine/platform/sms.cpp index acd2c0726..d755e84f6 100644 --- a/src/engine/platform/sms.cpp +++ b/src/engine/platform/sms.cpp @@ -98,6 +98,9 @@ void DivPlatformSMS::tick(bool sysTick) { } } } + if (chan[i].std.pitch.had) { + chan[i].freqChanged=true; + } } for (int i=0; i<3; i++) { if (chan[i].freqChanged) { diff --git a/src/engine/platform/swan.cpp b/src/engine/platform/swan.cpp index 99d819b1f..3b4a955ea 100644 --- a/src/engine/platform/swan.cpp +++ b/src/engine/platform/swan.cpp @@ -173,6 +173,9 @@ void DivPlatformSwan::tick(bool sysTick) { chan[i].ws.changeWave1(chan[i].wave); } } + if (chan[i].std.pitch.had) { + chan[i].freqChanged=true; + } if (chan[i].active) { sndCtrl|=(1<calcFreq(chan[i].baseFreq,chan[i].pitch,false,8)+chan[i].std.pitch.val; if (chan[i].freq>65535) chan[i].freq=65535; diff --git a/src/engine/platform/vic20.cpp b/src/engine/platform/vic20.cpp index 7817cd839..44aba9670 100644 --- a/src/engine/platform/vic20.cpp +++ b/src/engine/platform/vic20.cpp @@ -119,6 +119,9 @@ void DivPlatformVIC20::tick(bool sysTick) { chan[i].keyOn=true; } } + if (chan[i].std.pitch.had) { + chan[i].freqChanged=true; + } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true)+chan[i].std.pitch.val; if (i<3) { diff --git a/src/engine/platform/vrc6.cpp b/src/engine/platform/vrc6.cpp index 048b42cdb..d8b37d92f 100644 --- a/src/engine/platform/vrc6.cpp +++ b/src/engine/platform/vrc6.cpp @@ -178,6 +178,9 @@ void DivPlatformVRC6::tick(bool sysTick) { chWrite(i,0,(chan[i].outVol&0xf)|((chan[i].duty&7)<<4)); } } + if (chan[i].std.pitch.had) { + chan[i].freqChanged=true; + } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { if (i==2) { // sawtooth chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true)-1+chan[i].std.pitch.val; diff --git a/src/engine/platform/x1_010.cpp b/src/engine/platform/x1_010.cpp index df6682adb..229d8eab6 100644 --- a/src/engine/platform/x1_010.cpp +++ b/src/engine/platform/x1_010.cpp @@ -372,6 +372,9 @@ void DivPlatformX1_010::tick(bool sysTick) { } } } + if (chan[i].std.pitch.had) { + chan[i].freqChanged=true; + } if (chan[i].std.ex1.had) { bool nextEnable=(chan[i].std.ex1.val&1); if (nextEnable!=(chan[i].env.flag.envEnable)) { diff --git a/src/engine/platform/ym2610.cpp b/src/engine/platform/ym2610.cpp index 3d6c8ed29..6f4a1e46c 100644 --- a/src/engine/platform/ym2610.cpp +++ b/src/engine/platform/ym2610.cpp @@ -408,6 +408,10 @@ void DivPlatformYM2610::tick(bool sysTick) { } } + if (chan[i].std.pitch.had) { + chan[i].freqChanged=true; + } + if (chan[i].std.phaseReset.had) { if (chan[i].std.phaseReset.val==1) { chan[i].keyOn=true; diff --git a/src/engine/platform/ym2610b.cpp b/src/engine/platform/ym2610b.cpp index aeadeae47..04bc096a9 100644 --- a/src/engine/platform/ym2610b.cpp +++ b/src/engine/platform/ym2610b.cpp @@ -472,6 +472,10 @@ void DivPlatformYM2610B::tick(bool sysTick) { } } + if (chan[i].std.pitch.had) { + chan[i].freqChanged=true; + } + if (chan[i].std.phaseReset.had) { if (chan[i].std.phaseReset.val==1) { chan[i].keyOn=true; From d16d1260cb1abe2f918cbe15596d1e4e3e713f8c Mon Sep 17 00:00:00 2001 From: Natt Akuma Date: Sat, 16 Apr 2022 22:54:01 +0700 Subject: [PATCH 101/342] VGM: Support ROM data in the second chip --- src/engine/vgmOps.cpp | 110 ++++++++++++++++++++++++------------------ 1 file changed, 62 insertions(+), 48 deletions(-) diff --git a/src/engine/vgmOps.cpp b/src/engine/vgmOps.cpp index efac0e482..7817c060e 100644 --- a/src/engine/vgmOps.cpp +++ b/src/engine/vgmOps.cpp @@ -713,10 +713,10 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version) { bool writeDACSamples=false; bool writeNESSamples=false; bool writePCESamples=false; - bool writeADPCM=false; - bool writeSegaPCM=false; - bool writeX1010=false; - bool writeQSound=false; + int writeADPCM=0; + int writeSegaPCM=0; + int writeX1010=0; + int writeQSound=0; for (int i=0; ichipClock; willExport[i]=true; - writeX1010=true; + writeX1010=1; } else if (!(hasX1&0x40000000)) { isSecond[i]=true; willExport[i]=true; + writeX1010=2; hasX1|=0x40000000; howManyChips++; } @@ -821,10 +823,11 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version) { if (!hasOPNB) { hasOPNB=disCont[i].dispatch->chipClock; willExport[i]=true; - writeADPCM=true; + writeADPCM=1; } else if (!(hasOPNB&0x40000000)) { isSecond[i]=true; willExport[i]=true; + writeADPCM=2; hasOPNB|=0x40000000; howManyChips++; } @@ -926,10 +929,11 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version) { // not be able to handle the 64kb sample bank trick hasQSound=disCont[i].dispatch->chipClock; willExport[i]=true; - writeQSound=true; + writeQSound=1; } else if (!(hasQSound&0x40000000)) { isSecond[i]=true; willExport[i]=false; + writeQSound=2; addWarning("dual QSound is not supported by the VGM format"); } break; @@ -1200,7 +1204,7 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version) { } } - if (writeSegaPCM) { + if (writeSegaPCM>0) { unsigned char* pcmMem=new unsigned char[16777216]; size_t memPos=0; @@ -1232,60 +1236,70 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version) { if (memPos>=16777216) break; } - w->writeC(0x67); - w->writeC(0x66); - w->writeC(0x80); - w->writeI(memPos+8); - w->writeI(memPos); - w->writeI(0); - w->write(pcmMem,memPos); + for (int i=0; iwriteC(0x67); + w->writeC(0x66); + w->writeC(0x80); + w->writeI((memPos+8)|(i*0x80000000)); + w->writeI(memPos); + w->writeI(0); + w->write(pcmMem,memPos); + } delete[] pcmMem; } - if (writeADPCM && adpcmAMemLen>0) { - w->writeC(0x67); - w->writeC(0x66); - w->writeC(0x82); - w->writeI(adpcmAMemLen+8); - w->writeI(adpcmAMemLen); - w->writeI(0); - w->write(adpcmAMem,adpcmAMemLen); + if (adpcmAMemLen>0) { + for (int i=0; iwriteC(0x67); + w->writeC(0x66); + w->writeC(0x82); + w->writeI((adpcmAMemLen+8)|(i*0x80000000)); + w->writeI(adpcmAMemLen); + w->writeI(0); + w->write(adpcmAMem,adpcmAMemLen); + } } - if (writeADPCM && adpcmBMemLen>0) { - w->writeC(0x67); - w->writeC(0x66); - w->writeC(0x83); - w->writeI(adpcmBMemLen+8); - w->writeI(adpcmBMemLen); - w->writeI(0); - w->write(adpcmBMem,adpcmBMemLen); + if (adpcmBMemLen>0) { + for (int i=0; iwriteC(0x67); + w->writeC(0x66); + w->writeC(0x83); + w->writeI((adpcmBMemLen+8)|(i*0x80000000)); + w->writeI(adpcmBMemLen); + w->writeI(0); + w->write(adpcmBMem,adpcmBMemLen); + } } - if (writeQSound && qsoundMemLen>0) { + if (qsoundMemLen>0) { // always write a whole bank unsigned int blockSize=(qsoundMemLen+0xffff)&(~0xffff); if (blockSize > 0x1000000) { blockSize = 0x1000000; } - w->writeC(0x67); - w->writeC(0x66); - w->writeC(0x8F); - w->writeI(blockSize+8); - w->writeI(0x1000000); - w->writeI(0); - w->write(qsoundMem,blockSize); + for (int i=0; iwriteC(0x67); + w->writeC(0x66); + w->writeC(0x8F); + w->writeI((blockSize+8)|(i*0x80000000)); + w->writeI(0x1000000); + w->writeI(0); + w->write(qsoundMem,blockSize); + } } - if (writeX1010 && x1_010MemLen>0) { - w->writeC(0x67); - w->writeC(0x66); - w->writeC(0x91); - w->writeI(x1_010MemLen+8); - w->writeI(x1_010MemLen); - w->writeI(0); - w->write(x1_010Mem,x1_010MemLen); + if (x1_010MemLen>0) { + for (int i=0; iwriteC(0x67); + w->writeC(0x66); + w->writeC(0x91); + w->writeI((x1_010MemLen+8)|(i*0x80000000)); + w->writeI(x1_010MemLen); + w->writeI(0); + w->write(x1_010Mem,x1_010MemLen); + } } // initialize streams From 330171edc31dc9d7c0a82e2e9c44ae025e4275a7 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 16 Apr 2022 13:13:19 -0500 Subject: [PATCH 102/342] GUI: add "note preview behavior" setting --- TODO.md | 5 +++++ src/gui/gui.cpp | 8 ++++++++ src/gui/gui.h | 2 ++ src/gui/settings.cpp | 17 +++++++++++++++++ 4 files changed, 32 insertions(+) diff --git a/TODO.md b/TODO.md index d9817937e..04b724e0a 100644 --- a/TODO.md +++ b/TODO.md @@ -1,7 +1,11 @@ # to-do for 0.6pre1 - panning macro + - single macro for hard-panned chips + - two macros for soft-panned ones - pitch macro + - relative mode + - test - piano/input pad - note input via piano - input pad @@ -43,3 +47,4 @@ - settings: OK/Cancel buttons should be always visible - Apply button in settings - better FM chip names (number and codename) +- find and replace diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index b37726059..44de597bd 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -1960,6 +1960,7 @@ int _processEvent(void* instance, SDL_Event* event) { int FurnaceGUI::processEvent(SDL_Event* ev) { if (ev->type==SDL_KEYDOWN) { if (!ev->key.repeat && !wantCaptureKeyboard && (ev->key.keysym.mod&(~(KMOD_NUM|KMOD_CAPS|KMOD_SCROLL)))==0) { + if (settings.notePreviewBehavior==0) return 1; switch (curWindow) { case GUI_WINDOW_SAMPLE_EDIT: case GUI_WINDOW_SAMPLE_LIST: @@ -1991,6 +1992,13 @@ int FurnaceGUI::processEvent(SDL_Event* ev) { break; case GUI_WINDOW_ORDERS: // ignore here break; + case GUI_WINDOW_PATTERN: + if (settings.notePreviewBehavior==1) { + if (cursor.xFine!=0) break; + } else if (settings.notePreviewBehavior==2) { + if (edit && cursor.xFine!=0) break; + } + // fall-through default: try { int key=noteKeys.at(ev->key.keysym.scancode); diff --git a/src/gui/gui.h b/src/gui/gui.h index eaab6df43..447708b12 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -819,6 +819,7 @@ class FurnaceGUI { int oplStandardWaveNames; int cursorMoveNoScroll; int lowLatency; + int notePreviewBehavior; unsigned int maxUndoSteps; String mainFontPath; String patFontPath; @@ -891,6 +892,7 @@ class FurnaceGUI { oplStandardWaveNames(0), cursorMoveNoScroll(0), lowLatency(0), + notePreviewBehavior(1), maxUndoSteps(100), mainFontPath(""), patFontPath(""), diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index f4d62a81d..5bf76160e 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -301,6 +301,20 @@ void FurnaceGUI::drawSettings() { settings.sysFileDialog=sysFileDialogB; } + ImGui::Text("Note preview behavioe:"); + if (ImGui::RadioButton("Never##npb0",settings.notePreviewBehavior==0)) { + settings.notePreviewBehavior=0; + } + if (ImGui::RadioButton("When cursor is in Note column##npb1",settings.notePreviewBehavior==1)) { + settings.notePreviewBehavior=1; + } + if (ImGui::RadioButton("When cursor is in Note column or not in edit mode##npb2",settings.notePreviewBehavior==2)) { + settings.notePreviewBehavior=2; + } + if (ImGui::RadioButton("Always##npb3",settings.notePreviewBehavior==3)) { + settings.notePreviewBehavior=3; + } + ImGui::Text("Wrap pattern cursor horizontally:"); if (ImGui::RadioButton("No##wrapH0",settings.wrapHorizontal==0)) { settings.wrapHorizontal=0; @@ -1609,6 +1623,7 @@ void FurnaceGUI::syncSettings() { settings.oplStandardWaveNames=e->getConfInt("oplStandardWaveNames",0); settings.cursorMoveNoScroll=e->getConfInt("cursorMoveNoScroll",0); settings.lowLatency=e->getConfInt("lowLatency",0); + settings.notePreviewBehavior=e->getConfInt("notePreviewBehavior",1); clampSetting(settings.mainFontSize,2,96); clampSetting(settings.patFontSize,2,96); @@ -1670,6 +1685,7 @@ void FurnaceGUI::syncSettings() { clampSetting(settings.oplStandardWaveNames,0,1); clampSetting(settings.cursorMoveNoScroll,0,1); clampSetting(settings.lowLatency,0,1); + clampSetting(settings.notePreviewBehavior,0,3); // keybinds for (int i=0; isetConf("oplStandardWaveNames",settings.oplStandardWaveNames); e->setConf("cursorMoveNoScroll",settings.cursorMoveNoScroll); e->setConf("lowLatency",settings.lowLatency); + e->setConf("notePreviewBehavior",settings.notePreviewBehavior); // colors for (int i=0; i Date: Sat, 16 Apr 2022 14:12:55 -0500 Subject: [PATCH 103/342] use nightly.link for builds --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index f8ea1aec5..1eac106d2 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,8 @@ this is a multi-system chiptune tracker. check out the [Releases](https://github.com/tildearrow/furnace/releases) page. available for Windows, macOS and Linux (AppImage). +[see here](https://nightly.link/tildearrow/furnace/workflows/build/master) for unstable developer builds. + ## features - supports the following systems: @@ -64,6 +66,8 @@ some people have provided packages for Unix/Unix-like distributions. here's a li [![Build furnace](https://github.com/tildearrow/furnace/actions/workflows/build.yml/badge.svg)](https://github.com/tildearrow/furnace/actions/workflows/build.yml) +if you can't download these artifacts (because GitHub requires you to be logged in), [go here](https://nightly.link/tildearrow/furnace/workflows/build/master) instead. + **NOTE: do not download the project's source as a .zip or .tar.gz as these do not include the project's submodules which are necessary to proceed with building. please instead use Git as shown below.** ## dependencies From 8669e2cddcdfa3b4f45f5479c6ea0e41c9e54b02 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 16 Apr 2022 17:24:40 -0500 Subject: [PATCH 104/342] GUI: a better look for panning macro --- src/gui/insEdit.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index 50f93de2e..b54ccd136 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -175,6 +175,10 @@ const char* n163UpdateBits[8]={ "now", "every waveform changed", NULL }; +const char* panBits[3]={ + "left", "right", NULL +}; + const char* oneBit[2]={ "on", NULL }; @@ -2717,8 +2721,10 @@ void FurnaceGUI::drawInsEdit() { if (ins->type==DIV_INS_SAA1099) ex1Max=8; int panMax=0; + bool panSingle=false; if (ins->type==DIV_INS_FM || ins->type==DIV_INS_OPL || ins->type==DIV_INS_GB || ins->type==DIV_INS_OPZ || ins->type==DIV_INS_VERA) { panMax=1; + panSingle=true; } if (ins->type==DIV_INS_AMIGA) { panMax=127; @@ -2746,8 +2752,12 @@ void FurnaceGUI::drawInsEdit() { NORMAL_MACRO(ins->std.waveMacro,0,waveMax,"wave",waveLabel,(bitMode && ins->type!=DIV_INS_PET)?64:160,ins->std.waveMacro.open,bitMode,waveNames,false,NULL,0,0,0,((ins->type==DIV_INS_AY || ins->type==DIV_INS_AY8930)?1:0),false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_WAVE],mmlString[3],0,waveMax,NULL,false); } if (panMax>0) { - NORMAL_MACRO(ins->std.panLMacro,0,panMax,"panL","Panning (left)",(31+panMax),ins->std.panLMacro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[13],0,panMax,NULL,false); - NORMAL_MACRO(ins->std.panRMacro,0,panMax,"panR","Panning (right)",(31+panMax),ins->std.panRMacro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[14],0,panMax,NULL,false); + if (panSingle) { + NORMAL_MACRO(ins->std.panLMacro,0,2,"panL","Panning",32,ins->std.panLMacro.open,true,panBits,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[13],0,panMax,NULL,false); + } else { + NORMAL_MACRO(ins->std.panLMacro,0,panMax,"panL","Panning (left)",(31+panMax),ins->std.panLMacro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[13],0,panMax,NULL,false); + NORMAL_MACRO(ins->std.panRMacro,0,panMax,"panR","Panning (right)",(31+panMax),ins->std.panRMacro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[14],0,panMax,NULL,false); + } } NORMAL_MACRO(ins->std.pitchMacro,pitchMacroScroll,pitchMacroScroll+160,"pitch","Pitch",160,ins->std.pitchMacro.open,false,NULL,true,&pitchMacroScroll,-2048,2047,0,0,true,1,macroAbsoluteMode,uiColors[GUI_COLOR_MACRO_PITCH],mmlString[15],-2048,2047,NULL,!ins->std.pitchMacro.mode); if (ins->type==DIV_INS_FM || From dd8df45519854761dd702e5c35722cdc8420545a Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 16 Apr 2022 18:01:12 -0500 Subject: [PATCH 105/342] update format.md with more clarifications --- papers/format.md | 58 ++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 49 insertions(+), 9 deletions(-) diff --git a/papers/format.md b/papers/format.md index 2f4037a06..7775e9ea3 100644 --- a/papers/format.md +++ b/papers/format.md @@ -274,6 +274,19 @@ size | description # instrument +notes: + +- the entire instrument is stored, regardless of instrument type. +- the macro range varies depending on the instrument type. +- "macro open" indicates whether the macro is collapsed or not in the instrument editor. +- FM operator order is: + - 1/3/2/4 (internal order) for OPN, OPM, OPZ and OPL 4-op + - 1/2/?/? (? = unused) for OPL 2-op and OPLL +- meaning of extended macros varies depending on instrument type. +- meaning of panning macros varies depending on instrument type: + - for hard-panned chips (e.g. FM and Game Boy): left panning is 2-bit panning macro (left/right) + - otherwise both left and right panning macros are used + ``` size | description -----|------------------------------------ @@ -282,17 +295,39 @@ size | description 2 | format version (see header) 1 | instrument type | - 0: standard - | - 1: FM + | - 1: FM (OPM/OPN) | - 2: Game Boy | - 3: C64 | - 4: Amiga/sample + | - 5: PC Engine + | - 6: AY-3-8910 + | - 7: AY8930 + | - 8: TIA + | - 9: SAA1099 + | - 10: VIC + | - 11: PET + | - 12: VRC6 + | - 13: OPLL + | - 14: OPL + | - 15: FDS + | - 16: Virtual Boy + | - 17: Namco 163 + | - 18: SCC + | - 19: OPZ + | - 20: POKEY + | - 21: PC Speaker + | - 22: WonderSwan + | - 23: Lynx + | - 24: VERA + | - 25: X1-010 + | - 26: VRC6 (saw) 1 | reserved STR | instrument name --- | **FM instrument data** - 1 | alg + 1 | alg (SUS on OPLL) 1 | feedback - 1 | fms - 1 | ams + 1 | fms (DC on OPLL) + 1 | ams (DM on OPLL) 1 | operator count | - this is either 2 or 4, and is ignored on non-OPL systems. | - always read 4 ops regardless of this value. @@ -314,10 +349,12 @@ size | description 1 | dt 1 | d2r 1 | ssgEnv - 1 | dam - 1 | dvb - 1 | egt - 1 | ksl + | - bit 4: on (EG-S on OPLL) + | - bit 0-3: envelope type + 1 | dam (for YMU759 compat; REV on OPZ) + 1 | dvb (for YMU759 compat; FINE on OPZ) + 1 | egt (for YMU759 compat; FixedFreq on OPZ) + 1 | ksl (EGShift on OPZ) 1 | sus 1 | vib 1 | ws @@ -690,15 +727,18 @@ size | description | - 10: A# | - 11: B | - 12: C (of next octave) + | - this is actually a leftover of the .dmf format. | - 100: note off | - 100: note release | - 100: macro release | - octave | - this is an signed char stored in a short. | - therefore octave value 255 is actually octave -1. + | - yep, another leftover of the .dmf format... | - instrument | - volume - | - effect and effect data... + | - effect and effect data (× effect columns) + | - for note/octave, if both values are 0 then it means empty. | - for instrument, volume, effect and effect data, a value of -1 means empty. STR | pattern name (>=51) ``` From ef6e63239c5651b5386651dd14d98f4829e2388f Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 16 Apr 2022 18:35:25 -0500 Subject: [PATCH 106/342] GUI: introduce power-saving mode --- src/gui/gui.cpp | 9 +++++++++ src/gui/gui.h | 4 ++++ src/gui/osc.cpp | 9 ++++++++- src/gui/pattern.cpp | 1 + src/gui/settings.cpp | 20 +++++++++++++++++++- 5 files changed, 41 insertions(+), 2 deletions(-) diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 44de597bd..b2d1e5c94 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -2038,7 +2038,15 @@ bool FurnaceGUI::loop() { while (!quit) { SDL_Event ev; + if (e->isPlaying()) { + WAKE_UP; + } + if (--drawHalt<=0) { + drawHalt=0; + if (settings.powerSave) SDL_WaitEventTimeout(NULL,500); + } while (SDL_PollEvent(&ev)) { + WAKE_UP; ImGui_ImplSDL2_ProcessEvent(&ev); switch (ev.type) { case SDL_MOUSEMOTION: { @@ -3425,6 +3433,7 @@ FurnaceGUI::FurnaceGUI(): wantCaptureKeyboard(false), displayNew(false), vgmExportVersion(0x171), + drawHalt(10), curFileDialog(GUI_FILE_OPEN), warnAction(GUI_WARN_OPEN), postWarnAction(GUI_WARN_GENERIC), diff --git a/src/gui/gui.h b/src/gui/gui.h index 447708b12..b6613e9df 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -41,6 +41,7 @@ #define unimportant(x) if (x) {handleUnimportant} #define MARK_MODIFIED modified=true; +#define WAKE_UP drawHalt=16; #define TOGGLE_COLOR(x) ((x)?uiColors[GUI_COLOR_TOGGLE_ON]:uiColors[GUI_COLOR_TOGGLE_OFF]) @@ -715,6 +716,7 @@ class FurnaceGUI { bool displayNew; bool willExport[32]; int vgmExportVersion; + int drawHalt; FurnaceGUIFileDialogs curFileDialog; FurnaceGUIWarnings warnAction; @@ -820,6 +822,7 @@ class FurnaceGUI { int cursorMoveNoScroll; int lowLatency; int notePreviewBehavior; + int powerSave; unsigned int maxUndoSteps; String mainFontPath; String patFontPath; @@ -893,6 +896,7 @@ class FurnaceGUI { cursorMoveNoScroll(0), lowLatency(0), notePreviewBehavior(1), + powerSave(1), maxUndoSteps(100), mainFontPath(""), patFontPath(""), diff --git a/src/gui/osc.cpp b/src/gui/osc.cpp index 266dfa3bf..030a84e9c 100644 --- a/src/gui/osc.cpp +++ b/src/gui/osc.cpp @@ -49,12 +49,19 @@ void FurnaceGUI::readOsc() { for (int i=0; i<512; i++) { int pos=(readPos+(i*total/512))&0x7fff; oscValues[i]=(e->oscBuf[0][pos]+e->oscBuf[1][pos])*0.5f; + if (oscValues[i]>0.001f || oscValues[i]<-0.001f) { + WAKE_UP; + } } float peakDecay=0.05f*60.0f*ImGui::GetIO().DeltaTime; for (int i=0; i<2; i++) { peak[i]*=1.0-peakDecay; - if (peak[i]<0.0001) peak[i]=0.0; + if (peak[i]<0.0001) { + peak[i]=0.0; + } else { + WAKE_UP; + } float newPeak=peak[i]; for (int j=0; jgetConfInt("cursorMoveNoScroll",0); settings.lowLatency=e->getConfInt("lowLatency",0); settings.notePreviewBehavior=e->getConfInt("notePreviewBehavior",1); + settings.powerSave=e->getConfInt("powerSave",POWER_SAVE_DEFAULT); clampSetting(settings.mainFontSize,2,96); clampSetting(settings.patFontSize,2,96); @@ -1686,6 +1702,7 @@ void FurnaceGUI::syncSettings() { clampSetting(settings.cursorMoveNoScroll,0,1); clampSetting(settings.lowLatency,0,1); clampSetting(settings.notePreviewBehavior,0,3); + clampSetting(settings.powerSave,0,1); // keybinds for (int i=0; isetConf("cursorMoveNoScroll",settings.cursorMoveNoScroll); e->setConf("lowLatency",settings.lowLatency); e->setConf("notePreviewBehavior",settings.notePreviewBehavior); + e->setConf("powerSave",settings.powerSave); // colors for (int i=0; i Date: Sat, 16 Apr 2022 18:35:35 -0500 Subject: [PATCH 107/342] YM2151: implement panning macro i think --- src/engine/platform/arcade.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/engine/platform/arcade.cpp b/src/engine/platform/arcade.cpp index 2935bad06..f4311e29e 100644 --- a/src/engine/platform/arcade.cpp +++ b/src/engine/platform/arcade.cpp @@ -264,6 +264,16 @@ void DivPlatformArcade::tick(bool sysTick) { rWrite(0x1b,chan[i].std.wave.val&3); } + if (chan[i].std.panL.had) { + chan[i].chVolL=(chan[i].std.panL.val&2)>>1; + chan[i].chVolR=chan[i].std.panL.val&1; + if (isMuted[i]) { + rWrite(chanOffs[i]+ADDR_LR_FB_ALG,(chan[i].state.alg&7)|(chan[i].state.fb<<3)); + } else { + rWrite(chanOffs[i]+ADDR_LR_FB_ALG,(chan[i].state.alg&7)|(chan[i].state.fb<<3)|((chan[i].chVolL&1)<<6)|((chan[i].chVolR&1)<<7)); + } + } + if (chan[i].std.pitch.had) { chan[i].freqChanged=true; } From 559a99c5fc44282ec8f08cf9b64055e4ddf6d1da Mon Sep 17 00:00:00 2001 From: BlastBrothers Date: Sat, 16 Apr 2022 20:47:40 -0400 Subject: [PATCH 108/342] Menu option, settings file --- src/gui/gui.h | 2 ++ src/gui/settings.cpp | 8 ++++++++ 2 files changed, 10 insertions(+) diff --git a/src/gui/gui.h b/src/gui/gui.h index b6613e9df..e44fdcc50 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -803,6 +803,7 @@ class FurnaceGUI { int loadJapanese; int fmLayout; int sampleLayout; + int waveLayout; int susPosition; int effectCursorDir; int cursorPastePos; @@ -877,6 +878,7 @@ class FurnaceGUI { loadJapanese(0), fmLayout(0), sampleLayout(0), + waveLayout(0), susPosition(0), effectCursorDir(1), cursorPastePos(1), diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index 2e23f2f78..9dc04c773 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -963,6 +963,11 @@ void FurnaceGUI::drawSettings() { ImGui::Separator(); + bool waveLayoutB=settings.waveLayout; + if (ImGui::Checkbox("Use compact wave editor",&waveLayoutB)) { + settings.waveLayout=waveLayoutB; + } + bool sampleLayoutB=settings.sampleLayout; if (ImGui::Checkbox("Use compact sample editor",&sampleLayoutB)) { settings.sampleLayout=sampleLayoutB; @@ -1620,6 +1625,7 @@ void FurnaceGUI::syncSettings() { settings.loadJapanese=e->getConfInt("loadJapanese",0); settings.fmLayout=e->getConfInt("fmLayout",0); settings.sampleLayout=e->getConfInt("sampleLayout",0); + settings.waveLayout=e->getConfInt("waveLayout",0); settings.susPosition=e->getConfInt("susPosition",0); settings.effectCursorDir=e->getConfInt("effectCursorDir",1); settings.cursorPastePos=e->getConfInt("cursorPastePos",1); @@ -1693,6 +1699,7 @@ void FurnaceGUI::syncSettings() { clampSetting(settings.frameBorders,0,1); clampSetting(settings.effectDeletionAltersValue,0,1); clampSetting(settings.sampleLayout,0,1); + clampSetting(settings.waveLayout,0,1); clampSetting(settings.separateFMColors,0,1); clampSetting(settings.insEditColorize,0,1); clampSetting(settings.metroVol,0,200); @@ -1773,6 +1780,7 @@ void FurnaceGUI::commitSettings() { e->setConf("loadJapanese",settings.loadJapanese); e->setConf("fmLayout",settings.fmLayout); e->setConf("sampleLayout",settings.sampleLayout); + e->setConf("waveLayout",settings.waveLayout); e->setConf("susPosition",settings.susPosition); e->setConf("effectCursorDir",settings.effectCursorDir); e->setConf("cursorPastePos",settings.cursorPastePos); From ab4f9945a5c30bd6ca6a2d2a96b4d365b46a1095 Mon Sep 17 00:00:00 2001 From: BlastBrothers Date: Sat, 16 Apr 2022 21:51:53 -0400 Subject: [PATCH 109/342] Define the compact wave editor --- src/gui/waveEdit.cpp | 91 +++++++++++++++++++++++++++++++------------- 1 file changed, 65 insertions(+), 26 deletions(-) diff --git a/src/gui/waveEdit.cpp b/src/gui/waveEdit.cpp index a9efe512e..040e88207 100644 --- a/src/gui/waveEdit.cpp +++ b/src/gui/waveEdit.cpp @@ -31,7 +31,7 @@ void FurnaceGUI::drawWaveEdit() { } if (!waveEditOpen) return; float wavePreview[256]; - ImGui::SetNextWindowSizeConstraints(ImVec2(450.0f*dpiScale,300.0f*dpiScale),ImVec2(scrW*dpiScale,scrH*dpiScale)); + ImGui::SetNextWindowSizeConstraints(ImVec2(300.0f*dpiScale,300.0f*dpiScale),ImVec2(scrW*dpiScale,scrH*dpiScale)); if (ImGui::Begin("Wavetable Editor",&waveEditOpen,settings.allowEditDocking?0:ImGuiWindowFlags_NoDocking)) { if (curWave<0 || curWave>=(int)e->song.wave.size()) { ImGui::Text("no wavetable selected"); @@ -53,31 +53,34 @@ void FurnaceGUI::drawWaveEdit() { ImGui::SameLine(); DivWavetable* wave=e->song.wave[curWave]; - ImGui::Text("Width"); - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("use a width of:\n- any on Amiga/N163\n- 32 on Game Boy, PC Engine and WonderSwan\n- 64 on FDS\n- 128 on X1-010\nany other widths will be scaled during playback."); - } - ImGui::SameLine(); - ImGui::SetNextItemWidth(128.0f*dpiScale); - if (ImGui::InputInt("##_WTW",&wave->len,1,2)) { - if (wave->len>256) wave->len=256; - if (wave->len<1) wave->len=1; - e->notifyWaveChange(curWave); - if (wavePreviewOn) e->previewWave(curWave,wavePreviewNote); - MARK_MODIFIED; - } - ImGui::SameLine(); - ImGui::Text("Height"); - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("use a height of:\n- 15 for Game Boy, WonderSwan, X1-010 Envelope shape and N163\n- 31 for PC Engine\n- 63 for FDS\n- 255 for X1-010\nany other heights will be scaled during playback."); - } - ImGui::SameLine(); - ImGui::SetNextItemWidth(128.0f*dpiScale); - if (ImGui::InputInt("##_WTH",&wave->max,1,2)) { - if (wave->max>255) wave->max=255; - if (wave->max<1) wave->max=1; - e->notifyWaveChange(curWave); - MARK_MODIFIED; + + if (!settings.waveLayout){ + ImGui::Text("Width"); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("use a width of:\n- any on Amiga/N163\n- 32 on Game Boy, PC Engine and WonderSwan\n- 64 on FDS\n- 128 on X1-010\nany other widths will be scaled during playback."); + } + ImGui::SameLine(); + ImGui::SetNextItemWidth(128.0f*dpiScale); + if (ImGui::InputInt("##_WTW",&wave->len,1,2)) { + if (wave->len>256) wave->len=256; + if (wave->len<1) wave->len=1; + e->notifyWaveChange(curWave); + if (wavePreviewOn) e->previewWave(curWave,wavePreviewNote); + MARK_MODIFIED; + } + ImGui::SameLine(); + ImGui::Text("Height"); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("use a height of:\n- 15 for Game Boy, WonderSwan, X1-010 Envelope shape and N163\n- 31 for PC Engine\n- 63 for FDS\n- 255 for X1-010\nany other heights will be scaled during playback."); + } + ImGui::SameLine(); + ImGui::SetNextItemWidth(128.0f*dpiScale); + if (ImGui::InputInt("##_WTH",&wave->max,1,2)) { + if (wave->max>255) wave->max=255; + if (wave->max<1) wave->max=1; + e->notifyWaveChange(curWave); + MARK_MODIFIED; + } } ImGui::SameLine(); if (ImGui::RadioButton("Dec",!waveHex)) { @@ -87,6 +90,42 @@ void FurnaceGUI::drawWaveEdit() { if (ImGui::RadioButton("Hex",waveHex)) { waveHex=true; } + + if (settings.waveLayout){ + if (ImGui::BeginTable("SampleProps",2,ImGuiTableFlags_SizingStretchSame)) { + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::Text("Width"); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("use a width of:\n- any on Amiga/N163\n- 32 on Game Boy, PC Engine and WonderSwan\n- 64 on FDS\n- 128 on X1-010\nany other widths will be scaled during playback."); + } + ImGui::SameLine(); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + if (ImGui::InputInt("##_WTW",&wave->len,1,2)) { + if (wave->len>256) wave->len=256; + if (wave->len<1) wave->len=1; + e->notifyWaveChange(curWave); + if (wavePreviewOn) e->previewWave(curWave,wavePreviewNote); + MARK_MODIFIED; + } + ImGui::TableNextColumn(); + ImGui::SameLine(); + ImGui::Text("Height"); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("use a height of:\n- 15 for Game Boy, WonderSwan, X1-010 Envelope shape and N163\n- 31 for PC Engine\n- 63 for FDS\n- 255 for X1-010\nany other heights will be scaled during playback."); + } + ImGui::SameLine(); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + if (ImGui::InputInt("##_WTH",&wave->max,1,2)) { + if (wave->max>255) wave->max=255; + if (wave->max<1) wave->max=1; + e->notifyWaveChange(curWave); + MARK_MODIFIED; + } + ImGui::EndTable(); + } + } + for (int i=0; ilen; i++) { if (wave->data[i]>wave->max) wave->data[i]=wave->max; wavePreview[i]=wave->data[i]; From 6450a5323a3c0760dc74e53303621046a751115e Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 16 Apr 2022 22:26:41 -0500 Subject: [PATCH 110/342] fix preview of sample instruments --- src/engine/engine.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 04029c46b..6d0d4df0e 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -1971,7 +1971,7 @@ void DivEngine::autoNoteOn(int ch, int ins, int note, int vol) { } do { - if ((ins==-1 || getPreferInsType(finalChan)==getIns(ins)->type) && chan[finalChan].midiNote==-1) { + if ((ins==-1 || getPreferInsType(finalChan)==getIns(ins)->type || getIns(ins)->type==DIV_INS_AMIGA) && chan[finalChan].midiNote==-1) { chan[finalChan].midiNote=note; pendingNotes.push(DivNoteEvent(finalChan,ins,note,vol,true)); break; From 523adfac86fc91efc48b35ff815a70afdf1c09c8 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 16 Apr 2022 22:28:14 -0500 Subject: [PATCH 111/342] GUI: fix sample up/down actions not changing wavef orm --- src/gui/doAction.cpp | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/gui/doAction.cpp b/src/gui/doAction.cpp index 25b4afe6d..cd4dda075 100644 --- a/src/gui/doAction.cpp +++ b/src/gui/doAction.cpp @@ -559,12 +559,14 @@ void FurnaceGUI::doAction(int what) { case GUI_ACTION_SAMPLE_LIST_ADD: curSample=e->addSample(); + updateSampleTex=true; MARK_MODIFIED; break; case GUI_ACTION_SAMPLE_LIST_DUPLICATE: if (curSample>=0 && curSample<(int)e->song.sample.size()) { DivSample* prevSample=e->getSample(curSample); curSample=e->addSample(); + updateSampleTex=true; e->lockEngine([this,prevSample]() { DivSample* sample=e->getSample(curSample); if (sample!=NULL) { @@ -591,16 +593,23 @@ void FurnaceGUI::doAction(int what) { if (curSample>=0 && curSample<(int)e->song.sample.size()) openFileDialog(GUI_FILE_SAMPLE_SAVE); break; case GUI_ACTION_SAMPLE_LIST_MOVE_UP: - if (e->moveSampleUp(curSample)) curSample--; + if (e->moveSampleUp(curSample)) { + curSample--; + updateSampleTex=true; + } break; case GUI_ACTION_SAMPLE_LIST_MOVE_DOWN: - if (e->moveSampleDown(curSample)) curSample++; + if (e->moveSampleDown(curSample)) { + curSample++; + updateSampleTex=true; + } break; case GUI_ACTION_SAMPLE_LIST_DELETE: e->delSample(curSample); MARK_MODIFIED; if (curSample>=(int)e->song.sample.size()) { curSample--; + updateSampleTex=true; } break; case GUI_ACTION_SAMPLE_LIST_EDIT: @@ -608,9 +617,11 @@ void FurnaceGUI::doAction(int what) { break; case GUI_ACTION_SAMPLE_LIST_UP: if (--curSample<0) curSample=0; + updateSampleTex=true; break; case GUI_ACTION_SAMPLE_LIST_DOWN: if (++curSample>=(int)e->song.sample.size()) curSample=((int)e->song.sample.size())-1; + updateSampleTex=true; break; case GUI_ACTION_SAMPLE_LIST_PREVIEW: e->previewSample(curSample); From 42e84541432574b7b21e771a643aa650dbeefd31 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 16 Apr 2022 22:53:40 -0500 Subject: [PATCH 112/342] ADSR test area --- src/gui/debugWindow.cpp | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/gui/debugWindow.cpp b/src/gui/debugWindow.cpp index 31830681f..06184c0ba 100644 --- a/src/gui/debugWindow.cpp +++ b/src/gui/debugWindow.cpp @@ -226,6 +226,26 @@ void FurnaceGUI::drawDebug() { } ImGui::TreePop(); } + if (ImGui::TreeNode("ADSR Test Area")) { + static int tl, ar, dr, d2r, sl, rr, sus, egt, algOrGlobalSus, instType; + static float maxArDr, maxTl; + ImGui::Text("This window was done out of frustration"); + drawFMEnv(tl,ar,dr,d2r,rr,sl,sus,egt,algOrGlobalSus,maxTl,maxArDr,ImVec2(200.0f*dpiScale,100.0f*dpiScale),instType); + + ImGui::InputInt("tl",&tl); + ImGui::InputInt("ar",&ar); + ImGui::InputInt("dr",&dr); + ImGui::InputInt("d2r",&d2r); + ImGui::InputInt("sl",&sl); + ImGui::InputInt("rr",&rr); + ImGui::InputInt("sus",&sus); + ImGui::InputInt("egt",&egt); + ImGui::InputInt("algOrGlobalSus",&algOrGlobalSus); + ImGui::InputInt("instType",&instType); + ImGui::InputFloat("maxArDr",&maxArDr); + ImGui::InputFloat("maxTl",&maxTl); + ImGui::TreePop(); + } if (ImGui::TreeNode("User Interface")) { if (ImGui::Button("Inspect")) { inspectorOpen=!inspectorOpen; From 99d57bf342fe765205c93128fbb71fddeef22fdf Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 16 Apr 2022 23:43:49 -0500 Subject: [PATCH 113/342] GUI: C64 envelope view --- src/gui/insEdit.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index b54ccd136..730d1695e 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -2277,6 +2277,8 @@ void FurnaceGUI::drawInsEdit() { P(CWSliderScalar("Release",ImGuiDataType_U8,&ins->c64.r,&_ZERO,&_FIFTEEN)); rightClickable P(CWSliderScalar("Duty",ImGuiDataType_U16,&ins->c64.duty,&_ZERO,&_FOUR_THOUSAND_NINETY_FIVE)); rightClickable + drawFMEnv(0,16-ins->c64.a,16-ins->c64.d,15-ins->c64.r,15-ins->c64.r,15-ins->c64.s,0,0,0,15,16,ImVec2(ImGui::GetContentRegionAvail().x,100.0f*dpiScale),ins->type); + bool ringMod=ins->c64.ringMod; if (ImGui::Checkbox("Ring Modulation",&ringMod)) { PARAMETER ins->c64.ringMod=ringMod; From 003c9c323505f46a12fee9d31a2aa8a4596f167f Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 16 Apr 2022 23:58:29 -0500 Subject: [PATCH 114/342] GUI: prepare for GB envelope view --- src/gui/gui.h | 1 + src/gui/insEdit.cpp | 23 +++++++++++++++++++++++ src/gui/settings.cpp | 10 +++++++--- 3 files changed, 31 insertions(+), 3 deletions(-) diff --git a/src/gui/gui.h b/src/gui/gui.h index b6613e9df..36b6a8893 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -1078,6 +1078,7 @@ class FurnaceGUI { void drawWaveform(unsigned char type, bool opz, const ImVec2& size); void drawAlgorithm(unsigned char alg, FurnaceGUIFMAlgs algType, const ImVec2& size); void drawFMEnv(unsigned char tl, unsigned char ar, unsigned char dr, unsigned char d2r, unsigned char rr, unsigned char sl, unsigned char sus, unsigned char egt, unsigned char algOrGlobalSus, float maxTl, float maxArDr, const ImVec2& size, unsigned short instType); + void drawGBEnv(unsigned char vol, unsigned char len, unsigned char sLen, bool dir, const ImVec2& size); void drawSysConf(int i); // these ones offer ctrl-wheel fine value changes. diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index 730d1695e..a58feea87 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -1002,6 +1002,27 @@ void FurnaceGUI::drawFMEnv(unsigned char tl, unsigned char ar, unsigned char dr, } } +void FurnaceGUI::drawGBEnv(unsigned char vol, unsigned char len, unsigned char sLen, bool dir, const ImVec2& size) { + //ImDrawList* dl=ImGui::GetWindowDrawList(); + ImGuiWindow* window=ImGui::GetCurrentWindow(); + + 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(); + //ImU32 color=ImGui::GetColorU32(uiColors[GUI_COLOR_FM_ENVELOPE]); + //ImU32 colorS=ImGui::GetColorU32(uiColors[GUI_COLOR_FM_ENVELOPE_SUS_GUIDE]); // Sustain horiz/vert line color + ImGui::ItemSize(size,style.FramePadding.y); + if (ImGui::ItemAdd(rect,ImGui::GetID("alg"))) { + ImGui::RenderFrame(rect.Min,rect.Max,ImGui::GetColorU32(ImGuiCol_FrameBg),true,style.FrameRounding); + + // TODO: this whole thing + } +} + #define P(x) if (x) { \ MARK_MODIFIED; \ e->notifyInsChange(curIns); \ @@ -2242,6 +2263,8 @@ void FurnaceGUI::drawInsEdit() { goesUp=false; ins->gb.envDir=goesUp; } + + drawGBEnv(ins->gb.envVol,ins->gb.envLen,ins->gb.soundLen,ins->gb.envDir,ImVec2(ImGui::GetContentRegionAvail().x,100.0f*dpiScale)); ImGui::EndTabItem(); } if (ins->type==DIV_INS_C64) if (ImGui::BeginTabItem("C64")) { diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index 2e23f2f78..5e0bdbad6 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -1082,15 +1082,19 @@ void FurnaceGUI::drawSettings() { UI_COLOR_CONFIG(GUI_COLOR_ORDER_INACTIVE,"Inactive patterns"); ImGui::TreePop(); } + if (ImGui::TreeNode("Envelope View")) { + UI_COLOR_CONFIG(GUI_COLOR_FM_ENVELOPE,"Envelope"); + UI_COLOR_CONFIG(GUI_COLOR_FM_ENVELOPE_SUS_GUIDE,"Sustain guide"); + UI_COLOR_CONFIG(GUI_COLOR_FM_ENVELOPE_RELEASE,"Release"); + + ImGui::TreePop(); + } if (ImGui::TreeNode("FM Editor")) { UI_COLOR_CONFIG(GUI_COLOR_FM_ALG_BG,"Algorithm background"); UI_COLOR_CONFIG(GUI_COLOR_FM_ALG_LINE,"Algorithm lines"); UI_COLOR_CONFIG(GUI_COLOR_FM_MOD,"Modulator"); UI_COLOR_CONFIG(GUI_COLOR_FM_CAR,"Carrier"); - UI_COLOR_CONFIG(GUI_COLOR_FM_ENVELOPE,"Envelope"); - UI_COLOR_CONFIG(GUI_COLOR_FM_ENVELOPE_SUS_GUIDE,"Sustain guide"); - UI_COLOR_CONFIG(GUI_COLOR_FM_ENVELOPE_RELEASE,"Release"); UI_COLOR_CONFIG(GUI_COLOR_FM_SSG,"SSG-EG"); UI_COLOR_CONFIG(GUI_COLOR_FM_WAVE,"Waveform"); From d1fadf1076e61b57139c750f2df7972a098691c5 Mon Sep 17 00:00:00 2001 From: James Alan Nguyen Date: Sun, 17 Apr 2022 15:54:00 +1000 Subject: [PATCH 115/342] Address review comments take 2 --- src/engine/fileOpsIns.cpp | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/engine/fileOpsIns.cpp b/src/engine/fileOpsIns.cpp index b1e5b3974..f9eaab38b 100644 --- a/src/engine/fileOpsIns.cpp +++ b/src/engine/fileOpsIns.cpp @@ -101,7 +101,7 @@ void DivEngine::loadDMP(SafeReader& reader, std::vector& ret, St logD(".dmp version %d",version); } catch (EndOfFileException& e) { lastError="premature end of file"; - logE(lastError.c_str()); + logE("premature end of file"); delete ins; return; } @@ -160,7 +160,7 @@ void DivEngine::loadDMP(SafeReader& reader, std::vector& ret, St } } catch (EndOfFileException& e) { lastError="premature end of file"; - logE(lastError.c_str()); + logE("premature end of file"); delete ins; return; } @@ -352,7 +352,7 @@ void DivEngine::loadDMP(SafeReader& reader, std::vector& ret, St } } catch (EndOfFileException& e) { lastError="premature end of file"; - logE(lastError.c_str()); + logE("premature end of file"); delete ins; return; } @@ -387,7 +387,7 @@ void DivEngine::loadTFI(SafeReader& reader, std::vector& ret, St } } catch (EndOfFileException& e) { lastError="premature end of file"; - logE(lastError.c_str()); + logE("premature end of file"); delete ins; return; } @@ -429,7 +429,7 @@ void DivEngine::loadVGI(SafeReader& reader, std::vector& ret, St } } catch (EndOfFileException& e) { lastError="premature end of file"; - logE(lastError.c_str()); + logE("premature end of file"); delete ins; return; } @@ -496,7 +496,7 @@ void DivEngine::loadS3I(SafeReader& reader, std::vector& ret, St reader.seek(21, SEEK_CUR); } else { lastError = "S3I PCM samples currently not supported."; - logE(lastError.c_str()); + logE("S3I PCM samples currently not supported."); } ins->name = reader.readString(28); ins->name = (ins->name.length() == 0) ? stripPath : ins->name; @@ -504,12 +504,12 @@ void DivEngine::loadS3I(SafeReader& reader, std::vector& ret, St int s3i_signature = reader.readI(); if (s3i_signature != 0x49524353) { - warnings = "S3I signature invalid."; - logW(lastError.c_str()); + addWarning("S3I signature invalid."); + logW("S3I signature invalid."); }; } catch (EndOfFileException& e) { lastError = "premature end of file"; - logE(lastError.c_str()); + logE("premature end of file"); delete ins; return; } @@ -630,7 +630,7 @@ void DivEngine::loadSBI(SafeReader& reader, std::vector& ret, St } catch (EndOfFileException& e) { lastError = "premature end of file"; - logE(lastError.c_str()); + logE("premature end of file"); delete ins; } } @@ -712,7 +712,7 @@ void DivEngine::loadBNK(SafeReader& reader, std::vector& ret, St } catch (EndOfFileException& e) { lastError = "premature end of file"; - logE(lastError.c_str()); + logE("premature end of file"); for (int i = readCount; i >= 0; --i) { delete insList[i]; } @@ -722,7 +722,7 @@ void DivEngine::loadBNK(SafeReader& reader, std::vector& ret, St } else { // assume GEMS BNK for now. lastError = "GEMS BNK currently not supported."; - logE(lastError.c_str()); + logE("GEMS BNK currently not supported."); } if (!is_failed) { @@ -797,7 +797,7 @@ void DivEngine::loadFF(SafeReader& reader, std::vector& ret, Str } } catch (EndOfFileException& e) { lastError = "premature end of file"; - logE(lastError.c_str()); + logE("premature end of file"); for (int i = readCount; i >= 0; --i) { delete insList[i]; } @@ -818,7 +818,7 @@ void DivEngine::loadOPM(SafeReader& reader, std::vector& ret, St } catch (EndOfFileException& e) { lastError="premature end of file"; - logE(lastError.c_str()); + logE("premature end of file"); return; } } @@ -916,7 +916,7 @@ std::vector DivEngine::instrumentFromFile(const char* path) { } } catch (EndOfFileException& e) { lastError="premature end of file"; - logE(lastError.c_str()); + logE("premature end of file"); delete ins; delete[] buf; return ret; From 768419f46121c9267cbcbb7690e0e6a592e58e7f Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 17 Apr 2022 01:15:34 -0500 Subject: [PATCH 116/342] GUI: implement GB envelope view --- src/gui/insEdit.cpp | 47 ++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 42 insertions(+), 5 deletions(-) diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index a58feea87..dca41f17f 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -943,7 +943,7 @@ void FurnaceGUI::drawFMEnv(unsigned char tl, unsigned char ar, unsigned char dr, ImU32 colorR=ImGui::GetColorU32(uiColors[GUI_COLOR_FM_ENVELOPE_RELEASE]); // Relsease triangle ImU32 colorS=ImGui::GetColorU32(uiColors[GUI_COLOR_FM_ENVELOPE_SUS_GUIDE]); // Sustain horiz/vert line color ImGui::ItemSize(size,style.FramePadding.y); - if (ImGui::ItemAdd(rect,ImGui::GetID("alg"))) { + if (ImGui::ItemAdd(rect,ImGui::GetID("fmEnv"))) { ImGui::RenderFrame(rect.Min,rect.Max,ImGui::GetColorU32(ImGuiCol_FrameBg),true,style.FrameRounding); //Adjust for OPLL global sustain setting @@ -1003,7 +1003,7 @@ void FurnaceGUI::drawFMEnv(unsigned char tl, unsigned char ar, unsigned char dr, } void FurnaceGUI::drawGBEnv(unsigned char vol, unsigned char len, unsigned char sLen, bool dir, const ImVec2& size) { - //ImDrawList* dl=ImGui::GetWindowDrawList(); + ImDrawList* dl=ImGui::GetWindowDrawList(); ImGuiWindow* window=ImGui::GetCurrentWindow(); ImVec2 minArea=window->DC.CursorPos; @@ -1013,13 +1013,50 @@ void FurnaceGUI::drawGBEnv(unsigned char vol, unsigned char len, unsigned char s ); ImRect rect=ImRect(minArea,maxArea); ImGuiStyle& style=ImGui::GetStyle(); - //ImU32 color=ImGui::GetColorU32(uiColors[GUI_COLOR_FM_ENVELOPE]); + ImU32 color=ImGui::GetColorU32(uiColors[GUI_COLOR_FM_ENVELOPE]); //ImU32 colorS=ImGui::GetColorU32(uiColors[GUI_COLOR_FM_ENVELOPE_SUS_GUIDE]); // Sustain horiz/vert line color ImGui::ItemSize(size,style.FramePadding.y); - if (ImGui::ItemAdd(rect,ImGui::GetID("alg"))) { + if (ImGui::ItemAdd(rect,ImGui::GetID("gbEnv"))) { ImGui::RenderFrame(rect.Min,rect.Max,ImGui::GetColorU32(ImGuiCol_FrameBg),true,style.FrameRounding); + + float volY=1.0-((float)vol/15.0); + float lenPos=(sLen>62)?1.0:((float)sLen/384.0); + float envEndPoint=((float)len/7.0)*((float)(dir?(15-vol):vol)/15.0); - // TODO: this whole thing + ImVec2 pos1=ImLerp(rect.Min,rect.Max,ImVec2(0.0,volY)); + ImVec2 pos2; + if (dir) { + if (len>0) { + if (lenPos0) { + if (lenPos0 || sLen<63)?((dir && sLen>62)?0.0:1.0):volY)); + + addAALine(dl,pos1,pos2,color); + if (lenPos>=envEndPoint && sLen<63 && dir) { + pos3=ImLerp(rect.Min,rect.Max,ImVec2(lenPos,0.0)); + addAALine(dl,pos2,pos3,color); + ImVec2 pos4=ImLerp(rect.Min,rect.Max,ImVec2(lenPos,1.0)); + addAALine(dl,pos3,pos4,color); + } else { + addAALine(dl,pos2,pos3,color); + } } } From 211a4b182181961221f7c76dee4c95fcdc7cf382 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 17 Apr 2022 01:37:33 -0500 Subject: [PATCH 117/342] C64: fix portamento not working... --- src/engine/platform/c64.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/engine/platform/c64.cpp b/src/engine/platform/c64.cpp index 12ec3f5f9..02473c80c 100644 --- a/src/engine/platform/c64.cpp +++ b/src/engine/platform/c64.cpp @@ -169,7 +169,7 @@ void DivPlatformC64::tick(bool sysTick) { if (sysTick) { if (chan[i].testWhen>0) { if (--chan[i].testWhen<1) { - if (!chan[i].resetMask) { + if (!chan[i].resetMask && !chan[i].inPorta) { rWrite(i*7+5,0); rWrite(i*7+6,0); rWrite(i*7+4,(chan[i].wave<<4)|8|(chan[i].ring<<2)|(chan[i].sync<<1)); @@ -344,7 +344,7 @@ int DivPlatformC64::dispatch(DivCommand c) { break; case DIV_CMD_PRE_PORTA: if (chan[c.chan].active && c.value2) { - if (parent->song.resetMacroOnPorta) { + if (parent->song.resetMacroOnPorta || !chan[c.chan].inPorta) { chan[c.chan].std.init(parent->getIns(chan[c.chan].ins)); chan[c.chan].keyOn=true; } From 0952d1b2f927785fc159d31d45d7841a1f3294b8 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 17 Apr 2022 01:54:42 -0500 Subject: [PATCH 118/342] GUI: add a full-screen option --- src/gui/gui.cpp | 20 +++++++++++++++++--- src/gui/gui.h | 2 +- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 7f8f0d729..30b90793e 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -17,6 +17,7 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#include #define _USE_MATH_DEFINES #include "gui.h" #include "util.h" @@ -2475,6 +2476,10 @@ bool FurnaceGUI::loop() { ImGui::EndMenu(); } if (ImGui::BeginMenu("settings")) { + if (ImGui::MenuItem("full screen",NULL,fullScreen)) { + fullScreen=!fullScreen; + SDL_SetWindowFullscreen(sdlWin,fullScreen?(SDL_WINDOW_FULLSCREEN|SDL_WINDOW_FULLSCREEN_DESKTOP):0); + } if (ImGui::MenuItem("lock layout",NULL,lockLayout)) { lockLayout=!lockLayout; } @@ -3235,6 +3240,7 @@ bool FurnaceGUI::init() { tempoView=e->getConfBool("tempoView",true); waveHex=e->getConfBool("waveHex",false); lockLayout=e->getConfBool("lockLayout",false); + fullScreen=e->getConfBool("fullScreen",false); syncSettings(); @@ -3258,7 +3264,7 @@ bool FurnaceGUI::init() { SDL_Init(SDL_INIT_VIDEO); - 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|(fullScreen?SDL_WINDOW_FULLSCREEN_DESKTOP:0)); if (sdlWin==NULL) { logE("could not open window! %s",SDL_GetError()); return false; @@ -3269,12 +3275,18 @@ bool FurnaceGUI::init() { SDL_GetDisplayDPI(SDL_GetWindowDisplayIndex(sdlWin),&dpiScaleF,NULL,NULL); dpiScale=round(dpiScaleF/96.0f); if (dpiScale<1) dpiScale=1; - if (dpiScale!=1) SDL_SetWindowSize(sdlWin,scrW*dpiScale,scrH*dpiScale); + if (dpiScale!=1) { + if (!fullScreen) { + SDL_SetWindowSize(sdlWin,scrW*dpiScale,scrH*dpiScale); + } + } if (SDL_GetDisplayUsableBounds(SDL_GetWindowDisplayIndex(sdlWin),&displaySize)==0) { if (scrW>displaySize.w/dpiScale) scrW=(displaySize.w/dpiScale)-32; if (scrH>displaySize.h/dpiScale) scrH=(displaySize.h/dpiScale)-32; - SDL_SetWindowSize(sdlWin,scrW*dpiScale,scrH*dpiScale); + if (!fullScreen) { + SDL_SetWindowSize(sdlWin,scrW*dpiScale,scrH*dpiScale); + } } } #endif @@ -3402,6 +3414,7 @@ bool FurnaceGUI::finish() { e->setConf("tempoView",tempoView); e->setConf("waveHex",waveHex); e->setConf("lockLayout",lockLayout); + e->setConf("fullScreen",fullScreen); for (int i=0; i Date: Sun, 17 Apr 2022 01:57:50 -0500 Subject: [PATCH 119/342] GUI: add toggle full-screen keybind --- src/gui/doAction.cpp | 4 ++++ src/gui/gui.cpp | 5 ++--- src/gui/gui.h | 1 + src/gui/guiConst.cpp | 1 + src/gui/settings.cpp | 1 + 5 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/gui/doAction.cpp b/src/gui/doAction.cpp index cd4dda075..b68d684d3 100644 --- a/src/gui/doAction.cpp +++ b/src/gui/doAction.cpp @@ -136,6 +136,10 @@ void FurnaceGUI::doAction(int what) { case GUI_ACTION_FOLLOW_PATTERN: followPattern=!followPattern; break; + case GUI_ACTION_FULLSCREEN: + fullScreen=!fullScreen; + SDL_SetWindowFullscreen(sdlWin,fullScreen?(SDL_WINDOW_FULLSCREEN|SDL_WINDOW_FULLSCREEN_DESKTOP):0); + break; case GUI_ACTION_PANIC: e->syncReset(); break; diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 30b90793e..fab19120b 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -2476,9 +2476,8 @@ bool FurnaceGUI::loop() { ImGui::EndMenu(); } if (ImGui::BeginMenu("settings")) { - if (ImGui::MenuItem("full screen",NULL,fullScreen)) { - fullScreen=!fullScreen; - SDL_SetWindowFullscreen(sdlWin,fullScreen?(SDL_WINDOW_FULLSCREEN|SDL_WINDOW_FULLSCREEN_DESKTOP):0); + if (ImGui::MenuItem("full screen",BIND_FOR(GUI_ACTION_FULLSCREEN),fullScreen)) { + doAction(GUI_ACTION_FULLSCREEN); } if (ImGui::MenuItem("lock layout",NULL,lockLayout)) { lockLayout=!lockLayout; diff --git a/src/gui/gui.h b/src/gui/gui.h index 4743a0002..b193e39a5 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -297,6 +297,7 @@ enum FurnaceGUIActions { GUI_ACTION_REPEAT_PATTERN, GUI_ACTION_FOLLOW_ORDERS, GUI_ACTION_FOLLOW_PATTERN, + GUI_ACTION_FULLSCREEN, GUI_ACTION_PANIC, GUI_ACTION_WINDOW_EDIT_CONTROLS, diff --git a/src/gui/guiConst.cpp b/src/gui/guiConst.cpp index 10b004421..7cc173c52 100644 --- a/src/gui/guiConst.cpp +++ b/src/gui/guiConst.cpp @@ -167,6 +167,7 @@ const FurnaceGUIActionDef guiActions[GUI_ACTION_MAX]={ D("REPEAT_PATTERN", "Toggle repeat pattern", 0), D("FOLLOW_ORDERS", "Follow orders", 0), D("FOLLOW_PATTERN", "Follow pattern", 0), + D("FULLSCREEN", "Toggle full-screen", FURKMOD_ALT|SDLK_RETURN), D("PANIC", "Panic", SDLK_F12), D("WINDOW_EDIT_CONTROLS", "Edit Controls", 0), diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index be0805f3a..7ead6c204 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -1249,6 +1249,7 @@ void FurnaceGUI::drawSettings() { UI_KEYBIND_CONFIG(GUI_ACTION_REPEAT_PATTERN); UI_KEYBIND_CONFIG(GUI_ACTION_FOLLOW_ORDERS); UI_KEYBIND_CONFIG(GUI_ACTION_FOLLOW_PATTERN); + UI_KEYBIND_CONFIG(GUI_ACTION_FULLSCREEN); UI_KEYBIND_CONFIG(GUI_ACTION_PANIC); KEYBIND_CONFIG_END; From 53994cebbdfd3744b249352c401512f8a7436b62 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 17 Apr 2022 02:08:19 -0500 Subject: [PATCH 120/342] GUI: new default key for full-screen --- src/gui/guiConst.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/guiConst.cpp b/src/gui/guiConst.cpp index 7cc173c52..a0b3c17c4 100644 --- a/src/gui/guiConst.cpp +++ b/src/gui/guiConst.cpp @@ -167,7 +167,7 @@ const FurnaceGUIActionDef guiActions[GUI_ACTION_MAX]={ D("REPEAT_PATTERN", "Toggle repeat pattern", 0), D("FOLLOW_ORDERS", "Follow orders", 0), D("FOLLOW_PATTERN", "Follow pattern", 0), - D("FULLSCREEN", "Toggle full-screen", FURKMOD_ALT|SDLK_RETURN), + D("FULLSCREEN", "Toggle full-screen", SDLK_F11), D("PANIC", "Panic", SDLK_F12), D("WINDOW_EDIT_CONTROLS", "Edit Controls", 0), From 0258342324eb59ad2e9d7be8dd901d0663994d7c Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 17 Apr 2022 02:08:53 -0500 Subject: [PATCH 121/342] M S V C --- src/gui/gui.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index fab19120b..f3ab5a327 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -17,7 +17,6 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#include #define _USE_MATH_DEFINES #include "gui.h" #include "util.h" From c9324e04bd10cb1d1f27b313598b1a090ea96f07 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 17 Apr 2022 02:51:03 -0500 Subject: [PATCH 122/342] GUI: vertical C64 envelope editor --- src/gui/insEdit.cpp | 47 +++++++++++++++++++++++++++++++++++++++------ 1 file changed, 41 insertions(+), 6 deletions(-) diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index dca41f17f..d3f297d40 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -2331,13 +2331,48 @@ void FurnaceGUI::drawInsEdit() { } ImGui::PopStyleColor(); - P(CWSliderScalar("Attack",ImGuiDataType_U8,&ins->c64.a,&_ZERO,&_FIFTEEN)); rightClickable - P(CWSliderScalar("Decay",ImGuiDataType_U8,&ins->c64.d,&_ZERO,&_FIFTEEN)); rightClickable - P(CWSliderScalar("Sustain",ImGuiDataType_U8,&ins->c64.s,&_ZERO,&_FIFTEEN)); rightClickable - P(CWSliderScalar("Release",ImGuiDataType_U8,&ins->c64.r,&_ZERO,&_FIFTEEN)); rightClickable - P(CWSliderScalar("Duty",ImGuiDataType_U16,&ins->c64.duty,&_ZERO,&_FOUR_THOUSAND_NINETY_FIVE)); rightClickable + ImVec2 sliderSize=ImVec2(20.0f*dpiScale,128.0*dpiScale); - drawFMEnv(0,16-ins->c64.a,16-ins->c64.d,15-ins->c64.r,15-ins->c64.r,15-ins->c64.s,0,0,0,15,16,ImVec2(ImGui::GetContentRegionAvail().x,100.0f*dpiScale),ins->type); + if (ImGui::BeginTable("C64EnvParams",5,ImGuiTableFlags_NoHostExtendX)) { + ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthFixed,sliderSize.x); + ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthFixed,sliderSize.x); + ImGui::TableSetupColumn("c2",ImGuiTableColumnFlags_WidthFixed,sliderSize.x); + ImGui::TableSetupColumn("c3",ImGuiTableColumnFlags_WidthFixed,sliderSize.x); + ImGui::TableSetupColumn("c4",ImGuiTableColumnFlags_WidthStretch); + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + CENTER_TEXT("A"); + ImGui::TextUnformatted("A"); + ImGui::TableNextColumn(); + CENTER_TEXT("D"); + ImGui::TextUnformatted("D"); + ImGui::TableNextColumn(); + CENTER_TEXT("S"); + ImGui::TextUnformatted("S"); + ImGui::TableNextColumn(); + CENTER_TEXT("R"); + ImGui::TextUnformatted("R"); + ImGui::TableNextColumn(); + CENTER_TEXT("Envelope"); + ImGui::TextUnformatted("Envelope"); + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + P(CWVSliderScalar("##Attack",sliderSize,ImGuiDataType_U8,&ins->c64.a,&_ZERO,&_FIFTEEN)); + ImGui::TableNextColumn(); + P(CWVSliderScalar("##Decay",sliderSize,ImGuiDataType_U8,&ins->c64.d,&_ZERO,&_FIFTEEN)); + ImGui::TableNextColumn(); + P(CWVSliderScalar("##Sustain",sliderSize,ImGuiDataType_U8,&ins->c64.s,&_ZERO,&_FIFTEEN)); + ImGui::TableNextColumn(); + P(CWVSliderScalar("##Release",sliderSize,ImGuiDataType_U8,&ins->c64.r,&_ZERO,&_FIFTEEN)); + ImGui::TableNextColumn(); + drawFMEnv(0,16-ins->c64.a,16-ins->c64.d,15-ins->c64.r,15-ins->c64.r,15-ins->c64.s,0,0,0,15,16,ImVec2(ImGui::GetContentRegionAvail().x,sliderSize.y),ins->type); + + ImGui::EndTable(); + } + + P(CWSliderScalar("Duty",ImGuiDataType_U16,&ins->c64.duty,&_ZERO,&_FOUR_THOUSAND_NINETY_FIVE)); rightClickable bool ringMod=ins->c64.ringMod; if (ImGui::Checkbox("Ring Modulation",&ringMod)) { PARAMETER From 5e7a4eae16ed7a82a499855d87326f77c9a67d80 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 17 Apr 2022 05:01:54 -0500 Subject: [PATCH 123/342] VERA: finally proper volume --- src/engine/platform/vera.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/engine/platform/vera.cpp b/src/engine/platform/vera.cpp index 99da6062e..99c6f8822 100644 --- a/src/engine/platform/vera.cpp +++ b/src/engine/platform/vera.cpp @@ -114,8 +114,8 @@ void DivPlatformVERA::acquire(short* bufL, short* bufR, size_t start, size_t len psg_render(psg,buf[0],buf[1],curLen); pcm_render(pcm,buf[2],buf[3],curLen); for (int i=0; i>1))/2); - bufR[pos]=(short)(((int)buf[1][i]+(buf[3][i]>>1))/2); + bufL[pos]=(short)(((int)buf[0][i]+buf[2][i])/2); + bufR[pos]=(short)(((int)buf[1][i]+buf[3][i])/2); pos++; } len-=curLen; @@ -377,7 +377,7 @@ void DivPlatformVERA::muteChannel(int ch, bool mute) { } float DivPlatformVERA::getPostAmp() { - return 4.0f; + return 8.0f; } bool DivPlatformVERA::isStereo() { From 4bb77ea3cb49085ec7c3c83b3f7f7bfa1d78ce2f Mon Sep 17 00:00:00 2001 From: BlastBrothers Date: Sun, 17 Apr 2022 11:38:28 -0400 Subject: [PATCH 124/342] This is not a sample properties table --- src/gui/waveEdit.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/waveEdit.cpp b/src/gui/waveEdit.cpp index 040e88207..5c812283e 100644 --- a/src/gui/waveEdit.cpp +++ b/src/gui/waveEdit.cpp @@ -92,7 +92,7 @@ void FurnaceGUI::drawWaveEdit() { } if (settings.waveLayout){ - if (ImGui::BeginTable("SampleProps",2,ImGuiTableFlags_SizingStretchSame)) { + if (ImGui::BeginTable("WaveProps",2,ImGuiTableFlags_SizingStretchSame)) { ImGui::TableNextRow(); ImGui::TableNextColumn(); ImGui::Text("Width"); From fc8a130c71529059bca416b76392faa6925dce72 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 17 Apr 2022 14:15:57 -0500 Subject: [PATCH 125/342] implement panning macro except for QSound --- src/engine/platform/gb.cpp | 5 +++++ src/engine/platform/genesis.cpp | 5 +++++ src/engine/platform/lynx.cpp | 14 ++++++++++++++ src/engine/platform/opl.cpp | 6 +++++- src/engine/platform/pce.cpp | 11 +++++++++++ src/engine/platform/saa.cpp | 20 ++++++++++++++++++++ src/engine/platform/segapcm.cpp | 24 ++++++++++++++++++++++-- src/engine/platform/swan.cpp | 11 +++++++++++ src/engine/platform/vera.cpp | 6 ++++++ src/engine/platform/x1_010.cpp | 10 ++++++++++ src/engine/platform/ym2610.cpp | 5 +++++ src/engine/platform/ym2610b.cpp | 5 +++++ src/gui/insEdit.cpp | 4 ++-- 13 files changed, 121 insertions(+), 5 deletions(-) diff --git a/src/engine/platform/gb.cpp b/src/engine/platform/gb.cpp index d3bb28e5e..eb7b6881e 100644 --- a/src/engine/platform/gb.cpp +++ b/src/engine/platform/gb.cpp @@ -192,6 +192,11 @@ void DivPlatformGB::tick(bool sysTick) { if (!chan[i].keyOff) chan[i].keyOn=true; } } + if (chan[i].std.panL.had) { + lastPan&=~(0x11<>1); + } + if (chan[i].std.pitch.had) { chan[i].freqChanged=true; } @@ -286,7 +290,7 @@ void DivPlatformOPL::tick(bool sysTick) { chan[i].state.fb=chan[i].std.fb.val; } - if (chan[i].std.alg.had || chan[i].std.fb.had) { + if (chan[i].std.alg.had || chan[i].std.fb.had || (oplType==3 && chan[i].std.panL.had)) { if (isMuted[i]) { rWrite(chanMap[i]+ADDR_LR_FB_ALG,(chan[i].state.alg&1)|(chan[i].state.fb<<1)); if (ops==4) { diff --git a/src/engine/platform/pce.cpp b/src/engine/platform/pce.cpp index 2c248db09..e1cbb6808 100644 --- a/src/engine/platform/pce.cpp +++ b/src/engine/platform/pce.cpp @@ -196,6 +196,17 @@ void DivPlatformPCE::tick(bool sysTick) { if (!chan[i].keyOff) chan[i].keyOn=true; } } + if (chan[i].std.panL.had) { + chan[i].pan&=0x0f; + chan[i].pan|=(chan[i].std.panL.val&15)<<4; + } + if (chan[i].std.panR.had) { + chan[i].pan&=0xf0; + chan[i].pan|=chan[i].std.panR.val&15; + } + if (chan[i].std.panL.had || chan[i].std.panR.had) { + chWrite(i,0x05,isMuted[i]?0:chan[i].pan); + } if (chan[i].std.pitch.had) { chan[i].freqChanged=true; } diff --git a/src/engine/platform/saa.cpp b/src/engine/platform/saa.cpp index 0e06c3388..47b698051 100644 --- a/src/engine/platform/saa.cpp +++ b/src/engine/platform/saa.cpp @@ -166,6 +166,26 @@ void DivPlatformSAA1099::tick(bool sysTick) { if (chan[i].std.wave.had) { chan[i].psgMode=chan[i].std.wave.val&3; } + if (chan[i].std.panL.had) { + chan[i].pan&=0x0f; + chan[i].pan|=(chan[i].std.panL.val&15)<<4; + } + + if (chan[i].std.panR.had) { + chan[i].pan&=0xf0; + chan[i].pan|=chan[i].std.panR.val&15; + } + if (chan[i].std.panL.had || chan[i].std.panR.had) { + if (isMuted[i]) { + rWrite(i,0); + } else { + if (chan[i].std.vol.had) { + if (chan[i].active) rWrite(i,applyPan(chan[i].outVol&15,chan[i].pan)); + } else { + if (chan[i].active) rWrite(i,applyPan(chan[i].vol&15,chan[i].pan)); + } + } + } if (chan[i].std.pitch.had) { chan[i].freqChanged=true; } diff --git a/src/engine/platform/segapcm.cpp b/src/engine/platform/segapcm.cpp index 79d7179a2..700924476 100644 --- a/src/engine/platform/segapcm.cpp +++ b/src/engine/platform/segapcm.cpp @@ -80,9 +80,10 @@ void DivPlatformSegaPCM::tick(bool sysTick) { for (int i=0; i<16; i++) { chan[i].std.next(); - if (chan[i].std.vol.had) { + // TODO: fix + /*if (chan[i].std.vol.had) { chan[i].outVol=(chan[i].vol*MIN(127,chan[i].std.vol.val))/127; - } + }*/ if (chan[i].std.arp.had) { if (!chan[i].inPorta) { @@ -99,6 +100,20 @@ void DivPlatformSegaPCM::tick(bool sysTick) { chan[i].freqChanged=true; } } + + if (chan[i].std.panL.had) { + chan[i].chVolL=chan[i].std.panL.val&127; + if (dumpWrites) { + addWrite(0x10002+(i<<3),chan[i].chVolL); + } + } + + if (chan[i].std.panR.had) { + chan[i].chVolR=chan[i].std.panR.val&127; + if (dumpWrites) { + addWrite(0x10003+(i<<3),chan[i].chVolR); + } + } if (chan[i].std.pitch.had) { chan[i].freqChanged=true; @@ -143,14 +158,17 @@ int DivPlatformSegaPCM::dispatch(DivCommand c) { if (dumpWrites) { addWrite(0x10086+(c.chan<<3),3); } + chan[c.chan].std.init(NULL); break; } chan[c.chan].pcm.pos=0; if (c.value!=DIV_NOTE_NULL) { + chan[c.chan].note=c.value; chan[c.chan].baseFreq=(c.value<<6); chan[c.chan].freqChanged=true; } chan[c.chan].furnacePCM=true; + chan[c.chan].std.init(ins); if (dumpWrites) { // Sega PCM writes DivSample* s=parent->getSample(chan[c.chan].pcm.sample); addWrite(0x10086+(c.chan<<3),3+((s->offSegaPCM>>16)<<3)); @@ -167,6 +185,7 @@ int DivPlatformSegaPCM::dispatch(DivCommand c) { } } } else { + chan[c.chan].std.init(NULL); if (c.value!=DIV_NOTE_NULL) { chan[c.chan].note=c.value; } @@ -208,6 +227,7 @@ int DivPlatformSegaPCM::dispatch(DivCommand c) { chan[c.chan].keyOff=true; chan[c.chan].keyOn=false; chan[c.chan].active=false; + chan[c.chan].std.init(NULL); break; case DIV_CMD_NOTE_OFF_ENV: chan[c.chan].keyOff=true; diff --git a/src/engine/platform/swan.cpp b/src/engine/platform/swan.cpp index 3b4a955ea..22790c641 100644 --- a/src/engine/platform/swan.cpp +++ b/src/engine/platform/swan.cpp @@ -173,6 +173,17 @@ void DivPlatformSwan::tick(bool sysTick) { chan[i].ws.changeWave1(chan[i].wave); } } + if (chan[i].std.panL.had) { + chan[i].pan&=0x0f; + chan[i].pan|=(chan[i].std.panL.val&15)<<4; + } + if (chan[i].std.panR.had) { + chan[i].pan&=0xf0; + chan[i].pan|=chan[i].std.panR.val&15; + } + if (chan[i].std.panL.had || chan[i].std.panR.had) { + calcAndWriteOutVol(i,chan[i].std.vol.will?chan[i].std.vol.val:15); + } if (chan[i].std.pitch.had) { chan[i].freqChanged=true; } diff --git a/src/engine/platform/vera.cpp b/src/engine/platform/vera.cpp index 99c6f8822..89cccb99d 100644 --- a/src/engine/platform/vera.cpp +++ b/src/engine/platform/vera.cpp @@ -184,6 +184,12 @@ void DivPlatformVERA::tick(bool sysTick) { if (chan[i].std.wave.had) { rWriteHi(i,3,chan[i].std.wave.val); } + if (i<16) { + if (chan[i].std.panL.had) { + chan[i].pan=chan[i].std.panL.val&3; + rWriteHi(i,2,isMuted[i]?0:chan[i].pan); + } + } if (chan[i].std.pitch.had) { chan[i].freqChanged=true; } diff --git a/src/engine/platform/x1_010.cpp b/src/engine/platform/x1_010.cpp index 229d8eab6..9c9b3e20d 100644 --- a/src/engine/platform/x1_010.cpp +++ b/src/engine/platform/x1_010.cpp @@ -372,6 +372,16 @@ void DivPlatformX1_010::tick(bool sysTick) { } } } + if (chan[i].std.panL.had) { + chan[i].pan&=0x0f; + chan[i].pan|=(chan[i].std.panL.val&15)<<4; + chan[i].envChanged=true; + } + if (chan[i].std.panR.had) { + chan[i].pan&=0xf0; + chan[i].pan|=chan[i].std.panR.val&15; + chan[i].envChanged=true; + } if (chan[i].std.pitch.had) { chan[i].freqChanged=true; } diff --git a/src/engine/platform/ym2610.cpp b/src/engine/platform/ym2610.cpp index 6f4a1e46c..f955303d3 100644 --- a/src/engine/platform/ym2610.cpp +++ b/src/engine/platform/ym2610.cpp @@ -408,6 +408,11 @@ void DivPlatformYM2610::tick(bool sysTick) { } } + if (chan[i].std.panL.had) { + chan[i].pan=chan[i].std.panL.val&3; + rWrite(chanOffs[i]+ADDR_LRAF,(isMuted[i]?0:(chan[i].pan<<6))|(chan[i].state.fms&7)|((chan[i].state.ams&3)<<4)); + } + if (chan[i].std.pitch.had) { chan[i].freqChanged=true; } diff --git a/src/engine/platform/ym2610b.cpp b/src/engine/platform/ym2610b.cpp index 04bc096a9..75855f4fc 100644 --- a/src/engine/platform/ym2610b.cpp +++ b/src/engine/platform/ym2610b.cpp @@ -472,6 +472,11 @@ void DivPlatformYM2610B::tick(bool sysTick) { } } + if (chan[i].std.panL.had) { + chan[i].pan=chan[i].std.panL.val&3; + rWrite(chanOffs[i]+ADDR_LRAF,(isMuted[i]?0:(chan[i].pan<<6))|(chan[i].state.fms&7)|((chan[i].state.ams&3)<<4)); + } + if (chan[i].std.pitch.had) { chan[i].freqChanged=true; } diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index d3f297d40..995cc6e31 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -176,7 +176,7 @@ const char* n163UpdateBits[8]={ }; const char* panBits[3]={ - "left", "right", NULL + "right", "left", NULL }; const char* oneBit[2]={ @@ -2826,7 +2826,7 @@ void FurnaceGUI::drawInsEdit() { if (ins->type==DIV_INS_AMIGA) { panMax=127; } - if (ins->type==DIV_INS_X1_010 || ins->type==DIV_INS_PCE) { + if (ins->type==DIV_INS_X1_010 || ins->type==DIV_INS_PCE || ins->type==DIV_INS_MIKEY || ins->type==DIV_INS_SAA1099) { panMax=15; } From b8c790bf79df15d66aa9379c5acc9a742d79149e Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 18 Apr 2022 00:52:29 -0500 Subject: [PATCH 126/342] dev84 - new compat flag and store macro modes --- TODO.md | 3 +-- src/engine/engine.h | 4 ++-- src/engine/fileOps.cpp | 13 ++++++++++-- src/engine/instrument.cpp | 44 +++++++++++++++++++++++++++++++++++++++ src/engine/song.h | 4 +++- src/gui/compatFlags.cpp | 4 ++++ 6 files changed, 65 insertions(+), 7 deletions(-) diff --git a/TODO.md b/TODO.md index 04b724e0a..b6d059502 100644 --- a/TODO.md +++ b/TODO.md @@ -1,8 +1,7 @@ # to-do for 0.6pre1 - panning macro - - single macro for hard-panned chips - - two macros for soft-panned ones + - QSound? - pitch macro - relative mode - test diff --git a/src/engine/engine.h b/src/engine/engine.h index 3ec530f87..114d06d4c 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -42,8 +42,8 @@ #define BUSY_BEGIN_SOFT softLocked=true; isBusy.lock(); #define BUSY_END isBusy.unlock(); softLocked=false; -#define DIV_VERSION "dev83" -#define DIV_ENGINE_VERSION 83 +#define DIV_VERSION "dev84" +#define DIV_ENGINE_VERSION 84 // for imports #define DIV_VERSION_MOD 0xff01 diff --git a/src/engine/fileOps.cpp b/src/engine/fileOps.cpp index 183ff81a1..aee7de71f 100644 --- a/src/engine/fileOps.cpp +++ b/src/engine/fileOps.cpp @@ -990,6 +990,9 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) { ds.ignoreDACModeOutsideIntendedChannel=true; ds.e1e2AlsoTakePriority=false; } + if (ds.version<84) { + ds.newSegaPCM=false; + } ds.isDMF=false; reader.readS(); // reserved @@ -1334,7 +1337,12 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) { reader.readC(); reader.readC(); } - for (int i=0; i<23; i++) { + if (ds.version>=84) { + ds.newSegaPCM=reader.readC(); + } else { + reader.readC(); + } + for (int i=0; i<22; i++) { reader.readC(); } } @@ -2274,7 +2282,8 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) { w->writeC(song.sharedExtStat); w->writeC(song.ignoreDACModeOutsideIntendedChannel); w->writeC(song.e1e2AlsoTakePriority); - for (int i=0; i<23; i++) { + w->writeC(song.newSegaPCM); + for (int i=0; i<22; i++) { w->writeC(0); } diff --git a/src/engine/instrument.cpp b/src/engine/instrument.cpp index b5ed40890..273af9bf7 100644 --- a/src/engine/instrument.cpp +++ b/src/engine/instrument.cpp @@ -482,6 +482,27 @@ void DivInstrument::putInsData(SafeWriter* w) { w->writeC(ws.param2); w->writeC(ws.param3); w->writeC(ws.param4); + + // other macro modes + w->writeC(std.volMacro.mode); + w->writeC(std.dutyMacro.mode); + w->writeC(std.waveMacro.mode); + w->writeC(std.pitchMacro.mode); + w->writeC(std.ex1Macro.mode); + w->writeC(std.ex2Macro.mode); + w->writeC(std.ex3Macro.mode); + w->writeC(std.algMacro.mode); + w->writeC(std.fbMacro.mode); + w->writeC(std.fmsMacro.mode); + w->writeC(std.amsMacro.mode); + w->writeC(std.panLMacro.mode); + w->writeC(std.panRMacro.mode); + w->writeC(std.phaseResetMacro.mode); + w->writeC(std.ex4Macro.mode); + w->writeC(std.ex5Macro.mode); + w->writeC(std.ex6Macro.mode); + w->writeC(std.ex7Macro.mode); + w->writeC(std.ex8Macro.mode); } DivDataErrors DivInstrument::readInsData(SafeReader& reader, short version) { @@ -954,6 +975,29 @@ DivDataErrors DivInstrument::readInsData(SafeReader& reader, short version) { ws.param4=reader.readC(); } + // other macro modes + if (version>=84) { + std.volMacro.mode=reader.readC(); + std.dutyMacro.mode=reader.readC(); + std.waveMacro.mode=reader.readC(); + std.pitchMacro.mode=reader.readC(); + std.ex1Macro.mode=reader.readC(); + std.ex2Macro.mode=reader.readC(); + std.ex3Macro.mode=reader.readC(); + std.algMacro.mode=reader.readC(); + std.fbMacro.mode=reader.readC(); + std.fmsMacro.mode=reader.readC(); + std.amsMacro.mode=reader.readC(); + std.panLMacro.mode=reader.readC(); + std.panRMacro.mode=reader.readC(); + std.phaseResetMacro.mode=reader.readC(); + std.ex4Macro.mode=reader.readC(); + std.ex5Macro.mode=reader.readC(); + std.ex6Macro.mode=reader.readC(); + std.ex7Macro.mode=reader.readC(); + std.ex8Macro.mode=reader.readC(); + } + return DIV_DATA_SUCCESS; } diff --git a/src/engine/song.h b/src/engine/song.h index 6cda7c533..81c0cb8a8 100644 --- a/src/engine/song.h +++ b/src/engine/song.h @@ -323,6 +323,7 @@ struct DivSong { bool sharedExtStat; bool ignoreDACModeOutsideIntendedChannel; bool e1e2AlsoTakePriority; + bool newSegaPCM; DivOrders orders; std::vector ins; @@ -404,7 +405,8 @@ struct DivSong { gbInsAffectsEnvelope(true), sharedExtStat(true), ignoreDACModeOutsideIntendedChannel(false), - e1e2AlsoTakePriority(false) { + e1e2AlsoTakePriority(false), + newSegaPCM(true) { for (int i=0; i<32; i++) { system[i]=DIV_SYSTEM_NULL; systemVol[i]=64; diff --git a/src/gui/compatFlags.cpp b/src/gui/compatFlags.cpp index 8a45741f8..d2035ff70 100644 --- a/src/gui/compatFlags.cpp +++ b/src/gui/compatFlags.cpp @@ -166,6 +166,10 @@ void FurnaceGUI::drawCompatFlags() { if (ImGui::IsItemHovered()) { ImGui::SetTooltip("behavior changed in 0.6"); } + ImGui::Checkbox("New SegaPCM features (macros and better panning)",&e->song.newSegaPCM); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("behavior changed in 0.6"); + } } if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_COMPAT_FLAGS; ImGui::End(); From 744c5982e12ef1c7dc4f0a628cd42770f779aa26 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 18 Apr 2022 01:31:03 -0500 Subject: [PATCH 127/342] bring on backward-cpp cross your fingers --- .gitmodules | 3 +++ CMakeLists.txt | 2 +- extern/backward | 1 + src/main.cpp | 4 ++++ 4 files changed, 9 insertions(+), 1 deletion(-) create mode 160000 extern/backward diff --git a/.gitmodules b/.gitmodules index d63fd70b5..a1c7da6d6 100644 --- a/.gitmodules +++ b/.gitmodules @@ -22,3 +22,6 @@ [submodule "extern/Nuked-OPL3"] path = extern/Nuked-OPL3 url = https://github.com/nukeykt/Nuked-OPL3.git +[submodule "extern/backward"] + path = extern/backward + url = https://github.com/bombela/backward-cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 498f3df69..3d6b9834e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -442,7 +442,7 @@ else() endif() if (WIN32) - list(APPEND DEPENDENCIES_LIBRARIES shlwapi) + list(APPEND DEPENDENCIES_LIBRARIES shlwapi imagehlp msvcr90) if (NOT MSVC) list(APPEND DEPENDENCIES_LIBRARIES -static) endif() diff --git a/extern/backward b/extern/backward new file mode 160000 index 000000000..5ffb2c879 --- /dev/null +++ b/extern/backward @@ -0,0 +1 @@ +Subproject commit 5ffb2c879ebdbea3bdb8477c671e32b1c984beaa diff --git a/src/main.cpp b/src/main.cpp index 8f41b10cb..29018b8d5 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -39,6 +39,8 @@ #include "gui/gui.h" #endif +#include "../extern/backward/backward.hpp" + DivEngine e; #ifdef HAVE_GUI @@ -251,6 +253,8 @@ int main(int argc, char** argv) { outName=""; vgmOutName=""; + backward::SignalHandling crashHandler; + initParams(); // parse arguments From 4b0ffcafe834ccdf83e3ecc29fcecd1f8e209e8c Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 18 Apr 2022 01:40:26 -0500 Subject: [PATCH 128/342] take 2 --- src/main.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 29018b8d5..d7c2c194e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -17,6 +17,8 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#include "../extern/backward/backward.hpp" + #include #include #include @@ -39,8 +41,6 @@ #include "gui/gui.h" #endif -#include "../extern/backward/backward.hpp" - DivEngine e; #ifdef HAVE_GUI From 70ff71af44deb19f8d4d35e95e11d82e13f311b5 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 18 Apr 2022 01:49:28 -0500 Subject: [PATCH 129/342] take 3 --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3d6b9834e..9147beb66 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -442,9 +442,9 @@ else() endif() if (WIN32) - list(APPEND DEPENDENCIES_LIBRARIES shlwapi imagehlp msvcr90) + list(APPEND DEPENDENCIES_LIBRARIES shlwapi imagehlp) if (NOT MSVC) - list(APPEND DEPENDENCIES_LIBRARIES -static) + list(APPEND DEPENDENCIES_LIBRARIES msvcr90 -static) endif() endif() From 925b1c21798791cdf081070b2938f00d78a22f32 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 18 Apr 2022 01:56:26 -0500 Subject: [PATCH 130/342] i'm done here --- .github/workflows/build.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 293c4cd0f..13ef95c74 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -187,6 +187,8 @@ jobs: export USE_WAE=ON export CMAKE_EXTRA_ARGS=() if [ '${{ matrix.config.compiler }}' == 'msvc' ]; then + # 1. Go to hell + export USE_WAE=OFF CMAKE_EXTRA_ARGS+=('-DCMAKE_GENERATOR_PLATFORM=${{ steps.windows-identify.outputs.msvc-target }}') # Force static linking From d4f58cb6ea6538bed96a612b60643477169e732f Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 18 Apr 2022 02:09:37 -0500 Subject: [PATCH 131/342] the final take (hopefully) --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9147beb66..c8278959b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -444,7 +444,7 @@ endif() if (WIN32) list(APPEND DEPENDENCIES_LIBRARIES shlwapi imagehlp) if (NOT MSVC) - list(APPEND DEPENDENCIES_LIBRARIES msvcr90 -static) + list(APPEND DEPENDENCIES_LIBRARIES msvcr90 dbghelp -static) endif() endif() From eafbec6cfcd176a7b2320e342bf1b50c34e47cfa Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 18 Apr 2022 02:16:57 -0500 Subject: [PATCH 132/342] really what the heck --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c8278959b..950dc4ad5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -444,7 +444,7 @@ endif() if (WIN32) list(APPEND DEPENDENCIES_LIBRARIES shlwapi imagehlp) if (NOT MSVC) - list(APPEND DEPENDENCIES_LIBRARIES msvcr90 dbghelp -static) + list(APPEND DEPENDENCIES_LIBRARIES msvcr90 psapi -static) endif() endif() From 4d6fe8f0eae746dc200ec7d9bd2f974880b5a310 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 18 Apr 2022 03:15:39 -0500 Subject: [PATCH 133/342] get rid of backward thanks for the waste of time --- .gitmodules | 3 --- extern/backward | 1 - 2 files changed, 4 deletions(-) delete mode 160000 extern/backward diff --git a/.gitmodules b/.gitmodules index a1c7da6d6..d63fd70b5 100644 --- a/.gitmodules +++ b/.gitmodules @@ -22,6 +22,3 @@ [submodule "extern/Nuked-OPL3"] path = extern/Nuked-OPL3 url = https://github.com/nukeykt/Nuked-OPL3.git -[submodule "extern/backward"] - path = extern/backward - url = https://github.com/bombela/backward-cpp diff --git a/extern/backward b/extern/backward deleted file mode 160000 index 5ffb2c879..000000000 --- a/extern/backward +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 5ffb2c879ebdbea3bdb8477c671e32b1c984beaa From cc08dd895b43a698193ab3c1b956eb970dc28730 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 18 Apr 2022 03:17:11 -0500 Subject: [PATCH 134/342] get rid of backward completely for real --- CMakeLists.txt | 4 ++-- src/main.cpp | 5 +---- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 950dc4ad5..498f3df69 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -442,9 +442,9 @@ else() endif() if (WIN32) - list(APPEND DEPENDENCIES_LIBRARIES shlwapi imagehlp) + list(APPEND DEPENDENCIES_LIBRARIES shlwapi) if (NOT MSVC) - list(APPEND DEPENDENCIES_LIBRARIES msvcr90 psapi -static) + list(APPEND DEPENDENCIES_LIBRARIES -static) endif() endif() diff --git a/src/main.cpp b/src/main.cpp index d7c2c194e..4a3e9d0e2 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -17,8 +17,6 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#include "../extern/backward/backward.hpp" - #include #include #include @@ -242,6 +240,7 @@ void initParams() { } // TODO: CoInitializeEx on Windows? +// TODO: add crash log int main(int argc, char** argv) { initLog(); #if !(defined(__APPLE__) || defined(_WIN32)) @@ -253,8 +252,6 @@ int main(int argc, char** argv) { outName=""; vgmOutName=""; - backward::SignalHandling crashHandler; - initParams(); // parse arguments From afa59a27ff0567a2c608e4a6f5f8a1e98a617008 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 18 Apr 2022 04:16:59 -0500 Subject: [PATCH 135/342] fix note preview for ADPCM-A --- src/engine/engine.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 6d0d4df0e..88f50da92 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -1971,7 +1971,7 @@ void DivEngine::autoNoteOn(int ch, int ins, int note, int vol) { } do { - if ((ins==-1 || getPreferInsType(finalChan)==getIns(ins)->type || getIns(ins)->type==DIV_INS_AMIGA) && chan[finalChan].midiNote==-1) { + if ((ins==-1 || getChannelType(finalChan)==4 || getPreferInsType(finalChan)==getIns(ins)->type || getIns(ins)->type==DIV_INS_AMIGA) && chan[finalChan].midiNote==-1) { chan[finalChan].midiNote=note; pendingNotes.push(DivNoteEvent(finalChan,ins,note,vol,true)); break; From a4ff0c3876970b11e39208daeff60817a3cf2999 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 18 Apr 2022 04:18:33 -0500 Subject: [PATCH 136/342] GUI: fix relative/fixed toggle label being wrong --- src/gui/insEdit.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index 995cc6e31..baca9469e 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -1082,7 +1082,7 @@ void FurnaceGUI::drawGBEnv(unsigned char vol, unsigned char len, unsigned char s } \ if (macroMode) { \ bool modeVal=macro.mode; \ - if (ImGui::Checkbox("Relative##IMacroMode_" macroName,&modeVal)) { \ + if (ImGui::Checkbox("Fixed##IMacroMode_" macroName,&modeVal)) { \ macro.mode=modeVal; \ } \ } \ @@ -1176,7 +1176,7 @@ void FurnaceGUI::drawGBEnv(unsigned char vol, unsigned char len, unsigned char s } \ if (macroMode) { \ bool modeVal=macro.mode; \ - if (ImGui::Checkbox("Relative##IOPMacroMode_" macroName,&modeVal)) { \ + if (ImGui::Checkbox("Fixed##IOPMacroMode_" macroName,&modeVal)) { \ macro.mode=modeVal; \ } \ } \ From cc5013f3dc8c33f9f508e7b165a7d1f41f6cd5df Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 18 Apr 2022 04:42:51 -0500 Subject: [PATCH 137/342] possibly fix OPl3 per-channel audio export --- src/engine/engine.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 88f50da92..2597e1191 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -397,7 +397,7 @@ void DivEngine::runExportThread() { if (getChannelType(i)==5) { i++; while (true) { - if (++i>=chans) break; + if (i>=chans) break; if (getChannelType(i)!=5) break; } i--; From f550bd82ddc24c509feae47fb2e5d02e728a84a4 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 18 Apr 2022 16:21:00 -0500 Subject: [PATCH 138/342] possibly fix #373 --- src/engine/platform/amiga.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/engine/platform/amiga.cpp b/src/engine/platform/amiga.cpp index 104e66d45..6d015db48 100644 --- a/src/engine/platform/amiga.cpp +++ b/src/engine/platform/amiga.cpp @@ -108,7 +108,9 @@ void DivPlatformAmiga::acquire(short* bufL, short* bufR, size_t start, size_t le } else { DivSample* s=parent->getSample(chan[i].sample); if (s->samples>0) { - writeAudDat(s->data8[chan[i].audPos++]); + if (chan[i].audPossamples) { + writeAudDat(s->data8[chan[i].audPos++]); + } if (chan[i].audPos>=s->samples || chan[i].audPos>=131071) { if (s->loopStart>=0 && s->loopStart<(int)s->samples) { chan[i].audPos=s->loopStart; From 7767881ca7b4c214304e36e49c4e98913dbc0709 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Tue, 19 Apr 2022 13:59:17 -0500 Subject: [PATCH 139/342] VRC6: fix saw column not yielding saw type ins fixes #374 --- src/engine/sysDef.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/engine/sysDef.cpp b/src/engine/sysDef.cpp index 084504fe1..acd3cff25 100644 --- a/src/engine/sysDef.cpp +++ b/src/engine/sysDef.cpp @@ -1123,7 +1123,7 @@ const DivInstrumentType chanPrefType[47][28]={ {DIV_INS_VIC, DIV_INS_VIC, DIV_INS_VIC, DIV_INS_VIC}, // VIC-20 {DIV_INS_PET}, // PET {DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA}, // SNES/N163/RF5C68 - {DIV_INS_VRC6, DIV_INS_VRC6, DIV_INS_VRC6}, // VRC6 + {DIV_INS_VRC6, DIV_INS_VRC6, DIV_INS_VRC6_SAW}, // VRC6 {DIV_INS_OPLL, DIV_INS_OPLL, DIV_INS_OPLL, DIV_INS_OPLL, DIV_INS_OPLL, DIV_INS_OPLL, DIV_INS_OPLL, DIV_INS_OPLL, DIV_INS_OPLL}, // OPLL/VRC7 {DIV_INS_FDS}, // FDS {DIV_INS_STD, DIV_INS_STD, DIV_INS_AMIGA}, // MMC5 From b6026e76c538f680fbf6de75b4ef4d3e5a35d87b Mon Sep 17 00:00:00 2001 From: tildearrow Date: Tue, 19 Apr 2022 18:10:43 -0500 Subject: [PATCH 140/342] SMS: fix noise pitch being odd on linear pitch off issue #379 --- src/engine/platform/sms.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/engine/platform/sms.cpp b/src/engine/platform/sms.cpp index d755e84f6..da31e4784 100644 --- a/src/engine/platform/sms.cpp +++ b/src/engine/platform/sms.cpp @@ -23,8 +23,6 @@ #define rWrite(v) {if (!skipRegisterWrites) {sn->write(v); if (dumpWrites) {addWrite(0x200,v);}}} -#define CHIP_DIVIDER 64 - const char* regCheatSheetSN[]={ "DATA", "0", NULL @@ -55,6 +53,8 @@ int DivPlatformSMS::acquireOne() { void DivPlatformSMS::tick(bool sysTick) { for (int i=0; i<4; i++) { + int CHIP_DIVIDER=64; + if (i==3 && isRealSN) CHIP_DIVIDER=60; chan[i].std.next(); if (chan[i].std.vol.had) { chan[i].outVol=MIN(15,chan[i].std.vol.val)-(15-(chan[i].vol&15)); @@ -119,8 +119,7 @@ void DivPlatformSMS::tick(bool sysTick) { } } if (chan[3].freqChanged || updateSNMode) { - // seems arbitrary huh? - chan[3].freq=parent->calcFreq(chan[3].baseFreq,chan[3].pitch-1-(isRealSN?127:0),true)+chan[3].std.pitch.val; + chan[3].freq=parent->calcFreq(chan[3].baseFreq,chan[3].pitch,true)+chan[3].std.pitch.val; if (chan[3].freq>1023) chan[3].freq=1023; if (chan[3].actualNote>0x5d) chan[3].freq=0x01; if (snNoiseMode&2) { // take period from channel 3 @@ -164,6 +163,8 @@ void DivPlatformSMS::tick(bool sysTick) { } int DivPlatformSMS::dispatch(DivCommand c) { + int CHIP_DIVIDER=64; + if (c.chan==3 && isRealSN) CHIP_DIVIDER=60; switch (c.cmd) { case DIV_CMD_NOTE_ON: if (c.value!=DIV_NOTE_NULL) { From d4380e152451dab453c776aedcc865ad436704c8 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Tue, 19 Apr 2022 18:44:05 -0500 Subject: [PATCH 141/342] GUI: add an effect list window --- CMakeLists.txt | 1 + src/engine/engine.cpp | 7 ++-- src/engine/engine.h | 2 +- src/gui/doAction.cpp | 9 ++++++ src/gui/effectList.cpp | 73 ++++++++++++++++++++++++++++++++++++++++++ src/gui/gui.cpp | 9 +++++- src/gui/gui.h | 9 ++++-- src/gui/guiConst.cpp | 57 ++++++++++++++++++++++++++++++++- src/gui/guiConst.h | 4 ++- src/gui/log.cpp | 1 + src/gui/pattern.cpp | 54 ------------------------------- 11 files changed, 161 insertions(+), 65 deletions(-) create mode 100644 src/gui/effectList.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 498f3df69..623b6cc35 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -393,6 +393,7 @@ src/gui/debugWindow.cpp src/gui/doAction.cpp src/gui/editing.cpp src/gui/editControls.cpp +src/gui/effectList.cpp src/gui/insEdit.cpp src/gui/log.cpp src/gui/mixer.cpp diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 2597e1191..488b4bf63 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -41,7 +41,7 @@ void process(void* u, float** in, float** out, int inChans, int outChans, unsign ((DivEngine*)u)->nextBuf(in,out,inChans,outChans,size); } -const char* DivEngine::getEffectDesc(unsigned char effect, int chan) { +const char* DivEngine::getEffectDesc(unsigned char effect, int chan, bool notNull) { switch (effect) { case 0x00: return "00xy: Arpeggio"; @@ -116,14 +116,13 @@ const char* DivEngine::getEffectDesc(unsigned char effect, int chan) { default: if ((effect&0xf0)==0x90) { return "9xxx: Set sample offset*256"; - } - else if (chan>=0 && chan=0 && changetEffectName(effect); if (ret!=NULL) return ret; } break; } - return "Invalid effect"; + return notNull?"Invalid effect":NULL; } void DivEngine::walkSong(int& loopOrder, int& loopRow, int& loopEnd) { diff --git a/src/engine/engine.h b/src/engine/engine.h index 114d06d4c..efcbb21b0 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -402,7 +402,7 @@ class DivEngine { int getTotalChannelCount(); // get effect description - const char* getEffectDesc(unsigned char effect, int chan); + const char* getEffectDesc(unsigned char effect, int chan, bool notNull=false); // get channel type // - 0: FM diff --git a/src/gui/doAction.cpp b/src/gui/doAction.cpp index b68d684d3..fc0dbef90 100644 --- a/src/gui/doAction.cpp +++ b/src/gui/doAction.cpp @@ -213,6 +213,9 @@ void FurnaceGUI::doAction(int what) { case GUI_ACTION_WINDOW_LOG: nextWindow=GUI_WINDOW_LOG; break; + case GUI_ACTION_WINDOW_EFFECT_LIST: + nextWindow=GUI_WINDOW_EFFECT_LIST; + break; case GUI_ACTION_COLLAPSE_WINDOW: collapseWindow=true; @@ -285,6 +288,12 @@ void FurnaceGUI::doAction(int what) { case GUI_WINDOW_REGISTER_VIEW: regViewOpen=false; break; + case GUI_WINDOW_LOG: + logOpen=false; + break; + case GUI_WINDOW_EFFECT_LIST: + effectListOpen=false; + break; default: break; } diff --git a/src/gui/effectList.cpp b/src/gui/effectList.cpp new file mode 100644 index 000000000..c64966a43 --- /dev/null +++ b/src/gui/effectList.cpp @@ -0,0 +1,73 @@ +#include "gui.h" +#include "guiConst.h" +#include + +void FurnaceGUI::drawEffectList() { + if (nextWindow==GUI_WINDOW_EFFECT_LIST) { + effectListOpen=true; + ImGui::SetNextWindowFocus(); + nextWindow=GUI_WINDOW_NOTHING; + } + if (!effectListOpen) return; + if (ImGui::Begin("Effect List",&effectListOpen)) { + ImGui::Text("System at cursor: %s",e->getSystemName(e->sysOfChan[cursor.xCoarse])); + if (ImGui::BeginTable("effectList",2)) { + ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthFixed); + ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthStretch); + + ImGui::TableNextRow(ImGuiTableRowFlags_Headers); + ImGui::TableNextColumn(); + ImGui::Text("Name"); + ImGui::TableNextColumn(); + ImGui::Text("Description"); + + const char* prevName=NULL; + for (int i=0; i<256; i++) { + const char* name=e->getEffectDesc(i,cursor.xCoarse); + if (name==prevName) { + continue; + } + prevName=name; + if (name!=NULL) { + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::PushFont(patFont); + if (i<0x10) { + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[fxColors[i]]); + } else if (i<0x20) { + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY]); + } else if (i<0x30) { + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_SYS_SECONDARY]); + } else if (i<0x48) { + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY]); + } else if (i<0x90) { + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_INVALID]); + } else if (i<0xa0) { + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_MISC]); + } else if (i<0xc0) { + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_INVALID]); + } else if (i<0xd0) { + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_SPEED]); + } else if (i<0xe0) { + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_INVALID]); + } else { + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[extFxColors[i-0xe0]]); + } + ImGui::Text("%c%c%c%c",name[0],name[1],name[2],name[3]); + ImGui::PopStyleColor(); + ImGui::PopFont(); + + ImGui::TableNextColumn(); + if (strlen(name)>6) { + ImGui::TextWrapped("%s",&name[6]); + } else { + ImGui::Text("ERROR"); + } + } + } + ImGui::EndTable(); + } + } + if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_EFFECT_LIST; + ImGui::End(); +} \ No newline at end of file diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index f3ab5a327..63e0594d0 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -2523,6 +2523,7 @@ bool FurnaceGUI::loop() { ImGui::EndMenu(); } if (ImGui::BeginMenu("help")) { + if (ImGui::MenuItem("effect list",BIND_FOR(GUI_ACTION_WINDOW_EFFECT_LIST),effectListOpen)) effectListOpen=!effectListOpen; if (ImGui::MenuItem("debug menu",BIND_FOR(GUI_ACTION_WINDOW_DEBUG))) debugOpen=!debugOpen; if (ImGui::MenuItem("panic",BIND_FOR(GUI_ACTION_PANIC))) e->syncReset(); if (ImGui::MenuItem("about...",BIND_FOR(GUI_ACTION_WINDOW_ABOUT))) { @@ -2581,7 +2582,7 @@ bool FurnaceGUI::loop() { default: // effect int actualCursor=((cursor.xFine+1)&(~1)); if (p->data[cursor.y][actualCursor]>-1) { - info=e->getEffectDesc(p->data[cursor.y][actualCursor],cursor.xCoarse); + info=e->getEffectDesc(p->data[cursor.y][actualCursor],cursor.xCoarse,true); hasInfo=true; } break; @@ -2626,6 +2627,7 @@ bool FurnaceGUI::loop() { drawChannels(); drawRegView(); drawLog(); + drawEffectList(); if (inspectorOpen) ImGui::ShowMetricsWindow(&inspectorOpen); @@ -3234,6 +3236,7 @@ bool FurnaceGUI::init() { channelsOpen=e->getConfBool("channelsOpen",false); regViewOpen=e->getConfBool("regViewOpen",false); logOpen=e->getConfBool("logOpen",false); + effectListOpen=e->getConfBool("effectListOpen",false); tempoView=e->getConfBool("tempoView",true); waveHex=e->getConfBool("waveHex",false); @@ -3404,6 +3407,7 @@ bool FurnaceGUI::finish() { e->setConf("channelsOpen",channelsOpen); e->setConf("regViewOpen",regViewOpen); e->setConf("logOpen",logOpen); + e->setConf("effectListOpen",effectListOpen); // commit last window size e->setConf("lastWindowWidth",scrW); @@ -3510,6 +3514,7 @@ FurnaceGUI::FurnaceGUI(): channelsOpen(false), regViewOpen(false), logOpen(false), + effectListOpen(false), /* editControlsDocked(false), ordersDocked(false), @@ -3534,6 +3539,8 @@ FurnaceGUI::FurnaceGUI(): notesDocked(false), channelsDocked(false), regViewDocked(false), + logDocked(false), + effectListDocked(false), */ selecting(false), curNibble(false), diff --git a/src/gui/gui.h b/src/gui/gui.h index b193e39a5..88ced6b69 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -225,7 +225,8 @@ enum FurnaceGUIWindows { GUI_WINDOW_NOTES, GUI_WINDOW_CHANNELS, GUI_WINDOW_REGISTER_VIEW, - GUI_WINDOW_LOG + GUI_WINDOW_LOG, + GUI_WINDOW_EFFECT_LIST }; enum FurnaceGUIFileDialogs { @@ -323,6 +324,7 @@ enum FurnaceGUIActions { GUI_ACTION_WINDOW_CHANNELS, GUI_ACTION_WINDOW_REGISTER_VIEW, GUI_ACTION_WINDOW_LOG, + GUI_ACTION_WINDOW_EFFECT_LIST, GUI_ACTION_COLLAPSE_WINDOW, GUI_ACTION_CLOSE_WINDOW, @@ -915,13 +917,13 @@ class FurnaceGUI { bool editControlsOpen, ordersOpen, insListOpen, songInfoOpen, patternOpen, insEditOpen; bool waveListOpen, waveEditOpen, sampleListOpen, sampleEditOpen, aboutOpen, settingsOpen; bool mixerOpen, debugOpen, inspectorOpen, oscOpen, volMeterOpen, statsOpen, compatFlagsOpen; - bool pianoOpen, notesOpen, channelsOpen, regViewOpen, logOpen; + bool pianoOpen, notesOpen, channelsOpen, regViewOpen, logOpen, effectListOpen; /* there ought to be a better way... bool editControlsDocked, ordersDocked, insListDocked, songInfoDocked, patternDocked, insEditDocked; bool waveListDocked, waveEditDocked, sampleListDocked, sampleEditDocked, aboutDocked, settingsDocked; bool mixerDocked, debugDocked, inspectorDocked, oscDocked, volMeterDocked, statsDocked, compatFlagsDocked; - bool pianoDocked, notesDocked, channelsDocked, regViewDocked; + bool pianoDocked, notesDocked, channelsDocked, regViewDocked, logDocked, effectListDocked; */ SelectionPoint selStart, selEnd, cursor; @@ -1128,6 +1130,7 @@ class FurnaceGUI { void drawDebug(); void drawNewSong(); void drawLog(); + void drawEffectList(); void parseKeybinds(); void promptKey(int which); diff --git a/src/gui/guiConst.cpp b/src/gui/guiConst.cpp index a0b3c17c4..99068e95f 100644 --- a/src/gui/guiConst.cpp +++ b/src/gui/guiConst.cpp @@ -18,8 +18,8 @@ */ // guiConst: constants used in the GUI like arrays, strings and other stuff -#include "guiConst.h" #include "gui.h" +#include "guiConst.h" #include "../engine/song.h" const int opOrder[4]={ @@ -138,6 +138,60 @@ const char* resampleStrats[]={ "best possible" }; +const FurnaceGUIColors fxColors[16]={ + GUI_COLOR_PATTERN_EFFECT_MISC, // 00 + GUI_COLOR_PATTERN_EFFECT_PITCH, // 01 + GUI_COLOR_PATTERN_EFFECT_PITCH, // 02 + GUI_COLOR_PATTERN_EFFECT_PITCH, // 03 + GUI_COLOR_PATTERN_EFFECT_PITCH, // 04 + GUI_COLOR_PATTERN_EFFECT_VOLUME, // 05 + GUI_COLOR_PATTERN_EFFECT_VOLUME, // 06 + GUI_COLOR_PATTERN_EFFECT_VOLUME, // 07 + GUI_COLOR_PATTERN_EFFECT_PANNING, // 08 + GUI_COLOR_PATTERN_EFFECT_SPEED, // 09 + GUI_COLOR_PATTERN_EFFECT_VOLUME, // 0A + GUI_COLOR_PATTERN_EFFECT_SONG, // 0B + GUI_COLOR_PATTERN_EFFECT_TIME, // 0C + GUI_COLOR_PATTERN_EFFECT_SONG, // 0D + GUI_COLOR_PATTERN_EFFECT_INVALID, // 0E + GUI_COLOR_PATTERN_EFFECT_SPEED, // 0F +}; + +const FurnaceGUIColors extFxColors[32]={ + GUI_COLOR_PATTERN_EFFECT_MISC, // E0 + GUI_COLOR_PATTERN_EFFECT_PITCH, // E1 + GUI_COLOR_PATTERN_EFFECT_PITCH, // E2 + 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, // EA + GUI_COLOR_PATTERN_EFFECT_MISC, // EB + GUI_COLOR_PATTERN_EFFECT_TIME, // EC + GUI_COLOR_PATTERN_EFFECT_TIME, // ED + GUI_COLOR_PATTERN_EFFECT_SONG, // EE + GUI_COLOR_PATTERN_EFFECT_SONG, // EF + GUI_COLOR_PATTERN_EFFECT_SPEED, // F0 + GUI_COLOR_PATTERN_EFFECT_PITCH, // F1 + GUI_COLOR_PATTERN_EFFECT_PITCH, // F2 + GUI_COLOR_PATTERN_EFFECT_VOLUME, // F3 + GUI_COLOR_PATTERN_EFFECT_VOLUME, // F4 + GUI_COLOR_PATTERN_EFFECT_INVALID, // F5 + GUI_COLOR_PATTERN_EFFECT_INVALID, // F6 + GUI_COLOR_PATTERN_EFFECT_INVALID, // 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_INVALID, // FD + GUI_COLOR_PATTERN_EFFECT_INVALID, // FE + GUI_COLOR_PATTERN_EFFECT_SONG, // FF +}; + #define D FurnaceGUIActionDef #define NOT_AN_ACTION -1 @@ -193,6 +247,7 @@ const FurnaceGUIActionDef guiActions[GUI_ACTION_MAX]={ D("WINDOW_CHANNELS", "Channels", 0), D("WINDOW_REGISTER_VIEW", "Register View", 0), D("WINDOW_LOG", "Log Viewer", 0), + D("EFFECT_LIST", "Effect List", 0), D("COLLAPSE_WINDOW", "Collapse/expand current window", 0), D("CLOSE_WINDOW", "Close current window", FURKMOD_SHIFT|SDLK_ESCAPE), diff --git a/src/gui/guiConst.h b/src/gui/guiConst.h index 93dd143a0..c0a8cd6c3 100644 --- a/src/gui/guiConst.h +++ b/src/gui/guiConst.h @@ -46,4 +46,6 @@ extern const int availableSystems[]; extern const FurnaceGUIActionDef guiActions[]; extern const FurnaceGUIColorDef guiColors[]; extern const int altValues[24]; -extern const int vgmVersions[6]; \ No newline at end of file +extern const int vgmVersions[6]; +extern const FurnaceGUIColors fxColors[16]; +extern const FurnaceGUIColors extFxColors[32]; \ No newline at end of file diff --git a/src/gui/log.cpp b/src/gui/log.cpp index 7dc6bf8d8..b5f4d3556 100644 --- a/src/gui/log.cpp +++ b/src/gui/log.cpp @@ -92,5 +92,6 @@ void FurnaceGUI::drawLog() { ImGui::EndTable(); } } + if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_LOG; ImGui::End(); } diff --git a/src/gui/pattern.cpp b/src/gui/pattern.cpp index ab0eec099..6f357bd8b 100644 --- a/src/gui/pattern.cpp +++ b/src/gui/pattern.cpp @@ -27,60 +27,6 @@ #include "guiConst.h" #include -const FurnaceGUIColors fxColors[16]={ - GUI_COLOR_PATTERN_EFFECT_MISC, // 00 - GUI_COLOR_PATTERN_EFFECT_PITCH, // 01 - GUI_COLOR_PATTERN_EFFECT_PITCH, // 02 - GUI_COLOR_PATTERN_EFFECT_PITCH, // 03 - GUI_COLOR_PATTERN_EFFECT_PITCH, // 04 - GUI_COLOR_PATTERN_EFFECT_VOLUME, // 05 - GUI_COLOR_PATTERN_EFFECT_VOLUME, // 06 - GUI_COLOR_PATTERN_EFFECT_VOLUME, // 07 - GUI_COLOR_PATTERN_EFFECT_PANNING, // 08 - GUI_COLOR_PATTERN_EFFECT_SPEED, // 09 - GUI_COLOR_PATTERN_EFFECT_VOLUME, // 0A - GUI_COLOR_PATTERN_EFFECT_SONG, // 0B - GUI_COLOR_PATTERN_EFFECT_TIME, // 0C - GUI_COLOR_PATTERN_EFFECT_SONG, // 0D - GUI_COLOR_PATTERN_EFFECT_INVALID, // 0E - GUI_COLOR_PATTERN_EFFECT_SPEED, // 0F -}; - -const FurnaceGUIColors extFxColors[32]={ - GUI_COLOR_PATTERN_EFFECT_MISC, // E0 - GUI_COLOR_PATTERN_EFFECT_PITCH, // E1 - GUI_COLOR_PATTERN_EFFECT_PITCH, // E2 - 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, // EA - GUI_COLOR_PATTERN_EFFECT_MISC, // EB - GUI_COLOR_PATTERN_EFFECT_TIME, // EC - GUI_COLOR_PATTERN_EFFECT_TIME, // ED - GUI_COLOR_PATTERN_EFFECT_SONG, // EE - GUI_COLOR_PATTERN_EFFECT_SONG, // EF - GUI_COLOR_PATTERN_EFFECT_SPEED, // F0 - GUI_COLOR_PATTERN_EFFECT_PITCH, // F1 - GUI_COLOR_PATTERN_EFFECT_PITCH, // F2 - GUI_COLOR_PATTERN_EFFECT_VOLUME, // F3 - GUI_COLOR_PATTERN_EFFECT_VOLUME, // F4 - GUI_COLOR_PATTERN_EFFECT_INVALID, // F5 - GUI_COLOR_PATTERN_EFFECT_INVALID, // F6 - GUI_COLOR_PATTERN_EFFECT_INVALID, // 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_INVALID, // FD - GUI_COLOR_PATTERN_EFFECT_INVALID, // FE - GUI_COLOR_PATTERN_EFFECT_SONG, // FF -}; - inline float randRange(float min, float max) { return min+((float)rand()/(float)RAND_MAX)*(max-min); } From 1a81c6c3e7d7936417f8889cb1745d21211acc2e Mon Sep 17 00:00:00 2001 From: tildearrow Date: Tue, 19 Apr 2022 21:55:13 -0500 Subject: [PATCH 142/342] VRC6: saw volume 63 --- src/engine/platform/vrc6.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/engine/platform/vrc6.cpp b/src/engine/platform/vrc6.cpp index d8b37d92f..a35ed995b 100644 --- a/src/engine/platform/vrc6.cpp +++ b/src/engine/platform/vrc6.cpp @@ -434,9 +434,9 @@ void DivPlatformVRC6::reset() { chan[i]=DivPlatformVRC6::Channel(); chan[i].std.setEngine(parent); } - // a poll may be necessary to decide the default - chan[2].vol=30; - chan[2].outVol=30; + // HELP + chan[2].vol=63; + chan[2].outVol=63; if (dumpWrites) { addWrite(0xffffffff,0); } From dac623157471d95c872eae9cdd40e7338f6f0492 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Tue, 19 Apr 2022 21:55:23 -0500 Subject: [PATCH 143/342] GUI: fix about screen in power saving mode --- src/gui/about.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/gui/about.cpp b/src/gui/about.cpp index c15f3268c..ce543f5f4 100644 --- a/src/gui/about.cpp +++ b/src/gui/about.cpp @@ -214,6 +214,8 @@ void FurnaceGUI::drawAbout() { while (aboutHue>1) aboutHue--; while (aboutSin>=2400) aboutSin-=2400; if (aboutScroll>(42*aboutCount+scrH)) aboutScroll=-20; + + WAKE_UP; } if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_ABOUT; ImGui::End(); From ce5f3fd94ef5cf726bd7bccf1f486082e353bbab Mon Sep 17 00:00:00 2001 From: cam900 Date: Wed, 20 Apr 2022 20:38:20 +0900 Subject: [PATCH 144/342] Further fix looped sample preview --- src/gui/sampleEdit.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/gui/sampleEdit.cpp b/src/gui/sampleEdit.cpp index 9919c069f..802e10bc5 100644 --- a/src/gui/sampleEdit.cpp +++ b/src/gui/sampleEdit.cpp @@ -1137,7 +1137,8 @@ void FurnaceGUI::drawSampleEdit() { ImU32 centerLineColor=ImAlphaBlendColors(bgColor,ImGui::GetColorU32(ImGuiCol_PlotLines,0.25)); for (int i=0; iloopStart>=0 && sample->loopStart<(int)sample->samples && ((j+samplePos)*sampleZoom)>sample->loopStart) { + int scaledPos=samplePos+(j*sampleZoom); + if (sample->loopStart>=0 && sample->loopStart<(int)sample->samples && scaledPos>=sample->loopStart) { data[i*availX+j]=bgColorLoop; } else { data[i*availX+j]=bgColor; From 5630b69e64fd041c3c9d16ffe475c5e606b8641a Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 20 Apr 2022 15:29:07 -0500 Subject: [PATCH 145/342] GUI: fix selection being visible in dummy row area --- src/gui/gui.h | 2 +- src/gui/pattern.cpp | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/gui/gui.h b/src/gui/gui.h index 88ced6b69..bea900687 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -1101,7 +1101,7 @@ class FurnaceGUI { float calcBPM(int s1, int s2, float hz); - void patternRow(int i, bool isPlaying, float lineHeight, int chans, int ord, const DivPattern** patCache); + void patternRow(int i, bool isPlaying, float lineHeight, int chans, int ord, const DivPattern** patCache, bool inhibitSel); void actualWaveList(); void actualSampleList(); diff --git a/src/gui/pattern.cpp b/src/gui/pattern.cpp index 6f357bd8b..3b45cd27b 100644 --- a/src/gui/pattern.cpp +++ b/src/gui/pattern.cpp @@ -32,9 +32,9 @@ inline float randRange(float min, float max) { } // draw a pattern row -inline void FurnaceGUI::patternRow(int i, bool isPlaying, float lineHeight, int chans, int ord, const DivPattern** patCache) { +inline void FurnaceGUI::patternRow(int i, bool isPlaying, float lineHeight, int chans, int ord, const DivPattern** patCache, bool inhibitSel) { static char id[32]; - bool selectedRow=(i>=sel1.y && i<=sel2.y); + bool selectedRow=(i>=sel1.y && i<=sel2.y && !inhibitSel); ImGui::TableNextRow(0,lineHeight); ImGui::TableNextColumn(); float cursorPosY=ImGui::GetCursorPos().y-ImGui::GetScrollY(); @@ -559,7 +559,7 @@ void FurnaceGUI::drawPattern() { patCache[i]=e->song.pat[i].getPattern(e->song.orders.ord[i][ord-1],true); } for (int i=0; isong.patLen+i-dummyRows+1,e->isPlaying(),lineHeight,chans,ord-1,patCache); + patternRow(e->song.patLen+i-dummyRows+1,e->isPlaying(),lineHeight,chans,ord-1,patCache,true); } } else { for (int i=0; isong.pat[i].getPattern(e->song.orders.ord[i][ord],true); } for (int i=0; isong.patLen; i++) { - patternRow(i,e->isPlaying(),lineHeight,chans,ord,patCache); + patternRow(i,e->isPlaying(),lineHeight,chans,ord,patCache,false); } // next pattern ImGui::BeginDisabled(); @@ -582,7 +582,7 @@ void FurnaceGUI::drawPattern() { patCache[i]=e->song.pat[i].getPattern(e->song.orders.ord[i][ord+1],true); } for (int i=0; i<=dummyRows; i++) { - patternRow(i,e->isPlaying(),lineHeight,chans,ord+1,patCache); + patternRow(i,e->isPlaying(),lineHeight,chans,ord+1,patCache,true); } } else { for (int i=0; i<=dummyRows; i++) { From 79fa8f1d02afb73972bfc63fd2d326fedc0bb336 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 21 Apr 2022 02:24:06 -0500 Subject: [PATCH 146/342] better default instrument for OPL/OPLL no longer silence --- src/engine/engine.cpp | 20 +++++++++++++++++--- src/engine/engine.h | 3 ++- src/engine/platform/amiga.cpp | 8 ++++---- src/engine/platform/arcade.cpp | 2 +- src/engine/platform/ay.cpp | 4 ++-- src/engine/platform/ay8930.cpp | 4 ++-- src/engine/platform/bubsyswsg.cpp | 6 +++--- src/engine/platform/c64.cpp | 12 ++++++------ src/engine/platform/fds.cpp | 4 ++-- src/engine/platform/gb.cpp | 12 ++++++------ src/engine/platform/genesis.cpp | 2 +- src/engine/platform/genesisext.cpp | 2 +- src/engine/platform/lynx.cpp | 6 +++--- src/engine/platform/mmc5.cpp | 6 +++--- src/engine/platform/n163.cpp | 4 ++-- src/engine/platform/nes.cpp | 6 +++--- src/engine/platform/opl.cpp | 2 +- src/engine/platform/opll.cpp | 2 +- src/engine/platform/pce.cpp | 6 +++--- src/engine/platform/pcspkr.cpp | 4 ++-- src/engine/platform/pet.cpp | 4 ++-- src/engine/platform/qsound.cpp | 6 +++--- src/engine/platform/saa.cpp | 4 ++-- src/engine/platform/segapcm.cpp | 2 +- src/engine/platform/sms.cpp | 6 +++--- src/engine/platform/swan.cpp | 6 +++--- src/engine/platform/tia.cpp | 4 ++-- src/engine/platform/tx81z.cpp | 2 +- src/engine/platform/vera.cpp | 6 +++--- src/engine/platform/vic20.cpp | 4 ++-- src/engine/platform/vrc6.cpp | 6 +++--- src/engine/platform/x1_010.cpp | 4 ++-- src/engine/platform/ym2610.cpp | 6 +++--- src/engine/platform/ym2610b.cpp | 6 +++--- src/engine/platform/ym2610bext.cpp | 14 +++++++------- src/engine/platform/ym2610ext.cpp | 14 +++++++------- src/engine/song.h | 17 ++++++++++++++++- 37 files changed, 128 insertions(+), 98 deletions(-) diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 488b4bf63..98cd8454c 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -738,8 +738,22 @@ String DivEngine::getWarnings() { return warnings; } -DivInstrument* DivEngine::getIns(int index) { - if (index<0 || index>=song.insLen) return &song.nullIns; +DivInstrument* DivEngine::getIns(int index, DivInstrumentType fallbackType) { + if (index<0 || index>=song.insLen) { + switch (fallbackType) { + case DIV_INS_OPLL: + logV("returning the OPLL null instrument"); + return &song.nullInsOPLL; + break; + case DIV_INS_OPL: + logV("returning the OPL null instrument"); + return &song.nullInsOPL; + break; + default: + break; + } + return &song.nullIns; + } return song.ins[index]; } @@ -1970,7 +1984,7 @@ void DivEngine::autoNoteOn(int ch, int ins, int note, int vol) { } do { - if ((ins==-1 || getChannelType(finalChan)==4 || getPreferInsType(finalChan)==getIns(ins)->type || getIns(ins)->type==DIV_INS_AMIGA) && chan[finalChan].midiNote==-1) { + if ((ins<0 || ins>=song.insLen || getChannelType(finalChan)==4 || getPreferInsType(finalChan)==getIns(ins)->type || getIns(ins)->type==DIV_INS_AMIGA) && chan[finalChan].midiNote==-1) { chan[finalChan].midiNote=note; pendingNotes.push(DivNoteEvent(finalChan,ins,note,vol,true)); break; diff --git a/src/engine/engine.h b/src/engine/engine.h index efcbb21b0..515b9c590 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -19,6 +19,7 @@ #ifndef _ENGINE_H #define _ENGINE_H +#include "instrument.h" #include "song.h" #include "dispatch.h" #include "dataErrors.h" @@ -309,7 +310,7 @@ class DivEngine { void runExportThread(); void nextBuf(float** in, float** out, int inChans, int outChans, unsigned int size); - DivInstrument* getIns(int index); + DivInstrument* getIns(int index, DivInstrumentType fallbackType=DIV_INS_FM); DivWavetable* getWave(int index); DivSample* getSample(int index); // start fresh diff --git a/src/engine/platform/amiga.cpp b/src/engine/platform/amiga.cpp index 6d015db48..1212b15d0 100644 --- a/src/engine/platform/amiga.cpp +++ b/src/engine/platform/amiga.cpp @@ -206,7 +206,7 @@ void DivPlatformAmiga::tick(bool sysTick) { } } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { - //DivInstrument* ins=parent->getIns(chan[i].ins); + //DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_AMIGA); chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true)+chan[i].std.pitch.val; if (chan[i].freq>4095) chan[i].freq=4095; if (chan[i].freq<0) chan[i].freq=0; @@ -224,7 +224,7 @@ void DivPlatformAmiga::tick(bool sysTick) { int DivPlatformAmiga::dispatch(DivCommand c) { switch (c.cmd) { case DIV_CMD_NOTE_ON: { - DivInstrument* ins=parent->getIns(chan[c.chan].ins); + DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA); double off=1.0; if (ins->amiga.useWave) { chan[c.chan].useWave=true; @@ -314,7 +314,7 @@ int DivPlatformAmiga::dispatch(DivCommand c) { chan[c.chan].ws.changeWave1(chan[c.chan].wave); break; case DIV_CMD_NOTE_PORTA: { - DivInstrument* ins=parent->getIns(chan[c.chan].ins); + DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA); chan[c.chan].sample=ins->amiga.initSample; double off=1.0; if (!chan[c.chan].useWave && chan[c.chan].sample>=0 && chan[c.chan].samplesong.sampleLen) { @@ -364,7 +364,7 @@ int DivPlatformAmiga::dispatch(DivCommand c) { } case DIV_CMD_PRE_PORTA: if (chan[c.chan].active && c.value2) { - if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins)); + if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA)); } chan[c.chan].inPorta=c.value; break; diff --git a/src/engine/platform/arcade.cpp b/src/engine/platform/arcade.cpp index f4311e29e..6be50b6e4 100644 --- a/src/engine/platform/arcade.cpp +++ b/src/engine/platform/arcade.cpp @@ -446,7 +446,7 @@ void DivPlatformArcade::muteChannel(int ch, bool mute) { int DivPlatformArcade::dispatch(DivCommand c) { switch (c.cmd) { case DIV_CMD_NOTE_ON: { - DivInstrument* ins=parent->getIns(chan[c.chan].ins); + DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_FM); if (chan[c.chan].insChanged) { chan[c.chan].state=ins->fm; diff --git a/src/engine/platform/ay.cpp b/src/engine/platform/ay.cpp index 25f49e950..4ba384832 100644 --- a/src/engine/platform/ay.cpp +++ b/src/engine/platform/ay.cpp @@ -294,7 +294,7 @@ void DivPlatformAY8910::tick(bool sysTick) { int DivPlatformAY8910::dispatch(DivCommand c) { switch (c.cmd) { case DIV_CMD_NOTE_ON: { - DivInstrument* ins=parent->getIns(chan[c.chan].ins); + DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_AY); if (c.value!=DIV_NOTE_NULL) { chan[c.chan].baseFreq=NOTE_PERIODIC(c.value); chan[c.chan].freqChanged=true; @@ -457,7 +457,7 @@ int DivPlatformAY8910::dispatch(DivCommand c) { break; case DIV_CMD_PRE_PORTA: if (chan[c.chan].active && c.value2) { - if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins)); + if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_AY)); } chan[c.chan].inPorta=c.value; break; diff --git a/src/engine/platform/ay8930.cpp b/src/engine/platform/ay8930.cpp index e4fa30dc4..640b495b3 100644 --- a/src/engine/platform/ay8930.cpp +++ b/src/engine/platform/ay8930.cpp @@ -318,7 +318,7 @@ void DivPlatformAY8930::tick(bool sysTick) { int DivPlatformAY8930::dispatch(DivCommand c) { switch (c.cmd) { case DIV_CMD_NOTE_ON: { - DivInstrument* ins=parent->getIns(chan[c.chan].ins); + DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_AY8930); if (c.value!=DIV_NOTE_NULL) { chan[c.chan].baseFreq=NOTE_PERIODIC(c.value); chan[c.chan].freqChanged=true; @@ -482,7 +482,7 @@ int DivPlatformAY8930::dispatch(DivCommand c) { break; case DIV_CMD_PRE_PORTA: if (chan[c.chan].active && c.value2) { - if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins)); + if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_AY8930)); } chan[c.chan].inPorta=c.value; break; diff --git a/src/engine/platform/bubsyswsg.cpp b/src/engine/platform/bubsyswsg.cpp index 569e82f1c..6783bc336 100644 --- a/src/engine/platform/bubsyswsg.cpp +++ b/src/engine/platform/bubsyswsg.cpp @@ -119,7 +119,7 @@ void DivPlatformBubSysWSG::tick(bool sysTick) { } } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { - //DivInstrument* ins=parent->getIns(chan[i].ins); + //DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_SCC); chan[i].freq=0x1000-parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true)+chan[i].std.pitch.val; if (chan[i].freq<0) chan[i].freq=0; if (chan[i].freq>4095) chan[i].freq=4095; @@ -142,7 +142,7 @@ void DivPlatformBubSysWSG::tick(bool sysTick) { int DivPlatformBubSysWSG::dispatch(DivCommand c) { switch (c.cmd) { case DIV_CMD_NOTE_ON: { - DivInstrument* ins=parent->getIns(chan[c.chan].ins); + DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_SCC); if (c.value!=DIV_NOTE_NULL) { chan[c.chan].baseFreq=NOTE_PERIODIC(c.value); chan[c.chan].freqChanged=true; @@ -228,7 +228,7 @@ int DivPlatformBubSysWSG::dispatch(DivCommand c) { break; case DIV_CMD_PRE_PORTA: if (chan[c.chan].active && c.value2) { - if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins)); + if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_SCC)); } chan[c.chan].inPorta=c.value; break; diff --git a/src/engine/platform/c64.cpp b/src/engine/platform/c64.cpp index 02473c80c..604dcfe33 100644 --- a/src/engine/platform/c64.cpp +++ b/src/engine/platform/c64.cpp @@ -126,7 +126,7 @@ void DivPlatformC64::tick(bool sysTick) { for (int i=0; i<3; i++) { chan[i].std.next(); if (chan[i].std.vol.had) { - DivInstrument* ins=parent->getIns(chan[i].ins); + DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_C64); if (ins->c64.volIsCutoff) { if (ins->c64.filterIsAbs) { filtCut=MIN(2047,chan[i].std.vol.val); @@ -157,7 +157,7 @@ void DivPlatformC64::tick(bool sysTick) { } } if (chan[i].std.duty.had) { - DivInstrument* ins=parent->getIns(chan[i].ins); + DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_C64); if (ins->c64.dutyIsAbs) { chan[i].duty=chan[i].std.duty.val; } else { @@ -223,7 +223,7 @@ void DivPlatformC64::tick(bool sysTick) { int DivPlatformC64::dispatch(DivCommand c) { switch (c.cmd) { case DIV_CMD_NOTE_ON: { - DivInstrument* ins=parent->getIns(chan[c.chan].ins); + DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_C64); if (c.value!=DIV_NOTE_NULL) { chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value); chan[c.chan].freqChanged=true; @@ -345,7 +345,7 @@ int DivPlatformC64::dispatch(DivCommand c) { case DIV_CMD_PRE_PORTA: if (chan[c.chan].active && c.value2) { if (parent->song.resetMacroOnPorta || !chan[c.chan].inPorta) { - chan[c.chan].std.init(parent->getIns(chan[c.chan].ins)); + chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_C64)); chan[c.chan].keyOn=true; } } @@ -383,7 +383,7 @@ int DivPlatformC64::dispatch(DivCommand c) { break; case DIV_CMD_C64_FILTER_RESET: if (c.value&15) { - DivInstrument* ins=parent->getIns(chan[c.chan].ins); + DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_C64); if (ins->c64.initFilter) { filtCut=ins->c64.cut; updateFilter(); @@ -393,7 +393,7 @@ int DivPlatformC64::dispatch(DivCommand c) { break; case DIV_CMD_C64_DUTY_RESET: if (c.value&15) { - DivInstrument* ins=parent->getIns(chan[c.chan].ins); + DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_C64); chan[c.chan].duty=ins->c64.duty; rWrite(c.chan*7+2,chan[c.chan].duty&0xff); rWrite(c.chan*7+3,chan[c.chan].duty>>8); diff --git a/src/engine/platform/fds.cpp b/src/engine/platform/fds.cpp index c581c516e..2c3512137 100644 --- a/src/engine/platform/fds.cpp +++ b/src/engine/platform/fds.cpp @@ -198,7 +198,7 @@ void DivPlatformFDS::tick(bool sysTick) { int DivPlatformFDS::dispatch(DivCommand c) { switch (c.cmd) { case DIV_CMD_NOTE_ON: { - DivInstrument* ins=parent->getIns(chan[c.chan].ins); + DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_FDS); if (c.value!=DIV_NOTE_NULL) { chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value); chan[c.chan].freqChanged=true; @@ -358,7 +358,7 @@ int DivPlatformFDS::dispatch(DivCommand c) { break; case DIV_CMD_PRE_PORTA: if (chan[c.chan].active && c.value2) { - if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins)); + if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_FDS)); } chan[c.chan].inPorta=c.value; break; diff --git a/src/engine/platform/gb.cpp b/src/engine/platform/gb.cpp index eb7b6881e..b9668d58a 100644 --- a/src/engine/platform/gb.cpp +++ b/src/engine/platform/gb.cpp @@ -176,7 +176,7 @@ void DivPlatformGB::tick(bool sysTick) { } if (chan[i].std.duty.had) { chan[i].duty=chan[i].std.duty.val; - DivInstrument* ins=parent->getIns(chan[i].ins); + DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_GB); if (i!=2) { rWrite(16+i*5+1,((chan[i].duty&3)<<6)|(63-(ins->gb.soundLen&63))); } else { @@ -220,7 +220,7 @@ void DivPlatformGB::tick(bool sysTick) { } } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { - DivInstrument* ins=parent->getIns(chan[i].ins); + DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_GB); if (i==3) { // noise int ntPos=chan[i].baseFreq; if (ntPos<0) ntPos=0; @@ -269,7 +269,7 @@ void DivPlatformGB::muteChannel(int ch, bool mute) { int DivPlatformGB::dispatch(DivCommand c) { switch (c.cmd) { case DIV_CMD_NOTE_ON: { - DivInstrument* ins=parent->getIns(chan[c.chan].ins); + DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_GB); if (c.value!=DIV_NOTE_NULL) { if (c.chan==3) { // noise chan[c.chan].baseFreq=c.value; @@ -306,7 +306,7 @@ int DivPlatformGB::dispatch(DivCommand c) { chan[c.chan].ins=c.value; chan[c.chan].insChanged=true; if (c.chan!=2) { - DivInstrument* ins=parent->getIns(chan[c.chan].ins); + DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_GB); chan[c.chan].vol=ins->gb.envVol; if (parent->song.gbInsAffectsEnvelope) { rWrite(16+c.chan*5+2,((chan[c.chan].vol<<4))|(ins->gb.envLen&7)|((ins->gb.envDir&1)<<3)); @@ -360,7 +360,7 @@ int DivPlatformGB::dispatch(DivCommand c) { chan[c.chan].duty=c.value; if (c.chan!=2) { chan[c.chan].freqChanged=true; - rWrite(16+c.chan*5+1,((chan[c.chan].duty&3)<<6)|(63-(parent->getIns(chan[c.chan].ins)->gb.soundLen&63))); + rWrite(16+c.chan*5+1,((chan[c.chan].duty&3)<<6)|(63-(parent->getIns(chan[c.chan].ins,DIV_INS_GB)->gb.soundLen&63))); } break; case DIV_CMD_PANNING: { @@ -379,7 +379,7 @@ int DivPlatformGB::dispatch(DivCommand c) { break; case DIV_CMD_PRE_PORTA: if (chan[c.chan].active && c.value2) { - if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins)); + if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_GB)); } chan[c.chan].inPorta=c.value; break; diff --git a/src/engine/platform/genesis.cpp b/src/engine/platform/genesis.cpp index 210a50e74..81fcdefe5 100644 --- a/src/engine/platform/genesis.cpp +++ b/src/engine/platform/genesis.cpp @@ -484,7 +484,7 @@ void DivPlatformGenesis::muteChannel(int ch, bool mute) { int DivPlatformGenesis::dispatch(DivCommand c) { switch (c.cmd) { case DIV_CMD_NOTE_ON: { - DivInstrument* ins=parent->getIns(chan[c.chan].ins); + DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_FM); if (c.chan==5) { if (ins->type==DIV_INS_AMIGA) { dacMode=1; diff --git a/src/engine/platform/genesisext.cpp b/src/engine/platform/genesisext.cpp index 403c1a8cf..f0369370c 100644 --- a/src/engine/platform/genesisext.cpp +++ b/src/engine/platform/genesisext.cpp @@ -37,7 +37,7 @@ int DivPlatformGenesisExt::dispatch(DivCommand c) { int ordch=orderedOps[ch]; switch (c.cmd) { case DIV_CMD_NOTE_ON: { - DivInstrument* ins=parent->getIns(opChan[ch].ins); + DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM); if (opChan[ch].insChanged) { chan[2].state.alg=ins->fm.alg; diff --git a/src/engine/platform/lynx.cpp b/src/engine/platform/lynx.cpp index c51c6a068..4c688103c 100644 --- a/src/engine/platform/lynx.cpp +++ b/src/engine/platform/lynx.cpp @@ -224,7 +224,7 @@ int DivPlatformLynx::dispatch(DivCommand c) { } chan[c.chan].active=true; WRITE_VOLUME(c.chan,(isMuted[c.chan]?0:(chan[c.chan].vol&127))); - chan[c.chan].std.init(parent->getIns(chan[c.chan].ins)); + chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_MIKEY)); break; case DIV_CMD_NOTE_OFF: chan[c.chan].active=false; @@ -241,7 +241,7 @@ int DivPlatformLynx::dispatch(DivCommand c) { break; case DIV_CMD_INSTRUMENT: chan[c.chan].ins=c.value; - //chan[c.chan].std.init(parent->getIns(chan[c.chan].ins)); + //chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_MIKEY)); break; case DIV_CMD_VOLUME: if (chan[c.chan].vol!=c.value) { @@ -297,7 +297,7 @@ int DivPlatformLynx::dispatch(DivCommand c) { break; case DIV_CMD_PRE_PORTA: if (chan[c.chan].active && c.value2) { - if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins)); + if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_MIKEY)); } chan[c.chan].inPorta=c.value; break; diff --git a/src/engine/platform/mmc5.cpp b/src/engine/platform/mmc5.cpp index 696ae6d21..cf1ea36d6 100644 --- a/src/engine/platform/mmc5.cpp +++ b/src/engine/platform/mmc5.cpp @@ -177,7 +177,7 @@ int DivPlatformMMC5::dispatch(DivCommand c) { switch (c.cmd) { case DIV_CMD_NOTE_ON: if (c.chan==2) { // PCM - DivInstrument* ins=parent->getIns(chan[c.chan].ins); + DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_STD); if (ins->type==DIV_INS_AMIGA) { dacSample=ins->amiga.initSample; if (dacSample<0 || dacSample>=parent->song.sampleLen) { @@ -227,7 +227,7 @@ int DivPlatformMMC5::dispatch(DivCommand c) { } chan[c.chan].active=true; chan[c.chan].keyOn=true; - chan[c.chan].std.init(parent->getIns(chan[c.chan].ins)); + chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_STD)); rWrite(0x5000+c.chan*4,0x30|chan[c.chan].vol|((chan[c.chan].duty&3)<<6)); break; case DIV_CMD_NOTE_OFF: @@ -305,7 +305,7 @@ int DivPlatformMMC5::dispatch(DivCommand c) { break; case DIV_CMD_PRE_PORTA: if (chan[c.chan].active && c.value2) { - if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins)); + if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_STD)); } chan[c.chan].inPorta=c.value; break; diff --git a/src/engine/platform/n163.cpp b/src/engine/platform/n163.cpp index a9634fa95..a00d4d05d 100644 --- a/src/engine/platform/n163.cpp +++ b/src/engine/platform/n163.cpp @@ -374,7 +374,7 @@ void DivPlatformN163::tick(bool sysTick) { int DivPlatformN163::dispatch(DivCommand c) { switch (c.cmd) { case DIV_CMD_NOTE_ON: { - DivInstrument* ins=parent->getIns(chan[c.chan].ins); + DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_N163); if (chan[c.chan].insChanged) { chan[c.chan].wave=ins->n163.wave; chan[c.chan].ws.changeWave1(chan[c.chan].wave); @@ -546,7 +546,7 @@ int DivPlatformN163::dispatch(DivCommand c) { case DIV_CMD_PRE_PORTA: if (chan[c.chan].active && c.value2) { if (parent->song.resetMacroOnPorta) { - chan[c.chan].std.init(parent->getIns(chan[c.chan].ins)); + chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_N163)); chan[c.chan].keyOn=true; } } diff --git a/src/engine/platform/nes.cpp b/src/engine/platform/nes.cpp index e4bfbca9b..f855648b9 100644 --- a/src/engine/platform/nes.cpp +++ b/src/engine/platform/nes.cpp @@ -271,7 +271,7 @@ int DivPlatformNES::dispatch(DivCommand c) { switch (c.cmd) { case DIV_CMD_NOTE_ON: if (c.chan==4) { // PCM - DivInstrument* ins=parent->getIns(chan[c.chan].ins); + DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_STD); if (ins->type==DIV_INS_AMIGA) { dacSample=ins->amiga.initSample; if (dacSample<0 || dacSample>=parent->song.sampleLen) { @@ -325,7 +325,7 @@ int DivPlatformNES::dispatch(DivCommand c) { } chan[c.chan].active=true; chan[c.chan].keyOn=true; - chan[c.chan].std.init(parent->getIns(chan[c.chan].ins)); + chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_STD)); if (c.chan==2) { rWrite(0x4000+c.chan*4,0xff); } else { @@ -428,7 +428,7 @@ int DivPlatformNES::dispatch(DivCommand c) { break; case DIV_CMD_PRE_PORTA: if (chan[c.chan].active && c.value2) { - if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins)); + if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_STD)); } chan[c.chan].inPorta=c.value; break; diff --git a/src/engine/platform/opl.cpp b/src/engine/platform/opl.cpp index 96b0d23b5..ef9adecfc 100644 --- a/src/engine/platform/opl.cpp +++ b/src/engine/platform/opl.cpp @@ -535,7 +535,7 @@ int DivPlatformOPL::dispatch(DivCommand c) { } switch (c.cmd) { case DIV_CMD_NOTE_ON: { - DivInstrument* ins=parent->getIns(chan[c.chan].ins); + DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_OPL); if (chan[c.chan].insChanged) { chan[c.chan].state=ins->fm; diff --git a/src/engine/platform/opll.cpp b/src/engine/platform/opll.cpp index d94d1e26d..a02bc8de2 100644 --- a/src/engine/platform/opll.cpp +++ b/src/engine/platform/opll.cpp @@ -365,7 +365,7 @@ int DivPlatformOPLL::dispatch(DivCommand c) { switch (c.cmd) { case DIV_CMD_NOTE_ON: { if (c.chan>=9 && !properDrums) return 0; - DivInstrument* ins=parent->getIns(chan[c.chan].ins); + DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_OPLL); if (chan[c.chan].insChanged) { chan[c.chan].state=ins->fm; } diff --git a/src/engine/platform/pce.cpp b/src/engine/platform/pce.cpp index e1cbb6808..016b3b3c4 100644 --- a/src/engine/platform/pce.cpp +++ b/src/engine/platform/pce.cpp @@ -216,7 +216,7 @@ void DivPlatformPCE::tick(bool sysTick) { } } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { - //DivInstrument* ins=parent->getIns(chan[i].ins); + //DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_PCE); chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true)+chan[i].std.pitch.val; if (chan[i].furnaceDac) { double off=1.0; @@ -251,7 +251,7 @@ void DivPlatformPCE::tick(bool sysTick) { int DivPlatformPCE::dispatch(DivCommand c) { switch (c.cmd) { case DIV_CMD_NOTE_ON: { - DivInstrument* ins=parent->getIns(chan[c.chan].ins); + DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_PCE); if (ins->type==DIV_INS_AMIGA) { chan[c.chan].pcm=true; } else if (chan[c.chan].furnaceDac) { @@ -429,7 +429,7 @@ int DivPlatformPCE::dispatch(DivCommand c) { break; case DIV_CMD_PRE_PORTA: if (chan[c.chan].active && c.value2) { - if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins)); + if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_PCE)); } chan[c.chan].inPorta=c.value; break; diff --git a/src/engine/platform/pcspkr.cpp b/src/engine/platform/pcspkr.cpp index e7062d213..b34d16f24 100644 --- a/src/engine/platform/pcspkr.cpp +++ b/src/engine/platform/pcspkr.cpp @@ -217,7 +217,7 @@ int DivPlatformPCSpeaker::dispatch(DivCommand c) { } chan[c.chan].active=true; chan[c.chan].keyOn=true; - chan[c.chan].std.init(parent->getIns(chan[c.chan].ins)); + chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_BEEPER)); break; case DIV_CMD_NOTE_OFF: chan[c.chan].active=false; @@ -282,7 +282,7 @@ int DivPlatformPCSpeaker::dispatch(DivCommand c) { break; case DIV_CMD_PRE_PORTA: if (chan[c.chan].active && c.value2) { - if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins)); + if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_BEEPER)); } chan[c.chan].inPorta=c.value; break; diff --git a/src/engine/platform/pet.cpp b/src/engine/platform/pet.cpp index 47bb2e3d4..0e83e9d21 100644 --- a/src/engine/platform/pet.cpp +++ b/src/engine/platform/pet.cpp @@ -138,7 +138,7 @@ void DivPlatformPET::tick(bool sysTick) { int DivPlatformPET::dispatch(DivCommand c) { switch (c.cmd) { case DIV_CMD_NOTE_ON: { - DivInstrument* ins=parent->getIns(chan.ins); + DivInstrument* ins=parent->getIns(chan.ins,DIV_INS_PET); if (c.value!=DIV_NOTE_NULL) { chan.baseFreq=NOTE_PERIODIC(c.value); chan.freqChanged=true; @@ -213,7 +213,7 @@ int DivPlatformPET::dispatch(DivCommand c) { break; case DIV_CMD_PRE_PORTA: if (chan.active && c.value2) { - if (parent->song.resetMacroOnPorta) chan.std.init(parent->getIns(chan.ins)); + if (parent->song.resetMacroOnPorta) chan.std.init(parent->getIns(chan.ins,DIV_INS_PET)); } chan.inPorta=c.value; break; diff --git a/src/engine/platform/qsound.cpp b/src/engine/platform/qsound.cpp index 338870109..b5d194ffc 100644 --- a/src/engine/platform/qsound.cpp +++ b/src/engine/platform/qsound.cpp @@ -330,7 +330,7 @@ void DivPlatformQSound::tick(bool sysTick) { chan[i].freqChanged=true; } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { - //DivInstrument* ins=parent->getIns(chan[i].ins); + //DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_AMIGA); chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false)+chan[i].std.pitch.val; if (chan[i].freq>0xffff) chan[i].freq=0xffff; if (chan[i].keyOn) { @@ -363,7 +363,7 @@ void DivPlatformQSound::tick(bool sysTick) { int DivPlatformQSound::dispatch(DivCommand c) { switch (c.cmd) { case DIV_CMD_NOTE_ON: { - DivInstrument* ins=parent->getIns(chan[c.chan].ins); + DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA); chan[c.chan].sample=ins->amiga.initSample; double off=1.0; if (chan[c.chan].sample>=0 && chan[c.chan].samplesong.sampleLen) { @@ -487,7 +487,7 @@ int DivPlatformQSound::dispatch(DivCommand c) { } case DIV_CMD_PRE_PORTA: if (chan[c.chan].active && c.value2) { - if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins)); + if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA)); } chan[c.chan].inPorta=c.value; break; diff --git a/src/engine/platform/saa.cpp b/src/engine/platform/saa.cpp index 47b698051..f21c7d5b4 100644 --- a/src/engine/platform/saa.cpp +++ b/src/engine/platform/saa.cpp @@ -249,7 +249,7 @@ void DivPlatformSAA1099::tick(bool sysTick) { int DivPlatformSAA1099::dispatch(DivCommand c) { switch (c.cmd) { case DIV_CMD_NOTE_ON: { - DivInstrument* ins=parent->getIns(chan[c.chan].ins); + DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_SAA1099); if (c.value!=DIV_NOTE_NULL) { chan[c.chan].baseFreq=NOTE_PERIODIC(c.value); chan[c.chan].freqChanged=true; @@ -356,7 +356,7 @@ int DivPlatformSAA1099::dispatch(DivCommand c) { break; case DIV_CMD_PRE_PORTA: if (chan[c.chan].active && c.value2) { - if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins)); + if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_SAA1099)); } chan[c.chan].inPorta=c.value; break; diff --git a/src/engine/platform/segapcm.cpp b/src/engine/platform/segapcm.cpp index 700924476..a7db708a2 100644 --- a/src/engine/platform/segapcm.cpp +++ b/src/engine/platform/segapcm.cpp @@ -149,7 +149,7 @@ void DivPlatformSegaPCM::muteChannel(int ch, bool mute) { int DivPlatformSegaPCM::dispatch(DivCommand c) { switch (c.cmd) { case DIV_CMD_NOTE_ON: { - DivInstrument* ins=parent->getIns(chan[c.chan].ins); + DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA); if (skipRegisterWrites) break; if (ins->type==DIV_INS_AMIGA) { chan[c.chan].pcm.sample=ins->amiga.initSample; diff --git a/src/engine/platform/sms.cpp b/src/engine/platform/sms.cpp index da31e4784..af339f6e2 100644 --- a/src/engine/platform/sms.cpp +++ b/src/engine/platform/sms.cpp @@ -175,7 +175,7 @@ int DivPlatformSMS::dispatch(DivCommand c) { } chan[c.chan].active=true; rWrite(0x90|c.chan<<5|(isMuted[c.chan]?15:(15-(chan[c.chan].vol&15)))); - chan[c.chan].std.init(parent->getIns(chan[c.chan].ins)); + chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_STD)); break; case DIV_CMD_NOTE_OFF: chan[c.chan].active=false; @@ -188,7 +188,7 @@ int DivPlatformSMS::dispatch(DivCommand c) { break; case DIV_CMD_INSTRUMENT: chan[c.chan].ins=c.value; - //chan[c.chan].std.init(parent->getIns(chan[c.chan].ins)); + //chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_STD)); break; case DIV_CMD_VOLUME: if (chan[c.chan].vol!=c.value) { @@ -244,7 +244,7 @@ int DivPlatformSMS::dispatch(DivCommand c) { break; case DIV_CMD_PRE_PORTA: if (chan[c.chan].active && c.value2) { - if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins)); + if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_STD)); } chan[c.chan].inPorta=c.value; break; diff --git a/src/engine/platform/swan.cpp b/src/engine/platform/swan.cpp index 22790c641..b8f9a40dc 100644 --- a/src/engine/platform/swan.cpp +++ b/src/engine/platform/swan.cpp @@ -147,7 +147,7 @@ void DivPlatformSwan::tick(bool sysTick) { chan[i].std.next(); if (chan[i].std.vol.had) { int env=chan[i].std.vol.val; - if(parent->getIns(chan[i].ins)->type==DIV_INS_AMIGA) { + if(parent->getIns(chan[i].ins,DIV_INS_SWAN)->type==DIV_INS_AMIGA) { env=MIN(env/4,15); } calcAndWriteOutVol(i,env); @@ -240,7 +240,7 @@ void DivPlatformSwan::tick(bool sysTick) { int DivPlatformSwan::dispatch(DivCommand c) { switch (c.cmd) { case DIV_CMD_NOTE_ON: { - DivInstrument* ins=parent->getIns(chan[c.chan].ins); + DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_SWAN); if (c.chan==1) { if (ins->type==DIV_INS_AMIGA) { pcm=true; @@ -415,7 +415,7 @@ int DivPlatformSwan::dispatch(DivCommand c) { break; case DIV_CMD_PRE_PORTA: if (chan[c.chan].active && c.value2) { - if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins)); + if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_SWAN)); } chan[c.chan].inPorta=c.value; break; diff --git a/src/engine/platform/tia.cpp b/src/engine/platform/tia.cpp index 86b5e6be1..cc5151f03 100644 --- a/src/engine/platform/tia.cpp +++ b/src/engine/platform/tia.cpp @@ -154,7 +154,7 @@ void DivPlatformTIA::tick(bool sysTick) { int DivPlatformTIA::dispatch(DivCommand c) { switch (c.cmd) { case DIV_CMD_NOTE_ON: { - DivInstrument* ins=parent->getIns(chan[c.chan].ins); + DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_TIA); if (c.value!=DIV_NOTE_NULL) { chan[c.chan].baseFreq=c.value<<8; chan[c.chan].freqChanged=true; @@ -248,7 +248,7 @@ int DivPlatformTIA::dispatch(DivCommand c) { break; case DIV_CMD_PRE_PORTA: if (chan[c.chan].active && c.value2) { - if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins)); + if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_TIA)); } chan[c.chan].inPorta=c.value; break; diff --git a/src/engine/platform/tx81z.cpp b/src/engine/platform/tx81z.cpp index 98f68a54b..712335818 100644 --- a/src/engine/platform/tx81z.cpp +++ b/src/engine/platform/tx81z.cpp @@ -424,7 +424,7 @@ void DivPlatformTX81Z::muteChannel(int ch, bool mute) { int DivPlatformTX81Z::dispatch(DivCommand c) { switch (c.cmd) { case DIV_CMD_NOTE_ON: { - DivInstrument* ins=parent->getIns(chan[c.chan].ins); + DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_OPZ); if (chan[c.chan].insChanged) { chan[c.chan].state=ins->fm; diff --git a/src/engine/platform/vera.cpp b/src/engine/platform/vera.cpp index 89cccb99d..63415a0c1 100644 --- a/src/engine/platform/vera.cpp +++ b/src/engine/platform/vera.cpp @@ -237,7 +237,7 @@ int DivPlatformVERA::dispatch(DivCommand c) { if(c.chan<16) { rWriteLo(c.chan,2,chan[c.chan].vol) } else { - chan[16].pcm.sample=parent->getIns(chan[16].ins)->amiga.initSample; + chan[16].pcm.sample=parent->getIns(chan[16].ins,DIV_INS_VERA)->amiga.initSample; if (chan[16].pcm.sample<0 || chan[16].pcm.sample>=parent->song.sampleLen) { chan[16].pcm.sample=-1; } @@ -259,7 +259,7 @@ int DivPlatformVERA::dispatch(DivCommand c) { chan[c.chan].note=c.value; } chan[c.chan].active=true; - chan[c.chan].std.init(parent->getIns(chan[c.chan].ins)); + chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_VERA)); break; case DIV_CMD_NOTE_OFF: chan[c.chan].active=false; @@ -327,7 +327,7 @@ int DivPlatformVERA::dispatch(DivCommand c) { break; case DIV_CMD_PRE_PORTA: if (chan[c.chan].active && c.value2) { - if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins)); + if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_VERA)); } chan[c.chan].inPorta=c.value; break; diff --git a/src/engine/platform/vic20.cpp b/src/engine/platform/vic20.cpp index 44aba9670..aa787672b 100644 --- a/src/engine/platform/vic20.cpp +++ b/src/engine/platform/vic20.cpp @@ -158,7 +158,7 @@ void DivPlatformVIC20::tick(bool sysTick) { int DivPlatformVIC20::dispatch(DivCommand c) { switch (c.cmd) { case DIV_CMD_NOTE_ON: { - DivInstrument* ins=parent->getIns(chan[c.chan].ins); + DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_VIC); if (c.value!=DIV_NOTE_NULL) { chan[c.chan].baseFreq=NOTE_PERIODIC(c.value); chan[c.chan].freqChanged=true; @@ -232,7 +232,7 @@ int DivPlatformVIC20::dispatch(DivCommand c) { break; case DIV_CMD_PRE_PORTA: if (chan[c.chan].active && c.value2) { - if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins)); + if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_VIC)); } chan[c.chan].inPorta=c.value; break; diff --git a/src/engine/platform/vrc6.cpp b/src/engine/platform/vrc6.cpp index a35ed995b..56e95948c 100644 --- a/src/engine/platform/vrc6.cpp +++ b/src/engine/platform/vrc6.cpp @@ -220,7 +220,7 @@ int DivPlatformVRC6::dispatch(DivCommand c) { switch (c.cmd) { case DIV_CMD_NOTE_ON: if (c.chan!=2) { // pulse wave - DivInstrument* ins=parent->getIns(chan[c.chan].ins); + DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_VRC6); if (ins->type==DIV_INS_AMIGA) { chan[c.chan].pcm=true; } else if (chan[c.chan].furnaceDac) { @@ -284,7 +284,7 @@ int DivPlatformVRC6::dispatch(DivCommand c) { } chan[c.chan].active=true; chan[c.chan].keyOn=true; - chan[c.chan].std.init(parent->getIns(chan[c.chan].ins)); + chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_VRC6)); if (!isMuted[c.chan]) { if (c.chan==2) { // sawtooth chWrite(c.chan,0,chan[c.chan].vol); @@ -380,7 +380,7 @@ int DivPlatformVRC6::dispatch(DivCommand c) { break; case DIV_CMD_PRE_PORTA: if (chan[c.chan].active && c.value2) { - if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins)); + if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_VRC6)); } chan[c.chan].inPorta=c.value; break; diff --git a/src/engine/platform/x1_010.cpp b/src/engine/platform/x1_010.cpp index 9c9b3e20d..f1498709f 100644 --- a/src/engine/platform/x1_010.cpp +++ b/src/engine/platform/x1_010.cpp @@ -525,7 +525,7 @@ int DivPlatformX1_010::dispatch(DivCommand c) { switch (c.cmd) { case DIV_CMD_NOTE_ON: { chWrite(c.chan,0,0); // reset previous note - DivInstrument* ins=parent->getIns(chan[c.chan].ins); + DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_X1_010); if ((ins->type==DIV_INS_AMIGA) || chan[c.chan].pcm) { if (ins->type==DIV_INS_AMIGA) { chan[c.chan].furnacePCM=true; @@ -703,7 +703,7 @@ int DivPlatformX1_010::dispatch(DivCommand c) { break; case DIV_CMD_PRE_PORTA: if (chan[c.chan].active && c.value2) { - if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins)); + if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_X1_010)); } chan[c.chan].inPorta=c.value; break; diff --git a/src/engine/platform/ym2610.cpp b/src/engine/platform/ym2610.cpp index f955303d3..04338d265 100644 --- a/src/engine/platform/ym2610.cpp +++ b/src/engine/platform/ym2610.cpp @@ -635,7 +635,7 @@ int DivPlatformYM2610::dispatch(DivCommand c) { switch (c.cmd) { case DIV_CMD_NOTE_ON: { if (c.chan>12) { // ADPCM-B - DivInstrument* ins=parent->getIns(chan[c.chan].ins); + DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_FM); if (ins->type==DIV_INS_AMIGA) { chan[c.chan].furnacePCM=true; } else { @@ -718,7 +718,7 @@ int DivPlatformYM2610::dispatch(DivCommand c) { immWrite(0x100,0x00|(1<<(c.chan-7))); break; } - DivInstrument* ins=parent->getIns(chan[c.chan].ins); + DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_FM); chan[c.chan].std.init(ins); if (c.chan<4) { if (!chan[c.chan].std.vol.will) { @@ -981,7 +981,7 @@ int DivPlatformYM2610::dispatch(DivCommand c) { case DIV_CMD_PRE_PORTA: if (c.chan>3) { if (chan[c.chan].active && c.value2) { - if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins)); + if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_FM)); } } chan[c.chan].inPorta=c.value; diff --git a/src/engine/platform/ym2610b.cpp b/src/engine/platform/ym2610b.cpp index 75855f4fc..a250e04ce 100644 --- a/src/engine/platform/ym2610b.cpp +++ b/src/engine/platform/ym2610b.cpp @@ -698,7 +698,7 @@ int DivPlatformYM2610B::dispatch(DivCommand c) { switch (c.cmd) { case DIV_CMD_NOTE_ON: { if (c.chan>14) { // ADPCM-B - DivInstrument* ins=parent->getIns(chan[c.chan].ins); + DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_FM); if (ins->type==DIV_INS_AMIGA) { chan[c.chan].furnacePCM=true; } else { @@ -781,7 +781,7 @@ int DivPlatformYM2610B::dispatch(DivCommand c) { immWrite(0x100,0x00|(1<<(c.chan-9))); break; } - DivInstrument* ins=parent->getIns(chan[c.chan].ins); + DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_FM); chan[c.chan].std.init(ins); if (c.chan<6) { if (!chan[c.chan].std.vol.will) { @@ -1044,7 +1044,7 @@ int DivPlatformYM2610B::dispatch(DivCommand c) { case DIV_CMD_PRE_PORTA: if (c.chan>5) { if (chan[c.chan].active && c.value2) { - if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins)); + if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_FM)); } } chan[c.chan].inPorta=c.value; diff --git a/src/engine/platform/ym2610bext.cpp b/src/engine/platform/ym2610bext.cpp index 496a308e0..74794a7d7 100644 --- a/src/engine/platform/ym2610bext.cpp +++ b/src/engine/platform/ym2610bext.cpp @@ -36,7 +36,7 @@ int DivPlatformYM2610BExt::dispatch(DivCommand c) { int ordch=orderedOps[ch]; switch (c.cmd) { case DIV_CMD_NOTE_ON: { - DivInstrument* ins=parent->getIns(opChan[ch].ins); + DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM); unsigned short baseAddr=chanOffs[2]|opOffs[ordch]; DivInstrumentFM::Operator op=ins->fm.op[ordch]; @@ -78,7 +78,7 @@ int DivPlatformYM2610BExt::dispatch(DivCommand c) { break; case DIV_CMD_VOLUME: { opChan[ch].vol=c.value; - DivInstrument* ins=parent->getIns(opChan[ch].ins); + DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM); unsigned short baseAddr=chanOffs[2]|opOffs[ordch]; DivInstrumentFM::Operator op=ins->fm.op[ordch]; if (isOpMuted[ch]) { @@ -104,7 +104,7 @@ int DivPlatformYM2610BExt::dispatch(DivCommand c) { } else { opChan[ch].pan=((c.value&15)>0)|(((c.value>>4)>0)<<1); } - DivInstrument* ins=parent->getIns(opChan[ch].ins); + DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM); if (parent->song.sharedExtStat) { for (int i=0; i<4; i++) { if (ch==i) continue; @@ -159,14 +159,14 @@ int DivPlatformYM2610BExt::dispatch(DivCommand c) { } case DIV_CMD_FM_MULT: { // TODO unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[c.value]]; - DivInstrument* ins=parent->getIns(opChan[ch].ins); + DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM); DivInstrumentFM::Operator op=ins->fm.op[orderedOps[c.value]]; rWrite(baseAddr+0x30,(c.value2&15)|(dtTable[op.dt&7]<<4)); break; } case DIV_CMD_FM_TL: { // TODO unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[c.value]]; - DivInstrument* ins=parent->getIns(opChan[ch].ins); + DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM); if (isOutput[ins->fm.alg][c.value]) { rWrite(baseAddr+0x40,127-(((127-c.value2)*(opChan[ch].vol&0x7f))/127)); } else { @@ -175,7 +175,7 @@ int DivPlatformYM2610BExt::dispatch(DivCommand c) { break; } case DIV_CMD_FM_AR: { - DivInstrument* ins=parent->getIns(opChan[ch].ins); + DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM); if (c.value<0) { for (int i=0; i<4; i++) { DivInstrumentFM::Operator op=ins->fm.op[i]; @@ -268,7 +268,7 @@ void DivPlatformYM2610BExt::muteChannel(int ch, bool mute) { isOpMuted[ch-2]=mute; int ordch=orderedOps[ch-2]; - DivInstrument* ins=parent->getIns(opChan[ch-2].ins); + DivInstrument* ins=parent->getIns(opChan[ch-2].ins,DIV_INS_FM); unsigned short baseAddr=chanOffs[2]|opOffs[ordch]; DivInstrumentFM::Operator op=ins->fm.op[ordch]; if (isOpMuted[ch-2]) { diff --git a/src/engine/platform/ym2610ext.cpp b/src/engine/platform/ym2610ext.cpp index dc4625080..df9b43511 100644 --- a/src/engine/platform/ym2610ext.cpp +++ b/src/engine/platform/ym2610ext.cpp @@ -36,7 +36,7 @@ int DivPlatformYM2610Ext::dispatch(DivCommand c) { int ordch=orderedOps[ch]; switch (c.cmd) { case DIV_CMD_NOTE_ON: { - DivInstrument* ins=parent->getIns(opChan[ch].ins); + DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM); unsigned short baseAddr=chanOffs[1]|opOffs[ordch]; DivInstrumentFM::Operator op=ins->fm.op[ordch]; @@ -78,7 +78,7 @@ int DivPlatformYM2610Ext::dispatch(DivCommand c) { break; case DIV_CMD_VOLUME: { opChan[ch].vol=c.value; - DivInstrument* ins=parent->getIns(opChan[ch].ins); + DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM); unsigned short baseAddr=chanOffs[1]|opOffs[ordch]; DivInstrumentFM::Operator op=ins->fm.op[ordch]; if (isOpMuted[ch]) { @@ -104,7 +104,7 @@ int DivPlatformYM2610Ext::dispatch(DivCommand c) { } else { opChan[ch].pan=((c.value&15)>0)|(((c.value>>4)>0)<<1); } - DivInstrument* ins=parent->getIns(opChan[ch].ins); + DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM); if (parent->song.sharedExtStat) { for (int i=0; i<4; i++) { if (ch==i) continue; @@ -159,14 +159,14 @@ int DivPlatformYM2610Ext::dispatch(DivCommand c) { } case DIV_CMD_FM_MULT: { // TODO unsigned short baseAddr=chanOffs[1]|opOffs[orderedOps[c.value]]; - DivInstrument* ins=parent->getIns(opChan[ch].ins); + DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM); DivInstrumentFM::Operator op=ins->fm.op[orderedOps[c.value]]; rWrite(baseAddr+0x30,(c.value2&15)|(dtTable[op.dt&7]<<4)); break; } case DIV_CMD_FM_TL: { // TODO unsigned short baseAddr=chanOffs[1]|opOffs[orderedOps[c.value]]; - DivInstrument* ins=parent->getIns(opChan[ch].ins); + DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM); if (isOutput[ins->fm.alg][c.value]) { rWrite(baseAddr+0x40,127-(((127-c.value2)*(opChan[ch].vol&0x7f))/127)); } else { @@ -175,7 +175,7 @@ int DivPlatformYM2610Ext::dispatch(DivCommand c) { break; } case DIV_CMD_FM_AR: { - DivInstrument* ins=parent->getIns(opChan[ch].ins); + DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM); if (c.value<0) { for (int i=0; i<4; i++) { DivInstrumentFM::Operator op=ins->fm.op[i]; @@ -268,7 +268,7 @@ void DivPlatformYM2610Ext::muteChannel(int ch, bool mute) { isOpMuted[ch-1]=mute; int ordch=orderedOps[ch-1]; - DivInstrument* ins=parent->getIns(opChan[ch].ins); + DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM); unsigned short baseAddr=chanOffs[1]|opOffs[ordch]; DivInstrumentFM::Operator op=ins->fm.op[ordch]; if (isOpMuted[ch]) { diff --git a/src/engine/song.h b/src/engine/song.h index 81c0cb8a8..82d219338 100644 --- a/src/engine/song.h +++ b/src/engine/song.h @@ -334,7 +334,7 @@ struct DivSong { bool chanShow[DIV_MAX_CHANS]; bool chanCollapse[DIV_MAX_CHANS]; - DivInstrument nullIns; + DivInstrument nullIns, nullInsOPLL, nullInsOPL; DivWavetable nullWave; DivSample nullSample; @@ -419,6 +419,21 @@ struct DivSong { } system[0]=DIV_SYSTEM_YM2612; system[1]=DIV_SYSTEM_SMS; + + nullInsOPLL.fm.opllPreset=7; + nullInsOPLL.fm.op[1].tl=0; + + nullInsOPL.fm.alg=0; + nullInsOPL.fm.fb=7; + nullInsOPL.fm.op[0].dr=2; + nullInsOPL.fm.op[0].rr=7; + nullInsOPL.fm.op[0].tl=22; + nullInsOPL.fm.op[0].ksl=1; + nullInsOPL.fm.op[0].mult=3; + nullInsOPL.fm.op[1].tl=0; + nullInsOPL.fm.op[1].dr=3; + nullInsOPL.fm.op[1].rr=12; + nullInsOPL.fm.op[1].mult=1; } }; From 4195715dc0fa69f14a50b1a512c27e40fe396aa1 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 21 Apr 2022 02:29:20 -0500 Subject: [PATCH 147/342] use good default instrument when adding ins --- src/engine/engine.cpp | 15 ++++++++++++--- src/engine/song.h | 2 ++ 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 98cd8454c..905ba8aa5 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -742,11 +742,9 @@ DivInstrument* DivEngine::getIns(int index, DivInstrumentType fallbackType) { if (index<0 || index>=song.insLen) { switch (fallbackType) { case DIV_INS_OPLL: - logV("returning the OPLL null instrument"); return &song.nullInsOPLL; break; case DIV_INS_OPL: - logV("returning the OPL null instrument"); return &song.nullInsOPL; break; default: @@ -1310,8 +1308,19 @@ int DivEngine::addInstrument(int refChan) { BUSY_BEGIN; DivInstrument* ins=new DivInstrument; int insCount=(int)song.ins.size(); + DivInstrumentType prefType=getPreferInsType(refChan); + switch (prefType) { + case DIV_INS_OPLL: + *ins=song.nullInsOPLL; + break; + case DIV_INS_OPL: + *ins=song.nullInsOPL; + break; + default: + break; + } ins->name=fmt::sprintf("Instrument %d",insCount); - ins->type=getPreferInsType(refChan); + ins->type=prefType; saveLock.lock(); song.ins.push_back(ins); song.insLen=insCount+1; diff --git a/src/engine/song.h b/src/engine/song.h index 82d219338..20b17ae60 100644 --- a/src/engine/song.h +++ b/src/engine/song.h @@ -422,6 +422,7 @@ struct DivSong { nullInsOPLL.fm.opllPreset=7; nullInsOPLL.fm.op[1].tl=0; + nullInsOPLL.name="This is a bug! Report!"; nullInsOPL.fm.alg=0; nullInsOPL.fm.fb=7; @@ -434,6 +435,7 @@ struct DivSong { nullInsOPL.fm.op[1].dr=3; nullInsOPL.fm.op[1].rr=12; nullInsOPL.fm.op[1].mult=1; + nullInsOPL.name="This is a bug! Report!"; } }; From 06a69a20e89ff1e1be3d89282c1b3037dc9d8c64 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 21 Apr 2022 02:35:50 -0500 Subject: [PATCH 148/342] GUI: add "absorb" instrument input mode --- src/gui/gui.cpp | 3 +++ src/gui/gui.h | 2 ++ src/gui/settings.cpp | 8 ++++++++ 3 files changed, 13 insertions(+) diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 63e0594d0..068eb11d5 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -954,6 +954,9 @@ void FurnaceGUI::valueInput(int num, bool direct, int target) { pat->data[cursor.y][target]=(int)e->song.ins.size()-1; } } + if (settings.absorbInsInput) { + curIns=pat->data[cursor.y][target]; + } makeUndo(GUI_UNDO_PATTERN_EDIT); if (direct) { curNibble=false; diff --git a/src/gui/gui.h b/src/gui/gui.h index bea900687..6db3c4a23 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -826,6 +826,7 @@ class FurnaceGUI { int lowLatency; int notePreviewBehavior; int powerSave; + int absorbInsInput; unsigned int maxUndoSteps; String mainFontPath; String patFontPath; @@ -900,6 +901,7 @@ class FurnaceGUI { lowLatency(0), notePreviewBehavior(1), powerSave(1), + absorbInsInput(0), maxUndoSteps(100), mainFontPath(""), patFontPath(""), diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index 7ead6c204..f1cd27a36 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -258,6 +258,11 @@ void FurnaceGUI::drawSettings() { settings.stepOnDelete=stepOnDeleteB; } + bool absorbInsInputB=settings.absorbInsInput; + if (ImGui::Checkbox("Change current instrument when changing instrument column (absorb)",&absorbInsInputB)) { + settings.absorbInsInput=absorbInsInputB; + } + bool effectDeletionAltersValueB=settings.effectDeletionAltersValue; if (ImGui::Checkbox("Delete effect value when deleting effect",&effectDeletionAltersValueB)) { settings.effectDeletionAltersValue=effectDeletionAltersValueB; @@ -1645,6 +1650,7 @@ void FurnaceGUI::syncSettings() { settings.lowLatency=e->getConfInt("lowLatency",0); settings.notePreviewBehavior=e->getConfInt("notePreviewBehavior",1); settings.powerSave=e->getConfInt("powerSave",POWER_SAVE_DEFAULT); + settings.absorbInsInput=e->getConfInt("absorbInsInput",0); clampSetting(settings.mainFontSize,2,96); clampSetting(settings.patFontSize,2,96); @@ -1708,6 +1714,7 @@ void FurnaceGUI::syncSettings() { clampSetting(settings.lowLatency,0,1); clampSetting(settings.notePreviewBehavior,0,3); clampSetting(settings.powerSave,0,1); + clampSetting(settings.absorbInsInput,0,1); // keybinds for (int i=0; isetConf("lowLatency",settings.lowLatency); e->setConf("notePreviewBehavior",settings.notePreviewBehavior); e->setConf("powerSave",settings.powerSave); + e->setConf("absorbInsInput",settings.absorbInsInput); // colors for (int i=0; i Date: Thu, 21 Apr 2022 02:35:57 -0500 Subject: [PATCH 149/342] update to-do list --- TODO.md | 1 - 1 file changed, 1 deletion(-) diff --git a/TODO.md b/TODO.md index b6d059502..03e465ce6 100644 --- a/TODO.md +++ b/TODO.md @@ -26,7 +26,6 @@ - rewrite the system name detection function anyway - add nightly.link - scroll instrument/wave/sample list when selecting item -- "absorb" mode for instrument input - when this happens, current instrument is set to the input value - unified data view - separate "transpose note" and "transpose value" - see next point - volume commands should work on Game Boy From dba9482cee12e7eaf7709e7109808b8b0aef870e Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 21 Apr 2022 02:53:09 -0500 Subject: [PATCH 150/342] GUI: add a "none" option to instrument list --- src/gui/dataList.cpp | 244 ++++++++++++++++++++++--------------------- 1 file changed, 125 insertions(+), 119 deletions(-) diff --git a/src/gui/dataList.cpp b/src/gui/dataList.cpp index 8a02c40e3..671eb451e 100644 --- a/src/gui/dataList.cpp +++ b/src/gui/dataList.cpp @@ -72,126 +72,132 @@ void FurnaceGUI::drawInsList() { ImGui::Indent(); } - for (int i=0; i<(int)e->song.ins.size(); i++) { - DivInstrument* ins=e->song.ins[i]; - String name; - switch (ins->type) { - case DIV_INS_FM: - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_FM]); - name=fmt::sprintf(ICON_FA_AREA_CHART " %.2X: %s##_INS%d",i,ins->name,i); - break; - case DIV_INS_STD: - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_STD]); - name=fmt::sprintf(ICON_FA_BAR_CHART " %.2X: %s##_INS%d",i,ins->name,i); - break; - case DIV_INS_GB: - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_GB]); - name=fmt::sprintf(ICON_FA_GAMEPAD " %.2X: %s##_INS%d",i,ins->name,i); - break; - case DIV_INS_C64: - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_C64]); - name=fmt::sprintf(ICON_FA_KEYBOARD_O " %.2X: %s##_INS%d",i,ins->name,i); - break; - case DIV_INS_AMIGA: - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_AMIGA]); - name=fmt::sprintf(ICON_FA_VOLUME_UP " %.2X: %s##_INS%d",i,ins->name,i); - break; - case DIV_INS_PCE: - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_PCE]); - name=fmt::sprintf(ICON_FA_ID_BADGE " %.2X: %s##_INS%d",i,ins->name,i); - break; - case DIV_INS_AY: - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_AY]); - name=fmt::sprintf(ICON_FA_BAR_CHART " %.2X: %s##_INS%d",i,ins->name,i); - break; - case DIV_INS_AY8930: - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_AY8930]); - name=fmt::sprintf(ICON_FA_BAR_CHART " %.2X: %s##_INS%d",i,ins->name,i); - break; - case DIV_INS_TIA: - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_TIA]); - name=fmt::sprintf(ICON_FA_BAR_CHART " %.2X: %s##_INS%d",i,ins->name,i); - break; - case DIV_INS_SAA1099: - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_SAA1099]); - name=fmt::sprintf(ICON_FA_BAR_CHART " %.2X: %s##_INS%d",i,ins->name,i); - break; - case DIV_INS_VIC: - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_VIC]); - name=fmt::sprintf(ICON_FA_BAR_CHART " %.2X: %s##_INS%d",i,ins->name,i); - break; - case DIV_INS_PET: - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_PET]); - name=fmt::sprintf(ICON_FA_SQUARE " %.2X: %s##_INS%d",i,ins->name,i); - break; - case DIV_INS_VRC6: - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_VRC6]); - name=fmt::sprintf(ICON_FA_BAR_CHART " %.2X: %s##_INS%d",i,ins->name,i); - break; - case DIV_INS_VRC6_SAW: - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_VRC6_SAW]); - name=fmt::sprintf(ICON_FA_BAR_CHART " %.2X: %s##_INS%d",i,ins->name,i); - break; - case DIV_INS_OPLL: - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_OPLL]); - name=fmt::sprintf(ICON_FA_AREA_CHART " %.2X: %s##_INS%d",i,ins->name,i); - break; - case DIV_INS_OPL: - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_OPL]); - name=fmt::sprintf(ICON_FA_AREA_CHART " %.2X: %s##_INS%d",i,ins->name,i); - break; - case DIV_INS_FDS: - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_FDS]); - name=fmt::sprintf(ICON_FA_FLOPPY_O " %.2X: %s##_INS%d",i,ins->name,i); - break; - case DIV_INS_VBOY: - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_VBOY]); - name=fmt::sprintf(ICON_FA_BINOCULARS " %.2X: %s##_INS%d",i,ins->name,i); - break; - case DIV_INS_N163: - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_N163]); - name=fmt::sprintf(ICON_FA_CALCULATOR " %.2X: %s##_INS%d",i,ins->name,i); - break; - case DIV_INS_SCC: - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_SCC]); - name=fmt::sprintf(ICON_FA_CALCULATOR " %.2X: %s##_INS%d",i,ins->name,i); - break; - case DIV_INS_OPZ: - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_OPZ]); - name=fmt::sprintf(ICON_FA_AREA_CHART " %.2X: %s##_INS%d",i,ins->name,i); - break; - case DIV_INS_POKEY: - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_POKEY]); - name=fmt::sprintf(ICON_FA_BAR_CHART " %.2X: %s##_INS%d",i,ins->name,i); - break; - case DIV_INS_BEEPER: - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_BEEPER]); - name=fmt::sprintf(ICON_FA_SQUARE " %.2X: %s##_INS%d",i,ins->name,i); - break; - case DIV_INS_SWAN: - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_SWAN]); - name=fmt::sprintf(ICON_FA_GAMEPAD " %.2X: %s##_INS%d",i,ins->name,i); - break; - case DIV_INS_MIKEY: - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_MIKEY]); - name=fmt::sprintf(ICON_FA_BAR_CHART " %.2X: %s##_INS%d",i,ins->name,i); - break; - case DIV_INS_VERA: - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_VERA]); - name=fmt::sprintf(ICON_FA_KEYBOARD_O " %.2X: %s##_INS%d",i,ins->name,i); - break; - case DIV_INS_X1_010: - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_X1_010]); - name=fmt::sprintf(ICON_FA_BAR_CHART " %.2X: %s##_INS%d",i,ins->name,i); - break; - default: - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_UNKNOWN]); - name=fmt::sprintf(ICON_FA_QUESTION " %.2X: %s##_INS%d",i,ins->name,i); - break; + for (int i=-1; i<(int)e->song.ins.size(); i++) { + String name=ICON_FA_CIRCLE_O " - None -"; + const char* insType="Bug!"; + if (i>=0) { + DivInstrument* ins=e->song.ins[i]; + insType=(ins->type>DIV_INS_MAX)?"Unknown":insTypes[ins->type]; + switch (ins->type) { + case DIV_INS_FM: + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_FM]); + name=fmt::sprintf(ICON_FA_AREA_CHART " %.2X: %s##_INS%d",i,ins->name,i); + break; + case DIV_INS_STD: + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_STD]); + name=fmt::sprintf(ICON_FA_BAR_CHART " %.2X: %s##_INS%d",i,ins->name,i); + break; + case DIV_INS_GB: + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_GB]); + name=fmt::sprintf(ICON_FA_GAMEPAD " %.2X: %s##_INS%d",i,ins->name,i); + break; + case DIV_INS_C64: + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_C64]); + name=fmt::sprintf(ICON_FA_KEYBOARD_O " %.2X: %s##_INS%d",i,ins->name,i); + break; + case DIV_INS_AMIGA: + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_AMIGA]); + name=fmt::sprintf(ICON_FA_VOLUME_UP " %.2X: %s##_INS%d",i,ins->name,i); + break; + case DIV_INS_PCE: + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_PCE]); + name=fmt::sprintf(ICON_FA_ID_BADGE " %.2X: %s##_INS%d",i,ins->name,i); + break; + case DIV_INS_AY: + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_AY]); + name=fmt::sprintf(ICON_FA_BAR_CHART " %.2X: %s##_INS%d",i,ins->name,i); + break; + case DIV_INS_AY8930: + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_AY8930]); + name=fmt::sprintf(ICON_FA_BAR_CHART " %.2X: %s##_INS%d",i,ins->name,i); + break; + case DIV_INS_TIA: + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_TIA]); + name=fmt::sprintf(ICON_FA_BAR_CHART " %.2X: %s##_INS%d",i,ins->name,i); + break; + case DIV_INS_SAA1099: + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_SAA1099]); + name=fmt::sprintf(ICON_FA_BAR_CHART " %.2X: %s##_INS%d",i,ins->name,i); + break; + case DIV_INS_VIC: + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_VIC]); + name=fmt::sprintf(ICON_FA_BAR_CHART " %.2X: %s##_INS%d",i,ins->name,i); + break; + case DIV_INS_PET: + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_PET]); + name=fmt::sprintf(ICON_FA_SQUARE " %.2X: %s##_INS%d",i,ins->name,i); + break; + case DIV_INS_VRC6: + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_VRC6]); + name=fmt::sprintf(ICON_FA_BAR_CHART " %.2X: %s##_INS%d",i,ins->name,i); + break; + case DIV_INS_VRC6_SAW: + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_VRC6_SAW]); + name=fmt::sprintf(ICON_FA_BAR_CHART " %.2X: %s##_INS%d",i,ins->name,i); + break; + case DIV_INS_OPLL: + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_OPLL]); + name=fmt::sprintf(ICON_FA_AREA_CHART " %.2X: %s##_INS%d",i,ins->name,i); + break; + case DIV_INS_OPL: + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_OPL]); + name=fmt::sprintf(ICON_FA_AREA_CHART " %.2X: %s##_INS%d",i,ins->name,i); + break; + case DIV_INS_FDS: + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_FDS]); + name=fmt::sprintf(ICON_FA_FLOPPY_O " %.2X: %s##_INS%d",i,ins->name,i); + break; + case DIV_INS_VBOY: + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_VBOY]); + name=fmt::sprintf(ICON_FA_BINOCULARS " %.2X: %s##_INS%d",i,ins->name,i); + break; + case DIV_INS_N163: + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_N163]); + name=fmt::sprintf(ICON_FA_CALCULATOR " %.2X: %s##_INS%d",i,ins->name,i); + break; + case DIV_INS_SCC: + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_SCC]); + name=fmt::sprintf(ICON_FA_CALCULATOR " %.2X: %s##_INS%d",i,ins->name,i); + break; + case DIV_INS_OPZ: + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_OPZ]); + name=fmt::sprintf(ICON_FA_AREA_CHART " %.2X: %s##_INS%d",i,ins->name,i); + break; + case DIV_INS_POKEY: + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_POKEY]); + name=fmt::sprintf(ICON_FA_BAR_CHART " %.2X: %s##_INS%d",i,ins->name,i); + break; + case DIV_INS_BEEPER: + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_BEEPER]); + name=fmt::sprintf(ICON_FA_SQUARE " %.2X: %s##_INS%d",i,ins->name,i); + break; + case DIV_INS_SWAN: + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_SWAN]); + name=fmt::sprintf(ICON_FA_GAMEPAD " %.2X: %s##_INS%d",i,ins->name,i); + break; + case DIV_INS_MIKEY: + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_MIKEY]); + name=fmt::sprintf(ICON_FA_BAR_CHART " %.2X: %s##_INS%d",i,ins->name,i); + break; + case DIV_INS_VERA: + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_VERA]); + name=fmt::sprintf(ICON_FA_KEYBOARD_O " %.2X: %s##_INS%d",i,ins->name,i); + break; + case DIV_INS_X1_010: + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_X1_010]); + name=fmt::sprintf(ICON_FA_BAR_CHART " %.2X: %s##_INS%d",i,ins->name,i); + break; + default: + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_UNKNOWN]); + name=fmt::sprintf(ICON_FA_QUESTION " %.2X: %s##_INS%d",i,ins->name,i); + break; + } + } else { + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_TEXT]); } ImGui::TableNextRow(); ImGui::TableNextColumn(); - if (ImGui::Selectable(name.c_str(),curIns==i)) { + if (ImGui::Selectable(name.c_str(),(i==-1)?(curIns<0 || curIns>=e->song.insLen):(curIns==i))) { curIns=i; } if (settings.insFocusesPattern && patternOpen && ImGui::IsItemActivated()) { @@ -199,8 +205,8 @@ void FurnaceGUI::drawInsList() { curIns=i; } ImGui::PopStyleColor(); - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("%s",(ins->type>DIV_INS_MAX)?"Unknown":insTypes[ins->type]); + if (ImGui::IsItemHovered() && i>=0) { + ImGui::SetTooltip("%s",insType); if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) { insEditOpen=true; nextWindow=GUI_WINDOW_INS_EDIT; From bfdfac004fe261239004662898b2b0bf8c1fee27 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 21 Apr 2022 03:22:01 -0500 Subject: [PATCH 151/342] GUI: fix ins 0 being inserted on blank song --- TODO.md | 1 - src/gui/gui.cpp | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/TODO.md b/TODO.md index 03e465ce6..7204a6d12 100644 --- a/TODO.md +++ b/TODO.md @@ -21,7 +21,6 @@ - ability to customize startup system - store system presets in new file - Game Boy envelope macro/sequence -- Game Boy envelope view - option to display chip names instead of "multi-system" on title bar - rewrite the system name detection function anyway - add nightly.link diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 068eb11d5..8993a7d76 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -915,6 +915,7 @@ void FurnaceGUI::noteInput(int num, int key, int vol) { } pat->data[cursor.y][1]=(unsigned char)pat->data[cursor.y][1]; if (latchIns==-2) { + if (curIns>=(int)e->song.ins.size()) curIns=-1; pat->data[cursor.y][2]=curIns; } else if (latchIns!=-1 && !e->song.ins.empty()) { pat->data[cursor.y][2]=MIN(((int)e->song.ins.size())-1,latchIns); From 21c4d09c06a76092befbd89e6ce1ba4cf7e18f94 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 21 Apr 2022 03:26:34 -0500 Subject: [PATCH 152/342] GUI: "none" instrument should not remove inscolumn --- src/gui/gui.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 8993a7d76..689e26385 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -916,7 +916,9 @@ void FurnaceGUI::noteInput(int num, int key, int vol) { pat->data[cursor.y][1]=(unsigned char)pat->data[cursor.y][1]; if (latchIns==-2) { if (curIns>=(int)e->song.ins.size()) curIns=-1; - pat->data[cursor.y][2]=curIns; + if (curIns>=0) { + pat->data[cursor.y][2]=curIns; + } } else if (latchIns!=-1 && !e->song.ins.empty()) { pat->data[cursor.y][2]=MIN(((int)e->song.ins.size())-1,latchIns); } From bc235ed4943fcc3887e99a38202328ba41565ef7 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 21 Apr 2022 03:45:06 -0500 Subject: [PATCH 153/342] GUI: prepare for separate operation mask --- src/gui/editing.cpp | 46 ++++++++++++++++++++++----------------------- src/gui/gui.cpp | 25 ++++++++++-------------- src/gui/gui.h | 12 +++++++++++- 3 files changed, 44 insertions(+), 39 deletions(-) diff --git a/src/gui/editing.cpp b/src/gui/editing.cpp index bd17e20ab..ce1cb21c2 100644 --- a/src/gui/editing.cpp +++ b/src/gui/editing.cpp @@ -177,17 +177,17 @@ void FurnaceGUI::doSelectAll() { } } -#define maskOut(x) \ +#define maskOut(m,x) \ if (x==0) { \ - if (!opMaskNote) continue; \ + if (!m.note) continue; \ } else if (x==1) { \ - if (!opMaskIns) continue; \ + if (!m.ins) continue; \ } else if (x==2) { \ - if (!opMaskVol) continue; \ + if (!m.vol) continue; \ } else if (((x)&1)==0) { \ - if (!opMaskEffectVal) continue; \ + if (!m.effectVal) continue; \ } else if (((x)&1)==1) { \ - if (!opMaskEffect) continue; \ + if (!m.effect) continue; \ } void FurnaceGUI::doDelete() { @@ -201,7 +201,7 @@ void FurnaceGUI::doDelete() { if (!e->song.chanShow[iCoarse]) continue; DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true); for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsedata[j][iFine]=0; @@ -238,7 +238,7 @@ void FurnaceGUI::doPullDelete() { if (!e->song.chanShow[iCoarse]) continue; DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true); for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsesong.patLen; j++) { if (jsong.patLen-1) { if (iFine==0) { @@ -270,7 +270,7 @@ void FurnaceGUI::doInsert() { if (!e->song.chanShow[iCoarse]) continue; DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true); for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsesong.patLen-1; j>=selStart.y; j--) { if (j==selStart.y) { if (iFine==0) { @@ -302,7 +302,7 @@ void FurnaceGUI::doTranspose(int amount) { if (!e->song.chanShow[iCoarse]) continue; DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true); for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsedata[j][0]; @@ -469,7 +469,7 @@ void FurnaceGUI::doPaste(PasteMode mode) { note[2]=line[charPos++]; note[3]=0; - if (iFine==0 && !opMaskNote) { + if (iFine==0 && !opMask.note) { iFine++; continue; } @@ -498,22 +498,22 @@ void FurnaceGUI::doPaste(PasteMode mode) { note[2]=0; if (iFine==1) { - if (!opMaskIns) { + if (!opMask.ins) { iFine++; continue; } } else if (iFine==2) { - if (!opMaskVol) { + if (!opMask.vol) { iFine++; continue; } } else if ((iFine&1)==0) { - if (!opMaskEffectVal) { + if (!opMask.effectVal) { iFine++; continue; } } else if ((iFine&1)==1) { - if (!opMaskEffect) { + if (!opMask.effect) { iFine++; continue; } @@ -589,7 +589,7 @@ void FurnaceGUI::doInterpolate() { if (!e->song.chanShow[iCoarse]) continue; DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true); for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsesong.chanShow[iCoarse]) continue; DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true); for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsesong.chanShow[iCoarse]) continue; DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true); for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsesong.chanShow[iCoarse]) continue; DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true); for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsesong.chanShow[iCoarse]) continue; DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true); for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsesong.chanShow[iCoarse]) continue; DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true); for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsedata[j][0]; @@ -825,7 +825,7 @@ void FurnaceGUI::doCollapse(int divider) { if (!e->song.chanShow[iCoarse]) continue; DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true); for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsedata[j][0]; @@ -882,7 +882,7 @@ void FurnaceGUI::doExpand(int multiplier) { if (!e->song.chanShow[iCoarse]) continue; DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true); for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsedata[j][0]; diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 689e26385..83dbdce7b 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -1798,30 +1798,30 @@ void FurnaceGUI::editOptions(bool topMenu) { ImGui::TableNextRow(); ImGui::TableNextColumn(); ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_ACTIVE]); - if (ImGui::Selectable(opMaskNote?"C-4##opMaskNote":"---##opMaskNote",opMaskNote,ImGuiSelectableFlags_DontClosePopups)) { - opMaskNote=!opMaskNote; + if (ImGui::Selectable(opMask.note?"C-4##opMaskNote":"---##opMaskNote",opMask.note,ImGuiSelectableFlags_DontClosePopups)) { + opMask.note=!opMask.note; } ImGui::PopStyleColor(); ImGui::TableNextColumn(); ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_INS]); - if (ImGui::Selectable(opMaskIns?"01##opMaskIns":"--##opMaskIns",opMaskIns,ImGuiSelectableFlags_DontClosePopups)) { - opMaskIns=!opMaskIns; + if (ImGui::Selectable(opMask.ins?"01##opMaskIns":"--##opMaskIns",opMask.ins,ImGuiSelectableFlags_DontClosePopups)) { + opMask.ins=!opMask.ins; } ImGui::PopStyleColor(); ImGui::TableNextColumn(); ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_VOLUME_MAX]); - if (ImGui::Selectable(opMaskVol?"7F##opMaskVol":"--##opMaskVol",opMaskVol,ImGuiSelectableFlags_DontClosePopups)) { - opMaskVol=!opMaskVol; + if (ImGui::Selectable(opMask.vol?"7F##opMaskVol":"--##opMaskVol",opMask.vol,ImGuiSelectableFlags_DontClosePopups)) { + opMask.vol=!opMask.vol; } ImGui::PopStyleColor(); ImGui::TableNextColumn(); ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_PITCH]); - if (ImGui::Selectable(opMaskEffect?"04##opMaskEffect":"--##opMaskEffect",opMaskEffect,ImGuiSelectableFlags_DontClosePopups)) { - opMaskEffect=!opMaskEffect; + if (ImGui::Selectable(opMask.effect?"04##opMaskEffect":"--##opMaskEffect",opMask.effect,ImGuiSelectableFlags_DontClosePopups)) { + opMask.effect=!opMask.effect; } ImGui::TableNextColumn(); - if (ImGui::Selectable(opMaskEffectVal?"72##opMaskEffectVal":"--##opMaskEffectVal",opMaskEffectVal,ImGuiSelectableFlags_DontClosePopups)) { - opMaskEffectVal=!opMaskEffectVal; + if (ImGui::Selectable(opMask.effectVal?"72##opMaskEffectVal":"--##opMaskEffectVal",opMask.effectVal,ImGuiSelectableFlags_DontClosePopups)) { + opMask.effectVal=!opMask.effectVal; } ImGui::PopStyleColor(); ImGui::EndTable(); @@ -3565,11 +3565,6 @@ FurnaceGUI::FurnaceGUI(): curWindow(GUI_WINDOW_NOTHING), nextWindow(GUI_WINDOW_NOTHING), nextDesc(NULL), - opMaskNote(true), - opMaskIns(true), - opMaskVol(true), - opMaskEffect(true), - opMaskEffectVal(true), latchNote(-1), latchIns(-2), latchVol(-1), diff --git a/src/gui/gui.h b/src/gui/gui.h index 6db3c4a23..eaf52dc72 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -683,6 +683,16 @@ struct Particle { lifeSpeed(lS) {} }; +struct OperationMask { + bool note, ins, vol, effect, effectVal; + OperationMask(): + note(true), + ins(true), + vol(true), + effect(true), + effectVal(true) {} +}; + struct FurnaceGUISysDef { const char* name; std::vector definition; @@ -937,7 +947,7 @@ class FurnaceGUI { float patChanSlideY[DIV_MAX_CHANS+1]; const int* nextDesc; - bool opMaskNote, opMaskIns, opMaskVol, opMaskEffect, opMaskEffectVal; + OperationMask opMask; short latchNote, latchIns, latchVol, latchEffect, latchEffectVal; // bit 31: ctrl From 521bb9c16a79cbc19b9595c112489df686c484af Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 21 Apr 2022 16:01:46 -0500 Subject: [PATCH 154/342] GUI: add missing FDS preset --- src/gui/presets.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/gui/presets.cpp b/src/gui/presets.cpp index 5dd3b693d..d6e439a68 100644 --- a/src/gui/presets.cpp +++ b/src/gui/presets.cpp @@ -726,6 +726,13 @@ void FurnaceGUI::initSystemPresets() { 0 } )); + cat.systems.push_back(FurnaceGUISysDef( + "Famicom Disk System", { + DIV_SYSTEM_NES, 64, 0, 0, + DIV_SYSTEM_FDS, 64, 0, 0, + 0 + } + )); cat.systems.push_back(FurnaceGUISysDef( "Commodore 64 (6581 SID)", { DIV_SYSTEM_C64_6581, 64, 0, 1, From 023761373db3656ebd5e547c7742091e095ff3af Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 21 Apr 2022 17:32:28 -0500 Subject: [PATCH 155/342] GUI: separate operation masks also separate note/value transpose commands --- TODO.md | 3 +- src/gui/doAction.cpp | 20 ++++-- src/gui/editing.cpp | 36 +++++----- src/gui/gui.cpp | 164 ++++++++++++++++++++++++++++++++----------- src/gui/gui.h | 10 ++- src/gui/guiConst.cpp | 4 ++ 6 files changed, 170 insertions(+), 67 deletions(-) diff --git a/TODO.md b/TODO.md index 7204a6d12..f9230306d 100644 --- a/TODO.md +++ b/TODO.md @@ -26,14 +26,13 @@ - add nightly.link - scroll instrument/wave/sample list when selecting item - unified data view -- separate "transpose note" and "transpose value" - see next point - volume commands should work on Game Boy - macro editor menu - refactor sysDef.cpp - add another FM editor layout - try to find out why does VSlider not accept keyboard input - finish lock layout -- note input latch! and separate edit masks +- note input latch! - if macros have release, note off should release them - add "don't scroll on cursor movement" option - add ability to select entire row when clicking on row number diff --git a/src/gui/doAction.cpp b/src/gui/doAction.cpp index fc0dbef90..c12df2fe4 100644 --- a/src/gui/doAction.cpp +++ b/src/gui/doAction.cpp @@ -301,16 +301,28 @@ void FurnaceGUI::doAction(int what) { break; case GUI_ACTION_PAT_NOTE_UP: - doTranspose(1); + doTranspose(1,opMaskTransposeNote); break; case GUI_ACTION_PAT_NOTE_DOWN: - doTranspose(-1); + doTranspose(-1,opMaskTransposeNote); break; case GUI_ACTION_PAT_OCTAVE_UP: - doTranspose(12); + doTranspose(12,opMaskTransposeNote); break; case GUI_ACTION_PAT_OCTAVE_DOWN: - doTranspose(-12); + doTranspose(-12,opMaskTransposeNote); + break; + case GUI_ACTION_PAT_VALUE_UP: + doTranspose(1,opMaskTransposeValue); + break; + case GUI_ACTION_PAT_VALUE_DOWN: + doTranspose(-1,opMaskTransposeValue); + break; + case GUI_ACTION_PAT_VALUE_UP_COARSE: + doTranspose(16,opMaskTransposeValue); + break; + case GUI_ACTION_PAT_VALUE_DOWN_COARSE: + doTranspose(-16,opMaskTransposeValue); break; case GUI_ACTION_PAT_SELECT_ALL: doSelectAll(); diff --git a/src/gui/editing.cpp b/src/gui/editing.cpp index ce1cb21c2..106ec61b1 100644 --- a/src/gui/editing.cpp +++ b/src/gui/editing.cpp @@ -201,7 +201,7 @@ void FurnaceGUI::doDelete() { if (!e->song.chanShow[iCoarse]) continue; DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true); for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsedata[j][iFine]=0; @@ -238,7 +238,7 @@ void FurnaceGUI::doPullDelete() { if (!e->song.chanShow[iCoarse]) continue; DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true); for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsesong.patLen; j++) { if (jsong.patLen-1) { if (iFine==0) { @@ -270,7 +270,7 @@ void FurnaceGUI::doInsert() { if (!e->song.chanShow[iCoarse]) continue; DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true); for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsesong.patLen-1; j>=selStart.y; j--) { if (j==selStart.y) { if (iFine==0) { @@ -291,7 +291,7 @@ void FurnaceGUI::doInsert() { makeUndo(GUI_UNDO_PATTERN_PUSH); } -void FurnaceGUI::doTranspose(int amount) { +void FurnaceGUI::doTranspose(int amount, OperationMask& mask) { finishSelection(); prepareUndo(GUI_UNDO_PATTERN_DELETE); curNibble=false; @@ -302,7 +302,7 @@ void FurnaceGUI::doTranspose(int amount) { if (!e->song.chanShow[iCoarse]) continue; DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true); for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsedata[j][0]; @@ -469,7 +469,7 @@ void FurnaceGUI::doPaste(PasteMode mode) { note[2]=line[charPos++]; note[3]=0; - if (iFine==0 && !opMask.note) { + if (iFine==0 && !opMaskPaste.note) { iFine++; continue; } @@ -498,22 +498,22 @@ void FurnaceGUI::doPaste(PasteMode mode) { note[2]=0; if (iFine==1) { - if (!opMask.ins) { + if (!opMaskPaste.ins) { iFine++; continue; } } else if (iFine==2) { - if (!opMask.vol) { + if (!opMaskPaste.vol) { iFine++; continue; } } else if ((iFine&1)==0) { - if (!opMask.effectVal) { + if (!opMaskPaste.effectVal) { iFine++; continue; } } else if ((iFine&1)==1) { - if (!opMask.effect) { + if (!opMaskPaste.effect) { iFine++; continue; } @@ -589,7 +589,7 @@ void FurnaceGUI::doInterpolate() { if (!e->song.chanShow[iCoarse]) continue; DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true); for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsesong.chanShow[iCoarse]) continue; DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true); for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsesong.chanShow[iCoarse]) continue; DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true); for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsesong.chanShow[iCoarse]) continue; DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true); for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsesong.chanShow[iCoarse]) continue; DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true); for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsesong.chanShow[iCoarse]) continue; DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true); for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsedata[j][0]; @@ -825,7 +825,7 @@ void FurnaceGUI::doCollapse(int divider) { if (!e->song.chanShow[iCoarse]) continue; DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true); for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsedata[j][0]; @@ -882,7 +882,7 @@ void FurnaceGUI::doExpand(int multiplier) { if (!e->song.chanShow[iCoarse]) continue; DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true); for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsedata[j][0]; diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 83dbdce7b..09709b50b 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -1772,6 +1772,44 @@ void FurnaceGUI::processDrags(int dragX, int dragY) { fileName+=fallback; \ } +#define drawOpMask(m) \ + ImGui::PushFont(patFont); \ + ImGui::PushID("om_" #m); \ + if (ImGui::BeginTable("opMaskTable",5,ImGuiTableFlags_Borders|ImGuiTableFlags_SizingFixedFit|ImGuiTableFlags_NoHostExtendX)) { \ + ImGui::TableNextRow(); \ + ImGui::TableNextColumn(); \ + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_ACTIVE]); \ + if (ImGui::Selectable(m.note?"C-4##opMaskNote":"---##opMaskNote",m.note,ImGuiSelectableFlags_DontClosePopups)) { \ + m.note=!m.note; \ + } \ + ImGui::PopStyleColor(); \ + ImGui::TableNextColumn(); \ + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_INS]); \ + if (ImGui::Selectable(m.ins?"01##opMaskIns":"--##opMaskIns",m.ins,ImGuiSelectableFlags_DontClosePopups)) { \ + m.ins=!m.ins; \ + } \ + ImGui::PopStyleColor(); \ + ImGui::TableNextColumn(); \ + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_VOLUME_MAX]); \ + if (ImGui::Selectable(m.vol?"7F##opMaskVol":"--##opMaskVol",m.vol,ImGuiSelectableFlags_DontClosePopups)) { \ + m.vol=!m.vol; \ + } \ + ImGui::PopStyleColor(); \ + ImGui::TableNextColumn(); \ + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_PITCH]); \ + if (ImGui::Selectable(m.effect?"04##opMaskEffect":"--##opMaskEffect",m.effect,ImGuiSelectableFlags_DontClosePopups)) { \ + m.effect=!m.effect; \ + } \ + ImGui::TableNextColumn(); \ + if (ImGui::Selectable(m.effectVal?"72##opMaskEffectVal":"--##opMaskEffectVal",m.effectVal,ImGuiSelectableFlags_DontClosePopups)) { \ + m.effectVal=!m.effectVal; \ + } \ + ImGui::PopStyleColor(); \ + ImGui::EndTable(); \ + } \ + ImGui::PopID(); \ + ImGui::PopFont(); + void FurnaceGUI::editOptions(bool topMenu) { char id[4096]; if (ImGui::MenuItem("cut",BIND_FOR(GUI_ACTION_PAT_CUT))) doCopy(true); @@ -1790,43 +1828,61 @@ void FurnaceGUI::editOptions(bool topMenu) { } ImGui::Separator(); - ImGui::Text("operation mask"); - ImGui::SameLine(); + if (ImGui::BeginMenu("operation mask...")) { + drawOpMask(opMaskDelete); + ImGui::SameLine(); + ImGui::Text("delete"); - ImGui::PushFont(patFont); - if (ImGui::BeginTable("opMaskTable",5,ImGuiTableFlags_Borders|ImGuiTableFlags_SizingFixedFit|ImGuiTableFlags_NoHostExtendX)) { - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_ACTIVE]); - if (ImGui::Selectable(opMask.note?"C-4##opMaskNote":"---##opMaskNote",opMask.note,ImGuiSelectableFlags_DontClosePopups)) { - opMask.note=!opMask.note; - } - ImGui::PopStyleColor(); - ImGui::TableNextColumn(); - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_INS]); - if (ImGui::Selectable(opMask.ins?"01##opMaskIns":"--##opMaskIns",opMask.ins,ImGuiSelectableFlags_DontClosePopups)) { - opMask.ins=!opMask.ins; - } - ImGui::PopStyleColor(); - ImGui::TableNextColumn(); - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_VOLUME_MAX]); - if (ImGui::Selectable(opMask.vol?"7F##opMaskVol":"--##opMaskVol",opMask.vol,ImGuiSelectableFlags_DontClosePopups)) { - opMask.vol=!opMask.vol; - } - ImGui::PopStyleColor(); - ImGui::TableNextColumn(); - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_PITCH]); - if (ImGui::Selectable(opMask.effect?"04##opMaskEffect":"--##opMaskEffect",opMask.effect,ImGuiSelectableFlags_DontClosePopups)) { - opMask.effect=!opMask.effect; - } - ImGui::TableNextColumn(); - if (ImGui::Selectable(opMask.effectVal?"72##opMaskEffectVal":"--##opMaskEffectVal",opMask.effectVal,ImGuiSelectableFlags_DontClosePopups)) { - opMask.effectVal=!opMask.effectVal; - } - ImGui::PopStyleColor(); - ImGui::EndTable(); + drawOpMask(opMaskPullDelete); + ImGui::SameLine(); + ImGui::Text("pull delete"); + + drawOpMask(opMaskInsert); + ImGui::SameLine(); + ImGui::Text("insert"); + + drawOpMask(opMaskPaste); + ImGui::SameLine(); + ImGui::Text("paste"); + + drawOpMask(opMaskTransposeNote); + ImGui::SameLine(); + ImGui::Text("transpose (note)"); + + drawOpMask(opMaskTransposeValue); + ImGui::SameLine(); + ImGui::Text("transpose (value)"); + + drawOpMask(opMaskInterpolate); + ImGui::SameLine(); + ImGui::Text("interpolate"); + + drawOpMask(opMaskFade); + ImGui::SameLine(); + ImGui::Text("fade"); + + drawOpMask(opMaskInvertVal); + ImGui::SameLine(); + ImGui::Text("invert values"); + + drawOpMask(opMaskScale); + ImGui::SameLine(); + ImGui::Text("scale"); + + drawOpMask(opMaskRandomize); + ImGui::SameLine(); + ImGui::Text("randomize"); + + drawOpMask(opMaskFlip); + ImGui::SameLine(); + ImGui::Text("flip"); + + drawOpMask(opMaskCollapseExpand); + ImGui::SameLine(); + ImGui::Text("collapse/expand"); + + ImGui::EndMenu(); } - ImGui::PopFont(); ImGui::Text("input latch"); if (ImGui::MenuItem("set latch",BIND_FOR(GUI_ACTION_PAT_LATCH))) { @@ -1834,17 +1890,31 @@ void FurnaceGUI::editOptions(bool topMenu) { } ImGui::Separator(); - if (ImGui::MenuItem("note up",BIND_FOR(GUI_ACTION_PAT_NOTE_UP))) doTranspose(1); - if (ImGui::MenuItem("note down",BIND_FOR(GUI_ACTION_PAT_NOTE_DOWN))) doTranspose(-1); - if (ImGui::MenuItem("octave up",BIND_FOR(GUI_ACTION_PAT_OCTAVE_UP))) doTranspose(12); - if (ImGui::MenuItem("octave down",BIND_FOR(GUI_ACTION_PAT_OCTAVE_DOWN))) doTranspose(-12); + if (ImGui::MenuItem("note up",BIND_FOR(GUI_ACTION_PAT_NOTE_UP))) doTranspose(1,opMaskTransposeNote); + if (ImGui::MenuItem("note down",BIND_FOR(GUI_ACTION_PAT_NOTE_DOWN))) doTranspose(-1,opMaskTransposeNote); + if (ImGui::MenuItem("octave up",BIND_FOR(GUI_ACTION_PAT_OCTAVE_UP))) doTranspose(12,opMaskTransposeNote); + if (ImGui::MenuItem("octave down",BIND_FOR(GUI_ACTION_PAT_OCTAVE_DOWN))) doTranspose(-12,opMaskTransposeNote); + ImGui::Separator(); + if (ImGui::MenuItem("values up",BIND_FOR(GUI_ACTION_PAT_VALUE_UP))) doTranspose(1,opMaskTransposeValue); + if (ImGui::MenuItem("values down",BIND_FOR(GUI_ACTION_PAT_VALUE_DOWN))) doTranspose(-1,opMaskTransposeValue); + if (ImGui::MenuItem("values up (+16)",BIND_FOR(GUI_ACTION_PAT_VALUE_UP_COARSE))) doTranspose(16,opMaskTransposeValue); + if (ImGui::MenuItem("values down (-16)",BIND_FOR(GUI_ACTION_PAT_VALUE_DOWN_COARSE))) doTranspose(-16,opMaskTransposeValue); + ImGui::Separator(); + ImGui::Text("transpose"); + ImGui::SameLine(); + ImGui::SetNextItemWidth(120.0f*dpiScale); if (ImGui::InputInt("##TransposeAmount",&transposeAmount,1,1)) { if (transposeAmount<-96) transposeAmount=-96; if (transposeAmount>96) transposeAmount=96; } ImGui::SameLine(); - if (ImGui::Button("Transpose")) { - doTranspose(transposeAmount); + if (ImGui::Button("Notes")) { + doTranspose(transposeAmount,opMaskTransposeNote); + ImGui::CloseCurrentPopup(); + } + ImGui::SameLine(); + if (ImGui::Button("Values")) { + doTranspose(transposeAmount,opMaskTransposeNote); ImGui::CloseCurrentPopup(); } @@ -3699,6 +3769,18 @@ FurnaceGUI::FurnaceGUI(): peak[0]=0; peak[1]=0; + opMaskTransposeNote.note=true; + opMaskTransposeNote.ins=false; + opMaskTransposeNote.vol=false; + opMaskTransposeNote.effect=false; + opMaskTransposeNote.effectVal=false; + + opMaskTransposeValue.note=false; + opMaskTransposeValue.ins=true; + opMaskTransposeValue.vol=true; + opMaskTransposeValue.effect=true; + opMaskTransposeValue.effectVal=true; + memset(actionKeys,0,GUI_ACTION_MAX*sizeof(int)); memset(patChanX,0,sizeof(float)*(DIV_MAX_CHANS+1)); diff --git a/src/gui/gui.h b/src/gui/gui.h index eaf52dc72..328ad6a71 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -335,6 +335,10 @@ enum FurnaceGUIActions { GUI_ACTION_PAT_NOTE_DOWN, GUI_ACTION_PAT_OCTAVE_UP, GUI_ACTION_PAT_OCTAVE_DOWN, + GUI_ACTION_PAT_VALUE_UP, + GUI_ACTION_PAT_VALUE_DOWN, + GUI_ACTION_PAT_VALUE_UP_COARSE, + GUI_ACTION_PAT_VALUE_DOWN_COARSE, GUI_ACTION_PAT_SELECT_ALL, GUI_ACTION_PAT_CUT, GUI_ACTION_PAT_COPY, @@ -947,7 +951,9 @@ class FurnaceGUI { float patChanSlideY[DIV_MAX_CHANS+1]; const int* nextDesc; - OperationMask opMask; + OperationMask opMaskDelete, opMaskPullDelete, opMaskInsert, opMaskPaste, opMaskTransposeNote, opMaskTransposeValue; + OperationMask opMaskInterpolate, opMaskFade, opMaskInvertVal, opMaskScale; + OperationMask opMaskRandomize, opMaskFlip, opMaskCollapseExpand; short latchNote, latchIns, latchVol, latchEffect, latchEffectVal; // bit 31: ctrl @@ -1178,7 +1184,7 @@ class FurnaceGUI { void doDelete(); void doPullDelete(); void doInsert(); - void doTranspose(int amount); + void doTranspose(int amount, OperationMask& mask); void doCopy(bool cut); void doPaste(PasteMode mode=GUI_PASTE_MODE_NORMAL); void doChangeIns(int ins); diff --git a/src/gui/guiConst.cpp b/src/gui/guiConst.cpp index 99068e95f..b5d437072 100644 --- a/src/gui/guiConst.cpp +++ b/src/gui/guiConst.cpp @@ -258,6 +258,10 @@ const FurnaceGUIActionDef guiActions[GUI_ACTION_MAX]={ D("PAT_NOTE_DOWN", "Transpose (-1)", FURKMOD_CMD|SDLK_F1), D("PAT_OCTAVE_UP", "Transpose (+1 octave)", FURKMOD_CMD|SDLK_F4), D("PAT_OCTAVE_DOWN", "Transpose (-1 octave)", FURKMOD_CMD|SDLK_F3), + D("PAT_VALUE_UP", "Increase values (+1)", FURKMOD_CMD|FURKMOD_SHIFT|SDLK_F2), + D("PAT_VALUE_DOWN", "Increase values (-1)", FURKMOD_CMD|FURKMOD_SHIFT|SDLK_F1), + D("PAT_VALUE_UP_COARSE", "Increase values (+16)", FURKMOD_CMD|FURKMOD_SHIFT|SDLK_F4), + D("PAT_VALUE_DOWN_COARSE", "Increase values (-16)", FURKMOD_CMD|FURKMOD_SHIFT|SDLK_F3), D("PAT_SELECT_ALL", "Select all", FURKMOD_CMD|SDLK_a), D("PAT_CUT", "Cut", FURKMOD_CMD|SDLK_x), D("PAT_COPY", "Copy", FURKMOD_CMD|SDLK_c), From 741062242511465db7e7cc50b9bcc33a690aece0 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 21 Apr 2022 17:39:13 -0500 Subject: [PATCH 156/342] GUI: min ins selector width fix --- src/gui/insEdit.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index baca9469e..7879d966e 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -1346,6 +1346,7 @@ void FurnaceGUI::drawInsEdit() { ImGui::TableNextRow(); ImGui::TableNextColumn(); String insIndex=fmt::sprintf("%.2X",curIns); + ImGui::SetNextItemWidth(72.0f*dpiScale); if (ImGui::BeginCombo("##InsSelect",insIndex.c_str())) { String name; for (size_t i=0; isong.ins.size(); i++) { From 50deead11a1707a24f6b19e63dc48e5bbe0e64c7 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 21 Apr 2022 18:10:59 -0500 Subject: [PATCH 157/342] GUI: prepare for note input latch UI --- src/gui/gui.cpp | 71 ++++++++++++++++++++++++++++++++++++++++++++++++- src/gui/gui.h | 4 +-- 2 files changed, 72 insertions(+), 3 deletions(-) diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 09709b50b..dba51591f 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -1812,6 +1812,7 @@ void FurnaceGUI::processDrags(int dragX, int dragY) { void FurnaceGUI::editOptions(bool topMenu) { char id[4096]; + editOptsVisible=true; if (ImGui::MenuItem("cut",BIND_FOR(GUI_ACTION_PAT_CUT))) doCopy(true); if (ImGui::MenuItem("copy",BIND_FOR(GUI_ACTION_PAT_COPY))) doCopy(false); if (ImGui::MenuItem("paste",BIND_FOR(GUI_ACTION_PAT_PASTE))) doPaste(); @@ -1885,7 +1886,68 @@ void FurnaceGUI::editOptions(bool topMenu) { } ImGui::Text("input latch"); - if (ImGui::MenuItem("set latch",BIND_FOR(GUI_ACTION_PAT_LATCH))) { + if (ImGui::BeginTable("opMaskTable",5,ImGuiTableFlags_Borders|ImGuiTableFlags_SizingFixedFit|ImGuiTableFlags_NoHostExtendX)) { + static char id[64]; + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_ACTIVE]); + ImGui::Text("C-4"); + ImGui::PopStyleColor(); + ImGui::TableNextColumn(); + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_INS]); + if (latchIns==-2) { + strcpy(id,"&&##LatchIns"); + } else if (latchIns==-1) { + strcpy(id,"..##LatchIns"); + } else { + snprintf(id,63,"%.2x##LatchIns",latchIns&0xff); + } + if (ImGui::Selectable(id,latchTarget==1,ImGuiSelectableFlags_DontClosePopups)) { + latchTarget=1; + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("&&: selected instrument\n..: no instrument"); + } + ImGui::PopStyleColor(); + ImGui::TableNextColumn(); + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_VOLUME_MAX]); + if (latchVol==-1) { + strcpy(id,"..##LatchVol"); + } else { + snprintf(id,63,"%.2x##LatchVol",latchVol&0xff); + } + if (ImGui::Selectable(id,latchTarget==2,ImGuiSelectableFlags_DontClosePopups)) { + latchTarget=2; + } + ImGui::PopStyleColor(); + ImGui::TableNextColumn(); + if (latchEffect==-1) { + strcpy(id,"..##LatchFX"); + } else { + snprintf(id,63,"%.2x##LatchFX",latchEffect&0xff); + } + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_PITCH]); + if (ImGui::Selectable(id,latchTarget==3,ImGuiSelectableFlags_DontClosePopups)) { + latchTarget=3; + } + ImGui::TableNextColumn(); + if (latchEffectVal==-1) { + strcpy(id,"..##LatchFXV"); + } else { + snprintf(id,63,"%.2x##LatchFXV",latchEffectVal&0xff); + } + if (ImGui::Selectable(id,latchTarget==4,ImGuiSelectableFlags_DontClosePopups)) { + latchTarget=4; + } + ImGui::PopStyleColor(); + ImGui::EndTable(); + } + ImGui::SameLine(); + if (ImGui::Button("Set")) { + // TODO + } + ImGui::SameLine(); + if (ImGui::Button("Clear")) { // TODO } ImGui::Separator(); @@ -2400,6 +2462,7 @@ bool FurnaceGUI::loop() { ImGui::NewFrame(); curWindow=GUI_WINDOW_NOTHING; + editOptsVisible=false; ImGui::BeginMainMenuBar(); if (ImGui::BeginMenu("file")) { @@ -3266,6 +3329,10 @@ bool FurnaceGUI::loop() { willCommit=false; } + if (!editOptsVisible) { + latchTarget=0; + } + if (SDL_GetWindowFlags(sdlWin)&SDL_WINDOW_MINIMIZED) { SDL_Delay(100); } @@ -3564,6 +3631,7 @@ FurnaceGUI::FurnaceGUI(): extraChannelButtons(0), patNameTarget(-1), newSongCategory(0), + latchTarget(0), wheelX(0), wheelY(0), editControlsOpen(true), @@ -3632,6 +3700,7 @@ FurnaceGUI::FurnaceGUI(): tempoView(true), waveHex(false), lockLayout(false), + editOptsVisible(false), curWindow(GUI_WINDOW_NOTHING), nextWindow(GUI_WINDOW_NOTHING), nextDesc(NULL), diff --git a/src/gui/gui.h b/src/gui/gui.h index 328ad6a71..2bd286362 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -927,7 +927,7 @@ class FurnaceGUI { char finalLayoutPath[4096]; int curIns, curWave, curSample, curOctave, curOrder, oldRow, oldOrder, oldOrder1, editStep, exportLoops, soloChan, soloTimeout, orderEditMode, orderCursor; - int loopOrder, loopRow, loopEnd, isClipping, extraChannelButtons, patNameTarget, newSongCategory; + int loopOrder, loopRow, loopEnd, isClipping, extraChannelButtons, patNameTarget, newSongCategory, latchTarget; int wheelX, wheelY; bool editControlsOpen, ordersOpen, insListOpen, songInfoOpen, patternOpen, insEditOpen; @@ -944,7 +944,7 @@ class FurnaceGUI { SelectionPoint selStart, selEnd, cursor; bool selecting, curNibble, orderNibble, followOrders, followPattern, changeAllOrders; - bool collapseWindow, demandScrollX, fancyPattern, wantPatName, firstFrame, tempoView, waveHex, lockLayout; + bool collapseWindow, demandScrollX, fancyPattern, wantPatName, firstFrame, tempoView, waveHex, lockLayout, editOptsVisible; FurnaceGUIWindows curWindow, nextWindow; float peak[2]; float patChanX[DIV_MAX_CHANS+1]; From bd58059261008afd1133f5050a0cf78c0b15c866 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 21 Apr 2022 18:11:34 -0500 Subject: [PATCH 158/342] GUI: transpose value does not transpose effect ID --- src/gui/gui.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index dba51591f..388ac9645 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -3847,7 +3847,7 @@ FurnaceGUI::FurnaceGUI(): opMaskTransposeValue.note=false; opMaskTransposeValue.ins=true; opMaskTransposeValue.vol=true; - opMaskTransposeValue.effect=true; + opMaskTransposeValue.effect=false; opMaskTransposeValue.effectVal=true; memset(actionKeys,0,GUI_ACTION_MAX*sizeof(int)); From 2278c1a465bb65b9ea4046e032e5f5186f71bdff Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 21 Apr 2022 18:30:32 -0500 Subject: [PATCH 159/342] GUI: partially implement note input latch UI --- src/gui/gui.cpp | 115 +++++++++++++++++++++++++++++++++++++++++++++--- src/gui/gui.h | 2 +- 2 files changed, 110 insertions(+), 7 deletions(-) diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 388ac9645..880d6bc57 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -17,6 +17,7 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#include #define _USE_MATH_DEFINES #include "gui.h" #include "util.h" @@ -1018,6 +1019,15 @@ void FurnaceGUI::valueInput(int num, bool direct, int target) { } } +#define changeLatch(x) \ + if (x<0) x=0; \ + if (!latchNibble && !settings.pushNibble) x=0; \ + x=(x<<4)|num; \ + latchNibble=!latchNibble; \ + if (!latchNibble) { \ + if (++latchTarget>4) latchTarget=0; \ + } + void FurnaceGUI::keyDown(SDL_Event& ev) { if (ImGuiFileDialog::Instance()->IsOpened()) return; if (aboutOpen) return; @@ -1059,6 +1069,45 @@ void FurnaceGUI::keyDown(SDL_Event& ev) { return; } + if (latchTarget) { + if (mapped==SDLK_DELETE || mapped==SDLK_BACKSPACE) { + switch (latchTarget) { + case 1: + latchIns=-1; + break; + case 2: + latchVol=-1; + break; + case 3: + latchEffect=-1; + break; + case 4: + latchEffectVal=-1; + break; + } + } else { + try { + int num=valueKeys.at(ev.key.keysym.sym); + switch (latchTarget) { + case 1: // instrument + changeLatch(latchIns); + break; + case 2: // volume + changeLatch(latchVol); + break; + case 3: // effect + changeLatch(latchEffect); + break; + case 4: // effect value + changeLatch(latchEffectVal); + break; + } + } catch (std::out_of_range& e) { + } + } + return; + } + // PER-WINDOW KEYS switch (curWindow) { case GUI_WINDOW_PATTERN: @@ -1904,9 +1953,15 @@ void FurnaceGUI::editOptions(bool topMenu) { } if (ImGui::Selectable(id,latchTarget==1,ImGuiSelectableFlags_DontClosePopups)) { latchTarget=1; + latchNibble=false; + } + if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) { + latchIns=-2; } if (ImGui::IsItemHovered()) { + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_TEXT]); ImGui::SetTooltip("&&: selected instrument\n..: no instrument"); + ImGui::PopStyleColor(); } ImGui::PopStyleColor(); ImGui::TableNextColumn(); @@ -1918,17 +1973,48 @@ void FurnaceGUI::editOptions(bool topMenu) { } if (ImGui::Selectable(id,latchTarget==2,ImGuiSelectableFlags_DontClosePopups)) { latchTarget=2; + latchNibble=false; + } + if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) { + latchVol=-1; } ImGui::PopStyleColor(); ImGui::TableNextColumn(); if (latchEffect==-1) { strcpy(id,"..##LatchFX"); + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_INACTIVE]); } else { - snprintf(id,63,"%.2x##LatchFX",latchEffect&0xff); + const unsigned char data=latchEffect; + snprintf(id,63,"%.2x##LatchFX",data); + if (data<0x10) { + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[fxColors[data]]); + } else if (data<0x20) { + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY]); + } else if (data<0x30) { + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_SYS_SECONDARY]); + } else if (data<0x48) { + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY]); + } else if (data<0x90) { + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_INVALID]); + } else if (data<0xa0) { + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_MISC]); + } else if (data<0xc0) { + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_INVALID]); + } else if (data<0xd0) { + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_SPEED]); + } else if (data<0xe0) { + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_INVALID]); + } else { + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[extFxColors[data-0xe0]]); + } } - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_PITCH]); + if (ImGui::Selectable(id,latchTarget==3,ImGuiSelectableFlags_DontClosePopups)) { latchTarget=3; + latchNibble=false; + } + if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) { + latchEffect=-1; } ImGui::TableNextColumn(); if (latchEffectVal==-1) { @@ -1938,17 +2024,32 @@ void FurnaceGUI::editOptions(bool topMenu) { } if (ImGui::Selectable(id,latchTarget==4,ImGuiSelectableFlags_DontClosePopups)) { latchTarget=4; + latchNibble=false; + } + if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) { + latchEffectVal=-1; } ImGui::PopStyleColor(); ImGui::EndTable(); } ImGui::SameLine(); if (ImGui::Button("Set")) { - // TODO + DivPattern* pat=e->song.pat[cursor.xCoarse].getPattern(e->song.orders.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; } ImGui::SameLine(); - if (ImGui::Button("Clear")) { - // TODO + if (ImGui::Button("Reset")) { + latchIns=-2; + latchVol=-1; + latchEffect=-1; + latchEffectVal=-1; + latchTarget=0; + latchNibble=false; } ImGui::Separator(); @@ -2097,7 +2198,7 @@ int _processEvent(void* instance, SDL_Event* event) { int FurnaceGUI::processEvent(SDL_Event* ev) { if (ev->type==SDL_KEYDOWN) { - if (!ev->key.repeat && !wantCaptureKeyboard && (ev->key.keysym.mod&(~(KMOD_NUM|KMOD_CAPS|KMOD_SCROLL)))==0) { + if (!ev->key.repeat && latchTarget==0 && !wantCaptureKeyboard && (ev->key.keysym.mod&(~(KMOD_NUM|KMOD_CAPS|KMOD_SCROLL)))==0) { if (settings.notePreviewBehavior==0) return 1; switch (curWindow) { case GUI_WINDOW_SAMPLE_EDIT: @@ -3331,6 +3432,7 @@ bool FurnaceGUI::loop() { if (!editOptsVisible) { latchTarget=0; + latchNibble=false; } if (SDL_GetWindowFlags(sdlWin)&SDL_WINDOW_MINIMIZED) { @@ -3701,6 +3803,7 @@ FurnaceGUI::FurnaceGUI(): waveHex(false), lockLayout(false), editOptsVisible(false), + latchNibble(false), curWindow(GUI_WINDOW_NOTHING), nextWindow(GUI_WINDOW_NOTHING), nextDesc(NULL), diff --git a/src/gui/gui.h b/src/gui/gui.h index 2bd286362..6c50a914a 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -944,7 +944,7 @@ class FurnaceGUI { SelectionPoint selStart, selEnd, cursor; bool selecting, curNibble, orderNibble, followOrders, followPattern, changeAllOrders; - bool collapseWindow, demandScrollX, fancyPattern, wantPatName, firstFrame, tempoView, waveHex, lockLayout, editOptsVisible; + bool collapseWindow, demandScrollX, fancyPattern, wantPatName, firstFrame, tempoView, waveHex, lockLayout, editOptsVisible, latchNibble; FurnaceGUIWindows curWindow, nextWindow; float peak[2]; float patChanX[DIV_MAX_CHANS+1]; From 9c8d1223894544763463abeef1624299ee5c4f09 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 22 Apr 2022 01:27:27 -0500 Subject: [PATCH 160/342] GUI: use pattern font for input latch --- TODO.md | 1 - src/gui/gui.cpp | 4 +++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/TODO.md b/TODO.md index f9230306d..4c200e02a 100644 --- a/TODO.md +++ b/TODO.md @@ -32,7 +32,6 @@ - add another FM editor layout - try to find out why does VSlider not accept keyboard input - finish lock layout -- note input latch! - if macros have release, note off should release them - add "don't scroll on cursor movement" option - add ability to select entire row when clicking on row number diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 880d6bc57..009952a63 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -1935,7 +1935,8 @@ void FurnaceGUI::editOptions(bool topMenu) { } ImGui::Text("input latch"); - if (ImGui::BeginTable("opMaskTable",5,ImGuiTableFlags_Borders|ImGuiTableFlags_SizingFixedFit|ImGuiTableFlags_NoHostExtendX)) { + ImGui::PushFont(patFont); + if (ImGui::BeginTable("inputLatchTable",5,ImGuiTableFlags_Borders|ImGuiTableFlags_SizingFixedFit|ImGuiTableFlags_NoHostExtendX)) { static char id[64]; ImGui::TableNextRow(); ImGui::TableNextColumn(); @@ -2032,6 +2033,7 @@ void FurnaceGUI::editOptions(bool topMenu) { ImGui::PopStyleColor(); ImGui::EndTable(); } + ImGui::PopFont(); ImGui::SameLine(); if (ImGui::Button("Set")) { DivPattern* pat=e->song.pat[cursor.xCoarse].getPattern(e->song.orders.ord[cursor.xCoarse][curOrder],true); From 8db53faf720800d769fcbddbb564a24d33e34ccd Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 22 Apr 2022 04:23:52 -0500 Subject: [PATCH 161/342] new FM frequency calculation experiments --- src/engine/dispatch.h | 1 + src/engine/platform/genesis.cpp | 78 +++++++++--------------------- src/engine/platform/genesis.h | 3 -- src/engine/platform/genesisext.cpp | 66 +++++++++++-------------- 4 files changed, 51 insertions(+), 97 deletions(-) diff --git a/src/engine/dispatch.h b/src/engine/dispatch.h index c35ff919c..b358c464c 100644 --- a/src/engine/dispatch.h +++ b/src/engine/dispatch.h @@ -426,6 +426,7 @@ class DivDispatch { #define NOTE_PERIODIC(x) round(parent->calcBaseFreq(chipClock,CHIP_DIVIDER,x,true)) #define NOTE_PERIODIC_NOROUND(x) parent->calcBaseFreq(chipClock,CHIP_DIVIDER,x,true) #define NOTE_FREQUENCY(x) parent->calcBaseFreq(chipClock,CHIP_FREQBASE,x,false) +#define NOTE_FNUM_BLOCK(x,bits) ((((int)parent->calcBaseFreq(chipClock,CHIP_FREQBASE,(x)%12,false))&((1<calcFreq(chan[i].baseFreq,chan[i].pitch,false,octave(chan[i].baseFreq)); - if (chan[i].freq>262143) chan[i].freq=262143; - int freqt=toFreq(chan[i].freq)+chan[i].std.pitch.val; + chan[i].freq=((chan[i].baseFreq&0xf800)|parent->calcFreq(chan[i].baseFreq&0x7ff,chan[i].pitch,false))+chan[i].std.pitch.val; + if (chan[i].freq>65535) chan[i].freq=65535; + int freqt=chan[i].freq; immWrite(chanOffs[i]+ADDR_FREQH,freqt>>8); immWrite(chanOffs[i]+ADDR_FREQ,freqt&0xff); if (chan[i].furnaceDac && dacMode) { @@ -422,47 +422,6 @@ void DivPlatformGenesis::tick(bool sysTick) { } } -int DivPlatformGenesis::octave(int freq) { - if (freq>=82432) { - return 128; - } else if (freq>=41216) { - return 64; - } else if (freq>=20608) { - return 32; - } else if (freq>=10304) { - return 16; - } else if (freq>=5152) { - return 8; - } else if (freq>=2576) { - return 4; - } else if (freq>=1288) { - return 2; - } else { - return 1; - } - return 1; -} - -int DivPlatformGenesis::toFreq(int freq) { - if (freq>=82432) { - return 0x3800|((freq>>7)&0x7ff); - } else if (freq>=41216) { - return 0x3000|((freq>>6)&0x7ff); - } else if (freq>=20608) { - return 0x2800|((freq>>5)&0x7ff); - } else if (freq>=10304) { - return 0x2000|((freq>>4)&0x7ff); - } else if (freq>=5152) { - return 0x1800|((freq>>3)&0x7ff); - } else if (freq>=2576) { - return 0x1000|((freq>>2)&0x7ff); - } else if (freq>=1288) { - return 0x800|((freq>>1)&0x7ff); - } else { - return freq&0x7ff; - } -} - void DivPlatformGenesis::muteChannel(int ch, bool mute) { isMuted[ch]=mute; for (int j=0; j<4; j++) { @@ -509,7 +468,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) { dacPos=0; dacPeriod=0; if (c.value!=DIV_NOTE_NULL) { - chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value); + chan[c.chan].baseFreq=NOTE_FNUM_BLOCK(c.value,11); chan[c.chan].freqChanged=true; } chan[c.chan].furnaceDac=true; @@ -576,7 +535,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) { chan[c.chan].insChanged=false; if (c.value!=DIV_NOTE_NULL) { - chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value); + chan[c.chan].baseFreq=NOTE_FNUM_BLOCK(c.value,11); chan[c.chan].portaPause=false; chan[c.chan].note=c.value; chan[c.chan].freqChanged=true; @@ -656,26 +615,33 @@ int DivPlatformGenesis::dispatch(DivCommand c) { break; } case DIV_CMD_NOTE_PORTA: { - int destFreq=NOTE_FREQUENCY(c.value2); + int destFreq=NOTE_FNUM_BLOCK(c.value2,11); int newFreq; bool return2=false; if (destFreq>chan[c.chan].baseFreq) { - newFreq=chan[c.chan].baseFreq+c.value*octave(chan[c.chan].baseFreq); + newFreq=chan[c.chan].baseFreq+c.value; if (newFreq>=destFreq) { newFreq=destFreq; return2=true; } } else { - newFreq=chan[c.chan].baseFreq-c.value*octave(chan[c.chan].baseFreq); + newFreq=chan[c.chan].baseFreq-c.value; if (newFreq<=destFreq) { newFreq=destFreq; return2=true; } } + // check for octave boundary if (!chan[c.chan].portaPause) { - if (octave(chan[c.chan].baseFreq)!=octave(newFreq)) { - chan[c.chan].portaPause=true; - break; + if ((newFreq&0x7ff)>1288) { + newFreq=((newFreq&0x7ff)>>1)|((newFreq+0x800)&0xf800); + /*chan[c.chan].portaPause=true; + break;*/ + } + if ((newFreq&0x7ff)<644) { + newFreq=(newFreq&0x7ff)<<1|((newFreq-0x800)&0xf800); + /*chan[c.chan].portaPause=true; + break;*/ } } chan[c.chan].baseFreq=newFreq; @@ -699,7 +665,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) { } break; case DIV_CMD_LEGATO: { - chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value); + chan[c.chan].baseFreq=NOTE_FNUM_BLOCK(c.value,11); chan[c.chan].note=c.value; chan[c.chan].freqChanged=true; break; diff --git a/src/engine/platform/genesis.h b/src/engine/platform/genesis.h index 34db5a252..ce52c6df1 100644 --- a/src/engine/platform/genesis.h +++ b/src/engine/platform/genesis.h @@ -93,9 +93,6 @@ class DivPlatformGenesis: public DivDispatch { short oldWrites[512]; short pendingWrites[512]; - int octave(int freq); - int toFreq(int freq); - friend void putDispatchChan(void*,int,int); void acquire_nuked(short* bufL, short* bufR, size_t start, size_t len); diff --git a/src/engine/platform/genesisext.cpp b/src/engine/platform/genesisext.cpp index f0369370c..db51f5ead 100644 --- a/src/engine/platform/genesisext.cpp +++ b/src/engine/platform/genesisext.cpp @@ -72,7 +72,7 @@ int DivPlatformGenesisExt::dispatch(DivCommand c) { opChan[ch].insChanged=false; if (c.value!=DIV_NOTE_NULL) { - opChan[ch].baseFreq=NOTE_FREQUENCY(c.value); + opChan[ch].baseFreq=NOTE_FNUM_BLOCK(c.value,11); opChan[ch].portaPause=false; opChan[ch].freqChanged=true; } @@ -127,31 +127,46 @@ int DivPlatformGenesisExt::dispatch(DivCommand c) { break; } case DIV_CMD_NOTE_PORTA: { - int destFreq=NOTE_FREQUENCY(c.value2); + int destFreq=NOTE_FNUM_BLOCK(c.value2,11); int newFreq; bool return2=false; if (destFreq>opChan[ch].baseFreq) { - newFreq=opChan[ch].baseFreq+c.value*octave(opChan[ch].baseFreq); + newFreq=opChan[ch].baseFreq+c.value; if (newFreq>=destFreq) { newFreq=destFreq; return2=true; } } else { - newFreq=opChan[ch].baseFreq-c.value*octave(opChan[ch].baseFreq); + newFreq=opChan[ch].baseFreq-c.value; if (newFreq<=destFreq) { newFreq=destFreq; return2=true; } } if (!opChan[ch].portaPause) { - if (octave(opChan[ch].baseFreq)!=octave(newFreq)) { + opChan[ch].freqChanged=true; + if ((newFreq&0x7ff)>1288) { + newFreq=((newFreq&0x7ff)>>1)|((newFreq+0x800)&0xf800); opChan[ch].portaPause=true; - break; + opChan[ch].freqChanged=false; + return2=false; + if (ch==3) printf("%d: upper bound\n",ch); + //break; } + if ((newFreq&0x7ff)<644) { + newFreq=(newFreq&0x7ff)<<1|((newFreq-0x800)&0xf800); + opChan[ch].portaPause=true; + opChan[ch].freqChanged=false; + return2=false; + if (ch==3) printf("%d: lower bound\n",ch); + //break; + } + } else { + opChan[ch].portaPause=false; + opChan[ch].freqChanged=true; } + if (ch==3) printf("%d: writing %.4x to freq\n",ch,newFreq); opChan[ch].baseFreq=newFreq; - opChan[ch].portaPause=false; - opChan[ch].freqChanged=true; if (return2) return 2; break; } @@ -172,7 +187,7 @@ int DivPlatformGenesisExt::dispatch(DivCommand c) { } break; case DIV_CMD_LEGATO: { - opChan[ch].baseFreq=NOTE_FREQUENCY(c.value); + opChan[ch].baseFreq=NOTE_FNUM_BLOCK(c.value,11); opChan[ch].freqChanged=true; break; } @@ -289,35 +304,10 @@ void DivPlatformGenesisExt::tick(bool sysTick) { unsigned char writeMask=2; if (extMode) for (int i=0; i<4; i++) { if (opChan[i].freqChanged) { - opChan[i].freq=parent->calcFreq(opChan[i].baseFreq,opChan[i].pitch); - if (opChan[i].freq>262143) opChan[i].freq=262143; - if (opChan[i].freq>=82432) { - opChan[i].freqH=((opChan[i].freq>>15)&7)|0x38; - opChan[i].freqL=(opChan[i].freq>>7)&0xff; - } else if (opChan[i].freq>=41216) { - opChan[i].freqH=((opChan[i].freq>>14)&7)|0x30; - opChan[i].freqL=(opChan[i].freq>>6)&0xff; - } else if (opChan[i].freq>=20608) { - opChan[i].freqH=((opChan[i].freq>>13)&7)|0x28; - opChan[i].freqL=(opChan[i].freq>>5)&0xff; - } else if (opChan[i].freq>=10304) { - opChan[i].freqH=((opChan[i].freq>>12)&7)|0x20; - opChan[i].freqL=(opChan[i].freq>>4)&0xff; - } else if (opChan[i].freq>=5152) { - opChan[i].freqH=((opChan[i].freq>>11)&7)|0x18; - opChan[i].freqL=(opChan[i].freq>>3)&0xff; - } else if (opChan[i].freq>=2576) { - opChan[i].freqH=((opChan[i].freq>>10)&7)|0x10; - opChan[i].freqL=(opChan[i].freq>>2)&0xff; - } else if (opChan[i].freq>=1288) { - opChan[i].freqH=((opChan[i].freq>>9)&7)|0x08; - opChan[i].freqL=(opChan[i].freq>>1)&0xff; - } else { - opChan[i].freqH=(opChan[i].freq>>8)&7; - opChan[i].freqL=opChan[i].freq&0xff; - } - immWrite(opChanOffsH[i],opChan[i].freqH); - immWrite(opChanOffsL[i],opChan[i].freqL); + opChan[i].freq=(opChan[i].baseFreq&0xf800)|parent->calcFreq(opChan[i].baseFreq&0x7ff,opChan[i].pitch); + if (chan[i].freq>65535) chan[i].freq=65535; + immWrite(opChanOffsH[i],opChan[i].freq>>8); + immWrite(opChanOffsL[i],opChan[i].freq&0xff); } writeMask|=opChan[i].active<<(4+i); if (opChan[i].keyOn) { From 943e013cb469319fbbda2cc9540e39677ba773d7 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 22 Apr 2022 16:46:24 -0500 Subject: [PATCH 162/342] YM2612: new pitch slide formula finally nailed it (almost) --- src/engine/platform/genesis.cpp | 21 +++++++++++++-------- src/engine/platform/genesisext.cpp | 10 +++------- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/src/engine/platform/genesis.cpp b/src/engine/platform/genesis.cpp index 80dac1d46..615747dc4 100644 --- a/src/engine/platform/genesis.cpp +++ b/src/engine/platform/genesis.cpp @@ -632,21 +632,26 @@ int DivPlatformGenesis::dispatch(DivCommand c) { } } // check for octave boundary + // what the heck! if (!chan[c.chan].portaPause) { + chan[c.chan].freqChanged=true; if ((newFreq&0x7ff)>1288) { - newFreq=((newFreq&0x7ff)>>1)|((newFreq+0x800)&0xf800); - /*chan[c.chan].portaPause=true; - break;*/ + newFreq=(644)|((newFreq+0x800)&0xf800); + chan[c.chan].portaPause=true; + chan[c.chan].freqChanged=false; + return2=false; } if ((newFreq&0x7ff)<644) { - newFreq=(newFreq&0x7ff)<<1|((newFreq-0x800)&0xf800); - /*chan[c.chan].portaPause=true; - break;*/ + newFreq=(1287)|((newFreq-0x800)&0xf800); + chan[c.chan].portaPause=true; + chan[c.chan].freqChanged=false; + return2=false; } + } else { + chan[c.chan].portaPause=false; + chan[c.chan].freqChanged=true; } chan[c.chan].baseFreq=newFreq; - chan[c.chan].portaPause=false; - chan[c.chan].freqChanged=true; if (return2) { chan[c.chan].inPorta=false; return 2; diff --git a/src/engine/platform/genesisext.cpp b/src/engine/platform/genesisext.cpp index db51f5ead..2f8fba8c1 100644 --- a/src/engine/platform/genesisext.cpp +++ b/src/engine/platform/genesisext.cpp @@ -143,29 +143,25 @@ int DivPlatformGenesisExt::dispatch(DivCommand c) { return2=true; } } + // what the heck! if (!opChan[ch].portaPause) { opChan[ch].freqChanged=true; if ((newFreq&0x7ff)>1288) { - newFreq=((newFreq&0x7ff)>>1)|((newFreq+0x800)&0xf800); + newFreq=(644)|((newFreq+0x800)&0xf800); opChan[ch].portaPause=true; opChan[ch].freqChanged=false; return2=false; - if (ch==3) printf("%d: upper bound\n",ch); - //break; } if ((newFreq&0x7ff)<644) { - newFreq=(newFreq&0x7ff)<<1|((newFreq-0x800)&0xf800); + newFreq=(1287)|((newFreq-0x800)&0xf800); opChan[ch].portaPause=true; opChan[ch].freqChanged=false; return2=false; - if (ch==3) printf("%d: lower bound\n",ch); - //break; } } else { opChan[ch].portaPause=false; opChan[ch].freqChanged=true; } - if (ch==3) printf("%d: writing %.4x to freq\n",ch,newFreq); opChan[ch].baseFreq=newFreq; if (return2) return 2; break; From dd80cb8b55c9adf7e7c2441cf4ca47105300d7d6 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 22 Apr 2022 17:42:42 -0500 Subject: [PATCH 163/342] what the heck --- src/engine/platform/genesis.cpp | 2 +- src/engine/platform/genesisext.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/engine/platform/genesis.cpp b/src/engine/platform/genesis.cpp index 615747dc4..43aae3f13 100644 --- a/src/engine/platform/genesis.cpp +++ b/src/engine/platform/genesis.cpp @@ -394,7 +394,7 @@ void DivPlatformGenesis::tick(bool sysTick) { for (int i=0; i<6; i++) { if (i==2 && extMode) continue; if (chan[i].freqChanged) { - chan[i].freq=((chan[i].baseFreq&0xf800)|parent->calcFreq(chan[i].baseFreq&0x7ff,chan[i].pitch,false))+chan[i].std.pitch.val; + chan[i].freq=((chan[i].baseFreq&0xf800)|parent->calcFreq(chan[i].baseFreq&0x7ff,chan[i].pitch,false,4))+chan[i].std.pitch.val; if (chan[i].freq>65535) chan[i].freq=65535; int freqt=chan[i].freq; immWrite(chanOffs[i]+ADDR_FREQH,freqt>>8); diff --git a/src/engine/platform/genesisext.cpp b/src/engine/platform/genesisext.cpp index 2f8fba8c1..bb33f4017 100644 --- a/src/engine/platform/genesisext.cpp +++ b/src/engine/platform/genesisext.cpp @@ -300,7 +300,7 @@ void DivPlatformGenesisExt::tick(bool sysTick) { unsigned char writeMask=2; if (extMode) for (int i=0; i<4; i++) { if (opChan[i].freqChanged) { - opChan[i].freq=(opChan[i].baseFreq&0xf800)|parent->calcFreq(opChan[i].baseFreq&0x7ff,opChan[i].pitch); + opChan[i].freq=(opChan[i].baseFreq&0xf800)|parent->calcFreq(opChan[i].baseFreq&0x7ff,opChan[i].pitch,false,4); if (chan[i].freq>65535) chan[i].freq=65535; immWrite(opChanOffsH[i],opChan[i].freq>>8); immWrite(opChanOffsL[i],opChan[i].freq&0xff); From f6b34a5bb0535383385b3da4014c4bfc5da58eed Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 23 Apr 2022 04:25:22 -0500 Subject: [PATCH 164/342] huh? --- src/engine/platform/genesisext.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/engine/platform/genesisext.cpp b/src/engine/platform/genesisext.cpp index bb33f4017..d8ae8b389 100644 --- a/src/engine/platform/genesisext.cpp +++ b/src/engine/platform/genesisext.cpp @@ -144,19 +144,18 @@ int DivPlatformGenesisExt::dispatch(DivCommand c) { } } // what the heck! + // TODO: rework! if (!opChan[ch].portaPause) { - opChan[ch].freqChanged=true; if ((newFreq&0x7ff)>1288) { newFreq=(644)|((newFreq+0x800)&0xf800); opChan[ch].portaPause=true; - opChan[ch].freqChanged=false; return2=false; - } - if ((newFreq&0x7ff)<644) { + } else if ((newFreq&0x7ff)<644) { newFreq=(1287)|((newFreq-0x800)&0xf800); opChan[ch].portaPause=true; - opChan[ch].freqChanged=false; return2=false; + } else { + opChan[ch].freqChanged=true; } } else { opChan[ch].portaPause=false; From f716ac262d87a96676c07c20845d2e603b13be0e Mon Sep 17 00:00:00 2001 From: James Alan Nguyen Date: Sat, 23 Apr 2022 19:52:09 +1000 Subject: [PATCH 165/342] #79: OPM import progress and start everything else --- src/engine/engine.h | 7 ++ src/engine/fileOpsIns.cpp | 150 ++++++++++++++++++++++++++++++++++++-- src/engine/safeReader.cpp | 48 +++++++++++- src/engine/safeReader.h | 3 + 4 files changed, 198 insertions(+), 10 deletions(-) diff --git a/src/engine/engine.h b/src/engine/engine.h index 515b9c590..c8afb86a9 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -31,6 +31,7 @@ #include #include #include +#include #define addWarning(x) \ if (warnings.empty()) { \ @@ -288,9 +289,15 @@ class DivEngine { void loadVGI(SafeReader& reader, std::vector& ret, String& stripPath); void loadS3I(SafeReader& reader, std::vector& ret, String& stripPath); void loadSBI(SafeReader& reader, std::vector& ret, String& stripPath); + void loadOPLI(SafeReader& reader, std::vector& ret, String& stripPath); + void loadOPNI(SafeReader& reader, std::vector& ret, String& stripPath); + void loadPAT(SafeReader& reader, std::vector& ret, String& stripPath); + void loadY12(SafeReader& reader, std::vector& ret, String& stripPath); void loadBNK(SafeReader& reader, std::vector& ret, String& stripPath); void loadOPM(SafeReader& reader, std::vector& ret, String& stripPath); void loadFF(SafeReader& reader, std::vector& ret, String& stripPath); + void loadWOPL(SafeReader& reader, std::vector& ret, String& stripPath); + void loadWOPN(SafeReader& reader, std::vector& ret, String& stripPath); bool initAudioBackend(); bool deinitAudioBackend(); diff --git a/src/engine/fileOpsIns.cpp b/src/engine/fileOpsIns.cpp index f9eaab38b..148c9d2e9 100644 --- a/src/engine/fileOpsIns.cpp +++ b/src/engine/fileOpsIns.cpp @@ -30,9 +30,15 @@ enum DivInsFormats { DIV_INSFORMAT_BTI, DIV_INSFORMAT_S3I, DIV_INSFORMAT_SBI, + DIV_INSFORMAT_PAT, + DIV_INSFORMAT_Y12, + DIV_INSFORMAT_OPLI, + DIV_INSFORMAT_OPNI, DIV_INSFORMAT_BNK, DIV_INSFORMAT_OPM, DIV_INSFORMAT_FF, + DIV_INSFORMAT_WOPL, + DIV_INSFORMAT_WOPN, }; // Patch data structures @@ -670,6 +676,7 @@ void DivEngine::loadBNK(SafeReader& reader, std::vector& ret, St auto& ins = insList[i]; ins->type = DIV_INS_OPL; + ins->fm.ops = 2; timbre.mode = reader.readC(); timbre.percVoice = reader.readC(); @@ -810,16 +817,132 @@ void DivEngine::loadFF(SafeReader& reader, std::vector& ret, Str } void DivEngine::loadOPM(SafeReader& reader, std::vector& ret, String& stripPath) { - DivInstrument* ins[128]; - memset(ins,0,128*sizeof(void*)); + std::vector insList; + std::stringstream ss; + + int readCount = 0; + + bool patchNameRead = false, + lfoRead = false, + characteristicRead = false, + m1Read = false, + c1Read = false, + m2Read = false, + c2Read = false; + + auto completePatchRead = [&]() { + return patchNameRead && lfoRead && characteristicRead && m1Read && c1Read && m2Read && c2Read; + }; + auto resetPatchRead = [&]() { + patchNameRead = lfoRead = characteristicRead = m1Read = c1Read = m2Read = c2Read = false; + }; + auto readOpmOperator = [](SafeReader& reader, DivInstrumentFM::Operator& op) { + op.ar = atoi(reader.readString_Token().c_str()); + op.dr = atoi(reader.readString_Token().c_str()); + op.d2r = atoi(reader.readString_Token().c_str()); + op.rr = atoi(reader.readString_Token().c_str()); + op.sl = atoi(reader.readString_Token().c_str()); + op.tl = atoi(reader.readString_Token().c_str()); + op.ksl = atoi(reader.readString_Token().c_str()); + op.mult = atoi(reader.readString_Token().c_str()); + op.dt = atoi(reader.readString_Token().c_str()); + op.dt2 = atoi(reader.readString_Token().c_str()); + op.ssgEnv = atoi(reader.readString_Token().c_str()); + }; + + DivInstrument* newPatch = nullptr; try { - String line; - + reader.seek(0, SEEK_SET); + while (!reader.isEOF()) { + String token = reader.readString_Token(); + if (token.length() == 0) { + continue; + } + + if (token.compare(0,2,"//") == 0) { + if (!reader.isEOF()) { + reader.readString_Line(); + } + continue; + } + + // At this point we know any other line would be associated with patch params + if (newPatch == nullptr) { + newPatch = new DivInstrument; + newPatch->type = DIV_INS_FM; + newPatch->fm.ops = 4; + } + + // Read each line for their respective params. They may not be written in the same order but they + // must absolutely be properly grouped per patch! Line prefixes must be separated by a space! + + // Patch number + name + // "@:123 Name of patch" + if (token.length() >= 2) { + if (token[0] == '@') { + newPatch->name = reader.readString_Line(); + patchNameRead = true; + } else if (token.compare(0,3,"CH:") == 0) { + // CH: PAN FL CON AMS PMS SLOT NE + reader.readString_Token(); // skip PAN + newPatch->fm.fb = atoi(reader.readString_Token().c_str()); + newPatch->fm.alg = atoi(reader.readString_Token().c_str()); + newPatch->fm.ams = atoi(reader.readString_Token().c_str()); + newPatch->fm.ams2 = atoi(reader.readString_Token().c_str()); + reader.readString_Token(); // skip SLOT + reader.readString_Token(); // skip NE + characteristicRead = true; + } else if (token.compare(0,3,"C1:") == 0) { + // C1: AR D1R D2R RR D1L TL KS MUL DT1 DT2 AMS-EN + readOpmOperator(reader, newPatch->fm.op[2]); + c1Read = true; + } else if (token.compare(0,3,"C2:") == 0) { + // C2: AR D1R D2R RR D1L TL KS MUL DT1 DT2 AMS-EN + readOpmOperator(reader, newPatch->fm.op[3]); + c2Read = true; + } else if (token.compare(0,3,"M1:") == 0) { + // M1: AR D1R D2R RR D1L TL KS MUL DT1 DT2 AMS-EN + readOpmOperator(reader, newPatch->fm.op[0]); + m1Read = true; + } else if (token.compare(0,3,"M2:") == 0) { + // M2: AR D1R D2R RR D1L TL KS MUL DT1 DT2 AMS-EN + readOpmOperator(reader, newPatch->fm.op[1]); + m2Read = true; + } else if (token.compare(0,4,"LFO:") == 0) { + // LFO: LFRQ AMD PMD WF NFRQ + // Furnace patches do not store this as these are chip-global. + reader.readString_Line(); + lfoRead = true; + } else { + // other unsupported lines ignored. + reader.readString_Line(); + } + } + + if (completePatchRead()) { + insList.push_back(newPatch); + newPatch = nullptr; + ++readCount; + } + } + + if (newPatch != nullptr) { + addWarning("Last OPM patch read was incomplete and therefore not imported."); + logW("Last OPM patch read was incomplete and therefore not imported."); + delete newPatch; + } + + for (int i = 0; i < readCount; ++i) { + ret.push_back(insList[i]); + } } catch (EndOfFileException& e) { - lastError="premature end of file"; + lastError = "premature end of file"; logE("premature end of file"); - return; + for (int i = readCount; i >= 0; --i) { + delete insList[i]; + } + delete newPatch; } } @@ -947,12 +1070,24 @@ std::vector DivEngine::instrumentFromFile(const char* path) { format=DIV_INSFORMAT_S3I; } else if (extS==String(".sbi")) { format=DIV_INSFORMAT_SBI; + } else if (extS==String(".opli")) { + format=DIV_INSFORMAT_OPLI; + } else if (extS==String(".opni")) { + format=DIV_INSFORMAT_OPNI; + } else if (extS==String(".pat")) { + format=DIV_INSFORMAT_PAT; + } else if (extS==String(".y12")) { + format=DIV_INSFORMAT_Y12; } else if (extS==String(".bnk")) { format=DIV_INSFORMAT_BNK; } else if (extS==String(".opm")) { format=DIV_INSFORMAT_OPM; } else if (extS==String(".ff")) { format=DIV_INSFORMAT_FF; + } else if (extS==String(".wopl")) { + format=DIV_INSFORMAT_WOPL; + } else if (extS==String(".wopn")) { + format=DIV_INSFORMAT_WOPN; } } @@ -971,7 +1106,8 @@ std::vector DivEngine::instrumentFromFile(const char* path) { break; case DIV_INSFORMAT_BTI: // TODO break; - case DIV_INSFORMAT_OPM: // TODO + case DIV_INSFORMAT_OPM: + loadOPM(reader,ret,stripPath); break; case DIV_INSFORMAT_S3I: loadS3I(reader,ret,stripPath); diff --git a/src/engine/safeReader.cpp b/src/engine/safeReader.cpp index 7fa66e8b3..e46827c68 100644 --- a/src/engine/safeReader.cpp +++ b/src/engine/safeReader.cpp @@ -139,12 +139,14 @@ double SafeReader::readD() { } String SafeReader::readString(size_t stlen) { - String ret; + String ret(stlen, ' '); #ifdef READ_DEBUG logD("SR: reading string len %d at %x",stlen,curSeek); #endif size_t curPos=0; - while (curPos= len; +} \ No newline at end of file diff --git a/src/engine/safeReader.h b/src/engine/safeReader.h index f674f5922..9fd0282b3 100644 --- a/src/engine/safeReader.h +++ b/src/engine/safeReader.h @@ -66,6 +66,9 @@ class SafeReader { double readD_BE(); String readString(); String readString(size_t len); + String readString_Line(); + String readString_Token(unsigned char delim=' '); + bool isEOF(); SafeReader(void* b, size_t l): buf((unsigned char*)b), From 3550ad512a83bdd1c420056838905098fdd3a2fa Mon Sep 17 00:00:00 2001 From: James Alan Nguyen Date: Sat, 23 Apr 2022 20:20:29 +1000 Subject: [PATCH 166/342] OPM import: Fix reset read procedural flow --- src/engine/fileOpsIns.cpp | 7 ++++--- src/gui/gui.cpp | 4 ++-- src/gui/settings.cpp | 1 + 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/engine/fileOpsIns.cpp b/src/engine/fileOpsIns.cpp index 148c9d2e9..b74c7cba3 100644 --- a/src/engine/fileOpsIns.cpp +++ b/src/engine/fileOpsIns.cpp @@ -829,12 +829,15 @@ void DivEngine::loadOPM(SafeReader& reader, std::vector& ret, St c1Read = false, m2Read = false, c2Read = false; + + DivInstrument* newPatch = nullptr; auto completePatchRead = [&]() { return patchNameRead && lfoRead && characteristicRead && m1Read && c1Read && m2Read && c2Read; }; auto resetPatchRead = [&]() { patchNameRead = lfoRead = characteristicRead = m1Read = c1Read = m2Read = c2Read = false; + newPatch = nullptr; }; auto readOpmOperator = [](SafeReader& reader, DivInstrumentFM::Operator& op) { op.ar = atoi(reader.readString_Token().c_str()); @@ -850,8 +853,6 @@ void DivEngine::loadOPM(SafeReader& reader, std::vector& ret, St op.ssgEnv = atoi(reader.readString_Token().c_str()); }; - DivInstrument* newPatch = nullptr; - try { reader.seek(0, SEEK_SET); while (!reader.isEOF()) { @@ -922,7 +923,7 @@ void DivEngine::loadOPM(SafeReader& reader, std::vector& ret, St if (completePatchRead()) { insList.push_back(newPatch); - newPatch = nullptr; + resetPatchRead(); ++readCount; } } diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 009952a63..d3218b30c 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -1290,9 +1290,9 @@ void FurnaceGUI::openFileDialog(FurnaceGUIFileDialogs type) { if (!dirExists(workingDirIns)) workingDirIns=getHomeDir(); hasOpened=fileDialog->openLoad( "Load Instrument", - {"compatible files", "*.fui *.dmp *.tfi *.vgi *.s3i *.sbi *.bnk *.ff", + {"compatible files", "*.fui *.dmp *.tfi *.vgi *.s3i *.sbi *.bnk *.ff *.opm", "all files", ".*"}, - "compatible files{.fui,.dmp,.tfi,.vgi,.s3i,.sbi,.bnk,.ff},.*", + "compatible files{.fui,.dmp,.tfi,.vgi,.s3i,.sbi,.bnk,.ff,.opm},.*", workingDirIns, dpiScale ); diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index f1cd27a36..f1565b8ee 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -2489,6 +2489,7 @@ void FurnaceGUI::applyUISettings(bool updateFonts) { ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".fti",uiColors[GUI_COLOR_FILE_INSTR],ICON_FA_FILE); ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".bti",uiColors[GUI_COLOR_FILE_INSTR],ICON_FA_FILE); ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".ff",uiColors[GUI_COLOR_FILE_INSTR],ICON_FA_FILE); + ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".opm",uiColors[GUI_COLOR_FILE_INSTR],ICON_FA_FILE); if (updateFonts) { if (fileDialog!=NULL) delete fileDialog; From 8ad827478c20cd7858cb77f797231b802babf665 Mon Sep 17 00:00:00 2001 From: James Alan Nguyen Date: Sat, 23 Apr 2022 21:48:18 +1000 Subject: [PATCH 167/342] #79: OPM complete for now --- src/engine/fileOpsIns.cpp | 47 ++++++++++++++++++++++----------------- 1 file changed, 27 insertions(+), 20 deletions(-) diff --git a/src/engine/fileOpsIns.cpp b/src/engine/fileOpsIns.cpp index b74c7cba3..a8b2b652c 100644 --- a/src/engine/fileOpsIns.cpp +++ b/src/engine/fileOpsIns.cpp @@ -839,18 +839,23 @@ void DivEngine::loadOPM(SafeReader& reader, std::vector& ret, St patchNameRead = lfoRead = characteristicRead = m1Read = c1Read = m2Read = c2Read = false; newPatch = nullptr; }; - auto readOpmOperator = [](SafeReader& reader, DivInstrumentFM::Operator& op) { - op.ar = atoi(reader.readString_Token().c_str()); - op.dr = atoi(reader.readString_Token().c_str()); - op.d2r = atoi(reader.readString_Token().c_str()); - op.rr = atoi(reader.readString_Token().c_str()); - op.sl = atoi(reader.readString_Token().c_str()); - op.tl = atoi(reader.readString_Token().c_str()); - op.ksl = atoi(reader.readString_Token().c_str()); - op.mult = atoi(reader.readString_Token().c_str()); - op.dt = atoi(reader.readString_Token().c_str()); - op.dt2 = atoi(reader.readString_Token().c_str()); - op.ssgEnv = atoi(reader.readString_Token().c_str()); + auto readIntStrWithinRange = [](String& input, int limitLow, int limitHigh) { + int x = atoi(input.c_str()); + return (x>limitHigh) ? limitHigh : + (x& ret, St newPatch->fm.ops = 4; } - // Read each line for their respective params. They may not be written in the same order but they + // Read each line for their respective params. They may not be written in the same LINE order but they // must absolutely be properly grouped per patch! Line prefixes must be separated by a space! - // Patch number + name - // "@:123 Name of patch" if (token.length() >= 2) { if (token[0] == '@') { + // @:123 Name of patch + // Note: Fallback to bank filename and current patch number in _file_ order (not @n order) newPatch->name = reader.readString_Line(); + newPatch->name = newPatch->name.length() > 0 ? newPatch->name : fmt::format("{0}[{1}]", stripPath, readCount); + patchNameRead = true; } else if (token.compare(0,3,"CH:") == 0) { // CH: PAN FL CON AMS PMS SLOT NE reader.readString_Token(); // skip PAN - newPatch->fm.fb = atoi(reader.readString_Token().c_str()); - newPatch->fm.alg = atoi(reader.readString_Token().c_str()); - newPatch->fm.ams = atoi(reader.readString_Token().c_str()); - newPatch->fm.ams2 = atoi(reader.readString_Token().c_str()); + newPatch->fm.fb = readIntStrWithinRange(reader.readString_Token(), 0, 7); + newPatch->fm.alg = readIntStrWithinRange(reader.readString_Token(), 0, 7); + newPatch->fm.ams = readIntStrWithinRange(reader.readString_Token(), 0, 4); + newPatch->fm.fms = readIntStrWithinRange(reader.readString_Token(), 0, 7); reader.readString_Token(); // skip SLOT reader.readString_Token(); // skip NE characteristicRead = true; @@ -912,7 +919,7 @@ void DivEngine::loadOPM(SafeReader& reader, std::vector& ret, St m2Read = true; } else if (token.compare(0,4,"LFO:") == 0) { // LFO: LFRQ AMD PMD WF NFRQ - // Furnace patches do not store this as these are chip-global. + // Furnace patches do not store these as these are chip-global. reader.readString_Line(); lfoRead = true; } else { From 0f47a3ed7b049208183a6f84067b4a53898e3f47 Mon Sep 17 00:00:00 2001 From: James Alan Nguyen Date: Sat, 23 Apr 2022 23:40:58 +1000 Subject: [PATCH 168/342] Fix DT range --- src/engine/fileOpsIns.cpp | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/engine/fileOpsIns.cpp b/src/engine/fileOpsIns.cpp index a8b2b652c..8b43f65ef 100644 --- a/src/engine/fileOpsIns.cpp +++ b/src/engine/fileOpsIns.cpp @@ -640,6 +640,7 @@ void DivEngine::loadSBI(SafeReader& reader, std::vector& ret, St delete ins; } } + void DivEngine::loadBNK(SafeReader& reader, std::vector& ret, String& stripPath) { std::vector insList; std::vector instNames; @@ -853,7 +854,8 @@ void DivEngine::loadOPM(SafeReader& reader, std::vector& ret, St op.tl = readIntStrWithinRange(reader.readString_Token(), 0, 127); op.rs = readIntStrWithinRange(reader.readString_Token(), 0, 3);; op.mult = readIntStrWithinRange(reader.readString_Token(), 0, 15); - op.dt = readIntStrWithinRange(reader.readString_Token(), -3, 4); + op.dt = readIntStrWithinRange(reader.readString_Token(), 0, 7); + op.dt = (op.dt >= 4) ? (7 - op.dt) : (op.dt + 3); op.dt2 = readIntStrWithinRange(reader.readString_Token(), 0, 3); op.am = readIntStrWithinRange(reader.readString_Token(), 0, 1); }; @@ -881,7 +883,7 @@ void DivEngine::loadOPM(SafeReader& reader, std::vector& ret, St } // Read each line for their respective params. They may not be written in the same LINE order but they - // must absolutely be properly grouped per patch! Line prefixes must be separated by a space! + // must absolutely be properly grouped per patch! Line prefixes must be separated by a space! (see inline comments) if (token.length() >= 2) { if (token[0] == '@') { @@ -889,8 +891,8 @@ void DivEngine::loadOPM(SafeReader& reader, std::vector& ret, St // Note: Fallback to bank filename and current patch number in _file_ order (not @n order) newPatch->name = reader.readString_Line(); newPatch->name = newPatch->name.length() > 0 ? newPatch->name : fmt::format("{0}[{1}]", stripPath, readCount); - patchNameRead = true; + } else if (token.compare(0,3,"CH:") == 0) { // CH: PAN FL CON AMS PMS SLOT NE reader.readString_Token(); // skip PAN @@ -901,27 +903,33 @@ void DivEngine::loadOPM(SafeReader& reader, std::vector& ret, St reader.readString_Token(); // skip SLOT reader.readString_Token(); // skip NE characteristicRead = true; + } else if (token.compare(0,3,"C1:") == 0) { // C1: AR D1R D2R RR D1L TL KS MUL DT1 DT2 AMS-EN readOpmOperator(reader, newPatch->fm.op[2]); c1Read = true; + } else if (token.compare(0,3,"C2:") == 0) { // C2: AR D1R D2R RR D1L TL KS MUL DT1 DT2 AMS-EN readOpmOperator(reader, newPatch->fm.op[3]); c2Read = true; + } else if (token.compare(0,3,"M1:") == 0) { // M1: AR D1R D2R RR D1L TL KS MUL DT1 DT2 AMS-EN readOpmOperator(reader, newPatch->fm.op[0]); m1Read = true; + } else if (token.compare(0,3,"M2:") == 0) { // M2: AR D1R D2R RR D1L TL KS MUL DT1 DT2 AMS-EN readOpmOperator(reader, newPatch->fm.op[1]); m2Read = true; + } else if (token.compare(0,4,"LFO:") == 0) { // LFO: LFRQ AMD PMD WF NFRQ - // Furnace patches do not store these as these are chip-global. + // Furnace patches do not store these as they are chip-global. reader.readString_Line(); lfoRead = true; + } else { // other unsupported lines ignored. reader.readString_Line(); @@ -950,7 +958,9 @@ void DivEngine::loadOPM(SafeReader& reader, std::vector& ret, St for (int i = readCount; i >= 0; --i) { delete insList[i]; } - delete newPatch; + if (newPatch != nullptr) { + delete newPatch; + } } } From b8d9fab7455963f675f10ce223ad665a07841066 Mon Sep 17 00:00:00 2001 From: James Alan Nguyen Date: Sun, 24 Apr 2022 00:29:33 +1000 Subject: [PATCH 169/342] Start .y12 (Gens KMod/Kaneda) patch dump import --- src/engine/fileOpsIns.cpp | 48 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 45 insertions(+), 3 deletions(-) diff --git a/src/engine/fileOpsIns.cpp b/src/engine/fileOpsIns.cpp index 8b43f65ef..ddf9a1df7 100644 --- a/src/engine/fileOpsIns.cpp +++ b/src/engine/fileOpsIns.cpp @@ -641,6 +641,45 @@ void DivEngine::loadSBI(SafeReader& reader, std::vector& ret, St } } +void DivEngine::loadY12(SafeReader& reader, std::vector& ret, String& stripPath) { + DivInstrument *ins = new DivInstrument; + + try { + reader.seek(0, SEEK_SET); + ins->type = DIV_INS_FM; + ins->fm.ops = 4; + ins->name = stripPath; + + for (int i = 0; i < 4; ++i) { + DivInstrumentFM::Operator& insOp = ins->fm.op[i]; + uint8_t tmp = reader.readC(); + insOp.mult = (tmp & 0xF); + insOp.dt = ((tmp >> 4) & 0x7); + insOp.tl = (reader.readC() & 0xF); + tmp = reader.readC(); + insOp.rs = ((tmp >> 6) & 0x3); + insOp.ar = (tmp & 0x1F); + tmp = reader.readC(); + insOp.dr = (tmp & 0x1F); + insOp.am = ((tmp >> 7) & 0x1); + insOp.d2r = (reader.readC() & 0x1F); + tmp = reader.readC(); + insOp.rr = (tmp & 0xF); + insOp.sl = ((tmp >> 4) & 0xF); + insOp.ssgEnv = reader.readC(); + reader.seek(9, SEEK_CUR); + } + ins->fm.alg = reader.readC(); + ins->fm.fb = reader.readC(); + reader.seek(14, SEEK_CUR); + ret.push_back(ins); + } catch (EndOfFileException& e) { + lastError = "premature end of file"; + logE("premature end of file"); + delete ins; + } +} + void DivEngine::loadBNK(SafeReader& reader, std::vector& ret, String& stripPath) { std::vector insList; std::vector instNames; @@ -1124,21 +1163,24 @@ std::vector DivEngine::instrumentFromFile(const char* path) { break; case DIV_INSFORMAT_BTI: // TODO break; - case DIV_INSFORMAT_OPM: - loadOPM(reader,ret,stripPath); - break; case DIV_INSFORMAT_S3I: loadS3I(reader,ret,stripPath); break; case DIV_INSFORMAT_SBI: loadSBI(reader,ret,stripPath); break; + case DIV_INSFORMAT_Y12: + loadY12(reader,ret,stripPath); + break; case DIV_INSFORMAT_BNK: loadBNK(reader, ret, stripPath); break; case DIV_INSFORMAT_FF: loadFF(reader,ret,stripPath); break; + case DIV_INSFORMAT_OPM: + loadOPM(reader, ret, stripPath); + break; } if (reader.tell() Date: Sun, 24 Apr 2022 00:45:19 +1000 Subject: [PATCH 170/342] #79: .y12 import done --- src/engine/fileOpsIns.cpp | 6 +++--- src/gui/gui.cpp | 4 ++-- src/gui/settings.cpp | 1 + 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/engine/fileOpsIns.cpp b/src/engine/fileOpsIns.cpp index ddf9a1df7..602fbba55 100644 --- a/src/engine/fileOpsIns.cpp +++ b/src/engine/fileOpsIns.cpp @@ -650,12 +650,12 @@ void DivEngine::loadY12(SafeReader& reader, std::vector& ret, St ins->fm.ops = 4; ins->name = stripPath; - for (int i = 0; i < 4; ++i) { + for (int i : {0,1,2,3}) { DivInstrumentFM::Operator& insOp = ins->fm.op[i]; uint8_t tmp = reader.readC(); insOp.mult = (tmp & 0xF); insOp.dt = ((tmp >> 4) & 0x7); - insOp.tl = (reader.readC() & 0xF); + insOp.tl = (reader.readC() & 0x3F); tmp = reader.readC(); insOp.rs = ((tmp >> 6) & 0x3); insOp.ar = (tmp & 0x1F); @@ -671,7 +671,7 @@ void DivEngine::loadY12(SafeReader& reader, std::vector& ret, St } ins->fm.alg = reader.readC(); ins->fm.fb = reader.readC(); - reader.seek(14, SEEK_CUR); + reader.seek(62, SEEK_CUR); ret.push_back(ins); } catch (EndOfFileException& e) { lastError = "premature end of file"; diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index d3218b30c..47b81f6eb 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -1290,9 +1290,9 @@ void FurnaceGUI::openFileDialog(FurnaceGUIFileDialogs type) { if (!dirExists(workingDirIns)) workingDirIns=getHomeDir(); hasOpened=fileDialog->openLoad( "Load Instrument", - {"compatible files", "*.fui *.dmp *.tfi *.vgi *.s3i *.sbi *.bnk *.ff *.opm", + {"compatible files", "*.fui *.dmp *.tfi *.vgi *.s3i *.sbi *.y12 *.bnk *.ff *.opm", "all files", ".*"}, - "compatible files{.fui,.dmp,.tfi,.vgi,.s3i,.sbi,.bnk,.ff,.opm},.*", + "compatible files{.fui,.dmp,.tfi,.vgi,.s3i,.sbi,.y12,.bnk,.ff,.opm},.*", workingDirIns, dpiScale ); diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index f1565b8ee..5ed9cf0ad 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -2485,6 +2485,7 @@ void FurnaceGUI::applyUISettings(bool updateFonts) { ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".vgi",uiColors[GUI_COLOR_FILE_INSTR],ICON_FA_FILE); ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".s3i",uiColors[GUI_COLOR_FILE_INSTR],ICON_FA_FILE); ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".sbi",uiColors[GUI_COLOR_FILE_INSTR],ICON_FA_FILE); + ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".y12",uiColors[GUI_COLOR_FILE_INSTR],ICON_FA_FILE); ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".bnk",uiColors[GUI_COLOR_FILE_INSTR],ICON_FA_FILE); ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".fti",uiColors[GUI_COLOR_FILE_INSTR],ICON_FA_FILE); ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".bti",uiColors[GUI_COLOR_FILE_INSTR],ICON_FA_FILE); From 6bd199923f042bb1d9882fec6869eab7bf2112ae Mon Sep 17 00:00:00 2001 From: James Alan Nguyen Date: Sun, 24 Apr 2022 02:37:25 +1000 Subject: [PATCH 171/342] OPLI progress... still figuring out correct readings --- src/engine/engine.h | 3 - src/engine/fileOpsIns.cpp | 126 +++++++++++++++++++++++++++++++++----- src/engine/safeReader.cpp | 2 +- src/gui/settings.cpp | 2 + 4 files changed, 114 insertions(+), 19 deletions(-) diff --git a/src/engine/engine.h b/src/engine/engine.h index c8afb86a9..5e840362e 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -291,13 +291,10 @@ class DivEngine { void loadSBI(SafeReader& reader, std::vector& ret, String& stripPath); void loadOPLI(SafeReader& reader, std::vector& ret, String& stripPath); void loadOPNI(SafeReader& reader, std::vector& ret, String& stripPath); - void loadPAT(SafeReader& reader, std::vector& ret, String& stripPath); void loadY12(SafeReader& reader, std::vector& ret, String& stripPath); void loadBNK(SafeReader& reader, std::vector& ret, String& stripPath); void loadOPM(SafeReader& reader, std::vector& ret, String& stripPath); void loadFF(SafeReader& reader, std::vector& ret, String& stripPath); - void loadWOPL(SafeReader& reader, std::vector& ret, String& stripPath); - void loadWOPN(SafeReader& reader, std::vector& ret, String& stripPath); bool initAudioBackend(); bool deinitAudioBackend(); diff --git a/src/engine/fileOpsIns.cpp b/src/engine/fileOpsIns.cpp index 602fbba55..87233ef63 100644 --- a/src/engine/fileOpsIns.cpp +++ b/src/engine/fileOpsIns.cpp @@ -30,15 +30,12 @@ enum DivInsFormats { DIV_INSFORMAT_BTI, DIV_INSFORMAT_S3I, DIV_INSFORMAT_SBI, - DIV_INSFORMAT_PAT, DIV_INSFORMAT_Y12, DIV_INSFORMAT_OPLI, DIV_INSFORMAT_OPNI, DIV_INSFORMAT_BNK, DIV_INSFORMAT_OPM, DIV_INSFORMAT_FF, - DIV_INSFORMAT_WOPL, - DIV_INSFORMAT_WOPN, }; // Patch data structures @@ -641,6 +638,105 @@ void DivEngine::loadSBI(SafeReader& reader, std::vector& ret, St } } +void DivEngine::loadOPLI(SafeReader& reader, std::vector& ret, String& stripPath) { + DivInstrument* ins = new DivInstrument; + + try { + reader.seek(0, SEEK_SET); + String header = reader.readString(11); + if (header.compare("WOPL3-INST") == 0) { + uint16_t version = reader.readS(); + bool isPerc = (reader.readC() == 1); + + ins->type = DIV_INS_OPL; + String insName = reader.readString(32); + insName = (insName.length() > 0) ? insName : stripPath; + ins->name = insName; + reader.seek(7, SEEK_CUR); // skip MIDI params + uint8_t instTypeFlags = reader.readC(); // [0EEEDCBA] - see WOPL/OPLI spec + + bool is_2op = ((instTypeFlags & 0x1) > 0); + bool is_4op = (((instTypeFlags>>1) & 0x1) > 0); + bool is_2x2op = (((instTypeFlags>>2) & 0x1) > 0); + bool is_rhythm = (((instTypeFlags>>4) & 0x7) > 0); + + auto readOpliOp = [](SafeReader& reader, DivInstrumentFM::Operator op) { + uint8_t characteristics = reader.readC(); + uint8_t keyScaleLevel = reader.readC(); + uint8_t attackDecay = reader.readC(); + uint8_t sustainRelease = reader.readC(); + uint8_t waveSelect = reader.readC(); + + op.mult = characteristics & 0xF; + op.ksr = ((characteristics >> 4) & 0x1); + op.sus = ((characteristics >> 5) & 0x1); + op.vib = ((characteristics >> 6) & 0x1); + op.am = ((characteristics >> 7) & 0x1); + op.tl = keyScaleLevel & 0x3F; + op.ksl = ((keyScaleLevel >> 6) & 0x3); + op.ar = ((attackDecay >> 4) & 0xF); + op.dr = attackDecay & 0xF; + op.rr = sustainRelease & 0xF; + op.sl = ((sustainRelease >> 4) & 0xF); + op.ws = waveSelect; + }; + + uint8_t feedConnect = reader.readC(); + uint8_t feedConnect2nd = reader.readC(); + + ins->fm.alg = (feedConnect & 0x1); + ins->fm.fb = ((feedConnect >> 1) & 0xF); + + if (is_4op) { + ins->fm.ops = 4; + ins->fm.alg = (feedConnect & 0x1) | ((feedConnect2nd & 0x1) << 1); + for (int i : {0,2,1,3}) { + readOpliOp(reader, ins->fm.op[i]); + } + } else { + ins->fm.ops = 2; + for (int i = 0; i < 2; ++i) { + readOpliOp(reader, ins->fm.op[i]); + } + if (is_rhythm) { + ins->fm.opllPreset = (uint8_t)(1<<4); + + } else if (is_2x2op) { + ins->name = fmt::format("{0} (1)", insName); + ret.push_back(ins); + + ins = new DivInstrument; + ins->type = DIV_INS_OPL; + ins->name = fmt::format("{0} (2)", insName); + for (int i = 0; i < 2; ++i) { + readOpliOp(reader, ins->fm.op[i]); + } + } + } + + // Skip rest of file + reader.seek(0, SEEK_END); + ret.push_back(ins); + } + } catch (EndOfFileException& e) { + lastError = "premature end of file"; + logE("premature end of file"); + delete ins; + } +} + +void DivEngine::loadOPNI(SafeReader& reader, std::vector& ret, String& stripPath) { + DivInstrument* ins = new DivInstrument; + + try { + reader.seek(0, SEEK_SET); + } catch (EndOfFileException& e) { + lastError = "premature end of file"; + logE("premature end of file"); + delete ins; + } +} + void DivEngine::loadY12(SafeReader& reader, std::vector& ret, String& stripPath) { DivInstrument *ins = new DivInstrument; @@ -653,18 +749,18 @@ void DivEngine::loadY12(SafeReader& reader, std::vector& ret, St for (int i : {0,1,2,3}) { DivInstrumentFM::Operator& insOp = ins->fm.op[i]; uint8_t tmp = reader.readC(); - insOp.mult = (tmp & 0xF); + insOp.mult = tmp & 0xF; insOp.dt = ((tmp >> 4) & 0x7); insOp.tl = (reader.readC() & 0x3F); tmp = reader.readC(); insOp.rs = ((tmp >> 6) & 0x3); - insOp.ar = (tmp & 0x1F); + insOp.ar = tmp & 0x1F; tmp = reader.readC(); - insOp.dr = (tmp & 0x1F); + insOp.dr = tmp & 0x1F; insOp.am = ((tmp >> 7) & 0x1); insOp.d2r = (reader.readC() & 0x1F); tmp = reader.readC(); - insOp.rr = (tmp & 0xF); + insOp.rr = tmp & 0xF; insOp.sl = ((tmp >> 4) & 0xF); insOp.ssgEnv = reader.readC(); reader.seek(9, SEEK_CUR); @@ -884,7 +980,7 @@ void DivEngine::loadOPM(SafeReader& reader, std::vector& ret, St return (x>limitHigh) ? limitHigh : (x DivEngine::instrumentFromFile(const char* path) { } else if (extS==String(".opli")) { format=DIV_INSFORMAT_OPLI; } else if (extS==String(".opni")) { - format=DIV_INSFORMAT_OPNI; - } else if (extS==String(".pat")) { - format=DIV_INSFORMAT_PAT; + format=DIV_INSFORMAT_OPNI;; } else if (extS==String(".y12")) { format=DIV_INSFORMAT_Y12; } else if (extS==String(".bnk")) { @@ -1141,10 +1235,6 @@ std::vector DivEngine::instrumentFromFile(const char* path) { format=DIV_INSFORMAT_OPM; } else if (extS==String(".ff")) { format=DIV_INSFORMAT_FF; - } else if (extS==String(".wopl")) { - format=DIV_INSFORMAT_WOPL; - } else if (extS==String(".wopn")) { - format=DIV_INSFORMAT_WOPN; } } @@ -1169,6 +1259,12 @@ std::vector DivEngine::instrumentFromFile(const char* path) { case DIV_INSFORMAT_SBI: loadSBI(reader,ret,stripPath); break; + case DIV_INSFORMAT_OPLI: + loadOPLI(reader,ret,stripPath); + break; + case DIV_INSFORMAT_OPNI: + loadOPNI(reader, ret, stripPath); + break; case DIV_INSFORMAT_Y12: loadY12(reader,ret,stripPath); break; diff --git a/src/engine/safeReader.cpp b/src/engine/safeReader.cpp index e46827c68..97031b2be 100644 --- a/src/engine/safeReader.cpp +++ b/src/engine/safeReader.cpp @@ -139,7 +139,7 @@ double SafeReader::readD() { } String SafeReader::readString(size_t stlen) { - String ret(stlen, ' '); + String ret; #ifdef READ_DEBUG logD("SR: reading string len %d at %x",stlen,curSeek); #endif diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index 5ed9cf0ad..c7a235113 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -2485,6 +2485,8 @@ void FurnaceGUI::applyUISettings(bool updateFonts) { ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".vgi",uiColors[GUI_COLOR_FILE_INSTR],ICON_FA_FILE); ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".s3i",uiColors[GUI_COLOR_FILE_INSTR],ICON_FA_FILE); ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".sbi",uiColors[GUI_COLOR_FILE_INSTR],ICON_FA_FILE); + ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".opli",uiColors[GUI_COLOR_FILE_INSTR],ICON_FA_FILE); + ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".opni",uiColors[GUI_COLOR_FILE_INSTR],ICON_FA_FILE); ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".y12",uiColors[GUI_COLOR_FILE_INSTR],ICON_FA_FILE); ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".bnk",uiColors[GUI_COLOR_FILE_INSTR],ICON_FA_FILE); ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".fti",uiColors[GUI_COLOR_FILE_INSTR],ICON_FA_FILE); From bb0253d82fc3a8f90568adead3cd93625e747d95 Mon Sep 17 00:00:00 2001 From: James Alan Nguyen Date: Sun, 24 Apr 2022 03:15:20 +1000 Subject: [PATCH 172/342] Urgh why are your operators BACKWARDS FFS --- src/engine/fileOpsIns.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/engine/fileOpsIns.cpp b/src/engine/fileOpsIns.cpp index 87233ef63..d864bda5a 100644 --- a/src/engine/fileOpsIns.cpp +++ b/src/engine/fileOpsIns.cpp @@ -655,12 +655,12 @@ void DivEngine::loadOPLI(SafeReader& reader, std::vector& ret, S reader.seek(7, SEEK_CUR); // skip MIDI params uint8_t instTypeFlags = reader.readC(); // [0EEEDCBA] - see WOPL/OPLI spec - bool is_2op = ((instTypeFlags & 0x1) > 0); - bool is_4op = (((instTypeFlags>>1) & 0x1) > 0); - bool is_2x2op = (((instTypeFlags>>2) & 0x1) > 0); + bool is_2op = ((instTypeFlags & 0x3) == 0); + bool is_4op = ((instTypeFlags & 0x1) == 1); + bool is_2x2op = (((instTypeFlags>>1) & 0x1) == 1); bool is_rhythm = (((instTypeFlags>>4) & 0x7) > 0); - auto readOpliOp = [](SafeReader& reader, DivInstrumentFM::Operator op) { + auto readOpliOp = [](SafeReader& reader, DivInstrumentFM::Operator& op) { uint8_t characteristics = reader.readC(); uint8_t keyScaleLevel = reader.readC(); uint8_t attackDecay = reader.readC(); @@ -687,15 +687,15 @@ void DivEngine::loadOPLI(SafeReader& reader, std::vector& ret, S ins->fm.alg = (feedConnect & 0x1); ins->fm.fb = ((feedConnect >> 1) & 0xF); - if (is_4op) { + if (is_4op && !is_2x2op) { ins->fm.ops = 4; ins->fm.alg = (feedConnect & 0x1) | ((feedConnect2nd & 0x1) << 1); - for (int i : {0,2,1,3}) { + for (int i : {2,0,3,1}) { readOpliOp(reader, ins->fm.op[i]); } } else { ins->fm.ops = 2; - for (int i = 0; i < 2; ++i) { + for (int i : {1,0}) { readOpliOp(reader, ins->fm.op[i]); } if (is_rhythm) { @@ -708,7 +708,7 @@ void DivEngine::loadOPLI(SafeReader& reader, std::vector& ret, S ins = new DivInstrument; ins->type = DIV_INS_OPL; ins->name = fmt::format("{0} (2)", insName); - for (int i = 0; i < 2; ++i) { + for (int i : {1,0}) { readOpliOp(reader, ins->fm.op[i]); } } From 3865e3eac60fe6d5acc03afda5af6a544ca24b52 Mon Sep 17 00:00:00 2001 From: James Alan Nguyen Date: Sun, 24 Apr 2022 03:49:01 +1000 Subject: [PATCH 173/342] #79: OPNI support added...... --- src/engine/fileOpsIns.cpp | 52 +++++++++++++++++++++++++++++++++++++++ src/gui/gui.cpp | 4 +-- 2 files changed, 54 insertions(+), 2 deletions(-) diff --git a/src/engine/fileOpsIns.cpp b/src/engine/fileOpsIns.cpp index d864bda5a..d541a9df2 100644 --- a/src/engine/fileOpsIns.cpp +++ b/src/engine/fileOpsIns.cpp @@ -702,6 +702,7 @@ void DivEngine::loadOPLI(SafeReader& reader, std::vector& ret, S ins->fm.opllPreset = (uint8_t)(1<<4); } else if (is_2x2op) { + // Note: Pair detuning offset not mappable. Use E5xx effect :P ins->name = fmt::format("{0} (1)", insName); ret.push_back(ins); @@ -730,6 +731,57 @@ void DivEngine::loadOPNI(SafeReader& reader, std::vector& ret, S try { reader.seek(0, SEEK_SET); + + String header = reader.readString(11); + if (header.compare("WOPN2-INST") == 0 || header.compare("WOPN2-IN2T") == 0) { // omfg + uint16_t version = reader.readS(); + if (!(version >= 2) || version > 0xF) { + // version 1 doesn't have a version field........ + reader.seek(-2, SEEK_CUR); + } + + bool is_perc = (reader.readC() == 0x1); + ins->type = DIV_INS_FM; + ins->fm.ops = 4; + + String insName = reader.readString(32); + ins->name = (insName.length() > 0) ? insName : stripPath; + reader.seek(3, SEEK_CUR); // skip MIDI params + uint8_t feedAlgo = reader.readC(); + ins->fm.alg = (feedAlgo & 0x7); + ins->fm.fb = ((feedAlgo>>3) & 0x7); + reader.readC(); // Skip global bank flags - see WOPN/OPNI spec + + auto readOpniOp = [](SafeReader& reader, DivInstrumentFM::Operator& op) { + uint8_t dtMul = reader.readC(); + uint8_t totalLevel = reader.readC(); + uint8_t arRateScale = reader.readC(); + uint8_t drAmpEnable = reader.readC(); + uint8_t d2r = reader.readC(); + uint8_t susRelease = reader.readC(); + uint8_t ssgEg = reader.readC(); + + op.mult = dtMul & 0xF; + op.dt = ((dtMul >> 4) & 0x7); + op.tl = totalLevel & 0x3F; + op.rs = ((arRateScale >> 6) & 0x3); + op.ar = arRateScale & 0x1F; + op.dr = drAmpEnable & 0x1F; + op.am = ((drAmpEnable >> 7) & 0x1); + op.d2r = d2r & 0x1F; + op.rr = susRelease & 0xF; + op.sl = ((susRelease >> 4) & 0xF); + op.ssgEnv = ssgEg; + }; + + for (int i : {0,1,2,3}) { + readOpniOp(reader, ins->fm.op[i]); + } + + // Skip rest of file + reader.seek(0, SEEK_END); + ret.push_back(ins); + } } catch (EndOfFileException& e) { lastError = "premature end of file"; logE("premature end of file"); diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 47b81f6eb..4d6f10896 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -1290,9 +1290,9 @@ void FurnaceGUI::openFileDialog(FurnaceGUIFileDialogs type) { if (!dirExists(workingDirIns)) workingDirIns=getHomeDir(); hasOpened=fileDialog->openLoad( "Load Instrument", - {"compatible files", "*.fui *.dmp *.tfi *.vgi *.s3i *.sbi *.y12 *.bnk *.ff *.opm", + {"compatible files", "*.fui *.dmp *.tfi *.vgi *.s3i *.sbi *.opli *.opni *.y12 *.bnk *.ff *.opm", "all files", ".*"}, - "compatible files{.fui,.dmp,.tfi,.vgi,.s3i,.sbi,.y12,.bnk,.ff,.opm},.*", + "compatible files{.fui,.dmp,.tfi,.vgi,.s3i,.sbi,.opli,.opni,.y12,.bnk,.ff,.opm},.*", workingDirIns, dpiScale ); From 6638941c9dfd0c3c98c15b971dfa2377cbfc8ce8 Mon Sep 17 00:00:00 2001 From: James Alan Nguyen Date: Sun, 24 Apr 2022 04:34:32 +1000 Subject: [PATCH 174/342] Address unused stuff and CI fix --- src/engine/engine.h | 1 - src/engine/fileOpsIns.cpp | 12 +++++++----- src/engine/safeReader.cpp | 5 +++++ src/engine/safeReader.h | 3 ++- 4 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/engine/engine.h b/src/engine/engine.h index 5e840362e..b76e271e2 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -31,7 +31,6 @@ #include #include #include -#include #define addWarning(x) \ if (warnings.empty()) { \ diff --git a/src/engine/fileOpsIns.cpp b/src/engine/fileOpsIns.cpp index d541a9df2..d913d96d4 100644 --- a/src/engine/fileOpsIns.cpp +++ b/src/engine/fileOpsIns.cpp @@ -646,7 +646,11 @@ void DivEngine::loadOPLI(SafeReader& reader, std::vector& ret, S String header = reader.readString(11); if (header.compare("WOPL3-INST") == 0) { uint16_t version = reader.readS(); - bool isPerc = (reader.readC() == 1); + if (version > 3) { + logW("Unknown OPLI version."); + } + + reader.readC(); // skip isPerc field ins->type = DIV_INS_OPL; String insName = reader.readString(32); @@ -655,7 +659,6 @@ void DivEngine::loadOPLI(SafeReader& reader, std::vector& ret, S reader.seek(7, SEEK_CUR); // skip MIDI params uint8_t instTypeFlags = reader.readC(); // [0EEEDCBA] - see WOPL/OPLI spec - bool is_2op = ((instTypeFlags & 0x3) == 0); bool is_4op = ((instTypeFlags & 0x1) == 1); bool is_2x2op = (((instTypeFlags>>1) & 0x1) == 1); bool is_rhythm = (((instTypeFlags>>4) & 0x7) > 0); @@ -740,7 +743,7 @@ void DivEngine::loadOPNI(SafeReader& reader, std::vector& ret, S reader.seek(-2, SEEK_CUR); } - bool is_perc = (reader.readC() == 0x1); + reader.readC(); // skip isPerc ins->type = DIV_INS_FM; ins->fm.ops = 4; @@ -1006,7 +1009,6 @@ void DivEngine::loadFF(SafeReader& reader, std::vector& ret, Str void DivEngine::loadOPM(SafeReader& reader, std::vector& ret, String& stripPath) { std::vector insList; - std::stringstream ss; int readCount = 0; @@ -1027,7 +1029,7 @@ void DivEngine::loadOPM(SafeReader& reader, std::vector& ret, St patchNameRead = lfoRead = characteristicRead = m1Read = c1Read = m2Read = c2Read = false; newPatch = nullptr; }; - auto readIntStrWithinRange = [](String& input, int limitLow, int limitHigh) { + auto readIntStrWithinRange = [](String&& input, int limitLow, int limitHigh) { int x = atoi(input.c_str()); return (x>limitHigh) ? limitHigh : (x= len; diff --git a/src/engine/safeReader.h b/src/engine/safeReader.h index 9fd0282b3..4216a5969 100644 --- a/src/engine/safeReader.h +++ b/src/engine/safeReader.h @@ -67,7 +67,8 @@ class SafeReader { String readString(); String readString(size_t len); String readString_Line(); - String readString_Token(unsigned char delim=' '); + String readString_Token(unsigned char delim); + String readString_Token(); bool isEOF(); SafeReader(void* b, size_t l): From 6493a0481c80bb3cd91e6121ba2a78375a1a3e3d Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 23 Apr 2022 13:39:00 -0500 Subject: [PATCH 175/342] i hate this --- src/gui/gui.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 009952a63..19f2e77e3 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -17,7 +17,6 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#include #define _USE_MATH_DEFINES #include "gui.h" #include "util.h" From 0e3dbdc1b28cfff246ed438315b17dc3716bcb9b Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 23 Apr 2022 16:52:31 -0500 Subject: [PATCH 176/342] this might fix it --- src/engine/platform/genesisext.cpp | 24 ++++++++++++------------ src/engine/platform/genesisext.h | 19 +++++++++++++++++-- 2 files changed, 29 insertions(+), 14 deletions(-) diff --git a/src/engine/platform/genesisext.cpp b/src/engine/platform/genesisext.cpp index d8ae8b389..eefec5d8f 100644 --- a/src/engine/platform/genesisext.cpp +++ b/src/engine/platform/genesisext.cpp @@ -130,6 +130,9 @@ int DivPlatformGenesisExt::dispatch(DivCommand c) { int destFreq=NOTE_FNUM_BLOCK(c.value2,11); int newFreq; bool return2=false; + if (opChan[ch].portaPause) { + opChan[ch].baseFreq=opChan[ch].portaPauseFreq; + } if (destFreq>opChan[ch].baseFreq) { newFreq=opChan[ch].baseFreq+c.value; if (newFreq>=destFreq) { @@ -144,23 +147,20 @@ int DivPlatformGenesisExt::dispatch(DivCommand c) { } } // what the heck! - // TODO: rework! if (!opChan[ch].portaPause) { if ((newFreq&0x7ff)>1288) { - newFreq=(644)|((newFreq+0x800)&0xf800); + opChan[ch].portaPauseFreq=(644)|((newFreq+0x800)&0xf800); opChan[ch].portaPause=true; - return2=false; - } else if ((newFreq&0x7ff)<644) { - newFreq=(1287)|((newFreq-0x800)&0xf800); - opChan[ch].portaPause=true; - return2=false; - } else { - opChan[ch].freqChanged=true; + break; + } + if ((newFreq&0x7ff)<644) { + opChan[ch].portaPauseFreq=newFreq=(1287)|((newFreq-0x800)&0xf800); + opChan[ch].portaPause=true; + break; } - } else { - opChan[ch].portaPause=false; - opChan[ch].freqChanged=true; } + opChan[ch].portaPause=false; + opChan[ch].freqChanged=true; opChan[ch].baseFreq=newFreq; if (return2) return 2; break; diff --git a/src/engine/platform/genesisext.h b/src/engine/platform/genesisext.h index fc9eed892..4cdf9fc65 100644 --- a/src/engine/platform/genesisext.h +++ b/src/engine/platform/genesisext.h @@ -25,13 +25,28 @@ class DivPlatformGenesisExt: public DivPlatformGenesis { struct OpChannel { DivMacroInt std; unsigned char freqH, freqL; - int freq, baseFreq, pitch; + int freq, baseFreq, pitch, portaPauseFreq; unsigned char ins; signed char konCycles; bool active, insChanged, freqChanged, keyOn, keyOff, portaPause; int vol; unsigned char pan; - OpChannel(): freqH(0), freqL(0), freq(0), baseFreq(0), pitch(0), ins(-1), active(false), insChanged(true), freqChanged(false), keyOn(false), keyOff(false), portaPause(false), vol(0), pan(3) {} + OpChannel(): + freqH(0), + freqL(0), + freq(0), + baseFreq(0), + pitch(0), + portaPauseFreq(0), + ins(-1), + active(false), + insChanged(true), + freqChanged(false), + keyOn(false), + keyOff(false), + portaPause(false), + vol(0), + pan(3) {} }; OpChannel opChan[4]; bool isOpMuted[4]; From a7828d6ad63500ef44ba0ec2683540394982f506 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 23 Apr 2022 16:54:49 -0500 Subject: [PATCH 177/342] update contributing guidelines --- CONTRIBUTING.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ad608e4cb..eb25675d0 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -49,12 +49,27 @@ the coding style is described here: - `size_t` are 32-bit or 64-bit, depending on architecture. - in float/double operations, always use decimal and `f` if single-precision. - e.g. `1.0f` or `1.0` instead of `1`. +- prefer `NULL` over `nullptr` or any other proprietary null. - don't use `auto` unless needed. +- use `String` for `std::string` (this is typedef'd in ta-utils.h). +- prefer using operator for String (std::string) comparisons (a==""). some files (particularly the ones in `src/engine/platform/sound` and `extern/`) don't follow this style. you don't have to follow this style. I will fix it after I accept your contribution. +additional guidelines: + +- in general **strongly** avoid breaking compatibility. + - do not touch loadFur/saveFur unless you know what you're doing! + - new fields must be at the end of each block to ensure forward compatibility + - likewise, the instrument read/write functions in DivInstrument have to be handled carefully + - any change to the format requires a version bump (see `src/engine/engine.h`). + - do not bump the version number under any circumstances! + - if you are making major changes to the playback routine, make sure to test with older songs to ensure nothing breaks. + - I will run a test suite to make sure this is the case. + - if something breaks, you might want to add a compatibility flag (this requires changing the format though). + ## Demo Songs just put your demo song in `demos/`! From dd9bb8327a0ab8588d75dbc098810753f75f9958 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 23 Apr 2022 18:02:43 -0500 Subject: [PATCH 178/342] and this finally does it the last thing to do is a compatibility flag --- src/engine/platform/genesis.cpp | 19 +++++++++---------- src/engine/platform/genesis.h | 3 ++- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/engine/platform/genesis.cpp b/src/engine/platform/genesis.cpp index 43aae3f13..61fffcb81 100644 --- a/src/engine/platform/genesis.cpp +++ b/src/engine/platform/genesis.cpp @@ -618,6 +618,9 @@ int DivPlatformGenesis::dispatch(DivCommand c) { int destFreq=NOTE_FNUM_BLOCK(c.value2,11); int newFreq; bool return2=false; + if (chan[c.chan].portaPause) { + chan[c.chan].baseFreq=chan[c.chan].portaPauseFreq; + } if (destFreq>chan[c.chan].baseFreq) { newFreq=chan[c.chan].baseFreq+c.value; if (newFreq>=destFreq) { @@ -634,23 +637,19 @@ int DivPlatformGenesis::dispatch(DivCommand c) { // check for octave boundary // what the heck! if (!chan[c.chan].portaPause) { - chan[c.chan].freqChanged=true; if ((newFreq&0x7ff)>1288) { - newFreq=(644)|((newFreq+0x800)&0xf800); + chan[c.chan].portaPauseFreq=(644)|((newFreq+0x800)&0xf800); chan[c.chan].portaPause=true; - chan[c.chan].freqChanged=false; - return2=false; + break; } if ((newFreq&0x7ff)<644) { - newFreq=(1287)|((newFreq-0x800)&0xf800); + chan[c.chan].portaPauseFreq=newFreq=(1287)|((newFreq-0x800)&0xf800); chan[c.chan].portaPause=true; - chan[c.chan].freqChanged=false; - return2=false; + break; } - } else { - chan[c.chan].portaPause=false; - chan[c.chan].freqChanged=true; } + chan[c.chan].portaPause=false; + chan[c.chan].freqChanged=true; chan[c.chan].baseFreq=newFreq; if (return2) { chan[c.chan].inPorta=false; diff --git a/src/engine/platform/genesis.h b/src/engine/platform/genesis.h index ce52c6df1..b94303066 100644 --- a/src/engine/platform/genesis.h +++ b/src/engine/platform/genesis.h @@ -36,7 +36,7 @@ class DivPlatformGenesis: public DivDispatch { DivInstrumentFM state; DivMacroInt std; unsigned char freqH, freqL; - int freq, baseFreq, pitch, note; + int freq, baseFreq, pitch, portaPauseFreq, note; unsigned char ins; bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, furnaceDac, inPorta, hardReset; int vol, outVol; @@ -47,6 +47,7 @@ class DivPlatformGenesis: public DivDispatch { freq(0), baseFreq(0), pitch(0), + portaPauseFreq(0), note(0), ins(-1), active(false), From 18f7dcc0b0fff37545c51f98080147aa4466a752 Mon Sep 17 00:00:00 2001 From: James Alan Nguyen Date: Sun, 24 Apr 2022 12:31:37 +1000 Subject: [PATCH 179/342] Address review comments --- src/engine/fileOpsIns.cpp | 131 +++++++++++++++++++++----------------- src/engine/safeReader.cpp | 13 ++-- src/engine/safeReader.h | 8 +-- 3 files changed, 82 insertions(+), 70 deletions(-) diff --git a/src/engine/fileOpsIns.cpp b/src/engine/fileOpsIns.cpp index d913d96d4..796fd3ea6 100644 --- a/src/engine/fileOpsIns.cpp +++ b/src/engine/fileOpsIns.cpp @@ -498,11 +498,11 @@ void DivEngine::loadS3I(SafeReader& reader, std::vector& ret, St // Skip more stuff we don't need reader.seek(21, SEEK_CUR); } else { - lastError = "S3I PCM samples currently not supported."; + lastError="S3I PCM samples currently not supported."; logE("S3I PCM samples currently not supported."); } ins->name = reader.readString(28); - ins->name = (ins->name.length() == 0) ? stripPath : ins->name; + ins->name = (ins->name.size() == 0) ? stripPath : ins->name; int s3i_signature = reader.readI(); @@ -511,7 +511,7 @@ void DivEngine::loadS3I(SafeReader& reader, std::vector& ret, St logW("S3I signature invalid."); }; } catch (EndOfFileException& e) { - lastError = "premature end of file"; + lastError="premature end of file"; logE("premature end of file"); delete ins; return; @@ -534,7 +534,7 @@ void DivEngine::loadSBI(SafeReader& reader, std::vector& ret, St // 32-byte null terminated instrument name String patchName = reader.readString(32); - patchName = (patchName.length() == 0) ? stripPath : patchName; + patchName = (patchName.size() == 0) ? stripPath : patchName; auto writeOp = [](sbi_t& sbi, DivInstrumentFM::Operator& opM, DivInstrumentFM::Operator& opC) { opM.mult = sbi.Mcharacteristics & 0xF; @@ -608,7 +608,7 @@ void DivEngine::loadSBI(SafeReader& reader, std::vector& ret, St if (is_6op) { // Freq Monster 801 6op SBIs use a 4+2op layout // Save the 4op portion before reading the 2op part - ins->name = fmt::format("{0} (4op)", ins->name); + ins->name = fmt::sprintf("%s (4op)", ins->name); ret.push_back(ins); readSbiOpData(sbi_op12, reader); @@ -618,7 +618,7 @@ void DivEngine::loadSBI(SafeReader& reader, std::vector& ret, St DivInstrumentFM::Operator& opC6 = ins->fm.op[1]; ins->type = DIV_INS_OPL; ins->fm.ops = 2; - ins->name = fmt::format("{0} (2op)", patchName); + ins->name = fmt::sprintf("%s (2op)", patchName); writeOp(sbi_op12, opM6, opC6); ins->fm.alg = (sbi_op12.FeedConnect & 0x1); ins->fm.fb = ((sbi_op12.FeedConnect >> 1) & 0x7); @@ -632,7 +632,7 @@ void DivEngine::loadSBI(SafeReader& reader, std::vector& ret, St } } catch (EndOfFileException& e) { - lastError = "premature end of file"; + lastError="premature end of file"; logE("premature end of file"); delete ins; } @@ -644,7 +644,7 @@ void DivEngine::loadOPLI(SafeReader& reader, std::vector& ret, S try { reader.seek(0, SEEK_SET); String header = reader.readString(11); - if (header.compare("WOPL3-INST") == 0) { + if (header == "WOPL3-INST") { uint16_t version = reader.readS(); if (version > 3) { logW("Unknown OPLI version."); @@ -654,7 +654,7 @@ void DivEngine::loadOPLI(SafeReader& reader, std::vector& ret, S ins->type = DIV_INS_OPL; String insName = reader.readString(32); - insName = (insName.length() > 0) ? insName : stripPath; + insName = (insName.size() > 0) ? insName : stripPath; ins->name = insName; reader.seek(7, SEEK_CUR); // skip MIDI params uint8_t instTypeFlags = reader.readC(); // [0EEEDCBA] - see WOPL/OPLI spec @@ -693,7 +693,7 @@ void DivEngine::loadOPLI(SafeReader& reader, std::vector& ret, S if (is_4op && !is_2x2op) { ins->fm.ops = 4; ins->fm.alg = (feedConnect & 0x1) | ((feedConnect2nd & 0x1) << 1); - for (int i : {2,0,3,1}) { + for (int i : {2,0,3,1}) { // omfg >_< readOpliOp(reader, ins->fm.op[i]); } } else { @@ -706,12 +706,12 @@ void DivEngine::loadOPLI(SafeReader& reader, std::vector& ret, S } else if (is_2x2op) { // Note: Pair detuning offset not mappable. Use E5xx effect :P - ins->name = fmt::format("{0} (1)", insName); + ins->name = fmt::sprintf("%s (1)", insName); ret.push_back(ins); ins = new DivInstrument; ins->type = DIV_INS_OPL; - ins->name = fmt::format("{0} (2)", insName); + ins->name = fmt::sprintf("%s (2)", insName); for (int i : {1,0}) { readOpliOp(reader, ins->fm.op[i]); } @@ -723,7 +723,7 @@ void DivEngine::loadOPLI(SafeReader& reader, std::vector& ret, S ret.push_back(ins); } } catch (EndOfFileException& e) { - lastError = "premature end of file"; + lastError="premature end of file"; logE("premature end of file"); delete ins; } @@ -736,7 +736,7 @@ void DivEngine::loadOPNI(SafeReader& reader, std::vector& ret, S reader.seek(0, SEEK_SET); String header = reader.readString(11); - if (header.compare("WOPN2-INST") == 0 || header.compare("WOPN2-IN2T") == 0) { // omfg + if (header == "WOPN2-INST" || header == "WOPN2-IN2T") { // omfg >_< uint16_t version = reader.readS(); if (!(version >= 2) || version > 0xF) { // version 1 doesn't have a version field........ @@ -748,7 +748,7 @@ void DivEngine::loadOPNI(SafeReader& reader, std::vector& ret, S ins->fm.ops = 4; String insName = reader.readString(32); - ins->name = (insName.length() > 0) ? insName : stripPath; + ins->name = (insName.size() > 0) ? insName : stripPath; reader.seek(3, SEEK_CUR); // skip MIDI params uint8_t feedAlgo = reader.readC(); ins->fm.alg = (feedAlgo & 0x7); @@ -777,7 +777,7 @@ void DivEngine::loadOPNI(SafeReader& reader, std::vector& ret, S op.ssgEnv = ssgEg; }; - for (int i : {0,1,2,3}) { + for (int i = 0; i < 4; ++i) { readOpniOp(reader, ins->fm.op[i]); } @@ -786,7 +786,7 @@ void DivEngine::loadOPNI(SafeReader& reader, std::vector& ret, S ret.push_back(ins); } } catch (EndOfFileException& e) { - lastError = "premature end of file"; + lastError="premature end of file"; logE("premature end of file"); delete ins; } @@ -801,7 +801,7 @@ void DivEngine::loadY12(SafeReader& reader, std::vector& ret, St ins->fm.ops = 4; ins->name = stripPath; - for (int i : {0,1,2,3}) { + for (int i; i < 4; ++i) { DivInstrumentFM::Operator& insOp = ins->fm.op[i]; uint8_t tmp = reader.readC(); insOp.mult = tmp & 0xF; @@ -825,7 +825,7 @@ void DivEngine::loadY12(SafeReader& reader, std::vector& ret, St reader.seek(62, SEEK_CUR); ret.push_back(ins); } catch (EndOfFileException& e) { - lastError = "premature end of file"; + lastError="premature end of file"; logE("premature end of file"); delete ins; } @@ -904,14 +904,14 @@ void DivEngine::loadBNK(SafeReader& reader, std::vector& ret, St ins->fm.op[0].ws = reader.readC(); ins->fm.op[1].ws = reader.readC(); - ins->name = instNames[i]->length() > 0 ? (*instNames[i]) : fmt::format("{0}[{1}]", stripPath, i); + ins->name = instNames[i]->length() > 0 ? (*instNames[i]) : fmt::sprintf("%s[%d]", stripPath, i); } reader.seek(0, SEEK_END); } catch (EndOfFileException& e) { - lastError = "premature end of file"; + lastError="premature end of file"; logE("premature end of file"); - for (int i = readCount; i >= 0; --i) { + for (int i = readCount - 1; i >= 0; --i) { delete insList[i]; } is_failed = true; @@ -919,7 +919,7 @@ void DivEngine::loadBNK(SafeReader& reader, std::vector& ret, St } else { // assume GEMS BNK for now. - lastError = "GEMS BNK currently not supported."; + lastError="GEMS BNK currently not supported."; logE("GEMS BNK currently not supported."); } @@ -994,9 +994,9 @@ void DivEngine::loadFF(SafeReader& reader, std::vector& ret, Str ++readCount; } } catch (EndOfFileException& e) { - lastError = "premature end of file"; + lastError="premature end of file"; logE("premature end of file"); - for (int i = readCount; i >= 0; --i) { + for (int i = readCount - 1; i >= 0; --i) { delete insList[i]; } return; @@ -1011,6 +1011,7 @@ void DivEngine::loadOPM(SafeReader& reader, std::vector& ret, St std::vector insList; int readCount = 0; + bool is_failed = false; bool patchNameRead = false, lfoRead = false, @@ -1020,52 +1021,55 @@ void DivEngine::loadOPM(SafeReader& reader, std::vector& ret, St m2Read = false, c2Read = false; - DivInstrument* newPatch = nullptr; + DivInstrument* newPatch = NULL; auto completePatchRead = [&]() { return patchNameRead && lfoRead && characteristicRead && m1Read && c1Read && m2Read && c2Read; }; auto resetPatchRead = [&]() { patchNameRead = lfoRead = characteristicRead = m1Read = c1Read = m2Read = c2Read = false; - newPatch = nullptr; + newPatch = NULL; }; auto readIntStrWithinRange = [](String&& input, int limitLow, int limitHigh) { - int x = atoi(input.c_str()); + int x = std::stoi(input.c_str()); + if (x > limitHigh || x < limitLow) { + throw std::invalid_argument(fmt::sprintf("%s is out of bounds of range [%d..%d]", input, limitLow, limitHigh)); + } return (x>limitHigh) ? limitHigh : (x= 4) ? (7 - op.dt) : (op.dt + 3); - op.dt2 = readIntStrWithinRange(reader.readString_Token(), 0, 3); - op.am = readIntStrWithinRange(reader.readString_Token(), 0, 1); + op.dt2 = readIntStrWithinRange(reader.readStringToken(), 0, 3); + op.am = readIntStrWithinRange(reader.readStringToken(), 0, 1); }; try { reader.seek(0, SEEK_SET); while (!reader.isEOF()) { - String token = reader.readString_Token(); - if (token.length() == 0) { + String token = reader.readStringToken(); + if (token.size() == 0) { continue; } if (token.compare(0,2,"//") == 0) { if (!reader.isEOF()) { - reader.readString_Line(); + reader.readStringLine(); } continue; } // At this point we know any other line would be associated with patch params - if (newPatch == nullptr) { + if (newPatch == NULL) { newPatch = new DivInstrument; newPatch->type = DIV_INS_FM; newPatch->fm.ops = 4; @@ -1074,23 +1078,23 @@ void DivEngine::loadOPM(SafeReader& reader, std::vector& ret, St // Read each line for their respective params. They may not be written in the same LINE order but they // must absolutely be properly grouped per patch! Line prefixes must be separated by a space! (see inline comments) - if (token.length() >= 2) { + if (token.size() >= 2) { if (token[0] == '@') { // @:123 Name of patch // Note: Fallback to bank filename and current patch number in _file_ order (not @n order) - newPatch->name = reader.readString_Line(); - newPatch->name = newPatch->name.length() > 0 ? newPatch->name : fmt::format("{0}[{1}]", stripPath, readCount); + newPatch->name = reader.readStringLine(); + newPatch->name = newPatch->name.size() > 0 ? newPatch->name : fmt::sprintf("%s[%d]", stripPath, readCount); patchNameRead = true; } else if (token.compare(0,3,"CH:") == 0) { // CH: PAN FL CON AMS PMS SLOT NE - reader.readString_Token(); // skip PAN - newPatch->fm.fb = readIntStrWithinRange(reader.readString_Token(), 0, 7); - newPatch->fm.alg = readIntStrWithinRange(reader.readString_Token(), 0, 7); - newPatch->fm.ams = readIntStrWithinRange(reader.readString_Token(), 0, 4); - newPatch->fm.fms = readIntStrWithinRange(reader.readString_Token(), 0, 7); - reader.readString_Token(); // skip SLOT - reader.readString_Token(); // skip NE + reader.readStringToken(); // skip PAN + newPatch->fm.fb = readIntStrWithinRange(reader.readStringToken(), 0, 7); + newPatch->fm.alg = readIntStrWithinRange(reader.readStringToken(), 0, 7); + newPatch->fm.ams = readIntStrWithinRange(reader.readStringToken(), 0, 4); + newPatch->fm.fms = readIntStrWithinRange(reader.readStringToken(), 0, 7); + reader.readStringToken(); // skip SLOT + reader.readStringToken(); // skip NE characteristicRead = true; } else if (token.compare(0,3,"C1:") == 0) { @@ -1116,12 +1120,12 @@ void DivEngine::loadOPM(SafeReader& reader, std::vector& ret, St } else if (token.compare(0,4,"LFO:") == 0) { // LFO: LFRQ AMD PMD WF NFRQ // Furnace patches do not store these as they are chip-global. - reader.readString_Line(); + reader.readStringLine(); lfoRead = true; } else { // other unsupported lines ignored. - reader.readString_Line(); + reader.readStringLine(); } } @@ -1132,7 +1136,7 @@ void DivEngine::loadOPM(SafeReader& reader, std::vector& ret, St } } - if (newPatch != nullptr) { + if (newPatch != NULL) { addWarning("Last OPM patch read was incomplete and therefore not imported."); logW("Last OPM patch read was incomplete and therefore not imported."); delete newPatch; @@ -1142,12 +1146,21 @@ void DivEngine::loadOPM(SafeReader& reader, std::vector& ret, St ret.push_back(insList[i]); } } catch (EndOfFileException& e) { - lastError = "premature end of file"; + lastError="premature end of file"; logE("premature end of file"); - for (int i = readCount; i >= 0; --i) { + is_failed = true; + } catch (std::invalid_argument& e) { + lastError=fmt::sprintf("Invalid value found in patch file. %s", e.what()); + logE("Invalid value found in patch file."); + logE(e.what()); + is_failed = true; + } + + if (is_failed) { + for (int i = readCount - 1; i >= 0; --i) { delete insList[i]; } - if (newPatch != nullptr) { + if (newPatch != NULL) { delete newPatch; } } diff --git a/src/engine/safeReader.cpp b/src/engine/safeReader.cpp index 9d0da1a96..eb9f63ae1 100644 --- a/src/engine/safeReader.cpp +++ b/src/engine/safeReader.cpp @@ -165,7 +165,7 @@ String SafeReader::readString() { return ret; } -String SafeReader::readString_Line() { +String SafeReader::readStringLine() { String ret; unsigned char c; if (isEOF()) throw EndOfFileException(this, len); @@ -179,7 +179,7 @@ String SafeReader::readString_Line() { return ret; } -String SafeReader::readString_Token(unsigned char delim) { +String SafeReader::readStringToken(unsigned char delim) { String ret; unsigned char c; if (isEOF()) throw EndOfFileException(this, len); @@ -198,12 +198,11 @@ String SafeReader::readString_Token(unsigned char delim) { } return ret; } -String SafeReader::readString_Token() { - return readString_Token(' '); + +String SafeReader::readStringToken() { + return readStringToken(' '); } - - -bool SafeReader::isEOF() { +inline bool SafeReader::isEOF() { return curSeek >= len; } \ No newline at end of file diff --git a/src/engine/safeReader.h b/src/engine/safeReader.h index 4216a5969..5f27383c3 100644 --- a/src/engine/safeReader.h +++ b/src/engine/safeReader.h @@ -66,10 +66,10 @@ class SafeReader { double readD_BE(); String readString(); String readString(size_t len); - String readString_Line(); - String readString_Token(unsigned char delim); - String readString_Token(); - bool isEOF(); + String readStringLine(); + String readStringToken(unsigned char delim); + String readStringToken(); + inline bool isEOF(); SafeReader(void* b, size_t l): buf((unsigned char*)b), From 580cff9d32417684823a0f4537d6958ecdc6a37f Mon Sep 17 00:00:00 2001 From: James Alan Nguyen Date: Sun, 24 Apr 2022 12:37:49 +1000 Subject: [PATCH 180/342] Decided to EOF fail seeks beyond bounds --- src/engine/fileOpsIns.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/engine/fileOpsIns.cpp b/src/engine/fileOpsIns.cpp index 796fd3ea6..ce03915b0 100644 --- a/src/engine/fileOpsIns.cpp +++ b/src/engine/fileOpsIns.cpp @@ -818,11 +818,15 @@ void DivEngine::loadY12(SafeReader& reader, std::vector& ret, St insOp.rr = tmp & 0xF; insOp.sl = ((tmp >> 4) & 0xF); insOp.ssgEnv = reader.readC(); - reader.seek(9, SEEK_CUR); + if (!reader.seek(9, SEEK_CUR)) { + throw EndOfFileException(&reader, reader.tell() + 9); + } } ins->fm.alg = reader.readC(); ins->fm.fb = reader.readC(); - reader.seek(62, SEEK_CUR); + if (!reader.seek(62, SEEK_CUR)) { + throw EndOfFileException(&reader, reader.tell() + 62); + } ret.push_back(ins); } catch (EndOfFileException& e) { lastError="premature end of file"; @@ -858,7 +862,9 @@ void DivEngine::loadBNK(SafeReader& reader, std::vector& ret, St } // Seek to BNK data - reader.seek(data_offset, SEEK_SET); + if (!reader.seek(data_offset, SEEK_SET)) { + throw EndOfFileException(&reader, data_offset); + }; // Read until EOF for (int i = 0; i < readCount; ++i) { From 2a48adfde8a9d42cdd8ce23aa08b683ae7ade504 Mon Sep 17 00:00:00 2001 From: James Alan Nguyen Date: Sun, 24 Apr 2022 13:43:56 +1000 Subject: [PATCH 181/342] Inline error --- src/engine/safeReader.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/engine/safeReader.h b/src/engine/safeReader.h index 5f27383c3..e95c90a01 100644 --- a/src/engine/safeReader.h +++ b/src/engine/safeReader.h @@ -69,7 +69,7 @@ class SafeReader { String readStringLine(); String readStringToken(unsigned char delim); String readStringToken(); - inline bool isEOF(); + bool isEOF(); SafeReader(void* b, size_t l): buf((unsigned char*)b), From 2b90bd6c66b82d4589b0bab2b01c1b3bd249b520 Mon Sep 17 00:00:00 2001 From: James Alan Nguyen Date: Sun, 24 Apr 2022 13:55:21 +1000 Subject: [PATCH 182/342] = 0 --- src/engine/fileOpsIns.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/engine/fileOpsIns.cpp b/src/engine/fileOpsIns.cpp index ce03915b0..e080b7797 100644 --- a/src/engine/fileOpsIns.cpp +++ b/src/engine/fileOpsIns.cpp @@ -801,7 +801,7 @@ void DivEngine::loadY12(SafeReader& reader, std::vector& ret, St ins->fm.ops = 4; ins->name = stripPath; - for (int i; i < 4; ++i) { + for (int i = 0; i < 4; ++i) { DivInstrumentFM::Operator& insOp = ins->fm.op[i]; uint8_t tmp = reader.readC(); insOp.mult = tmp & 0xF; From 798bc08431419fe811262be24b5d1d9016d9434a Mon Sep 17 00:00:00 2001 From: James Alan Nguyen Date: Sun, 24 Apr 2022 14:04:56 +1000 Subject: [PATCH 183/342] =?UTF-8?q?verdammte=20compilerschie=C3=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/engine/safeReader.cpp | 4 ---- src/engine/safeReader.h | 2 +- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/src/engine/safeReader.cpp b/src/engine/safeReader.cpp index eb9f63ae1..36d6097d6 100644 --- a/src/engine/safeReader.cpp +++ b/src/engine/safeReader.cpp @@ -202,7 +202,3 @@ String SafeReader::readStringToken(unsigned char delim) { String SafeReader::readStringToken() { return readStringToken(' '); } - -inline bool SafeReader::isEOF() { - return curSeek >= len; -} \ No newline at end of file diff --git a/src/engine/safeReader.h b/src/engine/safeReader.h index e95c90a01..e21311f98 100644 --- a/src/engine/safeReader.h +++ b/src/engine/safeReader.h @@ -69,7 +69,7 @@ class SafeReader { String readStringLine(); String readStringToken(unsigned char delim); String readStringToken(); - bool isEOF(); + inline bool isEOF() { return curSeek >= len; }; SafeReader(void* b, size_t l): buf((unsigned char*)b), From 23be8d93367f0960338725fb26cf7f9e65437fcf Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 24 Apr 2022 14:40:07 -0500 Subject: [PATCH 184/342] the final piece of f-num/block work --- papers/format.md | 6 +++++- src/engine/engine.h | 4 ++-- src/engine/fileOps.cpp | 14 ++++++++++++-- src/engine/platform/genesisext.cpp | 20 ++++++++++++++------ src/engine/song.h | 4 +++- src/gui/compatFlags.cpp | 6 +++++- 6 files changed, 41 insertions(+), 13 deletions(-) diff --git a/papers/format.md b/papers/format.md index 7775e9ea3..ef8ab7e50 100644 --- a/papers/format.md +++ b/papers/format.md @@ -29,6 +29,8 @@ furthermore, an `or reserved` indicates this field is always present, but is res the format versions are: +- 85: Furnace dev85 +- 84: Furnace dev84 - 83: Furnace dev83 - 82: Furnace dev82 - 81: Furnace dev81 @@ -269,7 +271,9 @@ size | description 1 | ExtCh channel state is shared (>=78) or reserved 1 | ignore DAC mode change outside of intended channel (>=83) or reserved 1 | E1xx and E2xx also take priority over Slide00 (>=83) or reserved - 23 | reserved + 1 | new Sega PCM (with macros and proper vol/pan) (>=84) or reserved + 1 | weird f-num/block-based chip pitch slides (>=85) or reserved + 21 | reserved ``` # instrument diff --git a/src/engine/engine.h b/src/engine/engine.h index 515b9c590..81e15f726 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -43,8 +43,8 @@ #define BUSY_BEGIN_SOFT softLocked=true; isBusy.lock(); #define BUSY_END isBusy.unlock(); softLocked=false; -#define DIV_VERSION "dev84" -#define DIV_ENGINE_VERSION 84 +#define DIV_VERSION "dev85" +#define DIV_ENGINE_VERSION 85 // for imports #define DIV_VERSION_MOD 0xff01 diff --git a/src/engine/fileOps.cpp b/src/engine/fileOps.cpp index aee7de71f..48ef68bba 100644 --- a/src/engine/fileOps.cpp +++ b/src/engine/fileOps.cpp @@ -162,6 +162,7 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) { ds.gbInsAffectsEnvelope=true; ds.ignoreDACModeOutsideIntendedChannel=false; ds.e1e2AlsoTakePriority=true; + ds.fbPortaPause=true; // 1.1 compat flags if (ds.version>24) { @@ -993,6 +994,9 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) { if (ds.version<84) { ds.newSegaPCM=false; } + if (ds.version<85) { + ds.fbPortaPause=true; + } ds.isDMF=false; reader.readS(); // reserved @@ -1342,7 +1346,12 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) { } else { reader.readC(); } - for (int i=0; i<22; i++) { + if (ds.version>=85) { + ds.fbPortaPause=reader.readC(); + } else { + reader.readC(); + } + for (int i=0; i<21; i++) { reader.readC(); } } @@ -2283,7 +2292,8 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) { w->writeC(song.ignoreDACModeOutsideIntendedChannel); w->writeC(song.e1e2AlsoTakePriority); w->writeC(song.newSegaPCM); - for (int i=0; i<22; i++) { + w->writeC(song.fbPortaPause); + for (int i=0; i<21; i++) { w->writeC(0); } diff --git a/src/engine/platform/genesisext.cpp b/src/engine/platform/genesisext.cpp index eefec5d8f..479e42421 100644 --- a/src/engine/platform/genesisext.cpp +++ b/src/engine/platform/genesisext.cpp @@ -149,14 +149,22 @@ int DivPlatformGenesisExt::dispatch(DivCommand c) { // what the heck! if (!opChan[ch].portaPause) { if ((newFreq&0x7ff)>1288) { - opChan[ch].portaPauseFreq=(644)|((newFreq+0x800)&0xf800); - opChan[ch].portaPause=true; - break; + if (parent->song.fbPortaPause) { + opChan[ch].portaPauseFreq=(644)|((newFreq+0x800)&0xf800); + opChan[ch].portaPause=true; + break; + } else { + newFreq=(newFreq>>1)|((newFreq+0x800)&0xf800); + } } if ((newFreq&0x7ff)<644) { - opChan[ch].portaPauseFreq=newFreq=(1287)|((newFreq-0x800)&0xf800); - opChan[ch].portaPause=true; - break; + if (parent->song.fbPortaPause) { + opChan[ch].portaPauseFreq=newFreq=(1287)|((newFreq-0x800)&0xf800); + opChan[ch].portaPause=true; + break; + } else { + newFreq=(newFreq<<1)|((newFreq-0x800)&0xf800); + } } } opChan[ch].portaPause=false; diff --git a/src/engine/song.h b/src/engine/song.h index 20b17ae60..9b9187ac6 100644 --- a/src/engine/song.h +++ b/src/engine/song.h @@ -324,6 +324,7 @@ struct DivSong { bool ignoreDACModeOutsideIntendedChannel; bool e1e2AlsoTakePriority; bool newSegaPCM; + bool fbPortaPause; DivOrders orders; std::vector ins; @@ -406,7 +407,8 @@ struct DivSong { sharedExtStat(true), ignoreDACModeOutsideIntendedChannel(false), e1e2AlsoTakePriority(false), - newSegaPCM(true) { + newSegaPCM(true), + fbPortaPause(false) { for (int i=0; i<32; i++) { system[i]=DIV_SYSTEM_NULL; systemVol[i]=64; diff --git a/src/gui/compatFlags.cpp b/src/gui/compatFlags.cpp index d2035ff70..8d1fd6d18 100644 --- a/src/gui/compatFlags.cpp +++ b/src/gui/compatFlags.cpp @@ -101,6 +101,10 @@ void FurnaceGUI::drawCompatFlags() { if (ImGui::IsItemHovered()) { ImGui::SetTooltip("simulates a bug in where portamento does not work after sliding."); } + ImGui::Checkbox("FM pitch slide octave boundary odd behavior",&e->song.fbPortaPause); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("if this is on, a pitch slide that crosses the octave boundary will stop for one tick and then continue from the nearest octave boundary.\nfor .dmf compatibility."); + } ImGui::Checkbox("Apply Game Boy envelope on note-less instrument change",&e->song.gbInsAffectsEnvelope); if (ImGui::IsItemHovered()) { ImGui::SetTooltip("if this is on, an instrument change will also affect the envelope."); @@ -173,4 +177,4 @@ void FurnaceGUI::drawCompatFlags() { } if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_COMPAT_FLAGS; ImGui::End(); -} \ No newline at end of file +} From e414056f2d82e355c68c7a20976e7dc2572b3284 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 24 Apr 2022 16:57:58 -0500 Subject: [PATCH 185/342] prepare for a clear option --- src/gui/gui.cpp | 31 +++++++++++++++++++++++++++++-- src/gui/gui.h | 1 + 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index e87c1e8ea..1a9e537c3 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -2711,8 +2711,10 @@ bool FurnaceGUI::loop() { if (ImGui::MenuItem("redo",BIND_FOR(GUI_ACTION_REDO))) doRedo(); ImGui::Separator(); editOptions(true); - /*ImGui::Separator(); - ImGui::MenuItem("clear...");*/ + ImGui::Separator(); + if (ImGui::MenuItem("clear...")) { + showWarning("Are you sure you want to clear...",GUI_WARN_CLEAR); + } ImGui::EndMenu(); } if (ImGui::BeginMenu("settings")) { @@ -3370,6 +3372,31 @@ bool FurnaceGUI::loop() { ImGui::CloseCurrentPopup(); } break; + case GUI_WARN_CLEAR: + if (ImGui::Button("Song (orders and patterns)")) { + ImGui::CloseCurrentPopup(); + } + ImGui::SameLine(); + if (ImGui::Button("Pattern")) { + ImGui::CloseCurrentPopup(); + } + ImGui::SameLine(); + if (ImGui::Button("Instruments")) { + ImGui::CloseCurrentPopup(); + } + ImGui::SameLine(); + if (ImGui::Button("Wavetables")) { + ImGui::CloseCurrentPopup(); + } + ImGui::SameLine(); + if (ImGui::Button("Samples")) { + ImGui::CloseCurrentPopup(); + } + + if (ImGui::Button("Wait! What am I doing?")) { + ImGui::CloseCurrentPopup(); + } + break; case GUI_WARN_GENERIC: if (ImGui::Button("OK")) { ImGui::CloseCurrentPopup(); diff --git a/src/gui/gui.h b/src/gui/gui.h index 6c50a914a..8c850fd06 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -264,6 +264,7 @@ enum FurnaceGUIWarnings { GUI_WARN_RESET_COLORS, GUI_WARN_RESET_KEYBINDS, GUI_WARN_CLOSE_SETTINGS, + GUI_WARN_CLEAR, GUI_WARN_GENERIC }; From 521b3116922766344071b0f0d0a7acf4e767da99 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 24 Apr 2022 17:39:18 -0500 Subject: [PATCH 186/342] GUI: implement clear --- src/engine/song.cpp | 36 ++++++++++++++++++++++++++++++++++++ src/engine/song.h | 21 +++++++++++++++++++++ src/gui/gui.cpp | 40 +++++++++++++++++++++++++++++++++++++++- 3 files changed, 96 insertions(+), 1 deletion(-) diff --git a/src/engine/song.cpp b/src/engine/song.cpp index 4d2aef8da..7a815eb16 100644 --- a/src/engine/song.cpp +++ b/src/engine/song.cpp @@ -19,21 +19,57 @@ #include "song.h" +void DivSong::clearSongData() { + for (int i=0; ilockEngine([this]() { + e->song.clearSongData(); + }); + e->setOrder(0); + curOrder=0; + oldOrder=0; + oldOrder1=0; + MARK_MODIFIED; ImGui::CloseCurrentPopup(); } ImGui::SameLine(); if (ImGui::Button("Pattern")) { + stop(); + e->lockEngine([this]() { + for (int i=0; igetTotalChannelCount(); i++) { + DivPattern* pat=e->song.pat[i].getPattern(e->song.orders.ord[i][curOrder],true); + memset(pat->data,-1,256*32*sizeof(short)); + for (int j=0; j<256; j++) { + pat->data[j][0]=0; + pat->data[j][1]=0; + } + } + }); + MARK_MODIFIED; ImGui::CloseCurrentPopup(); } ImGui::SameLine(); if (ImGui::Button("Instruments")) { + stop(); + e->lockEngine([this]() { + e->song.clearInstruments(); + }); + curIns=-1; + MARK_MODIFIED; ImGui::CloseCurrentPopup(); } ImGui::SameLine(); if (ImGui::Button("Wavetables")) { + stop(); + e->lockEngine([this]() { + e->song.clearWavetables(); + }); + curWave=0; + MARK_MODIFIED; ImGui::CloseCurrentPopup(); } ImGui::SameLine(); if (ImGui::Button("Samples")) { + stop(); + e->lockEngine([this]() { + e->song.clearSamples(); + }); + curSample=0; ImGui::CloseCurrentPopup(); } - if (ImGui::Button("Wait! What am I doing?")) { + if (ImGui::Button("Wait! What am I doing? Cancel!")) { ImGui::CloseCurrentPopup(); } break; From f99ba810feab7b3bed0b5e8b46174df2a3e0617c Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 24 Apr 2022 17:48:04 -0500 Subject: [PATCH 187/342] PC speaker: fix timer issue --- src/engine/platform/pcspkr.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/engine/platform/pcspkr.cpp b/src/engine/platform/pcspkr.cpp index b34d16f24..8a00c5638 100644 --- a/src/engine/platform/pcspkr.cpp +++ b/src/engine/platform/pcspkr.cpp @@ -53,6 +53,7 @@ void DivPlatformPCSpeaker::acquire_unfilt(short* bufL, short* bufR, size_t start for (size_t i=start; ifreq) pos=freq; while (pos<0) { if (freq<1) { pos=1; @@ -71,6 +72,7 @@ void DivPlatformPCSpeaker::acquire_cone(short* bufL, short* bufR, size_t start, for (size_t i=start; ifreq) pos=freq; while (pos<0) { if (freq<1) { pos=1; @@ -95,6 +97,7 @@ void DivPlatformPCSpeaker::acquire_piezo(short* bufL, short* bufR, size_t start, for (size_t i=start; ifreq) pos=freq; while (pos<0) { if (freq<1) { pos=1; From bd68ef5cc3efdc439aa7f7418dc8028f65f6d106 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 24 Apr 2022 18:12:18 -0500 Subject: [PATCH 188/342] dev86 - SN duty macro phase reset fix --- papers/format.md | 4 +++- src/engine/engine.h | 4 ++-- src/engine/fileOps.cpp | 14 ++++++++++++-- src/engine/platform/sms.cpp | 10 ++++++---- src/engine/song.h | 4 +++- src/gui/compatFlags.cpp | 4 ++++ 6 files changed, 30 insertions(+), 10 deletions(-) diff --git a/papers/format.md b/papers/format.md index ef8ab7e50..207b232aa 100644 --- a/papers/format.md +++ b/papers/format.md @@ -29,6 +29,7 @@ furthermore, an `or reserved` indicates this field is always present, but is res the format versions are: +- 86: Furnace dev86 - 85: Furnace dev85 - 84: Furnace dev84 - 83: Furnace dev83 @@ -273,7 +274,8 @@ size | description 1 | E1xx and E2xx also take priority over Slide00 (>=83) or reserved 1 | new Sega PCM (with macros and proper vol/pan) (>=84) or reserved 1 | weird f-num/block-based chip pitch slides (>=85) or reserved - 21 | reserved + 1 | SN duty macro always resets phase (>=86) or reserved + 20 | reserved ``` # instrument diff --git a/src/engine/engine.h b/src/engine/engine.h index 427215df0..3bd1db3dc 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -43,8 +43,8 @@ #define BUSY_BEGIN_SOFT softLocked=true; isBusy.lock(); #define BUSY_END isBusy.unlock(); softLocked=false; -#define DIV_VERSION "dev85" -#define DIV_ENGINE_VERSION 85 +#define DIV_VERSION "dev86" +#define DIV_ENGINE_VERSION 86 // for imports #define DIV_VERSION_MOD 0xff01 diff --git a/src/engine/fileOps.cpp b/src/engine/fileOps.cpp index 48ef68bba..38b33502b 100644 --- a/src/engine/fileOps.cpp +++ b/src/engine/fileOps.cpp @@ -163,6 +163,7 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) { ds.ignoreDACModeOutsideIntendedChannel=false; ds.e1e2AlsoTakePriority=true; ds.fbPortaPause=true; + ds.snDutyReset=true; // 1.1 compat flags if (ds.version>24) { @@ -997,6 +998,9 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) { if (ds.version<85) { ds.fbPortaPause=true; } + if (ds.version<86) { + ds.snDutyReset=true; + } ds.isDMF=false; reader.readS(); // reserved @@ -1351,7 +1355,12 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) { } else { reader.readC(); } - for (int i=0; i<21; i++) { + if (ds.version>=86) { + ds.snDutyReset=reader.readC(); + } else { + reader.readC(); + } + for (int i=0; i<20; i++) { reader.readC(); } } @@ -2293,7 +2302,8 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) { w->writeC(song.e1e2AlsoTakePriority); w->writeC(song.newSegaPCM); w->writeC(song.fbPortaPause); - for (int i=0; i<21; i++) { + w->writeC(song.snDutyReset); + for (int i=0; i<20; i++) { w->writeC(0); } diff --git a/src/engine/platform/sms.cpp b/src/engine/platform/sms.cpp index af339f6e2..c32548b56 100644 --- a/src/engine/platform/sms.cpp +++ b/src/engine/platform/sms.cpp @@ -86,11 +86,13 @@ void DivPlatformSMS::tick(bool sysTick) { } if (i==3) { if (chan[i].std.duty.had) { - snNoiseMode=chan[i].std.duty.val; - if (chan[i].std.duty.val<2) { - chan[3].freqChanged=false; + if (chan[i].std.duty.val!=snNoiseMode || parent->song.snDutyReset) { + snNoiseMode=chan[i].std.duty.val; + if (chan[i].std.duty.val<2) { + chan[3].freqChanged=false; + } + updateSNMode=true; } - updateSNMode=true; } if (chan[i].std.phaseReset.had) { if (chan[i].std.phaseReset.val==1) { diff --git a/src/engine/song.h b/src/engine/song.h index 11264558b..7c06a14a5 100644 --- a/src/engine/song.h +++ b/src/engine/song.h @@ -325,6 +325,7 @@ struct DivSong { bool e1e2AlsoTakePriority; bool newSegaPCM; bool fbPortaPause; + bool snDutyReset; DivOrders orders; std::vector ins; @@ -429,7 +430,8 @@ struct DivSong { ignoreDACModeOutsideIntendedChannel(false), e1e2AlsoTakePriority(false), newSegaPCM(true), - fbPortaPause(false) { + fbPortaPause(false), + snDutyReset(false) { for (int i=0; i<32; i++) { system[i]=DIV_SYSTEM_NULL; systemVol[i]=64; diff --git a/src/gui/compatFlags.cpp b/src/gui/compatFlags.cpp index 8d1fd6d18..ccaeeb3fe 100644 --- a/src/gui/compatFlags.cpp +++ b/src/gui/compatFlags.cpp @@ -117,6 +117,10 @@ void FurnaceGUI::drawCompatFlags() { if (ImGui::IsItemHovered()) { ImGui::SetTooltip("does this make any sense by now?"); } + ImGui::Checkbox("SN76489 duty macro always resets phase",&e->song.snDutyReset); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("when enabled, duty macro will always reset phase, even if its value hasn't changed."); + } ImGui::Text("Loop modality:"); if (ImGui::RadioButton("Reset channels",e->song.loopModality==0)) { From 52e35fdf049ac9975f297341fcf363a85e57c1e6 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 24 Apr 2022 18:17:05 -0500 Subject: [PATCH 189/342] huh? TODO: fix this --- src/engine/platform/genesis.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/engine/platform/genesis.cpp b/src/engine/platform/genesis.cpp index 61fffcb81..0e8c59543 100644 --- a/src/engine/platform/genesis.cpp +++ b/src/engine/platform/genesis.cpp @@ -395,7 +395,7 @@ void DivPlatformGenesis::tick(bool sysTick) { if (i==2 && extMode) continue; if (chan[i].freqChanged) { chan[i].freq=((chan[i].baseFreq&0xf800)|parent->calcFreq(chan[i].baseFreq&0x7ff,chan[i].pitch,false,4))+chan[i].std.pitch.val; - if (chan[i].freq>65535) chan[i].freq=65535; + if (chan[i].freq>0x3fff) chan[i].freq=0x3fff; int freqt=chan[i].freq; immWrite(chanOffs[i]+ADDR_FREQH,freqt>>8); immWrite(chanOffs[i]+ADDR_FREQ,freqt&0xff); From c84ff399d9f649776c382a6603e1f5ac954d8cd9 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 24 Apr 2022 22:45:59 -0500 Subject: [PATCH 190/342] YM2612: fix negative octaves --- src/engine/dispatch.h | 3 ++- src/engine/engine.cpp | 11 +++++++++++ src/engine/engine.h | 3 +++ 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/engine/dispatch.h b/src/engine/dispatch.h index b358c464c..bd3b19a68 100644 --- a/src/engine/dispatch.h +++ b/src/engine/dispatch.h @@ -426,7 +426,8 @@ class DivDispatch { #define NOTE_PERIODIC(x) round(parent->calcBaseFreq(chipClock,CHIP_DIVIDER,x,true)) #define NOTE_PERIODIC_NOROUND(x) parent->calcBaseFreq(chipClock,CHIP_DIVIDER,x,true) #define NOTE_FREQUENCY(x) parent->calcBaseFreq(chipClock,CHIP_FREQBASE,x,false) -#define NOTE_FNUM_BLOCK(x,bits) ((((int)parent->calcBaseFreq(chipClock,CHIP_FREQBASE,(x)%12,false))&((1<calcBaseFreqFNumBlock(chipClock,CHIP_FREQBASE,x,bits) #define COLOR_NTSC (315000000.0/88.0) #define COLOR_PAL (283.75*15625.0+25.0) diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 905ba8aa5..2ebbd2fdb 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -887,6 +887,17 @@ double DivEngine::calcBaseFreq(double clock, double divider, int note, bool peri base*(divider/clock); } +unsigned short DivEngine::calcBaseFreqFNumBlock(double clock, double divider, int note, int bits) { + int bf=calcBaseFreq(clock,divider,note,false); + int block=note/12; + if (block<0) block=0; + if (block>7) block=7; + bf>>=block; + if (bf<0) bf=0; + if (bf>((1< Date: Mon, 25 Apr 2022 01:24:42 -0500 Subject: [PATCH 191/342] YM2612: fix several sample bugs --- src/engine/engine.cpp | 10 ++++- src/engine/platform/genesis.cpp | 69 +++++++++++++++++++++++------- src/engine/platform/genesisext.cpp | 14 +++++- 3 files changed, 74 insertions(+), 19 deletions(-) diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 2ebbd2fdb..3bfcb54c5 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -894,7 +894,15 @@ unsigned short DivEngine::calcBaseFreqFNumBlock(double clock, double divider, in if (block>7) block=7; bf>>=block; if (bf<0) bf=0; - if (bf>((1<1288) { // apparently octave boundary + while (block<7) { + bf>>=1; + block++; + } + if (bf>((1<=rate) { DivSample* s=parent->getSample(dacSample); if (s->samples>0) { if (!isMuted[5]) { @@ -106,7 +106,7 @@ void DivPlatformGenesis::acquire_nuked(short* bufL, short* bufR, size_t start, s } } } - dacPeriod+=MAX(40,dacRate); + dacPeriod-=rate; } else { dacSample=-1; } @@ -156,8 +156,8 @@ void DivPlatformGenesis::acquire_ymfm(short* bufL, short* bufR, size_t start, si for (size_t h=start; h=rate) { DivSample* s=parent->getSample(dacSample); if (s->samples>0) { if (!isMuted[5]) { @@ -175,7 +175,7 @@ void DivPlatformGenesis::acquire_ymfm(short* bufL, short* bufR, size_t start, si } } } - dacPeriod+=MAX(40,dacRate); + dacPeriod-=rate; } else { dacSample=-1; } @@ -394,11 +394,20 @@ void DivPlatformGenesis::tick(bool sysTick) { for (int i=0; i<6; i++) { if (i==2 && extMode) continue; if (chan[i].freqChanged) { - chan[i].freq=((chan[i].baseFreq&0xf800)|parent->calcFreq(chan[i].baseFreq&0x7ff,chan[i].pitch,false,4))+chan[i].std.pitch.val; + int fNum=parent->calcFreq(chan[i].baseFreq&0x7ff,chan[i].pitch,false,4)+chan[i].std.pitch.val; + int block=(chan[i].baseFreq&0xf800)>>11; + if (fNum<0) fNum=0; + if (fNum>2047) { + while (block<7) { + fNum>>=1; + block++; + } + if (fNum>2047) fNum=2047; + } + chan[i].freq=(block<<11)|fNum; if (chan[i].freq>0x3fff) chan[i].freq=0x3fff; - int freqt=chan[i].freq; - immWrite(chanOffs[i]+ADDR_FREQH,freqt>>8); - immWrite(chanOffs[i]+ADDR_FREQ,freqt&0xff); + immWrite(chanOffs[i]+ADDR_FREQH,chan[i].freq>>8); + immWrite(chanOffs[i]+ADDR_FREQ,chan[i].freq&0xff); if (chan[i].furnaceDac && dacMode) { double off=1.0; if (dacSample>=0 && dacSamplesong.sampleLen) { @@ -406,12 +415,13 @@ void DivPlatformGenesis::tick(bool sysTick) { if (s->centerRate<1) { off=1.0; } else { - off=8363.0/(double)s->centerRate; + off=(double)s->centerRate/8363.0; } } - dacRate=(1280000*1.25*off)/MAX(1,chan[i].baseFreq); + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,4)+chan[i].std.pitch.val;; + dacRate=chan[i].freq*off; if (dacRate<1) dacRate=1; - if (dumpWrites) addWrite(0xffff0001,1280000/dacRate); + if (dumpWrites) addWrite(0xffff0001,dacRate); } chan[i].freqChanged=false; } @@ -468,7 +478,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) { dacPos=0; dacPeriod=0; if (c.value!=DIV_NOTE_NULL) { - chan[c.chan].baseFreq=NOTE_FNUM_BLOCK(c.value,11); + chan[c.chan].baseFreq=parent->calcBaseFreq(1,1,c.value,false); chan[c.chan].freqChanged=true; } chan[c.chan].furnaceDac=true; @@ -487,7 +497,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) { } dacPos=0; dacPeriod=0; - dacRate=1280000/MAX(1,parent->getSample(dacSample)->rate); + dacRate=MAX(1,parent->getSample(dacSample)->rate); if (dumpWrites) addWrite(0xffff0001,parent->getSample(dacSample)->rate); chan[c.chan].furnaceDac=false; } @@ -615,6 +625,29 @@ int DivPlatformGenesis::dispatch(DivCommand c) { break; } case DIV_CMD_NOTE_PORTA: { + if (c.chan==5 && chan[c.chan].furnaceDac) { + int destFreq=parent->calcBaseFreq(1,1,c.value2,false); + bool return2=false; + if (destFreq>chan[c.chan].baseFreq) { + chan[c.chan].baseFreq+=c.value*16; + if (chan[c.chan].baseFreq>=destFreq) { + chan[c.chan].baseFreq=destFreq; + return2=true; + } + } else { + chan[c.chan].baseFreq-=c.value*16; + if (chan[c.chan].baseFreq<=destFreq) { + chan[c.chan].baseFreq=destFreq; + return2=true; + } + } + chan[c.chan].freqChanged=true; + if (return2) { + chan[c.chan].inPorta=false; + return 2; + } + break; + } int destFreq=NOTE_FNUM_BLOCK(c.value2,11); int newFreq; bool return2=false; @@ -669,7 +702,11 @@ int DivPlatformGenesis::dispatch(DivCommand c) { } break; case DIV_CMD_LEGATO: { - chan[c.chan].baseFreq=NOTE_FNUM_BLOCK(c.value,11); + if (c.chan==5 && chan[c.chan].furnaceDac) { + chan[c.chan].baseFreq=parent->calcBaseFreq(1,1,c.value,false); + } else { + chan[c.chan].baseFreq=NOTE_FNUM_BLOCK(c.value,11); + } chan[c.chan].note=c.value; chan[c.chan].freqChanged=true; break; diff --git a/src/engine/platform/genesisext.cpp b/src/engine/platform/genesisext.cpp index 479e42421..f146e2fc1 100644 --- a/src/engine/platform/genesisext.cpp +++ b/src/engine/platform/genesisext.cpp @@ -307,8 +307,18 @@ void DivPlatformGenesisExt::tick(bool sysTick) { unsigned char writeMask=2; if (extMode) for (int i=0; i<4; i++) { if (opChan[i].freqChanged) { - opChan[i].freq=(opChan[i].baseFreq&0xf800)|parent->calcFreq(opChan[i].baseFreq&0x7ff,opChan[i].pitch,false,4); - if (chan[i].freq>65535) chan[i].freq=65535; + int fNum=parent->calcFreq(opChan[i].baseFreq&0x7ff,opChan[i].pitch,false,4)+opChan[i].std.pitch.val; + int block=(opChan[i].baseFreq&0xf800)>>11; + if (fNum<0) fNum=0; + if (fNum>2047) { + while (block<7) { + fNum>>=1; + block++; + } + if (fNum>2047) fNum=2047; + } + opChan[i].freq=(block<<11)|fNum; + if (opChan[i].freq>0x3fff) opChan[i].freq=0x3fff; immWrite(opChanOffsH[i],opChan[i].freq>>8); immWrite(opChanOffsL[i],opChan[i].freq&0xff); } From bcade6931a45163096c59c04f4f38223d8600f0a Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 25 Apr 2022 01:42:26 -0500 Subject: [PATCH 192/342] YM2612: allow for DAC rate higher than 32KHz issue #391 --- src/engine/platform/genesis.cpp | 26 ++++++++++++++++++++++---- src/engine/platform/genesis.h | 2 ++ 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/src/engine/platform/genesis.cpp b/src/engine/platform/genesis.cpp index 324a66e0e..f2203fc31 100644 --- a/src/engine/platform/genesis.cpp +++ b/src/engine/platform/genesis.cpp @@ -86,14 +86,22 @@ void DivPlatformGenesis::acquire_nuked(short* bufL, short* bufR, size_t start, s static int os[2]; for (size_t h=start; h=rate) { + dacDelay-=rate; + dacReady=true; + } + } if (dacMode && dacSample!=-1) { dacPeriod+=dacRate; if (dacPeriod>=rate) { DivSample* s=parent->getSample(dacSample); if (s->samples>0) { if (!isMuted[5]) { - if (writes.size()<16) { + if (dacReady && writes.size()<16) { urgentWrite(0x2a,(unsigned char)s->data8[dacPos]+0x80); + dacReady=false; } } if (++dacPos>=s->samples) { @@ -106,7 +114,7 @@ void DivPlatformGenesis::acquire_nuked(short* bufL, short* bufR, size_t start, s } } } - dacPeriod-=rate; + while (dacPeriod>=rate) dacPeriod-=rate; } else { dacSample=-1; } @@ -155,14 +163,22 @@ void DivPlatformGenesis::acquire_ymfm(short* bufL, short* bufR, size_t start, si static int os[2]; for (size_t h=start; h=rate) { + dacDelay-=rate; + dacReady=true; + } + } if (dacMode && dacSample!=-1) { dacPeriod+=dacRate; if (dacPeriod>=rate) { DivSample* s=parent->getSample(dacSample); if (s->samples>0) { if (!isMuted[5]) { - if (writes.size()<16) { + if (dacReady && writes.size()<16) { urgentWrite(0x2a,(unsigned char)s->data8[dacPos]+0x80); + dacReady=false; } } if (++dacPos>=s->samples) { @@ -175,7 +191,7 @@ void DivPlatformGenesis::acquire_ymfm(short* bufL, short* bufR, size_t start, si } } } - dacPeriod-=rate; + while (dacPeriod>=rate) dacPeriod-=rate; } else { dacSample=-1; } @@ -858,6 +874,8 @@ void DivPlatformGenesis::reset() { dacPeriod=0; dacPos=0; dacRate=0; + dacDelay=0; + dacReady=true; dacSample=-1; sampleBank=0; lfoValue=8; diff --git a/src/engine/platform/genesis.h b/src/engine/platform/genesis.h index b94303066..aa7659fa3 100644 --- a/src/engine/platform/genesis.h +++ b/src/engine/platform/genesis.h @@ -85,6 +85,8 @@ class DivPlatformGenesis: public DivDispatch { int dacRate; unsigned int dacPos; int dacSample; + int dacDelay; + bool dacReady; unsigned char sampleBank; unsigned char lfoValue; From ed6820ac46df214cef057ae666bfe975b145263d Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 25 Apr 2022 11:23:44 -0500 Subject: [PATCH 193/342] GUI: fix cursor going out of bounds on paste --- src/gui/editing.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/gui/editing.cpp b/src/gui/editing.cpp index 106ec61b1..e5874f91a 100644 --- a/src/gui/editing.cpp +++ b/src/gui/editing.cpp @@ -554,6 +554,7 @@ void FurnaceGUI::doPaste(PasteMode mode) { } if (settings.cursorPastePos) { cursor.y=j; + if (cursor.y>=e->song.patLen) cursor.y=e->song.patLen-1; updateScroll(cursor.y); } @@ -1003,4 +1004,4 @@ void FurnaceGUI::doRedo() { } redoHist.pop_back(); -} \ No newline at end of file +} From 17cf657f6acb3aa323c6267def47cec0bb6cacfd Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 25 Apr 2022 11:52:05 -0500 Subject: [PATCH 194/342] one more pitch calc fix --- src/engine/engine.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 3bfcb54c5..c56eff9c6 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -894,7 +894,12 @@ unsigned short DivEngine::calcBaseFreqFNumBlock(double clock, double divider, in if (block>7) block=7; bf>>=block; if (bf<0) bf=0; - if (bf>1288) { // apparently octave boundary + // octave boundaries + while (bf>0 && bf<644 && block>0) { + bf<<=1; + block--; + } + if (bf>1288) { while (block<7) { bf>>=1; block++; From 1b97d3912bcd7872da78ac8b47779ee54c821c01 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 25 Apr 2022 11:59:02 -0500 Subject: [PATCH 195/342] YM2612: hopefully the final pitch slide fix --- src/engine/platform/genesis.cpp | 6 +++--- src/engine/platform/genesisext.cpp | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/engine/platform/genesis.cpp b/src/engine/platform/genesis.cpp index f2203fc31..72dc9c0fb 100644 --- a/src/engine/platform/genesis.cpp +++ b/src/engine/platform/genesis.cpp @@ -434,7 +434,7 @@ void DivPlatformGenesis::tick(bool sysTick) { off=(double)s->centerRate/8363.0; } } - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,4)+chan[i].std.pitch.val;; + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,4)+chan[i].std.pitch.val; dacRate=chan[i].freq*off; if (dacRate<1) dacRate=1; if (dumpWrites) addWrite(0xffff0001,dacRate); @@ -686,12 +686,12 @@ int DivPlatformGenesis::dispatch(DivCommand c) { // check for octave boundary // what the heck! if (!chan[c.chan].portaPause) { - if ((newFreq&0x7ff)>1288) { + if ((newFreq&0x7ff)>1288 && (newFreq&0xf800)<0x3800) { chan[c.chan].portaPauseFreq=(644)|((newFreq+0x800)&0xf800); chan[c.chan].portaPause=true; break; } - if ((newFreq&0x7ff)<644) { + if ((newFreq&0x7ff)<644 && (newFreq&0xf800)>0) { chan[c.chan].portaPauseFreq=newFreq=(1287)|((newFreq-0x800)&0xf800); chan[c.chan].portaPause=true; break; diff --git a/src/engine/platform/genesisext.cpp b/src/engine/platform/genesisext.cpp index f146e2fc1..59b77587b 100644 --- a/src/engine/platform/genesisext.cpp +++ b/src/engine/platform/genesisext.cpp @@ -148,7 +148,7 @@ int DivPlatformGenesisExt::dispatch(DivCommand c) { } // what the heck! if (!opChan[ch].portaPause) { - if ((newFreq&0x7ff)>1288) { + if ((newFreq&0x7ff)>1288 && (newFreq&0xf800)<0x3800) { if (parent->song.fbPortaPause) { opChan[ch].portaPauseFreq=(644)|((newFreq+0x800)&0xf800); opChan[ch].portaPause=true; @@ -157,7 +157,7 @@ int DivPlatformGenesisExt::dispatch(DivCommand c) { newFreq=(newFreq>>1)|((newFreq+0x800)&0xf800); } } - if ((newFreq&0x7ff)<644) { + if ((newFreq&0x7ff)<644 && (newFreq&0xf800)>0) { if (parent->song.fbPortaPause) { opChan[ch].portaPauseFreq=newFreq=(1287)|((newFreq-0x800)&0xf800); opChan[ch].portaPause=true; From 7b79c3225823be4a21508543c016d0bcbd632168 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 25 Apr 2022 14:58:58 -0500 Subject: [PATCH 196/342] increase range of arp macro to 120 --- src/gui/insEdit.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index 7879d966e..a3368e92f 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -2836,7 +2836,7 @@ void FurnaceGUI::drawInsEdit() { if (volMax>0) { NORMAL_MACRO(ins->std.volMacro,volMin,volMax,"vol",volumeLabel,160,ins->std.volMacro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_VOLUME],mmlString[0],volMin,volMax,NULL,false); } - NORMAL_MACRO(ins->std.arpMacro,arpMacroScroll,arpMacroScroll+24,"arp","Arpeggio",160,ins->std.arpMacro.open,false,NULL,true,&arpMacroScroll,(arpMode?-60:-80),70,0,0,true,1,macroAbsoluteMode,uiColors[GUI_COLOR_MACRO_PITCH],mmlString[1],-92,94,(ins->std.arpMacro.mode?(¯oHoverNote):NULL),true); + NORMAL_MACRO(ins->std.arpMacro,arpMacroScroll,arpMacroScroll+24,"arp","Arpeggio",160,ins->std.arpMacro.open,false,NULL,true,&arpMacroScroll,(arpMode?-60:-120),120,0,0,true,1,macroAbsoluteMode,uiColors[GUI_COLOR_MACRO_PITCH],mmlString[1],-120,120,(ins->std.arpMacro.mode?(¯oHoverNote):NULL),true); if (dutyMax>0) { if (ins->type==DIV_INS_MIKEY) { NORMAL_MACRO(ins->std.dutyMacro,0,dutyMax,"duty",dutyLabel,160,ins->std.dutyMacro.open,true,mikeyFeedbackBits,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[2],0,dutyMax,NULL,false); From f3f92b80352f4226a179b31860cee488ddbe911a Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 25 Apr 2022 15:07:36 -0500 Subject: [PATCH 197/342] GUI: deprecate compat rate --- src/gui/sampleEdit.cpp | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/src/gui/sampleEdit.cpp b/src/gui/sampleEdit.cpp index 802e10bc5..53379c992 100644 --- a/src/gui/sampleEdit.cpp +++ b/src/gui/sampleEdit.cpp @@ -74,15 +74,6 @@ void FurnaceGUI::drawSampleEdit() { ImGui::EndCombo(); } - ImGui::TableNextColumn(); - ImGui::Text("Rate (Hz)"); - ImGui::SameLine(); - ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); - if (ImGui::InputInt("##SampleRate",&sample->rate,10,200)) { MARK_MODIFIED - if (sample->rate<100) sample->rate=100; - if (sample->rate>96000) sample->rate=96000; - } - ImGui::TableNextColumn(); ImGui::Text("C-4 (Hz)"); ImGui::SameLine(); @@ -92,6 +83,15 @@ void FurnaceGUI::drawSampleEdit() { if (sample->centerRate>65535) sample->centerRate=65535; } + ImGui::TableNextColumn(); + ImGui::Text("Compat Rate"); + ImGui::SameLine(); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + if (ImGui::InputInt("##SampleRate",&sample->rate,10,200)) { MARK_MODIFIED + if (sample->rate<100) sample->rate=100; + if (sample->rate>96000) sample->rate=96000; + } + ImGui::TableNextColumn(); bool doLoop=(sample->loopStart>=0); if (ImGui::Checkbox("Loop",&doLoop)) { MARK_MODIFIED @@ -587,16 +587,6 @@ void FurnaceGUI::drawSampleEdit() { ImGui::EndCombo(); } - ImGui::TableNextColumn(); - ImGui::Text("Rate (Hz)"); - ImGui::SameLine(); - ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); - if (ImGui::InputInt("##SampleRate",&sample->rate,10,200)) { MARK_MODIFIED - if (sample->rate<100) sample->rate=100; - if (sample->rate>96000) sample->rate=96000; - } - - ImGui::TableNextRow(); ImGui::TableNextColumn(); ImGui::Text("C-4 (Hz)"); ImGui::SameLine(); @@ -606,6 +596,16 @@ void FurnaceGUI::drawSampleEdit() { if (sample->centerRate>65535) sample->centerRate=65535; } + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::Text("Compat Rate"); + ImGui::SameLine(); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + if (ImGui::InputInt("##SampleRate",&sample->rate,10,200)) { MARK_MODIFIED + if (sample->rate<100) sample->rate=100; + if (sample->rate>96000) sample->rate=96000; + } + ImGui::TableNextColumn(); bool doLoop=(sample->loopStart>=0); if (ImGui::Checkbox("Loop",&doLoop)) { MARK_MODIFIED From 5821135b38c916ffdb088fedc06836e3476e5057 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 25 Apr 2022 15:54:31 -0500 Subject: [PATCH 198/342] don't return 1 on -help and -version fixes #385 --- src/main.cpp | 67 ++++++++++++++++++++++++++++---------------------- src/ta-utils.h | 10 ++++++-- 2 files changed, 46 insertions(+), 31 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 4a3e9d0e2..5db37c305 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -60,7 +60,7 @@ bool displayEngineFailError=false; std::vector params; -bool pHelp(String) { +TAParamResult pHelp(String) { printf("usage: furnace [params] [filename]\n" "you may specify the following parameters:\n"); for (auto& i: params) { @@ -70,13 +70,13 @@ bool pHelp(String) { printf(" -%s: %s\n",i.name.c_str(),i.desc.c_str()); } } - return false; + return TA_PARAM_QUIT; } -bool pAudio(String val) { +TAParamResult pAudio(String val) { if (outName!="") { logE("can't use -audio and -output at the same time."); - return false; + return TA_PARAM_ERROR; } if (val=="jack") { e.setAudio(DIV_AUDIO_JACK); @@ -84,12 +84,12 @@ bool pAudio(String val) { e.setAudio(DIV_AUDIO_SDL); } else { logE("invalid value for audio engine! valid values are: jack, sdl."); - return false; + return TA_PARAM_ERROR; } - return true; + return TA_PARAM_SUCCESS; } -bool pView(String val) { +TAParamResult pView(String val) { if (val=="pattern") { e.setView(DIV_STATUS_PATTERN); } else if (val=="commands") { @@ -98,17 +98,17 @@ bool pView(String val) { e.setView(DIV_STATUS_NOTHING); } else { logE("invalid value for view type! valid values are: pattern, commands, nothing."); - return false; + return TA_PARAM_ERROR; } - return true; + return TA_PARAM_SUCCESS; } -bool pConsole(String val) { +TAParamResult pConsole(String val) { consoleMode=true; - return true; + return TA_PARAM_SUCCESS; } -bool pLogLevel(String val) { +TAParamResult pLogLevel(String val) { if (val=="trace") { logLevel=LOGLEVEL_TRACE; } else if (val=="debug") { @@ -121,12 +121,12 @@ bool pLogLevel(String val) { logLevel=LOGLEVEL_ERROR; } else { logE("invalid value for loglevel! valid values are: trace, debug, info, warning, error."); - return false; + return TA_PARAM_ERROR; } - return true; + return TA_PARAM_SUCCESS; } -bool pVersion(String) { +TAParamResult pVersion(String) { printf("Furnace version " DIV_VERSION ".\n\n"); printf("copyright (C) 2021-2022 tildearrow and contributors.\n"); printf("licensed under the GNU General Public License version 2 or later\n"); @@ -152,10 +152,10 @@ bool pVersion(String) { printf("- puNES by FHorse (GPLv2)\n"); printf("- reSID by Dag Lem (GPLv2)\n"); printf("- Stella by Stella Team (GPLv2)\n"); - return false; + return TA_PARAM_QUIT; } -bool pWarranty(String) { +TAParamResult pWarranty(String) { printf("This program is free software; you can redistribute it and/or\n" "modify it under the terms of the GNU General Public License\n" "as published by the Free Software Foundation; either version 2\n" @@ -169,10 +169,10 @@ bool pWarranty(String) { "You should have received a copy of the GNU General Public License\n" "along with this program; if not, write to the Free Software\n" "Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n"); - return false; + return TA_PARAM_QUIT; } -bool pLoops(String val) { +TAParamResult pLoops(String val) { try { int count=std::stoi(val); if (count<0) { @@ -182,12 +182,12 @@ bool pLoops(String val) { } } catch (std::exception& e) { logE("loop count shall be a number."); - return false; + return TA_PARAM_ERROR; } - return true; + return TA_PARAM_SUCCESS; } -bool pOutMode(String val) { +TAParamResult pOutMode(String val) { if (val=="one") { outMode=DIV_EXPORT_MODE_ONE; } else if (val=="persys") { @@ -196,21 +196,21 @@ bool pOutMode(String val) { outMode=DIV_EXPORT_MODE_MANY_CHAN; } else { logE("invalid value for outmode! valid values are: one, persys and perchan."); - return false; + return TA_PARAM_ERROR; } - return true; + return TA_PARAM_SUCCESS; } -bool pOutput(String val) { +TAParamResult pOutput(String val) { outName=val; e.setAudio(DIV_AUDIO_DUMMY); - return true; + return TA_PARAM_SUCCESS; } -bool pVGMOut(String val) { +TAParamResult pVGMOut(String val) { vgmOutName=val; e.setAudio(DIV_AUDIO_DUMMY); - return true; + return TA_PARAM_SUCCESS; } bool needsValue(String param) { @@ -283,7 +283,16 @@ int main(int argc, char** argv) { } for (size_t j=0; j Date: Mon, 25 Apr 2022 16:46:38 -0500 Subject: [PATCH 199/342] GUI: add two ImGui settings --- src/gui/gui.h | 4 ++++ src/gui/settings.cpp | 24 ++++++++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/src/gui/gui.h b/src/gui/gui.h index 8c850fd06..98a861127 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -842,6 +842,8 @@ class FurnaceGUI { int notePreviewBehavior; int powerSave; int absorbInsInput; + int eventDelay; + int moveWindowTitle; unsigned int maxUndoSteps; String mainFontPath; String patFontPath; @@ -917,6 +919,8 @@ class FurnaceGUI { notePreviewBehavior(1), powerSave(1), absorbInsInput(0), + eventDelay(0), + moveWindowTitle(0), maxUndoSteps(100), mainFontPath(""), patFontPath(""), diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index c7a235113..61493722c 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -313,6 +313,21 @@ void FurnaceGUI::drawSettings() { settings.sysFileDialog=sysFileDialogB; } + bool moveWindowTitleB=settings.moveWindowTitle; + if (ImGui::Checkbox("Only allow window movement when clicking on title bar",&moveWindowTitleB)) { + settings.moveWindowTitle=moveWindowTitleB; + applyUISettings(false); + } + + bool eventDelayB=settings.eventDelay; + if (ImGui::Checkbox("Enable event delay",&eventDelayB)) { + settings.eventDelay=eventDelayB; + applyUISettings(false); + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("may cause issues with high-polling-rate mice when previewing notes."); + } + bool powerSaveB=settings.powerSave; if (ImGui::Checkbox("Power-saving mode",&powerSaveB)) { settings.powerSave=powerSaveB; @@ -1651,6 +1666,8 @@ void FurnaceGUI::syncSettings() { settings.notePreviewBehavior=e->getConfInt("notePreviewBehavior",1); settings.powerSave=e->getConfInt("powerSave",POWER_SAVE_DEFAULT); settings.absorbInsInput=e->getConfInt("absorbInsInput",0); + settings.eventDelay=e->getConfInt("eventDelay",0); + settings.moveWindowTitle=e->getConfInt("moveWindowTitle",0); clampSetting(settings.mainFontSize,2,96); clampSetting(settings.patFontSize,2,96); @@ -1715,6 +1732,8 @@ void FurnaceGUI::syncSettings() { clampSetting(settings.notePreviewBehavior,0,3); clampSetting(settings.powerSave,0,1); clampSetting(settings.absorbInsInput,0,1); + clampSetting(settings.eventDelay,0,1); + clampSetting(settings.moveWindowTitle,0,1); // keybinds for (int i=0; isetConf("notePreviewBehavior",settings.notePreviewBehavior); e->setConf("powerSave",settings.powerSave); e->setConf("absorbInsInput",settings.absorbInsInput); + e->setConf("eventDelay",settings.eventDelay); + e->setConf("moveWindowTitle",settings.moveWindowTitle); // colors for (int i=0; i Date: Mon, 25 Apr 2022 17:55:48 -0500 Subject: [PATCH 200/342] dev87 - C64 changes issue #335 --- papers/format.md | 3 +++ src/engine/engine.h | 4 ++-- src/engine/fileOps.cpp | 30 ++++++++++++++++++++++++++---- src/engine/fileOpsIns.cpp | 10 ++++++++++ src/engine/instrument.cpp | 8 ++++++++ src/engine/platform/c64.cpp | 4 ++-- src/gui/insEdit.cpp | 12 ++++++------ 7 files changed, 57 insertions(+), 14 deletions(-) diff --git a/papers/format.md b/papers/format.md index 207b232aa..083fd7662 100644 --- a/papers/format.md +++ b/papers/format.md @@ -29,6 +29,7 @@ furthermore, an `or reserved` indicates this field is always present, but is res the format versions are: +- 87: Furnace dev87 - 86: Furnace dev86 - 85: Furnace dev85 - 84: Furnace dev84 @@ -423,9 +424,11 @@ size | description 1 | reserved (>=17) or duty macro height (>=15) or reserved 1 | reserved (>=17) or wave macro height (>=15) or reserved 4?? | volume macro + | - before version 87, if this is the C64 relative cutoff macro, its values were stored offset by 18. 4?? | arp macro | - before version 31, this macro's values were stored offset by 12. 4?? | duty macro + | - before version 87, if this is the C64 relative duty macro, its values were stored offset by 12. 4?? | wave macro 4?? | pitch macro (>=17) 4?? | extra 1 macro (>=17) diff --git a/src/engine/engine.h b/src/engine/engine.h index 57acf970d..212bf4228 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -43,8 +43,8 @@ #define BUSY_BEGIN_SOFT softLocked=true; isBusy.lock(); #define BUSY_END isBusy.unlock(); softLocked=false; -#define DIV_VERSION "dev86" -#define DIV_ENGINE_VERSION 86 +#define DIV_VERSION "dev87" +#define DIV_ENGINE_VERSION 87 // for imports #define DIV_VERSION_MOD 0xff01 diff --git a/src/engine/fileOps.cpp b/src/engine/fileOps.cpp index 38b33502b..ca2f2d10e 100644 --- a/src/engine/fileOps.cpp +++ b/src/engine/fileOps.cpp @@ -501,9 +501,9 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) { } else { ins->std.dutyMacro.val[j]=reader.readI(); } - if ((ds.system[0]==DIV_SYSTEM_C64_8580 || ds.system[0]==DIV_SYSTEM_C64_6581) && ins->std.dutyMacro.val[j]>24) { + /*if ((ds.system[0]==DIV_SYSTEM_C64_8580 || ds.system[0]==DIV_SYSTEM_C64_6581) && ins->std.dutyMacro.val[j]>24) { ins->std.dutyMacro.val[j]=24; - } + }*/ } if (ins->std.dutyMacro.len>0) { ins->std.dutyMacro.open=true; @@ -556,6 +556,16 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) { ins->c64.bp=reader.readC(); ins->c64.lp=reader.readC(); ins->c64.ch3off=reader.readC(); + + // weird storage + if (ins->c64.volIsCutoff) { + for (int j=0; jstd.volMacro.len; j++) { + ins->std.volMacro.val[j]-=18; + } + } + for (int j=0; jstd.dutyMacro.len; j++) { + ins->std.dutyMacro.val[j]-=12; + } } if (ds.system[0]==DIV_SYSTEM_GB && ds.version>0x11) { @@ -2609,7 +2619,13 @@ SafeWriter* DivEngine::saveDMF(unsigned char version) { } else { // STD if (sys!=DIV_SYSTEM_GB) { w->writeC(i->std.volMacro.len); - w->write(i->std.volMacro.val,4*i->std.volMacro.len); + if ((sys==DIV_SYSTEM_C64_6581 || sys==DIV_SYSTEM_C64_8580) && i->c64.volIsCutoff) { + for (int j=0; jstd.volMacro.len; j++) { + w->writeI(i->std.volMacro.val[j]+18); + } + } else { + w->write(i->std.volMacro.val,4*i->std.volMacro.len); + } if (i->std.volMacro.len>0) { w->writeC(i->std.volMacro.loop); } @@ -2629,7 +2645,13 @@ SafeWriter* DivEngine::saveDMF(unsigned char version) { w->writeC(i->std.arpMacro.mode); w->writeC(i->std.dutyMacro.len); - w->write(i->std.dutyMacro.val,4*i->std.dutyMacro.len); + if (sys==DIV_SYSTEM_C64_6581 || sys==DIV_SYSTEM_C64_8580) { + for (int j=0; jstd.dutyMacro.len; j++) { + w->writeI(i->std.dutyMacro.val[j]+12); + } + } else { + w->write(i->std.dutyMacro.val,4*i->std.dutyMacro.len); + } if (i->std.dutyMacro.len>0) { w->writeC(i->std.dutyMacro.loop); } diff --git a/src/engine/fileOpsIns.cpp b/src/engine/fileOpsIns.cpp index e080b7797..1173d59a0 100644 --- a/src/engine/fileOpsIns.cpp +++ b/src/engine/fileOpsIns.cpp @@ -345,6 +345,16 @@ void DivEngine::loadDMP(SafeReader& reader, std::vector& ret, St ins->c64.bp=reader.readC(); ins->c64.lp=reader.readC(); ins->c64.ch3off=reader.readC(); + + // weird storage + if (ins->c64.volIsCutoff) { + for (int j=0; jstd.volMacro.len; j++) { + ins->std.volMacro.val[j]-=18; + } + } + for (int j=0; jstd.dutyMacro.len; j++) { + ins->std.dutyMacro.val[j]-=12; + } } if (ins->type==DIV_INS_GB) { ins->gb.envVol=reader.readC(); diff --git a/src/engine/instrument.cpp b/src/engine/instrument.cpp index 273af9bf7..d5061e0b7 100644 --- a/src/engine/instrument.cpp +++ b/src/engine/instrument.cpp @@ -639,6 +639,14 @@ DivDataErrors DivInstrument::readInsData(SafeReader& reader, short version) { std.arpMacro.val[j]-=12; } } + if (type==DIV_INS_C64 && version<87) { + if (c64.volIsCutoff && !c64.filterIsAbs) for (int j=0; j=17) { reader.read(std.pitchMacro.val,4*std.pitchMacro.len); reader.read(std.ex1Macro.val,4*std.ex1Macro.len); diff --git a/src/engine/platform/c64.cpp b/src/engine/platform/c64.cpp index 604dcfe33..79cfb42ba 100644 --- a/src/engine/platform/c64.cpp +++ b/src/engine/platform/c64.cpp @@ -131,7 +131,7 @@ void DivPlatformC64::tick(bool sysTick) { if (ins->c64.filterIsAbs) { filtCut=MIN(2047,chan[i].std.vol.val); } else { - filtCut-=((signed char)chan[i].std.vol.val-18)*7; + filtCut-=((signed char)chan[i].std.vol.val)*7; if (filtCut>2047) filtCut=2047; if (filtCut<0) filtCut=0; } @@ -161,7 +161,7 @@ void DivPlatformC64::tick(bool sysTick) { if (ins->c64.dutyIsAbs) { chan[i].duty=chan[i].std.duty.val; } else { - chan[i].duty-=((signed char)chan[i].std.duty.val-12)*4; + chan[i].duty-=((signed char)chan[i].std.duty.val)*4; } rWrite(i*7+2,chan[i].duty&0xff); rWrite(i*7+3,chan[i].duty>>8); diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index a3368e92f..77781f9dd 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -2687,8 +2687,8 @@ void FurnaceGUI::drawInsEdit() { if (ins->c64.filterIsAbs) { volMax=2047; } else { - volMin=-18; - volMax=18; + volMin=-64; + volMax=64; } } } @@ -2717,13 +2717,15 @@ void FurnaceGUI::drawInsEdit() { bool arpMode=ins->std.arpMacro.mode; const char* dutyLabel="Duty/Noise"; + int dutyMin=0; int dutyMax=3; if (ins->type==DIV_INS_C64) { dutyLabel="Duty"; if (ins->c64.dutyIsAbs) { dutyMax=4095; } else { - dutyMax=24; + dutyMin=-96; + dutyMax=96; } } if (ins->type==DIV_INS_FM) { @@ -2840,10 +2842,8 @@ void FurnaceGUI::drawInsEdit() { if (dutyMax>0) { if (ins->type==DIV_INS_MIKEY) { NORMAL_MACRO(ins->std.dutyMacro,0,dutyMax,"duty",dutyLabel,160,ins->std.dutyMacro.open,true,mikeyFeedbackBits,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[2],0,dutyMax,NULL,false); - } else if (ins->type==DIV_INS_C64) { - NORMAL_MACRO(ins->std.dutyMacro,0,dutyMax,"duty",dutyLabel,160,ins->std.dutyMacro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[2],0,dutyMax,NULL,false); } else { - NORMAL_MACRO(ins->std.dutyMacro,0,dutyMax,"duty",dutyLabel,160,ins->std.dutyMacro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[2],0,dutyMax,NULL,false); + NORMAL_MACRO(ins->std.dutyMacro,dutyMin,dutyMax,"duty",dutyLabel,160,ins->std.dutyMacro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[2],dutyMin,dutyMax,NULL,false); } } if (waveMax>0) { From 3e1c2197ebf1f66f4396e57108f43d4f92a7cdfd Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 25 Apr 2022 18:04:13 -0500 Subject: [PATCH 201/342] OPL: fix #369 --- src/engine/platform/opl.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/engine/platform/opl.cpp b/src/engine/platform/opl.cpp index ef9adecfc..fac157da3 100644 --- a/src/engine/platform/opl.cpp +++ b/src/engine/platform/opl.cpp @@ -548,6 +548,9 @@ int DivPlatformOPL::dispatch(DivCommand c) { if (chan[c.chan].insChanged) { int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2; chan[c.chan].fourOp=(ops==4); + if (chan[c.chan].fourOp) { + chan[c.chan+1].std.init(NULL); + } update4OpMask=true; for (int i=0; i Date: Mon, 25 Apr 2022 18:07:49 -0500 Subject: [PATCH 202/342] WonderSwan: fix noise phase reset --- src/engine/platform/swan.cpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/engine/platform/swan.cpp b/src/engine/platform/swan.cpp index b8f9a40dc..86ad9b109 100644 --- a/src/engine/platform/swan.cpp +++ b/src/engine/platform/swan.cpp @@ -226,12 +226,14 @@ void DivPlatformSwan::tick(bool sysTick) { } } if (chan[3].std.duty.had) { - noise=chan[3].std.duty.val; - if (noise>0) { - rWrite(0x0e,((noise-1)&0x07)|0x18); - sndCtrl|=0x80; - } else { - sndCtrl&=~0x80; + if (noise!=chan[3].std.duty.val) { + noise=chan[3].std.duty.val; + if (noise>0) { + rWrite(0x0e,((noise-1)&0x07)|0x18); + sndCtrl|=0x80; + } else { + sndCtrl&=~0x80; + } } } rWrite(0x10,sndCtrl); From b3f1935f2b3f29f2188a6fa8a8f80a0522b9664d Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 25 Apr 2022 18:23:12 -0500 Subject: [PATCH 203/342] GUI: prepare for more UX improvements - add "temporary instrument" to DivEngine - prepare for two things: - instrument preview on file picker (at least built-in one) - instrument selector for banks (#364) --- src/engine/engine.cpp | 10 ++++++++++ src/engine/engine.h | 5 +++++ src/gui/gui.cpp | 2 +- 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index c56eff9c6..f77ab6350 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -739,6 +739,7 @@ String DivEngine::getWarnings() { } DivInstrument* DivEngine::getIns(int index, DivInstrumentType fallbackType) { + if (index==-2 && tempIns!=NULL) return tempIns; if (index<0 || index>=song.insLen) { switch (fallbackType) { case DIV_INS_OPLL: @@ -1363,6 +1364,15 @@ int DivEngine::addInstrumentPtr(DivInstrument* which) { return song.insLen; } +void DivEngine::loadTempIns(DivInstrument* which) { + BUSY_BEGIN; + if (tempIns==NULL) { + tempIns=new DivInstrument; + } + memcpy(tempIns,which,sizeof(DivInstrument)); + BUSY_END; +} + void DivEngine::delInstrument(int index) { BUSY_BEGIN; saveLock.lock(); diff --git a/src/engine/engine.h b/src/engine/engine.h index 212bf4228..5bbb1060e 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -302,6 +302,7 @@ class DivEngine { public: DivSong song; + DivInstrument* tempIns; DivSystem sysOfChan[DIV_MAX_CHANS]; int dispatchOfChan[DIV_MAX_CHANS]; int dispatchChanOfChan[DIV_MAX_CHANS]; @@ -525,6 +526,9 @@ class DivEngine { // if the returned vector is empty then there was an error. std::vector instrumentFromFile(const char* path); + // load temporary instrument + void loadTempIns(DivInstrument* which); + // delete instrument void delInstrument(int index); @@ -798,6 +802,7 @@ class DivEngine { metroAmp(0.0f), metroVol(1.0f), totalProcessed(0), + tempIns(NULL), oscBuf{NULL,NULL}, oscSize(1), oscReadPos(0), diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index a3c413179..26e414e05 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -2419,7 +2419,7 @@ bool FurnaceGUI::loop() { } } - wantCaptureKeyboard=ImGui::GetIO().WantCaptureKeyboard; + wantCaptureKeyboard=ImGui::GetIO().WantTextInput; while (true) { midiLock.lock(); From 663e724111242d4734d76506091188b2545b4bfc Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 25 Apr 2022 18:41:24 -0500 Subject: [PATCH 204/342] I KNEW IT --- src/engine/engine.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index f77ab6350..4821df3a7 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -1369,7 +1369,7 @@ void DivEngine::loadTempIns(DivInstrument* which) { if (tempIns==NULL) { tempIns=new DivInstrument; } - memcpy(tempIns,which,sizeof(DivInstrument)); + *tempIns=*which; BUSY_END; } From e8c9b645c53781cfe8863d70e14cb35944cabe48 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 25 Apr 2022 18:58:17 -0500 Subject: [PATCH 205/342] GUI: implement instrument preview in file dialog SADLY it's just for the built-in file dialog but i'll think of a solution for the external one --- extern/igfd/ImGuiFileDialog.cpp | 39 ++++++++++++++++++++++---------- extern/igfd/ImGuiFileDialog.h | 26 ++++++++++++++------- src/engine/engine.cpp | 6 +++-- src/engine/platform/amiga.h | 2 +- src/engine/platform/arcade.h | 2 +- src/engine/platform/ay.h | 3 ++- src/engine/platform/ay8930.h | 3 ++- src/engine/platform/bubsyswsg.h | 3 +-- src/engine/platform/c64.h | 4 ++-- src/engine/platform/fds.h | 4 ++-- src/engine/platform/gb.h | 4 ++-- src/engine/platform/genesis.h | 2 +- src/engine/platform/genesisext.h | 3 +-- src/engine/platform/lynx.h | 4 ++-- src/engine/platform/mmc5.h | 4 ++-- src/engine/platform/nes.h | 4 ++-- src/engine/platform/opl.h | 3 +-- src/engine/platform/opll.h | 3 +-- src/engine/platform/pce.h | 4 ++-- src/engine/platform/pcspkr.h | 4 ++-- src/engine/platform/pet.h | 3 +-- src/engine/platform/qsound.h | 3 +-- src/engine/platform/saa.h | 4 ++-- src/engine/platform/segapcm.h | 3 +-- src/engine/platform/sms.h | 3 +-- src/engine/platform/swan.h | 4 ++-- src/engine/platform/tia.h | 4 ++-- src/engine/platform/tx81z.h | 3 +-- src/engine/platform/vera.h | 4 ++-- src/engine/platform/vic20.h | 4 ++-- src/engine/platform/vrc6.h | 4 ++-- src/engine/platform/ym2610.h | 4 ++-- src/engine/platform/ym2610b.h | 4 ++-- src/engine/platform/ym2610bext.h | 3 +-- src/engine/platform/ym2610ext.h | 3 +-- src/gui/fileDialog.cpp | 4 ++-- src/gui/fileDialog.h | 4 +++- src/gui/gui.cpp | 10 +++++++- 38 files changed, 113 insertions(+), 85 deletions(-) diff --git a/extern/igfd/ImGuiFileDialog.cpp b/extern/igfd/ImGuiFileDialog.cpp index 3980bcd3e..98d6c909a 100644 --- a/extern/igfd/ImGuiFileDialog.cpp +++ b/extern/igfd/ImGuiFileDialog.cpp @@ -3295,7 +3295,8 @@ namespace IGFD const std::string& vFileName, const int& vCountSelectionMax, UserDatas vUserDatas, - ImGuiFileDialogFlags vFlags) + ImGuiFileDialogFlags vFlags, + SelectFun vSelectFun) { if (prFileDialogInternal.puShowDialog) // if already opened, quit return; @@ -3306,6 +3307,7 @@ namespace IGFD prFileDialogInternal.puDLGtitle = vTitle; prFileDialogInternal.puDLGuserDatas = vUserDatas; prFileDialogInternal.puDLGflags = vFlags; + prFileDialogInternal.puDLGselFun = vSelectFun; prFileDialogInternal.puDLGoptionsPane = nullptr; prFileDialogInternal.puDLGoptionsPaneWidth = 0.0f; prFileDialogInternal.puDLGmodal = false; @@ -3335,7 +3337,8 @@ namespace IGFD const std::string& vFilePathName, const int& vCountSelectionMax, UserDatas vUserDatas, - ImGuiFileDialogFlags vFlags) + ImGuiFileDialogFlags vFlags, + SelectFun vSelectFun) { if (prFileDialogInternal.puShowDialog) // if already opened, quit return; @@ -3348,6 +3351,7 @@ namespace IGFD prFileDialogInternal.puDLGoptionsPaneWidth = 0.0f; prFileDialogInternal.puDLGuserDatas = vUserDatas; prFileDialogInternal.puDLGflags = vFlags; + prFileDialogInternal.puDLGselFun = vSelectFun; prFileDialogInternal.puDLGmodal = false; auto ps = IGFD::Utils::ParsePathFileName(vFilePathName); @@ -3390,7 +3394,8 @@ namespace IGFD const float& vSidePaneWidth, const int& vCountSelectionMax, UserDatas vUserDatas, - ImGuiFileDialogFlags vFlags) + ImGuiFileDialogFlags vFlags, + SelectFun vSelectFun) { if (prFileDialogInternal.puShowDialog) // if already opened, quit return; @@ -3401,6 +3406,7 @@ namespace IGFD prFileDialogInternal.puDLGtitle = vTitle; prFileDialogInternal.puDLGuserDatas = vUserDatas; prFileDialogInternal.puDLGflags = vFlags; + prFileDialogInternal.puDLGselFun = vSelectFun; prFileDialogInternal.puDLGoptionsPane = vSidePane; prFileDialogInternal.puDLGoptionsPaneWidth = vSidePaneWidth; prFileDialogInternal.puDLGmodal = false; @@ -3435,7 +3441,8 @@ namespace IGFD const float& vSidePaneWidth, const int& vCountSelectionMax, UserDatas vUserDatas, - ImGuiFileDialogFlags vFlags) + ImGuiFileDialogFlags vFlags, + SelectFun vSelectFun) { if (prFileDialogInternal.puShowDialog) // if already opened, quit return; @@ -3448,6 +3455,7 @@ namespace IGFD prFileDialogInternal.puDLGoptionsPaneWidth = vSidePaneWidth; prFileDialogInternal.puDLGuserDatas = vUserDatas; prFileDialogInternal.puDLGflags = vFlags; + prFileDialogInternal.puDLGselFun = vSelectFun; prFileDialogInternal.puDLGmodal = false; auto ps = IGFD::Utils::ParsePathFileName(vFilePathName); @@ -3489,7 +3497,8 @@ namespace IGFD const std::string& vFileName, const int& vCountSelectionMax, UserDatas vUserDatas, - ImGuiFileDialogFlags vFlags) + ImGuiFileDialogFlags vFlags, + SelectFun vSelectFun) { if (prFileDialogInternal.puShowDialog) // if already opened, quit return; @@ -3497,7 +3506,7 @@ namespace IGFD OpenDialog( vKey, vTitle, vFilters, vPath, vFileName, - vCountSelectionMax, vUserDatas, vFlags); + vCountSelectionMax, vUserDatas, vFlags, vSelectFun); prFileDialogInternal.puDLGmodal = true; } @@ -3509,7 +3518,8 @@ namespace IGFD const std::string& vFilePathName, const int& vCountSelectionMax, UserDatas vUserDatas, - ImGuiFileDialogFlags vFlags) + ImGuiFileDialogFlags vFlags, + SelectFun vSelectFun) { if (prFileDialogInternal.puShowDialog) // if already opened, quit return; @@ -3517,7 +3527,7 @@ namespace IGFD OpenDialog( vKey, vTitle, vFilters, vFilePathName, - vCountSelectionMax, vUserDatas, vFlags); + vCountSelectionMax, vUserDatas, vFlags, vSelectFun); prFileDialogInternal.puDLGmodal = true; } @@ -3534,7 +3544,8 @@ namespace IGFD const float& vSidePaneWidth, const int& vCountSelectionMax, UserDatas vUserDatas, - ImGuiFileDialogFlags vFlags) + ImGuiFileDialogFlags vFlags, + SelectFun vSelectFun) { if (prFileDialogInternal.puShowDialog) // if already opened, quit return; @@ -3543,7 +3554,7 @@ namespace IGFD vKey, vTitle, vFilters, vPath, vFileName, vSidePane, vSidePaneWidth, - vCountSelectionMax, vUserDatas, vFlags); + vCountSelectionMax, vUserDatas, vFlags, vSelectFun); prFileDialogInternal.puDLGmodal = true; } @@ -3559,7 +3570,8 @@ namespace IGFD const float& vSidePaneWidth, const int& vCountSelectionMax, UserDatas vUserDatas, - ImGuiFileDialogFlags vFlags) + ImGuiFileDialogFlags vFlags, + SelectFun vSelectFun) { if (prFileDialogInternal.puShowDialog) // if already opened, quit return; @@ -3568,7 +3580,7 @@ namespace IGFD vKey, vTitle, vFilters, vFilePathName, vSidePane, vSidePaneWidth, - vCountSelectionMax, vUserDatas, vFlags); + vCountSelectionMax, vUserDatas, vFlags, vSelectFun); prFileDialogInternal.puDLGmodal = true; } @@ -3940,6 +3952,9 @@ namespace IGFD return 2; } else { fdi.SelectFileName(prFileDialogInternal, vInfos); + if (prFileDialogInternal.puDLGselFun!=NULL) { + prFileDialogInternal.puDLGselFun(GetFilePathName().c_str()); + } } } } diff --git a/extern/igfd/ImGuiFileDialog.h b/extern/igfd/ImGuiFileDialog.h index 2944bfbf4..016bd9a10 100644 --- a/extern/igfd/ImGuiFileDialog.h +++ b/extern/igfd/ImGuiFileDialog.h @@ -1080,6 +1080,7 @@ namespace IGFD typedef void* UserDatas; typedef std::function PaneFun; // side pane function binding + typedef std::function SelectFun; // click on file function binding class FileDialogInternal { public: @@ -1103,6 +1104,7 @@ namespace IGFD ImGuiFileDialogFlags puDLGflags = ImGuiFileDialogFlags_None; UserDatas puDLGuserDatas = nullptr; PaneFun puDLGoptionsPane = nullptr; + SelectFun puDLGselFun = nullptr; float puDLGoptionsPaneWidth = 0.0f; bool puDLGmodal = false; bool puNeedToExitDialog = false; @@ -1155,7 +1157,8 @@ namespace IGFD const std::string& vFileName, // defaut file name const int& vCountSelectionMax = 1, // count selection max UserDatas vUserDatas = nullptr, // user datas (can be retrieved in pane) - ImGuiFileDialogFlags vFlags = 0); // ImGuiFileDialogFlags + ImGuiFileDialogFlags vFlags = 0, // ImGuiFileDialogFlags + SelectFun vSelectFun = nullptr); // function to be called on file click void OpenDialog( // open simple dialog (path and filename are obtained from filePathName) const std::string& vKey, // key dialog @@ -1164,7 +1167,8 @@ namespace IGFD const std::string& vFilePathName, // file path name (will be decompsoed in path and fileName) const int& vCountSelectionMax = 1, // count selection max UserDatas vUserDatas = nullptr, // user datas (can be retrieved in pane) - ImGuiFileDialogFlags vFlags = 0); // ImGuiFileDialogFlags + ImGuiFileDialogFlags vFlags = 0, // ImGuiFileDialogFlags + SelectFun vSelectFun = nullptr); // function to be called on file click // with pane void OpenDialog( // open dialog with custom right pane (path and fileName can be specified) @@ -1177,7 +1181,8 @@ namespace IGFD const float& vSidePaneWidth = 250.0f, // side pane width const int& vCountSelectionMax = 1, // count selection max UserDatas vUserDatas = nullptr, // user datas (can be retrieved in pane) - ImGuiFileDialogFlags vFlags = 0); // ImGuiFileDialogFlags + ImGuiFileDialogFlags vFlags = 0, // ImGuiFileDialogFlags + SelectFun vSelectFun = nullptr); // function to be called on file click void OpenDialog( // open dialog with custom right pane (path and filename are obtained from filePathName) const std::string& vKey, // key dialog @@ -1188,7 +1193,8 @@ namespace IGFD const float& vSidePaneWidth = 250.0f, // side pane width const int& vCountSelectionMax = 1, // count selection max UserDatas vUserDatas = nullptr, // user datas (can be retrieved in pane) - ImGuiFileDialogFlags vFlags = 0); // ImGuiFileDialogFlags + ImGuiFileDialogFlags vFlags = 0, // ImGuiFileDialogFlags + SelectFun vSelectFun = nullptr); // function to be called on file click // modal dialog void OpenModal( // open simple modal (path and fileName can be specified) @@ -1199,7 +1205,8 @@ namespace IGFD const std::string& vFileName, // defaut file name const int& vCountSelectionMax = 1, // count selection max UserDatas vUserDatas = nullptr, // user datas (can be retrieved in pane) - ImGuiFileDialogFlags vFlags = 0); // ImGuiFileDialogFlags + ImGuiFileDialogFlags vFlags = 0, // ImGuiFileDialogFlags + SelectFun vSelectFun = nullptr); // function to be called on file click void OpenModal( // open simple modal (path and fielname are obtained from filePathName) const std::string& vKey, // key dialog @@ -1208,7 +1215,8 @@ namespace IGFD const std::string& vFilePathName, // file path name (will be decompsoed in path and fileName) const int& vCountSelectionMax = 1, // count selection max UserDatas vUserDatas = nullptr, // user datas (can be retrieved in pane) - ImGuiFileDialogFlags vFlags = 0); // ImGuiFileDialogFlags + ImGuiFileDialogFlags vFlags = 0, // ImGuiFileDialogFlags + SelectFun vSelectFun = nullptr); // function to be called on file click // with pane void OpenModal( // open modal with custom right pane (path and filename are obtained from filePathName) @@ -1221,7 +1229,8 @@ namespace IGFD const float& vSidePaneWidth = 250.0f, // side pane width const int& vCountSelectionMax = 1, // count selection max UserDatas vUserDatas = nullptr, // user datas (can be retrieved in pane) - ImGuiFileDialogFlags vFlags = 0); // ImGuiFileDialogFlags + ImGuiFileDialogFlags vFlags = 0, // ImGuiFileDialogFlags + SelectFun vSelectFun = nullptr); // function to be called on file click void OpenModal( // open modal with custom right pane (path and fielname are obtained from filePathName) const std::string& vKey, // key dialog @@ -1232,7 +1241,8 @@ namespace IGFD const float& vSidePaneWidth = 250.0f, // side pane width const int& vCountSelectionMax = 1, // count selection max UserDatas vUserDatas = nullptr, // user datas (can be retrieved in pane) - ImGuiFileDialogFlags vFlags = 0); // ImGuiFileDialogFlags + ImGuiFileDialogFlags vFlags = 0, // ImGuiFileDialogFlags + SelectFun vSelectFun = nullptr); // function to be called on file click // Display / Close dialog form bool Display( // Display the dialog. return true if a result was obtained (Ok or not) diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 4821df3a7..80fbb5ca9 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -739,7 +739,9 @@ String DivEngine::getWarnings() { } DivInstrument* DivEngine::getIns(int index, DivInstrumentType fallbackType) { - if (index==-2 && tempIns!=NULL) return tempIns; + if (index==-2 && tempIns!=NULL) { + return tempIns; + } if (index<0 || index>=song.insLen) { switch (fallbackType) { case DIV_INS_OPLL: @@ -2027,7 +2029,7 @@ void DivEngine::autoNoteOn(int ch, int ins, int note, int vol) { } do { - if ((ins<0 || ins>=song.insLen || getChannelType(finalChan)==4 || getPreferInsType(finalChan)==getIns(ins)->type || getIns(ins)->type==DIV_INS_AMIGA) && chan[finalChan].midiNote==-1) { + if ((ins==-1 || ins>=song.insLen || getChannelType(finalChan)==4 || getPreferInsType(finalChan)==getIns(ins)->type || getIns(ins)->type==DIV_INS_AMIGA) && chan[finalChan].midiNote==-1) { chan[finalChan].midiNote=note; pendingNotes.push(DivNoteEvent(finalChan,ins,note,vol,true)); break; diff --git a/src/engine/platform/amiga.h b/src/engine/platform/amiga.h index 07c44ee7c..a4c1a7a44 100644 --- a/src/engine/platform/amiga.h +++ b/src/engine/platform/amiga.h @@ -34,7 +34,7 @@ class DivPlatformAmiga: public DivDispatch { int audSub; signed char audDat; int sample, wave; - unsigned char ins; + int ins; int busClock; int note; bool active, insChanged, freqChanged, keyOn, keyOff, inPorta, useWave, setPos, useV, useP; diff --git a/src/engine/platform/arcade.h b/src/engine/platform/arcade.h index 41af49e33..e051e8aba 100644 --- a/src/engine/platform/arcade.h +++ b/src/engine/platform/arcade.h @@ -37,7 +37,7 @@ class DivPlatformArcade: public DivDispatch { DivMacroInt std; unsigned char freqH, freqL; int freq, baseFreq, pitch, note; - unsigned char ins; + int ins; signed char konCycles; bool active, insChanged, freqChanged, keyOn, keyOff, inPorta, portaPause, furnacePCM, hardReset; int vol, outVol; diff --git a/src/engine/platform/ay.h b/src/engine/platform/ay.h index a78f21bc8..ce06fc9fc 100644 --- a/src/engine/platform/ay.h +++ b/src/engine/platform/ay.h @@ -33,7 +33,8 @@ class DivPlatformAY8910: public DivDispatch { struct Channel { unsigned char freqH, freqL; int freq, baseFreq, note, pitch; - unsigned char ins, psgMode, autoEnvNum, autoEnvDen; + int ins; + unsigned char psgMode, autoEnvNum, autoEnvDen; signed char konCycles; bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, inPorta; int vol, outVol; diff --git a/src/engine/platform/ay8930.h b/src/engine/platform/ay8930.h index adbe0be87..61a186f4c 100644 --- a/src/engine/platform/ay8930.h +++ b/src/engine/platform/ay8930.h @@ -29,7 +29,8 @@ class DivPlatformAY8930: public DivDispatch { struct Channel { unsigned char freqH, freqL; int freq, baseFreq, note, pitch; - unsigned char ins, psgMode, autoEnvNum, autoEnvDen, duty; + int ins; + unsigned char psgMode, autoEnvNum, autoEnvDen, duty; signed char konCycles; bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, inPorta; int vol, outVol; diff --git a/src/engine/platform/bubsyswsg.h b/src/engine/platform/bubsyswsg.h index 2efcc4d98..0fbb2ac5d 100644 --- a/src/engine/platform/bubsyswsg.h +++ b/src/engine/platform/bubsyswsg.h @@ -28,8 +28,7 @@ class DivPlatformBubSysWSG: public DivDispatch { struct Channel { - int freq, baseFreq, pitch, note; - unsigned char ins; + int freq, baseFreq, pitch, note, ins; bool active, insChanged, freqChanged, keyOn, keyOff, inPorta; signed char vol, outVol, wave; signed char waveROM[32] = {0}; // 4 bit PROM per channel on bubble system diff --git a/src/engine/platform/c64.h b/src/engine/platform/c64.h index f77e24b97..8df82051c 100644 --- a/src/engine/platform/c64.h +++ b/src/engine/platform/c64.h @@ -26,8 +26,8 @@ class DivPlatformC64: public DivDispatch { struct Channel { - int freq, baseFreq, pitch, prevFreq, testWhen, note; - unsigned char ins, sweep, wave, attack, decay, sustain, release; + int freq, baseFreq, pitch, prevFreq, testWhen, note, ins; + unsigned char sweep, wave, attack, decay, sustain, release; short duty; bool active, insChanged, freqChanged, sweepChanged, keyOn, keyOff, inPorta, filter; bool resetMask, resetFilter, resetDuty, ring, sync; diff --git a/src/engine/platform/fds.h b/src/engine/platform/fds.h index 6d206f3ea..bf9492722 100644 --- a/src/engine/platform/fds.h +++ b/src/engine/platform/fds.h @@ -26,8 +26,8 @@ class DivPlatformFDS: public DivDispatch { struct Channel { - int freq, baseFreq, pitch, prevFreq, note, modFreq; - unsigned char ins, duty, sweep, modDepth, modPos; + int freq, baseFreq, pitch, prevFreq, note, modFreq, ins; + unsigned char duty, sweep, modDepth, modPos; bool active, insChanged, freqChanged, sweepChanged, keyOn, keyOff, inPorta, modOn; signed char vol, outVol, wave; signed char modTable[32]; diff --git a/src/engine/platform/gb.h b/src/engine/platform/gb.h index df7070ec8..c79603ab1 100644 --- a/src/engine/platform/gb.h +++ b/src/engine/platform/gb.h @@ -27,8 +27,8 @@ class DivPlatformGB: public DivDispatch { struct Channel { - int freq, baseFreq, pitch, note; - unsigned char ins, duty, sweep; + int freq, baseFreq, pitch, note, ins; + unsigned char duty, sweep; bool active, insChanged, freqChanged, sweepChanged, keyOn, keyOff, inPorta; signed char vol, outVol, wave; DivMacroInt std; diff --git a/src/engine/platform/genesis.h b/src/engine/platform/genesis.h index aa7659fa3..31f11d188 100644 --- a/src/engine/platform/genesis.h +++ b/src/engine/platform/genesis.h @@ -37,7 +37,7 @@ class DivPlatformGenesis: public DivDispatch { DivMacroInt std; unsigned char freqH, freqL; int freq, baseFreq, pitch, portaPauseFreq, note; - unsigned char ins; + int ins; bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, furnaceDac, inPorta, hardReset; int vol, outVol; unsigned char pan; diff --git a/src/engine/platform/genesisext.h b/src/engine/platform/genesisext.h index 4cdf9fc65..e3604a34c 100644 --- a/src/engine/platform/genesisext.h +++ b/src/engine/platform/genesisext.h @@ -25,8 +25,7 @@ class DivPlatformGenesisExt: public DivPlatformGenesis { struct OpChannel { DivMacroInt std; unsigned char freqH, freqL; - int freq, baseFreq, pitch, portaPauseFreq; - unsigned char ins; + int freq, baseFreq, pitch, portaPauseFreq, ins; signed char konCycles; bool active, insChanged, freqChanged, keyOn, keyOff, portaPause; int vol; diff --git a/src/engine/platform/lynx.h b/src/engine/platform/lynx.h index 61f6e67df..f1f974ed0 100644 --- a/src/engine/platform/lynx.h +++ b/src/engine/platform/lynx.h @@ -44,8 +44,8 @@ class DivPlatformLynx: public DivDispatch { DivMacroInt std; MikeyFreqDiv fd; MikeyDuty duty; - int baseFreq, pitch, note, actualNote, lfsr; - unsigned char ins, pan; + int baseFreq, pitch, note, actualNote, lfsr, ins; + unsigned char pan; bool active, insChanged, freqChanged, keyOn, keyOff, inPorta; signed char vol, outVol; Channel(): diff --git a/src/engine/platform/mmc5.h b/src/engine/platform/mmc5.h index 02ca06e84..879ebee18 100644 --- a/src/engine/platform/mmc5.h +++ b/src/engine/platform/mmc5.h @@ -25,8 +25,8 @@ class DivPlatformMMC5: public DivDispatch { struct Channel { - int freq, baseFreq, pitch, prevFreq, note; - unsigned char ins, duty, sweep; + int freq, baseFreq, pitch, prevFreq, note, ins; + unsigned char duty, sweep; bool active, insChanged, freqChanged, sweepChanged, keyOn, keyOff, inPorta, furnaceDac; signed char vol, outVol, wave; DivMacroInt std; diff --git a/src/engine/platform/nes.h b/src/engine/platform/nes.h index 2cf2a8a15..7650d39e9 100644 --- a/src/engine/platform/nes.h +++ b/src/engine/platform/nes.h @@ -25,8 +25,8 @@ class DivPlatformNES: public DivDispatch { struct Channel { - int freq, baseFreq, pitch, prevFreq, note; - unsigned char ins, duty, sweep; + int freq, baseFreq, pitch, prevFreq, note, ins; + unsigned char duty, sweep; bool active, insChanged, freqChanged, sweepChanged, keyOn, keyOff, inPorta, furnaceDac; signed char vol, outVol, wave; DivMacroInt std; diff --git a/src/engine/platform/opl.h b/src/engine/platform/opl.h index 76979a54f..ee3bcb36f 100644 --- a/src/engine/platform/opl.h +++ b/src/engine/platform/opl.h @@ -30,8 +30,7 @@ class DivPlatformOPL: public DivDispatch { DivInstrumentFM state; DivMacroInt std; unsigned char freqH, freqL; - int freq, baseFreq, pitch, note; - unsigned char ins; + int freq, baseFreq, pitch, note, ins; bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, furnaceDac, inPorta, fourOp; int vol, outVol; unsigned char pan; diff --git a/src/engine/platform/opll.h b/src/engine/platform/opll.h index 04f4f13a7..79f905c66 100644 --- a/src/engine/platform/opll.h +++ b/src/engine/platform/opll.h @@ -33,8 +33,7 @@ class DivPlatformOPLL: public DivDispatch { DivInstrumentFM state; DivMacroInt std; unsigned char freqH, freqL; - int freq, baseFreq, pitch, note; - unsigned char ins; + int freq, baseFreq, pitch, note, ins; bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, furnaceDac, inPorta; int vol, outVol; unsigned char pan; diff --git a/src/engine/platform/pce.h b/src/engine/platform/pce.h index 57a4b033f..deb85fbb9 100644 --- a/src/engine/platform/pce.h +++ b/src/engine/platform/pce.h @@ -31,8 +31,8 @@ class DivPlatformPCE: public DivDispatch { int freq, baseFreq, pitch, note; int dacPeriod, dacRate; unsigned int dacPos; - int dacSample; - unsigned char ins, pan; + int dacSample, ins; + unsigned char pan; bool active, insChanged, freqChanged, keyOn, keyOff, inPorta, noise, pcm, furnaceDac; signed char vol, outVol, wave; DivMacroInt std; diff --git a/src/engine/platform/pcspkr.h b/src/engine/platform/pcspkr.h index 68e6bf1a6..f8813f3ff 100644 --- a/src/engine/platform/pcspkr.h +++ b/src/engine/platform/pcspkr.h @@ -25,8 +25,8 @@ class DivPlatformPCSpeaker: public DivDispatch { struct Channel { - int freq, baseFreq, pitch, note; - unsigned char ins, duty, sweep; + int freq, baseFreq, pitch, note, ins; + unsigned char duty, sweep; bool active, insChanged, freqChanged, sweepChanged, keyOn, keyOff, inPorta, furnaceDac; signed char vol, outVol, wave; DivMacroInt std; diff --git a/src/engine/platform/pet.h b/src/engine/platform/pet.h index 7647a87fe..b4b75316e 100644 --- a/src/engine/platform/pet.h +++ b/src/engine/platform/pet.h @@ -25,8 +25,7 @@ class DivPlatformPET: public DivDispatch { struct Channel { - int freq, baseFreq, pitch, note; - unsigned char ins; + int freq, baseFreq, pitch, note, ins; bool active, insChanged, freqChanged, keyOn, keyOff, inPorta; int vol, outVol, wave; unsigned char sreg; diff --git a/src/engine/platform/qsound.h b/src/engine/platform/qsound.h index 62b1d647f..aa07a3b47 100644 --- a/src/engine/platform/qsound.h +++ b/src/engine/platform/qsound.h @@ -30,8 +30,7 @@ class DivPlatformQSound: public DivDispatch { int freq, baseFreq, pitch; unsigned short audLen; unsigned int audPos; - int sample, wave; - unsigned char ins; + int sample, wave, ins; int note; int panning; bool active, insChanged, freqChanged, keyOn, keyOff, inPorta, useWave; diff --git a/src/engine/platform/saa.h b/src/engine/platform/saa.h index 0542ff513..309092936 100644 --- a/src/engine/platform/saa.h +++ b/src/engine/platform/saa.h @@ -35,8 +35,8 @@ class DivPlatformSAA1099: public DivDispatch { protected: struct Channel { unsigned char freqH, freqL; - int freq, baseFreq, pitch, note; - unsigned char ins, psgMode; + int freq, baseFreq, pitch, note, ins; + unsigned char psgMode; signed char konCycles; bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, inPorta; int vol, outVol; diff --git a/src/engine/platform/segapcm.h b/src/engine/platform/segapcm.h index 4b05c160c..3a68a34b3 100644 --- a/src/engine/platform/segapcm.h +++ b/src/engine/platform/segapcm.h @@ -29,8 +29,7 @@ class DivPlatformSegaPCM: public DivDispatch { struct Channel { DivMacroInt std; unsigned char freqH, freqL; - int freq, baseFreq, pitch, note; - unsigned char ins; + int freq, baseFreq, pitch, note, ins; signed char konCycles; bool active, insChanged, freqChanged, keyOn, keyOff, inPorta, portaPause, furnacePCM; int vol, outVol; diff --git a/src/engine/platform/sms.h b/src/engine/platform/sms.h index 0aeb9e81a..66fdda857 100644 --- a/src/engine/platform/sms.h +++ b/src/engine/platform/sms.h @@ -26,8 +26,7 @@ class DivPlatformSMS: public DivDispatch { struct Channel { - int freq, baseFreq, pitch, note, actualNote; - unsigned char ins; + int freq, baseFreq, pitch, note, actualNote, ins; bool active, insChanged, freqChanged, keyOn, keyOff, inPorta; signed char vol, outVol; DivMacroInt std; diff --git a/src/engine/platform/swan.h b/src/engine/platform/swan.h index 0f1643c53..fdad150b2 100644 --- a/src/engine/platform/swan.h +++ b/src/engine/platform/swan.h @@ -28,8 +28,8 @@ class DivPlatformSwan: public DivDispatch { struct Channel { - int freq, baseFreq, pitch, note; - unsigned char ins, pan; + int freq, baseFreq, pitch, note, ins; + unsigned char pan; bool active, insChanged, freqChanged, keyOn, keyOff, inPorta; int vol, outVol, wave; DivMacroInt std; diff --git a/src/engine/platform/tia.h b/src/engine/platform/tia.h index 3ec3c7f34..602cc6f1e 100644 --- a/src/engine/platform/tia.h +++ b/src/engine/platform/tia.h @@ -27,8 +27,8 @@ class DivPlatformTIA: public DivDispatch { protected: struct Channel { - int freq, baseFreq, pitch, note; - unsigned char ins, shape; + int freq, baseFreq, pitch, note, ins; + unsigned char shape; signed char konCycles; bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, inPorta; int vol, outVol; diff --git a/src/engine/platform/tx81z.h b/src/engine/platform/tx81z.h index c61b4bfe7..d9261ebd7 100644 --- a/src/engine/platform/tx81z.h +++ b/src/engine/platform/tx81z.h @@ -35,8 +35,7 @@ class DivPlatformTX81Z: public DivDispatch { DivInstrumentFM state; DivMacroInt std; unsigned char freqH, freqL; - int freq, baseFreq, pitch, note; - unsigned char ins; + int freq, baseFreq, pitch, note, ins; signed char konCycles; bool active, insChanged, freqChanged, keyOn, keyOff, inPorta, portaPause, furnacePCM, hardReset; int vol, outVol; diff --git a/src/engine/platform/vera.h b/src/engine/platform/vera.h index ec6dcde11..64dee5c78 100644 --- a/src/engine/platform/vera.h +++ b/src/engine/platform/vera.h @@ -29,8 +29,8 @@ struct VERA_PCM; class DivPlatformVERA: public DivDispatch { protected: struct Channel { - int freq, baseFreq, pitch, note; - unsigned char ins, pan; + int freq, baseFreq, pitch, note, ins; + unsigned char pan; bool active, freqChanged, inPorta; int vol, outVol; unsigned accum; diff --git a/src/engine/platform/vic20.h b/src/engine/platform/vic20.h index 7948c5ef2..8c15c35d9 100644 --- a/src/engine/platform/vic20.h +++ b/src/engine/platform/vic20.h @@ -27,8 +27,8 @@ class DivPlatformVIC20: public DivDispatch { struct Channel { - int freq, baseFreq, pitch, note; - unsigned char ins, pan; + int freq, baseFreq, pitch, note, ins; + unsigned char pan; bool active, insChanged, freqChanged, keyOn, keyOff, inPorta; int vol, outVol, wave, waveWriteCycle; DivMacroInt std; diff --git a/src/engine/platform/vrc6.h b/src/engine/platform/vrc6.h index d28265a47..a44e9d336 100644 --- a/src/engine/platform/vrc6.h +++ b/src/engine/platform/vrc6.h @@ -31,8 +31,8 @@ class DivPlatformVRC6: public DivDispatch { int freq, baseFreq, pitch, note; int dacPeriod, dacRate, dacOut; unsigned int dacPos; - int dacSample; - unsigned char ins, duty; + int dacSample, ins; + unsigned char duty; bool active, insChanged, freqChanged, keyOn, keyOff, inPorta, pcm, furnaceDac; signed char vol, outVol; DivMacroInt std; diff --git a/src/engine/platform/ym2610.h b/src/engine/platform/ym2610.h index e75e24d79..af947199d 100644 --- a/src/engine/platform/ym2610.h +++ b/src/engine/platform/ym2610.h @@ -43,8 +43,8 @@ class DivPlatformYM2610: public DivDispatch { struct Channel { DivInstrumentFM state; unsigned char freqH, freqL; - int freq, baseFreq, pitch, note; - unsigned char ins, psgMode, autoEnvNum, autoEnvDen; + int freq, baseFreq, pitch, note, ins; + unsigned char psgMode, autoEnvNum, autoEnvDen; signed char konCycles; bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, inPorta, furnacePCM, hardReset; int vol, outVol; diff --git a/src/engine/platform/ym2610b.h b/src/engine/platform/ym2610b.h index 6d46ecb26..d2363ed14 100644 --- a/src/engine/platform/ym2610b.h +++ b/src/engine/platform/ym2610b.h @@ -35,8 +35,8 @@ class DivPlatformYM2610B: public DivDispatch { struct Channel { DivInstrumentFM state; unsigned char freqH, freqL; - int freq, baseFreq, pitch, note; - unsigned char ins, psgMode, autoEnvNum, autoEnvDen; + int freq, baseFreq, pitch, note, ins; + unsigned char psgMode, autoEnvNum, autoEnvDen; signed char konCycles; bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, inPorta, furnacePCM, hardReset; int vol, outVol; diff --git a/src/engine/platform/ym2610bext.h b/src/engine/platform/ym2610bext.h index e908916e3..c2cd4fb37 100644 --- a/src/engine/platform/ym2610bext.h +++ b/src/engine/platform/ym2610bext.h @@ -25,8 +25,7 @@ class DivPlatformYM2610BExt: public DivPlatformYM2610B { struct OpChannel { DivMacroInt std; unsigned char freqH, freqL; - int freq, baseFreq, pitch; - unsigned char ins; + int freq, baseFreq, pitch, ins; signed char konCycles; bool active, insChanged, freqChanged, keyOn, keyOff, portaPause; int vol; diff --git a/src/engine/platform/ym2610ext.h b/src/engine/platform/ym2610ext.h index 9fa44d0b1..b89229120 100644 --- a/src/engine/platform/ym2610ext.h +++ b/src/engine/platform/ym2610ext.h @@ -25,8 +25,7 @@ class DivPlatformYM2610Ext: public DivPlatformYM2610 { struct OpChannel { DivMacroInt std; unsigned char freqH, freqL; - int freq, baseFreq, pitch; - unsigned char ins; + int freq, baseFreq, pitch, ins; signed char konCycles; bool active, insChanged, freqChanged, keyOn, keyOff, portaPause; int vol; diff --git a/src/gui/fileDialog.cpp b/src/gui/fileDialog.cpp index 7bfafb4a3..3b8fdd46a 100644 --- a/src/gui/fileDialog.cpp +++ b/src/gui/fileDialog.cpp @@ -4,7 +4,7 @@ #include "../../extern/pfd-fixed/portable-file-dialogs.h" -bool FurnaceGUIFileDialog::openLoad(String header, std::vector filter, const char* noSysFilter, String path, double dpiScale) { +bool FurnaceGUIFileDialog::openLoad(String header, std::vector filter, const char* noSysFilter, String path, double dpiScale, FileDialogSelectCallback clickCallback) { if (opened) return false; saving=false; curPath=path; @@ -13,7 +13,7 @@ bool FurnaceGUIFileDialog::openLoad(String header, std::vector filter, c dialogO=new pfd::open_file(header,path,filter); } else { ImGuiFileDialog::Instance()->DpiScale=dpiScale; - ImGuiFileDialog::Instance()->OpenModal("FileDialog",header,noSysFilter,path); + ImGuiFileDialog::Instance()->OpenModal("FileDialog",header,noSysFilter,path,1,nullptr,0,clickCallback); } opened=true; return true; diff --git a/src/gui/fileDialog.h b/src/gui/fileDialog.h index 0dcd82a6d..6c5f2faac 100644 --- a/src/gui/fileDialog.h +++ b/src/gui/fileDialog.h @@ -7,6 +7,8 @@ namespace pfd { class save_file; } +typedef std::function FileDialogSelectCallback; + class FurnaceGUIFileDialog { bool sysDialog; bool opened; @@ -16,7 +18,7 @@ class FurnaceGUIFileDialog { pfd::open_file* dialogO; pfd::save_file* dialogS; public: - bool openLoad(String header, std::vector filter, const char* noSysFilter, String path, double dpiScale); + bool openLoad(String header, std::vector filter, const char* noSysFilter, String path, double dpiScale, FileDialogSelectCallback clickCallback=NULL); bool openSave(String header, std::vector filter, const char* noSysFilter, String path, double dpiScale); bool accepted(); void close(); diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 26e414e05..4aa37b815 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -1293,7 +1293,15 @@ void FurnaceGUI::openFileDialog(FurnaceGUIFileDialogs type) { "all files", ".*"}, "compatible files{.fui,.dmp,.tfi,.vgi,.s3i,.sbi,.opli,.opni,.y12,.bnk,.ff,.opm},.*", workingDirIns, - dpiScale + dpiScale, + [this](const char* path) { + std::vector instruments=e->instrumentFromFile(path); + if (!instruments.empty()) { + e->loadTempIns(instruments[0]); + curIns=-2; + } + for (DivInstrument* i: instruments) delete i; + } ); break; case GUI_FILE_INS_SAVE: From 584e97f313b4337bfc7fc1389d390386aaf72d51 Mon Sep 17 00:00:00 2001 From: cam900 Date: Tue, 26 Apr 2022 09:20:41 +0900 Subject: [PATCH 206/342] Missing header --- src/gui/fileDialog.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/gui/fileDialog.h b/src/gui/fileDialog.h index 6c5f2faac..8d3ff787a 100644 --- a/src/gui/fileDialog.h +++ b/src/gui/fileDialog.h @@ -1,5 +1,6 @@ #include "../ta-utils.h" #include "imgui.h" +#include #include namespace pfd { From 28af57c4f04b236e37ada6db4cf26aecd372d943 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Tue, 26 Apr 2022 00:22:49 -0500 Subject: [PATCH 207/342] fix metronome in low-latency mode --- src/engine/playback.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/engine/playback.cpp b/src/engine/playback.cpp index 38c7309d7..c2c20d3d0 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -1846,7 +1846,7 @@ void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsi // 2. check whether we gonna tick if (cycles<=0) { // we have to tick - if (!freelance && stepPlay!=-1) { + if (!freelance && stepPlay!=-1 && subticks==1) { unsigned int realPos=size-(runLeftG>>MASTER_CLOCK_PREC); if (realPos>=size) realPos=size-1; if (song.hilightA>0) { From 9eb9561b53cbd7b8eb70155829d4a8dfd85d9fdf Mon Sep 17 00:00:00 2001 From: tildearrow Date: Tue, 26 Apr 2022 01:07:28 -0500 Subject: [PATCH 208/342] GUI: fix #400 --- src/engine/engine.cpp | 12 ++++++++++++ src/engine/fileOpsIns.cpp | 5 +++++ src/gui/gui.cpp | 8 ++++++++ src/gui/gui.h | 2 +- 4 files changed, 26 insertions(+), 1 deletion(-) diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 80fbb5ca9..a8fd95a4a 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -1428,6 +1428,10 @@ bool DivEngine::addWaveFromFile(const char* path) { fclose(f); return false; } + if (len==(SIZE_MAX>>1)) { + fclose(f); + return false; + } if (len==0) { fclose(f); return false; @@ -1620,6 +1624,14 @@ int DivEngine::addSampleFromFile(const char* path) { return -1; } + if (len==(SIZE_MAX>>1)) { + fclose(f); + BUSY_END; + lastError="file is invalid!"; + delete sample; + return -1; + } + if (fseek(f,0,SEEK_SET)<0) { fclose(f); BUSY_END; diff --git a/src/engine/fileOpsIns.cpp b/src/engine/fileOpsIns.cpp index 1173d59a0..521ab4249 100644 --- a/src/engine/fileOpsIns.cpp +++ b/src/engine/fileOpsIns.cpp @@ -1220,6 +1220,11 @@ std::vector DivEngine::instrumentFromFile(const char* path) { fclose(f); return ret; } + if (len==(SIZE_MAX>>1)) { + lastError=strerror(errno); + fclose(f); + return ret; + } if (len==0) { lastError=strerror(errno); fclose(f); diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 4aa37b815..ddd525920 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -1286,6 +1286,7 @@ void FurnaceGUI::openFileDialog(FurnaceGUIFileDialogs type) { ); break; case GUI_FILE_INS_OPEN: + prevIns=-3; if (!dirExists(workingDirIns)) workingDirIns=getHomeDir(); hasOpened=fileDialog->openLoad( "Load Instrument", @@ -1298,6 +1299,9 @@ void FurnaceGUI::openFileDialog(FurnaceGUIFileDialogs type) { std::vector instruments=e->instrumentFromFile(path); if (!instruments.empty()) { e->loadTempIns(instruments[0]); + if (curIns!=-2) { + prevIns=curIns; + } curIns=-2; } for (DivInstrument* i: instruments) delete i; @@ -2937,6 +2941,9 @@ bool FurnaceGUI::loop() { workingDirLayout=fileDialog->getPath()+DIR_SEPARATOR_STR; break; } + if (prevIns!=-3) { + curIns=prevIns; + } if (fileDialog->accepted()) { fileName=fileDialog->getFileName(); if (fileName!="") { @@ -3791,6 +3798,7 @@ FurnaceGUI::FurnaceGUI(): curSample(0), curOctave(3), curOrder(0), + prevIns(0), oldRow(0), oldOrder(0), oldOrder1(0), diff --git a/src/gui/gui.h b/src/gui/gui.h index 98a861127..91c17b3f7 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -931,7 +931,7 @@ class FurnaceGUI { char finalLayoutPath[4096]; - int curIns, curWave, curSample, curOctave, curOrder, oldRow, oldOrder, oldOrder1, editStep, exportLoops, soloChan, soloTimeout, orderEditMode, orderCursor; + int curIns, curWave, curSample, curOctave, curOrder, prevIns, oldRow, oldOrder, oldOrder1, editStep, exportLoops, soloChan, soloTimeout, orderEditMode, orderCursor; int loopOrder, loopRow, loopEnd, isClipping, extraChannelButtons, patNameTarget, newSongCategory, latchTarget; int wheelX, wheelY; From e8f29cf12249c3fc672681c51431f0a569bc49b3 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Tue, 26 Apr 2022 15:24:45 -0500 Subject: [PATCH 209/342] prepare for macroInt preview in instrument editor --- src/engine/dispatch.h | 135 +++++++++------- src/engine/platform/abstract.cpp | 4 + src/engine/platform/sound/su.cpp | 261 +++++++++++++++++++++++++++++++ src/engine/platform/sound/su.h | 93 +++++++++++ src/gui/gui.cpp | 3 + src/gui/gui.h | 4 +- src/gui/guiConst.cpp | 1 + src/gui/settings.cpp | 10 ++ src/gui/songInfo.cpp | 13 +- 9 files changed, 467 insertions(+), 57 deletions(-) create mode 100644 src/engine/platform/sound/su.cpp create mode 100644 src/engine/platform/sound/su.h diff --git a/src/engine/dispatch.h b/src/engine/dispatch.h index bd3b19a68..307ad2f16 100644 --- a/src/engine/dispatch.h +++ b/src/engine/dispatch.h @@ -35,74 +35,77 @@ // names as strings for the commands (and other debug stuff). // // if you miss it, the program will crash or misbehave at some point. +// +// the comments are: (arg1, arg2) -> val +// not all commands have a return value enum DivDispatchCmds { - DIV_CMD_NOTE_ON=0, + DIV_CMD_NOTE_ON=0, // (note) DIV_CMD_NOTE_OFF, DIV_CMD_NOTE_OFF_ENV, DIV_CMD_ENV_RELEASE, - DIV_CMD_INSTRUMENT, - DIV_CMD_VOLUME, - DIV_CMD_GET_VOLUME, - DIV_CMD_GET_VOLMAX, - DIV_CMD_NOTE_PORTA, - DIV_CMD_PITCH, - DIV_CMD_PANNING, - DIV_CMD_LEGATO, - DIV_CMD_PRE_PORTA, - DIV_CMD_PRE_NOTE, // used in C64 + DIV_CMD_INSTRUMENT, // (ins, force) + DIV_CMD_VOLUME, // (vol) + DIV_CMD_GET_VOLUME, // () -> vol + DIV_CMD_GET_VOLMAX, // () -> volMax + DIV_CMD_NOTE_PORTA, // (target, speed) -> 2 if target reached + DIV_CMD_PITCH, // (pitch) + DIV_CMD_PANNING, // (pan) + DIV_CMD_LEGATO, // (note) + DIV_CMD_PRE_PORTA, // (inPorta, isPortaOrSlide) + DIV_CMD_PRE_NOTE, // used in C64 (note) - DIV_CMD_SAMPLE_MODE, - DIV_CMD_SAMPLE_FREQ, - DIV_CMD_SAMPLE_BANK, - DIV_CMD_SAMPLE_POS, + DIV_CMD_SAMPLE_MODE, // (enabled) + DIV_CMD_SAMPLE_FREQ, // (frequency) + DIV_CMD_SAMPLE_BANK, // (bank) + DIV_CMD_SAMPLE_POS, // (pos) - DIV_CMD_FM_HARD_RESET, - DIV_CMD_FM_LFO, - DIV_CMD_FM_LFO_WAVE, - DIV_CMD_FM_TL, - DIV_CMD_FM_AR, - DIV_CMD_FM_FB, - DIV_CMD_FM_MULT, - DIV_CMD_FM_EXTCH, - DIV_CMD_FM_AM_DEPTH, - DIV_CMD_FM_PM_DEPTH, + DIV_CMD_FM_HARD_RESET, // (enabled) + DIV_CMD_FM_LFO, // (speed) + DIV_CMD_FM_LFO_WAVE, // (waveform) + DIV_CMD_FM_TL, // (op, value) + DIV_CMD_FM_AR, // (op, value) + DIV_CMD_FM_FB, // (value) + DIV_CMD_FM_MULT, // (op, value) + DIV_CMD_FM_EXTCH, // (enabled) + DIV_CMD_FM_AM_DEPTH, // (depth) + DIV_CMD_FM_PM_DEPTH, // (depth) - DIV_CMD_GENESIS_LFO, + DIV_CMD_GENESIS_LFO, // unused? - DIV_CMD_ARCADE_LFO, + DIV_CMD_ARCADE_LFO, // unused? - DIV_CMD_STD_NOISE_FREQ, - DIV_CMD_STD_NOISE_MODE, + DIV_CMD_STD_NOISE_FREQ, // (freq) + DIV_CMD_STD_NOISE_MODE, // (mode) - DIV_CMD_WAVE, + DIV_CMD_WAVE, // (waveform) - DIV_CMD_GB_SWEEP_TIME, - DIV_CMD_GB_SWEEP_DIR, + DIV_CMD_GB_SWEEP_TIME, // (time) + DIV_CMD_GB_SWEEP_DIR, // (direction) - DIV_CMD_PCE_LFO_MODE, - DIV_CMD_PCE_LFO_SPEED, + DIV_CMD_PCE_LFO_MODE, // (mode) + DIV_CMD_PCE_LFO_SPEED, // (speed) - DIV_CMD_NES_SWEEP, + DIV_CMD_NES_SWEEP, // (direction, value) - DIV_CMD_C64_CUTOFF, - DIV_CMD_C64_RESONANCE, - DIV_CMD_C64_FILTER_MODE, - DIV_CMD_C64_RESET_TIME, - DIV_CMD_C64_RESET_MASK, - DIV_CMD_C64_FILTER_RESET, - DIV_CMD_C64_DUTY_RESET, - DIV_CMD_C64_EXTENDED, - DIV_CMD_C64_FINE_DUTY, - DIV_CMD_C64_FINE_CUTOFF, + DIV_CMD_C64_CUTOFF, // (value) + DIV_CMD_C64_RESONANCE, // (value) + DIV_CMD_C64_FILTER_MODE, // (value) + DIV_CMD_C64_RESET_TIME, // (value) + DIV_CMD_C64_RESET_MASK, // (mask) + DIV_CMD_C64_FILTER_RESET, // (value) + DIV_CMD_C64_DUTY_RESET, // (value) + DIV_CMD_C64_EXTENDED, // (value) + DIV_CMD_C64_FINE_DUTY, // (value) + DIV_CMD_C64_FINE_CUTOFF, // (value) DIV_CMD_AY_ENVELOPE_SET, DIV_CMD_AY_ENVELOPE_LOW, DIV_CMD_AY_ENVELOPE_HIGH, DIV_CMD_AY_ENVELOPE_SLIDE, - DIV_CMD_AY_NOISE_MASK_AND, - DIV_CMD_AY_NOISE_MASK_OR, - DIV_CMD_AY_AUTO_ENVELOPE, - DIV_CMD_AY_IO_WRITE, + DIV_CMD_AY_NOISE_MASK_AND, // (value) + DIV_CMD_AY_NOISE_MASK_OR, // (value) + DIV_CMD_AY_AUTO_ENVELOPE, // (value) + DIV_CMD_AY_IO_WRITE, // (port, value) DIV_CMD_AY_AUTO_PWM, DIV_CMD_FDS_MOD_DEPTH, @@ -111,13 +114,13 @@ enum DivDispatchCmds { DIV_CMD_FDS_MOD_POS, DIV_CMD_FDS_MOD_WAVE, - DIV_CMD_SAA_ENVELOPE, + DIV_CMD_SAA_ENVELOPE, // (value) - DIV_CMD_AMIGA_FILTER, - DIV_CMD_AMIGA_AM, - DIV_CMD_AMIGA_PM, + DIV_CMD_AMIGA_FILTER, // (enabled) + DIV_CMD_AMIGA_AM, // (enabled) + DIV_CMD_AMIGA_PM, // (enabled) - DIV_CMD_LYNX_LFSR_LOAD, + DIV_CMD_LYNX_LFSR_LOAD, // (value) DIV_CMD_QSOUND_ECHO_FEEDBACK, DIV_CMD_QSOUND_ECHO_DELAY, @@ -146,7 +149,7 @@ enum DivDispatchCmds { DIV_CMD_N163_GLOBAL_WAVE_LOADLEN, DIV_CMD_N163_GLOBAL_WAVE_LOADMODE, - DIV_ALWAYS_SET_VOLUME, + DIV_ALWAYS_SET_VOLUME, // () -> alwaysSetVol DIV_CMD_MAX }; @@ -201,6 +204,7 @@ struct DivRegWrite { }; class DivEngine; +class DivMacroInt; class DivDispatch { protected: @@ -214,11 +218,13 @@ class DivDispatch { /** * the rate the samples are provided. * the engine shall resample to the output rate. + * you have to initialize this one during init() or setFlags(). */ int rate; /** * the actual chip's clock. + * you have to initialize this one during init() or setFlags(). */ int chipClock; @@ -254,6 +260,12 @@ class DivDispatch { * @return a pointer, or NULL. */ virtual void* getChanState(int chan); + + /** + * get the DivMacroInt of a chanmel. + * @return a pointer, or NULL. + */ + virtual DivMacroInt* getChanMacroInt(int chan); /** * get the register pool of this dispatch. @@ -423,12 +435,25 @@ class DivDispatch { virtual ~DivDispatch(); }; +// pitch calculation: +// - a DivDispatch usually contains four variables per channel: +// - baseFreq: this changes on new notes, legato, arpeggio and slides. +// - pitch: this changes with DIV_CMD_PITCH (E5xx/04xy). +// - freq: this is the result of combining baseFreq and pitch using DivEngine::calcFreq(). +// - freqChanged: whether baseFreq and/or pitch have changed, and a frequency recalculation is required on the next tick. +// - the following definitions will help you calculate baseFreq. +// - to use them, define CHIP_DIVIDER and/or CHIP_FREQBASE in your code (not in the header though!). #define NOTE_PERIODIC(x) round(parent->calcBaseFreq(chipClock,CHIP_DIVIDER,x,true)) #define NOTE_PERIODIC_NOROUND(x) parent->calcBaseFreq(chipClock,CHIP_DIVIDER,x,true) #define NOTE_FREQUENCY(x) parent->calcBaseFreq(chipClock,CHIP_FREQBASE,x,false) +// this is a special case definition. only use it for f-num/block-based chips. #define NOTE_FNUM_BLOCK(x,bits) parent->calcBaseFreqFNumBlock(chipClock,CHIP_FREQBASE,x,bits) +// these are here for convenience. +// it is encouraged to use these, since you get an exact value this way. +// - NTSC colorburst: 3.58MHz +// - PAL colorburst: 4.43MHz #define COLOR_NTSC (315000000.0/88.0) #define COLOR_PAL (283.75*15625.0+25.0) diff --git a/src/engine/platform/abstract.cpp b/src/engine/platform/abstract.cpp index 343838e73..56f2aa3d7 100644 --- a/src/engine/platform/abstract.cpp +++ b/src/engine/platform/abstract.cpp @@ -29,6 +29,10 @@ void* DivDispatch::getChanState(int chan) { return NULL; } +DivMacroInt* DivDispatch::getChanMacroInt(int chan) { + return NULL; +} + unsigned char* DivDispatch::getRegisterPool() { return NULL; } diff --git a/src/engine/platform/sound/su.cpp b/src/engine/platform/sound/su.cpp new file mode 100644 index 000000000..daf710fce --- /dev/null +++ b/src/engine/platform/sound/su.cpp @@ -0,0 +1,261 @@ +#include "su.h" +#include + +#define minval(a,b) (((a)<(b))?(a):(b)) +#define maxval(a,b) (((a)>(b))?(a):(b)) + +void SoundUnit::NextSample(short* l, short* r) { + for (int i=0; i<8; i++) { + if (chan[i].vol==0 && !chan[i].flags.swvol) {fns[i]=0; continue;} + if (chan[i].flags.pcm) { + ns[i]=pcm[chan[i].pcmpos]; + } else switch (chan[i].flags.shape) { + case 0: + ns[i]=(((cycle[i]>>15)&127)>chan[i].duty)*127; + break; + case 1: + ns[i]=cycle[i]>>14; + break; + case 2: + ns[i]=SCsine[(cycle[i]>>14)&255]; + break; + case 3: + ns[i]=SCtriangle[(cycle[i]>>14)&255]; + break; + case 4: case 5: + ns[i]=(lfsr[i]&1)*127; + break; + case 6: + ns[i]=((((cycle[i]>>15)&127)>chan[i].duty)*127)^(short)SCsine[(cycle[i]>>14)&255]; + break; + case 7: + ns[i]=((((cycle[i]>>15)&127)>chan[i].duty)*127)^(short)SCtriangle[(cycle[i]>>14)&255]; + break; + } + + if (chan[i].flags.pcm) { + if (chan[i].freq>0x8000) { + pcmdec[i]+=0x8000; + } else { + pcmdec[i]+=chan[i].freq; + } + if (pcmdec[i]>=32768) { + pcmdec[i]-=32768; + if (chan[i].pcmpos>4)&3) { + case 0: + cycle[i]+=chan[i].freq*1-(chan[i].freq>>3); + break; + case 1: + cycle[i]+=chan[i].freq*2-(chan[i].freq>>3); + break; + case 2: + cycle[i]+=chan[i].freq*4-(chan[i].freq>>3); + break; + case 3: + cycle[i]+=chan[i].freq*8-(chan[i].freq>>3); + break; + } + } else { + cycle[i]+=chan[i].freq; + } + if ((cycle[i]&0xf80000)!=(ocycle[i]&0xf80000)) { + if (chan[i].flags.shape==4) { + lfsr[i]=(lfsr[i]>>1|(((lfsr[i]) ^ (lfsr[i] >> 2) ^ (lfsr[i] >> 3) ^ (lfsr[i] >> 5) ) & 1)<<31); + } else { + switch ((chan[i].duty>>4)&3) { + case 0: + lfsr[i]=(lfsr[i]>>1|(((lfsr[i] >> 3) ^ (lfsr[i] >> 4) ) & 1)<<5); + break; + case 1: + lfsr[i]=(lfsr[i]>>1|(((lfsr[i] >> 2) ^ (lfsr[i] >> 3) ) & 1)<<5); + break; + case 2: + lfsr[i]=(lfsr[i]>>1|(((lfsr[i]) ^ (lfsr[i] >> 2) ^ (lfsr[i] >> 3) ) & 1)<<5); + break; + case 3: + lfsr[i]=(lfsr[i]>>1|(((lfsr[i]) ^ (lfsr[i] >> 2) ^ (lfsr[i] >> 3) ^ (lfsr[i] >> 5) ) & 1)<<5); + break; + } + if ((lfsr[i]&63)==0) { + lfsr[i]=0xaaaa; + } + } + } + if (chan[i].flags.restim) { + if (--rcycle[i]<=0) { + cycle[i]=0; + rcycle[i]=chan[i].restimer; + lfsr[i]=0xaaaa; + } + } + } + fns[i]=ns[i]*chan[i].vol*2; + if (chan[i].flags.fmode!=0) { + int ff=chan[i].cutoff; + nslow[i]=nslow[i]+(((ff)*nsband[i])>>16); + nshigh[i]=fns[i]-nslow[i]-(((256-chan[i].reson)*nsband[i])>>8); + nsband[i]=(((ff)*nshigh[i])>>16)+nsband[i]; + fns[i]=(((chan[i].flags.fmode&1)?(nslow[i]):(0))+((chan[i].flags.fmode&2)?(nshigh[i]):(0))+((chan[i].flags.fmode&4)?(nsband[i]):(0))); + } + nsL[i]=(fns[i]*SCpantabL[(unsigned char)chan[i].pan])>>8; + nsR[i]=(fns[i]*SCpantabR[(unsigned char)chan[i].pan])>>8; + oldfreq[i]=chan[i].freq; + oldflags[i]=chan[i].flags.flags; + if (chan[i].flags.swvol) { + if (--swvolt[i]<=0) { + swvolt[i]=chan[i].swvol.speed; + if (chan[i].swvol.dir) { + chan[i].vol+=chan[i].swvol.amt; + if (chan[i].vol>chan[i].swvol.bound && !chan[i].swvol.loop) { + chan[i].vol=chan[i].swvol.bound; + } + if (chan[i].vol&0x80) { + if (chan[i].swvol.loop) { + if (chan[i].swvol.loopi) { + chan[i].swvol.dir=!chan[i].swvol.dir; + chan[i].vol=0xff-chan[i].vol; + } else { + chan[i].vol&=~0x80; + } + } else { + chan[i].vol=0x7f; + } + } + } else { + chan[i].vol-=chan[i].swvol.amt; + if (chan[i].vol&0x80) { + if (chan[i].swvol.loop) { + if (chan[i].swvol.loopi) { + chan[i].swvol.dir=!chan[i].swvol.dir; + chan[i].vol=-chan[i].vol; + } else { + chan[i].vol&=~0x80; + } + } else { + chan[i].vol=0x0; + } + } + if (chan[i].vol(0xffff-chan[i].swfreq.amt)) { + chan[i].freq=0xffff; + } else { + chan[i].freq=(chan[i].freq*(0x80+chan[i].swfreq.amt))>>7; + if ((chan[i].freq>>8)>chan[i].swfreq.bound) { + chan[i].freq=chan[i].swfreq.bound<<8; + } + } + } else { + if (chan[i].freq>8; + if ((chan[i].freq>>8)(0xffff-chan[i].swcut.amt)) { + chan[i].cutoff=0xffff; + } else { + chan[i].cutoff+=chan[i].swcut.amt; + if ((chan[i].cutoff>>8)>chan[i].swcut.bound) { + chan[i].cutoff=chan[i].swcut.bound<<8; + } + } + } else { + if (chan[i].cutoff>11; + if ((chan[i].cutoff>>8)>2; + tnsR=(nsR[0]+nsR[1]+nsR[2]+nsR[3]+nsR[4]+nsR[5]+nsR[6]+nsR[7])>>2; + + *l=minval(32767,maxval(-32767,tnsL)); + *r=minval(32767,maxval(-32767,tnsR)); +} + +void SoundUnit::Init() { + Reset(); + memset(pcm,0,SOUNDCHIP_PCM_SIZE); + for (int i=0; i<256; i++) { + SCsine[i]=sin((i/128.0f)*M_PI)*127; + SCtriangle[i]=(i>127)?(255-i):(i); + SCpantabL[i]=127; + SCpantabR[i]=127; + } + for (int i=0; i<128; i++) { + SCpantabL[i]=127-i; + SCpantabR[128+i]=i-1; + } + SCpantabR[128]=0; +} + +void SoundUnit::Reset() { + for (int i=0; i<8; i++) { + cycle[i]=0; + resetfreq[i]=0; + voldcycles[i]=0; + volicycles[i]=0; + fscycles[i]=0; + sweep[i]=0; + ns[i]=0; + swvolt[i]=1; + swfreqt[i]=1; + swcutt[i]=1; + lfsr[i]=0xaaaa; + } + memset(chan,0,sizeof(SUChannel)*8); +} + +void SoundUnit::Write(unsigned char addr, unsigned char data) { + ((unsigned char*)chan)[addr]=data; +} + +SoundUnit::SoundUnit() { + Init(); +} diff --git a/src/engine/platform/sound/su.h b/src/engine/platform/sound/su.h new file mode 100644 index 000000000..a201e955a --- /dev/null +++ b/src/engine/platform/sound/su.h @@ -0,0 +1,93 @@ +#include +#include +#include +#include + +#define SOUNDCHIP_PCM_SIZE 8192 + +class SoundUnit { + signed char SCsine[256]; + signed char SCtriangle[256]; + signed char SCpantabL[256]; + signed char SCpantabR[256]; + unsigned int ocycle[8]; + unsigned int cycle[8]; + int rcycle[8]; + unsigned int lfsr[8]; + signed char ns[8]; + int fns[8]; + int nsL[8]; + int nsR[8]; + int nslow[8]; + int nshigh[8]; + int nsband[8]; + int tnsL, tnsR; + unsigned short oldfreq[8]; + unsigned short oldflags[8]; + public: + unsigned short resetfreq[8]; + unsigned short voldcycles[8]; + unsigned short volicycles[8]; + unsigned short fscycles[8]; + unsigned char sweep[8]; + unsigned short swvolt[8]; + unsigned short swfreqt[8]; + unsigned short swcutt[8]; + unsigned short pcmdec[8]; + struct SUChannel { + unsigned short freq; + signed char vol; + signed char pan; + union { + unsigned short flags; + struct { + unsigned char shape: 3; + unsigned char pcm: 1; + unsigned char ring: 1; + unsigned char fmode: 3; + unsigned char resosc: 1; + unsigned char resfilt: 1; + unsigned char pcmloop: 1; + unsigned char restim: 1; + unsigned char swfreq: 1; + unsigned char swvol: 1; + unsigned char swcut: 1; + unsigned char padding: 1; + }; + } flags; + unsigned short cutoff; + unsigned char duty; + unsigned char reson; + unsigned short pcmpos; + unsigned short pcmbnd; + unsigned short pcmrst; + struct { + unsigned short speed; + unsigned char amt: 7; + unsigned char dir: 1; + unsigned char bound; + } swfreq; + struct { + unsigned short speed; + unsigned char amt: 5; + unsigned char dir: 1; + unsigned char loop: 1; + unsigned char loopi: 1; + unsigned char bound; + } swvol; + struct { + unsigned short speed; + unsigned char amt: 7; + unsigned char dir: 1; + unsigned char bound; + } swcut; + unsigned short wc; + unsigned short restimer; + } chan[8]; + signed char pcm[SOUNDCHIP_PCM_SIZE]; + void Write(unsigned char addr, unsigned char data); + void NextSample(short* l, short* r); + void Init(); + void Reset(); + SoundUnit(); +}; diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index ddd525920..6225b9293 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -2670,6 +2670,7 @@ bool FurnaceGUI::loop() { ImGui::Separator(); if (ImGui::BeginMenu("add system...")) { for (int j=0; availableSystems[j]; j++) { + if (!settings.hiddenSystems && availableSystems[j]==DIV_SYSTEM_YMU759) continue; sysAddOption((DivSystem)availableSystems[j]); } ImGui::EndMenu(); @@ -2687,6 +2688,7 @@ bool FurnaceGUI::loop() { for (int i=0; isong.systemLen; i++) { if (ImGui::BeginMenu(fmt::sprintf("%d. %s##_SYSC%d",i+1,getSystemName(e->song.system[i]),i).c_str())) { for (int j=0; availableSystems[j]; j++) { + if (!settings.hiddenSystems && availableSystems[j]==DIV_SYSTEM_YMU759) continue; sysChangeOption(i,(DivSystem)availableSystems[j]); } ImGui::EndMenu(); @@ -3886,6 +3888,7 @@ FurnaceGUI::FurnaceGUI(): lockLayout(false), editOptsVisible(false), latchNibble(false), + nonLatchNibble(false), curWindow(GUI_WINDOW_NOTHING), nextWindow(GUI_WINDOW_NOTHING), nextDesc(NULL), diff --git a/src/gui/gui.h b/src/gui/gui.h index 91c17b3f7..670718144 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -844,6 +844,7 @@ class FurnaceGUI { int absorbInsInput; int eventDelay; int moveWindowTitle; + int hiddenSystems; unsigned int maxUndoSteps; String mainFontPath; String patFontPath; @@ -921,6 +922,7 @@ class FurnaceGUI { absorbInsInput(0), eventDelay(0), moveWindowTitle(0), + hiddenSystems(0), maxUndoSteps(100), mainFontPath(""), patFontPath(""), @@ -949,7 +951,7 @@ class FurnaceGUI { SelectionPoint selStart, selEnd, cursor; bool selecting, curNibble, orderNibble, followOrders, followPattern, changeAllOrders; - bool collapseWindow, demandScrollX, fancyPattern, wantPatName, firstFrame, tempoView, waveHex, lockLayout, editOptsVisible, latchNibble; + bool collapseWindow, demandScrollX, fancyPattern, wantPatName, firstFrame, tempoView, waveHex, lockLayout, editOptsVisible, latchNibble, nonLatchNibble; FurnaceGUIWindows curWindow, nextWindow; float peak[2]; float patChanX[DIV_MAX_CHANS+1]; diff --git a/src/gui/guiConst.cpp b/src/gui/guiConst.cpp index b5d437072..ad49ad5f4 100644 --- a/src/gui/guiConst.cpp +++ b/src/gui/guiConst.cpp @@ -587,6 +587,7 @@ const int availableSystems[]={ DIV_SYSTEM_AY8910, DIV_SYSTEM_AMIGA, DIV_SYSTEM_PCSPKR, + DIV_SYSTEM_YMU759, DIV_SYSTEM_OPLL, DIV_SYSTEM_OPLL_DRUMS, DIV_SYSTEM_VRC7, diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index 61493722c..67b150721 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -952,6 +952,13 @@ void FurnaceGUI::drawSettings() { settings.oplStandardWaveNames=oplStandardWaveNamesB; } + if (nonLatchNibble) { + bool hiddenSystemsB=settings.hiddenSystems; + if (ImGui::Checkbox(":smile: :star_struck: :sunglasses: :ok_hand:",&hiddenSystemsB)) { + settings.hiddenSystems=hiddenSystemsB; + } + } + bool overflowHighlightB=settings.overflowHighlight; if (ImGui::Checkbox("Overflow pattern highlights",&overflowHighlightB)) { settings.overflowHighlight=overflowHighlightB; @@ -1668,6 +1675,7 @@ void FurnaceGUI::syncSettings() { settings.absorbInsInput=e->getConfInt("absorbInsInput",0); settings.eventDelay=e->getConfInt("eventDelay",0); settings.moveWindowTitle=e->getConfInt("moveWindowTitle",0); + settings.hiddenSystems=e->getConfInt("hiddenSystems",0); clampSetting(settings.mainFontSize,2,96); clampSetting(settings.patFontSize,2,96); @@ -1734,6 +1742,7 @@ void FurnaceGUI::syncSettings() { clampSetting(settings.absorbInsInput,0,1); clampSetting(settings.eventDelay,0,1); clampSetting(settings.moveWindowTitle,0,1); + clampSetting(settings.hiddenSystems,0,1); // keybinds for (int i=0; isetConf("absorbInsInput",settings.absorbInsInput); e->setConf("eventDelay",settings.eventDelay); e->setConf("moveWindowTitle",settings.moveWindowTitle); + e->setConf("hiddenSystems",settings.hiddenSystems); // colors for (int i=0; isong.name)) { MARK_MODIFIED updateWindowTitle(); } + if (e->song.name.size()==27) { + unsigned int checker=0x11111111; + unsigned int checker1=0; + for (int i=0; i<27; i++) { + checker^=e->song.name[i]<song.name[i]; + checker=(checker>>1|(((checker)^(checker>>2)^(checker>>3)^(checker>>5))&1)<<31); + checker1<<=1; + } + if (checker==0x94ffb4f7 && checker1==0x801c68a6) nonLatchNibble=true; + } ImGui::TableNextRow(); ImGui::TableNextColumn(); ImGui::Text("Author"); @@ -173,4 +184,4 @@ void FurnaceGUI::drawSongInfo() { } if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_SONG_INFO; ImGui::End(); -} \ No newline at end of file +} From 351c22cb774248c312aa5fe12f023dd9a35ad73f Mon Sep 17 00:00:00 2001 From: tildearrow Date: Tue, 26 Apr 2022 18:32:33 -0500 Subject: [PATCH 210/342] sysDef refactor, part 1 - PLEASE READ NOTE THIS WILL NOT COMPILE!!! i'm still working on it to @cam900: DO NOT PULL TO YOUR ES5506 BRANCH YET - wait until I am done with this refactor to @grauw: DO NOT PULL TO YOUR OPL4 BRANCH YET - wait until I am done with this refactor --- papers/format.md | 3 +- src/engine/engine.cpp | 3 + src/engine/engine.h | 92 ++++++++- src/engine/song.h | 12 +- src/engine/sysDef.cpp | 460 ++++++++++++++++++++++++++++++++++++++---- 5 files changed, 526 insertions(+), 44 deletions(-) diff --git a/papers/format.md b/papers/format.md index 083fd7662..0382c2d37 100644 --- a/papers/format.md +++ b/papers/format.md @@ -197,7 +197,7 @@ size | description | - 0xa6: Neo Geo extended (YM2610) - 17 channels | - 0xa7: OPLL drums (YM2413) - 11 channels | - 0xa8: Atari Lynx - 4 channels - | - 0xa9: SegaPCM (for Deflemask Compatibility) - 5 channels + | - 0xa9: SegaPCM (for DefleMask compatibility) - 5 channels | - 0xaa: MSM6295 - 4 channels | - 0xab: MSM6258 - 1 channel | - 0xac: Commander X16 (VERA) - 17 channels @@ -209,6 +209,7 @@ size | description | - 0xb2: Yamaha Y8950 - 10 channels | - 0xb3: Yamaha Y8950 drums - 12 channels | - 0xb4: Konami SCC+ - 5 channels + | - 0xb5: tildearrow Sound Unit - 8 channels | - 0xde: YM2610B extended - 19 channels | - 0xe0: QSound - 19 channels | - (compound!) means that the system is composed of two or more chips, diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index a8fd95a4a..212494fc9 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -2425,6 +2425,9 @@ bool DivEngine::deinitAudioBackend() { #endif bool DivEngine::init() { + // register systems + registerSystems(); + // init config #ifdef _WIN32 configPath=getWinConfigPath(); diff --git a/src/engine/engine.h b/src/engine/engine.h index 5bbb1060e..2b4caba38 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -27,6 +27,7 @@ #include "../audio/taAudio.h" #include "blip_buf.h" #include +#include #include #include #include @@ -177,6 +178,91 @@ struct DivDispatchContainer { dcOffCompensation(false) {} }; +typedef std::function EffectProcess; + +struct DivSysDef { + const char* name; + const char* nameJ; + unsigned char id; + unsigned char id_DMF; + int channels; + bool isFM, isSTD, isCompound; + unsigned char vgmVersion; + const char* chanNames[DIV_MAX_CHANS]; + const char* chanShortNames[DIV_MAX_CHANS]; + int chanTypes[DIV_MAX_CHANS]; + // 0: primary + // 1: alternate (usually PCM) + DivInstrumentType chanInsType[DIV_MAX_CHANS][2]; + EffectProcess effectFunc; + EffectProcess postEffectFunc; + DivSysDef( + const char* sysName, const char* sysNameJ, unsigned char fileID, unsigned char fileID_DMF, int chans, + bool isFMChip, bool isSTDChip, unsigned char vgmVer, bool compound, + std::initializer_list chNames, + std::initializer_list chShortNames, + std::initializer_list chTypes, + std::initializer_list chInsType1, + std::initializer_list chInsType2={}, + EffectProcess fxHandler=NULL, + EffectProcess postFxHandler=NULL): + name(sysName), + nameJ(sysNameJ), + id(fileID), + id_DMF(fileID_DMF), + channels(chans), + isFM(isFMChip), + isSTD(isSTDChip), + isCompound(compound), + vgmVersion(vgmVer), + effectFunc(fxHandler), + postEffectFunc(postFxHandler) { + memset(chanNames,0,DIV_MAX_CHANS*sizeof(void*)); + memset(chanShortNames,0,DIV_MAX_CHANS*sizeof(void*)); + memset(chanTypes,0,DIV_MAX_CHANS*sizeof(int)); + memset(chanInsType,0,DIV_MAX_CHANS*2*sizeof(DivInstrumentType)); + + int index=0; + for (const char* i: chNames) { + chanNames[index++]=i; + if (index>=DIV_MAX_CHANS) break; + } + + index=0; + for (const char* i: chShortNames) { + chanShortNames[index++]=i; + if (index>=DIV_MAX_CHANS) break; + } + + index=0; + for (int i: chTypes) { + chanTypes[index++]=i; + if (index>=DIV_MAX_CHANS) break; + } + + index=0; + for (DivInstrumentType i: chInsType1) { + chanInsType[index++][0]=i; + if (index>=DIV_MAX_CHANS) break; + } + + index=0; + for (DivInstrumentType i: chInsType2) { + chanInsType[index++][1]=i; + if (index>=DIV_MAX_CHANS) break; + } + } +}; + +enum DivChanTypes { + DIV_CH_FM=0, + DIV_CH_PULSE=1, + DIV_CH_NOISE=2, + DIV_CH_WAVE=3, + DIV_CH_PCM=4, + DIV_CH_OP=5 +}; + class DivEngine { DivDispatchContainer disCont[32]; TAAudio* output; @@ -230,6 +316,7 @@ class DivEngine { std::vector midiIns; std::vector midiOuts; std::vector cmdStream; + DivSysDef* sysDefs[256]; struct SamplePreview { int sample; @@ -298,6 +385,8 @@ class DivEngine { bool initAudioBackend(); bool deinitAudioBackend(); + void registerSystems(); + void exchangeIns(int one, int two); public: @@ -718,7 +807,7 @@ class DivEngine { // quit dispatch void quitDispatch(); - // initialize the engine. optionally provide an output file name. + // initialize the engine. bool init(); // terminate the engine. @@ -828,6 +917,7 @@ class DivEngine { memset(vibTable,0,64*sizeof(short)); memset(reversePitchTable,0,4096*sizeof(int)); memset(pitchTable,0,4096*sizeof(int)); + memset(sysDefs,0,256*sizeof(void*)); } }; #endif diff --git a/src/engine/song.h b/src/engine/song.h index 7c06a14a5..317ffa9b0 100644 --- a/src/engine/song.h +++ b/src/engine/song.h @@ -95,7 +95,17 @@ enum DivSystem { DIV_SYSTEM_YM2610B_EXT, DIV_SYSTEM_SEGAPCM_COMPAT, DIV_SYSTEM_X1_010, - DIV_SYSTEM_BUBSYS_WSG + DIV_SYSTEM_BUBSYS_WSG, + DIV_SYSTEM_OPL4, + DIV_SYSTEM_OPL4_DRUMS, + DIV_SYSTEM_ES5506, + DIV_SYSTEM_Y8950, + DIV_SYSTEM_Y8950_DRUMS, + DIV_SYSTEM_SCC_PLUS, + DIV_SYSTEM_SOUND_UNIT, + DIV_SYSTEM_MSM6295, + DIV_SYSTEM_MSM6258, + DIV_SYSTEM_DUMMY }; struct DivSong { diff --git a/src/engine/sysDef.cpp b/src/engine/sysDef.cpp index acd3cff25..da835bb37 100644 --- a/src/engine/sysDef.cpp +++ b/src/engine/sysDef.cpp @@ -1058,47 +1058,47 @@ const char* chanShortNames[38][32]={ }; const int chanTypes[41][32]={ - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4}, // YMU759 - {0, 0, 0, 0, 0, 0, 1, 1, 1, 2}, // Genesis - {0, 0, 5, 5, 5, 5, 0, 0, 0, 1, 1, 1, 2}, // Genesis (extended channel 3) - {1, 1, 1, 2}, // SMS - {1, 1, 3, 2}, // GB - {3, 3, 3, 3, 3, 3}, // PCE - {1, 1, 3, 2, 4}, // NES - {2, 2, 2}, // C64 - {0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4}, // Arcade - {0, 0, 0, 0, 1, 1, 1, 4, 4, 4, 4, 4, 4, 4}, // YM2610 - {0, 5, 5, 5, 5, 0, 0, 1, 1, 1, 4, 4, 4, 4, 4, 4, 4}, // YM2610 (extended channel 2) - {1, 1, 1}, // AY-3-8910 - {4, 4, 4, 4}, // Amiga - {0, 0, 0, 0, 0, 0, 0, 0}, // YM2151 - {0, 0, 0, 0, 0, 0}, // YM2612 - {3, 3}, // TIA - {1, 1, 1, 1, 1, 1}, // SAA1099 - {1, 1, 1}, // AY8930 - {1, 1, 1, 2}, // VIC-20 - {1}, // PET - {4, 4, 4, 4, 4, 4, 4, 4}, // SNES/N163/RF5C68 - {1, 1, 3}, // VRC6 - {0, 0, 0, 0, 0, 0, 0, 0, 0}, // OPLL/OPL/OPL2/VRC7 - {3}, // FDS - {1, 1, 4}, // MMC5 - {0, 0, 0, 1, 1, 1}, // OPN - {0, 0, 0, 0, 0, 0, 1, 1, 1, 2, 2, 2, 2, 2, 2, 4}, // PC-98 - {5, 0, 5, 0, 5, 0, 5, 0, 5, 0, 5, 0, 0, 0, 0, 0, 0, 0}, // OPL3 - {4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4}, // MultiPCM/QSound - {1}, // PC Speaker/Pokémon Mini - {3, 3, 3, 3, 3, 2}, // Virtual Boy/SCC - {0, 0, 0, 0, 0, 0, 1, 1, 1, 4, 4, 4, 4, 4, 4, 4}, // YM2610B - {0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2}, // OPLL/OPL/OPL2 drums - {5, 0, 5, 0, 5, 0, 5, 0, 5, 0, 5, 0, 0, 0, 0, 2, 2, 2, 2, 2}, // OPL3 drums - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, // OPL3 4-op (UNUSED) - {0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2}, // OPL3 4-op + drums (UNUSED) - {3, 3, 3, 3}, // Lynx - {0, 0, 5, 5, 5, 5, 0, 0, 0, 1, 1, 1, 4, 4, 4, 4, 4, 4, 4}, // YM2610B (extended channel 3) - {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4}, // VERA - {3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3}, // X1-010 - {3, 4, 3, 2}, // Swan + {DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_PCM}, // YMU759 + {DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_NOISE}, // Genesis + {DIV_CH_FM, DIV_CH_FM, DIV_CH_OP, DIV_CH_OP, DIV_CH_OP, DIV_CH_OP, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_NOISE}, // Genesis (extended channel 3) + {DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_NOISE}, // SMS + {DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_WAVE, DIV_CH_NOISE}, // GB + {DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE}, // PCE + {DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_WAVE, DIV_CH_NOISE, DIV_CH_PCM}, // NES + {DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE}, // C64 + {DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM}, // Arcade + {DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM}, // YM2610 + {DIV_CH_FM, DIV_CH_OP, DIV_CH_OP, DIV_CH_OP, DIV_CH_OP, DIV_CH_FM, DIV_CH_FM, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM}, // YM2610 (extended channel 2) + {DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE}, // AY-3-8910 + {DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM}, // Amiga + {DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM}, // YM2151 + {DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM}, // YM2612 + {DIV_CH_WAVE, DIV_CH_WAVE}, // TIA + {DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE}, // SAA1099 + {DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE}, // AY8930 + {DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_NOISE}, // VIC-20 + {DIV_CH_PULSE}, // PET + {DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM}, // SNES/N163/RF5C68 + {DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_WAVE}, // VRC6 + {DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM}, // OPLL/OPL/OPL2/VRC7 + {DIV_CH_WAVE}, // FDS + {DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PCM}, // MMC5 + {DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE}, // OPN + {DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_PCM}, // PC-98 + {DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM}, // OPL3 + {DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM}, // MultiPCM/QSound + {DIV_CH_PULSE}, // PC Speaker/Pokémon Mini + {DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_NOISE}, // Virtual Boy/SCC + {DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM}, // YM2610B + {DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE}, // OPLL/OPL/OPL2 drums + {DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE}, // OPL3 drums + {DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM}, // OPL3 4-op (UNUSED) + {DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE}, // OPL3 4-op + drums (UNUSED) + {DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE}, // Lynx + {DIV_CH_FM, DIV_CH_FM, DIV_CH_OP, DIV_CH_OP, DIV_CH_OP, DIV_CH_OP, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM}, // YM2610B (extended channel 3) + {DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PCM}, // VERA + {DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE}, // X1-010 + {DIV_CH_WAVE, DIV_CH_PCM, DIV_CH_WAVE, DIV_CH_NOISE}, // Swan }; const DivInstrumentType chanPrefType[47][28]={ @@ -1794,3 +1794,381 @@ int DivEngine::minVGMVersion(DivSystem which) { } return 0; } + +// define systems like: +// sysDefs[DIV_SYSTEM_ID]=new DivSysDef( +// "Name", "Name (japanese, optional)", fileID, fileID_DMF, channels, isFM, isSTD, vgmVersion, +// {"Channel Names", ...}, +// {"Channel Short Names", ...}, +// {chanTypes, ...}, +// {chanPreferInsType, ...}, +// {chanPreferInsType2, ...}, (optional) +// [this](int ch, unsigned char effect, unsigned char effectVal) -> bool {}, (effect handler, optional) +// [this](int ch, unsigned char effect, unsigned char effectVal) -> bool {} (post effect handler, optional) +// ); + +// TODO: EVERYTHING +void DivEngine::registerSystems() { + sysDefs[DIV_SYSTEM_YMU759]=new DivSysDef( + "Yamaha YMU759", NULL, 0x01, 0x01, 17, true, false, 0, false, + {"Channel 1", "Channel 2", "Channel 3", "Channel 4", "Channel 5", "Channel 6", "Channel 7", "Channel 8", "Channel 9", "Channel 10", "Channel 11", "Channel 12", "Channel 13", "Channel 14", "Channel 15", "Channel 16", "PCM" }, // name + {"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "PCM" }, // short + {DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_PCM}, // type + {DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM} // ins + ); + + sysDefs[DIV_SYSTEM_GENESIS]=new DivSysDef( + "Sega Genesis/Mega Drive", "セガメガドライブ", 0x02, 0x02, 10, true, true, 0, true, + {}, {}, {}, {} + ); + + sysDefs[DIV_SYSTEM_GENESIS_EXT]=new DivSysDef( + "Sega Genesis Extended Channel 3", NULL, 0x02, 0x02, 10, true, true, 0, true, + {}, {}, {}, {} + ); + + sysDefs[DIV_SYSTEM_SMS]=new DivSysDef( + "TI SN76489", NULL, 0x02, 0x02, 10, true, true, 0, true, + {}, {}, {}, {} + ); + + sysDefs[DIV_SYSTEM_SMS_OPLL]=new DivSysDef( + "Sega Master System + FM Expansion", NULL, 0x02, 0x02, 10, true, true, 0, true, + {}, {}, {}, {} + ); + + sysDefs[DIV_SYSTEM_GB]=new DivSysDef( + "Game Boy", NULL, 0x02, 0x02, 10, true, true, 0, true, + {}, {}, {}, {} + ); + + sysDefs[DIV_SYSTEM_PCE]=new DivSysDef( + "PC Engine/TurboGrafx-16", NULL, 0x02, 0x02, 10, true, true, 0, true, + {}, {}, {}, {} + ); + + sysDefs[DIV_SYSTEM_NES]=new DivSysDef( + "NES (Ricoh 2A03)", NULL, 0x02, 0x02, 10, true, true, 0, true, + {}, {}, {}, {} + ); + + sysDefs[DIV_SYSTEM_NES_VRC7]=new DivSysDef( + "NES + Konami VRC7", NULL, 0x02, 0x02, 10, true, true, 0, true, + {}, {}, {}, {} + ); + + sysDefs[DIV_SYSTEM_NES_FDS]=new DivSysDef( + "Famicom Disk System", NULL, 0x02, 0x02, 10, true, true, 0, true, + {}, {}, {}, {} + ); + + sysDefs[DIV_SYSTEM_C64_6581]=new DivSysDef( + "Commodore 64 (6581)", NULL, 0x02, 0x02, 10, true, true, 0, true, + {}, {}, {}, {} + ); + + sysDefs[DIV_SYSTEM_C64_8580]=new DivSysDef( + "Commodore 64 (8580)", NULL, 0x02, 0x02, 10, true, true, 0, true, + {}, {}, {}, {} + ); + + sysDefs[DIV_SYSTEM_ARCADE]=new DivSysDef( + "DefleCade", NULL, 0x02, 0x02, 10, true, true, 0, true, + {}, {}, {}, {} + ); + + sysDefs[DIV_SYSTEM_YM2610]=new DivSysDef( + "Neo Geo CD", NULL, 0x02, 0x02, 10, true, true, 0, true, + {}, {}, {}, {} + ); + + sysDefs[DIV_SYSTEM_YM2610_EXT]=new DivSysDef( + "Neo Geo CD Extended Channel 2", NULL, 0x02, 0x02, 10, true, true, 0, true, + {}, {}, {}, {} + ); + + sysDefs[DIV_SYSTEM_AY8910]=new DivSysDef( + "AY-3-8910", NULL, 0x02, 0x02, 10, true, true, 0, true, + {}, {}, {}, {} + ); + + sysDefs[DIV_SYSTEM_AMIGA]=new DivSysDef( + "Amiga", NULL, 0x02, 0x02, 10, true, true, 0, true, + {}, {}, {}, {} + ); + + sysDefs[DIV_SYSTEM_YM2151]=new DivSysDef( + "Yamaha YM2151", NULL, 0x02, 0x02, 10, true, true, 0, true, + {}, {}, {}, {} + ); + + sysDefs[DIV_SYSTEM_YM2612]=new DivSysDef( + "Yamaha YM2612", NULL, 0x02, 0x02, 10, true, true, 0, true, + {}, {}, {}, {} + ); + + sysDefs[DIV_SYSTEM_TIA]=new DivSysDef( + "Atari 2600", NULL, 0x02, 0x02, 10, true, true, 0, true, + {}, {}, {}, {} + ); + + sysDefs[DIV_SYSTEM_SAA1099]=new DivSysDef( + "Philips SAA1099", NULL, 0x02, 0x02, 10, true, true, 0, true, + {}, {}, {}, {} + ); + + sysDefs[DIV_SYSTEM_AY8930]=new DivSysDef( + "Microchip AY8930", NULL, 0x02, 0x02, 10, true, true, 0, true, + {}, {}, {}, {} + ); + + sysDefs[DIV_SYSTEM_VIC20]=new DivSysDef( + "Commodore VIC-20", NULL, 0x02, 0x02, 10, true, true, 0, true, + {}, {}, {}, {} + ); + + sysDefs[DIV_SYSTEM_PET]=new DivSysDef( + "Commodore PET", NULL, 0x02, 0x02, 10, true, true, 0, true, + {}, {}, {}, {} + ); + + sysDefs[DIV_SYSTEM_SNES]=new DivSysDef( + "SNES", NULL, 0x02, 0x02, 10, true, true, 0, true, + {}, {}, {}, {} + ); + + sysDefs[DIV_SYSTEM_VRC6]=new DivSysDef( + "Konami VRC6", NULL, 0x02, 0x02, 10, true, true, 0, true, + {}, {}, {}, {} + ); + + sysDefs[DIV_SYSTEM_OPLL]=new DivSysDef( + "Yamaha OPLL", NULL, 0x02, 0x02, 10, true, true, 0, true, + {}, {}, {}, {} + ); + + sysDefs[DIV_SYSTEM_FDS]=new DivSysDef( + "Famicom Disk System (chip)", NULL, 0x02, 0x02, 10, true, true, 0, true, + {}, {}, {}, {} + ); + + sysDefs[DIV_SYSTEM_MMC5]=new DivSysDef( + "MMC5", NULL, 0x02, 0x02, 10, true, true, 0, true, + {}, {}, {}, {} + ); + + sysDefs[DIV_SYSTEM_N163]=new DivSysDef( + "Namco 163", NULL, 0x02, 0x02, 10, true, true, 0, true, + {}, {}, {}, {} + ); + + sysDefs[DIV_SYSTEM_OPN]=new DivSysDef( + "Yamaha YM2203", NULL, 0x02, 0x02, 10, true, true, 0, true, + {}, {}, {}, {} + ); + + sysDefs[DIV_SYSTEM_PC98]=new DivSysDef( + "Yamaha YM2608", NULL, 0x02, 0x02, 10, true, true, 0, true, + {}, {}, {}, {} + ); + + sysDefs[DIV_SYSTEM_OPL]=new DivSysDef( + "Yamaha OPL", NULL, 0x02, 0x02, 10, true, true, 0, true, + {}, {}, {}, {} + ); + + sysDefs[DIV_SYSTEM_OPL2]=new DivSysDef( + "Yamaha OPL2", NULL, 0x02, 0x02, 10, true, true, 0, true, + {}, {}, {}, {} + ); + + sysDefs[DIV_SYSTEM_OPL3]=new DivSysDef( + "Yamaha OPL3", NULL, 0x02, 0x02, 10, true, true, 0, true, + {}, {}, {}, {} + ); + + sysDefs[DIV_SYSTEM_MULTIPCM]=new DivSysDef( + "MultiPCM", NULL, 0x02, 0x02, 10, true, true, 0, true, + {}, {}, {}, {} + ); + + sysDefs[DIV_SYSTEM_PCSPKR]=new DivSysDef( + "PC Speaker", NULL, 0x02, 0x02, 10, true, true, 0, true, + {}, {}, {}, {} + ); + + sysDefs[DIV_SYSTEM_POKEY]=new DivSysDef( + "POKEY", NULL, 0x02, 0x02, 10, true, true, 0, true, + {}, {}, {}, {} + ); + + sysDefs[DIV_SYSTEM_RF5C68]=new DivSysDef( + "Ricoh RF5C68", NULL, 0x02, 0x02, 10, true, true, 0, true, + {}, {}, {}, {} + ); + + sysDefs[DIV_SYSTEM_SWAN]=new DivSysDef( + "WonderSwan", NULL, 0x02, 0x02, 10, true, true, 0, true, + {}, {}, {}, {} + ); + + sysDefs[DIV_SYSTEM_OPZ]=new DivSysDef( + "Yamaha TX81Z/YS200", NULL, 0x02, 0x02, 10, true, true, 0, true, + {}, {}, {}, {} + ); + + sysDefs[DIV_SYSTEM_POKEMINI]=new DivSysDef( + "Pokémon Mini", NULL, 0x02, 0x02, 10, true, true, 0, true, + {}, {}, {}, {} + ); + + sysDefs[DIV_SYSTEM_SEGAPCM]=new DivSysDef( + "SegaPCM", NULL, 0x02, 0x02, 10, true, true, 0, true, + {}, {}, {}, {} + ); + + sysDefs[DIV_SYSTEM_VBOY]=new DivSysDef( + "Virtual Boy", NULL, 0x02, 0x02, 10, true, true, 0, true, + {}, {}, {}, {} + ); + + sysDefs[DIV_SYSTEM_VRC7]=new DivSysDef( + "Konami VRC7", NULL, 0x02, 0x02, 10, true, true, 0, true, + {}, {}, {}, {} + ); + + sysDefs[DIV_SYSTEM_YM2610B]=new DivSysDef( + "Yamaha YM2610B", NULL, 0x02, 0x02, 10, true, true, 0, true, + {}, {}, {}, {} + ); + + sysDefs[DIV_SYSTEM_SFX_BEEPER]=new DivSysDef( + "ZX Spectrum Beeper", NULL, 0x02, 0x02, 10, true, true, 0, true, + {}, {}, {}, {} + ); + + sysDefs[DIV_SYSTEM_YM2612_EXT]=new DivSysDef( + "Yamaha YM2612 Extended Channel 3", NULL, 0x02, 0x02, 10, true, true, 0, true, + {}, {}, {}, {} + ); + + sysDefs[DIV_SYSTEM_SCC]=new DivSysDef( + "Konami SCC", NULL, 0x02, 0x02, 10, true, true, 0, true, + {}, {}, {}, {} + ); + + sysDefs[DIV_SYSTEM_OPL_DRUMS]=new DivSysDef( + "Yamaha OPL with drums", NULL, 0x02, 0x02, 10, true, true, 0, true, + {}, {}, {}, {} + ); + + sysDefs[DIV_SYSTEM_OPL2_DRUMS]=new DivSysDef( + "Yamaha OPL2 with drums", NULL, 0x02, 0x02, 10, true, true, 0, true, + {}, {}, {}, {} + ); + + sysDefs[DIV_SYSTEM_OPL3_DRUMS]=new DivSysDef( + "Yamaha OPL3 with drums", NULL, 0x02, 0x02, 10, true, true, 0, true, + {}, {}, {}, {} + ); + + sysDefs[DIV_SYSTEM_YM2610_FULL]=new DivSysDef( + "Yamaha YM2610", NULL, 0x02, 0x02, 10, true, true, 0, true, + {}, {}, {}, {} + ); + + sysDefs[DIV_SYSTEM_YM2610_FULL_EXT]=new DivSysDef( + "Yamaha YM2610 Extended Channel 2", NULL, 0x02, 0x02, 10, true, true, 0, true, + {}, {}, {}, {} + ); + + sysDefs[DIV_SYSTEM_OPLL_DRUMS]=new DivSysDef( + "Yamaha OPLL with drums", NULL, 0x02, 0x02, 10, true, true, 0, true, + {}, {}, {}, {} + ); + + sysDefs[DIV_SYSTEM_LYNX]=new DivSysDef( + "Atari Lynx", NULL, 0x02, 0x02, 10, true, true, 0, true, + {}, {}, {}, {} + ); + + sysDefs[DIV_SYSTEM_QSOUND]=new DivSysDef( + "Capcom QSound", NULL, 0x02, 0x02, 10, true, true, 0, true, + {}, {}, {}, {} + ); + + sysDefs[DIV_SYSTEM_VERA]=new DivSysDef( + "VERA", NULL, 0x02, 0x02, 10, true, true, 0, true, + {}, {}, {}, {} + ); + + sysDefs[DIV_SYSTEM_YM2610B_EXT]=new DivSysDef( + "Yamaha YM2610B Extended Channel 3", NULL, 0x02, 0x02, 10, true, true, 0, true, + {}, {}, {}, {} + ); + + sysDefs[DIV_SYSTEM_SEGAPCM_COMPAT]=new DivSysDef( + "SegaPCM (compatible 5-channel mode)", NULL, 0x02, 0x02, 10, true, true, 0, true, + {}, {}, {}, {} + ); + + sysDefs[DIV_SYSTEM_X1_010]=new DivSysDef( + "Seta/Allumer X1-010", NULL, 0x02, 0x02, 10, true, true, 0, true, + {}, {}, {}, {} + ); + + sysDefs[DIV_SYSTEM_BUBSYS_WSG]=new DivSysDef( + "Konami Bubble System WSG", NULL, 0x02, 0x02, 10, true, true, 0, true, + {}, {}, {}, {} + ); + + sysDefs[DIV_SYSTEM_OPL4]=new DivSysDef( + "Yamaha OPL4", NULL, 0x02, 0x02, 10, true, true, 0, true, + {}, {}, {}, {} + ); + + sysDefs[DIV_SYSTEM_OPL4_DRUMS]=new DivSysDef( + "Yamaha OPL4 with drums", NULL, 0x02, 0x02, 10, true, true, 0, true, + {}, {}, {}, {} + ); + + sysDefs[DIV_SYSTEM_ES5506]=new DivSysDef( + "Ensoniq ES5506", NULL, 0x02, 0x02, 10, true, true, 0, true, + {}, {}, {}, {} + ); + + sysDefs[DIV_SYSTEM_Y8950]=new DivSysDef( + "Yamaha Y8950", NULL, 0x02, 0x02, 10, true, true, 0, true, + {}, {}, {}, {} + ); + + sysDefs[DIV_SYSTEM_Y8950_DRUMS]=new DivSysDef( + "Yamaha Y8950 with drums", NULL, 0x02, 0x02, 10, true, true, 0, true, + {}, {}, {}, {} + ); + + sysDefs[DIV_SYSTEM_SCC_PLUS]=new DivSysDef( + "Konami SCC+", NULL, 0x02, 0x02, 10, true, true, 0, true, + {}, {}, {}, {} + ); + + sysDefs[DIV_SYSTEM_SOUND_UNIT]=new DivSysDef( + "tildearrow Sound Unit", NULL, 0x02, 0x02, 10, true, true, 0, true, + {}, {}, {}, {} + ); + + sysDefs[DIV_SYSTEM_MSM6295]=new DivSysDef( + "OKI MSM6295", NULL, 0x02, 0x02, 10, true, true, 0, true, + {}, {}, {}, {} + ); + + sysDefs[DIV_SYSTEM_MSM6258]=new DivSysDef( + "OKI MSM6258", NULL, 0x02, 0x02, 10, true, true, 0, true, + {}, {}, {}, {} + ); + + sysDefs[DIV_SYSTEM_DUMMY]=new DivSysDef( + "Dummy System", NULL, 0x02, 0x02, 10, true, true, 0, true, + {}, {}, {}, {} + ); +} From 24d60507e7124595a4cc92bd37db3b1cf403eb08 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 27 Apr 2022 00:56:15 -0500 Subject: [PATCH 211/342] sysDef refactor, part 2 to @cam900 and @grauw: you may now pull this to your branch. check out the comments in sysDef.cpp though. --- papers/format.md | 7 + src/engine/dispatch.h | 4 +- src/engine/dispatchContainer.cpp | 3 + src/engine/engine.cpp | 2 +- src/engine/engine.h | 20 +- src/engine/fileOps.cpp | 4 + src/engine/instrument.h | 5 + src/engine/platform/dummy.cpp | 38 +- src/engine/platform/dummy.h | 3 +- src/engine/sysDef.cpp | 2011 ++++++------------------------ src/gui/dataList.cpp | 16 + src/gui/gui.cpp | 6 +- src/gui/gui.h | 4 + src/gui/guiConst.cpp | 12 +- src/gui/settings.cpp | 4 + 15 files changed, 495 insertions(+), 1644 deletions(-) diff --git a/papers/format.md b/papers/format.md index 0382c2d37..8d977e84c 100644 --- a/papers/format.md +++ b/papers/format.md @@ -212,6 +212,9 @@ size | description | - 0xb5: tildearrow Sound Unit - 8 channels | - 0xde: YM2610B extended - 19 channels | - 0xe0: QSound - 19 channels + | - 0xfd: Dummy System - 8 channels + | - 0xfe: reserved for development + | - 0xff: reserved for development | - (compound!) means that the system is composed of two or more chips, | and has to be flattened. 32 | sound chip volumes @@ -329,6 +332,10 @@ size | description | - 24: VERA | - 25: X1-010 | - 26: VRC6 (saw) + | - 27: ES5506 + | - 28: MultiPCM + | - 29: SNES + | - 30: Sound Unit 1 | reserved STR | instrument name --- | **FM instrument data** diff --git a/src/engine/dispatch.h b/src/engine/dispatch.h index 307ad2f16..96984b678 100644 --- a/src/engine/dispatch.h +++ b/src/engine/dispatch.h @@ -430,9 +430,9 @@ class DivDispatch { /** * quit the DivDispatch. */ - virtual void quit(); + virtual void quit(); - virtual ~DivDispatch(); + virtual ~DivDispatch(); }; // pitch calculation: diff --git a/src/engine/dispatchContainer.cpp b/src/engine/dispatchContainer.cpp index 4f4ecf398..7c821b4a4 100644 --- a/src/engine/dispatchContainer.cpp +++ b/src/engine/dispatchContainer.cpp @@ -311,6 +311,9 @@ void DivDispatchContainer::init(DivSystem sys, DivEngine* eng, int chanCount, do case DIV_SYSTEM_MMC5: dispatch=new DivPlatformMMC5; break; + case DIV_SYSTEM_DUMMY: + dispatch=new DivPlatformDummy; + break; default: logW("this system is not supported yet! using dummy platform."); dispatch=new DivPlatformDummy; diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 212494fc9..85831839e 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -2426,7 +2426,7 @@ bool DivEngine::deinitAudioBackend() { bool DivEngine::init() { // register systems - registerSystems(); + if (!systemsRegistered) registerSystems(); // init config #ifdef _WIN32 diff --git a/src/engine/engine.h b/src/engine/engine.h index 2b4caba38..1e460d81d 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -44,8 +44,8 @@ #define BUSY_BEGIN_SOFT softLocked=true; isBusy.lock(); #define BUSY_END isBusy.unlock(); softLocked=false; -#define DIV_VERSION "dev87" -#define DIV_ENGINE_VERSION 87 +#define DIV_VERSION "dev88" +#define DIV_ENGINE_VERSION 88 // for imports #define DIV_VERSION_MOD 0xff01 @@ -187,7 +187,7 @@ struct DivSysDef { unsigned char id_DMF; int channels; bool isFM, isSTD, isCompound; - unsigned char vgmVersion; + unsigned int vgmVersion; const char* chanNames[DIV_MAX_CHANS]; const char* chanShortNames[DIV_MAX_CHANS]; int chanTypes[DIV_MAX_CHANS]; @@ -198,7 +198,7 @@ struct DivSysDef { EffectProcess postEffectFunc; DivSysDef( const char* sysName, const char* sysNameJ, unsigned char fileID, unsigned char fileID_DMF, int chans, - bool isFMChip, bool isSTDChip, unsigned char vgmVer, bool compound, + bool isFMChip, bool isSTDChip, unsigned int vgmVer, bool compound, std::initializer_list chNames, std::initializer_list chShortNames, std::initializer_list chTypes, @@ -290,6 +290,7 @@ class DivEngine { bool skipping; bool midiIsDirect; bool lowLatency; + bool systemsRegistered; int softLockCount; int subticks, ticks, curRow, curOrder, remainingLoops, nextSpeed; double divider; @@ -317,6 +318,8 @@ class DivEngine { std::vector midiOuts; std::vector cmdStream; DivSysDef* sysDefs[256]; + DivSystem sysFileMapFur[256]; + DivSystem sysFileMapDMF[256]; struct SamplePreview { int sample; @@ -519,9 +522,6 @@ class DivEngine { // get sys name const char* getSystemName(DivSystem sys); - // get sys chips - const char* getSystemChips(DivSystem sys); - // get japanese system name const char* getSystemNameJ(DivSystem sys); @@ -850,6 +850,7 @@ class DivEngine { skipping(false), midiIsDirect(false), lowLatency(false), + systemsRegistered(false), softLockCount(0), subticks(0), ticks(0), @@ -918,6 +919,11 @@ class DivEngine { memset(reversePitchTable,0,4096*sizeof(int)); memset(pitchTable,0,4096*sizeof(int)); memset(sysDefs,0,256*sizeof(void*)); + + for (int i=0; i<256; i++) { + sysFileMapFur[i]=DIV_SYSTEM_NULL; + sysFileMapDMF[i]=DIV_SYSTEM_NULL; + } } }; #endif diff --git a/src/engine/fileOps.cpp b/src/engine/fileOps.cpp index ca2f2d10e..3996f2d0b 100644 --- a/src/engine/fileOps.cpp +++ b/src/engine/fileOps.cpp @@ -1101,9 +1101,11 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) { return false; } + logD("systems:"); for (int i=0; i<32; i++) { unsigned char sysID=reader.readC(); ds.system[i]=systemFromFileFur(sysID); + logD("- %d: %.2x (%s)",i,sysID,getSystemName(ds.system[i])); if (sysID!=0 && systemToFileFur(ds.system[i])==0) { logE("unrecognized system ID %.2x",ds.system[i]); lastError=fmt::sprintf("unrecognized system ID %.2x!",ds.system[i]); @@ -2023,6 +2025,8 @@ bool DivEngine::load(unsigned char* f, size_t slen) { return false; } + if (!systemsRegistered) registerSystems(); + // step 1: try loading as a zlib-compressed file logD("trying zlib..."); try { diff --git a/src/engine/instrument.h b/src/engine/instrument.h index d9e943866..0ca780043 100644 --- a/src/engine/instrument.h +++ b/src/engine/instrument.h @@ -54,7 +54,12 @@ enum DivInstrumentType: unsigned short { DIV_INS_VERA=24, DIV_INS_X1_010=25, DIV_INS_VRC6_SAW=26, + DIV_INS_ES5506=27, + DIV_INS_MULTIPCM=28, + DIV_INS_SNES=29, + DIV_INS_SU=30, DIV_INS_MAX, + DIV_INS_NULL }; // FM operator structure: diff --git a/src/engine/platform/dummy.cpp b/src/engine/platform/dummy.cpp index e751e3b6f..9b972ac18 100644 --- a/src/engine/platform/dummy.cpp +++ b/src/engine/platform/dummy.cpp @@ -22,15 +22,20 @@ #include #include +#define CHIP_FREQBASE 2048 + void DivPlatformDummy::acquire(short* bufL, short* bufR, size_t start, size_t len) { for (size_t i=start; i>13; + if (!isMuted[j]) out+=(((signed short)chan[j].pos)*chan[j].amp*chan[j].vol)>>12; chan[j].pos+=chan[j].freq; } } + if (out<-32768) out=-32768; + if (out>32767) out=32767; + bufL[i]=out; } } @@ -41,8 +46,8 @@ void DivPlatformDummy::muteChannel(int ch, bool mute) { void DivPlatformDummy::tick(bool sysTick) { for (unsigned char i=0; ichan[c.chan].baseFreq) { + chan[c.chan].baseFreq+=c.value; + if (chan[c.chan].baseFreq>=destFreq) { + chan[c.chan].baseFreq=destFreq; + return2=true; + } + } else { + chan[c.chan].baseFreq-=c.value; + if (chan[c.chan].baseFreq<=destFreq) { + chan[c.chan].baseFreq=destFreq; + return2=true; + } + } + chan[c.chan].freqChanged=true; + if (return2) return 2; + break; + } case DIV_CMD_LEGATO: - chan[c.chan].baseFreq=65.6f*pow(2.0f,((float)c.value/12.0f)); + chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value); chan[c.chan].freqChanged=true; break; case DIV_CMD_GET_VOLMAX: @@ -108,6 +133,7 @@ int DivPlatformDummy::init(DivEngine* p, int channels, int sugRate, unsigned int isMuted[i]=false; } rate=65536; + chipClock=65536; chans=channels; reset(); return channels; diff --git a/src/engine/platform/dummy.h b/src/engine/platform/dummy.h index aa38b33bf..ff24df622 100644 --- a/src/engine/platform/dummy.h +++ b/src/engine/platform/dummy.h @@ -23,8 +23,7 @@ // used when a DivDispatch for a system is not found. class DivPlatformDummy: public DivDispatch { struct Channel { - unsigned short freq, baseFreq; - short pitch; + int freq, baseFreq, pitch; unsigned short pos; bool active, freqChanged; unsigned char vol; diff --git a/src/engine/sysDef.cpp b/src/engine/sysDef.cpp index da835bb37..2c5e3c73d 100644 --- a/src/engine/sysDef.cpp +++ b/src/engine/sysDef.cpp @@ -19,464 +19,29 @@ #include "engine.h" #include "song.h" +#include "../ta-log.h" DivSystem DivEngine::systemFromFileFur(unsigned char val) { - switch (val) { - case 0x01: - return DIV_SYSTEM_YMU759; - case 0x02: - return DIV_SYSTEM_GENESIS; - case 0x03: - return DIV_SYSTEM_SMS; - case 0x04: - return DIV_SYSTEM_GB; - case 0x05: - return DIV_SYSTEM_PCE; - case 0x06: - return DIV_SYSTEM_NES; - case 0x07: - return DIV_SYSTEM_C64_8580; - case 0x08: - return DIV_SYSTEM_ARCADE; - case 0x09: - return DIV_SYSTEM_YM2610; - case 0x42: - return DIV_SYSTEM_GENESIS_EXT; - case 0x43: - return DIV_SYSTEM_SMS_OPLL; - case 0x46: - return DIV_SYSTEM_NES_VRC7; - case 0x47: - return DIV_SYSTEM_C64_6581; - case 0x49: - return DIV_SYSTEM_YM2610_EXT; - case 0x80: - return DIV_SYSTEM_AY8910; - case 0x81: - return DIV_SYSTEM_AMIGA; - case 0x82: - return DIV_SYSTEM_YM2151; - case 0x83: - return DIV_SYSTEM_YM2612; - case 0x84: - return DIV_SYSTEM_TIA; - case 0x85: - return DIV_SYSTEM_VIC20; - case 0x86: - return DIV_SYSTEM_PET; - case 0x87: - return DIV_SYSTEM_SNES; - case 0x88: - return DIV_SYSTEM_VRC6; - case 0x89: - return DIV_SYSTEM_OPLL; - case 0x8a: - return DIV_SYSTEM_FDS; - case 0x8b: - return DIV_SYSTEM_MMC5; - case 0x8c: - return DIV_SYSTEM_N163; - case 0x8d: - return DIV_SYSTEM_OPN; - case 0x8e: - return DIV_SYSTEM_PC98; - case 0x8f: - return DIV_SYSTEM_OPL; - case 0x90: - return DIV_SYSTEM_OPL2; - case 0x91: - return DIV_SYSTEM_OPL3; - case 0x92: - return DIV_SYSTEM_MULTIPCM; - case 0x93: - return DIV_SYSTEM_PCSPKR; - case 0x94: - return DIV_SYSTEM_POKEY; - case 0x95: - return DIV_SYSTEM_RF5C68; - case 0x96: - return DIV_SYSTEM_SWAN; - case 0x97: - return DIV_SYSTEM_SAA1099; - case 0x98: - return DIV_SYSTEM_OPZ; - case 0x99: - return DIV_SYSTEM_POKEMINI; - case 0x9a: - return DIV_SYSTEM_AY8930; - case 0x9b: - return DIV_SYSTEM_SEGAPCM; - case 0x9c: - return DIV_SYSTEM_VBOY; - case 0x9d: - return DIV_SYSTEM_VRC7; - case 0x9e: - return DIV_SYSTEM_YM2610B; - case 0x9f: - return DIV_SYSTEM_SFX_BEEPER; - case 0xa0: - return DIV_SYSTEM_YM2612_EXT; - case 0xa1: - return DIV_SYSTEM_SCC; - case 0xa2: - return DIV_SYSTEM_OPL_DRUMS; - case 0xa3: - return DIV_SYSTEM_OPL2_DRUMS; - case 0xa4: - return DIV_SYSTEM_OPL3_DRUMS; - case 0xa5: - return DIV_SYSTEM_YM2610_FULL; - case 0xa6: - return DIV_SYSTEM_YM2610_FULL_EXT; - case 0xa7: - return DIV_SYSTEM_OPLL_DRUMS; - case 0xa8: - return DIV_SYSTEM_LYNX; - case 0xa9: - return DIV_SYSTEM_SEGAPCM_COMPAT; - case 0xac: - return DIV_SYSTEM_VERA; - case 0xad: - return DIV_SYSTEM_BUBSYS_WSG; - case 0xb0: - return DIV_SYSTEM_X1_010; - case 0xde: - return DIV_SYSTEM_YM2610B_EXT; - case 0xe0: - return DIV_SYSTEM_QSOUND; - } - return DIV_SYSTEM_NULL; + return sysFileMapFur[val]; } unsigned char DivEngine::systemToFileFur(DivSystem val) { - switch (val) { - case DIV_SYSTEM_YMU759: - return 0x01; - case DIV_SYSTEM_GENESIS: - return 0x02; - case DIV_SYSTEM_SMS: - return 0x03; - case DIV_SYSTEM_GB: - return 0x04; - case DIV_SYSTEM_PCE: - return 0x05; - case DIV_SYSTEM_NES: - return 0x06; - case DIV_SYSTEM_C64_8580: - return 0x07; - case DIV_SYSTEM_ARCADE: - return 0x08; - case DIV_SYSTEM_YM2610: - return 0x09; - case DIV_SYSTEM_GENESIS_EXT: - return 0x42; - case DIV_SYSTEM_SMS_OPLL: - return 0x43; - case DIV_SYSTEM_NES_VRC7: - return 0x46; - case DIV_SYSTEM_NES_FDS: - return 0; // unsupported - case DIV_SYSTEM_C64_6581: - return 0x47; - case DIV_SYSTEM_YM2610_EXT: - return 0x49; - case DIV_SYSTEM_AY8910: - return 0x80; - case DIV_SYSTEM_AMIGA: - return 0x81; - case DIV_SYSTEM_YM2151: - return 0x82; - case DIV_SYSTEM_YM2612: - return 0x83; - case DIV_SYSTEM_TIA: - return 0x84; - case DIV_SYSTEM_VIC20: - return 0x85; - case DIV_SYSTEM_PET: - return 0x86; - case DIV_SYSTEM_SNES: - return 0x87; - case DIV_SYSTEM_VRC6: - return 0x88; - case DIV_SYSTEM_OPLL: - return 0x89; - case DIV_SYSTEM_FDS: - return 0x8a; - case DIV_SYSTEM_MMC5: - return 0x8b; - case DIV_SYSTEM_N163: - return 0x8c; - case DIV_SYSTEM_OPN: - return 0x8d; - case DIV_SYSTEM_PC98: - return 0x8e; - case DIV_SYSTEM_OPL: - return 0x8f; - case DIV_SYSTEM_OPL2: - return 0x90; - case DIV_SYSTEM_OPL3: - return 0x91; - case DIV_SYSTEM_MULTIPCM: - return 0x92; - case DIV_SYSTEM_PCSPKR: - return 0x93; - case DIV_SYSTEM_POKEY: - return 0x94; - case DIV_SYSTEM_RF5C68: - return 0x95; - case DIV_SYSTEM_SWAN: - return 0x96; - case DIV_SYSTEM_SAA1099: - return 0x97; - case DIV_SYSTEM_OPZ: - return 0x98; - case DIV_SYSTEM_POKEMINI: - return 0x99; - case DIV_SYSTEM_AY8930: - return 0x9a; - case DIV_SYSTEM_SEGAPCM: - return 0x9b; - case DIV_SYSTEM_VBOY: - return 0x9c; - case DIV_SYSTEM_VRC7: - return 0x9d; - case DIV_SYSTEM_YM2610B: - return 0x9e; - case DIV_SYSTEM_SFX_BEEPER: - return 0x9f; - case DIV_SYSTEM_YM2612_EXT: - return 0xa0; - case DIV_SYSTEM_SCC: - return 0xa1; - case DIV_SYSTEM_OPL_DRUMS: - return 0xa2; - case DIV_SYSTEM_OPL2_DRUMS: - return 0xa3; - case DIV_SYSTEM_OPL3_DRUMS: - return 0xa4; - case DIV_SYSTEM_YM2610_FULL: - return 0xa5; - case DIV_SYSTEM_YM2610_FULL_EXT: - return 0xa6; - case DIV_SYSTEM_OPLL_DRUMS: - return 0xa7; - case DIV_SYSTEM_LYNX: - return 0xa8; - case DIV_SYSTEM_SEGAPCM_COMPAT: - return 0xa9; - case DIV_SYSTEM_VERA: - return 0xac; - case DIV_SYSTEM_BUBSYS_WSG: - return 0xad; - case DIV_SYSTEM_X1_010: - return 0xb0; - case DIV_SYSTEM_YM2610B_EXT: - return 0xde; - case DIV_SYSTEM_QSOUND: - return 0xe0; - - case DIV_SYSTEM_NULL: - return 0; - } - return 0; + if (sysDefs[val]==NULL) return 0; + return sysDefs[val]->id; } DivSystem DivEngine::systemFromFileDMF(unsigned char val) { - switch (val) { - case 0x01: - return DIV_SYSTEM_YMU759; - case 0x02: - return DIV_SYSTEM_GENESIS; - case 0x03: - return DIV_SYSTEM_SMS; - case 0x04: - return DIV_SYSTEM_GB; - case 0x05: - return DIV_SYSTEM_PCE; - case 0x06: - return DIV_SYSTEM_NES; - case 0x07: - return DIV_SYSTEM_C64_8580; - case 0x08: - return DIV_SYSTEM_ARCADE; - case 0x09: - return DIV_SYSTEM_YM2610; - case 0x42: - return DIV_SYSTEM_GENESIS_EXT; - case 0x43: - return DIV_SYSTEM_SMS_OPLL; - case 0x46: - return DIV_SYSTEM_NES_VRC7; - case 0x47: - return DIV_SYSTEM_C64_6581; - case 0x49: - return DIV_SYSTEM_YM2610_EXT; - case 0x86: - return DIV_SYSTEM_NES_FDS; - } - return DIV_SYSTEM_NULL; + return sysFileMapDMF[val]; } unsigned char DivEngine::systemToFileDMF(DivSystem val) { - switch (val) { - case DIV_SYSTEM_YMU759: - return 0x01; - case DIV_SYSTEM_GENESIS: - return 0x02; - case DIV_SYSTEM_SMS: - return 0x03; - case DIV_SYSTEM_GB: - return 0x04; - case DIV_SYSTEM_PCE: - return 0x05; - case DIV_SYSTEM_NES: - return 0x06; - case DIV_SYSTEM_C64_8580: - return 0x07; - case DIV_SYSTEM_ARCADE: - return 0x08; - case DIV_SYSTEM_YM2610: - return 0x09; - case DIV_SYSTEM_GENESIS_EXT: - return 0x42; - case DIV_SYSTEM_SMS_OPLL: - return 0x43; - case DIV_SYSTEM_NES_VRC7: - return 0x46; - case DIV_SYSTEM_NES_FDS: - return 0x86; - case DIV_SYSTEM_C64_6581: - return 0x47; - case DIV_SYSTEM_YM2610_EXT: - return 0x49; - default: - return 0; - } - return 0; + if (sysDefs[val]==NULL) return 0; + return sysDefs[val]->id_DMF; } int DivEngine::getChannelCount(DivSystem sys) { - switch (sys) { - case DIV_SYSTEM_NULL: - return 0; - case DIV_SYSTEM_YMU759: - return 17; - case DIV_SYSTEM_GENESIS: - return 10; - case DIV_SYSTEM_SMS: - case DIV_SYSTEM_GB: - return 4; - case DIV_SYSTEM_PCE: - return 6; - case DIV_SYSTEM_NES: - return 5; - case DIV_SYSTEM_C64_6581: - case DIV_SYSTEM_C64_8580: - return 3; - case DIV_SYSTEM_ARCADE: - case DIV_SYSTEM_GENESIS_EXT: - case DIV_SYSTEM_YM2610: - case DIV_SYSTEM_SMS_OPLL: - return 13; - case DIV_SYSTEM_NES_VRC7: - return 11; - case DIV_SYSTEM_NES_FDS: - return 6; - case DIV_SYSTEM_YM2610_EXT: - return 16; - case DIV_SYSTEM_AY8910: - case DIV_SYSTEM_AY8930: - return 3; - case DIV_SYSTEM_AMIGA: - return 4; - case DIV_SYSTEM_YM2151: - return 8; - case DIV_SYSTEM_YM2612: - return 6; - case DIV_SYSTEM_TIA: - return 2; - case DIV_SYSTEM_VIC20: - return 4; - case DIV_SYSTEM_PET: - return 1; - case DIV_SYSTEM_SNES: - return 8; - case DIV_SYSTEM_VRC6: - return 3; - case DIV_SYSTEM_OPLL: - return 9; - case DIV_SYSTEM_FDS: - return 1; - case DIV_SYSTEM_MMC5: - return 3; - case DIV_SYSTEM_N163: - return 8; - case DIV_SYSTEM_OPN: - return 6; - case DIV_SYSTEM_PC98: - return 16; - case DIV_SYSTEM_OPL: - return 9; - case DIV_SYSTEM_OPL2: - return 9; - case DIV_SYSTEM_OPL3: - return 18; - case DIV_SYSTEM_MULTIPCM: - return 28; - case DIV_SYSTEM_PCSPKR: - return 1; - case DIV_SYSTEM_POKEY: - return 4; - case DIV_SYSTEM_RF5C68: - return 8; - case DIV_SYSTEM_SWAN: - return 4; - case DIV_SYSTEM_SAA1099: - return 6; - case DIV_SYSTEM_OPZ: - return 8; - case DIV_SYSTEM_POKEMINI: - return 1; - case DIV_SYSTEM_SEGAPCM: - case DIV_SYSTEM_X1_010: - return 16; - case DIV_SYSTEM_VBOY: - return 6; - case DIV_SYSTEM_VRC7: - return 6; - case DIV_SYSTEM_YM2610B: - return 16; - case DIV_SYSTEM_SFX_BEEPER: - return 6; - case DIV_SYSTEM_YM2612_EXT: - return 9; - case DIV_SYSTEM_SCC: - return 5; - case DIV_SYSTEM_OPL_DRUMS: - return 11; - case DIV_SYSTEM_OPL2_DRUMS: - return 11; - case DIV_SYSTEM_OPL3_DRUMS: - return 20; - case DIV_SYSTEM_YM2610_FULL: - return 14; - case DIV_SYSTEM_YM2610_FULL_EXT: - return 17; - case DIV_SYSTEM_OPLL_DRUMS: - return 11; - case DIV_SYSTEM_LYNX: - return 4; - case DIV_SYSTEM_SEGAPCM_COMPAT: - return 5; - case DIV_SYSTEM_YM2610B_EXT: - case DIV_SYSTEM_QSOUND: - return 19; - case DIV_SYSTEM_VERA: - return 17; - case DIV_SYSTEM_BUBSYS_WSG: - return 2; - } - return 0; + if (sysDefs[sys]==NULL) return 0; + return sysDefs[sys]->channels; } int DivEngine::getTotalChannelCount() { @@ -633,270 +198,15 @@ const char* DivEngine::getSongSystemName() { } const char* DivEngine::getSystemName(DivSystem sys) { - switch (sys) { - case DIV_SYSTEM_NULL: - return "Unknown"; - case DIV_SYSTEM_YMU759: - return "Yamaha YMU759"; - case DIV_SYSTEM_GENESIS: - return "Sega Genesis/Mega Drive"; - case DIV_SYSTEM_SMS: - return "TI SN76489"; - case DIV_SYSTEM_SMS_OPLL: - return "Sega Master System + FM Expansion"; - case DIV_SYSTEM_GB: - return "Game Boy"; - case DIV_SYSTEM_PCE: - return "PC Engine/TurboGrafx-16"; - case DIV_SYSTEM_NES: - return "NES"; - case DIV_SYSTEM_NES_VRC7: - return "NES + Konami VRC7"; - case DIV_SYSTEM_NES_FDS: - return "Famicom Disk System"; - case DIV_SYSTEM_C64_6581: - return "Commodore 64 with 6581"; - case DIV_SYSTEM_C64_8580: - return "Commodore 64 with 8580"; - case DIV_SYSTEM_ARCADE: - return "YM2151 + SegaPCM Arcade"; - case DIV_SYSTEM_GENESIS_EXT: - return "Sega Genesis Extended Channel 3"; - case DIV_SYSTEM_YM2610: - return "Neo Geo CD"; - case DIV_SYSTEM_YM2610_EXT: - return "Neo Geo CD Extended Channel 2"; - case DIV_SYSTEM_YM2610_FULL: - return "Neo Geo"; - case DIV_SYSTEM_YM2610_FULL_EXT: - return "Neo Geo Extended Channel 2"; - case DIV_SYSTEM_AY8910: - return "AY-3-8910"; - case DIV_SYSTEM_AMIGA: - return "Amiga"; - case DIV_SYSTEM_YM2151: - return "Yamaha YM2151"; - case DIV_SYSTEM_YM2612: - return "Yamaha YM2612"; - case DIV_SYSTEM_TIA: - return "Atari 2600"; - case DIV_SYSTEM_VIC20: - return "Commodore VIC-20"; - case DIV_SYSTEM_PET: - return "Commodore PET"; - case DIV_SYSTEM_SNES: - return "SNES"; - case DIV_SYSTEM_VRC6: - return "Konami VRC6"; - case DIV_SYSTEM_OPLL: - return "Yamaha OPLL"; - case DIV_SYSTEM_FDS: - return "Famicom Disk System (chip)"; - case DIV_SYSTEM_MMC5: - return "MMC5"; - case DIV_SYSTEM_N163: - return "Namco 163"; - case DIV_SYSTEM_OPN: - return "NEC PC-9801-26K"; - case DIV_SYSTEM_PC98: - return "PC-9801-86 + Chibi-oto"; - case DIV_SYSTEM_OPL: - return "Yamaha OPL"; - case DIV_SYSTEM_OPL2: - return "Yamaha OPL2"; - case DIV_SYSTEM_OPL3: - return "Yamaha OPL3"; - case DIV_SYSTEM_MULTIPCM: - return "MultiPCM"; - case DIV_SYSTEM_PCSPKR: - return "PC Speaker"; - case DIV_SYSTEM_POKEY: - return "Atari 400/800"; - case DIV_SYSTEM_RF5C68: - return "Ricoh RF5C68"; - case DIV_SYSTEM_SWAN: - return "WonderSwan"; - case DIV_SYSTEM_SAA1099: - return "Philips SAA1099"; - case DIV_SYSTEM_OPZ: - return "Yamaha TX81Z/YS200"; - case DIV_SYSTEM_POKEMINI: - return "Pokémon Mini"; - case DIV_SYSTEM_AY8930: - return "Microchip AY8930"; - case DIV_SYSTEM_SEGAPCM: - return "SegaPCM"; - case DIV_SYSTEM_VBOY: - return "Virtual Boy"; - case DIV_SYSTEM_VRC7: - return "Konami VRC7"; - case DIV_SYSTEM_YM2610B: - return "Taito Arcade"; - case DIV_SYSTEM_SFX_BEEPER: - return "ZX Spectrum Beeper"; - case DIV_SYSTEM_YM2612_EXT: - return "Yamaha YM2612 Extended Channel 3"; - case DIV_SYSTEM_SCC: - return "Konami SCC"; - case DIV_SYSTEM_OPL_DRUMS: - return "Yamaha OPL with drums"; - case DIV_SYSTEM_OPL2_DRUMS: - return "Yamaha OPL2 with drums"; - case DIV_SYSTEM_OPL3_DRUMS: - return "Yamaha OPL3 with drums"; - case DIV_SYSTEM_OPLL_DRUMS: - return "Yamaha OPLL with drums"; - case DIV_SYSTEM_LYNX: - return "Atari Lynx"; - case DIV_SYSTEM_SEGAPCM_COMPAT: - return "SegaPCM (compatible 5-channel mode)"; - case DIV_SYSTEM_YM2610B_EXT: - return "Taito Arcade Extended Channel 3"; - case DIV_SYSTEM_QSOUND: - return "Capcom QSound"; - case DIV_SYSTEM_VERA: - return "VERA"; - case DIV_SYSTEM_X1_010: - return "Seta/Allumer X1-010"; - case DIV_SYSTEM_BUBSYS_WSG: - return "Konami Bubble System WSG"; - } - return "Unknown"; -} - -const char* DivEngine::getSystemChips(DivSystem sys) { - switch (sys) { - case DIV_SYSTEM_NULL: - return "Unknown"; - case DIV_SYSTEM_YMU759: - return "Yamaha YMU759"; - case DIV_SYSTEM_GENESIS: - return "Yamaha YM2612 + TI SN76489"; - case DIV_SYSTEM_SMS: - return "TI SN76489"; - case DIV_SYSTEM_SMS_OPLL: - return "TI SN76489 + Yamaha YM2413"; - case DIV_SYSTEM_GB: - return "Sharp LR35902"; - case DIV_SYSTEM_PCE: - return "Hudson Soft HuC6280"; - case DIV_SYSTEM_NES: - return "Ricoh 2A03"; - case DIV_SYSTEM_NES_VRC7: - return "Ricoh 2A03 + Konami VRC7"; - case DIV_SYSTEM_NES_FDS: - return "Ricoh 2A03 + Famicom Disk System"; - case DIV_SYSTEM_C64_6581: - return "SID 6581"; - case DIV_SYSTEM_C64_8580: - return "SID 8580"; - case DIV_SYSTEM_ARCADE: - return "Yamaha YM2151 + SegaPCM"; - case DIV_SYSTEM_GENESIS_EXT: - return "Yamaha YM2612 (extended channel 3) + TI SN76489"; - case DIV_SYSTEM_YM2610: - return "Yamaha YM2610 no ADPCM-B"; - case DIV_SYSTEM_YM2610_EXT: - return "Yamaha YM2610 no ADPCM-B (extended channel 2)"; - case DIV_SYSTEM_AY8910: - return "AY-3-8910"; - case DIV_SYSTEM_AMIGA: - return "MOS 8364 Paula"; - case DIV_SYSTEM_YM2151: - return "Yamaha YM2151"; - case DIV_SYSTEM_YM2612: - return "Yamaha YM2612"; - case DIV_SYSTEM_TIA: - return "Atari TIA"; - case DIV_SYSTEM_VIC20: - return "VIC"; - case DIV_SYSTEM_PET: - return "Commodore PET"; - case DIV_SYSTEM_SNES: - return "SPC700"; - case DIV_SYSTEM_VRC6: - return "Konami VRC6"; - case DIV_SYSTEM_OPLL: - return "Yamaha YM2413"; - case DIV_SYSTEM_FDS: - return "Famicom Disk System"; - case DIV_SYSTEM_MMC5: - return "MMC5"; - case DIV_SYSTEM_N163: - return "Namco 163"; - case DIV_SYSTEM_OPN: - return "Yamaha YM2203"; - case DIV_SYSTEM_PC98: - return "Yamaha YM2608"; - case DIV_SYSTEM_OPL: - return "Yamaha YM3526"; - case DIV_SYSTEM_OPL2: - return "Yamaha YM3812"; - case DIV_SYSTEM_OPL3: - return "Yamaha YMF262"; - case DIV_SYSTEM_MULTIPCM: - return "Yamaha YMW258"; - case DIV_SYSTEM_PCSPKR: - return "Intel 8253"; - case DIV_SYSTEM_POKEY: - return "POKEY"; - case DIV_SYSTEM_RF5C68: - return "Ricoh RF5C68"; - case DIV_SYSTEM_SWAN: - return "WonderSwan"; - case DIV_SYSTEM_SAA1099: - return "Philips SAA1099"; - case DIV_SYSTEM_OPZ: - return "Yamaha YM2414"; - case DIV_SYSTEM_POKEMINI: - return "Pokémon Mini"; - case DIV_SYSTEM_AY8930: - return "Microchip AY8930"; - case DIV_SYSTEM_SEGAPCM: - return "SegaPCM"; - case DIV_SYSTEM_VBOY: - return "VSU"; - case DIV_SYSTEM_VRC7: - return "Konami VRC7"; - case DIV_SYSTEM_YM2610B: - return "Yamaha YM2610B"; - case DIV_SYSTEM_SFX_BEEPER: - return "ZX Spectrum Beeper"; - case DIV_SYSTEM_YM2612_EXT: - return "Yamaha YM2612 Extended Channel 3"; - case DIV_SYSTEM_SCC: - return "Konami K051649"; - case DIV_SYSTEM_OPL_DRUMS: - return "Yamaha YM3526 with drums"; - case DIV_SYSTEM_OPL2_DRUMS: - return "Yamaha YM3812 with drums"; - case DIV_SYSTEM_OPL3_DRUMS: - return "Yamaha YMF262 with drums"; - case DIV_SYSTEM_YM2610_FULL: - return "Yamaha YM2610"; - case DIV_SYSTEM_YM2610_FULL_EXT: - return "Yamaha YM2610 (extended channel 2)"; - case DIV_SYSTEM_OPLL_DRUMS: - return "Yamaha YM2413 with drums"; - case DIV_SYSTEM_LYNX: - return "Mikey"; - case DIV_SYSTEM_SEGAPCM_COMPAT: - return "SegaPCM (compatible 5-channel mode)"; - case DIV_SYSTEM_YM2610B_EXT: - return "Yamaha YM2610B Extended Channel 3"; - case DIV_SYSTEM_QSOUND: - return "Capcom DL-1425"; - case DIV_SYSTEM_VERA: - return "VERA"; - case DIV_SYSTEM_X1_010: - return "Seta/Allumer X1-010"; - case DIV_SYSTEM_BUBSYS_WSG: - return "Konami Bubble System WSG"; - } - return "Unknown"; + if (sysDefs[sys]==NULL) return "Unknown"; + return sysDefs[sys]->name; } const char* DivEngine::getSystemNameJ(DivSystem sys) { + if (sysDefs[sys]==NULL) return "䏿˜Ž"; + if (sysDefs[sys]->nameJ==NULL) return ""; + return sysDefs[sys]->nameJ; + /* switch (sys) { case DIV_SYSTEM_NULL: return "䏿˜Ž"; @@ -946,810 +256,49 @@ const char* DivEngine::getSystemNameJ(DivSystem sys) { default: // TODO return ""; } - return "䏿˜Ž"; + */ } bool DivEngine::isFMSystem(DivSystem sys) { - return (sys==DIV_SYSTEM_GENESIS || - sys==DIV_SYSTEM_GENESIS_EXT || - sys==DIV_SYSTEM_SMS_OPLL || - sys==DIV_SYSTEM_NES_VRC7 || - sys==DIV_SYSTEM_ARCADE || - sys==DIV_SYSTEM_YM2610 || - sys==DIV_SYSTEM_YM2610_EXT || - sys==DIV_SYSTEM_YM2610_FULL || - sys==DIV_SYSTEM_YM2610_FULL_EXT || - sys==DIV_SYSTEM_YM2610B || - sys==DIV_SYSTEM_YM2610B_EXT || - sys==DIV_SYSTEM_YMU759 || - sys==DIV_SYSTEM_YM2151 || - sys==DIV_SYSTEM_YM2612); + if (sysDefs[sys]==NULL) return false; + return sysDefs[sys]->isFM; } bool DivEngine::isSTDSystem(DivSystem sys) { - return (sys!=DIV_SYSTEM_ARCADE && - sys!=DIV_SYSTEM_YMU759 && - sys!=DIV_SYSTEM_YM2612 && - sys!=DIV_SYSTEM_YM2151); + if (sysDefs[sys]==NULL) return false; + return sysDefs[sys]->isSTD; } -const char* chanNames[40][32]={ - {"Channel 1", "Channel 2", "Channel 3", "Channel 4", "Channel 5", "Channel 6", "Channel 7", "Channel 8", "Channel 9", "Channel 10", "Channel 11", "Channel 12", "Channel 13", "Channel 14", "Channel 15", "Channel 16", "PCM"}, // YMU759/SegaPCM - {"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "Square 1", "Square 2", "Square 3", "Noise"}, // Genesis - {"FM 1", "FM 2", "FM 3 OP1", "FM 3 OP2", "FM 3 OP3", "FM 3 OP4", "FM 4", "FM 5", "FM 6", "Square 1", "Square 2", "Square 3", "Noise"}, // Genesis (extended channel 3) - {"Square 1", "Square 2", "Square 3", "Noise"}, // SMS - {"Pulse 1", "Pulse 2", "Wavetable", "Noise"}, // GB - {"Channel 1", "Channel 2", "Channel 3", "Channel 4", "Channel 5", "Channel 6"}, // PCE/ZX Beeper - {"Pulse 1", "Pulse 2", "Triangle", "Noise", "PCM"}, // NES - {"Channel 1", "Channel 2", "Channel 3"}, // C64 - {"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "FM 7", "FM 8", "PCM 1", "PCM 2", "PCM 3", "PCM 4", "PCM 5"}, // Arcade - {"FM 1", "FM 2", "FM 3", "FM 4", "PSG 1", "PSG 2", "PSG 3", "ADPCM-A 1", "ADPCM-A 2", "ADPCM-A 3", "ADPCM-A 4", "ADPCM-A 5", "ADPCM-A 6", "ADPCM-B"}, // YM2610 - {"FM 1", "FM 2 OP1", "FM 2 OP2", "FM 2 OP3", "FM 2 OP4", "FM 3", "FM 4", "PSG 1", "PSG 2", "PSG 3", "ADPCM-A 1", "ADPCM-A 2", "ADPCM-A 3", "ADPCM-A 4", "ADPCM-A 5", "ADPCM-A 6", "ADPCM-B"}, // YM2610 (extended channel 2) - {"PSG 1", "PSG 2", "PSG 3"}, // AY-3-8910 - {"Channel 1", "Channel 2", "Channel 3", "Channel 4"}, // Amiga/POKEY/Lynx - {"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "FM 7", "FM 8"}, // YM2151/YM2414 - {"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6"}, // YM2612 - {"Channel 1", "Channel 2"}, // TIA - {"PSG 1", "PSG 2", "PSG 3", "PSG 4", "PSG 5", "PSG 6"}, // SAA1099 - {"PSG 1", "PSG 2", "PSG 3"}, // AY8930 - {"Low", "Mid", "High", "Noise"}, // VIC-20 - {"Wave"}, // PET - {"Channel 1", "Channel 2", "Channel 3", "Channel 4", "Channel 5", "Channel 6", "Channel 7", "Channel 8"}, // SNES/N163/RF5C68 - {"VRC6 1", "VRC6 2", "VRC6 Saw"}, // VRC6 - {"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "FM 7", "FM 8", "FM 9"}, // OPLL/OPL/OPL2/VRC7 - {"FDS"}, // FDS - {"Pulse 1", "Pulse 2", "PCM"}, // MMC5 - {"FM 1", "FM 2", "FM 3", "PSG 1", "PSG 2", "PSG 3"}, // OPN - {"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "Square 1", "Square 2", "Square 3", "Kick", "Snare", "Top", "HiHat", "Tom", "Rim", "ADPCM"}, // PC-98 - {"4OP 1", "FM 2", "4OP 3", "FM 4", "4OP 5", "FM 6", "4OP 7", "FM 8", "4OP 9", "FM 10", "4OP 11", "FM 12", "FM 13", "FM 14", "FM 15", "FM 16", "FM 17", "FM 18"}, // OPL3 - {"Channel 1", "Channel 2", "Channel 3", "Channel 4", "Channel 5", "Channel 6", "Channel 7", "Channel 8", "Channel 9", "Channel 10", "Channel 11", "Channel 12", "Channel 13", "Channel 14", "Channel 15", "Channel 16", "Channel 17", "Channel 18", "Channel 19", "Channel 20", "Channel 21", "Channel 22", "Channel 23", "Channel 24", "Channel 25", "Channel 26", "Channel 27", "Channel 28"}, // MultiPCM - {"Square"}, // PC Speaker/Pokémon Mini - {"Channel 1", "Channel 2", "Channel 3", "Channel 4", "Channel 5", "Noise"}, // Virtual Boy/SCC - {"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "PSG 1", "PSG 2", "PSG 3", "ADPCM-A 1", "ADPCM-A 2", "ADPCM-A 3", "ADPCM-A 4", "ADPCM-A 5", "ADPCM-A 6", "ADPCM-B"}, // YM2610B - {"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "Kick", "Snare", "Tom", "Top", "HiHat"}, // OPLL/OPL/OPL2 drums - {"4OP 1", "FM 2", "4OP 3", "FM 4", "4OP 5", "FM 6", "4OP 7", "FM 8", "4OP 9", "FM 10", "4OP 11", "FM 12", "FM 13", "FM 14", "FM 15", "Kick", "Snare", "Tom", "Top", "HiHat"}, // OPL3 drums - {"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "4OP 1", "4OP 2", "4OP 3", "4OP 4", "4OP 5", "4OP 6"}, // OPL3 4-op (UNUSED) - {"FM 1", "FM 2", "FM 3", "4OP 1", "4OP 2", "4OP 3", "4OP 4", "4OP 5", "4OP 6", "Kick", "Snare", "Tom", "Top", "HiHat"}, // OPL3 4-op + drums (UNUSED) - {"PCM 1", "PCM 2", "PCM 3", "PCM 4", "PCM 5", "PCM 6", "PCM 7", "PCM 8", "PCM 9", "PCM 10", "PCM 11", "PCM 12", "PCM 13", "PCM 14", "PCM 15", "PCM 16", "ADPCM 1", "ADPCM 2", "ADPCM 3"}, // QSound - {"FM 1", "FM 2", "FM 3 OP1", "FM 3 OP2", "FM 3 OP3", "FM 3 OP4", "FM 4", "FM 5", "FM 6", "PSG 1", "PSG 2", "PSG 3", "ADPCM-A 1", "ADPCM-A 2", "ADPCM-A 3", "ADPCM-A 4", "ADPCM-A 5", "ADPCM-A 6", "ADPCM-B"}, // YM2610B (extended channel 3) - {"Wave", "Wave/PCM", "Wave", "Wave/Noise"}, // Swan - {"PSG 1", "PSG 2", "PSG 3", "PSG 4", "PSG 5", "PSG 6", "PSG 7", "PSG 8", "PSG 9", "PSG 10", "PSG 11", "PSG 12", "PSG 13", "PSG 14", "PSG 15", "PSG 16", "PCM"}, // VERA -}; - -const char* chanShortNames[38][32]={ - {"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", "B"}, // YM2610 - {"F1", "O1", "O2", "O3", "O4", "F3", "F4", "S1", "S2", "S3", "P1", "P2", "P3", "P4", "P5", "P6", "B"}, // YM2610 (extended channel 2) - {"S1", "S2", "S3"}, // AY-3-8910 - {"CH1", "CH2", "CH3", "CH4"}, // Amiga/Lynx - {"F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8"}, // YM2151 - {"F1", "F2", "F3", "F4", "F5", "F6"}, // YM2612 - {"CH1", "CH2"}, // TIA - {"S1", "S2", "S3", "S4", "S5", "S6"}, // SAA1099 - {"S1", "S2", "S3"}, // AY8930 - {"LO", "MID", "HI", "NO"}, // VIC-20 - {"PET"}, // PET - {"CH1", "CH2", "CH3", "CH4", "CH5", "CH6", "CH7", "CH8"}, // SNES/N163/RF5C68 - {"V1", "V2", "VS"}, // VRC6 - {"F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9"}, // OPLL/OPL/OPL2/VRC7 - {"FDS"}, // FDS - {"S1", "S2", "PCM"}, // MMC5 - {"F1", "F2", "F3", "S1", "S2", "S3"}, // OPN - {"F1", "F2", "F3", "F4", "F5", "F6", "S1", "S2", "S3", "BD", "SD", "TP", "HH", "TM", "RM", "P"}, // PC-98 - {"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18"}, // OPL3 - {"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28"}, // MultiPCM - {"SQ"}, // PC Speaker/Pokémon Mini - {"CH1", "CH2", "CH3", "CH4", "CH5", "NO"}, // Virtual Boy/SCC - {"F1", "F2", "F3", "F4", "F5", "F6", "S1", "S2", "S3", "P1", "P2", "P3", "P4", "P5", "P6", "B"}, // YM2610B - {"F1", "F2", "F3", "F4", "F5", "F6", "BD", "SD", "TM", "TP", "HH"}, // OPLL/OPL/OPL2 drums - {"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "BD", "SD", "TM", "TP", "HH"}, // OPL3 drums - {"F1", "F2", "F3", "F4", "F5", "F6", "Q1", "Q2", "Q3", "Q4", "Q5", "Q6"}, // OPL3 4-op (UNUSED) - {"F1", "F2", "F3", "Q1", "Q2", "Q3", "Q4", "Q5", "Q6", "BD", "SD", "TM", "TP", "HH"}, // OPL3 4-op + drums (UNUSED) - {"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "A1", "A2", "A3"}, // QSound - {"F1", "F2", "O1", "O2", "O3", "O4", "F4", "F5", "F6", "S1", "S2", "S3", "P1", "P2", "P3", "P4", "P5", "P6", "B"}, // YM2610B (extended channel 3) -}; - -const int chanTypes[41][32]={ - {DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_PCM}, // YMU759 - {DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_NOISE}, // Genesis - {DIV_CH_FM, DIV_CH_FM, DIV_CH_OP, DIV_CH_OP, DIV_CH_OP, DIV_CH_OP, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_NOISE}, // Genesis (extended channel 3) - {DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_NOISE}, // SMS - {DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_WAVE, DIV_CH_NOISE}, // GB - {DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE}, // PCE - {DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_WAVE, DIV_CH_NOISE, DIV_CH_PCM}, // NES - {DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE}, // C64 - {DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM}, // Arcade - {DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM}, // YM2610 - {DIV_CH_FM, DIV_CH_OP, DIV_CH_OP, DIV_CH_OP, DIV_CH_OP, DIV_CH_FM, DIV_CH_FM, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM}, // YM2610 (extended channel 2) - {DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE}, // AY-3-8910 - {DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM}, // Amiga - {DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM}, // YM2151 - {DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM}, // YM2612 - {DIV_CH_WAVE, DIV_CH_WAVE}, // TIA - {DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE}, // SAA1099 - {DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE}, // AY8930 - {DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_NOISE}, // VIC-20 - {DIV_CH_PULSE}, // PET - {DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM}, // SNES/N163/RF5C68 - {DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_WAVE}, // VRC6 - {DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM}, // OPLL/OPL/OPL2/VRC7 - {DIV_CH_WAVE}, // FDS - {DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PCM}, // MMC5 - {DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE}, // OPN - {DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_PCM}, // PC-98 - {DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM}, // OPL3 - {DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM}, // MultiPCM/QSound - {DIV_CH_PULSE}, // PC Speaker/Pokémon Mini - {DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_NOISE}, // Virtual Boy/SCC - {DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM}, // YM2610B - {DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE}, // OPLL/OPL/OPL2 drums - {DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE}, // OPL3 drums - {DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM}, // OPL3 4-op (UNUSED) - {DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE}, // OPL3 4-op + drums (UNUSED) - {DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE}, // Lynx - {DIV_CH_FM, DIV_CH_FM, DIV_CH_OP, DIV_CH_OP, DIV_CH_OP, DIV_CH_OP, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM}, // YM2610B (extended channel 3) - {DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PCM}, // VERA - {DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE}, // X1-010 - {DIV_CH_WAVE, DIV_CH_PCM, DIV_CH_WAVE, DIV_CH_NOISE}, // Swan -}; - -const DivInstrumentType chanPrefType[47][28]={ - {DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM}, // YMU759 - {DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_STD, DIV_INS_STD, DIV_INS_STD, DIV_INS_STD}, // Genesis - {DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_STD, DIV_INS_STD, DIV_INS_STD, DIV_INS_STD}, // Genesis (extended channel 3) - {DIV_INS_STD, DIV_INS_STD, DIV_INS_STD, DIV_INS_STD}, // SMS - {DIV_INS_GB, DIV_INS_GB, DIV_INS_GB, DIV_INS_GB}, // GB - {DIV_INS_PCE, DIV_INS_PCE, DIV_INS_PCE, DIV_INS_PCE, DIV_INS_PCE, DIV_INS_PCE}, // PCE - {DIV_INS_STD, DIV_INS_STD, DIV_INS_STD, DIV_INS_STD, DIV_INS_STD}, // NES - {DIV_INS_C64, DIV_INS_C64, DIV_INS_C64}, // C64 - {DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA}, // Arcade - {DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_AY, DIV_INS_AY, DIV_INS_AY, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA}, // YM2610 - {DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_AY, DIV_INS_AY, DIV_INS_AY, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA}, // YM2610 (extended channel 2) - {DIV_INS_AY, DIV_INS_AY, DIV_INS_AY}, // AY-3-8910 - {DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA}, // Amiga - {DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM}, // YM2151 - {DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM}, // YM2612 - {DIV_INS_TIA, DIV_INS_TIA}, // TIA - {DIV_INS_SAA1099, DIV_INS_SAA1099, DIV_INS_SAA1099, DIV_INS_SAA1099, DIV_INS_SAA1099, DIV_INS_SAA1099}, // SAA1099 - {DIV_INS_AY8930, DIV_INS_AY8930, DIV_INS_AY8930}, // AY8930 - {DIV_INS_VIC, DIV_INS_VIC, DIV_INS_VIC, DIV_INS_VIC}, // VIC-20 - {DIV_INS_PET}, // PET - {DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA}, // SNES/N163/RF5C68 - {DIV_INS_VRC6, DIV_INS_VRC6, DIV_INS_VRC6_SAW}, // VRC6 - {DIV_INS_OPLL, DIV_INS_OPLL, DIV_INS_OPLL, DIV_INS_OPLL, DIV_INS_OPLL, DIV_INS_OPLL, DIV_INS_OPLL, DIV_INS_OPLL, DIV_INS_OPLL}, // OPLL/VRC7 - {DIV_INS_FDS}, // FDS - {DIV_INS_STD, DIV_INS_STD, DIV_INS_AMIGA}, // MMC5 - {DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_AY, DIV_INS_AY, DIV_INS_AY}, // OPN - {DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_AY, DIV_INS_AY, DIV_INS_AY, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA}, // PC-98 - {DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL}, // OPL/OPL2/OPL3 - {DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA}, // MultiPCM/QSound - {DIV_INS_STD}, // PC Speaker/Pokémon Mini - {DIV_INS_VBOY, DIV_INS_VBOY, DIV_INS_VBOY, DIV_INS_VBOY, DIV_INS_VBOY, DIV_INS_VBOY}, // Virtual Boy - {DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_AY, DIV_INS_AY, DIV_INS_AY, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA}, // YM2610B - {DIV_INS_OPLL, DIV_INS_OPLL, DIV_INS_OPLL, DIV_INS_OPLL, DIV_INS_OPLL, DIV_INS_OPLL, DIV_INS_OPLL, DIV_INS_OPLL, DIV_INS_OPLL, DIV_INS_OPLL, DIV_INS_OPLL}, // OPLL drums - {DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL}, // OPL3 drums - {DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPZ, DIV_INS_OPZ, DIV_INS_OPZ, DIV_INS_OPZ, DIV_INS_OPZ, DIV_INS_OPZ}, // OPL3 4-op - {DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPZ, DIV_INS_OPZ, DIV_INS_OPZ, DIV_INS_OPZ, DIV_INS_OPZ, DIV_INS_OPZ, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL}, // OPL3 4-op + drums - {DIV_INS_SCC, DIV_INS_SCC, DIV_INS_SCC, DIV_INS_SCC, DIV_INS_SCC}, // SCC - {DIV_INS_N163, DIV_INS_N163, DIV_INS_N163, DIV_INS_N163, DIV_INS_N163, DIV_INS_N163, DIV_INS_N163, DIV_INS_N163}, // N163 - {DIV_INS_POKEY, DIV_INS_POKEY, DIV_INS_POKEY, DIV_INS_POKEY}, // POKEY - {DIV_INS_BEEPER, DIV_INS_BEEPER, DIV_INS_BEEPER, DIV_INS_BEEPER, DIV_INS_BEEPER, DIV_INS_BEEPER}, // ZX beeper - {DIV_INS_SWAN, DIV_INS_SWAN, DIV_INS_SWAN, DIV_INS_SWAN}, // Swan - {DIV_INS_OPZ, DIV_INS_OPZ, DIV_INS_OPZ, DIV_INS_OPZ, DIV_INS_OPZ, DIV_INS_OPZ, DIV_INS_OPZ, DIV_INS_OPZ}, // Z - {DIV_INS_MIKEY, DIV_INS_MIKEY, DIV_INS_MIKEY, DIV_INS_MIKEY}, // Lynx - {DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_AY, DIV_INS_AY, DIV_INS_AY, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA}, // YM2610B (extended channel 3) - {DIV_INS_VERA, DIV_INS_VERA, DIV_INS_VERA, DIV_INS_VERA, DIV_INS_VERA, DIV_INS_VERA, DIV_INS_VERA, DIV_INS_VERA, DIV_INS_VERA, DIV_INS_VERA, DIV_INS_VERA, DIV_INS_VERA, DIV_INS_VERA, DIV_INS_VERA, DIV_INS_VERA, DIV_INS_VERA, DIV_INS_AMIGA}, // VERA - {DIV_INS_X1_010, DIV_INS_X1_010, DIV_INS_X1_010, DIV_INS_X1_010, DIV_INS_X1_010, DIV_INS_X1_010, DIV_INS_X1_010, DIV_INS_X1_010, DIV_INS_X1_010, DIV_INS_X1_010, DIV_INS_X1_010, DIV_INS_X1_010, DIV_INS_X1_010, DIV_INS_X1_010, DIV_INS_X1_010, DIV_INS_X1_010}, // X1-010 - {DIV_INS_SCC, DIV_INS_SCC}, // Bubble System WSG -}; - const char* DivEngine::getChannelName(int chan) { if (chan<0 || chan>chans) return "??"; if (!song.chanName[chan].empty()) return song.chanName[chan].c_str(); - switch (sysOfChan[chan]) { - case DIV_SYSTEM_NULL: case DIV_SYSTEM_YMU759: - return chanNames[0][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_GENESIS: - return chanNames[1][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_GENESIS_EXT: - case DIV_SYSTEM_YM2612_EXT: - return chanNames[2][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_SMS: - return chanNames[3][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_SMS_OPLL: // this is flattened to SMS + OPLL. - case DIV_SYSTEM_NES_VRC7: // this is flattened to NES + VRC7. - case DIV_SYSTEM_NES_FDS: // this is flattened to NES + FDS. - return "??"; - break; - case DIV_SYSTEM_GB: - return chanNames[4][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_PCE: - case DIV_SYSTEM_SFX_BEEPER: - case DIV_SYSTEM_BUBSYS_WSG: - return chanNames[5][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_NES: - return chanNames[6][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_C64_6581: case DIV_SYSTEM_C64_8580: - return chanNames[7][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_ARCADE: - case DIV_SYSTEM_OPZ: - return chanNames[8][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_YM2610: - case DIV_SYSTEM_YM2610_FULL: - return chanNames[9][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_YM2610_EXT: - case DIV_SYSTEM_YM2610_FULL_EXT: - return chanNames[10][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_AY8910: - return chanNames[11][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_AMIGA: - case DIV_SYSTEM_POKEY: - case DIV_SYSTEM_LYNX: - return chanNames[12][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_SWAN: - return chanNames[38][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_YM2151: - return chanNames[13][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_YM2612: - return chanNames[14][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_TIA: - return chanNames[15][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_VIC20: - return chanNames[18][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_PET: - return chanNames[19][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_SNES: - case DIV_SYSTEM_N163: - case DIV_SYSTEM_RF5C68: - return chanNames[20][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_VRC6: - return chanNames[21][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_OPLL: - case DIV_SYSTEM_OPL: - case DIV_SYSTEM_OPL2: - case DIV_SYSTEM_VRC7: - return chanNames[22][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_FDS: - return chanNames[23][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_MMC5: - return chanNames[24][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_OPN: - return chanNames[25][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_PC98: - return chanNames[26][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_OPL3: - return chanNames[27][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_MULTIPCM: - case DIV_SYSTEM_SEGAPCM: - case DIV_SYSTEM_SEGAPCM_COMPAT: - case DIV_SYSTEM_X1_010: - return chanNames[28][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_PCSPKR: - case DIV_SYSTEM_POKEMINI: - return chanNames[29][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_VBOY: - case DIV_SYSTEM_SCC: - return chanNames[30][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_YM2610B: - return chanNames[31][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_YM2610B_EXT: - return chanNames[37][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_OPLL_DRUMS: - case DIV_SYSTEM_OPL_DRUMS: - case DIV_SYSTEM_OPL2_DRUMS: - return chanNames[32][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_OPL3_DRUMS: - return chanNames[33][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_SAA1099: - return chanNames[16][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_AY8930: - return chanNames[17][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_QSOUND: - return chanNames[36][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_VERA: - return chanNames[39][dispatchChanOfChan[chan]]; - break; - } - return "??"; + if (sysDefs[sysOfChan[chan]]==NULL) return "??"; + + const char* ret=sysDefs[sysOfChan[chan]]->chanNames[dispatchChanOfChan[chan]]; + if (ret==NULL) return "??"; + return ret; } const char* DivEngine::getChannelShortName(int chan) { if (chan<0 || chan>chans) return "??"; if (!song.chanShortName[chan].empty()) return song.chanShortName[chan].c_str(); - switch (sysOfChan[chan]) { - case DIV_SYSTEM_NULL: case DIV_SYSTEM_YMU759: - return chanShortNames[0][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_GENESIS: - return chanShortNames[1][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_GENESIS_EXT: - case DIV_SYSTEM_YM2612_EXT: - return chanShortNames[2][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_SMS: - return chanShortNames[3][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_SMS_OPLL: // this is flattened to SMS + OPLL. - case DIV_SYSTEM_NES_VRC7: // this is flattened to NES + VRC7. - case DIV_SYSTEM_NES_FDS: // this is flattened to NES + FDS. - return "??"; - break; - case DIV_SYSTEM_GB: - return chanShortNames[4][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_PCE: - case DIV_SYSTEM_SFX_BEEPER: - case DIV_SYSTEM_BUBSYS_WSG: - return chanShortNames[5][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_NES: - return chanShortNames[6][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_C64_6581: case DIV_SYSTEM_C64_8580: - return chanShortNames[7][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_ARCADE: - case DIV_SYSTEM_OPZ: - return chanShortNames[8][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_YM2610: - case DIV_SYSTEM_YM2610_FULL: - return chanShortNames[9][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_YM2610_EXT: - case DIV_SYSTEM_YM2610_FULL_EXT: - return chanShortNames[10][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_AY8910: - return chanShortNames[11][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_AMIGA: - case DIV_SYSTEM_POKEY: - case DIV_SYSTEM_SWAN: - case DIV_SYSTEM_LYNX: - return chanShortNames[12][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_YM2151: - return chanShortNames[13][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_YM2612: - return chanShortNames[14][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_TIA: - return chanShortNames[15][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_VIC20: - return chanShortNames[18][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_PET: - return chanShortNames[19][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_SNES: - case DIV_SYSTEM_N163: - case DIV_SYSTEM_RF5C68: - return chanShortNames[20][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_VRC6: - return chanShortNames[21][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_OPLL: - case DIV_SYSTEM_OPL: - case DIV_SYSTEM_OPL2: - case DIV_SYSTEM_VRC7: - return chanShortNames[22][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_FDS: - return chanShortNames[23][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_MMC5: - return chanShortNames[24][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_OPN: - return chanShortNames[25][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_PC98: - return chanShortNames[26][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_OPL3: - return chanShortNames[27][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_MULTIPCM: - case DIV_SYSTEM_SEGAPCM: - case DIV_SYSTEM_SEGAPCM_COMPAT: - case DIV_SYSTEM_X1_010: - return chanShortNames[28][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_PCSPKR: - case DIV_SYSTEM_POKEMINI: - return chanShortNames[29][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_VBOY: - case DIV_SYSTEM_SCC: - return chanShortNames[30][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_YM2610B: - return chanShortNames[31][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_YM2610B_EXT: - return chanShortNames[37][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_OPLL_DRUMS: - case DIV_SYSTEM_OPL_DRUMS: - case DIV_SYSTEM_OPL2_DRUMS: - return chanShortNames[32][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_OPL3_DRUMS: - return chanShortNames[33][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_SAA1099: - return chanShortNames[16][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_AY8930: - return chanShortNames[17][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_QSOUND: - return chanShortNames[36][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_VERA: - return chanShortNames[0][dispatchChanOfChan[chan]]; - break; - } - return "??"; + if (sysDefs[sysOfChan[chan]]==NULL) return "??"; + + const char* ret=sysDefs[sysOfChan[chan]]->chanShortNames[dispatchChanOfChan[chan]]; + if (ret==NULL) return "??"; + return ret; } int DivEngine::getChannelType(int chan) { - switch (sysOfChan[chan]) { - case DIV_SYSTEM_NULL: case DIV_SYSTEM_YMU759: - return chanTypes[0][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_GENESIS: - return chanTypes[1][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_GENESIS_EXT: - case DIV_SYSTEM_YM2612_EXT: - return chanTypes[2][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_SMS: - return chanTypes[3][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_SMS_OPLL: // this is flattened to SMS + OPLL. - case DIV_SYSTEM_NES_VRC7: // this is flattened to NES + VRC7. - case DIV_SYSTEM_NES_FDS: // this is flattened to NES + FDS. - return 0; - break; - case DIV_SYSTEM_GB: - return chanTypes[4][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_PCE: - case DIV_SYSTEM_SFX_BEEPER: - case DIV_SYSTEM_BUBSYS_WSG: - return chanTypes[5][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_NES: - return chanTypes[6][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_C64_6581: case DIV_SYSTEM_C64_8580: - return chanTypes[7][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_ARCADE: - case DIV_SYSTEM_OPZ: - return chanTypes[8][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_YM2610: - case DIV_SYSTEM_YM2610_FULL: - return chanTypes[9][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_YM2610_EXT: - case DIV_SYSTEM_YM2610_FULL_EXT: - return chanTypes[10][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_AY8910: - return chanTypes[11][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_AMIGA: - case DIV_SYSTEM_POKEY: - return chanTypes[12][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_YM2151: - return chanTypes[13][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_YM2612: - return chanTypes[14][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_TIA: - return chanTypes[15][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_VIC20: - return chanTypes[18][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_PET: - return chanTypes[19][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_SNES: - case DIV_SYSTEM_N163: - case DIV_SYSTEM_RF5C68: - return chanTypes[20][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_VRC6: - return chanTypes[21][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_OPLL: - case DIV_SYSTEM_OPL: - case DIV_SYSTEM_OPL2: - case DIV_SYSTEM_VRC7: - return chanTypes[22][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_FDS: - return chanTypes[23][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_MMC5: - return chanTypes[24][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_OPN: - return chanTypes[25][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_PC98: - return chanTypes[26][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_OPL3: - return chanTypes[27][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_MULTIPCM: - case DIV_SYSTEM_SEGAPCM: - case DIV_SYSTEM_SEGAPCM_COMPAT: - case DIV_SYSTEM_QSOUND: - return chanTypes[28][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_PCSPKR: - case DIV_SYSTEM_POKEMINI: - return chanTypes[29][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_VBOY: - case DIV_SYSTEM_SCC: - return chanTypes[30][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_YM2610B: - return chanTypes[31][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_YM2610B_EXT: - return chanTypes[37][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_OPLL_DRUMS: - case DIV_SYSTEM_OPL_DRUMS: - case DIV_SYSTEM_OPL2_DRUMS: - return chanTypes[32][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_OPL3_DRUMS: - return chanTypes[33][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_SAA1099: - return chanTypes[16][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_AY8930: - return chanTypes[17][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_LYNX: - return chanTypes[36][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_VERA: - return chanTypes[38][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_X1_010: - return chanTypes[39][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_SWAN: - return chanTypes[40][dispatchChanOfChan[chan]]; - break; - } - return 1; + if (chan<0 || chan>chans) return DIV_CH_NOISE; + if (sysDefs[sysOfChan[chan]]==NULL) return DIV_CH_NOISE; + return sysDefs[sysOfChan[chan]]->chanTypes[dispatchChanOfChan[chan]]; } DivInstrumentType DivEngine::getPreferInsType(int chan) { - switch (sysOfChan[chan]) { - case DIV_SYSTEM_NULL: case DIV_SYSTEM_YMU759: - return chanPrefType[0][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_GENESIS: - return chanPrefType[1][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_GENESIS_EXT: - case DIV_SYSTEM_YM2612_EXT: - return chanPrefType[2][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_SMS: - return chanPrefType[3][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_SMS_OPLL: // this is flattened to SMS + OPLL. - case DIV_SYSTEM_NES_VRC7: // this is flattened to NES + VRC7. - return DIV_INS_OPLL; - break; - case DIV_SYSTEM_NES_FDS: // this is flattened to NES + FDS. - return DIV_INS_STD; - break; - case DIV_SYSTEM_GB: - return chanPrefType[4][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_PCE: - return chanPrefType[5][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_NES: - return chanPrefType[6][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_C64_6581: case DIV_SYSTEM_C64_8580: - return chanPrefType[7][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_ARCADE: - return chanPrefType[8][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_YM2610: - case DIV_SYSTEM_YM2610_FULL: - return chanPrefType[9][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_YM2610_EXT: - case DIV_SYSTEM_YM2610_FULL_EXT: - return chanPrefType[10][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_AY8910: - return chanPrefType[11][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_AMIGA: - return chanPrefType[12][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_YM2151: - return chanPrefType[13][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_YM2612: - return chanPrefType[14][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_TIA: - return chanPrefType[15][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_VIC20: - return chanPrefType[18][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_PET: - return chanPrefType[19][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_SNES: - case DIV_SYSTEM_RF5C68: - return chanPrefType[20][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_VRC6: - return chanPrefType[21][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_OPLL: - case DIV_SYSTEM_VRC7: - return chanPrefType[22][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_FDS: - return chanPrefType[23][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_MMC5: - return chanPrefType[24][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_OPN: - return chanPrefType[25][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_PC98: - return chanPrefType[26][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_OPL: - case DIV_SYSTEM_OPL2: - case DIV_SYSTEM_OPL3: - return chanPrefType[27][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_MULTIPCM: - case DIV_SYSTEM_SEGAPCM: - case DIV_SYSTEM_SEGAPCM_COMPAT: - case DIV_SYSTEM_QSOUND: - return chanPrefType[28][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_PCSPKR: - case DIV_SYSTEM_POKEMINI: - return chanPrefType[29][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_VBOY: - return chanPrefType[30][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_SCC: - return chanPrefType[36][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_N163: - return chanPrefType[37][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_YM2610B: - return chanPrefType[31][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_YM2610B_EXT: - return chanPrefType[43][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_OPLL_DRUMS: - return chanPrefType[32][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_OPL_DRUMS: - case DIV_SYSTEM_OPL2_DRUMS: - case DIV_SYSTEM_OPL3_DRUMS: - return chanPrefType[33][dispatchChanOfChan[chan]]; - return chanPrefType[33][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_SAA1099: - return chanPrefType[16][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_AY8930: - return chanPrefType[17][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_POKEY: - return chanPrefType[38][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_SFX_BEEPER: - return chanPrefType[39][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_SWAN: - return chanPrefType[40][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_OPZ: - return chanPrefType[41][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_LYNX: - return chanPrefType[42][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_VERA: - return chanPrefType[44][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_X1_010: - return chanPrefType[45][dispatchChanOfChan[chan]]; - break; - case DIV_SYSTEM_BUBSYS_WSG: - return chanPrefType[46][dispatchChanOfChan[chan]]; - break; - } - return DIV_INS_FM; + if (chan<0 || chan>chans) return DIV_INS_STD; + if (sysDefs[sysOfChan[chan]]==NULL) return DIV_INS_STD; + return sysDefs[sysOfChan[chan]]->chanInsType[dispatchChanOfChan[chan]][0]; } int DivEngine::minVGMVersion(DivSystem which) { @@ -1807,8 +356,9 @@ int DivEngine::minVGMVersion(DivSystem which) { // [this](int ch, unsigned char effect, unsigned char effectVal) -> bool {} (post effect handler, optional) // ); -// TODO: EVERYTHING void DivEngine::registerSystems() { + logD("registering systems..."); + sysDefs[DIV_SYSTEM_YMU759]=new DivSysDef( "Yamaha YMU759", NULL, 0x01, 0x01, 17, true, false, 0, false, {"Channel 1", "Channel 2", "Channel 3", "Channel 4", "Channel 5", "Channel 6", "Channel 7", "Channel 8", "Channel 9", "Channel 10", "Channel 11", "Channel 12", "Channel 13", "Channel 14", "Channel 15", "Channel 16", "PCM" }, // name @@ -1823,352 +373,567 @@ void DivEngine::registerSystems() { ); sysDefs[DIV_SYSTEM_GENESIS_EXT]=new DivSysDef( - "Sega Genesis Extended Channel 3", NULL, 0x02, 0x02, 10, true, true, 0, true, + "Sega Genesis Extended Channel 3", NULL, 0x42, 0x42, 13, true, true, 0, true, {}, {}, {}, {} ); sysDefs[DIV_SYSTEM_SMS]=new DivSysDef( - "TI SN76489", NULL, 0x02, 0x02, 10, true, true, 0, true, - {}, {}, {}, {} + "TI SN76489", NULL, 0x03, 0x03, 4, false, true, 0x150, false, + {"Square 1", "Square 2", "Square 3", "Noise"}, + {"S1", "S2", "S3", "NO"}, + {DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_NOISE}, + {DIV_INS_STD, DIV_INS_STD, DIV_INS_STD, DIV_INS_STD} ); sysDefs[DIV_SYSTEM_SMS_OPLL]=new DivSysDef( - "Sega Master System + FM Expansion", NULL, 0x02, 0x02, 10, true, true, 0, true, + "Sega Master System + FM Expansion", NULL, 0x43, 0x43, 13, true, true, 0, true, {}, {}, {}, {} ); sysDefs[DIV_SYSTEM_GB]=new DivSysDef( - "Game Boy", NULL, 0x02, 0x02, 10, true, true, 0, true, - {}, {}, {}, {} + "Game Boy", NULL, 0x04, 0x04, 4, false, true, 0x161, false, + {"Pulse 1", "Pulse 2", "Wavetable", "Noise"}, + {"S1", "S2", "WA", "NO"}, + {DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_WAVE, DIV_CH_NOISE}, + {DIV_INS_GB, DIV_INS_GB, DIV_INS_GB, DIV_INS_GB} ); sysDefs[DIV_SYSTEM_PCE]=new DivSysDef( - "PC Engine/TurboGrafx-16", NULL, 0x02, 0x02, 10, true, true, 0, true, - {}, {}, {}, {} + "PC Engine/TurboGrafx-16", NULL, 0x05, 0x05, 6, false, true, 0x161, false, + {"Channel 1", "Channel 2", "Channel 3", "Channel 4", "Channel 5", "Channel 6"}, + {"CH1", "CH2", "CH3", "CH4", "CH5", "CH6"}, + {DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE}, + {DIV_INS_PCE, DIV_INS_PCE, DIV_INS_PCE, DIV_INS_PCE, DIV_INS_PCE, DIV_INS_PCE} ); sysDefs[DIV_SYSTEM_NES]=new DivSysDef( - "NES (Ricoh 2A03)", NULL, 0x02, 0x02, 10, true, true, 0, true, - {}, {}, {}, {} + "NES (Ricoh 2A03)", NULL, 0x06, 0x06, 5, false, true, 0x161, false, + {"Pulse 1", "Pulse 2", "Triangle", "Noise", "PCM"}, + {"S1", "S2", "TR", "NO", "PCM"}, + {DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_WAVE, DIV_CH_NOISE, DIV_CH_PCM}, + {DIV_INS_STD, DIV_INS_STD, DIV_INS_STD, DIV_INS_STD, DIV_INS_AMIGA} ); sysDefs[DIV_SYSTEM_NES_VRC7]=new DivSysDef( - "NES + Konami VRC7", NULL, 0x02, 0x02, 10, true, true, 0, true, + "NES + Konami VRC7", NULL, 0x46, 0x46, 11, true, true, 0, true, {}, {}, {}, {} ); sysDefs[DIV_SYSTEM_NES_FDS]=new DivSysDef( - "Famicom Disk System", NULL, 0x02, 0x02, 10, true, true, 0, true, + "Famicom Disk System", NULL, 0, 0x86, 6, false, true, 0, true, {}, {}, {}, {} ); sysDefs[DIV_SYSTEM_C64_6581]=new DivSysDef( - "Commodore 64 (6581)", NULL, 0x02, 0x02, 10, true, true, 0, true, - {}, {}, {}, {} + "Commodore 64 (6581)", NULL, 0x47, 0x47, 3, false, true, 0, false, + {"Channel 1", "Channel 2", "Channel 3"}, + {"CH1", "CH2", "CH3"}, + {DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE}, + {DIV_INS_C64, DIV_INS_C64, DIV_INS_C64} ); sysDefs[DIV_SYSTEM_C64_8580]=new DivSysDef( - "Commodore 64 (8580)", NULL, 0x02, 0x02, 10, true, true, 0, true, - {}, {}, {}, {} + "Commodore 64 (8580)", NULL, 0x07, 0x07, 3, false, true, 0, false, + {"Channel 1", "Channel 2", "Channel 3"}, + {"CH1", "CH2", "CH3"}, + {DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE}, + {DIV_INS_C64, DIV_INS_C64, DIV_INS_C64} ); sysDefs[DIV_SYSTEM_ARCADE]=new DivSysDef( - "DefleCade", NULL, 0x02, 0x02, 10, true, true, 0, true, + "DefleCade", NULL, 0x08, 0x08, 13, true, false, 0, true, {}, {}, {}, {} ); sysDefs[DIV_SYSTEM_YM2610]=new DivSysDef( - "Neo Geo CD", NULL, 0x02, 0x02, 10, true, true, 0, true, - {}, {}, {}, {} + "Neo Geo CD", NULL, 0x09, 0x09, 13, true, true, 0x151, false, + {"FM 1", "FM 2", "FM 3", "FM 4", "PSG 1", "PSG 2", "PSG 3", "ADPCM-A 1", "ADPCM-A 2", "ADPCM-A 3", "ADPCM-A 4", "ADPCM-A 5", "ADPCM-A 6"}, + {"F1", "F2", "F3", "F4", "S1", "S2", "S3", "P1", "P2", "P3", "P4", "P5", "P6"}, + {DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM}, + {DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_AY, DIV_INS_AY, DIV_INS_AY, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA} ); sysDefs[DIV_SYSTEM_YM2610_EXT]=new DivSysDef( - "Neo Geo CD Extended Channel 2", NULL, 0x02, 0x02, 10, true, true, 0, true, - {}, {}, {}, {} + "Neo Geo CD Extended Channel 2", NULL, 0x49, 0x49, 16, true, true, 0x151, false, + {"FM 1", "FM 2 OP1", "FM 2 OP2", "FM 2 OP3", "FM 2 OP4", "FM 3", "FM 4", "PSG 1", "PSG 2", "PSG 3", "ADPCM-A 1", "ADPCM-A 2", "ADPCM-A 3", "ADPCM-A 4", "ADPCM-A 5", "ADPCM-A 6"}, + {"F1", "O1", "O2", "O3", "O4", "F3", "F4", "S1", "S2", "S3", "P1", "P2", "P3", "P4", "P5", "P6"}, + {DIV_CH_FM, DIV_CH_OP, DIV_CH_OP, DIV_CH_OP, DIV_CH_OP, DIV_CH_FM, DIV_CH_FM, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM}, + {DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_AY, DIV_INS_AY, DIV_INS_AY, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA} ); sysDefs[DIV_SYSTEM_AY8910]=new DivSysDef( - "AY-3-8910", NULL, 0x02, 0x02, 10, true, true, 0, true, - {}, {}, {}, {} + "AY-3-8910", NULL, 0x80, 0, 3, false, true, 0x151, false, + {"PSG 1", "PSG 2", "PSG 3"}, + {"S1", "S2", "S3"}, + {DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE}, + {DIV_INS_AY, DIV_INS_AY, DIV_INS_AY} ); sysDefs[DIV_SYSTEM_AMIGA]=new DivSysDef( - "Amiga", NULL, 0x02, 0x02, 10, true, true, 0, true, - {}, {}, {}, {} + "Amiga", NULL, 0x81, 0, 4, false, true, 0, false, + {"Channel 1", "Channel 2", "Channel 3", "Channel 4"}, + {"CH1", "CH2", "CH3", "CH4"}, + {DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM}, + {DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA} ); sysDefs[DIV_SYSTEM_YM2151]=new DivSysDef( - "Yamaha YM2151", NULL, 0x02, 0x02, 10, true, true, 0, true, - {}, {}, {}, {} + "Yamaha YM2151", NULL, 0x82, 0, 8, true, false, 0x150, false, + {"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "FM 7", "FM 8"}, + {"F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8"}, + {DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM}, + {DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM} ); sysDefs[DIV_SYSTEM_YM2612]=new DivSysDef( - "Yamaha YM2612", NULL, 0x02, 0x02, 10, true, true, 0, true, - {}, {}, {}, {} + "Yamaha YM2612", NULL, 0x83, 0, 6, true, false, 0x150, false, + {"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6"}, + {"F1", "F2", "F3", "F4", "F5", "F6"}, + {DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM}, + {DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM}, + {DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_AMIGA} ); sysDefs[DIV_SYSTEM_TIA]=new DivSysDef( - "Atari 2600", NULL, 0x02, 0x02, 10, true, true, 0, true, - {}, {}, {}, {} + "Atari 2600", NULL, 0x84, 0, 2, false, true, 0, false, + {"Channel 1", "Channel 2"}, + {"CH1", "CH2"}, + {DIV_CH_WAVE, DIV_CH_WAVE}, + {DIV_INS_TIA, DIV_INS_TIA} ); sysDefs[DIV_SYSTEM_SAA1099]=new DivSysDef( - "Philips SAA1099", NULL, 0x02, 0x02, 10, true, true, 0, true, - {}, {}, {}, {} + "Philips SAA1099", NULL, 0x97, 0, 6, false, true, 0x171, false, + {"PSG 1", "PSG 2", "PSG 3", "PSG 4", "PSG 5", "PSG 6"}, + {"S1", "S2", "S3", "S4", "S5", "S6"}, + {DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE}, + {DIV_INS_SAA1099, DIV_INS_SAA1099, DIV_INS_SAA1099, DIV_INS_SAA1099, DIV_INS_SAA1099, DIV_INS_SAA1099} ); sysDefs[DIV_SYSTEM_AY8930]=new DivSysDef( - "Microchip AY8930", NULL, 0x02, 0x02, 10, true, true, 0, true, - {}, {}, {}, {} + "Microchip AY8930", NULL, 0x9a, 0, 3, false, true, 0x151, false, + {"PSG 1", "PSG 2", "PSG 3"}, + {"S1", "S2", "S3"}, + {DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE}, + {DIV_INS_AY8930, DIV_INS_AY8930, DIV_INS_AY8930} ); sysDefs[DIV_SYSTEM_VIC20]=new DivSysDef( - "Commodore VIC-20", NULL, 0x02, 0x02, 10, true, true, 0, true, - {}, {}, {}, {} + "Commodore VIC-20", NULL, 0x85, 0, 4, false, true, 0, false, + {"Low", "Mid", "High", "Noise"}, + {"LO", "MID", "HI", "NO"}, + {DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_NOISE}, + {DIV_INS_VIC, DIV_INS_VIC, DIV_INS_VIC, DIV_INS_VIC} ); sysDefs[DIV_SYSTEM_PET]=new DivSysDef( - "Commodore PET", NULL, 0x02, 0x02, 10, true, true, 0, true, - {}, {}, {}, {} + "Commodore PET", NULL, 0x86, 0, 1, false, true, 0, false, + {"Wave"}, + {"PET"}, + {DIV_CH_PULSE}, + {DIV_INS_PET} ); + // TODO: DIV_INS_SNES sysDefs[DIV_SYSTEM_SNES]=new DivSysDef( - "SNES", NULL, 0x02, 0x02, 10, true, true, 0, true, - {}, {}, {}, {} + "SNES", NULL, 0x87, 0, 8, false, true, 0, false, + {"Channel 1", "Channel 2", "Channel 3", "Channel 4", "Channel 5", "Channel 6", "Channel 7", "Channel 8"}, + {"CH1", "CH2", "CH3", "CH4", "CH5", "CH6", "CH7", "CH8"}, + {DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM}, + {DIV_INS_SNES, DIV_INS_SNES, DIV_INS_SNES, DIV_INS_SNES, DIV_INS_SNES, DIV_INS_SNES, DIV_INS_SNES, DIV_INS_SNES} ); sysDefs[DIV_SYSTEM_VRC6]=new DivSysDef( - "Konami VRC6", NULL, 0x02, 0x02, 10, true, true, 0, true, - {}, {}, {}, {} + "Konami VRC6", NULL, 0x88, 0, 3, false, true, 0, false, + {"VRC6 1", "VRC6 2", "VRC6 Saw"}, + {"V1", "V2", "VS"}, + {DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_WAVE}, + {DIV_INS_VRC6, DIV_INS_VRC6, DIV_INS_VRC6_SAW}, + {DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_NULL} ); sysDefs[DIV_SYSTEM_OPLL]=new DivSysDef( - "Yamaha OPLL", NULL, 0x02, 0x02, 10, true, true, 0, true, - {}, {}, {}, {} + "Yamaha OPLL", NULL, 0x89, 0, 9, true, false, 0x150, false, + {"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "FM 7", "FM 8", "FM 9"}, + {"F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9"}, + {DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM}, + {DIV_INS_OPLL, DIV_INS_OPLL, DIV_INS_OPLL, DIV_INS_OPLL, DIV_INS_OPLL, DIV_INS_OPLL, DIV_INS_OPLL, DIV_INS_OPLL, DIV_INS_OPLL} ); sysDefs[DIV_SYSTEM_FDS]=new DivSysDef( - "Famicom Disk System (chip)", NULL, 0x02, 0x02, 10, true, true, 0, true, - {}, {}, {}, {} + "Famicom Disk System (chip)", NULL, 0x8a, 0, 1, false, true, 0x161, false, + {"FDS"}, + {"FDS"}, + {DIV_CH_WAVE}, + {DIV_INS_FDS} ); sysDefs[DIV_SYSTEM_MMC5]=new DivSysDef( - "MMC5", NULL, 0x02, 0x02, 10, true, true, 0, true, - {}, {}, {}, {} + "MMC5", NULL, 0x8b, 0, 3, false, true, 0, false, + {"Pulse 1", "Pulse 2", "PCM"}, + {"S1", "S2", "PCM"}, + {DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PCM}, + {DIV_INS_STD, DIV_INS_STD, DIV_INS_AMIGA} ); sysDefs[DIV_SYSTEM_N163]=new DivSysDef( - "Namco 163", NULL, 0x02, 0x02, 10, true, true, 0, true, - {}, {}, {}, {} + "Namco 163", NULL, 0x8c, 0, 8, false, true, 0, false, + {"Channel 1", "Channel 2", "Channel 3", "Channel 4", "Channel 5", "Channel 6", "Channel 7", "Channel 8"}, + {"CH1", "CH2", "CH3", "CH4", "CH5", "CH6", "CH7", "CH8"}, + {DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE}, + {DIV_INS_N163, DIV_INS_N163, DIV_INS_N163, DIV_INS_N163, DIV_INS_N163, DIV_INS_N163, DIV_INS_N163, DIV_INS_N163} ); sysDefs[DIV_SYSTEM_OPN]=new DivSysDef( - "Yamaha YM2203", NULL, 0x02, 0x02, 10, true, true, 0, true, - {}, {}, {}, {} + "Yamaha YM2203", NULL, 0x8d, 0, 6, true, false, 0, false, + {"FM 1", "FM 2", "FM 3", "PSG 1", "PSG 2", "PSG 3"}, + {"F1", "F2", "F3", "S1", "S2", "S3"}, + {DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE}, + {DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_AY, DIV_INS_AY, DIV_INS_AY} ); sysDefs[DIV_SYSTEM_PC98]=new DivSysDef( - "Yamaha YM2608", NULL, 0x02, 0x02, 10, true, true, 0, true, - {}, {}, {}, {} + "Yamaha YM2608", NULL, 0x8e, 0, 16, true, false, 0, false, + {"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "Square 1", "Square 2", "Square 3", "Kick", "Snare", "Top", "HiHat", "Tom", "Rim", "ADPCM"}, + {"F1", "F2", "F3", "F4", "F5", "F6", "S1", "S2", "S3", "BD", "SD", "TP", "HH", "TM", "RM", "P"}, + {DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_PCM}, + {DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_AY, DIV_INS_AY, DIV_INS_AY, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA} ); sysDefs[DIV_SYSTEM_OPL]=new DivSysDef( - "Yamaha OPL", NULL, 0x02, 0x02, 10, true, true, 0, true, - {}, {}, {}, {} + "Yamaha OPL", NULL, 0x8f, 0, 9, true, false, 0x151, false, + {"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "FM 7", "FM 8", "FM 9"}, + {"F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9"}, + {DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM}, + {DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL} ); sysDefs[DIV_SYSTEM_OPL2]=new DivSysDef( - "Yamaha OPL2", NULL, 0x02, 0x02, 10, true, true, 0, true, - {}, {}, {}, {} + "Yamaha OPL2", NULL, 0x90, 0, 9, true, false, 0x151, false, + {"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "FM 7", "FM 8", "FM 9"}, + {"F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9"}, + {DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM}, + {DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL} ); sysDefs[DIV_SYSTEM_OPL3]=new DivSysDef( - "Yamaha OPL3", NULL, 0x02, 0x02, 10, true, true, 0, true, - {}, {}, {}, {} + "Yamaha OPL3", NULL, 0x91, 0, 18, true, false, 0x151, false, + {"4OP 1", "FM 2", "4OP 3", "FM 4", "4OP 5", "FM 6", "4OP 7", "FM 8", "4OP 9", "FM 10", "4OP 11", "FM 12", "FM 13", "FM 14", "FM 15", "FM 16", "FM 17", "FM 18"}, + {"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18"}, + {DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM}, + {DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL} ); sysDefs[DIV_SYSTEM_MULTIPCM]=new DivSysDef( - "MultiPCM", NULL, 0x02, 0x02, 10, true, true, 0, true, - {}, {}, {}, {} + "MultiPCM", NULL, 0x92, 0, 28, false, true, 0, false, + {"Channel 1", "Channel 2", "Channel 3", "Channel 4", "Channel 5", "Channel 6", "Channel 7", "Channel 8", "Channel 9", "Channel 10", "Channel 11", "Channel 12", "Channel 13", "Channel 14", "Channel 15", "Channel 16", "Channel 17", "Channel 18", "Channel 19", "Channel 20", "Channel 21", "Channel 22", "Channel 23", "Channel 24", "Channel 25", "Channel 26", "Channel 27", "Channel 28"}, + {"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28"}, + {DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM}, + {DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM} ); sysDefs[DIV_SYSTEM_PCSPKR]=new DivSysDef( - "PC Speaker", NULL, 0x02, 0x02, 10, true, true, 0, true, - {}, {}, {}, {} + "PC Speaker", NULL, 0x93, 0, 1, false, true, 0, false, + {"Square"}, + {"SQ"}, + {DIV_CH_PULSE}, + {DIV_INS_STD} ); sysDefs[DIV_SYSTEM_POKEY]=new DivSysDef( - "POKEY", NULL, 0x02, 0x02, 10, true, true, 0, true, - {}, {}, {}, {} + "POKEY", NULL, 0x94, 0, 4, false, true, 0, false, + {"Channel 1", "Channel 2", "Channel 3", "Channel 4"}, + {"CH1", "CH2", "CH3", "CH4"}, + {DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE}, + {DIV_INS_POKEY, DIV_INS_POKEY, DIV_INS_POKEY, DIV_INS_POKEY} ); sysDefs[DIV_SYSTEM_RF5C68]=new DivSysDef( - "Ricoh RF5C68", NULL, 0x02, 0x02, 10, true, true, 0, true, - {}, {}, {}, {} + "Ricoh RF5C68", NULL, 0x95, 0, 8, false, true, 0, false, + {"Channel 1", "Channel 2", "Channel 3", "Channel 4", "Channel 5", "Channel 6", "Channel 7", "Channel 8"}, + {"CH1", "CH2", "CH3", "CH4", "CH5", "CH6", "CH7", "CH8"}, + {DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM}, + {DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA} ); sysDefs[DIV_SYSTEM_SWAN]=new DivSysDef( - "WonderSwan", NULL, 0x02, 0x02, 10, true, true, 0, true, - {}, {}, {}, {} + "WonderSwan", NULL, 0x96, 0, 4, false, true, 0x171, false, + {"Wave", "Wave/PCM", "Wave", "Wave/Noise"}, + {"CH1", "CH2", "CH3", "CH4"}, + {DIV_CH_WAVE, DIV_CH_PCM, DIV_CH_WAVE, DIV_CH_NOISE}, + {DIV_INS_SWAN, DIV_INS_SWAN, DIV_INS_SWAN, DIV_INS_SWAN}, + {DIV_INS_NULL, DIV_INS_AMIGA, DIV_INS_NULL, DIV_INS_NULL} ); sysDefs[DIV_SYSTEM_OPZ]=new DivSysDef( - "Yamaha TX81Z/YS200", NULL, 0x02, 0x02, 10, true, true, 0, true, - {}, {}, {}, {} + "Yamaha TX81Z/YS200", NULL, 0x98, 0, 8, true, false, 0, false, + {"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "FM 7", "FM 8"}, + {"F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8"}, + {DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM}, + {DIV_INS_OPZ, DIV_INS_OPZ, DIV_INS_OPZ, DIV_INS_OPZ, DIV_INS_OPZ, DIV_INS_OPZ, DIV_INS_OPZ, DIV_INS_OPZ} ); sysDefs[DIV_SYSTEM_POKEMINI]=new DivSysDef( - "Pokémon Mini", NULL, 0x02, 0x02, 10, true, true, 0, true, - {}, {}, {}, {} + "Pokémon Mini", NULL, 0x99, 0, 1, false, true, 0, false, + {"Square"}, + {"SQ"}, + {DIV_CH_PULSE}, + {DIV_INS_STD} ); sysDefs[DIV_SYSTEM_SEGAPCM]=new DivSysDef( - "SegaPCM", NULL, 0x02, 0x02, 10, true, true, 0, true, - {}, {}, {}, {} + "SegaPCM", NULL, 0x9b, 0, 16, false, true, 0x151, false, + {"Channel 1", "Channel 2", "Channel 3", "Channel 4", "Channel 5", "Channel 6", "Channel 7", "Channel 8", "Channel 9", "Channel 10", "Channel 11", "Channel 12", "Channel 13", "Channel 14", "Channel 15", "Channel 16"}, + {"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16"}, + {DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM}, + {DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA} ); sysDefs[DIV_SYSTEM_VBOY]=new DivSysDef( - "Virtual Boy", NULL, 0x02, 0x02, 10, true, true, 0, true, - {}, {}, {}, {} + "Virtual Boy", NULL, 0x9c, 0, 6, false, true, 0, false, + {"Channel 1", "Channel 2", "Channel 3", "Channel 4", "Channel 5", "Noise"}, + {"CH1", "CH2", "CH3", "CH4", "CH5", "NO"}, + {DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_NOISE}, + {DIV_INS_VBOY, DIV_INS_VBOY, DIV_INS_VBOY, DIV_INS_VBOY, DIV_INS_VBOY, DIV_INS_VBOY} ); sysDefs[DIV_SYSTEM_VRC7]=new DivSysDef( - "Konami VRC7", NULL, 0x02, 0x02, 10, true, true, 0, true, - {}, {}, {}, {} + "Konami VRC7", NULL, 0x9d, 0, 6, true, false, 0x151, false, + {"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6"}, + {"F1", "F2", "F3", "F4", "F5", "F6"}, + {DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM}, + {DIV_INS_OPLL, DIV_INS_OPLL, DIV_INS_OPLL, DIV_INS_OPLL, DIV_INS_OPLL, DIV_INS_OPLL} ); sysDefs[DIV_SYSTEM_YM2610B]=new DivSysDef( - "Yamaha YM2610B", NULL, 0x02, 0x02, 10, true, true, 0, true, - {}, {}, {}, {} + "Yamaha YM2610B", NULL, 0x9e, 0, 16, true, false, 0x151, false, + {"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "PSG 1", "PSG 2", "PSG 3", "ADPCM-A 1", "ADPCM-A 2", "ADPCM-A 3", "ADPCM-A 4", "ADPCM-A 5", "ADPCM-A 6", "ADPCM-B"}, + {"F1", "F2", "F3", "F4", "F5", "F6", "S1", "S2", "S3", "P1", "P2", "P3", "P4", "P5", "P6", "B"}, + {DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM}, + {DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_AY, DIV_INS_AY, DIV_INS_AY, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA} ); sysDefs[DIV_SYSTEM_SFX_BEEPER]=new DivSysDef( - "ZX Spectrum Beeper", NULL, 0x02, 0x02, 10, true, true, 0, true, - {}, {}, {}, {} + "ZX Spectrum Beeper", NULL, 0x9f, 0, 6, false, true, 0, false, + {"Channel 1", "Channel 2", "Channel 3", "Channel 4", "Channel 5", "Channel 6"}, + {"CH1", "CH2", "CH3", "CH4", "CH5", "CH6"}, + {DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE}, + {DIV_INS_BEEPER, DIV_INS_BEEPER, DIV_INS_BEEPER, DIV_INS_BEEPER, DIV_INS_BEEPER, DIV_INS_BEEPER} ); sysDefs[DIV_SYSTEM_YM2612_EXT]=new DivSysDef( - "Yamaha YM2612 Extended Channel 3", NULL, 0x02, 0x02, 10, true, true, 0, true, - {}, {}, {}, {} + "Yamaha YM2612 Extended Channel 3", NULL, 0xa0, 0, 9, true, false, 0x150, false, + {"FM 1", "FM 2", "FM 3 OP1", "FM 3 OP2", "FM 3 OP3", "FM 3 OP4", "FM 4", "FM 5", "FM 6"}, + {"F1", "F2", "O1", "O2", "O3", "O4", "F4", "F5", "F6"}, + {DIV_CH_FM, DIV_CH_FM, DIV_CH_OP, DIV_CH_OP, DIV_CH_OP, DIV_CH_OP, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM}, + {DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM}, + {DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_AMIGA} ); sysDefs[DIV_SYSTEM_SCC]=new DivSysDef( - "Konami SCC", NULL, 0x02, 0x02, 10, true, true, 0, true, - {}, {}, {}, {} + "Konami SCC", NULL, 0xa1, 0, 5, false, true, 0, false, + {"Channel 1", "Channel 2", "Channel 3", "Channel 4", "Channel 5"}, + {"CH1", "CH2", "CH3", "CH4", "CH5"}, + {DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE}, + {DIV_INS_SCC, DIV_INS_SCC, DIV_INS_SCC, DIV_INS_SCC, DIV_INS_SCC} ); sysDefs[DIV_SYSTEM_OPL_DRUMS]=new DivSysDef( - "Yamaha OPL with drums", NULL, 0x02, 0x02, 10, true, true, 0, true, - {}, {}, {}, {} + "Yamaha OPL with drums", NULL, 0xa2, 0, 11, true, false, 0x151, false, + {"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "Kick", "Snare", "Tom", "Top", "HiHat"}, + {"F1", "F2", "F3", "F4", "F5", "F6", "BD", "SD", "TM", "TP", "HH"}, + {DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE}, + {DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL} ); sysDefs[DIV_SYSTEM_OPL2_DRUMS]=new DivSysDef( - "Yamaha OPL2 with drums", NULL, 0x02, 0x02, 10, true, true, 0, true, - {}, {}, {}, {} + "Yamaha OPL2 with drums", NULL, 0xa3, 0, 11, true, false, 0x151, false, + {"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "Kick", "Snare", "Tom", "Top", "HiHat"}, + {"F1", "F2", "F3", "F4", "F5", "F6", "BD", "SD", "TM", "TP", "HH"}, + {DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE}, + {DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL} ); sysDefs[DIV_SYSTEM_OPL3_DRUMS]=new DivSysDef( - "Yamaha OPL3 with drums", NULL, 0x02, 0x02, 10, true, true, 0, true, - {}, {}, {}, {} + "Yamaha OPL3 with drums", NULL, 0xa4, 0, 20, true, false, 0x151, false, + {"4OP 1", "FM 2", "4OP 3", "FM 4", "4OP 5", "FM 6", "4OP 7", "FM 8", "4OP 9", "FM 10", "4OP 11", "FM 12", "FM 13", "FM 14", "FM 15", "Kick", "Snare", "Tom", "Top", "HiHat"}, + {"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "BD", "SD", "TM", "TP", "HH"}, + {DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE}, + {DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL} ); sysDefs[DIV_SYSTEM_YM2610_FULL]=new DivSysDef( - "Yamaha YM2610", NULL, 0x02, 0x02, 10, true, true, 0, true, - {}, {}, {}, {} + "Yamaha YM2610", NULL, 0xa5, 0, 14, true, false, 0x151, false, + {"FM 1", "FM 2", "FM 3", "FM 4", "PSG 1", "PSG 2", "PSG 3", "ADPCM-A 1", "ADPCM-A 2", "ADPCM-A 3", "ADPCM-A 4", "ADPCM-A 5", "ADPCM-A 6", "ADPCM-B"}, + {"F1", "F2", "F3", "F4", "S1", "S2", "S3", "P1", "P2", "P3", "P4", "P5", "P6", "B"}, + {DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM}, + {DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_AY, DIV_INS_AY, DIV_INS_AY, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA} ); sysDefs[DIV_SYSTEM_YM2610_FULL_EXT]=new DivSysDef( - "Yamaha YM2610 Extended Channel 2", NULL, 0x02, 0x02, 10, true, true, 0, true, - {}, {}, {}, {} + "Yamaha YM2610 Extended Channel 2", NULL, 0xa6, 0, 17, true, false, 0x151, false, + {"FM 1", "FM 2 OP1", "FM 2 OP2", "FM 2 OP3", "FM 2 OP4", "FM 3", "FM 4", "PSG 1", "PSG 2", "PSG 3", "ADPCM-A 1", "ADPCM-A 2", "ADPCM-A 3", "ADPCM-A 4", "ADPCM-A 5", "ADPCM-A 6", "ADPCM-B"}, + {"F1", "O1", "O2", "O3", "O4", "F3", "F4", "S1", "S2", "S3", "P1", "P2", "P3", "P4", "P5", "P6", "B"}, + {DIV_CH_FM, DIV_CH_OP, DIV_CH_OP, DIV_CH_OP, DIV_CH_OP, DIV_CH_FM, DIV_CH_FM, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM}, + {DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_AY, DIV_INS_AY, DIV_INS_AY, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA} ); sysDefs[DIV_SYSTEM_OPLL_DRUMS]=new DivSysDef( - "Yamaha OPLL with drums", NULL, 0x02, 0x02, 10, true, true, 0, true, - {}, {}, {}, {} + "Yamaha OPLL with drums", NULL, 0xa7, 0, 11, true, false, 0x150, false, + {"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "Kick", "Snare", "Tom", "Top", "HiHat"}, + {"F1", "F2", "F3", "F4", "F5", "F6", "BD", "SD", "TM", "TP", "HH"}, + {DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE}, + {DIV_INS_OPLL, DIV_INS_OPLL, DIV_INS_OPLL, DIV_INS_OPLL, DIV_INS_OPLL, DIV_INS_OPLL, DIV_INS_OPLL, DIV_INS_OPLL, DIV_INS_OPLL, DIV_INS_OPLL, DIV_INS_OPLL} ); sysDefs[DIV_SYSTEM_LYNX]=new DivSysDef( - "Atari Lynx", NULL, 0x02, 0x02, 10, true, true, 0, true, - {}, {}, {}, {} + "Atari Lynx", NULL, 0xa8, 0, 4, false, true, 0, false, + {"Channel 1", "Channel 2", "Channel 3", "Channel 4"}, + {"CH1", "CH2", "CH3", "CH4"}, + {DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE}, + {DIV_INS_MIKEY, DIV_INS_MIKEY, DIV_INS_MIKEY, DIV_INS_MIKEY} ); sysDefs[DIV_SYSTEM_QSOUND]=new DivSysDef( - "Capcom QSound", NULL, 0x02, 0x02, 10, true, true, 0, true, - {}, {}, {}, {} + "Capcom QSound", NULL, 0xe0, 0, 19, false, true, 0x161, false, + {"PCM 1", "PCM 2", "PCM 3", "PCM 4", "PCM 5", "PCM 6", "PCM 7", "PCM 8", "PCM 9", "PCM 10", "PCM 11", "PCM 12", "PCM 13", "PCM 14", "PCM 15", "PCM 16", "ADPCM 1", "ADPCM 2", "ADPCM 3"}, + {"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "A1", "A2", "A3"}, + {DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE}, + {DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA} ); sysDefs[DIV_SYSTEM_VERA]=new DivSysDef( - "VERA", NULL, 0x02, 0x02, 10, true, true, 0, true, - {}, {}, {}, {} + "VERA", NULL, 0xac, 0, 17, false, true, 0, false, + {"Channel 1", "Channel 2", "Channel 3", "Channel 4", "Channel 5", "Channel 6", "Channel 7", "Channel 8", "Channel 9", "Channel 10", "Channel 11", "Channel 12", "Channel 13", "Channel 14", "Channel 15", "Channel 16", "PCM"}, + {"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "PCM"}, + {DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PCM}, + {DIV_INS_VERA, DIV_INS_VERA, DIV_INS_VERA, DIV_INS_VERA, DIV_INS_VERA, DIV_INS_VERA, DIV_INS_VERA, DIV_INS_VERA, DIV_INS_VERA, DIV_INS_VERA, DIV_INS_VERA, DIV_INS_VERA, DIV_INS_VERA, DIV_INS_VERA, DIV_INS_VERA, DIV_INS_VERA, DIV_INS_AMIGA} ); sysDefs[DIV_SYSTEM_YM2610B_EXT]=new DivSysDef( - "Yamaha YM2610B Extended Channel 3", NULL, 0x02, 0x02, 10, true, true, 0, true, - {}, {}, {}, {} + "Yamaha YM2610B Extended Channel 3", NULL, 0xde, 0, 19, true, false, 0x151, false, + {"FM 1", "FM 2", "FM 3 OP1", "FM 3 OP2", "FM 3 OP3", "FM 3 OP4", "FM 4", "FM 5", "FM 6", "PSG 1", "PSG 2", "PSG 3", "ADPCM-A 1", "ADPCM-A 2", "ADPCM-A 3", "ADPCM-A 4", "ADPCM-A 5", "ADPCM-A 6", "ADPCM-B"}, + {"F1", "F2", "O1", "O2", "O3", "O4", "F4", "F5", "F6", "S1", "S2", "S3", "P1", "P2", "P3", "P4", "P5", "P6", "B"}, + {DIV_CH_FM, DIV_CH_FM, DIV_CH_OP, DIV_CH_OP, DIV_CH_OP, DIV_CH_OP, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM}, + {DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_AY, DIV_INS_AY, DIV_INS_AY, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA} ); sysDefs[DIV_SYSTEM_SEGAPCM_COMPAT]=new DivSysDef( - "SegaPCM (compatible 5-channel mode)", NULL, 0x02, 0x02, 10, true, true, 0, true, - {}, {}, {}, {} + "SegaPCM (compatible 5-channel mode)", NULL, 0xa9, 0, 5, false, true, 0x151, false, + {"Channel 1", "Channel 2", "Channel 3", "Channel 4", "Channel 5"}, + {"P1", "P2", "P3", "P4", "P5"}, + {DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM}, + {DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA} ); sysDefs[DIV_SYSTEM_X1_010]=new DivSysDef( - "Seta/Allumer X1-010", NULL, 0x02, 0x02, 10, true, true, 0, true, - {}, {}, {}, {} + "Seta/Allumer X1-010", NULL, 0xb0, 0, 16, false, true, 0x171, false, + {"Channel 1", "Channel 2", "Channel 3", "Channel 4", "Channel 5", "Channel 6", "Channel 7", "Channel 8", "Channel 9", "Channel 10", "Channel 11", "Channel 12", "Channel 13", "Channel 14", "Channel 15", "Channel 16"}, + {"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16"}, + {DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE}, + {DIV_INS_X1_010, DIV_INS_X1_010, DIV_INS_X1_010, DIV_INS_X1_010, DIV_INS_X1_010, DIV_INS_X1_010, DIV_INS_X1_010, DIV_INS_X1_010, DIV_INS_X1_010, DIV_INS_X1_010, DIV_INS_X1_010, DIV_INS_X1_010, DIV_INS_X1_010, DIV_INS_X1_010, DIV_INS_X1_010, DIV_INS_X1_010}, + {DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA} ); sysDefs[DIV_SYSTEM_BUBSYS_WSG]=new DivSysDef( - "Konami Bubble System WSG", NULL, 0x02, 0x02, 10, true, true, 0, true, - {}, {}, {}, {} + "Konami Bubble System WSG", NULL, 0xad, 0, 2, false, true, 0, false, + {"Channel 1", "Channel 2"}, + {"CH1", "CH2"}, + {DIV_CH_WAVE, DIV_CH_WAVE}, + {DIV_INS_SCC, DIV_INS_SCC} ); + // to Grauw: feel free to change this to 24 during development of OPL4's PCM part. sysDefs[DIV_SYSTEM_OPL4]=new DivSysDef( - "Yamaha OPL4", NULL, 0x02, 0x02, 10, true, true, 0, true, - {}, {}, {}, {} + "Yamaha OPL4", NULL, 0xae, 0, 42, true, true, 0, false, + {"4OP 1", "FM 2", "4OP 3", "FM 4", "4OP 5", "FM 6", "4OP 7", "FM 8", "4OP 9", "FM 10", "4OP 11", "FM 12", "FM 13", "FM 14", "FM 15", "FM 16", "FM 17", "FM 18", "PCM 1", "PCM 2", "PCM 3", "PCM 4", "PCM 5", "PCM 6", "PCM 7", "PCM 8", "PCM 9", "PCM 10", "PCM 11", "PCM 12", "PCM 13", "PCM 14", "PCM 15", "PCM 16", "PCM 17", "PCM 18", "PCM 19", "PCM 20", "PCM 21", "PCM 22", "PCM 23", "PCM 24"}, + {"F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12", "F13", "F14", "F15", "F16", "F17", "F18", "P1", "P2", "P3", "P4", "P5", "P6", "P7", "P8", "P8", "P10", "P11", "P12", "P13", "P14", "P15", "P16", "P17", "P18", "P19", "P20", "P21", "P22", "P23", "P24"}, + {DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM}, + {DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM} ); sysDefs[DIV_SYSTEM_OPL4_DRUMS]=new DivSysDef( - "Yamaha OPL4 with drums", NULL, 0x02, 0x02, 10, true, true, 0, true, - {}, {}, {}, {} + "Yamaha OPL4 with drums", NULL, 0xaf, 0, 44, true, true, 0, false, + {"4OP 1", "FM 2", "4OP 3", "FM 4", "4OP 5", "FM 6", "4OP 7", "FM 8", "4OP 9", "FM 10", "4OP 11", "FM 12", "FM 13", "FM 14", "FM 15", "Kick", "Snare", "Tom", "Top", "HiHat", "PCM 1", "PCM 2", "PCM 3", "PCM 4", "PCM 5", "PCM 6", "PCM 7", "PCM 8", "PCM 9", "PCM 10", "PCM 11", "PCM 12", "PCM 13", "PCM 14", "PCM 15", "PCM 16", "PCM 17", "PCM 18", "PCM 19", "PCM 20", "PCM 21", "PCM 22", "PCM 23", "PCM 24"}, + {"F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12", "F13", "F14", "F15", "BD", "SD", "TM", "TP", "HH", "P1", "P2", "P3", "P4", "P5", "P6", "P7", "P8", "P8", "P10", "P11", "P12", "P13", "P14", "P15", "P16", "P17", "P18", "P19", "P20", "P21", "P22", "P23", "P24"}, + {DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM}, + {DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM} ); sysDefs[DIV_SYSTEM_ES5506]=new DivSysDef( - "Ensoniq ES5506", NULL, 0x02, 0x02, 10, true, true, 0, true, - {}, {}, {}, {} + "Ensoniq ES5506", NULL, 0xb1, 0, 32, false, true, 0, false, + {"Channel 1", "Channel 2", "Channel 3", "Channel 4", "Channel 5", "Channel 6", "Channel 7", "Channel 8", "Channel 9", "Channel 10", "Channel 11", "Channel 12", "Channel 13", "Channel 14", "Channel 15", "Channel 16", "Channel 17", "Channel 18", "Channel 19", "Channel 20", "Channel 21", "Channel 22", "Channel 23", "Channel 24", "Channel 25", "Channel 26", "Channel 27", "Channel 28", "Channel 29", "Channel 30", "Channel 31", "Channel 32"}, + {"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31", "32"}, + {DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM}, + {DIV_INS_ES5506, DIV_INS_ES5506, DIV_INS_ES5506, DIV_INS_ES5506, DIV_INS_ES5506, DIV_INS_ES5506, DIV_INS_ES5506, DIV_INS_ES5506, DIV_INS_ES5506, DIV_INS_ES5506, DIV_INS_ES5506, DIV_INS_ES5506, DIV_INS_ES5506, DIV_INS_ES5506, DIV_INS_ES5506, DIV_INS_ES5506, DIV_INS_ES5506, DIV_INS_ES5506, DIV_INS_ES5506, DIV_INS_ES5506, DIV_INS_ES5506, DIV_INS_ES5506, DIV_INS_ES5506, DIV_INS_ES5506, DIV_INS_ES5506, DIV_INS_ES5506, DIV_INS_ES5506, DIV_INS_ES5506, DIV_INS_ES5506, DIV_INS_ES5506, DIV_INS_ES5506, DIV_INS_ES5506} ); sysDefs[DIV_SYSTEM_Y8950]=new DivSysDef( - "Yamaha Y8950", NULL, 0x02, 0x02, 10, true, true, 0, true, - {}, {}, {}, {} + "Yamaha Y8950", NULL, 0xb2, 0, 10, true, false, 0, false, + {"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "FM 7", "FM 8", "FM 9", "PCM"}, + {"F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "PCM"}, + {DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_PCM}, + {DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_AMIGA} ); sysDefs[DIV_SYSTEM_Y8950_DRUMS]=new DivSysDef( - "Yamaha Y8950 with drums", NULL, 0x02, 0x02, 10, true, true, 0, true, - {}, {}, {}, {} + "Yamaha Y8950 with drums", NULL, 0xb3, 0, 12, true, false, 0, false, + {"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "Kick", "Snare", "Tom", "Top", "HiHat", "PCM"}, + {"F1", "F2", "F3", "F4", "F5", "F6", "BD", "SD", "TM", "TP", "HH", "PCM"}, + {DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_PCM}, + {DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_AMIGA} ); sysDefs[DIV_SYSTEM_SCC_PLUS]=new DivSysDef( - "Konami SCC+", NULL, 0x02, 0x02, 10, true, true, 0, true, - {}, {}, {}, {} + "Konami SCC+", NULL, 0xb4, 0, 5, false, true, 0, false, + {"Channel 1", "Channel 2", "Channel 3", "Channel 4", "Channel 5"}, + {"CH1", "CH2", "CH3", "CH4", "CH5"}, + {DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE}, + {DIV_INS_SCC, DIV_INS_SCC, DIV_INS_SCC, DIV_INS_SCC, DIV_INS_SCC} ); sysDefs[DIV_SYSTEM_SOUND_UNIT]=new DivSysDef( - "tildearrow Sound Unit", NULL, 0x02, 0x02, 10, true, true, 0, true, - {}, {}, {}, {} + "tildearrow Sound Unit", NULL, 0xb5, 0, 8, false, true, 0, false, + {"Channel 1", "Channel 2", "Channel 3", "Channel 4", "Channel 5", "Channel 6", "Channel 7", "Channel 8"}, + {"CH1", "CH2", "CH3", "CH4", "CH5", "CH6", "CH7", "CH8"}, + {DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE}, + {DIV_INS_SU, DIV_INS_SU, DIV_INS_SU, DIV_INS_SU, DIV_INS_SU, DIV_INS_SU, DIV_INS_SU, DIV_INS_SU}, + {DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA} ); sysDefs[DIV_SYSTEM_MSM6295]=new DivSysDef( - "OKI MSM6295", NULL, 0x02, 0x02, 10, true, true, 0, true, - {}, {}, {}, {} + "OKI MSM6295", NULL, 0xaa, 0, 4, false, true, 0, false, + {"Channel 1", "Channel 2", "Channel 3", "Channel 4"}, + {"CH1", "CH2", "CH3", "CH4"}, + {DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM}, + {DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA} ); sysDefs[DIV_SYSTEM_MSM6258]=new DivSysDef( - "OKI MSM6258", NULL, 0x02, 0x02, 10, true, true, 0, true, - {}, {}, {}, {} + "OKI MSM6258", NULL, 0xab, 0, 1, false, true, 0, false, + {"Sample"}, + {"PCM"}, + {DIV_CH_PCM}, + {DIV_INS_AMIGA} ); sysDefs[DIV_SYSTEM_DUMMY]=new DivSysDef( - "Dummy System", NULL, 0x02, 0x02, 10, true, true, 0, true, - {}, {}, {}, {} + "Dummy System", NULL, 0xfd, 0, 8, false, true, 0, false, + {"Channel 1", "Channel 2", "Channel 3", "Channel 4", "Channel 5", "Channel 6", "Channel 7", "Channel 8"}, + {"CH1", "CH2", "CH3", "CH4", "CH5", "CH6", "CH7", "CH8"}, + {DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE}, + {DIV_INS_STD, DIV_INS_STD, DIV_INS_STD, DIV_INS_STD, DIV_INS_STD, DIV_INS_STD, DIV_INS_STD, DIV_INS_STD} ); + + for (int i=0; i<256; i++) { + if (sysDefs[i]==NULL) continue; + if (sysDefs[i]->id!=0) { + sysFileMapFur[sysDefs[i]->id]=(DivSystem)i; + } + if (sysDefs[i]->id_DMF!=0) { + sysFileMapDMF[sysDefs[i]->id_DMF]=(DivSystem)i; + } + } + + systemsRegistered=true; } diff --git a/src/gui/dataList.cpp b/src/gui/dataList.cpp index 671eb451e..652c2eb99 100644 --- a/src/gui/dataList.cpp +++ b/src/gui/dataList.cpp @@ -187,6 +187,22 @@ void FurnaceGUI::drawInsList() { ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_X1_010]); name=fmt::sprintf(ICON_FA_BAR_CHART " %.2X: %s##_INS%d",i,ins->name,i); break; + case DIV_INS_ES5506: + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_ES5506]); + name=fmt::sprintf(ICON_FA_VOLUME_UP " %.2X: %s##_INS%d",i,ins->name,i); + break; + case DIV_INS_MULTIPCM: + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_MULTIPCM]); + name=fmt::sprintf(ICON_FA_VOLUME_UP " %.2X: %s##_INS%d",i,ins->name,i); + break; + case DIV_INS_SNES: + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_SNES]); + name=fmt::sprintf(ICON_FA_VOLUME_UP " %.2X: %s##_INS%d",i,ins->name,i); + break; + case DIV_INS_SU: + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_SU]); + name=fmt::sprintf(ICON_FA_MICROCHIP " %.2X: %s##_INS%d",i,ins->name,i); + break; default: ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_UNKNOWN]); name=fmt::sprintf(ICON_FA_QUESTION " %.2X: %s##_INS%d",i,ins->name,i); diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 6225b9293..372cf998d 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -505,9 +505,11 @@ bool FurnaceGUI::CWVSliderInt(const char* label, const ImVec2& size, int* v, int } const char* FurnaceGUI::getSystemName(DivSystem which) { + /* if (settings.chipNames) { return e->getSystemChips(which); } + */ return e->getSystemName(which); } @@ -2670,7 +2672,7 @@ bool FurnaceGUI::loop() { ImGui::Separator(); if (ImGui::BeginMenu("add system...")) { for (int j=0; availableSystems[j]; j++) { - if (!settings.hiddenSystems && availableSystems[j]==DIV_SYSTEM_YMU759) continue; + if (!settings.hiddenSystems && (availableSystems[j]==DIV_SYSTEM_YMU759 || availableSystems[j]==DIV_SYSTEM_DUMMY || availableSystems[j]==DIV_SYSTEM_SOUND_UNIT)) continue; sysAddOption((DivSystem)availableSystems[j]); } ImGui::EndMenu(); @@ -2688,7 +2690,7 @@ bool FurnaceGUI::loop() { for (int i=0; isong.systemLen; i++) { if (ImGui::BeginMenu(fmt::sprintf("%d. %s##_SYSC%d",i+1,getSystemName(e->song.system[i]),i).c_str())) { for (int j=0; availableSystems[j]; j++) { - if (!settings.hiddenSystems && availableSystems[j]==DIV_SYSTEM_YMU759) continue; + if (!settings.hiddenSystems && (availableSystems[j]==DIV_SYSTEM_YMU759 || availableSystems[j]==DIV_SYSTEM_DUMMY || availableSystems[j]==DIV_SYSTEM_SOUND_UNIT)) continue; sysChangeOption(i,(DivSystem)availableSystems[j]); } ImGui::EndMenu(); diff --git a/src/gui/gui.h b/src/gui/gui.h index 670718144..e5ac963b8 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -145,6 +145,10 @@ enum FurnaceGUIColors { GUI_COLOR_INSTR_VERA, GUI_COLOR_INSTR_X1_010, GUI_COLOR_INSTR_VRC6_SAW, + GUI_COLOR_INSTR_ES5506, + GUI_COLOR_INSTR_MULTIPCM, + GUI_COLOR_INSTR_SNES, + GUI_COLOR_INSTR_SU, GUI_COLOR_INSTR_UNKNOWN, GUI_COLOR_CHANNEL_FM, diff --git a/src/gui/guiConst.cpp b/src/gui/guiConst.cpp index ad49ad5f4..da01cf0ef 100644 --- a/src/gui/guiConst.cpp +++ b/src/gui/guiConst.cpp @@ -106,7 +106,11 @@ const char* insTypes[DIV_INS_MAX]={ "Atari Lynx", "VERA", "X1-010", - "VRC6 (saw)" + "VRC6 (saw)", + "ES5506", + "MultiPCM", + "SNES", + "Sound Unit", }; const char* sampleDepths[17]={ @@ -509,6 +513,10 @@ const FurnaceGUIColorDef guiColors[GUI_COLOR_MAX]={ D(GUI_COLOR_INSTR_VERA,"",ImVec4(0.4f,0.6f,1.0f,1.0f)), D(GUI_COLOR_INSTR_X1_010,"",ImVec4(0.3f,0.5f,1.0f,1.0f)), D(GUI_COLOR_INSTR_VRC6_SAW,"",ImVec4(0.8f,0.3f,0.0f,1.0f)), + D(GUI_COLOR_INSTR_ES5506,"",ImVec4(0.5f,0.5f,1.0f,1.0f)), + D(GUI_COLOR_INSTR_MULTIPCM,"",ImVec4(1.0f,0.8f,0.1f,1.0f)), + D(GUI_COLOR_INSTR_SNES,"",ImVec4(0.8f,0.7f,1.0f,1.0f)), + D(GUI_COLOR_INSTR_SU,"",ImVec4(0.95f,0.98f,1.0f,1.0f)), D(GUI_COLOR_INSTR_UNKNOWN,"",ImVec4(0.3f,0.3f,0.3f,1.0f)), D(GUI_COLOR_CHANNEL_FM,"",ImVec4(0.2f,0.8f,1.0f,1.0f)), @@ -588,6 +596,8 @@ const int availableSystems[]={ DIV_SYSTEM_AMIGA, DIV_SYSTEM_PCSPKR, DIV_SYSTEM_YMU759, + DIV_SYSTEM_DUMMY, + DIV_SYSTEM_SOUND_UNIT, DIV_SYSTEM_OPLL, DIV_SYSTEM_OPLL_DRUMS, DIV_SYSTEM_VRC7, diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index 67b150721..5630f0747 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -1174,6 +1174,10 @@ void FurnaceGUI::drawSettings() { UI_COLOR_CONFIG(GUI_COLOR_INSTR_MIKEY,"Lynx"); UI_COLOR_CONFIG(GUI_COLOR_INSTR_VERA,"VERA"); UI_COLOR_CONFIG(GUI_COLOR_INSTR_X1_010,"X1-010"); + UI_COLOR_CONFIG(GUI_COLOR_INSTR_ES5506,"ES5506"); + UI_COLOR_CONFIG(GUI_COLOR_INSTR_MULTIPCM,"MultiPCM"); + UI_COLOR_CONFIG(GUI_COLOR_INSTR_SNES,"SNES"); + UI_COLOR_CONFIG(GUI_COLOR_INSTR_SU,"Sound Unit"); UI_COLOR_CONFIG(GUI_COLOR_INSTR_UNKNOWN,"Other/Unknown"); ImGui::TreePop(); } From 78bdd98e0b9afe6c5a5133c188bab7f5dfd24585 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 27 Apr 2022 04:48:56 -0500 Subject: [PATCH 212/342] improvements to sysDef and UX changes - "no undo" in clear option - only display instrument types depending on current systems --- CMakeLists.txt | 3 + src/engine/dispatchContainer.cpp | 4 + src/engine/engine.cpp | 18 ++ src/engine/engine.h | 9 +- src/engine/platform/sound/su.cpp | 21 ++ src/engine/platform/sound/su.h | 1 + src/engine/platform/su.cpp | 325 +++++++++++++++++++++++++++++++ src/engine/platform/su.h | 102 ++++++++++ src/engine/sysDef.cpp | 6 +- src/gui/gui.cpp | 2 +- src/gui/insEdit.cpp | 31 ++- 11 files changed, 518 insertions(+), 4 deletions(-) create mode 100644 src/engine/platform/su.cpp create mode 100644 src/engine/platform/su.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 623b6cc35..75a3d3350 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -284,6 +284,8 @@ src/engine/platform/sound/x1_010/x1_010.cpp src/engine/platform/sound/swan.cpp +src/engine/platform/sound/su.cpp + src/engine/platform/sound/k005289/k005289.cpp src/engine/platform/sound/n163/n163.cpp @@ -341,6 +343,7 @@ src/engine/platform/segapcm.cpp src/engine/platform/qsound.cpp src/engine/platform/x1_010.cpp src/engine/platform/lynx.cpp +src/engine/platform/su.cpp src/engine/platform/swan.cpp src/engine/platform/vera.cpp src/engine/platform/bubsyswsg.cpp diff --git a/src/engine/dispatchContainer.cpp b/src/engine/dispatchContainer.cpp index 7c821b4a4..e9f1a03e2 100644 --- a/src/engine/dispatchContainer.cpp +++ b/src/engine/dispatchContainer.cpp @@ -44,6 +44,7 @@ #include "platform/qsound.h" #include "platform/vera.h" #include "platform/x1_010.h" +#include "platform/su.h" #include "platform/swan.h" #include "platform/lynx.h" #include "platform/bubsyswsg.h" @@ -311,6 +312,9 @@ void DivDispatchContainer::init(DivSystem sys, DivEngine* eng, int chanCount, do case DIV_SYSTEM_MMC5: dispatch=new DivPlatformMMC5; break; + case DIV_SYSTEM_SOUND_UNIT: + dispatch=new DivPlatformSoundUnit; + break; case DIV_SYSTEM_DUMMY: dispatch=new DivPlatformDummy; break; diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 85831839e..185f03633 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -1036,8 +1036,10 @@ const char** DivEngine::getRegisterSheet(int sys) { } void DivEngine::recalcChans() { + bool isInsTypePossible[DIV_INS_MAX]; chans=0; int chanIndex=0; + memset(isInsTypePossible,0,DIV_INS_MAX*sizeof(bool)); for (int i=0; ichanInsType[j][0]!=DIV_INS_NULL) { + isInsTypePossible[sysDefs[song.system[i]]->chanInsType[j][0]]=true; + logV("Marking"); + } + + if (sysDefs[song.system[i]]->chanInsType[j][1]!=DIV_INS_NULL) { + isInsTypePossible[sysDefs[song.system[i]]->chanInsType[j][1]]=true; + } + } } } + + possibleInsTypes.clear(); + for (int i=0; i midiIns; std::vector midiOuts; std::vector cmdStream; + std::vector possibleInsTypes; DivSysDef* sysDefs[256]; DivSystem sysFileMapFur[256]; DivSystem sysFileMapDMF[256]; @@ -501,6 +505,9 @@ class DivEngine { // get channel count int getTotalChannelCount(); + // get instrument types available for use + std::vector& getPossibleInsTypes(); + // get effect description const char* getEffectDesc(unsigned char effect, int chan, bool notNull=false); diff --git a/src/engine/platform/sound/su.cpp b/src/engine/platform/sound/su.cpp index daf710fce..08a648f16 100644 --- a/src/engine/platform/sound/su.cpp +++ b/src/engine/platform/sound/su.cpp @@ -211,6 +211,10 @@ void SoundUnit::NextSample(short* l, short* r) { ocycle[i]=0; chan[i].flags.resosc=0; } + if (muted[i]) { + nsL[i]=0; + nsR[i]=0; + } } tnsL=(nsL[0]+nsL[1]+nsL[2]+nsL[3]+nsL[4]+nsL[5]+nsL[6]+nsL[7])>>2; tnsR=(nsR[0]+nsR[1]+nsR[2]+nsR[3]+nsR[4]+nsR[5]+nsR[6]+nsR[7])>>2; @@ -233,22 +237,38 @@ void SoundUnit::Init() { SCpantabR[128+i]=i-1; } SCpantabR[128]=0; + for (int i=0; i<8; i++) { + muted[i]=false; + } } void SoundUnit::Reset() { for (int i=0; i<8; i++) { + ocycle[i]=0; cycle[i]=0; + rcycle[i]=0; resetfreq[i]=0; voldcycles[i]=0; volicycles[i]=0; fscycles[i]=0; sweep[i]=0; ns[i]=0; + fns[i]=0; + nsL[i]=0; + nsR[i]=0; + nslow[i]=0; + nshigh[i]=0; + nsband[i]=0; swvolt[i]=1; swfreqt[i]=1; swcutt[i]=1; lfsr[i]=0xaaaa; + oldfreq[i]=0; + oldflags[i]=0; + pcmdec[i]=0; } + tnsL=0; + tnsR=0; memset(chan,0,sizeof(SUChannel)*8); } @@ -258,4 +278,5 @@ void SoundUnit::Write(unsigned char addr, unsigned char data) { SoundUnit::SoundUnit() { Init(); + memset(pcm,0,SOUNDCHIP_PCM_SIZE); } diff --git a/src/engine/platform/sound/su.h b/src/engine/platform/sound/su.h index a201e955a..972d5343e 100644 --- a/src/engine/platform/sound/su.h +++ b/src/engine/platform/sound/su.h @@ -85,6 +85,7 @@ class SoundUnit { unsigned short restimer; } chan[8]; signed char pcm[SOUNDCHIP_PCM_SIZE]; + bool muted[8]; void Write(unsigned char addr, unsigned char data); void NextSample(short* l, short* r); void Init(); diff --git a/src/engine/platform/su.cpp b/src/engine/platform/su.cpp new file mode 100644 index 000000000..f98b19947 --- /dev/null +++ b/src/engine/platform/su.cpp @@ -0,0 +1,325 @@ +/** + * Furnace Tracker - multi-system chiptune tracker + * Copyright (C) 2021-2022 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 "su.h" +#include "../engine.h" +#include + +//#define rWrite(a,v) pendingWrites[a]=v; +#define rWrite(a,v) if (!skipRegisterWrites) {writes.emplace(a,v); if (dumpWrites) {addWrite(a,v);} } +#define chWrite(c,a,v) rWrite(((c)<<5)|(a),v); + +#define CHIP_FREQBASE 524288 + +const char** DivPlatformSoundUnit::getRegisterSheet() { + return NULL; +} + +const char* DivPlatformSoundUnit::getEffectName(unsigned char effect) { + return NULL; +} + +void DivPlatformSoundUnit::acquire(short* bufL, short* bufR, size_t start, size_t len) { + for (size_t h=start; hWrite(w.addr,w.val); + writes.pop(); + } + su->NextSample(&bufL[h],&bufR[h]); + } +} + +void DivPlatformSoundUnit::writeControl(int ch) { + chWrite(ch,0x04,(chan[ch].wave&7)|(chan[ch].pcm<<3)|(chan[ch].control<<4)); +} + +void DivPlatformSoundUnit::tick(bool sysTick) { + for (int i=0; i<8; i++) { + chan[i].std.next(); + if (chan[i].std.vol.had) { + chan[i].outVol=((chan[i].vol&127)*MIN(127,chan[i].std.vol.val))>>7; + chWrite(i,0x02,chan[i].outVol); + } + if (chan[i].std.arp.had) { + if (!chan[i].inPorta) { + if (chan[i].std.arp.mode) { + chan[i].baseFreq=NOTE_FREQUENCY(chan[i].std.arp.val); + } else { + chan[i].baseFreq=NOTE_FREQUENCY(chan[i].note+chan[i].std.arp.val); + } + } + chan[i].freqChanged=true; + } else { + if (chan[i].std.arp.mode && chan[i].std.arp.finished) { + chan[i].baseFreq=NOTE_FREQUENCY(chan[i].note); + chan[i].freqChanged=true; + } + } + if (chan[i].std.duty.had) { + chan[i].duty=chan[i].std.duty.val; + chWrite(i,0x08,chan[i].duty); + } + if (chan[i].std.wave.had) { + chan[i].wave=chan[i].std.wave.val&7; + writeControl(i); + } + if (chan[i].std.panL.had) { + chan[i].pan=chan[i].std.panL.val; + chWrite(i,0x03,chan[i].pan); + } + if (chan[i].std.pitch.had) { + chan[i].freqChanged=true; + } + if (chan[i].std.ex1.had) { + chan[i].cutoff=chan[i].std.ex1.val; + chWrite(i,0x06,chan[i].cutoff&0xff); + chWrite(i,0x07,chan[i].cutoff>>8); + } + if (chan[i].std.ex2.had) { + chan[i].res=chan[i].std.ex2.val; + chWrite(i,0x09,chan[i].res); + } + if (chan[i].std.ex3.had) { + chan[i].control=chan[i].std.ex3.val&15; + writeControl(i); + } + if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { + //DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_SU); + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false)+chan[i].std.pitch.val; + chWrite(i,0x00,chan[i].freq&0xff); + chWrite(i,0x01,chan[i].freq>>8); + if (chan[i].freq>65535) chan[i].freq=65535; + if (chan[i].keyOn) { + //rWrite(16+i*5,0x80); + //chWrite(i,0x04,0x80|chan[i].vol); + } + if (chan[i].keyOff) { + chWrite(i,0x02,0); + } + if (chan[i].keyOn) chan[i].keyOn=false; + if (chan[i].keyOff) chan[i].keyOff=false; + chan[i].freqChanged=false; + } + } +} + +int DivPlatformSoundUnit::dispatch(DivCommand c) { + switch (c.cmd) { + case DIV_CMD_NOTE_ON: { + DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_SU); + if (c.value!=DIV_NOTE_NULL) { + chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value); + chan[c.chan].freqChanged=true; + chan[c.chan].note=c.value; + } + chan[c.chan].active=true; + chan[c.chan].keyOn=true; + chWrite(c.chan,0x02,chan[c.chan].vol); + chan[c.chan].std.init(ins); + chan[c.chan].insChanged=false; + break; + } + case DIV_CMD_NOTE_OFF: + chan[c.chan].active=false; + chan[c.chan].keyOff=true; + chan[c.chan].std.init(NULL); + break; + case DIV_CMD_NOTE_OFF_ENV: + case DIV_CMD_ENV_RELEASE: + chan[c.chan].std.release(); + break; + case DIV_CMD_INSTRUMENT: + if (chan[c.chan].ins!=c.value || c.value2==1) { + chan[c.chan].ins=c.value; + chan[c.chan].insChanged=true; + } + break; + case DIV_CMD_VOLUME: + if (chan[c.chan].vol!=c.value) { + chan[c.chan].vol=c.value; + if (!chan[c.chan].std.vol.has) { + chan[c.chan].outVol=c.value; + if (chan[c.chan].active) chWrite(c.chan,0x02,chan[c.chan].outVol); + } + } + break; + case DIV_CMD_GET_VOLUME: + if (chan[c.chan].std.vol.has) { + return chan[c.chan].vol; + } + return chan[c.chan].outVol; + break; + case DIV_CMD_PITCH: + chan[c.chan].pitch=c.value; + chan[c.chan].freqChanged=true; + break; + case DIV_CMD_WAVE: + chan[c.chan].wave=c.value; + chan[c.chan].keyOn=true; + break; + case DIV_CMD_NOTE_PORTA: { + int destFreq=NOTE_FREQUENCY(c.value2); + bool return2=false; + if (destFreq>chan[c.chan].baseFreq) { + chan[c.chan].baseFreq+=c.value*(1+(chan[c.chan].baseFreq>>9)); + if (chan[c.chan].baseFreq>=destFreq) { + chan[c.chan].baseFreq=destFreq; + return2=true; + } + } else { + chan[c.chan].baseFreq-=c.value*(1+(chan[c.chan].baseFreq>>9)); + if (chan[c.chan].baseFreq<=destFreq) { + chan[c.chan].baseFreq=destFreq; + return2=true; + } + } + chan[c.chan].freqChanged=true; + if (return2) { + chan[c.chan].inPorta=false; + return 2; + } + break; + } + case DIV_CMD_PANNING: { + chan[c.chan].pan=c.value; + chWrite(c.chan,0x03,chan[c.chan].pan); + break; + } + case DIV_CMD_LEGATO: + chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value+((chan[c.chan].std.arp.will && !chan[c.chan].std.arp.mode)?(chan[c.chan].std.arp.val):(0))); + chan[c.chan].freqChanged=true; + chan[c.chan].note=c.value; + break; + case DIV_CMD_PRE_PORTA: + if (chan[c.chan].active && c.value2) { + if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_SU)); + } + chan[c.chan].inPorta=c.value; + break; + case DIV_CMD_GET_VOLMAX: + return 127; + break; + case DIV_ALWAYS_SET_VOLUME: + return 1; + break; + default: + break; + } + return 1; +} + +void DivPlatformSoundUnit::muteChannel(int ch, bool mute) { + isMuted[ch]=mute; + su->muted[ch]=mute; +} + +void DivPlatformSoundUnit::forceIns() { + for (int i=0; i<8; i++) { + chan[i].insChanged=true; + chan[i].freqChanged=true; + } +} + +void* DivPlatformSoundUnit::getChanState(int ch) { + return &chan[ch]; +} + +unsigned char* DivPlatformSoundUnit::getRegisterPool() { + return (unsigned char*)su->chan; +} + +int DivPlatformSoundUnit::getRegisterPoolSize() { + return 256; +} + +void DivPlatformSoundUnit::reset() { + while (!writes.empty()) writes.pop(); + memset(regPool,0,128); + for (int i=0; i<8; i++) { + chan[i]=DivPlatformSoundUnit::Channel(); + chan[i].std.setEngine(parent); + } + if (dumpWrites) { + addWrite(0xffffffff,0); + } + su->Reset(); + for (int i=0; i<8; i++) { + chWrite(i,0x08,0x3f); + } + lastPan=0xff; + cycles=0; + curChan=-1; + sampleBank=0; + lfoMode=0; + lfoSpeed=255; + delay=500; +} + +bool DivPlatformSoundUnit::isStereo() { + return true; +} + +bool DivPlatformSoundUnit::keyOffAffectsArp(int ch) { + return true; +} + +void DivPlatformSoundUnit::notifyInsDeletion(void* ins) { + for (int i=0; i<8; i++) { + chan[i].std.notifyInsDeletion((DivInstrument*)ins); + } +} + +void DivPlatformSoundUnit::setFlags(unsigned int flags) { + if (flags&1) { + chipClock=1190000; + } else { + chipClock=1236000; + } + rate=chipClock/4; +} + +void DivPlatformSoundUnit::poke(unsigned int addr, unsigned short val) { + rWrite(addr,val); +} + +void DivPlatformSoundUnit::poke(std::vector& wlist) { + for (DivRegWrite& i: wlist) rWrite(i.addr,i.val); +} + +int DivPlatformSoundUnit::init(DivEngine* p, int channels, int sugRate, unsigned int flags) { + parent=p; + dumpWrites=false; + skipRegisterWrites=false; + for (int i=0; i<8; i++) { + isMuted[i]=false; + } + setFlags(flags); + su=new SoundUnit(); + su->Init(); + reset(); + return 6; +} + +void DivPlatformSoundUnit::quit() { + delete su; +} + +DivPlatformSoundUnit::~DivPlatformSoundUnit() { +} diff --git a/src/engine/platform/su.h b/src/engine/platform/su.h new file mode 100644 index 000000000..5baf13f08 --- /dev/null +++ b/src/engine/platform/su.h @@ -0,0 +1,102 @@ +/** + * Furnace Tracker - multi-system chiptune tracker + * Copyright (C) 2021-2022 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. + */ + +#ifndef _SU_H +#define _SU_H + +#include "../dispatch.h" +#include +#include "../macroInt.h" +#include "sound/su.h" + +class DivPlatformSoundUnit: public DivDispatch { + struct Channel { + int freq, baseFreq, pitch, note; + int ins, cutoff, res, control; + signed char pan; + unsigned char duty; + bool active, insChanged, freqChanged, keyOn, keyOff, inPorta, noise, pcm; + signed char vol, outVol, wave; + DivMacroInt std; + Channel(): + freq(0), + baseFreq(0), + pitch(0), + note(0), + ins(-1), + cutoff(65535), + res(0), + control(0), + pan(0), + duty(63), + active(false), + insChanged(true), + freqChanged(false), + keyOn(false), + keyOff(false), + inPorta(false), + noise(false), + pcm(false), + vol(127), + outVol(127), + wave(-1) {} + }; + Channel chan[8]; + bool isMuted[8]; + struct QueuedWrite { + unsigned char addr; + unsigned char val; + QueuedWrite(unsigned char a, unsigned char v): addr(a), val(v) {} + }; + std::queue writes; + unsigned char lastPan; + + int cycles, curChan, delay; + short tempL; + short tempR; + unsigned char sampleBank, lfoMode, lfoSpeed; + SoundUnit* su; + unsigned char regPool[128]; + void writeControl(int ch); + + friend void putDispatchChan(void*,int,int); + public: + void acquire(short* bufL, short* bufR, size_t start, size_t len); + int dispatch(DivCommand c); + void* getChanState(int chan); + unsigned char* getRegisterPool(); + int getRegisterPoolSize(); + void reset(); + void forceIns(); + void tick(bool sysTick=true); + void muteChannel(int ch, bool mute); + bool isStereo(); + bool keyOffAffectsArp(int ch); + void setFlags(unsigned int flags); + void notifyInsDeletion(void* ins); + void poke(unsigned int addr, unsigned short val); + void poke(std::vector& wlist); + const char** getRegisterSheet(); + const char* getEffectName(unsigned char effect); + int init(DivEngine* parent, int channels, int sugRate, unsigned int flags); + void quit(); + ~DivPlatformSoundUnit(); +}; + +#endif diff --git a/src/engine/sysDef.cpp b/src/engine/sysDef.cpp index 2c5e3c73d..bbf5db2d2 100644 --- a/src/engine/sysDef.cpp +++ b/src/engine/sysDef.cpp @@ -48,6 +48,10 @@ int DivEngine::getTotalChannelCount() { return chans; } +std::vector& DivEngine::getPossibleInsTypes() { + return possibleInsTypes; +} + // TODO: rewrite this function (again). it's an unreliable mess. const char* DivEngine::getSongSystemName() { switch (song.systemLen) { @@ -358,7 +362,7 @@ int DivEngine::minVGMVersion(DivSystem which) { void DivEngine::registerSystems() { logD("registering systems..."); - + sysDefs[DIV_SYSTEM_YMU759]=new DivSysDef( "Yamaha YMU759", NULL, 0x01, 0x01, 17, true, false, 0, false, {"Channel 1", "Channel 2", "Channel 3", "Channel 4", "Channel 5", "Channel 6", "Channel 7", "Channel 8", "Channel 9", "Channel 10", "Channel 11", "Channel 12", "Channel 13", "Channel 14", "Channel 15", "Channel 16", "PCM" }, // name diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 372cf998d..2b0b3c751 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -2729,7 +2729,7 @@ bool FurnaceGUI::loop() { editOptions(true); ImGui::Separator(); if (ImGui::MenuItem("clear...")) { - showWarning("Are you sure you want to clear...",GUI_WARN_CLEAR); + showWarning("Are you sure you want to clear... (cannot be undone!)",GUI_WARN_CLEAR); } ImGui::EndMenu(); } diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index 77781f9dd..c0be1f45c 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -175,6 +175,10 @@ const char* n163UpdateBits[8]={ "now", "every waveform changed", NULL }; +const char* suControlBits[5]={ + "ring mod", "low pass", "band pass", "high pass", NULL +}; + const char* panBits[3]={ "right", "left", NULL }; @@ -1386,9 +1390,19 @@ void FurnaceGUI::drawInsEdit() { if (ins->type>=DIV_INS_MAX) ins->type=DIV_INS_FM; int insType=ins->type; ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + /* if (ImGui::Combo("##Type",&insType,insTypes,DIV_INS_MAX,DIV_INS_MAX)) { ins->type=(DivInstrumentType)insType; } + */ + if (ImGui::BeginCombo("##Type",insTypes[insType])) { + for (DivInstrumentType i: e->getPossibleInsTypes()) { + if (ImGui::Selectable(insTypes[i],insType==i)) { + ins->type=i; + } + } + ImGui::EndCombo(); + } ImGui::EndTable(); } @@ -2701,7 +2715,7 @@ void FurnaceGUI::drawInsEdit() { if (ins->type==DIV_INS_AMIGA) { volMax=64; } - if (ins->type==DIV_INS_FM || ins->type == DIV_INS_MIKEY) { + if (ins->type==DIV_INS_FM || ins->type==DIV_INS_MIKEY || ins->type==DIV_INS_SU) { volMax=127; } if (ins->type==DIV_INS_GB) { @@ -2769,6 +2783,9 @@ void FurnaceGUI::drawInsEdit() { dutyLabel="Duty"; dutyMax=7; } + if (ins->type==DIV_INS_SU) { + dutyMax=127; + } bool dutyIsRel=(ins->type==DIV_INS_C64 && !ins->c64.dutyIsAbs); const char* waveLabel="Waveform"; @@ -2783,6 +2800,7 @@ void FurnaceGUI::drawInsEdit() { if (ins->type==DIV_INS_SAA1099) waveMax=2; if (ins->type==DIV_INS_FM || ins->type==DIV_INS_OPL || ins->type==DIV_INS_OPZ) waveMax=0; if (ins->type==DIV_INS_MIKEY) waveMax=0; + if (ins->type==DIV_INS_SU) waveMax=7; if (ins->type==DIV_INS_PET) { waveMax=8; bitMode=true; @@ -2818,6 +2836,10 @@ void FurnaceGUI::drawInsEdit() { ex1Max=63; ex2Max=4095; } + if (ins->type==DIV_INS_SU) { + ex1Max=65535; + ex2Max=255; + } if (ins->type==DIV_INS_SAA1099) ex1Max=8; int panMax=0; @@ -2882,6 +2904,8 @@ void FurnaceGUI::drawInsEdit() { NORMAL_MACRO(ins->std.ex1Macro,0,ex1Max,"ex1","Waveform len.",160,ins->std.ex1Macro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[4],0,ex1Max,NULL,false); } else if (ins->type==DIV_INS_FDS) { NORMAL_MACRO(ins->std.ex1Macro,0,ex1Max,"ex1","Mod Depth",160,ins->std.ex1Macro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[4],0,ex1Max,NULL,false); + } else if (ins->type==DIV_INS_SU) { + NORMAL_MACRO(ins->std.ex1Macro,0,ex1Max,"ex1","Cutoff",160,ins->std.ex1Macro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[4],0,ex1Max,NULL,false); } else { NORMAL_MACRO(ins->std.ex1Macro,0,ex1Max,"ex1","Duty",160,ins->std.ex1Macro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[4],0,ex1Max,NULL,false); } @@ -2893,6 +2917,8 @@ void FurnaceGUI::drawInsEdit() { NORMAL_MACRO(ins->std.ex2Macro,0,ex2Max,"ex2","Waveform update",64,ins->std.ex2Macro.open,true,n163UpdateBits,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[5],0,ex2Max,NULL,false); } else if (ins->type==DIV_INS_FDS) { NORMAL_MACRO(ins->std.ex2Macro,0,ex2Max,"ex2","Mod Speed",160,ins->std.ex2Macro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[5],0,ex2Max,NULL,false); + } else if (ins->type==DIV_INS_SU) { + NORMAL_MACRO(ins->std.ex2Macro,0,ex2Max,"ex2","Resonance",160,ins->std.ex2Macro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[5],0,ex2Max,NULL,false); } else { NORMAL_MACRO(ins->std.ex2Macro,0,ex2Max,"ex2","Envelope",ex2Bit?64:160,ins->std.ex2Macro.open,ex2Bit,ayEnvBits,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[5],0,ex2Max,NULL,false); } @@ -2918,6 +2944,9 @@ void FurnaceGUI::drawInsEdit() { if (ins->type==DIV_INS_FDS) { NORMAL_MACRO(ins->std.ex3Macro,0,127,"ex3","Mod Position",160,ins->std.ex3Macro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[6],0,2,NULL,false); } + if (ins->type==DIV_INS_SU) { + NORMAL_MACRO(ins->std.ex3Macro,0,4,"ex3","Control",64,ins->std.ex3Macro.open,true,suControlBits,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[6],0,4,NULL,false); + } MACRO_END; } else { // classic view (TODO: possibly remove) From 8db3831ab6b13d0f8657b0f2b926a12df897e555 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 27 Apr 2022 05:02:18 -0500 Subject: [PATCH 213/342] fix MSVC oh my --- src/engine/platform/sound/su.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/engine/platform/sound/su.cpp b/src/engine/platform/sound/su.cpp index 08a648f16..69b02a781 100644 --- a/src/engine/platform/sound/su.cpp +++ b/src/engine/platform/sound/su.cpp @@ -1,3 +1,4 @@ +#define _USE_MATH_DEFINES #include "su.h" #include From 9eaf600b4b83467ad2f08c77c54044d2d53bbe24 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 27 Apr 2022 17:44:00 -0500 Subject: [PATCH 214/342] GUI: rename Amiga/Sample to Sample --- src/gui/guiConst.cpp | 4 ++-- src/gui/insEdit.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/gui/guiConst.cpp b/src/gui/guiConst.cpp index da01cf0ef..357868381 100644 --- a/src/gui/guiConst.cpp +++ b/src/gui/guiConst.cpp @@ -80,11 +80,11 @@ const int vgmVersions[6]={ }; const char* insTypes[DIV_INS_MAX]={ - "Standard", + "Standard (SMS/NES)", "FM (4-operator)", "Game Boy", "C64", - "Amiga/Sample", + "Sample", "PC Engine", "AY-3-8910/SSG", "AY8930", diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index c0be1f45c..13629816e 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -2435,7 +2435,7 @@ void FurnaceGUI::drawInsEdit() { P(ImGui::Checkbox("Absolute Duty Macro",&ins->c64.dutyIsAbs)); ImGui::EndTabItem(); } - if (ins->type==DIV_INS_AMIGA) if (ImGui::BeginTabItem("Amiga/Sample")) { + if (ins->type==DIV_INS_AMIGA) if (ImGui::BeginTabItem("Sample")) { String sName; if (ins->amiga.initSample<0 || ins->amiga.initSample>=e->song.sampleLen) { sName="none selected"; From 3306e853d1900065243d76948acb2b7f10a42b24 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 27 Apr 2022 17:53:55 -0500 Subject: [PATCH 215/342] effectRows -> effectCols i don't know why did I call it "rows" --- src/engine/engine.cpp | 2 +- src/engine/fileOps.cpp | 36 ++++++++++++++++++------------------ src/engine/pattern.cpp | 2 +- src/engine/pattern.h | 2 +- src/engine/playback.cpp | 10 +++++----- src/gui/cursor.cpp | 12 ++++++------ src/gui/doAction.cpp | 8 ++++---- src/gui/editing.cpp | 38 +++++++++++++++++++------------------- src/gui/gui.cpp | 2 +- src/gui/pattern.cpp | 16 ++++++++-------- 10 files changed, 64 insertions(+), 64 deletions(-) diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 185f03633..a62ae36e2 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -140,7 +140,7 @@ void DivEngine::walkSong(int& loopOrder, int& loopRow, int& loopEnd) { for (int j=nextRow; jdata[j][5+(l<<1)]; if (effectVal<0) effectVal=0; if (pat[k]->data[j][4+(l<<1)]==0x0d) { diff --git a/src/engine/fileOps.cpp b/src/engine/fileOps.cpp index 3996f2d0b..16678c49b 100644 --- a/src/engine/fileOps.cpp +++ b/src/engine/fileOps.cpp @@ -637,14 +637,14 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) { for (int i=0; i4 || chan.effectRows<1) { - logE("invalid effect row count %d. are you sure everything is ok?",chan.effectRows); - lastError="file is corrupt or unreadable at effect rows"; + logD("%d fx rows: %d",i,chan.effectCols); + if (chan.effectCols>4 || chan.effectCols<1) { + logE("invalid effect column count %d. are you sure everything is ok?",chan.effectCols); + lastError="file is corrupt or unreadable at effect columns"; delete[] file; return false; } @@ -694,7 +694,7 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) { pat->data[k][3]=(pat->data[k][3]&3)*5; } } - for (int l=0; ldata[k][4+(l<<1)]=reader.readS(); pat->data[k][5+(l<<1)]=reader.readS(); @@ -1291,10 +1291,10 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) { } for (int i=0; i8) { - logE("channel %d has zero or too many effect columns! (%d)",i,ds.pat[i].effectRows); - lastError=fmt::sprintf("channel %d has too many effect columns! (%d)",i,ds.pat[i].effectRows); + ds.pat[i].effectCols=reader.readC(); + if (ds.pat[i].effectCols<1 || ds.pat[i].effectCols>8) { + logE("channel %d has zero or too many effect columns! (%d)",i,ds.pat[i].effectCols); + lastError=fmt::sprintf("channel %d has too many effect columns! (%d)",i,ds.pat[i].effectCols); delete[] file; return false; } @@ -1565,7 +1565,7 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) { pat->data[j][1]=reader.readS(); pat->data[j][2]=reader.readS(); pat->data[j][3]=reader.readS(); - for (int k=0; kdata[j][4+(k<<1)]=reader.readS(); pat->data[j][5+(k<<1)]=reader.readS(); } @@ -1960,7 +1960,7 @@ bool DivEngine::loadMod(unsigned char* file, size_t len) { } } } - ds.pat[ch].effectRows=fxCols; + ds.pat[ch].effectCols=fxCols; } ds.pal=false; @@ -1977,7 +1977,7 @@ bool DivEngine::loadMod(unsigned char* file, size_t len) { ds.chanShortName[i]=fmt::sprintf("C%d",i+1); } for(int i=chCount; iwriteC(song.pat[i].effectRows); + w->writeC(song.pat[i].effectCols); } for (int i=0; iwriteS(pat->data[j][1]); // octave w->writeS(pat->data[j][2]); // instrument w->writeS(pat->data[j][3]); // volume - w->write(&pat->data[j][4],2*song.pat[i>>16].effectRows*2); // effects + w->write(&pat->data[j][4],2*song.pat[i>>16].effectCols*2); // effects } w->writeString(pat->name,false); @@ -2717,7 +2717,7 @@ SafeWriter* DivEngine::saveDMF(unsigned char version) { } for (int i=0; iwriteC(song.pat[i].effectRows); + w->writeC(song.pat[i].effectCols); for (int j=0; jwriteS(pat->data[k][0]); // note w->writeS(pat->data[k][1]); // octave w->writeS(pat->data[k][3]); // volume - w->write(&pat->data[k][4],2*song.pat[i].effectRows*2); // effects + w->write(&pat->data[k][4],2*song.pat[i].effectCols*2); // effects w->writeS(pat->data[k][2]); // instrument } } diff --git a/src/engine/pattern.cpp b/src/engine/pattern.cpp index 8241255b3..4f4663cae 100644 --- a/src/engine/pattern.cpp +++ b/src/engine/pattern.cpp @@ -130,6 +130,6 @@ SafeReader* DivPattern::compile(int len, int fxRows) { } DivChannelData::DivChannelData(): - effectRows(1) { + effectCols(1) { memset(data,0,256*sizeof(void*)); } diff --git a/src/engine/pattern.h b/src/engine/pattern.h index fd0b0d076..4d1070f3b 100644 --- a/src/engine/pattern.h +++ b/src/engine/pattern.h @@ -40,7 +40,7 @@ struct DivPattern { }; struct DivChannelData { - unsigned char effectRows; + unsigned char effectCols; // data goes as follows: data[ROW][TYPE] // TYPE is: // 0: note diff --git a/src/engine/playback.cpp b/src/engine/playback.cpp index c2c20d3d0..13cc495b9 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -910,7 +910,7 @@ void DivEngine::processRow(int i, bool afterDelay) { int whatRow=afterDelay?chan[i].delayRow:curRow; DivPattern* pat=song.pat[i].getPattern(song.orders.ord[i][whatOrder],false); // pre effects - if (!afterDelay) for (int j=0; jdata[whatRow][4+(j<<1)]; short effectVal=pat->data[whatRow][5+(j<<1)]; @@ -1019,7 +1019,7 @@ void DivEngine::processRow(int i, bool afterDelay) { bool calledPorta=false; // effects - for (int j=0; jdata[whatRow][4+(j<<1)]; short effectVal=pat->data[whatRow][5+(j<<1)]; @@ -1338,7 +1338,7 @@ void DivEngine::processRow(int i, bool afterDelay) { chan[i].noteOnInhibit=false; // post effects - for (int j=0; jdata[whatRow][4+(j<<1)]; short effectVal=pat->data[whatRow][5+(j<<1)]; @@ -1375,7 +1375,7 @@ void DivEngine::nextRow() { snprintf(pb2,4095,"\x1b[0;36m%.2x",pat->data[curRow][2]); strcat(pb3,pb2); } - for (int j=0; jdata[curRow][4+(j<<1)]==-1) { strcat(pb3,"\x1b[m--"); } else { @@ -1449,7 +1449,7 @@ void DivEngine::nextRow() { if (song.oneTickCut) { bool doPrepareCut=true; - for (int j=0; jdata[curRow][4+(j<<1)]==0x03) { doPrepareCut=false; break; diff --git a/src/gui/cursor.cpp b/src/gui/cursor.cpp index 53652ec70..1fdf64d77 100644 --- a/src/gui/cursor.cpp +++ b/src/gui/cursor.cpp @@ -87,7 +87,7 @@ void FurnaceGUI::finishSelection() { selStart.xFine=0; } if (e->song.chanCollapse[selEnd.xCoarse]) { - selEnd.xFine=2+e->song.pat[cursor.xCoarse].effectRows*2; + selEnd.xFine=2+e->song.pat[cursor.xCoarse].effectCols*2; } e->setMidiBaseChan(cursor.xCoarse); @@ -105,7 +105,7 @@ void FurnaceGUI::moveCursor(int x, int y, bool select) { demandScrollX=true; if (x>0) { for (int i=0; i=(e->song.chanCollapse[cursor.xCoarse]?1:(3+e->song.pat[cursor.xCoarse].effectRows*2))) { + if (++cursor.xFine>=(e->song.chanCollapse[cursor.xCoarse]?1:(3+e->song.pat[cursor.xCoarse].effectCols*2))) { cursor.xFine=0; if (++cursor.xCoarse>=lastChannel) { if (settings.wrapHorizontal!=0 && !select) { @@ -113,7 +113,7 @@ void FurnaceGUI::moveCursor(int x, int y, bool select) { if (settings.wrapHorizontal==2) y++; } else { cursor.xCoarse=lastChannel-1; - cursor.xFine=e->song.chanCollapse[cursor.xCoarse]?0:(2+e->song.pat[cursor.xCoarse].effectRows*2); + cursor.xFine=e->song.chanCollapse[cursor.xCoarse]?0:(2+e->song.pat[cursor.xCoarse].effectCols*2); } } else { while (!e->song.chanShow[cursor.xCoarse]) { @@ -129,7 +129,7 @@ void FurnaceGUI::moveCursor(int x, int y, bool select) { if (--cursor.xCoarsesong.pat[cursor.xCoarse].effectRows*2; + cursor.xFine=2+e->song.pat[cursor.xCoarse].effectCols*2; if (settings.wrapHorizontal==2) y--; } else { cursor.xCoarse=firstChannel; @@ -143,7 +143,7 @@ void FurnaceGUI::moveCursor(int x, int y, bool select) { if (e->song.chanCollapse[cursor.xCoarse]) { cursor.xFine=0; } else { - cursor.xFine=2+e->song.pat[cursor.xCoarse].effectRows*2; + cursor.xFine=2+e->song.pat[cursor.xCoarse].effectCols*2; } } } @@ -271,7 +271,7 @@ void FurnaceGUI::moveCursorBottom(bool select) { DETERMINE_LAST; cursor.xCoarse=lastChannel-1; if (cursor.xCoarse<0) cursor.xCoarse=0; - cursor.xFine=2+e->song.pat[cursor.xCoarse].effectRows*2; + cursor.xFine=2+e->song.pat[cursor.xCoarse].effectCols*2; demandScrollX=true; } else { cursor.y=e->song.patLen-1; diff --git a/src/gui/doAction.cpp b/src/gui/doAction.cpp index c12df2fe4..28f6cf882 100644 --- a/src/gui/doAction.cpp +++ b/src/gui/doAction.cpp @@ -462,13 +462,13 @@ void FurnaceGUI::doAction(int what) { break; case GUI_ACTION_PAT_INCREASE_COLUMNS: if (cursor.xCoarse<0 || cursor.xCoarse>=e->getTotalChannelCount()) break; - e->song.pat[cursor.xCoarse].effectRows++; - if (e->song.pat[cursor.xCoarse].effectRows>8) e->song.pat[cursor.xCoarse].effectRows=8; + e->song.pat[cursor.xCoarse].effectCols++; + if (e->song.pat[cursor.xCoarse].effectCols>8) e->song.pat[cursor.xCoarse].effectCols=8; break; case GUI_ACTION_PAT_DECREASE_COLUMNS: if (cursor.xCoarse<0 || cursor.xCoarse>=e->getTotalChannelCount()) break; - e->song.pat[cursor.xCoarse].effectRows--; - if (e->song.pat[cursor.xCoarse].effectRows<1) e->song.pat[cursor.xCoarse].effectRows=1; + e->song.pat[cursor.xCoarse].effectCols--; + if (e->song.pat[cursor.xCoarse].effectCols<1) e->song.pat[cursor.xCoarse].effectCols=1; break; case GUI_ACTION_PAT_INTERPOLATE: doInterpolate(); diff --git a/src/gui/editing.cpp b/src/gui/editing.cpp index e5874f91a..8832b7fbb 100644 --- a/src/gui/editing.cpp +++ b/src/gui/editing.cpp @@ -137,12 +137,12 @@ void FurnaceGUI::makeUndo(ActionType action) { void FurnaceGUI::doSelectAll() { finishSelection(); curNibble=false; - if (selStart.xFine==0 && selEnd.xFine==2+e->song.pat[selEnd.xCoarse].effectRows*2) { + if (selStart.xFine==0 && selEnd.xFine==2+e->song.pat[selEnd.xCoarse].effectCols*2) { if (selStart.y==0 && selEnd.y==e->song.patLen-1) { // select entire pattern selStart.xCoarse=0; selStart.xFine=0; selEnd.xCoarse=e->getTotalChannelCount()-1; - selEnd.xFine=2+e->song.pat[selEnd.xCoarse].effectRows*2; + selEnd.xFine=2+e->song.pat[selEnd.xCoarse].effectCols*2; } else { // select entire column selStart.y=0; selEnd.y=e->song.patLen-1; @@ -153,14 +153,14 @@ void FurnaceGUI::doSelectAll() { // find row position for (SelectionPoint i; i.xCoarse!=selStart.xCoarse || i.xFine!=selStart.xFine; selStartX++) { i.xFine++; - if (i.xFine>=3+e->song.pat[i.xCoarse].effectRows*2) { + if (i.xFine>=3+e->song.pat[i.xCoarse].effectCols*2) { i.xFine=0; i.xCoarse++; } } for (SelectionPoint i; i.xCoarse!=selEnd.xCoarse || i.xFine!=selEnd.xFine; selEndX++) { i.xFine++; - if (i.xFine>=3+e->song.pat[i.xCoarse].effectRows*2) { + if (i.xFine>=3+e->song.pat[i.xCoarse].effectCols*2) { i.xFine=0; i.xCoarse++; } @@ -172,7 +172,7 @@ void FurnaceGUI::doSelectAll() { selEnd.y=e->song.patLen-1; } else { // left-right selStart.xFine=0; - selEnd.xFine=2+e->song.pat[selEnd.xCoarse].effectRows*2; + selEnd.xFine=2+e->song.pat[selEnd.xCoarse].effectCols*2; } } } @@ -200,7 +200,7 @@ void FurnaceGUI::doDelete() { for (; iCoarse<=selEnd.xCoarse; iCoarse++) { if (!e->song.chanShow[iCoarse]) continue; DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true); - for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsesong.pat[iCoarse].effectCols*2 && (iCoarsesong.chanShow[iCoarse]) continue; DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true); - for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsesong.pat[iCoarse].effectCols*2 && (iCoarsesong.patLen; j++) { if (jsong.patLen-1) { @@ -269,7 +269,7 @@ void FurnaceGUI::doInsert() { for (; iCoarse<=selEnd.xCoarse; iCoarse++) { if (!e->song.chanShow[iCoarse]) continue; DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true); - for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsesong.pat[iCoarse].effectCols*2 && (iCoarsesong.patLen-1; j>=selStart.y; j--) { if (j==selStart.y) { @@ -301,7 +301,7 @@ void FurnaceGUI::doTranspose(int amount, OperationMask& mask) { for (; iCoarse<=selEnd.xCoarse; iCoarse++) { if (!e->song.chanShow[iCoarse]) continue; DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true); - for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsesong.pat[iCoarse].effectCols*2 && (iCoarsesong.chanShow[iCoarse]) continue; DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true); - for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsesong.pat[iCoarse].effectCols*2 && (iCoarsedata[j][0],pat->data[j][1]); if (cut) { @@ -530,7 +530,7 @@ void FurnaceGUI::doPaste(PasteMode mode) { break; } if (mode!=GUI_PASTE_MODE_MIX_BG || pat->data[j][iFine+1]==-1) { - if (iFine<(3+e->song.pat[iCoarse].effectRows*2)) pat->data[j][iFine+1]=val; + if (iFine<(3+e->song.pat[iCoarse].effectCols*2)) pat->data[j][iFine+1]=val; } } } @@ -589,7 +589,7 @@ void FurnaceGUI::doInterpolate() { for (; iCoarse<=selEnd.xCoarse; iCoarse++) { if (!e->song.chanShow[iCoarse]) continue; DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true); - for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsesong.pat[iCoarse].effectCols*2 && (iCoarsesong.chanShow[iCoarse]) continue; DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true); - for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsesong.pat[iCoarse].effectCols*2 && (iCoarsesong.chanShow[iCoarse]) continue; DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true); - for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsesong.pat[iCoarse].effectCols*2 && (iCoarsesong.chanShow[iCoarse]) continue; DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true); - for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsesong.pat[iCoarse].effectCols*2 && (iCoarsesong.chanShow[iCoarse]) continue; DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true); - for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsesong.pat[iCoarse].effectCols*2 && (iCoarsesong.chanShow[iCoarse]) continue; DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true); - for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsesong.pat[iCoarse].effectCols*2 && (iCoarsesong.chanShow[iCoarse]) continue; DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true); - for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsesong.pat[iCoarse].effectCols*2 && (iCoarsesong.chanShow[iCoarse]) continue; DivPattern* pat=e->song.pat[iCoarse].getPattern(e->song.orders.ord[iCoarse][curOrder],true); - for (; iFine<3+e->song.pat[iCoarse].effectRows*2 && (iCoarsesong.pat[iCoarse].effectCols*2 && (iCoarse=(3+(e->song.pat[cursor.xCoarse].effectRows*2))) { + if (++cursor.xFine>=(3+(e->song.pat[cursor.xCoarse].effectCols*2))) { cursor.xFine=3; } } else { diff --git a/src/gui/pattern.cpp b/src/gui/pattern.cpp index 3b45cd27b..9ba22ef51 100644 --- a/src/gui/pattern.cpp +++ b/src/gui/pattern.cpp @@ -217,7 +217,7 @@ inline void FurnaceGUI::patternRow(int i, bool isPlaying, float lineHeight, int ImGui::PopStyleColor(); // effects - for (int k=0; ksong.pat[j].effectRows; k++) { + for (int k=0; ksong.pat[j].effectCols; k++) { int index=4+(k<<1); bool selectedEffect=selectedRow && (j32+index-1>=sel1XSum && j32+index-1<=sel2XSum); bool selectedEffectVal=selectedRow && (j32+index>=sel1XSum && j32+index<=sel2XSum); @@ -427,7 +427,7 @@ void FurnaceGUI::drawPattern() { displayTooltip=true; } else { const char* chName=e->getChannelName(i); - size_t chNameLimit=6+4*e->song.pat[i].effectRows; + size_t chNameLimit=6+4*e->song.pat[i].effectCols; if (strlen(chName)>chNameLimit) { String shortChName=chName; shortChName.resize(chNameLimit-3); @@ -524,18 +524,18 @@ void FurnaceGUI::drawPattern() { if (!e->song.chanCollapse[i]) { ImGui::SameLine(); snprintf(chanID,2048,"<##_LCH%d",i); - ImGui::BeginDisabled(e->song.pat[i].effectRows<=1); + ImGui::BeginDisabled(e->song.pat[i].effectCols<=1); if (ImGui::SmallButton(chanID)) { - e->song.pat[i].effectRows--; - if (e->song.pat[i].effectRows<1) e->song.pat[i].effectRows=1; + e->song.pat[i].effectCols--; + if (e->song.pat[i].effectCols<1) e->song.pat[i].effectCols=1; } ImGui::EndDisabled(); ImGui::SameLine(); - ImGui::BeginDisabled(e->song.pat[i].effectRows>=8); + ImGui::BeginDisabled(e->song.pat[i].effectCols>=8); snprintf(chanID,2048,">##_RCH%d",i); if (ImGui::SmallButton(chanID)) { - e->song.pat[i].effectRows++; - if (e->song.pat[i].effectRows>8) e->song.pat[i].effectRows=8; + e->song.pat[i].effectCols++; + if (e->song.pat[i].effectCols>8) e->song.pat[i].effectCols=8; } ImGui::EndDisabled(); } From 410a509d7345e1cb20de188a2853d6aa23340f9a Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 27 Apr 2022 18:03:24 -0500 Subject: [PATCH 216/342] SoundUnit: max cutoff 16383 --- src/engine/platform/su.cpp | 2 +- src/gui/insEdit.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/engine/platform/su.cpp b/src/engine/platform/su.cpp index f98b19947..7acab3570 100644 --- a/src/engine/platform/su.cpp +++ b/src/engine/platform/su.cpp @@ -88,7 +88,7 @@ void DivPlatformSoundUnit::tick(bool sysTick) { chan[i].freqChanged=true; } if (chan[i].std.ex1.had) { - chan[i].cutoff=chan[i].std.ex1.val; + chan[i].cutoff=chan[i].std.ex1.val&16383; chWrite(i,0x06,chan[i].cutoff&0xff); chWrite(i,0x07,chan[i].cutoff>>8); } diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index 13629816e..7039f164c 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -2837,7 +2837,7 @@ void FurnaceGUI::drawInsEdit() { ex2Max=4095; } if (ins->type==DIV_INS_SU) { - ex1Max=65535; + ex1Max=16383; ex2Max=255; } if (ins->type==DIV_INS_SAA1099) ex1Max=8; From dce862fdb9ee9421939f742f003fe3d69226b97b Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 27 Apr 2022 18:28:46 -0500 Subject: [PATCH 217/342] SoundUnit: more stuff --- src/engine/platform/su.cpp | 66 ++++++++++++++++++++++++++++++++++++++ src/engine/platform/su.h | 13 ++++++-- src/gui/insEdit.cpp | 3 +- 3 files changed, 79 insertions(+), 3 deletions(-) diff --git a/src/engine/platform/su.cpp b/src/engine/platform/su.cpp index 7acab3570..2dca4b3ca 100644 --- a/src/engine/platform/su.cpp +++ b/src/engine/platform/su.cpp @@ -32,6 +32,62 @@ const char** DivPlatformSoundUnit::getRegisterSheet() { } const char* DivPlatformSoundUnit::getEffectName(unsigned char effect) { + switch (effect) { + case 0x10: + return "10xx: Set waveform (0 to 7)"; + break; + case 0x12: + return "12xx: Set pulse width (0 to 7F)"; + break; + case 0x13: + return "13xx: Set resonance (0 to F)"; + break; + case 0x14: + return "14xx: Set filter mode (bit 0: ring mod; bit 1: low pass; bit 2: band pass; bit 3: high pass)"; + break; + case 0x15: + return "15xx: Set frequency sweep period low byte"; + break; + case 0x16: + return "16xx: Set frequency sweep period high byte"; + break; + case 0x17: + return "17xx: Set volume sweep period low byte"; + break; + case 0x18: + return "18xx: Set volume sweep period high byte"; + break; + case 0x19: + return "19xx: Set cutoff sweep period low byte"; + break; + case 0x1a: + return "1Axx: Set cutoff sweep period high byte"; + break; + case 0x1b: + return "1Bxx: Set frequency sweep boundary"; + break; + case 0x1c: + return "1Cxx: Set volume sweep boundary"; + break; + case 0x1d: + return "1Dxx: Set cutoff sweep boundary"; + break; + case 0x20: + return "20xx: Toggle frequency sweep (bit 0-6: speed; bit 7: direction is up)"; + break; + case 0x21: + return "21xx: Toggle volume sweep (bit 0-4: speed; bit 5: direciton is up; bit 6: loop; bit 7: alternate)"; + break; + case 0x22: + return "22xx: Toggle cutoff sweep (bit 0-6: speed; bit 7: direction is up)"; + break; + case 0x40: case 0x41: case 0x42: case 0x43: + case 0x44: case 0x45: case 0x46: case 0x47: + case 0x48: case 0x49: case 0x4a: case 0x4b: + case 0x4c: case 0x4d: case 0x4e: case 0x4f: + return "4xxx: Set cutoff (0 to FFF)"; + break; + } return NULL; } @@ -50,6 +106,12 @@ void DivPlatformSoundUnit::writeControl(int ch) { chWrite(ch,0x04,(chan[ch].wave&7)|(chan[ch].pcm<<3)|(chan[ch].control<<4)); } +void DivPlatformSoundUnit::writeControlUpper(int ch) { + chWrite(ch,0x05,((int)chan[ch].phaseReset)|(chan[ch].filterPhaseReset<<1)|(chan[ch].pcmLoop<<2)|(chan[ch].timerSync<<3)|(chan[ch].freqSweep<<4)|(chan[ch].volSweep<<5)|(chan[ch].cutSweep<<6)); + chan[ch].phaseReset=false; + chan[ch].filterPhaseReset=false; +} + void DivPlatformSoundUnit::tick(bool sysTick) { for (int i=0; i<8; i++) { chan[i].std.next(); @@ -80,6 +142,10 @@ void DivPlatformSoundUnit::tick(bool sysTick) { chan[i].wave=chan[i].std.wave.val&7; writeControl(i); } + if (chan[i].std.phaseReset.had) { + chan[i].phaseReset=chan[i].std.phaseReset.val; + writeControlUpper(i); + } if (chan[i].std.panL.had) { chan[i].pan=chan[i].std.panL.val; chWrite(i,0x03,chan[i].pan); diff --git a/src/engine/platform/su.h b/src/engine/platform/su.h index 5baf13f08..6291d4531 100644 --- a/src/engine/platform/su.h +++ b/src/engine/platform/su.h @@ -31,7 +31,8 @@ class DivPlatformSoundUnit: public DivDispatch { int ins, cutoff, res, control; signed char pan; unsigned char duty; - bool active, insChanged, freqChanged, keyOn, keyOff, inPorta, noise, pcm; + bool active, insChanged, freqChanged, keyOn, keyOff, inPorta, noise, pcm, phaseReset, filterPhaseReset; + bool pcmLoop, timerSync, freqSweep, volSweep, cutSweep; signed char vol, outVol, wave; DivMacroInt std; Channel(): @@ -53,9 +54,16 @@ class DivPlatformSoundUnit: public DivDispatch { inPorta(false), noise(false), pcm(false), + phaseReset(false), + filterPhaseReset(false), + pcmLoop(false), + timerSync(false), + freqSweep(false), + volSweep(false), + cutSweep(false), vol(127), outVol(127), - wave(-1) {} + wave(0) {} }; Channel chan[8]; bool isMuted[8]; @@ -74,6 +82,7 @@ class DivPlatformSoundUnit: public DivDispatch { SoundUnit* su; unsigned char regPool[128]; void writeControl(int ch); + void writeControlUpper(int ch); friend void putDispatchChan(void*,int,int); public: diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index 7039f164c..8631e74f8 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -2890,7 +2890,8 @@ void FurnaceGUI::drawInsEdit() { ins->type==DIV_INS_OPLL || ins->type==DIV_INS_AY || ins->type==DIV_INS_AY8930 || - ins->type==DIV_INS_SWAN) { + ins->type==DIV_INS_SWAN || + ins->type==DIV_INS_SU) { NORMAL_MACRO(ins->std.phaseResetMacro,0,1,"phaseReset","Phase Reset",32,ins->std.phaseResetMacro.open,true,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[16],0,1,NULL,false); } if (ex1Max>0) { From de908d7b95e8f4de4724978ead79b3dc62610256 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 27 Apr 2022 18:28:55 -0500 Subject: [PATCH 218/342] update to-do list the refactor is missing ONE thing --- TODO.md | 1 + 1 file changed, 1 insertion(+) diff --git a/TODO.md b/TODO.md index 4c200e02a..88e3833c4 100644 --- a/TODO.md +++ b/TODO.md @@ -29,6 +29,7 @@ - volume commands should work on Game Boy - macro editor menu - refactor sysDef.cpp + - effect/postEffect refactor - add another FM editor layout - try to find out why does VSlider not accept keyboard input - finish lock layout From 180ff2457d4d4d87644edcb18349eb76cbb58560 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 27 Apr 2022 20:36:32 -0500 Subject: [PATCH 219/342] NES: fix audio resolution being too low --- src/engine/platform/nes.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/engine/platform/nes.cpp b/src/engine/platform/nes.cpp index f855648b9..cebd39e2d 100644 --- a/src/engine/platform/nes.cpp +++ b/src/engine/platform/nes.cpp @@ -108,7 +108,7 @@ void DivPlatformNES::acquire(short* bufL, short* bufR, size_t start, size_t len) if (nes->apu.clocked) { nes->apu.clocked=false; } - int sample=(pulse_output(nes)+tnd_output(nes)); + int sample=(pulse_output(nes)+tnd_output(nes))<<6; if (sample>32767) sample=32767; if (sample<-32768) sample=-32768; bufL[i]=sample; @@ -471,7 +471,7 @@ int DivPlatformNES::getRegisterPoolSize() { } float DivPlatformNES::getPostAmp() { - return 128.0f; + return 2.0f; } void DivPlatformNES::reset() { From f057581b279e7678a3d4819eb81564ac5f38105f Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 27 Apr 2022 20:40:30 -0500 Subject: [PATCH 220/342] GUI: possibly remember maximized state --- src/gui/gui.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index fdfdf86fc..5469ac417 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -3613,6 +3613,10 @@ bool FurnaceGUI::init() { } if (SDL_GetDisplayUsableBounds(SDL_GetWindowDisplayIndex(sdlWin),&displaySize)==0) { + if (scrW>((displaySize.w/dpiScale)-32) && scrH>((displaySize.h/dpiScale)-32)) { + // maximize + SDL_MaximizeWindow(sdlWin); + } if (scrW>displaySize.w/dpiScale) scrW=(displaySize.w/dpiScale)-32; if (scrH>displaySize.h/dpiScale) scrH=(displaySize.h/dpiScale)-32; if (!fullScreen) { From ff33eed2fd16e97cf86f076af336d9bd59ce2b81 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 27 Apr 2022 20:43:34 -0500 Subject: [PATCH 221/342] GUI: improve the maximize auto-detection a bit --- src/gui/gui.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 5469ac417..5ef9a5640 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -3613,7 +3613,7 @@ bool FurnaceGUI::init() { } if (SDL_GetDisplayUsableBounds(SDL_GetWindowDisplayIndex(sdlWin),&displaySize)==0) { - if (scrW>((displaySize.w/dpiScale)-32) && scrH>((displaySize.h/dpiScale)-32)) { + if (scrW>((displaySize.w/dpiScale)-48) && scrH>((displaySize.h/dpiScale)-64)) { // maximize SDL_MaximizeWindow(sdlWin); } From f2b974acc54d913b286c5ff153dcb0bbb0dbce62 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 27 Apr 2022 22:57:36 -0500 Subject: [PATCH 222/342] SoundUnit: ring modulation --- src/engine/platform/sound/su.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/engine/platform/sound/su.cpp b/src/engine/platform/sound/su.cpp index 69b02a781..a9e6d2430 100644 --- a/src/engine/platform/sound/su.cpp +++ b/src/engine/platform/sound/su.cpp @@ -33,6 +33,10 @@ void SoundUnit::NextSample(short* l, short* r) { ns[i]=((((cycle[i]>>15)&127)>chan[i].duty)*127)^(short)SCtriangle[(cycle[i]>>14)&255]; break; } + + if (chan[i].flags.ring) { + ns[i]=(ns[i]*ns[(i+1)&7])>>7; + } if (chan[i].flags.pcm) { if (chan[i].freq>0x8000) { From c0df205f2db97a917c223a7335689f90ec55c0c5 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 27 Apr 2022 23:32:07 -0500 Subject: [PATCH 223/342] more changes to autoNoteOn now that there's a second prefer ins type, we get rid of DIV_INS_AMIGA check --- src/engine/engine.cpp | 2 +- src/engine/engine.h | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index a62ae36e2..3177432cf 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -2059,7 +2059,7 @@ void DivEngine::autoNoteOn(int ch, int ins, int note, int vol) { } do { - if ((ins==-1 || ins>=song.insLen || getChannelType(finalChan)==4 || getPreferInsType(finalChan)==getIns(ins)->type || getIns(ins)->type==DIV_INS_AMIGA) && chan[finalChan].midiNote==-1) { + if ((ins==-1 || ins>=song.insLen || getPreferInsType(finalChan)==getIns(ins)->type || getPreferInsSecondType(finalChan)==getIns(ins)->type) && chan[finalChan].midiNote==-1) { chan[finalChan].midiNote=note; pendingNotes.push(DivNoteEvent(finalChan,ins,note,vol,true)); break; diff --git a/src/engine/engine.h b/src/engine/engine.h index 4d12d3bbd..b36ee1403 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -523,6 +523,9 @@ class DivEngine { // get preferred instrument type DivInstrumentType getPreferInsType(int ch); + // get alternate instrument type + DivInstrumentType getPreferInsSecondType(int ch); + // get song system name const char* getSongSystemName(); From 3e9c0dd066810b42eb6a81a5ad3e25a301259226 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 27 Apr 2022 23:32:20 -0500 Subject: [PATCH 224/342] YMU759: update system definition --- src/engine/sysDef.cpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/engine/sysDef.cpp b/src/engine/sysDef.cpp index bbf5db2d2..082eb0833 100644 --- a/src/engine/sysDef.cpp +++ b/src/engine/sysDef.cpp @@ -305,6 +305,12 @@ DivInstrumentType DivEngine::getPreferInsType(int chan) { return sysDefs[sysOfChan[chan]]->chanInsType[dispatchChanOfChan[chan]][0]; } +DivInstrumentType DivEngine::getPreferInsSecondType(int chan) { + if (chan<0 || chan>chans) return DIV_INS_NULL; + if (sysDefs[sysOfChan[chan]]==NULL) return DIV_INS_NULL; + return sysDefs[sysOfChan[chan]]->chanInsType[dispatchChanOfChan[chan]][1]; +} + int DivEngine::minVGMVersion(DivSystem which) { switch (which) { case DIV_SYSTEM_YM2612: @@ -365,10 +371,10 @@ void DivEngine::registerSystems() { sysDefs[DIV_SYSTEM_YMU759]=new DivSysDef( "Yamaha YMU759", NULL, 0x01, 0x01, 17, true, false, 0, false, - {"Channel 1", "Channel 2", "Channel 3", "Channel 4", "Channel 5", "Channel 6", "Channel 7", "Channel 8", "Channel 9", "Channel 10", "Channel 11", "Channel 12", "Channel 13", "Channel 14", "Channel 15", "Channel 16", "PCM" }, // name - {"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "PCM" }, // short - {DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_PCM}, // type - {DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM} // ins + {"Channel 1", "Channel 2", "Channel 3", "Channel 4", "Channel 5", "Channel 6", "Channel 7", "Channel 8", "Channel 9", "Channel 10", "Channel 11", "Channel 12", "Channel 13", "Channel 14", "Channel 15", "Channel 16", "PCM" }, // name + {"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "PCM" }, // short + {DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_PCM }, // type + {DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_AMIGA} // ins ); sysDefs[DIV_SYSTEM_GENESIS]=new DivSysDef( From 4a9855f090d4aa81f15d0271d2464ddde23df48f Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 27 Apr 2022 23:54:45 -0500 Subject: [PATCH 225/342] dev89 - C64: add test/gate macro and "don't test/gate before new note" setting --- src/engine/engine.h | 4 ++-- src/engine/instrument.cpp | 8 ++++++++ src/engine/instrument.h | 3 ++- src/engine/platform/c64.cpp | 21 ++++++++++++++------- src/engine/platform/c64.h | 3 ++- src/gui/insEdit.cpp | 2 ++ 6 files changed, 30 insertions(+), 11 deletions(-) diff --git a/src/engine/engine.h b/src/engine/engine.h index b36ee1403..bab5dcd03 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -44,8 +44,8 @@ #define BUSY_BEGIN_SOFT softLocked=true; isBusy.lock(); #define BUSY_END isBusy.unlock(); softLocked=false; -#define DIV_VERSION "dev88" -#define DIV_ENGINE_VERSION 88 +#define DIV_VERSION "dev89" +#define DIV_ENGINE_VERSION 89 // for imports #define DIV_VERSION_MOD 0xff01 diff --git a/src/engine/instrument.cpp b/src/engine/instrument.cpp index d5061e0b7..f9a39724a 100644 --- a/src/engine/instrument.cpp +++ b/src/engine/instrument.cpp @@ -503,6 +503,9 @@ void DivInstrument::putInsData(SafeWriter* w) { w->writeC(std.ex6Macro.mode); w->writeC(std.ex7Macro.mode); w->writeC(std.ex8Macro.mode); + + // C64 no test + w->writeC(c64.noTest); } DivDataErrors DivInstrument::readInsData(SafeReader& reader, short version) { @@ -1006,6 +1009,11 @@ DivDataErrors DivInstrument::readInsData(SafeReader& reader, short version) { std.ex8Macro.mode=reader.readC(); } + // C64 no test + if (version>=89) { + c64.noTest=reader.readC(); + } + return DIV_DATA_SUCCESS; } diff --git a/src/engine/instrument.h b/src/engine/instrument.h index 0ca780043..d660c5796 100644 --- a/src/engine/instrument.h +++ b/src/engine/instrument.h @@ -265,7 +265,7 @@ struct DivInstrumentC64 { unsigned char a, d, s, r; unsigned short duty; unsigned char ringMod, oscSync; - bool toFilter, volIsCutoff, initFilter, dutyIsAbs, filterIsAbs; + bool toFilter, volIsCutoff, initFilter, dutyIsAbs, filterIsAbs, noTest; unsigned char res; unsigned short cut; bool hp, lp, bp, ch3off; @@ -287,6 +287,7 @@ struct DivInstrumentC64 { initFilter(false), dutyIsAbs(false), filterIsAbs(false), + noTest(false), res(0), cut(0), hp(false), diff --git a/src/engine/platform/c64.cpp b/src/engine/platform/c64.cpp index 79cfb42ba..d893283fc 100644 --- a/src/engine/platform/c64.cpp +++ b/src/engine/platform/c64.cpp @@ -170,16 +170,17 @@ void DivPlatformC64::tick(bool sysTick) { if (chan[i].testWhen>0) { if (--chan[i].testWhen<1) { if (!chan[i].resetMask && !chan[i].inPorta) { + DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_C64); rWrite(i*7+5,0); rWrite(i*7+6,0); - rWrite(i*7+4,(chan[i].wave<<4)|8|(chan[i].ring<<2)|(chan[i].sync<<1)); + rWrite(i*7+4,(chan[i].wave<<4)|(ins->c64.noTest?0:8)|(chan[i].test<<3)|(chan[i].ring<<2)|(chan[i].sync<<1)); } } } } if (chan[i].std.wave.had) { chan[i].wave=chan[i].std.wave.val; - rWrite(i*7+4,(chan[i].wave<<4)|(chan[i].ring<<2)|(chan[i].sync<<1)|(int)(chan[i].active)); + rWrite(i*7+4,(chan[i].wave<<4)|(chan[i].test<<3)|(chan[i].ring<<2)|(chan[i].sync<<1)|(int)(chan[i].active)); } if (chan[i].std.pitch.had) { chan[i].freqChanged=true; @@ -196,6 +197,11 @@ void DivPlatformC64::tick(bool sysTick) { chan[i].sync=chan[i].std.ex3.val&1; chan[i].ring=chan[i].std.ex3.val&2; chan[i].freqChanged=true; + rWrite(i*7+4,(chan[i].wave<<4)|(chan[i].test<<3)|(chan[i].ring<<2)|(chan[i].sync<<1)|(int)(chan[i].active)); + } + if (chan[i].std.ex4.had) { + chan[i].test=chan[i].std.ex4.val&1; + rWrite(i*7+4,(chan[i].wave<<4)|(chan[i].test<<3)|(chan[i].ring<<2)|(chan[i].sync<<1)|(int)(chan[i].active)); } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { @@ -204,12 +210,12 @@ void DivPlatformC64::tick(bool sysTick) { if (chan[i].keyOn) { rWrite(i*7+5,(chan[i].attack<<4)|(chan[i].decay)); rWrite(i*7+6,(chan[i].sustain<<4)|(chan[i].release)); - rWrite(i*7+4,(chan[i].wave<<4)|(chan[i].ring<<2)|(chan[i].sync<<1)|1); + rWrite(i*7+4,(chan[i].wave<<4)|(chan[i].test<<3)|(chan[i].ring<<2)|(chan[i].sync<<1)|1); } if (chan[i].keyOff) { rWrite(i*7+5,(chan[i].attack<<4)|(chan[i].decay)); rWrite(i*7+6,(chan[i].sustain<<4)|(chan[i].release)); - rWrite(i*7+4,(chan[i].wave<<4)|(chan[i].ring<<2)|(chan[i].sync<<1)|0); + rWrite(i*7+4,(chan[i].wave<<4)|(chan[i].test<<3)|(chan[i].ring<<2)|(chan[i].sync<<1)|0); } rWrite(i*7,chan[i].freq&0xff); rWrite(i*7+1,chan[i].freq>>8); @@ -231,6 +237,7 @@ int DivPlatformC64::dispatch(DivCommand c) { } chan[c.chan].active=true; chan[c.chan].keyOn=true; + chan[c.chan].test=false; if (chan[c.chan].insChanged || chan[c.chan].resetDuty || ins->std.waveMacro.len>0) { chan[c.chan].duty=ins->c64.duty; rWrite(c.chan*7+2,chan[c.chan].duty&0xff); @@ -335,7 +342,7 @@ int DivPlatformC64::dispatch(DivCommand c) { break; case DIV_CMD_WAVE: chan[c.chan].wave=c.value; - rWrite(c.chan*7+4,(chan[c.chan].wave<<4)|(chan[c.chan].ring<<2)|(chan[c.chan].sync<<1)|(int)(chan[c.chan].active)); + rWrite(c.chan*7+4,(chan[c.chan].wave<<4)|(chan[c.chan].test<<3)|(chan[c.chan].ring<<2)|(chan[c.chan].sync<<1)|(int)(chan[c.chan].active)); break; case DIV_CMD_LEGATO: chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value+((chan[c.chan].std.arp.will && !chan[c.chan].std.arp.mode)?(chan[c.chan].std.arp.val):(0))); @@ -416,11 +423,11 @@ int DivPlatformC64::dispatch(DivCommand c) { break; case 4: chan[c.chan].ring=c.value; - rWrite(c.chan*7+4,(chan[c.chan].wave<<4)|(chan[c.chan].ring<<2)|(chan[c.chan].sync<<1)|(int)(chan[c.chan].active)); + rWrite(c.chan*7+4,(chan[c.chan].wave<<4)|(chan[c.chan].test<<3)|(chan[c.chan].ring<<2)|(chan[c.chan].sync<<1)|(int)(chan[c.chan].active)); break; case 5: chan[c.chan].sync=c.value; - rWrite(c.chan*7+4,(chan[c.chan].wave<<4)|(chan[c.chan].ring<<2)|(chan[c.chan].sync<<1)|(int)(chan[c.chan].active)); + rWrite(c.chan*7+4,(chan[c.chan].wave<<4)|(chan[c.chan].test<<3)|(chan[c.chan].ring<<2)|(chan[c.chan].sync<<1)|(int)(chan[c.chan].active)); break; case 6: filtControl&=7; diff --git a/src/engine/platform/c64.h b/src/engine/platform/c64.h index 8df82051c..bc3cbf98f 100644 --- a/src/engine/platform/c64.h +++ b/src/engine/platform/c64.h @@ -30,7 +30,7 @@ class DivPlatformC64: public DivDispatch { unsigned char sweep, wave, attack, decay, sustain, release; short duty; bool active, insChanged, freqChanged, sweepChanged, keyOn, keyOff, inPorta, filter; - bool resetMask, resetFilter, resetDuty, ring, sync; + bool resetMask, resetFilter, resetDuty, ring, sync, test; signed char vol, outVol; DivMacroInt std; Channel(): @@ -61,6 +61,7 @@ class DivPlatformC64: public DivDispatch { resetDuty(false), ring(false), sync(false), + test(false), vol(15) {} }; Channel chan[3]; diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index 8631e74f8..105921881 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -2433,6 +2433,7 @@ void FurnaceGUI::drawInsEdit() { P(ImGui::Checkbox("Volume Macro is Cutoff Macro",&ins->c64.volIsCutoff)); P(ImGui::Checkbox("Absolute Cutoff Macro",&ins->c64.filterIsAbs)); P(ImGui::Checkbox("Absolute Duty Macro",&ins->c64.dutyIsAbs)); + P(ImGui::Checkbox("Don't test/gate before new note",&ins->c64.noTest)); ImGui::EndTabItem(); } if (ins->type==DIV_INS_AMIGA) if (ImGui::BeginTabItem("Sample")) { @@ -2926,6 +2927,7 @@ void FurnaceGUI::drawInsEdit() { } if (ins->type==DIV_INS_C64) { NORMAL_MACRO(ins->std.ex3Macro,0,2,"ex3","Special",32,ins->std.ex3Macro.open,true,c64SpecialBits,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[6],0,2,NULL,false); + NORMAL_MACRO(ins->std.ex4Macro,0,1,"ex4","Test/Gate",32,ins->std.ex4Macro.open,true,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[7],0,1,NULL,false); } if (ins->type==DIV_INS_AY || ins->type==DIV_INS_AY8930 || ins->type==DIV_INS_X1_010) { NORMAL_MACRO(ins->std.ex3Macro,0,15,"ex3","AutoEnv Num",96,ins->std.ex3Macro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[6],0,15,NULL,false); From 66f5b2117fb1df254d8e96c8cef90866fe35d12c Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 28 Apr 2022 00:26:21 -0500 Subject: [PATCH 226/342] dev90 - linear pitch macro option --- papers/format.md | 8 +++++++- src/engine/engine.cpp | 19 +++++++++++++------ src/engine/engine.h | 6 +++--- src/engine/fileOps.cpp | 13 +++++++++++-- src/engine/platform/amiga.cpp | 2 +- src/engine/platform/ay.cpp | 2 +- src/engine/platform/ay8930.cpp | 2 +- src/engine/platform/c64.cpp | 2 +- src/engine/platform/fds.cpp | 2 +- src/engine/platform/gb.cpp | 2 +- src/engine/platform/genesis.cpp | 2 +- src/engine/platform/genesisext.cpp | 2 +- src/engine/platform/lynx.cpp | 2 +- src/engine/platform/mmc5.cpp | 2 +- src/engine/platform/n163.cpp | 2 +- src/engine/platform/nes.cpp | 2 +- src/engine/platform/opl.cpp | 2 +- src/engine/platform/opll.cpp | 2 +- src/engine/platform/pce.cpp | 2 +- src/engine/platform/pcspkr.cpp | 2 +- src/engine/platform/pet.cpp | 2 +- src/engine/platform/qsound.cpp | 2 +- src/engine/platform/saa.cpp | 2 +- src/engine/platform/sms.cpp | 4 ++-- src/engine/platform/su.cpp | 2 +- src/engine/platform/swan.cpp | 2 +- src/engine/platform/vera.cpp | 2 +- src/engine/platform/vic20.cpp | 2 +- src/engine/platform/vrc6.cpp | 4 ++-- src/engine/platform/x1_010.cpp | 2 +- src/engine/platform/ym2610.cpp | 2 +- src/engine/platform/ym2610b.cpp | 2 +- src/engine/song.h | 4 +++- src/gui/compatFlags.cpp | 4 ++++ 34 files changed, 71 insertions(+), 43 deletions(-) diff --git a/papers/format.md b/papers/format.md index 8d977e84c..feafbe603 100644 --- a/papers/format.md +++ b/papers/format.md @@ -29,6 +29,9 @@ furthermore, an `or reserved` indicates this field is always present, but is res the format versions are: +- 90: Furnace dev90 +- 89: Furnace dev89 +- 88: Furnace dev88 - 87: Furnace dev87 - 86: Furnace dev86 - 85: Furnace dev85 @@ -280,7 +283,8 @@ size | description 1 | new Sega PCM (with macros and proper vol/pan) (>=84) or reserved 1 | weird f-num/block-based chip pitch slides (>=85) or reserved 1 | SN duty macro always resets phase (>=86) or reserved - 20 | reserved + 1 | pitch macro is linear (>=90) or reserved + 19 | reserved ``` # instrument @@ -669,6 +673,8 @@ size | description 1 | parameter 2 1 | parameter 3 1 | parameter 4 + --- | **extra C64 data** (>=89) + 1 | don't test/gate before new note ``` # wavetable diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 3177432cf..b2c0257f6 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -914,21 +914,28 @@ unsigned short DivEngine::calcBaseFreqFNumBlock(double clock, double divider, in return bf|(block<4095) pitch=4095; - return period? - ((base*(reversePitchTable[pitch]))/whatTheFuck): - (((base*(pitchTable[pitch]))>>10)*whatTheFuck)/1024; + int ret=period? + ((base*(reversePitchTable[pitch]))/whatTheFuck): + (((base*(pitchTable[pitch]))>>10)*whatTheFuck)/1024; + if (!song.pitchMacroIsLinear) { + ret+=period?(-pitch2):pitch2; + } + return ret; } return period? - base-pitch: - base+((pitch*octave)>>1); + base-pitch-pitch2: + base+((pitch*octave)>>1)+pitch2; } void DivEngine::play() { diff --git a/src/engine/engine.h b/src/engine/engine.h index bab5dcd03..7aa94fc58 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -44,8 +44,8 @@ #define BUSY_BEGIN_SOFT softLocked=true; isBusy.lock(); #define BUSY_END isBusy.unlock(); softLocked=false; -#define DIV_VERSION "dev89" -#define DIV_ENGINE_VERSION 89 +#define DIV_VERSION "dev90" +#define DIV_ENGINE_VERSION 90 // for imports #define DIV_VERSION_MOD 0xff01 @@ -468,7 +468,7 @@ class DivEngine { unsigned short calcBaseFreqFNumBlock(double clock, double divider, int note, int bits); // calculate frequency/period - int calcFreq(int base, int pitch, bool period=false, int octave=0); + int calcFreq(int base, int pitch, bool period=false, int octave=0, int pitch2=0); // find song loop position void walkSong(int& loopOrder, int& loopRow, int& loopEnd); diff --git a/src/engine/fileOps.cpp b/src/engine/fileOps.cpp index 16678c49b..cfd23e1fb 100644 --- a/src/engine/fileOps.cpp +++ b/src/engine/fileOps.cpp @@ -1011,6 +1011,9 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) { if (ds.version<86) { ds.snDutyReset=true; } + if (ds.version<90) { + ds.pitchMacroIsLinear=false; + } ds.isDMF=false; reader.readS(); // reserved @@ -1372,7 +1375,12 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) { } else { reader.readC(); } - for (int i=0; i<20; i++) { + if (ds.version>=90) { + ds.pitchMacroIsLinear=reader.readC(); + } else { + reader.readC(); + } + for (int i=0; i<19; i++) { reader.readC(); } } @@ -2317,7 +2325,8 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) { w->writeC(song.newSegaPCM); w->writeC(song.fbPortaPause); w->writeC(song.snDutyReset); - for (int i=0; i<20; i++) { + w->writeC(song.pitchMacroIsLinear); + for (int i=0; i<19; i++) { w->writeC(0); } diff --git a/src/engine/platform/amiga.cpp b/src/engine/platform/amiga.cpp index 1212b15d0..ac76e193a 100644 --- a/src/engine/platform/amiga.cpp +++ b/src/engine/platform/amiga.cpp @@ -207,7 +207,7 @@ void DivPlatformAmiga::tick(bool sysTick) { } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { //DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_AMIGA); - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true)+chan[i].std.pitch.val; + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].std.pitch.val); if (chan[i].freq>4095) chan[i].freq=4095; if (chan[i].freq<0) chan[i].freq=0; if (chan[i].keyOn) { diff --git a/src/engine/platform/ay.cpp b/src/engine/platform/ay.cpp index 4ba384832..fc2a36ab6 100644 --- a/src/engine/platform/ay.cpp +++ b/src/engine/platform/ay.cpp @@ -239,7 +239,7 @@ void DivPlatformAY8910::tick(bool sysTick) { if (!chan[i].std.ex3.will) chan[i].autoEnvNum=1; } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true)+chan[i].std.pitch.val; + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].std.pitch.val); if (chan[i].freq>4095) chan[i].freq=4095; if (chan[i].keyOn) { //rWrite(16+i*5+1,((chan[i].duty&3)<<6)|(63-(ins->gb.soundLen&63))); diff --git a/src/engine/platform/ay8930.cpp b/src/engine/platform/ay8930.cpp index 640b495b3..03e99961d 100644 --- a/src/engine/platform/ay8930.cpp +++ b/src/engine/platform/ay8930.cpp @@ -261,7 +261,7 @@ void DivPlatformAY8930::tick(bool sysTick) { immWrite(0x1a,ayNoiseOr); } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true)+chan[i].std.pitch.val; + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].std.pitch.val); if (chan[i].freq>65535) chan[i].freq=65535; if (chan[i].keyOn) { if (chan[i].insChanged) { diff --git a/src/engine/platform/c64.cpp b/src/engine/platform/c64.cpp index d893283fc..3515f0465 100644 --- a/src/engine/platform/c64.cpp +++ b/src/engine/platform/c64.cpp @@ -205,7 +205,7 @@ void DivPlatformC64::tick(bool sysTick) { } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,8)+chan[i].std.pitch.val; + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,8,chan[i].std.pitch.val); if (chan[i].freq>0xffff) chan[i].freq=0xffff; if (chan[i].keyOn) { rWrite(i*7+5,(chan[i].attack<<4)|(chan[i].decay)); diff --git a/src/engine/platform/fds.cpp b/src/engine/platform/fds.cpp index 2c3512137..845e72475 100644 --- a/src/engine/platform/fds.cpp +++ b/src/engine/platform/fds.cpp @@ -177,7 +177,7 @@ void DivPlatformFDS::tick(bool sysTick) { } } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false)+chan[i].std.pitch.val; + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,2,chan[i].std.pitch.val); if (chan[i].freq>4095) chan[i].freq=4095; if (chan[i].freq<0) chan[i].freq=0; if (chan[i].keyOn) { diff --git a/src/engine/platform/gb.cpp b/src/engine/platform/gb.cpp index b9668d58a..ded7b493c 100644 --- a/src/engine/platform/gb.cpp +++ b/src/engine/platform/gb.cpp @@ -227,7 +227,7 @@ void DivPlatformGB::tick(bool sysTick) { if (ntPos>255) ntPos=255; chan[i].freq=noiseTable[ntPos]; } else { - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true)+chan[i].std.pitch.val; + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].std.pitch.val); if (chan[i].freq>2047) chan[i].freq=2047; if (chan[i].freq<0) chan[i].freq=0; } diff --git a/src/engine/platform/genesis.cpp b/src/engine/platform/genesis.cpp index 72dc9c0fb..80c51d950 100644 --- a/src/engine/platform/genesis.cpp +++ b/src/engine/platform/genesis.cpp @@ -410,7 +410,7 @@ void DivPlatformGenesis::tick(bool sysTick) { for (int i=0; i<6; i++) { if (i==2 && extMode) continue; if (chan[i].freqChanged) { - int fNum=parent->calcFreq(chan[i].baseFreq&0x7ff,chan[i].pitch,false,4)+chan[i].std.pitch.val; + int fNum=parent->calcFreq(chan[i].baseFreq&0x7ff,chan[i].pitch,false,4,chan[i].std.pitch.val); int block=(chan[i].baseFreq&0xf800)>>11; if (fNum<0) fNum=0; if (fNum>2047) { diff --git a/src/engine/platform/genesisext.cpp b/src/engine/platform/genesisext.cpp index 59b77587b..d77857a66 100644 --- a/src/engine/platform/genesisext.cpp +++ b/src/engine/platform/genesisext.cpp @@ -307,7 +307,7 @@ void DivPlatformGenesisExt::tick(bool sysTick) { unsigned char writeMask=2; if (extMode) for (int i=0; i<4; i++) { if (opChan[i].freqChanged) { - int fNum=parent->calcFreq(opChan[i].baseFreq&0x7ff,opChan[i].pitch,false,4)+opChan[i].std.pitch.val; + int fNum=parent->calcFreq(opChan[i].baseFreq&0x7ff,opChan[i].pitch,false,4,opChan[i].std.pitch.val); int block=(opChan[i].baseFreq&0xf800)>>11; if (fNum<0) fNum=0; if (fNum>2047) { diff --git a/src/engine/platform/lynx.cpp b/src/engine/platform/lynx.cpp index 4c688103c..f70198b6e 100644 --- a/src/engine/platform/lynx.cpp +++ b/src/engine/platform/lynx.cpp @@ -195,7 +195,7 @@ void DivPlatformLynx::tick(bool sysTick) { WRITE_OTHER(i, ((chan[i].lfsr&0xf00)>>4)); chan[i].lfsr=-1; } - chan[i].fd=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true)+chan[i].std.pitch.val; + chan[i].fd=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].std.pitch.val); if (chan[i].std.duty.had) { chan[i].duty=chan[i].std.duty.val; WRITE_FEEDBACK(i, chan[i].duty.feedback); diff --git a/src/engine/platform/mmc5.cpp b/src/engine/platform/mmc5.cpp index cf1ea36d6..dc8c15063 100644 --- a/src/engine/platform/mmc5.cpp +++ b/src/engine/platform/mmc5.cpp @@ -133,7 +133,7 @@ void DivPlatformMMC5::tick(bool sysTick) { } } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true)-1+chan[i].std.pitch.val; + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].std.pitch.val)-1; if (chan[i].freq>2047) chan[i].freq=2047; if (chan[i].freq<0) chan[i].freq=0; if (chan[i].keyOn) { diff --git a/src/engine/platform/n163.cpp b/src/engine/platform/n163.cpp index a00d4d05d..34515305e 100644 --- a/src/engine/platform/n163.cpp +++ b/src/engine/platform/n163.cpp @@ -347,7 +347,7 @@ void DivPlatformN163::tick(bool sysTick) { chan[i].waveUpdated=false; } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { - chan[i].freq=parent->calcFreq((((chan[i].baseFreq*chan[i].waveLen)*(chanMax+1))/16),chan[i].pitch,false,0)+chan[i].std.pitch.val; + chan[i].freq=parent->calcFreq((((chan[i].baseFreq*chan[i].waveLen)*(chanMax+1))/16),chan[i].pitch,false,0,chan[i].std.pitch.val); if (chan[i].freq<0) chan[i].freq=0; if (chan[i].freq>0x3ffff) chan[i].freq=0x3ffff; if (chan[i].keyOn) { diff --git a/src/engine/platform/nes.cpp b/src/engine/platform/nes.cpp index cebd39e2d..c54127874 100644 --- a/src/engine/platform/nes.cpp +++ b/src/engine/platform/nes.cpp @@ -217,7 +217,7 @@ void DivPlatformNES::tick(bool sysTick) { if (ntPos>252) ntPos=252; chan[i].freq=(parent->song.properNoiseLayout)?(15-(chan[i].baseFreq&15)):(noiseTable[ntPos]); } else { - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true)-1+chan[i].std.pitch.val; + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].std.pitch.val)-1; if (chan[i].freq>2047) chan[i].freq=2047; if (chan[i].freq<0) chan[i].freq=0; } diff --git a/src/engine/platform/opl.cpp b/src/engine/platform/opl.cpp index fac157da3..1e727fa66 100644 --- a/src/engine/platform/opl.cpp +++ b/src/engine/platform/opl.cpp @@ -417,7 +417,7 @@ void DivPlatformOPL::tick(bool sysTick) { bool updateDrums=false; for (int i=0; icalcFreq(chan[i].baseFreq,chan[i].pitch,false,octave(chan[i].baseFreq)); + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,octave(chan[i].baseFreq),chan[i].std.pitch.val); if (chan[i].freq>131071) chan[i].freq=131071; int freqt=toFreq(chan[i].freq)+chan[i].std.pitch.val; chan[i].freqH=freqt>>8; diff --git a/src/engine/platform/opll.cpp b/src/engine/platform/opll.cpp index a02bc8de2..c2e1342bd 100644 --- a/src/engine/platform/opll.cpp +++ b/src/engine/platform/opll.cpp @@ -261,7 +261,7 @@ void DivPlatformOPLL::tick(bool sysTick) { for (int i=0; i<11; i++) { if (chan[i].freqChanged) { - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,octave(chan[i].baseFreq)); + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,octave(chan[i].baseFreq),chan[i].std.pitch.val); if (chan[i].freq>262143) chan[i].freq=262143; int freqt=toFreq(chan[i].freq)+chan[i].std.pitch.val; chan[i].freqL=freqt&0xff; diff --git a/src/engine/platform/pce.cpp b/src/engine/platform/pce.cpp index 016b3b3c4..5178cad36 100644 --- a/src/engine/platform/pce.cpp +++ b/src/engine/platform/pce.cpp @@ -217,7 +217,7 @@ void DivPlatformPCE::tick(bool sysTick) { } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { //DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_PCE); - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true)+chan[i].std.pitch.val; + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].std.pitch.val); if (chan[i].furnaceDac) { double off=1.0; if (chan[i].dacSample>=0 && chan[i].dacSamplesong.sampleLen) { diff --git a/src/engine/platform/pcspkr.cpp b/src/engine/platform/pcspkr.cpp index 8a00c5638..1a4b2ee06 100644 --- a/src/engine/platform/pcspkr.cpp +++ b/src/engine/platform/pcspkr.cpp @@ -193,7 +193,7 @@ void DivPlatformPCSpeaker::tick(bool sysTick) { chan[i].freqChanged=true; } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true)-1+chan[i].std.pitch.val; + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].std.pitch.val)-1; if (chan[i].freq<0) chan[i].freq=0; if (chan[i].freq>65535) chan[i].freq=65535; if (chan[i].keyOn) { diff --git a/src/engine/platform/pet.cpp b/src/engine/platform/pet.cpp index 0e83e9d21..eb954a6e1 100644 --- a/src/engine/platform/pet.cpp +++ b/src/engine/platform/pet.cpp @@ -116,7 +116,7 @@ void DivPlatformPET::tick(bool sysTick) { chan.freqChanged=true; } if (chan.freqChanged || chan.keyOn || chan.keyOff) { - chan.freq=parent->calcFreq(chan.baseFreq,chan.pitch,true)+chan.std.pitch.val; + chan.freq=parent->calcFreq(chan.baseFreq,chan.pitch,true,0,chan.std.pitch.val); if (chan.freq>257) chan.freq=257; if (chan.freq<2) chan.freq=2; rWrite(8,chan.freq-2); diff --git a/src/engine/platform/qsound.cpp b/src/engine/platform/qsound.cpp index b5d194ffc..fa31caa81 100644 --- a/src/engine/platform/qsound.cpp +++ b/src/engine/platform/qsound.cpp @@ -331,7 +331,7 @@ void DivPlatformQSound::tick(bool sysTick) { } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { //DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_AMIGA); - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false)+chan[i].std.pitch.val; + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,2,chan[i].std.pitch.val); if (chan[i].freq>0xffff) chan[i].freq=0xffff; if (chan[i].keyOn) { rWrite(q1_reg_map[Q1V_BANK][i], qsound_bank); diff --git a/src/engine/platform/saa.cpp b/src/engine/platform/saa.cpp index f21c7d5b4..2d4986b58 100644 --- a/src/engine/platform/saa.cpp +++ b/src/engine/platform/saa.cpp @@ -194,7 +194,7 @@ void DivPlatformSAA1099::tick(bool sysTick) { rWrite(0x18+(i/3),saaEnv[i/3]); } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true)+chan[i].std.pitch.val; + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].std.pitch.val); if (chan[i].freq>65535) chan[i].freq=65535; if (chan[i].freq>=32768) { chan[i].freqH=7; diff --git a/src/engine/platform/sms.cpp b/src/engine/platform/sms.cpp index c32548b56..faf5ba7d6 100644 --- a/src/engine/platform/sms.cpp +++ b/src/engine/platform/sms.cpp @@ -106,7 +106,7 @@ void DivPlatformSMS::tick(bool sysTick) { } for (int i=0; i<3; i++) { if (chan[i].freqChanged) { - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true)+chan[i].std.pitch.val; + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].std.pitch.val); if (chan[i].freq>1023) chan[i].freq=1023; if (chan[i].freq<8) chan[i].freq=1; //if (chan[i].actualNote>0x5d) chan[i].freq=0x01; @@ -121,7 +121,7 @@ void DivPlatformSMS::tick(bool sysTick) { } } if (chan[3].freqChanged || updateSNMode) { - chan[3].freq=parent->calcFreq(chan[3].baseFreq,chan[3].pitch,true)+chan[3].std.pitch.val; + chan[3].freq=parent->calcFreq(chan[3].baseFreq,chan[3].pitch,true,0,chan[3].std.pitch.val); if (chan[3].freq>1023) chan[3].freq=1023; if (chan[3].actualNote>0x5d) chan[3].freq=0x01; if (snNoiseMode&2) { // take period from channel 3 diff --git a/src/engine/platform/su.cpp b/src/engine/platform/su.cpp index 2dca4b3ca..1f47a1629 100644 --- a/src/engine/platform/su.cpp +++ b/src/engine/platform/su.cpp @@ -168,7 +168,7 @@ void DivPlatformSoundUnit::tick(bool sysTick) { } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { //DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_SU); - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false)+chan[i].std.pitch.val; + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,2,chan[i].std.pitch.val); chWrite(i,0x00,chan[i].freq&0xff); chWrite(i,0x01,chan[i].freq>>8); if (chan[i].freq>65535) chan[i].freq=65535; diff --git a/src/engine/platform/swan.cpp b/src/engine/platform/swan.cpp index 86ad9b109..96752bd5a 100644 --- a/src/engine/platform/swan.cpp +++ b/src/engine/platform/swan.cpp @@ -194,7 +194,7 @@ void DivPlatformSwan::tick(bool sysTick) { } } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true)+chan[i].std.pitch.val; + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].std.pitch.val); if (i==1 && pcm && furnaceDac) { double off=1.0; if (dacSample>=0 && dacSamplesong.sampleLen) { diff --git a/src/engine/platform/vera.cpp b/src/engine/platform/vera.cpp index 63415a0c1..7d4ea7d54 100644 --- a/src/engine/platform/vera.cpp +++ b/src/engine/platform/vera.cpp @@ -194,7 +194,7 @@ void DivPlatformVERA::tick(bool sysTick) { chan[i].freqChanged=true; } if (chan[i].freqChanged) { - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,8)+chan[i].std.pitch.val; + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,8,chan[i].std.pitch.val); if (chan[i].freq>65535) chan[i].freq=65535; rWrite(i,0,chan[i].freq&0xff); rWrite(i,1,(chan[i].freq>>8)&0xff); diff --git a/src/engine/platform/vic20.cpp b/src/engine/platform/vic20.cpp index aa787672b..efeb0a206 100644 --- a/src/engine/platform/vic20.cpp +++ b/src/engine/platform/vic20.cpp @@ -123,7 +123,7 @@ void DivPlatformVIC20::tick(bool sysTick) { chan[i].freqChanged=true; } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true)+chan[i].std.pitch.val; + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].std.pitch.val); if (i<3) { chan[i].freq>>=(2-i); } else { diff --git a/src/engine/platform/vrc6.cpp b/src/engine/platform/vrc6.cpp index 56e95948c..29fadd4c7 100644 --- a/src/engine/platform/vrc6.cpp +++ b/src/engine/platform/vrc6.cpp @@ -183,9 +183,9 @@ void DivPlatformVRC6::tick(bool sysTick) { } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { if (i==2) { // sawtooth - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true)-1+chan[i].std.pitch.val; + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].std.pitch.val)-1; } else { // pulse - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true)-1+chan[i].std.pitch.val; + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].std.pitch.val)-1; if (chan[i].furnaceDac) { double off=1.0; if (chan[i].dacSample>=0 && chan[i].dacSamplesong.sampleLen) { diff --git a/src/engine/platform/x1_010.cpp b/src/engine/platform/x1_010.cpp index f1498709f..46db65622 100644 --- a/src/engine/platform/x1_010.cpp +++ b/src/engine/platform/x1_010.cpp @@ -476,7 +476,7 @@ void DivPlatformX1_010::tick(bool sysTick) { chan[i].envChanged=false; } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false)+chan[i].std.pitch.val; + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,2,chan[i].std.pitch.val); if (chan[i].pcm) { if (chan[i].freq<1) chan[i].freq=1; if (chan[i].freq>255) chan[i].freq=255; diff --git a/src/engine/platform/ym2610.cpp b/src/engine/platform/ym2610.cpp index 04338d265..0af0840ab 100644 --- a/src/engine/platform/ym2610.cpp +++ b/src/engine/platform/ym2610.cpp @@ -572,7 +572,7 @@ void DivPlatformYM2610::tick(bool sysTick) { for (int i=0; i<4; i++) { if (i==1 && extMode) continue; if (chan[i].freqChanged) { - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,octave(chan[i].baseFreq)); + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,octave(chan[i].baseFreq),chan[i].std.pitch.val); if (chan[i].freq>262143) chan[i].freq=262143; int freqt=toFreq(chan[i].freq)+chan[i].std.pitch.val; immWrite(chanOffs[i]+ADDR_FREQH,freqt>>8); diff --git a/src/engine/platform/ym2610b.cpp b/src/engine/platform/ym2610b.cpp index a250e04ce..b0e530db3 100644 --- a/src/engine/platform/ym2610b.cpp +++ b/src/engine/platform/ym2610b.cpp @@ -635,7 +635,7 @@ void DivPlatformYM2610B::tick(bool sysTick) { for (int i=0; i<6; i++) { if (i==2 && extMode) continue; if (chan[i].freqChanged) { - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,octave(chan[i].baseFreq)); + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,octave(chan[i].baseFreq),chan[i].std.pitch.val); if (chan[i].freq>262143) chan[i].freq=262143; int freqt=toFreq(chan[i].freq)+chan[i].std.pitch.val; immWrite(chanOffs[i]+ADDR_FREQH,freqt>>8); diff --git a/src/engine/song.h b/src/engine/song.h index 317ffa9b0..087d8c7b0 100644 --- a/src/engine/song.h +++ b/src/engine/song.h @@ -336,6 +336,7 @@ struct DivSong { bool newSegaPCM; bool fbPortaPause; bool snDutyReset; + bool pitchMacroIsLinear; DivOrders orders; std::vector ins; @@ -441,7 +442,8 @@ struct DivSong { e1e2AlsoTakePriority(false), newSegaPCM(true), fbPortaPause(false), - snDutyReset(false) { + snDutyReset(false), + pitchMacroIsLinear(true) { for (int i=0; i<32; i++) { system[i]=DIV_SYSTEM_NULL; systemVol[i]=64; diff --git a/src/gui/compatFlags.cpp b/src/gui/compatFlags.cpp index ccaeeb3fe..52f465027 100644 --- a/src/gui/compatFlags.cpp +++ b/src/gui/compatFlags.cpp @@ -121,6 +121,10 @@ void FurnaceGUI::drawCompatFlags() { if (ImGui::IsItemHovered()) { ImGui::SetTooltip("when enabled, duty macro will always reset phase, even if its value hasn't changed."); } + ImGui::Checkbox("Pitch macro is linear",&e->song.pitchMacroIsLinear); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("when enabled, the pitch macro of an instrument is in linear space."); + } ImGui::Text("Loop modality:"); if (ImGui::RadioButton("Reset channels",e->song.loopModality==0)) { From 77b4e57c589c3e5e53d62e4120576e026b372904 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 28 Apr 2022 00:50:09 -0500 Subject: [PATCH 227/342] prepare for relative pitch macro --- src/engine/platform/amiga.h | 3 ++- src/engine/platform/arcade.h | 3 ++- src/engine/platform/ay.h | 4 ++-- src/engine/platform/ay8930.h | 4 ++-- src/engine/platform/bubsyswsg.h | 3 ++- src/engine/platform/c64.h | 3 ++- src/engine/platform/fds.h | 3 ++- src/engine/platform/gb.h | 3 ++- src/engine/platform/genesis.h | 3 ++- src/engine/platform/genesisext.h | 3 ++- src/engine/platform/lynx.h | 3 ++- src/engine/platform/mmc5.h | 3 ++- src/engine/platform/n163.h | 3 ++- src/engine/platform/nes.h | 3 ++- src/engine/platform/opl.h | 3 ++- src/engine/platform/opll.h | 3 ++- src/engine/platform/pce.h | 3 ++- src/engine/platform/pcspkr.h | 3 ++- src/engine/platform/pet.h | 3 ++- src/engine/platform/qsound.h | 3 ++- src/engine/platform/saa.h | 4 ++-- src/engine/platform/segapcm.h | 4 ++-- src/engine/platform/sms.h | 3 ++- src/engine/platform/su.h | 3 ++- src/engine/platform/swan.h | 3 ++- src/engine/platform/tia.h | 4 ++-- src/engine/platform/tx81z.h | 3 ++- src/engine/platform/vera.h | 5 +++-- src/engine/platform/vic20.h | 3 ++- src/engine/platform/vrc6.h | 3 ++- src/engine/platform/x1_010.h | 6 +++--- src/engine/platform/ym2610.h | 3 ++- src/engine/platform/ym2610b.h | 3 ++- src/engine/platform/ym2610bext.h | 4 ++-- src/engine/platform/ym2610ext.h | 4 ++-- 35 files changed, 72 insertions(+), 45 deletions(-) diff --git a/src/engine/platform/amiga.h b/src/engine/platform/amiga.h index a4c1a7a44..7a7f0d242 100644 --- a/src/engine/platform/amiga.h +++ b/src/engine/platform/amiga.h @@ -27,7 +27,7 @@ class DivPlatformAmiga: public DivDispatch { struct Channel { - int freq, baseFreq, pitch; + int freq, baseFreq, pitch, pitch2; unsigned int audLoc; unsigned short audLen; unsigned int audPos; @@ -45,6 +45,7 @@ class DivPlatformAmiga: public DivDispatch { freq(0), baseFreq(0), pitch(0), + pitch2(0), audLoc(0), audLen(0), audPos(0), diff --git a/src/engine/platform/arcade.h b/src/engine/platform/arcade.h index e051e8aba..07a610f72 100644 --- a/src/engine/platform/arcade.h +++ b/src/engine/platform/arcade.h @@ -36,7 +36,7 @@ class DivPlatformArcade: public DivDispatch { DivInstrumentFM state; DivMacroInt std; unsigned char freqH, freqL; - int freq, baseFreq, pitch, note; + int freq, baseFreq, pitch, pitch2, note; int ins; signed char konCycles; bool active, insChanged, freqChanged, keyOn, keyOff, inPorta, portaPause, furnacePCM, hardReset; @@ -48,6 +48,7 @@ class DivPlatformArcade: public DivDispatch { freq(0), baseFreq(0), pitch(0), + pitch2(0), note(0), ins(-1), active(false), diff --git a/src/engine/platform/ay.h b/src/engine/platform/ay.h index ce06fc9fc..217d11acf 100644 --- a/src/engine/platform/ay.h +++ b/src/engine/platform/ay.h @@ -32,7 +32,7 @@ class DivPlatformAY8910: public DivDispatch { inline unsigned char regRemap(unsigned char reg) { return intellivision?AY8914RegRemap[reg&0x0f]:reg&0x0f; } struct Channel { unsigned char freqH, freqL; - int freq, baseFreq, note, pitch; + int freq, baseFreq, note, pitch, pitch2; int ins; unsigned char psgMode, autoEnvNum, autoEnvDen; signed char konCycles; @@ -40,7 +40,7 @@ class DivPlatformAY8910: public DivDispatch { int vol, outVol; unsigned char pan; DivMacroInt std; - Channel(): freqH(0), freqL(0), freq(0), baseFreq(0), note(0), pitch(0), ins(-1), psgMode(1), autoEnvNum(0), autoEnvDen(0), active(false), insChanged(true), freqChanged(false), keyOn(false), keyOff(false), portaPause(false), inPorta(false), vol(0), outVol(15), pan(3) {} + Channel(): freqH(0), freqL(0), freq(0), baseFreq(0), note(0), pitch(0), pitch2(0), ins(-1), psgMode(1), autoEnvNum(0), autoEnvDen(0), active(false), insChanged(true), freqChanged(false), keyOn(false), keyOff(false), portaPause(false), inPorta(false), vol(0), outVol(15), pan(3) {} }; Channel chan[3]; bool isMuted[3]; diff --git a/src/engine/platform/ay8930.h b/src/engine/platform/ay8930.h index 61a186f4c..098a239bb 100644 --- a/src/engine/platform/ay8930.h +++ b/src/engine/platform/ay8930.h @@ -28,7 +28,7 @@ class DivPlatformAY8930: public DivDispatch { protected: struct Channel { unsigned char freqH, freqL; - int freq, baseFreq, note, pitch; + int freq, baseFreq, note, pitch, pitch2; int ins; unsigned char psgMode, autoEnvNum, autoEnvDen, duty; signed char konCycles; @@ -36,7 +36,7 @@ class DivPlatformAY8930: public DivDispatch { int vol, outVol; unsigned char pan; DivMacroInt std; - Channel(): freqH(0), freqL(0), freq(0), baseFreq(0), note(0), pitch(0), ins(-1), psgMode(1), autoEnvNum(0), autoEnvDen(0), duty(4), active(false), insChanged(true), freqChanged(false), keyOn(false), keyOff(false), portaPause(false), inPorta(false), vol(0), outVol(31), pan(3) {} + Channel(): freqH(0), freqL(0), freq(0), baseFreq(0), note(0), pitch(0), pitch2(0), ins(-1), psgMode(1), autoEnvNum(0), autoEnvDen(0), duty(4), active(false), insChanged(true), freqChanged(false), keyOn(false), keyOff(false), portaPause(false), inPorta(false), vol(0), outVol(31), pan(3) {} }; Channel chan[3]; bool isMuted[3]; diff --git a/src/engine/platform/bubsyswsg.h b/src/engine/platform/bubsyswsg.h index 0fbb2ac5d..87978d71e 100644 --- a/src/engine/platform/bubsyswsg.h +++ b/src/engine/platform/bubsyswsg.h @@ -28,7 +28,7 @@ class DivPlatformBubSysWSG: public DivDispatch { struct Channel { - int freq, baseFreq, pitch, note, ins; + int freq, baseFreq, pitch, pitch2, note, ins; bool active, insChanged, freqChanged, keyOn, keyOff, inPorta; signed char vol, outVol, wave; signed char waveROM[32] = {0}; // 4 bit PROM per channel on bubble system @@ -38,6 +38,7 @@ class DivPlatformBubSysWSG: public DivDispatch { freq(0), baseFreq(0), pitch(0), + pitch2(0), note(0), ins(-1), active(false), diff --git a/src/engine/platform/c64.h b/src/engine/platform/c64.h index bc3cbf98f..2cb53c168 100644 --- a/src/engine/platform/c64.h +++ b/src/engine/platform/c64.h @@ -26,7 +26,7 @@ class DivPlatformC64: public DivDispatch { struct Channel { - int freq, baseFreq, pitch, prevFreq, testWhen, note, ins; + int freq, baseFreq, pitch, pitch2, prevFreq, testWhen, note, ins; unsigned char sweep, wave, attack, decay, sustain, release; short duty; bool active, insChanged, freqChanged, sweepChanged, keyOn, keyOff, inPorta, filter; @@ -37,6 +37,7 @@ class DivPlatformC64: public DivDispatch { freq(0), baseFreq(0), pitch(0), + pitch2(0), prevFreq(65535), testWhen(0), note(0), diff --git a/src/engine/platform/fds.h b/src/engine/platform/fds.h index bf9492722..252452e81 100644 --- a/src/engine/platform/fds.h +++ b/src/engine/platform/fds.h @@ -26,7 +26,7 @@ class DivPlatformFDS: public DivDispatch { struct Channel { - int freq, baseFreq, pitch, prevFreq, note, modFreq, ins; + int freq, baseFreq, pitch, pitch2, prevFreq, note, modFreq, ins; unsigned char duty, sweep, modDepth, modPos; bool active, insChanged, freqChanged, sweepChanged, keyOn, keyOff, inPorta, modOn; signed char vol, outVol, wave; @@ -36,6 +36,7 @@ class DivPlatformFDS: public DivDispatch { freq(0), baseFreq(0), pitch(0), + pitch2(0), prevFreq(65535), note(0), modFreq(0), diff --git a/src/engine/platform/gb.h b/src/engine/platform/gb.h index c79603ab1..9d1a4572b 100644 --- a/src/engine/platform/gb.h +++ b/src/engine/platform/gb.h @@ -27,7 +27,7 @@ class DivPlatformGB: public DivDispatch { struct Channel { - int freq, baseFreq, pitch, note, ins; + int freq, baseFreq, pitch, pitch2, note, ins; unsigned char duty, sweep; bool active, insChanged, freqChanged, sweepChanged, keyOn, keyOff, inPorta; signed char vol, outVol, wave; @@ -36,6 +36,7 @@ class DivPlatformGB: public DivDispatch { freq(0), baseFreq(0), pitch(0), + pitch2(0), note(0), ins(-1), duty(0), diff --git a/src/engine/platform/genesis.h b/src/engine/platform/genesis.h index 31f11d188..13df1e8b2 100644 --- a/src/engine/platform/genesis.h +++ b/src/engine/platform/genesis.h @@ -36,7 +36,7 @@ class DivPlatformGenesis: public DivDispatch { DivInstrumentFM state; DivMacroInt std; unsigned char freqH, freqL; - int freq, baseFreq, pitch, portaPauseFreq, note; + int freq, baseFreq, pitch, pitch2, portaPauseFreq, note; int ins; bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, furnaceDac, inPorta, hardReset; int vol, outVol; @@ -47,6 +47,7 @@ class DivPlatformGenesis: public DivDispatch { freq(0), baseFreq(0), pitch(0), + pitch2(0), portaPauseFreq(0), note(0), ins(-1), diff --git a/src/engine/platform/genesisext.h b/src/engine/platform/genesisext.h index e3604a34c..c246ee3f3 100644 --- a/src/engine/platform/genesisext.h +++ b/src/engine/platform/genesisext.h @@ -25,7 +25,7 @@ class DivPlatformGenesisExt: public DivPlatformGenesis { struct OpChannel { DivMacroInt std; unsigned char freqH, freqL; - int freq, baseFreq, pitch, portaPauseFreq, ins; + int freq, baseFreq, pitch, pitch2, portaPauseFreq, ins; signed char konCycles; bool active, insChanged, freqChanged, keyOn, keyOff, portaPause; int vol; @@ -36,6 +36,7 @@ class DivPlatformGenesisExt: public DivPlatformGenesis { freq(0), baseFreq(0), pitch(0), + pitch2(0), portaPauseFreq(0), ins(-1), active(false), diff --git a/src/engine/platform/lynx.h b/src/engine/platform/lynx.h index f1f974ed0..1093bad8a 100644 --- a/src/engine/platform/lynx.h +++ b/src/engine/platform/lynx.h @@ -44,7 +44,7 @@ class DivPlatformLynx: public DivDispatch { DivMacroInt std; MikeyFreqDiv fd; MikeyDuty duty; - int baseFreq, pitch, note, actualNote, lfsr, ins; + int baseFreq, pitch, pitch2, note, actualNote, lfsr, ins; unsigned char pan; bool active, insChanged, freqChanged, keyOn, keyOff, inPorta; signed char vol, outVol; @@ -54,6 +54,7 @@ class DivPlatformLynx: public DivDispatch { duty(0), baseFreq(0), pitch(0), + pitch2(0), note(0), actualNote(0), lfsr(-1), diff --git a/src/engine/platform/mmc5.h b/src/engine/platform/mmc5.h index 879ebee18..7db844a06 100644 --- a/src/engine/platform/mmc5.h +++ b/src/engine/platform/mmc5.h @@ -25,7 +25,7 @@ class DivPlatformMMC5: public DivDispatch { struct Channel { - int freq, baseFreq, pitch, prevFreq, note, ins; + int freq, baseFreq, pitch, pitch2, prevFreq, note, ins; unsigned char duty, sweep; bool active, insChanged, freqChanged, sweepChanged, keyOn, keyOff, inPorta, furnaceDac; signed char vol, outVol, wave; @@ -34,6 +34,7 @@ class DivPlatformMMC5: public DivDispatch { freq(0), baseFreq(0), pitch(0), + pitch2(0), prevFreq(65535), note(0), ins(-1), diff --git a/src/engine/platform/n163.h b/src/engine/platform/n163.h index 38ebeace3..0b4edbdd8 100644 --- a/src/engine/platform/n163.h +++ b/src/engine/platform/n163.h @@ -28,7 +28,7 @@ class DivPlatformN163: public DivDispatch { struct Channel { - int freq, baseFreq, pitch, note; + int freq, baseFreq, pitch, pitch2, note; short ins, wave, wavePos, waveLen; unsigned char waveMode; short loadWave, loadPos, loadLen; @@ -41,6 +41,7 @@ class DivPlatformN163: public DivDispatch { freq(0), baseFreq(0), pitch(0), + pitch2(0), note(0), ins(-1), wave(-1), diff --git a/src/engine/platform/nes.h b/src/engine/platform/nes.h index 7650d39e9..8de7f7b57 100644 --- a/src/engine/platform/nes.h +++ b/src/engine/platform/nes.h @@ -25,7 +25,7 @@ class DivPlatformNES: public DivDispatch { struct Channel { - int freq, baseFreq, pitch, prevFreq, note, ins; + int freq, baseFreq, pitch, pitch2, prevFreq, note, ins; unsigned char duty, sweep; bool active, insChanged, freqChanged, sweepChanged, keyOn, keyOff, inPorta, furnaceDac; signed char vol, outVol, wave; @@ -34,6 +34,7 @@ class DivPlatformNES: public DivDispatch { freq(0), baseFreq(0), pitch(0), + pitch2(0), prevFreq(65535), note(0), ins(-1), diff --git a/src/engine/platform/opl.h b/src/engine/platform/opl.h index ee3bcb36f..50a232aa8 100644 --- a/src/engine/platform/opl.h +++ b/src/engine/platform/opl.h @@ -30,7 +30,7 @@ class DivPlatformOPL: public DivDispatch { DivInstrumentFM state; DivMacroInt std; unsigned char freqH, freqL; - int freq, baseFreq, pitch, note, ins; + int freq, baseFreq, pitch, pitch2, note, ins; bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, furnaceDac, inPorta, fourOp; int vol, outVol; unsigned char pan; @@ -40,6 +40,7 @@ class DivPlatformOPL: public DivDispatch { freq(0), baseFreq(0), pitch(0), + pitch2(0), note(0), ins(-1), active(false), diff --git a/src/engine/platform/opll.h b/src/engine/platform/opll.h index 79f905c66..8b1732aaf 100644 --- a/src/engine/platform/opll.h +++ b/src/engine/platform/opll.h @@ -33,7 +33,7 @@ class DivPlatformOPLL: public DivDispatch { DivInstrumentFM state; DivMacroInt std; unsigned char freqH, freqL; - int freq, baseFreq, pitch, note, ins; + int freq, baseFreq, pitch, pitch2, note, ins; bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, furnaceDac, inPorta; int vol, outVol; unsigned char pan; @@ -43,6 +43,7 @@ class DivPlatformOPLL: public DivDispatch { freq(0), baseFreq(0), pitch(0), + pitch2(0), note(0), ins(-1), active(false), diff --git a/src/engine/platform/pce.h b/src/engine/platform/pce.h index deb85fbb9..1f92e6338 100644 --- a/src/engine/platform/pce.h +++ b/src/engine/platform/pce.h @@ -28,7 +28,7 @@ class DivPlatformPCE: public DivDispatch { struct Channel { - int freq, baseFreq, pitch, note; + int freq, baseFreq, pitch, pitch2, note; int dacPeriod, dacRate; unsigned int dacPos; int dacSample, ins; @@ -41,6 +41,7 @@ class DivPlatformPCE: public DivDispatch { freq(0), baseFreq(0), pitch(0), + pitch2(0), note(0), dacPeriod(0), dacRate(0), diff --git a/src/engine/platform/pcspkr.h b/src/engine/platform/pcspkr.h index f8813f3ff..815f2a96d 100644 --- a/src/engine/platform/pcspkr.h +++ b/src/engine/platform/pcspkr.h @@ -25,7 +25,7 @@ class DivPlatformPCSpeaker: public DivDispatch { struct Channel { - int freq, baseFreq, pitch, note, ins; + int freq, baseFreq, pitch, pitch2, note, ins; unsigned char duty, sweep; bool active, insChanged, freqChanged, sweepChanged, keyOn, keyOff, inPorta, furnaceDac; signed char vol, outVol, wave; @@ -34,6 +34,7 @@ class DivPlatformPCSpeaker: public DivDispatch { freq(0), baseFreq(0), pitch(0), + pitch2(0), note(0), ins(-1), duty(0), diff --git a/src/engine/platform/pet.h b/src/engine/platform/pet.h index b4b75316e..567d4fc43 100644 --- a/src/engine/platform/pet.h +++ b/src/engine/platform/pet.h @@ -25,7 +25,7 @@ class DivPlatformPET: public DivDispatch { struct Channel { - int freq, baseFreq, pitch, note, ins; + int freq, baseFreq, pitch, pitch2, note, ins; bool active, insChanged, freqChanged, keyOn, keyOff, inPorta; int vol, outVol, wave; unsigned char sreg; @@ -36,6 +36,7 @@ class DivPlatformPET: public DivDispatch { freq(0), baseFreq(0), pitch(0), + pitch2(0), note(0), ins(-1), active(false), diff --git a/src/engine/platform/qsound.h b/src/engine/platform/qsound.h index aa07a3b47..23229e142 100644 --- a/src/engine/platform/qsound.h +++ b/src/engine/platform/qsound.h @@ -27,7 +27,7 @@ class DivPlatformQSound: public DivDispatch { struct Channel { - int freq, baseFreq, pitch; + int freq, baseFreq, pitch, pitch2; unsigned short audLen; unsigned int audPos; int sample, wave, ins; @@ -40,6 +40,7 @@ class DivPlatformQSound: public DivDispatch { freq(0), baseFreq(0), pitch(0), + pitch2(0), audLen(0), audPos(0), sample(-1), diff --git a/src/engine/platform/saa.h b/src/engine/platform/saa.h index 309092936..8546c9679 100644 --- a/src/engine/platform/saa.h +++ b/src/engine/platform/saa.h @@ -35,14 +35,14 @@ class DivPlatformSAA1099: public DivDispatch { protected: struct Channel { unsigned char freqH, freqL; - int freq, baseFreq, pitch, note, ins; + int freq, baseFreq, pitch, pitch2, note, ins; unsigned char psgMode; signed char konCycles; bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, inPorta; int vol, outVol; unsigned char pan; DivMacroInt std; - Channel(): freqH(0), freqL(0), freq(0), baseFreq(0), pitch(0), note(0), ins(-1), psgMode(1), active(false), insChanged(true), freqChanged(false), keyOn(false), keyOff(false), portaPause(false), inPorta(false), vol(0), outVol(15), pan(255) {} + Channel(): freqH(0), freqL(0), freq(0), baseFreq(0), pitch(0), pitch2(0), note(0), ins(-1), psgMode(1), active(false), insChanged(true), freqChanged(false), keyOn(false), keyOff(false), portaPause(false), inPorta(false), vol(0), outVol(15), pan(255) {} }; Channel chan[6]; bool isMuted[6]; diff --git a/src/engine/platform/segapcm.h b/src/engine/platform/segapcm.h index 3a68a34b3..01e56b6bb 100644 --- a/src/engine/platform/segapcm.h +++ b/src/engine/platform/segapcm.h @@ -29,7 +29,7 @@ class DivPlatformSegaPCM: public DivDispatch { struct Channel { DivMacroInt std; unsigned char freqH, freqL; - int freq, baseFreq, pitch, note, ins; + int freq, baseFreq, pitch, pitch2, note, ins; signed char konCycles; bool active, insChanged, freqChanged, keyOn, keyOff, inPorta, portaPause, furnacePCM; int vol, outVol; @@ -42,7 +42,7 @@ class DivPlatformSegaPCM: public DivDispatch { unsigned char freq; PCMChannel(): sample(-1), pos(0), len(0), freq(0) {} } pcm; - Channel(): freqH(0), freqL(0), freq(0), baseFreq(0), pitch(0), note(0), ins(-1), active(false), insChanged(true), freqChanged(false), keyOn(false), keyOff(false), inPorta(false), portaPause(false), furnacePCM(false), vol(0), outVol(0), chVolL(127), chVolR(127) {} + Channel(): freqH(0), freqL(0), freq(0), baseFreq(0), pitch(0), pitch2(0), note(0), ins(-1), active(false), insChanged(true), freqChanged(false), keyOn(false), keyOff(false), inPorta(false), portaPause(false), furnacePCM(false), vol(0), outVol(0), chVolL(127), chVolR(127) {} }; Channel chan[16]; struct QueuedWrite { diff --git a/src/engine/platform/sms.h b/src/engine/platform/sms.h index 66fdda857..3342dc987 100644 --- a/src/engine/platform/sms.h +++ b/src/engine/platform/sms.h @@ -26,7 +26,7 @@ class DivPlatformSMS: public DivDispatch { struct Channel { - int freq, baseFreq, pitch, note, actualNote, ins; + int freq, baseFreq, pitch, pitch2, note, actualNote, ins; bool active, insChanged, freqChanged, keyOn, keyOff, inPorta; signed char vol, outVol; DivMacroInt std; @@ -34,6 +34,7 @@ class DivPlatformSMS: public DivDispatch { freq(0), baseFreq(0), pitch(0), + pitch2(0), note(0), actualNote(0), ins(-1), diff --git a/src/engine/platform/su.h b/src/engine/platform/su.h index 6291d4531..935e62ea8 100644 --- a/src/engine/platform/su.h +++ b/src/engine/platform/su.h @@ -27,7 +27,7 @@ class DivPlatformSoundUnit: public DivDispatch { struct Channel { - int freq, baseFreq, pitch, note; + int freq, baseFreq, pitch, pitch2, note; int ins, cutoff, res, control; signed char pan; unsigned char duty; @@ -39,6 +39,7 @@ class DivPlatformSoundUnit: public DivDispatch { freq(0), baseFreq(0), pitch(0), + pitch2(0), note(0), ins(-1), cutoff(65535), diff --git a/src/engine/platform/swan.h b/src/engine/platform/swan.h index fdad150b2..8997e28d2 100644 --- a/src/engine/platform/swan.h +++ b/src/engine/platform/swan.h @@ -28,7 +28,7 @@ class DivPlatformSwan: public DivDispatch { struct Channel { - int freq, baseFreq, pitch, note, ins; + int freq, baseFreq, pitch, pitch2, note, ins; unsigned char pan; bool active, insChanged, freqChanged, keyOn, keyOff, inPorta; int vol, outVol, wave; @@ -38,6 +38,7 @@ class DivPlatformSwan: public DivDispatch { freq(0), baseFreq(0), pitch(0), + pitch2(0), note(0), ins(-1), pan(255), diff --git a/src/engine/platform/tia.h b/src/engine/platform/tia.h index 602cc6f1e..cc59fc18e 100644 --- a/src/engine/platform/tia.h +++ b/src/engine/platform/tia.h @@ -27,13 +27,13 @@ class DivPlatformTIA: public DivDispatch { protected: struct Channel { - int freq, baseFreq, pitch, note, ins; + int freq, baseFreq, pitch, pitch2, note, ins; unsigned char shape; signed char konCycles; bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, inPorta; int vol, outVol; DivMacroInt std; - Channel(): freq(0), baseFreq(0), pitch(0), note(0), ins(-1), shape(4), active(false), insChanged(true), freqChanged(false), keyOn(false), keyOff(false), portaPause(false), inPorta(false), vol(0), outVol(15) {} + Channel(): freq(0), baseFreq(0), pitch(0), pitch2(0), note(0), ins(-1), shape(4), active(false), insChanged(true), freqChanged(false), keyOn(false), keyOff(false), portaPause(false), inPorta(false), vol(0), outVol(15) {} }; Channel chan[2]; bool isMuted[2]; diff --git a/src/engine/platform/tx81z.h b/src/engine/platform/tx81z.h index d9261ebd7..e8bda6a3d 100644 --- a/src/engine/platform/tx81z.h +++ b/src/engine/platform/tx81z.h @@ -35,7 +35,7 @@ class DivPlatformTX81Z: public DivDispatch { DivInstrumentFM state; DivMacroInt std; unsigned char freqH, freqL; - int freq, baseFreq, pitch, note, ins; + int freq, baseFreq, pitch, pitch2, note, ins; signed char konCycles; bool active, insChanged, freqChanged, keyOn, keyOff, inPorta, portaPause, furnacePCM, hardReset; int vol, outVol; @@ -46,6 +46,7 @@ class DivPlatformTX81Z: public DivDispatch { freq(0), baseFreq(0), pitch(0), + pitch2(0), note(0), ins(-1), active(false), diff --git a/src/engine/platform/vera.h b/src/engine/platform/vera.h index 64dee5c78..b40598b33 100644 --- a/src/engine/platform/vera.h +++ b/src/engine/platform/vera.h @@ -29,7 +29,7 @@ struct VERA_PCM; class DivPlatformVERA: public DivDispatch { protected: struct Channel { - int freq, baseFreq, pitch, note, ins; + int freq, baseFreq, pitch, pitch2, note, ins; unsigned char pan; bool active, freqChanged, inPorta; int vol, outVol; @@ -45,7 +45,8 @@ class DivPlatformVERA: public DivDispatch { bool depth16; PCMChannel(): sample(-1), pos(0), len(0), freq(0), depth16(false) {} } pcm; - Channel(): freq(0), baseFreq(0), pitch(0), note(0), ins(-1), pan(0), active(false), freqChanged(false), inPorta(false), vol(0), outVol(0), accum(0), noiseval(0) {} + // somebody please split this into multiple lines! + Channel(): freq(0), baseFreq(0), pitch(0), pitch2(0), note(0), ins(-1), pan(0), active(false), freqChanged(false), inPorta(false), vol(0), outVol(0), accum(0), noiseval(0) {} }; Channel chan[17]; bool isMuted[17]; diff --git a/src/engine/platform/vic20.h b/src/engine/platform/vic20.h index 8c15c35d9..0db37c5bf 100644 --- a/src/engine/platform/vic20.h +++ b/src/engine/platform/vic20.h @@ -27,7 +27,7 @@ class DivPlatformVIC20: public DivDispatch { struct Channel { - int freq, baseFreq, pitch, note, ins; + int freq, baseFreq, pitch, pitch2, note, ins; unsigned char pan; bool active, insChanged, freqChanged, keyOn, keyOff, inPorta; int vol, outVol, wave, waveWriteCycle; @@ -36,6 +36,7 @@ class DivPlatformVIC20: public DivDispatch { freq(0), baseFreq(0), pitch(0), + pitch2(0), note(0), ins(-1), pan(255), diff --git a/src/engine/platform/vrc6.h b/src/engine/platform/vrc6.h index a44e9d336..45dcb5c5f 100644 --- a/src/engine/platform/vrc6.h +++ b/src/engine/platform/vrc6.h @@ -28,7 +28,7 @@ class DivPlatformVRC6: public DivDispatch { struct Channel { - int freq, baseFreq, pitch, note; + int freq, baseFreq, pitch, pitch2, note; int dacPeriod, dacRate, dacOut; unsigned int dacPos; int dacSample, ins; @@ -40,6 +40,7 @@ class DivPlatformVRC6: public DivDispatch { freq(0), baseFreq(0), pitch(0), + pitch2(0), note(0), dacPeriod(0), dacRate(0), diff --git a/src/engine/platform/x1_010.h b/src/engine/platform/x1_010.h index bee546473..cceb61173 100644 --- a/src/engine/platform/x1_010.h +++ b/src/engine/platform/x1_010.h @@ -79,7 +79,7 @@ class DivPlatformX1_010: public DivDispatch { slide(0), slidefrac(0) {} }; - int freq, baseFreq, pitch, note; + int freq, baseFreq, pitch, pitch2, note; int wave, sample, ins; unsigned char pan, autoEnvNum, autoEnvDen; bool active, insChanged, envChanged, freqChanged, keyOn, keyOff, inPorta, furnacePCM, pcm; @@ -89,7 +89,7 @@ class DivPlatformX1_010: public DivDispatch { DivMacroInt std; DivWaveSynth ws; void reset() { - freq = baseFreq = pitch = note = 0; + freq = baseFreq = pitch = pitch2 = note = 0; wave = sample = ins = -1; pan = 255; autoEnvNum = autoEnvDen = 0; @@ -100,7 +100,7 @@ class DivPlatformX1_010: public DivDispatch { waveBank = 0; } Channel(): - freq(0), baseFreq(0), pitch(0), note(0), + freq(0), baseFreq(0), pitch(0), pitch2(0), note(0), wave(-1), sample(-1), ins(-1), pan(255), autoEnvNum(0), autoEnvDen(0), active(false), insChanged(true), envChanged(true), freqChanged(false), keyOn(false), keyOff(false), inPorta(false), furnacePCM(false), pcm(false), diff --git a/src/engine/platform/ym2610.h b/src/engine/platform/ym2610.h index af947199d..ec1b2497a 100644 --- a/src/engine/platform/ym2610.h +++ b/src/engine/platform/ym2610.h @@ -43,7 +43,7 @@ class DivPlatformYM2610: public DivDispatch { struct Channel { DivInstrumentFM state; unsigned char freqH, freqL; - int freq, baseFreq, pitch, note, ins; + int freq, baseFreq, pitch, pitch2, note, ins; unsigned char psgMode, autoEnvNum, autoEnvDen; signed char konCycles; bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, inPorta, furnacePCM, hardReset; @@ -57,6 +57,7 @@ class DivPlatformYM2610: public DivDispatch { freq(0), baseFreq(0), pitch(0), + pitch2(0), note(0), ins(-1), psgMode(1), diff --git a/src/engine/platform/ym2610b.h b/src/engine/platform/ym2610b.h index d2363ed14..0ce1e0f7f 100644 --- a/src/engine/platform/ym2610b.h +++ b/src/engine/platform/ym2610b.h @@ -35,7 +35,7 @@ class DivPlatformYM2610B: public DivDispatch { struct Channel { DivInstrumentFM state; unsigned char freqH, freqL; - int freq, baseFreq, pitch, note, ins; + int freq, baseFreq, pitch, pitch2, note, ins; unsigned char psgMode, autoEnvNum, autoEnvDen; signed char konCycles; bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, inPorta, furnacePCM, hardReset; @@ -49,6 +49,7 @@ class DivPlatformYM2610B: public DivDispatch { freq(0), baseFreq(0), pitch(0), + pitch2(0), note(0), ins(-1), psgMode(1), diff --git a/src/engine/platform/ym2610bext.h b/src/engine/platform/ym2610bext.h index c2cd4fb37..5dd98c87e 100644 --- a/src/engine/platform/ym2610bext.h +++ b/src/engine/platform/ym2610bext.h @@ -25,12 +25,12 @@ class DivPlatformYM2610BExt: public DivPlatformYM2610B { struct OpChannel { DivMacroInt std; unsigned char freqH, freqL; - int freq, baseFreq, pitch, ins; + int freq, baseFreq, pitch, pitch2, ins; signed char konCycles; bool active, insChanged, freqChanged, keyOn, keyOff, portaPause; int vol; unsigned char pan; - OpChannel(): freqH(0), freqL(0), freq(0), baseFreq(0), pitch(0), ins(-1), active(false), insChanged(true), freqChanged(false), keyOn(false), keyOff(false), portaPause(false), vol(0), pan(3) {} + OpChannel(): freqH(0), freqL(0), freq(0), baseFreq(0), pitch(0), pitch2(0), ins(-1), active(false), insChanged(true), freqChanged(false), keyOn(false), keyOff(false), portaPause(false), vol(0), pan(3) {} }; OpChannel opChan[4]; bool isOpMuted[4]; diff --git a/src/engine/platform/ym2610ext.h b/src/engine/platform/ym2610ext.h index b89229120..0ff45981e 100644 --- a/src/engine/platform/ym2610ext.h +++ b/src/engine/platform/ym2610ext.h @@ -25,12 +25,12 @@ class DivPlatformYM2610Ext: public DivPlatformYM2610 { struct OpChannel { DivMacroInt std; unsigned char freqH, freqL; - int freq, baseFreq, pitch, ins; + int freq, baseFreq, pitch, pitch2, ins; signed char konCycles; bool active, insChanged, freqChanged, keyOn, keyOff, portaPause; int vol; unsigned char pan; - OpChannel(): freqH(0), freqL(0), freq(0), baseFreq(0), pitch(0), ins(-1), active(false), insChanged(true), freqChanged(false), keyOn(false), keyOff(false), portaPause(false), vol(0), pan(3) {} + OpChannel(): freqH(0), freqL(0), freq(0), baseFreq(0), pitch(0), pitch2(0), ins(-1), active(false), insChanged(true), freqChanged(false), keyOn(false), keyOff(false), portaPause(false), vol(0), pan(3) {} }; OpChannel opChan[4]; bool isOpMuted[4]; From d211170e86a968c3d5057b7876e7af03a14155cf Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 28 Apr 2022 01:31:16 -0500 Subject: [PATCH 228/342] implement relative pitch macro mode --- TODO.md | 3 --- src/engine/dispatch.h | 4 ++++ src/engine/platform/amiga.cpp | 14 ++++++++++---- src/engine/platform/amiga.h | 4 ++++ src/engine/platform/arcade.cpp | 10 ++++++++-- src/engine/platform/arcade.h | 4 ++++ src/engine/platform/ay.cpp | 14 ++++++++++---- src/engine/platform/ay.h | 4 ++++ src/engine/platform/ay8930.cpp | 14 ++++++++++---- src/engine/platform/ay8930.h | 4 ++++ src/engine/platform/bubsyswsg.cpp | 14 ++++++++++---- src/engine/platform/bubsyswsg.h | 4 ++++ src/engine/platform/c64.cpp | 14 ++++++++++---- src/engine/platform/c64.h | 4 ++++ src/engine/platform/fds.cpp | 14 ++++++++++---- src/engine/platform/fds.h | 4 ++++ src/engine/platform/gb.cpp | 14 ++++++++++---- src/engine/platform/gb.h | 4 ++++ src/engine/platform/genesis.cpp | 12 +++++++++--- src/engine/platform/genesis.h | 4 ++++ src/engine/platform/genesisext.cpp | 2 +- src/engine/platform/lynx.cpp | 16 +++++++++++----- src/engine/platform/lynx.h | 4 ++++ src/engine/platform/mmc5.cpp | 14 ++++++++++---- src/engine/platform/mmc5.h | 4 ++++ src/engine/platform/n163.cpp | 14 ++++++++++---- src/engine/platform/n163.h | 4 ++++ src/engine/platform/nes.cpp | 14 ++++++++++---- src/engine/platform/nes.h | 4 ++++ src/engine/platform/opl.cpp | 14 ++++++++++---- src/engine/platform/opl.h | 4 ++++ src/engine/platform/opll.cpp | 12 +++++++++--- src/engine/platform/opll.h | 4 ++++ src/engine/platform/pce.cpp | 16 +++++++++++----- src/engine/platform/pce.h | 4 ++++ src/engine/platform/pcspkr.cpp | 14 ++++++++++---- src/engine/platform/pcspkr.h | 4 ++++ src/engine/platform/pet.cpp | 8 ++++---- src/engine/platform/pet.h | 4 ++++ src/engine/platform/qsound.cpp | 14 ++++++++++---- src/engine/platform/qsound.h | 4 ++++ src/engine/platform/saa.cpp | 14 ++++++++++---- src/engine/platform/saa.h | 4 ++++ src/engine/platform/segapcm.cpp | 16 +++++++++++----- src/engine/platform/segapcm.h | 4 ++++ src/engine/platform/sms.cpp | 18 ++++++++++++------ src/engine/platform/sms.h | 4 ++++ src/engine/platform/su.cpp | 14 ++++++++++---- src/engine/platform/su.h | 4 ++++ src/engine/platform/swan.cpp | 16 +++++++++++----- src/engine/platform/swan.h | 4 ++++ src/engine/platform/tia.cpp | 18 ++++++++++++------ src/engine/platform/tia.h | 4 ++++ src/engine/platform/tx81z.cpp | 10 ++++++++-- src/engine/platform/tx81z.h | 4 ++++ src/engine/platform/vera.cpp | 16 +++++++++++----- src/engine/platform/vera.h | 4 ++++ src/engine/platform/vic20.cpp | 14 ++++++++++---- src/engine/platform/vic20.h | 4 ++++ src/engine/platform/vrc6.cpp | 18 ++++++++++++------ src/engine/platform/vrc6.h | 4 ++++ src/engine/platform/x1_010.cpp | 20 +++++++++++++------- src/engine/platform/x1_010.h | 4 ++++ src/engine/platform/ym2610.cpp | 20 +++++++++++++------- src/engine/platform/ym2610.h | 4 ++++ src/engine/platform/ym2610b.cpp | 20 +++++++++++++------- src/engine/platform/ym2610b.h | 4 ++++ src/engine/platform/ym2610bext.cpp | 2 +- src/engine/platform/ym2610ext.cpp | 2 +- src/gui/insEdit.cpp | 16 ++++++---------- 70 files changed, 469 insertions(+), 158 deletions(-) diff --git a/TODO.md b/TODO.md index 88e3833c4..341d66dde 100644 --- a/TODO.md +++ b/TODO.md @@ -2,9 +2,6 @@ - panning macro - QSound? -- pitch macro - - relative mode - - test - piano/input pad - note input via piano - input pad diff --git a/src/engine/dispatch.h b/src/engine/dispatch.h index 96984b678..1ab4cba15 100644 --- a/src/engine/dispatch.h +++ b/src/engine/dispatch.h @@ -457,4 +457,8 @@ class DivDispatch { #define COLOR_NTSC (315000000.0/88.0) #define COLOR_PAL (283.75*15625.0+25.0) +#define CLAMP_VAR(x,xMin,xMax) \ + if (xxMax) x=xMax; + #endif diff --git a/src/engine/platform/amiga.cpp b/src/engine/platform/amiga.cpp index ac76e193a..406b3d406 100644 --- a/src/engine/platform/amiga.cpp +++ b/src/engine/platform/amiga.cpp @@ -198,6 +198,12 @@ void DivPlatformAmiga::tick(bool sysTick) { chan[i].ws.tick(); } if (chan[i].std.pitch.had) { + if (chan[i].std.pitch.mode) { + chan[i].pitch2+=chan[i].std.pitch.val; + CLAMP_VAR(chan[i].pitch2,-2048,2048); + } else { + chan[i].pitch2=chan[i].std.pitch.val; + } chan[i].freqChanged=true; } if (chan[i].std.phaseReset.had) { @@ -207,7 +213,7 @@ void DivPlatformAmiga::tick(bool sysTick) { } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { //DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_AMIGA); - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].std.pitch.val); + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].pitch2); if (chan[i].freq>4095) chan[i].freq=4095; if (chan[i].freq<0) chan[i].freq=0; if (chan[i].keyOn) { @@ -266,7 +272,7 @@ int DivPlatformAmiga::dispatch(DivCommand c) { } chan[c.chan].active=true; chan[c.chan].keyOn=true; - chan[c.chan].std.init(ins); + chan[c.chan].macroInit(ins); if (chan[c.chan].useWave) { chan[c.chan].ws.init(ins,chan[c.chan].audLen<<1,255,chan[c.chan].insChanged); } @@ -277,7 +283,7 @@ int DivPlatformAmiga::dispatch(DivCommand c) { chan[c.chan].sample=-1; chan[c.chan].active=false; chan[c.chan].keyOff=true; - chan[c.chan].std.init(NULL); + chan[c.chan].macroInit(NULL); break; case DIV_CMD_NOTE_OFF_ENV: case DIV_CMD_ENV_RELEASE: @@ -364,7 +370,7 @@ int DivPlatformAmiga::dispatch(DivCommand c) { } case DIV_CMD_PRE_PORTA: if (chan[c.chan].active && c.value2) { - if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA)); + if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA)); } chan[c.chan].inPorta=c.value; break; diff --git a/src/engine/platform/amiga.h b/src/engine/platform/amiga.h index 7a7f0d242..0777100d7 100644 --- a/src/engine/platform/amiga.h +++ b/src/engine/platform/amiga.h @@ -41,6 +41,10 @@ class DivPlatformAmiga: public DivDispatch { signed char vol, outVol; DivMacroInt std; DivWaveSynth ws; + void macroInit(DivInstrument* which) { + std.init(which); + pitch2=0; + } Channel(): freq(0), baseFreq(0), diff --git a/src/engine/platform/arcade.cpp b/src/engine/platform/arcade.cpp index 6be50b6e4..f993a400d 100644 --- a/src/engine/platform/arcade.cpp +++ b/src/engine/platform/arcade.cpp @@ -275,6 +275,12 @@ void DivPlatformArcade::tick(bool sysTick) { } if (chan[i].std.pitch.had) { + if (chan[i].std.pitch.mode) { + chan[i].pitch2+=chan[i].std.pitch.val; + CLAMP_VAR(chan[i].pitch2,-2048,2048); + } else { + chan[i].pitch2=chan[i].std.pitch.val; + } chan[i].freqChanged=true; } @@ -420,7 +426,7 @@ void DivPlatformArcade::tick(bool sysTick) { for (int i=0; i<8; i++) { if (chan[i].freqChanged) { - chan[i].freq=chan[i].baseFreq+(chan[i].pitch>>1)-64+chan[i].std.pitch.val; + chan[i].freq=chan[i].baseFreq+(chan[i].pitch>>1)-64+chan[i].pitch2; if (chan[i].freq<0) chan[i].freq=0; if (chan[i].freq>=(95<<6)) chan[i].freq=(95<<6)-1; immWrite(i+0x28,hScale(chan[i].freq>>6)); @@ -452,7 +458,7 @@ int DivPlatformArcade::dispatch(DivCommand c) { chan[c.chan].state=ins->fm; } - chan[c.chan].std.init(ins); + chan[c.chan].macroInit(ins); if (!chan[c.chan].std.vol.will) { chan[c.chan].outVol=chan[c.chan].vol; } diff --git a/src/engine/platform/arcade.h b/src/engine/platform/arcade.h index 07a610f72..68f6baad3 100644 --- a/src/engine/platform/arcade.h +++ b/src/engine/platform/arcade.h @@ -42,6 +42,10 @@ class DivPlatformArcade: public DivDispatch { bool active, insChanged, freqChanged, keyOn, keyOff, inPorta, portaPause, furnacePCM, hardReset; int vol, outVol; unsigned char chVolL, chVolR; + void macroInit(DivInstrument* which) { + std.init(which); + pitch2=0; + } Channel(): freqH(0), freqL(0), diff --git a/src/engine/platform/ay.cpp b/src/engine/platform/ay.cpp index fc2a36ab6..12ea0f64a 100644 --- a/src/engine/platform/ay.cpp +++ b/src/engine/platform/ay.cpp @@ -216,6 +216,12 @@ void DivPlatformAY8910::tick(bool sysTick) { } } if (chan[i].std.pitch.had) { + if (chan[i].std.pitch.mode) { + chan[i].pitch2+=chan[i].std.pitch.val; + CLAMP_VAR(chan[i].pitch2,-2048,2048); + } else { + chan[i].pitch2=chan[i].std.pitch.val; + } chan[i].freqChanged=true; } if (chan[i].std.phaseReset.had) { @@ -239,7 +245,7 @@ void DivPlatformAY8910::tick(bool sysTick) { if (!chan[i].std.ex3.will) chan[i].autoEnvNum=1; } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].std.pitch.val); + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].pitch2); if (chan[i].freq>4095) chan[i].freq=4095; if (chan[i].keyOn) { //rWrite(16+i*5+1,((chan[i].duty&3)<<6)|(63-(ins->gb.soundLen&63))); @@ -302,7 +308,7 @@ int DivPlatformAY8910::dispatch(DivCommand c) { } chan[c.chan].active=true; chan[c.chan].keyOn=true; - chan[c.chan].std.init(ins); + chan[c.chan].macroInit(ins); if (isMuted[c.chan]) { rWrite(0x08+c.chan,0); } else if (intellivision && (chan[c.chan].psgMode&4)) { @@ -315,7 +321,7 @@ int DivPlatformAY8910::dispatch(DivCommand c) { case DIV_CMD_NOTE_OFF: chan[c.chan].keyOff=true; chan[c.chan].active=false; - chan[c.chan].std.init(NULL); + chan[c.chan].macroInit(NULL); break; case DIV_CMD_NOTE_OFF_ENV: case DIV_CMD_ENV_RELEASE: @@ -457,7 +463,7 @@ int DivPlatformAY8910::dispatch(DivCommand c) { break; case DIV_CMD_PRE_PORTA: if (chan[c.chan].active && c.value2) { - if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_AY)); + if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_AY)); } chan[c.chan].inPorta=c.value; break; diff --git a/src/engine/platform/ay.h b/src/engine/platform/ay.h index 217d11acf..2bc4a03d3 100644 --- a/src/engine/platform/ay.h +++ b/src/engine/platform/ay.h @@ -40,6 +40,10 @@ class DivPlatformAY8910: public DivDispatch { int vol, outVol; unsigned char pan; DivMacroInt std; + void macroInit(DivInstrument* which) { + std.init(which); + pitch2=0; + } Channel(): freqH(0), freqL(0), freq(0), baseFreq(0), note(0), pitch(0), pitch2(0), ins(-1), psgMode(1), autoEnvNum(0), autoEnvDen(0), active(false), insChanged(true), freqChanged(false), keyOn(false), keyOff(false), portaPause(false), inPorta(false), vol(0), outVol(15), pan(3) {} }; Channel chan[3]; diff --git a/src/engine/platform/ay8930.cpp b/src/engine/platform/ay8930.cpp index 03e99961d..e0cbd9803 100644 --- a/src/engine/platform/ay8930.cpp +++ b/src/engine/platform/ay8930.cpp @@ -227,6 +227,12 @@ void DivPlatformAY8930::tick(bool sysTick) { } } if (chan[i].std.pitch.had) { + if (chan[i].std.pitch.mode) { + chan[i].pitch2+=chan[i].std.pitch.val; + CLAMP_VAR(chan[i].pitch2,-2048,2048); + } else { + chan[i].pitch2=chan[i].std.pitch.val; + } chan[i].freqChanged=true; } if (chan[i].std.phaseReset.had) { @@ -261,7 +267,7 @@ void DivPlatformAY8930::tick(bool sysTick) { immWrite(0x1a,ayNoiseOr); } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].std.pitch.val); + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].pitch2); if (chan[i].freq>65535) chan[i].freq=65535; if (chan[i].keyOn) { if (chan[i].insChanged) { @@ -326,7 +332,7 @@ int DivPlatformAY8930::dispatch(DivCommand c) { } chan[c.chan].active=true; chan[c.chan].keyOn=true; - chan[c.chan].std.init(ins); + chan[c.chan].macroInit(ins); if (isMuted[c.chan]) { rWrite(0x08+c.chan,0); } else { @@ -337,7 +343,7 @@ int DivPlatformAY8930::dispatch(DivCommand c) { case DIV_CMD_NOTE_OFF: chan[c.chan].keyOff=true; chan[c.chan].active=false; - chan[c.chan].std.init(NULL); + chan[c.chan].macroInit(NULL); break; case DIV_CMD_NOTE_OFF_ENV: case DIV_CMD_ENV_RELEASE: @@ -482,7 +488,7 @@ int DivPlatformAY8930::dispatch(DivCommand c) { break; case DIV_CMD_PRE_PORTA: if (chan[c.chan].active && c.value2) { - if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_AY8930)); + if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_AY8930)); } chan[c.chan].inPorta=c.value; break; diff --git a/src/engine/platform/ay8930.h b/src/engine/platform/ay8930.h index 098a239bb..ced107fcf 100644 --- a/src/engine/platform/ay8930.h +++ b/src/engine/platform/ay8930.h @@ -36,6 +36,10 @@ class DivPlatformAY8930: public DivDispatch { int vol, outVol; unsigned char pan; DivMacroInt std; + void macroInit(DivInstrument* which) { + std.init(which); + pitch2=0; + } Channel(): freqH(0), freqL(0), freq(0), baseFreq(0), note(0), pitch(0), pitch2(0), ins(-1), psgMode(1), autoEnvNum(0), autoEnvDen(0), duty(4), active(false), insChanged(true), freqChanged(false), keyOn(false), keyOff(false), portaPause(false), inPorta(false), vol(0), outVol(31), pan(3) {} }; Channel chan[3]; diff --git a/src/engine/platform/bubsyswsg.cpp b/src/engine/platform/bubsyswsg.cpp index 6783bc336..3224bfee9 100644 --- a/src/engine/platform/bubsyswsg.cpp +++ b/src/engine/platform/bubsyswsg.cpp @@ -111,6 +111,12 @@ void DivPlatformBubSysWSG::tick(bool sysTick) { } } if (chan[i].std.pitch.had) { + if (chan[i].std.pitch.mode) { + chan[i].pitch2+=chan[i].std.pitch.val; + CLAMP_VAR(chan[i].pitch2,-2048,2048); + } else { + chan[i].pitch2=chan[i].std.pitch.val; + } chan[i].freqChanged=true; } if (chan[i].active) { @@ -120,7 +126,7 @@ void DivPlatformBubSysWSG::tick(bool sysTick) { } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { //DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_SCC); - chan[i].freq=0x1000-parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true)+chan[i].std.pitch.val; + chan[i].freq=0x1000-parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true)+chan[i].pitch2; if (chan[i].freq<0) chan[i].freq=0; if (chan[i].freq>4095) chan[i].freq=4095; k005289->load(i,chan[i].freq); @@ -151,7 +157,7 @@ int DivPlatformBubSysWSG::dispatch(DivCommand c) { chan[c.chan].active=true; chan[c.chan].keyOn=true; rWrite(2+c.chan,(chan[c.chan].wave<<5)|chan[c.chan].vol); - chan[c.chan].std.init(ins); + chan[c.chan].macroInit(ins); if (chan[c.chan].wave<0) { chan[c.chan].wave=0; chan[c.chan].ws.changeWave1(chan[c.chan].wave); @@ -163,7 +169,7 @@ int DivPlatformBubSysWSG::dispatch(DivCommand c) { case DIV_CMD_NOTE_OFF: chan[c.chan].active=false; chan[c.chan].keyOff=true; - chan[c.chan].std.init(NULL); + chan[c.chan].macroInit(NULL); break; case DIV_CMD_NOTE_OFF_ENV: case DIV_CMD_ENV_RELEASE: @@ -228,7 +234,7 @@ int DivPlatformBubSysWSG::dispatch(DivCommand c) { break; case DIV_CMD_PRE_PORTA: if (chan[c.chan].active && c.value2) { - if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_SCC)); + if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_SCC)); } chan[c.chan].inPorta=c.value; break; diff --git a/src/engine/platform/bubsyswsg.h b/src/engine/platform/bubsyswsg.h index 87978d71e..a28b4ac1d 100644 --- a/src/engine/platform/bubsyswsg.h +++ b/src/engine/platform/bubsyswsg.h @@ -34,6 +34,10 @@ class DivPlatformBubSysWSG: public DivDispatch { signed char waveROM[32] = {0}; // 4 bit PROM per channel on bubble system DivMacroInt std; DivWaveSynth ws; + void macroInit(DivInstrument* which) { + std.init(which); + pitch2=0; + } Channel(): freq(0), baseFreq(0), diff --git a/src/engine/platform/c64.cpp b/src/engine/platform/c64.cpp index 3515f0465..7e9cf4d18 100644 --- a/src/engine/platform/c64.cpp +++ b/src/engine/platform/c64.cpp @@ -183,6 +183,12 @@ void DivPlatformC64::tick(bool sysTick) { rWrite(i*7+4,(chan[i].wave<<4)|(chan[i].test<<3)|(chan[i].ring<<2)|(chan[i].sync<<1)|(int)(chan[i].active)); } if (chan[i].std.pitch.had) { + if (chan[i].std.pitch.mode) { + chan[i].pitch2+=chan[i].std.pitch.val; + CLAMP_VAR(chan[i].pitch2,-2048,2048); + } else { + chan[i].pitch2=chan[i].std.pitch.val; + } chan[i].freqChanged=true; } if (chan[i].std.ex1.had) { @@ -205,7 +211,7 @@ void DivPlatformC64::tick(bool sysTick) { } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,8,chan[i].std.pitch.val); + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,8,chan[i].pitch2); if (chan[i].freq>0xffff) chan[i].freq=0xffff; if (chan[i].keyOn) { rWrite(i*7+5,(chan[i].attack<<4)|(chan[i].decay)); @@ -264,14 +270,14 @@ int DivPlatformC64::dispatch(DivCommand c) { if (chan[c.chan].insChanged) { chan[c.chan].insChanged=false; } - chan[c.chan].std.init(ins); + chan[c.chan].macroInit(ins); break; } case DIV_CMD_NOTE_OFF: chan[c.chan].active=false; chan[c.chan].keyOff=true; chan[c.chan].keyOn=false; - //chan[c.chan].std.init(NULL); + //chan[c.chan].macroInit(NULL); break; case DIV_CMD_NOTE_OFF_ENV: chan[c.chan].active=false; @@ -352,7 +358,7 @@ int DivPlatformC64::dispatch(DivCommand c) { case DIV_CMD_PRE_PORTA: if (chan[c.chan].active && c.value2) { if (parent->song.resetMacroOnPorta || !chan[c.chan].inPorta) { - chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_C64)); + chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_C64)); chan[c.chan].keyOn=true; } } diff --git a/src/engine/platform/c64.h b/src/engine/platform/c64.h index 2cb53c168..a4968fee5 100644 --- a/src/engine/platform/c64.h +++ b/src/engine/platform/c64.h @@ -33,6 +33,10 @@ class DivPlatformC64: public DivDispatch { bool resetMask, resetFilter, resetDuty, ring, sync, test; signed char vol, outVol; DivMacroInt std; + void macroInit(DivInstrument* which) { + std.init(which); + pitch2=0; + } Channel(): freq(0), baseFreq(0), diff --git a/src/engine/platform/fds.cpp b/src/engine/platform/fds.cpp index 845e72475..f1e2bfdec 100644 --- a/src/engine/platform/fds.cpp +++ b/src/engine/platform/fds.cpp @@ -146,6 +146,12 @@ void DivPlatformFDS::tick(bool sysTick) { } } if (chan[i].std.pitch.had) { + if (chan[i].std.pitch.mode) { + chan[i].pitch2+=chan[i].std.pitch.val; + CLAMP_VAR(chan[i].pitch2,-2048,2048); + } else { + chan[i].pitch2=chan[i].std.pitch.val; + } chan[i].freqChanged=true; } if (chan[i].active) { @@ -177,7 +183,7 @@ void DivPlatformFDS::tick(bool sysTick) { } } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,2,chan[i].std.pitch.val); + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,2,chan[i].pitch2); if (chan[i].freq>4095) chan[i].freq=4095; if (chan[i].freq<0) chan[i].freq=0; if (chan[i].keyOn) { @@ -241,7 +247,7 @@ int DivPlatformFDS::dispatch(DivCommand c) { } chan[c.chan].active=true; chan[c.chan].keyOn=true; - chan[c.chan].std.init(ins); + chan[c.chan].macroInit(ins); if (chan[c.chan].wave<0) { chan[c.chan].wave=0; ws.changeWave1(chan[c.chan].wave); @@ -254,7 +260,7 @@ int DivPlatformFDS::dispatch(DivCommand c) { case DIV_CMD_NOTE_OFF: chan[c.chan].active=false; chan[c.chan].keyOff=true; - chan[c.chan].std.init(NULL); + chan[c.chan].macroInit(NULL); break; case DIV_CMD_NOTE_OFF_ENV: case DIV_CMD_ENV_RELEASE: @@ -358,7 +364,7 @@ int DivPlatformFDS::dispatch(DivCommand c) { break; case DIV_CMD_PRE_PORTA: if (chan[c.chan].active && c.value2) { - if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_FDS)); + if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_FDS)); } chan[c.chan].inPorta=c.value; break; diff --git a/src/engine/platform/fds.h b/src/engine/platform/fds.h index 252452e81..ed0c77d72 100644 --- a/src/engine/platform/fds.h +++ b/src/engine/platform/fds.h @@ -32,6 +32,10 @@ class DivPlatformFDS: public DivDispatch { signed char vol, outVol, wave; signed char modTable[32]; DivMacroInt std; + void macroInit(DivInstrument* which) { + std.init(which); + pitch2=0; + } Channel(): freq(0), baseFreq(0), diff --git a/src/engine/platform/gb.cpp b/src/engine/platform/gb.cpp index ded7b493c..0cfdb960c 100644 --- a/src/engine/platform/gb.cpp +++ b/src/engine/platform/gb.cpp @@ -198,6 +198,12 @@ void DivPlatformGB::tick(bool sysTick) { rWrite(0x25,procMute()); } if (chan[i].std.pitch.had) { + if (chan[i].std.pitch.mode) { + chan[i].pitch2+=chan[i].std.pitch.val; + CLAMP_VAR(chan[i].pitch2,-2048,2048); + } else { + chan[i].pitch2=chan[i].std.pitch.val; + } chan[i].freqChanged=true; } if (chan[i].std.phaseReset.had) { @@ -227,7 +233,7 @@ void DivPlatformGB::tick(bool sysTick) { if (ntPos>255) ntPos=255; chan[i].freq=noiseTable[ntPos]; } else { - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].std.pitch.val); + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].pitch2); if (chan[i].freq>2047) chan[i].freq=2047; if (chan[i].freq<0) chan[i].freq=0; } @@ -281,7 +287,7 @@ int DivPlatformGB::dispatch(DivCommand c) { } chan[c.chan].active=true; chan[c.chan].keyOn=true; - chan[c.chan].std.init(ins); + chan[c.chan].macroInit(ins); if (c.chan==2) { if (chan[c.chan].wave<0) { chan[c.chan].wave=0; @@ -295,7 +301,7 @@ int DivPlatformGB::dispatch(DivCommand c) { case DIV_CMD_NOTE_OFF: chan[c.chan].active=false; chan[c.chan].keyOff=true; - chan[c.chan].std.init(NULL); + chan[c.chan].macroInit(NULL); break; case DIV_CMD_NOTE_OFF_ENV: case DIV_CMD_ENV_RELEASE: @@ -379,7 +385,7 @@ int DivPlatformGB::dispatch(DivCommand c) { break; case DIV_CMD_PRE_PORTA: if (chan[c.chan].active && c.value2) { - if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_GB)); + if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_GB)); } chan[c.chan].inPorta=c.value; break; diff --git a/src/engine/platform/gb.h b/src/engine/platform/gb.h index 9d1a4572b..2cdbc7858 100644 --- a/src/engine/platform/gb.h +++ b/src/engine/platform/gb.h @@ -32,6 +32,10 @@ class DivPlatformGB: public DivDispatch { bool active, insChanged, freqChanged, sweepChanged, keyOn, keyOff, inPorta; signed char vol, outVol, wave; DivMacroInt std; + void macroInit(DivInstrument* which) { + std.init(which); + pitch2=0; + } Channel(): freq(0), baseFreq(0), diff --git a/src/engine/platform/genesis.cpp b/src/engine/platform/genesis.cpp index 80c51d950..090379cf6 100644 --- a/src/engine/platform/genesis.cpp +++ b/src/engine/platform/genesis.cpp @@ -280,6 +280,12 @@ void DivPlatformGenesis::tick(bool sysTick) { } if (chan[i].std.pitch.had) { + if (chan[i].std.pitch.mode) { + chan[i].pitch2+=chan[i].std.pitch.val; + CLAMP_VAR(chan[i].pitch2,-2048,2048); + } else { + chan[i].pitch2=chan[i].std.pitch.val; + } chan[i].freqChanged=true; } @@ -410,7 +416,7 @@ void DivPlatformGenesis::tick(bool sysTick) { for (int i=0; i<6; i++) { if (i==2 && extMode) continue; if (chan[i].freqChanged) { - int fNum=parent->calcFreq(chan[i].baseFreq&0x7ff,chan[i].pitch,false,4,chan[i].std.pitch.val); + int fNum=parent->calcFreq(chan[i].baseFreq&0x7ff,chan[i].pitch,false,4,chan[i].pitch2); int block=(chan[i].baseFreq&0xf800)>>11; if (fNum<0) fNum=0; if (fNum>2047) { @@ -434,7 +440,7 @@ void DivPlatformGenesis::tick(bool sysTick) { off=(double)s->centerRate/8363.0; } } - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,4)+chan[i].std.pitch.val; + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,4)+chan[i].pitch2; dacRate=chan[i].freq*off; if (dacRate<1) dacRate=1; if (dumpWrites) addWrite(0xffff0001,dacRate); @@ -524,7 +530,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) { chan[c.chan].state=ins->fm; } - chan[c.chan].std.init(ins); + chan[c.chan].macroInit(ins); if (!chan[c.chan].std.vol.will) { chan[c.chan].outVol=chan[c.chan].vol; } diff --git a/src/engine/platform/genesis.h b/src/engine/platform/genesis.h index 13df1e8b2..0482ea0a0 100644 --- a/src/engine/platform/genesis.h +++ b/src/engine/platform/genesis.h @@ -41,6 +41,10 @@ class DivPlatformGenesis: public DivDispatch { bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, furnaceDac, inPorta, hardReset; int vol, outVol; unsigned char pan; + void macroInit(DivInstrument* which) { + std.init(which); + pitch2=0; + } Channel(): freqH(0), freqL(0), diff --git a/src/engine/platform/genesisext.cpp b/src/engine/platform/genesisext.cpp index d77857a66..e5d964e5d 100644 --- a/src/engine/platform/genesisext.cpp +++ b/src/engine/platform/genesisext.cpp @@ -307,7 +307,7 @@ void DivPlatformGenesisExt::tick(bool sysTick) { unsigned char writeMask=2; if (extMode) for (int i=0; i<4; i++) { if (opChan[i].freqChanged) { - int fNum=parent->calcFreq(opChan[i].baseFreq&0x7ff,opChan[i].pitch,false,4,opChan[i].std.pitch.val); + int fNum=parent->calcFreq(opChan[i].baseFreq&0x7ff,opChan[i].pitch,false,4,opChan[i].pitch2); int block=(opChan[i].baseFreq&0xf800)>>11; if (fNum<0) fNum=0; if (fNum>2047) { diff --git a/src/engine/platform/lynx.cpp b/src/engine/platform/lynx.cpp index f70198b6e..9a1f4d3d2 100644 --- a/src/engine/platform/lynx.cpp +++ b/src/engine/platform/lynx.cpp @@ -186,6 +186,12 @@ void DivPlatformLynx::tick(bool sysTick) { } if (chan[i].std.pitch.had) { + if (chan[i].std.pitch.mode) { + chan[i].pitch2+=chan[i].std.pitch.val; + CLAMP_VAR(chan[i].pitch2,-2048,2048); + } else { + chan[i].pitch2=chan[i].std.pitch.val; + } chan[i].freqChanged=true; } @@ -195,7 +201,7 @@ void DivPlatformLynx::tick(bool sysTick) { WRITE_OTHER(i, ((chan[i].lfsr&0xf00)>>4)); chan[i].lfsr=-1; } - chan[i].fd=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].std.pitch.val); + chan[i].fd=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].pitch2); if (chan[i].std.duty.had) { chan[i].duty=chan[i].std.duty.val; WRITE_FEEDBACK(i, chan[i].duty.feedback); @@ -224,12 +230,12 @@ int DivPlatformLynx::dispatch(DivCommand c) { } chan[c.chan].active=true; WRITE_VOLUME(c.chan,(isMuted[c.chan]?0:(chan[c.chan].vol&127))); - chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_MIKEY)); + chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_MIKEY)); break; case DIV_CMD_NOTE_OFF: chan[c.chan].active=false; WRITE_VOLUME(c.chan, 0); - chan[c.chan].std.init(NULL); + chan[c.chan].macroInit(NULL); break; case DIV_CMD_LYNX_LFSR_LOAD: chan[c.chan].freqChanged=true; @@ -241,7 +247,7 @@ int DivPlatformLynx::dispatch(DivCommand c) { break; case DIV_CMD_INSTRUMENT: chan[c.chan].ins=c.value; - //chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_MIKEY)); + //chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_MIKEY)); break; case DIV_CMD_VOLUME: if (chan[c.chan].vol!=c.value) { @@ -297,7 +303,7 @@ int DivPlatformLynx::dispatch(DivCommand c) { break; case DIV_CMD_PRE_PORTA: if (chan[c.chan].active && c.value2) { - if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_MIKEY)); + if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_MIKEY)); } chan[c.chan].inPorta=c.value; break; diff --git a/src/engine/platform/lynx.h b/src/engine/platform/lynx.h index 1093bad8a..5200fbd9c 100644 --- a/src/engine/platform/lynx.h +++ b/src/engine/platform/lynx.h @@ -48,6 +48,10 @@ class DivPlatformLynx: public DivDispatch { unsigned char pan; bool active, insChanged, freqChanged, keyOn, keyOff, inPorta; signed char vol, outVol; + void macroInit(DivInstrument* which) { + std.init(which); + pitch2=0; + } Channel(): std(), fd(0), diff --git a/src/engine/platform/mmc5.cpp b/src/engine/platform/mmc5.cpp index dc8c15063..e2aa915ed 100644 --- a/src/engine/platform/mmc5.cpp +++ b/src/engine/platform/mmc5.cpp @@ -124,6 +124,12 @@ void DivPlatformMMC5::tick(bool sysTick) { rWrite(0x5000+i*4,0x30|chan[i].outVol|((chan[i].duty&3)<<6)); } if (chan[i].std.pitch.had) { + if (chan[i].std.pitch.mode) { + chan[i].pitch2+=chan[i].std.pitch.val; + CLAMP_VAR(chan[i].pitch2,-2048,2048); + } else { + chan[i].pitch2=chan[i].std.pitch.val; + } chan[i].freqChanged=true; } if (chan[i].std.phaseReset.had) { @@ -133,7 +139,7 @@ void DivPlatformMMC5::tick(bool sysTick) { } } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].std.pitch.val)-1; + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].pitch2)-1; if (chan[i].freq>2047) chan[i].freq=2047; if (chan[i].freq<0) chan[i].freq=0; if (chan[i].keyOn) { @@ -227,7 +233,7 @@ int DivPlatformMMC5::dispatch(DivCommand c) { } chan[c.chan].active=true; chan[c.chan].keyOn=true; - chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_STD)); + chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_STD)); rWrite(0x5000+c.chan*4,0x30|chan[c.chan].vol|((chan[c.chan].duty&3)<<6)); break; case DIV_CMD_NOTE_OFF: @@ -237,7 +243,7 @@ int DivPlatformMMC5::dispatch(DivCommand c) { } chan[c.chan].active=false; chan[c.chan].keyOff=true; - chan[c.chan].std.init(NULL); + chan[c.chan].macroInit(NULL); break; case DIV_CMD_NOTE_OFF_ENV: case DIV_CMD_ENV_RELEASE: @@ -305,7 +311,7 @@ int DivPlatformMMC5::dispatch(DivCommand c) { break; case DIV_CMD_PRE_PORTA: if (chan[c.chan].active && c.value2) { - if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_STD)); + if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_STD)); } chan[c.chan].inPorta=c.value; break; diff --git a/src/engine/platform/mmc5.h b/src/engine/platform/mmc5.h index 7db844a06..bf7eeaad5 100644 --- a/src/engine/platform/mmc5.h +++ b/src/engine/platform/mmc5.h @@ -30,6 +30,10 @@ class DivPlatformMMC5: public DivDispatch { bool active, insChanged, freqChanged, sweepChanged, keyOn, keyOff, inPorta, furnaceDac; signed char vol, outVol, wave; DivMacroInt std; + void macroInit(DivInstrument* which) { + std.init(which); + pitch2=0; + } Channel(): freq(0), baseFreq(0), diff --git a/src/engine/platform/n163.cpp b/src/engine/platform/n163.cpp index 34515305e..5d3c9c201 100644 --- a/src/engine/platform/n163.cpp +++ b/src/engine/platform/n163.cpp @@ -262,6 +262,12 @@ void DivPlatformN163::tick(bool sysTick) { } } if (chan[i].std.pitch.had) { + if (chan[i].std.pitch.mode) { + chan[i].pitch2+=chan[i].std.pitch.val; + CLAMP_VAR(chan[i].pitch2,-2048,2048); + } else { + chan[i].pitch2=chan[i].std.pitch.val; + } chan[i].freqChanged=true; } if (chan[i].std.ex1.had) { @@ -347,7 +353,7 @@ void DivPlatformN163::tick(bool sysTick) { chan[i].waveUpdated=false; } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { - chan[i].freq=parent->calcFreq((((chan[i].baseFreq*chan[i].waveLen)*(chanMax+1))/16),chan[i].pitch,false,0,chan[i].std.pitch.val); + chan[i].freq=parent->calcFreq((((chan[i].baseFreq*chan[i].waveLen)*(chanMax+1))/16),chan[i].pitch,false,0,chan[i].pitch2); if (chan[i].freq<0) chan[i].freq=0; if (chan[i].freq>0x3ffff) chan[i].freq=0x3ffff; if (chan[i].keyOn) { @@ -398,7 +404,7 @@ int DivPlatformN163::dispatch(DivCommand c) { if (!isMuted[c.chan]) { chan[c.chan].volumeChanged=true; } - chan[c.chan].std.init(ins); + chan[c.chan].macroInit(ins); chan[c.chan].ws.init(ins,chan[c.chan].waveLen,15,chan[c.chan].insChanged); break; } @@ -406,7 +412,7 @@ int DivPlatformN163::dispatch(DivCommand c) { chan[c.chan].active=false; chan[c.chan].keyOff=true; chan[c.chan].keyOn=false; - //chan[c.chan].std.init(NULL); + //chan[c.chan].macroInit(NULL); break; case DIV_CMD_NOTE_OFF_ENV: chan[c.chan].active=false; @@ -546,7 +552,7 @@ int DivPlatformN163::dispatch(DivCommand c) { case DIV_CMD_PRE_PORTA: if (chan[c.chan].active && c.value2) { if (parent->song.resetMacroOnPorta) { - chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_N163)); + chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_N163)); chan[c.chan].keyOn=true; } } diff --git a/src/engine/platform/n163.h b/src/engine/platform/n163.h index 0b4edbdd8..d01030848 100644 --- a/src/engine/platform/n163.h +++ b/src/engine/platform/n163.h @@ -37,6 +37,10 @@ class DivPlatformN163: public DivDispatch { signed char vol, outVol, resVol; DivMacroInt std; DivWaveSynth ws; + void macroInit(DivInstrument* which) { + std.init(which); + pitch2=0; + } Channel(): freq(0), baseFreq(0), diff --git a/src/engine/platform/nes.cpp b/src/engine/platform/nes.cpp index c54127874..b61abde0d 100644 --- a/src/engine/platform/nes.cpp +++ b/src/engine/platform/nes.cpp @@ -196,6 +196,12 @@ void DivPlatformNES::tick(bool sysTick) { } } if (chan[i].std.pitch.had) { + if (chan[i].std.pitch.mode) { + chan[i].pitch2+=chan[i].std.pitch.val; + CLAMP_VAR(chan[i].pitch2,-2048,2048); + } else { + chan[i].pitch2=chan[i].std.pitch.val; + } chan[i].freqChanged=true; } if (chan[i].sweepChanged) { @@ -217,7 +223,7 @@ void DivPlatformNES::tick(bool sysTick) { if (ntPos>252) ntPos=252; chan[i].freq=(parent->song.properNoiseLayout)?(15-(chan[i].baseFreq&15)):(noiseTable[ntPos]); } else { - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].std.pitch.val)-1; + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].pitch2)-1; if (chan[i].freq>2047) chan[i].freq=2047; if (chan[i].freq<0) chan[i].freq=0; } @@ -325,7 +331,7 @@ int DivPlatformNES::dispatch(DivCommand c) { } chan[c.chan].active=true; chan[c.chan].keyOn=true; - chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_STD)); + chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_STD)); if (c.chan==2) { rWrite(0x4000+c.chan*4,0xff); } else { @@ -339,7 +345,7 @@ int DivPlatformNES::dispatch(DivCommand c) { } chan[c.chan].active=false; chan[c.chan].keyOff=true; - chan[c.chan].std.init(NULL); + chan[c.chan].macroInit(NULL); break; case DIV_CMD_NOTE_OFF_ENV: case DIV_CMD_ENV_RELEASE: @@ -428,7 +434,7 @@ int DivPlatformNES::dispatch(DivCommand c) { break; case DIV_CMD_PRE_PORTA: if (chan[c.chan].active && c.value2) { - if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_STD)); + if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_STD)); } chan[c.chan].inPorta=c.value; break; diff --git a/src/engine/platform/nes.h b/src/engine/platform/nes.h index 8de7f7b57..d88b447f4 100644 --- a/src/engine/platform/nes.h +++ b/src/engine/platform/nes.h @@ -30,6 +30,10 @@ class DivPlatformNES: public DivDispatch { bool active, insChanged, freqChanged, sweepChanged, keyOn, keyOff, inPorta, furnaceDac; signed char vol, outVol, wave; DivMacroInt std; + void macroInit(DivInstrument* which) { + std.init(which); + pitch2=0; + } Channel(): freq(0), baseFreq(0), diff --git a/src/engine/platform/opl.cpp b/src/engine/platform/opl.cpp index 1e727fa66..c127ce774 100644 --- a/src/engine/platform/opl.cpp +++ b/src/engine/platform/opl.cpp @@ -274,6 +274,12 @@ void DivPlatformOPL::tick(bool sysTick) { } if (chan[i].std.pitch.had) { + if (chan[i].std.pitch.mode) { + chan[i].pitch2+=chan[i].std.pitch.val; + CLAMP_VAR(chan[i].pitch2,-2048,2048); + } else { + chan[i].pitch2=chan[i].std.pitch.val; + } chan[i].freqChanged=true; } @@ -417,9 +423,9 @@ void DivPlatformOPL::tick(bool sysTick) { bool updateDrums=false; for (int i=0; icalcFreq(chan[i].baseFreq,chan[i].pitch,false,octave(chan[i].baseFreq),chan[i].std.pitch.val); + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,octave(chan[i].baseFreq),chan[i].pitch2); if (chan[i].freq>131071) chan[i].freq=131071; - int freqt=toFreq(chan[i].freq)+chan[i].std.pitch.val; + int freqt=toFreq(chan[i].freq)+chan[i].pitch2; chan[i].freqH=freqt>>8; chan[i].freqL=freqt&0xff; immWrite(chanMap[i]+ADDR_FREQ,chan[i].freqL); @@ -541,7 +547,7 @@ int DivPlatformOPL::dispatch(DivCommand c) { chan[c.chan].state=ins->fm; } - chan[c.chan].std.init(ins); + chan[c.chan].macroInit(ins); if (!chan[c.chan].std.vol.will) { chan[c.chan].outVol=chan[c.chan].vol; } @@ -549,7 +555,7 @@ int DivPlatformOPL::dispatch(DivCommand c) { int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2; chan[c.chan].fourOp=(ops==4); if (chan[c.chan].fourOp) { - chan[c.chan+1].std.init(NULL); + chan[c.chan+1].macroInit(NULL); } update4OpMask=true; for (int i=0; icalcFreq(chan[i].baseFreq,chan[i].pitch,false,octave(chan[i].baseFreq),chan[i].std.pitch.val); + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,octave(chan[i].baseFreq),chan[i].pitch2); if (chan[i].freq>262143) chan[i].freq=262143; - int freqt=toFreq(chan[i].freq)+chan[i].std.pitch.val; + int freqt=toFreq(chan[i].freq)+chan[i].pitch2; chan[i].freqL=freqt&0xff; if (i>=6 && properDrums) { immWrite(0x10+drumSlot[i],freqt&0xff); @@ -370,7 +376,7 @@ int DivPlatformOPLL::dispatch(DivCommand c) { chan[c.chan].state=ins->fm; } - chan[c.chan].std.init(ins); + chan[c.chan].macroInit(ins); if (!chan[c.chan].std.vol.will) { chan[c.chan].outVol=chan[c.chan].vol; } diff --git a/src/engine/platform/opll.h b/src/engine/platform/opll.h index 8b1732aaf..96cb1a914 100644 --- a/src/engine/platform/opll.h +++ b/src/engine/platform/opll.h @@ -37,6 +37,10 @@ class DivPlatformOPLL: public DivDispatch { bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, furnaceDac, inPorta; int vol, outVol; unsigned char pan; + void macroInit(DivInstrument* which) { + std.init(which); + pitch2=0; + } Channel(): freqH(0), freqL(0), diff --git a/src/engine/platform/pce.cpp b/src/engine/platform/pce.cpp index 5178cad36..b09263fc6 100644 --- a/src/engine/platform/pce.cpp +++ b/src/engine/platform/pce.cpp @@ -208,6 +208,12 @@ void DivPlatformPCE::tick(bool sysTick) { chWrite(i,0x05,isMuted[i]?0:chan[i].pan); } if (chan[i].std.pitch.had) { + if (chan[i].std.pitch.mode) { + chan[i].pitch2+=chan[i].std.pitch.val; + CLAMP_VAR(chan[i].pitch2,-2048,2048); + } else { + chan[i].pitch2=chan[i].std.pitch.val; + } chan[i].freqChanged=true; } if (chan[i].active) { @@ -217,7 +223,7 @@ void DivPlatformPCE::tick(bool sysTick) { } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { //DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_PCE); - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].std.pitch.val); + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].pitch2); if (chan[i].furnaceDac) { double off=1.0; if (chan[i].dacSample>=0 && chan[i].dacSamplesong.sampleLen) { @@ -279,7 +285,7 @@ int DivPlatformPCE::dispatch(DivCommand c) { chan[c.chan].note=c.value; } chan[c.chan].active=true; - chan[c.chan].std.init(ins); + chan[c.chan].macroInit(ins); //chan[c.chan].keyOn=true; chan[c.chan].furnaceDac=true; } else { @@ -316,7 +322,7 @@ int DivPlatformPCE::dispatch(DivCommand c) { chan[c.chan].active=true; chan[c.chan].keyOn=true; chWrite(c.chan,0x04,0x80|chan[c.chan].vol); - chan[c.chan].std.init(ins); + chan[c.chan].macroInit(ins); if (chan[c.chan].wave<0) { chan[c.chan].wave=0; chan[c.chan].ws.changeWave1(chan[c.chan].wave); @@ -331,7 +337,7 @@ int DivPlatformPCE::dispatch(DivCommand c) { chan[c.chan].pcm=false; chan[c.chan].active=false; chan[c.chan].keyOff=true; - chan[c.chan].std.init(NULL); + chan[c.chan].macroInit(NULL); break; case DIV_CMD_NOTE_OFF_ENV: case DIV_CMD_ENV_RELEASE: @@ -429,7 +435,7 @@ int DivPlatformPCE::dispatch(DivCommand c) { break; case DIV_CMD_PRE_PORTA: if (chan[c.chan].active && c.value2) { - if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_PCE)); + if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_PCE)); } chan[c.chan].inPorta=c.value; break; diff --git a/src/engine/platform/pce.h b/src/engine/platform/pce.h index 1f92e6338..b1b140383 100644 --- a/src/engine/platform/pce.h +++ b/src/engine/platform/pce.h @@ -37,6 +37,10 @@ class DivPlatformPCE: public DivDispatch { signed char vol, outVol, wave; DivMacroInt std; DivWaveSynth ws; + void macroInit(DivInstrument* which) { + std.init(which); + pitch2=0; + } Channel(): freq(0), baseFreq(0), diff --git a/src/engine/platform/pcspkr.cpp b/src/engine/platform/pcspkr.cpp index 1a4b2ee06..5da1923cf 100644 --- a/src/engine/platform/pcspkr.cpp +++ b/src/engine/platform/pcspkr.cpp @@ -190,10 +190,16 @@ void DivPlatformPCSpeaker::tick(bool sysTick) { } } if (chan[i].std.pitch.had) { + if (chan[i].std.pitch.mode) { + chan[i].pitch2+=chan[i].std.pitch.val; + CLAMP_VAR(chan[i].pitch2,-2048,2048); + } else { + chan[i].pitch2=chan[i].std.pitch.val; + } chan[i].freqChanged=true; } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].std.pitch.val)-1; + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].pitch2)-1; if (chan[i].freq<0) chan[i].freq=0; if (chan[i].freq>65535) chan[i].freq=65535; if (chan[i].keyOn) { @@ -220,12 +226,12 @@ int DivPlatformPCSpeaker::dispatch(DivCommand c) { } chan[c.chan].active=true; chan[c.chan].keyOn=true; - chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_BEEPER)); + chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_BEEPER)); break; case DIV_CMD_NOTE_OFF: chan[c.chan].active=false; chan[c.chan].keyOff=true; - chan[c.chan].std.init(NULL); + chan[c.chan].macroInit(NULL); break; case DIV_CMD_NOTE_OFF_ENV: case DIV_CMD_ENV_RELEASE: @@ -285,7 +291,7 @@ int DivPlatformPCSpeaker::dispatch(DivCommand c) { break; case DIV_CMD_PRE_PORTA: if (chan[c.chan].active && c.value2) { - if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_BEEPER)); + if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_BEEPER)); } chan[c.chan].inPorta=c.value; break; diff --git a/src/engine/platform/pcspkr.h b/src/engine/platform/pcspkr.h index 815f2a96d..d2c01454e 100644 --- a/src/engine/platform/pcspkr.h +++ b/src/engine/platform/pcspkr.h @@ -30,6 +30,10 @@ class DivPlatformPCSpeaker: public DivDispatch { bool active, insChanged, freqChanged, sweepChanged, keyOn, keyOff, inPorta, furnaceDac; signed char vol, outVol, wave; DivMacroInt std; + void macroInit(DivInstrument* which) { + std.init(which); + pitch2=0; + } Channel(): freq(0), baseFreq(0), diff --git a/src/engine/platform/pet.cpp b/src/engine/platform/pet.cpp index eb954a6e1..5a7efe1f9 100644 --- a/src/engine/platform/pet.cpp +++ b/src/engine/platform/pet.cpp @@ -116,7 +116,7 @@ void DivPlatformPET::tick(bool sysTick) { chan.freqChanged=true; } if (chan.freqChanged || chan.keyOn || chan.keyOff) { - chan.freq=parent->calcFreq(chan.baseFreq,chan.pitch,true,0,chan.std.pitch.val); + chan.freq=parent->calcFreq(chan.baseFreq,chan.pitch,true,0,chan.pitch2); if (chan.freq>257) chan.freq=257; if (chan.freq<2) chan.freq=2; rWrite(8,chan.freq-2); @@ -146,13 +146,13 @@ int DivPlatformPET::dispatch(DivCommand c) { } chan.active=true; chan.keyOn=true; - chan.std.init(ins); + chan.macroInit(ins); break; } case DIV_CMD_NOTE_OFF: chan.active=false; chan.keyOff=true; - chan.std.init(NULL); + chan.macroInit(NULL); break; case DIV_CMD_NOTE_OFF_ENV: case DIV_CMD_ENV_RELEASE: @@ -213,7 +213,7 @@ int DivPlatformPET::dispatch(DivCommand c) { break; case DIV_CMD_PRE_PORTA: if (chan.active && c.value2) { - if (parent->song.resetMacroOnPorta) chan.std.init(parent->getIns(chan.ins,DIV_INS_PET)); + if (parent->song.resetMacroOnPorta) chan.macroInit(parent->getIns(chan.ins,DIV_INS_PET)); } chan.inPorta=c.value; break; diff --git a/src/engine/platform/pet.h b/src/engine/platform/pet.h index 567d4fc43..c26ef5392 100644 --- a/src/engine/platform/pet.h +++ b/src/engine/platform/pet.h @@ -32,6 +32,10 @@ class DivPlatformPET: public DivDispatch { int cnt; short out; DivMacroInt std; + void macroInit(DivInstrument* which) { + std.init(which); + pitch2=0; + } Channel(): freq(0), baseFreq(0), diff --git a/src/engine/platform/qsound.cpp b/src/engine/platform/qsound.cpp index fa31caa81..8450d965b 100644 --- a/src/engine/platform/qsound.cpp +++ b/src/engine/platform/qsound.cpp @@ -327,11 +327,17 @@ void DivPlatformQSound::tick(bool sysTick) { } } if (chan[i].std.pitch.had) { + if (chan[i].std.pitch.mode) { + chan[i].pitch2+=chan[i].std.pitch.val; + CLAMP_VAR(chan[i].pitch2,-2048,2048); + } else { + chan[i].pitch2=chan[i].std.pitch.val; + } chan[i].freqChanged=true; } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { //DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_AMIGA); - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,2,chan[i].std.pitch.val); + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,2,chan[i].pitch2); if (chan[i].freq>0xffff) chan[i].freq=0xffff; if (chan[i].keyOn) { rWrite(q1_reg_map[Q1V_BANK][i], qsound_bank); @@ -386,14 +392,14 @@ int DivPlatformQSound::dispatch(DivCommand c) { } chan[c.chan].active=true; chan[c.chan].keyOn=true; - chan[c.chan].std.init(ins); + chan[c.chan].macroInit(ins); break; } case DIV_CMD_NOTE_OFF: chan[c.chan].sample=-1; chan[c.chan].active=false; chan[c.chan].keyOff=true; - chan[c.chan].std.init(NULL); + chan[c.chan].macroInit(NULL); break; case DIV_CMD_NOTE_OFF_ENV: case DIV_CMD_ENV_RELEASE: @@ -487,7 +493,7 @@ int DivPlatformQSound::dispatch(DivCommand c) { } case DIV_CMD_PRE_PORTA: if (chan[c.chan].active && c.value2) { - if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA)); + if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA)); } chan[c.chan].inPorta=c.value; break; diff --git a/src/engine/platform/qsound.h b/src/engine/platform/qsound.h index 23229e142..1eb9b2751 100644 --- a/src/engine/platform/qsound.h +++ b/src/engine/platform/qsound.h @@ -36,6 +36,10 @@ class DivPlatformQSound: public DivDispatch { bool active, insChanged, freqChanged, keyOn, keyOff, inPorta, useWave; int vol, outVol; DivMacroInt std; + void macroInit(DivInstrument* which) { + std.init(which); + pitch2=0; + } Channel(): freq(0), baseFreq(0), diff --git a/src/engine/platform/saa.cpp b/src/engine/platform/saa.cpp index 2d4986b58..4d79d45bd 100644 --- a/src/engine/platform/saa.cpp +++ b/src/engine/platform/saa.cpp @@ -187,6 +187,12 @@ void DivPlatformSAA1099::tick(bool sysTick) { } } if (chan[i].std.pitch.had) { + if (chan[i].std.pitch.mode) { + chan[i].pitch2+=chan[i].std.pitch.val; + CLAMP_VAR(chan[i].pitch2,-2048,2048); + } else { + chan[i].pitch2=chan[i].std.pitch.val; + } chan[i].freqChanged=true; } if (chan[i].std.ex1.had) { @@ -194,7 +200,7 @@ void DivPlatformSAA1099::tick(bool sysTick) { rWrite(0x18+(i/3),saaEnv[i/3]); } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].std.pitch.val); + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].pitch2); if (chan[i].freq>65535) chan[i].freq=65535; if (chan[i].freq>=32768) { chan[i].freqH=7; @@ -257,7 +263,7 @@ int DivPlatformSAA1099::dispatch(DivCommand c) { } chan[c.chan].active=true; chan[c.chan].keyOn=true; - chan[c.chan].std.init(ins); + chan[c.chan].macroInit(ins); if (isMuted[c.chan]) { rWrite(c.chan,0); } else { @@ -268,7 +274,7 @@ int DivPlatformSAA1099::dispatch(DivCommand c) { case DIV_CMD_NOTE_OFF: chan[c.chan].keyOff=true; chan[c.chan].active=false; - chan[c.chan].std.init(NULL); + chan[c.chan].macroInit(NULL); break; case DIV_CMD_NOTE_OFF_ENV: case DIV_CMD_ENV_RELEASE: @@ -356,7 +362,7 @@ int DivPlatformSAA1099::dispatch(DivCommand c) { break; case DIV_CMD_PRE_PORTA: if (chan[c.chan].active && c.value2) { - if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_SAA1099)); + if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_SAA1099)); } chan[c.chan].inPorta=c.value; break; diff --git a/src/engine/platform/saa.h b/src/engine/platform/saa.h index 8546c9679..24108da79 100644 --- a/src/engine/platform/saa.h +++ b/src/engine/platform/saa.h @@ -42,6 +42,10 @@ class DivPlatformSAA1099: public DivDispatch { int vol, outVol; unsigned char pan; DivMacroInt std; + void macroInit(DivInstrument* which) { + std.init(which); + pitch2=0; + } Channel(): freqH(0), freqL(0), freq(0), baseFreq(0), pitch(0), pitch2(0), note(0), ins(-1), psgMode(1), active(false), insChanged(true), freqChanged(false), keyOn(false), keyOff(false), portaPause(false), inPorta(false), vol(0), outVol(15), pan(255) {} }; Channel chan[6]; diff --git a/src/engine/platform/segapcm.cpp b/src/engine/platform/segapcm.cpp index a7db708a2..9064fa093 100644 --- a/src/engine/platform/segapcm.cpp +++ b/src/engine/platform/segapcm.cpp @@ -116,6 +116,12 @@ void DivPlatformSegaPCM::tick(bool sysTick) { } if (chan[i].std.pitch.had) { + if (chan[i].std.pitch.mode) { + chan[i].pitch2+=chan[i].std.pitch.val; + CLAMP_VAR(chan[i].pitch2,-2048,2048); + } else { + chan[i].pitch2=chan[i].std.pitch.val; + } chan[i].freqChanged=true; } /*if (chan[i].keyOn || chan[i].keyOff) { @@ -132,7 +138,7 @@ void DivPlatformSegaPCM::tick(bool sysTick) { DivSample* s=parent->getSample(chan[i].pcm.sample); off=(double)s->centerRate/8363.0; } - chan[i].pcm.freq=MIN(255,(15625+(off*parent->song.tuning*pow(2.0,double(chan[i].freq+256)/(64.0*12.0)))*255)/31250)+chan[i].std.pitch.val; + chan[i].pcm.freq=MIN(255,(15625+(off*parent->song.tuning*pow(2.0,double(chan[i].freq+256)/(64.0*12.0)))*255)/31250)+chan[i].pitch2; if (dumpWrites) { addWrite(0x10007+(i<<3),chan[i].pcm.freq); } @@ -158,7 +164,7 @@ int DivPlatformSegaPCM::dispatch(DivCommand c) { if (dumpWrites) { addWrite(0x10086+(c.chan<<3),3); } - chan[c.chan].std.init(NULL); + chan[c.chan].macroInit(NULL); break; } chan[c.chan].pcm.pos=0; @@ -168,7 +174,7 @@ int DivPlatformSegaPCM::dispatch(DivCommand c) { chan[c.chan].freqChanged=true; } chan[c.chan].furnacePCM=true; - chan[c.chan].std.init(ins); + chan[c.chan].macroInit(ins); if (dumpWrites) { // Sega PCM writes DivSample* s=parent->getSample(chan[c.chan].pcm.sample); addWrite(0x10086+(c.chan<<3),3+((s->offSegaPCM>>16)<<3)); @@ -185,7 +191,7 @@ int DivPlatformSegaPCM::dispatch(DivCommand c) { } } } else { - chan[c.chan].std.init(NULL); + chan[c.chan].macroInit(NULL); if (c.value!=DIV_NOTE_NULL) { chan[c.chan].note=c.value; } @@ -227,7 +233,7 @@ int DivPlatformSegaPCM::dispatch(DivCommand c) { chan[c.chan].keyOff=true; chan[c.chan].keyOn=false; chan[c.chan].active=false; - chan[c.chan].std.init(NULL); + chan[c.chan].macroInit(NULL); break; case DIV_CMD_NOTE_OFF_ENV: chan[c.chan].keyOff=true; diff --git a/src/engine/platform/segapcm.h b/src/engine/platform/segapcm.h index 01e56b6bb..7ef09892a 100644 --- a/src/engine/platform/segapcm.h +++ b/src/engine/platform/segapcm.h @@ -42,6 +42,10 @@ class DivPlatformSegaPCM: public DivDispatch { unsigned char freq; PCMChannel(): sample(-1), pos(0), len(0), freq(0) {} } pcm; + void macroInit(DivInstrument* which) { + std.init(which); + pitch2=0; + } Channel(): freqH(0), freqL(0), freq(0), baseFreq(0), pitch(0), pitch2(0), note(0), ins(-1), active(false), insChanged(true), freqChanged(false), keyOn(false), keyOff(false), inPorta(false), portaPause(false), furnacePCM(false), vol(0), outVol(0), chVolL(127), chVolR(127) {} }; Channel chan[16]; diff --git a/src/engine/platform/sms.cpp b/src/engine/platform/sms.cpp index faf5ba7d6..d0a19d1db 100644 --- a/src/engine/platform/sms.cpp +++ b/src/engine/platform/sms.cpp @@ -101,12 +101,18 @@ void DivPlatformSMS::tick(bool sysTick) { } } if (chan[i].std.pitch.had) { + if (chan[i].std.pitch.mode) { + chan[i].pitch2+=chan[i].std.pitch.val; + CLAMP_VAR(chan[i].pitch2,-2048,2048); + } else { + chan[i].pitch2=chan[i].std.pitch.val; + } chan[i].freqChanged=true; } } for (int i=0; i<3; i++) { if (chan[i].freqChanged) { - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].std.pitch.val); + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].pitch2); if (chan[i].freq>1023) chan[i].freq=1023; if (chan[i].freq<8) chan[i].freq=1; //if (chan[i].actualNote>0x5d) chan[i].freq=0x01; @@ -121,7 +127,7 @@ void DivPlatformSMS::tick(bool sysTick) { } } if (chan[3].freqChanged || updateSNMode) { - chan[3].freq=parent->calcFreq(chan[3].baseFreq,chan[3].pitch,true,0,chan[3].std.pitch.val); + chan[3].freq=parent->calcFreq(chan[3].baseFreq,chan[3].pitch,true,0,chan[3].pitch2); if (chan[3].freq>1023) chan[3].freq=1023; if (chan[3].actualNote>0x5d) chan[3].freq=0x01; if (snNoiseMode&2) { // take period from channel 3 @@ -177,12 +183,12 @@ int DivPlatformSMS::dispatch(DivCommand c) { } chan[c.chan].active=true; rWrite(0x90|c.chan<<5|(isMuted[c.chan]?15:(15-(chan[c.chan].vol&15)))); - chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_STD)); + chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_STD)); break; case DIV_CMD_NOTE_OFF: chan[c.chan].active=false; rWrite(0x9f|c.chan<<5); - chan[c.chan].std.init(NULL); + chan[c.chan].macroInit(NULL); break; case DIV_CMD_NOTE_OFF_ENV: case DIV_CMD_ENV_RELEASE: @@ -190,7 +196,7 @@ int DivPlatformSMS::dispatch(DivCommand c) { break; case DIV_CMD_INSTRUMENT: chan[c.chan].ins=c.value; - //chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_STD)); + //chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_STD)); break; case DIV_CMD_VOLUME: if (chan[c.chan].vol!=c.value) { @@ -246,7 +252,7 @@ int DivPlatformSMS::dispatch(DivCommand c) { break; case DIV_CMD_PRE_PORTA: if (chan[c.chan].active && c.value2) { - if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_STD)); + if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_STD)); } chan[c.chan].inPorta=c.value; break; diff --git a/src/engine/platform/sms.h b/src/engine/platform/sms.h index 3342dc987..a79945bdd 100644 --- a/src/engine/platform/sms.h +++ b/src/engine/platform/sms.h @@ -30,6 +30,10 @@ class DivPlatformSMS: public DivDispatch { bool active, insChanged, freqChanged, keyOn, keyOff, inPorta; signed char vol, outVol; DivMacroInt std; + void macroInit(DivInstrument* which) { + std.init(which); + pitch2=0; + } Channel(): freq(0), baseFreq(0), diff --git a/src/engine/platform/su.cpp b/src/engine/platform/su.cpp index 1f47a1629..6efa0a482 100644 --- a/src/engine/platform/su.cpp +++ b/src/engine/platform/su.cpp @@ -151,6 +151,12 @@ void DivPlatformSoundUnit::tick(bool sysTick) { chWrite(i,0x03,chan[i].pan); } if (chan[i].std.pitch.had) { + if (chan[i].std.pitch.mode) { + chan[i].pitch2+=chan[i].std.pitch.val; + CLAMP_VAR(chan[i].pitch2,-2048,2048); + } else { + chan[i].pitch2=chan[i].std.pitch.val; + } chan[i].freqChanged=true; } if (chan[i].std.ex1.had) { @@ -168,7 +174,7 @@ void DivPlatformSoundUnit::tick(bool sysTick) { } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { //DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_SU); - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,2,chan[i].std.pitch.val); + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,2,chan[i].pitch2); chWrite(i,0x00,chan[i].freq&0xff); chWrite(i,0x01,chan[i].freq>>8); if (chan[i].freq>65535) chan[i].freq=65535; @@ -198,14 +204,14 @@ int DivPlatformSoundUnit::dispatch(DivCommand c) { chan[c.chan].active=true; chan[c.chan].keyOn=true; chWrite(c.chan,0x02,chan[c.chan].vol); - chan[c.chan].std.init(ins); + chan[c.chan].macroInit(ins); chan[c.chan].insChanged=false; break; } case DIV_CMD_NOTE_OFF: chan[c.chan].active=false; chan[c.chan].keyOff=true; - chan[c.chan].std.init(NULL); + chan[c.chan].macroInit(NULL); break; case DIV_CMD_NOTE_OFF_ENV: case DIV_CMD_ENV_RELEASE: @@ -275,7 +281,7 @@ int DivPlatformSoundUnit::dispatch(DivCommand c) { break; case DIV_CMD_PRE_PORTA: if (chan[c.chan].active && c.value2) { - if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_SU)); + if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_SU)); } chan[c.chan].inPorta=c.value; break; diff --git a/src/engine/platform/su.h b/src/engine/platform/su.h index 935e62ea8..7315f49a3 100644 --- a/src/engine/platform/su.h +++ b/src/engine/platform/su.h @@ -35,6 +35,10 @@ class DivPlatformSoundUnit: public DivDispatch { bool pcmLoop, timerSync, freqSweep, volSweep, cutSweep; signed char vol, outVol, wave; DivMacroInt std; + void macroInit(DivInstrument* which) { + std.init(which); + pitch2=0; + } Channel(): freq(0), baseFreq(0), diff --git a/src/engine/platform/swan.cpp b/src/engine/platform/swan.cpp index 96752bd5a..147082924 100644 --- a/src/engine/platform/swan.cpp +++ b/src/engine/platform/swan.cpp @@ -185,6 +185,12 @@ void DivPlatformSwan::tick(bool sysTick) { calcAndWriteOutVol(i,chan[i].std.vol.will?chan[i].std.vol.val:15); } if (chan[i].std.pitch.had) { + if (chan[i].std.pitch.mode) { + chan[i].pitch2+=chan[i].std.pitch.val; + CLAMP_VAR(chan[i].pitch2,-2048,2048); + } else { + chan[i].pitch2=chan[i].std.pitch.val; + } chan[i].freqChanged=true; } if (chan[i].active) { @@ -194,7 +200,7 @@ void DivPlatformSwan::tick(bool sysTick) { } } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].std.pitch.val); + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].pitch2); if (i==1 && pcm && furnaceDac) { double off=1.0; if (dacSample>=0 && dacSamplesong.sampleLen) { @@ -271,7 +277,7 @@ int DivPlatformSwan::dispatch(DivCommand c) { } chan[1].active=true; chan[1].keyOn=true; - chan[1].std.init(ins); + chan[1].macroInit(ins); furnaceDac=true; } else { if (c.value!=DIV_NOTE_NULL) { @@ -303,7 +309,7 @@ int DivPlatformSwan::dispatch(DivCommand c) { } chan[c.chan].active=true; chan[c.chan].keyOn=true; - chan[c.chan].std.init(ins); + chan[c.chan].macroInit(ins); if (chan[c.chan].wave<0) { chan[c.chan].wave=0; chan[c.chan].ws.changeWave1(chan[c.chan].wave); @@ -320,7 +326,7 @@ int DivPlatformSwan::dispatch(DivCommand c) { } chan[c.chan].active=false; chan[c.chan].keyOff=true; - chan[c.chan].std.init(NULL); + chan[c.chan].macroInit(NULL); break; case DIV_CMD_NOTE_OFF_ENV: case DIV_CMD_ENV_RELEASE: @@ -417,7 +423,7 @@ int DivPlatformSwan::dispatch(DivCommand c) { break; case DIV_CMD_PRE_PORTA: if (chan[c.chan].active && c.value2) { - if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_SWAN)); + if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_SWAN)); } chan[c.chan].inPorta=c.value; break; diff --git a/src/engine/platform/swan.h b/src/engine/platform/swan.h index 8997e28d2..c03dd0d55 100644 --- a/src/engine/platform/swan.h +++ b/src/engine/platform/swan.h @@ -34,6 +34,10 @@ class DivPlatformSwan: public DivDispatch { int vol, outVol, wave; DivMacroInt std; DivWaveSynth ws; + void macroInit(DivInstrument* which) { + std.init(which); + pitch2=0; + } Channel(): freq(0), baseFreq(0), diff --git a/src/engine/platform/tia.cpp b/src/engine/platform/tia.cpp index cc5151f03..f6f7dd9cf 100644 --- a/src/engine/platform/tia.cpp +++ b/src/engine/platform/tia.cpp @@ -117,6 +117,12 @@ void DivPlatformTIA::tick(bool sysTick) { chan[i].freqChanged=true; } if (chan[i].std.pitch.had) { + if (chan[i].std.pitch.mode) { + chan[i].pitch2+=chan[i].std.pitch.val; + CLAMP_VAR(chan[i].pitch2,-2048,2048); + } else { + chan[i].pitch2=chan[i].std.pitch.val; + } chan[i].freqChanged=true; } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { @@ -127,14 +133,14 @@ void DivPlatformTIA::tick(bool sysTick) { } chan[i].insChanged=false; } - chan[i].freq=dealWithFreq(chan[i].shape,chan[i].baseFreq,chan[i].pitch)+chan[i].std.pitch.val; + chan[i].freq=dealWithFreq(chan[i].shape,chan[i].baseFreq,chan[i].pitch)+chan[i].pitch2; if ((chan[i].shape==4 || chan[i].shape==5) && !(chan[i].baseFreq&0x80000000 && ((chan[i].baseFreq&0x7fffffff)<32))) { if (chan[i].baseFreq<39*256) { rWrite(0x15+i,6); - chan[i].freq=dealWithFreq(6,chan[i].baseFreq,chan[i].pitch)+chan[i].std.pitch.val; + chan[i].freq=dealWithFreq(6,chan[i].baseFreq,chan[i].pitch)+chan[i].pitch2; } else if (chan[i].baseFreq<59*256) { rWrite(0x15+i,12); - chan[i].freq=dealWithFreq(12,chan[i].baseFreq,chan[i].pitch)+chan[i].std.pitch.val; + chan[i].freq=dealWithFreq(12,chan[i].baseFreq,chan[i].pitch)+chan[i].pitch2; } else { rWrite(0x15+i,chan[i].shape); } @@ -163,7 +169,7 @@ int DivPlatformTIA::dispatch(DivCommand c) { chan[c.chan].active=true; chan[c.chan].keyOn=true; rWrite(0x15+c.chan,chan[c.chan].shape); - chan[c.chan].std.init(ins); + chan[c.chan].macroInit(ins); if (isMuted[c.chan]) { rWrite(0x19+c.chan,0); } else { @@ -174,7 +180,7 @@ int DivPlatformTIA::dispatch(DivCommand c) { case DIV_CMD_NOTE_OFF: chan[c.chan].keyOff=true; chan[c.chan].active=false; - chan[c.chan].std.init(NULL); + chan[c.chan].macroInit(NULL); break; case DIV_CMD_NOTE_OFF_ENV: case DIV_CMD_ENV_RELEASE: @@ -248,7 +254,7 @@ int DivPlatformTIA::dispatch(DivCommand c) { break; case DIV_CMD_PRE_PORTA: if (chan[c.chan].active && c.value2) { - if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_TIA)); + if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_TIA)); } chan[c.chan].inPorta=c.value; break; diff --git a/src/engine/platform/tia.h b/src/engine/platform/tia.h index cc59fc18e..dc3bbfd10 100644 --- a/src/engine/platform/tia.h +++ b/src/engine/platform/tia.h @@ -33,6 +33,10 @@ class DivPlatformTIA: public DivDispatch { bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, inPorta; int vol, outVol; DivMacroInt std; + void macroInit(DivInstrument* which) { + std.init(which); + pitch2=0; + } Channel(): freq(0), baseFreq(0), pitch(0), pitch2(0), note(0), ins(-1), shape(4), active(false), insChanged(true), freqChanged(false), keyOn(false), keyOff(false), portaPause(false), inPorta(false), vol(0), outVol(15) {} }; Channel chan[2]; diff --git a/src/engine/platform/tx81z.cpp b/src/engine/platform/tx81z.cpp index 712335818..643b6ebcf 100644 --- a/src/engine/platform/tx81z.cpp +++ b/src/engine/platform/tx81z.cpp @@ -229,6 +229,12 @@ void DivPlatformTX81Z::tick(bool sysTick) { } if (chan[i].std.pitch.had) { + if (chan[i].std.pitch.mode) { + chan[i].pitch2+=chan[i].std.pitch.val; + CLAMP_VAR(chan[i].pitch2,-2048,2048); + } else { + chan[i].pitch2=chan[i].std.pitch.val; + } chan[i].freqChanged=true; } @@ -391,7 +397,7 @@ void DivPlatformTX81Z::tick(bool sysTick) { for (int i=0; i<8; i++) { if (chan[i].freqChanged) { - chan[i].freq=chan[i].baseFreq+(chan[i].pitch>>1)-64+chan[i].std.pitch.val; + chan[i].freq=chan[i].baseFreq+(chan[i].pitch>>1)-64+chan[i].pitch2; if (chan[i].freq<0) chan[i].freq=0; if (chan[i].freq>=(95<<6)) chan[i].freq=(95<<6)-1; immWrite(i+0x28,hScale(chan[i].freq>>6)); @@ -430,7 +436,7 @@ int DivPlatformTX81Z::dispatch(DivCommand c) { chan[c.chan].state=ins->fm; } - chan[c.chan].std.init(ins); + chan[c.chan].macroInit(ins); if (!chan[c.chan].std.vol.will) { chan[c.chan].outVol=chan[c.chan].vol; } diff --git a/src/engine/platform/tx81z.h b/src/engine/platform/tx81z.h index e8bda6a3d..5df591f64 100644 --- a/src/engine/platform/tx81z.h +++ b/src/engine/platform/tx81z.h @@ -40,6 +40,10 @@ class DivPlatformTX81Z: public DivDispatch { bool active, insChanged, freqChanged, keyOn, keyOff, inPorta, portaPause, furnacePCM, hardReset; int vol, outVol; unsigned char chVolL, chVolR; + void macroInit(DivInstrument* which) { + std.init(which); + pitch2=0; + } Channel(): freqH(0), freqL(0), diff --git a/src/engine/platform/vera.cpp b/src/engine/platform/vera.cpp index 7d4ea7d54..9b30abdad 100644 --- a/src/engine/platform/vera.cpp +++ b/src/engine/platform/vera.cpp @@ -191,10 +191,16 @@ void DivPlatformVERA::tick(bool sysTick) { } } if (chan[i].std.pitch.had) { + if (chan[i].std.pitch.mode) { + chan[i].pitch2+=chan[i].std.pitch.val; + CLAMP_VAR(chan[i].pitch2,-2048,2048); + } else { + chan[i].pitch2=chan[i].std.pitch.val; + } chan[i].freqChanged=true; } if (chan[i].freqChanged) { - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,8,chan[i].std.pitch.val); + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,8,chan[i].pitch2); if (chan[i].freq>65535) chan[i].freq=65535; rWrite(i,0,chan[i].freq&0xff); rWrite(i,1,(chan[i].freq>>8)&0xff); @@ -223,7 +229,7 @@ void DivPlatformVERA::tick(bool sysTick) { } } if (chan[16].freqChanged) { - chan[16].freq=parent->calcFreq(chan[16].baseFreq,chan[16].pitch,false,8)+chan[16].std.pitch.val; + chan[16].freq=parent->calcFreq(chan[16].baseFreq,chan[16].pitch,false,8,chan[16].pitch2); if (chan[16].freq>128) chan[16].freq=128; rWritePCMRate(chan[16].freq&0xff); chan[16].freqChanged=false; @@ -259,7 +265,7 @@ int DivPlatformVERA::dispatch(DivCommand c) { chan[c.chan].note=c.value; } chan[c.chan].active=true; - chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_VERA)); + chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_VERA)); break; case DIV_CMD_NOTE_OFF: chan[c.chan].active=false; @@ -270,7 +276,7 @@ int DivPlatformVERA::dispatch(DivCommand c) { rWritePCMCtrl(0x80); rWritePCMRate(0); } - chan[c.chan].std.init(NULL); + chan[c.chan].macroInit(NULL); break; case DIV_CMD_NOTE_OFF_ENV: case DIV_CMD_ENV_RELEASE: @@ -327,7 +333,7 @@ int DivPlatformVERA::dispatch(DivCommand c) { break; case DIV_CMD_PRE_PORTA: if (chan[c.chan].active && c.value2) { - if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_VERA)); + if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_VERA)); } chan[c.chan].inPorta=c.value; break; diff --git a/src/engine/platform/vera.h b/src/engine/platform/vera.h index b40598b33..ddcbed36a 100644 --- a/src/engine/platform/vera.h +++ b/src/engine/platform/vera.h @@ -46,6 +46,10 @@ class DivPlatformVERA: public DivDispatch { PCMChannel(): sample(-1), pos(0), len(0), freq(0), depth16(false) {} } pcm; // somebody please split this into multiple lines! + void macroInit(DivInstrument* which) { + std.init(which); + pitch2=0; + } Channel(): freq(0), baseFreq(0), pitch(0), pitch2(0), note(0), ins(-1), pan(0), active(false), freqChanged(false), inPorta(false), vol(0), outVol(0), accum(0), noiseval(0) {} }; Channel chan[17]; diff --git a/src/engine/platform/vic20.cpp b/src/engine/platform/vic20.cpp index efeb0a206..32b1418f2 100644 --- a/src/engine/platform/vic20.cpp +++ b/src/engine/platform/vic20.cpp @@ -120,10 +120,16 @@ void DivPlatformVIC20::tick(bool sysTick) { } } if (chan[i].std.pitch.had) { + if (chan[i].std.pitch.mode) { + chan[i].pitch2+=chan[i].std.pitch.val; + CLAMP_VAR(chan[i].pitch2,-2048,2048); + } else { + chan[i].pitch2=chan[i].std.pitch.val; + } chan[i].freqChanged=true; } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].std.pitch.val); + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].pitch2); if (i<3) { chan[i].freq>>=(2-i); } else { @@ -166,13 +172,13 @@ int DivPlatformVIC20::dispatch(DivCommand c) { } chan[c.chan].active=true; chan[c.chan].keyOn=true; - chan[c.chan].std.init(ins); + chan[c.chan].macroInit(ins); break; } case DIV_CMD_NOTE_OFF: chan[c.chan].active=false; chan[c.chan].keyOff=true; - chan[c.chan].std.init(NULL); + chan[c.chan].macroInit(NULL); break; case DIV_CMD_NOTE_OFF_ENV: case DIV_CMD_ENV_RELEASE: @@ -232,7 +238,7 @@ int DivPlatformVIC20::dispatch(DivCommand c) { break; case DIV_CMD_PRE_PORTA: if (chan[c.chan].active && c.value2) { - if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_VIC)); + if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_VIC)); } chan[c.chan].inPorta=c.value; break; diff --git a/src/engine/platform/vic20.h b/src/engine/platform/vic20.h index 0db37c5bf..2b3f08e7f 100644 --- a/src/engine/platform/vic20.h +++ b/src/engine/platform/vic20.h @@ -32,6 +32,10 @@ class DivPlatformVIC20: public DivDispatch { bool active, insChanged, freqChanged, keyOn, keyOff, inPorta; int vol, outVol, wave, waveWriteCycle; DivMacroInt std; + void macroInit(DivInstrument* which) { + std.init(which); + pitch2=0; + } Channel(): freq(0), baseFreq(0), diff --git a/src/engine/platform/vrc6.cpp b/src/engine/platform/vrc6.cpp index 29fadd4c7..64c4b831c 100644 --- a/src/engine/platform/vrc6.cpp +++ b/src/engine/platform/vrc6.cpp @@ -179,13 +179,19 @@ void DivPlatformVRC6::tick(bool sysTick) { } } if (chan[i].std.pitch.had) { + if (chan[i].std.pitch.mode) { + chan[i].pitch2+=chan[i].std.pitch.val; + CLAMP_VAR(chan[i].pitch2,-2048,2048); + } else { + chan[i].pitch2=chan[i].std.pitch.val; + } chan[i].freqChanged=true; } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { if (i==2) { // sawtooth - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].std.pitch.val)-1; + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].pitch2)-1; } else { // pulse - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].std.pitch.val)-1; + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].pitch2)-1; if (chan[i].furnaceDac) { double off=1.0; if (chan[i].dacSample>=0 && chan[i].dacSamplesong.sampleLen) { @@ -249,7 +255,7 @@ int DivPlatformVRC6::dispatch(DivCommand c) { chan[c.chan].note=c.value; } chan[c.chan].active=true; - chan[c.chan].std.init(ins); + chan[c.chan].macroInit(ins); //chan[c.chan].keyOn=true; chan[c.chan].furnaceDac=true; } else { @@ -284,7 +290,7 @@ int DivPlatformVRC6::dispatch(DivCommand c) { } chan[c.chan].active=true; chan[c.chan].keyOn=true; - chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_VRC6)); + chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_VRC6)); if (!isMuted[c.chan]) { if (c.chan==2) { // sawtooth chWrite(c.chan,0,chan[c.chan].vol); @@ -299,7 +305,7 @@ int DivPlatformVRC6::dispatch(DivCommand c) { chan[c.chan].pcm=false; chan[c.chan].active=false; chan[c.chan].keyOff=true; - chan[c.chan].std.init(NULL); + chan[c.chan].macroInit(NULL); break; case DIV_CMD_NOTE_OFF_ENV: case DIV_CMD_ENV_RELEASE: @@ -380,7 +386,7 @@ int DivPlatformVRC6::dispatch(DivCommand c) { break; case DIV_CMD_PRE_PORTA: if (chan[c.chan].active && c.value2) { - if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_VRC6)); + if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_VRC6)); } chan[c.chan].inPorta=c.value; break; diff --git a/src/engine/platform/vrc6.h b/src/engine/platform/vrc6.h index 45dcb5c5f..789af7f52 100644 --- a/src/engine/platform/vrc6.h +++ b/src/engine/platform/vrc6.h @@ -36,6 +36,10 @@ class DivPlatformVRC6: public DivDispatch { bool active, insChanged, freqChanged, keyOn, keyOff, inPorta, pcm, furnaceDac; signed char vol, outVol; DivMacroInt std; + void macroInit(DivInstrument* which) { + std.init(which); + pitch2=0; + } Channel(): freq(0), baseFreq(0), diff --git a/src/engine/platform/x1_010.cpp b/src/engine/platform/x1_010.cpp index 46db65622..9d64adfa9 100644 --- a/src/engine/platform/x1_010.cpp +++ b/src/engine/platform/x1_010.cpp @@ -383,6 +383,12 @@ void DivPlatformX1_010::tick(bool sysTick) { chan[i].envChanged=true; } if (chan[i].std.pitch.had) { + if (chan[i].std.pitch.mode) { + chan[i].pitch2+=chan[i].std.pitch.val; + CLAMP_VAR(chan[i].pitch2,-2048,2048); + } else { + chan[i].pitch2=chan[i].std.pitch.val; + } chan[i].freqChanged=true; } if (chan[i].std.ex1.had) { @@ -476,7 +482,7 @@ void DivPlatformX1_010::tick(bool sysTick) { chan[i].envChanged=false; } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,2,chan[i].std.pitch.val); + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,2,chan[i].pitch2); if (chan[i].pcm) { if (chan[i].freq<1) chan[i].freq=1; if (chan[i].freq>255) chan[i].freq=255; @@ -535,7 +541,7 @@ int DivPlatformX1_010::dispatch(DivCommand c) { if (skipRegisterWrites) break; if (chan[c.chan].furnacePCM) { chan[c.chan].pcm=true; - chan[c.chan].std.init(ins); + chan[c.chan].macroInit(ins); chan[c.chan].sample=ins->amiga.initSample; if (chan[c.chan].sample>=0 && chan[c.chan].samplesong.sampleLen) { DivSample* s=parent->getSample(chan[c.chan].sample); @@ -548,7 +554,7 @@ int DivPlatformX1_010::dispatch(DivCommand c) { chan[c.chan].freqChanged=true; } } else { - chan[c.chan].std.init(NULL); + chan[c.chan].macroInit(NULL); chan[c.chan].outVol=chan[c.chan].vol; if ((12*sampleBank+c.value%12)>=parent->song.sampleLen) { chWrite(c.chan,0,0); // reset @@ -560,7 +566,7 @@ int DivPlatformX1_010::dispatch(DivCommand c) { } } } else { - chan[c.chan].std.init(NULL); + chan[c.chan].macroInit(NULL); chan[c.chan].outVol=chan[c.chan].vol; if ((12*sampleBank+c.value%12)>=parent->song.sampleLen) { chWrite(c.chan,0,0); // reset @@ -585,7 +591,7 @@ int DivPlatformX1_010::dispatch(DivCommand c) { chan[c.chan].active=true; chan[c.chan].keyOn=true; chan[c.chan].envChanged=true; - chan[c.chan].std.init(ins); + chan[c.chan].macroInit(ins); if (chan[c.chan].wave<0) { chan[c.chan].wave=0; chan[c.chan].ws.changeWave1(chan[c.chan].wave); @@ -599,7 +605,7 @@ int DivPlatformX1_010::dispatch(DivCommand c) { chan[c.chan].pcm=false; chan[c.chan].active=false; chan[c.chan].keyOff=true; - chan[c.chan].std.init(NULL); + chan[c.chan].macroInit(NULL); break; case DIV_CMD_NOTE_OFF_ENV: case DIV_CMD_ENV_RELEASE: @@ -703,7 +709,7 @@ int DivPlatformX1_010::dispatch(DivCommand c) { break; case DIV_CMD_PRE_PORTA: if (chan[c.chan].active && c.value2) { - if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_X1_010)); + if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_X1_010)); } chan[c.chan].inPorta=c.value; break; diff --git a/src/engine/platform/x1_010.h b/src/engine/platform/x1_010.h index cceb61173..8dc749842 100644 --- a/src/engine/platform/x1_010.h +++ b/src/engine/platform/x1_010.h @@ -99,6 +99,10 @@ class DivPlatformX1_010: public DivDispatch { vol = outVol = lvol = rvol = 15; waveBank = 0; } + void macroInit(DivInstrument* which) { + std.init(which); + pitch2=0; + } Channel(): freq(0), baseFreq(0), pitch(0), pitch2(0), note(0), wave(-1), sample(-1), ins(-1), diff --git a/src/engine/platform/ym2610.cpp b/src/engine/platform/ym2610.cpp index 0af0840ab..4fcd53d36 100644 --- a/src/engine/platform/ym2610.cpp +++ b/src/engine/platform/ym2610.cpp @@ -414,6 +414,12 @@ void DivPlatformYM2610::tick(bool sysTick) { } if (chan[i].std.pitch.had) { + if (chan[i].std.pitch.mode) { + chan[i].pitch2+=chan[i].std.pitch.val; + CLAMP_VAR(chan[i].pitch2,-2048,2048); + } else { + chan[i].pitch2=chan[i].std.pitch.val; + } chan[i].freqChanged=true; } @@ -572,9 +578,9 @@ void DivPlatformYM2610::tick(bool sysTick) { for (int i=0; i<4; i++) { if (i==1 && extMode) continue; if (chan[i].freqChanged) { - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,octave(chan[i].baseFreq),chan[i].std.pitch.val); + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,octave(chan[i].baseFreq),chan[i].pitch2); if (chan[i].freq>262143) chan[i].freq=262143; - int freqt=toFreq(chan[i].freq)+chan[i].std.pitch.val; + int freqt=toFreq(chan[i].freq)+chan[i].pitch2; immWrite(chanOffs[i]+ADDR_FREQH,freqt>>8); immWrite(chanOffs[i]+ADDR_FREQ,freqt&0xff); chan[i].freqChanged=false; @@ -643,7 +649,7 @@ int DivPlatformYM2610::dispatch(DivCommand c) { } if (skipRegisterWrites) break; if (chan[c.chan].furnacePCM) { - chan[c.chan].std.init(ins); + chan[c.chan].macroInit(ins); if (!chan[c.chan].std.vol.will) { chan[c.chan].outVol=chan[c.chan].vol; immWrite(0x1b,chan[c.chan].outVol); @@ -675,7 +681,7 @@ int DivPlatformYM2610::dispatch(DivCommand c) { } } else { chan[c.chan].sample=-1; - chan[c.chan].std.init(NULL); + chan[c.chan].macroInit(NULL); chan[c.chan].outVol=chan[c.chan].vol; if ((12*sampleBank+c.value%12)>=parent->song.sampleLen) { immWrite(0x10,0x01); // reset @@ -719,7 +725,7 @@ int DivPlatformYM2610::dispatch(DivCommand c) { break; } DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_FM); - chan[c.chan].std.init(ins); + chan[c.chan].macroInit(ins); if (c.chan<4) { if (!chan[c.chan].std.vol.will) { chan[c.chan].outVol=chan[c.chan].vol; @@ -779,7 +785,7 @@ int DivPlatformYM2610::dispatch(DivCommand c) { chan[c.chan].keyOff=true; chan[c.chan].keyOn=false; chan[c.chan].active=false; - chan[c.chan].std.init(NULL); + chan[c.chan].macroInit(NULL); break; case DIV_CMD_NOTE_OFF_ENV: if (c.chan>12) { @@ -981,7 +987,7 @@ int DivPlatformYM2610::dispatch(DivCommand c) { case DIV_CMD_PRE_PORTA: if (c.chan>3) { if (chan[c.chan].active && c.value2) { - if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_FM)); + if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_FM)); } } chan[c.chan].inPorta=c.value; diff --git a/src/engine/platform/ym2610.h b/src/engine/platform/ym2610.h index ec1b2497a..56f6824d0 100644 --- a/src/engine/platform/ym2610.h +++ b/src/engine/platform/ym2610.h @@ -51,6 +51,10 @@ class DivPlatformYM2610: public DivDispatch { int sample; unsigned char pan; DivMacroInt std; + void macroInit(DivInstrument* which) { + std.init(which); + pitch2=0; + } Channel(): freqH(0), freqL(0), diff --git a/src/engine/platform/ym2610b.cpp b/src/engine/platform/ym2610b.cpp index b0e530db3..3ad47b17d 100644 --- a/src/engine/platform/ym2610b.cpp +++ b/src/engine/platform/ym2610b.cpp @@ -478,6 +478,12 @@ void DivPlatformYM2610B::tick(bool sysTick) { } if (chan[i].std.pitch.had) { + if (chan[i].std.pitch.mode) { + chan[i].pitch2+=chan[i].std.pitch.val; + CLAMP_VAR(chan[i].pitch2,-2048,2048); + } else { + chan[i].pitch2=chan[i].std.pitch.val; + } chan[i].freqChanged=true; } @@ -635,9 +641,9 @@ void DivPlatformYM2610B::tick(bool sysTick) { for (int i=0; i<6; i++) { if (i==2 && extMode) continue; if (chan[i].freqChanged) { - chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,octave(chan[i].baseFreq),chan[i].std.pitch.val); + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,octave(chan[i].baseFreq),chan[i].pitch2); if (chan[i].freq>262143) chan[i].freq=262143; - int freqt=toFreq(chan[i].freq)+chan[i].std.pitch.val; + int freqt=toFreq(chan[i].freq)+chan[i].pitch2; immWrite(chanOffs[i]+ADDR_FREQH,freqt>>8); immWrite(chanOffs[i]+ADDR_FREQ,freqt&0xff); chan[i].freqChanged=false; @@ -706,7 +712,7 @@ int DivPlatformYM2610B::dispatch(DivCommand c) { } if (skipRegisterWrites) break; if (chan[c.chan].furnacePCM) { - chan[c.chan].std.init(ins); + chan[c.chan].macroInit(ins); if (!chan[c.chan].std.vol.will) { chan[c.chan].outVol=chan[c.chan].vol; immWrite(0x1b,chan[c.chan].outVol); @@ -738,7 +744,7 @@ int DivPlatformYM2610B::dispatch(DivCommand c) { } } else { chan[c.chan].sample=-1; - chan[c.chan].std.init(NULL); + chan[c.chan].macroInit(NULL); chan[c.chan].outVol=chan[c.chan].vol; if ((12*sampleBank+c.value%12)>=parent->song.sampleLen) { immWrite(0x10,0x01); // reset @@ -782,7 +788,7 @@ int DivPlatformYM2610B::dispatch(DivCommand c) { break; } DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_FM); - chan[c.chan].std.init(ins); + chan[c.chan].macroInit(ins); if (c.chan<6) { if (!chan[c.chan].std.vol.will) { chan[c.chan].outVol=chan[c.chan].vol; @@ -842,7 +848,7 @@ int DivPlatformYM2610B::dispatch(DivCommand c) { chan[c.chan].keyOff=true; chan[c.chan].keyOn=false; chan[c.chan].active=false; - chan[c.chan].std.init(NULL); + chan[c.chan].macroInit(NULL); break; case DIV_CMD_NOTE_OFF_ENV: if (c.chan>14) { @@ -1044,7 +1050,7 @@ int DivPlatformYM2610B::dispatch(DivCommand c) { case DIV_CMD_PRE_PORTA: if (c.chan>5) { if (chan[c.chan].active && c.value2) { - if (parent->song.resetMacroOnPorta) chan[c.chan].std.init(parent->getIns(chan[c.chan].ins,DIV_INS_FM)); + if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_FM)); } } chan[c.chan].inPorta=c.value; diff --git a/src/engine/platform/ym2610b.h b/src/engine/platform/ym2610b.h index 0ce1e0f7f..c01b9515a 100644 --- a/src/engine/platform/ym2610b.h +++ b/src/engine/platform/ym2610b.h @@ -43,6 +43,10 @@ class DivPlatformYM2610B: public DivDispatch { int sample; unsigned char pan; DivMacroInt std; + void macroInit(DivInstrument* which) { + std.init(which); + pitch2=0; + } Channel(): freqH(0), freqL(0), diff --git a/src/engine/platform/ym2610bext.cpp b/src/engine/platform/ym2610bext.cpp index 74794a7d7..e930934d0 100644 --- a/src/engine/platform/ym2610bext.cpp +++ b/src/engine/platform/ym2610bext.cpp @@ -235,7 +235,7 @@ void DivPlatformYM2610BExt::tick(bool sysTick) { unsigned char writeMask=2; if (extMode) for (int i=0; i<4; i++) { if (opChan[i].freqChanged) { - opChan[i].freq=parent->calcFreq(opChan[i].baseFreq,opChan[i].pitch); + opChan[i].freq=parent->calcFreq(opChan[i].baseFreq,opChan[i].pitch,false,octave(opChan[i].baseFreq),opChan[i].pitch2); if (opChan[i].freq>262143) opChan[i].freq=262143; int freqt=toFreq(opChan[i].freq); opChan[i].freqH=freqt>>8; diff --git a/src/engine/platform/ym2610ext.cpp b/src/engine/platform/ym2610ext.cpp index df9b43511..c4cb3d9ca 100644 --- a/src/engine/platform/ym2610ext.cpp +++ b/src/engine/platform/ym2610ext.cpp @@ -235,7 +235,7 @@ void DivPlatformYM2610Ext::tick(bool sysTick) { unsigned char writeMask=2; if (extMode) for (int i=0; i<4; i++) { if (opChan[i].freqChanged) { - opChan[i].freq=parent->calcFreq(opChan[i].baseFreq,opChan[i].pitch); + opChan[i].freq=parent->calcFreq(opChan[i].baseFreq,opChan[i].pitch,false,octave(opChan[i].baseFreq),opChan[i].pitch2); if (opChan[i].freq>262143) opChan[i].freq=262143; int freqt=toFreq(opChan[i].freq); opChan[i].freqH=freqt>>8; diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index 105921881..716683358 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -210,15 +210,10 @@ const char* dualWSEffects[7]={ "Phase (dual)", }; -const char* macroAbsoluteMode[2]={ - "Relative", - "Absolute" -}; +const char* macroAbsoluteMode="Fixed"; +const char* macroRelativeMode="Relative"; -const char* macroDummyMode[2]={ - "Bug", - "Bug" -}; +const char* macroDummyMode="Bug"; String macroHoverNote(int id, float val) { if (val<-60 || val>=120) return "???"; @@ -1086,7 +1081,8 @@ void FurnaceGUI::drawGBEnv(unsigned char vol, unsigned char len, unsigned char s } \ if (macroMode) { \ bool modeVal=macro.mode; \ - if (ImGui::Checkbox("Fixed##IMacroMode_" macroName,&modeVal)) { \ + String modeName=fmt::sprintf("%s##IMacroMode_" macroName,displayModeName); \ + if (ImGui::Checkbox(modeName.c_str(),&modeVal)) { \ macro.mode=modeVal; \ } \ } \ @@ -2880,7 +2876,7 @@ void FurnaceGUI::drawInsEdit() { NORMAL_MACRO(ins->std.panRMacro,0,panMax,"panR","Panning (right)",(31+panMax),ins->std.panRMacro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[14],0,panMax,NULL,false); } } - NORMAL_MACRO(ins->std.pitchMacro,pitchMacroScroll,pitchMacroScroll+160,"pitch","Pitch",160,ins->std.pitchMacro.open,false,NULL,true,&pitchMacroScroll,-2048,2047,0,0,true,1,macroAbsoluteMode,uiColors[GUI_COLOR_MACRO_PITCH],mmlString[15],-2048,2047,NULL,!ins->std.pitchMacro.mode); + NORMAL_MACRO(ins->std.pitchMacro,pitchMacroScroll,pitchMacroScroll+160,"pitch","Pitch",160,ins->std.pitchMacro.open,false,NULL,true,&pitchMacroScroll,-2048,2047,0,0,true,1,macroRelativeMode,uiColors[GUI_COLOR_MACRO_PITCH],mmlString[15],-2048,2047,NULL,!ins->std.pitchMacro.mode); if (ins->type==DIV_INS_FM || ins->type==DIV_INS_STD || ins->type==DIV_INS_OPL || From 859b2cf8db8c2d1d935cfed8aef6087295dda3bd Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 28 Apr 2022 03:36:15 -0500 Subject: [PATCH 229/342] implement channel swapping issue #378 --- src/engine/engine.cpp | 90 +++++++++++++++++++++++++++++++++++++++++-- src/engine/engine.h | 9 ++++- src/gui/channels.cpp | 14 +++++++ src/gui/gui.cpp | 7 +++- src/gui/gui.h | 2 +- 5 files changed, 114 insertions(+), 8 deletions(-) diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index b2c0257f6..a95b2e2b6 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -644,10 +644,79 @@ void DivEngine::createNew(const int* description) { BUSY_END; } -void DivEngine::changeSystem(int index, DivSystem which) { +void DivEngine::swapChannels(int src, int dest) { + logV("swapping channel %d with %d",src,dest); + if (src==dest) { + logV("not swapping channels because it's the same channel!",src,dest); + return; + } + + for (int i=0; i<256; i++) { + song.orders.ord[dest][i]^=song.orders.ord[src][i]; + song.orders.ord[src][i]^=song.orders.ord[dest][i]; + song.orders.ord[dest][i]^=song.orders.ord[src][i]; + + DivPattern* prev=song.pat[src].data[i]; + song.pat[src].data[i]=song.pat[dest].data[i]; + song.pat[dest].data[i]=prev; + } + + song.pat[src].effectCols^=song.pat[dest].effectCols; + song.pat[dest].effectCols^=song.pat[src].effectCols; + song.pat[src].effectCols^=song.pat[dest].effectCols; +} + +void DivEngine::stompChannel(int ch) { + logV("stomping channel %d",ch); + for (int i=0; i<256; i++) { + song.orders.ord[ch][i]=0; + } + song.pat[ch].wipePatterns(); + song.pat[ch].effectCols=1; +} + +void DivEngine::swapChannelsP(int src, int dest) { + if (src<0 || src>=chans) return; + if (dest<0 || dest>=chans) return; + BUSY_BEGIN; + saveLock.lock(); + swapChannels(src,dest); + saveLock.unlock(); + BUSY_END; +} + +void DivEngine::changeSystem(int index, DivSystem which, bool preserveOrder) { + int chanCount=chans; quitDispatch(); BUSY_BEGIN; saveLock.lock(); + + if (!preserveOrder) { + int firstChan=0; + int chanMovement=getChannelCount(which)-getChannelCount(song.system[index]); + while (dispatchOfChan[firstChan]!=index) firstChan++; + int lastChan=firstChan+getChannelCount(song.system[index]); + if (chanMovement!=0) { + if (chanMovement>0) { + // add channels + for (int i=chanCount+chanMovement-1; i>=lastChan+chanMovement; i--) { + swapChannels(i,i-chanMovement); + } + for (int i=lastChan; ichanInsType[j][0]!=DIV_INS_NULL) { isInsTypePossible[sysDefs[song.system[i]]->chanInsType[j][0]]=true; - logV("Marking"); } if (sysDefs[song.system[i]]->chanInsType[j][1]!=DIV_INS_NULL) { diff --git a/src/engine/engine.h b/src/engine/engine.h index 7aa94fc58..ea11f9830 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -395,6 +395,8 @@ class DivEngine { void registerSystems(); void exchangeIns(int one, int two); + void swapChannels(int src, int dest); + void stompChannel(int ch); public: DivSong song; @@ -762,14 +764,17 @@ class DivEngine { // public render samples void renderSamplesP(); + // public swap channels + void swapChannelsP(int src, int dest); + // change system - void changeSystem(int index, DivSystem which); + void changeSystem(int index, DivSystem which, bool preserveOrder=true); // add system bool addSystem(DivSystem which); // remove system - bool removeSystem(int index); + bool removeSystem(int index, bool preserveOrder=true); // write to register on system void poke(int sys, unsigned int addr, unsigned short val); diff --git a/src/gui/channels.cpp b/src/gui/channels.cpp index 5b2a7e0c6..397ac2f65 100644 --- a/src/gui/channels.cpp +++ b/src/gui/channels.cpp @@ -19,6 +19,8 @@ #include "gui.h" #include "misc/cpp/imgui_stdlib.h" +#include "IconsFontAwesome4.h" +#include void FurnaceGUI::drawChannels() { if (nextWindow==GUI_WINDOW_CHANNELS) { @@ -37,6 +39,18 @@ void FurnaceGUI::drawChannels() { ImGui::TableNextRow(); ImGui::TableNextColumn(); ImGui::Checkbox("##Visible",&e->song.chanShow[i]); + ImGui::SameLine(); + ImGui::BeginDisabled(i==0); + if (ImGui::Button(ICON_FA_CHEVRON_UP)) { + e->swapChannelsP(i,i-1); + } + ImGui::EndDisabled(); + ImGui::SameLine(); + ImGui::BeginDisabled(i==(e->getTotalChannelCount()-1)); + if (ImGui::Button(ICON_FA_CHEVRON_DOWN)) { + e->swapChannelsP(i,i+1); + } + ImGui::EndDisabled(); ImGui::TableNextColumn(); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); ImGui::InputTextWithHint("##ChanName",e->getChannelName(i),&e->song.chanName[i]); diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 5ef9a5640..56e046f08 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -1812,7 +1812,7 @@ void FurnaceGUI::processDrags(int dragX, int dragY) { #define sysChangeOption(x,y) \ if (ImGui::MenuItem(getSystemName(y),NULL,e->song.system[x]==y)) { \ - e->changeSystem(x,y); \ + e->changeSystem(x,y,preserveChanPos); \ updateWindowTitle(); \ } @@ -2687,6 +2687,7 @@ bool FurnaceGUI::loop() { ImGui::EndMenu(); } if (ImGui::BeginMenu("change system...")) { + ImGui::Checkbox("Preserve channel positions",&preserveChanPos); for (int i=0; isong.systemLen; i++) { if (ImGui::BeginMenu(fmt::sprintf("%d. %s##_SYSC%d",i+1,getSystemName(e->song.system[i]),i).c_str())) { for (int j=0; availableSystems[j]; j++) { @@ -2699,9 +2700,10 @@ bool FurnaceGUI::loop() { ImGui::EndMenu(); } if (ImGui::BeginMenu("remove system...")) { + ImGui::Checkbox("Preserve channel positions",&preserveChanPos); for (int i=0; isong.systemLen; i++) { if (ImGui::MenuItem(fmt::sprintf("%d. %s##_SYSR%d",i+1,getSystemName(e->song.system[i]),i).c_str())) { - if (!e->removeSystem(i)) { + if (!e->removeSystem(i,preserveChanPos)) { showError("cannot remove system! ("+e->getLastError()+")"); } } @@ -3782,6 +3784,7 @@ FurnaceGUI::FurnaceGUI(): wantCaptureKeyboard(false), displayNew(false), fullScreen(false), + preserveChanPos(false), vgmExportVersion(0x171), drawHalt(10), curFileDialog(GUI_FILE_OPEN), diff --git a/src/gui/gui.h b/src/gui/gui.h index 3b9911c71..14b735b22 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -735,7 +735,7 @@ class FurnaceGUI { String mmlStringW; bool quit, warnQuit, willCommit, edit, modified, displayError, displayExporting, vgmExportLoop, wantCaptureKeyboard; - bool displayNew, fullScreen; + bool displayNew, fullScreen, preserveChanPos; bool willExport[32]; int vgmExportVersion; int drawHalt; From 3906fc1dd4f77e1d4d044f74d254e3a95020c229 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 28 Apr 2022 05:04:34 -0500 Subject: [PATCH 230/342] better channel allocation strategy issue #376 --- src/engine/engine.cpp | 47 ++++++++++++++++++++++++++++++++++++++++--- src/engine/engine.h | 4 ++++ 2 files changed, 48 insertions(+), 3 deletions(-) diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index a95b2e2b6..5abd2d53e 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -2138,10 +2138,12 @@ void DivEngine::noteOff(int chan) { } void DivEngine::autoNoteOn(int ch, int ins, int note, int vol) { - //if (ch<0 || ch>=chans) return; + bool isViable[DIV_MAX_CHANS]; + bool canPlayAnyway=false; if (midiBaseChan<0) midiBaseChan=0; if (midiBaseChan>=chans) midiBaseChan=chans-1; int finalChan=midiBaseChan; + int finalChanType=getChannelType(finalChan); if (!playing) { reset(); @@ -2149,16 +2151,55 @@ void DivEngine::autoNoteOn(int ch, int ins, int note, int vol) { playing=true; } + // 1. check which channels are viable for this instrument + DivInstrument* insInst=getIns(ins); + for (int i=0; i=song.insLen || getPreferInsType(i)==insInst->type || getPreferInsSecondType(i)==insInst->type) { + if (insInst->type==DIV_INS_OPL) { + if (insInst->fm.ops==2 || getChannelType(i)==DIV_CH_OP) { + isViable[i]=true; + canPlayAnyway=true; + } else { + isViable[i]=false; + } + } else { + isViable[i]=true; + canPlayAnyway=true; + } + } else { + isViable[i]=false; + } + } + + if (!canPlayAnyway) return; + + // 2. find a free channel do { - if ((ins==-1 || ins>=song.insLen || getPreferInsType(finalChan)==getIns(ins)->type || getPreferInsSecondType(finalChan)==getIns(ins)->type) && chan[finalChan].midiNote==-1) { + if (isViable[finalChan] && chan[finalChan].midiNote==-1 && getChannelType(finalChan)==finalChanType) { chan[finalChan].midiNote=note; + chan[finalChan].midiAge=midiAgeCounter++; pendingNotes.push(DivNoteEvent(finalChan,ins,note,vol,true)); - break; + return; } if (++finalChan>=chans) { finalChan=0; } } while (finalChan!=midiBaseChan); + + // 3. find the oldest channel + int candidate=finalChan; + do { + if (isViable[finalChan] && getChannelType(finalChan)==finalChanType && chan[finalChan].midiAge=chans) { + finalChan=0; + } + } while (finalChan!=midiBaseChan); + + chan[candidate].midiNote=note; + chan[candidate].midiAge=midiAgeCounter++; + pendingNotes.push(DivNoteEvent(candidate,ins,note,vol,true)); } void DivEngine::autoNoteOff(int ch, int note, int vol) { diff --git a/src/engine/engine.h b/src/engine/engine.h index ea11f9830..441096a23 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -89,6 +89,7 @@ struct DivChannelState { bool arpYield, delayLocked, inPorta, scheduledSlideReset, shorthandPorta, noteOnInhibit, resetArp; int midiNote, curMidiNote, midiPitch; + size_t midiAge; bool midiAftertouch; DivChannelState(): @@ -135,6 +136,7 @@ struct DivChannelState { midiNote(-1), curMidiNote(-1), midiPitch(-1), + midiAge(0), midiAftertouch(false) {} }; @@ -339,6 +341,7 @@ class DivEngine { int reversePitchTable[4096]; int pitchTable[4096]; int midiBaseChan; + size_t midiAgeCounter; blip_buffer_t* samp_bb; size_t samp_bbInLen; @@ -894,6 +897,7 @@ class DivEngine { audioEngine(DIV_AUDIO_NULL), exportMode(DIV_EXPORT_MODE_ONE), midiBaseChan(0), + midiAgeCounter(0), samp_bb(NULL), samp_bbInLen(0), samp_temp(0), From 8675cdf8029310090161fce383bf764b92704e17 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 28 Apr 2022 05:38:28 -0500 Subject: [PATCH 231/342] OPL 4-op note preview possible fix --- src/engine/engine.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 5abd2d53e..b5b60b90b 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -2175,7 +2175,7 @@ void DivEngine::autoNoteOn(int ch, int ins, int note, int vol) { // 2. find a free channel do { - if (isViable[finalChan] && chan[finalChan].midiNote==-1 && getChannelType(finalChan)==finalChanType) { + if (isViable[finalChan] && chan[finalChan].midiNote==-1 && (insInst->type==DIV_INS_OPL || getChannelType(finalChan)==finalChanType)) { chan[finalChan].midiNote=note; chan[finalChan].midiAge=midiAgeCounter++; pendingNotes.push(DivNoteEvent(finalChan,ins,note,vol,true)); @@ -2189,7 +2189,7 @@ void DivEngine::autoNoteOn(int ch, int ins, int note, int vol) { // 3. find the oldest channel int candidate=finalChan; do { - if (isViable[finalChan] && getChannelType(finalChan)==finalChanType && chan[finalChan].midiAgetype==DIV_INS_OPL || getChannelType(finalChan)==finalChanType) && chan[finalChan].midiAge=chans) { From 34c34733891a599a782b6699a452d46226d43c5e Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 28 Apr 2022 14:03:58 -0500 Subject: [PATCH 232/342] improvements to swap/stomp channel --- src/engine/engine.cpp | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index b5b60b90b..3d48c16cb 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -664,6 +664,20 @@ void DivEngine::swapChannels(int src, int dest) { song.pat[src].effectCols^=song.pat[dest].effectCols; song.pat[dest].effectCols^=song.pat[src].effectCols; song.pat[src].effectCols^=song.pat[dest].effectCols; + + String prevChanName=song.chanName[src]; + String prevChanShortName=song.chanShortName[src]; + bool prevChanShow=song.chanShow[src]; + bool prevChanCollapse=song.chanCollapse[src]; + + song.chanName[src]=song.chanName[dest]; + song.chanShortName[src]=song.chanShortName[dest]; + song.chanShow[src]=song.chanShow[dest]; + song.chanCollapse[src]=song.chanCollapse[dest]; + song.chanName[dest]=prevChanName; + song.chanShortName[dest]=prevChanShortName; + song.chanShow[dest]=prevChanShow; + song.chanCollapse[dest]=prevChanCollapse; } void DivEngine::stompChannel(int ch) { @@ -673,6 +687,10 @@ void DivEngine::stompChannel(int ch) { } song.pat[ch].wipePatterns(); song.pat[ch].effectCols=1; + song.chanName[ch]=""; + song.chanShortName[ch]=""; + song.chanShow[ch]=true; + song.chanCollapse[ch]=false; } void DivEngine::swapChannelsP(int src, int dest) { From 2a1d45fa355c1444804cc8236cad9c7f96587c54 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 28 Apr 2022 15:07:28 -0500 Subject: [PATCH 233/342] update format.md for eventual ExtCh extra systems --- papers/format.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/papers/format.md b/papers/format.md index feafbe603..570529c4e 100644 --- a/papers/format.md +++ b/papers/format.md @@ -213,6 +213,8 @@ size | description | - 0xb3: Yamaha Y8950 drums - 12 channels | - 0xb4: Konami SCC+ - 5 channels | - 0xb5: tildearrow Sound Unit - 8 channels + | - 0xb6: OPN extended - 9 channels + | - 0xb7: PC-98 extended - 19 channels | - 0xde: YM2610B extended - 19 channels | - 0xe0: QSound - 19 channels | - 0xfd: Dummy System - 8 channels From 2ac0e8af424c7bdb9ba8a0bb863a126b1da68e50 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 28 Apr 2022 18:32:24 -0500 Subject: [PATCH 234/342] further improve channel allocation --- src/engine/engine.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 3d48c16cb..c728e2152 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -2158,6 +2158,7 @@ void DivEngine::noteOff(int chan) { void DivEngine::autoNoteOn(int ch, int ins, int note, int vol) { bool isViable[DIV_MAX_CHANS]; bool canPlayAnyway=false; + bool notInViableChannel=false; if (midiBaseChan<0) midiBaseChan=0; if (midiBaseChan>=chans) midiBaseChan=chans-1; int finalChan=midiBaseChan; @@ -2171,6 +2172,7 @@ void DivEngine::autoNoteOn(int ch, int ins, int note, int vol) { // 1. check which channels are viable for this instrument DivInstrument* insInst=getIns(ins); + if (getPreferInsType(finalChan)!=insInst->type && getPreferInsSecondType(finalChan)!=insInst->type) notInViableChannel=true; for (int i=0; i=song.insLen || getPreferInsType(i)==insInst->type || getPreferInsSecondType(i)==insInst->type) { if (insInst->type==DIV_INS_OPL) { @@ -2193,7 +2195,7 @@ void DivEngine::autoNoteOn(int ch, int ins, int note, int vol) { // 2. find a free channel do { - if (isViable[finalChan] && chan[finalChan].midiNote==-1 && (insInst->type==DIV_INS_OPL || getChannelType(finalChan)==finalChanType)) { + if (isViable[finalChan] && chan[finalChan].midiNote==-1 && (insInst->type==DIV_INS_OPL || getChannelType(finalChan)==finalChanType || notInViableChannel)) { chan[finalChan].midiNote=note; chan[finalChan].midiAge=midiAgeCounter++; pendingNotes.push(DivNoteEvent(finalChan,ins,note,vol,true)); @@ -2207,7 +2209,7 @@ void DivEngine::autoNoteOn(int ch, int ins, int note, int vol) { // 3. find the oldest channel int candidate=finalChan; do { - if (isViable[finalChan] && (insInst->type==DIV_INS_OPL || getChannelType(finalChan)==finalChanType) && chan[finalChan].midiAgetype==DIV_INS_OPL || getChannelType(finalChan)==finalChanType || notInViableChannel) && chan[finalChan].midiAge=chans) { From 5567746e0bec407a2f1bba376c701a8c3a4cb76d Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 28 Apr 2022 23:58:11 -0500 Subject: [PATCH 235/342] QSound: implement panning macro - PLEASE READ the panning strategy for QSound has changed! it's now 08xy where x is left and y is right (muting is not possible though!) this makes it consistent with other chips, plus QSound's pan range was 32 anyway in order to toggle the QSound effect use effect 12xx --- TODO.md | 2 -- papers/doc/7-systems/qsound.md | 9 +++++---- src/engine/dispatch.h | 1 + src/engine/engine.cpp | 25 +++++++++++++++++++++++++ src/engine/engine.h | 4 ++++ src/engine/platform/qsound.cpp | 19 ++++++++++++++++++- src/engine/platform/qsound.h | 4 +++- src/engine/playback.cpp | 4 ++++ src/engine/song.h | 4 +++- src/gui/insEdit.cpp | 26 ++++++++++++++++++++++++-- 10 files changed, 87 insertions(+), 11 deletions(-) diff --git a/TODO.md b/TODO.md index 341d66dde..77eb864fd 100644 --- a/TODO.md +++ b/TODO.md @@ -1,7 +1,5 @@ # to-do for 0.6pre1 -- panning macro - - QSound? - piano/input pad - note input via piano - input pad diff --git a/papers/doc/7-systems/qsound.md b/papers/doc/7-systems/qsound.md index 9da79d822..5d6078aa7 100644 --- a/papers/doc/7-systems/qsound.md +++ b/papers/doc/7-systems/qsound.md @@ -12,7 +12,8 @@ There are also 3 ADPCM channels, however they cannot be used in Furnace yet. The # effects -- `08xx`: Set panning. Valid range is 00-20. 00 for full left, 10 for center and 20 for full right. It is also possible to bypass the QSound algorithm by using the range 30-50. -- `10xx`: Set echo feedback level. This effect will apply to all channels. -- `11xx`: Set echo level. -- `3xxx`: Set the length of the echo delay buffer. +- `10xx`: set echo feedback level. + - this effect will apply to all channels. +- `11xx`: set echo level. +- `12xx`: toggle QSound algorithm (on by default). +- `3xxx`: set the length of the echo delay buffer. diff --git a/src/engine/dispatch.h b/src/engine/dispatch.h index 1ab4cba15..d86f1b20f 100644 --- a/src/engine/dispatch.h +++ b/src/engine/dispatch.h @@ -125,6 +125,7 @@ enum DivDispatchCmds { DIV_CMD_QSOUND_ECHO_FEEDBACK, DIV_CMD_QSOUND_ECHO_DELAY, DIV_CMD_QSOUND_ECHO_LEVEL, + DIV_CMD_QSOUND_SURROUND, DIV_CMD_X1_010_ENVELOPE_SHAPE, DIV_CMD_X1_010_ENVELOPE_ENABLE, diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index c728e2152..c42f5cc5d 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -1041,6 +1041,28 @@ int DivEngine::calcFreq(int base, int pitch, bool period, int octave, int pitch2 base+((pitch*octave)>>1)+pitch2; } +int DivEngine::convertPanSplitToLinear(unsigned int val, unsigned char bits, int range) { + int panL=val>>bits; + int panR=val&((1<range) val=range; + int maxV=(1<maxV) panL=maxV; + if (panR>maxV) panR=maxV; + return (panL<name=fmt::sprintf("Instrument %d",insCount); ins->type=prefType; saveLock.lock(); diff --git a/src/engine/engine.h b/src/engine/engine.h index 441096a23..9f971d7a8 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -475,6 +475,10 @@ class DivEngine { // calculate frequency/period int calcFreq(int base, int pitch, bool period=false, int octave=0, int pitch2=0); + // convert panning formats + int convertPanSplitToLinear(unsigned int val, unsigned char bits, int range); + unsigned int convertPanLinearToSplit(int val, unsigned char bits, int range); + // find song loop position void walkSong(int& loopOrder, int& loopRow, int& loopEnd); diff --git a/src/engine/platform/qsound.cpp b/src/engine/platform/qsound.cpp index 8450d965b..9dd34589a 100644 --- a/src/engine/platform/qsound.cpp +++ b/src/engine/platform/qsound.cpp @@ -257,6 +257,9 @@ const char* DivPlatformQSound::getEffectName(unsigned char effect) { case 0x11: return "11xx: Set channel echo level (00 to FF)"; break; + case 0x12: + return "12xx: Toggle QSound algorithm (0: disabled; 1: enabled)"; + break; default: if ((effect & 0xf0) == 0x30) { return "3xxx: Set echo delay buffer length (000 to AA5)"; @@ -335,6 +338,15 @@ void DivPlatformQSound::tick(bool sysTick) { } chan[i].freqChanged=true; } + if (chan[i].std.panL.had) { // panning + chan[i].panning=chan[i].std.panL.val+16; + } + if (chan[i].std.panR.had) { // surround + chan[i].surround=chan[i].std.panR.val; + } + if (chan[i].std.panL.had || chan[i].std.panR.had) { + immWrite(Q1_PAN+i,chan[i].panning+0x110+(chan[i].surround?0:0x30)); + } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { //DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_AMIGA); chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,2,chan[i].pitch2); @@ -429,7 +441,8 @@ int DivPlatformQSound::dispatch(DivCommand c) { return chan[c.chan].outVol; break; case DIV_CMD_PANNING: - immWrite(Q1_PAN+c.chan, c.value + 0x110); + chan[c.chan].panning=parent->convertPanSplitToLinear(c.value,4,32); + immWrite(Q1_PAN+c.chan,chan[c.chan].panning+0x110+(chan[c.chan].surround?0:0x30)); break; case DIV_CMD_QSOUND_ECHO_LEVEL: immWrite(Q1_ECHO+c.chan, c.value << 7); @@ -440,6 +453,10 @@ int DivPlatformQSound::dispatch(DivCommand c) { case DIV_CMD_QSOUND_ECHO_DELAY: immWrite(Q1_ECHO_LENGTH, (c.value > 2725 ? 0xfff : 0xfff - (2725 - c.value))); break; + case DIV_CMD_QSOUND_SURROUND: + chan[c.chan].surround=c.value; + immWrite(Q1_PAN+c.chan,chan[c.chan].panning+0x110+(chan[c.chan].surround?0:0x30)); + break; case DIV_CMD_PITCH: chan[c.chan].pitch=c.value; chan[c.chan].freqChanged=true; diff --git a/src/engine/platform/qsound.h b/src/engine/platform/qsound.h index 1eb9b2751..6eb178d08 100644 --- a/src/engine/platform/qsound.h +++ b/src/engine/platform/qsound.h @@ -33,7 +33,7 @@ class DivPlatformQSound: public DivDispatch { int sample, wave, ins; int note; int panning; - bool active, insChanged, freqChanged, keyOn, keyOff, inPorta, useWave; + bool active, insChanged, freqChanged, keyOn, keyOff, inPorta, useWave, surround; int vol, outVol; DivMacroInt std; void macroInit(DivInstrument* which) { @@ -57,6 +57,8 @@ class DivPlatformQSound: public DivDispatch { keyOn(false), keyOff(false), inPorta(false), + useWave(false), + surround(true), vol(255), outVol(255) {} }; diff --git a/src/engine/playback.cpp b/src/engine/playback.cpp index 13cc495b9..5fd031167 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -127,6 +127,7 @@ const char* cmdName[]={ "QSOUND_ECHO_FEEDBACK", "QSOUND_ECHO_DELAY", "QSOUND_ECHO_LEVEL", + "QSOUND_SURROUND", "X1_010_ENVELOPE_SHAPE", "X1_010_ENVELOPE_ENABLE", @@ -434,6 +435,9 @@ bool DivEngine::perSystemEffect(int ch, unsigned char effect, unsigned char effe case 0x11: // echo level dispatchCmd(DivCommand(DIV_CMD_QSOUND_ECHO_LEVEL,ch,effectVal)); break; + case 0x12: // surround + dispatchCmd(DivCommand(DIV_CMD_QSOUND_SURROUND,ch,effectVal)); + break; default: if ((effect&0xf0)==0x30) { dispatchCmd(DivCommand(DIV_CMD_QSOUND_ECHO_DELAY,ch,((effect & 0x0f) << 8) | effectVal)); diff --git a/src/engine/song.h b/src/engine/song.h index 087d8c7b0..8e67cce83 100644 --- a/src/engine/song.h +++ b/src/engine/song.h @@ -347,7 +347,7 @@ struct DivSong { bool chanShow[DIV_MAX_CHANS]; bool chanCollapse[DIV_MAX_CHANS]; - DivInstrument nullIns, nullInsOPLL, nullInsOPL; + DivInstrument nullIns, nullInsOPLL, nullInsOPL, nullInsQSound; DivWavetable nullWave; DivSample nullSample; @@ -473,6 +473,8 @@ struct DivSong { nullInsOPL.fm.op[1].rr=12; nullInsOPL.fm.op[1].mult=1; nullInsOPL.name="This is a bug! Report!"; + + nullInsQSound.std.panLMacro.mode=true; } }; diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index 716683358..fbd843581 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -212,6 +212,7 @@ const char* dualWSEffects[7]={ const char* macroAbsoluteMode="Fixed"; const char* macroRelativeMode="Relative"; +const char* macroQSoundMode="QSound"; const char* macroDummyMode="Bug"; @@ -2839,8 +2840,10 @@ void FurnaceGUI::drawInsEdit() { } if (ins->type==DIV_INS_SAA1099) ex1Max=8; + int panMin=0; int panMax=0; bool panSingle=false; + bool panSingleNoBit=false; if (ins->type==DIV_INS_FM || ins->type==DIV_INS_OPL || ins->type==DIV_INS_GB || ins->type==DIV_INS_OPZ || ins->type==DIV_INS_VERA) { panMax=1; panSingle=true; @@ -2851,6 +2854,15 @@ void FurnaceGUI::drawInsEdit() { if (ins->type==DIV_INS_X1_010 || ins->type==DIV_INS_PCE || ins->type==DIV_INS_MIKEY || ins->type==DIV_INS_SAA1099) { panMax=15; } + if (ins->type==DIV_INS_AMIGA && ins->std.panLMacro.mode) { + panMin=-16; + panMax=16; + } + if (ins->type==DIV_INS_SU) { + panMin=-127; + panMax=127; + panSingleNoBit=true; + } if (settings.macroView==0) { // modern view MACRO_BEGIN(28*dpiScale); @@ -2872,8 +2884,18 @@ void FurnaceGUI::drawInsEdit() { if (panSingle) { NORMAL_MACRO(ins->std.panLMacro,0,2,"panL","Panning",32,ins->std.panLMacro.open,true,panBits,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[13],0,panMax,NULL,false); } else { - NORMAL_MACRO(ins->std.panLMacro,0,panMax,"panL","Panning (left)",(31+panMax),ins->std.panLMacro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[13],0,panMax,NULL,false); - NORMAL_MACRO(ins->std.panRMacro,0,panMax,"panR","Panning (right)",(31+panMax),ins->std.panRMacro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[14],0,panMax,NULL,false); + if (panSingleNoBit || (ins->type==DIV_INS_AMIGA && ins->std.panLMacro.mode)) { + NORMAL_MACRO(ins->std.panLMacro,panMin,panMax,"panL","Panning",(31+panMax-panMin),ins->std.panLMacro.open,false,NULL,false,NULL,0,0,0,0,(ins->type==DIV_INS_AMIGA),(ins->type==DIV_INS_AMIGA?1:0),macroQSoundMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[13],panMin,panMax,NULL,false); + } else { + NORMAL_MACRO(ins->std.panLMacro,panMin,panMax,"panL","Panning (left)",(31+panMax-panMin),ins->std.panLMacro.open,false,NULL,false,NULL,0,0,0,0,(ins->type==DIV_INS_AMIGA),(ins->type==DIV_INS_AMIGA?1:0),macroQSoundMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[13],panMin,panMax,NULL,false); + } + if (!panSingleNoBit) { + if (ins->type==DIV_INS_AMIGA && ins->std.panLMacro.mode) { + NORMAL_MACRO(ins->std.panRMacro,0,1,"panR","Surround",32,ins->std.panRMacro.open,true,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[14],0,1,NULL,false); + } else { + NORMAL_MACRO(ins->std.panRMacro,panMin,panMax,"panR","Panning (right)",(31+panMax-panMin),ins->std.panRMacro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[14],panMin,panMax,NULL,false); + } + } } } NORMAL_MACRO(ins->std.pitchMacro,pitchMacroScroll,pitchMacroScroll+160,"pitch","Pitch",160,ins->std.pitchMacro.open,false,NULL,true,&pitchMacroScroll,-2048,2047,0,0,true,1,macroRelativeMode,uiColors[GUI_COLOR_MACRO_PITCH],mmlString[15],-2048,2047,NULL,!ins->std.pitchMacro.mode); From abd42cbb03c42e68a40b993df9175cbeb9ce6990 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 29 Apr 2022 00:08:56 -0500 Subject: [PATCH 236/342] SoundUnit: implement panning --- src/engine/platform/su.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/engine/platform/su.cpp b/src/engine/platform/su.cpp index 6efa0a482..4733e5b24 100644 --- a/src/engine/platform/su.cpp +++ b/src/engine/platform/su.cpp @@ -270,7 +270,7 @@ int DivPlatformSoundUnit::dispatch(DivCommand c) { break; } case DIV_CMD_PANNING: { - chan[c.chan].pan=c.value; + chan[c.chan].pan=parent->convertPanSplitToLinear(c.value,4,254)-127; chWrite(c.chan,0x03,chan[c.chan].pan); break; } From 8500fa4c4ced13f4c57f942ace59afaf1c8648ab Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 29 Apr 2022 00:18:51 -0500 Subject: [PATCH 237/342] NES: add DMC write effect --- papers/doc/7-systems/nes.md | 7 +++++-- src/engine/dispatch.h | 1 + src/engine/platform/nes.cpp | 6 ++++++ src/engine/playback.cpp | 4 ++++ 4 files changed, 16 insertions(+), 2 deletions(-) diff --git a/papers/doc/7-systems/nes.md b/papers/doc/7-systems/nes.md index 3de0e3076..8ce5d808a 100644 --- a/papers/doc/7-systems/nes.md +++ b/papers/doc/7-systems/nes.md @@ -2,10 +2,13 @@ the console from Nintendo that plays Super Mario Bros. and helped revive the agonizing video game market in the US during mid-80s. -also known as Famicom. It is a five-channel PSG: first two channels play pulse wave with three different duty cycles, third is a fixed-volume triangle channel, fourth is a noise channel (can work in both pseudo-random and periodic modes) and fifth is a (D)PCM sample channel +also known as Famicom. It is a five-channel PSG: first two channels play pulse wave with three different duty cycles, third is a fixed-volume triangle channel, fourth is a noise channel (can work in both pseudo-random and periodic modes) and fifth is a (D)PCM sample channel # effects +- `11xx`: write to delta modulation counter. + - this may be used to attenuate the triangle and noise channels. + - will not work if a sample is playing. - `12xx`: set duty cycle or noise mode of channel. - may be 0-3 for the pulse channels and 0-1 for the noise channel. - `13xy`: setup sweep up. @@ -15,4 +18,4 @@ also known as Famicom. It is a five-channel PSG: first two channels play pulse w - `14xy`: setup sweep down. - `x` is the time. - `y` is the shift. - - set to 0 to disable it. + - set to 0 to disable it. \ No newline at end of file diff --git a/src/engine/dispatch.h b/src/engine/dispatch.h index d86f1b20f..a12ccbfe8 100644 --- a/src/engine/dispatch.h +++ b/src/engine/dispatch.h @@ -86,6 +86,7 @@ enum DivDispatchCmds { DIV_CMD_PCE_LFO_SPEED, // (speed) DIV_CMD_NES_SWEEP, // (direction, value) + DIV_CMD_NES_DMC, // (value) DIV_CMD_C64_CUTOFF, // (value) DIV_CMD_C64_RESONANCE, // (value) diff --git a/src/engine/platform/nes.cpp b/src/engine/platform/nes.cpp index b61abde0d..f7a290662 100644 --- a/src/engine/platform/nes.cpp +++ b/src/engine/platform/nes.cpp @@ -59,6 +59,9 @@ const char** DivPlatformNES::getRegisterSheet() { const char* DivPlatformNES::getEffectName(unsigned char effect) { switch (effect) { + case 0x11: + return "Write to delta modulation counter (0 to 7F)"; + break; case 0x12: return "12xx: Set duty cycle/noise mode (pulse: 0 to 3; noise: 0 or 1)"; break; @@ -420,6 +423,9 @@ int DivPlatformNES::dispatch(DivCommand c) { } rWrite(0x4001+(c.chan*4),chan[c.chan].sweep); break; + case DIV_CMD_NES_DMC: + rWrite(0x4011,c.value&0x7f); + break; case DIV_CMD_SAMPLE_BANK: sampleBank=c.value; if (sampleBank>(parent->song.sample.size()/12)) { diff --git a/src/engine/playback.cpp b/src/engine/playback.cpp index 5fd031167..29899f0d9 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -88,6 +88,7 @@ const char* cmdName[]={ "PCE_LFO_SPEED", "NES_SWEEP", + "NES_DMC", "C64_CUTOFF", "C64_RESONANCE", @@ -317,6 +318,9 @@ bool DivEngine::perSystemEffect(int ch, unsigned char effect, unsigned char effe case DIV_SYSTEM_NES: case DIV_SYSTEM_MMC5: switch (effect) { + case 0x11: // DMC write + dispatchCmd(DivCommand(DIV_CMD_NES_DMC,ch,effectVal)); + break; case 0x12: // duty or noise mode dispatchCmd(DivCommand(DIV_CMD_STD_NOISE_MODE,ch,effectVal)); break; From 3a354ea42b50531028aac8abe49bfe1df2927883 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 29 Apr 2022 00:18:55 -0500 Subject: [PATCH 238/342] oops --- papers/doc/7-systems/nes.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/papers/doc/7-systems/nes.md b/papers/doc/7-systems/nes.md index 8ce5d808a..45ce95bc3 100644 --- a/papers/doc/7-systems/nes.md +++ b/papers/doc/7-systems/nes.md @@ -2,7 +2,7 @@ the console from Nintendo that plays Super Mario Bros. and helped revive the agonizing video game market in the US during mid-80s. -also known as Famicom. It is a five-channel PSG: first two channels play pulse wave with three different duty cycles, third is a fixed-volume triangle channel, fourth is a noise channel (can work in both pseudo-random and periodic modes) and fifth is a (D)PCM sample channel +also known as Famicom. It is a five-channel PSG: first two channels play pulse wave with three different duty cycles, third is a fixed-volume triangle channel, fourth is a noise channel (can work in both pseudo-random and periodic modes) and fifth is a (D)PCM sample channel. # effects From 2583b18f86d23be88cb3cd1eef59a75f57aaaf8b Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 29 Apr 2022 02:24:27 -0500 Subject: [PATCH 239/342] update to-do list --- TODO.md | 1 + 1 file changed, 1 insertion(+) diff --git a/TODO.md b/TODO.md index 77eb864fd..c813fbfe8 100644 --- a/TODO.md +++ b/TODO.md @@ -39,3 +39,4 @@ - Apply button in settings - better FM chip names (number and codename) - find and replace +- precise panning effects (80xx linear, 81xx/82xx per-channel) From ee4d4c42513be9b78f5f2374ca40089de9c75f86 Mon Sep 17 00:00:00 2001 From: Zumi Daxuya Date: Fri, 29 Apr 2022 15:54:10 +0700 Subject: [PATCH 240/342] GUI: add chip randomizer button --- src/gui/newSong.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/gui/newSong.cpp b/src/gui/newSong.cpp index d6188b3e5..06aeb4bd6 100644 --- a/src/gui/newSong.cpp +++ b/src/gui/newSong.cpp @@ -70,6 +70,14 @@ void FurnaceGUI::drawNewSong() { ImGui::CloseCurrentPopup(); } + ImGui::SameLine(); + + if (ImGui::Button("I'm feeling lucky")) { + FurnaceGUISysCategory* newSystemCat=&sysCategories[rand()%sysCategories.size()]; + nextDesc=newSystemCat->systems[rand()%newSystemCat->systems.size()].definition.data(); + accepted=true; + } + if (accepted) { e->createNew(nextDesc); undoHist.clear(); From a9a01bad4a130b48a50c32dbda7060cab7d97ff9 Mon Sep 17 00:00:00 2001 From: Natt Akuma <77432377+akumanatt@users.noreply.github.com> Date: Fri, 29 Apr 2022 16:32:30 +0700 Subject: [PATCH 241/342] Have host sound chip appear before expansions So that they are consistent with NES expansions. For X16, the VERA is more integral to the system and has lower I/O address than YM2151 and YM2151 was once an expansion chip during development. --- src/gui/presets.cpp | 46 ++++++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/src/gui/presets.cpp b/src/gui/presets.cpp index d6e439a68..77bd9b10c 100644 --- a/src/gui/presets.cpp +++ b/src/gui/presets.cpp @@ -366,29 +366,29 @@ void FurnaceGUI::initSystemPresets() { )); cat.systems.push_back(FurnaceGUISysDef( "Commodore 64 (6581 SID + Sound Expander)", { - DIV_SYSTEM_OPL, 64, 0, 0, DIV_SYSTEM_C64_6581, 64, 0, 1, + DIV_SYSTEM_OPL, 64, 0, 0, 0 } )); cat.systems.push_back(FurnaceGUISysDef( "Commodore 64 (6581 SID + Sound Expander with drums mode)", { - DIV_SYSTEM_OPL_DRUMS, 64, 0, 0, DIV_SYSTEM_C64_6581, 64, 0, 1, + DIV_SYSTEM_OPL_DRUMS, 64, 0, 0, 0 } )); cat.systems.push_back(FurnaceGUISysDef( "Commodore 64 (8580 SID + Sound Expander)", { - DIV_SYSTEM_OPL, 64, 0, 0, DIV_SYSTEM_C64_8580, 64, 0, 1, + DIV_SYSTEM_OPL, 64, 0, 0, 0 } )); cat.systems.push_back(FurnaceGUISysDef( "Commodore 64 (8580 SID + Sound Expander with drums mode)", { - DIV_SYSTEM_OPL_DRUMS, 64, 0, 0, DIV_SYSTEM_C64_8580, 64, 0, 1, + DIV_SYSTEM_OPL_DRUMS, 64, 0, 0, 0 } )); @@ -406,22 +406,22 @@ void FurnaceGUI::initSystemPresets() { )); cat.systems.push_back(FurnaceGUISysDef( "MSX + SFG-01", { - DIV_SYSTEM_YM2151, 64, 0, 0, DIV_SYSTEM_AY8910, 64, 0, 16, + DIV_SYSTEM_YM2151, 64, 0, 0, 0 } )); cat.systems.push_back(FurnaceGUISysDef( "MSX + MSX-MUSIC", { - DIV_SYSTEM_OPLL, 64, 0, 0, DIV_SYSTEM_AY8910, 64, 0, 16, + DIV_SYSTEM_OPLL, 64, 0, 0, 0 } )); cat.systems.push_back(FurnaceGUISysDef( "MSX + MSX-MUSIC (drums mode)", { - DIV_SYSTEM_OPLL_DRUMS, 64, 0, 0, DIV_SYSTEM_AY8910, 64, 0, 16, + DIV_SYSTEM_OPLL_DRUMS, 64, 0, 0, 0 } )); @@ -463,101 +463,101 @@ void FurnaceGUI::initSystemPresets() { )); cat.systems.push_back(FurnaceGUISysDef( "PC + Covox Sound Master", { - DIV_SYSTEM_AY8930, 64, 0, 3, DIV_SYSTEM_PCSPKR, 64, 0, 0, + DIV_SYSTEM_AY8930, 64, 0, 3, 0 } )); cat.systems.push_back(FurnaceGUISysDef( "PC + SSI 2001", { - DIV_SYSTEM_C64_6581, 64, 0, 2, DIV_SYSTEM_PCSPKR, 64, 0, 0, + DIV_SYSTEM_C64_6581, 64, 0, 2, 0 } )); cat.systems.push_back(FurnaceGUISysDef( "PC + Game Blaster", { - DIV_SYSTEM_SAA1099, 64, 0, 1, - DIV_SYSTEM_SAA1099, 64, 0, 1, DIV_SYSTEM_PCSPKR, 64, 0, 0, + DIV_SYSTEM_SAA1099, 64, 0, 1, + DIV_SYSTEM_SAA1099, 64, 0, 1, 0 } )); cat.systems.push_back(FurnaceGUISysDef( "PC + AdLib/Sound Blaster", { - DIV_SYSTEM_OPL2, 64, 0, 0, DIV_SYSTEM_PCSPKR, 64, 0, 0, + DIV_SYSTEM_OPL2, 64, 0, 0, 0 } )); cat.systems.push_back(FurnaceGUISysDef( "PC + AdLib/Sound Blaster (drums mode)", { - DIV_SYSTEM_OPL2_DRUMS, 64, 0, 0, DIV_SYSTEM_PCSPKR, 64, 0, 0, + DIV_SYSTEM_OPL2_DRUMS, 64, 0, 0, 0 } )); cat.systems.push_back(FurnaceGUISysDef( "PC + Sound Blaster w/Game Blaster Compatible", { + DIV_SYSTEM_PCSPKR, 64, 0, 0, DIV_SYSTEM_OPL2, 64, 0, 0, DIV_SYSTEM_SAA1099, 64, 0, 1, DIV_SYSTEM_SAA1099, 64, 0, 1, - DIV_SYSTEM_PCSPKR, 64, 0, 0, 0 } )); cat.systems.push_back(FurnaceGUISysDef( "PC + Sound Blaster w/Game Blaster Compatible (drums mode)", { + DIV_SYSTEM_PCSPKR, 64, 0, 0, DIV_SYSTEM_OPL2_DRUMS, 64, 0, 0, DIV_SYSTEM_SAA1099, 64, 0, 1, DIV_SYSTEM_SAA1099, 64, 0, 1, - DIV_SYSTEM_PCSPKR, 64, 0, 0, 0 } )); cat.systems.push_back(FurnaceGUISysDef( "PC + Sound Blaster Pro", { + DIV_SYSTEM_PCSPKR, 64, 0, 0, DIV_SYSTEM_OPL2, 64, -127, 0, DIV_SYSTEM_OPL2, 64, 127, 0, - DIV_SYSTEM_PCSPKR, 64, 0, 0, 0 } )); cat.systems.push_back(FurnaceGUISysDef( "PC + Sound Blaster Pro (drums mode)", { + DIV_SYSTEM_PCSPKR, 64, 0, 0, DIV_SYSTEM_OPL2_DRUMS, 64, -127, 0, DIV_SYSTEM_OPL2_DRUMS, 64, 127, 0, - DIV_SYSTEM_PCSPKR, 64, 0, 0, 0 } )); cat.systems.push_back(FurnaceGUISysDef( "PC + Sound Blaster Pro 2", { - DIV_SYSTEM_OPL3, 64, 0, 0, DIV_SYSTEM_PCSPKR, 64, 0, 0, + DIV_SYSTEM_OPL3, 64, 0, 0, 0 } )); cat.systems.push_back(FurnaceGUISysDef( "PC + Sound Blaster Pro 2 (drums mode)", { - DIV_SYSTEM_OPL3_DRUMS, 64, 0, 0, DIV_SYSTEM_PCSPKR, 64, 0, 0, + DIV_SYSTEM_OPL3_DRUMS, 64, 0, 0, 0 } )); cat.systems.push_back(FurnaceGUISysDef( "PC + PC-FXGA", { - DIV_SYSTEM_PCE, 64, 0, 0, // HuC6230 (WSG from HuC6280 but with built in 2 OKI ADPCM playback engines) DIV_SYSTEM_PCSPKR, 64, 0, 0, + DIV_SYSTEM_PCE, 64, 0, 0, // HuC6230 (WSG from HuC6280 but with built in 2 OKI ADPCM playback engines) 0 } )); cat.systems.push_back(FurnaceGUISysDef( "PC + SAAYM", { + DIV_SYSTEM_PCSPKR, 64, 0, 0, DIV_SYSTEM_YM2151, 64, 0, 0, // 3.58MHz or 4MHz selectable via jumper DIV_SYSTEM_SAA1099, 64, 0, 1, // 7.16MHz or 8MHz selectable via jumper DIV_SYSTEM_SAA1099, 64, 0, 1, // "" - DIV_SYSTEM_PCSPKR, 64, 0, 0, 0 } )); @@ -584,8 +584,8 @@ void FurnaceGUI::initSystemPresets() { ));*/ cat.systems.push_back(FurnaceGUISysDef( "Commander X16", { - DIV_SYSTEM_YM2151, 64, 0, 0, DIV_SYSTEM_VERA, 64, 0, 0, + DIV_SYSTEM_YM2151, 64, 0, 0, 0 } )); @@ -651,8 +651,8 @@ void FurnaceGUI::initSystemPresets() { )); cat.systems.push_back(FurnaceGUISysDef( "Seta 1 + FM addon", { - DIV_SYSTEM_YM2612, 64, 0, 2, // Discrete YM3438 DIV_SYSTEM_X1_010, 64, 0, 0, + DIV_SYSTEM_YM2612, 64, 0, 2, // Discrete YM3438 0 } )); From 0817fdeb84b964022882541f7b623e1b972d1986 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 29 Apr 2022 04:34:20 -0500 Subject: [PATCH 242/342] GUI: possibly fix an instrument saving issue --- src/gui/gui.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 56e046f08..41c7b9552 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -2903,6 +2903,10 @@ bool FurnaceGUI::loop() { if (fileDialog->render(ImVec2(600.0f*dpiScale,400.0f*dpiScale),ImVec2(scrW*dpiScale,scrH*dpiScale))) { bool openOpen=false; //ImGui::GetIO().ConfigFlags&=~ImGuiConfigFlags_NavEnableKeyboard; + if (curFileDialog==GUI_FILE_INS_OPEN && prevIns!=-3) { + curIns=prevIns; + prevIns=-3; + } switch (curFileDialog) { case GUI_FILE_OPEN: case GUI_FILE_SAVE: @@ -2947,9 +2951,6 @@ bool FurnaceGUI::loop() { workingDirLayout=fileDialog->getPath()+DIR_SEPARATOR_STR; break; } - if (prevIns!=-3) { - curIns=prevIns; - } if (fileDialog->accepted()) { fileName=fileDialog->getFileName(); if (fileName!="") { From aeea8a49128f0efb5ef4de28e5ff562c138e24b1 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 29 Apr 2022 04:42:18 -0500 Subject: [PATCH 243/342] GUI: prevent division by zero --- src/gui/newSong.cpp | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/src/gui/newSong.cpp b/src/gui/newSong.cpp index 06aeb4bd6..2bf11fb5c 100644 --- a/src/gui/newSong.cpp +++ b/src/gui/newSong.cpp @@ -66,16 +66,24 @@ void FurnaceGUI::drawNewSong() { ImGui::EndTable(); } - if (ImGui::Button("Cancel")) { - ImGui::CloseCurrentPopup(); + if (ImGui::Button("I'm feeling lucky")) { + if (sysCategories.size()==0) { + ImGui::CloseCurrentPopup(); + } else { + FurnaceGUISysCategory* newSystemCat=&sysCategories[rand()%sysCategories.size()]; + if (newSystemCat->systems.size()==0) { + ImGui::CloseCurrentPopup(); + } else { + nextDesc=newSystemCat->systems[rand()%newSystemCat->systems.size()].definition.data(); + accepted=true; + } + } } ImGui::SameLine(); - if (ImGui::Button("I'm feeling lucky")) { - FurnaceGUISysCategory* newSystemCat=&sysCategories[rand()%sysCategories.size()]; - nextDesc=newSystemCat->systems[rand()%newSystemCat->systems.size()].definition.data(); - accepted=true; + if (ImGui::Button("Cancel")) { + ImGui::CloseCurrentPopup(); } if (accepted) { @@ -95,4 +103,4 @@ void FurnaceGUI::drawNewSong() { updateWindowTitle(); ImGui::CloseCurrentPopup(); } -} \ No newline at end of file +} From 635bd72904ec83fba8855936595e767a16c1df68 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 29 Apr 2022 05:39:18 -0500 Subject: [PATCH 244/342] GUI: sample/macro zoom with ctrl-wheel --- src/gui/gui.cpp | 1 + src/gui/gui.h | 1 + src/gui/insEdit.cpp | 7 ++++++- src/gui/sampleEdit.cpp | 11 +++++++++++ 4 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 41c7b9552..74a303542 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -3788,6 +3788,7 @@ FurnaceGUI::FurnaceGUI(): preserveChanPos(false), vgmExportVersion(0x171), drawHalt(10), + macroPointSize(16), curFileDialog(GUI_FILE_OPEN), warnAction(GUI_WARN_OPEN), postWarnAction(GUI_WARN_GENERIC), diff --git a/src/gui/gui.h b/src/gui/gui.h index 14b735b22..31ab566f1 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -739,6 +739,7 @@ class FurnaceGUI { bool willExport[32]; int vgmExportVersion; int drawHalt; + int macroPointSize; FurnaceGUIFileDialogs curFileDialog; FurnaceGUIWarnings warnAction; diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index fbd843581..0355e7fe7 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -1128,6 +1128,11 @@ void FurnaceGUI::drawGBEnv(unsigned char vol, unsigned char len, unsigned char s processDrags(ImGui::GetMousePos().x,ImGui::GetMousePos().y); \ } \ if (displayLoop) { \ + if (ImGui::IsItemHovered() && ctrlWheeling) { \ + macroPointSize+=wheelY; \ + if (macroPointSize<1) macroPointSize=1; \ + if (macroPointSize>256) macroPointSize=256; \ + } \ if (drawSlider) { \ ImGui::SameLine(); \ CWVSliderInt("##IMacroPos_" macroName,ImVec2(20.0f*dpiScale,displayHeight*dpiScale),sliderVal,sliderLow,sliderHigh); \ @@ -1262,7 +1267,7 @@ if (ImGui::BeginTable("MacroSpace",2)) { \ ImGui::Dummy(ImVec2(120.0f*dpiScale,dpiScale)); \ ImGui::TableNextColumn(); \ float availableWidth=ImGui::GetContentRegionAvail().x-reservedSpace; \ - int totalFit=MIN(127,availableWidth/(16*dpiScale)); \ + int totalFit=MIN(127,availableWidth/MAX(1,macroPointSize*dpiScale)); \ if (macroDragScroll>127-totalFit) { \ macroDragScroll=127-totalFit; \ } \ diff --git a/src/gui/sampleEdit.cpp b/src/gui/sampleEdit.cpp index 53379c992..7882e53c4 100644 --- a/src/gui/sampleEdit.cpp +++ b/src/gui/sampleEdit.cpp @@ -1268,6 +1268,17 @@ void FurnaceGUI::drawSampleEdit() { } if (ImGui::IsItemHovered()) { + if (ctrlWheeling) { + double zoomPercent=100.0/sampleZoom; + zoomPercent+=wheelY*10.0; + if (zoomPercent>10000.0) zoomPercent=10000.0; + if (zoomPercent<1.0) zoomPercent=1.0; + sampleZoom=100.0/zoomPercent; + if (sampleZoom<0.01) sampleZoom=0.01; + sampleZoomAuto=false; + updateSampleTex=true; + } + int posX=-1; int posY=0; ImVec2 pos=ImGui::GetMousePos(); From d16ee7ed4557e9ed89edcedc1a9b169fd6cb1319 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 29 Apr 2022 15:36:03 -0500 Subject: [PATCH 245/342] sysDef oopsie --- src/engine/sysDef.cpp | 43 ++----------------------------------------- 1 file changed, 2 insertions(+), 41 deletions(-) diff --git a/src/engine/sysDef.cpp b/src/engine/sysDef.cpp index 082eb0833..1f7920d7c 100644 --- a/src/engine/sysDef.cpp +++ b/src/engine/sysDef.cpp @@ -312,46 +312,8 @@ DivInstrumentType DivEngine::getPreferInsSecondType(int chan) { } int DivEngine::minVGMVersion(DivSystem which) { - switch (which) { - case DIV_SYSTEM_YM2612: - case DIV_SYSTEM_YM2612_EXT: - case DIV_SYSTEM_SMS: - case DIV_SYSTEM_OPLL: - case DIV_SYSTEM_OPLL_DRUMS: - case DIV_SYSTEM_VRC7: - case DIV_SYSTEM_YM2151: - return 0x150; // due to usage of data blocks - case DIV_SYSTEM_SEGAPCM: - case DIV_SYSTEM_SEGAPCM_COMPAT: - case DIV_SYSTEM_YM2610: - case DIV_SYSTEM_YM2610_EXT: - case DIV_SYSTEM_YM2610_FULL: - case DIV_SYSTEM_YM2610_FULL_EXT: - case DIV_SYSTEM_YM2610B: - case DIV_SYSTEM_YM2610B_EXT: - case DIV_SYSTEM_OPL: - case DIV_SYSTEM_OPL_DRUMS: - case DIV_SYSTEM_OPL2: - case DIV_SYSTEM_OPL2_DRUMS: - case DIV_SYSTEM_OPL3: - case DIV_SYSTEM_OPL3_DRUMS: - case DIV_SYSTEM_AY8910: - case DIV_SYSTEM_AY8930: - return 0x151; - case DIV_SYSTEM_GB: - case DIV_SYSTEM_PCE: - case DIV_SYSTEM_NES: - case DIV_SYSTEM_FDS: - case DIV_SYSTEM_QSOUND: - return 0x161; - case DIV_SYSTEM_SAA1099: - case DIV_SYSTEM_X1_010: - case DIV_SYSTEM_SWAN: - return 0x171; - default: - return 0; - } - return 0; + if (sysDefs[which]==NULL) return 0; + return sysDefs[which]->vgmVersion; } // define systems like: @@ -544,7 +506,6 @@ void DivEngine::registerSystems() { {DIV_INS_PET} ); - // TODO: DIV_INS_SNES sysDefs[DIV_SYSTEM_SNES]=new DivSysDef( "SNES", NULL, 0x87, 0, 8, false, true, 0, false, {"Channel 1", "Channel 2", "Channel 3", "Channel 4", "Channel 5", "Channel 6", "Channel 7", "Channel 8"}, From a8da4f0dba6f63c1661c25647801e1174720a80e Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 29 Apr 2022 17:55:22 -0500 Subject: [PATCH 246/342] GUI: implement sample scroll with mouse wheel --- src/gui/sampleEdit.cpp | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/gui/sampleEdit.cpp b/src/gui/sampleEdit.cpp index 7882e53c4..38a737bdb 100644 --- a/src/gui/sampleEdit.cpp +++ b/src/gui/sampleEdit.cpp @@ -1276,7 +1276,26 @@ void FurnaceGUI::drawSampleEdit() { sampleZoom=100.0/zoomPercent; if (sampleZoom<0.01) sampleZoom=0.01; sampleZoomAuto=false; + int bounds=((int)sample->samples-round(rectSize.x*sampleZoom)); + if (bounds<0) bounds=0; + if (samplePos>bounds) samplePos=bounds; updateSampleTex=true; + } else { + if (wheelY!=0) { + if (!sampleZoomAuto) { + double scrollAmount=MAX(fabs((double)wheelY*sampleZoom*60.0),1.0); + if (wheelY>0) { + samplePos+=scrollAmount; + } else { + samplePos-=scrollAmount; + } + if (samplePos<0) samplePos=0; + int bounds=((int)sample->samples-round(rectSize.x*sampleZoom)); + if (bounds<0) bounds=0; + if (samplePos>bounds) samplePos=bounds; + updateSampleTex=true; + } + } } int posX=-1; From 9c4fed2f4ed71ba03de60780ab52d949e02ded2b Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 29 Apr 2022 22:41:07 -0500 Subject: [PATCH 247/342] GUI: fix sample paste crash --- src/gui/doAction.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/gui/doAction.cpp b/src/gui/doAction.cpp index 28f6cf882..4020dfc7b 100644 --- a/src/gui/doAction.cpp +++ b/src/gui/doAction.cpp @@ -18,6 +18,7 @@ */ #include "gui.h" +#include "../ta-log.h" #include #include @@ -712,6 +713,9 @@ void FurnaceGUI::doAction(int what) { DivSample* sample=e->song.sample[curSample]; sample->prepareUndo(true); int pos=(sampleSelStart==-1 || sampleSelStart==sampleSelEnd)?sample->samples:sampleSelStart; + if (pos>=(int)sample->samples) pos=sample->samples-1; + if (pos<0) pos=0; + logV("paste position: %d",pos); e->lockEngine([this,sample,pos]() { if (!sample->insert(pos,sampleClipboardLen)) { @@ -739,6 +743,8 @@ void FurnaceGUI::doAction(int what) { DivSample* sample=e->song.sample[curSample]; sample->prepareUndo(true); int pos=(sampleSelStart==-1 || sampleSelStart==sampleSelEnd)?0:sampleSelStart; + if (pos>=(int)sample->samples) pos=sample->samples-1; + if (pos<0) pos=0; e->lockEngine([this,sample,pos]() { if (sample->depth==8) { @@ -767,6 +773,8 @@ void FurnaceGUI::doAction(int what) { DivSample* sample=e->song.sample[curSample]; sample->prepareUndo(true); int pos=(sampleSelStart==-1 || sampleSelStart==sampleSelEnd)?0:sampleSelStart; + if (pos>=(int)sample->samples) pos=sample->samples-1; + if (pos<0) pos=0; e->lockEngine([this,sample,pos]() { if (sample->depth==8) { From 2e8634626d17b7e1f3854a14693d777918bfbc5f Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 29 Apr 2022 23:41:14 -0500 Subject: [PATCH 248/342] new panning effects (80/81/82) --- papers/doc/3-pattern/effects.md | 9 ++ src/engine/dispatch.h | 2 +- src/engine/engine.cpp | 10 ++ src/engine/engine.h | 5 +- src/engine/platform/arcade.cpp | 4 +- src/engine/platform/gb.cpp | 8 +- src/engine/platform/genesis.cpp | 4 +- src/engine/platform/genesisext.cpp | 4 +- src/engine/platform/lynx.cpp | 2 +- src/engine/platform/opl.cpp | 4 +- src/engine/platform/pce.cpp | 2 +- src/engine/platform/qsound.cpp | 2 +- src/engine/platform/saa.cpp | 2 +- src/engine/platform/segapcm.cpp | 4 +- src/engine/platform/su.cpp | 2 +- src/engine/platform/swan.cpp | 2 +- src/engine/platform/tx81z.cpp | 4 +- src/engine/platform/vera.cpp | 4 +- src/engine/platform/x1_010.cpp | 5 +- src/engine/platform/ym2610.cpp | 4 +- src/engine/platform/ym2610b.cpp | 4 +- src/engine/platform/ym2610bext.cpp | 4 +- src/engine/platform/ym2610ext.cpp | 4 +- src/engine/playback.cpp | 26 +++- src/gui/effectList.cpp | 22 +-- src/gui/gui.cpp | 22 +-- src/gui/guiConst.cpp | 241 ++++++++++++++++++++++++++++- src/gui/guiConst.h | 3 +- src/gui/pattern.cpp | 31 +--- 29 files changed, 328 insertions(+), 112 deletions(-) diff --git a/papers/doc/3-pattern/effects.md b/papers/doc/3-pattern/effects.md index 773d30373..f72be5013 100644 --- a/papers/doc/3-pattern/effects.md +++ b/papers/doc/3-pattern/effects.md @@ -14,6 +14,15 @@ however, effects are continuous, which means you only need to type it once and t - maximum tremolo depth is -60 volume steps. - `08xy`: set panning. `x` is the left channel and `y` is the right one. - not all systems support this effect. +- `80xx`: set panning (linear). this effect behaves more like other trackers: + - `00` is left. + - `80` is center. + - `FF` is right. + - not all systems support this effect. +- `81xx`: set volume of left channel (from `00` to `FF`). + - not all systems support this effect. +- `82xx`: set volume of right channel (from `00` to `FF`). + - not all systems support this effect. - `09xx`: set speed 1. - `0Axy`: volume slide. - if `x` is 0 then this is a slide down. diff --git a/src/engine/dispatch.h b/src/engine/dispatch.h index a12ccbfe8..21598442b 100644 --- a/src/engine/dispatch.h +++ b/src/engine/dispatch.h @@ -49,7 +49,7 @@ enum DivDispatchCmds { DIV_CMD_GET_VOLMAX, // () -> volMax DIV_CMD_NOTE_PORTA, // (target, speed) -> 2 if target reached DIV_CMD_PITCH, // (pitch) - DIV_CMD_PANNING, // (pan) + DIV_CMD_PANNING, // (left, right) DIV_CMD_LEGATO, // (note) DIV_CMD_PRE_PORTA, // (inPorta, isPortaOrSlide) DIV_CMD_PRE_NOTE, // used in C64 (note) diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index c42f5cc5d..edd7dcc46 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -69,6 +69,12 @@ const char* DivEngine::getEffectDesc(unsigned char effect, int chan, bool notNul return "0Dxx: Jump to next pattern"; case 0x0f: return "0Fxx: Set speed 2"; + case 0x80: + return "80xx: Set panning (00: left; 80: center; FF: right)"; + case 0x81: + return "81xx: Set panning (left channel)"; + case 0x82: + return "82xx: Set panning (right channel)"; case 0xc0: case 0xc1: case 0xc2: case 0xc3: return "Cxxx: Set tick rate (hz)"; case 0xe0: @@ -1052,6 +1058,10 @@ int DivEngine::convertPanSplitToLinear(unsigned int val, unsigned char bits, int return pan*range; } +int DivEngine::convertPanSplitToLinearLR(unsigned char left, unsigned char right, int range) { + return convertPanSplitToLinear((left<<8)|right,8,range); +} + unsigned int DivEngine::convertPanLinearToSplit(int val, unsigned char bits, int range) { if (val<0) val=0; if (val>range) val=range; diff --git a/src/engine/engine.h b/src/engine/engine.h index 9f971d7a8..e3a0f71c2 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -84,7 +84,7 @@ struct DivChannelState { int delayOrder, delayRow, retrigSpeed, retrigTick; int vibratoDepth, vibratoRate, vibratoPos, vibratoDir, vibratoFine; int tremoloDepth, tremoloRate, tremoloPos; - unsigned char arp, arpStage, arpTicks; + unsigned char arp, arpStage, arpTicks, panL, panR; bool doNote, legato, portaStop, keyOn, keyOff, nowYouCanStop, stopOnOff; bool arpYield, delayLocked, inPorta, scheduledSlideReset, shorthandPorta, noteOnInhibit, resetArp; @@ -119,6 +119,8 @@ struct DivChannelState { arp(0), arpStage(-1), arpTicks(1), + panL(255), + panR(255), doNote(false), legato(false), portaStop(false), @@ -477,6 +479,7 @@ class DivEngine { // convert panning formats int convertPanSplitToLinear(unsigned int val, unsigned char bits, int range); + int convertPanSplitToLinearLR(unsigned char left, unsigned char right, int range); unsigned int convertPanLinearToSplit(int val, unsigned char bits, int range); // find song loop position diff --git a/src/engine/platform/arcade.cpp b/src/engine/platform/arcade.cpp index f993a400d..11cff5e94 100644 --- a/src/engine/platform/arcade.cpp +++ b/src/engine/platform/arcade.cpp @@ -543,8 +543,8 @@ int DivPlatformArcade::dispatch(DivCommand c) { chan[c.chan].ins=c.value; break; case DIV_CMD_PANNING: { - chan[c.chan].chVolL=((c.value>>4)>0); - chan[c.chan].chVolR=((c.value&15)>0); + chan[c.chan].chVolL=(c.value>0); + chan[c.chan].chVolR=(c.value2>0); if (isMuted[c.chan]) { rWrite(chanOffs[c.chan]+ADDR_LR_FB_ALG,(chan[c.chan].state.alg&7)|(chan[c.chan].state.fb<<3)); } else { diff --git a/src/engine/platform/gb.cpp b/src/engine/platform/gb.cpp index 0cfdb960c..e55e703ef 100644 --- a/src/engine/platform/gb.cpp +++ b/src/engine/platform/gb.cpp @@ -371,9 +371,11 @@ int DivPlatformGB::dispatch(DivCommand c) { break; case DIV_CMD_PANNING: { lastPan&=~(0x11<0)|(((c.value>>4)>0)<<4); - lastPan|=c.value<0) pan|=0x10; + if (c.value2>0) pan|=0x01; + if (pan==0) pan=0x11; + lastPan|=pan<0)|(((c.value>>4)>0)<<1); + chan[c.chan].pan=(c.value2>0)|((c.value>0)<<1); } rWrite(chanOffs[c.chan]+ADDR_LRAF,(isMuted[c.chan]?0:(chan[c.chan].pan<<6))|(chan[c.chan].state.fms&7)|((chan[c.chan].state.ams&3)<<4)); break; diff --git a/src/engine/platform/genesisext.cpp b/src/engine/platform/genesisext.cpp index e5d964e5d..e67c353d4 100644 --- a/src/engine/platform/genesisext.cpp +++ b/src/engine/platform/genesisext.cpp @@ -107,10 +107,10 @@ int DivPlatformGenesisExt::dispatch(DivCommand c) { opChan[ch].ins=c.value; break; case DIV_CMD_PANNING: { - if (c.value==0) { + if (c.value==0 && c.value2==0) { opChan[ch].pan=3; } else { - opChan[ch].pan=((c.value&15)>0)|(((c.value>>4)>0)<<1); + opChan[ch].pan=(c.value2>0)|((c.value>0)<<1); } if (parent->song.sharedExtStat) { for (int i=0; i<4; i++) { diff --git a/src/engine/platform/lynx.cpp b/src/engine/platform/lynx.cpp index 9a1f4d3d2..18a9b72fa 100644 --- a/src/engine/platform/lynx.cpp +++ b/src/engine/platform/lynx.cpp @@ -259,7 +259,7 @@ int DivPlatformLynx::dispatch(DivCommand c) { } break; case DIV_CMD_PANNING: - chan[c.chan].pan=c.value; + chan[c.chan].pan=(c.value&0xf0)|(c.value2>>4); WRITE_ATTEN(c.chan,chan[c.chan].pan); break; case DIV_CMD_GET_VOLUME: diff --git a/src/engine/platform/opl.cpp b/src/engine/platform/opl.cpp index c127ce774..3e385434e 100644 --- a/src/engine/platform/opl.cpp +++ b/src/engine/platform/opl.cpp @@ -680,10 +680,10 @@ int DivPlatformOPL::dispatch(DivCommand c) { break; case DIV_CMD_PANNING: { if (oplType!=3) break; - if (c.value==0) { + if (c.value==0 && c.value2==0) { chan[c.chan].pan=3; } else { - chan[c.chan].pan=(((c.value&15)>0)<<1)|((c.value>>4)>0); + chan[c.chan].pan=(c.value2>0)|((c.value>0)<<1); } int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2; if (isMuted[c.chan]) { diff --git a/src/engine/platform/pce.cpp b/src/engine/platform/pce.cpp index b09263fc6..a56732bef 100644 --- a/src/engine/platform/pce.cpp +++ b/src/engine/platform/pce.cpp @@ -424,7 +424,7 @@ int DivPlatformPCE::dispatch(DivCommand c) { } break; case DIV_CMD_PANNING: { - chan[c.chan].pan=c.value; + chan[c.chan].pan=(c.value&0xf0)|(c.value2>>4); chWrite(c.chan,0x05,isMuted[c.chan]?0:chan[c.chan].pan); break; } diff --git a/src/engine/platform/qsound.cpp b/src/engine/platform/qsound.cpp index 9dd34589a..f3197f6b4 100644 --- a/src/engine/platform/qsound.cpp +++ b/src/engine/platform/qsound.cpp @@ -441,7 +441,7 @@ int DivPlatformQSound::dispatch(DivCommand c) { return chan[c.chan].outVol; break; case DIV_CMD_PANNING: - chan[c.chan].panning=parent->convertPanSplitToLinear(c.value,4,32); + chan[c.chan].panning=parent->convertPanSplitToLinearLR(c.value,c.value2,32); immWrite(Q1_PAN+c.chan,chan[c.chan].panning+0x110+(chan[c.chan].surround?0:0x30)); break; case DIV_CMD_QSOUND_ECHO_LEVEL: diff --git a/src/engine/platform/saa.cpp b/src/engine/platform/saa.cpp index 4d79d45bd..a083355a6 100644 --- a/src/engine/platform/saa.cpp +++ b/src/engine/platform/saa.cpp @@ -331,7 +331,7 @@ int DivPlatformSAA1099::dispatch(DivCommand c) { break; } case DIV_CMD_PANNING: - chan[c.chan].pan=c.value; + chan[c.chan].pan=(c.value&0xf0)|(c.value2>>4); if (isMuted[c.chan]) { rWrite(c.chan,0); } else { diff --git a/src/engine/platform/segapcm.cpp b/src/engine/platform/segapcm.cpp index 9064fa093..eb506ff36 100644 --- a/src/engine/platform/segapcm.cpp +++ b/src/engine/platform/segapcm.cpp @@ -268,8 +268,8 @@ int DivPlatformSegaPCM::dispatch(DivCommand c) { chan[c.chan].ins=c.value; break; case DIV_CMD_PANNING: { - chan[c.chan].chVolL=(c.value>>4)|(((c.value>>4)>>1)<<4); - chan[c.chan].chVolR=(c.value&15)|(((c.value&15)>>1)<<4); + chan[c.chan].chVolL=c.value>>1; + chan[c.chan].chVolR=c.value2>>1; if (dumpWrites) { addWrite(0x10002+(c.chan<<3),chan[c.chan].chVolL); addWrite(0x10003+(c.chan<<3),chan[c.chan].chVolR); diff --git a/src/engine/platform/su.cpp b/src/engine/platform/su.cpp index 4733e5b24..3a6003cb8 100644 --- a/src/engine/platform/su.cpp +++ b/src/engine/platform/su.cpp @@ -270,7 +270,7 @@ int DivPlatformSoundUnit::dispatch(DivCommand c) { break; } case DIV_CMD_PANNING: { - chan[c.chan].pan=parent->convertPanSplitToLinear(c.value,4,254)-127; + chan[c.chan].pan=parent->convertPanSplitToLinearLR(c.value,c.value2,254)-127; chWrite(c.chan,0x03,chan[c.chan].pan); break; } diff --git a/src/engine/platform/swan.cpp b/src/engine/platform/swan.cpp index 147082924..9ac07f6e6 100644 --- a/src/engine/platform/swan.cpp +++ b/src/engine/platform/swan.cpp @@ -412,7 +412,7 @@ int DivPlatformSwan::dispatch(DivCommand c) { } break; case DIV_CMD_PANNING: { - chan[c.chan].pan=c.value; + chan[c.chan].pan=(c.value&0xf0)|(c.value2>>4); calcAndWriteOutVol(c.chan,chan[c.chan].std.vol.will?chan[c.chan].std.vol.val:15); break; } diff --git a/src/engine/platform/tx81z.cpp b/src/engine/platform/tx81z.cpp index 643b6ebcf..d69dfc875 100644 --- a/src/engine/platform/tx81z.cpp +++ b/src/engine/platform/tx81z.cpp @@ -525,8 +525,8 @@ int DivPlatformTX81Z::dispatch(DivCommand c) { chan[c.chan].ins=c.value; break; case DIV_CMD_PANNING: { - chan[c.chan].chVolL=((c.value>>4)>0); - chan[c.chan].chVolR=((c.value&15)>0); + chan[c.chan].chVolL=(c.value>0); + chan[c.chan].chVolR=(c.value2>0); chan[c.chan].freqChanged=true; /* if (isMuted[c.chan]) { diff --git a/src/engine/platform/vera.cpp b/src/engine/platform/vera.cpp index 9b30abdad..ccb819c9d 100644 --- a/src/engine/platform/vera.cpp +++ b/src/engine/platform/vera.cpp @@ -345,8 +345,8 @@ int DivPlatformVERA::dispatch(DivCommand c) { break; case DIV_CMD_PANNING: { tmp=0; - tmp|=(c.value&0x10)?1:0; - tmp|=(c.value&0x01)?2:0; + tmp|=(c.value>0)?1:0; + tmp|=(c.value2>0)?2:0; chan[c.chan].pan=tmp&3; if (c.chan<16) { rWriteHi(c.chan,2,isMuted[c.chan]?0:chan[c.chan].pan); diff --git a/src/engine/platform/x1_010.cpp b/src/engine/platform/x1_010.cpp index 9d64adfa9..42319a29b 100644 --- a/src/engine/platform/x1_010.cpp +++ b/src/engine/platform/x1_010.cpp @@ -694,8 +694,9 @@ int DivPlatformX1_010::dispatch(DivCommand c) { break; case DIV_CMD_PANNING: { if (!stereo) break; - if (chan[c.chan].pan!=c.value) { - chan[c.chan].pan=c.value; + unsigned char newPan=(c.value&0xf0)|(c.value2>>4); + if (chan[c.chan].pan!=newPan) { + chan[c.chan].pan=newPan; if (!isMuted[c.chan]) { chan[c.chan].envChanged=true; } diff --git a/src/engine/platform/ym2610.cpp b/src/engine/platform/ym2610.cpp index 4fcd53d36..c4ae0ad80 100644 --- a/src/engine/platform/ym2610.cpp +++ b/src/engine/platform/ym2610.cpp @@ -839,10 +839,10 @@ int DivPlatformYM2610::dispatch(DivCommand c) { chan[c.chan].ins=c.value; break; case DIV_CMD_PANNING: { - if (c.value==0) { + if (c.value==0 && c.value2==0) { chan[c.chan].pan=3; } else { - chan[c.chan].pan=((c.value&15)>0)|(((c.value>>4)>0)<<1); + chan[c.chan].pan=(c.value2>0)|((c.value>0)<<1); } if (c.chan>12) { immWrite(0x11,isMuted[c.chan]?0:(chan[c.chan].pan<<6)); diff --git a/src/engine/platform/ym2610b.cpp b/src/engine/platform/ym2610b.cpp index 3ad47b17d..12e32ef8c 100644 --- a/src/engine/platform/ym2610b.cpp +++ b/src/engine/platform/ym2610b.cpp @@ -902,10 +902,10 @@ int DivPlatformYM2610B::dispatch(DivCommand c) { chan[c.chan].ins=c.value; break; case DIV_CMD_PANNING: { - if (c.value==0) { + if (c.value==0 && c.value2==0) { chan[c.chan].pan=3; } else { - chan[c.chan].pan=((c.value&15)>0)|(((c.value>>4)>0)<<1); + chan[c.chan].pan=(c.value2>0)|((c.value>0)<<1); } if (c.chan>14) { immWrite(0x11,isMuted[c.chan]?0:(chan[c.chan].pan<<6)); diff --git a/src/engine/platform/ym2610bext.cpp b/src/engine/platform/ym2610bext.cpp index e930934d0..5eec24ece 100644 --- a/src/engine/platform/ym2610bext.cpp +++ b/src/engine/platform/ym2610bext.cpp @@ -99,10 +99,10 @@ int DivPlatformYM2610BExt::dispatch(DivCommand c) { opChan[ch].ins=c.value; break; case DIV_CMD_PANNING: { - if (c.value==0) { + if (c.value==0 && c.value2==0) { opChan[ch].pan=3; } else { - opChan[ch].pan=((c.value&15)>0)|(((c.value>>4)>0)<<1); + opChan[ch].pan=(c.value2>0)|((c.value>0)<<1); } DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM); if (parent->song.sharedExtStat) { diff --git a/src/engine/platform/ym2610ext.cpp b/src/engine/platform/ym2610ext.cpp index c4cb3d9ca..6ae911061 100644 --- a/src/engine/platform/ym2610ext.cpp +++ b/src/engine/platform/ym2610ext.cpp @@ -99,10 +99,10 @@ int DivPlatformYM2610Ext::dispatch(DivCommand c) { opChan[ch].ins=c.value; break; case DIV_CMD_PANNING: { - if (c.value==0) { + if (c.value==0 && c.value2==0) { opChan[ch].pan=3; } else { - opChan[ch].pan=((c.value&15)>0)|(((c.value>>4)>0)<<1); + opChan[ch].pan=(c.value2>0)|((c.value>0)<<1); } DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM); if (parent->song.sharedExtStat) { diff --git a/src/engine/playback.cpp b/src/engine/playback.cpp index 29899f0d9..cb4d663d1 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -1025,6 +1025,7 @@ void DivEngine::processRow(int i, bool afterDelay) { short lastSlide=-1; bool calledPorta=false; + bool panChanged=false; // effects for (int j=0; j>4)|(effectVal&0xf0); + chan[i].panR=(effectVal&15)|((effectVal&15)<<4); + panChanged=true; + break; + case 0x80: { // panning (linear) + unsigned short pan=convertPanLinearToSplit(effectVal,8,255); + chan[i].panL=pan>>8; + chan[i].panR=pan&0xff; + panChanged=true; + break; + } + case 0x81: // panning left (split 8-bit) + chan[i].panL=effectVal; + panChanged=true; + break; + case 0x82: // panning right (split 8-bit) + chan[i].panR=effectVal; + panChanged=true; break; case 0x01: // ramp up if (song.ignoreDuplicateSlides && (lastSlide==0x01 || lastSlide==0x1337)) break; @@ -1308,6 +1326,10 @@ void DivEngine::processRow(int i, bool afterDelay) { } } + if (panChanged) { + dispatchCmd(DivCommand(DIV_CMD_PANNING,i,chan[i].panL,chan[i].panR)); + } + if (insChanged && (chan[i].inPorta || calledPorta) && song.newInsTriggersInPorta) { dispatchCmd(DivCommand(DIV_CMD_NOTE_ON,i,DIV_NOTE_NULL)); } diff --git a/src/gui/effectList.cpp b/src/gui/effectList.cpp index c64966a43..032b87c33 100644 --- a/src/gui/effectList.cpp +++ b/src/gui/effectList.cpp @@ -32,27 +32,7 @@ void FurnaceGUI::drawEffectList() { ImGui::TableNextRow(); ImGui::TableNextColumn(); ImGui::PushFont(patFont); - if (i<0x10) { - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[fxColors[i]]); - } else if (i<0x20) { - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY]); - } else if (i<0x30) { - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_SYS_SECONDARY]); - } else if (i<0x48) { - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY]); - } else if (i<0x90) { - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_INVALID]); - } else if (i<0xa0) { - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_MISC]); - } else if (i<0xc0) { - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_INVALID]); - } else if (i<0xd0) { - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_SPEED]); - } else if (i<0xe0) { - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_INVALID]); - } else { - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[extFxColors[i-0xe0]]); - } + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[fxColors[i]]); ImGui::Text("%c%c%c%c",name[0],name[1],name[2],name[3]); ImGui::PopStyleColor(); ImGui::PopFont(); diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 74a303542..ee94e8fe8 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -2000,27 +2000,7 @@ void FurnaceGUI::editOptions(bool topMenu) { } else { const unsigned char data=latchEffect; snprintf(id,63,"%.2x##LatchFX",data); - if (data<0x10) { - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[fxColors[data]]); - } else if (data<0x20) { - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY]); - } else if (data<0x30) { - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_SYS_SECONDARY]); - } else if (data<0x48) { - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY]); - } else if (data<0x90) { - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_INVALID]); - } else if (data<0xa0) { - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_MISC]); - } else if (data<0xc0) { - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_INVALID]); - } else if (data<0xd0) { - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_SPEED]); - } else if (data<0xe0) { - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_INVALID]); - } else { - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[extFxColors[data-0xe0]]); - } + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[fxColors[data]]); } if (ImGui::Selectable(id,latchTarget==3,ImGuiSelectableFlags_DontClosePopups)) { diff --git a/src/gui/guiConst.cpp b/src/gui/guiConst.cpp index 357868381..ec157a9eb 100644 --- a/src/gui/guiConst.cpp +++ b/src/gui/guiConst.cpp @@ -142,7 +142,7 @@ const char* resampleStrats[]={ "best possible" }; -const FurnaceGUIColors fxColors[16]={ +const FurnaceGUIColors fxColors[256]={ GUI_COLOR_PATTERN_EFFECT_MISC, // 00 GUI_COLOR_PATTERN_EFFECT_PITCH, // 01 GUI_COLOR_PATTERN_EFFECT_PITCH, // 02 @@ -159,9 +159,242 @@ const FurnaceGUIColors fxColors[16]={ GUI_COLOR_PATTERN_EFFECT_SONG, // 0D GUI_COLOR_PATTERN_EFFECT_INVALID, // 0E GUI_COLOR_PATTERN_EFFECT_SPEED, // 0F -}; -const FurnaceGUIColors extFxColors[32]={ + // 10-1F + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + + // 20-2F + GUI_COLOR_PATTERN_EFFECT_SYS_SECONDARY, + GUI_COLOR_PATTERN_EFFECT_SYS_SECONDARY, + GUI_COLOR_PATTERN_EFFECT_SYS_SECONDARY, + GUI_COLOR_PATTERN_EFFECT_SYS_SECONDARY, + GUI_COLOR_PATTERN_EFFECT_SYS_SECONDARY, + GUI_COLOR_PATTERN_EFFECT_SYS_SECONDARY, + GUI_COLOR_PATTERN_EFFECT_SYS_SECONDARY, + GUI_COLOR_PATTERN_EFFECT_SYS_SECONDARY, + GUI_COLOR_PATTERN_EFFECT_SYS_SECONDARY, + GUI_COLOR_PATTERN_EFFECT_SYS_SECONDARY, + GUI_COLOR_PATTERN_EFFECT_SYS_SECONDARY, + GUI_COLOR_PATTERN_EFFECT_SYS_SECONDARY, + GUI_COLOR_PATTERN_EFFECT_SYS_SECONDARY, + GUI_COLOR_PATTERN_EFFECT_SYS_SECONDARY, + GUI_COLOR_PATTERN_EFFECT_SYS_SECONDARY, + GUI_COLOR_PATTERN_EFFECT_SYS_SECONDARY, + + // 30-3F + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + + // 40-4F + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + + // 50-5F + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + + // 60-6F + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + + // 70-7F + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + + // 80-8F + GUI_COLOR_PATTERN_EFFECT_PANNING, + GUI_COLOR_PATTERN_EFFECT_PANNING, + GUI_COLOR_PATTERN_EFFECT_PANNING, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + + // 90-9F + GUI_COLOR_PATTERN_EFFECT_MISC, + GUI_COLOR_PATTERN_EFFECT_MISC, + GUI_COLOR_PATTERN_EFFECT_MISC, + GUI_COLOR_PATTERN_EFFECT_MISC, + GUI_COLOR_PATTERN_EFFECT_MISC, + GUI_COLOR_PATTERN_EFFECT_MISC, + GUI_COLOR_PATTERN_EFFECT_MISC, + GUI_COLOR_PATTERN_EFFECT_MISC, + GUI_COLOR_PATTERN_EFFECT_MISC, + GUI_COLOR_PATTERN_EFFECT_MISC, + GUI_COLOR_PATTERN_EFFECT_MISC, + GUI_COLOR_PATTERN_EFFECT_MISC, + GUI_COLOR_PATTERN_EFFECT_MISC, + GUI_COLOR_PATTERN_EFFECT_MISC, + GUI_COLOR_PATTERN_EFFECT_MISC, + GUI_COLOR_PATTERN_EFFECT_MISC, + + // A0-AF + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + + // B0-BF + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + + // C0-CF + GUI_COLOR_PATTERN_EFFECT_SPEED, + GUI_COLOR_PATTERN_EFFECT_SPEED, + GUI_COLOR_PATTERN_EFFECT_SPEED, + GUI_COLOR_PATTERN_EFFECT_SPEED, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + + // D0-DF + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_INVALID, + + // E0-FF extended effects GUI_COLOR_PATTERN_EFFECT_MISC, // E0 GUI_COLOR_PATTERN_EFFECT_PITCH, // E1 GUI_COLOR_PATTERN_EFFECT_PITCH, // E2 @@ -193,7 +426,7 @@ const FurnaceGUIColors extFxColors[32]={ GUI_COLOR_PATTERN_EFFECT_INVALID, // FC GUI_COLOR_PATTERN_EFFECT_INVALID, // FD GUI_COLOR_PATTERN_EFFECT_INVALID, // FE - GUI_COLOR_PATTERN_EFFECT_SONG, // FF + GUI_COLOR_PATTERN_EFFECT_SONG // FF }; #define D FurnaceGUIActionDef diff --git a/src/gui/guiConst.h b/src/gui/guiConst.h index c0a8cd6c3..69085c324 100644 --- a/src/gui/guiConst.h +++ b/src/gui/guiConst.h @@ -47,5 +47,4 @@ extern const FurnaceGUIActionDef guiActions[]; extern const FurnaceGUIColorDef guiColors[]; extern const int altValues[24]; extern const int vgmVersions[6]; -extern const FurnaceGUIColors fxColors[16]; -extern const FurnaceGUIColors extFxColors[32]; \ No newline at end of file +extern const FurnaceGUIColors fxColors[256]; \ No newline at end of file diff --git a/src/gui/pattern.cpp b/src/gui/pattern.cpp index 9ba22ef51..61fccb5ae 100644 --- a/src/gui/pattern.cpp +++ b/src/gui/pattern.cpp @@ -235,27 +235,7 @@ inline void FurnaceGUI::patternRow(int i, bool isPlaying, float lineHeight, int } else { const unsigned char data=pat->data[i][index]; sprintf(id,"%.2X##PE%d_%d_%d",data,k,i,j); - if (data<0x10) { - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[fxColors[data]]); - } else if (data<0x20) { - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY]); - } else if (data<0x30) { - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_SYS_SECONDARY]); - } else if (data<0x48) { - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY]); - } else if (data<0x90) { - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_INVALID]); - } else if (data<0xa0) { - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_MISC]); - } else if (data<0xc0) { - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_INVALID]); - } else if (data<0xd0) { - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_SPEED]); - } else if (data<0xe0) { - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PATTERN_EFFECT_INVALID]); - } else { - ImGui::PushStyleColor(ImGuiCol_Text,uiColors[extFxColors[data-0xe0]]); - } + ImGui::PushStyleColor(ImGuiCol_Text,uiColors[fxColors[data]]); } } ImGui::SameLine(0.0f,0.0f); @@ -721,17 +701,14 @@ void FurnaceGUI::drawPattern() { break; } case DIV_CMD_PANNING: { - if (i.value==0) { - num=0; - break; - } - float ratio=float(((i.value>>4)&15)-(i.value&15))/MAX(((i.value>>4)&15),(i.value&15)); + float ratio=(float)(128-e->convertPanSplitToLinearLR(i.value,i.value2,256))/128.0f; + logV("ratio %f",ratio); speedX=-22.0f*sin(ratio*M_PI*0.5); speedY=-22.0f*cos(ratio*M_PI*0.5); spread=5.0f+fabs(sin(ratio*M_PI*0.5))*7.0f; grav=0.0f; frict=0.96f; - if (((i.value>>4)&15)==(i.value&15)) { + if (i.value==i.value2) { partIcon=ICON_FA_ARROWS_H; } else if (ratio>0) { partIcon=ICON_FA_ARROW_LEFT; From d79e5d77a770288174570e6d0cf09471f154edf9 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 29 Apr 2022 23:53:15 -0500 Subject: [PATCH 249/342] GUI: finally implement "don't scroll when moving c ursor" setting --- TODO.md | 4 +--- src/gui/cursor.cpp | 4 +++- src/gui/gui.cpp | 4 +++- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/TODO.md b/TODO.md index c813fbfe8..a3e829866 100644 --- a/TODO.md +++ b/TODO.md @@ -29,7 +29,6 @@ - try to find out why does VSlider not accept keyboard input - finish lock layout - if macros have release, note off should release them -- add "don't scroll on cursor movement" option - add ability to select entire row when clicking on row number - store edit/followOrders/followPattern state in config - add ability to select a column by double clicking @@ -38,5 +37,4 @@ - settings: OK/Cancel buttons should be always visible - Apply button in settings - better FM chip names (number and codename) -- find and replace -- precise panning effects (80xx linear, 81xx/82xx per-channel) +- find and replace \ No newline at end of file diff --git a/src/gui/cursor.cpp b/src/gui/cursor.cpp index 1fdf64d77..82f4a16a7 100644 --- a/src/gui/cursor.cpp +++ b/src/gui/cursor.cpp @@ -193,7 +193,9 @@ void FurnaceGUI::moveCursor(int x, int y, bool select) { selStart=cursor; } selEnd=cursor; - updateScroll(cursor.y); + if (!settings.cursorMoveNoScroll) { + updateScroll(cursor.y); + } e->setMidiBaseChan(cursor.xCoarse); } diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index ee94e8fe8..e82a16aed 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -2350,7 +2350,9 @@ bool FurnaceGUI::loop() { demandScrollX=true; if (cursor.xCoarse==selStart.xCoarse && cursor.xFine==selStart.xFine && cursor.y==selStart.y && cursor.xCoarse==selEnd.xCoarse && cursor.xFine==selEnd.xFine && cursor.y==selEnd.y) { - updateScroll(cursor.y); + if (!settings.cursorMoveNoScroll) { + updateScroll(cursor.y); + } } } break; From 9f49504891fce169100fa7de1a71d759f357411d Mon Sep 17 00:00:00 2001 From: Natt Akuma <77432377+akumanatt@users.noreply.github.com> Date: Sat, 30 Apr 2022 11:59:47 +0700 Subject: [PATCH 250/342] Revert PC speaker presets --- src/gui/presets.cpp | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/gui/presets.cpp b/src/gui/presets.cpp index 77bd9b10c..3dd24e0af 100644 --- a/src/gui/presets.cpp +++ b/src/gui/presets.cpp @@ -463,101 +463,101 @@ void FurnaceGUI::initSystemPresets() { )); cat.systems.push_back(FurnaceGUISysDef( "PC + Covox Sound Master", { - DIV_SYSTEM_PCSPKR, 64, 0, 0, DIV_SYSTEM_AY8930, 64, 0, 3, + DIV_SYSTEM_PCSPKR, 64, 0, 0, 0 } )); cat.systems.push_back(FurnaceGUISysDef( "PC + SSI 2001", { - DIV_SYSTEM_PCSPKR, 64, 0, 0, DIV_SYSTEM_C64_6581, 64, 0, 2, + DIV_SYSTEM_PCSPKR, 64, 0, 0, 0 } )); cat.systems.push_back(FurnaceGUISysDef( "PC + Game Blaster", { + DIV_SYSTEM_SAA1099, 64, 0, 1, + DIV_SYSTEM_SAA1099, 64, 0, 1, DIV_SYSTEM_PCSPKR, 64, 0, 0, - DIV_SYSTEM_SAA1099, 64, 0, 1, - DIV_SYSTEM_SAA1099, 64, 0, 1, 0 } )); cat.systems.push_back(FurnaceGUISysDef( "PC + AdLib/Sound Blaster", { - DIV_SYSTEM_PCSPKR, 64, 0, 0, DIV_SYSTEM_OPL2, 64, 0, 0, + DIV_SYSTEM_PCSPKR, 64, 0, 0, 0 } )); cat.systems.push_back(FurnaceGUISysDef( "PC + AdLib/Sound Blaster (drums mode)", { - DIV_SYSTEM_PCSPKR, 64, 0, 0, DIV_SYSTEM_OPL2_DRUMS, 64, 0, 0, + DIV_SYSTEM_PCSPKR, 64, 0, 0, 0 } )); cat.systems.push_back(FurnaceGUISysDef( "PC + Sound Blaster w/Game Blaster Compatible", { - DIV_SYSTEM_PCSPKR, 64, 0, 0, DIV_SYSTEM_OPL2, 64, 0, 0, DIV_SYSTEM_SAA1099, 64, 0, 1, DIV_SYSTEM_SAA1099, 64, 0, 1, + DIV_SYSTEM_PCSPKR, 64, 0, 0, 0 } )); cat.systems.push_back(FurnaceGUISysDef( "PC + Sound Blaster w/Game Blaster Compatible (drums mode)", { - DIV_SYSTEM_PCSPKR, 64, 0, 0, DIV_SYSTEM_OPL2_DRUMS, 64, 0, 0, DIV_SYSTEM_SAA1099, 64, 0, 1, DIV_SYSTEM_SAA1099, 64, 0, 1, + DIV_SYSTEM_PCSPKR, 64, 0, 0, 0 } )); cat.systems.push_back(FurnaceGUISysDef( "PC + Sound Blaster Pro", { - DIV_SYSTEM_PCSPKR, 64, 0, 0, DIV_SYSTEM_OPL2, 64, -127, 0, DIV_SYSTEM_OPL2, 64, 127, 0, + DIV_SYSTEM_PCSPKR, 64, 0, 0, 0 } )); cat.systems.push_back(FurnaceGUISysDef( "PC + Sound Blaster Pro (drums mode)", { - DIV_SYSTEM_PCSPKR, 64, 0, 0, DIV_SYSTEM_OPL2_DRUMS, 64, -127, 0, DIV_SYSTEM_OPL2_DRUMS, 64, 127, 0, + DIV_SYSTEM_PCSPKR, 64, 0, 0, 0 } )); cat.systems.push_back(FurnaceGUISysDef( "PC + Sound Blaster Pro 2", { - DIV_SYSTEM_PCSPKR, 64, 0, 0, DIV_SYSTEM_OPL3, 64, 0, 0, + DIV_SYSTEM_PCSPKR, 64, 0, 0, 0 } )); cat.systems.push_back(FurnaceGUISysDef( "PC + Sound Blaster Pro 2 (drums mode)", { - DIV_SYSTEM_PCSPKR, 64, 0, 0, DIV_SYSTEM_OPL3_DRUMS, 64, 0, 0, + DIV_SYSTEM_PCSPKR, 64, 0, 0, 0 } )); cat.systems.push_back(FurnaceGUISysDef( "PC + PC-FXGA", { - DIV_SYSTEM_PCSPKR, 64, 0, 0, DIV_SYSTEM_PCE, 64, 0, 0, // HuC6230 (WSG from HuC6280 but with built in 2 OKI ADPCM playback engines) + DIV_SYSTEM_PCSPKR, 64, 0, 0, 0 } )); cat.systems.push_back(FurnaceGUISysDef( "PC + SAAYM", { - DIV_SYSTEM_PCSPKR, 64, 0, 0, DIV_SYSTEM_YM2151, 64, 0, 0, // 3.58MHz or 4MHz selectable via jumper DIV_SYSTEM_SAA1099, 64, 0, 1, // 7.16MHz or 8MHz selectable via jumper DIV_SYSTEM_SAA1099, 64, 0, 1, // "" + DIV_SYSTEM_PCSPKR, 64, 0, 0, 0 } )); From 52c0044bd9f60bdbf60a9a4034580f9e7dcd8d7b Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 30 Apr 2022 01:37:37 -0500 Subject: [PATCH 251/342] GUI: add ability to customize startup system --- TODO.md | 2 - src/engine/engine.cpp | 119 +++++++++++++++++-- src/engine/engine.h | 6 + src/gui/gui.cpp | 2 +- src/gui/gui.h | 3 +- src/gui/settings.cpp | 119 ++++++++++++++++++- src/gui/sysConf.cpp | 270 ++++++++++++++++++++---------------------- 7 files changed, 361 insertions(+), 160 deletions(-) diff --git a/TODO.md b/TODO.md index a3e829866..09f794ab7 100644 --- a/TODO.md +++ b/TODO.md @@ -13,12 +13,10 @@ - maybe YMU759 ADPCM channel - ADPCM chips - more effects for FM param control -- ability to customize startup system - store system presets in new file - Game Boy envelope macro/sequence - option to display chip names instead of "multi-system" on title bar - rewrite the system name detection function anyway -- add nightly.link - scroll instrument/wave/sample list when selecting item - unified data view - volume commands should work on Game Boy diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index edd7dcc46..47d70093b 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -620,6 +620,98 @@ void DivEngine::renderSamples() { x1_010MemLen=memPos+256; } +String DivEngine::encodeSysDesc(std::vector& desc) { + String ret; + if (desc[0]!=0) { + int index=0; + for (size_t i=0; i=32) break; + } + } + return ret; +} + +std::vector DivEngine::decodeSysDesc(String desc) { + std::vector ret; + bool hasVal=false; + bool negative=false; + int val=0; + int curStage=0; + int sysID, sysVol, sysPan, sysFlags; + desc+=' '; // ha + for (char i: desc) { + switch (i) { + case ' ': + if (hasVal) { + if (negative) val=-val; + switch (curStage) { + case 0: + sysID=val; + curStage++; + break; + case 1: + sysVol=val; + curStage++; + break; + case 2: + sysPan=val; + curStage++; + break; + case 3: + sysFlags=val; + + if (systemFromFileFur(sysID)!=0) { + if (sysVol<-128) sysVol=-128; + if (sysVol>127) sysVol=127; + if (sysPan<-128) sysPan=-128; + if (sysPan>127) sysPan=127; + ret.push_back(systemFromFileFur(sysID)); + ret.push_back(sysVol); + ret.push_back(sysPan); + ret.push_back(sysFlags); + } + + curStage=0; + break; + } + hasVal=false; + negative=false; + val=0; + } + break; + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + val=(val*10)+(i-'0'); + hasVal=true; + break; + case '-': + if (!hasVal) negative=true; + break; + } + } + return ret; +} + +void DivEngine::initSongWithDesc(const int* description) { + int chanCount=0; + if (description[0]!=0) { + int index=0; + for (int i=0; description[i]; i+=4) { + song.system[index]=(DivSystem)description[i]; + song.systemVol[index]=description[i+1]; + song.systemPan[index]=description[i+2]; + song.systemFlags[index]=description[i+3]; + index++; + chanCount+=getChannelCount(song.system[index]); + if (chanCount>=63) break; + if (index>=32) break; + } + song.systemLen=index; + } +} + void DivEngine::createNew(const int* description) { quitDispatch(); BUSY_BEGIN; @@ -627,18 +719,7 @@ void DivEngine::createNew(const int* description) { song.unload(); song=DivSong(); if (description!=NULL) { - if (description[0]!=0) { - int index=0; - for (int i=0; description[i]; i+=4) { - song.system[index]=(DivSystem)description[i]; - song.systemVol[index]=description[i+1]; - song.systemPan[index]=description[i+2]; - song.systemFlags[index]=description[i+3]; - index++; - if (index>=32) break; - } - song.systemLen=index; - } + initSongWithDesc(description); } recalcChans(); renderSamples(); @@ -1207,6 +1288,8 @@ void DivEngine::recalcChans() { for (int i=0; i preset=decodeSysDesc(getConfString("initialSys","")); + logI("preset size %ld",preset.size()); + if (preset.size()>0 && (preset.size()&3)==0) { + preset.push_back(0); + initSongWithDesc(preset.data()); + } + hasLoadedSomething=true; + } + // init the rest of engine bool haveAudio=false; if (!initAudioBackend()) { diff --git a/src/engine/engine.h b/src/engine/engine.h index e3a0f71c2..7dd7489cb 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -298,6 +298,7 @@ class DivEngine { bool midiIsDirect; bool lowLatency; bool systemsRegistered; + bool hasLoadedSomething; int softLockCount; int subticks, ticks, curRow, curOrder, remainingLoops, nextSpeed; double divider; @@ -398,6 +399,7 @@ class DivEngine { bool deinitAudioBackend(); void registerSystems(); + void initSongWithDesc(const int* description); void exchangeIns(int one, int two); void swapChannels(int src, int dest); @@ -420,6 +422,9 @@ class DivEngine { DivInstrument* getIns(int index, DivInstrumentType fallbackType=DIV_INS_FM); DivWavetable* getWave(int index); DivSample* getSample(int index); + // parse system setup description + String encodeSysDesc(std::vector& desc); + std::vector decodeSysDesc(String desc); // start fresh void createNew(const int* description); // load a file. @@ -876,6 +881,7 @@ class DivEngine { midiIsDirect(false), lowLatency(false), systemsRegistered(false), + hasLoadedSomething(false), softLockCount(0), subticks(0), ticks(0), diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index e82a16aed..932a7f81b 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -2662,7 +2662,7 @@ bool FurnaceGUI::loop() { if (ImGui::BeginMenu("configure system...")) { for (int i=0; isong.systemLen; i++) { if (ImGui::TreeNode(fmt::sprintf("%d. %s##_SYSP%d",i+1,getSystemName(e->song.system[i]),i).c_str())) { - drawSysConf(i); + drawSysConf(i,e->song.system[i],e->song.systemFlags[i],true); ImGui::TreePop(); } } diff --git a/src/gui/gui.h b/src/gui/gui.h index 31ab566f1..9608e5eb7 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -857,6 +857,7 @@ class FurnaceGUI { String audioDevice; String midiInDevice; String midiOutDevice; + std::vector initialSys; Settings(): mainFontSize(18), @@ -1114,7 +1115,7 @@ class FurnaceGUI { void drawAlgorithm(unsigned char alg, FurnaceGUIFMAlgs algType, const ImVec2& size); void drawFMEnv(unsigned char tl, unsigned char ar, unsigned char dr, unsigned char d2r, unsigned char rr, unsigned char sl, unsigned char sus, unsigned char egt, unsigned char algOrGlobalSus, float maxTl, float maxArDr, const ImVec2& size, unsigned short instType); void drawGBEnv(unsigned char vol, unsigned char len, unsigned char sLen, bool dir, const ImVec2& size); - void drawSysConf(int i); + void drawSysConf(int chan, DivSystem type, unsigned int& flags, bool modifyOnChange); // these ones offer ctrl-wheel fine value changes. bool CWSliderScalar(const char* label, ImGuiDataType data_type, void* p_data, const void* p_min, const void* p_max, const char* format=NULL, ImGuiSliderFlags flags=0); diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index 6f8640871..9eeeeae9d 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -23,6 +23,7 @@ #include "../fileutils.h" #include "util.h" #include "guiConst.h" +#include "intConst.h" #include "ImGuiFileDialog.h" #include "IconsFontAwesome4.h" #include "misc/cpp/imgui_stdlib.h" @@ -219,7 +220,8 @@ void FurnaceGUI::drawSettings() { } if (ImGui::BeginTabBar("settingsTab")) { if (ImGui::BeginTabItem("General")) { - ImGui::Text("Workspace layout"); + ImGui::Text("Workspace layout:"); + ImGui::SameLine(); if (ImGui::Button("Import")) { openFileDialog(GUI_FILE_IMPORT_LAYOUT); } @@ -231,7 +233,108 @@ void FurnaceGUI::drawSettings() { if (ImGui::Button("Reset")) { showWarning("Are you sure you want to reset the workspace layout?",GUI_WARN_RESET_LAYOUT); } + ImGui::Separator(); + + ImGui::Text("Initial system/chips:"); + ImGui::SameLine(); + if (ImGui::Button("Current systems")) { + settings.initialSys.clear(); + for (int i=0; isong.systemLen; i++) { + settings.initialSys.push_back(e->song.system[i]); + settings.initialSys.push_back(e->song.systemVol[i]); + settings.initialSys.push_back(e->song.systemPan[i]); + settings.initialSys.push_back(e->song.systemFlags[i]); + } + } + ImGui::SameLine(); + if (ImGui::Button("Randomize")) { + settings.initialSys.clear(); + int howMany=1+rand()%3; + int totalAvailSys=0; + for (totalAvailSys=0; availableSystems[totalAvailSys]; totalAvailSys++); + if (totalAvailSys>0) { + for (int i=0; i=8) { + settings.initialSys.erase(settings.initialSys.begin()+i,settings.initialSys.begin()+i+4); + i-=4; + } + } + + if (ImGui::Button(ICON_FA_PLUS "##InitSysAdd")) { + settings.initialSys.push_back(DIV_SYSTEM_YM2612); + settings.initialSys.push_back(64); + settings.initialSys.push_back(0); + settings.initialSys.push_back(0); + } + + ImGui::Separator(); + ImGui::Text("Toggle channel solo on:"); if (ImGui::RadioButton("Right-click or double-click##soloA",settings.soloAction==0)) { settings.soloAction=0; @@ -1755,6 +1858,19 @@ void FurnaceGUI::syncSettings() { clampSetting(settings.moveWindowTitle,0,1); clampSetting(settings.hiddenSystems,0,1); + settings.initialSys=e->decodeSysDesc(e->getConfString("initialSys","")); + if (settings.initialSys.size()<4) { + settings.initialSys.clear(); + settings.initialSys.push_back(DIV_SYSTEM_YM2612); + settings.initialSys.push_back(64); + settings.initialSys.push_back(0); + settings.initialSys.push_back(0); + settings.initialSys.push_back(DIV_SYSTEM_SMS); + settings.initialSys.push_back(32); + settings.initialSys.push_back(0); + settings.initialSys.push_back(0); + } + // keybinds for (int i=0; isetConf("eventDelay",settings.eventDelay); e->setConf("moveWindowTitle",settings.moveWindowTitle); e->setConf("hiddenSystems",settings.hiddenSystems); + e->setConf("initialSys",e->encodeSysDesc(settings.initialSys)); // colors for (int i=0; isong.systemFlags[i]; - bool restart=settings.restartOnFlagChange; +void FurnaceGUI::drawSysConf(int chan, DivSystem type, unsigned int& flags, bool modifyOnChange) { + bool restart=settings.restartOnFlagChange && modifyOnChange; bool sysPal=flags&1; - switch (e->song.system[i]) { + unsigned int copyOfFlags=flags; + switch (type) { case DIV_SYSTEM_YM2612: case DIV_SYSTEM_YM2612_EXT: { if (ImGui::RadioButton("NTSC (7.67MHz)",(flags&3)==0)) { - e->setSysFlags(i,(flags&0x80000000)|0,restart); - updateWindowTitle(); + copyOfFlags=(flags&0x80000000)|0; } if (ImGui::RadioButton("PAL (7.61MHz)",(flags&3)==1)) { - e->setSysFlags(i,(flags&0x80000000)|1,restart); - updateWindowTitle(); + copyOfFlags=(flags&0x80000000)|1; } if (ImGui::RadioButton("FM Towns (8MHz)",(flags&3)==2)) { - e->setSysFlags(i,(flags&0x80000000)|2,restart); - updateWindowTitle(); + copyOfFlags=(flags&0x80000000)|2; } if (ImGui::RadioButton("AtGames Genesis (6.13MHz)",(flags&3)==3)) { - e->setSysFlags(i,(flags&0x80000000)|3,restart); - updateWindowTitle(); + copyOfFlags=(flags&0x80000000)|3; } bool ladder=flags&0x80000000; if (ImGui::Checkbox("Enable DAC distortion",&ladder)) { - e->setSysFlags(i,(flags&(~0x80000000))|(ladder?0x80000000:0),restart); - updateWindowTitle(); + copyOfFlags=(flags&(~0x80000000))|(ladder?0x80000000:0); } break; } case DIV_SYSTEM_SMS: { ImGui::Text("Clock rate:"); if (ImGui::RadioButton("NTSC (3.58MHz)",(flags&3)==0)) { - e->setSysFlags(i,(flags&(~3))|0,restart); - updateWindowTitle(); + copyOfFlags=(flags&(~3))|0; + } if (ImGui::RadioButton("PAL (3.55MHz)",(flags&3)==1)) { - e->setSysFlags(i,(flags&(~3))|1,restart); - updateWindowTitle(); + copyOfFlags=(flags&(~3))|1; + } if (ImGui::RadioButton("BBC Micro (4MHz)",(flags&3)==2)) { - e->setSysFlags(i,(flags&(~3))|2,restart); - updateWindowTitle(); + copyOfFlags=(flags&(~3))|2; + } if (ImGui::RadioButton("Half NTSC (1.79MHz)",(flags&3)==3)) { - e->setSysFlags(i,(flags&(~3))|3,restart); - updateWindowTitle(); + copyOfFlags=(flags&(~3))|3; + } ImGui::Text("Chip type:"); if (ImGui::RadioButton("Sega VDP/Master System",((flags>>2)&3)==0)) { - e->setSysFlags(i,(flags&(~12))|0,restart); - updateWindowTitle(); + copyOfFlags=(flags&(~12))|0; + } if (ImGui::RadioButton("TI SN76489",((flags>>2)&3)==1)) { - e->setSysFlags(i,(flags&(~12))|4,restart); - updateWindowTitle(); + copyOfFlags=(flags&(~12))|4; + } if (ImGui::RadioButton("TI SN76489 with Atari-like short noise",((flags>>2)&3)==2)) { - e->setSysFlags(i,(flags&(~12))|8,restart); - updateWindowTitle(); + copyOfFlags=(flags&(~12))|8; + } /*if (ImGui::RadioButton("Game Gear",(flags>>2)==3)) { - e->setSysFlags(i,(flags&3)|12); + copyOfFlags=(flags&3)|12); }*/ bool noPhaseReset=flags&16; if (ImGui::Checkbox("Disable noise period change phase reset",&noPhaseReset)) { - e->setSysFlags(i,(flags&(~16))|(noPhaseReset<<4),restart); - updateWindowTitle(); + copyOfFlags=(flags&(~16))|(noPhaseReset<<4); + } break; } @@ -96,54 +91,54 @@ void FurnaceGUI::drawSysConf(int i) { case DIV_SYSTEM_VRC7: { ImGui::Text("Clock rate:"); if (ImGui::RadioButton("NTSC (3.58MHz)",(flags&15)==0)) { - e->setSysFlags(i,(flags&(~15))|0,restart); - updateWindowTitle(); + copyOfFlags=(flags&(~15))|0; + } if (ImGui::RadioButton("PAL (3.55MHz)",(flags&15)==1)) { - e->setSysFlags(i,(flags&(~15))|1,restart); - updateWindowTitle(); + copyOfFlags=(flags&(~15))|1; + } if (ImGui::RadioButton("BBC Micro (4MHz)",(flags&15)==2)) { - e->setSysFlags(i,(flags&(~15))|2,restart); - updateWindowTitle(); + copyOfFlags=(flags&(~15))|2; + } if (ImGui::RadioButton("Half NTSC (1.79MHz)",(flags&15)==3)) { - e->setSysFlags(i,(flags&(~15))|3,restart); - updateWindowTitle(); + copyOfFlags=(flags&(~15))|3; + } - if (e->song.system[i]!=DIV_SYSTEM_VRC7) { + if (type!=DIV_SYSTEM_VRC7) { ImGui::Text("Patch set:"); if (ImGui::RadioButton("Yamaha YM2413",((flags>>4)&15)==0)) { - e->setSysFlags(i,(flags&(~0xf0))|0,restart); - updateWindowTitle(); + copyOfFlags=(flags&(~0xf0))|0; + } if (ImGui::RadioButton("Yamaha YMF281",((flags>>4)&15)==1)) { - e->setSysFlags(i,(flags&(~0xf0))|0x10,restart); - updateWindowTitle(); + copyOfFlags=(flags&(~0xf0))|0x10; + } if (ImGui::RadioButton("Yamaha YM2423",((flags>>4)&15)==2)) { - e->setSysFlags(i,(flags&(~0xf0))|0x20,restart); - updateWindowTitle(); + copyOfFlags=(flags&(~0xf0))|0x20; + } if (ImGui::RadioButton("Konami VRC7",((flags>>4)&15)==3)) { - e->setSysFlags(i,(flags&(~0xf0))|0x30,restart); - updateWindowTitle(); + copyOfFlags=(flags&(~0xf0))|0x30; + } } break; } case DIV_SYSTEM_YM2151: if (ImGui::RadioButton("NTSC/X16 (3.58MHz)",flags==0)) { - e->setSysFlags(i,0,restart); - updateWindowTitle(); + copyOfFlags=0; + } if (ImGui::RadioButton("PAL (3.55MHz)",flags==1)) { - e->setSysFlags(i,1,restart); - updateWindowTitle(); + copyOfFlags=1; + } if (ImGui::RadioButton("X1/X68000 (4MHz)",flags==2)) { - e->setSysFlags(i,2,restart); - updateWindowTitle(); + copyOfFlags=2; + } break; case DIV_SYSTEM_NES: @@ -151,120 +146,117 @@ void FurnaceGUI::drawSysConf(int i) { case DIV_SYSTEM_FDS: case DIV_SYSTEM_MMC5: if (ImGui::RadioButton("NTSC (1.79MHz)",flags==0)) { - e->setSysFlags(i,0,restart); - updateWindowTitle(); + copyOfFlags=0; + } if (ImGui::RadioButton("PAL (1.67MHz)",flags==1)) { - e->setSysFlags(i,1,restart); - updateWindowTitle(); + copyOfFlags=1; + } if (ImGui::RadioButton("Dendy (1.77MHz)",flags==2)) { - e->setSysFlags(i,2,restart); - updateWindowTitle(); + copyOfFlags=2; + } break; case DIV_SYSTEM_C64_8580: case DIV_SYSTEM_C64_6581: if (ImGui::RadioButton("NTSC (1.02MHz)",flags==0)) { - e->setSysFlags(i,0,restart); - updateWindowTitle(); + copyOfFlags=0; + } if (ImGui::RadioButton("PAL (0.99MHz)",flags==1)) { - e->setSysFlags(i,1,restart); - updateWindowTitle(); + copyOfFlags=1; + } if (ImGui::RadioButton("SSI 2001 (0.89MHz)",flags==2)) { - e->setSysFlags(i,2,restart); - updateWindowTitle(); + copyOfFlags=2; + } break; case DIV_SYSTEM_AY8910: case DIV_SYSTEM_AY8930: { ImGui::Text("Clock rate:"); if (ImGui::RadioButton("1.79MHz (ZX Spectrum NTSC/MSX)",(flags&15)==0)) { - e->setSysFlags(i,(flags&(~15))|0,restart); - updateWindowTitle(); + copyOfFlags=(flags&(~15))|0; + } if (ImGui::RadioButton("1.77MHz (ZX Spectrum)",(flags&15)==1)) { - e->setSysFlags(i,(flags&(~15))|1,restart); - updateWindowTitle(); + copyOfFlags=(flags&(~15))|1; + } if (ImGui::RadioButton("1.75MHz (ZX Spectrum)",(flags&15)==2)) { - e->setSysFlags(i,(flags&(~15))|2,restart); - updateWindowTitle(); + copyOfFlags=(flags&(~15))|2; + } if (ImGui::RadioButton("2MHz (Atari ST/Sharp X1)",(flags&15)==3)) { - e->setSysFlags(i,(flags&(~15))|3,restart); - updateWindowTitle(); + copyOfFlags=(flags&(~15))|3; + } if (ImGui::RadioButton("1.5MHz (Vectrex)",(flags&15)==4)) { - e->setSysFlags(i,(flags&(~15))|4,restart); - updateWindowTitle(); + copyOfFlags=(flags&(~15))|4; + } if (ImGui::RadioButton("1MHz (Amstrad CPC)",(flags&15)==5)) { - e->setSysFlags(i,(flags&(~15))|5,restart); - updateWindowTitle(); + copyOfFlags=(flags&(~15))|5; + } if (ImGui::RadioButton("0.89MHz (Sunsoft 5B)",(flags&15)==6)) { - e->setSysFlags(i,(flags&(~15))|6,restart); - updateWindowTitle(); + copyOfFlags=(flags&(~15))|6; + } if (ImGui::RadioButton("1.67MHz (?)",(flags&15)==7)) { - e->setSysFlags(i,(flags&(~15))|7,restart); - updateWindowTitle(); + copyOfFlags=(flags&(~15))|7; + } if (ImGui::RadioButton("0.83MHz (Sunsoft 5B on PAL)",(flags&15)==8)) { - e->setSysFlags(i,(flags&(~15))|8,restart); - updateWindowTitle(); + copyOfFlags=(flags&(~15))|8; + } if (ImGui::RadioButton("1.10MHz (Gamate/VIC-20 PAL)",(flags&15)==9)) { - e->setSysFlags(i,(flags&(~15))|9,restart); - updateWindowTitle(); + copyOfFlags=(flags&(~15))|9; + } if (ImGui::RadioButton("2^21Hz (Game Boy)",(flags&15)==10)) { - e->setSysFlags(i,(flags&(~15))|10,restart); - updateWindowTitle(); + copyOfFlags=(flags&(~15))|10; + } - if (e->song.system[i]==DIV_SYSTEM_AY8910) { + if (type==DIV_SYSTEM_AY8910) { ImGui::Text("Chip type:"); if (ImGui::RadioButton("AY-3-8910",(flags&0x30)==0)) { - e->setSysFlags(i,(flags&(~0x30))|0,restart); - updateWindowTitle(); + copyOfFlags=(flags&(~0x30))|0; + } if (ImGui::RadioButton("YM2149(F)",(flags&0x30)==16)) { - e->setSysFlags(i,(flags&(~0x30))|16,restart); - updateWindowTitle(); + copyOfFlags=(flags&(~0x30))|16; + } if (ImGui::RadioButton("Sunsoft 5B",(flags&0x30)==32)) { - e->setSysFlags(i,(flags&(~0x30))|32,restart); - updateWindowTitle(); + copyOfFlags=(flags&(~0x30))|32; + } if (ImGui::RadioButton("AY-3-8914",(flags&0x30)==48)) { - e->setSysFlags(i,(flags&(~0x30))|48,restart); - updateWindowTitle(); + copyOfFlags=(flags&(~0x30))|48; + } } bool stereo=flags&0x40; ImGui::BeginDisabled((flags&0x30)==32); if (ImGui::Checkbox("Stereo##_AY_STEREO",&stereo)) { - e->setSysFlags(i,(flags&(~0x40))|(stereo?0x40:0),restart); - updateWindowTitle(); + copyOfFlags=(flags&(~0x40))|(stereo?0x40:0); + } ImGui::EndDisabled(); break; } case DIV_SYSTEM_SAA1099: if (ImGui::RadioButton("SAM Coupé (8MHz)",flags==0)) { - e->setSysFlags(i,0,restart); - updateWindowTitle(); + copyOfFlags=0; } if (ImGui::RadioButton("NTSC (7.15MHz)",flags==1)) { - e->setSysFlags(i,1,restart); - updateWindowTitle(); + copyOfFlags=1; } if (ImGui::RadioButton("PAL (7.09MHz)",flags==2)) { - e->setSysFlags(i,2,restart); - updateWindowTitle(); + copyOfFlags=2; } break; case DIV_SYSTEM_AMIGA: { @@ -273,44 +265,37 @@ void FurnaceGUI::drawSysConf(int i) { if (CWSliderInt("##StereoSep",&stereoSep,0,127)) { if (stereoSep<0) stereoSep=0; if (stereoSep>127) stereoSep=127; - e->setSysFlags(i,(flags&(~0x7f00))|((stereoSep&127)<<8),restart); - updateWindowTitle(); + copyOfFlags=(flags&(~0x7f00))|((stereoSep&127)<<8); } rightClickable if (ImGui::RadioButton("Amiga 500 (OCS)",(flags&2)==0)) { - e->setSysFlags(i,flags&(~2),restart); + copyOfFlags=flags&(~2); } if (ImGui::RadioButton("Amiga 1200 (AGA)",(flags&2)==2)) { - e->setSysFlags(i,(flags&(~2))|2,restart); + copyOfFlags=(flags&(~2))|2; } sysPal=flags&1; if (ImGui::Checkbox("PAL",&sysPal)) { - e->setSysFlags(i,(flags&(~1))|(unsigned int)sysPal,restart); - updateWindowTitle(); + copyOfFlags=(flags&(~1))|(unsigned int)sysPal; } bool bypassLimits=flags&4; if (ImGui::Checkbox("Bypass frequency limits",&bypassLimits)) { - e->setSysFlags(i,(flags&(~4))|(bypassLimits<<2),restart); - updateWindowTitle(); + copyOfFlags=(flags&(~4))|(bypassLimits<<2); } break; } case DIV_SYSTEM_PCSPKR: { ImGui::Text("Speaker type:"); if (ImGui::RadioButton("Unfiltered",(flags&3)==0)) { - e->setSysFlags(i,(flags&(~3))|0,restart); - updateWindowTitle(); + copyOfFlags=(flags&(~3))|0; } if (ImGui::RadioButton("Cone",(flags&3)==1)) { - e->setSysFlags(i,(flags&(~3))|1,restart); - updateWindowTitle(); + copyOfFlags=(flags&(~3))|1; } if (ImGui::RadioButton("Piezo",(flags&3)==2)) { - e->setSysFlags(i,(flags&(~3))|2,restart); - updateWindowTitle(); + copyOfFlags=(flags&(~3))|2; } if (ImGui::RadioButton("Use system beeper (Linux only!)",(flags&3)==3)) { - e->setSysFlags(i,(flags&(~3))|3,restart); - updateWindowTitle(); + copyOfFlags=(flags&(~3))|3; } break; } @@ -320,62 +305,53 @@ void FurnaceGUI::drawSysConf(int i) { if (CWSliderInt("##EchoBufSize",&echoBufSize,0,2725)) { if (echoBufSize<0) echoBufSize=0; if (echoBufSize>2725) echoBufSize=2725; - e->setSysFlags(i,(flags & ~4095) | ((2725 - echoBufSize) & 4095),restart); - updateWindowTitle(); + copyOfFlags=(flags & ~4095) | ((2725 - echoBufSize) & 4095); } rightClickable ImGui::Text("Echo feedback:"); int echoFeedback=(flags>>12)&255; if (CWSliderInt("##EchoFeedback",&echoFeedback,0,255)) { if (echoFeedback<0) echoFeedback=0; if (echoFeedback>255) echoFeedback=255; - e->setSysFlags(i,(flags & ~(255 << 12)) | ((echoFeedback & 255) << 12),restart); - updateWindowTitle(); + copyOfFlags=(flags & ~(255 << 12)) | ((echoFeedback & 255) << 12); } rightClickable break; } case DIV_SYSTEM_X1_010: { ImGui::Text("Clock rate:"); if (ImGui::RadioButton("16MHz (Seta 1)",(flags&15)==0)) { - e->setSysFlags(i,(flags&(~15))|0,restart); - updateWindowTitle(); + copyOfFlags=(flags&(~15))|0; } if (ImGui::RadioButton("16.67MHz (Seta 2)",(flags&15)==1)) { - e->setSysFlags(i,(flags&(~15))|1,restart); - updateWindowTitle(); + copyOfFlags=(flags&(~15))|1; } bool x1_010Stereo=flags&16; if (ImGui::Checkbox("Stereo",&x1_010Stereo)) { - e->setSysFlags(i,(flags&(~16))|(x1_010Stereo<<4),restart); - updateWindowTitle(); + copyOfFlags=(flags&(~16))|(x1_010Stereo<<4); } break; } case DIV_SYSTEM_N163: { ImGui::Text("Clock rate:"); if (ImGui::RadioButton("NTSC (1.79MHz)",(flags&15)==0)) { - e->setSysFlags(i,(flags&(~15))|0,restart); - updateWindowTitle(); + copyOfFlags=(flags&(~15))|0; } if (ImGui::RadioButton("PAL (1.67MHz)",(flags&15)==1)) { - e->setSysFlags(i,(flags&(~15))|1,restart); - updateWindowTitle(); + copyOfFlags=(flags&(~15))|1; } if (ImGui::RadioButton("Dendy (1.77MHz)",(flags&15)==2)) { - e->setSysFlags(i,(flags&(~15))|2,restart); - updateWindowTitle(); + copyOfFlags=(flags&(~15))|2; } ImGui::Text("Initial channel limit:"); int initialChannelLimit=((flags>>4)&7)+1; if (CWSliderInt("##InitialChannelLimit",&initialChannelLimit,1,8)) { if (initialChannelLimit<1) initialChannelLimit=1; if (initialChannelLimit>8) initialChannelLimit=8; - e->setSysFlags(i,(flags & ~(7 << 4)) | (((initialChannelLimit-1) & 7) << 4),restart); - updateWindowTitle(); + copyOfFlags=(flags & ~(7 << 4)) | (((initialChannelLimit-1) & 7) << 4); + } rightClickable bool n163Multiplex=flags&128; if (ImGui::Checkbox("Disable hissing",&n163Multiplex)) { - e->setSysFlags(i,(flags&(~128))|(n163Multiplex<<7),restart); - updateWindowTitle(); + copyOfFlags=(flags&(~128))|(n163Multiplex<<7); } break; } @@ -395,9 +371,17 @@ void FurnaceGUI::drawSysConf(int i) { break; default: if (ImGui::Checkbox("PAL",&sysPal)) { - e->setSysFlags(i,sysPal,restart); - updateWindowTitle(); + copyOfFlags=sysPal; } break; } + + if (copyOfFlags!=flags) { + if (chan>=0) { + e->setSysFlags(chan,copyOfFlags,restart); + updateWindowTitle(); + } else { + flags=copyOfFlags; + } + } } From ba657fe4db68f40685dba5393093de3a94e34b72 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 30 Apr 2022 02:02:55 -0500 Subject: [PATCH 252/342] GUI: add backdrop if system file picker is open --- TODO.md | 1 - src/gui/fileDialog.cpp | 4 ++++ src/gui/fileDialog.h | 1 + src/gui/gui.cpp | 13 +++++++++++++ 4 files changed, 18 insertions(+), 1 deletion(-) diff --git a/TODO.md b/TODO.md index 09f794ab7..18c43c49d 100644 --- a/TODO.md +++ b/TODO.md @@ -13,7 +13,6 @@ - maybe YMU759 ADPCM channel - ADPCM chips - more effects for FM param control -- store system presets in new file - Game Boy envelope macro/sequence - option to display chip names instead of "multi-system" on title bar - rewrite the system name detection function anyway diff --git a/src/gui/fileDialog.cpp b/src/gui/fileDialog.cpp index 3b8fdd46a..e50298c1a 100644 --- a/src/gui/fileDialog.cpp +++ b/src/gui/fileDialog.cpp @@ -95,6 +95,10 @@ bool FurnaceGUIFileDialog::render(const ImVec2& min, const ImVec2& max) { } } +bool FurnaceGUIFileDialog::isOpen() { + return opened; +} + String FurnaceGUIFileDialog::getPath() { if (sysDialog) { if (curPath.size()>1) { diff --git a/src/gui/fileDialog.h b/src/gui/fileDialog.h index 8d3ff787a..5eb67d853 100644 --- a/src/gui/fileDialog.h +++ b/src/gui/fileDialog.h @@ -24,6 +24,7 @@ class FurnaceGUIFileDialog { bool accepted(); void close(); bool render(const ImVec2& min, const ImVec2& max); + bool isOpen(); String getPath(); String getFileName(); explicit FurnaceGUIFileDialog(bool system): diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 932a7f81b..dd37ceae1 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -2882,6 +2882,19 @@ bool FurnaceGUI::loop() { #endif } + if (fileDialog->isOpen() && settings.sysFileDialog) { + ImGui::OpenPopup("System File Dialog Pending"); + } + + if (ImGui::BeginPopupModal("System File Dialog Pending",NULL,ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoBackground|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoMove)) { + if (!fileDialog->isOpen()) { + ImGui::CloseCurrentPopup(); + } + ImDrawList* dl=ImGui::GetForegroundDrawList(); + dl->AddRectFilled(ImVec2(0.0f,0.0f),ImVec2(scrW*dpiScale,scrH*dpiScale),ImGui::ColorConvertFloat4ToU32(uiColors[GUI_COLOR_MODAL_BACKDROP])); + ImGui::EndPopup(); + } + if (fileDialog->render(ImVec2(600.0f*dpiScale,400.0f*dpiScale),ImVec2(scrW*dpiScale,scrH*dpiScale))) { bool openOpen=false; //ImGui::GetIO().ConfigFlags&=~ImGuiConfigFlags_NavEnableKeyboard; From 4197fa44fb0779d1260e26fcc0a82ec4c6915307 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 30 Apr 2022 03:58:30 -0500 Subject: [PATCH 253/342] GUI: prepare for a per-channel oscilloscope? currently supported systems: - Amiga - AY-3-8910 - Dummy System - OPLL - SegaPCM no trigger supported yet! --- CMakeLists.txt | 1 + src/engine/dispatch.h | 19 ++++++ src/engine/engine.cpp | 10 +++ src/engine/engine.h | 6 ++ src/engine/platform/abstract.cpp | 4 ++ src/engine/platform/amiga.cpp | 30 +++++++-- src/engine/platform/amiga.h | 2 + src/engine/platform/ay.cpp | 19 +++++- src/engine/platform/ay.h | 2 + src/engine/platform/dummy.cpp | 22 ++++++- src/engine/platform/dummy.h | 2 + src/engine/platform/opll.cpp | 16 ++++- src/engine/platform/opll.h | 2 + src/engine/platform/segapcm.cpp | 15 +++++ src/engine/platform/segapcm.h | 2 + src/gui/chanOsc.cpp | 101 +++++++++++++++++++++++++++++++ src/gui/doAction.cpp | 6 ++ src/gui/gui.cpp | 9 ++- src/gui/gui.h | 12 +++- src/gui/guiConst.cpp | 3 +- 20 files changed, 269 insertions(+), 14 deletions(-) create mode 100644 src/gui/chanOsc.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 75a3d3350..b757062f2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -389,6 +389,7 @@ src/gui/guiConst.cpp src/gui/about.cpp src/gui/channels.cpp +src/gui/chanOsc.cpp src/gui/compatFlags.cpp src/gui/cursor.cpp src/gui/dataList.cpp diff --git a/src/engine/dispatch.h b/src/engine/dispatch.h index 21598442b..7e5f998b9 100644 --- a/src/engine/dispatch.h +++ b/src/engine/dispatch.h @@ -21,6 +21,7 @@ #define _DISPATCH_H #include +#include #include #define ONE_SEMITONE 2200 @@ -205,6 +206,18 @@ struct DivRegWrite { addr(a), val(v) {} }; +struct DivDispatchOscBuffer { + unsigned int rate; + unsigned short needle; + short data[65536]; + + DivDispatchOscBuffer(): + rate(65536), + needle(0) { + memset(data,0,65536*sizeof(short)); + } +}; + class DivEngine; class DivMacroInt; @@ -268,6 +281,12 @@ class DivDispatch { * @return a pointer, or NULL. */ virtual DivMacroInt* getChanMacroInt(int chan); + + /** + * get an oscilloscope buffer for a channel. + * @return a pointer to a DivDispatchOscBuffer, or NULL if not supported. + */ + virtual DivDispatchOscBuffer* getOscBuffer(int chan); /** * get the register pool of this dispatch. diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 47d70093b..f8f518933 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -986,6 +986,16 @@ unsigned char* DivEngine::getRegisterPool(int sys, int& size, int& depth) { return disCont[sys].dispatch->getRegisterPool(); } +DivMacroInt* DivEngine::getMacroInt(int chan) { + if (chan<0 || chan>=chans) return NULL; + return disCont[dispatchOfChan[chan]].dispatch->getChanMacroInt(dispatchChanOfChan[chan]); +} + +DivDispatchOscBuffer* DivEngine::getOscBuffer(int chan) { + if (chan<0 || chan>=chans) return NULL; + return disCont[dispatchOfChan[chan]].dispatch->getOscBuffer(dispatchChanOfChan[chan]); +} + void DivEngine::enableCommandStream(bool enable) { cmdStreamEnabled=enable; } diff --git a/src/engine/engine.h b/src/engine/engine.h index 7dd7489cb..2ca301c53 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -722,6 +722,12 @@ class DivEngine { // get register pool unsigned char* getRegisterPool(int sys, int& size, int& depth); + // get macro interpreter + DivMacroInt* getMacroInt(int chan); + + // get osc buffer + DivDispatchOscBuffer* getOscBuffer(int chan); + // enable command stream dumping void enableCommandStream(bool enable); diff --git a/src/engine/platform/abstract.cpp b/src/engine/platform/abstract.cpp index 56f2aa3d7..91f61fc62 100644 --- a/src/engine/platform/abstract.cpp +++ b/src/engine/platform/abstract.cpp @@ -33,6 +33,10 @@ DivMacroInt* DivDispatch::getChanMacroInt(int chan) { return NULL; } +DivDispatchOscBuffer* DivDispatch::getOscBuffer(int chan) { + return NULL; +} + unsigned char* DivDispatch::getRegisterPool() { return NULL; } diff --git a/src/engine/platform/amiga.cpp b/src/engine/platform/amiga.cpp index 406b3d406..e78c3a77b 100644 --- a/src/engine/platform/amiga.cpp +++ b/src/engine/platform/amiga.cpp @@ -91,12 +91,15 @@ const char* DivPlatformAmiga::getEffectName(unsigned char effect) { } void DivPlatformAmiga::acquire(short* bufL, short* bufR, size_t start, size_t len) { - static int outL, outR; + static int outL, outR, output; for (size_t h=start; hdata[oscBuf[i]->needle++]=0; + continue; + } if (chan[i].useWave || (chan[i].sample>=0 && chan[i].samplesong.sampleLen)) { chan[i].audSub-=AMIGA_DIVIDER; if (chan[i].audSub<0) { @@ -139,13 +142,17 @@ void DivPlatformAmiga::acquire(short* bufL, short* bufR, size_t start, size_t le } } if (!isMuted[i]) { + output=chan[i].audDat*chan[i].outVol; if (i==0 || i==3) { - outL+=((chan[i].audDat*chan[i].outVol)*sep1)>>7; - outR+=((chan[i].audDat*chan[i].outVol)*sep2)>>7; + outL+=(output*sep1)>>7; + outR+=(output*sep2)>>7; } else { - outL+=((chan[i].audDat*chan[i].outVol)*sep2)>>7; - outR+=((chan[i].audDat*chan[i].outVol)*sep1)>>7; + outL+=(output*sep2)>>7; + outR+=(output*sep1)>>7; } + oscBuf[i]->data[oscBuf[i]->needle++]=output<<2; + } else { + oscBuf[i]->data[oscBuf[i]->needle++]=0; } } filter[0][0]+=(filtConst*(outL-filter[0][0]))>>12; @@ -419,6 +426,10 @@ void* DivPlatformAmiga::getChanState(int ch) { return &chan[ch]; } +DivDispatchOscBuffer* DivPlatformAmiga::getOscBuffer(int ch) { + return oscBuf[ch]; +} + void DivPlatformAmiga::reset() { for (int i=0; i<4; i++) { chan[i]=DivPlatformAmiga::Channel(); @@ -469,6 +480,9 @@ void DivPlatformAmiga::setFlags(unsigned int flags) { chipClock=COLOR_NTSC; } rate=chipClock/AMIGA_DIVIDER; + for (int i=0; i<4; i++) { + oscBuf[i]->rate=rate; + } sep1=((flags>>8)&127)+127; sep2=127-((flags>>8)&127); amigaModel=flags&2; @@ -487,6 +501,7 @@ int DivPlatformAmiga::init(DivEngine* p, int channels, int sugRate, unsigned int dumpWrites=false; skipRegisterWrites=false; for (int i=0; i<4; i++) { + oscBuf[i]=new DivDispatchOscBuffer; isMuted[i]=false; } setFlags(flags); @@ -495,4 +510,7 @@ int DivPlatformAmiga::init(DivEngine* p, int channels, int sugRate, unsigned int } void DivPlatformAmiga::quit() { + for (int i=0; i<4; i++) { + delete oscBuf[i]; + } } diff --git a/src/engine/platform/amiga.h b/src/engine/platform/amiga.h index 0777100d7..539f7830c 100644 --- a/src/engine/platform/amiga.h +++ b/src/engine/platform/amiga.h @@ -74,6 +74,7 @@ class DivPlatformAmiga: public DivDispatch { outVol(64) {} }; Channel chan[4]; + DivDispatchOscBuffer* oscBuf[4]; bool isMuted[4]; bool bypassLimits; bool amigaModel; @@ -91,6 +92,7 @@ class DivPlatformAmiga: public DivDispatch { void acquire(short* bufL, short* bufR, size_t start, size_t len); int dispatch(DivCommand c); void* getChanState(int chan); + DivDispatchOscBuffer* getOscBuffer(int chan); void reset(); void forceIns(); void tick(bool sysTick=true); diff --git a/src/engine/platform/ay.cpp b/src/engine/platform/ay.cpp index 12ea0f64a..37108452e 100644 --- a/src/engine/platform/ay.cpp +++ b/src/engine/platform/ay.cpp @@ -146,6 +146,12 @@ void DivPlatformAY8910::acquire(short* bufL, short* bufR, size_t start, size_t l bufR[i+start]=bufL[i+start]; } } + + for (int ch=0; ch<3; ch++) { + for (size_t i=0; idata[oscBuf[ch]->needle++]=ayBuf[ch][i]; + } + } } void DivPlatformAY8910::updateOutSel(bool immediate) { @@ -500,6 +506,10 @@ void* DivPlatformAY8910::getChanState(int ch) { return &chan[ch]; } +DivDispatchOscBuffer* DivPlatformAY8910::getOscBuffer(int ch) { + return oscBuf[ch]; +} + unsigned char* DivPlatformAY8910::getRegisterPool() { return regPool; } @@ -615,6 +625,9 @@ void DivPlatformAY8910::setFlags(unsigned int flags) { break; } rate=chipClock/8; + for (int i=0; i<3; i++) { + oscBuf[i]->rate=rate; + } if (ay!=NULL) delete ay; switch ((flags>>4)&3) { @@ -650,6 +663,7 @@ int DivPlatformAY8910::init(DivEngine* p, int channels, int sugRate, unsigned in skipRegisterWrites=false; for (int i=0; i<3; i++) { isMuted[i]=false; + oscBuf[i]=new DivDispatchOscBuffer; } ay=NULL; setFlags(flags); @@ -660,6 +674,9 @@ int DivPlatformAY8910::init(DivEngine* p, int channels, int sugRate, unsigned in } void DivPlatformAY8910::quit() { - for (int i=0; i<3; i++) delete[] ayBuf[i]; + for (int i=0; i<3; i++) { + delete oscBuf[i]; + delete[] ayBuf[i]; + } if (ay!=NULL) delete ay; } diff --git a/src/engine/platform/ay.h b/src/engine/platform/ay.h index 2bc4a03d3..b257e3bbc 100644 --- a/src/engine/platform/ay.h +++ b/src/engine/platform/ay.h @@ -56,6 +56,7 @@ class DivPlatformAY8910: public DivDispatch { }; std::queue writes; ay8910_device* ay; + DivDispatchOscBuffer* oscBuf[3]; unsigned char regPool[16]; unsigned char lastBusy; @@ -90,6 +91,7 @@ class DivPlatformAY8910: public DivDispatch { void acquire(short* bufL, short* bufR, size_t start, size_t len); int dispatch(DivCommand c); void* getChanState(int chan); + DivDispatchOscBuffer* getOscBuffer(int chan); unsigned char* getRegisterPool(); int getRegisterPoolSize(); void flushWrites(); diff --git a/src/engine/platform/dummy.cpp b/src/engine/platform/dummy.cpp index 9b972ac18..34d614eb3 100644 --- a/src/engine/platform/dummy.cpp +++ b/src/engine/platform/dummy.cpp @@ -25,12 +25,21 @@ #define CHIP_FREQBASE 2048 void DivPlatformDummy::acquire(short* bufL, short* bufR, size_t start, size_t len) { + int chanOut; for (size_t i=start; i>12; + if (!isMuted[j]) { + chanOut=(((signed short)chan[j].pos)*chan[j].amp*chan[j].vol)>>12; + oscBuf[j]->data[oscBuf[j]->needle++]=chanOut; + out+=chanOut; + } else { + oscBuf[j]->data[oscBuf[j]->needle++]=0; + } chan[j].pos+=chan[j].freq; + } else { + oscBuf[j]->data[oscBuf[j]->needle++]=0; } } if (out<-32768) out=-32768; @@ -61,6 +70,10 @@ void* DivPlatformDummy::getChanState(int ch) { return &chan[ch]; } +DivDispatchOscBuffer* DivPlatformDummy::getOscBuffer(int ch) { + return oscBuf[ch]; +} + int DivPlatformDummy::dispatch(DivCommand c) { switch (c.cmd) { case DIV_CMD_NOTE_ON: @@ -131,6 +144,10 @@ int DivPlatformDummy::init(DivEngine* p, int channels, int sugRate, unsigned int skipRegisterWrites=false; for (int i=0; irate=65536; + } } rate=65536; chipClock=65536; @@ -140,6 +157,9 @@ int DivPlatformDummy::init(DivEngine* p, int channels, int sugRate, unsigned int } void DivPlatformDummy::quit() { + for (int i=0; i=6 && properDrums) || !isMuted[nextOut]) { + oscBuf[nextOut]->data[oscBuf[nextOut]->needle++]=(o[0]+o[1])<<6; os+=(o[0]+o[1]); + } else { + oscBuf[nextOut]->data[oscBuf[nextOut]->needle++]=0; } } os*=50; @@ -731,6 +734,10 @@ void* DivPlatformOPLL::getChanState(int ch) { return &chan[ch]; } +DivDispatchOscBuffer* DivPlatformOPLL::getOscBuffer(int ch) { + return oscBuf[ch]; +} + unsigned char* DivPlatformOPLL::getRegisterPool() { return regPool; } @@ -842,6 +849,9 @@ void DivPlatformOPLL::setFlags(unsigned int flags) { } rate=chipClock/36; patchSet=flags>>4; + for (int i=0; i<11; i++) { + oscBuf[i]->rate=rate/2; + } } int DivPlatformOPLL::init(DivEngine* p, int channels, int sugRate, unsigned int flags) { @@ -851,14 +861,18 @@ int DivPlatformOPLL::init(DivEngine* p, int channels, int sugRate, unsigned int patchSet=0; for (int i=0; i<11; i++) { isMuted[i]=false; + oscBuf[i]=new DivDispatchOscBuffer; } setFlags(flags); reset(); - return 10; + return 11; } void DivPlatformOPLL::quit() { + for (int i=0; i<11; i++) { + delete oscBuf[i]; + } } DivPlatformOPLL::~DivPlatformOPLL() { diff --git a/src/engine/platform/opll.h b/src/engine/platform/opll.h index 96cb1a914..7a06bbb77 100644 --- a/src/engine/platform/opll.h +++ b/src/engine/platform/opll.h @@ -63,6 +63,7 @@ class DivPlatformOPLL: public DivDispatch { }; Channel chan[11]; bool isMuted[11]; + DivDispatchOscBuffer* oscBuf[11]; struct QueuedWrite { unsigned short addr; unsigned char val; @@ -100,6 +101,7 @@ class DivPlatformOPLL: public DivDispatch { void acquire(short* bufL, short* bufR, size_t start, size_t len); int dispatch(DivCommand c); void* getChanState(int chan); + DivDispatchOscBuffer* getOscBuffer(int chan); unsigned char* getRegisterPool(); int getRegisterPoolSize(); void reset(); diff --git a/src/engine/platform/segapcm.cpp b/src/engine/platform/segapcm.cpp index eb506ff36..346daeae1 100644 --- a/src/engine/platform/segapcm.cpp +++ b/src/engine/platform/segapcm.cpp @@ -46,9 +46,11 @@ void DivPlatformSegaPCM::acquire(short* bufL, short* bufR, size_t start, size_t DivSample* s=parent->getSample(chan[i].pcm.sample); if (s->samples<=0) { chan[i].pcm.sample=-1; + oscBuf[i]->data[oscBuf[i]->needle++]=0; continue; } if (!isMuted[i]) { + oscBuf[i]->data[oscBuf[i]->needle++]=s->data8[chan[i].pcm.pos>>8]*(chan[i].chVolL+chan[i].chVolR)>>1; pcmL+=(s->data8[chan[i].pcm.pos>>8]*chan[i].chVolL); pcmR+=(s->data8[chan[i].pcm.pos>>8]*chan[i].chVolR); } @@ -60,6 +62,8 @@ void DivPlatformSegaPCM::acquire(short* bufL, short* bufR, size_t start, size_t chan[i].pcm.sample=-1; } } + } else { + oscBuf[i]->data[oscBuf[i]->needle++]=0; } } @@ -359,6 +363,10 @@ void* DivPlatformSegaPCM::getChanState(int ch) { return &chan[ch]; } +DivDispatchOscBuffer* DivPlatformSegaPCM::getOscBuffer(int ch) { + return oscBuf[ch]; +} + unsigned char* DivPlatformSegaPCM::getRegisterPool() { return regPool; } @@ -408,6 +416,9 @@ void DivPlatformSegaPCM::reset() { void DivPlatformSegaPCM::setFlags(unsigned int flags) { chipClock=8000000.0; rate=31250; + for (int i=0; i<16; i++) { + oscBuf[i]->rate=rate; + } } bool DivPlatformSegaPCM::isStereo() { @@ -420,6 +431,7 @@ int DivPlatformSegaPCM::init(DivEngine* p, int channels, int sugRate, unsigned i skipRegisterWrites=false; for (int i=0; i<16; i++) { isMuted[i]=false; + oscBuf[i]=new DivDispatchOscBuffer; } setFlags(flags); reset(); @@ -428,6 +440,9 @@ int DivPlatformSegaPCM::init(DivEngine* p, int channels, int sugRate, unsigned i } void DivPlatformSegaPCM::quit() { + for (int i=0; i<16; i++) { + delete oscBuf[i]; + } } DivPlatformSegaPCM::~DivPlatformSegaPCM() { diff --git a/src/engine/platform/segapcm.h b/src/engine/platform/segapcm.h index 7ef09892a..32cd22c29 100644 --- a/src/engine/platform/segapcm.h +++ b/src/engine/platform/segapcm.h @@ -49,6 +49,7 @@ class DivPlatformSegaPCM: public DivDispatch { Channel(): freqH(0), freqL(0), freq(0), baseFreq(0), pitch(0), pitch2(0), note(0), ins(-1), active(false), insChanged(true), freqChanged(false), keyOn(false), keyOff(false), inPorta(false), portaPause(false), furnacePCM(false), vol(0), outVol(0), chVolL(127), chVolR(127) {} }; Channel chan[16]; + DivDispatchOscBuffer* oscBuf[16]; struct QueuedWrite { unsigned short addr; unsigned char val; @@ -77,6 +78,7 @@ class DivPlatformSegaPCM: public DivDispatch { void acquire(short* bufL, short* bufR, size_t start, size_t len); int dispatch(DivCommand c); void* getChanState(int chan); + DivDispatchOscBuffer* getOscBuffer(int chan); unsigned char* getRegisterPool(); int getRegisterPoolSize(); void reset(); diff --git a/src/gui/chanOsc.cpp b/src/gui/chanOsc.cpp new file mode 100644 index 000000000..c4924e7ef --- /dev/null +++ b/src/gui/chanOsc.cpp @@ -0,0 +1,101 @@ +/** + * Furnace Tracker - multi-system chiptune tracker + * Copyright (C) 2021-2022 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 "imgui.h" +#include "imgui_internal.h" + +void FurnaceGUI::drawChanOsc() { + if (nextWindow==GUI_WINDOW_CHAN_OSC) { + chanOscOpen=true; + ImGui::SetNextWindowFocus(); + nextWindow=GUI_WINDOW_NOTHING; + } + if (!chanOscOpen) return; + ImGui::SetNextWindowSizeConstraints(ImVec2(64.0f*dpiScale,32.0f*dpiScale),ImVec2(scrW*dpiScale,scrH*dpiScale)); + if (ImGui::Begin("Oscilloscope (per-channel)",&chanOscOpen)) { + if (ImGui::InputInt("Columns",&chanOscCols,1,1)) { + if (chanOscCols<1) chanOscCols=1; + if (chanOscCols>64) chanOscCols=64; + } + + ImGui::PushStyleVar(ImGuiStyleVar_CellPadding,ImVec2(0.0f,0.0f)); + float availY=ImGui::GetContentRegionAvail().y; + if (ImGui::BeginTable("ChanOsc",chanOscCols,ImGuiTableFlags_Borders)) { + int chans=e->getTotalChannelCount(); + int rows=(chans+(chanOscCols-1))/chanOscCols; + ImDrawList* dl=ImGui::GetWindowDrawList(); + ImGuiWindow* window=ImGui::GetCurrentWindow(); + ImVec2 waveform[512]; + + ImGuiStyle& style=ImGui::GetStyle(); + ImU32 color=ImGui::GetColorU32(uiColors[GUI_COLOR_OSC_WAVE]); + + for (int i=0; igetOscBuffer(i); + if (buf==NULL) { + ImGui::Text("Not Available"); + } else { + ImVec2 size=ImGui::GetContentRegionAvail(); + size.y=availY/rows; + + int displaySize=(buf->rate)/30; + + ImVec2 minArea=window->DC.CursorPos; + ImVec2 maxArea=ImVec2( + minArea.x+size.x, + minArea.y+size.y + ); + ImRect rect=ImRect(minArea,maxArea); + ImRect inRect=rect; + inRect.Min.x+=dpiScale; + inRect.Min.y+=dpiScale; + inRect.Max.x-=dpiScale; + inRect.Max.y-=dpiScale; + ImGui::ItemSize(size,style.FramePadding.y); + if (ImGui::ItemAdd(rect,ImGui::GetID("chOscDisplay"))) { + if (!e->isPlaying()) { + for (unsigned short i=0; i<512; i++) { + float x=(float)i/512.0f; + waveform[i]=ImLerp(inRect.Min,inRect.Max,ImVec2(x,0.5f)); + } + } else { + unsigned short needlePos=buf->needle-displaySize; + for (unsigned short i=0; i<512; i++) { + float x=(float)i/512.0f; + float y=(float)buf->data[(unsigned short)(needlePos+(i*displaySize/512))]/65536.0f; + 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)); + } + } + dl->AddPolyline(waveform,512,color,ImDrawFlags_None,dpiScale); + } + } + } + ImGui::EndTable(); + } + ImGui::PopStyleVar(); + } + if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_CHAN_OSC; + ImGui::End(); +} \ No newline at end of file diff --git a/src/gui/doAction.cpp b/src/gui/doAction.cpp index 4020dfc7b..92ce732f9 100644 --- a/src/gui/doAction.cpp +++ b/src/gui/doAction.cpp @@ -217,6 +217,9 @@ void FurnaceGUI::doAction(int what) { case GUI_ACTION_WINDOW_EFFECT_LIST: nextWindow=GUI_WINDOW_EFFECT_LIST; break; + case GUI_ACTION_WINDOW_CHAN_OSC: + nextWindow=GUI_WINDOW_CHAN_OSC; + break; case GUI_ACTION_COLLAPSE_WINDOW: collapseWindow=true; @@ -295,6 +298,9 @@ void FurnaceGUI::doAction(int what) { case GUI_WINDOW_EFFECT_LIST: effectListOpen=false; break; + case GUI_WINDOW_CHAN_OSC: + chanOscOpen=false; + break; default: break; } diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index dd37ceae1..6d63d0f50 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -2757,7 +2757,8 @@ bool FurnaceGUI::loop() { ImGui::Separator(); if (ImGui::MenuItem("play/edit controls",BIND_FOR(GUI_ACTION_WINDOW_EDIT_CONTROLS),editControlsOpen)) editControlsOpen=!editControlsOpen; if (ImGui::MenuItem("piano/input pad",BIND_FOR(GUI_ACTION_WINDOW_PIANO),pianoOpen)) pianoOpen=!pianoOpen; - if (ImGui::MenuItem("oscilloscope",BIND_FOR(GUI_ACTION_WINDOW_OSCILLOSCOPE),oscOpen)) oscOpen=!oscOpen; + if (ImGui::MenuItem("oscilloscope (master)",BIND_FOR(GUI_ACTION_WINDOW_OSCILLOSCOPE),oscOpen)) oscOpen=!oscOpen; + if (ImGui::MenuItem("oscilloscope (per-channel)",BIND_FOR(GUI_ACTION_WINDOW_CHAN_OSC),chanOscOpen)) chanOscOpen=!chanOscOpen; if (ImGui::MenuItem("volume meter",BIND_FOR(GUI_ACTION_WINDOW_VOL_METER),volMeterOpen)) volMeterOpen=!volMeterOpen; if (ImGui::MenuItem("register view",BIND_FOR(GUI_ACTION_WINDOW_REGISTER_VIEW),regViewOpen)) regViewOpen=!regViewOpen; if (ImGui::MenuItem("log viewer",BIND_FOR(GUI_ACTION_WINDOW_LOG),logOpen)) logOpen=!logOpen; @@ -2860,6 +2861,7 @@ bool FurnaceGUI::loop() { readOsc(); drawOsc(); + drawChanOsc(); drawVolMeter(); drawSettings(); drawDebug(); @@ -3556,6 +3558,7 @@ bool FurnaceGUI::init() { settingsOpen=e->getConfBool("settingsOpen",false); mixerOpen=e->getConfBool("mixerOpen",false); oscOpen=e->getConfBool("oscOpen",true); + chanOscOpen=e->getConfBool("chanOscOpen",false); volMeterOpen=e->getConfBool("volMeterOpen",true); statsOpen=e->getConfBool("statsOpen",false); compatFlagsOpen=e->getConfBool("compatFlagsOpen",false); @@ -3731,6 +3734,7 @@ bool FurnaceGUI::finish() { e->setConf("settingsOpen",settingsOpen); e->setConf("mixerOpen",mixerOpen); e->setConf("oscOpen",oscOpen); + e->setConf("chanOscOpen",chanOscOpen); e->setConf("volMeterOpen",volMeterOpen); e->setConf("statsOpen",statsOpen); e->setConf("compatFlagsOpen",compatFlagsOpen); @@ -3851,6 +3855,7 @@ FurnaceGUI::FurnaceGUI(): regViewOpen(false), logOpen(false), effectListOpen(false), + chanOscOpen(false), /* editControlsDocked(false), ordersDocked(false), @@ -3877,6 +3882,7 @@ FurnaceGUI::FurnaceGUI(): regViewDocked(false), logDocked(false), effectListDocked(false), + chanOscDocked(false), */ selecting(false), curNibble(false), @@ -3993,6 +3999,7 @@ FurnaceGUI::FurnaceGUI(): oscTotal(0), oscZoom(0.5f), oscZoomSlider(false), + chanOscCols(3), followLog(true), pianoOctaves(7), pianoOptions(false), diff --git a/src/gui/gui.h b/src/gui/gui.h index 9608e5eb7..cf532eef5 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -230,7 +230,8 @@ enum FurnaceGUIWindows { GUI_WINDOW_CHANNELS, GUI_WINDOW_REGISTER_VIEW, GUI_WINDOW_LOG, - GUI_WINDOW_EFFECT_LIST + GUI_WINDOW_EFFECT_LIST, + GUI_WINDOW_CHAN_OSC }; enum FurnaceGUIFileDialogs { @@ -330,6 +331,7 @@ enum FurnaceGUIActions { GUI_ACTION_WINDOW_REGISTER_VIEW, GUI_ACTION_WINDOW_LOG, GUI_ACTION_WINDOW_EFFECT_LIST, + GUI_ACTION_WINDOW_CHAN_OSC, GUI_ACTION_COLLAPSE_WINDOW, GUI_ACTION_CLOSE_WINDOW, @@ -948,13 +950,13 @@ class FurnaceGUI { bool editControlsOpen, ordersOpen, insListOpen, songInfoOpen, patternOpen, insEditOpen; bool waveListOpen, waveEditOpen, sampleListOpen, sampleEditOpen, aboutOpen, settingsOpen; bool mixerOpen, debugOpen, inspectorOpen, oscOpen, volMeterOpen, statsOpen, compatFlagsOpen; - bool pianoOpen, notesOpen, channelsOpen, regViewOpen, logOpen, effectListOpen; + bool pianoOpen, notesOpen, channelsOpen, regViewOpen, logOpen, effectListOpen, chanOscOpen; /* there ought to be a better way... bool editControlsDocked, ordersDocked, insListDocked, songInfoDocked, patternDocked, insEditDocked; bool waveListDocked, waveEditDocked, sampleListDocked, sampleEditDocked, aboutDocked, settingsDocked; bool mixerDocked, debugDocked, inspectorDocked, oscDocked, volMeterDocked, statsDocked, compatFlagsDocked; - bool pianoDocked, notesDocked, channelsDocked, regViewDocked, logDocked, effectListDocked; + bool pianoDocked, notesDocked, channelsDocked, regViewDocked, logDocked, effectListDocked, chanOscDocked; */ SelectionPoint selStart, selEnd, cursor; @@ -1097,6 +1099,9 @@ class FurnaceGUI { float oscZoom; bool oscZoomSlider; + // per-channel oscilloscope + int chanOscCols; + // visualizer float keyHit[DIV_MAX_CHANS]; int lastIns[DIV_MAX_CHANS]; @@ -1151,6 +1156,7 @@ class FurnaceGUI { void drawSampleEdit(); void drawMixer(); void drawOsc(); + void drawChanOsc(); void drawVolMeter(); void drawStats(); void drawCompatFlags(); diff --git a/src/gui/guiConst.cpp b/src/gui/guiConst.cpp index ec157a9eb..0cc1894ab 100644 --- a/src/gui/guiConst.cpp +++ b/src/gui/guiConst.cpp @@ -475,7 +475,7 @@ const FurnaceGUIActionDef guiActions[GUI_ACTION_MAX]={ D("WINDOW_SETTINGS", "Settings", 0), D("WINDOW_MIXER", "Mixer", 0), D("WINDOW_DEBUG", "Debug Menu", 0), - D("WINDOW_OSCILLOSCOPE", "Oscilloscope", 0), + D("WINDOW_OSCILLOSCOPE", "Oscilloscope (master)", 0), D("WINDOW_VOL_METER", "Volume Meter", 0), D("WINDOW_STATS", "Statistics", 0), D("WINDOW_COMPAT_FLAGS", "Compatibility Flags", 0), @@ -485,6 +485,7 @@ const FurnaceGUIActionDef guiActions[GUI_ACTION_MAX]={ D("WINDOW_REGISTER_VIEW", "Register View", 0), D("WINDOW_LOG", "Log Viewer", 0), D("EFFECT_LIST", "Effect List", 0), + D("WINDOW_CHAN_OSC", "Oscilloscope (per-channel)", 0), D("COLLAPSE_WINDOW", "Collapse/expand current window", 0), D("CLOSE_WINDOW", "Close current window", FURKMOD_SHIFT|SDLK_ESCAPE), From fae3de53a93002a6e5bd787c2564a0b64f5c65f2 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 30 Apr 2022 04:30:33 -0500 Subject: [PATCH 254/342] maybe uninitialized --- src/engine/engine.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index f8f518933..3a0a841d8 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -639,7 +639,10 @@ std::vector DivEngine::decodeSysDesc(String desc) { bool negative=false; int val=0; int curStage=0; - int sysID, sysVol, sysPan, sysFlags; + int sysID=0; + int sysVol=0; + int sysPan=0; + int sysFlags=0; desc+=' '; // ha for (char i: desc) { switch (i) { From c738ece3746f334edbbaaec1eb618f15ca0dee88 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 30 Apr 2022 14:36:12 -0500 Subject: [PATCH 255/342] per-channel oscilloscope, part 2 AY8930, K00, C64 and NES --- src/engine/platform/ay8930.cpp | 20 +++++++++++++++++++- src/engine/platform/ay8930.h | 2 ++ src/engine/platform/bubsyswsg.cpp | 19 +++++++++++++++++-- src/engine/platform/bubsyswsg.h | 2 ++ src/engine/platform/c64.cpp | 18 ++++++++++++++++++ src/engine/platform/c64.h | 3 +++ src/engine/platform/nes.cpp | 20 ++++++++++++++++++++ src/engine/platform/nes.h | 3 +++ src/engine/platform/sound/c64/sid.cc | 18 ++++++++++++++++-- src/engine/platform/sound/c64/sid.h | 2 ++ 10 files changed, 102 insertions(+), 5 deletions(-) diff --git a/src/engine/platform/ay8930.cpp b/src/engine/platform/ay8930.cpp index e0cbd9803..13308698c 100644 --- a/src/engine/platform/ay8930.cpp +++ b/src/engine/platform/ay8930.cpp @@ -149,6 +149,12 @@ void DivPlatformAY8930::acquire(short* bufL, short* bufR, size_t start, size_t l bufR[i+start]=bufL[i+start]; } } + + for (int ch=0; ch<3; ch++) { + for (size_t i=0; idata[oscBuf[ch]->needle++]=ayBuf[ch][i]; + } + } } void DivPlatformAY8930::updateOutSel(bool immediate) { @@ -523,6 +529,10 @@ void* DivPlatformAY8930::getChanState(int ch) { return &chan[ch]; } +DivDispatchOscBuffer* DivPlatformAY8930::getOscBuffer(int ch) { + return oscBuf[ch]; +} + unsigned char* DivPlatformAY8930::getRegisterPool() { return regPool; } @@ -629,6 +639,10 @@ void DivPlatformAY8930::setFlags(unsigned int flags) { break; } rate=chipClock/8; + for (int i=0; i<3; i++) { + oscBuf[i]->rate=rate; + } + stereo=flags>>6; } @@ -638,6 +652,7 @@ int DivPlatformAY8930::init(DivEngine* p, int channels, int sugRate, unsigned in skipRegisterWrites=false; for (int i=0; i<3; i++) { isMuted[i]=false; + oscBuf[i]=new DivDispatchOscBuffer; } setFlags(flags); ay=new ay8930_device(rate); @@ -649,6 +664,9 @@ int DivPlatformAY8930::init(DivEngine* p, int channels, int sugRate, unsigned in } void DivPlatformAY8930::quit() { - for (int i=0; i<3; i++) delete[] ayBuf[i]; + for (int i=0; i<3; i++) { + delete oscBuf[i]; + delete[] ayBuf[i]; + } delete ay; } diff --git a/src/engine/platform/ay8930.h b/src/engine/platform/ay8930.h index ced107fcf..ff992bb5e 100644 --- a/src/engine/platform/ay8930.h +++ b/src/engine/platform/ay8930.h @@ -52,6 +52,7 @@ class DivPlatformAY8930: public DivDispatch { }; std::queue writes; ay8930_device* ay; + DivDispatchOscBuffer* oscBuf[3]; unsigned char regPool[32]; unsigned char ayNoiseAnd, ayNoiseOr; bool bank; @@ -79,6 +80,7 @@ class DivPlatformAY8930: public DivDispatch { void acquire(short* bufL, short* bufR, size_t start, size_t len); int dispatch(DivCommand c); void* getChanState(int chan); + DivDispatchOscBuffer* getOscBuffer(int chan); unsigned char* getRegisterPool(); int getRegisterPoolSize(); void reset(); diff --git a/src/engine/platform/bubsyswsg.cpp b/src/engine/platform/bubsyswsg.cpp index 3224bfee9..2c09cf23d 100644 --- a/src/engine/platform/bubsyswsg.cpp +++ b/src/engine/platform/bubsyswsg.cpp @@ -49,6 +49,7 @@ const char* DivPlatformBubSysWSG::getEffectName(unsigned char effect) { } void DivPlatformBubSysWSG::acquire(short* bufL, short* bufR, size_t start, size_t len) { + int chanOut=0; for (size_t h=start; haddr(i)]*(regPool[2+i]&0xf); + if (isMuted[i]) { + oscBuf[i]->data[oscBuf[i]->needle++]=0; + continue; + } else { + chanOut=chan[i].waveROM[k005289->addr(i)]*(regPool[2+i]&0xf); + out+=chanOut; + oscBuf[i]->data[oscBuf[i]->needle++]=chanOut; + } } out<<=6; // scale output to 16 bit @@ -267,6 +274,10 @@ void* DivPlatformBubSysWSG::getChanState(int ch) { return &chan[ch]; } +DivDispatchOscBuffer* DivPlatformBubSysWSG::getOscBuffer(int ch) { + return oscBuf[ch]; +} + unsigned char* DivPlatformBubSysWSG::getRegisterPool() { return (unsigned char*)regPool; } @@ -335,6 +346,7 @@ int DivPlatformBubSysWSG::init(DivEngine* p, int channels, int sugRate, unsigned skipRegisterWrites=false; for (int i=0; i<2; i++) { isMuted[i]=false; + oscBuf[i]=new DivDispatchOscBuffer; } setFlags(flags); k005289=new k005289_core(); @@ -343,6 +355,9 @@ int DivPlatformBubSysWSG::init(DivEngine* p, int channels, int sugRate, unsigned } void DivPlatformBubSysWSG::quit() { + for (int i=0; i<2; i++) { + delete oscBuf[i]; + } delete k005289; } diff --git a/src/engine/platform/bubsyswsg.h b/src/engine/platform/bubsyswsg.h index a28b4ac1d..8eb6726fa 100644 --- a/src/engine/platform/bubsyswsg.h +++ b/src/engine/platform/bubsyswsg.h @@ -56,6 +56,7 @@ class DivPlatformBubSysWSG: public DivDispatch { wave(-1) {} }; Channel chan[2]; + DivDispatchOscBuffer* oscBuf[2]; bool isMuted[2]; k005289_core* k005289; @@ -66,6 +67,7 @@ class DivPlatformBubSysWSG: public DivDispatch { void acquire(short* bufL, short* bufR, size_t start, size_t len); int dispatch(DivCommand c); void* getChanState(int chan); + DivDispatchOscBuffer* getOscBuffer(int chan); unsigned char* getRegisterPool(); int getRegisterPoolSize(); int getRegisterPoolDepth(); diff --git a/src/engine/platform/c64.cpp b/src/engine/platform/c64.cpp index 7e9cf4d18..63f1b4709 100644 --- a/src/engine/platform/c64.cpp +++ b/src/engine/platform/c64.cpp @@ -112,6 +112,12 @@ void DivPlatformC64::acquire(short* bufL, short* bufR, size_t start, size_t len) for (size_t i=start; i=8) { + writeOscBuf=0; + oscBuf[0]->data[oscBuf[0]->needle++]=sid.last_chan_out[0]; + oscBuf[1]->data[oscBuf[1]->needle++]=sid.last_chan_out[1]; + oscBuf[2]->data[oscBuf[2]->needle++]=sid.last_chan_out[2]; + } } } @@ -485,6 +491,10 @@ void* DivPlatformC64::getChanState(int ch) { return &chan[ch]; } +DivDispatchOscBuffer* DivPlatformC64::getOscBuffer(int ch) { + return oscBuf[ch]; +} + unsigned char* DivPlatformC64::getRegisterPool() { return regPool; } @@ -545,14 +555,19 @@ void DivPlatformC64::setFlags(unsigned int flags) { break; } chipClock=rate; + for (int i=0; i<3; i++) { + oscBuf[i]->rate=rate/16; + } } int DivPlatformC64::init(DivEngine* p, int channels, int sugRate, unsigned int flags) { parent=p; dumpWrites=false; skipRegisterWrites=false; + writeOscBuf=0; for (int i=0; i<3; i++) { isMuted[i]=false; + oscBuf[i]=new DivDispatchOscBuffer; } setFlags(flags); @@ -562,6 +577,9 @@ int DivPlatformC64::init(DivEngine* p, int channels, int sugRate, unsigned int f } void DivPlatformC64::quit() { + for (int i=0; i<3; i++) { + delete oscBuf[i]; + } } DivPlatformC64::~DivPlatformC64() { diff --git a/src/engine/platform/c64.h b/src/engine/platform/c64.h index a4968fee5..495da461d 100644 --- a/src/engine/platform/c64.h +++ b/src/engine/platform/c64.h @@ -70,9 +70,11 @@ class DivPlatformC64: public DivDispatch { vol(15) {} }; Channel chan[3]; + DivDispatchOscBuffer* oscBuf[3]; bool isMuted[3]; unsigned char filtControl, filtRes, vol; + unsigned char writeOscBuf; int filtCut, resetTime; SID sid; @@ -85,6 +87,7 @@ class DivPlatformC64: public DivDispatch { void acquire(short* bufL, short* bufR, size_t start, size_t len); int dispatch(DivCommand c); void* getChanState(int chan); + DivDispatchOscBuffer* getOscBuffer(int chan); unsigned char* getRegisterPool(); int getRegisterPoolSize(); void reset(); diff --git a/src/engine/platform/nes.cpp b/src/engine/platform/nes.cpp index f7a290662..ca528d82a 100644 --- a/src/engine/platform/nes.cpp +++ b/src/engine/platform/nes.cpp @@ -115,6 +115,14 @@ void DivPlatformNES::acquire(short* bufL, short* bufR, size_t start, size_t len) if (sample>32767) sample=32767; if (sample<-32768) sample=-32768; bufL[i]=sample; + if (++writeOscBuf>=32) { + writeOscBuf=0; + oscBuf[0]->data[oscBuf[0]->needle++]=nes->S1.output<<7; + oscBuf[1]->data[oscBuf[1]->needle++]=nes->S2.output<<7; + oscBuf[2]->data[oscBuf[2]->needle++]=nes->TR.output<<7; + oscBuf[3]->data[oscBuf[3]->needle++]=nes->NS.output<<7; + oscBuf[4]->data[oscBuf[4]->needle++]=nes->DMC.output<<7; + } } } @@ -474,6 +482,10 @@ void* DivPlatformNES::getChanState(int ch) { return &chan[ch]; } +DivDispatchOscBuffer* DivPlatformNES::getOscBuffer(int ch) { + return oscBuf[ch]; +} + unsigned char* DivPlatformNES::getRegisterPool() { return regPool; } @@ -533,6 +545,9 @@ void DivPlatformNES::setFlags(unsigned int flags) { nes->apu.type=apuType; } chipClock=rate; + for (int i=0; i<5; i++) { + oscBuf[i]->rate=rate/32; + } } void DivPlatformNES::notifyInsDeletion(void* ins) { @@ -555,9 +570,11 @@ int DivPlatformNES::init(DivEngine* p, int channels, int sugRate, unsigned int f dumpWrites=false; skipRegisterWrites=false; nes=new struct NESAPU; + writeOscBuf=0; for (int i=0; i<5; i++) { isMuted[i]=false; nes->muted[i]=false; + oscBuf[i]=new DivDispatchOscBuffer; } setFlags(flags); @@ -567,6 +584,9 @@ int DivPlatformNES::init(DivEngine* p, int channels, int sugRate, unsigned int f } void DivPlatformNES::quit() { + for (int i=0; i<5; i++) { + delete oscBuf[i]; + } delete nes; } diff --git a/src/engine/platform/nes.h b/src/engine/platform/nes.h index d88b447f4..abd23da99 100644 --- a/src/engine/platform/nes.h +++ b/src/engine/platform/nes.h @@ -57,11 +57,13 @@ class DivPlatformNES: public DivDispatch { wave(-1) {} }; Channel chan[5]; + DivDispatchOscBuffer* oscBuf[5]; bool isMuted[5]; int dacPeriod, dacRate; unsigned int dacPos, dacAntiClick; int dacSample; unsigned char sampleBank; + unsigned char writeOscBuf; unsigned char apuType; bool dacAntiClickOn; struct NESAPU* nes; @@ -73,6 +75,7 @@ class DivPlatformNES: public DivDispatch { void acquire(short* bufL, short* bufR, size_t start, size_t len); int dispatch(DivCommand c); void* getChanState(int chan); + DivDispatchOscBuffer* getOscBuffer(int chan); unsigned char* getRegisterPool(); int getRegisterPoolSize(); void reset(); diff --git a/src/engine/platform/sound/c64/sid.cc b/src/engine/platform/sound/c64/sid.cc index d33a0d350..d6ebbb449 100644 --- a/src/engine/platform/sound/c64/sid.cc +++ b/src/engine/platform/sound/c64/sid.cc @@ -44,6 +44,10 @@ SID::SID() isMuted[0]=false; isMuted[1]=false; isMuted[2]=false; + + last_chan_out[0]=0; + last_chan_out[1]=0; + last_chan_out[2]=0; } @@ -638,8 +642,13 @@ void SID::clock() voice[i].wave.synchronize(); } + // write voice output + last_chan_out[0]=isMuted[0]?0:voice[0].output(); + last_chan_out[1]=isMuted[1]?0:voice[1].output(); + last_chan_out[2]=isMuted[2]?0:voice[2].output(); + // Clock filter. - filter.clock(isMuted[0]?0:voice[0].output(), isMuted[1]?0:voice[1].output(), isMuted[2]?0:voice[2].output(), ext_in); + filter.clock(last_chan_out[0], last_chan_out[1], last_chan_out[2], ext_in); // Clock external filter. extfilt.clock(filter.output()); @@ -717,9 +726,14 @@ void SID::clock(cycle_count delta_t) delta_t_osc -= delta_t_min; } + // write voice output + last_chan_out[0]=isMuted[0]?0:voice[0].output(); + last_chan_out[1]=isMuted[1]?0:voice[1].output(); + last_chan_out[2]=isMuted[2]?0:voice[2].output(); + // Clock filter. filter.clock(delta_t, - isMuted[0]?0:voice[0].output(), isMuted[1]?0:voice[1].output(), isMuted[2]?0:voice[2].output(), ext_in); + last_chan_out[0], last_chan_out[1], last_chan_out[2], ext_in); // Clock external filter. extfilt.clock(delta_t, filter.output()); diff --git a/src/engine/platform/sound/c64/sid.h b/src/engine/platform/sound/c64/sid.h index 6dbe6c599..f6b392713 100644 --- a/src/engine/platform/sound/c64/sid.h +++ b/src/engine/platform/sound/c64/sid.h @@ -32,6 +32,8 @@ public: SID(); ~SID(); + sound_sample last_chan_out[3]; + void set_is_muted(int ch, bool val); void set_chip_model(chip_model model); void enable_filter(bool enable); From 312a0378160af7cf8af191f0b2f875ea26bec368 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 30 Apr 2022 17:50:38 -0500 Subject: [PATCH 256/342] GUI: add missing actions to settings --- src/gui/settings.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index 9eeeeae9d..152f94b8c 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -1412,6 +1412,7 @@ void FurnaceGUI::drawSettings() { UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_MIXER); UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_DEBUG); UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_OSCILLOSCOPE); + UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_CHAN_OSC); UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_VOL_METER); UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_STATS); UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_COMPAT_FLAGS); @@ -1517,6 +1518,10 @@ void FurnaceGUI::drawSettings() { UI_KEYBIND_CONFIG(GUI_ACTION_PAT_NOTE_DOWN); UI_KEYBIND_CONFIG(GUI_ACTION_PAT_OCTAVE_UP); UI_KEYBIND_CONFIG(GUI_ACTION_PAT_OCTAVE_DOWN); + UI_KEYBIND_CONFIG(GUI_ACTION_PAT_VALUE_UP); + UI_KEYBIND_CONFIG(GUI_ACTION_PAT_VALUE_DOWN); + UI_KEYBIND_CONFIG(GUI_ACTION_PAT_VALUE_UP_COARSE); + UI_KEYBIND_CONFIG(GUI_ACTION_PAT_VALUE_DOWN_COARSE); UI_KEYBIND_CONFIG(GUI_ACTION_PAT_SELECT_ALL); UI_KEYBIND_CONFIG(GUI_ACTION_PAT_CUT); UI_KEYBIND_CONFIG(GUI_ACTION_PAT_COPY); From 1c3dcae05e9aa98e0ec6cded73578d4ddba62418 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 30 Apr 2022 18:33:12 -0500 Subject: [PATCH 257/342] per-channel oscilloscope, part 3 K00/C64/NES fixed FDS, Game Boy and Sound Unit --- src/engine/platform/bubsyswsg.cpp | 10 +++++++++- src/engine/platform/bubsyswsg.h | 1 + src/engine/platform/c64.cpp | 6 +++--- src/engine/platform/fds.cpp | 12 ++++++++++++ src/engine/platform/fds.h | 3 +++ src/engine/platform/gb.cpp | 17 +++++++++++++++-- src/engine/platform/gb.h | 2 ++ src/engine/platform/nes.cpp | 10 +++++----- src/engine/platform/sound/su.h | 6 ++++++ src/engine/platform/su.cpp | 14 ++++++++++++++ src/engine/platform/su.h | 2 ++ 11 files changed, 72 insertions(+), 11 deletions(-) diff --git a/src/engine/platform/bubsyswsg.cpp b/src/engine/platform/bubsyswsg.cpp index 2c09cf23d..b12a92ed4 100644 --- a/src/engine/platform/bubsyswsg.cpp +++ b/src/engine/platform/bubsyswsg.cpp @@ -63,10 +63,14 @@ void DivPlatformBubSysWSG::acquire(short* bufL, short* bufR, size_t start, size_ } else { chanOut=chan[i].waveROM[k005289->addr(i)]*(regPool[2+i]&0xf); out+=chanOut; - oscBuf[i]->data[oscBuf[i]->needle++]=chanOut; + if (writeOscBuf==0) { + oscBuf[i]->data[oscBuf[i]->needle++]=chanOut<<7; + } } } + if (++writeOscBuf>=64) writeOscBuf=0; + out<<=6; // scale output to 16 bit if (out<-32768) out=-32768; @@ -330,6 +334,9 @@ void DivPlatformBubSysWSG::notifyInsDeletion(void* ins) { void DivPlatformBubSysWSG::setFlags(unsigned int flags) { chipClock=COLOR_NTSC; rate=chipClock; + for (int i=0; i<2; i++) { + oscBuf[i]->rate=rate/64; + } } void DivPlatformBubSysWSG::poke(unsigned int addr, unsigned short val) { @@ -344,6 +351,7 @@ int DivPlatformBubSysWSG::init(DivEngine* p, int channels, int sugRate, unsigned parent=p; dumpWrites=false; skipRegisterWrites=false; + writeOscBuf=0; for (int i=0; i<2; i++) { isMuted[i]=false; oscBuf[i]=new DivDispatchOscBuffer; diff --git a/src/engine/platform/bubsyswsg.h b/src/engine/platform/bubsyswsg.h index 8eb6726fa..cb30d85c0 100644 --- a/src/engine/platform/bubsyswsg.h +++ b/src/engine/platform/bubsyswsg.h @@ -58,6 +58,7 @@ class DivPlatformBubSysWSG: public DivDispatch { Channel chan[2]; DivDispatchOscBuffer* oscBuf[2]; bool isMuted[2]; + unsigned char writeOscBuf; k005289_core* k005289; unsigned short regPool[4]; diff --git a/src/engine/platform/c64.cpp b/src/engine/platform/c64.cpp index 63f1b4709..c0efdd6f2 100644 --- a/src/engine/platform/c64.cpp +++ b/src/engine/platform/c64.cpp @@ -114,9 +114,9 @@ void DivPlatformC64::acquire(short* bufL, short* bufR, size_t start, size_t len) bufL[i]=sid.output(); if (++writeOscBuf>=8) { writeOscBuf=0; - oscBuf[0]->data[oscBuf[0]->needle++]=sid.last_chan_out[0]; - oscBuf[1]->data[oscBuf[1]->needle++]=sid.last_chan_out[1]; - oscBuf[2]->data[oscBuf[2]->needle++]=sid.last_chan_out[2]; + oscBuf[0]->data[oscBuf[0]->needle++]=sid.last_chan_out[0]>>5; + oscBuf[1]->data[oscBuf[1]->needle++]=sid.last_chan_out[1]>>5; + oscBuf[2]->data[oscBuf[2]->needle++]=sid.last_chan_out[2]>>5; } } } diff --git a/src/engine/platform/fds.cpp b/src/engine/platform/fds.cpp index f1e2bfdec..4c8e92b38 100644 --- a/src/engine/platform/fds.cpp +++ b/src/engine/platform/fds.cpp @@ -85,6 +85,10 @@ void DivPlatformFDS::acquire(short* bufL, short* bufR, size_t start, size_t len) if (sample>32767) sample=32767; if (sample<-32768) sample=-32768; bufL[i]=sample; + if (++writeOscBuf>=32) { + writeOscBuf=0; + oscBuf->data[oscBuf->needle++]=sample<<1; + } } } @@ -396,6 +400,10 @@ void* DivPlatformFDS::getChanState(int ch) { return &chan[ch]; } +DivDispatchOscBuffer* DivPlatformFDS::getOscBuffer(int ch) { + return oscBuf; +} + unsigned char* DivPlatformFDS::getRegisterPool() { return regPool; } @@ -436,6 +444,7 @@ void DivPlatformFDS::setFlags(unsigned int flags) { rate=COLOR_NTSC/2.0; } chipClock=rate; + oscBuf->rate=rate/32; } void DivPlatformFDS::notifyInsDeletion(void* ins) { @@ -457,7 +466,9 @@ int DivPlatformFDS::init(DivEngine* p, int channels, int sugRate, unsigned int f apuType=flags; dumpWrites=false; skipRegisterWrites=false; + writeOscBuf=0; fds=new struct _fds; + oscBuf=new DivDispatchOscBuffer; for (int i=0; i<1; i++) { isMuted[i]=false; } @@ -468,6 +479,7 @@ int DivPlatformFDS::init(DivEngine* p, int channels, int sugRate, unsigned int f } void DivPlatformFDS::quit() { + delete oscBuf; delete fds; } diff --git a/src/engine/platform/fds.h b/src/engine/platform/fds.h index ed0c77d72..c7c53aea2 100644 --- a/src/engine/platform/fds.h +++ b/src/engine/platform/fds.h @@ -64,9 +64,11 @@ class DivPlatformFDS: public DivDispatch { } }; Channel chan[1]; + DivDispatchOscBuffer* oscBuf; bool isMuted[1]; DivWaveSynth ws; unsigned char apuType; + unsigned char writeOscBuf; struct _fds* fds; unsigned char regPool[128]; @@ -78,6 +80,7 @@ class DivPlatformFDS: public DivDispatch { void acquire(short* bufL, short* bufR, size_t start, size_t len); int dispatch(DivCommand c); void* getChanState(int chan); + DivDispatchOscBuffer* getOscBuffer(int chan); unsigned char* getRegisterPool(); int getRegisterPoolSize(); void reset(); diff --git a/src/engine/platform/gb.cpp b/src/engine/platform/gb.cpp index e55e703ef..1fc2b278e 100644 --- a/src/engine/platform/gb.cpp +++ b/src/engine/platform/gb.cpp @@ -87,6 +87,10 @@ void DivPlatformGB::acquire(short* bufL, short* bufR, size_t start, size_t len) GB_advance_cycles(gb,16); bufL[i]=gb->apu_output.final_sample.left; bufR[i]=gb->apu_output.final_sample.right; + + for (int i=0; i<4; i++) { + oscBuf[i]->data[oscBuf[i]->needle++]=(gb->apu_output.current_sample[i].left+gb->apu_output.current_sample[i].right)<<6; + } } } @@ -429,6 +433,10 @@ void* DivPlatformGB::getChanState(int ch) { return &chan[ch]; } +DivDispatchOscBuffer* DivPlatformGB::getOscBuffer(int ch) { + return oscBuf[ch]; +} + unsigned char* DivPlatformGB::getRegisterPool() { return regPool; } @@ -495,20 +503,25 @@ void DivPlatformGB::poke(std::vector& wlist) { } int DivPlatformGB::init(DivEngine* p, int channels, int sugRate, unsigned int flags) { + chipClock=4194304; + rate=chipClock/16; for (int i=0; i<4; i++) { isMuted[i]=false; + oscBuf[i]=new DivDispatchOscBuffer; + oscBuf[i]->rate=rate; } parent=p; dumpWrites=false; skipRegisterWrites=false; - chipClock=4194304; - rate=chipClock/16; gb=new GB_gameboy_t; reset(); return 4; } void DivPlatformGB::quit() { + for (int i=0; i<4; i++) { + delete oscBuf[i]; + } delete gb; } diff --git a/src/engine/platform/gb.h b/src/engine/platform/gb.h index 2cdbc7858..cce1ffb6c 100644 --- a/src/engine/platform/gb.h +++ b/src/engine/platform/gb.h @@ -57,6 +57,7 @@ class DivPlatformGB: public DivDispatch { wave(-1) {} }; Channel chan[4]; + DivDispatchOscBuffer* oscBuf[4]; bool isMuted[4]; unsigned char lastPan; DivWaveSynth ws; @@ -71,6 +72,7 @@ class DivPlatformGB: public DivDispatch { void acquire(short* bufL, short* bufR, size_t start, size_t len); int dispatch(DivCommand c); void* getChanState(int chan); + DivDispatchOscBuffer* getOscBuffer(int chan); unsigned char* getRegisterPool(); int getRegisterPoolSize(); void reset(); diff --git a/src/engine/platform/nes.cpp b/src/engine/platform/nes.cpp index ca528d82a..7f35dc5bd 100644 --- a/src/engine/platform/nes.cpp +++ b/src/engine/platform/nes.cpp @@ -117,11 +117,11 @@ void DivPlatformNES::acquire(short* bufL, short* bufR, size_t start, size_t len) bufL[i]=sample; if (++writeOscBuf>=32) { writeOscBuf=0; - oscBuf[0]->data[oscBuf[0]->needle++]=nes->S1.output<<7; - oscBuf[1]->data[oscBuf[1]->needle++]=nes->S2.output<<7; - oscBuf[2]->data[oscBuf[2]->needle++]=nes->TR.output<<7; - oscBuf[3]->data[oscBuf[3]->needle++]=nes->NS.output<<7; - oscBuf[4]->data[oscBuf[4]->needle++]=nes->DMC.output<<7; + oscBuf[0]->data[oscBuf[0]->needle++]=nes->S1.output<<11; + oscBuf[1]->data[oscBuf[1]->needle++]=nes->S2.output<<11; + oscBuf[2]->data[oscBuf[2]->needle++]=nes->TR.output<<11; + oscBuf[3]->data[oscBuf[3]->needle++]=nes->NS.output<<11; + oscBuf[4]->data[oscBuf[4]->needle++]=nes->DMC.output<<8; } } } diff --git a/src/engine/platform/sound/su.h b/src/engine/platform/sound/su.h index 972d5343e..3152e8568 100644 --- a/src/engine/platform/sound/su.h +++ b/src/engine/platform/sound/su.h @@ -88,6 +88,12 @@ class SoundUnit { bool muted[8]; void Write(unsigned char addr, unsigned char data); void NextSample(short* l, short* r); + inline int GetSample(int ch) { + int ret=(nsL[ch]+nsR[ch])>>1; + if (ret<-32768) ret=-32768; + if (ret>32767) ret=32767; + return ret; + } void Init(); void Reset(); SoundUnit(); diff --git a/src/engine/platform/su.cpp b/src/engine/platform/su.cpp index 3a6003cb8..a75b7c0b8 100644 --- a/src/engine/platform/su.cpp +++ b/src/engine/platform/su.cpp @@ -99,6 +99,9 @@ void DivPlatformSoundUnit::acquire(short* bufL, short* bufR, size_t start, size_ writes.pop(); } su->NextSample(&bufL[h],&bufR[h]); + for (int i=0; i<8; i++) { + oscBuf[i]->data[oscBuf[i]->needle++]=su->GetSample(i); + } } } @@ -313,6 +316,10 @@ void* DivPlatformSoundUnit::getChanState(int ch) { return &chan[ch]; } +DivDispatchOscBuffer* DivPlatformSoundUnit::getOscBuffer(int ch) { + return oscBuf[ch]; +} + unsigned char* DivPlatformSoundUnit::getRegisterPool() { return (unsigned char*)su->chan; } @@ -365,6 +372,9 @@ void DivPlatformSoundUnit::setFlags(unsigned int flags) { chipClock=1236000; } rate=chipClock/4; + for (int i=0; i<8; i++) { + oscBuf[i]->rate=rate; + } } void DivPlatformSoundUnit::poke(unsigned int addr, unsigned short val) { @@ -381,6 +391,7 @@ int DivPlatformSoundUnit::init(DivEngine* p, int channels, int sugRate, unsigned skipRegisterWrites=false; for (int i=0; i<8; i++) { isMuted[i]=false; + oscBuf[i]=new DivDispatchOscBuffer; } setFlags(flags); su=new SoundUnit(); @@ -390,6 +401,9 @@ int DivPlatformSoundUnit::init(DivEngine* p, int channels, int sugRate, unsigned } void DivPlatformSoundUnit::quit() { + for (int i=0; i<8; i++) { + delete oscBuf[i]; + } delete su; } diff --git a/src/engine/platform/su.h b/src/engine/platform/su.h index 7315f49a3..7ff3e8709 100644 --- a/src/engine/platform/su.h +++ b/src/engine/platform/su.h @@ -71,6 +71,7 @@ class DivPlatformSoundUnit: public DivDispatch { wave(0) {} }; Channel chan[8]; + DivDispatchOscBuffer* oscBuf[8]; bool isMuted[8]; struct QueuedWrite { unsigned char addr; @@ -94,6 +95,7 @@ class DivPlatformSoundUnit: public DivDispatch { void acquire(short* bufL, short* bufR, size_t start, size_t len); int dispatch(DivCommand c); void* getChanState(int chan); + DivDispatchOscBuffer* getOscBuffer(int chan); unsigned char* getRegisterPool(); int getRegisterPoolSize(); void reset(); From ddcd486c618aaf9253f0e8877113f1e92d0efc91 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 30 Apr 2022 20:52:48 -0500 Subject: [PATCH 258/342] YM2612: fix Furnace DAC mode glitching slides --- src/engine/platform/genesis.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/engine/platform/genesis.cpp b/src/engine/platform/genesis.cpp index 98dce6cff..b4b926454 100644 --- a/src/engine/platform/genesis.cpp +++ b/src/engine/platform/genesis.cpp @@ -647,7 +647,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) { break; } case DIV_CMD_NOTE_PORTA: { - if (c.chan==5 && chan[c.chan].furnaceDac) { + if (c.chan==5 && chan[c.chan].furnaceDac && dacMode) { int destFreq=parent->calcBaseFreq(1,1,c.value2,false); bool return2=false; if (destFreq>chan[c.chan].baseFreq) { @@ -724,7 +724,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) { } break; case DIV_CMD_LEGATO: { - if (c.chan==5 && chan[c.chan].furnaceDac) { + if (c.chan==5 && chan[c.chan].furnaceDac && dacMode) { chan[c.chan].baseFreq=parent->calcBaseFreq(1,1,c.value,false); } else { chan[c.chan].baseFreq=NOTE_FNUM_BLOCK(c.value,11); From e1fd16637c4ac0d72adad70eba0e12b39c353bb1 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 30 Apr 2022 22:59:26 -0500 Subject: [PATCH 259/342] per-channel oscilloscope, part 4 OPM, OPN2, OPLL and SN plus hide unimplemented channels --- extern/opm/opm.c | 8 +++++ extern/opm/opm.h | 2 ++ src/engine/platform/arcade.cpp | 51 ++++++++++++++++++----------- src/engine/platform/arcade.h | 2 ++ src/engine/platform/genesis.cpp | 14 +++++++- src/engine/platform/genesis.h | 2 ++ src/engine/platform/genesisext.cpp | 6 ++++ src/engine/platform/genesisext.h | 1 + src/engine/platform/opl.cpp | 38 +++++++++++++++++++++ src/engine/platform/opl.h | 3 ++ src/engine/platform/opll.cpp | 1 + src/engine/platform/sms.cpp | 22 ++++++++++++- src/engine/platform/sms.h | 2 ++ src/engine/platform/sound/sn76496.h | 3 ++ src/gui/chanOsc.cpp | 12 +++++-- 15 files changed, 143 insertions(+), 24 deletions(-) diff --git a/extern/opm/opm.c b/extern/opm/opm.c index 7a776fc06..79f6414cb 100644 --- a/extern/opm/opm.c +++ b/extern/opm/opm.c @@ -1256,6 +1256,14 @@ static inline void OPM_Mixer(opm_t *chip) } chip->mix[0] += chip->op_mix * chip->op_mixl; chip->mix[1] += chip->op_mix * chip->op_mixr; + + if (slot<8) { + chip->op_chmix[slot&7]=0; + } + chip->op_chmix[slot&7]+=chip->op_mix*(chip->op_mixl|chip->op_mixr); + if (slot>=24) { + chip->ch_out[slot&7]=chip->op_chmix[slot&7]; + } } static inline void OPM_Noise(opm_t *chip) diff --git a/extern/opm/opm.h b/extern/opm/opm.h index fe5a1518b..732f020fe 100644 --- a/extern/opm/opm.h +++ b/extern/opm/opm.h @@ -141,6 +141,7 @@ typedef struct { int16_t op_fb[2]; uint8_t op_mixl; uint8_t op_mixr; + uint16_t op_chmix[8]; // Mixer @@ -161,6 +162,7 @@ typedef struct { uint8_t smp_so; uint8_t smp_sh1; uint8_t smp_sh2; + uint16_t ch_out[8]; // Noise uint32_t noise_lfsr; diff --git a/src/engine/platform/arcade.cpp b/src/engine/platform/arcade.cpp index 11cff5e94..844fe67cf 100644 --- a/src/engine/platform/arcade.cpp +++ b/src/engine/platform/arcade.cpp @@ -143,23 +143,29 @@ void DivPlatformArcade::acquire_nuked(short* bufL, short* bufR, size_t start, si static int o[2]; for (size_t h=start; hdata[oscBuf[i]->needle++]=fm.ch_out[i]; } - - OPM_Clock(&fm,NULL,NULL,NULL,NULL); - OPM_Clock(&fm,NULL,NULL,NULL,NULL); - OPM_Clock(&fm,NULL,NULL,NULL,NULL); - OPM_Clock(&fm,o,NULL,NULL,NULL); if (o[0]<-32768) o[0]=-32768; if (o[0]>32767) o[0]=32767; @@ -725,6 +731,10 @@ void* DivPlatformArcade::getChanState(int ch) { return &chan[ch]; } +DivDispatchOscBuffer* DivPlatformArcade::getOscBuffer(int ch) { + return oscBuf[ch]; +} + unsigned char* DivPlatformArcade::getRegisterPool() { return regPool; } @@ -792,10 +802,9 @@ void DivPlatformArcade::setFlags(unsigned int flags) { chipClock=COLOR_NTSC; baseFreqOff=0; } - if (useYMFM) { - rate=chipClock/64; - } else { - rate=chipClock/8; + rate=chipClock/64; + for (int i=0; i<8; i++) { + oscBuf[i]->rate=rate; } } @@ -813,6 +822,7 @@ int DivPlatformArcade::init(DivEngine* p, int channels, int sugRate, unsigned in skipRegisterWrites=false; for (int i=0; i<8; i++) { isMuted[i]=false; + oscBuf[i]=new DivDispatchOscBuffer; } setFlags(flags); if (useYMFM) fm_ymfm=new ymfm::ym2151(iface); @@ -822,6 +832,9 @@ int DivPlatformArcade::init(DivEngine* p, int channels, int sugRate, unsigned in } void DivPlatformArcade::quit() { + for (int i=0; i<8; i++) { + delete oscBuf[i]; + } if (useYMFM) { delete fm_ymfm; } diff --git a/src/engine/platform/arcade.h b/src/engine/platform/arcade.h index 68f6baad3..6f68eedad 100644 --- a/src/engine/platform/arcade.h +++ b/src/engine/platform/arcade.h @@ -70,6 +70,7 @@ class DivPlatformArcade: public DivDispatch { chVolR(127) {} }; Channel chan[8]; + DivDispatchOscBuffer* oscBuf[8]; struct QueuedWrite { unsigned short addr; unsigned char val; @@ -108,6 +109,7 @@ class DivPlatformArcade: public DivDispatch { void acquire(short* bufL, short* bufR, size_t start, size_t len); int dispatch(DivCommand c); void* getChanState(int chan); + DivDispatchOscBuffer* getOscBuffer(int chan); unsigned char* getRegisterPool(); int getRegisterPoolSize(); void reset(); diff --git a/src/engine/platform/genesis.cpp b/src/engine/platform/genesis.cpp index b4b926454..27efab759 100644 --- a/src/engine/platform/genesis.cpp +++ b/src/engine/platform/genesis.cpp @@ -144,7 +144,8 @@ void DivPlatformGenesis::acquire_nuked(short* bufL, short* bufR, size_t start, s OPN2_Clock(&fm,o); os[0]+=o[0]; os[1]+=o[1]; //OPN2_Write(&fm,0,0); - } + oscBuf[i]->data[oscBuf[i]->needle++]=fm.ch_out[i]<<7; + } os[0]=(os[0]<<5); if (os[0]<-32768) os[0]=-32768; @@ -844,6 +845,10 @@ void* DivPlatformGenesis::getChanState(int ch) { return &chan[ch]; } +DivDispatchOscBuffer* DivPlatformGenesis::getOscBuffer(int ch) { + return oscBuf[ch]; +} + unsigned char* DivPlatformGenesis::getRegisterPool() { return regPool; } @@ -956,6 +961,9 @@ void DivPlatformGenesis::setFlags(unsigned int flags) { } else { rate=chipClock/36; } + for (int i=0; i<10; i++) { + oscBuf[i]->rate=rate; + } } int DivPlatformGenesis::init(DivEngine* p, int channels, int sugRate, unsigned int flags) { @@ -965,6 +973,7 @@ int DivPlatformGenesis::init(DivEngine* p, int channels, int sugRate, unsigned i skipRegisterWrites=false; for (int i=0; i<10; i++) { isMuted[i]=false; + oscBuf[i]=new DivDispatchOscBuffer; } fm_ymfm=NULL; setFlags(flags); @@ -974,6 +983,9 @@ int DivPlatformGenesis::init(DivEngine* p, int channels, int sugRate, unsigned i } void DivPlatformGenesis::quit() { + for (int i=0; i<10; i++) { + delete oscBuf[i]; + } if (fm_ymfm!=NULL) delete fm_ymfm; } diff --git a/src/engine/platform/genesis.h b/src/engine/platform/genesis.h index 0482ea0a0..236e5c43f 100644 --- a/src/engine/platform/genesis.h +++ b/src/engine/platform/genesis.h @@ -68,6 +68,7 @@ class DivPlatformGenesis: public DivDispatch { pan(3) {} }; Channel chan[10]; + DivDispatchOscBuffer* oscBuf[10]; bool isMuted[10]; struct QueuedWrite { unsigned short addr; @@ -110,6 +111,7 @@ class DivPlatformGenesis: public DivDispatch { void acquire(short* bufL, short* bufR, size_t start, size_t len); int dispatch(DivCommand c); void* getChanState(int chan); + DivDispatchOscBuffer* getOscBuffer(int chan); unsigned char* getRegisterPool(); int getRegisterPoolSize(); void reset(); diff --git a/src/engine/platform/genesisext.cpp b/src/engine/platform/genesisext.cpp index e67c353d4..d5c98a56c 100644 --- a/src/engine/platform/genesisext.cpp +++ b/src/engine/platform/genesisext.cpp @@ -391,6 +391,12 @@ void* DivPlatformGenesisExt::getChanState(int ch) { return &chan[ch]; } +DivDispatchOscBuffer* DivPlatformGenesisExt::getOscBuffer(int ch) { + if (ch>=6) return oscBuf[ch-3]; + if (ch<3) return oscBuf[ch]; + return NULL; +} + void DivPlatformGenesisExt::reset() { DivPlatformGenesis::reset(); diff --git a/src/engine/platform/genesisext.h b/src/engine/platform/genesisext.h index c246ee3f3..b482663c2 100644 --- a/src/engine/platform/genesisext.h +++ b/src/engine/platform/genesisext.h @@ -54,6 +54,7 @@ class DivPlatformGenesisExt: public DivPlatformGenesis { public: int dispatch(DivCommand c); void* getChanState(int chan); + DivDispatchOscBuffer* getOscBuffer(int chan); void reset(); void forceIns(); void tick(bool sysTick=true); diff --git a/src/engine/platform/opl.cpp b/src/engine/platform/opl.cpp index 3e385434e..33c59eeb2 100644 --- a/src/engine/platform/opl.cpp +++ b/src/engine/platform/opl.cpp @@ -52,6 +52,10 @@ const unsigned short chanMapOPL2Drums[20]={ 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 7, N, N, N, N, N, N, N, N, N }; +const unsigned char outChanMapOPL2[18]={ + 0, 1, 2, 3, 4, 5, 6, 7, 8, N, N, N, N, N, N, N, N, N +}; + const unsigned char* slotsOPL2[4]={ slotsOPL2i[0], slotsOPL2i[1], @@ -88,6 +92,10 @@ const unsigned short chanMapOPL3Drums[20]={ 0, 3, 1, 4, 2, 5, 0x100, 0x103, 0x101, 0x104, 0x102, 0x105, 0x106, 0x107, 0x108, 6, 7, 8, 8, 7 }; +const unsigned char outChanMapOPL3[18]={ + 0, 3, 1, 4, 2, 5, 9, 12, 10, 13, 11, 14, 15, 16, 17, 6, 7, 8 +}; + const unsigned char* slotsOPL3[4]={ slotsOPL3i[0], slotsOPL3i[1], @@ -208,6 +216,20 @@ void DivPlatformOPL::acquire_nuked(short* bufL, short* bufR, size_t start, size_ } OPL3_Generate(&fm,o); os[0]+=o[0]; os[1]+=o[1]; + + for (int i=0; idata[oscBuf[i]->needle]=0; + if (fm.channel[i].out[0]!=NULL) { + oscBuf[i]->data[oscBuf[i]->needle]+=*fm.channel[ch].out[0]; + } + if (fm.channel[i].out[1]!=NULL) { + oscBuf[i]->data[oscBuf[i]->needle]+=*fm.channel[ch].out[1]; + } + oscBuf[i]->data[oscBuf[i]->needle]<<=1; + oscBuf[i]->needle++; + } if (os[0]<-32768) os[0]=-32768; if (os[0]>32767) os[0]=32767; @@ -917,6 +939,10 @@ void* DivPlatformOPL::getChanState(int ch) { return &chan[ch]; } +DivDispatchOscBuffer* DivPlatformOPL::getOscBuffer(int ch) { + return oscBuf[ch]; +} + unsigned char* DivPlatformOPL::getRegisterPool() { return regPool; } @@ -1034,6 +1060,7 @@ void DivPlatformOPL::setOPLType(int type, bool drums) { slotsDrums=slotsOPL2Drums; slots=drums?slotsDrums:slotsNonDrums; chanMap=drums?chanMapOPL2Drums:chanMapOPL2; + outChanMap=outChanMapOPL2; chipFreqBase=9440540*0.25; chans=9; melodicChans=drums?6:9; @@ -1044,6 +1071,7 @@ void DivPlatformOPL::setOPLType(int type, bool drums) { slotsDrums=slotsOPL3Drums; slots=drums?slotsDrums:slotsNonDrums; chanMap=drums?chanMapOPL3Drums:chanMapOPL3; + outChanMap=outChanMapOPL3; chipFreqBase=9440540; chans=18; melodicChans=drums?15:18; @@ -1096,6 +1124,10 @@ void DivPlatformOPL::setFlags(unsigned int flags) { rate=48000; chipClock=rate*288; } + + for (int i=0; i<18; i++) { + oscBuf[i]->rate=rate; + } } int DivPlatformOPL::init(DivEngine* p, int channels, int sugRate, unsigned int flags) { @@ -1105,6 +1137,9 @@ int DivPlatformOPL::init(DivEngine* p, int channels, int sugRate, unsigned int f for (int i=0; i<20; i++) { isMuted[i]=false; } + for (int i=0; i<18; i++) { + oscBuf[i]=new DivDispatchOscBuffer; + } setFlags(flags); reset(); @@ -1112,6 +1147,9 @@ int DivPlatformOPL::init(DivEngine* p, int channels, int sugRate, unsigned int f } void DivPlatformOPL::quit() { + for (int i=0; i<18; i++) { + delete oscBuf[i]; + } } DivPlatformOPL::~DivPlatformOPL() { diff --git a/src/engine/platform/opl.h b/src/engine/platform/opl.h index eeef111f0..8f8dad372 100644 --- a/src/engine/platform/opl.h +++ b/src/engine/platform/opl.h @@ -62,6 +62,7 @@ class DivPlatformOPL: public DivDispatch { } }; Channel chan[20]; + DivDispatchOscBuffer* oscBuf[18]; bool isMuted[20]; struct QueuedWrite { unsigned short addr; @@ -75,6 +76,7 @@ class DivPlatformOPL: public DivDispatch { const unsigned char** slotsDrums; const unsigned char** slots; const unsigned short* chanMap; + const unsigned char* outChanMap; double chipFreqBase; int delay, oplType, chans, melodicChans, totalChans; unsigned char lastBusy; @@ -104,6 +106,7 @@ class DivPlatformOPL: public DivDispatch { void acquire(short* bufL, short* bufR, size_t start, size_t len); int dispatch(DivCommand c); void* getChanState(int chan); + DivDispatchOscBuffer* getOscBuffer(int chan); unsigned char* getRegisterPool(); int getRegisterPoolSize(); void reset(); diff --git a/src/engine/platform/opll.cpp b/src/engine/platform/opll.cpp index f02ad40b8..0e4e92d61 100644 --- a/src/engine/platform/opll.cpp +++ b/src/engine/platform/opll.cpp @@ -735,6 +735,7 @@ void* DivPlatformOPLL::getChanState(int ch) { } DivDispatchOscBuffer* DivPlatformOPLL::getOscBuffer(int ch) { + if (ch>=9) return NULL; return oscBuf[ch]; } diff --git a/src/engine/platform/sms.cpp b/src/engine/platform/sms.cpp index d0a19d1db..f1c661634 100644 --- a/src/engine/platform/sms.cpp +++ b/src/engine/platform/sms.cpp @@ -42,7 +42,16 @@ const char* DivPlatformSMS::getEffectName(unsigned char effect) { } void DivPlatformSMS::acquire(short* bufL, short* bufR, size_t start, size_t len) { - sn->sound_stream_update(bufL+start,len); + for (size_t h=start; hsound_stream_update(bufL+h,1); + for (int i=0; i<4; i++) { + if (isMuted[i]) { + oscBuf[i]->data[oscBuf[i]->needle++]=0; + } else { + oscBuf[i]->data[oscBuf[i]->needle++]=sn->get_channel_output(i); + } + } + } } int DivPlatformSMS::acquireOne() { @@ -287,6 +296,10 @@ void* DivPlatformSMS::getChanState(int ch) { return &chan[ch]; } +DivDispatchOscBuffer* DivPlatformSMS::getOscBuffer(int ch) { + return oscBuf[ch]; +} + void DivPlatformSMS::reset() { for (int i=0; i<4; i++) { chan[i]=DivPlatformSMS::Channel(); @@ -359,6 +372,9 @@ void DivPlatformSMS::setFlags(unsigned int flags) { break; } rate=chipClock/16; + for (int i=0; i<4; i++) { + oscBuf[i]->rate=rate; + } } int DivPlatformSMS::init(DivEngine* p, int channels, int sugRate, unsigned int flags) { @@ -369,6 +385,7 @@ int DivPlatformSMS::init(DivEngine* p, int channels, int sugRate, unsigned int f oldValue=0xff; for (int i=0; i<4; i++) { isMuted[i]=false; + oscBuf[i]=new DivDispatchOscBuffer; } sn=NULL; setFlags(flags); @@ -377,6 +394,9 @@ int DivPlatformSMS::init(DivEngine* p, int channels, int sugRate, unsigned int f } void DivPlatformSMS::quit() { + for (int i=0; i<4; i++) { + delete oscBuf[i]; + } if (sn!=NULL) delete sn; } diff --git a/src/engine/platform/sms.h b/src/engine/platform/sms.h index a79945bdd..d1b528818 100644 --- a/src/engine/platform/sms.h +++ b/src/engine/platform/sms.h @@ -52,6 +52,7 @@ class DivPlatformSMS: public DivDispatch { outVol(15) {} }; Channel chan[4]; + DivDispatchOscBuffer* oscBuf[4]; bool isMuted[4]; unsigned char oldValue; unsigned char snNoiseMode; @@ -65,6 +66,7 @@ class DivPlatformSMS: public DivDispatch { void acquire(short* bufL, short* bufR, size_t start, size_t len); int dispatch(DivCommand c); void* getChanState(int chan); + DivDispatchOscBuffer* getOscBuffer(int chan); void reset(); void forceIns(); void tick(bool sysTick=true); diff --git a/src/engine/platform/sound/sn76496.h b/src/engine/platform/sound/sn76496.h index 2387574d1..4c24e938c 100644 --- a/src/engine/platform/sound/sn76496.h +++ b/src/engine/platform/sound/sn76496.h @@ -16,6 +16,9 @@ public: void write(u8 data); void device_start(); void sound_stream_update(short* outputs, int outLen); + inline int32_t get_channel_output(int ch) { + return ((m_output[ch]!=0)?m_volume[ch]:0); + } //DECLARE_READ_LINE_MEMBER( ready_r ) { return m_ready_state ? 1 : 0; } sn76496_base_device( diff --git a/src/gui/chanOsc.cpp b/src/gui/chanOsc.cpp index c4924e7ef..f1248ffe9 100644 --- a/src/gui/chanOsc.cpp +++ b/src/gui/chanOsc.cpp @@ -38,8 +38,8 @@ void FurnaceGUI::drawChanOsc() { ImGui::PushStyleVar(ImGuiStyleVar_CellPadding,ImVec2(0.0f,0.0f)); float availY=ImGui::GetContentRegionAvail().y; if (ImGui::BeginTable("ChanOsc",chanOscCols,ImGuiTableFlags_Borders)) { + std::vector oscBufs; int chans=e->getTotalChannelCount(); - int rows=(chans+(chanOscCols-1))/chanOscCols; ImDrawList* dl=ImGui::GetWindowDrawList(); ImGuiWindow* window=ImGui::GetCurrentWindow(); ImVec2 waveform[512]; @@ -48,12 +48,18 @@ void FurnaceGUI::drawChanOsc() { ImU32 color=ImGui::GetColorU32(uiColors[GUI_COLOR_OSC_WAVE]); for (int i=0; igetOscBuffer(i); + if (buf!=NULL) oscBufs.push_back(buf); + } + int rows=(oscBufs.size()+(chanOscCols-1))/chanOscCols; + + for (size_t i=0; igetOscBuffer(i); + DivDispatchOscBuffer* buf=oscBufs[i]; if (buf==NULL) { - ImGui::Text("Not Available"); + ImGui::Text("Error!"); } else { ImVec2 size=ImGui::GetContentRegionAvail(); size.y=availY/rows; From dadfd7efeeacd498105b72f8b19ac29da068f8a2 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 1 May 2022 00:58:10 -0500 Subject: [PATCH 260/342] but PCE supports samples! --- src/engine/sysDef.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/engine/sysDef.cpp b/src/engine/sysDef.cpp index 1f7920d7c..8f280847c 100644 --- a/src/engine/sysDef.cpp +++ b/src/engine/sysDef.cpp @@ -375,7 +375,8 @@ void DivEngine::registerSystems() { {"Channel 1", "Channel 2", "Channel 3", "Channel 4", "Channel 5", "Channel 6"}, {"CH1", "CH2", "CH3", "CH4", "CH5", "CH6"}, {DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE}, - {DIV_INS_PCE, DIV_INS_PCE, DIV_INS_PCE, DIV_INS_PCE, DIV_INS_PCE, DIV_INS_PCE} + {DIV_INS_PCE, DIV_INS_PCE, DIV_INS_PCE, DIV_INS_PCE, DIV_INS_PCE, DIV_INS_PCE}, + {DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA} ); sysDefs[DIV_SYSTEM_NES]=new DivSysDef( From c0e9b48b5b949cf8acc09ad55c3867638835c7ab Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 1 May 2022 02:40:03 -0500 Subject: [PATCH 261/342] per-channel oscilloscope, part 5 SAA1099 (SAASound and MAME), Lynx, MMC5, N163, PC Engine, PC Speaker, PET, QSound, WonderSwan, VERA, VIC-20, VRC6 and X1-010! --- extern/SAASound/src/SAADevice.cpp | 8 ++++- extern/SAASound/src/SAADevice.h | 2 +- extern/SAASound/src/SAAImpl.cpp | 4 +-- extern/SAASound/src/SAAImpl.h | 2 +- extern/SAASound/src/SAASndC.cpp | 2 +- extern/SAASound/src/SAASound.h | 4 ++- extern/SAASound/src/types.h | 2 ++ src/engine/platform/lynx.cpp | 14 ++++++++- src/engine/platform/lynx.h | 2 ++ src/engine/platform/mmc5.cpp | 31 +++++++++++++++---- src/engine/platform/mmc5.h | 3 ++ src/engine/platform/n163.cpp | 15 ++++++++++ src/engine/platform/n163.h | 2 ++ src/engine/platform/pce.cpp | 15 ++++++++++ src/engine/platform/pce.h | 2 ++ src/engine/platform/pcspkr.cpp | 33 ++++++++++++++++++++- src/engine/platform/pcspkr.h | 2 ++ src/engine/platform/pet.cpp | 12 ++++++++ src/engine/platform/pet.h | 3 ++ src/engine/platform/qsound.cpp | 25 ++++++++++++++-- src/engine/platform/qsound.h | 2 ++ src/engine/platform/saa.cpp | 16 ++++++++-- src/engine/platform/saa.h | 2 ++ src/engine/platform/sound/lynx/Mikey.cpp | 14 +++++++-- src/engine/platform/sound/lynx/Mikey.hpp | 5 +++- src/engine/platform/sound/n163/n163.cpp | 7 ++++- src/engine/platform/sound/n163/n163.hpp | 7 +++++ src/engine/platform/sound/pce_psg.cpp | 3 +- src/engine/platform/sound/pce_psg.h | 4 +-- src/engine/platform/sound/saa1099.cpp | 13 +++++--- src/engine/platform/sound/saa1099.h | 4 ++- src/engine/platform/sound/swan.h | 4 +-- src/engine/platform/sound/vera_psg.c | 6 ++++ src/engine/platform/sound/vera_psg.h | 1 + src/engine/platform/sound/vrcvi/vrcvi.cpp | 15 ++++++++-- src/engine/platform/sound/vrcvi/vrcvi.hpp | 3 ++ src/engine/platform/sound/x1_010/x1_010.hpp | 1 + src/engine/platform/swan.cpp | 12 ++++++++ src/engine/platform/swan.h | 2 ++ src/engine/platform/vera.cpp | 21 ++++++++++++- src/engine/platform/vera.h | 2 ++ src/engine/platform/vic20.cpp | 14 +++++++++ src/engine/platform/vic20.h | 2 ++ src/engine/platform/vrc6.cpp | 20 +++++++++++++ src/engine/platform/vrc6.h | 3 ++ src/engine/platform/x1_010.cpp | 15 ++++++++++ src/engine/platform/x1_010.h | 2 ++ 47 files changed, 345 insertions(+), 38 deletions(-) diff --git a/extern/SAASound/src/SAADevice.cpp b/extern/SAASound/src/SAADevice.cpp index 5cfb17b22..9064dcab9 100644 --- a/extern/SAASound/src/SAADevice.cpp +++ b/extern/SAASound/src/SAADevice.cpp @@ -307,7 +307,7 @@ BYTE CSAADevice::_ReadData(void) } #endif -void CSAADevice::_TickAndOutputStereo(unsigned int& left_mixed, unsigned int& right_mixed) +void CSAADevice::_TickAndOutputStereo(unsigned int& left_mixed, unsigned int& right_mixed, DivDispatchOscBuffer** oscBuf) { unsigned int temp_left, temp_right; unsigned int accum_left = 0, accum_right = 0; @@ -316,21 +316,27 @@ void CSAADevice::_TickAndOutputStereo(unsigned int& left_mixed, unsigned int& ri m_Noise0.Tick(); m_Noise1.Tick(); m_Amp0.TickAndOutputStereo(temp_left, temp_right); + oscBuf[0]->data[oscBuf[0]->needle++]=(temp_left+temp_right)<<4; accum_left += temp_left; accum_right += temp_right; m_Amp1.TickAndOutputStereo(temp_left, temp_right); + oscBuf[1]->data[oscBuf[1]->needle++]=(temp_left+temp_right)<<4; accum_left += temp_left; accum_right += temp_right; m_Amp2.TickAndOutputStereo(temp_left, temp_right); + oscBuf[2]->data[oscBuf[2]->needle++]=(temp_left+temp_right)<<4; accum_left += temp_left; accum_right += temp_right; m_Amp3.TickAndOutputStereo(temp_left, temp_right); + oscBuf[3]->data[oscBuf[3]->needle++]=(temp_left+temp_right)<<4; accum_left += temp_left; accum_right += temp_right; m_Amp4.TickAndOutputStereo(temp_left, temp_right); + oscBuf[4]->data[oscBuf[4]->needle++]=(temp_left+temp_right)<<4; accum_left += temp_left; accum_right += temp_right; m_Amp5.TickAndOutputStereo(temp_left, temp_right); + oscBuf[5]->data[oscBuf[5]->needle++]=(temp_left+temp_right)<<4; accum_left += temp_left; accum_right += temp_right; } diff --git a/extern/SAASound/src/SAADevice.h b/extern/SAASound/src/SAADevice.h index 639d90620..fe4339305 100644 --- a/extern/SAASound/src/SAADevice.h +++ b/extern/SAASound/src/SAADevice.h @@ -53,7 +53,7 @@ public: void _SetClockRate(unsigned int nClockRate); void _SetSampleRate(unsigned int nSampleRate); void _SetOversample(unsigned int nOversample); - void _TickAndOutputStereo(unsigned int& left_mixed, unsigned int& right_mixed); + void _TickAndOutputStereo(unsigned int& left_mixed, unsigned int& right_mixed, DivDispatchOscBuffer** oscBuf); void _TickAndOutputSeparate(unsigned int& left_mixed, unsigned int& right_mixed, unsigned int& left0, unsigned int& right0, unsigned int& left1, unsigned int& right1, diff --git a/extern/SAASound/src/SAAImpl.cpp b/extern/SAASound/src/SAAImpl.cpp index 455a6a18b..6602feee9 100644 --- a/extern/SAASound/src/SAAImpl.cpp +++ b/extern/SAASound/src/SAAImpl.cpp @@ -298,7 +298,7 @@ void scale_for_output(unsigned int left_input, unsigned int right_input, *pBuffer++ = (right_output >> 8) & 0x00ff; } -void CSAASoundInternal::GenerateMany(BYTE* pBuffer, unsigned long nSamples) +void CSAASoundInternal::GenerateMany(BYTE* pBuffer, unsigned long nSamples, DivDispatchOscBuffer** oscBuf) { unsigned int left_mixed, right_mixed; static double filterout_z1_left_mixed = 0, filterout_z1_right_mixed = 0; @@ -376,7 +376,7 @@ void CSAASoundInternal::GenerateMany(BYTE* pBuffer, unsigned long nSamples) #endif while (nSamples--) { - m_chip._TickAndOutputStereo(left_mixed, right_mixed); + m_chip._TickAndOutputStereo(left_mixed, right_mixed, oscBuf); scale_for_output(left_mixed, right_mixed, oversample, m_bHighpass, nBoost, filterout_z1_left_mixed, filterout_z1_right_mixed, pBuffer); } diff --git a/extern/SAASound/src/SAAImpl.h b/extern/SAASound/src/SAAImpl.h index 61fa79c58..f5f58d556 100755 --- a/extern/SAASound/src/SAAImpl.h +++ b/extern/SAASound/src/SAAImpl.h @@ -68,7 +68,7 @@ public: unsigned short GetCurrentBytesPerSample(void); static unsigned short GetBytesPerSample(SAAPARAM uParam); - void GenerateMany(BYTE * pBuffer, unsigned long nSamples); + void GenerateMany(BYTE * pBuffer, unsigned long nSamples, DivDispatchOscBuffer** oscBuf); }; diff --git a/extern/SAASound/src/SAASndC.cpp b/extern/SAASound/src/SAASndC.cpp index f03e82114..747c70a73 100755 --- a/extern/SAASound/src/SAASndC.cpp +++ b/extern/SAASound/src/SAASndC.cpp @@ -81,7 +81,7 @@ unsigned long SAAAPI SAASNDGetSampleRate(SAAPARAM uParam) void SAAAPI SAASNDGenerateMany(SAASND object, BYTE * pBuffer, unsigned long nSamples) { - ((LPCSAASOUND)(object))->GenerateMany(pBuffer, nSamples); + ((LPCSAASOUND)(object))->GenerateMany(pBuffer, nSamples, NULL); } void SAAAPI SAASNDSetSampleRate(SAASND object, unsigned int nSampleRate) diff --git a/extern/SAASound/src/SAASound.h b/extern/SAASound/src/SAASound.h index c80831484..7496cc360 100644 --- a/extern/SAASound/src/SAASound.h +++ b/extern/SAASound/src/SAASound.h @@ -61,6 +61,8 @@ typedef unsigned long SAAPARAM; #ifdef __cplusplus +#include "../../src/engine/dispatch.h" + class CSAASound { public: @@ -79,7 +81,7 @@ public: virtual unsigned short GetCurrentBytesPerSample () = 0; static unsigned short GetBytesPerSample (SAAPARAM uParam); - virtual void GenerateMany (BYTE * pBuffer, unsigned long nSamples) = 0; + virtual void GenerateMany (BYTE * pBuffer, unsigned long nSamples, DivDispatchOscBuffer** oscBuf) = 0; virtual void SetClockRate(unsigned int nClockRate) = 0; virtual void SetSampleRate(unsigned int nSampleRate) = 0; diff --git a/extern/SAASound/src/types.h b/extern/SAASound/src/types.h index 4eb62f485..8cce21247 100755 --- a/extern/SAASound/src/types.h +++ b/extern/SAASound/src/types.h @@ -12,8 +12,10 @@ defined(__arm__) || \ (defined(__mips__) && defined(__MIPSEL__)) #else +#ifndef __BIG_ENDIAN #define __BIG_ENDIAN #endif +#endif #ifndef NULL diff --git a/src/engine/platform/lynx.cpp b/src/engine/platform/lynx.cpp index 18a9b72fa..095710612 100644 --- a/src/engine/platform/lynx.cpp +++ b/src/engine/platform/lynx.cpp @@ -142,7 +142,7 @@ const char* DivPlatformLynx::getEffectName(unsigned char effect) { } void DivPlatformLynx::acquire(short* bufL, short* bufR, size_t start, size_t len) { - mikey->sampleAudio( bufL + start, bufR + start, len ); + mikey->sampleAudio( bufL + start, bufR + start, len, oscBuf ); } void DivPlatformLynx::tick(bool sysTick) { @@ -342,6 +342,10 @@ void* DivPlatformLynx::getChanState(int ch) { return &chan[ch]; } +DivDispatchOscBuffer* DivPlatformLynx::getOscBuffer(int ch) { + return oscBuf[ch]; +} + unsigned char* DivPlatformLynx::getRegisterPool() { return const_cast( mikey->getRegisterPool() ); @@ -398,16 +402,24 @@ int DivPlatformLynx::init(DivEngine* p, int channels, int sugRate, unsigned int for (int i=0; i<4; i++) { isMuted[i]=false; + oscBuf[i]=new DivDispatchOscBuffer; } chipClock = 16000000; rate = chipClock/128; + for (int i=0; i<4; i++) { + oscBuf[i]->rate=rate; + } + reset(); return 4; } void DivPlatformLynx::quit() { + for (int i=0; i<4; i++) { + delete oscBuf[i]; + } mikey.reset(); } diff --git a/src/engine/platform/lynx.h b/src/engine/platform/lynx.h index 5200fbd9c..fab8b0f82 100644 --- a/src/engine/platform/lynx.h +++ b/src/engine/platform/lynx.h @@ -74,6 +74,7 @@ class DivPlatformLynx: public DivDispatch { outVol(127) {} }; Channel chan[4]; + DivDispatchOscBuffer* oscBuf[4]; bool isMuted[4]; std::unique_ptr mikey; friend void putDispatchChan(void*,int,int); @@ -81,6 +82,7 @@ class DivPlatformLynx: public DivDispatch { void acquire(short* bufL, short* bufR, size_t start, size_t len); int dispatch(DivCommand c); void* getChanState(int chan); + DivDispatchOscBuffer* getOscBuffer(int chan); unsigned char* getRegisterPool(); int getRegisterPoolSize(); void reset(); diff --git a/src/engine/platform/mmc5.cpp b/src/engine/platform/mmc5.cpp index e2aa915ed..66bead9da 100644 --- a/src/engine/platform/mmc5.cpp +++ b/src/engine/platform/mmc5.cpp @@ -59,7 +59,7 @@ void DivPlatformMMC5::acquire(short* bufL, short* bufR, size_t start, size_t len if (dacPeriod>=rate) { DivSample* s=parent->getSample(dacSample); if (s->samples>0) { - if (!isMuted[4]) { + if (!isMuted[2]) { rWrite(0x5011,((unsigned char)s->data8[dacPos]+0x80)); } if (++dacPos>=s->samples) { @@ -92,6 +92,13 @@ void DivPlatformMMC5::acquire(short* bufL, short* bufR, size_t start, size_t len if (sample>32767) sample=32767; if (sample<-32768) sample=-32768; bufL[i]=sample; + + if (++writeOscBuf>=32) { + writeOscBuf=0; + oscBuf[0]->data[oscBuf[0]->needle++]=isMuted[0]?0:((mmc5->S3.output*10)<<7); + oscBuf[1]->data[oscBuf[1]->needle++]=isMuted[1]?0:((mmc5->S4.output*10)<<7); + oscBuf[2]->data[oscBuf[2]->needle++]=isMuted[2]?0:((mmc5->pcm.output*2)<<6); + } } } @@ -164,18 +171,18 @@ void DivPlatformMMC5::tick(bool sysTick) { } // PCM - if (chan[4].freqChanged) { - chan[4].freq=parent->calcFreq(chan[4].baseFreq,chan[4].pitch,false); - if (chan[4].furnaceDac) { + if (chan[2].freqChanged) { + chan[2].freq=parent->calcFreq(chan[2].baseFreq,chan[2].pitch,false); + if (chan[2].furnaceDac) { double off=1.0; if (dacSample>=0 && dacSamplesong.sampleLen) { DivSample* s=parent->getSample(dacSample); off=(double)s->centerRate/8363.0; } - dacRate=MIN(chan[4].freq*off,32000); + dacRate=MIN(chan[2].freq*off,32000); if (dumpWrites) addWrite(0xffff0001,dacRate); } - chan[4].freqChanged=false; + chan[2].freqChanged=false; } } @@ -342,6 +349,10 @@ void* DivPlatformMMC5::getChanState(int ch) { return &chan[ch]; } +DivDispatchOscBuffer* DivPlatformMMC5::getOscBuffer(int ch) { + return oscBuf[ch]; +} + unsigned char* DivPlatformMMC5::getRegisterPool() { return regPool; } @@ -389,6 +400,9 @@ void DivPlatformMMC5::setFlags(unsigned int flags) { rate=COLOR_NTSC/2.0; } chipClock=rate; + for (int i=0; i<3; i++) { + oscBuf[i]->rate=rate/32; + } } void DivPlatformMMC5::notifyInsDeletion(void* ins) { @@ -410,9 +424,11 @@ int DivPlatformMMC5::init(DivEngine* p, int channels, int sugRate, unsigned int apuType=flags; dumpWrites=false; skipRegisterWrites=false; + writeOscBuf=0; mmc5=new struct _mmc5; for (int i=0; i<3; i++) { isMuted[i]=false; + oscBuf[i]=new DivDispatchOscBuffer; //mmc5->muted[i]=false; // TODO } setFlags(flags); @@ -423,6 +439,9 @@ int DivPlatformMMC5::init(DivEngine* p, int channels, int sugRate, unsigned int } void DivPlatformMMC5::quit() { + for (int i=0; i<3; i++) { + delete oscBuf[i]; + } delete mmc5; } diff --git a/src/engine/platform/mmc5.h b/src/engine/platform/mmc5.h index bf7eeaad5..2213b995f 100644 --- a/src/engine/platform/mmc5.h +++ b/src/engine/platform/mmc5.h @@ -57,12 +57,14 @@ class DivPlatformMMC5: public DivDispatch { wave(-1) {} }; Channel chan[5]; + DivDispatchOscBuffer* oscBuf[3]; bool isMuted[5]; int dacPeriod, dacRate; unsigned int dacPos; int dacSample; unsigned char sampleBank; unsigned char apuType; + unsigned char writeOscBuf; struct _mmc5* mmc5; unsigned char regPool[128]; @@ -72,6 +74,7 @@ class DivPlatformMMC5: public DivDispatch { void acquire(short* bufL, short* bufR, size_t start, size_t len); int dispatch(DivCommand c); void* getChanState(int chan); + DivDispatchOscBuffer* getOscBuffer(int chan); unsigned char* getRegisterPool(); int getRegisterPoolSize(); void reset(); diff --git a/src/engine/platform/n163.cpp b/src/engine/platform/n163.cpp index 5d3c9c201..ef368203b 100644 --- a/src/engine/platform/n163.cpp +++ b/src/engine/platform/n163.cpp @@ -161,6 +161,10 @@ void DivPlatformN163::acquire(short* bufL, short* bufR, size_t start, size_t len if (out<-32768) out=-32768; bufL[i]=bufR[i]=out; + if (n163.voice_cycle()==0x78) for (int i=0; i<8; i++) { + oscBuf[i]->data[oscBuf[i]->needle++]=n163.chan_out(i)<<7; + } + // command queue while (!writes.empty()) { QueuedWrite w=writes.front(); @@ -619,6 +623,10 @@ void* DivPlatformN163::getChanState(int ch) { return &chan[ch]; } +DivDispatchOscBuffer* DivPlatformN163::getOscBuffer(int ch) { + return oscBuf[ch]; +} + unsigned char* DivPlatformN163::getRegisterPool() { for (int i=0; i<128; i++) { regPool[i]=n163.reg(i); @@ -678,6 +686,9 @@ void DivPlatformN163::setFlags(unsigned int flags) { rate/=15; n163.set_multiplex(multiplex); rWrite(0x7f,initChanMax<<4); + for (int i=0; i<8; i++) { + oscBuf[i]->rate=rate/(initChanMax+1); + } } int DivPlatformN163::init(DivEngine* p, int channels, int sugRate, unsigned int flags) { @@ -686,6 +697,7 @@ int DivPlatformN163::init(DivEngine* p, int channels, int sugRate, unsigned int skipRegisterWrites=false; for (int i=0; i<8; i++) { isMuted[i]=false; + oscBuf[i]=new DivDispatchOscBuffer; } setFlags(flags); @@ -695,6 +707,9 @@ int DivPlatformN163::init(DivEngine* p, int channels, int sugRate, unsigned int } void DivPlatformN163::quit() { + for (int i=0; i<8; i++) { + delete oscBuf[i]; + } } DivPlatformN163::~DivPlatformN163() { diff --git a/src/engine/platform/n163.h b/src/engine/platform/n163.h index d01030848..6ecb1e0c5 100644 --- a/src/engine/platform/n163.h +++ b/src/engine/platform/n163.h @@ -70,6 +70,7 @@ class DivPlatformN163: public DivDispatch { resVol(15) {} }; Channel chan[8]; + DivDispatchOscBuffer* oscBuf[8]; bool isMuted[8]; struct QueuedWrite { unsigned char addr; @@ -94,6 +95,7 @@ class DivPlatformN163: public DivDispatch { void acquire(short* bufL, short* bufR, size_t start, size_t len); int dispatch(DivCommand c); void* getChanState(int chan); + DivDispatchOscBuffer* getOscBuffer(int chan); unsigned char* getRegisterPool(); int getRegisterPoolSize(); void reset(); diff --git a/src/engine/platform/pce.cpp b/src/engine/platform/pce.cpp index a56732bef..c28b81db7 100644 --- a/src/engine/platform/pce.cpp +++ b/src/engine/platform/pce.cpp @@ -116,6 +116,10 @@ void DivPlatformPCE::acquire(short* bufL, short* bufR, size_t start, size_t len) pce->Update(24); pce->ResetTS(0); + for (int i=0; i<6; i++) { + oscBuf[i]->data[oscBuf[i]->needle++]=(pce->channel[i].blip_prev_samp[0]+pce->channel[i].blip_prev_samp[1])<<1; + } + tempL[0]=(tempL[0]>>1)+(tempL[0]>>2); tempR[0]=(tempR[0]>>1)+(tempR[0]>>2); @@ -469,6 +473,10 @@ void* DivPlatformPCE::getChanState(int ch) { return &chan[ch]; } +DivDispatchOscBuffer* DivPlatformPCE::getOscBuffer(int ch) { + return oscBuf[ch]; +} + unsigned char* DivPlatformPCE::getRegisterPool() { return regPool; } @@ -541,6 +549,9 @@ void DivPlatformPCE::setFlags(unsigned int flags) { chipClock=COLOR_NTSC; } rate=chipClock/12; + for (int i=0; i<6; i++) { + oscBuf[i]->rate=rate; + } } void DivPlatformPCE::poke(unsigned int addr, unsigned short val) { @@ -557,6 +568,7 @@ int DivPlatformPCE::init(DivEngine* p, int channels, int sugRate, unsigned int f skipRegisterWrites=false; for (int i=0; i<6; i++) { isMuted[i]=false; + oscBuf[i]=new DivDispatchOscBuffer; } setFlags(flags); pce=new PCE_PSG(tempL,tempR,PCE_PSG::REVISION_HUC6280A); @@ -565,6 +577,9 @@ int DivPlatformPCE::init(DivEngine* p, int channels, int sugRate, unsigned int f } void DivPlatformPCE::quit() { + for (int i=0; i<6; i++) { + delete oscBuf[i]; + } delete pce; } diff --git a/src/engine/platform/pce.h b/src/engine/platform/pce.h index b1b140383..58fa6600a 100644 --- a/src/engine/platform/pce.h +++ b/src/engine/platform/pce.h @@ -67,6 +67,7 @@ class DivPlatformPCE: public DivDispatch { wave(-1) {} }; Channel chan[6]; + DivDispatchOscBuffer* oscBuf[6]; bool isMuted[6]; struct QueuedWrite { unsigned char addr; @@ -88,6 +89,7 @@ class DivPlatformPCE: public DivDispatch { void acquire(short* bufL, short* bufR, size_t start, size_t len); int dispatch(DivCommand c); void* getChanState(int chan); + DivDispatchOscBuffer* getOscBuffer(int chan); unsigned char* getRegisterPool(); int getRegisterPoolSize(); void reset(); diff --git a/src/engine/platform/pcspkr.cpp b/src/engine/platform/pcspkr.cpp index 5da1923cf..434ab10b4 100644 --- a/src/engine/platform/pcspkr.cpp +++ b/src/engine/platform/pcspkr.cpp @@ -50,6 +50,7 @@ const float cut=0.05; const float reso=0.06; void DivPlatformPCSpeaker::acquire_unfilt(short* bufL, short* bufR, size_t start, size_t len) { + int out=0; for (size_t i=start; i(freq>>1) && !isMuted[0])?32767:0; + out=(pos>(freq>>1) && !isMuted[0])?32767:0; + bufL[i]=out; + oscBuf->data[oscBuf->needle++]=out; } else { bufL[i]=0; + oscBuf->data[oscBuf->needle++]=0; } } } @@ -87,8 +91,10 @@ void DivPlatformPCSpeaker::acquire_cone(short* bufL, short* bufR, size_t start, if (out>1.0) out=1.0; if (out<-1.0) out=-1.0; bufL[i]=out*32767; + oscBuf->data[oscBuf->needle++]=out*32767; } else { bufL[i]=0; + oscBuf->data[oscBuf->needle++]=0; } } } @@ -112,8 +118,10 @@ void DivPlatformPCSpeaker::acquire_piezo(short* bufL, short* bufR, size_t start, if (out>1.0) out=1.0; if (out<-1.0) out=-1.0; bufL[i]=out*32767; + oscBuf->data[oscBuf->needle++]=out*32767; } else { bufL[i]=0; + oscBuf->data[oscBuf->needle++]=0; } } } @@ -140,12 +148,28 @@ void DivPlatformPCSpeaker::beepFreq(int freq) { } void DivPlatformPCSpeaker::acquire_real(short* bufL, short* bufR, size_t start, size_t len) { + int out=0; if (lastOn!=on || lastFreq!=freq) { lastOn=on; lastFreq=freq; beepFreq((on && !isMuted[0])?freq:0); } for (size_t i=start; ifreq) pos=freq; + while (pos<0) { + if (freq<1) { + pos=1; + } else { + pos+=freq; + } + } + out=(pos>(freq>>1) && !isMuted[0])?32767:0; + oscBuf->data[oscBuf->needle++]=out; + } else { + oscBuf->data[oscBuf->needle++]=0; + } bufL[i]=0; } } @@ -321,6 +345,10 @@ void* DivPlatformPCSpeaker::getChanState(int ch) { return &chan[ch]; } +DivDispatchOscBuffer* DivPlatformPCSpeaker::getOscBuffer(int ch) { + return oscBuf; +} + unsigned char* DivPlatformPCSpeaker::getRegisterPool() { if (on) { regPool[0]=freq; @@ -379,6 +407,7 @@ void DivPlatformPCSpeaker::setFlags(unsigned int flags) { chipClock=COLOR_NTSC/3.0; rate=chipClock/PCSPKR_DIVIDER; speakerType=flags&3; + oscBuf->rate=rate; } void DivPlatformPCSpeaker::notifyInsDeletion(void* ins) { @@ -407,6 +436,7 @@ int DivPlatformPCSpeaker::init(DivEngine* p, int channels, int sugRate, unsigned for (int i=0; i<1; i++) { isMuted[i]=false; } + oscBuf=new DivDispatchOscBuffer; setFlags(flags); reset(); @@ -420,6 +450,7 @@ void DivPlatformPCSpeaker::quit() { #ifdef __linux__ if (beepFD>=0) close(beepFD); #endif + delete oscBuf; } DivPlatformPCSpeaker::~DivPlatformPCSpeaker() { diff --git a/src/engine/platform/pcspkr.h b/src/engine/platform/pcspkr.h index d2c01454e..155416bb8 100644 --- a/src/engine/platform/pcspkr.h +++ b/src/engine/platform/pcspkr.h @@ -56,6 +56,7 @@ class DivPlatformPCSpeaker: public DivDispatch { wave(-1) {} }; Channel chan[1]; + DivDispatchOscBuffer* oscBuf; bool isMuted[1]; bool on, flip, lastOn; int pos, speakerType, beepFD; @@ -78,6 +79,7 @@ class DivPlatformPCSpeaker: public DivDispatch { void acquire(short* bufL, short* bufR, size_t start, size_t len); int dispatch(DivCommand c); void* getChanState(int chan); + DivDispatchOscBuffer* getOscBuffer(int chan); unsigned char* getRegisterPool(); int getRegisterPoolSize(); void reset(); diff --git a/src/engine/platform/pet.cpp b/src/engine/platform/pet.cpp index 5a7efe1f9..8ddee9e86 100644 --- a/src/engine/platform/pet.cpp +++ b/src/engine/platform/pet.cpp @@ -64,12 +64,14 @@ void DivPlatformPET::acquire(short* bufL, short* bufR, size_t start, size_t len) } bufL[h]=chan.out; bufR[h]=chan.out; + oscBuf->data[oscBuf->needle++]=chan.out; } } else { chan.out=0; for (size_t h=start; hdata[oscBuf->needle++]=0; } } } @@ -244,6 +246,10 @@ void* DivPlatformPET::getChanState(int ch) { return &chan; } +DivDispatchOscBuffer* DivPlatformPET::getOscBuffer(int ch) { + return oscBuf; +} + unsigned char* DivPlatformPET::getRegisterPool() { return regPool; } @@ -281,9 +287,15 @@ int DivPlatformPET::init(DivEngine* p, int channels, int sugRate, unsigned int f chipClock=1000000; rate=chipClock/SAMP_DIVIDER; // = 250000kHz isMuted=false; + oscBuf=new DivDispatchOscBuffer; + oscBuf->rate=rate; reset(); return 1; } +void DivPlatformPET::quit() { + delete oscBuf; +} + DivPlatformPET::~DivPlatformPET() { } diff --git a/src/engine/platform/pet.h b/src/engine/platform/pet.h index c26ef5392..1e5e49ce5 100644 --- a/src/engine/platform/pet.h +++ b/src/engine/platform/pet.h @@ -57,6 +57,7 @@ class DivPlatformPET: public DivDispatch { out(0) {} }; Channel chan; + DivDispatchOscBuffer* oscBuf; bool isMuted; unsigned char regPool[16]; @@ -65,6 +66,7 @@ class DivPlatformPET: public DivDispatch { void acquire(short* bufL, short* bufR, size_t start, size_t len); int dispatch(DivCommand c); void* getChanState(int chan); + DivDispatchOscBuffer* getOscBuffer(int chan); unsigned char* getRegisterPool(); int getRegisterPoolSize(); void reset(); @@ -78,6 +80,7 @@ class DivPlatformPET: public DivDispatch { const char** getRegisterSheet(); const char* getEffectName(unsigned char effect); int init(DivEngine* parent, int channels, int sugRate, unsigned int flags); + void quit(); ~DivPlatformPET(); private: void writeOutVol(); diff --git a/src/engine/platform/qsound.cpp b/src/engine/platform/qsound.cpp index f3197f6b4..c5d003be5 100644 --- a/src/engine/platform/qsound.cpp +++ b/src/engine/platform/qsound.cpp @@ -274,6 +274,13 @@ void DivPlatformQSound::acquire(short* bufL, short* bufR, size_t start, size_t l qsound_update(&chip); bufL[h]=chip.out[0]; bufR[h]=chip.out[1]; + + for (int i=0; i<19; i++) { + int data=chip.voice_output[i]<<2; + if (data<-32768) data=-32768; + if (data>32767) data=32767; + oscBuf[i]->data[oscBuf[i]->needle++]=data; + } } } @@ -546,6 +553,10 @@ void* DivPlatformQSound::getChanState(int ch) { return &chan[ch]; } +DivDispatchOscBuffer* DivPlatformQSound::getOscBuffer(int ch) { + return oscBuf[ch]; +} + void DivPlatformQSound::reset() { for (int i=0; i<16; i++) { chan[i]=DivPlatformQSound::Channel(); @@ -632,9 +643,10 @@ int DivPlatformQSound::init(DivEngine* p, int channels, int sugRate, unsigned in dumpWrites=false; skipRegisterWrites=false; - //for (int i=0; i<16; i++) { - // isMuted[i]=false; - //} + for (int i=0; i<19; i++) { + oscBuf[i]=new DivDispatchOscBuffer; + //isMuted[i]=false; + } setFlags(flags); chipClock=60000000; @@ -642,8 +654,15 @@ int DivPlatformQSound::init(DivEngine* p, int channels, int sugRate, unsigned in chip.rom_data = (unsigned char*)&chip.rom_mask; chip.rom_mask = 0; reset(); + + for (int i=0; i<19; i++) { + oscBuf[i]->rate=rate; + } return 19; } void DivPlatformQSound::quit() { + for (int i=0; i<19; i++) { + delete oscBuf[i]; + } } diff --git a/src/engine/platform/qsound.h b/src/engine/platform/qsound.h index 6eb178d08..0fbce3b32 100644 --- a/src/engine/platform/qsound.h +++ b/src/engine/platform/qsound.h @@ -63,6 +63,7 @@ class DivPlatformQSound: public DivDispatch { outVol(255) {} }; Channel chan[19]; + DivDispatchOscBuffer* oscBuf[19]; int echoDelay; int echoFeedback; @@ -75,6 +76,7 @@ class DivPlatformQSound: public DivDispatch { void acquire(short* bufL, short* bufR, size_t start, size_t len); int dispatch(DivCommand c); void* getChanState(int chan); + DivDispatchOscBuffer* getOscBuffer(int chan); unsigned char* getRegisterPool(); int getRegisterPoolSize(); int getRegisterPoolDepth(); diff --git a/src/engine/platform/saa.cpp b/src/engine/platform/saa.cpp index a083355a6..3ecf0a3ed 100644 --- a/src/engine/platform/saa.cpp +++ b/src/engine/platform/saa.cpp @@ -86,7 +86,7 @@ void DivPlatformSAA1099::acquire_mame(short* bufL, short* bufR, size_t start, si regPool[w.addr&0x1f]=w.val; writes.pop(); } - saa.sound_stream_update(saaBuf,len); + saa.sound_stream_update(saaBuf,len,oscBuf); for (size_t i=0; iGenerateMany((unsigned char*)saaBuf[0],len); + saa_saaSound->GenerateMany((unsigned char*)saaBuf[0],len,oscBuf); for (size_t i=0; irate=rate; + } + switch (core) { case DIV_SAA_CORE_MAME: break; @@ -516,6 +524,7 @@ int DivPlatformSAA1099::init(DivEngine* p, int channels, int sugRate, unsigned i saa_saaSound=NULL; for (int i=0; i<6; i++) { isMuted[i]=false; + oscBuf[i]=new DivDispatchOscBuffer; } if (core==DIV_SAA_CORE_SAASOUND) { saa_saaSound=CreateCSAASound(); @@ -530,6 +539,9 @@ int DivPlatformSAA1099::init(DivEngine* p, int channels, int sugRate, unsigned i } void DivPlatformSAA1099::quit() { + for (int i=0; i<6; i++) { + delete oscBuf[i]; + } if (saa_saaSound!=NULL) { DestroyCSAASound(saa_saaSound); saa_saaSound=NULL; diff --git a/src/engine/platform/saa.h b/src/engine/platform/saa.h index 24108da79..70edaa249 100644 --- a/src/engine/platform/saa.h +++ b/src/engine/platform/saa.h @@ -49,6 +49,7 @@ class DivPlatformSAA1099: public DivDispatch { Channel(): freqH(0), freqL(0), freq(0), baseFreq(0), pitch(0), pitch2(0), note(0), ins(-1), psgMode(1), active(false), insChanged(true), freqChanged(false), keyOn(false), keyOff(false), portaPause(false), inPorta(false), vol(0), outVol(15), pan(255) {} }; Channel chan[6]; + DivDispatchOscBuffer* oscBuf[6]; bool isMuted[6]; struct QueuedWrite { unsigned short addr; @@ -90,6 +91,7 @@ class DivPlatformSAA1099: public DivDispatch { void acquire(short* bufL, short* bufR, size_t start, size_t len); int dispatch(DivCommand c); void* getChanState(int chan); + DivDispatchOscBuffer* getOscBuffer(int chan); unsigned char* getRegisterPool(); int getRegisterPoolSize(); void reset(); diff --git a/src/engine/platform/sound/lynx/Mikey.cpp b/src/engine/platform/sound/lynx/Mikey.cpp index 5d12bbb86..270e21bdf 100644 --- a/src/engine/platform/sound/lynx/Mikey.cpp +++ b/src/engine/platform/sound/lynx/Mikey.cpp @@ -458,23 +458,31 @@ public: return mAudioChannels[timer].fireAction( tick ); } - AudioSample sampleAudio() const + AudioSample sampleAudio( DivDispatchOscBuffer** oscb ) const { int left{}; int right{}; for ( size_t i = 0; i < 4; ++i ) { + int oscbWrite = 0; + if ( ( mStereo & ( (uint8_t)0x01 << i ) ) == 0 ) { const int attenuation = ( mPan & ( (uint8_t)0x01 << i ) ) != 0 ? mAttenuationLeft[i] : 0x3c; left += mAudioChannels[i].getOutput() * attenuation; + oscbWrite += mAudioChannels[i].getOutput() * attenuation; } if ( ( mStereo & ( (uint8_t)0x10 << i ) ) == 0 ) { const int attenuation = ( mPan & ( (uint8_t)0x01 << i ) ) != 0 ? mAttenuationRight[i] : 0x3c; right += mAudioChannels[i].getOutput() * attenuation; + oscbWrite += mAudioChannels[i].getOutput() * attenuation; + } + + if (oscb!=NULL) { + oscb[i]->data[oscb[i]->needle++]=oscbWrite; } } @@ -534,7 +542,7 @@ void Mikey::enqueueSampling() mQueue->push( ( mNextTick & ~15 ) | 4 ); } -void Mikey::sampleAudio( int16_t* bufL, int16_t* bufR, size_t size ) +void Mikey::sampleAudio( int16_t* bufL, int16_t* bufR, size_t size, DivDispatchOscBuffer** oscb ) { size_t i = 0; while ( i < size ) @@ -549,7 +557,7 @@ void Mikey::sampleAudio( int16_t* bufL, int16_t* bufR, size_t size ) } else { - auto sample = mMikey->sampleAudio(); + auto sample = mMikey->sampleAudio( oscb ); bufL[i] = sample.left; bufR[i] = sample.right; i += 1; diff --git a/src/engine/platform/sound/lynx/Mikey.hpp b/src/engine/platform/sound/lynx/Mikey.hpp index 20b8b0868..9d0f6884e 100644 --- a/src/engine/platform/sound/lynx/Mikey.hpp +++ b/src/engine/platform/sound/lynx/Mikey.hpp @@ -3,6 +3,9 @@ #include #include +// can you forgive me +#include "../../../dispatch.h" + namespace Lynx { @@ -18,7 +21,7 @@ public: ~Mikey(); void write( uint8_t address, uint8_t value ); - void sampleAudio( int16_t* bufL, int16_t* bufR, size_t size ); + void sampleAudio( int16_t* bufL, int16_t* bufR, size_t size, DivDispatchOscBuffer** oscb = NULL ); uint8_t const* getRegisterPool(); diff --git a/src/engine/platform/sound/n163/n163.cpp b/src/engine/platform/sound/n163/n163.cpp index b18f146ba..20853963e 100644 --- a/src/engine/platform/sound/n163/n163.cpp +++ b/src/engine/platform/sound/n163/n163.cpp @@ -98,6 +98,8 @@ void n163_core::tick() m_ram[m_voice_cycle + 3] = bitfield(accum, 8, 8); m_ram[m_voice_cycle + 5] = bitfield(accum, 16, 8); + const u8 prev_voice_cycle = m_voice_cycle; + // update voice cycle bool flush = m_multiplex ? true : false; m_voice_cycle -= 0x8; @@ -109,7 +111,9 @@ void n163_core::tick() } // output 4 bit waveform and volume, multiplexed - m_acc += wave * volume; + const u8 chan_index = ((0x78-prev_voice_cycle)>>3)&7; + m_ch_out[chan_index]=wave * volume; + m_acc += m_ch_out[chan_index]; if (flush) { m_out = m_acc / (m_multiplex ? 1 : (bitfield(m_ram[0x7f], 4, 3) + 1)); @@ -127,6 +131,7 @@ void n163_core::reset() m_addr_latch.reset(); m_out = 0; m_acc = 0; + std::fill(std::begin(m_ch_out), std::end(m_ch_out), 0); } // accessor diff --git a/src/engine/platform/sound/n163/n163.hpp b/src/engine/platform/sound/n163/n163.hpp index a31827572..aa059206b 100644 --- a/src/engine/platform/sound/n163/n163.hpp +++ b/src/engine/platform/sound/n163/n163.hpp @@ -46,6 +46,12 @@ public: // sound output pin s16 out() { return m_out; } + // get channel output + s16 chan_out(u8 ch) { return m_ch_out[ch]; } + + // get voice cycle + u8 voice_cycle() { return m_voice_cycle; } + // register pool u8 reg(u8 addr) { return m_ram[addr & 0x7f]; } void set_multiplex(bool multiplex = true) { m_multiplex = multiplex; } @@ -74,6 +80,7 @@ private: u8 m_voice_cycle = 0x78; // Voice cycle for processing addr_latch_t m_addr_latch; // address latch s16 m_out = 0; // output + s16 m_ch_out[8] = {0}; // per channel output // demultiplex related bool m_multiplex = true; // multiplex flag, but less noisy = inaccurate! s16 m_acc = 0; // accumulated output diff --git a/src/engine/platform/sound/pce_psg.cpp b/src/engine/platform/sound/pce_psg.cpp index f9c1ee099..0fa9993bf 100644 --- a/src/engine/platform/sound/pce_psg.cpp +++ b/src/engine/platform/sound/pce_psg.cpp @@ -96,10 +96,11 @@ inline void PCE_PSG::UpdateOutputSub(const int32_t timestamp, psg_channel *ch, c HRBufs[1][l + 4] += delta[1] * c[4]; HRBufs[1][l + 5] += delta[1] * c[5]; HRBufs[1][l + 6] += delta[1] * c[6]; + */ ch->blip_prev_samp[0] = samp0; ch->blip_prev_samp[1] = samp1; - */ + } void PCE_PSG::UpdateOutput_Norm(const int32_t timestamp, psg_channel *ch) diff --git a/src/engine/platform/sound/pce_psg.h b/src/engine/platform/sound/pce_psg.h index 595494036..08f7f88ce 100644 --- a/src/engine/platform/sound/pce_psg.h +++ b/src/engine/platform/sound/pce_psg.h @@ -147,6 +147,8 @@ class PCE_PSG void PeekWave(const unsigned int ch, uint32_t Address, uint32_t Length, uint8_t *Buffer); void PokeWave(const unsigned int ch, uint32_t Address, uint32_t Length, const uint8_t *Buffer); + + psg_channel channel[6]; private: @@ -178,8 +180,6 @@ class PCE_PSG int32_t vol_update_vllatch; bool vol_pending; - psg_channel channel[6]; - int32_t lastts; int revision; diff --git a/src/engine/platform/sound/saa1099.cpp b/src/engine/platform/sound/saa1099.cpp index 670afb97f..918ade54c 100644 --- a/src/engine/platform/sound/saa1099.cpp +++ b/src/engine/platform/sound/saa1099.cpp @@ -164,7 +164,7 @@ void saa1099_device::device_start() // sound_stream_update - handle a stream update //------------------------------------------------- -void saa1099_device::sound_stream_update(short** outputs, int len) +void saa1099_device::sound_stream_update(short** outputs, int len, DivDispatchOscBuffer** oscBuf) { int j, ch; /* if the channels are disabled we're done */ @@ -225,9 +225,14 @@ void saa1099_device::sound_stream_update(short** outputs, int len) } if (level) { - output_l += m_channels[ch].amplitude[ LEFT] * m_channels[ch].envelope[ LEFT] / 16; - output_r += m_channels[ch].amplitude[RIGHT] * m_channels[ch].envelope[RIGHT] / 16; - } + int this_output_l = m_channels[ch].amplitude[ LEFT] * m_channels[ch].envelope[ LEFT] / 16; + int this_output_r = m_channels[ch].amplitude[RIGHT] * m_channels[ch].envelope[RIGHT] / 16; + output_l+=this_output_l; + output_r+=this_output_r; + oscBuf[ch]->data[oscBuf[ch]->needle++]=(this_output_l+this_output_r)<<1; + } else if (oscBuf!=NULL) { + oscBuf[ch]->data[oscBuf[ch]->needle++]=0; + } } for (ch = 0; ch < 2; ch++) diff --git a/src/engine/platform/sound/saa1099.h b/src/engine/platform/sound/saa1099.h index 8ebad8a30..8b7cead25 100644 --- a/src/engine/platform/sound/saa1099.h +++ b/src/engine/platform/sound/saa1099.h @@ -7,6 +7,8 @@ #ifndef MAME_SOUND_SAA1099_H #define MAME_SOUND_SAA1099_H +#include "../../dispatch.h" + //************************************************************************** // TYPE DEFINITIONS //************************************************************************** @@ -28,7 +30,7 @@ public: void device_clock_changed(); // sound stream update overrides - void sound_stream_update(short** outputs, int len); + void sound_stream_update(short** outputs, int len, DivDispatchOscBuffer** oscBuf=NULL); private: struct saa1099_channel diff --git a/src/engine/platform/sound/swan.h b/src/engine/platform/sound/swan.h index a1d01fa54..bd421a74c 100644 --- a/src/engine/platform/sound/swan.h +++ b/src/engine/platform/sound/swan.h @@ -41,6 +41,8 @@ public: void SoundUpdate(uint32_t); void RAMWrite(uint32_t, uint8_t); + + int32_t sample_cache[4][2]; private: // Blip_Synth WaveSynth; @@ -61,8 +63,6 @@ private: uint8_t sweep_counter; uint8_t SampleRAMPos; - int32_t sample_cache[4][2]; - int32_t last_v_val; uint8_t HyperVoice; diff --git a/src/engine/platform/sound/vera_psg.c b/src/engine/platform/sound/vera_psg.c index afdf69cec..19f15d4eb 100644 --- a/src/engine/platform/sound/vera_psg.c +++ b/src/engine/platform/sound/vera_psg.c @@ -86,6 +86,12 @@ render(struct VERA_PSG* psg, int16_t *left, int16_t *right) if (ch->right) { r += val; } + + if (ch->left || ch->right) { + ch->lastOut=val; + } else { + ch->lastOut=0; + } } *left = l; diff --git a/src/engine/platform/sound/vera_psg.h b/src/engine/platform/sound/vera_psg.h index 7a6a7f01d..6a3f6828b 100644 --- a/src/engine/platform/sound/vera_psg.h +++ b/src/engine/platform/sound/vera_psg.h @@ -15,6 +15,7 @@ struct VERAChannel { uint8_t waveform; unsigned phase; + int lastOut; uint8_t noiseval; }; diff --git a/src/engine/platform/sound/vrcvi/vrcvi.cpp b/src/engine/platform/sound/vrcvi/vrcvi.cpp index 19152ed74..bca3ecda3 100644 --- a/src/engine/platform/sound/vrcvi/vrcvi.cpp +++ b/src/engine/platform/sound/vrcvi/vrcvi.cpp @@ -88,13 +88,23 @@ void vrcvi_core::tick() if (!m_control.m_halt) // Halt flag { // tick per each clock + int elemIndex=0; for (auto & elem : m_pulse) { - if (elem.tick()) + if (elem.tick()) { m_out += elem.m_control.m_volume; // add 4 bit pulse output + m_ch_out[elemIndex]=elem.m_control.m_volume; + } else { + m_ch_out[elemIndex]=0; + } + elemIndex++; } - if (m_sawtooth.tick()) + if (m_sawtooth.tick()) { m_out += bitfield(m_sawtooth.m_accum, 3, 5); // add 5 bit sawtooth output + m_ch_out[2]=bitfield(m_sawtooth.m_accum, 3, 5); + } else { + m_ch_out[2]=0; + } } if (m_timer.tick()) m_timer.counter_tick(); @@ -109,6 +119,7 @@ void vrcvi_core::reset() m_timer.reset(); m_control.reset(); m_out = 0; + std::fill(std::begin(m_ch_out),std::end(m_ch_out),0); } bool vrcvi_core::alu_t::tick() diff --git a/src/engine/platform/sound/vrcvi/vrcvi.hpp b/src/engine/platform/sound/vrcvi/vrcvi.hpp index 40b4245e3..d88ba7cf9 100644 --- a/src/engine/platform/sound/vrcvi/vrcvi.hpp +++ b/src/engine/platform/sound/vrcvi/vrcvi.hpp @@ -62,6 +62,8 @@ public: // 6 bit output s8 out() { return m_out; } + // channel output + s16 chan_out(u8 ch) { return m_ch_out[ch]; } private: // Common ALU for sound channels struct alu_t @@ -233,6 +235,7 @@ private: vrcvi_intf &m_intf; s8 m_out = 0; // 6 bit output + s8 m_ch_out[3] = {0}; // per-channel output }; #endif diff --git a/src/engine/platform/sound/x1_010/x1_010.hpp b/src/engine/platform/sound/x1_010/x1_010.hpp index e8f5e3e40..f208611da 100644 --- a/src/engine/platform/sound/x1_010/x1_010.hpp +++ b/src/engine/platform/sound/x1_010/x1_010.hpp @@ -63,6 +63,7 @@ public: // getters s32 output(u8 channel) { return m_out[channel & 1]; } + s32 chan_out(u8 channel) { return (m_voice[channel].data * (m_voice[channel].vol_out[0]+m_voice[channel].vol_out[1]))<<2; } // internal state void reset(); diff --git a/src/engine/platform/swan.cpp b/src/engine/platform/swan.cpp index 9ac07f6e6..97aa7b9ef 100644 --- a/src/engine/platform/swan.cpp +++ b/src/engine/platform/swan.cpp @@ -107,6 +107,9 @@ void DivPlatformSwan::acquire(short* bufL, short* bufR, size_t start, size_t len ws->SoundFlush(samp, 1); bufL[h]=samp[0]; bufR[h]=samp[1]; + for (int i=0; i<4; i++) { + oscBuf[i]->data[oscBuf[i]->needle++]=(ws->sample_cache[i][0]+ws->sample_cache[i][1])<<6; + } } } @@ -457,6 +460,10 @@ void* DivPlatformSwan::getChanState(int ch) { return &chan[ch]; } +DivDispatchOscBuffer* DivPlatformSwan::getOscBuffer(int ch) { + return oscBuf[ch]; +} + unsigned char* DivPlatformSwan::getRegisterPool() { // get Random from emulator regPool[0x12]=ws->SoundRead(0x92); @@ -532,6 +539,8 @@ int DivPlatformSwan::init(DivEngine* p, int channels, int sugRate, unsigned int rate=chipClock/16; // = 192000kHz, should be enough for (int i=0; i<4; i++) { isMuted[i]=false; + oscBuf[i]=new DivDispatchOscBuffer; + oscBuf[i]->rate=rate; } ws=new WSwan(); reset(); @@ -539,6 +548,9 @@ int DivPlatformSwan::init(DivEngine* p, int channels, int sugRate, unsigned int } void DivPlatformSwan::quit() { + for (int i=0; i<4; i++) { + delete oscBuf[i]; + } delete ws; } diff --git a/src/engine/platform/swan.h b/src/engine/platform/swan.h index c03dd0d55..32f400e5b 100644 --- a/src/engine/platform/swan.h +++ b/src/engine/platform/swan.h @@ -57,6 +57,7 @@ class DivPlatformSwan: public DivDispatch { wave(-1) {} }; Channel chan[4]; + DivDispatchOscBuffer* oscBuf[4]; bool isMuted[4]; bool pcm, sweep, furnaceDac; unsigned char sampleBank, noise; @@ -78,6 +79,7 @@ class DivPlatformSwan: public DivDispatch { void acquire(short* bufL, short* bufR, size_t start, size_t len); int dispatch(DivCommand c); void* getChanState(int chan); + DivDispatchOscBuffer* getOscBuffer(int chan); unsigned char* getRegisterPool(); int getRegisterPoolSize(); void reset(); diff --git a/src/engine/platform/vera.cpp b/src/engine/platform/vera.cpp index ccb819c9d..4dcc8f6c5 100644 --- a/src/engine/platform/vera.cpp +++ b/src/engine/platform/vera.cpp @@ -111,12 +111,20 @@ void DivPlatformVERA::acquire(short* bufL, short* bufR, size_t start, size_t len } int curLen=MIN(len,128); memset(buf,0,sizeof(buf)); - psg_render(psg,buf[0],buf[1],curLen); pcm_render(pcm,buf[2],buf[3],curLen); for (int i=0; idata[oscBuf[i]->needle++]=psg->channels[i].lastOut<<4; + } + int pcmOut=buf[2][i]+buf[3][i]; + if (pcmOut<-32768) pcmOut=-32768; + if (pcmOut>32767) pcmOut=32767; + oscBuf[16]->data[oscBuf[16]->needle++]=pcmOut; } len-=curLen; } @@ -373,6 +381,10 @@ void* DivPlatformVERA::getChanState(int ch) { return &chan[ch]; } +DivDispatchOscBuffer* DivPlatformVERA::getOscBuffer(int ch) { + return oscBuf[ch]; +} + unsigned char* DivPlatformVERA::getRegisterPool() { return regPool; } @@ -426,6 +438,7 @@ void DivPlatformVERA::poke(std::vector& wlist) { int DivPlatformVERA::init(DivEngine* p, int channels, int sugRate, unsigned int flags) { for (int i=0; i<17; i++) { isMuted[i]=false; + oscBuf[i]=new DivDispatchOscBuffer; } parent=p; psg=new struct VERA_PSG; @@ -434,11 +447,17 @@ int DivPlatformVERA::init(DivEngine* p, int channels, int sugRate, unsigned int skipRegisterWrites=false; chipClock=25000000; rate=chipClock/512; + for (int i=0; i<17; i++) { + oscBuf[i]->rate=rate; + } reset(); return 17; } void DivPlatformVERA::quit() { + for (int i=0; i<17; i++) { + delete oscBuf[i]; + } delete psg; delete pcm; } diff --git a/src/engine/platform/vera.h b/src/engine/platform/vera.h index ddcbed36a..734db020b 100644 --- a/src/engine/platform/vera.h +++ b/src/engine/platform/vera.h @@ -53,6 +53,7 @@ class DivPlatformVERA: public DivDispatch { Channel(): freq(0), baseFreq(0), pitch(0), pitch2(0), note(0), ins(-1), pan(0), active(false), freqChanged(false), inPorta(false), vol(0), outVol(0), accum(0), noiseval(0) {} }; Channel chan[17]; + DivDispatchOscBuffer* oscBuf[17]; bool isMuted[17]; unsigned char regPool[67]; struct VERA_PSG* psg; @@ -65,6 +66,7 @@ class DivPlatformVERA: public DivDispatch { void acquire(short* bufL, short* bufR, size_t start, size_t len); int dispatch(DivCommand c); void* getChanState(int chan); + DivDispatchOscBuffer* getOscBuffer(int chan); unsigned char* getRegisterPool(); int getRegisterPoolSize(); void reset(); diff --git a/src/engine/platform/vic20.cpp b/src/engine/platform/vic20.cpp index 32b1418f2..33250bfc6 100644 --- a/src/engine/platform/vic20.cpp +++ b/src/engine/platform/vic20.cpp @@ -77,6 +77,9 @@ void DivPlatformVIC20::acquire(short* bufL, short* bufR, size_t start, size_t le vic_sound_machine_calculate_samples(vic,&samp,1,1,0,SAMP_DIVIDER); bufL[h]=samp; bufR[h]=samp; + for (int i=0; i<4; i++) { + oscBuf[i]->data[oscBuf[i]->needle++]=vic->ch[i].out?(vic->volume<<11):0; + } } } @@ -275,6 +278,10 @@ void* DivPlatformVIC20::getChanState(int ch) { return &chan[ch]; } +DivDispatchOscBuffer* DivPlatformVIC20::getOscBuffer(int ch) { + return oscBuf[ch]; +} + unsigned char* DivPlatformVIC20::getRegisterPool() { return regPool; } @@ -314,6 +321,9 @@ void DivPlatformVIC20::setFlags(unsigned int flags) { chipClock=COLOR_NTSC*2.0/7.0; } rate=chipClock/4; + for (int i=0; i<4; i++) { + oscBuf[i]->rate=rate; + } } void DivPlatformVIC20::poke(unsigned int addr, unsigned short val) { @@ -330,6 +340,7 @@ int DivPlatformVIC20::init(DivEngine* p, int channels, int sugRate, unsigned int skipRegisterWrites=false; for (int i=0; i<4; i++) { isMuted[i]=false; + oscBuf[i]=new DivDispatchOscBuffer; } setFlags(flags); vic=new sound_vic20_t(); @@ -338,6 +349,9 @@ int DivPlatformVIC20::init(DivEngine* p, int channels, int sugRate, unsigned int } void DivPlatformVIC20::quit() { + for (int i=0; i<4; i++) { + delete oscBuf[i]; + } delete vic; } diff --git a/src/engine/platform/vic20.h b/src/engine/platform/vic20.h index 2b3f08e7f..f05ad8f2b 100644 --- a/src/engine/platform/vic20.h +++ b/src/engine/platform/vic20.h @@ -56,6 +56,7 @@ class DivPlatformVIC20: public DivDispatch { waveWriteCycle(-1) {} }; Channel chan[4]; + DivDispatchOscBuffer* oscBuf[4]; bool isMuted[4]; bool hasWaveWrite; @@ -67,6 +68,7 @@ class DivPlatformVIC20: public DivDispatch { void acquire(short* bufL, short* bufR, size_t start, size_t len); int dispatch(DivCommand c); void* getChanState(int chan); + DivDispatchOscBuffer* getOscBuffer(int chan); unsigned char* getRegisterPool(); int getRegisterPoolSize(); void reset(); diff --git a/src/engine/platform/vrc6.cpp b/src/engine/platform/vrc6.cpp index 64c4b831c..835acf4b8 100644 --- a/src/engine/platform/vrc6.cpp +++ b/src/engine/platform/vrc6.cpp @@ -97,6 +97,14 @@ void DivPlatformVRC6::acquire(short* bufL, short* bufR, size_t start, size_t len if (sample<-32768) sample=-32768; bufL[i]=bufR[i]=sample; + // Oscilloscope buffer part + if (++writeOscBuf>=32) { + writeOscBuf=0; + for (int i=0; i<3; i++) { + oscBuf[i]->data[oscBuf[i]->needle++]=vrc6.chan_out(i)<<10; + } + } + // Command part while (!writes.empty()) { QueuedWrite w=writes.front(); @@ -427,6 +435,10 @@ void* DivPlatformVRC6::getChanState(int ch) { return &chan[ch]; } +DivDispatchOscBuffer* DivPlatformVRC6::getOscBuffer(int ch) { + return oscBuf[ch]; +} + unsigned char* DivPlatformVRC6::getRegisterPool() { return regPool; } @@ -471,6 +483,9 @@ void DivPlatformVRC6::setFlags(unsigned int flags) { rate=COLOR_NTSC/2.0; } chipClock=rate; + for (int i=0; i<3; i++) { + oscBuf[i]->rate=rate/32; + } } void DivPlatformVRC6::notifyInsDeletion(void* ins) { @@ -491,8 +506,10 @@ int DivPlatformVRC6::init(DivEngine* p, int channels, int sugRate, unsigned int parent=p; dumpWrites=false; skipRegisterWrites=false; + writeOscBuf=0; for (int i=0; i<3; i++) { isMuted[i]=false; + oscBuf[i]=new DivDispatchOscBuffer; } setFlags(flags); reset(); @@ -500,6 +517,9 @@ int DivPlatformVRC6::init(DivEngine* p, int channels, int sugRate, unsigned int } void DivPlatformVRC6::quit() { + for (int i=0; i<3; i++) { + delete oscBuf[i]; + } } DivPlatformVRC6::~DivPlatformVRC6() { diff --git a/src/engine/platform/vrc6.h b/src/engine/platform/vrc6.h index 789af7f52..dd6863d5f 100644 --- a/src/engine/platform/vrc6.h +++ b/src/engine/platform/vrc6.h @@ -65,6 +65,7 @@ class DivPlatformVRC6: public DivDispatch { outVol(15) {} }; Channel chan[3]; + DivDispatchOscBuffer* oscBuf[3]; bool isMuted[3]; struct QueuedWrite { unsigned short addr; @@ -73,6 +74,7 @@ class DivPlatformVRC6: public DivDispatch { }; std::queue writes; unsigned char sampleBank; + unsigned char writeOscBuf; vrcvi_intf intf; vrcvi_core vrc6; unsigned char regPool[13]; @@ -83,6 +85,7 @@ class DivPlatformVRC6: public DivDispatch { void acquire(short* bufL, short* bufR, size_t start, size_t len); int dispatch(DivCommand c); void* getChanState(int chan); + DivDispatchOscBuffer* getOscBuffer(int chan); unsigned char* getRegisterPool(); int getRegisterPoolSize(); void reset(); diff --git a/src/engine/platform/x1_010.cpp b/src/engine/platform/x1_010.cpp index 42319a29b..a2e28b518 100644 --- a/src/engine/platform/x1_010.cpp +++ b/src/engine/platform/x1_010.cpp @@ -252,6 +252,10 @@ void DivPlatformX1_010::acquire(short* bufL, short* bufR, size_t start, size_t l //printf("tempL: %d tempR: %d\n",tempL,tempR); bufL[h]=stereo?tempL:((tempL+tempR)>>1); bufR[h]=stereo?tempR:bufL[h]; + + for (int i=0; i<16; i++) { + oscBuf[i]->data[oscBuf[i]->needle++]=x1_010->chan_out(i); + } } } @@ -822,6 +826,10 @@ void* DivPlatformX1_010::getChanState(int ch) { return &chan[ch]; } +DivDispatchOscBuffer* DivPlatformX1_010::getOscBuffer(int ch) { + return oscBuf[ch]; +} + unsigned char* DivPlatformX1_010::getRegisterPool() { for (int i=0; i<0x2000; i++) { regPool[i]=x1_010->ram_r(i); @@ -888,6 +896,9 @@ void DivPlatformX1_010::setFlags(unsigned int flags) { } rate=chipClock/512; stereo=flags&16; + for (int i=0; i<16; i++) { + oscBuf[i]->rate=rate; + } } void DivPlatformX1_010::poke(unsigned int addr, unsigned short val) { @@ -905,6 +916,7 @@ int DivPlatformX1_010::init(DivEngine* p, int channels, int sugRate, unsigned in stereo=false; for (int i=0; i<16; i++) { isMuted[i]=false; + oscBuf[i]=new DivDispatchOscBuffer; } setFlags(flags); intf.parent=parent; @@ -915,6 +927,9 @@ int DivPlatformX1_010::init(DivEngine* p, int channels, int sugRate, unsigned in } void DivPlatformX1_010::quit() { + for (int i=0; i<16; i++) { + delete oscBuf[i]; + } delete x1_010; } diff --git a/src/engine/platform/x1_010.h b/src/engine/platform/x1_010.h index 8dc749842..11de4bf4a 100644 --- a/src/engine/platform/x1_010.h +++ b/src/engine/platform/x1_010.h @@ -112,6 +112,7 @@ class DivPlatformX1_010: public DivDispatch { waveBank(0) {} }; Channel chan[16]; + DivDispatchOscBuffer* oscBuf[16]; bool isMuted[16]; bool stereo=false; unsigned char sampleBank; @@ -126,6 +127,7 @@ class DivPlatformX1_010: public DivDispatch { void acquire(short* bufL, short* bufR, size_t start, size_t len); int dispatch(DivCommand c); void* getChanState(int chan); + DivDispatchOscBuffer* getOscBuffer(int chan); unsigned char* getRegisterPool(); int getRegisterPoolSize(); void reset(); From 415e490025d694a23b1bba1d151c507fe12bc924 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 1 May 2022 03:12:32 -0500 Subject: [PATCH 262/342] per-channel oscilloscope, part 6 YM2612 and OPM (ymfm)! --- src/engine/platform/arcade.cpp | 6 ++++++ src/engine/platform/genesis.cpp | 6 ++++++ src/engine/platform/sound/ymfm/ymfm_fm.h | 18 ++++++++++++++---- src/engine/platform/sound/ymfm/ymfm_fm.ipp | 8 +++++++- src/engine/platform/sound/ymfm/ymfm_opm.h | 3 +++ src/engine/platform/sound/ymfm/ymfm_opn.h | 3 +++ 6 files changed, 39 insertions(+), 5 deletions(-) diff --git a/src/engine/platform/arcade.cpp b/src/engine/platform/arcade.cpp index 844fe67cf..f36b729a1 100644 --- a/src/engine/platform/arcade.cpp +++ b/src/engine/platform/arcade.cpp @@ -181,6 +181,8 @@ void DivPlatformArcade::acquire_nuked(short* bufL, short* bufR, size_t start, si void DivPlatformArcade::acquire_ymfm(short* bufL, short* bufR, size_t start, size_t len) { static int os[2]; + ymfm::ym2151::fm_engine* fme=fm_ymfm->debug_engine(); + for (size_t h=start; hgenerate(&out_ymfm); + for (int i=0; i<8; i++) { + oscBuf[i]->data[oscBuf[i]->needle++]=(fme->debug_channel(i)->debug_output(0)+fme->debug_channel(i)->debug_output(1)); + } + os[0]=out_ymfm.data[0]; if (os[0]<-32768) os[0]=-32768; if (os[0]>32767) os[0]=32767; diff --git a/src/engine/platform/genesis.cpp b/src/engine/platform/genesis.cpp index 27efab759..5325c982b 100644 --- a/src/engine/platform/genesis.cpp +++ b/src/engine/platform/genesis.cpp @@ -163,6 +163,8 @@ void DivPlatformGenesis::acquire_nuked(short* bufL, short* bufR, size_t start, s void DivPlatformGenesis::acquire_ymfm(short* bufL, short* bufR, size_t start, size_t len) { static int os[2]; + ymfm::ym2612::fm_engine* fme=fm_ymfm->debug_engine(); + for (size_t h=start; hdata[oscBuf[i]->needle++]=(fme->debug_channel(i)->debug_output(0)+fme->debug_channel(i)->debug_output(1))<<6; + } if (os[0]<-32768) os[0]=-32768; if (os[0]>32767) os[0]=32767; diff --git a/src/engine/platform/sound/ymfm/ymfm_fm.h b/src/engine/platform/sound/ymfm/ymfm_fm.h index 3239880e5..352ac9464 100644 --- a/src/engine/platform/sound/ymfm/ymfm_fm.h +++ b/src/engine/platform/sound/ymfm/ymfm_fm.h @@ -301,6 +301,7 @@ public: // simple getters for debugging fm_operator *debug_operator(uint32_t index) const { return m_op[index]; } + int32_t debug_output(uint32_t index) const { return m_output[index]; } private: // helper to add values to the outputs based on channel enables @@ -313,14 +314,22 @@ private: constexpr int out2_index = 2 % RegisterType::OUTPUTS; constexpr int out3_index = 3 % RegisterType::OUTPUTS; - if (RegisterType::OUTPUTS == 1 || m_regs.ch_output_0(choffs)) + if (RegisterType::OUTPUTS == 1 || m_regs.ch_output_0(choffs)) { + m_output[out0_index]=value; output.data[out0_index] += value; - if (RegisterType::OUTPUTS >= 2 && m_regs.ch_output_1(choffs)) + } + if (RegisterType::OUTPUTS >= 2 && m_regs.ch_output_1(choffs)) { + m_output[out1_index]=value; output.data[out1_index] += value; - if (RegisterType::OUTPUTS >= 3 && m_regs.ch_output_2(choffs)) + } + if (RegisterType::OUTPUTS >= 3 && m_regs.ch_output_2(choffs)) { + m_output[out2_index]=value; output.data[out2_index] += value; - if (RegisterType::OUTPUTS >= 4 && m_regs.ch_output_3(choffs)) + } + if (RegisterType::OUTPUTS >= 4 && m_regs.ch_output_3(choffs)) { + m_output[out3_index]=value; output.data[out3_index] += value; + } } // internal state @@ -330,6 +339,7 @@ private: fm_operator *m_op[4]; // up to 4 operators RegisterType &m_regs; // direct reference to registers fm_engine_base &m_owner; // reference to the owning engine + mutable int32_t m_output[4]; }; diff --git a/src/engine/platform/sound/ymfm/ymfm_fm.ipp b/src/engine/platform/sound/ymfm/ymfm_fm.ipp index 84948aedb..4e851737a 100644 --- a/src/engine/platform/sound/ymfm/ymfm_fm.ipp +++ b/src/engine/platform/sound/ymfm/ymfm_fm.ipp @@ -808,7 +808,8 @@ fm_channel::fm_channel(fm_engine_base &owner, uint32 m_feedback_in(0), m_op{ nullptr, nullptr, nullptr, nullptr }, m_regs(owner.regs()), - m_owner(owner) + m_owner(owner), + m_output{ 0, 0, 0, 0 } { } @@ -823,6 +824,11 @@ void fm_channel::reset() // reset our data m_feedback[0] = m_feedback[1] = 0; m_feedback_in = 0; + + m_output[0] = 0; + m_output[1] = 0; + m_output[2] = 0; + m_output[3] = 0; } diff --git a/src/engine/platform/sound/ymfm/ymfm_opm.h b/src/engine/platform/sound/ymfm/ymfm_opm.h index ad657ac01..830b195e4 100644 --- a/src/engine/platform/sound/ymfm/ymfm_opm.h +++ b/src/engine/platform/sound/ymfm/ymfm_opm.h @@ -281,6 +281,9 @@ public: // generate one sample of sound void generate(output_data *output, uint32_t numsamples = 1); + // get the engine + fm_engine* debug_engine() { return &m_fm; } + protected: // variants enum opm_variant diff --git a/src/engine/platform/sound/ymfm/ymfm_opn.h b/src/engine/platform/sound/ymfm/ymfm_opn.h index f4136c731..1a2f55805 100644 --- a/src/engine/platform/sound/ymfm/ymfm_opn.h +++ b/src/engine/platform/sound/ymfm/ymfm_opn.h @@ -761,6 +761,9 @@ public: // generate one sample of sound void generate(output_data *output, uint32_t numsamples = 1); + // get the engine + fm_engine* debug_engine() { return &m_fm; } + protected: // simulate the DAC discontinuity constexpr int32_t dac_discontinuity(int32_t value) const { return (value < 0) ? (value - 2) : (value + 3); } From 47f83c047217e4e6ac40eca65c6bfb627be5edd5 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 1 May 2022 03:47:04 -0500 Subject: [PATCH 263/342] per-channel oscilloscope, part 7 YM2610 and YM2610B only TIA is left --- src/engine/platform/sound/ymfm/ymfm_adpcm.h | 4 ++ src/engine/platform/sound/ymfm/ymfm_opn.h | 6 +++ src/engine/platform/sound/ymfm/ymfm_ssg.cpp | 2 + src/engine/platform/sound/ymfm/ymfm_ssg.h | 4 ++ src/engine/platform/ym2610.cpp | 43 +++++++++++++++++++++ src/engine/platform/ym2610.h | 2 + src/engine/platform/ym2610b.cpp | 38 ++++++++++++++++++ src/engine/platform/ym2610b.h | 2 + src/engine/platform/ym2610bext.cpp | 7 +++- src/engine/platform/ym2610bext.h | 1 + src/engine/platform/ym2610ext.cpp | 6 +++ src/engine/platform/ym2610ext.h | 1 + 12 files changed, 115 insertions(+), 1 deletion(-) diff --git a/src/engine/platform/sound/ymfm/ymfm_adpcm.h b/src/engine/platform/sound/ymfm/ymfm_adpcm.h index 4b4af0fdd..b2732cb1e 100644 --- a/src/engine/platform/sound/ymfm/ymfm_adpcm.h +++ b/src/engine/platform/sound/ymfm/ymfm_adpcm.h @@ -203,11 +203,15 @@ public: // return a reference to our registers adpcm_a_registers ®s() { return m_regs; } + // debug functions + adpcm_a_channel* debug_channel(uint32_t index) const { return m_channel[index].get(); } + private: // internal state ymfm_interface &m_intf; // reference to the interface std::unique_ptr m_channel[CHANNELS]; // array of channels adpcm_a_registers m_regs; // registers + int32_t m_last_out[CHANNELS]; // last output of channels }; diff --git a/src/engine/platform/sound/ymfm/ymfm_opn.h b/src/engine/platform/sound/ymfm/ymfm_opn.h index 1a2f55805..1b47885b9 100644 --- a/src/engine/platform/sound/ymfm/ymfm_opn.h +++ b/src/engine/platform/sound/ymfm/ymfm_opn.h @@ -697,6 +697,12 @@ public: // generate one sample of sound void generate(output_data *output, uint32_t numsamples = 1); + // get the engine + fm_engine* debug_fm_engine() { return &m_fm; } + ssg_engine* debug_ssg_engine() { return &m_ssg; } + adpcm_a_engine* debug_adpcm_a_engine() { return &m_adpcm_a; } + adpcm_b_engine* debug_adpcm_b_engine() { return &m_adpcm_b; } + protected: // internal helpers void update_prescale(); diff --git a/src/engine/platform/sound/ymfm/ymfm_ssg.cpp b/src/engine/platform/sound/ymfm/ymfm_ssg.cpp index 3335d47c7..5a9a9d43e 100644 --- a/src/engine/platform/sound/ymfm/ymfm_ssg.cpp +++ b/src/engine/platform/sound/ymfm/ymfm_ssg.cpp @@ -232,6 +232,8 @@ void ssg_engine::output(output_data &output) // convert to amplitude output.data[chan] = s_amplitudes[volume]; } + + m_last_out=output; } diff --git a/src/engine/platform/sound/ymfm/ymfm_ssg.h b/src/engine/platform/sound/ymfm/ymfm_ssg.h index 9f31c92f5..55e748d6b 100644 --- a/src/engine/platform/sound/ymfm/ymfm_ssg.h +++ b/src/engine/platform/sound/ymfm/ymfm_ssg.h @@ -187,6 +187,9 @@ public: // indicate the prescale has changed void prescale_changed() { if (m_override != nullptr) m_override->ssg_prescale_changed(); } + // get the last output + void get_last_out(output_data& out) { out=m_last_out; } + private: // internal state ymfm_interface &m_intf; // reference to the interface @@ -198,6 +201,7 @@ private: uint32_t m_noise_state; // current noise state ssg_registers m_regs; // registers ssg_override *m_override; // override interface + output_data m_last_out; }; } diff --git a/src/engine/platform/ym2610.cpp b/src/engine/platform/ym2610.cpp index c4ae0ad80..1e5d1f2ab 100644 --- a/src/engine/platform/ym2610.cpp +++ b/src/engine/platform/ym2610.cpp @@ -30,6 +30,10 @@ static unsigned char konOffs[4]={ 1, 2, 5, 6 }; +static unsigned char bchOffs[4]={ + 1, 2, 4, 5 +}; + #define CHIP_DIVIDER 32 const char* regCheatSheetYM2610[]={ @@ -337,6 +341,14 @@ double DivPlatformYM2610::NOTE_ADPCMB(int note) { void DivPlatformYM2610::acquire(short* bufL, short* bufR, size_t start, size_t len) { static int os[2]; + ymfm::ym2612::fm_engine* fme=fm->debug_fm_engine(); + ymfm::ssg_engine* ssge=fm->debug_ssg_engine(); + ymfm::adpcm_a_engine* aae=fm->debug_adpcm_a_engine(); + ymfm::adpcm_b_engine* abe=fm->debug_adpcm_b_engine(); + + ymfm::ssg_engine::output_data ssgOut; + ymfm::ymfm_output<2> adpcmOut; + for (size_t h=start; hdata[oscBuf[i]->needle++]=(fme->debug_channel(ch)->debug_output(0)+fme->debug_channel(ch)->debug_output(1)); + } + + ssge->get_last_out(ssgOut); + for (int i=4; i<7; i++) { + oscBuf[i]->data[oscBuf[i]->needle++]=ssgOut.data[i-4]; + } + + for (int i=7; i<13; i++) { + adpcmOut.clear(); + aae->debug_channel(i-7)->output(adpcmOut); + oscBuf[i]->data[oscBuf[i]->needle++]=adpcmOut.data[0]+adpcmOut.data[1]; + } + + adpcmOut.clear(); + abe->output(adpcmOut,1); + oscBuf[13]->data[oscBuf[13]->needle++]=adpcmOut.data[0]+adpcmOut.data[1]; } } @@ -1058,6 +1090,10 @@ void* DivPlatformYM2610::getChanState(int ch) { return &chan[ch]; } +DivDispatchOscBuffer* DivPlatformYM2610::getOscBuffer(int ch) { + return oscBuf[ch]; +} + unsigned char* DivPlatformYM2610::getRegisterPool() { return regPool; } @@ -1153,9 +1189,13 @@ int DivPlatformYM2610::init(DivEngine* p, int channels, int sugRate, unsigned in skipRegisterWrites=false; for (int i=0; i<14; i++) { isMuted[i]=false; + oscBuf[i]=new DivDispatchOscBuffer; } chipClock=8000000; rate=chipClock/16; + for (int i=0; i<14; i++) { + oscBuf[i]->rate=rate; + } iface.parent=parent; iface.sampleBank=0; fm=new ymfm::ym2610(iface); @@ -1168,6 +1208,9 @@ int DivPlatformYM2610::init(DivEngine* p, int channels, int sugRate, unsigned in } void DivPlatformYM2610::quit() { + for (int i=0; i<14; i++) { + delete oscBuf[i]; + } ay->quit(); delete ay; delete fm; diff --git a/src/engine/platform/ym2610.h b/src/engine/platform/ym2610.h index 56f6824d0..c46bc3c43 100644 --- a/src/engine/platform/ym2610.h +++ b/src/engine/platform/ym2610.h @@ -82,6 +82,7 @@ class DivPlatformYM2610: public DivDispatch { pan(3) {} }; Channel chan[14]; + DivDispatchOscBuffer* oscBuf[14]; bool isMuted[14]; struct QueuedWrite { unsigned short addr; @@ -117,6 +118,7 @@ class DivPlatformYM2610: public DivDispatch { void acquire(short* bufL, short* bufR, size_t start, size_t len); int dispatch(DivCommand c); void* getChanState(int chan); + DivDispatchOscBuffer* getOscBuffer(int chan); unsigned char* getRegisterPool(); int getRegisterPoolSize(); void reset(); diff --git a/src/engine/platform/ym2610b.cpp b/src/engine/platform/ym2610b.cpp index 12e32ef8c..65b8d723d 100644 --- a/src/engine/platform/ym2610b.cpp +++ b/src/engine/platform/ym2610b.cpp @@ -401,6 +401,14 @@ double DivPlatformYM2610B::NOTE_ADPCMB(int note) { void DivPlatformYM2610B::acquire(short* bufL, short* bufR, size_t start, size_t len) { static int os[2]; + ymfm::ym2612::fm_engine* fme=fm->debug_fm_engine(); + ymfm::ssg_engine* ssge=fm->debug_ssg_engine(); + ymfm::adpcm_a_engine* aae=fm->debug_adpcm_a_engine(); + ymfm::adpcm_b_engine* abe=fm->debug_adpcm_b_engine(); + + ymfm::ssg_engine::output_data ssgOut; + ymfm::ymfm_output<2> adpcmOut; + for (size_t h=start; hdata[oscBuf[i]->needle++]=(fme->debug_channel(i)->debug_output(0)+fme->debug_channel(i)->debug_output(1)); + } + + ssge->get_last_out(ssgOut); + for (int i=6; i<9; i++) { + oscBuf[i]->data[oscBuf[i]->needle++]=ssgOut.data[i-6]; + } + + for (int i=9; i<15; i++) { + adpcmOut.clear(); + aae->debug_channel(i-9)->output(adpcmOut); + oscBuf[i]->data[oscBuf[i]->needle++]=adpcmOut.data[0]+adpcmOut.data[1]; + } + + adpcmOut.clear(); + abe->output(adpcmOut,1); + oscBuf[15]->data[oscBuf[15]->needle++]=adpcmOut.data[0]+adpcmOut.data[1]; } } @@ -1121,6 +1148,10 @@ void* DivPlatformYM2610B::getChanState(int ch) { return &chan[ch]; } +DivDispatchOscBuffer* DivPlatformYM2610B::getOscBuffer(int ch) { + return oscBuf[ch]; +} + unsigned char* DivPlatformYM2610B::getRegisterPool() { return regPool; } @@ -1216,9 +1247,13 @@ int DivPlatformYM2610B::init(DivEngine* p, int channels, int sugRate, unsigned i skipRegisterWrites=false; for (int i=0; i<16; i++) { isMuted[i]=false; + oscBuf[i]=new DivDispatchOscBuffer; } chipClock=8000000; rate=chipClock/16; + for (int i=0; i<16; i++) { + oscBuf[i]->rate=rate; + } iface.parent=parent; iface.sampleBank=0; fm=new ymfm::ym2610b(iface); @@ -1231,6 +1266,9 @@ int DivPlatformYM2610B::init(DivEngine* p, int channels, int sugRate, unsigned i } void DivPlatformYM2610B::quit() { + for (int i=0; i<16; i++) { + delete oscBuf[i]; + } ay->quit(); delete ay; delete fm; diff --git a/src/engine/platform/ym2610b.h b/src/engine/platform/ym2610b.h index c01b9515a..df0a83233 100644 --- a/src/engine/platform/ym2610b.h +++ b/src/engine/platform/ym2610b.h @@ -74,6 +74,7 @@ class DivPlatformYM2610B: public DivDispatch { pan(3) {} }; Channel chan[16]; + DivDispatchOscBuffer* oscBuf[16]; bool isMuted[16]; struct QueuedWrite { unsigned short addr; @@ -108,6 +109,7 @@ class DivPlatformYM2610B: public DivDispatch { void acquire(short* bufL, short* bufR, size_t start, size_t len); int dispatch(DivCommand c); void* getChanState(int chan); + DivDispatchOscBuffer* getOscBuffer(int chan); unsigned char* getRegisterPool(); int getRegisterPoolSize(); void reset(); diff --git a/src/engine/platform/ym2610bext.cpp b/src/engine/platform/ym2610bext.cpp index 5eec24ece..87c635736 100644 --- a/src/engine/platform/ym2610bext.cpp +++ b/src/engine/platform/ym2610bext.cpp @@ -336,13 +336,18 @@ void DivPlatformYM2610BExt::forceIns() { } } - void* DivPlatformYM2610BExt::getChanState(int ch) { if (ch>=6) return &chan[ch-3]; if (ch>=2) return &opChan[ch-2]; return &chan[ch]; } +DivDispatchOscBuffer* DivPlatformYM2610BExt::getOscBuffer(int ch) { + if (ch>=6) return oscBuf[ch-3]; + if (ch<3) return oscBuf[ch]; + return NULL; +} + void DivPlatformYM2610BExt::reset() { DivPlatformYM2610B::reset(); diff --git a/src/engine/platform/ym2610bext.h b/src/engine/platform/ym2610bext.h index 5dd98c87e..b416b5c70 100644 --- a/src/engine/platform/ym2610bext.h +++ b/src/engine/platform/ym2610bext.h @@ -38,6 +38,7 @@ class DivPlatformYM2610BExt: public DivPlatformYM2610B { public: int dispatch(DivCommand c); void* getChanState(int chan); + DivDispatchOscBuffer* getOscBuffer(int chan); void reset(); void forceIns(); void tick(bool sysTick=true); diff --git a/src/engine/platform/ym2610ext.cpp b/src/engine/platform/ym2610ext.cpp index 6ae911061..013e7b954 100644 --- a/src/engine/platform/ym2610ext.cpp +++ b/src/engine/platform/ym2610ext.cpp @@ -343,6 +343,12 @@ void* DivPlatformYM2610Ext::getChanState(int ch) { return &chan[ch]; } +DivDispatchOscBuffer* DivPlatformYM2610Ext::getOscBuffer(int ch) { + if (ch>=5) return oscBuf[ch-3]; + if (ch<2) return oscBuf[ch]; + return NULL; +} + void DivPlatformYM2610Ext::reset() { DivPlatformYM2610::reset(); diff --git a/src/engine/platform/ym2610ext.h b/src/engine/platform/ym2610ext.h index 0ff45981e..10be6638d 100644 --- a/src/engine/platform/ym2610ext.h +++ b/src/engine/platform/ym2610ext.h @@ -38,6 +38,7 @@ class DivPlatformYM2610Ext: public DivPlatformYM2610 { public: int dispatch(DivCommand c); void* getChanState(int chan); + DivDispatchOscBuffer* getOscBuffer(int chan); void reset(); void forceIns(); void tick(bool sysTick=true); From 3a4f120120b9b578938becea25167619ac9bb8c9 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 1 May 2022 04:01:25 -0500 Subject: [PATCH 264/342] per-channel oscilloscope, part 8 TIA is done --- src/engine/platform/sound/tia/TIASnd.cpp | 7 ++++++- src/engine/platform/sound/tia/TIASnd.h | 3 ++- src/engine/platform/tia.cpp | 10 +++++++++- src/engine/platform/tia.h | 2 ++ 4 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/engine/platform/sound/tia/TIASnd.cpp b/src/engine/platform/sound/tia/TIASnd.cpp index f6ef80460..e2d0568c4 100644 --- a/src/engine/platform/sound/tia/TIASnd.cpp +++ b/src/engine/platform/sound/tia/TIASnd.cpp @@ -157,7 +157,7 @@ void TIASound::volume(unsigned int percent) } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void TIASound::process(short* buffer, unsigned int samples) +void TIASound::process(short* buffer, unsigned int samples, DivDispatchOscBuffer** oscBuf) { // Make temporary local copy unsigned char audc0 = myAUDC[0], audc1 = myAUDC[1]; @@ -339,6 +339,11 @@ void TIASound::process(short* buffer, unsigned int samples) samples--; break; } + + if (oscBuf!=NULL) { + oscBuf[0]->data[oscBuf[0]->needle++]=v0; + oscBuf[1]->data[oscBuf[1]->needle++]=v1; + } } // Save for next round diff --git a/src/engine/platform/sound/tia/TIASnd.h b/src/engine/platform/sound/tia/TIASnd.h index 14e48577a..78459426f 100644 --- a/src/engine/platform/sound/tia/TIASnd.h +++ b/src/engine/platform/sound/tia/TIASnd.h @@ -21,6 +21,7 @@ #define TIASOUND_HXX #include +#include "../../../dispatch.h" /** This class implements a fairly accurate emulation of the TIA sound @@ -87,7 +88,7 @@ class TIASound @param buffer The location to store generated samples @param samples The number of samples to generate */ - void process(short* buffer, unsigned int samples); + void process(short* buffer, unsigned int samples, DivDispatchOscBuffer** oscBuf=NULL); /** Set the volume of the samples created (0-100) diff --git a/src/engine/platform/tia.cpp b/src/engine/platform/tia.cpp index f6f7dd9cf..d3bd5ce2d 100644 --- a/src/engine/platform/tia.cpp +++ b/src/engine/platform/tia.cpp @@ -48,7 +48,7 @@ const char** DivPlatformTIA::getRegisterSheet() { } void DivPlatformTIA::acquire(short* bufL, short* bufR, size_t start, size_t len) { - tia.process(bufL+start,len); + tia.process(bufL+start,len,oscBuf); } unsigned char DivPlatformTIA::dealWithFreq(unsigned char shape, int base, int pitch) { @@ -290,6 +290,10 @@ void* DivPlatformTIA::getChanState(int ch) { return &chan[ch]; } +DivDispatchOscBuffer* DivPlatformTIA::getOscBuffer(int ch) { + return oscBuf[ch]; +} + unsigned char* DivPlatformTIA::getRegisterPool() { return regPool; } @@ -337,6 +341,9 @@ void DivPlatformTIA::setFlags(unsigned int flags) { rate=31468; } chipClock=rate; + for (int i=0; i<2; i++) { + oscBuf[i]->rate=rate; + } } int DivPlatformTIA::init(DivEngine* p, int channels, int sugRate, unsigned int flags) { @@ -345,6 +352,7 @@ int DivPlatformTIA::init(DivEngine* p, int channels, int sugRate, unsigned int f skipRegisterWrites=false; for (int i=0; i<2; i++) { isMuted[i]=false; + oscBuf[i]=new DivDispatchOscBuffer; } tia.channels(1,false); setFlags(flags); diff --git a/src/engine/platform/tia.h b/src/engine/platform/tia.h index dc3bbfd10..76064d069 100644 --- a/src/engine/platform/tia.h +++ b/src/engine/platform/tia.h @@ -40,6 +40,7 @@ class DivPlatformTIA: public DivDispatch { Channel(): freq(0), baseFreq(0), pitch(0), pitch2(0), note(0), ins(-1), shape(4), active(false), insChanged(true), freqChanged(false), keyOn(false), keyOff(false), portaPause(false), inPorta(false), vol(0), outVol(15) {} }; Channel chan[2]; + DivDispatchOscBuffer* oscBuf[2]; bool isMuted[2]; TIASound tia; unsigned char regPool[16]; @@ -51,6 +52,7 @@ class DivPlatformTIA: public DivDispatch { void acquire(short* bufL, short* bufR, size_t start, size_t len); int dispatch(DivCommand c); void* getChanState(int chan); + DivDispatchOscBuffer* getOscBuffer(int chan); unsigned char* getRegisterPool(); int getRegisterPoolSize(); void reset(); From e1a02f64de5c1f15ec7e28c798702e4884d7ccbe Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 1 May 2022 04:30:04 -0500 Subject: [PATCH 265/342] attempt to fix CI --- src/engine/platform/ym2610.cpp | 1 + src/engine/platform/ym2610b.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/src/engine/platform/ym2610.cpp b/src/engine/platform/ym2610.cpp index 1e5d1f2ab..18acd074d 100644 --- a/src/engine/platform/ym2610.cpp +++ b/src/engine/platform/ym2610.cpp @@ -18,6 +18,7 @@ */ #include "ym2610.h" +#include "sound/ymfm/ymfm.h" #include "../engine.h" #include #include diff --git a/src/engine/platform/ym2610b.cpp b/src/engine/platform/ym2610b.cpp index 65b8d723d..dde693054 100644 --- a/src/engine/platform/ym2610b.cpp +++ b/src/engine/platform/ym2610b.cpp @@ -18,6 +18,7 @@ */ #include "ym2610b.h" +#include "sound/ymfm/ymfm.h" #include "../engine.h" #include #include From 05ae7746171da96ff93262efb246fdb15b91afeb Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 1 May 2022 04:49:41 -0500 Subject: [PATCH 266/342] let's hope this works --- src/engine/platform/ym2610.cpp | 2 +- src/engine/platform/ym2610b.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/engine/platform/ym2610.cpp b/src/engine/platform/ym2610.cpp index 18acd074d..5154acc62 100644 --- a/src/engine/platform/ym2610.cpp +++ b/src/engine/platform/ym2610.cpp @@ -388,7 +388,7 @@ void DivPlatformYM2610::acquire(short* bufL, short* bufR, size_t start, size_t l for (int i=7; i<13; i++) { adpcmOut.clear(); - aae->debug_channel(i-7)->output(adpcmOut); + aae->debug_channel(i-7)->output<2>(adpcmOut); oscBuf[i]->data[oscBuf[i]->needle++]=adpcmOut.data[0]+adpcmOut.data[1]; } diff --git a/src/engine/platform/ym2610b.cpp b/src/engine/platform/ym2610b.cpp index dde693054..a0f1a6e3f 100644 --- a/src/engine/platform/ym2610b.cpp +++ b/src/engine/platform/ym2610b.cpp @@ -447,7 +447,7 @@ void DivPlatformYM2610B::acquire(short* bufL, short* bufR, size_t start, size_t for (int i=9; i<15; i++) { adpcmOut.clear(); - aae->debug_channel(i-9)->output(adpcmOut); + aae->debug_channel(i-9)->output<2>(adpcmOut); oscBuf[i]->data[oscBuf[i]->needle++]=adpcmOut.data[0]+adpcmOut.data[1]; } From 9f19b5dfc4dc2324eed40ed793a7f0d90f97715c Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 1 May 2022 05:10:41 -0500 Subject: [PATCH 267/342] ok so screw you --- src/engine/platform/sound/ymfm/ymfm_adpcm.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/engine/platform/sound/ymfm/ymfm_adpcm.cpp b/src/engine/platform/sound/ymfm/ymfm_adpcm.cpp index 0d285cd16..01f35367c 100644 --- a/src/engine/platform/sound/ymfm/ymfm_adpcm.cpp +++ b/src/engine/platform/sound/ymfm/ymfm_adpcm.cpp @@ -245,6 +245,8 @@ void adpcm_a_channel::output(ymfm_output &output) const output.data[1] += value; } +template void adpcm_a_channel::output<1>(ymfm_output<1> &output) const; +template void adpcm_a_channel::output<2>(ymfm_output<2> &output) const; //********************************************************* From 58fe971a3cb17205fe42a18d04233f7ad7ec9ecd Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 1 May 2022 05:18:30 -0500 Subject: [PATCH 268/342] Clang you are so arrogant --- src/engine/platform/sound/ymfm/ymfm_adpcm.h | 1 - 1 file changed, 1 deletion(-) diff --git a/src/engine/platform/sound/ymfm/ymfm_adpcm.h b/src/engine/platform/sound/ymfm/ymfm_adpcm.h index b2732cb1e..c08eeae59 100644 --- a/src/engine/platform/sound/ymfm/ymfm_adpcm.h +++ b/src/engine/platform/sound/ymfm/ymfm_adpcm.h @@ -211,7 +211,6 @@ private: ymfm_interface &m_intf; // reference to the interface std::unique_ptr m_channel[CHANNELS]; // array of channels adpcm_a_registers m_regs; // registers - int32_t m_last_out[CHANNELS]; // last output of channels }; From 4d8f86a27c85cb5473a113e9f9c74bbcde22deba Mon Sep 17 00:00:00 2001 From: Laurens Holst Date: Sun, 1 May 2022 17:25:27 +0200 Subject: [PATCH 269/342] Fix horizontal scroll direction on macOS. See open Dear ImGUI issue: https://github.com/ocornut/imgui/issues/4019 This patches it for macOS in the local copy of imgui_impl_sdl. --- extern/imgui_patched/imgui_impl_sdl.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/extern/imgui_patched/imgui_impl_sdl.cpp b/extern/imgui_patched/imgui_impl_sdl.cpp index 2870d2170..c1b2649ca 100644 --- a/extern/imgui_patched/imgui_impl_sdl.cpp +++ b/extern/imgui_patched/imgui_impl_sdl.cpp @@ -300,6 +300,9 @@ bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event) { float wheel_x = (event->wheel.x > 0) ? 1.0f : (event->wheel.x < 0) ? -1.0f : 0.0f; float wheel_y = (event->wheel.y > 0) ? 1.0f : (event->wheel.y < 0) ? -1.0f : 0.0f; +#ifdef __APPLE__ + wheel_x = -wheel_x; +#endif io.AddMouseWheelEvent(wheel_x, wheel_y); return true; } From 4bc70fad7b34de20c11ac4459b49035517cb0685 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 1 May 2022 13:41:30 -0500 Subject: [PATCH 270/342] format oops --- papers/format.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/papers/format.md b/papers/format.md index 570529c4e..42488719b 100644 --- a/papers/format.md +++ b/papers/format.md @@ -675,6 +675,26 @@ size | description 1 | parameter 2 1 | parameter 3 1 | parameter 4 + --- | **additional macro mode flags** (>=84) + 1 | volume macro mode + 1 | duty macro mode + 1 | wave macro mode + 1 | pitch macro mode + 1 | extra 1 macro mode + 1 | extra 2 macro mode + 1 | extra 3 macro mode + 1 | alg macro mode + 1 | fb macro mode + 1 | fms macro mode + 1 | ams macro mode + 1 | left panning macro mode + 1 | right panning macro mode + 1 | phase reset macro mode + 1 | extra 4 macro mode + 1 | extra 5 macro mode + 1 | extra 6 macro mode + 1 | extra 7 macro mode + 1 | extra 8 macro mode --- | **extra C64 data** (>=89) 1 | don't test/gate before new note ``` From 7f0074511c3f98c05930d12fe6a190a6949be36c Mon Sep 17 00:00:00 2001 From: Laurens Holst Date: Sun, 1 May 2022 19:57:44 +0200 Subject: [PATCH 271/342] Move renderSamples() to DivDispatch implementations. To prevent rendering samples for systems that are not in use. Additionally, it gives the systems more flexibility to render the samples according to their specific configuration. --- src/engine/dispatch.h | 20 ++++ src/engine/engine.cpp | 122 +++--------------------- src/engine/engine.h | 28 +----- src/engine/fileOps.cpp | 18 ++-- src/engine/platform/abstract.cpp | 16 ++++ src/engine/platform/qsound.cpp | 54 ++++++++++- src/engine/platform/qsound.h | 6 ++ src/engine/platform/x1_010.cpp | 48 +++++++++- src/engine/platform/x1_010.h | 14 ++- src/engine/platform/ym2610.cpp | 85 ++++++++++++++++- src/engine/platform/ym2610.h | 25 ++++- src/engine/platform/ym2610Interface.cpp | 10 +- src/engine/platform/ym2610b.cpp | 5 +- src/engine/platform/ym2610b.h | 3 +- src/engine/vgmOps.cpp | 65 +++++++------ src/gui/stats.cpp | 27 +++--- 16 files changed, 327 insertions(+), 219 deletions(-) diff --git a/src/engine/dispatch.h b/src/engine/dispatch.h index 7e5f998b9..58c9c9b71 100644 --- a/src/engine/dispatch.h +++ b/src/engine/dispatch.h @@ -438,6 +438,26 @@ class DivDispatch { */ virtual const char** getRegisterSheet(); + /** + * Get sample memory buffer. + */ + virtual const void* getSampleMem(int index = 0); + + /** + * Get sample memory capacity. + */ + virtual size_t getSampleMemCapacity(int index = 0); + + /** + * Get sample memory usage. + */ + virtual size_t getSampleMemUsage(int index = 0); + + /** + * Render samples into sample memory. + */ + virtual void renderSamples(); + /** * initialize this DivDispatch. * @param parent the parent DivEngine. diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 3a0a841d8..279d75226 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -506,118 +506,12 @@ void DivEngine::renderSamples() { song.sample[i]->render(); } - // step 2: allocate ADPCM-A samples - if (adpcmAMem==NULL) adpcmAMem=new unsigned char[16777216]; - - size_t memPos=0; - for (int i=0; ilengthA+255)&(~0xff); - if ((memPos&0xf00000)!=((memPos+paddedLen)&0xf00000)) { - memPos=(memPos+0xfffff)&0xf00000; + // step 2: render samples to dispatch + for (int i=0; irenderSamples(); } - if (memPos>=16777216) { - logW("out of ADPCM-A memory for sample %d!",i); - break; - } - if (memPos+paddedLen>=16777216) { - memcpy(adpcmAMem+memPos,s->dataA,16777216-memPos); - logW("out of ADPCM-A memory for sample %d!",i); - } else { - memcpy(adpcmAMem+memPos,s->dataA,paddedLen); - } - s->offA=memPos; - memPos+=paddedLen; } - adpcmAMemLen=memPos+256; - - // step 2: allocate ADPCM-B samples - if (adpcmBMem==NULL) adpcmBMem=new unsigned char[16777216]; - - memPos=0; - for (int i=0; ilengthB+255)&(~0xff); - if ((memPos&0xf00000)!=((memPos+paddedLen)&0xf00000)) { - memPos=(memPos+0xfffff)&0xf00000; - } - if (memPos>=16777216) { - logW("out of ADPCM-B memory for sample %d!",i); - break; - } - if (memPos+paddedLen>=16777216) { - memcpy(adpcmBMem+memPos,s->dataB,16777216-memPos); - logW("out of ADPCM-B memory for sample %d!",i); - } else { - memcpy(adpcmBMem+memPos,s->dataB,paddedLen); - } - s->offB=memPos; - memPos+=paddedLen; - } - adpcmBMemLen=memPos+256; - - // step 4: allocate qsound pcm samples - if (qsoundMem==NULL) qsoundMem=new unsigned char[16777216]; - memset(qsoundMem,0,16777216); - - memPos=0; - for (int i=0; ilength8; - if (length>65536-16) { - length=65536-16; - } - if ((memPos&0xff0000)!=((memPos+length)&0xff0000)) { - memPos=(memPos+0xffff)&0xff0000; - } - if (memPos>=16777216) { - logW("out of QSound PCM memory for sample %d!",i); - break; - } - if (memPos+length>=16777216) { - for (unsigned int i=0; i<16777216-(memPos+length); i++) { - qsoundMem[(memPos+i)^0x8000]=s->data8[i]; - } - logW("out of QSound PCM memory for sample %d!",i); - } else { - for (int i=0; idata8[i]; - } - } - s->offQSound=memPos^0x8000; - memPos+=length+16; - } - qsoundMemLen=memPos+256; - - // step 4: allocate x1-010 pcm samples - if (x1_010Mem==NULL) x1_010Mem=new unsigned char[1048576]; - memset(x1_010Mem,0,1048576); - - memPos=0; - for (int i=0; ilength8+4095)&(~0xfff); - // fit sample bank size to 128KB for Seta 2 external bankswitching logic (not emulated yet!) - if (paddedLen>131072) { - paddedLen=131072; - } - if ((memPos&0xfe0000)!=((memPos+paddedLen)&0xfe0000)) { - memPos=(memPos+0x1ffff)&0xfe0000; - } - if (memPos>=1048576) { - logW("out of X1-010 memory for sample %d!",i); - break; - } - if (memPos+paddedLen>=1048576) { - memcpy(x1_010Mem+memPos,s->data8,1048576-memPos); - logW("out of X1-010 memory for sample %d!",i); - } else { - memcpy(x1_010Mem+memPos,s->data8,paddedLen); - } - s->offX1_010=memPos; - memPos+=paddedLen; - } - x1_010MemLen=memPos+256; } String DivEngine::encodeSysDesc(std::vector& desc) { @@ -725,11 +619,11 @@ void DivEngine::createNew(const int* description) { initSongWithDesc(description); } recalcChans(); - renderSamples(); saveLock.unlock(); BUSY_END; initDispatch(); BUSY_BEGIN; + renderSamples(); reset(); BUSY_END; } @@ -967,6 +861,11 @@ DivSample* DivEngine::getSample(int index) { return song.sample[index]; } +DivDispatch* DivEngine::getDispatch(int index) { + if (index<0 || index>=song.systemLen) return NULL; + return disCont[index].dispatch; +} + void DivEngine::setLoops(int loops) { remainingLoops=loops; } @@ -2812,6 +2711,7 @@ bool DivEngine::init() { oscBuf[1]=new float[32768]; initDispatch(); + renderSamples(); reset(); active=true; diff --git a/src/engine/engine.h b/src/engine/engine.h index 2ca301c53..dc1436eed 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -422,6 +422,7 @@ class DivEngine { DivInstrument* getIns(int index, DivInstrumentType fallbackType=DIV_INS_FM); DivWavetable* getWave(int index); DivSample* getSample(int index); + DivDispatch* getDispatch(int index); // parse system setup description String encodeSysDesc(std::vector& desc); std::vector decodeSysDesc(String desc); @@ -849,19 +850,6 @@ class DivEngine { // terminate the engine. bool quit(); - unsigned char* adpcmAMem; - size_t adpcmAMemLen; - unsigned char* adpcmBMem; - size_t adpcmBMemLen; - unsigned char* qsoundMem; - size_t qsoundMemLen; - unsigned char* qsoundAMem; - size_t qsoundAMemLen; - unsigned char* dpcmMem; - size_t dpcmMemLen; - unsigned char* x1_010Mem; - size_t x1_010MemLen; - DivEngine(): output(NULL), exportThread(NULL), @@ -935,19 +923,7 @@ class DivEngine { oscSize(1), oscReadPos(0), oscWritePos(0), - tickMult(1), - adpcmAMem(NULL), - adpcmAMemLen(0), - adpcmBMem(NULL), - adpcmBMemLen(0), - qsoundMem(NULL), - qsoundMemLen(0), - qsoundAMem(NULL), - qsoundAMemLen(0), - dpcmMem(NULL), - dpcmMemLen(0), - x1_010Mem(NULL), - x1_010MemLen(0) { + tickMult(1) { memset(isMuted,0,DIV_MAX_CHANS*sizeof(bool)); memset(keyHit,0,DIV_MAX_CHANS*sizeof(bool)); memset(dispatchChanOfChan,0,DIV_MAX_CHANS*sizeof(int)); diff --git a/src/engine/fileOps.cpp b/src/engine/fileOps.cpp index cfd23e1fb..b543e3e12 100644 --- a/src/engine/fileOps.cpp +++ b/src/engine/fileOps.cpp @@ -899,12 +899,14 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) { song.unload(); song=ds; recalcChans(); - renderSamples(); saveLock.unlock(); BUSY_END; if (active) { initDispatch(); - syncReset(); + BUSY_BEGIN; + renderSamples(); + reset(); + BUSY_END; } } catch (EndOfFileException& e) { logE("premature end of file!"); @@ -1596,12 +1598,14 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) { song.unload(); song=ds; recalcChans(); - renderSamples(); saveLock.unlock(); BUSY_END; if (active) { initDispatch(); - syncReset(); + BUSY_BEGIN; + renderSamples(); + reset(); + BUSY_END; } } catch (EndOfFileException& e) { logE("premature end of file!"); @@ -2005,12 +2009,14 @@ bool DivEngine::loadMod(unsigned char* file, size_t len) { song.unload(); song=ds; recalcChans(); - renderSamples(); saveLock.unlock(); BUSY_END; if (active) { initDispatch(); - syncReset(); + BUSY_BEGIN; + renderSamples(); + reset(); + BUSY_END; } success=true; } catch (EndOfFileException& e) { diff --git a/src/engine/platform/abstract.cpp b/src/engine/platform/abstract.cpp index 91f61fc62..54366e1ba 100644 --- a/src/engine/platform/abstract.cpp +++ b/src/engine/platform/abstract.cpp @@ -141,6 +141,22 @@ const char** DivDispatch::getRegisterSheet() { return NULL; } +const void* DivDispatch::getSampleMem(int index) { + return NULL; +} + +size_t DivDispatch::getSampleMemCapacity(int index) { + return 0; +} + +size_t DivDispatch::getSampleMemUsage(int index) { + return 0; +} + +void DivDispatch::renderSamples() { + +} + int DivDispatch::init(DivEngine* p, int channels, int sugRate, unsigned int flags) { return 0; } diff --git a/src/engine/platform/qsound.cpp b/src/engine/platform/qsound.cpp index c5d003be5..6e89e5b61 100644 --- a/src/engine/platform/qsound.cpp +++ b/src/engine/platform/qsound.cpp @@ -268,8 +268,6 @@ const char* DivPlatformQSound::getEffectName(unsigned char effect) { return NULL; } void DivPlatformQSound::acquire(short* bufL, short* bufR, size_t start, size_t len) { - chip.rom_data = parent->qsoundMem; - chip.rom_mask = 0xffffff; for (size_t h=start; hsong.sampleLen; i++) { + DivSample* s=parent->song.sample[i]; + int length=s->length8; + if (length>65536-16) { + length=65536-16; + } + if ((memPos&0xff0000)!=((memPos+length)&0xff0000)) { + memPos=(memPos+0xffff)&0xff0000; + } + if (memPos>=getSampleMemCapacity()) { + logW("out of QSound PCM memory for sample %d!",i); + break; + } + if (memPos+length>=getSampleMemCapacity()) { + for (unsigned int i=0; idata8[i]; + } + logW("out of QSound PCM memory for sample %d!",i); + } else { + for (int i=0; idata8[i]; + } + } + s->offQSound=memPos^0x8000; + memPos+=length+16; + } + sampleMemLen=memPos+256; +} + int DivPlatformQSound::init(DivEngine* p, int channels, int sugRate, unsigned int flags) { parent=p; dumpWrites=false; @@ -651,8 +694,10 @@ int DivPlatformQSound::init(DivEngine* p, int channels, int sugRate, unsigned in chipClock=60000000; rate = qsound_start(&chip, chipClock); - chip.rom_data = (unsigned char*)&chip.rom_mask; - chip.rom_mask = 0; + sampleMem=new unsigned char[getSampleMemCapacity()]; + sampleMemLen=0; + chip.rom_data=sampleMem; + chip.rom_mask=0xffffff; reset(); for (int i=0; i<19; i++) { @@ -662,6 +707,7 @@ int DivPlatformQSound::init(DivEngine* p, int channels, int sugRate, unsigned in } void DivPlatformQSound::quit() { + delete[] sampleMem; for (int i=0; i<19; i++) { delete oscBuf[i]; } diff --git a/src/engine/platform/qsound.h b/src/engine/platform/qsound.h index 0fbce3b32..d12e952ef 100644 --- a/src/engine/platform/qsound.h +++ b/src/engine/platform/qsound.h @@ -67,6 +67,8 @@ class DivPlatformQSound: public DivDispatch { int echoDelay; int echoFeedback; + unsigned char* sampleMem; + size_t sampleMemLen; struct qsound_chip chip; unsigned short regPool[512]; @@ -94,6 +96,10 @@ class DivPlatformQSound: public DivDispatch { void poke(std::vector& wlist); const char** getRegisterSheet(); const char* getEffectName(unsigned char effect); + const void* getSampleMem(int index = 0); + size_t getSampleMemCapacity(int index = 0); + size_t getSampleMemUsage(int index = 0); + void renderSamples(); int init(DivEngine* parent, int channels, int sugRate, unsigned int flags); void quit(); }; diff --git a/src/engine/platform/x1_010.cpp b/src/engine/platform/x1_010.cpp index a2e28b518..03cd4f6be 100644 --- a/src/engine/platform/x1_010.cpp +++ b/src/engine/platform/x1_010.cpp @@ -19,6 +19,7 @@ #include "x1_010.h" #include "../engine.h" +#include "../../ta-log.h" #include //#define rWrite(a,v) pendingWrites[a]=v; @@ -909,6 +910,48 @@ void DivPlatformX1_010::poke(std::vector& wlist) { for (DivRegWrite& i: wlist) rWrite(i.addr,i.val); } +const void* DivPlatformX1_010::getSampleMem(int index) { + return index == 0 ? sampleMem : 0; +} + +size_t DivPlatformX1_010::getSampleMemCapacity(int index) { + return index == 0 ? 1048576 : 0; +} + +size_t DivPlatformX1_010::getSampleMemUsage(int index) { + return index == 0 ? sampleMemLen : 0; +} + +void DivPlatformX1_010::renderSamples() { + memset(sampleMem,0,getSampleMemCapacity()); + + size_t memPos=0; + for (int i=0; isong.sampleLen; i++) { + DivSample* s=parent->song.sample[i]; + int paddedLen=(s->length8+4095)&(~0xfff); + // fit sample bank size to 128KB for Seta 2 external bankswitching logic (not emulated yet!) + if (paddedLen>131072) { + paddedLen=131072; + } + if ((memPos&0xfe0000)!=((memPos+paddedLen)&0xfe0000)) { + memPos=(memPos+0x1ffff)&0xfe0000; + } + if (memPos>=getSampleMemCapacity()) { + logW("out of X1-010 memory for sample %d!",i); + break; + } + if (memPos+paddedLen>=getSampleMemCapacity()) { + memcpy(sampleMem+memPos,s->data8,getSampleMemCapacity()-memPos); + logW("out of X1-010 memory for sample %d!",i); + } else { + memcpy(sampleMem+memPos,s->data8,paddedLen); + } + s->offX1_010=memPos; + memPos+=paddedLen; + } + sampleMemLen=memPos+256; +} + int DivPlatformX1_010::init(DivEngine* p, int channels, int sugRate, unsigned int flags) { parent=p; dumpWrites=false; @@ -919,7 +962,9 @@ int DivPlatformX1_010::init(DivEngine* p, int channels, int sugRate, unsigned in oscBuf[i]=new DivDispatchOscBuffer; } setFlags(flags); - intf.parent=parent; + sampleMem=new unsigned char[getSampleMemCapacity()]; + sampleMemLen=0; + intf.memory=sampleMem; x1_010=new x1_010_core(intf); x1_010->reset(); reset(); @@ -931,6 +976,7 @@ void DivPlatformX1_010::quit() { delete oscBuf[i]; } delete x1_010; + delete[] sampleMem; } DivPlatformX1_010::~DivPlatformX1_010() { diff --git a/src/engine/platform/x1_010.h b/src/engine/platform/x1_010.h index 11de4bf4a..939280ef9 100644 --- a/src/engine/platform/x1_010.h +++ b/src/engine/platform/x1_010.h @@ -28,13 +28,13 @@ class DivX1_010Interface: public x1_010_mem_intf { public: - DivEngine* parent; + unsigned char* memory; int sampleBank; virtual u8 read_byte(u32 address) override { - if (parent->x1_010Mem==NULL) return 0; - return parent->x1_010Mem[address & 0xfffff]; + if (memory==NULL) return 0; + return memory[address & 0xfffff]; } - DivX1_010Interface(): parent(NULL), sampleBank(0) {} + DivX1_010Interface(): memory(NULL), sampleBank(0) {} }; class DivPlatformX1_010: public DivDispatch { @@ -115,6 +115,8 @@ class DivPlatformX1_010: public DivDispatch { DivDispatchOscBuffer* oscBuf[16]; bool isMuted[16]; bool stereo=false; + unsigned char* sampleMem; + size_t sampleMemLen; unsigned char sampleBank; DivX1_010Interface intf; x1_010_core* x1_010; @@ -141,6 +143,10 @@ class DivPlatformX1_010: public DivDispatch { void notifyInsDeletion(void* ins); void poke(unsigned int addr, unsigned short val); void poke(std::vector& wlist); + const void* getSampleMem(int index = 0); + size_t getSampleMemCapacity(int index = 0); + size_t getSampleMemUsage(int index = 0); + void renderSamples(); const char** getRegisterSheet(); const char* getEffectName(unsigned char effect); int init(DivEngine* parent, int channels, int sugRate, unsigned int flags); diff --git a/src/engine/platform/ym2610.cpp b/src/engine/platform/ym2610.cpp index 5154acc62..3711515aa 100644 --- a/src/engine/platform/ym2610.cpp +++ b/src/engine/platform/ym2610.cpp @@ -20,6 +20,7 @@ #include "ym2610.h" #include "sound/ymfm/ymfm.h" #include "../engine.h" +#include "../../ta-log.h" #include #include @@ -245,6 +246,85 @@ const char* regCheatSheetYM2610[]={ NULL }; +const void* DivPlatformYM2610Base::getSampleMem(int index) { + return index == 0 ? adpcmAMem : index == 1 ? adpcmBMem : NULL; +} + +size_t DivPlatformYM2610Base::getSampleMemCapacity(int index) { + return index == 0 ? 16777216 : index == 1 ? 16777216 : 0; +} + +size_t DivPlatformYM2610Base::getSampleMemUsage(int index) { + return index == 0 ? adpcmAMemLen : index == 1 ? adpcmBMemLen : 0; +} + +void DivPlatformYM2610Base::renderSamples() { + memset(adpcmAMem,0,getSampleMemCapacity(0)); + + size_t memPos=0; + for (int i=0; isong.sampleLen; i++) { + DivSample* s=parent->song.sample[i]; + int paddedLen=(s->lengthA+255)&(~0xff); + if ((memPos&0xf00000)!=((memPos+paddedLen)&0xf00000)) { + memPos=(memPos+0xfffff)&0xf00000; + } + if (memPos>=getSampleMemCapacity(0)) { + logW("out of ADPCM-A memory for sample %d!",i); + break; + } + if (memPos+paddedLen>=getSampleMemCapacity(0)) { + memcpy(adpcmAMem+memPos,s->dataA,getSampleMemCapacity(0)-memPos); + logW("out of ADPCM-A memory for sample %d!",i); + } else { + memcpy(adpcmAMem+memPos,s->dataA,paddedLen); + } + s->offA=memPos; + memPos+=paddedLen; + } + adpcmAMemLen=memPos+256; + + memset(adpcmBMem,0,getSampleMemCapacity(1)); + + memPos=0; + for (int i=0; isong.sampleLen; i++) { + DivSample* s=parent->song.sample[i]; + int paddedLen=(s->lengthB+255)&(~0xff); + if ((memPos&0xf00000)!=((memPos+paddedLen)&0xf00000)) { + memPos=(memPos+0xfffff)&0xf00000; + } + if (memPos>=getSampleMemCapacity(1)) { + logW("out of ADPCM-B memory for sample %d!",i); + break; + } + if (memPos+paddedLen>=getSampleMemCapacity(1)) { + memcpy(adpcmBMem+memPos,s->dataB,getSampleMemCapacity(1)-memPos); + logW("out of ADPCM-B memory for sample %d!",i); + } else { + memcpy(adpcmBMem+memPos,s->dataB,paddedLen); + } + s->offB=memPos; + memPos+=paddedLen; + } + adpcmBMemLen=memPos+256; +} + +int DivPlatformYM2610Base::init(DivEngine* p, int channels, int sugRate, unsigned int flags) { + parent=p; + adpcmAMem=new unsigned char[getSampleMemCapacity(0)]; + adpcmAMemLen=0; + adpcmBMem=new unsigned char[getSampleMemCapacity(1)]; + adpcmBMemLen=0; + iface.adpcmAMem=adpcmAMem; + iface.adpcmBMem=adpcmBMem; + iface.sampleBank=0; + return 0; +} + +void DivPlatformYM2610Base::quit() { + delete[] adpcmAMem; + delete[] adpcmBMem; +} + const char** DivPlatformYM2610::getRegisterSheet() { return regCheatSheetYM2610; } @@ -1185,7 +1265,7 @@ void DivPlatformYM2610::setSkipRegisterWrites(bool value) { } int DivPlatformYM2610::init(DivEngine* p, int channels, int sugRate, unsigned int flags) { - parent=p; + DivPlatformYM2610Base::init(p, channels, sugRate, flags); dumpWrites=false; skipRegisterWrites=false; for (int i=0; i<14; i++) { @@ -1197,8 +1277,6 @@ int DivPlatformYM2610::init(DivEngine* p, int channels, int sugRate, unsigned in for (int i=0; i<14; i++) { oscBuf[i]->rate=rate; } - iface.parent=parent; - iface.sampleBank=0; fm=new ymfm::ym2610(iface); // YM2149, 2MHz ay=new DivPlatformAY8910; @@ -1215,6 +1293,7 @@ void DivPlatformYM2610::quit() { ay->quit(); delete ay; delete fm; + DivPlatformYM2610Base::quit(); } DivPlatformYM2610::~DivPlatformYM2610() { diff --git a/src/engine/platform/ym2610.h b/src/engine/platform/ym2610.h index c46bc3c43..45ecb335a 100644 --- a/src/engine/platform/ym2610.h +++ b/src/engine/platform/ym2610.h @@ -27,14 +27,32 @@ class DivYM2610Interface: public ymfm::ymfm_interface { public: - DivEngine* parent; + unsigned char* adpcmAMem; + unsigned char* adpcmBMem; int sampleBank; uint8_t ymfm_external_read(ymfm::access_class type, uint32_t address); void ymfm_external_write(ymfm::access_class type, uint32_t address, uint8_t data); - DivYM2610Interface(): parent(NULL), sampleBank(0) {} + DivYM2610Interface(): adpcmAMem(NULL), adpcmBMem(NULL), sampleBank(0) {} }; -class DivPlatformYM2610: public DivDispatch { +class DivPlatformYM2610Base: public DivDispatch { + protected: + unsigned char* adpcmAMem; + size_t adpcmAMemLen; + unsigned char* adpcmBMem; + size_t adpcmBMemLen; + DivYM2610Interface iface; + + public: + const void* getSampleMem(int index); + size_t getSampleMemCapacity(int index); + size_t getSampleMemUsage(int index); + void renderSamples(); + int init(DivEngine* parent, int channels, int sugRate, unsigned int flags); + void quit(); +}; + +class DivPlatformYM2610: public DivPlatformYM2610Base { protected: const unsigned short chanOffs[4]={ 0x01, 0x02, 0x101, 0x102 @@ -93,7 +111,6 @@ class DivPlatformYM2610: public DivDispatch { std::queue writes; ymfm::ym2610* fm; ymfm::ym2610::output_data fmout; - DivYM2610Interface iface; DivPlatformAY8910* ay; unsigned char regPool[512]; diff --git a/src/engine/platform/ym2610Interface.cpp b/src/engine/platform/ym2610Interface.cpp index 9154b40e7..d442ce347 100644 --- a/src/engine/platform/ym2610Interface.cpp +++ b/src/engine/platform/ym2610Interface.cpp @@ -24,13 +24,11 @@ uint8_t DivYM2610Interface::ymfm_external_read(ymfm::access_class type, uint32_t address) { switch (type) { case ymfm::ACCESS_ADPCM_A: - if (parent->adpcmAMem==NULL) return 0; - if ((address&0xffffff)>=parent->adpcmAMemLen) return 0; - return parent->adpcmAMem[address&0xffffff]; + if (adpcmAMem==NULL) return 0; + return adpcmAMem[address&0xffffff]; case ymfm::ACCESS_ADPCM_B: - if (parent->adpcmBMem==NULL) return 0; - if ((address&0xffffff)>=parent->adpcmBMemLen) return 0; - return parent->adpcmBMem[address&0xffffff]; + if (adpcmBMem==NULL) return 0; + return adpcmBMem[address&0xffffff]; default: return 0; } diff --git a/src/engine/platform/ym2610b.cpp b/src/engine/platform/ym2610b.cpp index a0f1a6e3f..9c126c2b3 100644 --- a/src/engine/platform/ym2610b.cpp +++ b/src/engine/platform/ym2610b.cpp @@ -1243,7 +1243,7 @@ void DivPlatformYM2610B::setSkipRegisterWrites(bool value) { } int DivPlatformYM2610B::init(DivEngine* p, int channels, int sugRate, unsigned int flags) { - parent=p; + DivPlatformYM2610Base::init(p, channels, sugRate, flags); dumpWrites=false; skipRegisterWrites=false; for (int i=0; i<16; i++) { @@ -1255,8 +1255,6 @@ int DivPlatformYM2610B::init(DivEngine* p, int channels, int sugRate, unsigned i for (int i=0; i<16; i++) { oscBuf[i]->rate=rate; } - iface.parent=parent; - iface.sampleBank=0; fm=new ymfm::ym2610b(iface); // YM2149, 2MHz ay=new DivPlatformAY8910; @@ -1273,6 +1271,7 @@ void DivPlatformYM2610B::quit() { ay->quit(); delete ay; delete fm; + DivPlatformYM2610Base::quit(); } DivPlatformYM2610B::~DivPlatformYM2610B() { diff --git a/src/engine/platform/ym2610b.h b/src/engine/platform/ym2610b.h index df0a83233..d1af3069a 100644 --- a/src/engine/platform/ym2610b.h +++ b/src/engine/platform/ym2610b.h @@ -26,7 +26,7 @@ #include "ym2610.h" -class DivPlatformYM2610B: public DivDispatch { +class DivPlatformYM2610B: public DivPlatformYM2610Base { protected: const unsigned short chanOffs[6]={ 0x00, 0x01, 0x02, 0x100, 0x101, 0x102 @@ -85,7 +85,6 @@ class DivPlatformYM2610B: public DivDispatch { std::queue writes; ymfm::ym2610b* fm; ymfm::ym2610b::output_data fmout; - DivYM2610Interface iface; unsigned char regPool[512]; unsigned char lastBusy; diff --git a/src/engine/vgmOps.cpp b/src/engine/vgmOps.cpp index 7817c060e..53800a525 100644 --- a/src/engine/vgmOps.cpp +++ b/src/engine/vgmOps.cpp @@ -713,10 +713,10 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version) { bool writeDACSamples=false; bool writeNESSamples=false; bool writePCESamples=false; - int writeADPCM=0; + DivDispatch* writeADPCM[2]={NULL,NULL}; int writeSegaPCM=0; - int writeX1010=0; - int writeQSound=0; + DivDispatch* writeX1010[2]={NULL,NULL}; + DivDispatch* writeQSound[2]={NULL,NULL}; for (int i=0; ichipClock; willExport[i]=true; - writeX1010=1; + writeX1010[0]=disCont[i].dispatch; } else if (!(hasX1&0x40000000)) { isSecond[i]=true; willExport[i]=true; - writeX1010=2; + writeX1010[1]=disCont[i].dispatch; hasX1|=0x40000000; howManyChips++; } @@ -823,11 +823,11 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version) { if (!hasOPNB) { hasOPNB=disCont[i].dispatch->chipClock; willExport[i]=true; - writeADPCM=1; + writeADPCM[0]=disCont[i].dispatch; } else if (!(hasOPNB&0x40000000)) { isSecond[i]=true; willExport[i]=true; - writeADPCM=2; + writeADPCM[1]=disCont[i].dispatch; hasOPNB|=0x40000000; howManyChips++; } @@ -929,11 +929,11 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version) { // not be able to handle the 64kb sample bank trick hasQSound=disCont[i].dispatch->chipClock; willExport[i]=true; - writeQSound=1; + writeQSound[0]=disCont[i].dispatch; } else if (!(hasQSound&0x40000000)) { isSecond[i]=true; willExport[i]=false; - writeQSound=2; + writeQSound[1]=disCont[i].dispatch; addWarning("dual QSound is not supported by the VGM format"); } break; @@ -1249,56 +1249,55 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version) { delete[] pcmMem; } - if (adpcmAMemLen>0) { - for (int i=0; igetSampleMemUsage(0)>0) { w->writeC(0x67); w->writeC(0x66); w->writeC(0x82); - w->writeI((adpcmAMemLen+8)|(i*0x80000000)); - w->writeI(adpcmAMemLen); + w->writeI((writeADPCM[i]->getSampleMemUsage(0)+8)|(i*0x80000000)); + w->writeI(writeADPCM[i]->getSampleMemCapacity(0)); w->writeI(0); - w->write(adpcmAMem,adpcmAMemLen); + w->write(writeADPCM[i]->getSampleMem(0),writeADPCM[i]->getSampleMemUsage(0)); } } - if (adpcmBMemLen>0) { - for (int i=0; igetSampleMemUsage(1)>0) { w->writeC(0x67); w->writeC(0x66); w->writeC(0x83); - w->writeI((adpcmBMemLen+8)|(i*0x80000000)); - w->writeI(adpcmBMemLen); + w->writeI((writeADPCM[i]->getSampleMemUsage(1)+8)|(i*0x80000000)); + w->writeI(writeADPCM[i]->getSampleMemCapacity(1)); w->writeI(0); - w->write(adpcmBMem,adpcmBMemLen); + w->write(writeADPCM[i]->getSampleMem(1),writeADPCM[i]->getSampleMemUsage(1)); } } - if (qsoundMemLen>0) { - // always write a whole bank - unsigned int blockSize=(qsoundMemLen+0xffff)&(~0xffff); - if (blockSize > 0x1000000) { - blockSize = 0x1000000; - } - for (int i=0; igetSampleMemUsage()>0) { + unsigned int blockSize=(writeQSound[i]->getSampleMemUsage()+0xffff)&(~0xffff); + if (blockSize > 0x1000000) { + blockSize = 0x1000000; + } w->writeC(0x67); w->writeC(0x66); w->writeC(0x8F); w->writeI((blockSize+8)|(i*0x80000000)); - w->writeI(0x1000000); + w->writeI(writeQSound[i]->getSampleMemCapacity()); w->writeI(0); - w->write(qsoundMem,blockSize); + w->write(writeQSound[i]->getSampleMem(),blockSize); } } - if (x1_010MemLen>0) { - for (int i=0; igetSampleMemUsage()>0) { w->writeC(0x67); w->writeC(0x66); w->writeC(0x91); - w->writeI((x1_010MemLen+8)|(i*0x80000000)); - w->writeI(x1_010MemLen); + w->writeI((writeX1010[i]->getSampleMemUsage()+8)|(i*0x80000000)); + w->writeI(writeX1010[i]->getSampleMemCapacity()); w->writeI(0); - w->write(x1_010Mem,x1_010MemLen); + w->write(writeX1010[i]->getSampleMem(),writeX1010[i]->getSampleMemUsage()); } } diff --git a/src/gui/stats.cpp b/src/gui/stats.cpp index 274be2eda..35d6cfc49 100644 --- a/src/gui/stats.cpp +++ b/src/gui/stats.cpp @@ -28,22 +28,17 @@ void FurnaceGUI::drawStats() { } if (!statsOpen) return; if (ImGui::Begin("Statistics",&statsOpen)) { - String adpcmAUsage=fmt::sprintf("%d/16384KB",e->adpcmAMemLen/1024); - String adpcmBUsage=fmt::sprintf("%d/16384KB",e->adpcmBMemLen/1024); - String qsoundUsage=fmt::sprintf("%d/16384KB",e->qsoundMemLen/1024); - String x1_010Usage=fmt::sprintf("%d/1024KB",e->x1_010MemLen/1024); - ImGui::Text("ADPCM-A"); - ImGui::SameLine(); - ImGui::ProgressBar(((float)e->adpcmAMemLen)/16777216.0f,ImVec2(-FLT_MIN,0),adpcmAUsage.c_str()); - ImGui::Text("ADPCM-B"); - ImGui::SameLine(); - ImGui::ProgressBar(((float)e->adpcmBMemLen)/16777216.0f,ImVec2(-FLT_MIN,0),adpcmBUsage.c_str()); - ImGui::Text("QSound"); - ImGui::SameLine(); - ImGui::ProgressBar(((float)e->qsoundMemLen)/16777216.0f,ImVec2(-FLT_MIN,0),qsoundUsage.c_str()); - ImGui::Text("X1-010"); - ImGui::SameLine(); - ImGui::ProgressBar(((float)e->x1_010MemLen)/1048576.0f,ImVec2(-FLT_MIN,0),x1_010Usage.c_str()); + for (int i=0; isong.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=fmt::sprintf("%d/%dKB",usage/1024,capacity/1024); + 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(); From 269f07f5976ba580ca3ee944b4b796206e2ac72c Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 1 May 2022 17:26:56 -0500 Subject: [PATCH 272/342] these are debug messages --- src/engine/engine.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 3a0a841d8..ad67a018f 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -2764,9 +2764,9 @@ bool DivEngine::init() { // set default system preset if (!hasLoadedSomething) { - logI("setting"); + logD("setting default preset"); std::vector preset=decodeSysDesc(getConfString("initialSys","")); - logI("preset size %ld",preset.size()); + logD("preset size %ld",preset.size()); if (preset.size()>0 && (preset.size()&3)==0) { preset.push_back(0); initSongWithDesc(preset.data()); From 2c9bad3b3d03cd229eac0be4224ffb4307b10e8e Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 1 May 2022 18:29:16 -0500 Subject: [PATCH 273/342] per-channel oscilloscope, part 9 work in progress! --- src/engine/dispatch.h | 4 ++- src/gui/chanOsc.cpp | 58 ++++++++++++++++++++++++++++++++++++++----- src/gui/gui.cpp | 6 +++++ src/gui/gui.h | 6 +++++ 4 files changed, 67 insertions(+), 7 deletions(-) diff --git a/src/engine/dispatch.h b/src/engine/dispatch.h index 7e5f998b9..6071fc47e 100644 --- a/src/engine/dispatch.h +++ b/src/engine/dispatch.h @@ -209,11 +209,13 @@ struct DivRegWrite { struct DivDispatchOscBuffer { unsigned int rate; unsigned short needle; + unsigned short readNeedle; short data[65536]; DivDispatchOscBuffer(): rate(65536), - needle(0) { + needle(0), + readNeedle(0) { memset(data,0,65536*sizeof(short)); } }; diff --git a/src/gui/chanOsc.cpp b/src/gui/chanOsc.cpp index f1248ffe9..dc392a884 100644 --- a/src/gui/chanOsc.cpp +++ b/src/gui/chanOsc.cpp @@ -30,15 +30,38 @@ void FurnaceGUI::drawChanOsc() { if (!chanOscOpen) return; ImGui::SetNextWindowSizeConstraints(ImVec2(64.0f*dpiScale,32.0f*dpiScale),ImVec2(scrW*dpiScale,scrH*dpiScale)); if (ImGui::Begin("Oscilloscope (per-channel)",&chanOscOpen)) { - if (ImGui::InputInt("Columns",&chanOscCols,1,1)) { - if (chanOscCols<1) chanOscCols=1; - if (chanOscCols>64) chanOscCols=64; + if (ImGui::BeginTable("ChanOscSettings",3)) { + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::Text("Columns"); + ImGui::SameLine(); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + if (ImGui::InputInt("##COSColumns",&chanOscCols,1,1)) { + if (chanOscCols<1) chanOscCols=1; + if (chanOscCols>64) chanOscCols=64; + } + + ImGui::TableNextColumn(); + ImGui::Text("Size (ms)"); + ImGui::SameLine(); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + if (ImGui::InputFloat("##COSWinSize",&chanOscWindowSize,1.0f,1.0f)) { + if (chanOscWindowSize<1.0f) chanOscWindowSize=1.0f; + if (chanOscWindowSize>50.0f) chanOscWindowSize=50.0f; + } + + ImGui::TableNextColumn(); + ImGui::Checkbox("Center waveform",&chanOscWaveCorr); + + ImGui::EndTable(); } + ImGui::PushStyleVar(ImGuiStyleVar_CellPadding,ImVec2(0.0f,0.0f)); float availY=ImGui::GetContentRegionAvail().y; if (ImGui::BeginTable("ChanOsc",chanOscCols,ImGuiTableFlags_Borders)) { std::vector oscBufs; + std::vector oscChans; int chans=e->getTotalChannelCount(); ImDrawList* dl=ImGui::GetWindowDrawList(); ImGuiWindow* window=ImGui::GetCurrentWindow(); @@ -49,7 +72,10 @@ void FurnaceGUI::drawChanOsc() { for (int i=0; igetOscBuffer(i); - if (buf!=NULL) oscBufs.push_back(buf); + if (buf!=NULL) { + oscBufs.push_back(buf); + oscChans.push_back(i); + } } int rows=(oscBufs.size()+(chanOscCols-1))/chanOscCols; @@ -58,13 +84,14 @@ void FurnaceGUI::drawChanOsc() { ImGui::TableNextColumn(); DivDispatchOscBuffer* buf=oscBufs[i]; + int ch=oscChans[i]; if (buf==NULL) { ImGui::Text("Error!"); } else { ImVec2 size=ImGui::GetContentRegionAvail(); size.y=availY/rows; - int displaySize=(buf->rate)/30; + int displaySize=(float)(buf->rate)*(chanOscWindowSize/1000.0f); ImVec2 minArea=window->DC.CursorPos; ImVec2 maxArea=ImVec2( @@ -85,7 +112,26 @@ void FurnaceGUI::drawChanOsc() { waveform[i]=ImLerp(inRect.Min,inRect.Max,ImVec2(x,0.5f)); } } else { - unsigned short needlePos=buf->needle-displaySize; + unsigned short needlePos=buf->needle; + if (chanOscWaveCorr) { + float cutoff=0.01f; + while (buf->readNeedle!=needlePos) { + //float old=chanOscLP1[ch]; + chanOscLP0[ch]+=cutoff*((float)buf->data[buf->readNeedle]-chanOscLP0[ch]); + chanOscLP1[ch]+=cutoff*(chanOscLP0[ch]-chanOscLP1[ch]); + if (chanOscLP1[ch]>=20) { + lastCorrPos[ch]=buf->readNeedle; + } + buf->readNeedle++; + } + needlePos=lastCorrPos[ch]; + /* + for (unsigned short i=0; idata[needlePos--]; + if (buf->data[needlePos]>old) break; + }*/ + } + needlePos-=displaySize; for (unsigned short i=0; i<512; i++) { float x=(float)i/512.0f; float y=(float)buf->data[(unsigned short)(needlePos+(i*displaySize/512))]/65536.0f; diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 6d63d0f50..80fad49e2 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -4000,6 +4000,8 @@ FurnaceGUI::FurnaceGUI(): oscZoom(0.5f), oscZoomSlider(false), chanOscCols(3), + chanOscWindowSize(20.0f), + chanOscWaveCorr(true), followLog(true), pianoOctaves(7), pianoOptions(false), @@ -4057,4 +4059,8 @@ FurnaceGUI::FurnaceGUI(): memset(patChanSlideY,0,sizeof(float)*(DIV_MAX_CHANS+1)); memset(lastIns,-1,sizeof(int)*DIV_MAX_CHANS); memset(oscValues,0,sizeof(float)*512); + + memset(chanOscLP0,0,sizeof(float)*DIV_MAX_CHANS); + memset(chanOscLP1,0,sizeof(float)*DIV_MAX_CHANS); + memset(lastCorrPos,0,sizeof(short)*DIV_MAX_CHANS); } diff --git a/src/gui/gui.h b/src/gui/gui.h index cf532eef5..520abdc41 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -1101,6 +1101,12 @@ class FurnaceGUI { // per-channel oscilloscope int chanOscCols; + float chanOscWindowSize; + bool chanOscWaveCorr; + float chanOscLP0[DIV_MAX_CHANS]; + float chanOscLP1[DIV_MAX_CHANS]; + unsigned short lastNeedlePos[DIV_MAX_CHANS]; + unsigned short lastCorrPos[DIV_MAX_CHANS]; // visualizer float keyHit[DIV_MAX_CHANS]; From 6c517292ddb7713e41ef2837ba8202a4ef995736 Mon Sep 17 00:00:00 2001 From: djmaximum17 <61179162+djmaximum17@users.noreply.github.com> Date: Tue, 19 Apr 2022 07:14:12 -0500 Subject: [PATCH 274/342] Add files via upload --- extern/NSFplay/legacy/2413tone.h | 22 + extern/NSFplay/legacy/281btone.h | 20 + extern/NSFplay/legacy/emu2149.c | 363 ++++++ extern/NSFplay/legacy/emu2149.h | 94 ++ extern/NSFplay/legacy/emu2212.c | 485 ++++++++ extern/NSFplay/legacy/emu2212.h | 77 ++ extern/NSFplay/legacy/emu2413.c | 1461 +++++++++++++++++++++++++ extern/NSFplay/legacy/emu2413.h | 247 +++++ extern/NSFplay/legacy/emutypes.h | 41 + extern/NSFplay/legacy/vrc7tone_ft35.h | 20 + extern/NSFplay/legacy/vrc7tone_ft36.h | 21 + extern/NSFplay/legacy/vrc7tone_kt1.h | 21 + extern/NSFplay/legacy/vrc7tone_kt2.h | 21 + extern/NSFplay/legacy/vrc7tone_mo.h | 20 + extern/NSFplay/legacy/vrc7tone_nuke.h | 21 + extern/NSFplay/legacy/vrc7tone_rw.h | 21 + extern/NSFplay/nes_apu.cpp | 400 +++++++ extern/NSFplay/nes_apu.h | 88 ++ extern/NSFplay/nes_dmc.cpp | 771 +++++++++++++ extern/NSFplay/nes_dmc.h | 129 +++ extern/NSFplay/nes_fds.cpp | 397 +++++++ extern/NSFplay/nes_fds.h | 83 ++ extern/NSFplay/nes_fme7.cpp | 186 ++++ extern/NSFplay/nes_fme7.h | 42 + extern/NSFplay/nes_mmc5.cpp | 422 +++++++ extern/NSFplay/nes_mmc5.h | 74 ++ extern/NSFplay/nes_n106.cpp | 367 +++++++ extern/NSFplay/nes_n106.h | 74 ++ extern/NSFplay/nes_vrc6.cpp | 264 +++++ extern/NSFplay/nes_vrc6.h | 56 + extern/NSFplay/nes_vrc7.cpp | 189 ++++ extern/NSFplay/nes_vrc7.h | 53 + 32 files changed, 6550 insertions(+) create mode 100644 extern/NSFplay/legacy/2413tone.h create mode 100644 extern/NSFplay/legacy/281btone.h create mode 100644 extern/NSFplay/legacy/emu2149.c create mode 100644 extern/NSFplay/legacy/emu2149.h create mode 100644 extern/NSFplay/legacy/emu2212.c create mode 100644 extern/NSFplay/legacy/emu2212.h create mode 100644 extern/NSFplay/legacy/emu2413.c create mode 100644 extern/NSFplay/legacy/emu2413.h create mode 100644 extern/NSFplay/legacy/emutypes.h create mode 100644 extern/NSFplay/legacy/vrc7tone_ft35.h create mode 100644 extern/NSFplay/legacy/vrc7tone_ft36.h create mode 100644 extern/NSFplay/legacy/vrc7tone_kt1.h create mode 100644 extern/NSFplay/legacy/vrc7tone_kt2.h create mode 100644 extern/NSFplay/legacy/vrc7tone_mo.h create mode 100644 extern/NSFplay/legacy/vrc7tone_nuke.h create mode 100644 extern/NSFplay/legacy/vrc7tone_rw.h create mode 100644 extern/NSFplay/nes_apu.cpp create mode 100644 extern/NSFplay/nes_apu.h create mode 100644 extern/NSFplay/nes_dmc.cpp create mode 100644 extern/NSFplay/nes_dmc.h create mode 100644 extern/NSFplay/nes_fds.cpp create mode 100644 extern/NSFplay/nes_fds.h create mode 100644 extern/NSFplay/nes_fme7.cpp create mode 100644 extern/NSFplay/nes_fme7.h create mode 100644 extern/NSFplay/nes_mmc5.cpp create mode 100644 extern/NSFplay/nes_mmc5.h create mode 100644 extern/NSFplay/nes_n106.cpp create mode 100644 extern/NSFplay/nes_n106.h create mode 100644 extern/NSFplay/nes_vrc6.cpp create mode 100644 extern/NSFplay/nes_vrc6.h create mode 100644 extern/NSFplay/nes_vrc7.cpp create mode 100644 extern/NSFplay/nes_vrc7.h diff --git a/extern/NSFplay/legacy/2413tone.h b/extern/NSFplay/legacy/2413tone.h new file mode 100644 index 000000000..703979584 --- /dev/null +++ b/extern/NSFplay/legacy/2413tone.h @@ -0,0 +1,22 @@ +/* YM2413 tone by Mitsutaka Okazaki, 2020 */ +/* https://github.com/digital-sound-antiques/emu2413/blob/d2b9c8dfc5e84b7f8a535fdfee0149ac7bd84ca2/emu2413.c#L34 + */ +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0: Original +0x71, 0x61, 0x1e, 0x17, 0xd0, 0x78, 0x00, 0x17, // 1: Violin +0x13, 0x41, 0x1a, 0x0d, 0xd8, 0xf7, 0x23, 0x13, // 2: Guitar +0x13, 0x01, 0x99, 0x00, 0xf2, 0xc4, 0x21, 0x23, // 3: Piano +0x11, 0x61, 0x0e, 0x07, 0x8d, 0x64, 0x70, 0x27, // 4: Flute +0x32, 0x21, 0x1e, 0x06, 0xe1, 0x76, 0x01, 0x28, // 5: Clarinet +0x31, 0x22, 0x16, 0x05, 0xe0, 0x71, 0x00, 0x18, // 6: Oboe +0x21, 0x61, 0x1d, 0x07, 0x82, 0x81, 0x11, 0x07, // 7: Trumpet +0x33, 0x21, 0x2d, 0x13, 0xb0, 0x70, 0x00, 0x07, // 8: Organ +0x61, 0x61, 0x1b, 0x06, 0x64, 0x65, 0x10, 0x17, // 9: Horn +0x41, 0x61, 0x0b, 0x18, 0x85, 0xf0, 0x81, 0x07, // A: Synthesizer +0x33, 0x01, 0x83, 0x11, 0xea, 0xef, 0x10, 0x04, // B: Harpsichord +0x17, 0xc1, 0x24, 0x07, 0xf8, 0xf8, 0x22, 0x12, // C: Vibraphone +0x61, 0x50, 0x0c, 0x05, 0xd2, 0xf5, 0x40, 0x42, // D: Synthsizer Bass +0x01, 0x01, 0x55, 0x03, 0xe9, 0x90, 0x03, 0x02, // E: Acoustic Bass +0x41, 0x41, 0x89, 0x03, 0xf1, 0xe4, 0xc0, 0x13, // F: Electric Guitar +0x01, 0x01, 0x18, 0x0f, 0xdf, 0xf8, 0x6a, 0x6d, // R: Bass Drum (from VRC7) +0x01, 0x01, 0x00, 0x00, 0xc8, 0xd8, 0xa7, 0x68, // R: High-Hat(M) / Snare Drum(C) (from VRC7) +0x05, 0x01, 0x00, 0x00, 0xf8, 0xaa, 0x59, 0x55, // R: Tom-tom(M) / Top Cymbal(C) (from VRC7) diff --git a/extern/NSFplay/legacy/281btone.h b/extern/NSFplay/legacy/281btone.h new file mode 100644 index 000000000..bb0068bab --- /dev/null +++ b/extern/NSFplay/legacy/281btone.h @@ -0,0 +1,20 @@ +/* YMF281B tone by Chabin (4/10/2004) */ +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x62,0x21,0x1a,0x07,0xf0,0x6f,0x00,0x16, +0x00,0x10,0x44,0x02,0xf6,0xf4,0x54,0x23, +0x03,0x01,0x97,0x04,0xf3,0xf3,0x13,0xf3, +0x01,0x61,0x0a,0x0f,0xfa,0x64,0x70,0x17, +0x22,0x21,0x1e,0x06,0xf0,0x76,0x00,0x28, +0x00,0x61,0x8a,0x0e,0xc0,0x61,0x00,0x07, +0x21,0x61,0x1b,0x07,0x84,0x80,0x17,0x17, +0x37,0x32,0xc9,0x01,0x66,0x64,0x40,0x28, +0x01,0x21,0x06,0x03,0xa5,0x71,0x51,0x07, +0x06,0x11,0x5e,0x07,0xf3,0xf2,0xf6,0x11, +0x00,0x20,0x18,0x06,0xf5,0xf3,0x20,0x26, +0x97,0x41,0x20,0x07,0xff,0xf4,0x22,0x22, +0x65,0x61,0x15,0x00,0xf7,0xf3,0x16,0xf4, +0x01,0x31,0x0e,0x07,0xfa,0xf3,0xff,0xff, +0x48,0x61,0x09,0x07,0xf1,0x94,0xf0,0xf5, +0x07,0x21,0x14,0x00,0xee,0xf8,0xff,0xf8, +0x01,0x31,0x00,0x00,0xf8,0xf7,0xf8,0xf7, +0x25,0x11,0x00,0x00,0xf8,0xfa,0xf8,0x55, diff --git a/extern/NSFplay/legacy/emu2149.c b/extern/NSFplay/legacy/emu2149.c new file mode 100644 index 000000000..fa5142f73 --- /dev/null +++ b/extern/NSFplay/legacy/emu2149.c @@ -0,0 +1,363 @@ +/**************************************************************************** + + emu2149.c -- YM2149/AY-3-8910 emulator by Mitsutaka Okazaki 2001 + + 2001 04-28 : Version 1.00beta -- 1st Beta Release. + 2001 08-14 : Version 1.10 + 2001 10-03 : Version 1.11 -- Added PSG_set_quality(). + 2002 03-02 : Version 1.12 -- Removed PSG_init & PSG_close. + 2002 10-13 : Version 1.14 -- Fixed the envelope unit. + 2003 09-19 : Version 1.15 -- Added PSG_setMask and PSG_toggleMask + 2004 01-11 : Version 1.16 -- Fixed an envelope problem where the envelope + frequency register is written before key-on. + + References: + psg.vhd -- 2000 written by Kazuhiro Tsujikawa. + s_fme7.c -- 1999,2000 written by Mamiya (NEZplug). + ay8910.c -- 1998-2001 Author unknown (MAME). + MSX-Datapack -- 1991 ASCII Corp. + AY-3-8910 data sheet + +*****************************************************************************/ +#include +#include +#include +#include "emu2149.h" + +static e_uint32 voltbl[2][32] = { + {0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x03, 0x04, 0x05, 0x06, 0x07, 0x09, + 0x0B, 0x0D, 0x0F, 0x12, + 0x16, 0x1A, 0x1F, 0x25, 0x2D, 0x35, 0x3F, 0x4C, 0x5A, 0x6A, 0x7F, 0x97, + 0xB4, 0xD6, 0xEB, 0xFF}, + {0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x03, 0x05, 0x05, 0x07, 0x07, + 0x0B, 0x0B, 0x0F, 0x0F, + 0x16, 0x16, 0x1F, 0x1F, 0x2D, 0x2D, 0x3F, 0x3F, 0x5A, 0x5A, 0x7F, 0x7F, + 0xB4, 0xB4, 0xFF, 0xFF} +}; + +#define GETA_BITS 24 + +static void +internal_refresh (PSG * psg) +{ + if (psg->quality) + { + psg->base_incr = 1 << GETA_BITS; + psg->realstep = (e_uint32) ((1 << 31) / psg->rate); + psg->psgstep = (e_uint32) ((1 << 31) / (psg->clk / 16)); + psg->psgtime = 0; + } + else + { + psg->base_incr = + (e_uint32) ((double) psg->clk * (1 << GETA_BITS) / (16 * psg->rate)); + } +} + +EMU2149_API void +PSG_set_rate (PSG * psg, e_uint32 r) +{ + psg->rate = r ? r : 44100; + internal_refresh (psg); +} + +EMU2149_API void +PSG_set_quality (PSG * psg, e_uint32 q) +{ + psg->quality = q; + internal_refresh (psg); +} + +EMU2149_API PSG * +PSG_new (e_uint32 c, e_uint32 r) +{ + PSG *psg; + + psg = (PSG *) malloc (sizeof (PSG)); + if (psg == NULL) + return NULL; + + PSG_setVolumeMode (psg, EMU2149_VOL_DEFAULT); + psg->clk = c; + psg->rate = r ? r : 44100; + PSG_set_quality (psg, 0); + + return psg; +} + +EMU2149_API void +PSG_setVolumeMode (PSG * psg, int type) +{ + switch (type) + { + case 1: + psg->voltbl = voltbl[EMU2149_VOL_YM2149]; + break; + case 2: + psg->voltbl = voltbl[EMU2149_VOL_AY_3_8910]; + break; + default: + psg->voltbl = voltbl[EMU2149_VOL_DEFAULT]; + break; + } +} + +EMU2149_API e_uint32 +PSG_setMask (PSG *psg, e_uint32 mask) +{ + e_uint32 ret = 0; + if(psg) + { + ret = psg->mask; + psg->mask = mask; + } + return ret; +} + +EMU2149_API e_uint32 +PSG_toggleMask (PSG *psg, e_uint32 mask) +{ + e_uint32 ret = 0; + if(psg) + { + ret = psg->mask; + psg->mask ^= mask; + } + return ret; +} + +EMU2149_API void +PSG_reset (PSG * psg) +{ + int i; + + psg->base_count = 0; + + for (i = 0; i < 3; i++) + { + psg->cout[i] = 0; + psg->count[i] = 0x1000; + psg->freq[i] = 0; + psg->edge[i] = 0; + psg->volume[i] = 0; + } + + psg->mask = 0; + + for (i = 0; i < 16; i++) + psg->reg[i] = 0; + psg->adr = 0; + + psg->noise_seed = 0xffff; + psg->noise_count = 0x40; + psg->noise_freq = 0; + + psg->env_volume = 0; + psg->env_ptr = 0; + psg->env_freq = 0; + psg->env_count = 0; + psg->env_pause = 1; + + psg->out = 0; +} + +EMU2149_API void +PSG_delete (PSG * psg) +{ + free (psg); +} + +EMU2149_API e_uint8 +PSG_readIO (PSG * psg) +{ + return (e_uint8) (psg->reg[psg->adr]); +} + +EMU2149_API e_uint8 +PSG_readReg (PSG * psg, e_uint32 reg) +{ + return (e_uint8) (psg->reg[reg & 0x1f]); + +} + +EMU2149_API void +PSG_writeIO (PSG * psg, e_uint32 adr, e_uint32 val) +{ + if (adr & 1) + PSG_writeReg (psg, psg->adr, val); + else + psg->adr = val & 0x1f; +} + +INLINE static e_int16 +calc (PSG * psg) +{ + + int i, noise; + e_uint32 incr; + e_int32 mix = 0; + + psg->base_count += psg->base_incr; + incr = (psg->base_count >> GETA_BITS); + psg->base_count &= (1 << GETA_BITS) - 1; + + /* Envelope */ + psg->env_count += incr; + while (psg->env_count>=0x10000 && psg->env_freq!=0) + { + if (!psg->env_pause) + { + if(psg->env_face) + psg->env_ptr = (psg->env_ptr + 1) & 0x3f ; + else + psg->env_ptr = (psg->env_ptr + 0x3f) & 0x3f; + } + + if (psg->env_ptr & 0x20) /* if carry or borrow */ + { + if (psg->env_continue) + { + if (psg->env_alternate^psg->env_hold) psg->env_face ^= 1; + if (psg->env_hold) psg->env_pause = 1; + psg->env_ptr = psg->env_face?0:0x1f; + } + else + { + psg->env_pause = 1; + psg->env_ptr = 0; + } + } + + psg->env_count -= psg->env_freq; + } + + /* Noise */ + psg->noise_count += incr; + if (psg->noise_count & 0x40) + { + if (psg->noise_seed & 1) + psg->noise_seed ^= 0x24000; + psg->noise_seed >>= 1; + psg->noise_count -= psg->noise_freq; + } + noise = psg->noise_seed & 1; + + /* Tone */ + for (i = 0; i < 3; i++) + { + psg->count[i] += incr; + if (psg->count[i] & 0x1000) + { + if (psg->freq[i] > 1) + { + psg->edge[i] = !psg->edge[i]; + psg->count[i] -= psg->freq[i]; + } + else + { + psg->edge[i] = 1; + } + } + + psg->cout[i] = 0; // maintaining cout for stereo mix + + if (psg->mask&PSG_MASK_CH(i)) + continue; + + if ((psg->tmask[i] || psg->edge[i]) && (psg->nmask[i] || noise)) + { + if (!(psg->volume[i] & 32)) + psg->cout[i] = psg->voltbl[psg->volume[i] & 31]; + else + psg->cout[i] = psg->voltbl[psg->env_ptr]; + + mix += psg->cout[i]; + } + } + + return (e_int16) mix; +} + +EMU2149_API e_int16 +PSG_calc (PSG * psg) +{ + if (!psg->quality) + return (e_int16) (calc (psg) << 4); + + /* Simple rate converter */ + while (psg->realstep > psg->psgtime) + { + psg->psgtime += psg->psgstep; + psg->out += calc (psg); + psg->out >>= 1; + } + + psg->psgtime = psg->psgtime - psg->realstep; + + return (e_int16) (psg->out << 4); +} + +EMU2149_API void +PSG_writeReg (PSG * psg, e_uint32 reg, e_uint32 val) +{ + int c; + + if (reg > 15) return; + + psg->reg[reg] = (e_uint8) (val & 0xff); + switch (reg) + { + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + c = reg >> 1; + psg->freq[c] = ((psg->reg[c * 2 + 1] & 15) << 8) + psg->reg[c * 2]; + break; + + case 6: + psg->noise_freq = (val == 0) ? 1 : ((val & 31) << 1); + break; + + case 7: + psg->tmask[0] = (val & 1); + psg->tmask[1] = (val & 2); + psg->tmask[2] = (val & 4); + psg->nmask[0] = (val & 8); + psg->nmask[1] = (val & 16); + psg->nmask[2] = (val & 32); + break; + + case 8: + case 9: + case 10: + psg->volume[reg - 8] = val << 1; + + break; + + case 11: + case 12: + psg->env_freq = (psg->reg[12] << 8) + psg->reg[11]; + break; + + case 13: + psg->env_continue = (val >> 3) & 1; + psg->env_attack = (val >> 2) & 1; + psg->env_alternate = (val >> 1) & 1; + psg->env_hold = val & 1; + psg->env_face = psg->env_attack; + psg->env_pause = 0; + psg->env_count = 0x10000 - psg->env_freq; + psg->env_ptr = psg->env_face?0:0x1f; + break; + + case 14: + case 15: + default: + break; + } + + return; +} \ No newline at end of file diff --git a/extern/NSFplay/legacy/emu2149.h b/extern/NSFplay/legacy/emu2149.h new file mode 100644 index 000000000..a2fba126b --- /dev/null +++ b/extern/NSFplay/legacy/emu2149.h @@ -0,0 +1,94 @@ +/* emu2149.h */ +#ifndef _EMU2149_H_ +#define _EMU2149_H_ +#include "emutypes.h" + +#ifdef EMU2149_DLL_EXPORTS +#define EMU2149_API __declspec(dllexport) +#elif EMU2149_DLL_IMPORTS +#define EMU2149_API __declspec(dllimport) +#else +#define EMU2149_API +#endif + +#define EMU2149_VOL_DEFAULT 1 +#define EMU2149_VOL_YM2149 0 +#define EMU2149_VOL_AY_3_8910 1 + +#define PSG_MASK_CH(x) (1<<(x)) + +#ifdef __cplusplus +extern "C" +{ +#endif + + typedef struct __PSG + { + + /* Volume Table */ + e_uint32 *voltbl; + + e_uint8 reg[0x20]; + e_int32 out; + e_int32 cout[3]; + + e_uint32 clk, rate, base_incr, quality; + + e_uint32 count[3]; + e_uint32 volume[3]; + e_uint32 freq[3]; + e_uint32 edge[3]; + e_uint32 tmask[3]; + e_uint32 nmask[3]; + e_uint32 mask; + + e_uint32 base_count; + + e_uint32 env_volume; + e_uint32 env_ptr; + e_uint32 env_face; + + e_uint32 env_continue; + e_uint32 env_attack; + e_uint32 env_alternate; + e_uint32 env_hold; + e_uint32 env_pause; + e_uint32 env_reset; + + e_uint32 env_freq; + e_uint32 env_count; + + e_uint32 noise_seed; + e_uint32 noise_count; + e_uint32 noise_freq; + + /* rate converter */ + e_uint32 realstep; + e_uint32 psgtime; + e_uint32 psgstep; + + /* I/O Ctrl */ + e_uint32 adr; + + } + PSG; + + EMU2149_API void PSG_set_quality (PSG * psg, e_uint32 q); + EMU2149_API void PSG_set_rate (PSG * psg, e_uint32 r); + EMU2149_API PSG *PSG_new (e_uint32 clk, e_uint32 rate); + EMU2149_API void PSG_reset (PSG *); + EMU2149_API void PSG_delete (PSG *); + EMU2149_API void PSG_writeReg (PSG *, e_uint32 reg, e_uint32 val); + EMU2149_API void PSG_writeIO (PSG * psg, e_uint32 adr, e_uint32 val); + EMU2149_API e_uint8 PSG_readReg (PSG * psg, e_uint32 reg); + EMU2149_API e_uint8 PSG_readIO (PSG * psg); + EMU2149_API e_int16 PSG_calc (PSG *); + EMU2149_API void PSG_setVolumeMode (PSG * psg, int type); + EMU2149_API e_uint32 PSG_setMask (PSG *, e_uint32 mask); + EMU2149_API e_uint32 PSG_toggleMask (PSG *, e_uint32 mask); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/extern/NSFplay/legacy/emu2212.c b/extern/NSFplay/legacy/emu2212.c new file mode 100644 index 000000000..88d93382c --- /dev/null +++ b/extern/NSFplay/legacy/emu2212.c @@ -0,0 +1,485 @@ +/**************************************************************************** + + emu2212.c -- S.C.C. emulator by Mitsutaka Okazaki 2001 + + 2001 09-30 : Version 1.00 + 2001 10-03 : Version 1.01 -- Added SCC_set_quality(). + 2002 02-14 : Version 1.10 -- Added SCC_writeReg(), SCC_set_type(). + Fixed SCC_write(). + 2002 02-17 : Version 1.11 -- Fixed SCC_write(). + 2002 03-02 : Version 1.12 -- Removed SCC_init & SCC_close. + 2003 09-19 : Version 1.13 -- Added SCC_setMask() and SCC_toggleMask() + 2004 10-21 : Version 1.14 -- Fixed the problem where SCC+ is disabled. + + Registar map for SCC_writeReg() + + $00-1F : WaveTable CH.A + $20-3F : WaveTable CH.B + $40-5F : WaveTable CH.C + $60-7F : WaveTable CH.D&E(SCC), CH.D(SCC+) + $80-9F : WaveTable CH.E + + $C0 : CH.A Freq(L) + $C1 : CH.A Freq(H) + $C2 : CH.B Freq(L) + $C3 : CH.B Freq(H) + $C4 : CH.C Freq(L) + $C5 : CH.C Freq(H) + $C6 : CH.D Freq(L) + $C7 : CH.D Freq(H) + $C8 : CH.E Freq(L) + $C9 : CH.E Freq(H) + + $D0 : CH.A Volume + $D1 : CH.B Volume + $D2 : CH.C Volume + $D3 : CH.D Volume + $D4 : CH.E Volume + + $E0 : Bit0 = 0:SCC, 1:SCC+ + $E1 : CH mask + $E2 : Extra Flags + +*****************************************************************************/ +#include +#include +#include +#include "emu2212.h" + +#define GETA_BITS 22 + +static void +internal_refresh (SCC * scc) +{ + if (scc->quality) + { + scc->base_incr = 2 << GETA_BITS; + scc->realstep = (e_uint32) ((1 << 31) / scc->rate); + scc->sccstep = (e_uint32) ((1 << 31) / (scc->clk / 2)); + scc->scctime = 0; + } + else + { + scc->base_incr = (e_uint32) ((double) scc->clk * (1 << GETA_BITS) / scc->rate); + } +} + +EMU2212_API e_uint32 +SCC_setMask (SCC *scc, e_uint32 mask) +{ + e_uint32 ret = 0; + if(scc) + { + ret = scc->mask; + scc->mask = mask; + } + return ret; +} + +EMU2212_API e_uint32 +SCC_toggleMask (SCC *scc, e_uint32 mask) +{ + e_uint32 ret = 0; + if(scc) + { + ret = scc->mask; + scc->mask ^= mask; + } + return ret; +} + +EMU2212_API void +SCC_set_quality (SCC * scc, e_uint32 q) +{ + scc->quality = q; + internal_refresh (scc); +} + +EMU2212_API void +SCC_set_rate (SCC * scc, e_uint32 r) +{ + scc->rate = r ? r : 44100; + internal_refresh (scc); +} + +EMU2212_API SCC * +SCC_new (e_uint32 c, e_uint32 r) +{ + SCC *scc; + + scc = (SCC *) malloc (sizeof (SCC)); + if (scc == NULL) + return NULL; + memset(scc, 0, sizeof (SCC)); + + scc->clk = c; + scc->rate = r ? r : 44100; + SCC_set_quality (scc, 0); + scc->type = SCC_ENHANCED; + return scc; +} + +EMU2212_API void +SCC_reset (SCC * scc) +{ + int i, j; + + if (scc == NULL) + return; + + scc->mode = 0; + scc->active = 0; + scc->base_adr = 0x9000; + + for (i = 0; i < 5; i++) + { + for (j = 0; j < 5; j++) + scc->wave[i][j] = 0; + scc->count[i] = 0; + scc->freq[i] = 0; + scc->phase[i] = 0; + scc->volume[i] = 0; + scc->offset[i] = 0; + scc->rotate[i] = 0; + } + + memset(scc->reg,0,0x100-0xC0); + + scc->mask = 0; + + scc->ch_enable = 0xff; + scc->ch_enable_next = 0xff; + + scc->cycle_4bit = 0; + scc->cycle_8bit = 0; + scc->refresh = 0; + + scc->out = 0; + scc->prev = 0; + scc->next = 0; + + return; +} + +EMU2212_API void +SCC_delete (SCC * scc) +{ + if (scc != NULL) + free (scc); +} + +INLINE static e_int16 +calc (SCC * scc) +{ + int i; + e_int32 mix = 0; + + for (i = 0; i < 5; i++) + { + scc->count[i] = (scc->count[i] + scc->incr[i]); + + if (scc->count[i] & (1 << (GETA_BITS + 5))) + { + scc->count[i] &= ((1 << (GETA_BITS + 5)) - 1); + scc->offset[i] = (scc->offset[i] + 31) & scc->rotate[i]; + scc->ch_enable &= ~(1 << i); + scc->ch_enable |= scc->ch_enable_next & (1 << i); + } + + if (scc->ch_enable & (1 << i)) + { + scc->phase[i] = ((scc->count[i] >> (GETA_BITS)) + scc->offset[i]) & 0x1F; + if(!(scc->mask&SCC_MASK_CH(i))) + mix += ((((e_int8) (scc->wave[i][scc->phase[i]]) * (e_int8) scc->volume[i]))) >> 4; + } + } + + return (e_int16) (mix << 4); +} + +EMU2212_API e_int16 +SCC_calc (SCC * scc) +{ + if (!scc->quality) + return calc (scc); + + while (scc->realstep > scc->scctime) + { + scc->scctime += scc->sccstep; + scc->prev = scc->next; + scc->next = calc (scc); + } + + scc->scctime -= scc->realstep; + scc->out = (e_int16) (((double) scc->next * (scc->sccstep - scc->scctime) + (double) scc->prev * scc->scctime) / scc->sccstep); + + return (e_int16) (scc->out); +} + +EMU2212_API e_uint32 +SCC_readReg (SCC * scc, e_uint32 adr) +{ + if (adr < 0xA0) + return scc->wave[adr >> 5][adr & 0x1f]; + else if( 0xC0 < adr && adr < 0xF0 ) + return scc->reg[adr-0xC0]; + else + return 0; +} + +EMU2212_API void +SCC_writeReg (SCC * scc, e_uint32 adr, e_uint32 val) +{ + int ch; + e_uint32 freq; + + adr &= 0xFF; + + if (adr < 0xA0) + { + ch = (adr & 0xF0) >> 5; + if (!scc->rotate[ch]) + { + scc->wave[ch][adr & 0x1F] = (e_int8) val; + if (scc->mode == 0 && ch == 3) + scc->wave[4][adr & 0x1F] = (e_int8) val; + } + } + else if (0xC0 <= adr && adr <= 0xC9) + { + scc->reg[adr-0xC0] = val; + ch = (adr & 0x0F) >> 1; + if (adr & 1) + scc->freq[ch] = ((val & 0xF) << 8) | (scc->freq[ch] & 0xFF); + else + scc->freq[ch] = (scc->freq[ch] & 0xF00) | (val & 0xFF); + + if (scc->refresh) + scc->count[ch] = 0; + freq = scc->freq[ch]; + if (scc->cycle_8bit) + freq &= 0xFF; + if (scc->cycle_4bit) + freq >>= 8; + if (freq <= 8) + scc->incr[ch] = 0; + else + scc->incr[ch] = scc->base_incr / (freq + 1); + } + else if (0xD0 <= adr && adr <= 0xD4) + { + scc->reg[adr-0xC0] = val; + scc->volume[adr & 0x0F] = (e_uint8) (val & 0xF); + } + else if (adr == 0xE0) + { + scc->reg[adr-0xC0] = val; + scc->mode = (e_uint8) val & 1; + } + else if (adr == 0xE1) + { + scc->reg[adr-0xC0] = val; + scc->ch_enable_next = (e_uint8) val & 0x1F; + } + else if (adr == 0xE2) + { + scc->reg[adr-0xC0] = val; + scc->cycle_4bit = val & 1; + scc->cycle_8bit = val & 2; + scc->refresh = val & 32; + if (val & 64) + for (ch = 0; ch < 5; ch++) + scc->rotate[ch] = 0x1F; + else + for (ch = 0; ch < 5; ch++) + scc->rotate[ch] = 0; + if (val & 128) + scc->rotate[3] = scc->rotate[4] = 0x1F; + } + + return; +} + +INLINE static void +write_standard (SCC * scc, e_uint32 adr, e_uint32 val) +{ + adr &= 0xFF; + + if (adr < 0x80) /* wave */ + { + SCC_writeReg (scc, adr, val); + } + else if (adr < 0x8A) /* freq */ + { + SCC_writeReg (scc, adr + 0xC0 - 0x80, val); + } + else if (adr < 0x8F) /* volume */ + { + SCC_writeReg (scc, adr + 0xD0 - 0x8A, val); + } + else if (adr == 0x8F) /* ch enable */ + { + SCC_writeReg (scc, 0xE1, val); + } + else if (0xE0 <= adr) /* flags */ + { + SCC_writeReg (scc, 0xE2, val); + } +} + +INLINE static void +write_enhanced (SCC * scc, e_uint32 adr, e_uint32 val) +{ + adr &= 0xFF; + + if (adr < 0xA0) /* wave */ + { + SCC_writeReg (scc, adr, val); + } + else if (adr < 0xAA) /* freq */ + { + SCC_writeReg (scc, adr + 0xC0 - 0xA0, val); + } + else if (adr < 0xAF) /* volume */ + { + SCC_writeReg (scc, adr + 0xD0 - 0xAA, val); + } + else if (adr == 0xAF) /* ch enable */ + { + SCC_writeReg (scc, 0xE1, val); + } + else if (0xC0 <= adr && adr <= 0xDF) /* flags */ + { + SCC_writeReg (scc, 0xE2, val); + } +} + +INLINE static e_uint32 +read_enhanced (SCC * scc, e_uint32 adr) +{ + adr &= 0xFF; + if (adr < 0xA0) + return SCC_readReg (scc, adr); + else if (adr < 0xAA) + return SCC_readReg (scc, adr + 0xC0 - 0xA0); + else if (adr < 0xAF) + return SCC_readReg (scc, adr + 0xD0 - 0xAA); + else if (adr == 0xAF) + return SCC_readReg (scc, 0xE1); + else if (0xC0 <= adr && adr <= 0xDF) + return SCC_readReg (scc, 0xE2); + else + return 0; +} + +INLINE static e_uint32 +read_standard (SCC * scc, e_uint32 adr) +{ + adr &= 0xFF; + if(adr<0x80) + return SCC_readReg (scc, adr); + else if (0xA0<=adr&&adr<=0xBF) + return SCC_readReg (scc, 0x80+(adr&0x1F)); + else if (adr < 0x8A) + return SCC_readReg (scc, adr + 0xC0 - 0x80); + else if (adr < 0x8F) + return SCC_readReg (scc, adr + 0xD0 - 0x8A); + else if (adr == 0x8F) + return SCC_readReg (scc, 0xE1); + else if (0xE0 <= adr) + return SCC_readReg (scc, 0xE2); + else return 0; +} + +EMU2212_API e_uint32 +SCC_read (SCC * scc, e_uint32 adr) +{ + if( scc->type == SCC_ENHANCED && (adr&0xFFFE) == 0xBFFE ) + return (scc->base_adr>>8)&0x20; + + if( adr < scc->base_adr ) return 0; + adr -= scc->base_adr; + + if( adr == 0 ) + { + if(scc->mode) return 0x80; else return 0x3F; + } + + if(!scc->active||adr<0x800||0x8FFtype) + { + case SCC_STANDARD: + return read_standard (scc, adr); + break; + case SCC_ENHANCED: + if(!scc->mode) + return read_standard (scc, adr); + else + return read_enhanced (scc, adr); + break; + default: + break; + } + + return 0; +} + +EMU2212_API void +SCC_write (SCC * scc, e_uint32 adr, e_uint32 val) +{ + val = val & 0xFF; + + if( scc->type == SCC_ENHANCED && (adr&0xFFFE) == 0xBFFE ) + { + scc->base_adr = 0x9000 | ((val&0x20)<<8); + return; + } + + if( adr < scc->base_adr ) return; + adr -= scc->base_adr; + + if(adr == 0) + { + if( val == 0x3F ) + { + scc->mode = 0; + scc->active = 1; + } + else if( val&0x80 && scc->type == SCC_ENHANCED) + { + scc->mode = 1; + scc->active = 1; + } + else + { + scc->mode = 0; + scc->active = 0; + } + return; + } + + if(!scc->active||adr<0x800||0x8FFtype) + { + case SCC_STANDARD: + write_standard (scc, adr, val); + break; + case SCC_ENHANCED: + if(scc->mode) + write_enhanced (scc, adr, val); + else + write_standard (scc, adr, val); + default: + break; + } + + return; +} + +EMU2212_API void +SCC_set_type (SCC * scc, e_uint32 type) +{ + scc->type = type; +} diff --git a/extern/NSFplay/legacy/emu2212.h b/extern/NSFplay/legacy/emu2212.h new file mode 100644 index 000000000..b79e0c360 --- /dev/null +++ b/extern/NSFplay/legacy/emu2212.h @@ -0,0 +1,77 @@ +#ifndef _EMU2212_H_ +#define _EMU2212_H_ + +#ifdef EMU2212_DLL_EXPORTS + #define EMU2212_API __declspec(dllexport) +#elif defined(EMU2212_DLL_IMPORTS) + #define EMU2212_API __declspec(dllimport) +#else + #define EMU2212_API +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#include "emutypes.h" + +#define SCC_STANDARD 0 +#define SCC_ENHANCED 1 + +#define SCC_MASK_CH(x) (1<<(x)) + +typedef struct __SCC { + + e_uint32 clk, rate ,base_incr, quality ; + + e_int32 out, prev, next; + e_uint32 type ; + e_uint32 mode ; + e_uint32 active; + e_uint32 base_adr; + e_uint32 mask ; + + e_uint32 realstep ; + e_uint32 scctime ; + e_uint32 sccstep ; + + e_uint32 incr[5] ; + + e_int8 wave[5][32] ; + + e_uint32 count[5] ; + e_uint32 freq[5] ; + e_uint32 phase[5] ; + e_uint32 volume[5] ; + e_uint32 offset[5] ; + e_uint8 reg[0x100-0xC0]; + + int ch_enable ; + int ch_enable_next ; + + int cycle_4bit ; + int cycle_8bit ; + int refresh ; + int rotate[5] ; + +} SCC ; + + +EMU2212_API SCC *SCC_new(e_uint32 c, e_uint32 r) ; +EMU2212_API void SCC_reset(SCC *scc) ; +EMU2212_API void SCC_set_rate(SCC *scc, e_uint32 r); +EMU2212_API void SCC_set_quality(SCC *scc, e_uint32 q) ; +EMU2212_API void SCC_set_type(SCC *scc, e_uint32 type) ; +EMU2212_API void SCC_delete(SCC *scc) ; +EMU2212_API e_int16 SCC_calc(SCC *scc) ; +EMU2212_API void SCC_write(SCC *scc, e_uint32 adr, e_uint32 val) ; +EMU2212_API void SCC_writeReg(SCC *scc, e_uint32 adr, e_uint32 val) ; +EMU2212_API e_uint32 SCC_read(SCC *scc, e_uint32 adr) ; +EMU2212_API e_uint32 SCC_setMask(SCC *scc, e_uint32 adr) ; +EMU2212_API e_uint32 SCC_toggleMask(SCC *scc, e_uint32 adr) ; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/extern/NSFplay/legacy/emu2413.c b/extern/NSFplay/legacy/emu2413.c new file mode 100644 index 000000000..2ced98f86 --- /dev/null +++ b/extern/NSFplay/legacy/emu2413.c @@ -0,0 +1,1461 @@ +/** + * emu2413 v1.5.2 + * https://github.com/digital-sound-antiques/emu2413 + * Copyright (C) 2020 Mitsutaka Okazaki + * + * This source refers to the following documents. The author would like to thank all the authors who have + * contributed to the writing of them. + * - [YM2413 notes](http://www.smspower.org/Development/YM2413) by andete + * - ymf262.c by Jarek Burczynski + * - [VRC7 presets](https://siliconpr0n.org/archive/doku.php?id=vendor:yamaha:opl2#opll_vrc7_patch_format) by Nuke.YKT + * - YMF281B presets by Chabin + */ +#include "emu2413.h" +#include +#include +#include +#include + +#ifndef INLINE +#if defined(_MSC_VER) +#define INLINE __inline +#elif defined(__GNUC__) +#define INLINE __inline__ +#else +#define INLINE inline +#endif +#endif + +#define _PI_ 3.14159265358979323846264338327950288 + +#define OPLL_TONE_NUM 9 +static unsigned char default_inst[OPLL_TONE_NUM][(16 + 3) * 8] = { + { +#include "vrc7tone_nuke.h" + }, + { +#include "vrc7tone_rw.h" + }, + { +#include "vrc7tone_ft36.h" + }, + { +#include "vrc7tone_ft35.h" + }, + { +#include "vrc7tone_mo.h" + }, + { +#include "vrc7tone_kt2.h" + }, + { +#include "vrc7tone_kt1.h" + }, + { +#include "2413tone.h" + }, + { +#include "281btone.h" + }, +}; + +/* phase increment counter */ +#define DP_BITS 19 +#define DP_WIDTH (1 << DP_BITS) +#define DP_BASE_BITS (DP_BITS - PG_BITS) + +/* dynamic range of envelope output */ +#define EG_STEP 0.375 +#define EG_BITS 7 +#define EG_MUTE ((1 << EG_BITS) - 1) +#define EG_MAX (EG_MUTE - 3) + +/* dynamic range of total level */ +#define TL_STEP 0.75 +#define TL_BITS 6 + +/* dynamic range of sustine level */ +#define SL_STEP 3.0 +#define SL_BITS 4 + +/* damper speed before key-on. key-scale affects. */ +#define DAMPER_RATE 12 + +#define TL2EG(d) ((d) << 1) + +/* sine table */ +#define PG_BITS 10 /* 2^10 = 1024 length sine table */ +#define PG_WIDTH (1 << PG_BITS) + +/* clang-format off */ +/* exp_table[x] = round((exp2((double)x / 256.0) - 1) * 1024) */ +static uint16_t exp_table[256] = { +0, 3, 6, 8, 11, 14, 17, 20, 22, 25, 28, 31, 34, 37, 40, 42, +45, 48, 51, 54, 57, 60, 63, 66, 69, 72, 75, 78, 81, 84, 87, 90, +93, 96, 99, 102, 105, 108, 111, 114, 117, 120, 123, 126, 130, 133, 136, 139, +142, 145, 148, 152, 155, 158, 161, 164, 168, 171, 174, 177, 181, 184, 187, 190, +194, 197, 200, 204, 207, 210, 214, 217, 220, 224, 227, 231, 234, 237, 241, 244, +248, 251, 255, 258, 262, 265, 268, 272, 276, 279, 283, 286, 290, 293, 297, 300, +304, 308, 311, 315, 318, 322, 326, 329, 333, 337, 340, 344, 348, 352, 355, 359, +363, 367, 370, 374, 378, 382, 385, 389, 393, 397, 401, 405, 409, 412, 416, 420, +424, 428, 432, 436, 440, 444, 448, 452, 456, 460, 464, 468, 472, 476, 480, 484, +488, 492, 496, 501, 505, 509, 513, 517, 521, 526, 530, 534, 538, 542, 547, 551, +555, 560, 564, 568, 572, 577, 581, 585, 590, 594, 599, 603, 607, 612, 616, 621, +625, 630, 634, 639, 643, 648, 652, 657, 661, 666, 670, 675, 680, 684, 689, 693, +698, 703, 708, 712, 717, 722, 726, 731, 736, 741, 745, 750, 755, 760, 765, 770, +774, 779, 784, 789, 794, 799, 804, 809, 814, 819, 824, 829, 834, 839, 844, 849, +854, 859, 864, 869, 874, 880, 885, 890, 895, 900, 906, 911, 916, 921, 927, 932, +937, 942, 948, 953, 959, 964, 969, 975, 980, 986, 991, 996, 1002, 1007, 1013, 1018 +}; +/* fullsin_table[x] = round(-log2(sin((x + 0.5) * PI / (PG_WIDTH / 4) / 2)) * 256) */ +static uint16_t fullsin_table[PG_WIDTH] = { +2137, 1731, 1543, 1419, 1326, 1252, 1190, 1137, 1091, 1050, 1013, 979, 949, 920, 894, 869, +846, 825, 804, 785, 767, 749, 732, 717, 701, 687, 672, 659, 646, 633, 621, 609, +598, 587, 576, 566, 556, 546, 536, 527, 518, 509, 501, 492, 484, 476, 468, 461, +453, 446, 439, 432, 425, 418, 411, 405, 399, 392, 386, 380, 375, 369, 363, 358, +352, 347, 341, 336, 331, 326, 321, 316, 311, 307, 302, 297, 293, 289, 284, 280, +276, 271, 267, 263, 259, 255, 251, 248, 244, 240, 236, 233, 229, 226, 222, 219, +215, 212, 209, 205, 202, 199, 196, 193, 190, 187, 184, 181, 178, 175, 172, 169, +167, 164, 161, 159, 156, 153, 151, 148, 146, 143, 141, 138, 136, 134, 131, 129, +127, 125, 122, 120, 118, 116, 114, 112, 110, 108, 106, 104, 102, 100, 98, 96, +94, 92, 91, 89, 87, 85, 83, 82, 80, 78, 77, 75, 74, 72, 70, 69, +67, 66, 64, 63, 62, 60, 59, 57, 56, 55, 53, 52, 51, 49, 48, 47, +46, 45, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, +29, 28, 27, 26, 25, 24, 23, 23, 22, 21, 20, 20, 19, 18, 17, 17, +16, 15, 15, 14, 13, 13, 12, 12, 11, 10, 10, 9, 9, 8, 8, 7, +7, 7, 6, 6, 5, 5, 5, 4, 4, 4, 3, 3, 3, 2, 2, 2, +2, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, +}; +/* clang-format on */ + +static uint16_t halfsin_table[PG_WIDTH]; +static uint16_t *wave_table_map[2] = {fullsin_table, halfsin_table}; + +/* pitch modulator */ +/* offset to fnum, rough approximation of 14 cents depth. */ +static int8_t pm_table[8][8] = { + {0, 0, 0, 0, 0, 0, 0, 0}, // fnum = 000xxxxxx + {0, 0, 1, 0, 0, 0, -1, 0}, // fnum = 001xxxxxx + {0, 1, 2, 1, 0, -1, -2, -1}, // fnum = 010xxxxxx + {0, 1, 3, 1, 0, -1, -3, -1}, // fnum = 011xxxxxx + {0, 2, 4, 2, 0, -2, -4, -2}, // fnum = 100xxxxxx + {0, 2, 5, 2, 0, -2, -5, -2}, // fnum = 101xxxxxx + {0, 3, 6, 3, 0, -3, -6, -3}, // fnum = 110xxxxxx + {0, 3, 7, 3, 0, -3, -7, -3}, // fnum = 111xxxxxx +}; + +/* amplitude lfo table */ +/* The following envelop pattern is verified on real YM2413. */ +/* each element repeates 64 cycles */ +static uint8_t am_table[210] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, // + 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, // + 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, // + 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, // + 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, // + 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, // + 12, 12, 12, 12, 12, 12, 12, 12, // + 13, 13, 13, // + 12, 12, 12, 12, 12, 12, 12, 12, // + 11, 11, 11, 11, 11, 11, 11, 11, 10, 10, 10, 10, 10, 10, 10, 10, // + 9, 9, 9, 9, 9, 9, 9, 9, 8, 8, 8, 8, 8, 8, 8, 8, // + 7, 7, 7, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, 6, 6, 6, // + 5, 5, 5, 5, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, // + 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, // + 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0}; + +/* envelope decay increment step table */ +/* based on andete's research */ +static uint8_t eg_step_tables[4][8] = { + {0, 1, 0, 1, 0, 1, 0, 1}, + {0, 1, 0, 1, 1, 1, 0, 1}, + {0, 1, 1, 1, 0, 1, 1, 1}, + {0, 1, 1, 1, 1, 1, 1, 1}, +}; + +enum __OPLL_EG_STATE { ATTACK, DECAY, SUSTAIN, RELEASE, DAMP, UNKNOWN }; + +static uint32_t ml_table[16] = {1, 1 * 2, 2 * 2, 3 * 2, 4 * 2, 5 * 2, 6 * 2, 7 * 2, + 8 * 2, 9 * 2, 10 * 2, 10 * 2, 12 * 2, 12 * 2, 15 * 2, 15 * 2}; + +#define dB2(x) ((x)*2) +static double kl_table[16] = {dB2(0.000), dB2(9.000), dB2(12.000), dB2(13.875), dB2(15.000), dB2(16.125), + dB2(16.875), dB2(17.625), dB2(18.000), dB2(18.750), dB2(19.125), dB2(19.500), + dB2(19.875), dB2(20.250), dB2(20.625), dB2(21.000)}; + +static uint32_t tll_table[8 * 16][1 << TL_BITS][4]; +static int32_t rks_table[8 * 2][2]; + +static OPLL_PATCH null_patch = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; +static OPLL_PATCH default_patch[OPLL_TONE_NUM][(16 + 3) * 2]; + +#define min(i, j) (((i) < (j)) ? (i) : (j)) +#define max(i, j) (((i) > (j)) ? (i) : (j)) + +/*************************************************** + + Internal Sample Rate Converter + +****************************************************/ +/* Note: to disable internal rate converter, set clock/72 to output sampling rate. */ + +/* + * LW is truncate length of sinc(x) calculation. + * Lower LW is faster, higher LW results better quality. + * LW must be a non-zero positive even number, no upper limit. + * LW=16 or greater is recommended when upsampling. + * LW=8 is practically okay for downsampling. + */ +#define LW 16 + +/* resolution of sinc(x) table. sinc(x) where 0.0<=x<1.0 corresponds to sinc_table[0...SINC_RESO-1] */ +#define SINC_RESO 256 +#define SINC_AMP_BITS 12 + +// double hamming(double x) { return 0.54 - 0.46 * cos(2 * PI * x); } +static double blackman(double x) { return 0.42 - 0.5 * cos(2 * _PI_ * x) + 0.08 * cos(4 * _PI_ * x); } +static double sinc(double x) { return (x == 0.0 ? 1.0 : sin(_PI_ * x) / (_PI_ * x)); } +static double windowed_sinc(double x) { return blackman(0.5 + 0.5 * x / (LW / 2)) * sinc(x); } + +/* f_inp: input frequency. f_out: output frequencey, ch: number of channels */ +OPLL_RateConv *OPLL_RateConv_new(double f_inp, double f_out, int ch) { + OPLL_RateConv *conv = malloc(sizeof(OPLL_RateConv)); + int i; + + conv->ch = ch; + conv->f_ratio = f_inp / f_out; + conv->buf = malloc(sizeof(void *) * ch); + for (i = 0; i < ch; i++) { + conv->buf[i] = malloc(sizeof(conv->buf[0][0]) * LW); + } + + /* create sinc_table for positive 0 <= x < LW/2 */ + conv->sinc_table = malloc(sizeof(conv->sinc_table[0]) * SINC_RESO * LW / 2); + for (i = 0; i < SINC_RESO * LW / 2; i++) { + const double x = (double)i / SINC_RESO; + if (f_out < f_inp) { + /* for downsampling */ + conv->sinc_table[i] = (int16_t)((1 << SINC_AMP_BITS) * windowed_sinc(x / conv->f_ratio) / conv->f_ratio); + } else { + /* for upsampling */ + conv->sinc_table[i] = (int16_t)((1 << SINC_AMP_BITS) * windowed_sinc(x)); + } + } + + return conv; +} + +static INLINE int16_t lookup_sinc_table(int16_t *table, double x) { + int16_t index = (int16_t)(x * SINC_RESO); + if (index < 0) + index = -index; + return table[min(SINC_RESO * LW / 2 - 1, index)]; +} + +void OPLL_RateConv_reset(OPLL_RateConv *conv) { + int i; + conv->timer = 0; + for (i = 0; i < conv->ch; i++) { + memset(conv->buf[i], 0, sizeof(conv->buf[i][0]) * LW); + } +} + +/* put original data to this converter at f_inp. */ +void OPLL_RateConv_putData(OPLL_RateConv *conv, int ch, int16_t data) { + int16_t *buf = conv->buf[ch]; + int i; + for (i = 0; i < LW - 1; i++) { + buf[i] = buf[i + 1]; + } + buf[LW - 1] = data; +} + +/* get resampled data from this converter at f_out. */ +/* this function must be called f_out / f_inp times per one putData call. */ +int16_t OPLL_RateConv_getData(OPLL_RateConv *conv, int ch) { + int16_t *buf = conv->buf[ch]; + int32_t sum = 0; + int k; + double dn; + conv->timer += conv->f_ratio; + dn = conv->timer - floor(conv->timer); + conv->timer = dn; + + for (k = 0; k < LW; k++) { + double x = ((double)k - (LW / 2 - 1)) - dn; + sum += buf[k] * lookup_sinc_table(conv->sinc_table, x); + } + return sum >> SINC_AMP_BITS; +} + +void OPLL_RateConv_delete(OPLL_RateConv *conv) { + int i; + for (i = 0; i < conv->ch; i++) { + free(conv->buf[i]); + } + free(conv->buf); + free(conv->sinc_table); + free(conv); +} + +/*************************************************** + + Create tables + +****************************************************/ + +static void makeSinTable(void) { + int x; + + for (x = 0; x < PG_WIDTH / 4; x++) { + fullsin_table[PG_WIDTH / 4 + x] = fullsin_table[PG_WIDTH / 4 - x - 1]; + } + + for (x = 0; x < PG_WIDTH / 2; x++) { + fullsin_table[PG_WIDTH / 2 + x] = 0x8000 | fullsin_table[x]; + } + + for (x = 0; x < PG_WIDTH / 2; x++) + halfsin_table[x] = fullsin_table[x]; + + for (x = PG_WIDTH / 2; x < PG_WIDTH; x++) + halfsin_table[x] = 0xfff; +} + +static void makeTllTable(void) { + + int32_t tmp; + int32_t fnum, block, TL, KL; + + for (fnum = 0; fnum < 16; fnum++) { + for (block = 0; block < 8; block++) { + for (TL = 0; TL < 64; TL++) { + for (KL = 0; KL < 4; KL++) { + if (KL == 0) { + tll_table[(block << 4) | fnum][TL][KL] = TL2EG(TL); + } else { + tmp = (int32_t)(kl_table[fnum] - dB2(3.000) * (7 - block)); + if (tmp <= 0) + tll_table[(block << 4) | fnum][TL][KL] = TL2EG(TL); + else + tll_table[(block << 4) | fnum][TL][KL] = (uint32_t)((tmp >> (3 - KL)) / EG_STEP) + TL2EG(TL); + } + } + } + } + } +} + +static void makeRksTable(void) { + int fnum8, block; + for (fnum8 = 0; fnum8 < 2; fnum8++) + for (block = 0; block < 8; block++) { + rks_table[(block << 1) | fnum8][1] = (block << 1) + fnum8; + rks_table[(block << 1) | fnum8][0] = block >> 1; + } +} + +static void makeDefaultPatch() { + int i, j; + for (i = 0; i < OPLL_TONE_NUM; i++) + for (j = 0; j < 19; j++) + OPLL_getDefaultPatch(i, j, &default_patch[i][j * 2]); +} + +static uint8_t table_initialized = 0; + +static void initializeTables() { + makeTllTable(); + makeRksTable(); + makeSinTable(); + makeDefaultPatch(); + table_initialized = 1; +} + +/********************************************************* + + Synthesizing + +*********************************************************/ +#define SLOT_BD1 12 +#define SLOT_BD2 13 +#define SLOT_HH 14 +#define SLOT_SD 15 +#define SLOT_TOM 16 +#define SLOT_CYM 17 + +/* utility macros */ +#define MOD(o, x) (&(o)->slot[(x) << 1]) +#define CAR(o, x) (&(o)->slot[((x) << 1) | 1]) +#define BIT(s, b) (((s) >> (b)) & 1) + +#if OPLL_DEBUG +static void _debug_print_patch(OPLL_SLOT *slot) { + OPLL_PATCH *p = slot->patch; + printf("[slot#%d am:%d pm:%d eg:%d kr:%d ml:%d kl:%d tl:%d ws:%d fb:%d A:%d D:%d S:%d R:%d]\n", slot->number, // + p->AM, p->PM, p->EG, p->KR, p->ML, // + p->KL, p->TL, p->WS, p->FB, // + p->AR, p->DR, p->SL, p->RR); +} + +static char *_debug_eg_state_name(OPLL_SLOT *slot) { + switch (slot->eg_state) { + case ATTACK: + return "attack"; + case DECAY: + return "decay"; + case SUSTAIN: + return "sustain"; + case RELEASE: + return "release"; + case DAMP: + return "damp"; + default: + return "unknown"; + } +} + +static INLINE void _debug_print_slot_info(OPLL_SLOT *slot) { + char *name = _debug_eg_state_name(slot); + printf("[slot#%d state:%s fnum:%03x rate:%d-%d]\n", slot->number, name, slot->blk_fnum, slot->eg_rate_h, + slot->eg_rate_l); + _debug_print_patch(slot); + fflush(stdout); +} +#endif + +static INLINE int get_parameter_rate(OPLL_SLOT *slot) { + + if ((slot->type & 1) == 0 && slot->key_flag == 0) { + return 0; + } + + switch (slot->eg_state) { + case ATTACK: + return slot->patch->AR; + case DECAY: + return slot->patch->DR; + case SUSTAIN: + return slot->patch->EG ? 0 : slot->patch->RR; + case RELEASE: + if (slot->sus_flag) { + return 5; + } else if (slot->patch->EG) { + return slot->patch->RR; + } else { + return 7; + } + case DAMP: + return DAMPER_RATE; + default: + return 0; + } +} + +enum SLOT_UPDATE_FLAG { + UPDATE_WS = 1, + UPDATE_TLL = 2, + UPDATE_RKS = 4, + UPDATE_EG = 8, + UPDATE_ALL = 255, +}; + +static INLINE void request_update(OPLL_SLOT *slot, int flag) { slot->update_requests |= flag; } + +static void commit_slot_update(OPLL_SLOT *slot) { + +#if OPLL_DEBUG + if (slot->last_eg_state != slot->eg_state) { + _debug_print_slot_info(slot); + slot->last_eg_state = slot->eg_state; + } +#endif + + if (slot->update_requests & UPDATE_WS) { + slot->wave_table = wave_table_map[slot->patch->WS]; + } + + if (slot->update_requests & UPDATE_TLL) { + if ((slot->type & 1) == 0) { + slot->tll = tll_table[slot->blk_fnum >> 5][slot->patch->TL][slot->patch->KL]; + } else { + slot->tll = tll_table[slot->blk_fnum >> 5][slot->volume][slot->patch->KL]; + } + } + + if (slot->update_requests & UPDATE_RKS) { + slot->rks = rks_table[slot->blk_fnum >> 8][slot->patch->KR]; + } + + if (slot->update_requests & (UPDATE_RKS | UPDATE_EG)) { + int p_rate = get_parameter_rate(slot); + + if (p_rate == 0) { + slot->eg_shift = 0; + slot->eg_rate_h = 0; + slot->eg_rate_l = 0; + return; + } + + slot->eg_rate_h = min(15, p_rate + (slot->rks >> 2)); + slot->eg_rate_l = slot->rks & 3; + if (slot->eg_state == ATTACK) { + slot->eg_shift = (0 < slot->eg_rate_h && slot->eg_rate_h < 12) ? (13 - slot->eg_rate_h) : 0; + } else { + slot->eg_shift = (slot->eg_rate_h < 13) ? (13 - slot->eg_rate_h) : 0; + } + } + + slot->update_requests = 0; +} + +static void reset_slot(OPLL_SLOT *slot, int number) { + slot->number = number; + slot->type = number % 2; + slot->pg_keep = 0; + slot->wave_table = wave_table_map[0]; + slot->pg_phase = 0; + slot->output[0] = 0; + slot->output[1] = 0; + slot->eg_state = RELEASE; + slot->eg_shift = 0; + slot->rks = 0; + slot->tll = 0; + slot->key_flag = 0; + slot->sus_flag = 0; + slot->blk_fnum = 0; + slot->blk = 0; + slot->fnum = 0; + slot->volume = 0; + slot->pg_out = 0; + slot->eg_out = EG_MUTE; + slot->patch = &null_patch; +} + +static INLINE void slotOn(OPLL *opll, int i) { + OPLL_SLOT *slot = &opll->slot[i]; + slot->key_flag = 1; + slot->eg_state = DAMP; + request_update(slot, UPDATE_EG); +} + +static INLINE void slotOff(OPLL *opll, int i) { + OPLL_SLOT *slot = &opll->slot[i]; + slot->key_flag = 0; + if (slot->type & 1) { + slot->eg_state = RELEASE; + request_update(slot, UPDATE_EG); + } +} + +static INLINE void update_key_status(OPLL *opll) { + const uint8_t r14 = opll->reg[0x0e]; + const uint8_t rhythm_mode = BIT(r14, 5); + uint32_t new_slot_key_status = 0; + uint32_t updated_status; + int ch; + + for (ch = 0; ch < 9; ch++) + if (opll->reg[0x20 + ch] & 0x10) + new_slot_key_status |= 3 << (ch * 2); + + if (rhythm_mode) { + if (r14 & 0x10) + new_slot_key_status |= 3 << SLOT_BD1; + + if (r14 & 0x01) + new_slot_key_status |= 1 << SLOT_HH; + + if (r14 & 0x08) + new_slot_key_status |= 1 << SLOT_SD; + + if (r14 & 0x04) + new_slot_key_status |= 1 << SLOT_TOM; + + if (r14 & 0x02) + new_slot_key_status |= 1 << SLOT_CYM; + } + + updated_status = opll->slot_key_status ^ new_slot_key_status; + + if (updated_status) { + int i; + for (i = 0; i < 18; i++) + if (BIT(updated_status, i)) { + if (BIT(new_slot_key_status, i)) { + slotOn(opll, i); + } else { + slotOff(opll, i); + } + } + } + + opll->slot_key_status = new_slot_key_status; +} + +static INLINE void set_patch(OPLL *opll, int32_t ch, int32_t num) { + opll->patch_number[ch] = num; + MOD(opll, ch)->patch = &opll->patch[num * 2 + 0]; + CAR(opll, ch)->patch = &opll->patch[num * 2 + 1]; + request_update(MOD(opll, ch), UPDATE_ALL); + request_update(CAR(opll, ch), UPDATE_ALL); +} + +static INLINE void set_sus_flag(OPLL *opll, int ch, int flag) { + CAR(opll, ch)->sus_flag = flag; + request_update(CAR(opll, ch), UPDATE_EG); + if (MOD(opll, ch)->type & 1) { + MOD(opll, ch)->sus_flag = flag; + request_update(MOD(opll, ch), UPDATE_EG); + } +} + +/* set volume ( volume : 6bit, register value << 2 ) */ +static INLINE void set_volume(OPLL *opll, int ch, int volume) { + CAR(opll, ch)->volume = volume; + request_update(CAR(opll, ch), UPDATE_TLL); +} + +static INLINE void set_slot_volume(OPLL_SLOT *slot, int volume) { + slot->volume = volume; + request_update(slot, UPDATE_TLL); +} + +/* set f-Nnmber ( fnum : 9bit ) */ +static INLINE void set_fnumber(OPLL *opll, int ch, int fnum) { + OPLL_SLOT *car = CAR(opll, ch); + OPLL_SLOT *mod = MOD(opll, ch); + car->fnum = fnum; + car->blk_fnum = (car->blk_fnum & 0xe00) | (fnum & 0x1ff); + mod->fnum = fnum; + mod->blk_fnum = (mod->blk_fnum & 0xe00) | (fnum & 0x1ff); + request_update(car, UPDATE_EG | UPDATE_RKS | UPDATE_TLL); + request_update(mod, UPDATE_EG | UPDATE_RKS | UPDATE_TLL); +} + +/* set block data (blk : 3bit ) */ +static INLINE void set_block(OPLL *opll, int ch, int blk) { + OPLL_SLOT *car = CAR(opll, ch); + OPLL_SLOT *mod = MOD(opll, ch); + car->blk = blk; + car->blk_fnum = ((blk & 7) << 9) | (car->blk_fnum & 0x1ff); + mod->blk = blk; + mod->blk_fnum = ((blk & 7) << 9) | (mod->blk_fnum & 0x1ff); + request_update(car, UPDATE_EG | UPDATE_RKS | UPDATE_TLL); + request_update(mod, UPDATE_EG | UPDATE_RKS | UPDATE_TLL); +} + +static INLINE void update_rhythm_mode(OPLL *opll) { + const uint8_t new_rhythm_mode = (opll->reg[0x0e] >> 5) & 1; + + if (opll->rhythm_mode != new_rhythm_mode) { + + if (new_rhythm_mode) { + opll->slot[SLOT_HH].type = 3; + opll->slot[SLOT_HH].pg_keep = 1; + opll->slot[SLOT_SD].type = 3; + opll->slot[SLOT_TOM].type = 3; + opll->slot[SLOT_CYM].type = 3; + opll->slot[SLOT_CYM].pg_keep = 1; + set_patch(opll, 6, 16); + set_patch(opll, 7, 17); + set_patch(opll, 8, 18); + set_slot_volume(&opll->slot[SLOT_HH], ((opll->reg[0x37] >> 4) & 15) << 2); + set_slot_volume(&opll->slot[SLOT_TOM], ((opll->reg[0x38] >> 4) & 15) << 2); + } else { + opll->slot[SLOT_HH].type = 0; + opll->slot[SLOT_HH].pg_keep = 0; + opll->slot[SLOT_SD].type = 1; + opll->slot[SLOT_TOM].type = 0; + opll->slot[SLOT_CYM].type = 1; + opll->slot[SLOT_CYM].pg_keep = 0; + set_patch(opll, 6, opll->reg[0x36] >> 4); + set_patch(opll, 7, opll->reg[0x37] >> 4); + set_patch(opll, 8, opll->reg[0x38] >> 4); + } + } + + opll->rhythm_mode = new_rhythm_mode; +} + +static void update_ampm(OPLL *opll) { + if (opll->test_flag & 2) { + opll->pm_phase = 0; + opll->am_phase = 0; + } else { + opll->pm_phase += (opll->test_flag & 8) ? 1024 : 1; + opll->am_phase += (opll->test_flag & 8) ? 64 : 1; + } + opll->lfo_am = am_table[(opll->am_phase >> 6) % sizeof(am_table)]; +} + +static void update_noise(OPLL *opll, int cycle) { + int i; + for (i = 0; i < cycle; i++) { + if (opll->noise & 1) { + opll->noise ^= 0x800200; + } + opll->noise >>= 1; + } +} + +static void update_short_noise(OPLL *opll) { + const uint32_t pg_hh = opll->slot[SLOT_HH].pg_out; + const uint32_t pg_cym = opll->slot[SLOT_CYM].pg_out; + + const uint8_t h_bit2 = BIT(pg_hh, PG_BITS - 8); + const uint8_t h_bit7 = BIT(pg_hh, PG_BITS - 3); + const uint8_t h_bit3 = BIT(pg_hh, PG_BITS - 7); + + const uint8_t c_bit3 = BIT(pg_cym, PG_BITS - 7); + const uint8_t c_bit5 = BIT(pg_cym, PG_BITS - 5); + + opll->short_noise = (h_bit2 ^ h_bit7) | (h_bit3 ^ c_bit5) | (c_bit3 ^ c_bit5); +} + +static INLINE void calc_phase(OPLL_SLOT *slot, int32_t pm_phase, uint8_t reset) { + const int8_t pm = slot->patch->PM ? pm_table[(slot->fnum >> 6) & 7][(pm_phase >> 10) & 7] : 0; + if (reset) { + slot->pg_phase = 0; + } + slot->pg_phase += (((slot->fnum & 0x1ff) * 2 + pm) * ml_table[slot->patch->ML]) << slot->blk >> 2; + slot->pg_phase &= (DP_WIDTH - 1); + slot->pg_out = slot->pg_phase >> DP_BASE_BITS; +} + +static INLINE uint8_t lookup_attack_step(OPLL_SLOT *slot, uint32_t counter) { + int index; + + switch (slot->eg_rate_h) { + case 12: + index = (counter & 0xc) >> 1; + return 4 - eg_step_tables[slot->eg_rate_l][index]; + case 13: + index = (counter & 0xc) >> 1; + return 3 - eg_step_tables[slot->eg_rate_l][index]; + case 14: + index = (counter & 0xc) >> 1; + return 2 - eg_step_tables[slot->eg_rate_l][index]; + case 0: + case 15: + return 0; + default: + index = counter >> slot->eg_shift; + return eg_step_tables[slot->eg_rate_l][index & 7] ? 4 : 0; + } +} + +static INLINE uint8_t lookup_decay_step(OPLL_SLOT *slot, uint32_t counter) { + int index; + + switch (slot->eg_rate_h) { + case 0: + return 0; + case 13: + index = ((counter & 0xc) >> 1) | (counter & 1); + return eg_step_tables[slot->eg_rate_l][index]; + case 14: + index = ((counter & 0xc) >> 1); + return eg_step_tables[slot->eg_rate_l][index] + 1; + case 15: + return 2; + default: + index = counter >> slot->eg_shift; + return eg_step_tables[slot->eg_rate_l][index & 7]; + } +} + +static INLINE void start_envelope(OPLL_SLOT *slot) { + if (min(15, slot->patch->AR + (slot->rks >> 2)) == 15) { + slot->eg_state = DECAY; + slot->eg_out = 0; + } else { + slot->eg_state = ATTACK; + slot->eg_out = EG_MUTE; + } + request_update(slot, UPDATE_EG); +} + +static INLINE void calc_envelope(OPLL_SLOT *slot, OPLL_SLOT *buddy, uint16_t eg_counter, uint8_t test) { + + uint32_t mask = (1 << slot->eg_shift) - 1; + uint8_t s; + + if (slot->eg_state == ATTACK) { + if (0 < slot->eg_out && 0 < slot->eg_rate_h && (eg_counter & mask & ~3) == 0) { + s = lookup_attack_step(slot, eg_counter); + if (0 < s) { + slot->eg_out = max(0, ((int)slot->eg_out - (slot->eg_out >> s) - 1)); + } + } + } else { + if (slot->eg_rate_h > 0 && (eg_counter & mask) == 0) { + slot->eg_out = min(EG_MUTE, slot->eg_out + lookup_decay_step(slot, eg_counter)); + } + } + + switch (slot->eg_state) { + case DAMP: + if (slot->eg_out >= EG_MUTE) { + start_envelope(slot); + if (slot->type & 1) { + if (!slot->pg_keep) { + slot->pg_phase = 0; + } + if (buddy && !buddy->pg_keep) { + buddy->pg_phase = 0; + } + } + } + break; + + case ATTACK: + if (slot->eg_out == 0) { + slot->eg_state = DECAY; + request_update(slot, UPDATE_EG); + } + break; + + case DECAY: + if ((slot->eg_out >> 3) == slot->patch->SL) { + slot->eg_state = SUSTAIN; + request_update(slot, UPDATE_EG); + } + break; + + case SUSTAIN: + case RELEASE: + default: + break; + } + + if (test) { + slot->eg_out = 0; + } +} + +static void update_slots(OPLL *opll) { + int i; + opll->eg_counter++; + + for (i = 0; i < 18; i++) { + OPLL_SLOT *slot = &opll->slot[i]; + OPLL_SLOT *buddy = NULL; + if (slot->type == 0) { + buddy = &opll->slot[i + 1]; + } + if (slot->type == 1) { + buddy = &opll->slot[i - 1]; + } + if (slot->update_requests) { + commit_slot_update(slot); + } + calc_envelope(slot, buddy, opll->eg_counter, opll->test_flag & 1); + calc_phase(slot, opll->pm_phase, opll->test_flag & 4); + } +} + +/* output: -4095...4095 */ +static INLINE int16_t lookup_exp_table(uint16_t i) { + /* from andete's expressoin */ + int16_t t = (exp_table[(i & 0xff) ^ 0xff] + 1024); + int16_t res = t >> ((i & 0x7f00) >> 8); + return ((i & 0x8000) ? ~res : res) << 1; +} + +static INLINE int16_t to_linear(uint16_t h, OPLL_SLOT *slot, int16_t am) { + uint16_t att; + if (slot->eg_out >= EG_MAX) + return 0; + + att = min(EG_MAX, (slot->eg_out + slot->tll + am)) << 4; + return lookup_exp_table(h + att); +} + +static INLINE int16_t calc_slot_car(OPLL *opll, int ch, int16_t fm) { + OPLL_SLOT *slot = CAR(opll, ch); + + uint8_t am = slot->patch->AM ? opll->lfo_am : 0; + + slot->output[1] = slot->output[0]; + slot->output[0] = to_linear(slot->wave_table[(slot->pg_out + 2 * (fm >> 1)) & (PG_WIDTH - 1)], slot, am); + + return slot->output[0]; +} + +static INLINE int16_t calc_slot_mod(OPLL *opll, int ch) { + OPLL_SLOT *slot = MOD(opll, ch); + + int16_t fm = slot->patch->FB > 0 ? (slot->output[1] + slot->output[0]) >> (9 - slot->patch->FB) : 0; + uint8_t am = slot->patch->AM ? opll->lfo_am : 0; + + slot->output[1] = slot->output[0]; + slot->output[0] = to_linear(slot->wave_table[(slot->pg_out + fm) & (PG_WIDTH - 1)], slot, am); + + return slot->output[0]; +} + +static INLINE int16_t calc_slot_tom(OPLL *opll) { + OPLL_SLOT *slot = MOD(opll, 8); + + return to_linear(slot->wave_table[slot->pg_out], slot, 0); +} + +/* Specify phase offset directly based on 10-bit (1024-length) sine table */ +#define _PD(phase) ((PG_BITS < 10) ? (phase >> (10 - PG_BITS)) : (phase << (PG_BITS - 10))) + +static INLINE int16_t calc_slot_snare(OPLL *opll) { + OPLL_SLOT *slot = CAR(opll, 7); + + uint32_t phase; + + if (BIT(slot->pg_out, PG_BITS - 2)) + phase = (opll->noise & 1) ? _PD(0x300) : _PD(0x200); + else + phase = (opll->noise & 1) ? _PD(0x0) : _PD(0x100); + + return to_linear(slot->wave_table[phase], slot, 0); +} + +static INLINE int16_t calc_slot_cym(OPLL *opll) { + OPLL_SLOT *slot = CAR(opll, 8); + + uint32_t phase = opll->short_noise ? _PD(0x300) : _PD(0x100); + + return to_linear(slot->wave_table[phase], slot, 0); +} + +static INLINE int16_t calc_slot_hat(OPLL *opll) { + OPLL_SLOT *slot = MOD(opll, 7); + + uint32_t phase; + + if (opll->short_noise) + phase = (opll->noise & 1) ? _PD(0x2d0) : _PD(0x234); + else + phase = (opll->noise & 1) ? _PD(0x34) : _PD(0xd0); + + return to_linear(slot->wave_table[phase], slot, 0); +} + +#define _MO(x) (-(x) >> 1) +#define _RO(x) (x) + +static void update_output(OPLL *opll) { + int16_t *out; + int i; + + update_ampm(opll); + update_short_noise(opll); + update_slots(opll); + + out = opll->ch_out; + + /* CH1-6 */ + for (i = 0; i < 6; i++) { + if (!(opll->mask & OPLL_MASK_CH(i))) { + out[i] = _MO(calc_slot_car(opll, i, calc_slot_mod(opll, i))); + } + } + + /* CH7 */ + if (!opll->rhythm_mode) { + if (!(opll->mask & OPLL_MASK_CH(6))) { + out[6] = _MO(calc_slot_car(opll, 6, calc_slot_mod(opll, 6))); + } + } else { + if (!(opll->mask & OPLL_MASK_BD)) { + out[9] = _RO(calc_slot_car(opll, 6, calc_slot_mod(opll, 6))); + } + } + update_noise(opll, 14); + + /* CH8 */ + if (!opll->rhythm_mode) { + if (!(opll->mask & OPLL_MASK_CH(7))) { + out[7] = _MO(calc_slot_car(opll, 7, calc_slot_mod(opll, 7))); + } + } else { + if (!(opll->mask & OPLL_MASK_HH)) { + out[10] = _RO(calc_slot_hat(opll)); + } + if (!(opll->mask & OPLL_MASK_SD)) { + out[11] = _RO(calc_slot_snare(opll)); + } + } + update_noise(opll, 2); + + /* CH9 */ + if (!opll->rhythm_mode) { + if (!(opll->mask & OPLL_MASK_CH(8))) { + out[8] = _MO(calc_slot_car(opll, 8, calc_slot_mod(opll, 8))); + } + } else { + if (!(opll->mask & OPLL_MASK_TOM)) { + out[12] = _RO(calc_slot_tom(opll)); + } + if (!(opll->mask & OPLL_MASK_CYM)) { + out[13] = _RO(calc_slot_cym(opll)); + } + } + update_noise(opll, 2); +} + +INLINE static void mix_output(OPLL *opll) { + int16_t out = 0; + int i; + for (i = 0; i < 14; i++) { + out += opll->ch_out[i]; + } + if (opll->conv) { + OPLL_RateConv_putData(opll->conv, 0, out); + } else { + opll->mix_out[0] = out; + } +} + +INLINE static void mix_output_stereo(OPLL *opll) { + int16_t *out = opll->mix_out; + int i; + out[0] = out[1] = 0; + for (i = 0; i < 14; i++) { + if (opll->pan[i] & 2) + out[0] += (int16_t)(opll->ch_out[i] * opll->pan_fine[i][0]); + if (opll->pan[i] & 1) + out[1] += (int16_t)(opll->ch_out[i] * opll->pan_fine[i][1]); + } + if (opll->conv) { + OPLL_RateConv_putData(opll->conv, 0, out[0]); + OPLL_RateConv_putData(opll->conv, 1, out[1]); + } +} + +/*********************************************************** + + External Interfaces + +***********************************************************/ + +OPLL *OPLL_new(uint32_t clk, uint32_t rate) { + OPLL *opll; + int i; + + if (!table_initialized) { + initializeTables(); + } + + opll = (OPLL *)calloc(sizeof(OPLL), 1); + if (opll == NULL) + return NULL; + + for (i = 0; i < 19 * 2; i++) + memcpy(&opll->patch[i], &null_patch, sizeof(OPLL_PATCH)); + + opll->clk = clk; + opll->rate = rate; + opll->mask = 0; + opll->conv = NULL; + opll->mix_out[0] = 0; + opll->mix_out[1] = 0; + + OPLL_reset(opll); + OPLL_setChipType(opll, 0); + OPLL_resetPatch(opll, 0); + return opll; +} + +void OPLL_delete(OPLL *opll) { + if (opll->conv) { + OPLL_RateConv_delete(opll->conv); + opll->conv = NULL; + } + free(opll); +} + +static void reset_rate_conversion_params(OPLL *opll) { + const double f_out = opll->rate; + const double f_inp = opll->clk / 72; + + opll->out_time = 0; + opll->out_step = ((uint32_t)f_inp) << 8; + opll->inp_step = ((uint32_t)f_out) << 8; + + if (opll->conv) { + OPLL_RateConv_delete(opll->conv); + opll->conv = NULL; + } + + if (floor(f_inp) != f_out && floor(f_inp + 0.5) != f_out) { + opll->conv = OPLL_RateConv_new(f_inp, f_out, 2); + } + + if (opll->conv) { + OPLL_RateConv_reset(opll->conv); + } +} + +void OPLL_reset(OPLL *opll) { + int i; + + if (!opll) + return; + + opll->adr = 0; + + opll->pm_phase = 0; + opll->am_phase = 0; + + opll->noise = 0x1; + opll->mask = 0; + + opll->rhythm_mode = 0; + opll->slot_key_status = 0; + opll->eg_counter = 0; + + reset_rate_conversion_params(opll); + + for (i = 0; i < 18; i++) + reset_slot(&opll->slot[i], i); + + for (i = 0; i < 9; i++) { + set_patch(opll, i, 0); + } + + for (i = 0; i < 0x40; i++) + OPLL_writeReg(opll, i, 0); + + for (i = 0; i < 15; i++) { + opll->pan[i] = 3; + opll->pan_fine[i][1] = opll->pan_fine[i][0] = 1.0f; + } + + for (i = 0; i < 14; i++) { + opll->ch_out[i] = 0; + } +} + +void OPLL_forceRefresh(OPLL *opll) { + int i; + + if (opll == NULL) + return; + + for (i = 0; i < 9; i++) { + set_patch(opll, i, opll->patch_number[i]); + } + + for (i = 0; i < 18; i++) { + request_update(&opll->slot[i], UPDATE_ALL); + } +} + +void OPLL_setRate(OPLL *opll, uint32_t rate) { + opll->rate = rate; + reset_rate_conversion_params(opll); +} + +void OPLL_setQuality(OPLL *opll, uint8_t q) {} + +void OPLL_setChipType(OPLL *opll, uint8_t type) { opll->chip_type = type; } + +void OPLL_writeReg(OPLL *opll, uint32_t reg, uint8_t data) { + int ch, i; + + if (reg >= 0x40) + return; + + /* mirror registers */ + if ((0x19 <= reg && reg <= 0x1f) || (0x29 <= reg && reg <= 0x2f) || (0x39 <= reg && reg <= 0x3f)) { + reg -= 9; + } + + opll->reg[reg] = (uint8_t)data; + + switch (reg) { + case 0x00: + opll->patch[0].AM = (data >> 7) & 1; + opll->patch[0].PM = (data >> 6) & 1; + opll->patch[0].EG = (data >> 5) & 1; + opll->patch[0].KR = (data >> 4) & 1; + opll->patch[0].ML = (data)&15; + for (i = 0; i < 9; i++) { + if (opll->patch_number[i] == 0) { + request_update(MOD(opll, i), UPDATE_RKS | UPDATE_EG); + } + } + break; + + case 0x01: + opll->patch[1].AM = (data >> 7) & 1; + opll->patch[1].PM = (data >> 6) & 1; + opll->patch[1].EG = (data >> 5) & 1; + opll->patch[1].KR = (data >> 4) & 1; + opll->patch[1].ML = (data)&15; + for (i = 0; i < 9; i++) { + if (opll->patch_number[i] == 0) { + request_update(CAR(opll, i), UPDATE_RKS | UPDATE_EG); + } + } + break; + + case 0x02: + opll->patch[0].KL = (data >> 6) & 3; + opll->patch[0].TL = (data)&63; + for (i = 0; i < 9; i++) { + if (opll->patch_number[i] == 0) { + request_update(MOD(opll, i), UPDATE_TLL); + } + } + break; + + case 0x03: + opll->patch[1].KL = (data >> 6) & 3; + opll->patch[1].WS = (data >> 4) & 1; + opll->patch[0].WS = (data >> 3) & 1; + opll->patch[0].FB = (data)&7; + for (i = 0; i < 9; i++) { + if (opll->patch_number[i] == 0) { + request_update(MOD(opll, i), UPDATE_WS); + request_update(CAR(opll, i), UPDATE_WS | UPDATE_TLL); + } + } + break; + + case 0x04: + opll->patch[0].AR = (data >> 4) & 15; + opll->patch[0].DR = (data)&15; + for (i = 0; i < 9; i++) { + if (opll->patch_number[i] == 0) { + request_update(MOD(opll, i), UPDATE_EG); + } + } + break; + + case 0x05: + opll->patch[1].AR = (data >> 4) & 15; + opll->patch[1].DR = (data)&15; + for (i = 0; i < 9; i++) { + if (opll->patch_number[i] == 0) { + request_update(CAR(opll, i), UPDATE_EG); + } + } + break; + + case 0x06: + opll->patch[0].SL = (data >> 4) & 15; + opll->patch[0].RR = (data)&15; + for (i = 0; i < 9; i++) { + if (opll->patch_number[i] == 0) { + request_update(MOD(opll, i), UPDATE_EG); + } + } + break; + + case 0x07: + opll->patch[1].SL = (data >> 4) & 15; + opll->patch[1].RR = (data)&15; + for (i = 0; i < 9; i++) { + if (opll->patch_number[i] == 0) { + request_update(CAR(opll, i), UPDATE_EG); + } + } + break; + + case 0x0e: + if (opll->chip_type == 1) + break; + update_rhythm_mode(opll); + update_key_status(opll); + break; + + case 0x0f: + opll->test_flag = data; + break; + + case 0x10: + case 0x11: + case 0x12: + case 0x13: + case 0x14: + case 0x15: + case 0x16: + case 0x17: + case 0x18: + ch = reg - 0x10; + set_fnumber(opll, ch, data + ((opll->reg[0x20 + ch] & 1) << 8)); + break; + + case 0x20: + case 0x21: + case 0x22: + case 0x23: + case 0x24: + case 0x25: + case 0x26: + case 0x27: + case 0x28: + ch = reg - 0x20; + set_fnumber(opll, ch, ((data & 1) << 8) + opll->reg[0x10 + ch]); + set_block(opll, ch, (data >> 1) & 7); + set_sus_flag(opll, ch, (data >> 5) & 1); + update_key_status(opll); + break; + + case 0x30: + case 0x31: + case 0x32: + case 0x33: + case 0x34: + case 0x35: + case 0x36: + case 0x37: + case 0x38: + if ((opll->reg[0x0e] & 32) && (reg >= 0x36)) { + switch (reg) { + case 0x37: + set_slot_volume(MOD(opll, 7), ((data >> 4) & 15) << 2); + break; + case 0x38: + set_slot_volume(MOD(opll, 8), ((data >> 4) & 15) << 2); + break; + default: + break; + } + } else { + set_patch(opll, reg - 0x30, (data >> 4) & 15); + } + set_volume(opll, reg - 0x30, (data & 15) << 2); + break; + + default: + break; + } +} + +void OPLL_writeIO(OPLL *opll, uint32_t adr, uint8_t val) { + if (adr & 1) + OPLL_writeReg(opll, opll->adr, val); + else + opll->adr = val; +} + +void OPLL_setPan(OPLL *opll, uint32_t ch, uint8_t pan) { opll->pan[ch & 15] = pan; } + +void OPLL_setPanFine(OPLL *opll, uint32_t ch, float pan[2]) { + opll->pan_fine[ch & 15][0] = pan[0]; + opll->pan_fine[ch & 15][1] = pan[1]; +} + +void OPLL_dumpToPatch(const uint8_t *dump, OPLL_PATCH *patch) { + patch[0].AM = (dump[0] >> 7) & 1; + patch[1].AM = (dump[1] >> 7) & 1; + patch[0].PM = (dump[0] >> 6) & 1; + patch[1].PM = (dump[1] >> 6) & 1; + patch[0].EG = (dump[0] >> 5) & 1; + patch[1].EG = (dump[1] >> 5) & 1; + patch[0].KR = (dump[0] >> 4) & 1; + patch[1].KR = (dump[1] >> 4) & 1; + patch[0].ML = (dump[0]) & 15; + patch[1].ML = (dump[1]) & 15; + patch[0].KL = (dump[2] >> 6) & 3; + patch[1].KL = (dump[3] >> 6) & 3; + patch[0].TL = (dump[2]) & 63; + patch[1].TL = 0; + patch[0].FB = (dump[3]) & 7; + patch[1].FB = 0; + patch[0].WS = (dump[3] >> 3) & 1; + patch[1].WS = (dump[3] >> 4) & 1; + patch[0].AR = (dump[4] >> 4) & 15; + patch[1].AR = (dump[5] >> 4) & 15; + patch[0].DR = (dump[4]) & 15; + patch[1].DR = (dump[5]) & 15; + patch[0].SL = (dump[6] >> 4) & 15; + patch[1].SL = (dump[7] >> 4) & 15; + patch[0].RR = (dump[6]) & 15; + patch[1].RR = (dump[7]) & 15; +} + +void OPLL_getDefaultPatch(int32_t type, int32_t num, OPLL_PATCH *patch) { + OPLL_dump2patch(default_inst[type] + num * 8, patch); +} + +void OPLL_setPatch(OPLL *opll, const uint8_t *dump) { + OPLL_PATCH patch[2]; + int i; + for (i = 0; i < 19; i++) { + OPLL_dump2patch(dump + i * 8, patch); + memcpy(&opll->patch[i * 2 + 0], &patch[0], sizeof(OPLL_PATCH)); + memcpy(&opll->patch[i * 2 + 1], &patch[1], sizeof(OPLL_PATCH)); + } +} + +void OPLL_patchToDump(const OPLL_PATCH *patch, uint8_t *dump) { + dump[0] = (uint8_t)((patch[0].AM << 7) + (patch[0].PM << 6) + (patch[0].EG << 5) + (patch[0].KR << 4) + patch[0].ML); + dump[1] = (uint8_t)((patch[1].AM << 7) + (patch[1].PM << 6) + (patch[1].EG << 5) + (patch[1].KR << 4) + patch[1].ML); + dump[2] = (uint8_t)((patch[0].KL << 6) + patch[0].TL); + dump[3] = (uint8_t)((patch[1].KL << 6) + (patch[1].WS << 4) + (patch[0].WS << 3) + patch[0].FB); + dump[4] = (uint8_t)((patch[0].AR << 4) + patch[0].DR); + dump[5] = (uint8_t)((patch[1].AR << 4) + patch[1].DR); + dump[6] = (uint8_t)((patch[0].SL << 4) + patch[0].RR); + dump[7] = (uint8_t)((patch[1].SL << 4) + patch[1].RR); +} + +void OPLL_copyPatch(OPLL *opll, int32_t num, OPLL_PATCH *patch) { + memcpy(&opll->patch[num], patch, sizeof(OPLL_PATCH)); +} + +void OPLL_resetPatch(OPLL *opll, uint8_t type) { + int i; + for (i = 0; i < 19 * 2; i++) + OPLL_copyPatch(opll, i, &default_patch[type % OPLL_TONE_NUM][i]); +} + +int16_t OPLL_calc(OPLL *opll) { + while (opll->out_step > opll->out_time) { + opll->out_time += opll->inp_step; + update_output(opll); + mix_output(opll); + } + opll->out_time -= opll->out_step; + if (opll->conv) { + opll->mix_out[0] = OPLL_RateConv_getData(opll->conv, 0); + } + return opll->mix_out[0]; +} + +void OPLL_calcStereo(OPLL *opll, int32_t out[2]) { + while (opll->out_step > opll->out_time) { + opll->out_time += opll->inp_step; + update_output(opll); + mix_output_stereo(opll); + } + opll->out_time -= opll->out_step; + if (opll->conv) { + out[0] = OPLL_RateConv_getData(opll->conv, 0); + out[1] = OPLL_RateConv_getData(opll->conv, 1); + } else { + out[0] = opll->mix_out[0]; + out[1] = opll->mix_out[1]; + } +} + +uint32_t OPLL_setMask(OPLL *opll, uint32_t mask) { + uint32_t ret; + + if (opll) { + ret = opll->mask; + opll->mask = mask; + return ret; + } else + return 0; +} + +uint32_t OPLL_toggleMask(OPLL *opll, uint32_t mask) { + uint32_t ret; + + if (opll) { + ret = opll->mask; + opll->mask ^= mask; + return ret; + } else + return 0; +} diff --git a/extern/NSFplay/legacy/emu2413.h b/extern/NSFplay/legacy/emu2413.h new file mode 100644 index 000000000..2804f3fac --- /dev/null +++ b/extern/NSFplay/legacy/emu2413.h @@ -0,0 +1,247 @@ +#ifndef _EMU2413_H_ +#define _EMU2413_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define OPLL_DEBUG 0 + +enum OPLL_TONE_ENUM { + OPLL_VRC7_TONE = 0, + OPLL_VRC7_RW_TONE, + OPLL_VRC7_FT36_TONE, + OPLL_VRC7_FT35_TONE, + OPLL_VRC7_MO_TONE, + OPLL_VRC7_KT2_TONE, + OPLL_VRC7_KT1_TONE, + OPLL_2413_TONE, + OPLL_281B_TONE +}; + +/* voice data */ +typedef struct __OPLL_PATCH { + uint32_t TL, FB, EG, ML, AR, DR, SL, RR, KR, KL, AM, PM, WS; +} OPLL_PATCH; + +/* slot */ +typedef struct __OPLL_SLOT { + uint8_t number; + + /* type flags: + * 000000SM + * |+-- M: 0:modulator 1:carrier + * +--- S: 0:normal 1:single slot mode (sd, tom, hh or cym) + */ + uint8_t type; + + OPLL_PATCH *patch; /* voice parameter */ + + /* slot output */ + int32_t output[2]; /* output value, latest and previous. */ + + /* phase generator (pg) */ + uint16_t *wave_table; /* wave table */ + uint32_t pg_phase; /* pg phase */ + uint32_t pg_out; /* pg output, as index of wave table */ + uint8_t pg_keep; /* if 1, pg_phase is preserved when key-on */ + uint16_t blk_fnum; /* (block << 9) | f-number */ + uint16_t fnum; /* f-number (9 bits) */ + uint8_t blk; /* block (3 bits) */ + + /* envelope generator (eg) */ + uint8_t eg_state; /* current state */ + int32_t volume; /* current volume */ + uint8_t key_flag; /* key-on flag 1:on 0:off */ + uint8_t sus_flag; /* key-sus option 1:on 0:off */ + uint16_t tll; /* total level + key scale level*/ + uint8_t rks; /* key scale offset (rks) for eg speed */ + uint8_t eg_rate_h; /* eg speed rate high 4bits */ + uint8_t eg_rate_l; /* eg speed rate low 2bits */ + uint32_t eg_shift; /* shift for eg global counter, controls envelope speed */ + uint32_t eg_out; /* eg output */ + + uint32_t update_requests; /* flags to debounce update */ + +#if OPLL_DEBUG + uint8_t last_eg_state; +#endif +} OPLL_SLOT; + +/* mask */ +#define OPLL_MASK_CH(x) (1 << (x)) +#define OPLL_MASK_HH (1 << (9)) +#define OPLL_MASK_CYM (1 << (10)) +#define OPLL_MASK_TOM (1 << (11)) +#define OPLL_MASK_SD (1 << (12)) +#define OPLL_MASK_BD (1 << (13)) +#define OPLL_MASK_RHYTHM (OPLL_MASK_HH | OPLL_MASK_CYM | OPLL_MASK_TOM | OPLL_MASK_SD | OPLL_MASK_BD) + +/* rate conveter */ +typedef struct __OPLL_RateConv { + int ch; + double timer; + double f_ratio; + int16_t *sinc_table; + int16_t **buf; +} OPLL_RateConv; + +OPLL_RateConv *OPLL_RateConv_new(double f_inp, double f_out, int ch); +void OPLL_RateConv_reset(OPLL_RateConv *conv); +void OPLL_RateConv_putData(OPLL_RateConv *conv, int ch, int16_t data); +int16_t OPLL_RateConv_getData(OPLL_RateConv *conv, int ch); +void OPLL_RateConv_delete(OPLL_RateConv *conv); + +typedef struct __OPLL { + uint32_t clk; + uint32_t rate; + + uint8_t chip_type; + + uint32_t adr; + + uint32_t inp_step; + uint32_t out_step; + uint32_t out_time; + + uint8_t reg[0x40]; + uint8_t test_flag; + uint32_t slot_key_status; + uint8_t rhythm_mode; + + uint32_t eg_counter; + + uint32_t pm_phase; + int32_t am_phase; + + uint8_t lfo_am; + + uint32_t noise; + uint8_t short_noise; + + int32_t patch_number[9]; + OPLL_SLOT slot[18]; + OPLL_PATCH patch[19 * 2]; + + uint8_t pan[16]; + float pan_fine[16][2]; + + uint32_t mask; + + /* channel output */ + /* 0..8:tone 9:bd 10:hh 11:sd 12:tom 13:cym */ + int16_t ch_out[14]; + + int16_t mix_out[2]; + + OPLL_RateConv *conv; +} OPLL; + +OPLL *OPLL_new(uint32_t clk, uint32_t rate); +void OPLL_delete(OPLL *); + +void OPLL_reset(OPLL *); +void OPLL_resetPatch(OPLL *, uint8_t); + +/** + * Set output wave sampling rate. + * @param rate sampling rate. If clock / 72 (typically 49716 or 49715 at 3.58MHz) is set, the internal rate converter is + * disabled. + */ +void OPLL_setRate(OPLL *opll, uint32_t rate); + +/** + * Set internal calcuration quality. Currently no effects, just for compatibility. + * >= v1.0.0 always synthesizes internal output at clock/72 Hz. + */ +void OPLL_setQuality(OPLL *opll, uint8_t q); + +/** + * Set pan pot (extra function - not YM2413 chip feature) + * @param ch 0..8:tone 9:bd 10:hh 11:sd 12:tom 13:cym 14,15:reserved + * @param pan 0:mute 1:right 2:left 3:center + * ``` + * pan: 76543210 + * |+- bit 1: enable Left output + * +-- bit 0: enable Right output + * ``` + */ +void OPLL_setPan(OPLL *opll, uint32_t ch, uint8_t pan); + +/** + * Set fine-grained panning + * @param ch 0..8:tone 9:bd 10:hh 11:sd 12:tom 13:cym 14,15:reserved + * @param pan output strength of left/right channel. + * pan[0]: left, pan[1]: right. pan[0]=pan[1]=1.0f for center. + */ +void OPLL_setPanFine(OPLL *opll, uint32_t ch, float pan[2]); + +/** + * Set chip type. If vrc7 is selected, r#14 is ignored. + * This method not change the current ROM patch set. + * To change ROM patch set, use OPLL_resetPatch. + * @param type 0:YM2413 1:VRC7 + */ +void OPLL_setChipType(OPLL *opll, uint8_t type); + +void OPLL_writeIO(OPLL *opll, uint32_t reg, uint8_t val); +void OPLL_writeReg(OPLL *opll, uint32_t reg, uint8_t val); + +/** + * Calculate one sample + */ +int16_t OPLL_calc(OPLL *opll); + +/** + * Calulate stereo sample + */ +void OPLL_calcStereo(OPLL *opll, int32_t out[2]); + +void OPLL_setPatch(OPLL *, const uint8_t *dump); +void OPLL_copyPatch(OPLL *, int32_t, OPLL_PATCH *); + +/** + * Force to refresh. + * External program should call this function after updating patch parameters. + */ +void OPLL_forceRefresh(OPLL *); + +void OPLL_dumpToPatch(const uint8_t *dump, OPLL_PATCH *patch); +void OPLL_patchToDump(const OPLL_PATCH *patch, uint8_t *dump); +void OPLL_getDefaultPatch(int32_t type, int32_t num, OPLL_PATCH *); + +/** + * Set channel mask + * @param mask mask flag: OPLL_MASK_* can be used. + * - bit 0..8: mask for ch 1 to 9 (OPLL_MASK_CH(i)) + * - bit 9: mask for Hi-Hat (OPLL_MASK_HH) + * - bit 10: mask for Top-Cym (OPLL_MASK_CYM) + * - bit 11: mask for Tom (OPLL_MASK_TOM) + * - bit 12: mask for Snare Drum (OPLL_MASK_SD) + * - bit 13: mask for Bass Drum (OPLL_MASK_BD) + */ +uint32_t OPLL_setMask(OPLL *, uint32_t mask); + +/** + * Toggler channel mask flag + */ +uint32_t OPLL_toggleMask(OPLL *, uint32_t mask); + +/* for compatibility */ +#define OPLL_set_rate OPLL_setRate +#define OPLL_set_quality OPLL_setQuality +#define OPLL_set_pan OPLL_setPan +#define OPLL_set_pan_fine OPLL_setPanFine +#define OPLL_calc_stereo OPLL_calcStereo +#define OPLL_reset_patch OPLL_resetPatch +#define OPLL_dump2patch OPLL_dumpToPatch +#define OPLL_patch2dump OPLL_patchToDump +#define OPLL_setChipMode OPLL_setChipType + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/extern/NSFplay/legacy/emutypes.h b/extern/NSFplay/legacy/emutypes.h new file mode 100644 index 000000000..bf5d7e1bf --- /dev/null +++ b/extern/NSFplay/legacy/emutypes.h @@ -0,0 +1,41 @@ +#ifndef _EMUTYPES_H_ +#define _EMUTYPES_H_ + +#if defined(_MSC_VER) +#define INLINE __forceinline +#elif defined(__GNUC__) +#define INLINE __inline__ +#elif defined(_MWERKS_) +#define INLINE inline +#else +#define INLINE +#endif + +#if defined(EMU_DLL_IMPORTS) +#define EMU2149_DLL_IMPORTS +#define EMU2212_DLL_IMPORTS +#define EMU2413_DLL_IMPORTS +#define EMU8950_DLL_IMPORTS +#define EMU76489_DLL_IMPORTS +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +typedef unsigned int e_uint; +typedef signed int e_int; + +typedef unsigned char e_uint8 ; +typedef signed char e_int8 ; + +typedef unsigned short e_uint16 ; +typedef signed short e_int16 ; + +typedef unsigned int e_uint32 ; +typedef signed int e_int32 ; + +#ifdef __cplusplus +} +#endif +#endif diff --git a/extern/NSFplay/legacy/vrc7tone_ft35.h b/extern/NSFplay/legacy/vrc7tone_ft35.h new file mode 100644 index 000000000..7af08bcf7 --- /dev/null +++ b/extern/NSFplay/legacy/vrc7tone_ft35.h @@ -0,0 +1,20 @@ +// patch set by Mitsutaka Okazaki used in FamiTracker 0.3.5 and prior (6/24/2001) +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x33, 0x01, 0x09, 0x0e, 0x94, 0x90, 0x40, 0x01, +0x13, 0x41, 0x0f, 0x0d, 0xce, 0xd3, 0x43, 0x13, +0x01, 0x12, 0x1b, 0x06, 0xff, 0xd2, 0x00, 0x32, +0x61, 0x61, 0x1b, 0x07, 0xaf, 0x63, 0x20, 0x28, +0x22, 0x21, 0x1e, 0x06, 0xf0, 0x76, 0x08, 0x28, +0x66, 0x21, 0x15, 0x00, 0x93, 0x94, 0x20, 0xf8, +0x21, 0x61, 0x1c, 0x07, 0x82, 0x81, 0x10, 0x17, +0x23, 0x21, 0x20, 0x1f, 0xc0, 0x71, 0x07, 0x47, +0x25, 0x31, 0x26, 0x05, 0x64, 0x41, 0x18, 0xf8, +0x17, 0x21, 0x28, 0x07, 0xff, 0x83, 0x02, 0xf8, +0x97, 0x81, 0x25, 0x07, 0xcf, 0xc8, 0x02, 0x14, +0x21, 0x21, 0x54, 0x0f, 0x80, 0x7f, 0x07, 0x07, +0x01, 0x01, 0x56, 0x03, 0xd3, 0xb2, 0x43, 0x58, +0x31, 0x21, 0x0c, 0x03, 0x82, 0xc0, 0x40, 0x07, +0x21, 0x01, 0x0c, 0x03, 0xd4, 0xd3, 0x40, 0x84, +0x04, 0x21, 0x28, 0x00, 0xdf, 0xf8, 0xff, 0xf8, +0x23, 0x22, 0x00, 0x00, 0xa8, 0xf8, 0xf8, 0xf8, +0x25, 0x18, 0x00, 0x00, 0xf8, 0xa9, 0xf8, 0x55, diff --git a/extern/NSFplay/legacy/vrc7tone_ft36.h b/extern/NSFplay/legacy/vrc7tone_ft36.h new file mode 100644 index 000000000..f693955e6 --- /dev/null +++ b/extern/NSFplay/legacy/vrc7tone_ft36.h @@ -0,0 +1,21 @@ +// patch set by quietust (1/18/2004), used in FamiTracker 0.3.6 +// Source: http://nesdev.com/cgi-bin/wwwthreads/showpost.pl?Board=NESemdev&Number=1440 +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x03, 0x21, 0x04, 0x06, 0x8D, 0xF2, 0x42, 0x17, +0x13, 0x41, 0x05, 0x0E, 0x99, 0x96, 0x63, 0x12, +0x31, 0x11, 0x10, 0x0A, 0xF0, 0x9C, 0x32, 0x02, +0x21, 0x61, 0x1D, 0x07, 0x9F, 0x64, 0x20, 0x27, +0x22, 0x21, 0x1E, 0x06, 0xF0, 0x76, 0x08, 0x28, +0x02, 0x01, 0x06, 0x00, 0xF0, 0xF2, 0x03, 0x95, +0x21, 0x61, 0x1C, 0x07, 0x82, 0x81, 0x16, 0x07, +0x23, 0x21, 0x1A, 0x17, 0xEF, 0x82, 0x25, 0x15, +0x25, 0x11, 0x1F, 0x00, 0x86, 0x41, 0x20, 0x11, +0x85, 0x01, 0x1F, 0x0F, 0xE4, 0xA2, 0x11, 0x12, +0x07, 0xC1, 0x2B, 0x45, 0xB4, 0xF1, 0x24, 0xF4, +0x61, 0x23, 0x11, 0x06, 0x96, 0x96, 0x13, 0x16, +0x01, 0x02, 0xD3, 0x05, 0x82, 0xA2, 0x31, 0x51, +0x61, 0x22, 0x0D, 0x02, 0xC3, 0x7F, 0x24, 0x05, +0x21, 0x62, 0x0E, 0x00, 0xA1, 0xA0, 0x44, 0x17, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, diff --git a/extern/NSFplay/legacy/vrc7tone_kt1.h b/extern/NSFplay/legacy/vrc7tone_kt1.h new file mode 100644 index 000000000..59cff8a3a --- /dev/null +++ b/extern/NSFplay/legacy/vrc7tone_kt1.h @@ -0,0 +1,21 @@ +// patch set 1 by kevtris (11/14/1999) +// http://kevtris.org/nes/vrcvii.txt +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x05, 0x03, 0x10, 0x06, 0x74, 0xA1, 0x13, 0xF4, +0x05, 0x01, 0x16, 0x00, 0xF9, 0xA2, 0x15, 0xF5, +0x01, 0x41, 0x11, 0x00, 0xA0, 0xA0, 0x83, 0x95, +0x01, 0x41, 0x17, 0x00, 0x60, 0xF0, 0x83, 0x95, +0x24, 0x41, 0x1F, 0x00, 0x50, 0xB0, 0x94, 0x94, +0x05, 0x01, 0x0B, 0x04, 0x65, 0xA0, 0x54, 0x95, +0x11, 0x41, 0x0E, 0x04, 0x70, 0xC7, 0x13, 0x10, +0x02, 0x44, 0x16, 0x06, 0xE0, 0xE0, 0x31, 0x35, +0x48, 0x22, 0x22, 0x07, 0x50, 0xA1, 0xA5, 0xF4, +0x05, 0xA1, 0x18, 0x00, 0xA2, 0xA2, 0xF5, 0xF5, +0x07, 0x81, 0x2B, 0x05, 0xA5, 0xA5, 0x03, 0x03, +0x01, 0x41, 0x08, 0x08, 0xA0, 0xA0, 0x83, 0x95, +0x21, 0x61, 0x12, 0x00, 0x93, 0x92, 0x74, 0x75, +0x21, 0x62, 0x21, 0x00, 0x84, 0x85, 0x34, 0x15, +0x21, 0x62, 0x0E, 0x00, 0xA1, 0xA0, 0x34, 0x15, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, diff --git a/extern/NSFplay/legacy/vrc7tone_kt2.h b/extern/NSFplay/legacy/vrc7tone_kt2.h new file mode 100644 index 000000000..92caa42bf --- /dev/null +++ b/extern/NSFplay/legacy/vrc7tone_kt2.h @@ -0,0 +1,21 @@ +// patch set 2 by kevtris (11/15/1999) +// http://kevtris.org/nes/vrcvii.txt +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x31, 0x22, 0x23, 0x07, 0xF0, 0xF0, 0xE8, 0xF7, +0x03, 0x31, 0x68, 0x05, 0xF2, 0x74, 0x79, 0x9C, +0x01, 0x51, 0x72, 0x04, 0xF1, 0xD3, 0x9D, 0x8B, +0x22, 0x61, 0x1B, 0x05, 0xC0, 0xA1, 0xF8, 0xE8, +0x22, 0x61, 0x2C, 0x03, 0xD2, 0xA1, 0xA7, 0xE8, +0x31, 0x22, 0xFA, 0x01, 0xF1, 0xF1, 0xF4, 0xEE, +0x21, 0x61, 0x28, 0x06, 0xF1, 0xF1, 0xCE, 0x9B, +0x27, 0x61, 0x60, 0x00, 0xF0, 0xF0, 0xFF, 0xFD, +0x60, 0x21, 0x2B, 0x06, 0x85, 0xF1, 0x79, 0x9D, +0x31, 0xA1, 0xFF, 0x0A, 0x53, 0x62, 0x5E, 0xAF, +0x03, 0xA1, 0x70, 0x0F, 0xD4, 0xA3, 0x94, 0xBE, +0x2B, 0x61, 0xE4, 0x07, 0xF6, 0x93, 0xBD, 0xAC, +0x21, 0x63, 0xED, 0x07, 0x77, 0xF1, 0xC7, 0xE8, +0x21, 0x61, 0x2A, 0x03, 0xF3, 0xE2, 0xB6, 0xD9, +0x21, 0x63, 0x37, 0x03, 0xF3, 0xE2, 0xB6, 0xD9, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, diff --git a/extern/NSFplay/legacy/vrc7tone_mo.h b/extern/NSFplay/legacy/vrc7tone_mo.h new file mode 100644 index 000000000..3b216f874 --- /dev/null +++ b/extern/NSFplay/legacy/vrc7tone_mo.h @@ -0,0 +1,20 @@ +/* VRC7 TONES by okazaki@angel.ne.jp (4/10/2004) */ +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x33,0x01,0x09,0x0e,0x94,0x90,0x40,0x01, +0x13,0x41,0x0f,0x0d,0xce,0xd3,0x43,0x13, +0x01,0x12,0x1b,0x06,0xff,0xd2,0x00,0x32, +0x61,0x61,0x1b,0x07,0xaf,0x63,0x20,0x28, +0x22,0x21,0x1e,0x06,0xf0,0x76,0x08,0x28, +0x66,0x21,0x15,0x00,0x93,0x94,0x20,0xf8, +0x21,0x61,0x1c,0x07,0x82,0x81,0x10,0x17, +0x23,0x21,0x20,0x1f,0xc0,0x71,0x07,0x47, +0x25,0x31,0x26,0x05,0x64,0x41,0x18,0xf8, +0x17,0x21,0x28,0x07,0xff,0x83,0x02,0xf8, +0x97,0x81,0x25,0x07,0xcf,0xc8,0x02,0x14, +0x21,0x21,0x54,0x0f,0x80,0x7f,0x07,0x07, +0x01,0x01,0x56,0x03,0xd3,0xb2,0x43,0x58, +0x31,0x21,0x0c,0x03,0x82,0xc0,0x40,0x07, +0x21,0x01,0x0c,0x03,0xd4,0xd3,0x40,0x84, +0x07,0x21,0x14,0x00,0xee,0xf8,0xff,0xf8, +0x01,0x31,0x00,0x00,0xf8,0xf7,0xf8,0xf7, +0x25,0x11,0x00,0x00,0xf8,0xfa,0xf8,0x55, diff --git a/extern/NSFplay/legacy/vrc7tone_nuke.h b/extern/NSFplay/legacy/vrc7tone_nuke.h new file mode 100644 index 000000000..0afcc4ce0 --- /dev/null +++ b/extern/NSFplay/legacy/vrc7tone_nuke.h @@ -0,0 +1,21 @@ +// patch set by Nuke.YKT (3/15/2019) +// https://siliconpr0n.org/archive/doku.php?id=vendor:yamaha:opl2#ym2413_instruments +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x03, 0x21, 0x05, 0x06, 0xE8, 0x81, 0x42, 0x27, +0x13, 0x41, 0x14, 0x0D, 0xD8, 0xF6, 0x23, 0x12, +0x11, 0x11, 0x08, 0x08, 0xFA, 0xB2, 0x20, 0x12, +0x31, 0x61, 0x0C, 0x07, 0xA8, 0x64, 0x61, 0x27, +0x32, 0x21, 0x1E, 0x06, 0xE1, 0x76, 0x01, 0x28, +0x02, 0x01, 0x06, 0x00, 0xA3, 0xE2, 0xF4, 0xF4, +0x21, 0x61, 0x1D, 0x07, 0x82, 0x81, 0x11, 0x07, +0x23, 0x21, 0x22, 0x17, 0xA2, 0x72, 0x01, 0x17, +0x35, 0x11, 0x25, 0x00, 0x40, 0x73, 0x72, 0x01, +0xB5, 0x01, 0x0F, 0x0F, 0xA8, 0xA5, 0x51, 0x02, +0x17, 0xC1, 0x24, 0x07, 0xF8, 0xF8, 0x22, 0x12, +0x71, 0x23, 0x11, 0x06, 0x65, 0x74, 0x18, 0x16, +0x01, 0x02, 0xD3, 0x05, 0xC9, 0x95, 0x03, 0x02, +0x61, 0x63, 0x0C, 0x00, 0x94, 0xC0, 0x33, 0xF6, +0x21, 0x72, 0x0D, 0x00, 0xC1, 0xD5, 0x56, 0x06, +0x01, 0x01, 0x18, 0x0F, 0xDF, 0xF8, 0x6A, 0x6D, +0x01, 0x01, 0x00, 0x00, 0xC8, 0xD8, 0xA7, 0x68, +0x05, 0x01, 0x00, 0x00, 0xF8, 0xAA, 0x59, 0x55, diff --git a/extern/NSFplay/legacy/vrc7tone_rw.h b/extern/NSFplay/legacy/vrc7tone_rw.h new file mode 100644 index 000000000..bf2261208 --- /dev/null +++ b/extern/NSFplay/legacy/vrc7tone_rw.h @@ -0,0 +1,21 @@ +// patch set by rainwarrior (8/01/2012) +// http://forums.nesdev.com/viewtopic.php?f=6&t=9141 +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x03, 0x21, 0x05, 0x06, 0xB8, 0x82, 0x42, 0x27, +0x13, 0x41, 0x13, 0x0D, 0xD8, 0xD6, 0x23, 0x12, +0x31, 0x11, 0x08, 0x08, 0xFA, 0x9A, 0x22, 0x02, +0x31, 0x61, 0x18, 0x07, 0x78, 0x64, 0x30, 0x27, +0x22, 0x21, 0x1E, 0x06, 0xF0, 0x76, 0x08, 0x28, +0x02, 0x01, 0x06, 0x00, 0xF0, 0xF2, 0x03, 0xF5, +0x21, 0x61, 0x1D, 0x07, 0x82, 0x81, 0x16, 0x07, +0x23, 0x21, 0x1A, 0x17, 0xCF, 0x72, 0x25, 0x17, +0x15, 0x11, 0x25, 0x00, 0x4F, 0x71, 0x00, 0x11, +0x85, 0x01, 0x12, 0x0F, 0x99, 0xA2, 0x40, 0x02, +0x07, 0xC1, 0x69, 0x07, 0xF3, 0xF5, 0xA7, 0x12, +0x71, 0x23, 0x0D, 0x06, 0x66, 0x75, 0x23, 0x16, +0x01, 0x02, 0xD3, 0x05, 0xA3, 0x92, 0xF7, 0x52, +0x61, 0x63, 0x0C, 0x00, 0x94, 0xAF, 0x34, 0x06, +0x21, 0x62, 0x0D, 0x00, 0xB1, 0xA0, 0x54, 0x17, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, diff --git a/extern/NSFplay/nes_apu.cpp b/extern/NSFplay/nes_apu.cpp new file mode 100644 index 000000000..9d50dfc78 --- /dev/null +++ b/extern/NSFplay/nes_apu.cpp @@ -0,0 +1,400 @@ +// +// NES 2A03 +// +#include +#include "nes_apu.h" + +namespace xgm +{ + void NES_APU::sweep_sqr (int i) + { + int shifted = freq[i] >> sweep_amount[i]; + if (i == 0 && sweep_mode[i]) shifted += 1; + sfreq[i] = freq[i] + (sweep_mode[i] ? -shifted : shifted); + //DEBUG_OUT("shifted[%d] = %d (%d >> %d)\n",i,shifted,freq[i],sweep_amount[i]); + } + + void NES_APU::FrameSequence(int s) + { + //DEBUG_OUT("FrameSequence(%d)\n",s); + + if (s > 3) return; // no operation in step 4 + + // 240hz clock + for (int i=0; i < 2; ++i) + { + bool divider = false; + if (envelope_write[i]) + { + envelope_write[i] = false; + envelope_counter[i] = 15; + envelope_div[i] = 0; + } + else + { + ++envelope_div[i]; + if (envelope_div[i] > envelope_div_period[i]) + { + divider = true; + envelope_div[i] = 0; + } + } + if (divider) + { + if (envelope_loop[i] && envelope_counter[i] == 0) + envelope_counter[i] = 15; + else if (envelope_counter[i] > 0) + --envelope_counter[i]; + } + } + + // 120hz clock + if ((s&1) == 0) + for (int i=0; i < 2; ++i) + { + if (!envelope_loop[i] && (length_counter[i] > 0)) + --length_counter[i]; + + if (sweep_enable[i]) + { + //DEBUG_OUT("Clock sweep: %d\n", i); + + --sweep_div[i]; + if (sweep_div[i] <= 0) + { + sweep_sqr(i); // calculate new sweep target + + //DEBUG_OUT("sweep_div[%d] (0/%d)\n",i,sweep_div_period[i]); + //DEBUG_OUT("freq[%d]=%d > sfreq[%d]=%d\n",i,freq[i],i,sfreq[i]); + + if (freq[i] >= 8 && sfreq[i] < 0x800 && sweep_amount[i] > 0) // update frequency if appropriate + { + freq[i] = sfreq[i] < 0 ? 0 : sfreq[i]; + } + sweep_div[i] = sweep_div_period[i] + 1; + + //DEBUG_OUT("freq[%d]=%d\n",i,freq[i]); + } + + if (sweep_write[i]) + { + sweep_div[i] = sweep_div_period[i] + 1; + sweep_write[i] = false; + } + } + } + + } + + INT32 NES_APU::calc_sqr (int i, UINT32 clocks) + { + static const INT16 sqrtbl[4][16] = { + {0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0}, + {1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1} + }; + + scounter[i] -= clocks; + while (scounter[i] < 0) + { + sphase[i] = (sphase[i] + 1) & 15; + scounter[i] += freq[i] + 1; + } + + INT32 ret = 0; + if (length_counter[i] > 0 && + freq[i] >= 8 && + sfreq[i] < 0x800 + ) + { + int v = envelope_disable[i] ? volume[i] : envelope_counter[i]; + ret = sqrtbl[duty[i]][sphase[i]] ? v : 0; + } + + return ret; + } + + bool NES_APU::Read (UINT32 adr, UINT32 & val, UINT32 id) + { + if (0x4000 <= adr && adr < 0x4008) + { + val |= reg[adr&0x7]; + return true; + } + else if(adr==0x4015) + { + val |= (length_counter[1]?2:0)|(length_counter[0]?1:0); + return true; + } + else + return false; + } + + void NES_APU::Tick (UINT32 clocks) + { + out[0] = calc_sqr(0, clocks); + out[1] = calc_sqr(1, clocks); + } + + // ¶¬‚³‚ê‚é”gŒ`‚ÌU•‚Í0-8191 + UINT32 NES_APU::Render (INT32 b[2]) + { + out[0] = (mask & 1) ? 0 : out[0]; + out[1] = (mask & 2) ? 0 : out[1]; + + INT32 m[2]; + + if(option[OPT_NONLINEAR_MIXER]) + { + INT32 voltage = square_table[out[0] + out[1]]; + m[0] = out[0] << 6; + m[1] = out[1] << 6; + INT32 ref = m[0] + m[1]; + if (ref > 0) + { + m[0] = (m[0] * voltage) / ref; + m[1] = (m[1] * voltage) / ref; + } + else + { + m[0] = voltage; + m[1] = voltage; + } + } + else + { + m[0] = (out[0] * square_linear) / 15; + m[1] = (out[1] * square_linear) / 15; + } + + b[0] = m[0] * sm[0][0]; + b[0] += m[1] * sm[0][1]; + b[0] >>= 7; + + b[1] = m[0] * sm[1][0]; + b[1] += m[1] * sm[1][1]; + b[1] >>= 7; + + return 2; + } + + NES_APU::NES_APU () + { + SetClock (DEFAULT_CLOCK); + SetRate (DEFAULT_RATE); + option[OPT_UNMUTE_ON_RESET] = true; + option[OPT_PHASE_REFRESH] = true; + option[OPT_NONLINEAR_MIXER] = true; + option[OPT_DUTY_SWAP] = false; + option[OPT_NEGATE_SWEEP_INIT] = false; + + square_table[0] = 0; + for(int i=1;i<32;i++) + square_table[i]=(INT32)((8192.0*95.88)/(8128.0/i+100)); + + square_linear = square_table[15]; // match linear scale to one full volume square of nonlinear + + for(int c=0;c<2;++c) + for(int t=0;t<2;++t) + sm[c][t] = 128; + } + + NES_APU::~NES_APU () + { + } + + void NES_APU::Reset () + { + int i; + gclock = 0; + mask = 0; + + for (int i=0; i<2; ++i) + { + scounter[i] = 0; + sphase[i] = 0; + duty[i] = 0; + volume[i] = 0; + freq[i] = 0; + sfreq[i] = 0; + sweep_enable[i] = 0; + sweep_mode[i] = 0; + sweep_write[i] = 0; + sweep_div_period[i] = 0; + sweep_div[i] = 1; + sweep_amount[i] = 0; + envelope_disable[i] = 0; + envelope_loop[i] = 0; + envelope_write[i] = 0; + envelope_div_period[i] = 0; + envelope_div[0] = 0; + envelope_counter[i] = 0; + length_counter[i] = 0; + enable[i] = 0; + } + + for (i = 0x4000; i < 0x4008; i++) + Write (i, 0); + + Write (0x4015, 0); + if (option[OPT_UNMUTE_ON_RESET]) + Write (0x4015, 0x0f); + if (option[OPT_NEGATE_SWEEP_INIT]) + { + Write (0x4001, 0x08); + Write (0x4005, 0x08); + } + + for (i = 0; i < 2; i++) + out[i] = 0; + + SetRate(rate); + } + + void NES_APU::SetOption (int id, int val) + { + if(id 1) return; + sm[0][trk] = mixl; + sm[1][trk] = mixr; + } + + ITrackInfo *NES_APU::GetTrackInfo(int trk) + { + trkinfo[trk]._freq = freq[trk]; + if(freq[trk]) + trkinfo[trk].freq = clock/16/(freq[trk] + 1); + else + trkinfo[trk].freq = 0; + + trkinfo[trk].output = out[trk]; + trkinfo[trk].volume = volume[trk]+(envelope_disable[trk]?0:0x10)+(envelope_loop[trk]?0x20:0); + trkinfo[trk].key = + enable[trk] && + length_counter[trk] > 0 && + freq[trk] >= 8 && + sfreq[trk] < 0x800 && + (envelope_disable[trk] ? volume[trk] : (envelope_counter[trk] > 0)); + trkinfo[trk].tone = duty[trk]; + trkinfo[trk].max_volume = 15; + return &trkinfo[trk]; + } + + bool NES_APU::Write (UINT32 adr, UINT32 val, UINT32 id) + { + int ch; + + static const UINT8 length_table[32] = { + 0x0A, 0xFE, + 0x14, 0x02, + 0x28, 0x04, + 0x50, 0x06, + 0xA0, 0x08, + 0x3C, 0x0A, + 0x0E, 0x0C, + 0x1A, 0x0E, + 0x0C, 0x10, + 0x18, 0x12, + 0x30, 0x14, + 0x60, 0x16, + 0xC0, 0x18, + 0x48, 0x1A, + 0x10, 0x1C, + 0x20, 0x1E + }; + + if (0x4000 <= adr && adr < 0x4008) + { + //DEBUG_OUT("$%04X = %02X\n",adr,val); + + adr &= 0xf; + ch = adr >> 2; + switch (adr) + { + case 0x0: + case 0x4: + volume[ch] = val & 15; + envelope_disable[ch] = (val >> 4) & 1; + envelope_loop[ch] = (val >> 5) & 1; + envelope_div_period[ch] = (val & 15); + duty[ch] = (val >> 6) & 3; + if (option[OPT_DUTY_SWAP]) + { + if (duty[ch] == 1) duty[ch] = 2; + else if (duty[ch] == 2) duty[ch] = 1; + } + break; + + case 0x1: + case 0x5: + sweep_enable[ch] = (val >> 7) & 1; + sweep_div_period[ch] = (((val >> 4) & 7)); + sweep_mode[ch] = (val >> 3) & 1; + sweep_amount[ch] = val & 7; + sweep_write[ch] = true; + sweep_sqr(ch); + break; + + case 0x2: + case 0x6: + freq[ch] = val | (freq[ch] & 0x700) ; + sweep_sqr(ch); + break; + + case 0x3: + case 0x7: + freq[ch] = (freq[ch] & 0xFF) | ((val & 0x7) << 8) ; + if (option[OPT_PHASE_REFRESH]) + sphase[ch] = 0; + envelope_write[ch] = true; + if (enable[ch]) + { + length_counter[ch] = length_table[(val >> 3) & 0x1f]; + } + sweep_sqr(ch); + break; + + default: + return false; + } + reg[adr] = val; + return true; + } + else if (adr == 0x4015) + { + enable[0] = (val & 1) ? true : false; + enable[1] = (val & 2) ? true : false; + + if (!enable[0]) + length_counter[0] = 0; + if (!enable[1]) + length_counter[1] = 0; + + reg[adr-0x4000] = val; + return true; + } + + // 4017 is handled in nes_dmc.cpp + //else if (adr == 0x4017) + //{ + //} + + return false; + } +} // namespace xgm; diff --git a/extern/NSFplay/nes_apu.h b/extern/NSFplay/nes_apu.h new file mode 100644 index 000000000..2a8e75412 --- /dev/null +++ b/extern/NSFplay/nes_apu.h @@ -0,0 +1,88 @@ +#ifndef _NES_APU_H_ +#define _NES_APU_H_ +#include "../device.h" +#include "nes_dmc.h" + +namespace xgm +{ + /** Upper half of APU **/ + class NES_APU : public ISoundChip + { + public: + enum + { + OPT_UNMUTE_ON_RESET=0, + OPT_PHASE_REFRESH, + OPT_NONLINEAR_MIXER, + OPT_DUTY_SWAP, + OPT_NEGATE_SWEEP_INIT, + OPT_END }; + + enum + { SQR0_MASK = 1, SQR1_MASK = 2, }; + + protected: + int option[OPT_END]; // ŠeŽíƒIƒvƒVƒ‡ƒ“ + int mask; + INT32 sm[2][2]; + + UINT32 gclock; + UINT8 reg[0x20]; + INT32 out[2]; + double rate, clock; + + INT32 square_table[32]; // nonlinear mixer + INT32 square_linear; // linear mix approximation + + int scounter[2]; // frequency divider + int sphase[2]; // phase counter + + int duty[2]; + int volume[2]; + int freq[2]; + int sfreq[2]; + + bool sweep_enable[2]; + bool sweep_mode[2]; + bool sweep_write[2]; + int sweep_div_period[2]; + int sweep_div[2]; + int sweep_amount[2]; + + bool envelope_disable[2]; + bool envelope_loop[2]; + bool envelope_write[2]; + int envelope_div_period[2]; + int envelope_div[2]; + int envelope_counter[2]; + + int length_counter[2]; + + bool enable[2]; + + void sweep_sqr (int ch); // calculates target sweep frequency + INT32 calc_sqr (int ch, UINT32 clocks); + TrackInfoBasic trkinfo[2]; + + public: + NES_APU (); + ~NES_APU (); + + void FrameSequence(int s); + + virtual void Reset (); + virtual void Tick (UINT32 clocks); + virtual UINT32 Render (INT32 b[2]); + virtual bool Read (UINT32 adr, UINT32 & val, UINT32 id=0); + virtual bool Write (UINT32 adr, UINT32 val, UINT32 id=0); + virtual void SetRate (double rate); + virtual void SetClock (double clock); + virtual void SetOption (int id, int b); + virtual void SetMask(int m){ mask = m; } + virtual void SetStereoMix (int trk, xgm::INT16 mixl, xgm::INT16 mixr); + virtual ITrackInfo *GetTrackInfo(int trk); + }; + +} // namespace + +#endif diff --git a/extern/NSFplay/nes_dmc.cpp b/extern/NSFplay/nes_dmc.cpp new file mode 100644 index 000000000..8073a22ca --- /dev/null +++ b/extern/NSFplay/nes_dmc.cpp @@ -0,0 +1,771 @@ +#include "nes_dmc.h" +#include "nes_apu.h" +#include + +namespace xgm +{ + const UINT32 NES_DMC::wavlen_table[2][16] = { + { // NTSC + 4, 8, 16, 32, 64, 96, 128, 160, 202, 254, 380, 508, 762, 1016, 2034, 4068 + }, + { // PAL + 4, 8, 14, 30, 60, 88, 118, 148, 188, 236, 354, 472, 708, 944, 1890, 3778 + }}; + + const UINT32 NES_DMC::freq_table[2][16] = { + { // NTSC + 428, 380, 340, 320, 286, 254, 226, 214, 190, 160, 142, 128, 106, 84, 72, 54 + }, + { // PAL + 398, 354, 316, 298, 276, 236, 210, 198, 176, 148, 132, 118, 98, 78, 66, 50 + }}; + + const UINT32 BITREVERSE[256] = { + 0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0, + 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8, + 0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4, + 0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC, + 0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2, + 0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA, 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA, + 0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6, + 0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE, + 0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1, 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1, + 0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9, + 0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5, + 0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED, 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD, + 0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3, + 0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB, + 0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7, 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7, + 0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF, + }; + + NES_DMC::NES_DMC () : GETA_BITS (20) + { + SetClock (DEFAULT_CLOCK); + SetRate (DEFAULT_RATE); + SetPal (false); + option[OPT_ENABLE_4011] = 1; + option[OPT_ENABLE_PNOISE] = 1; + option[OPT_UNMUTE_ON_RESET] = 1; + option[OPT_DPCM_ANTI_CLICK] = 0; + option[OPT_NONLINEAR_MIXER] = 1; + option[OPT_RANDOMIZE_NOISE] = 1; + option[OPT_RANDOMIZE_TRI] = 1; + option[OPT_TRI_MUTE] = 1; + option[OPT_DPCM_REVERSE] = 0; + tnd_table[0][0][0][0] = 0; + tnd_table[1][0][0][0] = 0; + + apu = NULL; + frame_sequence_count = 0; + frame_sequence_length = 7458; + frame_sequence_steps = 4; + + for(int c=0;c<2;++c) + for(int t=0;t<3;++t) + sm[c][t] = 128; + } + + + NES_DMC::~NES_DMC () + { + } + + void NES_DMC::SetStereoMix(int trk, xgm::INT16 mixl, xgm::INT16 mixr) + { + if (trk < 0) return; + if (trk > 2) return; + sm[0][trk] = mixl; + sm[1][trk] = mixr; + } + + ITrackInfo *NES_DMC::GetTrackInfo(int trk) + { + switch(trk) + { + case 0: + trkinfo[trk].max_volume = 255; + trkinfo[0].key = (linear_counter>0 && length_counter[0]>0 && enable[0]); + trkinfo[0].volume = 0; + trkinfo[0]._freq = tri_freq; + if(trkinfo[0]._freq) + trkinfo[0].freq = clock/32/(trkinfo[0]._freq + 1); + else + trkinfo[0].freq = 0; + trkinfo[0].tone = -1; + trkinfo[0].output = out[0]; + break; + case 1: + trkinfo[1].max_volume = 15; + trkinfo[1].volume = noise_volume+(envelope_disable?0:0x10)+(envelope_loop?0x20:0); + trkinfo[1].key = length_counter[1]>0 && enable[1] && + (envelope_disable ? (noise_volume>0) : (envelope_counter>0)); + trkinfo[1]._freq = reg[0x400e - 0x4008]&0xF; + trkinfo[1].freq = clock/double(wavlen_table[pal][trkinfo[1]._freq] * ((noise_tap&(1<<6)) ? 93 : 1)); + trkinfo[1].tone = noise_tap & (1<<6); + trkinfo[1].output = out[1]; + break; + case 2: + trkinfo[2].max_volume = 127; + trkinfo[2].volume = reg[0x4011 - 0x4008]&0x7F; + trkinfo[2].key = dlength > 0; + trkinfo[2]._freq = reg[0x4010 - 0x4008]&0xF; + trkinfo[2].freq = clock/double(freq_table[pal][trkinfo[2]._freq]); + trkinfo[2].tone = (0xc000|(adr_reg<<6)); + trkinfo[2].output = (damp<<1)|dac_lsb; + break; + default: + return NULL; + } + return &trkinfo[trk]; + } + + void NES_DMC::FrameSequence(int s) + { + //DEBUG_OUT("FrameSequence: %d\n",s); + + if (s > 3) return; // no operation in step 4 + + if (apu) + { + apu->FrameSequence(s); + } + + if (s == 0 && (frame_sequence_steps == 4)) + { + if (frame_irq_enable) frame_irq = true; + cpu->UpdateIRQ(NES_CPU::IRQD_FRAME, frame_irq & frame_irq_enable); + } + + // 240hz clock + { + // triangle linear counter + if (linear_counter_halt) + { + linear_counter = linear_counter_reload; + } + else + { + if (linear_counter > 0) --linear_counter; + } + if (!linear_counter_control) + { + linear_counter_halt = false; + } + + // noise envelope + bool divider = false; + if (envelope_write) + { + envelope_write = false; + envelope_counter = 15; + envelope_div = 0; + } + else + { + ++envelope_div; + if (envelope_div > envelope_div_period) + { + divider = true; + envelope_div = 0; + } + } + if (divider) + { + if (envelope_loop && envelope_counter == 0) + envelope_counter = 15; + else if (envelope_counter > 0) + --envelope_counter; + } + } + + // 120hz clock + if ((s&1) == 0) + { + // triangle length counter + if (!linear_counter_control && (length_counter[0] > 0)) + --length_counter[0]; + + // noise length counter + if (!envelope_loop && (length_counter[1] > 0)) + --length_counter[1]; + } + + } + + // ŽOŠp”gƒ`ƒƒƒ“ƒlƒ‹‚ÌŒvŽZ –ß‚è’l‚Í0-15 + UINT32 NES_DMC::calc_tri (UINT32 clocks) + { + static UINT32 tritbl[32] = + { + 15,14,13,12,11,10, 9, 8, + 7, 6, 5, 4, 3, 2, 1, 0, + 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9,10,11,12,13,14,15, + }; + + if (linear_counter > 0 && length_counter[0] > 0 + && (!option[OPT_TRI_MUTE] || tri_freq > 0)) + { + counter[0] -= clocks; + while (counter[0] < 0) + { + tphase = (tphase + 1) & 31; + counter[0] += (tri_freq + 1); + } + } + + UINT32 ret = tritbl[tphase]; + return ret; + } + + // ƒmƒCƒYƒ`ƒƒƒ“ƒlƒ‹‚ÌŒvŽZ –ß‚è’l‚Í0-127 + // ’áƒTƒ“ƒvƒŠƒ“ƒOƒŒ[ƒg‚Ҭ‚·‚邯ƒGƒCƒŠƒAƒXƒmƒCƒY‚ªŒƒ‚µ‚¢‚̂Š+ // ƒmƒCƒY‚¾‚¯‚Í‚±‚ÌŠÖ”“à‚Å‚ƒNƒƒbƒN‡¬‚µAŠÈˆÕ‚ȃTƒ“ƒvƒŠƒ“ƒOƒŒ[ƒg + // •ÏŠ·‚ðs‚Á‚Ä‚¢‚éB + UINT32 NES_DMC::calc_noise(UINT32 clocks) + { + UINT32 env = envelope_disable ? noise_volume : envelope_counter; + if (length_counter[1] < 1) env = 0; + + UINT32 last = (noise & 0x4000) ? 0 : env; + if (clocks < 1) return last; + + // simple anti-aliasing (noise requires it, even when oversampling is off) + UINT32 count = 0; + UINT32 accum = counter[1] * last; // samples pending from previous calc + UINT32 accum_clocks = counter[1]; + #ifdef _DEBUG + INT32 start_clocks = counter[1]; + #endif + if (counter[1] < 0) // only happens on startup when using the randomize noise option + { + accum = 0; + accum_clocks = 0; + } + + counter[1] -= clocks; + assert (nfreq > 0); // prevent infinite loop + while (counter[1] < 0) + { + // tick the noise generator + UINT32 feedback = (noise&1) ^ ((noise&noise_tap)?1:0); + noise = (noise>>1) | (feedback<<14); + + last = (noise & 0x4000) ? 0 : env; + accum += (last * nfreq); + counter[1] += nfreq; + ++count; + accum_clocks += nfreq; + } + + if (count < 1) // no change over interval, don't anti-alias + { + return last; + } + + accum -= (last * counter[1]); // remove these samples which belong in the next calc + accum_clocks -= counter[1]; + #ifdef _DEBUG + if (start_clocks >= 0) assert(accum_clocks == clocks); // these should be equal + #endif + + UINT32 average = accum / accum_clocks; + assert(average <= 15); // above this would indicate overflow + return average; + } + + // Tick the DMC for the number of clocks, and return output counter; + UINT32 NES_DMC::calc_dmc (UINT32 clocks) + { + counter[2] -= clocks; + assert (dfreq > 0); // prevent infinite loop + while (counter[2] < 0) + { + counter[2] += dfreq; + + if ( data > 0x100 ) // data = 0x100 when shift register is empty + { + if (!empty) + { + if ((data & 1) && (damp < 63)) + damp++; + else if (!(data & 1) && (0 < damp)) + damp--; + } + data >>=1; + } + + if ( data <= 0x100 ) // shift register is empty + { + if (dlength > 0) + { + memory->Read (daddress, data); + cpu->StealCycles(4); // DMC read takes 3 or 4 CPU cycles, usually 4 + // (checking for the 3-cycle case would require sub-instruction emulation) + data &= 0xFF; // read 8 bits + if (option[OPT_DPCM_REVERSE]) data = BITREVERSE[data]; + data |= 0x10000; // use an extra bit to signal end of data + empty = false; + daddress = ((daddress+1)&0xFFFF)|0x8000 ; + --dlength; + if (dlength == 0) + { + if (mode & 1) // looped DPCM = auto-reload + { + daddress = ((adr_reg<<6)|0xC000); + dlength = (len_reg<<4)+1; + } + else if (mode & 2) // IRQ and not looped + { + irq = true; + cpu->UpdateIRQ(NES_CPU::IRQD_DMC, true); + } + } + } + else + { + data = 0x10000; // DMC will do nothing + empty = true; + } + } + } + + return (damp<<1) + dac_lsb; + } + + void NES_DMC::TickFrameSequence (UINT32 clocks) + { + frame_sequence_count += clocks; + while (frame_sequence_count > frame_sequence_length) + { + FrameSequence(frame_sequence_step); + frame_sequence_count -= frame_sequence_length; + ++frame_sequence_step; + if(frame_sequence_step >= frame_sequence_steps) + frame_sequence_step = 0; + } + } + + void NES_DMC::Tick (UINT32 clocks) + { + out[0] = calc_tri(clocks); + out[1] = calc_noise(clocks); + out[2] = calc_dmc(clocks); + } + + UINT32 NES_DMC::Render (INT32 b[2]) + { + out[0] = (mask & 1) ? 0 : out[0]; + out[1] = (mask & 2) ? 0 : out[1]; + out[2] = (mask & 4) ? 0 : out[2]; + + INT32 m[3]; + m[0] = tnd_table[0][out[0]][0][0]; + m[1] = tnd_table[0][0][out[1]][0]; + m[2] = tnd_table[0][0][0][out[2]]; + + if (option[OPT_NONLINEAR_MIXER]) + { + INT32 ref = m[0] + m[1] + m[2]; + INT32 voltage = tnd_table[1][out[0]][out[1]][out[2]]; + if (ref) + { + for (int i=0; i < 3; ++i) + m[i] = (m[i] * voltage) / ref; + } + else + { + for (int i=0; i < 3; ++i) + m[i] = voltage; + } + } + + // anti-click nullifies any 4011 write but preserves nonlinearity + if (option[OPT_DPCM_ANTI_CLICK]) + { + if (dmc_pop) // $4011 will cause pop this frame + { + // adjust offset to counteract pop + dmc_pop_offset += dmc_pop_follow - m[2]; + dmc_pop = false; + + // prevent overflow, keep headspace at edges + const INT32 OFFSET_MAX = (1 << 30) - (4 << 16); + if (dmc_pop_offset > OFFSET_MAX) dmc_pop_offset = OFFSET_MAX; + if (dmc_pop_offset < -OFFSET_MAX) dmc_pop_offset = -OFFSET_MAX; + } + dmc_pop_follow = m[2]; // remember previous position + + m[2] += dmc_pop_offset; // apply offset + + // TODO implement this in a better way + // roll off offset (not ideal, but prevents overflow) + if (dmc_pop_offset > 0) --dmc_pop_offset; + else if (dmc_pop_offset < 0) ++dmc_pop_offset; + } + + b[0] = m[0] * sm[0][0]; + b[0] += m[1] * sm[0][1]; + b[0] += m[2] * sm[0][2]; + b[0] >>= 7; + + b[1] = m[0] * sm[1][0]; + b[1] += m[1] * sm[1][1]; + b[1] += m[2] * sm[1][2]; + b[1] >>= 7; + + return 2; + } + + void NES_DMC::SetClock (double c) + { + clock = c; + } + + void NES_DMC::SetRate (double r) + { + rate = (UINT32)(r?r:DEFAULT_RATE); + } + + void NES_DMC::SetPal (bool is_pal) + { + pal = (is_pal ? 1 : 0); + // set CPU cycles in frame_sequence + frame_sequence_length = is_pal ? 8314 : 7458; + } + + void NES_DMC::SetAPU (NES_APU* apu_) + { + apu = apu_; + } + + // Initializing TRI, NOISE, DPCM mixing table + void NES_DMC::InitializeTNDTable(double wt, double wn, double wd) { + + // volume adjusted by 0.95 based on empirical measurements + const double MASTER = 8192.0 * 0.95; + // truthfully, the nonlinear curve does not appear to match well + // with my tests. Do more testing of the APU/DMC DAC later. + // this value keeps the triangle consistent with measured levels, + // but not necessarily the rest of this APU channel, + // because of the lack of a good DAC model, currently. + + { // Linear Mixer + for(int t=0; t<16 ; t++) { + for(int n=0; n<16; n++) { + for(int d=0; d<128; d++) { + tnd_table[0][t][n][d] = (UINT32)(MASTER*(3.0*t+2.0*n+d)/208.0); + } + } + } + } + { // Non-Linear Mixer + tnd_table[1][0][0][0] = 0; + for(int t=0; t<16 ; t++) { + for(int n=0; n<16; n++) { + for(int d=0; d<128; d++) { + if(t!=0||n!=0||d!=0) + tnd_table[1][t][n][d] = (UINT32)((MASTER*159.79)/(100.0+1.0/((double)t/wt+(double)n/wn+(double)d/wd))); + } + } + } + } + + } + + void NES_DMC::Reset () + { + int i; + mask = 0; + + InitializeTNDTable(8227,12241,22638); + + counter[0] = 0; + counter[1] = 0; + counter[2] = 0; + tphase = 0; + nfreq = wavlen_table[0][0]; + dfreq = freq_table[0][0]; + tri_freq = 0; + linear_counter = 0; + linear_counter_reload = 0; + linear_counter_halt = 0; + linear_counter_control = 0; + noise_volume = 0; + noise = 0; + noise_tap = 0; + envelope_loop = 0; + envelope_disable = 0; + envelope_write = 0; + envelope_div_period = 0; + envelope_div = 0; + envelope_counter = 0; + enable[0] = 0; + enable[1] = 0; + length_counter[0] = 0; + length_counter[1] = 0; + frame_irq = false; + frame_irq_enable = false; + frame_sequence_count = 0; + frame_sequence_steps = 4; + frame_sequence_step = 0; + cpu->UpdateIRQ(NES_CPU::IRQD_FRAME, false); + + for (i = 0; i < 0x0F; i++) + Write (0x4008 + i, 0); + Write (0x4017, 0x40); + + irq = false; + Write (0x4015, 0x00); + if (option[OPT_UNMUTE_ON_RESET]) + Write (0x4015, 0x0f); + cpu->UpdateIRQ(NES_CPU::IRQD_DMC, false); + + out[0] = out[1] = out[2] = 0; + damp = 0; + dmc_pop = false; + dmc_pop_offset = 0; + dmc_pop_follow = 0; + dac_lsb = 0; + data = 0x100; + empty = true; + adr_reg = 0; + dlength = 0; + len_reg = 0; + daddress = 0; + noise = 1; + noise_tap = (1<<1); + + if (option[OPT_RANDOMIZE_NOISE]) + { + noise |= ::rand(); + counter[1] = -(rand() & 511); + } + if (option[OPT_RANDOMIZE_TRI]) + { + tphase = ::rand() & 31; + counter[0] = -(rand() & 2047); + } + + SetRate(rate); + } + + void NES_DMC::SetMemory (IDevice * r) + { + memory = r; + } + + void NES_DMC::SetOption (int id, int val) + { + if(idUpdateIRQ(NES_CPU::IRQD_DMC, false); + + reg[adr-0x4008] = val; + return true; + } + + if (adr == 0x4017) + { + //DEBUG_OUT("4017 = %02X\n", val); + frame_irq_enable = ((val & 0x40) != 0x40); + if (frame_irq_enable) frame_irq = false; + cpu->UpdateIRQ(NES_CPU::IRQD_FRAME, false); + + frame_sequence_count = 0; + if (val & 0x80) + { + frame_sequence_steps = 5; + frame_sequence_step = 0; + FrameSequence(frame_sequence_step); + ++frame_sequence_step; + } + else + { + frame_sequence_steps = 4; + frame_sequence_step = 1; + } + } + + if (adr<0x4008||0x4013> 7) & 1; + linear_counter_reload = val & 0x7F; + break; + + case 0x4009: + break; + + case 0x400a: + tri_freq = val | (tri_freq & 0x700) ; + break; + + case 0x400b: + tri_freq = (tri_freq & 0xff) | ((val & 0x7) << 8) ; + linear_counter_halt = true; + if (enable[0]) + { + length_counter[0] = length_table[(val >> 3) & 0x1f]; + } + break; + + // noise + + case 0x400c: + noise_volume = val & 15; + envelope_div_period = val & 15; + envelope_disable = (val >> 4) & 1; + envelope_loop = (val >> 5) & 1; + break; + + case 0x400d: + break; + + case 0x400e: + if (option[OPT_ENABLE_PNOISE]) + noise_tap = (val & 0x80) ? (1<<6) : (1<<1); + else + noise_tap = (1<<1); + nfreq = wavlen_table[pal][val&15]; + break; + + case 0x400f: + if (enable[1]) + { + length_counter[1] = length_table[(val >> 3) & 0x1f]; + } + envelope_write = true; + break; + + // dmc + + case 0x4010: + mode = (val >> 6) & 3; + if (!(mode & 2)) + { + irq = false; + cpu->UpdateIRQ(NES_CPU::IRQD_DMC, false); + } + dfreq = freq_table[pal][val&15]; + break; + + case 0x4011: + if (option[OPT_ENABLE_4011]) + { + damp = (val >> 1) & 0x3f; + dac_lsb = val & 1; + dmc_pop = true; + } + break; + + case 0x4012: + adr_reg = val&0xff; + // ‚±‚±‚Ådaddress‚ÍXV‚³‚ê‚È‚¢ + break; + + case 0x4013: + len_reg = val&0xff; + // ‚±‚±‚Ålength‚ÍXV‚³‚ê‚È‚¢ + break; + + default: + return false; + } + + return true; + } + + bool NES_DMC::Read (UINT32 adr, UINT32 & val, UINT32 id) + { + if (adr == 0x4015) + { + val |=(irq ? 0x80 : 0) + | (frame_irq ? 0x40 : 0) + | ((dlength>0) ? 0x10 : 0) + | (length_counter[1] ? 0x08 : 0) + | (length_counter[0] ? 0x04 : 0) + ; + + frame_irq = false; + cpu->UpdateIRQ(NES_CPU::IRQD_FRAME, false); + return true; + } + else if (0x4008<=adr&&adr<=0x4014) + { + val |= reg[adr-0x4008]; + return true; + } + else + return false; + } + + // IRQ support requires CPU read access + void NES_DMC::SetCPU(NES_CPU* cpu_) + { + cpu = cpu_; + } +} // namespace diff --git a/extern/NSFplay/nes_dmc.h b/extern/NSFplay/nes_dmc.h new file mode 100644 index 000000000..9152584aa --- /dev/null +++ b/extern/NSFplay/nes_dmc.h @@ -0,0 +1,129 @@ +#ifndef _NES_DMC_H_ +#define _NES_DMC_H_ + +#include "../device.h" +#include "../Audio/MedianFilter.h" +#include "../CPU/nes_cpu.h" + +namespace xgm +{ + class NES_APU; // forward declaration + + /** Bottom Half of APU **/ + class NES_DMC:public ISoundChip + { + public: + enum + { + OPT_ENABLE_4011=0, + OPT_ENABLE_PNOISE, + OPT_UNMUTE_ON_RESET, + OPT_DPCM_ANTI_CLICK, + OPT_NONLINEAR_MIXER, + OPT_RANDOMIZE_NOISE, + OPT_TRI_MUTE, + OPT_RANDOMIZE_TRI, + OPT_DPCM_REVERSE, + OPT_END + }; + protected: + const int GETA_BITS; + static const UINT32 freq_table[2][16]; + static const UINT32 wavlen_table[2][16]; + UINT32 tnd_table[2][16][16][128]; + + int option[OPT_END]; + int mask; + INT32 sm[2][3]; + UINT8 reg[0x10]; + UINT32 len_reg; + UINT32 adr_reg; + IDevice *memory; + UINT32 out[3]; + UINT32 daddress; + UINT32 dlength; + UINT32 data; + bool empty; + INT16 damp; + int dac_lsb; + bool dmc_pop; + INT32 dmc_pop_offset; + INT32 dmc_pop_follow; + double clock; + UINT32 rate; + int pal; + int mode; + bool irq; + + INT32 counter[3]; // frequency dividers + int tphase; // triangle phase + UINT32 nfreq; // noise frequency + UINT32 dfreq; // DPCM frequency + + UINT32 tri_freq; + int linear_counter; + int linear_counter_reload; + bool linear_counter_halt; + bool linear_counter_control; + + int noise_volume; + UINT32 noise, noise_tap; + + // noise envelope + bool envelope_loop; + bool envelope_disable; + bool envelope_write; + int envelope_div_period; + int envelope_div; + int envelope_counter; + + bool enable[2]; // tri/noise enable + int length_counter[2]; // 0=tri, 1=noise + + TrackInfoBasic trkinfo[3]; + + // frame sequencer + NES_APU* apu; // apu is clocked by DMC's frame sequencer + int frame_sequence_count; // current cycle count + int frame_sequence_length; // CPU cycles per FrameSequence + int frame_sequence_step; // current step of frame sequence + int frame_sequence_steps; // 4/5 steps per frame + bool frame_irq; + bool frame_irq_enable; + + NES_CPU* cpu; // IRQ needs CPU access + + inline UINT32 calc_tri (UINT32 clocks); + inline UINT32 calc_dmc (UINT32 clocks); + inline UINT32 calc_noise (UINT32 clocks); + + public: + NES_DMC (); + ~NES_DMC (); + + void InitializeTNDTable(double wt, double wn, double wd); + void SetPal (bool is_pal); + void SetAPU (NES_APU* apu_); + void SetMemory (IDevice * r); + void FrameSequence(int s); + int GetDamp(){ return (damp<<1)|dac_lsb ; } + void TickFrameSequence (UINT32 clocks); + + virtual void Reset (); + virtual void Tick (UINT32 clocks); + virtual UINT32 Render (INT32 b[2]); + virtual bool Write (UINT32 adr, UINT32 val, UINT32 id=0); + virtual bool Read (UINT32 adr, UINT32 & val, UINT32 id=0); + virtual void SetRate (double rate); + virtual void SetClock (double rate); + virtual void SetOption (int, int); + virtual void SetMask(int m){ mask = m; } + virtual void SetStereoMix (int trk, xgm::INT16 mixl, xgm::INT16 mixr); + virtual ITrackInfo *GetTrackInfo(int trk); + + void SetCPU(NES_CPU* cpu_); + }; + +} + +#endif diff --git a/extern/NSFplay/nes_fds.cpp b/extern/NSFplay/nes_fds.cpp new file mode 100644 index 000000000..9d74c62f0 --- /dev/null +++ b/extern/NSFplay/nes_fds.cpp @@ -0,0 +1,397 @@ +#include +#include "nes_fds.h" + +namespace xgm { + +const int RC_BITS = 12; + +NES_FDS::NES_FDS () +{ + option[OPT_CUTOFF] = 2000; + option[OPT_4085_RESET] = 0; + option[OPT_WRITE_PROTECT] = 0; // not used here, see nsfplay.cpp + + rc_k = 0; + rc_l = (1< 1) return; + sm[0] = mixl; + sm[1] = mixr; +} + +ITrackInfo *NES_FDS::GetTrackInfo(int trk) +{ + trkinfo.max_volume = 32; + trkinfo.volume = last_vol; + trkinfo.key = last_vol > 0; + trkinfo._freq = last_freq; + trkinfo.freq = (double(last_freq) * clock) / (65536.0 * 64.0); + trkinfo.tone = env_out[EMOD]; + for(int i=0;i<64;i++) + trkinfo.wave[i] = wave[TWAV][i]; + + return &trkinfo; +} + +void NES_FDS::SetClock (double c) +{ + clock = c; +} + +void NES_FDS::SetRate (double r) +{ + rate = r; + + // configure lowpass filter + double cutoff = double(option[OPT_CUTOFF]); + double leak = 0.0; + if (cutoff > 0) + leak = ::exp(-2.0 * 3.14159 * cutoff / rate); + rc_k = INT32(leak * double(1<= period) + { + // clock the envelope + if (env_mode[i]) + { + if (env_out[i] < 32) ++env_out[i]; + } + else + { + if (env_out[i] > 0 ) --env_out[i]; + } + env_timer[i] -= period; + } + } + } + } + + // clock the mod table + if (!mod_halt) + { + // advance phase, adjust for modulator + UINT32 start_pos = phase[TMOD] >> 16; + phase[TMOD] += (clocks * freq[TMOD]); + UINT32 end_pos = phase[TMOD] >> 16; + + // wrap the phase to the 64-step table (+ 16 bit accumulator) + phase[TMOD] = phase[TMOD] & 0x3FFFFF; + + // execute all clocked steps + for (UINT32 p = start_pos; p < end_pos; ++p) + { + INT32 wv = wave[TMOD][p & 0x3F]; + if (wv == 4) // 4 resets mod position + mod_pos = 0; + else + { + const INT32 BIAS[8] = { 0, 1, 2, 4, 0, -4, -2, -1 }; + mod_pos += BIAS[wv]; + mod_pos &= 0x7F; // 7-bit clamp + } + } + } + + // clock the wav table + if (!wav_halt) + { + // complex mod calculation + INT32 mod = 0; + if (env_out[EMOD] != 0) // skip if modulator off + { + // convert mod_pos to 7-bit signed + INT32 pos = (mod_pos < 64) ? mod_pos : (mod_pos-128); + + // multiply pos by gain, + // shift off 4 bits but with odd "rounding" behaviour + INT32 temp = pos * env_out[EMOD]; + INT32 rem = temp & 0x0F; + temp >>= 4; + if ((rem > 0) && ((temp & 0x80) == 0)) + { + if (pos < 0) temp -= 1; + else temp += 2; + } + + // wrap if range is exceeded + while (temp >= 192) temp -= 256; + while (temp < -64) temp += 256; + + // multiply result by pitch, + // shift off 6 bits, round to nearest + temp = freq[TWAV] * temp; + rem = temp & 0x3F; + temp >>= 6; + if (rem >= 32) temp += 1; + + mod = temp; + } + + // advance wavetable position + INT32 f = freq[TWAV] + mod; + phase[TWAV] = phase[TWAV] + (clocks * f); + phase[TWAV] = phase[TWAV] & 0x3FFFFF; // wrap + + // store for trackinfo + last_freq = f; + } + + // output volume caps at 32 + INT32 vol_out = env_out[EVOL]; + if (vol_out > 32) vol_out = 32; + + // final output + if (!wav_write) + fout = wave[TWAV][(phase[TWAV]>>16)&0x3F] * vol_out; + + // NOTE: during wav_halt, the unit still outputs (at phase 0) + // and volume can affect it if the first sample is nonzero. + // haven't worked out 100% of the conditions for volume to + // effect (vol envelope does not seem to run, but am unsure) + // but this implementation is very close to correct + + // store for trackinfo + last_vol = vol_out; +} + +UINT32 NES_FDS::Render (INT32 b[2]) +{ + // 8 bit approximation of master volume + const double MASTER_VOL = 2.4 * 1223.0; // max FDS vol vs max APU square (arbitrarily 1223) + const double MAX_OUT = 32.0f * 63.0f; // value that should map to master vol + const INT32 MASTER[4] = { + int((MASTER_VOL / MAX_OUT) * 256.0 * 2.0f / 2.0f), + int((MASTER_VOL / MAX_OUT) * 256.0 * 2.0f / 3.0f), + int((MASTER_VOL / MAX_OUT) * 256.0 * 2.0f / 4.0f), + int((MASTER_VOL / MAX_OUT) * 256.0 * 2.0f / 5.0f) }; + + INT32 v = fout * MASTER[master_vol] >> 8; + + // lowpass RC filter + INT32 rc_out = ((rc_accum * rc_k) + (v * rc_l)) >> RC_BITS; + rc_accum = rc_out; + v = rc_out; + + // output mix + INT32 m = mask ? 0 : v; + b[0] = (m * sm[0]) >> 7; + b[1] = (m * sm[1]) >> 7; + return 2; +} + +bool NES_FDS::Write (UINT32 adr, UINT32 val, UINT32 id) +{ + // $4023 master I/O enable/disable + if (adr == 0x4023) + { + master_io = ((val & 2) != 0); + return true; + } + + if (!master_io) + return false; + if (adr < 0x4040 || adr > 0x408A) + return false; + + if (adr < 0x4080) // $4040-407F wave table write + { + if (wav_write) + wave[TWAV][adr - 0x4040] = val & 0x3F; + return true; + } + + switch (adr & 0x00FF) + { + case 0x80: // $4080 volume envelope + env_disable[EVOL] = ((val & 0x80) != 0); + env_mode[EVOL] = ((val & 0x40) != 0); + env_timer[EVOL] = 0; + env_speed[EVOL] = val & 0x3F; + if (env_disable[EVOL]) + env_out[EVOL] = env_speed[EVOL]; + return true; + case 0x81: // $4081 --- + return false; + case 0x82: // $4082 wave frequency low + freq[TWAV] = (freq[TWAV] & 0xF00) | val; + return true; + case 0x83: // $4083 wave frequency high / enables + freq[TWAV] = (freq[TWAV] & 0x0FF) | ((val & 0x0F) << 8); + wav_halt = ((val & 0x80) != 0); + env_halt = ((val & 0x40) != 0); + if (wav_halt) + phase[TWAV] = 0; + if (env_halt) + { + env_timer[EMOD] = 0; + env_timer[EVOL] = 0; + } + return true; + case 0x84: // $4084 mod envelope + env_disable[EMOD] = ((val & 0x80) != 0); + env_mode[EMOD] = ((val & 0x40) != 0); + env_timer[EMOD] = 0; + env_speed[EMOD] = val & 0x3F; + if (env_disable[EMOD]) + env_out[EMOD] = env_speed[EMOD]; + return true; + case 0x85: // $4085 mod position + mod_pos = val & 0x7F; + // not hardware accurate., but prevents detune due to cycle inaccuracies + // (notably in Bio Miracle Bokutte Upa) + if (option[OPT_4085_RESET]) + phase[TMOD] = mod_write_pos << 16; + return true; + case 0x86: // $4086 mod frequency low + freq[TMOD] = (freq[TMOD] & 0xF00) | val; + return true; + case 0x87: // $4087 mod frequency high / enable + freq[TMOD] = (freq[TMOD] & 0x0FF) | ((val & 0x0F) << 8); + mod_halt = ((val & 0x80) != 0); + if (mod_halt) + phase[TMOD] = phase[TMOD] & 0x3F0000; // reset accumulator phase + return true; + case 0x88: // $4088 mod table write + if (mod_halt) + { + // writes to current playback position (there is no direct way to set phase) + wave[TMOD][(phase[TMOD] >> 16) & 0x3F] = val & 0x07; + phase[TMOD] = (phase[TMOD] + 0x010000) & 0x3FFFFF; + wave[TMOD][(phase[TMOD] >> 16) & 0x3F] = val & 0x07; + phase[TMOD] = (phase[TMOD] + 0x010000) & 0x3FFFFF; + mod_write_pos = phase[TMOD] >> 16; // used by OPT_4085_RESET + } + return true; + case 0x89: // $4089 wave write enable, master volume + wav_write = ((val & 0x80) != 0); + master_vol = val & 0x03; + return true; + case 0x8A: // $408A envelope speed + master_env_speed = val; + // haven't tested whether this register resets phase on hardware, + // but this ensures my inplementation won't spam envelope clocks + // if this value suddenly goes low. + env_timer[EMOD] = 0; + env_timer[EVOL] = 0; + return true; + default: + return false; + } + return false; +} + +bool NES_FDS::Read (UINT32 adr, UINT32 & val, UINT32 id) +{ + if (adr >= 0x4040 && adr <= 0x407F) + { + // TODO: if wav_write is not enabled, the + // read address may not be reliable? need + // to test this on hardware. + val = wave[TWAV][adr - 0x4040]; + return true; + } + + if (adr == 0x4090) // $4090 read volume envelope + { + val = env_out[EVOL] | 0x40; + return true; + } + + if (adr == 0x4092) // $4092 read mod envelope + { + val = env_out[EMOD] | 0x40; + return true; + } + + return false; +} + +} // namespace diff --git a/extern/NSFplay/nes_fds.h b/extern/NSFplay/nes_fds.h new file mode 100644 index 000000000..212e472b4 --- /dev/null +++ b/extern/NSFplay/nes_fds.h @@ -0,0 +1,83 @@ +#ifndef _NES_FDS_H_ +#define _NES_FDS_H_ +#include "../device.h" + +namespace xgm { + +class TrackInfoFDS : public TrackInfoBasic +{ +public: + INT16 wave[64]; + virtual IDeviceInfo *Clone(){ return new TrackInfoFDS(*this); } +}; + +class NES_FDS : public ISoundChip +{ +public: + enum + { + OPT_CUTOFF=0, + OPT_4085_RESET, + OPT_WRITE_PROTECT, + OPT_END + }; + +protected: + double rate, clock; + int mask; + INT32 sm[2]; // stereo mix + INT32 fout; // current output + TrackInfoFDS trkinfo; + int option[OPT_END]; + + bool master_io; + UINT32 master_vol; + UINT32 last_freq; // for trackinfo + UINT32 last_vol; // for trackinfo + + // two wavetables + enum { TMOD=0, TWAV=1 }; + INT32 wave[2][64]; + UINT32 freq[2]; + UINT32 phase[2]; + bool wav_write; + bool wav_halt; + bool env_halt; + bool mod_halt; + UINT32 mod_pos; + UINT32 mod_write_pos; + + // two ramp envelopes + enum { EMOD=0, EVOL=1 }; + bool env_mode[2]; + bool env_disable[2]; + UINT32 env_timer[2]; + UINT32 env_speed[2]; + UINT32 env_out[2]; + UINT32 master_env_speed; + + // 1-pole RC lowpass filter + INT32 rc_accum; + INT32 rc_k; + INT32 rc_l; + +public: + NES_FDS (); + virtual ~ NES_FDS (); + + virtual void Reset (); + virtual void Tick (UINT32 clocks); + virtual UINT32 Render (INT32 b[2]); + virtual bool Write (UINT32 adr, UINT32 val, UINT32 id=0); + virtual bool Read (UINT32 adr, UINT32 & val, UINT32 id=0); + virtual void SetRate (double); + virtual void SetClock (double); + virtual void SetOption (int, int); + virtual void SetMask(int m){ mask = m&1; } + virtual void SetStereoMix (int trk, INT16 mixl, INT16 mixr); + virtual ITrackInfo *GetTrackInfo(int trk); +}; + +} // namespace xgm + +#endif diff --git a/extern/NSFplay/nes_fme7.cpp b/extern/NSFplay/nes_fme7.cpp new file mode 100644 index 000000000..9d48de794 --- /dev/null +++ b/extern/NSFplay/nes_fme7.cpp @@ -0,0 +1,186 @@ +#include "nes_fme7.h" + +using namespace xgm; + +const int DIVIDER = 8; // TODO this is not optimal, rewrite PSG output + +NES_FME7::NES_FME7 () +{ + psg = PSG_new ((e_uint32)DEFAULT_CLOCK, DEFAULT_RATE); + divider = 0; + + for(int c=0;c<2;++c) + for(int t=0;t<3;++t) + sm[c][t] = 128; +} + +NES_FME7::~NES_FME7 () +{ + if (psg) + PSG_delete (psg); +} + +void NES_FME7::SetClock (double c) +{ + this->clock = c * 2.0; +} + +void NES_FME7::SetRate (double r) +{ + //rate = r ? r : DEFAULT_RATE; + rate = DEFAULT_CLOCK / double(DIVIDER); // TODO rewrite PSG to integrate with clock + if (psg) + PSG_set_rate (psg, (e_uint32)rate); +} + +void NES_FME7::SetOption (int id, int val) +{ + if(id= DIVIDER) + { + divider -= DIVIDER; + if (psg) PSG_calc(psg); + } +} + +xgm::UINT32 NES_FME7::Render (xgm::INT32 b[2]) +{ + b[0] = b[1] = 0; + + for (int i=0; i < 3; ++i) + { + // note negative polarity + b[0] -= psg->cout[i] * sm[0][i]; + b[1] -= psg->cout[i] * sm[1][i]; + } + b[0] >>= (7-4); + b[1] >>= (7-4); + + // master volume adjustment + const INT32 MASTER = INT32(0.64 * 256.0); + b[0] = (b[0] * MASTER) >> 8; + b[1] = (b[1] * MASTER) >> 8; + + return 2; +} + +void NES_FME7::SetStereoMix(int trk, xgm::INT16 mixl, xgm::INT16 mixr) +{ + if (trk < 0) return; + if (trk > 2) return; + sm[0][trk] = mixl; + sm[1][trk] = mixr; +} + +ITrackInfo *NES_FME7::GetTrackInfo(int trk) +{ + assert(trk<5); + + if(psg) + { + if (trk<3) + { + trkinfo[trk]._freq = psg->freq[trk]; + if(psg->freq[trk]) + trkinfo[trk].freq = psg->clk/32.0/psg->freq[trk]; + else + trkinfo[trk].freq = 0; + + trkinfo[trk].output = psg->cout[trk]; + trkinfo[trk].max_volume = 15; + trkinfo[trk].volume = psg->volume[trk] >> 1; + //trkinfo[trk].key = (psg->cout[trk]>0)?true:false; + trkinfo[trk].key = !(psg->tmask[trk]); + trkinfo[trk].tone = (psg->tmask[trk]?2:0)+(psg->nmask[trk]?1:0); + } + else if (trk == 3) // envelope + { + trkinfo[trk]._freq = psg->env_freq; + if(psg->env_freq) + trkinfo[trk].freq = psg->clk/512.0/psg->env_freq; + else + trkinfo[trk].freq = 0; + + if (psg->env_continue && psg->env_alternate && !psg->env_hold) // triangle wave + { + trkinfo[trk].freq *= 0.5f; // sounds an octave down + } + + trkinfo[trk].output = psg->voltbl[psg->env_ptr]; + trkinfo[trk].max_volume = 0; + trkinfo[trk].volume = 0; + trkinfo[trk].key = (((psg->volume[0]|psg->volume[1]|psg->volume[2])&32) != 0); + trkinfo[trk].tone = + (psg->env_continue ?8:0) | + (psg->env_attack ?4:0) | + (psg->env_alternate?2:0) | + (psg->env_hold ?1:0) ; + } + else if (trk == 4) // noise + { + trkinfo[trk]._freq = psg->noise_freq >> 1; + if(trkinfo[trk]._freq > 0) + trkinfo[trk].freq = psg->clk/16.0/psg->noise_freq; + else + trkinfo[trk].freq = 0; + + trkinfo[trk].output = psg->noise_seed & 1; + trkinfo[trk].max_volume = 0; + trkinfo[trk].volume = 0; + //trkinfo[trk].key = ((psg->nmask[0]&psg->nmask[1]&psg->nmask[2]) == 0); + trkinfo[trk].key = false; + trkinfo[trk].tone = 0; + } + } + return &trkinfo[trk]; +} diff --git a/extern/NSFplay/nes_fme7.h b/extern/NSFplay/nes_fme7.h new file mode 100644 index 000000000..d351b0859 --- /dev/null +++ b/extern/NSFplay/nes_fme7.h @@ -0,0 +1,42 @@ +#ifndef _NES_FME7_H_ +#define _NES_FME7_H_ +#include "../device.h" +#include "legacy/emu2149.h" + +namespace xgm +{ + + class NES_FME7:public ISoundChip + { + public: + enum + { + OPT_END + }; + protected: + //int option[OPT_END]; + INT32 sm[2][3]; // stereo mix + INT16 buf[2]; + PSG *psg; + int divider; // clock divider + double clock, rate; + TrackInfoBasic trkinfo[5]; + public: + NES_FME7 (); + ~NES_FME7 (); + virtual void Reset (); + virtual void Tick (UINT32 clocks); + virtual UINT32 Render (INT32 b[2]); + virtual bool Read (UINT32 adr, UINT32 & val, UINT32 id=0); + virtual bool Write (UINT32 adr, UINT32 val, UINT32 id=0); + virtual void SetClock (double); + virtual void SetRate (double); + virtual void SetOption (int, int); + virtual void SetMask (int m){ if(psg) PSG_setMask(psg,m); } + virtual void SetStereoMix (int trk, xgm::INT16 mixl, xgm::INT16 mixr); + virtual ITrackInfo *GetTrackInfo(int trk); + }; + +} // namespace + +#endif diff --git a/extern/NSFplay/nes_mmc5.cpp b/extern/NSFplay/nes_mmc5.cpp new file mode 100644 index 000000000..2b2f8fcb7 --- /dev/null +++ b/extern/NSFplay/nes_mmc5.cpp @@ -0,0 +1,422 @@ +#include "nes_mmc5.h" + +namespace xgm +{ + + NES_MMC5::NES_MMC5 () + { + cpu = NULL; + SetClock (DEFAULT_CLOCK); + SetRate (DEFAULT_RATE); + option[OPT_NONLINEAR_MIXER] = true; + option[OPT_PHASE_REFRESH] = true; + frame_sequence_count = 0; + + // square nonlinear mix, same as 2A03 + square_table[0] = 0; + for(int i=1;i<32;i++) + square_table[i]=(INT32)((8192.0*95.88)/(8128.0/i+100)); + + // 2A03 style nonlinear pcm mix with double the bits + //pcm_table[0] = 0; + //INT32 wd = 22638; + //for(int d=1;d<256; ++d) + // pcm_table[d] = (INT32)((8192.0*159.79)/(100.0+1.0/((double)d/wd))); + + // linear pcm mix (actual hardware seems closer to this) + pcm_table[0] = 0; + double pcm_scale = 32.0; + for (int d=1; d<256; ++d) + pcm_table[d] = (INT32)(double(d) * pcm_scale); + + // stereo mix + for(int c=0;c<2;++c) + for(int t=0;t<3;++t) + sm[c][t] = 128; + } + + NES_MMC5::~NES_MMC5 () + { + } + + void NES_MMC5::Reset () + { + int i; + + scounter[0] = 0; + scounter[1] = 0; + sphase[0] = 0; + sphase[1] = 0; + + envelope_div[0] = 0; + envelope_div[1] = 0; + length_counter[0] = 0; + length_counter[1] = 0; + envelope_counter[0] = 0; + envelope_counter[1] = 0; + frame_sequence_count = 0; + + for (i = 0; i < 8; i++) + Write (0x5000 + i, 0); + + Write(0x5015, 0); + + for (i = 0; i < 3; ++i) + out[i] = 0; + + mask = 0; + pcm = 0; // PCM channel + pcm_mode = false; // write mode + + SetRate(rate); + } + + void NES_MMC5::SetOption (int id, int val) + { + if(idclock = c; + } + + void NES_MMC5::SetRate (double r) + { + rate = r ? r : DEFAULT_RATE; + } + + void NES_MMC5::FrameSequence () + { + // 240hz clock + for (int i=0; i < 2; ++i) + { + bool divider = false; + if (envelope_write[i]) + { + envelope_write[i] = false; + envelope_counter[i] = 15; + envelope_div[i] = 0; + } + else + { + ++envelope_div[i]; + if (envelope_div[i] > envelope_div_period[i]) + { + divider = true; + envelope_div[i] = 0; + } + } + if (divider) + { + if (envelope_loop[i] && envelope_counter[i] == 0) + envelope_counter[i] = 15; + else if (envelope_counter[i] > 0) + --envelope_counter[i]; + } + } + + // MMC5 length counter is clocked at 240hz, unlike 2A03 + for (int i=0; i < 2; ++i) + { + if (!envelope_loop[i] && (length_counter[i] > 0)) + --length_counter[i]; + } + } + + INT32 NES_MMC5::calc_sqr (int i, UINT32 clocks) + { + static const INT16 sqrtbl[4][16] = { + {0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0}, + {1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1} + }; + + scounter[i] += clocks; + while (scounter[i] > freq[i]) + { + sphase[i] = (sphase[i] + 1) & 15; + scounter[i] -= (freq[i] + 1); + } + + INT32 ret = 0; + if (length_counter[i] > 0) + { + // note MMC5 does not silence the highest 8 frequencies like APU, + // because this is done by the sweep unit. + + int v = envelope_disable[i] ? volume[i] : envelope_counter[i]; + ret = sqrtbl[duty[i]][sphase[i]] ? v : 0; + } + + return ret; + } + + void NES_MMC5::TickFrameSequence (UINT32 clocks) + { + frame_sequence_count += clocks; + while (frame_sequence_count > 7458) + { + FrameSequence(); + frame_sequence_count -= 7458; + } + } + + void NES_MMC5::Tick (UINT32 clocks) + { + out[0] = calc_sqr(0, clocks); + out[1] = calc_sqr(1, clocks); + out[2] = pcm; + } + + UINT32 NES_MMC5::Render (INT32 b[2]) + { + out[0] = (mask & 1) ? 0 : out[0]; + out[1] = (mask & 2) ? 0 : out[1]; + out[2] = (mask & 4) ? 0 : out[2]; + + INT32 m[3]; + + if(option[OPT_NONLINEAR_MIXER]) + { + // squares nonlinear + INT32 voltage = square_table[out[0] + out[1]]; + m[0] = out[0] << 6; + m[1] = out[1] << 6; + INT32 ref = m[0] + m[1]; + if (ref > 0) + { + m[0] = (m[0] * voltage) / ref; + m[1] = (m[1] * voltage) / ref; + } + else + { + m[0] = voltage; + m[1] = voltage; + } + + // pcm nonlinear + m[2] = pcm_table[out[2]]; + } + else + { + // squares + m[0] = out[0] << 6; + m[1] = out[1] << 6; + + // pcm channel + m[2] = out[2] << 5; + } + + // note polarity is flipped on output + + b[0] = m[0] * -sm[0][0]; + b[0] += m[1] * -sm[0][1]; + b[0] += m[2] * -sm[0][2]; + b[0] >>= 7; + + b[1] = m[0] * -sm[1][0]; + b[1] += m[1] * -sm[1][1]; + b[1] += m[2] * -sm[1][2]; + b[1] >>= 7; + + return 2; + } + + bool NES_MMC5::Write (UINT32 adr, UINT32 val, UINT32 id) + { + int ch; + + static const UINT8 length_table[32] = { + 0x0A, 0xFE, + 0x14, 0x02, + 0x28, 0x04, + 0x50, 0x06, + 0xA0, 0x08, + 0x3C, 0x0A, + 0x0E, 0x0C, + 0x1A, 0x0E, + 0x0C, 0x10, + 0x18, 0x12, + 0x30, 0x14, + 0x60, 0x16, + 0xC0, 0x18, + 0x48, 0x1A, + 0x10, 0x1C, + 0x20, 0x1E + }; + + if ((0x5c00 <= adr) && (adr < 0x5ff0)) + { + ram[adr & 0x3ff] = val; + return true; + } + else if ((0x5000 <= adr) && (adr < 0x5008)) + { + reg[adr & 0x7] = val; + } + + switch (adr) + { + case 0x5000: + case 0x5004: + ch = (adr >> 2) & 1; + volume[ch] = val & 15; + envelope_disable[ch] = (val >> 4) & 1; + envelope_loop[ch] = (val >> 5) & 1; + envelope_div_period[ch] = (val & 15); + duty[ch] = (val >> 6) & 3; + break; + + case 0x5002: + case 0x5006: + ch = (adr >> 2) & 1; + freq[ch] = val + (freq[ch] & 0x700); + if (scounter[ch] > freq[ch]) scounter[ch] = freq[ch]; + break; + + case 0x5003: + case 0x5007: + ch = (adr >> 2) & 1; + freq[ch] = (freq[ch] & 0xff) + ((val & 7) << 8); + if (scounter[ch] > freq[ch]) scounter[ch] = freq[ch]; + // phase reset + if (option[OPT_PHASE_REFRESH]) + sphase[ch] = 0; + envelope_write[ch] = true; + if (enable[ch]) + { + length_counter[ch] = length_table[(val >> 3) & 0x1f]; + } + break; + + // PCM channel control + case 0x5010: + pcm_mode = ((val & 1) != 0); // 0 = write, 1 = read + break; + + // PCM channel control + case 0x5011: + if (!pcm_mode) + { + val &= 0xFF; + if (val != 0) pcm = val; + } + break; + + case 0x5015: + enable[0] = (val & 1) ? true : false; + enable[1] = (val & 2) ? true : false; + if (!enable[0]) + length_counter[0] = 0; + if (!enable[1]) + length_counter[1] = 0; + break; + + case 0x5205: + mreg[0] = val; + break; + + case 0x5206: + mreg[1] = val; + break; + + default: + return false; + + } + return true; + } + + bool NES_MMC5::Read (UINT32 adr, UINT32 & val, UINT32 id) + { + // in PCM read mode, reads from $8000-$C000 automatically load the PCM output + if (pcm_mode && (0x8000 <= adr) && (adr < 0xC000) && cpu) + { + pcm_mode = false; // prevent recursive entry + UINT32 pcm_read; + cpu->Read(adr, pcm_read); + pcm_read &= 0xFF; + if (pcm_read != 0) + pcm = pcm_read; + pcm_mode = true; + } + + if ((0x5000 <= adr) && (adr < 0x5008)) + { + val = reg[adr&0x7]; + return true; + } + else if(adr == 0x5015) + { + val = (enable[1]?2:0)|(enable[0]?1:0); + return true; + } + + if ((0x5c00 <= adr) && (adr < 0x5ff0)) + { + val = ram[adr & 0x3ff]; + return true; + } + else if (adr == 0x5205) + { + val = (mreg[0] * mreg[1]) & 0xff; + return true; + } + else if (adr == 0x5206) + { + val = (mreg[0] * mreg[1]) >> 8; + return true; + } + + return false; + } + + void NES_MMC5::SetStereoMix(int trk, xgm::INT16 mixl, xgm::INT16 mixr) + { + if (trk < 0) return; + if (trk > 2) return; + sm[0][trk] = mixl; + sm[1][trk] = mixr; + } + + ITrackInfo *NES_MMC5::GetTrackInfo(int trk) + { + assert(trk<3); + + if (trk < 2) // square + { + trkinfo[trk]._freq = freq[trk]; + if(freq[trk]) + trkinfo[trk].freq = clock/16/(freq[trk] + 1); + else + trkinfo[trk].freq = 0; + + trkinfo[trk].output = out[trk]; + trkinfo[trk].max_volume = 15; + trkinfo[trk].volume = volume[trk]+(envelope_disable[trk]?0:0x10); + trkinfo[trk].key = (envelope_disable[trk]?(volume[trk]>0): (envelope_counter[trk]>0)); + trkinfo[trk].tone = duty[trk]; + } + else // pcm + { + trkinfo[trk]._freq = 0; + trkinfo[trk].freq = 0; + trkinfo[trk].output = out[2]; + trkinfo[trk].max_volume = 255; + trkinfo[trk].volume = pcm; + trkinfo[trk].key = 0; + trkinfo[trk].tone = pcm_mode ? 1 : 0; + } + + return &trkinfo[trk]; + } + + // pcm read mode requires CPU read access + void NES_MMC5::SetCPU(NES_CPU* cpu_) + { + cpu = cpu_; + } + +}// namespace diff --git a/extern/NSFplay/nes_mmc5.h b/extern/NSFplay/nes_mmc5.h new file mode 100644 index 000000000..b05fbb506 --- /dev/null +++ b/extern/NSFplay/nes_mmc5.h @@ -0,0 +1,74 @@ +#ifndef _NES_MMC5_H_ +#define _NES_MMC5_H_ +#include "../device.h" +#include "../CPU/nes_cpu.h" + +namespace xgm +{ + class NES_MMC5:public ISoundChip + { + public: + enum + { OPT_NONLINEAR_MIXER=0, OPT_PHASE_REFRESH, OPT_END }; + + protected: + int option[OPT_END]; + int mask; + INT32 sm[2][3]; // stereo panning + UINT8 ram[0x6000 - 0x5c00]; + UINT8 reg[8]; + UINT8 mreg[2]; + UINT8 pcm; // PCM channel + bool pcm_mode; // PCM channel + NES_CPU* cpu; // PCM channel reads need CPU access + + UINT32 scounter[2]; // frequency divider + UINT32 sphase[2]; // phase counter + + UINT32 duty[2]; + UINT32 volume[2]; + UINT32 freq[2]; + INT32 out[3]; + bool enable[2]; + + bool envelope_disable[2]; // ƒGƒ“ƒxƒ[ƒv—LŒøƒtƒ‰ƒO + bool envelope_loop[2]; // ƒGƒ“ƒxƒ[ƒvƒ‹[ƒv + bool envelope_write[2]; + int envelope_div_period[2]; + int envelope_div[2]; + int envelope_counter[2]; + + int length_counter[2]; + + int frame_sequence_count; + + double clock, rate; + INT32 calc_sqr (int i, UINT32 clocks); + INT32 square_table[32]; + INT32 pcm_table[256]; + TrackInfoBasic trkinfo[3]; + public: + NES_MMC5 (); + ~NES_MMC5 (); + + void FrameSequence (); + void TickFrameSequence (UINT32 clocks); + + virtual void Reset (); + virtual void Tick (UINT32 clocks); + virtual UINT32 Render (INT32 b[2]); + virtual bool Write (UINT32 adr, UINT32 val, UINT32 id=0); + virtual bool Read (UINT32 adr, UINT32 & val, UINT32 id=0); + virtual void SetOption (int id, int b); + virtual void SetClock (double); + virtual void SetRate (double); + virtual void SetMask (int m){ mask = m; } + virtual void SetStereoMix (int trk, xgm::INT16 mixl, xgm::INT16 mixr); + virtual ITrackInfo *GetTrackInfo(int trk); + + void SetCPU(NES_CPU* cpu_); + }; + +} + +#endif diff --git a/extern/NSFplay/nes_n106.cpp b/extern/NSFplay/nes_n106.cpp new file mode 100644 index 000000000..f53d4ae7c --- /dev/null +++ b/extern/NSFplay/nes_n106.cpp @@ -0,0 +1,367 @@ +#include +#include "nes_n106.h" + +namespace xgm { + +NES_N106::NES_N106 () +{ + option[OPT_SERIAL] = 0; + option[OPT_PHASE_READ_ONLY] = 0; + option[OPT_LIMIT_WAVELENGTH] = 0; + SetClock (DEFAULT_CLOCK); + SetRate (DEFAULT_RATE); + for (int i=0; i < 8; ++i) + { + sm[0][i] = 128; + sm[1][i] = 128; + } + Reset(); +} + +NES_N106::~NES_N106 () +{ +} + +void NES_N106::SetStereoMix (int trk, INT16 mixl, INT16 mixr) +{ + if (trk < 0 || trk >= 8) return; + trk = 7-trk; // displayed channels are inverted + sm[0][trk] = mixl; + sm[1][trk] = mixr; +} + +ITrackInfo *NES_N106::GetTrackInfo (int trk) +{ + int channels = get_channels(); + int channel = 7-trk; // invert the track display + + TrackInfoN106* t = &trkinfo[channel]; + + if (trk >= channels) + { + t->max_volume = 15; + t->volume = 0; + t->_freq = 0; + t->wavelen = 0; + t->tone = -1; + t->output = 0; + t->key = false; + t->freq = 0; + } + else + { + t->max_volume = 15; + t->volume = get_vol(channel); + t->_freq = get_freq(channel); + t->wavelen = get_len(channel); + t->tone = get_off(channel); + t->output = fout[channel]; + + t->key = (t->volume > 0) && (t->_freq > 0); + t->freq = (double(t->_freq) * clock) / double(15 * 65536 * channels * t->wavelen); + + for (int i=0; i < t->wavelen; ++i) + t->wave[i] = get_sample((i+t->tone)&0xFF); + } + + return t; +} + +void NES_N106::SetClock (double c) +{ + clock = c; +} + +void NES_N106::SetRate (double r) +{ + rate = r; +} + +void NES_N106::SetMask (int m) +{ + // bit reverse the mask, + // N163 waves are displayed in reverse order + mask = 0 + | ((m & (1<<0)) ? (1<<7) : 0) + | ((m & (1<<1)) ? (1<<6) : 0) + | ((m & (1<<2)) ? (1<<5) : 0) + | ((m & (1<<3)) ? (1<<4) : 0) + | ((m & (1<<4)) ? (1<<3) : 0) + | ((m & (1<<5)) ? (1<<2) : 0) + | ((m & (1<<6)) ? (1<<1) : 0) + | ((m & (1<<7)) ? (1<<0) : 0); +} + +void NES_N106::SetOption (int id, int val) +{ + if (id 0) + { + int channel = 7-tick_channel; + + UINT32 phase = get_phase(channel); + UINT32 freq = get_freq(channel); + UINT32 len = get_len(channel); + UINT32 off = get_off(channel); + INT32 vol = get_vol(channel); + + // accumulate 24-bit phase + phase = (phase + freq) & 0x00FFFFFF; + + // wrap phase if wavelength exceeded + UINT32 hilen = len << 16; + while (phase >= hilen) phase -= hilen; + + // write back phase + set_phase(phase, channel); + + // fetch sample (note: N163 output is centred at 8, and inverted w.r.t 2A03) + INT32 sample = 8 - get_sample(((phase >> 16) + off) & 0xFF); + fout[channel] = sample * vol; + + // cycle to next channel every 15 clocks + tick_clock -= 15; + ++tick_channel; + if (tick_channel >= channels) + tick_channel = 0; + } +} + +UINT32 NES_N106::Render (INT32 b[2]) +{ + b[0] = 0; + b[1] = 0; + if (master_disable) return 2; + + int channels = get_channels(); + + if (option[OPT_SERIAL]) // hardware accurate serial multiplexing + { + // this could be made more efficient than going clock-by-clock + // but this way is simpler + int clocks = render_clock; + while (clocks > 0) + { + int c = 7-render_channel; + if (0 == ((mask >> c) & 1)) + { + b[0] += fout[c] * sm[0][c]; + b[1] += fout[c] * sm[1][c]; + } + + ++render_subclock; + if (render_subclock >= 15) // each channel gets a 15-cycle slice + { + render_subclock = 0; + ++render_channel; + if (render_channel >= channels) + render_channel = 0; + } + --clocks; + } + + // increase output level by 1 bits (7 bits already added from sm) + b[0] <<= 1; + b[1] <<= 1; + + // average the output + if (render_clock > 0) + { + b[0] /= render_clock; + b[1] /= render_clock; + } + render_clock = 0; + } + else // just mix all channels + { + for (int i = (8-channels); i<8; ++i) + { + if (0 == ((mask >> i) & 1)) + { + b[0] += fout[i] * sm[0][i]; + b[1] += fout[i] * sm[1][i]; + } + } + + // mix together, increase output level by 8 bits, roll off 7 bits from sm + INT32 MIX[9] = { 256/1, 256/1, 256/2, 256/3, 256/4, 256/5, 256/6, 256/6, 256/6 }; + b[0] = (b[0] * MIX[channels]) >> 7; + b[1] = (b[1] * MIX[channels]) >> 7; + // when approximating the serial multiplex as a straight mix, once the + // multiplex frequency gets below the nyquist frequency an average mix + // begins to sound too quiet. To approximate this effect, I don't attenuate + // any further after 6 channels are active. + } + + // 8 bit approximation of master volume + // max N163 vol vs max APU square + // unfortunately, games have been measured as low as 3.4x and as high as 8.5x + // with higher volumes on Erika, King of Kings, and Rolling Thunder + // and lower volumes on others. Using 6.0x as a rough "one size fits all". + const double MASTER_VOL = 6.0 * 1223.0; + const double MAX_OUT = 15.0 * 15.0 * 256.0; // max digital value + const INT32 GAIN = int((MASTER_VOL / MAX_OUT) * 256.0f); + b[0] = (b[0] * GAIN) >> 8; + b[1] = (b[1] * GAIN) >> 8; + + return 2; +} + +bool NES_N106::Write (UINT32 adr, UINT32 val, UINT32 id) +{ + if (adr == 0xE000) // master disable + { + master_disable = ((val & 0x40) != 0); + return true; + } + else if (adr == 0xF800) // register select + { + reg_select = (val & 0x7F); + reg_advance = (val & 0x80) != 0; + return true; + } + else if (adr == 0x4800) // register write + { + if (option[OPT_PHASE_READ_ONLY]) // old emulators didn't know phase was stored here + { + int c = 15 - (reg_select/8); + int r = reg_select & 7; + if (c < get_channels() && + (r == 1 || + r == 3 || + r == 5)) + { + if (reg_advance) + reg_select = (reg_select + 1) & 0x7F; + return true; + } + } + if (option[OPT_LIMIT_WAVELENGTH]) // old emulators ignored top 3 bits of length + { + int c = 15 - (reg_select/8); + int r = reg_select & 7; + if (c < get_channels() && r == 4) + { + val |= 0xE0; + } + } + reg[reg_select] = val; + if (reg_advance) + reg_select = (reg_select + 1) & 0x7F; + return true; + } + return false; +} + +bool NES_N106::Read (UINT32 adr, UINT32 & val, UINT32 id) +{ + if (adr == 0x4800) // register read + { + val = reg[reg_select]; + if (reg_advance) + reg_select = (reg_select + 1) & 0x7F; + return true; + } + return false; +} + +// +// register decoding/encoding functions +// + +inline UINT32 NES_N106::get_phase (int channel) +{ + // 24-bit phase stored in channel regs 1/3/5 + channel = channel << 3; + return (reg[0x41 + channel] ) + + (reg[0x43 + channel] << 8 ) + + (reg[0x45 + channel] << 16); +} + +inline UINT32 NES_N106::get_freq (int channel) +{ + // 19-bit frequency stored in channel regs 0/2/4 + channel = channel << 3; + return ( reg[0x40 + channel] ) + + ( reg[0x42 + channel] << 8 ) + + ((reg[0x44 + channel] & 0x03) << 16); +} + +inline UINT32 NES_N106::get_off (int channel) +{ + // 8-bit offset stored in channel reg 6 + channel = channel << 3; + return reg[0x46 + channel]; +} + +inline UINT32 NES_N106::get_len (int channel) +{ + // 6-bit<<3 length stored obscurely in channel reg 4 + channel = channel << 3; + return 256 - (reg[0x44 + channel] & 0xFC); +} + +inline INT32 NES_N106::get_vol (int channel) +{ + // 4-bit volume stored in channel reg 7 + channel = channel << 3; + return reg[0x47 + channel] & 0x0F; +} + +inline INT32 NES_N106::get_sample (UINT32 index) +{ + // every sample becomes 2 samples in regs + return (index&1) ? + ((reg[index>>1] >> 4) & 0x0F) : + ( reg[index>>1] & 0x0F) ; +} + +inline int NES_N106::get_channels () +{ + // 3-bit channel count stored in reg 0x7F + return ((reg[0x7F] >> 4) & 0x07) + 1; +} + +inline void NES_N106::set_phase (UINT32 phase, int channel) +{ + // 24-bit phase stored in channel regs 1/3/5 + channel = channel << 3; + reg[0x41 + channel] = phase & 0xFF; + reg[0x43 + channel] = (phase >> 8 ) & 0xFF; + reg[0x45 + channel] = (phase >> 16) & 0xFF; +} + +} //namespace diff --git a/extern/NSFplay/nes_n106.h b/extern/NSFplay/nes_n106.h new file mode 100644 index 000000000..82d7eaa6a --- /dev/null +++ b/extern/NSFplay/nes_n106.h @@ -0,0 +1,74 @@ +#ifndef _NES_N106_H_ +#define _NES_N106_H_ +#include "../device.h" + +namespace xgm { + +class TrackInfoN106 : public TrackInfoBasic +{ +public: + int wavelen; + INT16 wave[256]; + virtual IDeviceInfo *Clone(){ return new TrackInfoN106(*this); } +}; + +class NES_N106:public ISoundChip +{ +public: + enum + { + OPT_SERIAL = 0, + OPT_PHASE_READ_ONLY = 1, + OPT_LIMIT_WAVELENGTH = 2, + OPT_END + }; + +protected: + double rate, clock; + int mask; + INT32 sm[2][8]; // stereo mix + INT32 fout[8]; // current output + TrackInfoN106 trkinfo[8]; + int option[OPT_END]; + + bool master_disable; + UINT32 reg[0x80]; // all state is contained here + unsigned int reg_select; + bool reg_advance; + int tick_channel; + int tick_clock; + int render_channel; + int render_clock; + int render_subclock; + + // convenience functions to interact with regs + inline UINT32 get_phase (int channel); + inline UINT32 get_freq (int channel); + inline UINT32 get_off (int channel); + inline UINT32 get_len (int channel); + inline INT32 get_vol (int channel); + inline INT32 get_sample (UINT32 index); + inline int get_channels (); + // for storing back the phase after modifying + inline void set_phase (UINT32 phase, int channel); + +public: + NES_N106 (); + ~NES_N106 (); + + virtual void Reset (); + virtual void Tick (UINT32 clocks); + virtual UINT32 Render (INT32 b[2]); + virtual bool Write (UINT32 adr, UINT32 val, UINT32 id=0); + virtual bool Read (UINT32 adr, UINT32 & val, UINT32 id=0); + virtual void SetRate (double); + virtual void SetClock (double); + virtual void SetOption (int, int); + virtual void SetMask (int m); + virtual void SetStereoMix (int trk, INT16 mixl, INT16 mixr); + virtual ITrackInfo *GetTrackInfo(int trk); +}; + +} // namespace xgm + +#endif diff --git a/extern/NSFplay/nes_vrc6.cpp b/extern/NSFplay/nes_vrc6.cpp new file mode 100644 index 000000000..6a19b5ea5 --- /dev/null +++ b/extern/NSFplay/nes_vrc6.cpp @@ -0,0 +1,264 @@ +#include "nes_vrc6.h" + +namespace xgm +{ + + NES_VRC6::NES_VRC6 () + { + SetClock (DEFAULT_CLOCK); + SetRate (DEFAULT_RATE); + + halt = false; + freq_shift = 0; + + for(int c=0;c<2;++c) + for(int t=0;t<3;++t) + sm[c][t] = 128; + } + + NES_VRC6::~NES_VRC6 () + { + } + + void NES_VRC6::SetStereoMix(int trk, xgm::INT16 mixl, xgm::INT16 mixr) + { + if (trk < 0) return; + if (trk > 2) return; + sm[0][trk] = mixl; + sm[1][trk] = mixr; + } + + ITrackInfo *NES_VRC6::GetTrackInfo(int trk) + { + if(trk<2) + { + trkinfo[trk].max_volume = 15; + trkinfo[trk].volume = volume[trk]; + trkinfo[trk]._freq = freq2[trk]; + trkinfo[trk].freq = freq2[trk]?clock/16/(freq2[trk]+1):0; + trkinfo[trk].tone = duty[trk]; + trkinfo[trk].key = (volume[trk]>0)&&enable[trk]&&!gate[trk]; + return &trkinfo[trk]; + } + else if(trk==2) + { + trkinfo[2].max_volume = 255; + trkinfo[2].volume = volume[2]; + trkinfo[2]._freq = freq2[2]; + trkinfo[2].freq = freq2[2]?clock/14/(freq2[2]+1):0; + trkinfo[2].tone = -1; + trkinfo[2].key = (enable[2]>0); + return &trkinfo[2]; + } + else + return NULL; + } + + void NES_VRC6::SetClock (double c) + { + clock = c; + } + + void NES_VRC6::SetRate (double r) + { + rate = r ? r : DEFAULT_RATE; + } + + void NES_VRC6::SetOption (int id, int val) + { + if(id freq2[i]) + { + phase[i] = (phase[i] + 1) & 15; + counter[i] -= (freq2[i] + 1); + } + } + + return (gate[i] + || sqrtbl[duty[i]][phase[i]])? volume[i] : 0; + } + + INT16 NES_VRC6::calc_saw (UINT32 clocks) + { + if (!enable[2]) + return 0; + + if (!halt) + { + counter[2] += clocks; + while(counter[2] > freq2[2]) + { + counter[2] -= (freq2[2] + 1); + + // accumulate saw + ++count14; + if (count14 >= 14) + { + count14 = 0; + phase[2] = 0; + } + else if (0 == (count14 & 1)) // only accumulate on even ticks + { + phase[2] = (phase[2] + volume[2]) & 0xFF; // note 8-bit wrapping behaviour + } + } + } + + // only top 5 bits of saw are output + return phase[2] >> 3; + } + + void NES_VRC6::Tick (UINT32 clocks) + { + out[0] = calc_sqr(0,clocks); + out[1] = calc_sqr(1,clocks); + out[2] = calc_saw(clocks); + } + + UINT32 NES_VRC6::Render (INT32 b[2]) + { + INT32 m[3]; + m[0] = out[0]; + m[1] = out[1]; + m[2] = out[2]; + + // note: signal is inverted compared to 2A03 + + m[0] = (mask & 1) ? 0 : -m[0]; + m[1] = (mask & 2) ? 0 : -m[1]; + m[2] = (mask & 4) ? 0 : -m[2]; + + b[0] = m[0] * sm[0][0]; + b[0] += m[1] * sm[0][1]; + b[0] += m[2] * sm[0][2]; + //b[0] >>= (7 - 7); + + b[1] = m[0] * sm[1][0]; + b[1] += m[1] * sm[1][1]; + b[1] += m[2] * sm[1][2]; + //b[1] >>= (7 - 7); + + // master volume adjustment + const INT32 MASTER = INT32(256.0 * 1223.0 / 1920.0); + b[0] = (b[0] * MASTER) >> 8; + b[1] = (b[1] * MASTER) >> 8; + + return 2; + } + + bool NES_VRC6::Write (UINT32 adr, UINT32 val, UINT32 id) + { + int ch, cmap[4] = { 0, 0, 1, 2 }; + + switch (adr) + { + case 0x9000: + case 0xa000: + ch = cmap[(adr >> 12) & 3]; + volume[ch] = val & 15; + duty[ch] = (val >> 4) & 7; + gate[ch] = (val >> 7) & 1; + break; + case 0xb000: + volume[2] = val & 63; + break; + + case 0x9001: + case 0xa001: + case 0xb001: + ch = cmap[(adr >> 12) & 3]; + freq[ch] = (freq[ch] & 0xf00) | val; + freq2[ch] = (freq[ch] >> freq_shift); + if (counter[ch] > freq2[ch]) counter[ch] = freq2[ch]; + break; + + case 0x9002: + case 0xa002: + case 0xb002: + ch = cmap[(adr >> 12) & 3]; + freq[ch] = ((val & 0xf) << 8) + (freq[ch] & 0xff); + freq2[ch] = (freq[ch] >> freq_shift); + if (counter[ch] > freq2[ch]) counter[ch] = freq2[ch]; + if (!enable[ch]) // if enable is being turned on, phase should be reset + { + if (ch == 2) + { + count14 = 0; // reset saw + } + phase[ch] = 0; + } + enable[ch] = (val >> 7) & 1; + break; + + case 0x9003: + halt = val & 1; + freq_shift = + (val & 4) ? 8 : + (val & 2) ? 4 : + 0; + freq2[0] = (freq[0] >> freq_shift); + freq2[1] = (freq[1] >> freq_shift); + freq2[2] = (freq[2] >> freq_shift); + if (counter[0] > freq2[0]) counter[0] = freq2[0]; + if (counter[1] > freq2[1]) counter[1] = freq2[1]; + if (counter[2] > freq2[2]) counter[2] = freq2[2]; + break; + + default: + return false; + + } + + return true; + } + + bool NES_VRC6::Read (UINT32 adr, UINT32 & val, UINT32 id) + { + return false; + } + + +} // namespace diff --git a/extern/NSFplay/nes_vrc6.h b/extern/NSFplay/nes_vrc6.h new file mode 100644 index 000000000..2e277e6ee --- /dev/null +++ b/extern/NSFplay/nes_vrc6.h @@ -0,0 +1,56 @@ +#ifndef _NES_VRC6_H_ +#define _NES_VRC6_H_ +#include "../device.h" + +namespace xgm +{ + + class NES_VRC6:public ISoundChip + { + public: + enum + { + OPT_END + }; + protected: + UINT32 counter[3]; // frequency divider + UINT32 phase[3]; // phase counter + UINT32 freq2[3]; // adjusted frequency + int count14; // saw 14-stage counter + + //int option[OPT_END]; + int mask; + INT32 sm[2][3]; // stereo mix + int duty[2]; + int volume[3]; + int enable[3]; + int gate[3]; + UINT32 freq[3]; + INT16 calc_sqr (int i, UINT32 clocks); + INT16 calc_saw (UINT32 clocks); + bool halt; + int freq_shift; + double clock, rate; + INT32 out[3]; + TrackInfoBasic trkinfo[3]; + + public: + NES_VRC6 (); + ~NES_VRC6 (); + + virtual void Reset (); + virtual void Tick (UINT32 clocks); + virtual UINT32 Render (INT32 b[2]); + virtual bool Read (UINT32 adr, UINT32 & val, UINT32 id=0); + virtual bool Write (UINT32 adr, UINT32 val, UINT32 id=0); + virtual void SetClock (double); + virtual void SetRate (double); + virtual void SetOption (int, int); + virtual void SetMask (int m){ mask = m; } + virtual void SetStereoMix (int trk, xgm::INT16 mixl, xgm::INT16 mixr); + virtual ITrackInfo *GetTrackInfo(int trk); + }; + +} // namespace + +#endif diff --git a/extern/NSFplay/nes_vrc7.cpp b/extern/NSFplay/nes_vrc7.cpp new file mode 100644 index 000000000..4123fb36b --- /dev/null +++ b/extern/NSFplay/nes_vrc7.cpp @@ -0,0 +1,189 @@ +#include +#include "nes_vrc7.h" + +namespace xgm +{ + NES_VRC7::NES_VRC7 () + { + use_all_channels = false; + patch_set = OPLL_VRC7_TONE; + patch_custom = NULL; + divider = 0; + + opll = OPLL_new ( 3579545, DEFAULT_RATE); + OPLL_reset_patch (opll, patch_set); + SetClock(DEFAULT_CLOCK); + + for(int c=0;c<2;++c) + //for(int t=0;t<6;++t) + for(int t=0;t<9;++t) // HACK for YM2413 support + sm[c][t] = 128; + } + + NES_VRC7::~NES_VRC7 () + { + OPLL_delete (opll); + } + + void NES_VRC7::UseAllChannels(bool b) + { + use_all_channels = b; + } + + void NES_VRC7::SetPatchSet(int p) + { + patch_set = p; + } + + void NES_VRC7::SetPatchSetCustom (const UINT8* pset) + { + patch_custom = pset; + } + + void NES_VRC7::SetClock (double c) + { + clock = c / 36; + } + + void NES_VRC7::SetRate (double r) + { + //rate = r ? r : DEFAULT_RATE; + (void)r; // rate is ignored + rate = 49716; + OPLL_set_quality(opll, 1); // quality always on (not really a CPU hog) + OPLL_set_rate(opll,(uint32_t)rate); + } + + void NES_VRC7::SetOption (int id, int val) + { + if(id 5) return; + if (trk > 8) return; // HACK YM2413 + sm[0][trk] = mixl; + sm[1][trk] = mixr; + } + + ITrackInfo *NES_VRC7::GetTrackInfo(int trk) + { + //if(opll&&trk<6) + if(opll&&trk<9) // HACK YM2413 (percussion mode isn't very diagnostic this way though) + { + trkinfo[trk].max_volume = 15; + trkinfo[trk].volume = 15 - ((opll->reg[0x30+trk])&15); + trkinfo[trk]._freq = opll->reg[0x10+trk]+((opll->reg[0x20+trk]&1)<<8); + int blk = (opll->reg[0x20+trk]>>1)&7; + trkinfo[trk].freq = clock*trkinfo[trk]._freq/(double)(0x80000>>blk); + trkinfo[trk].tone = (opll->reg[0x30+trk]>>4)&15; + //trkinfo[trk].key = (opll->reg[0x20+trk]&0x10)?true:false; + trkinfo[trk].key = opll->slot_key_status & (3 << trk)?true:false; + return &trkinfo[trk]; + } + else + return NULL; + } + + bool NES_VRC7::Write (UINT32 adr, UINT32 val, UINT32 id) + { + if (adr == 0x9010) + { + OPLL_writeIO (opll, 0, val); + return true; + } + if (adr == 0x9030) + { + OPLL_writeIO (opll, 1, val); + return true; + } + else + return false; + } + + bool NES_VRC7::Read (UINT32 adr, UINT32 & val, UINT32 id) + { + return false; + } + + void NES_VRC7::Tick (UINT32 clocks) + { + divider += clocks; + while (divider >= 36) + { + divider -= 36; + OPLL_calc(opll); + } + } + + UINT32 NES_VRC7::Render (INT32 b[2]) + { + b[0] = b[1] = 0; + for (int i=0; i < 6; ++i) + { + INT32 val = (mask & (1<ch_out[i] >> 4; + b[0] += val * sm[0][i]; + b[1] += val * sm[1][i]; + } + + // HACK for YM2413 support + if (use_all_channels) + { + for (int i=6; i < 9; ++i) + { + if (mask & (1<patch_number[i] > 15) // rhytm mode + { + if (i == 6) val = opll->ch_out[9]; // BD + else if (i == 7) val = opll->ch_out[10] + opll->ch_out[11]; // HH&SD + else val = opll->ch_out[12] + opll->ch_out[13]; // TOM&CYM + /* (i == 8) is implied */ + } + else + { + val = opll->ch_out[i]; + } + val >>= 4; + b[0] += val * sm[0][i]; + b[1] += val * sm[1][i]; + } + } + + b[0] >>= (7 - 4); + b[1] >>= (7 - 4); + + // master volume adjustment + const INT32 MASTER = INT32(1.15 * 256.0); + b[0] = (b[0] * MASTER) >> 8; + b[1] = (b[1] * MASTER) >> 8; + + return 2; + } +} diff --git a/extern/NSFplay/nes_vrc7.h b/extern/NSFplay/nes_vrc7.h new file mode 100644 index 000000000..e8a91d61d --- /dev/null +++ b/extern/NSFplay/nes_vrc7.h @@ -0,0 +1,53 @@ +#ifndef _NES_VRC7_H_ +#define _NES_VRC7_H_ +#include "../device.h" +#include "legacy/emu2413.h" + +namespace xgm +{ + + class NES_VRC7 : public ISoundChip + { + public: + enum + { + OPT_OPLL=0, + OPT_END + }; + protected: + int option[OPT_END]; + int mask; + int patch_set; + const UINT8* patch_custom; + //INT32 sm[2][6]; // stereo mix + INT32 sm[2][9]; // stereo mix temporary HACK to support YM2413 + INT16 buf[2]; + OPLL *opll; + UINT32 divider; // clock divider + double clock, rate; + //TrackInfoBasic trkinfo[6]; + TrackInfoBasic trkinfo[9]; // HACK to support YM2413 + bool use_all_channels; + public: + NES_VRC7 (); + ~NES_VRC7 (); + + virtual void Reset (); + virtual void Tick (UINT32 clocks); + virtual UINT32 Render (INT32 b[2]); + virtual bool Read (UINT32 adr, UINT32 & val, UINT32 id=0); + virtual bool Write (UINT32 adr, UINT32 val, UINT32 id=0); + virtual void UseAllChannels (bool b); + virtual void SetPatchSet (int p); + virtual void SetPatchSetCustom (const UINT8* pset); + virtual void SetClock (double); + virtual void SetRate (double); + virtual void SetOption (int, int); + virtual void SetMask (int m){ mask = m; if(opll) OPLL_setMask(opll, m); } + virtual void SetStereoMix (int trk, xgm::INT16 mixl, xgm::INT16 mixr); + virtual ITrackInfo *GetTrackInfo(int trk); + }; + +} // namespace + +#endif From f6fe93b0edf448d51c4529e3c9eb21fa1257c95b Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 1 May 2022 22:13:11 -0500 Subject: [PATCH 275/342] convertir de Shift-JIS a UTF-8 por eso es que no deseaba aceptar el uso de NSFplay --- extern/NSFplay/legacy/2413tone.h | 22 - extern/NSFplay/legacy/281btone.h | 20 - extern/NSFplay/legacy/emu2149.c | 363 ------ extern/NSFplay/legacy/emu2149.h | 94 -- extern/NSFplay/legacy/emu2212.c | 485 -------- extern/NSFplay/legacy/emu2212.h | 77 -- extern/NSFplay/legacy/emu2413.c | 1461 ------------------------- extern/NSFplay/legacy/emu2413.h | 247 ----- extern/NSFplay/legacy/emutypes.h | 41 - extern/NSFplay/legacy/vrc7tone_ft35.h | 20 - extern/NSFplay/legacy/vrc7tone_ft36.h | 21 - extern/NSFplay/legacy/vrc7tone_kt1.h | 21 - extern/NSFplay/legacy/vrc7tone_kt2.h | 21 - extern/NSFplay/legacy/vrc7tone_mo.h | 20 - extern/NSFplay/legacy/vrc7tone_nuke.h | 21 - extern/NSFplay/legacy/vrc7tone_rw.h | 21 - extern/NSFplay/nes_apu.cpp | 800 +++++++------- extern/NSFplay/nes_apu.h | 4 +- extern/NSFplay/nes_dmc.cpp | 22 +- extern/NSFplay/nes_dmc.h | 258 ++--- extern/NSFplay/nes_fds.cpp | 2 +- extern/NSFplay/nes_fds.h | 2 +- extern/NSFplay/nes_fme7.cpp | 186 ---- extern/NSFplay/nes_fme7.h | 42 - extern/NSFplay/nes_mmc5.cpp | 2 +- extern/NSFplay/nes_mmc5.h | 6 +- extern/NSFplay/nes_n106.cpp | 2 +- extern/NSFplay/nes_n106.h | 2 +- extern/NSFplay/nes_vrc6.cpp | 2 +- extern/NSFplay/nes_vrc6.h | 2 +- extern/NSFplay/nes_vrc7.cpp | 189 ---- extern/NSFplay/nes_vrc7.h | 53 - 32 files changed, 552 insertions(+), 3977 deletions(-) delete mode 100644 extern/NSFplay/legacy/2413tone.h delete mode 100644 extern/NSFplay/legacy/281btone.h delete mode 100644 extern/NSFplay/legacy/emu2149.c delete mode 100644 extern/NSFplay/legacy/emu2149.h delete mode 100644 extern/NSFplay/legacy/emu2212.c delete mode 100644 extern/NSFplay/legacy/emu2212.h delete mode 100644 extern/NSFplay/legacy/emu2413.c delete mode 100644 extern/NSFplay/legacy/emu2413.h delete mode 100644 extern/NSFplay/legacy/emutypes.h delete mode 100644 extern/NSFplay/legacy/vrc7tone_ft35.h delete mode 100644 extern/NSFplay/legacy/vrc7tone_ft36.h delete mode 100644 extern/NSFplay/legacy/vrc7tone_kt1.h delete mode 100644 extern/NSFplay/legacy/vrc7tone_kt2.h delete mode 100644 extern/NSFplay/legacy/vrc7tone_mo.h delete mode 100644 extern/NSFplay/legacy/vrc7tone_nuke.h delete mode 100644 extern/NSFplay/legacy/vrc7tone_rw.h delete mode 100644 extern/NSFplay/nes_fme7.cpp delete mode 100644 extern/NSFplay/nes_fme7.h delete mode 100644 extern/NSFplay/nes_vrc7.cpp delete mode 100644 extern/NSFplay/nes_vrc7.h diff --git a/extern/NSFplay/legacy/2413tone.h b/extern/NSFplay/legacy/2413tone.h deleted file mode 100644 index 703979584..000000000 --- a/extern/NSFplay/legacy/2413tone.h +++ /dev/null @@ -1,22 +0,0 @@ -/* YM2413 tone by Mitsutaka Okazaki, 2020 */ -/* https://github.com/digital-sound-antiques/emu2413/blob/d2b9c8dfc5e84b7f8a535fdfee0149ac7bd84ca2/emu2413.c#L34 - */ -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0: Original -0x71, 0x61, 0x1e, 0x17, 0xd0, 0x78, 0x00, 0x17, // 1: Violin -0x13, 0x41, 0x1a, 0x0d, 0xd8, 0xf7, 0x23, 0x13, // 2: Guitar -0x13, 0x01, 0x99, 0x00, 0xf2, 0xc4, 0x21, 0x23, // 3: Piano -0x11, 0x61, 0x0e, 0x07, 0x8d, 0x64, 0x70, 0x27, // 4: Flute -0x32, 0x21, 0x1e, 0x06, 0xe1, 0x76, 0x01, 0x28, // 5: Clarinet -0x31, 0x22, 0x16, 0x05, 0xe0, 0x71, 0x00, 0x18, // 6: Oboe -0x21, 0x61, 0x1d, 0x07, 0x82, 0x81, 0x11, 0x07, // 7: Trumpet -0x33, 0x21, 0x2d, 0x13, 0xb0, 0x70, 0x00, 0x07, // 8: Organ -0x61, 0x61, 0x1b, 0x06, 0x64, 0x65, 0x10, 0x17, // 9: Horn -0x41, 0x61, 0x0b, 0x18, 0x85, 0xf0, 0x81, 0x07, // A: Synthesizer -0x33, 0x01, 0x83, 0x11, 0xea, 0xef, 0x10, 0x04, // B: Harpsichord -0x17, 0xc1, 0x24, 0x07, 0xf8, 0xf8, 0x22, 0x12, // C: Vibraphone -0x61, 0x50, 0x0c, 0x05, 0xd2, 0xf5, 0x40, 0x42, // D: Synthsizer Bass -0x01, 0x01, 0x55, 0x03, 0xe9, 0x90, 0x03, 0x02, // E: Acoustic Bass -0x41, 0x41, 0x89, 0x03, 0xf1, 0xe4, 0xc0, 0x13, // F: Electric Guitar -0x01, 0x01, 0x18, 0x0f, 0xdf, 0xf8, 0x6a, 0x6d, // R: Bass Drum (from VRC7) -0x01, 0x01, 0x00, 0x00, 0xc8, 0xd8, 0xa7, 0x68, // R: High-Hat(M) / Snare Drum(C) (from VRC7) -0x05, 0x01, 0x00, 0x00, 0xf8, 0xaa, 0x59, 0x55, // R: Tom-tom(M) / Top Cymbal(C) (from VRC7) diff --git a/extern/NSFplay/legacy/281btone.h b/extern/NSFplay/legacy/281btone.h deleted file mode 100644 index bb0068bab..000000000 --- a/extern/NSFplay/legacy/281btone.h +++ /dev/null @@ -1,20 +0,0 @@ -/* YMF281B tone by Chabin (4/10/2004) */ -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x62,0x21,0x1a,0x07,0xf0,0x6f,0x00,0x16, -0x00,0x10,0x44,0x02,0xf6,0xf4,0x54,0x23, -0x03,0x01,0x97,0x04,0xf3,0xf3,0x13,0xf3, -0x01,0x61,0x0a,0x0f,0xfa,0x64,0x70,0x17, -0x22,0x21,0x1e,0x06,0xf0,0x76,0x00,0x28, -0x00,0x61,0x8a,0x0e,0xc0,0x61,0x00,0x07, -0x21,0x61,0x1b,0x07,0x84,0x80,0x17,0x17, -0x37,0x32,0xc9,0x01,0x66,0x64,0x40,0x28, -0x01,0x21,0x06,0x03,0xa5,0x71,0x51,0x07, -0x06,0x11,0x5e,0x07,0xf3,0xf2,0xf6,0x11, -0x00,0x20,0x18,0x06,0xf5,0xf3,0x20,0x26, -0x97,0x41,0x20,0x07,0xff,0xf4,0x22,0x22, -0x65,0x61,0x15,0x00,0xf7,0xf3,0x16,0xf4, -0x01,0x31,0x0e,0x07,0xfa,0xf3,0xff,0xff, -0x48,0x61,0x09,0x07,0xf1,0x94,0xf0,0xf5, -0x07,0x21,0x14,0x00,0xee,0xf8,0xff,0xf8, -0x01,0x31,0x00,0x00,0xf8,0xf7,0xf8,0xf7, -0x25,0x11,0x00,0x00,0xf8,0xfa,0xf8,0x55, diff --git a/extern/NSFplay/legacy/emu2149.c b/extern/NSFplay/legacy/emu2149.c deleted file mode 100644 index fa5142f73..000000000 --- a/extern/NSFplay/legacy/emu2149.c +++ /dev/null @@ -1,363 +0,0 @@ -/**************************************************************************** - - emu2149.c -- YM2149/AY-3-8910 emulator by Mitsutaka Okazaki 2001 - - 2001 04-28 : Version 1.00beta -- 1st Beta Release. - 2001 08-14 : Version 1.10 - 2001 10-03 : Version 1.11 -- Added PSG_set_quality(). - 2002 03-02 : Version 1.12 -- Removed PSG_init & PSG_close. - 2002 10-13 : Version 1.14 -- Fixed the envelope unit. - 2003 09-19 : Version 1.15 -- Added PSG_setMask and PSG_toggleMask - 2004 01-11 : Version 1.16 -- Fixed an envelope problem where the envelope - frequency register is written before key-on. - - References: - psg.vhd -- 2000 written by Kazuhiro Tsujikawa. - s_fme7.c -- 1999,2000 written by Mamiya (NEZplug). - ay8910.c -- 1998-2001 Author unknown (MAME). - MSX-Datapack -- 1991 ASCII Corp. - AY-3-8910 data sheet - -*****************************************************************************/ -#include -#include -#include -#include "emu2149.h" - -static e_uint32 voltbl[2][32] = { - {0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x03, 0x04, 0x05, 0x06, 0x07, 0x09, - 0x0B, 0x0D, 0x0F, 0x12, - 0x16, 0x1A, 0x1F, 0x25, 0x2D, 0x35, 0x3F, 0x4C, 0x5A, 0x6A, 0x7F, 0x97, - 0xB4, 0xD6, 0xEB, 0xFF}, - {0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x03, 0x05, 0x05, 0x07, 0x07, - 0x0B, 0x0B, 0x0F, 0x0F, - 0x16, 0x16, 0x1F, 0x1F, 0x2D, 0x2D, 0x3F, 0x3F, 0x5A, 0x5A, 0x7F, 0x7F, - 0xB4, 0xB4, 0xFF, 0xFF} -}; - -#define GETA_BITS 24 - -static void -internal_refresh (PSG * psg) -{ - if (psg->quality) - { - psg->base_incr = 1 << GETA_BITS; - psg->realstep = (e_uint32) ((1 << 31) / psg->rate); - psg->psgstep = (e_uint32) ((1 << 31) / (psg->clk / 16)); - psg->psgtime = 0; - } - else - { - psg->base_incr = - (e_uint32) ((double) psg->clk * (1 << GETA_BITS) / (16 * psg->rate)); - } -} - -EMU2149_API void -PSG_set_rate (PSG * psg, e_uint32 r) -{ - psg->rate = r ? r : 44100; - internal_refresh (psg); -} - -EMU2149_API void -PSG_set_quality (PSG * psg, e_uint32 q) -{ - psg->quality = q; - internal_refresh (psg); -} - -EMU2149_API PSG * -PSG_new (e_uint32 c, e_uint32 r) -{ - PSG *psg; - - psg = (PSG *) malloc (sizeof (PSG)); - if (psg == NULL) - return NULL; - - PSG_setVolumeMode (psg, EMU2149_VOL_DEFAULT); - psg->clk = c; - psg->rate = r ? r : 44100; - PSG_set_quality (psg, 0); - - return psg; -} - -EMU2149_API void -PSG_setVolumeMode (PSG * psg, int type) -{ - switch (type) - { - case 1: - psg->voltbl = voltbl[EMU2149_VOL_YM2149]; - break; - case 2: - psg->voltbl = voltbl[EMU2149_VOL_AY_3_8910]; - break; - default: - psg->voltbl = voltbl[EMU2149_VOL_DEFAULT]; - break; - } -} - -EMU2149_API e_uint32 -PSG_setMask (PSG *psg, e_uint32 mask) -{ - e_uint32 ret = 0; - if(psg) - { - ret = psg->mask; - psg->mask = mask; - } - return ret; -} - -EMU2149_API e_uint32 -PSG_toggleMask (PSG *psg, e_uint32 mask) -{ - e_uint32 ret = 0; - if(psg) - { - ret = psg->mask; - psg->mask ^= mask; - } - return ret; -} - -EMU2149_API void -PSG_reset (PSG * psg) -{ - int i; - - psg->base_count = 0; - - for (i = 0; i < 3; i++) - { - psg->cout[i] = 0; - psg->count[i] = 0x1000; - psg->freq[i] = 0; - psg->edge[i] = 0; - psg->volume[i] = 0; - } - - psg->mask = 0; - - for (i = 0; i < 16; i++) - psg->reg[i] = 0; - psg->adr = 0; - - psg->noise_seed = 0xffff; - psg->noise_count = 0x40; - psg->noise_freq = 0; - - psg->env_volume = 0; - psg->env_ptr = 0; - psg->env_freq = 0; - psg->env_count = 0; - psg->env_pause = 1; - - psg->out = 0; -} - -EMU2149_API void -PSG_delete (PSG * psg) -{ - free (psg); -} - -EMU2149_API e_uint8 -PSG_readIO (PSG * psg) -{ - return (e_uint8) (psg->reg[psg->adr]); -} - -EMU2149_API e_uint8 -PSG_readReg (PSG * psg, e_uint32 reg) -{ - return (e_uint8) (psg->reg[reg & 0x1f]); - -} - -EMU2149_API void -PSG_writeIO (PSG * psg, e_uint32 adr, e_uint32 val) -{ - if (adr & 1) - PSG_writeReg (psg, psg->adr, val); - else - psg->adr = val & 0x1f; -} - -INLINE static e_int16 -calc (PSG * psg) -{ - - int i, noise; - e_uint32 incr; - e_int32 mix = 0; - - psg->base_count += psg->base_incr; - incr = (psg->base_count >> GETA_BITS); - psg->base_count &= (1 << GETA_BITS) - 1; - - /* Envelope */ - psg->env_count += incr; - while (psg->env_count>=0x10000 && psg->env_freq!=0) - { - if (!psg->env_pause) - { - if(psg->env_face) - psg->env_ptr = (psg->env_ptr + 1) & 0x3f ; - else - psg->env_ptr = (psg->env_ptr + 0x3f) & 0x3f; - } - - if (psg->env_ptr & 0x20) /* if carry or borrow */ - { - if (psg->env_continue) - { - if (psg->env_alternate^psg->env_hold) psg->env_face ^= 1; - if (psg->env_hold) psg->env_pause = 1; - psg->env_ptr = psg->env_face?0:0x1f; - } - else - { - psg->env_pause = 1; - psg->env_ptr = 0; - } - } - - psg->env_count -= psg->env_freq; - } - - /* Noise */ - psg->noise_count += incr; - if (psg->noise_count & 0x40) - { - if (psg->noise_seed & 1) - psg->noise_seed ^= 0x24000; - psg->noise_seed >>= 1; - psg->noise_count -= psg->noise_freq; - } - noise = psg->noise_seed & 1; - - /* Tone */ - for (i = 0; i < 3; i++) - { - psg->count[i] += incr; - if (psg->count[i] & 0x1000) - { - if (psg->freq[i] > 1) - { - psg->edge[i] = !psg->edge[i]; - psg->count[i] -= psg->freq[i]; - } - else - { - psg->edge[i] = 1; - } - } - - psg->cout[i] = 0; // maintaining cout for stereo mix - - if (psg->mask&PSG_MASK_CH(i)) - continue; - - if ((psg->tmask[i] || psg->edge[i]) && (psg->nmask[i] || noise)) - { - if (!(psg->volume[i] & 32)) - psg->cout[i] = psg->voltbl[psg->volume[i] & 31]; - else - psg->cout[i] = psg->voltbl[psg->env_ptr]; - - mix += psg->cout[i]; - } - } - - return (e_int16) mix; -} - -EMU2149_API e_int16 -PSG_calc (PSG * psg) -{ - if (!psg->quality) - return (e_int16) (calc (psg) << 4); - - /* Simple rate converter */ - while (psg->realstep > psg->psgtime) - { - psg->psgtime += psg->psgstep; - psg->out += calc (psg); - psg->out >>= 1; - } - - psg->psgtime = psg->psgtime - psg->realstep; - - return (e_int16) (psg->out << 4); -} - -EMU2149_API void -PSG_writeReg (PSG * psg, e_uint32 reg, e_uint32 val) -{ - int c; - - if (reg > 15) return; - - psg->reg[reg] = (e_uint8) (val & 0xff); - switch (reg) - { - case 0: - case 1: - case 2: - case 3: - case 4: - case 5: - c = reg >> 1; - psg->freq[c] = ((psg->reg[c * 2 + 1] & 15) << 8) + psg->reg[c * 2]; - break; - - case 6: - psg->noise_freq = (val == 0) ? 1 : ((val & 31) << 1); - break; - - case 7: - psg->tmask[0] = (val & 1); - psg->tmask[1] = (val & 2); - psg->tmask[2] = (val & 4); - psg->nmask[0] = (val & 8); - psg->nmask[1] = (val & 16); - psg->nmask[2] = (val & 32); - break; - - case 8: - case 9: - case 10: - psg->volume[reg - 8] = val << 1; - - break; - - case 11: - case 12: - psg->env_freq = (psg->reg[12] << 8) + psg->reg[11]; - break; - - case 13: - psg->env_continue = (val >> 3) & 1; - psg->env_attack = (val >> 2) & 1; - psg->env_alternate = (val >> 1) & 1; - psg->env_hold = val & 1; - psg->env_face = psg->env_attack; - psg->env_pause = 0; - psg->env_count = 0x10000 - psg->env_freq; - psg->env_ptr = psg->env_face?0:0x1f; - break; - - case 14: - case 15: - default: - break; - } - - return; -} \ No newline at end of file diff --git a/extern/NSFplay/legacy/emu2149.h b/extern/NSFplay/legacy/emu2149.h deleted file mode 100644 index a2fba126b..000000000 --- a/extern/NSFplay/legacy/emu2149.h +++ /dev/null @@ -1,94 +0,0 @@ -/* emu2149.h */ -#ifndef _EMU2149_H_ -#define _EMU2149_H_ -#include "emutypes.h" - -#ifdef EMU2149_DLL_EXPORTS -#define EMU2149_API __declspec(dllexport) -#elif EMU2149_DLL_IMPORTS -#define EMU2149_API __declspec(dllimport) -#else -#define EMU2149_API -#endif - -#define EMU2149_VOL_DEFAULT 1 -#define EMU2149_VOL_YM2149 0 -#define EMU2149_VOL_AY_3_8910 1 - -#define PSG_MASK_CH(x) (1<<(x)) - -#ifdef __cplusplus -extern "C" -{ -#endif - - typedef struct __PSG - { - - /* Volume Table */ - e_uint32 *voltbl; - - e_uint8 reg[0x20]; - e_int32 out; - e_int32 cout[3]; - - e_uint32 clk, rate, base_incr, quality; - - e_uint32 count[3]; - e_uint32 volume[3]; - e_uint32 freq[3]; - e_uint32 edge[3]; - e_uint32 tmask[3]; - e_uint32 nmask[3]; - e_uint32 mask; - - e_uint32 base_count; - - e_uint32 env_volume; - e_uint32 env_ptr; - e_uint32 env_face; - - e_uint32 env_continue; - e_uint32 env_attack; - e_uint32 env_alternate; - e_uint32 env_hold; - e_uint32 env_pause; - e_uint32 env_reset; - - e_uint32 env_freq; - e_uint32 env_count; - - e_uint32 noise_seed; - e_uint32 noise_count; - e_uint32 noise_freq; - - /* rate converter */ - e_uint32 realstep; - e_uint32 psgtime; - e_uint32 psgstep; - - /* I/O Ctrl */ - e_uint32 adr; - - } - PSG; - - EMU2149_API void PSG_set_quality (PSG * psg, e_uint32 q); - EMU2149_API void PSG_set_rate (PSG * psg, e_uint32 r); - EMU2149_API PSG *PSG_new (e_uint32 clk, e_uint32 rate); - EMU2149_API void PSG_reset (PSG *); - EMU2149_API void PSG_delete (PSG *); - EMU2149_API void PSG_writeReg (PSG *, e_uint32 reg, e_uint32 val); - EMU2149_API void PSG_writeIO (PSG * psg, e_uint32 adr, e_uint32 val); - EMU2149_API e_uint8 PSG_readReg (PSG * psg, e_uint32 reg); - EMU2149_API e_uint8 PSG_readIO (PSG * psg); - EMU2149_API e_int16 PSG_calc (PSG *); - EMU2149_API void PSG_setVolumeMode (PSG * psg, int type); - EMU2149_API e_uint32 PSG_setMask (PSG *, e_uint32 mask); - EMU2149_API e_uint32 PSG_toggleMask (PSG *, e_uint32 mask); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/extern/NSFplay/legacy/emu2212.c b/extern/NSFplay/legacy/emu2212.c deleted file mode 100644 index 88d93382c..000000000 --- a/extern/NSFplay/legacy/emu2212.c +++ /dev/null @@ -1,485 +0,0 @@ -/**************************************************************************** - - emu2212.c -- S.C.C. emulator by Mitsutaka Okazaki 2001 - - 2001 09-30 : Version 1.00 - 2001 10-03 : Version 1.01 -- Added SCC_set_quality(). - 2002 02-14 : Version 1.10 -- Added SCC_writeReg(), SCC_set_type(). - Fixed SCC_write(). - 2002 02-17 : Version 1.11 -- Fixed SCC_write(). - 2002 03-02 : Version 1.12 -- Removed SCC_init & SCC_close. - 2003 09-19 : Version 1.13 -- Added SCC_setMask() and SCC_toggleMask() - 2004 10-21 : Version 1.14 -- Fixed the problem where SCC+ is disabled. - - Registar map for SCC_writeReg() - - $00-1F : WaveTable CH.A - $20-3F : WaveTable CH.B - $40-5F : WaveTable CH.C - $60-7F : WaveTable CH.D&E(SCC), CH.D(SCC+) - $80-9F : WaveTable CH.E - - $C0 : CH.A Freq(L) - $C1 : CH.A Freq(H) - $C2 : CH.B Freq(L) - $C3 : CH.B Freq(H) - $C4 : CH.C Freq(L) - $C5 : CH.C Freq(H) - $C6 : CH.D Freq(L) - $C7 : CH.D Freq(H) - $C8 : CH.E Freq(L) - $C9 : CH.E Freq(H) - - $D0 : CH.A Volume - $D1 : CH.B Volume - $D2 : CH.C Volume - $D3 : CH.D Volume - $D4 : CH.E Volume - - $E0 : Bit0 = 0:SCC, 1:SCC+ - $E1 : CH mask - $E2 : Extra Flags - -*****************************************************************************/ -#include -#include -#include -#include "emu2212.h" - -#define GETA_BITS 22 - -static void -internal_refresh (SCC * scc) -{ - if (scc->quality) - { - scc->base_incr = 2 << GETA_BITS; - scc->realstep = (e_uint32) ((1 << 31) / scc->rate); - scc->sccstep = (e_uint32) ((1 << 31) / (scc->clk / 2)); - scc->scctime = 0; - } - else - { - scc->base_incr = (e_uint32) ((double) scc->clk * (1 << GETA_BITS) / scc->rate); - } -} - -EMU2212_API e_uint32 -SCC_setMask (SCC *scc, e_uint32 mask) -{ - e_uint32 ret = 0; - if(scc) - { - ret = scc->mask; - scc->mask = mask; - } - return ret; -} - -EMU2212_API e_uint32 -SCC_toggleMask (SCC *scc, e_uint32 mask) -{ - e_uint32 ret = 0; - if(scc) - { - ret = scc->mask; - scc->mask ^= mask; - } - return ret; -} - -EMU2212_API void -SCC_set_quality (SCC * scc, e_uint32 q) -{ - scc->quality = q; - internal_refresh (scc); -} - -EMU2212_API void -SCC_set_rate (SCC * scc, e_uint32 r) -{ - scc->rate = r ? r : 44100; - internal_refresh (scc); -} - -EMU2212_API SCC * -SCC_new (e_uint32 c, e_uint32 r) -{ - SCC *scc; - - scc = (SCC *) malloc (sizeof (SCC)); - if (scc == NULL) - return NULL; - memset(scc, 0, sizeof (SCC)); - - scc->clk = c; - scc->rate = r ? r : 44100; - SCC_set_quality (scc, 0); - scc->type = SCC_ENHANCED; - return scc; -} - -EMU2212_API void -SCC_reset (SCC * scc) -{ - int i, j; - - if (scc == NULL) - return; - - scc->mode = 0; - scc->active = 0; - scc->base_adr = 0x9000; - - for (i = 0; i < 5; i++) - { - for (j = 0; j < 5; j++) - scc->wave[i][j] = 0; - scc->count[i] = 0; - scc->freq[i] = 0; - scc->phase[i] = 0; - scc->volume[i] = 0; - scc->offset[i] = 0; - scc->rotate[i] = 0; - } - - memset(scc->reg,0,0x100-0xC0); - - scc->mask = 0; - - scc->ch_enable = 0xff; - scc->ch_enable_next = 0xff; - - scc->cycle_4bit = 0; - scc->cycle_8bit = 0; - scc->refresh = 0; - - scc->out = 0; - scc->prev = 0; - scc->next = 0; - - return; -} - -EMU2212_API void -SCC_delete (SCC * scc) -{ - if (scc != NULL) - free (scc); -} - -INLINE static e_int16 -calc (SCC * scc) -{ - int i; - e_int32 mix = 0; - - for (i = 0; i < 5; i++) - { - scc->count[i] = (scc->count[i] + scc->incr[i]); - - if (scc->count[i] & (1 << (GETA_BITS + 5))) - { - scc->count[i] &= ((1 << (GETA_BITS + 5)) - 1); - scc->offset[i] = (scc->offset[i] + 31) & scc->rotate[i]; - scc->ch_enable &= ~(1 << i); - scc->ch_enable |= scc->ch_enable_next & (1 << i); - } - - if (scc->ch_enable & (1 << i)) - { - scc->phase[i] = ((scc->count[i] >> (GETA_BITS)) + scc->offset[i]) & 0x1F; - if(!(scc->mask&SCC_MASK_CH(i))) - mix += ((((e_int8) (scc->wave[i][scc->phase[i]]) * (e_int8) scc->volume[i]))) >> 4; - } - } - - return (e_int16) (mix << 4); -} - -EMU2212_API e_int16 -SCC_calc (SCC * scc) -{ - if (!scc->quality) - return calc (scc); - - while (scc->realstep > scc->scctime) - { - scc->scctime += scc->sccstep; - scc->prev = scc->next; - scc->next = calc (scc); - } - - scc->scctime -= scc->realstep; - scc->out = (e_int16) (((double) scc->next * (scc->sccstep - scc->scctime) + (double) scc->prev * scc->scctime) / scc->sccstep); - - return (e_int16) (scc->out); -} - -EMU2212_API e_uint32 -SCC_readReg (SCC * scc, e_uint32 adr) -{ - if (adr < 0xA0) - return scc->wave[adr >> 5][adr & 0x1f]; - else if( 0xC0 < adr && adr < 0xF0 ) - return scc->reg[adr-0xC0]; - else - return 0; -} - -EMU2212_API void -SCC_writeReg (SCC * scc, e_uint32 adr, e_uint32 val) -{ - int ch; - e_uint32 freq; - - adr &= 0xFF; - - if (adr < 0xA0) - { - ch = (adr & 0xF0) >> 5; - if (!scc->rotate[ch]) - { - scc->wave[ch][adr & 0x1F] = (e_int8) val; - if (scc->mode == 0 && ch == 3) - scc->wave[4][adr & 0x1F] = (e_int8) val; - } - } - else if (0xC0 <= adr && adr <= 0xC9) - { - scc->reg[adr-0xC0] = val; - ch = (adr & 0x0F) >> 1; - if (adr & 1) - scc->freq[ch] = ((val & 0xF) << 8) | (scc->freq[ch] & 0xFF); - else - scc->freq[ch] = (scc->freq[ch] & 0xF00) | (val & 0xFF); - - if (scc->refresh) - scc->count[ch] = 0; - freq = scc->freq[ch]; - if (scc->cycle_8bit) - freq &= 0xFF; - if (scc->cycle_4bit) - freq >>= 8; - if (freq <= 8) - scc->incr[ch] = 0; - else - scc->incr[ch] = scc->base_incr / (freq + 1); - } - else if (0xD0 <= adr && adr <= 0xD4) - { - scc->reg[adr-0xC0] = val; - scc->volume[adr & 0x0F] = (e_uint8) (val & 0xF); - } - else if (adr == 0xE0) - { - scc->reg[adr-0xC0] = val; - scc->mode = (e_uint8) val & 1; - } - else if (adr == 0xE1) - { - scc->reg[adr-0xC0] = val; - scc->ch_enable_next = (e_uint8) val & 0x1F; - } - else if (adr == 0xE2) - { - scc->reg[adr-0xC0] = val; - scc->cycle_4bit = val & 1; - scc->cycle_8bit = val & 2; - scc->refresh = val & 32; - if (val & 64) - for (ch = 0; ch < 5; ch++) - scc->rotate[ch] = 0x1F; - else - for (ch = 0; ch < 5; ch++) - scc->rotate[ch] = 0; - if (val & 128) - scc->rotate[3] = scc->rotate[4] = 0x1F; - } - - return; -} - -INLINE static void -write_standard (SCC * scc, e_uint32 adr, e_uint32 val) -{ - adr &= 0xFF; - - if (adr < 0x80) /* wave */ - { - SCC_writeReg (scc, adr, val); - } - else if (adr < 0x8A) /* freq */ - { - SCC_writeReg (scc, adr + 0xC0 - 0x80, val); - } - else if (adr < 0x8F) /* volume */ - { - SCC_writeReg (scc, adr + 0xD0 - 0x8A, val); - } - else if (adr == 0x8F) /* ch enable */ - { - SCC_writeReg (scc, 0xE1, val); - } - else if (0xE0 <= adr) /* flags */ - { - SCC_writeReg (scc, 0xE2, val); - } -} - -INLINE static void -write_enhanced (SCC * scc, e_uint32 adr, e_uint32 val) -{ - adr &= 0xFF; - - if (adr < 0xA0) /* wave */ - { - SCC_writeReg (scc, adr, val); - } - else if (adr < 0xAA) /* freq */ - { - SCC_writeReg (scc, adr + 0xC0 - 0xA0, val); - } - else if (adr < 0xAF) /* volume */ - { - SCC_writeReg (scc, adr + 0xD0 - 0xAA, val); - } - else if (adr == 0xAF) /* ch enable */ - { - SCC_writeReg (scc, 0xE1, val); - } - else if (0xC0 <= adr && adr <= 0xDF) /* flags */ - { - SCC_writeReg (scc, 0xE2, val); - } -} - -INLINE static e_uint32 -read_enhanced (SCC * scc, e_uint32 adr) -{ - adr &= 0xFF; - if (adr < 0xA0) - return SCC_readReg (scc, adr); - else if (adr < 0xAA) - return SCC_readReg (scc, adr + 0xC0 - 0xA0); - else if (adr < 0xAF) - return SCC_readReg (scc, adr + 0xD0 - 0xAA); - else if (adr == 0xAF) - return SCC_readReg (scc, 0xE1); - else if (0xC0 <= adr && adr <= 0xDF) - return SCC_readReg (scc, 0xE2); - else - return 0; -} - -INLINE static e_uint32 -read_standard (SCC * scc, e_uint32 adr) -{ - adr &= 0xFF; - if(adr<0x80) - return SCC_readReg (scc, adr); - else if (0xA0<=adr&&adr<=0xBF) - return SCC_readReg (scc, 0x80+(adr&0x1F)); - else if (adr < 0x8A) - return SCC_readReg (scc, adr + 0xC0 - 0x80); - else if (adr < 0x8F) - return SCC_readReg (scc, adr + 0xD0 - 0x8A); - else if (adr == 0x8F) - return SCC_readReg (scc, 0xE1); - else if (0xE0 <= adr) - return SCC_readReg (scc, 0xE2); - else return 0; -} - -EMU2212_API e_uint32 -SCC_read (SCC * scc, e_uint32 adr) -{ - if( scc->type == SCC_ENHANCED && (adr&0xFFFE) == 0xBFFE ) - return (scc->base_adr>>8)&0x20; - - if( adr < scc->base_adr ) return 0; - adr -= scc->base_adr; - - if( adr == 0 ) - { - if(scc->mode) return 0x80; else return 0x3F; - } - - if(!scc->active||adr<0x800||0x8FFtype) - { - case SCC_STANDARD: - return read_standard (scc, adr); - break; - case SCC_ENHANCED: - if(!scc->mode) - return read_standard (scc, adr); - else - return read_enhanced (scc, adr); - break; - default: - break; - } - - return 0; -} - -EMU2212_API void -SCC_write (SCC * scc, e_uint32 adr, e_uint32 val) -{ - val = val & 0xFF; - - if( scc->type == SCC_ENHANCED && (adr&0xFFFE) == 0xBFFE ) - { - scc->base_adr = 0x9000 | ((val&0x20)<<8); - return; - } - - if( adr < scc->base_adr ) return; - adr -= scc->base_adr; - - if(adr == 0) - { - if( val == 0x3F ) - { - scc->mode = 0; - scc->active = 1; - } - else if( val&0x80 && scc->type == SCC_ENHANCED) - { - scc->mode = 1; - scc->active = 1; - } - else - { - scc->mode = 0; - scc->active = 0; - } - return; - } - - if(!scc->active||adr<0x800||0x8FFtype) - { - case SCC_STANDARD: - write_standard (scc, adr, val); - break; - case SCC_ENHANCED: - if(scc->mode) - write_enhanced (scc, adr, val); - else - write_standard (scc, adr, val); - default: - break; - } - - return; -} - -EMU2212_API void -SCC_set_type (SCC * scc, e_uint32 type) -{ - scc->type = type; -} diff --git a/extern/NSFplay/legacy/emu2212.h b/extern/NSFplay/legacy/emu2212.h deleted file mode 100644 index b79e0c360..000000000 --- a/extern/NSFplay/legacy/emu2212.h +++ /dev/null @@ -1,77 +0,0 @@ -#ifndef _EMU2212_H_ -#define _EMU2212_H_ - -#ifdef EMU2212_DLL_EXPORTS - #define EMU2212_API __declspec(dllexport) -#elif defined(EMU2212_DLL_IMPORTS) - #define EMU2212_API __declspec(dllimport) -#else - #define EMU2212_API -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -#include "emutypes.h" - -#define SCC_STANDARD 0 -#define SCC_ENHANCED 1 - -#define SCC_MASK_CH(x) (1<<(x)) - -typedef struct __SCC { - - e_uint32 clk, rate ,base_incr, quality ; - - e_int32 out, prev, next; - e_uint32 type ; - e_uint32 mode ; - e_uint32 active; - e_uint32 base_adr; - e_uint32 mask ; - - e_uint32 realstep ; - e_uint32 scctime ; - e_uint32 sccstep ; - - e_uint32 incr[5] ; - - e_int8 wave[5][32] ; - - e_uint32 count[5] ; - e_uint32 freq[5] ; - e_uint32 phase[5] ; - e_uint32 volume[5] ; - e_uint32 offset[5] ; - e_uint8 reg[0x100-0xC0]; - - int ch_enable ; - int ch_enable_next ; - - int cycle_4bit ; - int cycle_8bit ; - int refresh ; - int rotate[5] ; - -} SCC ; - - -EMU2212_API SCC *SCC_new(e_uint32 c, e_uint32 r) ; -EMU2212_API void SCC_reset(SCC *scc) ; -EMU2212_API void SCC_set_rate(SCC *scc, e_uint32 r); -EMU2212_API void SCC_set_quality(SCC *scc, e_uint32 q) ; -EMU2212_API void SCC_set_type(SCC *scc, e_uint32 type) ; -EMU2212_API void SCC_delete(SCC *scc) ; -EMU2212_API e_int16 SCC_calc(SCC *scc) ; -EMU2212_API void SCC_write(SCC *scc, e_uint32 adr, e_uint32 val) ; -EMU2212_API void SCC_writeReg(SCC *scc, e_uint32 adr, e_uint32 val) ; -EMU2212_API e_uint32 SCC_read(SCC *scc, e_uint32 adr) ; -EMU2212_API e_uint32 SCC_setMask(SCC *scc, e_uint32 adr) ; -EMU2212_API e_uint32 SCC_toggleMask(SCC *scc, e_uint32 adr) ; - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/extern/NSFplay/legacy/emu2413.c b/extern/NSFplay/legacy/emu2413.c deleted file mode 100644 index 2ced98f86..000000000 --- a/extern/NSFplay/legacy/emu2413.c +++ /dev/null @@ -1,1461 +0,0 @@ -/** - * emu2413 v1.5.2 - * https://github.com/digital-sound-antiques/emu2413 - * Copyright (C) 2020 Mitsutaka Okazaki - * - * This source refers to the following documents. The author would like to thank all the authors who have - * contributed to the writing of them. - * - [YM2413 notes](http://www.smspower.org/Development/YM2413) by andete - * - ymf262.c by Jarek Burczynski - * - [VRC7 presets](https://siliconpr0n.org/archive/doku.php?id=vendor:yamaha:opl2#opll_vrc7_patch_format) by Nuke.YKT - * - YMF281B presets by Chabin - */ -#include "emu2413.h" -#include -#include -#include -#include - -#ifndef INLINE -#if defined(_MSC_VER) -#define INLINE __inline -#elif defined(__GNUC__) -#define INLINE __inline__ -#else -#define INLINE inline -#endif -#endif - -#define _PI_ 3.14159265358979323846264338327950288 - -#define OPLL_TONE_NUM 9 -static unsigned char default_inst[OPLL_TONE_NUM][(16 + 3) * 8] = { - { -#include "vrc7tone_nuke.h" - }, - { -#include "vrc7tone_rw.h" - }, - { -#include "vrc7tone_ft36.h" - }, - { -#include "vrc7tone_ft35.h" - }, - { -#include "vrc7tone_mo.h" - }, - { -#include "vrc7tone_kt2.h" - }, - { -#include "vrc7tone_kt1.h" - }, - { -#include "2413tone.h" - }, - { -#include "281btone.h" - }, -}; - -/* phase increment counter */ -#define DP_BITS 19 -#define DP_WIDTH (1 << DP_BITS) -#define DP_BASE_BITS (DP_BITS - PG_BITS) - -/* dynamic range of envelope output */ -#define EG_STEP 0.375 -#define EG_BITS 7 -#define EG_MUTE ((1 << EG_BITS) - 1) -#define EG_MAX (EG_MUTE - 3) - -/* dynamic range of total level */ -#define TL_STEP 0.75 -#define TL_BITS 6 - -/* dynamic range of sustine level */ -#define SL_STEP 3.0 -#define SL_BITS 4 - -/* damper speed before key-on. key-scale affects. */ -#define DAMPER_RATE 12 - -#define TL2EG(d) ((d) << 1) - -/* sine table */ -#define PG_BITS 10 /* 2^10 = 1024 length sine table */ -#define PG_WIDTH (1 << PG_BITS) - -/* clang-format off */ -/* exp_table[x] = round((exp2((double)x / 256.0) - 1) * 1024) */ -static uint16_t exp_table[256] = { -0, 3, 6, 8, 11, 14, 17, 20, 22, 25, 28, 31, 34, 37, 40, 42, -45, 48, 51, 54, 57, 60, 63, 66, 69, 72, 75, 78, 81, 84, 87, 90, -93, 96, 99, 102, 105, 108, 111, 114, 117, 120, 123, 126, 130, 133, 136, 139, -142, 145, 148, 152, 155, 158, 161, 164, 168, 171, 174, 177, 181, 184, 187, 190, -194, 197, 200, 204, 207, 210, 214, 217, 220, 224, 227, 231, 234, 237, 241, 244, -248, 251, 255, 258, 262, 265, 268, 272, 276, 279, 283, 286, 290, 293, 297, 300, -304, 308, 311, 315, 318, 322, 326, 329, 333, 337, 340, 344, 348, 352, 355, 359, -363, 367, 370, 374, 378, 382, 385, 389, 393, 397, 401, 405, 409, 412, 416, 420, -424, 428, 432, 436, 440, 444, 448, 452, 456, 460, 464, 468, 472, 476, 480, 484, -488, 492, 496, 501, 505, 509, 513, 517, 521, 526, 530, 534, 538, 542, 547, 551, -555, 560, 564, 568, 572, 577, 581, 585, 590, 594, 599, 603, 607, 612, 616, 621, -625, 630, 634, 639, 643, 648, 652, 657, 661, 666, 670, 675, 680, 684, 689, 693, -698, 703, 708, 712, 717, 722, 726, 731, 736, 741, 745, 750, 755, 760, 765, 770, -774, 779, 784, 789, 794, 799, 804, 809, 814, 819, 824, 829, 834, 839, 844, 849, -854, 859, 864, 869, 874, 880, 885, 890, 895, 900, 906, 911, 916, 921, 927, 932, -937, 942, 948, 953, 959, 964, 969, 975, 980, 986, 991, 996, 1002, 1007, 1013, 1018 -}; -/* fullsin_table[x] = round(-log2(sin((x + 0.5) * PI / (PG_WIDTH / 4) / 2)) * 256) */ -static uint16_t fullsin_table[PG_WIDTH] = { -2137, 1731, 1543, 1419, 1326, 1252, 1190, 1137, 1091, 1050, 1013, 979, 949, 920, 894, 869, -846, 825, 804, 785, 767, 749, 732, 717, 701, 687, 672, 659, 646, 633, 621, 609, -598, 587, 576, 566, 556, 546, 536, 527, 518, 509, 501, 492, 484, 476, 468, 461, -453, 446, 439, 432, 425, 418, 411, 405, 399, 392, 386, 380, 375, 369, 363, 358, -352, 347, 341, 336, 331, 326, 321, 316, 311, 307, 302, 297, 293, 289, 284, 280, -276, 271, 267, 263, 259, 255, 251, 248, 244, 240, 236, 233, 229, 226, 222, 219, -215, 212, 209, 205, 202, 199, 196, 193, 190, 187, 184, 181, 178, 175, 172, 169, -167, 164, 161, 159, 156, 153, 151, 148, 146, 143, 141, 138, 136, 134, 131, 129, -127, 125, 122, 120, 118, 116, 114, 112, 110, 108, 106, 104, 102, 100, 98, 96, -94, 92, 91, 89, 87, 85, 83, 82, 80, 78, 77, 75, 74, 72, 70, 69, -67, 66, 64, 63, 62, 60, 59, 57, 56, 55, 53, 52, 51, 49, 48, 47, -46, 45, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, -29, 28, 27, 26, 25, 24, 23, 23, 22, 21, 20, 20, 19, 18, 17, 17, -16, 15, 15, 14, 13, 13, 12, 12, 11, 10, 10, 9, 9, 8, 8, 7, -7, 7, 6, 6, 5, 5, 5, 4, 4, 4, 3, 3, 3, 2, 2, 2, -2, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, -}; -/* clang-format on */ - -static uint16_t halfsin_table[PG_WIDTH]; -static uint16_t *wave_table_map[2] = {fullsin_table, halfsin_table}; - -/* pitch modulator */ -/* offset to fnum, rough approximation of 14 cents depth. */ -static int8_t pm_table[8][8] = { - {0, 0, 0, 0, 0, 0, 0, 0}, // fnum = 000xxxxxx - {0, 0, 1, 0, 0, 0, -1, 0}, // fnum = 001xxxxxx - {0, 1, 2, 1, 0, -1, -2, -1}, // fnum = 010xxxxxx - {0, 1, 3, 1, 0, -1, -3, -1}, // fnum = 011xxxxxx - {0, 2, 4, 2, 0, -2, -4, -2}, // fnum = 100xxxxxx - {0, 2, 5, 2, 0, -2, -5, -2}, // fnum = 101xxxxxx - {0, 3, 6, 3, 0, -3, -6, -3}, // fnum = 110xxxxxx - {0, 3, 7, 3, 0, -3, -7, -3}, // fnum = 111xxxxxx -}; - -/* amplitude lfo table */ -/* The following envelop pattern is verified on real YM2413. */ -/* each element repeates 64 cycles */ -static uint8_t am_table[210] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, // - 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, // - 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, // - 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, // - 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, // - 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, // - 12, 12, 12, 12, 12, 12, 12, 12, // - 13, 13, 13, // - 12, 12, 12, 12, 12, 12, 12, 12, // - 11, 11, 11, 11, 11, 11, 11, 11, 10, 10, 10, 10, 10, 10, 10, 10, // - 9, 9, 9, 9, 9, 9, 9, 9, 8, 8, 8, 8, 8, 8, 8, 8, // - 7, 7, 7, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, 6, 6, 6, // - 5, 5, 5, 5, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, // - 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, // - 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0}; - -/* envelope decay increment step table */ -/* based on andete's research */ -static uint8_t eg_step_tables[4][8] = { - {0, 1, 0, 1, 0, 1, 0, 1}, - {0, 1, 0, 1, 1, 1, 0, 1}, - {0, 1, 1, 1, 0, 1, 1, 1}, - {0, 1, 1, 1, 1, 1, 1, 1}, -}; - -enum __OPLL_EG_STATE { ATTACK, DECAY, SUSTAIN, RELEASE, DAMP, UNKNOWN }; - -static uint32_t ml_table[16] = {1, 1 * 2, 2 * 2, 3 * 2, 4 * 2, 5 * 2, 6 * 2, 7 * 2, - 8 * 2, 9 * 2, 10 * 2, 10 * 2, 12 * 2, 12 * 2, 15 * 2, 15 * 2}; - -#define dB2(x) ((x)*2) -static double kl_table[16] = {dB2(0.000), dB2(9.000), dB2(12.000), dB2(13.875), dB2(15.000), dB2(16.125), - dB2(16.875), dB2(17.625), dB2(18.000), dB2(18.750), dB2(19.125), dB2(19.500), - dB2(19.875), dB2(20.250), dB2(20.625), dB2(21.000)}; - -static uint32_t tll_table[8 * 16][1 << TL_BITS][4]; -static int32_t rks_table[8 * 2][2]; - -static OPLL_PATCH null_patch = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; -static OPLL_PATCH default_patch[OPLL_TONE_NUM][(16 + 3) * 2]; - -#define min(i, j) (((i) < (j)) ? (i) : (j)) -#define max(i, j) (((i) > (j)) ? (i) : (j)) - -/*************************************************** - - Internal Sample Rate Converter - -****************************************************/ -/* Note: to disable internal rate converter, set clock/72 to output sampling rate. */ - -/* - * LW is truncate length of sinc(x) calculation. - * Lower LW is faster, higher LW results better quality. - * LW must be a non-zero positive even number, no upper limit. - * LW=16 or greater is recommended when upsampling. - * LW=8 is practically okay for downsampling. - */ -#define LW 16 - -/* resolution of sinc(x) table. sinc(x) where 0.0<=x<1.0 corresponds to sinc_table[0...SINC_RESO-1] */ -#define SINC_RESO 256 -#define SINC_AMP_BITS 12 - -// double hamming(double x) { return 0.54 - 0.46 * cos(2 * PI * x); } -static double blackman(double x) { return 0.42 - 0.5 * cos(2 * _PI_ * x) + 0.08 * cos(4 * _PI_ * x); } -static double sinc(double x) { return (x == 0.0 ? 1.0 : sin(_PI_ * x) / (_PI_ * x)); } -static double windowed_sinc(double x) { return blackman(0.5 + 0.5 * x / (LW / 2)) * sinc(x); } - -/* f_inp: input frequency. f_out: output frequencey, ch: number of channels */ -OPLL_RateConv *OPLL_RateConv_new(double f_inp, double f_out, int ch) { - OPLL_RateConv *conv = malloc(sizeof(OPLL_RateConv)); - int i; - - conv->ch = ch; - conv->f_ratio = f_inp / f_out; - conv->buf = malloc(sizeof(void *) * ch); - for (i = 0; i < ch; i++) { - conv->buf[i] = malloc(sizeof(conv->buf[0][0]) * LW); - } - - /* create sinc_table for positive 0 <= x < LW/2 */ - conv->sinc_table = malloc(sizeof(conv->sinc_table[0]) * SINC_RESO * LW / 2); - for (i = 0; i < SINC_RESO * LW / 2; i++) { - const double x = (double)i / SINC_RESO; - if (f_out < f_inp) { - /* for downsampling */ - conv->sinc_table[i] = (int16_t)((1 << SINC_AMP_BITS) * windowed_sinc(x / conv->f_ratio) / conv->f_ratio); - } else { - /* for upsampling */ - conv->sinc_table[i] = (int16_t)((1 << SINC_AMP_BITS) * windowed_sinc(x)); - } - } - - return conv; -} - -static INLINE int16_t lookup_sinc_table(int16_t *table, double x) { - int16_t index = (int16_t)(x * SINC_RESO); - if (index < 0) - index = -index; - return table[min(SINC_RESO * LW / 2 - 1, index)]; -} - -void OPLL_RateConv_reset(OPLL_RateConv *conv) { - int i; - conv->timer = 0; - for (i = 0; i < conv->ch; i++) { - memset(conv->buf[i], 0, sizeof(conv->buf[i][0]) * LW); - } -} - -/* put original data to this converter at f_inp. */ -void OPLL_RateConv_putData(OPLL_RateConv *conv, int ch, int16_t data) { - int16_t *buf = conv->buf[ch]; - int i; - for (i = 0; i < LW - 1; i++) { - buf[i] = buf[i + 1]; - } - buf[LW - 1] = data; -} - -/* get resampled data from this converter at f_out. */ -/* this function must be called f_out / f_inp times per one putData call. */ -int16_t OPLL_RateConv_getData(OPLL_RateConv *conv, int ch) { - int16_t *buf = conv->buf[ch]; - int32_t sum = 0; - int k; - double dn; - conv->timer += conv->f_ratio; - dn = conv->timer - floor(conv->timer); - conv->timer = dn; - - for (k = 0; k < LW; k++) { - double x = ((double)k - (LW / 2 - 1)) - dn; - sum += buf[k] * lookup_sinc_table(conv->sinc_table, x); - } - return sum >> SINC_AMP_BITS; -} - -void OPLL_RateConv_delete(OPLL_RateConv *conv) { - int i; - for (i = 0; i < conv->ch; i++) { - free(conv->buf[i]); - } - free(conv->buf); - free(conv->sinc_table); - free(conv); -} - -/*************************************************** - - Create tables - -****************************************************/ - -static void makeSinTable(void) { - int x; - - for (x = 0; x < PG_WIDTH / 4; x++) { - fullsin_table[PG_WIDTH / 4 + x] = fullsin_table[PG_WIDTH / 4 - x - 1]; - } - - for (x = 0; x < PG_WIDTH / 2; x++) { - fullsin_table[PG_WIDTH / 2 + x] = 0x8000 | fullsin_table[x]; - } - - for (x = 0; x < PG_WIDTH / 2; x++) - halfsin_table[x] = fullsin_table[x]; - - for (x = PG_WIDTH / 2; x < PG_WIDTH; x++) - halfsin_table[x] = 0xfff; -} - -static void makeTllTable(void) { - - int32_t tmp; - int32_t fnum, block, TL, KL; - - for (fnum = 0; fnum < 16; fnum++) { - for (block = 0; block < 8; block++) { - for (TL = 0; TL < 64; TL++) { - for (KL = 0; KL < 4; KL++) { - if (KL == 0) { - tll_table[(block << 4) | fnum][TL][KL] = TL2EG(TL); - } else { - tmp = (int32_t)(kl_table[fnum] - dB2(3.000) * (7 - block)); - if (tmp <= 0) - tll_table[(block << 4) | fnum][TL][KL] = TL2EG(TL); - else - tll_table[(block << 4) | fnum][TL][KL] = (uint32_t)((tmp >> (3 - KL)) / EG_STEP) + TL2EG(TL); - } - } - } - } - } -} - -static void makeRksTable(void) { - int fnum8, block; - for (fnum8 = 0; fnum8 < 2; fnum8++) - for (block = 0; block < 8; block++) { - rks_table[(block << 1) | fnum8][1] = (block << 1) + fnum8; - rks_table[(block << 1) | fnum8][0] = block >> 1; - } -} - -static void makeDefaultPatch() { - int i, j; - for (i = 0; i < OPLL_TONE_NUM; i++) - for (j = 0; j < 19; j++) - OPLL_getDefaultPatch(i, j, &default_patch[i][j * 2]); -} - -static uint8_t table_initialized = 0; - -static void initializeTables() { - makeTllTable(); - makeRksTable(); - makeSinTable(); - makeDefaultPatch(); - table_initialized = 1; -} - -/********************************************************* - - Synthesizing - -*********************************************************/ -#define SLOT_BD1 12 -#define SLOT_BD2 13 -#define SLOT_HH 14 -#define SLOT_SD 15 -#define SLOT_TOM 16 -#define SLOT_CYM 17 - -/* utility macros */ -#define MOD(o, x) (&(o)->slot[(x) << 1]) -#define CAR(o, x) (&(o)->slot[((x) << 1) | 1]) -#define BIT(s, b) (((s) >> (b)) & 1) - -#if OPLL_DEBUG -static void _debug_print_patch(OPLL_SLOT *slot) { - OPLL_PATCH *p = slot->patch; - printf("[slot#%d am:%d pm:%d eg:%d kr:%d ml:%d kl:%d tl:%d ws:%d fb:%d A:%d D:%d S:%d R:%d]\n", slot->number, // - p->AM, p->PM, p->EG, p->KR, p->ML, // - p->KL, p->TL, p->WS, p->FB, // - p->AR, p->DR, p->SL, p->RR); -} - -static char *_debug_eg_state_name(OPLL_SLOT *slot) { - switch (slot->eg_state) { - case ATTACK: - return "attack"; - case DECAY: - return "decay"; - case SUSTAIN: - return "sustain"; - case RELEASE: - return "release"; - case DAMP: - return "damp"; - default: - return "unknown"; - } -} - -static INLINE void _debug_print_slot_info(OPLL_SLOT *slot) { - char *name = _debug_eg_state_name(slot); - printf("[slot#%d state:%s fnum:%03x rate:%d-%d]\n", slot->number, name, slot->blk_fnum, slot->eg_rate_h, - slot->eg_rate_l); - _debug_print_patch(slot); - fflush(stdout); -} -#endif - -static INLINE int get_parameter_rate(OPLL_SLOT *slot) { - - if ((slot->type & 1) == 0 && slot->key_flag == 0) { - return 0; - } - - switch (slot->eg_state) { - case ATTACK: - return slot->patch->AR; - case DECAY: - return slot->patch->DR; - case SUSTAIN: - return slot->patch->EG ? 0 : slot->patch->RR; - case RELEASE: - if (slot->sus_flag) { - return 5; - } else if (slot->patch->EG) { - return slot->patch->RR; - } else { - return 7; - } - case DAMP: - return DAMPER_RATE; - default: - return 0; - } -} - -enum SLOT_UPDATE_FLAG { - UPDATE_WS = 1, - UPDATE_TLL = 2, - UPDATE_RKS = 4, - UPDATE_EG = 8, - UPDATE_ALL = 255, -}; - -static INLINE void request_update(OPLL_SLOT *slot, int flag) { slot->update_requests |= flag; } - -static void commit_slot_update(OPLL_SLOT *slot) { - -#if OPLL_DEBUG - if (slot->last_eg_state != slot->eg_state) { - _debug_print_slot_info(slot); - slot->last_eg_state = slot->eg_state; - } -#endif - - if (slot->update_requests & UPDATE_WS) { - slot->wave_table = wave_table_map[slot->patch->WS]; - } - - if (slot->update_requests & UPDATE_TLL) { - if ((slot->type & 1) == 0) { - slot->tll = tll_table[slot->blk_fnum >> 5][slot->patch->TL][slot->patch->KL]; - } else { - slot->tll = tll_table[slot->blk_fnum >> 5][slot->volume][slot->patch->KL]; - } - } - - if (slot->update_requests & UPDATE_RKS) { - slot->rks = rks_table[slot->blk_fnum >> 8][slot->patch->KR]; - } - - if (slot->update_requests & (UPDATE_RKS | UPDATE_EG)) { - int p_rate = get_parameter_rate(slot); - - if (p_rate == 0) { - slot->eg_shift = 0; - slot->eg_rate_h = 0; - slot->eg_rate_l = 0; - return; - } - - slot->eg_rate_h = min(15, p_rate + (slot->rks >> 2)); - slot->eg_rate_l = slot->rks & 3; - if (slot->eg_state == ATTACK) { - slot->eg_shift = (0 < slot->eg_rate_h && slot->eg_rate_h < 12) ? (13 - slot->eg_rate_h) : 0; - } else { - slot->eg_shift = (slot->eg_rate_h < 13) ? (13 - slot->eg_rate_h) : 0; - } - } - - slot->update_requests = 0; -} - -static void reset_slot(OPLL_SLOT *slot, int number) { - slot->number = number; - slot->type = number % 2; - slot->pg_keep = 0; - slot->wave_table = wave_table_map[0]; - slot->pg_phase = 0; - slot->output[0] = 0; - slot->output[1] = 0; - slot->eg_state = RELEASE; - slot->eg_shift = 0; - slot->rks = 0; - slot->tll = 0; - slot->key_flag = 0; - slot->sus_flag = 0; - slot->blk_fnum = 0; - slot->blk = 0; - slot->fnum = 0; - slot->volume = 0; - slot->pg_out = 0; - slot->eg_out = EG_MUTE; - slot->patch = &null_patch; -} - -static INLINE void slotOn(OPLL *opll, int i) { - OPLL_SLOT *slot = &opll->slot[i]; - slot->key_flag = 1; - slot->eg_state = DAMP; - request_update(slot, UPDATE_EG); -} - -static INLINE void slotOff(OPLL *opll, int i) { - OPLL_SLOT *slot = &opll->slot[i]; - slot->key_flag = 0; - if (slot->type & 1) { - slot->eg_state = RELEASE; - request_update(slot, UPDATE_EG); - } -} - -static INLINE void update_key_status(OPLL *opll) { - const uint8_t r14 = opll->reg[0x0e]; - const uint8_t rhythm_mode = BIT(r14, 5); - uint32_t new_slot_key_status = 0; - uint32_t updated_status; - int ch; - - for (ch = 0; ch < 9; ch++) - if (opll->reg[0x20 + ch] & 0x10) - new_slot_key_status |= 3 << (ch * 2); - - if (rhythm_mode) { - if (r14 & 0x10) - new_slot_key_status |= 3 << SLOT_BD1; - - if (r14 & 0x01) - new_slot_key_status |= 1 << SLOT_HH; - - if (r14 & 0x08) - new_slot_key_status |= 1 << SLOT_SD; - - if (r14 & 0x04) - new_slot_key_status |= 1 << SLOT_TOM; - - if (r14 & 0x02) - new_slot_key_status |= 1 << SLOT_CYM; - } - - updated_status = opll->slot_key_status ^ new_slot_key_status; - - if (updated_status) { - int i; - for (i = 0; i < 18; i++) - if (BIT(updated_status, i)) { - if (BIT(new_slot_key_status, i)) { - slotOn(opll, i); - } else { - slotOff(opll, i); - } - } - } - - opll->slot_key_status = new_slot_key_status; -} - -static INLINE void set_patch(OPLL *opll, int32_t ch, int32_t num) { - opll->patch_number[ch] = num; - MOD(opll, ch)->patch = &opll->patch[num * 2 + 0]; - CAR(opll, ch)->patch = &opll->patch[num * 2 + 1]; - request_update(MOD(opll, ch), UPDATE_ALL); - request_update(CAR(opll, ch), UPDATE_ALL); -} - -static INLINE void set_sus_flag(OPLL *opll, int ch, int flag) { - CAR(opll, ch)->sus_flag = flag; - request_update(CAR(opll, ch), UPDATE_EG); - if (MOD(opll, ch)->type & 1) { - MOD(opll, ch)->sus_flag = flag; - request_update(MOD(opll, ch), UPDATE_EG); - } -} - -/* set volume ( volume : 6bit, register value << 2 ) */ -static INLINE void set_volume(OPLL *opll, int ch, int volume) { - CAR(opll, ch)->volume = volume; - request_update(CAR(opll, ch), UPDATE_TLL); -} - -static INLINE void set_slot_volume(OPLL_SLOT *slot, int volume) { - slot->volume = volume; - request_update(slot, UPDATE_TLL); -} - -/* set f-Nnmber ( fnum : 9bit ) */ -static INLINE void set_fnumber(OPLL *opll, int ch, int fnum) { - OPLL_SLOT *car = CAR(opll, ch); - OPLL_SLOT *mod = MOD(opll, ch); - car->fnum = fnum; - car->blk_fnum = (car->blk_fnum & 0xe00) | (fnum & 0x1ff); - mod->fnum = fnum; - mod->blk_fnum = (mod->blk_fnum & 0xe00) | (fnum & 0x1ff); - request_update(car, UPDATE_EG | UPDATE_RKS | UPDATE_TLL); - request_update(mod, UPDATE_EG | UPDATE_RKS | UPDATE_TLL); -} - -/* set block data (blk : 3bit ) */ -static INLINE void set_block(OPLL *opll, int ch, int blk) { - OPLL_SLOT *car = CAR(opll, ch); - OPLL_SLOT *mod = MOD(opll, ch); - car->blk = blk; - car->blk_fnum = ((blk & 7) << 9) | (car->blk_fnum & 0x1ff); - mod->blk = blk; - mod->blk_fnum = ((blk & 7) << 9) | (mod->blk_fnum & 0x1ff); - request_update(car, UPDATE_EG | UPDATE_RKS | UPDATE_TLL); - request_update(mod, UPDATE_EG | UPDATE_RKS | UPDATE_TLL); -} - -static INLINE void update_rhythm_mode(OPLL *opll) { - const uint8_t new_rhythm_mode = (opll->reg[0x0e] >> 5) & 1; - - if (opll->rhythm_mode != new_rhythm_mode) { - - if (new_rhythm_mode) { - opll->slot[SLOT_HH].type = 3; - opll->slot[SLOT_HH].pg_keep = 1; - opll->slot[SLOT_SD].type = 3; - opll->slot[SLOT_TOM].type = 3; - opll->slot[SLOT_CYM].type = 3; - opll->slot[SLOT_CYM].pg_keep = 1; - set_patch(opll, 6, 16); - set_patch(opll, 7, 17); - set_patch(opll, 8, 18); - set_slot_volume(&opll->slot[SLOT_HH], ((opll->reg[0x37] >> 4) & 15) << 2); - set_slot_volume(&opll->slot[SLOT_TOM], ((opll->reg[0x38] >> 4) & 15) << 2); - } else { - opll->slot[SLOT_HH].type = 0; - opll->slot[SLOT_HH].pg_keep = 0; - opll->slot[SLOT_SD].type = 1; - opll->slot[SLOT_TOM].type = 0; - opll->slot[SLOT_CYM].type = 1; - opll->slot[SLOT_CYM].pg_keep = 0; - set_patch(opll, 6, opll->reg[0x36] >> 4); - set_patch(opll, 7, opll->reg[0x37] >> 4); - set_patch(opll, 8, opll->reg[0x38] >> 4); - } - } - - opll->rhythm_mode = new_rhythm_mode; -} - -static void update_ampm(OPLL *opll) { - if (opll->test_flag & 2) { - opll->pm_phase = 0; - opll->am_phase = 0; - } else { - opll->pm_phase += (opll->test_flag & 8) ? 1024 : 1; - opll->am_phase += (opll->test_flag & 8) ? 64 : 1; - } - opll->lfo_am = am_table[(opll->am_phase >> 6) % sizeof(am_table)]; -} - -static void update_noise(OPLL *opll, int cycle) { - int i; - for (i = 0; i < cycle; i++) { - if (opll->noise & 1) { - opll->noise ^= 0x800200; - } - opll->noise >>= 1; - } -} - -static void update_short_noise(OPLL *opll) { - const uint32_t pg_hh = opll->slot[SLOT_HH].pg_out; - const uint32_t pg_cym = opll->slot[SLOT_CYM].pg_out; - - const uint8_t h_bit2 = BIT(pg_hh, PG_BITS - 8); - const uint8_t h_bit7 = BIT(pg_hh, PG_BITS - 3); - const uint8_t h_bit3 = BIT(pg_hh, PG_BITS - 7); - - const uint8_t c_bit3 = BIT(pg_cym, PG_BITS - 7); - const uint8_t c_bit5 = BIT(pg_cym, PG_BITS - 5); - - opll->short_noise = (h_bit2 ^ h_bit7) | (h_bit3 ^ c_bit5) | (c_bit3 ^ c_bit5); -} - -static INLINE void calc_phase(OPLL_SLOT *slot, int32_t pm_phase, uint8_t reset) { - const int8_t pm = slot->patch->PM ? pm_table[(slot->fnum >> 6) & 7][(pm_phase >> 10) & 7] : 0; - if (reset) { - slot->pg_phase = 0; - } - slot->pg_phase += (((slot->fnum & 0x1ff) * 2 + pm) * ml_table[slot->patch->ML]) << slot->blk >> 2; - slot->pg_phase &= (DP_WIDTH - 1); - slot->pg_out = slot->pg_phase >> DP_BASE_BITS; -} - -static INLINE uint8_t lookup_attack_step(OPLL_SLOT *slot, uint32_t counter) { - int index; - - switch (slot->eg_rate_h) { - case 12: - index = (counter & 0xc) >> 1; - return 4 - eg_step_tables[slot->eg_rate_l][index]; - case 13: - index = (counter & 0xc) >> 1; - return 3 - eg_step_tables[slot->eg_rate_l][index]; - case 14: - index = (counter & 0xc) >> 1; - return 2 - eg_step_tables[slot->eg_rate_l][index]; - case 0: - case 15: - return 0; - default: - index = counter >> slot->eg_shift; - return eg_step_tables[slot->eg_rate_l][index & 7] ? 4 : 0; - } -} - -static INLINE uint8_t lookup_decay_step(OPLL_SLOT *slot, uint32_t counter) { - int index; - - switch (slot->eg_rate_h) { - case 0: - return 0; - case 13: - index = ((counter & 0xc) >> 1) | (counter & 1); - return eg_step_tables[slot->eg_rate_l][index]; - case 14: - index = ((counter & 0xc) >> 1); - return eg_step_tables[slot->eg_rate_l][index] + 1; - case 15: - return 2; - default: - index = counter >> slot->eg_shift; - return eg_step_tables[slot->eg_rate_l][index & 7]; - } -} - -static INLINE void start_envelope(OPLL_SLOT *slot) { - if (min(15, slot->patch->AR + (slot->rks >> 2)) == 15) { - slot->eg_state = DECAY; - slot->eg_out = 0; - } else { - slot->eg_state = ATTACK; - slot->eg_out = EG_MUTE; - } - request_update(slot, UPDATE_EG); -} - -static INLINE void calc_envelope(OPLL_SLOT *slot, OPLL_SLOT *buddy, uint16_t eg_counter, uint8_t test) { - - uint32_t mask = (1 << slot->eg_shift) - 1; - uint8_t s; - - if (slot->eg_state == ATTACK) { - if (0 < slot->eg_out && 0 < slot->eg_rate_h && (eg_counter & mask & ~3) == 0) { - s = lookup_attack_step(slot, eg_counter); - if (0 < s) { - slot->eg_out = max(0, ((int)slot->eg_out - (slot->eg_out >> s) - 1)); - } - } - } else { - if (slot->eg_rate_h > 0 && (eg_counter & mask) == 0) { - slot->eg_out = min(EG_MUTE, slot->eg_out + lookup_decay_step(slot, eg_counter)); - } - } - - switch (slot->eg_state) { - case DAMP: - if (slot->eg_out >= EG_MUTE) { - start_envelope(slot); - if (slot->type & 1) { - if (!slot->pg_keep) { - slot->pg_phase = 0; - } - if (buddy && !buddy->pg_keep) { - buddy->pg_phase = 0; - } - } - } - break; - - case ATTACK: - if (slot->eg_out == 0) { - slot->eg_state = DECAY; - request_update(slot, UPDATE_EG); - } - break; - - case DECAY: - if ((slot->eg_out >> 3) == slot->patch->SL) { - slot->eg_state = SUSTAIN; - request_update(slot, UPDATE_EG); - } - break; - - case SUSTAIN: - case RELEASE: - default: - break; - } - - if (test) { - slot->eg_out = 0; - } -} - -static void update_slots(OPLL *opll) { - int i; - opll->eg_counter++; - - for (i = 0; i < 18; i++) { - OPLL_SLOT *slot = &opll->slot[i]; - OPLL_SLOT *buddy = NULL; - if (slot->type == 0) { - buddy = &opll->slot[i + 1]; - } - if (slot->type == 1) { - buddy = &opll->slot[i - 1]; - } - if (slot->update_requests) { - commit_slot_update(slot); - } - calc_envelope(slot, buddy, opll->eg_counter, opll->test_flag & 1); - calc_phase(slot, opll->pm_phase, opll->test_flag & 4); - } -} - -/* output: -4095...4095 */ -static INLINE int16_t lookup_exp_table(uint16_t i) { - /* from andete's expressoin */ - int16_t t = (exp_table[(i & 0xff) ^ 0xff] + 1024); - int16_t res = t >> ((i & 0x7f00) >> 8); - return ((i & 0x8000) ? ~res : res) << 1; -} - -static INLINE int16_t to_linear(uint16_t h, OPLL_SLOT *slot, int16_t am) { - uint16_t att; - if (slot->eg_out >= EG_MAX) - return 0; - - att = min(EG_MAX, (slot->eg_out + slot->tll + am)) << 4; - return lookup_exp_table(h + att); -} - -static INLINE int16_t calc_slot_car(OPLL *opll, int ch, int16_t fm) { - OPLL_SLOT *slot = CAR(opll, ch); - - uint8_t am = slot->patch->AM ? opll->lfo_am : 0; - - slot->output[1] = slot->output[0]; - slot->output[0] = to_linear(slot->wave_table[(slot->pg_out + 2 * (fm >> 1)) & (PG_WIDTH - 1)], slot, am); - - return slot->output[0]; -} - -static INLINE int16_t calc_slot_mod(OPLL *opll, int ch) { - OPLL_SLOT *slot = MOD(opll, ch); - - int16_t fm = slot->patch->FB > 0 ? (slot->output[1] + slot->output[0]) >> (9 - slot->patch->FB) : 0; - uint8_t am = slot->patch->AM ? opll->lfo_am : 0; - - slot->output[1] = slot->output[0]; - slot->output[0] = to_linear(slot->wave_table[(slot->pg_out + fm) & (PG_WIDTH - 1)], slot, am); - - return slot->output[0]; -} - -static INLINE int16_t calc_slot_tom(OPLL *opll) { - OPLL_SLOT *slot = MOD(opll, 8); - - return to_linear(slot->wave_table[slot->pg_out], slot, 0); -} - -/* Specify phase offset directly based on 10-bit (1024-length) sine table */ -#define _PD(phase) ((PG_BITS < 10) ? (phase >> (10 - PG_BITS)) : (phase << (PG_BITS - 10))) - -static INLINE int16_t calc_slot_snare(OPLL *opll) { - OPLL_SLOT *slot = CAR(opll, 7); - - uint32_t phase; - - if (BIT(slot->pg_out, PG_BITS - 2)) - phase = (opll->noise & 1) ? _PD(0x300) : _PD(0x200); - else - phase = (opll->noise & 1) ? _PD(0x0) : _PD(0x100); - - return to_linear(slot->wave_table[phase], slot, 0); -} - -static INLINE int16_t calc_slot_cym(OPLL *opll) { - OPLL_SLOT *slot = CAR(opll, 8); - - uint32_t phase = opll->short_noise ? _PD(0x300) : _PD(0x100); - - return to_linear(slot->wave_table[phase], slot, 0); -} - -static INLINE int16_t calc_slot_hat(OPLL *opll) { - OPLL_SLOT *slot = MOD(opll, 7); - - uint32_t phase; - - if (opll->short_noise) - phase = (opll->noise & 1) ? _PD(0x2d0) : _PD(0x234); - else - phase = (opll->noise & 1) ? _PD(0x34) : _PD(0xd0); - - return to_linear(slot->wave_table[phase], slot, 0); -} - -#define _MO(x) (-(x) >> 1) -#define _RO(x) (x) - -static void update_output(OPLL *opll) { - int16_t *out; - int i; - - update_ampm(opll); - update_short_noise(opll); - update_slots(opll); - - out = opll->ch_out; - - /* CH1-6 */ - for (i = 0; i < 6; i++) { - if (!(opll->mask & OPLL_MASK_CH(i))) { - out[i] = _MO(calc_slot_car(opll, i, calc_slot_mod(opll, i))); - } - } - - /* CH7 */ - if (!opll->rhythm_mode) { - if (!(opll->mask & OPLL_MASK_CH(6))) { - out[6] = _MO(calc_slot_car(opll, 6, calc_slot_mod(opll, 6))); - } - } else { - if (!(opll->mask & OPLL_MASK_BD)) { - out[9] = _RO(calc_slot_car(opll, 6, calc_slot_mod(opll, 6))); - } - } - update_noise(opll, 14); - - /* CH8 */ - if (!opll->rhythm_mode) { - if (!(opll->mask & OPLL_MASK_CH(7))) { - out[7] = _MO(calc_slot_car(opll, 7, calc_slot_mod(opll, 7))); - } - } else { - if (!(opll->mask & OPLL_MASK_HH)) { - out[10] = _RO(calc_slot_hat(opll)); - } - if (!(opll->mask & OPLL_MASK_SD)) { - out[11] = _RO(calc_slot_snare(opll)); - } - } - update_noise(opll, 2); - - /* CH9 */ - if (!opll->rhythm_mode) { - if (!(opll->mask & OPLL_MASK_CH(8))) { - out[8] = _MO(calc_slot_car(opll, 8, calc_slot_mod(opll, 8))); - } - } else { - if (!(opll->mask & OPLL_MASK_TOM)) { - out[12] = _RO(calc_slot_tom(opll)); - } - if (!(opll->mask & OPLL_MASK_CYM)) { - out[13] = _RO(calc_slot_cym(opll)); - } - } - update_noise(opll, 2); -} - -INLINE static void mix_output(OPLL *opll) { - int16_t out = 0; - int i; - for (i = 0; i < 14; i++) { - out += opll->ch_out[i]; - } - if (opll->conv) { - OPLL_RateConv_putData(opll->conv, 0, out); - } else { - opll->mix_out[0] = out; - } -} - -INLINE static void mix_output_stereo(OPLL *opll) { - int16_t *out = opll->mix_out; - int i; - out[0] = out[1] = 0; - for (i = 0; i < 14; i++) { - if (opll->pan[i] & 2) - out[0] += (int16_t)(opll->ch_out[i] * opll->pan_fine[i][0]); - if (opll->pan[i] & 1) - out[1] += (int16_t)(opll->ch_out[i] * opll->pan_fine[i][1]); - } - if (opll->conv) { - OPLL_RateConv_putData(opll->conv, 0, out[0]); - OPLL_RateConv_putData(opll->conv, 1, out[1]); - } -} - -/*********************************************************** - - External Interfaces - -***********************************************************/ - -OPLL *OPLL_new(uint32_t clk, uint32_t rate) { - OPLL *opll; - int i; - - if (!table_initialized) { - initializeTables(); - } - - opll = (OPLL *)calloc(sizeof(OPLL), 1); - if (opll == NULL) - return NULL; - - for (i = 0; i < 19 * 2; i++) - memcpy(&opll->patch[i], &null_patch, sizeof(OPLL_PATCH)); - - opll->clk = clk; - opll->rate = rate; - opll->mask = 0; - opll->conv = NULL; - opll->mix_out[0] = 0; - opll->mix_out[1] = 0; - - OPLL_reset(opll); - OPLL_setChipType(opll, 0); - OPLL_resetPatch(opll, 0); - return opll; -} - -void OPLL_delete(OPLL *opll) { - if (opll->conv) { - OPLL_RateConv_delete(opll->conv); - opll->conv = NULL; - } - free(opll); -} - -static void reset_rate_conversion_params(OPLL *opll) { - const double f_out = opll->rate; - const double f_inp = opll->clk / 72; - - opll->out_time = 0; - opll->out_step = ((uint32_t)f_inp) << 8; - opll->inp_step = ((uint32_t)f_out) << 8; - - if (opll->conv) { - OPLL_RateConv_delete(opll->conv); - opll->conv = NULL; - } - - if (floor(f_inp) != f_out && floor(f_inp + 0.5) != f_out) { - opll->conv = OPLL_RateConv_new(f_inp, f_out, 2); - } - - if (opll->conv) { - OPLL_RateConv_reset(opll->conv); - } -} - -void OPLL_reset(OPLL *opll) { - int i; - - if (!opll) - return; - - opll->adr = 0; - - opll->pm_phase = 0; - opll->am_phase = 0; - - opll->noise = 0x1; - opll->mask = 0; - - opll->rhythm_mode = 0; - opll->slot_key_status = 0; - opll->eg_counter = 0; - - reset_rate_conversion_params(opll); - - for (i = 0; i < 18; i++) - reset_slot(&opll->slot[i], i); - - for (i = 0; i < 9; i++) { - set_patch(opll, i, 0); - } - - for (i = 0; i < 0x40; i++) - OPLL_writeReg(opll, i, 0); - - for (i = 0; i < 15; i++) { - opll->pan[i] = 3; - opll->pan_fine[i][1] = opll->pan_fine[i][0] = 1.0f; - } - - for (i = 0; i < 14; i++) { - opll->ch_out[i] = 0; - } -} - -void OPLL_forceRefresh(OPLL *opll) { - int i; - - if (opll == NULL) - return; - - for (i = 0; i < 9; i++) { - set_patch(opll, i, opll->patch_number[i]); - } - - for (i = 0; i < 18; i++) { - request_update(&opll->slot[i], UPDATE_ALL); - } -} - -void OPLL_setRate(OPLL *opll, uint32_t rate) { - opll->rate = rate; - reset_rate_conversion_params(opll); -} - -void OPLL_setQuality(OPLL *opll, uint8_t q) {} - -void OPLL_setChipType(OPLL *opll, uint8_t type) { opll->chip_type = type; } - -void OPLL_writeReg(OPLL *opll, uint32_t reg, uint8_t data) { - int ch, i; - - if (reg >= 0x40) - return; - - /* mirror registers */ - if ((0x19 <= reg && reg <= 0x1f) || (0x29 <= reg && reg <= 0x2f) || (0x39 <= reg && reg <= 0x3f)) { - reg -= 9; - } - - opll->reg[reg] = (uint8_t)data; - - switch (reg) { - case 0x00: - opll->patch[0].AM = (data >> 7) & 1; - opll->patch[0].PM = (data >> 6) & 1; - opll->patch[0].EG = (data >> 5) & 1; - opll->patch[0].KR = (data >> 4) & 1; - opll->patch[0].ML = (data)&15; - for (i = 0; i < 9; i++) { - if (opll->patch_number[i] == 0) { - request_update(MOD(opll, i), UPDATE_RKS | UPDATE_EG); - } - } - break; - - case 0x01: - opll->patch[1].AM = (data >> 7) & 1; - opll->patch[1].PM = (data >> 6) & 1; - opll->patch[1].EG = (data >> 5) & 1; - opll->patch[1].KR = (data >> 4) & 1; - opll->patch[1].ML = (data)&15; - for (i = 0; i < 9; i++) { - if (opll->patch_number[i] == 0) { - request_update(CAR(opll, i), UPDATE_RKS | UPDATE_EG); - } - } - break; - - case 0x02: - opll->patch[0].KL = (data >> 6) & 3; - opll->patch[0].TL = (data)&63; - for (i = 0; i < 9; i++) { - if (opll->patch_number[i] == 0) { - request_update(MOD(opll, i), UPDATE_TLL); - } - } - break; - - case 0x03: - opll->patch[1].KL = (data >> 6) & 3; - opll->patch[1].WS = (data >> 4) & 1; - opll->patch[0].WS = (data >> 3) & 1; - opll->patch[0].FB = (data)&7; - for (i = 0; i < 9; i++) { - if (opll->patch_number[i] == 0) { - request_update(MOD(opll, i), UPDATE_WS); - request_update(CAR(opll, i), UPDATE_WS | UPDATE_TLL); - } - } - break; - - case 0x04: - opll->patch[0].AR = (data >> 4) & 15; - opll->patch[0].DR = (data)&15; - for (i = 0; i < 9; i++) { - if (opll->patch_number[i] == 0) { - request_update(MOD(opll, i), UPDATE_EG); - } - } - break; - - case 0x05: - opll->patch[1].AR = (data >> 4) & 15; - opll->patch[1].DR = (data)&15; - for (i = 0; i < 9; i++) { - if (opll->patch_number[i] == 0) { - request_update(CAR(opll, i), UPDATE_EG); - } - } - break; - - case 0x06: - opll->patch[0].SL = (data >> 4) & 15; - opll->patch[0].RR = (data)&15; - for (i = 0; i < 9; i++) { - if (opll->patch_number[i] == 0) { - request_update(MOD(opll, i), UPDATE_EG); - } - } - break; - - case 0x07: - opll->patch[1].SL = (data >> 4) & 15; - opll->patch[1].RR = (data)&15; - for (i = 0; i < 9; i++) { - if (opll->patch_number[i] == 0) { - request_update(CAR(opll, i), UPDATE_EG); - } - } - break; - - case 0x0e: - if (opll->chip_type == 1) - break; - update_rhythm_mode(opll); - update_key_status(opll); - break; - - case 0x0f: - opll->test_flag = data; - break; - - case 0x10: - case 0x11: - case 0x12: - case 0x13: - case 0x14: - case 0x15: - case 0x16: - case 0x17: - case 0x18: - ch = reg - 0x10; - set_fnumber(opll, ch, data + ((opll->reg[0x20 + ch] & 1) << 8)); - break; - - case 0x20: - case 0x21: - case 0x22: - case 0x23: - case 0x24: - case 0x25: - case 0x26: - case 0x27: - case 0x28: - ch = reg - 0x20; - set_fnumber(opll, ch, ((data & 1) << 8) + opll->reg[0x10 + ch]); - set_block(opll, ch, (data >> 1) & 7); - set_sus_flag(opll, ch, (data >> 5) & 1); - update_key_status(opll); - break; - - case 0x30: - case 0x31: - case 0x32: - case 0x33: - case 0x34: - case 0x35: - case 0x36: - case 0x37: - case 0x38: - if ((opll->reg[0x0e] & 32) && (reg >= 0x36)) { - switch (reg) { - case 0x37: - set_slot_volume(MOD(opll, 7), ((data >> 4) & 15) << 2); - break; - case 0x38: - set_slot_volume(MOD(opll, 8), ((data >> 4) & 15) << 2); - break; - default: - break; - } - } else { - set_patch(opll, reg - 0x30, (data >> 4) & 15); - } - set_volume(opll, reg - 0x30, (data & 15) << 2); - break; - - default: - break; - } -} - -void OPLL_writeIO(OPLL *opll, uint32_t adr, uint8_t val) { - if (adr & 1) - OPLL_writeReg(opll, opll->adr, val); - else - opll->adr = val; -} - -void OPLL_setPan(OPLL *opll, uint32_t ch, uint8_t pan) { opll->pan[ch & 15] = pan; } - -void OPLL_setPanFine(OPLL *opll, uint32_t ch, float pan[2]) { - opll->pan_fine[ch & 15][0] = pan[0]; - opll->pan_fine[ch & 15][1] = pan[1]; -} - -void OPLL_dumpToPatch(const uint8_t *dump, OPLL_PATCH *patch) { - patch[0].AM = (dump[0] >> 7) & 1; - patch[1].AM = (dump[1] >> 7) & 1; - patch[0].PM = (dump[0] >> 6) & 1; - patch[1].PM = (dump[1] >> 6) & 1; - patch[0].EG = (dump[0] >> 5) & 1; - patch[1].EG = (dump[1] >> 5) & 1; - patch[0].KR = (dump[0] >> 4) & 1; - patch[1].KR = (dump[1] >> 4) & 1; - patch[0].ML = (dump[0]) & 15; - patch[1].ML = (dump[1]) & 15; - patch[0].KL = (dump[2] >> 6) & 3; - patch[1].KL = (dump[3] >> 6) & 3; - patch[0].TL = (dump[2]) & 63; - patch[1].TL = 0; - patch[0].FB = (dump[3]) & 7; - patch[1].FB = 0; - patch[0].WS = (dump[3] >> 3) & 1; - patch[1].WS = (dump[3] >> 4) & 1; - patch[0].AR = (dump[4] >> 4) & 15; - patch[1].AR = (dump[5] >> 4) & 15; - patch[0].DR = (dump[4]) & 15; - patch[1].DR = (dump[5]) & 15; - patch[0].SL = (dump[6] >> 4) & 15; - patch[1].SL = (dump[7] >> 4) & 15; - patch[0].RR = (dump[6]) & 15; - patch[1].RR = (dump[7]) & 15; -} - -void OPLL_getDefaultPatch(int32_t type, int32_t num, OPLL_PATCH *patch) { - OPLL_dump2patch(default_inst[type] + num * 8, patch); -} - -void OPLL_setPatch(OPLL *opll, const uint8_t *dump) { - OPLL_PATCH patch[2]; - int i; - for (i = 0; i < 19; i++) { - OPLL_dump2patch(dump + i * 8, patch); - memcpy(&opll->patch[i * 2 + 0], &patch[0], sizeof(OPLL_PATCH)); - memcpy(&opll->patch[i * 2 + 1], &patch[1], sizeof(OPLL_PATCH)); - } -} - -void OPLL_patchToDump(const OPLL_PATCH *patch, uint8_t *dump) { - dump[0] = (uint8_t)((patch[0].AM << 7) + (patch[0].PM << 6) + (patch[0].EG << 5) + (patch[0].KR << 4) + patch[0].ML); - dump[1] = (uint8_t)((patch[1].AM << 7) + (patch[1].PM << 6) + (patch[1].EG << 5) + (patch[1].KR << 4) + patch[1].ML); - dump[2] = (uint8_t)((patch[0].KL << 6) + patch[0].TL); - dump[3] = (uint8_t)((patch[1].KL << 6) + (patch[1].WS << 4) + (patch[0].WS << 3) + patch[0].FB); - dump[4] = (uint8_t)((patch[0].AR << 4) + patch[0].DR); - dump[5] = (uint8_t)((patch[1].AR << 4) + patch[1].DR); - dump[6] = (uint8_t)((patch[0].SL << 4) + patch[0].RR); - dump[7] = (uint8_t)((patch[1].SL << 4) + patch[1].RR); -} - -void OPLL_copyPatch(OPLL *opll, int32_t num, OPLL_PATCH *patch) { - memcpy(&opll->patch[num], patch, sizeof(OPLL_PATCH)); -} - -void OPLL_resetPatch(OPLL *opll, uint8_t type) { - int i; - for (i = 0; i < 19 * 2; i++) - OPLL_copyPatch(opll, i, &default_patch[type % OPLL_TONE_NUM][i]); -} - -int16_t OPLL_calc(OPLL *opll) { - while (opll->out_step > opll->out_time) { - opll->out_time += opll->inp_step; - update_output(opll); - mix_output(opll); - } - opll->out_time -= opll->out_step; - if (opll->conv) { - opll->mix_out[0] = OPLL_RateConv_getData(opll->conv, 0); - } - return opll->mix_out[0]; -} - -void OPLL_calcStereo(OPLL *opll, int32_t out[2]) { - while (opll->out_step > opll->out_time) { - opll->out_time += opll->inp_step; - update_output(opll); - mix_output_stereo(opll); - } - opll->out_time -= opll->out_step; - if (opll->conv) { - out[0] = OPLL_RateConv_getData(opll->conv, 0); - out[1] = OPLL_RateConv_getData(opll->conv, 1); - } else { - out[0] = opll->mix_out[0]; - out[1] = opll->mix_out[1]; - } -} - -uint32_t OPLL_setMask(OPLL *opll, uint32_t mask) { - uint32_t ret; - - if (opll) { - ret = opll->mask; - opll->mask = mask; - return ret; - } else - return 0; -} - -uint32_t OPLL_toggleMask(OPLL *opll, uint32_t mask) { - uint32_t ret; - - if (opll) { - ret = opll->mask; - opll->mask ^= mask; - return ret; - } else - return 0; -} diff --git a/extern/NSFplay/legacy/emu2413.h b/extern/NSFplay/legacy/emu2413.h deleted file mode 100644 index 2804f3fac..000000000 --- a/extern/NSFplay/legacy/emu2413.h +++ /dev/null @@ -1,247 +0,0 @@ -#ifndef _EMU2413_H_ -#define _EMU2413_H_ - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -#define OPLL_DEBUG 0 - -enum OPLL_TONE_ENUM { - OPLL_VRC7_TONE = 0, - OPLL_VRC7_RW_TONE, - OPLL_VRC7_FT36_TONE, - OPLL_VRC7_FT35_TONE, - OPLL_VRC7_MO_TONE, - OPLL_VRC7_KT2_TONE, - OPLL_VRC7_KT1_TONE, - OPLL_2413_TONE, - OPLL_281B_TONE -}; - -/* voice data */ -typedef struct __OPLL_PATCH { - uint32_t TL, FB, EG, ML, AR, DR, SL, RR, KR, KL, AM, PM, WS; -} OPLL_PATCH; - -/* slot */ -typedef struct __OPLL_SLOT { - uint8_t number; - - /* type flags: - * 000000SM - * |+-- M: 0:modulator 1:carrier - * +--- S: 0:normal 1:single slot mode (sd, tom, hh or cym) - */ - uint8_t type; - - OPLL_PATCH *patch; /* voice parameter */ - - /* slot output */ - int32_t output[2]; /* output value, latest and previous. */ - - /* phase generator (pg) */ - uint16_t *wave_table; /* wave table */ - uint32_t pg_phase; /* pg phase */ - uint32_t pg_out; /* pg output, as index of wave table */ - uint8_t pg_keep; /* if 1, pg_phase is preserved when key-on */ - uint16_t blk_fnum; /* (block << 9) | f-number */ - uint16_t fnum; /* f-number (9 bits) */ - uint8_t blk; /* block (3 bits) */ - - /* envelope generator (eg) */ - uint8_t eg_state; /* current state */ - int32_t volume; /* current volume */ - uint8_t key_flag; /* key-on flag 1:on 0:off */ - uint8_t sus_flag; /* key-sus option 1:on 0:off */ - uint16_t tll; /* total level + key scale level*/ - uint8_t rks; /* key scale offset (rks) for eg speed */ - uint8_t eg_rate_h; /* eg speed rate high 4bits */ - uint8_t eg_rate_l; /* eg speed rate low 2bits */ - uint32_t eg_shift; /* shift for eg global counter, controls envelope speed */ - uint32_t eg_out; /* eg output */ - - uint32_t update_requests; /* flags to debounce update */ - -#if OPLL_DEBUG - uint8_t last_eg_state; -#endif -} OPLL_SLOT; - -/* mask */ -#define OPLL_MASK_CH(x) (1 << (x)) -#define OPLL_MASK_HH (1 << (9)) -#define OPLL_MASK_CYM (1 << (10)) -#define OPLL_MASK_TOM (1 << (11)) -#define OPLL_MASK_SD (1 << (12)) -#define OPLL_MASK_BD (1 << (13)) -#define OPLL_MASK_RHYTHM (OPLL_MASK_HH | OPLL_MASK_CYM | OPLL_MASK_TOM | OPLL_MASK_SD | OPLL_MASK_BD) - -/* rate conveter */ -typedef struct __OPLL_RateConv { - int ch; - double timer; - double f_ratio; - int16_t *sinc_table; - int16_t **buf; -} OPLL_RateConv; - -OPLL_RateConv *OPLL_RateConv_new(double f_inp, double f_out, int ch); -void OPLL_RateConv_reset(OPLL_RateConv *conv); -void OPLL_RateConv_putData(OPLL_RateConv *conv, int ch, int16_t data); -int16_t OPLL_RateConv_getData(OPLL_RateConv *conv, int ch); -void OPLL_RateConv_delete(OPLL_RateConv *conv); - -typedef struct __OPLL { - uint32_t clk; - uint32_t rate; - - uint8_t chip_type; - - uint32_t adr; - - uint32_t inp_step; - uint32_t out_step; - uint32_t out_time; - - uint8_t reg[0x40]; - uint8_t test_flag; - uint32_t slot_key_status; - uint8_t rhythm_mode; - - uint32_t eg_counter; - - uint32_t pm_phase; - int32_t am_phase; - - uint8_t lfo_am; - - uint32_t noise; - uint8_t short_noise; - - int32_t patch_number[9]; - OPLL_SLOT slot[18]; - OPLL_PATCH patch[19 * 2]; - - uint8_t pan[16]; - float pan_fine[16][2]; - - uint32_t mask; - - /* channel output */ - /* 0..8:tone 9:bd 10:hh 11:sd 12:tom 13:cym */ - int16_t ch_out[14]; - - int16_t mix_out[2]; - - OPLL_RateConv *conv; -} OPLL; - -OPLL *OPLL_new(uint32_t clk, uint32_t rate); -void OPLL_delete(OPLL *); - -void OPLL_reset(OPLL *); -void OPLL_resetPatch(OPLL *, uint8_t); - -/** - * Set output wave sampling rate. - * @param rate sampling rate. If clock / 72 (typically 49716 or 49715 at 3.58MHz) is set, the internal rate converter is - * disabled. - */ -void OPLL_setRate(OPLL *opll, uint32_t rate); - -/** - * Set internal calcuration quality. Currently no effects, just for compatibility. - * >= v1.0.0 always synthesizes internal output at clock/72 Hz. - */ -void OPLL_setQuality(OPLL *opll, uint8_t q); - -/** - * Set pan pot (extra function - not YM2413 chip feature) - * @param ch 0..8:tone 9:bd 10:hh 11:sd 12:tom 13:cym 14,15:reserved - * @param pan 0:mute 1:right 2:left 3:center - * ``` - * pan: 76543210 - * |+- bit 1: enable Left output - * +-- bit 0: enable Right output - * ``` - */ -void OPLL_setPan(OPLL *opll, uint32_t ch, uint8_t pan); - -/** - * Set fine-grained panning - * @param ch 0..8:tone 9:bd 10:hh 11:sd 12:tom 13:cym 14,15:reserved - * @param pan output strength of left/right channel. - * pan[0]: left, pan[1]: right. pan[0]=pan[1]=1.0f for center. - */ -void OPLL_setPanFine(OPLL *opll, uint32_t ch, float pan[2]); - -/** - * Set chip type. If vrc7 is selected, r#14 is ignored. - * This method not change the current ROM patch set. - * To change ROM patch set, use OPLL_resetPatch. - * @param type 0:YM2413 1:VRC7 - */ -void OPLL_setChipType(OPLL *opll, uint8_t type); - -void OPLL_writeIO(OPLL *opll, uint32_t reg, uint8_t val); -void OPLL_writeReg(OPLL *opll, uint32_t reg, uint8_t val); - -/** - * Calculate one sample - */ -int16_t OPLL_calc(OPLL *opll); - -/** - * Calulate stereo sample - */ -void OPLL_calcStereo(OPLL *opll, int32_t out[2]); - -void OPLL_setPatch(OPLL *, const uint8_t *dump); -void OPLL_copyPatch(OPLL *, int32_t, OPLL_PATCH *); - -/** - * Force to refresh. - * External program should call this function after updating patch parameters. - */ -void OPLL_forceRefresh(OPLL *); - -void OPLL_dumpToPatch(const uint8_t *dump, OPLL_PATCH *patch); -void OPLL_patchToDump(const OPLL_PATCH *patch, uint8_t *dump); -void OPLL_getDefaultPatch(int32_t type, int32_t num, OPLL_PATCH *); - -/** - * Set channel mask - * @param mask mask flag: OPLL_MASK_* can be used. - * - bit 0..8: mask for ch 1 to 9 (OPLL_MASK_CH(i)) - * - bit 9: mask for Hi-Hat (OPLL_MASK_HH) - * - bit 10: mask for Top-Cym (OPLL_MASK_CYM) - * - bit 11: mask for Tom (OPLL_MASK_TOM) - * - bit 12: mask for Snare Drum (OPLL_MASK_SD) - * - bit 13: mask for Bass Drum (OPLL_MASK_BD) - */ -uint32_t OPLL_setMask(OPLL *, uint32_t mask); - -/** - * Toggler channel mask flag - */ -uint32_t OPLL_toggleMask(OPLL *, uint32_t mask); - -/* for compatibility */ -#define OPLL_set_rate OPLL_setRate -#define OPLL_set_quality OPLL_setQuality -#define OPLL_set_pan OPLL_setPan -#define OPLL_set_pan_fine OPLL_setPanFine -#define OPLL_calc_stereo OPLL_calcStereo -#define OPLL_reset_patch OPLL_resetPatch -#define OPLL_dump2patch OPLL_dumpToPatch -#define OPLL_patch2dump OPLL_patchToDump -#define OPLL_setChipMode OPLL_setChipType - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/extern/NSFplay/legacy/emutypes.h b/extern/NSFplay/legacy/emutypes.h deleted file mode 100644 index bf5d7e1bf..000000000 --- a/extern/NSFplay/legacy/emutypes.h +++ /dev/null @@ -1,41 +0,0 @@ -#ifndef _EMUTYPES_H_ -#define _EMUTYPES_H_ - -#if defined(_MSC_VER) -#define INLINE __forceinline -#elif defined(__GNUC__) -#define INLINE __inline__ -#elif defined(_MWERKS_) -#define INLINE inline -#else -#define INLINE -#endif - -#if defined(EMU_DLL_IMPORTS) -#define EMU2149_DLL_IMPORTS -#define EMU2212_DLL_IMPORTS -#define EMU2413_DLL_IMPORTS -#define EMU8950_DLL_IMPORTS -#define EMU76489_DLL_IMPORTS -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -typedef unsigned int e_uint; -typedef signed int e_int; - -typedef unsigned char e_uint8 ; -typedef signed char e_int8 ; - -typedef unsigned short e_uint16 ; -typedef signed short e_int16 ; - -typedef unsigned int e_uint32 ; -typedef signed int e_int32 ; - -#ifdef __cplusplus -} -#endif -#endif diff --git a/extern/NSFplay/legacy/vrc7tone_ft35.h b/extern/NSFplay/legacy/vrc7tone_ft35.h deleted file mode 100644 index 7af08bcf7..000000000 --- a/extern/NSFplay/legacy/vrc7tone_ft35.h +++ /dev/null @@ -1,20 +0,0 @@ -// patch set by Mitsutaka Okazaki used in FamiTracker 0.3.5 and prior (6/24/2001) -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x33, 0x01, 0x09, 0x0e, 0x94, 0x90, 0x40, 0x01, -0x13, 0x41, 0x0f, 0x0d, 0xce, 0xd3, 0x43, 0x13, -0x01, 0x12, 0x1b, 0x06, 0xff, 0xd2, 0x00, 0x32, -0x61, 0x61, 0x1b, 0x07, 0xaf, 0x63, 0x20, 0x28, -0x22, 0x21, 0x1e, 0x06, 0xf0, 0x76, 0x08, 0x28, -0x66, 0x21, 0x15, 0x00, 0x93, 0x94, 0x20, 0xf8, -0x21, 0x61, 0x1c, 0x07, 0x82, 0x81, 0x10, 0x17, -0x23, 0x21, 0x20, 0x1f, 0xc0, 0x71, 0x07, 0x47, -0x25, 0x31, 0x26, 0x05, 0x64, 0x41, 0x18, 0xf8, -0x17, 0x21, 0x28, 0x07, 0xff, 0x83, 0x02, 0xf8, -0x97, 0x81, 0x25, 0x07, 0xcf, 0xc8, 0x02, 0x14, -0x21, 0x21, 0x54, 0x0f, 0x80, 0x7f, 0x07, 0x07, -0x01, 0x01, 0x56, 0x03, 0xd3, 0xb2, 0x43, 0x58, -0x31, 0x21, 0x0c, 0x03, 0x82, 0xc0, 0x40, 0x07, -0x21, 0x01, 0x0c, 0x03, 0xd4, 0xd3, 0x40, 0x84, -0x04, 0x21, 0x28, 0x00, 0xdf, 0xf8, 0xff, 0xf8, -0x23, 0x22, 0x00, 0x00, 0xa8, 0xf8, 0xf8, 0xf8, -0x25, 0x18, 0x00, 0x00, 0xf8, 0xa9, 0xf8, 0x55, diff --git a/extern/NSFplay/legacy/vrc7tone_ft36.h b/extern/NSFplay/legacy/vrc7tone_ft36.h deleted file mode 100644 index f693955e6..000000000 --- a/extern/NSFplay/legacy/vrc7tone_ft36.h +++ /dev/null @@ -1,21 +0,0 @@ -// patch set by quietust (1/18/2004), used in FamiTracker 0.3.6 -// Source: http://nesdev.com/cgi-bin/wwwthreads/showpost.pl?Board=NESemdev&Number=1440 -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x03, 0x21, 0x04, 0x06, 0x8D, 0xF2, 0x42, 0x17, -0x13, 0x41, 0x05, 0x0E, 0x99, 0x96, 0x63, 0x12, -0x31, 0x11, 0x10, 0x0A, 0xF0, 0x9C, 0x32, 0x02, -0x21, 0x61, 0x1D, 0x07, 0x9F, 0x64, 0x20, 0x27, -0x22, 0x21, 0x1E, 0x06, 0xF0, 0x76, 0x08, 0x28, -0x02, 0x01, 0x06, 0x00, 0xF0, 0xF2, 0x03, 0x95, -0x21, 0x61, 0x1C, 0x07, 0x82, 0x81, 0x16, 0x07, -0x23, 0x21, 0x1A, 0x17, 0xEF, 0x82, 0x25, 0x15, -0x25, 0x11, 0x1F, 0x00, 0x86, 0x41, 0x20, 0x11, -0x85, 0x01, 0x1F, 0x0F, 0xE4, 0xA2, 0x11, 0x12, -0x07, 0xC1, 0x2B, 0x45, 0xB4, 0xF1, 0x24, 0xF4, -0x61, 0x23, 0x11, 0x06, 0x96, 0x96, 0x13, 0x16, -0x01, 0x02, 0xD3, 0x05, 0x82, 0xA2, 0x31, 0x51, -0x61, 0x22, 0x0D, 0x02, 0xC3, 0x7F, 0x24, 0x05, -0x21, 0x62, 0x0E, 0x00, 0xA1, 0xA0, 0x44, 0x17, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, diff --git a/extern/NSFplay/legacy/vrc7tone_kt1.h b/extern/NSFplay/legacy/vrc7tone_kt1.h deleted file mode 100644 index 59cff8a3a..000000000 --- a/extern/NSFplay/legacy/vrc7tone_kt1.h +++ /dev/null @@ -1,21 +0,0 @@ -// patch set 1 by kevtris (11/14/1999) -// http://kevtris.org/nes/vrcvii.txt -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x05, 0x03, 0x10, 0x06, 0x74, 0xA1, 0x13, 0xF4, -0x05, 0x01, 0x16, 0x00, 0xF9, 0xA2, 0x15, 0xF5, -0x01, 0x41, 0x11, 0x00, 0xA0, 0xA0, 0x83, 0x95, -0x01, 0x41, 0x17, 0x00, 0x60, 0xF0, 0x83, 0x95, -0x24, 0x41, 0x1F, 0x00, 0x50, 0xB0, 0x94, 0x94, -0x05, 0x01, 0x0B, 0x04, 0x65, 0xA0, 0x54, 0x95, -0x11, 0x41, 0x0E, 0x04, 0x70, 0xC7, 0x13, 0x10, -0x02, 0x44, 0x16, 0x06, 0xE0, 0xE0, 0x31, 0x35, -0x48, 0x22, 0x22, 0x07, 0x50, 0xA1, 0xA5, 0xF4, -0x05, 0xA1, 0x18, 0x00, 0xA2, 0xA2, 0xF5, 0xF5, -0x07, 0x81, 0x2B, 0x05, 0xA5, 0xA5, 0x03, 0x03, -0x01, 0x41, 0x08, 0x08, 0xA0, 0xA0, 0x83, 0x95, -0x21, 0x61, 0x12, 0x00, 0x93, 0x92, 0x74, 0x75, -0x21, 0x62, 0x21, 0x00, 0x84, 0x85, 0x34, 0x15, -0x21, 0x62, 0x0E, 0x00, 0xA1, 0xA0, 0x34, 0x15, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, diff --git a/extern/NSFplay/legacy/vrc7tone_kt2.h b/extern/NSFplay/legacy/vrc7tone_kt2.h deleted file mode 100644 index 92caa42bf..000000000 --- a/extern/NSFplay/legacy/vrc7tone_kt2.h +++ /dev/null @@ -1,21 +0,0 @@ -// patch set 2 by kevtris (11/15/1999) -// http://kevtris.org/nes/vrcvii.txt -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x31, 0x22, 0x23, 0x07, 0xF0, 0xF0, 0xE8, 0xF7, -0x03, 0x31, 0x68, 0x05, 0xF2, 0x74, 0x79, 0x9C, -0x01, 0x51, 0x72, 0x04, 0xF1, 0xD3, 0x9D, 0x8B, -0x22, 0x61, 0x1B, 0x05, 0xC0, 0xA1, 0xF8, 0xE8, -0x22, 0x61, 0x2C, 0x03, 0xD2, 0xA1, 0xA7, 0xE8, -0x31, 0x22, 0xFA, 0x01, 0xF1, 0xF1, 0xF4, 0xEE, -0x21, 0x61, 0x28, 0x06, 0xF1, 0xF1, 0xCE, 0x9B, -0x27, 0x61, 0x60, 0x00, 0xF0, 0xF0, 0xFF, 0xFD, -0x60, 0x21, 0x2B, 0x06, 0x85, 0xF1, 0x79, 0x9D, -0x31, 0xA1, 0xFF, 0x0A, 0x53, 0x62, 0x5E, 0xAF, -0x03, 0xA1, 0x70, 0x0F, 0xD4, 0xA3, 0x94, 0xBE, -0x2B, 0x61, 0xE4, 0x07, 0xF6, 0x93, 0xBD, 0xAC, -0x21, 0x63, 0xED, 0x07, 0x77, 0xF1, 0xC7, 0xE8, -0x21, 0x61, 0x2A, 0x03, 0xF3, 0xE2, 0xB6, 0xD9, -0x21, 0x63, 0x37, 0x03, 0xF3, 0xE2, 0xB6, 0xD9, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, diff --git a/extern/NSFplay/legacy/vrc7tone_mo.h b/extern/NSFplay/legacy/vrc7tone_mo.h deleted file mode 100644 index 3b216f874..000000000 --- a/extern/NSFplay/legacy/vrc7tone_mo.h +++ /dev/null @@ -1,20 +0,0 @@ -/* VRC7 TONES by okazaki@angel.ne.jp (4/10/2004) */ -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x33,0x01,0x09,0x0e,0x94,0x90,0x40,0x01, -0x13,0x41,0x0f,0x0d,0xce,0xd3,0x43,0x13, -0x01,0x12,0x1b,0x06,0xff,0xd2,0x00,0x32, -0x61,0x61,0x1b,0x07,0xaf,0x63,0x20,0x28, -0x22,0x21,0x1e,0x06,0xf0,0x76,0x08,0x28, -0x66,0x21,0x15,0x00,0x93,0x94,0x20,0xf8, -0x21,0x61,0x1c,0x07,0x82,0x81,0x10,0x17, -0x23,0x21,0x20,0x1f,0xc0,0x71,0x07,0x47, -0x25,0x31,0x26,0x05,0x64,0x41,0x18,0xf8, -0x17,0x21,0x28,0x07,0xff,0x83,0x02,0xf8, -0x97,0x81,0x25,0x07,0xcf,0xc8,0x02,0x14, -0x21,0x21,0x54,0x0f,0x80,0x7f,0x07,0x07, -0x01,0x01,0x56,0x03,0xd3,0xb2,0x43,0x58, -0x31,0x21,0x0c,0x03,0x82,0xc0,0x40,0x07, -0x21,0x01,0x0c,0x03,0xd4,0xd3,0x40,0x84, -0x07,0x21,0x14,0x00,0xee,0xf8,0xff,0xf8, -0x01,0x31,0x00,0x00,0xf8,0xf7,0xf8,0xf7, -0x25,0x11,0x00,0x00,0xf8,0xfa,0xf8,0x55, diff --git a/extern/NSFplay/legacy/vrc7tone_nuke.h b/extern/NSFplay/legacy/vrc7tone_nuke.h deleted file mode 100644 index 0afcc4ce0..000000000 --- a/extern/NSFplay/legacy/vrc7tone_nuke.h +++ /dev/null @@ -1,21 +0,0 @@ -// patch set by Nuke.YKT (3/15/2019) -// https://siliconpr0n.org/archive/doku.php?id=vendor:yamaha:opl2#ym2413_instruments -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x03, 0x21, 0x05, 0x06, 0xE8, 0x81, 0x42, 0x27, -0x13, 0x41, 0x14, 0x0D, 0xD8, 0xF6, 0x23, 0x12, -0x11, 0x11, 0x08, 0x08, 0xFA, 0xB2, 0x20, 0x12, -0x31, 0x61, 0x0C, 0x07, 0xA8, 0x64, 0x61, 0x27, -0x32, 0x21, 0x1E, 0x06, 0xE1, 0x76, 0x01, 0x28, -0x02, 0x01, 0x06, 0x00, 0xA3, 0xE2, 0xF4, 0xF4, -0x21, 0x61, 0x1D, 0x07, 0x82, 0x81, 0x11, 0x07, -0x23, 0x21, 0x22, 0x17, 0xA2, 0x72, 0x01, 0x17, -0x35, 0x11, 0x25, 0x00, 0x40, 0x73, 0x72, 0x01, -0xB5, 0x01, 0x0F, 0x0F, 0xA8, 0xA5, 0x51, 0x02, -0x17, 0xC1, 0x24, 0x07, 0xF8, 0xF8, 0x22, 0x12, -0x71, 0x23, 0x11, 0x06, 0x65, 0x74, 0x18, 0x16, -0x01, 0x02, 0xD3, 0x05, 0xC9, 0x95, 0x03, 0x02, -0x61, 0x63, 0x0C, 0x00, 0x94, 0xC0, 0x33, 0xF6, -0x21, 0x72, 0x0D, 0x00, 0xC1, 0xD5, 0x56, 0x06, -0x01, 0x01, 0x18, 0x0F, 0xDF, 0xF8, 0x6A, 0x6D, -0x01, 0x01, 0x00, 0x00, 0xC8, 0xD8, 0xA7, 0x68, -0x05, 0x01, 0x00, 0x00, 0xF8, 0xAA, 0x59, 0x55, diff --git a/extern/NSFplay/legacy/vrc7tone_rw.h b/extern/NSFplay/legacy/vrc7tone_rw.h deleted file mode 100644 index bf2261208..000000000 --- a/extern/NSFplay/legacy/vrc7tone_rw.h +++ /dev/null @@ -1,21 +0,0 @@ -// patch set by rainwarrior (8/01/2012) -// http://forums.nesdev.com/viewtopic.php?f=6&t=9141 -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x03, 0x21, 0x05, 0x06, 0xB8, 0x82, 0x42, 0x27, -0x13, 0x41, 0x13, 0x0D, 0xD8, 0xD6, 0x23, 0x12, -0x31, 0x11, 0x08, 0x08, 0xFA, 0x9A, 0x22, 0x02, -0x31, 0x61, 0x18, 0x07, 0x78, 0x64, 0x30, 0x27, -0x22, 0x21, 0x1E, 0x06, 0xF0, 0x76, 0x08, 0x28, -0x02, 0x01, 0x06, 0x00, 0xF0, 0xF2, 0x03, 0xF5, -0x21, 0x61, 0x1D, 0x07, 0x82, 0x81, 0x16, 0x07, -0x23, 0x21, 0x1A, 0x17, 0xCF, 0x72, 0x25, 0x17, -0x15, 0x11, 0x25, 0x00, 0x4F, 0x71, 0x00, 0x11, -0x85, 0x01, 0x12, 0x0F, 0x99, 0xA2, 0x40, 0x02, -0x07, 0xC1, 0x69, 0x07, 0xF3, 0xF5, 0xA7, 0x12, -0x71, 0x23, 0x0D, 0x06, 0x66, 0x75, 0x23, 0x16, -0x01, 0x02, 0xD3, 0x05, 0xA3, 0x92, 0xF7, 0x52, -0x61, 0x63, 0x0C, 0x00, 0x94, 0xAF, 0x34, 0x06, -0x21, 0x62, 0x0D, 0x00, 0xB1, 0xA0, 0x54, 0x17, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, diff --git a/extern/NSFplay/nes_apu.cpp b/extern/NSFplay/nes_apu.cpp index 9d50dfc78..b2ebb677a 100644 --- a/extern/NSFplay/nes_apu.cpp +++ b/extern/NSFplay/nes_apu.cpp @@ -1,400 +1,400 @@ -// -// NES 2A03 -// -#include -#include "nes_apu.h" - -namespace xgm -{ - void NES_APU::sweep_sqr (int i) - { - int shifted = freq[i] >> sweep_amount[i]; - if (i == 0 && sweep_mode[i]) shifted += 1; - sfreq[i] = freq[i] + (sweep_mode[i] ? -shifted : shifted); - //DEBUG_OUT("shifted[%d] = %d (%d >> %d)\n",i,shifted,freq[i],sweep_amount[i]); - } - - void NES_APU::FrameSequence(int s) - { - //DEBUG_OUT("FrameSequence(%d)\n",s); - - if (s > 3) return; // no operation in step 4 - - // 240hz clock - for (int i=0; i < 2; ++i) - { - bool divider = false; - if (envelope_write[i]) - { - envelope_write[i] = false; - envelope_counter[i] = 15; - envelope_div[i] = 0; - } - else - { - ++envelope_div[i]; - if (envelope_div[i] > envelope_div_period[i]) - { - divider = true; - envelope_div[i] = 0; - } - } - if (divider) - { - if (envelope_loop[i] && envelope_counter[i] == 0) - envelope_counter[i] = 15; - else if (envelope_counter[i] > 0) - --envelope_counter[i]; - } - } - - // 120hz clock - if ((s&1) == 0) - for (int i=0; i < 2; ++i) - { - if (!envelope_loop[i] && (length_counter[i] > 0)) - --length_counter[i]; - - if (sweep_enable[i]) - { - //DEBUG_OUT("Clock sweep: %d\n", i); - - --sweep_div[i]; - if (sweep_div[i] <= 0) - { - sweep_sqr(i); // calculate new sweep target - - //DEBUG_OUT("sweep_div[%d] (0/%d)\n",i,sweep_div_period[i]); - //DEBUG_OUT("freq[%d]=%d > sfreq[%d]=%d\n",i,freq[i],i,sfreq[i]); - - if (freq[i] >= 8 && sfreq[i] < 0x800 && sweep_amount[i] > 0) // update frequency if appropriate - { - freq[i] = sfreq[i] < 0 ? 0 : sfreq[i]; - } - sweep_div[i] = sweep_div_period[i] + 1; - - //DEBUG_OUT("freq[%d]=%d\n",i,freq[i]); - } - - if (sweep_write[i]) - { - sweep_div[i] = sweep_div_period[i] + 1; - sweep_write[i] = false; - } - } - } - - } - - INT32 NES_APU::calc_sqr (int i, UINT32 clocks) - { - static const INT16 sqrtbl[4][16] = { - {0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0}, - {1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1} - }; - - scounter[i] -= clocks; - while (scounter[i] < 0) - { - sphase[i] = (sphase[i] + 1) & 15; - scounter[i] += freq[i] + 1; - } - - INT32 ret = 0; - if (length_counter[i] > 0 && - freq[i] >= 8 && - sfreq[i] < 0x800 - ) - { - int v = envelope_disable[i] ? volume[i] : envelope_counter[i]; - ret = sqrtbl[duty[i]][sphase[i]] ? v : 0; - } - - return ret; - } - - bool NES_APU::Read (UINT32 adr, UINT32 & val, UINT32 id) - { - if (0x4000 <= adr && adr < 0x4008) - { - val |= reg[adr&0x7]; - return true; - } - else if(adr==0x4015) - { - val |= (length_counter[1]?2:0)|(length_counter[0]?1:0); - return true; - } - else - return false; - } - - void NES_APU::Tick (UINT32 clocks) - { - out[0] = calc_sqr(0, clocks); - out[1] = calc_sqr(1, clocks); - } - - // ¶¬‚³‚ê‚é”gŒ`‚ÌU•‚Í0-8191 - UINT32 NES_APU::Render (INT32 b[2]) - { - out[0] = (mask & 1) ? 0 : out[0]; - out[1] = (mask & 2) ? 0 : out[1]; - - INT32 m[2]; - - if(option[OPT_NONLINEAR_MIXER]) - { - INT32 voltage = square_table[out[0] + out[1]]; - m[0] = out[0] << 6; - m[1] = out[1] << 6; - INT32 ref = m[0] + m[1]; - if (ref > 0) - { - m[0] = (m[0] * voltage) / ref; - m[1] = (m[1] * voltage) / ref; - } - else - { - m[0] = voltage; - m[1] = voltage; - } - } - else - { - m[0] = (out[0] * square_linear) / 15; - m[1] = (out[1] * square_linear) / 15; - } - - b[0] = m[0] * sm[0][0]; - b[0] += m[1] * sm[0][1]; - b[0] >>= 7; - - b[1] = m[0] * sm[1][0]; - b[1] += m[1] * sm[1][1]; - b[1] >>= 7; - - return 2; - } - - NES_APU::NES_APU () - { - SetClock (DEFAULT_CLOCK); - SetRate (DEFAULT_RATE); - option[OPT_UNMUTE_ON_RESET] = true; - option[OPT_PHASE_REFRESH] = true; - option[OPT_NONLINEAR_MIXER] = true; - option[OPT_DUTY_SWAP] = false; - option[OPT_NEGATE_SWEEP_INIT] = false; - - square_table[0] = 0; - for(int i=1;i<32;i++) - square_table[i]=(INT32)((8192.0*95.88)/(8128.0/i+100)); - - square_linear = square_table[15]; // match linear scale to one full volume square of nonlinear - - for(int c=0;c<2;++c) - for(int t=0;t<2;++t) - sm[c][t] = 128; - } - - NES_APU::~NES_APU () - { - } - - void NES_APU::Reset () - { - int i; - gclock = 0; - mask = 0; - - for (int i=0; i<2; ++i) - { - scounter[i] = 0; - sphase[i] = 0; - duty[i] = 0; - volume[i] = 0; - freq[i] = 0; - sfreq[i] = 0; - sweep_enable[i] = 0; - sweep_mode[i] = 0; - sweep_write[i] = 0; - sweep_div_period[i] = 0; - sweep_div[i] = 1; - sweep_amount[i] = 0; - envelope_disable[i] = 0; - envelope_loop[i] = 0; - envelope_write[i] = 0; - envelope_div_period[i] = 0; - envelope_div[0] = 0; - envelope_counter[i] = 0; - length_counter[i] = 0; - enable[i] = 0; - } - - for (i = 0x4000; i < 0x4008; i++) - Write (i, 0); - - Write (0x4015, 0); - if (option[OPT_UNMUTE_ON_RESET]) - Write (0x4015, 0x0f); - if (option[OPT_NEGATE_SWEEP_INIT]) - { - Write (0x4001, 0x08); - Write (0x4005, 0x08); - } - - for (i = 0; i < 2; i++) - out[i] = 0; - - SetRate(rate); - } - - void NES_APU::SetOption (int id, int val) - { - if(id 1) return; - sm[0][trk] = mixl; - sm[1][trk] = mixr; - } - - ITrackInfo *NES_APU::GetTrackInfo(int trk) - { - trkinfo[trk]._freq = freq[trk]; - if(freq[trk]) - trkinfo[trk].freq = clock/16/(freq[trk] + 1); - else - trkinfo[trk].freq = 0; - - trkinfo[trk].output = out[trk]; - trkinfo[trk].volume = volume[trk]+(envelope_disable[trk]?0:0x10)+(envelope_loop[trk]?0x20:0); - trkinfo[trk].key = - enable[trk] && - length_counter[trk] > 0 && - freq[trk] >= 8 && - sfreq[trk] < 0x800 && - (envelope_disable[trk] ? volume[trk] : (envelope_counter[trk] > 0)); - trkinfo[trk].tone = duty[trk]; - trkinfo[trk].max_volume = 15; - return &trkinfo[trk]; - } - - bool NES_APU::Write (UINT32 adr, UINT32 val, UINT32 id) - { - int ch; - - static const UINT8 length_table[32] = { - 0x0A, 0xFE, - 0x14, 0x02, - 0x28, 0x04, - 0x50, 0x06, - 0xA0, 0x08, - 0x3C, 0x0A, - 0x0E, 0x0C, - 0x1A, 0x0E, - 0x0C, 0x10, - 0x18, 0x12, - 0x30, 0x14, - 0x60, 0x16, - 0xC0, 0x18, - 0x48, 0x1A, - 0x10, 0x1C, - 0x20, 0x1E - }; - - if (0x4000 <= adr && adr < 0x4008) - { - //DEBUG_OUT("$%04X = %02X\n",adr,val); - - adr &= 0xf; - ch = adr >> 2; - switch (adr) - { - case 0x0: - case 0x4: - volume[ch] = val & 15; - envelope_disable[ch] = (val >> 4) & 1; - envelope_loop[ch] = (val >> 5) & 1; - envelope_div_period[ch] = (val & 15); - duty[ch] = (val >> 6) & 3; - if (option[OPT_DUTY_SWAP]) - { - if (duty[ch] == 1) duty[ch] = 2; - else if (duty[ch] == 2) duty[ch] = 1; - } - break; - - case 0x1: - case 0x5: - sweep_enable[ch] = (val >> 7) & 1; - sweep_div_period[ch] = (((val >> 4) & 7)); - sweep_mode[ch] = (val >> 3) & 1; - sweep_amount[ch] = val & 7; - sweep_write[ch] = true; - sweep_sqr(ch); - break; - - case 0x2: - case 0x6: - freq[ch] = val | (freq[ch] & 0x700) ; - sweep_sqr(ch); - break; - - case 0x3: - case 0x7: - freq[ch] = (freq[ch] & 0xFF) | ((val & 0x7) << 8) ; - if (option[OPT_PHASE_REFRESH]) - sphase[ch] = 0; - envelope_write[ch] = true; - if (enable[ch]) - { - length_counter[ch] = length_table[(val >> 3) & 0x1f]; - } - sweep_sqr(ch); - break; - - default: - return false; - } - reg[adr] = val; - return true; - } - else if (adr == 0x4015) - { - enable[0] = (val & 1) ? true : false; - enable[1] = (val & 2) ? true : false; - - if (!enable[0]) - length_counter[0] = 0; - if (!enable[1]) - length_counter[1] = 0; - - reg[adr-0x4000] = val; - return true; - } - - // 4017 is handled in nes_dmc.cpp - //else if (adr == 0x4017) - //{ - //} - - return false; - } -} // namespace xgm; +// +// NES 2A03 +// +#include +#include "nes_apu.h" + +namespace xgm +{ + void NES_APU::sweep_sqr (int i) + { + int shifted = freq[i] >> sweep_amount[i]; + if (i == 0 && sweep_mode[i]) shifted += 1; + sfreq[i] = freq[i] + (sweep_mode[i] ? -shifted : shifted); + //DEBUG_OUT("shifted[%d] = %d (%d >> %d)Â¥n",i,shifted,freq[i],sweep_amount[i]); + } + + void NES_APU::FrameSequence(int s) + { + //DEBUG_OUT("FrameSequence(%d)Â¥n",s); + + if (s > 3) return; // no operation in step 4 + + // 240hz clock + for (int i=0; i < 2; ++i) + { + bool divider = false; + if (envelope_write[i]) + { + envelope_write[i] = false; + envelope_counter[i] = 15; + envelope_div[i] = 0; + } + else + { + ++envelope_div[i]; + if (envelope_div[i] > envelope_div_period[i]) + { + divider = true; + envelope_div[i] = 0; + } + } + if (divider) + { + if (envelope_loop[i] && envelope_counter[i] == 0) + envelope_counter[i] = 15; + else if (envelope_counter[i] > 0) + --envelope_counter[i]; + } + } + + // 120hz clock + if ((s&1) == 0) + for (int i=0; i < 2; ++i) + { + if (!envelope_loop[i] && (length_counter[i] > 0)) + --length_counter[i]; + + if (sweep_enable[i]) + { + //DEBUG_OUT("Clock sweep: %dÂ¥n", i); + + --sweep_div[i]; + if (sweep_div[i] <= 0) + { + sweep_sqr(i); // calculate new sweep target + + //DEBUG_OUT("sweep_div[%d] (0/%d)Â¥n",i,sweep_div_period[i]); + //DEBUG_OUT("freq[%d]=%d > sfreq[%d]=%dÂ¥n",i,freq[i],i,sfreq[i]); + + if (freq[i] >= 8 && sfreq[i] < 0x800 && sweep_amount[i] > 0) // update frequency if appropriate + { + freq[i] = sfreq[i] < 0 ? 0 : sfreq[i]; + } + sweep_div[i] = sweep_div_period[i] + 1; + + //DEBUG_OUT("freq[%d]=%dÂ¥n",i,freq[i]); + } + + if (sweep_write[i]) + { + sweep_div[i] = sweep_div_period[i] + 1; + sweep_write[i] = false; + } + } + } + + } + + INT32 NES_APU::calc_sqr (int i, UINT32 clocks) + { + static const INT16 sqrtbl[4][16] = { + {0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0}, + {1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1} + }; + + scounter[i] -= clocks; + while (scounter[i] < 0) + { + sphase[i] = (sphase[i] + 1) & 15; + scounter[i] += freq[i] + 1; + } + + INT32 ret = 0; + if (length_counter[i] > 0 && + freq[i] >= 8 && + sfreq[i] < 0x800 + ) + { + int v = envelope_disable[i] ? volume[i] : envelope_counter[i]; + ret = sqrtbl[duty[i]][sphase[i]] ? v : 0; + } + + return ret; + } + + bool NES_APU::Read (UINT32 adr, UINT32 & val, UINT32 id) + { + if (0x4000 <= adr && adr < 0x4008) + { + val |= reg[adr&0x7]; + return true; + } + else if(adr==0x4015) + { + val |= (length_counter[1]?2:0)|(length_counter[0]?1:0); + return true; + } + else + return false; + } + + void NES_APU::Tick (UINT32 clocks) + { + out[0] = calc_sqr(0, clocks); + out[1] = calc_sqr(1, clocks); + } + + // ツé’カツé’ャツã¤ï½³ï¾‚ã¥ï½ªï¾‚ã¥ï½©ï¾‚波ツ形ツã¥å€‹æŒ¯ï¾‚閉敖ã¥0-8191 + UINT32 NES_APU::Render (INT32 b[2]) + { + out[0] = (mask & 1) ? 0 : out[0]; + out[1] = (mask & 2) ? 0 : out[1]; + + INT32 m[2]; + + if(option[OPT_NONLINEAR_MIXER]) + { + INT32 voltage = square_table[out[0] + out[1]]; + m[0] = out[0] << 6; + m[1] = out[1] << 6; + INT32 ref = m[0] + m[1]; + if (ref > 0) + { + m[0] = (m[0] * voltage) / ref; + m[1] = (m[1] * voltage) / ref; + } + else + { + m[0] = voltage; + m[1] = voltage; + } + } + else + { + m[0] = (out[0] * square_linear) / 15; + m[1] = (out[1] * square_linear) / 15; + } + + b[0] = m[0] * sm[0][0]; + b[0] += m[1] * sm[0][1]; + b[0] >>= 7; + + b[1] = m[0] * sm[1][0]; + b[1] += m[1] * sm[1][1]; + b[1] >>= 7; + + return 2; + } + + NES_APU::NES_APU () + { + SetClock (DEFAULT_CLOCK); + SetRate (DEFAULT_RATE); + option[OPT_UNMUTE_ON_RESET] = true; + option[OPT_PHASE_REFRESH] = true; + option[OPT_NONLINEAR_MIXER] = true; + option[OPT_DUTY_SWAP] = false; + option[OPT_NEGATE_SWEEP_INIT] = false; + + square_table[0] = 0; + for(int i=1;i<32;i++) + square_table[i]=(INT32)((8192.0*95.88)/(8128.0/i+100)); + + square_linear = square_table[15]; // match linear scale to one full volume square of nonlinear + + for(int c=0;c<2;++c) + for(int t=0;t<2;++t) + sm[c][t] = 128; + } + + NES_APU::‾NES_APU () + { + } + + void NES_APU::Reset () + { + int i; + gclock = 0; + mask = 0; + + for (int i=0; i<2; ++i) + { + scounter[i] = 0; + sphase[i] = 0; + duty[i] = 0; + volume[i] = 0; + freq[i] = 0; + sfreq[i] = 0; + sweep_enable[i] = 0; + sweep_mode[i] = 0; + sweep_write[i] = 0; + sweep_div_period[i] = 0; + sweep_div[i] = 1; + sweep_amount[i] = 0; + envelope_disable[i] = 0; + envelope_loop[i] = 0; + envelope_write[i] = 0; + envelope_div_period[i] = 0; + envelope_div[0] = 0; + envelope_counter[i] = 0; + length_counter[i] = 0; + enable[i] = 0; + } + + for (i = 0x4000; i < 0x4008; i++) + Write (i, 0); + + Write (0x4015, 0); + if (option[OPT_UNMUTE_ON_RESET]) + Write (0x4015, 0x0f); + if (option[OPT_NEGATE_SWEEP_INIT]) + { + Write (0x4001, 0x08); + Write (0x4005, 0x08); + } + + for (i = 0; i < 2; i++) + out[i] = 0; + + SetRate(rate); + } + + void NES_APU::SetOption (int id, int val) + { + if(id 1) return; + sm[0][trk] = mixl; + sm[1][trk] = mixr; + } + + ITrackInfo *NES_APU::GetTrackInfo(int trk) + { + trkinfo[trk]._freq = freq[trk]; + if(freq[trk]) + trkinfo[trk].freq = clock/16/(freq[trk] + 1); + else + trkinfo[trk].freq = 0; + + trkinfo[trk].output = out[trk]; + trkinfo[trk].volume = volume[trk]+(envelope_disable[trk]?0:0x10)+(envelope_loop[trk]?0x20:0); + trkinfo[trk].key = + enable[trk] && + length_counter[trk] > 0 && + freq[trk] >= 8 && + sfreq[trk] < 0x800 && + (envelope_disable[trk] ? volume[trk] : (envelope_counter[trk] > 0)); + trkinfo[trk].tone = duty[trk]; + trkinfo[trk].max_volume = 15; + return &trkinfo[trk]; + } + + bool NES_APU::Write (UINT32 adr, UINT32 val, UINT32 id) + { + int ch; + + static const UINT8 length_table[32] = { + 0x0A, 0xFE, + 0x14, 0x02, + 0x28, 0x04, + 0x50, 0x06, + 0xA0, 0x08, + 0x3C, 0x0A, + 0x0E, 0x0C, + 0x1A, 0x0E, + 0x0C, 0x10, + 0x18, 0x12, + 0x30, 0x14, + 0x60, 0x16, + 0xC0, 0x18, + 0x48, 0x1A, + 0x10, 0x1C, + 0x20, 0x1E + }; + + if (0x4000 <= adr && adr < 0x4008) + { + //DEBUG_OUT("$%04X = %02XÂ¥n",adr,val); + + adr &= 0xf; + ch = adr >> 2; + switch (adr) + { + case 0x0: + case 0x4: + volume[ch] = val & 15; + envelope_disable[ch] = (val >> 4) & 1; + envelope_loop[ch] = (val >> 5) & 1; + envelope_div_period[ch] = (val & 15); + duty[ch] = (val >> 6) & 3; + if (option[OPT_DUTY_SWAP]) + { + if (duty[ch] == 1) duty[ch] = 2; + else if (duty[ch] == 2) duty[ch] = 1; + } + break; + + case 0x1: + case 0x5: + sweep_enable[ch] = (val >> 7) & 1; + sweep_div_period[ch] = (((val >> 4) & 7)); + sweep_mode[ch] = (val >> 3) & 1; + sweep_amount[ch] = val & 7; + sweep_write[ch] = true; + sweep_sqr(ch); + break; + + case 0x2: + case 0x6: + freq[ch] = val | (freq[ch] & 0x700) ; + sweep_sqr(ch); + break; + + case 0x3: + case 0x7: + freq[ch] = (freq[ch] & 0xFF) | ((val & 0x7) << 8) ; + if (option[OPT_PHASE_REFRESH]) + sphase[ch] = 0; + envelope_write[ch] = true; + if (enable[ch]) + { + length_counter[ch] = length_table[(val >> 3) & 0x1f]; + } + sweep_sqr(ch); + break; + + default: + return false; + } + reg[adr] = val; + return true; + } + else if (adr == 0x4015) + { + enable[0] = (val & 1) ? true : false; + enable[1] = (val & 2) ? true : false; + + if (!enable[0]) + length_counter[0] = 0; + if (!enable[1]) + length_counter[1] = 0; + + reg[adr-0x4000] = val; + return true; + } + + // 4017 is handled in nes_dmc.cpp + //else if (adr == 0x4017) + //{ + //} + + return false; + } +} // namespace xgm; diff --git a/extern/NSFplay/nes_apu.h b/extern/NSFplay/nes_apu.h index 2a8e75412..a6caa282b 100644 --- a/extern/NSFplay/nes_apu.h +++ b/extern/NSFplay/nes_apu.h @@ -22,7 +22,7 @@ namespace xgm { SQR0_MASK = 1, SQR1_MASK = 2, }; protected: - int option[OPT_END]; // ŠeŽíƒIƒvƒVƒ‡ƒ“ + int option[OPT_END]; // å„種オプション int mask; INT32 sm[2][2]; @@ -66,7 +66,7 @@ namespace xgm public: NES_APU (); - ~NES_APU (); + ‾NES_APU (); void FrameSequence(int s); diff --git a/extern/NSFplay/nes_dmc.cpp b/extern/NSFplay/nes_dmc.cpp index 8073a22ca..49a58eb5e 100644 --- a/extern/NSFplay/nes_dmc.cpp +++ b/extern/NSFplay/nes_dmc.cpp @@ -67,7 +67,7 @@ namespace xgm } - NES_DMC::~NES_DMC () + NES_DMC::‾NES_DMC () { } @@ -122,7 +122,7 @@ namespace xgm void NES_DMC::FrameSequence(int s) { - //DEBUG_OUT("FrameSequence: %d\n",s); + //DEBUG_OUT("FrameSequence: %dÂ¥n",s); if (s > 3) return; // no operation in step 4 @@ -193,7 +193,7 @@ namespace xgm } - // ŽOŠp”gƒ`ƒƒƒ“ƒlƒ‹‚ÌŒvŽZ –ß‚è’l‚Í0-15 + // 三角波ãƒãƒ£ãƒ³ãƒãƒ«ã®è¨ˆç®— 戻り値ã¯0-15 UINT32 NES_DMC::calc_tri (UINT32 clocks) { static UINT32 tritbl[32] = @@ -219,10 +219,10 @@ namespace xgm return ret; } - // ƒmƒCƒYƒ`ƒƒƒ“ƒlƒ‹‚ÌŒvŽZ –ß‚è’l‚Í0-127 - // ’áƒTƒ“ƒvƒŠƒ“ƒOƒŒ[ƒg‚Ҭ‚·‚邯ƒGƒCƒŠƒAƒXƒmƒCƒY‚ªŒƒ‚µ‚¢‚̂Š- // ƒmƒCƒY‚¾‚¯‚Í‚±‚ÌŠÖ”“à‚Å‚ƒNƒƒbƒN‡¬‚µAŠÈˆÕ‚ȃTƒ“ƒvƒŠƒ“ƒOƒŒ[ƒg - // •ÏŠ·‚ðs‚Á‚Ä‚¢‚éB + // ノイズãƒãƒ£ãƒ³ãƒãƒ«ã®è¨ˆç®— 戻り値ã¯0-127 + // 低サンプリングレートã§åˆæˆã™ã‚‹ã¨ã‚¨ã‚¤ãƒªã‚¢ã‚¹ãƒŽã‚¤ã‚ºãŒæ¿€ã—ã„ã®ã§ + // ノイズã ã‘ã¯ã“ã®é–¢æ•°å†…ã§é«˜ã‚¯ãƒ­ãƒƒã‚¯åˆæˆã—ã€ç°¡æ˜“ãªã‚µãƒ³ãƒ—リングレート + // 変æ›ã‚’行ã£ã¦ã„る。 UINT32 NES_DMC::calc_noise(UINT32 clocks) { UINT32 env = envelope_disable ? noise_volume : envelope_counter; @@ -620,7 +620,7 @@ namespace xgm if (adr == 0x4017) { - //DEBUG_OUT("4017 = %02X\n", val); + //DEBUG_OUT("4017 = %02XÂ¥n", val); frame_irq_enable = ((val & 0x40) != 0x40); if (frame_irq_enable) frame_irq = false; cpu->UpdateIRQ(NES_CPU::IRQD_FRAME, false); @@ -645,7 +645,7 @@ namespace xgm reg[adr-0x4008] = val&0xff; - //DEBUG_OUT("$%04X %02X\n", adr, val); + //DEBUG_OUT("$%04X %02XÂ¥n", adr, val); switch (adr) { @@ -724,12 +724,12 @@ namespace xgm case 0x4012: adr_reg = val&0xff; - // ‚±‚±‚Ådaddress‚ÍXV‚³‚ê‚È‚¢ + // ã“ã“ã§daddressã¯æ›´æ–°ã•れãªã„ break; case 0x4013: len_reg = val&0xff; - // ‚±‚±‚Ålength‚ÍXV‚³‚ê‚È‚¢ + // ã“ã“ã§lengthã¯æ›´æ–°ã•れãªã„ break; default: diff --git a/extern/NSFplay/nes_dmc.h b/extern/NSFplay/nes_dmc.h index 9152584aa..6cc8058a2 100644 --- a/extern/NSFplay/nes_dmc.h +++ b/extern/NSFplay/nes_dmc.h @@ -1,129 +1,129 @@ -#ifndef _NES_DMC_H_ -#define _NES_DMC_H_ - -#include "../device.h" -#include "../Audio/MedianFilter.h" -#include "../CPU/nes_cpu.h" - -namespace xgm -{ - class NES_APU; // forward declaration - - /** Bottom Half of APU **/ - class NES_DMC:public ISoundChip - { - public: - enum - { - OPT_ENABLE_4011=0, - OPT_ENABLE_PNOISE, - OPT_UNMUTE_ON_RESET, - OPT_DPCM_ANTI_CLICK, - OPT_NONLINEAR_MIXER, - OPT_RANDOMIZE_NOISE, - OPT_TRI_MUTE, - OPT_RANDOMIZE_TRI, - OPT_DPCM_REVERSE, - OPT_END - }; - protected: - const int GETA_BITS; - static const UINT32 freq_table[2][16]; - static const UINT32 wavlen_table[2][16]; - UINT32 tnd_table[2][16][16][128]; - - int option[OPT_END]; - int mask; - INT32 sm[2][3]; - UINT8 reg[0x10]; - UINT32 len_reg; - UINT32 adr_reg; - IDevice *memory; - UINT32 out[3]; - UINT32 daddress; - UINT32 dlength; - UINT32 data; - bool empty; - INT16 damp; - int dac_lsb; - bool dmc_pop; - INT32 dmc_pop_offset; - INT32 dmc_pop_follow; - double clock; - UINT32 rate; - int pal; - int mode; - bool irq; - - INT32 counter[3]; // frequency dividers - int tphase; // triangle phase - UINT32 nfreq; // noise frequency - UINT32 dfreq; // DPCM frequency - - UINT32 tri_freq; - int linear_counter; - int linear_counter_reload; - bool linear_counter_halt; - bool linear_counter_control; - - int noise_volume; - UINT32 noise, noise_tap; - - // noise envelope - bool envelope_loop; - bool envelope_disable; - bool envelope_write; - int envelope_div_period; - int envelope_div; - int envelope_counter; - - bool enable[2]; // tri/noise enable - int length_counter[2]; // 0=tri, 1=noise - - TrackInfoBasic trkinfo[3]; - - // frame sequencer - NES_APU* apu; // apu is clocked by DMC's frame sequencer - int frame_sequence_count; // current cycle count - int frame_sequence_length; // CPU cycles per FrameSequence - int frame_sequence_step; // current step of frame sequence - int frame_sequence_steps; // 4/5 steps per frame - bool frame_irq; - bool frame_irq_enable; - - NES_CPU* cpu; // IRQ needs CPU access - - inline UINT32 calc_tri (UINT32 clocks); - inline UINT32 calc_dmc (UINT32 clocks); - inline UINT32 calc_noise (UINT32 clocks); - - public: - NES_DMC (); - ~NES_DMC (); - - void InitializeTNDTable(double wt, double wn, double wd); - void SetPal (bool is_pal); - void SetAPU (NES_APU* apu_); - void SetMemory (IDevice * r); - void FrameSequence(int s); - int GetDamp(){ return (damp<<1)|dac_lsb ; } - void TickFrameSequence (UINT32 clocks); - - virtual void Reset (); - virtual void Tick (UINT32 clocks); - virtual UINT32 Render (INT32 b[2]); - virtual bool Write (UINT32 adr, UINT32 val, UINT32 id=0); - virtual bool Read (UINT32 adr, UINT32 & val, UINT32 id=0); - virtual void SetRate (double rate); - virtual void SetClock (double rate); - virtual void SetOption (int, int); - virtual void SetMask(int m){ mask = m; } - virtual void SetStereoMix (int trk, xgm::INT16 mixl, xgm::INT16 mixr); - virtual ITrackInfo *GetTrackInfo(int trk); - - void SetCPU(NES_CPU* cpu_); - }; - -} - -#endif +#ifndef _NES_DMC_H_ +#define _NES_DMC_H_ + +#include "../device.h" +#include "../Audio/MedianFilter.h" +#include "../CPU/nes_cpu.h" + +namespace xgm +{ + class NES_APU; // forward declaration + + /** Bottom Half of APU **/ + class NES_DMC:public ISoundChip + { + public: + enum + { + OPT_ENABLE_4011=0, + OPT_ENABLE_PNOISE, + OPT_UNMUTE_ON_RESET, + OPT_DPCM_ANTI_CLICK, + OPT_NONLINEAR_MIXER, + OPT_RANDOMIZE_NOISE, + OPT_TRI_MUTE, + OPT_RANDOMIZE_TRI, + OPT_DPCM_REVERSE, + OPT_END + }; + protected: + const int GETA_BITS; + static const UINT32 freq_table[2][16]; + static const UINT32 wavlen_table[2][16]; + UINT32 tnd_table[2][16][16][128]; + + int option[OPT_END]; + int mask; + INT32 sm[2][3]; + UINT8 reg[0x10]; + UINT32 len_reg; + UINT32 adr_reg; + IDevice *memory; + UINT32 out[3]; + UINT32 daddress; + UINT32 dlength; + UINT32 data; + bool empty; + INT16 damp; + int dac_lsb; + bool dmc_pop; + INT32 dmc_pop_offset; + INT32 dmc_pop_follow; + double clock; + UINT32 rate; + int pal; + int mode; + bool irq; + + INT32 counter[3]; // frequency dividers + int tphase; // triangle phase + UINT32 nfreq; // noise frequency + UINT32 dfreq; // DPCM frequency + + UINT32 tri_freq; + int linear_counter; + int linear_counter_reload; + bool linear_counter_halt; + bool linear_counter_control; + + int noise_volume; + UINT32 noise, noise_tap; + + // noise envelope + bool envelope_loop; + bool envelope_disable; + bool envelope_write; + int envelope_div_period; + int envelope_div; + int envelope_counter; + + bool enable[2]; // tri/noise enable + int length_counter[2]; // 0=tri, 1=noise + + TrackInfoBasic trkinfo[3]; + + // frame sequencer + NES_APU* apu; // apu is clocked by DMC's frame sequencer + int frame_sequence_count; // current cycle count + int frame_sequence_length; // CPU cycles per FrameSequence + int frame_sequence_step; // current step of frame sequence + int frame_sequence_steps; // 4/5 steps per frame + bool frame_irq; + bool frame_irq_enable; + + NES_CPU* cpu; // IRQ needs CPU access + + inline UINT32 calc_tri (UINT32 clocks); + inline UINT32 calc_dmc (UINT32 clocks); + inline UINT32 calc_noise (UINT32 clocks); + + public: + NES_DMC (); + ‾NES_DMC (); + + void InitializeTNDTable(double wt, double wn, double wd); + void SetPal (bool is_pal); + void SetAPU (NES_APU* apu_); + void SetMemory (IDevice * r); + void FrameSequence(int s); + int GetDamp(){ return (damp<<1)|dac_lsb ; } + void TickFrameSequence (UINT32 clocks); + + virtual void Reset (); + virtual void Tick (UINT32 clocks); + virtual UINT32 Render (INT32 b[2]); + virtual bool Write (UINT32 adr, UINT32 val, UINT32 id=0); + virtual bool Read (UINT32 adr, UINT32 & val, UINT32 id=0); + virtual void SetRate (double rate); + virtual void SetClock (double rate); + virtual void SetOption (int, int); + virtual void SetMask(int m){ mask = m; } + virtual void SetStereoMix (int trk, xgm::INT16 mixl, xgm::INT16 mixr); + virtual ITrackInfo *GetTrackInfo(int trk); + + void SetCPU(NES_CPU* cpu_); + }; + +} + +#endif diff --git a/extern/NSFplay/nes_fds.cpp b/extern/NSFplay/nes_fds.cpp index 9d74c62f0..1e08100c0 100644 --- a/extern/NSFplay/nes_fds.cpp +++ b/extern/NSFplay/nes_fds.cpp @@ -22,7 +22,7 @@ NES_FDS::NES_FDS () Reset(); } -NES_FDS::~NES_FDS () +NES_FDS::‾NES_FDS () { } diff --git a/extern/NSFplay/nes_fds.h b/extern/NSFplay/nes_fds.h index 212e472b4..67c60fd1c 100644 --- a/extern/NSFplay/nes_fds.h +++ b/extern/NSFplay/nes_fds.h @@ -63,7 +63,7 @@ protected: public: NES_FDS (); - virtual ~ NES_FDS (); + virtual ‾ NES_FDS (); virtual void Reset (); virtual void Tick (UINT32 clocks); diff --git a/extern/NSFplay/nes_fme7.cpp b/extern/NSFplay/nes_fme7.cpp deleted file mode 100644 index 9d48de794..000000000 --- a/extern/NSFplay/nes_fme7.cpp +++ /dev/null @@ -1,186 +0,0 @@ -#include "nes_fme7.h" - -using namespace xgm; - -const int DIVIDER = 8; // TODO this is not optimal, rewrite PSG output - -NES_FME7::NES_FME7 () -{ - psg = PSG_new ((e_uint32)DEFAULT_CLOCK, DEFAULT_RATE); - divider = 0; - - for(int c=0;c<2;++c) - for(int t=0;t<3;++t) - sm[c][t] = 128; -} - -NES_FME7::~NES_FME7 () -{ - if (psg) - PSG_delete (psg); -} - -void NES_FME7::SetClock (double c) -{ - this->clock = c * 2.0; -} - -void NES_FME7::SetRate (double r) -{ - //rate = r ? r : DEFAULT_RATE; - rate = DEFAULT_CLOCK / double(DIVIDER); // TODO rewrite PSG to integrate with clock - if (psg) - PSG_set_rate (psg, (e_uint32)rate); -} - -void NES_FME7::SetOption (int id, int val) -{ - if(id= DIVIDER) - { - divider -= DIVIDER; - if (psg) PSG_calc(psg); - } -} - -xgm::UINT32 NES_FME7::Render (xgm::INT32 b[2]) -{ - b[0] = b[1] = 0; - - for (int i=0; i < 3; ++i) - { - // note negative polarity - b[0] -= psg->cout[i] * sm[0][i]; - b[1] -= psg->cout[i] * sm[1][i]; - } - b[0] >>= (7-4); - b[1] >>= (7-4); - - // master volume adjustment - const INT32 MASTER = INT32(0.64 * 256.0); - b[0] = (b[0] * MASTER) >> 8; - b[1] = (b[1] * MASTER) >> 8; - - return 2; -} - -void NES_FME7::SetStereoMix(int trk, xgm::INT16 mixl, xgm::INT16 mixr) -{ - if (trk < 0) return; - if (trk > 2) return; - sm[0][trk] = mixl; - sm[1][trk] = mixr; -} - -ITrackInfo *NES_FME7::GetTrackInfo(int trk) -{ - assert(trk<5); - - if(psg) - { - if (trk<3) - { - trkinfo[trk]._freq = psg->freq[trk]; - if(psg->freq[trk]) - trkinfo[trk].freq = psg->clk/32.0/psg->freq[trk]; - else - trkinfo[trk].freq = 0; - - trkinfo[trk].output = psg->cout[trk]; - trkinfo[trk].max_volume = 15; - trkinfo[trk].volume = psg->volume[trk] >> 1; - //trkinfo[trk].key = (psg->cout[trk]>0)?true:false; - trkinfo[trk].key = !(psg->tmask[trk]); - trkinfo[trk].tone = (psg->tmask[trk]?2:0)+(psg->nmask[trk]?1:0); - } - else if (trk == 3) // envelope - { - trkinfo[trk]._freq = psg->env_freq; - if(psg->env_freq) - trkinfo[trk].freq = psg->clk/512.0/psg->env_freq; - else - trkinfo[trk].freq = 0; - - if (psg->env_continue && psg->env_alternate && !psg->env_hold) // triangle wave - { - trkinfo[trk].freq *= 0.5f; // sounds an octave down - } - - trkinfo[trk].output = psg->voltbl[psg->env_ptr]; - trkinfo[trk].max_volume = 0; - trkinfo[trk].volume = 0; - trkinfo[trk].key = (((psg->volume[0]|psg->volume[1]|psg->volume[2])&32) != 0); - trkinfo[trk].tone = - (psg->env_continue ?8:0) | - (psg->env_attack ?4:0) | - (psg->env_alternate?2:0) | - (psg->env_hold ?1:0) ; - } - else if (trk == 4) // noise - { - trkinfo[trk]._freq = psg->noise_freq >> 1; - if(trkinfo[trk]._freq > 0) - trkinfo[trk].freq = psg->clk/16.0/psg->noise_freq; - else - trkinfo[trk].freq = 0; - - trkinfo[trk].output = psg->noise_seed & 1; - trkinfo[trk].max_volume = 0; - trkinfo[trk].volume = 0; - //trkinfo[trk].key = ((psg->nmask[0]&psg->nmask[1]&psg->nmask[2]) == 0); - trkinfo[trk].key = false; - trkinfo[trk].tone = 0; - } - } - return &trkinfo[trk]; -} diff --git a/extern/NSFplay/nes_fme7.h b/extern/NSFplay/nes_fme7.h deleted file mode 100644 index d351b0859..000000000 --- a/extern/NSFplay/nes_fme7.h +++ /dev/null @@ -1,42 +0,0 @@ -#ifndef _NES_FME7_H_ -#define _NES_FME7_H_ -#include "../device.h" -#include "legacy/emu2149.h" - -namespace xgm -{ - - class NES_FME7:public ISoundChip - { - public: - enum - { - OPT_END - }; - protected: - //int option[OPT_END]; - INT32 sm[2][3]; // stereo mix - INT16 buf[2]; - PSG *psg; - int divider; // clock divider - double clock, rate; - TrackInfoBasic trkinfo[5]; - public: - NES_FME7 (); - ~NES_FME7 (); - virtual void Reset (); - virtual void Tick (UINT32 clocks); - virtual UINT32 Render (INT32 b[2]); - virtual bool Read (UINT32 adr, UINT32 & val, UINT32 id=0); - virtual bool Write (UINT32 adr, UINT32 val, UINT32 id=0); - virtual void SetClock (double); - virtual void SetRate (double); - virtual void SetOption (int, int); - virtual void SetMask (int m){ if(psg) PSG_setMask(psg,m); } - virtual void SetStereoMix (int trk, xgm::INT16 mixl, xgm::INT16 mixr); - virtual ITrackInfo *GetTrackInfo(int trk); - }; - -} // namespace - -#endif diff --git a/extern/NSFplay/nes_mmc5.cpp b/extern/NSFplay/nes_mmc5.cpp index 2b2f8fcb7..9243f6595 100644 --- a/extern/NSFplay/nes_mmc5.cpp +++ b/extern/NSFplay/nes_mmc5.cpp @@ -35,7 +35,7 @@ namespace xgm sm[c][t] = 128; } - NES_MMC5::~NES_MMC5 () + NES_MMC5::‾NES_MMC5 () { } diff --git a/extern/NSFplay/nes_mmc5.h b/extern/NSFplay/nes_mmc5.h index b05fbb506..5747ab0d6 100644 --- a/extern/NSFplay/nes_mmc5.h +++ b/extern/NSFplay/nes_mmc5.h @@ -31,8 +31,8 @@ namespace xgm INT32 out[3]; bool enable[2]; - bool envelope_disable[2]; // ƒGƒ“ƒxƒ[ƒv—LŒøƒtƒ‰ƒO - bool envelope_loop[2]; // ƒGƒ“ƒxƒ[ƒvƒ‹[ƒv + bool envelope_disable[2]; // エンベロープ有効フラグ + bool envelope_loop[2]; // エンベロープループ bool envelope_write[2]; int envelope_div_period[2]; int envelope_div[2]; @@ -49,7 +49,7 @@ namespace xgm TrackInfoBasic trkinfo[3]; public: NES_MMC5 (); - ~NES_MMC5 (); + ‾NES_MMC5 (); void FrameSequence (); void TickFrameSequence (UINT32 clocks); diff --git a/extern/NSFplay/nes_n106.cpp b/extern/NSFplay/nes_n106.cpp index f53d4ae7c..ae793dc08 100644 --- a/extern/NSFplay/nes_n106.cpp +++ b/extern/NSFplay/nes_n106.cpp @@ -18,7 +18,7 @@ NES_N106::NES_N106 () Reset(); } -NES_N106::~NES_N106 () +NES_N106::‾NES_N106 () { } diff --git a/extern/NSFplay/nes_n106.h b/extern/NSFplay/nes_n106.h index 82d7eaa6a..aebb6cb46 100644 --- a/extern/NSFplay/nes_n106.h +++ b/extern/NSFplay/nes_n106.h @@ -54,7 +54,7 @@ protected: public: NES_N106 (); - ~NES_N106 (); + ‾NES_N106 (); virtual void Reset (); virtual void Tick (UINT32 clocks); diff --git a/extern/NSFplay/nes_vrc6.cpp b/extern/NSFplay/nes_vrc6.cpp index 6a19b5ea5..c46109403 100644 --- a/extern/NSFplay/nes_vrc6.cpp +++ b/extern/NSFplay/nes_vrc6.cpp @@ -16,7 +16,7 @@ namespace xgm sm[c][t] = 128; } - NES_VRC6::~NES_VRC6 () + NES_VRC6::‾NES_VRC6 () { } diff --git a/extern/NSFplay/nes_vrc6.h b/extern/NSFplay/nes_vrc6.h index 2e277e6ee..2bb64eb8a 100644 --- a/extern/NSFplay/nes_vrc6.h +++ b/extern/NSFplay/nes_vrc6.h @@ -36,7 +36,7 @@ namespace xgm public: NES_VRC6 (); - ~NES_VRC6 (); + ‾NES_VRC6 (); virtual void Reset (); virtual void Tick (UINT32 clocks); diff --git a/extern/NSFplay/nes_vrc7.cpp b/extern/NSFplay/nes_vrc7.cpp deleted file mode 100644 index 4123fb36b..000000000 --- a/extern/NSFplay/nes_vrc7.cpp +++ /dev/null @@ -1,189 +0,0 @@ -#include -#include "nes_vrc7.h" - -namespace xgm -{ - NES_VRC7::NES_VRC7 () - { - use_all_channels = false; - patch_set = OPLL_VRC7_TONE; - patch_custom = NULL; - divider = 0; - - opll = OPLL_new ( 3579545, DEFAULT_RATE); - OPLL_reset_patch (opll, patch_set); - SetClock(DEFAULT_CLOCK); - - for(int c=0;c<2;++c) - //for(int t=0;t<6;++t) - for(int t=0;t<9;++t) // HACK for YM2413 support - sm[c][t] = 128; - } - - NES_VRC7::~NES_VRC7 () - { - OPLL_delete (opll); - } - - void NES_VRC7::UseAllChannels(bool b) - { - use_all_channels = b; - } - - void NES_VRC7::SetPatchSet(int p) - { - patch_set = p; - } - - void NES_VRC7::SetPatchSetCustom (const UINT8* pset) - { - patch_custom = pset; - } - - void NES_VRC7::SetClock (double c) - { - clock = c / 36; - } - - void NES_VRC7::SetRate (double r) - { - //rate = r ? r : DEFAULT_RATE; - (void)r; // rate is ignored - rate = 49716; - OPLL_set_quality(opll, 1); // quality always on (not really a CPU hog) - OPLL_set_rate(opll,(uint32_t)rate); - } - - void NES_VRC7::SetOption (int id, int val) - { - if(id 5) return; - if (trk > 8) return; // HACK YM2413 - sm[0][trk] = mixl; - sm[1][trk] = mixr; - } - - ITrackInfo *NES_VRC7::GetTrackInfo(int trk) - { - //if(opll&&trk<6) - if(opll&&trk<9) // HACK YM2413 (percussion mode isn't very diagnostic this way though) - { - trkinfo[trk].max_volume = 15; - trkinfo[trk].volume = 15 - ((opll->reg[0x30+trk])&15); - trkinfo[trk]._freq = opll->reg[0x10+trk]+((opll->reg[0x20+trk]&1)<<8); - int blk = (opll->reg[0x20+trk]>>1)&7; - trkinfo[trk].freq = clock*trkinfo[trk]._freq/(double)(0x80000>>blk); - trkinfo[trk].tone = (opll->reg[0x30+trk]>>4)&15; - //trkinfo[trk].key = (opll->reg[0x20+trk]&0x10)?true:false; - trkinfo[trk].key = opll->slot_key_status & (3 << trk)?true:false; - return &trkinfo[trk]; - } - else - return NULL; - } - - bool NES_VRC7::Write (UINT32 adr, UINT32 val, UINT32 id) - { - if (adr == 0x9010) - { - OPLL_writeIO (opll, 0, val); - return true; - } - if (adr == 0x9030) - { - OPLL_writeIO (opll, 1, val); - return true; - } - else - return false; - } - - bool NES_VRC7::Read (UINT32 adr, UINT32 & val, UINT32 id) - { - return false; - } - - void NES_VRC7::Tick (UINT32 clocks) - { - divider += clocks; - while (divider >= 36) - { - divider -= 36; - OPLL_calc(opll); - } - } - - UINT32 NES_VRC7::Render (INT32 b[2]) - { - b[0] = b[1] = 0; - for (int i=0; i < 6; ++i) - { - INT32 val = (mask & (1<ch_out[i] >> 4; - b[0] += val * sm[0][i]; - b[1] += val * sm[1][i]; - } - - // HACK for YM2413 support - if (use_all_channels) - { - for (int i=6; i < 9; ++i) - { - if (mask & (1<patch_number[i] > 15) // rhytm mode - { - if (i == 6) val = opll->ch_out[9]; // BD - else if (i == 7) val = opll->ch_out[10] + opll->ch_out[11]; // HH&SD - else val = opll->ch_out[12] + opll->ch_out[13]; // TOM&CYM - /* (i == 8) is implied */ - } - else - { - val = opll->ch_out[i]; - } - val >>= 4; - b[0] += val * sm[0][i]; - b[1] += val * sm[1][i]; - } - } - - b[0] >>= (7 - 4); - b[1] >>= (7 - 4); - - // master volume adjustment - const INT32 MASTER = INT32(1.15 * 256.0); - b[0] = (b[0] * MASTER) >> 8; - b[1] = (b[1] * MASTER) >> 8; - - return 2; - } -} diff --git a/extern/NSFplay/nes_vrc7.h b/extern/NSFplay/nes_vrc7.h deleted file mode 100644 index e8a91d61d..000000000 --- a/extern/NSFplay/nes_vrc7.h +++ /dev/null @@ -1,53 +0,0 @@ -#ifndef _NES_VRC7_H_ -#define _NES_VRC7_H_ -#include "../device.h" -#include "legacy/emu2413.h" - -namespace xgm -{ - - class NES_VRC7 : public ISoundChip - { - public: - enum - { - OPT_OPLL=0, - OPT_END - }; - protected: - int option[OPT_END]; - int mask; - int patch_set; - const UINT8* patch_custom; - //INT32 sm[2][6]; // stereo mix - INT32 sm[2][9]; // stereo mix temporary HACK to support YM2413 - INT16 buf[2]; - OPLL *opll; - UINT32 divider; // clock divider - double clock, rate; - //TrackInfoBasic trkinfo[6]; - TrackInfoBasic trkinfo[9]; // HACK to support YM2413 - bool use_all_channels; - public: - NES_VRC7 (); - ~NES_VRC7 (); - - virtual void Reset (); - virtual void Tick (UINT32 clocks); - virtual UINT32 Render (INT32 b[2]); - virtual bool Read (UINT32 adr, UINT32 & val, UINT32 id=0); - virtual bool Write (UINT32 adr, UINT32 val, UINT32 id=0); - virtual void UseAllChannels (bool b); - virtual void SetPatchSet (int p); - virtual void SetPatchSetCustom (const UINT8* pset); - virtual void SetClock (double); - virtual void SetRate (double); - virtual void SetOption (int, int); - virtual void SetMask (int m){ mask = m; if(opll) OPLL_setMask(opll, m); } - virtual void SetStereoMix (int trk, xgm::INT16 mixl, xgm::INT16 mixr); - virtual ITrackInfo *GetTrackInfo(int trk); - }; - -} // namespace - -#endif From 867f96ff0166de4dec4fa4b22053aaa305978cb5 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 1 May 2022 22:19:00 -0500 Subject: [PATCH 276/342] add readme --- extern/NSFplay/readme.txt | 60 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 extern/NSFplay/readme.txt diff --git a/extern/NSFplay/readme.txt b/extern/NSFplay/readme.txt new file mode 100644 index 000000000..f3a625d90 --- /dev/null +++ b/extern/NSFplay/readme.txt @@ -0,0 +1,60 @@ +MODIFIED + +this is a modified version of the NES audio emulation core. +it converts the files to UTF-8 and Unix line endings. + +XGM SOURCE ARCHIVE + +This source archive is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY. You can reuse these source code freely. However, +we possibly change the structure and interface of the program code without +any advance notice. + +HOW TO COMPILE + +Open the workspace file top.sln with Visual C++ 7.0 or later +version. We can compile both KbMediaPlayer and Winamp version of +NSFplug on this workspace. + +To make a KbMediaPlayer version of NSFplug, that is, in_nsf.kpi, +Please choose 'kbnsf' project as an active project. Then, you can +build in_nsf.kpi by build menu. + +On the other hand, to make a Winamp version of NSFplug, activate +'wa2nsf' project and build it. + +Note that after the build process, VC++ copies the plugin files to: + +C:\Program Files\KbMediaPlayer\Plugins\OK\in_nsf\in_nsf.kpi +C:\Program Files\Windamp\Plugins\in_nsf.dll + +If you don't need to have these copies, please remove or modify the + custom build settings. + +ACKNOWLEDGEMENT + +I thank Mamiya and Kobarin and Nullsoft for their great source code. +I thank Norix and Izumi for the fruitful discussions and the NSFplug +users for their comments and bug reports. + +COPYRIGHTS + +NSFplug is built on KM6502, KbMediaPlayer plugin SDK and Winamp2 +plugin SDK. + +NSFplug uses KM6502 in emulating a 6502 cpu. KM6502 code is stored in +devices\CPU\km6502 folder of this source archive. KM6502 is a public +domain software. See the document of KM6502 stored in the folder. + +KbMediaPlayer Plugin SDK is provided by Kobarin. The SDK code is also +packed in the kbmedia\sdk folder of this archive. The copyright of +the source code remains with Kobarin. + +The files in winamp/sdk folder of this archive are the header files +from Winamp2 Plugin SDK provided by Nullsoft. The copyright of these +header files remains with Nullsoft. + +CONTACT + +Digital Sound Antiques +http://dsa.sakura.ne.jp/ From e873070d8444efd6edfab230edfa1f509013e6a6 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 1 May 2022 22:22:02 -0500 Subject: [PATCH 277/342] now move these files --- .../NSFplay => src/engine/platform/sound/nes_nsfplay}/nes_apu.cpp | 0 .../NSFplay => src/engine/platform/sound/nes_nsfplay}/nes_apu.h | 0 .../NSFplay => src/engine/platform/sound/nes_nsfplay}/nes_dmc.cpp | 0 .../NSFplay => src/engine/platform/sound/nes_nsfplay}/nes_dmc.h | 0 .../NSFplay => src/engine/platform/sound/nes_nsfplay}/nes_fds.cpp | 0 .../NSFplay => src/engine/platform/sound/nes_nsfplay}/nes_fds.h | 0 .../engine/platform/sound/nes_nsfplay}/nes_mmc5.cpp | 0 .../NSFplay => src/engine/platform/sound/nes_nsfplay}/nes_mmc5.h | 0 .../engine/platform/sound/nes_nsfplay}/nes_n106.cpp | 0 .../NSFplay => src/engine/platform/sound/nes_nsfplay}/nes_n106.h | 0 .../engine/platform/sound/nes_nsfplay}/nes_vrc6.cpp | 0 .../NSFplay => src/engine/platform/sound/nes_nsfplay}/nes_vrc6.h | 0 .../NSFplay => src/engine/platform/sound/nes_nsfplay}/readme.txt | 0 13 files changed, 0 insertions(+), 0 deletions(-) rename {extern/NSFplay => src/engine/platform/sound/nes_nsfplay}/nes_apu.cpp (100%) rename {extern/NSFplay => src/engine/platform/sound/nes_nsfplay}/nes_apu.h (100%) rename {extern/NSFplay => src/engine/platform/sound/nes_nsfplay}/nes_dmc.cpp (100%) rename {extern/NSFplay => src/engine/platform/sound/nes_nsfplay}/nes_dmc.h (100%) rename {extern/NSFplay => src/engine/platform/sound/nes_nsfplay}/nes_fds.cpp (100%) rename {extern/NSFplay => src/engine/platform/sound/nes_nsfplay}/nes_fds.h (100%) rename {extern/NSFplay => src/engine/platform/sound/nes_nsfplay}/nes_mmc5.cpp (100%) rename {extern/NSFplay => src/engine/platform/sound/nes_nsfplay}/nes_mmc5.h (100%) rename {extern/NSFplay => src/engine/platform/sound/nes_nsfplay}/nes_n106.cpp (100%) rename {extern/NSFplay => src/engine/platform/sound/nes_nsfplay}/nes_n106.h (100%) rename {extern/NSFplay => src/engine/platform/sound/nes_nsfplay}/nes_vrc6.cpp (100%) rename {extern/NSFplay => src/engine/platform/sound/nes_nsfplay}/nes_vrc6.h (100%) rename {extern/NSFplay => src/engine/platform/sound/nes_nsfplay}/readme.txt (100%) diff --git a/extern/NSFplay/nes_apu.cpp b/src/engine/platform/sound/nes_nsfplay/nes_apu.cpp similarity index 100% rename from extern/NSFplay/nes_apu.cpp rename to src/engine/platform/sound/nes_nsfplay/nes_apu.cpp diff --git a/extern/NSFplay/nes_apu.h b/src/engine/platform/sound/nes_nsfplay/nes_apu.h similarity index 100% rename from extern/NSFplay/nes_apu.h rename to src/engine/platform/sound/nes_nsfplay/nes_apu.h diff --git a/extern/NSFplay/nes_dmc.cpp b/src/engine/platform/sound/nes_nsfplay/nes_dmc.cpp similarity index 100% rename from extern/NSFplay/nes_dmc.cpp rename to src/engine/platform/sound/nes_nsfplay/nes_dmc.cpp diff --git a/extern/NSFplay/nes_dmc.h b/src/engine/platform/sound/nes_nsfplay/nes_dmc.h similarity index 100% rename from extern/NSFplay/nes_dmc.h rename to src/engine/platform/sound/nes_nsfplay/nes_dmc.h diff --git a/extern/NSFplay/nes_fds.cpp b/src/engine/platform/sound/nes_nsfplay/nes_fds.cpp similarity index 100% rename from extern/NSFplay/nes_fds.cpp rename to src/engine/platform/sound/nes_nsfplay/nes_fds.cpp diff --git a/extern/NSFplay/nes_fds.h b/src/engine/platform/sound/nes_nsfplay/nes_fds.h similarity index 100% rename from extern/NSFplay/nes_fds.h rename to src/engine/platform/sound/nes_nsfplay/nes_fds.h diff --git a/extern/NSFplay/nes_mmc5.cpp b/src/engine/platform/sound/nes_nsfplay/nes_mmc5.cpp similarity index 100% rename from extern/NSFplay/nes_mmc5.cpp rename to src/engine/platform/sound/nes_nsfplay/nes_mmc5.cpp diff --git a/extern/NSFplay/nes_mmc5.h b/src/engine/platform/sound/nes_nsfplay/nes_mmc5.h similarity index 100% rename from extern/NSFplay/nes_mmc5.h rename to src/engine/platform/sound/nes_nsfplay/nes_mmc5.h diff --git a/extern/NSFplay/nes_n106.cpp b/src/engine/platform/sound/nes_nsfplay/nes_n106.cpp similarity index 100% rename from extern/NSFplay/nes_n106.cpp rename to src/engine/platform/sound/nes_nsfplay/nes_n106.cpp diff --git a/extern/NSFplay/nes_n106.h b/src/engine/platform/sound/nes_nsfplay/nes_n106.h similarity index 100% rename from extern/NSFplay/nes_n106.h rename to src/engine/platform/sound/nes_nsfplay/nes_n106.h diff --git a/extern/NSFplay/nes_vrc6.cpp b/src/engine/platform/sound/nes_nsfplay/nes_vrc6.cpp similarity index 100% rename from extern/NSFplay/nes_vrc6.cpp rename to src/engine/platform/sound/nes_nsfplay/nes_vrc6.cpp diff --git a/extern/NSFplay/nes_vrc6.h b/src/engine/platform/sound/nes_nsfplay/nes_vrc6.h similarity index 100% rename from extern/NSFplay/nes_vrc6.h rename to src/engine/platform/sound/nes_nsfplay/nes_vrc6.h diff --git a/extern/NSFplay/readme.txt b/src/engine/platform/sound/nes_nsfplay/readme.txt similarity index 100% rename from extern/NSFplay/readme.txt rename to src/engine/platform/sound/nes_nsfplay/readme.txt From 7b31f6a3e57b3b2aeedc55d23986a9d90559d618 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 1 May 2022 22:52:22 -0500 Subject: [PATCH 278/342] now fix it damn it --- CMakeLists.txt | 7 + .../platform/sound/nes_nsfplay/common.h | 4 + .../platform/sound/nes_nsfplay/nes_apu.cpp | 50 +++---- .../platform/sound/nes_nsfplay/nes_apu.h | 33 +++-- .../platform/sound/nes_nsfplay/nes_dmc.cpp | 124 +++++------------- .../platform/sound/nes_nsfplay/nes_dmc.h | 78 +++++------ .../platform/sound/nes_nsfplay/nes_fds.cpp | 64 ++++----- .../platform/sound/nes_nsfplay/nes_fds.h | 58 ++++---- .../platform/sound/nes_nsfplay/nes_mmc5.cpp | 88 +++---------- .../platform/sound/nes_nsfplay/nes_mmc5.h | 51 ++++--- .../platform/sound/nes_nsfplay/nes_n106.cpp | 81 ++++-------- .../platform/sound/nes_nsfplay/nes_n106.h | 44 +++---- .../platform/sound/nes_nsfplay/nes_vrc6.cpp | 49 ++----- .../platform/sound/nes_nsfplay/nes_vrc6.h | 33 +++-- 14 files changed, 268 insertions(+), 496 deletions(-) create mode 100644 src/engine/platform/sound/nes_nsfplay/common.h diff --git a/CMakeLists.txt b/CMakeLists.txt index b757062f2..a881bad60 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -250,6 +250,13 @@ src/engine/platform/sound/nes/mmc5.c src/engine/platform/sound/vera_psg.c src/engine/platform/sound/vera_pcm.c +src/engine/platform/sound/nes_nsfplay/nes_apu.cpp +src/engine/platform/sound/nes_nsfplay/nes_dmc.cpp +src/engine/platform/sound/nes_nsfplay/nes_fds.cpp +src/engine/platform/sound/nes_nsfplay/nes_mmc5.cpp +src/engine/platform/sound/nes_nsfplay/nes_n106.cpp +src/engine/platform/sound/nes_nsfplay/nes_vrc6.cpp + src/engine/platform/sound/c64/sid.cc src/engine/platform/sound/c64/voice.cc src/engine/platform/sound/c64/wave.cc diff --git a/src/engine/platform/sound/nes_nsfplay/common.h b/src/engine/platform/sound/nes_nsfplay/common.h new file mode 100644 index 000000000..894a69737 --- /dev/null +++ b/src/engine/platform/sound/nes_nsfplay/common.h @@ -0,0 +1,4 @@ +namespace xgm { + const unsigned int DEFAULT_CLOCK=1789773; + const unsigned int DEFAULT_RATE=1789773; +}; diff --git a/src/engine/platform/sound/nes_nsfplay/nes_apu.cpp b/src/engine/platform/sound/nes_nsfplay/nes_apu.cpp index b2ebb677a..f215a9aa6 100644 --- a/src/engine/platform/sound/nes_nsfplay/nes_apu.cpp +++ b/src/engine/platform/sound/nes_nsfplay/nes_apu.cpp @@ -3,6 +3,7 @@ // #include #include "nes_apu.h" +#include "common.h" namespace xgm { @@ -86,9 +87,9 @@ namespace xgm } - INT32 NES_APU::calc_sqr (int i, UINT32 clocks) + int NES_APU::calc_sqr (int i, unsigned int clocks) { - static const INT16 sqrtbl[4][16] = { + static const short sqrtbl[4][16] = { {0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0}, @@ -102,7 +103,7 @@ namespace xgm scounter[i] += freq[i] + 1; } - INT32 ret = 0; + int ret = 0; if (length_counter[i] > 0 && freq[i] >= 8 && sfreq[i] < 0x800 @@ -115,7 +116,7 @@ namespace xgm return ret; } - bool NES_APU::Read (UINT32 adr, UINT32 & val, UINT32 id) + bool NES_APU::Read (unsigned int adr, unsigned int & val, unsigned int id) { if (0x4000 <= adr && adr < 0x4008) { @@ -131,26 +132,26 @@ namespace xgm return false; } - void NES_APU::Tick (UINT32 clocks) + void NES_APU::Tick (unsigned int clocks) { out[0] = calc_sqr(0, clocks); out[1] = calc_sqr(1, clocks); } // ツé’カツé’ャツã¤ï½³ï¾‚ã¥ï½ªï¾‚ã¥ï½©ï¾‚波ツ形ツã¥å€‹æŒ¯ï¾‚閉敖ã¥0-8191 - UINT32 NES_APU::Render (INT32 b[2]) + unsigned int NES_APU::Render (int b[2]) { out[0] = (mask & 1) ? 0 : out[0]; out[1] = (mask & 2) ? 0 : out[1]; - INT32 m[2]; + int m[2]; if(option[OPT_NONLINEAR_MIXER]) { - INT32 voltage = square_table[out[0] + out[1]]; + int voltage = square_table[out[0] + out[1]]; m[0] = out[0] << 6; m[1] = out[1] << 6; - INT32 ref = m[0] + m[1]; + int ref = m[0] + m[1]; if (ref > 0) { m[0] = (m[0] * voltage) / ref; @@ -191,7 +192,7 @@ namespace xgm square_table[0] = 0; for(int i=1;i<32;i++) - square_table[i]=(INT32)((8192.0*95.88)/(8128.0/i+100)); + square_table[i]=(int)((8192.0*95.88)/(8128.0/i+100)); square_linear = square_table[15]; // match linear scale to one full volume square of nonlinear @@ -200,7 +201,7 @@ namespace xgm sm[c][t] = 128; } - NES_APU::‾NES_APU () + NES_APU::~NES_APU () { } @@ -267,7 +268,7 @@ namespace xgm rate = r ? r : DEFAULT_RATE; } - void NES_APU::SetStereoMix(int trk, xgm::INT16 mixl, xgm::INT16 mixr) + void NES_APU::SetStereoMix(int trk, short mixl, short mixr) { if (trk < 0) return; if (trk > 1) return; @@ -275,32 +276,11 @@ namespace xgm sm[1][trk] = mixr; } - ITrackInfo *NES_APU::GetTrackInfo(int trk) - { - trkinfo[trk]._freq = freq[trk]; - if(freq[trk]) - trkinfo[trk].freq = clock/16/(freq[trk] + 1); - else - trkinfo[trk].freq = 0; - - trkinfo[trk].output = out[trk]; - trkinfo[trk].volume = volume[trk]+(envelope_disable[trk]?0:0x10)+(envelope_loop[trk]?0x20:0); - trkinfo[trk].key = - enable[trk] && - length_counter[trk] > 0 && - freq[trk] >= 8 && - sfreq[trk] < 0x800 && - (envelope_disable[trk] ? volume[trk] : (envelope_counter[trk] > 0)); - trkinfo[trk].tone = duty[trk]; - trkinfo[trk].max_volume = 15; - return &trkinfo[trk]; - } - - bool NES_APU::Write (UINT32 adr, UINT32 val, UINT32 id) + bool NES_APU::Write (unsigned int adr, unsigned int val, unsigned int id) { int ch; - static const UINT8 length_table[32] = { + static const unsigned char length_table[32] = { 0x0A, 0xFE, 0x14, 0x02, 0x28, 0x04, diff --git a/src/engine/platform/sound/nes_nsfplay/nes_apu.h b/src/engine/platform/sound/nes_nsfplay/nes_apu.h index a6caa282b..004c99974 100644 --- a/src/engine/platform/sound/nes_nsfplay/nes_apu.h +++ b/src/engine/platform/sound/nes_nsfplay/nes_apu.h @@ -1,12 +1,11 @@ #ifndef _NES_APU_H_ #define _NES_APU_H_ -#include "../device.h" #include "nes_dmc.h" namespace xgm { /** Upper half of APU **/ - class NES_APU : public ISoundChip + class NES_APU { public: enum @@ -24,15 +23,15 @@ namespace xgm protected: int option[OPT_END]; // å„種オプション int mask; - INT32 sm[2][2]; + int sm[2][2]; - UINT32 gclock; - UINT8 reg[0x20]; - INT32 out[2]; + unsigned int gclock; + unsigned char reg[0x20]; + int out[2]; double rate, clock; - INT32 square_table[32]; // nonlinear mixer - INT32 square_linear; // linear mix approximation + int square_table[32]; // nonlinear mixer + int square_linear; // linear mix approximation int scounter[2]; // frequency divider int sphase[2]; // phase counter @@ -61,26 +60,24 @@ namespace xgm bool enable[2]; void sweep_sqr (int ch); // calculates target sweep frequency - INT32 calc_sqr (int ch, UINT32 clocks); - TrackInfoBasic trkinfo[2]; + int calc_sqr (int ch, unsigned int clocks); public: - NES_APU (); - ‾NES_APU (); + NES_APU (); + ~NES_APU (); void FrameSequence(int s); virtual void Reset (); - virtual void Tick (UINT32 clocks); - virtual UINT32 Render (INT32 b[2]); - virtual bool Read (UINT32 adr, UINT32 & val, UINT32 id=0); - virtual bool Write (UINT32 adr, UINT32 val, UINT32 id=0); + virtual void Tick (unsigned int clocks); + virtual unsigned int Render (int b[2]); + virtual bool Read (unsigned int adr, unsigned int & val, unsigned int id=0); + virtual bool Write (unsigned int adr, unsigned int val, unsigned int id=0); virtual void SetRate (double rate); virtual void SetClock (double clock); virtual void SetOption (int id, int b); virtual void SetMask(int m){ mask = m; } - virtual void SetStereoMix (int trk, xgm::INT16 mixl, xgm::INT16 mixr); - virtual ITrackInfo *GetTrackInfo(int trk); + virtual void SetStereoMix (int trk, short mixl, short mixr); }; } // namespace diff --git a/src/engine/platform/sound/nes_nsfplay/nes_dmc.cpp b/src/engine/platform/sound/nes_nsfplay/nes_dmc.cpp index 49a58eb5e..a37b544d2 100644 --- a/src/engine/platform/sound/nes_nsfplay/nes_dmc.cpp +++ b/src/engine/platform/sound/nes_nsfplay/nes_dmc.cpp @@ -1,10 +1,12 @@ #include "nes_dmc.h" #include "nes_apu.h" +#include "common.h" +#include #include namespace xgm { - const UINT32 NES_DMC::wavlen_table[2][16] = { + const unsigned int NES_DMC::wavlen_table[2][16] = { { // NTSC 4, 8, 16, 32, 64, 96, 128, 160, 202, 254, 380, 508, 762, 1016, 2034, 4068 }, @@ -12,7 +14,7 @@ namespace xgm 4, 8, 14, 30, 60, 88, 118, 148, 188, 236, 354, 472, 708, 944, 1890, 3778 }}; - const UINT32 NES_DMC::freq_table[2][16] = { + const unsigned int NES_DMC::freq_table[2][16] = { { // NTSC 428, 380, 340, 320, 286, 254, 226, 214, 190, 160, 142, 128, 106, 84, 72, 54 }, @@ -20,7 +22,7 @@ namespace xgm 398, 354, 316, 298, 276, 236, 210, 198, 176, 148, 132, 118, 98, 78, 66, 50 }}; - const UINT32 BITREVERSE[256] = { + const unsigned int BITREVERSE[256] = { 0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0, 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8, 0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4, @@ -67,11 +69,11 @@ namespace xgm } - NES_DMC::‾NES_DMC () + NES_DMC::~NES_DMC () { } - void NES_DMC::SetStereoMix(int trk, xgm::INT16 mixl, xgm::INT16 mixr) + void NES_DMC::SetStereoMix(int trk, short mixl, short mixr) { if (trk < 0) return; if (trk > 2) return; @@ -79,47 +81,6 @@ namespace xgm sm[1][trk] = mixr; } - ITrackInfo *NES_DMC::GetTrackInfo(int trk) - { - switch(trk) - { - case 0: - trkinfo[trk].max_volume = 255; - trkinfo[0].key = (linear_counter>0 && length_counter[0]>0 && enable[0]); - trkinfo[0].volume = 0; - trkinfo[0]._freq = tri_freq; - if(trkinfo[0]._freq) - trkinfo[0].freq = clock/32/(trkinfo[0]._freq + 1); - else - trkinfo[0].freq = 0; - trkinfo[0].tone = -1; - trkinfo[0].output = out[0]; - break; - case 1: - trkinfo[1].max_volume = 15; - trkinfo[1].volume = noise_volume+(envelope_disable?0:0x10)+(envelope_loop?0x20:0); - trkinfo[1].key = length_counter[1]>0 && enable[1] && - (envelope_disable ? (noise_volume>0) : (envelope_counter>0)); - trkinfo[1]._freq = reg[0x400e - 0x4008]&0xF; - trkinfo[1].freq = clock/double(wavlen_table[pal][trkinfo[1]._freq] * ((noise_tap&(1<<6)) ? 93 : 1)); - trkinfo[1].tone = noise_tap & (1<<6); - trkinfo[1].output = out[1]; - break; - case 2: - trkinfo[2].max_volume = 127; - trkinfo[2].volume = reg[0x4011 - 0x4008]&0x7F; - trkinfo[2].key = dlength > 0; - trkinfo[2]._freq = reg[0x4010 - 0x4008]&0xF; - trkinfo[2].freq = clock/double(freq_table[pal][trkinfo[2]._freq]); - trkinfo[2].tone = (0xc000|(adr_reg<<6)); - trkinfo[2].output = (damp<<1)|dac_lsb; - break; - default: - return NULL; - } - return &trkinfo[trk]; - } - void NES_DMC::FrameSequence(int s) { //DEBUG_OUT("FrameSequence: %dÂ¥n",s); @@ -134,7 +95,6 @@ namespace xgm if (s == 0 && (frame_sequence_steps == 4)) { if (frame_irq_enable) frame_irq = true; - cpu->UpdateIRQ(NES_CPU::IRQD_FRAME, frame_irq & frame_irq_enable); } // 240hz clock @@ -194,9 +154,9 @@ namespace xgm } // 三角波ãƒãƒ£ãƒ³ãƒãƒ«ã®è¨ˆç®— 戻り値ã¯0-15 - UINT32 NES_DMC::calc_tri (UINT32 clocks) + unsigned int NES_DMC::calc_tri (unsigned int clocks) { - static UINT32 tritbl[32] = + static unsigned int tritbl[32] = { 15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, @@ -215,7 +175,7 @@ namespace xgm } } - UINT32 ret = tritbl[tphase]; + unsigned int ret = tritbl[tphase]; return ret; } @@ -223,20 +183,20 @@ namespace xgm // 低サンプリングレートã§åˆæˆã™ã‚‹ã¨ã‚¨ã‚¤ãƒªã‚¢ã‚¹ãƒŽã‚¤ã‚ºãŒæ¿€ã—ã„ã®ã§ // ノイズã ã‘ã¯ã“ã®é–¢æ•°å†…ã§é«˜ã‚¯ãƒ­ãƒƒã‚¯åˆæˆã—ã€ç°¡æ˜“ãªã‚µãƒ³ãƒ—リングレート // 変æ›ã‚’行ã£ã¦ã„る。 - UINT32 NES_DMC::calc_noise(UINT32 clocks) + unsigned int NES_DMC::calc_noise(unsigned int clocks) { - UINT32 env = envelope_disable ? noise_volume : envelope_counter; + unsigned int env = envelope_disable ? noise_volume : envelope_counter; if (length_counter[1] < 1) env = 0; - UINT32 last = (noise & 0x4000) ? 0 : env; + unsigned int last = (noise & 0x4000) ? 0 : env; if (clocks < 1) return last; // simple anti-aliasing (noise requires it, even when oversampling is off) - UINT32 count = 0; - UINT32 accum = counter[1] * last; // samples pending from previous calc - UINT32 accum_clocks = counter[1]; + unsigned int count = 0; + unsigned int accum = counter[1] * last; // samples pending from previous calc + unsigned int accum_clocks = counter[1]; #ifdef _DEBUG - INT32 start_clocks = counter[1]; + int start_clocks = counter[1]; #endif if (counter[1] < 0) // only happens on startup when using the randomize noise option { @@ -249,7 +209,7 @@ namespace xgm while (counter[1] < 0) { // tick the noise generator - UINT32 feedback = (noise&1) ^ ((noise&noise_tap)?1:0); + unsigned int feedback = (noise&1) ^ ((noise&noise_tap)?1:0); noise = (noise>>1) | (feedback<<14); last = (noise & 0x4000) ? 0 : env; @@ -270,13 +230,13 @@ namespace xgm if (start_clocks >= 0) assert(accum_clocks == clocks); // these should be equal #endif - UINT32 average = accum / accum_clocks; + unsigned int average = accum / accum_clocks; assert(average <= 15); // above this would indicate overflow return average; } // Tick the DMC for the number of clocks, and return output counter; - UINT32 NES_DMC::calc_dmc (UINT32 clocks) + unsigned int NES_DMC::calc_dmc (unsigned int clocks) { counter[2] -= clocks; assert (dfreq > 0); // prevent infinite loop @@ -300,8 +260,7 @@ namespace xgm { if (dlength > 0) { - memory->Read (daddress, data); - cpu->StealCycles(4); // DMC read takes 3 or 4 CPU cycles, usually 4 + memory (daddress, data); // (checking for the 3-cycle case would require sub-instruction emulation) data &= 0xFF; // read 8 bits if (option[OPT_DPCM_REVERSE]) data = BITREVERSE[data]; @@ -319,7 +278,6 @@ namespace xgm else if (mode & 2) // IRQ and not looped { irq = true; - cpu->UpdateIRQ(NES_CPU::IRQD_DMC, true); } } } @@ -334,7 +292,7 @@ namespace xgm return (damp<<1) + dac_lsb; } - void NES_DMC::TickFrameSequence (UINT32 clocks) + void NES_DMC::TickFrameSequence (unsigned int clocks) { frame_sequence_count += clocks; while (frame_sequence_count > frame_sequence_length) @@ -347,28 +305,28 @@ namespace xgm } } - void NES_DMC::Tick (UINT32 clocks) + void NES_DMC::Tick (unsigned int clocks) { out[0] = calc_tri(clocks); out[1] = calc_noise(clocks); out[2] = calc_dmc(clocks); } - UINT32 NES_DMC::Render (INT32 b[2]) + unsigned int NES_DMC::Render (int b[2]) { out[0] = (mask & 1) ? 0 : out[0]; out[1] = (mask & 2) ? 0 : out[1]; out[2] = (mask & 4) ? 0 : out[2]; - INT32 m[3]; + int m[3]; m[0] = tnd_table[0][out[0]][0][0]; m[1] = tnd_table[0][0][out[1]][0]; m[2] = tnd_table[0][0][0][out[2]]; if (option[OPT_NONLINEAR_MIXER]) { - INT32 ref = m[0] + m[1] + m[2]; - INT32 voltage = tnd_table[1][out[0]][out[1]][out[2]]; + int ref = m[0] + m[1] + m[2]; + int voltage = tnd_table[1][out[0]][out[1]][out[2]]; if (ref) { for (int i=0; i < 3; ++i) @@ -391,7 +349,7 @@ namespace xgm dmc_pop = false; // prevent overflow, keep headspace at edges - const INT32 OFFSET_MAX = (1 << 30) - (4 << 16); + const int OFFSET_MAX = (1 << 30) - (4 << 16); if (dmc_pop_offset > OFFSET_MAX) dmc_pop_offset = OFFSET_MAX; if (dmc_pop_offset < -OFFSET_MAX) dmc_pop_offset = -OFFSET_MAX; } @@ -425,7 +383,7 @@ namespace xgm void NES_DMC::SetRate (double r) { - rate = (UINT32)(r?r:DEFAULT_RATE); + rate = (unsigned int)(r?r:DEFAULT_RATE); } void NES_DMC::SetPal (bool is_pal) @@ -455,7 +413,7 @@ namespace xgm for(int t=0; t<16 ; t++) { for(int n=0; n<16; n++) { for(int d=0; d<128; d++) { - tnd_table[0][t][n][d] = (UINT32)(MASTER*(3.0*t+2.0*n+d)/208.0); + tnd_table[0][t][n][d] = (unsigned int)(MASTER*(3.0*t+2.0*n+d)/208.0); } } } @@ -466,7 +424,7 @@ namespace xgm for(int n=0; n<16; n++) { for(int d=0; d<128; d++) { if(t!=0||n!=0||d!=0) - tnd_table[1][t][n][d] = (UINT32)((MASTER*159.79)/(100.0+1.0/((double)t/wt+(double)n/wn+(double)d/wd))); + tnd_table[1][t][n][d] = (unsigned int)((MASTER*159.79)/(100.0+1.0/((double)t/wt+(double)n/wn+(double)d/wd))); } } } @@ -510,7 +468,6 @@ namespace xgm frame_sequence_count = 0; frame_sequence_steps = 4; frame_sequence_step = 0; - cpu->UpdateIRQ(NES_CPU::IRQD_FRAME, false); for (i = 0; i < 0x0F; i++) Write (0x4008 + i, 0); @@ -520,7 +477,6 @@ namespace xgm Write (0x4015, 0x00); if (option[OPT_UNMUTE_ON_RESET]) Write (0x4015, 0x0f); - cpu->UpdateIRQ(NES_CPU::IRQD_DMC, false); out[0] = out[1] = out[2] = 0; damp = 0; @@ -551,7 +507,7 @@ namespace xgm SetRate(rate); } - void NES_DMC::SetMemory (IDevice * r) + void NES_DMC::SetMemory (std::function r) { memory = r; } @@ -566,9 +522,9 @@ namespace xgm } } - bool NES_DMC::Write (UINT32 adr, UINT32 val, UINT32 id) + bool NES_DMC::Write (unsigned int adr, unsigned int val, unsigned int id) { - static const UINT8 length_table[32] = { + static const unsigned char length_table[32] = { 0x0A, 0xFE, 0x14, 0x02, 0x28, 0x04, @@ -612,7 +568,6 @@ namespace xgm } irq = false; - cpu->UpdateIRQ(NES_CPU::IRQD_DMC, false); reg[adr-0x4008] = val; return true; @@ -623,7 +578,6 @@ namespace xgm //DEBUG_OUT("4017 = %02XÂ¥n", val); frame_irq_enable = ((val & 0x40) != 0x40); if (frame_irq_enable) frame_irq = false; - cpu->UpdateIRQ(NES_CPU::IRQD_FRAME, false); frame_sequence_count = 0; if (val & 0x80) @@ -708,7 +662,6 @@ namespace xgm if (!(mode & 2)) { irq = false; - cpu->UpdateIRQ(NES_CPU::IRQD_DMC, false); } dfreq = freq_table[pal][val&15]; break; @@ -739,7 +692,7 @@ namespace xgm return true; } - bool NES_DMC::Read (UINT32 adr, UINT32 & val, UINT32 id) + bool NES_DMC::Read (unsigned int adr, unsigned int & val, unsigned int id) { if (adr == 0x4015) { @@ -751,7 +704,6 @@ namespace xgm ; frame_irq = false; - cpu->UpdateIRQ(NES_CPU::IRQD_FRAME, false); return true; } else if (0x4008<=adr&&adr<=0x4014) @@ -762,10 +714,4 @@ namespace xgm else return false; } - - // IRQ support requires CPU read access - void NES_DMC::SetCPU(NES_CPU* cpu_) - { - cpu = cpu_; - } } // namespace diff --git a/src/engine/platform/sound/nes_nsfplay/nes_dmc.h b/src/engine/platform/sound/nes_nsfplay/nes_dmc.h index 6cc8058a2..4d19617d8 100644 --- a/src/engine/platform/sound/nes_nsfplay/nes_dmc.h +++ b/src/engine/platform/sound/nes_nsfplay/nes_dmc.h @@ -1,16 +1,13 @@ #ifndef _NES_DMC_H_ #define _NES_DMC_H_ - -#include "../device.h" -#include "../Audio/MedianFilter.h" -#include "../CPU/nes_cpu.h" +#include namespace xgm { class NES_APU; // forward declaration /** Bottom Half of APU **/ - class NES_DMC:public ISoundChip + class NES_DMC { public: enum @@ -28,46 +25,46 @@ namespace xgm }; protected: const int GETA_BITS; - static const UINT32 freq_table[2][16]; - static const UINT32 wavlen_table[2][16]; - UINT32 tnd_table[2][16][16][128]; + static const unsigned int freq_table[2][16]; + static const unsigned int wavlen_table[2][16]; + unsigned int tnd_table[2][16][16][128]; int option[OPT_END]; int mask; - INT32 sm[2][3]; - UINT8 reg[0x10]; - UINT32 len_reg; - UINT32 adr_reg; - IDevice *memory; - UINT32 out[3]; - UINT32 daddress; - UINT32 dlength; - UINT32 data; + int sm[2][3]; + unsigned int reg[0x10]; + unsigned int len_reg; + unsigned int adr_reg; + std::function memory; + unsigned int out[3]; + unsigned int daddress; + unsigned int dlength; + unsigned int data; bool empty; - INT16 damp; + short damp; int dac_lsb; bool dmc_pop; - INT32 dmc_pop_offset; - INT32 dmc_pop_follow; + int dmc_pop_offset; + int dmc_pop_follow; double clock; - UINT32 rate; + unsigned int rate; int pal; int mode; bool irq; - INT32 counter[3]; // frequency dividers + int counter[3]; // frequency dividers int tphase; // triangle phase - UINT32 nfreq; // noise frequency - UINT32 dfreq; // DPCM frequency + unsigned int nfreq; // noise frequency + unsigned int dfreq; // DPCM frequency - UINT32 tri_freq; + unsigned int tri_freq; int linear_counter; int linear_counter_reload; bool linear_counter_halt; bool linear_counter_control; int noise_volume; - UINT32 noise, noise_tap; + unsigned int noise, noise_tap; // noise envelope bool envelope_loop; @@ -80,8 +77,6 @@ namespace xgm bool enable[2]; // tri/noise enable int length_counter[2]; // 0=tri, 1=noise - TrackInfoBasic trkinfo[3]; - // frame sequencer NES_APU* apu; // apu is clocked by DMC's frame sequencer int frame_sequence_count; // current cycle count @@ -91,37 +86,32 @@ namespace xgm bool frame_irq; bool frame_irq_enable; - NES_CPU* cpu; // IRQ needs CPU access - - inline UINT32 calc_tri (UINT32 clocks); - inline UINT32 calc_dmc (UINT32 clocks); - inline UINT32 calc_noise (UINT32 clocks); + inline unsigned int calc_tri (unsigned int clocks); + inline unsigned int calc_dmc (unsigned int clocks); + inline unsigned int calc_noise (unsigned int clocks); public: NES_DMC (); - ‾NES_DMC (); + ~NES_DMC (); void InitializeTNDTable(double wt, double wn, double wd); void SetPal (bool is_pal); void SetAPU (NES_APU* apu_); - void SetMemory (IDevice * r); + void SetMemory (std::function r); void FrameSequence(int s); int GetDamp(){ return (damp<<1)|dac_lsb ; } - void TickFrameSequence (UINT32 clocks); + void TickFrameSequence (unsigned int clocks); virtual void Reset (); - virtual void Tick (UINT32 clocks); - virtual UINT32 Render (INT32 b[2]); - virtual bool Write (UINT32 adr, UINT32 val, UINT32 id=0); - virtual bool Read (UINT32 adr, UINT32 & val, UINT32 id=0); + virtual void Tick (unsigned int clocks); + virtual unsigned int Render (int b[2]); + virtual bool Write (unsigned int adr, unsigned int val, unsigned int id=0); + virtual bool Read (unsigned int adr, unsigned int & val, unsigned int id=0); virtual void SetRate (double rate); virtual void SetClock (double rate); virtual void SetOption (int, int); virtual void SetMask(int m){ mask = m; } - virtual void SetStereoMix (int trk, xgm::INT16 mixl, xgm::INT16 mixr); - virtual ITrackInfo *GetTrackInfo(int trk); - - void SetCPU(NES_CPU* cpu_); + virtual void SetStereoMix (int trk, short mixl, short mixr); }; } diff --git a/src/engine/platform/sound/nes_nsfplay/nes_fds.cpp b/src/engine/platform/sound/nes_nsfplay/nes_fds.cpp index 1e08100c0..f3e61decc 100644 --- a/src/engine/platform/sound/nes_nsfplay/nes_fds.cpp +++ b/src/engine/platform/sound/nes_nsfplay/nes_fds.cpp @@ -1,5 +1,7 @@ #include +#include #include "nes_fds.h" +#include "common.h" namespace xgm { @@ -22,11 +24,11 @@ NES_FDS::NES_FDS () Reset(); } -NES_FDS::‾NES_FDS () +NES_FDS::~NES_FDS () { } -void NES_FDS::SetStereoMix(int trk, INT16 mixl, INT16 mixr) +void NES_FDS::SetStereoMix(int trk, short mixl, short mixr) { if (trk < 0) return; if (trk > 1) return; @@ -34,20 +36,6 @@ void NES_FDS::SetStereoMix(int trk, INT16 mixl, INT16 mixr) sm[1] = mixr; } -ITrackInfo *NES_FDS::GetTrackInfo(int trk) -{ - trkinfo.max_volume = 32; - trkinfo.volume = last_vol; - trkinfo.key = last_vol > 0; - trkinfo._freq = last_freq; - trkinfo.freq = (double(last_freq) * clock) / (65536.0 * 64.0); - trkinfo.tone = env_out[EMOD]; - for(int i=0;i<64;i++) - trkinfo.wave[i] = wave[TWAV][i]; - - return &trkinfo; -} - void NES_FDS::SetClock (double c) { clock = c; @@ -61,8 +49,8 @@ void NES_FDS::SetRate (double r) double cutoff = double(option[OPT_CUTOFF]); double leak = 0.0; if (cutoff > 0) - leak = ::exp(-2.0 * 3.14159 * cutoff / rate); - rc_k = INT32(leak * double(1<= period) { // clock the envelope @@ -158,22 +146,22 @@ void NES_FDS::Tick (UINT32 clocks) if (!mod_halt) { // advance phase, adjust for modulator - UINT32 start_pos = phase[TMOD] >> 16; + unsigned int start_pos = phase[TMOD] >> 16; phase[TMOD] += (clocks * freq[TMOD]); - UINT32 end_pos = phase[TMOD] >> 16; + unsigned int end_pos = phase[TMOD] >> 16; // wrap the phase to the 64-step table (+ 16 bit accumulator) phase[TMOD] = phase[TMOD] & 0x3FFFFF; // execute all clocked steps - for (UINT32 p = start_pos; p < end_pos; ++p) + for (unsigned int p = start_pos; p < end_pos; ++p) { - INT32 wv = wave[TMOD][p & 0x3F]; + int wv = wave[TMOD][p & 0x3F]; if (wv == 4) // 4 resets mod position mod_pos = 0; else { - const INT32 BIAS[8] = { 0, 1, 2, 4, 0, -4, -2, -1 }; + const int BIAS[8] = { 0, 1, 2, 4, 0, -4, -2, -1 }; mod_pos += BIAS[wv]; mod_pos &= 0x7F; // 7-bit clamp } @@ -184,16 +172,16 @@ void NES_FDS::Tick (UINT32 clocks) if (!wav_halt) { // complex mod calculation - INT32 mod = 0; + int mod = 0; if (env_out[EMOD] != 0) // skip if modulator off { // convert mod_pos to 7-bit signed - INT32 pos = (mod_pos < 64) ? mod_pos : (mod_pos-128); + int pos = (mod_pos < 64) ? mod_pos : (mod_pos-128); // multiply pos by gain, // shift off 4 bits but with odd "rounding" behaviour - INT32 temp = pos * env_out[EMOD]; - INT32 rem = temp & 0x0F; + int temp = pos * env_out[EMOD]; + int rem = temp & 0x0F; temp >>= 4; if ((rem > 0) && ((temp & 0x80) == 0)) { @@ -216,7 +204,7 @@ void NES_FDS::Tick (UINT32 clocks) } // advance wavetable position - INT32 f = freq[TWAV] + mod; + int f = freq[TWAV] + mod; phase[TWAV] = phase[TWAV] + (clocks * f); phase[TWAV] = phase[TWAV] & 0x3FFFFF; // wrap @@ -225,7 +213,7 @@ void NES_FDS::Tick (UINT32 clocks) } // output volume caps at 32 - INT32 vol_out = env_out[EVOL]; + int vol_out = env_out[EVOL]; if (vol_out > 32) vol_out = 32; // final output @@ -242,32 +230,32 @@ void NES_FDS::Tick (UINT32 clocks) last_vol = vol_out; } -UINT32 NES_FDS::Render (INT32 b[2]) +unsigned int NES_FDS::Render (int b[2]) { // 8 bit approximation of master volume const double MASTER_VOL = 2.4 * 1223.0; // max FDS vol vs max APU square (arbitrarily 1223) const double MAX_OUT = 32.0f * 63.0f; // value that should map to master vol - const INT32 MASTER[4] = { + const int MASTER[4] = { int((MASTER_VOL / MAX_OUT) * 256.0 * 2.0f / 2.0f), int((MASTER_VOL / MAX_OUT) * 256.0 * 2.0f / 3.0f), int((MASTER_VOL / MAX_OUT) * 256.0 * 2.0f / 4.0f), int((MASTER_VOL / MAX_OUT) * 256.0 * 2.0f / 5.0f) }; - INT32 v = fout * MASTER[master_vol] >> 8; + int v = fout * MASTER[master_vol] >> 8; // lowpass RC filter - INT32 rc_out = ((rc_accum * rc_k) + (v * rc_l)) >> RC_BITS; + int rc_out = ((rc_accum * rc_k) + (v * rc_l)) >> RC_BITS; rc_accum = rc_out; v = rc_out; // output mix - INT32 m = mask ? 0 : v; + int m = mask ? 0 : v; b[0] = (m * sm[0]) >> 7; b[1] = (m * sm[1]) >> 7; return 2; } -bool NES_FDS::Write (UINT32 adr, UINT32 val, UINT32 id) +bool NES_FDS::Write (unsigned int adr, unsigned int val, unsigned int id) { // $4023 master I/O enable/disable if (adr == 0x4023) @@ -368,7 +356,7 @@ bool NES_FDS::Write (UINT32 adr, UINT32 val, UINT32 id) return false; } -bool NES_FDS::Read (UINT32 adr, UINT32 & val, UINT32 id) +bool NES_FDS::Read (unsigned int adr, unsigned int & val, unsigned int id) { if (adr >= 0x4040 && adr <= 0x407F) { diff --git a/src/engine/platform/sound/nes_nsfplay/nes_fds.h b/src/engine/platform/sound/nes_nsfplay/nes_fds.h index 67c60fd1c..180841dd8 100644 --- a/src/engine/platform/sound/nes_nsfplay/nes_fds.h +++ b/src/engine/platform/sound/nes_nsfplay/nes_fds.h @@ -1,17 +1,9 @@ #ifndef _NES_FDS_H_ #define _NES_FDS_H_ -#include "../device.h" namespace xgm { -class TrackInfoFDS : public TrackInfoBasic -{ -public: - INT16 wave[64]; - virtual IDeviceInfo *Clone(){ return new TrackInfoFDS(*this); } -}; - -class NES_FDS : public ISoundChip +class NES_FDS { public: enum @@ -25,57 +17,55 @@ public: protected: double rate, clock; int mask; - INT32 sm[2]; // stereo mix - INT32 fout; // current output - TrackInfoFDS trkinfo; + int sm[2]; // stereo mix + int fout; // current output int option[OPT_END]; bool master_io; - UINT32 master_vol; - UINT32 last_freq; // for trackinfo - UINT32 last_vol; // for trackinfo + unsigned int master_vol; + unsigned int last_freq; // for trackinfo + unsigned int last_vol; // for trackinfo // two wavetables enum { TMOD=0, TWAV=1 }; - INT32 wave[2][64]; - UINT32 freq[2]; - UINT32 phase[2]; + int wave[2][64]; + unsigned int freq[2]; + unsigned int phase[2]; bool wav_write; bool wav_halt; bool env_halt; bool mod_halt; - UINT32 mod_pos; - UINT32 mod_write_pos; + unsigned int mod_pos; + unsigned int mod_write_pos; // two ramp envelopes enum { EMOD=0, EVOL=1 }; bool env_mode[2]; bool env_disable[2]; - UINT32 env_timer[2]; - UINT32 env_speed[2]; - UINT32 env_out[2]; - UINT32 master_env_speed; + unsigned int env_timer[2]; + unsigned int env_speed[2]; + unsigned int env_out[2]; + unsigned int master_env_speed; // 1-pole RC lowpass filter - INT32 rc_accum; - INT32 rc_k; - INT32 rc_l; + int rc_accum; + int rc_k; + int rc_l; public: NES_FDS (); - virtual ‾ NES_FDS (); + ~NES_FDS (); virtual void Reset (); - virtual void Tick (UINT32 clocks); - virtual UINT32 Render (INT32 b[2]); - virtual bool Write (UINT32 adr, UINT32 val, UINT32 id=0); - virtual bool Read (UINT32 adr, UINT32 & val, UINT32 id=0); + virtual void Tick (unsigned int clocks); + virtual unsigned int Render (int b[2]); + virtual bool Write (unsigned int adr, unsigned int val, unsigned int id=0); + virtual bool Read (unsigned int adr, unsigned int & val, unsigned int id=0); virtual void SetRate (double); virtual void SetClock (double); virtual void SetOption (int, int); virtual void SetMask(int m){ mask = m&1; } - virtual void SetStereoMix (int trk, INT16 mixl, INT16 mixr); - virtual ITrackInfo *GetTrackInfo(int trk); + virtual void SetStereoMix (int trk, short mixl, short mixr); }; } // namespace xgm diff --git a/src/engine/platform/sound/nes_nsfplay/nes_mmc5.cpp b/src/engine/platform/sound/nes_nsfplay/nes_mmc5.cpp index 9243f6595..500be87ca 100644 --- a/src/engine/platform/sound/nes_nsfplay/nes_mmc5.cpp +++ b/src/engine/platform/sound/nes_nsfplay/nes_mmc5.cpp @@ -1,11 +1,11 @@ #include "nes_mmc5.h" +#include "common.h" namespace xgm { NES_MMC5::NES_MMC5 () { - cpu = NULL; SetClock (DEFAULT_CLOCK); SetRate (DEFAULT_RATE); option[OPT_NONLINEAR_MIXER] = true; @@ -15,19 +15,19 @@ namespace xgm // square nonlinear mix, same as 2A03 square_table[0] = 0; for(int i=1;i<32;i++) - square_table[i]=(INT32)((8192.0*95.88)/(8128.0/i+100)); + square_table[i]=(int)((8192.0*95.88)/(8128.0/i+100)); // 2A03 style nonlinear pcm mix with double the bits //pcm_table[0] = 0; - //INT32 wd = 22638; + //int wd = 22638; //for(int d=1;d<256; ++d) - // pcm_table[d] = (INT32)((8192.0*159.79)/(100.0+1.0/((double)d/wd))); + // pcm_table[d] = (int)((8192.0*159.79)/(100.0+1.0/((double)d/wd))); // linear pcm mix (actual hardware seems closer to this) pcm_table[0] = 0; double pcm_scale = 32.0; for (int d=1; d<256; ++d) - pcm_table[d] = (INT32)(double(d) * pcm_scale); + pcm_table[d] = (int)(double(d) * pcm_scale); // stereo mix for(int c=0;c<2;++c) @@ -35,7 +35,7 @@ namespace xgm sm[c][t] = 128; } - NES_MMC5::‾NES_MMC5 () + NES_MMC5::~NES_MMC5 () { } @@ -124,9 +124,9 @@ namespace xgm } } - INT32 NES_MMC5::calc_sqr (int i, UINT32 clocks) + int NES_MMC5::calc_sqr (int i, unsigned int clocks) { - static const INT16 sqrtbl[4][16] = { + static const short sqrtbl[4][16] = { {0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0}, @@ -140,7 +140,7 @@ namespace xgm scounter[i] -= (freq[i] + 1); } - INT32 ret = 0; + int ret = 0; if (length_counter[i] > 0) { // note MMC5 does not silence the highest 8 frequencies like APU, @@ -153,7 +153,7 @@ namespace xgm return ret; } - void NES_MMC5::TickFrameSequence (UINT32 clocks) + void NES_MMC5::TickFrameSequence (unsigned int clocks) { frame_sequence_count += clocks; while (frame_sequence_count > 7458) @@ -163,28 +163,28 @@ namespace xgm } } - void NES_MMC5::Tick (UINT32 clocks) + void NES_MMC5::Tick (unsigned int clocks) { out[0] = calc_sqr(0, clocks); out[1] = calc_sqr(1, clocks); out[2] = pcm; } - UINT32 NES_MMC5::Render (INT32 b[2]) + unsigned int NES_MMC5::Render (int b[2]) { out[0] = (mask & 1) ? 0 : out[0]; out[1] = (mask & 2) ? 0 : out[1]; out[2] = (mask & 4) ? 0 : out[2]; - INT32 m[3]; + int m[3]; if(option[OPT_NONLINEAR_MIXER]) { // squares nonlinear - INT32 voltage = square_table[out[0] + out[1]]; + int voltage = square_table[out[0] + out[1]]; m[0] = out[0] << 6; m[1] = out[1] << 6; - INT32 ref = m[0] + m[1]; + int ref = m[0] + m[1]; if (ref > 0) { m[0] = (m[0] * voltage) / ref; @@ -224,11 +224,11 @@ namespace xgm return 2; } - bool NES_MMC5::Write (UINT32 adr, UINT32 val, UINT32 id) + bool NES_MMC5::Write (unsigned int adr, unsigned int val, unsigned int id) { int ch; - static const UINT8 length_table[32] = { + static const unsigned char length_table[32] = { 0x0A, 0xFE, 0x14, 0x02, 0x28, 0x04, @@ -329,19 +329,8 @@ namespace xgm return true; } - bool NES_MMC5::Read (UINT32 adr, UINT32 & val, UINT32 id) + bool NES_MMC5::Read (unsigned int adr, unsigned int & val, unsigned int id) { - // in PCM read mode, reads from $8000-$C000 automatically load the PCM output - if (pcm_mode && (0x8000 <= adr) && (adr < 0xC000) && cpu) - { - pcm_mode = false; // prevent recursive entry - UINT32 pcm_read; - cpu->Read(adr, pcm_read); - pcm_read &= 0xFF; - if (pcm_read != 0) - pcm = pcm_read; - pcm_mode = true; - } if ((0x5000 <= adr) && (adr < 0x5008)) { @@ -373,50 +362,11 @@ namespace xgm return false; } - void NES_MMC5::SetStereoMix(int trk, xgm::INT16 mixl, xgm::INT16 mixr) + void NES_MMC5::SetStereoMix(int trk, short mixl, short mixr) { if (trk < 0) return; if (trk > 2) return; sm[0][trk] = mixl; sm[1][trk] = mixr; } - - ITrackInfo *NES_MMC5::GetTrackInfo(int trk) - { - assert(trk<3); - - if (trk < 2) // square - { - trkinfo[trk]._freq = freq[trk]; - if(freq[trk]) - trkinfo[trk].freq = clock/16/(freq[trk] + 1); - else - trkinfo[trk].freq = 0; - - trkinfo[trk].output = out[trk]; - trkinfo[trk].max_volume = 15; - trkinfo[trk].volume = volume[trk]+(envelope_disable[trk]?0:0x10); - trkinfo[trk].key = (envelope_disable[trk]?(volume[trk]>0): (envelope_counter[trk]>0)); - trkinfo[trk].tone = duty[trk]; - } - else // pcm - { - trkinfo[trk]._freq = 0; - trkinfo[trk].freq = 0; - trkinfo[trk].output = out[2]; - trkinfo[trk].max_volume = 255; - trkinfo[trk].volume = pcm; - trkinfo[trk].key = 0; - trkinfo[trk].tone = pcm_mode ? 1 : 0; - } - - return &trkinfo[trk]; - } - - // pcm read mode requires CPU read access - void NES_MMC5::SetCPU(NES_CPU* cpu_) - { - cpu = cpu_; - } - }// namespace diff --git a/src/engine/platform/sound/nes_nsfplay/nes_mmc5.h b/src/engine/platform/sound/nes_nsfplay/nes_mmc5.h index 5747ab0d6..d0ae8bd0c 100644 --- a/src/engine/platform/sound/nes_nsfplay/nes_mmc5.h +++ b/src/engine/platform/sound/nes_nsfplay/nes_mmc5.h @@ -1,11 +1,9 @@ #ifndef _NES_MMC5_H_ #define _NES_MMC5_H_ -#include "../device.h" -#include "../CPU/nes_cpu.h" namespace xgm { - class NES_MMC5:public ISoundChip + class NES_MMC5 { public: enum @@ -14,21 +12,20 @@ namespace xgm protected: int option[OPT_END]; int mask; - INT32 sm[2][3]; // stereo panning - UINT8 ram[0x6000 - 0x5c00]; - UINT8 reg[8]; - UINT8 mreg[2]; - UINT8 pcm; // PCM channel + int sm[2][3]; // stereo panning + unsigned char ram[0x6000 - 0x5c00]; + unsigned char reg[8]; + unsigned char mreg[2]; + unsigned char pcm; // PCM channel bool pcm_mode; // PCM channel - NES_CPU* cpu; // PCM channel reads need CPU access - UINT32 scounter[2]; // frequency divider - UINT32 sphase[2]; // phase counter + unsigned int scounter[2]; // frequency divider + unsigned int sphase[2]; // phase counter - UINT32 duty[2]; - UINT32 volume[2]; - UINT32 freq[2]; - INT32 out[3]; + unsigned int duty[2]; + unsigned int volume[2]; + unsigned int freq[2]; + int out[3]; bool enable[2]; bool envelope_disable[2]; // エンベロープ有効フラグ @@ -43,30 +40,26 @@ namespace xgm int frame_sequence_count; double clock, rate; - INT32 calc_sqr (int i, UINT32 clocks); - INT32 square_table[32]; - INT32 pcm_table[256]; - TrackInfoBasic trkinfo[3]; + int calc_sqr (int i, unsigned int clocks); + int square_table[32]; + int pcm_table[256]; public: NES_MMC5 (); - ‾NES_MMC5 (); + ~NES_MMC5 (); void FrameSequence (); - void TickFrameSequence (UINT32 clocks); + void TickFrameSequence (unsigned int clocks); virtual void Reset (); - virtual void Tick (UINT32 clocks); - virtual UINT32 Render (INT32 b[2]); - virtual bool Write (UINT32 adr, UINT32 val, UINT32 id=0); - virtual bool Read (UINT32 adr, UINT32 & val, UINT32 id=0); + virtual void Tick (unsigned int clocks); + virtual unsigned int Render (int b[2]); + virtual bool Write (unsigned int adr, unsigned int val, unsigned int id=0); + virtual bool Read (unsigned int adr, unsigned int & val, unsigned int id=0); virtual void SetOption (int id, int b); virtual void SetClock (double); virtual void SetRate (double); virtual void SetMask (int m){ mask = m; } - virtual void SetStereoMix (int trk, xgm::INT16 mixl, xgm::INT16 mixr); - virtual ITrackInfo *GetTrackInfo(int trk); - - void SetCPU(NES_CPU* cpu_); + virtual void SetStereoMix (int trk, short mixl, short mixr); }; } diff --git a/src/engine/platform/sound/nes_nsfplay/nes_n106.cpp b/src/engine/platform/sound/nes_nsfplay/nes_n106.cpp index ae793dc08..25e80fb47 100644 --- a/src/engine/platform/sound/nes_nsfplay/nes_n106.cpp +++ b/src/engine/platform/sound/nes_nsfplay/nes_n106.cpp @@ -1,5 +1,6 @@ #include #include "nes_n106.h" +#include "common.h" namespace xgm { @@ -18,11 +19,11 @@ NES_N106::NES_N106 () Reset(); } -NES_N106::‾NES_N106 () +NES_N106::~NES_N106 () { } -void NES_N106::SetStereoMix (int trk, INT16 mixl, INT16 mixr) +void NES_N106::SetStereoMix (int trk, short mixl, short mixr) { if (trk < 0 || trk >= 8) return; trk = 7-trk; // displayed channels are inverted @@ -30,42 +31,6 @@ void NES_N106::SetStereoMix (int trk, INT16 mixl, INT16 mixr) sm[1][trk] = mixr; } -ITrackInfo *NES_N106::GetTrackInfo (int trk) -{ - int channels = get_channels(); - int channel = 7-trk; // invert the track display - - TrackInfoN106* t = &trkinfo[channel]; - - if (trk >= channels) - { - t->max_volume = 15; - t->volume = 0; - t->_freq = 0; - t->wavelen = 0; - t->tone = -1; - t->output = 0; - t->key = false; - t->freq = 0; - } - else - { - t->max_volume = 15; - t->volume = get_vol(channel); - t->_freq = get_freq(channel); - t->wavelen = get_len(channel); - t->tone = get_off(channel); - t->output = fout[channel]; - - t->key = (t->volume > 0) && (t->_freq > 0); - t->freq = (double(t->_freq) * clock) / double(15 * 65536 * channels * t->wavelen); - - for (int i=0; i < t->wavelen; ++i) - t->wave[i] = get_sample((i+t->tone)&0xFF); - } - - return t; -} void NES_N106::SetClock (double c) { @@ -120,7 +85,7 @@ void NES_N106::Reset () Write(0xF800, 0x00); // select $00 without auto-increment } -void NES_N106::Tick (UINT32 clocks) +void NES_N106::Tick (unsigned int clocks) { if (master_disable) return; @@ -132,24 +97,24 @@ void NES_N106::Tick (UINT32 clocks) { int channel = 7-tick_channel; - UINT32 phase = get_phase(channel); - UINT32 freq = get_freq(channel); - UINT32 len = get_len(channel); - UINT32 off = get_off(channel); - INT32 vol = get_vol(channel); + unsigned int phase = get_phase(channel); + unsigned int freq = get_freq(channel); + unsigned int len = get_len(channel); + unsigned int off = get_off(channel); + int vol = get_vol(channel); // accumulate 24-bit phase phase = (phase + freq) & 0x00FFFFFF; // wrap phase if wavelength exceeded - UINT32 hilen = len << 16; + unsigned int hilen = len << 16; while (phase >= hilen) phase -= hilen; // write back phase set_phase(phase, channel); // fetch sample (note: N163 output is centred at 8, and inverted w.r.t 2A03) - INT32 sample = 8 - get_sample(((phase >> 16) + off) & 0xFF); + int sample = 8 - get_sample(((phase >> 16) + off) & 0xFF); fout[channel] = sample * vol; // cycle to next channel every 15 clocks @@ -160,7 +125,7 @@ void NES_N106::Tick (UINT32 clocks) } } -UINT32 NES_N106::Render (INT32 b[2]) +unsigned int NES_N106::Render (int b[2]) { b[0] = 0; b[1] = 0; @@ -217,7 +182,7 @@ UINT32 NES_N106::Render (INT32 b[2]) } // mix together, increase output level by 8 bits, roll off 7 bits from sm - INT32 MIX[9] = { 256/1, 256/1, 256/2, 256/3, 256/4, 256/5, 256/6, 256/6, 256/6 }; + int MIX[9] = { 256/1, 256/1, 256/2, 256/3, 256/4, 256/5, 256/6, 256/6, 256/6 }; b[0] = (b[0] * MIX[channels]) >> 7; b[1] = (b[1] * MIX[channels]) >> 7; // when approximating the serial multiplex as a straight mix, once the @@ -233,14 +198,14 @@ UINT32 NES_N106::Render (INT32 b[2]) // and lower volumes on others. Using 6.0x as a rough "one size fits all". const double MASTER_VOL = 6.0 * 1223.0; const double MAX_OUT = 15.0 * 15.0 * 256.0; // max digital value - const INT32 GAIN = int((MASTER_VOL / MAX_OUT) * 256.0f); + const int GAIN = int((MASTER_VOL / MAX_OUT) * 256.0f); b[0] = (b[0] * GAIN) >> 8; b[1] = (b[1] * GAIN) >> 8; return 2; } -bool NES_N106::Write (UINT32 adr, UINT32 val, UINT32 id) +bool NES_N106::Write (unsigned int adr, unsigned int val, unsigned int id) { if (adr == 0xE000) // master disable { @@ -286,7 +251,7 @@ bool NES_N106::Write (UINT32 adr, UINT32 val, UINT32 id) return false; } -bool NES_N106::Read (UINT32 adr, UINT32 & val, UINT32 id) +bool NES_N106::Read (unsigned int adr, unsigned int & val, unsigned int id) { if (adr == 0x4800) // register read { @@ -302,7 +267,7 @@ bool NES_N106::Read (UINT32 adr, UINT32 & val, UINT32 id) // register decoding/encoding functions // -inline UINT32 NES_N106::get_phase (int channel) +inline unsigned int NES_N106::get_phase (int channel) { // 24-bit phase stored in channel regs 1/3/5 channel = channel << 3; @@ -311,7 +276,7 @@ inline UINT32 NES_N106::get_phase (int channel) + (reg[0x45 + channel] << 16); } -inline UINT32 NES_N106::get_freq (int channel) +inline unsigned int NES_N106::get_freq (int channel) { // 19-bit frequency stored in channel regs 0/2/4 channel = channel << 3; @@ -320,28 +285,28 @@ inline UINT32 NES_N106::get_freq (int channel) + ((reg[0x44 + channel] & 0x03) << 16); } -inline UINT32 NES_N106::get_off (int channel) +inline unsigned int NES_N106::get_off (int channel) { // 8-bit offset stored in channel reg 6 channel = channel << 3; return reg[0x46 + channel]; } -inline UINT32 NES_N106::get_len (int channel) +inline unsigned int NES_N106::get_len (int channel) { // 6-bit<<3 length stored obscurely in channel reg 4 channel = channel << 3; return 256 - (reg[0x44 + channel] & 0xFC); } -inline INT32 NES_N106::get_vol (int channel) +inline int NES_N106::get_vol (int channel) { // 4-bit volume stored in channel reg 7 channel = channel << 3; return reg[0x47 + channel] & 0x0F; } -inline INT32 NES_N106::get_sample (UINT32 index) +inline int NES_N106::get_sample (unsigned int index) { // every sample becomes 2 samples in regs return (index&1) ? @@ -355,7 +320,7 @@ inline int NES_N106::get_channels () return ((reg[0x7F] >> 4) & 0x07) + 1; } -inline void NES_N106::set_phase (UINT32 phase, int channel) +inline void NES_N106::set_phase (unsigned int phase, int channel) { // 24-bit phase stored in channel regs 1/3/5 channel = channel << 3; diff --git a/src/engine/platform/sound/nes_nsfplay/nes_n106.h b/src/engine/platform/sound/nes_nsfplay/nes_n106.h index aebb6cb46..5ce937e26 100644 --- a/src/engine/platform/sound/nes_nsfplay/nes_n106.h +++ b/src/engine/platform/sound/nes_nsfplay/nes_n106.h @@ -1,18 +1,10 @@ #ifndef _NES_N106_H_ #define _NES_N106_H_ -#include "../device.h" namespace xgm { -class TrackInfoN106 : public TrackInfoBasic -{ -public: - int wavelen; - INT16 wave[256]; - virtual IDeviceInfo *Clone(){ return new TrackInfoN106(*this); } -}; -class NES_N106:public ISoundChip +class NES_N106 { public: enum @@ -26,13 +18,12 @@ public: protected: double rate, clock; int mask; - INT32 sm[2][8]; // stereo mix - INT32 fout[8]; // current output - TrackInfoN106 trkinfo[8]; + int sm[2][8]; // stereo mix + int fout[8]; // current output int option[OPT_END]; bool master_disable; - UINT32 reg[0x80]; // all state is contained here + unsigned int reg[0x80]; // all state is contained here unsigned int reg_select; bool reg_advance; int tick_channel; @@ -42,31 +33,30 @@ protected: int render_subclock; // convenience functions to interact with regs - inline UINT32 get_phase (int channel); - inline UINT32 get_freq (int channel); - inline UINT32 get_off (int channel); - inline UINT32 get_len (int channel); - inline INT32 get_vol (int channel); - inline INT32 get_sample (UINT32 index); + inline unsigned int get_phase (int channel); + inline unsigned int get_freq (int channel); + inline unsigned int get_off (int channel); + inline unsigned int get_len (int channel); + inline int get_vol (int channel); + inline int get_sample (unsigned int index); inline int get_channels (); // for storing back the phase after modifying - inline void set_phase (UINT32 phase, int channel); + inline void set_phase (unsigned int phase, int channel); public: NES_N106 (); - ‾NES_N106 (); + ~NES_N106 (); virtual void Reset (); - virtual void Tick (UINT32 clocks); - virtual UINT32 Render (INT32 b[2]); - virtual bool Write (UINT32 adr, UINT32 val, UINT32 id=0); - virtual bool Read (UINT32 adr, UINT32 & val, UINT32 id=0); + virtual void Tick (unsigned int clocks); + virtual unsigned int Render (int b[2]); + virtual bool Write (unsigned int adr, unsigned int val, unsigned int id=0); + virtual bool Read (unsigned int adr, unsigned int & val, unsigned int id=0); virtual void SetRate (double); virtual void SetClock (double); virtual void SetOption (int, int); virtual void SetMask (int m); - virtual void SetStereoMix (int trk, INT16 mixl, INT16 mixr); - virtual ITrackInfo *GetTrackInfo(int trk); + virtual void SetStereoMix (int trk, short mixl, short mixr); }; } // namespace xgm diff --git a/src/engine/platform/sound/nes_nsfplay/nes_vrc6.cpp b/src/engine/platform/sound/nes_nsfplay/nes_vrc6.cpp index c46109403..754d44cf7 100644 --- a/src/engine/platform/sound/nes_nsfplay/nes_vrc6.cpp +++ b/src/engine/platform/sound/nes_nsfplay/nes_vrc6.cpp @@ -1,4 +1,5 @@ #include "nes_vrc6.h" +#include "common.h" namespace xgm { @@ -16,11 +17,11 @@ namespace xgm sm[c][t] = 128; } - NES_VRC6::‾NES_VRC6 () + NES_VRC6::~NES_VRC6 () { } - void NES_VRC6::SetStereoMix(int trk, xgm::INT16 mixl, xgm::INT16 mixr) + void NES_VRC6::SetStereoMix(int trk, short mixl, short mixr) { if (trk < 0) return; if (trk > 2) return; @@ -28,32 +29,6 @@ namespace xgm sm[1][trk] = mixr; } - ITrackInfo *NES_VRC6::GetTrackInfo(int trk) - { - if(trk<2) - { - trkinfo[trk].max_volume = 15; - trkinfo[trk].volume = volume[trk]; - trkinfo[trk]._freq = freq2[trk]; - trkinfo[trk].freq = freq2[trk]?clock/16/(freq2[trk]+1):0; - trkinfo[trk].tone = duty[trk]; - trkinfo[trk].key = (volume[trk]>0)&&enable[trk]&&!gate[trk]; - return &trkinfo[trk]; - } - else if(trk==2) - { - trkinfo[2].max_volume = 255; - trkinfo[2].volume = volume[2]; - trkinfo[2]._freq = freq2[2]; - trkinfo[2].freq = freq2[2]?clock/14/(freq2[2]+1):0; - trkinfo[2].tone = -1; - trkinfo[2].key = (enable[2]>0); - return &trkinfo[2]; - } - else - return NULL; - } - void NES_VRC6::SetClock (double c) { clock = c; @@ -91,9 +66,9 @@ namespace xgm phase[0] = 2; } - INT16 NES_VRC6::calc_sqr (int i, UINT32 clocks) + short NES_VRC6::calc_sqr (int i, unsigned int clocks) { - static const INT16 sqrtbl[8][16] = { + static const short sqrtbl[8][16] = { {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1}, @@ -121,7 +96,7 @@ namespace xgm || sqrtbl[duty[i]][phase[i]])? volume[i] : 0; } - INT16 NES_VRC6::calc_saw (UINT32 clocks) + short NES_VRC6::calc_saw (unsigned int clocks) { if (!enable[2]) return 0; @@ -151,16 +126,16 @@ namespace xgm return phase[2] >> 3; } - void NES_VRC6::Tick (UINT32 clocks) + void NES_VRC6::Tick (unsigned int clocks) { out[0] = calc_sqr(0,clocks); out[1] = calc_sqr(1,clocks); out[2] = calc_saw(clocks); } - UINT32 NES_VRC6::Render (INT32 b[2]) + unsigned int NES_VRC6::Render (int b[2]) { - INT32 m[3]; + int m[3]; m[0] = out[0]; m[1] = out[1]; m[2] = out[2]; @@ -182,14 +157,14 @@ namespace xgm //b[1] >>= (7 - 7); // master volume adjustment - const INT32 MASTER = INT32(256.0 * 1223.0 / 1920.0); + const int MASTER = int(256.0 * 1223.0 / 1920.0); b[0] = (b[0] * MASTER) >> 8; b[1] = (b[1] * MASTER) >> 8; return 2; } - bool NES_VRC6::Write (UINT32 adr, UINT32 val, UINT32 id) + bool NES_VRC6::Write (unsigned int adr, unsigned int val, unsigned int id) { int ch, cmap[4] = { 0, 0, 1, 2 }; @@ -255,7 +230,7 @@ namespace xgm return true; } - bool NES_VRC6::Read (UINT32 adr, UINT32 & val, UINT32 id) + bool NES_VRC6::Read (unsigned int adr, unsigned int & val, unsigned int id) { return false; } diff --git a/src/engine/platform/sound/nes_nsfplay/nes_vrc6.h b/src/engine/platform/sound/nes_nsfplay/nes_vrc6.h index 2bb64eb8a..85fed39c4 100644 --- a/src/engine/platform/sound/nes_nsfplay/nes_vrc6.h +++ b/src/engine/platform/sound/nes_nsfplay/nes_vrc6.h @@ -1,11 +1,10 @@ #ifndef _NES_VRC6_H_ #define _NES_VRC6_H_ -#include "../device.h" namespace xgm { - class NES_VRC6:public ISoundChip + class NES_VRC6 { public: enum @@ -13,42 +12,40 @@ namespace xgm OPT_END }; protected: - UINT32 counter[3]; // frequency divider - UINT32 phase[3]; // phase counter - UINT32 freq2[3]; // adjusted frequency + unsigned int counter[3]; // frequency divider + unsigned int phase[3]; // phase counter + unsigned int freq2[3]; // adjusted frequency int count14; // saw 14-stage counter //int option[OPT_END]; int mask; - INT32 sm[2][3]; // stereo mix + int sm[2][3]; // stereo mix int duty[2]; int volume[3]; int enable[3]; int gate[3]; - UINT32 freq[3]; - INT16 calc_sqr (int i, UINT32 clocks); - INT16 calc_saw (UINT32 clocks); + unsigned int freq[3]; + short calc_sqr (int i, unsigned int clocks); + short calc_saw (unsigned int clocks); bool halt; int freq_shift; double clock, rate; - INT32 out[3]; - TrackInfoBasic trkinfo[3]; + int out[3]; public: NES_VRC6 (); - ‾NES_VRC6 (); + ~NES_VRC6 (); virtual void Reset (); - virtual void Tick (UINT32 clocks); - virtual UINT32 Render (INT32 b[2]); - virtual bool Read (UINT32 adr, UINT32 & val, UINT32 id=0); - virtual bool Write (UINT32 adr, UINT32 val, UINT32 id=0); + virtual void Tick (unsigned int clocks); + virtual unsigned int Render (int b[2]); + virtual bool Read (unsigned int adr, unsigned int & val, unsigned int id=0); + virtual bool Write (unsigned int adr, unsigned int val, unsigned int id=0); virtual void SetClock (double); virtual void SetRate (double); virtual void SetOption (int, int); virtual void SetMask (int m){ mask = m; } - virtual void SetStereoMix (int trk, xgm::INT16 mixl, xgm::INT16 mixr); - virtual ITrackInfo *GetTrackInfo(int trk); + virtual void SetStereoMix (int trk, short mixl, short mixr); }; } // namespace From cb7aa4aa058fdf423a2eb7094f1db26ed47df378 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 1 May 2022 23:20:20 -0500 Subject: [PATCH 279/342] NES: wire up NSFplay no muting supported for now see #27 --- src/engine/dispatchContainer.cpp | 1 + src/engine/platform/nes.cpp | 147 +++++++++++++----- src/engine/platform/nes.h | 10 ++ .../platform/sound/nes_nsfplay/nes_apu.h | 20 +-- .../platform/sound/nes_nsfplay/nes_dmc.h | 20 +-- .../platform/sound/nes_nsfplay/nes_fds.h | 20 +-- .../platform/sound/nes_nsfplay/nes_mmc5.h | 20 +-- .../platform/sound/nes_nsfplay/nes_n106.h | 20 +-- .../platform/sound/nes_nsfplay/nes_vrc6.h | 20 +-- src/gui/gui.h | 2 + src/gui/settings.cpp | 12 ++ 11 files changed, 193 insertions(+), 99 deletions(-) diff --git a/src/engine/dispatchContainer.cpp b/src/engine/dispatchContainer.cpp index e9f1a03e2..187523c72 100644 --- a/src/engine/dispatchContainer.cpp +++ b/src/engine/dispatchContainer.cpp @@ -189,6 +189,7 @@ void DivDispatchContainer::init(DivSystem sys, DivEngine* eng, int chanCount, do break; case DIV_SYSTEM_NES: dispatch=new DivPlatformNES; + ((DivPlatformNES*)dispatch)->setNSFPlay(eng->getConfInt("nesCore",0)==1); break; case DIV_SYSTEM_C64_6581: dispatch=new DivPlatformC64; diff --git a/src/engine/platform/nes.cpp b/src/engine/platform/nes.cpp index 7f35dc5bd..084c1123c 100644 --- a/src/engine/platform/nes.cpp +++ b/src/engine/platform/nes.cpp @@ -27,7 +27,7 @@ struct _nla_table nla_table; #define CHIP_DIVIDER 16 -#define rWrite(a,v) if (!skipRegisterWrites) {apu_wr_reg(nes,a,v); regPool[(a)&0x7f]=v; if (dumpWrites) {addWrite(a,v);} } +#define rWrite(a,v) if (!skipRegisterWrites) {doWrite(a,v); regPool[(a)&0x7f]=v; if (dumpWrites) {addWrite(a,v);} } const char* regCheatSheetNES[]={ "S0Volume", "4000", @@ -75,36 +75,48 @@ const char* DivPlatformNES::getEffectName(unsigned char effect) { return NULL; } -void DivPlatformNES::acquire(short* bufL, short* bufR, size_t start, size_t len) { +void DivPlatformNES::doWrite(unsigned short addr, unsigned char data) { + if (useNP) { + nes1_NP->Write(addr,data); + nes2_NP->Write(addr,data); + } else { + apu_wr_reg(nes,addr,data); + } +} + +#define doPCM \ + if (dacSample!=-1) { \ + dacPeriod+=dacRate; \ + if (dacPeriod>=rate) { \ + DivSample* s=parent->getSample(dacSample); \ + if (s->samples>0) { \ + if (!isMuted[4]) { \ + unsigned char next=((unsigned char)s->data8[dacPos]+0x80)>>1; \ + if (dacAntiClickOn && dacAntiClick=s->samples) { \ + if (s->loopStart>=0 && s->loopStart<(int)s->samples) { \ + dacPos=s->loopStart; \ + } else { \ + dacSample=-1; \ + } \ + } \ + dacPeriod-=rate; \ + } else { \ + dacSample=-1; \ + } \ + } \ + } + +void DivPlatformNES::acquire_puNES(short* bufL, short* bufR, size_t start, size_t len) { for (size_t i=start; i=rate) { - DivSample* s=parent->getSample(dacSample); - if (s->samples>0) { - if (!isMuted[4]) { - unsigned char next=((unsigned char)s->data8[dacPos]+0x80)>>1; - if (dacAntiClickOn && dacAntiClick=s->samples) { - if (s->loopStart>=0 && s->loopStart<(int)s->samples) { - dacPos=s->loopStart; - } else { - dacSample=-1; - } - } - dacPeriod-=rate; - } else { - dacSample=-1; - } - } - } + doPCM; apu_tick(nes,NULL); nes->apu.odd_cycle=!nes->apu.odd_cycle; @@ -126,6 +138,32 @@ void DivPlatformNES::acquire(short* bufL, short* bufR, size_t start, size_t len) } } +void DivPlatformNES::acquire_NSFPlay(short* bufL, short* bufR, size_t start, size_t len) { + int out1[2]; + int out2[2]; + for (size_t i=start; iTick(1); + nes2_NP->Tick(1); + nes1_NP->Render(out1); + nes2_NP->Render(out2); + + int sample=out1[0]+out1[1]+out2[0]+out2[1]; + if (sample>32767) sample=32767; + if (sample<-32768) sample=-32768; + bufL[i]=sample; + } +} + +void DivPlatformNES::acquire(short* bufL, short* bufR, size_t start, size_t len) { + if (useNP) { + acquire_NSFPlay(bufL,bufR,start,len); + } else { + acquire_puNES(bufL,bufR,start,len); + } +} + static unsigned char noiseTable[253]={ 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 4, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, @@ -466,7 +504,7 @@ int DivPlatformNES::dispatch(DivCommand c) { void DivPlatformNES::muteChannel(int ch, bool mute) { isMuted[ch]=mute; - nes->muted[ch]=mute; + if (!useNP) nes->muted[ch]=mute; } void DivPlatformNES::forceIns() { @@ -513,10 +551,15 @@ void DivPlatformNES::reset() { dacSample=-1; sampleBank=0; - apu_turn_on(nes,apuType); + if (useNP) { + nes1_NP->Reset(); + nes2_NP->Reset(); + } else { + apu_turn_on(nes,apuType); + nes->apu.cpu_cycles=0; + nes->apu.cpu_opcode_cycle=0; + } memset(regPool,0,128); - nes->apu.cpu_cycles=0; - nes->apu.cpu_opcode_cycle=0; rWrite(0x4015,0x1f); rWrite(0x4001,chan[0].sweep); @@ -534,14 +577,20 @@ void DivPlatformNES::setFlags(unsigned int flags) { if (flags==2) { // Dendy rate=COLOR_PAL*2.0/5.0; apuType=2; - nes->apu.type=apuType; } else if (flags==1) { // PAL rate=COLOR_PAL*3.0/8.0; apuType=1; - nes->apu.type=apuType; } else { // NTSC rate=COLOR_NTSC/2.0; apuType=0; + } + if (useNP) { + nes1_NP->SetClock(rate); + nes1_NP->SetRate(rate); + nes2_NP->SetClock(rate); + nes2_NP->SetRate(rate); + nes2_NP->SetPal(apuType==1); + } else { nes->apu.type=apuType; } chipClock=rate; @@ -564,16 +613,31 @@ void DivPlatformNES::poke(std::vector& wlist) { for (DivRegWrite& i: wlist) rWrite(i.addr,i.val); } +void DivPlatformNES::setNSFPlay(bool use) { + useNP=use; +} + int DivPlatformNES::init(DivEngine* p, int channels, int sugRate, unsigned int flags) { parent=p; apuType=flags; dumpWrites=false; skipRegisterWrites=false; - nes=new struct NESAPU; + if (useNP) { + nes1_NP=new xgm::NES_APU; + nes1_NP->SetOption(xgm::NES_APU::OPT_NONLINEAR_MIXER,1); + nes2_NP=new xgm::NES_DMC; + nes2_NP->SetOption(xgm::NES_DMC::OPT_NONLINEAR_MIXER,1); + nes2_NP->SetMemory([](unsigned short addr, unsigned int& data) { + data=0; + }); + nes2_NP->SetAPU(nes1_NP); + } else { + nes=new struct NESAPU; + } writeOscBuf=0; for (int i=0; i<5; i++) { isMuted[i]=false; - nes->muted[i]=false; + if (!useNP) nes->muted[i]=false; oscBuf[i]=new DivDispatchOscBuffer; } setFlags(flags); @@ -587,7 +651,12 @@ void DivPlatformNES::quit() { for (int i=0; i<5; i++) { delete oscBuf[i]; } - delete nes; + if (useNP) { + delete nes1_NP; + delete nes2_NP; + } else { + delete nes; + } } DivPlatformNES::~DivPlatformNES() { diff --git a/src/engine/platform/nes.h b/src/engine/platform/nes.h index abd23da99..8b898c7fc 100644 --- a/src/engine/platform/nes.h +++ b/src/engine/platform/nes.h @@ -23,6 +23,8 @@ #include "../dispatch.h" #include "../macroInt.h" +#include "sound/nes_nsfplay/nes_apu.h" + class DivPlatformNES: public DivDispatch { struct Channel { int freq, baseFreq, pitch, pitch2, prevFreq, note, ins; @@ -66,11 +68,18 @@ class DivPlatformNES: public DivDispatch { unsigned char writeOscBuf; unsigned char apuType; bool dacAntiClickOn; + bool useNP; struct NESAPU* nes; + xgm::NES_APU* nes1_NP; + xgm::NES_DMC* nes2_NP; unsigned char regPool[128]; friend void putDispatchChan(void*,int,int); + void doWrite(unsigned short addr, unsigned char data); + void acquire_puNES(short* bufL, short* bufR, size_t start, size_t len); + void acquire_NSFPlay(short* bufL, short* bufR, size_t start, size_t len); + public: void acquire(short* bufL, short* bufR, size_t start, size_t len); int dispatch(DivCommand c); @@ -84,6 +93,7 @@ class DivPlatformNES: public DivDispatch { void muteChannel(int ch, bool mute); bool keyOffAffectsArp(int ch); float getPostAmp(); + void setNSFPlay(bool use); void setFlags(unsigned int flags); void notifyInsDeletion(void* ins); void poke(unsigned int addr, unsigned short val); diff --git a/src/engine/platform/sound/nes_nsfplay/nes_apu.h b/src/engine/platform/sound/nes_nsfplay/nes_apu.h index 004c99974..b302dd02c 100644 --- a/src/engine/platform/sound/nes_nsfplay/nes_apu.h +++ b/src/engine/platform/sound/nes_nsfplay/nes_apu.h @@ -68,16 +68,16 @@ namespace xgm void FrameSequence(int s); - virtual void Reset (); - virtual void Tick (unsigned int clocks); - virtual unsigned int Render (int b[2]); - virtual bool Read (unsigned int adr, unsigned int & val, unsigned int id=0); - virtual bool Write (unsigned int adr, unsigned int val, unsigned int id=0); - virtual void SetRate (double rate); - virtual void SetClock (double clock); - virtual void SetOption (int id, int b); - virtual void SetMask(int m){ mask = m; } - virtual void SetStereoMix (int trk, short mixl, short mixr); + void Reset (); + void Tick (unsigned int clocks); + unsigned int Render (int b[2]); + bool Read (unsigned int adr, unsigned int & val, unsigned int id=0); + bool Write (unsigned int adr, unsigned int val, unsigned int id=0); + void SetRate (double rate); + void SetClock (double clock); + void SetOption (int id, int b); + void SetMask(int m){ mask = m; } + void SetStereoMix (int trk, short mixl, short mixr); }; } // namespace diff --git a/src/engine/platform/sound/nes_nsfplay/nes_dmc.h b/src/engine/platform/sound/nes_nsfplay/nes_dmc.h index 4d19617d8..b0aa2ef92 100644 --- a/src/engine/platform/sound/nes_nsfplay/nes_dmc.h +++ b/src/engine/platform/sound/nes_nsfplay/nes_dmc.h @@ -102,16 +102,16 @@ namespace xgm int GetDamp(){ return (damp<<1)|dac_lsb ; } void TickFrameSequence (unsigned int clocks); - virtual void Reset (); - virtual void Tick (unsigned int clocks); - virtual unsigned int Render (int b[2]); - virtual bool Write (unsigned int adr, unsigned int val, unsigned int id=0); - virtual bool Read (unsigned int adr, unsigned int & val, unsigned int id=0); - virtual void SetRate (double rate); - virtual void SetClock (double rate); - virtual void SetOption (int, int); - virtual void SetMask(int m){ mask = m; } - virtual void SetStereoMix (int trk, short mixl, short mixr); + void Reset (); + void Tick (unsigned int clocks); + unsigned int Render (int b[2]); + bool Write (unsigned int adr, unsigned int val, unsigned int id=0); + bool Read (unsigned int adr, unsigned int & val, unsigned int id=0); + void SetRate (double rate); + void SetClock (double rate); + void SetOption (int, int); + void SetMask(int m){ mask = m; } + void SetStereoMix (int trk, short mixl, short mixr); }; } diff --git a/src/engine/platform/sound/nes_nsfplay/nes_fds.h b/src/engine/platform/sound/nes_nsfplay/nes_fds.h index 180841dd8..9185e4da6 100644 --- a/src/engine/platform/sound/nes_nsfplay/nes_fds.h +++ b/src/engine/platform/sound/nes_nsfplay/nes_fds.h @@ -56,16 +56,16 @@ public: NES_FDS (); ~NES_FDS (); - virtual void Reset (); - virtual void Tick (unsigned int clocks); - virtual unsigned int Render (int b[2]); - virtual bool Write (unsigned int adr, unsigned int val, unsigned int id=0); - virtual bool Read (unsigned int adr, unsigned int & val, unsigned int id=0); - virtual void SetRate (double); - virtual void SetClock (double); - virtual void SetOption (int, int); - virtual void SetMask(int m){ mask = m&1; } - virtual void SetStereoMix (int trk, short mixl, short mixr); + void Reset (); + void Tick (unsigned int clocks); + unsigned int Render (int b[2]); + bool Write (unsigned int adr, unsigned int val, unsigned int id=0); + bool Read (unsigned int adr, unsigned int & val, unsigned int id=0); + void SetRate (double); + void SetClock (double); + void SetOption (int, int); + void SetMask(int m){ mask = m&1; } + void SetStereoMix (int trk, short mixl, short mixr); }; } // namespace xgm diff --git a/src/engine/platform/sound/nes_nsfplay/nes_mmc5.h b/src/engine/platform/sound/nes_nsfplay/nes_mmc5.h index d0ae8bd0c..19d5ab269 100644 --- a/src/engine/platform/sound/nes_nsfplay/nes_mmc5.h +++ b/src/engine/platform/sound/nes_nsfplay/nes_mmc5.h @@ -50,16 +50,16 @@ namespace xgm void FrameSequence (); void TickFrameSequence (unsigned int clocks); - virtual void Reset (); - virtual void Tick (unsigned int clocks); - virtual unsigned int Render (int b[2]); - virtual bool Write (unsigned int adr, unsigned int val, unsigned int id=0); - virtual bool Read (unsigned int adr, unsigned int & val, unsigned int id=0); - virtual void SetOption (int id, int b); - virtual void SetClock (double); - virtual void SetRate (double); - virtual void SetMask (int m){ mask = m; } - virtual void SetStereoMix (int trk, short mixl, short mixr); + void Reset (); + void Tick (unsigned int clocks); + unsigned int Render (int b[2]); + bool Write (unsigned int adr, unsigned int val, unsigned int id=0); + bool Read (unsigned int adr, unsigned int & val, unsigned int id=0); + void SetOption (int id, int b); + void SetClock (double); + void SetRate (double); + void SetMask (int m){ mask = m; } + void SetStereoMix (int trk, short mixl, short mixr); }; } diff --git a/src/engine/platform/sound/nes_nsfplay/nes_n106.h b/src/engine/platform/sound/nes_nsfplay/nes_n106.h index 5ce937e26..a59dd6687 100644 --- a/src/engine/platform/sound/nes_nsfplay/nes_n106.h +++ b/src/engine/platform/sound/nes_nsfplay/nes_n106.h @@ -47,16 +47,16 @@ public: NES_N106 (); ~NES_N106 (); - virtual void Reset (); - virtual void Tick (unsigned int clocks); - virtual unsigned int Render (int b[2]); - virtual bool Write (unsigned int adr, unsigned int val, unsigned int id=0); - virtual bool Read (unsigned int adr, unsigned int & val, unsigned int id=0); - virtual void SetRate (double); - virtual void SetClock (double); - virtual void SetOption (int, int); - virtual void SetMask (int m); - virtual void SetStereoMix (int trk, short mixl, short mixr); + void Reset (); + void Tick (unsigned int clocks); + unsigned int Render (int b[2]); + bool Write (unsigned int adr, unsigned int val, unsigned int id=0); + bool Read (unsigned int adr, unsigned int & val, unsigned int id=0); + void SetRate (double); + void SetClock (double); + void SetOption (int, int); + void SetMask (int m); + void SetStereoMix (int trk, short mixl, short mixr); }; } // namespace xgm diff --git a/src/engine/platform/sound/nes_nsfplay/nes_vrc6.h b/src/engine/platform/sound/nes_nsfplay/nes_vrc6.h index 85fed39c4..198a8bd66 100644 --- a/src/engine/platform/sound/nes_nsfplay/nes_vrc6.h +++ b/src/engine/platform/sound/nes_nsfplay/nes_vrc6.h @@ -36,16 +36,16 @@ namespace xgm NES_VRC6 (); ~NES_VRC6 (); - virtual void Reset (); - virtual void Tick (unsigned int clocks); - virtual unsigned int Render (int b[2]); - virtual bool Read (unsigned int adr, unsigned int & val, unsigned int id=0); - virtual bool Write (unsigned int adr, unsigned int val, unsigned int id=0); - virtual void SetClock (double); - virtual void SetRate (double); - virtual void SetOption (int, int); - virtual void SetMask (int m){ mask = m; } - virtual void SetStereoMix (int trk, short mixl, short mixr); + void Reset (); + void Tick (unsigned int clocks); + unsigned int Render (int b[2]); + bool Read (unsigned int adr, unsigned int & val, unsigned int id=0); + bool Write (unsigned int adr, unsigned int val, unsigned int id=0); + void SetClock (double); + void SetRate (double); + void SetOption (int, int); + void SetMask (int m){ mask = m; } + void SetStereoMix (int trk, short mixl, short mixr); }; } // namespace diff --git a/src/gui/gui.h b/src/gui/gui.h index 520abdc41..49e56f0c2 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -788,6 +788,7 @@ class FurnaceGUI { int arcadeCore; int ym2612Core; int saaCore; + int nesCore; int mainFont; int patFont; int audioRate; @@ -870,6 +871,7 @@ class FurnaceGUI { arcadeCore(0), ym2612Core(0), saaCore(1), + nesCore(0), mainFont(0), patFont(0), audioRate(44100), diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index 152f94b8c..383fa4202 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -84,6 +84,11 @@ const char* saaCores[]={ "SAASound" }; +const char* nesCores[]={ + "puNES", + "NSFplay" +}; + const char* valueInputStyles[]={ "Disabled/custom", "Two octaves (0 is C-4, F is D#5)", @@ -859,6 +864,10 @@ void FurnaceGUI::drawSettings() { ImGui::SameLine(); ImGui::Combo("##SAACore",&settings.saaCore,saaCores,2); + ImGui::Text("NES core"); + ImGui::SameLine(); + ImGui::Combo("##NESCore",&settings.nesCore,nesCores,2); + ImGui::EndTabItem(); } if (ImGui::BeginTabItem("Appearance")) { @@ -1731,6 +1740,7 @@ void FurnaceGUI::syncSettings() { settings.arcadeCore=e->getConfInt("arcadeCore",0); settings.ym2612Core=e->getConfInt("ym2612Core",0); settings.saaCore=e->getConfInt("saaCore",1); + settings.nesCore=e->getConfInt("nesCore",0); settings.mainFont=e->getConfInt("mainFont",0); settings.patFont=e->getConfInt("patFont",0); settings.mainFontPath=e->getConfString("mainFontPath",""); @@ -1805,6 +1815,7 @@ void FurnaceGUI::syncSettings() { clampSetting(settings.arcadeCore,0,1); clampSetting(settings.ym2612Core,0,1); clampSetting(settings.saaCore,0,1); + clampSetting(settings.nesCore,0,1); clampSetting(settings.mainFont,0,6); clampSetting(settings.patFont,0,6); clampSetting(settings.patRowsBase,0,1); @@ -1907,6 +1918,7 @@ void FurnaceGUI::commitSettings() { e->setConf("arcadeCore",settings.arcadeCore); e->setConf("ym2612Core",settings.ym2612Core); e->setConf("saaCore",settings.saaCore); + e->setConf("nesCore",settings.nesCore); e->setConf("mainFont",settings.mainFont); e->setConf("patFont",settings.patFont); e->setConf("mainFontPath",settings.mainFontPath); From 9479a8aa0ff148d7a44d51b5e3534f99c6f83566 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 1 May 2022 23:29:00 -0500 Subject: [PATCH 280/342] fix typo in playback engine possible memory leak --- src/engine/playback.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/engine/playback.cpp b/src/engine/playback.cpp index cb4d663d1..48ffd08a0 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -1847,8 +1847,8 @@ void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsi } runtotal[i]=blip_clocks_needed(disCont[i].bb[0],size-lastAvail[i]); if (runtotal[i]>disCont[i].bbInLen) { - delete disCont[i].bbIn[0]; - delete disCont[i].bbIn[1]; + delete[] disCont[i].bbIn[0]; + delete[] disCont[i].bbIn[1]; disCont[i].bbIn[0]=new short[runtotal[i]+256]; disCont[i].bbIn[1]=new short[runtotal[i]+256]; disCont[i].bbInLen=runtotal[i]+256; From 153e025cf89841aabd1b9e47af2e592735eb21a4 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 1 May 2022 23:48:02 -0500 Subject: [PATCH 281/342] NES: finally --- src/engine/platform/nes.cpp | 1 + src/engine/platform/sound/nes_nsfplay/nes_dmc.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/engine/platform/nes.cpp b/src/engine/platform/nes.cpp index 084c1123c..9f44ebe05 100644 --- a/src/engine/platform/nes.cpp +++ b/src/engine/platform/nes.cpp @@ -145,6 +145,7 @@ void DivPlatformNES::acquire_NSFPlay(short* bufL, short* bufR, size_t start, siz doPCM; nes1_NP->Tick(1); + nes2_NP->TickFrameSequence(1); nes2_NP->Tick(1); nes1_NP->Render(out1); nes2_NP->Render(out2); diff --git a/src/engine/platform/sound/nes_nsfplay/nes_dmc.cpp b/src/engine/platform/sound/nes_nsfplay/nes_dmc.cpp index a37b544d2..65742d0e5 100644 --- a/src/engine/platform/sound/nes_nsfplay/nes_dmc.cpp +++ b/src/engine/platform/sound/nes_nsfplay/nes_dmc.cpp @@ -53,7 +53,7 @@ namespace xgm option[OPT_NONLINEAR_MIXER] = 1; option[OPT_RANDOMIZE_NOISE] = 1; option[OPT_RANDOMIZE_TRI] = 1; - option[OPT_TRI_MUTE] = 1; + option[OPT_TRI_MUTE] = 0; option[OPT_DPCM_REVERSE] = 0; tnd_table[0][0][0][0] = 0; tnd_table[1][0][0][0] = 0; From e1cec62af4686b6e0efc7fdf47e749f63bd38cd7 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 1 May 2022 23:48:35 -0500 Subject: [PATCH 282/342] NES: now fix tri_mute --- src/engine/platform/sound/nes_nsfplay/nes_dmc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/engine/platform/sound/nes_nsfplay/nes_dmc.cpp b/src/engine/platform/sound/nes_nsfplay/nes_dmc.cpp index 65742d0e5..a37b544d2 100644 --- a/src/engine/platform/sound/nes_nsfplay/nes_dmc.cpp +++ b/src/engine/platform/sound/nes_nsfplay/nes_dmc.cpp @@ -53,7 +53,7 @@ namespace xgm option[OPT_NONLINEAR_MIXER] = 1; option[OPT_RANDOMIZE_NOISE] = 1; option[OPT_RANDOMIZE_TRI] = 1; - option[OPT_TRI_MUTE] = 0; + option[OPT_TRI_MUTE] = 1; option[OPT_DPCM_REVERSE] = 0; tnd_table[0][0][0][0] = 0; tnd_table[1][0][0][0] = 0; From 61b4e3745ff82117dd546ac7b8cc8d41801070e3 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 1 May 2022 23:57:53 -0500 Subject: [PATCH 283/342] NES: NSFPlay muting --- src/engine/platform/nes.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/engine/platform/nes.cpp b/src/engine/platform/nes.cpp index 9f44ebe05..e353b26dc 100644 --- a/src/engine/platform/nes.cpp +++ b/src/engine/platform/nes.cpp @@ -150,7 +150,7 @@ void DivPlatformNES::acquire_NSFPlay(short* bufL, short* bufR, size_t start, siz nes1_NP->Render(out1); nes2_NP->Render(out2); - int sample=out1[0]+out1[1]+out2[0]+out2[1]; + int sample=(out1[0]+out1[1]+out2[0]+out2[1])<<1; if (sample>32767) sample=32767; if (sample<-32768) sample=-32768; bufL[i]=sample; @@ -505,7 +505,12 @@ int DivPlatformNES::dispatch(DivCommand c) { void DivPlatformNES::muteChannel(int ch, bool mute) { isMuted[ch]=mute; - if (!useNP) nes->muted[ch]=mute; + if (useNP) { + nes1_NP->SetMask(((int)isMuted[0])|(isMuted[1]<<1)); + nes2_NP->SetMask(((int)isMuted[2])|(isMuted[3]<<1)|(isMuted[4]<<2)); + } else { + nes->muted[ch]=mute; + } } void DivPlatformNES::forceIns() { @@ -555,6 +560,8 @@ void DivPlatformNES::reset() { if (useNP) { nes1_NP->Reset(); nes2_NP->Reset(); + nes1_NP->SetMask(((int)isMuted[0])|(isMuted[1]<<1)); + nes2_NP->SetMask(((int)isMuted[2])|(isMuted[3]<<1)|(isMuted[4]<<2)); } else { apu_turn_on(nes,apuType); nes->apu.cpu_cycles=0; From de09073232f20fe82166d85d2d73f1b58bc6c48b Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 2 May 2022 00:05:28 -0500 Subject: [PATCH 284/342] NES: NSFPlay per-channel osc --- src/engine/platform/nes.cpp | 8 ++++++++ src/engine/platform/sound/nes_nsfplay/nes_apu.h | 2 +- src/engine/platform/sound/nes_nsfplay/nes_dmc.h | 2 +- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/engine/platform/nes.cpp b/src/engine/platform/nes.cpp index e353b26dc..8ca8d1f1c 100644 --- a/src/engine/platform/nes.cpp +++ b/src/engine/platform/nes.cpp @@ -154,6 +154,14 @@ void DivPlatformNES::acquire_NSFPlay(short* bufL, short* bufR, size_t start, siz if (sample>32767) sample=32767; if (sample<-32768) sample=-32768; bufL[i]=sample; + if (++writeOscBuf>=32) { + writeOscBuf=0; + oscBuf[0]->data[oscBuf[0]->needle++]=nes1_NP->out[0]<<11; + oscBuf[1]->data[oscBuf[1]->needle++]=nes1_NP->out[1]<<11; + oscBuf[2]->data[oscBuf[2]->needle++]=nes2_NP->out[0]<<11; + oscBuf[3]->data[oscBuf[3]->needle++]=nes2_NP->out[1]<<11; + oscBuf[4]->data[oscBuf[4]->needle++]=nes2_NP->out[2]<<8; + } } } diff --git a/src/engine/platform/sound/nes_nsfplay/nes_apu.h b/src/engine/platform/sound/nes_nsfplay/nes_apu.h index b302dd02c..20c4d3663 100644 --- a/src/engine/platform/sound/nes_nsfplay/nes_apu.h +++ b/src/engine/platform/sound/nes_nsfplay/nes_apu.h @@ -27,7 +27,6 @@ namespace xgm unsigned int gclock; unsigned char reg[0x20]; - int out[2]; double rate, clock; int square_table[32]; // nonlinear mixer @@ -63,6 +62,7 @@ namespace xgm int calc_sqr (int ch, unsigned int clocks); public: + int out[2]; NES_APU (); ~NES_APU (); diff --git a/src/engine/platform/sound/nes_nsfplay/nes_dmc.h b/src/engine/platform/sound/nes_nsfplay/nes_dmc.h index b0aa2ef92..f78dd9f4a 100644 --- a/src/engine/platform/sound/nes_nsfplay/nes_dmc.h +++ b/src/engine/platform/sound/nes_nsfplay/nes_dmc.h @@ -36,7 +36,6 @@ namespace xgm unsigned int len_reg; unsigned int adr_reg; std::function memory; - unsigned int out[3]; unsigned int daddress; unsigned int dlength; unsigned int data; @@ -91,6 +90,7 @@ namespace xgm inline unsigned int calc_noise (unsigned int clocks); public: + unsigned int out[3]; NES_DMC (); ~NES_DMC (); From 430d0329bd3274a5ab00a483c52acf9988d4b160 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 2 May 2022 00:22:34 -0500 Subject: [PATCH 285/342] per-channel oscilloscope, part 10 OPZ --- src/engine/platform/sound/ymfm/ymfm_opz.h | 3 +++ src/engine/platform/tx81z.cpp | 14 ++++++++++++++ src/engine/platform/tx81z.h | 2 ++ 3 files changed, 19 insertions(+) diff --git a/src/engine/platform/sound/ymfm/ymfm_opz.h b/src/engine/platform/sound/ymfm/ymfm_opz.h index 4bc4663a0..5be148a38 100644 --- a/src/engine/platform/sound/ymfm/ymfm_opz.h +++ b/src/engine/platform/sound/ymfm/ymfm_opz.h @@ -320,6 +320,9 @@ public: // generate one sample of sound void generate(output_data *output, uint32_t numsamples = 1); + // get the engine + fm_engine* debug_engine() { return &m_fm; } + protected: // internal state uint8_t m_address; // address register diff --git a/src/engine/platform/tx81z.cpp b/src/engine/platform/tx81z.cpp index d69dfc875..90f1d738b 100644 --- a/src/engine/platform/tx81z.cpp +++ b/src/engine/platform/tx81z.cpp @@ -147,6 +147,8 @@ const char* DivPlatformTX81Z::getEffectName(unsigned char effect) { void DivPlatformTX81Z::acquire(short* bufL, short* bufR, size_t start, size_t len) { static int os[2]; + ymfm::ym2414::fm_engine* fme=fm_ymfm->debug_engine(); + for (size_t h=start; hgenerate(&out_ymfm); + for (int i=0; i<8; i++) { + oscBuf[i]->data[oscBuf[i]->needle++]=(fme->debug_channel(i)->debug_output(0)+fme->debug_channel(i)->debug_output(1)); + } + os[0]=out_ymfm.data[0]; if (os[0]<-32768) os[0]=-32768; if (os[0]>32767) os[0]=32767; @@ -714,6 +720,10 @@ void* DivPlatformTX81Z::getChanState(int ch) { return &chan[ch]; } +DivDispatchOscBuffer* DivPlatformTX81Z::getOscBuffer(int ch) { + return oscBuf[ch]; +} + unsigned char* DivPlatformTX81Z::getRegisterPool() { return regPool; } @@ -789,6 +799,7 @@ int DivPlatformTX81Z::init(DivEngine* p, int channels, int sugRate, unsigned int skipRegisterWrites=false; for (int i=0; i<8; i++) { isMuted[i]=false; + oscBuf[i]=new DivDispatchOscBuffer; } setFlags(flags); fm_ymfm=new ymfm::ym2414(iface); @@ -798,6 +809,9 @@ int DivPlatformTX81Z::init(DivEngine* p, int channels, int sugRate, unsigned int } void DivPlatformTX81Z::quit() { + for (int i=0; i<8; i++) { + delete oscBuf[i]; + } delete fm_ymfm; } diff --git a/src/engine/platform/tx81z.h b/src/engine/platform/tx81z.h index 5df591f64..60ea66ae0 100644 --- a/src/engine/platform/tx81z.h +++ b/src/engine/platform/tx81z.h @@ -68,6 +68,7 @@ class DivPlatformTX81Z: public DivDispatch { chVolR(127) {} }; Channel chan[8]; + DivDispatchOscBuffer* oscBuf[8]; struct QueuedWrite { unsigned short addr; unsigned char val; @@ -102,6 +103,7 @@ class DivPlatformTX81Z: public DivDispatch { void acquire(short* bufL, short* bufR, size_t start, size_t len); int dispatch(DivCommand c); void* getChanState(int chan); + DivDispatchOscBuffer* getOscBuffer(int chan); unsigned char* getRegisterPool(); int getRegisterPoolSize(); void reset(); From 7a67730c291925e5408b3a596a5899e79f41736e Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 2 May 2022 00:23:03 -0500 Subject: [PATCH 286/342] damn it --- src/engine/platform/tx81z.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/engine/platform/tx81z.cpp b/src/engine/platform/tx81z.cpp index 90f1d738b..4391d49eb 100644 --- a/src/engine/platform/tx81z.cpp +++ b/src/engine/platform/tx81z.cpp @@ -787,6 +787,9 @@ void DivPlatformTX81Z::setFlags(unsigned int flags) { baseFreqOff=0; } rate=chipClock/64; + for (int i=0; i<8; i++) { + oscBuf[i]->rate=rate; + } } bool DivPlatformTX81Z::isStereo() { From 6380876b9a5557daf4ef96cb57d1efb63c0f34c3 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 2 May 2022 00:41:58 -0500 Subject: [PATCH 287/342] OPL: fix panning --- src/engine/platform/opl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/engine/platform/opl.cpp b/src/engine/platform/opl.cpp index 33c59eeb2..dfe83d364 100644 --- a/src/engine/platform/opl.cpp +++ b/src/engine/platform/opl.cpp @@ -705,7 +705,7 @@ int DivPlatformOPL::dispatch(DivCommand c) { if (c.value==0 && c.value2==0) { chan[c.chan].pan=3; } else { - chan[c.chan].pan=(c.value2>0)|((c.value>0)<<1); + chan[c.chan].pan=(c.value>0)|((c.value2>0)<<1); } int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2; if (isMuted[c.chan]) { From 34f7750c27112a7315059f27f64a05b8a0c51dab Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 2 May 2022 01:15:42 -0500 Subject: [PATCH 288/342] OPZ: fix muting --- src/engine/platform/tx81z.cpp | 114 +++++++++++++++++++--------------- 1 file changed, 64 insertions(+), 50 deletions(-) diff --git a/src/engine/platform/tx81z.cpp b/src/engine/platform/tx81z.cpp index 4391d49eb..0f02c5e22 100644 --- a/src/engine/platform/tx81z.cpp +++ b/src/engine/platform/tx81z.cpp @@ -198,10 +198,14 @@ void DivPlatformTX81Z::tick(bool sysTick) { for (int j=0; j<4; j++) { unsigned short baseAddr=chanOffs[i]|opOffs[j]; DivInstrumentFM::Operator& op=chan[i].state.op[j]; - if (isOutput[chan[i].state.alg][j]) { - rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127)); + if (isMuted[i]) { + rWrite(baseAddr+ADDR_TL,127); } else { - rWrite(baseAddr+ADDR_TL,op.tl); + if (isOutput[chan[i].state.alg][j]) { + rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127)); + } else { + rWrite(baseAddr+ADDR_TL,op.tl); + } } } } @@ -266,11 +270,7 @@ void DivPlatformTX81Z::tick(bool sysTick) { if (chan[i].std.alg.had) { chan[i].state.alg=chan[i].std.alg.val; - if (isMuted[i]) { - immWrite(chanOffs[i]+ADDR_LR_FB_ALG,(chan[i].state.alg&7)|(chan[i].state.fb<<3)|0x40); - } else { - immWrite(chanOffs[i]+ADDR_LR_FB_ALG,(chan[i].state.alg&7)|(chan[i].state.fb<<3)|(chan[i].active?0:0x40)|(chan[i].chVolR<<7)); - } + immWrite(chanOffs[i]+ADDR_LR_FB_ALG,(chan[i].state.alg&7)|(chan[i].state.fb<<3)|(chan[i].active?0:0x40)|(chan[i].chVolR<<7)); if (!parent->song.algMacroBehavior) for (int j=0; j<4; j++) { unsigned short baseAddr=chanOffs[i]|opOffs[j]; DivInstrumentFM::Operator& op=chan[i].state.op[j]; @@ -287,11 +287,7 @@ void DivPlatformTX81Z::tick(bool sysTick) { } if (chan[i].std.fb.had) { chan[i].state.fb=chan[i].std.fb.val; - if (isMuted[i]) { - immWrite(chanOffs[i]+ADDR_LR_FB_ALG,(chan[i].state.alg&7)|(chan[i].state.fb<<3)|0x40); - } else { - immWrite(chanOffs[i]+ADDR_LR_FB_ALG,(chan[i].state.alg&7)|(chan[i].state.fb<<3)|(chan[i].active?0:0x40)|(chan[i].chVolR<<7)); - } + immWrite(chanOffs[i]+ADDR_LR_FB_ALG,(chan[i].state.alg&7)|(chan[i].state.fb<<3)|(chan[i].active?0:0x40)|(chan[i].chVolR<<7)); } if (chan[i].std.fms.had) { chan[i].state.fms=chan[i].std.fms.val; @@ -331,10 +327,14 @@ void DivPlatformTX81Z::tick(bool sysTick) { } if (m.tl.had) { op.tl=127-m.tl.val; - if (isOutput[chan[i].state.alg][j]) { - rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127)); + if (isMuted[i]) { + rWrite(baseAddr+ADDR_TL,127); } else { - rWrite(baseAddr+ADDR_TL,op.tl); + if (isOutput[chan[i].state.alg][j]) { + rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127)); + } else { + rWrite(baseAddr+ADDR_TL,op.tl); + } } } if (m.rs.had) { @@ -364,12 +364,8 @@ void DivPlatformTX81Z::tick(bool sysTick) { oldWrites[baseAddr+ADDR_TL]=-1; } } - if (isMuted[i]) { - immWrite(chanOffs[i]+ADDR_LR_FB_ALG,(chan[i].state.alg&7)|(chan[i].state.fb<<3)|0x00); - } else { - //if (chan[i].keyOn) immWrite(0x08,i); - immWrite(chanOffs[i]+ADDR_LR_FB_ALG,(chan[i].state.alg&7)|(chan[i].state.fb<<3)|0x00|(chan[i].chVolR<<7)); - } + //if (chan[i].keyOn) immWrite(0x08,i); + immWrite(chanOffs[i]+ADDR_LR_FB_ALG,(chan[i].state.alg&7)|(chan[i].state.fb<<3)|0x00|(chan[i].chVolR<<7)); if (chan[i].hardReset && chan[i].keyOn) { for (int j=0; j<4; j++) { unsigned short baseAddr=chanOffs[i]|opOffs[j]; @@ -411,12 +407,8 @@ void DivPlatformTX81Z::tick(bool sysTick) { chan[i].freqChanged=false; } if (chan[i].keyOn) { - if (isMuted[i]) { - immWrite(chanOffs[i]+ADDR_LR_FB_ALG,(chan[i].state.alg&7)|(chan[i].state.fb<<3)); - } else { - //immWrite(0x08,i); - immWrite(chanOffs[i]+ADDR_LR_FB_ALG,(chan[i].state.alg&7)|(chan[i].state.fb<<3)|0x40|(chan[i].chVolR<<7)); - } + //immWrite(0x08,i); + immWrite(chanOffs[i]+ADDR_LR_FB_ALG,(chan[i].state.alg&7)|(chan[i].state.fb<<3)|0x40|(chan[i].chVolR<<7)); chan[i].keyOn=false; } } @@ -424,13 +416,19 @@ void DivPlatformTX81Z::tick(bool sysTick) { void DivPlatformTX81Z::muteChannel(int ch, bool mute) { isMuted[ch]=mute; - // TODO: use volume registers! - /* - if (isMuted[ch]) { - immWrite(chanOffs[ch]+ADDR_LR_FB_ALG,(chan[ch].state.alg&7)|(chan[ch].state.fb<<3)); - } else { - immWrite(chanOffs[ch]+ADDR_LR_FB_ALG,(chan[ch].state.alg&7)|(chan[ch].state.fb<<3)|((chan[ch].chVolL&1)<<6)|((chan[ch].chVolR&1)<<7)); - }*/ + for (int i=0; i<4; i++) { + unsigned short baseAddr=chanOffs[ch]|opOffs[i]; + DivInstrumentFM::Operator op=chan[ch].state.op[i]; + if (isMuted[ch]) { + rWrite(baseAddr+ADDR_TL,127); + } else { + if (isOutput[chan[ch].state.alg][i]) { + rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[ch].outVol&0x7f))/127)); + } else { + rWrite(baseAddr+ADDR_TL,op.tl); + } + } + } } int DivPlatformTX81Z::dispatch(DivCommand c) { @@ -450,13 +448,17 @@ int DivPlatformTX81Z::dispatch(DivCommand c) { for (int i=0; i<4; i++) { unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; DivInstrumentFM::Operator op=chan[c.chan].state.op[i]; - if (isOutput[chan[c.chan].state.alg][i]) { - if (!chan[c.chan].active || chan[c.chan].insChanged) { - rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[c.chan].outVol&0x7f))/127)); - } + if (isMuted[c.chan]) { + rWrite(baseAddr+ADDR_TL,127); } else { - if (chan[c.chan].insChanged) { - rWrite(baseAddr+ADDR_TL,op.tl); + if (isOutput[chan[c.chan].state.alg][i]) { + if (!chan[c.chan].active || chan[c.chan].insChanged) { + rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[c.chan].outVol&0x7f))/127)); + } + } else { + if (chan[c.chan].insChanged) { + rWrite(baseAddr+ADDR_TL,op.tl); + } } } if (chan[c.chan].insChanged) { @@ -512,10 +514,14 @@ int DivPlatformTX81Z::dispatch(DivCommand c) { for (int i=0; i<4; i++) { unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; - if (isOutput[chan[c.chan].state.alg][i]) { - rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[c.chan].outVol&0x7f))/127)); + if (isMuted[c.chan]) { + rWrite(baseAddr+ADDR_TL,127); } else { - rWrite(baseAddr+ADDR_TL,op.tl); + if (isOutput[chan[c.chan].state.alg][i]) { + rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[c.chan].outVol&0x7f))/127)); + } else { + rWrite(baseAddr+ADDR_TL,op.tl); + } } } break; @@ -606,10 +612,14 @@ int DivPlatformTX81Z::dispatch(DivCommand c) { unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; op.tl=c.value2; - if (isOutput[chan[c.chan].state.alg][c.value]) { - rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[c.chan].outVol&0x7f))/127)); + if (isMuted[c.chan]) { + rWrite(baseAddr+ADDR_TL,127); } else { - rWrite(baseAddr+ADDR_TL,op.tl); + if (isOutput[chan[c.chan].state.alg][c.value]) { + rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[c.chan].outVol&0x7f))/127)); + } else { + rWrite(baseAddr+ADDR_TL,op.tl); + } } break; } @@ -678,10 +688,14 @@ void DivPlatformTX81Z::forceIns() { for (int j=0; j<4; j++) { unsigned short baseAddr=chanOffs[i]|opOffs[j]; DivInstrumentFM::Operator op=chan[i].state.op[j]; - if (isOutput[chan[i].state.alg][j]) { - rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127)); + if (isMuted[i]) { + rWrite(baseAddr+ADDR_TL,127); } else { - rWrite(baseAddr+ADDR_TL,op.tl); + if (isOutput[chan[i].state.alg][j]) { + rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127)); + } else { + rWrite(baseAddr+ADDR_TL,op.tl); + } } rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4)); rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.egt<<5)|(op.rs<<6)); From a0db8ab49af873cd10a714e6032000d428cb25e0 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 2 May 2022 01:22:49 -0500 Subject: [PATCH 289/342] OPZ: possibly fix fixed freq emulation --- src/engine/platform/sound/ymfm/ymfm_opz.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/engine/platform/sound/ymfm/ymfm_opz.cpp b/src/engine/platform/sound/ymfm/ymfm_opz.cpp index 62a3d4d9c..cc7a7c9d5 100644 --- a/src/engine/platform/sound/ymfm/ymfm_opz.cpp +++ b/src/engine/platform/sound/ymfm/ymfm_opz.cpp @@ -498,7 +498,7 @@ uint32_t opz_registers::compute_phase_step(uint32_t choffs, uint32_t opoffs, opd // additional 12 bits of resolution; this calculation gives us, for // example, a frequency of 8.0009Hz when 8Hz is requested uint32_t substep = m_phase_substep[opoffs]; - substep += 75 * freq; + substep += 75 * 1024 * freq; phase_step = substep >> 12; m_phase_substep[opoffs] = substep & 0xfff; From b92ce84b3463d0acbe4bb4b8d64d628a1060a3a9 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 2 May 2022 02:12:02 -0500 Subject: [PATCH 290/342] OPZ: more fixed frequency mode fixes --- src/engine/platform/tx81z.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/engine/platform/tx81z.cpp b/src/engine/platform/tx81z.cpp index 0f02c5e22..788cfe245 100644 --- a/src/engine/platform/tx81z.cpp +++ b/src/engine/platform/tx81z.cpp @@ -315,7 +315,7 @@ void DivPlatformTX81Z::tick(bool sysTick) { } if (m.mult.had) { op.mult=m.mult.val; - rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4)); + rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|((op.egt?(op.dt&7):dtTable[op.dt&7])<<4)); } if (m.rr.had) { op.rr=m.rr.val; @@ -343,7 +343,7 @@ void DivPlatformTX81Z::tick(bool sysTick) { } if (m.dt.had) { op.dt=m.dt.val; - rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4)); + rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|((op.egt?(op.dt&7):dtTable[op.dt&7])<<4)); } if (m.d2r.had) { op.d2r=m.d2r.val; @@ -462,7 +462,7 @@ int DivPlatformTX81Z::dispatch(DivCommand c) { } } if (chan[c.chan].insChanged) { - rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4)); + rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|((op.egt?(op.dt&7):dtTable[op.dt&7])<<4)); rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.egt<<5)|(op.rs<<6)); rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7)); rWrite(baseAddr+ADDR_DT2_D2R,(op.d2r&31)|(op.dt2<<6)); @@ -605,7 +605,7 @@ int DivPlatformTX81Z::dispatch(DivCommand c) { unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; op.mult=c.value2&15; - rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4)); + rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|((op.egt?(op.dt&7):dtTable[op.dt&7])<<4)); break; } case DIV_CMD_FM_TL: { @@ -697,7 +697,7 @@ void DivPlatformTX81Z::forceIns() { rWrite(baseAddr+ADDR_TL,op.tl); } } - rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4)); + rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|((op.egt?(op.dt&7):dtTable[op.dt&7])<<4)); rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.egt<<5)|(op.rs<<6)); rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7)); rWrite(baseAddr+ADDR_DT2_D2R,(op.d2r&31)|(op.dt2<<6)); From a7b8f81da71e8667e1f1ce58560d61134b556359 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 2 May 2022 02:12:14 -0500 Subject: [PATCH 291/342] NES: prepare for DPCM --- src/engine/platform/nes.cpp | 15 +++++++++++++-- src/engine/platform/nes.h | 1 + src/engine/platform/sound/nes/apu.h | 4 +++- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/engine/platform/nes.cpp b/src/engine/platform/nes.cpp index 8ca8d1f1c..6f71cad52 100644 --- a/src/engine/platform/nes.cpp +++ b/src/engine/platform/nes.cpp @@ -53,6 +53,10 @@ const char* regCheatSheetNES[]={ NULL }; +unsigned char _readDMC(void* user, unsigned short addr) { + return ((DivPlatformNES*)user)->readDMC(addr); +} + const char** DivPlatformNES::getRegisterSheet() { return regCheatSheetNES; } @@ -633,6 +637,11 @@ void DivPlatformNES::setNSFPlay(bool use) { useNP=use; } +unsigned char DivPlatformNES::readDMC(unsigned short addr) { + printf("read from DMC! %x\n",addr); + return 0; +} + int DivPlatformNES::init(DivEngine* p, int channels, int sugRate, unsigned int flags) { parent=p; apuType=flags; @@ -643,12 +652,14 @@ int DivPlatformNES::init(DivEngine* p, int channels, int sugRate, unsigned int f nes1_NP->SetOption(xgm::NES_APU::OPT_NONLINEAR_MIXER,1); nes2_NP=new xgm::NES_DMC; nes2_NP->SetOption(xgm::NES_DMC::OPT_NONLINEAR_MIXER,1); - nes2_NP->SetMemory([](unsigned short addr, unsigned int& data) { - data=0; + nes2_NP->SetMemory([this](unsigned short addr, unsigned int& data) { + data=readDMC(addr); }); nes2_NP->SetAPU(nes1_NP); } else { nes=new struct NESAPU; + nes->readDMC=_readDMC; + nes->readDMCUser=this; } writeOscBuf=0; for (int i=0; i<5; i++) { diff --git a/src/engine/platform/nes.h b/src/engine/platform/nes.h index 8b898c7fc..cb58c3869 100644 --- a/src/engine/platform/nes.h +++ b/src/engine/platform/nes.h @@ -93,6 +93,7 @@ class DivPlatformNES: public DivDispatch { void muteChannel(int ch, bool mute); bool keyOffAffectsArp(int ch); float getPostAmp(); + unsigned char readDMC(unsigned short addr); void setNSFPlay(bool use); void setFlags(unsigned int flags); void notifyInsDeletion(void* ins); diff --git a/src/engine/platform/sound/nes/apu.h b/src/engine/platform/sound/nes/apu.h index 224ea2a04..d0f9c31f9 100644 --- a/src/engine/platform/sound/nes/apu.h +++ b/src/engine/platform/sound/nes/apu.h @@ -207,7 +207,7 @@ enum apu_mode { APU_60HZ, APU_48HZ }; break;\ }\ {\ - a->DMC.buffer = 0;\ + a->DMC.buffer = a->readDMC(a->readDMCUser,a->DMC.address);\ }\ /* incremento gli hwtick da compiere */\ if (hwtick) { hwtick[0] += tick; }\ @@ -525,6 +525,8 @@ EXTERNC struct NESAPU { _apuTriangle TR; _apuNoise NS; _apuDMC DMC; + void* readDMCUser; + unsigned char (*readDMC)(void*,unsigned short); unsigned char muted[5]; }; From dbe9bf25e7b2615b3890eae2ee00a26cb1318bed Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 2 May 2022 02:54:23 -0500 Subject: [PATCH 292/342] fix bug caused by new renderSamples approach when doing switchMaster --- src/engine/engine.cpp | 1 + src/engine/platform/nes.cpp | 83 +++++++++++++++++++++++++++++++++---- src/engine/platform/nes.h | 8 ++++ src/engine/playback.cpp | 3 ++ 4 files changed, 86 insertions(+), 9 deletions(-) diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index b37ed86cd..364b0d135 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -2366,6 +2366,7 @@ bool DivEngine::switchMaster() { } else { return false; } + renderSamples(); return true; } diff --git a/src/engine/platform/nes.cpp b/src/engine/platform/nes.cpp index 6f71cad52..9e87e2a5d 100644 --- a/src/engine/platform/nes.cpp +++ b/src/engine/platform/nes.cpp @@ -20,7 +20,8 @@ #include "nes.h" #include "sound/nes/cpu_inline.h" #include "../engine.h" -#include +#include "../../ta-log.h" +#include #include struct _nla_table nla_table; @@ -75,6 +76,9 @@ const char* DivPlatformNES::getEffectName(unsigned char effect) { case 0x14: return "14xy: Sweep down (x: time; y: shift)"; break; + case 0x18: + return "18xx: Select PCM/DPCM mode (0: PCM; 1: DPCM)"; + break; } return NULL; } @@ -89,7 +93,7 @@ void DivPlatformNES::doWrite(unsigned short addr, unsigned char data) { } #define doPCM \ - if (dacSample!=-1) { \ + if (!dpcmMode && dacSample!=-1) { \ dacPeriod+=dacRate; \ if (dacPeriod>=rate) { \ DivSample* s=parent->getSample(dacSample); \ @@ -344,10 +348,10 @@ int DivPlatformNES::dispatch(DivCommand c) { dacSample=ins->amiga.initSample; if (dacSample<0 || dacSample>=parent->song.sampleLen) { dacSample=-1; - if (dumpWrites) addWrite(0xffff0002,0); + if (dumpWrites && !dpcmMode) addWrite(0xffff0002,0); break; } else { - if (dumpWrites) addWrite(0xffff0000,dacSample); + if (dumpWrites && !dpcmMode) addWrite(0xffff0000,dacSample); } dacPos=0; dacPeriod=0; @@ -366,16 +370,29 @@ int DivPlatformNES::dispatch(DivCommand c) { dacSample=12*sampleBank+chan[c.chan].note%12; if (dacSample>=parent->song.sampleLen) { dacSample=-1; - if (dumpWrites) addWrite(0xffff0002,0); + if (dumpWrites && !dpcmMode) addWrite(0xffff0002,0); break; } else { - if (dumpWrites) addWrite(0xffff0000,dacSample); + if (dumpWrites && !dpcmMode) addWrite(0xffff0000,dacSample); } dacPos=0; dacPeriod=0; dacRate=parent->getSample(dacSample)->rate; - if (dumpWrites) addWrite(0xffff0001,dacRate); + if (dumpWrites && !dpcmMode) addWrite(0xffff0001,dacRate); chan[c.chan].furnaceDac=false; + if (dpcmMode && !skipRegisterWrites) { + unsigned int dpcmAddr=parent->getSample(dacSample)->offDPCM; + unsigned int dpcmLen=(parent->getSample(dacSample)->lengthDPCM+15)>>4; + if (dpcmLen>255) dpcmLen=255; + // write DPCM + rWrite(0x4015,15); + rWrite(0x4010,15); + rWrite(0x4012,(dpcmAddr>>6)&0xff); + rWrite(0x4013,dpcmLen&0xff); + rWrite(0x4015,31); + dpcmBank=dpcmAddr>>14; + logV("writing DPCM: %x %x",dpcmAddr,dpcmLen); + } } break; } else if (c.chan==3) { // noise @@ -484,6 +501,10 @@ int DivPlatformNES::dispatch(DivCommand c) { break; case DIV_CMD_NES_DMC: rWrite(0x4011,c.value&0x7f); + if (dumpWrites && dpcmMode) addWrite(0xffff0002,0); + break; + case DIV_CMD_SAMPLE_MODE: + dpcmMode=c.value; break; case DIV_CMD_SAMPLE_BANK: sampleBank=c.value; @@ -568,6 +589,8 @@ void DivPlatformNES::reset() { dacRate=0; dacSample=-1; sampleBank=0; + dpcmBank=0; + dpcmMode=false; if (useNP) { nes1_NP->Reset(); @@ -638,8 +661,46 @@ void DivPlatformNES::setNSFPlay(bool use) { } unsigned char DivPlatformNES::readDMC(unsigned short addr) { - printf("read from DMC! %x\n",addr); - return 0; + return dpcmMem[(addr&0x3fff)|((dpcmBank&15)<<14)]; +} + +const void* DivPlatformNES::getSampleMem(int index) { + return index==0?dpcmMem:NULL; +} + +size_t DivPlatformNES::getSampleMemCapacity(int index) { + return index==0?262144:0; +} + +size_t DivPlatformNES::getSampleMemUsage(int index) { + return index==0?dpcmMemLen:0; +} + +void DivPlatformNES::renderSamples() { + memset(dpcmMem,0,getSampleMemCapacity(0)); + + size_t memPos=0; + for (int i=0; isong.sampleLen; i++) { + DivSample* s=parent->song.sample[i]; + int paddedLen=(s->lengthDPCM+63)&(~0xff); + logV("%d padded length: %d",i,paddedLen); + if ((memPos&0x4000)!=((memPos+paddedLen)&0x4000)) { + memPos=(memPos+0x3fff)&0x4000; + } + if (memPos>=getSampleMemCapacity(0)) { + logW("out of DPCM memory for sample %d!",i); + break; + } + if (memPos+paddedLen>=getSampleMemCapacity(0)) { + memcpy(dpcmMem+memPos,s->dataDPCM,getSampleMemCapacity(0)-memPos); + logW("out of DPCM memory for sample %d!",i); + } else { + memcpy(dpcmMem+memPos,s->dataDPCM,paddedLen); + } + s->offDPCM=memPos; + memPos+=paddedLen; + } + dpcmMemLen=memPos; } int DivPlatformNES::init(DivEngine* p, int channels, int sugRate, unsigned int flags) { @@ -669,6 +730,10 @@ int DivPlatformNES::init(DivEngine* p, int channels, int sugRate, unsigned int f } setFlags(flags); + dpcmMem=new unsigned char[262144]; + dpcmMemLen=0; + dpcmBank=0; + init_nla_table(500,500); reset(); return 5; diff --git a/src/engine/platform/nes.h b/src/engine/platform/nes.h index cb58c3869..75205548a 100644 --- a/src/engine/platform/nes.h +++ b/src/engine/platform/nes.h @@ -64,9 +64,13 @@ class DivPlatformNES: public DivDispatch { int dacPeriod, dacRate; unsigned int dacPos, dacAntiClick; int dacSample; + unsigned char* dpcmMem; + size_t dpcmMemLen; + unsigned char dpcmBank; unsigned char sampleBank; unsigned char writeOscBuf; unsigned char apuType; + bool dpcmMode; bool dacAntiClickOn; bool useNP; struct NESAPU* nes; @@ -101,6 +105,10 @@ class DivPlatformNES: public DivDispatch { void poke(std::vector& wlist); const char** getRegisterSheet(); const char* getEffectName(unsigned char effect); + const void* getSampleMem(int index); + size_t getSampleMemCapacity(int index); + size_t getSampleMemUsage(int index); + void renderSamples(); int init(DivEngine* parent, int channels, int sugRate, unsigned int flags); void quit(); ~DivPlatformNES(); diff --git a/src/engine/playback.cpp b/src/engine/playback.cpp index 48ffd08a0..f478f9a1a 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -330,6 +330,9 @@ bool DivEngine::perSystemEffect(int ch, unsigned char effect, unsigned char effe case 0x14: // sweep down dispatchCmd(DivCommand(DIV_CMD_NES_SWEEP,ch,1,effectVal)); break; + case 0x18: // DPCM mode + dispatchCmd(DivCommand(DIV_CMD_SAMPLE_MODE,ch,effectVal)); + break; default: return false; } From 5a724e49494d5e6a7da3267c316b77a3151b21c7 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 2 May 2022 03:42:40 -0500 Subject: [PATCH 293/342] NES: DPCM work! --- papers/doc/7-systems/nes.md | 6 +++- src/engine/platform/nes.cpp | 57 ++++++++++++++++++++++++++++++++----- src/engine/platform/nes.h | 1 + src/gui/debugWindow.cpp | 45 +++++++++++++++++++++++++++++ 4 files changed, 101 insertions(+), 8 deletions(-) diff --git a/papers/doc/7-systems/nes.md b/papers/doc/7-systems/nes.md index 45ce95bc3..3ccca2647 100644 --- a/papers/doc/7-systems/nes.md +++ b/papers/doc/7-systems/nes.md @@ -18,4 +18,8 @@ also known as Famicom. It is a five-channel PSG: first two channels play pulse w - `14xy`: setup sweep down. - `x` is the time. - `y` is the shift. - - set to 0 to disable it. \ No newline at end of file + - set to 0 to disable it. +- `18xx`: set PCM channel mode. + - `00`: PCM (software). + - `01`: DPCM (hardware). + - when in DPCM mode, samples will sound muffled (due to its nature), availables pitches are limited and loop point is ignored. \ No newline at end of file diff --git a/src/engine/platform/nes.cpp b/src/engine/platform/nes.cpp index 9e87e2a5d..0e336df0e 100644 --- a/src/engine/platform/nes.cpp +++ b/src/engine/platform/nes.cpp @@ -206,6 +206,25 @@ static unsigned char noiseTable[253]={ 15 }; +unsigned char DivPlatformNES::calcDPCMRate(int inRate) { + if (inRate<4450) return 0; + if (inRate<5000) return 1; + if (inRate<5400) return 2; + if (inRate<5900) return 3; + if (inRate<6650) return 4; + if (inRate<7450) return 5; + if (inRate<8100) return 6; + if (inRate<8800) return 7; + if (inRate<10200) return 8; + if (inRate<11700) return 9; + if (inRate<13300) return 10; + if (inRate<15900) return 11; + if (inRate<18900) return 12; + if (inRate<23500) return 13; + if (inRate<29000) return 14; + return 15; +} + void DivPlatformNES::tick(bool sysTick) { for (int i=0; i<4; i++) { chan[i].std.next(); @@ -333,6 +352,9 @@ void DivPlatformNES::tick(bool sysTick) { off=(double)s->centerRate/8363.0; } dacRate=MIN(chan[4].freq*off,32000); + if (dpcmMode && !skipRegisterWrites) { + rWrite(0x4010,calcDPCMRate(dacRate)); + } if (dumpWrites) addWrite(0xffff0001,dacRate); } chan[4].freqChanged=false; @@ -363,6 +385,18 @@ int DivPlatformNES::dispatch(DivCommand c) { chan[c.chan].active=true; chan[c.chan].keyOn=true; chan[c.chan].furnaceDac=true; + if (dpcmMode && !skipRegisterWrites) { + unsigned int dpcmAddr=parent->getSample(dacSample)->offDPCM; + unsigned int dpcmLen=(parent->getSample(dacSample)->lengthDPCM+15)>>4; + if (dpcmLen>255) dpcmLen=255; + // write DPCM + rWrite(0x4015,15); + rWrite(0x4010,calcDPCMRate(chan[c.chan].baseFreq)); + rWrite(0x4012,(dpcmAddr>>6)&0xff); + rWrite(0x4013,dpcmLen&0xff); + rWrite(0x4015,31); + dpcmBank=dpcmAddr>>14; + } } else { if (c.value!=DIV_NOTE_NULL) { chan[c.chan].note=c.value; @@ -386,12 +420,11 @@ int DivPlatformNES::dispatch(DivCommand c) { if (dpcmLen>255) dpcmLen=255; // write DPCM rWrite(0x4015,15); - rWrite(0x4010,15); + rWrite(0x4010,calcDPCMRate(dacRate)); rWrite(0x4012,(dpcmAddr>>6)&0xff); rWrite(0x4013,dpcmLen&0xff); rWrite(0x4015,31); dpcmBank=dpcmAddr>>14; - logV("writing DPCM: %x %x",dpcmAddr,dpcmLen); } } break; @@ -421,6 +454,7 @@ int DivPlatformNES::dispatch(DivCommand c) { if (c.chan==4) { dacSample=-1; if (dumpWrites) addWrite(0xffff0002,0); + if (dpcmMode && !skipRegisterWrites) rWrite(0x4015,15); } chan[c.chan].active=false; chan[c.chan].keyOff=true; @@ -501,10 +535,16 @@ int DivPlatformNES::dispatch(DivCommand c) { break; case DIV_CMD_NES_DMC: rWrite(0x4011,c.value&0x7f); - if (dumpWrites && dpcmMode) addWrite(0xffff0002,0); break; case DIV_CMD_SAMPLE_MODE: dpcmMode=c.value; + if (dumpWrites && dpcmMode) addWrite(0xffff0002,0); + dacSample=-1; + rWrite(0x4015,15); + rWrite(0x4010,0); + rWrite(0x4012,0); + rWrite(0x4013,0); + rWrite(0x4015,31); break; case DIV_CMD_SAMPLE_BANK: sampleBank=c.value; @@ -682,10 +722,13 @@ void DivPlatformNES::renderSamples() { size_t memPos=0; for (int i=0; isong.sampleLen; i++) { DivSample* s=parent->song.sample[i]; - int paddedLen=(s->lengthDPCM+63)&(~0xff); + unsigned int paddedLen=(s->lengthDPCM+63)&(~0x3f); logV("%d padded length: %d",i,paddedLen); - if ((memPos&0x4000)!=((memPos+paddedLen)&0x4000)) { - memPos=(memPos+0x3fff)&0x4000; + if ((memPos&(~0x3fff))!=((memPos+paddedLen)&(~0x3fff))) { + memPos=(memPos+0x3fff)&(~0x3fff); + } + if (paddedLen>4081) { + paddedLen=4096; } if (memPos>=getSampleMemCapacity(0)) { logW("out of DPCM memory for sample %d!",i); @@ -695,7 +738,7 @@ void DivPlatformNES::renderSamples() { memcpy(dpcmMem+memPos,s->dataDPCM,getSampleMemCapacity(0)-memPos); logW("out of DPCM memory for sample %d!",i); } else { - memcpy(dpcmMem+memPos,s->dataDPCM,paddedLen); + memcpy(dpcmMem+memPos,s->dataDPCM,MIN(s->lengthDPCM,paddedLen)); } s->offDPCM=memPos; memPos+=paddedLen; diff --git a/src/engine/platform/nes.h b/src/engine/platform/nes.h index 75205548a..a03efc7a3 100644 --- a/src/engine/platform/nes.h +++ b/src/engine/platform/nes.h @@ -81,6 +81,7 @@ class DivPlatformNES: public DivDispatch { friend void putDispatchChan(void*,int,int); void doWrite(unsigned short addr, unsigned char data); + unsigned char calcDPCMRate(int inRate); void acquire_puNES(short* bufL, short* bufR, size_t start, size_t len); void acquire_NSFPlay(short* bufL, short* bufR, size_t start, size_t len); diff --git a/src/gui/debugWindow.cpp b/src/gui/debugWindow.cpp index 06184c0ba..7b232a9cf 100644 --- a/src/gui/debugWindow.cpp +++ b/src/gui/debugWindow.cpp @@ -21,6 +21,7 @@ #include "debug.h" #include "IconsFontAwesome4.h" #include +#include void FurnaceGUI::drawDebug() { static int bpOrder; @@ -141,6 +142,50 @@ void FurnaceGUI::drawDebug() { ImGui::Columns(); ImGui::TreePop(); } + if (ImGui::TreeNode("Sample Debug")) { + for (int i=0; isong.sampleLen; i++) { + DivSample* sample=e->getSample(i); + if (sample==NULL) { + ImGui::Text("%d: ",i); + continue; + } + ImGui::Text("%d: %s",i,sample->name.c_str()); + ImGui::Indent(); + ImGui::Text("rate: %d",sample->rate); + ImGui::Text("centerRate: %d",sample->centerRate); + ImGui::Text("loopStart: %d",sample->loopStart); + ImGui::Text("loopOffP: %d",sample->loopOffP); + ImGui::Text("depth: %d",sample->depth); + ImGui::Text("length8: %d",sample->length8); + ImGui::Text("length16: %d",sample->length16); + ImGui::Text("length1: %d",sample->length1); + ImGui::Text("lengthDPCM: %d",sample->lengthDPCM); + ImGui::Text("lengthQSoundA: %d",sample->lengthQSoundA); + ImGui::Text("lengthA: %d",sample->lengthA); + ImGui::Text("lengthB: %d",sample->lengthB); + ImGui::Text("lengthX68: %d",sample->lengthX68); + ImGui::Text("lengthBRR: %d",sample->lengthBRR); + ImGui::Text("lengthVOX: %d",sample->lengthVOX); + + ImGui::Text("off8: %x",sample->off8); + ImGui::Text("off16: %x",sample->off16); + ImGui::Text("off1: %x",sample->off1); + ImGui::Text("offDPCM: %x",sample->offDPCM); + ImGui::Text("offQSoundA: %x",sample->offQSoundA); + ImGui::Text("offA: %x",sample->offA); + ImGui::Text("offB: %x",sample->offB); + ImGui::Text("offX68: %x",sample->offX68); + ImGui::Text("offBRR: %x",sample->offBRR); + ImGui::Text("offVOX: %x",sample->offVOX); + ImGui::Text("offSegaPCM: %x",sample->offSegaPCM); + ImGui::Text("offQSound: %x",sample->offQSound); + ImGui::Text("offX1_010: %x",sample->offX1_010); + + ImGui::Text("samples: %d",sample->samples); + ImGui::Unindent(); + } + ImGui::TreePop(); + } if (ImGui::TreeNode("Playground")) { if (pgSys<0 || pgSys>=e->song.systemLen) pgSys=0; if (ImGui::BeginCombo("System",fmt::sprintf("%d. %s",pgSys+1,e->getSystemName(e->song.system[pgSys])).c_str())) { From 66f6ab4307d26a77767e2a53a77cb610ef3b1648 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 2 May 2022 03:52:45 -0500 Subject: [PATCH 294/342] prepare for #38 --- TODO.md | 1 - src/engine/dispatch.h | 15 +++++++++++++++ src/engine/playback.cpp | 15 +++++++++++++++ 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/TODO.md b/TODO.md index 18c43c49d..54e966303 100644 --- a/TODO.md +++ b/TODO.md @@ -30,7 +30,6 @@ - store edit/followOrders/followPattern state in config - add ability to select a column by double clicking - add ability to move selection by dragging -- NSFPlay core for NES - settings: OK/Cancel buttons should be always visible - Apply button in settings - better FM chip names (number and codename) diff --git a/src/engine/dispatch.h b/src/engine/dispatch.h index c91b56efd..2646a7947 100644 --- a/src/engine/dispatch.h +++ b/src/engine/dispatch.h @@ -64,9 +64,24 @@ enum DivDispatchCmds { DIV_CMD_FM_LFO, // (speed) DIV_CMD_FM_LFO_WAVE, // (waveform) DIV_CMD_FM_TL, // (op, value) + DIV_CMD_FM_AM, // (op, value) DIV_CMD_FM_AR, // (op, value) + DIV_CMD_FM_DR, // (op, value) + DIV_CMD_FM_SL, // (op, value) + DIV_CMD_FM_D2R, // (op, value) + DIV_CMD_FM_RR, // (op, value) + DIV_CMD_FM_DT, // (op, value) + DIV_CMD_FM_DT2, // (op, value) + DIV_CMD_FM_RS, // (op, value) + DIV_CMD_FM_KSR, // (op, value) + DIV_CMD_FM_VIB, // (op, value) + DIV_CMD_FM_SUS, // (op, value) + DIV_CMD_FM_WS, // (op, value) + DIV_CMD_FM_SSG, // (op, value) DIV_CMD_FM_FB, // (value) DIV_CMD_FM_MULT, // (op, value) + DIV_CMD_FM_FINE, // (op, value) + DIV_CMD_FM_FIXFREQ, // (op, value) DIV_CMD_FM_EXTCH, // (enabled) DIV_CMD_FM_AM_DEPTH, // (depth) DIV_CMD_FM_PM_DEPTH, // (depth) diff --git a/src/engine/playback.cpp b/src/engine/playback.cpp index f478f9a1a..c959f64ac 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -65,9 +65,24 @@ const char* cmdName[]={ "FM_LFO", "FM_LFO_WAVE", "FM_TL", + "FM_AM", "FM_AR", + "FM_DR", + "FM_SL", + "FM_D2R", + "FM_RR", + "FM_DT", + "FM_DT2", + "FM_RS", + "FM_KSR", + "FM_VIB", + "FM_SUS", + "FM_WS", + "FM_SSG", "FM_FB", "FM_MULT", + "FM_FINE", + "FM_FIXFREQ", "FM_EXTCH", "FM_AM_DEPTH", "FM_PM_DEPTH", From a6ef36d42e3c4940bc35562275cff5f441224ec7 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 2 May 2022 14:51:06 -0500 Subject: [PATCH 295/342] SoundUnit: PCM support --- src/engine/platform/su.cpp | 84 ++++++++++++++++++++++++++++++++++++-- src/engine/platform/su.h | 5 +++ src/engine/sample.h | 3 +- src/engine/song.h | 2 +- src/gui/debugWindow.cpp | 3 +- 5 files changed, 90 insertions(+), 7 deletions(-) diff --git a/src/engine/platform/su.cpp b/src/engine/platform/su.cpp index a75b7c0b8..284c20801 100644 --- a/src/engine/platform/su.cpp +++ b/src/engine/platform/su.cpp @@ -19,6 +19,7 @@ #include "su.h" #include "../engine.h" +#include "../../ta-log.h" #include //#define rWrite(a,v) pendingWrites[a]=v; @@ -178,12 +179,47 @@ void DivPlatformSoundUnit::tick(bool sysTick) { if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { //DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_SU); chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,2,chan[i].pitch2); + if (chan[i].pcm) { + DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_SU); + DivSample* sample=parent->getSample(ins->amiga.initSample); + if (sample!=NULL) { + double off=1.0; + if (sample->centerRate<1) { + off=1.0; + } else { + off=(double)sample->centerRate/8363.0; + } + chan[i].freq=(double)chan[i].freq*off; + } + } + if (chan[i].freq<0) chan[i].freq=0; + if (chan[i].freq>65535) chan[i].freq=65535; chWrite(i,0x00,chan[i].freq&0xff); chWrite(i,0x01,chan[i].freq>>8); - if (chan[i].freq>65535) chan[i].freq=65535; if (chan[i].keyOn) { - //rWrite(16+i*5,0x80); - //chWrite(i,0x04,0x80|chan[i].vol); + if (chan[i].pcm) { + DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_SU); + DivSample* sample=parent->getSample(ins->amiga.initSample); + if (sample!=NULL) { + unsigned int sampleEnd=sample->offSU+sample->samples; + if (sampleEnd>=getSampleMemCapacity(0)) sampleEnd=getSampleMemCapacity(0)-1; + chWrite(i,0x0a,sample->offSU&0xff); + chWrite(i,0x0b,sample->offSU>>8); + chWrite(i,0x0c,sampleEnd&0xff); + chWrite(i,0x0d,sampleEnd>>8); + if (sample->loopStart>=0 && sample->loopStart<(int)sample->samples) { + unsigned int sampleLoop=sample->offSU+sample->loopStart; + if (sampleLoop>=getSampleMemCapacity(0)) sampleLoop=getSampleMemCapacity(0)-1; + chWrite(i,0x0e,sampleLoop&0xff); + chWrite(i,0x0f,sampleLoop>>8); + chan[i].pcmLoop=true; + } else { + chan[i].pcmLoop=false; + } + writeControl(i); + writeControlUpper(i); + } + } } if (chan[i].keyOff) { chWrite(i,0x02,0); @@ -199,6 +235,11 @@ int DivPlatformSoundUnit::dispatch(DivCommand c) { switch (c.cmd) { case DIV_CMD_NOTE_ON: { DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_SU); + if (chan[c.chan].pcm && ins->type!=DIV_INS_AMIGA) { + writeControl(c.chan); + writeControlUpper(c.chan); + } + chan[c.chan].pcm=(ins->type==DIV_INS_AMIGA); if (c.value!=DIV_NOTE_NULL) { chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value); chan[c.chan].freqChanged=true; @@ -247,7 +288,6 @@ int DivPlatformSoundUnit::dispatch(DivCommand c) { break; case DIV_CMD_WAVE: chan[c.chan].wave=c.value; - chan[c.chan].keyOn=true; break; case DIV_CMD_NOTE_PORTA: { int destFreq=NOTE_FREQUENCY(c.value2); @@ -385,6 +425,42 @@ void DivPlatformSoundUnit::poke(std::vector& wlist) { for (DivRegWrite& i: wlist) rWrite(i.addr,i.val); } +const void* DivPlatformSoundUnit::getSampleMem(int index) { + return (index==0)?su->pcm:NULL; +} + +size_t DivPlatformSoundUnit::getSampleMemCapacity(int index) { + return (index==0)?8192:0; +} + +size_t DivPlatformSoundUnit::getSampleMemUsage(int index) { + return (index==0)?sampleMemLen:0; +} + +void DivPlatformSoundUnit::renderSamples() { + memset(su->pcm,0,getSampleMemCapacity(0)); + + size_t memPos=0; + for (int i=0; isong.sampleLen; i++) { + DivSample* s=parent->song.sample[i]; + int paddedLen=s->samples; + if (memPos>=getSampleMemCapacity(0)) { + logW("out of PCM memory for sample %d!",i); + break; + } + if (memPos+paddedLen>=getSampleMemCapacity(0)) { + memcpy(su->pcm+memPos,s->data8,getSampleMemCapacity(0)-memPos); + logW("out of PCM memory for sample %d!",i); + } else { + memcpy(su->pcm+memPos,s->data8,paddedLen); + } + s->offSU=memPos; + memPos+=paddedLen; + } + sampleMemLen=memPos; + +} + int DivPlatformSoundUnit::init(DivEngine* p, int channels, int sugRate, unsigned int flags) { parent=p; dumpWrites=false; diff --git a/src/engine/platform/su.h b/src/engine/platform/su.h index 7ff3e8709..22ea1e78d 100644 --- a/src/engine/platform/su.h +++ b/src/engine/platform/su.h @@ -86,6 +86,7 @@ class DivPlatformSoundUnit: public DivDispatch { short tempR; unsigned char sampleBank, lfoMode, lfoSpeed; SoundUnit* su; + size_t sampleMemLen; unsigned char regPool[128]; void writeControl(int ch); void writeControlUpper(int ch); @@ -110,6 +111,10 @@ class DivPlatformSoundUnit: public DivDispatch { void poke(std::vector& wlist); const char** getRegisterSheet(); const char* getEffectName(unsigned char effect); + const void* getSampleMem(int index); + size_t getSampleMemCapacity(int index); + size_t getSampleMemUsage(int index); + void renderSamples(); int init(DivEngine* parent, int channels, int sugRate, unsigned int flags); void quit(); ~DivPlatformSoundUnit(); diff --git a/src/engine/sample.h b/src/engine/sample.h index ae0c06dae..6e4de0941 100644 --- a/src/engine/sample.h +++ b/src/engine/sample.h @@ -86,7 +86,7 @@ struct DivSample { unsigned int length8, length16, length1, lengthDPCM, lengthQSoundA, lengthA, lengthB, lengthX68, lengthBRR, lengthVOX; unsigned int off8, off16, off1, offDPCM, offQSoundA, offA, offB, offX68, offBRR, offVOX; - unsigned int offSegaPCM, offQSound, offX1_010; + unsigned int offSegaPCM, offQSound, offX1_010, offSU; unsigned int samples; @@ -247,6 +247,7 @@ struct DivSample { offSegaPCM(0), offQSound(0), offX1_010(0), + offSU(0), samples(0) {} ~DivSample(); }; diff --git a/src/engine/song.h b/src/engine/song.h index 8e67cce83..c0852ea0e 100644 --- a/src/engine/song.h +++ b/src/engine/song.h @@ -457,7 +457,7 @@ struct DivSong { system[0]=DIV_SYSTEM_YM2612; system[1]=DIV_SYSTEM_SMS; - nullInsOPLL.fm.opllPreset=7; + nullInsOPLL.fm.opllPreset=0; nullInsOPLL.fm.op[1].tl=0; nullInsOPLL.name="This is a bug! Report!"; diff --git a/src/gui/debugWindow.cpp b/src/gui/debugWindow.cpp index 7b232a9cf..566f916fa 100644 --- a/src/gui/debugWindow.cpp +++ b/src/gui/debugWindow.cpp @@ -180,6 +180,7 @@ void FurnaceGUI::drawDebug() { ImGui::Text("offSegaPCM: %x",sample->offSegaPCM); ImGui::Text("offQSound: %x",sample->offQSound); ImGui::Text("offX1_010: %x",sample->offX1_010); + ImGui::Text("offSU: %x",sample->offSU); ImGui::Text("samples: %d",sample->samples); ImGui::Unindent(); @@ -312,4 +313,4 @@ void FurnaceGUI::drawDebug() { } if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_DEBUG; ImGui::End(); -} \ No newline at end of file +} From ce40085d3b77233bf655e8a24ce7b66cc95509b7 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 2 May 2022 15:07:59 -0500 Subject: [PATCH 296/342] SoundUnit: fix PCM pitch --- src/engine/platform/su.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/engine/platform/su.cpp b/src/engine/platform/su.cpp index 284c20801..e80153f07 100644 --- a/src/engine/platform/su.cpp +++ b/src/engine/platform/su.cpp @@ -120,7 +120,12 @@ void DivPlatformSoundUnit::tick(bool sysTick) { for (int i=0; i<8; i++) { chan[i].std.next(); if (chan[i].std.vol.had) { - chan[i].outVol=((chan[i].vol&127)*MIN(127,chan[i].std.vol.val))>>7; + DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_SU); + if (ins->type==DIV_INS_AMIGA) { + chan[i].outVol=((chan[i].vol&127)*MIN(64,chan[i].std.vol.val))>>6; + } else { + chan[i].outVol=((chan[i].vol&127)*MIN(127,chan[i].std.vol.val))>>7; + } chWrite(i,0x02,chan[i].outVol); } if (chan[i].std.arp.had) { @@ -183,11 +188,11 @@ void DivPlatformSoundUnit::tick(bool sysTick) { DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_SU); DivSample* sample=parent->getSample(ins->amiga.initSample); if (sample!=NULL) { - double off=1.0; + double off=0.25; if (sample->centerRate<1) { - off=1.0; + off=0.25; } else { - off=(double)sample->centerRate/8363.0; + off=(double)sample->centerRate/(8363.0*4.0); } chan[i].freq=(double)chan[i].freq*off; } From ffa0727bf61bddc5ca07d219862bd63a04d9fe6b Mon Sep 17 00:00:00 2001 From: Laurens Holst Date: Mon, 2 May 2022 19:38:17 +0200 Subject: [PATCH 297/342] AY8930: Fix VGM output. It was generating writes to register addresses > 15. --- src/engine/platform/ay8930.cpp | 29 ++++++++++++++++++----------- src/engine/platform/ay8930.h | 1 + 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/src/engine/platform/ay8930.cpp b/src/engine/platform/ay8930.cpp index 13308698c..5b823606b 100644 --- a/src/engine/platform/ay8930.cpp +++ b/src/engine/platform/ay8930.cpp @@ -25,7 +25,7 @@ #include #define rWrite(a,v) if (!skipRegisterWrites) {pendingWrites[a]=v;} -#define immWrite(a,v) if (!skipRegisterWrites) {writes.emplace(a,v); if (dumpWrites) {addWrite(a,v);} } +#define immWrite2(a,v) if (!skipRegisterWrites) {writes.emplace(a,v); if (dumpWrites) {addWrite(a,v);} } #define CHIP_DIVIDER 8 @@ -61,6 +61,18 @@ const char* regCheatSheetAY8930[]={ NULL }; +void DivPlatformAY8930::immWrite(unsigned char a, unsigned char v) { + if ((int)bank!=(a>>4)) { + bank=a>>4; + immWrite2(0x0d, 0xa0|(bank<<4)|ayEnvMode[0]); + } + if (a==0x0d) { + immWrite2(0x0d,0xa0|(bank<<4)|(v&15)); + } else { + immWrite2(a&15,v); + } +} + const char** DivPlatformAY8930::getRegisterSheet() { return regCheatSheetAY8930; } @@ -123,18 +135,13 @@ void DivPlatformAY8930::acquire(short* bufL, short* bufR, size_t start, size_t l } while (!writes.empty()) { QueuedWrite w=writes.front(); - if ((int)bank!=(w.addr>>4)) { - bank=w.addr>>4; - ay->address_w(0x0d); - ay->data_w(0xa0|(bank<<4)|ayEnvMode[0]); - } - ay->address_w(w.addr&15); - if (w.addr==0x0d) { - ay->data_w(0xa0|(bank<<4)|(w.val&15)); + ay->address_w(w.addr); + ay->data_w(w.val); + if (w.addr!=0x0d && (regPool[0x0d]&0xf0)==0xb0) { + regPool[(w.addr&0x0f)|0x10]=w.val; } else { - ay->data_w(w.val); + regPool[w.addr&0x0f]=w.val; } - regPool[w.addr&0x1f]=w.val; writes.pop(); } ay->sound_stream_update(ayBuf,len); diff --git a/src/engine/platform/ay8930.h b/src/engine/platform/ay8930.h index ff992bb5e..5f477e123 100644 --- a/src/engine/platform/ay8930.h +++ b/src/engine/platform/ay8930.h @@ -73,6 +73,7 @@ class DivPlatformAY8930: public DivDispatch { size_t ayBufLen; void updateOutSel(bool immediate=false); + void immWrite(unsigned char a, unsigned char v); friend void putDispatchChan(void*,int,int); From 960048cf4b5924076a9473dffe1e0c9121610790 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 2 May 2022 16:05:07 -0500 Subject: [PATCH 298/342] NES: fix Furnace-style DPCM --- src/engine/platform/nes.cpp | 48 +++++++++++++++++++++---------------- 1 file changed, 27 insertions(+), 21 deletions(-) diff --git a/src/engine/platform/nes.cpp b/src/engine/platform/nes.cpp index 0e336df0e..dffcb7ee1 100644 --- a/src/engine/platform/nes.cpp +++ b/src/engine/platform/nes.cpp @@ -313,8 +313,6 @@ void DivPlatformNES::tick(bool sysTick) { if (chan[i].freq<0) chan[i].freq=0; } if (chan[i].keyOn) { - //rWrite(16+i*5+1,((chan[i].duty&3)<<6)|(63-(ins->gb.soundLen&63))); - //rWrite(16+i*5+2,((chan[i].vol<<4))|(ins->gb.envLen&7)|((ins->gb.envDir&1)<<3)); } if (chan[i].keyOff) { //rWrite(16+i*5+2,8); @@ -343,7 +341,7 @@ void DivPlatformNES::tick(bool sysTick) { } // PCM - if (chan[4].freqChanged) { + if (chan[4].freqChanged || chan[4].keyOn) { chan[4].freq=parent->calcFreq(chan[4].baseFreq,chan[4].pitch,false); if (chan[4].furnaceDac) { double off=1.0; @@ -352,11 +350,27 @@ void DivPlatformNES::tick(bool sysTick) { off=(double)s->centerRate/8363.0; } dacRate=MIN(chan[4].freq*off,32000); - if (dpcmMode && !skipRegisterWrites) { - rWrite(0x4010,calcDPCMRate(dacRate)); + if (chan[4].keyOn) { + if (dpcmMode && !skipRegisterWrites && dacSample>=0 && dacSamplesong.sampleLen) { + unsigned int dpcmAddr=parent->getSample(dacSample)->offDPCM; + unsigned int dpcmLen=(parent->getSample(dacSample)->lengthDPCM+15)>>4; + if (dpcmLen>255) dpcmLen=255; + // write DPCM + rWrite(0x4015,15); + rWrite(0x4010,calcDPCMRate(dacRate)); + rWrite(0x4012,(dpcmAddr>>6)&0xff); + rWrite(0x4013,dpcmLen&0xff); + rWrite(0x4015,31); + dpcmBank=dpcmAddr>>14; + } + } else { + if (dpcmMode) { + rWrite(0x4010,calcDPCMRate(dacRate)); + } } - if (dumpWrites) addWrite(0xffff0001,dacRate); + if (dumpWrites && !dpcmMode) addWrite(0xffff0001,dacRate); } + if (chan[4].keyOn) chan[4].keyOn=false; chan[4].freqChanged=false; } } @@ -378,25 +392,13 @@ int DivPlatformNES::dispatch(DivCommand c) { dacPos=0; dacPeriod=0; if (c.value!=DIV_NOTE_NULL) { - chan[c.chan].baseFreq=parent->song.tuning*pow(2.0f,((float)(c.value+3)/12.0f)); + chan[c.chan].baseFreq=parent->calcBaseFreq(1,1,c.value,false); chan[c.chan].freqChanged=true; chan[c.chan].note=c.value; } chan[c.chan].active=true; chan[c.chan].keyOn=true; chan[c.chan].furnaceDac=true; - if (dpcmMode && !skipRegisterWrites) { - unsigned int dpcmAddr=parent->getSample(dacSample)->offDPCM; - unsigned int dpcmLen=(parent->getSample(dacSample)->lengthDPCM+15)>>4; - if (dpcmLen>255) dpcmLen=255; - // write DPCM - rWrite(0x4015,15); - rWrite(0x4010,calcDPCMRate(chan[c.chan].baseFreq)); - rWrite(0x4012,(dpcmAddr>>6)&0xff); - rWrite(0x4013,dpcmLen&0xff); - rWrite(0x4015,31); - dpcmBank=dpcmAddr>>14; - } } else { if (c.value!=DIV_NOTE_NULL) { chan[c.chan].note=c.value; @@ -492,7 +494,7 @@ int DivPlatformNES::dispatch(DivCommand c) { chan[c.chan].freqChanged=true; break; case DIV_CMD_NOTE_PORTA: { - int destFreq=NOTE_PERIODIC(c.value2); + int destFreq=(c.chan==4)?(parent->calcBaseFreq(1,1,c.value2,false)):(NOTE_PERIODIC(c.value2)); bool return2=false; if (destFreq>chan[c.chan].baseFreq) { chan[c.chan].baseFreq+=c.value; @@ -554,7 +556,11 @@ int DivPlatformNES::dispatch(DivCommand c) { break; case DIV_CMD_LEGATO: if (c.chan==3) break; - chan[c.chan].baseFreq=NOTE_PERIODIC(c.value+((chan[c.chan].std.arp.will && !chan[c.chan].std.arp.mode)?(chan[c.chan].std.arp.val):(0))); + if (c.chan==4) { + chan[c.chan].baseFreq=parent->calcBaseFreq(1,1,c.value+((chan[c.chan].std.arp.will && !chan[c.chan].std.arp.mode)?(chan[c.chan].std.arp.val):(0)),false); + } else { + chan[c.chan].baseFreq=NOTE_PERIODIC(c.value+((chan[c.chan].std.arp.will && !chan[c.chan].std.arp.mode)?(chan[c.chan].std.arp.val):(0))); + } chan[c.chan].freqChanged=true; chan[c.chan].note=c.value; break; From 62289d924eb5ed9dfcf1178cbc44edbe4b40bbdd Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 2 May 2022 16:53:55 -0500 Subject: [PATCH 299/342] FDS: add NSFPlay core --- src/engine/dispatchContainer.cpp | 1 + src/engine/platform/fds.cpp | 65 +++++++++++++++++-- src/engine/platform/fds.h | 9 +++ .../platform/sound/nes_nsfplay/nes_fds.cpp | 2 + src/gui/gui.h | 2 + src/gui/settings.cpp | 7 ++ 6 files changed, 80 insertions(+), 6 deletions(-) diff --git a/src/engine/dispatchContainer.cpp b/src/engine/dispatchContainer.cpp index 187523c72..f44ed0d5c 100644 --- a/src/engine/dispatchContainer.cpp +++ b/src/engine/dispatchContainer.cpp @@ -228,6 +228,7 @@ void DivDispatchContainer::init(DivSystem sys, DivEngine* eng, int chanCount, do break; case DIV_SYSTEM_FDS: dispatch=new DivPlatformFDS; + ((DivPlatformFDS*)dispatch)->setNSFPlay(eng->getConfInt("fdsCore",0)==1); break; case DIV_SYSTEM_TIA: dispatch=new DivPlatformTIA; diff --git a/src/engine/platform/fds.cpp b/src/engine/platform/fds.cpp index 4c8e92b38..f3908148d 100644 --- a/src/engine/platform/fds.cpp +++ b/src/engine/platform/fds.cpp @@ -20,11 +20,12 @@ #include "fds.h" #include "sound/nes/cpu_inline.h" #include "../engine.h" +#include "sound/nes_nsfplay/nes_fds.h" #include #define CHIP_FREQBASE 262144 -#define rWrite(a,v) if (!skipRegisterWrites) {fds_wr_mem(fds,a,v); regPool[(a)&0x7f]=v; if (dumpWrites) {addWrite(a,v);} } +#define rWrite(a,v) if (!skipRegisterWrites) {doWrite(a,v); regPool[(a)&0x7f]=v; if (dumpWrites) {addWrite(a,v);} } const char* regCheatSheetFDS[]={ "IOCtrl", "4023", @@ -78,7 +79,7 @@ const char* DivPlatformFDS::getEffectName(unsigned char effect) { return NULL; } -void DivPlatformFDS::acquire(short* bufL, short* bufR, size_t start, size_t len) { +void DivPlatformFDS::acquire_puNES(short* bufL, short* bufR, size_t start, size_t len) { for (size_t i=start; isnd.main.output; @@ -92,6 +93,38 @@ void DivPlatformFDS::acquire(short* bufL, short* bufR, size_t start, size_t len) } } +void DivPlatformFDS::acquire_NSFPlay(short* bufL, short* bufR, size_t start, size_t len) { + int out[2]; + for (size_t i=start; iTick(1); + fds_NP->Render(out); + int sample=isMuted[0]?0:(out[0]<<1); + if (sample>32767) sample=32767; + if (sample<-32768) sample=-32768; + bufL[i]=sample; + if (++writeOscBuf>=32) { + writeOscBuf=0; + oscBuf->data[oscBuf->needle++]=sample<<1; + } + } +} + +void DivPlatformFDS::doWrite(unsigned short addr, unsigned char data) { + if (useNP) { + fds_NP->Write(addr,data); + } else { + fds_wr_mem(fds,addr,data); + } +} + +void DivPlatformFDS::acquire(short* bufL, short* bufR, size_t start, size_t len) { + if (useNP) { + acquire_NSFPlay(bufL,bufR,start,len); + } else { + acquire_puNES(bufL,bufR,start,len); + } +} + void DivPlatformFDS::updateWave() { // TODO: master volume rWrite(0x4089,0x80); @@ -423,7 +456,11 @@ void DivPlatformFDS::reset() { addWrite(0xffffffff,0); } - fds_reset(fds); + if (useNP) { + fds_NP->Reset(); + } else { + fds_reset(fds); + } memset(regPool,0,128); rWrite(0x4023,0); @@ -435,6 +472,10 @@ bool DivPlatformFDS::keyOffAffectsArp(int ch) { return true; } +void DivPlatformFDS::setNSFPlay(bool use) { + useNP=use; +} + void DivPlatformFDS::setFlags(unsigned int flags) { if (flags==2) { // Dendy rate=COLOR_PAL*2.0/5.0; @@ -445,6 +486,10 @@ void DivPlatformFDS::setFlags(unsigned int flags) { } chipClock=rate; oscBuf->rate=rate/32; + if (useNP) { + fds_NP->SetClock(rate); + fds_NP->SetRate(rate); + } } void DivPlatformFDS::notifyInsDeletion(void* ins) { @@ -467,7 +512,11 @@ int DivPlatformFDS::init(DivEngine* p, int channels, int sugRate, unsigned int f dumpWrites=false; skipRegisterWrites=false; writeOscBuf=0; - fds=new struct _fds; + if (useNP) { + fds_NP=new xgm::NES_FDS; + } else { + fds=new struct _fds; + } oscBuf=new DivDispatchOscBuffer; for (int i=0; i<1; i++) { isMuted[i]=false; @@ -475,12 +524,16 @@ int DivPlatformFDS::init(DivEngine* p, int channels, int sugRate, unsigned int f setFlags(flags); reset(); - return 5; + return 1; } void DivPlatformFDS::quit() { delete oscBuf; - delete fds; + if (useNP) { + delete fds_NP; + } else { + delete fds; + } } DivPlatformFDS::~DivPlatformFDS() { diff --git a/src/engine/platform/fds.h b/src/engine/platform/fds.h index c7c53aea2..1c08e1bbb 100644 --- a/src/engine/platform/fds.h +++ b/src/engine/platform/fds.h @@ -24,6 +24,8 @@ #include "../macroInt.h" #include "../waveSynth.h" +#include "sound/nes_nsfplay/nes_fds.h" + class DivPlatformFDS: public DivDispatch { struct Channel { int freq, baseFreq, pitch, pitch2, prevFreq, note, modFreq, ins; @@ -69,13 +71,19 @@ class DivPlatformFDS: public DivDispatch { DivWaveSynth ws; unsigned char apuType; unsigned char writeOscBuf; + bool useNP; struct _fds* fds; + xgm::NES_FDS* fds_NP; unsigned char regPool[128]; void updateWave(); friend void putDispatchChan(void*,int,int); + void doWrite(unsigned short addr, unsigned char data); + void acquire_puNES(short* bufL, short* bufR, size_t start, size_t len); + void acquire_NSFPlay(short* bufL, short* bufR, size_t start, size_t len); + public: void acquire(short* bufL, short* bufR, size_t start, size_t len); int dispatch(DivCommand c); @@ -88,6 +96,7 @@ class DivPlatformFDS: public DivDispatch { void tick(bool sysTick=true); void muteChannel(int ch, bool mute); bool keyOffAffectsArp(int ch); + void setNSFPlay(bool use); void setFlags(unsigned int flags); void notifyInsDeletion(void* ins); void poke(unsigned int addr, unsigned short val); diff --git a/src/engine/platform/sound/nes_nsfplay/nes_fds.cpp b/src/engine/platform/sound/nes_nsfplay/nes_fds.cpp index f3e61decc..7599fa857 100644 --- a/src/engine/platform/sound/nes_nsfplay/nes_fds.cpp +++ b/src/engine/platform/sound/nes_nsfplay/nes_fds.cpp @@ -21,6 +21,8 @@ NES_FDS::NES_FDS () sm[0] = 128; sm[1] = 128; + mask=0; + Reset(); } diff --git a/src/gui/gui.h b/src/gui/gui.h index 49e56f0c2..90d909f2d 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -789,6 +789,7 @@ class FurnaceGUI { int ym2612Core; int saaCore; int nesCore; + int fdsCore; int mainFont; int patFont; int audioRate; @@ -872,6 +873,7 @@ class FurnaceGUI { ym2612Core(0), saaCore(1), nesCore(0), + fdsCore(0), mainFont(0), patFont(0), audioRate(44100), diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index 383fa4202..b515d6535 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -868,6 +868,10 @@ void FurnaceGUI::drawSettings() { ImGui::SameLine(); ImGui::Combo("##NESCore",&settings.nesCore,nesCores,2); + ImGui::Text("FDS core"); + ImGui::SameLine(); + ImGui::Combo("##FDSCore",&settings.fdsCore,nesCores,2); + ImGui::EndTabItem(); } if (ImGui::BeginTabItem("Appearance")) { @@ -1741,6 +1745,7 @@ void FurnaceGUI::syncSettings() { settings.ym2612Core=e->getConfInt("ym2612Core",0); settings.saaCore=e->getConfInt("saaCore",1); settings.nesCore=e->getConfInt("nesCore",0); + settings.fdsCore=e->getConfInt("fdsCore",0); settings.mainFont=e->getConfInt("mainFont",0); settings.patFont=e->getConfInt("patFont",0); settings.mainFontPath=e->getConfString("mainFontPath",""); @@ -1816,6 +1821,7 @@ void FurnaceGUI::syncSettings() { clampSetting(settings.ym2612Core,0,1); clampSetting(settings.saaCore,0,1); clampSetting(settings.nesCore,0,1); + clampSetting(settings.fdsCore,0,1); clampSetting(settings.mainFont,0,6); clampSetting(settings.patFont,0,6); clampSetting(settings.patRowsBase,0,1); @@ -1919,6 +1925,7 @@ void FurnaceGUI::commitSettings() { e->setConf("ym2612Core",settings.ym2612Core); e->setConf("saaCore",settings.saaCore); e->setConf("nesCore",settings.nesCore); + e->setConf("fdsCore",settings.fdsCore); e->setConf("mainFont",settings.mainFont); e->setConf("patFont",settings.patFont); e->setConf("mainFontPath",settings.mainFontPath); From 4db2ab96aec85719362de451c72fa751978646a8 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 2 May 2022 18:15:51 -0500 Subject: [PATCH 300/342] document (NON-WORKING) extended op param effects --- papers/doc/7-systems/opl.md | 34 +++++++++++ papers/doc/7-systems/opll.md | 24 ++++++++ papers/doc/7-systems/opz.md | 100 ++++++++++++++++++++++++++++++++ papers/doc/7-systems/ym2151.md | 38 ++++++++++++ papers/doc/7-systems/ym2610.md | 40 +++++++++++++ papers/doc/7-systems/ym2610b.md | 40 +++++++++++++ papers/doc/7-systems/ym2612.md | 40 +++++++++++++ 7 files changed, 316 insertions(+) create mode 100644 papers/doc/7-systems/opz.md diff --git a/papers/doc/7-systems/opl.md b/papers/doc/7-systems/opl.md index b019e3f98..056591a6d 100644 --- a/papers/doc/7-systems/opl.md +++ b/papers/doc/7-systems/opl.md @@ -44,3 +44,37 @@ afterwards everyone moved to Windows and software mixed PCM streaming... - only in 4-op mode (OPL3). - `1Dxx`: set attack of operator 4. - only in 4-op mode (OPL3). +- `2Axy`: set waveform of operator. + - `x` is the operator (1-4; last 2 operators only in 4-op mode). a value of 0 means "all operators". + - `y` is the value. + - only in OPL2 or higher. +- `30xx`: enable envelope hard reset. + - this works by inserting a quick release and tiny delay before a new note. +- `50xy`: set AM of operator. + - `x` is the operator (1-4; last 2 operators only in 4-op mode). a value of 0 means "all operators". + - `y` determines whether AM is on. +- `51xy` set SL of operator. + - `x` is the operator (1-4; last 2 operators only in 4-op mode). a value of 0 means "all operators". + - `y` is the value. +- `52xy` set RR of operator. + - `x` is the operator (1-4; last 2 operators only in 4-op mode). a value of 0 means "all operators". + - `y` is the value. +- `53xy`: set VIB of operator. + - `x` is the operator (1-4; last 2 operators only in 4-op mode). a value of 0 means "all operators". + - `y` determines whether VIB is on. +- `54xy` set KSL of operator. + - `x` is the operator (1-4; last 2 operators only in 4-op mode). a value of 0 means "all operators". + - `y` is the value. +- `55xy` set SUS of operator. + - `x` is the operator (1-4; last 2 operators only in 4-op mode). a value of 0 means "all operators". + - `y` determines whether SUS is on. +- `56xx`: set DR of all operators. +- `57xx`: set DR of operator 1. +- `58xx`: set DR of operator 2. +- `58xx`: set DR of operator 3. + - only in 4-op mode (OPL3). +- `58xx`: set DR of operator 4. + - only in 4-op mode (OPL3). +- `5Bxy`: set KSR of operator. + - `x` is the operator (1-4; last 2 operators only in 4-op mode). a value of 0 means "all operators". + - `y` determines whether KSR is on. \ No newline at end of file diff --git a/papers/doc/7-systems/opll.md b/papers/doc/7-systems/opll.md index 2e92171ba..4e91c137d 100644 --- a/papers/doc/7-systems/opll.md +++ b/papers/doc/7-systems/opll.md @@ -37,3 +37,27 @@ the YM2413 is equipped with the following features: - `19xx`: set attack of all operators. - `1Axx`: set attack of operator 1. - `1Bxx`: set attack of operator 2. +- `50xy`: set AM of operator. + - `x` is the operator (1-2). a value of 0 means "all operators". + - `y` determines whether AM is on. +- `51xy` set SL of operator. + - `x` is the operator (1-2). a value of 0 means "all operators". + - `y` is the value. +- `52xy` set RR of operator. + - `x` is the operator (1-2). a value of 0 means "all operators". + - `y` is the value. +- `53xy`: set VIB of operator. + - `x` is the operator (1-2). a value of 0 means "all operators". + - `y` determines whether VIB is on. +- `54xy` set KSL of operator. + - `x` is the operator (1-2). a value of 0 means "all operators". + - `y` is the value. +- `55xy` set EGT of operator. + - `x` is the operator (1-2). a value of 0 means "all operators". + - `y` determines whether EGT is on. +- `56xx`: set DR of all operators. +- `57xx`: set DR of operator 1. +- `58xx`: set DR of operator 2. +- `5Bxy`: set KSR of operator. + - `x` is the operator (1-2). a value of 0 means "all operators". + - `y` determines whether KSR is on. \ No newline at end of file diff --git a/papers/doc/7-systems/opz.md b/papers/doc/7-systems/opz.md new file mode 100644 index 000000000..dd69c85c8 --- /dev/null +++ b/papers/doc/7-systems/opz.md @@ -0,0 +1,100 @@ +# Yamaha OPZ (YM2414) + +this is the YM2151's little-known successor, used in the Yamaha TX81Z and a few other Yamaha synthesizers. oh, and the Korg Z3 too. + +it adds these features on top of the YM2151: +- 8 waveforms (but they're different from the OPL ones) +- per-channel (possibly) linear volume control separate from TL +- increased multiplier precision (in 1/16ths) +- 4-step envelope generator shift (minimum TL) +- another LFO +- no per-operator key on/off +- fixed frequency mode per operator (kind of like OPN family's extended channel mode but with a bit less precision and for all 8 channels) +- "reverb" effect (actually extends release) + +unlike the YM2151, this chip is officially undocumented. very few efforts have been made to study the chip and document it... +therefore emulation of this chip in Furnace is incomplete and uncertain. + +no plans have been made for TX81Z MIDI passthrough, because: +- Furnace works with register writes rather than MIDI commands +- the MIDI protocol is slow (would not be enough). +- the TX81Z is very slow to process a note on/off or parameter change event. +- the TL range has been reduced to 0-99, but the chip goes from 0-127. + +# effects + +- `10xx`: set noise frequency of channel 8 operator 4. 00 disables noise while 01 to 20 enables it. +- `11xx`: set feedback of channel. +- `12xx`: set operator 1 level. +- `13xx`: set operator 2 level. +- `14xx`: set operator 3 level. +- `15xx`: set operator 4 level. +- `16xy`: set multiplier of operator. + - `x` is the operator (1-4). + - `y` is the mutliplier. +- `17xx`: set LFO speed. +- `18xx`: set LFO waveform. `xx` may be one of the following: + - `00`: saw + - `01`: square + - `02`: triangle + - `03`: noise +- `19xx`: set attack of all operators. +- `1Axx`: set attack of operator 1. +- `1Bxx`: set attack of operator 2. +- `1Cxx`: set attack of operator 3. +- `1Dxx`: set attack of operator 4. +- `28xy`: set reverb of operator. + - `x` is the operator (1-4). a value of 0 means "all operators". + - `y` is the value. +- `29xy`: set EG shift of operator. + - `x` is the operator (1-4). a value of 0 means "all operators". + - `y` is the value. +- `2Axy`: set waveform of operator. + - `x` is the operator (1-4). a value of 0 means "all operators". + - `y` is the value. +- `2Fxx`: enable envelope hard reset. + - this works by inserting a quick release and tiny delay before a new note. +- `3xyy`: set fixed frequency of operator 1/2. + - `x` is the block (0-7 for operator 1; 8-F for operator 2). + - `y` is the frequency. fixed frequency mode will be disabled if this is less than 8. + - the actual frequency is: `y*(2^x)`. +- `4xyy`: set fixed frequency of operator 3/4. + - `x` is the block (0-7 for operator 3; 8-F for operator 4). + - `y` is the frequency. fixed frequency mode will be disabled if this is less than 8. + - the actual frequency is: `y*(2^x)`. +- `50xy`: set AM of operator. + - `x` is the operator (1-4). a value of 0 means "all operators". + - `y` determines whether AM is on. +- `51xy` set SL of operator. + - `x` is the operator (1-4). a value of 0 means "all operators". + - `y` is the value. +- `52xy` set RR of operator. + - `x` is the operator (1-4). a value of 0 means "all operators". + - `y` is the value. +- `53xy` set DT of operator. + - `x` is the operator (1-4). a value of 0 means "all operators". + - `y` is the value: + - 0: +0 + - 1: +1 + - 2: +2 + - 3: +3 + - 4: -0 + - 5: -3 + - 6: -2 + - 7: -1 +- `54xy` set RS of operator. + - `x` is the operator (1-4). a value of 0 means "all operators". + - `y` is the value. +- `55xy` set DT2 of operator. + - `x` is the operator (1-4). a value of 0 means "all operators". + - `y` is the value. +- `56xx`: set DR of all operators. +- `57xx`: set DR of operator 1. +- `58xx`: set DR of operator 2. +- `59xx`: set DR of operator 3. +- `5Axx`: set DR of operator 4. +- `5Bxx`: set D2R/SR of all operators. +- `5Cxx`: set D2R/SR of operator 1. +- `5Dxx`: set D2R/SR of operator 2. +- `5Exx`: set D2R/SR of operator 3. +- `5Fxx`: set D2R/SR of operator 4. \ No newline at end of file diff --git a/papers/doc/7-systems/ym2151.md b/papers/doc/7-systems/ym2151.md index 69eba0fb3..2291088e1 100644 --- a/papers/doc/7-systems/ym2151.md +++ b/papers/doc/7-systems/ym2151.md @@ -26,3 +26,41 @@ it also was present on several pinball machines and synthesizers of the era, and - `1Bxx`: set attack of operator 2. - `1Cxx`: set attack of operator 3. - `1Dxx`: set attack of operator 4. +- `30xx`: enable envelope hard reset. + - this works by inserting a quick release and tiny delay before a new note. +- `50xy`: set AM of operator. + - `x` is the operator (1-4). a value of 0 means "all operators". + - `y` determines whether AM is on. +- `51xy` set SL of operator. + - `x` is the operator (1-4). a value of 0 means "all operators". + - `y` is the value. +- `52xy` set RR of operator. + - `x` is the operator (1-4). a value of 0 means "all operators". + - `y` is the value. +- `53xy` set DT of operator. + - `x` is the operator (1-4). a value of 0 means "all operators". + - `y` is the value: + - 0: +0 + - 1: +1 + - 2: +2 + - 3: +3 + - 4: -0 + - 5: -3 + - 6: -2 + - 7: -1 +- `54xy` set RS of operator. + - `x` is the operator (1-4). a value of 0 means "all operators". + - `y` is the value. +- `55xy` set DT2 of operator. + - `x` is the operator (1-4). a value of 0 means "all operators". + - `y` is the value. +- `56xx`: set DR of all operators. +- `57xx`: set DR of operator 1. +- `58xx`: set DR of operator 2. +- `59xx`: set DR of operator 3. +- `5Axx`: set DR of operator 4. +- `5Bxx`: set D2R/SR of all operators. +- `5Cxx`: set D2R/SR of operator 1. +- `5Dxx`: set D2R/SR of operator 2. +- `5Exx`: set D2R/SR of operator 3. +- `5Fxx`: set D2R/SR of operator 4. \ No newline at end of file diff --git a/papers/doc/7-systems/ym2610.md b/papers/doc/7-systems/ym2610.md index 4515fde23..ec405efef 100644 --- a/papers/doc/7-systems/ym2610.md +++ b/papers/doc/7-systems/ym2610.md @@ -57,3 +57,43 @@ its soundchip is a 4-in-1: 4ch 4-op FM, YM2149 (AY-3-8910 clone) and 2 different - `x` is the numerator. - `y` is the denominator. - if `x` or `y` are 0 this will disable auto-envelope mode. +- `30xx`: enable envelope hard reset. + - this works by inserting a quick release and tiny delay before a new note. +- `50xy`: set AM of operator. + - `x` is the operator (1-4). a value of 0 means "all operators". + - `y` determines whether AM is on. +- `51xy` set SL of operator. + - `x` is the operator (1-4). a value of 0 means "all operators". + - `y` is the value. +- `52xy` set RR of operator. + - `x` is the operator (1-4). a value of 0 means "all operators". + - `y` is the value. +- `53xy` set DT of operator. + - `x` is the operator (1-4). a value of 0 means "all operators". + - `y` is the value: + - 0: +0 + - 1: +1 + - 2: +2 + - 3: +3 + - 4: -0 + - 5: -3 + - 6: -2 + - 7: -1 +- `54xy` set RS of operator. + - `x` is the operator (1-4). a value of 0 means "all operators". + - `y` is the value. +- `55xy` set SSG-EG of operator. + - `x` is the operator (1-4). a value of 0 means "all operators". + - `y` is the value (0-8). + - values between 0 and 7 set SSG-EG. + - value 8 disables it. +- `56xx`: set DR of all operators. +- `57xx`: set DR of operator 1. +- `58xx`: set DR of operator 2. +- `59xx`: set DR of operator 3. +- `5Axx`: set DR of operator 4. +- `5Bxx`: set D2R/SR of all operators. +- `5Cxx`: set D2R/SR of operator 1. +- `5Dxx`: set D2R/SR of operator 2. +- `5Exx`: set D2R/SR of operator 3. +- `5Fxx`: set D2R/SR of operator 4. \ No newline at end of file diff --git a/papers/doc/7-systems/ym2610b.md b/papers/doc/7-systems/ym2610b.md index e251122cd..17f274559 100644 --- a/papers/doc/7-systems/ym2610b.md +++ b/papers/doc/7-systems/ym2610b.md @@ -56,3 +56,43 @@ it is backward compatible with the original chip. - `x` is the numerator. - `y` is the denominator. - if `x` or `y` are 0 this will disable auto-envelope mode. +- `30xx`: enable envelope hard reset. + - this works by inserting a quick release and tiny delay before a new note. +- `50xy`: set AM of operator. + - `x` is the operator (1-4). a value of 0 means "all operators". + - `y` determines whether AM is on. +- `51xy` set SL of operator. + - `x` is the operator (1-4). a value of 0 means "all operators". + - `y` is the value. +- `52xy` set RR of operator. + - `x` is the operator (1-4). a value of 0 means "all operators". + - `y` is the value. +- `53xy` set DT of operator. + - `x` is the operator (1-4). a value of 0 means "all operators". + - `y` is the value: + - 0: +0 + - 1: +1 + - 2: +2 + - 3: +3 + - 4: -0 + - 5: -3 + - 6: -2 + - 7: -1 +- `54xy` set RS of operator. + - `x` is the operator (1-4). a value of 0 means "all operators". + - `y` is the value. +- `55xy` set SSG-EG of operator. + - `x` is the operator (1-4). a value of 0 means "all operators". + - `y` is the value (0-8). + - values between 0 and 7 set SSG-EG. + - value 8 disables it. +- `56xx`: set DR of all operators. +- `57xx`: set DR of operator 1. +- `58xx`: set DR of operator 2. +- `59xx`: set DR of operator 3. +- `5Axx`: set DR of operator 4. +- `5Bxx`: set D2R/SR of all operators. +- `5Cxx`: set D2R/SR of operator 1. +- `5Dxx`: set D2R/SR of operator 2. +- `5Exx`: set D2R/SR of operator 3. +- `5Fxx`: set D2R/SR of operator 4. \ No newline at end of file diff --git a/papers/doc/7-systems/ym2612.md b/papers/doc/7-systems/ym2612.md index b7dd8a16e..c32521f2c 100644 --- a/papers/doc/7-systems/ym2612.md +++ b/papers/doc/7-systems/ym2612.md @@ -25,3 +25,43 @@ one of two chips that powered the Sega Genesis. It is a six-channel, four-operat - `1Bxx`: set attack of operator 2. - `1Cxx`: set attack of operator 3. - `1Dxx`: set attack of operator 4. +- `30xx`: enable envelope hard reset. + - this works by inserting a quick release and tiny delay before a new note. +- `50xy`: set AM of operator. + - `x` is the operator (1-4). a value of 0 means "all operators". + - `y` determines whether AM is on. +- `51xy` set SL of operator. + - `x` is the operator (1-4). a value of 0 means "all operators". + - `y` is the value. +- `52xy` set RR of operator. + - `x` is the operator (1-4). a value of 0 means "all operators". + - `y` is the value. +- `53xy` set DT of operator. + - `x` is the operator (1-4). a value of 0 means "all operators". + - `y` is the value: + - 0: +0 + - 1: +1 + - 2: +2 + - 3: +3 + - 4: -0 + - 5: -3 + - 6: -2 + - 7: -1 +- `54xy` set RS of operator. + - `x` is the operator (1-4). a value of 0 means "all operators". + - `y` is the value. +- `55xy` set SSG-EG of operator. + - `x` is the operator (1-4). a value of 0 means "all operators". + - `y` is the value (0-8). + - values between 0 and 7 set SSG-EG. + - value 8 disables it. +- `56xx`: set DR of all operators. +- `57xx`: set DR of operator 1. +- `58xx`: set DR of operator 2. +- `59xx`: set DR of operator 3. +- `5Axx`: set DR of operator 4. +- `5Bxx`: set D2R/SR of all operators. +- `5Cxx`: set D2R/SR of operator 1. +- `5Dxx`: set D2R/SR of operator 2. +- `5Exx`: set D2R/SR of operator 3. +- `5Fxx`: set D2R/SR of operator 4. \ No newline at end of file From 3f0c83eb45c08b8757bdd8d5aeb8658247e62ece Mon Sep 17 00:00:00 2001 From: cam900 Date: Tue, 3 May 2022 10:20:20 +0900 Subject: [PATCH 301/342] Debug improvements Add oscilloscope debug, Hideable sample debug --- .gitignore | 1 + src/engine/dispatch.h | 6 +- src/gui/debugWindow.cpp | 118 +++++++++++++++++++++++++++++----------- 3 files changed, 92 insertions(+), 33 deletions(-) diff --git a/.gitignore b/.gitignore index df492a378..636c827fc 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,4 @@ test/songs/ test/delta/ test/result/ .vs/ +CMakeSettings.json \ No newline at end of file diff --git a/src/engine/dispatch.h b/src/engine/dispatch.h index 2646a7947..8799c7821 100644 --- a/src/engine/dispatch.h +++ b/src/engine/dispatch.h @@ -222,15 +222,19 @@ struct DivRegWrite { }; struct DivDispatchOscBuffer { + bool follow; unsigned int rate; unsigned short needle; unsigned short readNeedle; + unsigned short followNeedle; short data[65536]; DivDispatchOscBuffer(): + follow(true), rate(65536), needle(0), - readNeedle(0) { + readNeedle(0), + followNeedle(0) { memset(data,0,65536*sizeof(short)); } }; diff --git a/src/gui/debugWindow.cpp b/src/gui/debugWindow.cpp index 7b232a9cf..dc21a8c60 100644 --- a/src/gui/debugWindow.cpp +++ b/src/gui/debugWindow.cpp @@ -149,40 +149,94 @@ void FurnaceGUI::drawDebug() { ImGui::Text("%d: ",i); continue; } - ImGui::Text("%d: %s",i,sample->name.c_str()); - ImGui::Indent(); - ImGui::Text("rate: %d",sample->rate); - ImGui::Text("centerRate: %d",sample->centerRate); - ImGui::Text("loopStart: %d",sample->loopStart); - ImGui::Text("loopOffP: %d",sample->loopOffP); - ImGui::Text("depth: %d",sample->depth); - ImGui::Text("length8: %d",sample->length8); - ImGui::Text("length16: %d",sample->length16); - ImGui::Text("length1: %d",sample->length1); - ImGui::Text("lengthDPCM: %d",sample->lengthDPCM); - ImGui::Text("lengthQSoundA: %d",sample->lengthQSoundA); - ImGui::Text("lengthA: %d",sample->lengthA); - ImGui::Text("lengthB: %d",sample->lengthB); - ImGui::Text("lengthX68: %d",sample->lengthX68); - ImGui::Text("lengthBRR: %d",sample->lengthBRR); - ImGui::Text("lengthVOX: %d",sample->lengthVOX); + if (ImGui::TreeNode(fmt::sprintf("%d: %s",i,sample->name).c_str())) { + ImGui::Text("rate: %d",sample->rate); + ImGui::Text("centerRate: %d",sample->centerRate); + ImGui::Text("loopStart: %d",sample->loopStart); + ImGui::Text("loopOffP: %d",sample->loopOffP); + ImGui::Text("depth: %d",sample->depth); + ImGui::Text("length8: %d",sample->length8); + ImGui::Text("length16: %d",sample->length16); + ImGui::Text("length1: %d",sample->length1); + ImGui::Text("lengthDPCM: %d",sample->lengthDPCM); + ImGui::Text("lengthQSoundA: %d",sample->lengthQSoundA); + ImGui::Text("lengthA: %d",sample->lengthA); + ImGui::Text("lengthB: %d",sample->lengthB); + ImGui::Text("lengthX68: %d",sample->lengthX68); + ImGui::Text("lengthBRR: %d",sample->lengthBRR); + ImGui::Text("lengthVOX: %d",sample->lengthVOX); - ImGui::Text("off8: %x",sample->off8); - ImGui::Text("off16: %x",sample->off16); - ImGui::Text("off1: %x",sample->off1); - ImGui::Text("offDPCM: %x",sample->offDPCM); - ImGui::Text("offQSoundA: %x",sample->offQSoundA); - ImGui::Text("offA: %x",sample->offA); - ImGui::Text("offB: %x",sample->offB); - ImGui::Text("offX68: %x",sample->offX68); - ImGui::Text("offBRR: %x",sample->offBRR); - ImGui::Text("offVOX: %x",sample->offVOX); - ImGui::Text("offSegaPCM: %x",sample->offSegaPCM); - ImGui::Text("offQSound: %x",sample->offQSound); - ImGui::Text("offX1_010: %x",sample->offX1_010); + ImGui::Text("off8: %x",sample->off8); + ImGui::Text("off16: %x",sample->off16); + ImGui::Text("off1: %x",sample->off1); + ImGui::Text("offDPCM: %x",sample->offDPCM); + ImGui::Text("offQSoundA: %x",sample->offQSoundA); + ImGui::Text("offA: %x",sample->offA); + ImGui::Text("offB: %x",sample->offB); + ImGui::Text("offX68: %x",sample->offX68); + ImGui::Text("offBRR: %x",sample->offBRR); + ImGui::Text("offVOX: %x",sample->offVOX); + ImGui::Text("offSegaPCM: %x",sample->offSegaPCM); + ImGui::Text("offQSound: %x",sample->offQSound); + ImGui::Text("offX1_010: %x",sample->offX1_010); - ImGui::Text("samples: %d",sample->samples); - ImGui::Unindent(); + ImGui::Text("samples: %d",sample->samples); + ImGui::TreePop(); + } + } + ImGui::TreePop(); + } + if (ImGui::TreeNode("Oscilloscope Debug")) { + int c=0; + for (int i=0; isong.systemLen; i++) { + DivSystem system=e->song.system[i]; + if (e->getChannelCount(system)>0) { + if (ImGui::TreeNode(fmt::sprintf("%d: %s",i,e->getSystemName(system)).c_str())) { + if (ImGui::BeginTable("OscilloscopeTable",4,ImGuiTableFlags_Borders|ImGuiTableFlags_SizingStretchSame)) { + ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthFixed); + ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthFixed); + ImGui::TableSetupColumn("c2",ImGuiTableColumnFlags_WidthStretch); + ImGui::TableSetupColumn("c3",ImGuiTableColumnFlags_WidthStretch); + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::Text("Channel"); + ImGui::TableNextColumn(); + ImGui::Text("Follow"); + ImGui::TableNextColumn(); + ImGui::Text("Address"); + ImGui::TableNextColumn(); + ImGui::Text("Data"); + + for (int j=0; jgetChannelCount(system); j++) { + ImGui::TableNextRow(); + // channel + ImGui::TableNextColumn(); + ImGui::Text("%d",j); + // follow + ImGui::TableNextColumn(); + ImGui::Checkbox(fmt::sprintf("##%d_OSCFollow_%d",i,c).c_str(),&e->getOscBuffer(c)->follow); + // address + ImGui::TableNextColumn(); + int needle=e->getOscBuffer(c)->follow?e->getOscBuffer(c)->needle:e->getOscBuffer(c)->followNeedle; + ImGui::BeginDisabled(e->getOscBuffer(c)->follow); + if (ImGui::InputInt(fmt::sprintf("##%d_OSCFollowNeedle_%d",i,c).c_str(),&needle,1,100)) { + e->getOscBuffer(c)->followNeedle=MIN(MAX(needle,0),65535); + } + ImGui::EndDisabled(); + // data + ImGui::TableNextColumn(); + ImGui::Text("%d",e->getOscBuffer(c)->data[needle]); + c++; + } + ImGui::EndTable(); + } + ImGui::TreePop(); + } + } else { + ImGui::Text("%d: ",i); + continue; + } } ImGui::TreePop(); } From 0d5962bb08006e68ab6528d7c90c2ee896e37c3b Mon Sep 17 00:00:00 2001 From: cam900 Date: Tue, 3 May 2022 10:28:01 +0900 Subject: [PATCH 302/342] Reduce unnecessary line --- src/gui/debugWindow.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/gui/debugWindow.cpp b/src/gui/debugWindow.cpp index cc4bb1592..71856c8fc 100644 --- a/src/gui/debugWindow.cpp +++ b/src/gui/debugWindow.cpp @@ -209,7 +209,7 @@ void FurnaceGUI::drawDebug() { ImGui::TableNextColumn(); ImGui::Text("Data"); - for (int j=0; jgetChannelCount(system); j++) { + for (int j=0; jgetChannelCount(system); j++, c++) { ImGui::TableNextRow(); // channel ImGui::TableNextColumn(); @@ -228,7 +228,6 @@ void FurnaceGUI::drawDebug() { // data ImGui::TableNextColumn(); ImGui::Text("%d",e->getOscBuffer(c)->data[needle]); - c++; } ImGui::EndTable(); } From ebc3df9494c09de950b95b08bd918acf09da5fe9 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Mon, 2 May 2022 23:38:54 -0500 Subject: [PATCH 303/342] NO --- papers/doc/7-systems/opl.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/papers/doc/7-systems/opl.md b/papers/doc/7-systems/opl.md index 056591a6d..683be51e6 100644 --- a/papers/doc/7-systems/opl.md +++ b/papers/doc/7-systems/opl.md @@ -71,9 +71,9 @@ afterwards everyone moved to Windows and software mixed PCM streaming... - `56xx`: set DR of all operators. - `57xx`: set DR of operator 1. - `58xx`: set DR of operator 2. -- `58xx`: set DR of operator 3. +- `59xx`: set DR of operator 3. - only in 4-op mode (OPL3). -- `58xx`: set DR of operator 4. +- `5Axx`: set DR of operator 4. - only in 4-op mode (OPL3). - `5Bxy`: set KSR of operator. - `x` is the operator (1-4; last 2 operators only in 4-op mode). a value of 0 means "all operators". From e40e7c0fef9cf17b738a6b96f0e99dd360f460f2 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Tue, 3 May 2022 01:23:38 -0500 Subject: [PATCH 304/342] sysDef refactor, part 3 - PLEASE READ to cam900 and grauw: I have moved the per-system effect processing code to sysDef.cpp as lambdas you may want to do the same when you pull this commit. --- src/engine/engine.h | 4 +- src/engine/playback.cpp | 679 +------------------------------ src/engine/sysDef.cpp | 870 ++++++++++++++++++++++++++++++++++++++-- 3 files changed, 832 insertions(+), 721 deletions(-) diff --git a/src/engine/engine.h b/src/engine/engine.h index dc1436eed..809df944d 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -208,8 +208,8 @@ struct DivSysDef { std::initializer_list chTypes, std::initializer_list chInsType1, std::initializer_list chInsType2={}, - EffectProcess fxHandler=NULL, - EffectProcess postFxHandler=NULL): + EffectProcess fxHandler=[](int,unsigned char,unsigned char) -> bool {return false;}, + EffectProcess postFxHandler=[](int,unsigned char,unsigned char) -> bool {return false;}): name(sysName), nameJ(sysNameJ), id(fileID), diff --git a/src/engine/playback.cpp b/src/engine/playback.cpp index c959f64ac..0010d5a99 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -251,684 +251,13 @@ int DivEngine::dispatchCmd(DivCommand c) { } bool DivEngine::perSystemEffect(int ch, unsigned char effect, unsigned char effectVal) { - switch (sysOfChan[ch]) { - case DIV_SYSTEM_YM2612: - case DIV_SYSTEM_YM2612_EXT: - switch (effect) { - case 0x17: // DAC enable - dispatchCmd(DivCommand(DIV_CMD_SAMPLE_MODE,ch,(effectVal>0))); - break; - case 0x20: // SN noise mode - dispatchCmd(DivCommand(DIV_CMD_STD_NOISE_MODE,ch,effectVal)); - break; - case 0x30: // toggle hard-reset - dispatchCmd(DivCommand(DIV_CMD_FM_HARD_RESET,ch,effectVal)); - break; - default: - return false; - } - break; - case DIV_SYSTEM_YM2151: - case DIV_SYSTEM_YM2610: - case DIV_SYSTEM_YM2610_EXT: - case DIV_SYSTEM_YM2610B: - case DIV_SYSTEM_YM2610B_EXT: - case DIV_SYSTEM_OPZ: - switch (effect) { - case 0x30: // toggle hard-reset - dispatchCmd(DivCommand(DIV_CMD_FM_HARD_RESET,ch,effectVal)); - break; - default: - return false; - } - break; - case DIV_SYSTEM_SMS: - switch (effect) { - case 0x20: // SN noise mode - dispatchCmd(DivCommand(DIV_CMD_STD_NOISE_MODE,ch,effectVal)); - break; - default: - return false; - } - break; - case DIV_SYSTEM_GB: - switch (effect) { - case 0x10: // select waveform - dispatchCmd(DivCommand(DIV_CMD_WAVE,ch,effectVal)); - break; - case 0x11: case 0x12: // duty or noise mode - dispatchCmd(DivCommand(DIV_CMD_STD_NOISE_MODE,ch,effectVal)); - break; - case 0x13: // sweep params - dispatchCmd(DivCommand(DIV_CMD_GB_SWEEP_TIME,ch,effectVal)); - break; - case 0x14: // sweep direction - dispatchCmd(DivCommand(DIV_CMD_GB_SWEEP_DIR,ch,effectVal)); - break; - default: - return false; - } - break; - case DIV_SYSTEM_PCE: - switch (effect) { - case 0x10: // select waveform - dispatchCmd(DivCommand(DIV_CMD_WAVE,ch,effectVal)); - break; - case 0x11: // noise mode - dispatchCmd(DivCommand(DIV_CMD_STD_NOISE_MODE,ch,effectVal)); - break; - case 0x12: // LFO mode - dispatchCmd(DivCommand(DIV_CMD_PCE_LFO_MODE,ch,effectVal)); - break; - case 0x13: // LFO speed - dispatchCmd(DivCommand(DIV_CMD_PCE_LFO_SPEED,ch,effectVal)); - break; - case 0x17: // PCM enable - dispatchCmd(DivCommand(DIV_CMD_SAMPLE_MODE,ch,(effectVal>0))); - break; - default: - return false; - } - break; - case DIV_SYSTEM_NES: - case DIV_SYSTEM_MMC5: - switch (effect) { - case 0x11: // DMC write - dispatchCmd(DivCommand(DIV_CMD_NES_DMC,ch,effectVal)); - break; - case 0x12: // duty or noise mode - dispatchCmd(DivCommand(DIV_CMD_STD_NOISE_MODE,ch,effectVal)); - break; - case 0x13: // sweep up - dispatchCmd(DivCommand(DIV_CMD_NES_SWEEP,ch,0,effectVal)); - break; - case 0x14: // sweep down - dispatchCmd(DivCommand(DIV_CMD_NES_SWEEP,ch,1,effectVal)); - break; - case 0x18: // DPCM mode - dispatchCmd(DivCommand(DIV_CMD_SAMPLE_MODE,ch,effectVal)); - break; - default: - return false; - } - break; - case DIV_SYSTEM_FDS: - switch (effect) { - case 0x10: // select waveform - dispatchCmd(DivCommand(DIV_CMD_WAVE,ch,effectVal)); - break; - case 0x11: // modulation depth - dispatchCmd(DivCommand(DIV_CMD_FDS_MOD_DEPTH,ch,effectVal)); - break; - case 0x12: // modulation enable/high - dispatchCmd(DivCommand(DIV_CMD_FDS_MOD_HIGH,ch,effectVal)); - break; - case 0x13: // modulation low - dispatchCmd(DivCommand(DIV_CMD_FDS_MOD_LOW,ch,effectVal)); - break; - case 0x14: // modulation pos - dispatchCmd(DivCommand(DIV_CMD_FDS_MOD_POS,ch,effectVal)); - break; - case 0x15: // modulation wave - dispatchCmd(DivCommand(DIV_CMD_FDS_MOD_WAVE,ch,effectVal)); - break; - default: - return false; - } - break; - case DIV_SYSTEM_OPLL_DRUMS: - case DIV_SYSTEM_OPL_DRUMS: - case DIV_SYSTEM_OPL2_DRUMS: - case DIV_SYSTEM_OPL3_DRUMS: - switch (effect) { - case 0x18: // drum mode toggle - dispatchCmd(DivCommand(DIV_CMD_FM_EXTCH,ch,effectVal)); - break; - case 0x30: // toggle hard-reset - dispatchCmd(DivCommand(DIV_CMD_FM_HARD_RESET,ch,effectVal)); - break; - default: - return false; - } - break; - case DIV_SYSTEM_OPLL: - case DIV_SYSTEM_VRC7: - case DIV_SYSTEM_OPL: - case DIV_SYSTEM_OPL2: - case DIV_SYSTEM_OPL3: - switch (effect) { - case 0x30: // toggle hard-reset - dispatchCmd(DivCommand(DIV_CMD_FM_HARD_RESET,ch,effectVal)); - break; - default: - return false; - } - break; - case DIV_SYSTEM_N163: - switch (effect) { - case 0x10: // select instrument waveform - dispatchCmd(DivCommand(DIV_CMD_WAVE,ch,effectVal)); - break; - case 0x11: // select instrument waveform position in RAM - dispatchCmd(DivCommand(DIV_CMD_N163_WAVE_POSITION,ch,effectVal)); - break; - case 0x12: // select instrument waveform length in RAM - dispatchCmd(DivCommand(DIV_CMD_N163_WAVE_LENGTH,ch,effectVal)); - break; - case 0x13: // change instrument waveform update mode - dispatchCmd(DivCommand(DIV_CMD_N163_WAVE_MODE,ch,effectVal)); - break; - case 0x14: // select waveform for load to RAM - dispatchCmd(DivCommand(DIV_CMD_N163_WAVE_LOAD,ch,effectVal)); - break; - case 0x15: // select waveform position for load to RAM - dispatchCmd(DivCommand(DIV_CMD_N163_WAVE_LOADPOS,ch,effectVal)); - break; - case 0x16: // select waveform length for load to RAM - dispatchCmd(DivCommand(DIV_CMD_N163_WAVE_LOADLEN,ch,effectVal)); - break; - case 0x17: // change waveform load mode - dispatchCmd(DivCommand(DIV_CMD_N163_WAVE_LOADMODE,ch,effectVal)); - break; - case 0x18: // change channel limits - dispatchCmd(DivCommand(DIV_CMD_N163_CHANNEL_LIMIT,ch,effectVal)); - break; - case 0x20: // (global) select waveform for load to RAM - dispatchCmd(DivCommand(DIV_CMD_N163_GLOBAL_WAVE_LOAD,ch,effectVal)); - break; - case 0x21: // (global) select waveform position for load to RAM - dispatchCmd(DivCommand(DIV_CMD_N163_GLOBAL_WAVE_LOADPOS,ch,effectVal)); - break; - case 0x22: // (global) select waveform length for load to RAM - dispatchCmd(DivCommand(DIV_CMD_N163_GLOBAL_WAVE_LOADLEN,ch,effectVal)); - break; - case 0x23: // (global) change waveform load mode - dispatchCmd(DivCommand(DIV_CMD_N163_GLOBAL_WAVE_LOADMODE,ch,effectVal)); - break; - default: - return false; - } - break; - case DIV_SYSTEM_QSOUND: - switch (effect) { - case 0x10: // echo feedback - dispatchCmd(DivCommand(DIV_CMD_QSOUND_ECHO_FEEDBACK,ch,effectVal)); - break; - case 0x11: // echo level - dispatchCmd(DivCommand(DIV_CMD_QSOUND_ECHO_LEVEL,ch,effectVal)); - break; - case 0x12: // surround - dispatchCmd(DivCommand(DIV_CMD_QSOUND_SURROUND,ch,effectVal)); - break; - default: - if ((effect&0xf0)==0x30) { - dispatchCmd(DivCommand(DIV_CMD_QSOUND_ECHO_DELAY,ch,((effect & 0x0f) << 8) | effectVal)); - } else { - return false; - } - break; - } - break; - case DIV_SYSTEM_X1_010: - switch (effect) { - case 0x10: // select waveform - dispatchCmd(DivCommand(DIV_CMD_WAVE,ch,effectVal)); - break; - case 0x11: // select envelope shape - dispatchCmd(DivCommand(DIV_CMD_X1_010_ENVELOPE_SHAPE,ch,effectVal)); - break; - case 0x17: // PCM enable - dispatchCmd(DivCommand(DIV_CMD_SAMPLE_MODE,ch,(effectVal>0))); - break; - default: - return false; - } - break; - case DIV_SYSTEM_SWAN: - switch (effect) { - case 0x10: // select waveform - dispatchCmd(DivCommand(DIV_CMD_WAVE,ch,effectVal)); - break; - case 0x11: // noise mode - dispatchCmd(DivCommand(DIV_CMD_STD_NOISE_MODE,ch,effectVal)); - break; - case 0x12: // sweep period - dispatchCmd(DivCommand(DIV_CMD_WS_SWEEP_TIME,ch,effectVal)); - break; - case 0x13: // sweep amount - dispatchCmd(DivCommand(DIV_CMD_WS_SWEEP_AMOUNT,ch,effectVal)); - break; - case 0x17: // PCM enable - dispatchCmd(DivCommand(DIV_CMD_SAMPLE_MODE,ch,(effectVal>0))); - break; - default: - return false; - } - break; - case DIV_SYSTEM_VERA: - switch (effect) { - case 0x20: // select waveform - dispatchCmd(DivCommand(DIV_CMD_WAVE,ch,effectVal)); - break; - case 0x22: // duty - dispatchCmd(DivCommand(DIV_CMD_STD_NOISE_MODE,ch,effectVal)); - break; - default: - return false; - } - break; - case DIV_SYSTEM_BUBSYS_WSG: - case DIV_SYSTEM_PET: - case DIV_SYSTEM_VIC20: - switch (effect) { - case 0x10: // select waveform - dispatchCmd(DivCommand(DIV_CMD_WAVE,ch,effectVal)); - break; - default: - return false; - } - break; - case DIV_SYSTEM_VRC6: - switch (effect) { - case 0x12: // duty or noise mode - dispatchCmd(DivCommand(DIV_CMD_STD_NOISE_MODE,ch,effectVal)); - break; - case 0x17: // PCM enable - dispatchCmd(DivCommand(DIV_CMD_SAMPLE_MODE,ch,(effectVal>0))); - break; - default: - return false; - } - break; - default: - return false; - } - return true; + if (sysDefs[sysOfChan[ch]]==NULL) return false; + return sysDefs[sysOfChan[ch]]->effectFunc(ch,effect,effectVal); } -#define IS_YM2610 (sysOfChan[ch]==DIV_SYSTEM_YM2610 || sysOfChan[ch]==DIV_SYSTEM_YM2610_EXT || sysOfChan[ch]==DIV_SYSTEM_YM2610_FULL || sysOfChan[ch]==DIV_SYSTEM_YM2610_FULL_EXT || sysOfChan[ch]==DIV_SYSTEM_YM2610B || sysOfChan[ch]==DIV_SYSTEM_YM2610B_EXT) -#define IS_OPM_LIKE (sysOfChan[ch]==DIV_SYSTEM_YM2151 || sysOfChan[ch]==DIV_SYSTEM_OPZ) - bool DivEngine::perSystemPostEffect(int ch, unsigned char effect, unsigned char effectVal) { - switch (sysOfChan[ch]) { - case DIV_SYSTEM_YM2612: - case DIV_SYSTEM_YM2612_EXT: - case DIV_SYSTEM_YM2151: - case DIV_SYSTEM_YM2610: - case DIV_SYSTEM_YM2610_EXT: - case DIV_SYSTEM_YM2610_FULL: - case DIV_SYSTEM_YM2610_FULL_EXT: - case DIV_SYSTEM_YM2610B: - case DIV_SYSTEM_YM2610B_EXT: - case DIV_SYSTEM_OPZ: - switch (effect) { - case 0x10: // LFO or noise mode - if (IS_OPM_LIKE) { - dispatchCmd(DivCommand(DIV_CMD_STD_NOISE_FREQ,ch,effectVal)); - } else { - dispatchCmd(DivCommand(DIV_CMD_FM_LFO,ch,effectVal)); - } - break; - case 0x11: // FB - dispatchCmd(DivCommand(DIV_CMD_FM_FB,ch,effectVal&7)); - break; - case 0x12: // TL op1 - dispatchCmd(DivCommand(DIV_CMD_FM_TL,ch,0,effectVal&0x7f)); - break; - case 0x13: // TL op2 - dispatchCmd(DivCommand(DIV_CMD_FM_TL,ch,1,effectVal&0x7f)); - break; - case 0x14: // TL op3 - dispatchCmd(DivCommand(DIV_CMD_FM_TL,ch,2,effectVal&0x7f)); - break; - case 0x15: // TL op4 - dispatchCmd(DivCommand(DIV_CMD_FM_TL,ch,3,effectVal&0x7f)); - break; - case 0x16: // MULT - if ((effectVal>>4)>0 && (effectVal>>4)<5) { - dispatchCmd(DivCommand(DIV_CMD_FM_MULT,ch,(effectVal>>4)-1,effectVal&15)); - } - break; - case 0x17: // arcade LFO - if (IS_OPM_LIKE) { - dispatchCmd(DivCommand(DIV_CMD_FM_LFO,ch,effectVal)); - } - break; - case 0x18: // EXT or LFO waveform - if (IS_OPM_LIKE) { - dispatchCmd(DivCommand(DIV_CMD_FM_LFO_WAVE,ch,effectVal)); - } else { - dispatchCmd(DivCommand(DIV_CMD_FM_EXTCH,ch,effectVal)); - } - break; - case 0x19: // AR global - dispatchCmd(DivCommand(DIV_CMD_FM_AR,ch,-1,effectVal&31)); - break; - case 0x1a: // AR op1 - dispatchCmd(DivCommand(DIV_CMD_FM_AR,ch,0,effectVal&31)); - break; - case 0x1b: // AR op2 - dispatchCmd(DivCommand(DIV_CMD_FM_AR,ch,1,effectVal&31)); - break; - case 0x1c: // AR op3 - dispatchCmd(DivCommand(DIV_CMD_FM_AR,ch,2,effectVal&31)); - break; - case 0x1d: // AR op4 - dispatchCmd(DivCommand(DIV_CMD_FM_AR,ch,3,effectVal&31)); - break; - case 0x1e: // UNOFFICIAL: Arcade AM depth - dispatchCmd(DivCommand(DIV_CMD_FM_AM_DEPTH,ch,effectVal&127)); - break; - case 0x1f: // UNOFFICIAL: Arcade PM depth - dispatchCmd(DivCommand(DIV_CMD_FM_PM_DEPTH,ch,effectVal&127)); - break; - case 0x20: // Neo Geo PSG mode - if (IS_YM2610) { - dispatchCmd(DivCommand(DIV_CMD_STD_NOISE_MODE,ch,effectVal)); - } - break; - case 0x21: // Neo Geo PSG noise freq - if (IS_YM2610) { - dispatchCmd(DivCommand(DIV_CMD_STD_NOISE_FREQ,ch,effectVal)); - } - break; - case 0x22: // UNOFFICIAL: Neo Geo PSG envelope enable - if (IS_YM2610) { - dispatchCmd(DivCommand(DIV_CMD_AY_ENVELOPE_SET,ch,effectVal)); - } - break; - case 0x23: // UNOFFICIAL: Neo Geo PSG envelope period low - if (IS_YM2610) { - dispatchCmd(DivCommand(DIV_CMD_AY_ENVELOPE_LOW,ch,effectVal)); - } - break; - case 0x24: // UNOFFICIAL: Neo Geo PSG envelope period high - if (IS_YM2610) { - dispatchCmd(DivCommand(DIV_CMD_AY_ENVELOPE_HIGH,ch,effectVal)); - } - break; - case 0x25: // UNOFFICIAL: Neo Geo PSG envelope slide up - if (IS_YM2610) { - dispatchCmd(DivCommand(DIV_CMD_AY_ENVELOPE_SLIDE,ch,-effectVal)); - } - break; - case 0x26: // UNOFFICIAL: Neo Geo PSG envelope slide down - if (IS_YM2610) { - dispatchCmd(DivCommand(DIV_CMD_AY_ENVELOPE_SLIDE,ch,effectVal)); - } - break; - case 0x29: // auto-envelope - if (IS_YM2610) { - dispatchCmd(DivCommand(DIV_CMD_AY_AUTO_ENVELOPE,ch,effectVal)); - } - break; - default: - return false; - } - break; - case DIV_SYSTEM_OPLL: - case DIV_SYSTEM_OPLL_DRUMS: - case DIV_SYSTEM_VRC7: - switch (effect) { - case 0x11: // FB - dispatchCmd(DivCommand(DIV_CMD_FM_FB,ch,effectVal&7)); - break; - case 0x12: // TL op1 - dispatchCmd(DivCommand(DIV_CMD_FM_TL,ch,0,effectVal&0x3f)); - break; - case 0x13: // TL op2 - dispatchCmd(DivCommand(DIV_CMD_FM_TL,ch,1,effectVal&0x0f)); - break; - case 0x16: // MULT - if ((effectVal>>4)>0 && (effectVal>>4)<3) { - dispatchCmd(DivCommand(DIV_CMD_FM_MULT,ch,(effectVal>>4)-1,effectVal&15)); - } - break; - case 0x19: // AR global - dispatchCmd(DivCommand(DIV_CMD_FM_AR,ch,-1,effectVal&31)); - break; - case 0x1a: // AR op1 - dispatchCmd(DivCommand(DIV_CMD_FM_AR,ch,0,effectVal&31)); - break; - case 0x1b: // AR op2 - dispatchCmd(DivCommand(DIV_CMD_FM_AR,ch,1,effectVal&31)); - break; - default: - return false; - } - break; - case DIV_SYSTEM_OPL: - case DIV_SYSTEM_OPL2: - case DIV_SYSTEM_OPL3: - case DIV_SYSTEM_OPL_DRUMS: - case DIV_SYSTEM_OPL2_DRUMS: - case DIV_SYSTEM_OPL3_DRUMS: - switch (effect) { - case 0x10: // DAM - dispatchCmd(DivCommand(DIV_CMD_FM_LFO,ch,effectVal&1)); - break; - case 0x11: // FB - dispatchCmd(DivCommand(DIV_CMD_FM_FB,ch,effectVal&7)); - break; - case 0x12: // TL op1 - dispatchCmd(DivCommand(DIV_CMD_FM_TL,ch,0,effectVal&0x3f)); - break; - case 0x13: // TL op2 - dispatchCmd(DivCommand(DIV_CMD_FM_TL,ch,1,effectVal&0x3f)); - break; - case 0x14: // TL op3 - dispatchCmd(DivCommand(DIV_CMD_FM_TL,ch,2,effectVal&0x3f)); - break; - case 0x15: // TL op4 - dispatchCmd(DivCommand(DIV_CMD_FM_TL,ch,3,effectVal&0x3f)); - break; - case 0x16: // MULT - if ((effectVal>>4)>0 && (effectVal>>4)<5) { - dispatchCmd(DivCommand(DIV_CMD_FM_MULT,ch,(effectVal>>4)-1,effectVal&15)); - } - break; - case 0x17: // DVB - dispatchCmd(DivCommand(DIV_CMD_FM_LFO,ch,2+(effectVal&1))); - break; - case 0x19: // AR global - dispatchCmd(DivCommand(DIV_CMD_FM_AR,ch,-1,effectVal&15)); - break; - case 0x1a: // AR op1 - dispatchCmd(DivCommand(DIV_CMD_FM_AR,ch,0,effectVal&15)); - break; - case 0x1b: // AR op2 - dispatchCmd(DivCommand(DIV_CMD_FM_AR,ch,1,effectVal&15)); - break; - case 0x1c: // AR op3 - dispatchCmd(DivCommand(DIV_CMD_FM_AR,ch,2,effectVal&15)); - break; - case 0x1d: // AR op4 - dispatchCmd(DivCommand(DIV_CMD_FM_AR,ch,3,effectVal&15)); - break; - default: - return false; - } - break; - case DIV_SYSTEM_C64_6581: case DIV_SYSTEM_C64_8580: - switch (effect) { - case 0x10: // select waveform - dispatchCmd(DivCommand(DIV_CMD_WAVE,ch,effectVal)); - break; - case 0x11: // cutoff - dispatchCmd(DivCommand(DIV_CMD_C64_CUTOFF,ch,effectVal)); - break; - case 0x12: // duty - dispatchCmd(DivCommand(DIV_CMD_STD_NOISE_MODE,ch,effectVal)); - break; - case 0x13: // resonance - dispatchCmd(DivCommand(DIV_CMD_C64_RESONANCE,ch,effectVal)); - break; - case 0x14: // filter mode - dispatchCmd(DivCommand(DIV_CMD_C64_FILTER_MODE,ch,effectVal)); - break; - case 0x15: // reset time - dispatchCmd(DivCommand(DIV_CMD_C64_RESET_TIME,ch,effectVal)); - break; - case 0x1a: // reset mask - dispatchCmd(DivCommand(DIV_CMD_C64_RESET_MASK,ch,effectVal)); - break; - case 0x1b: // cutoff reset - dispatchCmd(DivCommand(DIV_CMD_C64_FILTER_RESET,ch,effectVal)); - break; - case 0x1c: // duty reset - dispatchCmd(DivCommand(DIV_CMD_C64_DUTY_RESET,ch,effectVal)); - break; - case 0x1e: // extended - dispatchCmd(DivCommand(DIV_CMD_C64_EXTENDED,ch,effectVal)); - break; - case 0x30: case 0x31: case 0x32: case 0x33: - case 0x34: case 0x35: case 0x36: case 0x37: - case 0x38: case 0x39: case 0x3a: case 0x3b: - case 0x3c: case 0x3d: case 0x3e: case 0x3f: // fine duty - dispatchCmd(DivCommand(DIV_CMD_C64_FINE_DUTY,ch,((effect&0x0f)<<8)|effectVal)); - break; - case 0x40: case 0x41: case 0x42: case 0x43: - case 0x44: case 0x45: case 0x46: case 0x47: // fine cutoff - dispatchCmd(DivCommand(DIV_CMD_C64_FINE_CUTOFF,ch,((effect&0x07)<<8)|effectVal)); - break; - default: - return false; - } - break; - case DIV_SYSTEM_AY8910: - case DIV_SYSTEM_AY8930: - switch (effect) { - case 0x12: // duty on 8930 - dispatchCmd(DivCommand(DIV_CMD_STD_NOISE_MODE,ch,0x10+(effectVal&15))); - break; - case 0x20: // mode - dispatchCmd(DivCommand(DIV_CMD_STD_NOISE_MODE,ch,effectVal&15)); - break; - case 0x21: // noise freq - dispatchCmd(DivCommand(DIV_CMD_STD_NOISE_FREQ,ch,effectVal)); - break; - case 0x22: // envelope enable - dispatchCmd(DivCommand(DIV_CMD_AY_ENVELOPE_SET,ch,effectVal)); - break; - case 0x23: // envelope period low - dispatchCmd(DivCommand(DIV_CMD_AY_ENVELOPE_LOW,ch,effectVal)); - break; - case 0x24: // envelope period high - dispatchCmd(DivCommand(DIV_CMD_AY_ENVELOPE_HIGH,ch,effectVal)); - break; - case 0x25: // envelope slide up - dispatchCmd(DivCommand(DIV_CMD_AY_ENVELOPE_SLIDE,ch,-effectVal)); - break; - case 0x26: // envelope slide down - dispatchCmd(DivCommand(DIV_CMD_AY_ENVELOPE_SLIDE,ch,effectVal)); - break; - case 0x27: // noise and mask - dispatchCmd(DivCommand(DIV_CMD_AY_NOISE_MASK_AND,ch,effectVal)); - break; - case 0x28: // noise or mask - dispatchCmd(DivCommand(DIV_CMD_AY_NOISE_MASK_OR,ch,effectVal)); - break; - case 0x29: // auto-envelope - dispatchCmd(DivCommand(DIV_CMD_AY_AUTO_ENVELOPE,ch,effectVal)); - break; - case 0x2d: // TEST - dispatchCmd(DivCommand(DIV_CMD_AY_IO_WRITE,ch,255,effectVal)); - break; - case 0x2e: // I/O port A - dispatchCmd(DivCommand(DIV_CMD_AY_IO_WRITE,ch,0,effectVal)); - break; - case 0x2f: // I/O port B - dispatchCmd(DivCommand(DIV_CMD_AY_IO_WRITE,ch,1,effectVal)); - break; - default: - return false; - } - break; - case DIV_SYSTEM_SAA1099: - switch (effect) { - case 0x10: // select channel mode - dispatchCmd(DivCommand(DIV_CMD_STD_NOISE_MODE,ch,effectVal)); - break; - case 0x11: // set noise freq - dispatchCmd(DivCommand(DIV_CMD_STD_NOISE_FREQ,ch,effectVal)); - break; - case 0x12: // setup envelope - dispatchCmd(DivCommand(DIV_CMD_SAA_ENVELOPE,ch,effectVal)); - break; - default: - return false; - } - break; - case DIV_SYSTEM_TIA: - switch (effect) { - case 0x10: // select waveform - dispatchCmd(DivCommand(DIV_CMD_WAVE,ch,effectVal)); - break; - default: - return false; - } - break; - case DIV_SYSTEM_AMIGA: - switch (effect) { - case 0x10: // toggle filter - dispatchCmd(DivCommand(DIV_CMD_AMIGA_FILTER,ch,effectVal)); - break; - case 0x11: // toggle AM - dispatchCmd(DivCommand(DIV_CMD_AMIGA_AM,ch,effectVal)); - break; - case 0x12: // toggle PM - dispatchCmd(DivCommand(DIV_CMD_AMIGA_PM,ch,effectVal)); - break; - default: - return false; - } - break; - case DIV_SYSTEM_SEGAPCM: - case DIV_SYSTEM_SEGAPCM_COMPAT: - switch (effect) { - case 0x20: // PCM frequency - dispatchCmd(DivCommand(DIV_CMD_SAMPLE_FREQ,ch,effectVal)); - break; - default: - return false; - } - break; - case DIV_SYSTEM_LYNX: - if (effect>=0x30 && effect<0x40) { - int value = ((int)(effect&0x0f)<<8)|effectVal; - dispatchCmd(DivCommand(DIV_CMD_LYNX_LFSR_LOAD,ch,value)); - break; - } - return false; - break; - case DIV_SYSTEM_X1_010: - switch (effect) { - case 0x20: // PCM frequency - dispatchCmd(DivCommand(DIV_CMD_SAMPLE_FREQ,ch,effectVal)); - break; - case 0x22: // envelope mode - dispatchCmd(DivCommand(DIV_CMD_X1_010_ENVELOPE_MODE,ch,effectVal)); - break; - case 0x23: // envelope period - dispatchCmd(DivCommand(DIV_CMD_X1_010_ENVELOPE_PERIOD,ch,effectVal)); - break; - case 0x25: // envelope slide up - dispatchCmd(DivCommand(DIV_CMD_X1_010_ENVELOPE_SLIDE,ch,effectVal)); - break; - case 0x26: // envelope slide down - dispatchCmd(DivCommand(DIV_CMD_X1_010_ENVELOPE_SLIDE,ch,-effectVal)); - break; - case 0x29: // auto-envelope - dispatchCmd(DivCommand(DIV_CMD_X1_010_AUTO_ENVELOPE,ch,effectVal)); - break; - default: - return false; - } - break; - default: - return false; - } - return true; + if (sysDefs[sysOfChan[ch]]==NULL) return false; + return sysDefs[sysOfChan[ch]]->postEffectFunc(ch,effect,effectVal); } void DivEngine::processRow(int i, bool afterDelay) { diff --git a/src/engine/sysDef.cpp b/src/engine/sysDef.cpp index 8f280847c..aed1d9f7c 100644 --- a/src/engine/sysDef.cpp +++ b/src/engine/sysDef.cpp @@ -316,6 +316,9 @@ int DivEngine::minVGMVersion(DivSystem which) { return sysDefs[which]->vgmVersion; } +#define IS_YM2610 (sysOfChan[ch]==DIV_SYSTEM_YM2610 || sysOfChan[ch]==DIV_SYSTEM_YM2610_EXT || sysOfChan[ch]==DIV_SYSTEM_YM2610_FULL || sysOfChan[ch]==DIV_SYSTEM_YM2610_FULL_EXT || sysOfChan[ch]==DIV_SYSTEM_YM2610B || sysOfChan[ch]==DIV_SYSTEM_YM2610B_EXT) +#define IS_OPM_LIKE (sysOfChan[ch]==DIV_SYSTEM_YM2151 || sysOfChan[ch]==DIV_SYSTEM_OPZ) + // define systems like: // sysDefs[DIV_SYSTEM_ID]=new DivSysDef( // "Name", "Name (japanese, optional)", fileID, fileID_DMF, channels, isFM, isSTD, vgmVersion, @@ -331,6 +334,303 @@ int DivEngine::minVGMVersion(DivSystem which) { void DivEngine::registerSystems() { logD("registering systems..."); + auto fmPostEffectHandler=[this](int ch, unsigned char effect, unsigned char effectVal) -> bool { + switch (effect) { + case 0x10: // LFO or noise mode + if (IS_OPM_LIKE) { + dispatchCmd(DivCommand(DIV_CMD_STD_NOISE_FREQ,ch,effectVal)); + } else { + dispatchCmd(DivCommand(DIV_CMD_FM_LFO,ch,effectVal)); + } + break; + case 0x11: // FB + dispatchCmd(DivCommand(DIV_CMD_FM_FB,ch,effectVal&7)); + break; + case 0x12: // TL op1 + dispatchCmd(DivCommand(DIV_CMD_FM_TL,ch,0,effectVal&0x7f)); + break; + case 0x13: // TL op2 + dispatchCmd(DivCommand(DIV_CMD_FM_TL,ch,1,effectVal&0x7f)); + break; + case 0x14: // TL op3 + dispatchCmd(DivCommand(DIV_CMD_FM_TL,ch,2,effectVal&0x7f)); + break; + case 0x15: // TL op4 + dispatchCmd(DivCommand(DIV_CMD_FM_TL,ch,3,effectVal&0x7f)); + break; + case 0x16: // MULT + if ((effectVal>>4)>0 && (effectVal>>4)<5) { + dispatchCmd(DivCommand(DIV_CMD_FM_MULT,ch,(effectVal>>4)-1,effectVal&15)); + } + break; + case 0x17: // arcade LFO + if (IS_OPM_LIKE) { + dispatchCmd(DivCommand(DIV_CMD_FM_LFO,ch,effectVal)); + } + break; + case 0x18: // EXT or LFO waveform + if (IS_OPM_LIKE) { + dispatchCmd(DivCommand(DIV_CMD_FM_LFO_WAVE,ch,effectVal)); + } else { + dispatchCmd(DivCommand(DIV_CMD_FM_EXTCH,ch,effectVal)); + } + break; + case 0x19: // AR global + dispatchCmd(DivCommand(DIV_CMD_FM_AR,ch,-1,effectVal&31)); + break; + case 0x1a: // AR op1 + dispatchCmd(DivCommand(DIV_CMD_FM_AR,ch,0,effectVal&31)); + break; + case 0x1b: // AR op2 + dispatchCmd(DivCommand(DIV_CMD_FM_AR,ch,1,effectVal&31)); + break; + case 0x1c: // AR op3 + dispatchCmd(DivCommand(DIV_CMD_FM_AR,ch,2,effectVal&31)); + break; + case 0x1d: // AR op4 + dispatchCmd(DivCommand(DIV_CMD_FM_AR,ch,3,effectVal&31)); + break; + case 0x1e: // UNOFFICIAL: Arcade AM depth + dispatchCmd(DivCommand(DIV_CMD_FM_AM_DEPTH,ch,effectVal&127)); + break; + case 0x1f: // UNOFFICIAL: Arcade PM depth + dispatchCmd(DivCommand(DIV_CMD_FM_PM_DEPTH,ch,effectVal&127)); + break; + case 0x20: // Neo Geo PSG mode + if (IS_YM2610) { + dispatchCmd(DivCommand(DIV_CMD_STD_NOISE_MODE,ch,effectVal)); + } + break; + case 0x21: // Neo Geo PSG noise freq + if (IS_YM2610) { + dispatchCmd(DivCommand(DIV_CMD_STD_NOISE_FREQ,ch,effectVal)); + } + break; + case 0x22: // UNOFFICIAL: Neo Geo PSG envelope enable + if (IS_YM2610) { + dispatchCmd(DivCommand(DIV_CMD_AY_ENVELOPE_SET,ch,effectVal)); + } + break; + case 0x23: // UNOFFICIAL: Neo Geo PSG envelope period low + if (IS_YM2610) { + dispatchCmd(DivCommand(DIV_CMD_AY_ENVELOPE_LOW,ch,effectVal)); + } + break; + case 0x24: // UNOFFICIAL: Neo Geo PSG envelope period high + if (IS_YM2610) { + dispatchCmd(DivCommand(DIV_CMD_AY_ENVELOPE_HIGH,ch,effectVal)); + } + break; + case 0x25: // UNOFFICIAL: Neo Geo PSG envelope slide up + if (IS_YM2610) { + dispatchCmd(DivCommand(DIV_CMD_AY_ENVELOPE_SLIDE,ch,-effectVal)); + } + break; + case 0x26: // UNOFFICIAL: Neo Geo PSG envelope slide down + if (IS_YM2610) { + dispatchCmd(DivCommand(DIV_CMD_AY_ENVELOPE_SLIDE,ch,effectVal)); + } + break; + case 0x29: // auto-envelope + if (IS_YM2610) { + dispatchCmd(DivCommand(DIV_CMD_AY_AUTO_ENVELOPE,ch,effectVal)); + } + break; + default: + return false; + } + return true; + }; + + auto fmOPLLPostEffectHandler=[this](int ch, unsigned char effect, unsigned char effectVal) -> bool { + switch (effect) { + case 0x11: // FB + dispatchCmd(DivCommand(DIV_CMD_FM_FB,ch,effectVal&7)); + break; + case 0x12: // TL op1 + dispatchCmd(DivCommand(DIV_CMD_FM_TL,ch,0,effectVal&0x3f)); + break; + case 0x13: // TL op2 + dispatchCmd(DivCommand(DIV_CMD_FM_TL,ch,1,effectVal&0x0f)); + break; + case 0x16: // MULT + if ((effectVal>>4)>0 && (effectVal>>4)<3) { + dispatchCmd(DivCommand(DIV_CMD_FM_MULT,ch,(effectVal>>4)-1,effectVal&15)); + } + break; + case 0x19: // AR global + dispatchCmd(DivCommand(DIV_CMD_FM_AR,ch,-1,effectVal&31)); + break; + case 0x1a: // AR op1 + dispatchCmd(DivCommand(DIV_CMD_FM_AR,ch,0,effectVal&31)); + break; + case 0x1b: // AR op2 + dispatchCmd(DivCommand(DIV_CMD_FM_AR,ch,1,effectVal&31)); + break; + default: + return false; + } + return true; + }; + + auto fmOPLPostEffectHandler=[this](int ch, unsigned char effect, unsigned char effectVal) -> bool { + switch (effect) { + case 0x10: // DAM + dispatchCmd(DivCommand(DIV_CMD_FM_LFO,ch,effectVal&1)); + break; + case 0x11: // FB + dispatchCmd(DivCommand(DIV_CMD_FM_FB,ch,effectVal&7)); + break; + case 0x12: // TL op1 + dispatchCmd(DivCommand(DIV_CMD_FM_TL,ch,0,effectVal&0x3f)); + break; + case 0x13: // TL op2 + dispatchCmd(DivCommand(DIV_CMD_FM_TL,ch,1,effectVal&0x3f)); + break; + case 0x14: // TL op3 + dispatchCmd(DivCommand(DIV_CMD_FM_TL,ch,2,effectVal&0x3f)); + break; + case 0x15: // TL op4 + dispatchCmd(DivCommand(DIV_CMD_FM_TL,ch,3,effectVal&0x3f)); + break; + case 0x16: // MULT + if ((effectVal>>4)>0 && (effectVal>>4)<5) { + dispatchCmd(DivCommand(DIV_CMD_FM_MULT,ch,(effectVal>>4)-1,effectVal&15)); + } + break; + case 0x17: // DVB + dispatchCmd(DivCommand(DIV_CMD_FM_LFO,ch,2+(effectVal&1))); + break; + case 0x19: // AR global + dispatchCmd(DivCommand(DIV_CMD_FM_AR,ch,-1,effectVal&15)); + break; + case 0x1a: // AR op1 + dispatchCmd(DivCommand(DIV_CMD_FM_AR,ch,0,effectVal&15)); + break; + case 0x1b: // AR op2 + dispatchCmd(DivCommand(DIV_CMD_FM_AR,ch,1,effectVal&15)); + break; + case 0x1c: // AR op3 + dispatchCmd(DivCommand(DIV_CMD_FM_AR,ch,2,effectVal&15)); + break; + case 0x1d: // AR op4 + dispatchCmd(DivCommand(DIV_CMD_FM_AR,ch,3,effectVal&15)); + break; + default: + return false; + } + return true; + }; + + auto c64PostEffectHandler=[this](int ch, unsigned char effect, unsigned char effectVal) -> bool { + switch (effect) { + case 0x10: // select waveform + dispatchCmd(DivCommand(DIV_CMD_WAVE,ch,effectVal)); + break; + case 0x11: // cutoff + dispatchCmd(DivCommand(DIV_CMD_C64_CUTOFF,ch,effectVal)); + break; + case 0x12: // duty + dispatchCmd(DivCommand(DIV_CMD_STD_NOISE_MODE,ch,effectVal)); + break; + case 0x13: // resonance + dispatchCmd(DivCommand(DIV_CMD_C64_RESONANCE,ch,effectVal)); + break; + case 0x14: // filter mode + dispatchCmd(DivCommand(DIV_CMD_C64_FILTER_MODE,ch,effectVal)); + break; + case 0x15: // reset time + dispatchCmd(DivCommand(DIV_CMD_C64_RESET_TIME,ch,effectVal)); + break; + case 0x1a: // reset mask + dispatchCmd(DivCommand(DIV_CMD_C64_RESET_MASK,ch,effectVal)); + break; + case 0x1b: // cutoff reset + dispatchCmd(DivCommand(DIV_CMD_C64_FILTER_RESET,ch,effectVal)); + break; + case 0x1c: // duty reset + dispatchCmd(DivCommand(DIV_CMD_C64_DUTY_RESET,ch,effectVal)); + break; + case 0x1e: // extended + dispatchCmd(DivCommand(DIV_CMD_C64_EXTENDED,ch,effectVal)); + break; + case 0x30: case 0x31: case 0x32: case 0x33: + case 0x34: case 0x35: case 0x36: case 0x37: + case 0x38: case 0x39: case 0x3a: case 0x3b: + case 0x3c: case 0x3d: case 0x3e: case 0x3f: // fine duty + dispatchCmd(DivCommand(DIV_CMD_C64_FINE_DUTY,ch,((effect&0x0f)<<8)|effectVal)); + break; + case 0x40: case 0x41: case 0x42: case 0x43: + case 0x44: case 0x45: case 0x46: case 0x47: // fine cutoff + dispatchCmd(DivCommand(DIV_CMD_C64_FINE_CUTOFF,ch,((effect&0x07)<<8)|effectVal)); + break; + default: + return false; + } + return true; + }; + + auto ayPostEffectHandler=[this](int ch, unsigned char effect, unsigned char effectVal) -> bool { + switch (effect) { + case 0x12: // duty on 8930 + dispatchCmd(DivCommand(DIV_CMD_STD_NOISE_MODE,ch,0x10+(effectVal&15))); + break; + case 0x20: // mode + dispatchCmd(DivCommand(DIV_CMD_STD_NOISE_MODE,ch,effectVal&15)); + break; + case 0x21: // noise freq + dispatchCmd(DivCommand(DIV_CMD_STD_NOISE_FREQ,ch,effectVal)); + break; + case 0x22: // envelope enable + dispatchCmd(DivCommand(DIV_CMD_AY_ENVELOPE_SET,ch,effectVal)); + break; + case 0x23: // envelope period low + dispatchCmd(DivCommand(DIV_CMD_AY_ENVELOPE_LOW,ch,effectVal)); + break; + case 0x24: // envelope period high + dispatchCmd(DivCommand(DIV_CMD_AY_ENVELOPE_HIGH,ch,effectVal)); + break; + case 0x25: // envelope slide up + dispatchCmd(DivCommand(DIV_CMD_AY_ENVELOPE_SLIDE,ch,-effectVal)); + break; + case 0x26: // envelope slide down + dispatchCmd(DivCommand(DIV_CMD_AY_ENVELOPE_SLIDE,ch,effectVal)); + break; + case 0x27: // noise and mask + dispatchCmd(DivCommand(DIV_CMD_AY_NOISE_MASK_AND,ch,effectVal)); + break; + case 0x28: // noise or mask + dispatchCmd(DivCommand(DIV_CMD_AY_NOISE_MASK_OR,ch,effectVal)); + break; + case 0x29: // auto-envelope + dispatchCmd(DivCommand(DIV_CMD_AY_AUTO_ENVELOPE,ch,effectVal)); + break; + case 0x2d: // TEST + dispatchCmd(DivCommand(DIV_CMD_AY_IO_WRITE,ch,255,effectVal)); + break; + case 0x2e: // I/O port A + dispatchCmd(DivCommand(DIV_CMD_AY_IO_WRITE,ch,0,effectVal)); + break; + case 0x2f: // I/O port B + dispatchCmd(DivCommand(DIV_CMD_AY_IO_WRITE,ch,1,effectVal)); + break; + default: + return false; + } + return true; + }; + + auto segaPCMPostEffectHandler=[this](int ch, unsigned char effect, unsigned char effectVal) -> bool { + switch (effect) { + case 0x20: // PCM frequency + dispatchCmd(DivCommand(DIV_CMD_SAMPLE_FREQ,ch,effectVal)); + break; + default: + return false; + } + return true; + }; + sysDefs[DIV_SYSTEM_YMU759]=new DivSysDef( "Yamaha YMU759", NULL, 0x01, 0x01, 17, true, false, 0, false, {"Channel 1", "Channel 2", "Channel 3", "Channel 4", "Channel 5", "Channel 6", "Channel 7", "Channel 8", "Channel 9", "Channel 10", "Channel 11", "Channel 12", "Channel 13", "Channel 14", "Channel 15", "Channel 16", "PCM" }, // name @@ -354,7 +654,18 @@ void DivEngine::registerSystems() { {"Square 1", "Square 2", "Square 3", "Noise"}, {"S1", "S2", "S3", "NO"}, {DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_NOISE}, - {DIV_INS_STD, DIV_INS_STD, DIV_INS_STD, DIV_INS_STD} + {DIV_INS_STD, DIV_INS_STD, DIV_INS_STD, DIV_INS_STD}, + {}, + [this](int ch, unsigned char effect, unsigned char effectVal) -> bool { + switch (effect) { + case 0x20: // SN noise mode + dispatchCmd(DivCommand(DIV_CMD_STD_NOISE_MODE,ch,effectVal)); + break; + default: + return false; + } + return true; + } ); sysDefs[DIV_SYSTEM_SMS_OPLL]=new DivSysDef( @@ -367,7 +678,27 @@ void DivEngine::registerSystems() { {"Pulse 1", "Pulse 2", "Wavetable", "Noise"}, {"S1", "S2", "WA", "NO"}, {DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_WAVE, DIV_CH_NOISE}, - {DIV_INS_GB, DIV_INS_GB, DIV_INS_GB, DIV_INS_GB} + {DIV_INS_GB, DIV_INS_GB, DIV_INS_GB, DIV_INS_GB}, + {}, + [this](int ch, unsigned char effect, unsigned char effectVal) -> bool { + switch (effect) { + case 0x10: // select waveform + dispatchCmd(DivCommand(DIV_CMD_WAVE,ch,effectVal)); + break; + case 0x11: case 0x12: // duty or noise mode + dispatchCmd(DivCommand(DIV_CMD_STD_NOISE_MODE,ch,effectVal)); + break; + case 0x13: // sweep params + dispatchCmd(DivCommand(DIV_CMD_GB_SWEEP_TIME,ch,effectVal)); + break; + case 0x14: // sweep direction + dispatchCmd(DivCommand(DIV_CMD_GB_SWEEP_DIR,ch,effectVal)); + break; + default: + return false; + } + return true; + } ); sysDefs[DIV_SYSTEM_PCE]=new DivSysDef( @@ -376,7 +707,29 @@ void DivEngine::registerSystems() { {"CH1", "CH2", "CH3", "CH4", "CH5", "CH6"}, {DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE}, {DIV_INS_PCE, DIV_INS_PCE, DIV_INS_PCE, DIV_INS_PCE, DIV_INS_PCE, DIV_INS_PCE}, - {DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA} + {DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA}, + [this](int ch, unsigned char effect, unsigned char effectVal) -> bool { + switch (effect) { + case 0x10: // select waveform + dispatchCmd(DivCommand(DIV_CMD_WAVE,ch,effectVal)); + break; + case 0x11: // noise mode + dispatchCmd(DivCommand(DIV_CMD_STD_NOISE_MODE,ch,effectVal)); + break; + case 0x12: // LFO mode + dispatchCmd(DivCommand(DIV_CMD_PCE_LFO_MODE,ch,effectVal)); + break; + case 0x13: // LFO speed + dispatchCmd(DivCommand(DIV_CMD_PCE_LFO_SPEED,ch,effectVal)); + break; + case 0x17: // PCM enable + dispatchCmd(DivCommand(DIV_CMD_SAMPLE_MODE,ch,(effectVal>0))); + break; + default: + return false; + } + return true; + } ); sysDefs[DIV_SYSTEM_NES]=new DivSysDef( @@ -384,7 +737,30 @@ void DivEngine::registerSystems() { {"Pulse 1", "Pulse 2", "Triangle", "Noise", "PCM"}, {"S1", "S2", "TR", "NO", "PCM"}, {DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_WAVE, DIV_CH_NOISE, DIV_CH_PCM}, - {DIV_INS_STD, DIV_INS_STD, DIV_INS_STD, DIV_INS_STD, DIV_INS_AMIGA} + {DIV_INS_STD, DIV_INS_STD, DIV_INS_STD, DIV_INS_STD, DIV_INS_AMIGA}, + {}, + [this](int ch, unsigned char effect, unsigned char effectVal) -> bool { + switch (effect) { + case 0x11: // DMC write + dispatchCmd(DivCommand(DIV_CMD_NES_DMC,ch,effectVal)); + break; + case 0x12: // duty or noise mode + dispatchCmd(DivCommand(DIV_CMD_STD_NOISE_MODE,ch,effectVal)); + break; + case 0x13: // sweep up + dispatchCmd(DivCommand(DIV_CMD_NES_SWEEP,ch,0,effectVal)); + break; + case 0x14: // sweep down + dispatchCmd(DivCommand(DIV_CMD_NES_SWEEP,ch,1,effectVal)); + break; + case 0x18: // DPCM mode + dispatchCmd(DivCommand(DIV_CMD_SAMPLE_MODE,ch,effectVal)); + break; + default: + return false; + } + return true; + } ); sysDefs[DIV_SYSTEM_NES_VRC7]=new DivSysDef( @@ -402,7 +778,10 @@ void DivEngine::registerSystems() { {"Channel 1", "Channel 2", "Channel 3"}, {"CH1", "CH2", "CH3"}, {DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE}, - {DIV_INS_C64, DIV_INS_C64, DIV_INS_C64} + {DIV_INS_C64, DIV_INS_C64, DIV_INS_C64}, + {}, + [](int,unsigned char,unsigned char) -> bool {return false;}, + c64PostEffectHandler ); sysDefs[DIV_SYSTEM_C64_8580]=new DivSysDef( @@ -410,7 +789,10 @@ void DivEngine::registerSystems() { {"Channel 1", "Channel 2", "Channel 3"}, {"CH1", "CH2", "CH3"}, {DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE}, - {DIV_INS_C64, DIV_INS_C64, DIV_INS_C64} + {DIV_INS_C64, DIV_INS_C64, DIV_INS_C64}, + {}, + [](int,unsigned char,unsigned char) -> bool {return false;}, + c64PostEffectHandler ); sysDefs[DIV_SYSTEM_ARCADE]=new DivSysDef( @@ -418,12 +800,26 @@ void DivEngine::registerSystems() { {}, {}, {}, {} ); + auto fmHardResetEffectHandler=[this](int ch, unsigned char effect, unsigned char effectVal) -> bool { + switch (effect) { + case 0x30: // toggle hard-reset + dispatchCmd(DivCommand(DIV_CMD_FM_HARD_RESET,ch,effectVal)); + break; + default: + return false; + } + return true; + }; + sysDefs[DIV_SYSTEM_YM2610]=new DivSysDef( "Neo Geo CD", NULL, 0x09, 0x09, 13, true, true, 0x151, false, {"FM 1", "FM 2", "FM 3", "FM 4", "PSG 1", "PSG 2", "PSG 3", "ADPCM-A 1", "ADPCM-A 2", "ADPCM-A 3", "ADPCM-A 4", "ADPCM-A 5", "ADPCM-A 6"}, {"F1", "F2", "F3", "F4", "S1", "S2", "S3", "P1", "P2", "P3", "P4", "P5", "P6"}, {DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM}, - {DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_AY, DIV_INS_AY, DIV_INS_AY, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA} + {DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_AY, DIV_INS_AY, DIV_INS_AY, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA}, + {}, + fmHardResetEffectHandler, + fmPostEffectHandler ); sysDefs[DIV_SYSTEM_YM2610_EXT]=new DivSysDef( @@ -431,7 +827,10 @@ void DivEngine::registerSystems() { {"FM 1", "FM 2 OP1", "FM 2 OP2", "FM 2 OP3", "FM 2 OP4", "FM 3", "FM 4", "PSG 1", "PSG 2", "PSG 3", "ADPCM-A 1", "ADPCM-A 2", "ADPCM-A 3", "ADPCM-A 4", "ADPCM-A 5", "ADPCM-A 6"}, {"F1", "O1", "O2", "O3", "O4", "F3", "F4", "S1", "S2", "S3", "P1", "P2", "P3", "P4", "P5", "P6"}, {DIV_CH_FM, DIV_CH_OP, DIV_CH_OP, DIV_CH_OP, DIV_CH_OP, DIV_CH_FM, DIV_CH_FM, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM}, - {DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_AY, DIV_INS_AY, DIV_INS_AY, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA} + {DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_AY, DIV_INS_AY, DIV_INS_AY, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA}, + {}, + fmHardResetEffectHandler, + fmPostEffectHandler ); sysDefs[DIV_SYSTEM_AY8910]=new DivSysDef( @@ -439,7 +838,10 @@ void DivEngine::registerSystems() { {"PSG 1", "PSG 2", "PSG 3"}, {"S1", "S2", "S3"}, {DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE}, - {DIV_INS_AY, DIV_INS_AY, DIV_INS_AY} + {DIV_INS_AY, DIV_INS_AY, DIV_INS_AY}, + {}, + [](int,unsigned char,unsigned char) -> bool {return false;}, + ayPostEffectHandler ); sysDefs[DIV_SYSTEM_AMIGA]=new DivSysDef( @@ -447,7 +849,25 @@ void DivEngine::registerSystems() { {"Channel 1", "Channel 2", "Channel 3", "Channel 4"}, {"CH1", "CH2", "CH3", "CH4"}, {DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM}, - {DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA} + {DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA}, + {}, + [](int,unsigned char,unsigned char) -> bool {return false;}, + [this](int ch, unsigned char effect, unsigned char effectVal) -> bool { + switch (effect) { + case 0x10: // toggle filter + dispatchCmd(DivCommand(DIV_CMD_AMIGA_FILTER,ch,effectVal)); + break; + case 0x11: // toggle AM + dispatchCmd(DivCommand(DIV_CMD_AMIGA_AM,ch,effectVal)); + break; + case 0x12: // toggle PM + dispatchCmd(DivCommand(DIV_CMD_AMIGA_PM,ch,effectVal)); + break; + default: + return false; + } + return true; + } ); sysDefs[DIV_SYSTEM_YM2151]=new DivSysDef( @@ -455,16 +875,35 @@ void DivEngine::registerSystems() { {"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "FM 7", "FM 8"}, {"F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8"}, {DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM}, - {DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM} + {DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM}, + {}, + fmHardResetEffectHandler, + fmPostEffectHandler ); + auto opn2EffectHandler=[this](int ch, unsigned char effect, unsigned char effectVal) -> bool { + switch (effect) { + case 0x17: // DAC enable + dispatchCmd(DivCommand(DIV_CMD_SAMPLE_MODE,ch,(effectVal>0))); + break; + case 0x30: // toggle hard-reset + dispatchCmd(DivCommand(DIV_CMD_FM_HARD_RESET,ch,effectVal)); + break; + default: + return false; + } + return true; + }; + sysDefs[DIV_SYSTEM_YM2612]=new DivSysDef( "Yamaha YM2612", NULL, 0x83, 0, 6, true, false, 0x150, false, {"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6"}, {"F1", "F2", "F3", "F4", "F5", "F6"}, {DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM}, {DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM}, - {DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_AMIGA} + {DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_AMIGA}, + opn2EffectHandler, + fmPostEffectHandler ); sysDefs[DIV_SYSTEM_TIA]=new DivSysDef( @@ -472,7 +911,19 @@ void DivEngine::registerSystems() { {"Channel 1", "Channel 2"}, {"CH1", "CH2"}, {DIV_CH_WAVE, DIV_CH_WAVE}, - {DIV_INS_TIA, DIV_INS_TIA} + {DIV_INS_TIA, DIV_INS_TIA}, + {}, + [](int,unsigned char,unsigned char) -> bool {return false;}, + [this](int ch, unsigned char effect, unsigned char effectVal) -> bool { + switch (effect) { + case 0x10: // select waveform + dispatchCmd(DivCommand(DIV_CMD_WAVE,ch,effectVal)); + break; + default: + return false; + } + return true; + } ); sysDefs[DIV_SYSTEM_SAA1099]=new DivSysDef( @@ -480,7 +931,25 @@ void DivEngine::registerSystems() { {"PSG 1", "PSG 2", "PSG 3", "PSG 4", "PSG 5", "PSG 6"}, {"S1", "S2", "S3", "S4", "S5", "S6"}, {DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE}, - {DIV_INS_SAA1099, DIV_INS_SAA1099, DIV_INS_SAA1099, DIV_INS_SAA1099, DIV_INS_SAA1099, DIV_INS_SAA1099} + {DIV_INS_SAA1099, DIV_INS_SAA1099, DIV_INS_SAA1099, DIV_INS_SAA1099, DIV_INS_SAA1099, DIV_INS_SAA1099}, + {}, + [](int,unsigned char,unsigned char) -> bool {return false;}, + [this](int ch, unsigned char effect, unsigned char effectVal) -> bool { + switch (effect) { + case 0x10: // select channel mode + dispatchCmd(DivCommand(DIV_CMD_STD_NOISE_MODE,ch,effectVal)); + break; + case 0x11: // set noise freq + dispatchCmd(DivCommand(DIV_CMD_STD_NOISE_FREQ,ch,effectVal)); + break; + case 0x12: // setup envelope + dispatchCmd(DivCommand(DIV_CMD_SAA_ENVELOPE,ch,effectVal)); + break; + default: + return false; + } + return true; + } ); sysDefs[DIV_SYSTEM_AY8930]=new DivSysDef( @@ -488,15 +957,31 @@ void DivEngine::registerSystems() { {"PSG 1", "PSG 2", "PSG 3"}, {"S1", "S2", "S3"}, {DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE}, - {DIV_INS_AY8930, DIV_INS_AY8930, DIV_INS_AY8930} + {DIV_INS_AY8930, DIV_INS_AY8930, DIV_INS_AY8930}, + {}, + [](int,unsigned char,unsigned char) -> bool {return false;}, + ayPostEffectHandler ); + auto waveOnlyEffectHandler=[this](int ch, unsigned char effect, unsigned char effectVal) -> bool { + switch (effect) { + case 0x10: // select waveform + dispatchCmd(DivCommand(DIV_CMD_WAVE,ch,effectVal)); + break; + default: + return false; + } + return true; + }; + sysDefs[DIV_SYSTEM_VIC20]=new DivSysDef( "Commodore VIC-20", NULL, 0x85, 0, 4, false, true, 0, false, {"Low", "Mid", "High", "Noise"}, {"LO", "MID", "HI", "NO"}, {DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_NOISE}, - {DIV_INS_VIC, DIV_INS_VIC, DIV_INS_VIC, DIV_INS_VIC} + {DIV_INS_VIC, DIV_INS_VIC, DIV_INS_VIC, DIV_INS_VIC}, + {}, + waveOnlyEffectHandler ); sysDefs[DIV_SYSTEM_PET]=new DivSysDef( @@ -504,7 +989,9 @@ void DivEngine::registerSystems() { {"Wave"}, {"PET"}, {DIV_CH_PULSE}, - {DIV_INS_PET} + {DIV_INS_PET}, + {}, + waveOnlyEffectHandler ); sysDefs[DIV_SYSTEM_SNES]=new DivSysDef( @@ -521,15 +1008,42 @@ void DivEngine::registerSystems() { {"V1", "V2", "VS"}, {DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_WAVE}, {DIV_INS_VRC6, DIV_INS_VRC6, DIV_INS_VRC6_SAW}, - {DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_NULL} + {DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_NULL}, + [this](int ch, unsigned char effect, unsigned char effectVal) -> bool { + switch (effect) { + case 0x12: // duty or noise mode + dispatchCmd(DivCommand(DIV_CMD_STD_NOISE_MODE,ch,effectVal)); + break; + case 0x17: // PCM enable + dispatchCmd(DivCommand(DIV_CMD_SAMPLE_MODE,ch,(effectVal>0))); + break; + default: + return false; + } + return true; + } ); + auto oplEffectHandler=[this](int ch, unsigned char effect, unsigned char effectVal) -> bool { + switch (effect) { + case 0x30: // toggle hard-reset + dispatchCmd(DivCommand(DIV_CMD_FM_HARD_RESET,ch,effectVal)); + break; + default: + return false; + } + return true; + }; + sysDefs[DIV_SYSTEM_OPLL]=new DivSysDef( "Yamaha OPLL", NULL, 0x89, 0, 9, true, false, 0x150, false, {"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "FM 7", "FM 8", "FM 9"}, {"F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9"}, {DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM}, - {DIV_INS_OPLL, DIV_INS_OPLL, DIV_INS_OPLL, DIV_INS_OPLL, DIV_INS_OPLL, DIV_INS_OPLL, DIV_INS_OPLL, DIV_INS_OPLL, DIV_INS_OPLL} + {DIV_INS_OPLL, DIV_INS_OPLL, DIV_INS_OPLL, DIV_INS_OPLL, DIV_INS_OPLL, DIV_INS_OPLL, DIV_INS_OPLL, DIV_INS_OPLL, DIV_INS_OPLL}, + {}, + oplEffectHandler, + fmOPLLPostEffectHandler ); sysDefs[DIV_SYSTEM_FDS]=new DivSysDef( @@ -537,7 +1051,33 @@ void DivEngine::registerSystems() { {"FDS"}, {"FDS"}, {DIV_CH_WAVE}, - {DIV_INS_FDS} + {DIV_INS_FDS}, + {}, + [this](int ch, unsigned char effect, unsigned char effectVal) -> bool { + switch (effect) { + case 0x10: // select waveform + dispatchCmd(DivCommand(DIV_CMD_WAVE,ch,effectVal)); + break; + case 0x11: // modulation depth + dispatchCmd(DivCommand(DIV_CMD_FDS_MOD_DEPTH,ch,effectVal)); + break; + case 0x12: // modulation enable/high + dispatchCmd(DivCommand(DIV_CMD_FDS_MOD_HIGH,ch,effectVal)); + break; + case 0x13: // modulation low + dispatchCmd(DivCommand(DIV_CMD_FDS_MOD_LOW,ch,effectVal)); + break; + case 0x14: // modulation pos + dispatchCmd(DivCommand(DIV_CMD_FDS_MOD_POS,ch,effectVal)); + break; + case 0x15: // modulation wave + dispatchCmd(DivCommand(DIV_CMD_FDS_MOD_WAVE,ch,effectVal)); + break; + default: + return false; + } + return true; + } ); sysDefs[DIV_SYSTEM_MMC5]=new DivSysDef( @@ -545,7 +1085,21 @@ void DivEngine::registerSystems() { {"Pulse 1", "Pulse 2", "PCM"}, {"S1", "S2", "PCM"}, {DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PCM}, - {DIV_INS_STD, DIV_INS_STD, DIV_INS_AMIGA} + {DIV_INS_STD, DIV_INS_STD, DIV_INS_AMIGA}, + {}, + [this](int ch, unsigned char effect, unsigned char effectVal) -> bool { + switch (effect) { + case 0x11: // DMC write + dispatchCmd(DivCommand(DIV_CMD_NES_DMC,ch,effectVal)); + break; + case 0x12: // duty or noise mode + dispatchCmd(DivCommand(DIV_CMD_STD_NOISE_MODE,ch,effectVal)); + break; + default: + return false; + } + return true; + } ); sysDefs[DIV_SYSTEM_N163]=new DivSysDef( @@ -553,7 +1107,54 @@ void DivEngine::registerSystems() { {"Channel 1", "Channel 2", "Channel 3", "Channel 4", "Channel 5", "Channel 6", "Channel 7", "Channel 8"}, {"CH1", "CH2", "CH3", "CH4", "CH5", "CH6", "CH7", "CH8"}, {DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE}, - {DIV_INS_N163, DIV_INS_N163, DIV_INS_N163, DIV_INS_N163, DIV_INS_N163, DIV_INS_N163, DIV_INS_N163, DIV_INS_N163} + {DIV_INS_N163, DIV_INS_N163, DIV_INS_N163, DIV_INS_N163, DIV_INS_N163, DIV_INS_N163, DIV_INS_N163, DIV_INS_N163}, + {}, + [this](int ch, unsigned char effect, unsigned char effectVal) -> bool { + switch (effect) { + case 0x10: // select instrument waveform + dispatchCmd(DivCommand(DIV_CMD_WAVE,ch,effectVal)); + break; + case 0x11: // select instrument waveform position in RAM + dispatchCmd(DivCommand(DIV_CMD_N163_WAVE_POSITION,ch,effectVal)); + break; + case 0x12: // select instrument waveform length in RAM + dispatchCmd(DivCommand(DIV_CMD_N163_WAVE_LENGTH,ch,effectVal)); + break; + case 0x13: // change instrument waveform update mode + dispatchCmd(DivCommand(DIV_CMD_N163_WAVE_MODE,ch,effectVal)); + break; + case 0x14: // select waveform for load to RAM + dispatchCmd(DivCommand(DIV_CMD_N163_WAVE_LOAD,ch,effectVal)); + break; + case 0x15: // select waveform position for load to RAM + dispatchCmd(DivCommand(DIV_CMD_N163_WAVE_LOADPOS,ch,effectVal)); + break; + case 0x16: // select waveform length for load to RAM + dispatchCmd(DivCommand(DIV_CMD_N163_WAVE_LOADLEN,ch,effectVal)); + break; + case 0x17: // change waveform load mode + dispatchCmd(DivCommand(DIV_CMD_N163_WAVE_LOADMODE,ch,effectVal)); + break; + case 0x18: // change channel limits + dispatchCmd(DivCommand(DIV_CMD_N163_CHANNEL_LIMIT,ch,effectVal)); + break; + case 0x20: // (global) select waveform for load to RAM + dispatchCmd(DivCommand(DIV_CMD_N163_GLOBAL_WAVE_LOAD,ch,effectVal)); + break; + case 0x21: // (global) select waveform position for load to RAM + dispatchCmd(DivCommand(DIV_CMD_N163_GLOBAL_WAVE_LOADPOS,ch,effectVal)); + break; + case 0x22: // (global) select waveform length for load to RAM + dispatchCmd(DivCommand(DIV_CMD_N163_GLOBAL_WAVE_LOADLEN,ch,effectVal)); + break; + case 0x23: // (global) change waveform load mode + dispatchCmd(DivCommand(DIV_CMD_N163_GLOBAL_WAVE_LOADMODE,ch,effectVal)); + break; + default: + return false; + } + return true; + } ); sysDefs[DIV_SYSTEM_OPN]=new DivSysDef( @@ -577,7 +1178,10 @@ void DivEngine::registerSystems() { {"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "FM 7", "FM 8", "FM 9"}, {"F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9"}, {DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM}, - {DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL} + {DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL}, + {}, + oplEffectHandler, + fmOPLPostEffectHandler ); sysDefs[DIV_SYSTEM_OPL2]=new DivSysDef( @@ -585,7 +1189,10 @@ void DivEngine::registerSystems() { {"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "FM 7", "FM 8", "FM 9"}, {"F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9"}, {DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM}, - {DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL} + {DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL}, + {}, + oplEffectHandler, + fmOPLPostEffectHandler ); sysDefs[DIV_SYSTEM_OPL3]=new DivSysDef( @@ -593,7 +1200,10 @@ void DivEngine::registerSystems() { {"4OP 1", "FM 2", "4OP 3", "FM 4", "4OP 5", "FM 6", "4OP 7", "FM 8", "4OP 9", "FM 10", "4OP 11", "FM 12", "FM 13", "FM 14", "FM 15", "FM 16", "FM 17", "FM 18"}, {"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18"}, {DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM}, - {DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL} + {DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL}, + {}, + oplEffectHandler, + fmOPLPostEffectHandler ); sysDefs[DIV_SYSTEM_MULTIPCM]=new DivSysDef( @@ -634,7 +1244,29 @@ void DivEngine::registerSystems() { {"CH1", "CH2", "CH3", "CH4"}, {DIV_CH_WAVE, DIV_CH_PCM, DIV_CH_WAVE, DIV_CH_NOISE}, {DIV_INS_SWAN, DIV_INS_SWAN, DIV_INS_SWAN, DIV_INS_SWAN}, - {DIV_INS_NULL, DIV_INS_AMIGA, DIV_INS_NULL, DIV_INS_NULL} + {DIV_INS_NULL, DIV_INS_AMIGA, DIV_INS_NULL, DIV_INS_NULL}, + [this](int ch, unsigned char effect, unsigned char effectVal) -> bool { + switch (effect) { + case 0x10: // select waveform + dispatchCmd(DivCommand(DIV_CMD_WAVE,ch,effectVal)); + break; + case 0x11: // noise mode + dispatchCmd(DivCommand(DIV_CMD_STD_NOISE_MODE,ch,effectVal)); + break; + case 0x12: // sweep period + dispatchCmd(DivCommand(DIV_CMD_WS_SWEEP_TIME,ch,effectVal)); + break; + case 0x13: // sweep amount + dispatchCmd(DivCommand(DIV_CMD_WS_SWEEP_AMOUNT,ch,effectVal)); + break; + case 0x17: // PCM enable + dispatchCmd(DivCommand(DIV_CMD_SAMPLE_MODE,ch,(effectVal>0))); + break; + default: + return false; + } + return true; + } ); sysDefs[DIV_SYSTEM_OPZ]=new DivSysDef( @@ -642,7 +1274,19 @@ void DivEngine::registerSystems() { {"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "FM 7", "FM 8"}, {"F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8"}, {DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM}, - {DIV_INS_OPZ, DIV_INS_OPZ, DIV_INS_OPZ, DIV_INS_OPZ, DIV_INS_OPZ, DIV_INS_OPZ, DIV_INS_OPZ, DIV_INS_OPZ} + {DIV_INS_OPZ, DIV_INS_OPZ, DIV_INS_OPZ, DIV_INS_OPZ, DIV_INS_OPZ, DIV_INS_OPZ, DIV_INS_OPZ, DIV_INS_OPZ}, + {}, + [this](int ch, unsigned char effect, unsigned char effectVal) -> bool { + switch (effect) { + case 0x2f: // toggle hard-reset + dispatchCmd(DivCommand(DIV_CMD_FM_HARD_RESET,ch,effectVal)); + break; + default: + return false; + } + return true; + }, + fmPostEffectHandler ); sysDefs[DIV_SYSTEM_POKEMINI]=new DivSysDef( @@ -658,7 +1302,10 @@ void DivEngine::registerSystems() { {"Channel 1", "Channel 2", "Channel 3", "Channel 4", "Channel 5", "Channel 6", "Channel 7", "Channel 8", "Channel 9", "Channel 10", "Channel 11", "Channel 12", "Channel 13", "Channel 14", "Channel 15", "Channel 16"}, {"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16"}, {DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM}, - {DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA} + {DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA}, + {}, + [](int,unsigned char,unsigned char) -> bool {return false;}, + segaPCMPostEffectHandler ); sysDefs[DIV_SYSTEM_VBOY]=new DivSysDef( @@ -674,7 +1321,10 @@ void DivEngine::registerSystems() { {"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6"}, {"F1", "F2", "F3", "F4", "F5", "F6"}, {DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM}, - {DIV_INS_OPLL, DIV_INS_OPLL, DIV_INS_OPLL, DIV_INS_OPLL, DIV_INS_OPLL, DIV_INS_OPLL} + {DIV_INS_OPLL, DIV_INS_OPLL, DIV_INS_OPLL, DIV_INS_OPLL, DIV_INS_OPLL, DIV_INS_OPLL}, + {}, + oplEffectHandler, + fmOPLLPostEffectHandler ); sysDefs[DIV_SYSTEM_YM2610B]=new DivSysDef( @@ -682,7 +1332,10 @@ void DivEngine::registerSystems() { {"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "PSG 1", "PSG 2", "PSG 3", "ADPCM-A 1", "ADPCM-A 2", "ADPCM-A 3", "ADPCM-A 4", "ADPCM-A 5", "ADPCM-A 6", "ADPCM-B"}, {"F1", "F2", "F3", "F4", "F5", "F6", "S1", "S2", "S3", "P1", "P2", "P3", "P4", "P5", "P6", "B"}, {DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM}, - {DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_AY, DIV_INS_AY, DIV_INS_AY, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA} + {DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_AY, DIV_INS_AY, DIV_INS_AY, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA}, + {}, + fmHardResetEffectHandler, + fmPostEffectHandler ); sysDefs[DIV_SYSTEM_SFX_BEEPER]=new DivSysDef( @@ -699,7 +1352,9 @@ void DivEngine::registerSystems() { {"F1", "F2", "O1", "O2", "O3", "O4", "F4", "F5", "F6"}, {DIV_CH_FM, DIV_CH_FM, DIV_CH_OP, DIV_CH_OP, DIV_CH_OP, DIV_CH_OP, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM}, {DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM}, - {DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_AMIGA} + {DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_AMIGA}, + opn2EffectHandler, + fmPostEffectHandler ); sysDefs[DIV_SYSTEM_SCC]=new DivSysDef( @@ -710,12 +1365,29 @@ void DivEngine::registerSystems() { {DIV_INS_SCC, DIV_INS_SCC, DIV_INS_SCC, DIV_INS_SCC, DIV_INS_SCC} ); + auto oplDrumsEffectHandler=[this](int ch, unsigned char effect, unsigned char effectVal) -> bool { + switch (effect) { + case 0x18: // drum mode toggle + dispatchCmd(DivCommand(DIV_CMD_FM_EXTCH,ch,effectVal)); + break; + case 0x30: // toggle hard-reset + dispatchCmd(DivCommand(DIV_CMD_FM_HARD_RESET,ch,effectVal)); + break; + default: + return false; + } + return true; + }; + sysDefs[DIV_SYSTEM_OPL_DRUMS]=new DivSysDef( "Yamaha OPL with drums", NULL, 0xa2, 0, 11, true, false, 0x151, false, {"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "Kick", "Snare", "Tom", "Top", "HiHat"}, {"F1", "F2", "F3", "F4", "F5", "F6", "BD", "SD", "TM", "TP", "HH"}, {DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE}, - {DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL} + {DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL}, + {}, + oplDrumsEffectHandler, + fmOPLPostEffectHandler ); sysDefs[DIV_SYSTEM_OPL2_DRUMS]=new DivSysDef( @@ -723,7 +1395,10 @@ void DivEngine::registerSystems() { {"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "Kick", "Snare", "Tom", "Top", "HiHat"}, {"F1", "F2", "F3", "F4", "F5", "F6", "BD", "SD", "TM", "TP", "HH"}, {DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE}, - {DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL} + {DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL}, + {}, + oplDrumsEffectHandler, + fmOPLPostEffectHandler ); sysDefs[DIV_SYSTEM_OPL3_DRUMS]=new DivSysDef( @@ -731,7 +1406,10 @@ void DivEngine::registerSystems() { {"4OP 1", "FM 2", "4OP 3", "FM 4", "4OP 5", "FM 6", "4OP 7", "FM 8", "4OP 9", "FM 10", "4OP 11", "FM 12", "FM 13", "FM 14", "FM 15", "Kick", "Snare", "Tom", "Top", "HiHat"}, {"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "BD", "SD", "TM", "TP", "HH"}, {DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE}, - {DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL} + {DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL}, + {}, + oplDrumsEffectHandler, + fmOPLPostEffectHandler ); sysDefs[DIV_SYSTEM_YM2610_FULL]=new DivSysDef( @@ -739,7 +1417,10 @@ void DivEngine::registerSystems() { {"FM 1", "FM 2", "FM 3", "FM 4", "PSG 1", "PSG 2", "PSG 3", "ADPCM-A 1", "ADPCM-A 2", "ADPCM-A 3", "ADPCM-A 4", "ADPCM-A 5", "ADPCM-A 6", "ADPCM-B"}, {"F1", "F2", "F3", "F4", "S1", "S2", "S3", "P1", "P2", "P3", "P4", "P5", "P6", "B"}, {DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM}, - {DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_AY, DIV_INS_AY, DIV_INS_AY, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA} + {DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_AY, DIV_INS_AY, DIV_INS_AY, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA}, + {}, + fmHardResetEffectHandler, + fmPostEffectHandler ); sysDefs[DIV_SYSTEM_YM2610_FULL_EXT]=new DivSysDef( @@ -747,7 +1428,10 @@ void DivEngine::registerSystems() { {"FM 1", "FM 2 OP1", "FM 2 OP2", "FM 2 OP3", "FM 2 OP4", "FM 3", "FM 4", "PSG 1", "PSG 2", "PSG 3", "ADPCM-A 1", "ADPCM-A 2", "ADPCM-A 3", "ADPCM-A 4", "ADPCM-A 5", "ADPCM-A 6", "ADPCM-B"}, {"F1", "O1", "O2", "O3", "O4", "F3", "F4", "S1", "S2", "S3", "P1", "P2", "P3", "P4", "P5", "P6", "B"}, {DIV_CH_FM, DIV_CH_OP, DIV_CH_OP, DIV_CH_OP, DIV_CH_OP, DIV_CH_FM, DIV_CH_FM, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM}, - {DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_AY, DIV_INS_AY, DIV_INS_AY, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA} + {DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_AY, DIV_INS_AY, DIV_INS_AY, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA}, + {}, + fmHardResetEffectHandler, + fmPostEffectHandler ); sysDefs[DIV_SYSTEM_OPLL_DRUMS]=new DivSysDef( @@ -755,7 +1439,10 @@ void DivEngine::registerSystems() { {"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "Kick", "Snare", "Tom", "Top", "HiHat"}, {"F1", "F2", "F3", "F4", "F5", "F6", "BD", "SD", "TM", "TP", "HH"}, {DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE}, - {DIV_INS_OPLL, DIV_INS_OPLL, DIV_INS_OPLL, DIV_INS_OPLL, DIV_INS_OPLL, DIV_INS_OPLL, DIV_INS_OPLL, DIV_INS_OPLL, DIV_INS_OPLL, DIV_INS_OPLL, DIV_INS_OPLL} + {DIV_INS_OPLL, DIV_INS_OPLL, DIV_INS_OPLL, DIV_INS_OPLL, DIV_INS_OPLL, DIV_INS_OPLL, DIV_INS_OPLL, DIV_INS_OPLL, DIV_INS_OPLL, DIV_INS_OPLL, DIV_INS_OPLL}, + {}, + oplDrumsEffectHandler, + fmOPLLPostEffectHandler ); sysDefs[DIV_SYSTEM_LYNX]=new DivSysDef( @@ -763,7 +1450,17 @@ void DivEngine::registerSystems() { {"Channel 1", "Channel 2", "Channel 3", "Channel 4"}, {"CH1", "CH2", "CH3", "CH4"}, {DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE}, - {DIV_INS_MIKEY, DIV_INS_MIKEY, DIV_INS_MIKEY, DIV_INS_MIKEY} + {DIV_INS_MIKEY, DIV_INS_MIKEY, DIV_INS_MIKEY, DIV_INS_MIKEY}, + {}, + [](int,unsigned char,unsigned char) -> bool {return false;}, + [this](int ch, unsigned char effect, unsigned char effectVal) -> bool { + if (effect>=0x30 && effect<0x40) { + int value=((int)(effect&0x0f)<<8)|effectVal; + dispatchCmd(DivCommand(DIV_CMD_LYNX_LFSR_LOAD,ch,value)); + return true; + } + return false; + } ); sysDefs[DIV_SYSTEM_QSOUND]=new DivSysDef( @@ -771,7 +1468,29 @@ void DivEngine::registerSystems() { {"PCM 1", "PCM 2", "PCM 3", "PCM 4", "PCM 5", "PCM 6", "PCM 7", "PCM 8", "PCM 9", "PCM 10", "PCM 11", "PCM 12", "PCM 13", "PCM 14", "PCM 15", "PCM 16", "ADPCM 1", "ADPCM 2", "ADPCM 3"}, {"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "A1", "A2", "A3"}, {DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE}, - {DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA} + {DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA}, + {}, + [this](int ch, unsigned char effect, unsigned char effectVal) -> bool { + switch (effect) { + case 0x10: // echo feedback + dispatchCmd(DivCommand(DIV_CMD_QSOUND_ECHO_FEEDBACK,ch,effectVal)); + break; + case 0x11: // echo level + dispatchCmd(DivCommand(DIV_CMD_QSOUND_ECHO_LEVEL,ch,effectVal)); + break; + case 0x12: // surround + dispatchCmd(DivCommand(DIV_CMD_QSOUND_SURROUND,ch,effectVal)); + break; + default: + if ((effect&0xf0)==0x30) { + dispatchCmd(DivCommand(DIV_CMD_QSOUND_ECHO_DELAY,ch,((effect & 0x0f) << 8) | effectVal)); + } else { + return false; + } + break; + } + return true; + } ); sysDefs[DIV_SYSTEM_VERA]=new DivSysDef( @@ -779,7 +1498,21 @@ void DivEngine::registerSystems() { {"Channel 1", "Channel 2", "Channel 3", "Channel 4", "Channel 5", "Channel 6", "Channel 7", "Channel 8", "Channel 9", "Channel 10", "Channel 11", "Channel 12", "Channel 13", "Channel 14", "Channel 15", "Channel 16", "PCM"}, {"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "PCM"}, {DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PCM}, - {DIV_INS_VERA, DIV_INS_VERA, DIV_INS_VERA, DIV_INS_VERA, DIV_INS_VERA, DIV_INS_VERA, DIV_INS_VERA, DIV_INS_VERA, DIV_INS_VERA, DIV_INS_VERA, DIV_INS_VERA, DIV_INS_VERA, DIV_INS_VERA, DIV_INS_VERA, DIV_INS_VERA, DIV_INS_VERA, DIV_INS_AMIGA} + {DIV_INS_VERA, DIV_INS_VERA, DIV_INS_VERA, DIV_INS_VERA, DIV_INS_VERA, DIV_INS_VERA, DIV_INS_VERA, DIV_INS_VERA, DIV_INS_VERA, DIV_INS_VERA, DIV_INS_VERA, DIV_INS_VERA, DIV_INS_VERA, DIV_INS_VERA, DIV_INS_VERA, DIV_INS_VERA, DIV_INS_AMIGA}, + {}, + [this](int ch, unsigned char effect, unsigned char effectVal) -> bool { + switch (effect) { + case 0x20: // select waveform + dispatchCmd(DivCommand(DIV_CMD_WAVE,ch,effectVal)); + break; + case 0x22: // duty + dispatchCmd(DivCommand(DIV_CMD_STD_NOISE_MODE,ch,effectVal)); + break; + default: + return false; + } + return true; + } ); sysDefs[DIV_SYSTEM_YM2610B_EXT]=new DivSysDef( @@ -787,7 +1520,10 @@ void DivEngine::registerSystems() { {"FM 1", "FM 2", "FM 3 OP1", "FM 3 OP2", "FM 3 OP3", "FM 3 OP4", "FM 4", "FM 5", "FM 6", "PSG 1", "PSG 2", "PSG 3", "ADPCM-A 1", "ADPCM-A 2", "ADPCM-A 3", "ADPCM-A 4", "ADPCM-A 5", "ADPCM-A 6", "ADPCM-B"}, {"F1", "F2", "O1", "O2", "O3", "O4", "F4", "F5", "F6", "S1", "S2", "S3", "P1", "P2", "P3", "P4", "P5", "P6", "B"}, {DIV_CH_FM, DIV_CH_FM, DIV_CH_OP, DIV_CH_OP, DIV_CH_OP, DIV_CH_OP, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM}, - {DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_AY, DIV_INS_AY, DIV_INS_AY, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA} + {DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_AY, DIV_INS_AY, DIV_INS_AY, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA}, + {}, + fmHardResetEffectHandler, + fmPostEffectHandler ); sysDefs[DIV_SYSTEM_SEGAPCM_COMPAT]=new DivSysDef( @@ -795,7 +1531,10 @@ void DivEngine::registerSystems() { {"Channel 1", "Channel 2", "Channel 3", "Channel 4", "Channel 5"}, {"P1", "P2", "P3", "P4", "P5"}, {DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM}, - {DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA} + {DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA}, + {}, + [](int,unsigned char,unsigned char) -> bool {return false;}, + segaPCMPostEffectHandler ); sysDefs[DIV_SYSTEM_X1_010]=new DivSysDef( @@ -804,7 +1543,48 @@ void DivEngine::registerSystems() { {"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16"}, {DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE}, {DIV_INS_X1_010, DIV_INS_X1_010, DIV_INS_X1_010, DIV_INS_X1_010, DIV_INS_X1_010, DIV_INS_X1_010, DIV_INS_X1_010, DIV_INS_X1_010, DIV_INS_X1_010, DIV_INS_X1_010, DIV_INS_X1_010, DIV_INS_X1_010, DIV_INS_X1_010, DIV_INS_X1_010, DIV_INS_X1_010, DIV_INS_X1_010}, - {DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA} + {DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA}, + [this](int ch, unsigned char effect, unsigned char effectVal) -> bool { + switch (effect) { + case 0x10: // select waveform + dispatchCmd(DivCommand(DIV_CMD_WAVE,ch,effectVal)); + break; + case 0x11: // select envelope shape + dispatchCmd(DivCommand(DIV_CMD_X1_010_ENVELOPE_SHAPE,ch,effectVal)); + break; + case 0x17: // PCM enable + dispatchCmd(DivCommand(DIV_CMD_SAMPLE_MODE,ch,(effectVal>0))); + break; + default: + return false; + } + return true; + }, + [this](int ch, unsigned char effect, unsigned char effectVal) -> bool { + switch (effect) { + case 0x20: // PCM frequency + dispatchCmd(DivCommand(DIV_CMD_SAMPLE_FREQ,ch,effectVal)); + break; + case 0x22: // envelope mode + dispatchCmd(DivCommand(DIV_CMD_X1_010_ENVELOPE_MODE,ch,effectVal)); + break; + case 0x23: // envelope period + dispatchCmd(DivCommand(DIV_CMD_X1_010_ENVELOPE_PERIOD,ch,effectVal)); + break; + case 0x25: // envelope slide up + dispatchCmd(DivCommand(DIV_CMD_X1_010_ENVELOPE_SLIDE,ch,effectVal)); + break; + case 0x26: // envelope slide down + dispatchCmd(DivCommand(DIV_CMD_X1_010_ENVELOPE_SLIDE,ch,-effectVal)); + break; + case 0x29: // auto-envelope + dispatchCmd(DivCommand(DIV_CMD_X1_010_AUTO_ENVELOPE,ch,effectVal)); + break; + default: + return false; + } + return true; + } ); sysDefs[DIV_SYSTEM_BUBSYS_WSG]=new DivSysDef( @@ -812,7 +1592,9 @@ void DivEngine::registerSystems() { {"Channel 1", "Channel 2"}, {"CH1", "CH2"}, {DIV_CH_WAVE, DIV_CH_WAVE}, - {DIV_INS_SCC, DIV_INS_SCC} + {DIV_INS_SCC, DIV_INS_SCC}, + {}, + waveOnlyEffectHandler ); // to Grauw: feel free to change this to 24 during development of OPL4's PCM part. From 31a35e5395fdeac563820c55f5617a2055624d68 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Tue, 3 May 2022 01:29:31 -0500 Subject: [PATCH 305/342] better FM chip names --- TODO.md | 3 --- src/engine/sysDef.cpp | 24 ++++++++++++------------ 2 files changed, 12 insertions(+), 15 deletions(-) diff --git a/TODO.md b/TODO.md index 54e966303..26efc81db 100644 --- a/TODO.md +++ b/TODO.md @@ -20,8 +20,6 @@ - unified data view - volume commands should work on Game Boy - macro editor menu -- refactor sysDef.cpp - - effect/postEffect refactor - add another FM editor layout - try to find out why does VSlider not accept keyboard input - finish lock layout @@ -32,5 +30,4 @@ - add ability to move selection by dragging - settings: OK/Cancel buttons should be always visible - Apply button in settings -- better FM chip names (number and codename) - find and replace \ No newline at end of file diff --git a/src/engine/sysDef.cpp b/src/engine/sysDef.cpp index aed1d9f7c..11e34187e 100644 --- a/src/engine/sysDef.cpp +++ b/src/engine/sysDef.cpp @@ -632,7 +632,7 @@ void DivEngine::registerSystems() { }; sysDefs[DIV_SYSTEM_YMU759]=new DivSysDef( - "Yamaha YMU759", NULL, 0x01, 0x01, 17, true, false, 0, false, + "Yamaha YMU759 (MA-2)", NULL, 0x01, 0x01, 17, true, false, 0, false, {"Channel 1", "Channel 2", "Channel 3", "Channel 4", "Channel 5", "Channel 6", "Channel 7", "Channel 8", "Channel 9", "Channel 10", "Channel 11", "Channel 12", "Channel 13", "Channel 14", "Channel 15", "Channel 16", "PCM" }, // name {"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "PCM" }, // short {DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_PCM }, // type @@ -871,7 +871,7 @@ void DivEngine::registerSystems() { ); sysDefs[DIV_SYSTEM_YM2151]=new DivSysDef( - "Yamaha YM2151", NULL, 0x82, 0, 8, true, false, 0x150, false, + "Yamaha YM2151 (OPM)", NULL, 0x82, 0, 8, true, false, 0x150, false, {"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "FM 7", "FM 8"}, {"F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8"}, {DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM}, @@ -896,7 +896,7 @@ void DivEngine::registerSystems() { }; sysDefs[DIV_SYSTEM_YM2612]=new DivSysDef( - "Yamaha YM2612", NULL, 0x83, 0, 6, true, false, 0x150, false, + "Yamaha YM2612 (OPN2)", NULL, 0x83, 0, 6, true, false, 0x150, false, {"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6"}, {"F1", "F2", "F3", "F4", "F5", "F6"}, {DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM}, @@ -1036,7 +1036,7 @@ void DivEngine::registerSystems() { }; sysDefs[DIV_SYSTEM_OPLL]=new DivSysDef( - "Yamaha OPLL", NULL, 0x89, 0, 9, true, false, 0x150, false, + "Yamaha YM2413 (OPLL)", NULL, 0x89, 0, 9, true, false, 0x150, false, {"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "FM 7", "FM 8", "FM 9"}, {"F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9"}, {DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM}, @@ -1158,7 +1158,7 @@ void DivEngine::registerSystems() { ); sysDefs[DIV_SYSTEM_OPN]=new DivSysDef( - "Yamaha YM2203", NULL, 0x8d, 0, 6, true, false, 0, false, + "Yamaha YM2203 (OPN)", NULL, 0x8d, 0, 6, true, false, 0, false, {"FM 1", "FM 2", "FM 3", "PSG 1", "PSG 2", "PSG 3"}, {"F1", "F2", "F3", "S1", "S2", "S3"}, {DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE}, @@ -1166,7 +1166,7 @@ void DivEngine::registerSystems() { ); sysDefs[DIV_SYSTEM_PC98]=new DivSysDef( - "Yamaha YM2608", NULL, 0x8e, 0, 16, true, false, 0, false, + "Yamaha YM2608 (OPNA)", NULL, 0x8e, 0, 16, true, false, 0, false, {"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "Square 1", "Square 2", "Square 3", "Kick", "Snare", "Top", "HiHat", "Tom", "Rim", "ADPCM"}, {"F1", "F2", "F3", "F4", "F5", "F6", "S1", "S2", "S3", "BD", "SD", "TP", "HH", "TM", "RM", "P"}, {DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_PCM}, @@ -1174,7 +1174,7 @@ void DivEngine::registerSystems() { ); sysDefs[DIV_SYSTEM_OPL]=new DivSysDef( - "Yamaha OPL", NULL, 0x8f, 0, 9, true, false, 0x151, false, + "Yamaha YM3526 (OPL)", NULL, 0x8f, 0, 9, true, false, 0x151, false, {"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "FM 7", "FM 8", "FM 9"}, {"F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9"}, {DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM}, @@ -1185,7 +1185,7 @@ void DivEngine::registerSystems() { ); sysDefs[DIV_SYSTEM_OPL2]=new DivSysDef( - "Yamaha OPL2", NULL, 0x90, 0, 9, true, false, 0x151, false, + "Yamaha YM3812 (OPL2)", NULL, 0x90, 0, 9, true, false, 0x151, false, {"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "FM 7", "FM 8", "FM 9"}, {"F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9"}, {DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM}, @@ -1196,7 +1196,7 @@ void DivEngine::registerSystems() { ); sysDefs[DIV_SYSTEM_OPL3]=new DivSysDef( - "Yamaha OPL3", NULL, 0x91, 0, 18, true, false, 0x151, false, + "Yamaha YMF262 (OPL3)", NULL, 0x91, 0, 18, true, false, 0x151, false, {"4OP 1", "FM 2", "4OP 3", "FM 4", "4OP 5", "FM 6", "4OP 7", "FM 8", "4OP 9", "FM 10", "4OP 11", "FM 12", "FM 13", "FM 14", "FM 15", "FM 16", "FM 17", "FM 18"}, {"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18"}, {DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM}, @@ -1270,7 +1270,7 @@ void DivEngine::registerSystems() { ); sysDefs[DIV_SYSTEM_OPZ]=new DivSysDef( - "Yamaha TX81Z/YS200", NULL, 0x98, 0, 8, true, false, 0, false, + "Yamaha YM2414 (OPZ)", NULL, 0x98, 0, 8, true, false, 0, false, {"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "FM 7", "FM 8"}, {"F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8"}, {DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM}, @@ -1328,7 +1328,7 @@ void DivEngine::registerSystems() { ); sysDefs[DIV_SYSTEM_YM2610B]=new DivSysDef( - "Yamaha YM2610B", NULL, 0x9e, 0, 16, true, false, 0x151, false, + "Yamaha YM2610B (OPNB-B)", NULL, 0x9e, 0, 16, true, false, 0x151, false, {"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "PSG 1", "PSG 2", "PSG 3", "ADPCM-A 1", "ADPCM-A 2", "ADPCM-A 3", "ADPCM-A 4", "ADPCM-A 5", "ADPCM-A 6", "ADPCM-B"}, {"F1", "F2", "F3", "F4", "F5", "F6", "S1", "S2", "S3", "P1", "P2", "P3", "P4", "P5", "P6", "B"}, {DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM}, @@ -1413,7 +1413,7 @@ void DivEngine::registerSystems() { ); sysDefs[DIV_SYSTEM_YM2610_FULL]=new DivSysDef( - "Yamaha YM2610", NULL, 0xa5, 0, 14, true, false, 0x151, false, + "Yamaha YM2610 (OPNB)", NULL, 0xa5, 0, 14, true, false, 0x151, false, {"FM 1", "FM 2", "FM 3", "FM 4", "PSG 1", "PSG 2", "PSG 3", "ADPCM-A 1", "ADPCM-A 2", "ADPCM-A 3", "ADPCM-A 4", "ADPCM-A 5", "ADPCM-A 6", "ADPCM-B"}, {"F1", "F2", "F3", "F4", "S1", "S2", "S3", "P1", "P2", "P3", "P4", "P5", "P6", "B"}, {DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM}, From c23ee8e7c994bc0f12bb5e6453648a53d0abfddd Mon Sep 17 00:00:00 2001 From: tildearrow Date: Tue, 3 May 2022 01:48:57 -0500 Subject: [PATCH 306/342] GUI: put "OK" and "Cancel" buttons in the bottom --- src/gui/settings.cpp | 2819 +++++++++++++++++++++--------------------- 1 file changed, 1420 insertions(+), 1399 deletions(-) diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index b515d6535..e4539b794 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -225,1484 +225,1505 @@ void FurnaceGUI::drawSettings() { } if (ImGui::BeginTabBar("settingsTab")) { if (ImGui::BeginTabItem("General")) { - ImGui::Text("Workspace layout:"); - ImGui::SameLine(); - if (ImGui::Button("Import")) { - openFileDialog(GUI_FILE_IMPORT_LAYOUT); - } - ImGui::SameLine(); - if (ImGui::Button("Export")) { - openFileDialog(GUI_FILE_EXPORT_LAYOUT); - } - ImGui::SameLine(); - if (ImGui::Button("Reset")) { - showWarning("Are you sure you want to reset the workspace layout?",GUI_WARN_RESET_LAYOUT); - } - - ImGui::Separator(); - - ImGui::Text("Initial system/chips:"); - ImGui::SameLine(); - if (ImGui::Button("Current systems")) { - settings.initialSys.clear(); - for (int i=0; isong.systemLen; i++) { - settings.initialSys.push_back(e->song.system[i]); - settings.initialSys.push_back(e->song.systemVol[i]); - settings.initialSys.push_back(e->song.systemPan[i]); - settings.initialSys.push_back(e->song.systemFlags[i]); + ImVec2 settingsViewSize=ImGui::GetContentRegionAvail(); + settingsViewSize.y-=ImGui::GetFrameHeight()+ImGui::GetStyle().WindowPadding.y; + if (ImGui::BeginChild("SettingsView",settingsViewSize)) { + ImGui::Text("Workspace layout:"); + ImGui::SameLine(); + if (ImGui::Button("Import")) { + openFileDialog(GUI_FILE_IMPORT_LAYOUT); } - } - ImGui::SameLine(); - if (ImGui::Button("Randomize")) { - settings.initialSys.clear(); - int howMany=1+rand()%3; - int totalAvailSys=0; - for (totalAvailSys=0; availableSystems[totalAvailSys]; totalAvailSys++); - if (totalAvailSys>0) { - for (int i=0; isong.systemLen; i++) { + settings.initialSys.push_back(e->song.system[i]); + settings.initialSys.push_back(e->song.systemVol[i]); + settings.initialSys.push_back(e->song.systemPan[i]); + settings.initialSys.push_back(e->song.systemFlags[i]); + } + } + ImGui::SameLine(); + if (ImGui::Button("Randomize")) { + settings.initialSys.clear(); + int howMany=1+rand()%3; + int totalAvailSys=0; + for (totalAvailSys=0; availableSystems[totalAvailSys]; totalAvailSys++); + if (totalAvailSys>0) { + for (int i=0; i=8) { + settings.initialSys.erase(settings.initialSys.begin()+i,settings.initialSys.begin()+i+4); + i-=4; + } + } + + if (ImGui::Button(ICON_FA_PLUS "##InitSysAdd")) { + settings.initialSys.push_back(DIV_SYSTEM_YM2612); settings.initialSys.push_back(64); settings.initialSys.push_back(0); settings.initialSys.push_back(0); } - } - ImGui::SameLine(); - if (ImGui::Button("Reset to defaults")) { - settings.initialSys.clear(); - settings.initialSys.push_back(DIV_SYSTEM_YM2612); - settings.initialSys.push_back(64); - settings.initialSys.push_back(0); - settings.initialSys.push_back(0); - settings.initialSys.push_back(DIV_SYSTEM_SMS); - settings.initialSys.push_back(32); - settings.initialSys.push_back(0); - settings.initialSys.push_back(0); - } - for (size_t i=0; i":settings.audioDevice; + if (ImGui::BeginCombo("##AudioDevice",audioDevName.c_str())) { + if (ImGui::Selectable("",settings.audioDevice.empty())) { + settings.audioDevice=""; + } + for (String& i: e->getAudioDevices()) { + if (ImGui::Selectable(i.c_str(),i==settings.audioDevice)) { + settings.audioDevice=i; } } ImGui::EndCombo(); } + ImGui::Text("Sample rate"); ImGui::SameLine(); - if (ImGui::Checkbox("Invert",&doInvert)) { - settings.initialSys[i+1]^=128; + String sr=fmt::sprintf("%d",settings.audioRate); + if (ImGui::BeginCombo("##SampleRate",sr.c_str())) { + SAMPLE_RATE_SELECTABLE(8000); + SAMPLE_RATE_SELECTABLE(16000); + SAMPLE_RATE_SELECTABLE(22050); + SAMPLE_RATE_SELECTABLE(32000); + SAMPLE_RATE_SELECTABLE(44100); + SAMPLE_RATE_SELECTABLE(48000); + SAMPLE_RATE_SELECTABLE(88200); + SAMPLE_RATE_SELECTABLE(96000); + SAMPLE_RATE_SELECTABLE(192000); + ImGui::EndCombo(); } + + ImGui::Text("Buffer size"); ImGui::SameLine(); - ImGui::BeginDisabled(settings.initialSys.size()<=4); - if (ImGui::Button(ICON_FA_MINUS "##InitSysRemove")) { - doRemove=true; + String bs=fmt::sprintf("%d (latency: ~%.1fms)",settings.audioBufSize,2000.0*(double)settings.audioBufSize/(double)MAX(1,settings.audioRate)); + if (ImGui::BeginCombo("##BufferSize",bs.c_str())) { + BUFFER_SIZE_SELECTABLE(64); + BUFFER_SIZE_SELECTABLE(128); + BUFFER_SIZE_SELECTABLE(256); + BUFFER_SIZE_SELECTABLE(512); + BUFFER_SIZE_SELECTABLE(1024); + BUFFER_SIZE_SELECTABLE(2048); + ImGui::EndCombo(); } - ImGui::EndDisabled(); - ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x-(50.0f*dpiScale)); - if (CWSliderScalar("Volume",ImGuiDataType_S8,&vol,&_ZERO,&_ONE_HUNDRED_TWENTY_SEVEN)) { - settings.initialSys[i+1]=(settings.initialSys[i+1]&128)|vol; - } rightClickable - ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x-(50.0f*dpiScale)); - CWSliderScalar("Panning",ImGuiDataType_S8,&settings.initialSys[i+2],&_MINUS_ONE_HUNDRED_TWENTY_SEVEN,&_ONE_HUNDRED_TWENTY_SEVEN); rightClickable - // oh please MSVC don't cry - if (ImGui::TreeNode("Configure")) { - drawSysConf(-1,(DivSystem)settings.initialSys[i],(unsigned int&)settings.initialSys[i+3],false); - ImGui::TreePop(); + ImGui::Text("Quality"); + ImGui::SameLine(); + ImGui::Combo("##Quality",&settings.audioQuality,audioQualities,2); + + ImGui::Text("Metronome volume"); + ImGui::SameLine(); + if (ImGui::SliderInt("##MetroVol",&settings.metroVol,0,200,"%d%%")) { + if (settings.metroVol<0) settings.metroVol=0; + if (settings.metroVol>200) settings.metroVol=200; + e->setMetronomeVol(((float)settings.metroVol)/100.0f); } - ImGui::PopID(); - if (doRemove && settings.initialSys.size()>=8) { - settings.initialSys.erase(settings.initialSys.begin()+i,settings.initialSys.begin()+i+4); - i-=4; + bool lowLatencyB=settings.lowLatency; + if (ImGui::Checkbox("Low-latency mode (experimental!)",&lowLatencyB)) { + settings.lowLatency=lowLatencyB; } - } - - if (ImGui::Button(ICON_FA_PLUS "##InitSysAdd")) { - settings.initialSys.push_back(DIV_SYSTEM_YM2612); - settings.initialSys.push_back(64); - settings.initialSys.push_back(0); - settings.initialSys.push_back(0); - } - - ImGui::Separator(); - - ImGui::Text("Toggle channel solo on:"); - if (ImGui::RadioButton("Right-click or double-click##soloA",settings.soloAction==0)) { - settings.soloAction=0; - } - if (ImGui::RadioButton("Right-click##soloR",settings.soloAction==1)) { - settings.soloAction=1; - } - if (ImGui::RadioButton("Double-click##soloD",settings.soloAction==2)) { - settings.soloAction=2; - } - - bool pushNibbleB=settings.pushNibble; - if (ImGui::Checkbox("Push value when overwriting instead of clearing it",&pushNibbleB)) { - settings.pushNibble=pushNibbleB; - } - - bool pullDeleteBehaviorB=settings.pullDeleteBehavior; - if (ImGui::Checkbox("Move cursor up on backspace-delete",&pullDeleteBehaviorB)) { - settings.pullDeleteBehavior=pullDeleteBehaviorB; - } - - bool stepOnDeleteB=settings.stepOnDelete; - if (ImGui::Checkbox("Move cursor by edit step on delete",&stepOnDeleteB)) { - settings.stepOnDelete=stepOnDeleteB; - } - - bool absorbInsInputB=settings.absorbInsInput; - if (ImGui::Checkbox("Change current instrument when changing instrument column (absorb)",&absorbInsInputB)) { - settings.absorbInsInput=absorbInsInputB; - } - - bool effectDeletionAltersValueB=settings.effectDeletionAltersValue; - if (ImGui::Checkbox("Delete effect value when deleting effect",&effectDeletionAltersValueB)) { - settings.effectDeletionAltersValue=effectDeletionAltersValueB; - } - - bool scrollChangesOrderB=settings.scrollChangesOrder; - if (ImGui::Checkbox("Change order when scrolling outside of pattern bounds",&scrollChangesOrderB)) { - settings.scrollChangesOrder=scrollChangesOrderB; - } - - bool stepOnInsertB=settings.stepOnInsert; - if (ImGui::Checkbox("Move cursor by edit step on insert (push)",&stepOnInsertB)) { - settings.stepOnInsert=stepOnInsertB; - } - - bool cursorPastePosB=settings.cursorPastePos; - if (ImGui::Checkbox("Move cursor to end of clipboard content when pasting",&cursorPastePosB)) { - settings.cursorPastePos=cursorPastePosB; - } - - bool cursorMoveNoScrollB=settings.cursorMoveNoScroll; - if (ImGui::Checkbox("Don't scroll when moving cursor",&cursorMoveNoScrollB)) { - settings.cursorMoveNoScroll=cursorMoveNoScrollB; - } - - bool allowEditDockingB=settings.allowEditDocking; - if (ImGui::Checkbox("Allow docking editors",&allowEditDockingB)) { - settings.allowEditDocking=allowEditDockingB; - } - - bool avoidRaisingPatternB=settings.avoidRaisingPattern; - if (ImGui::Checkbox("Don't raise pattern editor on click",&avoidRaisingPatternB)) { - settings.avoidRaisingPattern=avoidRaisingPatternB; - } - - bool insFocusesPatternB=settings.insFocusesPattern; - if (ImGui::Checkbox("Focus pattern editor when selecting instrument",&insFocusesPatternB)) { - settings.insFocusesPattern=insFocusesPatternB; - } - - bool restartOnFlagChangeB=settings.restartOnFlagChange; - if (ImGui::Checkbox("Restart song when changing system properties",&restartOnFlagChangeB)) { - settings.restartOnFlagChange=restartOnFlagChangeB; - } - - bool sysFileDialogB=settings.sysFileDialog; - if (ImGui::Checkbox("Use system file picker",&sysFileDialogB)) { - settings.sysFileDialog=sysFileDialogB; - } - - bool moveWindowTitleB=settings.moveWindowTitle; - if (ImGui::Checkbox("Only allow window movement when clicking on title bar",&moveWindowTitleB)) { - settings.moveWindowTitle=moveWindowTitleB; - applyUISettings(false); - } - - bool eventDelayB=settings.eventDelay; - if (ImGui::Checkbox("Enable event delay",&eventDelayB)) { - settings.eventDelay=eventDelayB; - applyUISettings(false); - } - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("may cause issues with high-polling-rate mice when previewing notes."); - } - - bool powerSaveB=settings.powerSave; - if (ImGui::Checkbox("Power-saving mode",&powerSaveB)) { - settings.powerSave=powerSaveB; - } - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("saves power by lowering the frame rate to 2fps when idle.\nmay cause issues under Mesa drivers!"); - } - - ImGui::Text("Note preview behavior:"); - if (ImGui::RadioButton("Never##npb0",settings.notePreviewBehavior==0)) { - settings.notePreviewBehavior=0; - } - if (ImGui::RadioButton("When cursor is in Note column##npb1",settings.notePreviewBehavior==1)) { - settings.notePreviewBehavior=1; - } - if (ImGui::RadioButton("When cursor is in Note column or not in edit mode##npb2",settings.notePreviewBehavior==2)) { - settings.notePreviewBehavior=2; - } - if (ImGui::RadioButton("Always##npb3",settings.notePreviewBehavior==3)) { - settings.notePreviewBehavior=3; - } - - ImGui::Text("Wrap pattern cursor horizontally:"); - if (ImGui::RadioButton("No##wrapH0",settings.wrapHorizontal==0)) { - settings.wrapHorizontal=0; - } - if (ImGui::RadioButton("Yes##wrapH1",settings.wrapHorizontal==1)) { - settings.wrapHorizontal=1; - } - if (ImGui::RadioButton("Yes, and move to next/prev row##wrapH2",settings.wrapHorizontal==2)) { - settings.wrapHorizontal=2; - } - - ImGui::Text("Wrap pattern cursor vertically:"); - if (ImGui::RadioButton("No##wrapV0",settings.wrapVertical==0)) { - settings.wrapVertical=0; - } - if (ImGui::RadioButton("Yes##wrapV1",settings.wrapVertical==1)) { - settings.wrapVertical=1; - } - if (ImGui::RadioButton("Yes, and move to next/prev pattern##wrapV2",settings.wrapVertical==2)) { - settings.wrapVertical=2; - } - - ImGui::Text("Cursor movement keys behavior:"); - if (ImGui::RadioButton("Move by one##cmk0",settings.scrollStep==0)) { - settings.scrollStep=0; - } - if (ImGui::RadioButton("Move by Edit Step##cmk1",settings.scrollStep==1)) { - settings.scrollStep=1; - } - - ImGui::Text("Effect input cursor behavior:"); - if (ImGui::RadioButton("Move down##eicb0",settings.effectCursorDir==0)) { - settings.effectCursorDir=0; - } - if (ImGui::RadioButton("Move to effect value (otherwise move down)##eicb1",settings.effectCursorDir==1)) { - settings.effectCursorDir=1; - } - if (ImGui::RadioButton("Move to effect value/next effect and wrap around##eicb2",settings.effectCursorDir==2)) { - settings.effectCursorDir=2; - } - - ImGui::EndTabItem(); - } - if (ImGui::BeginTabItem("Audio/MIDI")) { - ImGui::Text("Backend"); - ImGui::SameLine(); - ImGui::Combo("##Backend",&settings.audioEngine,audioBackends,2); - - ImGui::Text("Device"); - ImGui::SameLine(); - String audioDevName=settings.audioDevice.empty()?"":settings.audioDevice; - if (ImGui::BeginCombo("##AudioDevice",audioDevName.c_str())) { - if (ImGui::Selectable("",settings.audioDevice.empty())) { - settings.audioDevice=""; + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("reduces latency by running the engine faster than the tick rate.\nuseful for live playback/jam mode.\n\nwarning: experimental! may produce glitches.\nonly enable if your buffer size is small (10ms or less)."); } - for (String& i: e->getAudioDevices()) { - if (ImGui::Selectable(i.c_str(),i==settings.audioDevice)) { - settings.audioDevice=i; - } + + bool forceMonoB=settings.forceMono; + if (ImGui::Checkbox("Force mono audio",&forceMonoB)) { + settings.forceMono=forceMonoB; } - ImGui::EndCombo(); - } - ImGui::Text("Sample rate"); - ImGui::SameLine(); - String sr=fmt::sprintf("%d",settings.audioRate); - if (ImGui::BeginCombo("##SampleRate",sr.c_str())) { - SAMPLE_RATE_SELECTABLE(8000); - SAMPLE_RATE_SELECTABLE(16000); - SAMPLE_RATE_SELECTABLE(22050); - SAMPLE_RATE_SELECTABLE(32000); - SAMPLE_RATE_SELECTABLE(44100); - SAMPLE_RATE_SELECTABLE(48000); - SAMPLE_RATE_SELECTABLE(88200); - SAMPLE_RATE_SELECTABLE(96000); - SAMPLE_RATE_SELECTABLE(192000); - ImGui::EndCombo(); - } + TAAudioDesc& audioWant=e->getAudioDescWant(); + TAAudioDesc& audioGot=e->getAudioDescGot(); - ImGui::Text("Buffer size"); - ImGui::SameLine(); - String bs=fmt::sprintf("%d (latency: ~%.1fms)",settings.audioBufSize,2000.0*(double)settings.audioBufSize/(double)MAX(1,settings.audioRate)); - if (ImGui::BeginCombo("##BufferSize",bs.c_str())) { - BUFFER_SIZE_SELECTABLE(64); - BUFFER_SIZE_SELECTABLE(128); - BUFFER_SIZE_SELECTABLE(256); - BUFFER_SIZE_SELECTABLE(512); - BUFFER_SIZE_SELECTABLE(1024); - BUFFER_SIZE_SELECTABLE(2048); - ImGui::EndCombo(); - } - - ImGui::Text("Quality"); - ImGui::SameLine(); - ImGui::Combo("##Quality",&settings.audioQuality,audioQualities,2); + ImGui::Text("want: %d samples @ %.0fHz",audioWant.bufsize,audioWant.rate); + ImGui::Text("got: %d samples @ %.0fHz",audioGot.bufsize,audioGot.rate); - ImGui::Text("Metronome volume"); - ImGui::SameLine(); - if (ImGui::SliderInt("##MetroVol",&settings.metroVol,0,200,"%d%%")) { - if (settings.metroVol<0) settings.metroVol=0; - if (settings.metroVol>200) settings.metroVol=200; - e->setMetronomeVol(((float)settings.metroVol)/100.0f); - } + ImGui::Separator(); - bool lowLatencyB=settings.lowLatency; - if (ImGui::Checkbox("Low-latency mode (experimental!)",&lowLatencyB)) { - settings.lowLatency=lowLatencyB; - } - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("reduces latency by running the engine faster than the tick rate.\nuseful for live playback/jam mode.\n\nwarning: experimental! may produce glitches.\nonly enable if your buffer size is small (10ms or less)."); - } - - bool forceMonoB=settings.forceMono; - if (ImGui::Checkbox("Force mono audio",&forceMonoB)) { - settings.forceMono=forceMonoB; - } - - TAAudioDesc& audioWant=e->getAudioDescWant(); - TAAudioDesc& audioGot=e->getAudioDescGot(); - - ImGui::Text("want: %d samples @ %.0fHz",audioWant.bufsize,audioWant.rate); - ImGui::Text("got: %d samples @ %.0fHz",audioGot.bufsize,audioGot.rate); - - ImGui::Separator(); - - ImGui::Text("MIDI input"); - ImGui::SameLine(); - String midiInName=settings.midiInDevice.empty()?"":settings.midiInDevice; - bool hasToReloadMidi=false; - if (ImGui::BeginCombo("##MidiInDevice",midiInName.c_str())) { - if (ImGui::Selectable("",settings.midiInDevice.empty())) { - settings.midiInDevice=""; - hasToReloadMidi=true; - } - for (String& i: e->getMidiIns()) { - if (ImGui::Selectable(i.c_str(),i==settings.midiInDevice)) { - settings.midiInDevice=i; + ImGui::Text("MIDI input"); + ImGui::SameLine(); + String midiInName=settings.midiInDevice.empty()?"":settings.midiInDevice; + bool hasToReloadMidi=false; + if (ImGui::BeginCombo("##MidiInDevice",midiInName.c_str())) { + if (ImGui::Selectable("",settings.midiInDevice.empty())) { + settings.midiInDevice=""; hasToReloadMidi=true; } - } - ImGui::EndCombo(); - } - - if (hasToReloadMidi) { - midiMap.read(e->getConfigPath()+DIR_SEPARATOR_STR+"midiIn_"+stripName(settings.midiInDevice)+".cfg"); - midiMap.compile(); - } - - ImGui::Text("MIDI output"); - ImGui::SameLine(); - String midiOutName=settings.midiOutDevice.empty()?"":settings.midiOutDevice; - if (ImGui::BeginCombo("##MidiOutDevice",midiOutName.c_str())) { - if (ImGui::Selectable("",settings.midiOutDevice.empty())) { - settings.midiOutDevice=""; - } - for (String& i: e->getMidiIns()) { - if (ImGui::Selectable(i.c_str(),i==settings.midiOutDevice)) { - settings.midiOutDevice=i; - } - } - ImGui::EndCombo(); - } - - if (ImGui::TreeNode("MIDI input settings")) { - ImGui::Checkbox("Note input",&midiMap.noteInput); - ImGui::Checkbox("Velocity input",&midiMap.volInput); - // TODO - //ImGui::Checkbox("Use raw velocity value (don't map from linear to log)",&midiMap.rawVolume); - //ImGui::Checkbox("Polyphonic/chord input",&midiMap.polyInput); - ImGui::Checkbox("Map MIDI channels to direct channels",&midiMap.directChannel); - ImGui::Checkbox("Program change is instrument selection",&midiMap.programChange); - //ImGui::Checkbox("Listen to MIDI clock",&midiMap.midiClock); - //ImGui::Checkbox("Listen to MIDI time code",&midiMap.midiTimeCode); - ImGui::Combo("Value input style",&midiMap.valueInputStyle,valueInputStyles,7); - if (midiMap.valueInputStyle>3) { - if (midiMap.valueInputStyle==6) { - if (ImGui::InputInt("Control##valueCCS",&midiMap.valueInputControlSingle,1,16)) { - if (midiMap.valueInputControlSingle<0) midiMap.valueInputControlSingle=0; - if (midiMap.valueInputControlSingle>127) midiMap.valueInputControlSingle=127; - } - } else { - if (ImGui::InputInt((midiMap.valueInputStyle==4)?"CC of upper nibble##valueCC1":"MSB CC##valueCC1",&midiMap.valueInputControlMSB,1,16)) { - if (midiMap.valueInputControlMSB<0) midiMap.valueInputControlMSB=0; - if (midiMap.valueInputControlMSB>127) midiMap.valueInputControlMSB=127; - } - if (ImGui::InputInt((midiMap.valueInputStyle==4)?"CC of lower nibble##valueCC2":"LSB CC##valueCC2",&midiMap.valueInputControlLSB,1,16)) { - if (midiMap.valueInputControlLSB<0) midiMap.valueInputControlLSB=0; - if (midiMap.valueInputControlLSB>127) midiMap.valueInputControlLSB=127; + for (String& i: e->getMidiIns()) { + if (ImGui::Selectable(i.c_str(),i==settings.midiInDevice)) { + settings.midiInDevice=i; + hasToReloadMidi=true; } } + ImGui::EndCombo(); } - if (ImGui::TreeNode("Per-column control change")) { - for (int i=0; i<18; i++) { - ImGui::PushID(i); - ImGui::Combo(specificControls[i],&midiMap.valueInputSpecificStyle[i],valueSInputStyles,4); - if (midiMap.valueInputSpecificStyle[i]>0) { - ImGui::Indent(); - if (midiMap.valueInputSpecificStyle[i]==3) { - if (ImGui::InputInt("Control##valueCCS",&midiMap.valueInputSpecificSingle[i],1,16)) { - if (midiMap.valueInputSpecificSingle[i]<0) midiMap.valueInputSpecificSingle[i]=0; - if (midiMap.valueInputSpecificSingle[i]>127) midiMap.valueInputSpecificSingle[i]=127; - } - } else { - if (ImGui::InputInt((midiMap.valueInputSpecificStyle[i]==4)?"CC of upper nibble##valueCC1":"MSB CC##valueCC1",&midiMap.valueInputSpecificMSB[i],1,16)) { - if (midiMap.valueInputSpecificMSB[i]<0) midiMap.valueInputSpecificMSB[i]=0; - if (midiMap.valueInputSpecificMSB[i]>127) midiMap.valueInputSpecificMSB[i]=127; - } - if (ImGui::InputInt((midiMap.valueInputSpecificStyle[i]==4)?"CC of lower nibble##valueCC2":"LSB CC##valueCC2",&midiMap.valueInputSpecificLSB[i],1,16)) { - if (midiMap.valueInputSpecificLSB[i]<0) midiMap.valueInputSpecificLSB[i]=0; - if (midiMap.valueInputSpecificLSB[i]>127) midiMap.valueInputSpecificLSB[i]=127; - } - } - ImGui::Unindent(); - } - ImGui::PopID(); - } - ImGui::TreePop(); - } - if (ImGui::SliderFloat("Volume curve",&midiMap.volExp,0.01,8.0,"%.2f")) { - if (midiMap.volExp<0.01) midiMap.volExp=0.01; - if (midiMap.volExp>8.0) midiMap.volExp=8.0; - } rightClickable - float curve[128]; - for (int i=0; i<128; i++) { - curve[i]=(int)(pow((double)i/127.0,midiMap.volExp)*127.0); - } - ImGui::PlotLines("##VolCurveDisplay",curve,128,0,"Volume curve",0.0,127.0,ImVec2(200.0f*dpiScale,200.0f*dpiScale)); - ImGui::Text("Actions:"); + if (hasToReloadMidi) { + midiMap.read(e->getConfigPath()+DIR_SEPARATOR_STR+"midiIn_"+stripName(settings.midiInDevice)+".cfg"); + midiMap.compile(); + } + + ImGui::Text("MIDI output"); ImGui::SameLine(); - if (ImGui::Button(ICON_FA_PLUS "##AddAction")) { - midiMap.binds.push_back(MIDIBind()); - } - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_EXTERNAL_LINK "##AddLearnAction")) { - midiMap.binds.push_back(MIDIBind()); - learning=midiMap.binds.size()-1; - } - if (learning!=-1) { - ImGui::SameLine(); - ImGui::Text("(learning! press a button or move a slider/knob/something on your device.)"); - } - - if (ImGui::BeginTable("MIDIActions",7)) { - ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthStretch,0.2); - ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthStretch,0.1); - ImGui::TableSetupColumn("c2",ImGuiTableColumnFlags_WidthStretch,0.3); - ImGui::TableSetupColumn("c3",ImGuiTableColumnFlags_WidthStretch,0.2); - ImGui::TableSetupColumn("c4",ImGuiTableColumnFlags_WidthStretch,0.5); - ImGui::TableSetupColumn("c5",ImGuiTableColumnFlags_WidthFixed); - ImGui::TableSetupColumn("c6",ImGuiTableColumnFlags_WidthFixed); - - ImGui::TableNextRow(ImGuiTableRowFlags_Headers); - ImGui::TableNextColumn(); - ImGui::Text("Type"); - ImGui::TableNextColumn(); - ImGui::Text("Channel"); - ImGui::TableNextColumn(); - ImGui::Text("Note/Control"); - ImGui::TableNextColumn(); - ImGui::Text("Velocity/Value"); - ImGui::TableNextColumn(); - ImGui::Text("Action"); - ImGui::TableNextColumn(); - ImGui::Text("Learn"); - ImGui::TableNextColumn(); - ImGui::Text("Remove"); - - for (size_t i=0; i":settings.midiOutDevice; + if (ImGui::BeginCombo("##MidiOutDevice",midiOutName.c_str())) { + if (ImGui::Selectable("",settings.midiOutDevice.empty())) { + settings.midiOutDevice=""; + } + for (String& i: e->getMidiIns()) { + if (ImGui::Selectable(i.c_str(),i==settings.midiOutDevice)) { + settings.midiOutDevice=i; } + } + ImGui::EndCombo(); + } - ImGui::TableNextColumn(); - ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); - if (ImGui::BeginCombo("##BChannel",messageChannels[bind.channel])) { - if (ImGui::Selectable(messageChannels[16],bind.channel==16)) { - bind.channel=16; + if (ImGui::TreeNode("MIDI input settings")) { + ImGui::Checkbox("Note input",&midiMap.noteInput); + ImGui::Checkbox("Velocity input",&midiMap.volInput); + // TODO + //ImGui::Checkbox("Use raw velocity value (don't map from linear to log)",&midiMap.rawVolume); + //ImGui::Checkbox("Polyphonic/chord input",&midiMap.polyInput); + ImGui::Checkbox("Map MIDI channels to direct channels",&midiMap.directChannel); + ImGui::Checkbox("Program change is instrument selection",&midiMap.programChange); + //ImGui::Checkbox("Listen to MIDI clock",&midiMap.midiClock); + //ImGui::Checkbox("Listen to MIDI time code",&midiMap.midiTimeCode); + ImGui::Combo("Value input style",&midiMap.valueInputStyle,valueInputStyles,7); + if (midiMap.valueInputStyle>3) { + if (midiMap.valueInputStyle==6) { + if (ImGui::InputInt("Control##valueCCS",&midiMap.valueInputControlSingle,1,16)) { + if (midiMap.valueInputControlSingle<0) midiMap.valueInputControlSingle=0; + if (midiMap.valueInputControlSingle>127) midiMap.valueInputControlSingle=127; } - for (int j=0; j<16; j++) { - if (ImGui::Selectable(messageChannels[j],bind.channel==j)) { - bind.channel=j; - } - } - ImGui::EndCombo(); - } - - ImGui::TableNextColumn(); - if (bind.data1==128) { - snprintf(bindID,1024,"Any"); } else { - const char* nName="???"; - if ((bind.data1+60)>0 && (bind.data1+60)<180) { - nName=noteNames[bind.data1+60]; + if (ImGui::InputInt((midiMap.valueInputStyle==4)?"CC of upper nibble##valueCC1":"MSB CC##valueCC1",&midiMap.valueInputControlMSB,1,16)) { + if (midiMap.valueInputControlMSB<0) midiMap.valueInputControlMSB=0; + if (midiMap.valueInputControlMSB>127) midiMap.valueInputControlMSB=127; + } + if (ImGui::InputInt((midiMap.valueInputStyle==4)?"CC of lower nibble##valueCC2":"LSB CC##valueCC2",&midiMap.valueInputControlLSB,1,16)) { + if (midiMap.valueInputControlLSB<0) midiMap.valueInputControlLSB=0; + if (midiMap.valueInputControlLSB>127) midiMap.valueInputControlLSB=127; } - snprintf(bindID,1024,"%d (0x%.2X, %s)",bind.data1,bind.data1,nName); } - ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); - if (ImGui::BeginCombo("##BValue1",bindID)) { - if (ImGui::Selectable("Any",bind.data1==128)) { - bind.data1=128; - } - for (int j=0; j<128; j++) { - const char* nName="???"; - if ((j+60)>0 && (j+60)<180) { - nName=noteNames[j+60]; - } - snprintf(bindID,1024,"%d (0x%.2X, %s)##BV1_%d",j,j,nName,j); - if (ImGui::Selectable(bindID,bind.data1==j)) { - bind.data1=j; - } - } - ImGui::EndCombo(); - } - - ImGui::TableNextColumn(); - if (bind.data2==128) { - snprintf(bindID,1024,"Any"); - } else { - snprintf(bindID,1024,"%d (0x%.2X)",bind.data2,bind.data2); - } - ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); - if (ImGui::BeginCombo("##BValue2",bindID)) { - if (ImGui::Selectable("Any",bind.data2==128)) { - bind.data2=128; - } - for (int j=0; j<128; j++) { - snprintf(bindID,1024,"%d (0x%.2X)##BV2_%d",j,j,j); - if (ImGui::Selectable(bindID,bind.data2==j)) { - bind.data2=j; - } - } - ImGui::EndCombo(); - } - - ImGui::TableNextColumn(); - ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); - if (ImGui::BeginCombo("##BAction",(bind.action==0)?"--none--":guiActions[bind.action].friendlyName)) { - if (ImGui::Selectable("--none--",bind.action==0)) { - bind.action=0; - } - for (int j=0; j0) { + ImGui::Indent(); + if (midiMap.valueInputSpecificStyle[i]==3) { + if (ImGui::InputInt("Control##valueCCS",&midiMap.valueInputSpecificSingle[i],1,16)) { + if (midiMap.valueInputSpecificSingle[i]<0) midiMap.valueInputSpecificSingle[i]=0; + if (midiMap.valueInputSpecificSingle[i]>127) midiMap.valueInputSpecificSingle[i]=127; + } } else { - snprintf(bindID,1024,"%s##BA_%d",guiActions[j].friendlyName,j); - if (ImGui::Selectable(bindID,bind.action==j)) { - bind.action=j; + if (ImGui::InputInt((midiMap.valueInputSpecificStyle[i]==4)?"CC of upper nibble##valueCC1":"MSB CC##valueCC1",&midiMap.valueInputSpecificMSB[i],1,16)) { + if (midiMap.valueInputSpecificMSB[i]<0) midiMap.valueInputSpecificMSB[i]=0; + if (midiMap.valueInputSpecificMSB[i]>127) midiMap.valueInputSpecificMSB[i]=127; + } + if (ImGui::InputInt((midiMap.valueInputSpecificStyle[i]==4)?"CC of lower nibble##valueCC2":"LSB CC##valueCC2",&midiMap.valueInputSpecificLSB[i],1,16)) { + if (midiMap.valueInputSpecificLSB[i]<0) midiMap.valueInputSpecificLSB[i]=0; + if (midiMap.valueInputSpecificLSB[i]>127) midiMap.valueInputSpecificLSB[i]=127; } } + ImGui::Unindent(); } - ImGui::EndCombo(); + ImGui::PopID(); } - - ImGui::TableNextColumn(); - if (ImGui::Button((learning==(int)i)?("waiting...##BLearn"):(ICON_FA_SQUARE_O "##BLearn"))) { - if (learning==(int)i) { - learning=-1; - } else { - learning=i; - } - } - - ImGui::TableNextColumn(); - if (ImGui::Button(ICON_FA_TIMES "##BRemove")) { - midiMap.binds.erase(midiMap.binds.begin()+i); - if (learning==(int)i) learning=-1; - i--; - } - - ImGui::PopID(); + ImGui::TreePop(); } - ImGui::EndTable(); + if (ImGui::SliderFloat("Volume curve",&midiMap.volExp,0.01,8.0,"%.2f")) { + if (midiMap.volExp<0.01) midiMap.volExp=0.01; + if (midiMap.volExp>8.0) midiMap.volExp=8.0; + } rightClickable + float curve[128]; + for (int i=0; i<128; i++) { + curve[i]=(int)(pow((double)i/127.0,midiMap.volExp)*127.0); + } + ImGui::PlotLines("##VolCurveDisplay",curve,128,0,"Volume curve",0.0,127.0,ImVec2(200.0f*dpiScale,200.0f*dpiScale)); + + ImGui::Text("Actions:"); + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_PLUS "##AddAction")) { + midiMap.binds.push_back(MIDIBind()); + } + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_EXTERNAL_LINK "##AddLearnAction")) { + midiMap.binds.push_back(MIDIBind()); + learning=midiMap.binds.size()-1; + } + if (learning!=-1) { + ImGui::SameLine(); + ImGui::Text("(learning! press a button or move a slider/knob/something on your device.)"); + } + + if (ImGui::BeginTable("MIDIActions",7)) { + ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthStretch,0.2); + ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthStretch,0.1); + ImGui::TableSetupColumn("c2",ImGuiTableColumnFlags_WidthStretch,0.3); + ImGui::TableSetupColumn("c3",ImGuiTableColumnFlags_WidthStretch,0.2); + ImGui::TableSetupColumn("c4",ImGuiTableColumnFlags_WidthStretch,0.5); + ImGui::TableSetupColumn("c5",ImGuiTableColumnFlags_WidthFixed); + ImGui::TableSetupColumn("c6",ImGuiTableColumnFlags_WidthFixed); + + ImGui::TableNextRow(ImGuiTableRowFlags_Headers); + ImGui::TableNextColumn(); + ImGui::Text("Type"); + ImGui::TableNextColumn(); + ImGui::Text("Channel"); + ImGui::TableNextColumn(); + ImGui::Text("Note/Control"); + ImGui::TableNextColumn(); + ImGui::Text("Velocity/Value"); + ImGui::TableNextColumn(); + ImGui::Text("Action"); + ImGui::TableNextColumn(); + ImGui::Text("Learn"); + ImGui::TableNextColumn(); + ImGui::Text("Remove"); + + for (size_t i=0; i0 && (bind.data1+60)<180) { + nName=noteNames[bind.data1+60]; + } + snprintf(bindID,1024,"%d (0x%.2X, %s)",bind.data1,bind.data1,nName); + } + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + if (ImGui::BeginCombo("##BValue1",bindID)) { + if (ImGui::Selectable("Any",bind.data1==128)) { + bind.data1=128; + } + for (int j=0; j<128; j++) { + const char* nName="???"; + if ((j+60)>0 && (j+60)<180) { + nName=noteNames[j+60]; + } + snprintf(bindID,1024,"%d (0x%.2X, %s)##BV1_%d",j,j,nName,j); + if (ImGui::Selectable(bindID,bind.data1==j)) { + bind.data1=j; + } + } + ImGui::EndCombo(); + } + + ImGui::TableNextColumn(); + if (bind.data2==128) { + snprintf(bindID,1024,"Any"); + } else { + snprintf(bindID,1024,"%d (0x%.2X)",bind.data2,bind.data2); + } + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + if (ImGui::BeginCombo("##BValue2",bindID)) { + if (ImGui::Selectable("Any",bind.data2==128)) { + bind.data2=128; + } + for (int j=0; j<128; j++) { + snprintf(bindID,1024,"%d (0x%.2X)##BV2_%d",j,j,j); + if (ImGui::Selectable(bindID,bind.data2==j)) { + bind.data2=j; + } + } + ImGui::EndCombo(); + } + + ImGui::TableNextColumn(); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + if (ImGui::BeginCombo("##BAction",(bind.action==0)?"--none--":guiActions[bind.action].friendlyName)) { + if (ImGui::Selectable("--none--",bind.action==0)) { + bind.action=0; + } + for (int j=0; j3.0f) settings.dpiScale=3.0f; - } rightClickable - } - ImGui::Text("Main font"); - ImGui::SameLine(); - ImGui::Combo("##MainFont",&settings.mainFont,mainFonts,7); - if (settings.mainFont==6) { - ImGui::InputText("##MainFontPath",&settings.mainFontPath); - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_FOLDER "##MainFontLoad")) { - openFileDialog(GUI_FILE_LOAD_MAIN_FONT); - } - } - if (ImGui::InputInt("Size##MainFontSize",&settings.mainFontSize)) { - if (settings.mainFontSize<3) settings.mainFontSize=3; - if (settings.mainFontSize>96) settings.mainFontSize=96; - } - ImGui::Text("Pattern font"); - ImGui::SameLine(); - ImGui::Combo("##PatFont",&settings.patFont,patFonts,7); - if (settings.patFont==6) { - ImGui::InputText("##PatFontPath",&settings.patFontPath); - ImGui::SameLine(); - if (ImGui::Button(ICON_FA_FOLDER "##PatFontLoad")) { - openFileDialog(GUI_FILE_LOAD_PAT_FONT); - } - } - if (ImGui::InputInt("Size##PatFontSize",&settings.patFontSize)) { - if (settings.patFontSize<3) settings.patFontSize=3; - if (settings.patFontSize>96) settings.patFontSize=96; - } - - bool loadJapaneseB=settings.loadJapanese; - if (ImGui::Checkbox("Display Japanese characters",&loadJapaneseB)) { - settings.loadJapanese=loadJapaneseB; - } - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip( - "Only toggle this option if you have enough graphics memory.\n" - "This is a temporary solution until dynamic font atlas is implemented in Dear ImGui.\n\n" - "ã“ã®ã‚ªãƒ—ションã¯ã€å分ãªã‚°ãƒ©ãƒ•ィックメモリãŒã‚ã‚‹å ´åˆã«ã®ã¿åˆ‡ã‚Šæ›¿ãˆã¦ãã ã•ã„。\n" - "ã“れã¯ã€Dear ImGuiã«ãƒ€ã‚¤ãƒŠãƒŸãƒƒã‚¯ãƒ•ォントアトラスãŒå®Ÿè£…ã•れるã¾ã§ã®ä¸€æ™‚çš„ãªè§£æ±ºç­–ã§ã™ã€‚" - ); - } - - ImGui::Separator(); - - ImGui::Text("Orders row number format:"); - if (ImGui::RadioButton("Decimal##orbD",settings.orderRowsBase==0)) { - settings.orderRowsBase=0; - } - if (ImGui::RadioButton("Hexadecimal##orbH",settings.orderRowsBase==1)) { - settings.orderRowsBase=1; - } - - ImGui::Text("Pattern row number format:"); - if (ImGui::RadioButton("Decimal##prbD",settings.patRowsBase==0)) { - settings.patRowsBase=0; - } - if (ImGui::RadioButton("Hexadecimal##prbH",settings.patRowsBase==1)) { - settings.patRowsBase=1; - } - - ImGui::Text("FM parameter names:"); - if (ImGui::RadioButton("Friendly##fmn0",settings.fmNames==0)) { - settings.fmNames=0; - } - if (ImGui::RadioButton("Technical##fmn1",settings.fmNames==1)) { - settings.fmNames=1; - } - if (ImGui::RadioButton("Technical (alternate)##fmn2",settings.fmNames==2)) { - settings.fmNames=2; - } - - ImGui::Separator(); - - ImGui::Text("Title bar:"); - if (ImGui::RadioButton("Furnace##tbar0",settings.titleBarInfo==0)) { - settings.titleBarInfo=0; - updateWindowTitle(); - } - if (ImGui::RadioButton("Song Name - Furnace##tbar1",settings.titleBarInfo==1)) { - settings.titleBarInfo=1; - updateWindowTitle(); - } - if (ImGui::RadioButton("file_name.fur - Furnace##tbar2",settings.titleBarInfo==2)) { - settings.titleBarInfo=2; - updateWindowTitle(); - } - if (ImGui::RadioButton("/path/to/file.fur - Furnace##tbar3",settings.titleBarInfo==3)) { - settings.titleBarInfo=3; - updateWindowTitle(); - } - - bool titleBarSysB=settings.titleBarSys; - if (ImGui::Checkbox("Display system name on title bar",&titleBarSysB)) { - settings.titleBarSys=titleBarSysB; - updateWindowTitle(); - } - - ImGui::Text("Status bar:"); - if (ImGui::RadioButton("Cursor details##sbar0",settings.statusDisplay==0)) { - settings.statusDisplay=0; - } - if (ImGui::RadioButton("File path##sbar1",settings.statusDisplay==1)) { - settings.statusDisplay=1; - } - if (ImGui::RadioButton("Cursor details or file path##sbar2",settings.statusDisplay==2)) { - settings.statusDisplay=2; - } - if (ImGui::RadioButton("Nothing##sbar3",settings.statusDisplay==3)) { - settings.statusDisplay=3; - } - - ImGui::Text("Play/edit controls layout:"); - if (ImGui::RadioButton("Classic##ecl0",settings.controlLayout==0)) { - settings.controlLayout=0; - } - if (ImGui::RadioButton("Compact##ecl1",settings.controlLayout==1)) { - settings.controlLayout=1; - } - if (ImGui::RadioButton("Compact (vertical)##ecl2",settings.controlLayout==2)) { - settings.controlLayout=2; - } - if (ImGui::RadioButton("Split##ecl3",settings.controlLayout==3)) { - settings.controlLayout=3; - } - - ImGui::Text("FM parameter editor layout:"); - if (ImGui::RadioButton("Modern##fml0",settings.fmLayout==0)) { - settings.fmLayout=0; - } - if (ImGui::RadioButton("Compact (2x2, classic)##fml1",settings.fmLayout==1)) { - settings.fmLayout=1; - } - if (ImGui::RadioButton("Compact (1x4)##fml2",settings.fmLayout==2)) { - settings.fmLayout=2; - } - if (ImGui::RadioButton("Compact (4x1)##fml3",settings.fmLayout==3)) { - settings.fmLayout=3; - } - - ImGui::Text("Position of Sustain in FM editor:"); - if (ImGui::RadioButton("Between Decay and Sustain Rate##susp0",settings.susPosition==0)) { - settings.susPosition=0; - } - if (ImGui::RadioButton("After Release Rate##susp1",settings.susPosition==1)) { - settings.susPosition=1; - } - - ImGui::Separator(); - - bool insEditColorizeB=settings.insEditColorize; - if (ImGui::Checkbox("Colorize instrument editor using instrument type",&insEditColorizeB)) { - settings.insEditColorize=insEditColorizeB; - } - - bool separateFMColorsB=settings.separateFMColors; - if (ImGui::Checkbox("Use separate colors for carriers/modulators in FM editor",&separateFMColorsB)) { - settings.separateFMColors=separateFMColorsB; - } - - bool macroViewB=settings.macroView; - if (ImGui::Checkbox("Classic macro view (standard macros only; deprecated!)",¯oViewB)) { - settings.macroView=macroViewB; - } - - bool unifiedDataViewB=settings.unifiedDataView; - if (ImGui::Checkbox("Unified instrument/wavetable/sample list",&unifiedDataViewB)) { - settings.unifiedDataView=unifiedDataViewB; - } - - bool chipNamesB=settings.chipNames; - if (ImGui::Checkbox("Use chip names instead of system names",&chipNamesB)) { - settings.chipNames=chipNamesB; - } - - bool oplStandardWaveNamesB=settings.oplStandardWaveNames; - if (ImGui::Checkbox("Use standard OPL waveform names",&oplStandardWaveNamesB)) { - settings.oplStandardWaveNames=oplStandardWaveNamesB; - } - - if (nonLatchNibble) { - bool hiddenSystemsB=settings.hiddenSystems; - if (ImGui::Checkbox(":smile: :star_struck: :sunglasses: :ok_hand:",&hiddenSystemsB)) { - settings.hiddenSystems=hiddenSystemsB; - } - } - - bool overflowHighlightB=settings.overflowHighlight; - if (ImGui::Checkbox("Overflow pattern highlights",&overflowHighlightB)) { - settings.overflowHighlight=overflowHighlightB; - } - - bool viewPrevPatternB=settings.viewPrevPattern; - if (ImGui::Checkbox("Display previous/next pattern",&viewPrevPatternB)) { - settings.viewPrevPattern=viewPrevPatternB; - } - - bool germanNotationB=settings.germanNotation; - if (ImGui::Checkbox("Use German notation",&germanNotationB)) { - settings.germanNotation=germanNotationB; - } - - // sorry. temporarily disabled until ImGui has a way to add separators in tables arbitrarily. - /*bool sysSeparatorsB=settings.sysSeparators; - if (ImGui::Checkbox("Add separators between systems in Orders",&sysSeparatorsB)) { - settings.sysSeparators=sysSeparatorsB; - }*/ - - bool partyTimeB=settings.partyTime; - if (ImGui::Checkbox("About screen party time",&partyTimeB)) { - settings.partyTime=partyTimeB; - } - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("Warning: may cause epileptic seizures."); - } - - ImGui::Separator(); - - bool waveLayoutB=settings.waveLayout; - if (ImGui::Checkbox("Use compact wave editor",&waveLayoutB)) { - settings.waveLayout=waveLayoutB; - } - - bool sampleLayoutB=settings.sampleLayout; - if (ImGui::Checkbox("Use compact sample editor",&sampleLayoutB)) { - settings.sampleLayout=sampleLayoutB; - } - - bool roundedWindowsB=settings.roundedWindows; - if (ImGui::Checkbox("Rounded window corners",&roundedWindowsB)) { - settings.roundedWindows=roundedWindowsB; - } - - bool roundedButtonsB=settings.roundedButtons; - if (ImGui::Checkbox("Rounded buttons",&roundedButtonsB)) { - settings.roundedButtons=roundedButtonsB; - } - - bool roundedMenusB=settings.roundedMenus; - if (ImGui::Checkbox("Rounded menu corners",&roundedMenusB)) { - settings.roundedMenus=roundedMenusB; - } - - bool frameBordersB=settings.frameBorders; - if (ImGui::Checkbox("Borders around widgets",&frameBordersB)) { - settings.frameBorders=frameBordersB; - } - - ImGui::Separator(); - - ImGui::Text("Oscilloscope settings:"); - - bool oscRoundedCornersB=settings.oscRoundedCorners; - if (ImGui::Checkbox("Rounded corners",&oscRoundedCornersB)) { - settings.oscRoundedCorners=oscRoundedCornersB; - } - - bool oscTakesEntireWindowB=settings.oscTakesEntireWindow; - if (ImGui::Checkbox("Fill entire window",&oscTakesEntireWindowB)) { - settings.oscTakesEntireWindow=oscTakesEntireWindowB; - } - - bool oscBorderB=settings.oscBorder; - if (ImGui::Checkbox("Border",&oscBorderB)) { - settings.oscBorder=oscBorderB; - } - - ImGui::Separator(); - - if (ImGui::TreeNode("Color scheme")) { - if (ImGui::Button("Import")) { - openFileDialog(GUI_FILE_IMPORT_COLORS); - } - ImGui::SameLine(); - if (ImGui::Button("Export")) { - openFileDialog(GUI_FILE_EXPORT_COLORS); - } - ImGui::SameLine(); - if (ImGui::Button("Reset defaults")) { - showWarning("Are you sure you want to reset the color scheme?",GUI_WARN_RESET_COLORS); - } - if (ImGui::TreeNode("General")) { - ImGui::Text("Color scheme type:"); - if (ImGui::RadioButton("Dark##gcb0",settings.guiColorsBase==0)) { - settings.guiColorsBase=0; + ImVec2 settingsViewSize=ImGui::GetContentRegionAvail(); + settingsViewSize.y-=ImGui::GetFrameHeight()+ImGui::GetStyle().WindowPadding.y; + if (ImGui::BeginChild("SettingsView",settingsViewSize)) { + bool dpiScaleAuto=(settings.dpiScale<0.5f); + if (ImGui::Checkbox("Automatic UI scaling factor",&dpiScaleAuto)) { + if (dpiScaleAuto) { + settings.dpiScale=0.0f; + } else { + settings.dpiScale=1.0f; } - if (ImGui::RadioButton("Light##gcb1",settings.guiColorsBase==1)) { - settings.guiColorsBase=1; + } + if (!dpiScaleAuto) { + if (ImGui::SliderFloat("UI scaling factor",&settings.dpiScale,1.0f,3.0f,"%.2fx")) { + if (settings.dpiScale<0.5f) settings.dpiScale=0.5f; + if (settings.dpiScale>3.0f) settings.dpiScale=3.0f; + } rightClickable + } + ImGui::Text("Main font"); + ImGui::SameLine(); + ImGui::Combo("##MainFont",&settings.mainFont,mainFonts,7); + if (settings.mainFont==6) { + ImGui::InputText("##MainFontPath",&settings.mainFontPath); + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_FOLDER "##MainFontLoad")) { + openFileDialog(GUI_FILE_LOAD_MAIN_FONT); } - UI_COLOR_CONFIG(GUI_COLOR_BACKGROUND,"Background"); - UI_COLOR_CONFIG(GUI_COLOR_FRAME_BACKGROUND,"Window background"); - UI_COLOR_CONFIG(GUI_COLOR_MODAL_BACKDROP,"Modal backdrop"); - UI_COLOR_CONFIG(GUI_COLOR_HEADER,"Header"); - UI_COLOR_CONFIG(GUI_COLOR_TEXT,"Text"); - UI_COLOR_CONFIG(GUI_COLOR_ACCENT_PRIMARY,"Primary"); - UI_COLOR_CONFIG(GUI_COLOR_ACCENT_SECONDARY,"Secondary"); - UI_COLOR_CONFIG(GUI_COLOR_BORDER,"Border"); - UI_COLOR_CONFIG(GUI_COLOR_BORDER_SHADOW,"Border shadow"); - UI_COLOR_CONFIG(GUI_COLOR_TOGGLE_ON,"Toggle on"); - UI_COLOR_CONFIG(GUI_COLOR_TOGGLE_OFF,"Toggle off"); - UI_COLOR_CONFIG(GUI_COLOR_EDITING,"Editing"); - UI_COLOR_CONFIG(GUI_COLOR_SONG_LOOP,"Song loop"); - UI_COLOR_CONFIG(GUI_COLOR_PLAYBACK_STAT,"Playback status"); - ImGui::TreePop(); } - if (ImGui::TreeNode("File Picker (built-in)")) { - UI_COLOR_CONFIG(GUI_COLOR_FILE_DIR,"Directory"); - UI_COLOR_CONFIG(GUI_COLOR_FILE_SONG_NATIVE,"Song (native)"); - UI_COLOR_CONFIG(GUI_COLOR_FILE_SONG_IMPORT,"Song (import)"); - UI_COLOR_CONFIG(GUI_COLOR_FILE_INSTR,"Instrument"); - UI_COLOR_CONFIG(GUI_COLOR_FILE_AUDIO,"Audio"); - UI_COLOR_CONFIG(GUI_COLOR_FILE_WAVE,"Wavetable"); - UI_COLOR_CONFIG(GUI_COLOR_FILE_VGM,"VGM"); - UI_COLOR_CONFIG(GUI_COLOR_FILE_FONT,"Font"); - UI_COLOR_CONFIG(GUI_COLOR_FILE_OTHER,"Other"); - ImGui::TreePop(); + if (ImGui::InputInt("Size##MainFontSize",&settings.mainFontSize)) { + if (settings.mainFontSize<3) settings.mainFontSize=3; + if (settings.mainFontSize>96) settings.mainFontSize=96; } - if (ImGui::TreeNode("Oscilloscope")) { - UI_COLOR_CONFIG(GUI_COLOR_OSC_BORDER,"Border"); - UI_COLOR_CONFIG(GUI_COLOR_OSC_BG1,"Background (top-left)"); - UI_COLOR_CONFIG(GUI_COLOR_OSC_BG2,"Background (top-right)"); - UI_COLOR_CONFIG(GUI_COLOR_OSC_BG3,"Background (bottom-left)"); - UI_COLOR_CONFIG(GUI_COLOR_OSC_BG4,"Background (bottom-right)"); - UI_COLOR_CONFIG(GUI_COLOR_OSC_WAVE,"Waveform"); - UI_COLOR_CONFIG(GUI_COLOR_OSC_WAVE_PEAK,"Waveform (clip)"); - UI_COLOR_CONFIG(GUI_COLOR_OSC_REF,"Reference"); - UI_COLOR_CONFIG(GUI_COLOR_OSC_GUIDE,"Guide"); - ImGui::TreePop(); + ImGui::Text("Pattern font"); + ImGui::SameLine(); + ImGui::Combo("##PatFont",&settings.patFont,patFonts,7); + if (settings.patFont==6) { + ImGui::InputText("##PatFontPath",&settings.patFontPath); + ImGui::SameLine(); + if (ImGui::Button(ICON_FA_FOLDER "##PatFontLoad")) { + openFileDialog(GUI_FILE_LOAD_PAT_FONT); + } } - if (ImGui::TreeNode("Volume Meter")) { - UI_COLOR_CONFIG(GUI_COLOR_VOLMETER_LOW,"Low"); - UI_COLOR_CONFIG(GUI_COLOR_VOLMETER_HIGH,"High"); - UI_COLOR_CONFIG(GUI_COLOR_VOLMETER_PEAK,"Clip"); - ImGui::TreePop(); + if (ImGui::InputInt("Size##PatFontSize",&settings.patFontSize)) { + if (settings.patFontSize<3) settings.patFontSize=3; + if (settings.patFontSize>96) settings.patFontSize=96; } - if (ImGui::TreeNode("Orders")) { - UI_COLOR_CONFIG(GUI_COLOR_ORDER_ROW_INDEX,"Order number"); - UI_COLOR_CONFIG(GUI_COLOR_ORDER_ACTIVE,"Current order background"); - UI_COLOR_CONFIG(GUI_COLOR_ORDER_SIMILAR,"Similar patterns"); - UI_COLOR_CONFIG(GUI_COLOR_ORDER_INACTIVE,"Inactive patterns"); - ImGui::TreePop(); - } - if (ImGui::TreeNode("Envelope View")) { - UI_COLOR_CONFIG(GUI_COLOR_FM_ENVELOPE,"Envelope"); - UI_COLOR_CONFIG(GUI_COLOR_FM_ENVELOPE_SUS_GUIDE,"Sustain guide"); - UI_COLOR_CONFIG(GUI_COLOR_FM_ENVELOPE_RELEASE,"Release"); - ImGui::TreePop(); + bool loadJapaneseB=settings.loadJapanese; + if (ImGui::Checkbox("Display Japanese characters",&loadJapaneseB)) { + settings.loadJapanese=loadJapaneseB; + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip( + "Only toggle this option if you have enough graphics memory.\n" + "This is a temporary solution until dynamic font atlas is implemented in Dear ImGui.\n\n" + "ã“ã®ã‚ªãƒ—ションã¯ã€å分ãªã‚°ãƒ©ãƒ•ィックメモリãŒã‚ã‚‹å ´åˆã«ã®ã¿åˆ‡ã‚Šæ›¿ãˆã¦ãã ã•ã„。\n" + "ã“れã¯ã€Dear ImGuiã«ãƒ€ã‚¤ãƒŠãƒŸãƒƒã‚¯ãƒ•ォントアトラスãŒå®Ÿè£…ã•れるã¾ã§ã®ä¸€æ™‚çš„ãªè§£æ±ºç­–ã§ã™ã€‚" + ); } - if (ImGui::TreeNode("FM Editor")) { - UI_COLOR_CONFIG(GUI_COLOR_FM_ALG_BG,"Algorithm background"); - UI_COLOR_CONFIG(GUI_COLOR_FM_ALG_LINE,"Algorithm lines"); - UI_COLOR_CONFIG(GUI_COLOR_FM_MOD,"Modulator"); - UI_COLOR_CONFIG(GUI_COLOR_FM_CAR,"Carrier"); - UI_COLOR_CONFIG(GUI_COLOR_FM_SSG,"SSG-EG"); - UI_COLOR_CONFIG(GUI_COLOR_FM_WAVE,"Waveform"); + ImGui::Separator(); - ImGui::TextWrapped("(the following colors only apply when \"Use separate colors for carriers/modulators in FM editor\" is on!)"); + ImGui::Text("Orders row number format:"); + if (ImGui::RadioButton("Decimal##orbD",settings.orderRowsBase==0)) { + settings.orderRowsBase=0; + } + if (ImGui::RadioButton("Hexadecimal##orbH",settings.orderRowsBase==1)) { + settings.orderRowsBase=1; + } - UI_COLOR_CONFIG(GUI_COLOR_FM_PRIMARY_MOD,"Mod. accent (primary)"); - UI_COLOR_CONFIG(GUI_COLOR_FM_SECONDARY_MOD,"Mod. accent (secondary)"); - UI_COLOR_CONFIG(GUI_COLOR_FM_BORDER_MOD,"Mod. border"); - UI_COLOR_CONFIG(GUI_COLOR_FM_BORDER_SHADOW_MOD,"Mod. border shadow"); - - UI_COLOR_CONFIG(GUI_COLOR_FM_PRIMARY_CAR,"Car. accent (primary"); - UI_COLOR_CONFIG(GUI_COLOR_FM_SECONDARY_CAR,"Car. accent (secondary)"); - UI_COLOR_CONFIG(GUI_COLOR_FM_BORDER_CAR,"Car. border"); - UI_COLOR_CONFIG(GUI_COLOR_FM_BORDER_SHADOW_CAR,"Car. border shadow"); - + ImGui::Text("Pattern row number format:"); + if (ImGui::RadioButton("Decimal##prbD",settings.patRowsBase==0)) { + settings.patRowsBase=0; + } + if (ImGui::RadioButton("Hexadecimal##prbH",settings.patRowsBase==1)) { + settings.patRowsBase=1; + } + + ImGui::Text("FM parameter names:"); + if (ImGui::RadioButton("Friendly##fmn0",settings.fmNames==0)) { + settings.fmNames=0; + } + if (ImGui::RadioButton("Technical##fmn1",settings.fmNames==1)) { + settings.fmNames=1; + } + if (ImGui::RadioButton("Technical (alternate)##fmn2",settings.fmNames==2)) { + settings.fmNames=2; + } + + ImGui::Separator(); + + ImGui::Text("Title bar:"); + if (ImGui::RadioButton("Furnace##tbar0",settings.titleBarInfo==0)) { + settings.titleBarInfo=0; + updateWindowTitle(); + } + if (ImGui::RadioButton("Song Name - Furnace##tbar1",settings.titleBarInfo==1)) { + settings.titleBarInfo=1; + updateWindowTitle(); + } + if (ImGui::RadioButton("file_name.fur - Furnace##tbar2",settings.titleBarInfo==2)) { + settings.titleBarInfo=2; + updateWindowTitle(); + } + if (ImGui::RadioButton("/path/to/file.fur - Furnace##tbar3",settings.titleBarInfo==3)) { + settings.titleBarInfo=3; + updateWindowTitle(); + } + + bool titleBarSysB=settings.titleBarSys; + if (ImGui::Checkbox("Display system name on title bar",&titleBarSysB)) { + settings.titleBarSys=titleBarSysB; + updateWindowTitle(); + } + + ImGui::Text("Status bar:"); + if (ImGui::RadioButton("Cursor details##sbar0",settings.statusDisplay==0)) { + settings.statusDisplay=0; + } + if (ImGui::RadioButton("File path##sbar1",settings.statusDisplay==1)) { + settings.statusDisplay=1; + } + if (ImGui::RadioButton("Cursor details or file path##sbar2",settings.statusDisplay==2)) { + settings.statusDisplay=2; + } + if (ImGui::RadioButton("Nothing##sbar3",settings.statusDisplay==3)) { + settings.statusDisplay=3; + } + + ImGui::Text("Play/edit controls layout:"); + if (ImGui::RadioButton("Classic##ecl0",settings.controlLayout==0)) { + settings.controlLayout=0; + } + if (ImGui::RadioButton("Compact##ecl1",settings.controlLayout==1)) { + settings.controlLayout=1; + } + if (ImGui::RadioButton("Compact (vertical)##ecl2",settings.controlLayout==2)) { + settings.controlLayout=2; + } + if (ImGui::RadioButton("Split##ecl3",settings.controlLayout==3)) { + settings.controlLayout=3; + } + + ImGui::Text("FM parameter editor layout:"); + if (ImGui::RadioButton("Modern##fml0",settings.fmLayout==0)) { + settings.fmLayout=0; + } + if (ImGui::RadioButton("Compact (2x2, classic)##fml1",settings.fmLayout==1)) { + settings.fmLayout=1; + } + if (ImGui::RadioButton("Compact (1x4)##fml2",settings.fmLayout==2)) { + settings.fmLayout=2; + } + if (ImGui::RadioButton("Compact (4x1)##fml3",settings.fmLayout==3)) { + settings.fmLayout=3; + } + + ImGui::Text("Position of Sustain in FM editor:"); + if (ImGui::RadioButton("Between Decay and Sustain Rate##susp0",settings.susPosition==0)) { + settings.susPosition=0; + } + if (ImGui::RadioButton("After Release Rate##susp1",settings.susPosition==1)) { + settings.susPosition=1; + } + + ImGui::Separator(); + + bool insEditColorizeB=settings.insEditColorize; + if (ImGui::Checkbox("Colorize instrument editor using instrument type",&insEditColorizeB)) { + settings.insEditColorize=insEditColorizeB; + } + + bool separateFMColorsB=settings.separateFMColors; + if (ImGui::Checkbox("Use separate colors for carriers/modulators in FM editor",&separateFMColorsB)) { + settings.separateFMColors=separateFMColorsB; + } + + bool macroViewB=settings.macroView; + if (ImGui::Checkbox("Classic macro view (standard macros only; deprecated!)",¯oViewB)) { + settings.macroView=macroViewB; + } + + bool unifiedDataViewB=settings.unifiedDataView; + if (ImGui::Checkbox("Unified instrument/wavetable/sample list",&unifiedDataViewB)) { + settings.unifiedDataView=unifiedDataViewB; + } + + bool chipNamesB=settings.chipNames; + if (ImGui::Checkbox("Use chip names instead of system names",&chipNamesB)) { + settings.chipNames=chipNamesB; + } + + bool oplStandardWaveNamesB=settings.oplStandardWaveNames; + if (ImGui::Checkbox("Use standard OPL waveform names",&oplStandardWaveNamesB)) { + settings.oplStandardWaveNames=oplStandardWaveNamesB; + } + + if (nonLatchNibble) { + bool hiddenSystemsB=settings.hiddenSystems; + if (ImGui::Checkbox(":smile: :star_struck: :sunglasses: :ok_hand:",&hiddenSystemsB)) { + settings.hiddenSystems=hiddenSystemsB; + } + } + + bool overflowHighlightB=settings.overflowHighlight; + if (ImGui::Checkbox("Overflow pattern highlights",&overflowHighlightB)) { + settings.overflowHighlight=overflowHighlightB; + } + + bool viewPrevPatternB=settings.viewPrevPattern; + if (ImGui::Checkbox("Display previous/next pattern",&viewPrevPatternB)) { + settings.viewPrevPattern=viewPrevPatternB; + } + + bool germanNotationB=settings.germanNotation; + if (ImGui::Checkbox("Use German notation",&germanNotationB)) { + settings.germanNotation=germanNotationB; + } + + // sorry. temporarily disabled until ImGui has a way to add separators in tables arbitrarily. + /*bool sysSeparatorsB=settings.sysSeparators; + if (ImGui::Checkbox("Add separators between systems in Orders",&sysSeparatorsB)) { + settings.sysSeparators=sysSeparatorsB; + }*/ + + bool partyTimeB=settings.partyTime; + if (ImGui::Checkbox("About screen party time",&partyTimeB)) { + settings.partyTime=partyTimeB; + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Warning: may cause epileptic seizures."); + } + + ImGui::Separator(); + + bool waveLayoutB=settings.waveLayout; + if (ImGui::Checkbox("Use compact wave editor",&waveLayoutB)) { + settings.waveLayout=waveLayoutB; + } + + bool sampleLayoutB=settings.sampleLayout; + if (ImGui::Checkbox("Use compact sample editor",&sampleLayoutB)) { + settings.sampleLayout=sampleLayoutB; + } + + bool roundedWindowsB=settings.roundedWindows; + if (ImGui::Checkbox("Rounded window corners",&roundedWindowsB)) { + settings.roundedWindows=roundedWindowsB; + } + + bool roundedButtonsB=settings.roundedButtons; + if (ImGui::Checkbox("Rounded buttons",&roundedButtonsB)) { + settings.roundedButtons=roundedButtonsB; + } + + bool roundedMenusB=settings.roundedMenus; + if (ImGui::Checkbox("Rounded menu corners",&roundedMenusB)) { + settings.roundedMenus=roundedMenusB; + } + + bool frameBordersB=settings.frameBorders; + if (ImGui::Checkbox("Borders around widgets",&frameBordersB)) { + settings.frameBorders=frameBordersB; + } + + ImGui::Separator(); + + ImGui::Text("Oscilloscope settings:"); + + bool oscRoundedCornersB=settings.oscRoundedCorners; + if (ImGui::Checkbox("Rounded corners",&oscRoundedCornersB)) { + settings.oscRoundedCorners=oscRoundedCornersB; + } + + bool oscTakesEntireWindowB=settings.oscTakesEntireWindow; + if (ImGui::Checkbox("Fill entire window",&oscTakesEntireWindowB)) { + settings.oscTakesEntireWindow=oscTakesEntireWindowB; + } + + bool oscBorderB=settings.oscBorder; + if (ImGui::Checkbox("Border",&oscBorderB)) { + settings.oscBorder=oscBorderB; + } + + ImGui::Separator(); + + if (ImGui::TreeNode("Color scheme")) { + if (ImGui::Button("Import")) { + openFileDialog(GUI_FILE_IMPORT_COLORS); + } + ImGui::SameLine(); + if (ImGui::Button("Export")) { + openFileDialog(GUI_FILE_EXPORT_COLORS); + } + ImGui::SameLine(); + if (ImGui::Button("Reset defaults")) { + showWarning("Are you sure you want to reset the color scheme?",GUI_WARN_RESET_COLORS); + } + if (ImGui::TreeNode("General")) { + ImGui::Text("Color scheme type:"); + if (ImGui::RadioButton("Dark##gcb0",settings.guiColorsBase==0)) { + settings.guiColorsBase=0; + } + if (ImGui::RadioButton("Light##gcb1",settings.guiColorsBase==1)) { + settings.guiColorsBase=1; + } + UI_COLOR_CONFIG(GUI_COLOR_BACKGROUND,"Background"); + UI_COLOR_CONFIG(GUI_COLOR_FRAME_BACKGROUND,"Window background"); + UI_COLOR_CONFIG(GUI_COLOR_MODAL_BACKDROP,"Modal backdrop"); + UI_COLOR_CONFIG(GUI_COLOR_HEADER,"Header"); + UI_COLOR_CONFIG(GUI_COLOR_TEXT,"Text"); + UI_COLOR_CONFIG(GUI_COLOR_ACCENT_PRIMARY,"Primary"); + UI_COLOR_CONFIG(GUI_COLOR_ACCENT_SECONDARY,"Secondary"); + UI_COLOR_CONFIG(GUI_COLOR_BORDER,"Border"); + UI_COLOR_CONFIG(GUI_COLOR_BORDER_SHADOW,"Border shadow"); + UI_COLOR_CONFIG(GUI_COLOR_TOGGLE_ON,"Toggle on"); + UI_COLOR_CONFIG(GUI_COLOR_TOGGLE_OFF,"Toggle off"); + UI_COLOR_CONFIG(GUI_COLOR_EDITING,"Editing"); + UI_COLOR_CONFIG(GUI_COLOR_SONG_LOOP,"Song loop"); + UI_COLOR_CONFIG(GUI_COLOR_PLAYBACK_STAT,"Playback status"); + ImGui::TreePop(); + } + if (ImGui::TreeNode("File Picker (built-in)")) { + UI_COLOR_CONFIG(GUI_COLOR_FILE_DIR,"Directory"); + UI_COLOR_CONFIG(GUI_COLOR_FILE_SONG_NATIVE,"Song (native)"); + UI_COLOR_CONFIG(GUI_COLOR_FILE_SONG_IMPORT,"Song (import)"); + UI_COLOR_CONFIG(GUI_COLOR_FILE_INSTR,"Instrument"); + UI_COLOR_CONFIG(GUI_COLOR_FILE_AUDIO,"Audio"); + UI_COLOR_CONFIG(GUI_COLOR_FILE_WAVE,"Wavetable"); + UI_COLOR_CONFIG(GUI_COLOR_FILE_VGM,"VGM"); + UI_COLOR_CONFIG(GUI_COLOR_FILE_FONT,"Font"); + UI_COLOR_CONFIG(GUI_COLOR_FILE_OTHER,"Other"); + ImGui::TreePop(); + } + if (ImGui::TreeNode("Oscilloscope")) { + UI_COLOR_CONFIG(GUI_COLOR_OSC_BORDER,"Border"); + UI_COLOR_CONFIG(GUI_COLOR_OSC_BG1,"Background (top-left)"); + UI_COLOR_CONFIG(GUI_COLOR_OSC_BG2,"Background (top-right)"); + UI_COLOR_CONFIG(GUI_COLOR_OSC_BG3,"Background (bottom-left)"); + UI_COLOR_CONFIG(GUI_COLOR_OSC_BG4,"Background (bottom-right)"); + UI_COLOR_CONFIG(GUI_COLOR_OSC_WAVE,"Waveform"); + UI_COLOR_CONFIG(GUI_COLOR_OSC_WAVE_PEAK,"Waveform (clip)"); + UI_COLOR_CONFIG(GUI_COLOR_OSC_REF,"Reference"); + UI_COLOR_CONFIG(GUI_COLOR_OSC_GUIDE,"Guide"); + ImGui::TreePop(); + } + if (ImGui::TreeNode("Volume Meter")) { + UI_COLOR_CONFIG(GUI_COLOR_VOLMETER_LOW,"Low"); + UI_COLOR_CONFIG(GUI_COLOR_VOLMETER_HIGH,"High"); + UI_COLOR_CONFIG(GUI_COLOR_VOLMETER_PEAK,"Clip"); + ImGui::TreePop(); + } + if (ImGui::TreeNode("Orders")) { + UI_COLOR_CONFIG(GUI_COLOR_ORDER_ROW_INDEX,"Order number"); + UI_COLOR_CONFIG(GUI_COLOR_ORDER_ACTIVE,"Current order background"); + UI_COLOR_CONFIG(GUI_COLOR_ORDER_SIMILAR,"Similar patterns"); + UI_COLOR_CONFIG(GUI_COLOR_ORDER_INACTIVE,"Inactive patterns"); + ImGui::TreePop(); + } + if (ImGui::TreeNode("Envelope View")) { + UI_COLOR_CONFIG(GUI_COLOR_FM_ENVELOPE,"Envelope"); + UI_COLOR_CONFIG(GUI_COLOR_FM_ENVELOPE_SUS_GUIDE,"Sustain guide"); + UI_COLOR_CONFIG(GUI_COLOR_FM_ENVELOPE_RELEASE,"Release"); + + ImGui::TreePop(); + } + if (ImGui::TreeNode("FM Editor")) { + UI_COLOR_CONFIG(GUI_COLOR_FM_ALG_BG,"Algorithm background"); + UI_COLOR_CONFIG(GUI_COLOR_FM_ALG_LINE,"Algorithm lines"); + UI_COLOR_CONFIG(GUI_COLOR_FM_MOD,"Modulator"); + UI_COLOR_CONFIG(GUI_COLOR_FM_CAR,"Carrier"); + + UI_COLOR_CONFIG(GUI_COLOR_FM_SSG,"SSG-EG"); + UI_COLOR_CONFIG(GUI_COLOR_FM_WAVE,"Waveform"); + + ImGui::TextWrapped("(the following colors only apply when \"Use separate colors for carriers/modulators in FM editor\" is on!)"); + + UI_COLOR_CONFIG(GUI_COLOR_FM_PRIMARY_MOD,"Mod. accent (primary)"); + UI_COLOR_CONFIG(GUI_COLOR_FM_SECONDARY_MOD,"Mod. accent (secondary)"); + UI_COLOR_CONFIG(GUI_COLOR_FM_BORDER_MOD,"Mod. border"); + UI_COLOR_CONFIG(GUI_COLOR_FM_BORDER_SHADOW_MOD,"Mod. border shadow"); + + UI_COLOR_CONFIG(GUI_COLOR_FM_PRIMARY_CAR,"Car. accent (primary"); + UI_COLOR_CONFIG(GUI_COLOR_FM_SECONDARY_CAR,"Car. accent (secondary)"); + UI_COLOR_CONFIG(GUI_COLOR_FM_BORDER_CAR,"Car. border"); + UI_COLOR_CONFIG(GUI_COLOR_FM_BORDER_SHADOW_CAR,"Car. border shadow"); + + ImGui::TreePop(); + } + if (ImGui::TreeNode("Macro Editor")) { + UI_COLOR_CONFIG(GUI_COLOR_MACRO_VOLUME,"Volume"); + UI_COLOR_CONFIG(GUI_COLOR_MACRO_PITCH,"Pitch"); + UI_COLOR_CONFIG(GUI_COLOR_MACRO_WAVE,"Wave"); + UI_COLOR_CONFIG(GUI_COLOR_MACRO_OTHER,"Other"); + ImGui::TreePop(); + } + if (ImGui::TreeNode("Instrument Types")) { + UI_COLOR_CONFIG(GUI_COLOR_INSTR_FM,"FM (4-operator)"); + UI_COLOR_CONFIG(GUI_COLOR_INSTR_STD,"Standard"); + UI_COLOR_CONFIG(GUI_COLOR_INSTR_GB,"Game Boy"); + UI_COLOR_CONFIG(GUI_COLOR_INSTR_C64,"C64"); + UI_COLOR_CONFIG(GUI_COLOR_INSTR_AMIGA,"Amiga/Sample"); + UI_COLOR_CONFIG(GUI_COLOR_INSTR_PCE,"PC Engine"); + UI_COLOR_CONFIG(GUI_COLOR_INSTR_AY,"AY-3-8910/SSG"); + UI_COLOR_CONFIG(GUI_COLOR_INSTR_AY8930,"AY8930"); + UI_COLOR_CONFIG(GUI_COLOR_INSTR_TIA,"TIA"); + UI_COLOR_CONFIG(GUI_COLOR_INSTR_SAA1099,"SAA1099"); + UI_COLOR_CONFIG(GUI_COLOR_INSTR_VIC,"VIC"); + UI_COLOR_CONFIG(GUI_COLOR_INSTR_PET,"PET"); + UI_COLOR_CONFIG(GUI_COLOR_INSTR_VRC6,"VRC6"); + UI_COLOR_CONFIG(GUI_COLOR_INSTR_VRC6_SAW,"VRC6 (saw)"); + UI_COLOR_CONFIG(GUI_COLOR_INSTR_OPLL,"FM (OPLL)"); + UI_COLOR_CONFIG(GUI_COLOR_INSTR_OPL,"FM (OPL)"); + UI_COLOR_CONFIG(GUI_COLOR_INSTR_FDS,"FDS"); + UI_COLOR_CONFIG(GUI_COLOR_INSTR_VBOY,"Virtual Boy"); + UI_COLOR_CONFIG(GUI_COLOR_INSTR_N163,"Namco 163"); + UI_COLOR_CONFIG(GUI_COLOR_INSTR_SCC,"Konami SCC"); + UI_COLOR_CONFIG(GUI_COLOR_INSTR_OPZ,"FM (OPZ)"); + UI_COLOR_CONFIG(GUI_COLOR_INSTR_POKEY,"POKEY"); + UI_COLOR_CONFIG(GUI_COLOR_INSTR_BEEPER,"PC Beeper"); + UI_COLOR_CONFIG(GUI_COLOR_INSTR_SWAN,"WonderSwan"); + UI_COLOR_CONFIG(GUI_COLOR_INSTR_MIKEY,"Lynx"); + UI_COLOR_CONFIG(GUI_COLOR_INSTR_VERA,"VERA"); + UI_COLOR_CONFIG(GUI_COLOR_INSTR_X1_010,"X1-010"); + UI_COLOR_CONFIG(GUI_COLOR_INSTR_ES5506,"ES5506"); + UI_COLOR_CONFIG(GUI_COLOR_INSTR_MULTIPCM,"MultiPCM"); + UI_COLOR_CONFIG(GUI_COLOR_INSTR_SNES,"SNES"); + UI_COLOR_CONFIG(GUI_COLOR_INSTR_SU,"Sound Unit"); + UI_COLOR_CONFIG(GUI_COLOR_INSTR_UNKNOWN,"Other/Unknown"); + ImGui::TreePop(); + } + if (ImGui::TreeNode("Channel")) { + UI_COLOR_CONFIG(GUI_COLOR_CHANNEL_FM,"FM"); + UI_COLOR_CONFIG(GUI_COLOR_CHANNEL_PULSE,"Pulse"); + UI_COLOR_CONFIG(GUI_COLOR_CHANNEL_NOISE,"Noise"); + UI_COLOR_CONFIG(GUI_COLOR_CHANNEL_PCM,"PCM"); + UI_COLOR_CONFIG(GUI_COLOR_CHANNEL_WAVE,"Wave"); + UI_COLOR_CONFIG(GUI_COLOR_CHANNEL_OP,"FM operator"); + UI_COLOR_CONFIG(GUI_COLOR_CHANNEL_MUTED,"Muted"); + ImGui::TreePop(); + } + if (ImGui::TreeNode("Pattern")) { + UI_COLOR_CONFIG(GUI_COLOR_PATTERN_PLAY_HEAD,"Playhead"); + UI_COLOR_CONFIG(GUI_COLOR_PATTERN_CURSOR,"Cursor"); + UI_COLOR_CONFIG(GUI_COLOR_PATTERN_CURSOR_HOVER,"Cursor (hovered)"); + UI_COLOR_CONFIG(GUI_COLOR_PATTERN_CURSOR_ACTIVE,"Cursor (clicked)"); + UI_COLOR_CONFIG(GUI_COLOR_PATTERN_SELECTION,"Selection"); + UI_COLOR_CONFIG(GUI_COLOR_PATTERN_SELECTION_HOVER,"Selection (hovered)"); + UI_COLOR_CONFIG(GUI_COLOR_PATTERN_SELECTION_ACTIVE,"Selection (clicked)"); + UI_COLOR_CONFIG(GUI_COLOR_PATTERN_HI_1,"Highlight 1"); + UI_COLOR_CONFIG(GUI_COLOR_PATTERN_HI_2,"Highlight 2"); + UI_COLOR_CONFIG(GUI_COLOR_PATTERN_ROW_INDEX,"Row number"); + UI_COLOR_CONFIG(GUI_COLOR_PATTERN_ROW_INDEX_HI1,"Row number (highlight 1)"); + UI_COLOR_CONFIG(GUI_COLOR_PATTERN_ROW_INDEX_HI2,"Row number (highlight 2)"); + UI_COLOR_CONFIG(GUI_COLOR_PATTERN_ACTIVE,"Note"); + UI_COLOR_CONFIG(GUI_COLOR_PATTERN_ACTIVE_HI1,"Note (highlight 1)"); + UI_COLOR_CONFIG(GUI_COLOR_PATTERN_ACTIVE_HI2,"Note (highlight 2)"); + UI_COLOR_CONFIG(GUI_COLOR_PATTERN_INACTIVE,"Blank"); + UI_COLOR_CONFIG(GUI_COLOR_PATTERN_INACTIVE_HI1,"Blank (highlight 1)"); + UI_COLOR_CONFIG(GUI_COLOR_PATTERN_INACTIVE_HI2,"Blank (highlight 2)"); + UI_COLOR_CONFIG(GUI_COLOR_PATTERN_INS,"Instrument"); + UI_COLOR_CONFIG(GUI_COLOR_PATTERN_INS_WARN,"Instrument (invalid type)"); + UI_COLOR_CONFIG(GUI_COLOR_PATTERN_INS_ERROR,"Instrument (out of range)"); + UI_COLOR_CONFIG(GUI_COLOR_PATTERN_VOLUME_MIN,"Volume (0%)"); + UI_COLOR_CONFIG(GUI_COLOR_PATTERN_VOLUME_HALF,"Volume (50%)"); + UI_COLOR_CONFIG(GUI_COLOR_PATTERN_VOLUME_MAX,"Volume (100%)"); + UI_COLOR_CONFIG(GUI_COLOR_PATTERN_EFFECT_INVALID,"Invalid effect"); + UI_COLOR_CONFIG(GUI_COLOR_PATTERN_EFFECT_PITCH,"Pitch effect"); + UI_COLOR_CONFIG(GUI_COLOR_PATTERN_EFFECT_VOLUME,"Volume effect"); + UI_COLOR_CONFIG(GUI_COLOR_PATTERN_EFFECT_PANNING,"Panning effect"); + UI_COLOR_CONFIG(GUI_COLOR_PATTERN_EFFECT_SONG,"Song effect"); + UI_COLOR_CONFIG(GUI_COLOR_PATTERN_EFFECT_TIME,"Time effect"); + UI_COLOR_CONFIG(GUI_COLOR_PATTERN_EFFECT_SPEED,"Speed effect"); + UI_COLOR_CONFIG(GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY,"Primary system effect"); + UI_COLOR_CONFIG(GUI_COLOR_PATTERN_EFFECT_SYS_SECONDARY,"Secondary system effect"); + UI_COLOR_CONFIG(GUI_COLOR_PATTERN_EFFECT_MISC,"Miscellaneous"); + UI_COLOR_CONFIG(GUI_COLOR_EE_VALUE,"External command output"); + ImGui::TreePop(); + } + if (ImGui::TreeNode("Log Viewer")) { + UI_COLOR_CONFIG(GUI_COLOR_LOGLEVEL_ERROR,"Log level: Error"); + UI_COLOR_CONFIG(GUI_COLOR_LOGLEVEL_WARNING,"Log level: Warning"); + UI_COLOR_CONFIG(GUI_COLOR_LOGLEVEL_INFO,"Log level: Info"); + UI_COLOR_CONFIG(GUI_COLOR_LOGLEVEL_DEBUG,"Log level: Debug"); + UI_COLOR_CONFIG(GUI_COLOR_LOGLEVEL_TRACE,"Log level: Trace/Verbose"); + ImGui::TreePop(); + } ImGui::TreePop(); } - if (ImGui::TreeNode("Macro Editor")) { - UI_COLOR_CONFIG(GUI_COLOR_MACRO_VOLUME,"Volume"); - UI_COLOR_CONFIG(GUI_COLOR_MACRO_PITCH,"Pitch"); - UI_COLOR_CONFIG(GUI_COLOR_MACRO_WAVE,"Wave"); - UI_COLOR_CONFIG(GUI_COLOR_MACRO_OTHER,"Other"); - ImGui::TreePop(); - } - if (ImGui::TreeNode("Instrument Types")) { - UI_COLOR_CONFIG(GUI_COLOR_INSTR_FM,"FM (4-operator)"); - UI_COLOR_CONFIG(GUI_COLOR_INSTR_STD,"Standard"); - UI_COLOR_CONFIG(GUI_COLOR_INSTR_GB,"Game Boy"); - UI_COLOR_CONFIG(GUI_COLOR_INSTR_C64,"C64"); - UI_COLOR_CONFIG(GUI_COLOR_INSTR_AMIGA,"Amiga/Sample"); - UI_COLOR_CONFIG(GUI_COLOR_INSTR_PCE,"PC Engine"); - UI_COLOR_CONFIG(GUI_COLOR_INSTR_AY,"AY-3-8910/SSG"); - UI_COLOR_CONFIG(GUI_COLOR_INSTR_AY8930,"AY8930"); - UI_COLOR_CONFIG(GUI_COLOR_INSTR_TIA,"TIA"); - UI_COLOR_CONFIG(GUI_COLOR_INSTR_SAA1099,"SAA1099"); - UI_COLOR_CONFIG(GUI_COLOR_INSTR_VIC,"VIC"); - UI_COLOR_CONFIG(GUI_COLOR_INSTR_PET,"PET"); - UI_COLOR_CONFIG(GUI_COLOR_INSTR_VRC6,"VRC6"); - UI_COLOR_CONFIG(GUI_COLOR_INSTR_VRC6_SAW,"VRC6 (saw)"); - UI_COLOR_CONFIG(GUI_COLOR_INSTR_OPLL,"FM (OPLL)"); - UI_COLOR_CONFIG(GUI_COLOR_INSTR_OPL,"FM (OPL)"); - UI_COLOR_CONFIG(GUI_COLOR_INSTR_FDS,"FDS"); - UI_COLOR_CONFIG(GUI_COLOR_INSTR_VBOY,"Virtual Boy"); - UI_COLOR_CONFIG(GUI_COLOR_INSTR_N163,"Namco 163"); - UI_COLOR_CONFIG(GUI_COLOR_INSTR_SCC,"Konami SCC"); - UI_COLOR_CONFIG(GUI_COLOR_INSTR_OPZ,"FM (OPZ)"); - UI_COLOR_CONFIG(GUI_COLOR_INSTR_POKEY,"POKEY"); - UI_COLOR_CONFIG(GUI_COLOR_INSTR_BEEPER,"PC Beeper"); - UI_COLOR_CONFIG(GUI_COLOR_INSTR_SWAN,"WonderSwan"); - UI_COLOR_CONFIG(GUI_COLOR_INSTR_MIKEY,"Lynx"); - UI_COLOR_CONFIG(GUI_COLOR_INSTR_VERA,"VERA"); - UI_COLOR_CONFIG(GUI_COLOR_INSTR_X1_010,"X1-010"); - UI_COLOR_CONFIG(GUI_COLOR_INSTR_ES5506,"ES5506"); - UI_COLOR_CONFIG(GUI_COLOR_INSTR_MULTIPCM,"MultiPCM"); - UI_COLOR_CONFIG(GUI_COLOR_INSTR_SNES,"SNES"); - UI_COLOR_CONFIG(GUI_COLOR_INSTR_SU,"Sound Unit"); - UI_COLOR_CONFIG(GUI_COLOR_INSTR_UNKNOWN,"Other/Unknown"); - ImGui::TreePop(); - } - if (ImGui::TreeNode("Channel")) { - UI_COLOR_CONFIG(GUI_COLOR_CHANNEL_FM,"FM"); - UI_COLOR_CONFIG(GUI_COLOR_CHANNEL_PULSE,"Pulse"); - UI_COLOR_CONFIG(GUI_COLOR_CHANNEL_NOISE,"Noise"); - UI_COLOR_CONFIG(GUI_COLOR_CHANNEL_PCM,"PCM"); - UI_COLOR_CONFIG(GUI_COLOR_CHANNEL_WAVE,"Wave"); - UI_COLOR_CONFIG(GUI_COLOR_CHANNEL_OP,"FM operator"); - UI_COLOR_CONFIG(GUI_COLOR_CHANNEL_MUTED,"Muted"); - ImGui::TreePop(); - } - if (ImGui::TreeNode("Pattern")) { - UI_COLOR_CONFIG(GUI_COLOR_PATTERN_PLAY_HEAD,"Playhead"); - UI_COLOR_CONFIG(GUI_COLOR_PATTERN_CURSOR,"Cursor"); - UI_COLOR_CONFIG(GUI_COLOR_PATTERN_CURSOR_HOVER,"Cursor (hovered)"); - UI_COLOR_CONFIG(GUI_COLOR_PATTERN_CURSOR_ACTIVE,"Cursor (clicked)"); - UI_COLOR_CONFIG(GUI_COLOR_PATTERN_SELECTION,"Selection"); - UI_COLOR_CONFIG(GUI_COLOR_PATTERN_SELECTION_HOVER,"Selection (hovered)"); - UI_COLOR_CONFIG(GUI_COLOR_PATTERN_SELECTION_ACTIVE,"Selection (clicked)"); - UI_COLOR_CONFIG(GUI_COLOR_PATTERN_HI_1,"Highlight 1"); - UI_COLOR_CONFIG(GUI_COLOR_PATTERN_HI_2,"Highlight 2"); - UI_COLOR_CONFIG(GUI_COLOR_PATTERN_ROW_INDEX,"Row number"); - UI_COLOR_CONFIG(GUI_COLOR_PATTERN_ROW_INDEX_HI1,"Row number (highlight 1)"); - UI_COLOR_CONFIG(GUI_COLOR_PATTERN_ROW_INDEX_HI2,"Row number (highlight 2)"); - UI_COLOR_CONFIG(GUI_COLOR_PATTERN_ACTIVE,"Note"); - UI_COLOR_CONFIG(GUI_COLOR_PATTERN_ACTIVE_HI1,"Note (highlight 1)"); - UI_COLOR_CONFIG(GUI_COLOR_PATTERN_ACTIVE_HI2,"Note (highlight 2)"); - UI_COLOR_CONFIG(GUI_COLOR_PATTERN_INACTIVE,"Blank"); - UI_COLOR_CONFIG(GUI_COLOR_PATTERN_INACTIVE_HI1,"Blank (highlight 1)"); - UI_COLOR_CONFIG(GUI_COLOR_PATTERN_INACTIVE_HI2,"Blank (highlight 2)"); - UI_COLOR_CONFIG(GUI_COLOR_PATTERN_INS,"Instrument"); - UI_COLOR_CONFIG(GUI_COLOR_PATTERN_INS_WARN,"Instrument (invalid type)"); - UI_COLOR_CONFIG(GUI_COLOR_PATTERN_INS_ERROR,"Instrument (out of range)"); - UI_COLOR_CONFIG(GUI_COLOR_PATTERN_VOLUME_MIN,"Volume (0%)"); - UI_COLOR_CONFIG(GUI_COLOR_PATTERN_VOLUME_HALF,"Volume (50%)"); - UI_COLOR_CONFIG(GUI_COLOR_PATTERN_VOLUME_MAX,"Volume (100%)"); - UI_COLOR_CONFIG(GUI_COLOR_PATTERN_EFFECT_INVALID,"Invalid effect"); - UI_COLOR_CONFIG(GUI_COLOR_PATTERN_EFFECT_PITCH,"Pitch effect"); - UI_COLOR_CONFIG(GUI_COLOR_PATTERN_EFFECT_VOLUME,"Volume effect"); - UI_COLOR_CONFIG(GUI_COLOR_PATTERN_EFFECT_PANNING,"Panning effect"); - UI_COLOR_CONFIG(GUI_COLOR_PATTERN_EFFECT_SONG,"Song effect"); - UI_COLOR_CONFIG(GUI_COLOR_PATTERN_EFFECT_TIME,"Time effect"); - UI_COLOR_CONFIG(GUI_COLOR_PATTERN_EFFECT_SPEED,"Speed effect"); - UI_COLOR_CONFIG(GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY,"Primary system effect"); - UI_COLOR_CONFIG(GUI_COLOR_PATTERN_EFFECT_SYS_SECONDARY,"Secondary system effect"); - UI_COLOR_CONFIG(GUI_COLOR_PATTERN_EFFECT_MISC,"Miscellaneous"); - UI_COLOR_CONFIG(GUI_COLOR_EE_VALUE,"External command output"); - ImGui::TreePop(); - } - if (ImGui::TreeNode("Log Viewer")) { - UI_COLOR_CONFIG(GUI_COLOR_LOGLEVEL_ERROR,"Log level: Error"); - UI_COLOR_CONFIG(GUI_COLOR_LOGLEVEL_WARNING,"Log level: Warning"); - UI_COLOR_CONFIG(GUI_COLOR_LOGLEVEL_INFO,"Log level: Info"); - UI_COLOR_CONFIG(GUI_COLOR_LOGLEVEL_DEBUG,"Log level: Debug"); - UI_COLOR_CONFIG(GUI_COLOR_LOGLEVEL_TRACE,"Log level: Trace/Verbose"); - ImGui::TreePop(); - } - ImGui::TreePop(); } - + ImGui::EndChild(); ImGui::EndTabItem(); } if (ImGui::BeginTabItem("Keyboard")) { - if (ImGui::Button("Import")) { - openFileDialog(GUI_FILE_IMPORT_KEYBINDS); - } - ImGui::SameLine(); - if (ImGui::Button("Export")) { - openFileDialog(GUI_FILE_EXPORT_KEYBINDS); - } - ImGui::SameLine(); - if (ImGui::Button("Reset defaults")) { - showWarning("Are you sure you want to reset the keyboard settings?",GUI_WARN_RESET_KEYBINDS); - } - if (ImGui::TreeNode("Global hotkeys")) { - KEYBIND_CONFIG_BEGIN("keysGlobal"); - - UI_KEYBIND_CONFIG(GUI_ACTION_OPEN); - UI_KEYBIND_CONFIG(GUI_ACTION_OPEN_BACKUP); - UI_KEYBIND_CONFIG(GUI_ACTION_SAVE); - UI_KEYBIND_CONFIG(GUI_ACTION_SAVE_AS); - UI_KEYBIND_CONFIG(GUI_ACTION_UNDO); - UI_KEYBIND_CONFIG(GUI_ACTION_REDO); - UI_KEYBIND_CONFIG(GUI_ACTION_PLAY_TOGGLE); - UI_KEYBIND_CONFIG(GUI_ACTION_PLAY); - UI_KEYBIND_CONFIG(GUI_ACTION_STOP); - UI_KEYBIND_CONFIG(GUI_ACTION_PLAY_REPEAT); - UI_KEYBIND_CONFIG(GUI_ACTION_PLAY_CURSOR); - UI_KEYBIND_CONFIG(GUI_ACTION_STEP_ONE); - UI_KEYBIND_CONFIG(GUI_ACTION_OCTAVE_UP); - UI_KEYBIND_CONFIG(GUI_ACTION_OCTAVE_DOWN); - UI_KEYBIND_CONFIG(GUI_ACTION_INS_UP); - UI_KEYBIND_CONFIG(GUI_ACTION_INS_DOWN); - UI_KEYBIND_CONFIG(GUI_ACTION_STEP_UP); - UI_KEYBIND_CONFIG(GUI_ACTION_STEP_DOWN); - UI_KEYBIND_CONFIG(GUI_ACTION_TOGGLE_EDIT); - UI_KEYBIND_CONFIG(GUI_ACTION_METRONOME); - UI_KEYBIND_CONFIG(GUI_ACTION_REPEAT_PATTERN); - UI_KEYBIND_CONFIG(GUI_ACTION_FOLLOW_ORDERS); - UI_KEYBIND_CONFIG(GUI_ACTION_FOLLOW_PATTERN); - UI_KEYBIND_CONFIG(GUI_ACTION_FULLSCREEN); - UI_KEYBIND_CONFIG(GUI_ACTION_PANIC); - - KEYBIND_CONFIG_END; - ImGui::TreePop(); - } - if (ImGui::TreeNode("Window activation")) { - KEYBIND_CONFIG_BEGIN("keysWindow"); - - UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_EDIT_CONTROLS); - UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_ORDERS); - UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_INS_LIST); - UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_INS_EDIT); - UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_SONG_INFO); - UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_PATTERN); - UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_WAVE_LIST); - UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_WAVE_EDIT); - UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_SAMPLE_LIST); - UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_SAMPLE_EDIT); - UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_ABOUT); - UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_SETTINGS); - UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_MIXER); - UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_DEBUG); - UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_OSCILLOSCOPE); - UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_CHAN_OSC); - UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_VOL_METER); - UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_STATS); - UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_COMPAT_FLAGS); - UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_PIANO); - UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_NOTES); - UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_CHANNELS); - UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_REGISTER_VIEW); - UI_KEYBIND_CONFIG(GUI_ACTION_WINDOW_LOG); - - UI_KEYBIND_CONFIG(GUI_ACTION_COLLAPSE_WINDOW); - UI_KEYBIND_CONFIG(GUI_ACTION_CLOSE_WINDOW); - - KEYBIND_CONFIG_END; - ImGui::TreePop(); - } - if (ImGui::TreeNode("Note input")) { - std::vector sorted; - if (ImGui::BeginTable("keysNoteInput",4)) { - for (std::map::value_type& i: noteKeys) { - std::vector::iterator j; - for (j=sorted.begin(); j!=sorted.end(); j++) { - if (j->val>i.second) { - break; - } - } - sorted.insert(j,MappedInput(i.first,i.second)); - } - - static char id[4096]; - - ImGui::TableNextRow(ImGuiTableRowFlags_Headers); - ImGui::TableNextColumn(); - ImGui::Text("Key"); - ImGui::TableNextColumn(); - ImGui::Text("Type"); - ImGui::TableNextColumn(); - ImGui::Text("Value"); - ImGui::TableNextColumn(); - ImGui::Text("Remove"); - - for (MappedInput& i: sorted) { - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - ImGui::Text("%s",SDL_GetScancodeName((SDL_Scancode)i.scan)); - ImGui::TableNextColumn(); - if (i.val==102) { - snprintf(id,4095,"Envelope release##SNType_%d",i.scan); - if (ImGui::Button(id)) { - noteKeys[i.scan]=0; - } - } else if (i.val==101) { - snprintf(id,4095,"Note release##SNType_%d",i.scan); - if (ImGui::Button(id)) { - noteKeys[i.scan]=102; - } - } else if (i.val==100) { - snprintf(id,4095,"Note off##SNType_%d",i.scan); - if (ImGui::Button(id)) { - noteKeys[i.scan]=101; - } - } else { - snprintf(id,4095,"Note##SNType_%d",i.scan); - if (ImGui::Button(id)) { - noteKeys[i.scan]=100; - } - } - ImGui::TableNextColumn(); - if (i.val<100) { - snprintf(id,4095,"##SNValue_%d",i.scan); - if (ImGui::InputInt(id,&i.val,1,1)) { - if (i.val<0) i.val=0; - if (i.val>96) i.val=96; - noteKeys[i.scan]=i.val; - } - } - ImGui::TableNextColumn(); - snprintf(id,4095,ICON_FA_TIMES "##SNRemove_%d",i.scan); - if (ImGui::Button(id)) { - noteKeys.erase(i.scan); - } - } - ImGui::EndTable(); - - if (ImGui::BeginCombo("##SNAddNew","Add...")) { - for (int i=0; i sorted; + if (ImGui::BeginTable("keysNoteInput",4)) { + for (std::map::value_type& i: noteKeys) { + std::vector::iterator j; + for (j=sorted.begin(); j!=sorted.end(); j++) { + if (j->val>i.second) { + break; + } + } + sorted.insert(j,MappedInput(i.first,i.second)); + } + + static char id[4096]; + + ImGui::TableNextRow(ImGuiTableRowFlags_Headers); + ImGui::TableNextColumn(); + ImGui::Text("Key"); + ImGui::TableNextColumn(); + ImGui::Text("Type"); + ImGui::TableNextColumn(); + ImGui::Text("Value"); + ImGui::TableNextColumn(); + ImGui::Text("Remove"); + + for (MappedInput& i: sorted) { + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::Text("%s",SDL_GetScancodeName((SDL_Scancode)i.scan)); + ImGui::TableNextColumn(); + if (i.val==102) { + snprintf(id,4095,"Envelope release##SNType_%d",i.scan); + if (ImGui::Button(id)) { + noteKeys[i.scan]=0; + } + } else if (i.val==101) { + snprintf(id,4095,"Note release##SNType_%d",i.scan); + if (ImGui::Button(id)) { + noteKeys[i.scan]=102; + } + } else if (i.val==100) { + snprintf(id,4095,"Note off##SNType_%d",i.scan); + if (ImGui::Button(id)) { + noteKeys[i.scan]=101; + } + } else { + snprintf(id,4095,"Note##SNType_%d",i.scan); + if (ImGui::Button(id)) { + noteKeys[i.scan]=100; + } + } + ImGui::TableNextColumn(); + if (i.val<100) { + snprintf(id,4095,"##SNValue_%d",i.scan); + if (ImGui::InputInt(id,&i.val,1,1)) { + if (i.val<0) i.val=0; + if (i.val>96) i.val=96; + noteKeys[i.scan]=i.val; + } + } + ImGui::TableNextColumn(); + snprintf(id,4095,ICON_FA_TIMES "##SNRemove_%d",i.scan); + if (ImGui::Button(id)) { + noteKeys.erase(i.scan); + } + } + ImGui::EndTable(); + + if (ImGui::BeginCombo("##SNAddNew","Add...")) { + for (int i=0; i Date: Tue, 3 May 2022 02:29:12 -0500 Subject: [PATCH 307/342] GUI: add audio load meter to statistics --- TODO.md | 1 - src/engine/engine.h | 5 ++++- src/engine/playback.cpp | 7 +++++++ src/gui/stats.cpp | 8 ++++++++ 4 files changed, 19 insertions(+), 2 deletions(-) diff --git a/TODO.md b/TODO.md index 26efc81db..4e352566f 100644 --- a/TODO.md +++ b/TODO.md @@ -28,6 +28,5 @@ - store edit/followOrders/followPattern state in config - add ability to select a column by double clicking - add ability to move selection by dragging -- settings: OK/Cancel buttons should be always visible - Apply button in settings - find and replace \ No newline at end of file diff --git a/src/engine/engine.h b/src/engine/engine.h index 809df944d..572fd90bc 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -26,6 +26,7 @@ #include "safeWriter.h" #include "../audio/taAudio.h" #include "blip_buf.h" +#include #include #include #include @@ -416,6 +417,7 @@ class DivEngine { float oscSize; int oscReadPos, oscWritePos; int tickMult; + std::atomic processTime; void runExportThread(); void nextBuf(float** in, float** out, int inChans, int outChans, unsigned int size); @@ -923,7 +925,8 @@ class DivEngine { oscSize(1), oscReadPos(0), oscWritePos(0), - tickMult(1) { + tickMult(1), + processTime(0) { memset(isMuted,0,DIV_MAX_CHANS*sizeof(bool)); memset(keyHit,0,DIV_MAX_CHANS*sizeof(bool)); memset(dispatchChanOfChan,0,DIV_MAX_CHANS*sizeof(int)); diff --git a/src/engine/playback.cpp b/src/engine/playback.cpp index 0010d5a99..5572fea05 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -17,6 +17,7 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#include #define _USE_MATH_DEFINES #include "dispatch.h" #include "engine.h" @@ -1060,6 +1061,8 @@ void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsi } got.bufsize=size; + std::chrono::steady_clock::time_point ts_processBegin=std::chrono::steady_clock::now(); + // process MIDI events (TODO: everything) if (output) if (output->midiIn) while (!output->midiIn->queue.empty()) { TAMidiMessage& msg=output->midiIn->queue.front(); @@ -1338,4 +1341,8 @@ void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsi } } isBusy.unlock(); + + std::chrono::steady_clock::time_point ts_processEnd=std::chrono::steady_clock::now(); + + processTime=std::chrono::duration_cast(ts_processEnd-ts_processBegin).count(); } diff --git a/src/gui/stats.cpp b/src/gui/stats.cpp index 35d6cfc49..9e1790c9e 100644 --- a/src/gui/stats.cpp +++ b/src/gui/stats.cpp @@ -19,6 +19,7 @@ #include "gui.h" #include +#include void FurnaceGUI::drawStats() { if (nextWindow==GUI_WINDOW_STATS) { @@ -28,6 +29,13 @@ void FurnaceGUI::drawStats() { } if (!statsOpen) return; if (ImGui::Begin("Statistics",&statsOpen)) { + 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::Text("Audio load"); + ImGui::SameLine(); + ImGui::ProgressBar((double)lastProcTime/maxGot,ImVec2(-FLT_MIN,0),procStr.c_str()); + ImGui::Separator(); for (int i=0; isong.systemLen; i++) { DivDispatch* dispatch=e->getDispatch(i); for (int j=0; dispatch!=NULL && dispatch->getSampleMemCapacity(j)>0; j++) { From 4c9b172b50a240efc37aec8bdd4a59972fbf10c4 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Tue, 3 May 2022 02:52:38 -0500 Subject: [PATCH 308/342] YM2610: optimize oscilloscope fetch CPU usage --- src/engine/platform/sound/ymfm/ymfm_adpcm.cpp | 24 ++++++++++++------- src/engine/platform/sound/ymfm/ymfm_adpcm.h | 15 ++++++++++-- src/engine/platform/ym2610.cpp | 21 +++++++++------- src/engine/platform/ym2610b.cpp | 19 ++++++++------- 4 files changed, 52 insertions(+), 27 deletions(-) diff --git a/src/engine/platform/sound/ymfm/ymfm_adpcm.cpp b/src/engine/platform/sound/ymfm/ymfm_adpcm.cpp index 01f35367c..76839e787 100644 --- a/src/engine/platform/sound/ymfm/ymfm_adpcm.cpp +++ b/src/engine/platform/sound/ymfm/ymfm_adpcm.cpp @@ -220,7 +220,7 @@ bool adpcm_a_channel::clock() //------------------------------------------------- template -void adpcm_a_channel::output(ymfm_output &output) const +void adpcm_a_channel::output(ymfm_output &output) { // volume combines instrument and total levels int vol = (m_regs.ch_instrument_level(m_choffs) ^ 0x1f) + (m_regs.total_level() ^ 0x3f); @@ -239,14 +239,18 @@ void adpcm_a_channel::output(ymfm_output &output) const int16_t value = ((int16_t(m_accumulator << 4) * mul) >> shift) & ~3; // apply to left/right as appropriate - if (NumOutputs == 1 || m_regs.ch_pan_left(m_choffs)) + if (NumOutputs == 1 || m_regs.ch_pan_left(m_choffs)) { output.data[0] += value; - if (NumOutputs > 1 && m_regs.ch_pan_right(m_choffs)) + m_lastOut[0] = value; + } + if (NumOutputs > 1 && m_regs.ch_pan_right(m_choffs)) { output.data[1] += value; + m_lastOut[1] = value; + } } -template void adpcm_a_channel::output<1>(ymfm_output<1> &output) const; -template void adpcm_a_channel::output<2>(ymfm_output<2> &output) const; +template void adpcm_a_channel::output<1>(ymfm_output<1> &output); +template void adpcm_a_channel::output<2>(ymfm_output<2> &output); //********************************************************* @@ -528,7 +532,7 @@ void adpcm_b_channel::clock() //------------------------------------------------- template -void adpcm_b_channel::output(ymfm_output &output, uint32_t rshift) const +void adpcm_b_channel::output(ymfm_output &output, uint32_t rshift) { // mask out some channels for debug purposes if ((debug::GLOBAL_ADPCM_B_CHANNEL_MASK & 1) == 0) @@ -541,10 +545,14 @@ void adpcm_b_channel::output(ymfm_output &output, uint32_t rshift) c result = (result * int32_t(m_regs.level())) >> (8 + rshift); // apply to left/right - if (NumOutputs == 1 || m_regs.pan_left()) + if (NumOutputs == 1 || m_regs.pan_left()) { + m_lastOut[0] = result; output.data[0] += result; - if (NumOutputs > 1 && m_regs.pan_right()) + } + if (NumOutputs > 1 && m_regs.pan_right()) { + m_lastOut[1] = result; output.data[1] += result; + } } diff --git a/src/engine/platform/sound/ymfm/ymfm_adpcm.h b/src/engine/platform/sound/ymfm/ymfm_adpcm.h index c08eeae59..58bee8a22 100644 --- a/src/engine/platform/sound/ymfm/ymfm_adpcm.h +++ b/src/engine/platform/sound/ymfm/ymfm_adpcm.h @@ -146,7 +146,10 @@ public: // return the computed output value, with panning applied template - void output(ymfm_output &output) const; + void output(ymfm_output &output); + + // return the last output + int32_t get_last_out(int ch) { return m_lastOut[ch]; } private: // internal state @@ -158,6 +161,7 @@ private: uint32_t m_curaddress; // current address int32_t m_accumulator; // accumulator int32_t m_step_index; // index in the stepping table + int32_t m_lastOut[2]; // last output adpcm_a_registers &m_regs; // reference to registers adpcm_a_engine &m_owner; // reference to our owner }; @@ -326,7 +330,7 @@ public: // return the computed output value, with panning applied template - void output(ymfm_output &output, uint32_t rshift) const; + void output(ymfm_output &output, uint32_t rshift); // return the status register uint8_t status() const { return m_status; } @@ -337,6 +341,9 @@ public: // handle special register writes void write(uint32_t regnum, uint8_t value); + // return the last output + int32_t get_last_out(int ch) { return m_lastOut[ch]; } + private: // helper - return the current address shift uint32_t address_shift() const; @@ -361,6 +368,7 @@ private: int32_t m_accumulator; // accumulator int32_t m_prev_accum; // previous accumulator (for linear interp) int32_t m_adpcm_step; // next forecast + int32_t m_lastOut[2]; // last output adpcm_b_registers &m_regs; // reference to registers adpcm_b_engine &m_owner; // reference to our owner }; @@ -387,6 +395,9 @@ public: template void output(ymfm_output &output, uint32_t rshift); + // get last output + int32_t get_last_out(int ch) { return m_channel->get_last_out(ch); } + // read from the ADPCM-B registers uint32_t read(uint32_t regnum) { return m_channel->read(regnum); } diff --git a/src/engine/platform/ym2610.cpp b/src/engine/platform/ym2610.cpp index 3711515aa..a45266086 100644 --- a/src/engine/platform/ym2610.cpp +++ b/src/engine/platform/ym2610.cpp @@ -428,7 +428,15 @@ void DivPlatformYM2610::acquire(short* bufL, short* bufR, size_t start, size_t l ymfm::adpcm_b_engine* abe=fm->debug_adpcm_b_engine(); ymfm::ssg_engine::output_data ssgOut; - ymfm::ymfm_output<2> adpcmOut; + + ymfm::fm_channel>* fmChan[6]; + ymfm::adpcm_a_channel* adpcmAChan[6]; + for (int i=0; i<4; i++) { + fmChan[i]=fme->debug_channel(bchOffs[i]); + } + for (int i=0; i<6; i++) { + adpcmAChan[i]=aae->debug_channel(i); + } for (size_t h=start; hdata[oscBuf[i]->needle++]=(fme->debug_channel(ch)->debug_output(0)+fme->debug_channel(ch)->debug_output(1)); + oscBuf[i]->data[oscBuf[i]->needle++]=(fmChan[i]->debug_output(0)+fmChan[i]->debug_output(1)); } ssge->get_last_out(ssgOut); @@ -467,14 +474,10 @@ void DivPlatformYM2610::acquire(short* bufL, short* bufR, size_t start, size_t l } for (int i=7; i<13; i++) { - adpcmOut.clear(); - aae->debug_channel(i-7)->output<2>(adpcmOut); - oscBuf[i]->data[oscBuf[i]->needle++]=adpcmOut.data[0]+adpcmOut.data[1]; + oscBuf[i]->data[oscBuf[i]->needle++]=adpcmAChan[i-7]->get_last_out(0)+adpcmAChan[i-7]->get_last_out(1); } - adpcmOut.clear(); - abe->output(adpcmOut,1); - oscBuf[13]->data[oscBuf[13]->needle++]=adpcmOut.data[0]+adpcmOut.data[1]; + oscBuf[13]->data[oscBuf[13]->needle++]=abe->get_last_out(0)+abe->get_last_out(1); } } diff --git a/src/engine/platform/ym2610b.cpp b/src/engine/platform/ym2610b.cpp index 9c126c2b3..a66f0b575 100644 --- a/src/engine/platform/ym2610b.cpp +++ b/src/engine/platform/ym2610b.cpp @@ -408,7 +408,13 @@ void DivPlatformYM2610B::acquire(short* bufL, short* bufR, size_t start, size_t ymfm::adpcm_b_engine* abe=fm->debug_adpcm_b_engine(); ymfm::ssg_engine::output_data ssgOut; - ymfm::ymfm_output<2> adpcmOut; + + ymfm::fm_channel>* fmChan[6]; + ymfm::adpcm_a_channel* adpcmAChan[6]; + for (int i=0; i<6; i++) { + fmChan[i]=fme->debug_channel(i); + adpcmAChan[i]=aae->debug_channel(i); + } for (size_t h=start; hdata[oscBuf[i]->needle++]=(fme->debug_channel(i)->debug_output(0)+fme->debug_channel(i)->debug_output(1)); + oscBuf[i]->data[oscBuf[i]->needle++]=(fmChan[i]->debug_output(0)+fmChan[i]->debug_output(1)); } ssge->get_last_out(ssgOut); @@ -446,14 +453,10 @@ void DivPlatformYM2610B::acquire(short* bufL, short* bufR, size_t start, size_t } for (int i=9; i<15; i++) { - adpcmOut.clear(); - aae->debug_channel(i-9)->output<2>(adpcmOut); - oscBuf[i]->data[oscBuf[i]->needle++]=adpcmOut.data[0]+adpcmOut.data[1]; + oscBuf[i]->data[oscBuf[i]->needle++]=adpcmAChan[i-9]->get_last_out(0)+adpcmAChan[i-9]->get_last_out(1); } - adpcmOut.clear(); - abe->output(adpcmOut,1); - oscBuf[15]->data[oscBuf[15]->needle++]=adpcmOut.data[0]+adpcmOut.data[1]; + oscBuf[15]->data[oscBuf[15]->needle++]=abe->get_last_out(0)+abe->get_last_out(1); } } From 978b0798e5eae281e7f4c6ea30ce435413f657a6 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Tue, 3 May 2022 14:04:38 -0500 Subject: [PATCH 309/342] OPL: fix possible crash --- src/engine/platform/opl.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/engine/platform/opl.cpp b/src/engine/platform/opl.cpp index dfe83d364..dc121a06c 100644 --- a/src/engine/platform/opl.cpp +++ b/src/engine/platform/opl.cpp @@ -940,6 +940,7 @@ void* DivPlatformOPL::getChanState(int ch) { } DivDispatchOscBuffer* DivPlatformOPL::getOscBuffer(int ch) { + if (ch>=18) return NULL; return oscBuf[ch]; } From 49ff39abee9fa38601d7d77117edd0e9048c091e Mon Sep 17 00:00:00 2001 From: Laurens Holst Date: Tue, 3 May 2022 19:32:17 +0200 Subject: [PATCH 310/342] AY8930: Fix tone and noise period in expanded mode. By doubling the internal clock frequency. In expanded mode, the same tone period is an octave higher than in compat mode. In expanded mode, the noise period is twice as fast as currently implemented. See https://github.com/tildearrow/furnace/pull/416 --- src/engine/platform/ay8930.cpp | 4 ++-- src/engine/platform/sound/ay8910.cpp | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/engine/platform/ay8930.cpp b/src/engine/platform/ay8930.cpp index 5b823606b..3fec732b5 100644 --- a/src/engine/platform/ay8930.cpp +++ b/src/engine/platform/ay8930.cpp @@ -27,7 +27,7 @@ #define rWrite(a,v) if (!skipRegisterWrites) {pendingWrites[a]=v;} #define immWrite2(a,v) if (!skipRegisterWrites) {writes.emplace(a,v); if (dumpWrites) {addWrite(a,v);} } -#define CHIP_DIVIDER 8 +#define CHIP_DIVIDER 4 const char* regCheatSheetAY8930[]={ "FreqL_A", "00", @@ -645,7 +645,7 @@ void DivPlatformAY8930::setFlags(unsigned int flags) { chipClock=COLOR_NTSC/2.0; break; } - rate=chipClock/8; + rate=chipClock/4; for (int i=0; i<3; i++) { oscBuf[i]->rate=rate; } diff --git a/src/engine/platform/sound/ay8910.cpp b/src/engine/platform/sound/ay8910.cpp index 8bb387885..4d19e7de5 100644 --- a/src/engine/platform/sound/ay8910.cpp +++ b/src/engine/platform/sound/ay8910.cpp @@ -1064,7 +1064,7 @@ void ay8910_device::sound_stream_update(short** outputs, int outLen) { tone = &m_tone[chan]; const int period = std::max(1,tone->period); - tone->count += is_expanded_mode() ? 16 : 1; + tone->count += is_expanded_mode() ? 16 : (m_feature & PSG_HAS_EXPANDED_MODE) ? 2 : 1; while (tone->count >= period) { tone->duty_cycle = (tone->duty_cycle - 1) & 0x1f; @@ -1080,7 +1080,7 @@ void ay8910_device::sound_stream_update(short** outputs, int outLen) * channels. */ m_count_noise = 0; - m_prescale_noise ^= 1; + m_prescale_noise = (m_prescale_noise + 1) & ((m_feature & PSG_HAS_EXPANDED_MODE) ? 3 : 1); if (!m_prescale_noise || is_expanded_mode()) // AY8930 noise generator rate is twice compares as compatibility mode { @@ -1469,7 +1469,7 @@ ay8910_device::ay8910_device(device_type type, unsigned int clock, m_noise_latch(0), m_mode(0), m_env_step_mask((!(feature & PSG_HAS_EXPANDED_MODE)) && (psg_type == PSG_TYPE_AY) ? 0x0f : 0x1f), - m_step( (!(feature & PSG_HAS_EXPANDED_MODE)) && (psg_type == PSG_TYPE_AY) ? 2 : 1), + m_step( (feature & PSG_HAS_EXPANDED_MODE) || (psg_type == PSG_TYPE_AY) ? 2 : 1), m_zero_is_off( (!(feature & PSG_HAS_EXPANDED_MODE)) && (psg_type == PSG_TYPE_AY) ? 1 : 0), m_par( (!(feature & PSG_HAS_EXPANDED_MODE)) && (psg_type == PSG_TYPE_AY) ? &ay8910_param : &ym2149_param), m_par_env( (!(feature & PSG_HAS_EXPANDED_MODE)) && (psg_type == PSG_TYPE_AY) ? &ay8910_param : &ym2149_param_env), @@ -1502,7 +1502,7 @@ void ay8910_device::set_type(psg_type_t psg_type) else { m_env_step_mask = 0x1f; - m_step = 1; + m_step = (m_feature & PSG_HAS_EXPANDED_MODE) ? 2 : 1; m_zero_is_off = 0; m_par = &ym2149_param; m_par_env = &ym2149_param_env; From 1a6a132a59207ab084832841469d0c9cab0cbfd4 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Tue, 3 May 2022 16:22:24 -0500 Subject: [PATCH 311/342] aaaaaa --- src/engine/sysDef.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/engine/sysDef.cpp b/src/engine/sysDef.cpp index 11e34187e..14ab512f1 100644 --- a/src/engine/sysDef.cpp +++ b/src/engine/sysDef.cpp @@ -319,6 +319,18 @@ int DivEngine::minVGMVersion(DivSystem which) { #define IS_YM2610 (sysOfChan[ch]==DIV_SYSTEM_YM2610 || sysOfChan[ch]==DIV_SYSTEM_YM2610_EXT || sysOfChan[ch]==DIV_SYSTEM_YM2610_FULL || sysOfChan[ch]==DIV_SYSTEM_YM2610_FULL_EXT || sysOfChan[ch]==DIV_SYSTEM_YM2610B || sysOfChan[ch]==DIV_SYSTEM_YM2610B_EXT) #define IS_OPM_LIKE (sysOfChan[ch]==DIV_SYSTEM_YM2151 || sysOfChan[ch]==DIV_SYSTEM_OPZ) +#define OP_EFFECT_MULTI(x,c,op,mask) \ + case x: \ + dispatchCmd(DivCommand(c,ch,op,effectVal&mask)); \ + break; + +#define OP_EFFECT_SINGLE(x,c,maxOp,mask) \ + case x: \ + if ((effectVal>>4)>=0 && (effectVal>>4)<=maxOp) { \ + dispatchCmd(DivCommand(c,ch,(effectVal>>4)-1,effectVal&mask)); \ + } \ + break; + // define systems like: // sysDefs[DIV_SYSTEM_ID]=new DivSysDef( // "Name", "Name (japanese, optional)", fileID, fileID_DMF, channels, isFM, isSTD, vgmVersion, From 9dfa2f9cbbada463979da6e5d3253a924d836184 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Tue, 3 May 2022 16:53:25 -0500 Subject: [PATCH 312/342] dev91 - it's a breaking change --- src/engine/engine.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/engine/engine.h b/src/engine/engine.h index 572fd90bc..d15838942 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -45,8 +45,8 @@ #define BUSY_BEGIN_SOFT softLocked=true; isBusy.lock(); #define BUSY_END isBusy.unlock(); softLocked=false; -#define DIV_VERSION "dev90" -#define DIV_ENGINE_VERSION 90 +#define DIV_VERSION "dev91" +#define DIV_ENGINE_VERSION 91 // for imports #define DIV_VERSION_MOD 0xff01 From 30e60f643cbd584abf8093edd60183262fd289c0 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Tue, 3 May 2022 17:37:17 -0500 Subject: [PATCH 313/342] add extra FM effects to effect post handler see #38 the last thing left to do is to put these effects on the dispatch code --- papers/doc/7-systems/opz.md | 4 +- src/engine/dispatch.h | 2 + src/engine/playback.cpp | 2 + src/engine/sysDef.cpp | 80 +++++++++++++++++++++++++++++++++++++ 4 files changed, 86 insertions(+), 2 deletions(-) diff --git a/papers/doc/7-systems/opz.md b/papers/doc/7-systems/opz.md index dd69c85c8..c7812740e 100644 --- a/papers/doc/7-systems/opz.md +++ b/papers/doc/7-systems/opz.md @@ -46,10 +46,10 @@ no plans have been made for TX81Z MIDI passthrough, because: - `28xy`: set reverb of operator. - `x` is the operator (1-4). a value of 0 means "all operators". - `y` is the value. -- `29xy`: set EG shift of operator. +- `2Axy`: set waveform of operator. - `x` is the operator (1-4). a value of 0 means "all operators". - `y` is the value. -- `2Axy`: set waveform of operator. +- `2Bxy`: set EG shift of operator. - `x` is the operator (1-4). a value of 0 means "all operators". - `y` is the value. - `2Fxx`: enable envelope hard reset. diff --git a/src/engine/dispatch.h b/src/engine/dispatch.h index 8799c7821..bf80c645e 100644 --- a/src/engine/dispatch.h +++ b/src/engine/dispatch.h @@ -78,6 +78,8 @@ enum DivDispatchCmds { DIV_CMD_FM_SUS, // (op, value) DIV_CMD_FM_WS, // (op, value) DIV_CMD_FM_SSG, // (op, value) + DIV_CMD_FM_REV, // (op, value) + DIV_CMD_FM_EG_SHIFT, // (op, value) DIV_CMD_FM_FB, // (value) DIV_CMD_FM_MULT, // (op, value) DIV_CMD_FM_FINE, // (op, value) diff --git a/src/engine/playback.cpp b/src/engine/playback.cpp index 5572fea05..224838e84 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -80,6 +80,8 @@ const char* cmdName[]={ "FM_SUS", "FM_WS", "FM_SSG", + "FM_REV", + "FM_EG_SHIFT", "FM_FB", "FM_MULT", "FM_FINE", diff --git a/src/engine/sysDef.cpp b/src/engine/sysDef.cpp index 14ab512f1..70768fe0f 100644 --- a/src/engine/sysDef.cpp +++ b/src/engine/sysDef.cpp @@ -448,6 +448,54 @@ void DivEngine::registerSystems() { dispatchCmd(DivCommand(DIV_CMD_AY_AUTO_ENVELOPE,ch,effectVal)); } break; + // fixed frequency effects on OPZ + case 0x30: case 0x31: case 0x32: case 0x33: + case 0x34: case 0x35: case 0x36: case 0x37: + if (sysOfChan[ch]==DIV_SYSTEM_OPZ) { + dispatchCmd(DivCommand(DIV_CMD_FM_FIXFREQ,ch,0,((effect&7)<<8)|effectVal)); + } + break; + case 0x38: case 0x39: case 0x3a: case 0x3b: + case 0x3c: case 0x3d: case 0x3e: case 0x3f: + if (sysOfChan[ch]==DIV_SYSTEM_OPZ) { + dispatchCmd(DivCommand(DIV_CMD_FM_FIXFREQ,ch,1,((effect&7)<<8)|effectVal)); + } + break; + case 0x40: case 0x41: case 0x42: case 0x43: + case 0x44: case 0x45: case 0x46: case 0x47: + if (sysOfChan[ch]==DIV_SYSTEM_OPZ) { + dispatchCmd(DivCommand(DIV_CMD_FM_FIXFREQ,ch,2,((effect&7)<<8)|effectVal)); + } + break; + case 0x48: case 0x49: case 0x4a: case 0x4b: + case 0x4c: case 0x4d: case 0x4e: case 0x4f: + if (sysOfChan[ch]==DIV_SYSTEM_OPZ) { + dispatchCmd(DivCommand(DIV_CMD_FM_FIXFREQ,ch,3,((effect&7)<<8)|effectVal)); + } + break; + // extra FM effects here + OP_EFFECT_SINGLE(0x50,DIV_CMD_FM_AM,4,1); + OP_EFFECT_SINGLE(0x51,DIV_CMD_FM_SL,4,15); + OP_EFFECT_SINGLE(0x52,DIV_CMD_FM_RR,4,15); + OP_EFFECT_SINGLE(0x53,DIV_CMD_FM_DT,4,7); + OP_EFFECT_SINGLE(0x54,DIV_CMD_FM_RS,4,3); + OP_EFFECT_SINGLE(0x55,DIV_CMD_FM_SSG,4,(IS_OPM_LIKE?3:15)); + + OP_EFFECT_MULTI(0x56,DIV_CMD_FM_DR,-1,31); + OP_EFFECT_MULTI(0x57,DIV_CMD_FM_DR,0,31); + OP_EFFECT_MULTI(0x58,DIV_CMD_FM_DR,1,31); + OP_EFFECT_MULTI(0x59,DIV_CMD_FM_DR,2,31); + OP_EFFECT_MULTI(0x5a,DIV_CMD_FM_DR,3,31); + + OP_EFFECT_MULTI(0x5b,DIV_CMD_FM_D2R,-1,31); + OP_EFFECT_MULTI(0x5c,DIV_CMD_FM_D2R,0,31); + OP_EFFECT_MULTI(0x5d,DIV_CMD_FM_D2R,1,31); + OP_EFFECT_MULTI(0x5e,DIV_CMD_FM_D2R,2,31); + OP_EFFECT_MULTI(0x5f,DIV_CMD_FM_D2R,3,31); + + OP_EFFECT_SINGLE(0x28,DIV_CMD_FM_REV,4,7); + OP_EFFECT_SINGLE(0x2a,DIV_CMD_FM_WS,4,7); + OP_EFFECT_SINGLE(0x2b,DIV_CMD_FM_EG_SHIFT,4,3); default: return false; } @@ -479,6 +527,21 @@ void DivEngine::registerSystems() { case 0x1b: // AR op2 dispatchCmd(DivCommand(DIV_CMD_FM_AR,ch,1,effectVal&31)); break; + + // extra FM effects here + OP_EFFECT_SINGLE(0x50,DIV_CMD_FM_AM,2,1); + OP_EFFECT_SINGLE(0x51,DIV_CMD_FM_SL,2,15); + OP_EFFECT_SINGLE(0x52,DIV_CMD_FM_RR,2,15); + OP_EFFECT_SINGLE(0x53,DIV_CMD_FM_VIB,2,1); + OP_EFFECT_SINGLE(0x54,DIV_CMD_FM_RS,2,3); + OP_EFFECT_SINGLE(0x55,DIV_CMD_FM_SUS,2,1); + + OP_EFFECT_MULTI(0x56,DIV_CMD_FM_DR,-1,15); + OP_EFFECT_MULTI(0x57,DIV_CMD_FM_DR,0,15); + OP_EFFECT_MULTI(0x58,DIV_CMD_FM_DR,1,15); + + OP_EFFECT_SINGLE(0x5b,DIV_CMD_FM_KSR,2,1); + OP_EFFECT_SINGLE(0x2a,DIV_CMD_FM_WS,4,7); default: return false; } @@ -528,6 +591,23 @@ void DivEngine::registerSystems() { case 0x1d: // AR op4 dispatchCmd(DivCommand(DIV_CMD_FM_AR,ch,3,effectVal&15)); break; + + // extra FM effects here + OP_EFFECT_SINGLE(0x50,DIV_CMD_FM_AM,4,1); + OP_EFFECT_SINGLE(0x51,DIV_CMD_FM_SL,4,15); + OP_EFFECT_SINGLE(0x52,DIV_CMD_FM_RR,4,15); + OP_EFFECT_SINGLE(0x53,DIV_CMD_FM_VIB,4,1); + OP_EFFECT_SINGLE(0x54,DIV_CMD_FM_RS,4,3); + OP_EFFECT_SINGLE(0x55,DIV_CMD_FM_SUS,4,1); + + OP_EFFECT_MULTI(0x56,DIV_CMD_FM_DR,-1,15); + OP_EFFECT_MULTI(0x57,DIV_CMD_FM_DR,0,15); + OP_EFFECT_MULTI(0x58,DIV_CMD_FM_DR,1,15); + OP_EFFECT_MULTI(0x59,DIV_CMD_FM_DR,2,15); + OP_EFFECT_MULTI(0x5a,DIV_CMD_FM_DR,3,15); + + OP_EFFECT_SINGLE(0x5b,DIV_CMD_FM_KSR,4,1); + default: return false; } From 28e7b8672819f66cdcb24248ae6cd0b0f0305448 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Tue, 3 May 2022 17:41:10 -0500 Subject: [PATCH 314/342] fix sample saving --- src/engine/sample.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/engine/sample.cpp b/src/engine/sample.cpp index 3aec450b0..e46c267d1 100644 --- a/src/engine/sample.cpp +++ b/src/engine/sample.cpp @@ -71,7 +71,7 @@ bool DivSample::save(const char* path) { } sf_command(f, SFC_SET_INSTRUMENT, &inst, sizeof(inst)); - sf_write_short(f,data16,length16); + sf_writef_short(f,data16,samples); sf_close(f); From 467036df2a77d0da2a8ff126973af2f6f5e99170 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 4 May 2022 14:09:43 -0500 Subject: [PATCH 315/342] implement extra FM effects (OPN, OPM and OPZ) issue #38 --- papers/doc/7-systems/opz.md | 5 +- src/engine/engine.h | 1 + src/engine/fileOps.cpp | 40 +++++- src/engine/platform/arcade.cpp | 128 +++++++++++++++++ src/engine/platform/genesis.cpp | 130 ++++++++++++++++- src/engine/platform/genesisext.cpp | 128 +++++++++++++++++ src/engine/platform/tx81z.cpp | 221 ++++++++++++++++++++++++++++- src/engine/platform/ym2610.cpp | 128 +++++++++++++++++ src/engine/platform/ym2610b.cpp | 128 +++++++++++++++++ src/engine/platform/ym2610bext.cpp | 139 +++++++++++++++++- src/engine/platform/ym2610ext.cpp | 139 +++++++++++++++++- src/engine/sysDef.cpp | 1 + 12 files changed, 1171 insertions(+), 17 deletions(-) diff --git a/papers/doc/7-systems/opz.md b/papers/doc/7-systems/opz.md index c7812740e..61e931394 100644 --- a/papers/doc/7-systems/opz.md +++ b/papers/doc/7-systems/opz.md @@ -52,6 +52,9 @@ no plans have been made for TX81Z MIDI passthrough, because: - `2Bxy`: set EG shift of operator. - `x` is the operator (1-4). a value of 0 means "all operators". - `y` is the value. +- `2Cxy` set fine multiplier of operator. + - `x` is the operator (1-4). a value of 0 means "all operators". + - `y` is the value. - `2Fxx`: enable envelope hard reset. - this works by inserting a quick release and tiny delay before a new note. - `3xyy`: set fixed frequency of operator 1/2. @@ -97,4 +100,4 @@ no plans have been made for TX81Z MIDI passthrough, because: - `5Cxx`: set D2R/SR of operator 1. - `5Dxx`: set D2R/SR of operator 2. - `5Exx`: set D2R/SR of operator 3. -- `5Fxx`: set D2R/SR of operator 4. \ No newline at end of file +- `5Fxx`: set D2R/SR of operator 4. diff --git a/src/engine/engine.h b/src/engine/engine.h index d15838942..5d6d066c0 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -383,6 +383,7 @@ class DivEngine { bool loadDMF(unsigned char* file, size_t len); bool loadFur(unsigned char* file, size_t len); bool loadMod(unsigned char* file, size_t len); + bool loadFTM(unsigned char* file, size_t len); void loadDMP(SafeReader& reader, std::vector& ret, String& stripPath); void loadTFI(SafeReader& reader, std::vector& ret, String& stripPath); diff --git a/src/engine/fileOps.cpp b/src/engine/fileOps.cpp index b543e3e12..b45baa7ab 100644 --- a/src/engine/fileOps.cpp +++ b/src/engine/fileOps.cpp @@ -26,6 +26,7 @@ #define DIV_READ_SIZE 131072 #define DIV_DMF_MAGIC ".DelekDefleMask." #define DIV_FUR_MAGIC "-Furnace module-" +#define DIV_FTM_MAGIC "FamiTracker Module" struct InflateBlock { unsigned char* buf; @@ -1617,8 +1618,6 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) { return true; } - - bool DivEngine::loadMod(unsigned char* file, size_t len) { struct InvalidHeaderException {}; bool success=false; @@ -2029,10 +2028,43 @@ bool DivEngine::loadMod(unsigned char* file, size_t len) { return success; } +bool DivEngine::loadFTM(unsigned char* file, size_t len) { + SafeReader reader=SafeReader(file,len); + warnings=""; + try { + DivSong ds; + + if (!reader.seek(18,SEEK_SET)) { + logE("premature end of file!"); + lastError="incomplete file"; + delete[] file; + return false; + } + ds.version=(unsigned short)reader.readS(); + logI("module version %d (0x%.4x)",ds.version,ds.version); + + if (ds.version>0x0440) { + logE("incompatible version %x!",ds.version); + lastError="incompatible version"; + delete[] file; + return false; + } + + + } catch (EndOfFileException& e) { + logE("premature end of file!"); + lastError="incomplete file"; + delete[] file; + return false; + } + delete[] file; + return true; +} + bool DivEngine::load(unsigned char* f, size_t slen) { unsigned char* file; size_t len; - if (slen<16) { + if (slen<18) { logE("too small!"); lastError="file is too small"; delete[] f; @@ -2137,6 +2169,8 @@ bool DivEngine::load(unsigned char* f, size_t slen) { // step 2: try loading as .fur or .dmf if (memcmp(file,DIV_DMF_MAGIC,16)==0) { return loadDMF(file,len); + } else if (memcmp(file,DIV_FTM_MAGIC,18)==0) { + return loadFTM(file,len); } else if (memcmp(file,DIV_FUR_MAGIC,16)==0) { return loadFur(file,len); } diff --git a/src/engine/platform/arcade.cpp b/src/engine/platform/arcade.cpp index f36b729a1..2c6161e53 100644 --- a/src/engine/platform/arcade.cpp +++ b/src/engine/platform/arcade.cpp @@ -650,6 +650,134 @@ int DivPlatformArcade::dispatch(DivCommand c) { } break; } + case DIV_CMD_FM_RS: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; + op.rs=c.value2&3; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; + rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.rs<<6)); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; + op.rs=c.value2&3; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.rs<<6)); + } + break; + } + case DIV_CMD_FM_AM: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; + op.am=c.value2&1; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; + rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7)); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; + op.am=c.value2&1; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7)); + } + break; + } + case DIV_CMD_FM_DR: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; + op.dr=c.value2&31; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; + rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7)); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; + op.dr=c.value2&31; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7)); + } + break; + } + case DIV_CMD_FM_SL: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; + op.sl=c.value2&15; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; + rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4)); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; + op.sl=c.value2&15; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4)); + } + break; + } + case DIV_CMD_FM_RR: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; + op.rr=c.value2&15; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; + rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4)); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; + op.rr=c.value2&15; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4)); + } + break; + } + case DIV_CMD_FM_DT2: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; + op.dt2=c.value2&3; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; + rWrite(baseAddr+ADDR_DT2_D2R,(op.d2r&31)|(op.dt2<<6)); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; + op.dt2=c.value2&3; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_DT2_D2R,(op.d2r&31)|(op.dt2<<6)); + } + break; + } + case DIV_CMD_FM_D2R: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; + op.d2r=c.value2&31; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; + rWrite(baseAddr+ADDR_DT2_D2R,(op.d2r&31)|(op.dt2<<6)); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; + op.d2r=c.value2&31; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_DT2_D2R,(op.d2r&31)|(op.dt2<<6)); + } + break; + } + case DIV_CMD_FM_DT: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; + op.dt=c.value&7; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; + rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4)); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; + op.dt=c.value2&7; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4)); + } + break; + } case DIV_CMD_FM_AM_DEPTH: { amDepth=c.value; immWrite(0x19,amDepth); diff --git a/src/engine/platform/genesis.cpp b/src/engine/platform/genesis.cpp index 5325c982b..67970298a 100644 --- a/src/engine/platform/genesis.cpp +++ b/src/engine/platform/genesis.cpp @@ -780,7 +780,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) { unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.rs<<6)); } - } else { + } else if (c.value<4) { DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; op.ar=c.value2&31; unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; @@ -788,6 +788,134 @@ int DivPlatformGenesis::dispatch(DivCommand c) { } break; } + case DIV_CMD_FM_RS: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; + op.rs=c.value2&3; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; + rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.rs<<6)); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; + op.rs=c.value2&3; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.rs<<6)); + } + break; + } + case DIV_CMD_FM_AM: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; + op.am=c.value2&1; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; + rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7)); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; + op.am=c.value2&1; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7)); + } + break; + } + case DIV_CMD_FM_DR: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; + op.dr=c.value2&31; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; + rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7)); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; + op.dr=c.value2&31; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7)); + } + break; + } + case DIV_CMD_FM_SL: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; + op.sl=c.value2&15; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; + rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4)); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; + op.sl=c.value2&15; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4)); + } + break; + } + case DIV_CMD_FM_RR: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; + op.rr=c.value2&15; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; + rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4)); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; + op.rr=c.value2&15; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4)); + } + break; + } + case DIV_CMD_FM_D2R: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; + op.d2r=c.value2&31; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; + rWrite(baseAddr+ADDR_DT2_D2R,op.d2r&31); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; + op.d2r=c.value2&31; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_DT2_D2R,op.d2r&31); + } + break; + } + case DIV_CMD_FM_DT: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; + op.dt=c.value&7; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; + rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4)); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; + op.dt=c.value2&7; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4)); + } + break; + } + case DIV_CMD_FM_SSG: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; + op.ssgEnv=8^(c.value2&15); + unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; + rWrite(baseAddr+ADDR_SSG,op.ssgEnv&15); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; + op.ssgEnv=8^(c.value2&15); + unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_SSG,op.ssgEnv&15); + } + break; + } case DIV_CMD_FM_HARD_RESET: chan[c.chan].hardReset=c.value; break; diff --git a/src/engine/platform/genesisext.cpp b/src/engine/platform/genesisext.cpp index d5c98a56c..ae4d74cd9 100644 --- a/src/engine/platform/genesisext.cpp +++ b/src/engine/platform/genesisext.cpp @@ -235,6 +235,134 @@ int DivPlatformGenesisExt::dispatch(DivCommand c) { } break; } + case DIV_CMD_FM_RS: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[2].state.op[i]; + op.rs=c.value2&3; + unsigned short baseAddr=chanOffs[2]|opOffs[i]; + rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.rs<<6)); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[2].state.op[orderedOps[c.value]]; + op.rs=c.value2&3; + unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.rs<<6)); + } + break; + } + case DIV_CMD_FM_AM: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[2].state.op[i]; + op.am=c.value2&1; + unsigned short baseAddr=chanOffs[2]|opOffs[i]; + rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7)); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[2].state.op[orderedOps[c.value]]; + op.am=c.value2&1; + unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7)); + } + break; + } + case DIV_CMD_FM_DR: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[2].state.op[i]; + op.dr=c.value2&31; + unsigned short baseAddr=chanOffs[2]|opOffs[i]; + rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7)); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[2].state.op[orderedOps[c.value]]; + op.dr=c.value2&31; + unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7)); + } + break; + } + case DIV_CMD_FM_SL: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[2].state.op[i]; + op.sl=c.value2&15; + unsigned short baseAddr=chanOffs[2]|opOffs[i]; + rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4)); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[2].state.op[orderedOps[c.value]]; + op.sl=c.value2&15; + unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4)); + } + break; + } + case DIV_CMD_FM_RR: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[2].state.op[i]; + op.rr=c.value2&15; + unsigned short baseAddr=chanOffs[2]|opOffs[i]; + rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4)); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[2].state.op[orderedOps[c.value]]; + op.rr=c.value2&15; + unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4)); + } + break; + } + case DIV_CMD_FM_D2R: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[2].state.op[i]; + op.d2r=c.value2&31; + unsigned short baseAddr=chanOffs[2]|opOffs[i]; + rWrite(baseAddr+ADDR_DT2_D2R,op.d2r&31); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[2].state.op[orderedOps[c.value]]; + op.d2r=c.value2&31; + unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_DT2_D2R,op.d2r&31); + } + break; + } + case DIV_CMD_FM_DT: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[2].state.op[i]; + op.dt=c.value&7; + unsigned short baseAddr=chanOffs[2]|opOffs[i]; + rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4)); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[2].state.op[orderedOps[c.value]]; + op.dt=c.value2&7; + unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4)); + } + break; + } + case DIV_CMD_FM_SSG: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[2].state.op[i]; + op.ssgEnv=8^(c.value2&15); + unsigned short baseAddr=chanOffs[2]|opOffs[i]; + rWrite(baseAddr+ADDR_SSG,op.ssgEnv&15); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[2].state.op[orderedOps[c.value]]; + op.ssgEnv=8^(c.value2&15); + unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_SSG,op.ssgEnv&15); + } + break; + } case DIV_CMD_GET_VOLMAX: return 127; break; diff --git a/src/engine/platform/tx81z.cpp b/src/engine/platform/tx81z.cpp index 788cfe245..277de8835 100644 --- a/src/engine/platform/tx81z.cpp +++ b/src/engine/platform/tx81z.cpp @@ -604,8 +604,10 @@ int DivPlatformTX81Z::dispatch(DivCommand c) { case DIV_CMD_FM_MULT: { unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; - op.mult=c.value2&15; - rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|((op.egt?(op.dt&7):dtTable[op.dt&7])<<4)); + if (!op.egt) { + op.mult=c.value2&15; + rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|((op.egt?(op.dt&7):dtTable[op.dt&7])<<4)); + } break; } case DIV_CMD_FM_TL: { @@ -639,6 +641,221 @@ int DivPlatformTX81Z::dispatch(DivCommand c) { } break; } + case DIV_CMD_FM_RS: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; + op.rs=c.value2&3; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; + rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.egt<<5)|(op.rs<<6)); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; + op.rs=c.value2&3; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.egt<<5)|(op.rs<<6)); + } + break; + } + case DIV_CMD_FM_AM: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; + op.am=c.value2&1; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; + rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7)); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; + op.am=c.value2&1; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7)); + } + break; + } + case DIV_CMD_FM_DR: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; + op.dr=c.value2&31; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; + rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7)); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; + op.dr=c.value2&31; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7)); + } + break; + } + case DIV_CMD_FM_SL: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; + op.sl=c.value2&15; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; + rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4)); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; + op.sl=c.value2&15; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4)); + } + break; + } + case DIV_CMD_FM_RR: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; + op.rr=c.value2&15; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; + rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4)); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; + op.rr=c.value2&15; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4)); + } + break; + } + case DIV_CMD_FM_DT2: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; + op.dt2=c.value2&3; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; + rWrite(baseAddr+ADDR_DT2_D2R,(op.d2r&31)|(op.dt2<<6)); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; + op.dt2=c.value2&3; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_DT2_D2R,(op.d2r&31)|(op.dt2<<6)); + } + break; + } + case DIV_CMD_FM_D2R: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; + op.d2r=c.value2&31; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; + rWrite(baseAddr+ADDR_DT2_D2R,(op.d2r&31)|(op.dt2<<6)); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; + op.d2r=c.value2&31; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_DT2_D2R,(op.d2r&31)|(op.dt2<<6)); + } + break; + } + case DIV_CMD_FM_DT: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; + if (!op.egt) { + op.dt=c.value&7; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; + rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|((op.egt?(op.dt&7):dtTable[op.dt&7])<<4)); + } + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; + if (!op.egt) { + op.dt=c.value2&7; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|((op.egt?(op.dt&7):dtTable[op.dt&7])<<4)); + } + } + break; + } + case DIV_CMD_FM_EG_SHIFT: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; + op.ksl=c.value2&3; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; + rWrite(baseAddr+ADDR_EGS_REV,(op.dam&7)|(op.ksl<<6)); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; + op.ksl=c.value2&3; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_EGS_REV,(op.dam&7)|(op.ksl<<6)); + } + break; + } + case DIV_CMD_FM_REV: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; + op.dam=c.value2&7; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; + rWrite(baseAddr+ADDR_EGS_REV,(op.dam&7)|(op.ksl<<6)); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; + op.dam=c.value2&7; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_EGS_REV,(op.dam&7)|(op.ksl<<6)); + } + break; + } + case DIV_CMD_FM_WS: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; + op.ws=c.value2&7; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; + rWrite(baseAddr+ADDR_WS_FINE,(op.dvb&15)|(op.ws<<4)); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; + op.ws=c.value2&7; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_WS_FINE,(op.dvb&15)|(op.ws<<4)); + } + break; + } + case DIV_CMD_FM_FINE: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; + if (!op.egt) { + op.dvb=c.value2&15; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; + rWrite(baseAddr+ADDR_WS_FINE,(op.dvb&15)|(op.ws<<4)); + } + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; + if (!op.egt) { + op.dvb=c.value2&15; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_WS_FINE,(op.dvb&15)|(op.ws<<4)); + } + } + break; + } + case DIV_CMD_FM_FIXFREQ: { + if (c.value<0 || c.value>3) break; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; + DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; + op.egt=(c.value2>0); + rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.egt<<5)|(op.rs<<6)); + if (op.egt) { + rWrite(baseAddr+ADDR_MULT_DT,((c.value2>>4)&15)|((c.value2>>8)&7)); + rWrite(baseAddr+ADDR_WS_FINE,(c.value2&15)|(op.ws<<4)); + } else { + rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|((op.egt?(op.dt&7):dtTable[op.dt&7])<<4)); + rWrite(baseAddr+ADDR_WS_FINE,(op.dvb&15)|(op.ws<<4)); + } + break; + } case DIV_CMD_FM_AM_DEPTH: { amDepth=c.value; immWrite(0x19,amDepth); diff --git a/src/engine/platform/ym2610.cpp b/src/engine/platform/ym2610.cpp index a45266086..85aa36339 100644 --- a/src/engine/platform/ym2610.cpp +++ b/src/engine/platform/ym2610.cpp @@ -1088,6 +1088,134 @@ int DivPlatformYM2610::dispatch(DivCommand c) { } break; } + case DIV_CMD_FM_RS: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; + op.rs=c.value2&3; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; + rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.rs<<6)); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; + op.rs=c.value2&3; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.rs<<6)); + } + break; + } + case DIV_CMD_FM_AM: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; + op.am=c.value2&1; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; + rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7)); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; + op.am=c.value2&1; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7)); + } + break; + } + case DIV_CMD_FM_DR: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; + op.dr=c.value2&31; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; + rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7)); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; + op.dr=c.value2&31; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7)); + } + break; + } + case DIV_CMD_FM_SL: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; + op.sl=c.value2&15; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; + rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4)); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; + op.sl=c.value2&15; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4)); + } + break; + } + case DIV_CMD_FM_RR: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; + op.rr=c.value2&15; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; + rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4)); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; + op.rr=c.value2&15; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4)); + } + break; + } + case DIV_CMD_FM_D2R: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; + op.d2r=c.value2&31; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; + rWrite(baseAddr+ADDR_DT2_D2R,op.d2r&31); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; + op.d2r=c.value2&31; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_DT2_D2R,op.d2r&31); + } + break; + } + case DIV_CMD_FM_DT: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; + op.dt=c.value&7; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; + rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4)); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; + op.dt=c.value2&7; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4)); + } + break; + } + case DIV_CMD_FM_SSG: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; + op.ssgEnv=8^(c.value2&15); + unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; + rWrite(baseAddr+ADDR_SSG,op.ssgEnv&15); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; + op.ssgEnv=8^(c.value2&15); + unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_SSG,op.ssgEnv&15); + } + break; + } case DIV_CMD_FM_HARD_RESET: chan[c.chan].hardReset=c.value; break; diff --git a/src/engine/platform/ym2610b.cpp b/src/engine/platform/ym2610b.cpp index a66f0b575..83aa0fb9c 100644 --- a/src/engine/platform/ym2610b.cpp +++ b/src/engine/platform/ym2610b.cpp @@ -1066,6 +1066,134 @@ int DivPlatformYM2610B::dispatch(DivCommand c) { } break; } + case DIV_CMD_FM_RS: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; + op.rs=c.value2&3; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; + rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.rs<<6)); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; + op.rs=c.value2&3; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.rs<<6)); + } + break; + } + case DIV_CMD_FM_AM: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; + op.am=c.value2&1; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; + rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7)); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; + op.am=c.value2&1; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7)); + } + break; + } + case DIV_CMD_FM_DR: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; + op.dr=c.value2&31; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; + rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7)); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; + op.dr=c.value2&31; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7)); + } + break; + } + case DIV_CMD_FM_SL: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; + op.sl=c.value2&15; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; + rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4)); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; + op.sl=c.value2&15; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4)); + } + break; + } + case DIV_CMD_FM_RR: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; + op.rr=c.value2&15; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; + rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4)); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; + op.rr=c.value2&15; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4)); + } + break; + } + case DIV_CMD_FM_D2R: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; + op.d2r=c.value2&31; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; + rWrite(baseAddr+ADDR_DT2_D2R,op.d2r&31); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; + op.d2r=c.value2&31; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_DT2_D2R,op.d2r&31); + } + break; + } + case DIV_CMD_FM_DT: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; + op.dt=c.value&7; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; + rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4)); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; + op.dt=c.value2&7; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4)); + } + break; + } + case DIV_CMD_FM_SSG: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; + op.ssgEnv=8^(c.value2&15); + unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; + rWrite(baseAddr+ADDR_SSG,op.ssgEnv&15); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; + op.ssgEnv=8^(c.value2&15); + unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_SSG,op.ssgEnv&15); + } + break; + } case DIV_CMD_FM_HARD_RESET: chan[c.chan].hardReset=c.value; break; diff --git a/src/engine/platform/ym2610bext.cpp b/src/engine/platform/ym2610bext.cpp index 87c635736..03b91fccc 100644 --- a/src/engine/platform/ym2610bext.cpp +++ b/src/engine/platform/ym2610bext.cpp @@ -175,17 +175,146 @@ int DivPlatformYM2610BExt::dispatch(DivCommand c) { break; } case DIV_CMD_FM_AR: { - DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM); if (c.value<0) { for (int i=0; i<4; i++) { - DivInstrumentFM::Operator op=ins->fm.op[i]; + DivInstrumentFM::Operator& op=chan[2].state.op[i]; + op.ar=c.value2&31; unsigned short baseAddr=chanOffs[2]|opOffs[i]; - rWrite(baseAddr+0x50,(c.value2&31)|(op.rs<<6)); + rWrite(baseAddr+0x50,(op.ar&31)|(op.rs<<6)); } } else { - DivInstrumentFM::Operator op=ins->fm.op[orderedOps[c.value]]; + DivInstrumentFM::Operator& op=chan[2].state.op[orderedOps[c.value]]; + op.ar=c.value2&31; unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[c.value]]; - rWrite(baseAddr+0x50,(c.value2&31)|(op.rs<<6)); + rWrite(baseAddr+0x50,(op.ar&31)|(op.rs<<6)); + } + break; + } + case DIV_CMD_FM_RS: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[2].state.op[i]; + op.rs=c.value2&3; + unsigned short baseAddr=chanOffs[2]|opOffs[i]; + rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.rs<<6)); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[2].state.op[orderedOps[c.value]]; + op.rs=c.value2&3; + unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.rs<<6)); + } + break; + } + case DIV_CMD_FM_AM: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[2].state.op[i]; + op.am=c.value2&1; + unsigned short baseAddr=chanOffs[2]|opOffs[i]; + rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7)); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[2].state.op[orderedOps[c.value]]; + op.am=c.value2&1; + unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7)); + } + break; + } + case DIV_CMD_FM_DR: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[2].state.op[i]; + op.dr=c.value2&31; + unsigned short baseAddr=chanOffs[2]|opOffs[i]; + rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7)); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[2].state.op[orderedOps[c.value]]; + op.dr=c.value2&31; + unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7)); + } + break; + } + case DIV_CMD_FM_SL: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[2].state.op[i]; + op.sl=c.value2&15; + unsigned short baseAddr=chanOffs[2]|opOffs[i]; + rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4)); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[2].state.op[orderedOps[c.value]]; + op.sl=c.value2&15; + unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4)); + } + break; + } + case DIV_CMD_FM_RR: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[2].state.op[i]; + op.rr=c.value2&15; + unsigned short baseAddr=chanOffs[2]|opOffs[i]; + rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4)); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[2].state.op[orderedOps[c.value]]; + op.rr=c.value2&15; + unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4)); + } + break; + } + case DIV_CMD_FM_D2R: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[2].state.op[i]; + op.d2r=c.value2&31; + unsigned short baseAddr=chanOffs[2]|opOffs[i]; + rWrite(baseAddr+ADDR_DT2_D2R,op.d2r&31); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[2].state.op[orderedOps[c.value]]; + op.d2r=c.value2&31; + unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_DT2_D2R,op.d2r&31); + } + break; + } + case DIV_CMD_FM_DT: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[2].state.op[i]; + op.dt=c.value&7; + unsigned short baseAddr=chanOffs[2]|opOffs[i]; + rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4)); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[2].state.op[orderedOps[c.value]]; + op.dt=c.value2&7; + unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4)); + } + break; + } + case DIV_CMD_FM_SSG: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[2].state.op[i]; + op.ssgEnv=8^(c.value2&15); + unsigned short baseAddr=chanOffs[2]|opOffs[i]; + rWrite(baseAddr+ADDR_SSG,op.ssgEnv&15); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[2].state.op[orderedOps[c.value]]; + op.ssgEnv=8^(c.value2&15); + unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_SSG,op.ssgEnv&15); } break; } diff --git a/src/engine/platform/ym2610ext.cpp b/src/engine/platform/ym2610ext.cpp index 013e7b954..1ffbd74fe 100644 --- a/src/engine/platform/ym2610ext.cpp +++ b/src/engine/platform/ym2610ext.cpp @@ -175,17 +175,146 @@ int DivPlatformYM2610Ext::dispatch(DivCommand c) { break; } case DIV_CMD_FM_AR: { - DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM); if (c.value<0) { for (int i=0; i<4; i++) { - DivInstrumentFM::Operator op=ins->fm.op[i]; + DivInstrumentFM::Operator& op=chan[1].state.op[i]; + op.ar=c.value2&31; unsigned short baseAddr=chanOffs[1]|opOffs[i]; - rWrite(baseAddr+0x50,(c.value2&31)|(op.rs<<6)); + rWrite(baseAddr+0x50,(op.ar&31)|(op.rs<<6)); } } else { - DivInstrumentFM::Operator op=ins->fm.op[orderedOps[c.value]]; + DivInstrumentFM::Operator& op=chan[1].state.op[orderedOps[c.value]]; + op.ar=c.value2&31; unsigned short baseAddr=chanOffs[1]|opOffs[orderedOps[c.value]]; - rWrite(baseAddr+0x50,(c.value2&31)|(op.rs<<6)); + rWrite(baseAddr+0x50,(op.ar&31)|(op.rs<<6)); + } + break; + } + case DIV_CMD_FM_RS: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[1].state.op[i]; + op.rs=c.value2&3; + unsigned short baseAddr=chanOffs[1]|opOffs[i]; + rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.rs<<6)); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[1].state.op[orderedOps[c.value]]; + op.rs=c.value2&3; + unsigned short baseAddr=chanOffs[1]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.rs<<6)); + } + break; + } + case DIV_CMD_FM_AM: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[1].state.op[i]; + op.am=c.value2&1; + unsigned short baseAddr=chanOffs[1]|opOffs[i]; + rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7)); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[1].state.op[orderedOps[c.value]]; + op.am=c.value2&1; + unsigned short baseAddr=chanOffs[1]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7)); + } + break; + } + case DIV_CMD_FM_DR: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[1].state.op[i]; + op.dr=c.value2&31; + unsigned short baseAddr=chanOffs[1]|opOffs[i]; + rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7)); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[1].state.op[orderedOps[c.value]]; + op.dr=c.value2&31; + unsigned short baseAddr=chanOffs[1]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7)); + } + break; + } + case DIV_CMD_FM_SL: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[1].state.op[i]; + op.sl=c.value2&15; + unsigned short baseAddr=chanOffs[1]|opOffs[i]; + rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4)); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[1].state.op[orderedOps[c.value]]; + op.sl=c.value2&15; + unsigned short baseAddr=chanOffs[1]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4)); + } + break; + } + case DIV_CMD_FM_RR: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[1].state.op[i]; + op.rr=c.value2&15; + unsigned short baseAddr=chanOffs[1]|opOffs[i]; + rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4)); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[1].state.op[orderedOps[c.value]]; + op.rr=c.value2&15; + unsigned short baseAddr=chanOffs[1]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4)); + } + break; + } + case DIV_CMD_FM_D2R: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[1].state.op[i]; + op.d2r=c.value2&31; + unsigned short baseAddr=chanOffs[1]|opOffs[i]; + rWrite(baseAddr+ADDR_DT2_D2R,op.d2r&31); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[1].state.op[orderedOps[c.value]]; + op.d2r=c.value2&31; + unsigned short baseAddr=chanOffs[1]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_DT2_D2R,op.d2r&31); + } + break; + } + case DIV_CMD_FM_DT: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[1].state.op[i]; + op.dt=c.value&7; + unsigned short baseAddr=chanOffs[1]|opOffs[i]; + rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4)); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[1].state.op[orderedOps[c.value]]; + op.dt=c.value2&7; + unsigned short baseAddr=chanOffs[1]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4)); + } + break; + } + case DIV_CMD_FM_SSG: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[1].state.op[i]; + op.ssgEnv=8^(c.value2&15); + unsigned short baseAddr=chanOffs[1]|opOffs[i]; + rWrite(baseAddr+ADDR_SSG,op.ssgEnv&15); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[1].state.op[orderedOps[c.value]]; + op.ssgEnv=8^(c.value2&15); + unsigned short baseAddr=chanOffs[1]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_SSG,op.ssgEnv&15); } break; } diff --git a/src/engine/sysDef.cpp b/src/engine/sysDef.cpp index 70768fe0f..1a3714019 100644 --- a/src/engine/sysDef.cpp +++ b/src/engine/sysDef.cpp @@ -496,6 +496,7 @@ void DivEngine::registerSystems() { OP_EFFECT_SINGLE(0x28,DIV_CMD_FM_REV,4,7); OP_EFFECT_SINGLE(0x2a,DIV_CMD_FM_WS,4,7); OP_EFFECT_SINGLE(0x2b,DIV_CMD_FM_EG_SHIFT,4,3); + OP_EFFECT_SINGLE(0x2c,DIV_CMD_FM_FINE,4,15); default: return false; } From 6bd5a9bcb6a83ed25aa29c4ce545b451f6205854 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 4 May 2022 14:10:28 -0500 Subject: [PATCH 316/342] prepare for possible .ftm import --- src/gui/gui.cpp | 4 ++-- src/gui/settings.cpp | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 80fad49e2..c94c8348a 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -1259,9 +1259,9 @@ void FurnaceGUI::openFileDialog(FurnaceGUIFileDialogs type) { if (!dirExists(workingDirSong)) workingDirSong=getHomeDir(); hasOpened=fileDialog->openLoad( "Open File", - {"compatible files", "*.fur *.dmf *.mod", + {"compatible files", "*.fur *.dmf *.mod *.ftm", "all files", ".*"}, - "compatible files{.fur,.dmf,.mod},.*", + "compatible files{.fur,.dmf,.mod,.ftm},.*", workingDirSong, dpiScale ); diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index e4539b794..dfaa85cfd 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -2688,6 +2688,7 @@ void FurnaceGUI::applyUISettings(bool updateFonts) { ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".ttc",uiColors[GUI_COLOR_FILE_FONT],ICON_FA_FONT); ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".mod",uiColors[GUI_COLOR_FILE_SONG_IMPORT],ICON_FA_FILE); + ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".ftm",uiColors[GUI_COLOR_FILE_SONG_IMPORT],ICON_FA_FILE); ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".tfi",uiColors[GUI_COLOR_FILE_INSTR],ICON_FA_FILE); ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtension,".vgi",uiColors[GUI_COLOR_FILE_INSTR],ICON_FA_FILE); From 6731edc5681182db4d85a915d4286c659686838d Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 4 May 2022 14:10:37 -0500 Subject: [PATCH 317/342] update to-do list --- TODO.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/TODO.md b/TODO.md index 4e352566f..710e6091e 100644 --- a/TODO.md +++ b/TODO.md @@ -29,4 +29,6 @@ - add ability to select a column by double clicking - add ability to move selection by dragging - Apply button in settings -- find and replace \ No newline at end of file +- find and replace +- finish wave synth +- make compact mode multi-state (note, note/ins, note/ins/vol and note/ins/vol/effects) \ No newline at end of file From 897bf323f2dc7490ac1d8320b1213ec4d2837266 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 4 May 2022 16:56:45 -0500 Subject: [PATCH 318/342] implement extra FM effects (OPLL and OPL) --- src/engine/platform/opl.cpp | 216 +++++++++++++++++++++++++++++++++++ src/engine/platform/opll.cpp | 144 +++++++++++++++++++++++ src/engine/sysDef.cpp | 2 +- 3 files changed, 361 insertions(+), 1 deletion(-) diff --git a/src/engine/platform/opl.cpp b/src/engine/platform/opl.cpp index dc121a06c..7dad9e1c8 100644 --- a/src/engine/platform/opl.cpp +++ b/src/engine/platform/opl.cpp @@ -841,6 +841,222 @@ int DivPlatformOPL::dispatch(DivCommand c) { } break; } + case DIV_CMD_FM_DR: { + int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2; + if (c.value<0) { + for (int i=0; i=ops) break; + DivInstrumentFM::Operator& op=chan[c.chan].state.op[(ops==4)?orderedOpsL[c.value]:c.value]; + op.dr=c.value2&15; + unsigned char slot=slots[c.value][c.chan]; + if (slot==255) break; + unsigned short baseAddr=slotMap[slot]; + rWrite(baseAddr+ADDR_AR_DR,(op.ar<<4)|op.dr); + } + break; + } + case DIV_CMD_FM_SL: { + int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2; + if (c.value<0) { + for (int i=0; i=ops) break; + DivInstrumentFM::Operator& op=chan[c.chan].state.op[(ops==4)?orderedOpsL[c.value]:c.value]; + op.sl=c.value2&15; + unsigned char slot=slots[c.value][c.chan]; + if (slot==255) break; + unsigned short baseAddr=slotMap[slot]; + rWrite(baseAddr+ADDR_SL_RR,(op.sl<<4)|op.rr); + } + break; + } + case DIV_CMD_FM_RR: { + int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2; + if (c.value<0) { + for (int i=0; i=ops) break; + DivInstrumentFM::Operator& op=chan[c.chan].state.op[(ops==4)?orderedOpsL[c.value]:c.value]; + op.rr=c.value2&15; + unsigned char slot=slots[c.value][c.chan]; + if (slot==255) break; + unsigned short baseAddr=slotMap[slot]; + rWrite(baseAddr+ADDR_SL_RR,(op.sl<<4)|op.rr); + } + break; + } + case DIV_CMD_FM_AM: { + int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2; + if (c.value<0) { + for (int i=0; i=ops) break; + DivInstrumentFM::Operator& op=chan[c.chan].state.op[(ops==4)?orderedOpsL[c.value]:c.value]; + op.am=c.value2&1; + unsigned char slot=slots[c.value][c.chan]; + if (slot==255) break; + unsigned short baseAddr=slotMap[slot]; + rWrite(baseAddr+ADDR_AM_VIB_SUS_KSR_MULT,(op.am<<7)|(op.vib<<6)|(op.sus<<5)|(op.ksr<<4)|op.mult); + } + break; + } + case DIV_CMD_FM_VIB: { + int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2; + if (c.value<0) { + for (int i=0; i=ops) break; + DivInstrumentFM::Operator& op=chan[c.chan].state.op[(ops==4)?orderedOpsL[c.value]:c.value]; + op.vib=c.value2&1; + unsigned char slot=slots[c.value][c.chan]; + if (slot==255) break; + unsigned short baseAddr=slotMap[slot]; + rWrite(baseAddr+ADDR_AM_VIB_SUS_KSR_MULT,(op.am<<7)|(op.vib<<6)|(op.sus<<5)|(op.ksr<<4)|op.mult); + } + break; + } + case DIV_CMD_FM_SUS: { + int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2; + if (c.value<0) { + for (int i=0; i=ops) break; + DivInstrumentFM::Operator& op=chan[c.chan].state.op[(ops==4)?orderedOpsL[c.value]:c.value]; + op.sus=c.value2&1; + unsigned char slot=slots[c.value][c.chan]; + if (slot==255) break; + unsigned short baseAddr=slotMap[slot]; + rWrite(baseAddr+ADDR_AM_VIB_SUS_KSR_MULT,(op.am<<7)|(op.vib<<6)|(op.sus<<5)|(op.ksr<<4)|op.mult); + } + break; + } + case DIV_CMD_FM_KSR: { + int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2; + if (c.value<0) { + for (int i=0; i=ops) break; + DivInstrumentFM::Operator& op=chan[c.chan].state.op[(ops==4)?orderedOpsL[c.value]:c.value]; + op.ksr=c.value2&1; + unsigned char slot=slots[c.value][c.chan]; + if (slot==255) break; + unsigned short baseAddr=slotMap[slot]; + rWrite(baseAddr+ADDR_AM_VIB_SUS_KSR_MULT,(op.am<<7)|(op.vib<<6)|(op.sus<<5)|(op.ksr<<4)|op.mult); + } + break; + } + case DIV_CMD_FM_WS: { + if (oplType<2) break; + int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2; + if (c.value<0) { + for (int i=0; i=ops) break; + DivInstrumentFM::Operator& op=chan[c.chan].state.op[(ops==4)?orderedOpsL[c.value]:c.value]; + op.ws=c.value2&7; + unsigned char slot=slots[c.value][c.chan]; + if (slot==255) break; + unsigned short baseAddr=slotMap[slot]; + rWrite(baseAddr+ADDR_WS,op.ws&((oplType==3)?7:3)); + } + break; + } + case DIV_CMD_FM_RS: { + if (oplType<2) break; + int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2; + if (c.value<0) { + for (int i=0; i=ops) break; + DivInstrumentFM::Operator& op=chan[c.chan].state.op[(ops==4)?orderedOpsL[c.value]:c.value]; + op.ksl=c.value2&3; + unsigned char slot=slots[c.value][c.chan]; + if (slot==255) break; + unsigned short baseAddr=slotMap[slot]; + if (isMuted[c.chan]) { + rWrite(baseAddr+ADDR_KSL_TL,63|(op.ksl<<6)); + } else { + if (isOutputL[ops==4][chan[c.chan].state.alg][c.value]) { + rWrite(baseAddr+ADDR_KSL_TL,(63-(((63-op.tl)*(chan[c.chan].outVol&0x3f))/63))|(op.ksl<<6)); + } else { + rWrite(baseAddr+ADDR_KSL_TL,op.tl|(op.ksl<<6)); + } + } + } + break; + } case DIV_CMD_FM_EXTCH: { if (!properDrumsSys) break; properDrums=c.value; diff --git a/src/engine/platform/opll.cpp b/src/engine/platform/opll.cpp index 0e4e92d61..1e29a1e4e 100644 --- a/src/engine/platform/opll.cpp +++ b/src/engine/platform/opll.cpp @@ -637,6 +637,150 @@ int DivPlatformOPLL::dispatch(DivCommand c) { rWrite(0x05,(car.ar<<4)|(car.dr)); break; } + case DIV_CMD_FM_DR: { + if (c.chan>=9 && !properDrums) return 0; + DivInstrumentFM::Operator& mod=chan[c.chan].state.op[0]; + DivInstrumentFM::Operator& car=chan[c.chan].state.op[1]; + if (c.value<0) { + mod.dr=c.value2&15; + car.dr=c.value2&15; + } else { + if (c.value==0) { + mod.dr=c.value2&15; + } else { + car.dr=c.value2&15; + } + } + rWrite(0x04,(mod.ar<<4)|(mod.dr)); + rWrite(0x05,(car.ar<<4)|(car.dr)); + break; + } + case DIV_CMD_FM_SL: { + if (c.chan>=9 && !properDrums) return 0; + DivInstrumentFM::Operator& mod=chan[c.chan].state.op[0]; + DivInstrumentFM::Operator& car=chan[c.chan].state.op[1]; + if (c.value<0) { + mod.sl=c.value2&15; + car.sl=c.value2&15; + } else { + if (c.value==0) { + mod.sl=c.value2&15; + } else { + car.sl=c.value2&15; + } + } + rWrite(0x06,(mod.sl<<4)|(mod.rr)); + rWrite(0x07,(car.sl<<4)|(car.rr)); + break; + } + case DIV_CMD_FM_RR: { + if (c.chan>=9 && !properDrums) return 0; + DivInstrumentFM::Operator& mod=chan[c.chan].state.op[0]; + DivInstrumentFM::Operator& car=chan[c.chan].state.op[1]; + if (c.value<0) { + mod.rr=c.value2&15; + car.rr=c.value2&15; + } else { + if (c.value==0) { + mod.rr=c.value2&15; + } else { + car.rr=c.value2&15; + } + } + rWrite(0x06,(mod.sl<<4)|(mod.rr)); + rWrite(0x07,(car.sl<<4)|(car.rr)); + break; + } + case DIV_CMD_FM_AM: { + if (c.chan>=9 && !properDrums) return 0; + DivInstrumentFM::Operator& mod=chan[c.chan].state.op[0]; + DivInstrumentFM::Operator& car=chan[c.chan].state.op[1]; + if (c.value<0) { + mod.am=c.value2&1; + car.am=c.value2&1; + } else { + if (c.value==0) { + mod.am=c.value2&1; + } else { + car.am=c.value2&1; + } + } + rWrite(0x00,(mod.am<<7)|(mod.vib<<6)|((mod.ssgEnv&8)<<2)|(mod.ksr<<4)|(mod.mult)); + rWrite(0x01,(car.am<<7)|(car.vib<<6)|((car.ssgEnv&8)<<2)|(car.ksr<<4)|(car.mult)); + break; + } + case DIV_CMD_FM_VIB: { + if (c.chan>=9 && !properDrums) return 0; + DivInstrumentFM::Operator& mod=chan[c.chan].state.op[0]; + DivInstrumentFM::Operator& car=chan[c.chan].state.op[1]; + if (c.value<0) { + mod.vib=c.value2&1; + car.vib=c.value2&1; + } else { + if (c.value==0) { + mod.vib=c.value2&1; + } else { + car.vib=c.value2&1; + } + } + rWrite(0x00,(mod.am<<7)|(mod.vib<<6)|((mod.ssgEnv&8)<<2)|(mod.ksr<<4)|(mod.mult)); + rWrite(0x01,(car.am<<7)|(car.vib<<6)|((car.ssgEnv&8)<<2)|(car.ksr<<4)|(car.mult)); + break; + } + case DIV_CMD_FM_KSR: { + if (c.chan>=9 && !properDrums) return 0; + DivInstrumentFM::Operator& mod=chan[c.chan].state.op[0]; + DivInstrumentFM::Operator& car=chan[c.chan].state.op[1]; + if (c.value<0) { + mod.ksr=c.value2&1; + car.ksr=c.value2&1; + } else { + if (c.value==0) { + mod.ksr=c.value2&1; + } else { + car.ksr=c.value2&1; + } + } + rWrite(0x00,(mod.am<<7)|(mod.vib<<6)|((mod.ssgEnv&8)<<2)|(mod.ksr<<4)|(mod.mult)); + rWrite(0x01,(car.am<<7)|(car.vib<<6)|((car.ssgEnv&8)<<2)|(car.ksr<<4)|(car.mult)); + break; + } + case DIV_CMD_FM_SUS: { + if (c.chan>=9 && !properDrums) return 0; + DivInstrumentFM::Operator& mod=chan[c.chan].state.op[0]; + DivInstrumentFM::Operator& car=chan[c.chan].state.op[1]; + if (c.value<0) { + mod.ssgEnv=c.value2?8:0; + car.ssgEnv=c.value2?8:0; + } else { + if (c.value==0) { + mod.ssgEnv=c.value2?8:0; + } else { + car.ssgEnv=c.value2?8:0; + } + } + rWrite(0x00,(mod.am<<7)|(mod.vib<<6)|((mod.ssgEnv&8)<<2)|(mod.ksr<<4)|(mod.mult)); + rWrite(0x01,(car.am<<7)|(car.vib<<6)|((car.ssgEnv&8)<<2)|(car.ksr<<4)|(car.mult)); + break; + } + case DIV_CMD_FM_RS: { + if (c.chan>=9 && !properDrums) return 0; + DivInstrumentFM::Operator& mod=chan[c.chan].state.op[0]; + DivInstrumentFM::Operator& car=chan[c.chan].state.op[1]; + if (c.value<0) { + mod.ksl=c.value2&3; + car.ksl=c.value2&3; + } else { + if (c.value==0) { + mod.ksl=c.value2&3; + } else { + car.ksl=c.value2&3; + } + } + rWrite(0x02,(mod.ksl<<6)|(mod.tl&63)); + rWrite(0x03,(car.ksl<<6)|((chan[c.chan].state.fms&1)<<4)|((chan[c.chan].state.ams&1)<<3)|chan[c.chan].state.fb); + break; + } case DIV_CMD_FM_EXTCH: if (!properDrumsSys) break; if ((int)properDrums==c.value) break; diff --git a/src/engine/sysDef.cpp b/src/engine/sysDef.cpp index 1a3714019..981358ed4 100644 --- a/src/engine/sysDef.cpp +++ b/src/engine/sysDef.cpp @@ -542,7 +542,6 @@ void DivEngine::registerSystems() { OP_EFFECT_MULTI(0x58,DIV_CMD_FM_DR,1,15); OP_EFFECT_SINGLE(0x5b,DIV_CMD_FM_KSR,2,1); - OP_EFFECT_SINGLE(0x2a,DIV_CMD_FM_WS,4,7); default: return false; } @@ -608,6 +607,7 @@ void DivEngine::registerSystems() { OP_EFFECT_MULTI(0x5a,DIV_CMD_FM_DR,3,15); OP_EFFECT_SINGLE(0x5b,DIV_CMD_FM_KSR,4,1); + OP_EFFECT_SINGLE(0x2a,DIV_CMD_FM_WS,4,7); default: return false; From 6e7b4d7e7f6d5e04e705ea55703b77ecf8f2c68a Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 4 May 2022 17:39:22 -0500 Subject: [PATCH 319/342] the final part of extra FM effects --- TODO.md | 1 - papers/doc/7-systems/opll.md | 8 ++-- papers/doc/7-systems/ym2612.md | 16 +++---- src/engine/platform/arcade.cpp | 48 ++++++++++++++++++++ src/engine/platform/genesis.cpp | 48 ++++++++++++++++++++ src/engine/platform/opl.cpp | 42 +++++++++++++++++ src/engine/platform/opll.cpp | 30 +++++++++++++ src/engine/platform/tx81z.cpp | 80 ++++++++++++++++++++++++++++++++- src/engine/platform/ym2610.cpp | 48 ++++++++++++++++++++ src/engine/platform/ym2610b.cpp | 48 ++++++++++++++++++++ src/gui/guiConst.cpp | 32 ++++++------- 11 files changed, 370 insertions(+), 31 deletions(-) diff --git a/TODO.md b/TODO.md index 710e6091e..80357e3ef 100644 --- a/TODO.md +++ b/TODO.md @@ -12,7 +12,6 @@ - SCC/SCC+ system - maybe YMU759 ADPCM channel - ADPCM chips -- more effects for FM param control - Game Boy envelope macro/sequence - option to display chip names instead of "multi-system" on title bar - rewrite the system name detection function anyway diff --git a/papers/doc/7-systems/opll.md b/papers/doc/7-systems/opll.md index 4e91c137d..d0e6475c7 100644 --- a/papers/doc/7-systems/opll.md +++ b/papers/doc/7-systems/opll.md @@ -2,17 +2,17 @@ the YM2413, otherwise known as OPLL, is a cost-reduced FM synthesis sound chip, based on the Yamaha YM3812 (OPL2). thought OPL was downgraded enough? :p -OPLL spawned also a few derivative chips, the best known of these is: -- the myth. the legend. THE VRC7. 6 channels, *rather interesting* instruments sound bank, no drums mode +OPLL also spawned a few derivative chips, the best known of these is: +- the famous Konami VRC7. used in the Japan-only video game Lagrange Point, it was **another** cost reduction on top of the OPLL! this time just 6 channels... - Yamaha YM2423, same chip as YM2413, just a different patch set -- Yamaha YMF281, ditto +- Yamaha YMF281, ditto # technical specifications the YM2413 is equipped with the following features: - 9 channels of 2 operator FM synthesis -- A drum/percussion mode, replacing the last 3 voices with 3 rhythm channels +- A drum/percussion mode, replacing the last 3 voices with 5 rhythm channels - 1 user-definable patch (this patch can be changed throughout the course of the song) - 15 pre-defined patches which can all be used at the same time - Support for ADSR on both the modulator and the carrier diff --git a/papers/doc/7-systems/ym2612.md b/papers/doc/7-systems/ym2612.md index c32521f2c..e86dde9da 100644 --- a/papers/doc/7-systems/ym2612.md +++ b/papers/doc/7-systems/ym2612.md @@ -39,14 +39,14 @@ one of two chips that powered the Sega Genesis. It is a six-channel, four-operat - `53xy` set DT of operator. - `x` is the operator (1-4). a value of 0 means "all operators". - `y` is the value: - - 0: +0 - - 1: +1 - - 2: +2 - - 3: +3 - - 4: -0 - - 5: -3 - - 6: -2 - - 7: -1 + - 0: -3 + - 1: -2 + - 2: -1 + - 3: 0 + - 4: 1 + - 5: 2 + - 6: 3 + - 7: -0 - `54xy` set RS of operator. - `x` is the operator (1-4). a value of 0 means "all operators". - `y` is the value. diff --git a/src/engine/platform/arcade.cpp b/src/engine/platform/arcade.cpp index 2c6161e53..6bd4e2034 100644 --- a/src/engine/platform/arcade.cpp +++ b/src/engine/platform/arcade.cpp @@ -135,6 +135,54 @@ const char* DivPlatformArcade::getEffectName(unsigned char effect) { case 0x30: return "30xx: Toggle hard envelope reset on new notes"; break; + case 0x50: + return "50xy: Set AM (x: operator from 1 to 4 (0 for all ops); y: AM)"; + break; + case 0x51: + return "51xy: Set sustain level (x: operator from 1 to 4 (0 for all ops); y: sustain)"; + break; + case 0x52: + return "52xy: Set release (x: operator from 1 to 4 (0 for all ops); y: release)"; + break; + case 0x53: + return "53xy: Set detune (x: operator from 1 to 4 (0 for all ops); y: detune where 3 is center)"; + break; + case 0x54: + return "54xy: Set envelope scale (x: operator from 1 to 4 (0 for all ops); y: scale from 0 to 3)"; + break; + case 0x55: + return "55xy: Set detune 2 (x: operator from 1 to 4 (0 for all ops); y: detune from 0 to 3)"; + break; + case 0x56: + return "56xx: Set decay of all operators (0 to 1F)"; + break; + case 0x57: + return "57xx: Set decay of operator 1 (0 to 1F)"; + break; + case 0x58: + return "58xx: Set decay of operator 2 (0 to 1F)"; + break; + case 0x59: + return "59xx: Set decay of operator 3 (0 to 1F)"; + break; + case 0x5a: + return "5Axx: Set decay of operator 4 (0 to 1F)"; + break; + case 0x5b: + return "5Bxx: Set decay 2 of all operators (0 to 1F)"; + break; + case 0x5c: + return "5Cxx: Set decay 2 of operator 1 (0 to 1F)"; + break; + case 0x5d: + return "5Dxx: Set decay 2 of operator 2 (0 to 1F)"; + break; + case 0x5e: + return "5Exx: Set decay 2 of operator 3 (0 to 1F)"; + break; + case 0x5f: + return "5Fxx: Set decay 2 of operator 4 (0 to 1F)"; + break; } return NULL; } diff --git a/src/engine/platform/genesis.cpp b/src/engine/platform/genesis.cpp index 67970298a..0f665cba1 100644 --- a/src/engine/platform/genesis.cpp +++ b/src/engine/platform/genesis.cpp @@ -77,6 +77,54 @@ const char* DivPlatformGenesis::getEffectName(unsigned char effect) { case 0x30: return "30xx: Toggle hard envelope reset on new notes"; break; + case 0x50: + return "50xy: Set AM (x: operator from 1 to 4 (0 for all ops); y: AM)"; + break; + case 0x51: + return "51xy: Set sustain level (x: operator from 1 to 4 (0 for all ops); y: sustain)"; + break; + case 0x52: + return "52xy: Set release (x: operator from 1 to 4 (0 for all ops); y: release)"; + break; + case 0x53: + return "53xy: Set detune (x: operator from 1 to 4 (0 for all ops); y: detune where 3 is center)"; + break; + case 0x54: + return "54xy: Set envelope scale (x: operator from 1 to 4 (0 for all ops); y: scale from 0 to 3)"; + break; + case 0x55: + return "55xy: Set SSG envelope (x: operator from 1 to 4 (0 for all ops); y: 0-7 on, 8 off)"; + break; + case 0x56: + return "56xx: Set decay of all operators (0 to 1F)"; + break; + case 0x57: + return "57xx: Set decay of operator 1 (0 to 1F)"; + break; + case 0x58: + return "58xx: Set decay of operator 2 (0 to 1F)"; + break; + case 0x59: + return "59xx: Set decay of operator 3 (0 to 1F)"; + break; + case 0x5a: + return "5Axx: Set decay of operator 4 (0 to 1F)"; + break; + case 0x5b: + return "5Bxx: Set decay 2 of all operators (0 to 1F)"; + break; + case 0x5c: + return "5Cxx: Set decay 2 of operator 1 (0 to 1F)"; + break; + case 0x5d: + return "5Dxx: Set decay 2 of operator 2 (0 to 1F)"; + break; + case 0x5e: + return "5Exx: Set decay 2 of operator 3 (0 to 1F)"; + break; + case 0x5f: + return "5Fxx: Set decay 2 of operator 4 (0 to 1F)"; + break; } return NULL; } diff --git a/src/engine/platform/opl.cpp b/src/engine/platform/opl.cpp index 7dad9e1c8..c056231ee 100644 --- a/src/engine/platform/opl.cpp +++ b/src/engine/platform/opl.cpp @@ -197,6 +197,48 @@ const char* DivPlatformOPL::getEffectName(unsigned char effect) { case 0x1d: return "1Dxx: Set attack of operator 4 (0 to F; 4-op only)"; break; + case 0x2a: + return "2Axy: Set waveform (x: operator from 1 to 4 (0 for all ops); y: waveform from 0 to 3 in OPL2 and 0 to 7 in OPL3)"; + break; + case 0x30: + return "30xx: Toggle hard envelope reset on new notes"; + break; + case 0x50: + return "50xy: Set AM (x: operator from 1 to 4 (0 for all ops); y: AM)"; + break; + case 0x51: + return "51xy: Set sustain level (x: operator from 1 to 4 (0 for all ops); y: sustain)"; + break; + case 0x52: + return "52xy: Set release (x: operator from 1 to 4 (0 for all ops); y: release)"; + break; + case 0x53: + return "53xy: Set vibrato (x: operator from 1 to 4 (0 for all ops); y: enabled)"; + break; + case 0x54: + return "54xy: Set key scale level (x: operator from 1 to 4 (0 for all ops); y: level from 0 to 3)"; + break; + case 0x55: + return "55xy: Set envelope sustain (x: operator from 1 to 4 (0 for all ops); y: enabled)"; + break; + case 0x56: + return "56xx: Set decay of all operators (0 to F)"; + break; + case 0x57: + return "57xx: Set decay of operator 1 (0 to F)"; + break; + case 0x58: + return "58xx: Set decay of operator 2 (0 to F)"; + break; + case 0x59: + return "59xx: Set decay of operator 3 (0 to F; 4-op only)"; + break; + case 0x5a: + return "5Axx: Set decay of operator 4 (0 to F; 4-op only)"; + break; + case 0x5b: + return "5Bxy: Set whether key will scale envelope (x: operator from 1 to 4 (0 for all ops); y: enabled)"; + break; } return NULL; } diff --git a/src/engine/platform/opll.cpp b/src/engine/platform/opll.cpp index 1e29a1e4e..70b25dc0f 100644 --- a/src/engine/platform/opll.cpp +++ b/src/engine/platform/opll.cpp @@ -55,6 +55,36 @@ const char* DivPlatformOPLL::getEffectName(unsigned char effect) { case 0x1b: return "1Bxx: Set attack of operator 2 (0 to F)"; break; + case 0x50: + return "50xy: Set AM (x: operator from 1 to 2 (0 for all ops); y: AM)"; + break; + case 0x51: + return "51xy: Set sustain level (x: operator from 1 to 2 (0 for all ops); y: sustain)"; + break; + case 0x52: + return "52xy: Set release (x: operator from 1 to 2 (0 for all ops); y: release)"; + break; + case 0x53: + return "53xy: Set vibrato (x: operator from 1 to 2 (0 for all ops); y: enabled)"; + break; + case 0x54: + return "54xy: Set key scale level (x: operator from 1 to 2 (0 for all ops); y: level from 0 to 3)"; + break; + case 0x55: + return "55xy: Set envelope sustain (x: operator from 1 to 2 (0 for all ops); y: enabled)"; + break; + case 0x56: + return "56xx: Set decay of all operators (0 to F)"; + break; + case 0x57: + return "57xx: Set decay of operator 1 (0 to F)"; + break; + case 0x58: + return "58xx: Set decay of operator 2 (0 to F)"; + break; + case 0x5b: + return "5Bxy: Set whether key will scale envelope (x: operator from 1 to 2 (0 for all ops); y: enabled)"; + break; } return NULL; } diff --git a/src/engine/platform/tx81z.cpp b/src/engine/platform/tx81z.cpp index 277de8835..056eeaacb 100644 --- a/src/engine/platform/tx81z.cpp +++ b/src/engine/platform/tx81z.cpp @@ -137,8 +137,84 @@ const char* DivPlatformTX81Z::getEffectName(unsigned char effect) { case 0x1f: return "1Fxx: Set PM depth (0 to 7F)"; break; - case 0x30: - return "30xx: Toggle hard envelope reset on new notes"; + case 0x28: + return "28xy: Set reverb (x: operator from 1 to 4 (0 for all ops); y: reverb from 0 to 7)"; + break; + case 0x2a: + return "2Axy: Set waveform (x: operator from 1 to 4 (0 for all ops); y: waveform from 0 to 7)"; + break; + case 0x2b: + return "2Bxy: Set envelope generator shift (x: operator from 1 to 4 (0 for all ops); y: shift from 0 to 3)"; + break; + case 0x2c: + return "2Cxy: Set fine multiplier (x: operator from 1 to 4 (0 for all ops); y: fine)"; + break; + case 0x2f: + return "2Fxx: Toggle hard envelope reset on new notes"; + break; + case 0x30: case 0x31: case 0x32: case 0x33: + case 0x34: case 0x35: case 0x36: case 0x37: + return "3xyy: Set fixed frequency of operator 1 (x: octave from 0 to 7; y: frequency)"; + break; + case 0x38: case 0x39: case 0x3a: case 0x3b: + case 0x3c: case 0x3d: case 0x3e: case 0x3f: + return "3xyy: Set fixed frequency of operator 2 (x: octave from 8 to F; y: frequency)"; + break; + case 0x40: case 0x41: case 0x42: case 0x43: + case 0x44: case 0x45: case 0x46: case 0x47: + return "4xyy: Set fixed frequency of operator 3 (x: octave from 0 to 7; y: frequency)"; + break; + case 0x48: case 0x49: case 0x4a: case 0x4b: + case 0x4c: case 0x4d: case 0x4e: case 0x4f: + return "4xyy: Set fixed frequency of operator 4 (x: octave from 8 to F; y: frequency)"; + break; + case 0x50: + return "50xy: Set AM (x: operator from 1 to 4 (0 for all ops); y: AM)"; + break; + case 0x51: + return "51xy: Set sustain level (x: operator from 1 to 4 (0 for all ops); y: sustain)"; + break; + case 0x52: + return "52xy: Set release (x: operator from 1 to 4 (0 for all ops); y: release)"; + break; + case 0x53: + return "53xy: Set detune (x: operator from 1 to 4 (0 for all ops); y: detune where 3 is center)"; + break; + case 0x54: + return "54xy: Set envelope scale (x: operator from 1 to 4 (0 for all ops); y: scale from 0 to 3)"; + break; + case 0x55: + return "55xy: Set detune 2 (x: operator from 1 to 4 (0 for all ops); y: detune from 0 to 3)"; + break; + case 0x56: + return "56xx: Set decay of all operators (0 to 1F)"; + break; + case 0x57: + return "57xx: Set decay of operator 1 (0 to 1F)"; + break; + case 0x58: + return "58xx: Set decay of operator 2 (0 to 1F)"; + break; + case 0x59: + return "59xx: Set decay of operator 3 (0 to 1F)"; + break; + case 0x5a: + return "5Axx: Set decay of operator 4 (0 to 1F)"; + break; + case 0x5b: + return "5Bxx: Set decay 2 of all operators (0 to 1F)"; + break; + case 0x5c: + return "5Cxx: Set decay 2 of operator 1 (0 to 1F)"; + break; + case 0x5d: + return "5Dxx: Set decay 2 of operator 2 (0 to 1F)"; + break; + case 0x5e: + return "5Exx: Set decay 2 of operator 3 (0 to 1F)"; + break; + case 0x5f: + return "5Fxx: Set decay 2 of operator 4 (0 to 1F)"; break; } return NULL; diff --git a/src/engine/platform/ym2610.cpp b/src/engine/platform/ym2610.cpp index 85aa36339..54bbbd213 100644 --- a/src/engine/platform/ym2610.cpp +++ b/src/engine/platform/ym2610.cpp @@ -397,6 +397,54 @@ const char* DivPlatformYM2610::getEffectName(unsigned char effect) { case 0x30: return "30xx: Toggle hard envelope reset on new notes"; break; + case 0x50: + return "50xy: Set AM (x: operator from 1 to 4 (0 for all ops); y: AM)"; + break; + case 0x51: + return "51xy: Set sustain level (x: operator from 1 to 4 (0 for all ops); y: sustain)"; + break; + case 0x52: + return "52xy: Set release (x: operator from 1 to 4 (0 for all ops); y: release)"; + break; + case 0x53: + return "53xy: Set detune (x: operator from 1 to 4 (0 for all ops); y: detune where 3 is center)"; + break; + case 0x54: + return "54xy: Set envelope scale (x: operator from 1 to 4 (0 for all ops); y: scale from 0 to 3)"; + break; + case 0x55: + return "55xy: Set SSG envelope (x: operator from 1 to 4 (0 for all ops); y: 0-7 on, 8 off)"; + break; + case 0x56: + return "56xx: Set decay of all operators (0 to 1F)"; + break; + case 0x57: + return "57xx: Set decay of operator 1 (0 to 1F)"; + break; + case 0x58: + return "58xx: Set decay of operator 2 (0 to 1F)"; + break; + case 0x59: + return "59xx: Set decay of operator 3 (0 to 1F)"; + break; + case 0x5a: + return "5Axx: Set decay of operator 4 (0 to 1F)"; + break; + case 0x5b: + return "5Bxx: Set decay 2 of all operators (0 to 1F)"; + break; + case 0x5c: + return "5Cxx: Set decay 2 of operator 1 (0 to 1F)"; + break; + case 0x5d: + return "5Dxx: Set decay 2 of operator 2 (0 to 1F)"; + break; + case 0x5e: + return "5Exx: Set decay 2 of operator 3 (0 to 1F)"; + break; + case 0x5f: + return "5Fxx: Set decay 2 of operator 4 (0 to 1F)"; + break; } return NULL; } diff --git a/src/engine/platform/ym2610b.cpp b/src/engine/platform/ym2610b.cpp index 83aa0fb9c..b36cc87f5 100644 --- a/src/engine/platform/ym2610b.cpp +++ b/src/engine/platform/ym2610b.cpp @@ -377,6 +377,54 @@ const char* DivPlatformYM2610B::getEffectName(unsigned char effect) { case 0x30: return "30xx: Toggle hard envelope reset on new notes"; break; + case 0x50: + return "50xy: Set AM (x: operator from 1 to 4 (0 for all ops); y: AM)"; + break; + case 0x51: + return "51xy: Set sustain level (x: operator from 1 to 4 (0 for all ops); y: sustain)"; + break; + case 0x52: + return "52xy: Set release (x: operator from 1 to 4 (0 for all ops); y: release)"; + break; + case 0x53: + return "53xy: Set detune (x: operator from 1 to 4 (0 for all ops); y: detune where 3 is center)"; + break; + case 0x54: + return "54xy: Set envelope scale (x: operator from 1 to 4 (0 for all ops); y: scale from 0 to 3)"; + break; + case 0x55: + return "55xy: Set SSG envelope (x: operator from 1 to 4 (0 for all ops); y: 0-7 on, 8 off)"; + break; + case 0x56: + return "56xx: Set decay of all operators (0 to 1F)"; + break; + case 0x57: + return "57xx: Set decay of operator 1 (0 to 1F)"; + break; + case 0x58: + return "58xx: Set decay of operator 2 (0 to 1F)"; + break; + case 0x59: + return "59xx: Set decay of operator 3 (0 to 1F)"; + break; + case 0x5a: + return "5Axx: Set decay of operator 4 (0 to 1F)"; + break; + case 0x5b: + return "5Bxx: Set decay 2 of all operators (0 to 1F)"; + break; + case 0x5c: + return "5Cxx: Set decay 2 of operator 1 (0 to 1F)"; + break; + case 0x5d: + return "5Dxx: Set decay 2 of operator 2 (0 to 1F)"; + break; + case 0x5e: + return "5Exx: Set decay 2 of operator 3 (0 to 1F)"; + break; + case 0x5f: + return "5Fxx: Set decay 2 of operator 4 (0 to 1F)"; + break; } return NULL; } diff --git a/src/gui/guiConst.cpp b/src/gui/guiConst.cpp index 0cc1894ab..ca0647397 100644 --- a/src/gui/guiConst.cpp +++ b/src/gui/guiConst.cpp @@ -233,22 +233,22 @@ const FurnaceGUIColors fxColors[256]={ GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, // 50-5F - GUI_COLOR_PATTERN_EFFECT_INVALID, - GUI_COLOR_PATTERN_EFFECT_INVALID, - GUI_COLOR_PATTERN_EFFECT_INVALID, - GUI_COLOR_PATTERN_EFFECT_INVALID, - GUI_COLOR_PATTERN_EFFECT_INVALID, - GUI_COLOR_PATTERN_EFFECT_INVALID, - GUI_COLOR_PATTERN_EFFECT_INVALID, - GUI_COLOR_PATTERN_EFFECT_INVALID, - GUI_COLOR_PATTERN_EFFECT_INVALID, - GUI_COLOR_PATTERN_EFFECT_INVALID, - GUI_COLOR_PATTERN_EFFECT_INVALID, - GUI_COLOR_PATTERN_EFFECT_INVALID, - GUI_COLOR_PATTERN_EFFECT_INVALID, - GUI_COLOR_PATTERN_EFFECT_INVALID, - GUI_COLOR_PATTERN_EFFECT_INVALID, - GUI_COLOR_PATTERN_EFFECT_INVALID, + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, + GUI_COLOR_PATTERN_EFFECT_SYS_PRIMARY, // 60-6F GUI_COLOR_PATTERN_EFFECT_INVALID, From e50df18393bb641268e07a95cd683160d131638a Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 4 May 2022 17:43:06 -0500 Subject: [PATCH 320/342] OPLL: fix compatible drum mode --- src/engine/platform/opll.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/engine/platform/opll.cpp b/src/engine/platform/opll.cpp index 70b25dc0f..b4f0916a8 100644 --- a/src/engine/platform/opll.cpp +++ b/src/engine/platform/opll.cpp @@ -477,6 +477,7 @@ int DivPlatformOPLL::dispatch(DivCommand c) { if (drums) { drums=false; immWrite(0x0e,0); + drumState=0; } } if (c.chan<9) { From b75336bcb6d065ad81a15e41e76c562ff2fec04d Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 4 May 2022 18:06:32 -0500 Subject: [PATCH 321/342] OPL: fix channel muting - PLEASE READ! **SUBMODULES UPDATED** - Nuked-OPL3 is no longer a submodule as I had to directly modify the core for this if you mess up while removing Nuked-OPL3 from your repo, delete everything and clone again fixes #414 --- .gitmodules | 3 - CMakeLists.txt | 2 +- extern/Nuked-OPL3 | 1 - extern/opl/.github/FUNDING.yml | 12 + extern/opl/LICENSE | 504 +++++++++++ extern/opl/MODIFIED.md | 4 + extern/opl/opl3.c | 1476 ++++++++++++++++++++++++++++++++ extern/opl/opl3.h | 168 ++++ src/engine/platform/opl.cpp | 9 + src/engine/platform/opl.h | 2 +- 10 files changed, 2175 insertions(+), 6 deletions(-) delete mode 160000 extern/Nuked-OPL3 create mode 100644 extern/opl/.github/FUNDING.yml create mode 100644 extern/opl/LICENSE create mode 100644 extern/opl/MODIFIED.md create mode 100644 extern/opl/opl3.c create mode 100644 extern/opl/opl3.h diff --git a/.gitmodules b/.gitmodules index d63fd70b5..435f43c75 100644 --- a/.gitmodules +++ b/.gitmodules @@ -19,6 +19,3 @@ [submodule "extern/adpcm"] path = extern/adpcm url = https://github.com/superctr/adpcm -[submodule "extern/Nuked-OPL3"] - path = extern/Nuked-OPL3 - url = https://github.com/nukeykt/Nuked-OPL3.git diff --git a/CMakeLists.txt b/CMakeLists.txt index a881bad60..eaa1781d7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -237,7 +237,7 @@ extern/adpcm/ymz_codec.c extern/Nuked-OPN2/ym3438.c extern/opm/opm.c extern/Nuked-OPLL/opll.c -extern/Nuked-OPL3/opl3.c +extern/opl/opl3.c src/engine/platform/sound/sn76496.cpp src/engine/platform/sound/ay8910.cpp src/engine/platform/sound/saa1099.cpp diff --git a/extern/Nuked-OPL3 b/extern/Nuked-OPL3 deleted file mode 160000 index bb5c8d08a..000000000 --- a/extern/Nuked-OPL3 +++ /dev/null @@ -1 +0,0 @@ -Subproject commit bb5c8d08a85779c42b75c79d7b84f365a1b93b66 diff --git a/extern/opl/.github/FUNDING.yml b/extern/opl/.github/FUNDING.yml new file mode 100644 index 000000000..613b2dd88 --- /dev/null +++ b/extern/opl/.github/FUNDING.yml @@ -0,0 +1,12 @@ +# These are supported funding model platforms + +github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] +patreon: nukeykt # Replace with a single Patreon username +open_collective: # Replace with a single Open Collective username +ko_fi: # Replace with a single Ko-fi username +tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel +community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry +liberapay: # Replace with a single Liberapay username +issuehunt: # Replace with a single IssueHunt username +otechie: # Replace with a single Otechie username +custom: ['https://www.buymeacoffee.com/nukeykt', 'https://paypal.me/nukeykt'] # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] diff --git a/extern/opl/LICENSE b/extern/opl/LICENSE new file mode 100644 index 000000000..8000a6faa --- /dev/null +++ b/extern/opl/LICENSE @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random + Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/extern/opl/MODIFIED.md b/extern/opl/MODIFIED.md new file mode 100644 index 000000000..78341ad48 --- /dev/null +++ b/extern/opl/MODIFIED.md @@ -0,0 +1,4 @@ +# modification disclaimer + +this is a modified version of Nuked-OPL3 which implements channel muting in the core. +see [this issue](https://github.com/tildearrow/furnace/issues/414) for more information. diff --git a/extern/opl/opl3.c b/extern/opl/opl3.c new file mode 100644 index 000000000..b0a090c5f --- /dev/null +++ b/extern/opl/opl3.c @@ -0,0 +1,1476 @@ +/* Nuked OPL3 + * Copyright (C) 2013-2020 Nuke.YKT + * + * This file is part of Nuked OPL3. + * + * Nuked OPL3 is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 + * of the License, or (at your option) any later version. + * + * Nuked OPL3 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Nuked OPL3. If not, see . + + * Nuked OPL3 emulator. + * Thanks: + * MAME Development Team(Jarek Burczynski, Tatsuyuki Satoh): + * Feedback and Rhythm part calculation information. + * forums.submarine.org.uk(carbon14, opl3): + * Tremolo and phase generator calculation information. + * OPLx decapsulated(Matthew Gambrell, Olli Niemitalo): + * OPL2 ROMs. + * siliconpr0n.org(John McMaster, digshadow): + * YMF262 and VRC VII decaps and die shots. + * + * version: 1.8 + */ + +#include +#include +#include +#include "opl3.h" + +#if OPL_ENABLE_STEREOEXT && !defined OPL_SIN +#ifndef _USE_MATH_DEFINES +#define _USE_MATH_DEFINES 1 +#endif +#include +/* input: [0, 256), output: [0, 65536] */ +#define OPL_SIN(x) ((int32_t)(sin((x) * M_PI / 512.0) * 65536.0)) +#endif + +/* Quirk: Some FM channels are output one sample later on the left side than the right. */ +#ifndef OPL_QUIRK_CHANNELSAMPLEDELAY +#define OPL_QUIRK_CHANNELSAMPLEDELAY (!OPL_ENABLE_STEREOEXT) +#endif + +#define RSM_FRAC 10 + +/* Channel types */ + +enum { + ch_2op = 0, + ch_4op = 1, + ch_4op2 = 2, + ch_drum = 3 +}; + +/* Envelope key types */ + +enum { + egk_norm = 0x01, + egk_drum = 0x02 +}; + + +/* + logsin table +*/ + +static const uint16_t logsinrom[256] = { + 0x859, 0x6c3, 0x607, 0x58b, 0x52e, 0x4e4, 0x4a6, 0x471, + 0x443, 0x41a, 0x3f5, 0x3d3, 0x3b5, 0x398, 0x37e, 0x365, + 0x34e, 0x339, 0x324, 0x311, 0x2ff, 0x2ed, 0x2dc, 0x2cd, + 0x2bd, 0x2af, 0x2a0, 0x293, 0x286, 0x279, 0x26d, 0x261, + 0x256, 0x24b, 0x240, 0x236, 0x22c, 0x222, 0x218, 0x20f, + 0x206, 0x1fd, 0x1f5, 0x1ec, 0x1e4, 0x1dc, 0x1d4, 0x1cd, + 0x1c5, 0x1be, 0x1b7, 0x1b0, 0x1a9, 0x1a2, 0x19b, 0x195, + 0x18f, 0x188, 0x182, 0x17c, 0x177, 0x171, 0x16b, 0x166, + 0x160, 0x15b, 0x155, 0x150, 0x14b, 0x146, 0x141, 0x13c, + 0x137, 0x133, 0x12e, 0x129, 0x125, 0x121, 0x11c, 0x118, + 0x114, 0x10f, 0x10b, 0x107, 0x103, 0x0ff, 0x0fb, 0x0f8, + 0x0f4, 0x0f0, 0x0ec, 0x0e9, 0x0e5, 0x0e2, 0x0de, 0x0db, + 0x0d7, 0x0d4, 0x0d1, 0x0cd, 0x0ca, 0x0c7, 0x0c4, 0x0c1, + 0x0be, 0x0bb, 0x0b8, 0x0b5, 0x0b2, 0x0af, 0x0ac, 0x0a9, + 0x0a7, 0x0a4, 0x0a1, 0x09f, 0x09c, 0x099, 0x097, 0x094, + 0x092, 0x08f, 0x08d, 0x08a, 0x088, 0x086, 0x083, 0x081, + 0x07f, 0x07d, 0x07a, 0x078, 0x076, 0x074, 0x072, 0x070, + 0x06e, 0x06c, 0x06a, 0x068, 0x066, 0x064, 0x062, 0x060, + 0x05e, 0x05c, 0x05b, 0x059, 0x057, 0x055, 0x053, 0x052, + 0x050, 0x04e, 0x04d, 0x04b, 0x04a, 0x048, 0x046, 0x045, + 0x043, 0x042, 0x040, 0x03f, 0x03e, 0x03c, 0x03b, 0x039, + 0x038, 0x037, 0x035, 0x034, 0x033, 0x031, 0x030, 0x02f, + 0x02e, 0x02d, 0x02b, 0x02a, 0x029, 0x028, 0x027, 0x026, + 0x025, 0x024, 0x023, 0x022, 0x021, 0x020, 0x01f, 0x01e, + 0x01d, 0x01c, 0x01b, 0x01a, 0x019, 0x018, 0x017, 0x017, + 0x016, 0x015, 0x014, 0x014, 0x013, 0x012, 0x011, 0x011, + 0x010, 0x00f, 0x00f, 0x00e, 0x00d, 0x00d, 0x00c, 0x00c, + 0x00b, 0x00a, 0x00a, 0x009, 0x009, 0x008, 0x008, 0x007, + 0x007, 0x007, 0x006, 0x006, 0x005, 0x005, 0x005, 0x004, + 0x004, 0x004, 0x003, 0x003, 0x003, 0x002, 0x002, 0x002, + 0x002, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000 +}; + +/* + exp table +*/ + +static const uint16_t exprom[256] = { + 0x7fa, 0x7f5, 0x7ef, 0x7ea, 0x7e4, 0x7df, 0x7da, 0x7d4, + 0x7cf, 0x7c9, 0x7c4, 0x7bf, 0x7b9, 0x7b4, 0x7ae, 0x7a9, + 0x7a4, 0x79f, 0x799, 0x794, 0x78f, 0x78a, 0x784, 0x77f, + 0x77a, 0x775, 0x770, 0x76a, 0x765, 0x760, 0x75b, 0x756, + 0x751, 0x74c, 0x747, 0x742, 0x73d, 0x738, 0x733, 0x72e, + 0x729, 0x724, 0x71f, 0x71a, 0x715, 0x710, 0x70b, 0x706, + 0x702, 0x6fd, 0x6f8, 0x6f3, 0x6ee, 0x6e9, 0x6e5, 0x6e0, + 0x6db, 0x6d6, 0x6d2, 0x6cd, 0x6c8, 0x6c4, 0x6bf, 0x6ba, + 0x6b5, 0x6b1, 0x6ac, 0x6a8, 0x6a3, 0x69e, 0x69a, 0x695, + 0x691, 0x68c, 0x688, 0x683, 0x67f, 0x67a, 0x676, 0x671, + 0x66d, 0x668, 0x664, 0x65f, 0x65b, 0x657, 0x652, 0x64e, + 0x649, 0x645, 0x641, 0x63c, 0x638, 0x634, 0x630, 0x62b, + 0x627, 0x623, 0x61e, 0x61a, 0x616, 0x612, 0x60e, 0x609, + 0x605, 0x601, 0x5fd, 0x5f9, 0x5f5, 0x5f0, 0x5ec, 0x5e8, + 0x5e4, 0x5e0, 0x5dc, 0x5d8, 0x5d4, 0x5d0, 0x5cc, 0x5c8, + 0x5c4, 0x5c0, 0x5bc, 0x5b8, 0x5b4, 0x5b0, 0x5ac, 0x5a8, + 0x5a4, 0x5a0, 0x59c, 0x599, 0x595, 0x591, 0x58d, 0x589, + 0x585, 0x581, 0x57e, 0x57a, 0x576, 0x572, 0x56f, 0x56b, + 0x567, 0x563, 0x560, 0x55c, 0x558, 0x554, 0x551, 0x54d, + 0x549, 0x546, 0x542, 0x53e, 0x53b, 0x537, 0x534, 0x530, + 0x52c, 0x529, 0x525, 0x522, 0x51e, 0x51b, 0x517, 0x514, + 0x510, 0x50c, 0x509, 0x506, 0x502, 0x4ff, 0x4fb, 0x4f8, + 0x4f4, 0x4f1, 0x4ed, 0x4ea, 0x4e7, 0x4e3, 0x4e0, 0x4dc, + 0x4d9, 0x4d6, 0x4d2, 0x4cf, 0x4cc, 0x4c8, 0x4c5, 0x4c2, + 0x4be, 0x4bb, 0x4b8, 0x4b5, 0x4b1, 0x4ae, 0x4ab, 0x4a8, + 0x4a4, 0x4a1, 0x49e, 0x49b, 0x498, 0x494, 0x491, 0x48e, + 0x48b, 0x488, 0x485, 0x482, 0x47e, 0x47b, 0x478, 0x475, + 0x472, 0x46f, 0x46c, 0x469, 0x466, 0x463, 0x460, 0x45d, + 0x45a, 0x457, 0x454, 0x451, 0x44e, 0x44b, 0x448, 0x445, + 0x442, 0x43f, 0x43c, 0x439, 0x436, 0x433, 0x430, 0x42d, + 0x42a, 0x428, 0x425, 0x422, 0x41f, 0x41c, 0x419, 0x416, + 0x414, 0x411, 0x40e, 0x40b, 0x408, 0x406, 0x403, 0x400 +}; + +/* + freq mult table multiplied by 2 + + 1/2, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 12, 12, 15, 15 +*/ + +static const uint8_t mt[16] = { + 1, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 20, 24, 24, 30, 30 +}; + +/* + ksl table +*/ + +static const uint8_t kslrom[16] = { + 0, 32, 40, 45, 48, 51, 53, 55, 56, 58, 59, 60, 61, 62, 63, 64 +}; + +static const uint8_t kslshift[4] = { + 8, 1, 2, 0 +}; + +/* + envelope generator constants +*/ + +static const uint8_t eg_incstep[4][4] = { + { 0, 0, 0, 0 }, + { 1, 0, 0, 0 }, + { 1, 0, 1, 0 }, + { 1, 1, 1, 0 } +}; + +/* + address decoding +*/ + +static const int8_t ad_slot[0x20] = { + 0, 1, 2, 3, 4, 5, -1, -1, 6, 7, 8, 9, 10, 11, -1, -1, + 12, 13, 14, 15, 16, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 +}; + +static const uint8_t ch_slot[18] = { + 0, 1, 2, 6, 7, 8, 12, 13, 14, 18, 19, 20, 24, 25, 26, 30, 31, 32 +}; + +#if OPL_ENABLE_STEREOEXT +/* + stereo extension panning table +*/ + +static int32_t panpot_lut[256]; +static uint8_t panpot_lut_build = 0; +#endif + +/* + Envelope generator +*/ + +typedef int16_t(*envelope_sinfunc)(uint16_t phase, uint16_t envelope); +typedef void(*envelope_genfunc)(opl3_slot *slott); + +static int16_t OPL3_EnvelopeCalcExp(uint32_t level) +{ + if (level > 0x1fff) + { + level = 0x1fff; + } + return (exprom[level & 0xff] << 1) >> (level >> 8); +} + +static int16_t OPL3_EnvelopeCalcSin0(uint16_t phase, uint16_t envelope) +{ + uint16_t out = 0; + uint16_t neg = 0; + phase &= 0x3ff; + if (phase & 0x200) + { + neg = 0xffff; + } + if (phase & 0x100) + { + out = logsinrom[(phase & 0xff) ^ 0xff]; + } + else + { + out = logsinrom[phase & 0xff]; + } + return OPL3_EnvelopeCalcExp(out + (envelope << 3)) ^ neg; +} + +static int16_t OPL3_EnvelopeCalcSin1(uint16_t phase, uint16_t envelope) +{ + uint16_t out = 0; + phase &= 0x3ff; + if (phase & 0x200) + { + out = 0x1000; + } + else if (phase & 0x100) + { + out = logsinrom[(phase & 0xff) ^ 0xff]; + } + else + { + out = logsinrom[phase & 0xff]; + } + return OPL3_EnvelopeCalcExp(out + (envelope << 3)); +} + +static int16_t OPL3_EnvelopeCalcSin2(uint16_t phase, uint16_t envelope) +{ + uint16_t out = 0; + phase &= 0x3ff; + if (phase & 0x100) + { + out = logsinrom[(phase & 0xff) ^ 0xff]; + } + else + { + out = logsinrom[phase & 0xff]; + } + return OPL3_EnvelopeCalcExp(out + (envelope << 3)); +} + +static int16_t OPL3_EnvelopeCalcSin3(uint16_t phase, uint16_t envelope) +{ + uint16_t out = 0; + phase &= 0x3ff; + if (phase & 0x100) + { + out = 0x1000; + } + else + { + out = logsinrom[phase & 0xff]; + } + return OPL3_EnvelopeCalcExp(out + (envelope << 3)); +} + +static int16_t OPL3_EnvelopeCalcSin4(uint16_t phase, uint16_t envelope) +{ + uint16_t out = 0; + uint16_t neg = 0; + phase &= 0x3ff; + if ((phase & 0x300) == 0x100) + { + neg = 0xffff; + } + if (phase & 0x200) + { + out = 0x1000; + } + else if (phase & 0x80) + { + out = logsinrom[((phase ^ 0xff) << 1) & 0xff]; + } + else + { + out = logsinrom[(phase << 1) & 0xff]; + } + return OPL3_EnvelopeCalcExp(out + (envelope << 3)) ^ neg; +} + +static int16_t OPL3_EnvelopeCalcSin5(uint16_t phase, uint16_t envelope) +{ + uint16_t out = 0; + phase &= 0x3ff; + if (phase & 0x200) + { + out = 0x1000; + } + else if (phase & 0x80) + { + out = logsinrom[((phase ^ 0xff) << 1) & 0xff]; + } + else + { + out = logsinrom[(phase << 1) & 0xff]; + } + return OPL3_EnvelopeCalcExp(out + (envelope << 3)); +} + +static int16_t OPL3_EnvelopeCalcSin6(uint16_t phase, uint16_t envelope) +{ + uint16_t neg = 0; + phase &= 0x3ff; + if (phase & 0x200) + { + neg = 0xffff; + } + return OPL3_EnvelopeCalcExp(envelope << 3) ^ neg; +} + +static int16_t OPL3_EnvelopeCalcSin7(uint16_t phase, uint16_t envelope) +{ + uint16_t out = 0; + uint16_t neg = 0; + phase &= 0x3ff; + if (phase & 0x200) + { + neg = 0xffff; + phase = (phase & 0x1ff) ^ 0x1ff; + } + out = phase << 3; + return OPL3_EnvelopeCalcExp(out + (envelope << 3)) ^ neg; +} + +static const envelope_sinfunc envelope_sin[8] = { + OPL3_EnvelopeCalcSin0, + OPL3_EnvelopeCalcSin1, + OPL3_EnvelopeCalcSin2, + OPL3_EnvelopeCalcSin3, + OPL3_EnvelopeCalcSin4, + OPL3_EnvelopeCalcSin5, + OPL3_EnvelopeCalcSin6, + OPL3_EnvelopeCalcSin7 +}; + +enum envelope_gen_num +{ + envelope_gen_num_attack = 0, + envelope_gen_num_decay = 1, + envelope_gen_num_sustain = 2, + envelope_gen_num_release = 3 +}; + +static void OPL3_EnvelopeUpdateKSL(opl3_slot *slot) +{ + int16_t ksl = (kslrom[slot->channel->f_num >> 6] << 2) + - ((0x08 - slot->channel->block) << 5); + if (ksl < 0) + { + ksl = 0; + } + slot->eg_ksl = (uint8_t)ksl; +} + +static void OPL3_EnvelopeCalc(opl3_slot *slot) +{ + uint8_t nonzero; + uint8_t rate; + uint8_t rate_hi; + uint8_t rate_lo; + uint8_t reg_rate = 0; + uint8_t ks; + uint8_t eg_shift, shift; + uint16_t eg_rout; + int16_t eg_inc; + uint8_t eg_off; + uint8_t reset = 0; + slot->eg_out = slot->eg_rout + (slot->reg_tl << 2) + + (slot->eg_ksl >> kslshift[slot->reg_ksl]) + *slot->trem; + if (slot->key && slot->eg_gen == envelope_gen_num_release) + { + reset = 1; + reg_rate = slot->reg_ar; + } + else + { + switch (slot->eg_gen) + { + case envelope_gen_num_attack: + reg_rate = slot->reg_ar; + break; + case envelope_gen_num_decay: + reg_rate = slot->reg_dr; + break; + case envelope_gen_num_sustain: + if (!slot->reg_type) + { + reg_rate = slot->reg_rr; + } + break; + case envelope_gen_num_release: + reg_rate = slot->reg_rr; + break; + } + } + slot->pg_reset = reset; + ks = slot->channel->ksv >> ((slot->reg_ksr ^ 1) << 1); + nonzero = (reg_rate != 0); + rate = ks + (reg_rate << 2); + rate_hi = rate >> 2; + rate_lo = rate & 0x03; + if (rate_hi & 0x10) + { + rate_hi = 0x0f; + } + eg_shift = rate_hi + slot->chip->eg_add; + shift = 0; + if (nonzero) + { + if (rate_hi < 12) + { + if (slot->chip->eg_state) + { + switch (eg_shift) + { + case 12: + shift = 1; + break; + case 13: + shift = (rate_lo >> 1) & 0x01; + break; + case 14: + shift = rate_lo & 0x01; + break; + default: + break; + } + } + } + else + { + shift = (rate_hi & 0x03) + eg_incstep[rate_lo][slot->chip->timer & 0x03]; + if (shift & 0x04) + { + shift = 0x03; + } + if (!shift) + { + shift = slot->chip->eg_state; + } + } + } + eg_rout = slot->eg_rout; + eg_inc = 0; + eg_off = 0; + /* Instant attack */ + if (reset && rate_hi == 0x0f) + { + eg_rout = 0x00; + } + /* Envelope off */ + if ((slot->eg_rout & 0x1f8) == 0x1f8) + { + eg_off = 1; + } + if (slot->eg_gen != envelope_gen_num_attack && !reset && eg_off) + { + eg_rout = 0x1ff; + } + switch (slot->eg_gen) + { + case envelope_gen_num_attack: + if (!slot->eg_rout) + { + slot->eg_gen = envelope_gen_num_decay; + } + else if (slot->key && shift > 0 && rate_hi != 0x0f) + { + eg_inc = ~slot->eg_rout >> (4 - shift); + } + break; + case envelope_gen_num_decay: + if ((slot->eg_rout >> 4) == slot->reg_sl) + { + slot->eg_gen = envelope_gen_num_sustain; + } + else if (!eg_off && !reset && shift > 0) + { + eg_inc = 1 << (shift - 1); + } + break; + case envelope_gen_num_sustain: + case envelope_gen_num_release: + if (!eg_off && !reset && shift > 0) + { + eg_inc = 1 << (shift - 1); + } + break; + } + slot->eg_rout = (eg_rout + eg_inc) & 0x1ff; + /* Key off */ + if (reset) + { + slot->eg_gen = envelope_gen_num_attack; + } + if (!slot->key) + { + slot->eg_gen = envelope_gen_num_release; + } +} + +static void OPL3_EnvelopeKeyOn(opl3_slot *slot, uint8_t type) +{ + slot->key |= type; +} + +static void OPL3_EnvelopeKeyOff(opl3_slot *slot, uint8_t type) +{ + slot->key &= ~type; +} + +/* + Phase Generator +*/ + +static void OPL3_PhaseGenerate(opl3_slot *slot) +{ + opl3_chip *chip; + uint16_t f_num; + uint32_t basefreq; + uint8_t rm_xor, n_bit; + uint32_t noise; + uint16_t phase; + + chip = slot->chip; + f_num = slot->channel->f_num; + if (slot->reg_vib) + { + int8_t range; + uint8_t vibpos; + + range = (f_num >> 7) & 7; + vibpos = slot->chip->vibpos; + + if (!(vibpos & 3)) + { + range = 0; + } + else if (vibpos & 1) + { + range >>= 1; + } + range >>= slot->chip->vibshift; + + if (vibpos & 4) + { + range = -range; + } + f_num += range; + } + basefreq = (f_num << slot->channel->block) >> 1; + phase = (uint16_t)(slot->pg_phase >> 9); + if (slot->pg_reset) + { + slot->pg_phase = 0; + } + slot->pg_phase += (basefreq * mt[slot->reg_mult]) >> 1; + /* Rhythm mode */ + noise = chip->noise; + slot->pg_phase_out = phase; + if (slot->slot_num == 13) /* hh */ + { + chip->rm_hh_bit2 = (phase >> 2) & 1; + chip->rm_hh_bit3 = (phase >> 3) & 1; + chip->rm_hh_bit7 = (phase >> 7) & 1; + chip->rm_hh_bit8 = (phase >> 8) & 1; + } + if (slot->slot_num == 17 && (chip->rhy & 0x20)) /* tc */ + { + chip->rm_tc_bit3 = (phase >> 3) & 1; + chip->rm_tc_bit5 = (phase >> 5) & 1; + } + if (chip->rhy & 0x20) + { + rm_xor = (chip->rm_hh_bit2 ^ chip->rm_hh_bit7) + | (chip->rm_hh_bit3 ^ chip->rm_tc_bit5) + | (chip->rm_tc_bit3 ^ chip->rm_tc_bit5); + switch (slot->slot_num) + { + case 13: /* hh */ + slot->pg_phase_out = rm_xor << 9; + if (rm_xor ^ (noise & 1)) + { + slot->pg_phase_out |= 0xd0; + } + else + { + slot->pg_phase_out |= 0x34; + } + break; + case 16: /* sd */ + slot->pg_phase_out = (chip->rm_hh_bit8 << 9) + | ((chip->rm_hh_bit8 ^ (noise & 1)) << 8); + break; + case 17: /* tc */ + slot->pg_phase_out = (rm_xor << 9) | 0x80; + break; + default: + break; + } + } + n_bit = ((noise >> 14) ^ noise) & 0x01; + chip->noise = (noise >> 1) | (n_bit << 22); +} + +/* + Slot +*/ + +static void OPL3_SlotWrite20(opl3_slot *slot, uint8_t data) +{ + if ((data >> 7) & 0x01) + { + slot->trem = &slot->chip->tremolo; + } + else + { + slot->trem = (uint8_t*)&slot->chip->zeromod; + } + slot->reg_vib = (data >> 6) & 0x01; + slot->reg_type = (data >> 5) & 0x01; + slot->reg_ksr = (data >> 4) & 0x01; + slot->reg_mult = data & 0x0f; +} + +static void OPL3_SlotWrite40(opl3_slot *slot, uint8_t data) +{ + slot->reg_ksl = (data >> 6) & 0x03; + slot->reg_tl = data & 0x3f; + OPL3_EnvelopeUpdateKSL(slot); +} + +static void OPL3_SlotWrite60(opl3_slot *slot, uint8_t data) +{ + slot->reg_ar = (data >> 4) & 0x0f; + slot->reg_dr = data & 0x0f; +} + +static void OPL3_SlotWrite80(opl3_slot *slot, uint8_t data) +{ + slot->reg_sl = (data >> 4) & 0x0f; + if (slot->reg_sl == 0x0f) + { + slot->reg_sl = 0x1f; + } + slot->reg_rr = data & 0x0f; +} + +static void OPL3_SlotWriteE0(opl3_slot *slot, uint8_t data) +{ + slot->reg_wf = data & 0x07; + if (slot->chip->newm == 0x00) + { + slot->reg_wf &= 0x03; + } +} + +static void OPL3_SlotGenerate(opl3_slot *slot) +{ + slot->out = envelope_sin[slot->reg_wf](slot->pg_phase_out + *slot->mod, slot->eg_out); +} + +static void OPL3_SlotCalcFB(opl3_slot *slot) +{ + if (slot->channel->fb != 0x00) + { + slot->fbmod = (slot->prout + slot->out) >> (0x09 - slot->channel->fb); + } + else + { + slot->fbmod = 0; + } + slot->prout = slot->out; +} + +/* + Channel +*/ + +static void OPL3_ChannelSetupAlg(opl3_channel *channel); + +static void OPL3_ChannelUpdateRhythm(opl3_chip *chip, uint8_t data) +{ + opl3_channel *channel6; + opl3_channel *channel7; + opl3_channel *channel8; + uint8_t chnum; + + chip->rhy = data & 0x3f; + if (chip->rhy & 0x20) + { + channel6 = &chip->channel[6]; + channel7 = &chip->channel[7]; + channel8 = &chip->channel[8]; + channel6->out[0] = &channel6->slots[1]->out; + channel6->out[1] = &channel6->slots[1]->out; + channel6->out[2] = &chip->zeromod; + channel6->out[3] = &chip->zeromod; + channel7->out[0] = &channel7->slots[0]->out; + channel7->out[1] = &channel7->slots[0]->out; + channel7->out[2] = &channel7->slots[1]->out; + channel7->out[3] = &channel7->slots[1]->out; + channel8->out[0] = &channel8->slots[0]->out; + channel8->out[1] = &channel8->slots[0]->out; + channel8->out[2] = &channel8->slots[1]->out; + channel8->out[3] = &channel8->slots[1]->out; + for (chnum = 6; chnum < 9; chnum++) + { + chip->channel[chnum].chtype = ch_drum; + } + OPL3_ChannelSetupAlg(channel6); + OPL3_ChannelSetupAlg(channel7); + OPL3_ChannelSetupAlg(channel8); + /* hh */ + if (chip->rhy & 0x01) + { + OPL3_EnvelopeKeyOn(channel7->slots[0], egk_drum); + } + else + { + OPL3_EnvelopeKeyOff(channel7->slots[0], egk_drum); + } + /* tc */ + if (chip->rhy & 0x02) + { + OPL3_EnvelopeKeyOn(channel8->slots[1], egk_drum); + } + else + { + OPL3_EnvelopeKeyOff(channel8->slots[1], egk_drum); + } + /* tom */ + if (chip->rhy & 0x04) + { + OPL3_EnvelopeKeyOn(channel8->slots[0], egk_drum); + } + else + { + OPL3_EnvelopeKeyOff(channel8->slots[0], egk_drum); + } + /* sd */ + if (chip->rhy & 0x08) + { + OPL3_EnvelopeKeyOn(channel7->slots[1], egk_drum); + } + else + { + OPL3_EnvelopeKeyOff(channel7->slots[1], egk_drum); + } + /* bd */ + if (chip->rhy & 0x10) + { + OPL3_EnvelopeKeyOn(channel6->slots[0], egk_drum); + OPL3_EnvelopeKeyOn(channel6->slots[1], egk_drum); + } + else + { + OPL3_EnvelopeKeyOff(channel6->slots[0], egk_drum); + OPL3_EnvelopeKeyOff(channel6->slots[1], egk_drum); + } + } + else + { + for (chnum = 6; chnum < 9; chnum++) + { + chip->channel[chnum].chtype = ch_2op; + OPL3_ChannelSetupAlg(&chip->channel[chnum]); + OPL3_EnvelopeKeyOff(chip->channel[chnum].slots[0], egk_drum); + OPL3_EnvelopeKeyOff(chip->channel[chnum].slots[1], egk_drum); + } + } +} + +static void OPL3_ChannelWriteA0(opl3_channel *channel, uint8_t data) +{ + if (channel->chip->newm && channel->chtype == ch_4op2) + { + return; + } + channel->f_num = (channel->f_num & 0x300) | data; + channel->ksv = (channel->block << 1) + | ((channel->f_num >> (0x09 - channel->chip->nts)) & 0x01); + OPL3_EnvelopeUpdateKSL(channel->slots[0]); + OPL3_EnvelopeUpdateKSL(channel->slots[1]); + if (channel->chip->newm && channel->chtype == ch_4op) + { + channel->pair->f_num = channel->f_num; + channel->pair->ksv = channel->ksv; + OPL3_EnvelopeUpdateKSL(channel->pair->slots[0]); + OPL3_EnvelopeUpdateKSL(channel->pair->slots[1]); + } +} + +static void OPL3_ChannelWriteB0(opl3_channel *channel, uint8_t data) +{ + if (channel->chip->newm && channel->chtype == ch_4op2) + { + return; + } + channel->f_num = (channel->f_num & 0xff) | ((data & 0x03) << 8); + channel->block = (data >> 2) & 0x07; + channel->ksv = (channel->block << 1) + | ((channel->f_num >> (0x09 - channel->chip->nts)) & 0x01); + OPL3_EnvelopeUpdateKSL(channel->slots[0]); + OPL3_EnvelopeUpdateKSL(channel->slots[1]); + if (channel->chip->newm && channel->chtype == ch_4op) + { + channel->pair->f_num = channel->f_num; + channel->pair->block = channel->block; + channel->pair->ksv = channel->ksv; + OPL3_EnvelopeUpdateKSL(channel->pair->slots[0]); + OPL3_EnvelopeUpdateKSL(channel->pair->slots[1]); + } +} + +static void OPL3_ChannelSetupAlg(opl3_channel *channel) +{ + if (channel->chtype == ch_drum) + { + if (channel->ch_num == 7 || channel->ch_num == 8) + { + channel->slots[0]->mod = &channel->chip->zeromod; + channel->slots[1]->mod = &channel->chip->zeromod; + return; + } + switch (channel->alg & 0x01) + { + case 0x00: + channel->slots[0]->mod = &channel->slots[0]->fbmod; + channel->slots[1]->mod = &channel->slots[0]->out; + break; + case 0x01: + channel->slots[0]->mod = &channel->slots[0]->fbmod; + channel->slots[1]->mod = &channel->chip->zeromod; + break; + } + return; + } + if (channel->alg & 0x08) + { + return; + } + if (channel->alg & 0x04) + { + channel->pair->out[0] = &channel->chip->zeromod; + channel->pair->out[1] = &channel->chip->zeromod; + channel->pair->out[2] = &channel->chip->zeromod; + channel->pair->out[3] = &channel->chip->zeromod; + switch (channel->alg & 0x03) + { + case 0x00: + channel->pair->slots[0]->mod = &channel->pair->slots[0]->fbmod; + channel->pair->slots[1]->mod = &channel->pair->slots[0]->out; + channel->slots[0]->mod = &channel->pair->slots[1]->out; + channel->slots[1]->mod = &channel->slots[0]->out; + channel->out[0] = &channel->slots[1]->out; + channel->out[1] = &channel->chip->zeromod; + channel->out[2] = &channel->chip->zeromod; + channel->out[3] = &channel->chip->zeromod; + break; + case 0x01: + channel->pair->slots[0]->mod = &channel->pair->slots[0]->fbmod; + channel->pair->slots[1]->mod = &channel->pair->slots[0]->out; + channel->slots[0]->mod = &channel->chip->zeromod; + channel->slots[1]->mod = &channel->slots[0]->out; + channel->out[0] = &channel->pair->slots[1]->out; + channel->out[1] = &channel->slots[1]->out; + channel->out[2] = &channel->chip->zeromod; + channel->out[3] = &channel->chip->zeromod; + break; + case 0x02: + channel->pair->slots[0]->mod = &channel->pair->slots[0]->fbmod; + channel->pair->slots[1]->mod = &channel->chip->zeromod; + channel->slots[0]->mod = &channel->pair->slots[1]->out; + channel->slots[1]->mod = &channel->slots[0]->out; + channel->out[0] = &channel->pair->slots[0]->out; + channel->out[1] = &channel->slots[1]->out; + channel->out[2] = &channel->chip->zeromod; + channel->out[3] = &channel->chip->zeromod; + break; + case 0x03: + channel->pair->slots[0]->mod = &channel->pair->slots[0]->fbmod; + channel->pair->slots[1]->mod = &channel->chip->zeromod; + channel->slots[0]->mod = &channel->pair->slots[1]->out; + channel->slots[1]->mod = &channel->chip->zeromod; + channel->out[0] = &channel->pair->slots[0]->out; + channel->out[1] = &channel->slots[0]->out; + channel->out[2] = &channel->slots[1]->out; + channel->out[3] = &channel->chip->zeromod; + break; + } + } + else + { + switch (channel->alg & 0x01) + { + case 0x00: + channel->slots[0]->mod = &channel->slots[0]->fbmod; + channel->slots[1]->mod = &channel->slots[0]->out; + channel->out[0] = &channel->slots[1]->out; + channel->out[1] = &channel->chip->zeromod; + channel->out[2] = &channel->chip->zeromod; + channel->out[3] = &channel->chip->zeromod; + break; + case 0x01: + channel->slots[0]->mod = &channel->slots[0]->fbmod; + channel->slots[1]->mod = &channel->chip->zeromod; + channel->out[0] = &channel->slots[0]->out; + channel->out[1] = &channel->slots[1]->out; + channel->out[2] = &channel->chip->zeromod; + channel->out[3] = &channel->chip->zeromod; + break; + } + } +} + +static void OPL3_ChannelWriteC0(opl3_channel *channel, uint8_t data) +{ + channel->fb = (data & 0x0e) >> 1; + channel->con = data & 0x01; + channel->alg = channel->con; + if (channel->chip->newm) + { + if (channel->chtype == ch_4op) + { + channel->pair->alg = 0x04 | (channel->con << 1) | (channel->pair->con); + channel->alg = 0x08; + OPL3_ChannelSetupAlg(channel->pair); + } + else if (channel->chtype == ch_4op2) + { + channel->alg = 0x04 | (channel->pair->con << 1) | (channel->con); + channel->pair->alg = 0x08; + OPL3_ChannelSetupAlg(channel); + } + else + { + OPL3_ChannelSetupAlg(channel); + } + } + else + { + OPL3_ChannelSetupAlg(channel); + } + if (channel->chip->newm) + { + channel->cha = ((data >> 4) & 0x01) ? ~0 : 0; + channel->chb = ((data >> 5) & 0x01) ? ~0 : 0; + } + else + { + channel->cha = channel->chb = (uint16_t)~0; + } +#if OPL_ENABLE_STEREOEXT + if (!channel->chip->stereoext) + { + channel->leftpan = channel->cha << 16; + channel->rightpan = channel->chb << 16; + } +#endif +} + +#if OPL_ENABLE_STEREOEXT +static void OPL3_ChannelWriteD0(opl3_channel* channel, uint8_t data) +{ + if (channel->chip->stereoext) + { + channel->leftpan = panpot_lut[data ^ 0xff]; + channel->rightpan = panpot_lut[data]; + } +} +#endif + +static void OPL3_ChannelKeyOn(opl3_channel *channel) +{ + if (channel->chip->newm) + { + if (channel->chtype == ch_4op) + { + OPL3_EnvelopeKeyOn(channel->slots[0], egk_norm); + OPL3_EnvelopeKeyOn(channel->slots[1], egk_norm); + OPL3_EnvelopeKeyOn(channel->pair->slots[0], egk_norm); + OPL3_EnvelopeKeyOn(channel->pair->slots[1], egk_norm); + } + else if (channel->chtype == ch_2op || channel->chtype == ch_drum) + { + OPL3_EnvelopeKeyOn(channel->slots[0], egk_norm); + OPL3_EnvelopeKeyOn(channel->slots[1], egk_norm); + } + } + else + { + OPL3_EnvelopeKeyOn(channel->slots[0], egk_norm); + OPL3_EnvelopeKeyOn(channel->slots[1], egk_norm); + } +} + +static void OPL3_ChannelKeyOff(opl3_channel *channel) +{ + if (channel->chip->newm) + { + if (channel->chtype == ch_4op) + { + OPL3_EnvelopeKeyOff(channel->slots[0], egk_norm); + OPL3_EnvelopeKeyOff(channel->slots[1], egk_norm); + OPL3_EnvelopeKeyOff(channel->pair->slots[0], egk_norm); + OPL3_EnvelopeKeyOff(channel->pair->slots[1], egk_norm); + } + else if (channel->chtype == ch_2op || channel->chtype == ch_drum) + { + OPL3_EnvelopeKeyOff(channel->slots[0], egk_norm); + OPL3_EnvelopeKeyOff(channel->slots[1], egk_norm); + } + } + else + { + OPL3_EnvelopeKeyOff(channel->slots[0], egk_norm); + OPL3_EnvelopeKeyOff(channel->slots[1], egk_norm); + } +} + +static void OPL3_ChannelSet4Op(opl3_chip *chip, uint8_t data) +{ + uint8_t bit; + uint8_t chnum; + for (bit = 0; bit < 6; bit++) + { + chnum = bit; + if (bit >= 3) + { + chnum += 9 - 3; + } + if ((data >> bit) & 0x01) + { + chip->channel[chnum].chtype = ch_4op; + chip->channel[chnum + 3].chtype = ch_4op2; + } + else + { + chip->channel[chnum].chtype = ch_2op; + chip->channel[chnum + 3].chtype = ch_2op; + } + } +} + +static int16_t OPL3_ClipSample(int32_t sample) +{ + if (sample > 32767) + { + sample = 32767; + } + else if (sample < -32768) + { + sample = -32768; + } + return (int16_t)sample; +} + +static void OPL3_ProcessSlot(opl3_slot *slot) +{ + OPL3_SlotCalcFB(slot); + OPL3_EnvelopeCalc(slot); + OPL3_PhaseGenerate(slot); + OPL3_SlotGenerate(slot); +} + +void OPL3_Generate(opl3_chip *chip, int16_t *buf) +{ + opl3_channel *channel; + opl3_writebuf *writebuf; + int16_t **out; + int32_t mix; + uint8_t ii; + int16_t accm; + uint8_t shift = 0; + + buf[1] = OPL3_ClipSample(chip->mixbuff[1]); + +#if OPL_QUIRK_CHANNELSAMPLEDELAY + for (ii = 0; ii < 15; ii++) +#else + for (ii = 0; ii < 36; ii++) +#endif + { + OPL3_ProcessSlot(&chip->slot[ii]); + } + + mix = 0; + for (ii = 0; ii < 18; ii++) + { + channel = &chip->channel[ii]; + if (channel->muted) continue; + out = channel->out; + accm = *out[0] + *out[1] + *out[2] + *out[3]; +#if OPL_ENABLE_STEREOEXT + mix += (int16_t)((accm * channel->leftpan) >> 16); +#else + mix += (int16_t)(accm & channel->cha); +#endif + } + chip->mixbuff[0] = mix; + +#if OPL_QUIRK_CHANNELSAMPLEDELAY + for (ii = 15; ii < 18; ii++) + { + OPL3_ProcessSlot(&chip->slot[ii]); + } +#endif + + buf[0] = OPL3_ClipSample(chip->mixbuff[0]); + +#if OPL_QUIRK_CHANNELSAMPLEDELAY + for (ii = 18; ii < 33; ii++) + { + OPL3_ProcessSlot(&chip->slot[ii]); + } +#endif + + mix = 0; + for (ii = 0; ii < 18; ii++) + { + channel = &chip->channel[ii]; + if (channel->muted) continue; + out = channel->out; + accm = *out[0] + *out[1] + *out[2] + *out[3]; +#if OPL_ENABLE_STEREOEXT + mix += (int16_t)((accm * channel->rightpan) >> 16); +#else + mix += (int16_t)(accm & channel->chb); +#endif + } + chip->mixbuff[1] = mix; + +#if OPL_QUIRK_CHANNELSAMPLEDELAY + for (ii = 33; ii < 36; ii++) + { + OPL3_ProcessSlot(&chip->slot[ii]); + } +#endif + + if ((chip->timer & 0x3f) == 0x3f) + { + chip->tremolopos = (chip->tremolopos + 1) % 210; + } + if (chip->tremolopos < 105) + { + chip->tremolo = chip->tremolopos >> chip->tremoloshift; + } + else + { + chip->tremolo = (210 - chip->tremolopos) >> chip->tremoloshift; + } + + if ((chip->timer & 0x3ff) == 0x3ff) + { + chip->vibpos = (chip->vibpos + 1) & 7; + } + + chip->timer++; + + chip->eg_add = 0; + if (chip->eg_timer) + { + while (shift < 36 && ((chip->eg_timer >> shift) & 1) == 0) + { + shift++; + } + if (shift > 12) + { + chip->eg_add = 0; + } + else + { + chip->eg_add = shift + 1; + } + } + + if (chip->eg_timerrem || chip->eg_state) + { + if (chip->eg_timer == 0xfffffffff) + { + chip->eg_timer = 0; + chip->eg_timerrem = 1; + } + else + { + chip->eg_timer++; + chip->eg_timerrem = 0; + } + } + + chip->eg_state ^= 1; + + while ((writebuf = &chip->writebuf[chip->writebuf_cur]), writebuf->time <= chip->writebuf_samplecnt) + { + if (!(writebuf->reg & 0x200)) + { + break; + } + writebuf->reg &= 0x1ff; + OPL3_WriteReg(chip, writebuf->reg, writebuf->data); + chip->writebuf_cur = (chip->writebuf_cur + 1) % OPL_WRITEBUF_SIZE; + } + chip->writebuf_samplecnt++; +} + +void OPL3_GenerateResampled(opl3_chip *chip, int16_t *buf) +{ + while (chip->samplecnt >= chip->rateratio) + { + chip->oldsamples[0] = chip->samples[0]; + chip->oldsamples[1] = chip->samples[1]; + OPL3_Generate(chip, chip->samples); + chip->samplecnt -= chip->rateratio; + } + buf[0] = (int16_t)((chip->oldsamples[0] * (chip->rateratio - chip->samplecnt) + + chip->samples[0] * chip->samplecnt) / chip->rateratio); + buf[1] = (int16_t)((chip->oldsamples[1] * (chip->rateratio - chip->samplecnt) + + chip->samples[1] * chip->samplecnt) / chip->rateratio); + chip->samplecnt += 1 << RSM_FRAC; +} + +void OPL3_Reset(opl3_chip *chip, uint32_t samplerate) +{ + opl3_slot *slot; + opl3_channel *channel; + uint8_t slotnum; + uint8_t channum; + uint8_t local_ch_slot; + + memset(chip, 0, sizeof(opl3_chip)); + for (slotnum = 0; slotnum < 36; slotnum++) + { + slot = &chip->slot[slotnum]; + slot->chip = chip; + slot->mod = &chip->zeromod; + slot->eg_rout = 0x1ff; + slot->eg_out = 0x1ff; + slot->eg_gen = envelope_gen_num_release; + slot->trem = (uint8_t*)&chip->zeromod; + slot->slot_num = slotnum; + } + for (channum = 0; channum < 18; channum++) + { + channel = &chip->channel[channum]; + local_ch_slot = ch_slot[channum]; + channel->slots[0] = &chip->slot[local_ch_slot]; + channel->slots[1] = &chip->slot[local_ch_slot + 3]; + chip->slot[local_ch_slot].channel = channel; + chip->slot[local_ch_slot + 3].channel = channel; + if ((channum % 9) < 3) + { + channel->pair = &chip->channel[channum + 3]; + } + else if ((channum % 9) < 6) + { + channel->pair = &chip->channel[channum - 3]; + } + channel->chip = chip; + channel->out[0] = &chip->zeromod; + channel->out[1] = &chip->zeromod; + channel->out[2] = &chip->zeromod; + channel->out[3] = &chip->zeromod; + channel->muted = 0; + channel->chtype = ch_2op; + channel->cha = 0xffff; + channel->chb = 0xffff; +#if OPL_ENABLE_STEREOEXT + channel->leftpan = 0x10000; + channel->rightpan = 0x10000; +#endif + channel->ch_num = channum; + OPL3_ChannelSetupAlg(channel); + } + chip->noise = 1; + chip->rateratio = (samplerate << RSM_FRAC) / 49716; + chip->tremoloshift = 4; + chip->vibshift = 1; + +#if OPL_ENABLE_STEREOEXT + if (!panpot_lut_build) + { + int32_t i; + for (i = 0; i < 256; i++) + { + panpot_lut[i] = OPL_SIN(i); + } + panpot_lut_build = 1; + } +#endif +} + +void OPL3_WriteReg(opl3_chip *chip, uint16_t reg, uint8_t v) +{ + uint8_t high = (reg >> 8) & 0x01; + uint8_t regm = reg & 0xff; + switch (regm & 0xf0) + { + case 0x00: + if (high) + { + switch (regm & 0x0f) + { + case 0x04: + OPL3_ChannelSet4Op(chip, v); + break; + case 0x05: + chip->newm = v & 0x01; +#if OPL_ENABLE_STEREOEXT + chip->stereoext = (v >> 1) & 0x01; +#endif + break; + } + } + else + { + switch (regm & 0x0f) + { + case 0x08: + chip->nts = (v >> 6) & 0x01; + break; + } + } + break; + case 0x20: + case 0x30: + if (ad_slot[regm & 0x1f] >= 0) + { + OPL3_SlotWrite20(&chip->slot[18 * high + ad_slot[regm & 0x1f]], v); + } + break; + case 0x40: + case 0x50: + if (ad_slot[regm & 0x1f] >= 0) + { + OPL3_SlotWrite40(&chip->slot[18 * high + ad_slot[regm & 0x1f]], v); + } + break; + case 0x60: + case 0x70: + if (ad_slot[regm & 0x1f] >= 0) + { + OPL3_SlotWrite60(&chip->slot[18 * high + ad_slot[regm & 0x1f]], v); + } + break; + case 0x80: + case 0x90: + if (ad_slot[regm & 0x1f] >= 0) + { + OPL3_SlotWrite80(&chip->slot[18 * high + ad_slot[regm & 0x1f]], v); + } + break; + case 0xe0: + case 0xf0: + if (ad_slot[regm & 0x1f] >= 0) + { + OPL3_SlotWriteE0(&chip->slot[18 * high + ad_slot[regm & 0x1f]], v); + } + break; + case 0xa0: + if ((regm & 0x0f) < 9) + { + OPL3_ChannelWriteA0(&chip->channel[9 * high + (regm & 0x0f)], v); + } + break; + case 0xb0: + if (regm == 0xbd && !high) + { + chip->tremoloshift = (((v >> 7) ^ 1) << 1) + 2; + chip->vibshift = ((v >> 6) & 0x01) ^ 1; + OPL3_ChannelUpdateRhythm(chip, v); + } + else if ((regm & 0x0f) < 9) + { + OPL3_ChannelWriteB0(&chip->channel[9 * high + (regm & 0x0f)], v); + if (v & 0x20) + { + OPL3_ChannelKeyOn(&chip->channel[9 * high + (regm & 0x0f)]); + } + else + { + OPL3_ChannelKeyOff(&chip->channel[9 * high + (regm & 0x0f)]); + } + } + break; + case 0xc0: + if ((regm & 0x0f) < 9) + { + OPL3_ChannelWriteC0(&chip->channel[9 * high + (regm & 0x0f)], v); + } + break; +#if OPL_ENABLE_STEREOEXT + case 0xd0: + if ((regm & 0x0f) < 9) + { + OPL3_ChannelWriteD0(&chip->channel[9 * high + (regm & 0x0f)], v); + } + break; +#endif + } +} + +void OPL3_WriteRegBuffered(opl3_chip *chip, uint16_t reg, uint8_t v) +{ + uint64_t time1, time2; + opl3_writebuf *writebuf; + uint32_t writebuf_last; + + writebuf_last = chip->writebuf_last; + writebuf = &chip->writebuf[writebuf_last]; + + if (writebuf->reg & 0x200) + { + OPL3_WriteReg(chip, writebuf->reg & 0x1ff, writebuf->data); + + chip->writebuf_cur = (writebuf_last + 1) % OPL_WRITEBUF_SIZE; + chip->writebuf_samplecnt = writebuf->time; + } + + writebuf->reg = reg | 0x200; + writebuf->data = v; + time1 = chip->writebuf_lasttime + OPL_WRITEBUF_DELAY; + time2 = chip->writebuf_samplecnt; + + if (time1 < time2) + { + time1 = time2; + } + + writebuf->time = time1; + chip->writebuf_lasttime = time1; + chip->writebuf_last = (writebuf_last + 1) % OPL_WRITEBUF_SIZE; +} + +void OPL3_GenerateStream(opl3_chip *chip, int16_t *sndptr, uint32_t numsamples) +{ + uint32_t i; + + for(i = 0; i < numsamples; i++) + { + OPL3_GenerateResampled(chip, sndptr); + sndptr += 2; + } +} diff --git a/extern/opl/opl3.h b/extern/opl/opl3.h new file mode 100644 index 000000000..6ad33f3e0 --- /dev/null +++ b/extern/opl/opl3.h @@ -0,0 +1,168 @@ +/* Nuked OPL3 + * Copyright (C) 2013-2020 Nuke.YKT + * + * This file is part of Nuked OPL3. + * + * Nuked OPL3 is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 + * of the License, or (at your option) any later version. + * + * Nuked OPL3 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Nuked OPL3. If not, see . + + * Nuked OPL3 emulator. + * Thanks: + * MAME Development Team(Jarek Burczynski, Tatsuyuki Satoh): + * Feedback and Rhythm part calculation information. + * forums.submarine.org.uk(carbon14, opl3): + * Tremolo and phase generator calculation information. + * OPLx decapsulated(Matthew Gambrell, Olli Niemitalo): + * OPL2 ROMs. + * siliconpr0n.org(John McMaster, digshadow): + * YMF262 and VRC VII decaps and die shots. + * + * version: 1.8 + */ + +#ifndef OPL_OPL3_H +#define OPL_OPL3_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#ifndef OPL_ENABLE_STEREOEXT +#define OPL_ENABLE_STEREOEXT 0 +#endif + +#define OPL_WRITEBUF_SIZE 1024 +#define OPL_WRITEBUF_DELAY 2 + +typedef struct _opl3_slot opl3_slot; +typedef struct _opl3_channel opl3_channel; +typedef struct _opl3_chip opl3_chip; + +struct _opl3_slot { + opl3_channel *channel; + opl3_chip *chip; + int16_t out; + int16_t fbmod; + int16_t *mod; + int16_t prout; + uint16_t eg_rout; + uint16_t eg_out; + uint8_t eg_inc; + uint8_t eg_gen; + uint8_t eg_rate; + uint8_t eg_ksl; + uint8_t *trem; + uint8_t reg_vib; + uint8_t reg_type; + uint8_t reg_ksr; + uint8_t reg_mult; + uint8_t reg_ksl; + uint8_t reg_tl; + uint8_t reg_ar; + uint8_t reg_dr; + uint8_t reg_sl; + uint8_t reg_rr; + uint8_t reg_wf; + uint8_t key; + uint32_t pg_reset; + uint32_t pg_phase; + uint16_t pg_phase_out; + uint8_t slot_num; +}; + +struct _opl3_channel { + opl3_slot *slots[2]; + opl3_channel *pair; + opl3_chip *chip; + int16_t *out[4]; + +#if OPL_ENABLE_STEREOEXT + int32_t leftpan; + int32_t rightpan; +#endif + + uint8_t chtype; + uint16_t f_num; + uint8_t block; + uint8_t fb; + uint8_t con; + uint8_t alg; + uint8_t ksv; + uint16_t cha, chb; + uint8_t ch_num; + uint8_t muted; +}; + +typedef struct _opl3_writebuf { + uint64_t time; + uint16_t reg; + uint8_t data; +} opl3_writebuf; + +struct _opl3_chip { + opl3_channel channel[18]; + opl3_slot slot[36]; + uint16_t timer; + uint64_t eg_timer; + uint8_t eg_timerrem; + uint8_t eg_state; + uint8_t eg_add; + uint8_t newm; + uint8_t nts; + uint8_t rhy; + uint8_t vibpos; + uint8_t vibshift; + uint8_t tremolo; + uint8_t tremolopos; + uint8_t tremoloshift; + uint32_t noise; + int16_t zeromod; + int32_t mixbuff[2]; + uint8_t rm_hh_bit2; + uint8_t rm_hh_bit3; + uint8_t rm_hh_bit7; + uint8_t rm_hh_bit8; + uint8_t rm_tc_bit3; + uint8_t rm_tc_bit5; + +#if OPL_ENABLE_STEREOEXT + uint8_t stereoext; +#endif + + /* OPL3L */ + int32_t rateratio; + int32_t samplecnt; + int16_t oldsamples[2]; + int16_t samples[2]; + + uint64_t writebuf_samplecnt; + uint32_t writebuf_cur; + uint32_t writebuf_last; + uint64_t writebuf_lasttime; + opl3_writebuf writebuf[OPL_WRITEBUF_SIZE]; +}; + +void OPL3_Generate(opl3_chip *chip, int16_t *buf); +void OPL3_GenerateResampled(opl3_chip *chip, int16_t *buf); +void OPL3_Reset(opl3_chip *chip, uint32_t samplerate); +void OPL3_WriteReg(opl3_chip *chip, uint16_t reg, uint8_t v); +void OPL3_WriteRegBuffered(opl3_chip *chip, uint16_t reg, uint8_t v); +void OPL3_GenerateStream(opl3_chip *chip, int16_t *sndptr, uint32_t numsamples); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/engine/platform/opl.cpp b/src/engine/platform/opl.cpp index c056231ee..50e60247f 100644 --- a/src/engine/platform/opl.cpp +++ b/src/engine/platform/opl.cpp @@ -564,6 +564,9 @@ int DivPlatformOPL::toFreq(int freq) { void DivPlatformOPL::muteChannel(int ch, bool mute) { isMuted[ch]=mute; + if (ch -#include "../../../extern/Nuked-OPL3/opl3.h" +#include "../../../extern/opl/opl3.h" class DivPlatformOPL: public DivDispatch { protected: From 99b5afd4daeeb79e94c8a36b7485ef4664053883 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 4 May 2022 18:20:15 -0500 Subject: [PATCH 322/342] here's the new OPLL default instrument piano_guitar_idk by Weeppiko --- src/engine/song.h | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/engine/song.h b/src/engine/song.h index c0852ea0e..6af2841de 100644 --- a/src/engine/song.h +++ b/src/engine/song.h @@ -457,8 +457,34 @@ struct DivSong { system[0]=DIV_SYSTEM_YM2612; system[1]=DIV_SYSTEM_SMS; + // OPLL default instrument contest winner - piano_guitar_idk by Weeppiko nullInsOPLL.fm.opllPreset=0; + nullInsOPLL.fm.alg=0; + nullInsOPLL.fm.fb=7; + nullInsOPLL.fm.fms=1; + nullInsOPLL.fm.ams=0; + nullInsOPLL.fm.op[0].ar=15; + nullInsOPLL.fm.op[0].dr=5; + nullInsOPLL.fm.op[0].sl=3; + nullInsOPLL.fm.op[0].rr=3; + nullInsOPLL.fm.op[0].tl=40; + nullInsOPLL.fm.op[0].ksl=0; + nullInsOPLL.fm.op[0].mult=5; + nullInsOPLL.fm.op[0].am=0; + nullInsOPLL.fm.op[0].vib=1; + nullInsOPLL.fm.op[0].ksr=0; + nullInsOPLL.fm.op[0].ssgEnv=8; + nullInsOPLL.fm.op[1].ar=15; + nullInsOPLL.fm.op[1].dr=1; + nullInsOPLL.fm.op[1].sl=11; + nullInsOPLL.fm.op[1].rr=6; nullInsOPLL.fm.op[1].tl=0; + nullInsOPLL.fm.op[1].ksl=0; + nullInsOPLL.fm.op[1].mult=1; + nullInsOPLL.fm.op[1].am=0; + nullInsOPLL.fm.op[1].vib=0; + nullInsOPLL.fm.op[1].ksr=0; + nullInsOPLL.fm.op[1].ssgEnv=8; nullInsOPLL.name="This is a bug! Report!"; nullInsOPL.fm.alg=0; From 44f1134875047456f0748b81eea85f7d9cd673af Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 4 May 2022 18:42:36 -0500 Subject: [PATCH 323/342] GUI: fix more issues - macro range is now 128 (it was 127 before) - fix #407 --- src/gui/gui.cpp | 14 ++++++++++++-- src/gui/insEdit.cpp | 18 +++++++++--------- 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index c94c8348a..f89e120f1 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -1750,8 +1750,11 @@ void FurnaceGUI::processDrags(int dragX, int dragY) { if (macroLoopDragLen>0) { int x=(dragX-macroLoopDragStart.x)*macroLoopDragLen/MAX(1,macroLoopDragAreaSize.x); if (x<0) x=0; - if (x>=macroLoopDragLen) x=-1; - x+=macroDragScroll; + if (x>=macroLoopDragLen) { + x=-1; + } else { + x+=macroDragScroll; + } *macroLoopDragTarget=x; } } @@ -2416,6 +2419,13 @@ bool FurnaceGUI::loop() { } wantCaptureKeyboard=ImGui::GetIO().WantTextInput; + + if (wantCaptureKeyboard) { + WAKE_UP; + } + if (ImGui::GetIO().MouseDown[0] || ImGui::GetIO().MouseDown[1] || ImGui::GetIO().MouseDown[2] || ImGui::GetIO().MouseDown[3] || ImGui::GetIO().MouseDown[4]) { + WAKE_UP; + } while (true) { midiLock.lock(); diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index 0355e7fe7..8d3ea74a7 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -1078,7 +1078,7 @@ void FurnaceGUI::drawGBEnv(unsigned char vol, unsigned char len, unsigned char s if (displayLoop) { \ ImGui::SetNextItemWidth(lenAvail); \ if (ImGui::InputScalar("##IMacroLen_" macroName,ImGuiDataType_U8,¯o.len,&_ONE,&_THREE)) { MARK_MODIFIED \ - if (macro.len>127) macro.len=127; \ + if (macro.len>128) macro.len=128; \ } \ if (macroMode) { \ bool modeVal=macro.mode; \ @@ -1178,7 +1178,7 @@ void FurnaceGUI::drawGBEnv(unsigned char vol, unsigned char len, unsigned char s if (displayLoop) { \ ImGui::SetNextItemWidth(lenAvail); \ if (ImGui::InputScalar("##IOPMacroLen_" #op macroName,ImGuiDataType_U8,¯o.len,&_ONE,&_THREE)) { MARK_MODIFIED \ - if (macro.len>127) macro.len=127; \ + if (macro.len>128) macro.len=128; \ } \ if (macroMode) { \ bool modeVal=macro.mode; \ @@ -1267,14 +1267,14 @@ if (ImGui::BeginTable("MacroSpace",2)) { \ ImGui::Dummy(ImVec2(120.0f*dpiScale,dpiScale)); \ ImGui::TableNextColumn(); \ float availableWidth=ImGui::GetContentRegionAvail().x-reservedSpace; \ - int totalFit=MIN(127,availableWidth/MAX(1,macroPointSize*dpiScale)); \ - if (macroDragScroll>127-totalFit) { \ - macroDragScroll=127-totalFit; \ + int totalFit=MIN(128,availableWidth/MAX(1,macroPointSize*dpiScale)); \ + if (macroDragScroll>128-totalFit) { \ + macroDragScroll=128-totalFit; \ } \ ImGui::SetNextItemWidth(availableWidth); \ - if (CWSliderInt("##MacroScroll",¯oDragScroll,0,127-totalFit,"")) { \ + if (CWSliderInt("##MacroScroll",¯oDragScroll,0,128-totalFit,"")) { \ if (macroDragScroll<0) macroDragScroll=0; \ - if (macroDragScroll>127-totalFit) macroDragScroll=127-totalFit; \ + if (macroDragScroll>128-totalFit) macroDragScroll=128-totalFit; \ } #define MACRO_END \ @@ -1282,9 +1282,9 @@ if (ImGui::BeginTable("MacroSpace",2)) { \ ImGui::TableNextColumn(); \ ImGui::TableNextColumn(); \ ImGui::SetNextItemWidth(availableWidth); \ - if (CWSliderInt("##MacroScroll",¯oDragScroll,0,127-totalFit,"")) { \ + if (CWSliderInt("##MacroScroll",¯oDragScroll,0,128-totalFit,"")) { \ if (macroDragScroll<0) macroDragScroll=0; \ - if (macroDragScroll>127-totalFit) macroDragScroll=127-totalFit; \ + if (macroDragScroll>128-totalFit) macroDragScroll=128-totalFit; \ } \ ImGui::EndTable(); \ } From 71cef797c6cd77b6fe717381f4db5fd01ece89a9 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 4 May 2022 20:56:26 -0500 Subject: [PATCH 324/342] GUI: only use edit color when pat view is focused --- src/gui/gui.cpp | 2 ++ src/gui/gui.h | 2 +- src/gui/pattern.cpp | 4 ++-- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index f89e120f1..b922feab9 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -2569,6 +2569,7 @@ bool FurnaceGUI::loop() { ImGui_ImplSDL2_NewFrame(sdlWin); ImGui::NewFrame(); + curWindowLast=curWindow; curWindow=GUI_WINDOW_NOTHING; editOptsVisible=false; @@ -3913,6 +3914,7 @@ FurnaceGUI::FurnaceGUI(): nonLatchNibble(false), curWindow(GUI_WINDOW_NOTHING), nextWindow(GUI_WINDOW_NOTHING), + curWindowLast(GUI_WINDOW_NOTHING), nextDesc(NULL), latchNote(-1), latchIns(-2), diff --git a/src/gui/gui.h b/src/gui/gui.h index 90d909f2d..deabc8897 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -966,7 +966,7 @@ class FurnaceGUI { SelectionPoint selStart, selEnd, cursor; bool selecting, curNibble, orderNibble, followOrders, followPattern, changeAllOrders; bool collapseWindow, demandScrollX, fancyPattern, wantPatName, firstFrame, tempoView, waveHex, lockLayout, editOptsVisible, latchNibble, nonLatchNibble; - FurnaceGUIWindows curWindow, nextWindow; + FurnaceGUIWindows curWindow, nextWindow, curWindowLast; float peak[2]; float patChanX[DIV_MAX_CHANS+1]; float patChanSlideY[DIV_MAX_CHANS+1]; diff --git a/src/gui/pattern.cpp b/src/gui/pattern.cpp index 61fccb5ae..8fff1c9ac 100644 --- a/src/gui/pattern.cpp +++ b/src/gui/pattern.cpp @@ -64,7 +64,7 @@ inline void FurnaceGUI::patternRow(int i, bool isPlaying, float lineHeight, int } // check overflow highlight if (settings.overflowHighlight) { - if (edit && cursor.y==i) { + 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()) { ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0,ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_PLAY_HEAD])); @@ -75,7 +75,7 @@ inline void FurnaceGUI::patternRow(int i, bool isPlaying, float lineHeight, int } } else { isPushing=true; - if (edit && cursor.y==i) { + 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()) { ImGui::PushStyleColor(ImGuiCol_Header,ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_PLAY_HEAD])); From 10e8d71745b62a2509f13f16c47fc53df729b86d Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 4 May 2022 22:01:19 -0500 Subject: [PATCH 325/342] GUI: part 2 of previous commit --- src/gui/pattern.cpp | 10 +++++----- src/gui/settings.cpp | 4 +++- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/gui/pattern.cpp b/src/gui/pattern.cpp index 8fff1c9ac..e171eedff 100644 --- a/src/gui/pattern.cpp +++ b/src/gui/pattern.cpp @@ -113,9 +113,9 @@ inline void FurnaceGUI::patternRow(int i, bool isPlaying, float lineHeight, int bool selectedNote=selectedRow && (j32>=sel1XSum && j32<=sel2XSum); bool selectedIns=selectedRow && (j32+1>=sel1XSum && j32+1<=sel2XSum); bool selectedVol=selectedRow && (j32+2>=sel1XSum && j32+2<=sel2XSum); - bool cursorNote=(cursor.y==i && cursor.xCoarse==j && cursor.xFine==0); - bool cursorIns=(cursor.y==i && cursor.xCoarse==j && cursor.xFine==1); - bool cursorVol=(cursor.y==i && cursor.xCoarse==j && cursor.xFine==2); + bool cursorNote=(cursor.y==i && cursor.xCoarse==j && cursor.xFine==0 && curWindowLast==GUI_WINDOW_PATTERN); + bool cursorIns=(cursor.y==i && cursor.xCoarse==j && cursor.xFine==1 && curWindowLast==GUI_WINDOW_PATTERN); + bool cursorVol=(cursor.y==i && cursor.xCoarse==j && cursor.xFine==2 && curWindowLast==GUI_WINDOW_PATTERN); // note sprintf(id,"%s##PN_%d_%d",noteName(pat->data[i][0],pat->data[i][1]),i,j); @@ -221,8 +221,8 @@ inline void FurnaceGUI::patternRow(int i, bool isPlaying, float lineHeight, int int index=4+(k<<1); bool selectedEffect=selectedRow && (j32+index-1>=sel1XSum && j32+index-1<=sel2XSum); bool selectedEffectVal=selectedRow && (j32+index>=sel1XSum && j32+index<=sel2XSum); - bool cursorEffect=(cursor.y==i && cursor.xCoarse==j && cursor.xFine==index-1); - bool cursorEffectVal=(cursor.y==i && cursor.xCoarse==j && cursor.xFine==index); + bool cursorEffect=(cursor.y==i && cursor.xCoarse==j && cursor.xFine==index-1 && curWindowLast==GUI_WINDOW_PATTERN); + bool cursorEffectVal=(cursor.y==i && cursor.xCoarse==j && cursor.xFine==index && curWindowLast==GUI_WINDOW_PATTERN); // effect if (pat->data[i][index]==-1) { diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index dfaa85cfd..1d171dd08 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -2630,6 +2630,7 @@ void FurnaceGUI::applyUISettings(bool updateFonts) { if ((iconFont=ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF(iconFont_compressed_data,iconFont_compressed_size,e->getConfInt("iconSize",16)*dpiScale,&fc,fontRangeIcon))==NULL) { logE("could not load icon font!"); } + if (settings.mainFontSize==settings.patFontSize && settings.patFont<5 && builtinFontM[settings.patFont]==builtinFont[settings.mainFont]) { logD("using main font for pat font."); patFont=mainFont; @@ -2661,8 +2662,9 @@ void FurnaceGUI::applyUISettings(bool updateFonts) { logE("could not load pattern font!"); patFont=ImGui::GetIO().Fonts->AddFontDefault(); } + } } - } + if ((bigFont=ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF(font_plexSans_compressed_data,font_plexSans_compressed_size,40*dpiScale))==NULL) { logE("could not load big UI font!"); } From 6aefd392ff76e6ba43497bbfb590aa03e0d54c5d Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 4 May 2022 22:04:18 -0500 Subject: [PATCH 326/342] NES: fix duty effect not working at all --- src/engine/platform/mmc5.cpp | 1 + src/engine/platform/nes.cpp | 2 ++ 2 files changed, 3 insertions(+) diff --git a/src/engine/platform/mmc5.cpp b/src/engine/platform/mmc5.cpp index 66bead9da..8446643b6 100644 --- a/src/engine/platform/mmc5.cpp +++ b/src/engine/platform/mmc5.cpp @@ -304,6 +304,7 @@ int DivPlatformMMC5::dispatch(DivCommand c) { } case DIV_CMD_STD_NOISE_MODE: chan[c.chan].duty=c.value; + rWrite(0x5000+c.chan*4,0x30|chan[c.chan].outVol|((chan[c.chan].duty&3)<<6)); break; case DIV_CMD_SAMPLE_BANK: sampleBank=c.value; diff --git a/src/engine/platform/nes.cpp b/src/engine/platform/nes.cpp index dffcb7ee1..4b301839d 100644 --- a/src/engine/platform/nes.cpp +++ b/src/engine/platform/nes.cpp @@ -520,6 +520,8 @@ int DivPlatformNES::dispatch(DivCommand c) { chan[c.chan].duty=c.value; if (c.chan==3) { // noise chan[c.chan].freqChanged=true; + } else if (c.chan<2) { + rWrite(0x4000+c.chan*4,0x30|chan[c.chan].outVol|((chan[c.chan].duty&3)<<6)); } break; case DIV_CMD_NES_SWEEP: From 99a313b6846755888b89c2ac01ca8fffa881abdd Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 4 May 2022 22:55:11 -0500 Subject: [PATCH 327/342] GUI: implement instrument load replace --- src/gui/doAction.cpp | 3 +++ src/gui/gui.cpp | 59 +++++++++++++++++++++++++++++++++++++++----- src/gui/gui.h | 4 +++ src/gui/guiConst.cpp | 1 + src/gui/insEdit.cpp | 2 +- src/gui/settings.cpp | 1 + 6 files changed, 63 insertions(+), 7 deletions(-) diff --git a/src/gui/doAction.cpp b/src/gui/doAction.cpp index 92ce732f9..fc95c7ec9 100644 --- a/src/gui/doAction.cpp +++ b/src/gui/doAction.cpp @@ -518,6 +518,9 @@ void FurnaceGUI::doAction(int what) { case GUI_ACTION_INS_LIST_OPEN: openFileDialog(GUI_FILE_INS_OPEN); break; + case GUI_ACTION_INS_LIST_OPEN_REPLACE: + openFileDialog(GUI_FILE_INS_OPEN_REPLACE); + break; case GUI_ACTION_INS_LIST_SAVE: if (curIns>=0 && curIns<(int)e->song.ins.size()) openFileDialog(GUI_FILE_INS_SAVE); break; diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index b922feab9..01ef0b273 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -1288,7 +1288,14 @@ void FurnaceGUI::openFileDialog(FurnaceGUIFileDialogs type) { ); break; case GUI_FILE_INS_OPEN: + case GUI_FILE_INS_OPEN_REPLACE: prevIns=-3; + if (prevInsData!=NULL) { + delete prevInsData; + prevInsData=NULL; + } + prevInsData=new DivInstrument; + *prevInsData=*e->getIns(curIns); if (!dirExists(workingDirIns)) workingDirIns=getHomeDir(); hasOpened=fileDialog->openLoad( "Load Instrument", @@ -1300,11 +1307,20 @@ void FurnaceGUI::openFileDialog(FurnaceGUIFileDialogs type) { [this](const char* path) { std::vector instruments=e->instrumentFromFile(path); if (!instruments.empty()) { - e->loadTempIns(instruments[0]); - if (curIns!=-2) { - prevIns=curIns; + if (curFileDialog==GUI_FILE_INS_OPEN_REPLACE) { + if (prevIns==-3) { + prevIns=curIns; + } + if (prevIns>=0 && prevIns<=(int)e->song.ins.size()) { + *e->song.ins[prevIns]=*instruments[0]; + } + } else { + e->loadTempIns(instruments[0]); + if (curIns!=-2) { + prevIns=curIns; + } + curIns=-2; } - curIns=-2; } for (DivInstrument* i: instruments) delete i; } @@ -2911,8 +2927,18 @@ bool FurnaceGUI::loop() { if (fileDialog->render(ImVec2(600.0f*dpiScale,400.0f*dpiScale),ImVec2(scrW*dpiScale,scrH*dpiScale))) { bool openOpen=false; //ImGui::GetIO().ConfigFlags&=~ImGuiConfigFlags_NavEnableKeyboard; - if (curFileDialog==GUI_FILE_INS_OPEN && prevIns!=-3) { - curIns=prevIns; + if ((curFileDialog==GUI_FILE_INS_OPEN || curFileDialog==GUI_FILE_INS_OPEN_REPLACE) && prevIns!=-3) { + if (curFileDialog==GUI_FILE_INS_OPEN_REPLACE) { + if (prevInsData!=NULL) { + logI("try"); + if (prevIns>=0 && prevIns<(int)e->song.ins.size()) { + logI("replace"); + *e->song.ins[prevIns]=*prevInsData; + } + } + } else { + curIns=prevIns; + } prevIns=-3; } switch (curFileDialog) { @@ -2922,6 +2948,7 @@ bool FurnaceGUI::loop() { workingDirSong=fileDialog->getPath()+DIR_SEPARATOR_STR; break; case GUI_FILE_INS_OPEN: + case GUI_FILE_INS_OPEN_REPLACE: case GUI_FILE_INS_SAVE: workingDirIns=fileDialog->getPath()+DIR_SEPARATOR_STR; break; @@ -3101,6 +3128,25 @@ bool FurnaceGUI::loop() { } break; } + case GUI_FILE_INS_OPEN_REPLACE: { + std::vector instruments=e->instrumentFromFile(copyOfName.c_str()); + if (!instruments.empty()) { + if (!e->getWarnings().empty()) { + showWarning(e->getWarnings(),GUI_WARN_GENERIC); + } + if (curIns>=0 && curIns<(int)e->song.ins.size()) { + *e->song.ins[0]=*instruments[0]; + } else { + showError("...but you haven't selected an instrument!"); + } + for (DivInstrument* i: instruments) { + delete i; + } + } else { + showError("cannot load instrument! ("+e->getLastError()+")"); + } + break; + } case GUI_FILE_WAVE_OPEN: e->addWaveFromFile(copyOfName.c_str()); MARK_MODIFIED; @@ -3816,6 +3862,7 @@ FurnaceGUI::FurnaceGUI(): patFont(NULL), bigFont(NULL), fontRange(NULL), + prevInsData(NULL), curIns(0), curWave(0), curSample(0), diff --git a/src/gui/gui.h b/src/gui/gui.h index deabc8897..99ca008b6 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -239,6 +239,7 @@ enum FurnaceGUIFileDialogs { GUI_FILE_SAVE, GUI_FILE_SAVE_DMF_LEGACY, GUI_FILE_INS_OPEN, + GUI_FILE_INS_OPEN_REPLACE, GUI_FILE_INS_SAVE, GUI_FILE_WAVE_OPEN, GUI_FILE_WAVE_SAVE, @@ -406,6 +407,7 @@ enum FurnaceGUIActions { GUI_ACTION_INS_LIST_ADD, GUI_ACTION_INS_LIST_DUPLICATE, GUI_ACTION_INS_LIST_OPEN, + GUI_ACTION_INS_LIST_OPEN_REPLACE, GUI_ACTION_INS_LIST_SAVE, GUI_ACTION_INS_LIST_MOVE_UP, GUI_ACTION_INS_LIST_MOVE_DOWN, @@ -947,6 +949,8 @@ class FurnaceGUI { char finalLayoutPath[4096]; + DivInstrument* prevInsData; + int curIns, curWave, curSample, curOctave, curOrder, prevIns, oldRow, oldOrder, oldOrder1, editStep, exportLoops, soloChan, soloTimeout, orderEditMode, orderCursor; int loopOrder, loopRow, loopEnd, isClipping, extraChannelButtons, patNameTarget, newSongCategory, latchTarget; int wheelX, wheelY; diff --git a/src/gui/guiConst.cpp b/src/gui/guiConst.cpp index ca0647397..3cbd82046 100644 --- a/src/gui/guiConst.cpp +++ b/src/gui/guiConst.cpp @@ -560,6 +560,7 @@ const FurnaceGUIActionDef guiActions[GUI_ACTION_MAX]={ 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_MOVE_UP", "Move up", FURKMOD_SHIFT|SDLK_UP), D("INS_LIST_MOVE_DOWN", "Move down", FURKMOD_SHIFT|SDLK_DOWN), diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index 8d3ea74a7..ed776524d 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -1378,7 +1378,7 @@ void FurnaceGUI::drawInsEdit() { ImGui::TableNextColumn(); // TODO: load replace if (ImGui::Button(ICON_FA_FOLDER_OPEN "##IELoad")) { - doAction(GUI_ACTION_INS_LIST_OPEN); + doAction(GUI_ACTION_INS_LIST_OPEN_REPLACE); } ImGui::SameLine(); if (ImGui::Button(ICON_FA_FLOPPY_O "##IESave")) { diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index 1d171dd08..5d79ebce0 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -1616,6 +1616,7 @@ void FurnaceGUI::drawSettings() { UI_KEYBIND_CONFIG(GUI_ACTION_INS_LIST_ADD); UI_KEYBIND_CONFIG(GUI_ACTION_INS_LIST_DUPLICATE); UI_KEYBIND_CONFIG(GUI_ACTION_INS_LIST_OPEN); + UI_KEYBIND_CONFIG(GUI_ACTION_INS_LIST_OPEN_REPLACE); UI_KEYBIND_CONFIG(GUI_ACTION_INS_LIST_SAVE); UI_KEYBIND_CONFIG(GUI_ACTION_INS_LIST_MOVE_UP); UI_KEYBIND_CONFIG(GUI_ACTION_INS_LIST_MOVE_DOWN); From c3e55ae117b89478774cd3b425d5d6a5843d7023 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 4 May 2022 23:11:51 -0500 Subject: [PATCH 328/342] GUI: more improvements to instrument loading now with an "instrument load always replace" option --- src/gui/dataList.cpp | 2 +- src/gui/gui.cpp | 4 +--- src/gui/gui.h | 2 ++ src/gui/settings.cpp | 8 ++++++++ 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/gui/dataList.cpp b/src/gui/dataList.cpp index 652c2eb99..0e2d32450 100644 --- a/src/gui/dataList.cpp +++ b/src/gui/dataList.cpp @@ -45,7 +45,7 @@ void FurnaceGUI::drawInsList() { } ImGui::SameLine(); if (ImGui::Button(ICON_FA_FOLDER_OPEN "##InsLoad")) { - doAction(GUI_ACTION_INS_LIST_OPEN); + doAction((settings.insLoadAlwaysReplace && curIns>=0 && curIns<=(int)e->song.ins.size())?GUI_ACTION_INS_LIST_OPEN_REPLACE:GUI_ACTION_INS_LIST_OPEN); } ImGui::SameLine(); if (ImGui::Button(ICON_FA_FLOPPY_O "##InsSave")) { diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 01ef0b273..2653153c1 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -2930,9 +2930,7 @@ bool FurnaceGUI::loop() { if ((curFileDialog==GUI_FILE_INS_OPEN || curFileDialog==GUI_FILE_INS_OPEN_REPLACE) && prevIns!=-3) { if (curFileDialog==GUI_FILE_INS_OPEN_REPLACE) { if (prevInsData!=NULL) { - logI("try"); if (prevIns>=0 && prevIns<(int)e->song.ins.size()) { - logI("replace"); *e->song.ins[prevIns]=*prevInsData; } } @@ -3135,7 +3133,7 @@ bool FurnaceGUI::loop() { showWarning(e->getWarnings(),GUI_WARN_GENERIC); } if (curIns>=0 && curIns<(int)e->song.ins.size()) { - *e->song.ins[0]=*instruments[0]; + *e->song.ins[curIns]=*instruments[curIns]; } else { showError("...but you haven't selected an instrument!"); } diff --git a/src/gui/gui.h b/src/gui/gui.h index 99ca008b6..f4566b552 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -857,6 +857,7 @@ class FurnaceGUI { int eventDelay; int moveWindowTitle; int hiddenSystems; + int insLoadAlwaysReplace; unsigned int maxUndoSteps; String mainFontPath; String patFontPath; @@ -939,6 +940,7 @@ class FurnaceGUI { eventDelay(0), moveWindowTitle(0), hiddenSystems(0), + insLoadAlwaysReplace(1), maxUndoSteps(100), mainFontPath(""), patFontPath(""), diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index 5d79ebce0..b7b07cc43 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -419,6 +419,11 @@ void FurnaceGUI::drawSettings() { settings.restartOnFlagChange=restartOnFlagChangeB; } + bool insLoadAlwaysReplaceB=settings.insLoadAlwaysReplace; + if (ImGui::Checkbox("Always replace currently selected instrument when loading from instrument list",&insLoadAlwaysReplaceB)) { + settings.insLoadAlwaysReplace=insLoadAlwaysReplaceB; + } + bool sysFileDialogB=settings.sysFileDialog; if (ImGui::Checkbox("Use system file picker",&sysFileDialogB)) { settings.sysFileDialog=sysFileDialogB; @@ -1831,6 +1836,7 @@ void FurnaceGUI::syncSettings() { settings.eventDelay=e->getConfInt("eventDelay",0); settings.moveWindowTitle=e->getConfInt("moveWindowTitle",0); settings.hiddenSystems=e->getConfInt("hiddenSystems",0); + settings.insLoadAlwaysReplace=e->getConfInt("insLoadAlwaysReplace",1); clampSetting(settings.mainFontSize,2,96); clampSetting(settings.patFontSize,2,96); @@ -1901,6 +1907,7 @@ void FurnaceGUI::syncSettings() { clampSetting(settings.eventDelay,0,1); clampSetting(settings.moveWindowTitle,0,1); clampSetting(settings.hiddenSystems,0,1); + clampSetting(settings.insLoadAlwaysReplace,0,1); settings.initialSys=e->decodeSysDesc(e->getConfString("initialSys","")); if (settings.initialSys.size()<4) { @@ -2012,6 +2019,7 @@ void FurnaceGUI::commitSettings() { e->setConf("moveWindowTitle",settings.moveWindowTitle); e->setConf("hiddenSystems",settings.hiddenSystems); e->setConf("initialSys",e->encodeSysDesc(settings.initialSys)); + e->setConf("insLoadAlwaysReplace",settings.insLoadAlwaysReplace); // colors for (int i=0; i Date: Wed, 4 May 2022 23:36:03 -0500 Subject: [PATCH 329/342] dev92 - GUI: customizable channel collapsing! --- src/engine/engine.h | 4 ++-- src/engine/fileOps.cpp | 6 ++++++ src/engine/song.h | 4 ++-- src/gui/cursor.cpp | 10 +++++----- src/gui/pattern.cpp | 12 ++++++++++-- 5 files changed, 25 insertions(+), 11 deletions(-) diff --git a/src/engine/engine.h b/src/engine/engine.h index 5d6d066c0..87174d974 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -45,8 +45,8 @@ #define BUSY_BEGIN_SOFT softLocked=true; isBusy.lock(); #define BUSY_END isBusy.unlock(); softLocked=false; -#define DIV_VERSION "dev91" -#define DIV_ENGINE_VERSION 91 +#define DIV_VERSION "dev92" +#define DIV_ENGINE_VERSION 92 // for imports #define DIV_VERSION_MOD 0xff01 diff --git a/src/engine/fileOps.cpp b/src/engine/fileOps.cpp index b45baa7ab..7b09ca7d3 100644 --- a/src/engine/fileOps.cpp +++ b/src/engine/fileOps.cpp @@ -1315,6 +1315,12 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) { ds.chanCollapse[i]=reader.readC(); } + if (ds.version<92) { + for (int i=0; i0) ds.chanCollapse[i]=3; + } + } + for (int i=0; i sample; bool chanShow[DIV_MAX_CHANS]; - bool chanCollapse[DIV_MAX_CHANS]; + unsigned char chanCollapse[DIV_MAX_CHANS]; DivInstrument nullIns, nullInsOPLL, nullInsOPL, nullInsQSound; DivWavetable nullWave; @@ -452,7 +452,7 @@ struct DivSong { } for (int i=0; i=e->song.patLen) cursor.y=e->song.patLen-1; - if (e->song.chanCollapse[selEnd.xCoarse]) { + if (e->song.chanCollapse[selStart.xCoarse]==3) { selStart.xFine=0; } - if (e->song.chanCollapse[selEnd.xCoarse]) { + if (e->song.chanCollapse[selEnd.xCoarse] && selEnd.xFine>=(3-e->song.chanCollapse[selEnd.xCoarse])) { selEnd.xFine=2+e->song.pat[cursor.xCoarse].effectCols*2; } @@ -105,7 +105,7 @@ void FurnaceGUI::moveCursor(int x, int y, bool select) { demandScrollX=true; if (x>0) { for (int i=0; i=(e->song.chanCollapse[cursor.xCoarse]?1:(3+e->song.pat[cursor.xCoarse].effectCols*2))) { + if (++cursor.xFine>=(e->song.chanCollapse[cursor.xCoarse]?(4-e->song.chanCollapse[cursor.xCoarse]):(3+e->song.pat[cursor.xCoarse].effectCols*2))) { cursor.xFine=0; if (++cursor.xCoarse>=lastChannel) { if (settings.wrapHorizontal!=0 && !select) { @@ -113,7 +113,7 @@ void FurnaceGUI::moveCursor(int x, int y, bool select) { if (settings.wrapHorizontal==2) y++; } else { cursor.xCoarse=lastChannel-1; - cursor.xFine=e->song.chanCollapse[cursor.xCoarse]?0:(2+e->song.pat[cursor.xCoarse].effectCols*2); + cursor.xFine=e->song.chanCollapse[cursor.xCoarse]?(3-e->song.chanCollapse[cursor.xCoarse]):(2+e->song.pat[cursor.xCoarse].effectCols*2); } } else { while (!e->song.chanShow[cursor.xCoarse]) { @@ -141,7 +141,7 @@ void FurnaceGUI::moveCursor(int x, int y, bool select) { if (cursor.xCoarse<0) break; } if (e->song.chanCollapse[cursor.xCoarse]) { - cursor.xFine=0; + cursor.xFine=3-e->song.chanCollapse[cursor.xCoarse]; } else { cursor.xFine=2+e->song.pat[cursor.xCoarse].effectCols*2; } diff --git a/src/gui/pattern.cpp b/src/gui/pattern.cpp index e171eedff..d284b08fc 100644 --- a/src/gui/pattern.cpp +++ b/src/gui/pattern.cpp @@ -145,7 +145,7 @@ inline void FurnaceGUI::patternRow(int i, bool isPlaying, float lineHeight, int ImGui::PopStyleColor(); // the following is only visible when the channel is not collapsed - if (!e->song.chanCollapse[j]) { + if (e->song.chanCollapse[j]<3) { // instrument if (pat->data[i][2]==-1) { ImGui::PushStyleColor(ImGuiCol_Text,inactiveColor); @@ -183,7 +183,9 @@ inline void FurnaceGUI::patternRow(int i, bool isPlaying, float lineHeight, int updateSelection(j,1,i); } ImGui::PopStyleColor(); + } + if (e->song.chanCollapse[j]<2) { // volume if (pat->data[i][3]==-1) { sprintf(id,"..##PV_%d_%d",i,j); @@ -215,7 +217,9 @@ inline void FurnaceGUI::patternRow(int i, bool isPlaying, float lineHeight, int updateSelection(j,2,i); } ImGui::PopStyleColor(); + } + if (e->song.chanCollapse[j]<1) { // effects for (int k=0; ksong.pat[j].effectCols; k++) { int index=4+(k<<1); @@ -499,7 +503,11 @@ void FurnaceGUI::drawPattern() { snprintf(chanID,2048,"%c##_HCH%d",e->song.chanCollapse[i]?'+':'-',i); ImGui::SetCursorPosX(ImGui::GetCursorPosX()+4.0f*dpiScale); if (ImGui::SmallButton(chanID)) { - e->song.chanCollapse[i]=!e->song.chanCollapse[i]; + if (e->song.chanCollapse[i]==0) { + e->song.chanCollapse[i]=3; + } else if (e->song.chanCollapse[i]>0) { + e->song.chanCollapse[i]--; + } } if (!e->song.chanCollapse[i]) { ImGui::SameLine(); From 030ba9eaf105e9b1766e8b51c62f9022dcb1df1b Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 4 May 2022 23:49:33 -0500 Subject: [PATCH 330/342] GUI: implement this on the keybind as well --- src/gui/doAction.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/gui/doAction.cpp b/src/gui/doAction.cpp index fc95c7ec9..d966b797e 100644 --- a/src/gui/doAction.cpp +++ b/src/gui/doAction.cpp @@ -465,7 +465,11 @@ void FurnaceGUI::doAction(int what) { break; case GUI_ACTION_PAT_COLLAPSE: if (cursor.xCoarse<0 || cursor.xCoarse>=e->getTotalChannelCount()) break; - e->song.chanCollapse[cursor.xCoarse]=!e->song.chanCollapse[cursor.xCoarse]; + if (e->song.chanCollapse[cursor.xCoarse]==0) { + e->song.chanCollapse[cursor.xCoarse]=3; + } else if (e->song.chanCollapse[cursor.xCoarse]>0) { + e->song.chanCollapse[cursor.xCoarse]--; + } break; case GUI_ACTION_PAT_INCREASE_COLUMNS: if (cursor.xCoarse<0 || cursor.xCoarse>=e->getTotalChannelCount()) break; From 9915fc0c8f3f54c1fc76e6af17a8a774fd72e01c Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 5 May 2022 00:52:15 -0500 Subject: [PATCH 331/342] GUI: add "don't display multi-system" option --- TODO.md | 3 +-- src/engine/engine.h | 2 +- src/engine/sysDef.cpp | 12 ++++++++++-- src/gui/dataList.cpp | 25 ++++++++++++++++++++++--- src/gui/gui.cpp | 2 +- src/gui/gui.h | 4 ++++ src/gui/settings.cpp | 22 ++++++++++++++++++++++ 7 files changed, 61 insertions(+), 9 deletions(-) diff --git a/TODO.md b/TODO.md index 80357e3ef..907e1aeef 100644 --- a/TODO.md +++ b/TODO.md @@ -13,7 +13,6 @@ - maybe YMU759 ADPCM channel - ADPCM chips - Game Boy envelope macro/sequence -- option to display chip names instead of "multi-system" on title bar - rewrite the system name detection function anyway - scroll instrument/wave/sample list when selecting item - unified data view @@ -30,4 +29,4 @@ - Apply button in settings - find and replace - finish wave synth -- make compact mode multi-state (note, note/ins, note/ins/vol and note/ins/vol/effects) \ No newline at end of file +- add mono/poly note preview button \ No newline at end of file diff --git a/src/engine/engine.h b/src/engine/engine.h index 87174d974..9ca19ec09 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -548,7 +548,7 @@ class DivEngine { DivInstrumentType getPreferInsSecondType(int ch); // get song system name - const char* getSongSystemName(); + String getSongSystemName(bool isMultiSystemAcceptable=true); // get sys name const char* getSystemName(DivSystem sys); diff --git a/src/engine/sysDef.cpp b/src/engine/sysDef.cpp index 981358ed4..9f07c0149 100644 --- a/src/engine/sysDef.cpp +++ b/src/engine/sysDef.cpp @@ -53,7 +53,7 @@ std::vector& DivEngine::getPossibleInsTypes() { } // TODO: rewrite this function (again). it's an unreliable mess. -const char* DivEngine::getSongSystemName() { +String DivEngine::getSongSystemName(bool isMultiSystemAcceptable) { switch (song.systemLen) { case 0: return "help! what's going on!"; @@ -198,7 +198,15 @@ const char* DivEngine::getSongSystemName() { } break; } - return "multi-system"; + if (isMultiSystemAcceptable) return "multi-system"; + + String ret=""; + for (int i=0; i0) ret+=" + "; + ret+=getSystemName(song.system[i]); + } + + return ret; } const char* DivEngine::getSystemName(DivSystem sys) { diff --git a/src/gui/dataList.cpp b/src/gui/dataList.cpp index 0e2d32450..8cc35e792 100644 --- a/src/gui/dataList.cpp +++ b/src/gui/dataList.cpp @@ -23,6 +23,7 @@ #include "plot_nolerp.h" #include "guiConst.h" #include +#include const char* sampleNote[12]={ "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B" @@ -36,6 +37,7 @@ void FurnaceGUI::drawInsList() { } if (!insListOpen) return; if (ImGui::Begin("Instruments",&insListOpen)) { + if (settings.unifiedDataView) settings.horizontalDataView=0; if (ImGui::Button(ICON_FA_PLUS "##InsAdd")) { doAction(GUI_ACTION_INS_LIST_ADD); } @@ -64,7 +66,12 @@ void FurnaceGUI::drawInsList() { doAction(GUI_ACTION_INS_LIST_DELETE); } ImGui::Separator(); - if (ImGui::BeginTable("InsListScroll",1,ImGuiTableFlags_ScrollY)) { + int availableRows=ImGui::GetContentRegionAvail().y/ImGui::GetFrameHeight(); + if (availableRows<1) availableRows=1; + int columns=settings.horizontalDataView?(int)(ceil((double)(e->song.ins.size()+1)/(double)availableRows)):1; + if (columns<1) columns=1; + if (columns>64) columns=64; + if (ImGui::BeginTable("InsListScroll",columns,(settings.horizontalDataView?ImGuiTableFlags_ScrollX:0)|ImGuiTableFlags_ScrollY)) { if (settings.unifiedDataView) { ImGui::TableNextRow(); ImGui::TableNextColumn(); @@ -72,6 +79,11 @@ void FurnaceGUI::drawInsList() { ImGui::Indent(); } + if (settings.horizontalDataView) { + ImGui::TableNextRow(); + } + + int curRow=0; for (int i=-1; i<(int)e->song.ins.size(); i++) { String name=ICON_FA_CIRCLE_O " - None -"; const char* insType="Bug!"; @@ -211,8 +223,12 @@ void FurnaceGUI::drawInsList() { } else { ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_TEXT]); } - ImGui::TableNextRow(); - ImGui::TableNextColumn(); + if (!settings.horizontalDataView) { + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + } else if (curRow==0) { + ImGui::TableNextColumn(); + } if (ImGui::Selectable(name.c_str(),(i==-1)?(curIns<0 || curIns>=e->song.insLen):(curIns==i))) { curIns=i; } @@ -228,6 +244,9 @@ void FurnaceGUI::drawInsList() { nextWindow=GUI_WINDOW_INS_EDIT; } } + if (settings.horizontalDataView) { + if (++curRow>=availableRows) curRow=0; + } } if (settings.unifiedDataView) { diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 2653153c1..3e20f4c32 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -588,7 +588,7 @@ void FurnaceGUI::updateWindowTitle() { } if (settings.titleBarSys) { - title+=fmt::sprintf(" (%s)",e->getSongSystemName()); + title+=fmt::sprintf(" (%s)",e->getSongSystemName(!settings.noMultiSystem)); } if (sdlWin!=NULL) SDL_SetWindowTitle(sdlWin,title.c_str()); diff --git a/src/gui/gui.h b/src/gui/gui.h index f4566b552..cfbf90e3f 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -858,6 +858,8 @@ class FurnaceGUI { int moveWindowTitle; int hiddenSystems; int insLoadAlwaysReplace; + int horizontalDataView; + int noMultiSystem; unsigned int maxUndoSteps; String mainFontPath; String patFontPath; @@ -941,6 +943,8 @@ class FurnaceGUI { moveWindowTitle(0), hiddenSystems(0), insLoadAlwaysReplace(1), + horizontalDataView(0), + noMultiSystem(0), maxUndoSteps(100), mainFontPath(""), patFontPath(""), diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index b7b07cc43..87ed1fa1f 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -1006,6 +1006,12 @@ void FurnaceGUI::drawSettings() { updateWindowTitle(); } + bool noMultiSystemB=settings.noMultiSystem; + if (ImGui::Checkbox("Display chip names instead of \"multi-system\" in title bar",&noMultiSystemB)) { + settings.noMultiSystem=noMultiSystemB; + updateWindowTitle(); + } + ImGui::Text("Status bar:"); if (ImGui::RadioButton("Cursor details##sbar0",settings.statusDisplay==0)) { settings.statusDisplay=0; @@ -1077,6 +1083,16 @@ void FurnaceGUI::drawSettings() { if (ImGui::Checkbox("Unified instrument/wavetable/sample list",&unifiedDataViewB)) { settings.unifiedDataView=unifiedDataViewB; } + if (settings.unifiedDataView) { + settings.horizontalDataView=0; + } + + ImGui::BeginDisabled(settings.unifiedDataView); + bool horizontalDataViewB=settings.horizontalDataView; + if (ImGui::Checkbox("Horizontal instrument list",&horizontalDataViewB)) { + settings.horizontalDataView=horizontalDataViewB; + } + ImGui::EndDisabled(); bool chipNamesB=settings.chipNames; if (ImGui::Checkbox("Use chip names instead of system names",&chipNamesB)) { @@ -1837,6 +1853,8 @@ void FurnaceGUI::syncSettings() { settings.moveWindowTitle=e->getConfInt("moveWindowTitle",0); settings.hiddenSystems=e->getConfInt("hiddenSystems",0); settings.insLoadAlwaysReplace=e->getConfInt("insLoadAlwaysReplace",1); + settings.horizontalDataView=e->getConfInt("horizontalDataView",0); + settings.noMultiSystem=e->getConfInt("noMultiSystem",0); clampSetting(settings.mainFontSize,2,96); clampSetting(settings.patFontSize,2,96); @@ -1908,6 +1926,8 @@ void FurnaceGUI::syncSettings() { clampSetting(settings.moveWindowTitle,0,1); clampSetting(settings.hiddenSystems,0,1); clampSetting(settings.insLoadAlwaysReplace,0,1); + clampSetting(settings.horizontalDataView,0,1); + clampSetting(settings.noMultiSystem,0,1) settings.initialSys=e->decodeSysDesc(e->getConfString("initialSys","")); if (settings.initialSys.size()<4) { @@ -2020,6 +2040,8 @@ void FurnaceGUI::commitSettings() { e->setConf("hiddenSystems",settings.hiddenSystems); e->setConf("initialSys",e->encodeSysDesc(settings.initialSys)); e->setConf("insLoadAlwaysReplace",settings.insLoadAlwaysReplace); + e->setConf("horizontalDataView",settings.horizontalDataView); + e->setConf("noMultiSystem",settings.noMultiSystem); // colors for (int i=0; i Date: Thu, 5 May 2022 13:23:06 -0500 Subject: [PATCH 332/342] GUI: fix values button working as notes --- src/engine/platform/sound/ymfm/ymfm_opz.cpp | 12 +++++++++--- src/gui/gui.cpp | 2 +- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/engine/platform/sound/ymfm/ymfm_opz.cpp b/src/engine/platform/sound/ymfm/ymfm_opz.cpp index cc7a7c9d5..6408ae33a 100644 --- a/src/engine/platform/sound/ymfm/ymfm_opz.cpp +++ b/src/engine/platform/sound/ymfm/ymfm_opz.cpp @@ -70,16 +70,24 @@ // OPZ supports a "fixed frequency" mode for each operator, with a 3-bit // range and 4-bit frequency value, plus a 1-bit enable. Not sure how that // works at all, so it's not implemented. +// note by tildearrow: +// - I have verified behavior of this mode against real hardware. +// after applying a small fix on the existing early implementation, it matches hardware. +// this means fixed frequency is fully implemented and working. // // There are also several mystery fields in the operators which I have no // clue about: "fine" (4 bits), "eg_shift" (2 bits), and "rev" (3 bits). // eg_shift is some kind of envelope generator effect, but how it works is // unknown. +// note by tildearrow: +// - behavior of "fine" is now confirmed and matches hardware. // // Also, according to the site above, the panning controls are changed from // OPM, with a "mono" bit and only one control bit for the right channel. // Current implementation is just a guess. // +// additional modifications by tildearrow for Furnace +// namespace ymfm { @@ -409,9 +417,6 @@ uint32_t opz_registers::lfo_am_offset(uint32_t choffs) const void opz_registers::cache_operator_data(uint32_t choffs, uint32_t opoffs, opdata_cache &cache) { - // TODO: how does fixed frequency mode work? appears to be enabled by - // op_fix_mode(), and controlled by op_fix_range(), op_fix_frequency() - // TODO: what is op_rev()? // set up the easy stuff @@ -468,6 +473,7 @@ void opz_registers::cache_operator_data(uint32_t choffs, uint32_t opoffs, opdata cache.eg_rate[EG_REVERB] = std::min(effective_rate(reverb * 4 + 2, ksrval), cache.eg_rate[EG_REVERB]); // set the envelope shift; TX81Z manual says operator 1 shift is fixed at "off" + // TODO: change 0 to 3? operators are in reverse order in TX81Z cache.eg_shift = ((opoffs & 0x18) == 0) ? 0 : op_eg_shift(opoffs); } diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 3e20f4c32..72a575fbe 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -2091,7 +2091,7 @@ void FurnaceGUI::editOptions(bool topMenu) { } ImGui::SameLine(); if (ImGui::Button("Values")) { - doTranspose(transposeAmount,opMaskTransposeNote); + doTranspose(transposeAmount,opMaskTransposeValue); ImGui::CloseCurrentPopup(); } From 3784c4e1d226325915da259f826cab13dc6506dc Mon Sep 17 00:00:00 2001 From: Laurens Holst Date: Thu, 5 May 2022 21:20:36 +0200 Subject: [PATCH 333/342] MultiPCM instrument type, editor and file ops. --- papers/format.md | 11 ++++ src/engine/engine.h | 4 +- src/engine/instrument.cpp | 29 ++++++++++ src/engine/instrument.h | 11 ++++ src/gui/insEdit.cpp | 113 +++++++++++++++++++++++++++++++++++++- 5 files changed, 164 insertions(+), 4 deletions(-) diff --git a/papers/format.md b/papers/format.md index 42488719b..7ab678002 100644 --- a/papers/format.md +++ b/papers/format.md @@ -697,6 +697,17 @@ size | description 1 | extra 8 macro mode --- | **extra C64 data** (>=89) 1 | don't test/gate before new note + --- | **MultiPCM data** (>=93) + 1 | attack rate + 1 | decay 1 rate + 1 | decay level + 1 | decay 2 rate + 1 | release rate + 1 | rate correction + 1 | lfo rate + 1 | vib depth + 1 | am depth + 23 | reserved ``` # wavetable diff --git a/src/engine/engine.h b/src/engine/engine.h index 9ca19ec09..e9a5db2e0 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -45,8 +45,8 @@ #define BUSY_BEGIN_SOFT softLocked=true; isBusy.lock(); #define BUSY_END isBusy.unlock(); softLocked=false; -#define DIV_VERSION "dev92" -#define DIV_ENGINE_VERSION 92 +#define DIV_VERSION "dev93" +#define DIV_ENGINE_VERSION 93 // for imports #define DIV_VERSION_MOD 0xff01 diff --git a/src/engine/instrument.cpp b/src/engine/instrument.cpp index f9a39724a..8b8a1fb03 100644 --- a/src/engine/instrument.cpp +++ b/src/engine/instrument.cpp @@ -506,6 +506,20 @@ void DivInstrument::putInsData(SafeWriter* w) { // C64 no test w->writeC(c64.noTest); + + // MultiPCM + w->writeC(multipcm.ar); + w->writeC(multipcm.d1r); + w->writeC(multipcm.dl); + w->writeC(multipcm.d2r); + w->writeC(multipcm.rr); + w->writeC(multipcm.rc); + w->writeC(multipcm.lfo); + w->writeC(multipcm.vib); + w->writeC(multipcm.am); + for (int j=0; j<23; j++) { // reserved + w->writeC(0); + } } DivDataErrors DivInstrument::readInsData(SafeReader& reader, short version) { @@ -1014,6 +1028,21 @@ DivDataErrors DivInstrument::readInsData(SafeReader& reader, short version) { c64.noTest=reader.readC(); } + // MultiPCM + if (version>=93) { + multipcm.ar=reader.readC(); + multipcm.d1r=reader.readC(); + multipcm.dl=reader.readC(); + multipcm.d2r=reader.readC(); + multipcm.rr=reader.readC(); + multipcm.rc=reader.readC(); + multipcm.lfo=reader.readC(); + multipcm.vib=reader.readC(); + multipcm.am=reader.readC(); + // reserved + for (int k=0; k<23; k++) reader.readC(); + } + return DIV_DATA_SUCCESS; } diff --git a/src/engine/instrument.h b/src/engine/instrument.h index d660c5796..dc1b0b7f5 100644 --- a/src/engine/instrument.h +++ b/src/engine/instrument.h @@ -338,6 +338,16 @@ struct DivInstrumentFDS { } }; +struct DivInstrumentMultiPCM { + unsigned char ar, d1r, dl, d2r, rr, rc; + unsigned char lfo, vib, am; + + DivInstrumentMultiPCM(): + ar(15), d1r(15), dl(0), d2r(0), rr(15), rc(15), + lfo(0), vib(0), am(0) { + } +}; + enum DivWaveSynthEffects { DIV_WS_NONE=0, // one waveform effects @@ -393,6 +403,7 @@ struct DivInstrument { DivInstrumentAmiga amiga; DivInstrumentN163 n163; DivInstrumentFDS fds; + DivInstrumentMultiPCM multipcm; DivInstrumentWaveSynth ws; /** diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index ed776524d..99c5f4dd6 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -2587,6 +2587,108 @@ void FurnaceGUI::drawInsEdit() { } ImGui::EndTabItem(); } + if (ins->type==DIV_INS_MULTIPCM) { + if (ImGui::BeginTabItem("MultiPCM")) { + String sName; + if (ins->amiga.initSample<0 || ins->amiga.initSample>=e->song.sampleLen) { + sName="none selected"; + } else { + sName=e->song.sample[ins->amiga.initSample]->name; + } + if (ImGui::BeginCombo("Initial Sample",sName.c_str())) { + String id; + for (int i=0; isong.sampleLen; i++) { + id=fmt::sprintf("%d: %s",i,e->song.sample[i]->name); + if (ImGui::Selectable(id.c_str(),ins->amiga.initSample==i)) { + ins->amiga.initSample=i; + PARAMETER + } + } + ImGui::EndCombo(); + } + ImVec2 sliderSize=ImVec2(20.0f*dpiScale,128.0*dpiScale); + if (ImGui::BeginTable("MultiPCMADSRParams",7,ImGuiTableFlags_NoHostExtendX)) { + ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthFixed,sliderSize.x); + ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthFixed,sliderSize.x); + ImGui::TableSetupColumn("c2",ImGuiTableColumnFlags_WidthFixed,sliderSize.x); + ImGui::TableSetupColumn("c3",ImGuiTableColumnFlags_WidthFixed,sliderSize.x); + ImGui::TableSetupColumn("c4",ImGuiTableColumnFlags_WidthFixed,sliderSize.x); + ImGui::TableSetupColumn("c5",ImGuiTableColumnFlags_WidthFixed,sliderSize.x); + ImGui::TableSetupColumn("c6",ImGuiTableColumnFlags_WidthStretch); + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + CENTER_TEXT("AR"); + ImGui::TextUnformatted("AR"); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Attack Rate"); + } + ImGui::TableNextColumn(); + CENTER_TEXT("D1R"); + ImGui::TextUnformatted("D1R"); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Decay 1 Rate"); + } + ImGui::TableNextColumn(); + CENTER_TEXT("DL"); + ImGui::TextUnformatted("DL"); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Decay Level"); + } + ImGui::TableNextColumn(); + CENTER_TEXT("D2R"); + ImGui::TextUnformatted("D2R"); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Decay 2 Rate"); + } + ImGui::TableNextColumn(); + CENTER_TEXT("RR"); + ImGui::TextUnformatted("RR"); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Release Rate"); + } + ImGui::TableNextColumn(); + CENTER_TEXT("RC"); + ImGui::TextUnformatted("RC"); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Rate Correction"); + } + ImGui::TableNextColumn(); + CENTER_TEXT("Envelope"); + ImGui::TextUnformatted("Envelope"); + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + P(CWVSliderScalar("##Attack Rate",sliderSize,ImGuiDataType_U8,&ins->multipcm.ar,&_ZERO,&_FIFTEEN)); + ImGui::TableNextColumn(); + P(CWVSliderScalar("##Decay 1 Rate",sliderSize,ImGuiDataType_U8,&ins->multipcm.d1r,&_ZERO,&_FIFTEEN)); + ImGui::TableNextColumn(); + P(CWVSliderScalar("##Decay Level",sliderSize,ImGuiDataType_U8,&ins->multipcm.dl,&_ZERO,&_FIFTEEN)); + ImGui::TableNextColumn(); + P(CWVSliderScalar("##Decay 2 Rate",sliderSize,ImGuiDataType_U8,&ins->multipcm.d2r,&_ZERO,&_FIFTEEN)); + ImGui::TableNextColumn(); + P(CWVSliderScalar("##Release Rate",sliderSize,ImGuiDataType_U8,&ins->multipcm.rr,&_ZERO,&_FIFTEEN)); + ImGui::TableNextColumn(); + P(CWVSliderScalar("##Rate Correction",sliderSize,ImGuiDataType_U8,&ins->multipcm.rc,&_ZERO,&_FIFTEEN)); + ImGui::TableNextColumn(); + drawFMEnv(0,ins->multipcm.ar,ins->multipcm.d1r,ins->multipcm.d2r,ins->multipcm.rr,ins->multipcm.dl,0,0,0,127,15,ImVec2(ImGui::GetContentRegionAvail().x,sliderSize.y),ins->type); + ImGui::EndTable(); + } + if (ImGui::BeginTable("MultiPCMLFOParams",3,ImGuiTableFlags_SizingStretchSame)) { + ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthStretch,0.0); + ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthStretch,0.0); + ImGui::TableSetupColumn("c2",ImGuiTableColumnFlags_WidthStretch,0.0); + ImGui::TableNextColumn(); + P(CWSliderScalar("LFO Rate",ImGuiDataType_U8,&ins->multipcm.lfo,&_ZERO,&_SEVEN)); rightClickable + ImGui::TableNextColumn(); + P(CWSliderScalar("PM Depth",ImGuiDataType_U8,&ins->multipcm.vib,&_ZERO,&_SEVEN)); rightClickable + ImGui::TableNextColumn(); + P(CWSliderScalar("AM Depth",ImGuiDataType_U8,&ins->multipcm.am,&_ZERO,&_SEVEN)); rightClickable + ImGui::EndTable(); + } + ImGui::EndTabItem(); + } + } if (ins->type==DIV_INS_GB || (ins->type==DIV_INS_AMIGA && ins->amiga.useWave) || ins->type==DIV_INS_X1_010 || @@ -2718,7 +2820,7 @@ void FurnaceGUI::drawInsEdit() { if (ins->type==DIV_INS_AMIGA) { volMax=64; } - if (ins->type==DIV_INS_FM || ins->type==DIV_INS_MIKEY || ins->type==DIV_INS_SU) { + if (ins->type==DIV_INS_FM || ins->type==DIV_INS_MIKEY || ins->type==DIV_INS_MULTIPCM || ins->type==DIV_INS_SU) { volMax=127; } if (ins->type==DIV_INS_GB) { @@ -2771,7 +2873,7 @@ void FurnaceGUI::drawInsEdit() { dutyLabel="Noise"; dutyMax=8; } - if (ins->type==DIV_INS_OPLL || ins->type==DIV_INS_OPL || ins->type==DIV_INS_VRC6_SAW || ins->type==DIV_INS_FDS) { + if (ins->type==DIV_INS_OPLL || ins->type==DIV_INS_OPL || ins->type==DIV_INS_VRC6_SAW || ins->type==DIV_INS_FDS || ins->type==DIV_INS_MULTIPCM) { dutyMax=0; } if (ins->type==DIV_INS_VERA) { @@ -2803,6 +2905,7 @@ void FurnaceGUI::drawInsEdit() { if (ins->type==DIV_INS_SAA1099) waveMax=2; if (ins->type==DIV_INS_FM || ins->type==DIV_INS_OPL || ins->type==DIV_INS_OPZ) waveMax=0; if (ins->type==DIV_INS_MIKEY) waveMax=0; + if (ins->type==DIV_INS_MULTIPCM) waveMax=0; if (ins->type==DIV_INS_SU) waveMax=7; if (ins->type==DIV_INS_PET) { waveMax=8; @@ -2863,6 +2966,11 @@ void FurnaceGUI::drawInsEdit() { panMin=-16; panMax=16; } + if (ins->type==DIV_INS_MULTIPCM) { + panMin=-7; + panMax=7; + panSingleNoBit=true; + } if (ins->type==DIV_INS_SU) { panMin=-127; panMax=127; @@ -2915,6 +3023,7 @@ void FurnaceGUI::drawInsEdit() { ins->type==DIV_INS_AY || ins->type==DIV_INS_AY8930 || ins->type==DIV_INS_SWAN || + ins->type==DIV_INS_MULTIPCM || ins->type==DIV_INS_SU) { NORMAL_MACRO(ins->std.phaseResetMacro,0,1,"phaseReset","Phase Reset",32,ins->std.phaseResetMacro.open,true,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[16],0,1,NULL,false); } From 6672a886a32f63949864ddcf12ae25b317f8a2d7 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 5 May 2022 17:45:18 -0500 Subject: [PATCH 334/342] GUI: fix crash when loading instrument fixes #431 --- src/gui/gui.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 72a575fbe..700e55d09 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -3133,7 +3133,7 @@ bool FurnaceGUI::loop() { showWarning(e->getWarnings(),GUI_WARN_GENERIC); } if (curIns>=0 && curIns<(int)e->song.ins.size()) { - *e->song.ins[curIns]=*instruments[curIns]; + *e->song.ins[curIns]=*instruments[0]; } else { showError("...but you haven't selected an instrument!"); } From 5ba1833f748de77d5cbf8337bf0873e413a1b825 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 5 May 2022 18:23:28 -0500 Subject: [PATCH 335/342] GUI: I am so drunk --- src/gui/dataList.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/dataList.cpp b/src/gui/dataList.cpp index 8cc35e792..0de552054 100644 --- a/src/gui/dataList.cpp +++ b/src/gui/dataList.cpp @@ -47,7 +47,7 @@ void FurnaceGUI::drawInsList() { } ImGui::SameLine(); if (ImGui::Button(ICON_FA_FOLDER_OPEN "##InsLoad")) { - doAction((settings.insLoadAlwaysReplace && curIns>=0 && curIns<=(int)e->song.ins.size())?GUI_ACTION_INS_LIST_OPEN_REPLACE:GUI_ACTION_INS_LIST_OPEN); + doAction((settings.insLoadAlwaysReplace && curIns>=0 && curIns<(int)e->song.ins.size())?GUI_ACTION_INS_LIST_OPEN_REPLACE:GUI_ACTION_INS_LIST_OPEN); } ImGui::SameLine(); if (ImGui::Button(ICON_FA_FLOPPY_O "##InsSave")) { From 5974e8c04501b271287947618012250cdf310a65 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 5 May 2022 23:50:15 -0500 Subject: [PATCH 336/342] OPZ: fix EGShift emulation 1. the TX81Z has operators in reverse order 2. don't early out if EGShift is not 0 --- src/engine/platform/sound/ymfm/ymfm_fm.ipp | 2 +- src/engine/platform/sound/ymfm/ymfm_opz.cpp | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/engine/platform/sound/ymfm/ymfm_fm.ipp b/src/engine/platform/sound/ymfm/ymfm_fm.ipp index 4e851737a..d532d7b2e 100644 --- a/src/engine/platform/sound/ymfm/ymfm_fm.ipp +++ b/src/engine/platform/sound/ymfm/ymfm_fm.ipp @@ -480,7 +480,7 @@ if (m_choffs == 0) #endif // early out if the envelope is effectively off - if (m_env_attenuation > EG_QUIET) + if (m_env_attenuation > EG_QUIET && m_cache.eg_shift == 0) return 0; // get the absolute value of the sin, as attenuation, as a 4.8 fixed point value diff --git a/src/engine/platform/sound/ymfm/ymfm_opz.cpp b/src/engine/platform/sound/ymfm/ymfm_opz.cpp index 6408ae33a..94123bf62 100644 --- a/src/engine/platform/sound/ymfm/ymfm_opz.cpp +++ b/src/engine/platform/sound/ymfm/ymfm_opz.cpp @@ -472,9 +472,8 @@ void opz_registers::cache_operator_data(uint32_t choffs, uint32_t opoffs, opdata if (reverb != 0) cache.eg_rate[EG_REVERB] = std::min(effective_rate(reverb * 4 + 2, ksrval), cache.eg_rate[EG_REVERB]); - // set the envelope shift; TX81Z manual says operator 1 shift is fixed at "off" - // TODO: change 0 to 3? operators are in reverse order in TX81Z - cache.eg_shift = ((opoffs & 0x18) == 0) ? 0 : op_eg_shift(opoffs); + // set the envelope shift; TX81Z manual says operator 1 (actually operator 4) shift is fixed at "off" + cache.eg_shift = ((opoffs & 0x18) == 0x18) ? 0 : op_eg_shift(opoffs); } From 509e46417fed75ae261cdaf5e3820277124d8667 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 6 May 2022 00:09:49 -0500 Subject: [PATCH 337/342] OPZ: fix fixed frequency effects --- src/engine/platform/tx81z.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/engine/platform/tx81z.cpp b/src/engine/platform/tx81z.cpp index 056eeaacb..c3f7e9991 100644 --- a/src/engine/platform/tx81z.cpp +++ b/src/engine/platform/tx81z.cpp @@ -919,12 +919,13 @@ int DivPlatformTX81Z::dispatch(DivCommand c) { } case DIV_CMD_FM_FIXFREQ: { if (c.value<0 || c.value>3) break; + printf("fixfreq %x\n",c.value2); unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; op.egt=(c.value2>0); rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.egt<<5)|(op.rs<<6)); if (op.egt) { - rWrite(baseAddr+ADDR_MULT_DT,((c.value2>>4)&15)|((c.value2>>8)&7)); + rWrite(baseAddr+ADDR_MULT_DT,((c.value2>>4)&15)|(((c.value2>>8)&7)<<4)); rWrite(baseAddr+ADDR_WS_FINE,(c.value2&15)|(op.ws<<4)); } else { rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|((op.egt?(op.dt&7):dtTable[op.dt&7])<<4)); From 05af3c147abf299df00e51b3bc053381655977f5 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 6 May 2022 01:26:49 -0500 Subject: [PATCH 338/342] .ftm loading progress (incomplete, not working) --- src/engine/fileOps.cpp | 334 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 331 insertions(+), 3 deletions(-) diff --git a/src/engine/fileOps.cpp b/src/engine/fileOps.cpp index 7b09ca7d3..8c93ef5bd 100644 --- a/src/engine/fileOps.cpp +++ b/src/engine/fileOps.cpp @@ -19,6 +19,7 @@ #include "engine.h" #include "../ta-log.h" +#include "instrument.h" #include "song.h" #include #include @@ -2034,11 +2035,28 @@ bool DivEngine::loadMod(unsigned char* file, size_t len) { return success; } +#define CHECK_BLOCK_VERSION(x) \ + if (blockVersion>x) { \ + logE("incompatible block version %d for %s!",blockVersion,blockName); \ + lastError="incompatible block version"; \ + delete[] file; \ + return false; \ + } + bool DivEngine::loadFTM(unsigned char* file, size_t len) { SafeReader reader=SafeReader(file,len); warnings=""; try { DivSong ds; + String blockName; + unsigned char expansions=0; + unsigned int tchans=0; + unsigned int n163Chans=0; + bool hasSequence[256][8]; + unsigned char sequenceIndex[256][8]; + + memset(hasSequence,0,256*8*sizeof(bool)); + memset(sequenceIndex,0,256*8); if (!reader.seek(18,SEEK_SET)) { logE("premature end of file!"); @@ -2046,17 +2064,327 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len) { delete[] file; return false; } - ds.version=(unsigned short)reader.readS(); + ds.version=(unsigned short)reader.readI(); logI("module version %d (0x%.4x)",ds.version,ds.version); - if (ds.version>0x0440) { + if (ds.version>0x0450) { logE("incompatible version %x!",ds.version); lastError="incompatible version"; delete[] file; return false; } - + while (true) { + blockName=reader.readString(3); + if (blockName=="END") { + // end of module + logD("end of data"); + break; + } + + // not the end + reader.seek(-3,SEEK_CUR); + blockName=reader.readString(16); + unsigned int blockVersion=(unsigned int)reader.readI(); + unsigned int blockSize=(unsigned int)reader.readI(); + size_t blockStart=reader.tell(); + + logD("reading block %s (version %d, %d bytes)",blockName,blockVersion,blockSize); + if (blockName=="PARAMS") { + CHECK_BLOCK_VERSION(6); + unsigned int oldSpeedTempo=0; + if (blockVersion<=1) { + oldSpeedTempo=reader.readI(); + } + if (blockVersion>=2) { + expansions=reader.readC(); + } + tchans=reader.readI(); + unsigned int pal=reader.readI(); + unsigned int customHz=reader.readI(); + unsigned int newVibrato=0; + unsigned int speedSplitPoint=0; + if (blockVersion>=3) { + newVibrato=reader.readI(); + } + if (blockVersion>=4) { + ds.hilightA=reader.readI(); + ds.hilightB=reader.readI(); + } + if (expansions&8) if (blockVersion>=5) { // N163 channels + n163Chans=reader.readI(); + } + if (blockVersion>=6) { + speedSplitPoint=reader.readI(); + } + + logV("old speed/tempo: %d",oldSpeedTempo); + logV("expansions: %x",expansions); + logV("channels: %d",tchans); + logV("PAL: %d",pal); + logV("custom Hz: %d",customHz); + logV("new vibrato: %d",newVibrato); + logV("N163 channels: %d",n163Chans); + logV("highlight 1: %d",ds.hilightA); + logV("highlight 2: %d",ds.hilightB); + logV("split point: %d",speedSplitPoint); + + if (customHz!=0) { + ds.hz=customHz; + } + + // initialize channels + int systemID=0; + ds.system[systemID++]=DIV_SYSTEM_NES; + if (expansions&1) { + ds.system[systemID++]=DIV_SYSTEM_VRC6; + } + if (expansions&2) { + ds.system[systemID++]=DIV_SYSTEM_VRC7; + } + if (expansions&4) { + ds.system[systemID++]=DIV_SYSTEM_FDS; + } + if (expansions&8) { + ds.system[systemID++]=DIV_SYSTEM_MMC5; + } + if (expansions&16) { + ds.system[systemID]=DIV_SYSTEM_N163; + ds.systemFlags[systemID++]=n163Chans; + } + if (expansions&32) { + ds.system[systemID]=DIV_SYSTEM_AY8910; + ds.systemFlags[systemID++]=38; // Sunsoft 5B + } + ds.systemLen=systemID; + + unsigned int calcChans=0; + for (int i=0; iDIV_MAX_CHANS) { + tchans=DIV_MAX_CHANS; + logW("too many channels!"); + } + } else if (blockName=="INFO") { + CHECK_BLOCK_VERSION(1); + ds.name=reader.readString(32); + ds.author=reader.readString(32); + ds.copyright=reader.readString(32); + } else if (blockName=="HEADER") { + CHECK_BLOCK_VERSION(3); + unsigned char totalSongs=reader.readC(); + logV("%d songs:",totalSongs+1); + for (int i=0; i<=totalSongs; i++) { + String subSongName=reader.readString(); + logV("- %s",subSongName); + } + for (unsigned int i=0; i256) { + logE("too many instruments/out of range!"); + lastError="too many instruments/out of range"; + delete[] file; + return false; + } + + for (int i=0; i=ds.ins.size()) { + logE("instrument index %d is out of range!",insIndex); + lastError="instrument index out of range"; + delete[] file; + return false; + } + + DivInstrument* ins=ds.ins[insIndex]; + unsigned char insType=reader.readC(); + switch (insType) { + case 1: + ins->type=DIV_INS_STD; + break; + case 2: // TODO: tell VRC6 and VRC6 saw instruments apart + ins->type=DIV_INS_VRC6; + break; + case 3: + ins->type=DIV_INS_OPLL; + break; + case 4: + ins->type=DIV_INS_FDS; + break; + case 5: + ins->type=DIV_INS_N163; + break; + case 6: // 5B? + ins->type=DIV_INS_AY; + break; + default: { + logE("%d: invalid instrument type %d",insIndex,insType); + lastError="invalid instrument type"; + delete[] file; + return false; + } + } + + // instrument data + switch (ins->type) { + case DIV_INS_STD: { + unsigned int totalSeqs=reader.readI(); + if (totalSeqs>5) { + logE("%d: too many sequences!",insIndex); + lastError="too many sequences"; + delete[] file; + return false; + } + + for (unsigned int j=0; j=2)?96:72; + for (int j=0; jamiga.noteMap[j]=(short)((unsigned char)reader.readC())-1; + ins->amiga.noteFreq[j]=(unsigned char)reader.readC(); + if (blockVersion>=6) { + reader.readC(); // DMC value + } + } + break; + } + case DIV_INS_VRC6: { + unsigned int totalSeqs=reader.readI(); + if (totalSeqs>4) { + logE("%d: too many sequences!",insIndex); + lastError="too many sequences"; + delete[] file; + return false; + } + + for (unsigned int j=0; jfm.opllPreset=(unsigned int)reader.readI(); + // TODO + break; + } + case DIV_INS_FDS: { + DivWavetable* wave=new DivWavetable; + wave->len=64; + wave->max=64; + for (int j=0; j<64; j++) { + wave->data[j]=reader.readC(); + } + ins->std.waveMacro.len=1; + ins->std.waveMacro.val[0]=ds.wave.size(); + for (int j=0; j<32; j++) { + ins->fds.modTable[j]=reader.readC()-3; + } + ins->fds.modSpeed=reader.readI(); + ins->fds.modDepth=reader.readI(); + reader.readI(); // this is delay. currently ignored. TODO. + ds.wave.push_back(wave); + + ins->std.volMacro.len=reader.readC(); + ins->std.volMacro.loop=reader.readI(); + ins->std.volMacro.rel=reader.readI(); + reader.readI(); // arp mode does not apply here + for (int j=0; jstd.volMacro.len; j++) { + ins->std.volMacro.val[j]=reader.readC(); + } + + ins->std.arpMacro.len=reader.readC(); + ins->std.arpMacro.loop=reader.readI(); + ins->std.arpMacro.rel=reader.readI(); + ins->std.arpMacro.mode=reader.readI(); + for (int j=0; jstd.arpMacro.len; j++) { + ins->std.arpMacro.val[j]=reader.readC(); + } + + ins->std.pitchMacro.len=reader.readC(); + ins->std.pitchMacro.loop=reader.readI(); + ins->std.pitchMacro.rel=reader.readI(); + reader.readI(); // arp mode does not apply here + for (int j=0; jstd.pitchMacro.len; j++) { + ins->std.pitchMacro.val[j]=reader.readC(); + } + + break; + } + case DIV_INS_N163: { + // TODO! + break; + } + // TODO: 5B! + default: { + logE("%d: what's going on here?",insIndex); + lastError="invalid instrument type"; + delete[] file; + return false; + } + } + + // name + ins->name=reader.readString((unsigned int)reader.readI()); + logV("- %d: %s",insIndex,ins->name); + } + } else if (blockName=="SEQUENCES") { + CHECK_BLOCK_VERSION(6); + } else if (blockName=="FRAMES") { + CHECK_BLOCK_VERSION(3); + } else if (blockName=="PATTERNS") { + CHECK_BLOCK_VERSION(5); + } else if (blockName=="DPCM SAMPLES") { + CHECK_BLOCK_VERSION(1); + } else if (blockName=="SEQUENCES_VRC6") { + // where are the 5B and FDS sequences? + CHECK_BLOCK_VERSION(6); + } else if (blockName=="SEQUENCES_N163") { + CHECK_BLOCK_VERSION(1); + } else if (blockName=="COMMENTS") { + CHECK_BLOCK_VERSION(1); + } else { + logE("block %s is unknown!",blockName); + lastError="unknown block "+blockName; + delete[] file; + return false; + } + + if ((reader.tell()-blockStart)!=blockSize) { + logE("block %s is incomplete!",blockName); + lastError="incomplete block "+blockName; + delete[] file; + return false; + } + } } catch (EndOfFileException& e) { logE("premature end of file!"); lastError="incomplete file"; From 81a77a633b28f466ae687c9b1e7657cbcf40be63 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 6 May 2022 14:16:27 -0500 Subject: [PATCH 339/342] GUI: remove classic macro view after this, some changes to the macro view code will arrive hopefully these will make the code much cleaner (NORMAL_MACRO has too many arguments!) --- src/gui/insEdit.cpp | 446 ++++++++++--------------------------------- src/gui/settings.cpp | 5 - 2 files changed, 97 insertions(+), 354 deletions(-) diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index 99c5f4dd6..c37e9512c 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -2891,7 +2891,6 @@ void FurnaceGUI::drawInsEdit() { if (ins->type==DIV_INS_SU) { dutyMax=127; } - bool dutyIsRel=(ins->type==DIV_INS_C64 && !ins->c64.dutyIsAbs); const char* waveLabel="Waveform"; int waveMax=(ins->type==DIV_INS_AY || ins->type==DIV_INS_AY8930 || ins->type==DIV_INS_VERA)?3:63; @@ -2977,363 +2976,112 @@ void FurnaceGUI::drawInsEdit() { panSingleNoBit=true; } - if (settings.macroView==0) { // modern view - MACRO_BEGIN(28*dpiScale); - if (volMax>0) { - NORMAL_MACRO(ins->std.volMacro,volMin,volMax,"vol",volumeLabel,160,ins->std.volMacro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_VOLUME],mmlString[0],volMin,volMax,NULL,false); - } - NORMAL_MACRO(ins->std.arpMacro,arpMacroScroll,arpMacroScroll+24,"arp","Arpeggio",160,ins->std.arpMacro.open,false,NULL,true,&arpMacroScroll,(arpMode?-60:-120),120,0,0,true,1,macroAbsoluteMode,uiColors[GUI_COLOR_MACRO_PITCH],mmlString[1],-120,120,(ins->std.arpMacro.mode?(¯oHoverNote):NULL),true); - if (dutyMax>0) { - if (ins->type==DIV_INS_MIKEY) { - NORMAL_MACRO(ins->std.dutyMacro,0,dutyMax,"duty",dutyLabel,160,ins->std.dutyMacro.open,true,mikeyFeedbackBits,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[2],0,dutyMax,NULL,false); - } else { - NORMAL_MACRO(ins->std.dutyMacro,dutyMin,dutyMax,"duty",dutyLabel,160,ins->std.dutyMacro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[2],dutyMin,dutyMax,NULL,false); - } - } - if (waveMax>0) { - NORMAL_MACRO(ins->std.waveMacro,0,waveMax,"wave",waveLabel,(bitMode && ins->type!=DIV_INS_PET)?64:160,ins->std.waveMacro.open,bitMode,waveNames,false,NULL,0,0,0,((ins->type==DIV_INS_AY || ins->type==DIV_INS_AY8930)?1:0),false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_WAVE],mmlString[3],0,waveMax,NULL,false); - } - if (panMax>0) { - if (panSingle) { - NORMAL_MACRO(ins->std.panLMacro,0,2,"panL","Panning",32,ins->std.panLMacro.open,true,panBits,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[13],0,panMax,NULL,false); - } else { - if (panSingleNoBit || (ins->type==DIV_INS_AMIGA && ins->std.panLMacro.mode)) { - NORMAL_MACRO(ins->std.panLMacro,panMin,panMax,"panL","Panning",(31+panMax-panMin),ins->std.panLMacro.open,false,NULL,false,NULL,0,0,0,0,(ins->type==DIV_INS_AMIGA),(ins->type==DIV_INS_AMIGA?1:0),macroQSoundMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[13],panMin,panMax,NULL,false); - } else { - NORMAL_MACRO(ins->std.panLMacro,panMin,panMax,"panL","Panning (left)",(31+panMax-panMin),ins->std.panLMacro.open,false,NULL,false,NULL,0,0,0,0,(ins->type==DIV_INS_AMIGA),(ins->type==DIV_INS_AMIGA?1:0),macroQSoundMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[13],panMin,panMax,NULL,false); - } - if (!panSingleNoBit) { - if (ins->type==DIV_INS_AMIGA && ins->std.panLMacro.mode) { - NORMAL_MACRO(ins->std.panRMacro,0,1,"panR","Surround",32,ins->std.panRMacro.open,true,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[14],0,1,NULL,false); - } else { - NORMAL_MACRO(ins->std.panRMacro,panMin,panMax,"panR","Panning (right)",(31+panMax-panMin),ins->std.panRMacro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[14],panMin,panMax,NULL,false); - } - } - } - } - NORMAL_MACRO(ins->std.pitchMacro,pitchMacroScroll,pitchMacroScroll+160,"pitch","Pitch",160,ins->std.pitchMacro.open,false,NULL,true,&pitchMacroScroll,-2048,2047,0,0,true,1,macroRelativeMode,uiColors[GUI_COLOR_MACRO_PITCH],mmlString[15],-2048,2047,NULL,!ins->std.pitchMacro.mode); - if (ins->type==DIV_INS_FM || - ins->type==DIV_INS_STD || - ins->type==DIV_INS_OPL || - ins->type==DIV_INS_OPZ || - ins->type==DIV_INS_PCE || - ins->type==DIV_INS_GB || - ins->type==DIV_INS_AMIGA || - ins->type==DIV_INS_OPLL || - ins->type==DIV_INS_AY || - ins->type==DIV_INS_AY8930 || - ins->type==DIV_INS_SWAN || - ins->type==DIV_INS_MULTIPCM || - ins->type==DIV_INS_SU) { - NORMAL_MACRO(ins->std.phaseResetMacro,0,1,"phaseReset","Phase Reset",32,ins->std.phaseResetMacro.open,true,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[16],0,1,NULL,false); - } - if (ex1Max>0) { - if (ins->type==DIV_INS_C64) { - NORMAL_MACRO(ins->std.ex1Macro,0,ex1Max,"ex1","Filter Mode",64,ins->std.ex1Macro.open,true,filtModeBits,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[4],0,ex1Max,NULL,false); - } else if (ins->type==DIV_INS_SAA1099) { - NORMAL_MACRO(ins->std.ex1Macro,0,ex1Max,"ex1","Envelope",160,ins->std.ex1Macro.open,true,saaEnvBits,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[4],0,ex1Max,NULL,false); - } else if (ins->type==DIV_INS_X1_010) { - NORMAL_MACRO(ins->std.ex1Macro,0,ex1Max,"ex1","Envelope Mode",160,ins->std.ex1Macro.open,true,x1_010EnvBits,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[4],0,ex1Max,NULL,false); - } else if (ins->type==DIV_INS_N163) { - NORMAL_MACRO(ins->std.ex1Macro,0,ex1Max,"ex1","Waveform len.",160,ins->std.ex1Macro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[4],0,ex1Max,NULL,false); - } else if (ins->type==DIV_INS_FDS) { - NORMAL_MACRO(ins->std.ex1Macro,0,ex1Max,"ex1","Mod Depth",160,ins->std.ex1Macro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[4],0,ex1Max,NULL,false); - } else if (ins->type==DIV_INS_SU) { - NORMAL_MACRO(ins->std.ex1Macro,0,ex1Max,"ex1","Cutoff",160,ins->std.ex1Macro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[4],0,ex1Max,NULL,false); - } else { - NORMAL_MACRO(ins->std.ex1Macro,0,ex1Max,"ex1","Duty",160,ins->std.ex1Macro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[4],0,ex1Max,NULL,false); - } - } - if (ex2Max>0) { - if (ins->type==DIV_INS_C64) { - NORMAL_MACRO(ins->std.ex2Macro,0,ex2Max,"ex2","Resonance",64,ins->std.ex2Macro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[5],0,ex2Max,NULL,false); - } else if (ins->type==DIV_INS_N163) { - NORMAL_MACRO(ins->std.ex2Macro,0,ex2Max,"ex2","Waveform update",64,ins->std.ex2Macro.open,true,n163UpdateBits,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[5],0,ex2Max,NULL,false); - } else if (ins->type==DIV_INS_FDS) { - NORMAL_MACRO(ins->std.ex2Macro,0,ex2Max,"ex2","Mod Speed",160,ins->std.ex2Macro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[5],0,ex2Max,NULL,false); - } else if (ins->type==DIV_INS_SU) { - NORMAL_MACRO(ins->std.ex2Macro,0,ex2Max,"ex2","Resonance",160,ins->std.ex2Macro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[5],0,ex2Max,NULL,false); - } else { - NORMAL_MACRO(ins->std.ex2Macro,0,ex2Max,"ex2","Envelope",ex2Bit?64:160,ins->std.ex2Macro.open,ex2Bit,ayEnvBits,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[5],0,ex2Max,NULL,false); - } - } - if (ins->type==DIV_INS_C64) { - NORMAL_MACRO(ins->std.ex3Macro,0,2,"ex3","Special",32,ins->std.ex3Macro.open,true,c64SpecialBits,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[6],0,2,NULL,false); - NORMAL_MACRO(ins->std.ex4Macro,0,1,"ex4","Test/Gate",32,ins->std.ex4Macro.open,true,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[7],0,1,NULL,false); - } - if (ins->type==DIV_INS_AY || ins->type==DIV_INS_AY8930 || ins->type==DIV_INS_X1_010) { - NORMAL_MACRO(ins->std.ex3Macro,0,15,"ex3","AutoEnv Num",96,ins->std.ex3Macro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[6],0,15,NULL,false); - NORMAL_MACRO(ins->std.algMacro,0,15,"alg","AutoEnv Den",96,ins->std.algMacro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[7],0,15,NULL,false); - } - if (ins->type==DIV_INS_AY8930) { - // oh my i am running out of macros - NORMAL_MACRO(ins->std.fbMacro,0,8,"fb","Noise AND Mask",96,ins->std.fbMacro.open,true,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[8],0,8,NULL,false); - NORMAL_MACRO(ins->std.fmsMacro,0,8,"fms","Noise OR Mask",96,ins->std.fmsMacro.open,true,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[9],0,8,NULL,false); - } - if (ins->type==DIV_INS_N163) { - NORMAL_MACRO(ins->std.ex3Macro,0,255,"ex3","Waveform to Load",160,ins->std.ex3Macro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[6],0,255,NULL,false); - NORMAL_MACRO(ins->std.algMacro,0,255,"alg","Wave pos. to Load",160,ins->std.algMacro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[7],0,255,NULL,false); - NORMAL_MACRO(ins->std.fbMacro,0,252,"fb","Wave len. to Load",160,ins->std.fbMacro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[8],0,252,NULL,false); - NORMAL_MACRO(ins->std.fmsMacro,0,2,"fms","Waveform load",64,ins->std.fmsMacro.open,true,n163UpdateBits,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[9],0,2,NULL,false); - } - if (ins->type==DIV_INS_FDS) { - NORMAL_MACRO(ins->std.ex3Macro,0,127,"ex3","Mod Position",160,ins->std.ex3Macro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[6],0,2,NULL,false); - } - if (ins->type==DIV_INS_SU) { - NORMAL_MACRO(ins->std.ex3Macro,0,4,"ex3","Control",64,ins->std.ex3Macro.open,true,suControlBits,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[6],0,4,NULL,false); - } - - MACRO_END; - } else { // classic view (TODO: possibly remove) - // volume macro - ImGui::Separator(); - if (ins->type==DIV_INS_C64 && ins->c64.volIsCutoff) { - if (ins->c64.filterIsAbs) { - ImGui::Text("Cutoff Macro"); - } else { - ImGui::Text("Relative Cutoff Macro"); - } + MACRO_BEGIN(28*dpiScale); + if (volMax>0) { + NORMAL_MACRO(ins->std.volMacro,volMin,volMax,"vol",volumeLabel,160,ins->std.volMacro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_VOLUME],mmlString[0],volMin,volMax,NULL,false); + } + NORMAL_MACRO(ins->std.arpMacro,arpMacroScroll,arpMacroScroll+24,"arp","Arpeggio",160,ins->std.arpMacro.open,false,NULL,true,&arpMacroScroll,(arpMode?-60:-120),120,0,0,true,1,macroAbsoluteMode,uiColors[GUI_COLOR_MACRO_PITCH],mmlString[1],-120,120,(ins->std.arpMacro.mode?(¯oHoverNote):NULL),true); + if (dutyMax>0) { + if (ins->type==DIV_INS_MIKEY) { + NORMAL_MACRO(ins->std.dutyMacro,0,dutyMax,"duty",dutyLabel,160,ins->std.dutyMacro.open,true,mikeyFeedbackBits,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[2],0,dutyMax,NULL,false); } else { - ImGui::Text("Volume Macro"); + NORMAL_MACRO(ins->std.dutyMacro,dutyMin,dutyMax,"duty",dutyLabel,160,ins->std.dutyMacro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[2],dutyMin,dutyMax,NULL,false); } - for (int i=0; istd.volMacro.len; i++) { - if (ins->type==DIV_INS_C64 && ins->c64.volIsCutoff && !ins->c64.filterIsAbs) { - asFloat[i]=ins->std.volMacro.val[i]-18; + } + if (waveMax>0) { + NORMAL_MACRO(ins->std.waveMacro,0,waveMax,"wave",waveLabel,(bitMode && ins->type!=DIV_INS_PET)?64:160,ins->std.waveMacro.open,bitMode,waveNames,false,NULL,0,0,0,((ins->type==DIV_INS_AY || ins->type==DIV_INS_AY8930)?1:0),false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_WAVE],mmlString[3],0,waveMax,NULL,false); + } + if (panMax>0) { + if (panSingle) { + NORMAL_MACRO(ins->std.panLMacro,0,2,"panL","Panning",32,ins->std.panLMacro.open,true,panBits,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[13],0,panMax,NULL,false); + } else { + if (panSingleNoBit || (ins->type==DIV_INS_AMIGA && ins->std.panLMacro.mode)) { + NORMAL_MACRO(ins->std.panLMacro,panMin,panMax,"panL","Panning",(31+panMax-panMin),ins->std.panLMacro.open,false,NULL,false,NULL,0,0,0,0,(ins->type==DIV_INS_AMIGA),(ins->type==DIV_INS_AMIGA?1:0),macroQSoundMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[13],panMin,panMax,NULL,false); } else { - asFloat[i]=ins->std.volMacro.val[i]; + NORMAL_MACRO(ins->std.panLMacro,panMin,panMax,"panL","Panning (left)",(31+panMax-panMin),ins->std.panLMacro.open,false,NULL,false,NULL,0,0,0,0,(ins->type==DIV_INS_AMIGA),(ins->type==DIV_INS_AMIGA?1:0),macroQSoundMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[13],panMin,panMax,NULL,false); } - loopIndicator[i]=(ins->std.volMacro.loop!=-1 && i>=ins->std.volMacro.loop); - } - macroDragScroll=0; - if (volMax>0) { - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding,ImVec2(0.0f,0.0f)); - ImGui::PlotHistogram("##IVolMacro",asFloat,ins->std.volMacro.len,0,NULL,volMin,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=volMin; - macroDragMax=volMax; - macroDragLen=ins->std.volMacro.len; - macroDragActive=true; - macroDragTarget=ins->std.volMacro.val; - macroDragChar=false; - processDrags(ImGui::GetMousePos().x,ImGui::GetMousePos().y); - } - ImGui::PlotHistogram("##IVolMacro.loop",loopIndicator,ins->std.volMacro.len,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.volMacro.len; - macroLoopDragTarget=&ins->std.volMacro.loop; - macroLoopDragActive=true; - processDrags(ImGui::GetMousePos().x,ImGui::GetMousePos().y); - } - if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) { - ins->std.volMacro.loop=-1; - } - ImGui::PopStyleVar(); - if (ImGui::InputScalar("Length##IVolMacroL",ImGuiDataType_U8,&ins->std.volMacro.len,&_ONE,&_THREE)) { - if (ins->std.volMacro.len>127) ins->std.volMacro.len=127; - } - } - - // arp macro - ImGui::Separator(); - ImGui::Text("Arpeggio Macro"); - for (int i=0; istd.arpMacro.len; i++) { - asFloat[i]=ins->std.arpMacro.val[i]; - loopIndicator[i]=(ins->std.arpMacro.loop!=-1 && i>=ins->std.arpMacro.loop); - } - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding,ImVec2(0.0f,0.0f)); - ImGui::PlotHistogram("##IArpMacro",asFloat,ins->std.arpMacro.len,0,NULL,arpMode?arpMacroScroll:(arpMacroScroll-12),arpMacroScroll+(arpMode?24:12),ImVec2(400.0f*dpiScale,200.0f*dpiScale)); - if (ImGui::IsItemClicked(ImGuiMouseButton_Left)) { - macroDragStart=ImGui::GetItemRectMin(); - macroDragAreaSize=ImVec2(400.0f*dpiScale,200.0f*dpiScale); - macroDragMin=arpMacroScroll; - macroDragMax=arpMacroScroll+24; - macroDragLen=ins->std.arpMacro.len; - macroDragActive=true; - macroDragTarget=ins->std.arpMacro.val; - macroDragChar=false; - processDrags(ImGui::GetMousePos().x,ImGui::GetMousePos().y); - } - ImGui::SameLine(); - CWVSliderInt("##IArpMacroPos",ImVec2(20.0f*dpiScale,200.0f*dpiScale),&arpMacroScroll,arpMode?0:-80,70); - ImGui::PlotHistogram("##IArpMacro.loop",loopIndicator,ins->std.arpMacro.len,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.arpMacro.len; - macroLoopDragTarget=&ins->std.arpMacro.loop; - macroLoopDragActive=true; - processDrags(ImGui::GetMousePos().x,ImGui::GetMousePos().y); - } - if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) { - ins->std.arpMacro.loop=-1; - } - ImGui::PopStyleVar(); - if (ImGui::InputScalar("Length##IArpMacroL",ImGuiDataType_U8,&ins->std.arpMacro.len,&_ONE,&_THREE)) { - if (ins->std.arpMacro.len>127) ins->std.arpMacro.len=127; - } - if (ImGui::Checkbox("Fixed",&arpMode)) { - ins->std.arpMacro.mode=arpMode; - if (arpMode) { - if (arpMacroScroll<0) arpMacroScroll=0; - } - } - - // duty macro - if (dutyMax>0) { - ImGui::Separator(); - if (ins->type==DIV_INS_C64) { - if (ins->c64.dutyIsAbs) { - ImGui::Text("Duty Macro"); + if (!panSingleNoBit) { + if (ins->type==DIV_INS_AMIGA && ins->std.panLMacro.mode) { + NORMAL_MACRO(ins->std.panRMacro,0,1,"panR","Surround",32,ins->std.panRMacro.open,true,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[14],0,1,NULL,false); } else { - ImGui::Text("Relative Duty Macro"); + NORMAL_MACRO(ins->std.panRMacro,panMin,panMax,"panR","Panning (right)",(31+panMax-panMin),ins->std.panRMacro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[14],panMin,panMax,NULL,false); } - } else { - if (ins->type==DIV_INS_AY || ins->type==DIV_INS_AY8930 || ins->type==DIV_INS_SAA1099) { - ImGui::Text("Noise Frequency Macro"); - } else { - ImGui::Text("Duty/Noise Mode Macro"); - } - } - for (int i=0; istd.dutyMacro.len; i++) { - asFloat[i]=ins->std.dutyMacro.val[i]-(dutyIsRel?12:0); - loopIndicator[i]=(ins->std.dutyMacro.loop!=-1 && i>=ins->std.dutyMacro.loop); - } - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding,ImVec2(0.0f,0.0f)); - - ImGui::PlotHistogram("##IDutyMacro",asFloat,ins->std.dutyMacro.len,0,NULL,dutyIsRel?-12:0,dutyMax-(dutyIsRel?12:0),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=dutyMax; - macroDragLen=ins->std.dutyMacro.len; - macroDragActive=true; - macroDragTarget=ins->std.dutyMacro.val; - macroDragChar=false; - processDrags(ImGui::GetMousePos().x,ImGui::GetMousePos().y); - } - ImGui::PlotHistogram("##IDutyMacro.loop",loopIndicator,ins->std.dutyMacro.len,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.dutyMacro.len; - macroLoopDragTarget=&ins->std.dutyMacro.loop; - macroLoopDragActive=true; - processDrags(ImGui::GetMousePos().x,ImGui::GetMousePos().y); - } - if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) { - ins->std.dutyMacro.loop=-1; - } - ImGui::PopStyleVar(); - if (ImGui::InputScalar("Length##IDutyMacroL",ImGuiDataType_U8,&ins->std.dutyMacro.len,&_ONE,&_THREE)) { - if (ins->std.dutyMacro.len>127) ins->std.dutyMacro.len=127; - } - } - - // wave macro - if (waveMax>0) { - ImGui::Separator(); - ImGui::Text("Waveform Macro"); - for (int i=0; istd.waveMacro.len; i++) { - asFloat[i]=ins->std.waveMacro.val[i]; - if (ins->type==DIV_INS_AY || ins->type==DIV_INS_AY8930) { - asInt[i]=ins->std.waveMacro.val[i]+1; - } else { - asInt[i]=ins->std.waveMacro.val[i]; - } - loopIndicator[i]=(ins->std.waveMacro.loop!=-1 && i>=ins->std.waveMacro.loop); - } - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding,ImVec2(0.0f,0.0f)); - - ImVec2 areaSize=ImVec2(400.0f*dpiScale,200.0f*dpiScale); - if (ins->type==DIV_INS_C64 || ins->type==DIV_INS_AY || ins->type==DIV_INS_AY8930 || ins->type==DIV_INS_SAA1099) { - areaSize=ImVec2(400.0f*dpiScale,waveMax*32.0f*dpiScale); - PlotBitfield("##IWaveMacro",asInt,ins->std.waveMacro.len,0,(ins->type==DIV_INS_C64)?c64ShapeBits:ayShapeBits,waveMax,areaSize); - bitMode=true; - } else { - ImGui::PlotHistogram("##IWaveMacro",asFloat,ins->std.waveMacro.len,0,NULL,0,waveMax,areaSize); - } - if (ImGui::IsItemClicked(ImGuiMouseButton_Left)) { - macroDragStart=ImGui::GetItemRectMin(); - macroDragAreaSize=areaSize; - macroDragMin=0; - macroDragMax=waveMax; - macroDragBitOff=(ins->type==DIV_INS_AY || ins->type==DIV_INS_AY8930)?1:0; - macroDragBitMode=bitMode; - macroDragInitialValueSet=false; - macroDragInitialValue=false; - macroDragLen=ins->std.waveMacro.len; - macroDragActive=true; - macroDragTarget=ins->std.waveMacro.val; - macroDragChar=false; - processDrags(ImGui::GetMousePos().x,ImGui::GetMousePos().y); - } - ImGui::PlotHistogram("##IWaveMacro.loop",loopIndicator,ins->std.waveMacro.len,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.waveMacro.len; - macroLoopDragTarget=&ins->std.waveMacro.loop; - macroLoopDragActive=true; - processDrags(ImGui::GetMousePos().x,ImGui::GetMousePos().y); - } - if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) { - ins->std.waveMacro.loop=-1; - } - ImGui::PopStyleVar(); - if (ImGui::InputScalar("Length##IWaveMacroL",ImGuiDataType_U8,&ins->std.waveMacro.len,&_ONE,&_THREE)) { - if (ins->std.waveMacro.len>127) ins->std.waveMacro.len=127; - } - } - - // extra 1 macro - if (ex1Max>0) { - ImGui::Separator(); - if (ins->type==DIV_INS_AY8930) { - ImGui::Text("Duty Macro"); - } else { - ImGui::Text("Extra 1 Macro"); - } - for (int i=0; istd.ex1Macro.len; i++) { - asFloat[i]=ins->std.ex1Macro.val[i]; - loopIndicator[i]=(ins->std.ex1Macro.loop!=-1 && i>=ins->std.ex1Macro.loop); - } - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding,ImVec2(0.0f,0.0f)); - - ImGui::PlotHistogram("##IEx1Macro",asFloat,ins->std.ex1Macro.len,0,NULL,0,ex1Max,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=ex1Max; - macroDragLen=ins->std.ex1Macro.len; - macroDragActive=true; - macroDragTarget=ins->std.ex1Macro.val; - macroDragChar=false; - processDrags(ImGui::GetMousePos().x,ImGui::GetMousePos().y); - } - ImGui::PlotHistogram("##IEx1Macro.loop",loopIndicator,ins->std.ex1Macro.len,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.ex1Macro.len; - macroLoopDragTarget=&ins->std.ex1Macro.loop; - macroLoopDragActive=true; - processDrags(ImGui::GetMousePos().x,ImGui::GetMousePos().y); - } - if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) { - ins->std.ex1Macro.loop=-1; - } - ImGui::PopStyleVar(); - if (ImGui::InputScalar("Length##IEx1MacroL",ImGuiDataType_U8,&ins->std.ex1Macro.len,&_ONE,&_THREE)) { - if (ins->std.ex1Macro.len>127) ins->std.ex1Macro.len=127; } } } + NORMAL_MACRO(ins->std.pitchMacro,pitchMacroScroll,pitchMacroScroll+160,"pitch","Pitch",160,ins->std.pitchMacro.open,false,NULL,true,&pitchMacroScroll,-2048,2047,0,0,true,1,macroRelativeMode,uiColors[GUI_COLOR_MACRO_PITCH],mmlString[15],-2048,2047,NULL,!ins->std.pitchMacro.mode); + if (ins->type==DIV_INS_FM || + ins->type==DIV_INS_STD || + ins->type==DIV_INS_OPL || + ins->type==DIV_INS_OPZ || + ins->type==DIV_INS_PCE || + ins->type==DIV_INS_GB || + ins->type==DIV_INS_AMIGA || + ins->type==DIV_INS_OPLL || + ins->type==DIV_INS_AY || + ins->type==DIV_INS_AY8930 || + ins->type==DIV_INS_SWAN || + ins->type==DIV_INS_MULTIPCM || + ins->type==DIV_INS_SU) { + NORMAL_MACRO(ins->std.phaseResetMacro,0,1,"phaseReset","Phase Reset",32,ins->std.phaseResetMacro.open,true,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[16],0,1,NULL,false); + } + if (ex1Max>0) { + if (ins->type==DIV_INS_C64) { + NORMAL_MACRO(ins->std.ex1Macro,0,ex1Max,"ex1","Filter Mode",64,ins->std.ex1Macro.open,true,filtModeBits,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[4],0,ex1Max,NULL,false); + } else if (ins->type==DIV_INS_SAA1099) { + NORMAL_MACRO(ins->std.ex1Macro,0,ex1Max,"ex1","Envelope",160,ins->std.ex1Macro.open,true,saaEnvBits,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[4],0,ex1Max,NULL,false); + } else if (ins->type==DIV_INS_X1_010) { + NORMAL_MACRO(ins->std.ex1Macro,0,ex1Max,"ex1","Envelope Mode",160,ins->std.ex1Macro.open,true,x1_010EnvBits,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[4],0,ex1Max,NULL,false); + } else if (ins->type==DIV_INS_N163) { + NORMAL_MACRO(ins->std.ex1Macro,0,ex1Max,"ex1","Waveform len.",160,ins->std.ex1Macro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[4],0,ex1Max,NULL,false); + } else if (ins->type==DIV_INS_FDS) { + NORMAL_MACRO(ins->std.ex1Macro,0,ex1Max,"ex1","Mod Depth",160,ins->std.ex1Macro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[4],0,ex1Max,NULL,false); + } else if (ins->type==DIV_INS_SU) { + NORMAL_MACRO(ins->std.ex1Macro,0,ex1Max,"ex1","Cutoff",160,ins->std.ex1Macro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[4],0,ex1Max,NULL,false); + } else { + NORMAL_MACRO(ins->std.ex1Macro,0,ex1Max,"ex1","Duty",160,ins->std.ex1Macro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[4],0,ex1Max,NULL,false); + } + } + if (ex2Max>0) { + if (ins->type==DIV_INS_C64) { + NORMAL_MACRO(ins->std.ex2Macro,0,ex2Max,"ex2","Resonance",64,ins->std.ex2Macro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[5],0,ex2Max,NULL,false); + } else if (ins->type==DIV_INS_N163) { + NORMAL_MACRO(ins->std.ex2Macro,0,ex2Max,"ex2","Waveform update",64,ins->std.ex2Macro.open,true,n163UpdateBits,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[5],0,ex2Max,NULL,false); + } else if (ins->type==DIV_INS_FDS) { + NORMAL_MACRO(ins->std.ex2Macro,0,ex2Max,"ex2","Mod Speed",160,ins->std.ex2Macro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[5],0,ex2Max,NULL,false); + } else if (ins->type==DIV_INS_SU) { + NORMAL_MACRO(ins->std.ex2Macro,0,ex2Max,"ex2","Resonance",160,ins->std.ex2Macro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[5],0,ex2Max,NULL,false); + } else { + NORMAL_MACRO(ins->std.ex2Macro,0,ex2Max,"ex2","Envelope",ex2Bit?64:160,ins->std.ex2Macro.open,ex2Bit,ayEnvBits,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[5],0,ex2Max,NULL,false); + } + } + if (ins->type==DIV_INS_C64) { + NORMAL_MACRO(ins->std.ex3Macro,0,2,"ex3","Special",32,ins->std.ex3Macro.open,true,c64SpecialBits,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[6],0,2,NULL,false); + NORMAL_MACRO(ins->std.ex4Macro,0,1,"ex4","Test/Gate",32,ins->std.ex4Macro.open,true,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[7],0,1,NULL,false); + } + if (ins->type==DIV_INS_AY || ins->type==DIV_INS_AY8930 || ins->type==DIV_INS_X1_010) { + NORMAL_MACRO(ins->std.ex3Macro,0,15,"ex3","AutoEnv Num",96,ins->std.ex3Macro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[6],0,15,NULL,false); + NORMAL_MACRO(ins->std.algMacro,0,15,"alg","AutoEnv Den",96,ins->std.algMacro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[7],0,15,NULL,false); + } + if (ins->type==DIV_INS_AY8930) { + // oh my i am running out of macros + NORMAL_MACRO(ins->std.fbMacro,0,8,"fb","Noise AND Mask",96,ins->std.fbMacro.open,true,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[8],0,8,NULL,false); + NORMAL_MACRO(ins->std.fmsMacro,0,8,"fms","Noise OR Mask",96,ins->std.fmsMacro.open,true,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[9],0,8,NULL,false); + } + if (ins->type==DIV_INS_N163) { + NORMAL_MACRO(ins->std.ex3Macro,0,255,"ex3","Waveform to Load",160,ins->std.ex3Macro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[6],0,255,NULL,false); + NORMAL_MACRO(ins->std.algMacro,0,255,"alg","Wave pos. to Load",160,ins->std.algMacro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[7],0,255,NULL,false); + NORMAL_MACRO(ins->std.fbMacro,0,252,"fb","Wave len. to Load",160,ins->std.fbMacro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[8],0,252,NULL,false); + NORMAL_MACRO(ins->std.fmsMacro,0,2,"fms","Waveform load",64,ins->std.fmsMacro.open,true,n163UpdateBits,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[9],0,2,NULL,false); + } + if (ins->type==DIV_INS_FDS) { + NORMAL_MACRO(ins->std.ex3Macro,0,127,"ex3","Mod Position",160,ins->std.ex3Macro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[6],0,2,NULL,false); + } + if (ins->type==DIV_INS_SU) { + NORMAL_MACRO(ins->std.ex3Macro,0,4,"ex3","Control",64,ins->std.ex3Macro.open,true,suControlBits,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[6],0,4,NULL,false); + } + + MACRO_END; ImGui::EndTabItem(); } ImGui::EndTabBar(); diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index 87ed1fa1f..b98004e5e 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -1074,11 +1074,6 @@ void FurnaceGUI::drawSettings() { settings.separateFMColors=separateFMColorsB; } - bool macroViewB=settings.macroView; - if (ImGui::Checkbox("Classic macro view (standard macros only; deprecated!)",¯oViewB)) { - settings.macroView=macroViewB; - } - bool unifiedDataViewB=settings.unifiedDataView; if (ImGui::Checkbox("Unified instrument/wavetable/sample list",&unifiedDataViewB)) { settings.unifiedDataView=unifiedDataViewB; From 716ef56f03e8499a97785d2e83e348a1f71d80eb Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 6 May 2022 18:01:12 -0500 Subject: [PATCH 340/342] GUI: macro UI refactor, part 1 - PLEASE READ - DO NOT USE THIS VERSION! - to @cam900 and @grauw: do NOT pull this commit yet! it will fail to compile! --- src/gui/insEdit.cpp | 303 ++++++++++++++++++-------------------------- 1 file changed, 123 insertions(+), 180 deletions(-) diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index c37e9512c..48919dafd 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -1067,6 +1067,34 @@ void FurnaceGUI::drawGBEnv(unsigned char vol, unsigned char len, unsigned char s #define PARAMETER MARK_MODIFIED; e->notifyInsChange(curIns); +struct FurnaceGUIMacroDesc { + DivInstrumentMacro* macro; + int min, max; + float height; + const char* displayName; + const char** bitfieldBits; + const char* modeName; + ImVec4 color; + unsigned int bitOffset; + bool isBitfield, blockMode; + String (*hoverFunc)(int,float); + + FurnaceGUIMacroDesc(const char* name, DivInstrumentMacro* m, int macroMin, int macroMax, float macroHeight, ImVec4 col=ImVec4(1.0f,1.0f,1.0f,1.0f), bool block=false, const char* mName=NULL, String (*hf)(int,float)=NULL, bool bitfield=false, const char** bfVal=NULL, unsigned int bitOff=0): + macro(m), + min(macroMin), + max(macroMax), + height(macroHeight), + displayName(name), + bitfieldBits(bfVal), + modeName(mName), + color(col), + bitOffset(bitOff), + isBitfield(bitfield), + blockMode(block), + hoverFunc(hf) { + } +}; + #define NORMAL_MACRO(macro,macroMin,macroHeight,macroName,displayName,displayHeight,displayLoop,bitfield,bfVal,drawSlider,sliderVal,sliderLow,sliderHigh,macroDispMin,bitOff,macroMode,macroModeMax,displayModeName,macroColor,mmlStr,macroAMin,macroAMax,hoverFunc,blockMode) \ ImGui::TableNextRow(); \ ImGui::TableNextColumn(); \ @@ -1167,96 +1195,6 @@ void FurnaceGUI::drawGBEnv(unsigned char vol, unsigned char len, unsigned char s } \ ImGui::PopStyleVar(); -#define OP_MACRO(macro,macroHeight,op,macroName,displayName,displayHeight,displayLoop,bitfield,bfVal,macroMode,macroModeMax,displayModeName,mmlStr) \ - ImGui::TableNextRow(); \ - ImGui::TableNextColumn(); \ - ImGui::Text("%s",displayName); \ - ImGui::SameLine(); \ - if (ImGui::SmallButton(displayLoop?(ICON_FA_CHEVRON_UP "##IOPMacroOpen_" macroName):(ICON_FA_CHEVRON_DOWN "##IOPMacroOpen_" macroName))) { \ - displayLoop=!displayLoop; \ - } \ - if (displayLoop) { \ - ImGui::SetNextItemWidth(lenAvail); \ - if (ImGui::InputScalar("##IOPMacroLen_" #op macroName,ImGuiDataType_U8,¯o.len,&_ONE,&_THREE)) { MARK_MODIFIED \ - if (macro.len>128) macro.len=128; \ - } \ - if (macroMode) { \ - bool modeVal=macro.mode; \ - if (ImGui::Checkbox("Fixed##IOPMacroMode_" macroName,&modeVal)) { \ - macro.mode=modeVal; \ - } \ - } \ - } \ - ImGui::TableNextColumn(); \ - for (int j=0; j<256; j++) { \ - if (j+macroDragScroll>=macro.len) { \ - asFloat[j]=0; \ - asInt[j]=0; \ - } else { \ - asFloat[j]=macro.val[j+macroDragScroll]; \ - asInt[j]=macro.val[j+macroDragScroll]; \ - } \ - if (j+macroDragScroll>=macro.len || (j+macroDragScroll>macro.rel && macro.loop=macro.loop))|((macro.rel!=-1 && (j+macroDragScroll)==macro.rel)<<1); \ - } \ - } \ - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding,ImVec2(0.0f,0.0f)); \ - \ - if (bitfield) { \ - PlotBitfield("##IOPMacro_" #op macroName,asInt,totalFit,0,bfVal,macroHeight,ImVec2(availableWidth,displayLoop?(displayHeight*dpiScale):(24*dpiScale))); \ - } else { \ - PlotCustom("##IOPMacro_" #op macroName,asFloat,totalFit,macroDragScroll,NULL,0,macroHeight,ImVec2(availableWidth,displayLoop?(displayHeight*dpiScale):(24*dpiScale)),sizeof(float),uiColors[GUI_COLOR_MACRO_OTHER],macro.len-macroDragScroll); \ - } \ - if (displayLoop && (ImGui::IsItemClicked(ImGuiMouseButton_Left) || ImGui::IsItemClicked(ImGuiMouseButton_Right))) { \ - macroDragStart=ImGui::GetItemRectMin(); \ - macroDragAreaSize=ImVec2(availableWidth,displayHeight*dpiScale); \ - macroDragMin=0; \ - macroDragMax=macroHeight; \ - macroDragBitOff=0; \ - macroDragBitMode=bitfield; \ - macroDragInitialValueSet=false; \ - macroDragInitialValue=false; \ - macroDragLen=totalFit; \ - macroDragActive=true; \ - macroDragTarget=macro.val; \ - macroDragChar=false; \ - macroDragLineMode=(bitfield)?false:ImGui::IsItemClicked(ImGuiMouseButton_Right); \ - macroDragLineInitial=ImVec2(0,0); \ - processDrags(ImGui::GetMousePos().x,ImGui::GetMousePos().y); \ - } \ - if (displayLoop) { \ - PlotCustom("##IOPMacroLoop_" #op macroName,loopIndicator,totalFit,macroDragScroll,NULL,0,2,ImVec2(availableWidth,12.0f*dpiScale),sizeof(float),uiColors[GUI_COLOR_MACRO_OTHER],macro.len-macroDragScroll,¯oHoverLoop); \ - if (ImGui::IsItemClicked(ImGuiMouseButton_Left)) { \ - macroLoopDragStart=ImGui::GetItemRectMin(); \ - macroLoopDragAreaSize=ImVec2(availableWidth,8.0f*dpiScale); \ - macroLoopDragLen=totalFit; \ - if (ImGui::IsKeyDown(ImGuiKey_LeftShift) || ImGui::IsKeyDown(ImGuiKey_RightShift)) { \ - macroLoopDragTarget=¯o.rel; \ - } else { \ - macroLoopDragTarget=¯o.loop; \ - } \ - macroLoopDragActive=true; \ - processDrags(ImGui::GetMousePos().x,ImGui::GetMousePos().y); \ - } \ - if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) { \ - if (ImGui::IsKeyDown(ImGuiKey_LeftShift) || ImGui::IsKeyDown(ImGuiKey_RightShift)) { \ - macro.rel=-1; \ - } else { \ - macro.loop=-1; \ - } \ - } \ - ImGui::SetNextItemWidth(availableWidth); \ - if (ImGui::InputText("##IOPMacroMML_" macroName,&mmlStr)) { \ - decodeMMLStr(mmlStr,macro.val,macro.len,macro.loop,0,bitfield?((1<<(bitfield?macroHeight:0))-1):(macroHeight),macro.rel); \ - } \ - if (!ImGui::IsItemActive()) { \ - encodeMMLStr(mmlStr,macro.val,macro.len,macro.loop,macro.rel); \ - } \ - } \ - ImGui::PopStyleVar(); - #define MACRO_BEGIN(reservedSpace) \ if (ImGui::BeginTable("MacroSpace",2)) { \ ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthFixed,0.0); \ @@ -1411,6 +1349,7 @@ void FurnaceGUI::drawInsEdit() { if (ImGui::BeginTabBar("insEditTab")) { + std::vector macroList; if (ins->type==DIV_INS_FM || ins->type==DIV_INS_OPL || ins->type==DIV_INS_OPLL || ins->type==DIV_INS_OPZ) { char label[32]; float asFloat[256]; @@ -2205,31 +2144,31 @@ void FurnaceGUI::drawInsEdit() { if (ImGui::BeginTabItem("FM Macros")) { MACRO_BEGIN(0); if (ins->type==DIV_INS_OPLL) { - NORMAL_MACRO(ins->std.algMacro,0,1,"alg",FM_NAME(FM_SUS),32,ins->std.algMacro.open,true,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[0],0,1,NULL,false); - NORMAL_MACRO(ins->std.fbMacro,0,7,"fb",FM_NAME(FM_FB),96,ins->std.fbMacro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[1],0,7,NULL,false); - NORMAL_MACRO(ins->std.fmsMacro,0,1,"fms",FM_NAME(FM_DC),32,ins->std.fmsMacro.open,true,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[2],0,1,NULL,false); - NORMAL_MACRO(ins->std.amsMacro,0,1,"ams",FM_NAME(FM_DM),32,ins->std.amsMacro.open,true,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[3],0,1,NULL,false); + macroList.push_back(FurnaceGUIMacroDesc(FM_NAME(FM_SUS),&ins->std.algMacro,0,1,32,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true)); + macroList.push_back(FurnaceGUIMacroDesc(FM_NAME(FM_FB),&ins->std.fbMacro,0,7,96,uiColors[GUI_COLOR_MACRO_OTHER])); + macroList.push_back(FurnaceGUIMacroDesc(FM_NAME(FM_DC),&ins->std.fmsMacro,0,1,32,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true)); + macroList.push_back(FurnaceGUIMacroDesc(FM_NAME(FM_DM),&ins->std.amsMacro,0,1,32,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true)); } else { - NORMAL_MACRO(ins->std.algMacro,0,7,"alg",FM_NAME(FM_ALG),96,ins->std.algMacro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[0],0,7,NULL,false); - NORMAL_MACRO(ins->std.fbMacro,0,7,"fb",FM_NAME(FM_FB),96,ins->std.fbMacro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[1],0,7,NULL,false); + macroList.push_back(FurnaceGUIMacroDesc(FM_NAME(FM_ALG),&ins->std.algMacro,0,7,96,uiColors[GUI_COLOR_MACRO_OTHER])); + macroList.push_back(FurnaceGUIMacroDesc(FM_NAME(FM_FB),&ins->std.fbMacro,0,7,96,uiColors[GUI_COLOR_MACRO_OTHER])); if (ins->type!=DIV_INS_OPL) { if (ins->type==DIV_INS_OPZ) { // TODO: FMS2/AMS2 macros - NORMAL_MACRO(ins->std.fmsMacro,0,7,"fms",FM_NAME(FM_FMS),96,ins->std.fmsMacro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[2],0,7,NULL,false); - NORMAL_MACRO(ins->std.amsMacro,0,3,"ams",FM_NAME(FM_AMS),48,ins->std.amsMacro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[4],0,3,NULL,false); + macroList.push_back(FurnaceGUIMacroDesc(FM_NAME(FM_FMS),&ins->std.fmsMacro,0,7,96,uiColors[GUI_COLOR_MACRO_OTHER])); + macroList.push_back(FurnaceGUIMacroDesc(FM_NAME(FM_AMS),&ins->std.amsMacro,0,3,48,uiColors[GUI_COLOR_MACRO_OTHER])); } else { - NORMAL_MACRO(ins->std.fmsMacro,0,7,"fms",FM_NAME(FM_FMS),96,ins->std.fmsMacro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[2],0,7,NULL,false); - NORMAL_MACRO(ins->std.amsMacro,0,3,"ams",FM_NAME(FM_AMS),48,ins->std.amsMacro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[3],0,3,NULL,false); + macroList.push_back(FurnaceGUIMacroDesc(FM_NAME(FM_FMS),&ins->std.fmsMacro,0,7,96,uiColors[GUI_COLOR_MACRO_OTHER])); + macroList.push_back(FurnaceGUIMacroDesc(FM_NAME(FM_AMS),&ins->std.amsMacro,0,3,48,uiColors[GUI_COLOR_MACRO_OTHER])); } } } if (ins->type==DIV_INS_FM || ins->type==DIV_INS_OPZ) { - NORMAL_MACRO(ins->std.ex1Macro,0,127,"ex1","AM Depth",128,ins->std.ex1Macro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[6],0,127,NULL,false); - NORMAL_MACRO(ins->std.ex2Macro,0,127,"ex2","PM Depth",128,ins->std.ex2Macro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[7],0,127,NULL,false); - NORMAL_MACRO(ins->std.ex3Macro,0,255,"ex3","LFO Speed",128,ins->std.ex3Macro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[8],0,255,NULL,false); - NORMAL_MACRO(ins->std.waveMacro,0,3,"wave","LFO Shape",48,ins->std.waveMacro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_WAVE],mmlString[9],0,3,¯oLFOWaves,false); - NORMAL_MACRO(ins->std.ex4Macro,0,4,"ex4","OpMask",128,ins->std.ex4Macro.open,true,fmOperatorBits,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[10],0,4,NULL,false); + macroList.push_back(FurnaceGUIMacroDesc("AM Depth",&ins->std.ex1Macro,0,127,128,uiColors[GUI_COLOR_MACRO_OTHER])); + macroList.push_back(FurnaceGUIMacroDesc("PM Depth",&ins->std.ex2Macro,0,127,128,uiColors[GUI_COLOR_MACRO_OTHER])); + macroList.push_back(FurnaceGUIMacroDesc("LFO Speed",&ins->std.ex3Macro,0,255,128,uiColors[GUI_COLOR_MACRO_OTHER])); + macroList.push_back(FurnaceGUIMacroDesc("LFO Shape",&ins->std.waveMacro,0,3,48,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,macroLFOWaves)); + macroList.push_back(FurnaceGUIMacroDesc("OpMask",&ins->std.ex4Macro,0,4,128,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true,fmOperatorBits)); } MACRO_END; ImGui::EndTabItem(); @@ -2254,45 +2193,48 @@ void FurnaceGUI::drawInsEdit() { int maxArDr=(ins->type==DIV_INS_FM || ins->type==DIV_INS_OPZ)?31:15; if (ins->type==DIV_INS_OPL) { - OP_MACRO(ins->std.opMacros[ordi].tlMacro,maxTl,ordi,"tl",FM_NAME(FM_TL),128,ins->std.opMacros[ordi].tlMacro.open,false,NULL,false,0,macroDummyMode,mmlString[0]); - OP_MACRO(ins->std.opMacros[ordi].arMacro,maxArDr,ordi,"ar",FM_NAME(FM_AR),64,ins->std.opMacros[ordi].arMacro.open,false,NULL,false,0,macroDummyMode,mmlString[1]); - OP_MACRO(ins->std.opMacros[ordi].drMacro,maxArDr,ordi,"dr",FM_NAME(FM_DR),64,ins->std.opMacros[ordi].drMacro.open,false,NULL,false,0,macroDummyMode,mmlString[2]); - OP_MACRO(ins->std.opMacros[ordi].slMacro,15,ordi,"sl",FM_NAME(FM_SL),64,ins->std.opMacros[ordi].slMacro.open,false,NULL,false,0,macroDummyMode,mmlString[5]); - OP_MACRO(ins->std.opMacros[ordi].rrMacro,15,ordi,"rr",FM_NAME(FM_RR),64,ins->std.opMacros[ordi].rrMacro.open,false,NULL,false,0,macroDummyMode,mmlString[4]); - OP_MACRO(ins->std.opMacros[ordi].kslMacro,3,ordi,"ksl",FM_NAME(FM_KSL),32,ins->std.opMacros[ordi].kslMacro.open,false,NULL,false,0,macroDummyMode,mmlString[6]); - OP_MACRO(ins->std.opMacros[ordi].multMacro,15,ordi,"mult",FM_NAME(FM_MULT),64,ins->std.opMacros[ordi].multMacro.open,false,NULL,false,0,macroDummyMode,mmlString[7]); - OP_MACRO(ins->std.opMacros[ordi].wsMacro,7,ordi,"ws",FM_NAME(FM_WS),64,ins->std.opMacros[ordi].wsMacro.open,false,NULL,false,0,macroDummyMode,mmlString[8]); + macroList.push_back(FurnaceGUIMacroDesc(FM_NAME(FM_TL),&ins->std.opMacros[ordi].tlMacro,0,maxTl,128,uiColors[GUI_COLOR_MACRO_OTHER])); + macroList.push_back(FurnaceGUIMacroDesc(FM_NAME(FM_AR),&ins->std.opMacros[ordi].arMacro,0,maxArDr,64,uiColors[GUI_COLOR_MACRO_OTHER])); + macroList.push_back(FurnaceGUIMacroDesc(FM_NAME(FM_DR),&ins->std.opMacros[ordi].drMacro,0,maxArDr,64,uiColors[GUI_COLOR_MACRO_OTHER])); + macroList.push_back(FurnaceGUIMacroDesc(FM_NAME(FM_SL),&ins->std.opMacros[ordi].slMacro,0,15,64,uiColors[GUI_COLOR_MACRO_OTHER])); + macroList.push_back(FurnaceGUIMacroDesc(FM_NAME(FM_RR),&ins->std.opMacros[ordi].rrMacro,0,15,64,uiColors[GUI_COLOR_MACRO_OTHER])); + macroList.push_back(FurnaceGUIMacroDesc(FM_NAME(FM_KSL),&ins->std.opMacros[ordi].kslMacro,0,3,32,uiColors[GUI_COLOR_MACRO_OTHER])); + macroList.push_back(FurnaceGUIMacroDesc(FM_NAME(FM_MULT),&ins->std.opMacros[ordi].multMacro,0,15,64,uiColors[GUI_COLOR_MACRO_OTHER])); + macroList.push_back(FurnaceGUIMacroDesc(FM_NAME(FM_WS),&ins->std.opMacros[ordi].wsMacro,0,7,64,uiColors[GUI_COLOR_MACRO_OTHER])); - OP_MACRO(ins->std.opMacros[ordi].amMacro,1,ordi,"am",FM_NAME(FM_AM),32,ins->std.opMacros[ordi].amMacro.open,true,NULL,false,0,macroDummyMode,mmlString[9]); - OP_MACRO(ins->std.opMacros[ordi].vibMacro,1,ordi,"vib",FM_NAME(FM_VIB),32,ins->std.opMacros[ordi].vibMacro.open,true,NULL,false,0,macroDummyMode,mmlString[10]); - OP_MACRO(ins->std.opMacros[ordi].ksrMacro,1,ordi,"ksr",FM_NAME(FM_KSR),32,ins->std.opMacros[ordi].ksrMacro.open,true,NULL,false,0,macroDummyMode,mmlString[11]); - OP_MACRO(ins->std.opMacros[ordi].susMacro,1,ordi,"sus",FM_NAME(FM_SUS),32,ins->std.opMacros[ordi].susMacro.open,true,NULL,false,0,macroDummyMode,mmlString[12]); + macroList.push_back(FurnaceGUIMacroDesc(FM_NAME(FM_AM),&ins->std.opMacros[ordi].amMacro,0,1,32,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true)); + macroList.push_back(FurnaceGUIMacroDesc(FM_NAME(FM_VIB),&ins->std.opMacros[ordi].vibMacro,0,1,32,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true)); + macroList.push_back(FurnaceGUIMacroDesc(FM_NAME(FM_KSR),&ins->std.opMacros[ordi].ksrMacro,0,1,32,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true)); + macroList.push_back(FurnaceGUIMacroDesc(FM_NAME(FM_SUS),&ins->std.opMacros[ordi].susMacro,0,1,32,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true)); } else if (ins->type==DIV_INS_OPLL) { - OP_MACRO(ins->std.opMacros[ordi].tlMacro,maxTl,ordi,"tl",FM_NAME(FM_TL),128,ins->std.opMacros[ordi].tlMacro.open,false,NULL,false,0,macroDummyMode,mmlString[0]); - OP_MACRO(ins->std.opMacros[ordi].arMacro,maxArDr,ordi,"ar",FM_NAME(FM_AR),64,ins->std.opMacros[ordi].arMacro.open,false,NULL,false,0,macroDummyMode,mmlString[1]); - OP_MACRO(ins->std.opMacros[ordi].drMacro,maxArDr,ordi,"dr",FM_NAME(FM_DR),64,ins->std.opMacros[ordi].drMacro.open,false,NULL,false,0,macroDummyMode,mmlString[2]); - OP_MACRO(ins->std.opMacros[ordi].slMacro,15,ordi,"sl",FM_NAME(FM_SL),64,ins->std.opMacros[ordi].slMacro.open,false,NULL,false,0,macroDummyMode,mmlString[5]); - OP_MACRO(ins->std.opMacros[ordi].rrMacro,15,ordi,"rr",FM_NAME(FM_RR),64,ins->std.opMacros[ordi].rrMacro.open,false,NULL,false,0,macroDummyMode,mmlString[4]); - OP_MACRO(ins->std.opMacros[ordi].kslMacro,3,ordi,"ksl",FM_NAME(FM_KSL),32,ins->std.opMacros[ordi].kslMacro.open,false,NULL,false,0,macroDummyMode,mmlString[6]); - OP_MACRO(ins->std.opMacros[ordi].multMacro,15,ordi,"mult",FM_NAME(FM_MULT),64,ins->std.opMacros[ordi].multMacro.open,false,NULL,false,0,macroDummyMode,mmlString[7]); - - OP_MACRO(ins->std.opMacros[ordi].amMacro,1,ordi,"am",FM_NAME(FM_AM),32,ins->std.opMacros[ordi].amMacro.open,true,NULL,false,0,macroDummyMode,mmlString[8]); - OP_MACRO(ins->std.opMacros[ordi].vibMacro,1,ordi,"vib",FM_NAME(FM_VIB),32,ins->std.opMacros[ordi].vibMacro.open,true,NULL,false,0,macroDummyMode,mmlString[9]); - OP_MACRO(ins->std.opMacros[ordi].ksrMacro,1,ordi,"ksr",FM_NAME(FM_KSR),32,ins->std.opMacros[ordi].ksrMacro.open,true,NULL,false,0,macroDummyMode,mmlString[10]); - OP_MACRO(ins->std.opMacros[ordi].egtMacro,1,ordi,"egt",FM_NAME(FM_EGS),32,ins->std.opMacros[ordi].egtMacro.open,true,NULL,false,0,macroDummyMode,mmlString[11]); + macroList.push_back(FurnaceGUIMacroDesc(FM_NAME(FM_TL),&ins->std.opMacros[ordi].tlMacro,0,maxTl,128,uiColors[GUI_COLOR_MACRO_OTHER])); + macroList.push_back(FurnaceGUIMacroDesc(FM_NAME(FM_AR),&ins->std.opMacros[ordi].arMacro,0,maxArDr,64,uiColors[GUI_COLOR_MACRO_OTHER])); + macroList.push_back(FurnaceGUIMacroDesc(FM_NAME(FM_DR),&ins->std.opMacros[ordi].drMacro,0,maxArDr,64,uiColors[GUI_COLOR_MACRO_OTHER])); + macroList.push_back(FurnaceGUIMacroDesc(FM_NAME(FM_SL),&ins->std.opMacros[ordi].slMacro,0,15,64,uiColors[GUI_COLOR_MACRO_OTHER])); + macroList.push_back(FurnaceGUIMacroDesc(FM_NAME(FM_RR),&ins->std.opMacros[ordi].rrMacro,0,15,64,uiColors[GUI_COLOR_MACRO_OTHER])); + macroList.push_back(FurnaceGUIMacroDesc(FM_NAME(FM_KSL),&ins->std.opMacros[ordi].kslMacro,0,3,32,uiColors[GUI_COLOR_MACRO_OTHER])); + macroList.push_back(FurnaceGUIMacroDesc(FM_NAME(FM_MULT),&ins->std.opMacros[ordi].multMacro,0,15,64,uiColors[GUI_COLOR_MACRO_OTHER])); + + macroList.push_back(FurnaceGUIMacroDesc(FM_NAME(FM_AM),&ins->std.opMacros[ordi].amMacro,0,1,32,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true)); + macroList.push_back(FurnaceGUIMacroDesc(FM_NAME(FM_VIB),&ins->std.opMacros[ordi].vibMacro,0,1,32,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true)); + macroList.push_back(FurnaceGUIMacroDesc(FM_NAME(FM_KSR),&ins->std.opMacros[ordi].ksrMacro,0,1,32,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true)); + macroList.push_back(FurnaceGUIMacroDesc(FM_NAME(FM_EGS),&ins->std.opMacros[ordi].egtMacro,0,1,32,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true)); } else { - OP_MACRO(ins->std.opMacros[ordi].tlMacro,maxTl,ordi,"tl",FM_NAME(FM_TL),128,ins->std.opMacros[ordi].tlMacro.open,false,NULL,false,0,macroDummyMode,mmlString[0]); - OP_MACRO(ins->std.opMacros[ordi].arMacro,maxArDr,ordi,"ar",FM_NAME(FM_AR),64,ins->std.opMacros[ordi].arMacro.open,false,NULL,false,0,macroDummyMode,mmlString[1]); - OP_MACRO(ins->std.opMacros[ordi].drMacro,maxArDr,ordi,"dr",FM_NAME(FM_DR),64,ins->std.opMacros[ordi].drMacro.open,false,NULL,false,0,macroDummyMode,mmlString[2]); - OP_MACRO(ins->std.opMacros[ordi].d2rMacro,31,ordi,"d2r",FM_NAME(FM_D2R),64,ins->std.opMacros[ordi].d2rMacro.open,false,NULL,false,0,macroDummyMode,mmlString[3]); - OP_MACRO(ins->std.opMacros[ordi].rrMacro,15,ordi,"rr",FM_NAME(FM_RR),64,ins->std.opMacros[ordi].rrMacro.open,false,NULL,false,0,macroDummyMode,mmlString[4]); - OP_MACRO(ins->std.opMacros[ordi].slMacro,15,ordi,"sl",FM_NAME(FM_SL),64,ins->std.opMacros[ordi].slMacro.open,false,NULL,false,0,macroDummyMode,mmlString[5]); - OP_MACRO(ins->std.opMacros[ordi].rsMacro,3,ordi,"rs",FM_NAME(FM_RS),32,ins->std.opMacros[ordi].rsMacro.open,false,NULL,false,0,macroDummyMode,mmlString[6]); - OP_MACRO(ins->std.opMacros[ordi].multMacro,15,ordi,"mult",FM_NAME(FM_MULT),64,ins->std.opMacros[ordi].multMacro.open,false,NULL,false,0,macroDummyMode,mmlString[7]); - OP_MACRO(ins->std.opMacros[ordi].dtMacro,7,ordi,"dt",FM_NAME(FM_DT),64,ins->std.opMacros[ordi].dtMacro.open,false,NULL,false,0,macroDummyMode,mmlString[8]); - OP_MACRO(ins->std.opMacros[ordi].dt2Macro,3,ordi,"dt2",FM_NAME(FM_DT2),32,ins->std.opMacros[ordi].dt2Macro.open,false,NULL,false,0,macroDummyMode,mmlString[9]); - OP_MACRO(ins->std.opMacros[ordi].amMacro,1,ordi,"am",FM_NAME(FM_AM),32,ins->std.opMacros[ordi].amMacro.open,true,NULL,false,0,macroDummyMode,mmlString[10]); - OP_MACRO(ins->std.opMacros[ordi].ssgMacro,4,ordi,"ssg",FM_NAME(FM_SSG),64,ins->std.opMacros[ordi].ssgMacro.open,true,ssgEnvBits,false,0,macroDummyMode,mmlString[11]); + macroList.push_back(FurnaceGUIMacroDesc(FM_NAME(FM_TL),&ins->std.opMacros[ordi].tlMacro,0,maxTl,128,uiColors[GUI_COLOR_MACRO_OTHER])); + macroList.push_back(FurnaceGUIMacroDesc(FM_NAME(FM_AR),&ins->std.opMacros[ordi].arMacro,0,maxArDr,64,uiColors[GUI_COLOR_MACRO_OTHER])); + macroList.push_back(FurnaceGUIMacroDesc(FM_NAME(FM_DR),&ins->std.opMacros[ordi].drMacro,0,maxArDr,64,uiColors[GUI_COLOR_MACRO_OTHER])); + macroList.push_back(FurnaceGUIMacroDesc(FM_NAME(FM_D2R),&ins->std.opMacros[ordi].d2rMacro,0,31,64,uiColors[GUI_COLOR_MACRO_OTHER])); + macroList.push_back(FurnaceGUIMacroDesc(FM_NAME(FM_RR),&ins->std.opMacros[ordi].rrMacro,0,15,64,uiColors[GUI_COLOR_MACRO_OTHER])); + macroList.push_back(FurnaceGUIMacroDesc(FM_NAME(FM_SL),&ins->std.opMacros[ordi].slMacro,0,15,64,uiColors[GUI_COLOR_MACRO_OTHER])); + macroList.push_back(FurnaceGUIMacroDesc(FM_NAME(FM_RS),&ins->std.opMacros[ordi].rsMacro,0,3,32,uiColors[GUI_COLOR_MACRO_OTHER])); + macroList.push_back(FurnaceGUIMacroDesc(FM_NAME(FM_MULT),&ins->std.opMacros[ordi].multMacro,0,15,64,uiColors[GUI_COLOR_MACRO_OTHER])); + macroList.push_back(FurnaceGUIMacroDesc(FM_NAME(FM_DT),&ins->std.opMacros[ordi].dtMacro,0,7,64,uiColors[GUI_COLOR_MACRO_OTHER])); + macroList.push_back(FurnaceGUIMacroDesc(FM_NAME(FM_DT2),&ins->std.opMacros[ordi].dt2Macro,0,3,32,uiColors[GUI_COLOR_MACRO_OTHER])); + macroList.push_back(FurnaceGUIMacroDesc(FM_NAME(FM_AM),&ins->std.opMacros[ordi].amMacro,0,1,32,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true)); + + if (ins->type==DIV_INS_FM) { + macroList.push_back(FurnaceGUIMacroDesc(FM_NAME(FM_SSG),&ins->std.opMacros[ordi].ssgMacro,0,4,64,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true,ssgEnvBits)); + } } MACRO_END; ImGui::PopID(); @@ -2793,6 +2735,7 @@ void FurnaceGUI::drawInsEdit() { } } if (ImGui::BeginTabItem("Macros")) { + // TODO: rewrite all of this float asFloat[256]; int asInt[256]; float loopIndicator[256]; @@ -2833,8 +2776,6 @@ void FurnaceGUI::drawInsEdit() { volMax=32; } - bool arpMode=ins->std.arpMacro.mode; - const char* dutyLabel="Duty/Noise"; int dutyMin=0; int dutyMax=3; @@ -2976,40 +2917,39 @@ void FurnaceGUI::drawInsEdit() { panSingleNoBit=true; } - MACRO_BEGIN(28*dpiScale); if (volMax>0) { - NORMAL_MACRO(ins->std.volMacro,volMin,volMax,"vol",volumeLabel,160,ins->std.volMacro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_VOLUME],mmlString[0],volMin,volMax,NULL,false); + macroList.push_back(FurnaceGUIMacroDesc(volumeLabel,&ins->std.volMacro,volMin,volMax,160,uiColors[GUI_COLOR_MACRO_VOLUME])); } - NORMAL_MACRO(ins->std.arpMacro,arpMacroScroll,arpMacroScroll+24,"arp","Arpeggio",160,ins->std.arpMacro.open,false,NULL,true,&arpMacroScroll,(arpMode?-60:-120),120,0,0,true,1,macroAbsoluteMode,uiColors[GUI_COLOR_MACRO_PITCH],mmlString[1],-120,120,(ins->std.arpMacro.mode?(¯oHoverNote):NULL),true); + macroList.push_back(FurnaceGUIMacroDesc("Arpeggio",&ins->std.arpMacro,-120,120,160,uiColors[GUI_COLOR_MACRO_PITCH],false,macroAbsoluteMode,ins->std.arpMacro.mode?(¯oHoverNote):NULL)); if (dutyMax>0) { if (ins->type==DIV_INS_MIKEY) { - NORMAL_MACRO(ins->std.dutyMacro,0,dutyMax,"duty",dutyLabel,160,ins->std.dutyMacro.open,true,mikeyFeedbackBits,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[2],0,dutyMax,NULL,false); + macroList.push_back(FurnaceGUIMacroDesc(dutyLabel,&ins->std.dutyMacro,0,dutyMax,160,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true,mikeyFeedbackBits)); } else { - NORMAL_MACRO(ins->std.dutyMacro,dutyMin,dutyMax,"duty",dutyLabel,160,ins->std.dutyMacro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[2],dutyMin,dutyMax,NULL,false); + macroList.push_back(FurnaceGUIMacroDesc(dutyLabel,&ins->std.dutyMacro,dutyMin,dutyMax,160,uiColors[GUI_COLOR_MACRO_OTHER])); } } if (waveMax>0) { - NORMAL_MACRO(ins->std.waveMacro,0,waveMax,"wave",waveLabel,(bitMode && ins->type!=DIV_INS_PET)?64:160,ins->std.waveMacro.open,bitMode,waveNames,false,NULL,0,0,0,((ins->type==DIV_INS_AY || ins->type==DIV_INS_AY8930)?1:0),false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_WAVE],mmlString[3],0,waveMax,NULL,false); + macroList.push_back(FurnaceGUIMacroDesc(waveLabel,&ins->std.waveMacro,0,waveMax,(bitMode && ins->type!=DIV_INS_PET)?64:160,uiColors[GUI_COLOR_MACRO_WAVE],false,NULL,NULL,bitMode,waveNames,((ins->type==DIV_INS_AY || ins->type==DIV_INS_AY8930)?1:0))); } if (panMax>0) { if (panSingle) { - NORMAL_MACRO(ins->std.panLMacro,0,2,"panL","Panning",32,ins->std.panLMacro.open,true,panBits,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[13],0,panMax,NULL,false); + macroList.push_back(FurnaceGUIMacroDesc("Panning",&ins->std.panLMacro,0,2,32,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true,panBits)); } else { if (panSingleNoBit || (ins->type==DIV_INS_AMIGA && ins->std.panLMacro.mode)) { - NORMAL_MACRO(ins->std.panLMacro,panMin,panMax,"panL","Panning",(31+panMax-panMin),ins->std.panLMacro.open,false,NULL,false,NULL,0,0,0,0,(ins->type==DIV_INS_AMIGA),(ins->type==DIV_INS_AMIGA?1:0),macroQSoundMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[13],panMin,panMax,NULL,false); + macroList.push_back(FurnaceGUIMacroDesc("Panning",&ins->std.panLMacro,panMin,panMax,(31+panMax-panMin),uiColors[GUI_COLOR_MACRO_OTHER],false,(ins->type==DIV_INS_AMIGA)?macroQSoundMode:NULL)); } else { - NORMAL_MACRO(ins->std.panLMacro,panMin,panMax,"panL","Panning (left)",(31+panMax-panMin),ins->std.panLMacro.open,false,NULL,false,NULL,0,0,0,0,(ins->type==DIV_INS_AMIGA),(ins->type==DIV_INS_AMIGA?1:0),macroQSoundMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[13],panMin,panMax,NULL,false); + macroList.push_back(FurnaceGUIMacroDesc("Panning (left)",&ins->std.panLMacro,panMin,panMax,(31+panMax-panMin),uiColors[GUI_COLOR_MACRO_OTHER],false,(ins->type==DIV_INS_AMIGA)?macroQSoundMode:NULL)); } if (!panSingleNoBit) { if (ins->type==DIV_INS_AMIGA && ins->std.panLMacro.mode) { - NORMAL_MACRO(ins->std.panRMacro,0,1,"panR","Surround",32,ins->std.panRMacro.open,true,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[14],0,1,NULL,false); + macroList.push_back(FurnaceGUIMacroDesc("Surround",&ins->std.panRMacro,0,1,32,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true)); } else { - NORMAL_MACRO(ins->std.panRMacro,panMin,panMax,"panR","Panning (right)",(31+panMax-panMin),ins->std.panRMacro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[14],panMin,panMax,NULL,false); + macroList.push_back(FurnaceGUIMacroDesc("Panning (right)",&ins->std.panRMacro,panMin,panMax,(31+panMax-panMin),uiColors[GUI_COLOR_MACRO_OTHER])); } } } } - NORMAL_MACRO(ins->std.pitchMacro,pitchMacroScroll,pitchMacroScroll+160,"pitch","Pitch",160,ins->std.pitchMacro.open,false,NULL,true,&pitchMacroScroll,-2048,2047,0,0,true,1,macroRelativeMode,uiColors[GUI_COLOR_MACRO_PITCH],mmlString[15],-2048,2047,NULL,!ins->std.pitchMacro.mode); + macroList.push_back(FurnaceGUIMacroDesc("Pitch",&ins->std.pitchMacro,-2048,2047,160,uiColors[GUI_COLOR_MACRO_PITCH],true,macroRelativeMode)); if (ins->type==DIV_INS_FM || ins->type==DIV_INS_STD || ins->type==DIV_INS_OPL || @@ -3023,64 +2963,67 @@ void FurnaceGUI::drawInsEdit() { ins->type==DIV_INS_SWAN || ins->type==DIV_INS_MULTIPCM || ins->type==DIV_INS_SU) { - NORMAL_MACRO(ins->std.phaseResetMacro,0,1,"phaseReset","Phase Reset",32,ins->std.phaseResetMacro.open,true,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[16],0,1,NULL,false); + macroList.push_back(FurnaceGUIMacroDesc("Phase Reset",&ins->std.phaseResetMacro,0,1,32,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true)); } if (ex1Max>0) { if (ins->type==DIV_INS_C64) { - NORMAL_MACRO(ins->std.ex1Macro,0,ex1Max,"ex1","Filter Mode",64,ins->std.ex1Macro.open,true,filtModeBits,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[4],0,ex1Max,NULL,false); + macroList.push_back(FurnaceGUIMacroDesc("Filter Mode",&ins->std.ex1Macro,0,ex1Max,64,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true,filtModeBits)); } else if (ins->type==DIV_INS_SAA1099) { - NORMAL_MACRO(ins->std.ex1Macro,0,ex1Max,"ex1","Envelope",160,ins->std.ex1Macro.open,true,saaEnvBits,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[4],0,ex1Max,NULL,false); + macroList.push_back(FurnaceGUIMacroDesc("Envelope",&ins->std.ex1Macro,0,ex1Max,160,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true,saaEnvBits)); } else if (ins->type==DIV_INS_X1_010) { - NORMAL_MACRO(ins->std.ex1Macro,0,ex1Max,"ex1","Envelope Mode",160,ins->std.ex1Macro.open,true,x1_010EnvBits,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[4],0,ex1Max,NULL,false); + macroList.push_back(FurnaceGUIMacroDesc("Envelope Mode",&ins->std.ex1Macro,0,ex1Max,160,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true,x1_010EnvBits)); } else if (ins->type==DIV_INS_N163) { - NORMAL_MACRO(ins->std.ex1Macro,0,ex1Max,"ex1","Waveform len.",160,ins->std.ex1Macro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[4],0,ex1Max,NULL,false); + macroList.push_back(FurnaceGUIMacroDesc("Wave Length",&ins->std.ex1Macro,0,ex1Max,160,uiColors[GUI_COLOR_MACRO_OTHER])); } else if (ins->type==DIV_INS_FDS) { - NORMAL_MACRO(ins->std.ex1Macro,0,ex1Max,"ex1","Mod Depth",160,ins->std.ex1Macro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[4],0,ex1Max,NULL,false); + macroList.push_back(FurnaceGUIMacroDesc("Mod Depth",&ins->std.ex1Macro,0,ex1Max,160,uiColors[GUI_COLOR_MACRO_OTHER])); } else if (ins->type==DIV_INS_SU) { - NORMAL_MACRO(ins->std.ex1Macro,0,ex1Max,"ex1","Cutoff",160,ins->std.ex1Macro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[4],0,ex1Max,NULL,false); + macroList.push_back(FurnaceGUIMacroDesc("Cutoff",&ins->std.ex1Macro,0,ex1Max,160,uiColors[GUI_COLOR_MACRO_OTHER])); } else { - NORMAL_MACRO(ins->std.ex1Macro,0,ex1Max,"ex1","Duty",160,ins->std.ex1Macro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[4],0,ex1Max,NULL,false); + macroList.push_back(FurnaceGUIMacroDesc("Duty",&ins->std.ex1Macro,0,ex1Max,160,uiColors[GUI_COLOR_MACRO_OTHER])); } } if (ex2Max>0) { if (ins->type==DIV_INS_C64) { - NORMAL_MACRO(ins->std.ex2Macro,0,ex2Max,"ex2","Resonance",64,ins->std.ex2Macro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[5],0,ex2Max,NULL,false); + macroList.push_back(FurnaceGUIMacroDesc("Resonance",&ins->std.ex2Macro,0,ex2Max,64,uiColors[GUI_COLOR_MACRO_OTHER])); } else if (ins->type==DIV_INS_N163) { - NORMAL_MACRO(ins->std.ex2Macro,0,ex2Max,"ex2","Waveform update",64,ins->std.ex2Macro.open,true,n163UpdateBits,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[5],0,ex2Max,NULL,false); + macroList.push_back(FurnaceGUIMacroDesc("Wave Update",&ins->std.ex2Macro,0,ex2Max,64,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true,n163UpdateBits)); } else if (ins->type==DIV_INS_FDS) { - NORMAL_MACRO(ins->std.ex2Macro,0,ex2Max,"ex2","Mod Speed",160,ins->std.ex2Macro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[5],0,ex2Max,NULL,false); + macroList.push_back(FurnaceGUIMacroDesc("Mod Speed",&ins->std.ex2Macro,0,ex2Max,160,uiColors[GUI_COLOR_MACRO_OTHER])); } else if (ins->type==DIV_INS_SU) { - NORMAL_MACRO(ins->std.ex2Macro,0,ex2Max,"ex2","Resonance",160,ins->std.ex2Macro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[5],0,ex2Max,NULL,false); + macroList.push_back(FurnaceGUIMacroDesc("Resonance",&ins->std.ex2Macro,0,ex2Max,160,uiColors[GUI_COLOR_MACRO_OTHER])); } else { - NORMAL_MACRO(ins->std.ex2Macro,0,ex2Max,"ex2","Envelope",ex2Bit?64:160,ins->std.ex2Macro.open,ex2Bit,ayEnvBits,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[5],0,ex2Max,NULL,false); + macroList.push_back(FurnaceGUIMacroDesc("Envelope",&ins->std.ex2Macro,0,ex2Max,ex2Bit?64:160,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,ex2Bit,ayEnvBits)); } } if (ins->type==DIV_INS_C64) { - NORMAL_MACRO(ins->std.ex3Macro,0,2,"ex3","Special",32,ins->std.ex3Macro.open,true,c64SpecialBits,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[6],0,2,NULL,false); - NORMAL_MACRO(ins->std.ex4Macro,0,1,"ex4","Test/Gate",32,ins->std.ex4Macro.open,true,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[7],0,1,NULL,false); + macroList.push_back(FurnaceGUIMacroDesc("Special",&ins->std.ex3Macro,0,2,32,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true,c64SpecialBits)); + macroList.push_back(FurnaceGUIMacroDesc("Test/Gate",&ins->std.ex4Macro,0,1,32,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true)); } if (ins->type==DIV_INS_AY || ins->type==DIV_INS_AY8930 || ins->type==DIV_INS_X1_010) { - NORMAL_MACRO(ins->std.ex3Macro,0,15,"ex3","AutoEnv Num",96,ins->std.ex3Macro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[6],0,15,NULL,false); - NORMAL_MACRO(ins->std.algMacro,0,15,"alg","AutoEnv Den",96,ins->std.algMacro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[7],0,15,NULL,false); + macroList.push_back(FurnaceGUIMacroDesc("AutoEnv Num",&ins->std.ex3Macro,0,15,160,uiColors[GUI_COLOR_MACRO_OTHER])); + macroList.push_back(FurnaceGUIMacroDesc("AutoEnv Den",&ins->std.algMacro,0,15,160,uiColors[GUI_COLOR_MACRO_OTHER])); } if (ins->type==DIV_INS_AY8930) { // oh my i am running out of macros - NORMAL_MACRO(ins->std.fbMacro,0,8,"fb","Noise AND Mask",96,ins->std.fbMacro.open,true,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[8],0,8,NULL,false); - NORMAL_MACRO(ins->std.fmsMacro,0,8,"fms","Noise OR Mask",96,ins->std.fmsMacro.open,true,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[9],0,8,NULL,false); + macroList.push_back(FurnaceGUIMacroDesc("Noise AND Mask",&ins->std.fbMacro,0,8,96,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true)); + macroList.push_back(FurnaceGUIMacroDesc("Noise OR Mask",&ins->std.fmsMacro,0,8,96,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true)); } if (ins->type==DIV_INS_N163) { - NORMAL_MACRO(ins->std.ex3Macro,0,255,"ex3","Waveform to Load",160,ins->std.ex3Macro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[6],0,255,NULL,false); - NORMAL_MACRO(ins->std.algMacro,0,255,"alg","Wave pos. to Load",160,ins->std.algMacro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[7],0,255,NULL,false); - NORMAL_MACRO(ins->std.fbMacro,0,252,"fb","Wave len. to Load",160,ins->std.fbMacro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[8],0,252,NULL,false); - NORMAL_MACRO(ins->std.fmsMacro,0,2,"fms","Waveform load",64,ins->std.fmsMacro.open,true,n163UpdateBits,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[9],0,2,NULL,false); + macroList.push_back(FurnaceGUIMacroDesc("WaveLoad Wave",&ins->std.ex3Macro,0,255,160,uiColors[GUI_COLOR_MACRO_OTHER])); + macroList.push_back(FurnaceGUIMacroDesc("WaveLoad Pos",&ins->std.algMacro,0,255,160,uiColors[GUI_COLOR_MACRO_OTHER])); + macroList.push_back(FurnaceGUIMacroDesc("WaveLoad Len",&ins->std.fbMacro,0,252,160,uiColors[GUI_COLOR_MACRO_OTHER])); + macroList.push_back(FurnaceGUIMacroDesc("WaveLoad Trigger",&ins->std.fmsMacro,0,2,160,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true,n163UpdateBits)); } if (ins->type==DIV_INS_FDS) { - NORMAL_MACRO(ins->std.ex3Macro,0,127,"ex3","Mod Position",160,ins->std.ex3Macro.open,false,NULL,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[6],0,2,NULL,false); + macroList.push_back(FurnaceGUIMacroDesc("Mod Position",&ins->std.ex3Macro,0,127,160,uiColors[GUI_COLOR_MACRO_OTHER])); } if (ins->type==DIV_INS_SU) { - NORMAL_MACRO(ins->std.ex3Macro,0,4,"ex3","Control",64,ins->std.ex3Macro.open,true,suControlBits,false,NULL,0,0,0,0,false,0,macroDummyMode,uiColors[GUI_COLOR_MACRO_OTHER],mmlString[6],0,4,NULL,false); + macroList.push_back(FurnaceGUIMacroDesc("Control",&ins->std.ex3Macro,0,4,64,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true,suControlBits)); } + MACRO_BEGIN(28*dpiScale); + for (FurnaceGUIMacroDesc& i: macroList) { + } MACRO_END; ImGui::EndTabItem(); } From b2953e00fe4b14ab35853d7b0c29df88bb8024e0 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 6 May 2022 23:52:14 -0500 Subject: [PATCH 341/342] OPL: fix 4-op muting (I think) fixes #435 --- src/engine/platform/opl.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/engine/platform/opl.cpp b/src/engine/platform/opl.cpp index 50e60247f..e43895289 100644 --- a/src/engine/platform/opl.cpp +++ b/src/engine/platform/opl.cpp @@ -564,7 +564,7 @@ int DivPlatformOPL::toFreq(int freq) { void DivPlatformOPL::muteChannel(int ch, bool mute) { isMuted[ch]=mute; - if (ch Date: Fri, 6 May 2022 23:52:26 -0500 Subject: [PATCH 342/342] GUI: is the macro UI back yet --- src/gui/gui.h | 30 +++++ src/gui/insEdit.cpp | 319 ++++++++++++++++++++------------------------ 2 files changed, 178 insertions(+), 171 deletions(-) diff --git a/src/gui/gui.h b/src/gui/gui.h index cfbf90e3f..bb045d257 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -723,6 +723,34 @@ struct FurnaceGUISysCategory { name(NULL) {} }; +struct FurnaceGUIMacroDesc { + DivInstrumentMacro* macro; + int min, max; + float height; + const char* displayName; + const char** bitfieldBits; + const char* modeName; + ImVec4 color; + unsigned int bitOffset; + bool isBitfield, blockMode; + String (*hoverFunc)(int,float); + + FurnaceGUIMacroDesc(const char* name, DivInstrumentMacro* m, int macroMin, int macroMax, float macroHeight, ImVec4 col=ImVec4(1.0f,1.0f,1.0f,1.0f), bool block=false, const char* mName=NULL, String (*hf)(int,float)=NULL, bool bitfield=false, const char** bfVal=NULL, unsigned int bitOff=0): + macro(m), + min(macroMin), + max(macroMax), + height(macroHeight), + displayName(name), + bitfieldBits(bfVal), + modeName(mName), + color(col), + bitOffset(bitOff), + isBitfield(bitfield), + blockMode(block), + hoverFunc(hf) { + } +}; + class FurnaceGUI { DivEngine* e; @@ -1161,6 +1189,8 @@ class FurnaceGUI { void patternRow(int i, bool isPlaying, float lineHeight, int chans, int ord, const DivPattern** patCache, bool inhibitSel); + void drawMacros(std::vector& macros); + void actualWaveList(); void actualSampleList(); diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index 48919dafd..47de22e9c 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -1067,164 +1067,153 @@ void FurnaceGUI::drawGBEnv(unsigned char vol, unsigned char len, unsigned char s #define PARAMETER MARK_MODIFIED; e->notifyInsChange(curIns); -struct FurnaceGUIMacroDesc { - DivInstrumentMacro* macro; - int min, max; - float height; - const char* displayName; - const char** bitfieldBits; - const char* modeName; - ImVec4 color; - unsigned int bitOffset; - bool isBitfield, blockMode; - String (*hoverFunc)(int,float); +void FurnaceGUI::drawMacros(std::vector& macros) { + float asFloat[256]; + int asInt[256]; + float loopIndicator[256]; + int index=0; - FurnaceGUIMacroDesc(const char* name, DivInstrumentMacro* m, int macroMin, int macroMax, float macroHeight, ImVec4 col=ImVec4(1.0f,1.0f,1.0f,1.0f), bool block=false, const char* mName=NULL, String (*hf)(int,float)=NULL, bool bitfield=false, const char** bfVal=NULL, unsigned int bitOff=0): - macro(m), - min(macroMin), - max(macroMax), - height(macroHeight), - displayName(name), - bitfieldBits(bfVal), - modeName(mName), - color(col), - bitOffset(bitOff), - isBitfield(bitfield), - blockMode(block), - hoverFunc(hf) { + float reservedSpace=ImGui::GetContentRegionAvail().x-28.0f*dpiScale; + + if (ImGui::BeginTable("MacroSpace",2)) { + ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthFixed,0.0); + ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthStretch,0.0); + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + float lenAvail=ImGui::GetContentRegionAvail().x; + ImGui::Dummy(ImVec2(120.0f*dpiScale,dpiScale)); + ImGui::TableNextColumn(); + float availableWidth=ImGui::GetContentRegionAvail().x-reservedSpace; + int totalFit=MIN(128,availableWidth/MAX(1,macroPointSize*dpiScale)); + if (macroDragScroll>128-totalFit) { + macroDragScroll=128-totalFit; + } + ImGui::SetNextItemWidth(availableWidth); + if (CWSliderInt("##MacroScroll",¯oDragScroll,0,128-totalFit,"")) { + if (macroDragScroll<0) macroDragScroll=0; + if (macroDragScroll>128-totalFit) macroDragScroll=128-totalFit; + } + + // draw macros + for (FurnaceGUIMacroDesc& i: macros) { + ImGui::PushID(index); + ImGui::TableNextRow(); + + // description + ImGui::TableNextColumn(); + ImGui::Text("%s",i.displayName); + ImGui::SameLine(); + if (ImGui::SmallButton(i.macro->open?(ICON_FA_CHEVRON_UP "##IMacroOpen"):(ICON_FA_CHEVRON_DOWN "##IMacroOpen"))) { + i.macro->open=!i.macro->open; + } + if (i.macro->open) { + ImGui::SetNextItemWidth(lenAvail); + if (ImGui::InputScalar("##IMacroLen",ImGuiDataType_U8,&i.macro->len,&_ONE,&_THREE)) { MARK_MODIFIED + if (i.macro->len>128) i.macro->len=128; + } + if (i.modeName!=NULL) { + bool modeVal=i.macro->mode; + String modeName=fmt::sprintf("%s##IMacroMode",i.modeName); + if (ImGui::Checkbox(modeName.c_str(),&modeVal)) { + i.macro->mode=modeVal; + } + } + } + + // macro area + ImGui::TableNextColumn(); + for (int j=0; j<256; j++) { + if (j+macroDragScroll>=i.macro->len) { + asFloat[j]=0; + asInt[j]=0; + } else { + asFloat[j]=i.macro->val[j+macroDragScroll]; + asInt[j]=i.macro->val[j+macroDragScroll]+i.bitOffset; + } + if (j+macroDragScroll>=i.macro->len || (j+macroDragScroll>i.macro->rel && i.macro->looprel)) { + loopIndicator[j]=0; + } else { + loopIndicator[j]=((i.macro->loop!=-1 && (j+macroDragScroll)>=i.macro->loop))|((i.macro->rel!=-1 && (j+macroDragScroll)==i.macro->rel)<<1); + } + } + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding,ImVec2(0.0f,0.0f)); + + if (i.isBitfield) { + PlotBitfield("##IMacro",asInt,totalFit,0,i.bitfieldBits,i.max,ImVec2(availableWidth,(i.macro->open)?(i.height*dpiScale):(32.0f*dpiScale))); + } else { + PlotCustom("##IMacro",asFloat,totalFit,macroDragScroll,NULL,i.min,i.max,ImVec2(availableWidth,(i.macro->open)?(i.height*dpiScale):(32.0f*dpiScale)),sizeof(float),i.color,i.macro->len-macroDragScroll,i.hoverFunc,i.blockMode); + } + if (i.macro->open && (ImGui::IsItemClicked(ImGuiMouseButton_Left) || ImGui::IsItemClicked(ImGuiMouseButton_Right))) { + macroDragStart=ImGui::GetItemRectMin(); + macroDragAreaSize=ImVec2(availableWidth,i.height*dpiScale); + macroDragMin=i.min; + macroDragMax=i.max; + macroDragBitOff=i.bitOffset; + macroDragBitMode=i.isBitfield; + macroDragInitialValueSet=false; + macroDragInitialValue=false; + macroDragLen=totalFit; + macroDragActive=true; + macroDragTarget=i.macro->val; + macroDragChar=false; + macroDragLineMode=(i.isBitfield)?false:ImGui::IsItemClicked(ImGuiMouseButton_Right); + macroDragLineInitial=ImVec2(0,0); + processDrags(ImGui::GetMousePos().x,ImGui::GetMousePos().y); + } + if (i.macro->open) { + if (ImGui::IsItemHovered() && ctrlWheeling) { + macroPointSize+=wheelY; + if (macroPointSize<1) macroPointSize=1; + if (macroPointSize>256) macroPointSize=256; + } + /*if (drawSlider) { + ImGui::SameLine(); + CWVSliderInt("##IMacroPos",ImVec2(20.0f*dpiScale,i.height*dpiScale),sliderVal,sliderLow,sliderHigh); + }*/ + PlotCustom("##IMacroLoop",loopIndicator,totalFit,macroDragScroll,NULL,0,2,ImVec2(availableWidth,12.0f*dpiScale),sizeof(float),i.color,i.macro->len-macroDragScroll,¯oHoverLoop); + if (ImGui::IsItemClicked(ImGuiMouseButton_Left)) { + macroLoopDragStart=ImGui::GetItemRectMin(); + macroLoopDragAreaSize=ImVec2(availableWidth,12.0f*dpiScale); + macroLoopDragLen=totalFit; + if (ImGui::IsKeyDown(ImGuiKey_LeftShift) || ImGui::IsKeyDown(ImGuiKey_RightShift)) { + macroLoopDragTarget=&i.macro->rel; + } else { + macroLoopDragTarget=&i.macro->loop; + } + macroLoopDragActive=true; + processDrags(ImGui::GetMousePos().x,ImGui::GetMousePos().y); + } + if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) { + if (ImGui::IsKeyDown(ImGuiKey_LeftShift) || ImGui::IsKeyDown(ImGuiKey_RightShift)) { + i.macro->rel=-1; + } else { + i.macro->loop=-1; + } + } + ImGui::SetNextItemWidth(availableWidth); + String& mmlStr=mmlString[index]; + if (ImGui::InputText("##IMacroMML",&mmlStr)) { + decodeMMLStr(mmlStr,i.macro->val,i.macro->len,i.macro->loop,i.min,(i.isBitfield)?((1<<(i.isBitfield?i.max:0))-1):i.max,i.macro->rel); + } + if (!ImGui::IsItemActive()) { + encodeMMLStr(mmlStr,i.macro->val,i.macro->len,i.macro->loop,i.macro->rel); + } + } + ImGui::PopStyleVar(); + ImGui::PopID(); + index++; + } + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::TableNextColumn(); + ImGui::SetNextItemWidth(availableWidth); + if (CWSliderInt("##MacroScroll",¯oDragScroll,0,128-totalFit,"")) { + if (macroDragScroll<0) macroDragScroll=0; + if (macroDragScroll>128-totalFit) macroDragScroll=128-totalFit; + } + ImGui::EndTable(); } -}; - -#define NORMAL_MACRO(macro,macroMin,macroHeight,macroName,displayName,displayHeight,displayLoop,bitfield,bfVal,drawSlider,sliderVal,sliderLow,sliderHigh,macroDispMin,bitOff,macroMode,macroModeMax,displayModeName,macroColor,mmlStr,macroAMin,macroAMax,hoverFunc,blockMode) \ - ImGui::TableNextRow(); \ - ImGui::TableNextColumn(); \ - ImGui::Text("%s",displayName); \ - ImGui::SameLine(); \ - if (ImGui::SmallButton(displayLoop?(ICON_FA_CHEVRON_UP "##IMacroOpen_" macroName):(ICON_FA_CHEVRON_DOWN "##IMacroOpen_" macroName))) { \ - displayLoop=!displayLoop; \ - } \ - if (displayLoop) { \ - ImGui::SetNextItemWidth(lenAvail); \ - if (ImGui::InputScalar("##IMacroLen_" macroName,ImGuiDataType_U8,¯o.len,&_ONE,&_THREE)) { MARK_MODIFIED \ - if (macro.len>128) macro.len=128; \ - } \ - if (macroMode) { \ - bool modeVal=macro.mode; \ - String modeName=fmt::sprintf("%s##IMacroMode_" macroName,displayModeName); \ - if (ImGui::Checkbox(modeName.c_str(),&modeVal)) { \ - macro.mode=modeVal; \ - } \ - } \ - } \ - ImGui::TableNextColumn(); \ - for (int j=0; j<256; j++) { \ - if (j+macroDragScroll>=macro.len) { \ - asFloat[j]=0; \ - asInt[j]=0; \ - } else { \ - asFloat[j]=macro.val[j+macroDragScroll]+macroDispMin; \ - asInt[j]=macro.val[j+macroDragScroll]+macroDispMin+bitOff; \ - } \ - if (j+macroDragScroll>=macro.len || (j+macroDragScroll>macro.rel && macro.loop=macro.loop))|((macro.rel!=-1 && (j+macroDragScroll)==macro.rel)<<1); \ - } \ - } \ - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding,ImVec2(0.0f,0.0f)); \ - \ - if (bitfield) { \ - PlotBitfield("##IMacro_" macroName,asInt,totalFit,0,bfVal,macroHeight,ImVec2(availableWidth,(displayLoop)?(displayHeight*dpiScale):(32.0f*dpiScale))); \ - } else { \ - PlotCustom("##IMacro_" macroName,asFloat,totalFit,macroDragScroll,NULL,macroDispMin+macroMin,macroHeight+macroDispMin,ImVec2(availableWidth,(displayLoop)?(displayHeight*dpiScale):(32.0f*dpiScale)),sizeof(float),macroColor,macro.len-macroDragScroll,hoverFunc,blockMode); \ - } \ - if (displayLoop && (ImGui::IsItemClicked(ImGuiMouseButton_Left) || ImGui::IsItemClicked(ImGuiMouseButton_Right))) { \ - macroDragStart=ImGui::GetItemRectMin(); \ - macroDragAreaSize=ImVec2(availableWidth,displayHeight*dpiScale); \ - macroDragMin=macroMin; \ - macroDragMax=macroHeight; \ - macroDragBitOff=bitOff; \ - macroDragBitMode=bitfield; \ - macroDragInitialValueSet=false; \ - macroDragInitialValue=false; \ - macroDragLen=totalFit; \ - macroDragActive=true; \ - macroDragTarget=macro.val; \ - macroDragChar=false; \ - macroDragLineMode=(bitfield)?false:ImGui::IsItemClicked(ImGuiMouseButton_Right); \ - macroDragLineInitial=ImVec2(0,0); \ - processDrags(ImGui::GetMousePos().x,ImGui::GetMousePos().y); \ - } \ - if (displayLoop) { \ - if (ImGui::IsItemHovered() && ctrlWheeling) { \ - macroPointSize+=wheelY; \ - if (macroPointSize<1) macroPointSize=1; \ - if (macroPointSize>256) macroPointSize=256; \ - } \ - if (drawSlider) { \ - ImGui::SameLine(); \ - CWVSliderInt("##IMacroPos_" macroName,ImVec2(20.0f*dpiScale,displayHeight*dpiScale),sliderVal,sliderLow,sliderHigh); \ - } \ - PlotCustom("##IMacroLoop_" macroName,loopIndicator,totalFit,macroDragScroll,NULL,0,2,ImVec2(availableWidth,12.0f*dpiScale),sizeof(float),macroColor,macro.len-macroDragScroll,¯oHoverLoop); \ - if (ImGui::IsItemClicked(ImGuiMouseButton_Left)) { \ - macroLoopDragStart=ImGui::GetItemRectMin(); \ - macroLoopDragAreaSize=ImVec2(availableWidth,12.0f*dpiScale); \ - macroLoopDragLen=totalFit; \ - if (ImGui::IsKeyDown(ImGuiKey_LeftShift) || ImGui::IsKeyDown(ImGuiKey_RightShift)) { \ - macroLoopDragTarget=¯o.rel; \ - } else { \ - macroLoopDragTarget=¯o.loop; \ - } \ - macroLoopDragActive=true; \ - processDrags(ImGui::GetMousePos().x,ImGui::GetMousePos().y); \ - } \ - if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) { \ - if (ImGui::IsKeyDown(ImGuiKey_LeftShift) || ImGui::IsKeyDown(ImGuiKey_RightShift)) { \ - macro.rel=-1; \ - } else { \ - macro.loop=-1; \ - } \ - } \ - ImGui::SetNextItemWidth(availableWidth); \ - if (ImGui::InputText("##IMacroMML_" macroName,&mmlStr)) { \ - decodeMMLStr(mmlStr,macro.val,macro.len,macro.loop,macroAMin,(bitfield)?((1<<(bitfield?macroAMax:0))-1):macroAMax,macro.rel); \ - } \ - if (!ImGui::IsItemActive()) { \ - encodeMMLStr(mmlStr,macro.val,macro.len,macro.loop,macro.rel); \ - } \ - } \ - ImGui::PopStyleVar(); - -#define MACRO_BEGIN(reservedSpace) \ -if (ImGui::BeginTable("MacroSpace",2)) { \ - ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthFixed,0.0); \ - ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthStretch,0.0); \ - ImGui::TableNextRow(); \ - ImGui::TableNextColumn(); \ - float lenAvail=ImGui::GetContentRegionAvail().x; \ - ImGui::Dummy(ImVec2(120.0f*dpiScale,dpiScale)); \ - ImGui::TableNextColumn(); \ - float availableWidth=ImGui::GetContentRegionAvail().x-reservedSpace; \ - int totalFit=MIN(128,availableWidth/MAX(1,macroPointSize*dpiScale)); \ - if (macroDragScroll>128-totalFit) { \ - macroDragScroll=128-totalFit; \ - } \ - ImGui::SetNextItemWidth(availableWidth); \ - if (CWSliderInt("##MacroScroll",¯oDragScroll,0,128-totalFit,"")) { \ - if (macroDragScroll<0) macroDragScroll=0; \ - if (macroDragScroll>128-totalFit) macroDragScroll=128-totalFit; \ - } - -#define MACRO_END \ - ImGui::TableNextRow(); \ - ImGui::TableNextColumn(); \ - ImGui::TableNextColumn(); \ - ImGui::SetNextItemWidth(availableWidth); \ - if (CWSliderInt("##MacroScroll",¯oDragScroll,0,128-totalFit,"")) { \ - if (macroDragScroll<0) macroDragScroll=0; \ - if (macroDragScroll>128-totalFit) macroDragScroll=128-totalFit; \ - } \ - ImGui::EndTable(); \ } #define DRUM_FREQ(name,db,df,prop) \ @@ -1352,9 +1341,6 @@ void FurnaceGUI::drawInsEdit() { std::vector macroList; if (ins->type==DIV_INS_FM || ins->type==DIV_INS_OPL || ins->type==DIV_INS_OPLL || ins->type==DIV_INS_OPZ) { char label[32]; - float asFloat[256]; - int asInt[256]; - float loopIndicator[256]; int opCount=4; if (ins->type==DIV_INS_OPLL) opCount=2; if (ins->type==DIV_INS_OPL) opCount=(ins->fm.ops==4)?4:2; @@ -2142,7 +2128,6 @@ void FurnaceGUI::drawInsEdit() { ImGui::EndTabItem(); } if (ImGui::BeginTabItem("FM Macros")) { - MACRO_BEGIN(0); if (ins->type==DIV_INS_OPLL) { macroList.push_back(FurnaceGUIMacroDesc(FM_NAME(FM_SUS),&ins->std.algMacro,0,1,32,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true)); macroList.push_back(FurnaceGUIMacroDesc(FM_NAME(FM_FB),&ins->std.fbMacro,0,7,96,uiColors[GUI_COLOR_MACRO_OTHER])); @@ -2170,14 +2155,13 @@ void FurnaceGUI::drawInsEdit() { macroList.push_back(FurnaceGUIMacroDesc("LFO Shape",&ins->std.waveMacro,0,3,48,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,macroLFOWaves)); macroList.push_back(FurnaceGUIMacroDesc("OpMask",&ins->std.ex4Macro,0,4,128,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true,fmOperatorBits)); } - MACRO_END; + drawMacros(macroList); ImGui::EndTabItem(); } for (int i=0; itype==DIV_INS_OPLL) { @@ -2236,7 +2220,7 @@ void FurnaceGUI::drawInsEdit() { macroList.push_back(FurnaceGUIMacroDesc(FM_NAME(FM_SSG),&ins->std.opMacros[ordi].ssgMacro,0,4,64,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true,ssgEnvBits)); } } - MACRO_END; + drawMacros(macroList); ImGui::PopID(); ImGui::EndTabItem(); } @@ -2735,10 +2719,6 @@ void FurnaceGUI::drawInsEdit() { } } if (ImGui::BeginTabItem("Macros")) { - // TODO: rewrite all of this - float asFloat[256]; - int asInt[256]; - float loopIndicator[256]; const char* volumeLabel="Volume"; int volMax=15; @@ -3021,10 +3001,7 @@ void FurnaceGUI::drawInsEdit() { macroList.push_back(FurnaceGUIMacroDesc("Control",&ins->std.ex3Macro,0,4,64,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true,suControlBits)); } - MACRO_BEGIN(28*dpiScale); - for (FurnaceGUIMacroDesc& i: macroList) { - } - MACRO_END; + drawMacros(macroList); ImGui::EndTabItem(); } ImGui::EndTabBar();