[Scummvm-cvs-logs] CVS: scummvm/sound/softsynth/mt32 .cvsignore,NONE,1.1 freeverb.cpp,NONE,1.1 freeverb.h,NONE,1.1 i386.cpp,NONE,1.1 i386.h,NONE,1.1 module.mk,NONE,1.1 mt32_file.cpp,NONE,1.1 mt32_file.h,NONE,1.1 mt32emu.h,NONE,1.1 part.cpp,NONE,1.1 part.h,NONE,1.1 partial.cpp,NONE,1.1 partial.h,NONE,1.1 partialManager.cpp,NONE,1.1 partialManager.h,NONE,1.1 structures.h,NONE,1.1 synth.cpp,NONE,1.1 synth.h,NONE,1.1 tables.cpp,NONE,1.1 tables.h,NONE,1.1

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


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

Added Files:
	.cvsignore freeverb.cpp freeverb.h i386.cpp i386.h module.mk 
	mt32_file.cpp mt32_file.h mt32emu.h part.cpp part.h 
	partial.cpp partial.h partialManager.cpp partialManager.h 
	structures.h synth.cpp synth.h tables.cpp tables.h 
Log Message:
Moved the softsynth midi drivers into a sound/softsynth; amongst other things, this fixes bug #1083058

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

--- NEW FILE: freeverb.cpp ---
/* ScummVM - Scumm Interpreter
 * Copyright (C) 2004 The ScummVM project
 * Copyright (C) 2000 Jezar at Dreampoint
 *
 * This code is public domain
 *
 * Parts of this code are:
 *
 * 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/freeverb.cpp,v 1.1 2004/12/25 18:34:44 fingolfin Exp $
 *
 */

// Comb filter implementation
//
// Written by 
// http://www.dreampoint.co.uk
// This code is public domain

#include "stdafx.h"
#include "sound/softsynth/mt32/freeverb.h"

comb::comb() {
	filterstore = 0;
	bufidx = 0;
}

void comb::setbuffer(float *buf, int size) {
	buffer = buf; 
	bufsize = size;
}

void comb::mute() {
	for (int i = 0; i < bufsize; i++)
		buffer[i] = 0;
}

void comb::setdamp(float val) {
	damp1 = val; 
	damp2 = 1 - val;
}

float comb::getdamp() {
	return damp1;
}

void comb::setfeedback(float val) {
	feedback = val;
}

float comb::getfeedback() {
	return feedback;
}

// Allpass filter implementation

allpass::allpass() {
	bufidx = 0;
}

void allpass::setbuffer(float *buf, int size) {
	buffer = buf; 
	bufsize = size;
}

void allpass::mute() {
	for (int i = 0; i < bufsize; i++)
		buffer[i] = 0;
}

void allpass::setfeedback(float val) {
	feedback = val;
}

float allpass::getfeedback() {
	return feedback;
}

// Reverb model implementation

revmodel::revmodel() {
	// Tie the components to their buffers
	combL[0].setbuffer(bufcombL1,combtuningL1);
	combR[0].setbuffer(bufcombR1,combtuningR1);
	combL[1].setbuffer(bufcombL2,combtuningL2);
	combR[1].setbuffer(bufcombR2,combtuningR2);
	combL[2].setbuffer(bufcombL3,combtuningL3);
	combR[2].setbuffer(bufcombR3,combtuningR3);
	combL[3].setbuffer(bufcombL4,combtuningL4);
	combR[3].setbuffer(bufcombR4,combtuningR4);
	combL[4].setbuffer(bufcombL5,combtuningL5);
	combR[4].setbuffer(bufcombR5,combtuningR5);
	combL[5].setbuffer(bufcombL6,combtuningL6);
	combR[5].setbuffer(bufcombR6,combtuningR6);
	combL[6].setbuffer(bufcombL7,combtuningL7);
	combR[6].setbuffer(bufcombR7,combtuningR7);
	combL[7].setbuffer(bufcombL8,combtuningL8);
	combR[7].setbuffer(bufcombR8,combtuningR8);
	allpassL[0].setbuffer(bufallpassL1,allpasstuningL1);
	allpassR[0].setbuffer(bufallpassR1,allpasstuningR1);
	allpassL[1].setbuffer(bufallpassL2,allpasstuningL2);
	allpassR[1].setbuffer(bufallpassR2,allpasstuningR2);
	allpassL[2].setbuffer(bufallpassL3,allpasstuningL3);
	allpassR[2].setbuffer(bufallpassR3,allpasstuningR3);
	allpassL[3].setbuffer(bufallpassL4,allpasstuningL4);
	allpassR[3].setbuffer(bufallpassR4,allpasstuningR4);

	// Set default values
	allpassL[0].setfeedback(0.5f);
	allpassR[0].setfeedback(0.5f);
	allpassL[1].setfeedback(0.5f);
	allpassR[1].setfeedback(0.5f);
	allpassL[2].setfeedback(0.5f);
	allpassR[2].setfeedback(0.5f);
	allpassL[3].setfeedback(0.5f);
	allpassR[3].setfeedback(0.5f);
	setwet(initialwet);
	setroomsize(initialroom);
	setdry(initialdry);
	setdamp(initialdamp);
	setwidth(initialwidth);
	setmode(initialmode);

	// Buffer will be full of rubbish - so we MUST mute them
	mute();
}

void revmodel::mute() {
	int i;

	if (getmode() >= freezemode)
		return;

	for (i = 0; i < numcombs; i++) {
		combL[i].mute();
		combR[i].mute();
	}

	for (i = 0; i < numallpasses; i++) {
		allpassL[i].mute();
		allpassR[i].mute();
	}
}

void revmodel::processreplace(float *inputL, float *inputR, float *outputL, float *outputR, long numsamples, int skip) {
	float outL, outR, input;

	while(numsamples-- > 0) {
		int i;

		outL = outR = 0;
		input = (*inputL + *inputR) * gain;

		// Accumulate comb filters in parallel
		for (i = 0; i < numcombs; i++) {
			outL += combL[i].process(input);
			outR += combR[i].process(input);
		}

		// Feed through allpasses in series
		for (i = 0; i < numallpasses; i++) {
			outL = allpassL[i].process(outL);
			outR = allpassR[i].process(outR);
		}

		// Calculate output REPLACING anything already there
		*outputL = outL * wet1 + outR * wet2 + *inputL * dry;
		*outputR = outR * wet1 + outL * wet2 + *inputR * dry;

		// Increment sample pointers, allowing for interleave (if any)
		inputL += skip;
		inputR += skip;
		outputL += skip;
		outputR += skip;
	}
}

void revmodel::processmix(float *inputL, float *inputR, float *outputL, float *outputR, long numsamples, int skip) {
	float outL, outR, input;

	while (numsamples-- > 0) {
		int i;

		outL = outR = 0;
		input = (*inputL + *inputR) * gain;

		// Accumulate comb filters in parallel
		for (i = 0; i < numcombs; i++) {
			outL += combL[i].process(input);
			outR += combR[i].process(input);
		}

		// Feed through allpasses in series
		for (i = 0; i < numallpasses; i++) {
			outL = allpassL[i].process(outL);
			outR = allpassR[i].process(outR);
		}

		// Calculate output MIXING with anything already there
		*outputL += outL * wet1 + outR * wet2 + *inputL * dry;
		*outputR += outR * wet1 + outL * wet2 + *inputR * dry;

		// Increment sample pointers, allowing for interleave (if any)
		inputL += skip;
		inputR += skip;
		outputL += skip;
		outputR += skip;
	}
}

void revmodel::update() {
	// Recalculate internal values after parameter change

	int i;

	wet1 = wet * (width / 2 + 0.5f);
	wet2 = wet * ((1 - width) / 2);

	if (mode >= freezemode) {
		roomsize1 = 1;
		damp1 = 0;
		gain = muted;
	} else {
		roomsize1 = roomsize;
		damp1 = damp;
		gain = fixedgain;
	}

	for (i = 0; i < numcombs; i++) {
		combL[i].setfeedback(roomsize1);
		combR[i].setfeedback(roomsize1);
	}

	for (i = 0; i < numcombs; i++) {
		combL[i].setdamp(damp1);
		combR[i].setdamp(damp1);
	}
}

// The following get/set functions are not inlined, because
// speed is never an issue when calling them, and also
// because as you develop the reverb model, you may
// wish to take dynamic action when they are called.

void revmodel::setroomsize(float value) {
	roomsize = (value * scaleroom) + offsetroom;
	update();
}

float revmodel::getroomsize() {
	return (roomsize - offsetroom) / scaleroom;
}

void revmodel::setdamp(float value) {
	damp = value * scaledamp;
	update();
}

float revmodel::getdamp() {
	return damp / scaledamp;
}

void revmodel::setwet(float value) {
	wet = value * scalewet;
	update();
}

float revmodel::getwet() {
	return wet / scalewet;
}

void revmodel::setdry(float value) {
	dry = value * scaledry;
}

float revmodel::getdry() {
	return dry / scaledry;
}

void revmodel::setwidth(float value) {
	width = value;
	update();
}

float revmodel::getwidth() {
	return width;
}

void revmodel::setmode(float value) {
	mode = value;
	update();
}

float revmodel::getmode() {
	if (mode >= freezemode)
		return 1;
	else
		return 0;
}

--- NEW FILE: freeverb.h ---
/* ScummVM - Scumm Interpreter
 * Copyright (C) 2004 The ScummVM project
 * Copyright (C) 2000 Jezar at Dreampoint
 *
 * This code is public domain
 *
 * Parts of this code are:
 *
 * 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/freeverb.h,v 1.1 2004/12/25 18:34:44 fingolfin Exp $
 *
 */

// Macro for killing denormalled numbers
//
// Written by Jezar at Dreampoint, June 2000
// http://www.dreampoint.co.uk
// Based on IS_DENORMAL macro by Jon Watte
// This code is public domain

#ifndef FREEVERB_H
#define FREEVERB_H

#define undenormalise(sample) if (((*(unsigned int*)&sample) & 0x7f800000) == 0) sample = 0.0f

// Comb filter class declaration

class comb {
public:
	comb();
	void setbuffer(float *buf, int size);
	inline float process(float inp);
	void mute();
	void setdamp(float val);
	float getdamp();
	void setfeedback(float val);
	float getfeedback();
private:
	float feedback;
	float filterstore;
	float damp1;
	float damp2;
	float *buffer;
	int bufsize;
	int bufidx;
};


// Big to inline - but crucial for speed

inline float comb::process(float input) {
	float output;

	output = buffer[bufidx];
	undenormalise(output);

	filterstore = (output * damp2) + (filterstore * damp1);
	undenormalise(filterstore);

	buffer[bufidx] = input + (filterstore * feedback);

	if (++bufidx >= bufsize)
		bufidx = 0;

	return output;
}

// Allpass filter declaration

class allpass {
public:
	allpass();
	void setbuffer(float *buf, int size);
	inline float process(float inp);
	void mute();
	void setfeedback(float val);
	float getfeedback();
private:
	float feedback;
	float *buffer;
	int bufsize;
	int bufidx;
};


// Big to inline - but crucial for speed

inline float allpass::process(float input) {
	float output;
	float bufout;
	
	bufout = buffer[bufidx];
	undenormalise(bufout);
	
	output = -input + bufout;
	buffer[bufidx] = input + (bufout * feedback);

	if (++bufidx >= bufsize)
		bufidx = 0;

	return output;
}


// Reverb model tuning values

const int	numcombs	= 8;
const int	numallpasses	= 4;
const float	muted		= 0;
const float	fixedgain	= 0.015f;
const float	scalewet	= 3;
const float	scaledry	= 2;
const float	scaledamp	= 0.4f;
const float	scaleroom	= 0.28f;
const float	offsetroom	= 0.7f;
const float	initialroom	= 0.5f;
const float	initialdamp	= 0.5f;
const float	initialwet	= 1 / scalewet;
const float	initialdry	= 0;
const float	initialwidth	= 1;
const float	initialmode	= 0;
const float	freezemode	= 0.5f;
const int	stereospread	= 23;

// These values assume 44.1KHz sample rate
// they will probably be OK for 48KHz sample rate
// but would need scaling for 96KHz (or other) sample rates.
// The values were obtained by listening tests.
const int combtuningL1		= 1116;
const int combtuningR1		= 1116 + stereospread;
const int combtuningL2		= 1188;
const int combtuningR2		= 1188 + stereospread;
const int combtuningL3		= 1277;
const int combtuningR3		= 1277 + stereospread;
const int combtuningL4		= 1356;
const int combtuningR4		= 1356 + stereospread;
const int combtuningL5		= 1422;
const int combtuningR5		= 1422 + stereospread;
const int combtuningL6		= 1491;
const int combtuningR6		= 1491 + stereospread;
const int combtuningL7		= 1557;
const int combtuningR7		= 1557 + stereospread;
const int combtuningL8		= 1617;
const int combtuningR8		= 1617 + stereospread;
const int allpasstuningL1	= 556;
const int allpasstuningR1	= 556 + stereospread;
const int allpasstuningL2	= 441;
const int allpasstuningR2	= 441 + stereospread;
const int allpasstuningL3	= 341;
const int allpasstuningR3	= 341 + stereospread;
const int allpasstuningL4	= 225;
const int allpasstuningR4	= 225 + stereospread;


// Reverb model declaration

class revmodel {
public:
	revmodel();
	void mute();
	void processmix(float *inputL, float *inputR, float *outputL, float *outputR, long numsamples, int skip);
	void processreplace(float *inputL, float *inputR, float *outputL, float *outputR, long numsamples, int skip);
	void setroomsize(float value);
	float getroomsize();
	void setdamp(float value);
	float getdamp();
	void setwet(float value);
	float getwet();
	void setdry(float value);
	float getdry();
	void setwidth(float value);
	float getwidth();
	void setmode(float value);
	float getmode();
private:
	void update();

	float gain;
	float roomsize, roomsize1;
	float damp, damp1;
	float wet, wet1, wet2;
	float dry;
	float width;
	float mode;

	// The following are all declared inline 
	// to remove the need for dynamic allocation
	// with its subsequent error-checking messiness

	// Comb filters
	comb combL[numcombs];
	comb combR[numcombs];

	// Allpass filters
	allpass	allpassL[numallpasses];
	allpass	allpassR[numallpasses];

	// Buffers for the combs
	float bufcombL1[combtuningL1];
	float bufcombR1[combtuningR1];
	float bufcombL2[combtuningL2];
	float bufcombR2[combtuningR2];
	float bufcombL3[combtuningL3];
	float bufcombR3[combtuningR3];
	float bufcombL4[combtuningL4];
	float bufcombR4[combtuningR4];
	float bufcombL5[combtuningL5];
	float bufcombR5[combtuningR5];
	float bufcombL6[combtuningL6];
	float bufcombR6[combtuningR6];
	float bufcombL7[combtuningL7];
	float bufcombR7[combtuningR7];
	float bufcombL8[combtuningL8];
	float bufcombR8[combtuningR8];

	// Buffers for the allpasses
	float bufallpassL1[allpasstuningL1];
	float bufallpassR1[allpasstuningR1];
	float bufallpassL2[allpasstuningL2];
	float bufallpassR2[allpasstuningR2];
	float bufallpassL3[allpasstuningL3];
	float bufallpassR3[allpasstuningR3];
	float bufallpassL4[allpasstuningL4];
	float bufallpassR4[allpasstuningR4];
};

#endif

--- NEW FILE: i386.cpp ---
/* Copyright (c) 2003-2004 Various contributors
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 */

#include "mt32emu.h"

#ifdef MT32EMU_HAVE_X86

