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
This commit is contained in:
tildearrow 2023-07-21 19:54:35 -05:00
parent d411c9caba
commit ad88ce46ab
11 changed files with 172 additions and 159 deletions

View File

@ -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
<!--
- **Waveform pos.**: sets the waveform source address in RAM for playback (single nibble unit)
- **Waveform len.**: sets the waveform source length for playback (4 nibble unit)
- **Waveform update**: sets the waveform update trigger behavior for playback
- **Waveform to load**: sets waveform source for load to RAM immediately or later
- **Wave pos. to load**: sets address of waveform for load to RAM (single nibble unit)
- **Wave len. to load**: sets length of waveform for load to RAM (4 nibble unit)
- **Waveform load**: sets the waveform load trigger behavior
-->
- **Phase Reset**: trigger restart of waveform
-->

View File

@ -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.**

View File

@ -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.

View File

@ -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)

View File

@ -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)

View File

@ -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

View File

@ -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;
}

View File

@ -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 {

View File

@ -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; i<len; i++) {
for (int i=0; i<wt->len; 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 && loadWave<parent->song.waveLen) {
updateWave(-1,loadWave,loadPos,-1);
}
break;
case DIV_CMD_N163_GLOBAL_WAVE_LOADPOS:
loadPos=c.value;

View File

@ -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)"}},
}
);

View File

@ -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();
}