///////////////////////////////////////////////////////////////////////////////
//
// File      : Asura_PS3_FMV_adec.cpp
// Author    : Philip Hall
// Created   : 18/10/2007
//
// Copyright (C) 2007 Rebellion. All Rights Reserved
//
///////////////////////////////////////////////////////////////////////////////

#include "Core/Asura.h"

// This file's header
#include "FMV/Asura_PS3_FMV_adec.h"

// Asura headers
#include "Debugging/Asura_Assert.h"
#include "Debugging/Asura_LogFile.h"

// Asura PS3 headers
//

#include <cell/sysmodule.h>
#include <cell/mstream.h>
#include <sdk_version.h>

#if CELL_SDK_VERSION < 0x240000
#include <ppu_thread.h>
#endif


const uint32_t  ADEC_LIB_PPU_THREAD_PRIO = 550;
const size_t    ADEC_LIB_PPU_STACK_SIZE = 16384;
const uint32_t  ADEC_LIB_SPU_THREAD_PRIO = 155;
const u_int     ADEC_AUDIO_BUFFER_COUNT = 24;


Asura_PS3_FMV_adec::Asura_PS3_FMV_adec()
{
	m_chNum	       = 0;
	m_uSampleBits  = 0;
	m_uMemSize	   = 0;
	m_pWorkMemory  = 0;
	m_uPRXModule   = 0;
	m_pControl     = NULL;
	m_pESInfo      = 0;
	m_iRunningFrameCount = 0;
	m_iMSStreamHandle = -1;
	m_pAUInfo = NULL;
	m_bHoldingBlock = false;
	m_decHandle = NULL;
}

Asura_PS3_FMV_adec::~Asura_PS3_FMV_adec()
{
	Close();
}


bool Asura_PS3_FMV_adec::Initialise(Asura_PS3_FMV_Control* pControl)
{
	m_pControl = pControl;
	return true;
}


