Merge branch 'master' of https://github.com/tildearrow/furnace into minmod

This commit is contained in:
Natt Akuma 2024-03-17 19:54:56 +07:00
commit 1b867b33ce
21 changed files with 3140 additions and 750 deletions

View file

@ -105,8 +105,14 @@ const char* DivEngine::getEffectDesc(unsigned char effect, int chan, bool notNul
return "E4xx: Set vibrato range";
case 0xe5:
return "E5xx: Set pitch (80: center)";
case 0xe6:
return "E6xy: Quick legato (x: time (0-7 up; 8-F down); y: semitones)";
case 0xe7:
return "E7xx: Macro release";
case 0xe8:
return "E8xy: Quick legato up (x: time; y: semitones)";
case 0xe9:
return "E9xy: Quick legato down (x: time; y: semitones)";
case 0xea:
return "EAxx: Legato";
case 0xeb:

View file

@ -100,7 +100,7 @@ enum DivMIDIModes {
struct DivChannelState {
std::vector<DivDelayedCommand> delayed;
int note, oldNote, lastIns, pitch, portaSpeed, portaNote;
int volume, volSpeed, cut, rowDelay, volMax;
int volume, volSpeed, cut, legatoDelay, legatoTarget, rowDelay, volMax;
int delayOrder, delayRow, retrigSpeed, retrigTick;
int vibratoDepth, vibratoRate, vibratoPos, vibratoPosGiant, vibratoDir, vibratoFine;
int tremoloDepth, tremoloRate, tremoloPos;
@ -123,6 +123,8 @@ struct DivChannelState {
volume(0x7f00),
volSpeed(0),
cut(-1),
legatoDelay(-1),
legatoTarget(0),
rowDelay(0),
volMax(0),
delayOrder(0),
@ -547,7 +549,7 @@ class DivEngine {
bool loadFur(unsigned char* file, size_t len, int variantID=0);
bool loadMod(unsigned char* file, size_t len);
bool loadS3M(unsigned char* file, size_t len);
bool loadFTM(unsigned char* file, size_t len);
bool loadFTM(unsigned char* file, size_t len, bool dnft, bool dnftSig, bool eft);
bool loadFC(unsigned char* file, size_t len);
void loadDMP(SafeReader& reader, std::vector<DivInstrument*>& ret, String& stripPath);
@ -632,7 +634,7 @@ class DivEngine {
void createNew(const char* description, String sysName, bool inBase64=true);
void createNewFromDefaults();
// load a file.
bool load(unsigned char* f, size_t length);
bool load(unsigned char* f, size_t length, const char* nameHint=NULL);
// play a binary command stream.
bool playStream(unsigned char* f, size_t length);
// get the playing stream.

View file

@ -19,10 +19,10 @@
#include "fileOpsCommon.h"
bool DivEngine::load(unsigned char* f, size_t slen) {
bool DivEngine::load(unsigned char* f, size_t slen, const char* nameHint) {
unsigned char* file;
size_t len;
if (slen<18) {
if (slen<21) {
logE("too small!");
lastError="file is too small";
delete[] f;
@ -31,6 +31,21 @@ bool DivEngine::load(unsigned char* f, size_t slen) {
if (!systemsRegistered) registerSystems();
// step 0: get extension of file
String extS;
if (nameHint!=NULL) {
const char* ext=strrchr(nameHint,'.');
if (ext!=NULL) {
for (; *ext; ext++) {
char i=*ext;
if (i>='A' && i<='Z') {
i+='a'-'A';
}
extS+=i;
}
}
}
// step 1: try loading as a zlib-compressed file
logD("trying zlib...");
try {
@ -128,7 +143,9 @@ bool DivEngine::load(unsigned char* f, size_t slen) {
if (memcmp(file,DIV_DMF_MAGIC,16)==0) {
return loadDMF(file,len);
} else if (memcmp(file,DIV_FTM_MAGIC,18)==0) {
return loadFTM(file,len);
return loadFTM(file,len,(extS==".dnm"),false,(extS==".eft"));
} else if (memcmp(file,DIV_DNM_MAGIC,21)==0) {
return loadFTM(file,len,true,true,false);
} else if (memcmp(file,DIV_FUR_MAGIC,16)==0) {
return loadFur(file,len);
} else if (memcmp(file,DIV_FUR_MAGIC_DS0,16)==0) {

View file

@ -49,6 +49,7 @@ struct NotZlibException {
#define DIV_DMF_MAGIC ".DelekDefleMask."
#define DIV_FUR_MAGIC "-Furnace module-"
#define DIV_FTM_MAGIC "FamiTracker Module"
#define DIV_DNM_MAGIC "Dn-FamiTracker Module"
#define DIV_FC13_MAGIC "SMOD"
#define DIV_FC14_MAGIC "FC14"
#define DIV_S3M_MAGIC "SCRM"

File diff suppressed because it is too large Load diff

View file

@ -259,6 +259,40 @@ bool DivInstrumentPowerNoise::operator==(const DivInstrumentPowerNoise& other) {
#undef _C
#define CONSIDER(x,t) \
case t: \
return &x; \
break;
DivInstrumentMacro* DivInstrumentSTD::macroByType(DivMacroType type) {
switch (type) {
CONSIDER(volMacro,DIV_MACRO_VOL)
CONSIDER(arpMacro,DIV_MACRO_ARP)
CONSIDER(dutyMacro,DIV_MACRO_DUTY)
CONSIDER(waveMacro,DIV_MACRO_WAVE)
CONSIDER(pitchMacro,DIV_MACRO_PITCH)
CONSIDER(ex1Macro,DIV_MACRO_EX1)
CONSIDER(ex2Macro,DIV_MACRO_EX2)
CONSIDER(ex3Macro,DIV_MACRO_EX3)
CONSIDER(algMacro,DIV_MACRO_ALG)
CONSIDER(fbMacro,DIV_MACRO_FB)
CONSIDER(fmsMacro,DIV_MACRO_FMS)
CONSIDER(amsMacro,DIV_MACRO_AMS)
CONSIDER(panLMacro,DIV_MACRO_PAN_LEFT)
CONSIDER(panRMacro,DIV_MACRO_PAN_RIGHT)
CONSIDER(phaseResetMacro,DIV_MACRO_PHASE_RESET)
CONSIDER(ex4Macro,DIV_MACRO_EX4)
CONSIDER(ex5Macro,DIV_MACRO_EX5)
CONSIDER(ex6Macro,DIV_MACRO_EX6)
CONSIDER(ex7Macro,DIV_MACRO_EX7)
CONSIDER(ex8Macro,DIV_MACRO_EX8)
}
return NULL;
}
#undef CONSIDER
#define FEATURE_BEGIN(x) \
w->write(x,2); \
size_t featStartSeek=w->tell(); \

View file

@ -332,6 +332,9 @@ struct DivInstrumentSTD {
damMacro(DIV_MACRO_OP_DAM), dvbMacro(DIV_MACRO_OP_DVB), egtMacro(DIV_MACRO_OP_EGT), kslMacro(DIV_MACRO_OP_KSL),
susMacro(DIV_MACRO_OP_SUS), vibMacro(DIV_MACRO_OP_VIB), wsMacro(DIV_MACRO_OP_WS), ksrMacro(DIV_MACRO_OP_KSR) {}
} opMacros[4];
DivInstrumentMacro* macroByType(DivMacroType type);
DivInstrumentSTD():
volMacro(DIV_MACRO_VOL,true),
arpMacro(DIV_MACRO_ARP),

View file

@ -400,6 +400,7 @@ int DivPlatformN163::dispatch(DivCommand c) {
}
case DIV_CMD_WAVE:
chan[c.chan].wave=c.value;
chan[c.chan].ws.changeWave1(chan[c.chan].wave);
if (chan[c.chan].waveMode) {
chan[c.chan].waveUpdated=true;
}

View file

@ -959,6 +959,21 @@ void DivEngine::processRow(int i, bool afterDelay) {
dispatchCmd(DivCommand(DIV_CMD_PITCH,i,chan[i].pitch+(((chan[i].vibratoDepth*vibTable[chan[i].vibratoPos]*chan[i].vibratoFine)>>4)/15)));
dispatchCmd(DivCommand(DIV_CMD_HINT_PITCH,i,chan[i].pitch));
break;
case 0xe6: // Delayed legato
// why does this have to follow FamiTracker verbatim
// couldn't you do better?
if ((effectVal&15)!=0) {
chan[i].legatoDelay=(((effectVal&0xf0)>>4)&7)+1;
if (effectVal&128) {
chan[i].legatoTarget=-(effectVal&15);
} else {
chan[i].legatoTarget=(effectVal&15);
}
} else {
chan[i].legatoDelay=-1;
chan[i].legatoTarget=0;
}
break;
case 0xe7: // delayed macro release
// "Bruh"
if (effectVal>0 && (song.delayBehavior==2 || effectVal<nextSpeed)) {
@ -966,6 +981,25 @@ void DivEngine::processRow(int i, bool afterDelay) {
chan[i].cutType=2;
}
break;
case 0xe8: // delayed legato up
// see? you COULD do better!
if ((effectVal&15)!=0) {
chan[i].legatoDelay=((effectVal&0xf0)>>4)+1;
chan[i].legatoTarget=(effectVal&15);
} else {
chan[i].legatoDelay=-1;
chan[i].legatoTarget=0;
}
break;
case 0xe9: // delayed legato down
if ((effectVal&15)!=0) {
chan[i].legatoDelay=((effectVal&0xf0)>>4)+1;
chan[i].legatoTarget=-(effectVal&15);
} else {
chan[i].legatoDelay=-1;
chan[i].legatoTarget=0;
}
break;
case 0xea: // legato mode
chan[i].legato=effectVal;
break;
@ -1064,7 +1098,7 @@ void DivEngine::processRow(int i, bool afterDelay) {
break;
case 0xfc: // delayed note release
if (effectVal>0 && (song.delayBehavior==2 || effectVal<nextSpeed)) {
if (song.delayBehavior==2 || effectVal<nextSpeed) {
chan[i].cut=effectVal+1;
chan[i].cutType=1;
}
@ -1542,6 +1576,15 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) {
break;
}
}
if (chan[i].legatoDelay>0) {
if (--chan[i].legatoDelay<1) {
chan[i].note+=chan[i].legatoTarget;
dispatchCmd(DivCommand(DIV_CMD_LEGATO,i,chan[i].note));
dispatchCmd(DivCommand(DIV_CMD_HINT_LEGATO,i,chan[i].note));
chan[i].legatoDelay=-1;
chan[i].legatoTarget=0;
}
}
if (!song.noSlidesOnFirstTick || !firstTick) {
if ((chan[i].keyOn || chan[i].keyOff) && chan[i].portaSpeed>0) {
if (dispatchCmd(DivCommand(DIV_CMD_NOTE_PORTA,i,chan[i].portaSpeed*(song.linearPitch==2?song.pitchSlideSpeed:1),chan[i].portaNote))==2 && chan[i].portaStop && song.targetResetsSlides) {