Saturday, September 27, 2008

Auto Call Answering and playing prerecorded voice when call is active

------------------------Answering Machine in S60------------------------------------


This article is for sending prerecorded voice over UPLINK when call is active.
1. Whenever an incoming call is coming this application will automatically answers the call.
2. Then it will play a prerecorded voice over uplink.
3. Once play completes it will automatically terminates the call.


Class that need for achieving this
1. CTelephony

---->This class is used for getting notification change, whenever there is a change happens in voice line. It’s not only for getting the notification and also dialing a new call, answering an incoming call, holding, & resuming call etc… But there are some problem using CTelephony classes. Most of the functions in CTelephony calls are asynchronies functions so we cannot use NotifyChange function and other CTelephony function in same Active objects because one active object cannot serve two asynchronies function at the same time. We will discuss about this later. For the time being just understand the purpose of CTelephony class.


2. CMdaAudioPlayerUtility
---->This class is used for playing MP3, WAV file. There are some tricks we need to use here for sending voice over UPLINK.

3. MMdaAudioPlayerCallback
---->This M class is used by CMdaAudioPlayerUtility for call back. What is call back here? CMdaAudioPlayerUtility::Play() this function is used for playing a sounds. Once this function completes playing, it will call MapcPlayComplete function present in MMdaAudioPlayerCallback.

CallHandling.h


#ifndef __CALLHANDLING_H__
#define __CALLHANDLING_H__

#include "e32base.h"
#include "Etel3rdParty.h"
#include "w32std.h"
#include "mdaAudiosampleplayer.h"

class CTelCallHandling;
class CCallListener: public CActive
{
public:
CCallListener();
static CCallListener* NewL();
static CCallListener* NewLC();
~CCallListener();
void StartListening();

private:
void ConstructL();
void RunL();
TInt RunError(TInt anError);
void DoCancel();

private:
CTelephony* iTelephony;
RWsSession wsSession;
CTelephony::TCallStatusV1 iCallStatus;
CTelephony::TCallStatusV1Pckg iCallStatusPkg;
CTelephony::TCallId icallId;
CTelCallHandling* iCallHandlingObj;

};

class CTelCallHandling : public CActive, public MMdaAudioPlayerCallback
{
public:
CTelCallHandling();
static CTelCallHandling* NewL();
static CTelCallHandling* NewLC();
~CTelCallHandling();
void StartListening();
void CallHandling(TInt aCallStatus);
enum TAudioPlayerState
{
EPlayerNotReady=0,
EPlayerReadyToPlay,
EPlayerPlaying
};
CMdaAudioPlayerUtility * iMdaAudioPlayerUtility;
private:
void ConstructL();
void RunL();
void DoCancel();

private:
TAudioPlayerState iPlayerState;
TInt iCallStatus;
CTelephony::TCallId icallId;
CTelephony* iTelephony;


void MapcInitComplete(TInt aError, const TTimeIntervalMicroSeconds &aDuration);
void MapcPlayComplete(TInt aError);
void PlayL();

};
#endif

CallHandling.cpp

#include "e32base.h"
#include "Etel3rdParty.h"
#include "e32debug.h"
#include "TerminateCall.h"


CCallListener::CCallListener() :CActive(EPriorityIdle),iCallStatusPkg(iCallStatus)
{
}


CCallListener* CCallListener::NewL()
{
CTelObserver* self = CTelObserver::NewLC();
CleanupStack::Pop(self);
return self;

}


CCallListener* CCallListener::NewLC()
{
CCallListener* self = new (ELeave) CCallListener();
CleanupStack::PushL(self);
self->ConstructL();
return self;

}


void CCallListener::ConstructL()
{
iTelephony = CTelephony::NewL();
TInt Err = wsSession.Connect();
if (Err != KErrNone)
RDebug::Printf("Window server session connect error\n");
iCallHandlingObj = CTelCallHandling::NewL();
CActiveScheduler::Add(this);

}


CCallListener::~CTelObserver()
{
RDebug::Printf("STA CTel Observer\n");
Cancel();
wsSession.Close();
if (iTelephony)
delete iTelephony;

}


void CCallListener::RunL()
{
CTelephony::TCallStatus status = iCallStatus.iStatus;
switch (status)
{
case CTelephony::EStatusRinging:
iCallHandlingObj->CallHandling(status);
StartListening();

break;
case CTelephony::EStatusAnswering:
iCallHandlingObj->CallHandling(status);
StartListening();

break;
case CTelephony::EStatusConnected:
iCallHandlingObj->CallHandling(status);
StartListening();

break;
case CTelephony::EStatusDisconnecting:
iCallHandlingObj->CallHandling(status);
StartListening();
break;
default:
StartListening();
break;
}
}


TInt CCallListener::RunError(TInt anError)
{
return anError;
}


void CCallListener::DoCancel()
{
iTelephony->CancelAsync(CTelephony::EVoiceLineStatusChangeCancel);
}