////PGH////////////////////////////////////////////////////////////////////////
// Function    : Asura_PS3_FMV_adec::Open
// Description : Open the audio decoder ready for processing the given 
//               elementary stream (originating from the demuxer)
//
// Parameters  : 
//   Asura_PS3_FMV_ESInfo* pESInfo  - Source elementary stream
//   u_int uChNum          - Number of audio channels in stream
//
// Returns     : bool - Returns true on success, false otherwise
///////////////////////////////////////////////////////////////////////////////
bool Asura_PS3_FMV_adec::Open(Asura_PS3_FMV_ESInfo* pESInfo, u_int uChNum)
{
	if ( !m_xAudioBuffer.Create(ADEC_AUDIO_BUFFER_COUNT, ASURA_ALIGN_128_BYTE) )
	{
		Asura_Assert(false, "[Asura_PS3_FMV_adec::Open] - Unable to allocate audio ring buffer\n")
		return false;
	}

	int iResult;
	CellAdecCb cb = { &Asura_PS3_FMV_adec::CallbackProxy, this };

	m_pESInfo = pESInfo;
	m_chNum = uChNum;


	// Open a stream to play the audio through
	m_iMSStreamHandle = cellMSStreamOpen();
	if ( m_iMSStreamHandle < 0 )
	{
		// Multistream MUST be initialised at this point (by other systems)
		Asura_Assert(false, "[Asura_PS3_FMV_adec::InitialiseAudio] - Unable to open audio stream for playback");
		return false;
	}
	
	// Set the stream callback
	cellMSStreamSetCallbackData(m_iMSStreamHandle, this);
	cellMSStreamSetCallbackFunc(m_iMSStreamHandle, &Asura_PS3_FMV_adec::AudioStreamCallback );
	if ( m_iMSStreamHandle < 0 )
	{
		Asura_OutputToDebugger(("[Asura_PS3_FMV_adec::InitialiseAudio] - Invalid stream handle\n"));
		return false;
	}

	// Set the volumes for each speaker based on channels present in stream
	for (u_int uChannel=0; uChannel < uChNum; uChannel++)
	{
		cellMSCoreSetVolume1(m_iMSStreamHandle, CELL_MS_DRY, (CELL_MS_SPEAKERS)uChannel, (CELL_MS_CHANNELS)uChannel, 1.0f);
	}

	switch (pESInfo->uStreamType)
	{
		case CELL_PAMF_STREAM_TYPE_PAMF_LPCM:
			Asura_OutputToDebugger(("[Asura_PS3_FMV_adec::Open] - Stream is LPCM\n"));
			m_uPRXModule = CELL_SYSMODULE_ADEC_LPCM;
			m_decType.audioCodecType = CELL_ADEC_TYPE_LPCM_PAMF;
			m_uSampleBits = pESInfo->xSpecInfo.lpcm.bitsPerSample;
		break;
		
		case CELL_PAMF_STREAM_TYPE_AC3:
			Asura_OutputToDebugger(("[Asura_PS3_FMV_adec::Open] - Stream is AC3\n"));
			m_uPRXModule = CELL_SYSMODULE_ADEC_AC3;
			m_decType.audioCodecType = CELL_ADEC_TYPE_AC3;
		break;
		
		case CELL_PAMF_STREAM_TYPE_ATRAC3PLUS:
			Asura_OutputToDebugger(("[Asura_PS3_FMV_adec::Open] - Stream is ATRAC3PLUS\n"));
			m_uPRXModule = CELL_SYSMODULE_ADEC_ATX;
			if (m_chNum <= 2)
			{
				m_decType.audioCodecType = CELL_ADEC_TYPE_ATRACX_2CH;
			}
			else if(m_chNum <= 6)
			{
				m_decType.audioCodecType = CELL_ADEC_TYPE_ATRACX_6CH;
			}
			else
			{
				m_decType.audioCodecType = CELL_ADEC_TYPE_ATRACX_8CH;
			}
		break;
		
		default:
		{
			Asura_Assert(false, "[Asura_PS3_FMV_adec::Open] - Don't understand this audio stream type\n");
			return false;
		}
	}
	
	iResult = cellSysmoduleLoadModule(m_uPRXModule);
	if (iResult < CELL_OK)
	{
		Asura_Assertf(false,("[Asura_PS3_FMV_adec::Open] - cellSysmoduleLoadModule failed (error = %X)\n", iResult));
		return false;
	}
	

	CellAdecAttr xAttr;
	iResult = cellAdecQueryAttr(&m_decType, &xAttr);
	if (iResult < CELL_OK)
	{
		Asura_OutputToDebugger(("[Asura_PS3_FMV_adec::Open] - cellAdecQueryAttr failed (error = %X)\n", iResult));
		goto labelError;
	}
	
	
	m_uMemSize = xAttr.workMemSize;
	m_pWorkMemory = Asura_Allocate(m_uMemSize);
	if (m_pWorkMemory == NULL)
	{
		Asura_OutputToDebugger(("[Asura_PS3_FMV_adec::Open] - Unable to allocate working memory\n"));
		goto labelError;
	}

	CellAdecResource xResource;
	xResource.totalMemSize		 = m_uMemSize;
	xResource.startAddr			 = m_pWorkMemory;
	xResource.ppuThreadPriority	 = ADEC_LIB_PPU_THREAD_PRIO;
	xResource.spuThreadPriority	 = ADEC_LIB_SPU_THREAD_PRIO;
	xResource.ppuThreadStackSize = ADEC_LIB_PPU_STACK_SIZE;
		
	iResult = cellAdecOpen(&m_decType, &xResource, &cb, &m_decHandle);
	if (iResult < CELL_OK)
	{
		Asura_OutputToDebugger(("[Asura_PS3_FMV_adec::Open] - cellAdecQueryAttr failed (error = %X)\n", iResult));
		goto labelError;
	}
	
	iResult = MakeSeqParam();
	if (iResult < CELL_OK)
	{
		Asura_OutputToDebugger(("[Asura_PS3_FMV_adec::Open] - MakeSeqParam failed (error = %X)\n", iResult));
		goto labelError;
	}	

	iResult = cellAdecStartSeq(m_decHandle, &m_decParam);
	if (iResult < CELL_OK)
	{
		Asura_OutputToDebugger(("[Asura_PS3_FMV_adec::Open] - cellAdecStartSeq failed (error = %X)\n", iResult));
		goto labelError;
	}	
	
	return true;
	
labelError:
	Close();
	return false;
}


