[Scummvm-cvs-logs] CVS: scummvm/backends/midi/mt32 file.cpp,NONE,1.1 file.h,NONE,1.1 i386.cpp,NONE,1.1 i386.h,NONE,1.1 module.mk,NONE,1.1 mt32emu.h,NONE,1.1 part.cpp,NONE,1.1 part.h,NONE,1.1 partialManager.cpp,NONE,1.1 partialManager.h,NONE,1.1 tables.cpp,NONE,1.1 tables.h,NONE,1.1 partial.cpp,1.4,1.5 partial.h,1.3,1.4 structures.h,1.4,1.5 synth.cpp,1.17,1.18 synth.h,1.4,1.5 mt32.cpp,1.5,NONE

Eugene Sandulenko sev at users.sourceforge.net
Fri Nov 5 17:46:46 CET 2004


Update of /cvsroot/scummvm/scummvm/backends/midi/mt32
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv27409/backends/midi/mt32

Modified Files:
	partial.cpp partial.h structures.h synth.cpp synth.h 
Added Files:
	file.cpp file.h i386.cpp i386.h module.mk mt32emu.h part.cpp 
	part.h partialManager.cpp partialManager.h tables.cpp tables.h 
Removed Files:
	mt32.cpp 
Log Message:
Major MT-32 emu overhaul based on KingGuppy's code.
 o added configure option
 o mi2 intro doesn't freeze anymore and has no sound glitches
 o missing instruments in many titles are fixed
 o numerous memory overwrite bugs are fixed
 o code is cleaned a lot and splitted into many smaller files
 o mt32.cpp went to backends/midi
 o synced with upstream code
 o reverberation fixed

 * don't complain about File class wrapper :)
 * all custom types are back
 * #pragmas are to do
 * maybe some indentation is wrong too

I prefer smaller commits, but this thing came in one piece.


--- NEW FILE: 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 "file.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);
	}

	int ANSIFile::readByte() {
		return fgetc(fp);
	}

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

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

	bool ANSIFile::writeByte(unsigned char out) {
		return fputc(out, fp) != EOF;
	}

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

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

--- NEW FILE: 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 *ptr, size_t size) = 0;
	virtual bool readLine(char *ptr, size_t size) = 0;
	virtual size_t write(const void *ptr, size_t size) = 0;
	// Returns -1 in case of EOF or error
	virtual int readByte() = 0;
	virtual bool writeByte(unsigned char out) = 0;
	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 *ptr, size_t size);
	bool readLine(char *ptr, size_t size);
	size_t write(const void *, size_t size);
	int readByte();
	bool writeByte(unsigned char out);
	bool isEOF();
};

}

#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 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 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 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 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 := backends/midi/mt32

MODULE_OBJS := \
	backends/midi/mt32/file.o \
	backends/midi/mt32/i386.o \
	backends/midi/mt32/part.o \
	backends/midi/mt32/partial.o \
	backends/midi/mt32/partialManager.o \
	backends/midi/mt32/synth.o \
	backends/midi/mt32/tables.o \
	backends/midi/mt32/freeverb.o


MODULE_DIRS += \
	backends/midi/mt32

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

--- 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

#include "freeverb.h"

#include "structures.h"
#include "i386.h"
#include "file.h"
#include "partial.h"
#include "partialManager.h"
#include "part.h"
#include "tables.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"

// Debugging stuff
// Shows the instruments played
#define DISPLAYINSTR 1

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 Bit32u drumBend = 0x1000;

// This caches the timbres/settings in use by the rhythm part
static PatchCache drumCache[94][4];

static volset drumPan[64];

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

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

Part::Part(Synth *useSynth, int usePartNum) {
	this->synth = useSynth;
	this->partNum = usePartNum;
	isRhythm = (usePartNum == 8);
	holdpedal = false;
	if (isRhythm) {
		strcpy(name, "Rhythm");
		patchTemp = NULL;
		timbreTemp = NULL;
		rhythmTemp = &synth->mt32ram.params.rhythmSettings[0];
	} else {
		sprintf(name, "Part %d", partNum + 1);
		patchTemp = &synth->mt32ram.params.patchSettings[partNum];
		timbreTemp = &synth->mt32ram.params.timbreSettings[partNum];
		rhythmTemp = NULL;
	}
	currentInstr[0] = 0;
	volume = 102;
	volumesetting.leftvol = 32767;
	volumesetting.rightvol = 32767;
	bend = 0x1000;
	memset(polyTable,0,sizeof(polyTable));
	memset(patchCache, 0, sizeof(patchCache));

	if (isRhythm) {
		init = true;
		RefreshDrumCache();
	}
	init = false;
}

void Part::SetHoldPedal(bool pedalval) {
	if (holdpedal && !pedalval)
		StopPedalHold();
	holdpedal = pedalval;
}

void Part::SetBend(int vol) {
	if (isRhythm) {
		synth->printDebug("%s: Setting bend (%d) not supported on rhythm", name, vol);
		return;
	}
	//int tmpbend = ((vol - 0x2000) * (int)patchTemp->patch.benderRange) >> 13;
	//bend = bendtable[tmpbend+24];

	float bend_range = (float)patchTemp->patch.benderRange / 24;
	bend = (Bit32u)(4096 + (vol - 8192) * bend_range);
}

void Part::SetModulation(int vol) {
	if (isRhythm) {
		synth->printDebug("%s: Setting modulation (%d) not supported on rhythm", name, vol);
		return;
	}
	// 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 * vol) >> 7;
			//patchCache[t].lfoperiod = lfotable[newrate];
			patchCache[t].lfodepth = newrate;
			//FIXME:KG: timbreTemp->partial[t].lfo.depth =
		}
	}
}

void Part::RefreshDrumCache() {
	if (!isRhythm) {
		synth->printDebug("ERROR: RefreshDrumCache() called on non-rhythm part");
	}
	// Cache drum patches
	for (int m = 0; m < 64; m++) {
		int drumTimbre = rhythmTemp[m].timbre;
		if (drumTimbre >= 94)
			continue;
		SetPatch(drumTimbre + 128); // This is to cache all the mapped drum timbres ahead of time
		Bit16s pan = rhythmTemp[m].panpot; // They use R-L 0-14...
		// FIXME:KG: If I don't have left/right mixed up here, it's pure luck
		if (pan < 7) {
			drumPan[m].leftvol = 32767;
			drumPan[m].rightvol = pan * 4681;
		} else {
			drumPan[m].rightvol = 32767;
			drumPan[m].leftvol = (14 - pan) * 4681;
		}
	}
}

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

	return outnote;
}

int Part::FixKeyfollow(int srckey, int *dir) {
	if (srckey>=0 && srckey<=16) {
		//int keyfix[17] = { 256, 128, 64, 0, 32, 64, 96, 128, 128+32, 192, 192+32, 256, 256+64, 256+128, 512, 259, 269 };
		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};

		if (srckey<3)
			*dir = -1;
		else if (srckey==3)
			*dir = 0;
		else
			*dir = 1;

		return keyfix[srckey];
	} else {
		//LOG(LOG_ERROR|LOG_MISC,"Missed key: %d", srckey);
		return 256;
	}
}

void Part::RefreshPatch() {
	SetPatch(-1);
}

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(PatchParam *patch) {
	patchTemp->patch = *patch;
}

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

unsigned int Part::getAbsTimbreNum() {
	if (isRhythm) {
		synth->printDebug("%s: Attempted to call getAbsTimbreNum() - doesn't make sense for rhythm");
		return 0;
	}
	return (patchTemp->patch.timbreGroup * 64) + patchTemp->patch.timbreNum;
}

