[Scummvm-cvs-logs] CVS: scummvm/sound/softsynth .cvsignore,NONE,1.1 adlib.cpp,NONE,1.1 emumidi.h,NONE,1.1 mt32.cpp,NONE,1.1 ym2612.cpp,NONE,1.1

Max Horn fingolfin at users.sourceforge.net
Sat Dec 25 10:35:04 CET 2004


Update of /cvsroot/scummvm/scummvm/sound/softsynth
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv14193/sound/softsynth

Added Files:
	.cvsignore adlib.cpp emumidi.h mt32.cpp ym2612.cpp 
Log Message:
Moved the softsynth midi drivers into a sound/softsynth; amongst other things, this fixes bug #1083058

--- NEW FILE: .cvsignore ---
.deps

--- NEW FILE: adlib.cpp ---
/* ScummVM - Scumm Interpreter
 * Copyright (C) 2001-2004 The ScummVM project
 *
 * 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 * $Header: /cvsroot/scummvm/scummvm/sound/softsynth/adlib.cpp,v 1.1 2004/12/25 18:34:44 fingolfin Exp $
 */
[...1470 lines suppressed...]
		return 0;
	}

	as = &adlib_setparam_table[param];
	val = adlib_read(port + as->a);
	val &= as->c;
	val >>= as->b;
	if (as->d)
		val = as->d - val;

	return val;
}

void MidiDriver_ADLIB::adlib_note_on(int chan, byte note, int mod) {
	int code;
	assert(chan >= 0 && chan < 9);
	code = (note << 7) + mod;
	curnote_table[chan] = code;
	adlib_playnote(chan, (int16) channel_table_2[chan] + code);
}

--- NEW FILE: emumidi.h ---
/* ScummVM - Scumm Interpreter
 * Copyright (C) 2001-2004 The ScummVM project
 *
 * 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 * $Header: /cvsroot/scummvm/scummvm/sound/softsynth/emumidi.h,v 1.1 2004/12/25 18:34:44 fingolfin Exp $
 */

#include "stdafx.h"
#include "sound/audiostream.h"
#include "sound/mididrv.h"
#include "sound/mixer.h"

#define FIXP_SHIFT 16

class MidiDriver_Emulated : public AudioStream, public MidiDriver {
protected:
	bool _isOpen;
	SoundMixer *_mixer;

private:
	Timer::TimerProc _timerProc;
	void *_timerParam;

	int _nextTick;
	int _samplesPerTick;

protected:
	virtual void generateSamples(int16 *buf, int len) = 0;
	virtual void onTimer() {}

	int _baseFreq;

public:
	MidiDriver_Emulated(SoundMixer *mixer) : _mixer(mixer) {
		_isOpen = false;
	
		_timerProc = 0;
		_timerParam = 0;
	
		_nextTick = 0;
		_samplesPerTick = 0;

		_baseFreq = 250;
	}

	int open() {
		_isOpen = true;

		int d = getRate() / _baseFreq;
		int r = getRate() % _baseFreq;

		// This is equivalent to (getRate() << FIXP_SHIFT) / BASE_FREQ
		// but less prone to arithmetic overflow.

		_samplesPerTick = (d << FIXP_SHIFT) + (r << FIXP_SHIFT) / _baseFreq;
		return 0;
	}

	void setTimerCallback(void *timer_param, Timer::TimerProc timer_proc) {
		_timerProc = timer_proc;
		_timerParam = timer_param;
	}

	uint32 getBaseTempo() { return 1000000 / _baseFreq; }


	// AudioStream API
	int readBuffer(int16 *data, const int numSamples) {
		const int stereoFactor = isStereo() ? 2 : 1;
		int len = numSamples / stereoFactor;
		int step;

		do {
			step = len;
			if (step > (_nextTick >> FIXP_SHIFT))
				step = (_nextTick >> FIXP_SHIFT);

			generateSamples(data, step);
	
			_nextTick -= step << FIXP_SHIFT;
			if (!(_nextTick >> FIXP_SHIFT)) {
				if (_timerProc)
					(*_timerProc)(_timerParam);
				onTimer();
				_nextTick += _samplesPerTick;
			}
			data += step * stereoFactor;
			len -= step;
		} while (len);

		return numSamples;
	}
	bool endOfData() const { return false; }
};

--- NEW FILE: mt32.cpp ---
/* ScummVM - Scumm Interpreter
 * Copyright (C) 2001-2004 The ScummVM project
 *
 * 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 * $Header: /cvsroot/scummvm/scummvm/sound/softsynth/mt32.cpp,v 1.1 2004/12/25 18:34:44 fingolfin Exp $
 */

#include "stdafx.h"
#include "common/scummsys.h"

#ifdef USE_MT32EMU

#include "sound/softsynth/mt32/mt32emu.h"

#include "sound/softsynth/emumidi.h"
#include "sound/mpu401.h"

#include "common/util.h"
#include "common/file.h"
#include "common/config-manager.h"

#include "graphics/font.h"
#include "graphics/surface.h"

class MidiChannel_MT32 : public MidiChannel_MPU401 {
	void effectLevel(byte value) { }
	void chorusLevel(byte value) { }
};

class MidiDriver_MT32 : public MidiDriver_Emulated {
private:
	PlayingSoundHandle _handle;
	MidiChannel_MT32 _midiChannels[16];
	uint16 _channelMask;
	MT32Emu::Synth *_synth;

	int _outputRate;

protected:
	void generateSamples(int16 *buf, int len);

public:
	bool _initialising;

	MidiDriver_MT32(SoundMixer *mixer);
	virtual ~MidiDriver_MT32();

	int open();
	void close();
	void send(uint32 b);
	void setPitchBendRange (byte channel, uint range);
	void sysEx(byte *msg, uint16 length);

	uint32 property(int prop, uint32 param);
	MidiChannel *allocateChannel();
	MidiChannel *getPercussionChannel();

	// AudioStream API
	bool isStereo() const { return true; }
	int getRate() const { return _outputRate; }
};

typedef File SFile;

