/*****************************************************************************/
/*
** "GenPriv.h"
**
**	Generalized Music Synthesis package. Part of SoundMusicSys.
**
**	\xA9 Copyright 1993-1999 Beatnik, Inc, All Rights Reserved.
**	Written by Jim Nitchals and Steve Hales
**
**	Beatnik products contain certain trade secrets and confidential and
**	proprietary information of Beatnik.  Use, reproduction, disclosure
**	and distribution by any means are prohibited, except pursuant to
**	a written license from Beatnik. Use of copyright notice is
**	precautionary and does not imply publication or disclosure.
**
**	Restricted Rights Legend:
**	Use, duplication, or disclosure by the Government is subject to
**	restrictions as set forth in subparagraph (c)(1)(ii) of The
**	Rights in Technical Data and Computer Software clause in DFARS
**	252.227-7013 or subparagraphs (c)(1) and (2) of the Commercial
**	Computer Software--Restricted Rights at 48 CFR 52.227-19, as
**	applicable.
**
**	Confidential-- Internal use only
**
**
** Overview
**	Private structures
**
** Modification History
**
**	11/7/95		Major changes, revised just about everything.
**	11/11/95	Added microSyncCount for live link
**	11/16/95	Removed microSyncCount
**				Moved static variables into MusicVar structure
**				Created an external function 'PV_GetExternalTimeSync()' for the external midi source
**   12/95 		upgraded mixing bus to 32 bit; improved scaleback resolution; added reverb unit; first pass at volume ADSR
**	12/6/95 	removed reference to USE_AMP_LOOKUP
**				moved REVERB_TYPE to GENSND.H
**	12/7/95		Added channelReverb to GM_Mixer structure
**				Added REVERB_CONTROLER_THRESHOLD
**	1/18/96		Spruced up for C++ extra error checking
**				Changed InstUsedList to pUsedList and allocate it when needed
**	2/5/96		Removed unused variables. Working towards multiple songs
**				Moved lots of variables from the GM_Mixer structure into
**				Moved the MAX_TRACKS define to GenSnd.h
**	2/12/96		Added PV_CleanExternalQueue
**				Moved SongMicroseconds to GenSnd.h
**	2/13/96		Added multi song support
**	3/5/96		Eliminated the global songVolume
**	3/28/96		Added PV_SetSampleIntoCache & PV_GetInstrument
**	4/10/96		Reworked the sample cache system to not clone the sample data
**	5/2/96		Changed int to BOOL_FLAG
**	5/18/96		Added error condition to PV_MusicIRQ
**	6/30/96		Changed font and re tabbed
**	7/3/96		Added packing pragmas
**				Removed usage of Machine.h. Now merged into X_API.h
**	7/14/96		Fixed structure alignment issue for PowerPC
**	7/23/96		Changed PV_GetExternalTimeSync to unsigned long
**	7/24/96		Changed Midi Queue system to use a head/tail
**	7/25/96		Moved Mac audio variables to GenMacTools.c
**				Changed PV_GetExternalTimeSync to GM_GetSyncTimeStampQuantizedAhead
**	8/12/96		Changed PV_ResetControlers to support semi-complete reset
**	9/25/96		Added GM_Song pointer in NoteRecord structure
**	9/27/96		Added more parameters to ServeMIDINote & PV_StopMIDINote
**	10/18/96	Made CacheSampleInfo smaller
**	10/23/96	Removed reference to BYTE and changed them all to XBYTE or XSBYTE
**	12/19/96	Added Sparc pragmas
**	12/30/96	Changed copyrights
**	1/23/97		Added support for stereoFilter
**				In NoteRecord changed PitchBend to NotePitchBend
**				Added in NoteRecord NoteFadeRate
**	1/30/97		Changed SYMPHONY_SIZE to MAX_VOICES
**	3/17/97		Changed the API to PV_GetInstrument. Enlarged CacheSampleInfo member
**				theID to a long
**	4/9/97		Added sampleExpansion factor
**	4/20/97		Changed PV_MusicIRQ to PV_ProcessMidiSequencerSlice
**	6/4/97		Renamed InitSoundManager to GM_StartHardwareSoundManager, and
**				renamed FinsSoundManager to GM_StopHardwareSoundManager, and
**				now pass in a thread context
**	712/97		Added a drift fixer flag to GM_Mixer that tries
**				to compensate for real time midi time stamping
**	7/16/97		Moved GM_Mixer *MusicGlobals to be protected againsts C++
**				name mangling
**	7/17/97		Aligned GM_Mixer structure to 8 bytes
**	7/18/97		Moved GM_AudioTaskCallbackPtr pTaskProc &
**				GM_AudioOutputCallbackPtr pOutputProc into here from GenXXXTools.c
**	7/22/97		Changed SYNC_BUFFER_TIME to BUFFER_SLICE_TIME
**	7/28/97		Changed pack structure alignment for SPARC from 8 to 4. Compiler bug.
**	8/8/97		Added PV_FreePgmEntries
**	8/27/97		Moved GM_StartHardwareSoundManager & GM_StopHardwareSoundManager to
**				GenSnd.h
**	9/2/97		Fixed bug with THE_CHECK that forgot to look at zero length buffers
**	9/19/97		Changed name of PV_FreePatchInfo.
**				Added PV_InsertBankSelect
**	10/15/97	Added processingSlice to NoteRecord to handle threading issues
**	10/27/97	Removed reference to MusicGlobals->theSongPlaying
**	10/28/97	Eliminated reference to FAR
**	10/29/97	Promoted PV_AnyStereoInstrumentsLoaded to GM_AnyStereoInstrumentsLoaded and
**				moved it to GenSnd.h
**	12/4/97		Renamed GM_Mixer to GM_Mixer. Renamed NoteRecord to GM_Voice
**	1/14/98		kk: added NoteLoopTarget to GM_Voice (number of loops between loop points desired
**				before sample continues to end.
**				changed NoteLoopCount from XBYTE to XDWORD because we are actually counting loops now and  
**				may want quite a few.
**	1/27/98		Renamed MACINTOSH to H_MACINTOSH
**	2/3/98		Renamed songBufferLeftMono to songBufferDry
**	2/5/98		Added a GM_Song pointer to PV_SetSampleIntoCache
**	2/8/98		Changed BOOL_FLAG to XBOOL
**	2/10/98		added a bunch of structures for storing new effect parameters
**	2/20/98		kcr	converted floating-point to fixed for new effects -- added chorus buffer
**	2/23/98		Removed last of old variable reverb code
**	2/24/98		kcr	deal with sample-rate changes for chorus and reverb...
**	3/16/98		Removed PV_ProcessReverbMono & PV_ProcessReverbStereo & PV_PostFilterStereo
**				from public view
**				Changed InitNewReverb to return a XBOOL for success or failure
**	4/1/98		MOE: took out references to FilterEnvelope{} so that all compiles
**	4/14/98		Added some comments and removed extra structures that are not being used
**
**	6/5/98		Jim Nitchals RIP	1/15/62 - 6/5/98
**				I'm going to miss your irreverent humor. Your coding style and desire
**				to make things as fast as possible. Your collaboration behind this entire
**				codebase. Your absolute belief in creating the best possible relationships 
**				from honesty and integrity. Your ability to enjoy conversation. Your business 
**				savvy in understanding the big picture. Your gentleness. Your willingness 
**				to understand someone else's way of thinking. Your debates on the latest 
**				political issues. Your generosity. Your great mimicking of cartoon voices. 
**				Your friendship. - Steve Hales
**
**	7/1/98		Changed various API to use the new XResourceType and XLongResourceID
**	7/7/98		Removed reverbIsVariable from GM_Mixer structure. Using function 
**				GM_IsReverbFixed instead.
**	7/28/98		Renamed inst_struct to pInstrument
**				Changed meaning of processExternalMidiQueue in GM_Song. Now its a counter
**				instead of just a boolean.
**	7/30/98		Added constant value to MAX_CHUNK_SIZE for 48k output in GM_Mixer structure
**	8/12/98		Added PV_ModifyVelocityFromCurve
**	10/27/98	Moved MIN_LOOP_SIZE to GenSnd.h
**	11/9/98		Renamed NoteDur to voiceMode
**	12/22/98	Removed old USE_SEQUENCER flag
**	1/12/99		Added a useKatmaiCPU flag that is dynamic if the USE_KAT flag
**				is set to build.
**	3/1/99		Changed NoteRefNum to NoteContext
**	3/3/99		Added PV_GetVoiceFromSoundReference
**				Added voiceStartTimeStamp to GM_Voice
**				Removed USE_DIRECT_MIXDOWN
**	3/5/99		Added VOICE_ALLOCATED_READY_TO_SYNC_START
**				Added threadContext to PV_ServeEffectCallbacks & PV_ProcessSampleFrame &
**				PV_ProcessSequencerEvents & PV_ProcessMidiSequencerSlice
**	3/11/99		Renamed ADSRRecord to GM_ADSR. Renamed LFORecord to GM_LFO. Renamed CurveRecord to GM_TieTo.
**	3/31/99		Added pMixer pointer to the GM_Voice structure.
**				Renamed ServeMIDINote to PV_StartMIDINote, renamed StopMIDINote to PV_StopMIDINote
**	5/10/99		Removed CODE_TYPE
**	5/28/99		MOE:  Moved DEFAULT_ constants to GenSnd.h
**	6/8/99		Modified Q_MIDIEvent and added an status byte for type of event to work with the new
**				wire event model.
**	6/15/99		Changed PV_CleanExternalQueue parmeters
**	7/9/99		Added taskReference for the Taskcallback
**	7/19/99		Renamed UBYTE to XBYTE. Renamed INT16 to XSWORD. Renamed INT32 to XSDWORD.
**				Renamed UINT32 to XDWORD. Renamed SBYTE to XSBYTE. Renamed UINT16 to XWORD
**	8/3/99		Changed pragma settings for X_BE
*/
/*****************************************************************************/

