///////////////////////////////////////////////////////////////////////////////
//
// File      : Asura_PS3_MultiStream_Streaming.cpp
// Author    : Philip Hall
// Created   : 17/09/2007
//
// Copyright (C) 2007 Rebellion. All Rights Reserved
//
///////////////////////////////////////////////////////////////////////////////

#include "Core/Asura.h"

#ifndef ASURA_USE_AUDIO_SYSTEM

// Asura headers
#include "Core/Threads/Asura_ProducerConsumerQueue.h"
#include "Debugging/Asura_LogFile.h"
#include "FileAccess/Asura_FileAccess.h"
#include "Sound/Asura_Sound.h"

// Asura PS3 headers
#include "Sound/Asura_PS3_Sound_System.h"
#include "Sound/Asura_PS3_Sound_Loader.h"

// This file's header
#include "Sound/Asura_PS3_MultiStream_Streaming.h"

#if defined(ASURA_PS3_USE_MULTISTREAM) && (ASURA_PS3_USE_MULTISTREAM != 0)

// SDK headers
#include <errno.h>
#include <sys/timer.h>
#include <sys/ppu_thread.h>
#include <sysutil/sysutil_sysparam.h>
#include <cell/audio.h>
#include <cell/mstream.h>
#include <cell/sysmodule.h>

// D E B U G V A R S /////////////////////////////////////////////////////////
DEBUGVAR float dbg_fPS3StreamingSoundVolScale = 1.0f;
DEBUGVAR bool dbg_bPS3StreamingForceVolumeUpdate = false;

// S T A T I C   C L A S S   D A T A /////////////////////////////////////////
static const int ASURA_PS3_SOUND_CHANNELS_PER_STREAM = 2; // using stereo music - would be 6 for 5.1 music

// Repeated error messages
static const char szINVALID_STREAMING_SOUND_INDEX[] = "Invalid streaming sound index (out of range)";


static const u_int uNUM_STREAM_LOADERS = 4;
static Asura_PS3_MSStreamLoader g_axStreamLoaders[uNUM_STREAM_LOADERS];

//queue for asynchronous initial loading of streams
//this runs in just 1 thread at the moment, and code depends on that for a strict order of load/deinit calls.
Asura_ProducerConsumerQueue Asura_PS3_MultiStream_Streaming::s_xLoadingThreadQueue( 8, 1 );


////PGH////////////////////////////////////////////////////////////////////////
// Function    : Asura_PS3_MultiStream_Streaming::Asura_PS3_MultiStream_Streaming
// Description : Construct the streaming sound system - just sets default values
//
// Parameters  : none
//
// Returns     : nothing
///////////////////////////////////////////////////////////////////////////////
Asura_PS3_MultiStream_Streaming::Asura_PS3_MultiStream_Streaming()
{
	m_pWorkingMemory = NULL;
}


////PGH////////////////////////////////////////////////////////////////////////
// Function    : Asura_PS3_MultiStream_Streaming::Initialise
// Description : Initialise the streaming sound system   (using ATRAC)
//
// Parameters  : none
//
// Returns     : nothing
///////////////////////////////////////////////////////////////////////////////
bool Asura_PS3_MultiStream_Streaming::Initialise()
{

#if defined ASURA_PS3_SOUND_USE_ATRAC && (ASURA_PS3_SOUND_USE_ATRAC != 0)
	// Get size and allocate working-memory for the MultiStream ATRAC system
	int iSize = cellMSATRACGetNeededMemorySize( ASURA_PS3_SOUND_CHANNELS_PER_STREAM * GetMaxStreamingSounds() );
	m_pWorkingMemory = Asura_AllocateAlign( iSize, ASURA_ALIGN_16_BYTE );
	if ( m_pWorkingMemory == NULL )
	{
		Asura_Assert(false, "Unable to allocate ATRAC playback memory" );
		return false;
	}

	// Initialise ATRAC
	int iErr = cellMSATRACInit( ASURA_PS3_SOUND_CHANNELS_PER_STREAM * GetMaxStreamingSounds(), m_pWorkingMemory );
	if ( iErr < 0 )
	{
		Asura_Assert(false, "Unable to initialise ATRAC system");
		return false;
	}
#endif

#if defined ASURA_PS3_SOUND_USE_MP3 && (ASURA_PS3_SOUND_USE_MP3 != 0)
	// Get size and allocate working-memory for the MultiStream MP3 system
	u_int uMaxChannels = ASURA_PS3_SOUND_CHANNELS_PER_STREAM * GetMaxStreamingSounds() + iASURA_PS3_SOUND_MAX_VOICE_CHANNELS;
	int iSize = cellMSMP3GetNeededMemorySize( uMaxChannels );
	Asura_OutputToDebugger(("[Asura_PS3_MultiStream_Streaming::Initialise] - MultiStream using additional %d bytes of system memory for MP3 buffers\n", iSize));
	m_pWorkingMemory = Asura_AllocateAlign( iSize, ASURA_ALIGN_16_BYTE );
	if ( m_pWorkingMemory == NULL )
	{
		Asura_Assert(false, "Unable to allocate MP3 playback memory" );
		return false;
	}

	// Initialise MP3 playback
	int iErr = cellMSMP3Init( uMaxChannels, m_pWorkingMemory );
	if ( iErr < 0 )
	{
		Asura_Assert(false, "Unable to initialise MP3 system");
		return false;
	}
#endif

	return true;
}


////PGH////////////////////////////////////////////////////////////////////////
// Function    : Asura_PS3_MultiStream_Streaming::Shutdown
// Description : Shutdown ATRAC processing and free the memory buffers
//
// Parameters  : none
//
// Returns     : nothing
///////////////////////////////////////////////////////////////////////////////
void Asura_PS3_MultiStream_Streaming::Shutdown()
{
	// Destroy all the streams
	for ( int iLoop=0; iLoop < GetMaxStreamingSounds(); iLoop++)
	{
		if ( m_axStreamingSounds[iLoop].IsValid() )
		{
			m_axStreamingSounds[iLoop].Destroy();
		}
	}

#if defined ASURA_PS3_SOUND_USE_ATRAC && (ASURA_PS3_SOUND_USE_ATRAC != 0)
	cellMSATRACClose();
#endif

	if ( m_pWorkingMemory != NULL )
	{
		Asura_DeallocateAlign(m_pWorkingMemory);
		m_pWorkingMemory = NULL;
	}
}


////PGH////////////////////////////////////////////////////////////////////////
// Function    : Asura_PS3_MultiStream_Streaming::Reset
// Description : Reset the streaming system, destroys sounds and stops playback.
//
// Parameters  : none
//
// Returns     : nothing
///////////////////////////////////////////////////////////////////////////////
void Asura_PS3_MultiStream_Streaming::Reset()
{
	// Destroy all the streams
	for ( int iLoop=0; iLoop < GetMaxStreamingSounds(); iLoop++)
	{
		if ( m_axStreamingSounds[iLoop].IsValid() )
		{
			m_axStreamingSounds[iLoop].Destroy();
		}
	}
}


