/*
* Copyright (c) 2005-2009 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 implements legacy volume control of AVRCP Controller.
*
*
*/
// INCLUDE FILES
#include <remconbeareravrcp.h> // KRemConBearerAvrcpImplementationUid = 0x1020685f
#include <MediaStreamPropertiesPSKeys.h>
#include <remconinterfaceselector.h>
#include <remconcoreapicontroller.h>
#include <remconaddress.h>
#include <e32math.h>
#include "btrccLegacyVolumeLevelController.h"
#include "debug.h"
const TInt KVolumeNotSynchronized = -1;
const TInt KLegacyVolumeLevelSetServiceId = 0x03;
const TInt KVolumeSyncRecheckTimer = 500000; // 0.5 second
const TInt KVolumeCommandInterval = 50000; // 0.05 second
const TInt KVolumeScaleMax = 15;
// MODULE DATA STRUCTURES
// ================= MEMBER FUNCTIONS =======================
// -----------------------------------------------------------------------------
// CBTRCCLegacyVolumeLevelController::NewL
// Two-phased constructor.
// -----------------------------------------------------------------------------
//
CBTRCCLegacyVolumeLevelController* CBTRCCLegacyVolumeLevelController::NewL(CRemConInterfaceSelector& aInterfaceSelector, MBTRCCVolumeControllerObserver& aObserver)
{
CBTRCCLegacyVolumeLevelController* self = new (ELeave) CBTRCCLegacyVolumeLevelController(aObserver);
CleanupStack::PushL(self);
self->ConstructL(aInterfaceSelector);
CleanupStack::Pop(self);
return self;
}
// -----------------------------------------------------------------------------
// CBTRCCLegacyVolumeLevelController::CBTRCCVolumeLevelController
// C++ constructor.
// -----------------------------------------------------------------------------
//
CBTRCCLegacyVolumeLevelController::CBTRCCLegacyVolumeLevelController(MBTRCCVolumeControllerObserver& aObserver)
: CBTRCCVolumeLevelControllerBase(aObserver), iState(EStateWaitingForVolumeLevelChanges), iPhoneVolume(KVolumeNotSynchronized),
iCurrentPhoneVolume(KVolumeNotSynchronized), iCurrentRemoteVolume(KVolumeNotSynchronized)
{
}
// -----------------------------------------------------------------------------
// CBTRCCLegacyVolumeLevelController::ConstructL
// Symbian 2nd phase constructor.
// -----------------------------------------------------------------------------
//
void CBTRCCLegacyVolumeLevelController::ConstructL(CRemConInterfaceSelector& aInterfaceSelector)
{
TRACE_FUNC
CBTRCCVolumeLevelControllerBase::ConstructL();
iInterfaceSelector = &aInterfaceSelector;
iRemConVolController = CRemConCoreApiController::NewL(aInterfaceSelector, *this);
TRACE_INFO((_L("CRemConCoreApiController created")))
iBtrccLegacyVolumeActive = CBTRCCActive::NewL(*this, KLegacyVolumeLevelSetServiceId);
TRACE_INFO((_L("CBTRCCActive created")))
iCtrlRTimer.CreateLocal();
TRACE_INFO((_L("RTimer created")))
iLocalMaxVolume = GetLocalMaxVolume();
}
// -----------------------------------------------------------------------------
// Destructor.
// -----------------------------------------------------------------------------
//
CBTRCCLegacyVolumeLevelController::~CBTRCCLegacyVolumeLevelController()
{
TRACE_FUNC
delete iBtrccLegacyVolumeActive;
iCtrlRTimer.Close();
}
// -----------------------------------------------------------------------------
// CBTRCCLegacyVolumeLevelController::DoStart
// -----------------------------------------------------------------------------
//
void CBTRCCLegacyVolumeLevelController::DoStart(TInt aInitialVolume)
{
TRACE_FUNC
AdjustRemoteVolume(aInitialVolume);
}
// -----------------------------------------------------------------------------
// CBTRCCLegacyVolumeLevelController::DoStop
// -----------------------------------------------------------------------------
//
void CBTRCCLegacyVolumeLevelController::DoStop()
{
TRACE_FUNC
iBtrccLegacyVolumeActive->Cancel();
iState = EStateWaitingForVolumeLevelChanges;
}
// -----------------------------------------------------------------------------
// CBTRCCLegacyVolumeLevelController::DoReset
// -----------------------------------------------------------------------------
//
void CBTRCCLegacyVolumeLevelController::DoReset()
{
TRACE_FUNC
iBtrccLegacyVolumeActive->Cancel();
iCurrentRemoteVolume = KVolumeNotSynchronized;
}
// -----------------------------------------------------------------------------
// CBTRCCLegacyVolumeLevelController::AdjustRemoteVolume
// -----------------------------------------------------------------------------
//
void CBTRCCLegacyVolumeLevelController::AdjustRemoteVolume(TInt aVolume)
{
// A call to this is made when PS Key changes.
TRACE_INFO((_L("CBTRCCLegacyVolumeLevelController::AdjustRemoteVolume, volume %d, state %d"), aVolume, iState))
iPhoneVolume = aVolume;
if(iCurrentRemoteVolume == KVolumeNotSynchronized)
{
// We set current phone volume level as the reference synchronization point
// if we haven't done it yet. Otherwise we synchronize remote to the current level.
// Just synchronize volumes and return, there is nothing else to do at this point.
iCurrentRemoteVolume = iPhoneVolume;
return;
}
switch(iState)
{
case EStateWaitingForVolumeLevelChanges:
// Just break here.
break;
case EStateDelay:
// Sharp volume jump handling is ongoing. In this case we want to update
// new volume level immediately.
if( iBtrccLegacyVolumeActive->IsActive() )
{
iBtrccLegacyVolumeActive->Cancel();
}
break;
default:
// We are middle of timing or volume up/down processing. New value
// is stored, just return here. New value is updated after previous
// job is done.
return;
}
DoRemoteControl();
}
// -----------------------------------------------------------------------------
// CBTRCCLegacyVolumeLevelController::DoRemoteControl
// -----------------------------------------------------------------------------
//
void CBTRCCLegacyVolumeLevelController::DoRemoteControl()
{
TRACE_INFO((_L("target volume level %d"), iPhoneVolume))
TRACE_INFO((_L("phone current volume level %d"), iCurrentPhoneVolume))
TRACE_INFO((_L("remote current volume level %d"), iCurrentRemoteVolume))
TState preState = iState;
iState = EStateWaitingForVolumeLevelChanges;
if ( iPhoneVolume != iCurrentRemoteVolume )
{
TInt prevPhoneVolume = iCurrentPhoneVolume;
iCurrentPhoneVolume = iPhoneVolume;
if ( iCurrentPhoneVolume == 0 && prevPhoneVolume > 1 )
{
// A sharp volume jump, most probably caused by audio stream switching.
// Delay the starting of audio adjustment for 0.5 seconds.
iState = EStateDelay;
}
else if (iCurrentPhoneVolume > iCurrentRemoteVolume)
{
iState = preState == EStateVolumeUpTiming ? EStateVolumeUp : EStateVolumeUpTiming;
}
else if (iCurrentPhoneVolume < iCurrentRemoteVolume)
{
iState = preState == EStateVolumeDownTiming ? EStateVolumeDown : EStateVolumeDownTiming;
}
}
switch (iState)
{
case EStateVolumeUpTiming:
{
TRACE_INFO(_L("Volume up timing"))
StartTimer(KVolumeCommandInterval);
break;
}
case EStateVolumeUp:
{
TRACE_INFO(_L("Volume up"))
SendVolumeCommand(EStateVolumeUp);
break;
}
case EStateVolumeDownTiming:
{
TRACE_INFO(_L("Volume down timing"))
StartTimer(KVolumeCommandInterval);
break;
}
case EStateVolumeDown:
{
TRACE_INFO(_L("Volume down"))
SendVolumeCommand(EStateVolumeDown);
break;
}
case EStateDelay:
{
TRACE_INFO(_L("Delay before updating volume"))
StartTimer( KVolumeSyncRecheckTimer );
break;
}
case EStateWaitingForVolumeLevelChanges:
{
TRACE_INFO(_L("Waiting volume change"))
break;
}
default:
{
// Not possible
}
}
}
// -----------------------------------------------------------------------------
// CBTRCCLegacyVolumeLevelController::ScalePhoneVolume
// -----------------------------------------------------------------------------
//
void CBTRCCLegacyVolumeLevelController::ScalePhoneVolume(TInt &aVolume)
{
TRACE_FUNC
// Converts volume level scale used in DevSound Audio (0 - 10000) into 0 - KVolumeScaleMax.
if( aVolume <= 0 )
{
// 0 level and -1 (no stream) don't need adjustment.
return;
}
TReal tgt;
TReal src(aVolume);
TReal maxScale(KVolumeScaleMax);
TReal maxDevSound(iLocalMaxVolume);
TReal scale = maxScale/maxDevSound;
Math::Round( tgt, src*scale, 0);
if( TInt(tgt) == 0 )
{
// Only aVol value 0 is real zero (muted). All other scaled zero
// values have to be upgraded to level 1.
tgt = 1;
}
TRACE_INFO((_L("Volume scaled: original %d, scaled %d"), aVolume, TInt(tgt)))
aVolume = TInt(tgt);
}
// -----------------------------------------------------------------------------
// CBTRCCLegacyVolumeLevelController::RequestCompletedL
// -----------------------------------------------------------------------------
//
void CBTRCCLegacyVolumeLevelController::RequestCompletedL(CBTRCCActive& aActive, TInt aErr)
{
TRACE_FUNC
if( aActive.ServiceId() == KLegacyVolumeLevelSetServiceId )
{
TRACE_INFO( (_L("CBTRCCLegacyVolumeLevelController iState: %d"), iState ) )
if ( aErr != KErrNone )
{
// Something wrong happened, could be e.g. the connection has
// been released while we are changing volume on remote device.
iState = EStateWaitingForVolumeLevelChanges;
return;
}
switch(iState)
{
case EStateVolumeUp:
{
++iCurrentRemoteVolume;
break;
}
case EStateVolumeDown:
{
--iCurrentRemoteVolume;
break;
}
case EStateDelay:
case EStateWaitingForVolumeLevelChanges:
case EStateVolumeUpTiming:
case EStateVolumeDownTiming:
break;
default:
// Impossible to happen.
break;
}
DoRemoteControl();
}
else
{
// This is something the base class can handle.
CBTRCCVolumeLevelControllerBase::RequestCompletedL(aActive, aErr);
}
}
// -----------------------------------------------------------------------------
// CBTRCCLegacyVolumeLevelController::CancelRequest
// -----------------------------------------------------------------------------
//
void CBTRCCLegacyVolumeLevelController::CancelRequest(TInt aServiceId)
{
TRACE_FUNC
if( aServiceId == KLegacyVolumeLevelSetServiceId )
{
// Cancel possible active timers
iCtrlRTimer.Cancel();
if (iState == EStateVolumeUp)
{
++iCurrentRemoteVolume;
}
else if (iState == EStateVolumeDown)
{
--iCurrentRemoteVolume;
}
}
else
{
// This is something the base class can handle.
CBTRCCVolumeLevelControllerBase::CancelRequest(aServiceId);
}
}
// -----------------------------------------------------------------------------
// CBTRCCLegacyVolumeLevelController::StartTimer
// -----------------------------------------------------------------------------
//
void CBTRCCLegacyVolumeLevelController::StartTimer(TInt aInterval)
{
TRACE_FUNC
if( !iBtrccLegacyVolumeActive->IsActive() )
{
iCtrlRTimer.After(iBtrccLegacyVolumeActive->iStatus, aInterval);
iBtrccLegacyVolumeActive->GoActive();
}
else
{
TRACE_INFO(_L("CBTRCCLegacyVolumeLevelController::StartTimer() ERR, timer already active!"))
}
}
// -----------------------------------------------------------------------------
// CBTRCCLegacyVolumeLevelController::SendVolumeCommand
// -----------------------------------------------------------------------------
//
void CBTRCCLegacyVolumeLevelController::SendVolumeCommand(TInt aDirection)
{
TRACE_FUNC
if( !iBtrccLegacyVolumeActive->IsActive() )
{
if( aDirection == EStateVolumeUp )
{
iRemConVolController->VolumeUp(iBtrccLegacyVolumeActive->iStatus, iNumRemotes, ERemConCoreApiButtonClick);
}
else
{
iRemConVolController->VolumeDown(iBtrccLegacyVolumeActive->iStatus, iNumRemotes, ERemConCoreApiButtonClick);
}
iBtrccLegacyVolumeActive->GoActive();
}
else
{
TRACE_INFO(_L("CBTRCCLegacyVolumeLevelController::DoRemConControl() ERR, previous action ongoing!"))
}
}
// -----------------------------------------------------------------------------
// CBTRCCLegacyVolumeLevelController::MrccacoResponse()
// -----------------------------------------------------------------------------
//
TInt CBTRCCLegacyVolumeLevelController::MrccacoResponse()
{
TRACE_FUNC
return 0;
}
// End of File