summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSolomon Peachy <pizza@shaftnet.org>2025-01-03 08:34:47 -0500
committerSolomon Peachy <pizza@shaftnet.org>2025-01-03 11:34:24 -0500
commitaf7ed73f311fcb0e4ef2e40d77369b3cc165671c (patch)
tree7870fe1d7c86cf4a5b8497fcde6f38be47580789
parent08c32cee7c8e460f949f268e011adc5bbec396e2 (diff)
downloadrockbox-af7ed73f31.tar.gz
rockbox-af7ed73f31.zip
FS#13539: Resync mikmod plugin with upstream
Brings it up to libmikmod 3.3.12, relased 2024-12-31 Also fix a segfault that only happened on simulators when using non-default samplerates. Change-Id: I2ade2d72a00edab5395328fe76a88a88516aac72
-rw-r--r--apps/plugins/mikmod/README.rockbox12
-rw-r--r--apps/plugins/mikmod/load_669.c7
-rw-r--r--apps/plugins/mikmod/load_amf.c164
-rw-r--r--apps/plugins/mikmod/load_asy.c42
-rw-r--r--apps/plugins/mikmod/load_far.c103
-rw-r--r--apps/plugins/mikmod/load_gdm.c46
-rw-r--r--apps/plugins/mikmod/load_it.c37
-rw-r--r--apps/plugins/mikmod/load_med.c326
-rw-r--r--apps/plugins/mikmod/load_mod.c102
-rw-r--r--apps/plugins/mikmod/load_s3m.c1
-rw-r--r--apps/plugins/mikmod/load_stm.c75
-rw-r--r--apps/plugins/mikmod/load_ult.c29
-rw-r--r--apps/plugins/mikmod/load_umx.c95
-rw-r--r--apps/plugins/mikmod/load_uni.c34
-rw-r--r--apps/plugins/mikmod/load_xm.c34
-rw-r--r--apps/plugins/mikmod/mdriver.c5
-rw-r--r--apps/plugins/mikmod/mikmod.c5
-rw-r--r--apps/plugins/mikmod/mikmod.h30
-rw-r--r--apps/plugins/mikmod/mikmod_internals.h59
-rw-r--r--apps/plugins/mikmod/mloader.c23
-rw-r--r--apps/plugins/mikmod/mlutil.c31
-rw-r--r--apps/plugins/mikmod/mmalloc.c13
-rw-r--r--apps/plugins/mikmod/mmerror.c12
-rw-r--r--apps/plugins/mikmod/mplayer.c1189
-rw-r--r--apps/plugins/mikmod/munitrk.c23
-rw-r--r--apps/plugins/mikmod/sloader.c28
-rw-r--r--apps/plugins/mikmod/virtch.c20
-rw-r--r--apps/plugins/mikmod/virtch2.c15
-rw-r--r--apps/plugins/mikmod/virtch_common.c4
29 files changed, 1859 insertions, 705 deletions
diff --git a/apps/plugins/mikmod/README.rockbox b/apps/plugins/mikmod/README.rockbox
new file mode 100644
index 0000000000..22f17b7b41
--- /dev/null
+++ b/apps/plugins/mikmod/README.rockbox
@@ -0,0 +1,12 @@
+mikmod (https://mikmod.sourceforge.net/)
+
+Based on upstream commit 72424e1bebab470fbf8a2914dcd7903b742205e6
+corresponding to mikmod 3.2.9 / libmikmod 3.3.12
+
+Heavily customized for rockbox use:
+
+ * converted most BOOL/INTs in function prototypes to 'int'
+ * converted BOOL/INT in option enums to RB_BOOL / RB_INT
+ * Silenced a _ton_ of "unused parameter" options
+ * Custom player frontend that is fully i18n'd
+ * Settings for mixer freq, quality, and other parameters
diff --git a/apps/plugins/mikmod/load_669.c b/apps/plugins/mikmod/load_669.c
index 0872043f65..c393434648 100644
--- a/apps/plugins/mikmod/load_669.c
+++ b/apps/plugins/mikmod/load_669.c
@@ -20,8 +20,6 @@
/*==============================================================================
- $Id: $
-
Composer 669 module loader
==============================================================================*/
@@ -321,7 +319,8 @@ static int S69_Load(int curious)
sample.length=_mm_read_I_SLONG(modreader);
sample.loopbeg=_mm_read_I_SLONG(modreader);
sample.loopend=_mm_read_I_SLONG(modreader);
- if (sample.loopend==0xfffff) sample.loopend=0;
+ /* Note: 'Lost in Germany' has 0xf0ffff as marker */
+ if (sample.loopend>=0xfffff) sample.loopend=0;
if((sample.length<0)||(sample.loopbeg<-1)||(sample.loopend<-1)) {
_mm_errno = MMERR_LOADING_HEADER;
@@ -330,7 +329,7 @@ static int S69_Load(int curious)
current->samplename=DupStr(sample.filename,13,1);
current->seekpos=0;
- current->speed=0;
+ current->speed=128; /* Used as finetune when UF_XMPERIODS is enabled; 128 is centered. */
current->length=sample.length;
current->loopstart=sample.loopbeg;
current->loopend=sample.loopend;
diff --git a/apps/plugins/mikmod/load_amf.c b/apps/plugins/mikmod/load_amf.c
index ba1f59a5be..7d25430aac 100644
--- a/apps/plugins/mikmod/load_amf.c
+++ b/apps/plugins/mikmod/load_amf.c
@@ -94,7 +94,7 @@ static int AMF_Test(void)
if(memcmp(id,"AMF",3)) return 0;
ver=_mm_read_UBYTE(modreader);
- if((ver>=10)&&(ver<=14)) return 1;
+ if((ver>=8)&&(ver<=14)) return 1;
return 0;
}
@@ -114,7 +114,48 @@ static void AMF_Cleanup(void)
track=NULL;
}
-static int AMF_UnpackTrack(MREADER* r)
+/* Some older version 1.0 AMFs contain an anomaly where the sample length is
+ * a DWORD, the loop start is a WORD, and the loop end is missing. Later AMF
+ * 1.0 modules and up contain all three as DWORDs, and earlier versions have
+ * all three as WORDs. This function tries to detect this edge case in the
+ * instruments table. This should only be called on 1.0 AMFs.
+ */
+static int AMF_ScanV10Instruments(MREADER *r, unsigned int numins)
+{
+ SLONG resetpos;
+ int res = 0;
+ char str[32];
+ ULONG idx, len, start, end;
+ UBYTE type, vol;
+ UWORD c2spd;
+ unsigned int i;
+
+ resetpos = _mm_ftell(r);
+ if(resetpos < 0) return 0;
+
+ for(i = 0; i < numins; i++) {
+ type = _mm_read_UBYTE(r); /* type: should be 0 or 1 */
+ _mm_read_string(str, 32, r); /* name */
+ _mm_read_string(str, 13, r); /* filename */
+ idx = _mm_read_I_ULONG(r); /* index (should be <= numins) */
+ len = _mm_read_I_ULONG(r);
+ c2spd = _mm_read_I_UWORD(r); /* should be > 0 */
+ vol = _mm_read_UBYTE(r); /* should be [0,0x40] */
+ start = _mm_read_I_ULONG(r); /* should be <= len */
+ end = _mm_read_I_ULONG(r); /* should be <= len */
+
+ if((type != 0 && type != 1) || (idx > numins) || (c2spd == 0) ||
+ (vol > 0x40) || (start > len) || (end > len)) {
+ res = 1;
+ break;
+ }
+
+ }
+ _mm_fseek(r, resetpos, SEEK_SET);
+ return res;
+}
+
+static int AMF_UnpackTrack(MREADER *r)
{
ULONG tracksize;
UBYTE row,cmd;
@@ -126,7 +167,12 @@ static int AMF_UnpackTrack(MREADER* r)
/* read packed track */
if (r) {
tracksize=_mm_read_I_UWORD(r);
- tracksize+=((ULONG)_mm_read_UBYTE(r))<<16;
+
+ /* The original code in DSMI library read the byte,
+ but it is not used, so we won't either */
+// tracksize+=((ULONG)_mm_read_UBYTE(r))<<16;
+ (void)_mm_read_UBYTE(r);
+
if (tracksize)
while(tracksize--) {
row=_mm_read_UBYTE(r);
@@ -144,18 +190,23 @@ static int AMF_UnpackTrack(MREADER* r)
}
/* invalid row (probably unexpected end of row) */
- if (row>=64)
- return 0;
+ if (row>=64) {
+ _mm_fseek(modreader, tracksize * 3, SEEK_CUR);
+ return 1;
+ }
if (cmd<0x7f) {
/* note, vol */
+ /* Note that 0xff values mean this note was not originally
+ accomanied by a volume event. The +1 here causes it to
+ overflow to 0, which will then (correctly) be ignored later. */
track[row].note=cmd;
track[row].volume=(UBYTE)arg+1;
} else
if (cmd==0x7f) {
- /* duplicate row */
- if ((arg<0)&&(row+arg>=0)) {
- memcpy(track+row,track+(row+arg),sizeof(AMFNOTE));
- }
+ /* AMF.TXT claims this should duplicate the previous row, but
+ this is a lie. This note value is used to communicate to
+ DSMI that the current playing note should be updated when
+ an instrument is used with no note. This can be ignored. */
} else
if (cmd==0x80) {
/* instr */
@@ -173,8 +224,11 @@ static int AMF_UnpackTrack(MREADER* r)
} else
if(track[row].fxcnt<3) {
/* effect, param */
- if(cmd>0x97)
- return 0;
+ if(cmd>0x97) {
+ /* Instead of failing, we just ignore unknown effects.
+ This will load the "escape from dulce base" module */
+ continue;
+ }
track[row].effect[track[row].fxcnt]=cmd&0x7f;
track[row].parameter[track[row].fxcnt]=arg;
track[row].fxcnt++;
@@ -185,7 +239,7 @@ static int AMF_UnpackTrack(MREADER* r)
return 1;
}
-static UBYTE* AMF_ConvertTrack(void)
+static UBYTE *AMF_ConvertTrack(void)
{
int row,fx4memory=0;
@@ -315,6 +369,7 @@ static UBYTE* AMF_ConvertTrack(void)
UniEffect(fx4memory,0);
break;
case 0x17: /* Panning */
+ /* S3M pan, except offset by -64. */
if (inf>64)
UniEffect(UNI_ITEFFECTS0,0x91); /* surround */
else
@@ -338,17 +393,29 @@ static int AMF_Load(int curious)
SAMPLE *q;
UWORD *track_remap;
ULONG samplepos, fileend;
- int channel_remap[16];
+ UBYTE channel_remap[16];
+ int no_loopend;
(void)curious;
-
/* try to read module header */
_mm_read_UBYTES(mh->id,3,modreader);
mh->version =_mm_read_UBYTE(modreader);
+
+ /* For version 8, the song name is only 20 characters long and then come
+ // some data, which I do not know what is. The original code by Otto Chrons
+ // load the song name as 20 characters long and then it is overwritten again
+ // it another function, where it loads 32 characters, no matter which version
+ // it is. So we do the same here */
_mm_read_string(mh->songname,32,modreader);
+
mh->numsamples =_mm_read_UBYTE(modreader);
mh->numorders =_mm_read_UBYTE(modreader);
mh->numtracks =_mm_read_I_UWORD(modreader);
- mh->numchannels =_mm_read_UBYTE(modreader);
+
+ if(mh->version>=9)
+ mh->numchannels=_mm_read_UBYTE(modreader);
+ else
+ mh->numchannels=4;
+
if((!mh->numchannels)||(mh->numchannels>(mh->version>=12?32:16))) {
_mm_errno=MMERR_NOT_A_MODULE;
return 0;
@@ -357,7 +424,7 @@ static int AMF_Load(int curious)
if(mh->version>=11) {
memset(mh->panpos,0,32);
_mm_read_SBYTES(mh->panpos,(mh->version>=13)?32:16,modreader);
- } else
+ } else if(mh->version>=9)
_mm_read_UBYTES(channel_remap,16,modreader);
if (mh->version>=13) {
@@ -408,16 +475,22 @@ static int AMF_Load(int curious)
* UF_PANNING, to use our preferred panning table for this case.
*/
defaultpanning = 1;
- for (t = 0; t < 32; t++) {
- if (mh->panpos[t] > 64) {
- of.panning[t] = PAN_SURROUND;
- defaultpanning = 0;
- } else
- if (mh->panpos[t] == 64)
- of.panning[t] = PAN_RIGHT;
- else
- of.panning[t] = (mh->panpos[t] + 64) << 1;
+
+ if(mh->version>=11) {
+ for (t = 0; t < 32; t++) {
+ if (mh->panpos[t] > 64) {
+ of.panning[t] = PAN_SURROUND;
+ defaultpanning = 0;
+ } else
+ if (mh->panpos[t] == 64)
+ of.panning[t] = PAN_RIGHT;
+ else
+ of.panning[t] = (mh->panpos[t] + 64) << 1;
+ }
}
+ else
+ defaultpanning = 0;
+
if (defaultpanning) {
for (t = 0; t < of.numchn; t++)
if (of.panning[t] == (((t + 1) & 2) ? PAN_RIGHT : PAN_LEFT)) {
@@ -442,11 +515,13 @@ static int AMF_Load(int curious)
if (mh->version>=14)
/* track size */
of.pattrows[t]=_mm_read_I_UWORD(modreader);
- if (mh->version>=10)
- _mm_read_I_UWORDS(of.patterns+(t*of.numchn),of.numchn,modreader);
+ if ((mh->version==9) || (mh->version==10)) {
+ /* Only version 9 and 10 uses channel remap */
+ for (u = 0; u < of.numchn; u++)
+ of.patterns[t * of.numchn + channel_remap[u]] = _mm_read_I_UWORD(modreader);
+ }
else
- for(u=0;u<of.numchn;u++)
- of.patterns[t*of.numchn+channel_remap[u]]=_mm_read_I_UWORD(modreader);
+ _mm_read_I_UWORDS(of.patterns + (t * of.numchn), of.numchn, modreader);
}
if(_mm_eof(modreader)) {
_mm_errno = MMERR_LOADING_HEADER;
@@ -455,6 +530,12 @@ static int AMF_Load(int curious)
/* read sample information */
if(!AllocSamples()) return 0;
+
+ no_loopend = 0;
+ if(mh->version == 10) {
+ no_loopend = AMF_ScanV10Instruments(modreader, of.numins);
+ }
+
q=of.samples;
for(t=0;t<of.numins;t++) {
/* try to read sample info */
@@ -462,19 +543,38 @@ static int AMF_Load(int curious)
_mm_read_string(s.samplename,32,modreader);
_mm_read_string(s.filename,13,modreader);
s.offset =_mm_read_I_ULONG(modreader);
- s.length =_mm_read_I_ULONG(modreader);
+
+ if(mh->version>=10)
+ s.length =_mm_read_I_ULONG(modreader);
+ else
+ s.length = _mm_read_I_UWORD(modreader);
+
s.c2spd =_mm_read_I_UWORD(modreader);
if(s.c2spd==8368) s.c2spd=8363;
s.volume =_mm_read_UBYTE(modreader);
/* "the tribal zone.amf" and "the way its gonna b.amf" by Maelcum
* are the only version 10 files I can find, and they have 32 bit
* reppos and repend, not 16. */
- if(mh->version>=10) {/* was 11 */
+ if(mh->version>=10 && no_loopend==0) {/* was 11 */
s.reppos =_mm_read_I_ULONG(modreader);
s.repend =_mm_read_I_ULONG(modreader);
- } else {
+ } else if(mh->version==10) {
+ /* Early AMF 1.0 modules have the upper two bytes of
+ * the loop start and the entire loop end truncated.
+ * libxmp cites "sweetdrm.amf" and "facing_n.amf", but
+ * these are currently missing. M2AMF 1.3 (from DMP 2.32)
+ * has been confirmed to output these, however. */
s.reppos =_mm_read_I_UWORD(modreader);
s.repend =s.length;
+ /* There's not really a correct way to handle the loop
+ * end, but this makes unlooped samples work at least. */
+ if(s.reppos==0)
+ s.repend=0;
+ } else {
+ s.reppos =_mm_read_I_UWORD(modreader);
+ s.repend =_mm_read_I_UWORD(modreader);
+ if (s.repend==0xffff)
+ s.repend=0;
}
if(_mm_eof(modreader)) {
diff --git a/apps/plugins/mikmod/load_asy.c b/apps/plugins/mikmod/load_asy.c
index 48d746c8e8..edd6b6b416 100644
--- a/apps/plugins/mikmod/load_asy.c
+++ b/apps/plugins/mikmod/load_asy.c
@@ -58,8 +58,12 @@ typedef struct MSAMPINFO {
typedef struct MODULEHEADER {
CHAR songname[21];
+ UBYTE initspeed;
+ UBYTE inittempo;
+ UBYTE num_samples;
UBYTE num_patterns; /* number of patterns used */
UBYTE num_orders;
+ UBYTE reppos;
UBYTE positions[256]; /* which pattern to play at pos */
MSAMPINFO samples[64]; /* all sampleinfo */
} MODULEHEADER;
@@ -177,7 +181,7 @@ static int ConvertNote(MODNOTE *n)
if (instrument) {
/* if instrument does not exist, note cut */
- if ((instrument > 31) || (!mh->samples[instrument - 1].length)) {
+ if ((instrument > mh->num_samples) || (!mh->samples[instrument - 1].length)) {
UniPTEffect(0xc, 0);
if (effect == 0xc)
effect = effdat = 0;
@@ -194,7 +198,6 @@ static int ConvertNote(MODNOTE *n)
* played */
if (effect || effdat) {
UniInstrument(instrument - 1);
- note = lastnote;
} else
UniPTEffect(0xc,
mh->samples[instrument -
@@ -294,22 +297,33 @@ static int ASY_Load(int curious)
/* no title in asylum amf files :( */
mh->songname[0] = '\0';
- _mm_fseek(modreader, 0x23, SEEK_SET);
+ _mm_fseek(modreader, 0x20, SEEK_SET);
+ mh->initspeed = _mm_read_UBYTE(modreader);
+ mh->inittempo = _mm_read_UBYTE(modreader);
+ mh->num_samples = _mm_read_UBYTE(modreader);
mh->num_patterns = _mm_read_UBYTE(modreader);
mh->num_orders = _mm_read_UBYTE(modreader);
+ mh->reppos = _mm_read_UBYTE(modreader);
- /* skip unknown byte */
- _mm_skip_BYTE(modreader);
_mm_read_UBYTES(mh->positions, 256, modreader);
+ #ifdef MIKMOD_DEBUG
+ fprintf(stderr, "ASY: bpm=%d, spd=%d, numins=%d, numpat=%d\n",
+ mh->inittempo, mh->initspeed, mh->num_samples, mh->num_patterns);
+ #endif
+ if (!mh->initspeed || !mh->inittempo || mh->num_samples > 64) {
+ _mm_errno = MMERR_NOT_A_MODULE;
+ return 0;
+ }
+
/* read samples headers*/
- for (t = 0; t < 64; t++) {
+ for (t = 0; t < mh->num_samples; t++) {
s = &mh->samples[t];
_mm_fseek(modreader, 0x126 + (t*37), SEEK_SET);
_mm_read_string(s->samplename, 22, modreader);
- s->samplename[21] = 0; /* just in case */
+ s->samplename[22] = 0;
s->finetune = _mm_read_UBYTE(modreader);
s->volume = _mm_read_UBYTE(modreader);
@@ -324,14 +338,16 @@ static int ASY_Load(int curious)
return 0;
}
+ _mm_fseek(modreader, 37*(64-mh->num_samples), SEEK_CUR);
+
/* set module variables */
- of.initspeed = 6;
- of.inittempo = 125;
+ of.initspeed = mh->initspeed;
+ of.inittempo = mh->inittempo;
of.numchn = 8;
modtype = 0;
- of.songname = DupStr(mh->songname, 21, 1);
+ of.songname = MikMod_strdup("");
of.numpos = mh->num_orders;
- of.reppos = 0;
+ of.reppos = mh->reppos;
of.numpat = mh->num_patterns;
of.numtrk = of.numpat * of.numchn;
@@ -348,8 +364,8 @@ static int ASY_Load(int curious)
}
/* Finally, init the sampleinfo structures */
- of.numins = 31;
- of.numsmp = 31;
+ of.numins = mh->num_samples;
+ of.numsmp = mh->num_samples;
if (!AllocSamples())
return 0;
s = mh->samples;
diff --git a/apps/plugins/mikmod/load_far.c b/apps/plugins/mikmod/load_far.c
index 643cf15d49..44cd8ebfab 100644
--- a/apps/plugins/mikmod/load_far.c
+++ b/apps/plugins/mikmod/load_far.c
@@ -93,6 +93,7 @@ static FARHEADER2 *mh2 = NULL;
static FARNOTE *pat = NULL;
static const unsigned char FARSIG[4+3]={'F','A','R',0xfe,13,10,26};
+static const UWORD FAR_MAXPATSIZE=(256*16*4)+2;
/*========== Loader code */
@@ -134,20 +135,36 @@ static UBYTE *FAR_ConvertTrack(FARNOTE* n,int rows)
UniInstrument(n->ins);
UniNote(n->note+3*OCTAVE-1);
}
- if (n->vol&0xf) UniPTEffect(0xc,(n->vol&0xf)<<2);
+ if (n->vol>=0x01 && n->vol<=0x10) UniPTEffect(0xc,(n->vol - 1)<<2);
if (n->eff)
switch(n->eff>>4) {
+ case 0x0: /* global effects */
+ switch(n->eff & 0xf) {
+ case 0x3: /* fulfill loop */
+ UniEffect(UNI_KEYFADE, 0);
+ break;
+ case 0x4: /* old tempo mode */
+ case 0x5: /* new tempo mode */
+ break;
+ }
+ break;
+ case 0x1: /* pitch adjust up */
+ UniEffect(UNI_FAREFFECT1, n->eff & 0xf);
+ break;
+ case 0x2: /* pitch adjust down */
+ UniEffect(UNI_FAREFFECT2, n->eff & 0xf);
+ break;
case 0x3: /* porta to note */
- UniPTEffect(0x3,(n->eff&0xf)<<4);
+ UniEffect(UNI_FAREFFECT3, n->eff & 0xf);
break;
case 0x4: /* retrigger */
- UniPTEffect(0x0e, 0x90 | (n->eff & 0x0f));
+ UniEffect(UNI_FAREFFECT4, n->eff & 0xf);
break;
case 0x5: /* set vibrato depth */
vibdepth=n->eff&0xf;
break;
case 0x6: /* vibrato */
- UniPTEffect(0x4,((n->eff&0xf)<<4)|vibdepth);
+ UniEffect(UNI_FAREFFECT6,((n->eff&0xf)<<4)|vibdepth);
break;
case 0x7: /* volume slide up */
UniPTEffect(0xa,(n->eff&0xf)<<4);
@@ -155,11 +172,21 @@ static UBYTE *FAR_ConvertTrack(FARNOTE* n,int rows)
case 0x8: /* volume slide down */
UniPTEffect(0xa,n->eff&0xf);
break;
+ case 0x9: /* sustained vibrato */
+ break;
case 0xb: /* panning */
UniPTEffect(0xe,0x80|(n->eff&0xf));
break;
+ case 0xc: /* note offset */
+ break;
+ case 0xd: /* fine tempo down */
+ UniEffect(UNI_FAREFFECTD, n->eff & 0xf);
+ break;
+ case 0xe: /* fine tempo up */
+ UniEffect(UNI_FAREFFECTE, n->eff & 0xf);
+ break;
case 0xf: /* set speed */
- UniPTEffect(0xf,n->eff&0xf);
+ UniEffect(UNI_FAREFFECTF,n->eff&0xf);
break;
/* others not yet implemented */
@@ -178,11 +205,12 @@ static UBYTE *FAR_ConvertTrack(FARNOTE* n,int rows)
static int FAR_Load(int curious)
{
- int t,u,tracks=0;
+ int r,t,u,tracks=0;
SAMPLE *q;
FARSAMPLE s;
FARNOTE *crow;
UBYTE smap[8];
+ UBYTE addextrapattern;
(void)curious;
/* try to read module header (first part) */
@@ -202,10 +230,9 @@ static int FAR_Load(int curious)
of.modtype = MikMod_strdup(FAR_Version);
of.songname = DupStr(mh1->songname,40,1);
of.numchn = 16;
- of.initspeed = mh1->speed;
- of.inittempo = 80;
- of.reppos = 0;
- of.flags |= UF_PANNING;
+ of.initspeed = mh1->speed != 0 ? mh1->speed : 4;
+ of.bpmlimit = 5;
+ of.flags |= UF_PANNING | UF_FARTEMPO | UF_HIGHBPM;
for(t=0;t<16;t++) of.panning[t]=mh1->panning[t]<<4;
/* read songtext into comment field */
@@ -225,17 +252,29 @@ static int FAR_Load(int curious)
_mm_read_I_UWORDS(mh2->patsiz,256,modreader);
of.numpos = mh2->snglen;
+ of.reppos = mh2->loopto;
+
if(!AllocPositions(of.numpos)) return 0;
- for(t=0;t<of.numpos;t++) {
- if(mh2->orders[t]==0xff) break;
- of.positions[t] = mh2->orders[t];
- }
/* count number of patterns stored in file */
of.numpat = 0;
for(t=0;t<256;t++)
if(mh2->patsiz[t])
if((t+1)>of.numpat) of.numpat=t+1;
+
+ addextrapattern = 0;
+ for (t = 0; t < of.numpos; t++) {
+ if (mh2->orders[t] == 0xff) break;
+ of.positions[t] = mh2->orders[t];
+ if (of.positions[t] >= of.numpat) {
+ of.positions[t] = of.numpat;
+ addextrapattern = 1;
+ }
+ }
+
+ if (addextrapattern)
+ of.numpat++;
+
of.numtrk = of.numpat*of.numchn;
/* seek across eventual new data */
@@ -246,18 +285,17 @@ static int FAR_Load(int curious)
if(!AllocPatterns()) return 0;
for(t=0;t<of.numpat;t++) {
- UBYTE rows=0;
- UBYTE tempo;
-
memset(pat,0,256*16*4*sizeof(FARNOTE));
if(mh2->patsiz[t]) {
- rows = _mm_read_UBYTE(modreader);
- tempo = _mm_read_UBYTE(modreader);
- (void)tempo; /* unused */
+ /* Break position byte is always 1 less than the final row index,
+ i.e. it is 2 less than the total row count. */
+ UWORD rows = _mm_read_UBYTE(modreader) + 2;
+ _mm_skip_BYTE(modreader); /* tempo */
crow = pat;
/* file often allocates 64 rows even if there are less in pattern */
- if (mh2->patsiz[t]<2+(rows*16*4)) {
+ /* Also, don't allow more than 256 rows. */
+ if (mh2->patsiz[t]<2+(rows*16*4) || rows>256 || mh2->patsiz[t]>FAR_MAXPATSIZE) {
_mm_errno = MMERR_LOADING_PATTERN;
return 0;
}
@@ -280,8 +318,17 @@ static int FAR_Load(int curious)
_mm_errno=MMERR_LOADING_PATTERN;
return 0;
}
- } else
- tracks+=16;
+ } else {
+ // Farandole Composer normally use a 64 rows blank track for patterns with 0 rows
+ for (u = 0; u < 16; u++) {
+ UniReset();
+ for (r = 0; r < 64; r++) {
+ UniNewline();
+ }
+ of.tracks[tracks++] = UniDup();
+ }
+ of.pattrows[t] = 64;
+ }
}
/* read sample map */
@@ -319,11 +366,17 @@ static int FAR_Load(int curious)
q->loopend = s.repend;
q->volume = s.volume<<2;
- if(s.type&1) q->flags|=SF_16BITS;
+ if(s.type&1) {
+ q->flags|=SF_16BITS;
+ q->length >>= 1;
+ q->loopstart >>= 1;
+ q->loopend >>= 1;
+ }
+
if(s.loop&8) q->flags|=SF_LOOP;
q->seekpos = _mm_ftell(modreader);
- _mm_fseek(modreader,q->length,SEEK_CUR);
+ _mm_fseek(modreader,s.length,SEEK_CUR);
} else
q->samplename = MikMod_strdup("");
q++;
diff --git a/apps/plugins/mikmod/load_gdm.c b/apps/plugins/mikmod/load_gdm.c
index 5f06f9c70b..52ceea9c85 100644
--- a/apps/plugins/mikmod/load_gdm.c
+++ b/apps/plugins/mikmod/load_gdm.c
@@ -221,7 +221,7 @@ static UBYTE *GDM_ConvertTrack(GDMNOTE*tr)
if ((ins)&&(ins!=255))
UniInstrument(ins-1);
- if (note!=255) {
+ if (note && note!=255) {
UniNote(((note>>4)*OCTAVE)+(note&0xf)-1);
}
for (i=0;i<4;i++) {
@@ -237,18 +237,18 @@ static UBYTE *GDM_ConvertTrack(GDMNOTE*tr)
UniEffect(UNI_ITEFFECTG,inf);
break;
case 4: /* vibrato */
- UniEffect(UNI_ITEFFECTH,inf);
+ UniEffect(UNI_GDMEFFECT4,inf);
break;
case 5: /* portamento+volslide */
UniEffect(UNI_ITEFFECTG,0);
UniEffect(UNI_S3MEFFECTD,inf);
break;
case 6: /* vibrato+volslide */
- UniEffect(UNI_ITEFFECTH,0);
+ UniEffect(UNI_GDMEFFECT4,0);
UniEffect(UNI_S3MEFFECTD,inf);
break;
case 7: /* tremolo */
- UniEffect(UNI_S3MEFFECTR,inf);
+ UniEffect(UNI_GDMEFFECT7,inf);
break;
case 8: /* tremor */
UniEffect(UNI_S3MEFFECTI,inf);
@@ -271,37 +271,29 @@ static UBYTE *GDM_ConvertTrack(GDMNOTE*tr)
case 0x0e: /* extended */
switch (inf&0xf0) {
case 0x10: /* fine portamento up */
- UniEffect(UNI_S3MEFFECTF, 0x0f|((inf<<4)&0x0f));
+ UniEffect(UNI_S3MEFFECTF, 0xf0|(inf&0x0f));
break;
case 0x20: /* fine portamento down */
UniEffect(UNI_S3MEFFECTE, 0xf0|(inf&0x0f));
break;
case 0x30: /* glissando control */
- UniEffect(SS_GLISSANDO, inf&0x0f);
- break;
case 0x40: /* vibrato waveform */
- UniEffect(SS_VIBWAVE, inf&0x0f);
- break;
case 0x50: /* set c4spd */
- UniEffect(SS_FINETUNE, inf&0x0f);
- break;
case 0x60: /* loop fun */
- UniEffect(UNI_ITEFFECTS0, (inf&0x0f)|0xb0);
- break;
case 0x70: /* tremolo waveform */
- UniEffect(SS_TREMWAVE, inf&0x0f);
+ UniPTEffect(0xe, inf);
break;
case 0x80: /* extra fine porta up */
- UniEffect(UNI_S3MEFFECTF, 0x0e|((inf<<4)&0x0f));
+ UniEffect(UNI_S3MEFFECTF, 0xe0|(inf&0x0f));
break;
case 0x90: /* extra fine porta down */
UniEffect(UNI_S3MEFFECTE, 0xe0|(inf&0x0f));
break;
case 0xa0: /* fine volslide up */
- UniEffect(UNI_S3MEFFECTD, 0x0f|((inf<<4)&0x0f));
+ UniEffect(UNI_S3MEFFECTD, 0x0f|((inf&0x0f)<<4));
break;
case 0xb0: /* fine volslide down */
- UniEffect(UNI_S3MEFFECTE, 0xf0|(inf&0x0f));
+ UniEffect(UNI_S3MEFFECTD, 0xf0|(inf&0x0f));
break;
case 0xc0: /* note cut */
case 0xd0: /* note delay */
@@ -320,18 +312,15 @@ static UBYTE *GDM_ConvertTrack(GDMNOTE*tr)
UniEffect(UNI_S3MEFFECTQ,inf);
break;
case 0x13: /* set global volume */
- UniEffect(UNI_XMEFFECTG,inf<<1);
+ UniEffect(UNI_XMEFFECTG,inf);
break;
case 0x14: /* fine vibrato */
- UniEffect(UNI_ITEFFECTU,inf);
+ UniEffect(UNI_GDMEFFECT14,inf);
break;
case 0x1e: /* special */
switch (inf&0xf0) {
- case 8: /* set pan position */
- if (inf >=128)
- UniPTEffect(0x08,255);
- else
- UniPTEffect(0x08,inf<<1);
+ case 0x80: /* set pan position */
+ UniPTEffect(0xe,inf);
break;
}
break;
@@ -473,16 +462,21 @@ static int GDM_Load(int curious)
q->length=s.length;
q->loopstart=s.loopbeg;
q->loopend=s.loopend;
- q->volume=s.vol;
- q->panning=s.pan;
+ q->volume=64;
q->seekpos=position;
position +=s.length;
+ /* Only use the sample volume byte if bit 2 is set. When bit 3 is set,
+ the sample panning is supposed to be used, but 2GDM isn't capable
+ of making a GDM using this feature; the panning byte is always 0xFF
+ or junk. Likewise, bit 5 is unused (supposed to be LZW compression). */
if (s.flags&1)
q->flags |=SF_LOOP;
if (s.flags&2)
q->flags |=SF_16BITS;
+ if ((s.flags&4) && s.vol<=64)
+ q->volume=s.vol;
if (s.flags&16)
q->flags |=SF_STEREO;
q++;
diff --git a/apps/plugins/mikmod/load_it.c b/apps/plugins/mikmod/load_it.c
index c1e257fd97..3f8c0e6996 100644
--- a/apps/plugins/mikmod/load_it.c
+++ b/apps/plugins/mikmod/load_it.c
@@ -76,6 +76,7 @@ typedef struct ITHEADER {
/* sample information */
typedef struct ITSAMPLE {
+ UBYTE id[4]; /* 'IMPS' */
CHAR filename[12];
UBYTE zerobyte;
UBYTE globvol;
@@ -102,6 +103,7 @@ typedef struct ITSAMPLE {
#define ITENVCNT 25
#define ITNOTECNT 120
typedef struct ITINSTHEADER {
+ UBYTE id[4]; /* 'IMPI' */
ULONG size; /* (dword) Instrument size */
CHAR filename[12]; /* (char) Instrument filename */
UBYTE zerobyte; /* (byte) Instrument type (always 0) */
@@ -194,8 +196,8 @@ static int IT_Init(void)
if(!(mh=(ITHEADER*)MikMod_malloc(sizeof(ITHEADER)))) return 0;
if(!(poslookup=(UBYTE*)MikMod_malloc(256*sizeof(UBYTE)))) return 0;
if(!(itpat=(ITNOTE*)MikMod_malloc(200*64*sizeof(ITNOTE)))) return 0;
- if(!(mask=(UBYTE*)MikMod_malloc(64*sizeof(UBYTE)))) return 0;
- if(!(last=(ITNOTE*)MikMod_malloc(64*sizeof(ITNOTE)))) return 0;
+ if(!(mask=(UBYTE*)MikMod_calloc(64,sizeof(UBYTE)))) return 0;
+ if(!(last=(ITNOTE*)MikMod_calloc(64,sizeof(ITNOTE)))) return 0;
return 1;
}
@@ -277,7 +279,10 @@ static UBYTE* IT_ConvertTrack(ITNOTE* tr,UWORD numrows)
UniNote(note);
}
- if((ins)&&(ins<100))
+ /* Impulse Tracker only allows up to 99 instruments and crashes when it
+ encounters instruments >=100. But the file format supports them just
+ fine and there are many MPT-created ITs with that many instruments. */
+ if((ins)&&(ins<253))
UniInstrument(ins-1);
else if(ins==253)
UniWriteByte(UNI_KEYOFF);
@@ -632,7 +637,16 @@ static int IT_Load(int curious)
ITSAMPLE s;
/* seek to sample position */
- _mm_fseek(modreader,(long)(paraptr[mh->insnum+t]+4),SEEK_SET);
+ _mm_fseek(modreader,(long)(paraptr[mh->insnum+t]),SEEK_SET);
+ if(!_mm_read_UBYTES(s.id,4,modreader)||
+ memcmp(s.id,"IMPS",4) != 0) {
+ /* no error so that use-brdg.it and use-funk.it
+ * to load correctly (both IT 2.04) (from libxmp) */
+#ifdef MIKMOD_DEBUG
+ fprintf(stderr,"Bad magic in sample %d\n",t);
+#endif
+ continue;
+ }
/* load sample info */
_mm_read_string(s.filename,12,modreader);
@@ -702,7 +716,8 @@ static int IT_Load(int curious)
if(s.flag&16) q->flags|=SF_LOOP;
if(s.flag&64) q->flags|=SF_BIDI;
- if(mh->cwt>=0x200) {
+ if(s.convert==0xff) q->flags|=SF_ADPCM4|SF_SIGNED; /* MODPlugin ADPCM */
+ else if(mh->cwt>=0x200) {
if(s.convert&1) q->flags|=SF_SIGNED;
if(s.convert&4) q->flags|=SF_DELTA;
}
@@ -719,7 +734,12 @@ static int IT_Load(int curious)
ITINSTHEADER ih;
/* seek to instrument position */
- _mm_fseek(modreader,paraptr[t]+4,SEEK_SET);
+ _mm_fseek(modreader,paraptr[t],SEEK_SET);
+ if(!_mm_read_UBYTES(ih.id,4,modreader)||
+ memcmp(ih.id,"IMPI",4) != 0) {
+ _mm_errno = MMERR_LOADING_SAMPLEINFO;
+ return 0;
+ }
/* load instrument info */
_mm_read_string(ih.filename,12,modreader);
@@ -988,8 +1008,6 @@ static int IT_Load(int curious)
if(!AllocTracks()) return 0;
for(t=0;t<of.numpat;t++) {
- UWORD packlen;
-
/* seek to pattern position */
if(!paraptr[mh->insnum+mh->smpnum+t]) { /* 0 -> empty 64 row pattern */
of.pattrows[t]=64;
@@ -1002,8 +1020,7 @@ static int IT_Load(int curious)
}
} else {
_mm_fseek(modreader,((long)paraptr[mh->insnum+mh->smpnum+t]),SEEK_SET);
- packlen=_mm_read_I_UWORD(modreader);
- (void)packlen; /* unused */
+ (void) _mm_read_I_UWORD(modreader); /* packlen */
of.pattrows[t]=_mm_read_I_UWORD(modreader);
_mm_read_I_ULONG(modreader);
if(!IT_ReadPattern(of.pattrows[t])) return 0;
diff --git a/apps/plugins/mikmod/load_med.c b/apps/plugins/mikmod/load_med.c
index a6af8c06cb..2d491729bb 100644
--- a/apps/plugins/mikmod/load_med.c
+++ b/apps/plugins/mikmod/load_med.c
@@ -20,8 +20,6 @@
/*==============================================================================
- $Id$
-
Amiga MED module loader
==============================================================================*/
@@ -48,6 +46,8 @@ extern int fprintf(FILE *, const char *, ...);
/*========== Module information */
+#define MEDNOTECNT 120
+
typedef struct MEDHEADER {
ULONG id;
ULONG modlen;
@@ -137,6 +137,19 @@ typedef struct MEDINSTINFO {
UBYTE name[40];
} MEDINSTINFO;
+enum MEDINSTTYPE {
+ INST_HYBRID = -2,
+ INST_SYNTH = -1,
+ INST_SAMPLE = 0,
+ INST_IFFOCT_5 = 1,
+ INST_IFFOCT_3 = 2,
+ INST_IFFOCT_2 = 3,
+ INST_IFFOCT_4 = 4,
+ INST_IFFOCT_6 = 5,
+ INST_IFFOCT_7 = 6,
+ INST_EXTSAMPLE = 7
+};
+
/*========== Loader variables */
#define MMD0_string 0x4D4D4430
@@ -149,8 +162,11 @@ static ULONG *ba = NULL;
static MMD0NOTE *mmd0pat = NULL;
static MMD1NOTE *mmd1pat = NULL;
+static UBYTE medversion;
static int decimalvolumes;
static int bpmtempos;
+static int is8channel;
+static UWORD rowsperbeat;
#define d0note(row,col) mmd0pat[((row)*(UWORD)of.numchn)+(col)]
#define d1note(row,col) mmd1pat[((row)*(UWORD)of.numchn)+(col)]
@@ -197,52 +213,118 @@ static void MED_Cleanup(void)
mmd1pat = NULL;
}
-static void EffectCvt(UBYTE eff, UBYTE dat)
+static UWORD MED_ConvertTempo(UWORD tempo)
+{
+ /* MED tempos 1-10 are compatibility tempos that are converted to different values.
+ These were determined by testing with OctaMED 2.00 and roughly correspond to the
+ formula: (195 + speed/2) / speed. Despite being "tracker compatible" these really
+ are supposed to change the tempo and NOT the speed. These are tempo-mode only. */
+ static const UBYTE tempocompat[11] =
+ {
+ 0, 195, 97, 65, 49, 39, 32, 28, 24, 22, 20
+ };
+
+ /* MEDs with 8 channels do something completely different with their tempos.
+ This behavior completely overrides normal timing when it is enabled.
+ NOTE: the tempos used for this are directly from OctaMED Soundstudio 2, but
+ in older versions the playback speeds differed slightly between NTSC and PAL.
+ This table seems to have been intended to be a compromise between the two.*/
+ static const UBYTE tempo8channel[11] =
+ {
+ 0, 179, 164, 152, 141, 131, 123, 116, 110, 104, 99
+ };
+
+ ULONG result;
+
+ if (is8channel) {
+ tempo = tempo < 10 ? tempo : 10;
+ return tempo8channel[tempo];
+ }
+
+ if (bpmtempos) {
+ /* Convert MED BPM into ProTracker-compatible BPM. All that really needs to be done
+ here is the BPM needs to be multiplied by (rows per beat)/(PT rows per beat = 4).
+ BPM mode doesn't have compatibility tempos like tempo mode but OctaMED does
+ something unusual with BPM<=2 that was found in electrosound 64.med. */
+ result = (tempo > 2) ? ((ULONG)tempo * rowsperbeat + 2) / 4 : 125;
+ } else {
+ if (tempo >= 1 && tempo <= 10)
+ tempo = tempocompat[tempo];
+
+ /* Convert MED tempo into ProTracker-compatble BPM. */
+ result = ((ULONG)tempo * 125) / 33;
+ }
+
+ return result < 65535 ? result : 65535;
+}
+
+static void EffectCvt(UBYTE note, UBYTE eff, UBYTE dat)
{
switch (eff) {
- /* 0x0 0x1 0x2 0x3 0x4 PT effects */
- case 0x5: /* PT vibrato with speed/depth nibbles swapped */
- UniPTEffect(0x4, (dat >> 4) | ((dat & 0xf) << 4));
+ /* 0x0: arpeggio */
+ case 0x1: /* portamento up (PT compatible, ignore 0) */
+ if (dat)
+ UniPTEffect(0x1, dat);
+ break;
+ case 0x2: /* portamento down (PT compatible, ignore 0) */
+ if (dat)
+ UniPTEffect(0x2, dat);
break;
- /* 0x6 0x7 not used */
- case 0x6:
- case 0x7:
+ /* 0x3: tone portamento */
+ case 0x4: /* vibrato (~2x the speed/depth of PT vibrato) */
+ UniWriteByte(UNI_MEDEFFECT_VIB);
+ UniWriteByte((dat & 0xf0) >> 3);
+ UniWriteByte((dat & 0x0f) << 1);
break;
- case 0x8: /* midi hold/decay */
+ case 0x5: /* tone portamento + volslide (MMD0: old vibrato) */
+ if (medversion == 0) {
+ /* Approximate conversion, probably wrong.
+ The entire param is depth and the rate is fixed. */
+ UniWriteByte(UNI_MEDEFFECT_VIB);
+ UniWriteByte(0x16);
+ UniWriteByte((dat + 3) >> 2);
+ break;
+ }
+ UniPTEffect(eff, dat);
+ break;
+ /* 0x6: vibrato + volslide */
+ /* 0x7: tremolo */
+ case 0x8: /* set hold/decay (FIXME- hold/decay not implemented) */
break;
- case 0x9:
- if (bpmtempos) {
- if (!dat)
- dat = of.initspeed;
+ case 0x9: /* set speed */
+ /* TODO: Rarely MED modules request values of 0x00 or >0x20. In most Amiga versions
+ these behave as speed=(param & 0x1F) ? (param & 0x1F) : 32. From Soundstudio 2
+ up, these have different behavior. Since the docs/UI insist you shouldn't use
+ these values and not many modules use these, just ignore them for now. */
+ if (dat >= 0x01 && dat <= 0x20) {
UniEffect(UNI_S3MEFFECTA, dat);
- } else {
- if (dat <= 0x20) {
- if (!dat)
- dat = of.initspeed;
- else
- dat /= 4;
- UniPTEffect(0xf, dat);
- } else
- UniEffect(UNI_MEDSPEED, ((UWORD)dat * 125) / (33 * 4));
}
break;
- /* 0xa 0xb PT effects */
+ /* 0xa: volslide */
+ /* 0xb: position jump */
case 0xc:
if (decimalvolumes)
dat = (dat >> 4) * 10 + (dat & 0xf);
UniPTEffect(0xc, dat);
break;
+ case 0xa:
case 0xd: /* same as PT volslide */
+ /* if both nibbles are set, a slide up is performed. */
+ if ((dat & 0xf) && (dat & 0xf0))
+ dat &= 0xf0;
UniPTEffect(0xa, dat);
break;
- case 0xe: /* synth jmp - midi */
+ case 0xe: /* synth jump (FIXME- synth instruments not implemented) */
break;
case 0xf:
switch (dat) {
- case 0: /* patternbreak */
+ case 0: /* patternbreak */
UniPTEffect(0xd, 0);
break;
case 0xf1: /* play note twice */
+ /* Note: OctaMED 6.00d and up will not play FF1/FF3 effects when
+ they are used on a line without a note. Since MMD2/MMD3 support is
+ theoretical at this point, allow these unconditionally for now. */
UniWriteByte(UNI_MEDEFFECTF1);
break;
case 0xf2: /* delay note */
@@ -251,6 +333,15 @@ static void EffectCvt(UBYTE eff, UBYTE dat)
case 0xf3: /* play note three times */
UniWriteByte(UNI_MEDEFFECTF3);
break;
+ case 0xf8: /* hardware filter off */
+ UniPTEffect(0xe, 0x01);
+ break;
+ case 0xf9: /* hardware filter on */
+ UniPTEffect(0xe, 0x00);
+ break;
+ case 0xfd: /* set pitch */
+ UniWriteByte(UNI_MEDEFFECT_FD);
+ break;
case 0xfe: /* stop playing */
UniPTEffect(0xb, of.numpat);
break;
@@ -258,18 +349,65 @@ static void EffectCvt(UBYTE eff, UBYTE dat)
UniPTEffect(0xc, 0);
break;
default:
- if (dat <= 10)
- UniPTEffect(0xf, dat);
- else if (dat <= 240) {
- if (bpmtempos)
- UniPTEffect(0xf, (dat < 32) ? 32 : dat);
- else
- UniEffect(UNI_MEDSPEED, ((UWORD)dat * 125) / 33);
- }
+ if (dat <= 240)
+ UniEffect(UNI_MEDSPEED, MED_ConvertTempo(dat));
}
break;
- default: /* all normal PT effects are handled here */
- UniPTEffect(eff, dat);
+ case 0x11: /* fine portamento up */
+ /* fine portamento of 0 does nothing. */
+ if (dat)
+ UniEffect(UNI_XMEFFECTE1, dat);
+ break;
+ case 0x12: /* fine portamento down */
+ if (dat)
+ UniEffect(UNI_XMEFFECTE2, dat);
+ break;
+ case 0x14: /* vibrato (PT compatible depth, ~2x speed) */
+ UniWriteByte(UNI_MEDEFFECT_VIB);
+ UniWriteByte((dat & 0xf0) >> 3);
+ UniWriteByte((dat & 0x0f));
+ break;
+ case 0x15: /* set finetune */
+ /* Valid values are 0x0 to 0x7 and 0xF8 to 0xFF. */
+ if (dat <= 0x7 || dat >= 0xF8)
+ UniPTEffect(0xe, 0x50 | (dat & 0xF));
+ break;
+ case 0x16: /* loop */
+ UniEffect(UNI_MEDEFFECT_16, dat);
+ break;
+ case 0x18: /* cut note */
+ UniEffect(UNI_MEDEFFECT_18, dat);
+ break;
+ case 0x19: /* sample offset */
+ UniPTEffect(0x9, dat);
+ break;
+ case 0x1a: /* fine volslide up */
+ /* volslide of 0 does nothing. */
+ if (dat)
+ UniEffect(UNI_XMEFFECTEA, dat);
+ break;
+ case 0x1b: /* fine volslide down */
+ if (dat)
+ UniEffect(UNI_XMEFFECTEB, dat);
+ break;
+ case 0x1d: /* pattern break */
+ UniPTEffect(0xd, dat);
+ break;
+ case 0x1e: /* pattern delay */
+ UniEffect(UNI_MEDEFFECT_1E, dat);
+ break;
+ case 0x1f: /* combined delay-retrigger */
+ /* This effect does nothing on lines without a note. */
+ if (note)
+ UniEffect(UNI_MEDEFFECT_1F, dat);
+ break;
+ default:
+ if (eff < 0x10)
+ UniPTEffect(eff, dat);
+#ifdef MIKMOD_DEBUG
+ else
+ fprintf(stderr, "unsupported OctaMED command %u\n", eff);
+#endif
break;
}
}
@@ -286,14 +424,14 @@ static UBYTE *MED_Convert1(int count, int col)
note = n->a & 0x7f;
inst = n->b & 0x3f;
- eff = n->c & 0xf;
+ eff = n->c;
dat = n->d;
if (inst)
UniInstrument(inst - 1);
if (note)
- UniNote(note + 3 * OCTAVE - 1);
- EffectCvt(eff, dat);
+ UniNote(note - 1);
+ EffectCvt(note, eff, dat);
UniNewline();
}
return UniDup();
@@ -321,8 +459,8 @@ static UBYTE *MED_Convert0(int count, int col)
if (inst)
UniInstrument(inst - 1);
if (note)
- UniNote(note + 3 * OCTAVE - 1);
- EffectCvt(eff, dat);
+ UniNote(note - 1);
+ EffectCvt(note, eff, dat);
UniNewline();
}
return UniDup();
@@ -349,7 +487,7 @@ static int LoadMEDPatterns(void)
return 0;
}
/* sanity check */
- if (! of.numchn) /* docs say 4, 8, 12 or 16 */
+ if (! of.numchn || of.numchn > 16) /* docs say 4, 8, 12 or 16 */
return 0;
of.numtrk = of.numpat * of.numchn;
@@ -375,6 +513,8 @@ static int LoadMEDPatterns(void)
mmdp->b = _mm_read_UBYTE(modreader);
mmdp->c = _mm_read_UBYTE(modreader);
}
+ /* Skip tracks this block doesn't use. */
+ for (col = numtracks; col < of.numchn; col++, mmdp++) {}
}
for (col = 0; col < of.numchn; col++)
@@ -405,7 +545,7 @@ static int LoadMMD1Patterns(void)
return 0;
}
/* sanity check */
- if (! of.numchn) /* docs say 4, 8, 12 or 16 */
+ if (! of.numchn || of.numchn > 16) /* docs say 4, 8, 12 or 16 */
return 0;
of.numtrk = of.numpat * of.numchn;
@@ -434,6 +574,8 @@ static int LoadMMD1Patterns(void)
mmdp->c = _mm_read_UBYTE(modreader);
mmdp->d = _mm_read_UBYTE(modreader);
}
+ /* Skip tracks this block doesn't use. */
+ for (col = numtracks; col < of.numchn; col++, mmdp++) {}
}
for (col = 0; col < of.numchn; col++)
@@ -444,9 +586,10 @@ static int LoadMMD1Patterns(void)
static int MED_Load(int curious)
{
- int t;
+ int t, i;
ULONG sa[64];
MEDINSTHEADER s;
+ INSTRUMENT *d;
SAMPLE *q;
MEDSAMPLE *mss;
@@ -536,7 +679,7 @@ static int MED_Load(int curious)
_mm_errno = MMERR_NOT_A_MODULE;
return 0;
}
- /* truncate insane songnamelen (fail instead??) */
+ /* truncate insane songnamelen (fail instead?) */
if (me->songnamelen > 256)
me->songnamelen = 256;
}
@@ -570,46 +713,18 @@ static int MED_Load(int curious)
}
decimalvolumes = (ms->flags & 0x10) ? 0 : 1;
+ is8channel = (ms->flags & 0x40) ? 1 : 0;
bpmtempos = (ms->flags2 & 0x20) ? 1 : 0;
if (bpmtempos) {
- int bpmlen = (ms->flags2 & 0x1f) + 1;
+ rowsperbeat = (ms->flags2 & 0x1f) + 1;
of.initspeed = ms->tempo2;
- of.inittempo = ms->deftempo * bpmlen / 4;
-
- if (bpmlen != 4) {
- /* Let's do some math : compute GCD of BPM beat length and speed */
- int a, b;
-
- a = bpmlen;
- b = ms->tempo2;
-
- if (a > b) {
- t = b;
- b = a;
- a = t;
- }
- while ((a != b) && (a)) {
- t = a;
- a = b - a;
- b = t;
- if (a > b) {
- t = b;
- b = a;
- a = t;
- }
- }
-
- of.initspeed /= b;
- of.inittempo = ms->deftempo * bpmlen / (4 * b);
- }
+ of.inittempo = MED_ConvertTempo(ms->deftempo);
} else {
of.initspeed = ms->tempo2;
- of.inittempo = ms->deftempo ? ((UWORD)ms->deftempo * 125) / 33 : 128;
- if ((ms->deftempo <= 10) && (ms->deftempo))
- of.inittempo = (of.inittempo * 33) / 6;
- of.flags |= UF_HIGHBPM;
+ of.inittempo = ms->deftempo ? MED_ConvertTempo(ms->deftempo) : 128;
}
+ of.flags |= UF_HIGHBPM;
MED_Version[12] = mh->id;
of.modtype = MikMod_strdup(MED_Version);
of.numchn = 0; /* will be counted later */
@@ -633,18 +748,29 @@ static int MED_Load(int curious)
ReadComment(me->annolen);
}
- if (!AllocSamples())
+ /* TODO: should do an initial scan for IFFOCT instruments to determine the
+ actual number of samples (instead of assuming 1-to-1). */
+ if (!AllocSamples() || !AllocInstruments())
return 0;
+
+ of.flags |= UF_INST;
q = of.samples;
+ d = of.instruments;
for (t = 0; t < of.numins; t++) {
q->flags = SF_SIGNED;
q->volume = 64;
+ s.type = INST_SAMPLE;
if (sa[t]) {
_mm_fseek(modreader, sa[t], SEEK_SET);
s.length = _mm_read_M_ULONG(modreader);
s.type = _mm_read_M_SWORD(modreader);
- if (s.type) {
+ switch (s.type) {
+ case INST_SAMPLE:
+ case INST_EXTSAMPLE:
+ break;
+
+ default:
#ifdef MIKMOD_DEBUG
fprintf(stderr, "\rNon-sample instruments not supported in MED loader yet\n");
#endif
@@ -668,6 +794,9 @@ static int MED_Load(int curious)
if (ms->sample[t].replen > 1)
q->flags |= SF_LOOP;
+ if(ms->sample[t].svol <= 64)
+ q->volume = ms->sample[t].svol;
+
/* don't load sample if length>='MMD0'...
such kluges make libmikmod's code unique !!! */
if (q->length >= MMD0_string)
@@ -697,18 +826,53 @@ static int MED_Load(int curious)
_mm_fseek(modreader, me->iinfo + t * me->i_ext_entrsz, SEEK_SET);
_mm_read_UBYTES(ii.name, 40, modreader);
q->samplename = DupStr((char*)ii.name, 40, 1);
- } else
+ d->insname = DupStr((char*)ii.name, 40, 1);
+ } else {
q->samplename = NULL;
+ d->insname = NULL;
+ }
+
+ /* Instrument transpose tables. */
+ for (i = 0; i < MEDNOTECNT; i++) {
+ int note = i + 3 * OCTAVE + ms->sample[t].strans + ms->playtransp;
+
+ /* TODO: IFFOCT instruments... */
+ switch (s.type) {
+ case INST_EXTSAMPLE:
+ /* TODO: not clear if this has the same wrapping behavior as regular samples.
+ This is a MMD2/MMD3 extension so it has not been tested. */
+ note -= 2 * OCTAVE;
+ /* fall-through */
+
+ case INST_SAMPLE:
+ /* TODO: in MMD2/MMD3 mixing mode, these wrapping transforms don't apply. */
+ if (note >= 10 * OCTAVE) {
+ /* Buggy octaves 8 through A wrap to 2 octaves below octave 1.
+ Technically they're also a finetune step higher but that's safe
+ to ignore. */
+ note -= 9 * OCTAVE;
+ } else if (note >= 6 * OCTAVE) {
+ /* Octaves 4 through 7 repeat octave 3. */
+ note = (note % 12) + 5 * OCTAVE;
+ }
+ d->samplenumber[i] = t;
+ d->samplenote[i] = note<0 ? 0 : note>255 ? 255 : note;
+ break;
+ }
+ }
q++;
+ d++;
}
if (mh->id == MMD0_string) {
+ medversion = 0;
if (!LoadMEDPatterns()) {
_mm_errno = MMERR_LOADING_PATTERN;
return 0;
}
} else if (mh->id == MMD1_string) {
+ medversion = 1;
if (!LoadMMD1Patterns()) {
_mm_errno = MMERR_LOADING_PATTERN;
return 0;
diff --git a/apps/plugins/mikmod/load_mod.c b/apps/plugins/mikmod/load_mod.c
index 0fe0dcd595..9ce39d6922 100644
--- a/apps/plugins/mikmod/load_mod.c
+++ b/apps/plugins/mikmod/load_mod.c
@@ -84,10 +84,13 @@ typedef struct MODNOTE {
static CHAR protracker[] = "Protracker";
static CHAR startrekker[] = "Startrekker";
static CHAR fasttracker[] = "Fasttracker";
-static CHAR oktalyser[] = "Oktalyser";
+static CHAR octalyser[] = "Octalyser";
static CHAR oktalyzer[] = "Oktalyzer";
static CHAR taketracker[] = "TakeTracker";
+static CHAR digitaltracker[] = "Digital Tracker MOD";
static CHAR orpheus[] = "Imago Orpheus (MOD format)";
+static CHAR modsgrave[] = "Mod's Grave";
+static CHAR unknown[] = "Unknown tracker MOD";
static MODULEHEADER *mh = NULL;
static MODNOTE *patbuf = NULL;
@@ -102,7 +105,7 @@ static int MOD_CheckType(UBYTE *id, UBYTE *numchn, CHAR **descr)
modtype = trekker = 0;
/* Protracker and variants */
- if ((!memcmp(id, "M.K.", 4)) || (!memcmp(id, "M!K!", 4))) {
+ if ((!memcmp(id, "M.K.", 4)) || (!memcmp(id, "M!K!", 4)) || (!memcmp(id, "M&K!", 4))) {
*descr = protracker;
modtype = 0;
*numchn = 4;
@@ -132,11 +135,11 @@ static int MOD_CheckType(UBYTE *id, UBYTE *numchn, CHAR **descr)
return 1;
}
- /* Oktalyser (Atari) */
- if (!memcmp(id, "CD81", 4)) {
- *descr = oktalyser;
+ /* Octalyser (Atari) */
+ if (!memcmp(id, "CD81", 4) || !memcmp(id, "CD61", 4)) {
+ *descr = octalyser;
modtype = 1;
- *numchn = 8;
+ *numchn = id[2] - '0';
return 1;
}
@@ -160,6 +163,27 @@ static int MOD_CheckType(UBYTE *id, UBYTE *numchn, CHAR **descr)
*numchn = (id[0] - '0') * 10 + (id[1] - '0');
return 1;
}
+ /* Taketracker */
+ if (!memcmp(id, "TDZ", 3) && (id[3] >= '1' && id[3] <= '3')) {
+ *descr = taketracker;
+ *numchn = (id[3] - '0');
+ return 1;
+ }
+
+ /* Digital Tracker */
+ if (!memcmp(id, "FA0", 3) && (id[3] == '4' || id[3] == '6' || id[3] == '8')) {
+ *descr = digitaltracker;
+ *numchn = (id[3] - '0');
+ return 1;
+ }
+
+ /* Standard 4-channel MODs with unusual IDs. */
+ if (!memcmp(id, "LARD", 4) /* judgement_day_gvine.mod */
+ || !memcmp(id, "NSMS", 4)) { /* kingdomofpleasure.mod */
+ *descr = unknown;
+ *numchn = 4;
+ return 1;
+ }
return 0;
}
@@ -368,6 +392,16 @@ static int MOD_Load(int curious)
SAMPLE *q;
MSAMPINFO *s;
CHAR *descr;
+ int maybewow = 1;
+ ULONG samplelength = 0;
+ ULONG filelength;
+ ULONG pos;
+ char adpcm[5];
+
+ pos = _mm_ftell(modreader);
+ _mm_fseek(modreader, 0, SEEK_END);
+ filelength = _mm_ftell(modreader);
+ _mm_fseek(modreader, pos, SEEK_SET);
/* try to read module header */
_mm_read_string((CHAR *)mh->songname, 20, modreader);
@@ -382,6 +416,11 @@ static int MOD_Load(int curious)
s->volume = _mm_read_UBYTE(modreader);
s->reppos = _mm_read_M_UWORD(modreader);
s->replen = _mm_read_M_UWORD(modreader);
+ /* Mod's Grave .WOW files are converted from .669 and thus
+ do not have sample finetune or volume. */
+ samplelength += (ULONG)s->length << 1;
+ if (s->length && (s->finetune != 0x00 || s->volume != 0x40))
+ maybewow = 0;
}
mh->songlength = _mm_read_UBYTE(modreader);
@@ -394,6 +433,10 @@ static int MOD_Load(int curious)
_mm_read_UBYTES(mh->positions, 128, modreader);
_mm_read_UBYTES(mh->magic2, 4, modreader);
+ /* Mod's Grave .WOW files always use 0x00 for the "restart" byte. */
+ if (mh->magic1 != 0x00)
+ maybewow = 0;
+
if (_mm_eof(modreader)) {
_mm_errno = MMERR_LOADING_HEADER;
return 0;
@@ -406,6 +449,12 @@ static int MOD_Load(int curious)
_mm_errno = MMERR_NOT_A_MODULE;
return 0;
}
+ if (descr == digitaltracker) {
+ /* Digital Tracker FA0x modules add four extra bytes after the
+ * magic. These don't seem to have ever been used for their
+ * intended purpose (rows per pattern and sample bits/rate). */
+ _mm_read_M_ULONG(modreader);
+ }
if (trekker && of.numchn == 8)
for (t = 0; t < 128; t++)
/* if module pretends to be FLT8, yet the order table
@@ -443,6 +492,24 @@ static int MOD_Load(int curious)
of.numpos = t + 1;
}
of.numpat++;
+
+ /* Mod's Grave .WOW files have an M.K. signature but they're actually 8 channel.
+ The only way to distinguish them from a 4-channel M.K. file is to check the
+ length of the .MOD against the expected length of a .WOW file with the same
+ number of patterns as this file. To make things harder, Mod's Grave occasionally
+ adds an extra byte to .WOW files and sometimes .MOD authors pad their files.
+ Prior checks for WOW behavior should help eliminate false positives here.
+
+ Also note the length check relies on counting samples with a length word=1 to work. */
+ if (modtype == 0 && maybewow == 1) {
+ ULONG wowlength = MODULEHEADERSIZE + 4 + samplelength + of.numpat * (64 * 4 * 8);
+ if ((filelength & ~1) == wowlength) {
+ modtype = 1;
+ descr = modsgrave;
+ of.numchn = 8;
+ }
+ }
+
of.numtrk = of.numpat * of.numchn;
if (!AllocPositions(of.numpos))
@@ -450,12 +517,16 @@ static int MOD_Load(int curious)
for (t = 0; t < of.numpos; t++)
of.positions[t] = mh->positions[t];
+ if (!ML_LoadPatterns())
+ return 0;
+
/* Finally, init the sampleinfo structures */
of.numins = of.numsmp = 31;
if (!AllocSamples())
return 0;
s = mh->samples;
q = of.samples;
+ pos = _mm_ftell(modreader);
for (t = 0; t < of.numins; t++) {
/* convert the samplename */
q->samplename = DupStr(s->samplename, 23, 1);
@@ -474,15 +545,28 @@ static int MOD_Load(int curious)
if (s->replen > 2)
q->flags |= SF_LOOP;
+ q->seekpos = pos;
+ /* Test for MODPlugin ADPCM. These are indicated by "ADPCM"
+ * embedded at the start of each sample's data. :( */
+ memset(adpcm, 0, sizeof(adpcm));
+ _mm_read_UBYTES(adpcm, 5, modreader);
+ if (!memcmp(adpcm, "ADPCM", 5)) {
+ q->flags |= SF_ADPCM4;
+ q->seekpos += 5;
+ /* Stored half-length, plus a 16 byte table. */
+ pos += s->length + 16 + 5;
+ _mm_fseek(modreader, s->length + 16, SEEK_CUR);
+ } else {
+ pos += q->length;
+ _mm_fseek(modreader, q->length - 5, SEEK_CUR);
+ }
+
s++;
q++;
}
of.modtype = MikMod_strdup(descr);
- if (!ML_LoadPatterns())
- return 0;
-
return 1;
}
diff --git a/apps/plugins/mikmod/load_s3m.c b/apps/plugins/mikmod/load_s3m.c
index 076159afc5..0470c8bcab 100644
--- a/apps/plugins/mikmod/load_s3m.c
+++ b/apps/plugins/mikmod/load_s3m.c
@@ -403,6 +403,7 @@ static int S3M_Load(int curious)
if(s.flags&1) q->flags |= SF_LOOP;
if(s.flags&4) q->flags |= SF_16BITS;
if(mh->fileformat==1) q->flags |= SF_SIGNED;
+ if(s.pack==4) q->flags |= SF_ADPCM4 | SF_SIGNED; /* MODPlugin ADPCM4. */
/* don't load sample if it doesn't have the SCRS tag */
if(memcmp(s.scrs,"SCRS",4)) q->length = 0;
diff --git a/apps/plugins/mikmod/load_stm.c b/apps/plugins/mikmod/load_stm.c
index c62a6d7f36..a2718fa81c 100644
--- a/apps/plugins/mikmod/load_stm.c
+++ b/apps/plugins/mikmod/load_stm.c
@@ -225,7 +225,7 @@ static UBYTE *STM_ConvertTrack(STMNOTE *n)
return UniDup();
}
-static int STM_LoadPatterns(void)
+static int STM_LoadPatterns(unsigned int pattoload)
{
unsigned int t,s,tracks=0;
@@ -233,7 +233,7 @@ static int STM_LoadPatterns(void)
if(!AllocTracks()) return 0;
/* Allocate temporary buffer for loading and converting the patterns */
- for(t=0;t<of.numpat;t++) {
+ for(t=0;t<pattoload;t++) {
for(s=0;s<(64U*of.numchn);s++) {
stmbuf[s].note = _mm_read_UBYTE(modreader);
stmbuf[s].insvol = _mm_read_UBYTE(modreader);
@@ -254,11 +254,13 @@ static int STM_LoadPatterns(void)
static int STM_Load(int curious)
{
+ int blankpattern=0;
+ int pattoload;
int t;
- ULONG MikMod_ISA; /* We must generate our own ISA, it's not stored in stm */
+ ULONG samplestart;
+ ULONG sampleend;
SAMPLE *q;
(void)curious;
-
/* try to read stm header */
_mm_read_string(mh->songname,20,modreader);
_mm_read_string(mh->trackername,8,modreader);
@@ -318,22 +320,38 @@ static int STM_Load(int curious)
t=0;
if(!AllocPositions(0x80)) return 0;
/* 99 terminates the patorder list */
- while((mh->patorder[t]<=99)&&(mh->patorder[t]<mh->numpat)) {
+ while(mh->patorder[t]<99) {
of.positions[t]=mh->patorder[t];
+
+ /* Screamtracker 2 treaks patterns >= numpat as blank patterns.
+ * Example modules: jimmy.stm, Rauno/dogs.stm, Skaven/hevijanis istu maas.stm.
+ *
+ * Patterns>=64 have unpredictable behavior in Screamtracker 2,
+ * but nothing seems to rely on them, so they're OK to blank too.
+ */
+ if(of.positions[t]>=mh->numpat) {
+ of.positions[t]=mh->numpat;
+ blankpattern=1;
+ }
+
if(++t == 0x80) {
_mm_errno = MMERR_NOT_A_MODULE;
return 0;
}
}
- if(mh->patorder[t]<=99) t++;
+ /* Allocate an extra blank pattern if the module references one. */
+ pattoload=of.numpat;
+ if(blankpattern) of.numpat++;
of.numpos=t;
of.numtrk=of.numpat*of.numchn;
of.numins=of.numsmp=31;
if(!AllocSamples()) return 0;
- if(!STM_LoadPatterns()) return 0;
- MikMod_ISA=_mm_ftell(modreader);
- MikMod_ISA=(MikMod_ISA+15)&0xfffffff0; /* normalize */
+ if(!STM_LoadPatterns(pattoload)) return 0;
+
+ samplestart=_mm_ftell(modreader);
+ _mm_fseek(modreader,0,SEEK_END);
+ sampleend=_mm_ftell(modreader);
for(q=of.samples,t=0;t<of.numsmp;t++,q++) {
/* load sample info */
@@ -341,19 +359,46 @@ static int STM_Load(int curious)
q->speed = (mh->sample[t].c2spd * 8363) / 8448;
q->volume = mh->sample[t].volume;
q->length = mh->sample[t].length;
- if (/*!mh->sample[t].volume || */q->length==1) q->length=0;
+ if (!mh->sample[t].volume || q->length==1) q->length=0;
q->loopstart = mh->sample[t].loopbeg;
q->loopend = mh->sample[t].loopend;
- q->seekpos = MikMod_ISA;
+ q->seekpos = mh->sample[t].reserved << 4;
- MikMod_ISA+=q->length;
- MikMod_ISA=(MikMod_ISA+15)&0xfffffff0; /* normalize */
+ /* Sanity checks to make sure samples are bounded within the file. */
+ if(q->length) {
+ if(q->seekpos<samplestart) {
+#ifdef MIKMOD_DEBUG
+ fprintf(stderr,"rejected sample # %d (seekpos=%u < samplestart=%u)\n",t,q->seekpos,samplestart);
+#endif
+ _mm_errno = MMERR_LOADING_SAMPLEINFO;
+ return 0;
+ }
+ /* Some .STMs seem to rely on allowing truncated samples... */
+ if(q->seekpos>=sampleend) {
+#ifdef MIKMOD_DEBUG
+ fprintf(stderr,"truncating sample # %d from length %u to 0\n",t,q->length);
+#endif
+ q->seekpos = q->length = 0;
+ } else if(q->seekpos+q->length>sampleend) {
+#ifdef MIKMOD_DEBUG
+ fprintf(stderr,"truncating sample # %d from length %u to %u\n",t,q->length,sampleend - q->seekpos);
+#endif
+ q->length = sampleend - q->seekpos;
+ }
+ }
+ else
+ q->seekpos = 0;
/* contrary to the STM specs, sample data is signed */
q->flags = SF_SIGNED;
- if(q->loopend && q->loopend != 0xffff)
- q->flags|=SF_LOOP;
+ if(q->loopend && q->loopend != 0xffff && q->loopstart < q->length) {
+ q->flags|=SF_LOOP;
+ if (q->loopend > q->length)
+ q->loopend = q->length;
+ }
+ else
+ q->loopstart = q->loopend = 0;
}
return 1;
}
diff --git a/apps/plugins/mikmod/load_ult.c b/apps/plugins/mikmod/load_ult.c
index 1d4e5cf72b..58c66d16aa 100644
--- a/apps/plugins/mikmod/load_ult.c
+++ b/apps/plugins/mikmod/load_ult.c
@@ -20,8 +20,6 @@
/*==============================================================================
- $Id$
-
Ultratracker (ULT) module loader
==============================================================================*/
@@ -183,17 +181,19 @@ static int ULT_Load(int curious)
}
q->samplename=DupStr(s.samplename,32,1);
- /* The correct formula for the coefficient would be
- pow(2,(double)s.finetume/OCTAVE/32768), but to avoid floating point
- here, we'll use a first order approximation here.
+ /* The correct formula would be
+ s.speed * pow(2, (double)s.finetune / (OCTAVE * 32768))
+ but to avoid libm, we'll use a first order approximation.
1/567290 == Ln(2)/OCTAVE/32768 */
- q->speed=s.speed+s.speed*(((SLONG)s.speed*(SLONG)s.finetune)/567290);
+ if(!s.finetune) q->speed = s.speed;
+ else q->speed= s.speed*((double)s.finetune/567290.0 + 1.0);
q->length = s.sizeend-s.sizestart;
q->volume = s.volume>>2;
q->loopstart = s.loopstart;
q->loopend = s.loopend;
q->flags = SF_SIGNED;
if(s.flags&ULTS_LOOP) q->flags|=SF_LOOP;
+ else q->loopstart = q->loopend = 0;
if(s.flags&ULTS_16BITS) {
s.sizeend+=(s.sizeend-s.sizestart);
s.sizestart<<=1;
@@ -246,6 +246,11 @@ static int ULT_Load(int curious)
for(t=0;t<of.numtrk;t++) {
int rep,row=0;
+ /* FIXME: unrolling continuous portamento is a HACK and needs to
+ * be replaced with a real continuous effect. This implementation
+ * breaks when tone portamento continues between patterns. See
+ * discussion in https://github.com/sezero/mikmod/pull/40 . */
+ int continuePortaToNote = 0;
UniReset();
while(row<64) {
@@ -261,14 +266,22 @@ static int ULT_Load(int curious)
int offset;
if(ev.sample) UniInstrument(ev.sample-1);
- if(ev.note) UniNote(ev.note+2*OCTAVE-1);
+ if(ev.note) {
+ UniNote(ev.note+2*OCTAVE-1);
+ continuePortaToNote = 0;
+ }
/* first effect - various fixes by Alexander Kerkhove and
Thomas Neumann */
eff = ev.eff>>4;
+
+ if (continuePortaToNote && (eff != 0x3) && ((ev.eff & 0xf) != 0x3))
+ UniEffect(UNI_ITEFFECTG, 0);
+
switch(eff) {
case 0x3: /* tone portamento */
UniEffect(UNI_ITEFFECTG,ev.dat2);
+ continuePortaToNote = 1;
break;
case 0x5:
break;
@@ -293,6 +306,7 @@ static int ULT_Load(int curious)
switch(eff) {
case 0x3: /* tone portamento */
UniEffect(UNI_ITEFFECTG,ev.dat1);
+ continuePortaToNote = 1;
break;
case 0x5:
break;
@@ -344,5 +358,4 @@ MIKMODAPI MLOADER load_ult={
ULT_LoadTitle
};
-
/* ex:set ts=4: */
diff --git a/apps/plugins/mikmod/load_umx.c b/apps/plugins/mikmod/load_umx.c
index 1a0535affd..c817145158 100644
--- a/apps/plugins/mikmod/load_umx.c
+++ b/apps/plugins/mikmod/load_umx.c
@@ -29,12 +29,7 @@
*
* UPKG parsing partially based on Unreal Media Ripper (UMR) v0.3
* by Andy Ward <wardwh@swbell.net>, with additional updates
- * by O. Sezer - see git repo at https://github.com/sezero/umr/
- *
- * The cheaper way, i.e. linear search of music object like libxmp
- * and libmodplug does, is possible. With this however we're using
- * the embedded offset, size and object type directly from the umx
- * file, and I feel safer with it.
+ * by O. Sezer - see git repo at https://github.com/sezero/umr.git
*/
#ifdef HAVE_CONFIG_H
@@ -79,11 +74,10 @@ struct upkg_hdr {
ULONG guid[4];
SLONG generation_count;
#define UPKG_HDR_SIZE 64 /* 64 bytes up until here */
- /*struct _genhist *gen;*/
+ struct _genhist *gen;
};
/* compile time assert for upkg_hdr size */
-/*typedef int _check_hdrsize[2 * (offsetof(struct upkg_hdr, gen) == UPKG_HDR_SIZE) - 1];*/
-typedef int _check_hdrsize[2 * (sizeof(struct upkg_hdr) == UPKG_HDR_SIZE) - 1];
+typedef int _check_hdrsize[2 * (offsetof(struct upkg_hdr, gen) == UPKG_HDR_SIZE) - 1];
/*========== Supported content types */
@@ -145,6 +139,7 @@ static int get_objtype (SLONG ofs, int type)
{
char sig[16];
_retry:
+ memset(sig, 0, sizeof(sig));
_mm_fseek(modreader, ofs, SEEK_SET);
_mm_read_UBYTES(sig, 16, modreader);
if (type == UMUSIC_IT) {
@@ -207,20 +202,21 @@ static int read_export (const struct upkg_hdr *hdr,
}
static int read_typname(const struct upkg_hdr *hdr,
- int idx, char *out)
+ int idx, char *out, long end)
{
int i, s;
long l;
char buf[64];
if (idx >= hdr->name_count) return -1;
- buf[63] = '\0';
+ memset(buf, 0, 64);
for (i = 0, l = 0; i <= idx; i++) {
+ if (hdr->name_offset + l >= end) return -1;
_mm_fseek(modreader, hdr->name_offset + l, SEEK_SET);
_mm_read_UBYTES(buf, 63, modreader);
if (hdr->file_version >= 64) {
s = *(signed char *)buf; /* numchars *including* terminator */
- if (s <= 0 || s > 64) return -1;
+ if (s <= 0) return -1;
l += s + 5; /* 1 for buf[0], 4 for int32_t name_flags */
} else {
l += (long)strlen(buf);
@@ -244,6 +240,12 @@ static int probe_umx (const struct upkg_hdr *hdr,
_mm_fseek(modreader, 0, SEEK_END);
fsiz = _mm_ftell(modreader);
+ if (hdr->name_offset >= fsiz ||
+ hdr->export_offset >= fsiz ||
+ hdr->import_offset >= fsiz) {
+ return -1;
+ }
+
/* Find the offset and size of the first IT, S3M or XM
* by parsing the exports table. The umx files should
* have only one export. Kran32.umx from Unreal has two,
@@ -267,7 +269,7 @@ static int probe_umx (const struct upkg_hdr *hdr,
if ((t = read_export(hdr, &pos, &s)) < 0) return -1;
if (s <= 0 || s > fsiz - pos) return -1;
- if (read_typname(hdr, t, buf) < 0) return -1;
+ if (read_typname(hdr, t, buf, fsiz) < 0) return -1;
for (i = 0; mustype[i] != NULL; i++) {
if (!strcasecmp(buf, mustype[i])) {
t = i;
@@ -282,33 +284,34 @@ static int probe_umx (const struct upkg_hdr *hdr,
return t;
}
-static SLONG probe_header (void *header)
+static SLONG probe_header (struct upkg_hdr *hdr)
{
- struct upkg_hdr *hdr;
- unsigned char *p;
- ULONG *swp;
- int i;
-
- /* byte swap the header - all members are 32 bit LE values */
- p = (unsigned char *) header;
- swp = (ULONG *) header;
- for (i = 0; i < UPKG_HDR_SIZE/4; i++, p += 4) {
- swp[i] = p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24);
- }
-
- hdr = (struct upkg_hdr *) header;
+ hdr->tag = _mm_read_I_ULONG(modreader);
+ hdr->file_version = _mm_read_I_SLONG(modreader);
+ hdr->pkg_flags = _mm_read_I_ULONG(modreader);
+ hdr->name_count = _mm_read_I_SLONG(modreader);
+ hdr->name_offset = _mm_read_I_SLONG(modreader);
+ hdr->export_count = _mm_read_I_SLONG(modreader);
+ hdr->export_offset = _mm_read_I_SLONG(modreader);
+ hdr->import_count = _mm_read_I_SLONG(modreader);
+ hdr->import_offset = _mm_read_I_SLONG(modreader);
+
+ if (_mm_eof(modreader)) return -1;
if (hdr->tag != UPKG_HDR_TAG) {
return -1;
}
if (hdr->name_count < 0 ||
- hdr->name_offset < 0 ||
hdr->export_count < 0 ||
- hdr->export_offset < 0 ||
hdr->import_count < 0 ||
- hdr->import_offset < 0 ) {
+ hdr->name_offset < 36 ||
+ hdr->export_offset < 36 ||
+ hdr->import_offset < 36 ) {
return -1;
}
+#if 1 /* no need being overzealous */
+ return 0;
+#else
switch (hdr->file_version) {
case 35: case 37: /* Unreal beta - */
case 40: case 41: /* 1998 */
@@ -324,30 +327,20 @@ static SLONG probe_header (void *header)
}
return -1;
+#endif /* #if 0 */
}
static int process_upkg (SLONG *ofs, SLONG *objsize)
{
- char header[UPKG_HDR_SIZE];
+ struct upkg_hdr header;
- if (!_mm_read_UBYTES(header, UPKG_HDR_SIZE, modreader))
- return -1;
- if (probe_header(header) < 0)
+ memset(&header, 0, sizeof(header));
+ if (probe_header(&header) < 0)
return -1;
- return probe_umx((struct upkg_hdr *)header, ofs, objsize);
+ return probe_umx(&header, ofs, objsize);
}
-/*========== Loader vars */
-
-typedef struct _umx_info {
- int type;
- SLONG ofs, size;
- MLOADER* loader;
-} umx_info;
-
-static umx_info *umx_data = NULL;
-
/*========== Loader code */
/* Without Test() being called first, Load[Title] is never called.
@@ -359,15 +352,23 @@ static umx_info *umx_data = NULL;
* and always clear it when returning from LoadTitle() or Cleanup().
*/
+typedef struct _umx_info {
+ int type;
+ SLONG ofs, size;
+ MLOADER* loader;
+} umx_info;
+
+static umx_info *umx_data = NULL;
+
static int UMX_Test(void)
{
int type;
SLONG ofs = 0, size = 0;
if (umx_data) {
-#ifdef MIKMOD_DEBUG
+ #ifdef MIKMOD_DEBUG
fprintf(stderr, "UMX_Test called while a previous instance is active\n");
-#endif
+ #endif
MikMod_free(umx_data);
umx_data = NULL;
}
diff --git a/apps/plugins/mikmod/load_uni.c b/apps/plugins/mikmod/load_uni.c
index 834fd3a85a..569422470b 100644
--- a/apps/plugins/mikmod/load_uni.c
+++ b/apps/plugins/mikmod/load_uni.c
@@ -201,7 +201,7 @@ static UBYTE* readtrack(void)
opcode++;
}
- if((!opcode)||(opcode>=UNI_LAST)) {
+ if((!opcode)||(opcode>=UNI_FORMAT_LAST)) {
MikMod_free(t);
return NULL;
}
@@ -477,14 +477,23 @@ static int loadsmp5(void)
/* convert flags */
q->flags=0;
- if(s->flags&128) q->flags|=SF_REVERSE;
- if(s->flags& 64) q->flags|=SF_SUSTAIN;
- if(s->flags& 32) q->flags|=SF_BIDI;
- if(s->flags& 16) q->flags|=SF_LOOP;
- if(s->flags& 8) q->flags|=SF_BIG_ENDIAN;
- if(s->flags& 4) q->flags|=SF_DELTA;
- if(s->flags& 2) q->flags|=SF_SIGNED;
- if(s->flags& 1) q->flags|=SF_16BITS;
+ if (universion < 5) {
+ /* UN04 flags, as suggested by Thomas Neumann */
+ if(s->flags& 64) q->flags|=SF_OWNPAN;
+ if(s->flags& 32) q->flags|=SF_SIGNED;
+ if(s->flags& 16) q->flags|=SF_BIDI;
+ if(s->flags& 8) q->flags|=SF_LOOP;
+ if(s->flags& 4) q->flags|=SF_DELTA;
+ } else {
+ if(s->flags&128) q->flags|=SF_REVERSE;
+ if(s->flags& 64) q->flags|=SF_OWNPAN;
+ if(s->flags& 32) q->flags|=SF_BIDI;
+ if(s->flags& 16) q->flags|=SF_LOOP;
+ if(s->flags& 8) q->flags|=SF_BIG_ENDIAN;
+ if(s->flags& 4) q->flags|=SF_DELTA;
+ if(s->flags& 2) q->flags|=SF_SIGNED;
+ if(s->flags& 1) q->flags|=SF_16BITS;
+ }
}
d=of.instruments;s=wh;
@@ -574,6 +583,8 @@ static int UNI_Load(int curious)
of.bpmlimit=32;
of.songname=readstring();
+ if(!of.songname)
+ of.songname=MikMod_strdup("");
if(universion<0x102)
oldtype=readstring();
if(oldtype) {
@@ -698,13 +709,16 @@ static CHAR *UNI_LoadTitle(void)
{
UBYTE ver;
int posit[3]={304,306,26};
+ CHAR *title;
_mm_fseek(modreader,3,SEEK_SET);
ver=_mm_read_UBYTE(modreader);
if(ver=='N') ver='6';
_mm_fseek(modreader,posit[ver-'4'],SEEK_SET);
- return readstring();
+ title=readstring();
+ if(!title) title=MikMod_strdup("");
+ return title;
}
/*========== Loader information */
diff --git a/apps/plugins/mikmod/load_xm.c b/apps/plugins/mikmod/load_xm.c
index 3eb0803668..bdeca11b34 100644
--- a/apps/plugins/mikmod/load_xm.c
+++ b/apps/plugins/mikmod/load_xm.c
@@ -73,7 +73,8 @@ typedef struct XMINSTHEADER {
ULONG ssize;
} XMINSTHEADER;
-#define XMENVCNT (12*2)
+#define XMENVPTS (12)
+#define XMENVCNT (XMENVPTS*2)
#define XMNOTECNT (8*OCTAVE)
typedef struct XMPATCHHEADER {
UBYTE what[XMNOTECNT]; /* Sample number for all notes */
@@ -442,6 +443,12 @@ static void FixEnvelope(ENVPT *cur, int pts)
}
}
+/* Check for MOD plugin packed samples */
+static int IsSamplePacked(void)
+{
+ return (s->reserved == 0xad) && !(s->type & 0x30);
+}
+
static int LoadInstruments(void)
{
long filend,ck;
@@ -513,10 +520,10 @@ static int LoadInstruments(void)
/* we can't trust the envelope point count here, as some
modules have incorrect values (K_OSPACE.XM reports 32 volume
points, for example). */
- if(pth.volpts>XMENVCNT/2) pth.volpts=XMENVCNT/2;
- if(pth.panpts>XMENVCNT/2) pth.panpts=XMENVCNT/2;
+ if(pth.volpts>XMENVPTS) pth.volpts=XMENVPTS;
+ if(pth.panpts>XMENVPTS) pth.panpts=XMENVPTS;
- if((_mm_eof(modreader))||(pth.volpts>XMENVCNT/2)||(pth.panpts>XMENVCNT/2)) {
+ if(_mm_eof(modreader)) {
MikMod_free(nextwav);nextwav=NULL;
MikMod_free(wh);wh=NULL;
_mm_errno = MMERR_LOADING_SAMPLEINFO;
@@ -529,7 +536,7 @@ static int LoadInstruments(void)
#if defined __STDC__ || defined _MSC_VER || defined __WATCOMC__ || defined MPW_C
#define XM_ProcessEnvelope(name) \
- for (u = 0; u < (XMENVCNT >> 1); u++) { \
+ for (u = 0; u < XMENVPTS; u++) { \
d-> name##env[u].pos = pth. name##env[u << 1]; \
d-> name##env[u].val = pth. name##env[(u << 1)+ 1]; \
} \
@@ -542,14 +549,14 @@ static int LoadInstruments(void)
d-> name##pts=pth. name##pts; \
\
/* scale envelope */ \
- for (p=0;p<XMENVCNT/2;p++) \
+ for (p = 0; p < XMENVPTS; p++) \
d-> name##env[p].val<<=2; \
\
if ((d-> name##flg&EF_ON)&&(d-> name##pts<2)) \
d-> name##flg&=~EF_ON
#else
#define XM_ProcessEnvelope(name) \
- for (u = 0; u < (XMENVCNT >> 1); u++) { \
+ for (u = 0; u < XMENVPTS; u++) { \
d-> name/**/env[u].pos = pth. name/**/env[u << 1]; \
d-> name/**/env[u].val = pth. name/**/env[(u << 1)+ 1]; \
} \
@@ -563,7 +570,7 @@ static int LoadInstruments(void)
d-> name/**/pts=pth. name/**/pts; \
\
/* scale envelope */ \
- for (p=0;p<XMENVCNT/2;p++) \
+ for (p = 0; p < XMENVPTS; p++) \
d-> name/**/env[p].val<<=2; \
\
if ((d-> name/**/flg&EF_ON)&&(d-> name/**/pts<2)) \
@@ -625,7 +632,11 @@ static int LoadInstruments(void)
_mm_read_string(s->samplename, 22, modreader);
nextwav[of.numsmp+u]=next;
- next+=s->length;
+
+ if(IsSamplePacked())
+ next+=((s->length + 1) >> 1) + 16;
+ else
+ next+=s->length;
}
if(mh->version>0x0103) {
@@ -793,6 +804,11 @@ static int XM_Load(int curious)
if(s->type&0x3) q->flags|=SF_LOOP;
if(s->type&0x2) q->flags|=SF_BIDI;
if(s->type&0x10) q->flags|=SF_16BITS;
+
+ if(IsSamplePacked()) {
+ q->flags &= ~SF_DELTA;
+ q->flags |= SF_ADPCM4;
+ }
}
d=of.instruments;
diff --git a/apps/plugins/mikmod/mdriver.c b/apps/plugins/mikmod/mdriver.c
index 2e1f8063b9..771bc3d061 100644
--- a/apps/plugins/mikmod/mdriver.c
+++ b/apps/plugins/mikmod/mdriver.c
@@ -187,7 +187,7 @@ MIKMODAPI CHAR* MikMod_InfoDriver(void)
MUTEX_LOCK(lists);
/* compute size of buffer */
for(l = firstdriver; l; l = l->next)
- len += 4 + (l->next ? 1 : 0) + strlen(l->Version);
+ len += 4 + 1 + strlen(l->Version);
if(len)
if((list=(CHAR*)MikMod_malloc(len*sizeof(CHAR))) != NULL) {
@@ -195,7 +195,8 @@ MIKMODAPI CHAR* MikMod_InfoDriver(void)
list[0] = 0;
/* list all registered device drivers : */
for(t = 1, l = firstdriver; l; l = l->next, t++) {
- list_end += sprintf(list_end, "%2d %s%s", t, l->Version, (l->next)? "\n" : "");
+ list_end += sprintf(list_end, "%2d %s\n", t, l->Version);
+ if (!l->next) list_end[-1] = 0;
}
}
MUTEX_UNLOCK(lists);
diff --git a/apps/plugins/mikmod/mikmod.c b/apps/plugins/mikmod/mikmod.c
index 86911de64e..0cddbf3282 100644
--- a/apps/plugins/mikmod/mikmod.c
+++ b/apps/plugins/mikmod/mikmod.c
@@ -45,6 +45,7 @@ static int curfile = 0, direction = DIR_NEXT, entries = 0;
/* list of the mod files */
static char **file_pt;
+static int inited = 0;
/* The MP3 audio buffer which we will use as heap memory */
static unsigned char* audio_buffer;
@@ -532,7 +533,7 @@ static void applysettings(void)
}
#endif
- if (md_mixfreq != rb->hw_freq_sampr[settings.sample_rate]) {
+ if (inited && (md_mixfreq != rb->hw_freq_sampr[settings.sample_rate])) {
md_mixfreq = rb->hw_freq_sampr[settings.sample_rate];
// MikMod_Reset(""); BROKEN!
rb->pcm_play_stop();
@@ -993,6 +994,8 @@ enum plugin_status plugin_start(const void* parameter)
return PLUGIN_ERROR;
}
+ inited = 1;
+
do
{
retval = playfile(np_file);
diff --git a/apps/plugins/mikmod/mikmod.h b/apps/plugins/mikmod/mikmod.h
index 8256299451..36b39c15c2 100644
--- a/apps/plugins/mikmod/mikmod.h
+++ b/apps/plugins/mikmod/mikmod.h
@@ -41,7 +41,7 @@ extern "C" {
*
* ========== NOTE TO WINDOWS DEVELOPERS:
* If you are compiling for Windows and will link to the static library
- * (libmikmod.a with MinGW, or mikmod_static.lib with MSVC or LCC, etc),
+ * (libmikmod.a with MinGW, or mikmod_static.lib with MSVC, Watcom, ..),
* you must define MIKMOD_STATIC in your project. Otherwise, dllimport
* will be assumed.
*/
@@ -76,7 +76,7 @@ extern "C" {
#define LIBMIKMOD_VERSION_MAJOR 3L
#define LIBMIKMOD_VERSION_MINOR 3L
-#define LIBMIKMOD_REVISION 11L
+#define LIBMIKMOD_REVISION 12L
#define LIBMIKMOD_VERSION \
((LIBMIKMOD_VERSION_MAJOR<<16)| \
@@ -89,7 +89,7 @@ MIKMODAPI extern long MikMod_GetVersion(void);
* ========== Dependency platform headers
*/
-#ifdef _WIN32
+#if defined(_WIN32)||defined(__CYGWIN__)
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
@@ -136,10 +136,8 @@ typedef unsigned char UBYTE;
#endif
/* 2 bytes, signed and unsigned: */
-#if !(defined __LCC__ && defined _WIN32)
typedef signed short int SWORD;
-#endif
-#if !((defined __LCC__ && defined _WIN32) || defined(_MIKMOD_AMIGA))
+#if !defined(_MIKMOD_AMIGA)
typedef unsigned short int UWORD;
#endif
@@ -147,13 +145,13 @@ typedef unsigned short int UWORD;
#if defined(_LP64) || defined(__LP64__) || defined(__arch64__) || defined(__alpha) || defined(__x86_64) || defined(__powerpc64__)
/* 64 bit architectures: */
typedef signed int SLONG;
-#if !(defined(_WIN32) || defined(_MIKMOD_AMIGA))
+#if !defined(_MIKMOD_AMIGA)
typedef unsigned int ULONG;
#endif
#else /* 32 bit architectures: */
typedef signed long int SLONG;
-#if !(defined(_MIKMOD_OS2) || defined(_MIKMOD_WIN32) || defined(_MIKMOD_AMIGA))
+#if !(defined(_MIKMOD_OS2) || defined(_MIKMOD_AMIGA))
typedef unsigned long int ULONG;
#endif
#endif
@@ -254,14 +252,14 @@ enum {
MMERR_MAC_SPEED,
MMERR_MAC_START,
- MMERR_OSX_UNKNOWN_DEVICE,
- MMERR_OSX_BAD_PROPERTY,
+ MMERR_OSX_UNKNOWN_DEVICE, /* obsolete */
+ MMERR_OSX_BAD_PROPERTY, /* obsolete */
MMERR_OSX_UNSUPPORTED_FORMAT,
- MMERR_OSX_SET_STEREO,
- MMERR_OSX_BUFFER_ALLOC,
- MMERR_OSX_ADD_IO_PROC,
+ MMERR_OSX_SET_STEREO, /* obsolete */
+ MMERR_OSX_BUFFER_ALLOC, /* obsolete */
+ MMERR_OSX_ADD_IO_PROC, /* obsolete */
MMERR_OSX_DEVICE_START,
- MMERR_OSX_PTHREAD,
+ MMERR_OSX_PTHREAD, /* obsolete */
MMERR_DOSWSS_STARTDMA,
MMERR_DOSSB_STARTDMA,
@@ -376,8 +374,9 @@ typedef struct MWRITER {
#define SF_BIG_ENDIAN 0x0008
#define SF_DELTA 0x0010
#define SF_ITPACKED 0x0020
+#define SF_ADPCM4 0x0040
-#define SF_FORMATMASK 0x003F
+#define SF_FORMATMASK 0x007F
/* General Playback flags */
@@ -551,6 +550,7 @@ struct MP_VOICE;
#define UF_FT2QUIRKS 0x0200 /* emulate some FT2 replay quirks */
#define UF_PANNING 0x0400 /* module uses panning effects or have
non-tracker default initial panning */
+#define UF_FARTEMPO 0x0800 /* Module uses Farandole tempo calculations */
typedef struct MODULE {
/* general module information */
diff --git a/apps/plugins/mikmod/mikmod_internals.h b/apps/plugins/mikmod/mikmod_internals.h
index e6ba1875c0..f7ca8a1741 100644
--- a/apps/plugins/mikmod/mikmod_internals.h
+++ b/apps/plugins/mikmod/mikmod_internals.h
@@ -70,14 +70,11 @@ typedef long long SLONGLONG;
#endif
typedef int __s64_typetest [(sizeof(SLONGLONG)==8) * 2 - 1];
-/* pointer-sized signed int (ssize_t/intptr_t) : */
-#if defined(_WIN64) /* win64 is LLP64, not LP64 */
-typedef long long SINTPTR_T;
-#else
-/* long should be pointer-sized for all others : */
-typedef long SINTPTR_T;
+/* signed size type (ssize_t) */
+#if !defined(_WIN32) /* Win32 SDK has SSIZE_T */
+typedef long SSIZE_T;
#endif
-typedef int __iptr_typetest [(sizeof(SINTPTR_T)==sizeof(void*)) * 2 - 1];
+typedef int __ssize_typetest [(sizeof(SSIZE_T)==sizeof(size_t)) * 2 - 1];
/*========== Error handling */
@@ -106,7 +103,7 @@ extern MikMod_handler_t _mm_errorhandler;
if(_mm_mutex_##name)\
DosReleaseMutexSem(_mm_mutex_##name)
-#elif defined(_WIN32)
+#elif defined(_WIN32)||defined(__CYGWIN__)
#include <windows.h>
#define DECLARE_MUTEX(name) \
extern HANDLE _mm_mutex_##name
@@ -338,6 +335,35 @@ enum {
/* Oktalyzer effects */
UNI_OKTARP, /* arpeggio */
+ /* Last effect supported by old modules in the UNI format. */
+ UNI_FORMAT_LAST,
+
+ /* Scream Tracker effects */
+ UNI_S3MEFFECTH, /* vibrato */
+ /* Impulse Tracker effects */
+ UNI_ITEFFECTH_OLD, /* vibrato (old) */
+ UNI_ITEFFECTU_OLD, /* fine vibrato (old) */
+ /* GDM effects. */
+ UNI_GDMEFFECT4, /* vibrato */
+ UNI_GDMEFFECT7, /* tremolo */
+ UNI_GDMEFFECT14, /* fine vibrato */
+ /* OctaMED effects. */
+ UNI_MEDEFFECT_VIB, /* MED vibrato */
+ UNI_MEDEFFECT_FD, /* set pitch */
+ UNI_MEDEFFECT_16, /* loop */
+ UNI_MEDEFFECT_18, /* stop note */
+ UNI_MEDEFFECT_1E, /* pattern delay */
+ UNI_MEDEFFECT_1F, /* note delay and retrigger */
+ /* Farandole effects. */
+ UNI_FAREFFECT1, /* Porta up */
+ UNI_FAREFFECT2, /* Porta down */
+ UNI_FAREFFECT3, /* Porta to note */
+ UNI_FAREFFECT4, /* Retrigger */
+ UNI_FAREFFECT6, /* Vibrato */
+ UNI_FAREFFECTD, /* Fine tempo down */
+ UNI_FAREFFECTE, /* Fine tempo up */
+ UNI_FAREFFECTF, /* Set tempo */
+
UNI_LAST
};
@@ -503,6 +529,16 @@ typedef struct MP_CONTROL {
UBYTE s3mrtgspeed;/* last used retrig speed */
UBYTE s3mrtgslide;/* last used retrig slide */
+ UBYTE fartoneportarunning; /* FAR tone porta (effect 3) is a little bit different than other effects. It should keep running when the effect has first started, even if it is not given on subsequently rows */
+ SLONG fartoneportaspeed; /* FAR tone porta increment value */
+ SLONG farcurrentvalue; /* Because we're using fixing points as speed and the current period is an integer, we need to store the current value here for next round */
+ UBYTE farretrigcount; /* Number of retrigs to do */
+
+ /* These variables are only stored on the first control instance and therefore used globally.
+ The reason they are stored here is to minimize the number of global variables. */
+ UBYTE farcurtempo; /* Farandole current speed */
+ SWORD fartempobend; /* Used by the Farandole fine tempo effects and store the current bend value */
+
UBYTE glissando; /* glissando (0 means off) */
UBYTE wavecontrol;
@@ -533,7 +569,8 @@ typedef struct MP_CONTROL {
SBYTE panbspd; /* "" speed */
UBYTE panbdepth; /* "" depth */
- UWORD newsamp; /* set to 1 upon a sample / inst change */
+ UBYTE newnote; /* set to 1 if the current row contains a note */
+ UBYTE newsamp; /* set to 1 upon a sample / inst change */
UBYTE voleffect; /* Volume Column Effect Memory as used by IT */
UBYTE voldata; /* Volume Column Data Memory */
@@ -748,7 +785,7 @@ extern MikMod_callback_t vc_callback;
#if defined(_WIN64)
# if defined(_MSC_VER)
# define IS_ALIGNED_16(ptr) (!((__int64)(ptr) & 15i64))
-# else /* GCC, LCC, .. */
+# else /* GCC, etc. */
# define IS_ALIGNED_16(ptr) (!((long long)(ptr) & 15LL))
# endif
#else /* long cast should be OK for all else */
@@ -860,7 +897,7 @@ void MikMod_afree(void *); /* frees if ptr != NULL */
#endif
#else /* NO SIMD */
-#define MikMod_amalloc MikMod_malloc
+#define MikMod_amalloc(s) MikMod_calloc(1,(s))
#define MikMod_afree MikMod_free
#endif
diff --git a/apps/plugins/mikmod/mloader.c b/apps/plugins/mikmod/mloader.c
index 9ea9489272..4634c85ccf 100644
--- a/apps/plugins/mikmod/mloader.c
+++ b/apps/plugins/mikmod/mloader.c
@@ -115,18 +115,20 @@ MIKMODAPI void MikMod_RegisterLoader(struct MLOADER* ldr)
int ReadComment(UWORD len)
{
if(len) {
- int i;
+ CHAR *ptr;
- if(!(of.comment=(CHAR*)MikMod_malloc(len+1))) return 0;
+ of.comment=(CHAR*)MikMod_calloc(1,len+1);
+ if(!of.comment) return 0;
_mm_read_UBYTES(of.comment,len,modreader);
/* translate IT linefeeds */
- for(i=0;i<len;i++)
- if(of.comment[i]=='\r') of.comment[i]='\n';
-
- of.comment[len]=0; /* just in case */
+ ptr=of.comment;
+ while(*ptr) {
+ if(*ptr=='\r') *ptr='\n';
+ ++ptr;
+ }
}
- if(!of.comment[0]) {
+ if(of.comment && !of.comment[0]) {
MikMod_free(of.comment);
of.comment=NULL;
}
@@ -142,16 +144,17 @@ int ReadLinedComment(UWORD len,UWORD linelen)
if (!linelen) return 0;
if (!len) return 1;
- if (!(buf = (CHAR *) MikMod_malloc(len))) return 0;
numlines = (len + linelen - 1) / linelen;
cnt = (linelen + 1) * numlines;
- if (!(storage = (CHAR *) MikMod_malloc(cnt + 1))) {
+ buf = (CHAR *) MikMod_calloc(1, len);
+ if (!buf) return 0;
+ storage = (CHAR *) MikMod_calloc(1, cnt + 1);
+ if (!storage) {
MikMod_free(buf);
return 0;
}
_mm_read_UBYTES(buf,len,modreader);
- storage[cnt] = 0;
for (line = 0, fpos = 0, cpos = 0; line < numlines; line++, fpos += linelen, cpos += (linelen + 1))
{
cnt = len - fpos;
diff --git a/apps/plugins/mikmod/mlutil.c b/apps/plugins/mikmod/mlutil.c
index 328b1d4089..ce9fbfb6fd 100644
--- a/apps/plugins/mikmod/mlutil.c
+++ b/apps/plugins/mikmod/mlutil.c
@@ -182,9 +182,12 @@ void S3MIT_ProcessCmd(UBYTE cmd, UBYTE inf, unsigned int flags)
UniEffect(UNI_ITEFFECTG,inf);
break;
case 8: /* Hxy vibrato */
- if (flags & S3MIT_OLDSTYLE)
- UniPTEffect(0x4,inf);
- else
+ if (flags & S3MIT_OLDSTYLE) {
+ if (flags & S3MIT_IT)
+ UniEffect(UNI_ITEFFECTH_OLD,inf);
+ else
+ UniEffect(UNI_S3MEFFECTH,inf);
+ } else
UniEffect(UNI_ITEFFECTH,inf);
break;
case 9: /* Ixy tremor, ontime x, offtime y */
@@ -197,9 +200,12 @@ void S3MIT_ProcessCmd(UBYTE cmd, UBYTE inf, unsigned int flags)
UniPTEffect(0x0,inf);
break;
case 0xb: /* Kxy Dual command H00 & Dxy */
- if (flags & S3MIT_OLDSTYLE)
- UniPTEffect(0x4,0);
- else
+ if (flags & S3MIT_OLDSTYLE) {
+ if (flags & S3MIT_IT)
+ UniEffect(UNI_ITEFFECTH_OLD,0);
+ else
+ UniEffect(UNI_S3MEFFECTH,0);
+ } else
UniEffect(UNI_ITEFFECTH,0);
UniEffect(UNI_S3MEFFECTD,inf);
break;
@@ -211,7 +217,9 @@ void S3MIT_ProcessCmd(UBYTE cmd, UBYTE inf, unsigned int flags)
UniEffect(UNI_S3MEFFECTD,inf);
break;
case 0xd: /* Mxx Set Channel Volume */
- UniEffect(UNI_ITEFFECTM,inf);
+ /* Ignore invalid values > 64. */
+ if (inf <= 0x40)
+ UniEffect(UNI_ITEFFECTM,inf);
break;
case 0xe: /* Nxy Slide Channel Volume */
UniEffect(UNI_ITEFFECTN,inf);
@@ -259,9 +267,12 @@ void S3MIT_ProcessCmd(UBYTE cmd, UBYTE inf, unsigned int flags)
}
break;
case 0x15: /* Uxy Fine Vibrato speed x, depth y */
- if(flags & S3MIT_OLDSTYLE)
- UniEffect(UNI_S3MEFFECTU,inf);
- else
+ if(flags & S3MIT_OLDSTYLE) {
+ if (flags & S3MIT_IT)
+ UniEffect(UNI_ITEFFECTU_OLD,inf);
+ else
+ UniEffect(UNI_S3MEFFECTU,inf);
+ } else
UniEffect(UNI_ITEFFECTU,inf);
break;
case 0x16: /* Vxx Set Global Volume */
diff --git a/apps/plugins/mikmod/mmalloc.c b/apps/plugins/mikmod/mmalloc.c
index ed5fd4684c..81493ab683 100644
--- a/apps/plugins/mikmod/mmalloc.c
+++ b/apps/plugins/mikmod/mmalloc.c
@@ -20,8 +20,6 @@
/*==============================================================================
- $Id$
-
Dynamic memory routines
==============================================================================*/
@@ -106,7 +104,12 @@ void* MikMod_realloc(void *data, size_t size)
/* Same as malloc, but sets error variable _mm_error when fails */
void* MikMod_malloc(size_t size)
{
- return MikMod_calloc(1, size);
+ void *d = malloc(size);
+ if (d) return d;
+
+ _mm_errno = MMERR_OUT_OF_MEMORY;
+ if(_mm_errorhandler) _mm_errorhandler();
+ return NULL;
}
/* Same as calloc, but sets error variable _mm_error when fails */
@@ -134,8 +137,8 @@ CHAR *MikMod_strdup(const CHAR *s)
if (!s) return NULL;
l = strlen(s) + 1;
- d = (CHAR *) MikMod_calloc(1, l * sizeof(CHAR));
- if (d) strcpy(d, s);
+ d = (CHAR *) MikMod_malloc(l);
+ if (d) memcpy(d, s, l);
return d;
}
diff --git a/apps/plugins/mikmod/mmerror.c b/apps/plugins/mikmod/mmerror.c
index 4d7949890e..3e39c8fc81 100644
--- a/apps/plugins/mikmod/mmerror.c
+++ b/apps/plugins/mikmod/mmerror.c
@@ -202,14 +202,14 @@ static const char *_mm_errmsg[MMERR_MAX+1] =
/* MacOS X/Darwin driver errors */
#ifdef DRV_OSX
- "Unknown device",
- "Bad property",
+ _mmerr_invalid,
+ _mmerr_invalid,
"Could not set playback format",
- "Could not set mono/stereo setting",
- "Could not create playback buffers",
- "Could not create playback thread",
+ _mmerr_invalid,
+ _mmerr_invalid,
+ _mmerr_invalid,
"Could not start audio device",
- "Could not create buffer thread",
+ _mmerr_invalid,
#else
_mmerr_invalid, _mmerr_invalid, _mmerr_invalid,
_mmerr_invalid, _mmerr_invalid, _mmerr_invalid,
diff --git a/apps/plugins/mikmod/mplayer.c b/apps/plugins/mikmod/mplayer.c
index ee5c2d187e..afc01b789a 100644
--- a/apps/plugins/mikmod/mplayer.c
+++ b/apps/plugins/mikmod/mplayer.c
@@ -34,11 +34,7 @@
#include <string.h>
#include <stdarg.h>
-#ifdef SRANDOM_IN_MATH_H
-#include <math.h>
-#else
#include <stdlib.h>
-#endif
#include <limits.h>
@@ -56,6 +52,11 @@ MODULE *pf = NULL;
#define HIGH_OCTAVE 2 /* number of above-range octaves */
+enum vibratoflags {
+ VIB_PT_BUGS = 0x01, /* MOD vibrato is not applied on tick 0. */
+ VIB_TICK_0 = 0x02, /* Increment LFO position on tick 0. */
+};
+
static const UWORD oldperiods[OCTAVE*2]={
0x6b00, 0x6800, 0x6500, 0x6220, 0x5f50, 0x5c80,
0x5a00, 0x5740, 0x54d0, 0x5260, 0x5010, 0x4dc0,
@@ -63,20 +64,15 @@ static const UWORD oldperiods[OCTAVE*2]={
0x3f90, 0x3dc0, 0x3c10, 0x3a40, 0x38b0, 0x3700
};
-static const UBYTE VibratoTable[32]={
- 0, 24, 49, 74, 97,120,141,161,180,197,212,224,235,244,250,253,
- 255,253,250,244,235,224,212,197,180,161,141,120, 97, 74, 49, 24
-};
-
-static const UBYTE avibtab[128]={
- 0, 1, 3, 4, 6, 7, 9,10,12,14,15,17,18,20,21,23,
- 24,25,27,28,30,31,32,34,35,36,38,39,40,41,42,44,
- 45,46,47,48,49,50,51,52,53,54,54,55,56,57,57,58,
- 59,59,60,60,61,61,62,62,62,63,63,63,63,63,63,63,
- 64,63,63,63,63,63,63,63,62,62,62,61,61,60,60,59,
- 59,58,57,57,56,55,54,54,53,52,51,50,49,48,47,46,
- 45,44,42,41,40,39,38,36,35,34,32,31,30,28,27,25,
- 24,23,21,20,18,17,15,14,12,10, 9, 7, 6, 4, 3, 1
+static const UBYTE VibratoTable[128]={
+ 0, 6, 13, 19, 25, 31, 37, 44, 50, 56, 62, 68, 74, 80, 86, 92,
+ 98,103,109,115,120,126,131,136,142,147,152,157,162,167,171,176,
+ 180,185,189,193,197,201,205,208,212,215,219,222,225,228,231,233,
+ 236,238,240,242,244,246,247,249,250,251,252,253,254,254,255,255,
+ 255,255,255,254,254,253,252,251,250,249,247,246,244,242,240,238,
+ 236,233,231,228,225,222,219,215,212,208,205,201,197,193,189,185,
+ 180,176,171,167,162,157,152,147,142,136,131,126,120,115,109,103,
+ 98, 92, 86, 80, 74, 68, 62, 56, 50, 44, 37, 31, 25, 19, 13, 6,
};
/* Triton's linear periods to frequency translation table (for XM modules) */
@@ -228,6 +224,11 @@ static const SBYTE PanbrelloTable[256]={
-24,-23,-22,-20,-19,-17,-16,-14,-12,-11,- 9,- 8,- 6,- 5,- 3,- 2
};
+/* tempo[0] = 256; tempo[i] = floor(128 /i) */
+static const int far_tempos[16] = {
+ 256, 128, 64, 42, 32, 25, 21, 18, 16, 14, 12, 11, 10, 9, 9, 8
+};
+
/* returns a random value between 0 and ceil-1, ceil must be a power of two */
static int getrandom(int ceilval)
{
@@ -368,6 +369,12 @@ static SWORD StartEnvelope(ENVPR *t,UBYTE flg,UBYTE pts,UBYTE susbeg,UBYTE susen
return t->env[0].val;
}
+ /* Ignore junk loops */
+ if (beg > pts || beg > end)
+ t->flg &= ~EF_LOOP;
+ if (susbeg > pts || susbeg > susend)
+ t->flg &= ~EF_SUSTAIN;
+
/* Imago Orpheus sometimes stores an extra initial point in the envelope */
if ((t->pts>=2)&&(t->env[0].pos==t->env[1].pos)) {
t->a++;
@@ -404,6 +411,9 @@ static SWORD StartEnvelope(ENVPR *t,UBYTE flg,UBYTE pts,UBYTE susbeg,UBYTE susen
*/
static SWORD ProcessEnvelope(MP_VOICE *aout, ENVPR *t, SWORD v)
{
+ if (!t->pts) { /* happens with e.g. Vikings In The Hood!.xm */
+ return v;
+ }
if (t->flg & EF_ON) {
UBYTE a, b; /* actual points in the envelope */
UWORD p; /* the 'tick counter' - real point being played */
@@ -479,6 +489,30 @@ static SWORD ProcessEnvelope(MP_VOICE *aout, ENVPR *t, SWORD v)
return v;
}
+/* Set envelope tick to the position given */
+static void SetEnvelopePosition(ENVPR *t, ENVPT *p, SWORD pos)
+{
+ if (t->pts > 0) {
+ UWORD i;
+
+ for (i = 0; i < t->pts - 1; i++) {
+
+ if ((pos >= p[i].pos) && (pos < p[i + 1].pos)) {
+ t->a = i;
+ t->b = i + 1;
+ t->p = pos;
+ return;
+ }
+ }
+
+ /* If position is after the last envelope point, just set
+ it to the last one */
+ t->a = t->pts - 1;
+ t->b = t->pts;
+ t->p = p[t->a].pos;
+ }
+}
+
/* XM linear period to frequency conversion */
ULONG getfrequency(UWORD flags,ULONG period)
{
@@ -493,6 +527,81 @@ ULONG getfrequency(UWORD flags,ULONG period)
return (8363L*1712L)/(period?period:1);
}
+static SWORD LFOVibrato(SBYTE position, UBYTE waveform)
+{
+ SWORD amp;
+
+ switch (waveform) {
+ case 0: /* sine */
+ amp = VibratoTable[position & 0x7f];
+ return (position >= 0) ? amp : -amp;
+ case 1: /* ramp down - ramps up because MOD/S3M apply these to period. */
+ return ((UBYTE)position << 1) - 255;
+ case 2: /* square wave */
+ return (position >= 0) ? 255 : -255;
+ case 3: /* random wave */
+ return getrandom(512) - 256;
+ }
+ return 0;
+}
+
+static SWORD LFOTremolo(SBYTE position, UBYTE waveform)
+{
+ switch (waveform) {
+ case 1: /* ramp down */
+ /* Tremolo ramp down actually ramps down. */
+ return 255 - ((UBYTE)position << 1);
+ }
+ return LFOVibrato(position, waveform);
+}
+
+static SWORD LFOVibratoIT(SBYTE position, UBYTE waveform)
+{
+ switch (waveform) {
+ case 1: /* ramp down */
+ /* IT ramp down actually ramps down. */
+ return 255 - ((UBYTE)position << 1);
+ case 2: /* square wave */
+ /* IT square wave oscillates between 0 and 255. */
+ return (position >= 0) ? 255 : 0;
+ }
+ return LFOVibrato(position, waveform);
+}
+
+static SWORD LFOPanbrello(SBYTE position, UBYTE waveform)
+{
+ switch (waveform) {
+ case 0: /* sine */
+ return PanbrelloTable[(UBYTE)position];
+ case 1: /* ramp down */
+ return 64 - ((UBYTE)position >> 1);
+ case 2: /* square wave */
+ return (position >= 0) ? 64 : 0;
+ case 3: /* random */
+ return getrandom(128) - 64;
+ }
+ return 0;
+}
+
+static SWORD LFOAutoVibratoXM(SBYTE position, UBYTE waveform)
+{
+ /* XM auto-vibrato uses a different set of waveforms than vibrato/tremolo. */
+ SWORD amp;
+
+ switch (waveform) {
+ case 0: /* sine */
+ amp = VibratoTable[position & 0x7f];
+ return (position >= 0) ? amp : -amp;
+ case 1: /* square wave */
+ return (position >= 0) ? 255 : -255;
+ case 2: /* ramp down */
+ return -((SWORD)position << 1);
+ case 3: /* ramp up */
+ return (SWORD)position << 1;
+ }
+ return 0;
+}
+
/*========== Protracker effects */
static void DoArpeggio(UWORD tick, UWORD flags, MP_CONTROL *a, UBYTE style)
@@ -651,43 +760,22 @@ static int DoPTEffect3(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWOR
return 0;
}
-static void DoVibrato(UWORD tick, MP_CONTROL *a)
+static void DoVibrato(UWORD tick, MP_CONTROL *a, UWORD flags)
{
- UBYTE q;
- UWORD temp = 0; /* silence warning */
+ SWORD temp;
- if (!tick)
+ if (!tick && (flags & VIB_PT_BUGS))
return;
- q=(a->vibpos>>2)&0x1f;
-
- switch (a->wavecontrol&3) {
- case 0: /* sine */
- temp=VibratoTable[q];
- break;
- case 1: /* ramp down */
- q<<=3;
- if (a->vibpos<0) q=255-q;
- temp=q;
- break;
- case 2: /* square wave */
- temp=255;
- break;
- case 3: /* random wave */
- temp=getrandom(256);
- break;
- }
-
+ temp = LFOVibrato(a->vibpos, a->wavecontrol & 3);
temp*=a->vibdepth;
- temp>>=7;temp<<=2;
+ temp>>=7;
+ temp<<=2;
- if (a->vibpos>=0)
- a->main.period=a->tmpperiod+temp;
- else
- a->main.period=a->tmpperiod-temp;
+ a->main.period = a->tmpperiod + temp;
a->ownper = 1;
- if (tick != 0)
+ if (tick != 0 || (flags & VIB_TICK_0))
a->vibpos+=a->vibspd;
}
@@ -704,7 +792,26 @@ static int DoPTEffect4(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWOR
if (dat&0xf0) a->vibspd=(dat&0xf0)>>2;
}
if (a->main.period)
- DoVibrato(tick, a);
+ DoVibrato(tick, a, VIB_PT_BUGS);
+
+ return 0;
+}
+
+static int DoPTEffect4Fix(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
+{
+ /* PT-equivalent vibrato but without the tick 0 bug. */
+ UBYTE dat;
+ (void)flags;
+ (void)mod;
+ (void)channel;
+
+ dat=UniGetByte();
+ if (!tick) {
+ if (dat&0x0f) a->vibdepth=dat&0xf;
+ if (dat&0xf0) a->vibspd=(dat&0xf0)>>2;
+ }
+ if (a->main.period)
+ DoVibrato(tick, a, 0);
return 0;
}
@@ -741,54 +848,61 @@ static int DoPTEffect5(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWOR
/* DoPTEffect6 after DoPTEffectA */
+static void DoTremolo(UWORD tick, MP_CONTROL *a, UWORD flags)
+{
+ SWORD temp;
+
+ if (!tick && (flags & VIB_PT_BUGS))
+ return;
+
+ temp = LFOTremolo(a->trmpos, (a->wavecontrol >> 4) & 3);
+ temp*=a->trmdepth;
+ temp>>=6;
+
+ a->volume = a->tmpvolume + temp;
+ if (a->volume>64) a->volume=64;
+ if (a->volume<0) a->volume=0;
+ a->ownvol = 1;
+
+ if (tick || (flags & VIB_TICK_0))
+ a->trmpos+=a->trmspd;
+}
+
static int DoPTEffect7(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
{
UBYTE dat;
- UBYTE q;
- UWORD temp = 0; /* silence warning */
- (void)flags;
- (void)mod;
- (void)channel;
+ (void)flags;
+ (void)mod;
+ (void)channel;
dat=UniGetByte();
if (!tick) {
if (dat&0x0f) a->trmdepth=dat&0xf;
if (dat&0xf0) a->trmspd=(dat&0xf0)>>2;
}
- if (a->main.period) {
- q=(a->trmpos>>2)&0x1f;
+ /* TODO: PT should have the same tick 0 bug here that vibrato does. Several other
+ formats use this effect and rely on it not being broken, so don't right now. */
+ if (a->main.period)
+ DoTremolo(tick, a, 0);
- switch ((a->wavecontrol>>4)&3) {
- case 0: /* sine */
- temp=VibratoTable[q];
- break;
- case 1: /* ramp down */
- q<<=3;
- if (a->trmpos<0) q=255-q;
- temp=q;
- break;
- case 2: /* square wave */
- temp=255;
- break;
- case 3: /* random wave */
- temp=getrandom(256);
- break;
- }
- temp*=a->trmdepth;
- temp>>=6;
+ return 0;
+}
- if (a->trmpos>=0) {
- a->volume=a->tmpvolume+temp;
- if (a->volume>64) a->volume=64;
- } else {
- a->volume=a->tmpvolume-temp;
- if (a->volume<0) a->volume=0;
- }
- a->ownvol = 1;
+static int DoPTEffect7Fix(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
+{
+ /* PT equivalent vibrato but without the tick 0 bug. */
+ UBYTE dat;
+ (void)flags;
+ (void)mod;
+ (void)channel;
- if (tick)
- a->trmpos+=a->trmspd;
+ dat=UniGetByte();
+ if (!tick) {
+ if (dat&0x0f) a->trmdepth=dat&0xf;
+ if (dat&0xf0) a->trmspd=(dat&0xf0)>>2;
}
+ if (a->main.period)
+ DoTremolo(tick, a, 0);
return 0;
}
@@ -843,7 +957,7 @@ static int DoPTEffectA(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWOR
static int DoPTEffect6(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
{
if (a->main.period)
- DoVibrato(tick, a);
+ DoVibrato(tick, a, VIB_PT_BUGS);
DoPTEffectA(tick, flags, a, mod, channel);
return 0;
@@ -943,6 +1057,43 @@ static int DoPTEffectD(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWOR
return 0;
}
+static void DoLoop(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, UBYTE param)
+{
+ if (tick) return;
+ if (param) { /* set reppos or repcnt ? */
+ /* set repcnt, so check if repcnt already is set, which means we
+ are already looping */
+ if (a->pat_repcnt)
+ a->pat_repcnt--; /* already looping, decrease counter */
+ else {
+#if 0
+ /* this would make walker.xm, shipped with Xsoundtracker,
+ play correctly, but it's better to remain compatible
+ with FT2 */
+ if ((!(flags&UF_NOWRAP))||(a->pat_reppos!=POS_NONE))
+#endif
+ a->pat_repcnt=param; /* not yet looping, so set repcnt */
+ }
+
+ if (a->pat_repcnt) { /* jump to reppos if repcnt>0 */
+ if (a->pat_reppos==POS_NONE)
+ a->pat_reppos=mod->patpos-1;
+ if (a->pat_reppos==-1) {
+ mod->pat_repcrazy=1;
+ mod->patpos=0;
+ } else
+ mod->patpos=a->pat_reppos;
+ } else a->pat_reppos=POS_NONE;
+ } else {
+ a->pat_reppos=mod->patpos-1; /* set reppos - can be (-1) */
+ /* emulate the FT2 pattern loop (E60) bug:
+ * http://milkytracker.org/docs/MilkyTracker.html#fxE6x
+ * roadblas.xm plays correctly with this. */
+ if (flags & UF_FT2QUIRKS) mod->patbrk=mod->patpos;
+ }
+ return;
+}
+
static void DoEEffects(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod,
SWORD channel, UBYTE dat)
{
@@ -978,39 +1129,7 @@ static void DoEEffects(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod,
}
break;
case 0x6: /* set patternloop */
- if (tick)
- break;
- if (nib) { /* set reppos or repcnt ? */
- /* set repcnt, so check if repcnt already is set, which means we
- are already looping */
- if (a->pat_repcnt)
- a->pat_repcnt--; /* already looping, decrease counter */
- else {
-#if 0
- /* this would make walker.xm, shipped with Xsoundtracker,
- play correctly, but it's better to remain compatible
- with FT2 */
- if ((!(flags&UF_NOWRAP))||(a->pat_reppos!=POS_NONE))
-#endif
- a->pat_repcnt=nib; /* not yet looping, so set repcnt */
- }
-
- if (a->pat_repcnt) { /* jump to reppos if repcnt>0 */
- if (a->pat_reppos==POS_NONE)
- a->pat_reppos=mod->patpos-1;
- if (a->pat_reppos==-1) {
- mod->pat_repcrazy=1;
- mod->patpos=0;
- } else
- mod->patpos=a->pat_reppos;
- } else a->pat_reppos=POS_NONE;
- } else {
- a->pat_reppos=mod->patpos-1; /* set reppos - can be (-1) */
- /* emulate the FT2 pattern loop (E60) bug:
- * http://milkytracker.org/docs/MilkyTracker.html#fxE6x
- * roadblas.xm plays correctly with this. */
- if (flags & UF_FT2QUIRKS) mod->patbrk=mod->patpos;
- }
+ DoLoop(tick, flags, a, mod, nib);
break;
case 0x7: /* set tremolo waveform */
a->wavecontrol&=0x0f;
@@ -1024,10 +1143,16 @@ static void DoEEffects(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod,
}
break;
case 0x9: /* retrig note */
- /* do not retrigger on tick 0, until we are emulating FT2 and effect
- data is zero */
- if (!tick && !((flags & UF_FT2QUIRKS) && (!nib)))
- break;
+ /* Protracker: retriggers on tick 0 first; does nothing when nib=0.
+ Fasttracker 2: retriggers on tick nib first, including nib=0. */
+ if (!tick) {
+ if (flags & UF_FT2QUIRKS)
+ a->retrig=nib;
+ else if (nib)
+ a->retrig=0;
+ else
+ break;
+ }
/* only retrigger if data nibble > 0, or if tick 0 (FT2 compat) */
if (nib || !tick) {
if (!a->retrig) {
@@ -1115,8 +1240,18 @@ static int DoS3MEffectA(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWO
if (tick || mod->patdly2)
return 0;
+#if 0
+ /* This was added between MikMod 3.1.2 and libmikmod 3.1.5 with
+ * no documentation justifying its inclusion. Players for relevant
+ * formats (S3M, IT, DSMI AMF, GDM, IMF) all allow values between
+ * 128 and 255, so it's not clear what the purpose of this was.
+ * See the following pull request threads:
+ *
+ * https://github.com/sezero/mikmod/pull/42
+ * https://github.com/sezero/mikmod/pull/35 */
if (speed > 128)
speed -= 128;
+#endif
if (speed) {
mod->sngspd = speed;
mod->vbtick = 0;
@@ -1331,8 +1466,8 @@ static int DoS3MEffectQ(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWO
static int DoS3MEffectR(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
{
- UBYTE dat, q;
- UWORD temp=0; /* silence warning */
+ UBYTE dat;
+ SWORD temp;
(void)flags;
(void)mod;
(void)channel;
@@ -1343,35 +1478,13 @@ static int DoS3MEffectR(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWO
if (dat&0xf0) a->trmspd=(dat&0xf0)>>2;
}
- q=(a->trmpos>>2)&0x1f;
-
- switch ((a->wavecontrol>>4)&3) {
- case 0: /* sine */
- temp=VibratoTable[q];
- break;
- case 1: /* ramp down */
- q<<=3;
- if (a->trmpos<0) q=255-q;
- temp=q;
- break;
- case 2: /* square wave */
- temp=255;
- break;
- case 3: /* random */
- temp=getrandom(256);
- break;
- }
-
+ temp = LFOTremolo(a->trmpos, (a->wavecontrol >> 4) & 3);
temp*=a->trmdepth;
temp>>=7;
- if (a->trmpos>=0) {
- a->volume=a->tmpvolume+temp;
- if (a->volume>64) a->volume=64;
- } else {
- a->volume=a->tmpvolume-temp;
- if (a->volume<0) a->volume=0;
- }
+ a->volume = a->tmpvolume + temp;
+ if (a->volume>64) a->volume=64;
+ if (a->volume<0) a->volume=0;
a->ownvol = 1;
if (tick)
@@ -1399,8 +1512,8 @@ static int DoS3MEffectT(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWO
static int DoS3MEffectU(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
{
- UBYTE dat, q;
- UWORD temp = 0; /* silence warning */
+ UBYTE dat;
+ SWORD temp;
(void)flags;
(void)mod;
(void)channel;
@@ -1409,38 +1522,18 @@ static int DoS3MEffectU(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWO
if (!tick) {
if (dat&0x0f) a->vibdepth=dat&0xf;
if (dat&0xf0) a->vibspd=(dat&0xf0)>>2;
- } else
- if (a->main.period) {
- q=(a->vibpos>>2)&0x1f;
-
- switch (a->wavecontrol&3) {
- case 0: /* sine */
- temp=VibratoTable[q];
- break;
- case 1: /* ramp down */
- q<<=3;
- if (a->vibpos<0) q=255-q;
- temp=q;
- break;
- case 2: /* square wave */
- temp=255;
- break;
- case 3: /* random */
- temp=getrandom(256);
- break;
- }
-
- temp*=a->vibdepth;
- temp>>=8;
+ }
+ if (a->main.period) {
+ temp = LFOVibrato(a->vibpos, a->wavecontrol & 3);
+ temp*=a->vibdepth;
+ temp>>=7;
- if (a->vibpos>=0)
- a->main.period=a->tmpperiod+temp;
- else
- a->main.period=a->tmpperiod-temp;
- a->ownper = 1;
+ a->main.period = a->tmpperiod + temp;
+ a->ownper = 1;
- a->vibpos+=a->vibspd;
- }
+ if (tick)
+ a->vibpos+=a->vibspd;
+ }
return 0;
}
@@ -1515,7 +1608,7 @@ static int DoXMEffect6(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWOR
(void)mod;
(void)channel;
if (a->main.period)
- DoVibrato(tick, a);
+ DoVibrato(tick, a, 0);
return DoXMEffectA(tick, flags, a, mod, channel);
}
@@ -1562,11 +1655,11 @@ static int DoXMEffectEA(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWO
(void)channel;
dat=UniGetByte();
- if (!tick)
+ if (!tick) {
if (dat) a->fslideupspd=dat;
- a->tmpvolume+=a->fslideupspd;
- if (a->tmpvolume>64) a->tmpvolume=64;
-
+ a->tmpvolume+=a->fslideupspd;
+ if (a->tmpvolume>64) a->tmpvolume=64;
+ }
return 0;
}
@@ -1578,11 +1671,11 @@ static int DoXMEffectEB(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWO
(void)channel;
dat=UniGetByte();
- if (!tick)
+ if (!tick) {
if (dat) a->fslidednspd=dat;
- a->tmpvolume-=a->fslidednspd;
- if (a->tmpvolume<0) a->tmpvolume=0;
-
+ a->tmpvolume-=a->fslidednspd;
+ if (a->tmpvolume<0) a->tmpvolume=0;
+ }
return 0;
}
@@ -1631,18 +1724,20 @@ static int DoXMEffectL(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWOR
dat=UniGetByte();
if ((!tick)&&(a->main.i)) {
- UWORD points;
INSTRUMENT *i=a->main.i;
MP_VOICE *aout;
if ((aout=a->slave) != NULL) {
if (aout->venv.env) {
- points=i->volenv[i->volpts-1].pos;
- aout->venv.p=aout->venv.env[(dat>points)?points:dat].pos;
+ SetEnvelopePosition(&aout->venv, i->volenv, dat);
}
if (aout->penv.env) {
- points=i->panenv[i->panpts-1].pos;
- aout->penv.p=aout->penv.env[(dat>points)?points:dat].pos;
+ /* Because of a bug in FastTracker II, only the panning envelope
+ position is set if the volume sustain flag is set. Other players
+ may set the panning all the time */
+ if (!(mod->flags & UF_FT2QUIRKS) || (i->volflg & EF_SUSTAIN)) {
+ SetEnvelopePosition(&aout->penv, i->panenv, dat);
+ }
}
}
}
@@ -1779,10 +1874,14 @@ static int DoITEffectG(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWOR
return 0;
}
-static void DoITVibrato(UWORD tick, MP_CONTROL *a, UBYTE dat)
+enum itvibratoflags {
+ ITVIB_FINE = 0x01,
+ ITVIB_OLD = 0x02,
+};
+
+static void DoITVibrato(UWORD tick, MP_CONTROL *a, UBYTE dat, UWORD flags)
{
- UBYTE q;
- UWORD temp=0;
+ SWORD temp;
if (!tick) {
if (dat&0x0f) a->vibdepth=dat&0xf;
@@ -1791,44 +1890,53 @@ static void DoITVibrato(UWORD tick, MP_CONTROL *a, UBYTE dat)
if (!a->main.period)
return;
- q=(a->vibpos>>2)&0x1f;
+ temp = LFOVibratoIT(a->vibpos, a->wavecontrol & 3);
+ temp*=a->vibdepth;
- switch (a->wavecontrol&3) {
- case 0: /* sine */
- temp=VibratoTable[q];
- break;
- case 1: /* square wave */
- temp=255;
- break;
- case 2: /* ramp down */
- q<<=3;
- if (a->vibpos<0) q=255-q;
- temp=q;
- break;
- case 3: /* random */
- temp=getrandom(256);
- break;
- }
+ if (!(flags & ITVIB_OLD)) {
+ temp>>=8;
+ if (!(flags & ITVIB_FINE))
+ temp<<=2;
- temp*=a->vibdepth;
- temp>>=8;
- temp<<=2;
+ /* Subtract vibrato from period so positive vibrato translates to increase in pitch. */
+ a->main.period = a->tmpperiod - temp;
+ a->ownper=1;
+ } else {
+ /* Old IT vibrato is twice as deep. */
+ temp>>=7;
+ if (!(flags & ITVIB_FINE))
+ temp<<=2;
- if (a->vibpos>=0)
- a->main.period=a->tmpperiod+temp;
- else
- a->main.period=a->tmpperiod-temp;
- a->ownper=1;
+ /* Old IT vibrato uses the same waveforms but they are applied reversed. */
+ a->main.period = a->tmpperiod + temp;
+ a->ownper=1;
+
+ /* Old IT vibrato does not update on the first tick. */
+ if (!tick)
+ return;
+ }
a->vibpos+=a->vibspd;
}
static int DoITEffectH(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
{
- (void)flags;
- (void)mod;
- (void)channel;
- DoITVibrato(tick, a, UniGetByte());
+ (void)flags;
+ (void)mod;
+ (void)channel;
+
+ DoITVibrato(tick, a, UniGetByte(), 0);
+
+ return 0;
+}
+
+static int DoITEffectHOld(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
+{
+ (void)flags;
+ (void)mod;
+ (void)channel;
+
+ DoITVibrato(tick, a, UniGetByte(), ITVIB_OLD);
return 0;
}
@@ -1982,48 +2090,20 @@ static int DoITEffectT(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWOR
static int DoITEffectU(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
{
- UBYTE dat, q;
- UWORD temp = 0; /* silence warning */
+ DoITVibrato(tick, a, UniGetByte(), ITVIB_FINE);
(void)flags;
(void)mod;
(void)channel;
- dat = UniGetByte();
- if (!tick) {
- if (dat&0x0f) a->vibdepth=dat&0xf;
- if (dat&0xf0) a->vibspd=(dat&0xf0)>>2;
- }
- if (a->main.period) {
- q=(a->vibpos>>2)&0x1f;
-
- switch (a->wavecontrol&3) {
- case 0: /* sine */
- temp=VibratoTable[q];
- break;
- case 1: /* square wave */
- temp=255;
- break;
- case 2: /* ramp down */
- q<<=3;
- if (a->vibpos<0) q=255-q;
- temp=q;
- break;
- case 3: /* random */
- temp=getrandom(256);
- break;
- }
-
- temp*=a->vibdepth;
- temp>>=8;
-
- if (a->vibpos>=0)
- a->main.period=a->tmpperiod+temp;
- else
- a->main.period=a->tmpperiod-temp;
- a->ownper = 1;
+ return 0;
+}
- a->vibpos+=a->vibspd;
- }
+static int DoITEffectUOld(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
+{
+ DoITVibrato(tick, a, UniGetByte(), ITVIB_FINE | ITVIB_OLD);
+ (void)flags;
+ (void)mod;
+ (void)channel;
return 0;
}
@@ -2068,8 +2148,8 @@ static int DoITEffectW(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWOR
static int DoITEffectY(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
{
- UBYTE dat, q;
- SLONG temp = 0; /* silence warning */
+ UBYTE dat;
+ SLONG temp;
(void)flags;
@@ -2079,31 +2159,15 @@ static int DoITEffectY(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWOR
if (dat&0xf0) a->panbspd=(dat&0xf0)>>4;
}
if (mod->panflag) {
- q=a->panbpos;
-
- switch (a->panbwave) {
- case 0: /* sine */
- temp=PanbrelloTable[q];
- break;
- case 1: /* square wave */
- temp=(q<0x80)?64:0;
- break;
- case 2: /* ramp down */
- q<<=3;
- temp=q;
- break;
- case 3: /* random */
- temp=getrandom(256);
- break;
- }
-
+ /* TODO: when wave is random, each random value persists for a number of
+ ticks equal to the speed nibble. This behavior is unique to panbrello. */
+ temp = LFOPanbrello(a->panbpos, a->panbwave);
temp*=a->panbdepth;
temp=(temp/8)+mod->panning[channel];
a->main.panning=
(temp<PAN_LEFT)?PAN_LEFT:(temp>PAN_RIGHT?PAN_RIGHT:temp);
a->panbpos+=a->panbspd;
-
}
return 0;
@@ -2234,7 +2298,7 @@ static int DoVolEffects(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWO
DoITToneSlide(tick, a, inf);
break;
case VOL_VIBRATO:
- DoITVibrato(tick, a, inf);
+ DoITVibrato(tick, a, inf, 0);
break;
}
@@ -2277,27 +2341,157 @@ static int DoMEDSpeed(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD
return 0;
}
+static int DoMEDEffectVib(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
+{
+ /* MED vibrato (larger speed/depth range than PT vibrato). */
+ UBYTE rate = UniGetByte();
+ UBYTE depth = UniGetByte();
+ if (!tick) {
+ a->vibspd = rate;
+ a->vibdepth = depth;
+ }
+ if (a->main.period)
+ DoVibrato(tick, a, VIB_TICK_0);
+
+ (void)flags;
+ (void)mod;
+ (void)channel;
+
+ return 0;
+}
+
static int DoMEDEffectF1(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
{
- DoEEffects(tick, flags, a, mod, channel, 0x90|(mod->sngspd/2));
+ (void)flags;
+ (void)mod;
+ (void)channel;
+ /* "Play twice." Despite the documentation, this only retriggers exactly one time
+ on the third tick (i.e. it is not equivalent to PT E93). */
+ if (tick == 3) {
+ if (a->main.period)
+ a->main.kick = KICK_NOTE;
+ }
return 0;
}
static int DoMEDEffectF2(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
{
- DoEEffects(tick, flags, a, mod, channel, 0xd0|(mod->sngspd/2));
+ /* Delay for 3 ticks before playing. */
+ DoEEffects(tick, flags, a, mod, channel, 0xd3);
return 0;
}
static int DoMEDEffectF3(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
{
- DoEEffects(tick, flags, a, mod, channel, 0x90|(mod->sngspd/3));
+ (void)flags;
+ (void)mod;
+ (void)channel;
+
+ /* "Play three times." Actually, it's just a regular retrigger every 2 ticks,
+ starting from tick 2. */
+ if (!tick) a->retrig=2;
+ if (!a->retrig) {
+ if (a->main.period) a->main.kick = KICK_NOTE;
+ a->retrig=2;
+ }
+ a->retrig--;
+
+ return 0;
+}
+
+static int DoMEDEffectFD(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
+{
+ (void)flags;
+ (void)mod;
+ (void)channel;
+ (void)tick;
+
+ /* Set pitch without triggering a new note. */
+ a->main.kick = KICK_ABSENT;
+ return 0;
+}
+
+static int DoMEDEffect16(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
+{
+ (void)channel;
+
+ /* Loop (similar to PT E6x but with an extended range).
+ TODO: currently doesn't support the loop point persisting between patterns.
+ It's not clear if anything actually relies on that. */
+ UBYTE param = UniGetByte();
+ int reppos;
+ int i;
+
+ DoLoop(tick, flags, a, mod, param);
+
+ /* OctaMED repeat position is global so set it for every channel...
+ This fixes a playback bug found in "(brooker) #01.med", which sets
+ the jump position in track 2 but jumps in track 1. */
+ reppos = a->pat_reppos;
+ for (i = 0; i < pf->numchn; i++)
+ pf->control[i].pat_reppos = reppos;
+
+ return 0;
+}
+
+static int DoMEDEffect18(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
+{
+ (void)flags;
+ (void)mod;
+ (void)channel;
+
+ /* Cut note (same as PT ECx but with an extended range). */
+ UBYTE param = UniGetByte();
+ if (tick >= param)
+ a->tmpvolume=0;
+
+ return 0;
+}
+
+static int DoMEDEffect1E(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
+{
+ (void)flags;
+ (void)a;
+ (void)channel;
+
+ /* Pattern delay (same as PT EEx but with an extended range). */
+ UBYTE param = UniGetByte();
+ if (!tick && !mod->patdly2)
+ mod->patdly = (param<255) ? param+1 : 255;
return 0;
}
+static int DoMEDEffect1F(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
+{
+ (void)flags;
+ (void)mod;
+ (void)channel;
+
+ /* Combined note delay and retrigger (same as PT E9x and EDx but can be combined).
+ The high nibble is delay and the low nibble is retrigger. */
+ UBYTE param = UniGetByte();
+ UBYTE retrig = param & 0xf;
+
+ if (!tick) {
+ a->main.notedelay = (param & 0xf0) >> 4;
+ a->retrig = retrig;
+ } else if (a->main.notedelay) {
+ a->main.notedelay--;
+ }
+
+ if (!a->main.notedelay) {
+ if (retrig && !a->retrig) {
+ if (a->main.period) a->main.kick = KICK_NOTE;
+ a->retrig = retrig;
+ }
+ a->retrig--;
+ }
+ return 0;
+}
+
/*========== Oktalyzer effects */
static int DoOktArp(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
@@ -2320,6 +2514,310 @@ static int DoOktArp(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD c
return 0;
}
+/*========== Farandole effects */
+
+static void DoFarTonePorta(MP_CONTROL *a)
+{
+ int reachedNote;
+
+ if (!a->main.fadevol)
+ a->main.kick = (a->main.kick == KICK_NOTE) ? KICK_NOTE : KICK_KEYOFF;
+ else
+ a->main.kick = (a->main.kick == KICK_NOTE) ? KICK_ENV : KICK_ABSENT;
+
+ a->farcurrentvalue += a->fartoneportaspeed;
+
+ /* Have we reached our note */
+ if (a->fartoneportaspeed > 0) {
+ reachedNote = (a->farcurrentvalue >> 16) >= a->wantedperiod;
+ } else {
+ reachedNote = (a->farcurrentvalue >> 16) <= a->wantedperiod;
+ }
+ if (reachedNote) {
+ /* Stop the porta and set the periods to the reached note */
+ a->tmpperiod = a->main.period = a->wantedperiod;
+ a->fartoneportarunning = 0;
+ }
+ else {
+ /* Do the porta */
+ a->tmpperiod = a->main.period = (UWORD)(a->farcurrentvalue >> 16);
+ }
+
+ a->ownper = 1;
+}
+
+static SWORD GetFARTempo(MODULE *mod)
+{
+ return mod->control[0].fartempobend + far_tempos[mod->control[0].farcurtempo];
+}
+
+/* Set the right speed and BPM for Farandole modules */
+static void SetFARTempo(MODULE *mod)
+{
+ /* According to the Farandole document, the tempo of the song is
+ 32/tempo notes per second. Internally, it tracks time using
+ (128/tempo + fine_tempo) ticks per second, and (usually) four ticks
+ per row. Since almost everything else uses Amiga-style BPM instead,
+ this needs to be converted to BPM.
+
+ Amiga-style BPM converts a value of 125 BPM to 50 Hz (ticks/second)
+ (see https://modarchive.org/forums/index.php?topic=2709.0), so
+ the factor is 125/50 = 2.5. To get an Amiga-compatible BPM from
+ Farandole Composer ticks per second,: BPM/Hz = 2.5 -> BPM = 2.5 * Hz.
+
+ Example: at tempo 4, Hz = 128/4 + 0 = 32, so use BPM = 2.5*32 = 80.
+
+ This is further complicated by the bizarre timing system it uses for
+ slower tempos. Farandole Composer uses the programmable interval timer
+ to determine when to execute the player interrupt, which requires
+ calculating a divisor from the original Hz. The PIT only supports
+ divisors up to 0x10000, which corresponds to 18.2Hz.
+
+ When the computed divisor is > 0xffff, Farandole iteratively divides
+ the divisor by 2 (effectively doubling Hz) and increments the number
+ of ticks/second by 1. It also adds 1 to the number of ticks/second if
+ two or more of these divisions occur. Further strange behavior can
+ occur with negative ticks/second, which overflows to very slow tempos.
+
+ Note: to compute the divisor it uses 1197255 Hz instead of the rate
+ of the programmable interval timer (1193182 Hz). This results in a
+ slightly slower speed than computed, but it's not worth supporting.
+
+ Note: Farandole Composer also has an "old tempo mode" that uses 33
+ ticks/second and only executes every 8th tick. Nothing uses it and
+ it's not supported here. */
+
+ SWORD bpm = GetFARTempo(mod);
+ SLONG speed;
+ ULONG divisor;
+ if (!bpm)
+ return;
+
+ speed = 0;
+ divisor = 1197255 / bpm;
+
+ while (divisor > 0xffff) {
+ divisor >>= 1;
+ bpm <<= 1;
+ speed++;
+ }
+
+ /* Negative tempos can result in low BPMs, so clamp them to 18Hz. */
+ if (bpm <= 18)
+ bpm = 18;
+
+ if (speed >= 2)
+ speed++;
+
+ mod->sngspd = speed + 4;
+ mod->bpm = (UWORD)(bpm * 5) >> 1;
+}
+
+static int DoFAREffect1(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
+{
+ (void)flags;
+ (void)mod;
+ (void)channel;
+
+ UBYTE dat = UniGetByte();
+
+ if (!tick) {
+ a->slidespeed = (UWORD)dat << 2;
+
+ if (a->main.period)
+ a->tmpperiod -= a->slidespeed;
+
+ a->fartoneportarunning = 0;
+ }
+
+ return 0;
+}
+
+static int DoFAREffect2(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
+{
+ (void)flags;
+ (void)mod;
+ (void)channel;
+
+ UBYTE dat = UniGetByte();
+
+ if (!tick) {
+ a->slidespeed = (UWORD)dat << 2;
+
+ if (a->main.period)
+ a->tmpperiod += a->slidespeed;
+
+ a->fartoneportarunning = 0;
+ }
+
+ return 0;
+}
+
+static int DoFAREffect3(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
+{
+ (void)flags;
+ (void)channel;
+
+ UBYTE dat = UniGetByte();
+
+ if (!tick) {
+ /* We have to slide a.Main.Period toward a.WantedPeriod,
+ compute the difference between those two values */
+ SLONG dist = a->wantedperiod - a->main.period;
+ SWORD tempo = GetFARTempo(mod);
+
+ /* Adjust effect argument */
+ if (dat == 0)
+ dat = 1;
+ /* This causes crashes and other weird behavior in Farandole Composer. */
+ if (tempo <= 0)
+ tempo = 1;
+
+ /* The data is supposed to be the number of rows until completion of
+ the slide, but because it's Farandole Composer, it isn't. While
+ that claim holds for tempo 4, for tempo 2 it takes param*2 rows,
+ for tempo 1 it takes param*4 rows, etc. This calculation is based
+ on the final tempo/interrupts per second count. */
+ a->fartoneportaspeed = (dist << 16) * 8 / (tempo * dat);
+ a->farcurrentvalue = (SLONG)a->main.period << 16;
+ a->fartoneportarunning = 1;
+ }
+
+ return 0;
+}
+
+static int DoFAREffect4(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
+{
+ (void)flags;
+ (void)channel;
+
+ UBYTE dat = UniGetByte();
+
+ /* Here the argument is the number of retrigs to play evenly
+ spaced in the current row */
+ if (!tick) {
+ if (dat) {
+ a->farretrigcount = dat;
+ a->retrig = 0;
+ }
+ }
+
+ if (dat && a->newnote) {
+ if (!a->retrig) {
+ if (a->farretrigcount > 0) {
+ /* When retrig counter reaches 0,
+ reset counter and restart the sample */
+ if (a->main.period != 0)
+ a->main.kick = KICK_NOTE;
+
+ a->farretrigcount--;
+ if (a->farretrigcount > 0) {
+ SWORD delay = GetFARTempo(mod) / dat;
+ /* Effect divides by 4, timer increments
+ by 2 (round up). */
+ a->retrig = ((delay >> 2) + 1) >> 1;
+ if (a->retrig <= 0)
+ a->retrig = 1;
+ }
+ }
+ }
+ a->retrig--;
+ }
+
+ return 0;
+}
+
+static int DoFAREffect6(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
+{
+ (void)flags;
+ (void)mod;
+ (void)channel;
+
+ UBYTE dat;
+
+ dat = UniGetByte();
+ if (!tick) {
+ if (dat & 0x0f) a->vibdepth = dat & 0xf;
+ if (dat & 0xf0) a->vibspd = (dat & 0xf0) * 6;
+ }
+ if (a->main.period)
+ DoVibrato(tick, a, VIB_TICK_0);
+
+ return 0;
+}
+
+static int DoFAREffectD(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
+{
+ (void)flags;
+ (void)a;
+ (void)channel;
+
+ UBYTE dat = UniGetByte();
+
+ if (tick == 0) {
+ MP_CONTROL *firstControl = &mod->control[0];
+
+ if (dat != 0) {
+ firstControl->fartempobend -= dat;
+
+ if (GetFARTempo(mod) <= 0)
+ firstControl->fartempobend = 0;
+ }
+ else
+ firstControl->fartempobend = 0;
+
+ SetFARTempo(mod);
+ }
+
+ return 0;
+}
+
+static int DoFAREffectE(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
+{
+ (void)flags;
+ (void)a;
+ (void)channel;
+
+ UBYTE dat = UniGetByte();
+
+ if (tick == 0) {
+ MP_CONTROL *firstControl = &mod->control[0];
+
+ if (dat != 0) {
+ firstControl->fartempobend += dat;
+
+ if (GetFARTempo(mod) >= 100)
+ firstControl->fartempobend = 100;
+ }
+ else
+ firstControl->fartempobend = 0;
+
+ SetFARTempo(mod);
+ }
+
+ return 0;
+}
+
+static int DoFAREffectF(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
+{
+ (void)flags;
+ (void)a;
+ (void)channel;
+
+ UBYTE dat = UniGetByte();
+
+ if (!tick) {
+ MP_CONTROL *firstControl = &mod->control[0];
+
+ firstControl->farcurtempo = dat;
+ mod->vbtick = 0;
+
+ SetFARTempo(mod);
+ }
+
+ return 0;
+}
+
/*========== General player functions */
static int DoNothing(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
@@ -2368,7 +2866,7 @@ static effect_func effects[UNI_LAST] = {
DoKeyOff, /* UNI_KEYOFF */
DoKeyFade, /* UNI_KEYFADE */
DoVolEffects, /* UNI_VOLEFFECTS */
- DoPTEffect4, /* UNI_XMEFFECT4 */
+ DoPTEffect4Fix, /* UNI_XMEFFECT4 */
DoXMEffect6, /* UNI_XMEFFECT6 */
DoXMEffectA, /* UNI_XMEFFECTA */
DoXMEffectE1, /* UNI_XMEFFECTE1 */
@@ -2399,6 +2897,27 @@ static effect_func effects[UNI_LAST] = {
DoMEDEffectF2, /* UNI_MEDEFFECTF2 */
DoMEDEffectF3, /* UNI_MEDEFFECTF3 */
DoOktArp, /* UNI_OKTARP */
+ DoNothing, /* unused */
+ DoPTEffect4Fix, /* UNI_S3MEFFECTH */
+ DoITEffectHOld, /* UNI_ITEFFECTH_OLD */
+ DoITEffectUOld, /* UNI_ITEFFECTU_OLD */
+ DoPTEffect4Fix, /* UNI_GDMEFFECT4 */
+ DoPTEffect7Fix, /* UNI_GDMEFFECT7 */
+ DoS3MEffectU, /* UNI_GDMEFFECT14 */
+ DoMEDEffectVib, /* UNI_MEDEFFECT_VIB */
+ DoMEDEffectFD, /* UNI_MEDEFFECT_FD */
+ DoMEDEffect16, /* UNI_MEDEFFECT_16 */
+ DoMEDEffect18, /* UNI_MEDEFFECT_18 */
+ DoMEDEffect1E, /* UNI_MEDEFFECT_1E */
+ DoMEDEffect1F, /* UNI_MEDEFFECT_1F */
+ DoFAREffect1, /* UNI_FAREFFECT1 */
+ DoFAREffect2, /* UNI_FAREFFECT2 */
+ DoFAREffect3, /* UNI_FAREFFECT3 */
+ DoFAREffect4, /* UNI_FAREFFECT4 */
+ DoFAREffect6, /* UNI_FAREFFECT6 */
+ DoFAREffectD, /* UNI_FAREFFECTD */
+ DoFAREffectE, /* UNI_FAREFFECTE */
+ DoFAREffectF, /* UNI_FAREFFECTF */
};
static int pt_playeffects(MODULE *mod, SWORD channel, MP_CONTROL *a)
@@ -2586,24 +3105,17 @@ static void pt_UpdateVoices(MODULE *mod, int max_volume)
else
Voice_SetPanning_internal(channel,aout->main.panning);
- if (aout->main.period && s->vibdepth)
- switch (s->vibtype) {
- case 0:
- vibval=avibtab[s->avibpos&127];
- if (aout->avibpos & 0x80) vibval=-vibval;
- break;
- case 1:
- vibval=64;
- if (aout->avibpos & 0x80) vibval=-vibval;
- break;
- case 2:
- vibval=63-(((aout->avibpos+128)&255)>>1);
- break;
- default:
- vibval=(((aout->avibpos+128)&255)>>1)-64;
- break;
+ if (aout->main.period && s->vibdepth) {
+ if (s->vibflags & AV_IT) {
+ /* IT auto-vibrato uses regular waveforms. */
+ vibval = LFOVibratoIT((SBYTE)aout->avibpos, s->vibtype);
+ } else {
+ /* XM auto-vibrato uses its own set of waveforms.
+ Also, uses LFO amplitudes on [-63,63], possibly to compensate
+ for depth being multiplied by 4 in the loader(?). */
+ vibval = LFOAutoVibratoXM((SBYTE)aout->avibpos, s->vibtype) >> 2;
}
- else
+ } else
vibval=0;
if (s->vibflags & AV_IT) {
@@ -2614,7 +3126,10 @@ static void pt_UpdateVoices(MODULE *mod, int max_volume)
vibdpt=s->vibdepth<<8;
vibval=(vibval*vibdpt)>>16;
if (aout->mflag) {
- if (!(mod->flags&UF_LINEAR)) vibval>>=1;
+ /* This vibrato value is the correct value in fine linear slide
+ steps, but MikMod linear periods are halved, so the final
+ value also needs to be halved in linear mode. */
+ if (mod->flags&UF_LINEAR) vibval>>=1;
aout->main.period-=vibval;
}
} else {
@@ -2684,8 +3199,8 @@ static void pt_UpdateVoices(MODULE *mod, int max_volume)
}
md_bpm=mod->bpm+mod->relspd;
- if (md_bpm<32)
- md_bpm=32;
+ if (md_bpm<mod->bpmlimit)
+ md_bpm=mod->bpmlimit;
else if ((!(mod->flags&UF_HIGHBPM)) && md_bpm>255)
md_bpm=255;
}
@@ -2711,10 +3226,11 @@ static void pt_Notes(MODULE *mod)
}
a->row=(tr<mod->numtrk)?UniFindRow(mod->tracks[tr],mod->patpos):NULL;
+ a->newnote=0;
a->newsamp=0;
if (!mod->vbtick) a->main.notedelay=0;
- if (!a->row) continue;
+ if (!a->row || (mod->numrow == 0)) continue;
UniSetRow(a->row);
funky=0;
@@ -2726,11 +3242,13 @@ static void pt_Notes(MODULE *mod)
a->main.kick =KICK_NOTE;
a->main.start=-1;
a->sliding=0;
+ a->newnote=1;
+ a->fartoneportarunning = 0;
/* retrig tremolo and vibrato waves ? */
- if (!(a->wavecontrol & 0x80)) a->trmpos=0;
- if (!(a->wavecontrol & 0x08)) a->vibpos=0;
- if (!a->panbwave) a->panbpos=0;
+ if (!(a->wavecontrol & 0x40)) a->trmpos=0;
+ if (!(a->wavecontrol & 0x04)) a->vibpos=0;
+ a->panbpos=0;
break;
case UNI_INSTRUMENT:
inst=UniGetByte();
@@ -2861,6 +3379,10 @@ static void pt_EffectsPass1(MODULE *mod)
a->sliding = explicitslides;
}
+ /* keep running Farandole tone porta */
+ if (a->fartoneportarunning)
+ DoFarTonePorta(a);
+
if (!a->ownper)
a->main.period=a->tmpperiod;
if (!a->ownvol)
@@ -3066,7 +3588,7 @@ void Player_HandleTick(void)
/* do we have to get a new patternpointer ? (when pf->patpos reaches the
pattern size, or when a patternbreak is active) */
- if (((pf->patpos>=pf->numrow)&&(pf->numrow>0))&&(!pf->posjmp))
+ if ((pf->patpos>=pf->numrow)&&(!pf->posjmp))
pf->posjmp=3;
if (pf->posjmp) {
@@ -3087,11 +3609,18 @@ void Player_HandleTick(void)
if (!pf->wrap) return;
if (!(pf->sngpos=pf->reppos)) {
pf->volume=pf->initvolume>128?128:pf->initvolume;
- if(pf->initspeed!=0)
- pf->sngspd=pf->initspeed<32?pf->initspeed:32;
- else
- pf->sngspd=6;
- pf->bpm=pf->inittempo<32?32:pf->inittempo;
+ if (pf->flags & UF_FARTEMPO) {
+ pf->control[0].farcurtempo = pf->initspeed;
+ pf->control[0].fartempobend = 0;
+ SetFARTempo(pf);
+ }
+ else {
+ if(pf->initspeed!=0)
+ pf->sngspd=pf->initspeed<pf->bpmlimit?pf->initspeed:pf->bpmlimit;
+ else
+ pf->sngspd=6;
+ pf->bpm=pf->inittempo<pf->bpmlimit?pf->bpmlimit:pf->inittempo;
+ }
}
}
}
@@ -3132,16 +3661,26 @@ static void Player_Init_internal(MODULE* mod)
mod->pat_repcrazy=0;
mod->sngpos=0;
- if(mod->initspeed!=0)
- mod->sngspd=mod->initspeed<32?mod->initspeed:32;
- else
- mod->sngspd=6;
+
+ if (mod->flags & UF_FARTEMPO) {
+ mod->control[0].farcurtempo = mod->initspeed;
+ mod->control[0].fartempobend = 0;
+ SetFARTempo(mod);
+ }
+ else {
+ if(mod->initspeed!=0)
+ mod->sngspd=mod->initspeed<mod->bpmlimit?mod->initspeed:mod->bpmlimit;
+ else
+ mod->sngspd=6;
+
+ mod->bpm=mod->inittempo<mod->bpmlimit?mod->bpmlimit:mod->inittempo;
+ }
+
mod->volume=mod->initvolume>128?128:mod->initvolume;
mod->vbtick=mod->sngspd;
mod->patdly=0;
mod->patdly2=0;
- mod->bpm=mod->inittempo<32?32:mod->inittempo;
mod->realchn=0;
mod->patpos=0;
diff --git a/apps/plugins/mikmod/munitrk.c b/apps/plugins/mikmod/munitrk.c
index 72e7de4e7e..8abf0c19ec 100644
--- a/apps/plugins/mikmod/munitrk.c
+++ b/apps/plugins/mikmod/munitrk.c
@@ -100,6 +100,27 @@ const UWORD unioperands[UNI_LAST] = {
0, /* UNI_MEDEFFECTF2 */
0, /* UNI_MEDEFFECTF3 */
2, /* UNI_OKTARP */
+ 0, /* not used */
+ 1, /* UNI_S3MEFFECTH */
+ 1, /* UNI_ITEFFECTH_OLD */
+ 1, /* UNI_ITEFFECTU_OLD */
+ 1, /* UNI_GDMEFFECT4 */
+ 1, /* UNI_GDMEFFECT7 */
+ 1, /* UNI_GDMEFFECT14 */
+ 2, /* UNI_MEDEFFECT_VIB */
+ 0, /* UNI_MEDEFFECT_FD */
+ 1, /* UNI_MEDEFFECT_16 */
+ 1, /* UNI_MEDEFFECT_18 */
+ 1, /* UNI_MEDEFFECT_1E */
+ 1, /* UNI_MEDEFFECT_1F */
+ 1, /* UNI_FAREFFECT1 */
+ 1, /* UNI_FAREFFECT2 */
+ 1, /* UNI_FAREFFECT3 */
+ 1, /* UNI_FAREFFECT4 */
+ 1, /* UNI_FAREFFECT6 */
+ 1, /* UNI_FAREFFECTD */
+ 1, /* UNI_FAREFFECTE */
+ 1, /* UNI_FAREFFECTF */
};
/* Sparse description of the internal module format
@@ -262,7 +283,7 @@ void UniNewline(void)
unibuf[lastp]+=0x20;
unipc = unitt+1;
} else {
- if (UniExpand(unitt-unipc)) {
+ if (UniExpand(len)) {
/* current and previous row aren't equal... update the pointers */
unibuf[unitt] = len;
lastp = unitt;
diff --git a/apps/plugins/mikmod/sloader.c b/apps/plugins/mikmod/sloader.c
index af7f623799..755242a1fb 100644
--- a/apps/plugins/mikmod/sloader.c
+++ b/apps/plugins/mikmod/sloader.c
@@ -20,8 +20,6 @@
/*==============================================================================
- $Id$
-
Routines for loading samples. The sample loader utilizes the routines
provided by the "registered" sample loader.
@@ -56,7 +54,7 @@ typedef struct ITPACK {
int SL_Init(SAMPLOAD* s)
{
if(!sl_buffer)
- if(!(sl_buffer=(SWORD*)MikMod_malloc(SLBUFSIZE*sizeof(SWORD)))) return 0;
+ if(!(sl_buffer=(SWORD*)MikMod_calloc(1,SLBUFSIZE*sizeof(SWORD)))) return 0;
sl_rlength = s->length;
if(s->infmt & SF_16BITS) sl_rlength>>=1;
@@ -221,7 +219,7 @@ static int read_itcompr16(ITPACK *status,MREADER *reader,SWORD *out,UWORD count,
return (dest-out);
}
-static int SL_LoadInternal(void* buffer,UWORD infmt,UWORD outfmt,int scalefactor,ULONG length,MREADER* reader,int dither)
+static int SL_LoadInternal(void *buffer,UWORD infmt,UWORD outfmt,int scalefactor,ULONG length,MREADER *reader,int dither)
{
SBYTE *bptr = (SBYTE*)buffer;
SWORD *wptr = (SWORD*)buffer;
@@ -231,6 +229,10 @@ static int SL_LoadInternal(void* buffer,UWORD infmt,UWORD outfmt,int scalefactor
ITPACK status;
UWORD incnt = 0;
+ SBYTE compressionTable[16];
+ SWORD adpcmDelta = 0;
+ int hasTable = 0;
+
status.buf = 0;
status.last = 0;
status.bufbits = 0;
@@ -260,6 +262,22 @@ static int SL_LoadInternal(void* buffer,UWORD infmt,UWORD outfmt,int scalefactor
return 1;
}
c_block -= stodo;
+ } else if (infmt&SF_ADPCM4) {
+ if (!hasTable) {
+ /* Read compression table */
+ _mm_read_SBYTES(compressionTable, 16, reader);
+ hasTable = 1;
+ }
+
+ // 4-bit ADPCM data, used by MOD plugin
+ for(t=0;t<stodo;t+=2) {
+ UBYTE b = _mm_read_UBYTE(reader);
+
+ adpcmDelta += compressionTable[b & 0x0f];
+ sl_buffer[t] = adpcmDelta << 8;
+ adpcmDelta += compressionTable[(b >> 4) & 0x0f];
+ sl_buffer[t+1] = adpcmDelta << 8;
+ }
} else {
if(infmt&SF_16BITS) {
if(_mm_eof(reader)) {
@@ -368,7 +386,7 @@ SAMPLOAD* SL_RegisterSample(SAMPLE* s,int type,MREADER* reader)
return NULL;
/* Allocate and add structure to the END of the list */
- if(!(news=(SAMPLOAD*)MikMod_malloc(sizeof(SAMPLOAD)))) return NULL;
+ if(!(news=(SAMPLOAD*)MikMod_calloc(1, sizeof(SAMPLOAD)))) return NULL;
if(cruise) {
while(cruise->next) cruise=cruise->next;
diff --git a/apps/plugins/mikmod/virtch.c b/apps/plugins/mikmod/virtch.c
index f2fd528b00..3919dc40cf 100644
--- a/apps/plugins/mikmod/virtch.c
+++ b/apps/plugins/mikmod/virtch.c
@@ -20,19 +20,14 @@
/*==============================================================================
- $Id$
-
Sample mixing routines, using a 32 bits mixing buffer.
-==============================================================================*/
-
-/*
-
Optional features include:
(a) 4-step reverb (for 16 bit output only)
(b) Interpolation of sample data during mixing
(c) Dolby Surround Sound
-*/
+
+==============================================================================*/
#ifdef HAVE_CONFIG_H
#include "config.h"
@@ -129,7 +124,7 @@ static SLONG *RVbufR1=NULL,*RVbufR2=NULL,*RVbufR3=NULL,*RVbufR4=NULL,
#if defined HAVE_SSE2 || defined HAVE_ALTIVEC
# if !defined(NATIVE_64BIT_INT)
-static SINTPTR_T MixSIMDMonoNormal(const SWORD* srce,SLONG* dest,SINTPTR_T idx,SINTPTR_T increment,SINTPTR_T todo)
+static SSIZE_T MixSIMDMonoNormal(const SWORD* srce,SLONG* dest,SSIZE_T idx,SSIZE_T increment,SSIZE_T todo)
{
/* TODO: */
SWORD sample;
@@ -145,7 +140,7 @@ static SINTPTR_T MixSIMDMonoNormal(const SWORD* srce,SLONG* dest,SINTPTR_T idx,S
}
# endif /* !NATIVE_64BIT_INT */
-static SINTPTR_T MixSIMDStereoNormal(const SWORD* srce,SLONG* dest,SINTPTR_T idx,SINTPTR_T increment,SINTPTR_T todo)
+static SSIZE_T MixSIMDStereoNormal(const SWORD* srce,SLONG* dest,SSIZE_T idx,SSIZE_T increment,SSIZE_T todo)
{
SWORD vol[8] = {vnf->lvolsel, vnf->rvolsel};
SWORD sample;
@@ -289,7 +284,7 @@ static SLONG Mix32MonoNormal(const SWORD* srce,SLONG* dest,SLONG idx,SLONG incre
}
/* FIXME: This mixer should works also on 64-bit platform */
-/* Hint : changes SLONG / SLONGLONG mess with intptr */
+/* Hint : changes SLONG / SLONGLONG mess with ssize_t */
static SLONG Mix32StereoNormal(const SWORD* srce,SLONG* dest,SLONG idx,SLONG increment,SLONG todo)
{
#if defined HAVE_ALTIVEC || defined HAVE_SSE2
@@ -1029,9 +1024,10 @@ static void AddChannel(SLONG* ptr,NATIVE todo)
(vnf->flags&SF_LOOP)?idxlend:idxsize;
/* if the sample is not blocked... */
- if((end==vnf->current)||(!vnf->increment))
+ if((vnf->increment>0 && vnf->current>=end) ||
+ (vnf->increment<0 && vnf->current<=end) || !vnf->increment) {
done=0;
- else {
+ } else {
done=MIN((end-vnf->current)/vnf->increment+1,todo);
if(done<0) done=0;
}
diff --git a/apps/plugins/mikmod/virtch2.c b/apps/plugins/mikmod/virtch2.c
index d512833bbe..a901186870 100644
--- a/apps/plugins/mikmod/virtch2.c
+++ b/apps/plugins/mikmod/virtch2.c
@@ -20,20 +20,14 @@
/*==============================================================================
- $Id$
-
High-quality sample mixing routines, using a 32 bits mixing buffer,
interpolation, and sample smoothing to improve sound quality and remove
clicks.
-==============================================================================*/
-
-/*
-
Future Additions:
- Low-Pass filter to remove annoying staticy buzz.
+ - Low-Pass filter to remove annoying staticy buzz.
-*/
+==============================================================================*/
#ifdef HAVE_CONFIG_H
#include "config.h"
@@ -1074,9 +1068,10 @@ static void AddChannel(SLONG* ptr,NATIVE todo)
(vnf->flags&SF_LOOP)?idxlend:idxsize;
/* if the sample is not blocked... */
- if((end==vnf->current)||(!vnf->increment))
+ if((vnf->increment>0 && vnf->current>=end) ||
+ (vnf->increment<0 && vnf->current<=end) || !vnf->increment) {
done=0;
- else {
+ } else {
done=MIN((end-vnf->current)/vnf->increment+1,todo);
if(done<0) done=0;
}
diff --git a/apps/plugins/mikmod/virtch_common.c b/apps/plugins/mikmod/virtch_common.c
index 3395120a47..70cd9417b2 100644
--- a/apps/plugins/mikmod/virtch_common.c
+++ b/apps/plugins/mikmod/virtch_common.c
@@ -20,10 +20,8 @@
/*==============================================================================
- $Id$
-
Common source parts between the two software mixers.
- This file is probably the ugliest part of libmikmod...
+ This file is probably the ugliest part of libmikmod.
==============================================================================*/