#ifndef G_PRIVATE
#define G_PRIVATE

#ifndef __X_API__
	#include "X_API.h"
#endif

#ifndef G_SOUND
	#include "GenSnd.h"
#endif

#define VOLUME_PRECISION_SCALAR		6L		// used to be 8, so we must scale down output by 2
#define OUTPUT_SCALAR				9L		// 9 for volume minus 4 for increased volume_range resolution, plus 2 for increased volume precision scalar
#define VOLUME_RANGE				4096	// original range was 256, therefore:
#define UPSCALAR					16L		// multiplier (NOT a shift count!) for increasing amplitude resolution
#define MAXRESONANCE				127		// mask and buffer size for resonant filter.  Higher means wider frequency range.


#define MAX_CHUNK_SIZE				512		// max samples to build per slice at 44k
#define SOUND_EFFECT_CHANNEL		16		// channel used for sound effects. One beyond the normal

// BUFFER_SLICE_TIME is calculated by the formula:
//
// 1 second / sample rate * samples
// 1 000 000 / 22050 * 256
//
#define BUFFER_SLICE_TIME			11610	// the amount of time in microseconds that
											// passes when calling ProcessSampleFrame

// 20.12 (whole.fractional)
#define STEP_BIT_RANGE				12L
#define STEP_OVERFLOW_FLAG			(1<<(STEP_BIT_RANGE-1))		
#define STEP_FULL_RANGE				((1<<STEP_BIT_RANGE)-1)

#define ALLOW_16_BIT			1			// 1 - allow 16 bit if available, 0 - force 8 bit
#define ALLOW_STEREO			1			// 1 - allow stereo if available, 0 - force mono
#define ALLOW_DEBUG_STEREO		0			// 1 - allow keyboard debugging of stereo code
#define USE_DLS					0			// 1 - allow DLS changes, 0 - IGOR


#if (MAX_CHUNK_SIZE%4) != 0
	#error "Bad Chunk Size, Divisible by 4 only!" 
#endif

// a macro to handle broken loops and partial buffers in the inner loop code
#define THE_CHECK(TYPE) \
	if (cur_wave >= end_wave)\
	{\
		if (looping)\
		{\
			cur_wave -= wave_adjust;	/* back off pointer for previous sample*/ \
			if (this_voice->doubleBufferProc)\
			{\
				/* we hit the end of the loop call double buffer to notify swap*/ \
				if (PV_DoubleBufferCallbackAndSwap(this_voice->doubleBufferProc, this_voice)) \
				{\
					/* recalculate our internal pointers */\
					end_wave = (XFIXED)(this_voice->NoteLoopEnd - this_voice->NotePtr) << STEP_BIT_RANGE;\
					wave_adjust =  (XFIXED)(this_voice->NoteLoopEnd - this_voice->NoteLoopPtr) << STEP_BIT_RANGE;\
					source = (TYPE) this_voice->NotePtr;\
				}\
				else\
				{\
					goto FINISH;\
				}\
			}\
		}\
		else\
		{\
			this_voice->voiceMode = VOICE_UNUSED;\
			PV_DoCallBack(this_voice);\
			goto FINISH;\
		}\
	}

#if USE_U3232_LOOPS != FALSE

typedef unsigned long	U32;
typedef struct U3232
{
	U32		i;
	U32		f;
} U3232;

#define ADD_32_32(target_int, target_frac, addend_int, addend_frac)\
	{\
		target_frac += addend_frac;\
		if ((U32)target_frac < (U32)addend_frac) target_int++;\
		target_int += addend_int;\
	}
#define ADD_U3232(target_int, target_frac, u3232)\
	ADD_32_32(target_int, target_frac, u3232.i, u3232.f)