namespace MT32Emu {

#ifndef _MSC_VER

#define eflag(value) __asm__ __volatile__("pushfl \n popfl \n" : : "a"(value))
#define cpuid_flag  (1 << 21)

static inline bool atti386_DetectCPUID() {
	unsigned int result;

	// Is there a cpuid?
	result = cpuid_flag; // set test
	eflag(result);
	if (!(result & cpuid_flag))
		return false;

	result = 0; // clear test
	eflag(result);
	if (result & cpuid_flag)
		return false;

	return true;
}

static inline bool atti386_DetectSIMD() {
	unsigned int result;

	if (atti386_DetectCPUID() == false)
		return false;

	/* check cpuid */
	__asm__ __volatile__(
		"movl   $1, %%eax        \n" \
		"cpuid                   \n" \
		"movl   %%edx, %0        \n" \
		: "=r"(result) : : "eax", "ebx", "ecx", "edx");

	if (result & (1 << 25))
		return true;

	return false;
}

static inline bool atti386_Detect3DNow() {
	unsigned int result;

	if (atti386_DetectCPUID() == false)
		return false;

	// get cpuid
	__asm__ __volatile__(
		"movl   $0x80000001, %%eax \n" \
		"cpuid                     \n" \
		"movl   %%edx, %0          \n" \
		: "=r"(result) : : "eax", "ebx", "ecx", "edx");

	if (result & 0x80000000)
		return true;

	return false;
}


static inline float atti386_iir_filter_sse(float *output, float *hist1_ptr, float *coef_ptr) {
	__asm__  __volatile__ (
		"pushl  %1                      \n" \
		"pushl  %2                      \n" \
		"movss  0(%0), %%xmm1           \n" \
		"movups 0(%1), %%xmm2           \n" \
		"movlps 0(%2), %%xmm3           \n" \
		"                               \n" \
		"shufps $0x44, %%xmm3, %%xmm3   \n" \
		"                               \n" \
		"mulps  %%xmm3, %%xmm2          \n" \
		"                               \n" \
		"subss  %%xmm2, %%xmm1          \n" \
		"shufps $0x39,  %%xmm2, %%xmm2  \n" \
		"subss  %%xmm2, %%xmm1          \n" \
		"                               \n" \
		"movss  %%xmm1, 0(%2)           \n" \
		"                               \n" \
		"shufps $0x39,  %%xmm2, %%xmm2  \n" \
		"addss  %%xmm2, %%xmm1          \n" \
		"                               \n" \
		"shufps $0x39,  %%xmm2, %%xmm2  \n" \
		"addss  %%xmm2, %%xmm1          \n" \
		"                               \n" \
		"movss  %%xmm3, 4(%2)           \n" \
		"                               \n" \
		"addl   $16, %1                 \n" \
		"addl   $8, %2                  \n" \
		"                               \n" \
		"movups 0(%1), %%xmm2           \n" \
		"                               \n" \
		"movlps 0(%2), %%xmm3           \n" \
		"shufps $0x44, %%xmm3, %%xmm3   \n" \
		"                               \n" \
		"mulps %%xmm3, %%xmm2           \n" \
		"                               \n" \
		"subss  %%xmm2, %%xmm1          \n" \
		"shufps $0x39,  %%xmm2, %%xmm2  \n" \
		"subss  %%xmm2, %%xmm1          \n" \
		"                               \n" \
		"movss %%xmm1, 0(%2)            \n" \
		"                               \n" \
		"shufps $0x39, %%xmm2, %%xmm2   \n" \
		"addss %%xmm2, %%xmm1           \n" \
		"                               \n" \
		"shufps $0x39, %%xmm2, %%xmm2   \n" \
		"addss %%xmm2, %%xmm1           \n" \
		"                               \n" \
		"movss %%xmm3, 4(%2)            \n" \
		"movss %%xmm1, 0(%0)            \n" \
		"popl %2                        \n" \
		"popl %1                        \n" \
		: : "r"(output), "r"(coef_ptr), "r"(hist1_ptr)
		: "xmm1", "xmm2", "xmm3", "memory");

	return *output;
}

static inline float atti386_iir_filter_3DNow(float output, float *hist1_ptr, float *coef_ptr) {
	float tmp;

	__asm__ __volatile__ (
		"movq %0, %%mm1       \n" \
		"                     \n" \
		"movl  %1, %%ebx      \n" \
		"movq 0(%%ebx), %%mm2 \n" \
		"                     \n" \
		"movl %2, %%eax;      \n" \
		"movq 0(%%eax), %%mm3 \n" \
		"                     \n" \
		"pfmul %%mm3, %%mm2   \n" \
		"pfsub %%mm2, %%mm1   \n" \
		"                     \n" \
		"psrlq $32, %%mm2     \n" \
		"pfsub %%mm2, %%mm1   \n" \
		"                     \n" \
		"movd %%mm1, %3       \n" \
		"                     \n" \
		"addl  $8, %%ebx      \n" \
		"movq 0(%%ebx), %%mm2 \n" \
		"movq 0(%%eax), %%mm3 \n" \
		"                     \n" \
		"pfmul %%mm3, %%mm2   \n" \
		"pfadd %%mm2, %%mm1   \n" \
		"                     \n" \
		"psrlq $32, %%mm2     \n" \
		"pfadd %%mm2, %%mm1   \n" \
		"                     \n" \
		"pushl %3             \n" \
		"popl 0(%%eax)        \n" \
		"                     \n" \
		"movd %%mm3, 4(%%eax) \n" \
		"                     \n" \
		"addl $8, %%ebx       \n" \
		"addl $8, %%eax       \n" \
		"                     \n" \
		"movq 0(%%ebx), %%mm2 \n" \
		"movq 0(%%eax), %%mm3 \n" \
		"                     \n" \
		"pfmul %%mm3, %%mm2   \n" \
		"pfsub %%mm2, %%mm1   \n" \
		"                     \n" \
		"psrlq $32, %%mm2     \n" \
		"pfsub %%mm2, %%mm1   \n" \
		"                     \n" \
		"movd %%mm1, %3       \n" \
		"                     \n" \
		"addl $8, %%ebx       \n" \
		"movq 0(%%ebx), %%mm2 \n" \
		"movq 0(%%eax), %%mm3 \n" \
		"                     \n" \
		"pfmul %%mm3, %%mm2   \n" \
		"pfadd %%mm2, %%mm1   \n" \
		"                     \n" \
		"psrlq $32, %%mm2     \n" \
		"pfadd %%mm2, %%mm1   \n" \
		"                     \n" \
		"pushl %3             \n" \
		"popl 0(%%eax)        \n" \
		"movd %%mm3, 4(%%eax) \n" \
		"                     \n" \
		"movd %%mm1, %0       \n" \
		"femms                \n" \
		: "=m"(output) : "g"(coef_ptr), "g"(hist1_ptr), "m"(tmp)
		: "eax", "ebx", "mm1", "mm2", "mm3", "memory");

	return output;
}

static inline void atti386_produceOutput1(int tmplen, Bit16s myvolume, Bit16s *useBuf, Bit16s *snd) {
	__asm__ __volatile__(
		"movl %0,  %%ecx      \n" \
		"movw %1,  %%ax       \n" \
		"shll $16, %%eax      \n" \
		"movw %1,  %%ax       \n" \
		"movd %%eax, %%mm3    \n" \
		"movd %%eax, %%mm2    \n" \
		"psllq $32, %%mm3     \n" \
		"por %%mm2, %%mm3     \n" \
		"movl %2, %%esi       \n" \
		"movl %3, %%edi       \n" \
		"1:                   \n" \
		"movq 0(%%esi), %%mm1 \n" \
		"movq 0(%%edi), %%mm2 \n" \
		"pmulhw %%mm3, %%mm1  \n" \
		"paddw %%mm2, %%mm1   \n" \
		"movq %%mm1, 0(%%edi) \n" \
		"                     \n" \
		"addl $8, %%esi       \n" \
		"addl $8, %%edi       \n" \
		"                     \n" \
		"decl %%ecx           \n" \
		"cmpl $0, %%ecx       \n" \
		"jg   1b              \n" \
		"emms                 \n" \
		: : "g"(tmplen), "g"(myvolume), "g"(useBuf), "g"(snd)
		: "eax", "ecx", "edi", "esi", "mm1", "mm2", "mm3", "memory");
}

static inline void atti386_produceOutput2(Bit32u len, Bit16s *snd, float *sndbufl, float *sndbufr, float *multFactor) {
	__asm__ __volatile__(
		"movl  %4, %%ecx         \n" \
		"shrl  $1, %%ecx         \n" \
		"addl  $4, %%ecx         \n" \
		"pushl %%ecx             \n" \
		"                        \n" \
		"movl %0, %%esi          \n" \
		"movups 0(%%esi), %%xmm1 \n" \
		"                        \n" \
		"movl %1, %%esi          \n" \
		"movl %2, %%edi          \n" \
		"1:                      \n" \
		"xorl %%eax, %%eax       \n" \
		"movw 0(%1), %%ax        \n" \
		"cwde                    \n" \
		"incl %1                 \n" \
		"incl %1                 \n" \
		"movd  %%eax, %%mm1      \n" \
		"psrlq $32, %%mm1        \n" \
		"movw 0(%1), %%ax        \n" \
		"incl %1                 \n" \
		"incl %1                 \n" \
		"movd %%eax, %%mm2       \n" \
		"por %%mm2, %%mm1        \n" \
		"                        \n" \
		"decl %%ecx              \n" \
		"jnz 1b                  \n" \
		"                        \n" \
		"popl %%ecx              \n" \
		"movl %1, %%esi          \n" \
		"movl %3, %%edi          \n" \
		"incl %%esi              \n" \
		"2:                      \n" \
		"decl %%ecx              \n" \
		"jnz 2b                  \n" \
		: : "g"(multFactor), "r"(snd), "g"(sndbufl), "g"(sndbufr), "g"(len)
		: "eax", "ecx", "edi", "esi", "mm1", "mm2", "xmm1", "memory");
}

static inline void atti386_mixBuffers(Bit16s * buf1, Bit16s *buf2, int len) {
	__asm__ __volatile__(
		"movl %0, %%ecx       \n" \
		"movl %1, %%esi       \n" \
		"movl %2, %%edi       \n" \
		"1:                   \n" \
		"movq 0(%%edi), %%mm1 \n" \
		"movq 0(%%esi), %%mm2 \n" \
		"paddw %%mm2, %%mm1   \n" \
		"movq %%mm1, 0(%%esi) \n" \
		"addl $8, %%edi       \n" \
		"addl $8, %%esi       \n" \
		"decl %%ecx           \n" \
		"cmpl $0, %%ecx       \n" \
		"jg   1b              \n" \
		"emms                 \n" \
		: : "g"(len), "g"(buf1), "g"(buf2)
		: "ecx", "edi", "esi", "mm1", "mm2", "memory");
}

static inline void atti386_mixBuffersRingMix(Bit16s * buf1, Bit16s *buf2, int len) {
	__asm__ __volatile__(
		"movl %0, %%ecx       \n" \
		"movl %1, %%esi       \n" \
		"movl %2, %%edi       \n" \
		"1:                   \n" \
		"movq 0(%%esi), %%mm1 \n" \
		"movq 0(%%edi), %%mm2 \n" \
		"movq %%mm1, %%mm3    \n" \
		"pmulhw %%mm2, %%mm1  \n" \
		"paddw %%mm3, %%mm1   \n" \
		"movq %%mm1, 0(%%esi) \n" \
		"addl $8, %%edi       \n" \
		"addl $8, %%esi       \n" \
		"decl %%ecx           \n" \
		"cmpl $0, %%ecx       \n" \
		"jg   1b              \n" \
		"emms                 \n" \
		: : "g"(len), "g"(buf1), "g"(buf2)
		: "ecx", "edi", "esi", "mm1", "mm2", "mm3", "memory");
}

static inline void atti386_mixBuffersRing(Bit16s * buf1, Bit16s *buf2, int len) {
	__asm__ __volatile__(
		"movl %0, %%ecx       \n" \
		"movl %1, %%esi       \n" \
		"movl %2, %%edi       \n" \
		"1:                   \n" \
		"movq 0(%%esi), %%mm1 \n" \
		"movq 0(%%edi), %%mm2 \n" \
		"pmulhw %%mm2, %%mm1  \n" \
		"movq %%mm1, 0(%%esi) \n" \
		"addl $8, %%edi       \n" \
		"addl $8, %%esi       \n" \
		"decl %%ecx           \n" \
		"cmpl $0, %%ecx       \n" \
		"jg   1b              \n" \
		"emms                 \n" \
		: : "g"(len), "g"(buf1), "g"(buf2)
		: "ecx", "edi", "esi", "mm1", "mm2", "memory");
}

static inline void atti386_partialProductOutput(int quadlen, Bit16s leftvol, Bit16s rightvol, Bit16s *partialBuf, Bit16s *p1buf) {
	__asm__ __volatile__(
		"movl %0, %%ecx       \n" \
		"movw %1, %%ax        \n" \
		"shll $16, %%eax      \n" \
		"movw %2, %%ax        \n" \
		"movd %%eax, %%mm1    \n" \
		"movd %%eax, %%mm2    \n" \
		"psllq $32, %%mm1     \n" \
		"por  %%mm2, %%mm1    \n" \
		"movl %3, %%edi       \n" \
		"movl %4, %%esi       \n" \
		"1:                   \n" \
		"movw 0(%%esi), %%bx  \n" \
		"addl $2, %%esi       \n" \
		"movw 0(%%esi), %%dx  \n" \
		"addl $2, %%esi       \n" \
		""                        \
		"movw %%dx, %%ax      \n" \
		"shll $16, %%eax      \n" \
		"movw %%dx, %%ax      \n" \
		"movd %%eax, %%mm2    \n" \
		"psllq $32, %%mm2     \n" \
		"movw %%bx, %%ax      \n" \
		"shll $16, %%eax      \n" \
		"movw %%bx, %%ax      \n" \
		"movd %%eax, %%mm3    \n" \
		"por  %%mm3, %%mm2    \n" \
		""                        \
		"pmulhw %%mm1, %%mm2  \n" \
		"movq %%mm2, 0(%%edi) \n" \
		"addl $8, %%edi       \n" \
		""                        \
		"decl %%ecx           \n" \
		"cmpl $0, %%ecx       \n" \
		"jg 1b                \n" \
		"emms                 \n"  \
		: : "g"(quadlen), "g"(leftvol), "g"(rightvol), "g"(partialBuf), "g"(p1buf)
		: "eax", "ebx", "ecx", "edx", "edi", "esi", "mm1", "mm2", "mm3", "memory");
}

#endif

bool DetectSIMD() {
#ifdef _MSC_VER
	bool found_simd;
	__asm {
		pushfd
		pop eax // get EFLAGS into eax
		mov ebx,eax // keep a copy
		xor eax,0x200000
		// toggle CPUID bit

		push eax
		popfd // set new EFLAGS
		pushfd
		pop eax // EFLAGS back into eax

		xor eax,ebx
		// have we changed the ID bit?

		je NO_SIMD
		// No, no CPUID instruction

		// we could toggle the
		// ID bit so CPUID is present
		mov eax,1

		cpuid // get processor features
		test edx,1<<25 // check the SIMD bit
		jz NO_SIMD
		mov found_simd,1
		jmp DONE
		NO_SIMD:
		mov found_simd,0
		DONE:
	}
	return found_simd;
#else
	return atti386_DetectSIMD();
#endif
}

bool Detect3DNow() {
#ifdef _MSC_VER
	bool found3D = false;
	__asm {
		pushfd
		pop eax
		mov edx, eax
		xor eax, 00200000h
		push eax
		popfd
		pushfd
		pop eax
		xor eax, edx
		jz NO_3DNOW

		mov eax, 80000000h
		cpuid

		cmp eax, 80000000h
		jbe NO_3DNOW

		mov eax, 80000001h
		cpuid
		test edx, 80000000h
		jz NO_3DNOW
		mov found3D, 1
NO_3DNOW:

	}
	return found3D;
#else
	return atti386_Detect3DNow();
#endif
}

float iir_filter_sse(float input,float *hist1_ptr, float *coef_ptr, int revLevel) {
	float output;

	// 1st number of coefficients array is overall input scale factor, or filter gain
	output = input * (*coef_ptr++);

#ifdef _MSC_VER
	__asm {

		movss xmm1,	output

		mov eax, coef_ptr
		movups xmm2, [eax]

		mov eax, hist1_ptr
		movlps xmm3, [eax]
		shufps xmm3, xmm3, 44h
		// hist1_ptr+1, hist1_ptr, hist1_ptr+1, hist1_ptr

		mulps xmm2, xmm3

		subss xmm1, xmm2
		// Rotate elements right
		shufps xmm2, xmm2, 39h
		subss xmm1, xmm2

		// Store new_hist
		movss DWORD PTR [eax], xmm1

		// Rotate elements right
		shufps xmm2, xmm2, 39h
		addss xmm1, xmm2

		// Rotate elements right
		shufps xmm2, xmm2, 39h
		addss xmm1, xmm2

		// Store previous hist
		movss DWORD PTR [eax+4], xmm3

		add coef_ptr, 16
		add hist1_ptr, 8

		mov eax, coef_ptr
		movups xmm2, [eax]

		mov eax, hist1_ptr
		movlps xmm3, [eax]
		shufps xmm3, xmm3, 44h
		// hist1_ptr+1, hist1_ptr, hist1_ptr+1, hist1_ptr

		mulps xmm2, xmm3

		subss xmm1, xmm2
		// Rotate elements right
		shufps xmm2, xmm2, 39h
		subss xmm1, xmm2

		// Store new_hist
		movss DWORD PTR [eax], xmm1

		// Rotate elements right
		shufps xmm2, xmm2, 39h
		addss xmm1, xmm2

		// Rotate elements right
		shufps xmm2, xmm2, 39h
		addss xmm1, xmm2

		// Store previous hist
		movss DWORD PTR [eax+4], xmm3

		movss output, xmm1
	}
#else
	output = atti386_iir_filter_sse(&output, hist1_ptr, coef_ptr);
#endif
	output *= ResonInv[revLevel];
	return output;
}

float iir_filter_3dnow(float input,float *hist1_ptr, float *coef_ptr, int revLevel) {
	float output;

	// 1st number of coefficients array is overall input scale factor, or filter gain
	output = input * (*coef_ptr++);

	// I find it very sad that 3DNow requires twice as many instructions as Intel's SSE
	// Intel does have the upper hand here.
#ifdef _MSC_VER
	float tmp;
	__asm {
		movq mm1, output
		mov ebx, coef_ptr
		movq mm2, [ebx]

		mov eax, hist1_ptr;
		movq mm3, [eax]

		pfmul mm2, mm3
		pfsub mm1, mm2

		psrlq mm2, 32
		pfsub mm1, mm2

		// Store new hist
		movd tmp, mm1

		add ebx, 8
		movq mm2, [ebx]
		movq mm3, [eax]

		pfmul mm2, mm3
		pfadd mm1, mm2

		psrlq mm2, 32
		pfadd mm1, mm2

		push tmp
		pop DWORD PTR [eax]

		movd DWORD PTR [eax+4], mm3

		add ebx, 8
		add eax, 8

		movq mm2, [ebx]
		movq mm3, [eax]

		pfmul mm2, mm3
		pfsub mm1, mm2

		psrlq mm2, 32
		pfsub mm1, mm2

		// Store new hist
		movd tmp, mm1

		add ebx, 8
		movq mm2, [ebx]
		movq mm3, [eax]

		pfmul mm2, mm3
		pfadd mm1, mm2

		psrlq mm2, 32
		pfadd mm1, mm2

		push tmp
		pop DWORD PTR [eax]
		movd DWORD PTR [eax+4], mm3

		movd output, mm1

		femms
	}
#else
	output = atti386_iir_filter_3DNow(output, hist1_ptr, coef_ptr);
#endif
	output *= ResonInv[revLevel];
	return output;
}

#if MT32EMU_USE_MMX > 0

int i386_partialProductOutput(int len, Bit16s leftvol, Bit16s rightvol, Bit16s *partialBuf, Bit16s *mixedBuf) {
	int tmplen = len >> 1;
	if (tmplen == 0) {
		return 0;
	}
#ifdef _MSC_VER
	__asm {
		mov ecx,tmplen
		mov ax, leftvol
		shl eax,16
		mov ax, rightvol
		movd mm1, eax
		movd mm2, eax
		psllq mm1, 32
		por mm1, mm2
		mov edi, partialBuf
		mov esi, mixedBuf
mmxloop1:
		mov bx, [esi]
		add esi,2
		mov dx, [esi]
		add esi,2

		mov ax, dx
		shl eax, 16
		mov ax, dx
		movd mm2,eax
		psllq mm2, 32
		mov ax, bx
		shl eax, 16
		mov ax, bx
		movd mm3,eax
		por mm2,mm3

		pmulhw mm2, mm1
		movq [edi], mm2
		add edi, 8

		dec ecx
		cmp ecx,0
		jg mmxloop1
		emms
	}
#else
	atti386_partialProductOutput(tmplen, leftvol, rightvol, partialBuf, mixedBuf);
#endif
	return tmplen << 1;
}

int i386_mixBuffers(Bit16s * buf1, Bit16s *buf2, int len) {
	int tmplen = len >> 2;
	if (tmplen == 0) {
		return 0;
	}
#ifdef _MSC_VER
	__asm {
		mov ecx, tmplen
		mov esi, buf1
		mov edi, buf2

mixloop1:
		movq mm1, [edi]
		movq mm2, [esi]
		paddw mm1,mm2
		movq [esi],mm1
		add edi,8
		add esi,8

		dec ecx
		cmp ecx,0
		jg mixloop1
		emms
	}
#else
	atti386_mixBuffers(buf1, buf2, tmplen);
#endif
	return tmplen << 2;
}


int i386_mixBuffersRingMix(Bit16s * buf1, Bit16s *buf2, int len) {
	int tmplen = len >> 2;
	if (tmplen == 0) {
		return 0;
	}
#ifdef _MSC_VER
	__asm {
		mov ecx, tmplen
		mov esi, buf1
		mov edi, buf2

mixloop2:
		movq mm1, [esi]
		movq mm2, [edi]
		movq mm3, mm1
		pmulhw mm1, mm2
		paddw mm1,mm3
		movq [esi],mm1
		add edi,8
		add esi,8

		dec ecx
		cmp ecx,0
		jg mixloop2
		emms
	}
#else
	atti386_mixBuffersRingMix(buf1, buf2, tmplen);
#endif
	return tmplen << 2;
}

int i386_mixBuffersRing(Bit16s * buf1, Bit16s *buf2, int len) {
	int tmplen = len >> 2;
	if (tmplen == 0) {
		return 0;
	}
#ifdef _MSC_VER
	__asm {
		mov ecx, tmplen
		mov esi, buf1
		mov edi, buf2

mixloop3:
		movq mm1, [esi]
		movq mm2, [edi]
		pmulhw mm1, mm2
		movq [esi],mm1
		add edi,8
		add esi,8

		dec ecx
		cmp ecx,0
		jg mixloop3
		emms
	}
#else
	atti386_mixBuffersRing(buf1, buf2, tmplen);
#endif
	return tmplen << 2;
}

int i386_produceOutput1(Bit16s *useBuf, Bit16s *stream, Bit32u len, Bit16s volume) {
	int tmplen = (len >> 1);
	if (tmplen == 0) {
		return 0;
	}
#ifdef _MSC_VER
	__asm {
		mov ecx, tmplen
		mov ax,volume
		shl eax,16
		mov ax,volume
		movd mm3,eax
		movd mm2,eax
		psllq mm3, 32
		por mm3,mm2
		mov esi, useBuf
		mov edi, stream
mixloop4:
		movq mm1, [esi]
		movq mm2, [edi]
		pmulhw mm1, mm3
		paddw mm1,mm2
		movq [edi], mm1

		add esi,8
		add edi,8

		dec ecx
		cmp ecx,0
		jg mixloop4
		emms
	}
#else
	atti386_produceOutput1(tmplen, volume, useBuf, stream);
#endif
	return tmplen << 1;
}

#endif

}

#endif

--- NEW FILE: i386.h ---
/* Copyright (c) 2003-2004 Various contributors
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 */

#ifndef MT32EMU_I386_H
#define MT32EMU_I386_H

namespace MT32Emu {
#ifdef MT32EMU_HAVE_X86

// Function that detects the availablity of SSE SIMD instructions
bool DetectSIMD();
// Function that detects the availablity of 3DNow instructions
bool Detect3DNow();

float iir_filter_sse(float input,float *hist1_ptr, float *coef_ptr, int revLevel);
float iir_filter_3dnow(float input,float *hist1_ptr, float *coef_ptr, int revLevel);
float iir_filter_normal(float input,float *hist1_ptr, float *coef_ptr, int revLevel);

#if MT32EMU_USE_MMX > 0
int i386_partialProductOutput(int len, Bit16s leftvol, Bit16s rightvol, Bit16s *partialBuf, Bit16s *mixedBuf);
int i386_mixBuffers(Bit16s * buf1, Bit16s *buf2, int len);
int i386_mixBuffersRingMix(Bit16s * buf1, Bit16s *buf2, int len);
int i386_mixBuffersRing(Bit16s * buf1, Bit16s *buf2, int len);
int i386_produceOutput1(Bit16s *useBuf, Bit16s *stream, Bit32u len, Bit16s volume);
#endif

#endif

}

#endif

--- NEW FILE: module.mk ---
MODULE := sound/softsynth/mt32