class MT32File: public MT32Emu::File {
	SFile file;
public:
	bool open(const char *filename, OpenMode mode) {
		SFile::AccessMode accessMode = mode == OpenMode_read ? SFile::kFileReadMode : SFile::kFileWriteMode;
		return file.open(filename, accessMode);
	}
	void close() {
		return file.close();
	}
	size_t read(void *in, size_t size) {
		return file.read(in, size);
	}
	bool readLine(char *in, size_t size) {
		return file.gets(in, size) != NULL;
	}
	bool readBit8u(MT32Emu::Bit8u *in) {
		byte b = file.readByte();
		if (file.eof())
			return false;
		*in = b;
		return true;
	}
	size_t write(const void *in, size_t size) {
		return file.write(in, size);
	}
	bool writeBit8u(MT32Emu::Bit8u out) {
		file.writeByte(out);
		return !file.ioFailed();
	}
	bool isEOF() {
		return file.eof();
	}
};

static int eatSystemEvents() {
	OSystem::Event event;
	while (g_system->pollEvent(event)) {
		switch (event.type) {
		case OSystem::EVENT_QUIT:
			return 1;
		default:
			break;
		}
	}
	return 0;
}

static void drawProgress(float progress) {
	Graphics::Surface surf;
	uint32 borderColor = 0x2;
	uint32 fillColor = 0x4;
	surf.w = g_system->getWidth() / 7 * 5;
	surf.h = Graphics::g_scummfont.getFontHeight();
	int x = g_system->getWidth() / 7;
	int y = g_system->getHeight() / 2 - surf.h / 2;
	surf.pitch = surf.w;
	surf.bytesPerPixel = 1;
	surf.pixels = calloc(surf.w, surf.h);
	Common::Rect r(surf.w, surf.h);
	surf.frameRect(r, borderColor);
	r.grow(-1);
	r.right = r.left + (uint16)(r.width() * progress);
	surf.fillRect(r, fillColor);
	g_system->copyRectToScreen((byte *)surf.pixels, surf.pitch, x, y, surf.w, surf.h);
	g_system->updateScreen();
	free(surf.pixels);
}

static void drawMessage(int offset, const Common::String &text) {
	const Graphics::Font &font(Graphics::g_scummfont);
	Graphics::Surface surf;
	uint32 color = 0x2;
	surf.w = g_system->getWidth();
	surf.h = font.getFontHeight();
	surf.pitch = surf.w;
	surf.bytesPerPixel = 1;
	surf.pixels = calloc(surf.w, surf.h);
	font.drawString(&surf, text, 0, 0, surf.w, color, Graphics::kTextAlignCenter);
	int y = g_system->getHeight() / 2 - font.getFontHeight() / 2 + offset * (font.getFontHeight() + 1);
	g_system->copyRectToScreen((byte *)surf.pixels, surf.pitch, 0, y, surf.w, surf.h);
	g_system->updateScreen();
	free(surf.pixels);
}

static MT32Emu::File *MT32_OpenFile(void *userData, const char *filename, MT32Emu::File::OpenMode mode) {
	MT32File *file = new MT32File();
	if (!file->open(filename, mode)) {
		delete file;
		return NULL;
	}
	return file;
}

static void MT32_PrintDebug(void *userData, const char *fmt, va_list list) {
	char buf[512];
	if (((MidiDriver_MT32 *)userData)->_initialising) {
		vsprintf(buf, fmt, list);
		buf[70] = 0; // Truncate to a reasonable length
		drawMessage(1, buf);
	}
	//vdebug(0, fmt, list); // FIXME: Use a higher debug level
}

static int MT32_Report(void *userData, MT32Emu::ReportType type, const void *reportData) {
	switch(type) {
	case MT32Emu::ReportType_lcdMessage:
		g_system->displayMessageOnOSD((const char *)reportData);
		break;
	case MT32Emu::ReportType_errorControlROM:
		error("Failed to load MT32_CONTROL.ROM");
		break;
	case MT32Emu::ReportType_errorPCMROM:
		error("Failed to load MT32_PCM.ROM");
		break;
	case MT32Emu::ReportType_progressInit:
		if (((MidiDriver_MT32 *)userData)->_initialising) {
			drawProgress(*((const float *)reportData));
			return eatSystemEvents();
		}
		break;
	default:
		break;
	}
	return 0;
}

////////////////////////////////////////
//
// MidiDriver_MT32
//
////////////////////////////////////////

MidiDriver_MT32::MidiDriver_MT32(SoundMixer *mixer) : MidiDriver_Emulated(mixer) {
	_channelMask = 0xFFFF; // Permit all 16 channels by default
	uint i;
	for (i = 0; i < ARRAYSIZE(_midiChannels); ++i) {
		_midiChannels[i].init(this, i);
	}
	_synth = NULL;
	// A higher baseFreq reduces the length used in generateSamples(),
	// and means that the timer callback will be called more often.
	// That results in more accurate timing.
	_baseFreq = 10000;
	// Unfortunately bugs in the emulator cause inaccurate tuning
	// at rates other than 32KHz, thus we produce data at 32KHz and
	// rely on SoundMixer to convert.
	_outputRate = 32000; //_mixer->getOutputRate();
	_initialising = false;
}

MidiDriver_MT32::~MidiDriver_MT32() {
	if (_synth != NULL)
		delete _synth;
}

int MidiDriver_MT32::open() {
	MT32Emu::SynthProperties prop;

	if (_isOpen)
		return MERR_ALREADY_OPEN;

	MidiDriver_Emulated::open();
	
	memset(&prop, 0, sizeof(prop));
	prop.sampleRate = getRate();
	prop.useReverb = true;
	prop.useDefaultReverb = false;
	prop.reverbType = 0;
	prop.reverbTime = 5;
	prop.reverbLevel = 3;
	prop.userData = this;
	prop.printDebug = MT32_PrintDebug;
	prop.report = MT32_Report;
	prop.openFile = MT32_OpenFile;
	_synth = new MT32Emu::Synth();
	_initialising = true;
	const byte dummy_palette[] = {
		0, 0, 0, 0, 
		0, 0, 171, 0, 
		0, 171, 0, 0, 
		0, 171, 171, 0, 
		171, 0, 0, 0
	};

	g_system->setPalette(dummy_palette, 0, 5);
	drawMessage(-1, "Initialising MT-32 Emulator");
	if (!_synth->open(prop))
		return MERR_DEVICE_NOT_AVAILABLE;
	_initialising = false;
	g_system->clearScreen();
	g_system->updateScreen();
	_mixer->playInputStream(&_handle, this, false, -1, 255, 0, false, true);
	return 0;
}

