A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita audio rust zig deno mpris rockbox mpd
at master 429 lines 9.8 kB view raw
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: */