// Copyright (c) 2008, Niels Martin Hansen
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
//   * Redistributions of source code must retain the above copyright notice,
//     this list of conditions and the following disclaimer.
//   * Redistributions in binary form must reproduce the above copyright notice,
//     this list of conditions and the following disclaimer in the documentation
//     and/or other materials provided with the distribution.
//   * Neither the name of the Aegisub Group nor the names of its contributors
//     may be used to endorse or promote products derived from this software
//     without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
// Aegisub Project http://www.aegisub.org/
//
// $Id$

/// @file audio_player_dsound2.cpp
/// @brief New DirectSound-based audio output
/// @ingroup audio_output
///


///////////
// Headers
#include "config.h"

#ifdef WITH_DIRECTSOUND

#include <wx/wxprec.h>
#include <mmsystem.h>
#include <dsound.h>
#include <process.h>
#include "include/aegisub/audio_provider.h"
#include "utils.h"
#include "main.h"
#include "frame_main.h"
#include "options.h"
#include "audio_player_dsound2.h"



/// DOCME
struct COMInitialization {

	/// DOCME
	bool inited;


	/// @brief DOCME
	///
	COMInitialization()
	{
		inited = false;
	}


	/// @brief DOCME
	///
	~COMInitialization()
	{
		if (inited) CoUninitialize();
	}


	/// @brief DOCME
	///
	void Init()
	{
		if (!inited)
		{
			if (FAILED(CoInitialize(NULL)))
				throw std::exception();
			inited = true;
		}
	}
};


template<class T>

/// DOCME
struct COMObjectRetainer {

	/// DOCME
	T *obj;


	/// @brief DOCME
	///
	COMObjectRetainer()
	{
		obj = 0;
	}


	/// @brief DOCME
	/// @param _obj 
	///
	COMObjectRetainer(T *_obj)
	{
		obj = _obj;
	}


	/// @brief DOCME
	///
	~COMObjectRetainer()
	{
		if (obj) obj->Release();
	}


	/// @brief DOCME
	/// @return 
	///
	T * operator -> ()
	{
		return obj;
	}
};



/// DOCME
/// @class DirectSoundPlayer2Thread
/// @brief DOCME
///
/// DOCME
class DirectSoundPlayer2Thread {
	static unsigned int __stdcall ThreadProc(void *parameter);
	void Run();

	DWORD FillAndUnlockBuffers(void *buf1, DWORD buf1sz, void *buf2, DWORD buf2sz, int64_t &input_frame, IDirectSoundBuffer8 *bfr);

	void CheckError();


	/// DOCME
	HANDLE thread_handle;

	// Used to signal state-changes to thread
	HANDLE

		/// DOCME
		event_start_playback,

		/// DOCME
		event_stop_playback,

		/// DOCME
		event_update_end_time,

		/// DOCME
		event_set_volume,

		/// DOCME
		event_kill_self;

	// Thread communicating back
	HANDLE

		/// DOCME
		thread_running,

		/// DOCME
		is_playing,

		/// DOCME
		error_happened;


	/// DOCME
	wxChar *error_message;

	/// DOCME
	double volume;

	/// DOCME
	int64_t start_frame;

	/// DOCME
	int64_t end_frame;


	/// DOCME
	int wanted_latency;

	/// DOCME
	int buffer_length;


	/// DOCME
	DWORD last_playback_restart;


	/// DOCME
	AudioProvider *provider;

public:
	DirectSoundPlayer2Thread(AudioProvider *provider, int WantedLatency, int BufferLength);
	~DirectSoundPlayer2Thread();

	void Play(int64_t start, int64_t count);
	void Stop();
	void SetEndFrame(int64_t new_end_frame);
	void SetVolume(double new_volume);

	bool IsPlaying();
	int64_t GetStartFrame();
	int64_t GetCurrentFrame();
	int64_t GetEndFrame();
	double GetVolume();
	bool IsDead();
};



/// @brief DOCME
/// @param parameter 
/// @return 
///
unsigned int __stdcall DirectSoundPlayer2Thread::ThreadProc(void *parameter)
{
	static_cast<DirectSoundPlayer2Thread*>(parameter)->Run();
	return 0;
}



