From 559a99c5fc44282ec8f08cf9b64055e4ddf6d1da Mon Sep 17 00:00:00 2001 From: BlastBrothers Date: Sat, 16 Apr 2022 20:47:40 -0400 Subject: [PATCH 01/16] 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 b6613e9d..e44fdcc5 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 2e23f2f7..9dc04c77 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 02/16] 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 a9efe512..040e8820 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 4bb77ea3cb49085ec7c3c83b3f7f7bfa1d78ce2f Mon Sep 17 00:00:00 2001 From: BlastBrothers Date: Sun, 17 Apr 2022 11:38:28 -0400 Subject: [PATCH 03/16] 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 040e8820..5c812283 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 9eaf600b4b83467ad2f08c77c54044d2d53bbe24 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 27 Apr 2022 17:44:00 -0500 Subject: [PATCH 04/16] 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 da01cf0e..35786838 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 c0be1f45..13629816 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 05/16] 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 185f0363..a62ae36e 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 3996f2d0..16678c49 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 8241255b..4f4663ca 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 fd0b0d07..4d1070f3 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 c2c20d3d..13cc495b 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 53652ec7..1fdf64d7 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 c12df2fe..28f6cf88 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 e5874f91..8832b7fb 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 3b45cd27..9ba22ef5 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 06/16] 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 f98b1994..7acab357 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 13629816..7039f164 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 07/16] 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 7acab357..2dca4b3c 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 5baf13f0..6291d453 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 7039f164..8631e74f 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 08/16] 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 4c200e02..88e3833c 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 09/16] 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 f855648b..cebd39e2 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 10/16] 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 fdfdf86f..5469ac41 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 11/16] 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 5469ac41..5ef9a564 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 12/16] 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 69b02a78..a9e6d243 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 13/16] 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 a62ae36e..3177432c 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 4d12d3bb..b36ee140 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 14/16] 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 bbf5db2d..082eb083 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 15/16] 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 b36ee140..bab5dcd0 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 d5061e0b..f9a39724 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 0ca78004..d660c579 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 79cfb42b..d893283f 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 8df82051..bc3cbf98 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 8631e74f..10592188 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 16/16] 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 8d977e84..feafbe60 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 3177432c..b2c0257f 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 bab5dcd0..7aa94fc5 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 16678c49..cfd23e1f 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 1212b15d..ac76e193 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 4ba38483..fc2a36ab 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 640b495b..03e99961 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 d893283f..3515f046 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 2c351213..845e7247 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 b9668d58..ded7b493 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 72dc9c0f..80c51d95 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 59b77587..d77857a6 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 4c688103..f70198b6 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 cf1ea36d..dc8c1506 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 a00d4d05..34515305 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 cebd39e2..c5412787 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 fac157da..1e727fa6 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 a02bc8de..c2e1342b 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 016b3b3c..5178cad3 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 8a00c563..1a4b2ee0 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 0e83e9d2..eb954a6e 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 b5d194ff..fa31caa8 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 f21c7d5b..2d4986b5 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 c32548b5..faf5ba7d 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 2dca4b3c..1f47a162 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 86ad9b10..96752bd5 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 63415a0c..7d4ea7d5 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 aa787672..efeb0a20 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 56e95948..29fadd4c 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 f1498709..46db6562 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 04338d26..0af0840a 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 a250e04c..b0e530db 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 317ffa9b..087d8c7b 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 ccaeeb3f..52f46502 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)) {