mirror of
				https://github.com/RaySollium99/picodrive.git
				synced 2025-10-28 05:48:52 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			363 lines
		
	
	
	
		
			10 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			363 lines
		
	
	
	
		
			10 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*******************************************************************
 | |
|  *
 | |
|  *	File:		Audio_motorola.cpp
 | |
|  *
 | |
|  *	Author:		Peter van Sebille (peter@yipton.net)
 | |
|  *
 | |
|  *  Modified/adapted for picodriveN by notaz, 2006
 | |
|  *
 | |
|  *  (c) Copyright 2006, notaz
 | |
|  *	(c) Copyright 2001, Peter van Sebille
 | |
|  *	All Rights Reserved
 | |
|  *
 | |
|  *******************************************************************/
 | |
| 
 | |
| // if only I had Motorola to test this on..
 | |
| 
 | |
| 
 | |
| #include "audio_motorola.h"
 | |
| 
 | |
| #ifdef __DEBUG_PRINT_SND
 | |
| 	#include <e32svr.h> // RDebug
 | |
| 	#define DEBUGPRINT(x...) RDebug::Print(x)
 | |
| #else
 | |
| 	#define DEBUGPRINT(x...)
 | |
| #endif
 | |
| 
 | |
| 
 | |
| GLDEF_C TInt E32Dll(TDllReason)
 | |