////PGH////////////////////////////////////////////////////////////////////////
// Function    : Asura_PS3_FMV_adec::Close
// Description : Clean up adec wrapper and unload audio decoder module
//
// Parameters  : none
//
// Returns     : nothing
///////////////////////////////////////////////////////////////////////////////
void Asura_PS3_FMV_adec::Close()
{
	if ( m_iMSStreamHandle != -1 )
	{
		cellMSStreamClose(m_iMSStreamHandle);
		m_iMSStreamHandle = -1;
	}

	if ( m_decHandle != NULL )
	{
		// Close the audio decoder
		int iResult = cellAdecClose(m_decHandle);
		if (iResult < CELL_OK)
		{
			Asura_Assertf(false, ("[Asura_PS3_FMV_adec::Close] - cellAdecClose failed (error = %X)\n", iResult));
		}
		m_decHandle = NULL;
	}

	// Free allocated working memory 
	if ( m_pWorkMemory != NULL )
	{
		Asura_Deallocate(m_pWorkMemory);
		m_pWorkMemory = NULL;
	}

	// Unload PRX module
	if ( m_uPRXModule != 0 )
	{
		cellSysmoduleUnloadModule(m_uPRXModule);
		m_uPRXModule = 0;
	}
}



///////////////////////////////////////////////////////////////////////////////
// Function    : Asura_PS3_FMV_adec::MakeSeqParam
// Description : 
//
// Parameters  : none
//
// Returns     : int - Error result code
///////////////////////////////////////////////////////////////////////////////
int Asura_PS3_FMV_adec::MakeSeqParam()
{
	UAdecParam*	pDecParam = &m_decParam;
	uint32_t	chNum = m_chNum;
	uint32_t	sampleByte = 0;
	
	switch (m_decType.audioCodecType)
	{
		case CELL_ADEC_TYPE_LPCM_PAMF:
			switch(chNum)
			{
				case 1:
					pDecParam->lpcm.channelNumber = CELL_ADEC_CH_MONO;
					break;
				case 2:
					pDecParam->lpcm.channelNumber = CELL_ADEC_CH_STEREO;
					break;
				case 6:
					pDecParam->lpcm.channelNumber = CELL_ADEC_CH_3_2_LFE;
					break;
				case 8:
					pDecParam->lpcm.channelNumber = CELL_ADEC_CH_3_4_LFE;
					break;
				default:
					return RET_CODE_ERR_ARG;
			}
			pDecParam->lpcm.sampleRate	= CELL_ADEC_FS_48kHz;
			if (m_uSampleBits == CELL_DMUX_PAMF_BITS_PER_SAMPLE_24)
			{
				pDecParam->lpcm.sizeOfWord	= CELL_ADEC_BIT_LENGTH_24;
				sampleByte = 3;
			}
			else if(m_uSampleBits == CELL_DMUX_PAMF_BITS_PER_SAMPLE_16)
			{
				pDecParam->lpcm.sizeOfWord	= CELL_ADEC_BIT_LENGTH_16;
				sampleByte = 2;
			}
			else
			{
				return RET_CODE_ERR_ARG;
			}
			pDecParam->lpcm.audioPayloadSize = (chNum+(chNum&1)) * sampleByte * 48 * 5;
		break;
		
		case CELL_ADEC_TYPE_AC3:
			pDecParam->ac3.wordSize	= CELL_ADEC_AC3_WORD_SZ_FLOAT;
			switch(chNum)
			{
				case 1:
					pDecParam->ac3.outputMode	= CELL_ADEC_AC3_OUTPUT_MODE_1_0;
					pDecParam->ac3.outLfeOn		= CELL_ADEC_AC3_LFE_NOT_PRESENT;
					pDecParam->ac3.channelPointer0 = CELL_ADEC_AC3_INPUT_CHANNEL_C;
					pDecParam->ac3.channelPointer1 = CELL_ADEC_AC3_INPUT_CHANNEL_L;
					pDecParam->ac3.channelPointer2 = CELL_ADEC_AC3_INPUT_CHANNEL_R;
					break;
				case 2:
					pDecParam->ac3.outputMode	= CELL_ADEC_AC3_OUTPUT_MODE_2_0;
					pDecParam->ac3.outLfeOn		= CELL_ADEC_AC3_LFE_NOT_PRESENT;
					pDecParam->ac3.channelPointer0 = CELL_ADEC_AC3_INPUT_CHANNEL_L;
					pDecParam->ac3.channelPointer1 = CELL_ADEC_AC3_INPUT_CHANNEL_R;
					pDecParam->ac3.channelPointer2 = CELL_ADEC_AC3_INPUT_CHANNEL_C;
					break;
				case 6:
					pDecParam->ac3.outputMode	= CELL_ADEC_AC3_OUTPUT_MODE_3_2;
					pDecParam->ac3.outLfeOn		= CELL_ADEC_AC3_LFE_PRESENT;
					pDecParam->ac3.channelPointer0 = CELL_ADEC_AC3_INPUT_CHANNEL_L;
					pDecParam->ac3.channelPointer1 = CELL_ADEC_AC3_INPUT_CHANNEL_C;
					pDecParam->ac3.channelPointer2 = CELL_ADEC_AC3_INPUT_CHANNEL_R;
					break;
				default:
					return RET_CODE_ERR_ARG;
			}
			pDecParam->ac3.channelPointer3 = CELL_ADEC_AC3_INPUT_CHANNEL_l;
			pDecParam->ac3.channelPointer4 = CELL_ADEC_AC3_INPUT_CHANNEL_r;
			pDecParam->ac3.channelPointer5 = CELL_ADEC_AC3_INPUT_CHANNEL_s;
			pDecParam->ac3.drcCutScaleFactor = 1.0f;
			pDecParam->ac3.drcBoostScaleFactor = 1.0f;
			pDecParam->ac3.compressionMode = CELL_ADEC_AC3_COMPRESSION_MODE_LINE_OUT;
			pDecParam->ac3.numberOfChannels = chNum;
			pDecParam->ac3.stereoMode	= CELL_ADEC_AC3_STEREO_MODE_AUTO_DETECT;
			pDecParam->ac3.dualmonoMode = CELL_ADEC_AC3_DUALMONO_MODE_STEREO;
			pDecParam->ac3.karaokeCapableMode = CELL_ADEC_AC3_KARAOKE_CAPABLE_MODE_BOTH_VOCAL;
			pDecParam->ac3.pcmScaleFactor  = 1.0f;
		break;
		
		case CELL_ADEC_TYPE_ATRACX_2CH: // Fallthrough
		case CELL_ADEC_TYPE_ATRACX_6CH:	// Fallthrough
		case CELL_ADEC_TYPE_ATRACX_8CH:
			pDecParam->at3plus.nch_out = chNum;
			pDecParam->at3plus.bw_pcm = CELL_ADEC_ATRACX_WORD_SZ_FLOAT;
			pDecParam->at3plus.downmix_flag = 0;
			pDecParam->at3plus.au_includes_ats_hdr_flg = CELL_ADEC_ATRACX_ATS_HDR_INC;
			break;
		default:
			return RET_CODE_ERR_ARG;
	}
	
	return CELL_OK;
}