////PGH////////////////////////////////////////////////////////////////////////
// Function    : Asura_PS3_MultiStream_Streaming::Update
// Description : Update all the valid streams
//
// Parameters  : none
//
// Returns     : nothing
///////////////////////////////////////////////////////////////////////////////
void Asura_PS3_MultiStream_Streaming::Update()
{
	for ( int iLoop=0; iLoop < GetMaxStreamingSounds(); iLoop++)
	{
		if ( m_axStreamingSounds[iLoop].IsValid() )
		{
			m_axStreamingSounds[iLoop].Update();
		}
	}
}



////PGH////////////////////////////////////////////////////////////////////////
// Function    : Asura_PS3_MultiStream_Streaming::Create
// Description : Create/initialise a streamed sound
//
// Parameters  :
//   int iStreamID           - Id of stream to initialise
//   const char* szFilename  - Name of source sound file
//
// Returns     : bool - Returns true if successfully created
///////////////////////////////////////////////////////////////////////////////
bool Asura_PS3_MultiStream_Streaming::Create(int iStreamID, const char* szFilename)
{
	if ( (iStreamID < 0) || iStreamID >= GetMaxStreamingSounds() )
	{
		Asura_Assert(false, szINVALID_STREAMING_SOUND_INDEX);
		return false;
	}
	
	// Twiddle the file extension to 'mp3' - Tools stores music filenames as '.wav'
	char szTempFilename[ASURA_PS3_MAX_PATH];
	strncpy(szTempFilename, szFilename, ASURA_PS3_MAX_PATH);
	char *szExt = strrchr(szTempFilename, '.');
	if ( szExt == NULL ) { szExt = szTempFilename + strlen(szTempFilename); }
	strncpy(szExt, ".mp3", 4);

	// Create the streaming sound with the new file name
	return m_axStreamingSounds[iStreamID].Create( szTempFilename);
}



////PGH////////////////////////////////////////////////////////////////////////
// Function    : Asura_PS3_MultiStream_Streaming::Play
// Description : Play the given stream
//
// Parameters  :
//   int iStreamID  - Stream ID
//   bool bLooping  - Set true to loop playback
//   bool bPreload  - Set true to preload this sound stream
//
// Returns     : nothing
///////////////////////////////////////////////////////////////////////////////
void Asura_PS3_MultiStream_Streaming::Play( int iStreamID, bool bLooping, bool bPreload )
{
	if ( (iStreamID < 0) || iStreamID >= GetMaxStreamingSounds() )
	{
		Asura_Assert(false, szINVALID_STREAMING_SOUND_INDEX);
		return;
	}

    m_axStreamingSounds[iStreamID].Play( bLooping, bPreload );
}


////PGH////////////////////////////////////////////////////////////////////////
// Function    : Asura_PS3_MultiStream_Streaming::Stop
// Description : Stop stream playback
//
// Parameters  :
//   int iStreamID - Stream ID
//
// Returns     : nothing
///////////////////////////////////////////////////////////////////////////////
void Asura_PS3_MultiStream_Streaming::Stop( int iStreamID )
{
	if ( (iStreamID < 0) || iStreamID >= GetMaxStreamingSounds() )
	{
		Asura_Assert(false, szINVALID_STREAMING_SOUND_INDEX);
		return;
	}

	m_axStreamingSounds[iStreamID].Stop();
}


////PGH////////////////////////////////////////////////////////////////////////
// Function    : Asura_PS3_MultiStream_Streaming::Release
// Description : Release the given Stream ID
//
// Parameters  :
//   int iStreamID - Stream ID
//
// Returns     : nothing
///////////////////////////////////////////////////////////////////////////////
void Asura_PS3_MultiStream_Streaming::Release(int iStreamID)
{
	if ( (iStreamID < 0) || iStreamID >= GetMaxStreamingSounds() )
	{
		Asura_Assert(false, szINVALID_STREAMING_SOUND_INDEX);
		return;
	}

	m_axStreamingSounds[iStreamID].Destroy();
}



////PGH////////////////////////////////////////////////////////////////////////
// Function    : Asura_PS3_MultiStream_Streaming::Pause
// Description : Pause the given streaming sound
//
// Parameters  :
//   int iStreamID - ID of stream to pause
//
// Returns     : nothing
///////////////////////////////////////////////////////////////////////////////
void Asura_PS3_MultiStream_Streaming::Pause( int iStreamID )
{
	if ( (iStreamID < 0) || iStreamID >= GetMaxStreamingSounds() )
	{
		Asura_Assert(false, szINVALID_STREAMING_SOUND_INDEX);
		return;
	}

	m_axStreamingSounds[iStreamID].Pause();
}


////PGH////////////////////////////////////////////////////////////////////////
// Function    : Asura_PS3_MultiStream_Streaming::Resume
// Description : Resume the given streaming sound
//
// Parameters  :
//   int iStreamID - ID of stream to resume
//
// Returns     : nothing
///////////////////////////////////////////////////////////////////////////////
void Asura_PS3_MultiStream_Streaming::Resume( int iStreamID )
{
	if ( (iStreamID < 0) || iStreamID >= GetMaxStreamingSounds() )
	{
		Asura_Assert(false, szINVALID_STREAMING_SOUND_INDEX);
		return;
	}

	m_axStreamingSounds[iStreamID].Resume();
}


////PGH////////////////////////////////////////////////////////////////////////
// Function    : Asura_PS3_MultiStream_Streaming::SetVolume
// Description : Set the given stream volume
//
// Parameters  :
//   int iStreamID  - ID of stream to change volume for
//   float fVol     - New stream volume
//
// Returns     : nothing
///////////////////////////////////////////////////////////////////////////////
void Asura_PS3_MultiStream_Streaming::SetVolume( int iStreamID, float fVol )
{
	if ( (iStreamID < 0) || iStreamID >= GetMaxStreamingSounds() )
	{
		Asura_Assert(false, szINVALID_STREAMING_SOUND_INDEX);
		return;
	}

	m_axStreamingSounds[iStreamID].SetVolume( fVol );
}


////PGH////////////////////////////////////////////////////////////////////////
// Function    : Asura_PS3_MultiStream_Streaming::IsFinished
// Description : Return the playback state of the given stream
//
// Parameters  :
//   int iStreamID - ID of stream to query
//
// Returns     : bool - Returns true if stream has finished playing
///////////////////////////////////////////////////////////////////////////////
bool Asura_PS3_MultiStream_Streaming::IsFinished( int iStreamID )
{
	if ( (iStreamID < 0) || iStreamID >= GetMaxStreamingSounds() )
	{
		Asura_Assert(false, szINVALID_STREAMING_SOUND_INDEX);
		return true; // It's finished because it's not valid...
	}

	return m_axStreamingSounds[iStreamID].IsFinished();
}


