From 0660e25f06a412aeae530c0c7b436182362f1e84 Mon Sep 17 00:00:00 2001 From: KungFuFurby Date: Tue, 9 Sep 2025 20:31:22 -0400 Subject: [PATCH] Add vibrato waveform import support for S3M, XM, MOD and IT Fine print: - OpenMPT hacks are not implemented here for MOD and XM for random waveform - Retrigger/Continuous setting is not handled for MOD, XM and S3M -- For S3M, I can't confirm if the setting even existed: I'm pretty sure it got cut out of Impulse Tracker. - Your waveform may sound different here than in the original player If tremolo and panbrello waveforms are implemented in Furnace, then their corresponding conversions can be implemented. For now, though, they can't make the cut since there is no corresponding effect to change those waveforms from the default setting. --- src/engine/fileOps/it.cpp | 20 ++++++++++++++++++++ src/engine/fileOps/mod.cpp | 14 ++++++++++++++ src/engine/fileOps/s3m.cpp | 20 ++++++++++++++++++++ src/engine/fileOps/xm.cpp | 17 +++++++++++++++++ 4 files changed, 71 insertions(+) diff --git a/src/engine/fileOps/it.cpp b/src/engine/fileOps/it.cpp index facbecaf9..5677dc0cc 100644 --- a/src/engine/fileOps/it.cpp +++ b/src/engine/fileOps/it.cpp @@ -1516,6 +1516,26 @@ bool DivEngine::loadIT(unsigned char* file, size_t len) { break; case 'S': // special... switch (effectVal[chan]>>4) { + case 0x3: // vibrato waveform + switch (effectVal[chan]&3) { + case 0x0: // sine + p->data[readRow][effectCol[chan]++]=0xe3; + p->data[readRow][effectCol[chan]++]=0x00; + break; + case 0x1: // ramp down + p->data[readRow][effectCol[chan]++]=0xe3; + p->data[readRow][effectCol[chan]++]=0x05; + break; + case 0x2: // square + p->data[readRow][effectCol[chan]++]=0xe3; + p->data[readRow][effectCol[chan]++]=0x06; + break; + case 0x3: // random + p->data[readRow][effectCol[chan]++]=0xe3; + p->data[readRow][effectCol[chan]++]=0x07; + break; + } + break; case 0x8: // panning p->data[readRow][effectCol[chan]++]=0x80; p->data[readRow][effectCol[chan]++]=(effectVal[chan]&15)<<4; diff --git a/src/engine/fileOps/mod.cpp b/src/engine/fileOps/mod.cpp index 353060bff..44df0a35e 100644 --- a/src/engine/fileOps/mod.cpp +++ b/src/engine/fileOps/mod.cpp @@ -356,6 +356,20 @@ bool DivEngine::loadMod(unsigned char* file, size_t len) { case 2: // single note slide down writeFxCol(fxTyp-1+0xf1,fxVal); break; + case 0x3: // vibrato waveform + switch (fxVal&3) { + case 0x0: // sine + writeFxCol(0xe3,0x00); + break; + case 0x1: // ramp down + writeFxCol(0xe3,0x05); + break; + case 0x2: // square + case 0x3: + writeFxCol(0xe3,0x06); + break; + } + break; case 9: // retrigger writeFxCol(0x0c,fxVal); break; diff --git a/src/engine/fileOps/s3m.cpp b/src/engine/fileOps/s3m.cpp index aacfc4fdf..2aaf6f757 100644 --- a/src/engine/fileOps/s3m.cpp +++ b/src/engine/fileOps/s3m.cpp @@ -1101,6 +1101,26 @@ bool DivEngine::loadS3M(unsigned char* file, size_t len) { break; case 'S': // special... switch (effectVal>>4) { + case 0x3: // vibrato waveform + switch (effectVal&3) { + case 0x0: // sine + p->data[readRow][effectCol[chan]++]=0xe3; + p->data[readRow][effectCol[chan]++]=0x00; + break; + case 0x1: // ramp down + p->data[readRow][effectCol[chan]++]=0xe3; + p->data[readRow][effectCol[chan]++]=0x05; + break; + case 0x2: // square + p->data[readRow][effectCol[chan]++]=0xe3; + p->data[readRow][effectCol[chan]++]=0x06; + break; + case 0x3: // random + p->data[readRow][effectCol[chan]++]=0xe3; + p->data[readRow][effectCol[chan]++]=0x07; + break; + } + break; case 0x8: // panning p->data[readRow][effectCol[chan]++]=0x80; p->data[readRow][effectCol[chan]++]=(effectVal&15)<<4; diff --git a/src/engine/fileOps/xm.cpp b/src/engine/fileOps/xm.cpp index f78973ee8..3e533eb11 100644 --- a/src/engine/fileOps/xm.cpp +++ b/src/engine/fileOps/xm.cpp @@ -1147,6 +1147,23 @@ bool DivEngine::loadXM(unsigned char* file, size_t len) { case 0xe: // special... // TODO: implement the rest switch (effectVal>>4) { + case 0x3: // vibrato waveform + switch (effectVal&3) { + case 0x0: // sine + p->data[j][effectCol[k]++]=0xe3; + p->data[j][effectCol[k]++]=0x00; + break; + case 0x1: // ramp down + p->data[j][effectCol[k]++]=0xe3; + p->data[j][effectCol[k]++]=0x05; + break; + case 0x2: // square + case 0x3: + p->data[j][effectCol[k]++]=0xe3; + p->data[j][effectCol[k]++]=0x06; + break; + } + break; case 0x5: // fine tune p->data[j][effectCol[k]++]=0xe5; p->data[j][effectCol[k]++]=(effectVal&15)<<4;