////PGH////////////////////////////////////////////////////////////////////////
// Function    : Asura_PS3_FMV_adec::StartProcessing
// Description : Start processing the next ES element for the given stream
//
// Parameters  : 
//
// Returns     : Asura_PS3_FMV_DecodeResult - Result code
///////////////////////////////////////////////////////////////////////////////
Asura_PS3_FMV_DecodeResult Asura_PS3_FMV_adec::StartProcessing()
{
	// Release any completed audio AUs
	if ( m_pESInfo->xAUDone.TryWait() )
	{
		cellDmuxReleaseAu(m_pESInfo->xHandle);
	}

	// If there are any new AUs then try to process them
	if ( m_pAUInfo == NULL )
	{
		void* pAuSpecificInfo;
		int iResult = cellDmuxGetAu(m_pESInfo->xHandle, &m_pAUInfo, &pAuSpecificInfo);
		switch ( iResult )
		{
			case CELL_OK: break;

			case CELL_DMUX_ERROR_EMPTY:
			{
				return ASURA_PS3_FMV_DECODE_EMPTY;	
			}

            default: break;
		}
	}

	if (m_pAUInfo != NULL) 
	{
		CellAdecAuInfo au = { m_pAUInfo->auAddr, m_pAUInfo->auSize, { m_pAUInfo->ptsUpper, m_pAUInfo->ptsLower }, 0 };
		int iResult = cellAdecDecodeAu(m_decHandle, &au);
		switch (iResult)
		{
			case CELL_OK:
				m_pAUInfo = NULL;
				if ( m_pESInfo->Lock() )
				{
					m_pESInfo->iDMUXFoundAUCount--; 
					m_pESInfo->Unlock();
				}
			break;
			
			case CELL_ADEC_ERROR_BUSY:
			case CELL_ADEC_ERROR_LPCM_BUSY:
			case CELL_ADEC_ERROR_AC3_BUSY:
			case CELL_ADEC_ERROR_ATX_BUSY:
			break;
			
			default:
				m_pControl->Abort(RET_CODE_ERR_UNKNOWN);
			break;
		}
	}


	// We're holding a block destined for the audio buffer (try to insert it)
	if ( m_bHoldingBlock )
	{
		Asura_PS3_AudioBlock* pBlock = m_xAudioBuffer.BeginAlloc();	 // Safe since only this end allocates
		if (pBlock != NULL)
		{
			if ( m_xBufferLock.Lock(1) )
			{
				*pBlock = m_xHeldBlock;		  // Copy block
				m_xAudioBuffer.EndAlloc();
				m_bHoldingBlock = false;
				m_xBufferLock.Unlock();
			}
		}
	}
	else // Not holding a block
	{
		// See if there are any new completed PCM blocks to extract
		if ( m_pESInfo->xReady.TryWait() )
		{
			if ( GetNextPCMBlock() != CELL_OK )
			{
				m_pControl->Abort(RET_CODE_ERR_FATAL);
			}
			m_bHoldingBlock = true;
		}
	}

	return (m_pAUInfo == NULL) ? ASURA_PS3_FMV_DECODE_OK : ASURA_PS3_FMV_DECODE_BUSY;
}