void MidiDriver_MT32::send(uint32 b) {
	_synth->playMsg(b);
}

void MidiDriver_MT32::setPitchBendRange(byte channel, uint range) {
	if (range > 24) {
		printf("setPitchBendRange() called with range > 24: %d", range);
	}
	byte benderRangeSysex[9];
	benderRangeSysex[0] = 0x41; // Roland
	benderRangeSysex[1] = channel;
	benderRangeSysex[2] = 0x16; // MT-32
	benderRangeSysex[3] = 0x12; // Write
	benderRangeSysex[4] = 0x00;
	benderRangeSysex[5] = 0x00;
	benderRangeSysex[6] = 0x04;
	benderRangeSysex[7] = (byte)range;
	benderRangeSysex[8] = MT32Emu::Synth::calcSysexChecksum(&benderRangeSysex[4], 4, 0);
	sysEx(benderRangeSysex, 9);
}

void MidiDriver_MT32::sysEx(byte *msg, uint16 length) {
	if (msg[0] == 0xf0) {
		_synth->playSysex(msg, length);
	} else {
		_synth->playSysexWithoutFraming(msg, length);
	}
}

void MidiDriver_MT32::close() {
	if (!_isOpen)
		return;
	_isOpen = false;

	// Detach the player callback handler
	setTimerCallback(NULL, NULL);
	// Detach the mixer callback handler
	_mixer->stopHandle(_handle);

	_synth->close();
	delete _synth;
	_synth = NULL;
}

void MidiDriver_MT32::generateSamples(int16 *data, int len) {
	_synth->render(data, len);
}

uint32 MidiDriver_MT32::property(int prop, uint32 param) {
	switch (prop) {
	case PROP_CHANNEL_MASK:
		_channelMask = param & 0xFFFF;
		return 1;
	}

	return 0;
}

MidiChannel *MidiDriver_MT32::allocateChannel() {
	MidiChannel_MT32 *chan;
	uint i;

	for (i = 0; i < ARRAYSIZE(_midiChannels); ++i) {
		if (i == 9 || !(_channelMask & (1 << i)))
			continue;
		chan = &_midiChannels[i];
		if (chan->allocate()) {
			return chan;
		}
	}
	return NULL;
}

MidiChannel *MidiDriver_MT32::getPercussionChannel() {
	return &_midiChannels[9];
}

// This code should be used when calling the timer callback from the mixer thread is undesirable.
// Note that it results in less accurate timing.
#if 0
class MidiEvent_MT32 {
public:
	MidiEvent_MT32 *_next;
	uint32 _msg; // 0xFFFFFFFF indicates a sysex message
	byte *_data;
	uint32 _len;

	MidiEvent_MT32(uint32 msg, byte *data, uint32 len) {
		_msg = msg;
		if (len > 0) {
			_data = new byte[len];
			memcpy(_data, data, len);
		}
		_len = len;
		_next = NULL;
	}

	MidiEvent_MT32() {
		if (_len > 0)
			delete _data;
	}
};

class MidiDriver_ThreadedMT32 : public MidiDriver_MT32 {
private:
	OSystem::MutexRef _eventMutex;
	MidiEvent_MT32 *_events;
	Timer::TimerProc _timer_proc;

	void pushMidiEvent(MidiEvent_MT32 *event);
	MidiEvent_MT32 *popMidiEvent();

protected:
	void send(uint32 b);
	void sysEx(byte *msg, uint16 length);

public:
	MidiDriver_ThreadedMT32(SoundMixer *mixer);
	virtual ~MidiDriver_ThreadedMT32();

	void onTimer();
	void close();
	void setTimerCallback(void *timer_param, Timer::TimerProc timer_proc);
};


MidiDriver_ThreadedMT32::MidiDriver_ThreadedMT32(SoundMixer *mixer) : MidiDriver_MT32(mixer) {
	_eventMutex = g_system->createMutex();
	_events = NULL;
	_timer_proc = NULL;
}

MidiDriver_ThreadedMT32::~MidiDriver_ThreadedMT32() {
	g_system->deleteMutex(_eventMutex);
}

void MidiDriver_ThreadedMT32::close() {
	MidiDriver_MT32::close();
	while ((popMidiEvent() != NULL)) {
		// Just eat any leftover events
	}
}

void MidiDriver_ThreadedMT32::setTimerCallback(void *timer_param, Timer::TimerProc timer_proc) {
	if (!_timer_proc || !timer_proc) {
		if (_timer_proc)
			g_timer->removeTimerProc(_timer_proc);
		_timer_proc = timer_proc;
		if (timer_proc)
			g_timer->installTimerProc(timer_proc, getBaseTempo(), timer_param);
	}
}

void MidiDriver_ThreadedMT32::pushMidiEvent(MidiEvent_MT32 *event) {
	g_system->lockMutex(_eventMutex);
	if (_events == NULL) {
		_events = event;
	} else {
		MidiEvent_MT32 *last = _events;
		while (last->_next != NULL)
			last = last->_next;
		last->_next = event;
	}
	g_system->unlockMutex(_eventMutex);
}

MidiEvent_MT32 *MidiDriver_ThreadedMT32::popMidiEvent() {
	MidiEvent_MT32 *event;
	g_system->lockMutex(_eventMutex);
	event = _events;
	if (event != NULL)
		_events = event->_next;
	g_system->unlockMutex(_eventMutex);
	return event;
}

void MidiDriver_ThreadedMT32::send(uint32 b) {
	MidiEvent_MT32 *event = new MidiEvent_MT32(b, NULL, 0);
	pushMidiEvent(event);
}

void MidiDriver_ThreadedMT32::sysEx(byte *msg, uint16 length) {
	MidiEvent_MT32 *event = new MidiEvent_MT32(0xFFFFFFFF, msg, length);
	pushMidiEvent(event);
}