#define THE_CHECK_U3232(TYPE) \
	if (cur_wave_i >= end_wave)\
	{\
		if (looping)\
		{\
			cur_wave_i -= wave_adjust;	/* back off pointer for previous sample*/\
/*			cur_wave_f = 0;	TRY PUTTING THIS IN SOME DAY, MIGHT SOUND BETTER */\
			if (this_voice->doubleBufferProc)\
			{\
				/* we hit the end of the loop call double buffer to notify swap*/ \
				if (PV_DoubleBufferCallbackAndSwap(this_voice->doubleBufferProc, this_voice)) \
				{\
					/* recalculate our internal pointers */\
					end_wave = this_voice->NoteLoopEnd - this_voice->NotePtr;\
					wave_adjust = this_voice->NoteLoopEnd - this_voice->NoteLoopPtr;\
					source = (TYPE)this_voice->NotePtr;\
				}\
				else\
				{\
					goto FINISH;\
				}\
			}\
		}\
		else\
		{\
			this_voice->voiceMode = VOICE_UNUSED;\
			PV_DoCallBack(this_voice);\
			goto FINISH;\
		}\
	}
#endif


#if USE_FLOAT_LOOPS != FALSE
#define THE_CHECK_FLOAT(TYPE) \
	if (cur_wave >= end_wave)\
	{\
		if (looping)\
		{\
			cur_wave -= wave_adjust;	/* back off pointer for previous sample*/ \
			if (this_voice->doubleBufferProc)\
			{\
				/* we hit the end of the loop call double buffer to notify swap*/ \
				if (PV_DoubleBufferCallbackAndSwap(this_voice->doubleBufferProc, this_voice)) \
				{\
					/* recalculate our internal pointers */\
					end_wave = (this_voice->NoteLoopEnd - this_voice->NotePtr);\
					wave_adjust = (this_voice->NoteLoopEnd - this_voice->NoteLoopPtr);\
					source = (TYPE) this_voice->NotePtr;\
				}\
				else\
				{\
					goto FINISH;\
				}\
			}\
		}\
		else\
		{\
			this_voice->voiceMode = VOICE_UNUSED;\
			PV_DoCallBack(this_voice);\
			goto FINISH;\
		}\
	}
#endif

typedef unsigned char			OUTSAMPLE8;
typedef short int				OUTSAMPLE16;		// 16 bit output sample

enum
{
	SUS_NORMAL			=	0,		// normal release at note off
	SUS_ON_NOTE_ON		=	1,		// note on, with pedal
	SUS_ON_NOTE_OFF		=	2		// note off, with pedal
};

#if X_PLATFORM == X_BE
	#pragma pack (4)
#else
	#if CPU_TYPE == kRISC
		#pragma options align=power
	#endif
	/* Change pack(8) to pack(4) for SPARC.				-Liang */
	#if (CPU_TYPE == kSPARC)
		#pragma pack (4)
	#endif
	/* $$kk: pack(4) for solaris x86 */
	#if (CPU_TYPE == k80X86)
		#if (X_PLATFORM == X_SOLARIS)
			#pragma pack (4)
		#else
			#pragma pack (8)
		#endif
	#endif
#endif

// Mode in which a GM_Voice is currently being used
typedef enum 
{
	// These are left as reference. They refer to the old code base of what the numbers
	// ment.
//	VOICE_UNUSED		=	-1,				// voice is free
//	VOICE_RELEASING		=	0,				// voice is releasing
//	VOICE_SUSTAINING	=	32767,			// voice is sustaining
//	VOICE_ALLOCATED		=	1				// voice is allocated, but not active

	VOICE_UNUSED		=	0,				// voice is free
	VOICE_ALLOCATED,						// voice is allocated, but not active
	VOICE_ALLOCATED_READY_TO_SYNC_START,	// voice is allocated, ready to start and on the next slice it will
											// set them to VOICE_SUSTAINING. This will look at the syncVoiceReference
											// variable and start all voices with the same reference
	VOICE_RELEASING,						// voice is releasing
	VOICE_SUSTAINING						// voice is sustaining
} VoiceMode;

// Need a forward reference to the GM_Mixer struct to keep
// our compiler from complaining.
struct GM_Mixer;

// This structure is created and maintained for each sample that is to mixed into the final output
struct GM_Voice
{
	VoiceMode				voiceMode;				// duration of note to play. VOICE_UNUSED is dead
													// This field must be first!
	void					*syncVoiceReference;	// this field is used when voiceMode has been set to VOICE_ALLOCATED_READY_TO_SYNC_START
													// A single pass search will happen and it will look for matching syncVoiceReference
													// values. Once the voice is started it will be set to NULL.
	XSWORD					NoteDecay;				// after voiceMode == VOICE_RELEASING then this is ticks of decay
	XSDWORD					voiceStartTimeStamp;	// this is a time stamp of when this voice is started, used to
													// track unique voices
	GM_Instrument			*pInstrument;			// read-only pointer to instrument information
	GM_Song					*pSong;					// read-only pointer to song information
	struct GM_Mixer			*pMixer;				// read-only pointer to mixer information
													// used to backtrace where note came from
	XBYTE 					*NotePtr;				// pointer to start of sample
	XBYTE 					*NotePtrEnd;			// pointer to end of sample
#if USE_U3232_LOOPS != FALSE
	U3232					samplePosition;			// new index from NotePtr
#endif
#if USE_FLOAT_LOOPS != FALSE
	UFLOAT					samplePosition_f;		// new index from NotePtr
#endif
	XFIXED					NoteWave;				// current fractional position within sample (NotePtr:NotePtrEnd)
	XFIXED					NotePitch;				// playback pitch in 16.16 fixed. 1.0 will play recorded speed
	XFIXED					noteSamplePitchAdjust;	// adjustment to pitch based on difference from 22KHz in recorded rate
	XBYTE 					*NoteLoopPtr;			// pointer to start of loop point within NotePtr & NotePtrEnd
	XBYTE 					*NoteLoopEnd;			// pointer to end of loop point within NotePtr & NotePtrEnd
	
	// $$kk: 01.14.98: added NoteLoopTarget
	XDWORD					NoteLoopTarget;			// target number of loops before continuing to end of sample

	void					*NoteContext;			// user context for callbacks

// Double buffer variables. If using double buffering, then doubleBufferPtr1 will be non-zero. These variables
// will be swapped with NotePtr, NotePtrEnd, NoteLoopPtr, NoteLoopPtrEnd
	XBYTE						*doubleBufferPtr1;
	XBYTE						*doubleBufferPtr2;
	GM_DoubleBufferCallbackPtr	doubleBufferProc;

// Call back procs
	GM_LoopDoneCallbackPtr		NoteLoopProc;		// normal loop continue proc
	GM_SoundDoneCallbackPtr		NoteEndCallback;	// sample done callback proc

