forked from len0rd/rockbox
Add MikMod plugin, ported by Jason Yu, with some minor work by Craig Mann and William Peters (FS#8806)
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@28810 a1c6a512-1295-4272-9138-f99709370657
This commit is contained in:
parent
d192bdf11e
commit
26f2bfde03
47 changed files with 22320 additions and 1 deletions
512
apps/plugins/mikmod/load_mod.c
Normal file
512
apps/plugins/mikmod/load_mod.c
Normal file
|
@ -0,0 +1,512 @@
|
|||
/* MikMod sound library
|
||||
(c) 1998, 1999, 2000, 2001, 2002 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_mod.c,v 1.3 2005/04/07 19:57:38 realtech Exp $
|
||||
|
||||
Generic MOD loader (Protracker, StarTracker, FastTracker, etc)
|
||||
|
||||
==============================================================================*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#ifdef HAVE_MEMORY_H
|
||||
#include <memory.h>
|
||||
#endif
|
||||
#include <string.h>
|
||||
|
||||
#include "mikmod_internals.h"
|
||||
|
||||
#ifdef SUNOS
|
||||
extern int fprintf(FILE *, const char *, ...);
|
||||
#endif
|
||||
|
||||
/*========== Module structure */
|
||||
|
||||
typedef struct MSAMPINFO {
|
||||
CHAR samplename[23]; /* 22 in module, 23 in memory */
|
||||
UWORD length;
|
||||
UBYTE finetune;
|
||||
UBYTE volume;
|
||||
UWORD reppos;
|
||||
UWORD replen;
|
||||
} MSAMPINFO;
|
||||
|
||||
typedef struct MODULEHEADER {
|
||||
CHAR songname[21]; /* the songname.. 20 in module, 21 in memory */
|
||||
MSAMPINFO samples[31]; /* all sampleinfo */
|
||||
UBYTE songlength; /* number of patterns used */
|
||||
UBYTE magic1; /* should be 127 */
|
||||
UBYTE positions[128]; /* which pattern to play at pos */
|
||||
UBYTE magic2[4]; /* string "M.K." or "FLT4" or "FLT8" */
|
||||
} MODULEHEADER;
|
||||
|
||||
typedef struct MODTYPE {
|
||||
CHAR id[5];
|
||||
UBYTE channels;
|
||||
CHAR *name;
|
||||
} MODTYPE;
|
||||
|
||||
typedef struct MODNOTE {
|
||||
UBYTE a, b, c, d;
|
||||
} MODNOTE;
|
||||
|
||||
/*========== Loader variables */
|
||||
|
||||
#define MODULEHEADERSIZE 0x438
|
||||
|
||||
static CHAR protracker[] = "Protracker";
|
||||
static CHAR startrekker[] = "Startrekker";
|
||||
static CHAR fasttracker[] = "Fasttracker";
|
||||
static CHAR oktalyser[] = "Oktalyser";
|
||||
static CHAR oktalyzer[] = "Oktalyzer";
|
||||
static CHAR taketracker[] = "TakeTracker";
|
||||
static CHAR orpheus[] = "Imago Orpheus (MOD format)";
|
||||
|
||||
static MODULEHEADER *mh = NULL;
|
||||
static MODNOTE *patbuf = NULL;
|
||||
static int modtype, trekker;
|
||||
|
||||
/*========== Loader code */
|
||||
|
||||
/* given the module ID, determine the number of channels and the tracker
|
||||
description ; also alters modtype */
|
||||
static int MOD_CheckType(UBYTE *id, UBYTE *numchn, CHAR **descr)
|
||||
{
|
||||
modtype = trekker = 0;
|
||||
|
||||
/* Protracker and variants */
|
||||
if ((!memcmp(id, "M.K.", 4)) || (!memcmp(id, "M!K!", 4))) {
|
||||
*descr = protracker;
|
||||
modtype = 0;
|
||||
*numchn = 4;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Star Tracker */
|
||||
if (((!memcmp(id, "FLT", 3)) || (!memcmp(id, "EXO", 3))) &&
|
||||
(isdigit(id[3]))) {
|
||||
*descr = startrekker;
|
||||
modtype = trekker = 1;
|
||||
*numchn = id[3] - '0';
|
||||
if (*numchn == 4 || *numchn == 8)
|
||||
return 1;
|
||||
#ifdef MIKMOD_DEBUG
|
||||
else
|
||||
fprintf(stderr, "\rUnknown FLT%d module type\n", *numchn);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Oktalyzer (Amiga) */
|
||||
if (!memcmp(id, "OKTA", 4)) {
|
||||
*descr = oktalyzer;
|
||||
modtype = 1;
|
||||
*numchn = 8;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Oktalyser (Atari) */
|
||||
if (!memcmp(id, "CD81", 4)) {
|
||||
*descr = oktalyser;
|
||||
modtype = 1;
|
||||
*numchn = 8;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Fasttracker */
|
||||
if ((!memcmp(id + 1, "CHN", 3)) && (isdigit(id[0]))) {
|
||||
*descr = fasttracker;
|
||||
modtype = 1;
|
||||
*numchn = id[0] - '0';
|
||||
return 1;
|
||||
}
|
||||
/* Fasttracker or Taketracker */
|
||||
if (((!memcmp(id + 2, "CH", 2)) || (!memcmp(id + 2, "CN", 2)))
|
||||
&& (isdigit(id[0])) && (isdigit(id[1]))) {
|
||||
if (id[3] == 'H') {
|
||||
*descr = fasttracker;
|
||||
modtype = 2; /* this can also be Imago Orpheus */
|
||||
} else {
|
||||
*descr = taketracker;
|
||||
modtype = 1;
|
||||
}
|
||||
*numchn = (id[0] - '0') * 10 + (id[1] - '0');
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int MOD_Test(void)
|
||||
{
|
||||
UBYTE id[4], numchn;
|
||||
CHAR *descr;
|
||||
|
||||
_mm_fseek(modreader, MODULEHEADERSIZE, SEEK_SET);
|
||||
if (!_mm_read_UBYTES(id, 4, modreader))
|
||||
return 0;
|
||||
|
||||
if (MOD_CheckType(id, &numchn, &descr))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int MOD_Init(void)
|
||||
{
|
||||
if (!(mh = (MODULEHEADER *)MikMod_malloc(sizeof(MODULEHEADER))))
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void MOD_Cleanup(void)
|
||||
{
|
||||
MikMod_free(mh);
|
||||
MikMod_free(patbuf);
|
||||
}
|
||||
|
||||
/*
|
||||
Old (amiga) noteinfo:
|
||||
|
||||
_____byte 1_____ byte2_ _____byte 3_____ byte4_
|
||||
/ \ / \ / \ / \
|
||||
0000 0000-00000000 0000 0000-00000000
|
||||
|
||||
Upper four 12 bits for Lower four Effect command.
|
||||
bits of sam- note period. bits of sam-
|
||||
ple number. ple number.
|
||||
|
||||
*/
|
||||
|
||||
static UBYTE ConvertNote(MODNOTE *n, UBYTE lasteffect)
|
||||
{
|
||||
UBYTE instrument, effect, effdat, note;
|
||||
UWORD period;
|
||||
UBYTE lastnote = 0;
|
||||
|
||||
/* extract the various information from the 4 bytes that make up a note */
|
||||
instrument = (n->a & 0x10) | (n->c >> 4);
|
||||
period = (((UWORD)n->a & 0xf) << 8) + n->b;
|
||||
effect = n->c & 0xf;
|
||||
effdat = n->d;
|
||||
|
||||
/* Convert the period to a note number */
|
||||
note = 0;
|
||||
if (period) {
|
||||
for (note = 0; note < 7 * OCTAVE; note++)
|
||||
if (period >= npertab[note])
|
||||
break;
|
||||
if (note == 7 * OCTAVE)
|
||||
note = 0;
|
||||
else
|
||||
note++;
|
||||
}
|
||||
|
||||
if (instrument) {
|
||||
/* if instrument does not exist, note cut */
|
||||
if ((instrument > 31) || (!mh->samples[instrument - 1].length)) {
|
||||
UniPTEffect(0xc, 0);
|
||||
if (effect == 0xc)
|
||||
effect = effdat = 0;
|
||||
} else {
|
||||
/* Protracker handling */
|
||||
if (!modtype) {
|
||||
/* if we had a note, then change instrument... */
|
||||
if (note)
|
||||
UniInstrument(instrument - 1);
|
||||
/* ...otherwise, only adjust volume... */
|
||||
else {
|
||||
/* ...unless an effect was specified, which forces a new
|
||||
note to be played */
|
||||
if (effect || effdat) {
|
||||
UniInstrument(instrument - 1);
|
||||
note = lastnote;
|
||||
} else
|
||||
UniPTEffect(0xc,
|
||||
mh->samples[instrument -
|
||||
1].volume & 0x7f);
|
||||
}
|
||||
} else {
|
||||
/* Fasttracker handling */
|
||||
UniInstrument(instrument - 1);
|
||||
if (!note)
|
||||
note = lastnote;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (note) {
|
||||
UniNote(note + 2 * OCTAVE - 1);
|
||||
lastnote = note;
|
||||
}
|
||||
|
||||
/* Convert pattern jump from Dec to Hex */
|
||||
if (effect == 0xd)
|
||||
effdat = (((effdat & 0xf0) >> 4) * 10) + (effdat & 0xf);
|
||||
|
||||
/* Volume slide, up has priority */
|
||||
if ((effect == 0xa) && (effdat & 0xf) && (effdat & 0xf0))
|
||||
effdat &= 0xf0;
|
||||
|
||||
/* Handle ``heavy'' volumes correctly */
|
||||
if ((effect == 0xc) && (effdat > 0x40))
|
||||
effdat = 0x40;
|
||||
|
||||
/* An isolated 100, 200 or 300 effect should be ignored (no
|
||||
"standalone" porta memory in mod files). However, a sequence such
|
||||
as 1XX, 100, 100, 100 is fine. */
|
||||
if ((!effdat) && ((effect == 1)||(effect == 2)||(effect ==3)) &&
|
||||
(lasteffect < 0x10) && (effect != lasteffect))
|
||||
effect = 0;
|
||||
|
||||
UniPTEffect(effect, effdat);
|
||||
if (effect == 8)
|
||||
of.flags |= UF_PANNING;
|
||||
|
||||
return effect;
|
||||
}
|
||||
|
||||
static UBYTE *ConvertTrack(MODNOTE *n, int numchn)
|
||||
{
|
||||
int t;
|
||||
UBYTE lasteffect = 0x10; /* non existant effect */
|
||||
|
||||
UniReset();
|
||||
for (t = 0; t < 64; t++) {
|
||||
lasteffect = ConvertNote(n,lasteffect);
|
||||
UniNewline();
|
||||
n += numchn;
|
||||
}
|
||||
return UniDup();
|
||||
}
|
||||
|
||||
/* Loads all patterns of a modfile and converts them into the 3 byte format. */
|
||||
static int ML_LoadPatterns(void)
|
||||
{
|
||||
int t, s, tracks = 0;
|
||||
|
||||
if (!AllocPatterns())
|
||||
return 0;
|
||||
if (!AllocTracks())
|
||||
return 0;
|
||||
|
||||
/* Allocate temporary buffer for loading and converting the patterns */
|
||||
if (!(patbuf = (MODNOTE *)MikMod_calloc(64U * of.numchn, sizeof(MODNOTE))))
|
||||
return 0;
|
||||
|
||||
if (trekker && of.numchn == 8) {
|
||||
/* Startrekker module dual pattern */
|
||||
for (t = 0; t < of.numpat; t++) {
|
||||
for (s = 0; s < (64U * 4); s++) {
|
||||
patbuf[s].a = _mm_read_UBYTE(modreader);
|
||||
patbuf[s].b = _mm_read_UBYTE(modreader);
|
||||
patbuf[s].c = _mm_read_UBYTE(modreader);
|
||||
patbuf[s].d = _mm_read_UBYTE(modreader);
|
||||
}
|
||||
for (s = 0; s < 4; s++)
|
||||
if (!(of.tracks[tracks++] = ConvertTrack(patbuf + s, 4)))
|
||||
return 0;
|
||||
for (s = 0; s < (64U * 4); s++) {
|
||||
patbuf[s].a = _mm_read_UBYTE(modreader);
|
||||
patbuf[s].b = _mm_read_UBYTE(modreader);
|
||||
patbuf[s].c = _mm_read_UBYTE(modreader);
|
||||
patbuf[s].d = _mm_read_UBYTE(modreader);
|
||||
}
|
||||
for (s = 0; s < 4; s++)
|
||||
if (!(of.tracks[tracks++] = ConvertTrack(patbuf + s, 4)))
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
/* Generic module pattern */
|
||||
for (t = 0; t < of.numpat; t++) {
|
||||
/* Load the pattern into the temp buffer and convert it */
|
||||
for (s = 0; s < (64U * of.numchn); s++) {
|
||||
patbuf[s].a = _mm_read_UBYTE(modreader);
|
||||
patbuf[s].b = _mm_read_UBYTE(modreader);
|
||||
patbuf[s].c = _mm_read_UBYTE(modreader);
|
||||
patbuf[s].d = _mm_read_UBYTE(modreader);
|
||||
}
|
||||
for (s = 0; s < of.numchn; s++)
|
||||
if (!(of.tracks[tracks++] = ConvertTrack(patbuf + s, of.numchn)))
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int MOD_Load(int curious)
|
||||
{
|
||||
int t, scan;
|
||||
SAMPLE *q;
|
||||
MSAMPINFO *s;
|
||||
CHAR *descr;
|
||||
|
||||
/* try to read module header */
|
||||
_mm_read_string((CHAR *)mh->songname, 20, modreader);
|
||||
mh->songname[20] = 0; /* just in case */
|
||||
|
||||
for (t = 0; t < 31; t++) {
|
||||
s = &mh->samples[t];
|
||||
_mm_read_string(s->samplename, 22, modreader);
|
||||
s->samplename[22] = 0; /* just in case */
|
||||
s->length = _mm_read_M_UWORD(modreader);
|
||||
s->finetune = _mm_read_UBYTE(modreader);
|
||||
s->volume = _mm_read_UBYTE(modreader);
|
||||
s->reppos = _mm_read_M_UWORD(modreader);
|
||||
s->replen = _mm_read_M_UWORD(modreader);
|
||||
}
|
||||
|
||||
mh->songlength = _mm_read_UBYTE(modreader);
|
||||
|
||||
/* this fixes mods which declare more than 128 positions.
|
||||
* eg: beatwave.mod */
|
||||
if (mh->songlength > 128) { mh->songlength = 128; }
|
||||
|
||||
mh->magic1 = _mm_read_UBYTE(modreader);
|
||||
_mm_read_UBYTES(mh->positions, 128, modreader);
|
||||
_mm_read_UBYTES(mh->magic2, 4, modreader);
|
||||
|
||||
if (_mm_eof(modreader)) {
|
||||
_mm_errno = MMERR_LOADING_HEADER;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* set module variables */
|
||||
of.initspeed = 6;
|
||||
of.inittempo = 125;
|
||||
if (!(MOD_CheckType(mh->magic2, &of.numchn, &descr))) {
|
||||
_mm_errno = MMERR_NOT_A_MODULE;
|
||||
return 0;
|
||||
}
|
||||
if (trekker && of.numchn == 8)
|
||||
for (t = 0; t < 128; t++)
|
||||
/* if module pretends to be FLT8, yet the order table
|
||||
contains odd numbers, chances are it's a lying FLT4... */
|
||||
if (mh->positions[t] & 1) {
|
||||
of.numchn = 4;
|
||||
break;
|
||||
}
|
||||
if (trekker && of.numchn == 8)
|
||||
for (t = 0; t < 128; t++)
|
||||
mh->positions[t] >>= 1;
|
||||
|
||||
of.songname = DupStr(mh->songname, 21, 1);
|
||||
of.numpos = mh->songlength;
|
||||
of.reppos = 0;
|
||||
|
||||
/* Count the number of patterns */
|
||||
of.numpat = 0;
|
||||
for (t = 0; t < of.numpos; t++)
|
||||
if (mh->positions[t] > of.numpat)
|
||||
of.numpat = mh->positions[t];
|
||||
|
||||
/* since some old modules embed extra patterns, we have to check the
|
||||
whole list to get the samples' file offsets right - however we can find
|
||||
garbage here, so check carefully */
|
||||
scan = 1;
|
||||
for (t = of.numpos; t < 128; t++)
|
||||
if (mh->positions[t] >= 0x80)
|
||||
scan = 0;
|
||||
if (scan)
|
||||
for (t = of.numpos; t < 128; t++) {
|
||||
if (mh->positions[t] > of.numpat)
|
||||
of.numpat = mh->positions[t];
|
||||
if ((curious) && (mh->positions[t]))
|
||||
of.numpos = t + 1;
|
||||
}
|
||||
of.numpat++;
|
||||
of.numtrk = of.numpat * of.numchn;
|
||||
|
||||
if (!AllocPositions(of.numpos))
|
||||
return 0;
|
||||
for (t = 0; t < of.numpos; t++)
|
||||
of.positions[t] = mh->positions[t];
|
||||
|
||||
/* Finally, init the sampleinfo structures */
|
||||
of.numins = of.numsmp = 31;
|
||||
if (!AllocSamples())
|
||||
return 0;
|
||||
s = mh->samples;
|
||||
q = of.samples;
|
||||
for (t = 0; t < of.numins; t++) {
|
||||
/* convert the samplename */
|
||||
q->samplename = DupStr(s->samplename, 23, 1);
|
||||
/* init the sampleinfo variables and convert the size pointers */
|
||||
q->speed = finetune[s->finetune & 0xf];
|
||||
q->volume = s->volume & 0x7f;
|
||||
q->loopstart = (ULONG)s->reppos << 1;
|
||||
q->loopend = q->loopstart + ((ULONG)s->replen << 1);
|
||||
q->length = (ULONG)s->length << 1;
|
||||
q->flags = SF_SIGNED;
|
||||
/* Imago Orpheus creates MODs with 16 bit samples, check */
|
||||
if ((modtype == 2) && (s->volume & 0x80)) {
|
||||
q->flags |= SF_16BITS;
|
||||
descr = orpheus;
|
||||
}
|
||||
if (s->replen > 2)
|
||||
q->flags |= SF_LOOP;
|
||||
|
||||
s++;
|
||||
q++;
|
||||
}
|
||||
|
||||
of.modtype = StrDup(descr);
|
||||
|
||||
if (!ML_LoadPatterns())
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static CHAR *MOD_LoadTitle(void)
|
||||
{
|
||||
CHAR s[21];
|
||||
|
||||
_mm_fseek(modreader, 0, SEEK_SET);
|
||||
if (!_mm_read_UBYTES(s, 20, modreader))
|
||||
return NULL;
|
||||
s[20] = 0; /* just in case */
|
||||
|
||||
return (DupStr(s, 21, 1));
|
||||
}
|
||||
|
||||
/*========== Loader information */
|
||||
|
||||
MIKMODAPI MLOADER load_mod = {
|
||||
NULL,
|
||||
"Standard module",
|
||||
"MOD (31 instruments)",
|
||||
MOD_Init,
|
||||
MOD_Test,
|
||||
MOD_Load,
|
||||
MOD_Cleanup,
|
||||
MOD_LoadTitle
|
||||
};
|
||||
|
||||
/* ex:set ts=4: */
|
Loading…
Add table
Add a link
Reference in a new issue