void MidiDriver_ThreadedMT32::onTimer() {
	MidiEvent_MT32 *event;
	while ((event = popMidiEvent()) != NULL) {
		if (event->_msg == 0xFFFFFFFF) {
			MidiDriver_MT32::sysEx(event->_data, event->_len);
		} else {
			MidiDriver_MT32::send(event->_msg);
		}
		delete event;
	}
}
#endif

////////////////////////////////////////
//
// MidiDriver_MT32 factory
//
////////////////////////////////////////

MidiDriver *MidiDriver_MT32_create(SoundMixer *mixer) {
	// HACK: It will stay here until engine plugin loader overhaul
	if (ConfMan.hasKey("extrapath"))                                        
		File::addDefaultDirectory(ConfMan.get("extrapath"));
	return new MidiDriver_MT32(mixer);
}

#endif

--- NEW FILE: ym2612.cpp ---
/* ScummVM - Scumm Interpreter
 * Copyright (C) 2001-2004 The ScummVM project
 *
 * YM2612 tone generation code written by Tomoaki Hayasaka.
 * Used under the terms of the GNU General Public License.
 * Adpated to ScummVM by Jamieson Christian.
 *
 * 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 * $Header: /cvsroot/scummvm/scummvm/sound/softsynth/ym2612.cpp,v 1.1 2004/12/25 18:34:44 fingolfin Exp $
 */

#include "sound/softsynth/emumidi.h"

#include <math.h>


////////////////////////////////////////
//
// Miscellaneous
//
////////////////////////////////////////

static int *sintbl = 0;
static int *powtbl = 0;
static int *frequencyTable = 0;
static int *keycodeTable = 0;
static int *keyscaleTable = 0;
static int *attackOut = 0;

////////////////////////////////////////
//
// Class declarations
//
////////////////////////////////////////

class Operator2612;
class Voice2612;
class MidiChannel_YM2612;
class MidiDriver_YM2612;

class Operator2612 {
protected:
	Voice2612 *_owner;
	enum State { _s_ready, _s_attacking, _s_decaying, _s_sustaining, _s_releasing };
	State _state;
	int32 _currentLevel;
	int _frequency;
	uint32 _phase;
	int _lastOutput;
	int _feedbackLevel;
	int _detune;
	int _multiple;
	int32 _totalLevel;
	int _keyScale;
	int _velocity;
	int _specifiedTotalLevel;
	int _specifiedAttackRate;
	int _specifiedDecayRate;
	int _specifiedSustainLevel;
	int _specifiedSustainRate;
	int _specifiedReleaseRate;
	int _tickCount;
	int _attackTime;
	int32 _decayRate;
	int32 _sustainLevel;
	int32 _sustainRate;
	int32 _releaseRate;

public:
	Operator2612 (Voice2612 *owner);
	~Operator2612();
	void feedbackLevel(int level);
	void setInstrument(byte const *instrument);
	void velocity(int velo);
	void keyOn();
	void keyOff();
	void frequency(int freq);
	void nextTick(const int *phaseShift, int *outbuf, int buflen);
	bool inUse() { return (_state != _s_ready); }
};

class Voice2612 {
public:
	Voice2612 *next;
	uint16 _rate;

protected:
	Operator2612 *_opr[4];
	int _velocity;
	int _control7;
	int _note;
	int _frequencyOffs;
	int _frequency;
	int _algorithm;
	
	int *_buffer;
	int _buflen;

public:
	Voice2612();
	~Voice2612();
	void setControlParameter(int control, int value);
	void setInstrument(byte const *instrument);
	void velocity(int velo);
	void nextTick(int *outbuf, int buflen);
	void noteOn(int n, int onVelo);
	bool noteOff(int note);
	void pitchBend(int value);
	void recalculateFrequency();
};

class MidiChannel_YM2612 : public MidiChannel {
protected:
	uint16 _rate;
	Voice2612 *_voices;
	Voice2612 *_next_voice;

public:
	void removeAllVoices();
	void nextTick(int *outbuf, int buflen);
	void rate(uint16 r);

public:
	MidiChannel_YM2612();
	virtual ~MidiChannel_YM2612();

	// MidiChannel interface
	MidiDriver *device() { return 0; }
	byte getNumber() { return 0; }
	void release() { }
	void send(uint32 b) { }
	void noteOff(byte note);
	void noteOn(byte note, byte onVelo);
	void programChange(byte program) { }
	void pitchBend(int16 value);
	void controlChange(byte control, byte value);
	void pitchBendFactor(byte value) { }
	void sysEx_customInstrument(uint32 type, byte *instr);
};

class MidiDriver_YM2612 : public MidiDriver_Emulated {
protected:
	MidiChannel_YM2612 *_channel[16];

	int _next_voice;
	int _volume;

protected:
	static void createLookupTables();
	void nextTick(int16 *buf1, int buflen);
	int volume(int val = -1) { if (val >= 0) _volume = val; return _volume; }
	void rate(uint16 r);

	void generateSamples(int16 *buf, int len);

public:
	MidiDriver_YM2612(SoundMixer *mixer);
	virtual ~MidiDriver_YM2612();

	int open();
	void close();
	void send(uint32 b);
	void send(byte channel, uint32 b); // Supports higher than channel 15
	uint32 property(int prop, uint32 param) { return 0; }

	void setPitchBendRange(byte channel, uint range) { }
	void sysEx(byte *msg, uint16 length);

	MidiChannel *allocateChannel() { return 0; }
	MidiChannel *getPercussionChannel() { return 0; }


	// AudioStream API
	bool isStereo() const { return true; }
	int getRate() const { return _mixer->getOutputRate(); }
};

////////////////////////////////////////
//
// Operator2612 implementation
//
////////////////////////////////////////

Operator2612::Operator2612 (Voice2612 *owner) :
	_owner (owner),
	_state (_s_ready),
	_currentLevel ((int32)0x7f << 15),
	_phase (0),
	_lastOutput (0),
	_feedbackLevel (0),
	_detune (0),
	_multiple (1),
	_keyScale (0),
	_specifiedTotalLevel (127),
	_specifiedAttackRate (0),
	_specifiedDecayRate (0),
	_specifiedSustainRate (0),
	_specifiedReleaseRate (15) {
		velocity(0);
}

