diff options
| -rw-r--r-- | sound/fmopl.cpp | 90 | ||||
| -rw-r--r-- | sound/fmopl.h | 123 | ||||
| -rw-r--r-- | sound/module.mk | 2 | ||||
| -rw-r--r-- | sound/softsynth/opl/dosbox.cpp | 355 | ||||
| -rw-r--r-- | sound/softsynth/opl/dosbox.h | 122 | ||||
| -rw-r--r-- | sound/softsynth/opl/mame.cpp | 46 | ||||
| -rw-r--r-- | sound/softsynth/opl/mame.h | 34 | ||||
| -rw-r--r-- | sound/softsynth/opl/opl.cpp | 1445 | ||||
| -rw-r--r-- | sound/softsynth/opl/opl.h | 197 | 
9 files changed, 2408 insertions, 6 deletions
| diff --git a/sound/fmopl.cpp b/sound/fmopl.cpp new file mode 100644 index 0000000000..7bc33fc724 --- /dev/null +++ b/sound/fmopl.cpp @@ -0,0 +1,90 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + */ + +#include "sound/fmopl.h" + +#include "sound/softsynth/opl/dosbox.h" +#include "sound/softsynth/opl/mame.h" + +namespace OPL { + +bool OPL::_hasInstance = false; + +OPL *OPL::create(kOplType type) { +	// Simple hack to assure we only create one instance, +	// since the DOSBox OPL emulator does not allow more than +	// one instance. +	assert(!_hasInstance); +	_hasInstance = true; + +	if (type == kOpl2) +		return new MAME::OPL_MAME(); +	else +#ifndef DISABLE_DOSBOX_ADLIB +		return new DOSBox::OPL_DOSBox(type); +#else +		return 0; +#endif +} + +} // end of namespace OPL + +void OPLDestroy(FM_OPL *OPL) { +	delete OPL; +} + +void OPLResetChip(FM_OPL *OPL) { +	OPL->reset(); +} + +void OPLWrite(FM_OPL *OPL, int a, int v) { +	OPL->write(a, v); +} + +unsigned char OPLRead(FM_OPL *OPL, int a) { +	return OPL->read(a); +} + +void OPLWriteReg(FM_OPL *OPL, int r, int v) { +	OPL->writeReg(r, v); +} + +void YM3812UpdateOne(FM_OPL *OPL, int16 *buffer, int length) { +	OPL->readBuffer(buffer, length); +} + +// Factory method +FM_OPL *makeAdlibOPL(int rate) { +	FM_OPL *opl = OPL::OPL::create(); + +	if (opl) { +		if (!opl->init(rate)) { +			delete opl; +			opl = 0; +		} +	} + +	return opl; +} + diff --git a/sound/fmopl.h b/sound/fmopl.h new file mode 100644 index 0000000000..ad0fb9eb6f --- /dev/null +++ b/sound/fmopl.h @@ -0,0 +1,123 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + */ + +#ifndef SOUND_FMOPL_H +#define SOUND_FMOPL_H + +#include "common/scummsys.h" + +namespace OPL { + +class OPL { +private: +	// TODO: This is part of a temporary HACK to allow only 1 instance +	static bool _hasInstance; +public: +	virtual ~OPL() { _hasInstance = false; } + +	/** +	 * OPL type to emulate. +	 */ +	enum kOplType { +		kOpl2, +		kDualOpl2, +		kOpl3 +	}; + +	/** +	 * Initializes the OPL emulator. +	 * +	 * @param rate	output sample rate +	 * @return		true on success, false on failure +	 */ +	virtual bool init(int rate) = 0; + +	/** +	 * Reinitializes the OPL emulator +	 */ +	virtual void reset() = 0; + +	/** +	 * Writes a byte to the given I/O port. +	 * +	 * @param a		port address +	 * @param v		value, which will be written +	 */ +	virtual void write(int a, int v) = 0; + +	/** +	 * Reads a byte from the given I/O port. +	 * +	 * @param a		port address +	 * @return		value read +	 */ +	virtual byte read(int a) = 0; + +	/** +	 * Function to directly write to a specific OPL register. +	 * This writes to *both* chips for a Dual OPL2. +	 * +	 * @param r		hardware register number to write to +	 * @param v		value, which will be written +	 */ +	virtual void writeReg(int r, int v) = 0; + +	/** +	 * Read up to 'length' samples. +	 * +	 * Data will be in native endianess, 16 bit per sample, signed. +	 * For stereo OPL, buffer will be filled with interleaved +	 * left and right channel samples, starting with a left sample. +	 * Furthermore, the samples in the left and right are summed up. +	 * So if you request 4 samples from a stereo OPL, you will get +	 * a total of two left channel and two right channel samples. +	 */ +	virtual void readBuffer(int16 *buffer, int length) = 0; + +	/** +	 * Returns whether the setup OPL mode is stereo or not +	 */ +	virtual bool isStereo() const = 0; + +	static OPL *create(kOplType type = kOpl2); +}; + +} // end of namespace OPL + +// Legacy API +typedef OPL::OPL FM_OPL; + +void OPLDestroy(FM_OPL *OPL); + +void OPLResetChip(FM_OPL *OPL); +void OPLWrite(FM_OPL *OPL, int a, int v); +unsigned char OPLRead(FM_OPL *OPL, int a); +void OPLWriteReg(FM_OPL *OPL, int r, int v); +void YM3812UpdateOne(FM_OPL *OPL, int16 *buffer, int length); + +// Factory method +FM_OPL *makeAdlibOPL(int rate); + +#endif + diff --git a/sound/module.mk b/sound/module.mk index 1d7c2ffc31..dba9501a38 100644 --- a/sound/module.mk +++ b/sound/module.mk @@ -7,6 +7,7 @@ MODULE_OBJS := \  	audiostream.o \  	iff.o \  	flac.o \ +	fmopl.o \  	mididrv.o \  	midiparser.o \  	midiparser_smf.o \ @@ -29,6 +30,7 @@ MODULE_OBJS := \  	mods/rjp1.o \  	mods/soundfx.o \  	softsynth/adlib.o \ +	softsynth/opl/dosbox.o \  	softsynth/opl/mame.o \  	softsynth/ym2612.o \  	softsynth/fluidsynth.o \ diff --git a/sound/softsynth/opl/dosbox.cpp b/sound/softsynth/opl/dosbox.cpp new file mode 100644 index 0000000000..60bc417cdd --- /dev/null +++ b/sound/softsynth/opl/dosbox.cpp @@ -0,0 +1,355 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + */ + +/* + * Based on AdLib emulation code of DOSBox + * Copyright (C) 2002-2009  The DOSBox Team + * Licensed under GPLv2+ + * http://www.dosbox.com + */ + +#ifndef DISABLE_DOSBOX_OPL + +#include "dosbox.h" + +#include "common/system.h" + +#include <math.h> +#include <string.h> + +namespace OPL { +namespace DOSBox {	 + +Timer::Timer() { +	masked = false; +	overflow = false; +	enabled = false; +	counter = 0; +	delay = 0; +} + +void Timer::update(double time) { +	if (!enabled || !delay)  +		return; +	double deltaStart = time - startTime; +	// Only set the overflow flag when not masked +	if (deltaStart >= 0 && !masked) +		overflow = 1; +} + +void Timer::reset(double time) { +	overflow = false; +	if (!delay || !enabled) +		return; +	double delta = (time - startTime); +	double rem = fmod(delta, delay); +	double next = delay - rem; +	startTime = time + next;		 +} + +void Timer::stop() { +	enabled = false; +} + +void Timer::start(double time, int scale) { +	//Don't enable again +	if (enabled) +		return; +	enabled = true; +	delay = 0.001 * (256 - counter) * scale; +	startTime = time + delay; +} + +bool Chip::write(uint32 reg, uint8 val) { +	switch (reg) { +	case 0x02: +		timer[0].counter = val; +		return true; +	case 0x03: +		timer[1].counter = val; +		return true; +	case 0x04: +		double time = g_system->getMillis() / 1000.0; + +		if (val & 0x80) { +			timer[0].reset(time); +			timer[1].reset(time); +		} else { +			timer[0].update(time); +			timer[1].update(time); + +			if (val & 0x1) +				timer[0].start(time, 80); +			else +				timer[0].stop(); + +			timer[0].masked = (val & 0x40) > 0; + +			if (timer[0].masked) +				timer[0].overflow = false; + +			if (val & 0x2) +				timer[1].start(time, 320); +			else +				timer[1].stop(); + +			timer[1].masked = (val & 0x20) > 0; +	 +			if (timer[1].masked) +				timer[1].overflow = false; +		} +		return true; +	} +	return false; +} + +uint8 Chip::read() { +	double time = g_system->getMillis() / 1000.0; + +	timer[0].update(time); +	timer[1].update(time); + +	uint8 ret = 0; +	// Overflow won't be set if a channel is masked +	if (timer[0].overflow) { +		ret |= 0x40; +		ret |= 0x80; +	} +	if (timer[1].overflow) { +		ret |= 0x20; +		ret |= 0x80; +	} +	return ret; +} + +namespace OPL2 { +#include "opl.cpp" + +struct Handler : public DOSBox::Handler { +	void writeReg(uint32 reg, uint8 val) {	 +		adlib_write(reg, val); +	} + +	uint32 writeAddr(uint32 port, uint8 val) { +		return val; +	} + +	void generate(int16 *chan, uint samples) { +		adlib_getsample(chan, samples); +	} + +	void init(uint rate) { +		adlib_init(rate); +	} +}; +} // end of namespace OPL2 + +namespace OPL3 { +#define OPLTYPE_IS_OPL3 +#include "opl.cpp" + +struct Handler : public DOSBox::Handler { +	void writeReg(uint32 reg, uint8 val) {	 +		adlib_write(reg,val); +	} + +	uint32 writeAddr(uint32 port, uint8 val) { +		adlib_write_index(port, val); +		return index; +	} + +	void generate(int16 *chan, uint samples) { +		adlib_getsample(chan, samples); +	} + +	void init(uint rate) { +		adlib_init(rate); +	} +}; +} // end of namespace OPL3 + +OPL_DOSBox::OPL_DOSBox(kOplType type) : _type(type), _rate(0), _handler(0) { +} + +OPL_DOSBox::~OPL_DOSBox() { +	free(); +} + +void OPL_DOSBox::free() { +	delete _handler; +	_handler = 0; +} + +bool OPL_DOSBox::init(int rate) { +	free(); + +	memset(&_reg, 0, sizeof(_reg)); +	memset(_chip, 0, sizeof(_chip)); + +	switch (_type) { +	case kOpl2: +		_handler = new OPL2::Handler(); +		break; + +	case kDualOpl2: +	case kOpl3: +		_handler = new OPL3::Handler(); +		break; +	 +	default: +		return false; +	} + +	_handler->init(rate); +	_rate = rate; +	return true; +} + +void OPL_DOSBox::reset() { +	init(_rate);	 +} + +void OPL_DOSBox::write(int port, int val) { +	if (port&1) { +		switch (_type) { +		case kOpl2: +		case kOpl3: +			if (!_chip[0].write(_reg.normal, val)) +				_handler->writeReg(_reg.normal, val); +			break; +		case kDualOpl2: +			// Not a 0x??8 port, then write to a specific port +			if (!(port & 0x8)) { +				byte index = (port & 2) >> 1; +				dualWrite(index, _reg.dual[index], val); +			} else { +				//Write to both ports +				dualWrite(0, _reg.dual[0], val); +				dualWrite(1, _reg.dual[1], val); +			} +			break; +		} +	} else { +		// Ask the handler to write the address +		// Make sure to clip them in the right range +		switch (_type) { +		case kOpl2: +			_reg.normal = _handler->writeAddr(port, val) & 0xff; +			break; +		case kOpl3: +			_reg.normal = _handler->writeAddr(port, val) & 0x1ff; +			break; +		case kDualOpl2: +			// Not a 0x?88 port, when write to a specific side +			if (!(port & 0x8)) { +				byte index = (port & 2) >> 1; +				_reg.dual[index] = val & 0xff; +			} else { +				_reg.dual[0] = val & 0xff; +				_reg.dual[1] = val & 0xff; +			} +			break; +		} +	} +} + +byte OPL_DOSBox::read(int port) { +	switch (_type) { +	case kOpl2: +		if (!(port & 1)) +			//Make sure the low bits are 6 on opl2 +			return _chip[0].read() | 0x6; +		break; +	case kOpl3: +		if (!(port & 1)) +			return _chip[0].read(); +		break; +	case kDualOpl2: +		// Only return for the lower ports +		if (port & 1) +			return 0xff; +		// Make sure the low bits are 6 on opl2 +		return _chip[(port >> 1) & 1].read() | 0x6; +	} +	return 0; +} + +void OPL_DOSBox::writeReg(int r, int v) { +	byte tempReg = 0; +	switch (_type) { +	case kOpl2: +	case kDualOpl2: +	case kOpl3: +		// We can't use _handler->writeReg here directly, since it would miss timer changes. + +		// Backup old setup register +		tempReg = _reg.normal; + +		// We need to set the register we want to write to via port 0x388 +		write(0x388, r); +		// Do the real writing to the register +		write(0x389, v); +		// Restore the old register +		write(0x388, tempReg); +		break; +	}; +} + +void OPL_DOSBox::dualWrite(uint8 index, uint8 reg, uint8 val) { +	// Make sure you don't use opl3 features +	// Don't allow write to disable opl3		 +	if (reg == 5) +		return; + +	// Only allow 4 waveforms +	if (reg >= 0xE0) +		val &= 3; + +	// Write to the timer? +	if (_chip[index].write(reg, val))  +		return; + +	// Enabling panning +	if (reg >= 0xc0 && reg < 0xc8) { +		val &= 7; +		val |= index ? 0xA0 : 0x50; +	} + +	uint32 fullReg = reg + (index ? 0x100 : 0); +	_handler->writeReg(fullReg, val); +} + +void OPL_DOSBox::readBuffer(int16 *buffer, int length) { +	// For stereo OPL cards, we divide the sample count by 2, +	// to match stereo AudioStream behavior. +	if (_type != kOpl2) +		length >>= 1; + +	_handler->generate(buffer, length); +} + +} // end of namespace DOSBox +} // end of namespace OPL + +#endif // !DISABLE_DOSBOX_ADLIB diff --git a/sound/softsynth/opl/dosbox.h b/sound/softsynth/opl/dosbox.h new file mode 100644 index 0000000000..f09ddf85e4 --- /dev/null +++ b/sound/softsynth/opl/dosbox.h @@ -0,0 +1,122 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + */ + +/* + * Based on OPL emulation code of DOSBox + * Copyright (C) 2002-2009  The DOSBox Team + * Licensed under GPLv2+ + * http://www.dosbox.com + */ + +#ifndef SOUND_SOFTSYNTH_OPL_DOSBOX_H +#define SOUND_SOFTSYNTH_OPL_DOSBOX_H + +#ifndef DISABLE_DOSBOX_OPL + +#include "sound/fmopl.h" + +namespace OPL { +namespace DOSBox { + +class Handler; + +struct Timer { +	double startTime; +	double delay; +	bool enabled, overflow, masked; +	uint8 counter; + +	Timer(); + +	//Call update before making any further changes +	void update(double time); + +	//On a reset make sure the start is in sync with the next cycle +	void reset(double time); + +	void stop(); + +	void start(double time, int scale); +}; + +struct Chip { +	//Last selected register +	Timer timer[2]; +	//Check for it being a write to the timer +	bool write(uint32 addr, uint8 val); +	//Read the current timer state, will use current double +	uint8 read(); +}; + +class Handler { +public: +	virtual ~Handler() {} + +	// Write an address to a chip, returns the address the chip sets +	virtual uint32 writeAddr(uint32 port, uint8 val) = 0; +	// Write to a specific register in the chip +	virtual void writeReg(uint32 addr, uint8 val) = 0; +	// Generate a certain amount of samples +	virtual void generate(int16 *chan, uint samples) = 0; +	// Initialize at a specific sample rate and mode +	virtual void init(uint rate) = 0; +}; + +class OPL_DOSBox : public OPL { +private: +	kOplType _type; +	uint _rate; + +	Handler *_handler; +	Chip _chip[2]; +	union { +		uint16 normal; +		uint8 dual[2]; +	} _reg; + +	void free(); +	void dualWrite(uint8 index, uint8 reg, uint8 val); +public: +	OPL_DOSBox(kOplType type); +	~OPL_DOSBox(); + +	bool init(int rate); +	void reset(); + +	void write(int a, int v); +	byte read(int a); + +	void writeReg(int r, int v); + +	void readBuffer(int16 *buffer, int length); +	bool isStereo() const { return _type != kOpl2; } +}; + +} // end of namespace DOSBox +} // end of namespace OPL + +#endif // !DISABLE_DOSBOX_OPL + +#endif + diff --git a/sound/softsynth/opl/mame.cpp b/sound/softsynth/opl/mame.cpp index 4834e586c3..6427469a59 100644 --- a/sound/softsynth/opl/mame.cpp +++ b/sound/softsynth/opl/mame.cpp @@ -31,12 +31,48 @@  #include <stdarg.h>  #include <math.h> -#include "sound/fmopl.h" +#include "mame.h"  #if defined (_WIN32_WCE) || defined (__SYMBIAN32__) || defined(PALMOS_MODE) || defined(__GP32__) || defined(GP2X) || defined (__MAEMO__) || defined(__DS__) || defined (__MINT__)  #include "common/config-manager.h"  #endif +namespace OPL { +namespace MAME { + +OPL_MAME::~OPL_MAME() { +	MAME::OPLDestroy(_opl); +	_opl = 0; +} + +bool OPL_MAME::init(int rate) { +	if (_opl) +		MAME::OPLDestroy(_opl); + +	_opl = MAME::makeAdlibOPL(rate); +	return (_opl != 0); +} + +void OPL_MAME::reset() { +	MAME::OPLResetChip(_opl); +} + +void OPL_MAME::write(int a, int v) { +	MAME::OPLWrite(_opl, a, v); +} + +byte OPL_MAME::read(int a) { +	return MAME::OPLRead(_opl, a); +} + +void OPL_MAME::writeReg(int r, int v) { +	MAME::OPLWriteReg(_opl, r, v); +} + +void OPL_MAME::readBuffer(int16 *buffer, int length) { +	MAME::YM3812UpdateOne(_opl, buffer, length); +} +  /* -------------------- preliminary define section --------------------- */  /* attack/decay rate time rate */  #define OPL_ARRATE     141280  /* RATE 4 =  2826.24ms @ 3.6MHz */ @@ -978,7 +1014,7 @@ static void OPL_UnLockTable(void) {  /*******************************************************************************/  /* ---------- update one of chip ----------- */ -void YM3812UpdateOne(FM_OPL *OPL, int16 *buffer, int length, int interleave) { +void YM3812UpdateOne(FM_OPL *OPL, int16 *buffer, int length) {  	int i;  	int data;  	int16 *buf = buffer; @@ -1020,7 +1056,7 @@ void YM3812UpdateOne(FM_OPL *OPL, int16 *buffer, int length, int interleave) {  		/* limit check */  		data = CLIP(outd[0], OPL_MINOUT, OPL_MAXOUT);  		/* store to sound buffer */ -		buf[i << interleave] = data >> OPL_OUTSB; +		buf[i] = data >> OPL_OUTSB;  	}  	OPL->amsCnt = amsCnt; @@ -1189,3 +1225,7 @@ FM_OPL *makeAdlibOPL(int rate) {  	OPLBuildTables(env_bits, eg_ent);  	return OPLCreate(OPL_TYPE_YM3812, 3579545, rate);  } + +} // end of namespace MAME +} // end of namespace OPL + diff --git a/sound/softsynth/opl/mame.h b/sound/softsynth/opl/mame.h index 47f21c9956..a9835f3b97 100644 --- a/sound/softsynth/opl/mame.h +++ b/sound/softsynth/opl/mame.h @@ -26,12 +26,17 @@   */ -#ifndef SOUND_FMOPL_H -#define SOUND_FMOPL_H +#ifndef SOUND_SOFTSYNTH_OPL_MAME_H +#define SOUND_SOFTSYNTH_OPL_MAME_H  #include "common/scummsys.h"  #include "common/util.h" +#include "sound/fmopl.h" + +namespace OPL { +namespace MAME { +  enum {  	FMOPL_ENV_BITS_HQ = 16,  	FMOPL_ENV_BITS_MQ = 8, @@ -165,9 +170,32 @@ int OPLWrite(FM_OPL *OPL, int a, int v);  unsigned char OPLRead(FM_OPL *OPL, int a);  int OPLTimerOver(FM_OPL *OPL, int c);  void OPLWriteReg(FM_OPL *OPL, int r, int v); -void YM3812UpdateOne(FM_OPL *OPL, int16 *buffer, int length, int interleave = 0); +void YM3812UpdateOne(FM_OPL *OPL, int16 *buffer, int length);  // Factory method  FM_OPL *makeAdlibOPL(int rate); +// OPL API implementation +class OPL_MAME : public OPL { +private: +	FM_OPL *_opl; +public: +	OPL_MAME() : _opl(0) {} +	~OPL_MAME(); + +	bool init(int rate); +	void reset(); + +	void write(int a, int v); +	byte read(int a); + +	void writeReg(int r, int v); + +	void readBuffer(int16 *buffer, int length); +	bool isStereo() const { return false; } +}; + +} // end of namespace MAME +} // end of namespace OPL +  #endif diff --git a/sound/softsynth/opl/opl.cpp b/sound/softsynth/opl/opl.cpp new file mode 100644 index 0000000000..416f38a8ce --- /dev/null +++ b/sound/softsynth/opl/opl.cpp @@ -0,0 +1,1445 @@ +/* + *  Copyright (C) 2002-2009  The DOSBox Team + *  OPL2/OPL3 emulation library + * + *  This library is free software; you can redistribute it and/or + *  modify it under the terms of the GNU Lesser General Public + *  License as published by the Free Software Foundation; either + *  version 2.1 of the License, or (at your option) any later version. + *  + *  This library is distributed in the hope that it will be useful, + *  but WITHOUT ANY WARRANTY; without even the implied warranty of + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + *  Lesser General Public License for more details. + *  + *  You should have received a copy of the GNU Lesser General Public + *  License along with this library; if not, write to the Free Software + *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + */ + + +/* + * Originally based on ADLIBEMU.C, an AdLib/OPL2 emulation library by Ken Silverman + * Copyright (C) 1998-2001 Ken Silverman + * Ken Silverman's official web site: "http://www.advsys.net/ken" + */ + +#include "opl.h" + + +static fltype recipsamp;	// inverse of sampling rate +static Bit16s wavtable[WAVEPREC*3];	// wave form table + +// vibrato/tremolo tables +static Bit32s vib_table[VIBTAB_SIZE]; +static Bit32s trem_table[TREMTAB_SIZE*2]; + +static Bit32s vibval_const[BLOCKBUF_SIZE]; +static Bit32s tremval_const[BLOCKBUF_SIZE]; + +// vibrato value tables (used per-operator) +static Bit32s vibval_var1[BLOCKBUF_SIZE]; +static Bit32s vibval_var2[BLOCKBUF_SIZE]; + +// vibrato/trmolo value table pointers +static Bit32s *vibval1, *vibval2, *vibval3, *vibval4; +static Bit32s *tremval1, *tremval2, *tremval3, *tremval4; + + +// key scale level lookup table +static const fltype kslmul[4] = { +	0.0, 0.5, 0.25, 1.0		// -> 0, 3, 1.5, 6 dB/oct +}; + +// frequency multiplicator lookup table +static const fltype frqmul_tab[16] = { +	0.5,1,2,3,4,5,6,7,8,9,10,10,12,12,15,15 +}; +// calculated frequency multiplication values (depend on sampling rate) +static float frqmul[16]; + +// key scale levels +static Bit8u kslev[8][16]; + +// map a channel number to the register offset of the modulator (=register base) +static const Bit8u modulatorbase[9]	= { +	0,1,2, +	8,9,10, +	16,17,18 +}; + +// map a register base to a modulator operator number or operator number +#if defined(OPLTYPE_IS_OPL3) +static const Bit8u regbase2modop[44] = { +	0,1,2,0,1,2,0,0,3,4,5,3,4,5,0,0,6,7,8,6,7,8,					// first set +	18,19,20,18,19,20,0,0,21,22,23,21,22,23,0,0,24,25,26,24,25,26	// second set +}; +static const Bit8u regbase2op[44] = { +	0,1,2,9,10,11,0,0,3,4,5,12,13,14,0,0,6,7,8,15,16,17,			// first set +	18,19,20,27,28,29,0,0,21,22,23,30,31,32,0,0,24,25,26,33,34,35	// second set +}; +#else +static const Bit8u regbase2modop[22] = { +	0,1,2,0,1,2,0,0,3,4,5,3,4,5,0,0,6,7,8,6,7,8 +}; +static const Bit8u regbase2op[22] = { +	0,1,2,9,10,11,0,0,3,4,5,12,13,14,0,0,6,7,8,15,16,17 +}; +#endif + + +// start of the waveform +static Bit32u waveform[8] = { +	WAVEPREC, +	WAVEPREC>>1, +	WAVEPREC, +	(WAVEPREC*3)>>2, +	0, +	0, +	(WAVEPREC*5)>>2, +	WAVEPREC<<1 +}; + +// length of the waveform as mask +static Bit32u wavemask[8] = { +	WAVEPREC-1, +	WAVEPREC-1, +	(WAVEPREC>>1)-1, +	(WAVEPREC>>1)-1, +	WAVEPREC-1, +	((WAVEPREC*3)>>2)-1, +	WAVEPREC>>1, +	WAVEPREC-1 +}; + +// where the first entry resides +static Bit32u wavestart[8] = { +	0, +	WAVEPREC>>1, +	0, +	WAVEPREC>>2, +	0, +	0, +	0, +	WAVEPREC>>3 +}; + +// envelope generator function constants +static fltype attackconst[4] = {1/2.82624,1/2.25280,1/1.88416,1/1.59744}; +static fltype decrelconst[4] = {1/39.28064,1/31.41608,1/26.17344,1/22.44608}; + + +void operator_advance(op_type* op_pt, Bit32s vib) { +	op_pt->wfpos = op_pt->tcount;						// waveform position +	 +	// advance waveform time +	op_pt->tcount += op_pt->tinc; +	op_pt->tcount += (Bit32s)(op_pt->tinc)*vib/FIXEDPT; + +	op_pt->generator_pos += generator_add; +} + +void operator_advance_drums(op_type* op_pt1, Bit32s vib1, op_type* op_pt2, Bit32s vib2, op_type* op_pt3, Bit32s vib3) { +	Bit32u c1 = op_pt1->tcount/FIXEDPT; +	Bit32u c3 = op_pt3->tcount/FIXEDPT; +	Bit32u phasebit = (((c1 & 0x88) ^ ((c1<<5) & 0x80)) | ((c3 ^ (c3<<2)) & 0x20)) ? 0x02 : 0x00; + +	Bit32u noisebit = rand()&1; + +	Bit32u snare_phase_bit = (((Bitu)((op_pt1->tcount/FIXEDPT) / 0x100))&1); + +	//Hihat +	Bit32u inttm = (phasebit<<8) | (0x34<<(phasebit ^ (noisebit<<1))); +	op_pt1->wfpos = inttm*FIXEDPT;				// waveform position +	// advance waveform time +	op_pt1->tcount += op_pt1->tinc; +	op_pt1->tcount += (Bit32s)(op_pt1->tinc)*vib1/FIXEDPT; +	op_pt1->generator_pos += generator_add; + +	//Snare +	inttm = ((1+snare_phase_bit) ^ noisebit)<<8; +	op_pt2->wfpos = inttm*FIXEDPT;				// waveform position +	// advance waveform time +	op_pt2->tcount += op_pt2->tinc; +	op_pt2->tcount += (Bit32s)(op_pt2->tinc)*vib2/FIXEDPT; +	op_pt2->generator_pos += generator_add; + +	//Cymbal +	inttm = (1+phasebit)<<8; +	op_pt3->wfpos = inttm*FIXEDPT;				// waveform position +	// advance waveform time +	op_pt3->tcount += op_pt3->tinc; +	op_pt3->tcount += (Bit32s)(op_pt3->tinc)*vib3/FIXEDPT; +	op_pt3->generator_pos += generator_add; +} + + +// output level is sustained, mode changes only when operator is turned off (->release) +// or when the keep-sustained bit is turned off (->sustain_nokeep) +void operator_output(op_type* op_pt, Bit32s modulator, Bit32s trem) { +	if (op_pt->op_state != OF_TYPE_OFF) { +		op_pt->lastcval = op_pt->cval; +		Bit32u i = (Bit32u)((op_pt->wfpos+modulator)/FIXEDPT); + +		// wform: -16384 to 16383 (0x4000) +		// trem :  32768 to 65535 (0x10000) +		// step_amp: 0.0 to 1.0 +		// vol  : 1/2^14 to 1/2^29 (/0x4000; /1../0x8000) + +		op_pt->cval = (Bit32s)(op_pt->step_amp*op_pt->vol*op_pt->cur_wform[i&op_pt->cur_wmask]*trem/16.0); +	} +} + + +// no action, operator is off +void operator_off(op_type* /*op_pt*/) { +} + +// output level is sustained, mode changes only when operator is turned off (->release) +// or when the keep-sustained bit is turned off (->sustain_nokeep) +void operator_sustain(op_type* op_pt) { +	Bit32u num_steps_add = op_pt->generator_pos/FIXEDPT;	// number of (standardized) samples +	for (Bit32u ct=0; ct<num_steps_add; ct++) { +		op_pt->cur_env_step++; +	} +	op_pt->generator_pos -= num_steps_add*FIXEDPT; +} + +// operator in release mode, if output level reaches zero the operator is turned off +void operator_release(op_type* op_pt) { +	// ??? boundary? +	if (op_pt->amp > 0.00000001) { +		// release phase +		op_pt->amp *= op_pt->releasemul; +	} + +	Bit32u num_steps_add = op_pt->generator_pos/FIXEDPT;	// number of (standardized) samples +	for (Bit32u ct=0; ct<num_steps_add; ct++) { +		op_pt->cur_env_step++;						// sample counter +		if ((op_pt->cur_env_step & op_pt->env_step_r)==0) { +			if (op_pt->amp <= 0.00000001) { +				// release phase finished, turn off this operator +				op_pt->amp = 0.0; +				if (op_pt->op_state == OF_TYPE_REL) { +					op_pt->op_state = OF_TYPE_OFF; +				} +			} +			op_pt->step_amp = op_pt->amp; +		} +	} +	op_pt->generator_pos -= num_steps_add*FIXEDPT; +} + +// operator in decay mode, if sustain level is reached the output level is either +// kept (sustain level keep enabled) or the operator is switched into release mode +void operator_decay(op_type* op_pt) { +	if (op_pt->amp > op_pt->sustain_level) { +		// decay phase +		op_pt->amp *= op_pt->decaymul; +	} + +	Bit32u num_steps_add = op_pt->generator_pos/FIXEDPT;	// number of (standardized) samples +	for (Bit32u ct=0; ct<num_steps_add; ct++) { +		op_pt->cur_env_step++; +		if ((op_pt->cur_env_step & op_pt->env_step_d)==0) { +			if (op_pt->amp <= op_pt->sustain_level) { +				// decay phase finished, sustain level reached +				if (op_pt->sus_keep) { +					// keep sustain level (until turned off) +					op_pt->op_state = OF_TYPE_SUS; +					op_pt->amp = op_pt->sustain_level; +				} else { +					// next: release phase +					op_pt->op_state = OF_TYPE_SUS_NOKEEP; +				} +			} +			op_pt->step_amp = op_pt->amp; +		} +	} +	op_pt->generator_pos -= num_steps_add*FIXEDPT; +} + +// operator in attack mode, if full output level is reached, +// the operator is switched into decay mode +void operator_attack(op_type* op_pt) { +	op_pt->amp = ((op_pt->a3*op_pt->amp + op_pt->a2)*op_pt->amp + op_pt->a1)*op_pt->amp + op_pt->a0; + +	Bit32u num_steps_add = op_pt->generator_pos/FIXEDPT;		// number of (standardized) samples +	for (Bit32u ct=0; ct<num_steps_add; ct++) { +		op_pt->cur_env_step++;	// next sample +		if ((op_pt->cur_env_step & op_pt->env_step_a)==0) {		// check if next step already reached +			if (op_pt->amp > 1.0) { +				// attack phase finished, next: decay +				op_pt->op_state = OF_TYPE_DEC; +				op_pt->amp = 1.0; +				op_pt->step_amp = 1.0; +			} +			op_pt->step_skip_pos <<= 1; +			if (op_pt->step_skip_pos==0) op_pt->step_skip_pos = 1; +			if (op_pt->step_skip_pos & op_pt->env_step_skip_a) {	// check if required to skip next step +				op_pt->step_amp = op_pt->amp; +			} +		} +	} +	op_pt->generator_pos -= num_steps_add*FIXEDPT; +} + + +typedef void (*optype_fptr)(op_type*); + +optype_fptr opfuncs[6] = { +	operator_attack, +	operator_decay, +	operator_release, +	operator_sustain,	// sustain phase (keeping level) +	operator_release,	// sustain_nokeep phase (release-style) +	operator_off +}; + +void change_attackrate(Bitu regbase, op_type* op_pt) { +	Bits attackrate = adlibreg[ARC_ATTR_DECR+regbase]>>4; +	if (attackrate) { +		fltype f = (fltype)(pow(FL2,(fltype)attackrate+(op_pt->toff>>2)-1)*attackconst[op_pt->toff&3]*recipsamp); +		// attack rate coefficients +		op_pt->a0 = (fltype)(0.0377*f); +		op_pt->a1 = (fltype)(10.73*f+1); +		op_pt->a2 = (fltype)(-17.57*f); +		op_pt->a3 = (fltype)(7.42*f); + +		Bits step_skip = attackrate*4 + op_pt->toff; +		Bits steps = step_skip >> 2; +		op_pt->env_step_a = (1<<(steps<=12?12-steps:0))-1; + +		Bits step_num = (step_skip<=48)?(4-(step_skip&3)):0; +		static Bit8u step_skip_mask[5] = {0xff, 0xfe, 0xee, 0xba, 0xaa};  +		op_pt->env_step_skip_a = step_skip_mask[step_num]; + +#if defined(OPLTYPE_IS_OPL3) +		if (step_skip>=60) { +#else +		if (step_skip>=62) { +#endif +			op_pt->a0 = (fltype)(2.0);	// something that triggers an immediate transition to amp:=1.0 +			op_pt->a1 = (fltype)(0.0); +			op_pt->a2 = (fltype)(0.0); +			op_pt->a3 = (fltype)(0.0); +		} +	} else { +		// attack disabled +		op_pt->a0 = 0.0; +		op_pt->a1 = 1.0; +		op_pt->a2 = 0.0; +		op_pt->a3 = 0.0; +		op_pt->env_step_a = 0; +		op_pt->env_step_skip_a = 0; +	} +} + +void change_decayrate(Bitu regbase, op_type* op_pt) { +	Bits decayrate = adlibreg[ARC_ATTR_DECR+regbase]&15; +	// decaymul should be 1.0 when decayrate==0 +	if (decayrate) { +		fltype f = (fltype)(-7.4493*decrelconst[op_pt->toff&3]*recipsamp); +		op_pt->decaymul = (fltype)(pow(FL2,f*pow(FL2,(fltype)(decayrate+(op_pt->toff>>2))))); +		Bits steps = (decayrate*4 + op_pt->toff) >> 2; +		op_pt->env_step_d = (1<<(steps<=12?12-steps:0))-1; +	} else { +		op_pt->decaymul = 1.0; +		op_pt->env_step_d = 0; +	} +} + +void change_releaserate(Bitu regbase, op_type* op_pt) { +	Bits releaserate = adlibreg[ARC_SUSL_RELR+regbase]&15; +	// releasemul should be 1.0 when releaserate==0 +	if (releaserate) { +		fltype f = (fltype)(-7.4493*decrelconst[op_pt->toff&3]*recipsamp); +		op_pt->releasemul = (fltype)(pow(FL2,f*pow(FL2,(fltype)(releaserate+(op_pt->toff>>2))))); +		Bits steps = (releaserate*4 + op_pt->toff) >> 2; +		op_pt->env_step_r = (1<<(steps<=12?12-steps:0))-1; +	} else { +		op_pt->releasemul = 1.0; +		op_pt->env_step_r = 0; +	} +} + +void change_sustainlevel(Bitu regbase, op_type* op_pt) { +	Bits sustainlevel = adlibreg[ARC_SUSL_RELR+regbase]>>4; +	// sustainlevel should be 0.0 when sustainlevel==15 (max) +	if (sustainlevel<15) { +		op_pt->sustain_level = (fltype)(pow(FL2,(fltype)sustainlevel * (-FL05))); +	} else { +		op_pt->sustain_level = 0.0; +	} +} + +void change_waveform(Bitu regbase, op_type* op_pt) { +#if defined(OPLTYPE_IS_OPL3) +	if (regbase>=ARC_SECONDSET) regbase -= (ARC_SECONDSET-22);	// second set starts at 22 +#endif +	// waveform selection +	op_pt->cur_wmask = wavemask[wave_sel[regbase]]; +	op_pt->cur_wform = &wavtable[waveform[wave_sel[regbase]]]; +	// (might need to be adapted to waveform type here...) +} + +void change_keepsustain(Bitu regbase, op_type* op_pt) { +	op_pt->sus_keep = (adlibreg[ARC_TVS_KSR_MUL+regbase]&0x20)>0; +	if (op_pt->op_state==OF_TYPE_SUS) { +		if (!op_pt->sus_keep) op_pt->op_state = OF_TYPE_SUS_NOKEEP; +	} else if (op_pt->op_state==OF_TYPE_SUS_NOKEEP) { +		if (op_pt->sus_keep) op_pt->op_state = OF_TYPE_SUS; +	} +} + +// enable/disable vibrato/tremolo LFO effects +void change_vibrato(Bitu regbase, op_type* op_pt) { +	op_pt->vibrato = (adlibreg[ARC_TVS_KSR_MUL+regbase]&0x40)!=0; +	op_pt->tremolo = (adlibreg[ARC_TVS_KSR_MUL+regbase]&0x80)!=0; +} + +// change amount of self-feedback +void change_feedback(Bitu chanbase, op_type* op_pt) { +	Bits feedback = adlibreg[ARC_FEEDBACK+chanbase]&14; +	if (feedback) op_pt->mfbi = (Bit32s)(pow(FL2,(fltype)((feedback>>1)+8))); +	else op_pt->mfbi = 0; +} + +void change_frequency(Bitu chanbase, Bitu regbase, op_type* op_pt) { +	// frequency +	Bit32u frn = ((((Bit32u)adlibreg[ARC_KON_BNUM+chanbase])&3)<<8) + (Bit32u)adlibreg[ARC_FREQ_NUM+chanbase]; +	// block number/octave +	Bit32u oct = ((((Bit32u)adlibreg[ARC_KON_BNUM+chanbase])>>2)&7); +	op_pt->freq_high = (Bit32s)((frn>>7)&7); + +	// keysplit +	Bit32u note_sel = (adlibreg[8]>>6)&1; +	op_pt->toff = ((frn>>9)&(note_sel^1)) | ((frn>>8)¬e_sel); +	op_pt->toff += (oct<<1); + +	// envelope scaling (KSR) +	if (!(adlibreg[ARC_TVS_KSR_MUL+regbase]&0x10)) op_pt->toff >>= 2; + +	// 20+a0+b0: +	op_pt->tinc = (Bit32u)((((fltype)(frn<<oct))*frqmul[adlibreg[ARC_TVS_KSR_MUL+regbase]&15])); +	// 40+a0+b0: +	fltype vol_in = (fltype)((fltype)(adlibreg[ARC_KSL_OUTLEV+regbase]&63) + +							kslmul[adlibreg[ARC_KSL_OUTLEV+regbase]>>6]*kslev[oct][frn>>6]); +	op_pt->vol = (fltype)(pow(FL2,(fltype)(vol_in * -0.125 - 14))); + +	// operator frequency changed, care about features that depend on it +	change_attackrate(regbase,op_pt); +	change_decayrate(regbase,op_pt); +	change_releaserate(regbase,op_pt); +} + +void enable_operator(Bitu regbase, op_type* op_pt, Bit32u act_type) { +	// check if this is really an off-on transition +	if (op_pt->act_state == OP_ACT_OFF) { +		Bits wselbase = regbase; +		if (wselbase>=ARC_SECONDSET) wselbase -= (ARC_SECONDSET-22);	// second set starts at 22 + +		op_pt->tcount = wavestart[wave_sel[wselbase]]*FIXEDPT; + +		// start with attack mode +		op_pt->op_state = OF_TYPE_ATT; +		op_pt->act_state |= act_type; +	} +} + +void disable_operator(op_type* op_pt, Bit32u act_type) { +	// check if this is really an on-off transition +	if (op_pt->act_state != OP_ACT_OFF) { +		op_pt->act_state &= (~act_type); +		if (op_pt->act_state == OP_ACT_OFF) { +			if (op_pt->op_state != OF_TYPE_OFF) op_pt->op_state = OF_TYPE_REL; +		} +	} +} + +void adlib_init(Bit32u samplerate) { +	Bits i, j, oct; + +	int_samplerate = samplerate; + +	generator_add = (Bit32u)(INTFREQU*FIXEDPT/int_samplerate); + + +	memset((void *)adlibreg,0,sizeof(adlibreg)); +	memset((void *)op,0,sizeof(op_type)*MAXOPERATORS); +	memset((void *)wave_sel,0,sizeof(wave_sel)); + +	for (i=0;i<MAXOPERATORS;i++) { +		op[i].op_state = OF_TYPE_OFF; +		op[i].act_state = OP_ACT_OFF; +		op[i].amp = 0.0; +		op[i].step_amp = 0.0; +		op[i].vol = 0.0; +		op[i].tcount = 0; +		op[i].tinc = 0; +		op[i].toff = 0; +		op[i].cur_wmask = wavemask[0]; +		op[i].cur_wform = &wavtable[waveform[0]]; +		op[i].freq_high = 0; + +		op[i].generator_pos = 0; +		op[i].cur_env_step = 0; +		op[i].env_step_a = 0; +		op[i].env_step_d = 0; +		op[i].env_step_r = 0; +		op[i].step_skip_pos = 0; +		op[i].env_step_skip_a = 0; + +#if defined(OPLTYPE_IS_OPL3) +		op[i].is_4op = false; +		op[i].is_4op_attached = false; +		op[i].left_pan = 1; +		op[i].right_pan = 1; +#endif +	} + +	recipsamp = 1.0 / (fltype)int_samplerate; +	for (i=15;i>=0;i--) { +		frqmul[i] = (fltype)(frqmul_tab[i]*INTFREQU/(fltype)WAVEPREC*(fltype)FIXEDPT*recipsamp); +	} + +	status = 0; +	index = 0; + + +	// create vibrato table +	vib_table[0] = 8; +	vib_table[1] = 4; +	vib_table[2] = 0; +	vib_table[3] = -4; +	for (i=4; i<VIBTAB_SIZE; i++) vib_table[i] = vib_table[i-4]*-1; + +	// vibrato at ~6.1 ?? (opl3 docs say 6.1, opl4 docs say 6.0, y8950 docs say 6.4) +	vibtab_add = static_cast<Bit32u>(VIBTAB_SIZE*FIXEDPT_LFO/8192*INTFREQU/int_samplerate); +	vibtab_pos = 0; + +	for (i=0; i<BLOCKBUF_SIZE; i++) vibval_const[i] = 0; + + +	// create tremolo table +	Bit32s trem_table_int[TREMTAB_SIZE]; +	for (i=0; i<14; i++)	trem_table_int[i] = i-13;		// upwards (13 to 26 -> -0.5/6 to 0) +	for (i=14; i<41; i++)	trem_table_int[i] = -i+14;		// downwards (26 to 0 -> 0 to -1/6) +	for (i=41; i<53; i++)	trem_table_int[i] = i-40-26;	// upwards (1 to 12 -> -1/6 to -0.5/6) + +	for (i=0; i<TREMTAB_SIZE; i++) { +		// 0.0 .. -26/26*4.8/6 == [0.0 .. -0.8], 4/53 steps == [1 .. 0.57] +		fltype trem_val1=(fltype)(((fltype)trem_table_int[i])*4.8/26.0/6.0);				// 4.8db +		fltype trem_val2=(fltype)((fltype)((Bit32s)(trem_table_int[i]/4))*1.2/6.0/6.0);		// 1.2db (larger stepping) + +		trem_table[i] = (Bit32s)(pow(FL2,trem_val1)*FIXEDPT); +		trem_table[TREMTAB_SIZE+i] = (Bit32s)(pow(FL2,trem_val2)*FIXEDPT); +	} + +	// tremolo at 3.7hz +	tremtab_add = (Bit32u)((fltype)TREMTAB_SIZE * TREM_FREQ * FIXEDPT_LFO / (fltype)int_samplerate); +	tremtab_pos = 0; + +	for (i=0; i<BLOCKBUF_SIZE; i++) tremval_const[i] = FIXEDPT; + + +	static Bitu initfirstime = 0; +	if (!initfirstime) { +		initfirstime = 1; + +		// create waveform tables +		for (i=0;i<(WAVEPREC>>1);i++) { +			wavtable[(i<<1)  +WAVEPREC]	= (Bit16s)(16384*sin((fltype)((i<<1)  )*PI*2/WAVEPREC)); +			wavtable[(i<<1)+1+WAVEPREC]	= (Bit16s)(16384*sin((fltype)((i<<1)+1)*PI*2/WAVEPREC)); +			wavtable[i]					= wavtable[(i<<1)  +WAVEPREC]; +			// table to be verified, alternative: (zero-less) +/*			wavtable[(i<<1)  +WAVEPREC]	= (Bit16s)(16384*sin((fltype)(((i*2+1)<<1)-1)*PI/WAVEPREC)); +			wavtable[(i<<1)+1+WAVEPREC]	= (Bit16s)(16384*sin((fltype)(((i*2+1)<<1)  )*PI/WAVEPREC)); +			wavtable[i]					= wavtable[(i<<1)-1+WAVEPREC]; */ +		} +		for (i=0;i<(WAVEPREC>>3);i++) { +			wavtable[i+(WAVEPREC<<1)]		= wavtable[i+(WAVEPREC>>3)]-16384; +			wavtable[i+((WAVEPREC*17)>>3)]	= wavtable[i+(WAVEPREC>>2)]+16384; +		} + +		// key scale level table verified ([table in book]*8/3) +		kslev[7][0] = 0;	kslev[7][1] = 24;	kslev[7][2] = 32;	kslev[7][3] = 37; +		kslev[7][4] = 40;	kslev[7][5] = 43;	kslev[7][6] = 45;	kslev[7][7] = 47; +		kslev[7][8] = 48; +		for (i=9;i<16;i++) kslev[7][i] = (Bit8u)(i+41); +		for (j=6;j>=0;j--) { +			for (i=0;i<16;i++) { +				oct = (Bits)kslev[j+1][i]-8; +				if (oct < 0) oct = 0; +				kslev[j][i] = (Bit8u)oct; +			} +		} +	} + +} + + + +void adlib_write(Bitu idx, Bit8u val) { +	Bit32u second_set = idx&0x100; +	adlibreg[idx] = val; + +	switch (idx&0xf0) { +	case ARC_CONTROL: +		// here we check for the second set registers, too: +		switch (idx) { +		case 0x02:	// timer1 counter +		case 0x03:	// timer2 counter +			break; +		case 0x04: +			// IRQ reset, timer mask/start +			if (val&0x80) { +				// clear IRQ bits in status register +				status &= ~0x60; +			} else { +				status = 0; +			} +			break; +#if defined(OPLTYPE_IS_OPL3) +		case 0x04|ARC_SECONDSET: +			// 4op enable/disable switches for each possible channel +			op[0].is_4op = (val&1)>0; +			op[3].is_4op_attached = op[0].is_4op; +			op[1].is_4op = (val&2)>0; +			op[4].is_4op_attached = op[1].is_4op; +			op[2].is_4op = (val&4)>0; +			op[5].is_4op_attached = op[2].is_4op; +			op[18].is_4op = (val&8)>0; +			op[21].is_4op_attached = op[18].is_4op; +			op[19].is_4op = (val&16)>0; +			op[22].is_4op_attached = op[19].is_4op; +			op[20].is_4op = (val&32)>0; +			op[23].is_4op_attached = op[20].is_4op; +			break; +		case 0x05|ARC_SECONDSET: +			break; +#endif +		case 0x08: +			// CSW, note select +			break; +		default: +			break; +		} +		break; +	case ARC_TVS_KSR_MUL: +	case ARC_TVS_KSR_MUL+0x10: { +		// tremolo/vibrato/sustain keeping enabled; key scale rate; frequency multiplication +		int num = idx&7; +		Bitu base = (idx-ARC_TVS_KSR_MUL)&0xff; +		if ((num<6) && (base<22)) { +			Bitu modop = regbase2modop[second_set?(base+22):base]; +			Bitu regbase = base+second_set; +			Bitu chanbase = second_set?(modop-18+ARC_SECONDSET):modop; + +			// change tremolo/vibrato and sustain keeping of this operator +			op_type* op_ptr = &op[modop+((num<3) ? 0 : 9)]; +			change_keepsustain(regbase,op_ptr); +			change_vibrato(regbase,op_ptr); + +			// change frequency calculations of this operator as +			// key scale rate and frequency multiplicator can be changed +#if defined(OPLTYPE_IS_OPL3) +			if ((adlibreg[0x105]&1) && (op[modop].is_4op_attached)) { +				// operator uses frequency of channel +				change_frequency(chanbase-3,regbase,op_ptr); +			} else { +				change_frequency(chanbase,regbase,op_ptr); +			} +#else +			change_frequency(chanbase,base,op_ptr); +#endif +		} +		} +		break; +	case ARC_KSL_OUTLEV: +	case ARC_KSL_OUTLEV+0x10: { +		// key scale level; output rate +		int num = idx&7; +		Bitu base = (idx-ARC_KSL_OUTLEV)&0xff; +		if ((num<6) && (base<22)) { +			Bitu modop = regbase2modop[second_set?(base+22):base]; +			Bitu chanbase = second_set?(modop-18+ARC_SECONDSET):modop; + +			// change frequency calculations of this operator as +			// key scale level and output rate can be changed +			op_type* op_ptr = &op[modop+((num<3) ? 0 : 9)]; +#if defined(OPLTYPE_IS_OPL3) +			Bitu regbase = base+second_set; +			if ((adlibreg[0x105]&1) && (op[modop].is_4op_attached)) { +				// operator uses frequency of channel +				change_frequency(chanbase-3,regbase,op_ptr); +			} else { +				change_frequency(chanbase,regbase,op_ptr); +			} +#else +			change_frequency(chanbase,base,op_ptr); +#endif +		} +		} +		break; +	case ARC_ATTR_DECR: +	case ARC_ATTR_DECR+0x10: { +		// attack/decay rates +		int num = idx&7; +		Bitu base = (idx-ARC_ATTR_DECR)&0xff; +		if ((num<6) && (base<22)) { +			Bitu regbase = base+second_set; + +			// change attack rate and decay rate of this operator +			op_type* op_ptr = &op[regbase2op[second_set?(base+22):base]]; +			change_attackrate(regbase,op_ptr); +			change_decayrate(regbase,op_ptr); +		} +		} +		break; +	case ARC_SUSL_RELR: +	case ARC_SUSL_RELR+0x10: { +		// sustain level; release rate +		int num = idx&7; +		Bitu base = (idx-ARC_SUSL_RELR)&0xff; +		if ((num<6) && (base<22)) { +			Bitu regbase = base+second_set; + +			// change sustain level and release rate of this operator +			op_type* op_ptr = &op[regbase2op[second_set?(base+22):base]]; +			change_releaserate(regbase,op_ptr); +			change_sustainlevel(regbase,op_ptr); +		} +		} +		break; +	case ARC_FREQ_NUM: { +		// 0xa0-0xa8 low8 frequency +		Bitu base = (idx-ARC_FREQ_NUM)&0xff; +		if (base<9) { +			Bits opbase = second_set?(base+18):base; +#if defined(OPLTYPE_IS_OPL3) +			if ((adlibreg[0x105]&1) && op[opbase].is_4op_attached) break; +#endif +			// regbase of modulator: +			Bits modbase = modulatorbase[base]+second_set; + +			Bitu chanbase = base+second_set; + +			change_frequency(chanbase,modbase,&op[opbase]); +			change_frequency(chanbase,modbase+3,&op[opbase+9]); +#if defined(OPLTYPE_IS_OPL3) +			// for 4op channels all four operators are modified to the frequency of the channel +			if ((adlibreg[0x105]&1) && op[second_set?(base+18):base].is_4op) { +				change_frequency(chanbase,modbase+8,&op[opbase+3]); +				change_frequency(chanbase,modbase+3+8,&op[opbase+3+9]); +			} +#endif +		} +		} +		break; +	case ARC_KON_BNUM: { +		if (idx == ARC_PERC_MODE) { +#if defined(OPLTYPE_IS_OPL3) +			if (second_set) return; +#endif + +			if ((val&0x30) == 0x30) {		// BassDrum active +				enable_operator(16,&op[6],OP_ACT_PERC); +				change_frequency(6,16,&op[6]); +				enable_operator(16+3,&op[6+9],OP_ACT_PERC); +				change_frequency(6,16+3,&op[6+9]); +			} else { +				disable_operator(&op[6],OP_ACT_PERC); +				disable_operator(&op[6+9],OP_ACT_PERC); +			} +			if ((val&0x28) == 0x28) {		// Snare active +				enable_operator(17+3,&op[16],OP_ACT_PERC); +				change_frequency(7,17+3,&op[16]); +			} else { +				disable_operator(&op[16],OP_ACT_PERC); +			} +			if ((val&0x24) == 0x24) {		// TomTom active +				enable_operator(18,&op[8],OP_ACT_PERC); +				change_frequency(8,18,&op[8]); +			} else { +				disable_operator(&op[8],OP_ACT_PERC); +			} +			if ((val&0x22) == 0x22) {		// Cymbal active +				enable_operator(18+3,&op[8+9],OP_ACT_PERC); +				change_frequency(8,18+3,&op[8+9]); +			} else { +				disable_operator(&op[8+9],OP_ACT_PERC); +			} +			if ((val&0x21) == 0x21) {		// Hihat active +				enable_operator(17,&op[7],OP_ACT_PERC); +				change_frequency(7,17,&op[7]); +			} else { +				disable_operator(&op[7],OP_ACT_PERC); +			} + +			break; +		} +		// regular 0xb0-0xb8 +		Bitu base = (idx-ARC_KON_BNUM)&0xff; +		if (base<9) { +			Bits opbase = second_set?(base+18):base; +#if defined(OPLTYPE_IS_OPL3) +			if ((adlibreg[0x105]&1) && op[opbase].is_4op_attached) break; +#endif +			// regbase of modulator: +			Bits modbase = modulatorbase[base]+second_set; + +			if (val&32) { +				// operator switched on +				enable_operator(modbase,&op[opbase],OP_ACT_NORMAL);		// modulator (if 2op) +				enable_operator(modbase+3,&op[opbase+9],OP_ACT_NORMAL);	// carrier (if 2op) +#if defined(OPLTYPE_IS_OPL3) +				// for 4op channels all four operators are switched on +				if ((adlibreg[0x105]&1) && op[opbase].is_4op) { +					// turn on chan+3 operators as well +					enable_operator(modbase+8,&op[opbase+3],OP_ACT_NORMAL); +					enable_operator(modbase+3+8,&op[opbase+3+9],OP_ACT_NORMAL); +				} +#endif +			} else { +				// operator switched off +				disable_operator(&op[opbase],OP_ACT_NORMAL); +				disable_operator(&op[opbase+9],OP_ACT_NORMAL); +#if defined(OPLTYPE_IS_OPL3) +				// for 4op channels all four operators are switched off +				if ((adlibreg[0x105]&1) && op[opbase].is_4op) { +					// turn off chan+3 operators as well +					disable_operator(&op[opbase+3],OP_ACT_NORMAL); +					disable_operator(&op[opbase+3+9],OP_ACT_NORMAL); +				} +#endif +			} + +			Bitu chanbase = base+second_set; + +			// change frequency calculations of modulator and carrier (2op) as +			// the frequency of the channel has changed +			change_frequency(chanbase,modbase,&op[opbase]); +			change_frequency(chanbase,modbase+3,&op[opbase+9]); +#if defined(OPLTYPE_IS_OPL3) +			// for 4op channels all four operators are modified to the frequency of the channel +			if ((adlibreg[0x105]&1) && op[second_set?(base+18):base].is_4op) { +				// change frequency calculations of chan+3 operators as well +				change_frequency(chanbase,modbase+8,&op[opbase+3]); +				change_frequency(chanbase,modbase+3+8,&op[opbase+3+9]); +			} +#endif +		} +		} +		break; +	case ARC_FEEDBACK: { +		// 0xc0-0xc8 feedback/modulation type (AM/FM) +		Bitu base = (idx-ARC_FEEDBACK)&0xff; +		if (base<9) { +			Bits opbase = second_set?(base+18):base; +			Bitu chanbase = base+second_set; +			change_feedback(chanbase,&op[opbase]); +#if defined(OPLTYPE_IS_OPL3) +			// OPL3 panning +			op[opbase].left_pan = ((val&0x10)>>4); +			op[opbase].right_pan = ((val&0x20)>>5); +#endif +		} +		} +		break; +	case ARC_WAVE_SEL: +	case ARC_WAVE_SEL+0x10: { +		int num = idx&7; +		Bitu base = (idx-ARC_WAVE_SEL)&0xff; +		if ((num<6) && (base<22)) { +#if defined(OPLTYPE_IS_OPL3) +			Bits wselbase = second_set?(base+22):base;	// for easier mapping onto wave_sel[] +			// change waveform +			if (adlibreg[0x105]&1) wave_sel[wselbase] = val&7;	// opl3 mode enabled, all waveforms accessible +			else wave_sel[wselbase] = val&3; +			op_type* op_ptr = &op[regbase2modop[wselbase]+((num<3) ? 0 : 9)]; +			change_waveform(wselbase,op_ptr); +#else +			if (adlibreg[0x01]&0x20) { +				// wave selection enabled, change waveform +				wave_sel[base] = val&3; +				op_type* op_ptr = &op[regbase2modop[base]+((num<3) ? 0 : 9)]; +				change_waveform(base,op_ptr); +			} +#endif +		} +		} +		break; +	default: +		break; +	} +} + + +Bitu adlib_reg_read(Bitu port) { +#if defined(OPLTYPE_IS_OPL3) +	// opl3-detection routines require ret&6 to be zero +	if ((port&1)==0) { +		return status; +	} +	return 0x00; +#else +	// opl2-detection routines require ret&6 to be 6 +	if ((port&1)==0) { +		return status|6; +	} +	return 0xff; +#endif +} + +void adlib_write_index(Bitu port, Bit8u val) { +	index = val; +#if defined(OPLTYPE_IS_OPL3) +	if ((port&3)!=0) { +		// possibly second set +		if (((adlibreg[0x105]&1)!=0) || (index==5)) index |= ARC_SECONDSET; +	} +#endif +} + +static inline void clipit16(Bit32s ival, Bit16s* outval) { +	if (ival<32768) { +		if (ival>-32769) { +			*outval=(Bit16s)ival; +		} else { +			*outval = -32768; +		} +	} else { +		*outval = 32767; +	} +} + + + +// be careful with this +// uses cptr and chanval, outputs into outbufl(/outbufr) +// for opl3 check if opl3-mode is enabled (which uses stereo panning) +#undef CHANVAL_OUT +#if defined(OPLTYPE_IS_OPL3) +#define CHANVAL_OUT									\ +	if (adlibreg[0x105]&1) {						\ +		outbufl[i] += chanval*cptr[0].left_pan;		\ +		outbufr[i] += chanval*cptr[0].right_pan;	\ +	} else {										\ +		outbufl[i] += chanval;						\ +	} +#else +#define CHANVAL_OUT									\ +	outbufl[i] += chanval; +#endif + +void adlib_getsample(Bit16s* sndptr, Bits numsamples) { +	Bits i, endsamples; +	op_type* cptr; + +	Bit32s outbufl[BLOCKBUF_SIZE]; +#if defined(OPLTYPE_IS_OPL3) +	// second output buffer (right channel for opl3 stereo) +	Bit32s outbufr[BLOCKBUF_SIZE]; +#endif + +	// vibrato/tremolo lookup tables (global, to possibly be used by all operators) +	Bit32s vib_lut[BLOCKBUF_SIZE]; +	Bit32s trem_lut[BLOCKBUF_SIZE]; + +	Bits samples_to_process = numsamples; + +	for (Bits cursmp=0; cursmp<samples_to_process; cursmp+=endsamples) { +		endsamples = samples_to_process-cursmp; +		if (endsamples>BLOCKBUF_SIZE) endsamples = BLOCKBUF_SIZE; + +		memset((void*)&outbufl,0,endsamples*sizeof(Bit32s)); +#if defined(OPLTYPE_IS_OPL3) +		// clear second output buffer (opl3 stereo) +		if (adlibreg[0x105]&1) memset((void*)&outbufr,0,endsamples*sizeof(Bit32s)); +#endif + +		// calculate vibrato/tremolo lookup tables +		Bit32s vib_tshift = ((adlibreg[ARC_PERC_MODE]&0x40)==0) ? 1 : 0;	// 14cents/7cents switching +		for (i=0;i<endsamples;i++) { +			// cycle through vibrato table +			vibtab_pos += vibtab_add; +			if (vibtab_pos/FIXEDPT_LFO>=VIBTAB_SIZE) vibtab_pos-=VIBTAB_SIZE*FIXEDPT_LFO; +			vib_lut[i] = vib_table[vibtab_pos/FIXEDPT_LFO]>>vib_tshift;		// 14cents (14/100 of a semitone) or 7cents + +			// cycle through tremolo table +			tremtab_pos += tremtab_add; +			if (tremtab_pos/FIXEDPT_LFO>=TREMTAB_SIZE) tremtab_pos-=TREMTAB_SIZE*FIXEDPT_LFO; +			if (adlibreg[ARC_PERC_MODE]&0x80) trem_lut[i] = trem_table[tremtab_pos/FIXEDPT_LFO]; +			else trem_lut[i] = trem_table[TREMTAB_SIZE+tremtab_pos/FIXEDPT_LFO]; +		} + +		if (adlibreg[ARC_PERC_MODE]&0x20) { +			//BassDrum +			cptr = &op[6]; +			if (adlibreg[ARC_FEEDBACK+6]&1) { +				// additive synthesis +				if (cptr[9].op_state != OF_TYPE_OFF) { +					if (cptr[9].vibrato) { +						vibval1 = vibval_var1; +						for (i=0;i<endsamples;i++) +							vibval1[i] = (Bit32s)((vib_lut[i]*cptr[9].freq_high/8)*FIXEDPT*VIBFAC); +					} else vibval1 = vibval_const; +					if (cptr[9].tremolo) tremval1 = trem_lut;	// tremolo enabled, use table +					else tremval1 = tremval_const; + +					// calculate channel output +					for (i=0;i<endsamples;i++) { +						operator_advance(&cptr[9],vibval1[i]); +						opfuncs[cptr[9].op_state](&cptr[9]); +						operator_output(&cptr[9],0,tremval1[i]); +						 +						Bit32s chanval = cptr[9].cval*2; +						CHANVAL_OUT +					} +				} +			} else { +				// frequency modulation +				if ((cptr[9].op_state != OF_TYPE_OFF) || (cptr[0].op_state != OF_TYPE_OFF)) { +					if ((cptr[0].vibrato) && (cptr[0].op_state != OF_TYPE_OFF)) { +						vibval1 = vibval_var1; +						for (i=0;i<endsamples;i++) +							vibval1[i] = (Bit32s)((vib_lut[i]*cptr[0].freq_high/8)*FIXEDPT*VIBFAC); +					} else vibval1 = vibval_const; +					if ((cptr[9].vibrato) && (cptr[9].op_state != OF_TYPE_OFF)) { +						vibval2 = vibval_var2; +						for (i=0;i<endsamples;i++) +							vibval2[i] = (Bit32s)((vib_lut[i]*cptr[9].freq_high/8)*FIXEDPT*VIBFAC); +					} else vibval2 = vibval_const; +					if (cptr[0].tremolo) tremval1 = trem_lut;	// tremolo enabled, use table +					else tremval1 = tremval_const; +					if (cptr[9].tremolo) tremval2 = trem_lut;	// tremolo enabled, use table +					else tremval2 = tremval_const; + +					// calculate channel output +					for (i=0;i<endsamples;i++) { +						operator_advance(&cptr[0],vibval1[i]); +						opfuncs[cptr[0].op_state](&cptr[0]); +						operator_output(&cptr[0],(cptr[0].lastcval+cptr[0].cval)*cptr[0].mfbi/2,tremval1[i]); + +						operator_advance(&cptr[9],vibval2[i]); +						opfuncs[cptr[9].op_state](&cptr[9]); +						operator_output(&cptr[9],cptr[0].cval*FIXEDPT,tremval2[i]); +						 +						Bit32s chanval = cptr[9].cval*2; +						CHANVAL_OUT +					} +				} +			} + +			//TomTom (j=8) +			if (op[8].op_state != OF_TYPE_OFF) { +				cptr = &op[8]; +				if (cptr[0].vibrato) { +					vibval3 = vibval_var1; +					for (i=0;i<endsamples;i++) +						vibval3[i] = (Bit32s)((vib_lut[i]*cptr[0].freq_high/8)*FIXEDPT*VIBFAC); +				} else vibval3 = vibval_const; + +				if (cptr[0].tremolo) tremval3 = trem_lut;	// tremolo enabled, use table +				else tremval3 = tremval_const; + +				// calculate channel output +				for (i=0;i<endsamples;i++) { +					operator_advance(&cptr[0],vibval3[i]); +					opfuncs[cptr[0].op_state](&cptr[0]);		//TomTom +					operator_output(&cptr[0],0,tremval3[i]); +					Bit32s chanval = cptr[0].cval*2; +					CHANVAL_OUT +				} +			} + +			//Snare/Hihat (j=7), Cymbal (j=8) +			if ((op[7].op_state != OF_TYPE_OFF) || (op[16].op_state != OF_TYPE_OFF) || +				(op[17].op_state != OF_TYPE_OFF)) { +				cptr = &op[7]; +				if ((cptr[0].vibrato) && (cptr[0].op_state != OF_TYPE_OFF)) { +					vibval1 = vibval_var1; +					for (i=0;i<endsamples;i++) +						vibval1[i] = (Bit32s)((vib_lut[i]*cptr[0].freq_high/8)*FIXEDPT*VIBFAC); +				} else vibval1 = vibval_const; +				if ((cptr[9].vibrato) && (cptr[9].op_state == OF_TYPE_OFF)) { +					vibval2 = vibval_var2; +					for (i=0;i<endsamples;i++) +						vibval2[i] = (Bit32s)((vib_lut[i]*cptr[9].freq_high/8)*FIXEDPT*VIBFAC); +				} else vibval2 = vibval_const; + +				if (cptr[0].tremolo) tremval1 = trem_lut;	// tremolo enabled, use table +				else tremval1 = tremval_const; +				if (cptr[9].tremolo) tremval2 = trem_lut;	// tremolo enabled, use table +				else tremval2 = tremval_const; + +				cptr = &op[8]; +				if ((cptr[9].vibrato) && (cptr[9].op_state == OF_TYPE_OFF)) { +					vibval4 = vibval_var2; +					for (i=0;i<endsamples;i++) +						vibval4[i] = (Bit32s)((vib_lut[i]*cptr[9].freq_high/8)*FIXEDPT*VIBFAC); +				} else vibval4 = vibval_const; + +				if (cptr[9].tremolo) tremval4 = trem_lut;	// tremolo enabled, use table +				else tremval4 = tremval_const; + +				// calculate channel output +				for (i=0;i<endsamples;i++) { +					operator_advance_drums(&op[7],vibval1[i],&op[7+9],vibval2[i],&op[8+9],vibval4[i]); + +					opfuncs[op[7].op_state](&op[7]);			//Hihat +					operator_output(&op[7],0,tremval1[i]); + +					opfuncs[op[7+9].op_state](&op[7+9]);		//Snare +					operator_output(&op[7+9],0,tremval2[i]); + +					opfuncs[op[8+9].op_state](&op[8+9]);		//Cymbal +					operator_output(&op[8+9],0,tremval4[i]); + +					Bit32s chanval = (op[7].cval + op[7+9].cval + op[8+9].cval)*2; +					CHANVAL_OUT +				} +			} +		} + +		Bitu max_channel = NUM_CHANNELS; +#if defined(OPLTYPE_IS_OPL3) +		if ((adlibreg[0x105]&1)==0) max_channel = NUM_CHANNELS/2; +#endif +		for (Bits cur_ch=max_channel-1; cur_ch>=0; cur_ch--) { +			// skip drum/percussion operators +			if ((adlibreg[ARC_PERC_MODE]&0x20) && (cur_ch >= 6) && (cur_ch < 9)) continue; + +			Bitu k = cur_ch; +#if defined(OPLTYPE_IS_OPL3) +			if (cur_ch < 9) { +				cptr = &op[cur_ch]; +			} else { +				cptr = &op[cur_ch+9];	// second set is operator18-operator35 +				k += (-9+256);		// second set uses registers 0x100 onwards +			} +			// check if this operator is part of a 4-op +			if ((adlibreg[0x105]&1) && cptr->is_4op_attached) continue; +#else +			cptr = &op[cur_ch]; +#endif + +			// check for FM/AM +			if (adlibreg[ARC_FEEDBACK+k]&1) { +#if defined(OPLTYPE_IS_OPL3) +				if ((adlibreg[0x105]&1) && cptr->is_4op) { +					if (adlibreg[ARC_FEEDBACK+k+3]&1) { +						// AM-AM-style synthesis (op1[fb] + (op2 * op3) + op4) +						if (cptr[0].op_state != OF_TYPE_OFF) { +							if (cptr[0].vibrato) { +								vibval1 = vibval_var1; +								for (i=0;i<endsamples;i++) +									vibval1[i] = (Bit32s)((vib_lut[i]*cptr[0].freq_high/8)*FIXEDPT*VIBFAC); +							} else vibval1 = vibval_const; +							if (cptr[0].tremolo) tremval1 = trem_lut;	// tremolo enabled, use table +							else tremval1 = tremval_const; + +							// calculate channel output +							for (i=0;i<endsamples;i++) { +								operator_advance(&cptr[0],vibval1[i]); +								opfuncs[cptr[0].op_state](&cptr[0]); +								operator_output(&cptr[0],(cptr[0].lastcval+cptr[0].cval)*cptr[0].mfbi/2,tremval1[i]); + +								Bit32s chanval = cptr[0].cval; +								CHANVAL_OUT +							} +						} + +						if ((cptr[3].op_state != OF_TYPE_OFF) || (cptr[9].op_state != OF_TYPE_OFF)) { +							if ((cptr[9].vibrato) && (cptr[9].op_state != OF_TYPE_OFF)) { +								vibval1 = vibval_var1; +								for (i=0;i<endsamples;i++) +									vibval1[i] = (Bit32s)((vib_lut[i]*cptr[9].freq_high/8)*FIXEDPT*VIBFAC); +							} else vibval1 = vibval_const; +							if (cptr[9].tremolo) tremval1 = trem_lut;	// tremolo enabled, use table +							else tremval1 = tremval_const; +							if (cptr[3].tremolo) tremval2 = trem_lut;	// tremolo enabled, use table +							else tremval2 = tremval_const; + +							// calculate channel output +							for (i=0;i<endsamples;i++) { +								operator_advance(&cptr[9],vibval1[i]); +								opfuncs[cptr[9].op_state](&cptr[9]); +								operator_output(&cptr[9],0,tremval1[i]); + +								operator_advance(&cptr[3],0); +								opfuncs[cptr[3].op_state](&cptr[3]); +								operator_output(&cptr[3],cptr[9].cval*FIXEDPT,tremval2[i]); + +								Bit32s chanval = cptr[3].cval; +								CHANVAL_OUT +							} +						} + +						if (cptr[3+9].op_state != OF_TYPE_OFF) { +							if (cptr[3+9].tremolo) tremval1 = trem_lut;	// tremolo enabled, use table +							else tremval1 = tremval_const; + +							// calculate channel output +							for (i=0;i<endsamples;i++) { +								operator_advance(&cptr[3+9],0); +								opfuncs[cptr[3+9].op_state](&cptr[3+9]); +								operator_output(&cptr[3+9],0,tremval1[i]); + +								Bit32s chanval = cptr[3+9].cval; +								CHANVAL_OUT +							} +						} +					} else { +						// AM-FM-style synthesis (op1[fb] + (op2 * op3 * op4)) +						if (cptr[0].op_state != OF_TYPE_OFF) { +							if (cptr[0].vibrato) { +								vibval1 = vibval_var1; +								for (i=0;i<endsamples;i++) +									vibval1[i] = (Bit32s)((vib_lut[i]*cptr[0].freq_high/8)*FIXEDPT*VIBFAC); +							} else vibval1 = vibval_const; +							if (cptr[0].tremolo) tremval1 = trem_lut;	// tremolo enabled, use table +							else tremval1 = tremval_const; + +							// calculate channel output +							for (i=0;i<endsamples;i++) { +								operator_advance(&cptr[0],vibval1[i]); +								opfuncs[cptr[0].op_state](&cptr[0]); +								operator_output(&cptr[0],(cptr[0].lastcval+cptr[0].cval)*cptr[0].mfbi/2,tremval1[i]); + +								Bit32s chanval = cptr[0].cval; +								CHANVAL_OUT +							} +						} + +						if ((cptr[9].op_state != OF_TYPE_OFF) || (cptr[3].op_state != OF_TYPE_OFF) || (cptr[3+9].op_state != OF_TYPE_OFF)) { +							if ((cptr[9].vibrato) && (cptr[9].op_state != OF_TYPE_OFF)) { +								vibval1 = vibval_var1; +								for (i=0;i<endsamples;i++) +									vibval1[i] = (Bit32s)((vib_lut[i]*cptr[9].freq_high/8)*FIXEDPT*VIBFAC); +							} else vibval1 = vibval_const; +							if (cptr[9].tremolo) tremval1 = trem_lut;	// tremolo enabled, use table +							else tremval1 = tremval_const; +							if (cptr[3].tremolo) tremval2 = trem_lut;	// tremolo enabled, use table +							else tremval2 = tremval_const; +							if (cptr[3+9].tremolo) tremval3 = trem_lut;	// tremolo enabled, use table +							else tremval3 = tremval_const; + +							// calculate channel output +							for (i=0;i<endsamples;i++) { +								operator_advance(&cptr[9],vibval1[i]); +								opfuncs[cptr[9].op_state](&cptr[9]); +								operator_output(&cptr[9],0,tremval1[i]); + +								operator_advance(&cptr[3],0); +								opfuncs[cptr[3].op_state](&cptr[3]); +								operator_output(&cptr[3],cptr[9].cval*FIXEDPT,tremval2[i]); + +								operator_advance(&cptr[3+9],0); +								opfuncs[cptr[3+9].op_state](&cptr[3+9]); +								operator_output(&cptr[3+9],cptr[3].cval*FIXEDPT,tremval3[i]); + +								Bit32s chanval = cptr[3+9].cval; +								CHANVAL_OUT +							} +						} +					} +					continue; +				} +#endif +				// 2op additive synthesis +				if ((cptr[9].op_state == OF_TYPE_OFF) && (cptr[0].op_state == OF_TYPE_OFF)) continue; +				if ((cptr[0].vibrato) && (cptr[0].op_state != OF_TYPE_OFF)) { +					vibval1 = vibval_var1; +					for (i=0;i<endsamples;i++) +						vibval1[i] = (Bit32s)((vib_lut[i]*cptr[0].freq_high/8)*FIXEDPT*VIBFAC); +				} else vibval1 = vibval_const; +				if ((cptr[9].vibrato) && (cptr[9].op_state != OF_TYPE_OFF)) { +					vibval2 = vibval_var2; +					for (i=0;i<endsamples;i++) +						vibval2[i] = (Bit32s)((vib_lut[i]*cptr[9].freq_high/8)*FIXEDPT*VIBFAC); +				} else vibval2 = vibval_const; +				if (cptr[0].tremolo) tremval1 = trem_lut;	// tremolo enabled, use table +				else tremval1 = tremval_const; +				if (cptr[9].tremolo) tremval2 = trem_lut;	// tremolo enabled, use table +				else tremval2 = tremval_const; + +				// calculate channel output +				for (i=0;i<endsamples;i++) { +					// carrier1 +					operator_advance(&cptr[0],vibval1[i]); +					opfuncs[cptr[0].op_state](&cptr[0]); +					operator_output(&cptr[0],(cptr[0].lastcval+cptr[0].cval)*cptr[0].mfbi/2,tremval1[i]); + +					// carrier2 +					operator_advance(&cptr[9],vibval2[i]); +					opfuncs[cptr[9].op_state](&cptr[9]); +					operator_output(&cptr[9],0,tremval2[i]); + +					Bit32s chanval = cptr[9].cval + cptr[0].cval; +					CHANVAL_OUT +				} +			} else { +#if defined(OPLTYPE_IS_OPL3) +				if ((adlibreg[0x105]&1) && cptr->is_4op) { +					if (adlibreg[ARC_FEEDBACK+k+3]&1) { +						// FM-AM-style synthesis ((op1[fb] * op2) + (op3 * op4)) +						if ((cptr[0].op_state != OF_TYPE_OFF) || (cptr[9].op_state != OF_TYPE_OFF)) { +							if ((cptr[0].vibrato) && (cptr[0].op_state != OF_TYPE_OFF)) { +								vibval1 = vibval_var1; +								for (i=0;i<endsamples;i++) +									vibval1[i] = (Bit32s)((vib_lut[i]*cptr[0].freq_high/8)*FIXEDPT*VIBFAC); +							} else vibval1 = vibval_const; +							if ((cptr[9].vibrato) && (cptr[9].op_state != OF_TYPE_OFF)) { +								vibval2 = vibval_var2; +								for (i=0;i<endsamples;i++) +									vibval2[i] = (Bit32s)((vib_lut[i]*cptr[9].freq_high/8)*FIXEDPT*VIBFAC); +							} else vibval2 = vibval_const; +							if (cptr[0].tremolo) tremval1 = trem_lut;	// tremolo enabled, use table +							else tremval1 = tremval_const; +							if (cptr[9].tremolo) tremval2 = trem_lut;	// tremolo enabled, use table +							else tremval2 = tremval_const; + +							// calculate channel output +							for (i=0;i<endsamples;i++) { +								operator_advance(&cptr[0],vibval1[i]); +								opfuncs[cptr[0].op_state](&cptr[0]); +								operator_output(&cptr[0],(cptr[0].lastcval+cptr[0].cval)*cptr[0].mfbi/2,tremval1[i]); + +								operator_advance(&cptr[9],vibval2[i]); +								opfuncs[cptr[9].op_state](&cptr[9]); +								operator_output(&cptr[9],cptr[0].cval*FIXEDPT,tremval2[i]); + +								Bit32s chanval = cptr[9].cval; +								CHANVAL_OUT +							} +						} + +						if ((cptr[3].op_state != OF_TYPE_OFF) || (cptr[3+9].op_state != OF_TYPE_OFF)) { +							if (cptr[3].tremolo) tremval1 = trem_lut;	// tremolo enabled, use table +							else tremval1 = tremval_const; +							if (cptr[3+9].tremolo) tremval2 = trem_lut;	// tremolo enabled, use table +							else tremval2 = tremval_const; + +							// calculate channel output +							for (i=0;i<endsamples;i++) { +								operator_advance(&cptr[3],0); +								opfuncs[cptr[3].op_state](&cptr[3]); +								operator_output(&cptr[3],0,tremval1[i]); + +								operator_advance(&cptr[3+9],0); +								opfuncs[cptr[3+9].op_state](&cptr[3+9]); +								operator_output(&cptr[3+9],cptr[3].cval*FIXEDPT,tremval2[i]); + +								Bit32s chanval = cptr[3+9].cval; +								CHANVAL_OUT +							} +						} + +					} else { +						// FM-FM-style synthesis (op1[fb] * op2 * op3 * op4) +						if ((cptr[0].op_state != OF_TYPE_OFF) || (cptr[9].op_state != OF_TYPE_OFF) ||  +							(cptr[3].op_state != OF_TYPE_OFF) || (cptr[3+9].op_state != OF_TYPE_OFF)) { +							if ((cptr[0].vibrato) && (cptr[0].op_state != OF_TYPE_OFF)) { +								vibval1 = vibval_var1; +								for (i=0;i<endsamples;i++) +									vibval1[i] = (Bit32s)((vib_lut[i]*cptr[0].freq_high/8)*FIXEDPT*VIBFAC); +							} else vibval1 = vibval_const; +							if ((cptr[9].vibrato) && (cptr[9].op_state != OF_TYPE_OFF)) { +								vibval2 = vibval_var2; +								for (i=0;i<endsamples;i++) +									vibval2[i] = (Bit32s)((vib_lut[i]*cptr[9].freq_high/8)*FIXEDPT*VIBFAC); +							} else vibval2 = vibval_const; +							if (cptr[0].tremolo) tremval1 = trem_lut;	// tremolo enabled, use table +							else tremval1 = tremval_const; +							if (cptr[9].tremolo) tremval2 = trem_lut;	// tremolo enabled, use table +							else tremval2 = tremval_const; +							if (cptr[3].tremolo) tremval3 = trem_lut;	// tremolo enabled, use table +							else tremval3 = tremval_const; +							if (cptr[3+9].tremolo) tremval4 = trem_lut;	// tremolo enabled, use table +							else tremval4 = tremval_const; + +							// calculate channel output +							for (i=0;i<endsamples;i++) { +								operator_advance(&cptr[0],vibval1[i]); +								opfuncs[cptr[0].op_state](&cptr[0]); +								operator_output(&cptr[0],(cptr[0].lastcval+cptr[0].cval)*cptr[0].mfbi/2,tremval1[i]); + +								operator_advance(&cptr[9],vibval2[i]); +								opfuncs[cptr[9].op_state](&cptr[9]); +								operator_output(&cptr[9],cptr[0].cval*FIXEDPT,tremval2[i]); + +								operator_advance(&cptr[3],0); +								opfuncs[cptr[3].op_state](&cptr[3]); +								operator_output(&cptr[3],cptr[9].cval*FIXEDPT,tremval3[i]); + +								operator_advance(&cptr[3+9],0); +								opfuncs[cptr[3+9].op_state](&cptr[3+9]); +								operator_output(&cptr[3+9],cptr[3].cval*FIXEDPT,tremval4[i]); + +								Bit32s chanval = cptr[3+9].cval; +								CHANVAL_OUT +							} +						} +					} +					continue; +				} +#endif +				// 2op frequency modulation +				if ((cptr[9].op_state == OF_TYPE_OFF) && (cptr[0].op_state == OF_TYPE_OFF)) continue; +				if ((cptr[0].vibrato) && (cptr[0].op_state != OF_TYPE_OFF)) { +					vibval1 = vibval_var1; +					for (i=0;i<endsamples;i++) +						vibval1[i] = (Bit32s)((vib_lut[i]*cptr[0].freq_high/8)*FIXEDPT*VIBFAC); +				} else vibval1 = vibval_const; +				if ((cptr[9].vibrato) && (cptr[9].op_state != OF_TYPE_OFF)) { +					vibval2 = vibval_var2; +					for (i=0;i<endsamples;i++) +						vibval2[i] = (Bit32s)((vib_lut[i]*cptr[9].freq_high/8)*FIXEDPT*VIBFAC); +				} else vibval2 = vibval_const; +				if (cptr[0].tremolo) tremval1 = trem_lut;	// tremolo enabled, use table +				else tremval1 = tremval_const; +				if (cptr[9].tremolo) tremval2 = trem_lut;	// tremolo enabled, use table +				else tremval2 = tremval_const; + +				// calculate channel output +				for (i=0;i<endsamples;i++) { +					// modulator +					operator_advance(&cptr[0],vibval1[i]); +					opfuncs[cptr[0].op_state](&cptr[0]); +					operator_output(&cptr[0],(cptr[0].lastcval+cptr[0].cval)*cptr[0].mfbi/2,tremval1[i]); + +					// carrier +					operator_advance(&cptr[9],vibval2[i]); +					opfuncs[cptr[9].op_state](&cptr[9]); +					operator_output(&cptr[9],cptr[0].cval*FIXEDPT,tremval2[i]); + +					Bit32s chanval = cptr[9].cval; +					CHANVAL_OUT +				} +			} +		} + +#if defined(OPLTYPE_IS_OPL3) +		if (adlibreg[0x105]&1) { +			// convert to 16bit samples (stereo) +			for (i=0;i<endsamples;i++) { +				clipit16(outbufl[i],sndptr++); +				clipit16(outbufr[i],sndptr++); +			} +		} else { +			// convert to 16bit samples (mono) +			for (i=0;i<endsamples;i++) { +				clipit16(outbufl[i],sndptr++); +				clipit16(outbufl[i],sndptr++); +			} +		} +#else +		// convert to 16bit samples +		for (i=0;i<endsamples;i++) +			clipit16(outbufl[i],sndptr++); +#endif + +	} +} diff --git a/sound/softsynth/opl/opl.h b/sound/softsynth/opl/opl.h new file mode 100644 index 0000000000..4a04f9a0b7 --- /dev/null +++ b/sound/softsynth/opl/opl.h @@ -0,0 +1,197 @@ +/* + *  Copyright (C) 2002-2009  The DOSBox Team + *  OPL2/OPL3 emulation library + * + *  This library is free software; you can redistribute it and/or + *  modify it under the terms of the GNU Lesser General Public + *  License as published by the Free Software Foundation; either + *  version 2.1 of the License, or (at your option) any later version. + *  + *  This library is distributed in the hope that it will be useful, + *  but WITHOUT ANY WARRANTY; without even the implied warranty of + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + *  Lesser General Public License for more details. + *  + *  You should have received a copy of the GNU Lesser General Public + *  License along with this library; if not, write to the Free Software + *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + */ + + +/* + * Originally based on ADLIBEMU.C, an AdLib/OPL2 emulation library by Ken Silverman + * Copyright (C) 1998-2001 Ken Silverman + * Ken Silverman's official web site: "http://www.advsys.net/ken" + */ + + +#define fltype double + +/* +	define Bits, Bitu, Bit32s, Bit32u, Bit16s, Bit16u, Bit8s, Bit8u here +*/ + +typedef uint	Bitu; +typedef int		Bits; +typedef uint32	Bit32u; +typedef int32	Bit32s; +typedef uint16	Bit16u; +typedef int16	Bit16s; +typedef uint8	Bit8u; +typedef int8	Bit8s; + +#undef NUM_CHANNELS +#if defined(OPLTYPE_IS_OPL3) +#define NUM_CHANNELS	18 +#else +#define NUM_CHANNELS	9 +#endif + +#define MAXOPERATORS	(NUM_CHANNELS*2) + + +#define FL05	((fltype)0.5) +#define FL2		((fltype)2.0) + +#ifdef PI +#undef PI +#endif + +#define PI		((fltype)3.1415926535897932384626433832795) + + +#define FIXEDPT			0x10000		// fixed-point calculations using 16+16 +#define FIXEDPT_LFO		0x1000000	// fixed-point calculations using 8+24 + +#define WAVEPREC		1024		// waveform precision (10 bits) + +#define INTFREQU		((fltype)(14318180.0 / 288.0))		// clocking of the chip + + +#define OF_TYPE_ATT			0 +#define OF_TYPE_DEC			1 +#define OF_TYPE_REL			2 +#define OF_TYPE_SUS			3 +#define OF_TYPE_SUS_NOKEEP	4 +#define OF_TYPE_OFF			5 + +#define ARC_CONTROL			0x00 +#define ARC_TVS_KSR_MUL		0x20 +#define ARC_KSL_OUTLEV		0x40 +#define ARC_ATTR_DECR		0x60 +#define ARC_SUSL_RELR		0x80 +#define ARC_FREQ_NUM		0xa0 +#define ARC_KON_BNUM		0xb0 +#define ARC_PERC_MODE		0xbd +#define ARC_FEEDBACK		0xc0 +#define ARC_WAVE_SEL		0xe0 + +#define ARC_SECONDSET		0x100	// second operator set for OPL3 + + +#define OP_ACT_OFF			0x00 +#define OP_ACT_NORMAL		0x01	// regular channel activated (bitmasked) +#define OP_ACT_PERC			0x02	// percussion channel activated (bitmasked) + +#define BLOCKBUF_SIZE		512 + + +// vibrato constants +#define VIBTAB_SIZE			8 +#define VIBFAC				70/50000		// no braces, integer mul/div + +// tremolo constants and table +#define TREMTAB_SIZE		53 +#define TREM_FREQ			((fltype)(3.7))			// tremolo at 3.7hz + + +/* operator struct definition +     For OPL2 all 9 channels consist of two operators each, carrier and modulator. +     Channel x has operators x as modulator and operators (9+x) as carrier. +     For OPL3 all 18 channels consist either of two operators (2op mode) or four +     operators (4op mode) which is determined through register4 of the second +     adlib register set. +     Only the channels 0,1,2 (first set) and 9,10,11 (second set) can act as +     4op channels. The two additional operators for a channel y come from the +     2op channel y+3 so the operatorss y, (9+y), y+3, (9+y)+3 make up a 4op +     channel. +*/ +typedef struct operator_struct { +	Bit32s cval, lastcval;			// current output/last output (used for feedback) +	Bit32u tcount, wfpos, tinc;		// time (position in waveform) and time increment +	fltype amp, step_amp;			// and amplification (envelope) +	fltype vol;						// volume +	fltype sustain_level;			// sustain level +	Bit32s mfbi;					// feedback amount +	fltype a0, a1, a2, a3;			// attack rate function coefficients +	fltype decaymul, releasemul;	// decay/release rate functions +	Bit32u op_state;				// current state of operator (attack/decay/sustain/release/off) +	Bit32u toff; +	Bit32s freq_high;				// highest three bits of the frequency, used for vibrato calculations +	Bit16s* cur_wform;				// start of selected waveform +	Bit32u cur_wmask;				// mask for selected waveform +	Bit32u act_state;				// activity state (regular, percussion) +	bool sus_keep;					// keep sustain level when decay finished +	bool vibrato,tremolo;			// vibrato/tremolo enable bits +	 +	// variables used to provide non-continuous envelopes +	Bit32u generator_pos;			// for non-standard sample rates we need to determine how many samples have passed +	Bits cur_env_step;				// current (standardized) sample position +	Bits env_step_a,env_step_d,env_step_r;	// number of std samples of one step (for attack/decay/release mode) +	Bit8u step_skip_pos;			// position of 8-cyclic step skipping (always 2^x to check against mask) +	Bits env_step_skip_a;			// bitmask that determines if a step is skipped (respective bit is zero then) + +#if defined(OPLTYPE_IS_OPL3) +	bool is_4op,is_4op_attached;	// base of a 4op channel/part of a 4op channel +	Bit32s left_pan,right_pan;		// opl3 stereo panning amount +#endif +} op_type; + +// per-chip variables +Bitu chip_num; +op_type op[MAXOPERATORS]; + +Bits int_samplerate; +	 +Bit8u status; +Bit32u index; +#if defined(OPLTYPE_IS_OPL3) +Bit8u adlibreg[512];	// adlib register set (including second set) +Bit8u wave_sel[44];		// waveform selection +#else +Bit8u adlibreg[256];	// adlib register set +Bit8u wave_sel[22];		// waveform selection +#endif + + +// vibrato/tremolo increment/counter +Bit32u vibtab_pos; +Bit32u vibtab_add; +Bit32u tremtab_pos; +Bit32u tremtab_add; + + +// enable an operator +void enable_operator(Bitu regbase, op_type* op_pt); + +// functions to change parameters of an operator +void change_frequency(Bitu chanbase, Bitu regbase, op_type* op_pt); + +void change_attackrate(Bitu regbase, op_type* op_pt); +void change_decayrate(Bitu regbase, op_type* op_pt); +void change_releaserate(Bitu regbase, op_type* op_pt); +void change_sustainlevel(Bitu regbase, op_type* op_pt); +void change_waveform(Bitu regbase, op_type* op_pt); +void change_keepsustain(Bitu regbase, op_type* op_pt); +void change_vibrato(Bitu regbase, op_type* op_pt); +void change_feedback(Bitu chanbase, op_type* op_pt); + +// general functions +void adlib_init(Bit32u samplerate); +void adlib_write(Bitu idx, Bit8u val); +void adlib_getsample(Bit16s* sndptr, Bits numsamples); + +Bitu adlib_reg_read(Bitu port); +void adlib_write_index(Bitu port, Bit8u val); + +static Bit32u generator_add;	// should be a chip parameter | 
