Diffstat (limited to 'core/multimedia/opieplayer/modplug/load_amf.cpp') (more/less context) (ignore whitespace changes)
-rw-r--r-- | core/multimedia/opieplayer/modplug/load_amf.cpp | 420 |
1 files changed, 420 insertions, 0 deletions
diff --git a/core/multimedia/opieplayer/modplug/load_amf.cpp b/core/multimedia/opieplayer/modplug/load_amf.cpp new file mode 100644 index 0000000..188b5f5 --- a/dev/null +++ b/core/multimedia/opieplayer/modplug/load_amf.cpp | |||
@@ -0,0 +1,420 @@ | |||
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 | } Q_PACKED 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 | } Q_PACKED 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 = *(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)) *m = 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 = *((LPDWORD)(lpStream+dwMemPos+25)); | ||
208 | psmp->nLoopStart = *((LPDWORD)(lpStream+dwMemPos+29)); | ||
209 | psmp->nLoopEnd = psmp->nLoopStart + *((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) || (!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] = *(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 = psh->length; | ||
338 | pins->nC4Speed = psh->c2spd; | ||
339 | pins->nGlobalVol = 64; | ||
340 | pins->nVolume = psh->volume * 4; | ||
341 | if (pfh->version >= 11) | ||
342 | { | ||
343 | pins->nLoopStart = *(DWORD *)(lpStream+dwMemPos); | ||
344 | pins->nLoopEnd = *(DWORD *)(lpStream+dwMemPos+4); | ||
345 | dwMemPos += 8; | ||
346 | } else | ||
347 | { | ||
348 | pins->nLoopStart = *(WORD *)(lpStream+dwMemPos); | ||
349 | pins->nLoopEnd = pins->nLength; | ||
350 | dwMemPos += 2; | ||
351 | } | ||
352 | sampleseekpos[iIns] = 0; | ||
353 | if ((psh->type) && (psh->offset < dwMemLength-1)) | ||
354 | { | ||
355 | sampleseekpos[iIns] = psh->offset; | ||
356 | if (psh->offset > maxsampleseekpos) maxsampleseekpos = psh->offset; | ||
357 | if ((pins->nLoopEnd > pins->nLoopStart + 2) | ||
358 | && (pins->nLoopEnd <= pins->nLength)) pins->uFlags |= CHN_LOOP; | ||
359 | } | ||
360 | } | ||
361 | // Read Track Mapping Table | ||
362 | USHORT *pTrackMap = (USHORT *)(lpStream+dwMemPos); | ||
363 | UINT realtrackcnt = 0; | ||
364 | dwMemPos += pfh->numtracks * sizeof(USHORT); | ||
365 | for (UINT iTrkMap=0; iTrkMap<pfh->numtracks; iTrkMap++) | ||
366 | { | ||
367 | if (realtrackcnt < pTrackMap[iTrkMap]) realtrackcnt = pTrackMap[iTrkMap]; | ||
368 | } | ||
369 | // Store tracks positions | ||
370 | BYTE **pTrackData = new BYTE *[realtrackcnt]; | ||
371 | memset(pTrackData, 0, sizeof(pTrackData)); | ||
372 | for (UINT iTrack=0; iTrack<realtrackcnt; iTrack++) if (dwMemPos + 3 <= dwMemLength) | ||
373 | { | ||
374 | UINT nTrkSize = *(USHORT *)(lpStream+dwMemPos); | ||
375 | nTrkSize += (UINT)lpStream[dwMemPos+2] << 16; | ||
376 | if (dwMemPos + nTrkSize * 3 + 3 <= dwMemLength) | ||
377 | { | ||
378 | pTrackData[iTrack] = (BYTE *)(lpStream + dwMemPos); | ||
379 | } | ||
380 | dwMemPos += nTrkSize * 3 + 3; | ||
381 | } | ||
382 | // Create the patterns from the list of tracks | ||
383 | for (UINT iPat=0; iPat<pfh->numorders; iPat++) | ||
384 | { | ||
385 | MODCOMMAND *p = AllocatePattern(PatternSize[iPat], m_nChannels); | ||
386 | if (!p) break; | ||
387 | Patterns[iPat] = p; | ||
388 | for (UINT iChn=0; iChn<m_nChannels; iChn++) | ||
389 | { | ||
390 | UINT nTrack = ptracks[iPat][iChn]; | ||
391 | if ((nTrack) && (nTrack <= pfh->numtracks)) | ||
392 | { | ||
393 | UINT realtrk = pTrackMap[nTrack-1]; | ||
394 | if (realtrk) | ||
395 | { | ||
396 | realtrk--; | ||
397 | if ((realtrk < realtrackcnt) && (pTrackData[realtrk])) | ||
398 | { | ||
399 | AMF_Unpack(p+iChn, pTrackData[realtrk], PatternSize[iPat], m_nChannels); | ||
400 | } | ||
401 | } | ||
402 | } | ||
403 | } | ||
404 | } | ||
405 | delete pTrackData; | ||
406 | // Read Sample Data | ||
407 | for (UINT iSeek=1; iSeek<=maxsampleseekpos; iSeek++) | ||
408 | { | ||
409 | if (dwMemPos >= dwMemLength) break; | ||
410 | for (UINT iSmp=0; iSmp<m_nSamples; iSmp++) if (iSeek == sampleseekpos[iSmp]) | ||
411 | { | ||
412 | MODINSTRUMENT *pins = &Ins[iSmp+1]; | ||
413 | dwMemPos += ReadSample(pins, RS_PCM8U, (LPCSTR)(lpStream+dwMemPos), dwMemLength-dwMemPos); | ||
414 | break; | ||
415 | } | ||
416 | } | ||
417 | return TRUE; | ||
418 | } | ||
419 | |||
420 | |||