////PGH////////////////////////////////////////////////////////////////////////
// Function    : Asura_PS3_MultiStream_Streaming::GetPosition
// Description : Get position in time in the given stream
//
// Parameters  :
//   int iStreamID - ID of stream to query
//
// Returns     : float - Position in time
///////////////////////////////////////////////////////////////////////////////
float Asura_PS3_MultiStream_Streaming::GetPosition( int iStreamID )
{
	if ( (iStreamID < 0) || iStreamID >= GetMaxStreamingSounds() )
	{
		Asura_Assert(false, szINVALID_STREAMING_SOUND_INDEX);
		return 0.0f;
	}

	return m_axStreamingSounds[iStreamID].GetPosition();
}


////PGH////////////////////////////////////////////////////////////////////////
// Function    : Asura_PS3_MultiStream_Streaming::GetLength
// Description : Get the time duration of the given stream
//
// Parameters  :
//   int iStreamID - ID of stream to query
//
// Returns     : float - Stream time duration
///////////////////////////////////////////////////////////////////////////////
float Asura_PS3_MultiStream_Streaming::GetLength( int iStreamID )
{
	if ( (iStreamID < 0) || iStreamID >= GetMaxStreamingSounds() )
	{
		Asura_Assert(false, szINVALID_STREAMING_SOUND_INDEX);
		return 0.0f;
	}

	return m_axStreamingSounds[iStreamID].GetLength();
}


////PGH////////////////////////////////////////////////////////////////////////
// Function    : Asura_PS3_MultiStream_Streaming::SetPosition
// Description : Set playback position in a stream
//
// Parameters  :
//   int iStreamID  - Stream ID
//   float fTime    - Time in stream
//
// Returns     : nothing
///////////////////////////////////////////////////////////////////////////////
void Asura_PS3_MultiStream_Streaming::SetPosition( int iStreamID, float fTime )
{
	if ( (iStreamID < 0) || iStreamID >= GetMaxStreamingSounds() )
	{
		Asura_Assert(false, szINVALID_STREAMING_SOUND_INDEX);
		return;
	}

	m_axStreamingSounds[iStreamID].SetPosition(fTime);
}


//////////////////////////////////////////////////////////////////////////
// Asura_PS3_MultiStream_Streaming::AddToLoadQueue()
// 
// Static function to keep a single queue for all MS data load requests
//////////////////////////////////////////////////////////////////////////
void Asura_PS3_MultiStream_Streaming::AddToLoadQueue( Asura_ProduceConsumerFunction pfTaskFunction, void* pData, u_int uSizeOfData )
{
	s_xLoadingThreadQueue.AddToQueue( pfTaskFunction, pData, uSizeOfData );
}

//////////////////////////////////////////////////////////////////////////
// Asura_PS3_MultiStream_Streaming::QueuedFileClose
//
// Static function.
// adds file handle to queue of files to close and sets reference to invalid handle.
//////////////////////////////////////////////////////////////////////////
void Asura_PS3_MultiStream_Streaming::QueuedFileClose( Asura_PS3_FileSystem::Handle& iHandle )
{
	s_xLoadingThreadQueue.AddToQueue( CloseFileTask, reinterpret_cast<void*>(iHandle), sizeof(int) );
	iHandle = Asura_PS3_FileSystem::INVALID_HANDLE;
}

//////////////////////////////////////////////////////////////////////////
// Asura_PS3_MultiStream_Streaming::QueuedFileClose
//
// Static function.
// Runs in loader thread.
//////////////////////////////////////////////////////////////////////////
void Asura_PS3_MultiStream_Streaming::CloseFileTask( void* pData, u_int uSize )
{
	Asura_PS3_FileSystem::Handle iHandle = reinterpret_cast<Asura_PS3_FileSystem::Handle>(pData);
	Asura_PS3_FileSystem::Close(&iHandle);
}

bool Asura_PS3_MultiStream_Streaming::IsSoundReady( int iStreamID )
{
	if ( (iStreamID < 0) || iStreamID >= GetMaxStreamingSounds() )
	{
		Asura_Assert(false, szINVALID_STREAMING_SOUND_INDEX);
		return true;
	}

	ASURA_MSSTREAM_LOADING_STATUS eStatus = m_axStreamingSounds[iStreamID].GetLoadStatus();
	if( eStatus == ASURA_MSSTREAM_LOADING_STATUS_PRELOAD || eStatus == ASURA_MSSTREAM_LOADING_STATUS_LOADING )
	{
		return false;
	}
	return true;
}


////PGH////////////////////////////////////////////////////////////////////////
// Function    : Asura_PS3_MSStreamingSound::Asura_PS3_MSStreamingSound
// Description : Construct a multi-streaming sound - inits default values
//
// Parameters  : none
//
// Returns     : nothing
///////////////////////////////////////////////////////////////////////////////
Asura_PS3_MSStreamingSound::Asura_PS3_MSStreamingSound()
{
	m_iMSStreamHandle = -1;
	m_iMSSubBus = CELL_MS_MASTER_BUS;
	m_fVolume = 1.0f;
	m_iLoopStart  = -1;
	m_xFileHandle = Asura_PS3_FileSystem::INVALID_HANDLE;

	// Allocate streaming buffers
	//BW the asynchronous _MSStreamLoader code relies on these existing, check it if you move the alloc/dealloc
	m_pStreamBuffers[0] = Asura_AllocateAlign(ASURA_PS3_MUSIC_STREAM_BUFFER_SIZE, ASURA_ALIGN_16_BYTE);
	m_pStreamBuffers[1] = Asura_AllocateAlign(ASURA_PS3_MUSIC_STREAM_BUFFER_SIZE, ASURA_ALIGN_16_BYTE);
	m_uiBufferStarts[0] = m_uiBufferStarts[1] = 0;
	m_uiBufferSizeRead[0] = m_uiBufferSizeRead[1] = 0;
	
	m_pxLoader = NULL;
}


Asura_PS3_MSStreamingSound::~Asura_PS3_MSStreamingSound()
{
	//BW the asynchronous _MSStreamLoader code relies on these existing, check it if you move the alloc/dealloc
	Asura_DeallocateAlign( m_pStreamBuffers[0] );
	Asura_DeallocateAlign( m_pStreamBuffers[1] );
}


void Asura_PS3_MSStreamingSound::Update()
{
#ifdef _DEBUG
	if (dbg_bPS3StreamingForceVolumeUpdate)
	{
		dbg_bPS3StreamingForceVolumeUpdate = false;
		UpdateVolume();
	}
#endif

	if ( m_pxLoader )
	{
		// Check if we've finished async loading
		//BW this is a pretty dumb way to do it, but it's a bit simpler than doing this on the loading thread.
		bool bOk = true;
		ASURA_MSSTREAM_LOADING_STATUS eStatus = m_pxLoader->Status();
		if ( eStatus == ASURA_MSSTREAM_LOADING_STATUS_SUCCESS )
		{
			//it just finished loading

			//get ready to play it
			bOk = PostLoad();

			if ( bOk && !m_bStopped )
			{
				//actually play it!!
				Asura_PS3_MSStreamingSound::DoPlay();
			}
		}
		
		if ( !bOk || eStatus == ASURA_MSSTREAM_LOADING_STATUS_ERROR )
		{
			Destroy();
		}
	}
}


