1
0
Fork 0
forked from len0rd/rockbox

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
This commit is contained in:
Solomon Peachy 2025-01-03 08:34:47 -05:00
parent 08c32cee7c
commit af7ed73f31
29 changed files with 1860 additions and 706 deletions

View file

@ -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;
/* 0x6 0x7 not used */
case 0x6:
case 0x7:
case 0x2: /* portamento down (PT compatible, ignore 0) */
if (dat)
UniPTEffect(0x2, dat);
break;
case 0x8: /* midi hold/decay */
/* 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 0x9:
if (bpmtempos) {
if (!dat)
dat = of.initspeed;
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: /* 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;