/* Extended Module Player * Copyright (C) 1996-2001 Claudio Matsuoka and Hipolito Carraro Jr * * This file is part of the Extended Module Player and is distributed * under the terms of the GNU General Public License. See doc/COPYING * for more information. * * $Id: med_load.c,v 1.3 2001/01/13 19:58:02 claudio Exp $ */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "med.h" #include "load.h" #define NUM_INST_TYPES 9 static char *inst_type[] = { "HYB", /* -2 */ "SYN", /* -1 */ "SMP", /* 0 */ "I5O", /* 1 */ "I3O", /* 2 */ "I2O", /* 3 */ "I4O", /* 4 */ "I6O", /* 5 */ "I7O", /* 6 */ "EXT", /* 7 */ }; static int bpmon, bpmlen; static void xlat_fx (uint8 *fxt, uint8 *fxp) { switch (*fxt) { case 0x05: /* Old vibrato */ *fxp = (LSN (*fxp) << 4) | MSN (*fxp); break; case 0x06: /* Not defined in MED 3.2 */ case 0x07: /* Not defined in MED 3.2 */ break; case 0x08: /* Set hold/decay */ break; case 0x09: /* Set secondary tempo */ *fxt = 0x0f; break; case 0x0d: /* Volume slide */ *fxt = 0x0a; break; case 0x0e: /* Synth JMP */ break; case 0x0f: if (*fxp == 0x00) { /* Jump to next block */ *fxt = 0x0d; break; } else if (*fxp <= 0x0a) { break; } else if (*fxp <= 0xf0) { if (*fxp < 0x21) *fxp = 0x21; break; } switch (*fxp) { case 0xf1: /* Play note twice */ break; case 0xf2: /* Delay note */ break; case 0xf3: /* Play note three times */ break; case 0xf8: /* Turn filter off */ case 0xf9: /* Turn filter on */ case 0xfa: /* MIDI pedal on */ case 0xfb: /* MIDI pedal off */ *fxt = *fxp = 0; break; case 0xfd: /* Set pitch */ *fxt = *fxp = 0; break; case 0xfe: /* End of song */ *fxt = *fxp = 0; break; case 0xff: /* Note cut */ *fxt = *fxp = 0; break; } break; } } void bytecopy(void *dst, void *src, int size) { while (size-- > 0) *(char *)dst++ = *(char *)src++; } int med_load (FILE *f) { int i, j, k; char *mmd, m[4]; struct MMD0 *header; struct MMD0song *song; struct MMD2song *song2; struct MMD0Block *block0; struct MMD1Block *block1; struct PlaySeq **playseq; struct InstrHdr *instr; struct SynthInstr *synth; struct SynthWF *synthwf; struct MMD0exp *expdata; struct InstrExt *instrext = NULL; struct xxm_event *event; int ver = 0; int smp_idx = 0; uint8 *e; LOAD_INIT (); fread (&m, 1, 4, f); fseek (f, -4, SEEK_CUR); if (m[0] != 'M' || m[1] != 'M' || m[2] != 'D' || (m[3] != '0' && m[3] != '1' && m[3] != '2' && m[3] != '3')) return -1; ver = m[3] - '1' + 1; if (ver > 3) { report ("MMD%c format currently unsupported.\n", m[3]); return -1; } mmd = calloc (1, xmp_ctl->size); fread (mmd, 1, xmp_ctl->size, f); /* Slurp module to memory */ header = (struct MMD0 *)mmd; B_ENDIAN32 ((uint32)header->modlen); B_ENDIAN32 ((uint32)header->song); B_ENDIAN32 ((uint32)header->blockarr); B_ENDIAN32 ((uint32)header->smplarr); B_ENDIAN32 ((uint32)header->expdata); song = (struct MMD0song *)(mmd + (uint32)header->song); song2 = (struct MMD2song *)(mmd + (uint32)header->song); expdata = header->expdata ? (struct MMD0exp *)(mmd + (uint32)header->expdata) : NULL; if (expdata) { B_ENDIAN32 ((uint32)expdata->nextmod); B_ENDIAN32 ((uint32)expdata->exp_smp); B_ENDIAN16 ((uint16)expdata->s_ext_entries); B_ENDIAN16 ((uint16)expdata->s_ext_entrsz); B_ENDIAN32 ((uint32)expdata->annotxt); B_ENDIAN32 ((uint32)expdata->annolen); B_ENDIAN32 ((uint32)expdata->iinfo); B_ENDIAN16 ((uint16)expdata->i_ext_entries); B_ENDIAN16 ((uint16)expdata->i_ext_entrsz); B_ENDIAN32 ((uint32)expdata->jumpmask); B_ENDIAN32 ((uint32)expdata->rgbtable); B_ENDIAN32 ((uint32)expdata->n_info); B_ENDIAN32 ((uint32)expdata->songname); B_ENDIAN32 ((uint32)expdata->songnamelen); B_ENDIAN32 ((uint32)expdata->dumps); } for (i = 0; i < 63; i++) { B_ENDIAN16 (song->sample[i].rep); B_ENDIAN16 (song->sample[i].replen); } B_ENDIAN16 (song->numblocks); B_ENDIAN16 (song->songlen); B_ENDIAN16 (song->deftempo); xmp_ctl->c4rate = C4_NTSC_RATE; xmp_ctl->fetch |= song->flags & FLAG_STSLIDE ? 0 : XMP_CTL_VSALL; bpmon = song->flags2 & FLAG2_BPM; bpmlen = 1 + (song->flags2 & FLAG2_BMASK); xmp_ctl->fetch |= bpmon ? 0 : XMP_CTL_MEDBPM; #warning FIXME: med tempos are incorrectly handled xxh->tpo = song->tempo2; xxh->bpm = bpmon ? song->deftempo * bpmlen / 4 : song->deftempo; if (!bpmon && xxh->bpm <= 10) xxh->bpm = xxh->bpm * 33 / 6; xxh->pat = song->numblocks; xxh->ins = song->numsamples; if (ver < 2) xxh->len = song->songlen; xxh->rst = 0; xxh->chn = 0; /* We can have more samples than instruments -- each synth instrument * can have up to 64 samples! */ for (i = 0; i < xxh->ins; i++) { bytecopy(&instr, mmd + (uint32)header->smplarr + i * 4, 4); B_ENDIAN32 ((uint32)instr); if (!instr) continue; instr = (struct InstrHdr *)(mmd + (uint32)instr); synth = (struct SynthInstr *)instr; switch (instr->type) { case -1: /* Synth */ xxh->smp += synth->wforms; break; case -2: /* Hybrid */ case 0: /* Sample */ xxh->smp++; break; case 1: /* IFF5OCT */ case 2: /* IFF3OCT */ case 3: /* IFF2OCT */ case 4: /* IFF4OCT */ case 5: /* IFF6OCT */ case 6: /* IFF7OCT */ break; } } if (ver < 2) { memcpy (xxo, song->playseq, xxh->len); } else { B_ENDIAN32 ((uint32)song2->playseqtable); playseq = (struct PlaySeq **)(mmd + (uint32)song2->playseqtable); B_ENDIAN32 ((uint32)playseq[0]); playseq[0] = (struct PlaySeq *)(mmd + (uint32)playseq[0]); B_ENDIAN16 ((uint16)playseq[0]->length); xxh->len = playseq[0]->length; if (xxh->len > 0xff) xxh->len = 0xff; for (i = 0; i < xxh->len; i++) { B_ENDIAN16 ((uint16)playseq[0]->seq[i]); xxo[i] = playseq[0]->seq[i]; } } /* Quickly scan patterns to check the number of channels */ for (i = 0; i < xxh->pat; i++) { bytecopy(&block0, mmd + (uint32)header->blockarr + i * 4, 4); B_ENDIAN32 ((uint32)block0); block0 = (struct MMD0Block *)(mmd + (uint32)block0); block1 = (struct MMD1Block *)block0; if (ver > 0) { B_ENDIAN16 (block1->numtracks); B_ENDIAN16 (block1->lines); if (block1->numtracks > xxh->chn) xxh->chn = block1->numtracks; } else { if (block0->numtracks > xxh->chn) xxh->chn = block0->numtracks; } } xxh->trk = xxh->pat * xxh->chn; if (expdata && expdata->songname) strncpy (xmp_ctl->name, mmd + (uint32)expdata->songname, XMP_DEF_NAMESIZE); sprintf (xmp_ctl->type, "MMD%c (MED)", m[3]); MODULE_INFO (); if (V (0)) { report ("BPM mode : %s", bpmon ? "on" : "off"); if (bpmon) report (" (length = %d)", bpmlen); report ("\n"); if (song->playtransp) report ("Song transpose : %d semitones\n", song->playtransp); report ("Stored patterns: %d ", xxh->pat); } /* Read and convert patterns */ PATTERN_INIT (); for (i = 0; i < xxh->pat; i++) { bytecopy(&block0, mmd + (uint32)header->blockarr + i * 4, 4); B_ENDIAN32 ((uint32)block0); block0 = (struct MMD0Block *)(mmd + (uint32)block0); block1 = (struct MMD1Block *)block0; PATTERN_ALLOC (i); if (ver > 0) { xxp[i]->rows = block1->lines + 1; TRACK_ALLOC (i); e = (uint8 *)block1 + sizeof (struct MMD1Block); for (j = 0; j < xxp[i]->rows; j++) { for (k = 0; k < block1->numtracks; k++, e += 4) { event = &EVENT (i, k, j); event->note = e[0] & 0x7f; if (event->note) event->note += 36 + song->playtransp; event->ins = e[1] & 0x3f; event->fxt = e[2]; event->fxp = e[3]; xlat_fx (&event->fxt, &event->fxp); } } } else { xxp[i]->rows = block0->lines + 1; TRACK_ALLOC (i); e = (uint8 *)block0 + sizeof (struct MMD0Block); for (j = 0; j < xxp[i]->rows; j++) { for (k = 0; k < block0->numtracks; k++, e += 3) { event = &EVENT (i, k, j); if ((event->note = e[0] & 0x3f)) event->note += 36; event->ins = (e[1] >> 4) | ((e[0] & 0x80) >> 3) | ((e[0] & 0x40) >> 1); event->fxt = e[1] & 0x0f; event->fxp = e[2]; xlat_fx (&event->fxt, &event->fxp); } } } if (V (0)) report ("."); } if (V (0)) report ("\n"); INSTRUMENT_INIT (); med_vol_table = calloc (sizeof (uint8 *), xxh->ins); med_wav_table = calloc (sizeof (uint8 *), xxh->ins); /* Read and convert instruments and samples */ if (V (0)) report ("Instruments : %d ", xxh->ins); if (V (1)) report ("\n Instrument name " "Typ Len LBeg LEnd Vl Xp Ft"); for (smp_idx = i = 0; i < xxh->ins; i++) { bytecopy(&instr, mmd + (uint32)header->smplarr + i * 4, 4); B_ENDIAN32 ((uint32)instr); if (!instr) continue; instr = (struct InstrHdr *)(mmd + (uint32)instr); synth = (struct SynthInstr *)instr; B_ENDIAN32 (instr->length); B_ENDIAN16 (instr->type); if (V (1)) { report ("\n[%2x] %-40.40s ", i, expdata && expdata->iinfo ? mmd + (uint32)expdata->iinfo + expdata->i_ext_entrsz * i : ""); report ("%s ", instr->type + 2 <= NUM_INST_TYPES ? inst_type[instr->type + 2] : "???"); } if (expdata) instrext = (struct InstrExt *)(mmd + (uint32)expdata->exp_smp); switch (instr->type) { case -2: /* Hybrid */ B_ENDIAN16 (synth->rep); B_ENDIAN16 (synth->replen); B_ENDIAN16 (synth->voltbllen); B_ENDIAN16 (synth->wftbllen); B_ENDIAN16 (synth->wforms); if (expdata) instrext = (struct InstrExt *)(mmd + (uint32)expdata->exp_smp); xxi[i] = calloc (sizeof (struct xxm_instrument), 1); xxih[i].nsm = 1; xxih[i].vts = synth->volspeed; xxih[i].wts = synth->wfspeed; B_ENDIAN32 ((uint32)synth->wf[0]); instr = (struct InstrHdr *)((char *)synth + (uint32)synth->wf[0]); B_ENDIAN32 (instr->length); B_ENDIAN16 (instr->type); xxi[i][0].pan = 0x80; xxi[i][0].vol = song->sample[i].svol; xxi[i][0].xpo = song->sample[i].strans; xxi[i][0].sid = smp_idx; xxi[i][0].fin = expdata ? instrext->finetune << 4 : 0; xxs[smp_idx].len = instr->length; xxs[smp_idx].lps = 2 * song->sample[i].rep; xxs[smp_idx].lpe = xxs[smp_idx].lps + 2 * song->sample[i].replen; xxs[smp_idx].flg = song->sample[i].replen > 1 ? WAVE_LOOPING : 0; if (V (1)) { report ("%05x %05x %05x %02x %02x %+1d ", xxs[smp_idx].len, xxs[smp_idx].lps, xxs[smp_idx].lpe, xxi[i][0].vol, (uint8)xxi[i][0].xpo, xxi[i][0].fin >> 4); } xmp_drv_loadpatch (NULL, smp_idx, xmp_ctl->c4rate, XMP_SMP_NOLOAD, &xxs[smp_idx], (char *)instr + sizeof (struct InstrHdr)); smp_idx++; med_vol_table[i] = calloc (1, synth->voltbllen); memcpy (med_vol_table[i], synth->voltbl, synth->voltbllen); med_wav_table[i] = calloc (1, synth->wftbllen); memcpy (med_wav_table[i], synth->wftbl, synth->wftbllen); if (V (0)) report ("."); break; case -1: /* Synthetic */ B_ENDIAN16 (synth->rep); B_ENDIAN16 (synth->replen); B_ENDIAN16 (synth->voltbllen); B_ENDIAN16 (synth->wftbllen); B_ENDIAN16 (synth->wforms); if (V (1)) report ("VS:%02x WS:%02x WF:%02x %02x %02x %+1d ", synth->volspeed, synth->wfspeed, synth->wforms, song->sample[i].svol, (uint8) song->sample[i].strans, expdata ? instrext->finetune : 0); xxi[i] = calloc (sizeof (struct xxm_instrument), synth->wforms); xxih[i].nsm = synth->wforms; xxih[i].vts = synth->volspeed; xxih[i].wts = synth->wfspeed; for (j = 0; j < synth->wforms; j++) { B_ENDIAN32 ((uint32)synth->wf[j]); xxi[i][j].pan = 0x80; xxi[i][j].vol = song->sample[i].svol; xxi[i][j].xpo = song->sample[i].strans - 24; xxi[i][j].sid = smp_idx; xxi[i][j].fin = expdata ? instrext->finetune << 4 : 0; synthwf = (struct SynthWF *)((char *)synth + (uint32)synth->wf[j]); B_ENDIAN16 (synthwf->length); xxs[smp_idx].len = synthwf->length * 2; xxs[smp_idx].lps = 0; xxs[smp_idx].lpe = xxs[smp_idx].len; xxs[smp_idx].flg = WAVE_LOOPING; xmp_drv_loadpatch (NULL, smp_idx, xmp_ctl->c4rate, XMP_SMP_NOLOAD | XMP_SMP_8X, &xxs[smp_idx], &synthwf->wfdata[0]); smp_idx++; } med_vol_table[i] = calloc (1, synth->voltbllen); memcpy (med_vol_table[i], synth->voltbl, synth->voltbllen); med_wav_table[i] = calloc (1, synth->wftbllen); memcpy (med_wav_table[i], synth->wftbl, synth->wftbllen); if (V (0)) report ("."); break; case 0: /* Sample */ xxi[i] = calloc (sizeof (struct xxm_instrument), 1); xxih[i].nsm = 1; xxi[i][0].vol = song->sample[i].svol; xxi[i][0].xpo = song->sample[i].strans; xxi[i][0].sid = smp_idx; xxi[i][0].fin = expdata ? instrext->finetune << 4 : 0; if (ver >= 2) xxi[i][0].xpo -= 24; xxs[smp_idx].len = instr->length; xxs[smp_idx].lps = 2 * song->sample[i].rep; xxs[smp_idx].lpe = xxs[smp_idx].lps + 2 * song->sample[i].replen; xxs[smp_idx].flg = song->sample[i].replen > 1 ? WAVE_LOOPING : 0; if (V (1)) { report ("%05x %05x %05x %02x %02x %+1d ", xxs[smp_idx].len, xxs[smp_idx].lps, xxs[smp_idx].lpe, xxi[i][0].vol, (uint8)xxi[i][0].xpo, xxi[i][0].fin >> 4); } xmp_drv_loadpatch (NULL, smp_idx, xmp_ctl->c4rate, XMP_SMP_NOLOAD, &xxs[smp_idx], (char *)instr + sizeof (struct InstrHdr)); if (V (0)) report ("."); smp_idx++; break; default: if (V (1)) report ("(unsupported)"); } } if (V (0)) report ("\n"); for (i = 0; i < xxh->chn; i++) { if (ver < 2) { xxc[i].vol = song->trkvol[i]; xxc[i].pan = (((i + 1) / 2) % 2) * 0xff; } else { xxc[i].vol = 0x40; xxc[i].pan = (((i + 1) / 2) % 2) * 0xff; } } free (mmd); return 0; }