////PGH////////////////////////////////////////////////////////////////////////
// Function    : Asura_PS3_MSStreamingSound::Create
// Description : Create and initialise a streaming sound
//
// Parameters  :
//   const char* szFilename  - Source filename
//
// Returns     : bool - Returns true on success
///////////////////////////////////////////////////////////////////////////////
bool Asura_PS3_MSStreamingSound::Create( const char* szFilename)
{
	m_fVolume = 1.0f;

	strncpy(m_szFilename, szFilename, ASURA_PS3_MAX_PATH-1);
	m_bStopped = true;
	m_bPaused = false;

	//Open the file asyncronously, because it's v slow from a bluray disc.

	//find an unused Asura_PS3_MSStreamLoader
	u_int i=0;
	while (i < uNUM_STREAM_LOADERS && g_axStreamLoaders[i].IsInUse())
		i++;
		
	if ( i < uNUM_STREAM_LOADERS )
	{
		m_pxLoader = &g_axStreamLoaders[i];
		m_pxLoader->Init(szFilename, m_pStreamBuffers); //flag it as being used

		// kick off an asynchronous file open & init (they take a long time when reading from bluray)
		Asura_PS3_MultiStream_Streaming::AddToLoadQueue( Asura_PS3_MSStreamLoader::QueuedLoad, (void*)m_pxLoader, sizeof(Asura_PS3_MSStreamLoader) );
		//Asura_PS3_MSStreamLoader::QueuedLoad(m_pxLoader, sizeof(Asura_PS3_MSStreamLoader)); // synchronous version, for debugging

		Asura_OutputToDebugger(("[Asura_PS3_MSStreamingSound::Create] - Created music stream (filename='%s') using loader %d\n", szFilename, i));
		return true;
	}
	else
	{
		Asura_Assert(0,"[Asura_PS3_MSStreamingSound::Create] - Ran out of stream loaders, cannot play stream\n");
		ASURA_PLATFORM_DEBUGBREAK(); //BW please let me know if this happens
		return false;
	}
}

////PGH////////////////////////////////////////////////////////////////////////
// Function    : Asura_PS3_MSStreamingSound::Destroy
// Description : Close streaming sound and reset for future use
//
// Parameters  : none
//
// Returns     : nothing
///////////////////////////////////////////////////////////////////////////////
void Asura_PS3_MSStreamingSound::Destroy()
{
	Asura_OutputToDebugger(("[Asura_PS3_MSStreamingSound::Destroy] - Destroyed music stream (handle=%d, filename='%s')\n", m_iMSStreamHandle, m_szFilename));

	Stop();

	if ( m_pxLoader )
	{
		//still loading
		m_pxLoader->Deinit();
		m_pxLoader = NULL;
	}

	//clean up anything that's been set

	// Close the output stream
	if ( m_iMSStreamHandle >= 0 )
	{
		cellMSCoreStop(m_iMSStreamHandle, CELL_MS_STOP);
		cellMSStreamClose(m_iMSStreamHandle);

		m_iMSSubBus = CELL_MS_MASTER_BUS; // Return to the master bus
		m_iMSStreamHandle = -1;
	}

	// Close the file we were streaming from
	if ( m_xFileHandle != Asura_PS3_FileSystem::INVALID_HANDLE )
	{
		Asura_PS3_MultiStream_Streaming::QueuedFileClose(m_xFileHandle);
	}
}


////PGH////////////////////////////////////////////////////////////////////////
// Function    : Asura_PS3_MSStreamingSound::Play
// Description : Start the streaming sound playing
//
// Parameters  :
//   bool bLooping - Set true to loop (doesn't work)
//   bool bPreload - Set true to preload this sound (ignored here)
//
// Returns     : nothing
///////////////////////////////////////////////////////////////////////////////
void Asura_PS3_MSStreamingSound::Play( bool bLooping, bool bPreload  )
{
	m_iLoopStart = bLooping ? m_xSoundInfo.GetLoopOffset() : -1;
	
	m_bStopped = false; //flag that we're playing (even if it hasn't loaded yet)
	
	bool bOk = true;
	if ( bPreload && m_pxLoader )
	{
		Asura_OutputToDebugger_Retail(("[Asura_PS3_MSStreamingSound::Play] - Preload: Waiting for stream to start\n"));
	
		//wait for loader to finish
		ASURA_MSSTREAM_LOADING_STATUS eStatus = m_pxLoader->Status();
		while ( eStatus == ASURA_MSSTREAM_LOADING_STATUS_PRELOAD || eStatus == ASURA_MSSTREAM_LOADING_STATUS_LOADING )
		{
			Asura_Thread_System::YieldCurrentThread();
			eStatus = m_pxLoader->Status();
		}
		
		bOk = PostLoad();
	}
	
	if ( bOk && !m_pxLoader )
	{
		//actually play it, only if it's loaded
		DoPlay();
	}
}
	
void Asura_PS3_MSStreamingSound::DoPlay( void )
{
	if ( m_iMSStreamHandle < 0 )
	{
		Asura_OutputToDebugger(("[Asura_PS3_MSStreamingSound::DoPlay] - Invalid music stream handle\n"));
		return;
	}

	UpdateVolume();

	Asura_OutputToDebugger(("Asura_PS3_MSStreamingSound::DoPlay] - Playing streaming music, handle %d, file %s\n", m_iMSStreamHandle, m_szFilename));

	if ( !m_bStopped )
	{
		// Set this stream playing
		cellMSStreamPlay(m_iMSStreamHandle);
	}
	
	if ( m_bPaused )
	{
		//immediately pause it
		cellMSCoreSetPause(m_iMSStreamHandle, CELL_MS_PAUSED);
	}
}



////PGH////////////////////////////////////////////////////////////////////////
// Function    : Asura_PS3_MSStreamingSound::Stop
// Description : Stop the stream from playing
//
// Parameters  : none
//
// Returns     : nothing
///////////////////////////////////////////////////////////////////////////////
void Asura_PS3_MSStreamingSound::Stop()
{
	if ( m_pxLoader )
	{
		//it's loading, just remember not to start playing it
		m_bStopped = true;
		return;
	}
	
	if ( m_iMSStreamHandle < 0 )
	{
		Asura_OutputToDebugger(("[Asura_PS3_MSStreamingSound::Stop] - invalid stream handle\n"));
		return;
	}

	if ( !m_bStopped )
	{
		cellMSCoreStop(m_iMSStreamHandle, CELL_MS_STOP);
	}

	m_bStopped = true;
}


////PGH////////////////////////////////////////////////////////////////////////
// Function    : Asura_PS3_MSStreamingSound::Pause
// Description : Pause playing this audio stream
//
// Parameters  : none
//
// Returns     : nothing
///////////////////////////////////////////////////////////////////////////////
void Asura_PS3_MSStreamingSound::Pause()
{
	if ( m_pxLoader )
	{
		//it's still loading, just remember to pause it when it's loaded
		m_bPaused = true;
		return;
	}

	if ( m_iMSStreamHandle < 0 )
	{
		Asura_OutputToDebugger(("[Asura_PS3_MSStreamingSound::Pause] - invalid stream handle\n"));
		return;
	}

	if ( IsPlaying() )
	{
		cellMSCoreSetPause(m_iMSStreamHandle, CELL_MS_PAUSED);
	}

	m_bPaused = true;
}


