/* 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$ Imago Orpheus (IMF) module loader ==============================================================================*/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include "unimod_priv.h" /*========== Module structure */ /* module header */ typedef struct IMFHEADER { CHAR songname[32]; UWORD ordnum; UWORD patnum; UWORD insnum; UWORD flags; UBYTE initspeed; UBYTE inittempo; UBYTE mastervol; UBYTE mastermult; UBYTE orders[256]; } IMFHEADER; /* channel settings */ typedef struct IMFCHANNEL { CHAR name[12]; UBYTE chorus; UBYTE reverb; UBYTE pan; UBYTE status; } IMFCHANNEL; /* instrument header */ #define IMFNOTECNT (10*OCTAVE) #define IMFENVCNT (16*2) typedef struct IMFINSTHEADER { CHAR name[32]; UBYTE what[IMFNOTECNT]; UWORD volenv[IMFENVCNT]; UWORD panenv[IMFENVCNT]; UWORD pitenv[IMFENVCNT]; UBYTE volpts; UBYTE volsus; UBYTE volbeg; UBYTE volend; UBYTE volflg; UBYTE panpts; UBYTE pansus; UBYTE panbeg; UBYTE panend; UBYTE panflg; UBYTE pitpts; UBYTE pitsus; UBYTE pitbeg; UBYTE pitend; UBYTE pitflg; UWORD volfade; UWORD numsmp; ULONG signature; } IMFINSTHEADER; /* sample header */ typedef struct IMFWAVHEADER { CHAR samplename[13]; ULONG length; ULONG loopstart; ULONG loopend; ULONG samplerate; UBYTE volume; UBYTE pan; UBYTE flags; } IMFWAVHEADER; typedef struct IMFNOTE { UBYTE note, ins, eff1, dat1, eff2, dat2; } IMFNOTE; /*========== Loader variables */ static CHAR IMF_Version[] = "Imago Orpheus"; static IMFNOTE *imfpat = NULL; static IMFHEADER *mh = NULL; /*========== Loader code */ static BOOL IMF_Test (void) { UBYTE id[4]; _mm_fseek (modreader, 0x3c, SEEK_SET); if (!_mm_read_UBYTES (id, 4, modreader)) return 0; if (!memcmp (id, "IM10", 4)) return 1; return 0; } static BOOL IMF_Init (void) { if (!(imfpat = (IMFNOTE *) _mm_malloc (32 * 256 * sizeof (IMFNOTE)))) return 0; if (!(mh = (IMFHEADER *) _mm_malloc (sizeof (IMFHEADER)))) return 0; return 1; } static void IMF_Cleanup (void) { FreeLinear (); _mm_free (imfpat); _mm_free (mh); } static BOOL IMF_ReadPattern (SLONG size, UWORD rows) { int row = 0, flag, ch; IMFNOTE *n, dummy; /* clear pattern data */ memset (imfpat, 255, 32 * 256 * sizeof (IMFNOTE)); while ((size > 0) && (row < rows)) { flag = _mm_read_UBYTE (modreader); size--; if (_mm_eof (modreader)) { _mm_errno = MMERR_LOADING_PATTERN; return 0; } if (flag) { ch = remap[flag & 31]; if (ch != -1) n = &imfpat[256 * ch + row]; else n = &dummy; if (flag & 32) { n->note = _mm_read_UBYTE (modreader); if (n->note >= 0xa0) n->note = 0xa0; /* note off */ n->ins = _mm_read_UBYTE (modreader); size -= 2; } if (flag & 64) { size -= 2; n->eff2 = _mm_read_UBYTE (modreader); n->dat2 = _mm_read_UBYTE (modreader); } if (flag & 128) { n->eff1 = _mm_read_UBYTE (modreader); n->dat1 = _mm_read_UBYTE (modreader); size -= 2; } } else row++; } if ((size) || (row != rows)) { _mm_errno = MMERR_LOADING_PATTERN; return 0; } return 1; } static void IMF_ProcessCmd (UBYTE eff, UBYTE inf) { if ((eff) && (eff != 255)) switch (eff) { case 0x01: /* set tempo */ UniEffect (UNI_S3MEFFECTA, inf); break; case 0x02: /* set BPM */ if (inf >= 0x20) UniEffect (UNI_S3MEFFECTT, inf); break; case 0x03: /* tone portamento */ UniEffect (UNI_ITEFFECTG, inf); break; case 0x04: /* porta + volslide */ UniEffect (UNI_ITEFFECTG, inf); UniEffect (UNI_S3MEFFECTD, 0); break; case 0x05: /* vibrato */ UniPTEffect (0x4, inf); break; case 0x06: /* vibrato + volslide */ UniPTEffect (0x4, inf); UniEffect (UNI_S3MEFFECTD, 0); break; case 0x07: /* fine vibrato */ UniEffect (UNI_ITEFFECTU, inf); break; case 0x08: /* tremolo */ UniEffect (UNI_S3MEFFECTR, inf); break; case 0x09: /* arpeggio */ UniPTEffect (0x0, inf); break; case 0x0a: /* panning */ UniPTEffect (0x8, (inf >= 128) ? 255 : (inf << 1)); break; case 0x0b: /* pan slide */ UniEffect (UNI_XMEFFECTP, inf); break; case 0x0c: /* set channel volume */ if (inf <= 64) UniPTEffect (0xc, inf); break; case 0x0d: /* volume slide */ UniEffect (UNI_S3MEFFECTD, inf); break; case 0x0e: /* fine volume slide */ if (inf) { if (inf >> 4) UniEffect (UNI_S3MEFFECTD, 0x0f | inf); else UniEffect (UNI_S3MEFFECTD, 0xf0 | inf); } else UniEffect (UNI_S3MEFFECTD, 0); break; case 0x0f: /* set finetune */ UniPTEffect (0xe, 0x50 | (inf >> 4)); break; #ifdef MIKMOD_DEBUG case 0x10: /* note slide up */ case 0x11: /* not slide down */ fprintf (stderr, "\rIMF effect 0x10/0x11 (note slide)" " not implemented (eff=%2X inf=%2X)\n", eff, inf); break; #endif case 0x12: /* slide up */ UniEffect (UNI_S3MEFFECTF, inf); break; case 0x13: /* slide down */ UniEffect (UNI_S3MEFFECTE, inf); break; case 0x14: /* fine slide up */ if (inf) { if (inf < 0x40) UniEffect (UNI_S3MEFFECTF, 0xe0 | (inf / 4)); else UniEffect (UNI_S3MEFFECTF, 0xf0 | (inf >> 4)); } else UniEffect (UNI_S3MEFFECTF, 0); break; case 0x15: /* fine slide down */ if (inf) { if (inf < 0x40) UniEffect (UNI_S3MEFFECTE, 0xe0 | (inf / 4)); else UniEffect (UNI_S3MEFFECTE, 0xf0 | (inf >> 4)); } else UniEffect (UNI_S3MEFFECTE, 0); break; /* 0x16 set filter cutoff (awe32) */ /* 0x17 filter side + resonance (awe32) */ case 0x18: /* sample offset */ UniPTEffect (0x9, inf); break; #ifdef MIKMOD_DEBUG case 0x19: /* set fine sample offset */ fprintf (stderr, "\rIMF effect 0x19 (fine sample offset)" " not implemented (inf=%2X)\n", inf); break; #endif case 0x1a: /* keyoff */ UniWriteByte (UNI_KEYOFF); break; case 0x1b: /* retrig */ UniEffect (UNI_S3MEFFECTQ, inf); break; case 0x1c: /* tremor */ UniEffect (UNI_S3MEFFECTI, inf); break; case 0x1d: /* position jump */ UniPTEffect (0xb, inf); break; case 0x1e: /* pattern break */ UniPTEffect (0xd, (inf >> 4) * 10 + (inf & 0xf)); break; case 0x1f: /* set master volume */ if (inf <= 64) UniEffect (UNI_XMEFFECTG, inf); break; case 0x20: /* master volume slide */ UniEffect (UNI_XMEFFECTH, inf); break; case 0x21: /* extended effects */ switch (inf >> 4) { case 0x1: /* set filter */ case 0x5: /* vibrato waveform */ case 0x8: /* tremolo waveform */ UniPTEffect (0xe, inf - 0x10); break; case 0xa: /* pattern loop */ UniPTEffect (0xe, 0x60 | (inf & 0xf)); break; case 0xb: /* pattern delay */ UniPTEffect (0xe, 0xe0 | (inf & 0xf)); break; case 0x3: /* glissando */ case 0xc: /* note cut */ case 0xd: /* note delay */ case 0xf: /* invert loop */ UniPTEffect (0xe, inf); break; case 0xe: /* ignore envelope */ UniEffect (UNI_ITEFFECTS0, 0x77); /* vol */ UniEffect (UNI_ITEFFECTS0, 0x79); /* pan */ UniEffect (UNI_ITEFFECTS0, 0x7b); /* pit */ break; } break; /* 0x22 chorus (awe32) */ /* 0x23 reverb (awe32) */ } } static UBYTE * IMF_ConvertTrack (IMFNOTE * tr, UWORD rows) { int t; UBYTE note, ins; UniReset (); for (t = 0; t < rows; t++) { note = tr[t].note; ins = tr[t].ins; if ((ins) && (ins != 255)) UniInstrument (ins - 1); if (note != 255) { if (note == 0xa0) { UniPTEffect (0xc, 0); /* Note cut */ if (tr[t].eff1 == 0x0c) tr[t].eff1 = 0; if (tr[t].eff2 == 0x0c) tr[t].eff2 = 0; } else UniNote (((note >> 4) * OCTAVE) + (note & 0xf)); } IMF_ProcessCmd (tr[t].eff1, tr[t].dat1); IMF_ProcessCmd (tr[t].eff2, tr[t].dat2); UniNewline (); } return UniDup (); } static BOOL IMF_Load (BOOL curious) { #define IMF_SMPINCR 64 int t, u, track = 0; IMFCHANNEL channels[32]; INSTRUMENT *d; SAMPLE *q; IMFWAVHEADER *wh = NULL, *s = NULL; ULONG *nextwav = NULL; UWORD wavcnt = 0; UBYTE id[4]; /* try to read the module header */ _mm_read_string (mh->songname, 32, modreader); mh->ordnum = _mm_read_I_UWORD (modreader); mh->patnum = _mm_read_I_UWORD (modreader); mh->insnum = _mm_read_I_UWORD (modreader); mh->flags = _mm_read_I_UWORD (modreader); _mm_fseek (modreader, 8, SEEK_CUR); mh->initspeed = _mm_read_UBYTE (modreader); mh->inittempo = _mm_read_UBYTE (modreader); mh->mastervol = _mm_read_UBYTE (modreader); mh->mastermult = _mm_read_UBYTE (modreader); _mm_fseek (modreader, 64, SEEK_SET); if (_mm_eof (modreader)) { _mm_errno = MMERR_LOADING_HEADER; return 0; } /* set module variables */ of.songname = DupStr (mh->songname, 31, 1); of.modtype = strdup (IMF_Version); of.numpat = mh->patnum; of.numins = mh->insnum; of.reppos = 0; of.initspeed = mh->initspeed; of.inittempo = mh->inittempo; of.initvolume = mh->mastervol << 1; of.flags |= UF_INST; if (mh->flags & 1) of.flags |= UF_LINEAR; /* read channel information */ of.numchn = 0; memset (remap, -1, 32 * sizeof (UBYTE)); for (t = 0; t < 32; t++) { _mm_read_string (channels[t].name, 12, modreader); channels[t].chorus = _mm_read_UBYTE (modreader); channels[t].reverb = _mm_read_UBYTE (modreader); channels[t].pan = _mm_read_UBYTE (modreader); channels[t].status = _mm_read_UBYTE (modreader); } /* bug in Imago Orpheus ? If only channel 1 is enabled, in fact we have to enable 16 channels */ if (!channels[0].status) { for (t = 1; t < 16; t++) if (channels[t].status != 1) break; if (t == 16) for (t = 1; t < 16; t++) channels[t].status = 0; } for (t = 0; t < 32; t++) { if (channels[t].status != 2) remap[t] = of.numchn++; else remap[t] = -1; } for (t = 0; t < 32; t++) if (remap[t] != -1) { of.panning[remap[t]] = channels[t].pan; of.chanvol[remap[t]] = channels[t].status ? 0 : 64; } if (_mm_eof (modreader)) { _mm_errno = MMERR_LOADING_HEADER; return 0; } /* read order list */ _mm_read_UBYTES (mh->orders, 256, modreader); if (_mm_eof (modreader)) { _mm_errno = MMERR_LOADING_HEADER; return 0; } of.numpos = 0; for (t = 0; t < mh->ordnum; t++) if (mh->orders[t] != 0xff) of.numpos++; if (!AllocPositions (of.numpos)) return 0; for (t = u = 0; t < mh->ordnum; t++) if (mh->orders[t] != 0xff) of.positions[u++] = mh->orders[t]; /* load pattern info */ of.numtrk = of.numpat * of.numchn; if (!AllocTracks ()) return 0; if (!AllocPatterns ()) return 0; for (t = 0; t < of.numpat; t++) { SLONG size; UWORD rows; size = (SLONG) _mm_read_I_UWORD (modreader); rows = _mm_read_I_UWORD (modreader); if ((rows > 256) || (size < 4)) { _mm_errno = MMERR_LOADING_PATTERN; return 0; } of.pattrows[t] = rows; if (!IMF_ReadPattern (size - 4, rows)) return 0; for (u = 0; u < of.numchn; u++) if (!(of.tracks[track++] = IMF_ConvertTrack (&imfpat[u * 256], rows))) return 0; } /* load instruments */ if (!AllocInstruments ()) return 0; d = of.instruments; for (t = 0; t < of.numins; t++) { IMFINSTHEADER ih; memset (d->samplenumber, 0xff, INSTNOTES * sizeof (UWORD)); /* read instrument header */ _mm_read_string (ih.name, 32, modreader); d->insname = DupStr (ih.name, 31, 1); _mm_read_UBYTES (ih.what, IMFNOTECNT, modreader); _mm_fseek (modreader, 8, SEEK_CUR); _mm_read_I_UWORDS (ih.volenv, IMFENVCNT, modreader); _mm_read_I_UWORDS (ih.panenv, IMFENVCNT, modreader); _mm_read_I_UWORDS (ih.pitenv, IMFENVCNT, modreader); #define IMF_FinishLoadingEnvelope(name) \ ih.name##pts=_mm_read_UBYTE(modreader); \ ih.name##sus=_mm_read_UBYTE(modreader); \ ih.name##beg=_mm_read_UBYTE(modreader); \ ih.name##end=_mm_read_UBYTE(modreader); \ ih.name##flg=_mm_read_UBYTE(modreader); \ _mm_read_UBYTE(modreader); \ _mm_read_UBYTE(modreader); \ _mm_read_UBYTE(modreader); IMF_FinishLoadingEnvelope (vol); IMF_FinishLoadingEnvelope (pan); IMF_FinishLoadingEnvelope (pit); ih.volfade = _mm_read_I_UWORD (modreader); ih.numsmp = _mm_read_I_UWORD (modreader); _mm_read_UBYTES (id, 4, modreader); if (memcmp (id, "II10", 4)) { if (nextwav) free (nextwav); if (wh) free (wh); _mm_errno = MMERR_LOADING_SAMPLEINFO; return 0; } if ((ih.numsmp > 16) || (ih.volpts > IMFENVCNT / 2) || (ih.panpts > IMFENVCNT / 2) || (ih.pitpts > IMFENVCNT / 2) || (_mm_eof (modreader))) { if (nextwav) free (nextwav); if (wh) free (wh); _mm_errno = MMERR_LOADING_SAMPLEINFO; return 0; } for (u = 0; u < IMFNOTECNT; u++) d->samplenumber[u] = ih.what[u] > ih.numsmp ? 0xffff : ih.what[u] + of.numsmp; d->volfade = ih.volfade; #define IMF_ProcessEnvelope(name) \ for (u = 0; u < (IMFENVCNT >> 1); u++) { \ d->name##env[u].pos = ih.name##env[u << 1]; \ d->name##env[u].val = ih.name##env[(u << 1)+ 1]; \ } \ if (ih.name##flg&1) d->name##flg|=EF_ON; \ if (ih.name##flg&2) d->name##flg|=EF_SUSTAIN; \ if (ih.name##flg&4) d->name##flg|=EF_LOOP; \ d->name##susbeg=d->name##susend=ih.name##sus; \ d->name##beg=ih.name##beg; \ d->name##end=ih.name##end; \ d->name##pts=ih.name##pts; \ \ if ((d->name##flg&EF_ON)&&(d->name##pts<2)) \ d->name##flg&=~EF_ON; IMF_ProcessEnvelope (vol); IMF_ProcessEnvelope (pan); IMF_ProcessEnvelope (pit); #undef IMF_ProcessEnvelope if (ih.pitflg & 1) { d->pitflg &= ~EF_ON; #ifdef MIKMOD_DEBUG fputs ("\rFilter envelopes not supported yet\n", stderr); #endif } /* gather sample information */ for (u = 0; u < ih.numsmp; u++, s++) { /* allocate more room for sample information if necessary */ if (of.numsmp + u == wavcnt) { wavcnt += IMF_SMPINCR; if (!(nextwav = realloc (nextwav, wavcnt * sizeof (ULONG)))) { if (wh) free (wh); _mm_errno = MMERR_OUT_OF_MEMORY; return 0; } if (!(wh = realloc (wh, wavcnt * sizeof (IMFWAVHEADER)))) { free (nextwav); _mm_errno = MMERR_OUT_OF_MEMORY; return 0; } s = wh + (wavcnt - IMF_SMPINCR); } _mm_read_string (s->samplename, 13, modreader); _mm_read_UBYTE (modreader); _mm_read_UBYTE (modreader); _mm_read_UBYTE (modreader); s->length = _mm_read_I_ULONG (modreader); s->loopstart = _mm_read_I_ULONG (modreader); s->loopend = _mm_read_I_ULONG (modreader); s->samplerate = _mm_read_I_ULONG (modreader); s->volume = _mm_read_UBYTE (modreader) & 0x7f; s->pan = _mm_read_UBYTE (modreader); _mm_fseek (modreader, 14, SEEK_CUR); s->flags = _mm_read_UBYTE (modreader); _mm_fseek (modreader, 11, SEEK_CUR); _mm_read_UBYTES (id, 4, modreader); if (((memcmp (id, "IS10", 4)) && (memcmp (id, "IW10", 4))) || (_mm_eof (modreader))) { free (nextwav); free (wh); _mm_errno = MMERR_LOADING_SAMPLEINFO; return 0; } nextwav[of.numsmp + u] = _mm_ftell (modreader); _mm_fseek (modreader, s->length, SEEK_CUR); } of.numsmp += ih.numsmp; d++; } /* sanity check */ if (!of.numsmp) { if (nextwav) free (nextwav); if (wh) free (wh); _mm_errno = MMERR_LOADING_SAMPLEINFO; return 0; } /* load samples */ if (!AllocSamples ()) { free (nextwav); free (wh); return 0; } if (!AllocLinear ()) { free (nextwav); free (wh); return 0; } q = of.samples; s = wh; for (u = 0; u < of.numsmp; u++, s++, q++) { q->samplename = DupStr (s->samplename, 12, 1); q->length = s->length; q->loopstart = s->loopstart; q->loopend = s->loopend; q->volume = s->volume; q->speed = s->samplerate; if (of.flags & UF_LINEAR) q->speed = speed_to_finetune (s->samplerate << 1, u); q->panning = s->pan; q->seekpos = nextwav[u]; q->flags |= SF_SIGNED; if (s->flags & 0x1) q->flags |= SF_LOOP; if (s->flags & 0x2) q->flags |= SF_BIDI; if (s->flags & 0x8) q->flags |= SF_OWNPAN; if (s->flags & 0x4) { q->flags |= SF_16BITS; q->length >>= 1; q->loopstart >>= 1; q->loopend >>= 1; } } d = of.instruments; s = wh; for (u = 0; u < of.numins; u++, d++) { for (t = 0; t < IMFNOTECNT; t++) { if (d->samplenumber[t] >= of.numsmp) d->samplenote[t] = 255; else if (of.flags & UF_LINEAR) { int note = (int) d->samplenote[u] + noteindex[d->samplenumber[u]]; d->samplenote[u] = (note < 0) ? 0 : (note > 255 ? 255 : note); } else d->samplenote[t] = t; } } free (wh); free (nextwav); return 1; } static CHAR * IMF_LoadTitle (void) { CHAR s[31]; _mm_fseek (modreader, 0, SEEK_SET); if (!_mm_read_UBYTES (s, 31, modreader)) return NULL; return (DupStr (s, 31, 1)); } /*========== Loader information */ MLOADER load_imf = { NULL, "IMF", "IMF (Imago Orpheus)", IMF_Init, IMF_Test, IMF_Load, IMF_Cleanup, IMF_LoadTitle }; /* ex:set ts=4: */