/*
 * Main source code was taken from Pete's PSX sound plugin.
 * I plan full source reorganising - want to set more sound
 * engine behaviour
 */



#ifndef SOUND_MANAGER_H
#define SOUND_MANAGER_H



#include "common/TypeDefine.h"



class SoundManager
{
public:
    SoundManager();
    virtual ~SoundManager();

    void Update();

    void PsxChannelPlay(u8 arg_channel);
    void PsxChannelStop(u8 arg_channel);
    void PsxReverbOn(u8 arg_channel);
    void PsxSetReverbDepth( u16 arg_left, u16 arg_right );

    enum SpuRateMode
    {
        SPU_VOICE_LINEARIncN = 1,
        SPU_VOICE_LINEARDecN = 3,
        SPU_VOICE_EXPIncN    = 5,
        SPU_VOICE_EXPDec     = 7
    };

    static const u32 m_PSX_SPU_BASE = 0x1F801C00;

    void SetVoiceStartAddress(u8 arg_voice, u32 arg_address);
    void SetVoiceLoopAddress(u8 arg_voice, u32 arg_address);
    void SetVoiceVolume(u8 arg_voice, s16 arg_volume_left, s16 arg_volume_right);
    void SetVoicePitch(u8 arg_voice, u16 arg_pitch);
    void SetVoiceAttackRate(u8 arg_voice, u16 arg_rate, SpuRateMode arg_mode);
    void SetVoiceSustainRate(u8 arg_voice, u16 arg_rate, SpuRateMode arg_mode);
    void SetVoiceReleaseRate(u8 arg_voice, u16 arg_rate, SpuRateMode arg_mode);
    void SetVoiceDecayRate(u8 arg_voice, u16 arg_rate);
    void SetVoiceSustainLevel(u8 arg_voice, u16 arg_level);

    static const u32 m_REGISTER_BASE = 0x1f801c00;

    bool PsxWriteDMA(u16 arg_address, u8 *arg_data, u32 arg_length);
};



extern SoundManager *SOUNDMAN;

void InitADSR();
void StartADSR(int ch);
int MixADSR(int ch);



// PETE ///////////////
unsigned short SPUreadDMA();
void SPUreadDMAMem( unsigned short *pusPSXMem, int iSize );
void SPUwriteDMA( unsigned short val );
void SPUwriteDMAMem( unsigned short *pusPSXMem, int iSize );
// PETE ///////////////




extern const int gauss[];

#define H_SPUReverbAddr  0x0da2
#define H_SPUirqAddr     0x0da4
#define H_SPUaddr        0x0da6
#define H_SPUdata        0x0da8
#define H_SPUctrl        0x0daa
#define H_SPUstat        0x0dae
#define H_SPUmvolL       0x0d80
#define H_SPUmvolR       0x0d82
#define H_SPUrvolL       0x0d84
#define H_SPUrvolR       0x0d86
#define H_SPUon1         0x0d88
#define H_SPUon2         0x0d8a
#define H_SPUoff1        0x0d8c
#define H_SPUoff2        0x0d8e
#define H_FMod1          0x0d90
#define H_FMod2          0x0d92
#define H_Noise1         0x0d94
#define H_Noise2         0x0d96
#define H_RVBon1         0x0d98
#define H_RVBon2         0x0d9a
#define H_SPUMute1       0x0d9c
#define H_SPUMute2       0x0d9e
#define H_CDLeft         0x0db0
#define H_CDRight        0x0db2
#define H_ExtLeft        0x0db4
#define H_ExtRight       0x0db6
#define H_Reverb         0x0dc0
#define H_SPUPitch0      0x0c04
#define H_SPUPitch1      0x0c14
#define H_SPUPitch2      0x0c24
#define H_SPUPitch3      0x0c34
#define H_SPUPitch4      0x0c44
#define H_SPUPitch5      0x0c54
#define H_SPUPitch6      0x0c64
#define H_SPUPitch7      0x0c74
#define H_SPUPitch8      0x0c84
#define H_SPUPitch9      0x0c94
#define H_SPUPitch10     0x0ca4
#define H_SPUPitch11     0x0cb4
#define H_SPUPitch12     0x0cc4
#define H_SPUPitch13     0x0cd4
#define H_SPUPitch14     0x0ce4
#define H_SPUPitch15     0x0cf4
#define H_SPUPitch16     0x0d04
#define H_SPUPitch17     0x0d14
#define H_SPUPitch18     0x0d24
#define H_SPUPitch19     0x0d34
#define H_SPUPitch20     0x0d44
#define H_SPUPitch21     0x0d54
#define H_SPUPitch22     0x0d64
#define H_SPUPitch23     0x0d74

