mirror of
https://github.com/tildearrow/furnace.git
synced 2024-11-16 01:35:07 +00:00
add two new compatibility flags
proper noise range and duty macro is wave volume
This commit is contained in:
parent
8772439d3e
commit
e06e316e10
8 changed files with 79 additions and 13 deletions
|
@ -25,6 +25,9 @@ furthermore, an `or reserved` indicates this field is always present, but is res
|
||||||
|
|
||||||
the format versions are:
|
the format versions are:
|
||||||
|
|
||||||
|
- 42: Furnace 0.5.5pre1
|
||||||
|
- 41: Furnace 0.5.4
|
||||||
|
- 40: Furnace 0.5.3
|
||||||
- 39: Furnace 0.5.2
|
- 39: Furnace 0.5.2
|
||||||
- 38: Furnace 0.5.2pre2
|
- 38: Furnace 0.5.2pre2
|
||||||
- 37: Furnace 0.5.2pre1
|
- 37: Furnace 0.5.2pre1
|
||||||
|
@ -148,7 +151,9 @@ size | description
|
||||||
1 | limit slides (>=36) or reserved
|
1 | limit slides (>=36) or reserved
|
||||||
1 | linear pitch (>=36) or reserved
|
1 | linear pitch (>=36) or reserved
|
||||||
1 | loop modality (>=36) or reserved
|
1 | loop modality (>=36) or reserved
|
||||||
17 | reserved
|
1 | proper noise layout (>=42) or reserved
|
||||||
|
1 | wave duty is volume (>=42) or reserved
|
||||||
|
15 | reserved
|
||||||
4?? | pointers to instruments
|
4?? | pointers to instruments
|
||||||
4?? | pointers to wavetables
|
4?? | pointers to wavetables
|
||||||
4?? | pointers to samples
|
4?? | pointers to samples
|
||||||
|
|
|
@ -2077,6 +2077,10 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
||||||
ds.linearPitch=true;
|
ds.linearPitch=true;
|
||||||
ds.loopModality=0;
|
ds.loopModality=0;
|
||||||
}
|
}
|
||||||
|
if (ds.version<43) {
|
||||||
|
ds.properNoiseLayout=false;
|
||||||
|
ds.waveDutyIsVol=false;
|
||||||
|
}
|
||||||
|
|
||||||
reader.readS(); // reserved
|
reader.readS(); // reserved
|
||||||
int infoSeek=reader.readI();
|
int infoSeek=reader.readI();
|
||||||
|
@ -2148,7 +2152,17 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
||||||
ds.limitSlides=reader.readC();
|
ds.limitSlides=reader.readC();
|
||||||
ds.linearPitch=reader.readC();
|
ds.linearPitch=reader.readC();
|
||||||
ds.loopModality=reader.readC();
|
ds.loopModality=reader.readC();
|
||||||
for (int i=0; i<17; i++) reader.readC();
|
if (ds.version>=43) {
|
||||||
|
ds.properNoiseLayout=reader.readC();
|
||||||
|
} else {
|
||||||
|
reader.readC();
|
||||||
|
}
|
||||||
|
if (ds.version>=43) {
|
||||||
|
ds.waveDutyIsVol=reader.readC();
|
||||||
|
} else {
|
||||||
|
reader.readC();
|
||||||
|
}
|
||||||
|
for (int i=0; i<15; i++) reader.readC();
|
||||||
} else {
|
} else {
|
||||||
for (int i=0; i<20; i++) reader.readC();
|
for (int i=0; i<20; i++) reader.readC();
|
||||||
}
|
}
|
||||||
|
@ -2522,7 +2536,9 @@ SafeWriter* DivEngine::saveFur() {
|
||||||
w->writeC(song.limitSlides);
|
w->writeC(song.limitSlides);
|
||||||
w->writeC(song.linearPitch);
|
w->writeC(song.linearPitch);
|
||||||
w->writeC(song.loopModality);
|
w->writeC(song.loopModality);
|
||||||
for (int i=0; i<17; i++) {
|
w->writeC(song.properNoiseLayout);
|
||||||
|
w->writeC(song.waveDutyIsVol);
|
||||||
|
for (int i=0; i<15; i++) {
|
||||||
w->writeC(0);
|
w->writeC(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,8 +11,8 @@
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <queue>
|
#include <queue>
|
||||||
|
|
||||||
#define DIV_VERSION "0.5.4"
|
#define DIV_VERSION "0.5.5pre1"
|
||||||
#define DIV_ENGINE_VERSION 42
|
#define DIV_ENGINE_VERSION 43
|
||||||
|
|
||||||
enum DivStatusView {
|
enum DivStatusView {
|
||||||
DIV_STATUS_NOTHING=0,
|
DIV_STATUS_NOTHING=0,
|
||||||
|
|
|
@ -144,6 +144,10 @@ void DivPlatformGB::tick() {
|
||||||
DivInstrument* ins=parent->getIns(chan[i].ins);
|
DivInstrument* ins=parent->getIns(chan[i].ins);
|
||||||
if (i!=2) {
|
if (i!=2) {
|
||||||
rWrite(16+i*5+1,((chan[i].duty&3)<<6)|(63-(ins->gb.soundLen&63)));
|
rWrite(16+i*5+1,((chan[i].duty&3)<<6)|(63-(ins->gb.soundLen&63)));
|
||||||
|
} else {
|
||||||
|
if (parent->song.waveDutyIsVol) {
|
||||||
|
rWrite(16+i*5+2,gbVolMap[(chan[i].std.duty&3)<<2]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (chan[i].std.hadWave) {
|
if (chan[i].std.hadWave) {
|
||||||
|
@ -164,7 +168,10 @@ void DivPlatformGB::tick() {
|
||||||
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
|
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
|
||||||
DivInstrument* ins=parent->getIns(chan[i].ins);
|
DivInstrument* ins=parent->getIns(chan[i].ins);
|
||||||
if (i==3) { // noise
|
if (i==3) { // noise
|
||||||
chan[i].freq=noiseTable[chan[i].baseFreq];
|
int ntPos=chan[i].baseFreq;
|
||||||
|
if (ntPos<0) ntPos=0;
|
||||||
|
if (ntPos>255) ntPos=255;
|
||||||
|
chan[i].freq=noiseTable[ntPos];
|
||||||
} else {
|
} else {
|
||||||
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true);
|
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true);
|
||||||
if (chan[i].freq>2047) chan[i].freq=2047;
|
if (chan[i].freq>2047) chan[i].freq=2047;
|
||||||
|
|
|
@ -73,7 +73,7 @@ void DivPlatformNES::acquire(short* bufL, short* bufR, size_t start, size_t len)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static unsigned char noiseTable[256]={
|
static unsigned char noiseTable[253]={
|
||||||
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 4,
|
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 4,
|
||||||
15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4,
|
15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4,
|
||||||
15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4,
|
15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4,
|
||||||
|
@ -155,7 +155,10 @@ void DivPlatformNES::tick() {
|
||||||
}
|
}
|
||||||
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
|
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
|
||||||
if (i==3) { // noise
|
if (i==3) { // noise
|
||||||
chan[i].freq=noiseTable[chan[i].baseFreq];
|
int ntPos=chan[i].baseFreq;
|
||||||
|
if (ntPos<0) ntPos=0;
|
||||||
|
if (ntPos>252) ntPos=252;
|
||||||
|
chan[i].freq=(parent->song.properNoiseLayout)?(15-(chan[i].baseFreq&15)):(noiseTable[ntPos]);
|
||||||
} else {
|
} else {
|
||||||
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true);
|
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true);
|
||||||
if (chan[i].freq>2047) chan[i].freq=2047;
|
if (chan[i].freq>2047) chan[i].freq=2047;
|
||||||
|
|
|
@ -129,17 +129,23 @@ void DivPlatformPCE::tick() {
|
||||||
if (chan[i].std.arpMode) {
|
if (chan[i].std.arpMode) {
|
||||||
chan[i].baseFreq=NOTE_PERIODIC(chan[i].std.arp);
|
chan[i].baseFreq=NOTE_PERIODIC(chan[i].std.arp);
|
||||||
// noise
|
// noise
|
||||||
chWrite(i,0x07,chan[i].noise?(0x80|noiseFreq[(chan[i].std.arp)%12]):0);
|
int noiseSeek=chan[i].std.arp;
|
||||||
|
if (noiseSeek<0) noiseSeek=0;
|
||||||
|
chWrite(i,0x07,chan[i].noise?(0x80|(parent->song.properNoiseLayout?(noiseSeek&31):noiseFreq[noiseSeek%12])):0);
|
||||||
} else {
|
} else {
|
||||||
chan[i].baseFreq=NOTE_PERIODIC(chan[i].note+chan[i].std.arp);
|
chan[i].baseFreq=NOTE_PERIODIC(chan[i].note+chan[i].std.arp);
|
||||||
chWrite(i,0x07,chan[i].noise?(0x80|noiseFreq[(chan[i].note+chan[i].std.arp)%12]):0);
|
int noiseSeek=chan[i].note+chan[i].std.arp;
|
||||||
|
if (noiseSeek<0) noiseSeek=0;
|
||||||
|
chWrite(i,0x07,chan[i].noise?(0x80|(parent->song.properNoiseLayout?(noiseSeek&31):noiseFreq[noiseSeek%12])):0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
chan[i].freqChanged=true;
|
chan[i].freqChanged=true;
|
||||||
} else {
|
} else {
|
||||||
if (chan[i].std.arpMode && chan[i].std.finishedArp) {
|
if (chan[i].std.arpMode && chan[i].std.finishedArp) {
|
||||||
chan[i].baseFreq=NOTE_PERIODIC(chan[i].note);
|
chan[i].baseFreq=NOTE_PERIODIC(chan[i].note);
|
||||||
chWrite(i,0x07,chan[i].noise?(0x80|noiseFreq[chan[i].note%12]):0);
|
int noiseSeek=chan[i].note;
|
||||||
|
if (noiseSeek<0) noiseSeek=0;
|
||||||
|
chWrite(i,0x07,chan[i].noise?(0x80|(parent->song.properNoiseLayout?(noiseSeek&31):noiseFreq[noiseSeek%12])):0);
|
||||||
chan[i].freqChanged=true;
|
chan[i].freqChanged=true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -248,7 +254,9 @@ int DivPlatformPCE::dispatch(DivCommand c) {
|
||||||
chan[c.chan].baseFreq=NOTE_PERIODIC(c.value);
|
chan[c.chan].baseFreq=NOTE_PERIODIC(c.value);
|
||||||
chan[c.chan].freqChanged=true;
|
chan[c.chan].freqChanged=true;
|
||||||
chan[c.chan].note=c.value;
|
chan[c.chan].note=c.value;
|
||||||
chWrite(c.chan,0x07,chan[c.chan].noise?(0x80|noiseFreq[chan[c.chan].note%12]):0);
|
int noiseSeek=chan[c.chan].note;
|
||||||
|
if (noiseSeek<0) noiseSeek=0;
|
||||||
|
chWrite(c.chan,0x07,chan[c.chan].noise?(0x80|(parent->song.properNoiseLayout?(noiseSeek&31):noiseFreq[noiseSeek%12])):0);
|
||||||
}
|
}
|
||||||
chan[c.chan].active=true;
|
chan[c.chan].active=true;
|
||||||
chan[c.chan].keyOn=true;
|
chan[c.chan].keyOn=true;
|
||||||
|
|
|
@ -215,6 +215,8 @@ struct DivSong {
|
||||||
// 1: fake reset on loop
|
// 1: fake reset on loop
|
||||||
// 2: don't do anything on loop
|
// 2: don't do anything on loop
|
||||||
unsigned char loopModality;
|
unsigned char loopModality;
|
||||||
|
bool properNoiseLayout;
|
||||||
|
bool waveDutyIsVol;
|
||||||
|
|
||||||
DivOrders orders;
|
DivOrders orders;
|
||||||
std::vector<DivInstrument*> ins;
|
std::vector<DivInstrument*> ins;
|
||||||
|
@ -263,7 +265,9 @@ struct DivSong {
|
||||||
tuning(440.0f),
|
tuning(440.0f),
|
||||||
limitSlides(false),
|
limitSlides(false),
|
||||||
linearPitch(true),
|
linearPitch(true),
|
||||||
loopModality(0) {
|
loopModality(0),
|
||||||
|
properNoiseLayout(false),
|
||||||
|
waveDutyIsVol(false) {
|
||||||
for (int i=0; i<32; i++) {
|
for (int i=0; i<32; i++) {
|
||||||
system[i]=DIV_SYSTEM_NULL;
|
system[i]=DIV_SYSTEM_NULL;
|
||||||
systemVol[i]=64;
|
systemVol[i]=64;
|
||||||
|
|
|
@ -3845,17 +3845,40 @@ void FurnaceGUI::drawCompatFlags() {
|
||||||
if (ImGui::Begin("Compatibility Flags",&compatFlagsOpen)) {
|
if (ImGui::Begin("Compatibility Flags",&compatFlagsOpen)) {
|
||||||
ImGui::TextWrapped("these flags are stored in the song when saving in .fur format, and are automatically enabled when saving in .dmf format.");
|
ImGui::TextWrapped("these flags are stored in the song when saving in .fur format, and are automatically enabled when saving in .dmf format.");
|
||||||
ImGui::Checkbox("Limit slide range",&e->song.limitSlides);
|
ImGui::Checkbox("Limit slide range",&e->song.limitSlides);
|
||||||
|
if (ImGui::IsItemHovered()) {
|
||||||
|
ImGui::SetTooltip("when enabled, slides are limited to a compatible range.\nmay cause problems with slides in negative octaves.");
|
||||||
|
}
|
||||||
ImGui::Checkbox("Linear pitch control",&e->song.linearPitch);
|
ImGui::Checkbox("Linear pitch control",&e->song.linearPitch);
|
||||||
|
if (ImGui::IsItemHovered()) {
|
||||||
|
ImGui::SetTooltip("linear pitch:\n- slides work in frequency/period space\n- E5xx and 04xx effects work in tonality space\nnon-linear pitch:\n- slides work in frequency/period space\n- E5xx and 04xx effects work on frequency/period space");
|
||||||
|
}
|
||||||
|
ImGui::Checkbox("Proper noise layout on NES and PC Engine",&e->song.properNoiseLayout);
|
||||||
|
if (ImGui::IsItemHovered()) {
|
||||||
|
ImGui::SetTooltip("use a proper noise channel note mapping (0-15) instead of a rather unusual compatible one.\nunlocks all noise frequencies on PC Engine.");
|
||||||
|
}
|
||||||
|
ImGui::Checkbox("Game Boy instrument duty is wave volume",&e->song.waveDutyIsVol);
|
||||||
|
if (ImGui::IsItemHovered()) {
|
||||||
|
ImGui::SetTooltip("if enabled, an instrument with duty macro in the wave channel will be mapped to wavetable volume.");
|
||||||
|
}
|
||||||
ImGui::Text("Loop modality:");
|
ImGui::Text("Loop modality:");
|
||||||
if (ImGui::RadioButton("Reset channels",e->song.loopModality==0)) {
|
if (ImGui::RadioButton("Reset channels",e->song.loopModality==0)) {
|
||||||
e->song.loopModality=0;
|
e->song.loopModality=0;
|
||||||
}
|
}
|
||||||
|
if (ImGui::IsItemHovered()) {
|
||||||
|
ImGui::SetTooltip("select to reset channels on loop. may trigger a voltage click on every loop!");
|
||||||
|
}
|
||||||
if (ImGui::RadioButton("Soft reset channels",e->song.loopModality==1)) {
|
if (ImGui::RadioButton("Soft reset channels",e->song.loopModality==1)) {
|
||||||
e->song.loopModality=1;
|
e->song.loopModality=1;
|
||||||
}
|
}
|
||||||
|
if (ImGui::IsItemHovered()) {
|
||||||
|
ImGui::SetTooltip("select to turn channels off on loop.");
|
||||||
|
}
|
||||||
if (ImGui::RadioButton("Do nothing",e->song.loopModality==2)) {
|
if (ImGui::RadioButton("Do nothing",e->song.loopModality==2)) {
|
||||||
e->song.loopModality=2;
|
e->song.loopModality=2;
|
||||||
}
|
}
|
||||||
|
if (ImGui::IsItemHovered()) {
|
||||||
|
ImGui::SetTooltip("select to not reset channels on loop.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ImGui::End();
|
ImGui::End();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue