Add "MikMod for Rockbox 0.1" from 2007-06-29
[mikmod-rockbox.git] / apps / plugins / mikmod / loaders / load_okt.c
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
4 \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
9 \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
14 \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
18         02111-1307, USA.\r
19 */\r
20 \r
21 /*==============================================================================\r
22 \r
23   $Id: load_okt.c,v 1.1.1.1 2004/01/21 01:36:35 raph Exp $\r
24 \r
25   Oktalyzer (OKT) module loader\r
26 \r
27 ==============================================================================*/\r
28 \r
29 /*\r
30         Written by UFO <ufo303@poczta.onet.pl>\r
31         based on the file description compiled by Harald Zappe\r
32                                                               <zappe@gaea.sietec.de>\r
33 \r
34 */\r
35 \r
36 #ifdef HAVE_CONFIG_H\r
37 #include "config.h"\r
38 #endif\r
39 \r
40 #ifdef HAVE_UNISTD_H\r
41 #include <unistd.h>\r
42 #endif\r
43 \r
44 #include <stdio.h>\r
45 #ifdef HAVE_MEMORY_H\r
46 #include <memory.h>\r
47 #endif\r
48 #include <string.h>\r
49 \r
50 #include "mikmod_internals.h"\r
51 \r
52 #ifdef SUNOS\r
53 extern int fprintf(FILE *, const char *, ...);\r
54 #endif\r
55 \r
56 /*========== Module blocks */\r
57 \r
58 /* sample information */\r
59 typedef struct OKTSAMPLE {\r
60         CHAR sampname[20];\r
61         ULONG len;\r
62         UWORD loopbeg;\r
63         UWORD looplen;\r
64         UBYTE volume;\r
65 } OKTSAMPLE;\r
66 \r
67 typedef struct OKTNOTE {\r
68         UBYTE note, ins, eff, dat;\r
69 } OKTNOTE;\r
70 \r
71 /*========== Loader variables */\r
72 \r
73 static OKTNOTE *okttrk = NULL;\r
74 \r
75 /*========== Loader code */\r
76 \r
77 BOOL OKT_Test(void)\r
78 {\r
79         CHAR id[8];\r
80 \r
81         if (!_mm_read_UBYTES(id, 8, modreader))\r
82                 return 0;\r
83         if (!memcmp(id, "OKTASONG", 8))\r
84                 return 1;\r
85 \r
86         return 0;\r
87 }\r
88 \r
89 /*      Pattern analysis routine.\r
90         Effects not implemented (yet) : (in decimal)\r
91         11 Arpeggio 4: Change note every 50Hz tick between N,H,N,L\r
92         12 Arpeggio 5: Change note every 50Hz tick between H,H,N\r
93                    N = normal note being played in this channel (1-36)\r
94                    L = normal note number minus upper four bits of 'data'.\r
95                    H = normal note number plus  lower four bits of 'data'.\r
96     13 Decrease note number by 'data' once per tick.\r
97     17 Increase note number by 'data' once per tick.\r
98     21 Decrease note number by 'data' once per line.\r
99     30 Increase note number by 'data' once per line.\r
100 */\r
101 static UBYTE *OKT_ConvertTrack(UBYTE patrows)\r
102 {\r
103         int t;\r
104         UBYTE ins, note, eff, dat;\r
105 \r
106         UniReset();\r
107         for (t = 0; t < patrows; t++) {\r
108                 note = okttrk[t].note;\r
109                 ins = okttrk[t].ins;\r
110                 eff = okttrk[t].eff;\r
111                 dat = okttrk[t].dat;\r
112 \r
113                 if (note) {\r
114                         UniNote(note + 3 * OCTAVE - 1);\r
115                         UniInstrument(ins);\r
116                 }\r
117 \r
118                 if (eff)\r
119                         switch (eff) {\r
120                         case 1:                         /* Porta Up */\r
121                                 UniPTEffect(0x1, dat);\r
122                                 break;\r
123                         case 2:                         /* Portamento Down */\r
124                                 UniPTEffect(0x2, dat);\r
125                                 break;\r
126                         /* case 9: what is this? */\r
127                         case 10:                        /* Arpeggio 3 */\r
128                         case 11:                        /* Arpeggio 4 */\r
129                         case 12:                        /* Arpeggio 5 */\r
130                                 UniWriteByte(UNI_OKTARP);\r
131                                 UniWriteByte(eff + 3 - 10);\r
132                                 UniWriteByte(dat);\r
133                                 break;\r
134                         case 15:                        /* Amiga filter toggle, ignored */\r
135                                 break;\r
136                         case 25:                        /* Pattern Jump */\r
137                                 dat = (dat >> 4) * 10 + (dat & 0x0f);\r
138                                 UniPTEffect(0xb, dat);\r
139                                 break;\r
140                         case 27:                        /* Release - similar to Keyoff */\r
141                                 UniWriteByte(UNI_KEYOFF);\r
142                                 break;\r
143                         case 28:                        /* Set Tempo */\r
144                                 UniPTEffect(0xf, dat & 0x0f);\r
145                                 break;\r
146                         case 31:                        /* volume Control */\r
147                                 if (dat <= 0x40)\r
148                                         UniPTEffect(0xc, dat);\r
149                                 else if (dat <= 0x50)\r
150                                         UniEffect(UNI_XMEFFECTA, (dat - 0x40)); /* fast fade out */\r
151                                 else if (dat <= 0x60)\r
152                                         UniEffect(UNI_XMEFFECTA, (dat - 0x50) << 4);    /* fast fade in */\r
153                                 else if (dat <= 0x70)\r
154                                         UniEffect(UNI_XMEFFECTEB, (dat - 0x60));        /* slow fade out */\r
155                                 else if (dat <= 0x80)\r
156                                         UniEffect(UNI_XMEFFECTEA, (dat - 0x70));        /* slow fade in */\r
157                                 break;\r
158 #ifdef MIKMOD_DEBUG\r
159                         default:\r
160                                 fprintf(stderr, "\rUnimplemented effect (%02d,%02x)\n",\r
161                                                 eff, dat);\r
162 #endif\r
163                         }\r
164 \r
165                 UniNewline();\r
166         }\r
167         return UniDup();\r
168 }\r
169 \r
170 /* Read "channel modes" i.e. channel number and panning information */\r
171 static void OKT_doCMOD(void)\r
172 {\r
173         /* amiga channel panning table */\r
174         UBYTE amigapan[4] = { 0x00, 0xff, 0xff, 0x00 };\r
175         int t;\r
176 \r
177         of.numchn = 0;\r
178         of.flags |= UF_PANNING;\r
179 \r
180         for (t = 0; t < 4; t++)\r
181                 if (_mm_read_M_UWORD(modreader)) {\r
182                         /* two channels tied to the same Amiga hardware voice */\r
183                         of.panning[of.numchn++] = amigapan[t];\r
184                         of.panning[of.numchn++] = amigapan[t];\r
185                 } else\r
186                         /* one channel tied to the Amiga hardware voice */\r
187                         of.panning[of.numchn++] = amigapan[t];\r
188 }\r
189 \r
190 /* Read sample information */\r
191 static BOOL OKT_doSAMP(int len)\r
192 {\r
193         int t;\r
194         SAMPLE *q;\r
195         OKTSAMPLE s;\r
196 \r
197         of.numins = of.numsmp = (len / 0x20);\r
198         if (!AllocSamples())\r
199                 return 0;\r
200 \r
201         for (t = 0, q = of.samples; t < of.numins; t++, q++) {\r
202                 _mm_read_UBYTES(s.sampname, 20, modreader);\r
203                 s.len = _mm_read_M_ULONG(modreader);\r
204                 s.loopbeg = _mm_read_M_UWORD(modreader) * 2;\r
205                 s.looplen = _mm_read_M_UWORD(modreader) * 2;\r
206                 _mm_read_UBYTE(modreader);\r
207                 s.volume = _mm_read_UBYTE(modreader);\r
208                 _mm_read_M_UWORD(modreader);\r
209 \r
210                 if (_mm_eof(modreader)) {\r
211                         _mm_errno = MMERR_LOADING_SAMPLEINFO;\r
212                         return 0;\r
213                 }\r
214 \r
215                 if (!s.len)\r
216                         q->seekpos = q->length = q->loopstart = q->loopend = q->flags = 0;\r
217                 else {\r
218                         s.len--;\r
219                         /* sanity checks */\r
220                         if (s.loopbeg > s.len)\r
221                                 s.loopbeg = s.len;\r
222                         if (s.loopbeg + s.looplen > s.len)\r
223                                 s.looplen = s.len - s.loopbeg;\r
224                         if (s.looplen < 2)\r
225                                 s.looplen = 0;\r
226 \r
227                         q->length = s.len;\r
228                         q->loopstart = s.loopbeg;\r
229                         q->loopend = s.looplen + q->loopstart;\r
230                         q->volume = s.volume;\r
231                         q->flags = SF_SIGNED;\r
232 \r
233                         if (s.looplen)\r
234                                 q->flags |= SF_LOOP;\r
235                 }\r
236                 q->samplename = DupStr(s.sampname, 20, 1);\r
237                 q->speed = 8287;\r
238         }\r
239         return 1;\r
240 }\r
241 \r
242 /* Read speed information */\r
243 static void OKT_doSPEE(void)\r
244 {\r
245         int tempo = _mm_read_M_UWORD(modreader);\r
246 \r
247         of.initspeed = tempo;\r
248 }\r
249 \r
250 /* Read song length information */\r
251 static void OKT_doSLEN(void)\r
252 {\r
253         of.numpat = _mm_read_M_UWORD(modreader);\r
254 }\r
255 \r
256 /* Read pattern length information */\r
257 static void OKT_doPLEN(void)\r
258 {\r
259         of.numpos = _mm_read_M_UWORD(modreader);\r
260 }\r
261 \r
262 /* Read order table */\r
263 static BOOL OKT_doPATT(void)\r
264 {\r
265         int t;\r
266 \r
267         if (!of.numpos || !AllocPositions(of.numpos))\r
268                 return 0;\r
269 \r
270         for (t = 0; t < 128; t++)\r
271                 if (t < of.numpos)\r
272                         of.positions[t] = _mm_read_UBYTE(modreader);\r
273                 else\r
274                         break;\r
275 \r
276         return 1;\r
277 }\r
278 \r
279 static BOOL OKT_doPBOD(int patnum)\r
280 {\r
281         char *patbuf;\r
282         int rows, i;\r
283         int u;\r
284 \r
285         if (!patnum) {\r
286                 of.numtrk = of.numpat * of.numchn;\r
287 \r
288                 if (!AllocTracks() || !AllocPatterns())\r
289                         return 0;\r
290         }\r
291 \r
292         /* Read pattern */\r
293         of.pattrows[patnum] = rows = _mm_read_M_UWORD(modreader);\r
294 \r
295         if (!(okttrk = (OKTNOTE *) _mm_calloc(rows, sizeof(OKTNOTE))) ||\r
296             !(patbuf = (char *)_mm_calloc(rows * of.numchn, sizeof(OKTNOTE))))\r
297                 return 0;\r
298         _mm_read_UBYTES(patbuf, rows * of.numchn * sizeof(OKTNOTE), modreader);\r
299         if (_mm_eof(modreader)) {\r
300                 _mm_errno = MMERR_LOADING_PATTERN;\r
301                 return 0;\r
302         }\r
303 \r
304         for (i = 0; i < of.numchn; i++) {\r
305                 for (u = 0; u < rows; u++) {\r
306                         okttrk[u].note = patbuf[(u * of.numchn + i) * sizeof(OKTNOTE)];\r
307                         okttrk[u].ins = patbuf[(u * of.numchn + i) * sizeof(OKTNOTE) + 1];\r
308                         okttrk[u].eff = patbuf[(u * of.numchn + i) * sizeof(OKTNOTE) + 2];\r
309                         okttrk[u].dat = patbuf[(u * of.numchn + i) * sizeof(OKTNOTE) + 3];\r
310                 }\r
311 \r
312                 if (!(of.tracks[patnum * of.numchn + i] = OKT_ConvertTrack(rows)))\r
313                         return 0;\r
314         }\r
315         _mm_free(patbuf);\r
316         _mm_free(okttrk);\r
317         return 1;\r
318 }\r
319 \r
320 static void OKT_doSBOD(int insnum)\r
321 {\r
322         of.samples[insnum].seekpos = _mm_ftell(modreader);\r
323 }\r
324 \r
325 BOOL OKT_Load(BOOL curious)\r
326 {\r
327         UBYTE id[4];\r
328         ULONG len;\r
329         ULONG fp;\r
330         BOOL seen_cmod = 0, seen_samp = 0, seen_slen = 0, seen_plen = 0, seen_patt\r
331                         = 0, seen_spee = 0;\r
332         int patnum = 0, insnum = 0;\r
333         (void)curious;\r
334 \r
335         /* skip OKTALYZER header */\r
336         _mm_fseek(modreader, 8, SEEK_SET);\r
337         of.songname = strdup("");\r
338 \r
339         of.modtype = strdup("Amiga Oktalyzer");\r
340         of.numpos = of.reppos = 0;\r
341         \r
342         /* default values */\r
343         of.initspeed = 6;\r
344         of.inittempo = 125;\r
345 \r
346         while (1) {\r
347                 /* read block header */\r
348                 _mm_read_UBYTES(id, 4, modreader);\r
349                 len = _mm_read_M_ULONG(modreader);\r
350                 \r
351                 if (_mm_eof(modreader))\r
352                         break;\r
353                 fp = _mm_ftell(modreader);\r
354                 \r
355                 if (!memcmp(id, "CMOD", 4)) {\r
356                         /*if (!seen_cmod) {\r
357                                 OKT_doCMOD();  // gcc for ipod stucks here, saying it is a call to memcpy...\r
358                                 seen_cmod = 1;\r
359                         } else {*/\r
360                                 _mm_errno = MMERR_LOADING_HEADER;\r
361                                 return 0;\r
362                         /* } */\r
363                 } else if (!memcmp(id, "SAMP", 4)) {\r
364                         if (!seen_samp && OKT_doSAMP(len))\r
365                                 seen_samp = 1;\r
366                         else {\r
367                                 _mm_errno = MMERR_LOADING_HEADER;\r
368                                 return 0;\r
369                         }\r
370                 } else if (!memcmp(id, "SPEE", 4)) {\r
371                         if (!seen_spee) {\r
372                                 OKT_doSPEE();\r
373                                 seen_spee = 1;\r
374                         } else {\r
375                                 _mm_errno = MMERR_LOADING_HEADER;\r
376                                 return 0;\r
377                         }\r
378                 } else if (!memcmp(id, "SLEN", 4)) {\r
379                         if (!seen_slen) {\r
380                                 OKT_doSLEN();\r
381                                 seen_slen = 1;\r
382                         } else {\r
383                                 _mm_errno = MMERR_LOADING_HEADER;\r
384                                 return 0;\r
385                         }\r
386                 } else if (!memcmp(id, "PLEN", 4)) {\r
387                         if (!seen_plen) {\r
388                                 OKT_doPLEN();\r
389                                 seen_plen = 1;\r
390                         } else {\r
391                                 _mm_errno = MMERR_LOADING_HEADER;\r
392                                 return 0;\r
393                         }\r
394                 } else if (!memcmp(id, "PATT", 4)) {\r
395                         if (!seen_plen) {\r
396                                 _mm_errno = MMERR_LOADING_HEADER;\r
397                                 return 0;\r
398                         }\r
399                         if (!seen_patt && OKT_doPATT())\r
400                                 seen_patt = 1;\r
401                         else {\r
402                                 _mm_errno = MMERR_LOADING_HEADER;\r
403                                 return 0;\r
404                         }\r
405                 } else if (!memcmp(id,"PBOD", 4)) {\r
406                         /* need to know numpat and numchn */\r
407                         if (!seen_slen || !seen_cmod || (patnum >= of.numpat)) {\r
408                                 _mm_errno = MMERR_LOADING_HEADER;\r
409                                 return 0;\r
410                         }\r
411                         if (!OKT_doPBOD(patnum++)) {\r
412                                 _mm_errno = MMERR_LOADING_PATTERN;\r
413                                 return 0;\r
414                         }\r
415                 } else if (!memcmp(id,"SBOD",4)) {\r
416                         /* need to know numsmp */\r
417                         if (!seen_samp) {\r
418                                 _mm_errno = MMERR_LOADING_HEADER;\r
419                                 return 0;\r
420                         }\r
421                         while ((insnum < of.numins) && !of.samples[insnum].length)\r
422                                 insnum++;\r
423                         if (insnum >= of.numins) {\r
424                                 _mm_errno = MMERR_LOADING_HEADER;\r
425                                 return 0;\r
426                         }\r
427                         OKT_doSBOD(insnum++);\r
428                 }\r
429 \r
430                 /* goto next block start position */\r
431                 _mm_fseek(modreader, fp + len, SEEK_SET);\r
432         }\r
433 \r
434         if (!seen_cmod || !seen_samp || !seen_patt ||\r
435                 !seen_slen || !seen_plen || (patnum != of.numpat)) {\r
436                 _mm_errno = MMERR_LOADING_HEADER;\r
437                 return 0;\r
438         }\r
439 \r
440         return 1;\r
441 }\r
442 \r
443 CHAR *OKT_LoadTitle(void)\r
444 {\r
445         return strdup("");\r
446 }\r
447 \r
448 /*========== Loader information */\r
449 \r
450 MIKMODAPI MLOADER load_okt = {\r
451         NULL,\r
452         "OKT",\r
453         "OKT (Amiga Oktalyzer)",\r
454         NULL,\r
455         OKT_Test,\r
456         OKT_Load,\r
457         NULL,\r
458         OKT_LoadTitle\r
459 };\r
460 \r
461 /* ex:set ts=4: */\r