MODULE_OBJS := \
	sound/softsynth/mt32/mt32_file.o \
	sound/softsynth/mt32/i386.o \
	sound/softsynth/mt32/part.o \
	sound/softsynth/mt32/partial.o \
	sound/softsynth/mt32/partialManager.o \
	sound/softsynth/mt32/synth.o \
	sound/softsynth/mt32/tables.o \
	sound/softsynth/mt32/freeverb.o

MODULE_DIRS += \
	sound/softsynth/mt32

# Include common rules 
include $(srcdir)/common.rules

--- NEW FILE: mt32_file.cpp ---
/* Copyright (c) 2003-2004 Various contributors
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 */

#include <stdio.h>

#include "mt32emu.h"

namespace MT32Emu {

	bool ANSIFile::open(const char *filename, OpenMode mode) {
		const char *fmode;
		if (mode == OpenMode_read) {
			fmode = "rb";
		} else {
			fmode = "wb";
		}
		fp = fopen(filename, fmode);
		return (fp != NULL);
	}

	void ANSIFile::close() {
		fclose(fp);
	}

	size_t ANSIFile::read(void *in, size_t size) {
		return fread(in, 1, size, fp);
	}

	bool ANSIFile::readLine(char *in, size_t size) {
		return fgets(in, (int)size, fp) != NULL;
	}

	bool ANSIFile::readBit8u(Bit8u *in) {
		int c = fgetc(fp);
		if (c == EOF)
			return false;
		*in = (Bit8u)c;
		return true;
	}

	bool File::readBit16u(Bit16u *in) {
		Bit8u b[2];
		if (read(&b[0], 2) != 2)
			return false;
		*in = ((b[0] << 8) | b[1]);
		return true;
	}

	bool File::readBit32u(Bit32u *in) {
		Bit8u b[4];
		if (read(&b[0], 4) != 4)
			return false;
		*in = ((b[0] << 24) | (b[1] << 16) | (b[2] << 8) | b[3]);
		return true;
	}

	size_t ANSIFile::write(const void *out, size_t size) {
		return fwrite(out, 1, size, fp);
	}

	bool ANSIFile::writeBit8u(Bit8u out) {
		return fputc(out, fp) != EOF;
	}

	bool File::writeBit16u(Bit16u out) {
		if (!writeBit8u((Bit8u)((out & 0xFF00) >> 8))) {
			return false;
		}
		if (!writeBit8u((Bit8u)(out & 0x00FF))) {
			return false;
		}
		return true;
	}

	bool File::writeBit32u(Bit32u out) {
		if (!writeBit8u((Bit8u)((out & 0xFF000000) >> 24))) {
			return false;
		}
		if (!writeBit8u((Bit8u)((out & 0x00FF0000) >> 16))) {
			return false;
		}
		if (!writeBit8u((Bit8u)((out & 0x0000FF00) >> 8))) {
			return false;
		}
		if (!writeBit8u((Bit8u)(out & 0x000000FF))) {
			return false;
		}
		return true;
	}

	bool ANSIFile::isEOF() {
		return feof(fp) != 0;
	}
}

--- NEW FILE: mt32_file.h ---
/* Copyright (c) 2003-2004 Various contributors
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 */

#ifndef MT32EMU_FILE_H
#define MT32EMU_FILE_H

#include <stdio.h>

namespace MT32Emu {

class File {
public:
	enum OpenMode {
		OpenMode_read  = 0,
		OpenMode_write = 1
	};
	virtual ~File() {}
	virtual void close() = 0;
	virtual size_t read(void *in, size_t size) = 0;
	virtual bool readLine(char *in, size_t size) = 0;
	virtual bool readBit8u(Bit8u *in) = 0;
	virtual bool readBit16u(Bit16u *in);
	virtual bool readBit32u(Bit32u *in);
	virtual size_t write(const void *out, size_t size) = 0;
	virtual bool writeBit8u(Bit8u out) = 0;
	// Note: May write a single byte to the file before failing
	virtual bool writeBit16u(Bit16u out);
	// Note: May write some (<4) bytes to the file before failing
	virtual bool writeBit32u(Bit32u out);
	virtual bool isEOF() = 0;
};

class ANSIFile: public File {
private:
	FILE *fp;
public:
	bool open(const char *filename, OpenMode mode);
	void close();
	size_t read(void *in, size_t size);
	bool readLine(char *in, size_t size);
	bool readBit8u(Bit8u *in);
	size_t write(const void *out, size_t size);
	bool writeBit8u(unsigned char out);
	bool isEOF();
};

}

#endif

--- NEW FILE: mt32emu.h ---
/* Copyright (c) 2003-2004 Various contributors
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 */

#ifndef MT32EMU_MT32EMU_H
#define MT32EMU_MT32EMU_H

// Debugging
// Show the instruments played
#define MT32EMU_MONITOR_INSTRUMENTS 1
// Shows number of partials MT-32 is playing, and on which parts
#define MT32EMU_MONITOR_PARTIALS 0
// Determines how the waveform cache file is handled (must be regenerated after sampling rate change)
#define MT32EMU_WAVECACHEMODE 0 // Load existing cache if possible, otherwise generate and save cache
//#define MT32EMU_WAVECACHEMODE 1 // Load existing cache if possible, otherwise generage but don't save cache
//#define MT32EMU_WAVECACHEMODE 2 // Ignore existing cache, generate and save cache
//#define MT32EMU_WAVECACHEMODE 3 // Ignore existing cache, generate but don't save cache

// Configuration
// The maximum number of partials playing simultaneously
#define MT32EMU_MAX_PARTIALS 32
// The maximum number of notes playing simultaneously per part.
// No point making it more than MT32EMU_MAX_PARTIALS, since each note needs at least one partial.
#define MT32EMU_MAX_POLY 32
// This calculates the exact frequencies of notes as they are played, instead of offsetting from pre-cached semitones. Potentially very slow.
#define MT32EMU_ACCURATENOTES 0

#if (defined (_MSC_VER) && defined(_M_IX86))
#define MT32EMU_HAVE_X86
#elif  defined(__GNUC__)
#if __GNUC__ >= 3 && defined(__i386__)
#define MT32EMU_HAVE_X86
#endif
#endif

#ifdef MT32EMU_HAVE_X86
#define MT32EMU_USE_MMX 1
#else
#define MT32EMU_USE_MMX 0
#endif

#include "freeverb.h"

#include "structures.h"
#include "i386.h"
#include "mt32_file.h"
#include "tables.h"
#include "partial.h"
#include "partialManager.h"
#include "part.h"
#include "synth.h"

#endif

--- NEW FILE: part.cpp ---
/* Copyright (c) 2003-2004 Various contributors
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 */

#include <string.h>
#include <math.h>

#include "mt32emu.h"

namespace MT32Emu {

static const Bit8u PartialStruct[13] = {
	0, 0, 2, 2, 1, 3,
	3, 0, 3, 0, 2, 1, 3 };

static const Bit8u PartialMixStruct[13] = {
	0, 1, 0, 1, 1, 0,
	1, 3, 3, 2, 2, 2, 2 };

static const float floatKeyfollow[17] = {
	-1.0f, -1.0f/2.0f, -1.0f/4.0f, 0.0f,
	1.0f/8.0f, 1.0f/4.0f, 3.0f/8.0f, 1.0f/2.0f, 5.0f/8.0f, 3.0f/4.0f, 7.0f/8.0f, 1.0f,
	5.0f/4.0f, 3.0f/2.0f, 2.0f,
	1.0009765625f, 1.0048828125f
};

//FIXME:KG: Put this dpoly stuff somewhere better
bool dpoly::isActive() const {
	return partials[0] != NULL || partials[1] != NULL || partials[2] != NULL || partials[3] != NULL;
}

Bit32u dpoly::getAge() const {
	for (int i = 0; i < 4; i++) {
		if (partials[i] != NULL) {
			return partials[i]->age;
		}
	}
	return 0;
}

RhythmPart::RhythmPart(Synth *useSynth, unsigned int usePartNum): Part(useSynth, usePartNum) {
	strcpy(name, "Rhythm");
	rhythmTemp = &synth->mt32ram.rhythmSettings[0];
	refresh();
}

Part::Part(Synth *useSynth, unsigned int usePartNum) {
	this->synth = useSynth;
	this->partNum = usePartNum;
	patchCache[0].dirty = true;
	holdpedal = false;
	if (usePartNum == 8) {
		// Nasty hack for rhythm
		patchTemp = NULL;
		timbreTemp = NULL;
	} else {
		sprintf(name, "Part %d", partNum + 1);
		patchTemp = &synth->mt32ram.patchSettings[partNum];
		timbreTemp = &synth->mt32ram.timbreSettings[partNum];
	}
	currentInstr[0] = 0;
	currentInstr[10] = 0;
	volume = voltable[102]; //FIXME:KG: Original was just volume=102; I assume this is intended
	volumesetting.leftvol = 32767;
	volumesetting.rightvol = 32767;
	bend = 0.0f;
	memset(polyTable,0,sizeof(polyTable));
	memset(patchCache, 0, sizeof(patchCache));
}

void Part::setHoldPedal(bool pedalval) {
	if (holdpedal && !pedalval)
		stopPedalHold();
	holdpedal = pedalval;
}

void RhythmPart::setBend(unsigned int midiBend) {
	synth->printDebug("%s: Setting bend (%d) not supported on rhythm", name, midiBend);
	return;
}

void Part::setBend(unsigned int midiBend) {
	// FIXME:KG: Slightly unbalanced increments, but I wanted min -1.0, centre 0.0 and max 1.0
	if (midiBend <= 0x2000) {
		bend = ((signed int)midiBend - 0x2000) / (float)0x2000;
	} else {
		bend = ((signed int)midiBend - 0x2000) / (float)0x1FFF;
	}
	// Loop through all partials to update their bend
	for (int i = 0; i < MT32EMU_MAX_POLY; i++) {
		for (int j = 0; j < 4; j++) {
			if (polyTable[i].partials[j] != NULL) {
				polyTable[i].partials[j]->setBend(bend);
			}
		}
	}
}

void RhythmPart::setModulation(unsigned int midiModulation) {
	synth->printDebug("%s: Setting modulation (%d) not supported on rhythm", name, midiModulation);
}

void Part::setModulation(unsigned int midiModulation) {
	// Just a bloody guess, as always, before I get things figured out
	for (int t = 0; t < 4; t++) {
		if (patchCache[t].playPartial) {
			int newrate = (patchCache[t].modsense * midiModulation) >> 7;
			//patchCache[t].lfoperiod = lfotable[newrate];
			patchCache[t].lfodepth = newrate;
			//FIXME:KG: timbreTemp->partial[t].lfo.depth =
		}
	}
}

void RhythmPart::refresh() {
	// (Re-)cache all the mapped timbres ahead of time
	for (unsigned int drumNum = 0; drumNum < 64; drumNum++) {
		int drumTimbreNum = rhythmTemp[drumNum].timbre;
		if (drumTimbreNum >= 94)
			continue;
		Bit16s pan = rhythmTemp[drumNum].panpot; // They use R-L 0-14...
		// FIXME:KG: Panning cache should be backed up to partials using it, too
		// FIXME:KG: If I don't have left/right mixed up here, it's pure luck
		if (pan < 7) {
			drumPan[drumNum].leftvol = 32767;
			drumPan[drumNum].rightvol = pan * 4681;
		} else {
			drumPan[drumNum].rightvol = 32767;
			drumPan[drumNum].leftvol = (14 - pan) * 4681;
		}
		PatchCache *cache = drumCache[drumNum];
		backupCacheToPartials(cache);
		for (int t = 0; t < 4; t++) {
			// Common parameters, stored redundantly
			cache[t].dirty = true;
			cache[t].pitchShift = 0.0f;
			cache[t].benderRange = 0.0f;
			cache[t].pansetptr = &drumPan[drumNum];
			cache[t].reverb = rhythmTemp[drumNum].reverbSwitch > 0;
		}
	}
}

void Part::refresh() {
	backupCacheToPartials(patchCache);
	for (int t = 0; t < 4; t++) {
		// Common parameters, stored redundantly
		patchCache[t].dirty = true;
		patchCache[t].pitchShift = (patchTemp->patch.keyShift - 24) + (patchTemp->patch.fineTune - 50) / 100.0f;
		patchCache[t].benderRange = patchTemp->patch.benderRange;
		patchCache[t].pansetptr = &volumesetting;
		patchCache[t].reverb = patchTemp->patch.reverbSwitch > 0;
	}
	memcpy(currentInstr, timbreTemp->common.name, 10);
}

void RhythmPart::refreshTimbre(unsigned int absTimbreNum) {
	for (int m = 0; m < 64; m++) {
		if (rhythmTemp[m].timbre == absTimbreNum - 128)
			drumCache[m][0].dirty = true;
	}
}

void Part::refreshTimbre(unsigned int absTimbreNum) {
	if (getAbsTimbreNum() == absTimbreNum) {
		memcpy(currentInstr, timbreTemp->common.name, 10);
		patchCache[0].dirty = true;
	}
}

int Part::fixBiaslevel(int srcpnt, int *dir) {
	int noteat = srcpnt & 0x3F;
	int outnote;
	if (srcpnt < 64)
		*dir = 0;
	else
		*dir = 1;
	outnote = 33 + noteat;
	//synth->printDebug("Bias note %d, dir %d", outnote, *dir);

	return outnote;
}

int Part::fixKeyfollow(int srckey) {
	if (srckey>=0 && srckey<=16) {
		int keyfix[17] = { -256*16, -128*16, -64*16, 0, 32*16, 64*16, 96*16, 128*16, (128+32)*16, 192*16, (192+32)*16, 256*16, (256+64)*16, (256+128)*16, (512)*16, 4100, 4116};
		return keyfix[srckey];
	} else {
		//LOG(LOG_ERROR|LOG_MISC,"Missed key: %d", srckey);
		return 256;
	}
}

void Part::abortPoly(dpoly *poly) {
	if (!poly->isPlaying) {
		return;
	}
	for (int i = 0; i < 4; i++) {
		Partial *partial = poly->partials[i];
		if (partial != NULL) {
			partial->deactivate();
		}
	}
	poly->isPlaying = false;
}

void Part::setPatch(const PatchParam *patch) {
	patchTemp->patch = *patch;
}

void Part::setTimbre(TimbreParam *timbre) {
	*timbreTemp = *timbre;
}

unsigned int RhythmPart::getAbsTimbreNum() const {
	synth->printDebug("%s: Attempted to call getAbsTimbreNum() - doesn't make sense for rhythm");
	return 0;
}

unsigned int Part::getAbsTimbreNum() const {
	return (patchTemp->patch.timbreGroup * 64) + patchTemp->patch.timbreNum;
}

void RhythmPart::setProgram(unsigned int patchNum) {
	synth->printDebug("%s: Attempt to set program (%d) on rhythm is invalid", name, patchNum);
}

void Part::setProgram(unsigned int patchNum) {
	setPatch(&synth->mt32ram.patches[patchNum]);
	setTimbre(&synth->mt32ram.timbres[getAbsTimbreNum()].timbre);
#if 0
	// Immediately stop all partials on this part (this is apparently *not* the correct behaviour)
	for (int m = 0; m < MT32EMU_MAX_POLY; m++) {
		AbortPoly(poly);
	}
#endif

	refresh();

	allStop(); //FIXME:KG: Is this correct?
}

void Part::backupCacheToPartials(PatchCache cache[4]) {
	// check if any partials are still playing with the old patch cache
	// if so then duplicate the cached data from the part to the partial so that
	// we can change the part's cache without affecting the partial.
	// We delay this until now to avoid a copy operation with every note played
	for (int m = 0; m < MT32EMU_MAX_POLY; m++) {
		for (int i = 0; i < 4; i++) {
			Partial *partial = polyTable[m].partials[i];
			if (partial != NULL && partial->patchCache == &cache[i]) {
				partial->cachebackup = cache[i];
				partial->patchCache = &partial->cachebackup;
			}
		}
	}
}

void Part::cacheTimbre(PatchCache cache[4], const TimbreParam *timbre) {
	backupCacheToPartials(cache);
	int partialCount = 0;
	for (int t = 0; t < 4; t++) {
		if (((timbre->common.pmute >> t) & 0x1) == 1) {
			cache[t].playPartial = true;
			partialCount++;
		} else {
			cache[t].playPartial = false;
			continue;
		}

		// Calculate and cache common parameters

		cache[t].pcm = timbre->partial[t].wg.pcmwave;
		cache[t].useBender = (timbre->partial[t].wg.bender == 1);

		switch (t) {
		case 0:
			cache[t].PCMPartial = (PartialStruct[(int)timbre->common.pstruct12] & 0x2) ? true : false;
			cache[t].structureMix = PartialMixStruct[(int)timbre->common.pstruct12];
			cache[t].structurePosition = 0;
			cache[t].structurePair = 1;
			break;
		case 1:
			cache[t].PCMPartial = (PartialStruct[(int)timbre->common.pstruct12] & 0x1) ? true : false;
			cache[t].structureMix = PartialMixStruct[(int)timbre->common.pstruct12];
			cache[t].structurePosition = 1;
			cache[t].structurePair = 0;
			break;
		case 2:
			cache[t].PCMPartial = (PartialStruct[(int)timbre->common.pstruct34] & 0x2) ? true : false;
			cache[t].structureMix = PartialMixStruct[(int)timbre->common.pstruct34];
			cache[t].structurePosition = 0;
			cache[t].structurePair = 3;
			break;
		case 3:
			cache[t].PCMPartial = (PartialStruct[(int)timbre->common.pstruct34] & 0x1) ? true : false;
			cache[t].structureMix = PartialMixStruct[(int)timbre->common.pstruct34];
			cache[t].structurePosition = 1;
			cache[t].structurePair = 2;
			break;
		default:
			break;
		}

		cache[t].waveform = timbre->partial[t].wg.waveform;
		cache[t].pulsewidth = timbre->partial[t].wg.pulsewid;
		cache[t].pwsens = timbre->partial[t].wg.pwvelo;
		if (timbre->partial[t].wg.keyfollow > 16) {
			synth->printDebug("Bad keyfollow value in timbre!");
			cache[t].pitchKeyfollow = 1.0f;
		} else {
			cache[t].pitchKeyfollow = floatKeyfollow[timbre->partial[t].wg.keyfollow];
		}

		cache[t].pitch = timbre->partial[t].wg.coarse + (timbre->partial[t].wg.fine - 50) / 100.0f + 24.0f;
		cache[t].pitchEnv = timbre->partial[t].env;
		cache[t].pitchEnv.sensitivity = (char)((float)cache[t].pitchEnv.sensitivity * 1.27f);
		cache[t].pitchsustain = cache[t].pitchEnv.level[3];

		// Calculate and cache TVA envelope stuff
		cache[t].ampEnv = timbre->partial[t].tva;
		for (int i = 0; i < 4; i++)
			cache[t].ampEnv.envlevel[i] = (char)((float)cache[t].ampEnv.envlevel[i] * 1.27f);
		cache[t].ampEnv.level = (char)((float)cache[t].ampEnv.level * 1.27f);
		float tvelo = ((float)timbre->partial[t].tva.velosens / 100.0f);
		float velo = fabs(tvelo-0.5f) * 2.0f;
		velo *= 63.0f;
		cache[t].ampEnv.velosens = (char)velo;
		if (tvelo<0.5f)
			cache[t].ampenvdir = 1;
		else
			cache[t].ampenvdir = 0;

		cache[t].ampbias[0] = fixBiaslevel(cache[t].ampEnv.biaspoint1, &cache[t].ampdir[0]);
		cache[t].ampblevel[0] = 12 - cache[t].ampEnv.biaslevel1;
		cache[t].ampbias[1] = fixBiaslevel(cache[t].ampEnv.biaspoint2, &cache[t].ampdir[1]);
		cache[t].ampblevel[1] = 12 - cache[t].ampEnv.biaslevel2;
		cache[t].ampdepth = cache[t].ampEnv.envvkf * cache[t].ampEnv.envvkf;
		cache[t].ampsustain = cache[t].ampEnv.envlevel[3];
		cache[t].amplevel = cache[t].ampEnv.level;

		// Calculate and cache filter stuff
		cache[t].filtEnv = timbre->partial[t].tvf;
		cache[t].tvfdepth = cache[t].filtEnv.envdkf;
		cache[t].filtkeyfollow  = fixKeyfollow(cache[t].filtEnv.keyfollow);
		cache[t].filtEnv.envdepth = (char)((float)cache[t].filtEnv.envdepth * 1.27);
		cache[t].tvfbias = fixBiaslevel(cache[t].filtEnv.biaspoint, &cache[t].tvfdir);
		cache[t].tvfblevel = cache[t].filtEnv.biaslevel;
		cache[t].filtsustain  = cache[t].filtEnv.envlevel[3];

		// Calculate and cache LFO stuff
		cache[t].lfodepth = timbre->partial[t].lfo.depth;
		cache[t].lfoperiod = lfotable[(int)timbre->partial[t].lfo.rate];
		cache[t].lforate = timbre->partial[t].lfo.rate;
		cache[t].modsense = timbre->partial[t].lfo.modsense;
	}
	for (int t = 0; t < 4; t++) {
		// Common parameters, stored redundantly
		cache[t].dirty = false;
		cache[t].partialCount = partialCount;
		cache[t].sustain = (timbre->common.nosustain == 0);
	}
	//synth->printDebug("Res 1: %d 2: %d 3: %d 4: %d", cache[0].waveform, cache[1].waveform, cache[2].waveform, cache[3].waveform);

#if MT32EMU_MONITOR_INSTRUMENTS == 1
	synth->printDebug("%s (%s): Recached timbre", name, currentInstr);
	for (int i = 0; i < 4; i++) {
		synth->printDebug(" %d: play=%s, pcm=%s (%d), wave=%d", i, cache[i].playPartial ? "YES" : "NO", cache[i].PCMPartial ? "YES" : "NO", timbre->partial[i].wg.pcmwave, timbre->partial[i].wg.waveform);
	}
#endif
}

const char *Part::getName() const {
	return name;
}

void Part::setVolume(int vol) {
	volume = voltable[vol];
}

void RhythmPart::setPan(unsigned int midiPan)
{
	// FIXME:KG: This is unchangeable for drums (they always use drumPan), is that correct?
	synth->printDebug("%s: Setting pan (%d) not supported on rhythm", name, midiPan);
}

void Part::setPan(unsigned int midiPan) {
	// FIXME:KG: Tweaked this a bit so that we have a left 100%, centre and right 100%
	// (But this makes the range somewhat skewed)
	if (midiPan < 64) {
		volumesetting.leftvol = 32767;
		volumesetting.rightvol = (Bit16s)(midiPan * 512);
	} else if (midiPan == 64) {
		volumesetting.leftvol = 32767;
		volumesetting.rightvol = 32767;
	} else {
		volumesetting.rightvol = 32767;
		volumesetting.leftvol = (Bit16s)((127 - midiPan) * 520);
	}
	//synth->printDebug("%s (%s): Set pan to %d", name, currentInstr, panpot);
}

void RhythmPart::playNote(unsigned int key, int vel) {
	if (key < 24 || key > 87) {
		synth->printDebug("%s: Attempted to play invalid key %d", name, key);
		return;
	}
	int drumNum = key - 24;
	int drumTimbreNum = rhythmTemp[drumNum].timbre;
	if (drumTimbreNum >= 94) {
		synth->printDebug("%s: Attempted to play unmapped key %d", name, key);
		return;
	}
	int absTimbreNum = drumTimbreNum + 128;
	TimbreParam *timbre = &synth->mt32ram.timbres[absTimbreNum].timbre;
	memcpy(currentInstr, timbre->common.name, 10);
#if MT32EMU_MONITOR_INSTRUMENTS == 1
	synth->printDebug("%s (%s): starting poly (drum %d, timbre %d) - Vel %d Key %d Vol %d", name, currentInstr, drumNum, absTimbreNum, vel, key, volume);
#endif
	if (drumCache[drumNum][0].dirty) {
		cacheTimbre(drumCache[drumNum], timbre);
	}
	playPoly(drumCache[drumNum], key, MIDDLEC, vel);
}

void Part::playNote(unsigned int key, int vel) {
	int freqNum = key;
	if (freqNum < 12) {
		synth->printDebug("%s (%s): Attempted to play invalid key %d < 12; moving up by octave", name, currentInstr, key);
		freqNum += 12;
	} else if (freqNum > 108) {
		synth->printDebug("%s (%s): Attempted to play invalid key %d > 108; moving down by octave", name, currentInstr, key);
		while (freqNum > 108) {
			freqNum -= 12;
		}
	}
	// POLY1 mode, Single Assign
	// Haven't found any software that uses any of the other poly modes
	// FIXME:KG: Should this also apply to rhythm?
	for (unsigned int i = 0; i < MT32EMU_MAX_POLY; i++) {
		if (polyTable[i].isActive() && (polyTable[i].key == key)) {
			//AbortPoly(&polyTable[i]);
			stopNote(key);
			break;
		}
	}
#if MT32EMU_MONITOR_INSTRUMENTS == 1
	synth->printDebug("%s (%s): starting poly - Vel %d Key %d Vol %d", name, currentInstr, vel, key, volume);
#endif
	if (patchCache[0].dirty) {
		cacheTimbre(patchCache, timbreTemp);
	}
	playPoly(patchCache, key, freqNum, vel);
}

void Part::playPoly(const PatchCache cache[4], unsigned int key, int freqNum, int vel) {
	unsigned int needPartials = cache[0].partialCount;
	unsigned int freePartials = synth->partialManager->getFreePartialCount();

	if (freePartials < needPartials) {
		if (!synth->partialManager->freePartials(needPartials - freePartials, partNum)) {
			synth->printDebug("%s (%s): Insufficient free partials to play key %d (vel=%d); needed=%d, free=%d", name, currentInstr, key, vel, needPartials, synth->partialManager->getFreePartialCount());
			return;
		}
	}
	// Find free poly
	int m;
	for (m = 0; m < MT32EMU_MAX_POLY; m++) {
		if (!polyTable[m].isActive()) {
			break;
		}
	}
	if (m == MT32EMU_MAX_POLY) {
		synth->printDebug("%s (%s): No free poly to play key %d (vel %d)", name, currentInstr, key, vel);
		return;
	}

	dpoly *tpoly = &polyTable[m];

	tpoly->isPlaying = true;
	tpoly->key = key;
	tpoly->isDecay = false;
	tpoly->freqnum = freqNum;
	tpoly->vel = vel;
	tpoly->pedalhold = false;

	bool allnull = true;
	for (int x = 0; x < 4; x++) {
		if (cache[x].playPartial) {
			tpoly->partials[x] = synth->partialManager->allocPartial(partNum);
			allnull = false;
		} else {
			tpoly->partials[x] = NULL;
		}
	}

	if (allnull)
		synth->printDebug("%s (%s): No partials to play for this instrument", name, this->currentInstr);

	tpoly->sustain = cache[0].sustain;
	tpoly->volumeptr = &volume;

	for (int x = 0; x < 4; x++) {
		if (tpoly->partials[x] != NULL) {
			tpoly->partials[x]->startPartial(tpoly, &cache[x], tpoly->partials[cache[x].structurePair]);
			tpoly->partials[x]->setBend(bend);
		}
	}
}

static void startDecayPoly(dpoly *tpoly) {
	if (tpoly->isDecay) {
		return;
	}
	tpoly->isDecay = true;

	for (int t = 0; t < 4; t++) {
		Partial *partial = tpoly->partials[t];
		if (partial == NULL)
			continue;
		partial->startDecayAll();
	}
	tpoly->isPlaying = false;
}

void Part::allStop() {
	for (int q = 0; q < MT32EMU_MAX_POLY; q++) {
		dpoly *tpoly = &polyTable[q];
		if (tpoly->isPlaying) {
			startDecayPoly(tpoly);
		}
	}
}

void Part::stopPedalHold() {
	for (int q = 0; q < MT32EMU_MAX_POLY; q++) {
		dpoly *tpoly;
		tpoly = &polyTable[q];
		if (tpoly->isActive() && tpoly->pedalhold)
			stopNote(tpoly->key);
	}
}

void Part::stopNote(unsigned int key) {
	// Non-sustaining instruments ignore stop commands.
	// They die away eventually anyway

#if MT32EMU_MONITOR_INSTRUMENTS == 1
	synth->printDebug("%s (%s): stopping key %d", name, currentInstr, key);
#endif

	if (key != 255) {
		for (int q = 0; q < MT32EMU_MAX_POLY; q++) {
			dpoly *tpoly = &polyTable[q];
			if (tpoly->isPlaying && tpoly->key == key) {
				if (holdpedal)
					tpoly->pedalhold = true;
				else if (tpoly->sustain)
					startDecayPoly(tpoly);
			}
		}
		return;
	}

	// Find oldest poly... yes, the MT-32 can be reconfigured to kill different poly first
	// This is simplest
	int oldest = -1;
	Bit32u oldage = 0;

	for (int q = 0; q < MT32EMU_MAX_POLY; q++) {
		dpoly *tpoly = &polyTable[q];

		if (tpoly->isPlaying && !tpoly->isDecay) {
			if (tpoly->getAge() >= oldage) {
				oldage = tpoly->getAge();
				oldest = q;
			}
		}
	}

	if (oldest != -1) {
		startDecayPoly(&polyTable[oldest]);
	}
}

}

