diff options
Diffstat (limited to 'lib/timidity/libunimod/load_med.c')
-rw-r--r-- | lib/timidity/libunimod/load_med.c | 757 |
1 files changed, 757 insertions, 0 deletions
diff --git a/lib/timidity/libunimod/load_med.c b/lib/timidity/libunimod/load_med.c new file mode 100644 index 0000000000..71d9511696 --- /dev/null +++ b/lib/timidity/libunimod/load_med.c @@ -0,0 +1,757 @@ +/* MikMod sound library + (c) 1998, 1999 Miodrag Vallat and others - see file AUTHORS for + complete list. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. + */ + +/*============================================================================== + + $Id: load_med.c,v 1.27 1999/10/25 16:31:41 miod Exp $ + + Amiga MED module loader + +==============================================================================*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <string.h> + +#include "unimod_priv.h" + +/*========== Module information */ + +typedef struct MEDHEADER + { + ULONG id; + ULONG modlen; + ULONG pMEDSONG; /* struct MEDSONG *song; */ + UWORD psecnum; /* for the player routine, MMD2 only */ + UWORD pseq; /* " " " " */ + ULONG pMEDBLOCKP; /* struct MMD0Block **blockarr; */ + ULONG reserved1; + ULONG ppMedInstrHdr; /* struct InstrHdr **smplarr; */ + ULONG reserved2; + ULONG pMEDEXP; /* struct MEDEXP *expdata; */ + ULONG reserved3; + UWORD pstate; /* some data for the player routine */ + UWORD pblock; + UWORD pline; + UWORD pseqnum; + SWORD actplayline; + UBYTE counter; + UBYTE extra_songs; /* number of songs - 1 */ + } +MEDHEADER; + +typedef struct MEDSAMPLE + { + UWORD rep, replen; /* offs: 0(s), 2(s) */ + UBYTE midich; /* offs: 4(s) */ + UBYTE midipreset; /* offs: 5(s) */ + UBYTE svol; /* offs: 6(s) */ + SBYTE strans; /* offs: 7(s) */ + } +MEDSAMPLE; + +typedef struct MEDSONG + { + MEDSAMPLE sample[63]; /* 63 * 8 bytes = 504 bytes */ + UWORD numblocks; /* offs: 504 */ + UWORD songlen; /* offs: 506 */ + UBYTE playseq[256]; /* offs: 508 */ + UWORD deftempo; /* offs: 764 */ + SBYTE playtransp; /* offs: 766 */ + UBYTE flags; /* offs: 767 */ + UBYTE flags2; /* offs: 768 */ + UBYTE tempo2; /* offs: 769 */ + UBYTE trkvol[16]; /* offs: 770 */ + UBYTE mastervol; /* offs: 786 */ + UBYTE numsamples; /* offs: 787 */ + } +MEDSONG; + +typedef struct MEDEXP + { + ULONG nextmod; /* pointer to next module */ + ULONG exp_smp; /* pointer to InstrExt array */ + UWORD s_ext_entries; + UWORD s_ext_entrsz; + ULONG annotxt; /* pointer to annotation text */ + ULONG annolen; + ULONG iinfo; /* pointer to InstrInfo array */ + UWORD i_ext_entries; + UWORD i_ext_entrsz; + ULONG jumpmask; + ULONG rgbtable; + ULONG channelsplit; + ULONG n_info; + ULONG songname; /* pointer to songname */ + ULONG songnamelen; + ULONG dumps; + ULONG reserved2[7]; + } +MEDEXP; + +typedef struct MMD0NOTE + { + UBYTE a, b, c; + } +MMD0NOTE; + +typedef struct MMD1NOTE + { + UBYTE a, b, c, d; + } +MMD1NOTE; + +typedef struct MEDINSTHEADER + { + ULONG length; + SWORD type; + /* Followed by actual data */ + } +MEDINSTHEADER; + +typedef struct MEDINSTEXT + { + UBYTE hold; + UBYTE decay; + UBYTE suppress_midi_off; + SBYTE finetune; + } +MEDINSTEXT; + +typedef struct MEDINSTINFO + { + UBYTE name[40]; + } +MEDINSTINFO; + +/*========== Loader variables */ + +#define MMD0_string 0x4D4D4430 +#define MMD1_string 0x4D4D4431 + +static MEDHEADER *mh = NULL; +static MEDSONG *ms = NULL; +static MEDEXP *me = NULL; +static ULONG *ba = NULL; +static MMD0NOTE *mmd0pat = NULL; +static MMD1NOTE *mmd1pat = NULL; + +static BOOL decimalvolumes; +static BOOL bpmtempos; + +#define d0note(row,col) mmd0pat[((row)*(UWORD)of.numchn)+(col)] +#define d1note(row,col) mmd1pat[((row)*(UWORD)of.numchn)+(col)] + +static CHAR MED_Version[] = "OctaMED (MMDx)"; + +/*========== Loader code */ + +BOOL +MED_Test (void) +{ + UBYTE id[4]; + + if (!_mm_read_UBYTES (id, 4, modreader)) + return 0; + if ((!memcmp (id, "MMD0", 4)) || (!memcmp (id, "MMD1", 4))) + return 1; + return 0; +} + +BOOL +MED_Init (void) +{ + if (!(me = (MEDEXP *) _mm_malloc (sizeof (MEDEXP)))) + return 0; + if (!(mh = (MEDHEADER *) _mm_malloc (sizeof (MEDHEADER)))) + return 0; + if (!(ms = (MEDSONG *) _mm_malloc (sizeof (MEDSONG)))) + return 0; + return 1; +} + +void +MED_Cleanup (void) +{ + _mm_free (me); + _mm_free (mh); + _mm_free (ms); + _mm_free (ba); + _mm_free (mmd0pat); + _mm_free (mmd1pat); +} + +static void +EffectCvt (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)); + break; + /* 0x6 0x7 not used */ + case 0x6: + case 0x7: + break; + case 0x8: /* midi hold/decay */ + break; + case 0x9: + if (bpmtempos) + { + if (!dat) + dat = of.initspeed; + 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 */ + case 0xc: + if (decimalvolumes) + dat = (dat >> 4) * 10 + (dat & 0xf); + UniPTEffect (0xc, dat); + break; + case 0xd: /* same as PT volslide */ + UniPTEffect (0xa, dat); + break; + case 0xe: /* synth jmp - midi */ + break; + case 0xf: + switch (dat) + { + case 0: /* patternbreak */ + UniPTEffect (0xd, 0); + break; + case 0xf1: /* play note twice */ + UniWriteByte (UNI_MEDEFFECTF1); + break; + case 0xf2: /* delay note */ + UniWriteByte (UNI_MEDEFFECTF2); + break; + case 0xf3: /* play note three times */ + UniWriteByte (UNI_MEDEFFECTF3); + break; + case 0xfe: /* stop playing */ + UniPTEffect (0xb, of.numpat); + break; + case 0xff: /* note cut */ + 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); + } + } + break; + default: /* all normal PT effects are handled here */ + UniPTEffect (eff, dat); + break; + } +} + +static UBYTE * +MED_Convert1 (int count, int col) +{ + int t; + UBYTE inst, note, eff, dat; + MMD1NOTE *n; + + UniReset (); + for (t = 0; t < count; t++) + { + n = &d1note (t, col); + + note = n->a & 0x7f; + inst = n->b & 0x3f; + eff = n->c & 0xf; + dat = n->d; + + if (inst) + UniInstrument (inst - 1); + if (note) + UniNote (note + 3 * OCTAVE - 1); + EffectCvt (eff, dat); + UniNewline (); + } + return UniDup (); +} + +static UBYTE * +MED_Convert0 (int count, int col) +{ + int t; + UBYTE a, b, inst, note, eff, dat; + MMD0NOTE *n; + + UniReset (); + for (t = 0; t < count; t++) + { + n = &d0note (t, col); + a = n->a; + b = n->b; + + note = a & 0x3f; + a >>= 6; + a = ((a & 1) << 1) | (a >> 1); + inst = (b >> 4) | (a << 4); + eff = b & 0xf; + dat = n->c; + + if (inst) + UniInstrument (inst - 1); + if (note) + UniNote (note + 3 * OCTAVE - 1); + EffectCvt (eff, dat); + UniNewline (); + } + return UniDup (); +} + +static BOOL +LoadMMD0Patterns (void) +{ + int t, row, col; + UWORD numtracks, numlines, maxlines = 0, track = 0; + MMD0NOTE *mmdp; + + /* first, scan patterns to see how many channels are used */ + for (t = 0; t < of.numpat; t++) + { + _mm_fseek (modreader, ba[t], SEEK_SET); + numtracks = _mm_read_UBYTE (modreader); + numlines = _mm_read_UBYTE (modreader); + + if (numtracks > of.numchn) + of.numchn = numtracks; + if (numlines > maxlines) + maxlines = numlines; + } + + of.numtrk = of.numpat * of.numchn; + if (!AllocTracks ()) + return 0; + if (!AllocPatterns ()) + return 0; + + if (!(mmd0pat = (MMD0NOTE *) _mm_calloc (of.numchn * (maxlines + 1), sizeof (MMD0NOTE)))) + return 0; + + /* second read: read and convert patterns */ + for (t = 0; t < of.numpat; t++) + { + _mm_fseek (modreader, ba[t], SEEK_SET); + numtracks = _mm_read_UBYTE (modreader); + numlines = _mm_read_UBYTE (modreader); + + of.pattrows[t] = ++numlines; + memset (mmdp = mmd0pat, 0, of.numchn * maxlines * sizeof (MMD0NOTE)); + for (row = numlines; row; row--) + { + for (col = numtracks; col; col--, mmdp++) + { + mmdp->a = _mm_read_UBYTE (modreader); + mmdp->b = _mm_read_UBYTE (modreader); + mmdp->c = _mm_read_UBYTE (modreader); + } + } + + for (col = 0; col < of.numchn; col++) + of.tracks[track++] = MED_Convert0 (numlines, col); + } + return 1; +} + +static BOOL +LoadMMD1Patterns (void) +{ + int t, row, col; + UWORD numtracks, numlines, maxlines = 0, track = 0; + MMD1NOTE *mmdp; + + /* first, scan patterns to see how many channels are used */ + for (t = 0; t < of.numpat; t++) + { + _mm_fseek (modreader, ba[t], SEEK_SET); + numtracks = _mm_read_M_UWORD (modreader); + numlines = _mm_read_M_UWORD (modreader); + if (numtracks > of.numchn) + of.numchn = numtracks; + if (numlines > maxlines) + maxlines = numlines; + } + + of.numtrk = of.numpat * of.numchn; + if (!AllocTracks ()) + return 0; + if (!AllocPatterns ()) + return 0; + + if (!(mmd1pat = (MMD1NOTE *) _mm_calloc (of.numchn * (maxlines + 1), sizeof (MMD1NOTE)))) + return 0; + + /* second read: really read and convert patterns */ + for (t = 0; t < of.numpat; t++) + { + _mm_fseek (modreader, ba[t], SEEK_SET); + numtracks = _mm_read_M_UWORD (modreader); + numlines = _mm_read_M_UWORD (modreader); + + _mm_fseek (modreader, sizeof (ULONG), SEEK_CUR); + of.pattrows[t] = ++numlines; + memset (mmdp = mmd1pat, 0, of.numchn * maxlines * sizeof (MMD1NOTE)); + + for (row = numlines; row; row--) + { + for (col = numtracks; col; col--, mmdp++) + { + mmdp->a = _mm_read_UBYTE (modreader); + mmdp->b = _mm_read_UBYTE (modreader); + mmdp->c = _mm_read_UBYTE (modreader); + mmdp->d = _mm_read_UBYTE (modreader); + } + } + + for (col = 0; col < of.numchn; col++) + of.tracks[track++] = MED_Convert1 (numlines, col); + } + return 1; +} + +BOOL +MED_Load (BOOL curious) +{ + int t; + ULONG sa[64]; + MEDINSTHEADER s; + SAMPLE *q; + MEDSAMPLE *mss; + + /* try to read module header */ + mh->id = _mm_read_M_ULONG (modreader); + mh->modlen = _mm_read_M_ULONG (modreader); + mh->pMEDSONG = _mm_read_M_ULONG (modreader); + mh->psecnum = _mm_read_M_UWORD (modreader); + mh->pseq = _mm_read_M_UWORD (modreader); + mh->pMEDBLOCKP = _mm_read_M_ULONG (modreader); + mh->reserved1 = _mm_read_M_ULONG (modreader); + mh->ppMedInstrHdr = _mm_read_M_ULONG (modreader); + mh->reserved2 = _mm_read_M_ULONG (modreader); + mh->pMEDEXP = _mm_read_M_ULONG (modreader); + mh->reserved3 = _mm_read_M_ULONG (modreader); + mh->pstate = _mm_read_M_UWORD (modreader); + mh->pblock = _mm_read_M_UWORD (modreader); + mh->pline = _mm_read_M_UWORD (modreader); + mh->pseqnum = _mm_read_M_UWORD (modreader); + mh->actplayline = _mm_read_M_SWORD (modreader); + mh->counter = _mm_read_UBYTE (modreader); + mh->extra_songs = _mm_read_UBYTE (modreader); + + /* Seek to MEDSONG struct */ + _mm_fseek (modreader, mh->pMEDSONG, SEEK_SET); + + /* Load the MMD0 Song Header */ + mss = ms->sample; /* load the sample data first */ + for (t = 63; t; t--, mss++) + { + mss->rep = _mm_read_M_UWORD (modreader); + mss->replen = _mm_read_M_UWORD (modreader); + mss->midich = _mm_read_UBYTE (modreader); + mss->midipreset = _mm_read_UBYTE (modreader); + mss->svol = _mm_read_UBYTE (modreader); + mss->strans = _mm_read_SBYTE (modreader); + } + + ms->numblocks = _mm_read_M_UWORD (modreader); + ms->songlen = _mm_read_M_UWORD (modreader); + _mm_read_UBYTES (ms->playseq, 256, modreader); + ms->deftempo = _mm_read_M_UWORD (modreader); + ms->playtransp = _mm_read_SBYTE (modreader); + ms->flags = _mm_read_UBYTE (modreader); + ms->flags2 = _mm_read_UBYTE (modreader); + ms->tempo2 = _mm_read_UBYTE (modreader); + _mm_read_UBYTES (ms->trkvol, 16, modreader); + ms->mastervol = _mm_read_UBYTE (modreader); + ms->numsamples = _mm_read_UBYTE (modreader); + + /* check for a bad header */ + if (_mm_eof (modreader)) + { + _mm_errno = MMERR_LOADING_HEADER; + return 0; + } + + /* load extension structure */ + if (mh->pMEDEXP) + { + _mm_fseek (modreader, mh->pMEDEXP, SEEK_SET); + me->nextmod = _mm_read_M_ULONG (modreader); + me->exp_smp = _mm_read_M_ULONG (modreader); + me->s_ext_entries = _mm_read_M_UWORD (modreader); + me->s_ext_entrsz = _mm_read_M_UWORD (modreader); + me->annotxt = _mm_read_M_ULONG (modreader); + me->annolen = _mm_read_M_ULONG (modreader); + me->iinfo = _mm_read_M_ULONG (modreader); + me->i_ext_entries = _mm_read_M_UWORD (modreader); + me->i_ext_entrsz = _mm_read_M_UWORD (modreader); + me->jumpmask = _mm_read_M_ULONG (modreader); + me->rgbtable = _mm_read_M_ULONG (modreader); + me->channelsplit = _mm_read_M_ULONG (modreader); + me->n_info = _mm_read_M_ULONG (modreader); + me->songname = _mm_read_M_ULONG (modreader); + me->songnamelen = _mm_read_M_ULONG (modreader); + me->dumps = _mm_read_M_ULONG (modreader); + } + + /* seek to and read the samplepointer array */ + _mm_fseek (modreader, mh->ppMedInstrHdr, SEEK_SET); + if (!_mm_read_M_ULONGS (sa, ms->numsamples, modreader)) + { + _mm_errno = MMERR_LOADING_HEADER; + return 0; + } + + /* alloc and read the blockpointer array */ + if (!(ba = (ULONG *) _mm_calloc (ms->numblocks, sizeof (ULONG)))) + return 0; + _mm_fseek (modreader, mh->pMEDBLOCKP, SEEK_SET); + if (!_mm_read_M_ULONGS (ba, ms->numblocks, modreader)) + { + _mm_errno = MMERR_LOADING_HEADER; + return 0; + } + + /* copy song positions */ + if (!AllocPositions (ms->songlen)) + return 0; + for (t = 0; t < ms->songlen; t++) + of.positions[t] = ms->playseq[t]; + + decimalvolumes = (ms->flags & 0x10) ? 0 : 1; + bpmtempos = (ms->flags2 & 0x20) ? 1 : 0; + + if (bpmtempos) + { + int bpmlen = (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); + } + } + 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; + } + MED_Version[12] = mh->id; + of.modtype = strdup (MED_Version); + of.numchn = 0; /* will be counted later */ + of.numpat = ms->numblocks; + of.numpos = ms->songlen; + of.numins = ms->numsamples; + of.numsmp = of.numins; + of.reppos = 0; + if ((mh->pMEDEXP) && (me->songname) && (me->songnamelen)) + { + char *name; + + _mm_fseek (modreader, me->songname, SEEK_SET); + name = _mm_malloc (me->songnamelen); + _mm_read_UBYTES (name, me->songnamelen, modreader); + of.songname = DupStr (name, me->songnamelen, 1); + free (name); + } + else + of.songname = DupStr (NULL, 0, 0); + if ((mh->pMEDEXP) && (me->annotxt) && (me->annolen)) + { + _mm_fseek (modreader, me->annotxt, SEEK_SET); + ReadComment (me->annolen); + } + + if (!AllocSamples ()) + return 0; + q = of.samples; + for (t = 0; t < of.numins; t++) + { + q->flags = SF_SIGNED; + q->volume = 64; + 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) + { +#ifdef MIKMOD_DEBUG + fputs ("\rNon-sample instruments not supported in MED loader yet\n", stderr); +#endif + if (!curious) + { + _mm_errno = MMERR_MED_SYNTHSAMPLES; + return 0; + } + s.length = 0; + } + + if (_mm_eof (modreader)) + { + _mm_errno = MMERR_LOADING_SAMPLEINFO; + return 0; + } + + q->length = s.length; + q->seekpos = _mm_ftell (modreader); + q->loopstart = ms->sample[t].rep << 1; + q->loopend = q->loopstart + (ms->sample[t].replen << 1); + + if (ms->sample[t].replen > 1) + q->flags |= SF_LOOP; + + /* don't load sample if length>='MMD0'... + such kluges make libmikmod's code unique !!! */ + if (q->length >= MMD0_string) + q->length = 0; + } + else + q->length = 0; + + if ((mh->pMEDEXP) && (me->exp_smp) && + (t < me->s_ext_entries) && (me->s_ext_entrsz >= 4)) + { + MEDINSTEXT ie; + + _mm_fseek (modreader, me->exp_smp + t * me->s_ext_entrsz, SEEK_SET); + ie.hold = _mm_read_UBYTE (modreader); + ie.decay = _mm_read_UBYTE (modreader); + ie.suppress_midi_off = _mm_read_UBYTE (modreader); + ie.finetune = _mm_read_SBYTE (modreader); + + q->speed = finetune[ie.finetune & 0xf]; + } + else + q->speed = 8363; + + if ((mh->pMEDEXP) && (me->iinfo) && + (t < me->i_ext_entries) && (me->i_ext_entrsz >= 40)) + { + MEDINSTINFO ii; + + _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 + q->samplename = NULL; + + q++; + } + + if (mh->id == MMD0_string) + { + if (!LoadMMD0Patterns ()) + { + _mm_errno = MMERR_LOADING_PATTERN; + return 0; + } + } + else if (mh->id == MMD1_string) + { + if (!LoadMMD1Patterns ()) + { + _mm_errno = MMERR_LOADING_PATTERN; + return 0; + } + } + else + { + _mm_errno = MMERR_NOT_A_MODULE; + return 0; + } + return 1; +} + +/*========== Loader information */ + +MLOADER load_med = +{ + NULL, + "MED", + "MED (OctaMED)", + MED_Init, + MED_Test, + MED_Load, + MED_Cleanup, + NULL +}; + +/* ex:set ts=4: */ |