A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita audio rust zig deno mpris rockbox mpd
at master 361 lines 8.5 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 Ultratracker (ULT) module loader 24 25==============================================================================*/ 26 27#ifdef HAVE_CONFIG_H 28#include "config.h" 29#endif 30 31#ifdef HAVE_UNISTD_H 32#include <unistd.h> 33#endif 34 35#include <stdio.h> 36#ifdef HAVE_MEMORY_H 37#include <memory.h> 38#endif 39#include <string.h> 40 41#include "mikmod_internals.h" 42 43#ifdef SUNOS 44extern int fprintf(FILE *, const char *, ...); 45#endif 46 47/*========== Module structure */ 48 49/* header */ 50typedef struct ULTHEADER { 51 CHAR id[16]; 52 CHAR songtitle[32]; 53 UBYTE reserved; 54} ULTHEADER; 55 56/* sample information */ 57typedef struct ULTSAMPLE { 58 CHAR samplename[32]; 59 CHAR dosname[12]; 60 SLONG loopstart; 61 SLONG loopend; 62 SLONG sizestart; 63 SLONG sizeend; 64 UBYTE volume; 65 UBYTE flags; 66 UWORD speed; 67 SWORD finetune; 68} ULTSAMPLE; 69 70typedef struct ULTEVENT { 71 UBYTE note,sample,eff,dat1,dat2; 72} ULTEVENT; 73 74/*========== Loader variables */ 75 76#define ULTS_16BITS 4 77#define ULTS_LOOP 8 78#define ULTS_REVERSE 16 79 80#define ULT_VERSION_LEN 18 81static CHAR ULT_Version[ULT_VERSION_LEN+1]="Ultra Tracker v1.x"; 82 83static ULTEVENT ev; 84 85/*========== Loader code */ 86 87static int ULT_Test(void) 88{ 89 CHAR id[16]; 90 91 if(!_mm_read_string(id,15,modreader)) return 0; 92 if(strncmp(id,"MAS_UTrack_V00",14)) return 0; 93 if((id[14]<'1')||(id[14]>'4')) return 0; 94 return 1; 95} 96 97static int ULT_Init(void) 98{ 99 return 1; 100} 101 102static void ULT_Cleanup(void) 103{ 104} 105 106static UBYTE ReadUltEvent(ULTEVENT* event) 107{ 108 UBYTE flag,rep=1; 109 110 flag = _mm_read_UBYTE(modreader); 111 if(flag==0xfc) { 112 rep = _mm_read_UBYTE(modreader); 113 event->note =_mm_read_UBYTE(modreader); 114 } else 115 event->note = flag; 116 117 event->sample =_mm_read_UBYTE(modreader); 118 event->eff =_mm_read_UBYTE(modreader); 119 event->dat1 =_mm_read_UBYTE(modreader); 120 event->dat2 =_mm_read_UBYTE(modreader); 121 122 return rep; 123} 124 125static int ULT_Load(int curious) 126{ 127 int t,u,tracks=0; 128 SAMPLE *q; 129 ULTSAMPLE s; 130 ULTHEADER mh; 131 UBYTE nos,noc,RBnop; 132 (void)curious; 133 134 /* try to read module header */ 135 _mm_read_string(mh.id,15,modreader); 136 _mm_read_string(mh.songtitle,32,modreader); 137 mh.reserved=_mm_read_UBYTE(modreader); 138 139 if(_mm_eof(modreader)) { 140 _mm_errno = MMERR_LOADING_HEADER; 141 return 0; 142 } 143 144 ULT_Version[ULT_VERSION_LEN-1]='3'+(mh.id[14]-'1'); 145 of.modtype = DupStr(ULT_Version,ULT_VERSION_LEN,1); 146 of.initspeed = 6; 147 of.inittempo = 125; 148 of.reppos = 0; 149 150 /* read songtext */ 151 if ((mh.id[14]>'1')&&(mh.reserved)) 152 if(!ReadLinedComment(mh.reserved * 32, 32)) return 0; 153 154 nos=_mm_read_UBYTE(modreader); 155 if(_mm_eof(modreader)) { 156 _mm_errno = MMERR_LOADING_HEADER; 157 return 0; 158 } 159 160 of.songname=DupStr(mh.songtitle,32,1); 161 of.numins=of.numsmp=nos; 162 163 if(!AllocSamples()) return 0; 164 q = of.samples; 165 for(t=0;t<nos;t++) { 166 /* try to read sample info */ 167 _mm_read_string(s.samplename,32,modreader); 168 _mm_read_string(s.dosname,12,modreader); 169 s.loopstart =_mm_read_I_ULONG(modreader); 170 s.loopend =_mm_read_I_ULONG(modreader); 171 s.sizestart =_mm_read_I_ULONG(modreader); 172 s.sizeend =_mm_read_I_ULONG(modreader); 173 s.volume =_mm_read_UBYTE(modreader); 174 s.flags =_mm_read_UBYTE(modreader); 175 s.speed =(mh.id[14]>='4')?_mm_read_I_UWORD(modreader):8363; 176 s.finetune =_mm_read_I_SWORD(modreader); 177 178 if(_mm_eof(modreader)) { 179 _mm_errno = MMERR_LOADING_SAMPLEINFO; 180 return 0; 181 } 182 183 q->samplename=DupStr(s.samplename,32,1); 184 /* The correct formula would be 185 s.speed * pow(2, (double)s.finetune / (OCTAVE * 32768)) 186 but to avoid libm, we'll use a first order approximation. 187 1/567290 == Ln(2)/OCTAVE/32768 */ 188 if(!s.finetune) q->speed = s.speed; 189 else q->speed= s.speed*((double)s.finetune/567290.0 + 1.0); 190 q->length = s.sizeend-s.sizestart; 191 q->volume = s.volume>>2; 192 q->loopstart = s.loopstart; 193 q->loopend = s.loopend; 194 q->flags = SF_SIGNED; 195 if(s.flags&ULTS_LOOP) q->flags|=SF_LOOP; 196 else q->loopstart = q->loopend = 0; 197 if(s.flags&ULTS_16BITS) { 198 s.sizeend+=(s.sizeend-s.sizestart); 199 s.sizestart<<=1; 200 q->flags|=SF_16BITS; 201 q->loopstart>>=1; 202 q->loopend>>=1; 203 } 204 q++; 205 } 206 207 if(!AllocPositions(256)) return 0; 208 for(t=0;t<256;t++) 209 of.positions[t]=_mm_read_UBYTE(modreader); 210 211 noc=_mm_read_UBYTE(modreader); 212 RBnop=_mm_read_UBYTE(modreader); 213 214 of.numchn=++noc; 215 of.numpat=++RBnop; 216 of.numtrk=of.numchn*of.numpat; 217 218 for(t=0;t<256;t++) { 219 if(of.positions[t]==255) { 220 of.positions[t]=LAST_PATTERN; 221 break; 222 } 223 if (of.positions[t]>of.numpat) { /* SANITIY CHECK */ 224 /* fprintf(stderr,"positions[%d]=%d > numpat=%d\n",t,of.positions[t],of.numpat);*/ 225 _mm_errno = MMERR_LOADING_HEADER; 226 return 0; 227 } 228 } 229 of.numpos=t; 230 231 if(!AllocTracks()) return 0; 232 if(!AllocPatterns()) return 0; 233 for(u=0;u<of.numchn;u++) 234 for(t=0;t<of.numpat;t++) 235 of.patterns[(t*of.numchn)+u]=tracks++; 236 237 /* Secunia SA37775 / CVE-2009-3996 */ 238 if (of.numchn>=UF_MAXCHAN) 239 of.numchn=UF_MAXCHAN - 1; 240 241 /* read pan position table for v1.5 and higher */ 242 if(mh.id[14]>='3') { 243 for(t=0;t<of.numchn;t++) of.panning[t]=_mm_read_UBYTE(modreader)<<4; 244 of.flags |= UF_PANNING; 245 } 246 247 for(t=0;t<of.numtrk;t++) { 248 int rep,row=0; 249 /* FIXME: unrolling continuous portamento is a HACK and needs to 250 * be replaced with a real continuous effect. This implementation 251 * breaks when tone portamento continues between patterns. See 252 * discussion in https://github.com/sezero/mikmod/pull/40 . */ 253 int continuePortaToNote = 0; 254 255 UniReset(); 256 while(row<64) { 257 rep=ReadUltEvent(&ev); 258 259 if(_mm_eof(modreader)) { 260 _mm_errno = MMERR_LOADING_TRACK; 261 return 0; 262 } 263 264 while(rep--) { 265 UBYTE eff; 266 int offset; 267 268 if(ev.sample) UniInstrument(ev.sample-1); 269 if(ev.note) { 270 UniNote(ev.note+2*OCTAVE-1); 271 continuePortaToNote = 0; 272 } 273 274 /* first effect - various fixes by Alexander Kerkhove and 275 Thomas Neumann */ 276 eff = ev.eff>>4; 277 278 if (continuePortaToNote && (eff != 0x3) && ((ev.eff & 0xf) != 0x3)) 279 UniEffect(UNI_ITEFFECTG, 0); 280 281 switch(eff) { 282 case 0x3: /* tone portamento */ 283 UniEffect(UNI_ITEFFECTG,ev.dat2); 284 continuePortaToNote = 1; 285 break; 286 case 0x5: 287 break; 288 case 0x9: /* sample offset */ 289 offset=(ev.dat2<<8)|((ev.eff&0xf)==9?ev.dat1:0); 290 UniEffect(UNI_ULTEFFECT9,offset); 291 break; 292 case 0xb: /* panning */ 293 UniPTEffect(8,ev.dat2*0xf); 294 of.flags |= UF_PANNING; 295 break; 296 case 0xc: /* volume */ 297 UniPTEffect(eff,ev.dat2>>2); 298 break; 299 default: 300 UniPTEffect(eff,ev.dat2); 301 break; 302 } 303 304 /* second effect */ 305 eff=ev.eff&0xf; 306 switch(eff) { 307 case 0x3: /* tone portamento */ 308 UniEffect(UNI_ITEFFECTG,ev.dat1); 309 continuePortaToNote = 1; 310 break; 311 case 0x5: 312 break; 313 case 0x9: /* sample offset */ 314 if((ev.eff>>4)!=9) 315 UniEffect(UNI_ULTEFFECT9,((UWORD)ev.dat1)<<8); 316 break; 317 case 0xb: /* panning */ 318 UniPTEffect(8,ev.dat1*0xf); 319 of.flags |= UF_PANNING; 320 break; 321 case 0xc: /* volume */ 322 UniPTEffect(eff,ev.dat1>>2); 323 break; 324 default: 325 UniPTEffect(eff,ev.dat1); 326 break; 327 } 328 329 UniNewline(); 330 row++; 331 } 332 } 333 if(!(of.tracks[t]=UniDup())) return 0; 334 } 335 return 1; 336} 337 338static CHAR * ULT_LoadTitle(void) 339{ 340 CHAR s[32]; 341 342 _mm_fseek(modreader,15,SEEK_SET); 343 if(!_mm_read_UBYTES(s,32,modreader)) return NULL; 344 345 return(DupStr(s,32,1)); 346} 347 348/*========== Loader information */ 349 350MIKMODAPI MLOADER load_ult={ 351 NULL, 352 "ULT", 353 "ULT (UltraTracker)", 354 ULT_Init, 355 ULT_Test, 356 ULT_Load, 357 ULT_Cleanup, 358 ULT_LoadTitle 359}; 360 361/* ex:set ts=4: */