Operator2612::~Operator2612()
{ }

void Operator2612::velocity(int velo) {
	_velocity = velo;
	_totalLevel = ((int32)_specifiedTotalLevel << 15) +
					((int32)(127-_velocity) << 13);
	_sustainLevel = ((int32)_specifiedSustainLevel << 17);
}

void Operator2612::feedbackLevel(int level) {
	_feedbackLevel = level;
}

void Operator2612::setInstrument(byte const *instrument) {
	_detune = (instrument[8] >> 4) & 7;
	_multiple = instrument[8] & 15;
	_specifiedTotalLevel = instrument[12] & 127;
	_keyScale = (instrument[16] >> 6) & 3;
	_specifiedAttackRate = instrument[16] & 31;
	_specifiedDecayRate = instrument[20] & 31;
	_specifiedSustainRate = instrument[24] & 31;
	_specifiedSustainLevel = (instrument[28] >> 4) & 15;
	_specifiedReleaseRate = instrument[28] & 15;
	_state = _s_ready; // ËÜʪ¤Ç¤Ï¤É¤¦¤Ê¤Î¤«¤Ê?
	velocity(_velocity);
}

void Operator2612::keyOn() {
	_state = _s_attacking;
	_tickCount = 0;
	_phase = 0;			// ¤É¤¦¤â¡¢¼ÂºÝ¤³¤¦¤é¤·¤¤
	_currentLevel = ((int32)0x7f << 15); // ¤³¤ì¤â¡¢¼ÂºÝ¤³¤¦¤é¤·¤¤
}

void Operator2612::keyOff() {
	if (_state != _s_ready)
		_state = _s_releasing;
}

void Operator2612::frequency(int freq) {
	double value; // Use for intermediate computations to avoid int64 arithmetic
	int r;

	_frequency = freq / _owner->_rate;

	r = _specifiedAttackRate;
	if (r != 0) {
		r = r * 2 + (keyscaleTable[freq/262205] >> (3-_keyScale));
		if (r >= 64)
		r = 63; // ¤¹¤ë¤Ù¤­¤Ê¤ó¤À¤í¤¦¤È¤Ï»×¤¦¤ó¤À¤±¤É (ÀÖp.207)
	}

	r = 63 - r;
	if (_specifiedTotalLevel >= 128)
		value = 0;
	else {
		value = powtbl[(r&3) << 7];
		value *= 1 << (r >> 2);
		value *= 41; // r == 20 ¤Î¤È¤­¡¢0-96[db] ¤¬ 10.01[ms] == 41.00096
		value /= 1 << (15 + 5);
		value *= 127 - _specifiedTotalLevel;
		value /= 127;
	}
	_attackTime = (int32) value; // 1 ÉÃ == (1 << 12)
	if (_attackTime > 0)
		_attackTime = (1 << (12+10)) / (_owner->_rate * _attackTime);

	r = _specifiedDecayRate;
	if (r != 0) {
		r = r * 2 + (keyscaleTable[freq/262205] >> (3-_keyScale));
		if (r >= 64)
			r = 63;
	}
	value = (double) powtbl[(r&3) << 7] * (0x10 << (r>>2)) / 31;
	_decayRate = (int32) value / _owner->_rate;

	r = _specifiedSustainRate;
	if (r != 0) {
		r = r * 2 + (keyscaleTable[freq/262205] >> (3-_keyScale));
		if (r >= 64)
			r = 63;
	}
	value = (double) powtbl[(r&3) << 7] * (0x10 << (r>>2)) / 31;
	_sustainRate = (int32) value / _owner->_rate;

	r = _specifiedReleaseRate;
	if (r != 0) {
		r = r * 2 + 1;		// (Translated) I cannot know whether the timing is a good choice or not
		r = r * 2 + (keyscaleTable[freq/262205] >> (3-_keyScale));
		// KS ¤Ë¤è¤ëÊäÀµ¤Ï¤¢¤ë¤é¤·¤¤¡£ÀÖp.206 ¤Ç¤Ïµ­½Ò¤µ¤ì¤Æ¤Ê¤¤¤±¤É¡£
		if (r >= 64)
			r = 63;
	}
	value = (double) powtbl[(r&3) << 7] * (0x10 << (r>>2)) / 31;
	_releaseRate = (int32) value / _owner->_rate;
}

void Operator2612::nextTick(const int *phasebuf, int *outbuf, int buflen) {
	if (_state == _s_ready)
		return;
	if (_state == _s_attacking && _attackTime <= 0) {
		_currentLevel = 0;
		_state = _s_decaying;
	}

	int32 levelIncrement = 0;
	int32 target = 0;
	State next_state = _s_ready;
	const int32 zero_level = ((int32)0x7f << 15);
	const int phaseIncrement = (_multiple > 0) ? (_frequency * _multiple) : (_frequency / 2);

	int32 output = _lastOutput;
	int32 level = _currentLevel + _totalLevel;

	while (buflen) {
		switch (_state) {
		case _s_ready:
			return;
			break;
		case _s_attacking:
			next_state = _s_attacking;
			break;
		case _s_decaying:
			levelIncrement = _decayRate;
			target = _sustainLevel + _totalLevel;
			next_state = _s_sustaining;
			break;
		case _s_sustaining:
			levelIncrement = _sustainRate;
			target = zero_level + _totalLevel;
			next_state = _s_ready;
			break;
		case _s_releasing:
			levelIncrement = _releaseRate;
			target = zero_level + _totalLevel;
			next_state = _s_ready;
			break;
		}

		bool switching = false;
		do {
			if (next_state == _s_attacking) {
				// Attack phase
				++_tickCount;
				int i = (int) (_tickCount * _attackTime);
				if (i >= 1024) {
					level = _totalLevel;
					_state = _s_decaying;
					switching = true;
				} else {
					level = (attackOut[i] << (31 - 8 - 16)) + _totalLevel;
				}
			} else {
				// Decay, Sustain and Release phases
				level += levelIncrement;
				if (level >= target) {
					level = target;
					_state = next_state;
					switching = true;
				}
			}

			if (level < zero_level) {
				int phaseShift = *phasebuf >> 2; // Àµ¤·¤¤ÊÑÄ´Î̤Ï?  3 ¤¸¤ã¾®¤µ¤¹¤®¤Ç 2 ¤¸¤ãÂ礭¤¤¤è¤¦¤Ê¡£
				if (_feedbackLevel)
					phaseShift += (output << (_feedbackLevel - 1)) / 1024;
				output = sintbl[((_phase >> 7) + phaseShift) & 0x7ff];
				output >>= (level >> 18);	// Àµ¤·¤¤¸º¿êÎ̤Ï?
				// Here is the original code, which requires 64-bit ints
//				output *= powtbl[511 - ((level>>25)&511)];
//				output >>= 16;
//				output >>= 1;
				// And here's our 32-bit trick for doing it. (Props to Fingolfin!)
				// Result varies from original code by max of 1.
//				int powVal = powtbl[511 - ((level>>9)&511)];
//				int outputHI = output / 256;
//				int powHI = powVal / 256;
//				output = (outputHI * powHI) / 2 + (outputHI * (powVal % 256) + powHI * (output % 256)) / 512;
				// And here's the even faster code.
				// Result varies from original code by max of 8.
				output = ((output >> 4) * (powtbl[511-((level>>9)&511)] >> 3)) / 1024;

				_phase += phaseIncrement;
				_phase &= 0x3ffff;
			} else
				output = 0;

			*outbuf += output;
			 --buflen;
			 ++phasebuf;
			 ++outbuf;
		} while (buflen && !switching);
	}
	_lastOutput = output;
	_currentLevel = level - _totalLevel;
}