////PGH////////////////////////////////////////////////////////////////////////
// Function    : Asura_PS3_MSStreamingSound::Resume
// Description : Resume playing this audio stream
//
// Parameters  : none
//
// Returns     : nothing
///////////////////////////////////////////////////////////////////////////////
void Asura_PS3_MSStreamingSound::Resume()
{
	if ( m_pxLoader )
	{
		//it's loading, just flip the flag to deal with after load (in Play())
		m_bPaused = false;
		return;
	}

	if ( m_iMSStreamHandle < 0 )
	{
		Asura_OutputToDebugger(("[Asura_PS3_MSStreamingSound::Resume] - invalid stream handle\n"));
		return;
	}

	if ( !m_bStopped )
	{
		cellMSCoreSetPause(m_iMSStreamHandle, CELL_MS_NOTPAUSED);
	}

	m_bPaused = false;
}


////PGH////////////////////////////////////////////////////////////////////////
// Function    : Asura_PS3_MSStreamingSound::SetVolume
// Description : Set the stream playback volume (immediate execution)
//
// Parameters  :
//   float fVolume - new volume
//
// Returns     : nothing
///////////////////////////////////////////////////////////////////////////////
void Asura_PS3_MSStreamingSound::SetVolume( float fVolume )
{
	if (fVolume != m_fVolume)
	{
		m_fVolume = fVolume;
		if ( !m_bStopped )
		{
			UpdateVolume();
		}
	}
}


////PGH////////////////////////////////////////////////////////////////////////
// Function    : Asura_PS3_MSStreamingSound::UpdateVolume
// Description : Update the streaming sound's playback volume
//
// Parameters  : none
//
// Returns     : nothing
///////////////////////////////////////////////////////////////////////////////
void Asura_PS3_MSStreamingSound::UpdateVolume()
{
	if ( m_pxLoader || m_iMSStreamHandle < 0 )
	{
		//it's not playing yet so nothing to update, it will be updated when it starts playing
		return;
	}

	//TY - volume modifier, unfortunately completely empirical
	const float fVolumeMod = 0.5f;

	// Scale volume
	float fVolume = m_fVolume* dbg_fPS3StreamingSoundVolScale * fVolumeMod;

	// If only 1 channel, make it stereo
	if ( m_xSoundInfo.GetChannelCount() == 1 )
	{
		cellMSCoreSetVolume1(m_iMSStreamHandle, CELL_MS_DRY, CELL_MS_SPEAKER_FL, CELL_MS_CHANNEL_0, fVolume);
		cellMSCoreSetVolume1(m_iMSStreamHandle, CELL_MS_DRY, CELL_MS_SPEAKER_FR, CELL_MS_CHANNEL_0, fVolume);
	}
	else
	{
		// Set the volumes for each speaker based on channels present in stream
		for (int i=0; i < m_xSoundInfo.GetChannelCount(); i++)
		{
			cellMSCoreSetVolume1(m_iMSStreamHandle, CELL_MS_DRY, (CELL_MS_SPEAKERS)i, (CELL_MS_CHANNELS)i, fVolume);
		}
	}
}


////PGH////////////////////////////////////////////////////////////////////////
// Function    : Asura_PS3_MSStreamingSound::SetPosition
// Description : Set the playback position of the streaming sound
//
// Parameters  : 
//   float fTime - Time into stream in seconds
//
// Returns     : nothing
///////////////////////////////////////////////////////////////////////////////
void Asura_PS3_MSStreamingSound::SetPosition( float fTime )
{
	Asura_Assert(false, "SetPosition not currently implemented" );
}



////PGH////////////////////////////////////////////////////////////////////////
// Function    : Asura_PS3_MSStreamingSound::GetPosition
// Description : Get the playback position of the streaming sound
//
// Parameters  : none
//
// Returns     : float - Stream position in seconds
///////////////////////////////////////////////////////////////////////////////
float Asura_PS3_MSStreamingSound::GetPosition() const
{
	if ( m_pxLoader )
	{
		//it's still loading
		return 0.0f;
	}

	if ( m_iMSStreamHandle < 0 )
	{
		return CalcDuration(NULL, NULL);
	}

	int iByteOffset;
	if (m_xSoundInfo.GetSampleFormat() == CELL_MS_MP3)
	{
		// from the Multistream docs about cellMSStreamGetCurrentPlayPos:
		// "Please note that when using MP3s, the returned address is the current 
		// place in the buffer MultiStream has decoded to, and not the current place 
		// in the buffer MultiStream has played to. Because of internal buffering 
		// within MultiStream, to get the correct play position, you must use the 
		// function cellMSStreamGetMP3FramePos()."
		// The patch to SDK210 adds cellMSStreamGetMP3FrameInfo which also works.
		int iNumFrames;
		int iRet = cellMSStreamGetMP3FrameInfo(m_iMSStreamHandle, &iNumFrames, &iByteOffset);
		if (iRet < 0)
		{
			Asura_Assert(false, "cellMSStreamGetMP3FrameInfo failed");
			return 0.0f;
		}

		iByteOffset = iByteOffset % m_xSoundInfo.GetTotalDataBytes();
	}
	else
	{
		int iBuffer;
		cellMSStreamGetCurrentPlayPos(m_iMSStreamHandle, &iByteOffset, &iBuffer);  
		void *pBuffer = reinterpret_cast<void*>(iBuffer);   // Bizarre way for Sony to return a pointer (PGH)

		// Fix the offset position relative to tracked buffer starts (might be a thread-update race condition here)
		if ( pBuffer == m_pStreamBuffers[0] )
		{
			iByteOffset += m_uiBufferStarts[0];
		}
		else if ( pBuffer == m_pStreamBuffers[1] )
		{
			iByteOffset += m_uiBufferStarts[1];
		}
	}

	int iSamplesPerBlock, iSampleBlockSize;
	CalcDuration(&iSampleBlockSize, &iSamplesPerBlock);
	float fTime = (float(iByteOffset) * iSamplesPerBlock) / (float(m_xSoundInfo.GetSampleRate()) * iSampleBlockSize); // Rearranged from (iByteOffset / iSampleBlockSize) * (iSamplesPerBlock / m_xSoundInfo.m_iSampleRate);
	return fTime;
}


////PGH////////////////////////////////////////////////////////////////////////
// Function    : Asura_PS3_MSStreamingSound::GetLength
// Description : Returns the (approximate) length of the audio stream
//
// Parameters  : none
//
// Returns     : float - Stream duration in seconds 
///////////////////////////////////////////////////////////////////////////////
float Asura_PS3_MSStreamingSound::GetLength() const
{
	return CalcDuration(NULL, NULL);
}


