dev106 - Game Boy: implement hw seq

and prepare for software envelope maybe
This commit is contained in:
tildearrow 2022-08-10 01:55:44 -05:00
parent da8f7dabd5
commit 28698beaf3
8 changed files with 93 additions and 13 deletions

View File

@ -1,6 +1,5 @@
# to-do for 0.6pre1.5-0.6pre2
- Game Boy envelope macro/sequence
- volume commands should work on Game Boy
- ability to customize `OFF`, `===` and `REL`
- stereo separation control for AY

View File

@ -32,7 +32,8 @@ these fields are 0 in format versions prior to 100 (0.6pre1).
the format versions are:
- 105: Furance dev105
- 106: Furnace dev106
- 105: Furnace dev105
- 104: Furnace dev104
- 103: Furnace dev103
- 102: Furnace 0.6pre1 (dev102)
@ -846,6 +847,9 @@ size | description
| - 2 bytes: nothing
| - for loop/loop until release:
| - 2 bytes: position
--- | **Game Boy extra flags** (>=106)
1 | use software envelope
1 | always init hard env on new note
```
# wavetable

View File

@ -45,8 +45,8 @@
#define BUSY_BEGIN_SOFT softLocked=true; isBusy.lock();
#define BUSY_END isBusy.unlock(); softLocked=false;
#define DIV_VERSION "dev105"
#define DIV_ENGINE_VERSION 105
#define DIV_VERSION "dev106"
#define DIV_ENGINE_VERSION 106
// for imports
#define DIV_VERSION_MOD 0xff01

View File

@ -539,6 +539,10 @@ void DivInstrument::putInsData(SafeWriter* w) {
w->writeS(gb.hwSeq[i].data);
}
// GB additional flags
w->writeC(gb.softEnv);
w->writeC(gb.alwaysInit);
blockEndSeek=w->tell();
w->seek(blockStartSeek,SEEK_SET);
w->writeI(blockEndSeek-blockStartSeek-4);
@ -1101,6 +1105,12 @@ DivDataErrors DivInstrument::readInsData(SafeReader& reader, short version) {
}
}
// GB additional flags
if (version>=106) {
gb.softEnv=reader.readC();
gb.alwaysInit=reader.readC();
}
return DIV_DATA_SUCCESS;
}

View File

@ -262,6 +262,7 @@ struct DivInstrumentSTD {
struct DivInstrumentGB {
unsigned char envVol, envDir, envLen, soundLen, hwSeqLen;
bool softEnv, alwaysInit;
enum HWSeqCommands: unsigned char {
DIV_GB_HWCMD_ENVELOPE=0,
DIV_GB_HWCMD_SWEEP,
@ -281,7 +282,9 @@ struct DivInstrumentGB {
envDir(0),
envLen(2),
soundLen(64),
hwSeqLen(0) {
hwSeqLen(0),
softEnv(false),
alwaysInit(false) {
memset(hwSeq,0,256*sizeof(int));
}
};

View File

@ -233,12 +233,61 @@ void DivPlatformGB::tick(bool sysTick) {
}
}
}
// run hardware sequence
if (chan[i].active) {
if (--chan[i].hwSeqDelay<=0) {
chan[i].hwSeqDelay=0;
DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_GB);
int hwSeqCount=0;
while (chan[i].hwSeqPos<ins->gb.hwSeqLen && hwSeqCount<4) {
bool leave=false;
unsigned short data=ins->gb.hwSeq[chan[i].hwSeqPos].data;
switch (ins->gb.hwSeq[chan[i].hwSeqPos].cmd) {
case DivInstrumentGB::DIV_GB_HWCMD_ENVELOPE:
chan[i].envLen=data&7;
chan[i].envDir=(data&8)?1:0;
chan[i].envVol=(data>>4)&15;
chan[i].soundLen=data>>8;
chan[i].keyOn=true;
break;
case DivInstrumentGB::DIV_GB_HWCMD_SWEEP:
chan[i].sweep=data;
chan[i].sweepChanged=true;
break;
case DivInstrumentGB::DIV_GB_HWCMD_WAIT:
chan[i].hwSeqDelay=data+1;
leave=true;
break;
case DivInstrumentGB::DIV_GB_HWCMD_WAIT_REL:
if (!chan[i].released) {
chan[i].hwSeqPos--;
leave=true;
}
break;
case DivInstrumentGB::DIV_GB_HWCMD_LOOP:
chan[i].hwSeqPos=data-1;
break;
case DivInstrumentGB::DIV_GB_HWCMD_LOOP_REL:
if (!chan[i].released) {
chan[i].hwSeqPos=data-1;
}
break;
}
chan[i].hwSeqPos++;
if (leave) break;
hwSeqCount++;
}
}
}
if (chan[i].sweepChanged) {
chan[i].sweepChanged=false;
if (i==0) {
rWrite(16+i*5,chan[i].sweep);
}
}
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
if (i==3) { // noise
int ntPos=chan[i].baseFreq;
@ -300,6 +349,9 @@ int DivPlatformGB::dispatch(DivCommand c) {
}
chan[c.chan].active=true;
chan[c.chan].keyOn=true;
chan[c.chan].hwSeqPos=0;
chan[c.chan].hwSeqDelay=0;
chan[c.chan].released=false;
chan[c.chan].macroInit(ins);
if (c.chan==2) {
if (chan[c.chan].wave<0) {
@ -308,7 +360,7 @@ int DivPlatformGB::dispatch(DivCommand c) {
}
ws.init(ins,32,15,chan[c.chan].insChanged);
}
if (chan[c.chan].insChanged) {
if (chan[c.chan].insChanged || ins->gb.alwaysInit) {
chan[c.chan].envVol=ins->gb.envVol;
chan[c.chan].envLen=ins->gb.envLen;
chan[c.chan].envDir=ins->gb.envDir;
@ -320,11 +372,14 @@ int DivPlatformGB::dispatch(DivCommand c) {
case DIV_CMD_NOTE_OFF:
chan[c.chan].active=false;
chan[c.chan].keyOff=true;
chan[c.chan].hwSeqPos=0;
chan[c.chan].hwSeqDelay=0;
chan[c.chan].macroInit(NULL);
break;
case DIV_CMD_NOTE_OFF_ENV:
case DIV_CMD_ENV_RELEASE:
chan[c.chan].std.release();
chan[c.chan].released=true;
break;
case DIV_CMD_INSTRUMENT:
if (chan[c.chan].ins!=c.value || c.value2==1) {

View File

@ -29,10 +29,11 @@ class DivPlatformGB: public DivDispatch {
struct Channel {
int freq, baseFreq, pitch, pitch2, note, ins;
unsigned char duty, sweep;
bool active, insChanged, freqChanged, sweepChanged, keyOn, keyOff, inPorta;
bool active, insChanged, freqChanged, sweepChanged, keyOn, keyOff, inPorta, released;
signed char vol, outVol, wave;
unsigned char envVol, envDir, envLen, soundLen;
unsigned short hwSeqPos, hwSeqDelay;
unsigned short hwSeqPos;
short hwSeqDelay;
DivMacroInt std;
void macroInit(DivInstrument* which) {
std.init(which);
@ -54,6 +55,7 @@ class DivPlatformGB: public DivDispatch {
keyOn(false),
keyOff(false),
inPorta(false),
released(false),
vol(15),
outVol(15),
wave(-1),

View File

@ -2965,6 +2965,10 @@ void FurnaceGUI::drawInsEdit() {
}
}
if (ins->type==DIV_INS_GB) if (ImGui::BeginTabItem("Game Boy")) {
P(ImGui::Checkbox("Use software envelope",&ins->gb.softEnv));
P(ImGui::Checkbox("Initialize envelope on every note",&ins->gb.alwaysInit));
ImGui::BeginDisabled(ins->gb.softEnv);
P(CWSliderScalar("Volume",ImGuiDataType_U8,&ins->gb.envVol,&_ZERO,&_FIFTEEN)); rightClickable
P(CWSliderScalar("Envelope Length",ImGuiDataType_U8,&ins->gb.envLen,&_ZERO,&_SEVEN)); rightClickable
P(CWSliderScalar("Sound Length",ImGuiDataType_U8,&ins->gb.soundLen,&_ZERO,&_SIXTY_FOUR,ins->gb.soundLen>63?"Infinity":"%d")); rightClickable
@ -3060,13 +3064,13 @@ void FurnaceGUI::drawInsEdit() {
somethingChanged=true;
}
if (ImGui::RadioButton("Up",hwsDir)) { PARAMETER
hwsDir=true;
if (ImGui::RadioButton("Up",!hwsDir)) { PARAMETER
hwsDir=false;
somethingChanged=true;
}
ImGui::SameLine();
if (ImGui::RadioButton("Down",!hwsDir)) { PARAMETER
hwsDir=false;
if (ImGui::RadioButton("Down",hwsDir)) { PARAMETER
hwsDir=true;
somethingChanged=true;
}
@ -3128,6 +3132,7 @@ void FurnaceGUI::drawInsEdit() {
}
ImGui::EndChild();
ImGui::EndDisabled();
}
ImGui::EndTabItem();
}
@ -3689,8 +3694,10 @@ void FurnaceGUI::drawInsEdit() {
if (ins->type==DIV_INS_FM || ins->type==DIV_INS_MIKEY || ins->type==DIV_INS_MULTIPCM || ins->type==DIV_INS_SU) {
volMax=127;
}
if (ins->type==DIV_INS_GB) {
if (ins->type==DIV_INS_GB && !ins->gb.softEnv) {
volMax=0;
} else {
volMax=15;
}
if (ins->type==DIV_INS_PET || ins->type==DIV_INS_BEEPER) {
volMax=1;