--- NEW FILE: part.h ---
/* Copyright (c) 2003-2004 Various contributors
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 */

#ifndef MT32EMU_PART_H
#define MT32EMU_PART_H

namespace MT32Emu {

class PartialManager;
class Synth;

class Part {
private:
	// Pointers to the areas of the MT-32's memory dedicated to this part (for parts 1-8)
	MemParams::PatchTemp *patchTemp;
	TimbreParam *timbreTemp;

	// 0=Part 1, .. 7=Part 8, 8=Rhythm
	unsigned int partNum;

	bool holdpedal;

	StereoVolume volumesetting;

	PatchCache patchCache[4];

	float bend; // -1.0 .. +1.0 

	dpoly polyTable[MT32EMU_MAX_POLY];

	void abortPoly(dpoly *poly);

	static int fixKeyfollow(int srckey);
	static int fixBiaslevel(int srcpnt, int *dir);

	void setPatch(const PatchParam *patch);

protected:
	Synth *synth;
	char name[8]; // "Part 1".."Part 8", "Rhythm"
	char currentInstr[11];
	Bit32u volume;
	void backupCacheToPartials(PatchCache cache[4]);
	void cacheTimbre(PatchCache cache[4], const TimbreParam *timbre);
	void playPoly(const PatchCache cache[4], unsigned int key, int freqNum, int vel);
	const char *getName() const;

public:
	Part(Synth *synth, unsigned int usePartNum);
	virtual void playNote(unsigned int key, int vel);
	void stopNote(unsigned int key);
	void allStop();
	void setVolume(int vol);
	virtual void setPan(unsigned int midiPan);
	virtual void setBend(unsigned int midiBend);
	virtual void setModulation(unsigned int midiModulation);
	virtual void setProgram(unsigned int patchNum);
	void setHoldPedal(bool pedalval);
	void stopPedalHold();
	virtual void refresh();
	virtual void refreshTimbre(unsigned int absTimbreNum);
	void setTimbre(TimbreParam *timbre);
	virtual unsigned int getAbsTimbreNum() const;
};

class RhythmPart: public Part {
	// Pointer to the area of the MT-32's memory dedicated to rhythm
	const MemParams::RhythmTemp *rhythmTemp;

	// This caches the timbres/settings in use by the rhythm part
	PatchCache drumCache[64][4];
	StereoVolume drumPan[64];
public:
	RhythmPart(Synth *synth, unsigned int usePartNum);
	void refreshTimbre(unsigned int timbreNum);
	void refresh();
	void playNote(unsigned int key, int vel);
	unsigned int getAbsTimbreNum() const;
	void setPan(unsigned int midiPan);
	void setBend(unsigned int midiBend);
	void setModulation(unsigned int midiModulation);
	void setProgram(unsigned int patchNum);
};

}
#endif

--- NEW FILE: partial.cpp ---
/* Copyright (c) 2003-2004 Various contributors
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 */

#include <stdlib.h>
#include <math.h>
#include <string.h>

#include "mt32emu.h"

#define FIXEDPOINT_UDIV(x, y, point) (((x) << (point)) / ((y)))
#define FIXEDPOINT_SDIV(x, y, point) (((x) * (1 << point)) / ((y)))
#define FIXEDPOINT_UMULT(x, y, point) (((x) * (y)) >> point)
#define FIXEDPOINT_SMULT(x, y, point) (((x) * (y)) / (1 << point))

using namespace MT32Emu;

Partial::Partial(Synth *useSynth) {
	this->synth = useSynth;
	ownerPart = -1;
	poly = NULL;
	pair = NULL;
#if MT32EMU_ACCURATENOTES == 1
	for (int i = 0; i < 3; i++) {
		noteLookupStorage.waveforms[i] = new Bit16s[65536];
	}
	noteLookup = ¬eLookupStorage;
#endif
}

Partial::~Partial() {
#if MT32EMU_ACCURATENOTES == 1
	for (int i = 0; i < 3; i++) {
		delete[] noteLookupStorage.waveforms[i];
	}
#endif
}

int Partial::getOwnerPart() {
	return ownerPart;
}

bool Partial::isActive() {
	return ownerPart > -1;
}

void Partial::activate(int part) {
	// This just marks the partial as being assigned to a part
	ownerPart = part;
}

void Partial::deactivate() {
	ownerPart = -1;
	if (poly != NULL) {
		for (int i = 0; i < 4; i++) {
			if (poly->partials[i] == this) {
				poly->partials[i] = NULL;
				break;
			}
		}
		if (pair != NULL) {
			pair->pair = NULL;
		}
	}
}

void Partial::initKeyFollow(int key) {
	// Setup partial keyfollow
	// Note follow relative to middle C

	// Calculate keyfollow for pitch
#if 1
	float rel = key == -1 ? 0.0f : (key - MIDDLEC);
	float newPitch = rel * patchCache->pitchKeyfollow + patchCache->pitch + patchCache->pitchShift;
	//FIXME:KG: Does it truncate the keyfollowed pitch to a semitone (towards MIDDLEC)?
	//int newKey = (int)(rel * patchCache->pitchKeyfollow);
	//float newPitch = newKey + patchCache->pitch + patchCache->pitchShift;
#else
	float rel = key == -1 ? 0.0f : (key + patchCache->pitchShift - MIDDLEC);
	float newPitch = rel * patchCache->pitchKeyfollow + patchCache->pitch;
#endif
#if MT32EMU_ACCURATENOTES == 1
	noteVal = newPitch;
	synth->printDebug("key=%d, pitch=%f, pitchKeyfollow=%f, pitchShift=%f, newPitch=%f", key, patchCache->pitch, patchCache->pitchKeyfollow, patchCache->pitchShift, newPitch);
#else
	float newPitchInt;
	float newPitchFract = modff(newPitch, &newPitchInt);
	if (newPitchFract > 0.5f) {
		newPitchInt += 1.0f;
		newPitchFract -= 1.0f;
	}
	noteVal = (int)newPitchInt;
	fineShift = (int)(powf(2.0f, newPitchFract / 12.0f) * 4096.0f);
	synth->printDebug("key=%d, pitch=%f, pitchKeyfollow=%f, pitchShift=%f, newPitch=%f, noteVal=%d, fineShift=%d", key, patchCache->pitch, patchCache->pitchKeyfollow, patchCache->pitchShift, newPitch, noteVal, fineShift);
#endif
	// FIXME:KG: Raise/lower by octaves until in the supported range.
	while (noteVal > HIGHEST_NOTE) // FIXME:KG: see tables.cpp: >108?
		noteVal -= 12;
	while (noteVal < LOWEST_NOTE) // FIXME:KG: see tables.cpp: <12?
		noteVal += 12;
	// Calculate keyfollow for filter
	int keyfollow = ((key - MIDDLEC) * patchCache->filtkeyfollow) / 4096;
	if (keyfollow > 108)
		keyfollow = 108;
	if (keyfollow < -108)
		keyfollow = -108;
	filtVal = keytable[keyfollow + 108];
	realVal = keytable[(key - MIDDLEC) + 108];
}

void Partial::startPartial(dpoly *usePoly, const PatchCache *useCache, Partial *pairPartial) {
	if (usePoly == NULL || useCache == NULL) {
		synth->printDebug("*** Error: Starting partial for owner %d, usePoly=%s, useCache=%s", ownerPart, usePoly == NULL ? "*** NULL ***" : "OK", useCache == NULL ? "*** NULL ***" : "OK");
		return;
	}
	patchCache = useCache;
	poly = usePoly;
	mixType = patchCache->structureMix;
	structurePosition = patchCache->structurePosition;

	play = true;
	initKeyFollow(poly->freqnum); // Initialises noteVal, filtVal and realVal
#if MT32EMU_ACCURATENOTES == 0
	noteLookup = &noteLookups[noteVal - LOWEST_NOTE];
#else
	TableInitialiser::initNote(synth, &noteLookupStorage, noteVal, (float)synth->myProp.sampleRate, synth->masterTune, synth->PCMList, NULL);
#endif

	lfoPos = 0;
	pulsewidth = patchCache->pulsewidth + pwveltable[patchCache->pwsens][poly->vel];
	if (pulsewidth > 100) {
		pulsewidth = 100;
	} else if (pulsewidth < 0) {
		pulsewidth = 0;
	}

	for (int e = 0; e < 3; e++) {
		envs[e].envpos = 0;
		envs[e].envstat = -1;
		envs[e].envbase = 0;
		envs[e].envdist = 0;
		envs[e].envsize = 0;
		envs[e].sustaining = false;
		envs[e].decaying = false;
		envs[e].prevlevel = 0;
		envs[e].counter = 0;
		envs[e].count = 0;
	}
	ampEnvVal = 0;
	pitchEnvVal = 0;
	pitchSustain = false;
	loopPos = 0;
	partialOff.pcmoffset = partialOff.pcmplace = 0;
	pair = pairPartial;
	useNoisePair = pairPartial == NULL && (mixType == 1 || mixType == 2);
	age = 0;
	alreadyOutputed = false;
	memset(history,0,sizeof(history));
}

