//---------------------------------------------------------------------------
/*
	TVP2 ( T Visual Presenter 2 )  A script authoring tool
	Copyright (C) 2000 W.Dee <dee@kikyou.info> and contributors

	See details of license at "license.txt"
*/
//---------------------------------------------------------------------------
// Wave Player implementation
//---------------------------------------------------------------------------
#ifndef WaveImplH
#define WaveImplH

#define DIRECTSOUND_VERSION 0x0300

#include <mmsystem.h>
#include <dsound.h>
#include <ks.h>
#include <ksmedia.h>

#include "WaveIntf.h"
#include "WaveLoopManager.h"

/*[*/
//---------------------------------------------------------------------------
// IDirectSound former declaration
//---------------------------------------------------------------------------
#ifndef __DSOUND_INCLUDED__
struct IDirectSound;
#endif



/*]*/

//---------------------------------------------------------------------------
// Constants
//---------------------------------------------------------------------------

#define TVP_WSB_ACCESS_FREQ (8)  // wave sound buffer access frequency (hz)

#define TVP_TIMEOFS_INVALID_VALUE ((tjs_int)(- 2147483648i64)) // invalid value for 32bit time offset

//---------------------------------------------------------------------------

TJS_EXP_FUNC_DEF(void, TVPReleaseDirectSound, ());
TJS_EXP_FUNC_DEF(IDirectSound *, TVPGetDirectSound, ());
extern void TVPResetVolumeToAllSoundBuffer();
extern void TVPSetWaveSoundBufferUse3DMode(bool b);
extern bool TVPGetWaveSoundBufferUse3DMode();
extern void TVPWaveSoundBufferCommitSettings();
extern tjs_int TVPVolumeToDSAttenuate(tjs_int volume);
extern tjs_int TVPDSAttenuateToVolume(tjs_int att);
extern tjs_int TVPPanToDSAttenuate(tjs_int volume);
extern tjs_int TVPDSAttenuateToPan(tjs_int att);
//---------------------------------------------------------------------------



//---------------------------------------------------------------------------
// tTJSNI_WaveSoundBuffer : Wave Native Instance
//---------------------------------------------------------------------------
class tTVPWaveLoopManager;
class tTVPWaveSoundBufferDecodeThread;
class tTJSNI_WaveSoundBuffer : public tTJSNI_BaseWaveSoundBuffer
{
	typedef  tTJSNI_BaseWaveSoundBuffer inherited;

public:
	tTJSNI_WaveSoundBuffer();
	tjs_error TJS_INTF_METHOD Construct(tjs_int numparams, tTJSVariant **param,
		iTJSDispatch2 *tjs_obj);
	void TJS_INTF_METHOD Invalidate();

	//-- buffer management ------------------------------------------------
private:
	LPDIRECTSOUNDBUFFER SoundBuffer;
	LPDIRECTSOUND3DBUFFER Sound3DBuffer;

	void ThrowSoundBufferException(const ttstr &reason);
	void TryCreateSoundBuffer(bool use3d);
	void CreateSoundBuffer();
	void DestroySoundBuffer();
	void ResetSoundBuffer();
	void ResetSamplePositions();

	WAVEFORMATEXTENSIBLE Format;

	tTVPWaveFormat C_InputFormat;
	tTVPWaveFormat InputFormat;

	tjs_int BufferBytes;
	tjs_int AccessUnitBytes;
	tjs_int AccessUnitSamples;
	tjs_int L2AccessUnitBytes;

	tjs_int L2BufferUnits;
	tjs_int L1BufferUnits;

	tjs_int Level2BufferSize;
	tjs_uint8 *Level2Buffer;
public:
	void FreeDirectSoundBuffer(bool disableevent = true)
	{
		// called at exit ( system uninitialization )
		bool b = CanDeliverEvents;
		if(disableevent)
			CanDeliverEvents = false; // temporarily disables event derivering
		Stop();
		DestroySoundBuffer();
		CanDeliverEvents = b;
	}


	//-- playing stuff ----------------------------------------------------
private:
	tTJSCriticalSection BufferCS;
	tTJSCriticalSection L2BufferCS;
	tTJSCriticalSection L2BufferRemainCS;

public:
	tTJSCriticalSection & GetBufferCS() { return BufferCS; }

private:
	tTVPWaveDecoder * Decoder;
	tTVPWaveSoundBufferDecodeThread * Thread;
public:
	bool ThreadCallbackEnabled;
private:
	bool BufferPlaying; // whether this sound buffer is playing
	bool DSBufferPlaying; // whether the DS buffer is 'actually' playing
	bool Paused;

	bool UseVisBuffer;

	tjs_int SoundBufferPrevReadPos;
	tjs_int SoundBufferWritePos;
	tjs_int PlayStopPos; // in bytes

