1 /* MikMod sound library
\r
2 (c) 1998, 1999, 2000, 2001, 2002 Miodrag Vallat and others - see file
\r
3 AUTHORS for complete list.
\r
5 This library is free software; you can redistribute it and/or modify
\r
6 it under the terms of the GNU Library General Public License as
\r
7 published by the Free Software Foundation; either version 2 of
\r
8 the License, or (at your option) any later version.
\r
10 This program is distributed in the hope that it will be useful,
\r
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
13 GNU Library General Public License for more details.
\r
15 You should have received a copy of the GNU Library General Public
\r
16 License along with this library; if not, write to the Free Software
\r
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
\r
21 /*==============================================================================
\r
23 $Id: load_med.c,v 1.1.1.1 2004/01/21 01:36:35 raph Exp $
\r
25 Amiga MED module loader
\r
27 ==============================================================================*/
\r
29 #ifdef HAVE_CONFIG_H
\r
33 #ifdef HAVE_UNISTD_H
\r
38 #ifdef HAVE_MEMORY_H
\r
43 #include "mikmod_internals.h"
\r
46 extern int fprintf(FILE *, const char *, ...);
\r
49 /*========== Module information */
\r
51 typedef struct MEDHEADER {
\r
54 ULONG MEDSONGP; /* struct MEDSONG *song; */
\r
55 UWORD psecnum; /* for the player routine, MMD2 only */
\r
56 UWORD pseq; /* " " " " */
\r
57 ULONG MEDBlockPP; /* struct MEDBlock **blockarr; */
\r
59 ULONG MEDINSTHEADERPP; /* struct MEDINSTHEADER **smplarr; */
\r
61 ULONG MEDEXPP; /* struct MEDEXP *expdata; */
\r
63 UWORD pstate; /* some data for the player routine */
\r
69 UBYTE extra_songs; /* number of songs - 1 */
\r
72 typedef struct MEDSAMPLE {
\r
73 UWORD rep, replen; /* offs: 0(s), 2(s) */
\r
74 UBYTE midich; /* offs: 4(s) */
\r
75 UBYTE midipreset; /* offs: 5(s) */
\r
76 UBYTE svol; /* offs: 6(s) */
\r
77 SBYTE strans; /* offs: 7(s) */
\r
80 typedef struct MEDSONG {
\r
81 MEDSAMPLE sample[63]; /* 63 * 8 bytes = 504 bytes */
\r
82 UWORD numblocks; /* offs: 504 */
\r
83 UWORD songlen; /* offs: 506 */
\r
84 UBYTE playseq[256]; /* offs: 508 */
\r
85 UWORD deftempo; /* offs: 764 */
\r
86 SBYTE playtransp; /* offs: 766 */
\r
87 UBYTE flags; /* offs: 767 */
\r
88 UBYTE flags2; /* offs: 768 */
\r
89 UBYTE tempo2; /* offs: 769 */
\r
90 UBYTE trkvol[16]; /* offs: 770 */
\r
91 UBYTE mastervol; /* offs: 786 */
\r
92 UBYTE numsamples; /* offs: 787 */
\r
95 typedef struct MEDEXP {
\r
96 ULONG nextmod; /* pointer to next module */
\r
97 ULONG exp_smp; /* pointer to MEDINSTEXT array */
\r
98 UWORD s_ext_entries;
\r
100 ULONG annotxt; /* pointer to annotation text */
\r
102 ULONG iinfo; /* pointer to MEDINSTINFO array */
\r
103 UWORD i_ext_entries;
\r
104 UWORD i_ext_entrsz;
\r
107 ULONG channelsplit;
\r
109 ULONG songname; /* pointer to songname */
\r
112 ULONG reserved2[7];
\r
115 typedef struct MMD0NOTE {
\r
119 typedef struct MMD1NOTE {
\r
123 typedef struct MEDINSTHEADER {
\r
126 /* Followed by actual data */
\r
129 typedef struct MEDINSTEXT {
\r
132 UBYTE suppress_midi_off;
\r
136 typedef struct MEDINSTINFO {
\r
140 /*========== Loader variables */
\r
142 #define MMD0_string 0x4D4D4430
\r
143 #define MMD1_string 0x4D4D4431
\r
145 static MEDHEADER *mh = NULL;
\r
146 static MEDSONG *ms = NULL;
\r
147 static MEDEXP *me = NULL;
\r
148 static ULONG *ba = NULL;
\r
149 static MMD0NOTE *mmd0pat = NULL;
\r
150 static MMD1NOTE *mmd1pat = NULL;
\r
152 static BOOL decimalvolumes;
\r
153 static BOOL bpmtempos;
\r
155 #define d0note(row,col) mmd0pat[((row)*(UWORD)of.numchn)+(col)]
\r
156 #define d1note(row,col) mmd1pat[((row)*(UWORD)of.numchn)+(col)]
\r
158 static CHAR MED_Version[] = "OctaMED (MMDx)";
\r
160 /*========== Loader code */
\r
162 BOOL MED_Test(void)
\r
166 if (!_mm_read_UBYTES(id, 4, modreader))
\r
168 if ((!memcmp(id, "MMD0", 4)) || (!memcmp(id, "MMD1", 4)))
\r
173 BOOL MED_Init(void)
\r
175 if (!(me = (MEDEXP *)_mm_malloc(sizeof(MEDEXP))))
\r
177 if (!(mh = (MEDHEADER *)_mm_malloc(sizeof(MEDHEADER))))
\r
179 if (!(ms = (MEDSONG *)_mm_malloc(sizeof(MEDSONG))))
\r
184 void MED_Cleanup(void)
\r
194 static void EffectCvt(UBYTE eff, UBYTE dat)
\r
197 /* 0x0 0x1 0x2 0x3 0x4 PT effects */
\r
198 case 0x5: /* PT vibrato with speed/depth nibbles swapped */
\r
199 UniPTEffect(0x4, (dat >> 4) | ((dat & 0xf) << 4));
\r
201 /* 0x6 0x7 not used */
\r
205 case 0x8: /* midi hold/decay */
\r
210 dat = of.initspeed;
\r
211 UniEffect(UNI_S3MEFFECTA, dat);
\r
215 dat = of.initspeed;
\r
218 UniPTEffect(0xf, dat);
\r
220 UniEffect(UNI_MEDSPEED, ((UWORD)dat * 125) / (33 * 4));
\r
223 /* 0xa 0xb PT effects */
\r
225 if (decimalvolumes)
\r
226 dat = (dat >> 4) * 10 + (dat & 0xf);
\r
227 UniPTEffect(0xc, dat);
\r
229 case 0xd: /* same as PT volslide */
\r
230 UniPTEffect(0xa, dat);
\r
232 case 0xe: /* synth jmp - midi */
\r
236 case 0: /* patternbreak */
\r
237 UniPTEffect(0xd, 0);
\r
239 case 0xf1: /* play note twice */
\r
240 UniWriteByte(UNI_MEDEFFECTF1);
\r
242 case 0xf2: /* delay note */
\r
243 UniWriteByte(UNI_MEDEFFECTF2);
\r
245 case 0xf3: /* play note three times */
\r
246 UniWriteByte(UNI_MEDEFFECTF3);
\r
248 case 0xfe: /* stop playing */
\r
249 UniPTEffect(0xb, of.numpat);
\r
251 case 0xff: /* note cut */
\r
252 UniPTEffect(0xc, 0);
\r
256 UniPTEffect(0xf, dat);
\r
257 else if (dat <= 240) {
\r
259 UniPTEffect(0xf, (dat < 32) ? 32 : dat);
\r
261 UniEffect(UNI_MEDSPEED, ((UWORD)dat * 125) / 33);
\r
265 default: /* all normal PT effects are handled here */
\r
266 UniPTEffect(eff, dat);
\r
271 static UBYTE *MED_Convert1(int count, int col)
\r
274 UBYTE inst, note, eff, dat;
\r
278 for (t = 0; t < count; t++) {
\r
279 n = &d1note(t, col);
\r
281 note = n->a & 0x7f;
\r
282 inst = n->b & 0x3f;
\r
287 UniInstrument(inst - 1);
\r
289 UniNote(note + 3 * OCTAVE - 1);
\r
290 EffectCvt(eff, dat);
\r
296 static UBYTE *MED_Convert0(int count, int col)
\r
299 UBYTE a, b, inst, note, eff, dat;
\r
303 for (t = 0; t < count; t++) {
\r
304 n = &d0note(t, col);
\r
310 a = ((a & 1) << 1) | (a >> 1);
\r
311 inst = (b >> 4) | (a << 4);
\r
316 UniInstrument(inst - 1);
\r
318 UniNote(note + 3 * OCTAVE - 1);
\r
319 EffectCvt(eff, dat);
\r
325 static BOOL LoadMEDPatterns(void)
\r
328 UWORD numtracks, numlines, maxlines = 0, track = 0;
\r
331 /* first, scan patterns to see how many channels are used */
\r
332 for (t = 0; t < of.numpat; t++) {
\r
333 _mm_fseek(modreader, ba[t], SEEK_SET);
\r
334 numtracks = _mm_read_UBYTE(modreader);
\r
335 numlines = _mm_read_UBYTE(modreader);
\r
337 if (numtracks > of.numchn)
\r
338 of.numchn = numtracks;
\r
339 if (numlines > maxlines)
\r
340 maxlines = numlines;
\r
343 of.numtrk = of.numpat * of.numchn;
\r
344 if (!AllocTracks())
\r
346 if (!AllocPatterns())
\r
351 (MMD0NOTE *)_mm_calloc(of.numchn * (maxlines + 1),
\r
352 sizeof(MMD0NOTE)))) return 0;
\r
354 /* second read: read and convert patterns */
\r
355 for (t = 0; t < of.numpat; t++) {
\r
356 _mm_fseek(modreader, ba[t], SEEK_SET);
\r
357 numtracks = _mm_read_UBYTE(modreader);
\r
358 numlines = _mm_read_UBYTE(modreader);
\r
360 of.pattrows[t] = ++numlines;
\r
361 memset(mmdp = mmd0pat, 0, of.numchn * maxlines * sizeof(MMD0NOTE));
\r
362 for (row = numlines; row; row--) {
\r
363 for (col = numtracks; col; col--, mmdp++) {
\r
364 mmdp->a = _mm_read_UBYTE(modreader);
\r
365 mmdp->b = _mm_read_UBYTE(modreader);
\r
366 mmdp->c = _mm_read_UBYTE(modreader);
\r
370 for (col = 0; col < of.numchn; col++)
\r
371 of.tracks[track++] = MED_Convert0(numlines, col);
\r
376 static BOOL LoadMMD1Patterns(void)
\r
379 UWORD numtracks, numlines, maxlines = 0, track = 0;
\r
382 /* first, scan patterns to see how many channels are used */
\r
383 for (t = 0; t < of.numpat; t++) {
\r
384 _mm_fseek(modreader, ba[t], SEEK_SET);
\r
385 numtracks = _mm_read_M_UWORD(modreader);
\r
386 numlines = _mm_read_M_UWORD(modreader);
\r
387 if (numtracks > of.numchn)
\r
388 of.numchn = numtracks;
\r
389 if (numlines > maxlines)
\r
390 maxlines = numlines;
\r
393 of.numtrk = of.numpat * of.numchn;
\r
394 if (!AllocTracks())
\r
396 if (!AllocPatterns())
\r
401 (MMD1NOTE *)_mm_calloc(of.numchn * (maxlines + 1),
\r
402 sizeof(MMD1NOTE)))) return 0;
\r
404 /* second read: really read and convert patterns */
\r
405 for (t = 0; t < of.numpat; t++) {
\r
406 _mm_fseek(modreader, ba[t], SEEK_SET);
\r
407 numtracks = _mm_read_M_UWORD(modreader);
\r
408 numlines = _mm_read_M_UWORD(modreader);
\r
410 _mm_fseek(modreader, sizeof(ULONG), SEEK_CUR);
\r
411 of.pattrows[t] = ++numlines;
\r
412 memset(mmdp = mmd1pat, 0, of.numchn * maxlines * sizeof(MMD1NOTE));
\r
414 for (row = numlines; row; row--) {
\r
415 for (col = numtracks; col; col--, mmdp++) {
\r
416 mmdp->a = _mm_read_UBYTE(modreader);
\r
417 mmdp->b = _mm_read_UBYTE(modreader);
\r
418 mmdp->c = _mm_read_UBYTE(modreader);
\r
419 mmdp->d = _mm_read_UBYTE(modreader);
\r
423 for (col = 0; col < of.numchn; col++)
\r
424 of.tracks[track++] = MED_Convert1(numlines, col);
\r
429 BOOL MED_Load(BOOL curious)
\r
437 /* try to read module header */
\r
438 mh->id = _mm_read_M_ULONG(modreader);
\r
439 mh->modlen = _mm_read_M_ULONG(modreader);
\r
440 mh->MEDSONGP = _mm_read_M_ULONG(modreader);
\r
441 mh->psecnum = _mm_read_M_UWORD(modreader);
\r
442 mh->pseq = _mm_read_M_UWORD(modreader);
\r
443 mh->MEDBlockPP = _mm_read_M_ULONG(modreader);
\r
444 mh->reserved1 = _mm_read_M_ULONG(modreader);
\r
445 mh->MEDINSTHEADERPP = _mm_read_M_ULONG(modreader);
\r
446 mh->reserved2 = _mm_read_M_ULONG(modreader);
\r
447 mh->MEDEXPP = _mm_read_M_ULONG(modreader);
\r
448 mh->reserved3 = _mm_read_M_ULONG(modreader);
\r
449 mh->pstate = _mm_read_M_UWORD(modreader);
\r
450 mh->pblock = _mm_read_M_UWORD(modreader);
\r
451 mh->pline = _mm_read_M_UWORD(modreader);
\r
452 mh->pseqnum = _mm_read_M_UWORD(modreader);
\r
453 mh->actplayline = _mm_read_M_SWORD(modreader);
\r
454 mh->counter = _mm_read_UBYTE(modreader);
\r
455 mh->extra_songs = _mm_read_UBYTE(modreader);
\r
457 /* Seek to MEDSONG struct */
\r
458 _mm_fseek(modreader, mh->MEDSONGP, SEEK_SET);
\r
460 /* Load the MED Song Header */
\r
461 mss = ms->sample; /* load the sample data first */
\r
462 for (t = 63; t; t--, mss++) {
\r
463 mss->rep = _mm_read_M_UWORD(modreader);
\r
464 mss->replen = _mm_read_M_UWORD(modreader);
\r
465 mss->midich = _mm_read_UBYTE(modreader);
\r
466 mss->midipreset = _mm_read_UBYTE(modreader);
\r
467 mss->svol = _mm_read_UBYTE(modreader);
\r
468 mss->strans = _mm_read_SBYTE(modreader);
\r
471 ms->numblocks = _mm_read_M_UWORD(modreader);
\r
472 ms->songlen = _mm_read_M_UWORD(modreader);
\r
473 _mm_read_UBYTES(ms->playseq, 256, modreader);
\r
474 ms->deftempo = _mm_read_M_UWORD(modreader);
\r
475 ms->playtransp = _mm_read_SBYTE(modreader);
\r
476 ms->flags = _mm_read_UBYTE(modreader);
\r
477 ms->flags2 = _mm_read_UBYTE(modreader);
\r
478 ms->tempo2 = _mm_read_UBYTE(modreader);
\r
479 _mm_read_UBYTES(ms->trkvol, 16, modreader);
\r
480 ms->mastervol = _mm_read_UBYTE(modreader);
\r
481 ms->numsamples = _mm_read_UBYTE(modreader);
\r
483 /* check for a bad header */
\r
484 if (_mm_eof(modreader)) {
\r
485 _mm_errno = MMERR_LOADING_HEADER;
\r
489 /* load extension structure */
\r
491 _mm_fseek(modreader, mh->MEDEXPP, SEEK_SET);
\r
492 me->nextmod = _mm_read_M_ULONG(modreader);
\r
493 me->exp_smp = _mm_read_M_ULONG(modreader);
\r
494 me->s_ext_entries = _mm_read_M_UWORD(modreader);
\r
495 me->s_ext_entrsz = _mm_read_M_UWORD(modreader);
\r
496 me->annotxt = _mm_read_M_ULONG(modreader);
\r
497 me->annolen = _mm_read_M_ULONG(modreader);
\r
498 me->iinfo = _mm_read_M_ULONG(modreader);
\r
499 me->i_ext_entries = _mm_read_M_UWORD(modreader);
\r
500 me->i_ext_entrsz = _mm_read_M_UWORD(modreader);
\r
501 me->jumpmask = _mm_read_M_ULONG(modreader);
\r
502 me->rgbtable = _mm_read_M_ULONG(modreader);
\r
503 me->channelsplit = _mm_read_M_ULONG(modreader);
\r
504 me->n_info = _mm_read_M_ULONG(modreader);
\r
505 me->songname = _mm_read_M_ULONG(modreader);
\r
506 me->songnamelen = _mm_read_M_ULONG(modreader);
\r
507 me->dumps = _mm_read_M_ULONG(modreader);
\r
510 /* seek to and read the samplepointer array */
\r
511 _mm_fseek(modreader, mh->MEDINSTHEADERPP, SEEK_SET);
\r
512 if (!_mm_read_M_ULONGS(sa, ms->numsamples, modreader)) {
\r
513 _mm_errno = MMERR_LOADING_HEADER;
\r
517 /* alloc and read the blockpointer array */
\r
518 if (!(ba = (ULONG *)_mm_calloc(ms->numblocks, sizeof(ULONG))))
\r
520 _mm_fseek(modreader, mh->MEDBlockPP, SEEK_SET);
\r
521 if (!_mm_read_M_ULONGS(ba, ms->numblocks, modreader)) {
\r
522 _mm_errno = MMERR_LOADING_HEADER;
\r
526 /* copy song positions */
\r
527 if (!AllocPositions(ms->songlen))
\r
529 for (t = 0; t < ms->songlen; t++)
\r
530 of.positions[t] = ms->playseq[t];
\r
532 decimalvolumes = (ms->flags & 0x10) ? 0 : 1;
\r
533 bpmtempos = (ms->flags2 & 0x20) ? 1 : 0;
\r
536 int bpmlen = (ms->flags2 & 0x1f) + 1;
\r
537 of.initspeed = ms->tempo2;
\r
538 of.inittempo = ms->deftempo * bpmlen / 4;
\r
541 /* Let's do some math : compute GCD of BPM beat length and speed */
\r
552 while ((a != b) && (a)) {
\r
564 of.inittempo = ms->deftempo * bpmlen / (4 * b);
\r
567 of.initspeed = ms->tempo2;
\r
568 of.inittempo = ms->deftempo ? ((UWORD)ms->deftempo * 125) / 33 : 128;
\r
569 if ((ms->deftempo <= 10) && (ms->deftempo))
\r
570 of.inittempo = (of.inittempo * 33) / 6;
\r
571 of.flags |= UF_HIGHBPM;
\r
573 MED_Version[12] = mh->id;
\r
574 of.modtype = strdup(MED_Version);
\r
575 of.numchn = 0; /* will be counted later */
\r
576 of.numpat = ms->numblocks;
\r
577 of.numpos = ms->songlen;
\r
578 of.numins = ms->numsamples;
\r
579 of.numsmp = of.numins;
\r
581 if ((mh->MEDEXPP) && (me->songname) && (me->songnamelen)) {
\r
584 _mm_fseek(modreader, me->songname, SEEK_SET);
\r
585 name = _mm_malloc(me->songnamelen);
\r
586 _mm_read_UBYTES(name, me->songnamelen, modreader);
\r
587 of.songname = DupStr(name, me->songnamelen, 1);
\r
590 of.songname = DupStr(NULL, 0, 0);
\r
591 if ((mh->MEDEXPP) && (me->annotxt) && (me->annolen)) {
\r
592 _mm_fseek(modreader, me->annotxt, SEEK_SET);
\r
593 ReadComment(me->annolen);
\r
596 if (!AllocSamples())
\r
599 for (t = 0; t < of.numins; t++) {
\r
600 q->flags = SF_SIGNED;
\r
603 _mm_fseek(modreader, sa[t], SEEK_SET);
\r
604 s.length = _mm_read_M_ULONG(modreader);
\r
605 s.type = _mm_read_M_SWORD(modreader);
\r
608 #ifdef MIKMOD_DEBUG
\r
609 fprintf(stderr, "\rNon-sample instruments not supported in MED loader yet\n");
\r
612 _mm_errno = MMERR_MED_SYNTHSAMPLES;
\r
618 if (_mm_eof(modreader)) {
\r
619 _mm_errno = MMERR_LOADING_SAMPLEINFO;
\r
623 q->length = s.length;
\r
624 q->seekpos = _mm_ftell(modreader);
\r
625 q->loopstart = ms->sample[t].rep << 1;
\r
626 q->loopend = q->loopstart + (ms->sample[t].replen << 1);
\r
628 if (ms->sample[t].replen > 1)
\r
629 q->flags |= SF_LOOP;
\r
631 /* don't load sample if length>='MMD0'...
\r
632 such kluges make libmikmod's code unique !!! */
\r
633 if (q->length >= MMD0_string)
\r
638 if ((mh->MEDEXPP) && (me->exp_smp) &&
\r
639 (t < me->s_ext_entries) && (me->s_ext_entrsz >= 4)) {
\r
642 _mm_fseek(modreader, me->exp_smp + t * me->s_ext_entrsz,
\r
644 ie.hold = _mm_read_UBYTE(modreader);
\r
645 ie.decay = _mm_read_UBYTE(modreader);
\r
646 ie.suppress_midi_off = _mm_read_UBYTE(modreader);
\r
647 ie.finetune = _mm_read_SBYTE(modreader);
\r
649 q->speed = finetune[ie.finetune & 0xf];
\r
653 if ((mh->MEDEXPP) && (me->iinfo) &&
\r
654 (t < me->i_ext_entries) && (me->i_ext_entrsz >= 40)) {
\r
657 _mm_fseek(modreader, me->iinfo + t * me->i_ext_entrsz, SEEK_SET);
\r
658 _mm_read_UBYTES(ii.name, 40, modreader);
\r
659 q->samplename = DupStr((char*)ii.name, 40, 1);
\r
661 q->samplename = NULL;
\r
666 if (mh->id == MMD0_string) {
\r
667 if (!LoadMEDPatterns()) {
\r
668 _mm_errno = MMERR_LOADING_PATTERN;
\r
671 } else if (mh->id == MMD1_string) {
\r
672 if (!LoadMMD1Patterns()) {
\r
673 _mm_errno = MMERR_LOADING_PATTERN;
\r
677 _mm_errno = MMERR_NOT_A_MODULE;
\r
683 CHAR *MED_LoadTitle(void)
\r
685 ULONG posit, namelen;
\r
686 CHAR *name, *retvalue = NULL;
\r
688 _mm_fseek(modreader, 0x20, SEEK_SET);
\r
689 posit = _mm_read_M_ULONG(modreader);
\r
692 _mm_fseek(modreader, posit + 0x2C, SEEK_SET);
\r
693 posit = _mm_read_M_ULONG(modreader);
\r
694 namelen = _mm_read_M_ULONG(modreader);
\r
696 _mm_fseek(modreader, posit, SEEK_SET);
\r
697 name = _mm_malloc(namelen);
\r
698 _mm_read_UBYTES(name, namelen, modreader);
\r
699 retvalue = DupStr(name, namelen, 1);
\r
706 /*========== Loader information */
\r
708 MIKMODAPI MLOADER load_med = {
\r