	XSWORD					NoteNextSize;			// number of samples per slice. Use 0 to recalculate
	XSBYTE					NoteMIDIPitch;			// midi note pitch to start note
	XSBYTE					noteOffsetStart;		// at the start of the midi note, what was the offset
	XSWORD					ProcessedPitch;			// actual pitch to play (proccessed)
	XSWORD					NoteProgram;			// midi program number
	XSBYTE					NoteChannel;			// channel note is playing on
	XSBYTE					NoteTrack;				// track note is playing on
	XSDWORD					NoteVolume;				// note volume (scaled)
	XSWORD					NoteVolumeEnvelope;		// scalar from volume ADSR and LFO's.  0 min, VOLUME_RANGE max.
	XSWORD					NoteVolumeEnvelopeBeforeLFO;	// as described.
	XSWORD					NoteMIDIVolume;			// note volume (unscaled)
	XSWORD					NotePitchBend;			// 8.8 Fixed amount of bend
	XSWORD					ModWheelValue;			// 0-127
	XSWORD					LastModWheelValue;		// has it changed?  This is how we know.
	XSWORD					LastPitchBend;			// last bend
	XSWORD					stereoPosition;			// -63 (left) 0 (Middle) 63 (Right)

	// $$kk: 01.14.98: changed NoteLoopCount from XBYTE to XSDWORD because we are actually counting loops now and may want quite a few
	XDWORD					NoteLoopCount;

	XBYTE					bitSize;				// 8 or 16 bit data
	XBYTE					channels;				// mono or stereo data
	XBYTE					sustainMode;			// sustain mode, for pedal controls
	XBYTE					sampleAndHold;			// flag whether to sample & hold, or sample & release
	XBYTE					avoidReverb;			// don't mix into reverb unit
	XBYTE					reverbLevel;			// 0-127 when reverb is enabled

	XBYTE					processingSlice;		// if TRUE, then thread is processing slice of this instrument

// sound effects variables. Not used for normal envelope or instruments
	XBYTE					soundEndAtFade;
	XFIXED					soundFadeRate;			// when non-zero fading is enabled
	XFIXED					soundFixedVolume;		// inital volume level that will be changed by soundFadeRate
	XSWORD					soundFadeMaxVolume;		// max volume
	XSWORD					soundFadeMinVolume;		// min volume
	GM_SampleCallbackEntry	*pSampleMarkList;		// linked list of callbacks on a per sample frame basis

	XSDWORD					stereoPanBend;

	GM_ADSR					volumeADSRRecord;
	XSDWORD					volumeLFOValue;
	XSWORD					LFORecordCount;
	GM_LFO					LFORecords[MAX_LFOS+1];	// allocate for maximum allowed
	XSDWORD					lastAmplitudeL;
	XSDWORD					lastAmplitudeR;			// used to interpolate between points in volume ADSR
	XSWORD					chorusLevel;			// 0-127 when chorus is enabled
	XSWORD					z[MAXRESONANCE+1];
	XSDWORD					zIndex, Z1value, previous_zFrequency;
	XSDWORD					LPF_lowpassAmount, LPF_frequency, LPF_resonance;
	XSDWORD					LPF_base_lowpassAmount, LPF_base_frequency, LPF_base_resonance;
//	XSDWORD					s1Left, s2Left, s3Left, s4Left, s5Left, s6Left; // for INTERP3 mode only
};
typedef struct GM_Voice GM_Voice;

// support for historical reasons
#define NoteRecord	GM_Voice

// Structure used for caching samples for instruments
struct CacheSampleInfo
{
	unsigned long	cacheBlockID;	// block ID. for debugging
	XFIXED			rate;			// sample rate
	unsigned long	waveSize;		// size in bytes
	unsigned long	waveFrames;		// number of frames
	unsigned long	loopStart;		// loop start frame
	unsigned long	loopEnd;		// loop end frame
	char			bitSize;		// sample bit size; 8 or 16
	char			channels;		// mono or stereo; 1 or 2
	short int		baseKey;		// base sample key
	XLongResourceID	theID;			// sample ID
	long			referenceCount;	// how many references to this sample block
	void			*pSampleData;	// pointer to sample data. This may be an offset into the pMasterPtr
	void			*pMasterPtr;	// master pointer that contains the snd format information
};
typedef struct CacheSampleInfo CacheSampleInfo;

struct InstrumentRemap
{
	XSWORD	from;
	XSWORD	to;
};
typedef struct InstrumentRemap InstrumentRemap;

#if X_PLATFORM == X_WEBTV
	#define MAX_QUEUE_EVENTS				32
#else
	#define MAX_QUEUE_EVENTS				256
#endif

#define REVERB_BUFFER_SIZE_SMALL		4096		// * sizeof(long)
#define REVERB_BUFFER_MASK_SMALL		4095
#if USE_SMALL_MEMORY_REVERB
	#define REVERB_BUFFER_SIZE			REVERB_BUFFER_SIZE_SMALL
	#define REVERB_BUFFER_MASK 			REVERB_BUFFER_MASK_SMALL
#else
	#define REVERB_BUFFER_SIZE			16384
	#define REVERB_BUFFER_MASK_SHORT 	16383
	#define REVERB_BUFFER_MASK 			32767
#endif


enum
{
	Q_MIDI_DEAD = 0,
	Q_MIDI_ALLOCATING,
	Q_MIDI_READY
};

// This structure is to allow for queuing midi events into the playback other than those that are
// pulled from the midi file stream
struct Q_MIDIEvent
{
	GM_Song			*pSong;			// pSong the event was placed from
	XDWORD			timeStamp;		// timestamp of event
	XBYTE			status;			// status of event: 0 - dead, 1 - allocating, 2 - ready
	XBYTE			midiChannel;	// which channel
	XBYTE			command;		// which command
	XBYTE			byte1;			// note, controller
	XBYTE			byte2;			// velocity, lsb/msb
};
typedef struct Q_MIDIEvent Q_MIDIEvent;

typedef void			(*InnerLoop)(GM_Voice *pVoice);
typedef void			(*InnerLoop2)(GM_Voice *pVoice, XBOOL looping);


// tried to 8 byte align structure (7/17/97)
struct GM_Mixer
{
	TerpMode			interpolationMode;				// output interpolation mode
	Quality				outputQuality;					// output sample rate

	ReverbMode			reverbUnitType;					// verb mode
	ReverbMode			reverbTypeAllocated;			// verb mode allocated

	XBYTE				sampleFrameSize;				// size in bytes of each sample frame
	XBYTE				sampleExpansion;				// output expansion factor 1, 2, or 4
	XSWORD 				MasterVolume;

	XSWORD				effectsVolume;					// volume multiplier of all effects
	XSDWORD				scaleBackAmount;

