From ad88ce46ab2a17a93ba3a990dbcab04cc7e02870 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 21 Jul 2023 19:54:35 -0500 Subject: [PATCH] dev164 - Namco 163: improvements, part 4 - per-channel wave offset/length - wave load position/length effects - global wave load effects that actually make sense - update doc --- doc/4-instrument/n163.md | 26 ++++------ doc/7-systems/n163.md | 34 +++++-------- papers/format.md | 97 +----------------------------------- papers/newIns.md | 6 +++ src/engine/dispatch.h | 10 ++-- src/engine/engine.h | 4 +- src/engine/instrument.cpp | 42 +++++++++++++++- src/engine/instrument.h | 11 +++- src/engine/platform/n163.cpp | 24 +++++++-- src/engine/sysDef.cpp | 13 ++--- src/gui/insEdit.cpp | 64 +++++++++++++++++++++--- 11 files changed, 172 insertions(+), 159 deletions(-) diff --git a/doc/4-instrument/n163.md b/doc/4-instrument/n163.md index 1ec45914..2e37b0d5 100644 --- a/doc/4-instrument/n163.md +++ b/doc/4-instrument/n163.md @@ -4,27 +4,23 @@ The Namco 163 instrument editor consists of two tabs: "Namco 163" for control of ## Namco 163 -- **Waveform**: Determines the initial waveform for playing. -- **Offset**: Determines the initial waveform position will be load to RAM. -- **Length**: Determines the initial waveform length will be load to RAM. -- **Load waveform before playback**: Determines the load initial waveform into RAM before playback. -- **Update waveforms into RAM when every waveform changes**: Determines the update every different waveform changes in playback. - +- **Load waveform**: if enabled, a waveform will be loaded when this instrument plays. + - if it isn't then only the offset/length change. +- **Waveform**: determines the waveform that will be loaded. + - only appears when Load waveform is enabled. +- **Per-channel wave offset/length**: when enabled, the offset/length settings are split per channel. +- **Offset**: determines the waveform position in RAM. +- **Length**: determines the waveform length in RAM. ## Macros - **Volume**: volume levels sequence - **Arpeggio**: pitch sequence +- **Wave Pos**: sets waveform seek position in RAM - **Waveform**: sets waveform source for playback immediately or update later - **Panning**: output for left and right channels - **Pitch**: fine pitch -- **Phase Reset**: trigger restart of waveform +- **Wave Length**: sets waveform length \ No newline at end of file +- **Phase Reset**: trigger restart of waveform +--> diff --git a/doc/7-systems/n163.md b/doc/7-systems/n163.md index cb5ff530..2fcec7e6 100644 --- a/doc/7-systems/n163.md +++ b/doc/7-systems/n163.md @@ -18,24 +18,16 @@ global commands don't care about the channel columns for work commands and its l # effects - `10xx`: **set waveform for playback.** -- `11xx`: **set waveform position in RAM for playback.** single nibble unit. -- `12xx`: **set waveform length in RAM for playback.** `04` to `FC`, 4 nibble unit. -- `130x`: **set playback waveform update behavior.** - - `0`: off. - - bit 0: update now. - - bit 1: update when every waveform is changed. -- `14xx`: **set waveform for load to RAM.** -- `15xx`: **set waveform position for load to RAM.** single nibble unit. -- `16xx`: **set waveform length for load to RAM.** `04` to `FC`, 4 nibble unit. -- `170x`: **set waveform load behavior.** - - `0`: off. - - bit 0: load now. - - bit 1: load when every waveform is changed. -- `180x`: **set channel limit.** range is `0` to `7`; 1 is added to get results of 1 through 8. -- `20xx`: **globally set waveform for load to RAM.** -- `21xx`: **globally set waveform position for load to RAM.** single nibble unit. -- `22xx`: **globally set waveform length for load to RAM.** `04` to `FC`, 4 nibble unit. -- `230x`: **globally set waveform load behavior.** - - `0`: off. - - bit 0: load now. - - bit 1: load when every waveform is changed. +- `11xx`: **set waveform position in RAM for playback.** +- `12xx`: **set waveform length in RAM for playback.** + - `x` goes from `04` to `FC` in steps of 4. +- `15xx`: **set waveform load position.** +- `16xx`: **set waveform load length.** + - `x` goes from `04` to `FC` in steps of 4. +- `180x`: **set channel limit.** + - range of `x` is `0` to `7`. 1 is added to get results of 1 through 8. +- `20xx`: **load a waveform to RAM.** + - `x` is the waveform. + - the length is determined by the wave's width (it will be snapped to a multiple of 4 if it isn't). + - make sure to use `21xx` first! +- `21xx`: **set position for 20xx.** diff --git a/papers/format.md b/papers/format.md index 78d62d48..499efe48 100644 --- a/papers/format.md +++ b/papers/format.md @@ -34,110 +34,15 @@ the format versions are: - 162: Furnace 0.6pre7 - 161: Furnace 0.6pre6 -- 160: Furnace dev160 -- 159: Furnace dev159 - 158: Furnace 0.6pre5 -- 157: Furnace dev157 -- 156: Furnace dev156 -- 155: Furnace dev155 -- 154: Furnace dev154 -- 153: Furnace dev153 -- 152: Furnace dev152 -- 151: Furnace dev151 -- 150: Furnace dev150 -- 149: Furnace dev149 -- 148: Furnace dev148 -- 147: Furnace dev147 - 146: Furnace Pro (joke version) -- 145: Furnace dev145 -- 144: Furnace dev144 - 143: Furnace 0.6pre4 -- 142: Furnace dev142 - 141: Furnace Tournament Edition (for intro tune contest) -- 140: Furnace dev140 -- 139: Furnace dev139 -- 138: Furnace dev138 -- 137: Furnace dev137 -- 136: Furnace dev136 -- 135: Furnace dev135 -- 134: Furnace dev134 - 133: Furnace 0.6pre3 - 132: Furnace 0.6pre2 -- 131: Furnace dev131 -- 130: Furnace dev130 -- 129: Furnace dev129 -- 128: Furnace dev128 -- 127: Furnace dev127 -- 126: Furnace dev126 -- 125: Furnace dev125 -- 124: Furnace dev124 -- 123: Furnace dev123 -- 122: Furnace dev122 -- 121: Furnace dev121 -- 120: Furnace dev120 -- 119: Furnace dev119 -- 118: Furnace dev118 -- 117: Furnace dev117 - 116: Furnace 0.6pre1.5 -- 115: Furnace dev115 -- 114: Furnace dev114 -- 113: Furnace dev113 -- 112: Furnace dev112 -- 111: Furnace dev111 -- 110: Furnace dev110 -- 109: Furnace dev109 -- 108: Furnace dev108 -- 107: Furnace dev107 -- 106: Furnace dev106 -- 105: Furnace dev105 -- 104: Furnace dev104 -- 103: Furnace dev103 -- 102: Furnace 0.6pre1 (dev102) -- 101: Furnace 0.6pre1 (dev101) - 100: Furnace 0.6pre1 -- 99: Furnace dev99 -- 98: Furnace dev98 -- 97: Furnace dev97 -- 96: Furnace dev96 -- 95: Furnace dev95 -- 94: Furnace dev94 -- 93: Furnace dev93 -- 92: Furnace dev92 -- 91: Furnace dev91 -- 90: Furnace dev90 -- 89: Furnace dev89 -- 88: Furnace dev88 -- 87: Furnace dev87 -- 86: Furnace dev86 -- 85: Furnace dev85 -- 84: Furnace dev84 -- 83: Furnace dev83 -- 82: Furnace dev82 -- 81: Furnace dev81 -- 80: Furnace dev80 -- 79: Furnace dev79 -- 78: Furnace dev78 -- 77: Furnace dev77 -- 76: Furnace dev76 - 75: Furnace dev75/April Fools' 0.6pre0 -- 74: Furnace dev74 -- 73: Furnace dev73 -- 72: Furnace dev72 -- 71: Furnace dev71 -- 70: Furnace dev70 -- 69: Furnace dev69 -- 68: Furnace dev68 -- 67: Furnace dev67 -- 66: Furnace dev66 -- 65: Furnace dev65 -- 64: Furnace dev64 -- 63: Furnace dev63 -- 62: Furnace dev62 -- 61: Furnace dev61 -- 60: Furnace dev60 -- 59: Furnace dev59 -- 58: Furnace dev58 -- 57: Furnace dev57 - 54: Furnace 0.5.8 - 53: Furnace 0.5.7 @@ -176,6 +81,8 @@ the format versions are: - 13: Furnace 0.2.1 - 12: Furnace 0.2 +versions that do not appear in this list are `dev???` ones. + # header the header is 32 bytes long. diff --git a/papers/newIns.md b/papers/newIns.md index 84369a6c..bd4daaec 100644 --- a/papers/newIns.md +++ b/papers/newIns.md @@ -468,6 +468,12 @@ size | description 1 | wave pos 1 | wave len 1 | wave mode + | **extra info** (>=164) + 1 | enable per channel wave pos/len + 8 | per channel wave pos + | - only read if enabled. + 8 | per channel wave len + | - only read if enabled. ``` # FDS/Virtual Boy data (FD) diff --git a/src/engine/dispatch.h b/src/engine/dispatch.h index 767db912..acd0cc77 100644 --- a/src/engine/dispatch.h +++ b/src/engine/dispatch.h @@ -173,16 +173,16 @@ enum DivDispatchCmds { DIV_CMD_N163_WAVE_POSITION, DIV_CMD_N163_WAVE_LENGTH, - DIV_CMD_N163_WAVE_MODE, - DIV_CMD_N163_WAVE_LOAD, + DIV_CMD_N163_WAVE_UNUSED1, + DIV_CMD_N163_WAVE_UNUSED2, DIV_CMD_N163_WAVE_LOADPOS, DIV_CMD_N163_WAVE_LOADLEN, - DIV_CMD_N163_WAVE_LOADMODE, + DIV_CMD_N163_WAVE_UNUSED3, DIV_CMD_N163_CHANNEL_LIMIT, DIV_CMD_N163_GLOBAL_WAVE_LOAD, DIV_CMD_N163_GLOBAL_WAVE_LOADPOS, - DIV_CMD_N163_GLOBAL_WAVE_LOADLEN, - DIV_CMD_N163_GLOBAL_WAVE_LOADMODE, + DIV_CMD_N163_UNUSED4, + DIV_CMD_N163_UNUSED5, DIV_CMD_SU_SWEEP_PERIOD_LOW, // (which, val) DIV_CMD_SU_SWEEP_PERIOD_HIGH, // (which, val) diff --git a/src/engine/engine.h b/src/engine/engine.h index e07338db..5c81f3e1 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -56,8 +56,8 @@ #define DIV_UNSTABLE -#define DIV_VERSION "dev163" -#define DIV_ENGINE_VERSION 163 +#define DIV_VERSION "dev164" +#define DIV_ENGINE_VERSION 164 // for imports #define DIV_VERSION_MOD 0xff01 #define DIV_VERSION_FC 0xff02 diff --git a/src/engine/instrument.cpp b/src/engine/instrument.cpp index 76c410f6..fe2885ee 100644 --- a/src/engine/instrument.cpp +++ b/src/engine/instrument.cpp @@ -134,7 +134,24 @@ bool DivInstrumentN163::operator==(const DivInstrumentN163& other) { _C(wave) && _C(wavePos) && _C(waveLen) && - _C(waveMode) + _C(waveMode) && + _C(perChanPos) && + _C(wavePosCh[0]) && + _C(wavePosCh[1]) && + _C(wavePosCh[2]) && + _C(wavePosCh[3]) && + _C(wavePosCh[4]) && + _C(wavePosCh[5]) && + _C(wavePosCh[6]) && + _C(wavePosCh[7]) && + _C(waveLenCh[0]) && + _C(waveLenCh[1]) && + _C(waveLenCh[2]) && + _C(waveLenCh[3]) && + _C(waveLenCh[4]) && + _C(waveLenCh[5]) && + _C(waveLenCh[6]) && + _C(waveLenCh[7]) ); } @@ -519,6 +536,17 @@ void DivInstrument::writeFeatureN1(SafeWriter* w) { w->writeC(n163.waveLen); w->writeC(n163.waveMode); + w->writeC(n163.perChanPos); + + if (n163.perChanPos) { + for (int i=0; i<8; i++) { + w->writeC(n163.wavePosCh[i]); + } + for (int i=0; i<8; i++) { + w->writeC(n163.waveLenCh[i]); + } + } + FEATURE_END; } @@ -2280,6 +2308,18 @@ void DivInstrument::readFeatureN1(SafeReader& reader, short version) { n163.waveLen=(unsigned char)reader.readC(); n163.waveMode=(unsigned char)reader.readC(); + if (version>=164) { + n163.perChanPos=reader.readC(); + if (n163.perChanPos) { + for (int i=0; i<8; i++) { + n163.wavePosCh[i]=(unsigned char)reader.readC(); + } + for (int i=0; i<8; i++) { + n163.waveLenCh[i]=(unsigned char)reader.readC(); + } + } + } + READ_FEAT_END; } diff --git a/src/engine/instrument.h b/src/engine/instrument.h index ab2bc5e0..a6d2505f 100644 --- a/src/engine/instrument.h +++ b/src/engine/instrument.h @@ -446,6 +446,9 @@ struct DivInstrumentX1_010 { struct DivInstrumentN163 { int wave, wavePos, waveLen; unsigned char waveMode; + bool perChanPos; + int wavePosCh[8]; + int waveLenCh[8]; bool operator==(const DivInstrumentN163& other); bool operator!=(const DivInstrumentN163& other) { @@ -456,7 +459,13 @@ struct DivInstrumentN163 { wave(-1), wavePos(0), waveLen(32), - waveMode(3) {} + waveMode(3), + perChanPos(false) { + for (int i=0; i<8; i++) { + wavePosCh[i]=(i&3)<<5; + waveLenCh[i]=32; + } + } }; struct DivInstrumentFDS { diff --git a/src/engine/platform/n163.cpp b/src/engine/platform/n163.cpp index 96ccf2d4..30061ceb 100644 --- a/src/engine/platform/n163.cpp +++ b/src/engine/platform/n163.cpp @@ -146,7 +146,7 @@ void DivPlatformN163::updateWave(int ch, int wave, int pos, int len) { } else { // load from custom DivWavetable* wt=parent->getWave(wave); - for (int i=0; ilen; i++) { unsigned char addr=(pos+i); // address (nibble each) if (addr>=((0x78-(chanMax<<3))<<1)) { // avoid conflict with channel register area break; @@ -155,7 +155,7 @@ void DivPlatformN163::updateWave(int ch, int wave, int pos, int len) { if (wt->max<1 || wt->len<1) { rWriteMask(addr>>1,0,mask); } else { - int data=wt->data[i*wt->len/len]*15/wt->max; + int data=wt->data[i]*15/wt->max; if (data<0) data=0; if (data>15) data=15; rWriteMask(addr>>1,(addr&1)?(data<<4):(data&0xf),mask); @@ -282,8 +282,8 @@ int DivPlatformN163::dispatch(DivCommand c) { if (ins->n163.wave>=0) { chan[c.chan].wave=ins->n163.wave; } - chan[c.chan].wavePos=ins->n163.wavePos; - chan[c.chan].waveLen=ins->n163.waveLen; + chan[c.chan].wavePos=ins->n163.perChanPos?ins->n163.wavePosCh[c.chan&7]:ins->n163.wavePos; + chan[c.chan].waveLen=ins->n163.perChanPos?ins->n163.waveLenCh[c.chan&7]:ins->n163.waveLen; chan[c.chan].waveMode=ins->n163.waveMode; chan[c.chan].curWavePos=chan[c.chan].wavePos; chan[c.chan].curWaveLen=chan[c.chan].waveLen; @@ -393,9 +393,23 @@ int DivPlatformN163::dispatch(DivCommand c) { chan[c.chan].curWaveLen=c.value&0xfc; chan[c.chan].freqChanged=true; break; + case DIV_CMD_N163_WAVE_LOADPOS: + chan[c.chan].wavePos=c.value; + if (chan[c.chan].waveMode) { + chan[c.chan].waveUpdated=true; + } + break; + case DIV_CMD_N163_WAVE_LOADLEN: + chan[c.chan].waveLen=c.value&0xfc; + if (chan[c.chan].waveMode) { + chan[c.chan].waveUpdated=true; + } + break; case DIV_CMD_N163_GLOBAL_WAVE_LOAD: loadWave=c.value; - // TODO: load wave here + if (loadWave>=0 && loadWavesong.waveLen) { + updateWave(-1,loadWave,loadPos,-1); + } break; case DIV_CMD_N163_GLOBAL_WAVE_LOADPOS: loadPos=c.value; diff --git a/src/engine/sysDef.cpp b/src/engine/sysDef.cpp index 1529575d..a911e2df 100644 --- a/src/engine/sysDef.cpp +++ b/src/engine/sysDef.cpp @@ -995,15 +995,16 @@ void DivEngine::registerSystems() { {DIV_INS_N163, DIV_INS_N163, DIV_INS_N163, DIV_INS_N163, DIV_INS_N163, DIV_INS_N163, DIV_INS_N163, DIV_INS_N163}, {}, { - {0x18, {DIV_CMD_N163_CHANNEL_LIMIT, "180x: Change channel limits (0 to 7, x + 1)"}} + {0x18, {DIV_CMD_N163_CHANNEL_LIMIT, "18xx: Change channel limits (0 to 7, x + 1)"}}, + {0x20, {DIV_CMD_N163_GLOBAL_WAVE_LOAD, "20xx: Load a waveform into memory"}}, + {0x21, {DIV_CMD_N163_GLOBAL_WAVE_LOADPOS, "21xx: Set position for wave load"}} }, { {0x10, {DIV_CMD_WAVE, "10xx: Select waveform"}}, - {0x11, {DIV_CMD_N163_WAVE_POSITION, "11xx: Set waveform position in RAM (single nibble unit)"}}, - {0x12, {DIV_CMD_N163_WAVE_LENGTH, "12xx: Set waveform length in RAM (04 to FC, 4 nibble unit)"}}, - {0x13, {DIV_CMD_N163_WAVE_MODE, "130x: Change waveform update mode (0: off; bit 0: update now; bit 1: update when every waveform changes)"}}, - {0x20, {DIV_CMD_N163_GLOBAL_WAVE_LOAD, "20xx: Load a waveform into memory"}}, - {0x21, {DIV_CMD_N163_GLOBAL_WAVE_LOADPOS, "21xx: Set position for wave load"}}, + {0x11, {DIV_CMD_N163_WAVE_POSITION, "11xx: Set waveform position in RAM"}}, + {0x12, {DIV_CMD_N163_WAVE_LENGTH, "12xx: Set waveform length in RAM (04 to FC in steps of 4)"}}, + {0x15, {DIV_CMD_N163_WAVE_LOADPOS, "15xx: Set waveform load position"}}, + {0x16, {DIV_CMD_N163_WAVE_LOADLEN, "16xx: Set waveform load length (04 to FC in steps of 4)"}}, } ); diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index 7eec3a44..8d0bf0d0 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -4681,14 +4681,62 @@ void FurnaceGUI::drawInsEdit() { if (ins->n163.wave>=e->song.waveLen) ins->n163.wave=e->song.waveLen-1; } } - if (ImGui::InputInt("Offset##WAVEPOS",&ins->n163.wavePos,1,16)) { PARAMETER - if (ins->n163.wavePos<0) ins->n163.wavePos=0; - if (ins->n163.wavePos>255) ins->n163.wavePos=255; - } - if (ImGui::InputInt("Length##WAVELEN",&ins->n163.waveLen,4,16)) { PARAMETER - if (ins->n163.waveLen<0) ins->n163.waveLen=0; - if (ins->n163.waveLen>252) ins->n163.waveLen=252; - ins->n163.waveLen&=0xfc; + + ImGui::Separator(); + + P(ImGui::Checkbox("Per-channel wave offset/length",&ins->n163.perChanPos)); + + if (ins->n163.perChanPos) { + if (ImGui::BeginTable("N1PerChPos",3)) { + ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthFixed); + ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthStretch,0.5f); + ImGui::TableSetupColumn("c2",ImGuiTableColumnFlags_WidthStretch,0.5f); + + ImGui::TableNextRow(ImGuiTableRowFlags_Headers); + ImGui::TableNextColumn(); + ImGui::Text("Ch"); + ImGui::TableNextColumn(); + ImGui::Text("Offset"); + ImGui::TableNextColumn(); + ImGui::Text("Length"); + + for (int i=0; i<8; i++) { + ImGui::PushID(64+i); + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::Dummy(ImVec2(dpiScale,ImGui::GetFrameHeightWithSpacing())); + ImGui::SameLine(); + ImGui::Text("%d",i+1); + + ImGui::TableNextColumn(); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + if (ImGui::InputInt("##pcOff",&ins->n163.wavePosCh[i],1,16)) { PARAMETER + if (ins->n163.wavePosCh[i]<0) ins->n163.wavePosCh[i]=0; + if (ins->n163.wavePosCh[i]>255) ins->n163.wavePosCh[i]=255; + } + + ImGui::TableNextColumn(); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + if (ImGui::InputInt("##pcLen",&ins->n163.waveLenCh[i],4,16)) { PARAMETER + if (ins->n163.waveLenCh[i]<0) ins->n163.waveLenCh[i]=0; + if (ins->n163.waveLenCh[i]>252) ins->n163.waveLenCh[i]=252; + ins->n163.waveLenCh[i]&=0xfc; + } + ImGui::PopID(); + } + + ImGui::EndTable(); + } + } else { + if (ImGui::InputInt("Offset##WAVEPOS",&ins->n163.wavePos,1,16)) { PARAMETER + if (ins->n163.wavePos<0) ins->n163.wavePos=0; + if (ins->n163.wavePos>255) ins->n163.wavePos=255; + } + if (ImGui::InputInt("Length##WAVELEN",&ins->n163.waveLen,4,16)) { PARAMETER + if (ins->n163.waveLen<0) ins->n163.waveLen=0; + if (ins->n163.waveLen>252) ins->n163.waveLen=252; + ins->n163.waveLen&=0xfc; + } } ImGui::EndTabItem(); }