/* * Adplug - Replayer for many OPL2/OPL3 audio file formats. * Copyright (C) 1999 - 2006 Simon Peter, , 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * ksm.cpp - KSM Player for AdPlug by Simon Peter */ #include #include "ksm.h" #include "debug.h" const unsigned int CksmPlayer::adlibfreq[63] = { 0, 2390,2411,2434,2456,2480,2506,2533,2562,2592,2625,2659,2695, 3414,3435,3458,3480,3504,3530,3557,3586,3616,3649,3683,3719, 4438,4459,4482,4504,4528,4554,4581,4610,4640,4673,4707,4743, 5462,5483,5506,5528,5552,5578,5605,5634,5664,5697,5731,5767, 6486,6507,6530,6552,6576,6602,6629,6658,6688,6721,6755,6791, 7510}; /*** public methods **************************************/ CPlayer *CksmPlayer::factory(Copl *newopl) { return new CksmPlayer(newopl); } bool CksmPlayer::load(const char *filename, const CFileProvider &fp) { binistream *f; int i; char *fn = new char[strlen (filename) + 9]; // file validation section if(!fp.extension(filename, ".ksm")) { AdPlug_LogWrite("CksmPlayer::load(,\"%s\"): File doesn't have '.ksm' " "extension! Rejected!\n", filename); delete[] fn; return false; } AdPlug_LogWrite("*** CksmPlayer::load(,\"%s\") ***\n", filename); // Load instruments from 'insts.dat' strcpy(fn, filename); for(i = strlen(fn) - 1; i >= 0; i--) if(fn[i] == '/' || fn[i] == '\\') break; strcpy(fn + i + 1, "insts.dat"); AdPlug_LogWrite("Instruments file: \"%s\"\n", fn); f = fp.open(fn); delete [] fn; if(!f) { AdPlug_LogWrite("Couldn't open instruments file! Aborting!\n"); AdPlug_LogWrite("--- CksmPlayer::load ---\n"); return false; } loadinsts(f); fp.close(f); f = fp.open(filename); if(!f) return false; for(i = 0; i < 16; i++) trinst[i] = f->readInt(1); for(i = 0; i < 16; i++) trquant[i] = f->readInt(1); for(i = 0; i < 16; i++) trchan[i] = f->readInt(1); f->ignore(16); for(i = 0; i < 16; i++) trvol[i] = f->readInt(1); numnotes = f->readInt(2); note = new unsigned long [numnotes]; for(i = 0; i < numnotes; i++) note[i] = f->readInt(4); fp.close(f); if(!trchan[11]) { drumstat = 0; numchans = 9; } else { drumstat = 32; numchans = 6; } rewind(0); AdPlug_LogWrite("--- CksmPlayer::load ---\n"); return true; } bool CksmPlayer::update() { int quanter,chan,drumnum,freq,track,volevel,volval; unsigned int i,j,bufnum; unsigned long temp,templong; count++; if (count >= countstop) { bufnum = 0; while (count >= countstop) { templong = note[nownote]; track = (int)((templong>>8)&15); if ((templong&192) == 0) { i = 0; while ((i < numchans) && ((chanfreq[i] != (templong&63)) || (chantrack[i] != ((templong>>8)&15)))) i++; if (i < numchans) { databuf[bufnum] = (char)0; bufnum++; databuf[bufnum] = (unsigned char)(0xb0+i); bufnum++; databuf[bufnum] = (unsigned char)((adlibfreq[templong&63]>>8)&223); bufnum++; chanfreq[i] = 0; chanage[i] = 0; } } else { volevel = trvol[track]; if ((templong&192) == 128) { volevel -= 4; if (volevel < 0) volevel = 0; } if ((templong&192) == 192) { volevel += 4; if (volevel > 63) volevel = 63; } if (track < 11) { temp = 0; i = numchans; for(j=0;j= temp) && (chantrack[j] == track)) { temp = countstop - chanage[j]; i = j; } if (i < numchans) { databuf[bufnum] = (char)0, bufnum++; databuf[bufnum] = (unsigned char)(0xb0+i); bufnum++; databuf[bufnum] = (unsigned char)0; bufnum++; volval = (inst[trinst[track]][1]&192)+(volevel^63); databuf[bufnum] = (char)0, bufnum++; databuf[bufnum] = (unsigned char)(0x40+op_table[i]+3); bufnum++; databuf[bufnum] = (unsigned char)volval; bufnum++; databuf[bufnum] = (char)0, bufnum++; databuf[bufnum] = (unsigned char)(0xa0+i); bufnum++; databuf[bufnum] = (unsigned char)(adlibfreq[templong&63]&255); bufnum++; databuf[bufnum] = (char)0, bufnum++; databuf[bufnum] = (unsigned char)(0xb0+i); bufnum++; databuf[bufnum] = (unsigned char)((adlibfreq[templong&63]>>8)|32); bufnum++; chanfreq[i] = templong&63; chanage[i] = countstop; } } else if ((drumstat&32) > 0) { freq = adlibfreq[templong&63]; switch(track) { case 11: drumnum = 16; chan = 6; freq -= 2048; break; case 12: drumnum = 8; chan = 7; freq -= 2048; break; case 13: drumnum = 4; chan = 8; break; case 14: drumnum = 2; chan = 8; break; case 15: drumnum = 1; chan = 7; freq -= 2048; break; } databuf[bufnum] = (char)0, bufnum++; databuf[bufnum] = (unsigned char)(0xa0+chan); bufnum++; databuf[bufnum] = (unsigned char)(freq&255); bufnum++; databuf[bufnum] = (char)0, bufnum++; databuf[bufnum] = (unsigned char)(0xb0+chan); bufnum++; databuf[bufnum] = (unsigned char)((freq>>8)&223); bufnum++; databuf[bufnum] = (char)0, bufnum++; databuf[bufnum] = (unsigned char)(0xbd); bufnum++; databuf[bufnum] = (unsigned char)(drumstat&(255-drumnum)); bufnum++; drumstat |= drumnum; if ((track == 11) || (track == 12) || (track == 14)) { volval = (inst[trinst[track]][1]&192)+(volevel^63); databuf[bufnum] = (char)0, bufnum++; databuf[bufnum] = (unsigned char)(0x40+op_table[chan]+3); bufnum++; databuf[bufnum] = (unsigned char)(volval); bufnum++; } else { volval = (inst[trinst[track]][6]&192)+(volevel^63); databuf[bufnum] = (char)0, bufnum++; databuf[bufnum] = (unsigned char)(0x40+op_table[chan]); bufnum++; databuf[bufnum] = (unsigned char)(volval); bufnum++; } databuf[bufnum] = (char)0, bufnum++; databuf[bufnum] = (unsigned char)(0xbd); bufnum++; databuf[bufnum] = (unsigned char)(drumstat); bufnum++; } } nownote++; if (nownote >= numnotes) { nownote = 0; songend = true; } templong = note[nownote]; if (nownote == 0) count = (templong>>12)-1; quanter = (240/trquant[(templong>>8)&15]); countstop = (((templong>>12)+(quanter>>1)) / quanter) * quanter; } for(i=0;iwrite(databuf[i+1],databuf[i+2]); } return !songend; } void CksmPlayer::rewind(int subsong) { unsigned int i,j,k; unsigned char instbuf[11]; unsigned long templong; songend = false; opl->init(); opl->write(1,32); opl->write(4,0); opl->write(8,0); opl->write(0xbd,drumstat); if (trchan[11] == 1) { for(i=0;i<11;i++) instbuf[i] = inst[trinst[11]][i]; instbuf[1] = ((instbuf[1]&192)|(trvol[11])^63); setinst(6,instbuf[0],instbuf[1],instbuf[2],instbuf[3],instbuf[4],instbuf[5],instbuf[6],instbuf[7],instbuf[8],instbuf[9],instbuf[10]); for(i=0;i<5;i++) instbuf[i] = inst[trinst[12]][i]; for(i=5;i<11;i++) instbuf[i] = inst[trinst[15]][i]; instbuf[1] = ((instbuf[1]&192)|(trvol[12])^63); instbuf[6] = ((instbuf[6]&192)|(trvol[15])^63); setinst(7,instbuf[0],instbuf[1],instbuf[2],instbuf[3],instbuf[4],instbuf[5],instbuf[6],instbuf[7],instbuf[8],instbuf[9],instbuf[10]); for(i=0;i<5;i++) instbuf[i] = inst[trinst[14]][i]; for(i=5;i<11;i++) instbuf[i] = inst[trinst[13]][i]; instbuf[1] = ((instbuf[1]&192)|(trvol[14])^63); instbuf[6] = ((instbuf[6]&192)|(trvol[13])^63); setinst(8,instbuf[0],instbuf[1],instbuf[2],instbuf[3],instbuf[4],instbuf[5],instbuf[6],instbuf[7],instbuf[8],instbuf[9],instbuf[10]); } for(i=0;i 0) && (j < numchans)) { k = trchan[i]; while ((j < numchans) && (k > 0)) { chantrack[j] = i; k--; j++; } } for(i=0;i>12)-1; countstop = (templong>>12)-1; nownote = 0; } const char * CksmPlayer::getinstrument(unsigned int n) { if(trchan[n]) return instname[trinst[n]]; else return ""; } /*** private methods *************************************/ void CksmPlayer::loadinsts(binistream *f) { int i, j; for(i = 0; i < 256; i++) { f->readString(instname[i], 20); for(j = 0; j < 11; j++) inst[i][j] = f->readInt(1); f->ignore(2); } } void CksmPlayer::setinst(int chan, unsigned char v0,unsigned char v1,unsigned char v2, unsigned char v3,unsigned char v4,unsigned char v5, unsigned char v6,unsigned char v7,unsigned char v8, unsigned char v9,unsigned char v10) { int offs; opl->write(0xa0+chan,0); opl->write(0xb0+chan,0); opl->write(0xc0+chan,v10); offs = op_table[chan]; opl->write(0x20+offs,v5); opl->write(0x40+offs,v6); opl->write(0x60+offs,v7); opl->write(0x80+offs,v8); opl->write(0xe0+offs,v9); offs+=3; opl->write(0x20+offs,v0); opl->write(0x40+offs,v1); opl->write(0x60+offs,v2); opl->write(0x80+offs,v3); opl->write(0xe0+offs,v4); }