////////////////////////////////////////
//
// Voice2612 implementation
//
////////////////////////////////////////

Voice2612::Voice2612() {
	next = 0;
	_control7 = 127;
	_note = 40;
	_frequency = 440;
	_frequencyOffs = 0x2000;
	_algorithm = 7;

	_buffer = 0;
	_buflen = 0;

	int i;
	for (i = 0; i < ARRAYSIZE(_opr); ++i)
		_opr[i] = new Operator2612 (this);
	velocity(0);
}

Voice2612::~Voice2612() {
	int i;
	for (i = 0; i < ARRAYSIZE(_opr); ++i)
		delete _opr[i];
	free(_buffer);
}

void Voice2612::velocity(int velo) {
	_velocity = velo;
#if 0
	int v = (velo * _control7) >> 7; // ¤³¤ì¤À¤ÈÀºÅÙÎɤ¯¤Ê¤¤¤Ç¤¹¤Í
#else
	int v = velo + (_control7 - 127) * 4;
#endif
	bool iscarrier[8][4] = {
		{ false, false, false,  true, }, //0
		{ false, false, false,  true, }, //1
		{ false, false, false,  true, }, //2
		{ false, false, false,  true, }, //3
		{ false,  true, false,  true, }, //4
		{ false,  true,  true,  true, }, //5
		{ false,  true,  true,  true, }, //6
		{  true,  true,  true,  true, }, //7
	};
	int opr;
	for (opr = 0; opr < 4; opr++)
		if (iscarrier[_algorithm][opr])
			_opr[opr]->velocity(v);
		else
			_opr[opr]->velocity(127);
}

void Voice2612::setControlParameter(int control, int value) {
	switch (control) {
	case 7:
		_control7 = value;
		velocity(_velocity);
		break;
	case 123:
		// All notes off
		noteOff(_note);
	};
}

void Voice2612::setInstrument(byte const *instrument) {
	if (instrument == NULL)
		return;

	_algorithm = instrument[32] & 7;
	_opr[0]->feedbackLevel((instrument[32] >> 3) & 7);
	_opr[1]->feedbackLevel(0);
	_opr[2]->feedbackLevel(0);
	_opr[3]->feedbackLevel(0);
	_opr[0]->setInstrument(instrument + 0);
	_opr[1]->setInstrument(instrument + 2);
	_opr[2]->setInstrument(instrument + 1);
	_opr[3]->setInstrument(instrument + 3);
}

void Voice2612::nextTick(int *outbuf, int buflen) {
	if (_velocity == 0)
		return;

	if (_buflen < buflen) {
		free(_buffer);
		_buflen = buflen;
		_buffer = (int *) malloc(sizeof(int) * buflen * 2);
	}

	int *buf1 = _buffer;
	int *buf2 = _buffer + buflen;
	memset(_buffer, 0, sizeof(int) * buflen * 2);

	switch (_algorithm) {
	case 0:
		_opr[0]->nextTick(buf1, buf2, buflen);
		_opr[1]->nextTick(buf2, buf1, buflen);
		memset (buf2, 0, sizeof (int) * buflen);
		_opr[2]->nextTick(buf1, buf2, buflen);
		_opr[3]->nextTick(buf2, outbuf, buflen);
		break;
	case 1:
		_opr[0]->nextTick(buf1, buf2, buflen);
		_opr[1]->nextTick(buf1, buf2, buflen);
		_opr[2]->nextTick(buf2, buf1, buflen);
		_opr[3]->nextTick(buf1, outbuf, buflen);
		break;
	case 2:
		_opr[1]->nextTick(buf1, buf2, buflen);
		_opr[2]->nextTick(buf2, buf1, buflen);
		memset(buf2, 0, sizeof(int) * buflen);
		_opr[0]->nextTick(buf2, buf1, buflen);
		_opr[3]->nextTick(buf1, outbuf, buflen);
		break;
	case 3:
		_opr[0]->nextTick(buf1, buf2, buflen);
		_opr[1]->nextTick(buf2, buf1, buflen);
		memset(buf2, 0, sizeof(int) * buflen);
		_opr[2]->nextTick(buf2, buf1, buflen);
		_opr[3]->nextTick(buf1, outbuf, buflen);
		break;
	case 4:
		_opr[0]->nextTick(buf1, buf2, buflen);
		_opr[1]->nextTick(buf2, outbuf, buflen);
		_opr[2]->nextTick(buf1, buf1, buflen);
		_opr[3]->nextTick(buf1, outbuf, buflen);
		break;
	case 5:
		_opr[0]->nextTick(buf1, buf2, buflen);
		_opr[1]->nextTick(buf2, outbuf, buflen);
		_opr[2]->nextTick(buf2, outbuf, buflen);
		_opr[3]->nextTick(buf2, outbuf, buflen);
		break;
	case 6:
		_opr[0]->nextTick(buf1, buf2, buflen);
		_opr[1]->nextTick(buf2, outbuf, buflen);
		_opr[2]->nextTick(buf1, outbuf, buflen);
		_opr[3]->nextTick(buf1, outbuf, buflen);
		break;
	case 7:
		_opr[0]->nextTick(buf1, outbuf, buflen);
		_opr[1]->nextTick(buf1, outbuf, buflen);
		_opr[2]->nextTick(buf1, outbuf, buflen);
		_opr[3]->nextTick(buf1, outbuf, buflen);
		break;
	};
}