////PGH////////////////////////////////////////////////////////////////////////
// Function    : Asura_PS3_MSStreamingSound::CalcDuration
// Description : Calculate the stream's duration (modified from PS3 dev net)
//
// Parameters  : 
//   int* piSampleBlockSize - Output pointer for size of the sample block
//   int* piSamplesPerBlock	- Output pointer for number of samples per block
//
// Returns     : float - Stream duration in seconds
///////////////////////////////////////////////////////////////////////////////
float Asura_PS3_MSStreamingSound::CalcDuration(int* piSampleBlockSize, int* piSamplesPerBlock) const
{
	if ( m_pxLoader )
	{
		return 0.0f; //BW code that calls this needs to be able to cope with this return value until the track's loaded
	}

	float fDuration = 0.0f;
	int iSampleBlockSize = 0;
	int iSamplesPerBlock = 0;
	
	const int iChannelCount = m_xSoundInfo.GetChannelCount();
	switch(m_xSoundInfo.GetSampleFormat())
	{
		case CELL_MS_ADPCM:	// VAG
		{
			// 16 bytes for 28 samples
			iSampleBlockSize = 16 * iChannelCount; 
			iSamplesPerBlock = 28;
			break;
		}
		case CELL_MS_ATRAC_LOW:
		{
			// 96 bytes for 1024 samples
			iSampleBlockSize = 96 * ((iChannelCount & 1) ? (iChannelCount + 1) : iChannelCount);
			iSamplesPerBlock = 1024;
			break;
		}
		case CELL_MS_ATRAC_MEDIUM:
		{
			// 152 bytes for 1024 samples
			iSampleBlockSize = 152 * iChannelCount;
			iSamplesPerBlock = 1024;
			break;
		}
		case CELL_MS_ATRAC_HIGH:
		{
			// 192 bytes for 1024 samples
			iSampleBlockSize = 192 * iChannelCount;
			iSamplesPerBlock = 1024;
			break;
		}
		case CELL_MS_16BIT_BIG:
		case CELL_MS_16BIT_LITTLE:
		{
			iSampleBlockSize = (m_xSoundInfo.GetSampleBits() >> 3) * iChannelCount;
			iSamplesPerBlock = 1;
			break;
		}
		case CELL_MS_MP3:
		{
			iSampleBlockSize = m_xSoundInfo.GetMP3FrameSize();
			iSamplesPerBlock = m_xSoundInfo.GetSamplesPerMP3Frame();
			break;
		}
		default:
		{
			Asura_Assert(false, "unhandled sample format");
		}
	}

	fDuration = (float(m_xSoundInfo.GetTotalDataBytes()) * iSamplesPerBlock) / (float(m_xSoundInfo.GetSampleRate()) * iSampleBlockSize);
	
	//  Return sample block size (if requested)
	if ( piSampleBlockSize != NULL )
	{
		*piSampleBlockSize = iSampleBlockSize;
	}

	//  Return sample count per block (if requested)
	if ( piSamplesPerBlock != NULL )
	{
		*piSamplesPerBlock = iSamplesPerBlock;
	}
	
	return fDuration;
}


////PGH////////////////////////////////////////////////////////////////////////
// Function    : Asura_PS3_MSStreamingSound::DoStreamData
// Description : Function streams data into the given buffer (limted in size)
//
// Parameters  :
//   void* pBuffer	    - Target buffer
//   u_int uBufferSize  - Maximum size of buffer
//   u_int& uSizeRead	- Actual size of data read
//   bool bRestartLoop  - Set true to restart at the loop point
//
// Returns     : bool - Returns true if more data to stream, false if finished
///////////////////////////////////////////////////////////////////////////////
bool Asura_PS3_MSStreamingSound::DoStreamData(void* pBuffer, u_int uBufferSize, u_int& uSizeRead, bool bRestartLoop)
{
	if ( m_xFileHandle != Asura_PS3_FileSystem::INVALID_HANDLE )
	{
		if ( bRestartLoop )
		{
			Asura_PS3_FileSystem::SetCurrentPosition(m_xFileHandle, m_xSoundInfo.GetDataOffset() + m_iLoopStart);
		}

		uSizeRead = Asura_PS3_FileSystem::ReadData(m_xFileHandle, uBufferSize, pBuffer);
		return (uSizeRead != 0);
	}

	return false;
}



////PGH////////////////////////////////////////////////////////////////////////
// Function    : Asura_PS3_MSStreamingSound::StreamLocalCallback
// Description : Local stream callback
//
// Parameters  :
//   int iStreamHandle   - Handle to stream making callback
//   void* pUserData     - Pointer to custom user data
//   int iCallbackType   - Type of callback request
//   void* pWriteBuffer  - Next write buffer to use
//   int nBufferSize     - Size of write buffer
//
// Returns     : nothing
///////////////////////////////////////////////////////////////////////////////
void Asura_PS3_MSStreamingSound::StreamLocalCallback(int iStreamHandle, void* pUserData, int iCallbackType, void* pWriteBuffer, int nBufferSize)
{
    switch( iCallbackType )
    {
        case CELL_MS_CALLBACK_MOREDATA:
        {
			Asura_PS3_MSStreamingSound *pxThis = reinterpret_cast<Asura_PS3_MSStreamingSound*>(pUserData);
			
			pxThis->m_xLoadDataRequest.m_iStreamHandle = iStreamHandle;
			pxThis->m_xLoadDataRequest.m_pWriteBuffer = pWriteBuffer;
			Asura_PS3_MultiStream_Streaming::AddToLoadQueue( LoadDataRequest, pxThis, sizeof(Asura_PS3_MSStreamingSound) );
			break;
        }

        case CELL_MS_CALLBACK_CLOSESTREAM:   //	CELL_MS_CALLBACK_FINISHSTREAM
        case CELL_MS_CALLBACK_FINISHSTREAM:
		{
			Asura_PS3_MSStreamingSound *pxThis = reinterpret_cast<Asura_PS3_MSStreamingSound*>(pUserData);
			if ( pxThis != NULL )
			{
				pxThis->m_bStopped = true;
			}

			Asura_OutputToDebugger(("Playback of streaming music completed (handle=%d)\n", iStreamHandle));
			break;
		}

		case CELL_MS_CALLBACK_MP3DECODEERROR:
		{
			Asura_Assert(false, "MP3 decode error");
			break;
		}

		case CELL_MS_CALLBACK_FINISHEDDRY:
		{
			//ignore
			break;
		}

        default: 
		{
			Asura_OutputToDebugger(("Unexpected Multistream callback type (%d) for music stream\n", iCallbackType));
			break;
		}
	}
}