Bit16s *Partial::generateSamples(long length) {
	if (!isActive() || alreadyOutputed) {
		return NULL;
	}
	if (poly == NULL) {
		synth->printDebug("*** ERROR: poly is NULL at Partial::generateSamples()!");
		return NULL;
	}

	alreadyOutputed = true;

	// Generate samples

	Bit16s *partialBuf = &myBuffer[0];
	while (length--) {
		Bit32s envval;
		Bit32s sample = 0;
		if (!envs[EnvelopeType_amp].sustaining) {
			if (envs[EnvelopeType_amp].count <= 0) {
				Bit32u ampval = getAmpEnvelope();
				if (!play) {
					deactivate();
					break;
				}
				if (ampval > 127) {
					ampval = 127;
				}

				ampval = voltable[ampval];
				int tmpvel;
				if (patchCache->ampenvdir == 1)
					tmpvel = 127 - poly->vel;
				else
					tmpvel = poly->vel;
				ampval = (ampval * ampveltable[tmpvel][(int)patchCache->ampEnv.velosens]) >> 8;
				//if (envs[EnvelopeType_amp].sustaining)
				ampEnvVal = ampval;
			}
			--envs[EnvelopeType_amp].count;
		}

		int lfoat = 0x1000;
		if (pitchSustain) {
			// Calculate LFO position
			// LFO does not kick in completely until pitch envelope sustains
			if (patchCache->lfodepth > 0) {
				lfoPos++;
				if (lfoPos >= patchCache->lfoperiod)
					lfoPos = 0;
				int lfoatm = FIXEDPOINT_UDIV(lfoPos, patchCache->lfoperiod, 16);
				int lfoatr = sintable[lfoatm];
				lfoat = lfoptable[patchCache->lfodepth][lfoatr];
			}
		} else {
			// Calculate Pitch envelope
			envval = getPitchEnvelope();
			int pd = patchCache->pitchEnv.depth;
			pitchEnvVal = penvtable[pd][envval];
		}

		int delta;
		// These two are only for PCM partials, obviously
		PCMWaveEntry *pcmWave = NULL; // Initialise to please compiler
		Bit32u pcmAddr = 0; // Initialise to please compiler

		// Wrap positions or end if necessary
		if (patchCache->PCMPartial) {
			// PCM partial
			int len;
			pcmWave = &synth->PCMList[patchCache->pcm];

			delta = noteLookup->wavTable[patchCache->pcm];
			pcmAddr = pcmWave->addr;
			len = pcmWave->len;
			if (partialOff.pcmplace >= len) {
				if (pcmWave->loop) {
					//partialOff.pcmplace = partialOff.pcmoffset = 0;
					partialOff.pcmplace %= len;
				} else {
					play = false;
					deactivate();
					break;
				}
			}
		} else {
			// Synthesis partial
			delta = 0x10707;
			partialOff.pcmplace %= (Bit16u)(noteLookup->div << 1);
		}

		// Build delta for position of next sample
		// Fix delta code
		Bit64u tdelta = (Bit64u)delta;
#if MT32EMU_ACCURATENOTES == 0
		tdelta = (tdelta * fineShift) >> 12;
#endif
		tdelta = (tdelta * pitchEnvVal) >> 12;
		tdelta = (tdelta * lfoat) >> 12;
		tdelta = (tdelta * bendShift) >> 12;
		delta = (int)tdelta;
		Bit32u volume = *poly->volumeptr;

		// Get waveform - either PCM or synthesized sawtooth or square
		if (ampEnvVal > 0) {
			if (patchCache->PCMPartial) {
				// Render PCM sample
				int ra, rb, dist;
				Bit32u taddr;
				if (delta < 0x10000) {
					// Linear sound interpolation
					taddr = pcmAddr + partialOff.pcmplace;
					ra = synth->romfile[taddr];
					taddr++;
					if (taddr == pcmAddr + pcmWave->len) {
						// Past end of PCM
						if (pcmWave->loop) {
							rb = synth->romfile[pcmAddr];
						} else {
							rb = 0;
						}
					} else {
						rb = synth->romfile[taddr];
					}
					dist = rb - ra;
					sample = (ra + ((dist * (Bit32s)(partialOff.pcmoffset >> 8)) >> 8));
				} else {
					// Sound decimation
					// The right way to do it is to use a lowpass filter on the waveform before selecting
					// a point.  This is too slow.  The following approximates this as fast as possible
					int idelta = delta >> 16;
					taddr = pcmAddr + partialOff.pcmplace;
					ra = synth->romfile[taddr++];
					for (int ix = 0; ix < idelta - 1; ix++) {
						if (taddr == pcmAddr + pcmWave->len) {
							// Past end of PCM
							if (pcmWave->loop) {
								taddr = pcmAddr;
							} else {
								// Behave as if all subsequent samples were 0
								break;
							}
						}
						ra += synth->romfile[taddr++];
					}
					sample = ra / idelta;
				}
			} else {
				// Render synthesised sample
				Bit32u div = noteLookup->div;
				int wf = patchCache->waveform;
				int toff = partialOff.pcmplace;
				int minorplace = partialOff.pcmoffset >> 14;

				Bit32s filtval = getFiltEnvelope();

				//synth->printDebug("Filtval: %d", filtval);

				if (wf==0) {
					// Square waveform.  Made by combining two pregenerated bandlimited
					// sawtooth waveforms
					// Pulse width is not yet correct
					if (div == 0) {
						synth->printDebug("ERROR: div=0 generating square wave, this should never happen!");
						div = 1;
					}
					Bit32u ofsA = toff % div;
					Bit32u ofsB = toff + FIXEDPOINT_UMULT(div, pulsetable[pulsewidth], 8);
					ofsB = ofsB % div;
					Bit16s pa = noteLookup->waveforms[0][(ofsA << 2) + minorplace];
					Bit16s pb = noteLookup->waveforms[0][(ofsB << 2) + minorplace];
					sample = (pa - pb) * 4;
					// Non-bandlimited squarewave
					/*
					ofs = ((div << 1) * pulsetable[patchCache->pulsewidth]) >> 8;
					if (toff < ofs)
						sample = 1 * WGAMP;
					else
						sample = -1 * WGAMP;
					*/
				} else {
					// Sawtooth.  Made by combining the full cosine and half cosine according
					// to how it looks on the MT-32.  What it really does it takes the
					// square wave and multiplies it by a full cosine
					int waveoff = (toff << 2) + minorplace;
					if (toff < noteLookup->sawTable[pulsewidth])
						sample = noteLookup->waveforms[1][waveoff % noteLookup->waveformSize[1]];
					else
						sample = noteLookup->waveforms[2][waveoff % noteLookup->waveformSize[2]];
					sample = sample * 4;
					// This is the correct way
					// Seems slow to me (though bandlimited) -- doesn't seem to
					// sound any better though
					/*
					//int pw = (patchCache->pulsewidth * pulsemod[filtval]) >> 8;

					Bit32u ofs = toff % div;

					Bit32u ofs3 = toff + FIXEDPOINT_UMULT(div, pulsetable[patchCache->pulsewidth], 8);
					ofs3 = ofs3 % div;

					pa = noteLookup->waveforms[0][ofs];
					pb = noteLookup->waveforms[0][ofs3];
					sample = ((pa - pb) * noteLookup->waveforms[2][toff]) / WGAMP;
					sample = sample *4;
					*/
				}

				//Very exact filter
				if (filtval > ((FILTERGRAN * 15) / 16))
					filtval = ((FILTERGRAN * 15) / 16);
				sample = (Bit32s)floor((synth->iirFilter)((float)sample, &history[0], filtcoeff[filtval][(int)patchCache->filtEnv.resonance], patchCache->filtEnv.resonance));
			}
		}

		// Add calculated delta to our waveform offset
		Bit32u absOff = ((partialOff.pcmplace << 16) | partialOff.pcmoffset);
		absOff += delta;
		partialOff.pcmplace = (Bit16u)((absOff & 0xFFFF0000) >> 16);
		partialOff.pcmoffset = (Bit16u)(absOff & 0xFFFF);

		// Put volume envelope over generated sample
		sample = FIXEDPOINT_SMULT(sample, ampEnvVal, 9);
		sample = FIXEDPOINT_SMULT(sample, volume, 7);
		envs[EnvelopeType_amp].envpos++;
		envs[EnvelopeType_pitch].envpos++;
		envs[EnvelopeType_filt].envpos++;

		*partialBuf++ = (Bit16s)sample;
	}
	// We may have deactivated and broken out of the loop before the end of the buffer,
	// if so then fill the remainder with 0s.
	if (++length > 0)
		memset(partialBuf, 0, length * 2);
	return &myBuffer[0];
}

void Partial::setBend(float factor) {
	if (!patchCache->useBender || factor == 0.0f) {
		bendShift = 4096;
		return;
	}
	// NOTE:KG: We can't do this smoothly with lookup tables, unless we use several MB.
	// FIXME:KG: Bend should be influenced by pitch key-follow too, according to docs.
	float bendSemitones = factor * patchCache->benderRange; // -24 .. 24
	float mult = powf(2.0f, bendSemitones / 12.0f);
	synth->printDebug("setBend(): factor=%f, benderRange=%f, semitones=%f, mult=%f\n", factor, patchCache->benderRange, bendSemitones, mult);
	bendShift = (int)(mult * 4096.0f);
}

Bit16s *Partial::mixBuffers(Bit16s * buf1, Bit16s *buf2, int len) {
	if (buf1 == NULL)
		return buf2;
	if (buf2 == NULL)
		return buf1;

	Bit16s *outBuf = buf1;
#if MT32EMU_USE_MMX >= 1
	// KG: This seems to be fine
	int donelen = i386_mixBuffers(buf1, buf2, len);
	len -= donelen;
	buf1 += donelen;
	buf2 += donelen;
#endif
	while (len--) {
		*buf1 = *buf1 + *buf2;
		buf1++, buf2++;
	}
	return outBuf;
}

Bit16s *Partial::mixBuffersRingMix(Bit16s * buf1, Bit16s *buf2, int len) {
	if (buf1 == NULL)
		return NULL;
	if (buf2 == NULL) {
		Bit16s *outBuf = buf1;
		while (len--) {
			if (*buf1 < -8192)
				*buf1 = -8192;
			else if (*buf1 > 8192)
				*buf1 = 8192;
			buf1++;
		}
		return outBuf;
	}

	Bit16s *outBuf = buf1;
#if MT32EMU_USE_MMX >= 1
	// KG: This seems to be fine
	int donelen = i386_mixBuffersRingMix(buf1, buf2, len);
	len -= donelen;
	buf1 += donelen;
	buf2 += donelen;
#endif
	while (len--) {
		float a, b;
		a = ((float)*buf1) / 8192.0f;
		b = ((float)*buf2) / 8192.0f;
		a = (a * b) + a;
		if (a>1.0)
			a = 1.0;
		if (a<-1.0)
			a = -1.0;
		*buf1 = (Bit16s)(a * 8192.0f);
		buf1++;
		buf2++;
		//buf1[i] = (Bit16s)(((Bit32s)buf1[i] * (Bit32s)buf2[i]) >> 10) + buf1[i];
	}
	return outBuf;
}

Bit16s *Partial::mixBuffersRing(Bit16s * buf1, Bit16s *buf2, int len) {
	if (buf1 == NULL) {
		return NULL;
	}
	if (buf2 == NULL) {
		return NULL;
	}

	Bit16s *outBuf = buf1;
#if MT32EMU_USE_MMX >= 1
	// FIXME:KG: Not really checked as working
	int donelen = i386_mixBuffersRing(buf1, buf2, len);
	len -= donelen;
	buf1 += donelen;
	buf2 += donelen;
#endif
	while (len--) {
		float a, b;
		a = ((float)*buf1) / 8192.0f;
		b = ((float)*buf2) / 8192.0f;
		a *= b;
		if (a>1.0)
			a = 1.0;
		if (a<-1.0)
			a = -1.0;
		*buf1 = (Bit16s)(a * 8192.0f);
		buf1++;
		buf2++;
	}
	return outBuf;
}

void Partial::mixBuffersStereo(Bit16s *buf1, Bit16s *buf2, Bit16s *outBuf, int len) {
	if (buf2 == NULL) {
		while (len--) {
			*outBuf++ = *buf1++;
			*outBuf++ = 0;
		}
	} else if (buf1 == NULL) {
		while (len--) {
			*outBuf++ = 0;
			*outBuf++ = *buf2++;
		}
	} else {
		while (len--) {
			*outBuf++ = *buf1++;
			*outBuf++ = *buf2++;
		}
	}
}

bool Partial::produceOutput(Bit16s *partialBuf, long length) {
	if (!isActive() || alreadyOutputed)
		return false;
	if (poly == NULL) {
		synth->printDebug("*** ERROR: poly is NULL at Partial::produceOutput()!");
		return false;
	}

	Bit16s *pairBuf = NULL;
	// Check for dependant partial
	if (pair != NULL) {
		if (!pair->alreadyOutputed) {
			// Note: pair may have become NULL after this
			pairBuf = pair->generateSamples(length);
		}
	} else if (useNoisePair) {
		// Generate noise for pairless ring mix
		pairBuf = smallnoise;
	}

	Bit16s *myBuf = generateSamples(length);

	if (myBuf == NULL && pairBuf == NULL)
		return false;

	Bit16s * p1buf, * p2buf;

	if (structurePosition == 0 || pairBuf == NULL) {
		p1buf = myBuf;
		p2buf = pairBuf;
	} else {
		p2buf = myBuf;
		p1buf = pairBuf;
	}

	//synth->printDebug("mixType: %d", mixType);

	Bit16s *mixedBuf;
	switch(mixType) {
		case 0:
			// Standard sound mix
			mixedBuf = mixBuffers(p1buf, p2buf, length);
			break;

		case 1:
			// Ring modulation with sound mix
			mixedBuf = mixBuffersRingMix(p1buf, p2buf, length);
			break;

		case 2:
			// Ring modulation alone
			mixedBuf = mixBuffersRing(p1buf, p2buf, length);
			break;

		case 3:
			// Stereo mixing.  One partial to one speaker channel, one to another.
			// FIXME:KG: Surely we should be multiplying by the left/right volumes here?
			mixBuffersStereo(p1buf, p2buf, partialBuf, length);
			return true;

		default:
			mixedBuf = mixBuffers(p1buf, p2buf, length);
			break;
	}

	if (mixedBuf == NULL)
		return false;

	Bit16s leftvol, rightvol;
	leftvol = patchCache->pansetptr->leftvol;
	rightvol = patchCache->pansetptr->rightvol;

#if MT32EMU_USE_MMX >= 2
	// FIXME:KG: This appears to introduce crackle
	int donelen = i386_partialProductOutput(length, leftvol, rightvol, partialBuf, mixedBuf);
	length -= donelen;
	mixedBuf += donelen;
	partialBuf += donelen * 2;
#endif
	while (length--) {
		*partialBuf++ = (Bit16s)(((Bit32s)*mixedBuf * (Bit32s)leftvol) >> 16);
		*partialBuf++ = (Bit16s)(((Bit32s)*mixedBuf * (Bit32s)rightvol) >> 16);
		mixedBuf++;
	}
	return true;
}

Bit32s Partial::getFiltEnvelope() {
	int reshigh;

	int cutoff,depth,keyfollow, realfollow;

	EnvelopeStatus *tStat  = &envs[EnvelopeType_filt];

	keyfollow = filtVal;
	realfollow = realVal;

	if (tStat->decaying) {
		reshigh = tStat->envbase;
		reshigh = (reshigh + ((tStat->envdist * tStat->envpos) / tStat->envsize));
		if (tStat->envpos >= tStat->envsize)
			reshigh = 0;
	} else {
		if (tStat->envstat==4) {
			reshigh = patchCache->filtsustain;
			if (!poly->sustain) {
				startDecay(EnvelopeType_filt, reshigh);
			}
		} else {
			if ((tStat->envstat==-1) || (tStat->envpos >= tStat->envsize)) {
				if (tStat->envstat==-1)
					tStat->envbase = 0;
				else
					tStat->envbase = patchCache->filtEnv.envlevel[tStat->envstat];
				tStat->envstat++;
				tStat->envpos = 0;
				if (tStat->envstat == 3) {
					tStat->envsize = lasttimetable[(int)patchCache->filtEnv.envtime[tStat->envstat]];
				} else {
					tStat->envsize = (envtimetable[(int)patchCache->filtEnv.envtime[tStat->envstat]] * noteLookup->timekeyTable[(int)patchCache->filtEnv.envtkf]) >> 8;
				}

				tStat->envsize++;
				tStat->envdist = patchCache->filtEnv.envlevel[tStat->envstat] - tStat->envbase;
			}

			reshigh = tStat->envbase;
			reshigh = (reshigh + ((tStat->envdist * tStat->envpos) / tStat->envsize));

		}
		tStat->prevlevel = reshigh;
	}

	cutoff = patchCache->filtEnv.cutoff;

	//if (patchCache->waveform==1) reshigh = (reshigh * 3) >> 2;

	depth = patchCache->filtEnv.envdepth;

	//int sensedep = (depth * 127-patchCache->filtEnv.envsense) >> 7;
	depth = (depth * filveltable[poly->vel][(int)patchCache->filtEnv.envsense]) >> 8;

	int bias = patchCache->tvfbias;
	int dist;

	if (bias!=0) {
		//FIXME:KG: Is this really based on pitch (as now), or key pressed?
		//synth->printDebug("Cutoff before %d", cutoff);
		if (patchCache->tvfdir == 0) {
			if (noteVal < bias) {
				dist = bias - noteVal;
				cutoff = (cutoff * fbiastable[patchCache->tvfblevel][dist]) >> 8;
			}
		} else {
			// > Bias
			if (noteVal > bias) {
				dist = noteVal - bias;
				cutoff = (cutoff * fbiastable[patchCache->tvfblevel][dist]) >> 8;
			}

		}
		//synth->printDebug("Cutoff after %d", cutoff);
	}

	depth = (depth * noteLookup->fildepTable[patchCache->tvfdepth]) >> 8;
	reshigh = (reshigh * depth) >> 7;

	Bit32s tmp;

	cutoff *= keyfollow;
	cutoff /= realfollow;

	reshigh *= keyfollow;
	reshigh /= realfollow;

	if (cutoff>100)
		cutoff = 100;
	else if (cutoff<0)
		cutoff = 0;
	if (reshigh>100)
		reshigh = 100;
	else if (reshigh<0)
		reshigh = 0;
	tmp = noteLookup->nfiltTable[cutoff][reshigh];
	//tmp *= keyfollow;
	//tmp /= realfollow;

	//synth->printDebug("Cutoff %d, tmp %d, freq %d", cutoff, tmp, tmp * 256);
	return tmp;
}

bool Partial::shouldReverb() {
	if (!isActive())
		return false;
	return patchCache->reverb;
}

Bit32u Partial::getAmpEnvelope() {
	Bit32s tc;

	EnvelopeStatus *tStat = &envs[EnvelopeType_amp];

	if (!play)
		return 0;

	if (tStat->decaying) {
		tc = tStat->envbase;
		tc = (tc + ((tStat->envdist * tStat->envpos) / tStat->envsize));
		if (tc < 0)
			tc = 0;
		if ((tStat->envpos >= tStat->envsize) || (tc == 0)) {
			play = false;
			// Don't have to worry about prevlevel storage or anything, this partial's about to die
			return 0;
		}
	} else {
		if ((tStat->envstat==-1) || (tStat->envpos >= tStat->envsize)) {
			if (tStat->envstat==-1)
				tStat->envbase = 0;
			else
				tStat->envbase = patchCache->ampEnv.envlevel[tStat->envstat];
			tStat->envstat++;
			tStat->envpos = 0;

			switch(tStat->envstat) {
			case 0:
				//Spot for velocity time follow
				//Only used for first attack
				tStat->envsize = (envtimetable[(int)patchCache->ampEnv.envtime[tStat->envstat]] * veltkeytable[(int)patchCache->ampEnv.envvkf][poly->vel]) >> 8;
				//synth->printDebug("Envstat %d, size %d", tStat->envstat, tStat->envsize);
				break;
			case 3:
				// Final attack envelope uses same time table as the decay
				//tStat->envsize = decaytimetable[patchCache->ampEnv.envtime[tStat->envstat]];
				tStat->envsize = lasttimetable[(int)patchCache->ampEnv.envtime[tStat->envstat]];
				//synth->printDebug("Envstat %d, size %d", tStat->envstat, tStat->envsize);
				break;
			case 4:
				//synth->printDebug("Envstat %d, size %d", tStat->envstat, tStat->envsize);
				tc = patchCache->ampsustain;
				if (!poly->sustain)
					startDecay(EnvelopeType_amp, tc);
				else
					tStat->sustaining = true;

				goto PastCalc;
			default:
				//Spot for timekey follow
				//Only used in subsquent envelope parameters, including the decay
				tStat->envsize = (envtimetable[(int)patchCache->ampEnv.envtime[tStat->envstat]] * noteLookup->timekeyTable[(int)patchCache->ampEnv.envtkf]) >> 8;

				//synth->printDebug("Envstat %d, size %d", tStat->envstat, tStat->envsize);
				break;
			}

			tStat->envsize++;
			tStat->envdist = patchCache->ampEnv.envlevel[tStat->envstat] - tStat->envbase;

			if (tStat->envdist != 0) {
				tStat->counter = abs(tStat->envsize / tStat->envdist);
				//synth->printDebug("Pos %d, envsize %d envdist %d", tStat->envstat, tStat->envsize, tStat->envdist);
			} else {
				tStat->counter = 0;
				//synth->printDebug("Pos %d, envsize %d envdist %d", tStat->envstat, tStat->envsize, tStat->envdist);
			}
		}
		tc = tStat->envbase;
		tc = (tc + ((tStat->envdist * tStat->envpos) / tStat->envsize));
		tStat->count = tStat->counter;
PastCalc:
		tc = (tc * (Bit32s)patchCache->amplevel) >> 7;
	}

	// Prevlevel storage is bottle neck
	tStat->prevlevel = tc;

	//Bias level crap stuff now

	for (int i = 0; i < 2; i++) {
		if (patchCache->ampblevel[i]!=0) {
			int bias = patchCache->ampbias[i];
			if (patchCache->ampdir[i]==0) {
				// < Bias
				if (noteVal < bias) {
					int dist = bias - noteVal;
					tc = (tc * ampbiastable[patchCache->ampblevel[i]][dist]) >> 8;
				}
			} else {
				// > Bias
				if (noteVal > bias) {
					int dist = noteVal - bias;
					tc = (tc * ampbiastable[patchCache->ampblevel[i]][dist]) >> 8;
				}
			}
		}
	}
	if (tc < 0) {
		synth->printDebug("*** ERROR: tc < 0 (%d) at getAmpEnvelope()", tc);
		tc = 0;
	}
	return (Bit32u)tc;
}