void Voice2612::noteOn(int n, int onVelo) {
	_note = n;
	velocity(onVelo);
	recalculateFrequency();
	int i;
	for (i = 0; i < ARRAYSIZE(_opr); i++)
		_opr[i]->keyOn();
}

bool Voice2612::noteOff(int note) {
	if (_note != note)
		return false;
	int i;
	for (i = 0; i < ARRAYSIZE(_opr); i++)
		_opr[i]->keyOff();
	return true;
}

void Voice2612::pitchBend(int value) {
	_frequencyOffs = value;
	recalculateFrequency();
}

void Voice2612::recalculateFrequency() {
	// MIDI ¤È¤â°ã¤¦¤·....
	// ¤É¤¦¤¤¤¦»ÅÍͤʤó¤À¤í¤¦¤«?
	// ¤È»×¤Ã¤¿¤é¡¢¤Ê¤ó¤È¡¢¤³¤ì (¢­) ¤¬Àµ²ò¤é¤·¤¤¡£
	int32 basefreq = frequencyTable[_note];
	int cfreq = frequencyTable[_note - (_note % 12)];
	int oct = _note / 12;
	int fnum = (int) (((double)basefreq * (1 << 13)) / cfreq); // OPL ¤Î fnum ¤ÈƱ¤¸¤è¤¦¤Ê¤â¤Î¡£
	fnum += _frequencyOffs - 0x2000;
	if (fnum < 0x2000) {
		fnum += 0x2000;
		oct--;
	}
	if (fnum >= 0x4000) {
		fnum -= 0x2000;
		oct++;
	}

	// _frequency ¤ÏºÇ½ªÅª¤Ë¥Ð¥¤¥¢¥¹ 256*1024 ÇÜ
	_frequency = (int) ((frequencyTable[oct*12] * (double)fnum) / 8);

	int i;
	for (i = 0; i < ARRAYSIZE(_opr); i++)
		_opr[i]->frequency(_frequency);
}

////////////////////////////////////////
//
// MidiChannel_YM2612
//
////////////////////////////////////////

MidiChannel_YM2612::MidiChannel_YM2612() {
	_voices = 0;
	_next_voice = 0;
}

MidiChannel_YM2612::~MidiChannel_YM2612() {
	removeAllVoices();
}

void MidiChannel_YM2612::removeAllVoices() {
	if (!_voices)
		return;
	Voice2612 *last, *voice = _voices;
	for (; voice; voice = last) {
		last = voice->next;
		delete voice;
	}
	_voices = _next_voice = 0;
}

void MidiChannel_YM2612::noteOn(byte note, byte onVelo) {
	if (!_voices)
		return;
	_next_voice = _next_voice ? _next_voice : _voices;
	_next_voice->noteOn(note, onVelo);
	_next_voice = _next_voice->next;
}

void MidiChannel_YM2612::noteOff(byte note) {
	if (!_voices)
		return;
	if (_next_voice == _voices)
		_next_voice = 0;
	Voice2612 *voice = _next_voice;
	do {
		if (!voice)
			voice = _voices;
		if (voice->noteOff(note)) {
			_next_voice = voice;
			break;
		}
		voice = voice->next;
	} while (voice != _next_voice);
}

void MidiChannel_YM2612::controlChange(byte control, byte value) {
	// ¤¤¤¤¤Î¤«¤³¤ì¤Ç?
	if (control == 121) {
		// Reset controller
		removeAllVoices();
	} else {
		Voice2612 *voice = _voices;
		for (; voice; voice = voice->next)
			voice->setControlParameter(control, value);
	}
}

void MidiChannel_YM2612::sysEx_customInstrument(uint32 type, byte *fmInst) {
	if (type != 'EUP ')
		return;
	Voice2612 *voice = new Voice2612;
	voice->next = _voices;
	_voices = voice;
	voice->_rate = _rate;
	voice->setInstrument(fmInst);
}

void MidiChannel_YM2612::pitchBend(int16 value) {
  // ¤¤¤¤¤Î¤«¤³¤ì¤Ç?
	Voice2612 *voice = _voices;
	for (; voice; voice = voice->next)
		voice->pitchBend(value);
}

void MidiChannel_YM2612::nextTick(int *outbuf, int buflen) {
	Voice2612 *voice = _voices;
	for (; voice; voice = voice->next)
		voice->nextTick(outbuf, buflen);
}

void MidiChannel_YM2612::rate(uint16 r) {
	_rate = r;
	Voice2612 *voice = _voices;
	for (; voice; voice = voice->next)
		voice->_rate = r;
}

////////////////////////////////////////
//
// MidiDriver_YM2612
//
////////////////////////////////////////

MidiDriver_YM2612::MidiDriver_YM2612(SoundMixer *mixer)
	: MidiDriver_Emulated(mixer) {
	_next_voice = 0;

	createLookupTables();
	_volume = 256;
	int i;
	for (i = 0; i < ARRAYSIZE(_channel); i++)
		_channel[i] = new MidiChannel_YM2612;
	rate(getRate());
}

MidiDriver_YM2612::~MidiDriver_YM2612() {
	int i;
	for (i = 0; i < ARRAYSIZE(_channel); i++)
		delete _channel[i];
	delete sintbl;
	delete powtbl;
	delete frequencyTable;
	delete keycodeTable;
	delete keyscaleTable;
	delete attackOut;
	sintbl = powtbl = frequencyTable = keycodeTable = keyscaleTable = attackOut = 0;
}

