diff Plugins/Input/adplug/core/bam.cpp @ 359:8df427a314a8 trunk

[svn] Adlib synthesizer (AdPlug) support.
author chainsaw
date Fri, 30 Dec 2005 16:31:39 -0800
parents
children f12d7e208b43
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Input/adplug/core/bam.cpp	Fri Dec 30 16:31:39 2005 -0800
@@ -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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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);
+}