#define H_SPUStartAdr0   0x0c06
#define H_SPUStartAdr1   0x0c16
#define H_SPUStartAdr2   0x0c26
#define H_SPUStartAdr3   0x0c36
#define H_SPUStartAdr4   0x0c46
#define H_SPUStartAdr5   0x0c56
#define H_SPUStartAdr6   0x0c66
#define H_SPUStartAdr7   0x0c76
#define H_SPUStartAdr8   0x0c86
#define H_SPUStartAdr9   0x0c96
#define H_SPUStartAdr10  0x0ca6
#define H_SPUStartAdr11  0x0cb6
#define H_SPUStartAdr12  0x0cc6
#define H_SPUStartAdr13  0x0cd6
#define H_SPUStartAdr14  0x0ce6
#define H_SPUStartAdr15  0x0cf6
#define H_SPUStartAdr16  0x0d06
#define H_SPUStartAdr17  0x0d16
#define H_SPUStartAdr18  0x0d26
#define H_SPUStartAdr19  0x0d36
#define H_SPUStartAdr20  0x0d46
#define H_SPUStartAdr21  0x0d56
#define H_SPUStartAdr22  0x0d66
#define H_SPUStartAdr23  0x0d76

#define H_SPULoopAdr0   0x0c0e
#define H_SPULoopAdr1   0x0c1e
#define H_SPULoopAdr2   0x0c2e
#define H_SPULoopAdr3   0x0c3e
#define H_SPULoopAdr4   0x0c4e
#define H_SPULoopAdr5   0x0c5e
#define H_SPULoopAdr6   0x0c6e
#define H_SPULoopAdr7   0x0c7e
#define H_SPULoopAdr8   0x0c8e
#define H_SPULoopAdr9   0x0c9e
#define H_SPULoopAdr10  0x0cae
#define H_SPULoopAdr11  0x0cbe
#define H_SPULoopAdr12  0x0cce
#define H_SPULoopAdr13  0x0cde
#define H_SPULoopAdr14  0x0cee
#define H_SPULoopAdr15  0x0cfe
#define H_SPULoopAdr16  0x0d0e
#define H_SPULoopAdr17  0x0d1e
#define H_SPULoopAdr18  0x0d2e
#define H_SPULoopAdr19  0x0d3e
#define H_SPULoopAdr20  0x0d4e
#define H_SPULoopAdr21  0x0d5e
#define H_SPULoopAdr22  0x0d6e
#define H_SPULoopAdr23  0x0d7e

#define H_SPU_ADSRLevel0   0x0c08
#define H_SPU_ADSRLevel1   0x0c18
#define H_SPU_ADSRLevel2   0x0c28
#define H_SPU_ADSRLevel3   0x0c38
#define H_SPU_ADSRLevel4   0x0c48
#define H_SPU_ADSRLevel5   0x0c58
#define H_SPU_ADSRLevel6   0x0c68
#define H_SPU_ADSRLevel7   0x0c78
#define H_SPU_ADSRLevel8   0x0c88
#define H_SPU_ADSRLevel9   0x0c98
#define H_SPU_ADSRLevel10  0x0ca8
#define H_SPU_ADSRLevel11  0x0cb8
#define H_SPU_ADSRLevel12  0x0cc8
#define H_SPU_ADSRLevel13  0x0cd8
#define H_SPU_ADSRLevel14  0x0ce8
#define H_SPU_ADSRLevel15  0x0cf8
#define H_SPU_ADSRLevel16  0x0d08
#define H_SPU_ADSRLevel17  0x0d18
#define H_SPU_ADSRLevel18  0x0d28
#define H_SPU_ADSRLevel19  0x0d38
#define H_SPU_ADSRLevel20  0x0d48
#define H_SPU_ADSRLevel21  0x0d58
#define H_SPU_ADSRLevel22  0x0d68
#define H_SPU_ADSRLevel23  0x0d78

// we have a timebase of 1.020408f ms, not 1 ms... so adjust adsr defines
#define ATTACK_MS      494L
#define DECAYHALF_MS   286L
#define DECAY_MS       572L
#define SUSTAIN_MS     441L
#define RELEASE_MS     437L

#ifndef LOWORD
#define LOWORD(l)    ((unsigned short)(l))
#endif

#ifndef HIWORD
#define HIWORD(l)    ((unsigned short)(((unsigned long)(l) >> 16) & 0xFFFF))
#endif

extern int bSPUIsOpen;


typedef struct
{
	long y0, y1;
} ADPCM_Decode_t;