void CCallListener::StartListening()
{
iTelephony->NotifyChange(iStatus, CTelephony::EVoiceLineStatusChange, iCallStatusPkg);
SetActive();

}

CallHandling.cpp

//call handling class
CTelCallHandling::CTelCallHandling():CActive(EPriorityIdle)
{
}


CTelCallHandling* CTelCallHandling::NewL()
{
CTelCallHandling* self = CTelCallHandling::NewLC();
CleanupStack::Pop(self);
return self;

}


CTelCallHandling* CTelCallHandling::NewLC()
{
CTelCallHandling* self = new (ELeave) CTelCallHandling();
CleanupStack::PushL(self);
self->ConstructL();
return self;

}


void CTelCallHandling::ConstructL()
{
iTelephony = CTelephony::NewL();
CActiveScheduler::Add(this);

}


void CTelCallHandling::CallHandling(TInt aCallStatus)
{
iCallStatus = aCallStatus;
switch(aCallStatus)
{
case CTelephony::EStatusRinging:
RDebug::Printf("CTelCallHandling::CallHandling --- EStatusRinging\n");
iTelephony->AnswerIncomingCall(iStatus, icallId);
SetActive();

break;
case CTelephony::EStatusAnswering:
User::After(5000000);
_LIT(KSong, "z:\\Data\\Sounds\\Tones\\tune.mp3");
TBufC<128> buffer(KSong);
iMdaAudioPlayerUtility=CMdaAudioPlayerUtility::NewFilePlayerL(buffer, *this);
if (iMdaAudioPlayerUtility->SetPriority(80,(TMdaPriorityPreference)0x00060001) == KErrNone)
{
RDebug::Printf("STA SetPriority Success-------------...!!!!!!\n");
}
else
RDebug::Printf("STA setpriority failure.....!!!!!!\n");
break;
case CTelephony::EStatusConnected:
RunL();
break;
case CTelephony::EStatusDisconnecting:
RunL();
break;
}
}


void CTelCallHandling::MapcInitComplete(TInt aError, const TTimeIntervalMicroSeconds &aDuration)
{
RDebug::Printf("CTelCallHandling::MapcInitComplete Error code %d = \n", aError);
iPlayerState = aError ? EPlayerNotReady : EPlayerReadyToPlay;

if (aError==KErrNone)
{
RDebug::Printf("PlayL()\n");
PlayL();

}
else
RDebug::Printf("Error playing music\n");
}


void CTelCallHandling::MapcPlayComplete(TInt aError)
{
iPlayerState = aError ? EPlayerNotReady : EPlayerReadyToPlay;
User::After(6000000);
iTelephony->Hangup(iStatus, icallId);
SetActive();

}


void CTelCallHandling::PlayL()
{
if (iPlayerState==EPlayerReadyToPlay)
{
RDebug::Printf("STA playing....\n");
iMdaAudioPlayerUtility ->Play();
iPlayerState=EPlayerPlaying;
}
}


void CTelCallHandling::RunL()
{
switch(iCallStatus)
{
case CTelephony::EStatusRinging:
RDebug::Printf("Call handling RunL()-- EStatusRinging-- %d\n",CTelephony::EStatusRinging);
break;
case CTelephony::EStatusConnected:
RDebug::Printf("Call handling RunL()-- EStatusConnected--%d\n",CTelephony::EStatusConnected);
break;
case CTelephony::EStatusAnswering:
RDebug::Printf("Call handling RunL()-- EStatusAnswering-- %d\n",CTelephony::EStatusAnswering);
break;
case CTelephony::EStatusConnecting:
RDebug::Printf("Call handling RunL()-- EStatusConnecting-- %d\n", CTelephony::EStatusConnecting);
break;
case CTelephony::EStatusDisconnecting:
RDebug::Printf("Call handling RunL()-- EStatusDisconnecting-- %d\n",CTelephony::EStatusDisconnecting);
break;
default:
RDebug::Printf("Call Handling RunL default\n");
break;
}
}


void CTelCallHandling::DoCancel()
{
}


CTelCallHandling::~CTelCallHandling()
{
Cancel();
}

6 comments:

Anonymous said...

where is terminatecall.h library

Stalin said...

We can remove that header its not needed anymore.

Anonymous said...

Hello Sir
please help me.
I am new in Symbian c. I want to make answering machine with some enhancement.your code is very good but please explain how many classes i make in my project because you give two classes with same name..So i will contact with you for further details.i am waiting for your answer.thanx.

Stalin said...

CTelCallHandling
CCallListener

i written this two years back. if i find the code i will share with you.

Anonymous said...

above code is not able to uplink audio

Stalin said...

I tested this when i was working on symbian in Samsung along with my friend playing an tamil song in the background. I tested on S60 3.2. if its not working also no issues, try to get working code somewhere. no other go :-).