diff options
author | Solomon Peachy <pizza@shaftnet.org> | 2025-01-03 08:34:47 -0500 |
---|---|---|
committer | Solomon Peachy <pizza@shaftnet.org> | 2025-01-03 11:34:24 -0500 |
commit | af7ed73f311fcb0e4ef2e40d77369b3cc165671c (patch) | |
tree | 7870fe1d7c86cf4a5b8497fcde6f38be47580789 | |
parent | 08c32cee7c8e460f949f268e011adc5bbec396e2 (diff) | |
download | rockbox-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
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. ==============================================================================*/ |