////PGH////////////////////////////////////////////////////////////////////////
// Function    : Asura_PS3_FMV_adec::GetNextPCMBlock
// Description : Retrieve the next PCM data chunk into the holding block
//
// Parameters  : none
//
// Returns     : int - Cell result code
///////////////////////////////////////////////////////////////////////////////
int Asura_PS3_FMV_adec::GetNextPCMBlock()
{
	// Get information about the next PCM data block
	const CellAdecPcmItem* pPCMItem;
	int iResult = cellAdecGetPcmItem( m_decHandle, &pPCMItem );
	if ( iResult != CELL_OK )
	{
		return iResult;
	}

	// Retrieve the next PCM data block and hold it until we can fit it into the buffer
	m_xHeldBlock.xTimestamp = pPCMItem->auInfo.pts;
	m_xHeldBlock.uSize = pPCMItem->size;
	Asura_Assert( m_xHeldBlock.uSize < sizeof(m_xHeldBlock.acData), "PCM data won't fit in buffer (make the buffer blocks bigger)");
	iResult = cellAdecGetPcm( m_decHandle, m_xHeldBlock.acData );
	if (iResult != CELL_OK)
	{
		return iResult;
	}

	return CELL_OK;	
}



////PGH////////////////////////////////////////////////////////////////////////
// Function    : Asura_PS3_FMV_adec::Callback
// Description : Audio decoder AU decoding callback
//
// Parameters  : 
//   CellAdecHandle handle    - Handle to the adec audio decoder
//   CellAdecMsgType msgType  - Audio decoder message
//   int32_t msgData          - Message specific parameters
//
// Returns     : int32_t - Result code (always 0)
///////////////////////////////////////////////////////////////////////////////
int32_t Asura_PS3_FMV_adec::Callback(CellAdecHandle /*handle*/, CellAdecMsgType msgType, int32_t msgData)
{
	switch ( msgType )
	{
		case CELL_ADEC_MSG_TYPE_AUDONE:
			m_pESInfo->xAUDone.Signal();
		break;

		case CELL_ADEC_MSG_TYPE_PCMOUT:
		{
			//Asura_OutputToDebugger(("    ADEC PCM Frame %d\n", m_iRunningFrameCount++));
			m_pESInfo->xReady.Signal();
		}
		break;

		case CELL_ADEC_MSG_TYPE_SEQDONE:
			Asura_OutputToDebugger(("[Asura_PS3_FMV_adec::Callback] - ADEC Sequence completed\n"));
			m_pControl->EndAudioData();
		break;

		case CELL_ADEC_MSG_TYPE_ERROR:
			Asura_OutputToDebugger(("[Asura_PS3_FMV_adec::Callback] - ADEC fatal error 0x%X\n", msgData));
			m_pControl->Abort(RET_CODE_ERR_FATAL);
		break;

		default:
			Asura_Assert(false, "[Asura_PS3_FMV_adec::Callback] - Unhandled callback message type\n");
			m_pControl->Abort(RET_CODE_ERR_FATAL);
		break;
	}

	return 0;
}