void Part::SetPatch(int patchNum) {
	int pcm;
	int absTimbreNum = -1; // Initialised to please compiler
	TimbreParam timSrc;
	if (isRhythm) {
		// "patchNum" is treated as "timbreNum" for rhythm part
		if (patchNum < 128) {
			synth->printDebug("%s: Patch #%d is not valid for rhythm (must be >= 128)", name, patchNum);
			return;
		}
		absTimbreNum = patchNum;
		timSrc = synth->mt32ram.params.timbres[absTimbreNum].timbre;
	} else {
		if (patchNum >= 0) {
			setPatch(&synth->mt32ram.params.patches[patchNum]);
		}
		if (patchNum >= 0) {
			setTimbre(&synth->mt32ram.params.timbres[getAbsTimbreNum()].timbre);
		}
		timSrc = *timbreTemp;
#if 0
		// Immediately stop all partials on this part (this is apparently *not* the correct behaviour)
		for (int m = 0; m < MAXPOLY; m++) {
			AbortPoly(poly);
		}
#else
		// check if any partials are still playing on this part
		// 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.
		// Hopefully this is fairly rare.
		for (int m = 0; m < MAXPOLY; m++) {
			for (int i = 0; i < 4; i++) {
				Partial *partial = polyTable[m].partials[i];
				if (partial != NULL) {
					// copy cache data
					partial->cachebackup = patchCache[i];
					// update pointers
					partial->patchCache = &partial->cachebackup;
				}
			}
		}
#endif
	}

	memcpy(currentInstr, timSrc.common.name, 10);
	currentInstr[10] = 0;

	int partialCount = 0;
	for (int t=0;t<4;t++) {
		if ( ((timSrc.common.pmute >> (t)) & 0x1) == 1 ) {
			patchCache[t].playPartial = true;
			partialCount++;
		} else {
			patchCache[t].playPartial = false;
			continue;
		}

		// Calculate and cache common parameters

		pcm = timSrc.partial[t].wg.pcmwave;

		patchCache[t].pcm = timSrc.partial[t].wg.pcmwave;
		patchCache[t].useBender = (timSrc.partial[t].wg.bender == 1);

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

		patchCache[t].waveform = timSrc.partial[t].wg.waveform;
		patchCache[t].pulsewidth = timSrc.partial[t].wg.pulsewid;
		patchCache[t].pwsens = timSrc.partial[t].wg.pwvelo;
		patchCache[t].pitchkeyfollow = FixKeyfollow(timSrc.partial[t].wg.keyfollow, &patchCache[t].pitchkeydir);

		// Calculate and cache pitch stuff
		patchCache[t].pitchshift = timSrc.partial[t].wg.coarse;
		Bit32s pFine, fShift;
		pFine = (Bit32s)timSrc.partial[t].wg.fine;
		if (isRhythm) {
			patchCache[t].pitchshift += 24;
			fShift = pFine + 50;
		} else {
			patchCache[t].pitchshift += patchTemp->patch.keyShift;
			fShift = pFine + (Bit32s)patchTemp->patch.fineTune;
		}
		patchCache[t].fineshift = finetable[fShift];

		patchCache[t].pitchEnv = timSrc.partial[t].env;
		patchCache[t].pitchEnv.sensitivity = (char)((float)patchCache[t].pitchEnv.sensitivity*1.27);
		patchCache[t].pitchsustain = patchCache[t].pitchEnv.level[3];

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

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

		// Calculate and cache filter stuff
		patchCache[t].filtEnv = timSrc.partial[t].tvf;
		patchCache[t].tvfdepth = patchCache[t].filtEnv.envdkf;
		patchCache[t].filtkeyfollow  = FixKeyfollow(patchCache[t].filtEnv.keyfollow, &patchCache[t].keydir);
		patchCache[t].filtEnv.envdepth = (char)((float)patchCache[t].filtEnv.envdepth * 1.27);
		patchCache[t].tvfbias = FixBiaslevel(patchCache[t].filtEnv.biaspoint, &patchCache[t].tvfdir);
		patchCache[t].tvfblevel = patchCache[t].filtEnv.biaslevel;
		patchCache[t].filtsustain  = patchCache[t].filtEnv.envlevel[3];

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

	if (isRhythm)
		memcpy(drumCache[absTimbreNum - 128], patchCache, sizeof(patchCache));
	else
		AllStop();
#if DISPLAYINSTR == 1
	synth->printDebug("%s: Recache, param %d (timbre: %s), %d partials", name, patchNum, currentInstr, partialCount);
	for (int i = 0; i < 4; i++) {
		synth->printDebug(" %d: play=%s, pcm=%d, wave=%d", i, patchCache[i].playPartial ? "YES" : "NO", timSrc.partial[i].wg.pcmwave, timSrc.partial[i].wg.waveform);
	}
#endif
}

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

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

void Part::SetPan(int pan) {
	// FIXME:KG: This is unchangeable for drums (they always use drumPan), is that correct?
	// FIXME:KG: There is no way to get a centred balance here... And the middle two
	// pan settings have a distance of 1024, double the usual.
	// Perhaps we should multiply by 516? 520? 520.111..?
	// KG: Changed to 516, to mostly eliminate that jump in the middle
	if (pan < 64) {
		volumesetting.leftvol = 32767;
		volumesetting.rightvol = (Bit16s)(pan * 516);
	} else {
		volumesetting.rightvol = 32767;
		volumesetting.leftvol = (Bit16s)((127 - pan) * 516);
	}
	//synth->printDebug("%s (%s): Set pan to %d", name, currentInstr, panpot);
}

void Part::PlayNote(PartialManager *partialManager, int f, int vel) {
	int drumNum = -1; // Initialised to please compiler
	int drumTimbre = -1; // As above
	int freqNum;

	if (isRhythm) {
		if (f < 24 || f > 87) {
			synth->printDebug("%s: Attempted to play invalid note %d", name, f);
			return;
		}
		drumNum = f - 24;
		drumTimbre = rhythmTemp[drumNum].timbre;
		if (drumTimbre >= 94) {
			synth->printDebug("%s: Attempted to play unmapped note %d!", name, f);
			return;
		}
		memcpy(patchCache, drumCache[drumTimbre], sizeof(patchCache));
		memcpy(&currentInstr, synth->mt32ram.params.timbres[128 + drumTimbre].timbre.common.name, 10);
		currentInstr[10] = 0;
		freqNum = MIDDLEC;
	} else {
		if (f < 12 || f > 108) {
			synth->printDebug("%s (%s): Attempted to play invalid note %d", name, currentInstr, f);
			return;
		}
		freqNum = f;
	}
	// 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?
	if (!isRhythm) {
		for (int i = 0; i < MAXPOLY; i++) {
			if (polyTable[i].isActive() && (polyTable[i].note == f)) {
				//AbortPoly(&polyTable[i]);
				StopNote(f);
				break;
			}
		}
	}

	unsigned int needPartials = patchCache[0].partialCount;

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

	dpoly *tpoly = &polyTable[m];
	Bit16s freq = freqtable[freqNum];

	tpoly->isPlaying = true;
	tpoly->note = f;
	tpoly->isDecay = false;
	tpoly->freq = freq;
	tpoly->freqnum = freqNum;
	tpoly->vel = vel;
	tpoly->pedalhold = false;

	bool allnull = true;
	for (int x = 0; x < 4; x++) {
		if (patchCache[x].playPartial) {
			tpoly->partials[x] = 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);

	if (isRhythm) {
		tpoly->bendptr = &drumBend;
		tpoly->pansetptr = &drumPan[drumNum];
		tpoly->reverb = rhythmTemp[drumNum].reverbSwitch > 0;
	} else {
		tpoly->bendptr = &bend;
		tpoly->pansetptr = &volumesetting;
		tpoly->reverb = patchTemp->patch.reverbSwitch > 0;
	}
	tpoly->sustain = patchCache[0].sustain;
	tpoly->volumeptr = &volume;

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

#if DISPLAYINSTR == 1
	if (isRhythm) {
		synth->printDebug("%s (%s): starting note poly %d (drum %d, timbre %d) - Vel %d Vol %d", name, currentInstr, m, drumNum, drumTimbre, vel, volume);
	} else {
		synth->printDebug("%s (%s): starting note poly %d - Vel %d Freq %d Vol %d", name, currentInstr, m, vel, f, volume);
	}
#endif
}

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 < MAXPOLY; q++) {
		dpoly *tpoly = &polyTable[q];
		if (tpoly->isPlaying) {
			StartDecayPoly(tpoly);
		}
	}
}