Bit32s Partial::getPitchEnvelope() {
	EnvelopeStatus *tStat = &envs[EnvelopeType_pitch];

	Bit32s tc;
	pitchSustain = false;
	if (tStat->decaying) {
		if (tStat->envpos >= tStat->envsize)
			tc = patchCache->pitchEnv.level[4];
		else {
			tc = tStat->envbase;
			tc = (tc + ((tStat->envdist * tStat->envpos) / tStat->envsize));
		}
	} else {
		if (tStat->envstat==3) {
			tc = patchCache->pitchsustain;
			if (poly->sustain)
				pitchSustain = true;
			else
				startDecay(EnvelopeType_pitch, tc);
		} else {
			if ((tStat->envstat==-1) || (tStat->envpos >= tStat->envsize)) {
				tStat->envstat++;

				tStat->envbase = patchCache->pitchEnv.level[tStat->envstat];
				tStat->envsize = (envtimetable[(int)patchCache->pitchEnv.time[tStat->envstat]] * noteLookup->timekeyTable[(int)patchCache->pitchEnv.timekeyfollow]) >> 8;

				tStat->envpos = 0;
				tStat->envsize++;
				tStat->envdist = patchCache->pitchEnv.level[tStat->envstat + 1] - tStat->envbase;
			}
			tc = tStat->envbase;
			tc = (tc + ((tStat->envdist * tStat->envpos) / tStat->envsize));
		}
		tStat->prevlevel = tc;
	}
	return tc;
}

void Partial::startDecayAll() {
	startDecay(EnvelopeType_amp, envs[EnvelopeType_amp].prevlevel);
	startDecay(EnvelopeType_filt, envs[EnvelopeType_filt].prevlevel);
	startDecay(EnvelopeType_pitch, envs[EnvelopeType_pitch].prevlevel);
	pitchSustain = false;
}

void Partial::startDecay(EnvelopeType envnum, Bit32s startval) {
	EnvelopeStatus *tStat  = &envs[envnum];

	tStat->sustaining = false;
	tStat->decaying = true;
	tStat->envpos = 0;
	tStat->envbase = startval;

	switch(envnum) {
	case EnvelopeType_amp:
		tStat->envsize = (decaytimetable[(int)patchCache->ampEnv.envtime[4]] * noteLookup->timekeyTable[(int)patchCache->ampEnv.envtkf]) >> 8;
		tStat->envdist = -startval;
		break;
	case EnvelopeType_filt:
		tStat->envsize = (decaytimetable[(int)patchCache->filtEnv.envtime[4]] * noteLookup->timekeyTable[(int)patchCache->filtEnv.envtkf]) >> 8;
		tStat->envdist = -startval;
		break;
	case EnvelopeType_pitch:
		tStat->envsize = (decaytimetable[(int)patchCache->pitchEnv.time[3]] * noteLookup->timekeyTable[(int)patchCache->pitchEnv.timekeyfollow]) >> 8 ;
		tStat->envdist = patchCache->pitchEnv.level[4] - startval;
		break;
	default:
		break;
	}
	tStat->envsize++;
}

--- NEW FILE: partial.h ---
/* Copyright (c) 2003-2004 Various contributors
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 */

#ifndef MT32EMU_PARTIAL_H
#define MT32EMU_PARTIAL_H

namespace MT32Emu {

class Synth;
struct NoteLookup;

enum EnvelopeType {
	EnvelopeType_amp = 0,
	EnvelopeType_filt = 1,
	EnvelopeType_pitch = 2
};

struct EnvelopeStatus {
	Bit32s envpos;
	Bit32s envstat;
	Bit32s envbase;
	Bit32s envdist;
	Bit32s envsize;

	bool sustaining;
	bool decaying;
	Bit32s prevlevel;

	Bit32s counter;
	Bit32s count;
};

// Class definition of MT-32 partials.  32 in all.
class Partial {
private:
	Synth *synth;

	int ownerPart; // -1 if unassigned
	int mixType;
	int structurePosition; // 0 or 1 of a structure pair
	bool useNoisePair;

	Bit16s myBuffer[MAX_SAMPLE_OUTPUT];

	bool play;

	// Keyfollowed note value
#if MT32EMU_ACCURATENOTES == 1
	NoteLookup noteLookupStorage;
	float noteVal;
#else
	int noteVal;
	int fineShift;
#endif
	const NoteLookup *noteLookup; // LUTs for this noteVal

	// Keyfollowed filter values
	int realVal;
	int filtVal;

	EnvelopeStatus envs[3];

	int pulsewidth;

	Bit32u lfoPos;
	soundaddr partialOff;

	Bit32u ampEnvVal;
	Bit32u pitchEnvVal;

	float history[32];

	bool pitchSustain;

	int loopPos;

	dpoly *poly;

	int bendShift;

	Bit16s *mixBuffers(Bit16s *buf1, Bit16s *buf2, int len);
	Bit16s *mixBuffersRingMix(Bit16s *buf1, Bit16s *buf2, int len);
	Bit16s *mixBuffersRing(Bit16s *buf1, Bit16s *buf2, int len);
	void mixBuffersStereo(Bit16s *buf1, Bit16s *buf2, Bit16s *outBuf, int len);

	Bit32s getFiltEnvelope();
	Bit32u getAmpEnvelope();
	Bit32s getPitchEnvelope();

	void initKeyFollow(int freqNum);

public:
	const PatchCache *patchCache;
	PatchCache cachebackup;

	Partial *pair;
	bool alreadyOutputed;
	Bit32u age;

	Partial(Synth *synth);
	~Partial();

	int getOwnerPart();
	bool isActive();
	void activate(int part);
	void deactivate(void);
	void startPartial(dpoly *usePoly, const PatchCache *useCache, Partial *pairPartial);
	void startDecay(EnvelopeType envnum, Bit32s startval);
	void startDecayAll();
	void setBend(float factor);
	bool shouldReverb();

	// Returns true only if data written to buffer
	// This function (unlike the one below it) returns processed stereo samples
	// made from combining this single partial with its pair, if it has one.
	bool produceOutput(Bit16s * partialBuf, long length);

	// This function produces mono sample output using the partial's private internal buffer
	Bit16s *generateSamples(long length);
};

}

#endif

--- NEW FILE: partialManager.cpp ---
/* Copyright (c) 2003-2004 Various contributors
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 */

#include <memory.h>

#include "mt32emu.h"

using namespace MT32Emu;

PartialManager::PartialManager(Synth *useSynth) {
	this->synth = useSynth;
	for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++)
		partialTable[i] = new Partial(synth);
}

PartialManager::~PartialManager(void) {
	for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++)
		delete partialTable[i];
}

void PartialManager::getPerPartPartialUsage(int usage[9]) {
	memset(usage, 0, 9 * sizeof (int));
	for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++) {
		if (partialTable[i]->isActive())
			usage[partialTable[i]->getOwnerPart()]++;
	}
}

void PartialManager::clearAlreadyOutputed() {
	for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++)
		partialTable[i]->alreadyOutputed = false;
}

void PartialManager::ageAll() {
	for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++)
		partialTable[i]->age++;
}

bool PartialManager::shouldReverb(int i) {
	return partialTable[i]->shouldReverb();
}

bool PartialManager::produceOutput(int i, Bit16s *buffer, Bit32u bufferLength) {
	return partialTable[i]->produceOutput(buffer, bufferLength);
}

void PartialManager::deactivateAll() {
	for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++) {
		partialTable[i]->deactivate();
	}
}

unsigned int PartialManager::setReserve(Bit8u *rset) {
	unsigned int pr = 0;
	for (int x = 0; x < 9; x++) {
		for (int y = 0; y < rset[x]; y++) {
			partialReserveTable[pr] = x;
			pr++;
		}
	}
	return pr;
}

Partial *PartialManager::allocPartial(int partNum) {
	Partial *outPartial = NULL;

	// Use the first inactive partial reserved for the specified part (if there are any)
	// Otherwise, use the last inactive partial, if any
	for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++) {
		if (!partialTable[i]->isActive()) {
			outPartial = partialTable[i];
			if (partialReserveTable[i] == partNum)
				break;
		}
	}
	if (outPartial != NULL) {
		outPartial->activate(partNum);
		outPartial->age = 0;
	}
	return outPartial;
}

unsigned int PartialManager::getFreePartialCount(void) {
	int count = 0;
	memset(partialPart, 0, sizeof(partialPart));
	for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++) {
		if (!partialTable[i]->isActive())
			count++;
		else
			partialPart[partialTable[i]->getOwnerPart()]++;
	}
	return count;
}

/*
bool PartialManager::freePartials(unsigned int needed, int partNum) {
	int i;
	int myPartPrior = (int)mt32ram.system.reserveSettings[partNum];
	if (myPartPrior<partialPart[partNum]) {
		//This can have more parts, must kill off those with less priority
		int most, mostPart;
		while (needed > 0) {
			int selectPart = -1;
			//Find the worst offender with more partials than allocated and kill them
			most = -1;
			mostPart = -1;
			int diff;

			for (i=0;i<9;i++) {
				diff = partialPart[i] - (int)mt32ram.system.reserveSettings[i];

				if (diff>0) {
					if (diff>most) {
						most = diff;
						mostPart = i;
					}
				}
			}
			selectPart = mostPart;
			if (selectPart == -1) {
				// All parts are within the allocated limits, you suck
				// Look for first partial not of this part that's decaying perhaps?
				return false;
			}
			bool found;
			int oldest;
			int oldnum;
			while (partialPart[selectPart] > (int)mt32ram.system.reserveSettings[selectPart]) {
				oldest = -1;
				oldnum = -1;
				found = false;
				for (i = 0; i < MT32EMU_MAX_PARTIALS; i++) {
					if (partialTable[i]->isActive) {
						if (partialTable[i]->ownerPart == selectPart) {
							found = true;
							if (partialTable[i]->age > oldest) {
								oldest = partialTable[i]->age;
								oldnum = i;
							}
						}
					}
				}
				if (!found) break;
				partialTable[oldnum]->deactivate();
				--partialPart[selectPart];
				--needed;
			}

		}
		return true;

	} else {
		//This part has reached its max, must kill off its own
		bool found;
		int oldest;
		int oldnum;
		while (needed > 0) {
			oldest = -1;
			oldnum = -1;
			found = false;
			for (i = 0; i < MT32EMU_MAX_PARTIALS; i++) {
				if (partialTable[i]->isActive) {
					if (partialTable[i]->ownerPart == partNum) {
						found = true;
						if (partialTable[i]->age > oldest) {
							oldest = partialTable[i]->age;
							oldnum = i;
						}
					}
				}
			}
			if (!found) break;
			partialTable[oldnum]->deactivate();
			--needed;
		}
		// Couldn't free enough partials, sorry
		if (needed>0) return false;
		return true;
	}

}
*/
bool PartialManager::freePartials(unsigned int needed, int partNum) {
	if (needed == 0) {
		return true;
	}
	// Reclaim partials reserved for this part
	// Kill those that are already decaying first
	/*
	for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++) {
		if (partialReserveTable[i] == partNum) {
			if (partialTable[i]->ownerPart != partNum) {
				if (partialTable[i]->partCache->envs[AMPENV].decaying) {
					partialTable[i]->isActive = false;
					--needed;
					if (needed == 0)
						return true;
				}
			}
		}
	}*/
	// Then kill those with the lowest part priority -- oldest at the moment
	while (needed > 0) {
		Bit32u prior = 0;
		int priornum = -1;

		for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++) {
			if (partialReserveTable[i] == partNum && partialTable[i]->isActive() && partialTable[i]->getOwnerPart() != partNum) {
				/*
				if (mt32ram.system.reserveSettings[partialTable[i]->ownerPart] < prior) {
					prior = mt32ram.system.reserveSettings[partialTable[i]->ownerPart];
					priornum = i;
				}*/
				if (partialTable[i]->age >= prior) {
					prior = partialTable[i]->age;
					priornum = i;
				}
			}
		}
		if (priornum != -1) {
			partialTable[priornum]->deactivate();
			--needed;
		} else {
			break;
		}
	}

	// Kill off the oldest partials within this part
	while (needed > 0) {
		Bit32u oldest = 0;
		int oldlist = -1;
		for (int i = 0; i < MT32EMU_MAX_PARTIALS; i++) {
			if (partialTable[i]->getOwnerPart() == partNum && partialTable[i]->isActive()) {
				if (partialTable[i]->age >= oldest) {
					oldest = partialTable[i]->age;
					oldlist = i;
				}
			}
		}
		if (oldlist != -1) {
			partialTable[oldlist]->deactivate();
			--needed;
		} else {
			break;
		}
	}
	return needed == 0;
}

--- NEW FILE: partialManager.h ---
/* Copyright (c) 2003-2004 Various contributors
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 */

#ifndef MT32EMU_PARTIALMANAGER_H
#define MT32EMU_PARTIALMANAGER_H

namespace MT32Emu {

class Synth;

class PartialManager {
private:
	Synth *synth; // Only used for sending debug output

	Partial *partialTable[MT32EMU_MAX_PARTIALS];
	Bit32s partialReserveTable[MT32EMU_MAX_PARTIALS];
	Bit32s partialPart[9]; // The count of partials played per part

public:
	PartialManager(Synth *synth);
	~PartialManager();
	Partial *allocPartial(int partNum);
	unsigned int getFreePartialCount(void);
	bool freePartials(unsigned int needed, int partNum);
	unsigned int setReserve(Bit8u *rset);
	void deactivateAll();
	void ageAll();
	bool produceOutput(int i, Bit16s *buffer, Bit32u bufferLength);
	bool shouldReverb(int i);
	void clearAlreadyOutputed();
	void getPerPartPartialUsage(int usage[9]);
};

}

#endif

--- NEW FILE: structures.h ---
/* Copyright (c) 2003-2004 Various contributors
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 */

#ifndef MT32EMU_STRUCTURES_H
#define MT32EMU_STRUCTURES_H

namespace MT32Emu {

const unsigned int MAX_SAMPLE_OUTPUT = 4096;

#ifdef _MSC_VER
#define  MT32EMU_ALIGN_PACKED __declspec(align(1))
typedef unsigned __int64   Bit64u;
typedef   signed __int64   Bit64s;
#else
#define MT32EMU_ALIGN_PACKED __attribute__((packed))
typedef unsigned long long Bit64u;
typedef   signed long long Bit64s;
#endif

typedef unsigned int       Bit32u;
typedef   signed int       Bit32s;
typedef unsigned short int Bit16u;
typedef   signed short int Bit16s;
typedef unsigned char      Bit8u;
typedef   signed char      Bit8s;

// The following structures represent the MT-32's memory
// Since sysex allows this memory to be written to in blocks of bytes,
// we keep this packed so that we can copy data into the various
// banks directly
#if defined(_MSC_VER) || defined (__MINGW32__)
#pragma pack(push, 1)
#else
#pragma pack(1)
#endif

struct TimbreParam {
	struct commonParam {
		char name[10];
		Bit8u pstruct12;  // 1&2  0-12 (1-13)
		Bit8u pstruct34;  // #3&4  0-12 (1-13)
		Bit8u pmute;  // 0-15 (0000-1111)
		Bit8u nosustain; // 0-1(Normal, No sustain)
	} MT32EMU_ALIGN_PACKED common;

	struct partialParam {
		struct wgParam {
			Bit8u coarse;  // 0-96 (C1,C#1-C9)
			Bit8u fine;  // 0-100 (-50 to +50 (cents?))
			Bit8u keyfollow;  // 0-16 (-1,-1/2,0,1,1/8,1/4,3/8,1/2,5/8,3/4,7/8,1,5/4,3/2,2.s1,s2)
			Bit8u bender;  // 0,1 (ON/OFF)
			Bit8u waveform; //  0-1 (SQU/SAW)
			Bit8u pcmwave; // 0-127 (1-128)
			Bit8u pulsewid; // 0-100
			Bit8u pwvelo; // 0-14 (-7 - +7)
		} MT32EMU_ALIGN_PACKED wg;

		struct envParam {
			Bit8u depth; // 0-10
			Bit8u sensitivity; // 1-100
			Bit8u timekeyfollow; // 0-4
			Bit8u time[4]; // 1-100
			Bit8u level[5]; // 1-100 (-50 - +50)
		} MT32EMU_ALIGN_PACKED env;

		struct lfoParam {
			Bit8u rate; // 0-100
			Bit8u depth; // 0-100
			Bit8u modsense; // 0-100
		} MT32EMU_ALIGN_PACKED lfo;

		struct tvfParam {
			Bit8u cutoff; // 0-100
			Bit8u resonance; // 0-30
			Bit8u keyfollow; // 0-16 (-1,-1/2,1/4,0,1,1/8,1/4,3/8,1/2,5/8,3/2,7/8,1,5/4,3/2,2,s1,s2)
			Bit8u biaspoint; // 0-127 (<1A-<7C >1A-7C)
			Bit8u biaslevel; // 0-14 (-7 - +7)
			Bit8u envdepth; // 0-100
			Bit8u envsense; // 0-100
			Bit8u envdkf; // DEPTH KEY FOLL0W 0-4
			Bit8u envtkf; // TIME KEY FOLLOW 0-4
			Bit8u envtime[5]; // 1-100
			Bit8u envlevel[4]; // 1-100
		} MT32EMU_ALIGN_PACKED tvf;

		struct tvaParam {
			Bit8u level; // 0-100
			Bit8u velosens; // 0-100
			Bit8u biaspoint1; // 0-127 (<1A-<7C >1A-7C)
			Bit8u biaslevel1; // 0-12 (-12 - 0)
			Bit8u biaspoint2; // 0-127 (<1A-<7C >1A-7C)
			Bit8u biaslevel2; // 0-12 (-12 - 0)
			Bit8u envtkf; // TIME KEY FOLLOW 0-4
			Bit8u envvkf; // VELOS KEY FOLL0W 0-4
			Bit8u envtime[5]; // 1-100
			Bit8u envlevel[4]; // 1-100
		} MT32EMU_ALIGN_PACKED tva;
	} MT32EMU_ALIGN_PACKED partial[4];
} MT32EMU_ALIGN_PACKED;

struct PatchParam {
	Bit8u timbreGroup; // TIMBRE GROUP  0-3 (group A, group B, Memory, Rhythm)
	Bit8u timbreNum; // TIMBRE NUMBER 0-63
	Bit8u keyShift; // KEY SHIFT 0-48 (-24 - +24 semitones)
	Bit8u fineTune; // FINE TUNE 0-100 (-50 - +50 cents)
	Bit8u benderRange; // BENDER RANGE 0-24
	Bit8u assignMode;  // ASSIGN MODE 0-3 (POLY1, POLY2, POLY3, POLY4)
	Bit8u reverbSwitch;  // REVERB SWITCH 0-1 (OFF,ON)
	Bit8u dummy; // (DUMMY)
} MT32EMU_ALIGN_PACKED;

struct MemParams {
	struct PatchTemp {
		PatchParam patch;
		Bit8u outlevel; // OUTPUT LEVEL 0-100
		Bit8u panpot; // PANPOT 0-14 (R-L)
		Bit8u dummyv[6];
	} MT32EMU_ALIGN_PACKED patchSettings[8];

	struct RhythmTemp {
		Bit8u timbre; // TIMBRE  0-94 (M1-M64,R1-30,OFF)
		Bit8u outlevel; // OUTPUT LEVEL 0-100
		Bit8u panpot; // PANPOT 0-14 (R-L)
		Bit8u reverbSwitch;  // REVERB SWITCH 0-1 (OFF,ON)
	} MT32EMU_ALIGN_PACKED rhythmSettings[86]; // FIXME: Was 64, but let's support the next model...

	TimbreParam MT32EMU_ALIGN_PACKED timbreSettings[8];

	PatchParam MT32EMU_ALIGN_PACKED patches[128];

	struct PaddedTimbre {
		TimbreParam timbre;
		Bit8u padding[10];
	} MT32EMU_ALIGN_PACKED timbres[64 + 64 + 64 + 30]; // Group A, Group B, Memory, Rhythm