////PGH////////////////////////////////////////////////////////////////////////
// Function    : Asura_PS3_FMV_adec::AudioStreamCallback
// Description : Multistream audio update callback
//
// Parameters  : 
//   int iStreamHandle   - Stream handle to update
//   void* pUserData     - Custom user data
//   int iCallbackType   - Callback message type
//   void* pWriteBuffer  - Target write buffer for data (unused here)
//   int nBufferSize     - Size of target buffer
//
// Returns     : nothing
///////////////////////////////////////////////////////////////////////////////
void Asura_PS3_FMV_adec::AudioStreamCallback(int iStreamHandle, void* pUserData, int iCallbackType, void* pWriteBuffer, int nBufferSize)
{
	Asura_PS3_FMV_adec* pThis = reinterpret_cast<Asura_PS3_FMV_adec*>(pUserData);
    switch( iCallbackType )
    {
        case CELL_MS_CALLBACK_MOREDATA:
        {
			// Drop the previous buffer (because we just played it)
			if ( pThis->m_xBufferLock.Lock(0) )
			{
				pThis->m_xAudioBuffer.Drop();
				pThis->m_xBufferLock.Unlock();
			}

			// Wait for next block to appear (only for a short time)
			while ( !(pThis->m_pControl->IsEndOfAudioData() || pThis->m_pControl->IsAborted()) )
			{
				// Limited wait so that we can poll the exit flag...
				if ( pThis->m_xBufferLock.Lock(5000) )
				{
					Asura_PS3_AudioBlock* pBlock = pThis->m_xAudioBuffer.Peek(0);
					if ( pBlock != NULL )
					{
						pThis->m_pControl->SetATime(pBlock->xTimestamp);

						cellMSStreamSetSecondRead( iStreamHandle, pBlock->acData, (int)pBlock->uSize );
						pThis->m_xBufferLock.Unlock();
						break;
					}
					pThis->m_xBufferLock.Unlock();					
				}
			}

			// If the audio decoder's no longer running and there are no blocks left, we're done
			if ( pThis->m_pControl->IsEndOfAudioData() && pThis->m_xAudioBuffer.IsEmpty() )
			{
				cellMSStreamSetSecondRead( iStreamHandle, NULL, 0 );
				break;
			}
        }
        break;

        case CELL_MS_CALLBACK_CLOSESTREAM:   //	CELL_MS_CALLBACK_FINISHSTREAM
        case CELL_MS_CALLBACK_FINISHSTREAM:
		{
			Asura_OutputToDebugger(("[Asura_PS3_FMV_adec::AudioStreamCallback] - Playback of FMV audio completed (stream handle=%d)\n", iStreamHandle));
		}
		break;

        default: break;
	}
}