/// @brief DOCME
/// @return 
///
void DirectSoundPlayer2Thread::Run()
{

/// DOCME
#define REPORT_ERROR(msg) { error_message = _T("DirectSoundPlayer2Thread: ") _T(msg); SetEvent(error_happened); return; }

	COMInitialization COM_library;
	try	{ COM_library.Init(); }
	catch (std::exception e)
		REPORT_ERROR("Could not initialise COM")


	// Create DirectSound object
	COMObjectRetainer<IDirectSound8> ds;
	if (FAILED(DirectSoundCreate8(&DSDEVID_DefaultPlayback, &ds.obj, NULL)))
		REPORT_ERROR("Cound not create DirectSound object")


	// Ensure we can get interesting wave formats (unless we have PRIORITY we can only use a standard 8 bit format)
	ds->SetCooperativeLevel((HWND)static_cast<AegisubApp*>(wxApp::GetInstance())->frame->GetHandle(), DSSCL_PRIORITY);

	// Describe the wave format
	WAVEFORMATEX waveFormat;
	waveFormat.wFormatTag = WAVE_FORMAT_PCM;
	waveFormat.nSamplesPerSec = provider->GetSampleRate();
	waveFormat.nChannels = provider->GetChannels();
	waveFormat.wBitsPerSample = provider->GetBytesPerSample() * 8;
	waveFormat.nBlockAlign = waveFormat.nChannels * waveFormat.wBitsPerSample / 8;
	waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign;
	waveFormat.cbSize = sizeof(waveFormat);

	// And the buffer itself
	int aim = waveFormat.nAvgBytesPerSec * (wanted_latency*buffer_length)/1000;
	int min = DSBSIZE_MIN;
	int max = DSBSIZE_MAX;
	DWORD bufSize = MIN(MAX(min,aim),max); // size of entier playback buffer
	DSBUFFERDESC desc;
	desc.dwSize = sizeof(DSBUFFERDESC);
	desc.dwFlags = DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_GLOBALFOCUS;
	desc.dwBufferBytes = bufSize;
	desc.dwReserved = 0;
	desc.lpwfxFormat = &waveFormat;
	desc.guid3DAlgorithm = GUID_NULL;

	// And then create the buffer
	IDirectSoundBuffer *bfr7 = 0;
	if FAILED(ds->CreateSoundBuffer(&desc, &bfr7, 0))
		REPORT_ERROR("Could not create buffer")

	// But it's an old version interface we get, query it for the DSound8 interface
	COMObjectRetainer<IDirectSoundBuffer8> bfr;
	if (FAILED(bfr7->QueryInterface(IID_IDirectSoundBuffer8, (LPVOID*)&bfr.obj)))
		REPORT_ERROR("Buffer doesn't support version 8 interface")
	bfr7->Release();
	bfr7 = 0;

	//wxLogDebug(_T("DirectSoundPlayer2: Created buffer of %d bytes, supposed to be %d milliseconds or %d frames"), bufSize, WANTED_LATENCY*BUFFER_LENGTH, bufSize/provider->GetBytesPerSample());


	// Now we're ready to roll!
	SetEvent(thread_running);
	bool running = true;

	HANDLE events_to_wait[] = {
		event_start_playback,
		event_stop_playback,
		event_update_end_time,
		event_set_volume,
		event_kill_self
	};

	int64_t next_input_frame = 0;
	DWORD buffer_offset = 0;
	bool playback_should_be_running = false;
	int current_latency = wanted_latency;
	const DWORD wanted_latency_bytes = wanted_latency*waveFormat.nSamplesPerSec*provider->GetBytesPerSample()/1000;

	while (running)
	{
		DWORD wait_result = WaitForMultipleObjects(sizeof(events_to_wait)/sizeof(HANDLE), events_to_wait, FALSE, current_latency);

		switch (wait_result)
		{
		case WAIT_OBJECT_0+0:
			{
				// Start or restart playback
				bfr->Stop();
				ResetEvent(is_playing);

				next_input_frame = start_frame;

				DWORD buf_size; // size of buffer locked for filling
				void *buf;
				buffer_offset = 0;

				if (FAILED(bfr->SetCurrentPosition(0)))
					REPORT_ERROR("Could not reset playback buffer cursor before filling first buffer.")

				HRESULT res = bfr->Lock(buffer_offset, 0, &buf, &buf_size, 0, 0, DSBLOCK_ENTIREBUFFER);
				while (FAILED(res)) // yes, while, so I can break out of it without a goto!
				{
					if (res == DSERR_BUFFERLOST)
					{
						// Try to regain the buffer
						if (SUCCEEDED(bfr->Restore()) &&
							SUCCEEDED(bfr->Lock(buffer_offset, 0, &buf, &buf_size, 0, 0, DSBLOCK_ENTIREBUFFER)))
						{
							//wxLogDebug(_T("DirectSoundPlayer2: Lost and restored buffer"));
							break;
						}

						REPORT_ERROR("Lost buffer and could not restore it.")
					}

					REPORT_ERROR("Could not lock buffer for playback.")
				}

				// Clear the buffer in case we can't fill it completely
				memset(buf, 0, buf_size);

				DWORD bytes_filled = FillAndUnlockBuffers(buf, buf_size, 0, 0, next_input_frame, bfr.obj);
				buffer_offset += bytes_filled;
				if (buffer_offset >= bufSize) buffer_offset -= bufSize;

				if (FAILED(bfr->SetCurrentPosition(0)))
					REPORT_ERROR("Could not reset playback buffer cursor before playback.")

				if (bytes_filled < wanted_latency_bytes)
				{
					// Very short playback length, do without streaming playback
					current_latency = (bytes_filled*1000) / (waveFormat.nSamplesPerSec*provider->GetBytesPerSample());
					if (FAILED(bfr->Play(0, 0, 0)))
						REPORT_ERROR("Could not start single-buffer playback.")
				}
				else
				{
					// We filled the entire buffer so there's reason to do streaming playback
					current_latency = wanted_latency;
					if (FAILED(bfr->Play(0, 0, DSBPLAY_LOOPING)))
						REPORT_ERROR("Could not start looping playback.")
				}

				SetEvent(is_playing);
				playback_should_be_running = true;

				break;
			}

		case WAIT_OBJECT_0+1:
			{
				// Stop playing
				bfr->Stop();
				ResetEvent(is_playing);
				playback_should_be_running = false;
				break;
			}

		case WAIT_OBJECT_0+2:
			{
				// Set end frame
				if (end_frame <= next_input_frame)
				{
					bfr->Stop();
					ResetEvent(is_playing);
					playback_should_be_running = false;
				}
				break;
			}

		case WAIT_OBJECT_0+3:
			{
				// Change volume
				// We aren't thread safe right now, filling the buffers grabs volume directly
				// from the field set by the controlling thread, but it shouldn't be a major
				// problem if race conditions do occur, just some momentary distortion.
				break;
			}

		case WAIT_OBJECT_0+4:
			{
				// Perform suicide
				bfr->Stop();
				ResetEvent(is_playing);
				playback_should_be_running = false;
				running = false;
				break;
			}

		case WAIT_TIMEOUT:
			{
				// Time to fill more into buffer

				if (!playback_should_be_running)
					break;

				DWORD status;
				if (FAILED(bfr->GetStatus(&status)))
					REPORT_ERROR("Could not get playback buffer status")

				if (!(status & DSBSTATUS_LOOPING))
				{
					// Not looping playback...
					// hopefully we only triggered timeout after being done with the buffer
					bfr->Stop();
					ResetEvent(is_playing);
					playback_should_be_running = false;
					break;
				}

				DWORD play_cursor;
				if (FAILED(bfr->GetCurrentPosition(&play_cursor, 0)))
					REPORT_ERROR("Could not get play cursor position for filling buffer.")

				int bytes_needed = (int)play_cursor - (int)buffer_offset;
				if (bytes_needed < 0) bytes_needed += (int)bufSize;

				DWORD buf1sz, buf2sz;
				void *buf1, *buf2;

				HRESULT res = bfr->Lock(buffer_offset, bytes_needed, &buf1, &buf1sz, &buf2, &buf2sz, 0);
				while (FAILED(res)) // yes, while, so I can break out of it without a goto!
				{
					if (res == DSERR_BUFFERLOST)
					{
						// Try to regain the buffer
						// When the buffer was lost the entire contents was lost too, so we have to start over
						if (SUCCEEDED(bfr->Restore()) &&
						    SUCCEEDED(bfr->Lock(0, bufSize, &buf1, &buf1sz, &buf2, &buf2sz, 0)) &&
						    SUCCEEDED(bfr->Play(0, 0, DSBPLAY_LOOPING)))
						{
							wxLogDebug(_T("DirectSoundPlayer2: Lost and restored buffer"));
							break;
						}

						REPORT_ERROR("Lost buffer and could not restore it.")
					}

					REPORT_ERROR("Could not lock buffer for filling.")
				}

				DWORD bytes_filled = FillAndUnlockBuffers(buf1, buf1sz, buf2, buf2sz, next_input_frame, bfr.obj);
				buffer_offset += bytes_filled;
				if (buffer_offset >= bufSize) buffer_offset -= bufSize;

				if (bytes_filled < 1024)
				{
					// Arbitrary low number, we filled in very little so better get back to filling in the rest with silence
					// really fast... set latency to zero in this case.
					current_latency = 0;
				}
				else if (bytes_filled < wanted_latency_bytes)
				{
					// Didn't fill as much as we wanted to, let's get back to filling sooner than normal
					current_latency = (bytes_filled*1000) / (waveFormat.nSamplesPerSec*provider->GetBytesPerSample());
				}
				else
				{
					// Plenty filled in, do regular latency
					current_latency = wanted_latency;
				}

				break;
			}

		default:
			REPORT_ERROR("Something bad happened while waiting on events in playback loop, either the wait failed or an event object was abandoned.")
			break;
		}
	}


/// DOCME
#undef REPORT_ERROR
}