	struct SystemArea {
		Bit8u masterTune; // MASTER TUNE 0-127 432.1-457.6Hz
		Bit8u reverbMode; // REVERB MODE 0-3 (room, hall, plate, tap delay)
		Bit8u reverbTime; // REVERB TIME 0-7 (1-8)
		Bit8u reverbLevel; // REVERB LEVEL 0-7 (1-8)
		Bit8u reserveSettings[9]; // PARTIAL RESERVE (PART 1) 0-32
		Bit8u chanAssign[9]; // MIDI CHANNEL (PART1) 0-16 (1-16,OFF)
		Bit8u masterVol; // MASTER VOLUME 0-100
	} MT32EMU_ALIGN_PACKED system;
};

struct MemBanks {
	Bit8u pTemp[8][sizeof(MemParams::PatchTemp)];
	Bit8u rTemp[86][sizeof(MemParams::RhythmTemp)];
	Bit8u tTemp[8][sizeof(TimbreParam)];
	Bit8u patchBank[128][sizeof(PatchParam)];
	Bit8u timbreBank[64 + 64 + 64 + 30][sizeof(MemParams::PaddedTimbre)];
	Bit8u systemBank[sizeof(MemParams::SystemArea)];
	// System memory 0x100000
	// Display 0x200000
	// Reset 0x7F0000
};

#if defined(_MSC_VER) || defined (__MINGW32__)
#pragma pack(pop)
#else
#pragma pack()
#endif

struct PCMWaveEntry {
	Bit32u addr;
	Bit32u len;
	double tune;
	bool loop;
};

struct soundaddr {
	Bit16u pcmplace;
	Bit16u pcmoffset;
};

struct StereoVolume {
	Bit16s leftvol;
	Bit16s rightvol;
};

// This is basically a per-partial, pre-processed combination of timbre and patch/rhythm settings
struct PatchCache {
	bool playPartial;
	bool PCMPartial;
	int pcm;
	char waveform;
	int pulsewidth;
	int pwsens;

	float pitch;

	int lfodepth;
	int lforate;
	Bit32u lfoperiod;
	int modsense;

	float pitchKeyfollow;

	int filtkeyfollow;

	int tvfbias;
	int tvfblevel;
	int tvfdir;

	int ampbias[2];
	int ampblevel[2];
	int ampdir[2];

	int ampdepth;
	int ampenvdir;
	int amplevel;
	int tvfdepth;

	bool useBender;
	float benderRange; // 0.0, 1.0, .., 24.0 (semitones)

	TimbreParam::partialParam::envParam pitchEnv;
	TimbreParam::partialParam::tvaParam ampEnv;
	TimbreParam::partialParam::tvfParam filtEnv;

	Bit32s ampsustain;
	Bit32s pitchsustain;
	Bit32s filtsustain;

	Bit32u structureMix;
	int structurePosition;
	int structurePair;

	// The following fields are actually common to all partials in the timbre
	bool dirty;
	Bit32u partialCount;
	bool sustain;
	float pitchShift;
	bool reverb;
	const StereoVolume *pansetptr;
};

class Partial; // Forward reference for class defined in partial.h

struct dpoly {
	bool isPlaying;

	unsigned int key;
	int freqnum;
	int vel;

	bool isDecay;

	const Bit32u *volumeptr;

	Partial *partials[4];

	bool pedalhold; // This marks keys that have been released on the keyboard, but are being held by the pedal
	bool sustain;

	bool isActive() const;
	Bit32u getAge() const;
};

}

#endif

--- NEW FILE: synth.cpp ---
/* Copyright (c) 2003-2004 Various contributors
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
[...1009 lines suppressed...]
			if (partialManager->produceOutput(i, &tmpBuffer[0], len))
				ProduceOutput1(&tmpBuffer[0], stream, len, masterVolume);
		}
	}

	partialManager->clearAlreadyOutputed();

#if MT32EMU_MONITOR_PARTIALS == 1
	samplepos += len;
	if (samplepos > myProp.SampleRate * 5) {
		samplepos = 0;
		int partialUsage[9];
		partialManager->GetPerPartPartialUsage(partialUsage);
		printDebug("1:%02d 2:%02d 3:%02d 4:%02d 5:%02d 6:%02d 7:%02d 8:%02d", partialUsage[0], partialUsage[1], partialUsage[2], partialUsage[3], partialUsage[4], partialUsage[5], partialUsage[6], partialUsage[7]);
		printDebug("Rhythm: %02d  TOTAL: %02d", partialUsage[8], MT32EMU_MAX_PARTIALS - partialManager->GetFreePartialCount());
	}
#endif
}

}

--- NEW FILE: synth.h ---
/* Copyright (c) 2003-2004 Various contributors
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 */

#ifndef MT32EMU_SYNTH_H
#define MT32EMU_SYNTH_H

#include <stdarg.h>

class revmodel;

namespace MT32Emu {

const int ROMSIZE = 512 * 1024;
const int PCMSIZE = ROMSIZE / 2;
const int GRAN = 512;

class File;
class TableInitialiser;
class Partial;
class PartialManager;
class Part;

enum ReportType {
	// Errors
	ReportType_errorControlROM = 1,
	ReportType_errorPCMROM,
	ReportType_errorSampleRate,

	// Progress
	ReportType_progressInit,

	// HW spec
	ReportType_availableSSE,
	ReportType_available3DNow,
	ReportType_usingSSE,
	ReportType_using3DNow,

	// General info
	ReportType_lcdMessage,
	ReportType_devReset,
	ReportType_devReconfig,
	ReportType_newReverbMode,
	ReportType_newReverbTime,
	ReportType_newReverbLevel
};

struct SynthProperties {
	// Sample rate to use in mixing
	int sampleRate;

	// Flag to activate reverb.  True = use reverb, False = no reverb
	bool useReverb;
	// True to use software set reverb settings, False to set reverb settings in
	// following parameters
	bool useDefaultReverb;
	// When not using the default settings, this specifies one of the 4 reverb types
	// 1 = Room 2 = Hall 3 = Plate 4 = Tap
	unsigned char reverbType;
	// This specifies the delay time, from 0-7 (not sure of the actual MT-32's measurement)
	unsigned char reverbTime;
	// This specifies the reverb level, from 0-7 (not sure of the actual MT-32's measurement)
	unsigned char reverbLevel;
	// The name of the directory in which the ROM and data files are stored (with trailing slash/backslash)
	// Not used if "openFile" is set. May be NULL in any case.
	char *baseDir;
	// This is used as the first argument to all callbacks
	void *userData;
	// Callback for reporting various errors and information. May be NULL
	int (*report)(void *userData, ReportType type, const void *reportData);
	// Callback for debug messages, in vprintf() format
	void (*printDebug)(void *userData, const char *fmt, va_list list);
	// Callback for providing an implementation of File, opened and ready for use
	// May be NULL, in which case a default implementation will be used.
	File *(*openFile)(void *userData, const char *filename, File::OpenMode mode);
	// Callback for closing a File. May be NULL, in which case the File will automatically be close()d/deleted.
	void (*closeFile)(void *userData, File *file);
};

// This is the specification of the Callback routine used when calling the RecalcWaveforms
// function
typedef void (*recalcStatusCallback)(int percDone);

// This external function recreates the base waveform file (waveforms.raw) using a specifed
// sampling rate.  The callback routine provides interactivity to let the user know what
// percentage is complete in regenerating the waveforms.  When a NULL pointer is used as the
// callback routine, no status is reported.
bool RecalcWaveforms(char * baseDir, int sampRate, recalcStatusCallback callBack);

typedef float (*iir_filter_type)(float input,float *hist1_ptr, float *coef_ptr, int revLevel);

class Synth {
friend class Part;
friend class RhythmPart;
friend class Partial;
friend class TableInitialiser;
private:
	bool isEnabled;

	iir_filter_type iirFilter;

	PCMWaveEntry PCMList[128];

	Bit8u controlROMData[64 * 1024];
	Bit16s romfile[PCMSIZE + GRAN];
	Bit8s chantable[32];

	#if MT32EMU_MONITOR_PARTIALS == 1
	static Bit32s samplepos = 0;
	#endif

	MemParams mt32ram, mt32default;

	revmodel *reverbModel;

	float masterTune;
	Bit16u masterVolume;

	bool isOpen;

	PartialManager *partialManager;
	Part *parts[9];

	Bit16s tmpBuffer[MAX_SAMPLE_OUTPUT * 2];
	float sndbufl[MAX_SAMPLE_OUTPUT];
	float sndbufr[MAX_SAMPLE_OUTPUT];
	float outbufl[MAX_SAMPLE_OUTPUT];
	float outbufr[MAX_SAMPLE_OUTPUT];

	SynthProperties myProp;

	bool loadPreset(File *file);
	void initReverb(Bit8u newRevMode, Bit8u newRevTime, Bit8u newRevLevel);
	void doRender(Bit16s * stream, Bit32u len);
	void playMsgOnPart(unsigned char part, unsigned char code, unsigned char note, unsigned char velocity);
	void playSysexWithoutHeader(unsigned char channel, const Bit8u *sysex, Bit32u len);

	bool loadControlROM(const char *filename);
	bool loadPCMROM(const char *filename);
	bool dumpTimbre(File *file, const TimbreParam *timbre, Bit32u addr);
	int dumpTimbres(const char *filename, int start, int len);

	void initPCMList();
	void initRhythmTimbres();
	void initTimbres(Bit16u mapAddress, int startTimbre);
	void initRhythmTimbre(int drumNum, const Bit8u *mem);
	bool refreshSystem();
protected:
	int report(ReportType type, const void *reportData);
	File *openFile(const char *filename, File::OpenMode mode);
	void closeFile(File *file);
	void printDebug(const char *fmt, ...);

public:
	static Bit8u calcSysexChecksum(const Bit8u *data, Bit32u len, Bit8u checksum);

	Synth();
	~Synth();

	// Used to initialise the MT-32. Must be called before any other function.
	// Returns true if initialization was sucessful, otherwise returns false.
	bool open(SynthProperties &useProp);

	// Closes the MT-32 and deallocates any memory used by the synthesizer
	void close(void);

	// Sends a 4-byte MIDI message to the MT-32 for immediate playback
	void playMsg(Bit32u msg);

	// Sends a string of Sysex commands to the MT-32 for immediate interpretation
	// The length is in bytes
	void playSysex(const Bit8u *sysex, Bit32u len);
	void playSysexWithoutFraming(const Bit8u *sysex, Bit32u len);

	// This callback routine is used to have the MT-32 generate samples to the specified
	// output stream.  The length is in whole samples, not bytes. (I.E. in 16-bit stereo,
	// one sample is 4 bytes)
	void render(Bit16s * stream, Bit32u len);
};

}

#endif

--- NEW FILE: tables.cpp ---
/* Copyright (c) 2003-2004 Various contributors
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 */

#include <stdlib.h>
#include <string.h>
#include <math.h>

#include "mt32emu.h"

#define FIXEDPOINT_MAKE(x, point) ((Bit32u)((1 << point) * x))

