/*
* Copyright (c) 2002-2007 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description: This class is used for playing sounds
*
*/
// INCLUDE FILES
#include <mmf/server/mmfdes.h>
#include <mmf/server/mmffile.h>
#include <AudioPreference.h>
#include <logger.h>
#include "cmmaaudioplayer.h"
const TInt KPlayerPriority = KAudioPriorityRecording;
const TInt KErrorMessageSize = 32;
const TUid KSourceUid = { KMmfUidDescriptorSource };
const TUid KFileSourceUid = { KMmfUidFileSource };
_LIT(KErrDefaultError, "Symbian OS Error: %d");
const TInt KMinIntervalBeforePrime = 0;
CPlaybackCompletedCallback* CPlaybackCompletedCallback::NewL(MPlaybackCompletedCallback& aObs)
{
CPlaybackCompletedCallback* self = new(ELeave)CPlaybackCompletedCallback(aObs);
CleanupStack::PushL(self);
self->ConstructL();
CleanupStack::Pop();
return self;
}
CPlaybackCompletedCallback::~CPlaybackCompletedCallback()
{
Cancel();
}
void CPlaybackCompletedCallback::RunL()
{
iObs.HandlePlaybackCompleteL();
}
TInt CPlaybackCompletedCallback::RunError(TInt aError)
{
iObs.ErrorPlaybackComplete(aError);
return KErrNone;
}
CPlaybackCompletedCallback::CPlaybackCompletedCallback(MPlaybackCompletedCallback& aObs)
: CTimer(EPriorityStandard), iObs(aObs)
{
CActiveScheduler::Add(this);
}
void CPlaybackCompletedCallback::Callback()
{
if (!IsActive())
After(KMinIntervalBeforePrime);
}
CMMAAudioPlayer* CMMAAudioPlayer::NewLC(
CMMAMMFResolver* aResolver)
{
CMMAAudioPlayer* self = new(ELeave) CMMAAudioPlayer(aResolver);
CleanupStack::PushL(self);
self->ConstructL();
return self;
}
CMMAAudioPlayer::~CMMAAudioPlayer()
{
delete iPlaybackCompleted;
}
CMMAAudioPlayer::CMMAAudioPlayer(
CMMAMMFResolver* aResolver):
CMMAMMFPlayerBase(aResolver)
{
}
void CMMAAudioPlayer::ConstructL()
{
CMMAMMFPlayerBase::ConstructL();
iPlaybackCompleted = CPlaybackCompletedCallback::NewL(*this);
}
EXPORT_C void CMMAAudioPlayer::PrefetchDataL(const TDesC8& aData)
{
LOG1(EJavaMMAPI, EInfo, "MMA::CMMAAudioPlayer::PrefetchDataL aData size %d",
aData.Size());
// player priority settings
TMMFPrioritySettings prioritySettings;
prioritySettings.iPriority = KPlayerPriority;
prioritySettings.iPref = EMdaPriorityPreferenceTimeAndQuality;
prioritySettings.iState = EMMFStatePlaying;
TMMFDescriptorConfig srcConfig;
srcConfig().iDes = (TAny*)&aData; // wants pointer to TPtr8
srcConfig().iDesThreadId = RThread().Id();
User::LeaveIfError(DoOpen(KSourceUid,
srcConfig,
KUidMmfAudioOutput,
KNullDesC8,
prioritySettings));
User::LeaveIfError(iController.Prime());
}
EXPORT_C void CMMAAudioPlayer::PrefetchFileL()
{
LOG(EJavaMMAPI, EInfo, "MMA::CMMAAudioPlayer::Prefetching from file");
// player priority settings
TMMFPrioritySettings prioritySettings;
prioritySettings.iPriority = KPlayerPriority;
prioritySettings.iPref = EMdaPriorityPreferenceTimeAndQuality;
prioritySettings.iState = EMMFStatePlaying;
TMMFFileConfig srcConfig;
srcConfig().iPath = iFileName->Des();
User::LeaveIfError(DoOpen(KFileSourceUid,
srcConfig,
KUidMmfAudioOutput,
KNullDesC8,
prioritySettings));
User::LeaveIfError(iController.Prime());
}
EXPORT_C void CMMAAudioPlayer::PlayCompleteL(TInt aError)
{
LOG(EJavaMMAPI, EInfo, "CMMAAudioPlayer::PlayCompleteL +");
TInt64 time;
GetDuration(&time);
iMediaTime = time;
iStartedEventTime = 0;
ChangeState(EPrefetched); // ready to play again
// Send 'Stopped' only when stop() is called.
PostLongEvent(CMMAPlayerEvent::EEndOfMedia, time);
if (aError == KErrNone)
{
iRepeatCount++;
// priming again for allowing e.g. mediatime setting
User::LeaveIfError(iController.Prime());
if (iRepeatForever || iRepeatCount < iRepeatNumberOfTimes)
{
StartL();
}
else
{
iRepeatCount = 0;
TTimeIntervalMicroSeconds position(0);
User::LeaveIfError(iController.SetPosition(position));
}
}
else
{
// error has occured, setting correct number of
// repeats for next start
SetLoopCount(iRepeatNumberOfTimes);
}
LOG(EJavaMMAPI, EInfo, "CMMAAudioPlayer::PlayCompleteL -");
}
void CMMAAudioPlayer::RealizeL()
{
LOG(EJavaMMAPI, EInfo, "CMMAAudioPlayer::RealizeL");
// DataSource must have at least 1 stream or
// we must have file to play
if ((iSourceStreams.Count() == 0) && !iFileName)
{
User::Leave(KErrNotEnoughStreams);
}
CMMAPlayer::RealizeL();
}
void CMMAAudioPlayer::PrefetchL()
{
LOG(EJavaMMAPI, EInfo, "CMMAAudioPlayer::PrefetchL");
__ASSERT_DEBUG((iSourceStreams.Count() > 0) || iFileName, User::Invariant());
if (iFileName)
{
// prefetching the file
PrefetchFileL();
// we can go to prefetched state immediately
PostActionCompletedFile();
ChangeState(EPrefetched);
//PostActionCompleted(KErrNone);
}
else
{
// Using TDes -- load the whole sound
iSourceStreams[ 0 ]->ReadAllL();
}
// CMMASourceStream will notify with ReadCompleted
}
const TDesC& CMMAAudioPlayer::Type()
{
return KMMAAudioPlayer;
}
//
// CMMASourceStreamReader finished read operation
// This is called when ReadL is completed in Prefetch()
//
void CMMAAudioPlayer::ReadCompletedL(TInt aStatus, const TDesC8& aData)
{
LOG1(EJavaMMAPI, EInfo, "CMMAAudioPlayer::ReadCompletedL: status = %d", aStatus);
if (aStatus < KErrNone)
{
PostActionCompleted(aStatus);
}
else
{
TRAPD(err, PrefetchDataL(aData));
if (err == KErrNone)
{
ChangeState(EPrefetched);
}
PostActionCompleted(err);
}
}
void CMMAAudioPlayer::HandleEvent(const TMMFEvent& aEvent)
{
LOG(EJavaMMAPI, EInfo, "MID::CMMAAudioPlayer::HandleEvent");
TInt err = aEvent.iErrorCode;
if (iState == EStarted)
{
// normal situation; will loop in PlayCompleteL if looping is set
if ((err == KErrEof || err == KErrUnderflow || err == KErrNone)
&& aEvent.iEventType == KMMFEventCategoryPlaybackComplete)
{
iPlaybackCompleted->Callback();
}
}
if (err == KErrDied && aEvent.iEventType == KMMFEventCategoryPlaybackComplete)
{
// basically pausing the playback
//1. when the phone call is received/answered , the player will be pushed to pause state and phone call is given high priority.
//2. when the call ends the player will still be in pause state , In this case the user should resume the player.
err = iController.Pause();
if (iState == EStarted)
{
TInt64 time;
GetMediaTime(&time);
iStartedEventTime = time;
LOG(EJavaMMAPI, EInfo, "MID::CMMAAudioPlayer::Going to ChangeState( EPrefetched );");
PostLongEvent(CMMAPlayerEvent::EStopped, time);
ChangeState(EPrefetched);
}
}
if (err != KErrNone && err != KErrDied)
{
ErrorPlaybackComplete(err);
}
}
EXPORT_C void CMMAAudioPlayer::HandlePlaybackCompleteL()
{
PlayCompleteL(KErrNone);
}
EXPORT_C void CMMAAudioPlayer::ErrorPlaybackComplete(TInt aError)
{
ELOG1(EJavaMMAPI, "MID::CMMAAudioPlayer::ErrorPlaybackComplete: aError = %d", aError);
TBuf<KErrorMessageSize> errorMessage;
errorMessage.Format(KErrDefaultError, aError);
PostStringEvent(CMMAPlayerEvent::EError, errorMessage);
// Preparing controller for next try
TInt err = iController.Prime();
if (err != KErrNone)
{
// Prime failed
errorMessage.Format(KErrDefaultError, err);
PostStringEvent(CMMAPlayerEvent::EError, errorMessage);
// we cannot recover, going back to unrealized state
ChangeState(EUnrealized);
return;
}
// If player was in started state, then error will change state to
// EPrefetched. In other cases the old state is retained.
if (iState == EStarted)
{
ChangeState(EPrefetched);
}
}
// END OF FILE