sl@0: // Copyright (c) 2003-2009 Nokia Corporation and/or its subsidiary(-ies). sl@0: // All rights reserved. sl@0: // This component and the accompanying materials are made available sl@0: // under the terms of "Eclipse Public License v1.0" sl@0: // which accompanies this distribution, and is available sl@0: // at the URL "http://www.eclipse.org/legal/epl-v10.html". sl@0: // sl@0: // Initial Contributors: sl@0: // Nokia Corporation - initial contribution. sl@0: // sl@0: // Contributors: sl@0: // sl@0: // Description: sl@0: // sl@0: sl@0: sl@0: #include sl@0: #include sl@0: #include sl@0: #include sl@0: #include sl@0: #include sl@0: #include "MmfAudioToneController.h" sl@0: #include sl@0: sl@0: /* sl@0: A list of panic codes for the Audio Tone Controller sl@0: @internalTechnology sl@0: sl@0: TMmfAudioToneControllerPanics is an enumeration with the following entries: sl@0: EBadArgument indicates a bad argument sl@0: EBadState indicates a state viaolation sl@0: EBadInvariant indicates an invariant violation sl@0: EBadReset indicates failed reset sl@0: EPostConditionViolation indicates a post condition violation sl@0: */ sl@0: enum TMmfAudioToneControllerPanics sl@0: { sl@0: EBadArgument, sl@0: EBadState, sl@0: EBadInvariant, sl@0: EBadReset, sl@0: EPostConditionViolation, sl@0: EBadCall, sl@0: ENoDataSource, sl@0: ENoDataSink, sl@0: EMessageAlreadyBeingProcessed, sl@0: ENoMessageToProcess, sl@0: EStateNotReadyToPlay, sl@0: EStateNotConstructed, sl@0: EBadStateToGetDataSource, sl@0: EBadStateToGetDataSink, sl@0: EBadStateForPrime, sl@0: EStateNotPrimed, sl@0: EBadResetState, sl@0: EBadStateToReset, sl@0: EBadPlayState, sl@0: EBadPauseState, sl@0: EBadStateToPause, sl@0: EBadStateToStop, sl@0: EBadStopState, sl@0: ENotReadyForDataSourceRemoval, sl@0: EBadDataSourceRemoval, sl@0: ENotReadyForDataSinkRemoval, sl@0: EBadDataSinkRemoval, sl@0: ENotReadyForCustomCommand, sl@0: EBadStateAfterNegotiate, sl@0: EBadStateToSetPriority, sl@0: EBadPriorityState, sl@0: EBadInitializeState, sl@0: EBadStateToSetVolume, sl@0: EBadStateAfterVolumeSet, sl@0: EBadStateToGetMaxVolume, sl@0: EBadStateAfterGetMaxVolume, sl@0: EBadStateToGetVolume, sl@0: EBadStateAfterGetVolume, sl@0: EBadStateToSetVolumeRamp, sl@0: EBadStateAfterSetVolumeRamp, sl@0: EBadStateToSetBalance, sl@0: EBadStateAfterSetBalance, sl@0: EBadStateToGetBalance, sl@0: EBadStateAfterGetBalance, sl@0: EBadStateForTransition, sl@0: EBadStateAfterTransition, sl@0: EStateNotValid, sl@0: }; sl@0: sl@0: sl@0: /** sl@0: Instantiates a new Audio Tone Controller. Usually invoked from ECom sl@0: sl@0: @internalTechnology sl@0: @return CMMFController* bas class for all plugin controllers sl@0: */ sl@0: CMMFController* CMMFAudioToneController::NewL() sl@0: { sl@0: CMMFAudioToneController* self = new(ELeave) CMMFAudioToneController; sl@0: CleanupStack::PushL(self); sl@0: self->ConstructL(); sl@0: CleanupStack::Pop( self ); sl@0: return STATIC_CAST( CMMFController*, self ); sl@0: } sl@0: sl@0: sl@0: sl@0: /** sl@0: 2nd Phase constructor sl@0: sl@0: @internalComponent sl@0: */ sl@0: void CMMFAudioToneController::ConstructL() sl@0: { sl@0: iSourceAndSinkAdded = EFalse; sl@0: sl@0: // Construct custom command parsers sl@0: CMMFAudioPlayDeviceCustomCommandParser* audPlayDevParser = CMMFAudioPlayDeviceCustomCommandParser::NewL(*this); sl@0: CleanupStack::PushL(audPlayDevParser); sl@0: AddCustomCommandParserL(*audPlayDevParser); sl@0: CleanupStack::Pop( audPlayDevParser );//audPlayDevParser sl@0: sl@0: CMMFAudioPlayControllerCustomCommandParser* audPlayConParser = CMMFAudioPlayControllerCustomCommandParser::NewL(*this); sl@0: CleanupStack::PushL(audPlayConParser); sl@0: AddCustomCommandParserL(*audPlayConParser); sl@0: CleanupStack::Pop(audPlayConParser);//audPlayConParser sl@0: sl@0: CMMFAudioPlayControllerSetRepeatsCustomCommandParser* audPlayConSetRepeatsParser = CMMFAudioPlayControllerSetRepeatsCustomCommandParser::NewL(*this); sl@0: CleanupStack::PushL(audPlayConSetRepeatsParser); sl@0: AddCustomCommandParserL(*audPlayConSetRepeatsParser); sl@0: CleanupStack::Pop(audPlayConSetRepeatsParser); sl@0: sl@0: // [ assert the invariant now that we are constructed ] sl@0: __ASSERT_ALWAYS( Invariant(), Panic( EStateNotConstructed)); sl@0: } sl@0: sl@0: sl@0: /** sl@0: 1st Phase constructor sl@0: sl@0: @internalComponent sl@0: */ sl@0: sl@0: CMMFAudioToneController::CMMFAudioToneController() : iState(EStopped) sl@0: { sl@0: } sl@0: sl@0: /** sl@0: Destructor sl@0: sl@0: @internalTechnology sl@0: */ sl@0: CMMFAudioToneController::~CMMFAudioToneController() sl@0: { sl@0: delete iMMFDevSound; sl@0: delete iToneSequenceData; sl@0: delete iMessage; sl@0: } sl@0: sl@0: /** sl@0: Adds a data source to the controller sl@0: sl@0: @internalTechnology sl@0: sl@0: @param aSource will provide the data the controller plays sl@0: */ sl@0: void CMMFAudioToneController::AddDataSourceL(MDataSource& aSource) sl@0: { sl@0: //[ assert the invariant ] sl@0: __ASSERT_ALWAYS( Invariant(), Panic( EBadStateToGetDataSource)); sl@0: sl@0: // [ precondition that the controller is stopped ] sl@0: if( State() != EStopped ) sl@0: User::Leave( KErrNotReady ); sl@0: sl@0: //[ precondition iData source is not already configured ] sl@0: if (iDataSource) sl@0: User::Leave(KErrAlreadyExists); sl@0: sl@0: //Only works with files or descriptors sl@0: if ((aSource.DataSourceType() != KUidMmfFileSource ) && (aSource.DataSourceType() != KUidMmfDescriptorSource)) sl@0: User::Leave( KErrNotSupported ) ; sl@0: sl@0: sl@0: //extract the tone data into a buffer ready to play. sl@0: iToneSequenceData = CMMFDataBuffer::NewL(STATIC_CAST(CMMFClip*, &aSource)->Size()); sl@0: sl@0: aSource.SourcePrimeL(); sl@0: STATIC_CAST(CMMFClip*, &aSource)->ReadBufferL(iToneSequenceData,0); sl@0: aSource.SourceStopL(); sl@0: sl@0: sl@0: //[ its now safe to set the source ] sl@0: iDataSource = &aSource ; sl@0: sl@0: //[ assert the post condition ] sl@0: __ASSERT_ALWAYS(iDataSource, Panic(EMMFAudioControllerPanicDataSourceDoesNotExist)); sl@0: sl@0: iDataSource->SetSourcePrioritySettings(iPrioritySettings); sl@0: } sl@0: sl@0: sl@0: sl@0: /** sl@0: Adds a data sink to the controller sl@0: sl@0: @internalTechnology sl@0: sl@0: @param aSink will accept the data the controller plays sl@0: */ sl@0: void CMMFAudioToneController::AddDataSinkL(MDataSink& aSink) sl@0: { sl@0: //[ assert the invariant ] sl@0: __ASSERT_ALWAYS( Invariant(), Panic( EBadStateToGetDataSink)); sl@0: sl@0: // [ precondition that the controller is stopped ] sl@0: if( State() != EStopped ) sl@0: User::Leave( KErrNotReady ); sl@0: sl@0: // [ assert precondition that sink does not exist ] sl@0: if (iMMFDevSound) sl@0: User::Leave(KErrAlreadyExists); sl@0: sl@0: //Only support playing to audio output sl@0: if (aSink.DataSinkType() != KUidMmfAudioOutput) sl@0: User::Leave( KErrNotSupported ); sl@0: sl@0: iMMFDevSound = CMMFDevSound::NewL(); sl@0: sl@0: // [ assert post conditions that a sink has been added ] sl@0: __ASSERT_ALWAYS(iMMFDevSound, Panic(EMMFAudioControllerPanicDataSinkDoesNotExist)); sl@0: } sl@0: sl@0: /** sl@0: Primes the controller, ready for playing sl@0: sl@0: @internalTechnology sl@0: sl@0: @param aMessage allows response to client upon completion or error sl@0: */ sl@0: void CMMFAudioToneController::PrimeL(TMMFMessage& aMessage) sl@0: { sl@0: //[ assert the invariant ] sl@0: __ASSERT_ALWAYS( Invariant(), Panic( EBadStateForPrime)); sl@0: sl@0: //[ assert the precondition ( in a friendly way for this api sl@0: // that we are either stopped or primed already ] sl@0: if(!(( State() == EStopped ) || (State() == EPrimed ))) sl@0: User::Leave( KErrNotReady ); sl@0: sl@0: // [ precondition we have a data source & sink and aren't already processing a message] sl@0: __ASSERT_ALWAYS( iDataSource, Panic( ENoDataSource)); sl@0: __ASSERT_ALWAYS( iMMFDevSound, Panic( ENoDataSink)); sl@0: __ASSERT_ALWAYS( !iMessage, Panic( EMessageAlreadyBeingProcessed )); sl@0: sl@0: if (iState == EStopped) sl@0: { sl@0: sl@0: iMessage = new(ELeave) TMMFMessage(aMessage); //store message sl@0: __ASSERT_ALWAYS( iMessage, Panic( EPostConditionViolation )); //check if message created sucessfully sl@0: SetState(EPriming); sl@0: sl@0: TRAPD(err,NegotiateL()); sl@0: if(err != KErrNone) sl@0: { sl@0: SetState( EStopped ); sl@0: delete iMessage; sl@0: iMessage = NULL; sl@0: User::Leave(err); sl@0: } sl@0: } sl@0: sl@0: __ASSERT_ALWAYS( Invariant(), Panic( EStateNotPrimed ) ); sl@0: sl@0: } sl@0: sl@0: /** sl@0: Primes the controller, ready for playingAdds a data sink to the controller sl@0: sl@0: @internalTechnology sl@0: */ sl@0: void CMMFAudioToneController::PrimeL() sl@0: { sl@0: Panic(EBadCall); sl@0: } sl@0: sl@0: /** sl@0: This method resets the controller sl@0: sl@0: @internalTechnology sl@0: */ sl@0: void CMMFAudioToneController::ResetL() sl@0: { sl@0: __ASSERT_ALWAYS( Invariant(), Panic( EBadStateToReset ) ); sl@0: sl@0: // Stop recording if it's not stopped, sl@0: if (State() != EStopped) sl@0: { sl@0: StopL(); sl@0: } sl@0: sl@0: //[ ensure loggoff of source and sink ] sl@0: iDataSource = NULL ; sl@0: delete iMMFDevSound; iMMFDevSound = NULL ; sl@0: iSourceAndSinkAdded = EFalse; sl@0: delete iMessage; sl@0: iMessage = NULL; sl@0: sl@0: sl@0: // [ assert the invariant] sl@0: __ASSERT_ALWAYS( Invariant(), Panic( EBadResetState ) ); sl@0: sl@0: __ASSERT_ALWAYS( ResetPostCondition(), Panic( EBadReset )); sl@0: __ASSERT_ALWAYS( Invariant(), Panic(EBadState)); sl@0: } sl@0: sl@0: /** sl@0: This function determnines if the reset post condition is valid sl@0: sl@0: @internalComponent sl@0: */ sl@0: TBool CMMFAudioToneController::ResetPostCondition() const sl@0: { sl@0: TBool result = EFalse ; sl@0: if((iMMFDevSound == NULL) && sl@0: (iDataSource == NULL) && sl@0: (State() == EStopped)) sl@0: { sl@0: result = ETrue; sl@0: } sl@0: return result; sl@0: } sl@0: sl@0: sl@0: /** sl@0: Start playing the audio tone, passing the data to Dev Sound sl@0: sl@0: The controller will be put into the EPlaying state sl@0: sl@0: @internalTechnology sl@0: */ sl@0: void CMMFAudioToneController::PlayL() sl@0: { sl@0: // [ assert the precondition that the sl@0: // play command is only activated in the primed state] sl@0: if ( State() != EPrimed && State() != EPausePlaying) sl@0: User::Leave(KErrNotReady); sl@0: sl@0: // [ assert the Invariant ] sl@0: __ASSERT_ALWAYS( Invariant(), Panic(EStateNotReadyToPlay)); sl@0: sl@0: if(State() == EPausePlaying && iIsResumeSupported) sl@0: { sl@0: User::LeaveIfError(iMMFDevSound->Resume()); sl@0: } sl@0: else sl@0: { sl@0: iMMFDevSound->PlayToneSequenceL(iToneSequenceData->Data()); sl@0: } sl@0: sl@0: SetState( EPlaying ); sl@0: sl@0: //[ assert the post condition we are playing ] sl@0: __ASSERT_ALWAYS( (State() == EPlaying ), Panic( EBadState)); sl@0: //[ assert the invariant ] sl@0: __ASSERT_ALWAYS( Invariant(), Panic(EBadPlayState)); sl@0: } sl@0: sl@0: /** sl@0: This should Pause playing of the audio tone. HOWEVER, this is not possible with a sl@0: tone sequence. This method is therefore provided as a NOP purely to allow audio sl@0: clients to operate correctly. sl@0: sl@0: The controller will be put into the EPrimed state sl@0: sl@0: @internalTechnology sl@0: */ sl@0: void CMMFAudioToneController::PauseL() sl@0: { sl@0: //[ assert the invariant ] sl@0: __ASSERT_ALWAYS( Invariant(), Panic(EBadStateToPause)); sl@0: sl@0: __ASSERT_ALWAYS(iMMFDevSound, Panic(EMMFAudioControllerPanicDataSinkDoesNotExist)); sl@0: sl@0: if(iIsResumeSupported) sl@0: { sl@0: iMMFDevSound->Pause(); sl@0: SetState(EPausePlaying); sl@0: } sl@0: else sl@0: { sl@0: // If Resume is not supported sl@0: // this method can't do anything, as we have no interface to restart DevSound sl@0: // after pausing a tone. It need to be here however as client utility does sl@0: // Pause() Stop() when stopping. sl@0: SetState(EPrimed); sl@0: } sl@0: sl@0: //[ assert the post condition we are stopped ] sl@0: __ASSERT_ALWAYS( (State() == EPrimed || State() == EPausePlaying), Panic( EBadState)); sl@0: //[ assert the invariant ] sl@0: __ASSERT_ALWAYS( Invariant(), Panic(EBadPauseState)); sl@0: } sl@0: sl@0: /** sl@0: This stops the tone that is currently playing sl@0: The controller will be put into the EStopped state sl@0: sl@0: @internalTechnology sl@0: */ sl@0: void CMMFAudioToneController::StopL() sl@0: { sl@0: //[ assert the invariant ] sl@0: __ASSERT_ALWAYS( Invariant(), Panic(EBadStateToStop)); sl@0: sl@0: // [ precondition that we are not already stopped sl@0: // && if we are stopped do nothing. sl@0: // Due to the asynchronous nature of the controller sl@0: // interaction the response to stopped when stopped sl@0: // should not be an error ] sl@0: if (State() != EStopped) sl@0: { sl@0: //[ update state to stopped propogate to devsound ] sl@0: iMMFDevSound->Stop(); sl@0: SetState(EStopped); sl@0: } sl@0: sl@0: //[ assert the post condition we are stopped ] sl@0: __ASSERT_ALWAYS( (State() == EStopped), Panic( EBadState)); sl@0: //[ assert the invariant ] sl@0: __ASSERT_ALWAYS( Invariant(), Panic(EBadStopState)); sl@0: } sl@0: sl@0: sl@0: /** sl@0: Removes a data source form the controller sl@0: sl@0: @internalTechnology sl@0: sl@0: @param aDataSource The source that is to be removed. sl@0: */ sl@0: void CMMFAudioToneController::RemoveDataSourceL(MDataSource& aDataSource ) sl@0: { sl@0: //[ assert the invariant ] sl@0: __ASSERT_ALWAYS( Invariant(), Panic(ENotReadyForDataSourceRemoval) ); sl@0: sl@0: //[ precondition is that we have a data source ] sl@0: if( !iDataSource ) sl@0: User::Leave(KErrNotReady); sl@0: sl@0: //[precondition the data source is the data source we have] sl@0: if( iDataSource != &aDataSource ) sl@0: User::Leave(KErrArgument); sl@0: sl@0: //[ the controller is in the stopped state ] sl@0: if(State() != EStopped) sl@0: User::Leave(KErrNotReady); sl@0: sl@0: //[ remove the data sink from the controller and delete the format] sl@0: if( iSourceAndSinkAdded ) sl@0: { sl@0: iMMFDevSound->Stop(); sl@0: iSourceAndSinkAdded = EFalse ; sl@0: } sl@0: sl@0: iDataSource = NULL ; sl@0: sl@0: // [ assert postcondition we are stopped ] sl@0: __ASSERT_ALWAYS( (State() == EStopped), Panic(EPostConditionViolation) ); sl@0: sl@0: //[ assert postcondition the SourceAndSinkAdded is false ] sl@0: __ASSERT_ALWAYS( !iSourceAndSinkAdded, Panic( EPostConditionViolation )); sl@0: sl@0: //[ assert postcondition the data sink is null ] sl@0: __ASSERT_ALWAYS( (iDataSource == NULL ), Panic( EPostConditionViolation )); sl@0: sl@0: //[ assert the invariant ] sl@0: __ASSERT_ALWAYS( Invariant(), Panic(EBadDataSourceRemoval)); sl@0: } sl@0: sl@0: sl@0: /** sl@0: Removes a data sink form the controller sl@0: sl@0: @internalTechnology sl@0: sl@0: @param aDataSink The sink that is to be removed. We donot sl@0: use this value, as we only support a single sink. sl@0: */ sl@0: void CMMFAudioToneController::RemoveDataSinkL(MDataSink& /*aDataSink*/) sl@0: { sl@0: //[ assert the invariant ] sl@0: __ASSERT_ALWAYS( Invariant(), Panic(ENotReadyForDataSinkRemoval) ); sl@0: sl@0: //[ precondition is that we have a data sink ] sl@0: if(!iMMFDevSound) sl@0: User::Leave(KErrNotReady); sl@0: sl@0: //[ the controller is in the stopped state ] sl@0: if(State() != EStopped) sl@0: User::Leave(KErrNotReady); sl@0: sl@0: //[ remove the data sink from the controller and delete the format] sl@0: if(iSourceAndSinkAdded) sl@0: { sl@0: iMMFDevSound->Stop(); sl@0: iSourceAndSinkAdded = EFalse; sl@0: } sl@0: sl@0: delete iMMFDevSound; iMMFDevSound = NULL; sl@0: sl@0: sl@0: // [ assert postcondition we are stopped ] sl@0: __ASSERT_ALWAYS( (State() == EStopped), Panic(EPostConditionViolation) ); sl@0: sl@0: //[ assert postcondition the SourceAndSinkAdded is false ] sl@0: __ASSERT_ALWAYS( !iSourceAndSinkAdded, Panic( EPostConditionViolation )); sl@0: sl@0: //[ assert the invariant ] sl@0: __ASSERT_ALWAYS( Invariant(), Panic(EBadDataSinkRemoval)); sl@0: } sl@0: sl@0: /** sl@0: Handles a CustomCommand for the controller. This controller doesn't support Custom Commands sl@0: sl@0: @internalTechnology sl@0: @param aMessage sl@0: */ sl@0: void CMMFAudioToneController::CustomCommand(TMMFMessage& aMessage) sl@0: { sl@0: //[ assert the invariant ] sl@0: __ASSERT_ALWAYS( Invariant(), Panic(ENotReadyForCustomCommand)); sl@0: // [ We do not have any custom commands ] sl@0: aMessage.Complete(KErrNotSupported); sl@0: } sl@0: sl@0: /** sl@0: Configures Dev Sound, ready for playing. sl@0: sl@0: @internalComponent sl@0: */ sl@0: void CMMFAudioToneController::NegotiateL() sl@0: { sl@0: if (!iMMFDevSound) sl@0: Panic(EMMFAudioOutputDevSoundNotLoaded); sl@0: sl@0: iMMFDevSound->InitializeL(*this, EMMFStateTonePlaying); sl@0: iMMFDevSound->SetPrioritySettings(iPrioritySettings); sl@0: sl@0: //[ assert the invariant ] sl@0: __ASSERT_ALWAYS( Invariant(), Panic(EBadStateAfterNegotiate)); sl@0: } sl@0: sl@0: /** sl@0: Set the priority settings that this controller should use to access Dev Sound sl@0: sl@0: @internalTechnology sl@0: @param aPrioritySettings The requires priority settings sl@0: */ sl@0: void CMMFAudioToneController::SetPrioritySettings(const TMMFPrioritySettings& aPrioritySettings) sl@0: { sl@0: //[ assert the invariant ] sl@0: __ASSERT_ALWAYS( Invariant(), Panic(EBadStateToSetPriority)); sl@0: sl@0: //[ assert the precondition ] sl@0: if(State() != EStopped) sl@0: { sl@0: ASSERT(EFalse); // used to leave here with KErrNotReady sl@0: return; sl@0: } sl@0: sl@0: //[ update the priority settings of the controller] sl@0: iPrioritySettings = aPrioritySettings; sl@0: sl@0: if (iMMFDevSound) sl@0: { sl@0: iMMFDevSound->SetPrioritySettings(iPrioritySettings); sl@0: } sl@0: sl@0: __ASSERT_ALWAYS( Invariant(), Panic(EBadPriorityState)); sl@0: } sl@0: sl@0: sl@0: sl@0: /** sl@0: This method is called by DevSound after initialization, indicating that it has completed and sl@0: whether there was an error sl@0: sl@0: @internalTechnology sl@0: @param aError the error supplied by Dev Sound sl@0: */ sl@0: void CMMFAudioToneController::InitializeComplete(TInt aError) sl@0: { sl@0: //[ assert the state is EPriming ] sl@0: __ASSERT_ALWAYS( ( State() == EPriming ), Panic( EBadState )); sl@0: __ASSERT_ALWAYS( iMessage, Panic( ENoMessageToProcess )); sl@0: sl@0: if(aError != KErrNone) sl@0: { sl@0: SetState( EStopped ); sl@0: iMessage->Complete(aError); sl@0: } sl@0: else sl@0: { sl@0: SetState( EPrimed ); sl@0: iMessage->Complete(aError); sl@0: iIsResumeSupported = iMMFDevSound->IsResumeSupported(); sl@0: } sl@0: sl@0: delete iMessage; sl@0: iMessage = NULL; sl@0: sl@0: // [ assert the invariant] sl@0: __ASSERT_ALWAYS( Invariant(), Panic( EBadInitializeState ) ); sl@0: } sl@0: sl@0: sl@0: /** sl@0: This method is called by DevSound after it has finished playing a tone sequence, indicating sl@0: whether there was an error sl@0: sl@0: @internalTechnology sl@0: @param aError the error supplied by DevSound sl@0: */ sl@0: void CMMFAudioToneController::ToneFinished(TInt aError) sl@0: { sl@0: // NB KErrInUse, KErrDied OR KErrAccessDenied may be returned sl@0: // to indicate that the sound device is in use by another higher sl@0: // priority client. sl@0: if (aError == KErrCancel || aError == KErrInUse || sl@0: aError == KErrDied || aError == KErrAccessDenied) sl@0: return; sl@0: sl@0: if (aError == KErrUnderflow) sl@0: aError = KErrNone; sl@0: sl@0: if (State() != EStopped) sl@0: { sl@0: iMMFDevSound->Stop(); sl@0: SetState( EStopped ); sl@0: } sl@0: sl@0: //now send event to client... sl@0: TMMFEvent event; sl@0: event.iEventType = KMMFEventCategoryPlaybackComplete; sl@0: event.iErrorCode = aError; sl@0: DoSendEventToClient(event); sl@0: } sl@0: sl@0: sl@0: sl@0: /** sl@0: Called my DevSound to indicate we have been thrown off H/W by a higher priority sl@0: sl@0: @internalTechnology sl@0: @param aEvent contains the reason we have been contacted by DevSound sl@0: */ sl@0: void CMMFAudioToneController::SendEventToClient(const TMMFEvent& aEvent) sl@0: { sl@0: if(State() == EPlaying) sl@0: SetState(EStopped); sl@0: sl@0: DoSendEventToClient(aEvent); sl@0: } sl@0: sl@0: sl@0: sl@0: sl@0: /** sl@0: Sets the playback volume sl@0: sl@0: @internalTechnology sl@0: @param aVolume the required volume sl@0: */ sl@0: void CMMFAudioToneController::MapdSetVolumeL(TInt aVolume) sl@0: { sl@0: //[ assert the invariant ] sl@0: __ASSERT_ALWAYS( Invariant(), Panic(EBadStateToSetVolume)); sl@0: sl@0: // [ precondition is true for state sl@0: // we can set the volume in any state ] sl@0: sl@0: //[ precondition we have a data sink ] sl@0: if (!iMMFDevSound) sl@0: User::Leave(KErrNotReady); sl@0: sl@0: sl@0: // [ assert the precondition that aVolume is in range ] sl@0: TInt maxVolume = iMMFDevSound->MaxVolume(); sl@0: if( ( aVolume < 0 ) || ( aVolume > maxVolume )) sl@0: User::Leave(KErrArgument); sl@0: sl@0: //[ set the volume on the device ] sl@0: iMMFDevSound->SetVolume(aVolume); sl@0: sl@0: //[ assert the post condition volume is equal to a volume] sl@0: TInt soundVolume = 0; sl@0: soundVolume = iMMFDevSound->Volume(); sl@0: sl@0: __ASSERT_ALWAYS( ( soundVolume == aVolume), Panic(EPostConditionViolation)); sl@0: sl@0: //[ assert the invariant ] sl@0: __ASSERT_ALWAYS( Invariant(), Panic(EBadStateAfterVolumeSet)); sl@0: } sl@0: sl@0: /** sl@0: Gets the maximum level the playback volume can be set to sl@0: sl@0: @internalTechnology sl@0: @param aMaxVolume contains the maximum volume setting sl@0: */ sl@0: void CMMFAudioToneController::MapdGetMaxVolumeL(TInt& aMaxVolume) sl@0: { sl@0: // [ assert the invariant ] sl@0: __ASSERT_ALWAYS( Invariant(), Panic(EBadStateToGetMaxVolume)); sl@0: sl@0: //[ precondition we have a data sink ] sl@0: if (!iMMFDevSound) sl@0: User::Leave(KErrNotReady); sl@0: sl@0: //[ get the volume from the device ] sl@0: aMaxVolume = iMMFDevSound->MaxVolume(); sl@0: sl@0: //[ assert the invariant ] sl@0: __ASSERT_ALWAYS( Invariant(), Panic(EBadStateAfterGetMaxVolume)); sl@0: sl@0: } sl@0: sl@0: sl@0: /** sl@0: Gets the current playback volume sl@0: sl@0: @internalTechnology sl@0: @param aVolume the current volume sl@0: */ sl@0: void CMMFAudioToneController::MapdGetVolumeL(TInt& aVolume) sl@0: { sl@0: // [ assert the invariant ] sl@0: __ASSERT_ALWAYS( Invariant(), Panic(EBadStateToGetVolume)); sl@0: sl@0: //[ precondition that we have a data sink ] sl@0: if (!iMMFDevSound) sl@0: User::Leave(KErrNotReady); sl@0: sl@0: aVolume = iMMFDevSound->Volume(); sl@0: sl@0: // [ assert precondition that the volume is in range sl@0: // 0.. aMaxVolume ] sl@0: TInt aMaxVolume = iMMFDevSound->MaxVolume(); sl@0: __ASSERT_ALWAYS( (aVolume <= aMaxVolume), Panic(EBadState)); sl@0: __ASSERT_ALWAYS( (aVolume >= 0), Panic(EBadState)); sl@0: sl@0: // [ assert the invariant ] sl@0: __ASSERT_ALWAYS( Invariant(), Panic(EBadStateAfterGetVolume)); sl@0: sl@0: } sl@0: sl@0: /** sl@0: Sets the duration over which the volume should increase sl@0: sl@0: @internalTechnology sl@0: @param aRampDuration the time over which the volume should ramp sl@0: */ sl@0: void CMMFAudioToneController::MapdSetVolumeRampL(const TTimeIntervalMicroSeconds& aRampDuration) sl@0: { sl@0: // [ assert the invariant ] sl@0: __ASSERT_ALWAYS( Invariant(), Panic(EBadStateToSetVolumeRamp)); sl@0: sl@0: //[ precondition that we have a data sink ] sl@0: if (!iMMFDevSound) sl@0: User::Leave(KErrNotReady); sl@0: sl@0: iMMFDevSound->SetVolumeRamp(aRampDuration); sl@0: sl@0: //[ assert the invariant ] sl@0: __ASSERT_ALWAYS( Invariant(), Panic(EBadStateAfterSetVolumeRamp)); sl@0: sl@0: } sl@0: sl@0: sl@0: /** sl@0: Sets the balance of the tone playback sl@0: sl@0: @internalTechnology sl@0: @param aBalance the required balance level (KMMFBalanceMaxLeft <= aBalance <= KMMFBalanceMaxRight) sl@0: */ sl@0: void CMMFAudioToneController::MapdSetBalanceL(TInt aBalance) sl@0: { sl@0: //[ assert the invariant ] sl@0: __ASSERT_ALWAYS( Invariant(), Panic( EBadStateToSetBalance)); sl@0: sl@0: // [ precondition is that we have a data sink ] sl@0: if (!iMMFDevSound) sl@0: User::Leave(KErrNotReady); sl@0: sl@0: sl@0: // [ separate out left and right balance ] sl@0: TInt left = 0; sl@0: TInt right = 0; sl@0: CalculateLeftRightBalance( left, right, aBalance ); sl@0: sl@0: //[ set the balance ] sl@0: iMMFDevSound->SetPlayBalanceL(left, right); sl@0: sl@0: // [assert the post condition that the balance is set correctly] sl@0: TInt rightBalance = 0; sl@0: TInt leftBalance = 0; sl@0: iMMFDevSound->GetPlayBalanceL(leftBalance, rightBalance); sl@0: sl@0: //[ assert post condition holds] sl@0: TBool postCondition = (( rightBalance == right) && ( leftBalance == left)); sl@0: __ASSERT_ALWAYS( postCondition, Panic( EPostConditionViolation ) ); sl@0: sl@0: //[ assert the invariant ] sl@0: __ASSERT_ALWAYS( Invariant(), Panic( EBadStateAfterSetBalance)); sl@0: } sl@0: sl@0: /** sl@0: Converts the balance from a range to a left/right form. sl@0: sl@0: Balance is provided to devsound using left and right levels, but supplied to the controller as a range sl@0: (KMMFBalanceMaxLeft --> KMMFBalanceMaxRight). This method converts the range into a left/right form sl@0: sl@0: @internalComponent sl@0: sl@0: @param aLeft set to the left setting sl@0: @param aRight set to the right setting sl@0: @param aBalance the range required sl@0: */ sl@0: void CMMFAudioToneController::CalculateLeftRightBalance( TInt& aLeft, TInt& aRight, TInt aBalance ) const sl@0: { sl@0: // Check the balance is within limits & modify to min or max values if necessary sl@0: if (aBalance < KMMFBalanceMaxLeft) sl@0: aBalance = KMMFBalanceMaxLeft; sl@0: if (aBalance > KMMFBalanceMaxRight) sl@0: aBalance = KMMFBalanceMaxRight; sl@0: sl@0: //[ Now separate percentage balances out from aBalance ] sl@0: aLeft = (100 * (aBalance-KMMFBalanceMaxRight)) / (KMMFBalanceMaxLeft-KMMFBalanceMaxRight); sl@0: aRight = 100 - aLeft; sl@0: sl@0: //[ assert post condition that left and right are within range ] sl@0: __ASSERT_ALWAYS( ( (aLeft <= 100) && (aLeft >= 0) ), Panic(EPostConditionViolation)); sl@0: __ASSERT_ALWAYS( ( (aRight <= 100) && (aRight >= 0) ), Panic(EPostConditionViolation)); sl@0: } sl@0: sl@0: sl@0: /** sl@0: Gets the balance of the tone playback sl@0: sl@0: @internalTechnology sl@0: @param aBalance set to the current balance level (KMMFBalanceMaxLeft <= aBalance <= KMMFBalanceMaxRight) sl@0: */ sl@0: void CMMFAudioToneController::MapdGetBalanceL(TInt& aBalance) sl@0: { sl@0: //[ assert the invariant ] sl@0: __ASSERT_ALWAYS( Invariant(), Panic(EBadStateToGetBalance)); sl@0: sl@0: //[ precondition that we have a sink] sl@0: if (!iMMFDevSound) sl@0: User::Leave(KErrNotReady); sl@0: sl@0: sl@0: TInt left = 50; // arbitrary values sl@0: TInt right = 50; sl@0: iMMFDevSound->GetPlayBalanceL(left, right); sl@0: sl@0: CalculateBalance( aBalance, left, right ); sl@0: sl@0: //[ assert the invariant ] sl@0: __ASSERT_ALWAYS( Invariant(), Panic(EBadStateAfterGetBalance)); sl@0: } sl@0: sl@0: sl@0: /** sl@0: Converts the balance from a left/right form to a range. sl@0: sl@0: Balance is obtained from DevSound using left and right levels, but supplied to the client as a range sl@0: (KMMFBalanceMaxLeft --> KMMFBalanceMaxRight). sl@0: sl@0: @internalComponent sl@0: sl@0: @param aLeft the current left setting sl@0: @param aRight current right setting sl@0: @param aBalance set to the balance range sl@0: */ sl@0: void CMMFAudioToneController::CalculateBalance( TInt& aBalance, TInt aLeft, TInt aRight ) const sl@0: { sl@0: //[ assert pre conditions ] sl@0: __ASSERT_ALWAYS( (( aLeft + aRight ) == 100 ), Panic( EBadArgument )); sl@0: __ASSERT_ALWAYS( (( 0 <= aLeft) && ( 100 >= aLeft)), Panic( EBadArgument) ); sl@0: __ASSERT_ALWAYS( (( 0 <= aRight) && ( 100 >= aRight)), Panic( EBadArgument) ); sl@0: sl@0: aBalance = (aLeft * (KMMFBalanceMaxLeft-KMMFBalanceMaxRight))/100 + KMMFBalanceMaxRight; sl@0: sl@0: //[ assert post condition that aBalance is within limits ] sl@0: __ASSERT_ALWAYS( !(aBalance < KMMFBalanceMaxLeft || aBalance > KMMFBalanceMaxRight), Panic(EBadArgument)); sl@0: sl@0: } sl@0: sl@0: sl@0: sl@0: sl@0: /** sl@0: The function validates a state transition from iState to aState and sl@0: returns ETrue if the transition is allowed. sl@0: sl@0: @internalComponent sl@0: @param TControllerState sl@0: @return Valid controller state or not sl@0: */ sl@0: TBool CMMFAudioToneController::IsValidStateTransition( TControllerState aState ) const sl@0: { sl@0: TBool result = ETrue ; sl@0: //[ assert the precondition that aState is a valid State ] sl@0: __ASSERT_ALWAYS( IsValidState(aState), Panic( EBadArgument ) ); sl@0: //[ assert the invariant that iState is a valid State ] sl@0: __ASSERT_ALWAYS( Invariant(), Panic( EBadStateForTransition )); sl@0: sl@0: // [ check the valid state transitions ] sl@0: // the only invalid transition is sl@0: // stopped to playing sl@0: if( ( iState == EStopped ) && ( aState == EPlaying )) sl@0: { sl@0: result = EFalse ; sl@0: } sl@0: sl@0: //[ assert the invariant that iState is a valid State ] sl@0: __ASSERT_ALWAYS( Invariant(), Panic( EBadStateAfterTransition )); sl@0: sl@0: return result ; sl@0: } sl@0: sl@0: /* sl@0: This function returns whether the invariant is valid sl@0: sl@0: @internalComponent sl@0: @return Is the class in a good condition sl@0: */ sl@0: TBool CMMFAudioToneController::Invariant() const sl@0: { sl@0: //[ The invariant is for now defined sl@0: // as simply being in the correct state sl@0: return IsValidState( iState); sl@0: } sl@0: sl@0: /* sl@0: This function sets the state of the controller. sl@0: sl@0: @internalComponent sl@0: @return Whether ths state transition is valid sl@0: */ sl@0: TBool CMMFAudioToneController::SetState(TControllerState aState) sl@0: { sl@0: TBool result = ETrue; sl@0: //[ assert the precondition that the state is a valid state ] sl@0: __ASSERT_ALWAYS( IsValidState( aState), Panic( EBadArgument ) ); sl@0: //[ assert the invariant the current state is valid ] sl@0: __ASSERT_ALWAYS( Invariant(), Panic( EStateNotValid ) ); sl@0: //[ only allow valid state transitions ] sl@0: if( IsValidStateTransition( aState ) ) sl@0: { sl@0: //[ valid state transition set the state] sl@0: iState = aState ; sl@0: } sl@0: else sl@0: { sl@0: //[ invalid state transition return EFalse ] sl@0: result = EFalse; sl@0: } sl@0: // [ assert the invariant on the state ] sl@0: __ASSERT_ALWAYS( Invariant(), Panic( EBadState )); sl@0: sl@0: return result ; sl@0: } sl@0: sl@0: /* sl@0: checks whether a state is valid sl@0: sl@0: @internalComponent sl@0: @return Is the state a valid one sl@0: */ sl@0: TBool CMMFAudioToneController::IsValidState( TControllerState aState ) const sl@0: { sl@0: TBool result = EFalse; sl@0: if(( aState >= EStopped ) && ( aState <= EPlaying )) sl@0: { sl@0: result = ETrue; sl@0: } sl@0: return result; sl@0: } sl@0: sl@0: /** sl@0: The function State returns the current state of the audio controller sl@0: sl@0: @internalComponent sl@0: @return State of the controller sl@0: */ sl@0: CMMFAudioToneController::TControllerState CMMFAudioToneController::State() const sl@0: { sl@0: __ASSERT_ALWAYS( Invariant(), Panic( EBadState ) ); sl@0: return iState; sl@0: } sl@0: sl@0: sl@0: /** sl@0: * MapcSetRepeatsL sl@0: * @param aRepeatNumberOfTimes sl@0: * @param aTrailingSilence sl@0: * sl@0: */ sl@0: TInt CMMFAudioToneController::MapcSetRepeats(TInt aRepeatNumberOfTimes, const TTimeIntervalMicroSeconds& aTrailingSilence) sl@0: { sl@0: TInt err = KErrNone; sl@0: if (!iMMFDevSound) sl@0: { sl@0: return KErrNotReady; sl@0: } sl@0: else sl@0: { sl@0: if(iMMFDevSound->QueryIgnoresUnderflow()) sl@0: { sl@0: iMMFDevSound->SetToneRepeats(aRepeatNumberOfTimes, aTrailingSilence); sl@0: } sl@0: else sl@0: { sl@0: err = KErrNotSupported; sl@0: } sl@0: } sl@0: return err; sl@0: }