/// @brief DOCME
/// @param buf1        
/// @param buf1sz      
/// @param buf2        
/// @param buf2sz      
/// @param input_frame 
/// @param bfr         
/// @return 
///
DWORD DirectSoundPlayer2Thread::FillAndUnlockBuffers(void *buf1, DWORD buf1sz, void *buf2, DWORD buf2sz, int64_t &input_frame, IDirectSoundBuffer8 *bfr)
{
	// Assume buffers have been locked and are ready to be filled

	DWORD bytes_per_frame = provider->GetChannels() * provider->GetBytesPerSample();
	DWORD buf1szf = buf1sz / bytes_per_frame;
	DWORD buf2szf = buf2sz / bytes_per_frame;

	if (input_frame >= end_frame)
	{
		// Silence

		if (buf1)
			memset(buf1, 0, buf1sz);

		if (buf2)
			memset(buf2, 0, buf2sz);

		input_frame += buf1szf + buf2szf;

		bfr->Unlock(buf1, buf1sz, buf2, buf2sz); // should be checking for success

		return buf1sz + buf2sz;
	}

	if (buf1 && buf1sz)
	{
		if (buf1szf + input_frame > end_frame)
		{
			buf1szf = end_frame - input_frame;
			buf1sz = buf1szf * bytes_per_frame;
			buf2szf = 0;
			buf2sz = 0;
		}

		provider->GetAudioWithVolume(buf1, input_frame, buf1szf, volume);

		input_frame += buf1szf;
	}

	if (buf2 && buf2sz)
	{
		if (buf2szf + input_frame > end_frame)
		{
			buf2szf = end_frame - input_frame;
			buf2sz = buf2szf * bytes_per_frame;
		}

		provider->GetAudioWithVolume(buf2, input_frame, buf2szf, volume);

		input_frame += buf2szf;
	}

	bfr->Unlock(buf1, buf1sz, buf2, buf2sz); // bad? should check for success

	return buf1sz + buf2sz;
}



