Mercurial > audlegacy-plugins
comparison src/adplug/core/d00.cxx @ 12:3da1b8942b8b trunk
[svn] - remove src/Input src/Output src/Effect src/General src/Visualization src/Container
author | nenolod |
---|---|
date | Mon, 18 Sep 2006 03:14:20 -0700 |
parents | src/Input/adplug/core/d00.cxx@13389e613d67 |
children | cae46214b8bf |
comparison
equal
deleted
inserted
replaced
11:cff1d04026ae | 12:3da1b8942b8b |
---|---|
1 /* | |
2 * Adplug - Replayer for many OPL2/OPL3 audio file formats. | |
3 * Copyright (C) 1999 - 2006 Simon Peter, <dn.tlp@gmx.net>, et al. | |
4 * | |
5 * This library is free software; you can redistribute it and/or | |
6 * modify it under the terms of the GNU Lesser General Public | |
7 * License as published by the Free Software Foundation; either | |
8 * version 2.1 of the License, or (at your option) any later version. | |
9 * | |
10 * This library 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 GNU | |
13 * Lesser General Public License for more details. | |
14 * | |
15 * You should have received a copy of the GNU Lesser General Public | |
16 * License along with this library; if not, write to the Free Software | |
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |
18 * | |
19 * d00.c - D00 Player by Simon Peter <dn.tlp@gmx.net> | |
20 * | |
21 * NOTES: | |
22 * Sorry for the goto's, but the code looks so much nicer now. | |
23 * I tried it with while loops but it was just a mess. If you | |
24 * can come up with a nicer solution, just tell me. | |
25 * | |
26 * BUGS: | |
27 * Hard restart SR is sometimes wrong | |
28 */ | |
29 | |
30 #include <string.h> | |
31 #include <stdio.h> | |
32 #include <inttypes.h> | |
33 | |
34 #include "debug.h" | |
35 #include "d00.h" | |
36 | |
37 #define HIBYTE(val) (val >> 8) | |
38 #define LOBYTE(val) (val & 0xff) | |
39 | |
40 static const unsigned short notetable[12] = // D00 note table | |
41 {340,363,385,408,432,458,485,514,544,577,611,647}; | |
42 | |
43 static inline uint16_t LE_WORD(const uint16_t *val) | |
44 { | |
45 const uint8_t *b = (const uint8_t *)val; | |
46 return (b[1] << 8) + b[0]; | |
47 } | |
48 | |
49 /*** public methods *************************************/ | |
50 | |
51 CPlayer *Cd00Player::factory(Copl *newopl) | |
52 { | |
53 return new Cd00Player(newopl); | |
54 } | |
55 | |
56 bool Cd00Player::load(const std::string &filename, const CFileProvider &fp) | |
57 { | |
58 binistream *f = fp.open(filename); if(!f) return false; | |
59 d00header *checkhead; | |
60 d00header1 *ch; | |
61 unsigned long filesize; | |
62 int i,ver1=0; | |
63 char *str; | |
64 | |
65 // file validation section | |
66 checkhead = new d00header; | |
67 f->readString((char *)checkhead, sizeof(d00header)); | |
68 | |
69 // Check for version 2-4 header | |
70 if(strncmp(checkhead->id,"JCH\x26\x02\x66",6) || checkhead->type || | |
71 !checkhead->subsongs || checkhead->soundcard) { | |
72 // Check for version 0 or 1 header (and .d00 file extension) | |
73 delete checkhead; | |
74 if(!fp.extension(filename, ".d00")) { fp.close(f); return false; } | |
75 ch = new d00header1; | |
76 f->seek(0); f->readString((char *)ch, sizeof(d00header1)); | |
77 if(ch->version > 1 || !ch->subsongs) | |
78 { delete ch; fp.close(f); return false; } | |
79 delete ch; | |
80 ver1 = 1; | |
81 } else | |
82 delete checkhead; | |
83 | |
84 AdPlug_LogWrite("Cd00Player::load(f,\"%s\"): %s format D00 file detected!\n", | |
85 filename.c_str(), ver1 ? "Old" : "New"); | |
86 | |
87 // load section | |
88 filesize = fp.filesize(f); f->seek(0); | |
89 filedata = new char [filesize + 1]; // 1 byte is needed for old-style DataInfo block | |
90 f->readString((char *)filedata, filesize); | |
91 fp.close(f); | |
92 if(!ver1) { // version 2 and above | |
93 header = (struct d00header *)filedata; | |
94 version = header->version; | |
95 datainfo = (char *)filedata + LE_WORD(&header->infoptr); | |
96 inst = (struct Sinsts *)((char *)filedata + LE_WORD(&header->instptr)); | |
97 seqptr = (unsigned short *)((char *)filedata + LE_WORD(&header->seqptr)); | |
98 for(i=31;i>=0;i--) // erase whitespace | |
99 if(header->songname[i] == ' ') | |
100 header->songname[i] = '\0'; | |
101 else | |
102 break; | |
103 for(i=31;i>=0;i--) | |
104 if(header->author[i] == ' ') | |
105 header->author[i] = '\0'; | |
106 else | |
107 break; | |
108 } else { // version 1 | |
109 header1 = (struct d00header1 *)filedata; | |
110 version = header1->version; | |
111 datainfo = (char *)filedata + LE_WORD(&header1->infoptr); | |
112 inst = (struct Sinsts *)((char *)filedata + LE_WORD(&header1->instptr)); | |
113 seqptr = (unsigned short *)((char *)filedata + LE_WORD(&header1->seqptr)); | |
114 } | |
115 switch(version) { | |
116 case 0: | |
117 levpuls = 0; | |
118 spfx = 0; | |
119 header1->speed = 70; // v0 files default to 70Hz | |
120 break; | |
121 case 1: | |
122 levpuls = (struct Slevpuls *)((char *)filedata + LE_WORD(&header1->lpulptr)); | |
123 spfx = 0; | |
124 break; | |
125 case 2: | |
126 levpuls = (struct Slevpuls *)((char *)filedata + LE_WORD(&header->spfxptr)); | |
127 spfx = 0; | |
128 break; | |
129 case 3: | |
130 spfx = 0; | |
131 levpuls = 0; | |
132 break; | |
133 case 4: | |
134 spfx = (struct Sspfx *)((char *)filedata + LE_WORD(&header->spfxptr)); | |
135 levpuls = 0; | |
136 break; | |
137 } | |
138 if((str = strstr(datainfo,"\xff\xff"))) | |
139 while((*str == '\xff' || *str == ' ') && str >= datainfo) { | |
140 *str = '\0'; str--; | |
141 } | |
142 else // old-style block | |
143 memset((char *)filedata+filesize,0,1); | |
144 | |
145 rewind(0); | |
146 return true; | |
147 } | |
148 | |
149 bool Cd00Player::update() | |
150 { | |
151 unsigned char c,cnt,trackend=0,fx,note; | |
152 unsigned short ord,*patt,buf,fxop,pattpos; | |
153 | |
154 // effect handling (timer dependant) | |
155 for(c=0;c<9;c++) { | |
156 channel[c].slideval += channel[c].slide; setfreq(c); // sliding | |
157 vibrato(c); // vibrato | |
158 | |
159 if(channel[c].spfx != 0xffff) { // SpFX | |
160 if(channel[c].fxdel) | |
161 channel[c].fxdel--; | |
162 else { | |
163 channel[c].spfx = LE_WORD(&spfx[channel[c].spfx].ptr); | |
164 channel[c].fxdel = spfx[channel[c].spfx].duration; | |
165 channel[c].inst = LE_WORD(&spfx[channel[c].spfx].instnr) & 0xfff; | |
166 if(spfx[channel[c].spfx].modlev != 0xff) | |
167 channel[c].modvol = spfx[channel[c].spfx].modlev; | |
168 setinst(c); | |
169 if(LE_WORD(&spfx[channel[c].spfx].instnr) & 0x8000) // locked frequency | |
170 note = spfx[channel[c].spfx].halfnote; | |
171 else // unlocked frequency | |
172 note = spfx[channel[c].spfx].halfnote + channel[c].note; | |
173 channel[c].freq = notetable[note%12] + ((note/12) << 10); | |
174 setfreq(c); | |
175 } | |
176 channel[c].modvol += spfx[channel[c].spfx].modlevadd; channel[c].modvol &= 63; | |
177 setvolume(c); | |
178 } | |
179 | |
180 if(channel[c].levpuls != 0xff) // Levelpuls | |
181 if(channel[c].frameskip) | |
182 channel[c].frameskip--; | |
183 else { | |
184 channel[c].frameskip = inst[channel[c].inst].timer; | |
185 if(channel[c].fxdel) | |
186 channel[c].fxdel--; | |
187 else { | |
188 channel[c].levpuls = levpuls[channel[c].levpuls].ptr - 1; | |
189 channel[c].fxdel = levpuls[channel[c].levpuls].duration; | |
190 if(levpuls[channel[c].levpuls].level != 0xff) | |
191 channel[c].modvol = levpuls[channel[c].levpuls].level; | |
192 } | |
193 channel[c].modvol += levpuls[channel[c].levpuls].voladd; channel[c].modvol &= 63; | |
194 setvolume(c); | |
195 } | |
196 } | |
197 | |
198 // song handling | |
199 for(c=0;c<9;c++) | |
200 if(version < 3 ? channel[c].del : channel[c].del <= 0x7f) { | |
201 if(version == 4) // v4: hard restart SR | |
202 if(channel[c].del == inst[channel[c].inst].timer) | |
203 if(channel[c].nextnote) | |
204 opl->write(0x83 + op_table[c], inst[channel[c].inst].sr); | |
205 if(version < 3) | |
206 channel[c].del--; | |
207 else | |
208 if(channel[c].speed) | |
209 channel[c].del += channel[c].speed; | |
210 else { | |
211 channel[c].seqend = 1; | |
212 continue; | |
213 } | |
214 } else { | |
215 if(channel[c].speed) { | |
216 if(version < 3) | |
217 channel[c].del = channel[c].speed; | |
218 else { | |
219 channel[c].del &= 0x7f; | |
220 channel[c].del += channel[c].speed; | |
221 } | |
222 } else { | |
223 channel[c].seqend = 1; | |
224 continue; | |
225 } | |
226 if(channel[c].rhcnt) { // process pending REST/HOLD events | |
227 channel[c].rhcnt--; | |
228 continue; | |
229 } | |
230 readorder: // process arrangement (orderlist) | |
231 ord = LE_WORD(&channel[c].order[channel[c].ordpos]); | |
232 switch(ord) { | |
233 case 0xfffe: channel[c].seqend = 1; continue; // end of arrangement stream | |
234 case 0xffff: // jump to order | |
235 channel[c].ordpos = LE_WORD(&channel[c].order[channel[c].ordpos + 1]); | |
236 channel[c].seqend = 1; | |
237 goto readorder; | |
238 default: | |
239 if(ord >= 0x9000) { // set speed | |
240 channel[c].speed = ord & 0xff; | |
241 ord = LE_WORD(&channel[c].order[channel[c].ordpos - 1]); | |
242 channel[c].ordpos++; | |
243 } else | |
244 if(ord >= 0x8000) { // transpose track | |
245 channel[c].transpose = ord & 0xff; | |
246 if(ord & 0x100) | |
247 channel[c].transpose = -channel[c].transpose; | |
248 ord = LE_WORD(&channel[c].order[++channel[c].ordpos]); | |
249 } | |
250 patt = (unsigned short *)((char *)filedata + LE_WORD(&seqptr[ord])); | |
251 break; | |
252 } | |
253 readseq: // process sequence (pattern) | |
254 if(!version) // v0: always initialize rhcnt | |
255 channel[c].rhcnt = channel[c].irhcnt; | |
256 pattpos = LE_WORD(&patt[channel[c].pattpos]); | |
257 if(pattpos == 0xffff) { // pattern ended? | |
258 channel[c].pattpos = 0; | |
259 channel[c].ordpos++; | |
260 goto readorder; | |
261 } | |
262 cnt = HIBYTE(pattpos); | |
263 note = LOBYTE(pattpos); | |
264 fx = pattpos >> 12; | |
265 fxop = pattpos & 0x0fff; | |
266 channel[c].pattpos++; pattpos = LE_WORD(&patt[channel[c].pattpos]); | |
267 channel[c].nextnote = LOBYTE(pattpos) & 0x7f; | |
268 if(version ? cnt < 0x40 : !fx) { // note event | |
269 switch(note) { | |
270 case 0: // REST event | |
271 case 0x80: | |
272 if(!note || version) { | |
273 channel[c].key = 0; | |
274 setfreq(c); | |
275 } | |
276 // fall through... | |
277 case 0x7e: // HOLD event | |
278 if(version) | |
279 channel[c].rhcnt = cnt; | |
280 channel[c].nextnote = 0; | |
281 break; | |
282 default: // play note | |
283 // restart fx | |
284 channel[c].slideval = 0; channel[c].slide = 0; channel[c].vibdepth = 0; | |
285 | |
286 if(version) { // note handling for v1 and above | |
287 if(note > 0x80) // locked note (no channel transpose) | |
288 note -= 0x80; | |
289 else // unlocked note | |
290 note += channel[c].transpose; | |
291 channel[c].note = note; // remember note for SpFX | |
292 | |
293 if(channel[c].ispfx != 0xffff && cnt < 0x20) { // reset SpFX | |
294 channel[c].spfx = channel[c].ispfx; | |
295 if(LE_WORD(&spfx[channel[c].spfx].instnr) & 0x8000) // locked frequency | |
296 note = spfx[channel[c].spfx].halfnote; | |
297 else // unlocked frequency | |
298 note += spfx[channel[c].spfx].halfnote; | |
299 channel[c].inst = LE_WORD(&spfx[channel[c].spfx].instnr) & 0xfff; | |
300 channel[c].fxdel = spfx[channel[c].spfx].duration; | |
301 if(spfx[channel[c].spfx].modlev != 0xff) | |
302 channel[c].modvol = spfx[channel[c].spfx].modlev; | |
303 else | |
304 channel[c].modvol = inst[channel[c].inst].data[7] & 63; | |
305 } | |
306 | |
307 if(channel[c].ilevpuls != 0xff && cnt < 0x20) { // reset LevelPuls | |
308 channel[c].levpuls = channel[c].ilevpuls; | |
309 channel[c].fxdel = levpuls[channel[c].levpuls].duration; | |
310 channel[c].frameskip = inst[channel[c].inst].timer; | |
311 if(levpuls[channel[c].levpuls].level != 0xff) | |
312 channel[c].modvol = levpuls[channel[c].levpuls].level; | |
313 else | |
314 channel[c].modvol = inst[channel[c].inst].data[7] & 63; | |
315 } | |
316 | |
317 channel[c].freq = notetable[note%12] + ((note/12) << 10); | |
318 if(cnt < 0x20) // normal note | |
319 playnote(c); | |
320 else { // tienote | |
321 setfreq(c); | |
322 cnt -= 0x20; // make count proper | |
323 } | |
324 channel[c].rhcnt = cnt; | |
325 } else { // note handling for v0 | |
326 if(cnt < 2) // unlocked note | |
327 note += channel[c].transpose; | |
328 channel[c].note = note; | |
329 | |
330 channel[c].freq = notetable[note%12] + ((note/12) << 10); | |
331 if(cnt == 1) // tienote | |
332 setfreq(c); | |
333 else // normal note | |
334 playnote(c); | |
335 } | |
336 break; | |
337 } | |
338 continue; // event is complete | |
339 } else { // effect event | |
340 switch(fx) { | |
341 case 6: // Cut/Stop Voice | |
342 buf = channel[c].inst; | |
343 channel[c].inst = 0; | |
344 playnote(c); | |
345 channel[c].inst = buf; | |
346 channel[c].rhcnt = fxop; | |
347 continue; // no note follows this event | |
348 case 7: // Vibrato | |
349 channel[c].vibspeed = fxop & 0xff; | |
350 channel[c].vibdepth = fxop >> 8; | |
351 channel[c].trigger = fxop >> 9; | |
352 break; | |
353 case 8: // v0: Duration | |
354 if(!version) | |
355 channel[c].irhcnt = fxop; | |
356 break; | |
357 case 9: // New Level | |
358 channel[c].vol = fxop & 63; | |
359 if(channel[c].vol + channel[c].cvol < 63) // apply channel volume | |
360 channel[c].vol += channel[c].cvol; | |
361 else | |
362 channel[c].vol = 63; | |
363 setvolume(c); | |
364 break; | |
365 case 0xb: // v4: Set SpFX | |
366 if(version == 4) | |
367 channel[c].ispfx = fxop; | |
368 break; | |
369 case 0xc: // Set Instrument | |
370 channel[c].ispfx = 0xffff; | |
371 channel[c].spfx = 0xffff; | |
372 channel[c].inst = fxop; | |
373 channel[c].modvol = inst[fxop].data[7] & 63; | |
374 if(version < 3 && version && inst[fxop].tunelev) // Set LevelPuls | |
375 channel[c].ilevpuls = inst[fxop].tunelev - 1; | |
376 else { | |
377 channel[c].ilevpuls = 0xff; | |
378 channel[c].levpuls = 0xff; | |
379 } | |
380 break; | |
381 case 0xd: // Slide up | |
382 channel[c].slide = fxop; | |
383 break; | |
384 case 0xe: // Slide down | |
385 channel[c].slide = -fxop; | |
386 break; | |
387 } | |
388 goto readseq; // event is incomplete, note follows | |
389 } | |
390 } | |
391 | |
392 for(c=0;c<9;c++) | |
393 if(channel[c].seqend) | |
394 trackend++; | |
395 if(trackend == 9) | |
396 songend = 1; | |
397 | |
398 return !songend; | |
399 } | |
400 | |
401 void Cd00Player::rewind(int subsong) | |
402 { | |
403 struct Stpoin { | |
404 unsigned short ptr[9]; | |
405 unsigned char volume[9],dummy[5]; | |
406 } *tpoin; | |
407 int i; | |
408 | |
409 if(version > 1) { // do nothing if subsong > number of subsongs | |
410 if(subsong >= header->subsongs) | |
411 return; | |
412 } else | |
413 if(subsong >= header1->subsongs) | |
414 return; | |
415 | |
416 memset(channel,0,sizeof(channel)); | |
417 if(version > 1) | |
418 tpoin = (struct Stpoin *)((char *)filedata + LE_WORD(&header->tpoin)); | |
419 else | |
420 tpoin = (struct Stpoin *)((char *)filedata + LE_WORD(&header1->tpoin)); | |
421 for(i=0;i<9;i++) { | |
422 if(LE_WORD(&tpoin[subsong].ptr[i])) { // track enabled | |
423 channel[i].speed = LE_WORD((unsigned short *) | |
424 ((char *)filedata + LE_WORD(&tpoin[subsong].ptr[i]))); | |
425 channel[i].order = (unsigned short *) | |
426 ((char *)filedata + LE_WORD(&tpoin[subsong].ptr[i]) + 2); | |
427 } else { // track disabled | |
428 channel[i].speed = 0; | |
429 channel[i].order = 0; | |
430 } | |
431 channel[i].ispfx = 0xffff; channel[i].spfx = 0xffff; // no SpFX | |
432 channel[i].ilevpuls = 0xff; channel[i].levpuls = 0xff; // no LevelPuls | |
433 channel[i].cvol = tpoin[subsong].volume[i] & 0x7f; // our player may savely ignore bit 7 | |
434 channel[i].vol = channel[i].cvol; // initialize volume | |
435 } | |
436 songend = 0; | |
437 opl->init(); opl->write(1,32); // reset OPL chip | |
438 } | |
439 | |
440 std::string Cd00Player::gettype() | |
441 { | |
442 char tmpstr[40]; | |
443 | |
444 sprintf(tmpstr,"EdLib packed (version %d)",version > 1 ? header->version : header1->version); | |
445 return std::string(tmpstr); | |
446 } | |
447 | |
448 float Cd00Player::getrefresh() | |
449 { | |
450 if(version > 1) | |
451 return header->speed; | |
452 else | |
453 return header1->speed; | |
454 } | |
455 | |
456 unsigned int Cd00Player::getsubsongs() | |
457 { | |
458 if(version <= 1) // return number of subsongs | |
459 return header1->subsongs; | |
460 else | |
461 return header->subsongs; | |
462 } | |
463 | |
464 /*** private methods *************************************/ | |
465 | |
466 void Cd00Player::setvolume(unsigned char chan) | |
467 { | |
468 unsigned char op = op_table[chan]; | |
469 unsigned short insnr = channel[chan].inst; | |
470 | |
471 opl->write(0x43 + op,(int)(63-((63-(inst[insnr].data[2] & 63))/63.0)*(63-channel[chan].vol)) + | |
472 (inst[insnr].data[2] & 192)); | |
473 if(inst[insnr].data[10] & 1) | |
474 opl->write(0x40 + op,(int)(63-((63-channel[chan].modvol)/63.0)*(63-channel[chan].vol)) + | |
475 (inst[insnr].data[7] & 192)); | |
476 else | |
477 opl->write(0x40 + op,channel[chan].modvol + (inst[insnr].data[7] & 192)); | |
478 } | |
479 | |
480 void Cd00Player::setfreq(unsigned char chan) | |
481 { | |
482 unsigned short freq = channel[chan].freq; | |
483 | |
484 if(version == 4) // v4: apply instrument finetune | |
485 freq += inst[channel[chan].inst].tunelev; | |
486 | |
487 freq += channel[chan].slideval; | |
488 opl->write(0xa0 + chan, freq & 255); | |
489 if(channel[chan].key) | |
490 opl->write(0xb0 + chan, ((freq >> 8) & 31) | 32); | |
491 else | |
492 opl->write(0xb0 + chan, (freq >> 8) & 31); | |
493 } | |
494 | |
495 void Cd00Player::setinst(unsigned char chan) | |
496 { | |
497 unsigned char op = op_table[chan]; | |
498 unsigned short insnr = channel[chan].inst; | |
499 | |
500 // set instrument data | |
501 opl->write(0x63 + op, inst[insnr].data[0]); | |
502 opl->write(0x83 + op, inst[insnr].data[1]); | |
503 opl->write(0x23 + op, inst[insnr].data[3]); | |
504 opl->write(0xe3 + op, inst[insnr].data[4]); | |
505 opl->write(0x60 + op, inst[insnr].data[5]); | |
506 opl->write(0x80 + op, inst[insnr].data[6]); | |
507 opl->write(0x20 + op, inst[insnr].data[8]); | |
508 opl->write(0xe0 + op, inst[insnr].data[9]); | |
509 if(version) | |
510 opl->write(0xc0 + chan, inst[insnr].data[10]); | |
511 else | |
512 opl->write(0xc0 + chan, (inst[insnr].data[10] << 1) + (inst[insnr].tunelev & 1)); | |
513 } | |
514 | |
515 void Cd00Player::playnote(unsigned char chan) | |
516 { | |
517 // set misc vars & play | |
518 opl->write(0xb0 + chan, 0); // stop old note | |
519 setinst(chan); | |
520 channel[chan].key = 1; | |
521 setfreq(chan); | |
522 setvolume(chan); | |
523 } | |
524 | |
525 void Cd00Player::vibrato(unsigned char chan) | |
526 { | |
527 if(!channel[chan].vibdepth) | |
528 return; | |
529 | |
530 if(channel[chan].trigger) | |
531 channel[chan].trigger--; | |
532 else { | |
533 channel[chan].trigger = channel[chan].vibdepth; | |
534 channel[chan].vibspeed = -channel[chan].vibspeed; | |
535 } | |
536 channel[chan].freq += channel[chan].vibspeed; | |
537 setfreq(chan); | |
538 } |