summaryrefslogtreecommitdiff
path: root/plugins/adplug/adplug
diff options
context:
space:
mode:
authorGravatar Alexey Yakovenko <wakeroid@gmail.com>2010-01-09 18:15:36 +0100
committerGravatar Alexey Yakovenko <wakeroid@gmail.com>2010-01-09 18:15:36 +0100
commite12d8cdf0cc670f0f2302eab15fa9811fb5507e1 (patch)
tree66d8f655c6fadd801f6223c86c73377dec1aa64b /plugins/adplug/adplug
parent782ab60f2e459d795d7fe0c4fcd3a17f32606d54 (diff)
merged adplug-2.2
Diffstat (limited to 'plugins/adplug/adplug')
-rw-r--r--plugins/adplug/adplug/a2m.cpp2
-rw-r--r--plugins/adplug/adplug/adl.cpp14
-rw-r--r--plugins/adplug/adplug/adl.h19
-rw-r--r--plugins/adplug/adplug/adplug.cpp24
-rw-r--r--plugins/adplug/adplug/adplug.h2
-rw-r--r--plugins/adplug/adplug/bmf.cpp2
-rw-r--r--plugins/adplug/adplug/cff.cpp3
-rw-r--r--plugins/adplug/adplug/cmf.cpp776
-rw-r--r--plugins/adplug/adplug/cmf.h112
-rw-r--r--plugins/adplug/adplug/d00.cpp8
-rw-r--r--plugins/adplug/adplug/d00.h2
-rw-r--r--plugins/adplug/adplug/dro.cpp8
-rw-r--r--plugins/adplug/adplug/dro.h2
-rw-r--r--plugins/adplug/adplug/dro2.cpp142
-rw-r--r--plugins/adplug/adplug/dro2.h60
-rw-r--r--plugins/adplug/adplug/dtm.cpp2
-rw-r--r--plugins/adplug/adplug/fmc.cpp7
-rw-r--r--plugins/adplug/adplug/fprovide.h2
-rw-r--r--plugins/adplug/adplug/hsc.h12
-rw-r--r--plugins/adplug/adplug/imf.cpp5
-rw-r--r--plugins/adplug/adplug/jbm.cpp293
-rw-r--r--plugins/adplug/adplug/jbm.h80
-rw-r--r--plugins/adplug/adplug/lds.h2
-rw-r--r--plugins/adplug/adplug/mad.cpp2
-rw-r--r--plugins/adplug/adplug/mid.cpp148
-rw-r--r--plugins/adplug/adplug/mid.h2
-rw-r--r--plugins/adplug/adplug/mkj.cpp2
-rw-r--r--plugins/adplug/adplug/msc.cpp2
-rw-r--r--plugins/adplug/adplug/mtk.cpp2
-rw-r--r--plugins/adplug/adplug/player.h28
-rw-r--r--plugins/adplug/adplug/protrack.cpp24
-rw-r--r--plugins/adplug/adplug/rad.cpp2
-rw-r--r--plugins/adplug/adplug/rat.cpp2
-rw-r--r--plugins/adplug/adplug/raw.cpp2
-rw-r--r--plugins/adplug/adplug/rix.cpp2
-rw-r--r--plugins/adplug/adplug/rol.cpp3
-rw-r--r--plugins/adplug/adplug/rol.h4
-rw-r--r--plugins/adplug/adplug/s3m.cpp25
-rw-r--r--plugins/adplug/adplug/sa2.cpp5
-rw-r--r--plugins/adplug/adplug/sng.cpp2
-rw-r--r--plugins/adplug/adplug/surroundopl.cpp203
-rw-r--r--plugins/adplug/adplug/surroundopl.h69
-rw-r--r--plugins/adplug/adplug/xsm.h2
43 files changed, 1959 insertions, 151 deletions
diff --git a/plugins/adplug/adplug/a2m.cpp b/plugins/adplug/adplug/a2m.cpp
index 3fd68d98..f506275d 100644
--- a/plugins/adplug/adplug/a2m.cpp
+++ b/plugins/adplug/adplug/a2m.cpp
@@ -29,7 +29,7 @@
* Following commands are ignored: Gxy, Hxy, Kxy - &xy
*/
-#include <string.h>
+#include <cstring>
#include "a2m.h"
const unsigned int Ca2mLoader::MAXFREQ = 2000,
diff --git a/plugins/adplug/adplug/adl.cpp b/plugins/adplug/adplug/adl.cpp
index 40896fdb..ae8c4e99 100644
--- a/plugins/adplug/adplug/adl.cpp
+++ b/plugins/adplug/adplug/adl.cpp
@@ -44,14 +44,14 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* $URL: https://svn.sourceforge.net/svnroot/scummvm/scummvm/trunk/engines/kyra/sound_adlib.cpp $
- * $Id: adl.cpp,v 1.9 2006/08/16 00:20:45 dynamite Exp $
+ * $Id: adl.cpp,v 1.11 2008/02/11 20:18:27 dynamite Exp $
*
*/
+#include <cstring>
#include <inttypes.h>
#include <stdarg.h>
#include <assert.h>
-#include <string.h>
#include "adl.h"
#include "debug.h"
@@ -2378,16 +2378,22 @@ bool CadlPlayer::load(const std::string &filename, const CFileProvider &fp)
// _soundFileLoaded = file;
- for(int i = 0; i < 200; i++)
- if(_trackEntries[i] != 0xff)
+ // find last subsong
+ for(int i = 199; i >= 0; i--)
+ if(_trackEntries[i] != 0xff) {
numsubsongs = i + 1;
+ break;
+ }
fp.close(f);
+ cursubsong = 2;
+ rewind();
return true;
}
void CadlPlayer::rewind(int subsong)
{
+ if(subsong == -1) subsong = cursubsong;
opl->init();
opl->write(1,32);
playSoundEffect(subsong);
diff --git a/plugins/adplug/adplug/adl.h b/plugins/adplug/adplug/adl.h
index b0030c43..203692b0 100644
--- a/plugins/adplug/adplug/adl.h
+++ b/plugins/adplug/adplug/adl.h
@@ -1,6 +1,20 @@
/*
* Adplug - Replayer for many OPL2/OPL3 audio file formats.
- * Copyright (C) 1999 - 2006 Simon Peter, <dn.tlp@gmx.net>, et al.
+ * Copyright (C) 1999 - 2007 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
*
* adl.h - ADL player adaption by Simon Peter <dn.tlp@gmx.net>
*/
@@ -24,7 +38,7 @@ class CadlPlayer: public CPlayer
bool load(const std::string &filename, const CFileProvider &fp);
bool update();
- void rewind(int subsong);
+ void rewind(int subsong = -1);
// refresh rate is fixed at 72Hz
float getrefresh()
@@ -33,6 +47,7 @@ class CadlPlayer: public CPlayer
}
unsigned int getsubsongs();
+ unsigned int getsubsong() { return cursubsong; }
std::string gettype() { return std::string("Westwood ADL"); }
private:
diff --git a/plugins/adplug/adplug/adplug.cpp b/plugins/adplug/adplug/adplug.cpp
index 32fea695..d02bb047 100644
--- a/plugins/adplug/adplug/adplug.cpp
+++ b/plugins/adplug/adplug/adplug.cpp
@@ -1,6 +1,6 @@
/*
* Adplug - Replayer for many OPL2/OPL3 audio file formats.
- * Copyright (C) 1999 - 2006 Simon Peter <dn.tlp@gmx.net>, et al.
+ * Copyright (C) 1999 - 2008 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
@@ -19,9 +19,9 @@
* adplug.cpp - CAdPlug utility class, by Simon Peter <dn.tlp@gmx.net>
*/
+#include <cstring>
#include <string>
#include <binfile.h>
-#include <string.h>
#include "adplug.h"
#include "debug.h"
@@ -35,6 +35,7 @@
#include "sng.h"
#include "adtrack.h"
#include "bam.h"
+#include "cmf.h"
#include "d00.h"
#include "dfm.h"
#include "hsp.h"
@@ -62,9 +63,11 @@
#include "rol.h"
#include "xsm.h"
#include "dro.h"
+#include "dro2.h"
#include "msc.h"
#include "rix.h"
#include "adl.h"
+#include "jbm.h"
/***** CAdPlug *****/
@@ -77,12 +80,13 @@ const CPlayerDesc CAdPlug::allplayers[] = {
CPlayerDesc(CadtrackLoader::factory, "Adlib Tracker", ".sng\0"),
CPlayerDesc(CamdLoader::factory, "AMUSIC", ".amd\0"),
CPlayerDesc(CbamPlayer::factory, "Bob's Adlib Music", ".bam\0"),
+ CPlayerDesc(CcmfPlayer::factory, "Creative Music File", ".cmf\0"),
CPlayerDesc(Cd00Player::factory, "Packed EdLib", ".d00\0"),
CPlayerDesc(CdfmLoader::factory, "Digital-FM", ".dfm\0"),
CPlayerDesc(ChspLoader::factory, "HSC Packed", ".hsp\0"),
CPlayerDesc(CksmPlayer::factory, "Ken Silverman Music", ".ksm\0"),
CPlayerDesc(CmadLoader::factory, "Mlat Adlib Tracker", ".mad\0"),
- CPlayerDesc(CmidPlayer::factory, "MIDI", ".mid\0.cmf\0.sci\0.laa\0"),
+ CPlayerDesc(CmidPlayer::factory, "MIDI", ".mid\0.sci\0.laa\0"),
CPlayerDesc(CmkjPlayer::factory, "MKJamz", ".mkj\0"),
CPlayerDesc(CcffLoader::factory, "Boomtracker", ".cff\0"),
CPlayerDesc(CdmoLoader::factory, "TwinTeam", ".dmo\0"),
@@ -103,10 +107,12 @@ const CPlayerDesc CAdPlug::allplayers[] = {
CPlayerDesc(Cu6mPlayer::factory, "Ultima 6 Music", ".m\0"),
CPlayerDesc(CrolPlayer::factory, "Adlib Visual Composer", ".rol\0"),
CPlayerDesc(CxsmPlayer::factory, "eXtra Simple Music", ".xsm\0"),
- CPlayerDesc(CdroPlayer::factory, "DOSBox Raw OPL", ".dro\0"),
+ CPlayerDesc(CdroPlayer::factory, "DOSBox Raw OPL v0.1", ".dro\0"),
+ CPlayerDesc(Cdro2Player::factory, "DOSBox Raw OPL v2.0", ".dro\0"),
CPlayerDesc(CmscPlayer::factory, "Adlib MSC Player", ".msc\0"),
CPlayerDesc(CrixPlayer::factory, "Softstar RIX OPL Music", ".rix\0"),
CPlayerDesc(CadlPlayer::factory, "Westwood ADL", ".adl\0"),
+ CPlayerDesc(CjbmPlayer::factory, "JBM Adlib Music", ".jbm\0"),
CPlayerDesc()
};
@@ -124,39 +130,41 @@ const CPlayers &CAdPlug::init_players(const CPlayerDesc pd[])
const CPlayers CAdPlug::players = CAdPlug::init_players(CAdPlug::allplayers);
CAdPlugDatabase *CAdPlug::database = 0;
-CPlayer *CAdPlug::factory(const char *fn, Copl *opl, const CPlayers &pl,
+CPlayer *CAdPlug::factory(const std::string &fn, Copl *opl, const CPlayers &pl,
const CFileProvider &fp)
{
CPlayer *p;
CPlayers::const_iterator i;
unsigned int j;
- AdPlug_LogWrite("*** CAdPlug::factory(\"%s\",opl,fp) ***\n", fn);
+ AdPlug_LogWrite("*** CAdPlug::factory(\"%s\",opl,fp) ***\n", fn.c_str());
// Try a direct hit by file extension
for(i = pl.begin(); i != pl.end(); i++)
for(j = 0; (*i)->get_extension(j); j++)
if(fp.extension(fn, (*i)->get_extension(j))) {
AdPlug_LogWrite("Trying direct hit: %s\n", (*i)->filetype.c_str());
- if((p = (*i)->factory(opl)))
+ if((p = (*i)->factory(opl))) {
if(p->load(fn, fp)) {
AdPlug_LogWrite("got it!\n");
AdPlug_LogWrite("--- CAdPlug::factory ---\n");
return p;
} else
delete p;
+ }
}
// Try all players, one by one
for(i = pl.begin(); i != pl.end(); i++) {
AdPlug_LogWrite("Trying: %s\n", (*i)->filetype.c_str());
- if((p = (*i)->factory(opl)))
+ if((p = (*i)->factory(opl))) {
if(p->load(fn, fp)) {
AdPlug_LogWrite("got it!\n");
AdPlug_LogWrite("--- CAdPlug::factory ---\n");
return p;
} else
delete p;
+ }
}
// Unknown file
diff --git a/plugins/adplug/adplug/adplug.h b/plugins/adplug/adplug/adplug.h
index 54ee042a..e5aec896 100644
--- a/plugins/adplug/adplug/adplug.h
+++ b/plugins/adplug/adplug/adplug.h
@@ -37,7 +37,7 @@ class CAdPlug
public:
static const CPlayers players;
- static CPlayer *factory(const char *fn, Copl *opl,
+ static CPlayer *factory(const std::string &fn, Copl *opl,
const CPlayers &pl = players,
const CFileProvider &fp = CProvider_Filesystem());
diff --git a/plugins/adplug/adplug/bmf.cpp b/plugins/adplug/adplug/bmf.cpp
index df403c86..469fde43 100644
--- a/plugins/adplug/adplug/bmf.cpp
+++ b/plugins/adplug/adplug/bmf.cpp
@@ -40,7 +40,7 @@
comment : inaccurate replaying, because constant outport; in original player it can be 380 or 382.
*/
-#include <string.h>
+#include <cstring>
#include "bmf.h"
#include "debug.h"
diff --git a/plugins/adplug/adplug/cff.cpp b/plugins/adplug/adplug/cff.cpp
index 37197fd9..68a11692 100644
--- a/plugins/adplug/adplug/cff.cpp
+++ b/plugins/adplug/adplug/cff.cpp
@@ -1,6 +1,6 @@
/*
AdPlug - Replayer for many OPL2/OPL3 audio file formats.
- Copyright (C) 1999 - 2006 Simon Peter <dn.tlp@gmx.net>, et al.
+ Copyright (C) 1999 - 2008 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
@@ -24,6 +24,7 @@
slides use previous effect data instead of current.
*/
+#include <cstring>
#include <stdlib.h>
#include <string.h>
diff --git a/plugins/adplug/adplug/cmf.cpp b/plugins/adplug/adplug/cmf.cpp
new file mode 100644
index 00000000..cd3d8006
--- /dev/null
+++ b/plugins/adplug/adplug/cmf.cpp
@@ -0,0 +1,776 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2009 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
+ *
+ * cmf.cpp - CMF player by Adam Nielsen <malvineous@shikadi.net>
+ * Subset of CMF reader in MOPL code (Malvineous' OPL player), no seeking etc.
+ */
+
+#include <stdint.h> // for uintxx_t
+#include <cassert>
+#include <math.h> // for pow() etc.
+#include <string.h> // for memset
+#include "debug.h"
+#include "cmf.h"
+
+// ------------------------------
+// OPTIONS
+// ------------------------------
+
+// The official Creative Labs CMF player seems to ignore the note velocity
+// (playing every note at the same volume), but you can uncomment this to
+// allow the note velocity to affect the volume (as presumably the composer
+// originally intended.)
+//
+//#define USE_VELOCITY
+//
+// The Xargon demo song is a good example of a song that uses note velocity.
+
+// OPL register offsets
+#define BASE_CHAR_MULT 0x20
+#define BASE_SCAL_LEVL 0x40
+#define BASE_ATCK_DCAY 0x60
+#define BASE_SUST_RLSE 0x80
+#define BASE_FNUM_L 0xA0
+#define BASE_KEYON_FREQ 0xB0
+#define BASE_RHYTHM 0xBD
+#define BASE_WAVE 0xE0
+#define BASE_FEED_CONN 0xC0
+
+#define OPLBIT_KEYON 0x20 // Bit in BASE_KEYON_FREQ register for turning a note on
+
+// Supplied with a channel, return the offset from a base OPL register for the
+// Modulator cell (e.g. channel 4's modulator is at offset 0x09. Since 0x60 is
+// the attack/decay function, register 0x69 will thus set the attack/decay for
+// channel 4's modulator.) (channels go from 0 to 8 inclusive)
+#define OPLOFFSET(channel) (((channel) / 3) * 8 + ((channel) % 3))
+
+// These 16 instruments are repeated to fill up the 128 available slots. A CMF
+// file can override none/some/all of the 128 slots with custom instruments,
+// so any that aren't overridden are still available for use with these default
+// patches. The Word Rescue CMFs are good examples of songs that rely on these
+// default patches.
+uint8_t cDefaultPatches[] =
+"\x01\x11\x4F\x00\xF1\xD2\x53\x74\x00\x00\x06"
+"\x07\x12\x4F\x00\xF2\xF2\x60\x72\x00\x00\x08"
+"\x31\xA1\x1C\x80\x51\x54\x03\x67\x00\x00\x0E"
+"\x31\xA1\x1C\x80\x41\x92\x0B\x3B\x00\x00\x0E"
+"\x31\x16\x87\x80\xA1\x7D\x11\x43\x00\x00\x08"
+"\x30\xB1\xC8\x80\xD5\x61\x19\x1B\x00\x00\x0C"
+"\xF1\x21\x01\x00\x97\xF1\x17\x18\x00\x00\x08"
+"\x32\x16\x87\x80\xA1\x7D\x10\x33\x00\x00\x08"
+"\x01\x12\x4F\x00\x71\x52\x53\x7C\x00\x00\x0A"
+"\x02\x03\x8D\x00\xD7\xF5\x37\x18\x00\x00\x04"
+"\x21\x21\xD1\x00\xA3\xA4\x46\x25\x00\x00\x0A"
+"\x22\x22\x0F\x00\xF6\xF6\x95\x36\x00\x00\x0A"
+"\xE1\xE1\x00\x00\x44\x54\x24\x34\x02\x02\x07"
+"\xA5\xB1\xD2\x80\x81\xF1\x03\x05\x00\x00\x02"
+"\x71\x22\xC5\x00\x6E\x8B\x17\x0E\x00\x00\x02"
+"\x32\x21\x16\x80\x73\x75\x24\x57\x00\x00\x0E";
+
+
+CPlayer *CcmfPlayer::factory(Copl *newopl)
+{
+ return new CcmfPlayer(newopl);
+}
+
+CcmfPlayer::CcmfPlayer(Copl *newopl) :
+ CPlayer(newopl),
+ data(NULL),
+ pInstruments(NULL),
+ bPercussive(false),
+ iTranspose(0),
+ iPrevCommand(0)
+{
+ assert(OPLOFFSET(1-1) == 0x00);
+ assert(OPLOFFSET(5-1) == 0x09);
+ assert(OPLOFFSET(9-1) == 0x12);
+}
+
+CcmfPlayer::~CcmfPlayer()
+{
+ if (this->data) delete[] data;
+}
+
+bool CcmfPlayer::load(const std::string &filename, const CFileProvider &fp)
+{
+ binistream *f = fp.open(filename); if(!f) return false;
+
+ char cSig[4];
+ f->readString(cSig, 4);
+ if (
+ (cSig[0] != 'C') ||
+ (cSig[1] != 'T') ||
+ (cSig[2] != 'M') ||
+ (cSig[3] != 'F')
+ ) {
+ // Not a CMF file
+ fp.close(f);
+ return false;
+ }
+ uint16_t iVer = f->readInt(2);
+ if ((iVer != 0x0101) && (iVer != 0x0100)) {
+ AdPlug_LogWrite("CMF file is not v1.0 or v1.1 (reports %d.%d)\n", iVer >> 8 , iVer & 0xFF);
+ fp.close(f);
+ return false;
+ }
+
+ this->cmfHeader.iInstrumentBlockOffset = f->readInt(2);
+ this->cmfHeader.iMusicOffset = f->readInt(2);
+ this->cmfHeader.iTicksPerQuarterNote = f->readInt(2);
+ this->cmfHeader.iTicksPerSecond = f->readInt(2);
+ this->cmfHeader.iTagOffsetTitle = f->readInt(2);
+ this->cmfHeader.iTagOffsetComposer = f->readInt(2);
+ this->cmfHeader.iTagOffsetRemarks = f->readInt(2);
+ f->readString((char *)this->cmfHeader.iChannelsInUse, 16);
+ this->cmfHeader.iNumInstruments = f->readInt(2);
+ this->cmfHeader.iTempo = f->readInt(2);
+
+ // Load the instruments
+
+ f->seek(this->cmfHeader.iInstrumentBlockOffset);
+ this->pInstruments = new SBI[128]; // Always 128 available for use
+
+ for (int i = 0; i < this->cmfHeader.iNumInstruments; i++) {
+ this->pInstruments[i].op[0].iCharMult = f->readInt(1);
+ this->pInstruments[i].op[1].iCharMult = f->readInt(1);
+ this->pInstruments[i].op[0].iScalingOutput = f->readInt(1);
+ this->pInstruments[i].op[1].iScalingOutput = f->readInt(1);
+ this->pInstruments[i].op[0].iAttackDecay = f->readInt(1);
+ this->pInstruments[i].op[1].iAttackDecay = f->readInt(1);
+ this->pInstruments[i].op[0].iSustainRelease = f->readInt(1);
+ this->pInstruments[i].op[1].iSustainRelease = f->readInt(1);
+ this->pInstruments[i].op[0].iWaveSel = f->readInt(1);
+ this->pInstruments[i].op[1].iWaveSel = f->readInt(1);
+ this->pInstruments[i].iConnection = f->readInt(1);
+ f->seek(5, binio::Add); // skip over the padding bytes
+ }
+
+ // Set the rest of the instruments to the CMF defaults
+ for (int i = this->cmfHeader.iNumInstruments; i < 128; i++) {
+ this->pInstruments[i].op[0].iCharMult = cDefaultPatches[(i % 16) * 11 + 0];
+ this->pInstruments[i].op[1].iCharMult = cDefaultPatches[(i % 16) * 11 + 1];
+ this->pInstruments[i].op[0].iScalingOutput = cDefaultPatches[(i % 16) * 11 + 2];
+ this->pInstruments[i].op[1].iScalingOutput = cDefaultPatches[(i % 16) * 11 + 3];
+ this->pInstruments[i].op[0].iAttackDecay = cDefaultPatches[(i % 16) * 11 + 4];
+ this->pInstruments[i].op[1].iAttackDecay = cDefaultPatches[(i % 16) * 11 + 5];
+ this->pInstruments[i].op[0].iSustainRelease = cDefaultPatches[(i % 16) * 11 + 6];
+ this->pInstruments[i].op[1].iSustainRelease = cDefaultPatches[(i % 16) * 11 + 7];
+ this->pInstruments[i].op[0].iWaveSel = cDefaultPatches[(i % 16) * 11 + 8];
+ this->pInstruments[i].op[1].iWaveSel = cDefaultPatches[(i % 16) * 11 + 9];
+ this->pInstruments[i].iConnection = cDefaultPatches[(i % 16) * 11 + 10];
+ }
+
+ if (this->cmfHeader.iTagOffsetTitle) {
+ f->seek(this->cmfHeader.iTagOffsetTitle);
+ this->strTitle = f->readString('\0');
+ }
+ if (this->cmfHeader.iTagOffsetComposer) {
+ f->seek(this->cmfHeader.iTagOffsetComposer);
+ this->strComposer = f->readString('\0');
+ }
+ if (this->cmfHeader.iTagOffsetRemarks) {
+ f->seek(this->cmfHeader.iTagOffsetRemarks);
+ this->strRemarks = f->readString('\0');
+ }
+
+ // Load the MIDI data into memory
+ f->seek(this->cmfHeader.iMusicOffset);
+ this->iSongLen = fp.filesize(f) - this->cmfHeader.iMusicOffset;
+ this->data = new unsigned char[this->iSongLen];
+ f->readString((char *)data, this->iSongLen);
+
+ fp.close(f);
+ rewind(0);
+
+ return true;
+}
+
+bool CcmfPlayer::update()
+{
+ // This has to be here and not in getrefresh() for some reason.
+ this->iDelayRemaining = 0;
+
+ // Read in the next event
+ while (!this->iDelayRemaining) {
+ uint8_t iCommand = this->data[this->iPlayPointer++];
+ if ((iCommand & 0x80) == 0) {
+ // Running status, use previous command
+ this->iPlayPointer--;
+ iCommand = this->iPrevCommand;
+ } else {
+ this->iPrevCommand = iCommand;
+ }
+ uint8_t iChannel = iCommand & 0x0F;
+ switch (iCommand & 0xF0) {
+ case 0x80: { // Note off (two data bytes)
+ uint8_t iNote = this->data[this->iPlayPointer++];
+ uint8_t iVelocity = this->data[this->iPlayPointer++]; // release velocity
+ this->cmfNoteOff(iChannel, iNote, iVelocity);
+ break;
+ }
+ case 0x90: { // Note on (two data bytes)
+ uint8_t iNote = this->data[this->iPlayPointer++];
+ uint8_t iVelocity = this->data[this->iPlayPointer++]; // attack velocity
+ if (iVelocity) {
+ this->cmfNoteOn(iChannel, iNote, iVelocity);
+ } else {
+ // This is a note-off instead (velocity == 0)
+ this->cmfNoteOff(iChannel, iNote, iVelocity); // 64 is the MIDI default note-off velocity
+ break;
+ }
+ break;
+ }
+ case 0xA0: { // Polyphonic key pressure (two data bytes)
+ uint8_t iNote = this->data[this->iPlayPointer++];
+ uint8_t iPressure = this->data[this->iPlayPointer++];
+ AdPlug_LogWrite("CMF: Key pressure not yet implemented! (wanted ch%d/note %d set to %d)\n", iChannel, iNote, iPressure);
+ break;
+ }
+ case 0xB0: { // Controller (two data bytes)
+ uint8_t iController = this->data[this->iPlayPointer++];
+ uint8_t iValue = this->data[this->iPlayPointer++];
+ this->MIDIcontroller(iChannel, iController, iValue);
+ break;
+ }
+ case 0xC0: { // Instrument change (one data byte)
+ uint8_t iNewInstrument = this->data[this->iPlayPointer++];
+ this->chMIDI[iChannel].iPatch = iNewInstrument;
+ AdPlug_LogWrite("CMF: Remembering MIDI channel %d now uses patch %d\n", iChannel, iNewInstrument);
+ break;
+ }
+ case 0xD0: { // Channel pressure (one data byte)
+ uint8_t iPressure = this->data[this->iPlayPointer++];
+ AdPlug_LogWrite("CMF: Channel pressure not yet implemented! (wanted ch%d set to %d)\n", iChannel, iPressure);
+ break;
+ }
+ case 0xE0: { // Pitch bend (two data bytes)
+ uint8_t iLSB = this->data[this->iPlayPointer++];
+ uint8_t iMSB = this->data[this->iPlayPointer++];
+ uint16_t iValue = (iMSB << 7) | iLSB;
+ // 8192 is middle/off, 0 is -2 semitones, 16384 is +2 semitones
+ this->chMIDI[iChannel].iPitchbend = iValue;
+ AdPlug_LogWrite("CMF: Channel %d pitchbent to %d (%+.2f)\n", iChannel + 1, iValue, (float)(iValue - 8192) / 8192);
+ break;
+ }
+ case 0xF0: // System message (arbitrary data bytes)
+ switch (iCommand) {
+ case 0xF0: { // Sysex
+ uint8_t iNextByte;
+ AdPlug_LogWrite("Sysex message: ");
+ do {
+ iNextByte = this->data[this->iPlayPointer++];
+ AdPlug_LogWrite("%02X", iNextByte);
+ } while ((iNextByte & 0x80) == 0);
+ AdPlug_LogWrite("\n");
+ // This will have read in the terminating EOX (0xF7) message too
+ break;
+ }
+ case 0xF1: // MIDI Time Code Quarter Frame
+ this->data[this->iPlayPointer++]; // message data (ignored)
+ break;
+ case 0xF2: // Song position pointer
+ this->data[this->iPlayPointer++]; // message data (ignored)
+ this->data[this->iPlayPointer++];
+ break;
+ case 0xF3: // Song select
+ this->data[this->iPlayPointer++]; // message data (ignored)
+ AdPlug_LogWrite("CMF: MIDI Song Select is not implemented.\n");
+ break;
+ case 0xF6: // Tune request
+ break;
+ case 0xF7: // End of System Exclusive (EOX) - should never be read, should be absorbed by Sysex handling code
+ break;
+
+ // These messages are "real time", meaning they can be sent between
+ // the bytes of other messages - but we're lazy and don't handle these
+ // here (hopefully they're not necessary in a MIDI file, and even less
+ // likely to occur in a CMF.)
+ case 0xF8: // Timing clock (sent 24 times per quarter note, only when playing)
+ case 0xFA: // Start
+ case 0xFB: // Continue
+ case 0xFE: // Active sensing (sent every 300ms or MIDI connection assumed lost)
+ break;
+ case 0xFC: // Stop
+ AdPlug_LogWrite("CMF: Received Real Time Stop message (0xFC)\n");
+ this->bSongEnd = true;
+ this->iPlayPointer = 0; // for repeat in endless-play mode
+ break;
+ case 0xFF: { // System reset, used as meta-events in a MIDI file
+ uint8_t iEvent = this->data[this->iPlayPointer++];
+ switch (iEvent) {
+ case 0x2F: // end of track
+ AdPlug_LogWrite("CMF: End-of-track, stopping playback\n");
+ this->bSongEnd = true;
+ this->iPlayPointer = 0; // for repeat in endless-play mode
+ break;
+ default:
+ AdPlug_LogWrite("CMF: Unknown MIDI meta-event 0xFF 0x%02X\n", iEvent);
+ break;
+ }
+ break;
+ }
+ default:
+ AdPlug_LogWrite("CMF: Unknown MIDI system command 0x%02X\n", iCommand);
+ break;
+ }
+ break;
+ default:
+ AdPlug_LogWrite("CMF: Unknown MIDI command 0x%02X\n", iCommand);
+ break;
+ }
+
+ if (this->iPlayPointer >= this->iSongLen) {
+ this->bSongEnd = true;
+ this->iPlayPointer = 0; // for repeat in endless-play mode
+ }
+
+ // Read in the number of ticks until the next event
+ this->iDelayRemaining = this->readMIDINumber();
+ }
+
+ return !this->bSongEnd;
+}
+
+void CcmfPlayer::rewind(int subsong)
+{
+ this->opl->init();
+
+ // Initialise
+
+ // Enable use of WaveSel register on OPL3 (even though we're only an OPL2!)
+ // Apparently this enables nine-channel mode?
+ this->writeOPL(0x01, 0x20);
+
+ // Really make sure CSM+SEL are off (again, Creative's player...)
+ this->writeOPL(0x08, 0x00);
+
+ // This freq setting is required for the hihat to sound correct at the start
+ // of funky.cmf, even though it's for an unrelated channel.
+ // If it's here however, it makes the hihat in Word Rescue's theme.cmf
+ // sound really bad.
+ // TODO: How do we figure out whether we need it or not???
+ this->writeOPL(BASE_FNUM_L + 8, 514 & 0xFF);
+ this->writeOPL(BASE_KEYON_FREQ + 8, (1 << 2) | (514 >> 8));
+
+ // default freqs?
+ this->writeOPL(BASE_FNUM_L + 7, 509 & 0xFF);
+ this->writeOPL(BASE_KEYON_FREQ + 7, (2 << 2) | (509 >> 8));
+ this->writeOPL(BASE_FNUM_L + 6, 432 & 0xFF);
+ this->writeOPL(BASE_KEYON_FREQ + 6, (2 << 2) | (432 >> 8));
+
+ // Amplify AM + VIB depth. Creative's CMF player does this, and there
+ // doesn't seem to be any way to stop it from doing so - except for the
+ // non-standard controller 0x63 I added :-)
+ this->writeOPL(0xBD, 0xC0);
+
+ this->bSongEnd = false;
+ this->iPlayPointer = 0;
+ this->iPrevCommand = 0; // just in case
+
+ // Read in the number of ticks until the first event
+ this->iDelayRemaining = this->readMIDINumber();
+
+ // Reset song state. This used to be in the constructor, but the XMMS2
+ // plugin sets the song length before starting playback. AdPlug plays the
+ // song in its entirety (with no synth) to determine the song length, which
+ // results in the state variables below matching the end of the song. When
+ // the real OPL synth is activated for playback, it no longer matches the
+ // state variables and the instruments are not set correctly!
+ for (int i = 0; i < 9; i++) {
+ this->chOPL[i].iNoteStart = 0; // no note playing atm
+ this->chOPL[i].iMIDINote = -1;
+ this->chOPL[i].iMIDIChannel = -1;
+ this->chOPL[i].iMIDIPatch = -1;
+
+ this->chMIDI[i].iPatch = -2;
+ this->chMIDI[i].iPitchbend = 8192;
+ }
+ for (int i = 9; i < 16; i++) {
+ this->chMIDI[i].iPatch = -2;
+ this->chMIDI[i].iPitchbend = 8192;
+ }
+
+ memset(this->iCurrentRegs, 0, 256);
+
+ return;
+}
+
+// Return value: 1 == 1 second, 2 == 0.5 seconds
+float CcmfPlayer::getrefresh()
+{
+ if (this->iDelayRemaining) {
+ return (float)this->cmfHeader.iTicksPerSecond / (float)this->iDelayRemaining;
+ } else {
+ // Delay-remaining is zero (e.g. start of song) so use a tiny delay
+ return this->cmfHeader.iTicksPerSecond; // wait for one tick
+ }
+}
+
+std::string CcmfPlayer::gettitle()
+{
+ return this->strTitle;
+}
+std::string CcmfPlayer::getauthor()
+{
+ return this->strComposer;
+}
+std::string CcmfPlayer::getdesc()
+{
+ return this->strRemarks;
+}
+
+
+//
+// PROTECTED
+//
+
+// Read a variable-length integer from MIDI data
+uint32_t CcmfPlayer::readMIDINumber()
+{
+ uint32_t iValue = 0;
+ for (int i = 0; i < 4; i++) {
+ uint8_t iNext = this->data[this->iPlayPointer++];
+ iValue <<= 7;
+ iValue |= (iNext & 0x7F); // ignore the MSB
+ if ((iNext & 0x80) == 0) break; // last byte has the MSB unset
+ }
+ return iValue;
+}
+
+// iChannel: OPL channel (0-8)
+// iOperator: 0 == Modulator, 1 == Carrier
+// Source - source operator to read from instrument definition
+// Dest - destination operator on OPL chip
+// iInstrument: Index into this->pInstruments array of CMF instruments
+void CcmfPlayer::writeInstrumentSettings(uint8_t iChannel, uint8_t iOperatorSource, uint8_t iOperatorDest, uint8_t iInstrument)
+{
+ assert(iChannel <= 8);
+
+ uint8_t iOPLOffset = OPLOFFSET(iChannel);
+ if (iOperatorDest) iOPLOffset += 3; // Carrier if iOperator == 1 (else Modulator)
+
+ this->writeOPL(BASE_CHAR_MULT + iOPLOffset, this->pInstruments[iInstrument].op[iOperatorSource].iCharMult);
+ this->writeOPL(BASE_SCAL_LEVL + iOPLOffset, this->pInstruments[iInstrument].op[iOperatorSource].iScalingOutput);
+ this->writeOPL(BASE_ATCK_DCAY + iOPLOffset, this->pInstruments[iInstrument].op[iOperatorSource].iAttackDecay);
+ this->writeOPL(BASE_SUST_RLSE + iOPLOffset, this->pInstruments[iInstrument].op[iOperatorSource].iSustainRelease);
+ this->writeOPL(BASE_WAVE + iOPLOffset, this->pInstruments[iInstrument].op[iOperatorSource].iWaveSel);
+
+ // TODO: Check to see whether we should only be loading this for one or both operators
+ this->writeOPL(BASE_FEED_CONN + iChannel, this->pInstruments[iInstrument].iConnection);
+ return;
+}
+
+// Write a byte to the OPL "chip" and update the current record of register states
+void CcmfPlayer::writeOPL(uint8_t iRegister, uint8_t iValue)
+{
+ this->opl->write(iRegister, iValue);
+ this->iCurrentRegs[iRegister] = iValue;
+ return;
+}
+
+void CcmfPlayer::cmfNoteOn(uint8_t iChannel, uint8_t iNote, uint8_t iVelocity)
+{
+ uint8_t iBlock = iNote / 12;
+ if (iBlock > 1) iBlock--; // keep in the same range as the Creative player
+ //if (iBlock > 7) iBlock = 7; // don't want to go out of range
+
+ double d = pow(2, (
+ (double)iNote + (
+ (this->chMIDI[iChannel].iPitchbend - 8192) / 8192.0
+ ) + (
+ this->iTranspose / 128
+ ) - 9) / 12.0 - (iBlock - 20))
+ * 440.0 / 32.0 / 50000.0;
+ uint16_t iOPLFNum = (uint16_t)(d+0.5);
+ if (iOPLFNum > 1023) AdPlug_LogWrite("CMF: This note is out of range! (send this song to malvineous@shikadi.net!)\n");
+
+ // See if we're playing a rhythm mode percussive instrument
+ if ((iChannel > 10) && (this->bPercussive)) {
+ uint8_t iPercChannel = this->getPercChannel(iChannel);
+
+ // Will have to set every time (easier) than figuring out whether the mod
+ // or car needs to be changed.
+ //if (this->chOPL[iPercChannel].iMIDIPatch != this->chMIDI[iChannel].iPatch) {
+ this->MIDIchangeInstrument(iPercChannel, iChannel, this->chMIDI[iChannel].iPatch);
+ //}
+
+ /* Velocity calculations - TODO: Work out the proper formula
+
+ iVelocity -> iLevel (values generated by Creative's player)
+ 7f -> 00
+ 7c -> 00
+
+ 7b -> 09
+ 73 -> 0a
+ 6b -> 0b
+ 63 -> 0c
+ 5b -> 0d
+ 53 -> 0e
+ 4b -> 0f
+ 43 -> 10
+ 3b -> 11
+ 33 -> 13
+ 2b -> 15
+ 23 -> 19
+ 1b -> 1b
+ 13 -> 1d
+ 0b -> 1f
+ 03 -> 21
+
+ 02 -> 21
+ 00 -> N/A (note off)
+ */
+ // Approximate formula, need to figure out more accurate one (my maths isn't so good...)
+ int iLevel = 0x25 - sqrt(iVelocity * 16/*6*/);//(127 - iVelocity) * 0x20 / 127;
+ if (iVelocity > 0x7b) iLevel = 0; // full volume
+ if (iLevel < 0) iLevel = 0;
+ if (iLevel > 0x3F) iLevel = 0x3F;
+ //if (iVelocity < 0x40) iLevel = 0x10;
+
+ int iOPLOffset = BASE_SCAL_LEVL + OPLOFFSET(iPercChannel);
+ //if ((iChannel == 11) || (iChannel == 12) || (iChannel == 14)) {
+ if (iChannel == 11) iOPLOffset += 3; // only do bassdrum carrier for volume control
+ //iOPLOffset += 3; // carrier
+ this->writeOPL(iOPLOffset, (this->iCurrentRegs[iOPLOffset] & ~0x3F) | iLevel);//(iVelocity * 0x3F / 127));
+ //}
+ // Bass drum (ch11) uses both operators
+ //if (iChannel == 11) this->writeOPL(iOPLOffset + 3, (this->iCurrentRegs[iOPLOffset + 3] & ~0x3F) | iLevel);
+
+/* #ifdef USE_VELOCITY // Official CMF player seems to ignore velocity levels
+ uint16_t iLevel = 0x2F - (iVelocity * 0x2F / 127); // 0x2F should be 0x3F but it's too quiet then
+ AdPlug_LogWrite("%02X + vel %d (lev %02X) == %02X\n", this->iCurrentRegs[iOPLOffset], iVelocity, iLevel, (this->iCurrentRegs[iOPLOffset] & ~0x3F) | iLevel);
+ //this->writeOPL(iOPLOffset, (this->iCurrentRegs[iOPLOffset] & ~0x3F) | (0x3F - (iVelocity >> 1)));//(iVelocity * 0x3F / 127));
+ this->writeOPL(iOPLOffset, (this->iCurrentRegs[iOPLOffset] & ~0x3F) | iLevel);//(iVelocity * 0x3F / 127));
+ #endif*/
+
+ // Apparently you can't set the frequency for the cymbal or hihat?
+ // Vinyl requires you don't set it, Kiloblaster requires you do!
+ this->writeOPL(BASE_FNUM_L + iPercChannel, iOPLFNum & 0xFF);
+ this->writeOPL(BASE_KEYON_FREQ + iPercChannel, (iBlock << 2) | ((iOPLFNum >> 8) & 0x03));
+
+ uint8_t iBit = 1 << (15 - iChannel);
+
+ // Turn the perc instrument off if it's already playing (OPL can't do
+ // polyphonic notes w/ percussion)
+ if (this->iCurrentRegs[BASE_RHYTHM] & iBit) this->writeOPL(BASE_RHYTHM, this->iCurrentRegs[BASE_RHYTHM] & ~iBit);
+
+ // I wonder whether we need to delay or anything here?
+
+ // Turn the note on
+ //if (iChannel == 15) {
+ this->writeOPL(BASE_RHYTHM, this->iCurrentRegs[BASE_RHYTHM] | iBit);
+ //AdPlug_LogWrite("CMF: Note %d on MIDI channel %d (mapped to OPL channel %d-1) - vel %02X, fnum %d/%d\n", iNote, iChannel, iPercChannel+1, iVelocity, iOPLFNum, iBlock);
+ //}
+
+ this->chOPL[iPercChannel].iNoteStart = ++this->iNoteCount;
+ this->chOPL[iPercChannel].iMIDIChannel = iChannel;
+ this->chOPL[iPercChannel].iMIDINote = iNote;
+
+ } else { // Non rhythm-mode or a normal instrument channel
+
+ // Figure out which OPL channel to play this note on
+ int iOPLChannel = -1;
+ int iNumChannels = this->bPercussive ? 6 : 9;
+ for (int i = iNumChannels - 1; i >= 0; i--) {
+ // If there's no note playing on this OPL channel, use that
+ if (this->chOPL[i].iNoteStart == 0) {
+ iOPLChannel = i;
+ // See if this channel is already set to the instrument we want.
+ if (this->chOPL[i].iMIDIPatch == this->chMIDI[iChannel].iPatch) {
+ // It is, so stop searching
+ break;
+ } // else keep searching just in case there's a better match
+ }
+ }
+ if (iOPLChannel == -1) {
+ // All channels were in use, find the one with the longest note
+ iOPLChannel = 0;
+ int iEarliest = this->chOPL[0].iNoteStart;
+ for (int i = 1; i < iNumChannels; i++) {
+ if (this->chOPL[i].iNoteStart < iEarliest) {
+ // Found a channel with a note being played for longer
+ iOPLChannel = i;
+ iEarliest = this->chOPL[i].iNoteStart;
+ }
+ }
+ AdPlug_LogWrite("CMF: Too many polyphonic notes, cutting note on channel %d\n", iOPLChannel);
+ }
+
+ // Run through all the channels with negative notestart values - these
+ // channels have had notes recently stop - and increment the counter
+ // to slowly move the channel closer to being reused for a future note.
+ //for (int i = 0; i < iNumChannels; i++) {
+ // if (this->chOPL[i].iNoteStart < 0) this->chOPL[i].iNoteStart++;
+ //}
+
+ // Now the new note should be played on iOPLChannel, but see if the instrument
+ // is right first.
+ if (this->chOPL[iOPLChannel].iMIDIPatch != this->chMIDI[iChannel].iPatch) {
+ this->MIDIchangeInstrument(iOPLChannel, iChannel, this->chMIDI[iChannel].iPatch);
+ }
+
+ this->chOPL[iOPLChannel].iNoteStart = ++this->iNoteCount;
+ this->chOPL[iOPLChannel].iMIDIChannel = iChannel;
+ this->chOPL[iOPLChannel].iMIDINote = iNote;
+
+ #ifdef USE_VELOCITY // Official CMF player seems to ignore velocity levels
+ // Adjust the channel volume to match the note velocity
+ uint8_t iOPLOffset = BASE_SCAL_LEVL + OPLOFFSET(iChannel) + 3; // +3 == Carrier
+ uint16_t iLevel = 0x2F - (iVelocity * 0x2F / 127); // 0x2F should be 0x3F but it's too quiet then
+ this->writeOPL(iOPLOffset, (this->iCurrentRegs[iOPLOffset] & ~0x3F) | iLevel);
+ #endif
+
+ // Set the frequency and play the note
+ this->writeOPL(BASE_FNUM_L + iOPLChannel, iOPLFNum & 0xFF);
+ this->writeOPL(BASE_KEYON_FREQ + iOPLChannel, OPLBIT_KEYON | (iBlock << 2) | ((iOPLFNum & 0x300) >> 8));
+ }
+ return;
+}
+
+void CcmfPlayer::cmfNoteOff(uint8_t iChannel, uint8_t iNote, uint8_t iVelocity)
+{
+ if ((iChannel > 10) && (this->bPercussive)) {
+ int iOPLChannel = this->getPercChannel(iChannel);
+ if (this->chOPL[iOPLChannel].iMIDINote != iNote) return; // there's a different note playing now
+ this->writeOPL(BASE_RHYTHM, this->iCurrentRegs[BASE_RHYTHM] & ~(1 << (15 - iChannel)));
+ this->chOPL[iOPLChannel].iNoteStart = 0; // channel free
+ } else { // Non rhythm-mode or a normal instrument channel
+ int iOPLChannel = -1;
+ int iNumChannels = this->bPercussive ? 6 : 9;
+ for (int i = 0; i < iNumChannels; i++) {
+ if (
+ (this->chOPL[i].iMIDIChannel == iChannel) &&
+ (this->chOPL[i].iMIDINote == iNote) &&
+ (this->chOPL[i].iNoteStart != 0)
+ ) {
+ // Found the note, switch it off
+ this->chOPL[i].iNoteStart = 0;
+ iOPLChannel = i;
+ break;
+ }
+ }
+ if (iOPLChannel == -1) return;
+
+ this->writeOPL(BASE_KEYON_FREQ + iOPLChannel, this->iCurrentRegs[BASE_KEYON_FREQ + iOPLChannel] & ~OPLBIT_KEYON);
+ }
+ return;
+}
+
+uint8_t CcmfPlayer::getPercChannel(uint8_t iChannel)
+{
+ switch (iChannel) {
+ case 11: return 7-1; // Bass drum
+ case 12: return 8-1; // Snare drum
+ case 13: return 9-1; // Tom tom
+ case 14: return 9-1; // Top cymbal
+ case 15: return 8-1; // Hihat
+ }
+ AdPlug_LogWrite("CMF ERR: Tried to get the percussion channel from MIDI channel %d - this shouldn't happen!\n", iChannel);
+ return 0;
+}
+
+
+void CcmfPlayer::MIDIchangeInstrument(uint8_t iOPLChannel, uint8_t iMIDIChannel, uint8_t iNewInstrument)
+{
+ if ((iMIDIChannel > 10) && (this->bPercussive)) {
+ switch (iMIDIChannel) {
+ case 11: // Bass drum (operator 13+16 == channel 7 modulator+carrier)
+ this->writeInstrumentSettings(7-1, 0, 0, iNewInstrument);
+ this->writeInstrumentSettings(7-1, 1, 1, iNewInstrument);
+ break;
+ case 12: // Snare drum (operator 17 == channel 8 carrier)
+ //case 15:
+ this->writeInstrumentSettings(8-1, 0, 1, iNewInstrument);
+
+ //
+ //this->writeInstrumentSettings(8-1, 0, 0, iNewInstrument);
+ break;
+ case 13: // Tom tom (operator 15 == channel 9 modulator)
+ //case 14:
+ this->writeInstrumentSettings(9-1, 0, 0, iNewInstrument);
+
+ //
+ //this->writeInstrumentSettings(9-1, 0, 1, iNewInstrument);
+ break;
+ case 14: // Top cymbal (operator 18 == channel 9 carrier)
+ this->writeInstrumentSettings(9-1, 0, 1, iNewInstrument);
+ break;
+ case 15: // Hi-hat (operator 14 == channel 8 modulator)
+ this->writeInstrumentSettings(8-1, 0, 0, iNewInstrument);
+ break;
+ default:
+ AdPlug_LogWrite("CMF: Invalid MIDI channel %d (not melodic and not percussive!)\n", iMIDIChannel + 1);
+ break;
+ }
+ this->chOPL[iOPLChannel].iMIDIPatch = iNewInstrument;
+ } else {
+ // Standard nine OPL channels
+ this->writeInstrumentSettings(iOPLChannel, 0, 0, iNewInstrument);
+ this->writeInstrumentSettings(iOPLChannel, 1, 1, iNewInstrument);
+ this->chOPL[iOPLChannel].iMIDIPatch = iNewInstrument;
+ }
+ return;
+}
+
+void CcmfPlayer::MIDIcontroller(uint8_t iChannel, uint8_t iController, uint8_t iValue)
+{
+ switch (iController) {
+ case 0x63:
+ // Custom extension to allow CMF files to switch the AM+VIB depth on and
+ // off (officially both are on, and there's no way to switch them off.)
+ // Controller values:
+ // 0 == AM+VIB off
+ // 1 == VIB on
+ // 2 == AM on
+ // 3 == AM+VIB on
+ if (iValue) {
+ this->writeOPL(BASE_RHYTHM, (this->iCurrentRegs[BASE_RHYTHM] & ~0xC0) | (iValue << 6)); // switch AM+VIB extension on
+ } else {
+ this->writeOPL(BASE_RHYTHM, this->iCurrentRegs[BASE_RHYTHM] & ~0xC0); // switch AM+VIB extension off
+ }
+ AdPlug_LogWrite("CMF: AM+VIB depth change - AM %s, VIB %s\n",
+ (this->iCurrentRegs[BASE_RHYTHM] & 0x80) ? "on" : "off",
+ (this->iCurrentRegs[BASE_RHYTHM] & 0x40) ? "on" : "off");
+ break;
+ case 0x66:
+ AdPlug_LogWrite("CMF: Song set marker to 0x%02X\n", iValue);
+ break;
+ case 0x67:
+ this->bPercussive = (iValue != 0);
+ if (this->bPercussive) {
+ this->writeOPL(BASE_RHYTHM, this->iCurrentRegs[BASE_RHYTHM] | 0x20); // switch rhythm-mode on
+ } else {
+ this->writeOPL(BASE_RHYTHM, this->iCurrentRegs[BASE_RHYTHM] & ~0x20); // switch rhythm-mode off
+ }
+ AdPlug_LogWrite("CMF: Percussive/rhythm mode %s\n", this->bPercussive ? "enabled" : "disabled");
+ break;
+ case 0x68:
+ // TODO: Shouldn't this just affect the one channel, not the whole song? -- have pitchbends for that
+ this->iTranspose = iValue;
+ AdPlug_LogWrite("CMF: Transposing all notes up by %d * 1/128ths of a semitone.\n", iValue);
+ break;
+ case 0x69:
+ this->iTranspose = -iValue;
+ AdPlug_LogWrite("CMF: Transposing all notes down by %d * 1/128ths of a semitone.\n", iValue);
+ break;
+ default:
+ AdPlug_LogWrite("CMF: Unsupported MIDI controller 0x%02X, ignoring.\n", iController);
+ break;
+ }
+ return;
+}
diff --git a/plugins/adplug/adplug/cmf.h b/plugins/adplug/adplug/cmf.h
new file mode 100644
index 00000000..e4347426
--- /dev/null
+++ b/plugins/adplug/adplug/cmf.h
@@ -0,0 +1,112 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2009 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
+ *
+ * cmf.h - CMF player by Adam Nielsen <malvineous@shikadi.net>
+ */
+
+#include <stdint.h> // for uintxx_t
+#include "player.h"
+
+typedef struct {
+ uint16_t iInstrumentBlockOffset;
+ uint16_t iMusicOffset;
+ uint16_t iTicksPerQuarterNote;
+ uint16_t iTicksPerSecond;
+ uint16_t iTagOffsetTitle;
+ uint16_t iTagOffsetComposer;
+ uint16_t iTagOffsetRemarks;
+ uint8_t iChannelsInUse[16];
+ uint16_t iNumInstruments;
+ uint16_t iTempo;
+} CMFHEADER;
+
+typedef struct {
+ uint8_t iCharMult;
+ uint8_t iScalingOutput;
+ uint8_t iAttackDecay;
+ uint8_t iSustainRelease;
+ uint8_t iWaveSel;
+} OPERATOR;
+
+typedef struct {
+ OPERATOR op[2]; // 0 == modulator, 1 == carrier
+ uint8_t iConnection;
+} SBI;
+
+typedef struct {
+ int iPatch; // MIDI patch for this channel
+ int iPitchbend; // Current pitchbend amount for this channel
+} MIDICHANNEL;
+
+typedef struct {
+ int iNoteStart; // When the note started playing (longest notes get cut first, 0 == channel free)
+ int iMIDINote; // MIDI note number currently being played on this OPL channel
+ int iMIDIChannel; // Source MIDI channel where this note came from
+ int iMIDIPatch; // Current MIDI patch set on this OPL channel
+} OPLCHANNEL;
+
+class CcmfPlayer: public CPlayer
+{
+ private:
+ uint8_t *data; // song data (CMF music block)
+ int iPlayPointer; // Current location of playback pointer
+ int iSongLen; // Max value for iPlayPointer
+ CMFHEADER cmfHeader;
+ SBI *pInstruments;
+ bool bPercussive; // are rhythm-mode instruments enabled?
+ uint8_t iCurrentRegs[256]; // Current values in the OPL chip
+ int iTranspose; // Transpose amount for entire song (between -128 and +128)
+ uint8_t iPrevCommand; // Previous command (used for repeated MIDI commands, as the seek and playback code need to share this)
+
+ int iNoteCount; // Used to count how long notes have been playing for
+ MIDICHANNEL chMIDI[16];
+ OPLCHANNEL chOPL[9];
+
+ // Additions for AdPlug's design
+ int iDelayRemaining;
+ bool bSongEnd;
+ std::string strTitle, strComposer, strRemarks;
+
+ public:
+ static CPlayer *factory(Copl *newopl);
+
+ CcmfPlayer(Copl *newopl);
+ ~CcmfPlayer();
+
+ bool load(const std::string &filename, const CFileProvider &fp);
+ bool update();
+ void rewind(int subsong);
+ float getrefresh();
+
+ std::string gettype()
+ { return std::string("Creative Music File (CMF)"); };
+ std::string gettitle();
+ std::string getauthor();
+ std::string getdesc();
+
+ protected:
+ uint32_t readMIDINumber();
+ void writeInstrumentSettings(uint8_t iChannel, uint8_t iOperatorSource, uint8_t iOperatorDest, uint8_t iInstrument);
+ void writeOPL(uint8_t iRegister, uint8_t iValue);
+ void cmfNoteOn(uint8_t iChannel, uint8_t iNote, uint8_t iVelocity);
+ void cmfNoteOff(uint8_t iChannel, uint8_t iNote, uint8_t iVelocity);
+ uint8_t getPercChannel(uint8_t iChannel);
+ void MIDIchangeInstrument(uint8_t iOPLChannel, uint8_t iMIDIChannel, uint8_t iNewInstrument);
+ void MIDIcontroller(uint8_t iChannel, uint8_t iController, uint8_t iValue);
+
+};
diff --git a/plugins/adplug/adplug/d00.cpp b/plugins/adplug/adplug/d00.cpp
index 7aa042d1..0644b84b 100644
--- a/plugins/adplug/adplug/d00.cpp
+++ b/plugins/adplug/adplug/d00.cpp
@@ -1,6 +1,6 @@
/*
* Adplug - Replayer for many OPL2/OPL3 audio file formats.
- * Copyright (C) 1999 - 2007 Simon Peter, <dn.tlp@gmx.net>, et al.
+ * Copyright (C) 1999 - 2008 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
@@ -177,7 +177,7 @@ bool Cd00Player::update()
setvolume(c);
}
- if(channel[c].levpuls != 0xff) // Levelpuls
+ if(channel[c].levpuls != 0xff) { // Levelpuls
if(channel[c].frameskip)
channel[c].frameskip--;
else {
@@ -193,6 +193,7 @@ bool Cd00Player::update()
channel[c].modvol += levpuls[channel[c].levpuls].voladd; channel[c].modvol &= 63;
setvolume(c);
}
+ }
}
// song handling
@@ -413,6 +414,8 @@ void Cd00Player::rewind(int subsong)
} *tpoin;
int i;
+ if(subsong == -1) subsong = cursubsong;
+
if(version > 1) { // do nothing if subsong > number of subsongs
if(subsong >= header->subsongs)
return;
@@ -442,6 +445,7 @@ void Cd00Player::rewind(int subsong)
}
songend = 0;
opl->init(); opl->write(1,32); // reset OPL chip
+ cursubsong = subsong;
}
std::string Cd00Player::gettype()
diff --git a/plugins/adplug/adplug/d00.h b/plugins/adplug/adplug/d00.h
index 50b0de3c..d0bf4b24 100644
--- a/plugins/adplug/adplug/d00.h
+++ b/plugins/adplug/adplug/d00.h
@@ -91,7 +91,7 @@ class Cd00Player: public CPlayer
unsigned char duration,ptr;
} *levpuls;
- unsigned char songend,version;
+ unsigned char songend,version,cursubsong;
char *datainfo;
unsigned short *seqptr;
d00header *header;
diff --git a/plugins/adplug/adplug/dro.cpp b/plugins/adplug/adplug/dro.cpp
index eb97c0f8..8342fe89 100644
--- a/plugins/adplug/adplug/dro.cpp
+++ b/plugins/adplug/adplug/dro.cpp
@@ -1,6 +1,6 @@
/*
* Adplug - Replayer for many OPL2/OPL3 audio file formats.
- * Copyright (C) 1999 - 2005 Simon Peter, <dn.tlp@gmx.net>, et al.
+ * Copyright (C) 1999 - 2007 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
@@ -23,8 +23,8 @@
* NOTES: 3-oct-04: the DRO format is not yet finalized. beware.
*/
+#include <cstring>
#include <stdio.h>
-#include <string.h>
#include "dro.h"
@@ -59,9 +59,9 @@ bool CdroPlayer::load(const std::string &filename, const CFileProvider &fp)
// load section
mstotal = f->readInt(4); // Total milliseconds in file
length = f->readInt(4); // Total data bytes in file
- f->ignore(1); // Type of opl data this can contain - ignored
+ f->ignore(4); // Type of opl data this can contain - ignored
data = new unsigned char [length];
- for (i=0;i<length;i++)
+ for (i=0;i<length;i++)
data[i]=f->readInt(1);
fp.close(f);
rewind(0);
diff --git a/plugins/adplug/adplug/dro.h b/plugins/adplug/adplug/dro.h
index 0f59e7b1..995da6cc 100644
--- a/plugins/adplug/adplug/dro.h
+++ b/plugins/adplug/adplug/dro.h
@@ -40,7 +40,7 @@ class CdroPlayer: public CPlayer
std::string gettype()
{
- return std::string("DOSBox Raw OPL");
+ return std::string("DOSBox Raw OPL v0.1");
}
protected:
diff --git a/plugins/adplug/adplug/dro2.cpp b/plugins/adplug/adplug/dro2.cpp
new file mode 100644
index 00000000..e11196e7
--- /dev/null
+++ b/plugins/adplug/adplug/dro2.cpp
@@ -0,0 +1,142 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2007 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
+ *
+ * dro2.cpp - DOSBox Raw OPL v2.0 player by Adam Nielsen <malvineous@shikadi.net>
+ */
+
+#include <cstring>
+#include <stdio.h>
+
+#include "dro2.h"
+
+CPlayer *Cdro2Player::factory(Copl *newopl)
+{
+ return new Cdro2Player(newopl);
+}
+
+Cdro2Player::Cdro2Player(Copl *newopl) :
+ CPlayer(newopl),
+ piConvTable(NULL),
+ data(0)
+{
+}
+
+Cdro2Player::~Cdro2Player()
+{
+ if (this->data) delete[] this->data;
+ if (this->piConvTable) delete[] this->piConvTable;
+}
+
+bool Cdro2Player::load(const std::string &filename, const CFileProvider &fp)
+{
+ binistream *f = fp.open(filename);
+ if (!f) return false;
+
+ char id[8];
+ f->readString(id, 8);
+ if (strncmp(id, "DBRAWOPL", 8)) {
+ fp.close(f);
+ return false;
+ }
+ int version = f->readInt(4);
+ if (version != 0x2) {
+ fp.close(f);
+ return false;
+ }
+
+ this->iLength = f->readInt(4) * 2; // stored in file as number of byte pairs
+ f->ignore(4); // Length in milliseconds
+ f->ignore(1); /// OPL type (0 == OPL2, 1 == Dual OPL2, 2 == OPL3)
+ int iFormat = f->readInt(1);
+ if (iFormat != 0) {
+ fp.close(f);
+ return false;
+ }
+ int iCompression = f->readInt(1);
+ if (iCompression != 0) {
+ fp.close(f);
+ return false;
+ }
+ this->iCmdDelayS = f->readInt(1);
+ this->iCmdDelayL = f->readInt(1);
+ this->iConvTableLen = f->readInt(1);
+
+ this->piConvTable = new uint8_t[this->iConvTableLen];
+ f->readString((char *)this->piConvTable, this->iConvTableLen);
+
+ this->data = new uint8_t[this->iLength];
+ f->readString((char *)this->data, this->iLength);
+
+ fp.close(f);
+ rewind(0);
+
+ return true;
+}
+
+bool Cdro2Player::update()
+{
+ while (this->iPos < this->iLength) {
+ int iIndex = this->data[this->iPos++];
+ int iValue = this->data[this->iPos++];
+
+ // Short delay
+ if (iIndex == this->iCmdDelayS) {
+ this->iDelay = iValue + 1;
+ return true;
+
+ // Long delay
+ } else if (iIndex == this->iCmdDelayL) {
+ this->iDelay = (iValue + 1) << 8;
+ return true;
+
+ // Normal write
+ } else {
+ if (iIndex & 0x80) {
+ // High bit means use second chip in dual-OPL2 config
+ this->opl->setchip(1);
+ iIndex &= 0x7F;
+ } else {
+ this->opl->setchip(0);
+ }
+ if (iIndex > this->iConvTableLen) {
+ printf("DRO2: Error - index beyond end of codemap table! Corrupted .dro?\n");
+ return false; // EOF
+ }
+ int iReg = this->piConvTable[iIndex];
+ this->opl->write(iReg, iValue);
+ }
+
+ }
+
+ // This won't result in endless-play using Adplay, but IMHO that code belongs
+ // in Adplay itself, not here.
+ return this->iPos < this->iLength;
+}
+
+void Cdro2Player::rewind(int subsong)
+{
+ this->iDelay = 0;
+ this->iPos = 0;
+ opl->init();
+}
+
+float Cdro2Player::getrefresh()
+{
+ if (this->iDelay > 0) return 1000.0 / this->iDelay;
+ else return 1000.0;
+}
diff --git a/plugins/adplug/adplug/dro2.h b/plugins/adplug/adplug/dro2.h
new file mode 100644
index 00000000..fdd2f06d
--- /dev/null
+++ b/plugins/adplug/adplug/dro2.h
@@ -0,0 +1,60 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2005 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
+ *
+ * dro2.h - DOSBox Raw OPL v2.0 player by Adam Nielsen <malvineous@shikadi.net>
+ */
+
+#include <stdint.h> // for uintxx_t
+#include "player.h"
+
+class Cdro2Player: public CPlayer
+{
+ protected:
+ uint8_t iCmdDelayS, iCmdDelayL;
+ int iConvTableLen;
+ uint8_t *piConvTable;
+
+ uint8_t *data;
+ int iLength;
+ int iPos;
+ int iDelay;
+
+
+ public:
+ static CPlayer *factory(Copl *newopl);
+
+ Cdro2Player(Copl *newopl);
+ ~Cdro2Player();
+
+ bool load(const std::string &filename, const CFileProvider &fp);
+ bool update();
+ void rewind(int subsong);
+ float getrefresh();
+
+ std::string gettype()
+ {
+ return std::string("DOSBox Raw OPL v2.0");
+ }
+
+ protected:
+ //unsigned char *data;
+ //unsigned long pos,length;
+ //unsigned long msdone,mstotal;
+ //unsigned short delay;
+ //unsigned char index, opl3_mode;
+};
diff --git a/plugins/adplug/adplug/dtm.cpp b/plugins/adplug/adplug/dtm.cpp
index 3d022ae8..91ea837c 100644
--- a/plugins/adplug/adplug/dtm.cpp
+++ b/plugins/adplug/adplug/dtm.cpp
@@ -22,7 +22,7 @@
NOTE: Panning (Ex) effect is ignored.
*/
-#include <string.h>
+#include <cstring>
#include "dtm.h"
/* -------- Public Methods -------------------------------- */
diff --git a/plugins/adplug/adplug/fmc.cpp b/plugins/adplug/adplug/fmc.cpp
index c6839a61..75cd99a7 100644
--- a/plugins/adplug/adplug/fmc.cpp
+++ b/plugins/adplug/adplug/fmc.cpp
@@ -19,7 +19,7 @@
fmc.cpp - FMC Loader by Riven the Mage <riven@ok.ru>
*/
-#include <string.h>
+#include <cstring>
#include "fmc.h"
/* -------- Public Methods -------------------------------- */
@@ -115,9 +115,9 @@ bool CfmcLoader::load(const std::string &filename, const CFileProvider &fp)
tracks[t][k].param2 = event.byte2 & 0x0F;
// fix effects
- if (tracks[t][k].command == 0x0E) // 0x0E (14): Retrig
+ if (tracks[t][k].command == 0x0E) // 0x0E (14): Retrig
tracks[t][k].param1 = 3;
- if (tracks[t][k].command == 0x1A) // 0x1A (26): Volume Slide
+ if (tracks[t][k].command == 0x1A) { // 0x1A (26): Volume Slide
if (tracks[t][k].param1 > tracks[t][k].param2)
{
tracks[t][k].param1 -= tracks[t][k].param2;
@@ -128,6 +128,7 @@ bool CfmcLoader::load(const std::string &filename, const CFileProvider &fp)
tracks[t][k].param2 -= tracks[t][k].param1;
tracks[t][k].param1 = 0;
}
+ }
}
t++;
diff --git a/plugins/adplug/adplug/fprovide.h b/plugins/adplug/adplug/fprovide.h
index 2f1a0b62..572f28ac 100644
--- a/plugins/adplug/adplug/fprovide.h
+++ b/plugins/adplug/adplug/fprovide.h
@@ -32,7 +32,7 @@ public:
{
}
- virtual binistream *open(std::string filename) const = 0;
+ virtual binistream *open(std::string) const = 0;
virtual void close(binistream *) const = 0;
static bool extension(const std::string &filename,
diff --git a/plugins/adplug/adplug/hsc.h b/plugins/adplug/adplug/hsc.h
index d09b8906..4a9e2d27 100644
--- a/plugins/adplug/adplug/hsc.h
+++ b/plugins/adplug/adplug/hsc.h
@@ -1,6 +1,6 @@
/*
* Adplug - Replayer for many OPL2/OPL3 audio file formats.
- * Copyright (C) 1999 - 2002 Simon Peter, <dn.tlp@gmx.net>, et al.
+ * Copyright (C) 1999 - 2007 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
@@ -31,7 +31,7 @@ class ChscPlayer: public CPlayer
ChscPlayer(Copl *newopl): CPlayer(newopl), mtkmode(0) {}
- bool load(const std::string &fn, const CFileProvider &fp);
+ bool load(const std::string &filename, const CFileProvider &fp);
bool update();
void rewind(int subsong);
float getrefresh() { return 18.2f; }; // refresh rate is fixed at 18.2Hz
@@ -48,16 +48,16 @@ class ChscPlayer: public CPlayer
protected:
struct hscnote {
unsigned char note, effect;
- }; // note type in HSC pattern
+ }; // note type in HSC pattern
struct hscchan {
unsigned char inst; // current instrument
signed char slide; // used for manual slide-effects
unsigned short freq; // actual replaying frequency
- }; // HSC channel data
+ }; // HSC channel data
- hscchan channel[9]; // player channel-info
- unsigned char instr[128][12]; // instrument data
+ hscchan channel[9]; // player channel-info
+ unsigned char instr[128][12]; // instrument data
unsigned char song[0x80]; // song-arrangement (MPU-401 Trakker enhanced)
hscnote patterns[50][64*9]; // pattern data
unsigned char pattpos,songpos, // various bytes & flags
diff --git a/plugins/adplug/adplug/imf.cpp b/plugins/adplug/adplug/imf.cpp
index 37af7aca..c525ac8b 100644
--- a/plugins/adplug/adplug/imf.cpp
+++ b/plugins/adplug/adplug/imf.cpp
@@ -1,6 +1,6 @@
/*
* Adplug - Replayer for many OPL2/OPL3 audio file formats.
- * Copyright (C) 1999 - 2006 Simon Peter <dn.tlp@gmx.net>, et al.
+ * Copyright (C) 1999 - 2008 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
@@ -105,7 +105,7 @@ bool CimfPlayer::load(const std::string &filename, const CFileProvider &fp)
}
// read footer, if any
- if(fsize && (fsize < flsize - 2 - mfsize))
+ if(fsize && (fsize < flsize - 2 - mfsize)) {
if(f->readInt(1) == 0x1a) {
// Adam Nielsen's footer format
track_name = f->readString();
@@ -119,6 +119,7 @@ bool CimfPlayer::load(const std::string &filename, const CFileProvider &fp)
f->readString(footer, footerlen);
footer[footerlen] = '\0'; // Make ASCIIZ string
}
+ }
rate = getrate(filename, fp, f);
fp.close(f);
diff --git a/plugins/adplug/adplug/jbm.cpp b/plugins/adplug/adplug/jbm.cpp
new file mode 100644
index 00000000..0990001b
--- /dev/null
+++ b/plugins/adplug/adplug/jbm.cpp
@@ -0,0 +1,293 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2007 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
+ *
+ * Johannes Bjerregaard's JBM Adlib Music Format player for AdPlug
+ * Written by Dennis Lindroos <lindroos@nls.fi>, February-March 2007
+ * - Designed and coded from scratch (only frequency-table taken from MUSIC.BIN)
+ * - The percussion mode is buggy (?) but i'm not good enough to find them
+ * and honestly i think the melodic-mode tunes are much better ;)
+ *
+ * This version doesn't use the binstr.h functions (coded with custom func.)
+ * This is my first attempt on writing a musicplayer for AdPlug, and i'm not
+ * coding C++ very often..
+ *
+ * Released under the terms of the GNU General Public License.
+ */
+
+#include "jbm.h"
+
+static const unsigned short notetable[96] = {
+ 0x0158, 0x016d, 0x0183, 0x019a, 0x01b2, 0x01cc, 0x01e7, 0x0204,
+ 0x0223, 0x0244, 0x0266, 0x028b, 0x0558, 0x056d, 0x0583, 0x059a,
+ 0x05b2, 0x05cc, 0x05e7, 0x0604, 0x0623, 0x0644, 0x0666, 0x068b,
+ 0x0958, 0x096d, 0x0983, 0x099a, 0x09b2, 0x09cc, 0x09e7, 0x0a04,
+ 0x0a23, 0x0a44, 0x0a66, 0x0a8b, 0x0d58, 0x0d6d, 0x0d83, 0x0d9a,
+ 0x0db2, 0x0dcc, 0x0de7, 0x0e04, 0x0e23, 0x0e44, 0x0e66, 0x0e8b,
+ 0x1158, 0x116d, 0x1183, 0x119a, 0x11b2, 0x11cc, 0x11e7, 0x1204,
+ 0x1223, 0x1244, 0x1266, 0x128b, 0x1558, 0x156d, 0x1583, 0x159a,
+ 0x15b2, 0x15cc, 0x15e7, 0x1604, 0x1623, 0x1644, 0x1666, 0x168b,
+ 0x1958, 0x196d, 0x1983, 0x199a, 0x19b2, 0x19cc, 0x19e7, 0x1a04,
+ 0x1a23, 0x1a44, 0x1a66, 0x1a8b, 0x1d58, 0x1d6d, 0x1d83, 0x1d9a,
+ 0x1db2, 0x1dcc, 0x1de7, 0x1e04, 0x1e23, 0x1e44, 0x1e66, 0x1e8b
+};
+
+static const unsigned char percmx_tab[4] = { 0x14, 0x12, 0x15, 0x11 };
+static const unsigned char perchn_tab[5] = { 6, 7, 8, 8, 7 };
+static unsigned char percmaskoff[5] = { 0xef, 0xf7, 0xfb, 0xfd, 0xfe };
+static unsigned char percmaskon[5] = { 0x10, 0x08, 0x04, 0x02, 0x01 };
+
+static inline unsigned short GET_WORD(unsigned char *b, int x)
+{
+ return ((unsigned short)(b[x+1] << 8) | b[x]);
+}
+
+/*** public methods *************************************/
+
+CPlayer *CjbmPlayer::factory(Copl *newopl)
+{
+ return new CjbmPlayer(newopl);
+}
+
+bool CjbmPlayer::load(const std::string &filename, const CFileProvider &fp)
+{
+ binistream *f = fp.open(filename); if(!f) return false;
+ int filelen = fp.filesize(f);
+ int i;
+
+ if (!filelen || !fp.extension(filename, ".jbm")) goto loaderr;
+
+ // Allocate memory buffer m[] and read entire file into it
+
+ m = new unsigned char[filelen];
+ if (f->readString((char *)m, filelen) != filelen) goto loaderr;
+
+ fp.close(f);
+
+ // The known .jbm files always seem to start with the number 0x0002
+
+ if (GET_WORD(m, 0) != 0x0002)
+ return false;
+
+ // Song tempo
+
+ i = GET_WORD(m, 2);
+ timer = 1193810.0 / (i ? i : 0xffff);
+
+ seqtable = GET_WORD(m, 4);
+ instable = GET_WORD(m, 6);
+
+ // The flags word has atleast 1 bit, the Adlib's rhythm mode, but
+ // currently we don't support that :(
+
+ flags = GET_WORD(m, 8);
+
+ // Instrument datas are directly addressed with m[]
+
+ inscount = (filelen - instable) >> 4;
+
+ // Voice' and sequence pointers
+
+ seqcount = 0xffff;
+ for (i = 0; i < 11; i++) {
+ voice[i].trkpos = voice[i].trkstart = GET_WORD(m, 10 + (i<<1));
+ if (voice[i].trkpos && voice[i].trkpos < seqcount)
+ seqcount = voice[i].trkpos;
+ }
+ seqcount = (seqcount - seqtable) >> 1;
+ sequences = new unsigned short[seqcount];
+ for (i = 0; i < seqcount; i++)
+ sequences[i] = GET_WORD(m, seqtable + (i<<1));
+
+ rewind(0);
+ return true;
+ loaderr:
+ fp.close(f);
+ return false;
+}
+
+bool CjbmPlayer::update()
+{
+ short c, spos, frq;
+
+ for (c = 0; c < 11; c++) {
+ if (!voice[c].trkpos) // Unused channel
+ continue;
+
+ if (--voice[c].delay)
+ continue;
+
+ // Turn current note/percussion off
+
+ if (voice[c].note&0x7f)
+ opl_noteonoff(c, &voice[c], 0);
+
+ // Process events until we have a note
+
+ spos = voice[c].seqpos;
+ while(!voice[c].delay) {
+ switch(m[spos]) {
+ case 0xFD: // Set Instrument
+ voice[c].instr = m[spos+1];
+ set_opl_instrument(c, &voice[c]);
+ spos+=2;
+ break;
+ case 0xFF: // End of Sequence
+ voice[c].seqno = m[++voice[c].trkpos];
+ if (voice[c].seqno == 0xff) {
+ voice[c].trkpos = voice[c].trkstart;
+ voice[c].seqno = m[voice[c].trkpos];
+ //voicemask &= 0x7ff-(1<<c);
+ voicemask &= ~(1<<c);
+ }
+ spos = voice[c].seqpos = sequences[voice[c].seqno];
+ break;
+ default: // Note Event
+ if ((m[spos] & 127) > 95)
+ return 0;
+
+ voice[c].note = m[spos];
+ voice[c].vol = m[spos+1];
+ voice[c].delay =
+ (m[spos+2] + (m[spos+3]<<8)) + 1;
+
+ frq = notetable[voice[c].note&127];
+ voice[c].frq[0] = (unsigned char)frq;
+ voice[c].frq[1] = frq >> 8;
+ spos+=4;
+ }
+ }
+ voice[c].seqpos = spos;
+
+ // Write new volume to the carrier operator, or percussion
+
+ if (flags&1 && c > 6)
+ opl->write(0x40 + percmx_tab[c-7], voice[c].vol ^ 0x3f);
+ else
+ opl->write(0x43 + op_table[c], voice[c].vol ^ 0x3f);
+
+ // Write new frequencies and Gate bit
+
+ opl_noteonoff(c, &voice[c], !(voice[c].note & 0x80));
+ }
+ return (voicemask);
+}
+
+void CjbmPlayer::rewind(int subsong)
+{
+ int c;
+
+ voicemask = 0;
+
+ for (c = 0; c < 11; c++) {
+ voice[c].trkpos = voice[c].trkstart;
+
+ if (!voice[c].trkpos) continue;
+
+ voicemask |= (1<<c);
+
+ voice[c].seqno = m[voice[c].trkpos];
+ voice[c].seqpos = sequences[voice[c].seqno];
+
+ voice[c].note = 0;
+ voice[c].delay = 1;
+ }
+
+ opl->init();
+ opl->write(0x01, 32);
+
+ // Set rhythm mode if flags bit #0 is set
+ // AM and Vibrato are full depths (taken from DosBox RAW output)
+ bdreg = 0xC0 | (flags&1)<<5;
+
+ opl->write(0xbd, bdreg);
+
+#if 0
+ if (flags&1) {
+ voice[7].frq[0] = 0x58; voice[7].frq[1] = 0x09; // XXX
+ voice[8].frq[0] = 0x04; voice[8].frq[1] = 0x0a; // XXX
+ opl_noteonoff(7, &voice[7], 0);
+ opl_noteonoff(8, &voice[8], 0);
+ }
+#endif
+
+ return;
+}
+
+/*** private methods ************************************/
+
+void CjbmPlayer::opl_noteonoff(int channel, JBMVoice *v, bool state)
+{
+ if (flags&1 && channel > 5) {
+ // Percussion
+ opl->write(0xa0 + perchn_tab[channel-6], voice[channel].frq[0]);
+ opl->write(0xb0 + perchn_tab[channel-6], voice[channel].frq[1]);
+ opl->write(0xbd,
+ state ? bdreg | percmaskon[channel-6] :
+ bdreg & percmaskoff[channel-6]);
+ } else {
+ // Melodic mode or Rhythm mode melodic channels
+ opl->write(0xa0 + channel, voice[channel].frq[0]);
+ opl->write(0xb0 + channel,
+ state ? voice[channel].frq[1] | 0x20 :
+ voice[channel].frq[1] & 0x1f);
+ }
+ return;
+}
+
+
+void CjbmPlayer::set_opl_instrument(int channel, JBMVoice *v)
+{
+ short i = instable + (v->instr << 4);
+
+ // Sanity check on instr number - or we'll be reading outside m[] !
+
+ if (v->instr >= inscount)
+ return;
+
+ // For rhythm mode, multiplexed drums. I don't care about waveforms!
+ if ((flags&1) & (channel > 6)) {
+ opl->write(0x20 + percmx_tab[channel-7], m[i+0]);
+ opl->write(0x40 + percmx_tab[channel-7], m[i+1] ^ 0x3f);
+ opl->write(0x60 + percmx_tab[channel-7], m[i+2]);
+ opl->write(0x80 + percmx_tab[channel-7], m[i+3]);
+
+ opl->write(0xc0 + perchn_tab[channel-6], m[i+8]&15);
+ return;
+ }
+
+ // AM/VIB/EG/KSR/FRQMUL, KSL/OUTPUT, ADSR for 1st operator
+ opl->write(0x20 + op_table[channel], m[i+0]);
+ opl->write(0x40 + op_table[channel], m[i+1] ^ 0x3f);
+ opl->write(0x60 + op_table[channel], m[i+2]);
+ opl->write(0x80 + op_table[channel], m[i+3]);
+
+ // AM/VIB/EG/KSR/FRQMUL, KSL/OUTPUT, ADSR for 2nd operator
+ opl->write(0x23 + op_table[channel], m[i+4]);
+ opl->write(0x43 + op_table[channel], m[i+5] ^ 0x3f);
+ opl->write(0x63 + op_table[channel], m[i+6]);
+ opl->write(0x83 + op_table[channel], m[i+7]);
+
+ // WAVEFORM for operators
+ opl->write(0xe0 + op_table[channel], (m[i+8]>>4)&3);
+ opl->write(0xe3 + op_table[channel], (m[i+8]>>6)&3);
+
+ // FEEDBACK/FM mode
+ opl->write(0xc0 + channel, m[i+8]&15);
+
+ return;
+}
diff --git a/plugins/adplug/adplug/jbm.h b/plugins/adplug/adplug/jbm.h
new file mode 100644
index 00000000..3f95b03f
--- /dev/null
+++ b/plugins/adplug/adplug/jbm.h
@@ -0,0 +1,80 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2007 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
+ *
+ * jbm.h - JBM Player by Dennis Lindroos <lindroos@nls.fi>
+ */
+
+#ifndef H_ADPLUG_JBMPLAYER
+#define H_ADPLUG_JBMPLAYER
+
+#include "player.h"
+
+class CjbmPlayer: public CPlayer
+{
+ public:
+ static CPlayer *factory(Copl *newopl);
+
+ CjbmPlayer(Copl *newopl) : CPlayer(newopl), m(0)
+ { }
+ ~CjbmPlayer()
+ { if(m != NULL) delete [] m; }
+
+ bool load(const std::string &filename, const CFileProvider &fp);
+ bool update();
+ void rewind(int subsong);
+
+ float getrefresh()
+ { return timer; }
+
+ std::string gettype()
+ {
+ return std::string(flags&1 ? "JBM Adlib Music [rhythm mode]" :
+ "JBM Adlib Music");
+ }
+ std::string getauthor()
+ { return std::string("Johannes Bjerregaard"); }
+
+ protected:
+
+ unsigned char *m;
+ float timer;
+ unsigned short flags, voicemask;
+ unsigned short seqtable, seqcount;
+ unsigned short instable, inscount;
+ unsigned short *sequences;
+ unsigned char bdreg;
+
+ typedef struct {
+ unsigned short trkpos, trkstart, seqpos;
+ unsigned char seqno, note;
+ short vol;
+ short delay;
+ short instr;
+ unsigned char frq[2];
+ unsigned char ivol, dummy;
+ } JBMVoice;
+
+ JBMVoice voice[11];
+
+ private:
+ //void calc_opl_frequency(JBMVoice *);
+ void set_opl_instrument(int, JBMVoice *);
+ void opl_noteonoff(int, JBMVoice *, bool);
+};
+
+#endif
diff --git a/plugins/adplug/adplug/lds.h b/plugins/adplug/adplug/lds.h
index ec61ad29..95f59606 100644
--- a/plugins/adplug/adplug/lds.h
+++ b/plugins/adplug/adplug/lds.h
@@ -29,7 +29,7 @@ class CldsPlayer: public CPlayer
CldsPlayer(Copl *newopl);
virtual ~CldsPlayer();
- bool load(const std::string &fn, const CFileProvider &fp);
+ bool load(const std::string &filename, const CFileProvider &fp);
virtual bool update();
virtual void rewind(int subsong = -1);
float getrefresh() { return 70.0f; }
diff --git a/plugins/adplug/adplug/mad.cpp b/plugins/adplug/adplug/mad.cpp
index d5a60c43..b8cb527d 100644
--- a/plugins/adplug/adplug/mad.cpp
+++ b/plugins/adplug/adplug/mad.cpp
@@ -19,7 +19,7 @@
mad.cpp - MAD loader by Riven the Mage <riven@ok.ru>
*/
-#include <string.h>
+#include <cstring>
#include "mad.h"
/* -------- Public Methods -------------------------------- */
diff --git a/plugins/adplug/adplug/mid.cpp b/plugins/adplug/adplug/mid.cpp
index e3dd88c5..03a2d84a 100644
--- a/plugins/adplug/adplug/mid.cpp
+++ b/plugins/adplug/adplug/mid.cpp
@@ -1,6 +1,6 @@
/*
* Adplug - Replayer for many OPL2/OPL3 audio file formats.
- * Copyright (C) 1999 - 2006 Simon Peter, <dn.tlp@gmx.net>, et al.
+ * Copyright (C) 1999 - 2008 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
@@ -62,6 +62,11 @@
* 10/15/2005: Changes by Simon Peter
* Added rhythm mode support for CMF format.
*
+ * 09/13/2008: Changes by Adam Nielsen (malvineous@shikadi.net)
+ * Fixed a couple of CMF rhythm mode bugs
+ * Disabled note velocity for CMF files
+ * Added support for nonstandard CMF AM+VIB controller (for VGFM CMFs)
+ *
* Other acknowledgements:
* Allegro - for the midi instruments and the midi volume table
* SCUMM Revisited - for getting the .LAA / .MIDs out of those
@@ -106,9 +111,6 @@ void CmidPlayer::midiprintf(const char *format, ...)
// AdLib standard operator table
const unsigned char CmidPlayer::adlib_opadd[] = {0x00 ,0x01 ,0x02 ,0x08 ,0x09 ,0x0A ,0x10 ,0x11 ,0x12};
-// dunno
-const int CmidPlayer::ops[] = {0x20,0x20,0x40,0x40,0x60,0x60,0x80,0x80,0xe0,0xe0,0xc0};
-
// map CMF drum channels 12 - 15 to corresponding AdLib drum operators
// bass drum (channel 11) not mapped, cause it's handled like a normal instrument
const int CmidPlayer::map_chan[] = { 0x14, 0x12, 0x15, 0x11 };
@@ -300,12 +302,13 @@ bool CmidPlayer::load(const std::string &filename, const CFileProvider &fp)
if (s[1]=='T' && s[2]=='M' && s[3]=='F') good=FILE_CMF;
break;
case 0x84:
- if (s[1]==0x00 && load_sierra_ins(filename, fp))
- if (s[2]==0xf0)
- good=FILE_ADVSIERRA;
- else
- good=FILE_SIERRA;
- break;
+ if (s[1]==0x00 && load_sierra_ins(filename, fp)) {
+ if (s[2]==0xf0)
+ good=FILE_ADVSIERRA;
+ else
+ good=FILE_SIERRA;
+ }
+ break;
default:
if (s[4]=='A' && s[5]=='D') good=FILE_OLDLUCAS;
break;
@@ -346,30 +349,24 @@ void CmidPlayer::midi_fm_instrument(int voice, unsigned char *inst)
midi_write_adlib(0x20+adlib_opadd[voice],inst[0]);
midi_write_adlib(0x23+adlib_opadd[voice],inst[1]);
- if ((adlib_style&LUCAS_STYLE)!=0)
- {
+ if (adlib_style & LUCAS_STYLE) {
midi_write_adlib(0x43+adlib_opadd[voice],0x3f);
if ((inst[10] & 1)==0)
midi_write_adlib(0x40+adlib_opadd[voice],inst[2]);
- else
- midi_write_adlib(0x40+adlib_opadd[voice],0x3f);
- }
else
- {
- if ((adlib_style&SIERRA_STYLE)!=0)
- {
- midi_write_adlib(0x40+adlib_opadd[voice],inst[2]);
+ midi_write_adlib(0x40+adlib_opadd[voice],0x3f);
+
+ } else if ((adlib_style & SIERRA_STYLE) || (adlib_style & CMF_STYLE)) {
+ midi_write_adlib(0x40+adlib_opadd[voice],inst[2]);
+ midi_write_adlib(0x43+adlib_opadd[voice],inst[3]);
+
+ } else {
+ midi_write_adlib(0x40+adlib_opadd[voice],inst[2]);
+ if ((inst[10] & 1)==0)
midi_write_adlib(0x43+adlib_opadd[voice],inst[3]);
- }
- else
- {
- midi_write_adlib(0x40+adlib_opadd[voice],inst[2]);
- if ((inst[10] & 1)==0)
- midi_write_adlib(0x43+adlib_opadd[voice],inst[3]);
- else
- midi_write_adlib(0x43+adlib_opadd[voice],0);
- }
- }
+ else
+ midi_write_adlib(0x43+adlib_opadd[voice],0);
+ }
midi_write_adlib(0x60+adlib_opadd[voice],inst[4]);
midi_write_adlib(0x63+adlib_opadd[voice],inst[5]);
@@ -390,7 +387,8 @@ void CmidPlayer::midi_fm_percussion(int ch, unsigned char *inst)
midi_write_adlib(0x60 + opadd, inst[4]);
midi_write_adlib(0x80 + opadd, inst[6]);
midi_write_adlib(0xe0 + opadd, inst[8]);
- midi_write_adlib(0xc0 + opadd, inst[10]);
+ if (opadd < 0x13) // only output this for the modulator, not the carrier, as it affects the entire channel
+ midi_write_adlib(0xc0 + percussion_map[ch - 11], inst[10]);
}
void CmidPlayer::midi_fm_volume(int voice, int volume)
@@ -429,7 +427,7 @@ void CmidPlayer::midi_fm_playnote(int voice, int note, int volume)
midi_fm_volume(voice,volume);
midi_write_adlib(0xa0+voice,(unsigned char)(freq&0xff));
- c=((freq&0x300) >> 8)+(oct<<2) + (adlib_mode == ADLIB_MELODIC || voice < 6 ? (1<<5) : 0);
+ c=((freq&0x300) >> 8)+((oct&7)<<2) + (adlib_mode == ADLIB_MELODIC || voice < 6 ? (1<<5) : 0);
midi_write_adlib(0xb0+voice,(unsigned char)c);
}
@@ -541,58 +539,62 @@ bool CmidPlayer::update()
} else
on = percussion_map[c - 11];
- if (vel!=0 && ch[c].inum>=0 && ch[c].inum<128)
- {
- if (adlib_mode == ADLIB_MELODIC || c < 12)
+ if (vel!=0 && ch[c].inum>=0 && ch[c].inum<128) {
+ if (adlib_mode == ADLIB_MELODIC || c < 12) // 11 == bass drum, handled like a normal instrument, on == channel 6 thanks to percussion_map[] above
midi_fm_instrument(on,ch[c].ins);
else
midi_fm_percussion(c, ch[c].ins);
- if ((adlib_style&MIDI_STYLE)!=0)
- {
+ if (adlib_style & MIDI_STYLE) {
nv=((ch[c].vol*vel)/128);
- if ((adlib_style&LUCAS_STYLE)!=0)
- nv*=2;
+ if ((adlib_style&LUCAS_STYLE)!=0) nv*=2;
if (nv>127) nv=127;
nv=my_midi_fm_vol_table[nv];
if ((adlib_style&LUCAS_STYLE)!=0)
nv=(int)((float)sqrt((float)nv)*11);
- }
- else
- {
+ } else if (adlib_style & CMF_STYLE) {
+ // CMF doesn't support note velocity (even though some files have them!)
+ nv = 127;
+ } else {
nv=vel;
- }
+ }
- midi_fm_playnote(on,note+ch[c].nshift,nv*2);
+ midi_fm_playnote(on,note+ch[c].nshift,nv*2); // sets freq in rhythm mode
chp[on][0]=c;
chp[on][1]=note;
chp[on][2]=0;
if(adlib_mode == ADLIB_RYTHM && c >= 11) {
+ // Still need to turn off the perc instrument before playing it again,
+ // as not all songs send a noteoff.
midi_write_adlib(0xbd, adlib_data[0xbd] & ~(0x10 >> (c - 11)));
+ // Play the perc instrument
midi_write_adlib(0xbd, adlib_data[0xbd] | (0x10 >> (c - 11)));
}
- }
- else
- {
- if (vel==0) //same code as end note
- {
- for (i=0; i<9; i++)
- if (chp[i][0]==c && chp[i][1]==note)
- {
- // midi_fm_volume(i,0); // really end the note
- midi_fm_endnote(i);
- chp[i][0]=-1;
+ } else {
+ if (vel==0) { //same code as end note
+ if (adlib_mode == ADLIB_RYTHM && c >= 11) {
+ // Turn off the percussion instrument
+ midi_write_adlib(0xbd, adlib_data[0xbd] & ~(0x10 >> (c - 11)));
+ //midi_fm_endnote(percussion_map[c]);
+ chp[percussion_map[c - 11]][0]=-1;
+ } else {
+ for (i=0; i<9; i++) {
+ if (chp[i][0]==c && chp[i][1]==note) {
+ // midi_fm_volume(i,0); // really end the note
+ midi_fm_endnote(i);
+ chp[i][0]=-1;
}
+ }
}
- else
- { // i forget what this is for.
+ } else {
+ // i forget what this is for.
chp[on][0]=-1;
chp[on][2]=0;
- }
}
- midiprintf(" [%d:%d:%d:%d]\n",c,ch[c].inum,note,vel);
+ }
+ midiprintf(" [%d:%d:%d:%d]\n",c,ch[c].inum,note,vel);
}
else
midiprintf ("off");
@@ -616,8 +618,25 @@ midi_fm_playnote(i,note+cnote[c],my_midi_fm_vol_table[(cvols[c]*vel)/128]*2);
ch[c].vol=vel;
midiprintf("vol");
break;
+ case 0x63:
+ if (adlib_style & CMF_STYLE) {
+ // Custom extension to allow CMF files to switch the
+ // AM+VIB depth on and off (officially this is on,
+ // and there's no way to switch it off.) Controller
+ // values:
+ // 0 == AM+VIB off
+ // 1 == VIB on
+ // 2 == AM on
+ // 3 == AM+VIB on
+ midi_write_adlib(0xbd, (adlib_data[0xbd] & ~0xC0) | (vel << 6));
+ midiprintf(" AM+VIB depth change - AM %s, VIB %s\n",
+ (adlib_data[0xbd] & 0x80) ? "on" : "off",
+ (adlib_data[0xbd] & 0x40) ? "on" : "off"
+ );
+ }
+ break;
case 0x67:
- midiprintf ("\n\nhere:%d\n\n",vel);
+ midiprintf("Rhythm mode: %d\n", vel);
if ((adlib_style&CMF_STYLE)!=0) {
adlib_mode=vel;
if(adlib_mode == ADLIB_RYTHM)
@@ -811,11 +830,12 @@ fwait=1.0f/(((float)iwait/(float)deltas)*((float)msqtr/(float)1000000));
midiprintf ("\n");
for (i=0; i<16; i++)
- if (track[i].on)
- if (track[i].pos < track[i].tend)
- midiprintf ("<%d>",track[i].iwait);
- else
- midiprintf("stop");
+ if (track[i].on) {
+ if (track[i].pos < track[i].tend)
+ midiprintf ("<%d>",track[i].iwait);
+ else
+ midiprintf("stop");
+ }
/*
if (ret==0 && type==FILE_ADVSIERRA)
diff --git a/plugins/adplug/adplug/mid.h b/plugins/adplug/adplug/mid.h
index d28f7252..5dfe9e76 100644
--- a/plugins/adplug/adplug/mid.h
+++ b/plugins/adplug/adplug/mid.h
@@ -1,6 +1,6 @@
/*
* Adplug - Replayer for many OPL2/OPL3 audio file formats.
- * Copyright (C) 1999 - 2005 Simon Peter, <dn.tlp@gmx.net>, et al.
+ * Copyright (C) 1999 - 2008 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
diff --git a/plugins/adplug/adplug/mkj.cpp b/plugins/adplug/adplug/mkj.cpp
index 32d7e27c..38c1c082 100644
--- a/plugins/adplug/adplug/mkj.cpp
+++ b/plugins/adplug/adplug/mkj.cpp
@@ -19,8 +19,8 @@
* mkj.cpp - MKJamz Player, by Simon Peter <dn.tlp@gmx.net>
*/
+#include <cstring>
#include <assert.h>
-#include <string.h>
#include "mkj.h"
#include "debug.h"
diff --git a/plugins/adplug/adplug/msc.cpp b/plugins/adplug/adplug/msc.cpp
index 3702adcf..2a10c5ce 100644
--- a/plugins/adplug/adplug/msc.cpp
+++ b/plugins/adplug/adplug/msc.cpp
@@ -19,8 +19,8 @@
* msc.c - MSC Player by Lubomir Bulej (pallas@kadan.cz)
*/
+#include <cstring>
#include <stdio.h>
-#include <string.h>
#include "msc.h"
#include "debug.h"
diff --git a/plugins/adplug/adplug/mtk.cpp b/plugins/adplug/adplug/mtk.cpp
index 410e00e0..6bafc853 100644
--- a/plugins/adplug/adplug/mtk.cpp
+++ b/plugins/adplug/adplug/mtk.cpp
@@ -19,7 +19,7 @@
* mtk.cpp - MPU-401 Trakker Loader by Simon Peter (dn.tlp@gmx.net)
*/
-#include <string.h>
+#include <cstring>
#include "mtk.h"
/*** public methods **************************************/
diff --git a/plugins/adplug/adplug/player.h b/plugins/adplug/adplug/player.h
index 6b99c34b..8c474daa 100644
--- a/plugins/adplug/adplug/player.h
+++ b/plugins/adplug/adplug/player.h
@@ -37,7 +37,7 @@ public:
/***** Operational methods *****/
void seek(unsigned long ms);
- virtual bool load(const std::string &fn, // loads file
+ virtual bool load(const std::string &filename, // loads file
const CFileProvider &fp = CProvider_Filesystem()) = 0;
virtual bool update() = 0; // executes replay code for 1 tick
virtual void rewind(int subsong = -1) = 0; // rewinds to specified subsong
@@ -48,29 +48,31 @@ public:
virtual std::string gettype() = 0; // returns file type
virtual std::string gettitle() // returns song title
- { return std::string(); }
+ { return std::string(); }
virtual std::string getauthor() // returns song author name
- { return std::string(); }
+ { return std::string(); }
virtual std::string getdesc() // returns song description
- { return std::string(); }
+ { return std::string(); }
virtual unsigned int getpatterns() // returns number of patterns
- { return 0; }
+ { return 0; }
virtual unsigned int getpattern() // returns currently playing pattern
- { return 0; }
+ { return 0; }
virtual unsigned int getorders() // returns size of orderlist
- { return 0; }
+ { return 0; }
virtual unsigned int getorder() // returns currently playing song position
- { return 0; }
+ { return 0; }
virtual unsigned int getrow() // returns currently playing row
- { return 0; }
+ { return 0; }
virtual unsigned int getspeed() // returns current song speed
- { return 0; }
+ { return 0; }
virtual unsigned int getsubsongs() // returns number of subsongs
- { return 1; }
+ { return 1; }
+ virtual unsigned int getsubsong() // returns current subsong
+ { return 0; }
virtual unsigned int getinstruments() // returns number of instruments
- { return 0; }
+ { return 0; }
virtual std::string getinstrument(unsigned int n) // returns n-th instrument name
- { return std::string(); }
+ { return std::string(); }
protected:
Copl *opl; // our OPL chip
diff --git a/plugins/adplug/adplug/protrack.cpp b/plugins/adplug/adplug/protrack.cpp
index 86716ef8..ebb1570b 100644
--- a/plugins/adplug/adplug/protrack.cpp
+++ b/plugins/adplug/adplug/protrack.cpp
@@ -25,7 +25,7 @@
* Protracker-like format, this is most certainly the player you want to use.
*/
-#include <string.h>
+#include <cstring>
#include "protrack.h"
#include "debug.h"
@@ -72,7 +72,7 @@ bool CmodPlayer::update()
for(chan = 0; chan < nchans; chan++) {
oplchan = set_opl_chip(chan);
- if(arplist && arpcmd && inst[channel[chan].inst].arpstart) // special arpeggio
+ if(arplist && arpcmd && inst[channel[chan].inst].arpstart) { // special arpeggio
if(channel[chan].arpspdcnt)
channel[chan].arpspdcnt--;
else
@@ -107,6 +107,7 @@ bool CmodPlayer::update()
channel[chan].arppos++;
channel[chan].arpspdcnt = inst[channel[chan].inst].arpspeed - 1;
}
+ }
info1 = channel[chan].info1;
info2 = channel[chan].info2;
@@ -631,8 +632,8 @@ void CmodPlayer::setvolume_alt(unsigned char chan)
unsigned char ivol2 = inst[channel[chan].inst].data[9] & 63;
unsigned char ivol1 = inst[channel[chan].inst].data[10] & 63;
- opl->write(0x40 + op_table[oplchan], (((63 - channel[chan].vol2 & 63) + ivol2) >> 1) + (inst[channel[chan].inst].data[9] & 192));
- opl->write(0x43 + op_table[oplchan], (((63 - channel[chan].vol1 & 63) + ivol1) >> 1) + (inst[channel[chan].inst].data[10] & 192));
+ opl->write(0x40 + op_table[oplchan], (((63 - (channel[chan].vol2 & 63)) + ivol2) >> 1) + (inst[channel[chan].inst].data[9] & 192));
+ opl->write(0x43 + op_table[oplchan], (((63 - (channel[chan].vol1 & 63)) + ivol1) >> 1) + (inst[channel[chan].inst].data[10] & 192));
}
void CmodPlayer::setfreq(unsigned char chan)
@@ -679,13 +680,14 @@ void CmodPlayer::playnote(unsigned char chan)
void CmodPlayer::setnote(unsigned char chan, int note)
{
- if(note > 96)
+ if(note > 96) {
if(note == 127) { // key off
channel[chan].key = 0;
setfreq(chan);
return;
} else
note = 96;
+ }
if(note < 13)
channel[chan].freq = notetable[note - 1];
@@ -701,23 +703,25 @@ void CmodPlayer::setnote(unsigned char chan, int note)
void CmodPlayer::slide_down(unsigned char chan, int amount)
{
channel[chan].freq -= amount;
- if(channel[chan].freq <= 342)
+ if(channel[chan].freq <= 342) {
if(channel[chan].oct) {
channel[chan].oct--;
channel[chan].freq <<= 1;
} else
channel[chan].freq = 342;
+ }
}
void CmodPlayer::slide_up(unsigned char chan, int amount)
{
channel[chan].freq += amount;
- if(channel[chan].freq >= 686)
+ if(channel[chan].freq >= 686) {
if(channel[chan].oct < 7) {
channel[chan].oct++;
channel[chan].freq >>= 1;
} else
channel[chan].freq = 686;
+ }
}
void CmodPlayer::tone_portamento(unsigned char chan, unsigned char info)
@@ -799,11 +803,12 @@ void CmodPlayer::vol_up_alt(unsigned char chan, int amount)
channel[chan].vol1 += amount;
else
channel[chan].vol1 = 63;
- if(inst[channel[chan].inst].data[0] & 1)
+ if(inst[channel[chan].inst].data[0] & 1) {
if(channel[chan].vol2 + amount < 63)
channel[chan].vol2 += amount;
else
channel[chan].vol2 = 63;
+ }
}
void CmodPlayer::vol_down_alt(unsigned char chan, int amount)
@@ -812,9 +817,10 @@ void CmodPlayer::vol_down_alt(unsigned char chan, int amount)
channel[chan].vol1 -= amount;
else
channel[chan].vol1 = 0;
- if(inst[channel[chan].inst].data[0] & 1)
+ if(inst[channel[chan].inst].data[0] & 1) {
if(channel[chan].vol2 - amount > 0)
channel[chan].vol2 -= amount;
else
channel[chan].vol2 = 0;
+ }
}
diff --git a/plugins/adplug/adplug/rad.cpp b/plugins/adplug/adplug/rad.cpp
index 0178a6c3..7996d9ae 100644
--- a/plugins/adplug/adplug/rad.cpp
+++ b/plugins/adplug/adplug/rad.cpp
@@ -22,7 +22,7 @@
* some volumes are dropped out
*/
-#include <string.h>
+#include <cstring>
#include "rad.h"
CPlayer *CradLoader::factory(Copl *newopl)
diff --git a/plugins/adplug/adplug/rat.cpp b/plugins/adplug/adplug/rat.cpp
index b20af250..34f97cb6 100644
--- a/plugins/adplug/adplug/rat.cpp
+++ b/plugins/adplug/adplug/rat.cpp
@@ -29,7 +29,7 @@
comment : there are bug in original replayer's adlib_init(): wrong frequency registers.
*/
-#include <string.h>
+#include <cstring>
#include "rat.h"
#include "debug.h"
diff --git a/plugins/adplug/adplug/raw.cpp b/plugins/adplug/adplug/raw.cpp
index 1672a1d0..0ca3f36b 100644
--- a/plugins/adplug/adplug/raw.cpp
+++ b/plugins/adplug/adplug/raw.cpp
@@ -19,7 +19,7 @@
* raw.c - RAW Player by Simon Peter <dn.tlp@gmx.net>
*/
-#include <string.h>
+#include <cstring>
#include "raw.h"
/*** public methods *************************************/
diff --git a/plugins/adplug/adplug/rix.cpp b/plugins/adplug/adplug/rix.cpp
index 02ebc8d5..5cd69bd1 100644
--- a/plugins/adplug/adplug/rix.cpp
+++ b/plugins/adplug/adplug/rix.cpp
@@ -20,7 +20,7 @@
* BSPAL <BSPAL.ys168.com>
*/
-#include <string.h>
+#include <cstring>
#include "rix.h"
#include "debug.h"
diff --git a/plugins/adplug/adplug/rol.cpp b/plugins/adplug/adplug/rol.cpp
index 7507d79b..a2a88fe8 100644
--- a/plugins/adplug/adplug/rol.cpp
+++ b/plugins/adplug/adplug/rol.cpp
@@ -20,6 +20,7 @@
*
* Visit: http://tenacity.hispeed.com/aomit/oplx/
*/
+#include <cstring>
#include <algorithm>
#include "rol.h"
@@ -665,7 +666,7 @@ int CrolPlayer::load_rol_instrument( binistream *f, SBnkHeader const &header, st
else
{
// set up default instrument data here
- memset( &usedIns.instrument, 0, kSizeofDataRecord );
+ memset( &usedIns.instrument, 0, sizeof(SRolInstrument) );
}
ins_list.push_back( usedIns );
diff --git a/plugins/adplug/adplug/rol.h b/plugins/adplug/adplug/rol.h
index fdb9fabb..82337adf 100644
--- a/plugins/adplug/adplug/rol.h
+++ b/plugins/adplug/adplug/rol.h
@@ -1,6 +1,6 @@
/*
* Adplug - Replayer for many OPL2/OPL3 audio file formats.
- * Copyright (C) 1999 - 2004 Simon Peter, <dn.tlp@gmx.net>, et al.
+ * Copyright (C) 1999 - 2008 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
@@ -25,7 +25,7 @@
#include <vector>
#include <string>
-#include <string.h>
+#include <strings.h>
#include "player.h"
diff --git a/plugins/adplug/adplug/s3m.cpp b/plugins/adplug/adplug/s3m.cpp
index ddc2a3b9..ff0baa35 100644
--- a/plugins/adplug/adplug/s3m.cpp
+++ b/plugins/adplug/adplug/s3m.cpp
@@ -22,7 +22,7 @@
* Extra Fine Slides (EEx, FEx) & Fine Vibrato (Uxy) are inaccurate
*/
-#include <string.h>
+#include <cstring>
#include "s3m.h"
const char Cs3mPlayer::chnresolv[] = // S3M -> adlib channel conversion
@@ -164,16 +164,18 @@ bool Cs3mPlayer::update()
vibrato(realchan,channel[realchan].dualinfo);
else // dual command: G00 and Dxy
tone_portamento(realchan,channel[realchan].dualinfo);
- case 4: if(info <= 0x0f) // volume slide down
+ case 4: if(info <= 0x0f) { // volume slide down
if(channel[realchan].vol - info >= 0)
channel[realchan].vol -= info;
else
channel[realchan].vol = 0;
- if((info & 0x0f) == 0) // volume slide up
+ }
+ if((info & 0x0f) == 0) { // volume slide up
if(channel[realchan].vol + (info >> 4) <= 63)
channel[realchan].vol += info >> 4;
else
channel[realchan].vol = 63;
+ }
setvolume(realchan);
break;
case 5: if(info == 0xf0 || info <= 0xe0) { // slide down
@@ -247,7 +249,7 @@ bool Cs3mPlayer::update()
if(realchan != -1) { // channel playable?
// set channel values
donote = 0;
- if(pattern[pattnr][row][chan].note < 14)
+ if(pattern[pattnr][row][chan].note < 14) {
// tone portamento
if(pattern[pattnr][row][chan].command == 7 || pattern[pattnr][row][chan].command == 12) {
channel[realchan].nextfreq = notetable[pattern[pattnr][row][chan].note];
@@ -259,6 +261,7 @@ bool Cs3mPlayer::update()
channel[realchan].key = 1;
donote = 1;
}
+ }
if(pattern[pattnr][row][chan].note == 14) { // key off (is 14 here, cause note is only first 4 bits)
channel[realchan].key = 0;
setfreq(realchan);
@@ -284,11 +287,12 @@ bool Cs3mPlayer::update()
if(pattern[pattnr][row][chan].command != 7)
donote = 1;
}
- if(pattern[pattnr][row][chan].volume != 255)
+ if(pattern[pattnr][row][chan].volume != 255) {
if(pattern[pattnr][row][chan].volume < 64) // set volume
channel[realchan].vol = pattern[pattnr][row][chan].volume;
else
channel[realchan].vol = 63;
+ }
channel[realchan].fx = pattern[pattnr][row][chan].command; // set command
if(pattern[pattnr][row][chan].info) // set infobyte
channel[realchan].info = pattern[pattnr][row][chan].info;
@@ -315,16 +319,18 @@ bool Cs3mPlayer::update()
case 1: speed = info; break; // set speed
case 2: if(info <= ord) songend = 1; ord = info; crow = 0; pattbreak = 1; break; // jump to order
case 3: if(!pattbreak) { crow = info; ord++; pattbreak = 1; } break; // pattern break
- case 4: if(info > 0xf0) // fine volume down
+ case 4: if(info > 0xf0) { // fine volume down
if(channel[realchan].vol - (info & 0x0f) >= 0)
channel[realchan].vol -= info & 0x0f;
else
channel[realchan].vol = 0;
- if((info & 0x0f) == 0x0f && info >= 0x1f) // fine volume up
+ }
+ if((info & 0x0f) == 0x0f && info >= 0x1f) { // fine volume up
if(channel[realchan].vol + ((info & 0xf0) >> 4) <= 63)
channel[realchan].vol += (info & 0xf0) >> 4;
else
channel[realchan].vol = 63;
+ }
setvolume(realchan);
break;
case 5: if(info > 0xf0) { // fine slide down
@@ -353,7 +359,7 @@ bool Cs3mPlayer::update()
case 10: channel[realchan].trigger = 0; break; // arpeggio (set trigger)
case 19: if(info == 0xb0) // set loop start
loopstart = row;
- if(info > 0xb0 && info <= 0xbf) // pattern loop
+ if(info > 0xb0 && info <= 0xbf) { // pattern loop
if(!loopcnt) {
loopcnt = info & 0x0f;
crow = loopstart;
@@ -363,6 +369,7 @@ bool Cs3mPlayer::update()
crow = loopstart;
pattbreak = 1;
}
+ }
if((info & 0xf0) == 0xe0) // patterndelay
del = speed * (info & 0x0f) - 1;
break;
@@ -450,7 +457,7 @@ void Cs3mPlayer::setfreq(unsigned char chan)
{
opl->write(0xa0 + chan, channel[chan].freq & 255);
if(channel[chan].key)
- opl->write(0xb0 + chan, ((channel[chan].freq & 768) >> 8) + (channel[chan].oct << 2) | 32);
+ opl->write(0xb0 + chan, (((channel[chan].freq & 768) >> 8) + (channel[chan].oct << 2)) | 32);
else
opl->write(0xb0 + chan, ((channel[chan].freq & 768) >> 8) + (channel[chan].oct << 2));
}
diff --git a/plugins/adplug/adplug/sa2.cpp b/plugins/adplug/adplug/sa2.cpp
index 2e1248bc..8d81e9c1 100644
--- a/plugins/adplug/adplug/sa2.cpp
+++ b/plugins/adplug/adplug/sa2.cpp
@@ -1,6 +1,6 @@
/*
* Adplug - Replayer for many OPL2/OPL3 audio file formats.
- * Copyright (C) 1999 - 2007 Simon Peter, <dn.tlp@gmx.net>, et al.
+ * Copyright (C) 1999 - 2008 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
@@ -20,8 +20,9 @@
* SAdT Loader by Mamiya <mamiya@users.sourceforge.net>
*/
-#include <string.h>
+#include <cstring>
#include <stdio.h>
+#include <string.h>
#include "sa2.h"
#include "debug.h"
diff --git a/plugins/adplug/adplug/sng.cpp b/plugins/adplug/adplug/sng.cpp
index bf0fa699..5e8b6af4 100644
--- a/plugins/adplug/adplug/sng.cpp
+++ b/plugins/adplug/adplug/sng.cpp
@@ -19,7 +19,7 @@
* sng.cpp - SNG Player by Simon Peter <dn.tlp@gmx.net>
*/
-#include <string.h>
+#include <cstring>
#include "sng.h"
CPlayer *CsngPlayer::factory(Copl *newopl)
diff --git a/plugins/adplug/adplug/surroundopl.cpp b/plugins/adplug/adplug/surroundopl.cpp
new file mode 100644
index 00000000..14e67e21
--- /dev/null
+++ b/plugins/adplug/adplug/surroundopl.cpp
@@ -0,0 +1,203 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2010 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
+ *
+ * surroundopl.cpp - Wrapper class to provide a surround/harmonic effect
+ * for another OPL emulator, by Adam Nielsen <malvineous@shikadi.net>
+ *
+ * Stereo harmonic algorithm by Adam Nielsen <malvineous@shikadi.net>
+ * Please give credit if you use this algorithm elsewhere :-)
+ */
+
+#include <math.h> // for pow()
+#include "surroundopl.h"
+#include "debug.h"
+
+CSurroundopl::CSurroundopl(Copl *a, Copl *b, bool use16bit)
+ : use16bit(use16bit),
+ bufsize(4096),
+ a(a), b(b)
+{
+ currType = TYPE_OPL2;
+ this->lbuf = new short[this->bufsize];
+ this->rbuf = new short[this->bufsize];
+};
+
+CSurroundopl::~CSurroundopl()
+{
+ delete[] this->rbuf;
+ delete[] this->lbuf;
+ delete a;
+ delete b;
+}
+
+void CSurroundopl::update(short *buf, int samples)
+{
+ if (samples * 2 > this->bufsize) {
+ // Need to realloc the buffer
+ delete[] this->rbuf;
+ delete[] this->lbuf;
+ this->bufsize = samples * 2;
+ this->lbuf = new short[this->bufsize];
+ this->rbuf = new short[this->bufsize];
+ }
+
+ a->update(this->lbuf, samples);
+ b->update(this->rbuf, samples);
+
+ // Copy the two mono OPL buffers into the stereo buffer
+ for (int i = 0; i < samples; i++) {
+ if (this->use16bit) {
+ buf[i * 2] = this->lbuf[i];
+ buf[i * 2 + 1] = this->rbuf[i];
+ } else {
+ ((char *)buf)[i * 2] = ((char *)this->lbuf)[i];
+ ((char *)buf)[i * 2 + 1] = ((char *)this->rbuf)[i];
+ }
+ }
+
+}
+
+// template methods
+void CSurroundopl::write(int reg, int val)
+{
+ a->write(reg, val);
+
+ // Transpose the other channel to produce the harmonic effect
+
+ int iChannel = -1;
+ int iRegister = reg; // temp
+ int iValue = val; // temp
+ if ((iRegister >> 4 == 0xA) || (iRegister >> 4 == 0xB)) iChannel = iRegister & 0x0F;
+
+ // Remember the FM state, so that the harmonic effect can access
+ // previously assigned register values.
+ /*if (((iRegister >> 4 == 0xB) && (iValue & 0x20) && !(this->iFMReg[iRegister] & 0x20)) ||
+ (iRegister == 0xBD) && (
+ ((iValue & 0x01) && !(this->iFMReg[0xBD] & 0x01))
+ )) {
+ this->iFMReg[iRegister] = iValue;
+ }*/
+ this->iFMReg[iRegister] = iValue;
+
+ if ((iChannel >= 0)) {// && (i == 1)) {
+ uint8_t iBlock = (this->iFMReg[0xB0 + iChannel] >> 2) & 0x07;
+ uint16_t iFNum = ((this->iFMReg[0xB0 + iChannel] & 0x03) << 8) | this->iFMReg[0xA0 + iChannel];
+ //double dbOriginalFreq = 50000.0 * (double)iFNum * pow(2, iBlock - 20);
+ double dbOriginalFreq = 49716.0 * (double)iFNum * pow(2, iBlock - 20);
+
+ uint8_t iNewBlock = iBlock;
+ uint16_t iNewFNum;
+
+ // Adjust the frequency and calculate the new FNum
+ //double dbNewFNum = (dbOriginalFreq+(dbOriginalFreq/FREQ_OFFSET)) / (50000.0 * pow(2, iNewBlock - 20));
+ //#define calcFNum() ((dbOriginalFreq+(dbOriginalFreq/FREQ_OFFSET)) / (50000.0 * pow(2, iNewBlock - 20)))
+ #define calcFNum() ((dbOriginalFreq+(dbOriginalFreq/FREQ_OFFSET)) / (49716.0 * pow(2, iNewBlock - 20)))
+ double dbNewFNum = calcFNum();
+
+ // Make sure it's in range for the OPL chip
+ if (dbNewFNum > 1023 - NEWBLOCK_LIMIT) {
+ // It's too high, so move up one block (octave) and recalculate
+
+ if (iNewBlock > 6) {
+ // Uh oh, we're already at the highest octave!
+ AdPlug_LogWrite("OPL WARN: FNum %d/B#%d would need block 8+ after being transposed (new FNum is %d)\n",
+ iFNum, iBlock, (int)dbNewFNum);
+ // The best we can do here is to just play the same note out of the second OPL, so at least it shouldn't
+ // sound *too* bad (hopefully it will just miss out on the nice harmonic.)
+ iNewBlock = iBlock;
+ iNewFNum = iFNum;
+ } else {
+ iNewBlock++;
+ iNewFNum = (uint16_t)calcFNum();
+ }
+ } else if (dbNewFNum < 0 + NEWBLOCK_LIMIT) {
+ // It's too low, so move down one block (octave) and recalculate
+
+ if (iNewBlock == 0) {
+ // Uh oh, we're already at the lowest octave!
+ AdPlug_LogWrite("OPL WARN: FNum %d/B#%d would need block -1 after being transposed (new FNum is %d)!\n",
+ iFNum, iBlock, (int)dbNewFNum);
+ // The best we can do here is to just play the same note out of the second OPL, so at least it shouldn't
+ // sound *too* bad (hopefully it will just miss out on the nice harmonic.)
+ iNewBlock = iBlock;
+ iNewFNum = iFNum;
+ } else {
+ iNewBlock--;
+ iNewFNum = (uint16_t)calcFNum();
+ }
+ } else {
+ // Original calculation is within range, use that
+ iNewFNum = (uint16_t)dbNewFNum;
+ }
+
+ // Sanity check
+ if (iNewFNum > 1023) {
+ // Uh oh, the new FNum is still out of range! (This shouldn't happen)
+ AdPlug_LogWrite("OPL ERR: Original note (FNum %d/B#%d is still out of range after change to FNum %d/B#%d!\n",
+ iFNum, iBlock, iNewFNum, iNewBlock);
+ // The best we can do here is to just play the same note out of the second OPL, so at least it shouldn't
+ // sound *too* bad (hopefully it will just miss out on the nice harmonic.)
+ iNewBlock = iBlock;
+ iNewFNum = iFNum;
+ }
+
+ if ((iRegister >= 0xB0) && (iRegister <= 0xB8)) {
+
+ // Overwrite the supplied value with the new F-Number and Block.
+ iValue = (iValue & ~0x1F) | (iNewBlock << 2) | ((iNewFNum >> 8) & 0x03);
+
+ this->iCurrentTweakedBlock[iChannel] = iNewBlock; // save it so we don't have to update register 0xB0 later on
+ this->iCurrentFNum[iChannel] = iNewFNum;
+
+ if (this->iTweakedFMReg[0xA0 + iChannel] != (iNewFNum & 0xFF)) {
+ // Need to write out low bits
+ uint8_t iAdditionalReg = 0xA0 + iChannel;
+ uint8_t iAdditionalValue = iNewFNum & 0xFF;
+ b->write(iAdditionalReg, iAdditionalValue);
+ this->iTweakedFMReg[iAdditionalReg] = iAdditionalValue;
+ }
+ } else if ((iRegister >= 0xA0) && (iRegister <= 0xA8)) {
+
+ // Overwrite the supplied value with the new F-Number.
+ iValue = iNewFNum & 0xFF;
+
+ // See if we need to update the block number, which is stored in a different register
+ uint8_t iNewB0Value = (this->iFMReg[0xB0 + iChannel] & ~0x1F) | (iNewBlock << 2) | ((iNewFNum >> 8) & 0x03);
+ if (
+ (iNewB0Value & 0x20) && // but only update if there's a note currently playing (otherwise we can just wait
+ (this->iTweakedFMReg[0xB0 + iChannel] != iNewB0Value) // until the next noteon and update it then)
+ ) {
+ AdPlug_LogWrite("OPL INFO: CH%d - FNum %d/B#%d -> FNum %d/B#%d == keyon register update!\n",
+ iChannel, iFNum, iBlock, iNewFNum, iNewBlock);
+ // The note is already playing, so we need to adjust the upper bits too
+ uint8_t iAdditionalReg = 0xB0 + iChannel;
+ b->write(iAdditionalReg, iNewB0Value);
+ this->iTweakedFMReg[iAdditionalReg] = iNewB0Value;
+ } // else the note is not playing, the upper bits will be set when the note is next played
+
+ } // if (register 0xB0 or 0xA0)
+
+ } // if (a register we're interested in)
+
+ // Now write to the original register with a possibly modified value
+ b->write(iRegister, iValue);
+ this->iTweakedFMReg[iRegister] = iValue;
+
+};
+
+void CSurroundopl::init() {};
diff --git a/plugins/adplug/adplug/surroundopl.h b/plugins/adplug/adplug/surroundopl.h
new file mode 100644
index 00000000..d302a9e8
--- /dev/null
+++ b/plugins/adplug/adplug/surroundopl.h
@@ -0,0 +1,69 @@
+/*
+ * Adplug - Replayer for many OPL2/OPL3 audio file formats.
+ * Copyright (C) 1999 - 2010 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
+ *
+ * surroundopl.h - Wrapper class to provide a surround/harmonic effect
+ * for another OPL emulator, by Adam Nielsen <malvineous@shikadi.net>
+ *
+ * Stereo harmonic algorithm by Adam Nielsen <malvineous@shikadi.net>
+ * Please give credit if you use this algorithm elsewhere :-)
+ */
+
+#ifndef H_ADPLUG_SURROUNDOPL
+#define H_ADPLUG_SURROUNDOPL
+
+#include <stdint.h> // for uintxx_t
+#include "opl.h"
+
+// The right-channel is increased in frequency by itself divided by this amount.
+// The right value should not noticeably change the pitch, but it should provide
+// a nice stereo harmonic effect.
+#define FREQ_OFFSET 128.0//96.0
+
+// Number of FNums away from the upper/lower limit before switching to the next
+// block (octave.) By rights it should be zero, but for some reason this seems
+// to cut it to close and the transposed OPL doesn't hit the right note all the
+// time. Setting it higher means it will switch blocks sooner and that seems
+// to help. Don't set it too high or it'll get stuck in an infinite loop if
+// one block is too high and the adjacent block is too low ;-)
+#define NEWBLOCK_LIMIT 32
+
+class CSurroundopl: public Copl
+{
+ private:
+ bool use16bit;
+ short bufsize;
+ short *lbuf, *rbuf;
+ Copl *a, *b;
+ uint8_t iFMReg[256];
+ uint8_t iTweakedFMReg[256];
+ uint8_t iCurrentTweakedBlock[9]; // Current value of the Block in the tweaked OPL chip
+ uint8_t iCurrentFNum[9]; // Current value of the FNum in the tweaked OPL chip
+
+ public:
+
+ CSurroundopl(Copl *a, Copl *b, bool use16bit);
+ ~CSurroundopl();
+
+ void update(short *buf, int samples);
+ void write(int reg, int val);
+
+ void init();
+
+};
+
+#endif
diff --git a/plugins/adplug/adplug/xsm.h b/plugins/adplug/adplug/xsm.h
index 15129606..0e844afc 100644
--- a/plugins/adplug/adplug/xsm.h
+++ b/plugins/adplug/adplug/xsm.h
@@ -29,7 +29,7 @@ public:
CxsmPlayer(Copl *newopl);
~CxsmPlayer();
- bool load(const std::string &fn, const CFileProvider &fp);
+ bool load(const std::string &filename, const CFileProvider &fp);
bool update();
void rewind(int subsong);
float getrefresh();