/// @brief DOCME
/// @return 
///
void DirectSoundPlayer2Thread::CheckError()
{
	try
	{
		switch (WaitForSingleObject(error_happened, 0))
		{
		case WAIT_OBJECT_0:
			throw error_message;

		case WAIT_ABANDONED:
			throw _T("The DirectShowPlayer2Thread error signal event was abandoned, somehow. This should not happen.");

		case WAIT_FAILED:
			throw _T("Failed checking state of DirectShowPlayer2Thread error signal event.");

		case WAIT_TIMEOUT:
		default:
			return;
		}
	}
	catch (...)
	{
		ResetEvent(is_playing);
		ResetEvent(thread_running);
		throw;
	}
}



/// @brief DOCME
/// @param provider       
/// @param _WantedLatency 
/// @param _BufferLength  
///
DirectSoundPlayer2Thread::DirectSoundPlayer2Thread(AudioProvider *provider, int _WantedLatency, int _BufferLength)
{
	event_start_playback  = CreateEvent(0, FALSE, FALSE, 0);
	event_stop_playback   = CreateEvent(0, FALSE, FALSE, 0);
	event_update_end_time = CreateEvent(0, FALSE, FALSE, 0);
	event_set_volume      = CreateEvent(0, FALSE, FALSE, 0);
	event_kill_self       = CreateEvent(0, FALSE, FALSE, 0);

	thread_running        = CreateEvent(0,  TRUE, FALSE, 0);
	is_playing            = CreateEvent(0,  TRUE, FALSE, 0);
	error_happened        = CreateEvent(0, FALSE, FALSE, 0);

	error_message = 0;
	volume = 1.0;
	start_frame = 0;
	end_frame = 0;

	wanted_latency	= _WantedLatency;
	buffer_length	= _BufferLength;

	this->provider = provider;

	thread_handle = (HANDLE)_beginthreadex(0, 0, ThreadProc, this, 0, 0);

	if (!thread_handle)
		throw _T("Failed creating playback thread in DirectSoundPlayer2. This is bad.");

	CheckError();

	WaitForSingleObject(thread_running, INFINITE);
}



