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