typedef struct
{
	int freq;
	int nbits;
	int stereo;
	int nsamples;
	ADPCM_Decode_t left, right;
	short pcm[16384];
} xa_decode_t;



extern xa_decode_t *xapGlobal;

extern u32 *XAFeed;
extern u32 *XAPlay;
extern u32 *XAStart;
extern u32 *XAEnd;

extern u32 XARepeat;
extern u32 XALastVal;

extern int iLeftXAVol;
extern int iRightXAVol;

#define PSE_LT_SPU                  4
#define PSE_SPU_ERR_SUCCESS         0
#define PSE_SPU_ERR                 -60
#define PSE_SPU_ERR_NOTCONFIGURED   PSE_SPU_ERR - 1
#define PSE_SPU_ERR_INIT            PSE_SPU_ERR - 2
#ifndef max
#define max(a,b)            (((a) > (b)) ? (a) : (b))
#define min(a,b)            (((a) < (b)) ? (a) : (b))
#endif

// 137 ms test buffer... if less than that is buffered, a new upload will happen
#define TESTSIZE    24192
// num of channels
#define MAXCHAN     24
// ~ 1 ms of data
#define NSSIZE 45



// ADSR infos per channel
typedef struct
{
	int            AttackModeExp;
	long           AttackTime;
	long           DecayTime;
	long           SustainLevel;
	int            SustainModeExp;
	long           SustainModeDec;
	long           SustainTime;
	int            ReleaseModeExp;
	unsigned long  ReleaseVal;
	long           ReleaseTime;
	long           ReleaseStartTime;
	long           ReleaseVol;
	long           lTime;
	long           lVolume;
} ADSRInfo;

typedef struct
{
	int            State;
	int            AttackModeExp;
	int            AttackRate;
	int            DecayRate;
	int            SustainLevel;
	int            SustainModeExp;
	int            SustainIncrease;
	int            SustainRate;
	int            ReleaseModeExp;
	int            ReleaseRate;
	int            EnvelopeVol;
	long           lVolume;
	long           lDummy1;
	long           lDummy2;
} ADSRInfoEx;

// used for debug channel muting
#define FLAG_MUTE  1
// used for simple interpolation
#define FLAG_IPOL0 2
#define FLAG_IPOL1 4


// MAIN CHANNEL STRUCT
typedef struct
{
	int bNew;              // start flag
	int iSBPos;            // mixing stuff
	int spos;
	int sinc;
	int SB[32 + 32];       // Pete added another 32 dwords in 1.6 ...
	                       // prevents overflow issues with gaussian/cubic
						   // interpolation (thanx xodnizel!), and can be
						   // used for even better interpolations, eh? :)
	int sval;

	unsigned char *pStart; // start ptr into sound mem
	unsigned char *pCurr;  // current pos in sound mem
	unsigned char *pLoop;  // loop ptr in sound mem

	int bOn;               // is channel active (sample playing?)
	int bStop;             // is ch stop (can still be playing, if ADSR Release)
	int bReverb;           // reverb, must have ctrl register bit, to get active
	int iActFreq;          // current psx pitch
	int iUsedFreq;         // current pc pitch
	int iLeftVolume;       // left volume
	int iLeftVolRaw;       // left psx volume value
	int bIgnoreLoop;       // ignore loop, if an external loop address is used
	int iMute;             // mute mode
	int iRightVolume;      // right volume
	int iRightVolRaw;      // right psx volume value
	int iRawPitch;         // raw pitch (0...3fff)
	int iIrqDone;          // debug irq done flag
	int s_1;               // last decoding infos
	int s_2;
	int bRVBActive;        // reverb active flag
	int iRVBOffset;        // reverb offset
	int iRVBRepeat;        // reverb repeat
	int bNoise;            // noise active flag
	int bFMod;             // freq mod (0=off, 1=sound channel, 2=freq channel)
	int iRVBNum;           // another reverb helper
	int iOldNoise;         // old noise val for this channel
	ADSRInfo ADSR;         // active ADSR settings
	ADSRInfoEx ADSRX;      // next ADSR settings
					       // (will be moved to active on sample start)
} SPUCHAN;


