/* Extended Module Player * Copyright (C) 1996-1999 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. */ /* Note: envelope switching (effect 9) and sample status change (effect 8) * are not supported. Frequency envelopes will be added in xmp 1.2.0. In * MDL envelopes are defined for each instrument in a patch -- xmp accepts * only one envelope for each patch, and the enveope of the first instrument * will be taken. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "load.h" #include "iff.h" #include "mdl.h" #include "period.h" static struct dev_info *dinfo; static int i_map[16]; static int *i_index; static int *s_index; static int *v_index; static int *p_index; static int *c2spd; static int *packinfo; static int v_envnum; static int p_envnum; static struct mdl_envelope *v_env; static struct mdl_envelope *p_env; /* Effects 1-6 (note effects) can only be entered in the first effect * column, G-L (volume-effects) only in the second column. */ static void xlat_fx1 (uint8 *t, uint8 *p) { switch (*t) { case 0x05: /* Arpeggio */ *t = FX_ARPEGGIO; break; case 0x06: /* 6 - Not used */ case 0x0a: /* 6 - Not used */ case 0x09: /* Set envelope -- not supported */ *t = *p = 0x00; break; case 0x07: /* Set BPM */ *t = FX_TEMPO; if (*p < 0x20) *p = 0x20; break; case 0x0e: /* Extended */ switch (MSN (*p)) { case 0x0: /* E0 - not used */ case 0x3: /* E3 - not used */ case 0x8: /* Set sample status -- unsupported */ *t = *p = 0x00; break; case 0x1: /* Pan slide left */ *t = FX_PANSLIDE; *p <<= 4; break; case 0x2: /* Pan slide right */ *t = FX_PANSLIDE; *p &= 0x0f; break; } break; case 0x0f: if (*p >= 0x20) *t = FX_S3M_TEMPO; break; } } static void xlat_fx2 (uint8 *t, uint8 *p) { switch (*t) { case 0x06: /* L - Not used */ case 0x09: /* Set envelope -- not supported */ *t = *p = 0x00; break; case 0x07: /* Set BPM */ *t = FX_TEMPO; if (*p < 0x20) *p = 0x20; break; *t = *p = 0x00; case 0x03: /* Multi-retrig */ *t = FX_MULTI_RETRIG; break; case 0x04: /* Tremolo */ *t = FX_TREMOLO; break; case 0x05: /* Tremor */ *t = FX_TREMOR; break; case 0x0e: /* Extended */ switch (MSN (*p)) { case 0x0: /* E0 - not used */ case 0x3: /* E3 - not used */ case 0x8: /* Set sample status -- unsupported */ *t = *p = 0x00; break; case 0x1: /* Pan slide left */ *t = FX_PANSLIDE; *p <<= 4; break; case 0x2: /* Pan slide right */ *t = FX_PANSLIDE; *p &= 0x0f; break; } break; } } static unsigned int get_bits (char i, uint8 **buf) { static uint32 b = 0, n = 32; unsigned int x; if (i == 0) { b = *((uint32 *)(*buf))++; n = 32; return 0; } x = b & ((1 << i) - 1); b >>= i; if ((n -= i) <= 24) { b |= (uint32)(*(*buf)++) << n; n += 8; } return x; } /* From the Digitrakker docs: * * The method is based on the Huffman algorithm. It's easy and very fast * and effective on samples. The packed sample is a bit stream: * * Byte 0 Byte 1 Byte 2 Byte 3 * Bit 76543210 fedcba98 nmlkjihg ....rqpo * * A packed byte is stored in the following form: * * xxxx10..0s => byte = + (number of <0> bits between * s and 1) * 16 - 8; * if s==1 then byte = byte xor 255 * * If there are no <0> bits between the first bit (sign) and the <1> bit, * you have the following form: * * xxx1s => byte = ; if s=1 then byte = byte xor 255 */ static void unpack_sample8 (uint8 *t, uint8 *f, int l) { int i, s; uint8 b, d; get_bits (0, &f); for (i = b = d = 0; i < l; i++) { s = get_bits (1, &f); if (get_bits (1, &f)) { b = get_bits (3, &f); } else { b = 8; while (!get_bits (1, &f)) b += 16; b += get_bits (4, &f); } if (s) b ^= 0xff; d += b; *t++ = d; } } static void unpack_sample16 (uint16 *t, uint8 *f, int l) { int i, lo, s; uint8 b, d; for (i = lo = b = d = 0; i < l; i++) { lo = get_bits (8, &f); s = get_bits (1, &f); if (get_bits (1, &f)) { b = get_bits (3, &f); } else { b = 8; while (!get_bits (1, &f)) b += 16; b += get_bits (4, &f); } if (s) b ^= 0xff; d += b; *t++ = ((uint32)d << 8) | lo; } } /* * IFF chunk handlers */ static void get_chunk_in (int size, struct mdl_in_chunk *buffer) { int i; strncpy (xmp_ctl->name, buffer->name, 32); strncpy (author_name, buffer->author, 20); L_ENDIAN16 (buffer->length); L_ENDIAN16 (buffer->restart); xxh->len = buffer->length; xxh->rst = buffer->restart; xxh->tpo = buffer->tempo; xxh->bpm = buffer->bpm; for (i = 0; i < 32; i++) { if (buffer->chinfo[i] & 0x80) break; xxc[i].pan = buffer->chinfo[i] << 1; } xxh->chn = i; memcpy (xxo, buffer->orders, xxh->len); MODULE_INFO (); } static void get_chunk_pa (int size, uint8 *buffer) { int i, j, chn; uint16 x16; xxh->pat = *buffer++; xxh->trk = xxh->pat * xxh->chn; /* Max */ PATTERN_INIT (); if (V (0)) report ("Stored patterns: %d ", xxh->pat); for (i = 0; i < xxh->pat; i++) { PATTERN_ALLOC (i); chn = *buffer++; xxp[i]->rows = (int) (*buffer++) + 1; buffer += 16; /* Skip pattern name */ for (j = 0; j < chn; j++) { x16 = *((uint16 *)buffer)++; L_ENDIAN16 (x16); xxp[i]->info[j].index = x16; } if (V (0)) report ("."); } if (V (0)) report ("\n"); } static void get_chunk_p0 (int size, uint8 *buffer) { int i, j; uint16 x16; xxh->pat = *buffer++; xxh->trk = xxh->pat * xxh->chn; /* Max */ PATTERN_INIT (); if (V (0)) report ("Stored patterns: %d ", xxh->pat); for (i = 0; i < xxh->pat; i++) { PATTERN_ALLOC (i); xxp[i]->rows = 64; for (j = 0; j < 32; j++) { x16 = *((uint16 *)buffer)++; L_ENDIAN16 (x16); if (j < xxh->chn) xxp[i]->info[j].index = x16; } if (V (0)) report ("."); } if (V (0)) report ("\n"); } static void get_chunk_tr (int size, uint8 *buffer) { int i, j, k, row, len; struct xxm_track *track; xxh->trk = *((uint16 *) buffer)++ + 1; if (V (0)) report ("Stored tracks : %d ", xxh->trk); track = calloc (1, sizeof (struct xxm_track) + sizeof (struct xxm_event) * 256); /* Empty track 0 is not stored in the file */ xxt[0] = calloc (1, sizeof (struct xxm_track) + 256 * sizeof (struct xxm_event)); xxt[0]->rows = 256; for (i = 1; i < xxh->trk; i++) { /* Length of the track in bytes */ len = *((uint16 *) buffer)++; memset (track, 0, sizeof (struct xxm_track) + sizeof (struct xxm_event) * 256); for (row = 0; len;) { j = *buffer++; len--; switch (j & 0x03) { case 0: row += j >> 2; break; case 1: for (k = 0; k <= (j >> 2); k++) memcpy (&track->event[row + k], &track->event[row - 1], sizeof (struct xxm_event)); row += k - 1; break; case 2: memcpy (&track->event[row], &track->event[j >> 2], sizeof (struct xxm_event)); break; case 3: if (j & MDL_NOTE_FOLLOWS) len--, track->event[row].note = *buffer++; if (j & MDL_INSTRUMENT_FOLLOWS) len--, track->event[row].ins = *buffer++; if (j & MDL_VOLUME_FOLLOWS) len--, track->event[row].vol = *buffer++; if (j & MDL_EFFECT_FOLLOWS) { len--, k = *buffer++; track->event[row].fxt = LSN (k); track->event[row].f2t = MSN (k); } if (j & MDL_PARAMETER1_FOLLOWS) len--, track->event[row].fxp = *buffer++; if (j & MDL_PARAMETER2_FOLLOWS) len--, track->event[row].f2p = *buffer++; break; } xlat_fx1 (&track->event[row].fxt, &track->event[row].fxp); xlat_fx2 (&track->event[row].f2t, &track->event[row].f2p); row++; } if (row <= 64) row = 64; else if (row <= 128) row = 128; else row = 256; xxt[i] = calloc (1, sizeof (struct xxm_track) + sizeof (struct xxm_event) * row); memcpy (xxt[i], track, sizeof (struct xxm_track) + sizeof (struct xxm_event) * row); xxt[i]->rows = row; if (V (0) && !(i % xxh->chn)) report ("."); } free (track); if (V (0)) report ("\n"); } static void get_chunk_ii (int size, uint8 *buffer) { int i, j; char *instr; xxh->ins = *buffer++; if (V (0)) report ("Instruments : %d ", xxh->ins); INSTRUMENT_INIT (); for (i = 0; i < xxh->ins; i++) { i_index[i] = *buffer++; xxih[i].nsm = *buffer++; instr = buffer; *(instr + 31) = 0; str_adj (instr); strncpy (xxih[i].name, buffer, 24); buffer += 32; if (V (1) && (strlen ((char *) xxih[i].name) || xxih[i].nsm)) report ("\n[%2X] %-32.32s %2d ", i_index[i], instr, xxih[i].nsm); xxi[i] = calloc (sizeof (struct xxm_instrument), xxih[i].nsm); for (j = 0; j < xxih[i].nsm; j++) { xxi[i][j].sid = *buffer++; i_map[j] = *buffer++; xxi[i][j].vol = *buffer++; if (j == 0) v_index[i] = *buffer & 0x80 ? *buffer & 0x3f : -1; if (~*buffer & 0x40) xxi[i][j].vol = 0xff; buffer++; xxi[i][j].pan = *buffer++ << 1; if (j == 0) p_index[i] = *buffer & 0x80 ? *buffer & 0x3f : -1; if (~*buffer & 0x40) xxi[i][j].pan = 0x80; buffer++; if (j == 0) { xxih[i].rls = *(uint16 *)buffer; L_ENDIAN32 (xxih[i].rls); } ((uint16 *)buffer)++; xxi[i][j].vra = *buffer++; xxi[i][j].vde = *buffer++; xxi[i][j].vsw = *buffer++; xxi[i][j].vwf = *buffer++; buffer++; /* Reserved */ buffer++; /* Pitch envelope */ if (V (1)) { report ("%s[%1x] V%02x S%02x ", j ? "\n\t\t\t\t" : "\t", j, xxi[i][j].vol, xxi[i][j].sid); if (v_index[i] > 0) report ("v%02x ", v_index[i]); else report ("v-- "); if (p_index[i] > 0) report ("p%02x ", p_index[i]); else report ("p-- "); } else if (V (0) == 1) report ("."); } } if (V (0)) report ("\n"); } static void get_chunk_is (int size, uint8 *buffer) { int i; xxh->smp = *buffer++; xxs = calloc (sizeof (struct xxm_sample), xxh->smp); packinfo = calloc (sizeof (int), xxh->smp); if (V (1)) report ("Sample infos : %d ", xxh->smp); for (i = 0; i < xxh->smp; i++) { s_index[i] = *buffer++; /* Sample number */ *(buffer + 31) = 0; str_adj (buffer); if (V (2)) report ("\n[%2X] %-32.32s ", s_index[i], (char *)buffer); buffer += 32; /* Sample name */ buffer += 8; /* Sample filename */ c2spd[i] = *((uint32 *)buffer)++; xxs[i].len = *((uint32 *)buffer)++; xxs[i].lps = *((uint32 *)buffer)++; xxs[i].lpe = *((uint32 *)buffer)++; L_ENDIAN32 (xxs[i].len); L_ENDIAN32 (xxs[i].lps); L_ENDIAN32 (xxs[i].lpe); xxs[i].flg = xxs[i].lpe > 0 ? WAVE_LOOPING : 0; xxs[i].lpe = xxs[i].lps + xxs[i].lpe; buffer++; /* Volume in DMDL 0.0 */ xxs[i].flg |= (*buffer & 0x01) ? WAVE_16_BITS : 0; xxs[i].flg |= (*buffer & 0x02) ? WAVE_BIDIR_LOOP : 0; packinfo[i] = (*buffer & 0x0c) >> 2; buffer++; #if 0 if (xxs[i].flg & WAVE_16_BITS) { xxs[i].len <<= 1; xxs[i].lps <<= 1; xxs[i].lpe <<= 1; } #endif if (V (2)) { report ("%5d %05x%c %05x %05x ", c2spd[i], xxs[i].len, xxs[i].flg & WAVE_16_BITS ? '+' : ' ', xxs[i].lps, xxs[i].lpe); switch (packinfo[i]) { case 0: report ("[nopack] "); break; case 1: report ("[pack8] "); break; case 2: report ("[pack16] "); break; case 3: report ("[error] "); break; } } if (V (1)) report ("."); } if (V (1)) report ("\n"); } static void get_chunk_i0 (int size, uint8 *buffer) { int i; xxh->ins = xxh->smp = *buffer++; if (V (0)) report ("Instruments : %d ", xxh->ins); INSTRUMENT_INIT (); xxs = calloc (sizeof (struct xxm_sample), xxh->smp); packinfo = calloc (sizeof (int), xxh->smp); for (i = 0; i < xxh->ins; i++) { xxih[i].nsm = 1; xxi[i] = calloc (sizeof (struct xxm_instrument), 1); xxi[i][0].sid = i_index[i] = s_index[i] = *buffer++; *(buffer + 31) = 0; str_adj (buffer); if (V (1)) report ("\n[%2X] %-32.32s ", i_index[i], (char *)buffer); buffer += 32; /* Sample name */ buffer += 8; /* Sample filename */ c2spd[i] = *((uint16 *)buffer)++; L_ENDIAN16 (c2spd[i]); xxs[i].len = *((uint32 *)buffer)++; xxs[i].lps = *((uint32 *)buffer)++; xxs[i].lpe = *((uint32 *)buffer)++; L_ENDIAN32 (xxs[i].len); L_ENDIAN32 (xxs[i].lps); L_ENDIAN32 (xxs[i].lpe); xxs[i].flg = xxs[i].lpe > 0 ? WAVE_LOOPING : 0; xxs[i].lpe = xxs[i].lps + xxs[i].lpe; xxi[i][0].vol = *buffer++; /* Volume */ xxi[i][0].pan = 0x80; xxs[i].flg |= (*buffer & 0x01) ? WAVE_16_BITS : 0; xxs[i].flg |= (*buffer & 0x02) ? WAVE_BIDIR_LOOP : 0; packinfo[i] = (*buffer & 0x0c) >> 2; buffer++; if (V (1)) { report ("%5d V%02x %05x%c %05x %05x ", c2spd[i], xxi[i][0].vol, xxs[i].len, xxs[i].flg & WAVE_16_BITS ? '+' : ' ', xxs[i].lps, xxs[i].lpe); switch (packinfo[i]) { case 0: report ("[nopack] "); break; case 1: report ("[pack8] "); break; case 2: report ("[pack16] "); break; case 3: report ("[error] "); break; } } if (V (0)) report ("."); } if (V (0)) report ("\n"); } static void get_chunk_sa (int size, uint8 *buffer) { int i, len; char *smpbuf; if (V (0)) report ("Stored samples : %d ", xxh->smp); for (i = 0; i < xxh->smp; i++) { smpbuf = calloc (1, xxs[i].flg & WAVE_16_BITS ? xxs[i].len << 1 : xxs[i].len); switch (packinfo[i]) { case 0: memcpy (smpbuf, buffer, xxs[i].len); buffer += xxs[i].len; break; case 1: len = *((uint32 *)buffer)++; unpack_sample8 (smpbuf, buffer, xxs[i].len); buffer += len; break; case 2: len = *((uint32 *)buffer)++; unpack_sample16 ((uint16 *)smpbuf, buffer, xxs[i].len >> 1); buffer += len; break; } xmp_drv_loadpatch (NULL, i, xmp_ctl->c4rate, XMP_SMP_NOLOAD, &xxs[i], smpbuf); free (smpbuf); if (V (0)) report ("."); } if (V (0)) report ("\n"); } static void get_chunk_ve (int size, uint8 *buffer) { if ((v_envnum = *buffer++) == 0) return; if (V (1)) report ("Vol envelopes : %d\n", v_envnum); v_env = calloc (v_envnum, sizeof (struct mdl_envelope)); memcpy (v_env, buffer, v_envnum * sizeof (struct mdl_envelope)); } static void get_chunk_pe (int size, uint8 *buffer) { if ((p_envnum = *buffer++) == 0) return; if (V (1)) report ("Pan envelopes : %d\n", p_envnum); p_env = calloc (p_envnum, sizeof (struct mdl_envelope)); memcpy (p_env, buffer, p_envnum * sizeof (struct mdl_envelope)); } int mdl_load (FILE *f, struct dev_info *d) { int i, j, k, l; char buf[8]; LOAD_INIT (); /* Check magic and get version */ fread (buf, 1, 4, f); if (strncmp (buf, "DMDL", 4)) return -1; fread (buf, 1, 1, f); /* IFFoid chunk IDs */ iff_register ("IN", get_chunk_in); /* Module info */ iff_register ("TR", get_chunk_tr); /* Tracks */ iff_register ("SA", get_chunk_sa); /* Sampled data */ iff_register ("VE", get_chunk_ve); /* Volume envelopes */ iff_register ("PE", get_chunk_pe); /* Pan envelopes */ if (MSN (*buf)) { iff_register ("II", get_chunk_ii); /* Instruments */ iff_register ("PA", get_chunk_pa); /* Patterns */ iff_register ("IS", get_chunk_is); /* Sample info */ } else { iff_register ("PA", get_chunk_p0); /* Old 0.0 patterns */ iff_register ("IS", get_chunk_i0); /* Old 0.0 Sample info */ } /* MDL uses a degenerated IFF-style file format with 16 bit IDs and * little endian 32 bit chunk size. There's only one chunk per data * type i.e. one huge chunk for all the sampled instruments. */ iff_idsize (2); iff_setflag (IFF_LITTLE_ENDIAN); sprintf (xmp_ctl->type, "DMDL %d.%d", MSN (*buf), LSN (*buf)); dinfo = d; xmp_ctl->volbase = 0xff; xmp_ctl->c4rate = C4_NTSC_RATE; v_envnum = 0; s_index = calloc (256, sizeof (int)); i_index = calloc (256, sizeof (int)); v_index = calloc (256, sizeof (int)); p_index = calloc (256, sizeof (int)); c2spd = calloc (256, sizeof (int)); /* Load IFFoid chunks */ while (!feof (f)) iff_chunk (f); iff_release (); /* Re-index instruments & samples */ for (i = 0; i < xxh->pat; i++) for (j = 0; j < xxp[i]->rows; j++) for (k = 0; k < xxh->chn; k++) for (l = 0; l < xxh->ins; l++) if (EVENT(i, k, j).ins && EVENT(i, k, j).ins == i_index[l]) { EVENT(i, k, j).ins = l + 1; break; } for (i = 0; i < xxh->ins; i++) { /* Envelopes */ if (v_index[i] > 0) { xxih[i].aei.flg = XXM_ENV_ON; xxih[i].aei.npt = 16; xxae[i] = calloc (4, xxih[i].aei.npt); for (j = 0; j < v_envnum; j++) { if (v_index[i] == j) { xxih[i].aei.flg |= v_env[j].sus & 0x10 ? XXM_ENV_SUS : 0; xxih[i].aei.flg |= v_env[j].sus & 0x20 ? XXM_ENV_LOOP : 0; xxih[i].aei.sus = v_env[j].sus & 0x0f; xxih[i].aei.lps = v_env[j].loop & 0x0f; xxih[i].aei.lpe = v_env[j].loop & 0xf0; xxae[i][0] = 0; for (k = 1; k < xxih[i].aei.npt; k++) { if ((xxae[i][k * 2] = v_env[j].data[(k - 1) * 2]) == 0) break; xxae[i][k * 2] += xxae[i][(k - 1) * 2]; xxae[i][k * 2 + 1] = v_env[j].data[k * 2]; } xxih[i].aei.npt = k; break; } } } if (p_index[i] > 0) { xxih[i].pei.flg = XXM_ENV_ON; xxih[i].pei.npt = 16; xxae[i] = calloc (4, xxih[i].pei.npt); for (j = 0; j < p_envnum; j++) { if (p_index[i] == j) { xxih[i].pei.flg |= p_env[j].sus & 0x10 ? XXM_ENV_SUS : 0; xxih[i].pei.flg |= p_env[j].sus & 0x20 ? XXM_ENV_LOOP : 0; xxih[i].pei.sus = p_env[j].sus & 0x0f; xxih[i].pei.lps = p_env[j].loop & 0x0f; xxih[i].pei.lpe = p_env[j].loop & 0xf0; xxae[i][0] = 0; for (k = 1; k < xxih[i].pei.npt; k++) { if ((xxae[i][k * 2] = p_env[j].data[(k - 1) * 2]) == 0) break; xxae[i][k * 2] += xxae[i][(k - 1) * 2]; xxae[i][k * 2 + 1] = p_env[j].data[k * 2]; } xxih[i].pei.npt = k; break; } } } for (j = 0; j < xxih[i].nsm; j++) for (k = 0; k < xxh->smp; k++) if (xxi[i][j].sid == s_index[k]) { xxi[i][j].sid = k; c2spd_to_note (c2spd[k], &xxi[i][j].xpo, &xxi[i][j].fin); break; } } free (c2spd); free (p_index); free (v_index); free (i_index); free (s_index); if (v_envnum) free (v_env); if (p_envnum) free (p_env); xmp_ctl->fetch |= XMP_CTL_FINEFX; return 0; }