void Part::StopPedalHold() {
	for (int q = 0; q < MAXPOLY; q++) {
		dpoly *tpoly;
		tpoly = &polyTable[q];
		if (tpoly->isActive() && tpoly->pedalhold)
			StopNote(tpoly->note);
	}
}

void Part::StopNote(int f) {
	// Non-sustaining instruments ignore stop note commands.
	// They die away eventually anyway
	//if (!tpoly->sustain) return;

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

	if (f != -1) {
		for (int q = 0; q < MAXPOLY; q++) {
			dpoly *tpoly = &polyTable[q];
			if (tpoly->isPlaying && tpoly->note == f) {
				if (holdpedal)
					tpoly->pedalhold = true;
				else if (tpoly->sustain)
					StartDecayPoly(tpoly);
			}
		}
		return;
	}

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

	for (int q = 0; q < MAXPOLY; 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

#define AMPENV 0
#define FILTENV 1
#define PITCHENV 2

#define MAXPOLY 64

namespace MT32Emu {

class PartialManager;
class Synth;

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

	// Pointers to the areas of the MT-32's memory dedicated to this part (for parts 1-8)
	MemParams::PatchTemp *patchTemp;
	TimbreParam *timbreTemp;
	//... and for rhythm
	MemParams::RhythmTemp *rhythmTemp;

	bool isRhythm;
	bool init;
	int partNum;

	char name[8]; // "Part 1".."Part 8", "Rhythm"
	char currentInstr[11];

	bool holdpedal;

	volset volumesetting;

	PatchCache patchCache[4];

	Bit32u bend;
	Bit32s volume;

	dpoly polyTable[MAXPOLY];

	void AbortPoly(dpoly *poly);

public:
	Part(Synth *synth, int usePartNum);
	char *getName();
	void PlayNote(PartialManager *partialManager, int f, int vel);
	void StopNote(int f);
	void AllStop();
	void SetVolume(int vol);
	void SetPan(int vol);
	void SetBend(int vol);
	void SetModulation(int vol);
	void SetPatch(int patchnum);
	void SetHoldPedal(bool pedalval);
	void StopPedalHold();
	void RefreshPatch();
	void RefreshDrumCache();
	void setPatch(PatchParam *patch);
	void setTimbre(TimbreParam *timbre);
	unsigned int getAbsTimbreNum();

	int FixKeyfollow(int srckey, int *dir);
	int FixBiaslevel(int srcpnt, int *dir);
};

}
#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 < MAXPARTIALS; i++)
		partialTable[i] = new Partial(synth);
}

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

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

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

void PartialManager::AgeAll() {
	for (int i = 0; i < MAXPARTIALS; 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 < MAXPARTIALS; i++) {
		partialTable[i]->deactivate();
	}
}