// REVERB
typedef struct
{
	int StartAddr;      // reverb area start addr in samples
	int CurrAddr;       // reverb area curr addr in samples

	int VolLeft;
	int VolRight;
	int iLastRVBLeft;
	int iLastRVBRight;
	int iRVBLeft;
	int iRVBRight;


	int FB_SRC_A;       // (offset)
	int FB_SRC_B;       // (offset)
	int IIR_ALPHA;      // (coef.)
	int ACC_COEF_A;     // (coef.)
	int ACC_COEF_B;     // (coef.)
	int ACC_COEF_C;     // (coef.)
	int ACC_COEF_D;     // (coef.)
	int IIR_COEF;       // (coef.)
	int FB_ALPHA;       // (coef.)
	int FB_X;           // (coef.)
	int IIR_DEST_A0;    // (offset)
	int IIR_DEST_A1;    // (offset)
	int ACC_SRC_A0;     // (offset)
	int ACC_SRC_A1;     // (offset)
	int ACC_SRC_B0;     // (offset)
	int ACC_SRC_B1;     // (offset)
	int IIR_SRC_A0;     // (offset)
	int IIR_SRC_A1;     // (offset)
	int IIR_DEST_B0;    // (offset)
	int IIR_DEST_B1;    // (offset)
	int ACC_SRC_C0;     // (offset)
	int ACC_SRC_C1;     // (offset)
	int ACC_SRC_D0;     // (offset)
	int ACC_SRC_D1;     // (offset)
	int IIR_SRC_B1;     // (offset)
	int IIR_SRC_B0;     // (offset)
	int MIX_DEST_A0;    // (offset)
	int MIX_DEST_A1;    // (offset)
	int MIX_DEST_B0;    // (offset)
	int MIX_DEST_B1;    // (offset)
	int IN_COEF_L;      // (coef.)
	int IN_COEF_R;      // (coef.)
} REVERBInfo;


extern char * pConfigFile;

extern int *          sRVBPlay;
extern int *          sRVBEnd;
extern int *          sRVBStart;
extern int            iReverbOff;
extern int            iReverbRepeat;
extern int            iReverbNum;

// psx buffers / addresses
extern unsigned short  regArea[];
extern unsigned short  spuMem[];
extern unsigned char * spuMemC;
extern unsigned char * pSpuIrq;
extern unsigned char * pSpuBuffer;

// user settings
extern int        iUseXA;
extern int        iVolume;
extern int        iXAPitch;
extern int        iUseTimer;
extern int        iSPUIRQWait;
extern int        iDebugMode;
extern int        iRecordMode;
extern int        iUseReverb;
extern int        iUseInterpolation;
extern int        iDisStereo;
// MISC

extern int iWatchDog;

//extern SPUCHAN s_chan[];
extern REVERBInfo rvb;

extern unsigned long dwNoiseVal;
extern unsigned short spuCtrl;
extern unsigned short spuStat;
extern unsigned short spuIrq;
extern unsigned long  spuAddr;
extern int      bEndThread;
extern int      bThreadEnded;
extern int      bSpuInit;
extern unsigned long dwNewChannel;

extern int      SSumR[];
extern int      SSumL[];
extern int      iCycle;
extern short *  pS;

extern void (*cddavCallback)(unsigned short,unsigned short);
extern SPUCHAN s_chan[];



void SPUwriteRegister(unsigned long reg, unsigned short val);
unsigned short SPUreadRegister(unsigned long reg);
void SoundOn(int start, int end, unsigned short val);
void SoundOff(int start, int end, unsigned short val);
void FModOn(int start, int end, unsigned short val);
void NoiseOn(int start, int end, unsigned short val);
void SetVolumeL(unsigned char ch, short vol);
void SetVolumeR(unsigned char ch, short vol);
void SetPitch(int ch, unsigned short val);
void ReverbOn(int start, int end, unsigned short val);

void SetREVERB(unsigned short val);
void StartREVERB(int ch);
void InitREVERB();
void StoreREVERB(int ch, int ns);
int g_buffer(int iOff);
void s_buffer(int iOff, int iVal);
void s_buffer1(int iOff, int iVal);
int MixREVERBLeft(int ns);
int MixREVERBRight();

void MixXA();
void FeedXA(xa_decode_t *xap);

long SPUinit(void);
long SPUopen(void);
long SPUclose(void);
void SPUasync(unsigned long cycle);
void SPUplayADPCMchannel(xa_decode_t *xap);
unsigned short SPUgetOne(unsigned long val);
void SPUputOne(unsigned long val,unsigned short data);
void SPUplaySample(unsigned char ch);
void SPUsetAddr(unsigned char ch, unsigned short waddr);
void SPUsetPitch(unsigned char ch, unsigned short pitch);
void SPUsetVolumeL(unsigned char ch, short vol);
void SPUsetVolumeR(unsigned char ch, short vol);
void SPUstartChannels1(unsigned short channels);
void SPUstartChannels2(unsigned short channels);
void SPUstopChannels1(unsigned short channels);
void SPUstopChannels2(unsigned short channels);
void SPUplaySector(unsigned long mode, unsigned char * p);
u32 GGetTime();



#endif

