Mercurial > audlegacy-plugins
comparison src/modplug/load_amf.cxx @ 136:6b5a52635b3b trunk
[svn] - like with so many other things, modplug is now maintained by us.
author | nenolod |
---|---|
date | Sun, 29 Oct 2006 01:04:52 -0700 |
parents | |
children | 032053ca08ab 3673c7ec4ea2 |
comparison
equal
deleted
inserted
replaced
135:33d24bd94ccc | 136:6b5a52635b3b |
---|---|
1 /* | |
2 * This program is free software; you can redistribute it and modify it | |
3 * under the terms of the GNU General Public License as published by the | |
4 * Free Software Foundation; either version 2 of the license or (at your | |
5 * option) any later version. | |
6 * | |
7 * Authors: Olivier Lapicque <olivierl@jps.net> | |
8 */ | |
9 | |
10 /////////////////////////////////////////////////// | |
11 // | |
12 // AMF module loader | |
13 // | |
14 // There is 2 types of AMF files: | |
15 // - ASYLUM Music Format | |
16 // - Advanced Music Format(DSM) | |
17 // | |
18 /////////////////////////////////////////////////// | |
19 #include "stdafx.h" | |
20 #include "sndfile.h" | |
21 | |
22 //#define AMFLOG | |
23 | |
24 //#pragma warning(disable:4244) | |
25 | |
26 #pragma pack(1) | |
27 | |
28 typedef struct _AMFFILEHEADER | |
29 { | |
30 UCHAR szAMF[3]; | |
31 UCHAR version; | |
32 CHAR title[32]; | |
33 UCHAR numsamples; | |
34 UCHAR numorders; | |
35 USHORT numtracks; | |
36 UCHAR numchannels; | |
37 } AMFFILEHEADER; | |
38 | |
39 typedef struct _AMFSAMPLE | |
40 { | |
41 UCHAR type; | |
42 CHAR samplename[32]; | |
43 CHAR filename[13]; | |
44 ULONG offset; | |
45 ULONG length; | |
46 USHORT c2spd; | |
47 UCHAR volume; | |
48 } AMFSAMPLE; | |
49 | |
50 | |
51 #pragma pack() | |
52 | |
53 | |
54 #ifdef AMFLOG | |
55 extern void Log(LPCSTR, ...); | |
56 #endif | |
57 | |
58 VOID AMF_Unpack(MODCOMMAND *pPat, const BYTE *pTrack, UINT nRows, UINT nChannels) | |
59 //------------------------------------------------------------------------------- | |
60 { | |
61 UINT lastinstr = 0; | |
62 UINT nTrkSize = bswapLE16(*(USHORT *)pTrack); | |
63 nTrkSize += (UINT)pTrack[2] <<16; | |
64 pTrack += 3; | |
65 while (nTrkSize--) | |
66 { | |
67 UINT row = pTrack[0]; | |
68 UINT cmd = pTrack[1]; | |
69 UINT arg = pTrack[2]; | |
70 if (row >= nRows) break; | |
71 MODCOMMAND *m = pPat + row * nChannels; | |
72 if (cmd < 0x7F) // note+vol | |
73 { | |
74 m->note = cmd+1; | |
75 if (!m->instr) m->instr = lastinstr; | |
76 m->volcmd = VOLCMD_VOLUME; | |
77 m->vol = arg; | |
78 } else | |
79 if (cmd == 0x7F) // duplicate row | |
80 { | |
81 signed char rdelta = (signed char)arg; | |
82 int rowsrc = (int)row + (int)rdelta; | |
83 if ((rowsrc >= 0) && (rowsrc < (int)nRows)) memcpy(m, &pPat[rowsrc*nChannels],sizeof(pPat[rowsrc*nChannels])); | |
84 } else | |
85 if (cmd == 0x80) // instrument | |
86 { | |
87 m->instr = arg+1; | |
88 lastinstr = m->instr; | |
89 } else | |
90 if (cmd == 0x83) // volume | |
91 { | |
92 m->volcmd = VOLCMD_VOLUME; | |
93 m->vol = arg; | |
94 } else | |
95 // effect | |
96 { | |
97 UINT command = cmd & 0x7F; | |
98 UINT param = arg; | |
99 switch(command) | |
100 { | |
101 // 0x01: Set Speed | |
102 case 0x01: command = CMD_SPEED; break; | |
103 // 0x02: Volume Slide | |
104 // 0x0A: Tone Porta + Vol Slide | |
105 // 0x0B: Vibrato + Vol Slide | |
106 case 0x02: command = CMD_VOLUMESLIDE; | |
107 case 0x0A: if (command == 0x0A) command = CMD_TONEPORTAVOL; | |
108 case 0x0B: if (command == 0x0B) command = CMD_VIBRATOVOL; | |
109 if (param & 0x80) param = (-(signed char)param)&0x0F; | |
110 else param = (param&0x0F)<<4; | |
111 break; | |
112 // 0x04: Porta Up/Down | |
113 case 0x04: if (param & 0x80) { command = CMD_PORTAMENTOUP; param = -(signed char)param; } | |
114 else { command = CMD_PORTAMENTODOWN; } break; | |
115 // 0x06: Tone Portamento | |
116 case 0x06: command = CMD_TONEPORTAMENTO; break; | |
117 // 0x07: Tremor | |
118 case 0x07: command = CMD_TREMOR; break; | |
119 // 0x08: Arpeggio | |
120 case 0x08: command = CMD_ARPEGGIO; break; | |
121 // 0x09: Vibrato | |
122 case 0x09: command = CMD_VIBRATO; break; | |
123 // 0x0C: Pattern Break | |
124 case 0x0C: command = CMD_PATTERNBREAK; break; | |
125 // 0x0D: Position Jump | |
126 case 0x0D: command = CMD_POSITIONJUMP; break; | |
127 // 0x0F: Retrig | |
128 case 0x0F: command = CMD_RETRIG; break; | |
129 // 0x10: Offset | |
130 case 0x10: command = CMD_OFFSET; break; | |
131 // 0x11: Fine Volume Slide | |
132 case 0x11: if (param) { command = CMD_VOLUMESLIDE; | |
133 if (param & 0x80) param = 0xF0|((-(signed char)param)&0x0F); | |
134 else param = 0x0F|((param&0x0F)<<4); | |
135 } else command = 0; break; | |
136 // 0x12: Fine Portamento | |
137 // 0x16: Extra Fine Portamento | |
138 case 0x12: | |
139 case 0x16: if (param) { int mask = (command == 0x16) ? 0xE0 : 0xF0; | |
140 command = (param & 0x80) ? CMD_PORTAMENTOUP : CMD_PORTAMENTODOWN; | |
141 if (param & 0x80) param = mask|((-(signed char)param)&0x0F); | |
142 else param |= mask; | |
143 } else command = 0; break; | |
144 // 0x13: Note Delay | |
145 case 0x13: command = CMD_S3MCMDEX; param = 0xD0|(param & 0x0F); break; | |
146 // 0x14: Note Cut | |
147 case 0x14: command = CMD_S3MCMDEX; param = 0xC0|(param & 0x0F); break; | |
148 // 0x15: Set Tempo | |
149 case 0x15: command = CMD_TEMPO; break; | |
150 // 0x17: Panning | |
151 case 0x17: param = (param+64)&0x7F; | |
152 if (m->command) { if (!m->volcmd) { m->volcmd = VOLCMD_PANNING; m->vol = param/2; } command = 0; } | |
153 else { command = CMD_PANNING8; } | |
154 // Unknown effects | |
155 default: command = param = 0; | |
156 } | |
157 if (command) | |
158 { | |
159 m->command = command; | |
160 m->param = param; | |
161 } | |
162 } | |
163 pTrack += 3; | |
164 } | |
165 } | |
166 | |
167 | |
168 | |
169 BOOL CSoundFile::ReadAMF(LPCBYTE lpStream, DWORD dwMemLength) | |
170 //----------------------------------------------------------- | |
171 { | |
172 AMFFILEHEADER *pfh = (AMFFILEHEADER *)lpStream; | |
173 DWORD dwMemPos; | |
174 | |
175 if ((!lpStream) || (dwMemLength < 2048)) return FALSE; | |
176 if ((!strncmp((LPCTSTR)lpStream, "ASYLUM Music Format V1.0", 25)) && (dwMemLength > 4096)) | |
177 { | |
178 UINT numorders, numpats, numsamples; | |
179 | |
180 dwMemPos = 32; | |
181 numpats = lpStream[dwMemPos+3]; | |
182 numorders = lpStream[dwMemPos+4]; | |
183 numsamples = 64; | |
184 dwMemPos += 6; | |
185 if ((!numpats) || (numpats > MAX_PATTERNS) || (!numorders) | |
186 || (numpats*64*32 + 294 + 37*64 >= dwMemLength)) return FALSE; | |
187 m_nType = MOD_TYPE_AMF0; | |
188 m_nChannels = 8; | |
189 m_nInstruments = 0; | |
190 m_nSamples = 31; | |
191 m_nDefaultTempo = 125; | |
192 m_nDefaultSpeed = 6; | |
193 for (UINT iOrd=0; iOrd<MAX_ORDERS; iOrd++) | |
194 { | |
195 Order[iOrd] = (iOrd < numorders) ? lpStream[dwMemPos+iOrd] : 0xFF; | |
196 } | |
197 dwMemPos = 294; // ??? | |
198 for (UINT iSmp=0; iSmp<numsamples; iSmp++) | |
199 { | |
200 MODINSTRUMENT *psmp = &Ins[iSmp+1]; | |
201 memcpy(m_szNames[iSmp+1], lpStream+dwMemPos, 22); | |
202 psmp->nFineTune = MOD2XMFineTune(lpStream[dwMemPos+22]); | |
203 psmp->nVolume = lpStream[dwMemPos+23]; | |
204 psmp->nGlobalVol = 64; | |
205 if (psmp->nVolume > 0x40) psmp->nVolume = 0x40; | |
206 psmp->nVolume <<= 2; | |
207 psmp->nLength = bswapLE32(*((LPDWORD)(lpStream+dwMemPos+25))); | |
208 psmp->nLoopStart = bswapLE32(*((LPDWORD)(lpStream+dwMemPos+29))); | |
209 psmp->nLoopEnd = psmp->nLoopStart + bswapLE32(*((LPDWORD)(lpStream+dwMemPos+33))); | |
210 if ((psmp->nLoopEnd > psmp->nLoopStart) && (psmp->nLoopEnd <= psmp->nLength)) | |
211 { | |
212 psmp->uFlags = CHN_LOOP; | |
213 } else | |
214 { | |
215 psmp->nLoopStart = psmp->nLoopEnd = 0; | |
216 } | |
217 if ((psmp->nLength) && (iSmp>31)) m_nSamples = iSmp+1; | |
218 dwMemPos += 37; | |
219 } | |
220 for (UINT iPat=0; iPat<numpats; iPat++) | |
221 { | |
222 MODCOMMAND *p = AllocatePattern(64, m_nChannels); | |
223 if (!p) break; | |
224 Patterns[iPat] = p; | |
225 PatternSize[iPat] = 64; | |
226 const UCHAR *pin = lpStream + dwMemPos; | |
227 for (UINT i=0; i<8*64; i++) | |
228 { | |
229 p->note = 0; | |
230 | |
231 if (pin[0]) | |
232 { | |
233 p->note = pin[0] + 13; | |
234 } | |
235 p->instr = pin[1]; | |
236 p->command = pin[2]; | |
237 p->param = pin[3]; | |
238 if (p->command > 0x0F) | |
239 { | |
240 #ifdef AMFLOG | |
241 Log("0x%02X.0x%02X ?", p->command, p->param); | |
242 #endif | |
243 p->command = 0; | |
244 } | |
245 ConvertModCommand(p); | |
246 pin += 4; | |
247 p++; | |
248 } | |
249 dwMemPos += 64*32; | |
250 } | |
251 // Read samples | |
252 for (UINT iData=0; iData<m_nSamples; iData++) | |
253 { | |
254 MODINSTRUMENT *psmp = &Ins[iData+1]; | |
255 if (psmp->nLength) | |
256 { | |
257 dwMemPos += ReadSample(psmp, RS_PCM8S, (LPCSTR)(lpStream+dwMemPos), dwMemLength); | |
258 } | |
259 } | |
260 return TRUE; | |
261 } | |
262 //////////////////////////// | |
263 // DSM/AMF | |
264 USHORT *ptracks[MAX_PATTERNS]; | |
265 DWORD sampleseekpos[MAX_SAMPLES]; | |
266 | |
267 if ((pfh->szAMF[0] != 'A') || (pfh->szAMF[1] != 'M') || (pfh->szAMF[2] != 'F') | |
268 || (pfh->version < 10) || (pfh->version > 14) || (!bswapLE16(pfh->numtracks)) | |
269 || (!pfh->numorders) || (pfh->numorders > MAX_PATTERNS) | |
270 || (!pfh->numsamples) || (pfh->numsamples > MAX_SAMPLES) | |
271 || (pfh->numchannels < 4) || (pfh->numchannels > 32)) | |
272 return FALSE; | |
273 memcpy(m_szNames[0], pfh->title, 32); | |
274 dwMemPos = sizeof(AMFFILEHEADER); | |
275 m_nType = MOD_TYPE_AMF; | |
276 m_nChannels = pfh->numchannels; | |
277 m_nSamples = pfh->numsamples; | |
278 m_nInstruments = 0; | |
279 // Setup Channel Pan Positions | |
280 if (pfh->version >= 11) | |
281 { | |
282 signed char *panpos = (signed char *)(lpStream + dwMemPos); | |
283 UINT nchannels = (pfh->version >= 13) ? 32 : 16; | |
284 for (UINT i=0; i<nchannels; i++) | |
285 { | |
286 int pan = (panpos[i] + 64) * 2; | |
287 if (pan < 0) pan = 0; | |
288 if (pan > 256) { pan = 128; ChnSettings[i].dwFlags |= CHN_SURROUND; } | |
289 ChnSettings[i].nPan = pan; | |
290 } | |
291 dwMemPos += nchannels; | |
292 } else | |
293 { | |
294 for (UINT i=0; i<16; i++) | |
295 { | |
296 ChnSettings[i].nPan = (lpStream[dwMemPos+i] & 1) ? 0x30 : 0xD0; | |
297 } | |
298 dwMemPos += 16; | |
299 } | |
300 // Get Tempo/Speed | |
301 m_nDefaultTempo = 125; | |
302 m_nDefaultSpeed = 6; | |
303 if (pfh->version >= 13) | |
304 { | |
305 if (lpStream[dwMemPos] >= 32) m_nDefaultTempo = lpStream[dwMemPos]; | |
306 if (lpStream[dwMemPos+1] <= 32) m_nDefaultSpeed = lpStream[dwMemPos+1]; | |
307 dwMemPos += 2; | |
308 } | |
309 // Setup sequence list | |
310 for (UINT iOrd=0; iOrd<MAX_ORDERS; iOrd++) | |
311 { | |
312 Order[iOrd] = 0xFF; | |
313 if (iOrd < pfh->numorders) | |
314 { | |
315 Order[iOrd] = iOrd; | |
316 PatternSize[iOrd] = 64; | |
317 if (pfh->version >= 14) | |
318 { | |
319 PatternSize[iOrd] = bswapLE16(*(USHORT *)(lpStream+dwMemPos)); | |
320 dwMemPos += 2; | |
321 } | |
322 ptracks[iOrd] = (USHORT *)(lpStream+dwMemPos); | |
323 dwMemPos += m_nChannels * sizeof(USHORT); | |
324 } | |
325 } | |
326 if (dwMemPos + m_nSamples * (sizeof(AMFSAMPLE)+8) > dwMemLength) return TRUE; | |
327 // Read Samples | |
328 UINT maxsampleseekpos = 0; | |
329 for (UINT iIns=0; iIns<m_nSamples; iIns++) | |
330 { | |
331 MODINSTRUMENT *pins = &Ins[iIns+1]; | |
332 AMFSAMPLE *psh = (AMFSAMPLE *)(lpStream + dwMemPos); | |
333 | |
334 dwMemPos += sizeof(AMFSAMPLE); | |
335 memcpy(m_szNames[iIns+1], psh->samplename, 32); | |
336 memcpy(pins->name, psh->filename, 13); | |
337 pins->nLength = bswapLE32(psh->length); | |
338 pins->nC4Speed = bswapLE16(psh->c2spd); | |
339 pins->nGlobalVol = 64; | |
340 pins->nVolume = psh->volume * 4; | |
341 if (pfh->version >= 11) | |
342 { | |
343 pins->nLoopStart = bswapLE32(*(DWORD *)(lpStream+dwMemPos)); | |
344 pins->nLoopEnd = bswapLE32(*(DWORD *)(lpStream+dwMemPos+4)); | |
345 dwMemPos += 8; | |
346 } else | |
347 { | |
348 pins->nLoopStart = bswapLE16(*(WORD *)(lpStream+dwMemPos)); | |
349 pins->nLoopEnd = pins->nLength; | |
350 dwMemPos += 2; | |
351 } | |
352 sampleseekpos[iIns] = 0; | |
353 if ((psh->type) && (bswapLE32(psh->offset) < dwMemLength-1)) | |
354 { | |
355 sampleseekpos[iIns] = bswapLE32(psh->offset); | |
356 if (bswapLE32(psh->offset) > maxsampleseekpos) | |
357 maxsampleseekpos = bswapLE32(psh->offset); | |
358 if ((pins->nLoopEnd > pins->nLoopStart + 2) | |
359 && (pins->nLoopEnd <= pins->nLength)) pins->uFlags |= CHN_LOOP; | |
360 } | |
361 } | |
362 // Read Track Mapping Table | |
363 USHORT *pTrackMap = (USHORT *)(lpStream+dwMemPos); | |
364 UINT realtrackcnt = 0; | |
365 dwMemPos += pfh->numtracks * sizeof(USHORT); | |
366 for (UINT iTrkMap=0; iTrkMap<pfh->numtracks; iTrkMap++) | |
367 { | |
368 if (realtrackcnt < pTrackMap[iTrkMap]) realtrackcnt = pTrackMap[iTrkMap]; | |
369 } | |
370 // Store tracks positions | |
371 BYTE **pTrackData = new BYTE *[realtrackcnt]; | |
372 memset(pTrackData, 0, sizeof(pTrackData)); | |
373 for (UINT iTrack=0; iTrack<realtrackcnt; iTrack++) if (dwMemPos + 3 <= dwMemLength) | |
374 { | |
375 UINT nTrkSize = bswapLE16(*(USHORT *)(lpStream+dwMemPos)); | |
376 nTrkSize += (UINT)lpStream[dwMemPos+2] << 16; | |
377 | |
378 if (dwMemPos + nTrkSize * 3 + 3 <= dwMemLength) | |
379 { | |
380 pTrackData[iTrack] = (BYTE *)(lpStream + dwMemPos); | |
381 } | |
382 dwMemPos += nTrkSize * 3 + 3; | |
383 } | |
384 // Create the patterns from the list of tracks | |
385 for (UINT iPat=0; iPat<pfh->numorders; iPat++) | |
386 { | |
387 MODCOMMAND *p = AllocatePattern(PatternSize[iPat], m_nChannels); | |
388 if (!p) break; | |
389 Patterns[iPat] = p; | |
390 for (UINT iChn=0; iChn<m_nChannels; iChn++) | |
391 { | |
392 UINT nTrack = bswapLE16(ptracks[iPat][iChn]); | |
393 if ((nTrack) && (nTrack <= pfh->numtracks)) | |
394 { | |
395 UINT realtrk = bswapLE16(pTrackMap[nTrack-1]); | |
396 if (realtrk) | |
397 { | |
398 realtrk--; | |
399 if ((realtrk < realtrackcnt) && (pTrackData[realtrk])) | |
400 { | |
401 AMF_Unpack(p+iChn, pTrackData[realtrk], PatternSize[iPat], m_nChannels); | |
402 } | |
403 } | |
404 } | |
405 } | |
406 } | |
407 delete pTrackData; | |
408 // Read Sample Data | |
409 for (UINT iSeek=1; iSeek<=maxsampleseekpos; iSeek++) | |
410 { | |
411 if (dwMemPos >= dwMemLength) break; | |
412 for (UINT iSmp=0; iSmp<m_nSamples; iSmp++) if (iSeek == sampleseekpos[iSmp]) | |
413 { | |
414 MODINSTRUMENT *pins = &Ins[iSmp+1]; | |
415 dwMemPos += ReadSample(pins, RS_PCM8U, (LPCSTR)(lpStream+dwMemPos), dwMemLength-dwMemPos); | |
416 break; | |
417 } | |
418 } | |
419 return TRUE; | |
420 } |