Mercurial > audlegacy
diff Plugins/Input/adplug/core/hsc.cxx @ 1713:a4d7227231e3 trunk
[svn] More .cpp -> .cxx. Noticed by Chainsaw. Shame on me that I forgot some ;).
author | js |
---|---|
date | Sat, 16 Sep 2006 07:33:28 -0700 |
parents | Plugins/Input/adplug/core/hsc.cpp@705d4c089fce |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Plugins/Input/adplug/core/hsc.cxx Sat Sep 16 07:33:28 2006 -0700 @@ -0,0 +1,317 @@ +/* + * Adplug - Replayer for many OPL2/OPL3 audio file formats. + * Copyright (C) 1999 - 2004 Simon Peter, <dn.tlp@gmx.net>, et al. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * hsc.cpp - HSC Player by Simon Peter <dn.tlp@gmx.net> + */ + +#include <string.h> + +#include "hsc.h" +#include "debug.h" + +/*** public methods **************************************/ + +CPlayer *ChscPlayer::factory(Copl *newopl) +{ + return new ChscPlayer(newopl); +} + +bool ChscPlayer::load(const std::string &filename, const CFileProvider &fp) +{ + binistream *f = fp.open(filename); + int i; + + // file validation section + if(!f || !fp.extension(filename, ".hsc") || fp.filesize(f) > 59187) { + AdPlug_LogWrite("ChscPlayer::load(\"%s\"): Not a HSC file!\n", filename.c_str()); + fp.close(f); + return false; + } + + // load section + for(i=0;i<128*12;i++) // load instruments + *((unsigned char *)instr + i) = f->readInt(1); + for (i=0;i<128;i++) { // correct instruments + instr[i][2] ^= (instr[i][2] & 0x40) << 1; + instr[i][3] ^= (instr[i][3] & 0x40) << 1; + instr[i][11] >>= 4; // slide + } + for(i=0;i<51;i++) song[i] = f->readInt(1); // load tracklist + for(i=0;i<50*64*9;i++) // load patterns + *((char *)patterns + i) = f->readInt(1); + + fp.close(f); + rewind(0); // rewind module + return true; +} + +bool ChscPlayer::update() +{ + // general vars + unsigned char chan,pattnr,note,effect,eff_op,inst,vol,Okt,db; + unsigned short Fnr; + unsigned long pattoff; + + del--; // player speed handling + if(del) + return !songend; // nothing done + + if(fadein) // fade-in handling + fadein--; + + pattnr = song[songpos]; + if(pattnr == 0xff) { // arrangement handling + songend = 1; // set end-flag + songpos = 0; + pattnr = song[songpos]; + } else + if ((pattnr & 128) && (pattnr <= 0xb1)) { // goto pattern "nr" + songpos = song[songpos] & 127; + pattpos = 0; + pattnr = song[songpos]; + songend = 1; + } + + pattoff = pattpos*9; + for (chan=0;chan<9;chan++) { // handle all channels + note = patterns[pattnr][pattoff].note; + effect = patterns[pattnr][pattoff].effect; + pattoff++; + + if(note & 128) { // set instrument + setinstr(chan,effect); + continue; + } + eff_op = effect & 0x0f; + inst = channel[chan].inst; + if(note) + channel[chan].slide = 0; + + switch (effect & 0xf0) { // effect handling + case 0: // global effect + /* The following fx are unimplemented on purpose: + * 02 - Slide Mainvolume up + * 03 - Slide Mainvolume down (here: fade in) + * 04 - Set Mainvolume to 0 + * + * This is because i've never seen any HSC modules using the fx this way. + * All modules use the fx the way, i've implemented it. + */ + switch(eff_op) { + case 1: pattbreak++; break; // jump to next pattern + case 3: fadein = 31; break; // fade in (divided by 2) + case 5: mode6 = 1; break; // 6 voice mode on + case 6: mode6 = 0; break; // 6 voice mode off + } + break; + case 0x20: + case 0x10: // manual slides + if (effect & 0x10) { + channel[chan].freq += eff_op; + channel[chan].slide += eff_op; + } else { + channel[chan].freq -= eff_op; + channel[chan].slide -= eff_op; + } + if(!note) + setfreq(chan,channel[chan].freq); + break; + case 0x50: // set percussion instrument (unimplemented) + break; + case 0x60: // set feedback + opl->write(0xc0 + chan, (instr[channel[chan].inst][8] & 1) + (eff_op << 1)); + break; + case 0xa0: // set carrier volume + vol = eff_op << 2; + opl->write(0x43 + op_table[chan], vol | (instr[channel[chan].inst][2] & ~63)); + break; + case 0xb0: // set modulator volume + vol = eff_op << 2; + if (instr[inst][8] & 1) + opl->write(0x40 + op_table[chan], vol | (instr[channel[chan].inst][3] & ~63)); + else + opl->write(0x40 + op_table[chan],vol | (instr[inst][3] & ~63)); + break; + case 0xc0: // set instrument volume + db = eff_op << 2; + opl->write(0x43 + op_table[chan], db | (instr[channel[chan].inst][2] & ~63)); + if (instr[inst][8] & 1) + opl->write(0x40 + op_table[chan], db | (instr[channel[chan].inst][3] & ~63)); + break; + case 0xd0: pattbreak++; songpos = eff_op; songend = 1; break; // position jump + case 0xf0: // set speed + speed = eff_op; + del = ++speed; + break; + } + + if(fadein) // fade-in volume setting + setvolume(chan,fadein*2,fadein*2); + + if(!note) // note handling + continue; + note--; + + if ((note == 0x7f-1) || ((note/12) & ~7)) { // pause (7fh) + adl_freq[chan] &= ~32; + opl->write(0xb0 + chan,adl_freq[chan]); + continue; + } + + // play the note + if(mtkmode) // imitate MPU-401 Trakker bug + note--; + Okt = ((note/12) & 7) << 2; + Fnr = note_table[(note % 12)] + instr[inst][11] + channel[chan].slide; + channel[chan].freq = Fnr; + if(!mode6 || chan < 6) + adl_freq[chan] = Okt | 32; + else + adl_freq[chan] = Okt; // never set key for drums + opl->write(0xb0 + chan, 0); + setfreq(chan,Fnr); + if(mode6) { + switch(chan) { // play drums + case 6: opl->write(0xbd,bd & ~16); bd |= 48; break; // bass drum + case 7: opl->write(0xbd,bd & ~1); bd |= 33; break; // hihat + case 8: opl->write(0xbd,bd & ~2); bd |= 34; break; // cymbal + } + opl->write(0xbd,bd); + } + } + + del = speed; // player speed-timing + if(pattbreak) { // do post-effect handling + pattpos=0; // pattern break! + pattbreak=0; + songpos++; + songpos %= 50; + if(!songpos) + songend = 1; + } else { + pattpos++; + pattpos &= 63; // advance in pattern data + if (!pattpos) { + songpos++; + songpos %= 50; + if(!songpos) + songend = 1; + } + } + return !songend; // still playing +} + +void ChscPlayer::rewind(int subsong) +{ + int i; // counter + + // rewind HSC player + pattpos = 0; songpos = 0; pattbreak = 0; speed = 2; + del = 1; songend = 0; mode6 = 0; bd = 0; fadein = 0; + + opl->init(); // reset OPL chip + opl->write(1,32); opl->write(8,128); opl->write(0xbd,0); + + for(i=0;i<9;i++) + setinstr((char) i,(char) i); // init channels +} + +unsigned int ChscPlayer::getpatterns() +{ + unsigned char poscnt,pattcnt=0; + + // count patterns + for(poscnt=0;poscnt<51 && song[poscnt] != 0xff;poscnt++) + if(song[poscnt] > pattcnt) + pattcnt = song[poscnt]; + + return (pattcnt+1); +} + +unsigned int ChscPlayer::getorders() +{ + unsigned char poscnt; + + // count positions + for(poscnt=0;poscnt<51;poscnt++) + if(song[poscnt] == 0xff) + break; + + return poscnt; +} + +unsigned int ChscPlayer::getinstruments() +{ + unsigned char instcnt,instnum=0,i; + bool isinst; + + // count instruments + for(instcnt=0;instcnt<128;instcnt++) { + isinst = false; + for(i=0;i<12;i++) + if(instr[instcnt][i]) + isinst = true; + if(isinst) + instnum++; + } + + return instnum; +} + +/*** private methods *************************************/ + +void ChscPlayer::setfreq(unsigned char chan, unsigned short freq) +{ + adl_freq[chan] = (adl_freq[chan] & ~3) | (freq >> 8); + + opl->write(0xa0 + chan, freq & 0xff); + opl->write(0xb0 + chan, adl_freq[chan]); +} + +void ChscPlayer::setvolume(unsigned char chan, int volc, int volm) +{ + unsigned char *ins = instr[channel[chan].inst]; + char op = op_table[chan]; + + opl->write(0x43 + op,volc | (ins[2] & ~63)); + if (ins[8] & 1) // carrier + opl->write(0x40 + op,volm | (ins[3] & ~63)); + else + opl->write(0x40 + op, ins[3]); // modulator +} + +void ChscPlayer::setinstr(unsigned char chan, unsigned char insnr) +{ + unsigned char *ins = instr[insnr]; + char op = op_table[chan]; + + channel[chan].inst = insnr; // set internal instrument + opl->write(0xb0 + chan,0); // stop old note + + // set instrument + opl->write(0xc0 + chan, ins[8]); + opl->write(0x23 + op, ins[0]); // carrier + opl->write(0x20 + op, ins[1]); // modulator + opl->write(0x63 + op, ins[4]); // bits 0..3 = decay; 4..7 = attack + opl->write(0x60 + op, ins[5]); + opl->write(0x83 + op, ins[6]); // 0..3 = release; 4..7 = sustain + opl->write(0x80 + op, ins[7]); + opl->write(0xe3 + op, ins[9]); // bits 0..1 = Wellenform + opl->write(0xe0 + op, ins[10]); + setvolume(chan, ins[2] & 63, ins[3] & 63); +}