--- /dev/null
+/* MikMod sound library\r
+ (c) 1998, 1999, 2000, 2001, 2002 Miodrag Vallat and others - see file\r
+ AUTHORS for complete list.\r
+\r
+ This library is free software; you can redistribute it and/or modify\r
+ it under the terms of the GNU Library General Public License as\r
+ published by the Free Software Foundation; either version 2 of\r
+ the License, or (at your option) any later version.\r
+\r
+ This program is distributed in the hope that it will be useful,\r
+ but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+ GNU Library General Public License for more details.\r
+\r
+ You should have received a copy of the GNU Library General Public\r
+ License along with this library; if not, write to the Free Software\r
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\r
+ 02111-1307, USA.\r
+*/\r
+\r
+/*==============================================================================\r
+\r
+ $Id: load_okt.c,v 1.1.1.1 2004/01/21 01:36:35 raph Exp $\r
+\r
+ Oktalyzer (OKT) module loader\r
+\r
+==============================================================================*/\r
+\r
+/*\r
+ Written by UFO <ufo303@poczta.onet.pl>\r
+ based on the file description compiled by Harald Zappe\r
+ <zappe@gaea.sietec.de>\r
+\r
+*/\r
+\r
+#ifdef HAVE_CONFIG_H\r
+#include "config.h"\r
+#endif\r
+\r
+#ifdef HAVE_UNISTD_H\r
+#include <unistd.h>\r
+#endif\r
+\r
+#include <stdio.h>\r
+#ifdef HAVE_MEMORY_H\r
+#include <memory.h>\r
+#endif\r
+#include <string.h>\r
+\r
+#include "mikmod_internals.h"\r
+\r
+#ifdef SUNOS\r
+extern int fprintf(FILE *, const char *, ...);\r
+#endif\r
+\r
+/*========== Module blocks */\r
+\r
+/* sample information */\r
+typedef struct OKTSAMPLE {\r
+ CHAR sampname[20];\r
+ ULONG len;\r
+ UWORD loopbeg;\r
+ UWORD looplen;\r
+ UBYTE volume;\r
+} OKTSAMPLE;\r
+\r
+typedef struct OKTNOTE {\r
+ UBYTE note, ins, eff, dat;\r
+} OKTNOTE;\r
+\r
+/*========== Loader variables */\r
+\r
+static OKTNOTE *okttrk = NULL;\r
+\r
+/*========== Loader code */\r
+\r
+BOOL OKT_Test(void)\r
+{\r
+ CHAR id[8];\r
+\r
+ if (!_mm_read_UBYTES(id, 8, modreader))\r
+ return 0;\r
+ if (!memcmp(id, "OKTASONG", 8))\r
+ return 1;\r
+\r
+ return 0;\r
+}\r
+\r
+/* Pattern analysis routine.\r
+ Effects not implemented (yet) : (in decimal)\r
+ 11 Arpeggio 4: Change note every 50Hz tick between N,H,N,L\r
+ 12 Arpeggio 5: Change note every 50Hz tick between H,H,N\r
+ N = normal note being played in this channel (1-36)\r
+ L = normal note number minus upper four bits of 'data'.\r
+ H = normal note number plus lower four bits of 'data'.\r
+ 13 Decrease note number by 'data' once per tick.\r
+ 17 Increase note number by 'data' once per tick.\r
+ 21 Decrease note number by 'data' once per line.\r
+ 30 Increase note number by 'data' once per line.\r
+*/\r
+static UBYTE *OKT_ConvertTrack(UBYTE patrows)\r
+{\r
+ int t;\r
+ UBYTE ins, note, eff, dat;\r
+\r
+ UniReset();\r
+ for (t = 0; t < patrows; t++) {\r
+ note = okttrk[t].note;\r
+ ins = okttrk[t].ins;\r
+ eff = okttrk[t].eff;\r
+ dat = okttrk[t].dat;\r
+\r
+ if (note) {\r
+ UniNote(note + 3 * OCTAVE - 1);\r
+ UniInstrument(ins);\r
+ }\r
+\r
+ if (eff)\r
+ switch (eff) {\r
+ case 1: /* Porta Up */\r
+ UniPTEffect(0x1, dat);\r
+ break;\r
+ case 2: /* Portamento Down */\r
+ UniPTEffect(0x2, dat);\r
+ break;\r
+ /* case 9: what is this? */\r
+ case 10: /* Arpeggio 3 */\r
+ case 11: /* Arpeggio 4 */\r
+ case 12: /* Arpeggio 5 */\r
+ UniWriteByte(UNI_OKTARP);\r
+ UniWriteByte(eff + 3 - 10);\r
+ UniWriteByte(dat);\r
+ break;\r
+ case 15: /* Amiga filter toggle, ignored */\r
+ break;\r
+ case 25: /* Pattern Jump */\r
+ dat = (dat >> 4) * 10 + (dat & 0x0f);\r
+ UniPTEffect(0xb, dat);\r
+ break;\r
+ case 27: /* Release - similar to Keyoff */\r
+ UniWriteByte(UNI_KEYOFF);\r
+ break;\r
+ case 28: /* Set Tempo */\r
+ UniPTEffect(0xf, dat & 0x0f);\r
+ break;\r
+ case 31: /* volume Control */\r
+ if (dat <= 0x40)\r
+ UniPTEffect(0xc, dat);\r
+ else if (dat <= 0x50)\r
+ UniEffect(UNI_XMEFFECTA, (dat - 0x40)); /* fast fade out */\r
+ else if (dat <= 0x60)\r
+ UniEffect(UNI_XMEFFECTA, (dat - 0x50) << 4); /* fast fade in */\r
+ else if (dat <= 0x70)\r
+ UniEffect(UNI_XMEFFECTEB, (dat - 0x60)); /* slow fade out */\r
+ else if (dat <= 0x80)\r
+ UniEffect(UNI_XMEFFECTEA, (dat - 0x70)); /* slow fade in */\r
+ break;\r
+#ifdef MIKMOD_DEBUG\r
+ default:\r
+ fprintf(stderr, "\rUnimplemented effect (%02d,%02x)\n",\r
+ eff, dat);\r
+#endif\r
+ }\r
+\r
+ UniNewline();\r
+ }\r
+ return UniDup();\r
+}\r
+\r
+/* Read "channel modes" i.e. channel number and panning information */\r
+static void OKT_doCMOD(void)\r
+{\r
+ /* amiga channel panning table */\r
+ UBYTE amigapan[4] = { 0x00, 0xff, 0xff, 0x00 };\r
+ int t;\r
+\r
+ of.numchn = 0;\r
+ of.flags |= UF_PANNING;\r
+\r
+ for (t = 0; t < 4; t++)\r
+ if (_mm_read_M_UWORD(modreader)) {\r
+ /* two channels tied to the same Amiga hardware voice */\r
+ of.panning[of.numchn++] = amigapan[t];\r
+ of.panning[of.numchn++] = amigapan[t];\r
+ } else\r
+ /* one channel tied to the Amiga hardware voice */\r
+ of.panning[of.numchn++] = amigapan[t];\r
+}\r
+\r
+/* Read sample information */\r
+static BOOL OKT_doSAMP(int len)\r
+{\r
+ int t;\r
+ SAMPLE *q;\r
+ OKTSAMPLE s;\r
+\r
+ of.numins = of.numsmp = (len / 0x20);\r
+ if (!AllocSamples())\r
+ return 0;\r
+\r
+ for (t = 0, q = of.samples; t < of.numins; t++, q++) {\r
+ _mm_read_UBYTES(s.sampname, 20, modreader);\r
+ s.len = _mm_read_M_ULONG(modreader);\r
+ s.loopbeg = _mm_read_M_UWORD(modreader) * 2;\r
+ s.looplen = _mm_read_M_UWORD(modreader) * 2;\r
+ _mm_read_UBYTE(modreader);\r
+ s.volume = _mm_read_UBYTE(modreader);\r
+ _mm_read_M_UWORD(modreader);\r
+\r
+ if (_mm_eof(modreader)) {\r
+ _mm_errno = MMERR_LOADING_SAMPLEINFO;\r
+ return 0;\r
+ }\r
+\r
+ if (!s.len)\r
+ q->seekpos = q->length = q->loopstart = q->loopend = q->flags = 0;\r
+ else {\r
+ s.len--;\r
+ /* sanity checks */\r
+ if (s.loopbeg > s.len)\r
+ s.loopbeg = s.len;\r
+ if (s.loopbeg + s.looplen > s.len)\r
+ s.looplen = s.len - s.loopbeg;\r
+ if (s.looplen < 2)\r
+ s.looplen = 0;\r
+\r
+ q->length = s.len;\r
+ q->loopstart = s.loopbeg;\r
+ q->loopend = s.looplen + q->loopstart;\r
+ q->volume = s.volume;\r
+ q->flags = SF_SIGNED;\r
+\r
+ if (s.looplen)\r
+ q->flags |= SF_LOOP;\r
+ }\r
+ q->samplename = DupStr(s.sampname, 20, 1);\r
+ q->speed = 8287;\r
+ }\r
+ return 1;\r
+}\r
+\r
+/* Read speed information */\r
+static void OKT_doSPEE(void)\r
+{\r
+ int tempo = _mm_read_M_UWORD(modreader);\r
+\r
+ of.initspeed = tempo;\r
+}\r
+\r
+/* Read song length information */\r
+static void OKT_doSLEN(void)\r
+{\r
+ of.numpat = _mm_read_M_UWORD(modreader);\r
+}\r
+\r
+/* Read pattern length information */\r
+static void OKT_doPLEN(void)\r
+{\r
+ of.numpos = _mm_read_M_UWORD(modreader);\r
+}\r
+\r
+/* Read order table */\r
+static BOOL OKT_doPATT(void)\r
+{\r
+ int t;\r
+\r
+ if (!of.numpos || !AllocPositions(of.numpos))\r
+ return 0;\r
+\r
+ for (t = 0; t < 128; t++)\r
+ if (t < of.numpos)\r
+ of.positions[t] = _mm_read_UBYTE(modreader);\r
+ else\r
+ break;\r
+\r
+ return 1;\r
+}\r
+\r
+static BOOL OKT_doPBOD(int patnum)\r
+{\r
+ char *patbuf;\r
+ int rows, i;\r
+ int u;\r
+\r
+ if (!patnum) {\r
+ of.numtrk = of.numpat * of.numchn;\r
+\r
+ if (!AllocTracks() || !AllocPatterns())\r
+ return 0;\r
+ }\r
+\r
+ /* Read pattern */\r
+ of.pattrows[patnum] = rows = _mm_read_M_UWORD(modreader);\r
+\r
+ if (!(okttrk = (OKTNOTE *) _mm_calloc(rows, sizeof(OKTNOTE))) ||\r
+ !(patbuf = (char *)_mm_calloc(rows * of.numchn, sizeof(OKTNOTE))))\r
+ return 0;\r
+ _mm_read_UBYTES(patbuf, rows * of.numchn * sizeof(OKTNOTE), modreader);\r
+ if (_mm_eof(modreader)) {\r
+ _mm_errno = MMERR_LOADING_PATTERN;\r
+ return 0;\r
+ }\r
+\r
+ for (i = 0; i < of.numchn; i++) {\r
+ for (u = 0; u < rows; u++) {\r
+ okttrk[u].note = patbuf[(u * of.numchn + i) * sizeof(OKTNOTE)];\r
+ okttrk[u].ins = patbuf[(u * of.numchn + i) * sizeof(OKTNOTE) + 1];\r
+ okttrk[u].eff = patbuf[(u * of.numchn + i) * sizeof(OKTNOTE) + 2];\r
+ okttrk[u].dat = patbuf[(u * of.numchn + i) * sizeof(OKTNOTE) + 3];\r
+ }\r
+\r
+ if (!(of.tracks[patnum * of.numchn + i] = OKT_ConvertTrack(rows)))\r
+ return 0;\r
+ }\r
+ _mm_free(patbuf);\r
+ _mm_free(okttrk);\r
+ return 1;\r
+}\r
+\r
+static void OKT_doSBOD(int insnum)\r
+{\r
+ of.samples[insnum].seekpos = _mm_ftell(modreader);\r
+}\r
+\r
+BOOL OKT_Load(BOOL curious)\r
+{\r
+ UBYTE id[4];\r
+ ULONG len;\r
+ ULONG fp;\r
+ BOOL seen_cmod = 0, seen_samp = 0, seen_slen = 0, seen_plen = 0, seen_patt\r
+ = 0, seen_spee = 0;\r
+ int patnum = 0, insnum = 0;\r
+ (void)curious;\r
+\r
+ /* skip OKTALYZER header */\r
+ _mm_fseek(modreader, 8, SEEK_SET);\r
+ of.songname = strdup("");\r
+\r
+ of.modtype = strdup("Amiga Oktalyzer");\r
+ of.numpos = of.reppos = 0;\r
+ \r
+ /* default values */\r
+ of.initspeed = 6;\r
+ of.inittempo = 125;\r
+\r
+ while (1) {\r
+ /* read block header */\r
+ _mm_read_UBYTES(id, 4, modreader);\r
+ len = _mm_read_M_ULONG(modreader);\r
+ \r
+ if (_mm_eof(modreader))\r
+ break;\r
+ fp = _mm_ftell(modreader);\r
+ \r
+ if (!memcmp(id, "CMOD", 4)) {\r
+ /*if (!seen_cmod) {\r
+ OKT_doCMOD(); // gcc for ipod stucks here, saying it is a call to memcpy...\r
+ seen_cmod = 1;\r
+ } else {*/\r
+ _mm_errno = MMERR_LOADING_HEADER;\r
+ return 0;\r
+ /* } */\r
+ } else if (!memcmp(id, "SAMP", 4)) {\r
+ if (!seen_samp && OKT_doSAMP(len))\r
+ seen_samp = 1;\r
+ else {\r
+ _mm_errno = MMERR_LOADING_HEADER;\r
+ return 0;\r
+ }\r
+ } else if (!memcmp(id, "SPEE", 4)) {\r
+ if (!seen_spee) {\r
+ OKT_doSPEE();\r
+ seen_spee = 1;\r
+ } else {\r
+ _mm_errno = MMERR_LOADING_HEADER;\r
+ return 0;\r
+ }\r
+ } else if (!memcmp(id, "SLEN", 4)) {\r
+ if (!seen_slen) {\r
+ OKT_doSLEN();\r
+ seen_slen = 1;\r
+ } else {\r
+ _mm_errno = MMERR_LOADING_HEADER;\r
+ return 0;\r
+ }\r
+ } else if (!memcmp(id, "PLEN", 4)) {\r
+ if (!seen_plen) {\r
+ OKT_doPLEN();\r
+ seen_plen = 1;\r
+ } else {\r
+ _mm_errno = MMERR_LOADING_HEADER;\r
+ return 0;\r
+ }\r
+ } else if (!memcmp(id, "PATT", 4)) {\r
+ if (!seen_plen) {\r
+ _mm_errno = MMERR_LOADING_HEADER;\r
+ return 0;\r
+ }\r
+ if (!seen_patt && OKT_doPATT())\r
+ seen_patt = 1;\r
+ else {\r
+ _mm_errno = MMERR_LOADING_HEADER;\r
+ return 0;\r
+ }\r
+ } else if (!memcmp(id,"PBOD", 4)) {\r
+ /* need to know numpat and numchn */\r
+ if (!seen_slen || !seen_cmod || (patnum >= of.numpat)) {\r
+ _mm_errno = MMERR_LOADING_HEADER;\r
+ return 0;\r
+ }\r
+ if (!OKT_doPBOD(patnum++)) {\r
+ _mm_errno = MMERR_LOADING_PATTERN;\r
+ return 0;\r
+ }\r
+ } else if (!memcmp(id,"SBOD",4)) {\r
+ /* need to know numsmp */\r
+ if (!seen_samp) {\r
+ _mm_errno = MMERR_LOADING_HEADER;\r
+ return 0;\r
+ }\r
+ while ((insnum < of.numins) && !of.samples[insnum].length)\r
+ insnum++;\r
+ if (insnum >= of.numins) {\r
+ _mm_errno = MMERR_LOADING_HEADER;\r
+ return 0;\r
+ }\r
+ OKT_doSBOD(insnum++);\r
+ }\r
+\r
+ /* goto next block start position */\r
+ _mm_fseek(modreader, fp + len, SEEK_SET);\r
+ }\r
+\r
+ if (!seen_cmod || !seen_samp || !seen_patt ||\r
+ !seen_slen || !seen_plen || (patnum != of.numpat)) {\r
+ _mm_errno = MMERR_LOADING_HEADER;\r
+ return 0;\r
+ }\r
+\r
+ return 1;\r
+}\r
+\r
+CHAR *OKT_LoadTitle(void)\r
+{\r
+ return strdup("");\r
+}\r
+\r
+/*========== Loader information */\r
+\r
+MIKMODAPI MLOADER load_okt = {\r
+ NULL,\r
+ "OKT",\r
+ "OKT (Amiga Oktalyzer)",\r
+ NULL,\r
+ OKT_Test,\r
+ OKT_Load,\r
+ NULL,\r
+ OKT_LoadTitle\r
+};\r
+\r
+/* ex:set ts=4: */\r