int MidiDriver_YM2612::open() {
	if (_isOpen)
		return MERR_ALREADY_OPEN;

	MidiDriver_Emulated::open();

	_mixer->setupPremix(this);
	return 0;
}

void MidiDriver_YM2612::close() {
	if (!_isOpen)
		return;
	_isOpen = false;

	// Detach the premix callback handler
	_mixer->setupPremix(0);
}

void MidiDriver_YM2612::send(uint32 b) {
	send(b & 0xF, b & 0xFFFFFFF0);
}

void MidiDriver_YM2612::send(byte chan, uint32 b) {
	//byte param3 = (byte) ((b >> 24) & 0xFF);
	byte param2 = (byte) ((b >> 16) & 0xFF);
	byte param1 = (byte) ((b >>  8) & 0xFF);
	byte cmd    = (byte) (b & 0xF0);
	if (chan > ARRAYSIZE(_channel))
		return;

	switch (cmd) {
	case 0x80:// Note Off
		_channel[chan]->noteOff(param1);
		break;
	case 0x90: // Note On
		_channel[chan]->noteOn(param1, param2);
		break;
	case 0xA0: // Aftertouch
		break; // Not supported.
	case 0xB0: // Control Change
		_channel[chan]->controlChange(param1, param2);
		break;
	case 0xC0: // Program Change
		_channel[chan]->programChange(param1);
		break;
	case 0xD0: // Channel Pressure
		break; // Not supported.
	case 0xE0: // Pitch Bend
		_channel[chan]->pitchBend((param1 | (param2 << 7)) - 0x2000);
		break;
	case 0xF0: // SysEx
		// We should never get here! SysEx information has to be
		// sent via high-level semantic methods.
		warning("MidiDriver_YM2612: Receiving SysEx command on a send() call");
		break;

	default:
		warning("MidiDriver_YM2612: Unknown send() command 0x%02X", cmd);
	}
}

void MidiDriver_YM2612::sysEx(byte *msg, uint16 length) {
	if (msg[0] != 0x7C || msg[1] >= ARRAYSIZE(_channel))
		return;
	_channel[msg[1]]->sysEx_customInstrument('EUP ', &msg[2]);
}

void MidiDriver_YM2612::generateSamples(int16 *data, int len) {
	memset(data, 0, 2 * sizeof(int16) * len);
	nextTick(data, len);
}

void MidiDriver_YM2612::nextTick(int16 *buf1, int buflen) {
	int *buf0 = (int *)buf1;

	int i;
	for (i = 0; i < ARRAYSIZE(_channel); i++)
		_channel[i]->nextTick(buf0, buflen);

	for (i = 0; i < buflen; ++i)
		buf1[i*2+1] = buf1[i*2] = ((buf0[i] * volume()) >> 10) & 0xffff;
}

void MidiDriver_YM2612::rate(uint16 r)
{
	int i;
	for (i = 0; i < ARRAYSIZE(_channel); i++)
		_channel[i]->rate(r);
}

void MidiDriver_YM2612::createLookupTables() {
	{
		int i;
		sintbl = new int [2048];
		for (i = 0; i < 2048; i++)
			sintbl[i] = (int)(0xffff * sin(i/2048.0*2.0*PI));
	}

	{
		int i;
		powtbl = new int [1025];
		for (i = 0; i <= 1024; i++)
			powtbl[i] = (int)(0x10000 * pow(2.0, (i-512)/512.0));
	}

	{
		int i;
		int block;

		static int fnum[] = {
			0x026a, 0x028f, 0x02b6, 0x02df,
			0x030b, 0x0339, 0x036a, 0x039e,
			0x03d5, 0x0410, 0x044e, 0x048f,
		};

		// (int)(880.0 * 256.0 * pow(2.0, (note-0x51)/12.0)); // ¥Ð¥¤¥¢¥¹ 256 ÇÜ
		// 0x45 ¤¬ 440Hz (a4)¡¢0x51 ¤¬ 880Hz (a5) ¤é¤·¤¤
		frequencyTable = new int [120];
		for (block = -1; block < 9; block++) {
			for (i = 0; i < 12; i++) {
				double freq = fnum[i] * (166400.0 / 3) * pow(2.0, block-21);
				frequencyTable[(block+1)*12+i] = (int)(256.0 * freq);
			}
		}

		keycodeTable = new int [120];
		// detune Î̤η׻»¤ä KS ¤Ë¤è¤ë rate ÊÑ´¹¤Ë»È¤¦¤ó¤¸¤ã¤Ê¤¤¤«¤Ê
		for (block = -1; block < 9; block++) {
			for (i = 0; i < 12; i++) {
				// see p.204
				int  f8 = (fnum[i] >>  7) & 1;
				int  f9 = (fnum[i] >>  8) & 1;
				int f10 = (fnum[i] >>  9) & 1;
				int f11 = (fnum[i] >> 10) & 1;
				int  n4 = f11;
				int  n3 = f11&(f10|f9|f8) | (~f11&f10&f9&f8);
				int note = n4*2 + n3;
				// see p.207
				keycodeTable[(block+1)*12+i] = block*4 + note;
			}
		}
	}

	{
		int freq;
		keyscaleTable = new int [8192];
		keyscaleTable[0] = 0;
		for (freq = 1; freq < 8192; freq++) {
			keyscaleTable[freq] = (int)(log((double)freq) / 9.03 * 32.0) - 1;
			// 8368[Hz] (o9c) ¤Ç 32¤¯¤é¤¤¡£9.03 =:= ln 8368
		}
	}

	{
		int i;
		attackOut = new int [1024];
		for (i = 0; i < 1024; i++)
			attackOut[i] = (int)(((0x7fff+0x03a5)*30.0) / (30.0+i)) - 0x03a5;
	}
}

////////////////////////////////////////
//
// MidiDriver_YM2612 factory
//
////////////////////////////////////////

MidiDriver *MidiDriver_YM2612_create(SoundMixer *mixer) {
	return new MidiDriver_YM2612(mixer);
}





More information about the Scummvm-git-logs mailing list