Add "MikMod for Rockbox 0.1" from 2007-06-29
[mikmod-rockbox.git] / apps / plugins / mikmod / loaders / load_amf.c
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
4 \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
9  \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
14  \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
18         02111-1307, USA.\r
19 */\r
20 \r
21 /*==============================================================================\r
22 \r
23   $Id: load_amf.c,v 1.1.1.1 2004/01/21 01:36:35 raph Exp $\r
24 \r
25   DMP Advanced Module Format loader\r
26 \r
27 ==============================================================================*/\r
28 \r
29 #ifdef HAVE_CONFIG_H\r
30 #include "config.h"\r
31 #endif\r
32 \r
33 #ifdef HAVE_UNISTD_H\r
34 #include <unistd.h>\r
35 #endif\r
36 \r
37 #include <stdio.h>\r
38 #ifdef HAVE_MEMORY_H\r
39 #include <memory.h>\r
40 #endif\r
41 #include <string.h>\r
42 \r
43 #include "mikmod_internals.h"\r
44 \r
45 #ifdef SUNOS\r
46 extern int fprintf(FILE *, const char *, ...);\r
47 #endif\r
48 \r
49 /*========== Module structure */\r
50 \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
56         UBYTE numorders;\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
60         UBYTE songbpm;\r
61         UBYTE songspd;\r
62 } AMFHEADER;\r
63 \r
64 typedef struct AMFSAMPLE {\r
65         UBYTE type;\r
66         CHAR  samplename[32];\r
67         CHAR  filename[13];\r
68         ULONG offset;\r
69         ULONG length;\r
70         UWORD c2spd;\r
71         UBYTE volume;\r
72         ULONG reppos;\r
73         ULONG repend;\r
74 } AMFSAMPLE;\r
75 \r
76 typedef struct AMFNOTE {\r
77         UBYTE note,instr,volume,fxcnt;\r
78         UBYTE effect[3];\r
79         SBYTE parameter[3];\r
80 } AMFNOTE;\r
81 \r
82 /*========== Loader variables */\r
83 \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
88 \r
89 /*========== Loader code */\r
90 \r
91 BOOL AMF_Test(void)\r
92 {\r
93         UBYTE id[3],ver;\r
94 \r
95         if(!_mm_read_UBYTES(id,3,modreader)) return 0;\r
96         if(memcmp(id,"AMF",3)) return 0;\r
97 \r
98         ver=_mm_read_UBYTE(modreader);\r
99         if((ver>=10)&&(ver<=14)) return 1;\r
100         return 0;\r
101 }\r
102 \r
103 BOOL AMF_Init(void)\r
104 {\r
105         if(!(mh=(AMFHEADER*)_mm_malloc(sizeof(AMFHEADER)))) return 0;\r
106         if(!(track=(AMFNOTE*)_mm_calloc(64,sizeof(AMFNOTE)))) return 0;\r
107 \r
108         return 1;\r
109 }\r
110 \r
111 void AMF_Cleanup(void)\r
112 {\r
113         _mm_free(mh);\r
114         _mm_free(track);\r
115 }\r
116 \r
117 static BOOL AMF_UnpackTrack(MREADER* modreader)\r
118 {\r
119         ULONG tracksize;\r
120         UBYTE row,cmd;\r
121         SBYTE arg;\r
122 \r
123         /* empty track */\r
124         memset(track,0,64*sizeof(AMFNOTE));\r
125 \r
126         /* read packed track */\r
127         if (modreader) {\r
128                 tracksize=_mm_read_I_UWORD(modreader);\r
129                 tracksize+=((ULONG)_mm_read_UBYTE(modreader))<<16;\r
130                 if (tracksize)\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
136                                 if(!tracksize) {\r
137                                         if((row==0xff)&&(cmd==0xff)&&(arg==-1))\r
138                                                 break;\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
141                                         else\r
142                                                 return 0;\r
143                                         */\r
144 \r
145                                 }\r
146                                 /* invalid row (probably unexpected end of row) */\r
147                                 if (row>=64)\r
148                                         return 0;\r
149                                 if (cmd<0x7f) {\r
150                                         /* note, vol */\r
151                                         track[row].note=cmd;\r
152                                         track[row].volume=(UBYTE)arg+1;\r
153                                 } else\r
154                                   if (cmd==0x7f) {\r
155                                         /* duplicate row */\r
156                                         if ((arg<0)&&(row+arg>=0)) {\r
157                                                 memcpy(track+row,track+(row+arg),sizeof(AMFNOTE));\r
158                                         }\r
159                                 } else\r
160                                   if (cmd==0x80) {\r
161                                         /* instr */\r
162                                         track[row].instr=arg+1;\r
163                                 } else\r
164                                   if (cmd==0x83) {\r
165                                         /* volume without note */\r
166                                         track[row].volume=(UBYTE)arg+1;\r
167                                 } else \r
168                                   if (cmd==0xff) {\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
173                                 } else\r
174                                   if(track[row].fxcnt<3) {\r
175                                         /* effect, param */\r
176                                         if(cmd>0x97)\r
177                                                 return 0;\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
181                                 } else\r
182                                         return 0;\r
183                         }\r
184         }\r
185         return 1;\r
186 }\r
187 \r
188 static UBYTE* AMF_ConvertTrack(void)\r
189 {\r
190         int row,fx4memory=0;\r
191 \r
192         /* convert track */\r
193         UniReset();\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
197 \r
198                 /* AMF effects */\r
199                 while(track[row].fxcnt--) {\r
200                         SBYTE inf=track[row].parameter[track[row].fxcnt];\r
201 \r
202                         switch(track[row].effect[track[row].fxcnt]) {\r
203                                 case 1: /* Set speed */\r
204                                         UniEffect(UNI_S3MEFFECTA,inf);\r
205                                         break;\r
206                                 case 2: /* Volume slide */\r
207                                         if(inf) {\r
208                                                 UniWriteByte(UNI_S3MEFFECTD);\r
209                                                 if (inf>=0)\r
210                                                         UniWriteByte((inf&0xf)<<4);\r
211                                                 else\r
212                                                         UniWriteByte((-inf)&0xf);\r
213                                         }\r
214                                         break;\r
215                                 /* effect 3, set channel volume, done in UnpackTrack */\r
216                                 case 4: /* Porta up/down */\r
217                                         if(inf) {\r
218                                                 if(inf>0) {\r
219                                                         UniEffect(UNI_S3MEFFECTE,inf);\r
220                                                         fx4memory=UNI_S3MEFFECTE;\r
221                                                 } else {\r
222                                                         UniEffect(UNI_S3MEFFECTF,-inf);\r
223                                                         fx4memory=UNI_S3MEFFECTF;\r
224                                                 }\r
225                                         } else if(fx4memory)\r
226                                                 UniEffect(fx4memory,0);\r
227                                         break;\r
228                                 /* effect 5, "Porta abs", not supported */\r
229                                 case 6: /* Porta to note */\r
230                                         UniEffect(UNI_ITEFFECTG,inf);\r
231                                         break;\r
232                                 case 7: /* Tremor */\r
233                                         UniEffect(UNI_S3MEFFECTI,inf);\r
234                                         break;\r
235                                 case 8: /* Arpeggio */\r
236                                         UniPTEffect(0x0,inf);\r
237                                         break;\r
238                                 case 9: /* Vibrato */\r
239                                         UniPTEffect(0x4,inf);\r
240                                         break;\r
241                                 case 0xa: /* Porta + Volume slide */\r
242                                         UniPTEffect(0x3,0);\r
243                                         if(inf) {\r
244                                                 UniWriteByte(UNI_S3MEFFECTD);\r
245                                                 if (inf>=0)\r
246                                                         UniWriteByte((inf&0xf)<<4);\r
247                                                 else\r
248                                                         UniWriteByte((-inf)&0xf);\r
249                                         }\r
250                                         break;\r
251                                 case 0xb: /* Vibrato + Volume slide */\r
252                                         UniPTEffect(0x4,0);\r
253                                         if(inf) {\r
254                                                 UniWriteByte(UNI_S3MEFFECTD);\r
255                                                 if (inf>=0)\r
256                                                         UniWriteByte((inf&0xf)<<4);\r
257                                                 else\r
258                                                         UniWriteByte((-inf)&0xf);\r
259                                         }\r
260                                         break;\r
261                                 case 0xc: /* Pattern break (in hex) */\r
262                                         UniPTEffect(0xd,inf);\r
263                                         break;\r
264                                 case 0xd: /* Pattern jump */\r
265                                         UniPTEffect(0xb,inf);\r
266                                         break;\r
267                                 /* effect 0xe, "Sync", not supported */\r
268                                 case 0xf: /* Retrig */\r
269                                         UniEffect(UNI_S3MEFFECTQ,inf&0xf);\r
270                                         break;\r
271                                 case 0x10: /* Sample offset */\r
272                                         UniPTEffect(0x9,inf);\r
273                                         break;\r
274                                 case 0x11: /* Fine volume slide */\r
275                                         if(inf) {\r
276                                                 UniWriteByte(UNI_S3MEFFECTD);\r
277                                                 if (inf>=0)\r
278                                                         UniWriteByte((inf&0xf)<<4|0xf);\r
279                                                 else\r
280                                                         UniWriteByte(0xf0|((-inf)&0xf));\r
281                                         }\r
282                                         break;\r
283                                 case 0x12: /* Fine portamento */\r
284                                         if(inf) {\r
285                                                 if(inf>0) {\r
286                                                         UniEffect(UNI_S3MEFFECTE,0xf0|(inf&0xf));\r
287                                                         fx4memory=UNI_S3MEFFECTE;\r
288                                                 } else {\r
289                                                         UniEffect(UNI_S3MEFFECTF,0xf0|((-inf)&0xf));\r
290                                                         fx4memory=UNI_S3MEFFECTF;\r
291                                                 }\r
292                                         } else if(fx4memory)\r
293                                                 UniEffect(fx4memory,0);\r
294                                         break;\r
295                                 case 0x13: /* Delay note */\r
296                                         UniPTEffect(0xe,0xd0|(inf&0xf));\r
297                                         break;\r
298                                 case 0x14: /* Note cut */\r
299                                         UniPTEffect(0xc,0);\r
300                                         track[row].volume=0;\r
301                                         break;\r
302                                 case 0x15: /* Set tempo */\r
303                                         UniEffect(UNI_S3MEFFECTT,inf);\r
304                                         break;\r
305                                 case 0x16: /* Extra fine portamento */\r
306                                         if(inf) {\r
307                                                 if(inf>0) {\r
308                                                         UniEffect(UNI_S3MEFFECTE,0xe0|((inf>>2)&0xf));\r
309                                                         fx4memory=UNI_S3MEFFECTE;\r
310                                                 } else {\r
311                                                         UniEffect(UNI_S3MEFFECTF,0xe0|(((-inf)>>2)&0xf));\r
312                                                         fx4memory=UNI_S3MEFFECTF;\r
313                                                 }\r
314                                         } else if(fx4memory)\r
315                                                 UniEffect(fx4memory,0);\r
316                                         break;\r
317                                 case 0x17: /* Panning */\r
318                                         if (inf>64)\r
319                                                 UniEffect(UNI_ITEFFECTS0,0x91); /* surround */\r
320                                         else\r
321                                                 UniPTEffect(0x8,(inf==64)?255:(inf+64)<<1);\r
322                                         of.flags |= UF_PANNING;\r
323                                         break;\r
324                         }\r
325                         \r
326                 }\r
327                 if (track[row].volume) UniVolEffect(VOL_VOLUME,track[row].volume-1);\r
328                 UniNewline();\r
329         }\r
330         return UniDup();\r
331 }\r
332 \r
333 BOOL AMF_Load(BOOL curious)\r
334 {\r
335         int t,u,realtrackcnt,realsmpcnt,defaultpanning;\r
336         AMFSAMPLE s;\r
337         SAMPLE *q;\r
338         UWORD *track_remap;\r
339         ULONG samplepos;\r
340         int channel_remap[16];\r
341         (void)curious;\r
342 \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
353                 return 0;\r
354         }\r
355 \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
359         } else\r
360                 _mm_read_UBYTES(channel_remap,16,modreader);\r
361 \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
366                         return 0;\r
367                 }\r
368                 mh->songspd=_mm_read_UBYTE(modreader);\r
369                 if(mh->songspd>32) {\r
370                         _mm_errno=MMERR_NOT_A_MODULE;\r
371                         return 0;\r
372                 }\r
373         } else {\r
374                 mh->songbpm=125;\r
375                 mh->songspd=6;\r
376         }\r
377 \r
378         if(_mm_eof(modreader)) {\r
379                 _mm_errno = MMERR_LOADING_HEADER;\r
380                 return 0;\r
381         }\r
382 \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
397         of.reppos    = 0;\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
401         of.bpmlimit = 32;\r
402         \r
403         /*\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
408          */\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
414                 } else\r
415                         if (mh->panpos[t] == 64)\r
416                                 of.panning[t] = PAN_RIGHT;\r
417                         else\r
418                                 of.panning[t] = (mh->panpos[t] + 64) << 1;\r
419         }\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
424                                 break;\r
425                         }\r
426         }\r
427         if (defaultpanning)\r
428                 of.flags |= UF_PANNING;\r
429 \r
430         of.numins=of.numsmp=mh->numsamples;\r
431 \r
432         if(!AllocPositions(of.numpos)) return 0;\r
433         for(t=0;t<of.numpos;t++)\r
434                 of.positions[t]=t;\r
435 \r
436         if(!AllocTracks()) return 0;\r
437         if(!AllocPatterns()) return 0;\r
438 \r
439         /* read AMF order table */\r
440         for (t=0;t<of.numpat;t++) {\r
441                 if (mh->version>=14)\r
442                         /* track size */\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
446                 else\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
449         }\r
450         if(_mm_eof(modreader)) {\r
451                 _mm_errno = MMERR_LOADING_HEADER;\r
452                 return 0;\r
453         }\r
454 \r
455         /* read sample information */\r
456         if(!AllocSamples()) return 0;\r
457         q=of.samples;\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
471                 } else {\r
472                         s.reppos    =_mm_read_I_UWORD(modreader);\r
473                         s.repend    =s.length;\r
474                 }\r
475 \r
476                 if(_mm_eof(modreader)) {\r
477                         _mm_errno = MMERR_LOADING_SAMPLEINFO; \r
478                         return 0;\r
479                 }\r
480 \r
481                 q->samplename = DupStr(s.samplename,32,1);\r
482                 q->speed     = s.c2spd;\r
483                 q->volume    = s.volume;\r
484                 if (s.type) {\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
490                 }\r
491                 q++;\r
492         }\r
493 \r
494         /* read track table */\r
495         if(!(track_remap=_mm_calloc(mh->numtracks+1,sizeof(UWORD))))\r
496                 return 0;\r
497         _mm_read_I_UWORDS(track_remap+1,mh->numtracks,modreader);\r
498         if(_mm_eof(modreader)) {\r
499                 free(track_remap);\r
500                 _mm_errno=MMERR_LOADING_TRACK;\r
501                 return 0;\r
502         }\r
503 \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
510 \r
511         free(track_remap);\r
512 \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
517                         return 0;\r
518                 }\r
519                 if (!AMF_UnpackTrack(modreader)) {\r
520                         _mm_errno = MMERR_LOADING_TRACK;\r
521                         return 0;\r
522                 }\r
523                 if(!(of.tracks[t]=AMF_ConvertTrack()))\r
524                         return 0;\r
525         }\r
526         /* add an extra void track */\r
527         UniReset();\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
531 \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
538                 q=of.samples;\r
539                 while(q->seekpos!=t) q++;\r
540                 q->seekpos=samplepos;\r
541                 samplepos+=q->length;\r
542         }\r
543                 \r
544         return 1;\r
545 }\r
546 \r
547 CHAR *AMF_LoadTitle(void)\r
548 {\r
549         CHAR s[32];\r
550 \r
551         _mm_fseek(modreader,4,SEEK_SET);\r
552         if(!_mm_read_UBYTES(s,32,modreader)) return NULL;\r
553 \r
554         return(DupStr(s,32,1));\r
555 }\r
556 \r
557 /*========== Loader information */\r
558 \r
559 MIKMODAPI MLOADER load_amf={\r
560         NULL,\r
561         "AMF",\r
562         "AMF (DSMI Advanced Module Format)",\r
563         AMF_Init,\r
564         AMF_Test,\r
565         AMF_Load,\r
566         AMF_Cleanup,\r
567         AMF_LoadTitle\r
568 };\r
569 \r
570 /* ex:set ts=4: */\r