A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita
audio
rust
zig
deno
mpris
rockbox
mpd
1/* MikMod sound library
2 (c) 2004, Raphael Assenat and others - see file AUTHORS for
3 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 ASYLUM Music Format v1.0 (.amf) loader
26 adapted from load_mod.c by Raphael Assenat <raph@raphnet.net>,
27 with the help of the AMF2MOD utility sourcecode,
28 written to convert crusader's amf files into 8
29 channels mod file in 1995 by Mr. P / Powersource
30 mrp@fish.share.net, ac054@sfn.saskatoon.sk.ca
31
32
33==============================================================================*/
34
35#ifdef HAVE_CONFIG_H
36#include "config.h"
37#endif
38
39#ifdef HAVE_UNISTD_H
40#include <unistd.h>
41#endif
42
43#include <ctype.h>
44#include <string.h>
45
46#include "mikmod_internals.h"
47
48/*========== Module structure */
49
50typedef struct MSAMPINFO {
51 CHAR samplename[24];
52 UBYTE finetune;
53 UBYTE volume;
54 ULONG length;
55 ULONG reppos;
56 ULONG replen;
57} MSAMPINFO;
58
59typedef struct MODULEHEADER {
60 CHAR songname[21];
61 UBYTE initspeed;
62 UBYTE inittempo;
63 UBYTE num_samples;
64 UBYTE num_patterns; /* number of patterns used */
65 UBYTE num_orders;
66 UBYTE reppos;
67 UBYTE positions[256]; /* which pattern to play at pos */
68 MSAMPINFO samples[64]; /* all sampleinfo */
69} MODULEHEADER;
70
71typedef struct MODTYPE {
72 CHAR id[5];
73 UBYTE channels;
74 CHAR *name;
75} MODTYPE;
76
77typedef struct MODNOTE {
78 UBYTE a, b, c, d;
79} MODNOTE;
80
81/* This table is taken from AMF2MOD.C
82 * written in 1995 by Mr. P / Powersource
83 * mrp@fish.share.net, ac054@sfn.saskatoon.sk.ca */
84static const UWORD periodtable[] = {
85 6848,6464,6096,5760,5424,5120,4832,4560,4304,
86 4064,3840,3628,3424,3232,3048,2880,2712,2560,
87 2416,2280,2152,2032,1920,1814,1712,1616,1524,
88 1440,1356,1280,1208,1140,1076,1016, 960, 907,
89 856, 808, 762, 720, 678, 640, 604, 570, 538,
90 508, 480, 453, 428, 404, 381, 360, 339, 320,
91 302, 285, 269, 254, 240, 226, 214, 202, 190,
92 180, 170, 160, 151, 143, 135, 127, 120, 113,
93 107, 101, 95, 90, 85, 80, 75, 71, 67,
94 63, 60, 56, 53, 50, 47, 45, 42, 40,
95 37, 35, 33, 31, 30, 28};
96
97/*========== Loader variables */
98
99static CHAR asylum[] = "Asylum 1.0";
100
101static MODULEHEADER *mh = NULL;
102static MODNOTE *patbuf = NULL;
103static int modtype = 0;
104
105/*========== Loader code */
106
107static int ASY_CheckType(UBYTE *id, UBYTE *numchn, CHAR **descr)
108{
109 if (!memcmp(id, "ASYLUM Music Format V1.0", 24))
110 {
111 *descr = asylum;
112 *numchn = 8;
113 modtype = 1;
114 return 1;
115 }
116
117 return 0;
118}
119
120static int ASY_Test(void)
121{
122 UBYTE namestring[24], numchn;
123 CHAR *descr;
124
125 /* Read the magic string */
126 _mm_fseek(modreader, 0, SEEK_SET);
127 if (!_mm_read_UBYTES(namestring, 24, modreader))
128 return 0;
129
130 /* Test if the string is what we expect */
131 if (ASY_CheckType(namestring, &numchn, &descr))
132 return 1;
133
134 return 0;
135}
136
137static int ASY_Init(void)
138{
139 if (!(mh = (MODULEHEADER *)MikMod_malloc(sizeof(MODULEHEADER))))
140 return 0;
141 return 1;
142}
143
144static void ASY_Cleanup(void)
145{
146 MikMod_free(mh);
147 MikMod_free(patbuf);
148 mh = NULL;
149 patbuf = NULL;
150}
151
152static int ConvertNote(MODNOTE *n)
153{
154 UBYTE instrument, effect, effdat, note;
155 UWORD period;
156 UBYTE lastnote = 0;
157
158 instrument = n->b&0x1f;
159 effect = n->c;
160 effdat = n->d;
161
162 /* convert amf note to mod period */
163 if (n->a) {
164 period = periodtable[n->a];
165 } else {
166 period = 0;
167 }
168
169 /* Convert the period to a note number */
170 note = 0;
171 if (period)
172 {
173 for (note = 0; note < 7 * OCTAVE; note++)
174 if (period >= npertab[note])
175 break;
176 if (note == 7 * OCTAVE)
177 note = 0;
178 else
179 note++;
180 }
181
182 if (instrument) {
183 /* if instrument does not exist, note cut */
184 if ((instrument > mh->num_samples) || (!mh->samples[instrument - 1].length)) {
185 UniPTEffect(0xc, 0);
186 if (effect == 0xc)
187 effect = effdat = 0;
188 } else {
189 /* Protracker handling */
190 if (!modtype) {
191 /* if we had a note, then change instrument...*/
192 if (note)
193 UniInstrument(instrument - 1);
194 /* ...otherwise, only adjust volume... */
195 else {
196 /* ...unless an effect was specified,
197 * which forces a new note to be
198 * played */
199 if (effect || effdat) {
200 UniInstrument(instrument - 1);
201 } else
202 UniPTEffect(0xc,
203 mh->samples[instrument -
204 1].volume & 0x7f);
205 }
206 } else {
207 /* Fasttracker handling */
208 UniInstrument(instrument - 1);
209 if (!note)
210 note = lastnote;
211 }
212 }
213 }
214 if (note) {
215 UniNote(note + 2 * OCTAVE - 1);
216 lastnote = note;
217 }
218
219 /* Convert pattern jump from Dec to Hex */
220 if (effect == 0xd)
221 effdat = (((effdat & 0xf0) >> 4) * 10) + (effdat & 0xf);
222
223 /* Volume slide, up has priority */
224 if ((effect == 0xa) && (effdat & 0xf) && (effdat & 0xf0))
225 effdat &= 0xf0;
226
227 if (effect == 0x1b) {
228 return 0; /* UniEffect(UNI_S3MEFFECTQ,dat) ? */
229 }
230 if (effect > 0xf) {
231 return 0; /* return -1 to fail? */
232 }
233
234 UniPTEffect(effect, effdat);
235 return 0;
236}
237
238static UBYTE *ConvertTrack(MODNOTE *n)
239{
240 int t;
241
242 UniReset();
243 for (t = 0; t < 64; t++) {
244 if (ConvertNote(n) < 0)
245 return NULL;
246 UniNewline();
247 n += of.numchn;
248 }
249 return UniDup();
250}
251
252/* Loads all patterns of a modfile and converts them into the 3 byte format. */
253static int ML_LoadPatterns(void)
254{
255 unsigned int t, s, tracks = 0;
256
257 if (!AllocPatterns()) {
258 return 0;
259 }
260 if (!AllocTracks()) {
261 return 0;
262 }
263
264 /* Allocate temporary buffer for loading and converting the patterns */
265 if (!(patbuf = (MODNOTE *)MikMod_calloc(64U * of.numchn, sizeof(MODNOTE))))
266 return 0;
267
268
269 /* patterns start here */
270 _mm_fseek(modreader, 0xA66, SEEK_SET);
271 for (t = 0; t < of.numpat; t++) {
272 /* Load the pattern into the temp buffer and convert it */
273 for (s = 0; s < (64U * of.numchn); s++) {
274 patbuf[s].a = _mm_read_UBYTE(modreader);
275 patbuf[s].b = _mm_read_UBYTE(modreader);
276 patbuf[s].c = _mm_read_UBYTE(modreader);
277 patbuf[s].d = _mm_read_UBYTE(modreader);
278 }
279 for (s = 0; s < of.numchn; s++) {
280 if (!(of.tracks[tracks++] = ConvertTrack(patbuf + s))) {
281 return 0;
282 }
283 }
284 }
285 return 1;
286}
287
288static int ASY_Load(int curious)
289{
290 int t;
291 SAMPLE *q;
292 MSAMPINFO *s;
293 CHAR *descr=asylum;
294 ULONG seekpos;
295 (void)curious;
296
297 /* no title in asylum amf files :( */
298 mh->songname[0] = '\0';
299
300 _mm_fseek(modreader, 0x20, SEEK_SET);
301 mh->initspeed = _mm_read_UBYTE(modreader);
302 mh->inittempo = _mm_read_UBYTE(modreader);
303 mh->num_samples = _mm_read_UBYTE(modreader);
304 mh->num_patterns = _mm_read_UBYTE(modreader);
305 mh->num_orders = _mm_read_UBYTE(modreader);
306 mh->reppos = _mm_read_UBYTE(modreader);
307
308 _mm_read_UBYTES(mh->positions, 256, modreader);
309
310 #ifdef MIKMOD_DEBUG
311 fprintf(stderr, "ASY: bpm=%d, spd=%d, numins=%d, numpat=%d\n",
312 mh->inittempo, mh->initspeed, mh->num_samples, mh->num_patterns);
313 #endif
314 if (!mh->initspeed || !mh->inittempo || mh->num_samples > 64) {
315 _mm_errno = MMERR_NOT_A_MODULE;
316 return 0;
317 }
318
319 /* read samples headers*/
320 for (t = 0; t < mh->num_samples; t++) {
321 s = &mh->samples[t];
322
323 _mm_fseek(modreader, 0x126 + (t*37), SEEK_SET);
324
325 _mm_read_string(s->samplename, 22, modreader);
326 s->samplename[22] = 0;
327
328 s->finetune = _mm_read_UBYTE(modreader);
329 s->volume = _mm_read_UBYTE(modreader);
330 _mm_skip_BYTE(modreader);/* skip unknown byte */
331 s->length = _mm_read_I_ULONG(modreader);
332 s->reppos = _mm_read_I_ULONG(modreader);
333 s->replen = _mm_read_I_ULONG(modreader);
334 }
335
336 if (_mm_eof(modreader)) {
337 _mm_errno = MMERR_LOADING_HEADER;
338 return 0;
339 }
340
341 _mm_fseek(modreader, 37*(64-mh->num_samples), SEEK_CUR);
342
343 /* set module variables */
344 of.initspeed = mh->initspeed;
345 of.inittempo = mh->inittempo;
346 of.numchn = 8;
347 modtype = 0;
348 of.songname = MikMod_strdup("");
349 of.numpos = mh->num_orders;
350 of.reppos = mh->reppos;
351 of.numpat = mh->num_patterns;
352 of.numtrk = of.numpat * of.numchn;
353
354 /* Copy positions (orders) */
355 if (!AllocPositions(of.numpos))
356 return 0;
357 for (t = 0; t < of.numpos; t++) {
358 of.positions[t] = mh->positions[t];
359 if (of.positions[t]>of.numpat) { /* SANITIY CHECK */
360 /* fprintf(stderr,"positions[%d]=%d > numpat=%d\n",t,of.positions[t],of.numpat);*/
361 _mm_errno = MMERR_LOADING_HEADER;
362 return 0;
363 }
364 }
365
366 /* Finally, init the sampleinfo structures */
367 of.numins = mh->num_samples;
368 of.numsmp = mh->num_samples;
369 if (!AllocSamples())
370 return 0;
371 s = mh->samples;
372 q = of.samples;
373 seekpos = 2662+(2048*(of.numpat));
374 for (t = 0; t < of.numins; t++) {
375 /* convert the samplename */
376 q->samplename = DupStr(s->samplename, 23, 1);
377
378 /* init the sampleinfo variables */
379 q->speed = finetune[s->finetune & 0xf];
380 q->volume = s->volume & 0x7f;
381
382 q->loopstart = (ULONG)s->reppos;
383 q->loopend = (ULONG)q->loopstart + (s->replen);
384 q->length = (ULONG)s->length;
385
386 q->flags = SF_SIGNED;
387
388 q->seekpos = seekpos;
389 seekpos += q->length;
390
391 if ((s->replen) > 2) {
392 q->flags |= SF_LOOP;
393 }
394
395 /* fix replen if repend > length */
396 if (q->loopend > q->length)
397 q->loopend = q->length;
398
399 s++;
400 q++;
401 }
402
403 of.modtype = MikMod_strdup(descr);
404
405 if (!ML_LoadPatterns())
406 return 0;
407
408 return 1;
409}
410
411static CHAR *ASY_LoadTitle(void)
412{
413 return MikMod_strdup("");
414}
415
416/*========== Loader information */
417
418MLOADER load_asy = {
419 NULL,
420 "AMF",
421 "AMF (ASYLUM Music Format V1.0)",
422 ASY_Init,
423 ASY_Test,
424 ASY_Load,
425 ASY_Cleanup,
426 ASY_LoadTitle
427};
428
429/* ex:set ts=4: */