| {
 | |
| 	return KErrNone;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*******************************************
 | |
|  *
 | |
|  * CGameAudioMot
 | |
|  *
 | |
|  *******************************************/
 | |
| 
 | |
| CGameAudioMot::CGameAudioMot(TInt aRate, TBool aStereo, TInt aPcmFrames,  TInt aBufferedFrames)
 | |
| : iRate(aRate), iStereo(aStereo), iBufferedFrames(aBufferedFrames), iPcmFrames(aPcmFrames)
 | |
| {
 | |
| 	DEBUGPRINT(_L("CGameAudioMot::CGameAudioMot"));
 | |
| }
 | |
| 
 | |
| 
 | |
| CGameAudioMot* CGameAudioMot::NewL(TInt aRate, TBool aStereo, TInt aPcmFrames, TInt aBufferedFrames)
 | |
| {
 | |
| 	DEBUGPRINT(_L("CGameAudioMot::NewL(%i, %i, %i, %i)"),aRate, aStereo, aPcmFrames, aBufferedFrames);
 | |
| 	CGameAudioMot*		self = new(ELeave) CGameAudioMot(aRate, aStereo, aPcmFrames, aBufferedFrames);
 | |
| 	CleanupStack::PushL(self);
 | |
| 	self->ConstructL();
 | |
| 	CleanupStack::Pop();		// self
 | |
| 	return self;
 | |
| }
 | |
| 
 | |
| 
 | |
| CGameAudioMot::~CGameAudioMot()
 | |
| {
 | |
| 	DEBUGPRINT(_L("CGameAudioMot::~CGameAudioMot()"));
 | |
| 	if(iAudioOutputStream) {
 | |
| 		iScheduler->Schedule(); // let it finish it's stuff
 | |
| 		//iAudioOutputStream->Stop();
 | |
| 		delete iAudioOutputStream;
 | |
| 	}
 | |
| 
 | |
| 	if(iAudioControl) delete iAudioControl;
 | |
| 
 | |
| 	for (TInt i=0 ; i < KSoundBuffers+1; i++) {
 | |
| 		delete iSoundBufferPtrs[i];
 | |
| 		delete iSoundBuffers[i];
 | |
| 	}
 | |
| 
 | |
| 	// Polled AS
 | |
| 	if(iScheduler) delete iScheduler;
 | |
| }
 | |
| 
 | |
| 
 | |
| void CGameAudioMot::ConstructL()
 | |
| {
 | |
| 	iScheduler = CPolledActiveScheduler::NewL();
 | |
| 
 | |
| 	iSettings.iPCMSettings.iSamplingFreq = (TMSampleRate) iRate;
 | |
| 	iSettings.iPCMSettings.iStereo       = iStereo;
 | |
| 
 | |
| 	TInt	bytesPerFrame = iStereo ? iPcmFrames << 2 : iPcmFrames << 1;
 | |
| 	for (TInt i=0 ; i<KSoundBuffers ; i++)
 | |
| 	{
 | |
| 		iSoundBuffers[i] = HBufC8::NewL(bytesPerFrame * iBufferedFrames);
 | |
| 		iSoundBuffers[i]->Des().FillZ  (bytesPerFrame * iBufferedFrames);
 | |
| 		iSoundBufferPtrs[i] = new TPtr8( iSoundBuffers[i]->Des() );
 | |
| 	}
 | |
| 	// because feeding 2 buffers after an underflow is a little too much, but feeding 1 may be not enough,
 | |
| 	// prepare this ~50ms empty buffer to additionaly feed after every underflow.
 | |
| 	iSoundBuffers[KSoundBuffers] = HBufC8::NewL(bytesPerFrame * (iBufferedFrames / 4));
 | |
| 	iSoundBuffers[KSoundBuffers]->Des().FillZ  (bytesPerFrame * (iBufferedFrames / 4));
 | |
| 	iSoundBufferPtrs[KSoundBuffers] = new TPtr8( iSoundBuffers[KSoundBuffers]->Des() );
 | |
| 
 | |
| 	iCurrentBuffer = 0;
 | |
| 	iListener.iFatalError = iListener.iIsOpen = iListener.iIsCtrlOpen = EFalse;
 | |
| 
 | |
| 	// here we actually test if we can create and open CMdaAudioOutputStream at all, but really create and use it later.
 | |
| 	iAudioOutputStream = CMAudioFB::NewL(EMAudioFBRequestTypeDecode, EMAudioFBFormatPCM, iSettings, iListener);
 | |
| 	if(iAudioOutputStream) {
 | |
| 		delete iAudioOutputStream;
 | |
| 		iAudioOutputStream = 0;
 | |
| 	}
 | |
| 
 | |
| 	// ceate audio control object
 | |
| 	iAudioControl = CMAudioAC::NewL(iListener);
 | |
| }
 | |
| 
 | |
| 
 | |
| // returns a pointer to buffer for next frame,
 | |
| // to be used when iSoundBuffers are used directly
 | |
| TInt16 *CGameAudioMot::NextFrameL()
 | |
| {
 | |
| 	iCurrentPosition += iPcmFrames << (iStereo?1:0);
 | |
| 
 | |
| 	if (++iFrameCount == iBufferedFrames)
 | |
| 	{
 | |
| 		WriteBlockL();
 | |
| 	}
 | |
| 
 | |
| 	iScheduler->Schedule();
 | |
| 
 | |
| 	if(iListener.iFatalError || iListener.iUnderflowed > KMaxUnderflows) {
 | |
| 		if(iAudioOutputStream) delete iAudioOutputStream;
 | |
| 		iAudioOutputStream = 0;
 | |
| 		return 0;
 | |
| 	}
 | |
| 	else if(iListener.iUnderflowed) UnderflowedL();
 | |
| 
 | |
| 	return iCurrentPosition;
 | |
| }
 | |
| 
 | |
| TInt16 *CGameAudioMot::DupeFrameL(TInt &aUnderflowed)
 | |
| {
 | |
| 	TInt shorts = iStereo ? (iPcmFrames << 1) : iPcmFrames;
 | |
| 	if(iFrameCount)
 | |
| 		Mem::Copy(iCurrentPosition, iCurrentPosition-shorts, shorts<<1);
 | |
| 	else {
 | |
| 		TInt lastBuffer = iCurrentBuffer;
 | |
| 		if(--lastBuffer < 0) lastBuffer = KSoundBuffers - 1;
 | |
| 		Mem::Copy(iCurrentPosition, ((TInt16*) (iSoundBuffers[lastBuffer]->Ptr()))+shorts*(iBufferedFrames-1), shorts<<1);
 | |
| 	}				
 | |
| 	iCurrentPosition += shorts;
 | |
| 
 | |
| 	if (++iFrameCount == iBufferedFrames)
 | |
| 	{
 | |
| 		WriteBlockL();
 | |
| 	}
 | |
| 
 | |
| 	iScheduler->Schedule();
 | |
| 
 | |
| 	if(iListener.iFatalError || iListener.iUnderflowed > KMaxUnderflows) {
 | |
| 		if(iAudioOutputStream) delete iAudioOutputStream;
 | |
| 		iAudioOutputStream = 0;
 | |
| 		return 0;
 | |
| 	}
 | |
| 	else if((aUnderflowed = iListener.iUnderflowed)) UnderflowedL(); // not again!
 | |
| 
 | |
| 	return iCurrentPosition;
 | |
| }
 | |
| 
 | |
| void CGameAudioMot::WriteBlockL()
 | |
| {
 | |
| 	iScheduler->Schedule();
 | |
| 
 | |
| 	// do not write until stream is open
 | |
| 	if(!iListener.iIsOpen) WaitForOpenToCompleteL();
 | |
| 	//if(!iListener.iHasCopied) WaitForCopyToCompleteL(); // almost never happens anyway and sometimes even deadlocks?
 | |
| 	//iListener.iHasCopied = EFalse;
 | |
| 	
 | |
| 
 | |
| 	if(!iListener.iUnderflowed) {
 | |
| 		iAudioOutputStream->QueueBufferL(iSoundBufferPtrs[iCurrentBuffer]);
 | |
| 		// it is certain we already Queued at least 2 buffers (one just after underflow, another above)
 | |
| 		if(!iDecoding) {
 | |
| 			iAudioOutputStream->DecodeL();
 | |
| 			iDecoding = ETrue;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	iFrameCount = 0;
 | |
| 	if (++iCurrentBuffer == KSoundBuffers)
 | |
| 		iCurrentBuffer = 0;
 | |
| 	iCurrentPosition = (TInt16*) iSoundBuffers[iCurrentBuffer]->Ptr();
 | |
| }
 | |
| 
 | |
| void CGameAudioMot::Pause()
 | |
| {
 | |
| 	if(!iAudioOutputStream) return;
 | |
| 
 | |
| 	iScheduler->Schedule();
 | |
| 	// iAudioOutputStream->Stop(); // may be this breaks everything in A925?
 | |
| 	delete iAudioOutputStream;
 | |
| 	iAudioOutputStream = 0;
 | |
| }
 | |
| 
 | |
| // call this before doing any playback!
 | |
| TInt16 *CGameAudioMot::ResumeL()
 | |
| {
 | |
| 	DEBUGPRINT(_L("CGameAudioMot::Resume()"));
 | |
| 	iScheduler->Schedule();
 | |
| 
 | |
| 	// we act a bit strange here: simulate buffer underflow, which actually starts audio
 | |
| 	iListener.iIsOpen = ETrue;
 | |
| 	iListener.iUnderflowed = 1;
 | |
| 	iListener.iFatalError = EFalse;
 | |
| 	iFrameCount = 0;
 | |
| 	iCurrentPosition = (TInt16*) iSoundBuffers[iCurrentBuffer]->Ptr();
 | |
| 	return iCurrentPosition;
 | |
| }
 | |
| 
 | |
| // handles underflow condition
 | |
| void CGameAudioMot::UnderflowedL()
 | |
| {
 | |
| 	// recreate the stream
 | |
| 	if(iAudioOutputStream) delete iAudioOutputStream;
 | |
| 	if(iListener.iUnderflowed > 4) {
 | |
| 		// HACK: A925 user said sound works for the first time, but fails after pause/resume, etc.
 | |
| 		// at the very beginning we create and delete CMAudioFB object, maybe we should do this every time?
 | |
| 		iAudioOutputStream = CMAudioFB::NewL(EMAudioFBRequestTypeDecode, EMAudioFBFormatPCM, iSettings, iListener);
 | |
| 		if(iAudioOutputStream) delete iAudioOutputStream;
 | |
| 	}
 | |
| 
 | |
| 	iAudioOutputStream = CMAudioFB::NewL(EMAudioFBRequestTypeDecode, EMAudioFBFormatPCM, iSettings, iListener);
 | |
| 	iListener.iIsOpen = EFalse;   // wait for it to open
 | |
| 	iDecoding = EFalse;
 | |
| 	//iListener.iHasCopied = ETrue; // but don't wait for last copy to complete
 | |
| 	// let it open and feed some stuff to make it happy
 | |
| 	User::After(0);
 | |
| 	//TInt lastBuffer = iCurrentBuffer;
 | |
| 	//if(--lastBuffer < 0) lastBuffer = KSoundBuffers - 1;
 | |
| 	iScheduler->Schedule();
 | |
| 	if(!iListener.iIsOpen) WaitForOpenToCompleteL();
 | |
| 	if(iListener.iUnderflowed) {
 | |
| 		// something went wrong again. May be it needs time? Trying to fix something without ability to test is hell.
 | |
| 		if(iAudioOutputStream) delete iAudioOutputStream;
 | |
| 		iAudioOutputStream = 0;
 | |
| 		User::After(50*000);
 | |
| 		iScheduler->Schedule();
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	iAudioOutputStream->QueueBufferL(iSoundBufferPtrs[KSoundBuffers]); // try a short buffer with hope to reduce lag
 | |
| }
 | |
| 
 | |
| 
 | |
| void CGameAudioMot::ChangeVolume(TInt aUp)
 | |
| {
 | |
| 	if(iAudioControl && iListener.iIsCtrlOpen)
 | |
| 	{
 | |
| 		TInt vol = iAudioControl->GetMasterVolume();
 | |
| 		TInt max = iAudioControl->GetMaxMasterVolume();
 | |
| 
 | |
| 		if(aUp) vol++; // adjust volume
 | |
| 		else    vol--;
 | |
| 
 | |
| 		if(vol >= 0 && vol <= max)
 | |
| 		{
 | |
| 			iAudioControl->SetMasterVolume(vol);
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| 
 | |
| void CGameAudioMot::WaitForOpenToCompleteL()
 | |
| {
 | |
| 	DEBUGPRINT(_L("CGameAudioMot::WaitForOpenToCompleteL"));
 | |
| 	TInt	count = 20;		// 2 seconds
 | |
| 	TInt	waitPeriod = 100 * 1000;
 | |
| 
 | |
| 	if(!iListener.iIsOpen) {
 | |
| 		// it is often enough to do this
 | |
| 		User::After(0);
 | |
| 		iScheduler->Schedule();
 | |
| 	}
 | |
| 	while (!iListener.iIsOpen && --count)
 | |
| 	{
 | |
| 		User::After(waitPeriod);
 | |
| 		iScheduler->Schedule();
 | |
| 	}
 | |
| 	if (!iListener.iIsOpen)
 | |
| 		User::LeaveIfError(KErrNotSupported);
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| void TGameAudioEventListener::OnEvent(TMAudioFBCallbackState aState, TInt aError)
 | |
| {
 | |
| 	switch ( aState )
 | |
| 	{
 | |
| 	case EMAudioFBCallbackStateReady:
 | |
| 		iIsOpen = ETrue;
 | |
| 		iUnderflowed = 0;
 | |
| 		break;
 | |
| 
 | |
| 	case EMAudioFBCallbackStateDecodeCompleteStopped:
 | |
| 		break;
 | |
| 
 | |
| 	//case EMAudioFBCallbackStateDecodeFileSystemError:
 | |
| 	case EMAudioFBCallbackStateDecodeError:
 | |
| 		switch( aError )
 | |
| 		{
 | |
| 		case EMAudioFBCallbackErrorBufferFull:
 | |
| 		case EMAudioFBCallbackErrorForcedStop:
 | |
| 		case EMAudioFBCallbackErrorForcedClose:
 | |
| 		//case EMAudioFBCallbackErrorForcedPause:
 | |
| 		case EMAudioFBCallbackErrorPriorityRejection:
 | |
| 		case EMAudioFBCallbackErrorAlertModeRejection:
 | |
| 		case EMAudioFBCallbackErrorResourceRejection:
 | |
| 		case EMAudioFBCallbackErrorUnknown:
 | |
| 			iUnderflowed++;
 | |
| 			break;
 | |
| 
 | |
| 		// these look like really bad errors
 | |
| 		case EMAudioFBCallbackErrorInvalidParameter:
 | |
| 		case EMAudioFBCallbackErrorWrongState:
 | |
| 		case EMAudioFBCallbackErrorFormatNotSupported:
 | |
| 		case EMAudioFBCallbackErrorFunctionNotSupported:
 | |
| 		case EMAudioFBCallbackErrorNoBuffer:
 | |
| 		case EMAudioFBCallbackErrorSampleOrBitRateNotSupported:
 | |
| 		//case EMAudioFBCallbackErrorPriorityOrPreferenceNotSupported:
 | |
| 		//case EMAudioFBCallbackErrorFileSystemFull:
 | |
| 			//iFatalError = ETrue;
 | |
| 			// who cares, just keep retrying
 | |
| 			iUnderflowed++;
 | |
| 			break;
 | |
| 
 | |
| 		default:
 | |
| 			iUnderflowed++;
 | |
| 			break;
 | |
| 		}
 | |
| 		// in error condition we also set to open, so that the
 | |
| 		// framework would not leave, catch the error and retry
 | |
| 		iIsOpen = ETrue;
 | |
| 		break;
 | |
| 
 | |
| 	default:
 | |
| 		break;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void TGameAudioEventListener::OnEvent(TMAudioFBCallbackState aState, TInt aError, TDes8* aBuffer)
 | |
| {
 | |
|    switch( aState )
 | |
|    {
 | |
|    case EMAudioFBCallbackStateDecodeBufferDecoded:
 | |
| 	  break;
 | |
| 
 | |
|    default:
 | |
|       OnEvent( aState, aError );
 | |
|       break;
 | |
|    }
 | |
| }
 | |
| 
 | |
| void TGameAudioEventListener::OnEvent(TMAudioACCallbackState aState, TInt aError)
 | |
| {
 | |
| 	if(aState == EMAudioACCallbackStateReady) iIsCtrlOpen = ETrue;
 | |
| }
 | |
| 
 | 