////PGH////////////////////////////////////////////////////////////////////////
// Function    : Asura_PS3_FMV_adec::StartAudio
// Description : Start audio playback (if possible)
//
// Parameters  : none
//
// Returns     : bool - Returns true if audio playback actually started
///////////////////////////////////////////////////////////////////////////////
bool Asura_PS3_FMV_adec::StartAudio()
{
	// If there are at least 2 buffers ready we can actually start playing
	if ( m_xAudioBuffer.GetCount() > 1 && (m_iMSStreamHandle >= 0) )
	{
		// First sample is teeny tiny chunk of silence (because multistream seems to need priming).
		static float safSilence[8] __attribute__((aligned (128))) = { 0,0,0,0,0,0,0,0 };

		Asura_PS3_AudioBlock* pBlock = m_xAudioBuffer.Peek(0);
		if ( pBlock->uSize == 0 )
		{
			m_xAudioBuffer.Drop();
			pBlock = m_xAudioBuffer.Peek(0);
		}

		// Setup stream playback
		CellMSInfo xWaveInfo;
		xWaveInfo.SubBusGroup		= CELL_MS_MASTER_BUS;
		xWaveInfo.flags				= CELL_MS_STREAM_AUTOCLOSE;
		xWaveInfo.initialOffset		= 0;
		xWaveInfo.inputType			= CELL_MS_32BIT_FLOAT;   // Adec always outputs floats
		xWaveInfo.Pitch             = 48000;                 // Adec always outputs at 48000
		xWaveInfo.numChannels		= m_chNum;

		// Initialise buffers - these BOTH must be set before playback starts
		xWaveInfo.FirstBuffer		= safSilence;
		xWaveInfo.FirstBufferSize	= sizeof(safSilence);

		xWaveInfo.SecondBuffer		= pBlock->acData;
		xWaveInfo.SecondBufferSize	= pBlock->uSize;

		// Set the audio start time for synchronisation
		m_pControl->SetATime(pBlock->xTimestamp);

		// Bind audio information to the current stream
		if ( m_iMSStreamHandle >= 0 )
		{
			int iErr = cellMSStreamSetInfo(m_iMSStreamHandle, &xWaveInfo);
			if ( iErr != CELL_OK )
			{
				Asura_Assert(false, ("[Asura_PS3_FMV_adec::InitialiseAudio] - cellMSStreamSetInfo error\n"));
				return false;
			}
			
			cellMSStreamPlay(m_iMSStreamHandle);
		}
		return true;
	}
	return false;
}

////PGH////////////////////////////////////////////////////////////////////////
// Function    : Asura_PS3_FMV_adec::StopAudio
// Description : Stop audio playback
//
// Parameters  : none
//
// Returns     : nothing
///////////////////////////////////////////////////////////////////////////////
void Asura_PS3_FMV_adec::StopAudio()
{
	if ( m_iMSStreamHandle != -1 )
	{
		cellMSCoreStop(m_iMSStreamHandle, CELL_MS_STOP);
		cellMSStreamClose(m_iMSStreamHandle);
		m_iMSStreamHandle = -1;
	}
}