	XSWORD				MaxNotes;
	XSWORD				mixLevel;
	XSWORD				MaxEffects;
	XSWORD				maxChunkSize;

	LOOPCOUNT			One_Slice, One_Loop, Two_Loop, Four_Loop;
	LOOPCOUNT			Sixteen_Loop;

	XBOOL		/*0*/	generate16output;				// if TRUE, then build 16 bit output
	XBOOL		/*1*/	generateStereoOutput;			// if TRUE, then output stereo data
	XBOOL		/*2*/	insideAudioInterrupt;
	XBOOL		/*3*/	systemPaused;					// all sound paused and disengaged from hardware

	XBOOL 		/*4*/	enableDriftFixer;				// if enabled, this will fix the drift of real time with our synth time.
	XBOOL		/*5*/	sequencerPaused;				// MIDI sequencer paused
	XBOOL		/*6*/	cacheSamples;					// if TRUE, then samples will be cached
	XBOOL		/*7*/	cacheInstruments;				// current not used

	XBOOL		/*0*/	stereoFilter;					// if TRUE, then filter stereo output
	XBYTE		/*1*/	processExternalMidiQueue;		// counter flag to lock processing of queue. 0 means process
#if USE_KAT
	XBOOL		/*2*/	useKatmaiCPU;
#endif
	CacheSampleInfo 	*sampleCaches[MAX_SAMPLES];		// cache of samples loaded

	// voice allocation, and dry and wet mix buffers
	GM_Voice			NoteEntry[MAX_VOICES];
	XSDWORD				songBufferDry[(MAX_CHUNK_SIZE+64)*2];	// interleaved samples: left-right
	XSDWORD				songBufferReverb[MAX_CHUNK_SIZE+64];	// the +64 is for 48k output
	XSDWORD				songBufferChorus[MAX_CHUNK_SIZE+64];

#if USE_MOD_API
// MOD interpreter file playing. NULL is no file
	GM_ModData			*pModPlaying;
#endif

// MIDI Interpreter variables
	GM_Song				*pSongsToPlay[MAX_SONGS];		// number of songs to play at once

// normal inner loop procs
	InnerLoop2			partialBufferProc;
	InnerLoop			fullBufferProc;
	InnerLoop2			partialBufferProc16;
	InnerLoop			fullBufferProc16;

// procs for resonant low-pass filtering
	InnerLoop2			filterPartialBufferProc;
	InnerLoop			filterFullBufferProc;
	InnerLoop2			filterPartialBufferProc16;
	InnerLoop			filterFullBufferProc16;

// external midi control variables
	Q_MIDIEvent			theExternalMidiQueue[MAX_QUEUE_EVENTS];
// pointers for circular event buffer
	Q_MIDIEvent			*pHead;							// pointer to events to read from queue
	Q_MIDIEvent			*pTail;							// pointer to events to write to queue
														// always points to the next one to use
	XDWORD				syncCount;						// in microseconds. Current tick of audio output
	XSDWORD				syncBufferCount;

	XDWORD				samplesPlayed;					// number of samples played by device
	XDWORD				samplesWritten;					// number of samples written to device
	XDWORD				lastSamplePosition;				// last time GM_UpdateSamplesPlayed was called

	XDWORD				timeSliceDifference;			// value in microseconds between calls to
														// HAE_BuildMixerSlice
	
	GM_AudioTaskCallbackPtr		pTaskProc;				// callback for audio tasks
	void						*taskReference;
	GM_AudioOutputCallbackPtr	pOutputProc;			// callback for audio output

// variables used for "classic" fixed verb
	XSDWORD				*reverbBuffer;			// this is the master pointer used
												// for verb. It is shared between
												// different types of verbs, although
												// the data maybe different
	XDWORD				reverbBufferSize;		// Set the size of memory allocated here.
												// Make sure you set this because it is
												// compared and tested against
	XSDWORD				reverbPtr;				// delay line index into verb buffer
	XSDWORD				LPfilterL, LPfilterR;	// used for fixed verb
	XSDWORD				LPfilterLz, LPfilterRz;
};
typedef struct GM_Mixer GM_Mixer;

// support for historical reasons
#define MusicVars	GM_Mixer

#if CPU_TYPE == kRISC
	#pragma options align=reset
#endif
#if ((CPU_TYPE == k80X86) || (CPU_TYPE == kSPARC))
	#pragma pack ()
#endif

