Mercurial > audlegacy-plugins
diff src/Input/adplug/core/bam.cxx @ 0:13389e613d67 trunk
[svn] - initial import of audacious-plugins tree (lots to do)
author | nenolod |
---|---|
date | Mon, 18 Sep 2006 01:11:49 -0700 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/Input/adplug/core/bam.cxx Mon Sep 18 01:11:49 2006 -0700 @@ -0,0 +1,203 @@ +/* + * Adplug - Replayer for many OPL2/OPL3 audio file formats. + * Copyright (C) 1999 - 2003 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 + * + * bam.cpp - Bob's Adlib Music Player, by Simon Peter <dn.tlp@gmx.net> + * + * NOTES: + * In my player, the loop counter is stored with the label. This can be + * dangerous for some situations (see below), but there shouldn't be any BAM + * files triggering this situation. + * + * From SourceForge Bug #476088: + * ----------------------------- + * Using just one loop counter for each label, my player can't + * handle files that loop twice to the same label (if that's at + * all possible with BAM). Imagine the following situation: + * + * ... [*] ---- [<- *] ---- [<- *] ... + * ^ ^ ^ ^ ^ ^ ^ + * | | | | | | | + * +---|----+-----|-----+-----|----+--- normal song data + * +----------|-----------|-------- label 1 + * +-----------+-------- loop points to label 1 + * + * both loop points loop to the same label. Storing the loop + * count with the label would cause chaos with the counter, + * when the player executes the inner jump. + * ------------------ + * Not to worry. my reference implementation of BAM does not + * support the multiple loop situation you describe, and + * neither do any BAM-creation programs. Then both loops point + * to the same label, the inner loop's counter is just allowed + * to clobber the outer loop's counter. No stack is neccisary. + */ + +#include <string.h> +#include "bam.h" + +const unsigned short CbamPlayer::freq[] = {172,182,193,205,217,230,243,258,274, +290,307,326,345,365,387,410,435,460,489,517,547,580,614,651,1369,1389,1411, +1434,1459,1484,1513,1541,1571,1604,1638,1675,2393,2413,2435,2458,2483,2508, +2537,2565,2595,2628,2662,2699,3417,3437,3459,3482,3507,3532,3561,3589,3619, +3652,3686,3723,4441,4461,4483,4506,4531,4556,4585,4613,4643,4676,4710,4747, +5465,5485,5507,5530,5555,5580,5609,5637,5667,5700,5734,5771,6489,6509,6531, +6554,6579,6604,6633,6661,6691,6724,6758,6795,7513,7533,7555,7578,7603,7628, +7657,7685,7715,7748,7782,7819,7858,7898,7942,7988,8037,8089,8143,8191,8191, +8191,8191,8191,8191,8191,8191,8191,8191,8191,8191}; + +CPlayer *CbamPlayer::factory(Copl *newopl) +{ + return new CbamPlayer(newopl); +} + +bool CbamPlayer::load(const std::string &filename, const CFileProvider &fp) +{ + binistream *f = fp.open(filename); if(!f) return false; + char id[4]; + unsigned int i; + + size = fp.filesize(f) - 4; // filesize minus header + f->readString(id, 4); + if(strncmp(id,"CBMF",4)) { fp.close(f); return false; } + + song = new unsigned char [size]; + for(i = 0; i < size; i++) song[i] = f->readInt(1); + + fp.close(f); + rewind(0); + return true; +} + +bool CbamPlayer::update() +{ + unsigned char cmd,c; + + if(del) { + del--; + return !songend; + } + + if(pos >= size) { // EOF detection + pos = 0; + songend = true; + } + + while(song[pos] < 128) { + cmd = song[pos] & 240; + c = song[pos] & 15; + switch(cmd) { + case 0: // stop song + pos = 0; + songend = true; + break; + case 16: // start note + if(c < 9) { + opl->write(0xa0 + c, freq[song[++pos]] & 255); + opl->write(0xb0 + c, (freq[song[pos]] >> 8) + 32); + } else + pos++; + pos++; + break; + case 32: // stop note + if(c < 9) + opl->write(0xb0 + c, 0); + pos++; + break; + case 48: // define instrument + if(c < 9) { + opl->write(0x20 + op_table[c],song[pos+1]); + opl->write(0x23 + op_table[c],song[pos+2]); + opl->write(0x40 + op_table[c],song[pos+3]); + opl->write(0x43 + op_table[c],song[pos+4]); + opl->write(0x60 + op_table[c],song[pos+5]); + opl->write(0x63 + op_table[c],song[pos+6]); + opl->write(0x80 + op_table[c],song[pos+7]); + opl->write(0x83 + op_table[c],song[pos+8]); + opl->write(0xe0 + op_table[c],song[pos+9]); + opl->write(0xe3 + op_table[c],song[pos+10]); + opl->write(0xc0 + c,song[pos+11]); + } + pos += 12; + break; + case 80: // set label + label[c].target = ++pos; + label[c].defined = true; + break; + case 96: // jump + if(label[c].defined) + switch(song[pos+1]) { + case 254: // infinite loop + if(label[c].defined) { + pos = label[c].target; + songend = true; + break; + } + // fall through... + case 255: // chorus + if(!chorus && label[c].defined) { + chorus = true; + gosub = pos + 2; + pos = label[c].target; + break; + } + // fall through... + case 0: // end of loop + pos += 2; + break; + default: // finite loop + if(!label[c].count) { // loop elapsed + label[c].count = 255; + pos += 2; + break; + } + if(label[c].count < 255) // loop defined + label[c].count--; + else // loop undefined + label[c].count = song[pos+1] - 1; + pos = label[c].target; + break; + } + break; + case 112: // end of chorus + if(chorus) { + pos = gosub; + chorus = false; + } else + pos++; + break; + default: // reserved command (skip) + pos++; + break; + } + } + if(song[pos] >= 128) { // wait + del = song[pos] - 127; + pos++; + } + return !songend; +} + +void CbamPlayer::rewind(int subsong) +{ + int i; + + pos = 0; songend = false; del = 0; gosub = 0; chorus = false; + memset(label, 0, sizeof(label)); label[0].defined = true; + for(i = 0; i < 16; i++) label[i].count = 255; // 255 = undefined + opl->init(); opl->write(1,32); +}