// static function
void Asura_PS3_MSStreamingSound::LoadDataRequest( void* pThis, u_int uSize )
{

	u_int uSizeRead;

	Asura_PS3_MSStreamingSound* pxThis = static_cast<Asura_PS3_MSStreamingSound*>(pThis);

	if ( pxThis != NULL )
	{
		int iStreamHandle = pxThis->m_xLoadDataRequest.m_iStreamHandle;
		Asura_Assert(uSize = sizeof(Asura_PS3_MSStreamingSound), "unexpected size");
		void* pWriteBuffer = pxThis->m_xLoadDataRequest.m_pWriteBuffer;

		int iBufferIndex = (pWriteBuffer == pxThis->m_pStreamBuffers[0]) ? 0 : 1;  // Crude

		// Submit streamed data from file (until no bytes are read)
		if ( pxThis->DoStreamData(pWriteBuffer, ASURA_PS3_MUSIC_STREAM_BUFFER_SIZE, uSizeRead, false) )
		{
			cellMSStreamSetSecondRead( iStreamHandle, pWriteBuffer, (int)uSizeRead);
		}
		// If looping is set, restart streaming from the loop point
		else if ( pxThis->m_iLoopStart >= 0 )
		{
			// hack
			pxThis->DoStreamData(pWriteBuffer, ASURA_PS3_MUSIC_STREAM_BUFFER_SIZE, uSizeRead, true);
			cellMSStreamSetSecondRead( iStreamHandle, pWriteBuffer, (int)uSizeRead);
			pxThis->m_uiBufferStarts[1] = pxThis->m_uiBufferStarts[0] = 0;
		}
		// If no data and no loop point set, terminate playback
		else
		{
			cellMSStreamSetSecondRead( iStreamHandle, NULL, 0);
		}

		// Track the starts of the buffers (in bytes)	
		pxThis->m_uiBufferSizeRead[iBufferIndex]  = uSizeRead;
		pxThis->m_uiBufferStarts[1-iBufferIndex] += uSizeRead;
	}
}

bool Asura_PS3_MSStreamingSound::PostLoad ( void )
{
	m_pxLoader->GetData( &m_xFileHandle, &m_xSoundInfo, m_uiBufferSizeRead );
	m_pxLoader->Deinit();
	m_pxLoader = NULL; //checked by everything else to see if it's still loading.

	// Open a stream to play the audio through
	m_iMSStreamHandle = cellMSStreamOpen();
	if ( m_iMSStreamHandle < 0 )
	{
		Asura_Assertf(false, ("[Asura_PS3_MSStreamLoader::Load] - Unable to open new stream (error %d)\n", cellMSSystemGetLastError()));
		return false;
	}

	// Set the global stream callback (called for _all_ stream events unless overridden)
	cellMSStreamSetCallbackData(m_iMSStreamHandle, this);
	cellMSStreamSetCallbackFunc(m_iMSStreamHandle, &Asura_PS3_MSStreamingSound::StreamLocalCallback );

	// Load playback information for the stream
	CellMSInfo xWaveInfo;
	xWaveInfo.SubBusGroup		= m_iMSSubBus;
	xWaveInfo.flags				= CELL_MS_STREAM_NOFLAGS;
	xWaveInfo.initialOffset		= 0;
	xWaveInfo.inputType			= m_xSoundInfo.GetSampleFormat();
	xWaveInfo.Pitch             = m_xSoundInfo.GetSampleRate();
	xWaveInfo.numChannels		= m_xSoundInfo.GetChannelCount();
	xWaveInfo.FirstBuffer		= m_pStreamBuffers[0];
	xWaveInfo.FirstBufferSize	= m_uiBufferSizeRead[0];

	xWaveInfo.SecondBuffer		= m_pStreamBuffers[1];
	xWaveInfo.SecondBufferSize	= m_uiBufferSizeRead[1];

	// testing in case we really need to call these here
	cellMSStreamSetFirstRead(m_iMSStreamHandle, xWaveInfo.FirstBuffer, xWaveInfo.FirstBufferSize);
	cellMSStreamSetSecondRead(m_iMSStreamHandle, xWaveInfo.SecondBuffer, xWaveInfo.SecondBufferSize);

	// Track buffer read start position
	m_uiBufferStarts[0] = 0;		
	m_uiBufferStarts[1] = m_uiBufferSizeRead[0];

	// Bind audio information to the current stream
	int iErr = cellMSStreamSetInfo(m_iMSStreamHandle, &xWaveInfo);
	if ( iErr != CELL_OK )
	{
		iErr = cellMSSystemGetLastError();
		Asura_OutputToDebugger(("[Asura_PS3_MSStreamingSound::DoPlay] - cellMSStreamSetInfo error (code %d)\n", (iErr-CELL_MS_ERROR)));
		return false;
	}
	
	return true;
}

ASURA_MSSTREAM_LOADING_STATUS Asura_PS3_MSStreamingSound::GetLoadStatus ()
{
	if(m_pxLoader)
	{
		return m_pxLoader->Status();
	}
	return ASURA_MSSTREAM_LOADING_STATUS_INACTIVE;
}

// Asura_PS3_MSStreamLoader::Init
// 
// pStreamBuffers points to m_pStreamBuffers[2] in the calling Asura_PS3_MSStreamingSound
//
// runs in: main thread
//
void Asura_PS3_MSStreamLoader::Init( const char* szFilename, void **pStreamBuffers )
{
	Asura_Assert( m_eStatus == ASURA_MSSTREAM_LOADING_STATUS_INACTIVE, "[Asura_PS3_MSStreamLoader::Init] - This loader is already in use!" );

	strncpy(m_szFilename, szFilename, ASURA_PS3_MAX_PATH-1);

	m_xMutex.Lock(); //strictly speaking, we know nothing else is using it since we know this isn't set, but for convention always lock it when writing
	m_eStatus = ASURA_MSSTREAM_LOADING_STATUS_PRELOAD;
	m_xMutex.Unlock();

	//BW it is safe to use these buffers but ONLY because the only way to begin filling them is using Asura_PS3_MSStreamLoader, which all goes into 1 queue.
	//so in a bad case: an Asura_PS3_MSStreamingSound does Create(), Destroy(), and then is reused with Create() before the loader queue has finished QueuedLoad() and QueuedDeinit()
	//this means a different loader will be used, but it's load will go into the same queue, so it can't happen until after the first one in any case.
	//so it's safe provided we don't assume these buffers are unused immediately after Asura_PS3_MSStreamingSound::Destroy()
	m_pStreamBuffers[0] = pStreamBuffers[0];
	m_pStreamBuffers[1] = pStreamBuffers[1];
}


// Asura_PS3_MSStreamLoader::QueuedLoad
// 
// callback for the load function from the queue
//
// runs in: loader thread
//
void Asura_PS3_MSStreamLoader::QueuedLoad( void *pData, u_int uIgnore )
{	
	Asura_OutputToDebugger_Retail(("[Asura_PS3_MSStreamLoader::QueuedLoad] - Using  Loader %d/%d. ", static_cast<Asura_PS3_MSStreamLoader*>(pData) - g_axStreamLoaders, uNUM_STREAM_LOADERS));
	static_cast<Asura_PS3_MSStreamLoader*>(pData)->Load();
}


