Merge branch 'master' into feature/esfm

This commit is contained in:
Kagamiin~ 2023-10-25 17:10:16 -03:00
commit 85db9ca16e
17 changed files with 478 additions and 26 deletions

11
TODO.md
View file

@ -4,13 +4,18 @@
# THE REAL TO-DO LIST
because I want to focus for a couple hours
- Amiga's Period Modulation not working
- Song is silent after playing an order after loop point
- Crash when opening one-column chan osc with 48 channels
- Select loaded instrument on open - rewrite because I want a setting...
- re-engineer volume handling? Sound Unit cries at me
- finish status view
- finish auto-clone
once you have done all of this (maybe not the first one) and merged the two or so pending pull requests, release 0.6.1
Furnace is like alcohol...
# and then
- new oscilloscope renderer - custom code that uses texture and fixes two issues: too many vertices, and broken anti-aliasing
- new pattern renderer - performance improvements

View file

@ -47,6 +47,7 @@ two versions of aforementioned chip exist - 6581 (original chip) and 8580 (impro
- if `y` is not 0: now
- this effect is not necessary if the instrument's duty macro is absolute.
- `1Exy`: **change additional parameters.**
- _this effect only exists for compatibility reasons, and its use is discouraged._
- `x` may be one of the following:
- `0`: attack (`y` from `0` to `F`)
- `1`: decay (`y` from `0` to `F`)
@ -55,6 +56,12 @@ two versions of aforementioned chip exist - 6581 (original chip) and 8580 (impro
- `4`: ring modulation (`y` is `0` or `1`)
- `5`: oscillator sync (`y` is `0` or `1`)
- `6`: disable channel 3 (`y` is `0` or `1`)
- `20xy`: **set attack/decay.**
- `x` is the attack.
- `y` is the decay.
- `21xy`: **set sustain/release.**
- `x` is the sustain.
- `y` is the release.
- `3xxx`: **set duty cycle.** `xxx` range is `000` to `FFF`.
- `4xxx`: **set cutoff.** `xxx` range is `000` to `7FF`.

View file

@ -556,6 +556,31 @@ size | description
size | description
-----|------------------------------------
1 | switch roles of phase reset timer and frequency
1 | hardware sequence length (>=185)
??? | hardware sequence...
| - length: 5*hwSeqLen
```
a value in the hardware sequence has the following format:
```
size | description
-----|------------------------------------
1 | command
| - 0: set volume sweep
| - 1: set frequency sweep
| - 2: set cutoff sweep
| - 3: wait
| - 4: wait for release
| - 5: loop
| - 6: loop until release
1 | sweep bound
1 | sweep amount/command data
| - if "set sweep", this is amount.
| - for wait: length in ticks
| - for wait for release: nothing
| - for loop/loop until release: position
2 | sweep period
```
# ES5506 data (ES)

View file

@ -238,13 +238,16 @@ enum DivDispatchCmds {
DIV_CMD_EXTERNAL, // (value)
DIV_ALWAYS_SET_VOLUME, // () -> alwaysSetVol
DIV_CMD_C64_AD, // (value)
DIV_CMD_C64_SR, // (value)
DIV_CMD_ESFM_OP_PANNING, // (op, value)
DIV_CMD_ESFM_OUTLVL, // (op, value)
DIV_CMD_ESFM_MODIN, // (op, value)
DIV_CMD_ESFM_ENV_DELAY, // (op, value)
DIV_ALWAYS_SET_VOLUME, // () -> alwaysSetVol
DIV_CMD_MAX
};

View file

@ -54,8 +54,8 @@ class DivWorkPool;
#define DIV_UNSTABLE
#define DIV_VERSION "dev184"
#define DIV_ENGINE_VERSION 184
#define DIV_VERSION "dev186"
#define DIV_ENGINE_VERSION 186
// for imports
#define DIV_VERSION_MOD 0xff01
#define DIV_VERSION_FC 0xff02

View file

@ -3008,6 +3008,15 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
}
}
// C64 1Exy compat
if (ds.version<186) {
for (int i=0; i<ds.systemLen; i++) {
if (ds.system[i]==DIV_SYSTEM_C64_8580 || ds.system[i]==DIV_SYSTEM_C64_6581) {
ds.systemFlags[i].set("no1EUpdate",true);
}
}
}
if (active) quitDispatch();
BUSY_BEGIN_SOFT;
saveLock.lock();
@ -3149,8 +3158,8 @@ bool DivEngine::loadMod(unsigned char* file, size_t len) {
ds.sampleLen=ds.sample.size();
// orders
ds.subsong[0]->ordersLen=ordCount=reader.readC();
if (ds.subsong[0]->ordersLen<1 || ds.subsong[0]->ordersLen>127) {
ds.subsong[0]->ordersLen=ordCount=(unsigned char)reader.readC();
if (ds.subsong[0]->ordersLen<1 || ds.subsong[0]->ordersLen>128) {
logD("invalid order count!");
throw EndOfFileException(&reader,reader.tell());
}

View file

@ -196,7 +196,10 @@ bool DivInstrumentWaveSynth::operator==(const DivInstrumentWaveSynth& other) {
}
bool DivInstrumentSoundUnit::operator==(const DivInstrumentSoundUnit& other) {
return _C(switchRoles);
return (
_C(switchRoles) &&
_C(hwSeqLen)
);
}
bool DivInstrumentES5506::operator==(const DivInstrumentES5506& other) {
@ -714,6 +717,14 @@ void DivInstrument::writeFeatureSU(SafeWriter* w) {
w->writeC(su.switchRoles);
w->writeC(su.hwSeqLen);
for (int i=0; i<su.hwSeqLen; i++) {
w->writeC(su.hwSeq[i].cmd);
w->writeC(su.hwSeq[i].bound);
w->writeC(su.hwSeq[i].val);
w->writeS(su.hwSeq[i].speed);
}
FEATURE_END;
}
@ -2585,6 +2596,16 @@ void DivInstrument::readFeatureSU(SafeReader& reader, short version) {
su.switchRoles=reader.readC();
if (version>=185) {
su.hwSeqLen=reader.readC();
for (int i=0; i<su.hwSeqLen; i++) {
su.hwSeq[i].cmd=reader.readC();
su.hwSeq[i].bound=reader.readC();
su.hwSeq[i].val=reader.readC();
su.hwSeq[i].speed=reader.readS();
}
}
READ_FEAT_END;
}

View file

@ -388,7 +388,7 @@ struct DivInstrumentGB {
DIV_GB_HWCMD_MAX
};
struct HWSeqCommand {
struct HWSeqCommandGB {
unsigned char cmd;
unsigned short data;
} hwSeq[256];
@ -406,7 +406,7 @@ struct DivInstrumentGB {
hwSeqLen(0),
softEnv(false),
alwaysInit(false) {
memset(hwSeq,0,256*sizeof(int));
memset(hwSeq,0,256*sizeof(HWSeqCommandGB));
}
};
@ -666,6 +666,25 @@ struct DivInstrumentWaveSynth {
struct DivInstrumentSoundUnit {
bool switchRoles;
unsigned char hwSeqLen;
enum HWSeqCommands: unsigned char {
DIV_SU_HWCMD_VOL=0,
DIV_SU_HWCMD_PITCH,
DIV_SU_HWCMD_CUT,
DIV_SU_HWCMD_WAIT,
DIV_SU_HWCMD_WAIT_REL,
DIV_SU_HWCMD_LOOP,
DIV_SU_HWCMD_LOOP_REL,
DIV_SU_HWCMD_MAX
};
struct HWSeqCommandSU {
unsigned char cmd;
unsigned char bound;
unsigned char val;
unsigned short speed;
unsigned short padding;
} hwSeq[256];
bool operator==(const DivInstrumentSoundUnit& other);
bool operator!=(const DivInstrumentSoundUnit& other) {
@ -673,7 +692,10 @@ struct DivInstrumentSoundUnit {
}
DivInstrumentSoundUnit():
switchRoles(false) {}
switchRoles(false),
hwSeqLen(0) {
memset(hwSeq,0,256*sizeof(HWSeqCommandSU));
}
};
struct DivInstrumentES5506 {

View file

@ -457,15 +457,27 @@ int DivPlatformC64::dispatch(DivCommand c) {
switch (c.value>>4) {
case 0:
chan[c.chan].attack=c.value&15;
if (!no1EUpdate) {
rWrite(c.chan*7+5,(chan[c.chan].attack<<4)|(chan[c.chan].decay));
}
break;
case 1:
chan[c.chan].decay=c.value&15;
if (!no1EUpdate) {
rWrite(c.chan*7+5,(chan[c.chan].attack<<4)|(chan[c.chan].decay));
}
break;
case 2:
chan[c.chan].sustain=c.value&15;
if (!no1EUpdate) {
rWrite(c.chan*7+6,(chan[c.chan].sustain<<4)|(chan[c.chan].release));
}
break;
case 3:
chan[c.chan].release=c.value&15;
if (!no1EUpdate) {
rWrite(c.chan*7+6,(chan[c.chan].sustain<<4)|(chan[c.chan].release));
}
break;
case 4:
chan[c.chan].ring=c.value;
@ -481,6 +493,16 @@ int DivPlatformC64::dispatch(DivCommand c) {
break;
}
break;
case DIV_CMD_C64_AD:
chan[c.chan].attack=c.value>>4;
chan[c.chan].decay=c.value&15;
rWrite(c.chan*7+5,(chan[c.chan].attack<<4)|(chan[c.chan].decay));
break;
case DIV_CMD_C64_SR:
chan[c.chan].sustain=c.value>>4;
chan[c.chan].release=c.value&15;
rWrite(c.chan*7+6,(chan[c.chan].sustain<<4)|(chan[c.chan].release));
break;
case DIV_CMD_MACRO_OFF:
chan[c.chan].std.mask(c.value,true);
break;
@ -652,6 +674,7 @@ void DivPlatformC64::setFlags(const DivConfig& flags) {
if (sidCore==1) sid_fp->setSamplingParameters(chipClock,reSIDfp::DECIMATE,rate,0);
}
keyPriority=flags.getBool("keyPriority",true);
no1EUpdate=flags.getBool("no1EUpdate",false);
testAD=((flags.getInt("testAttack",0)&15)<<4)|(flags.getInt("testDecay",0)&15);
testSR=((flags.getInt("testSustain",0)&15)<<4)|(flags.getInt("testRelease",0)&15);

View file

@ -72,7 +72,7 @@ class DivPlatformC64: public DivDispatch {
unsigned char sidCore;
int filtCut, resetTime;
bool keyPriority, sidIs6581, needInitTables;
bool keyPriority, sidIs6581, needInitTables, no1EUpdate;
unsigned char chanOrder[3];
unsigned char testAD, testSR;

View file

@ -134,6 +134,79 @@ void DivPlatformSoundUnit::tick(bool sysTick) {
}
writeControlUpper(i);
}
// 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_SU);
int hwSeqCount=0;
while (chan[i].hwSeqPos<ins->su.hwSeqLen && hwSeqCount<8) {
bool leave=false;
unsigned char bound=ins->su.hwSeq[chan[i].hwSeqPos].bound;
unsigned char val=ins->su.hwSeq[chan[i].hwSeqPos].val;
unsigned short speed=ins->su.hwSeq[chan[i].hwSeqPos].speed;
switch (ins->su.hwSeq[chan[i].hwSeqPos].cmd) {
case DivInstrumentSoundUnit::DIV_SU_HWCMD_VOL:
chan[i].volSweepP=speed;
chan[i].volSweepV=val;
chan[i].volSweepB=bound;
chan[i].volSweep=(val>0);
chWrite(i,0x14,chan[i].volSweepP&0xff);
chWrite(i,0x15,chan[i].volSweepP>>8);
chWrite(i,0x16,chan[i].volSweepV);
chWrite(i,0x17,chan[i].volSweepB);
writeControlUpper(i);
break;
case DivInstrumentSoundUnit::DIV_SU_HWCMD_PITCH:
chan[i].freqSweepP=speed;
chan[i].freqSweepV=val;
chan[i].freqSweepB=bound;
chan[i].freqSweep=(val>0);
chWrite(i,0x10,chan[i].freqSweepP&0xff);
chWrite(i,0x11,chan[i].freqSweepP>>8);
chWrite(i,0x12,chan[i].freqSweepV);
chWrite(i,0x13,chan[i].freqSweepB);
writeControlUpper(i);
break;
case DivInstrumentSoundUnit::DIV_SU_HWCMD_CUT:
chan[i].cutSweepP=speed;
chan[i].cutSweepV=val;
chan[i].cutSweepB=bound;
chan[i].cutSweep=(val>0);
chWrite(i,0x18,chan[i].cutSweepP&0xff);
chWrite(i,0x19,chan[i].cutSweepP>>8);
chWrite(i,0x1a,chan[i].cutSweepV);
chWrite(i,0x1b,chan[i].cutSweepB);
writeControlUpper(i);
break;
case DivInstrumentSoundUnit::DIV_SU_HWCMD_WAIT:
chan[i].hwSeqDelay=(val+1)*parent->tickMult;
leave=true;
break;
case DivInstrumentSoundUnit::DIV_SU_HWCMD_WAIT_REL:
if (!chan[i].released) {
chan[i].hwSeqPos--;
leave=true;
}
break;
case DivInstrumentSoundUnit::DIV_SU_HWCMD_LOOP:
chan[i].hwSeqPos=val-1;
break;
case DivInstrumentSoundUnit::DIV_SU_HWCMD_LOOP_REL:
if (!chan[i].released) {
chan[i].hwSeqPos=val-1;
}
break;
}
chan[i].hwSeqPos++;
if (leave) break;
hwSeqCount++;
}
}
}
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,chan[i].fixedArp?chan[i].baseNoteOverride:chan[i].arpOff,chan[i].fixedArp,chan[i].switchRoles,2,chan[i].pitch2,chipClock,chan[i].switchRoles?CHIP_DIVIDER:CHIP_FREQBASE);
@ -160,8 +233,7 @@ void DivPlatformSoundUnit::tick(bool sysTick) {
}
if (chan[i].keyOn) {
if (chan[i].pcm) {
DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_SU);
int sNum=ins->amiga.getSample(chan[i].note);
int sNum=chan[i].sample;
DivSample* sample=parent->getSample(sNum);
if (sample!=NULL && sNum>=0 && sNum<parent->song.sampleLen) {
unsigned int sampleEnd=sampleOffSU[sNum]+(sample->getLoopEndPosition());
@ -220,6 +292,9 @@ int DivPlatformSoundUnit::dispatch(DivCommand c) {
}
chan[c.chan].active=true;
chan[c.chan].keyOn=true;
chan[c.chan].released=false;
chan[c.chan].hwSeqPos=0;
chan[c.chan].hwSeqDelay=0;
chWrite(c.chan,0x02,chan[c.chan].vol);
chan[c.chan].macroInit(ins);
if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) {
@ -231,11 +306,14 @@ int DivPlatformSoundUnit::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

@ -30,12 +30,14 @@ class DivPlatformSoundUnit: public DivDispatch {
signed char pan;
unsigned char duty;
bool noise, pcm, phaseReset, filterPhaseReset, switchRoles;
bool pcmLoop, timerSync, freqSweep, volSweep, cutSweep;
bool pcmLoop, timerSync, freqSweep, volSweep, cutSweep, released;
unsigned short freqSweepP, volSweepP, cutSweepP;
unsigned char freqSweepB, volSweepB, cutSweepB;
unsigned char freqSweepV, volSweepV, cutSweepV;
unsigned short syncTimer;
signed short wave;
unsigned short hwSeqPos;
short hwSeqDelay;
Channel():
SharedChannel<signed char>(127),
cutoff(16383),
@ -56,6 +58,7 @@ class DivPlatformSoundUnit: public DivDispatch {
freqSweep(false),
volSweep(false),
cutSweep(false),
released(false),
freqSweepP(0),
volSweepP(0),
cutSweepP(0),
@ -66,7 +69,9 @@ class DivPlatformSoundUnit: public DivDispatch {
volSweepV(0),
cutSweepV(0),
syncTimer(0),
wave(0) {}
wave(0),
hwSeqPos(0),
hwSeqDelay(0) {}
};
Channel chan[8];
DivDispatchOscBuffer* oscBuf[8];

View file

@ -239,12 +239,16 @@ const char* cmdName[]={
"EXTERNAL",
"C64_AD",
"C64_SR",
"ESFM_OP_PANNING",
"ESFM_OUTLVL",
"ESFM_MODIN",
"ESFM_ENV_DELAY",
"ALWAYS_SET_VOLUME",
"DIV_CMD_ESFM_OP_PANNING",
"DIV_CMD_ESFM_OUTLVL",
"DIV_CMD_ESFM_MODIN",
"DIV_CMD_ESFM_ENV_DELAY"
};
static_assert((sizeof(cmdName)/sizeof(void*))==DIV_CMD_MAX,"update cmdName!");

View file

@ -606,6 +606,8 @@ void DivEngine::registerSystems() {
{0x1b, {DIV_CMD_C64_FILTER_RESET, "1Bxy: Reset cutoff (x: on new note; y: now)"}},
{0x1c, {DIV_CMD_C64_DUTY_RESET, "1Cxy: Reset pulse width (x: on new note; y: now)"}},
{0x1e, {DIV_CMD_C64_EXTENDED, "1Exy: Change additional parameters"}},
{0x20, {DIV_CMD_C64_AD, "20xy: Set attack/decay (x: attack; y: decay)"}},
{0x21, {DIV_CMD_C64_SR, "21xy: Set sustain/release (x: sustain; y: release)"}},
};
const EffectHandler c64FineDutyHandler(DIV_CMD_C64_FINE_DUTY, "3xxx: Set pulse width (0 to FFF)", effectValLong<12>);
const EffectHandler c64FineCutoffHandler(DIV_CMD_C64_FINE_CUTOFF, "4xxx: Set cutoff (0 to 7FF)", effectValLong<11>);

View file

@ -378,6 +378,16 @@ const char* gbHWSeqCmdTypes[6]={
"Loop until Release"
};
const char* suHWSeqCmdTypes[7]={
"Volume Sweep",
"Frequency Sweep",
"Cutoff Sweep",
"Wait",
"Wait for Release",
"Loop",
"Loop until Release"
};
const char* snesGainModes[5]={
"Direct",
"Decrease (linear)",
@ -2453,7 +2463,6 @@ void FurnaceGUI::alterSampleMap(int column, int val) {
void FurnaceGUI::insTabSample(DivInstrument* ins) {
const char* sampleTabName="Sample";
if (ins->type==DIV_INS_SU) sampleTabName="Sound Unit";
if (ins->type==DIV_INS_NES) sampleTabName="DPCM";
if (ImGui::BeginTabItem(sampleTabName)) {
if (ins->type==DIV_INS_NES && e->song.oldDPCM) {
@ -2482,9 +2491,6 @@ void FurnaceGUI::insTabSample(DivInstrument* ins) {
ins->type==DIV_INS_VRC6 ||
ins->type==DIV_INS_SU) {
P(ImGui::Checkbox("Use sample",&ins->amiga.useSample));
if (ins->type==DIV_INS_SU) {
P(ImGui::Checkbox("Switch roles of frequency and phase reset timer",&ins->su.switchRoles));
}
if (ins->type==DIV_INS_X1_010) {
if (ImGui::InputInt("Sample bank slot##BANKSLOT",&ins->x1_010.bankSlot,1,4)) { PARAMETER
if (ins->x1_010.bankSlot<0) ins->x1_010.bankSlot=0;
@ -5733,6 +5739,242 @@ void FurnaceGUI::drawInsEdit() {
P(ImGui::Checkbox("Don't test before new note",&ins->c64.noTest));
ImGui::EndTabItem();
}
if (ins->type==DIV_INS_SU) if (ImGui::BeginTabItem("Sound Unit")) {
P(ImGui::Checkbox("Switch roles of frequency and phase reset timer",&ins->su.switchRoles));
if (ImGui::BeginChild("HWSeqSU",ImGui::GetContentRegionAvail(),true,ImGuiWindowFlags_MenuBar)) {
ImGui::BeginMenuBar();
ImGui::Text("Hardware Sequence");
ImGui::EndMenuBar();
if (ins->su.hwSeqLen>0) if (ImGui::BeginTable("HWSeqListSU",3)) {
ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthFixed);
ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthStretch);
ImGui::TableSetupColumn("c2",ImGuiTableColumnFlags_WidthFixed);
int curFrame=0;
ImGui::TableNextRow(ImGuiTableRowFlags_Headers);
ImGui::TableNextColumn();
ImGui::Text("Tick");
ImGui::TableNextColumn();
ImGui::Text("Command");
ImGui::TableNextColumn();
ImGui::Text("Move/Remove");
for (int i=0; i<ins->su.hwSeqLen; i++) {
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Text("%d (#%d)",curFrame,i);
ImGui::TableNextColumn();
ImGui::PushID(i);
if (ins->su.hwSeq[i].cmd>=DivInstrumentSoundUnit::DIV_SU_HWCMD_MAX) {
ins->su.hwSeq[i].cmd=0;
}
int cmd=ins->su.hwSeq[i].cmd;
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
if (ImGui::Combo("##HWSeqCmd",&cmd,suHWSeqCmdTypes,DivInstrumentSoundUnit::DIV_SU_HWCMD_MAX)) {
if (ins->su.hwSeq[i].cmd!=cmd) {
ins->su.hwSeq[i].cmd=cmd;
ins->su.hwSeq[i].val=0;
ins->su.hwSeq[i].bound=0;
ins->su.hwSeq[i].speed=0;
}
}
bool somethingChanged=false;
switch (ins->su.hwSeq[i].cmd) {
case DivInstrumentSoundUnit::DIV_SU_HWCMD_VOL: {
int swPeriod=ins->su.hwSeq[i].speed;
int swBound=ins->su.hwSeq[i].bound;
int swVal=ins->su.hwSeq[i].val&31;
bool swDir=ins->su.hwSeq[i].val&32;
bool swLoop=ins->su.hwSeq[i].val&64;
bool swInvert=ins->su.hwSeq[i].val&128;
if (ImGui::InputInt("Period",&swPeriod,1,16)) {
if (swPeriod<0) swPeriod=0;
if (swPeriod>65535) swPeriod=65535;
somethingChanged=true;
}
if (CWSliderInt("Amount",&swVal,0,31)) {
somethingChanged=true;
}
if (CWSliderInt("Bound",&swBound,0,255)) {
somethingChanged=true;
}
if (ImGui::RadioButton("Up",swDir)) { PARAMETER
swDir=true;
somethingChanged=true;
}
ImGui::SameLine();
if (ImGui::RadioButton("Down",!swDir)) { PARAMETER
swDir=false;
somethingChanged=true;
}
if (ImGui::Checkbox("Loop",&swLoop)) { PARAMETER
somethingChanged=true;
}
ImGui::SameLine();
if (ImGui::Checkbox("Flip",&swInvert)) { PARAMETER
somethingChanged=true;
}
if (somethingChanged) {
ins->su.hwSeq[i].speed=swPeriod;
ins->su.hwSeq[i].bound=swBound;
ins->su.hwSeq[i].val=(swVal&31)|(swDir?32:0)|(swLoop?64:0)|(swInvert?128:0);
PARAMETER;
}
break;
}
case DivInstrumentSoundUnit::DIV_SU_HWCMD_PITCH:
case DivInstrumentSoundUnit::DIV_SU_HWCMD_CUT: {
int swPeriod=ins->su.hwSeq[i].speed;
int swBound=ins->su.hwSeq[i].bound;
int swVal=ins->su.hwSeq[i].val&127;
bool swDir=ins->su.hwSeq[i].val&128;
if (ImGui::InputInt("Period",&swPeriod,1,16)) {
if (swPeriod<0) swPeriod=0;
if (swPeriod>65535) swPeriod=65535;
somethingChanged=true;
}
if (CWSliderInt("Amount",&swVal,0,31)) {
somethingChanged=true;
}
if (CWSliderInt("Bound",&swBound,0,255)) {
somethingChanged=true;
}
if (ImGui::RadioButton("Up",swDir)) { PARAMETER
swDir=true;
somethingChanged=true;
}
ImGui::SameLine();
if (ImGui::RadioButton("Down",!swDir)) { PARAMETER
swDir=false;
somethingChanged=true;
}
if (somethingChanged) {
ins->su.hwSeq[i].speed=swPeriod;
ins->su.hwSeq[i].bound=swBound;
ins->su.hwSeq[i].val=(swVal&127)|(swDir?128:0);
PARAMETER;
}
break;
}
case DivInstrumentSoundUnit::DIV_SU_HWCMD_WAIT: {
int len=ins->su.hwSeq[i].val+1;
curFrame+=ins->su.hwSeq[i].val+1;
if (ImGui::InputInt("Ticks",&len)) {
if (len<1) len=1;
if (len>255) len=256;
somethingChanged=true;
}
if (somethingChanged) {
ins->su.hwSeq[i].val=len-1;
PARAMETER;
}
break;
}
case DivInstrumentSoundUnit::DIV_SU_HWCMD_WAIT_REL:
curFrame++;
break;
case DivInstrumentSoundUnit::DIV_SU_HWCMD_LOOP:
case DivInstrumentSoundUnit::DIV_SU_HWCMD_LOOP_REL: {
int pos=ins->su.hwSeq[i].val;
if (ImGui::InputInt("Position",&pos)) {
if (pos<0) pos=0;
if (pos>(ins->su.hwSeqLen-1)) pos=(ins->su.hwSeqLen-1);
somethingChanged=true;
}
if (somethingChanged) {
ins->su.hwSeq[i].val=pos;
PARAMETER;
}
break;
}
default:
break;
}
ImGui::PopID();
ImGui::TableNextColumn();
ImGui::PushID(i+512);
if (ImGui::Button(ICON_FA_CHEVRON_UP "##HWCmdUp")) {
if (i>0) {
e->lockEngine([ins,i]() {
ins->su.hwSeq[i-1].cmd^=ins->su.hwSeq[i].cmd;
ins->su.hwSeq[i].cmd^=ins->su.hwSeq[i-1].cmd;
ins->su.hwSeq[i-1].cmd^=ins->su.hwSeq[i].cmd;
ins->su.hwSeq[i-1].speed^=ins->su.hwSeq[i].speed;
ins->su.hwSeq[i].speed^=ins->su.hwSeq[i-1].speed;
ins->su.hwSeq[i-1].speed^=ins->su.hwSeq[i].speed;
ins->su.hwSeq[i-1].val^=ins->su.hwSeq[i].val;
ins->su.hwSeq[i].val^=ins->su.hwSeq[i-1].val;
ins->su.hwSeq[i-1].val^=ins->su.hwSeq[i].val;
ins->su.hwSeq[i-1].bound^=ins->su.hwSeq[i].bound;
ins->su.hwSeq[i].bound^=ins->su.hwSeq[i-1].bound;
ins->su.hwSeq[i-1].bound^=ins->su.hwSeq[i].bound;
});
}
MARK_MODIFIED;
}
ImGui::SameLine();
if (ImGui::Button(ICON_FA_CHEVRON_DOWN "##HWCmdDown")) {
if (i<ins->su.hwSeqLen-1) {
e->lockEngine([ins,i]() {
ins->su.hwSeq[i+1].cmd^=ins->su.hwSeq[i].cmd;
ins->su.hwSeq[i].cmd^=ins->su.hwSeq[i+1].cmd;
ins->su.hwSeq[i+1].cmd^=ins->su.hwSeq[i].cmd;
ins->su.hwSeq[i+1].speed^=ins->su.hwSeq[i].speed;
ins->su.hwSeq[i].speed^=ins->su.hwSeq[i+1].speed;
ins->su.hwSeq[i+1].speed^=ins->su.hwSeq[i].speed;
ins->su.hwSeq[i+1].val^=ins->su.hwSeq[i].val;
ins->su.hwSeq[i].val^=ins->su.hwSeq[i+1].val;
ins->su.hwSeq[i+1].val^=ins->su.hwSeq[i].val;
ins->su.hwSeq[i+1].bound^=ins->su.hwSeq[i].bound;
ins->su.hwSeq[i].bound^=ins->su.hwSeq[i+1].bound;
ins->su.hwSeq[i+1].bound^=ins->su.hwSeq[i].bound;
});
}
MARK_MODIFIED;
}
ImGui::SameLine();
pushDestColor();
if (ImGui::Button(ICON_FA_TIMES "##HWCmdDel")) {
for (int j=i; j<ins->su.hwSeqLen-1; j++) {
ins->su.hwSeq[j].cmd=ins->su.hwSeq[j+1].cmd;
ins->su.hwSeq[j].speed=ins->su.hwSeq[j+1].speed;
ins->su.hwSeq[j].val=ins->su.hwSeq[j+1].val;
ins->su.hwSeq[j].bound=ins->su.hwSeq[j+1].bound;
}
ins->su.hwSeqLen--;
}
popDestColor();
ImGui::PopID();
}
ImGui::EndTable();
}
if (ImGui::Button(ICON_FA_PLUS "##HWCmdAdd")) {
if (ins->su.hwSeqLen<255) {
ins->su.hwSeq[ins->su.hwSeqLen].cmd=0;
ins->su.hwSeq[ins->su.hwSeqLen].speed=0;
ins->su.hwSeq[ins->su.hwSeqLen].val=0;
ins->su.hwSeq[ins->su.hwSeqLen].bound=0;
ins->su.hwSeqLen++;
}
}
}
ImGui::EndChild();
ImGui::EndTabItem();
}
if (ins->type==DIV_INS_MSM6258 ||
ins->type==DIV_INS_MSM6295 ||
ins->type==DIV_INS_ADPCMA ||

View file

@ -976,7 +976,7 @@ void FurnaceGUI::initSystemPresets() {
);
ENTRY(
"PC + AdLib (drums mode)", {
CH(DIV_SYSTEM_OPL2, 1.0f, 0, ""),
CH(DIV_SYSTEM_OPL2_DRUMS, 1.0f, 0, ""),
CH(DIV_SYSTEM_PCSPKR, 1.0f, 0, "")
}
);

View file

@ -586,6 +586,7 @@ bool FurnaceGUI::drawSysConf(int chan, int sysPos, DivSystem type, DivConfig& fl
case DIV_SYSTEM_C64_6581: {
int clockSel=flags.getInt("clockSel",0);
bool keyPriority=flags.getBool("keyPriority",true);
bool no1EUpdate=flags.getBool("no1EUpdate",false);
int testAttack=flags.getInt("testAttack",0);
int testDecay=flags.getInt("testDecay",0);
int testSustain=flags.getInt("testSustain",0);
@ -644,10 +645,15 @@ bool FurnaceGUI::drawSysConf(int chan, int sysPos, DivSystem type, DivConfig& fl
altered=true;
}
if (ImGui::Checkbox("Disable 1Exy env update (compatibility)",&no1EUpdate)) {
altered=true;
}
if (altered) {
e->lockSave([&]() {
flags.set("clockSel",clockSel);
flags.set("keyPriority",keyPriority);
flags.set("no1EUpdate",no1EUpdate);
flags.set("testAttack",testAttack);
flags.set("testDecay",testDecay);
flags.set("testSustain",testSustain);