1 /* MikMod sound library
\r
2 (c) 1998, 1999, 2000, 2001, 2002 Miodrag Vallat and others - see file
\r
3 AUTHORS for complete list.
\r
5 This library is free software; you can redistribute it and/or modify
\r
6 it under the terms of the GNU Library General Public License as
\r
7 published by the Free Software Foundation; either version 2 of
\r
8 the License, or (at your option) any later version.
\r
10 This program is distributed in the hope that it will be useful,
\r
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
13 GNU Library General Public License for more details.
\r
15 You should have received a copy of the GNU Library General Public
\r
16 License along with this library; if not, write to the Free Software
\r
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
\r
21 /*==============================================================================
\r
23 $Id: load_amf.c,v 1.1.1.1 2004/01/21 01:36:35 raph Exp $
\r
25 DMP Advanced Module Format loader
\r
27 ==============================================================================*/
\r
29 #ifdef HAVE_CONFIG_H
\r
33 #ifdef HAVE_UNISTD_H
\r
38 #ifdef HAVE_MEMORY_H
\r
43 #include "mikmod_internals.h"
\r
46 extern int fprintf(FILE *, const char *, ...);
\r
49 /*========== Module structure */
\r
51 typedef struct AMFHEADER {
\r
52 UBYTE id[3]; /* AMF file marker */
\r
53 UBYTE version; /* upper major, lower nibble minor version number */
\r
54 CHAR songname[32]; /* ASCIIZ songname */
\r
55 UBYTE numsamples; /* number of samples saved */
\r
57 UWORD numtracks; /* number of tracks saved */
\r
58 UBYTE numchannels; /* number of channels used */
\r
59 SBYTE panpos[32]; /* voice pan positions */
\r
64 typedef struct AMFSAMPLE {
\r
66 CHAR samplename[32];
\r
76 typedef struct AMFNOTE {
\r
77 UBYTE note,instr,volume,fxcnt;
\r
82 /*========== Loader variables */
\r
84 static AMFHEADER *mh = NULL;
\r
85 #define AMFTEXTLEN 22
\r
86 static CHAR AMF_Version[AMFTEXTLEN+1] = "DSMI Module Format 0.0";
\r
87 static AMFNOTE *track = NULL;
\r
89 /*========== Loader code */
\r
95 if(!_mm_read_UBYTES(id,3,modreader)) return 0;
\r
96 if(memcmp(id,"AMF",3)) return 0;
\r
98 ver=_mm_read_UBYTE(modreader);
\r
99 if((ver>=10)&&(ver<=14)) return 1;
\r
103 BOOL AMF_Init(void)
\r
105 if(!(mh=(AMFHEADER*)_mm_malloc(sizeof(AMFHEADER)))) return 0;
\r
106 if(!(track=(AMFNOTE*)_mm_calloc(64,sizeof(AMFNOTE)))) return 0;
\r
111 void AMF_Cleanup(void)
\r
117 static BOOL AMF_UnpackTrack(MREADER* modreader)
\r
124 memset(track,0,64*sizeof(AMFNOTE));
\r
126 /* read packed track */
\r
128 tracksize=_mm_read_I_UWORD(modreader);
\r
129 tracksize+=((ULONG)_mm_read_UBYTE(modreader))<<16;
\r
131 while(tracksize--) {
\r
132 row=_mm_read_UBYTE(modreader);
\r
133 cmd=_mm_read_UBYTE(modreader);
\r
134 arg=_mm_read_SBYTE(modreader);
\r
135 /* unexpected end of track */
\r
137 if((row==0xff)&&(cmd==0xff)&&(arg==-1))
\r
139 /* the last triplet should be FF FF FF, but this is not
\r
140 always the case... maybe a bug in m2amf ?
\r
146 /* invalid row (probably unexpected end of row) */
\r
151 track[row].note=cmd;
\r
152 track[row].volume=(UBYTE)arg+1;
\r
155 /* duplicate row */
\r
156 if ((arg<0)&&(row+arg>=0)) {
\r
157 memcpy(track+row,track+(row+arg),sizeof(AMFNOTE));
\r
162 track[row].instr=arg+1;
\r
165 /* volume without note */
\r
166 track[row].volume=(UBYTE)arg+1;
\r
169 /* apparently, some M2AMF version fail to estimate the
\r
170 size of the compressed patterns correctly, and end
\r
171 up with blanks, i.e. dead triplets. Those are marked
\r
172 with cmd == 0xff. Let's ignore them. */
\r
174 if(track[row].fxcnt<3) {
\r
175 /* effect, param */
\r
178 track[row].effect[track[row].fxcnt]=cmd&0x7f;
\r
179 track[row].parameter[track[row].fxcnt]=arg;
\r
180 track[row].fxcnt++;
\r
188 static UBYTE* AMF_ConvertTrack(void)
\r
190 int row,fx4memory=0;
\r
192 /* convert track */
\r
194 for (row=0;row<64;row++) {
\r
195 if (track[row].instr) UniInstrument(track[row].instr-1);
\r
196 if (track[row].note>OCTAVE) UniNote(track[row].note-OCTAVE);
\r
199 while(track[row].fxcnt--) {
\r
200 SBYTE inf=track[row].parameter[track[row].fxcnt];
\r
202 switch(track[row].effect[track[row].fxcnt]) {
\r
203 case 1: /* Set speed */
\r
204 UniEffect(UNI_S3MEFFECTA,inf);
\r
206 case 2: /* Volume slide */
\r
208 UniWriteByte(UNI_S3MEFFECTD);
\r
210 UniWriteByte((inf&0xf)<<4);
\r
212 UniWriteByte((-inf)&0xf);
\r
215 /* effect 3, set channel volume, done in UnpackTrack */
\r
216 case 4: /* Porta up/down */
\r
219 UniEffect(UNI_S3MEFFECTE,inf);
\r
220 fx4memory=UNI_S3MEFFECTE;
\r
222 UniEffect(UNI_S3MEFFECTF,-inf);
\r
223 fx4memory=UNI_S3MEFFECTF;
\r
225 } else if(fx4memory)
\r
226 UniEffect(fx4memory,0);
\r
228 /* effect 5, "Porta abs", not supported */
\r
229 case 6: /* Porta to note */
\r
230 UniEffect(UNI_ITEFFECTG,inf);
\r
232 case 7: /* Tremor */
\r
233 UniEffect(UNI_S3MEFFECTI,inf);
\r
235 case 8: /* Arpeggio */
\r
236 UniPTEffect(0x0,inf);
\r
238 case 9: /* Vibrato */
\r
239 UniPTEffect(0x4,inf);
\r
241 case 0xa: /* Porta + Volume slide */
\r
242 UniPTEffect(0x3,0);
\r
244 UniWriteByte(UNI_S3MEFFECTD);
\r
246 UniWriteByte((inf&0xf)<<4);
\r
248 UniWriteByte((-inf)&0xf);
\r
251 case 0xb: /* Vibrato + Volume slide */
\r
252 UniPTEffect(0x4,0);
\r
254 UniWriteByte(UNI_S3MEFFECTD);
\r
256 UniWriteByte((inf&0xf)<<4);
\r
258 UniWriteByte((-inf)&0xf);
\r
261 case 0xc: /* Pattern break (in hex) */
\r
262 UniPTEffect(0xd,inf);
\r
264 case 0xd: /* Pattern jump */
\r
265 UniPTEffect(0xb,inf);
\r
267 /* effect 0xe, "Sync", not supported */
\r
268 case 0xf: /* Retrig */
\r
269 UniEffect(UNI_S3MEFFECTQ,inf&0xf);
\r
271 case 0x10: /* Sample offset */
\r
272 UniPTEffect(0x9,inf);
\r
274 case 0x11: /* Fine volume slide */
\r
276 UniWriteByte(UNI_S3MEFFECTD);
\r
278 UniWriteByte((inf&0xf)<<4|0xf);
\r
280 UniWriteByte(0xf0|((-inf)&0xf));
\r
283 case 0x12: /* Fine portamento */
\r
286 UniEffect(UNI_S3MEFFECTE,0xf0|(inf&0xf));
\r
287 fx4memory=UNI_S3MEFFECTE;
\r
289 UniEffect(UNI_S3MEFFECTF,0xf0|((-inf)&0xf));
\r
290 fx4memory=UNI_S3MEFFECTF;
\r
292 } else if(fx4memory)
\r
293 UniEffect(fx4memory,0);
\r
295 case 0x13: /* Delay note */
\r
296 UniPTEffect(0xe,0xd0|(inf&0xf));
\r
298 case 0x14: /* Note cut */
\r
299 UniPTEffect(0xc,0);
\r
300 track[row].volume=0;
\r
302 case 0x15: /* Set tempo */
\r
303 UniEffect(UNI_S3MEFFECTT,inf);
\r
305 case 0x16: /* Extra fine portamento */
\r
308 UniEffect(UNI_S3MEFFECTE,0xe0|((inf>>2)&0xf));
\r
309 fx4memory=UNI_S3MEFFECTE;
\r
311 UniEffect(UNI_S3MEFFECTF,0xe0|(((-inf)>>2)&0xf));
\r
312 fx4memory=UNI_S3MEFFECTF;
\r
314 } else if(fx4memory)
\r
315 UniEffect(fx4memory,0);
\r
317 case 0x17: /* Panning */
\r
319 UniEffect(UNI_ITEFFECTS0,0x91); /* surround */
\r
321 UniPTEffect(0x8,(inf==64)?255:(inf+64)<<1);
\r
322 of.flags |= UF_PANNING;
\r
327 if (track[row].volume) UniVolEffect(VOL_VOLUME,track[row].volume-1);
\r
333 BOOL AMF_Load(BOOL curious)
\r
335 int t,u,realtrackcnt,realsmpcnt,defaultpanning;
\r
338 UWORD *track_remap;
\r
340 int channel_remap[16];
\r
343 /* try to read module header */
\r
344 _mm_read_UBYTES(mh->id,3,modreader);
\r
345 mh->version =_mm_read_UBYTE(modreader);
\r
346 _mm_read_string(mh->songname,32,modreader);
\r
347 mh->numsamples =_mm_read_UBYTE(modreader);
\r
348 mh->numorders =_mm_read_UBYTE(modreader);
\r
349 mh->numtracks =_mm_read_I_UWORD(modreader);
\r
350 mh->numchannels =_mm_read_UBYTE(modreader);
\r
351 if((!mh->numchannels)||(mh->numchannels>(mh->version>=12?32:16))) {
\r
352 _mm_errno=MMERR_NOT_A_MODULE;
\r
356 if(mh->version>=11) {
\r
357 memset(mh->panpos,0,32);
\r
358 _mm_read_SBYTES(mh->panpos,(mh->version>=13)?32:16,modreader);
\r
360 _mm_read_UBYTES(channel_remap,16,modreader);
\r
362 if (mh->version>=13) {
\r
363 mh->songbpm=_mm_read_UBYTE(modreader);
\r
364 if(mh->songbpm<32) {
\r
365 _mm_errno=MMERR_NOT_A_MODULE;
\r
368 mh->songspd=_mm_read_UBYTE(modreader);
\r
369 if(mh->songspd>32) {
\r
370 _mm_errno=MMERR_NOT_A_MODULE;
\r
378 if(_mm_eof(modreader)) {
\r
379 _mm_errno = MMERR_LOADING_HEADER;
\r
383 /* set module variables */
\r
384 of.initspeed = mh->songspd;
\r
385 of.inittempo = mh->songbpm;
\r
386 AMF_Version[AMFTEXTLEN-3]='0'+(mh->version/10);
\r
387 AMF_Version[AMFTEXTLEN-1]='0'+(mh->version%10);
\r
388 of.modtype = strdup(AMF_Version);
\r
389 of.numchn = mh->numchannels;
\r
390 of.numtrk = mh->numorders*mh->numchannels;
\r
391 if (mh->numtracks>of.numtrk)
\r
392 of.numtrk=mh->numtracks;
\r
393 of.numtrk++; /* add room for extra, empty track */
\r
394 of.songname = DupStr(mh->songname,32,1);
\r
395 of.numpos = mh->numorders;
\r
396 of.numpat = mh->numorders;
\r
398 of.flags |= UF_S3MSLIDES;
\r
399 /* XXX whenever possible, we should try to determine the original format.
\r
400 Here we assume it was S3M-style wrt bpmlimit... */
\r
404 * Play with the panning table. Although the AMF format embeds a
\r
405 * panning table, if the module was a MOD or an S3M with default
\r
406 * panning and didn't use any panning commands, don't flag
\r
407 * UF_PANNING, to use our preferred panning table for this case.
\r
409 defaultpanning = 1;
\r
410 for (t = 0; t < 32; t++) {
\r
411 if (mh->panpos[t] > 64) {
\r
412 of.panning[t] = PAN_SURROUND;
\r
413 defaultpanning = 0;
\r
415 if (mh->panpos[t] == 64)
\r
416 of.panning[t] = PAN_RIGHT;
\r
418 of.panning[t] = (mh->panpos[t] + 64) << 1;
\r
420 if (defaultpanning) {
\r
421 for (t = 0; t < of.numchn; t++)
\r
422 if (of.panning[t] == (((t + 1) & 2) ? PAN_RIGHT : PAN_LEFT)) {
\r
423 defaultpanning = 0; /* not MOD canonical panning */
\r
427 if (defaultpanning)
\r
428 of.flags |= UF_PANNING;
\r
430 of.numins=of.numsmp=mh->numsamples;
\r
432 if(!AllocPositions(of.numpos)) return 0;
\r
433 for(t=0;t<of.numpos;t++)
\r
436 if(!AllocTracks()) return 0;
\r
437 if(!AllocPatterns()) return 0;
\r
439 /* read AMF order table */
\r
440 for (t=0;t<of.numpat;t++) {
\r
441 if (mh->version>=14)
\r
443 of.pattrows[t]=_mm_read_I_UWORD(modreader);
\r
444 if (mh->version>=10)
\r
445 _mm_read_I_UWORDS(of.patterns+(t*of.numchn),of.numchn,modreader);
\r
447 for(u=0;u<of.numchn;u++)
\r
448 of.patterns[t*of.numchn+channel_remap[u]]=_mm_read_I_UWORD(modreader);
\r
450 if(_mm_eof(modreader)) {
\r
451 _mm_errno = MMERR_LOADING_HEADER;
\r
455 /* read sample information */
\r
456 if(!AllocSamples()) return 0;
\r
458 for(t=0;t<of.numins;t++) {
\r
459 /* try to read sample info */
\r
460 s.type=_mm_read_UBYTE(modreader);
\r
461 _mm_read_string(s.samplename,32,modreader);
\r
462 _mm_read_string(s.filename,13,modreader);
\r
463 s.offset =_mm_read_I_ULONG(modreader);
\r
464 s.length =_mm_read_I_ULONG(modreader);
\r
465 s.c2spd =_mm_read_I_UWORD(modreader);
\r
466 if(s.c2spd==8368) s.c2spd=8363;
\r
467 s.volume =_mm_read_UBYTE(modreader);
\r
468 if(mh->version>=11) {
\r
469 s.reppos =_mm_read_I_ULONG(modreader);
\r
470 s.repend =_mm_read_I_ULONG(modreader);
\r
472 s.reppos =_mm_read_I_UWORD(modreader);
\r
473 s.repend =s.length;
\r
476 if(_mm_eof(modreader)) {
\r
477 _mm_errno = MMERR_LOADING_SAMPLEINFO;
\r
481 q->samplename = DupStr(s.samplename,32,1);
\r
482 q->speed = s.c2spd;
\r
483 q->volume = s.volume;
\r
485 q->seekpos = s.offset;
\r
486 q->length = s.length;
\r
487 q->loopstart = s.reppos;
\r
488 q->loopend = s.repend;
\r
489 if((s.repend-s.reppos)>2) q->flags |= SF_LOOP;
\r
494 /* read track table */
\r
495 if(!(track_remap=_mm_calloc(mh->numtracks+1,sizeof(UWORD))))
\r
497 _mm_read_I_UWORDS(track_remap+1,mh->numtracks,modreader);
\r
498 if(_mm_eof(modreader)) {
\r
500 _mm_errno=MMERR_LOADING_TRACK;
\r
504 for(realtrackcnt=t=0;t<=mh->numtracks;t++)
\r
505 if (realtrackcnt<track_remap[t])
\r
506 realtrackcnt=track_remap[t];
\r
507 for(t=0;t<of.numpat*of.numchn;t++)
\r
508 of.patterns[t]=(of.patterns[t]<=mh->numtracks)?
\r
509 track_remap[of.patterns[t]]-1:realtrackcnt;
\r
513 /* unpack tracks */
\r
514 for(t=0;t<realtrackcnt;t++) {
\r
515 if(_mm_eof(modreader)) {
\r
516 _mm_errno = MMERR_LOADING_TRACK;
\r
519 if (!AMF_UnpackTrack(modreader)) {
\r
520 _mm_errno = MMERR_LOADING_TRACK;
\r
523 if(!(of.tracks[t]=AMF_ConvertTrack()))
\r
526 /* add an extra void track */
\r
528 for(t=0;t<64;t++) UniNewline();
\r
529 of.tracks[realtrackcnt++]=UniDup();
\r
530 for(t=realtrackcnt;t<of.numtrk;t++) of.tracks[t]=NULL;
\r
532 /* compute sample offsets */
\r
533 samplepos=_mm_ftell(modreader);
\r
534 for(realsmpcnt=t=0;t<of.numsmp;t++)
\r
535 if(realsmpcnt<of.samples[t].seekpos)
\r
536 realsmpcnt=of.samples[t].seekpos;
\r
537 for(t=1;t<=realsmpcnt;t++) {
\r
539 while(q->seekpos!=t) q++;
\r
540 q->seekpos=samplepos;
\r
541 samplepos+=q->length;
\r
547 CHAR *AMF_LoadTitle(void)
\r
551 _mm_fseek(modreader,4,SEEK_SET);
\r
552 if(!_mm_read_UBYTES(s,32,modreader)) return NULL;
\r
554 return(DupStr(s,32,1));
\r
557 /*========== Loader information */
\r
559 MIKMODAPI MLOADER load_amf={
\r
562 "AMF (DSMI Advanced Module Format)",
\r