// Asura_PS3_MSStreamLoader::Load
// 
// asyncronous load of initial stream data. m_Mutex gets locked only very briefly when updating load status.
// main thread must not touch: m_xFileHandle, m_uiBufferSizeRead, and m_pStreamBuffers
//
// runs in: loader thread
//
void Asura_PS3_MSStreamLoader::Load( void )
{
	Asura_OutputToDebugger_Retail(("Opening %s\n", m_szFilename));

	m_xMutex.Lock();
	m_eStatus = ASURA_MSSTREAM_LOADING_STATUS_LOADING;
	m_xMutex.Unlock();

	// Attempt to pre-open the file (this can take a while for the disk to seek)
	if ( !Asura_PS3_FileSystem::OpenForReading(m_szFilename, &m_xFileHandle) )
	{
		Asura_OutputToDebugger_Retail(("Asynchronous loading FAILED %s\n", m_szFilename));
		Asura_Assertf( false, ("[Asura_PS3_MSStreamLoader::Load] - Unable to open file %s", m_szFilename) );
		m_xMutex.Lock();
		m_eStatus = ASURA_MSSTREAM_LOADING_STATUS_ERROR;
		m_xMutex.Unlock();
		return;
	}
	
	// Extract ATRAC information
	if ( !Asura_PS3_Sound_Loader::LoadFileInfoAuto(m_xFileHandle, m_xSoundInfo) )
	{
		Asura_Assertf( false, ("[Asura_PS3_MSStreamLoader::Load] - Unable to load audio from file '%s'", m_szFilename) );
		Asura_PS3_FileSystem::Close(&m_xFileHandle);
		m_xMutex.Lock();
		m_eStatus = ASURA_MSSTREAM_LOADING_STATUS_ERROR;
		m_xMutex.Unlock();
		return;
	}

	Asura_Assert(m_xSoundInfo.GetChannelCount() <= ASURA_PS3_SOUND_CHANNELS_PER_STREAM, "streaming sound has too many channels");

	Asura_PS3_FileSystem::SetCurrentPosition( m_xFileHandle, m_xSoundInfo.GetDataOffset() );

	// Stream in initial data (we need to do this async 'cause it's also quite slow)
	m_uiBufferSizeRead[0] = Asura_PS3_FileSystem::ReadData(m_xFileHandle, ASURA_PS3_MUSIC_STREAM_BUFFER_SIZE, m_pStreamBuffers[0]);
	m_uiBufferSizeRead[1] = Asura_PS3_FileSystem::ReadData(m_xFileHandle, ASURA_PS3_MUSIC_STREAM_BUFFER_SIZE, m_pStreamBuffers[1]);

	m_xMutex.Lock();
	m_eStatus = ASURA_MSSTREAM_LOADING_STATUS_SUCCESS;
	m_xMutex.Unlock();
	//this triggers the deinit in the main thread, DO NOTHING AFTER THIS POINT!
}


// Asura_PS3_MSStreamLoader::QueuedDeinit
// 
// callback for queued deinit
//
// runs in: loader thread
//
void Asura_PS3_MSStreamLoader::QueuedDeinit( void *pData, u_int uIgnore )
{
	Asura_OutputToDebugger_Retail(("[Asura_PS3_MSStreamLoader::QueuedDeinit] - Using  Loader %d/%d\n", static_cast<Asura_PS3_MSStreamLoader*>(pData) - g_axStreamLoaders, uNUM_STREAM_LOADERS));
	static_cast<Asura_PS3_MSStreamLoader*>(pData)->Deinit(true);
}


// Asura_PS3_MSStreamLoader::Deinit
// 
// clean up from the loader, including closing the file (which is a bit counter intuitive but that's how streaming does things, apparently)
//
// runs in: main thread / loader thread, adds itself to queue if corresponding load is still running.
//
void Asura_PS3_MSStreamLoader::Deinit( bool bQueued )
{
	m_xMutex.Lock(); //load could still be running if this is called from destroy

	Asura_Assert( m_eStatus != ASURA_MSSTREAM_LOADING_STATUS_INACTIVE, "[Asura_PS3_MSStreamLoader::Deinit] - This loader is not in use, cannot be Deinitialised!" );

	if ( m_eStatus == ASURA_MSSTREAM_LOADING_STATUS_PRELOAD || m_eStatus == ASURA_MSSTREAM_LOADING_STATUS_LOADING )
	{
		m_xMutex.Unlock();
		Asura_Assert( !bQueued, "[Asura_PS3_MSStreamLoader::Deinit] - calling QueuedDeinit from QueuedDeinit, this should be impossible! Fix it!!");
		//this assert "will never fire" because this function must be in the queue *after* the corresponding Load, which will set status to something else on completion.
		
		//it's currently loading, queue the deinit to happen sometime after the load
		Asura_PS3_MultiStream_Streaming::AddToLoadQueue(Asura_PS3_MSStreamLoader::QueuedDeinit, this, sizeof(Asura_PS3_MSStreamLoader));
		return;
	}

	Asura_OutputToDebugger_Retail(("[Asura_PS3_MSStreamLoader::Deinit] - Using  Loader %d/%d\n", this - g_axStreamLoaders, uNUM_STREAM_LOADERS));

	//if we still own this file, close it. If it's been handed over to MSStreamingSound this will be set to INVALID_HANDLE.
	if ( m_xFileHandle != Asura_PS3_FileSystem::INVALID_HANDLE )
	{
		Asura_PS3_MultiStream_Streaming::QueuedFileClose(m_xFileHandle);
	}

	m_eStatus = ASURA_MSSTREAM_LOADING_STATUS_INACTIVE;
	m_xMutex.Unlock();
}


// Asura_PS3_MSStreamLoader::GetData
// 
// hand the loaded data over to the caller
//
// runs in: main thread
//
void Asura_PS3_MSStreamLoader::GetData( Asura_PS3_FileSystem::Handle *pxFileHandle, Asura_PS3_Sound_Info *pxSoundInfo, u_int *puBufferSizeRead )
{
	if (m_xFileHandle == Asura_PS3_FileSystem::INVALID_HANDLE)
	{
		Asura_Assert(false, "MSLoader invalid handle");
	}
	else
	{
		*pxFileHandle = m_xFileHandle;
		*pxSoundInfo = m_xSoundInfo;
		puBufferSizeRead[0] = m_uiBufferSizeRead[0];
		puBufferSizeRead[1] = m_uiBufferSizeRead[1];
		
		//data has been handed over, we don't need the file (this is important for the deinit)
		m_xFileHandle = Asura_PS3_FileSystem::INVALID_HANDLE;
	}
}

Asura_PS3_MSStreamLoader::Asura_PS3_MSStreamLoader()
{
	m_eStatus = ASURA_MSSTREAM_LOADING_STATUS_INACTIVE;
	m_xFileHandle = Asura_PS3_FileSystem::INVALID_HANDLE;
}

#endif // #if defined(ASURA_PS3_USE_MULTISTREAM) && (ASURA_PS3_USE_MULTISTREAM != 0)

#endif // #ifndef ASURA_USE_AUDIO_SYSTEM

//eof