/// @brief DOCME
///
DirectSoundPlayer2Thread::~DirectSoundPlayer2Thread()
{
	SetEvent(event_kill_self);
	WaitForSingleObject(thread_handle, INFINITE);
}



/// @brief DOCME
/// @param start 
/// @param count 
///
void DirectSoundPlayer2Thread::Play(int64_t start, int64_t count)
{
	CheckError();

	start_frame = start;
	end_frame = start+count;
	SetEvent(event_start_playback);

	last_playback_restart = GetTickCount();
}



/// @brief DOCME
///
void DirectSoundPlayer2Thread::Stop()
{
	CheckError();

	SetEvent(event_stop_playback);
}



/// @brief DOCME
/// @param new_end_frame 
///
void DirectSoundPlayer2Thread::SetEndFrame(int64_t new_end_frame)
{
	CheckError();

	end_frame = new_end_frame;
	SetEvent(event_update_end_time);
}



/// @brief DOCME
/// @param new_volume 
///
void DirectSoundPlayer2Thread::SetVolume(double new_volume)
{
	CheckError();

	volume = new_volume;
	SetEvent(event_set_volume);
}



/// @brief DOCME
/// @return 
///
bool DirectSoundPlayer2Thread::IsPlaying()
{
	CheckError();

	switch (WaitForSingleObject(is_playing, 0))
	{
	case WAIT_ABANDONED:
		throw _T("The DirectShowPlayer2Thread playback state event was abandoned, somehow. This should not happen.");

	case WAIT_FAILED:
		throw _T("Failed checking state of DirectShowPlayer2Thread playback state event.");

	case WAIT_OBJECT_0:
		return true;

	case WAIT_TIMEOUT:
	default:
		return false;
	}
}



/// @brief DOCME
/// @return 
///
int64_t DirectSoundPlayer2Thread::GetStartFrame()
{
	CheckError();

	return start_frame;
}



/// @brief DOCME
/// @return 
///
int64_t DirectSoundPlayer2Thread::GetCurrentFrame()
{
	CheckError();

	if (!IsPlaying()) return 0;

	DWORD milliseconds_elapsed = GetTickCount() - last_playback_restart;

	return start_frame + milliseconds_elapsed * provider->GetSampleRate() / 1000;
}



/// @brief DOCME
/// @return 
///
int64_t DirectSoundPlayer2Thread::GetEndFrame()
{
	CheckError();

	return end_frame;
}



/// @brief DOCME
/// @return 
///
double DirectSoundPlayer2Thread::GetVolume()
{
	CheckError();

	return volume;
}



/// @brief DOCME
/// @return 
///
bool DirectSoundPlayer2Thread::IsDead()
{
	switch (WaitForSingleObject(thread_running, 0))
	{
	case WAIT_OBJECT_0:
		return false;

	default:
		return true;
	}
}





/// @brief DOCME
///
DirectSoundPlayer2::DirectSoundPlayer2()
{
	thread = 0;

	// The buffer will hold BufferLength times WantedLatency milliseconds of audio
	WantedLatency = Options.AsInt(_T("Audio dsound buffer latency"));
	BufferLength = Options.AsInt(_T("Audio dsound buffer length"));

	// sanity checking
	if (WantedLatency <= 0)
		WantedLatency = 100;
	if (BufferLength <= 0)
		BufferLength = 5;
}



/// @brief DOCME
///
DirectSoundPlayer2::~DirectSoundPlayer2()
{
	CloseStream();
}



/// @brief DOCME
/// @return 
///
bool DirectSoundPlayer2::IsThreadAlive()
{
	if (!thread) return false;
	
	if (thread->IsDead())
	{
		delete thread;
		thread = 0;
		return false;
	}

	return true;
}