#ifdef __cplusplus
	extern "C" {
#endif

extern GM_Mixer *MusicGlobals;

#if USE_NEW_EFFECTS
/******************************* new reverb stuff *****************************/

#define kCombBufferFrameSize			4096	/* 5000 */
#define kDiffusionBufferFrameSize		4096	/* 4410 */
#define kStereoizerBufferFrameSize		1024	/* 1000 */
#define kEarlyReflectionBufferFrameSize	0x2000	/* 0x1500 */

#define kCombBufferMask					(kCombBufferFrameSize - 1)
#define kDiffusionBufferMask			(kDiffusionBufferFrameSize - 1)
#define kStereoizerBufferMask			(kStereoizerBufferFrameSize - 1)
#define kEarlyReflectionBufferMask		(kEarlyReflectionBufferFrameSize - 1)


#define kNumberOfCombFilters		6
#define kNumberOfEarlyReflections	7


#define kNumberOfDiffusionStages	3


struct NewReverbParams
{
	XBOOL				mIsInitialized;
	Quality				mSampleRate;
	XSDWORD				mReverbType;	
	
	/* early reflection params */
	XSDWORD				*mEarlyReflectionBuffer;
	XSDWORD				mEarlyReflectionGain[kNumberOfEarlyReflections];
	int					mReflectionWriteIndex;
	int					mReflectionReadIndex[kNumberOfEarlyReflections];
	
	
	/* comb filter params */	
	XSDWORD				*mReverbBuffer[kNumberOfCombFilters];
	
	int					mReadIndex[kNumberOfCombFilters];
	int					mWriteIndex[kNumberOfCombFilters];
	
	long				mUnscaledDelayFrames[kNumberOfCombFilters];
	long				mDelayFrames[kNumberOfCombFilters];
	
	XSDWORD 				mFeedbackList[kNumberOfCombFilters];
	
	XSDWORD				mRoomSize;
	XSDWORD				mRoomChoice;
	XSDWORD				mMaxRegen;		// 0-127
	XSDWORD				mDiffusedBalance;
	
	/* diffusion params */
	XSDWORD				*mDiffusionBuffer[kNumberOfDiffusionStages];
	int					mDiffReadIndex[kNumberOfDiffusionStages];
	int					mDiffWriteIndex[kNumberOfDiffusionStages];
	
	/* output filter */
	XSDWORD				mLopassK;
	XSDWORD				mFilterMemory;
	
	/* stereoizer params */
	XSDWORD				*mStereoizerBufferL;
	XSDWORD				*mStereoizerBufferR;
	int					mStereoReadIndex;
	int					mStereoWriteIndex;
};

typedef struct NewReverbParams NewReverbParams;

extern NewReverbParams		gNewReverbParams;

/* prototypes */
NewReverbParams*	GetNewReverbParams();
XBOOL InitNewReverb();	// returns TRUE if success
void ShutdownNewReverb();
XBOOL CheckReverbType();
void ScaleDelayTimes();
void GenerateDelayTimes();
void GenerateFeedbackValues();
void SetupDiffusion();
void SetupStereoizer();
void SetupEarlyReflections();
void RunNewReverb(XSDWORD *sourceP, XSDWORD *destP, int nSampleFrames);
XDWORD GetSamplingRate();
XDWORD GetSR_44100Ratio();
XDWORD Get44100_SRRatio();

/******************************* new chorus stuff *****************************/
#define kChorusBufferFrameSize		4410L

struct ChorusParams
{
	XBOOL				mIsInitialized;
	Quality				mSampleRate;
	
	XSDWORD*				mChorusBufferL;
	XSDWORD*				mChorusBufferR;

	int					mWriteIndex;
	XSDWORD				mReadIndexL;
	XSDWORD				mReadIndexR;
	
	int					mSampleFramesDelay;

	XSDWORD				mRate;
	//float				mDepth;
	XSDWORD				mPhi;
	
	XSDWORD				mFeedbackGain;	// between 0-127
};

typedef struct ChorusParams ChorusParams;

/* prototypes */
ChorusParams* GetChorusParams();
void InitChorus();
void ShutdownChorus();
XSDWORD GetChorusReadIncrement(XSDWORD readIndex, long writeIndex, long nSampleFrames, XSDWORD phase);
void SetupChorusDelay();
void RunChorus(XSDWORD *sourceP, XSDWORD *destP, int nSampleFrames);


#if 0	// only reverb and chorus are currently activated...

/******************************* delay stuff *****************************/
#define kDelayBufferFrameSize		44100

struct DelayEffect
{
	XSDWORD*				mDelayBuffer;

	int					mWriteIndex;
	int					mReadIndex;
	
	float				mSecondsDelay;
	
	float				mFeedbackValue;
	float				mFeedbackGain;
	
	XSDWORD				mFilterMemoryL;
	XSDWORD				mFilterMemoryR;
	XSDWORD				mLopassK;
};

typedef struct DelayEffect DelayEffect;

/* prototypes */
void Delay_Initialize(DelayEffect *This);
void Delay_Shutdown(DelayEffect *This);
void Delay_Run(DelayEffect *This, XSDWORD *sourceP);

extern DelayEffect		gDelay;


/******************************* graphic eq stuff *****************************/
#define kNumberOfBands		7

struct GraphicEqParams
{
	/* right and left filter memory */
	XSDWORD		mHistory1L[kNumberOfBands];
	XSDWORD		mHistory2L[kNumberOfBands];
	XSDWORD		mHistory1R[kNumberOfBands];
	XSDWORD		mHistory2R[kNumberOfBands];
	
	float		mControlList[kNumberOfBands];		/* values between 0.0 and 1.0 */
	float		mGain[kNumberOfBands];
};

typedef struct GraphicEqParams GraphicEqParams;

/* prototypes */
GraphicEqParams* GetGraphicEqParams();
void InitGraphicEq();
void CalculateGraphicEqGains();
void RunGraphicEq(XSDWORD *sourceP, int nSampleFrames);


/******************************* parametric eq stuff *****************************/

struct ParametricEq
{
	float	mFreqValue;
	float	mQValue;
	float	mGainValue;

	float	mControlList[3];
	
	double	pi;

	float	sweep;
	
	/* filter memory */
	XSDWORD	x1;
	XSDWORD	x2;
	XSDWORD	y1;
	XSDWORD	y2;
	
	/* filter coefficients */
	float	b0;
	float	b1;
	float	b2;
	float	a1;
	float	a2;
};

typedef struct ParametricEq ParametricEq;

/* prototypes */
void 	ParametricEq_Initialize(ParametricEq *This);
void 	ParametricEq_CalculateParams(ParametricEq *This);
void 	ParametricEq_Run(ParametricEq *This, XSDWORD *buffer);

extern ParametricEq		gParametricEq;

/******************************* resonant filter stuff *****************************/

struct ResonantFilterParams
{
	float	mFrequency;
	float	mResonance;

	float	mControlList[2];
	
	double	pi;

	float	sweep;
	
	/* filter memory */
	XSDWORD	y1;
	XSDWORD	y2;
	
	/* filter coefficients */
	float	c0;
	float	c1;
	float	c2;
};

typedef struct ResonantFilterParams ResonantFilterParams;

/* prototypes */
ResonantFilterParams* GetResonantFilterParams();
void InitResonantFilter();
void CalculateResonantParams(float inFrequency, float inResonance);
void RunResonantFilter(XSDWORD *buffer, int nSampleFrames);

#endif // 0

#endif // USE_NEW_EFFECTS
/******************************************************************************/





// internal function declarations

void PV_Generate8outputStereo(OUTSAMPLE8 * dest8);
void PV_Generate8outputMono(OUTSAMPLE8 * dest8);
void PV_Generate16outputStereo(OUTSAMPLE16 * dest16);
void PV_Generate16outputMono(OUTSAMPLE16 * dest16);

long PV_DoubleBufferCallbackAndSwap(GM_DoubleBufferCallbackPtr doubleBufferCallback, 
										GM_Voice *this_voice);
void PV_CalculateStereoVolume(GM_Voice *this_voice, XSDWORD *pLeft, XSDWORD *pRight);

void PV_ServeEffectsFades(void);
void PV_ServeEffectCallbacks(void *threadContext);

#if USE_U3232_LOOPS != FALSE
void PV_ServeU3232FilterFullBufferNewReverb (GM_Voice *this_voice);
void PV_ServeU3232StereoFilterFullBufferNewReverb (GM_Voice *this_voice);
void PV_ServeU3232FilterFullBufferNewReverb16 (GM_Voice *this_voice);
void PV_ServeU3232StereoFilterFullBufferNewReverb16 (GM_Voice *this_voice);

void PV_ServeU3232FilterFullBuffer (GM_Voice *this_voice);
void PV_ServeU3232StereoFilterFullBuffer (GM_Voice *this_voice);
void PV_ServeU3232FilterFullBuffer16 (GM_Voice *this_voice);
void PV_ServeU3232StereoFilterFullBuffer16 (GM_Voice *this_voice);

void PV_ServeU3232FilterPartialBuffer (GM_Voice *this_voice, XBOOL looping);
void PV_ServeU3232StereoFilterPartialBuffer (GM_Voice *this_voice, XBOOL looping);
void PV_ServeU3232FilterPartialBuffer16 (GM_Voice *this_voice, XBOOL looping);
void PV_ServeU3232StereoFilterPartialBuffer16 (GM_Voice *this_voice, XBOOL looping);

void PV_ServeU3232FilterPartialBufferNewReverb (GM_Voice *this_voice, XBOOL looping);
void PV_ServeU3232FilterPartialBufferNewReverb16 (GM_Voice *this_voice, XBOOL looping);
void PV_ServeU3232StereoFilterPartialBufferNewReverb (GM_Voice *this_voice, XBOOL looping);
void PV_ServeU3232StereoFilterPartialBufferNewReverb16 (GM_Voice *this_voice, XBOOL looping);

void PV_ServeU3232FullBuffer (GM_Voice *this_voice);
void PV_ServeU3232StereoFullBuffer (GM_Voice *this_voice);
void PV_ServeU3232FullBuffer16 (GM_Voice *this_voice);
void PV_ServeU3232StereoFullBuffer16 (GM_Voice *this_voice);

void PV_ServeU3232PartialBuffer (GM_Voice *this_voice, XBOOL looping);
void PV_ServeU3232StereoPartialBuffer (GM_Voice *this_voice, XBOOL looping);
void PV_ServeU3232PartialBuffer16 (GM_Voice *this_voice, XBOOL looping);
void PV_ServeU3232StereoPartialBuffer16 (GM_Voice *this_voice, XBOOL looping);

void PV_ServeU3232FullBufferNewReverb (GM_Voice *this_voice);
void PV_ServeU3232StereoFullBufferNewReverb (GM_Voice *this_voice);
void PV_ServeU3232FullBuffer16NewReverb (GM_Voice *this_voice);
void PV_ServeU3232StereoFullBuffer16NewReverb (GM_Voice *this_voice);

void PV_ServeU3232PartialBufferNewReverb (GM_Voice *this_voice, XBOOL looping);
void PV_ServeU3232StereoPartialBufferNewReverb (GM_Voice *this_voice, XBOOL looping);
void PV_ServeU3232PartialBuffer16NewReverb (GM_Voice *this_voice, XBOOL looping);
void PV_ServeU3232StereoPartialBuffer16NewReverb (GM_Voice *this_voice, XBOOL looping);
#endif

#if USE_FLOAT_LOOPS != FALSE
void PV_ServeFloatFilterFullBufferNewReverb (GM_Voice *this_voice);
void PV_ServeStereoFloatFilterFullBufferNewReverb (GM_Voice *this_voice);
void PV_ServeFloatFilterFullBufferNewReverb16 (GM_Voice *this_voice);
void PV_ServeStereoFloatFilterFullBufferNewReverb16 (GM_Voice *this_voice);

void PV_ServeFloatFilterFullBuffer (GM_Voice *this_voice);
void PV_ServeStereoFloatFilterFullBuffer (GM_Voice *this_voice);
void PV_ServeFloatFilterFullBuffer16 (GM_Voice *this_voice);
void PV_ServeStereoFloatFilterFullBuffer16 (GM_Voice *this_voice);

void PV_ServeFloatFilterPartialBuffer (GM_Voice *this_voice, XBOOL looping);
void PV_ServeStereoFloatFilterPartialBuffer (GM_Voice *this_voice, XBOOL looping);
void PV_ServeFloatFilterPartialBuffer16 (GM_Voice *this_voice, XBOOL looping);
void PV_ServeStereoFloatFilterPartialBuffer16 (GM_Voice *this_voice, XBOOL looping);

void PV_ServeFloatFilterPartialBufferNewReverb (GM_Voice *this_voice, XBOOL looping);
void PV_ServeFloatFilterPartialBufferNewReverb16 (GM_Voice *this_voice, XBOOL looping);
void PV_ServeStereoFloatFilterPartialBufferNewReverb (GM_Voice *this_voice, XBOOL looping);
void PV_ServeStereoFloatFilterPartialBufferNewReverb16 (GM_Voice *this_voice, XBOOL looping);

void PV_ServeFloatFullBuffer (GM_Voice *this_voice);
void PV_ServeStereoFloatFullBuffer (GM_Voice *this_voice);
void PV_ServeFloatFullBuffer16 (GM_Voice *this_voice);
void PV_ServeStereoFloatFullBuffer16 (GM_Voice *this_voice);

void PV_ServeFloatPartialBuffer (GM_Voice *this_voice, XBOOL looping);
void PV_ServeStereoFloatPartialBuffer (GM_Voice *this_voice, XBOOL looping);
void PV_ServeFloatPartialBuffer16 (GM_Voice *this_voice, XBOOL looping);
void PV_ServeStereoFloatPartialBuffer16 (GM_Voice *this_voice, XBOOL looping);

void PV_ServeFloatFullBufferNewReverb (GM_Voice *this_voice);
void PV_ServeStereoFloatFullBufferNewReverb (GM_Voice *this_voice);
void PV_ServeFloatFullBuffer16NewReverb (GM_Voice *this_voice);
void PV_ServeStereoFloatFullBuffer16NewReverb (GM_Voice *this_voice);

void PV_ServeFloatPartialBufferNewReverb (GM_Voice *this_voice, XBOOL looping);
void PV_ServeStereoFloatPartialBufferNewReverb (GM_Voice *this_voice, XBOOL looping);
void PV_ServeFloatPartialBuffer16NewReverb (GM_Voice *this_voice, XBOOL looping);
void PV_ServeStereoFloatPartialBuffer16NewReverb (GM_Voice *this_voice, XBOOL looping);
#endif

void PV_ServeInterp2FilterFullBufferNewReverb (GM_Voice *this_voice);
void PV_ServeStereoInterp2FilterFullBufferNewReverb (GM_Voice *this_voice);
void PV_ServeInterp2FilterFullBufferNewReverb16 (GM_Voice *this_voice);
void PV_ServeStereoInterp2FilterFullBufferNewReverb16 (GM_Voice *this_voice);

void PV_ServeInterp2FilterFullBuffer (GM_Voice *this_voice);
void PV_ServeStereoInterp2FilterFullBuffer (GM_Voice *this_voice);
void PV_ServeInterp2FilterFullBuffer16 (GM_Voice *this_voice);
void PV_ServeStereoInterp2FilterFullBuffer16 (GM_Voice *this_voice);

void PV_ServeInterp2FilterPartialBuffer (GM_Voice *this_voice, XBOOL looping);
void PV_ServeStereoInterp2FilterPartialBuffer (GM_Voice *this_voice, XBOOL looping);
void PV_ServeInterp2FilterPartialBuffer16 (GM_Voice *this_voice, XBOOL looping);
void PV_ServeStereoInterp2FilterPartialBuffer16 (GM_Voice *this_voice, XBOOL looping);

void PV_ServeInterp2FilterPartialBufferNewReverb (GM_Voice *this_voice, XBOOL looping);
void PV_ServeInterp2FilterPartialBufferNewReverb16 (GM_Voice *this_voice, XBOOL looping);
void PV_ServeStereoInterp2FilterPartialBufferNewReverb (GM_Voice *this_voice, XBOOL looping);
void PV_ServeStereoInterp2FilterPartialBufferNewReverb16 (GM_Voice *this_voice, XBOOL looping);

void PV_ServeInterp2FullBuffer (GM_Voice *this_voice);
void PV_ServeStereoInterp2FullBuffer (GM_Voice *this_voice);
void PV_ServeInterp2FullBuffer16 (GM_Voice *this_voice);
void PV_ServeStereoInterp2FullBuffer16 (GM_Voice *this_voice);

void PV_ServeInterp2PartialBuffer (GM_Voice *this_voice, XBOOL looping);
void PV_ServeStereoInterp2PartialBuffer (GM_Voice *this_voice, XBOOL looping);
void PV_ServeInterp2PartialBuffer16 (GM_Voice *this_voice, XBOOL looping);
void PV_ServeStereoInterp2PartialBuffer16 (GM_Voice *this_voice, XBOOL looping);

void PV_ServeInterp2FullBufferNewReverb (GM_Voice *this_voice);
void PV_ServeStereoInterp2FullBufferNewReverb (GM_Voice *this_voice);
void PV_ServeInterp2FullBuffer16NewReverb (GM_Voice *this_voice);
void PV_ServeStereoInterp2FullBuffer16NewReverb (GM_Voice *this_voice);

void PV_ServeInterp2PartialBufferNewReverb (GM_Voice *this_voice, XBOOL looping);
void PV_ServeStereoInterp2PartialBufferNewReverb (GM_Voice *this_voice, XBOOL looping);
void PV_ServeInterp2PartialBuffer16NewReverb (GM_Voice *this_voice, XBOOL looping);
void PV_ServeStereoInterp2PartialBuffer16NewReverb (GM_Voice *this_voice, XBOOL looping);

void PV_ServeInterp1FullBuffer (GM_Voice *this_voice);
void PV_ServeInterp1PartialBuffer (GM_Voice *this_voice, XBOOL looping);
void PV_ServeStereoInterp1FullBuffer (GM_Voice *this_voice);
void PV_ServeStereoInterp1PartialBuffer (GM_Voice *this_voice, XBOOL looping);

void PV_ServeDropSampleFullBuffer (GM_Voice *this_voice);
void PV_ServeDropSamplePartialBuffer (GM_Voice *this_voice, XBOOL looping);
void PV_ServeDropSampleFullBuffer16 (GM_Voice *this_voice);
void PV_ServeDropSamplePartialBuffer16 (GM_Voice *this_voice, XBOOL looping);
void PV_ServeStereoAmpFullBuffer (GM_Voice *this_voice);
void PV_ServeStereoAmpPartialBuffer (GM_Voice *this_voice, XBOOL looping);


void PV_StartMIDINote(GM_Song *pSong, XSWORD the_instrument, 
						XSWORD the_channel, XSWORD the_track, XSWORD notePitch, XSDWORD Volume);
void PV_StopMIDINote(GM_Song *pSong, XSWORD the_instrument, 
						XSWORD the_channel, XSWORD the_track, XSWORD notePitch);

// voices modifiers
XSWORD SetChannelPitchBend(GM_Song *pSong, XSWORD the_channel, XBYTE bendRange, XBYTE bendMSB, XBYTE bendLSB);
void SetChannelVolume(GM_Song *pSong, XSWORD the_channel, XSWORD newVolume);
XSWORD SetChannelStereoPosition(GM_Song *pSong, XSWORD the_channel, XWORD newPosition);
void SetChannelModWheel(GM_Song *pSong, XSWORD the_channel, XWORD value);
void PV_ChangeSustainedNotes(GM_Song *pSong, XSWORD the_channel, XSWORD data);

void PV_CleanExternalQueue(GM_Mixer *pMixer);

// process 11 ms worth of sample data
void PV_ProcessSampleFrame(void *threadContext, void *destSampleData);
void PV_ProcessSequencerEvents(void *threadContext);

OPErr PV_ProcessMidiSequencerSlice(void *threadContext, GM_Song *pSong);

void PV_ConfigureInstruments(GM_Song *theSong);
OPErr PV_ConfigureMusic(GM_Song *theSong);
void PV_ResetControlers(GM_Song *pSong, XSWORD channel2Reset, XBOOL completeReset);

// GenPatch.c
GM_Instrument * PV_GetInstrument(XLongResourceID theID, void *theExternalX, long patchSize, OPErr *pErr);
void PV_SetSampleIntoCache(GM_Song *pSong, XLongResourceID theID, XPTR pSndFormatData, OPErr *pErr);

XDWORD PV_ScaleVolumeFromChannelAndSong(GM_Song *pSong, XSWORD channel, XDWORD volume);
void PV_DoCallBack(GM_Voice *this_one);
void PV_CleanNoteEntry(GM_Voice * the_entry);
void PV_CalcScaleBack(void);


XFIXED PV_GetWavePitch(XFIXED notePitch);
#if USE_FLOAT_LOOPS != FALSE
UFLOAT PV_GetWavePitchFloat(XFIXED notePitch);
#define FRAC(num, num_whole)		((num) - (num_whole))
#endif
#if USE_U3232_LOOPS != FALSE
UFLOAT PV_GetWavePitchFloat(XFIXED notePitch);
U3232 PV_GetWavePitchU3232(XFIXED notePitch);
#endif

// get voice sample position from active voice
unsigned long PV_GetPositionFromVoice(GM_Voice *pVoice);
void PV_SetPositionFromVoice(GM_Voice *pVoice, unsigned long pos);

// GenModFiles.c
void PV_WriteModOutput(Quality q, XBOOL stereo);

// GenAudioStreams.c
void PV_ServeStreamFades(void);

// GenSeq.c
void PV_FreePatchInfo(GM_Song *pSong);
void PV_InsertBankSelect(GM_Song *pSong, short channel, short currentTrack);
// process end song callback
void PV_CallSongCallback(void *threadContext, GM_Song *theSong, XBOOL clearCallback);

// GenSynth.c
long PV_ModifyVelocityFromCurve(GM_Song *pSong, long volume);

// GenSample.c
GM_Voice * PV_GetVoiceFromSoundReference(VOICE_REFERENCE reference);

// GenSetup.c
#if (X_PLATFORM == X_WIN95) && (USE_KAT)
XBOOL PV_IntelKatActive(void);
#endif

#ifdef __cplusplus
	}
#endif

#endif 	/* G_PRIVATE	*/ 
