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 11 kB view raw
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 Screamtracker 2 (STM) module loader 26 27==============================================================================*/ 28 29#ifdef HAVE_CONFIG_H 30#include "config.h" 31#endif 32 33#ifdef HAVE_UNISTD_H 34#include <unistd.h> 35#endif 36 37#include <stdio.h> 38#ifdef HAVE_MEMORY_H 39#include <memory.h> 40#endif 41#include <string.h> 42 43#include "mikmod_internals.h" 44 45#ifdef SUNOS 46extern int fprintf(FILE *, const char *, ...); 47#endif 48 49/*========== Module structure */ 50 51/* sample information */ 52typedef struct STMSAMPLE { 53 CHAR filename[12]; 54 UBYTE unused; /* 0x00 */ 55 UBYTE instdisk; /* Instrument disk */ 56 UWORD reserved; 57 UWORD length; /* Sample length */ 58 UWORD loopbeg; /* Loop start point */ 59 UWORD loopend; /* Loop end point */ 60 UBYTE volume; /* Volume */ 61 UBYTE reserved2; 62 UWORD c2spd; /* Good old c2spd */ 63 ULONG reserved3; 64 UWORD isa; 65} STMSAMPLE; 66 67/* header */ 68typedef struct STMHEADER { 69 CHAR songname[20]; 70 CHAR trackername[8]; /* !Scream! for ST 2.xx */ 71 UBYTE unused; /* 0x1A */ 72 UBYTE filetype; /* 1=song, 2=module */ 73 UBYTE ver_major; 74 UBYTE ver_minor; 75 UBYTE inittempo; /* initspeed= stm inittempo>>4 */ 76 UBYTE numpat; /* number of patterns */ 77 UBYTE globalvol; 78 UBYTE reserved[13]; 79 STMSAMPLE sample[31]; /* STM sample data */ 80 UBYTE patorder[128]; /* Docs say 64 - actually 128 */ 81} STMHEADER; 82 83typedef struct STMNOTE { 84 UBYTE note,insvol,volcmd,cmdinf; 85} STMNOTE; 86 87/*========== Loader variables */ 88 89static STMNOTE *stmbuf = NULL; 90static STMHEADER *mh = NULL; 91 92/* tracker identifiers */ 93static const CHAR * STM_Version[STM_NTRACKERS] = { 94 "Screamtracker 2", 95 "Converted by MOD2STM (STM format)", 96 "Wuzamod (STM format)" 97}; 98 99/*========== Loader code */ 100 101static int STM_Test(void) 102{ 103 UBYTE str[44]; 104 int t; 105 106 memset(str,0,44); 107 _mm_fseek(modreader,20,SEEK_SET); 108 _mm_read_UBYTES(str,44,modreader); 109 if(str[9]!=2) return 0; /* STM Module = filetype 2 */ 110 111 /* Prevent false positives for S3M files */ 112 if(!memcmp(str+40,"SCRM",4)) 113 return 0; 114 115 for (t=0;t<STM_NTRACKERS;t++) 116 if(!memcmp(str,STM_Signatures[t],8)) 117 return 1; 118 119 return 0; 120} 121 122static int STM_Init(void) 123{ 124 if(!(mh=(STMHEADER*)MikMod_malloc(sizeof(STMHEADER)))) return 0; 125 if(!(stmbuf=(STMNOTE*)MikMod_calloc(64U*4,sizeof(STMNOTE)))) return 0; 126 127 return 1; 128} 129 130static void STM_Cleanup(void) 131{ 132 MikMod_free(mh); 133 MikMod_free(stmbuf); 134 mh=NULL; 135 stmbuf=NULL; 136} 137 138static void STM_ConvertNote(STMNOTE *n) 139{ 140 UBYTE note,ins,vol,cmd,inf; 141 142 /* extract the various information from the 4 bytes that make up a note */ 143 note = n->note; 144 ins = n->insvol>>3; 145 vol = (n->insvol&7)+((n->volcmd&0x70)>>1); 146 cmd = n->volcmd&15; 147 inf = n->cmdinf; 148 149 if((ins)&&(ins<32)) UniInstrument(ins-1); 150 151 /* special values of [SBYTE0] are handled here 152 we have no idea if these strange values will ever be encountered. 153 but it appears as those stms sound correct. */ 154 if((note==254)||(note==252)) { 155 UniPTEffect(0xc,0); /* note cut */ 156 n->volcmd|=0x80; 157 } else 158 /* if note < 251, then all three bytes are stored in the file */ 159 if(note<251) UniNote((((note>>4)+2)*OCTAVE)+(note&0xf)); 160 161 if((!(n->volcmd&0x80))&&(vol<65)) UniPTEffect(0xc,vol); 162 if(cmd!=255) 163 switch(cmd) { 164 case 1: /* Axx set speed to xx */ 165 UniPTEffect(0xf,inf>>4); 166 break; 167 case 2: /* Bxx position jump */ 168 UniPTEffect(0xb,inf); 169 break; 170 case 3: /* Cxx patternbreak to row xx */ 171 UniPTEffect(0xd,(((inf&0xf0)>>4)*10)+(inf&0xf)); 172 break; 173 case 4: /* Dxy volumeslide */ 174 UniEffect(UNI_S3MEFFECTD,inf); 175 break; 176 case 5: /* Exy toneslide down */ 177 UniEffect(UNI_S3MEFFECTE,inf); 178 break; 179 case 6: /* Fxy toneslide up */ 180 UniEffect(UNI_S3MEFFECTF,inf); 181 break; 182 case 7: /* Gxx Tone portamento,speed xx */ 183 UniPTEffect(0x3,inf); 184 break; 185 case 8: /* Hxy vibrato */ 186 UniPTEffect(0x4,inf); 187 break; 188 case 9: /* Ixy tremor, ontime x, offtime y */ 189 UniEffect(UNI_S3MEFFECTI,inf); 190 break; 191 case 0: /* protracker arpeggio */ 192 if(!inf) break; 193 /* fall through */ 194 case 0xa: /* Jxy arpeggio */ 195 UniPTEffect(0x0,inf); 196 break; 197 case 0xb: /* Kxy Dual command H00 & Dxy */ 198 UniPTEffect(0x4,0); 199 UniEffect(UNI_S3MEFFECTD,inf); 200 break; 201 case 0xc: /* Lxy Dual command G00 & Dxy */ 202 UniPTEffect(0x3,0); 203 UniEffect(UNI_S3MEFFECTD,inf); 204 break; 205 /* Support all these above, since ST2 can LOAD these values but can 206 actually only play up to J - and J is only half-way implemented 207 in ST2 */ 208 case 0x18: /* Xxx amiga panning command 8xx */ 209 UniPTEffect(0x8,inf); 210 of.flags |= UF_PANNING; 211 break; 212 } 213} 214 215static UBYTE *STM_ConvertTrack(STMNOTE *n) 216{ 217 int t; 218 219 UniReset(); 220 for(t=0;t<64;t++) { 221 STM_ConvertNote(n); 222 UniNewline(); 223 n+=of.numchn; 224 } 225 return UniDup(); 226} 227 228static int STM_LoadPatterns(unsigned int pattoload) 229{ 230 unsigned int t,s,tracks=0; 231 232 if(!AllocPatterns()) return 0; 233 if(!AllocTracks()) return 0; 234 235 /* Allocate temporary buffer for loading and converting the patterns */ 236 for(t=0;t<pattoload;t++) { 237 for(s=0;s<(64U*of.numchn);s++) { 238 stmbuf[s].note = _mm_read_UBYTE(modreader); 239 stmbuf[s].insvol = _mm_read_UBYTE(modreader); 240 stmbuf[s].volcmd = _mm_read_UBYTE(modreader); 241 stmbuf[s].cmdinf = _mm_read_UBYTE(modreader); 242 } 243 244 if(_mm_eof(modreader)) { 245 _mm_errno = MMERR_LOADING_PATTERN; 246 return 0; 247 } 248 249 for(s=0;s<of.numchn;s++) 250 if(!(of.tracks[tracks++]=STM_ConvertTrack(stmbuf+s))) return 0; 251 } 252 return 1; 253} 254 255static int STM_Load(int curious) 256{ 257 int blankpattern=0; 258 int pattoload; 259 int t; 260 ULONG samplestart; 261 ULONG sampleend; 262 SAMPLE *q; 263 (void)curious; 264 /* try to read stm header */ 265 _mm_read_string(mh->songname,20,modreader); 266 _mm_read_string(mh->trackername,8,modreader); 267 mh->unused =_mm_read_UBYTE(modreader); 268 mh->filetype =_mm_read_UBYTE(modreader); 269 mh->ver_major =_mm_read_UBYTE(modreader); 270 mh->ver_minor =_mm_read_UBYTE(modreader); 271 mh->inittempo =_mm_read_UBYTE(modreader); 272 if(!mh->inittempo) { 273 _mm_errno=MMERR_NOT_A_MODULE; 274 return 0; 275 } 276 mh->numpat =_mm_read_UBYTE(modreader); 277 mh->globalvol =_mm_read_UBYTE(modreader); 278 _mm_read_UBYTES(mh->reserved,13,modreader); 279 if(mh->numpat > 128) { 280 _mm_errno = MMERR_NOT_A_MODULE; 281 return 0; 282 } 283 284 for(t=0;t<31;t++) { 285 STMSAMPLE *s=&mh->sample[t]; /* STM sample data */ 286 287 _mm_read_string(s->filename,12,modreader); 288 s->unused =_mm_read_UBYTE(modreader); 289 s->instdisk =_mm_read_UBYTE(modreader); 290 s->reserved =_mm_read_I_UWORD(modreader); 291 s->length =_mm_read_I_UWORD(modreader); 292 s->loopbeg =_mm_read_I_UWORD(modreader); 293 s->loopend =_mm_read_I_UWORD(modreader); 294 s->volume =_mm_read_UBYTE(modreader); 295 s->reserved2=_mm_read_UBYTE(modreader); 296 s->c2spd =_mm_read_I_UWORD(modreader); 297 s->reserved3=_mm_read_I_ULONG(modreader); 298 s->isa =_mm_read_I_UWORD(modreader); 299 } 300 _mm_read_UBYTES(mh->patorder,128,modreader); 301 302 if(_mm_eof(modreader)) { 303 _mm_errno = MMERR_LOADING_HEADER; 304 return 0; 305 } 306 307 /* set module variables */ 308 for(t=0;t<STM_NTRACKERS;t++) 309 if(!memcmp(mh->trackername,STM_Signatures[t],8)) break; 310 of.modtype = MikMod_strdup(STM_Version[t]); 311 of.songname = DupStr(mh->songname,20,1); /* make a cstr of songname */ 312 of.numpat = mh->numpat; 313 of.inittempo = 125; /* mh->inittempo+0x1c; */ 314 of.initspeed = mh->inittempo>>4; 315 of.numchn = 4; /* get number of channels */ 316 of.reppos = 0; 317 of.flags |= UF_S3MSLIDES; 318 of.bpmlimit = 32; 319 320 t=0; 321 if(!AllocPositions(0x80)) return 0; 322 /* 99 terminates the patorder list */ 323 while(mh->patorder[t]<99) { 324 of.positions[t]=mh->patorder[t]; 325 326 /* Screamtracker 2 treaks patterns >= numpat as blank patterns. 327 * Example modules: jimmy.stm, Rauno/dogs.stm, Skaven/hevijanis istu maas.stm. 328 * 329 * Patterns>=64 have unpredictable behavior in Screamtracker 2, 330 * but nothing seems to rely on them, so they're OK to blank too. 331 */ 332 if(of.positions[t]>=mh->numpat) { 333 of.positions[t]=mh->numpat; 334 blankpattern=1; 335 } 336 337 if(++t == 0x80) { 338 _mm_errno = MMERR_NOT_A_MODULE; 339 return 0; 340 } 341 } 342 /* Allocate an extra blank pattern if the module references one. */ 343 pattoload=of.numpat; 344 if(blankpattern) of.numpat++; 345 of.numpos=t; 346 of.numtrk=of.numpat*of.numchn; 347 of.numins=of.numsmp=31; 348 349 if(!AllocSamples()) return 0; 350 if(!STM_LoadPatterns(pattoload)) return 0; 351 352 samplestart=_mm_ftell(modreader); 353 _mm_fseek(modreader,0,SEEK_END); 354 sampleend=_mm_ftell(modreader); 355 356 for(q=of.samples,t=0;t<of.numsmp;t++,q++) { 357 /* load sample info */ 358 q->samplename = DupStr(mh->sample[t].filename,12,1); 359 q->speed = (mh->sample[t].c2spd * 8363) / 8448; 360 q->volume = mh->sample[t].volume; 361 q->length = mh->sample[t].length; 362 if (!mh->sample[t].volume || q->length==1) q->length=0; 363 q->loopstart = mh->sample[t].loopbeg; 364 q->loopend = mh->sample[t].loopend; 365 q->seekpos = mh->sample[t].reserved << 4; 366 367 /* Sanity checks to make sure samples are bounded within the file. */ 368 if(q->length) { 369 if(q->seekpos<samplestart) { 370#ifdef MIKMOD_DEBUG 371 fprintf(stderr,"rejected sample # %d (seekpos=%u < samplestart=%u)\n",t,q->seekpos,samplestart); 372#endif 373 _mm_errno = MMERR_LOADING_SAMPLEINFO; 374 return 0; 375 } 376 /* Some .STMs seem to rely on allowing truncated samples... */ 377 if(q->seekpos>=sampleend) { 378#ifdef MIKMOD_DEBUG 379 fprintf(stderr,"truncating sample # %d from length %u to 0\n",t,q->length); 380#endif 381 q->seekpos = q->length = 0; 382 } else if(q->seekpos+q->length>sampleend) { 383#ifdef MIKMOD_DEBUG 384 fprintf(stderr,"truncating sample # %d from length %u to %u\n",t,q->length,sampleend - q->seekpos); 385#endif 386 q->length = sampleend - q->seekpos; 387 } 388 } 389 else 390 q->seekpos = 0; 391 392 /* contrary to the STM specs, sample data is signed */ 393 q->flags = SF_SIGNED; 394 395 if(q->loopend && q->loopend != 0xffff && q->loopstart < q->length) { 396 q->flags|=SF_LOOP; 397 if (q->loopend > q->length) 398 q->loopend = q->length; 399 } 400 else 401 q->loopstart = q->loopend = 0; 402 } 403 return 1; 404} 405 406static CHAR *STM_LoadTitle(void) 407{ 408 CHAR s[20]; 409 410 _mm_fseek(modreader,0,SEEK_SET); 411 if(!_mm_read_UBYTES(s,20,modreader)) return NULL; 412 413 return(DupStr(s,20,1)); 414} 415 416/*========== Loader information */ 417 418MIKMODAPI MLOADER load_stm={ 419 NULL, 420 "STM", 421 "STM (Scream Tracker)", 422 STM_Init, 423 STM_Test, 424 STM_Load, 425 STM_Cleanup, 426 STM_LoadTitle 427}; 428 429/* ex:set ts=4: */