/// @brief DOCME
/// @return 
///
void DirectSoundPlayer2::OpenStream()
{
	if (IsThreadAlive()) return;

	try
	{
		thread = new DirectSoundPlayer2Thread(GetProvider(), WantedLatency, BufferLength);
	}
	catch (const wxChar *msg)
	{
		wxLogError(msg);
		thread = 0;
	}
}



/// @brief DOCME
/// @return 
///
void DirectSoundPlayer2::CloseStream()
{
	if (!IsThreadAlive()) return;

	try
	{
		delete thread;
	}
	catch (const wxChar *msg)
	{
		wxLogError(msg);
	}
	thread = 0;
}



/// @brief DOCME
/// @param provider 
///
void DirectSoundPlayer2::SetProvider(AudioProvider *provider)
{
	try
	{
		if (IsThreadAlive() && provider != GetProvider())
		{
			delete thread;
			thread = new DirectSoundPlayer2Thread(provider, WantedLatency, BufferLength);
		}

		AudioPlayer::SetProvider(provider);
	}
	catch (const wxChar *msg)
	{
		wxLogError(msg);
	}
}



/// @brief DOCME
/// @param start 
/// @param count 
///
void DirectSoundPlayer2::Play(int64_t start,int64_t count)
{
	try
	{
		OpenStream();
		thread->Play(start, count);

		if (displayTimer && !displayTimer->IsRunning()) displayTimer->Start(15);
	}
	catch (const wxChar *msg)
	{
		wxLogError(msg);
	}
}



/// @brief DOCME
/// @param timerToo 
///
void DirectSoundPlayer2::Stop(bool timerToo)
{
	try
	{
		if (IsThreadAlive()) thread->Stop();

		if (timerToo && displayTimer) {
			displayTimer->Stop();
		}
	}
	catch (const wxChar *msg)
	{
		wxLogError(msg);
	}
}



/// @brief DOCME
/// @return 
///
bool DirectSoundPlayer2::IsPlaying()
{
	try
	{
		if (!IsThreadAlive()) return false;
		return thread->IsPlaying();
	}
	catch (const wxChar *msg)
	{
		wxLogError(msg);
		return false;
	}
}



/// @brief DOCME
/// @return 
///
int64_t DirectSoundPlayer2::GetStartPosition()
{
	try
	{
		if (!IsThreadAlive()) return 0;
		return thread->GetStartFrame();
	}
	catch (const wxChar *msg)
	{
		wxLogError(msg);
		return 0;
	}
}



/// @brief DOCME
/// @return 
///
int64_t DirectSoundPlayer2::GetEndPosition()
{
	try
	{
		if (!IsThreadAlive()) return 0;
		return thread->GetEndFrame();
	}
	catch (const wxChar *msg)
	{
		wxLogError(msg);
		return 0;
	}
}



/// @brief DOCME
/// @return 
///
int64_t DirectSoundPlayer2::GetCurrentPosition()
{
	try
	{
		if (!IsThreadAlive()) return 0;
		return thread->GetCurrentFrame();
	}
	catch (const wxChar *msg)
	{
		wxLogError(msg);
		return 0;
	}
}



/// @brief DOCME
/// @param pos 
///
void DirectSoundPlayer2::SetEndPosition(int64_t pos)
{
	try
	{
		if (IsThreadAlive()) thread->SetEndFrame(pos);
	}
	catch (const wxChar *msg)
	{
		wxLogError(msg);
	}
}



/// @brief DOCME
/// @param pos 
///
void DirectSoundPlayer2::SetCurrentPosition(int64_t pos)
{
	try
	{
		if (IsThreadAlive()) thread->Play(pos, thread->GetEndFrame()-pos);
	}
	catch (const wxChar *msg)
	{
		wxLogError(msg);
	}
}



/// @brief DOCME
/// @param vol 
///
void DirectSoundPlayer2::SetVolume(double vol)
{
	try
	{
		if (IsThreadAlive()) thread->SetVolume(vol);
	}
	catch (const wxChar *msg)
	{
		wxLogError(msg);
	}
}



/// @brief DOCME
///
double DirectSoundPlayer2::GetVolume()
{
	try
	{
		if (!IsThreadAlive()) return 0;
		return thread->GetVolume();
	}
	catch (const wxChar *msg)
	{
		wxLogError(msg);
		return 0;
	}
}


#endif // WITH_DIRECTSOUND