namespace MT32Emu {

//Amplitude time velocity follow exponential coefficients
const double tvcatconst[5] = {0.0, 0.002791309, 0.005942882, 0.012652792, 0.026938637};
const double tvcatmult[5] = {1.0, 1.072662811, 1.169129367, 1.288579123, 1.229630539};

// These are division constants for the TVF depth key follow
static const Bit32u depexp[5] = {3000, 950, 485, 255, 138};

//Envelope time keyfollow exponential coefficients
static const double tkcatconst[5] = {0.0, 0.005853144, 0.011148054, 0.019086143, 0.043333215};
static const double tkcatmult[5] = {1.0, 1.058245688, 1.048488989, 1.016049301, 1.097538067};

static float initialisedSampleRate = 0.0f;
static float initialisedMasterTune = 0.0f;

Bit16s smallnoise[MAX_SAMPLE_OUTPUT];

// Some optimization stuff
Bit32s keytable[217];
Bit16s sintable[65536];
Bit32u lfotable[101];
Bit32s penvtable[16][101];
Bit32s filveltable[128][101];
Bit32s veltkeytable[5][128];
Bit32s pulsetable[101];
Bit32s pulseoffset[101];
Bit32s ampbiastable[13][128];
Bit32s fbiastable[15][128];
float filtcoeff[FILTERGRAN][31][8];
Bit32s finetable[201];
Bit32u lfoptable[101][101];
Bit32s ampveltable[128][64];
Bit32s pwveltable[15][128];
Bit32s envtimetable[101];
Bit32s decaytimetable[101];
Bit32s lasttimetable[101];
Bit32s voltable[128];
float ResonFactor[31];
float ResonInv[31];

NoteLookup noteLookups[NUM_NOTES];

// Begin filter stuff

// Pre-warp the coefficients of a numerator or denominator.
// Note that a0 is assumed to be 1, so there is no wrapping
// of it.
static void prewarp(double *a1, double *a2, double fc, double fs) {
	double wp;

	wp = 2.0 * fs * tan(DOUBLE_PI * fc / fs);

	*a2 = *a2 / (wp * wp);
	*a1 = *a1 / wp;
}

// Transform the numerator and denominator coefficients
// of s-domain biquad section into corresponding
// z-domain coefficients.
//
//      Store the 4 IIR coefficients in array pointed by coef
//      in following order:
//             beta1, beta2    (denominator)
//             alpha1, alpha2  (numerator)
//
// Arguments:
//             a0-a2   - s-domain numerator coefficients
//             b0-b2   - s-domain denominator coefficients
//             k               - filter gain factor. initially set to 1
//                                and modified by each biquad section in such
//                                a way, as to make it the coefficient by
//                                which to multiply the overall filter gain
//                                in order to achieve a desired overall filter gain,
//                                specified in initial value of k.
//             fs             - sampling rate (Hz)
//             coef    - array of z-domain coefficients to be filled in.
//
// Return:
//             On return, set coef z-domain coefficients
static void bilinear(double a0, double a1, double a2, double b0, double b1, double b2, double *k, double fs, float *coef) {
	double ad, bd;

	// alpha (Numerator in s-domain)
	ad = 4. * a2 * fs * fs + 2. * a1 * fs + a0;
	// beta (Denominator in s-domain)
	bd = 4. * b2 * fs * fs + 2. * b1* fs + b0;

	// update gain constant for this section
	*k *= ad/bd;

	// Denominator
	*coef++ = (float)((2. * b0 - 8. * b2 * fs * fs) / bd);           // beta1
	*coef++ = (float)((4. * b2 * fs * fs - 2. * b1 * fs + b0) / bd); // beta2

	// Nominator
	*coef++ = (float)((2. * a0 - 8. * a2 * fs * fs) / ad);           // alpha1
	*coef = (float)((4. * a2 * fs * fs - 2. * a1 * fs + a0) / ad);   // alpha2
}

// a0-a2: numerator coefficients
// b0-b2: denominator coefficients
// fc: Filter cutoff frequency
// fs: sampling rate
// k: overall gain factor
// coef: pointer to 4 iir coefficients
static void szxform(double *a0, double *a1, double *a2, double *b0, double *b1, double *b2, double fc, double fs, double *k, float *coef) {
	// Calculate a1 and a2 and overwrite the original values
	prewarp(a1, a2, fc, fs);
	prewarp(b1, b2, fc, fs);
	bilinear(*a0, *a1, *a2, *b0, *b1, *b2, k, fs, coef);
}

static void initFilter(float fs, float fc, float *icoeff, float Q) {
	float *coef;
	double a0, a1, a2, b0, b1, b2;

	double k = 1.5;    // Set overall filter gain factor
	coef = icoeff + 1; // Skip k, or gain

	// Section 1
	a0 = 1.0;
	a1 = 0;
	a2 = 0;
	b0 = 1.0;
	b1 = 0.765367 / Q; // Divide by resonance or Q
	b2 = 1.0;
	szxform(&a0, &a1, &a2, &b0, &b1, &b2, fc, fs, &k, coef);
	coef += 4;         // Point to next filter section

	// Section 2
	a0 = 1.0;
	a1 = 0;
	a2 = 0;
	b0 = 1.0;
	b1 = 1.847759 / Q;
	b2 = 1.0;
	szxform(&a0, &a1, &a2, &b0, &b1, &b2, fc, fs, &k, coef);

	icoeff[0] = (float)k;
}

static void initFiltCoeff(float samplerate) {
	for (int j = 0; j < FILTERGRAN; j++) {
		for (int res = 0; res < 31; res++) {
			float tres = ResonFactor[res];
			initFilter((float)samplerate, (((float)(j+1.0)/FILTERGRAN)) * ((float)samplerate/2), filtcoeff[j][res], tres);
		}
	}
}

static void initEnvelopes(float samplerate) {
	for (int lf = 0; lf <= 100; lf++) {
		float elf = (float)lf;

		// General envelope
		float logtime = elf * 0.088362939f;
		envtimetable[lf] = (int)((exp(logtime)/312.12) * (float)samplerate);

		// Decay envelope -- shorter for some reason
		// This is also the timing for the envelope right before the
		// amp and filter envelope sustains

		lasttimetable[lf] = decaytimetable[lf] = (int)((exp(logtime)/(312.12*2)) * (float)samplerate);
		//lasttimetable[lf] = (int)((exp(logtime)/(312.12*6)) * (float)samplerate);

		float mv = (float)lf / 100.0f;
		float pt = mv - 0.5f;
		if (pt < 0)
			pt = 0;

		pulsetable[lf] = (int)(pt * 215.04f) + 128;

		// I am certain of this:  Verified by hand LFO log
		lfotable[lf] = (Bit32u)(((float)samplerate) / (powf(1.088883372f,(float)lf) * 0.021236044f));

		//LOG(LOG_ERROR|LOG_MISC,"lf %d = lfo %d pulsetable %d", lf, lfotable[lf], pulsetable[lf]);
	}
}

void TableInitialiser::initMT32ConstantTables(Synth *synth) {
	if (initialisedSampleRate > 0.0f) {
		return;
	}
	int lf;
	synth->printDebug("Initialising Pitch Tables");
	for (lf = -108; lf <= 108; lf++) {
		keytable[lf + 108] = (int)(256 * powf(2.0f, (float)(lf / 24.0f)));
		//synth->printDebug("KT %d = %d", f, keytable[f+108]);
	}

	int res;
	float fres;
	for (res = 0; res < 31; res++) {
		fres = (float)res / 30.0f;
		ResonFactor[res] = (powf(2.0f, logf(powf(fres, 16.0f))) * 2.5f) + 1.0f;
		ResonInv[res] = 1 / ResonFactor[res];
	}

	int period = 65536;

	for (int ang = 0; ang < period; ang++) {
		int halfang = (period / 2);
		int angval  = ang % halfang;
		float tval = (((float)angval / (float)halfang) - 0.5f) * 2;
		if (ang >= halfang)
			tval = -tval;
		sintable[ang] = (Bit16s)(tval * 50.0f) + 50;
	}

	int velt, dep;
	float tempdep;
	for (velt = 0; velt < 128; velt++) {
		for (dep = 0; dep < 5; dep++) {
			if (dep > 0) {
				float ff = (float)(exp(3.5f*tvcatconst[dep] * (59.0f-(float)velt)) * tvcatmult[dep]);
				tempdep = 256.0f * ff;
				veltkeytable[dep][velt] = (int)tempdep;
				//if ((velt % 16) == 0) {
				//	synth->printDebug("Key %d, depth %d, factor %d", velt, dep, (int)tempdep);
				//}
			} else
				veltkeytable[dep][velt] = 256;
		}

		for (dep = -7; dep < 8; dep++) {
			float fldep = (float)abs(dep) / 7.0f;
			fldep = powf(fldep,2.5f);
			if (dep < 0)
				fldep = fldep * -1.0f;
			pwveltable[dep+7][velt] = Bit32s((fldep * (float)velt * 100) / 128.0);
		}
	}

	for (dep = 0; dep <= 100; dep++) {
		for (velt = 0; velt < 128; velt++) {
			float fdep = (float)dep * 0.000347013f; // Another MT-32 constant
			float fv = ((float)velt - 64.0f)/7.26f;
			float flogdep = powf(10, fdep * fv);
			float fbase;

			if (velt > 64)
				filveltable[velt][dep] = (int)(flogdep * 256.0);
			else {
				//lff = 1 - (pow(((128.0 - (float)lf) / 64.0),.25) * ((float)velt / 96));
				fbase = 1 - (powf(((float)dep / 100.0f),.25f) * ((float)(64-velt) / 96.0f));
				filveltable[velt][dep] = (int)(fbase * 256.0);
			}
			//synth->printDebug("Filvel dep %d velt %d = %x", dep, velt, filveltable[velt][dep]);
		}
	}

	for (lf = 0; lf <= 200; lf++) {
		//FIXME:KG: I'm fairly sure this is wrong... lf=100 should yield no fine-tuning (4096)?
		finetable[lf] = (int)((powf(2.0f, (((float)lf / 200.0f) - 1.0f) / 12.0f)) * 4096.0f);

		// FIXME:KG: This now gives a range of -1 .. 1 semitone. Should be correct, but check
		//finetable[lf] = (int)((powf(2.0f, (((float)lf / 100.0f) - 1.0f) / 12.0f)) * 4096.0f);
	}

	float lff;
	for (lf = 0; lf < 128; lf++) {
		for (velt = 0; velt < 64; velt++) {
			lff = 1 - (powf(((128.0f - (float)lf) / 64.0f), 0.25f) * ((float)velt / 96));
			ampveltable[lf][velt] = (int)(lff * 256.0);
			//synth->printDebug("Ampveltable: %d, %d = %d", lf, velt, ampveltable[lf][velt]);
		}
	}

	for (lf = 0; lf < 128; lf++) {
		// Converts MIDI velocity to volume.
		voltable[lf] = FIXEDPOINT_MAKE(powf((float)lf / 127.0f, FLOAT_LN), 7);
	}
	for (unsigned int i = 0; i < MAX_SAMPLE_OUTPUT; i++) {
		int myRand;
		myRand = rand();
		int origRand = myRand;
		//myRand = ((myRand - 16383) * WGAMP) >> 16;
		// This one is slower but works with all values of RAND_MAX
		myRand = (int)((origRand - RAND_MAX / 2) / (float)RAND_MAX * (WGAMP / 2));
		//FIXME:KG: Original ultimately set the lowest two bits to 0, for no obvious reason
		smallnoise[i] = (Bit16s)myRand;
	}

	float tdist;
	float padjtable[51];
	for (lf = 0; lf <= 50; lf++) {
		if (lf == 0)
			padjtable[lf] = 7;
		else if (lf == 1)
			padjtable[lf] = 6;
		else if (lf == 2)
			padjtable[lf] = 5;
		else if (lf == 3)
			padjtable[lf] = 4;
		else if (lf == 4)
			padjtable[lf] = 4 - (0.333333f);
		else if (lf == 5)
			padjtable[lf] = 4 - (0.333333f * 2);
		else if (lf == 6)
			padjtable[lf] = 3;
		else if ((lf > 6) && (lf <= 12)) {
			tdist = (lf-6.0f) / 6.0f;
			padjtable[lf] = 3.0f - tdist;
		} else if ((lf > 12) && (lf <= 25)) {
			tdist = (lf - 12.0f) / 13.0f;
			padjtable[lf] = 2.0f - tdist;
		} else {
			tdist = (lf - 25.0f) / 25.0f;
			padjtable[lf] = 1.0f - tdist;
		}
		//synth->printDebug("lf %d = padj %f", lf, padjtable[lf]);
	}

	float lfp, depf, finalval, tlf;
	int depat, pval, depti;
	for (lf = 0; lf <= 10; lf++) {
		// I believe the depth is cubed or something

		for (depat = 0; depat <= 100; depat++) {
			if (lf > 0) {
				depti = abs(depat - 50);
				tlf = (float)lf - padjtable[depti];
				if (tlf < 0)
					tlf = 0;
				lfp = expf(0.713619942f * tlf) / 407.4945111f;

				if (depat < 50)
					finalval = 4096.0f * powf(2, -lfp);
				else
					finalval = 4096.0f * powf(2, lfp);
				pval = (int)finalval;

				penvtable[lf][depat] = pval;
				//synth->printDebug("lf %d depat %d pval %d tlf %f lfp %f", lf,depat,pval, tlf, lfp);
			} else {
				penvtable[lf][depat] = 4096;
				//synth->printDebug("lf %d depat %d pval 4096", lf, depat);
			}
		}
	}
	for (lf = 0; lf <= 100; lf++) {
		// It's linear - verified on MT-32 - one of the few things linear
		lfp = ((float)lf * 0.1904f) / 310.55f;

		for (depat = 0; depat <= 100; depat++) {
			depf = ((float)depat - 50.0f) / 50.0f;
			//finalval = pow(2, lfp * depf * .5);
			finalval = 4096.0f + (4096.0f * lfp * depf);

			pval = (int)finalval;

			lfoptable[lf][depat] = pval;

			//synth->printDebug("lf %d depat %d pval %x", lf,depat,pval);
		}
	}

	for (lf = 0; lf <= 12; lf++) {
		for (int distval = 0; distval < 128; distval++) {
			float amplog, dval;
			if (lf == 0) {
				amplog = 0;
				dval = 1;
				ampbiastable[lf][distval] = 256;
			} else {
				amplog = powf(1.431817011f, (float)lf) / FLOAT_PI;
				dval = ((128.0f - (float)distval) / 128.0f);
				amplog = expf(amplog);
				dval = powf(amplog, dval) / amplog;
				ampbiastable[lf][distval] = (int)(dval * 256.0);
			}
			//synth->printDebug("Ampbias lf %d distval %d = %f (%x) %f", lf, distval, dval, ampbiastable[lf][distval],amplog);
		}
	}

	for (lf = 0; lf <= 14; lf++) {
		for (int distval = 0; distval < 128; distval++) {
			float filval = fabsf((float)((lf - 7) * 12) / 7.0f);
			float amplog, dval;
			if (lf == 7) {
				amplog = 0;
				dval = 1;
				fbiastable[lf][distval] = 256;
			} else {
				//amplog = pow(1.431817011, filval) / FLOAT_PI;
				amplog = powf(1.531817011f, filval) / FLOAT_PI;
				dval = (128.0f - (float)distval) / 128.0f;
				amplog = expf(amplog);
				dval = powf(amplog,dval)/amplog;
				if (lf < 8) {
					fbiastable[lf][distval] = (int)(dval * 256.0f);
				} else {
					dval = powf(dval, 0.3333333f);
					if (dval < 0.01f)
						dval = 0.01f;
					dval = 1 / dval;
					fbiastable[lf][distval] = (int)(dval * 256.0f);
				}
			}
			//synth->printDebug("Fbias lf %d distval %d = %f (%x) %f", lf, distval, dval, fbiastable[lf][distval],amplog);
		}
	}
}

// Per-note table initialisation follows

static void initSaw(NoteLookup *noteLookup, Bit32s div) {
	for (int rsaw = 0; rsaw <= 100; rsaw++) {
		float fsaw;
		if (rsaw < 50)
			fsaw = 50.0f;
		else
			fsaw = (float)rsaw;
		int tmpdiv = div << 17;

		//(66 - (((A8 - 50) / 50) ^ 0.63) * 50) / 132
		float sawfact = (66.0f - (powf((fsaw - 50.0f) / 50.0f, 0.63f) * 50.0f)) / 132.0f;
		noteLookup->sawTable[rsaw] = (int)(sawfact * (float)tmpdiv) >> 16;
		//synth->printDebug("F %d divtable %d saw %d sawtable %d", f, div, rsaw, sawtable[f][rsaw]);
	}
}

static void initDep(NoteLookup *noteLookup, float f) {
	for (int dep = 0; dep < 5; dep++) {
		if (dep == 0) {
			noteLookup->fildepTable[dep] = 256;
			noteLookup->timekeyTable[dep] = 256;
		} else {
			float depfac = 3000.0f;
			float ff, tempdep;
			depfac = (float)depexp[dep];

			ff = (f - (float)MIDDLEC) / depfac;
			tempdep = powf(2, ff) * 256.0f;
			noteLookup->fildepTable[dep] = (int)tempdep;

			ff = (float)(exp(tkcatconst[dep] * ((float)MIDDLEC - f)) * tkcatmult[dep]);
			noteLookup->timekeyTable[dep] = (int)(ff * 256.0f);
		}
	}
	//synth->printDebug("F %f d1 %x d2 %x d3 %x d4 %x d5 %x", f, noteLookup->fildepTable[0], noteLookup->fildepTable[1], noteLookup->fildepTable[2], noteLookup->fildepTable[3], noteLookup->fildepTable[4]);
}

File *TableInitialiser::initWave(Synth *synth, NoteLookup *noteLookup, float ampsize, float div, File *file) {
	int iDiv = (int)div;
	noteLookup->waveformSize[0] = iDiv << 2;
	noteLookup->waveformSize[1] = iDiv << 2;
	noteLookup->waveformSize[2] = iDiv << 3;
	for (int i = 0; i < 3; i++) {
		if (noteLookup->waveforms[i] == NULL) {
			noteLookup->waveforms[i] = new Bit16s[noteLookup->waveformSize[i]];
		}
	}
	if (file != NULL) {
		for (int i = 0; i < 3 && file != NULL; i++) {
			size_t len = noteLookup->waveformSize[i];
			for (unsigned int j = 0; j < len; j++) {
				if (!file->readBit16u((Bit16u *)&noteLookup->waveforms[i][j])) {
					synth->printDebug("Error reading wave file cache!");
					file->close();
					file = NULL;
					break;
				}
			}
		}
	}
	if (file == NULL) {
		double sd = DOUBLE_PI / (div * 2.0);

		for (int fa = 0; fa < (iDiv << 2); fa++) {
			double sa = fa * sd;

#if 0
			//FIXME:KG: Credit Timo Strunk (bastardo on #scummvm) for help with this!
			double saw = 0.5 * DOUBLE_PI - sa / 2;
#else
			double saw = 0.0;
			for (int sinus = 1; sinus < div; sinus++) {
				double fsinus = (double)sinus;
				saw += sin(fsinus * sa) / fsinus;
			}
#endif

			// This works pretty well
			noteLookup->waveforms[0][fa] = (Bit16s)(saw * -ampsize / 2);
			noteLookup->waveforms[1][fa] = (Bit16s)(cos(sa / 2.0) * -ampsize);
			noteLookup->waveforms[2][fa * 2] = (Bit16s)(cos(sa - DOUBLE_PI) * -ampsize);
			noteLookup->waveforms[2][fa * 2 + 1] = (Bit16s)(cos((sa + (sd / 2)) - DOUBLE_PI) * -ampsize);
		}
	}
	return file;
}

static void initFiltTable(NoteLookup *noteLookup, float freq, float rate) {
	for (int tr = 0; tr <= 200; tr++) {
		float ftr = (float)tr;

		// Verified exact on MT-32
		if (tr > 100)
			ftr = 100.0f + (powf((ftr - 100.0f) / 100.0f, 3.0f) * 100.0f);

		// I think this is the one
		float brsq = powf(10.0f, (tr / 50.0f) - 1.0f);
		noteLookup->filtTable[0][tr] = (int)((freq * brsq) / (rate / 2) * FILTERGRAN);
		if (noteLookup->filtTable[0][tr]>=((FILTERGRAN*15)/16))
			noteLookup->filtTable[0][tr] = ((FILTERGRAN*15)/16);

		float brsa = powf(10.0f, ((tr / 55.0f) - 1.0f)) / 2.0f;
		noteLookup->filtTable[1][tr] = (int)((freq * brsa) / (rate / 2) * FILTERGRAN);
		if (noteLookup->filtTable[1][tr]>=((FILTERGRAN*15)/16))
			noteLookup->filtTable[1][tr] = ((FILTERGRAN*15)/16);
	}
}

static void initNFiltTable(NoteLookup *noteLookup, float freq, float rate) {
	for (int cf = 0; cf <= 100; cf++) {
		float cfmult = (float)cf;

		for (int tf = 0;tf <= 100; tf++) {
			float tfadd = (float)(tf - 0);
			if (tfadd < 0)
				tfadd = 0;

			float freqsum = expf((cfmult + tfadd) / 30.0f) / 4.0f;

			noteLookup->nfiltTable[cf][tf] = (int)((freq * freqsum) / (rate / 2) * FILTERGRAN);
			if (noteLookup->nfiltTable[cf][tf] >= ((FILTERGRAN * 15) / 16))
				noteLookup->nfiltTable[cf][tf] = ((FILTERGRAN * 15) / 16);
		}
	}
}

File *TableInitialiser::initNote(Synth *synth, NoteLookup *noteLookup, float note, float rate, float masterTune, PCMWaveEntry pcmWaves[128], File *file) {
	float ampsize = WGAMP;
	float freq = (float)(masterTune * pow(2.0, ((double)note - MIDDLEA) / 12.0));
	float div = rate / freq;
	noteLookup->div = (int)div;

	if (noteLookup->div == 0)
		noteLookup->div = 1;

	initSaw(noteLookup, noteLookup->div);
	initDep(noteLookup, note);

	//synth->printDebug("Note %f; freq=%f, div=%f", note, freq, rate / freq);
	file = initWave(synth, noteLookup, ampsize, div, file);

	// Create the pitch tables

	double rateMult = 32000.0 / rate;
	double tuner = freq * 65536.0f;
	for (int pc = 0; pc < 128; pc++) {
		noteLookup->wavTable[pc] = (int)(tuner / pcmWaves[pc].tune * rateMult);
	}

	initFiltTable(noteLookup, freq, rate);
	initNFiltTable(noteLookup, freq, rate);
	return file;
}

bool TableInitialiser::initNotes(Synth *synth, PCMWaveEntry pcmWaves[128], float rate, float masterTune) {
	const char *NoteNames[12] = {
		"C ", "C#", "D ", "D#", "E ", "F ", "F#", "G ", "G#", "A ", "A#", "B "
	};
	char filename[64];
	int intRate = (int)rate;
	char version[4] = {0, 0, 0, 3};
	sprintf(filename, "waveformcache-%d-%.2f.raw", intRate, masterTune);

	File *file = NULL;
	char header[20];
	strncpy(header, "MT32WAVE", 8);
	int pos = 8;
	// Version...
	for (int i = 0; i < 4; i++)
		header[pos++] = version[i];
	header[pos++] = (char)((intRate >> 24) & 0xFF);
	header[pos++] = (char)((intRate >> 16) & 0xFF);
	header[pos++] = (char)((intRate >> 8) & 0xFF);
	header[pos++] = (char)(intRate & 0xFF);
	int intTuning = (int)masterTune;
	header[pos++] = (char)((intTuning >> 8) & 0xFF);
	header[pos++] = (char)(intTuning & 0xFF);
	header[pos++] = 0;
	header[pos] = (char)((masterTune - intTuning) * 10);
#if MT32EMU_WAVECACHEMODE < 2
	bool reading = false;
	file = synth->openFile(filename, File::OpenMode_read);
	if (file != NULL) {
		char fileHeader[20];
		if (file->read(fileHeader, 20) == 20) {
			if (memcmp(fileHeader, header, 20) == 0) {
				Bit16u endianCheck;
				if (file->readBit16u(&endianCheck)) {
					if (endianCheck == 1) {
						reading = true;
					} else {
						synth->printDebug("Endian check in %s does not match expected", filename);
					}
				} else {
					synth->printDebug("Unable to read endian check in %s", filename);
				}
			} else {
				synth->printDebug("Header of %s does not match expected", filename);
			}
		} else {
			synth->printDebug("Error reading 16 bytes of %s", filename);
		}
		if (!reading) {
			file->close();
			file = NULL;
		}
	} else {
		synth->printDebug("Unable to open %s for reading", filename);
	}
#endif

	float progress = 0.0f;
	bool abort = false;
	synth->report(ReportType_progressInit, &progress);
	for (int f = LOWEST_NOTE; f <= HIGHEST_NOTE; f++) {
		synth->printDebug("Initialising note %s%d", NoteNames[f % 12], (f / 12) - 1);
		NoteLookup *noteLookup = &noteLookups[f - LOWEST_NOTE];
		file = initNote(synth, noteLookup, (float)f, rate, masterTune, pcmWaves, file);
		progress = (f - LOWEST_NOTE + 1) / (float)NUM_NOTES;
		abort = synth->report(ReportType_progressInit, &progress) != 0;
		if (abort)
			break;
	}

#if MT32EMU_WAVECACHEMODE == 0 || MT32EMU_WAVECACHEMODE == 2
	if (file == NULL) {
		file = synth->openFile(filename, File::OpenMode_write);
		if (file != NULL) {
			if (file->write(header, 20) == 20 && file->writeBit16u(1)) {
				for (int f = 0; f < NUM_NOTES; f++) {
					for (int i = 0; i < 3 && file != NULL; i++) {
						int len = noteLookups[f].waveformSize[i];
						for (int j = 0; j < len; j++) {
							if (!file->writeBit16u(noteLookups[f].waveforms[i][j])) {
								synth->printDebug("Error writing waveform cache file");
								file->close();
								file = NULL;
								break;
							}
						}
					}
				}
			} else {
				synth->printDebug("Error writing 16-byte header to %s - won't continue saving", filename);
			}
		} else {
			synth->printDebug("Unable to open %s for writing - won't be created", filename);
		}
	}
#endif

	if (file != NULL)
		synth->closeFile(file);
	return !abort;
}

void TableInitialiser::freeNotes() {
	for (int t = 0; t < 3; t++) {
		for (int m = 0; m < NUM_NOTES; m++) {
			if (noteLookups[m].waveforms[t] != NULL) {
				delete[] noteLookups[m].waveforms[t];
				noteLookups[m].waveforms[t] = NULL;
				noteLookups[m].waveformSize[t] = 0;
			}
		}
	}
	initialisedMasterTune = 0.0f;
}

bool TableInitialiser::initMT32Tables(Synth *synth, PCMWaveEntry pcmWaves[128], float sampleRate, float masterTune) {
	if (sampleRate <= 0.0f) {
		synth->printDebug("Bad sampleRate (%d <= 0.0f)", sampleRate);
		return false;
	}
	if (initialisedSampleRate == 0.0f) {
		initMT32ConstantTables(synth);
	}
	if (initialisedSampleRate != sampleRate) {
		initFiltCoeff(sampleRate);
		initEnvelopes(sampleRate);
	}
	if (initialisedSampleRate != sampleRate || initialisedMasterTune != masterTune) {
		freeNotes();
		if (!initNotes(synth, pcmWaves, sampleRate, masterTune)) {
			return false;
		}
		initialisedSampleRate = sampleRate;
		initialisedMasterTune = masterTune;
	}
	return true;
}

}

--- NEW FILE: tables.h ---
/* Copyright (c) 2003-2004 Various contributors
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 */

#ifndef MT32EMU_TABLES_H
#define MT32EMU_TABLES_H

namespace MT32Emu {

// Mathematical constants
const double DOUBLE_PI = 3.1415926535897932384626433832795;
const double DOUBLE_LN = 2.3025850929940456840179914546844;
const float FLOAT_PI = 3.1415926535897932384626433832795f;
const float FLOAT_LN = 2.3025850929940456840179914546844f;

// Filter settings
const int FILTERGRAN = 512;

const int MIDDLEC = 60;
const int MIDDLEA = 69; // By this I mean "A above middle C"

//FIXME:KG: may only need to do 12 to 108
//12..108 is the range allowed by note on commands, but the key can be modified by pitch keyfollow
//and adjustment for timbre pitch, so the results can be outside that range. Do move it (by octave) into
// the 12..108 range, or keep it in 0..127 range, or something else altogether?
const int LOWEST_NOTE = 12;
const int HIGHEST_NOTE = 127;
const int NUM_NOTES = HIGHEST_NOTE - LOWEST_NOTE + 1; // Number of slots for note LUT

// Amplitude of waveform generator
const int WGAMP = 7168; // 8192?

class Synth;

extern Bit16s smallnoise[MAX_SAMPLE_OUTPUT];

// Some optimization stuff
extern Bit32s keytable[217];
extern Bit16s sintable[65536];
extern Bit32u lfotable[101];
extern Bit32s penvtable[16][101];
extern Bit32s filveltable[128][101];
extern Bit32s veltkeytable[5][128];
extern Bit32s pulsetable[101];
extern Bit32s ampbiastable[13][128];
extern Bit32s fbiastable[15][128];
extern float filtcoeff[FILTERGRAN][31][8];
extern Bit32s finetable[201];
extern Bit32u lfoptable[101][101];
extern Bit32s ampveltable[128][64];
extern Bit32s pwveltable[15][128];
extern Bit32s envtimetable[101];
extern Bit32s decaytimetable[101];
extern Bit32s lasttimetable[101];
extern Bit32s voltable[128];
extern float ResonInv[31];

struct NoteLookup {
	Bit32u div;
	Bit32u wavTable[128];
	Bit32s sawTable[101];
	Bit32s fildepTable[5];
	Bit32s timekeyTable[5];
	int filtTable[2][201];
	int nfiltTable[101][101];
	Bit16s *waveforms[3];
	Bit32u waveformSize[3];
};

extern NoteLookup noteLookups[NUM_NOTES];

class TableInitialiser {
	static void initMT32ConstantTables(Synth *synth);
	static File *initWave(Synth *synth, NoteLookup *noteLookup, float ampsize, float div, File *file);
	static bool initNotes(Synth *synth, PCMWaveEntry pcmWaves[128], float rate, float tuning);
public:
	static bool initMT32Tables(Synth *synth, PCMWaveEntry pcmWaves[128], float sampleRate, float masterTune);
	static File *initNote(Synth *synth, NoteLookup *noteLookup, float note, float rate, float tuning, PCMWaveEntry pcmWaves[128], File *file);
	static void freeNotes();
};

}

#endif





More information about the Scummvm-git-logs mailing list