	tjs_int L2BufferReadPos; // in unit
	tjs_int L2BufferWritePos;
	tjs_int L2BufferRemain;
	bool L2BufferEnded;
	tjs_uint8 *VisBuffer; // buffer for visualization
	tjs_int *L2BufferDecodedSamplesInUnit;
	tTVPWaveSegmentQueue *L1BufferSegmentQueues;
	std::vector<tTVPWaveLabel> LabelEventQueue;
	tjs_int64 *L1BufferDecodeSamplePos;
	tTVPWaveSegmentQueue *L2BufferSegmentQueues;

	tjs_int64 DecodePos; // decoded samples from directsound buffer play
	tjs_int64 LastCheckedDecodePos; // last sured position (-1 for not checked) and 
	tjs_uint64 LastCheckedTick; // last sured tick time

	bool Looping;

	void Clear();

	tjs_uint Decode(void *buffer, tjs_uint bufsamplelen,
		tTVPWaveSegmentQueue & segments);

public:
	bool FillL2Buffer(bool firstwrite, bool fromdecodethread);

private:
	void PrepareToReadL2Buffer(bool firstread);
	tjs_uint ReadL2Buffer(void *buffer,
		tTVPWaveSegmentQueue & segments);

	void FillDSBuffer(tjs_int writepos,
		tTVPWaveSegmentQueue & segments);
public:
	bool FillBuffer(bool firstwrite = false, bool allowpause = true);

private:
	void ResetLastCheckedDecodePos(DWORD pp = (DWORD)-1);

public:
	tjs_int FireLabelEventsAndGetNearestLabelEventStep(tjs_int64 tick);
	tjs_int GetNearestEventStep();
	void FlushAllLabelEvents();

private:
	void StartPlay();
	void StopPlay();

public:
	void Play();
	void Stop();

	bool GetPaused() const { return Paused; }
	void SetPaused(bool b);

	tjs_int GetBitsPerSample() const {
		if(Format.Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE)
			return Format.Samples.wValidBitsPerSample;
		else
			return Format.Format.wBitsPerSample; }
	tjs_int GetChannels() const { return Format.Format.nChannels; }

protected:
	void TimerBeatHandler(); // override

public:
	void Open(const ttstr & storagename);

public:
	void SetLooping(bool b);
	bool GetLooping() const { return Looping; }

    tjs_uint64 GetSamplePosition();
	void SetSamplePosition(tjs_uint64 pos);

    tjs_uint64 GetPosition();
	void SetPosition(tjs_uint64 pos);

	tjs_uint64 GetTotalTime();

	//-- volume/pan/3D position/freq stuff -------------------------------------
private:
	tjs_int Volume;
	tjs_int Volume2;
	tjs_int Frequency;
	static tjs_int GlobalVolume;
	static tTVPSoundGlobalFocusMode GlobalFocusMode;

	bool BufferCanControlPan;
	bool BufferCanControlFrequency;
	tjs_int Pan; // -100000 .. 0 .. 100000
	D3DVALUE PosX, PosY, PosZ; // 3D position

public:
	void SetVolumeToSoundBuffer();

public:
	void SetVolume(tjs_int v);
	tjs_int GetVolume() const { return Volume; }
	void SetVolume2(tjs_int v);
	tjs_int GetVolume2() const { return Volume2; }
	void SetPan(tjs_int v);
	tjs_int GetPan() const { return Pan; }
	static void SetGlobalVolume(tjs_int v);
	static tjs_int GetGlobalVolume() { return GlobalVolume; }
	static void SetGlobalFocusMode(tTVPSoundGlobalFocusMode b);
	static tTVPSoundGlobalFocusMode GetGlobalFocusMode()
		{ return GlobalFocusMode; }

private:
	void Set3DPositionToBuffer();
public:
	void SetPos(D3DVALUE x, D3DVALUE y, D3DVALUE z);
	void SetPosX(D3DVALUE v);
	D3DVALUE GetPosX() const {return PosX;}
	void SetPosY(D3DVALUE v);
	D3DVALUE GetPosY() const {return PosY;}
	void SetPosZ(D3DVALUE v);
	D3DVALUE GetPosZ() const {return PosZ;}

private:
	void SetFrequencyToBuffer();
public:
	tjs_int GetFrequency() const { return Frequency; }
	void SetFrequency(tjs_int freq);

	//-- visualization stuff ----------------------------------------------
public:
	void SetUseVisBuffer(bool b);
	bool GetUseVisBuffer() const { return UseVisBuffer; }

protected:
	void ResetVisBuffer(); // reset or recreate visualication buffer
	void DeallocateVisBuffer();

	void CopyVisBuffer(tjs_int16 *dest, const tjs_uint8 *src,
		tjs_int numsamples, tjs_int channels);
public:
	tjs_int GetVisBuffer(tjs_int16 *dest, tjs_int numsamples, tjs_int channels,
		tjs_int aheadsamples);
};
//---------------------------------------------------------------------------

#endif