unsigned int PartialManager::SetReserve(char *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 < MAXPARTIALS; 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 < MAXPARTIALS; 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.params.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.params.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.params.system.reserveSettings[selectPart]) {
				oldest = -1;
				oldnum = -1;
				found = false;
				for (i = 0; i < MAXPARTIALS; 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 < MAXPARTIALS; 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 < MAXPARTIALS; 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) {
		Bit64s prior = -1;
		int priornum = -1;

		for (int i = 0; i < MAXPARTIALS; i++) {
			if (PartialReserveTable[i] == partNum && partialTable[i]->isActive() && partialTable[i]->getOwnerPart() != partNum) {
				/*
				if (mt32ram.params.system.reserveSettings[partialTable[i]->ownerPart] < prior) {
					prior = mt32ram.params.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) {
		Bit64s oldest = -1;
		Bit64s oldlist = -1;
		for (int i = 0; i < MAXPARTIALS; i++) {
			if (partialTable[i]->isActive()) {
				if (partialTable[i]->getOwnerPart() == partNum) {
					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

#define MAXPARTIALS 32

namespace MT32Emu {

class Synth;

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

	Partial *partialTable[MAXPARTIALS];
	Bit32s PartialReserveTable[MAXPARTIALS];
	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(char *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: 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"

// Determines how the waveform cache file is handled (must be regenerated after sampling rate change)
#define WAVECACHEMODE 0 // Load existing cache if possible, otherwise generate and save cache
//#define WAVECACHEMODE 1 // Load existing cache if possible, otherwise generage but don't save cache
//#define WAVECACHEMODE 2 // Ignore existing cache, generate and save cache
//#define WAVECACHEMODE 3 // Ignore existing cache, generate but don't save cache

// Constant tuning for now
#define TUNING 440.0f

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};

const Bit8s LoopPatterns[9][10] = {
	{ 2,  3, 4,   5,  6,  7, -1, -1, -1, -1},
	{ 8,  9, 10, 11, 12, 13, 14, 15, 16, -1},
	{17, 18, 19, 20, 21, -1, -1, -1, -1, -1},
	{22, 23, 24, 25, 26, 27, 28, 29, -1, -1},
	{30, 31, 32, 33, 34, 35, 36, 37, -1, -1},
	{45, 46, 47, 48, 49, 50, 51, 52, 53, -1},
	{15, 11, 12, 13, 14, 15, 16, -1, -1, -1},
	{30, 35, 32, 33, 34, -1, -1, -1, -1, -1},
	{ 2,  3, -1, -1, -1, -1, -1, -1, -1, -1},
};

static const Bit32s LoopPatternTuning[9][10] = {
	{0x1294A, 0x1294A, 0x1294A, 0x1294A, 0x1294A, 0x1294A,      -1,      -1,      -1,      -1},
	{0x1294A, 0x1294A, 0x1294A, 0x1294A, 0x1294A, 0x1294A, 0x1294A, 0x1294A, 0x1294A,      -1},
	{0x1294A, 0x1294A, 0x1294A, 0x1294A, 0x1294A,      -1,      -1,      -1,      -1,      -1},
	{0x1294A, 0x1294A, 0x1294A, 0x1294A, 0x1294A, 0x1294A, 0x1294A, 0x1294A,      -1,      -1},
	{0x1294A, 0x1294A, 0x1294A, 0x1294A, 0x1294A, 0x1294A, 0x1294A, 0x1294A,      -1,      -1},
	{0x1294A, 0x1294A, 0x1294A, 0x1294A, 0x1294A, 0x1294A, 0x1294A, 0x1294A, 0x1294A,      -1},
	{0x2590B, 0x1294A, 0x1294A, 0x1294A, 0x1294A, 0x1294A, 0x1294A,      -1,      -1,      -1},
	{0x1294A, 0x1294A, 0x1294A, 0x1294A, 0x1294A,      -1,      -1,      -1,      -1,      -1},
	{0x1294A, 0x1294A,      -1,      -1,      -1,      -1,      -1,      -1,      -1,      -1},
};

// 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;

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];
Bit32s bendtable[49];
float ResonFactor[31];
float ResonInv[31];

// Per-note initialisation tables follow

Bit16s freqtable[NUMNOTES];
Bit32s fildeptable[5][NUMNOTES];
Bit32s timekeytable[5][NUMNOTES];
int filttable[2][NUMNOTES][201];
int nfilttable[NUMNOTES][101][101];

Bit32s divtable[NUMNOTES];
Bit32s smalldivtable[NUMNOTES];
Bit32u wavtabler[54][NUMNOTES];
Bit32u looptabler[9][10][NUMNOTES];
Bit32s sawtable[NUMNOTES][101];

Bit16s *waveforms[4][NUMNOTES];
Bit32u waveformsize[4][NUMNOTES];
static Bit16s tmpforms[4][65536];


// 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(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) / (pow(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);
	}
	for (lf = 0; lf <= 48; lf++)
		bendtable[lf] = (int)((powf(2.0f, (((float)lf / 12.0f) - 2.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] = (int)(127.0 * pow((float)lf / 127.0, LN));
	}
	for (lf = 0; lf < MAX_SAMPLE_OUTPUT; lf++) {
		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[lf] = (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(int f, Bit32s div) {
	for (int rsaw = 0; rsaw <= 100; rsaw++) {
		float fsaw;
		if (rsaw < 50)
			fsaw = 50.0f;
		else
			fsaw = (float)rsaw;
		int tmpdiv = div << 1;

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

static void initDep(int f) {
	for (int dep = 0; dep < 5; dep++) {
		if (dep == 0) {
			fildeptable[dep][f] = 256;
			timekeytable[dep][f] = 256;
		} else {
			float depfac = 3000.0f;
			float ff, tempdep;
			depfac = (float)depexp[dep];

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

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

void TableInitialiser::initWave(Synth *synth, File *file, bool reading, bool writing, int f, float freq, float rate, double ampsize, Bit32s div) {
	double sd = (2.0*PI)/((((float)div/65536.0)) * 4.0);
	waveformsize[0][f] = div >> 14;
	waveformsize[1][f] = div >> 14;
	waveformsize[2][f] = div >> 14;
	waveformsize[3][f] = div >> 13;
	for (int i = 0; i < 4; i++)
		waveforms[i][f] = new Bit16s[waveformsize[i][f]];
	if (reading) {
		for (int i = 0; i < 4; i++) {
			size_t len = waveformsize[i][f] * sizeof(Bit16s);
			if (file->read(waveforms[i][f], len) != len) {
				synth->printDebug("Error reading wave file cache!");
				break;
			}
		}
	} else {
		float dumbfire;
		double saw = 0.0f;
		double sa = 0.0;
		int fa = 0;
		memset(tmpforms, 0,sizeof(tmpforms));

		while (sa <= (2.0 * PI)) {
			float sqp;

			if (sa < PI) {
				sqp = -1;
				sqp = (float)(sqp + (0.25 * (sa/PI)));
			} else {
				sqp=1;
				sqp = (float)(sqp - (0.25 * ((sa-PI)/PI)));
			}

			saw = 0;
			for (int sinus = 1; sinus * freq < rate; sinus++) {
				double fsinus = (double)sinus;
				saw += (1 / fsinus) * sin(fsinus * sa);
			}

			dumbfire = (float)(sa / 2);

			//This works pretty good
			tmpforms[0][fa] = (Bit16s)(saw * -ampsize / 2);
			tmpforms[1][fa] = (Bit16s)(saw * ampsize / 2);

			tmpforms[2][fa] = (Bit16s)(cos(dumbfire) * -ampsize);
			tmpforms[3][fa * 2] = (Bit16s)(cos(sa - PI) * -ampsize);
			tmpforms[3][fa * 2 + 1] = (Bit16s)(cos((sa + (sd / 2)) - PI) * -ampsize);

			fa++;
			sa += sd;
		}

		//synth->printDebug("f num %d freq %f and fa %d, div>>13=%d", f, freq, fa, div>>13);

		for (int i = 0; i < 4; i++)
			memcpy(waveforms[i][f], tmpforms[i], waveformsize[i][f] * sizeof (Bit16s));

		if (writing) {
			for (int i = 0; i < 4; i++) {
				int len = waveformsize[i][f] * sizeof(Bit16s);
				if (!file->write(waveforms[i][f], len)) {
					synth->printDebug("Error writing waveform cache file");
					break;
				}
			}
		}
	}
}

static void initFiltTable(int f, 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);
		filttable[0][f][tr] = (int)((freq * brsq) / (rate / 2) * FILTERGRAN);
		if (filttable[0][f][tr]>=((FILTERGRAN*15)/16))
			filttable[0][f][tr] = ((FILTERGRAN*15)/16);

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

static void initNFiltTable(int f, 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;

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

void TableInitialiser::initNotes(Synth *synth, sampleFormat pcms[54], float rate) {
	char filename[32];
	int intRate = (int)rate;
	sprintf(filename, "waveformcache-%d.raw", intRate);

	File *file = NULL;
	bool reading = false, writing = false;
	char header[16];
	strncpy(header, "MT32WAVE", 8);
	int pos = 8;
	// Version...
	for (int i = 0; i < 4; i++)
		header[pos++] = 0;
	header[pos++] = (char)(intRate & 0xFF);
	header[pos++] = (char)((intRate >> 8) & 0xFF);
	header[pos++] = (char)((intRate >> 16) & 0xFF);
	header[pos] = (char)((intRate >> 24) & 0xFF);
#if WAVECACHEMODE < 2
	file = synth->openFile(filename, File::OpenMode_read);
	if (file != NULL) {
		char fileHeader[16];
		if (file->read(fileHeader, 16) == 16) {
			if (memcmp(fileHeader, header, 16) == 0) {
				reading = true;
			} else {
				synth->printDebug("Header of %s does not match expected - will generate", filename);
			}
		} else {
			synth->printDebug("Error reading 16 bytes of %s - will generate", filename);
		}
	} else {
		synth->printDebug("Unable to open %s for reading - will generate", filename);
	}
#endif
#if WAVECACHEMODE == 0 || WAVECACHEMODE == 2
	if (!reading) {
		file = synth->openFile(filename, File::OpenMode_write);
		if (file != NULL) {
			if (file->write(header, 16) == 16) {
				writing = true;
			} 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

	double ampsize = WGAMP;
	for (int f = 12; f < 109; f++) {
		float freq = (float)(TUNING * pow(2.0, ((double)f - 69.0) / 12.0));
		freqtable[f] = (Bit16s)freq;
		divtable[f] = (int)(rate / freq);
		smalldivtable[f] = divtable[f] << 8;
		divtable[f] = divtable[f] << 16;

		initSaw(f, divtable[f]);
		initDep(f);

		//synth->printDebug("F %d sd %f div %d", f, sd, divtable[f]);
		initWave(synth, file, reading, writing, f, freq, rate, ampsize, divtable[f]);

		// Create the pitch tables

		float tuner = (32000.0f / rate) * 65536.0f;
		for (int pc = 0; pc < 54; pc++)
			wavtabler[pc][f] = (int)(tuner * (freq / pcms[pc].tune));
		for (int lp = 0; lp < 9; lp++) {
			for (int ln = 0; ln < 10; ln++) {
				// FIXME:KG: Surely this needs to be adjusted for the rate?
				looptabler[lp][ln][f] = (int)((float)LoopPatternTuning[lp][ln] * (freq / 220.0f));
			}
		}

		initFiltTable(f, freq, rate);
		initNFiltTable(f, freq, rate);
	}
	if (file != NULL)
		synth->closeFile(file);
}

void TableInitialiser::initMT32Tables(Synth *synth, sampleFormat pcms[54], float sampleRate) {
	if (sampleRate <= 0.0f) {
		synth->printDebug("Bad sampleRate (%d <= 0.0f)", sampleRate);
	}
	if (initialisedSampleRate == 0.0f) {
		initMT32ConstantTables(synth);
		initFiltCoeff(sampleRate);
		initEnvelopes(sampleRate);
		initialisedSampleRate = sampleRate;
	}
	// This always needs to be done, to allocate the waveforms
	initNotes(synth, pcms, sampleRate);
}

}

--- 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

// Mathematical constants
#ifndef PI
#define PI 3.1415926535897932384626433832795
#endif
#ifndef LN
#define LN 2.30258509
#endif

// Filter settings
#define FILTERGRAN 512

#define MIDDLEC 60

#define NUMNOTES 128 // MIDI supports 128 notes/keys

// Amplitude of waveform generator
#define WGAMP (7168)
//#define WGAMP (8192)

namespace MT32Emu {

class Synth;

extern Bit16s smallnoise[MAX_SAMPLE_OUTPUT];

// Some optimization stuff
extern Bit32s keytable[217];
extern Bit32s divtable[NUMNOTES];
extern Bit32s smalldivtable[NUMNOTES];
extern Bit32u wavtabler[54][NUMNOTES];
extern Bit32u looptabler[9][10][NUMNOTES];
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 sawtable[NUMNOTES][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];

extern Bit16s freqtable[NUMNOTES];
extern Bit32s fildeptable[5][NUMNOTES];
extern Bit32s timekeytable[5][NUMNOTES];
extern int filttable[2][NUMNOTES][201];
extern int nfilttable[NUMNOTES][101][101];

extern const Bit8s LoopPatterns[9][10];

extern Bit16s *waveforms[4][NUMNOTES];
extern Bit32u waveformsize[4][NUMNOTES];

class TableInitialiser {
	void initMT32ConstantTables(Synth *synth);
	void initWave(Synth *synth, File *file, bool reading, bool writing, int f, float freq, float rate, double ampsize, Bit32s div);
	void initNotes(Synth *synth, sampleFormat pcms[54], float rate);
public:
	void initMT32Tables(Synth *synth, sampleFormat pcms[54], float sampleRate);
};

}

#endif

Index: partial.cpp
===================================================================
RCS file: /cvsroot/scummvm/scummvm/backends/midi/mt32/partial.cpp,v
retrieving revision 1.4
retrieving revision 1.5
diff -u -d -r1.4 -r1.5
--- partial.cpp	24 Oct 2004 19:21:38 -0000	1.4
+++ partial.cpp	6 Nov 2004 01:41:30 -0000	1.5
@@ -1,601 +1,884 @@
-/* ScummVM - Scumm Interpreter
- * Copyright (C) 2004 The ScummVM project
- * Based on Tristan's conversion of Canadacow's code
+/* Copyright (c) 2003-2004 Various contributors
  *
- * 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,
[...1319 lines suppressed...]
+	tStat->envbase = startval;
+
+	switch(envnum) {
+	case AMPENV:
+		tStat->envsize = (decaytimetable[(int)patchCache->ampEnv.envtime[4]] * timekeytable[(int)patchCache->ampEnv.envtkf][poly->freqnum]) >> 8;
+		tStat->envdist = -startval;
+		break;
+	case FILTENV:
+		tStat->envsize = (decaytimetable[(int)patchCache->filtEnv.envtime[4]] * timekeytable[(int)patchCache->filtEnv.envtkf][poly->freqnum]) >> 8;
+		tStat->envdist = -startval;
+		break;
+	case PITCHENV:
+		tStat->envsize = (decaytimetable[(int)patchCache->pitchEnv.time[3]] * timekeytable[(int)patchCache->pitchEnv.timekeyfollow][poly->freqnum]) >> 8 ;
+		tStat->envdist = patchCache->pitchEnv.level[4] - startval;
+		break;
+	default:
+		break;
+	}
+	tStat->envsize++;
+};

Index: partial.h
===================================================================
RCS file: /cvsroot/scummvm/scummvm/backends/midi/mt32/partial.h,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -d -r1.3 -r1.4
--- partial.h	22 Oct 2004 15:56:39 -0000	1.3
+++ partial.h	6 Nov 2004 01:41:30 -0000	1.4
@@ -1,115 +1,124 @@
-/* ScummVM - Scumm Interpreter
- * Copyright (C) 2004 The ScummVM project
- * Based on Tristan's conversion of Canadacow's code
+/* Copyright (c) 2003-2004 Various contributors
  *
- * 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.
+ * 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:
  *
- * $Header$
+ * 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 CPARTIALMT32_H
-#define CPARTIALMT32_H
+#ifndef MT32EMU_PARTIAL_H
+#define MT32EMU_PARTIAL_H
 
-#include "backends/midi/mt32/structures.h"
+namespace MT32Emu {
+
+struct envstatus {
+	Bit32s envpos;
+	Bit32s envstat;
+	Bit32s envbase;
+	Bit32s envdist;
+	Bit32s envsize;
+
+	bool sustaining;
+	bool decaying;
+	Bit32s prevlevel;
+
+	Bit32s counter;
+	Bit32s count;
+};
+
+class Synth;
 
 // Class definition of MT-32 partials.  32 in all.
-class CPartialMT32 {
+class Partial {
 private:
-	int useMix;
-	int partNum;
+	Synth *synth; // Only used for sending debug output
 
-	int pN;
+	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
+	int noteVal;
 
-	int16 myBuffer[2048];
-	// For temporary output of paired buffer
-	int16 pairBuffer[2048];
+	// Keyfollowed filter values
+	int realVal;
+	int filtVal;
 
-	void mixBuffers(int16 * buf1, int16 * buf2, int len);
-	void mixBuffersRingMix(int16 * buf1, int16 * buf2, int len);
-	void mixBuffersRing(int16 * buf1, int16 * buf2, int len);
-	void mixBuffersStereo(int16 * buf1, int16 * buf2, int16 * outBuf, int len);
+	envstatus envs[3];
 
+	int pulsewidth;
 
-public:
-	patchCache *tcache;
-	patchCache cachebackup[4];
+	Bit32u lfoPos;
+	soundaddr partialOff;
 
-	//FILE *fp;
-	//FILE *fp2;
+	Bit32u ampEnvCache;
+	Bit32u pitchEnvCache;
 
-	dpoly::partialStatus *partCache;
+	float history[32];
 
-	CPartialMT32 *tibrePair;
-	bool isActive;
-	bool alreadyOutputed;
-	int ownerChan;
-	int64 age;
-	int timbreNum;
-	dpoly *tmppoly;
+	bool pitchSustain;
 
-	CPartialMT32(int partialNum) {
+	int loopPos;
 
-		isActive = false;		
-		pN = partialNum;
+	dpoly *poly;
 
-		/*
-		sprintf(buffer, "partial%d.raw",pN);
-		fp = fopen(buffer,"wb");
+	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);
 
-		sprintf(buffer, "partial%dx.raw",pN);
-		fp2 = fopen(buffer,"wb");
-		*/
+	Bit32s getFiltEnvelope();
+	Bit32s getAmpEnvelope();
+	Bit32s getPitchEnvelope();
 
-		
-	};
-	
-	void startPartial(dpoly *usePoly, patchCache *useCache, dpoly::partialStatus * usePart, CPartialMT32 * pairPart, int mixType, int num, int ownChan, int timNum) {
+	void initKeyFollow(int freqNum);
 
-		//LOG_MSG("Starting partial %d for %d", num, ownChan);
-		tmppoly = usePoly;
-		tcache = useCache;
-		partCache = usePart;
-		tibrePair = pairPart;
-		isActive = true;
-		useMix = mixType;
-		partNum = num;
-		age = 0;
-		ownerChan = ownChan;
-		alreadyOutputed = false;
-		timbreNum = timNum;
-		memset(usePart->history,0,sizeof(usePart->history));
+public:
+	PatchCache *patchCache;
+	PatchCache cachebackup;
 
-	}
+	Partial *pair;
+	bool alreadyOutputed;
+	Bit64s age;
 
-	void stopPartial(void) { isActive = false; }
+	Partial(Synth *synth);
+
+	int getOwnerPart();
+	bool isActive();
+	void activate(int part);
+	void deactivate(void);
+	void startPartial(dpoly *usePoly, PatchCache *useCache, Partial *pairPartial);
+	void startDecay(int envnum, Bit32s startval);
+	void startDecayAll();
+	bool shouldReverb();
 
-	
 	// Returns true only if data written to buffer
-	// This function (unline the one below it) returns processed stereo samples
+	// 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(int16 * partialBuf, long length);
-
-	// This function produces mono sample output of the specific partial
-	void generateSamples(int16 * partialBuf, long length);
+	bool produceOutput(Bit16s * partialBuf, long length);
 
+	// This function produces mono sample output using the partial's private internal buffer
+	Bit16s *generateSamples(long length);
 };
 
+}
 
 #endif
-

Index: structures.h
===================================================================
RCS file: /cvsroot/scummvm/scummvm/backends/midi/mt32/structures.h,v
retrieving revision 1.4
retrieving revision 1.5
diff -u -d -r1.4 -r1.5
--- structures.h	22 Oct 2004 15:56:39 -0000	1.4
+++ structures.h	6 Nov 2004 01:41:30 -0000	1.5
@@ -1,448 +1,60 @@
-/* ScummVM - Scumm Interpreter
- * Copyright (C) 2004 The ScummVM project
- * Based on Tristan's conversion of Canadacow's code
+/* Copyright (c) 2003-2004 Various contributors
  *
- * 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.
+ * 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:
  *
- * $Header$
+ * 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 MT32STRUCTURES_H
-#define MT32STRUCTURES_H
+#ifndef MT32EMU_STRUCTURES_H
+#define MT32EMU_STRUCTURES_H
 
-#include "stdafx.h"
-#include "common/scummsys.h"
+#if (defined (_MSC_VER) && defined(_M_IX86)) || (defined(__GNUC__) && defined(__i386__))
+#define HAVE_X86
+#endif
 
-#if defined(_MSC_VER)
-typedef unsigned __int64 uint64;
-typedef   signed __int64 int64;
+#define MAX_SAMPLE_OUTPUT 4096
+#ifdef HAVE_X86
+#define USE_MMX 1
 #else
-typedef unsigned long long uint64;
-typedef   signed long long int64;
+#define USE_MMX 0
 #endif
 
-#define INLINE
-
-
-static inline void LOG_MSG(char *fmt, ...)
-{
-	va_list ap;
-	
-	va_start(ap, fmt);
-	vfprintf(stdout, fmt, ap);
-	va_end(ap);
-	fprintf(stdout, "\n");
-	fflush(stdout);
-}
-
-#define ALIGN_PACKED GCC_PACK
-
-#if defined(WIN32) && !(defined(__CYGWIN__) || defined(__MINGW32__))
+namespace MT32Emu {
 
+#ifdef _MSC_VER
+#define  ALIGN_PACKED __declspec(align(1))
+typedef unsigned __int64   Bit64u;
+typedef   signed __int64   Bit64s;
 #else
-
-#ifdef HAVE_X86
-#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 float atti386_iir_filter_3dnow(float input,float *hist1_ptr, float *coef_ptr, int revLevel)
-{
-	return 0;
-}
-
-static inline void atti386_produceOutput1(int tmplen, int16 myvolume, int16 *useBuf, int16 *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");
-}
-
-// FIXME: This is buggy
-static inline void atti386_produceOutput2(uint32 len, int16 *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(int16 * buf1, int16 *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(int16 * buf1, int16 *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(int16 * buf1, int16 *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_PartProductOutput(int quadlen, int16 leftvol, int16 rightvol,
-					     int16 *partialBuf, int16 *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
-
+//#define ALIGN_PACKED __attribute__ ((__packed__))
+#define ALIGN_PACKED __attribute__ ((aligned (1)))
+typedef unsigned long long Bit64u;
+typedef   signed long long Bit64s;
 #endif
 
-#if !defined(__GNUC__)
-	#pragma START_PACK_STRUCTS
-#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;
 
-struct timbreParam {
+#pragma pack(1)
+struct TimbreParam {
 	struct commonParam {
 		char name[10];
 		char pstruct12;  // 1&2  0-12 (1-13)
@@ -470,7 +82,7 @@
 			char time[4]; // 1-100
 			char level[5]; // 1-100 (-50 - +50)
 		} ALIGN_PACKED env;
-		
+
 		struct lfoParam {
 			char rate; // 0-100
 			char depth; // 0-100
@@ -490,7 +102,7 @@
 			char envtime[5]; // 1-100
 			char envlevel[4]; // 1-100
 		} ALIGN_PACKED tvf;
-		
+
 		struct tvaParam {
 			char level; // 0-100
 			char velosens; // 0-100
@@ -503,46 +115,47 @@
 			char envtime[5]; // 1-100
 			char envlevel[4]; // 1-100
 		} ALIGN_PACKED tva;
-			
+
 	} ALIGN_PACKED partial[4];
 	//char dummy[20];
 } ALIGN_PACKED;
 
-struct memParams {
-	struct patchTemp {
-		char timbreGroup; // TIMBRE GROUP  0-3 (group A, group B, Memory, Rhythm)
-		char timbreNum; // TIMBRE NUMBER 0-63
-		char keyShift; // KEY SHIFT 0-48 (-24 - +24)
-		char fineTune; // FINE TUNE 0-100 (-50 - +50)
-		char benderRange; // BENDER RANGE 0-24
-		char assignMode;  // ASSIGN MODE 0-3 (POLY1, POLY2, POLY3, POLY4)
-		char reverbSwitch;  // REVERB SWITCH 0-1 (OFF,ON)
-		char dummy; // (DUMMY)
+struct PatchParam {
+	char timbreGroup; // TIMBRE GROUP  0-3 (group A, group B, Memory, Rhythm)
+	char timbreNum; // TIMBRE NUMBER 0-63
+	char keyShift; // KEY SHIFT 0-48 (-24 - +24)
+	char fineTune; // FINE TUNE 0-100 (-50 - +50)
+	char benderRange; // BENDER RANGE 0-24
+	char assignMode;  // ASSIGN MODE 0-3 (POLY1, POLY2, POLY3, POLY4)
+	char reverbSwitch;  // REVERB SWITCH 0-1 (OFF,ON)
+	char dummy; // (DUMMY)
+} ALIGN_PACKED;
+
+struct MemParams {
+	struct PatchTemp {
+		PatchParam patch;
 		char outlevel; // OUTPUT LEVEL 0-100
 		char panpot; // PANPOT 0-14 (R-L)
 		char dummyv[6];
-	} ALIGN_PACKED tmpSettings[8];
-	struct ryhTemp {
+	} ALIGN_PACKED patchSettings[8];
+
+	struct RhythmTemp {
 		char timbre; // TIMBRE  0-94 (M1-M64,R1-30,OFF)
 		char outlevel; // OUTPUT LEVEL 0-100
 		char panpot; // PANPOT 0-14 (R-L)
 		char reverbSwitch;  // REVERB SWITCH 0-1 (OFF,ON)
-	} ALIGN_PACKED rhySettings[64];
+	} ALIGN_PACKED rhythmSettings[64];
 
-	timbreParam timTemp[8];
+	TimbreParam timbreSettings[8];
 
-	struct patchArea {
-		char timbreGroup; // TIMBRE GROUP  0-3 (group A, group B, Memory, Rhythm)
-		char timbreNum; // TIMBRE NUMBER 0-63
-		char keyShift; // KEY SHIFT 0-48 (-24 - +24)
-		char fineTune; // FINE TUNE 0-100 (-50 - +50)
-		char benderRange; // BENDER RANGE 0-24
-		char assignMode;  // ASSIGN MODE 0-3 (POLY1, POLY2, POLY3, POLY4)
-		char reverbSwitch;  // REVERB SWITCH 0-1 (OFF,ON)
-		char dummy; // (DUMMY)
-	} ALIGN_PACKED pSettings[128];
-	timbreParam patch[192];
-	struct systemArea {
+	PatchParam patches[128];
+
+	struct PaddedTimbre {
+		TimbreParam timbre;
+		char padding[10];
+	} ALIGN_PACKED timbres[64 + 64 + 64 + 30]; // Group A, Group B, Memory, Rhythm
+
+	struct SystemArea {
 		char masterTune; // MASTER TUNE 0-127 432.1-457.6Hz
 		char reverbMode; // REVERB MODE 0-3 (room, hall, plate, tap delay)
 		char reverbTime; // REVERB TIME 0-7 (1-8)
@@ -551,84 +164,74 @@
 		char chanAssign[9]; // MIDI CHANNEL (PART1) 0-16 (1-16,OFF)
 		char masterVol; // MASTER VOLUME 0-100
 	} ALIGN_PACKED system;
-
 } ALIGN_PACKED;
 
-struct memBanks {
-	char pTemp[8][sizeof(memParams::patchTemp)];
-	char rTemp[64][sizeof(memParams::ryhTemp)];
-	char tTemp[8][sizeof(timbreParam)];
-	char patchmemory[128][sizeof(memParams::patchArea)];
-	char patchbanks[128][sizeof(timbreParam)];
-	char timbrebanks[64][sizeof(timbreParam)];
-	char systemBank[sizeof(memParams::systemArea)];
+struct MemBanks {
+	char pTemp[8][sizeof(MemParams::PatchTemp)];
+	char rTemp[64][sizeof(MemParams::RhythmTemp)];
+	char tTemp[8][sizeof(TimbreParam)];
+	char patchBank[128][sizeof(PatchParam)];
+	char timbreBank[64 + 64 + 64 + 30][sizeof(MemParams::PaddedTimbre)];
+	char systemBank[sizeof(MemParams::SystemArea)];
 } ALIGN_PACKED;
 
-struct memAbsolute {
-	char mt32memory[sizeof(memBanks)];
+union MT32RAMFormat {
+	MemParams params;
+	MemBanks banks;
+
+	// System memory 10
+
+	// Display 20
+
+	// Reset 7F
+
 } ALIGN_PACKED;
 
-#if !defined(__GNUC__)
-	#pragma END_PACK_STRUCTS
-#endif
+#pragma pack()
 
-struct partialFormat {
-	uint32 addr;
-	uint16 len;
+struct sampleFormat {
+	Bit32u addr;
+	Bit32u len;
 	bool loop;
 	float tune;
-	int32 ampval;
+	Bit32s ampval;
 };
 
-struct partialTable {
-	uint32 addr;
-	uint32 len;
-	uint32 pcmnum;
-	int32 ampval;
+struct sampleTable {
+	Bit32u addr;
+	Bit32u len;
+	Bit32u pcmnum;
 	bool loop;
-	int32 aggSound; // This variable is for the last 9 PCM samples, which are actually loop combinations
+	Bit32s aggSound; // This variable is for the last 9 PCM samples, which are actually loop combinations
 };
 
-
-
 union soundaddr {
-	uint32 pcmabs;
+	Bit32u pcmabs;
 	struct offsets {
-#if defined(SCUMM_LITTLE_ENDIAN)
-		uint16 pcmoffset;
-		uint16 pcmplace;
-#else
-		uint16 pcmplace;
-		uint16 pcmoffset;
-#endif
+		Bit16u pcmoffset;
+		Bit16u pcmplace;
 	} pcmoffs;
 };
 
-
 struct volset {
-	int16 leftvol;
-	int16 rightvol;
-	int16 leftvol2;
-	int16 rightvol2;
+	Bit16s leftvol;
+	Bit16s rightvol;
 };
 
-struct patchCache {
-	int rawPCM;
-	partialTable convPCM;
-
+// This is basically a per-partial, pre-processed combination of timbre and patch/rhythm settings
+struct PatchCache {
 	bool playPartial;
-	bool usePartial;
 	bool PCMPartial;
+	int pcm;
 	char waveform;
 	int pulsewidth;
 	int pwsens;
 	int pitchshift;
 	int fineshift;
-	bool sustain;
 
 	int lfodepth;
 	int lforate;
-	uint32 lfoperiod;
+	Bit32u lfoperiod;
 	int modsense;
 
 	int keydir;
@@ -650,121 +253,51 @@
 	int amplevel;
 	int tvfdepth;
 
-	int prevsample;
-
 	bool useBender;
 
-	timbreParam::partialParam::envParam pitchEnv;
-	timbreParam::partialParam::tvaParam ampEnv;
-	timbreParam::partialParam::tvfParam filtEnv;
-
-	int32 ampsustain;
-	int32 pitchsustain;
-	int32 filtsustain;
+	TimbreParam::partialParam::envParam pitchEnv;
+	TimbreParam::partialParam::tvaParam ampEnv;
+	TimbreParam::partialParam::tvfParam filtEnv;
 
-	uint32 partCount;
+	Bit32s ampsustain;
+	Bit32s pitchsustain;
+	Bit32s filtsustain;
 
-	uint8 padding[64]; //Used to pad the patch cache to 4096 bytes.  This replaces an imul with a shl 12
+	Bit32u mix;
+	int structurePosition;
+	int structurePair;
 
+	// The following fields are actually common to all partials in the timbre
+	Bit32u partialCount;
+	bool sustain;
 };
 
+class Partial; // Forward reference for class defined in partial.h
+
 struct dpoly {
 	bool isPlaying;
-	bool isDecay;
-	bool isActive;
-
-	bool partActive[4];
-
-	bool isRy;
-	uint32 *bendptr;
-	uint32 drumbend;
-	int32 *volumeptr;
-	volset *pansetptr;
 
-	int pcmnum;
+	int note;
 	int freq;
 	int freqnum;
 	int vel;
 
-	uint32 partCount;
-
-	soundaddr pcmoff;
-	uint32 pcmdelta;
-
-
-	struct partialStatus {
-		// Note played on keyboard
-		int noteval;
-		// Keyfollowed note values
-		int keyedval;
-
-		// Keyfollowed filter values
-		int realval;
-		int filtval;
-		// Keyfollowed filter w/o table
-		int filtnoval;
-		int pulsewidth;
-
-		struct envstatus {
-			int32 envpos;
-			int32 envstat;
-			int32 envbase;
-			int32 envdist;
-			int32 envsize;
-
-			bool sustaining;
-			bool decaying;
-			bool notdecayed;
-			uint32 decay;
-			int32 prevlevel;
-
-			int32 counter;
-			int32 count;
-
-		} envs[4];
-
-		uint32 lfopos;
-		soundaddr partialOff;
-		soundaddr wgOff;
-
-		uint32 ampEnvCache;
-		uint32 pitchEnvCache;
-
-		bool isDecayed;
-		bool PCMDone;
-
-		float history[32];
-
-		float pastfilt;
-		bool pitchsustain;
-		bool playPartial;
-		bool usePartial;
-
-		int looppos;
-		int partNum;
-
-		patchCache *tcache;
-
-		void * myPart;
-
-
-
-	} pStatus[4];
-
-
-	int chan;
+	bool reverb;
+	bool isDecay;
 
-	int origpat;
-	int drumnum;
-	
-	int age;
+	const Bit32u *bendptr;
+	Bit32s *volumeptr;
+	volset *pansetptr;
 
-	bool pedalhold;
-	bool firstsamp;
+	Partial *partials[4];
 
-	uint32 P1Mix;
-	uint32 P2Mix;
+	bool pedalhold; // This marks keys that have been released on the keyboard, but are being held by the pedal
 	bool sustain;
+
+	bool isActive();
+	Bit64s getAge();
 };
 
+}
+
 #endif

Index: synth.cpp
===================================================================
RCS file: /cvsroot/scummvm/scummvm/backends/midi/mt32/synth.cpp,v
retrieving revision 1.17
retrieving revision 1.18
diff -u -d -r1.17 -r1.18
--- synth.cpp	24 Oct 2004 19:21:38 -0000	1.17
+++ synth.cpp	6 Nov 2004 01:41:30 -0000	1.18
@@ -1,4446 +1,1277 @@
-/* ScummVM - Scumm Interpreter
- * Copyright (C) 2004 The ScummVM project
- * Based on Tristan's conversion of Canadacow's code
- *
- * 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
[...5352 lines suppressed...]
-			if(partTable[i]->isActive) partUse[partTable[i]->ownerChan]++;
-		}
-		//LOG_MSG("C1: %d C2: %d C3: %d C4 %d", partUse[0], partUse[1], partUse[2], partUse[3]);
-		//LOG_MSG("C5: %d C6: %d C7: %d C8 %d", partUse[4], partUse[5], partUse[6], partUse[7]);
-		//LOG_MSG("Rythmn: %d", partUse[8]);
-
+		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], MAXPARTIALS - partialManager->GetFreePartialCount());
 	}
-
 #endif
-
-
-#endif
-
+}
 
 }

Index: synth.h
===================================================================
RCS file: /cvsroot/scummvm/scummvm/backends/midi/mt32/synth.h,v
retrieving revision 1.4
retrieving revision 1.5
diff -u -d -r1.4 -r1.5
--- synth.h	24 Oct 2004 13:03:42 -0000	1.4
+++ synth.h	6 Nov 2004 01:41:30 -0000	1.5
@@ -1,68 +1,73 @@
-/* ScummVM - Scumm Interpreter
- * Copyright (C) 2004 The ScummVM project
- * Based on Tristan's conversion of Canadacow's code
+/* Copyright (c) 2003-2004 Various contributors
  *
- * 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.
+ * 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:
  *
- * $Header$
+ * 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 CSYNTHMT32_H
-#define CSYNTHMT32_H
+#ifndef MT32EMU_SYNTH_H
+#define MT32EMU_SYNTH_H
 
-#ifdef HAVE_X86
-#if defined(_MSC_VER)
-#define USE_MMX 2
-#define I_ASM
-#else
-#define USE_MMX 0
-#undef I_ASM
-#endif
-#else
-#define USE_MMX 0
-#endif
+#include <stdarg.h>
 
-#define AMPENV 0
-#define FILTENV 1
-#define PITCHENV 2
+// Shows number of partials MT-32 is playing
+#define MONITORPARTIALS 0
 
-// Filter setting
-#define FILTER_FLOAT 1
-#define FILTER_64BIT 0
-#define FILTER_INT 0
+#define ROMSIZE (512 * 1024)
+#define PCMSIZE (ROMSIZE / 2)
+#define GRAN 512
 
-#define FILTERGRAN 512
+class revmodel;
 
-// Amplitude of waveform generator
-#define WGAMP (7168)
-//#define WGAMP (8192)
+namespace MT32Emu {
 
-#include "backends/midi/mt32/structures.h"
-#include "sound/mixer.h"
+class File;
+class TableInitialiser;
+class Partial;
+class PartialManager;
+class Part;
 
-// Function that detects the availablity of SSE SIMD instructions
-// On non-MSVC compilers it automatically returns FALSE as inline assembly is required
-bool DetectSIMD();
-// Function that detects the availablity of 3DNow instructions
-// On non-MSVC compilers it automatically returns FALSE as inline assembly is required
-bool Detect3DNow();
+enum ReportType {
+	// Files missing
+	ReportType_errorPreset1    = 1,
+	ReportType_errorPreset2    = 2,
+	ReportType_errorDrumpat    = 3,
+	ReportType_errorPatchlog   = 4,
+	ReportType_errorMT32ROM    = 5,
+
+	// HW spec
+	ReportType_availableSSE    = 6,
+	ReportType_available3DNow  = 7,
+	ReportType_asingSSE        = 8,
+	ReportType_asing3DNow      = 9,
+
+	// General info
+	ReportType_lcdMessage      = 10,
+	ReportType_devReset        = 11,
+	ReportType_devReconfig     = 12,
+	ReportType_newReverbMode   = 13,
+	ReportType_newReverbTime   = 14,
+	ReportType_newReverbLevel  = 15
+};
 
 struct SynthProperties {
 	// Sample rate to use in mixing
 	int SampleRate;
+
 	// Flag to activate reverb.  True = use reverb, False = no reverb
 	bool UseReverb;
 	// Flag True to use software set reverb settings, Flag False to set reverb settings in
@@ -70,11 +75,25 @@
 	bool UseDefault;
 	// When not using the default settings, this specifies one of the 4 reverb types
 	// 1 = Room 2 = Hall 3 = Plate 4 = Tap
-	int RevType;
+	unsigned char RevType;
 	// This specifies the delay time, from 0-7 (not sure of the actual MT-32's measurement)
-	int RevTime;
+	unsigned char RevTime;
 	// This specifies the reverb level, from 0-7 (not sure of the actual MT-32's measurement)
-	int RevLevel;
+	unsigned char RevLevel;
+	// 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
+	void (*report)(void *userData, ReportType type, 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
@@ -85,73 +104,98 @@
 // 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(int sampRate, recalcStatusCallback callBack);
+bool RecalcWaveforms(char * baseDir, int sampRate, recalcStatusCallback callBack);
 
 typedef float (*iir_filter_type)(float input,float *hist1_ptr, float *coef_ptr, int revLevel);
 extern iir_filter_type usefilter;
 
-extern partialFormat PCM[54];
-extern int16 romfile[262656];
-extern int32 divtable[256];
-extern int32 smalldivtable[256];
-extern uint32 wavtabler[64][256];
-extern uint32 looptabler[16][16][256];	
-extern int16 sintable[65536];
-extern int32 penvtable[16][128];		
-extern int32 pulsetable[101];
-extern int32 pulseoffset[101];
-extern int32 sawtable[128][128];
-extern float filtcoeff[FILTERGRAN][32][16];	
-extern uint32 lfoptable[101][128];
-extern int32 ampveltable[128][64];
-extern int32 amptable[129];
-extern int16 smallnoise[441];
-extern int32 samplepos;
-extern int16* waveforms[4][256];
-extern uint32 waveformsize[4][256];
-extern int8 LoopPatterns[16][16];
-extern int drumPan[30][2];
-extern float ResonFactor[32];
-extern float ResonInv[32];
+class Synth {
+friend class Part;
+friend class Partial;
+friend class TableInitialiser;
+private:
+	bool isEnabled;
 
-extern int32 getPitchEnvelope(dpoly::partialStatus *pStat, dpoly *poly);
-extern int32 getAmpEnvelope(dpoly::partialStatus *pStat, dpoly *poly);
-extern int32 getFiltEnvelope(int16 wg, dpoly::partialStatus *pStat, dpoly *poly);
+	sampleFormat PCM[54];
+	sampleTable PCMList[128];
+	Bit32u PCMReassign[54];
+	Bit32s PCMLoopTable[54];
 
-class CSynthMT32 {
-private:
+	Bit16s romfile[PCMSIZE + GRAN];
+	Bit8s chantable[32];
+
+	#if MONITORPARTIALS == 1
+	static Bit32s samplepos = 0;
+	#endif
+
+	MT32RAMFormat mt32ram, mt32default;
+
+	revmodel *reverbModel;
+
+	Bit16s mastervolume;
+
+	char curRevMode;
+	char curRevTime;
+	Bit32u curRevLevel;
 
 	unsigned char initmode;
 	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 InitTables();
+
+	bool loadPreset(const char *filename);
+	void initReverb(char newRevMode, char newRevTime);
+	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, Bit8u *sysex, Bit32u len);
+
+	bool loadDrums(const char *filename);
+	bool loadPCMToROMMap(const char *filename);
+	bool loadROM(const char *filename);
+	void dumpDrums(const char *filename);
+	// Save the system state to a sysex file specified by filename
+	int dumpSysex(char *filename);
+
+protected:
+	void report(ReportType type, void *reportData);
+	File *openFile(const char *filename, File::OpenMode mode);
+	void closeFile(File *file);
+	void printDebug(const char *fmt, ...);
 
 public:
-	CSynthMT32() : isOpen(false) {};
+	Synth();
+	~Synth();
 
-	// Used to initialized the MT-32.  The useProp parameter specifies
-	// properties for the synthesizer, as outlined in the structure above.
-	// Returns TRUE if initialization was sucessful, otherwise returns FALSE.
-	bool ClassicOpen(SynthProperties useProp);
+	// 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);
+	void close(void);
 
 	// Sends a 4-byte MIDI message to the MT-32 for immediate playback
-	void PlayMsg(uint32 msg);
+	void playMsg(Bit32u msg);
 
 	// Sends a string of Sysex commands to the MT-32 for immediate interpretation
-	void PlaySysex(uint8 * sysex, uint32 len);
-
-	// Save the system state to a sysex file specified by filename 
-	int DumpSysex(char *filename);
+	// The length is in bytes
+	void playSysex(Bit8u *sysex, Bit32u len);
+	void playSysexWithoutFraming(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 MT32_CallBack(uint8 * stream, uint32 len, int volume);
-
+	void render(Bit16s * stream, Bit32u len);
 };
 
+}
+
 #endif

--- mt32.cpp DELETED ---





More information about the Scummvm-git-logs mailing list