Using Sound with Symbian
The Final Code
This example code (based on the SDK's Sound example) is for an adapter class which takes frequency and duration as parameters when it is first created. It is only ever capable of playing the note it was assigned at creation, but it can do so safely as many times as you wish to call its public PlayL() method. If PlayL() is called when the player is not ready, the note will simply not play.
It is left as an exercise for the reader to add an extra PrepareL() method to allow this note to be changed, with appropriate checks to make sure no calls are made at inappropriate times.
In the header file:
class CToneAdapter : public CBase, public MMdaAudioToneObserver {
public:
// Standard two-phase construction
static CToneAdapter* NewL(TInt aFrequency, const TTimeIntervalMicroSeconds& aDuration);
static CToneAdapter* NewLC(TInt aFrequency, const TTimeIntervalMicroSeconds& aDuration);
~CToneAdapter();
void PlayL(); // Call this from somewhere else in your code to play the current note
void StopL(); // Call this from somewhere else in your code to stop the current note prematurely
public: // inherited from MMdaAudioToneObserver
void MatoPrepareComplete(TInt aError);
void MatoPlayComplete(TInt aError);
private:
CToneAdapter(TInt aFrequency, const TTimeIntervalMicroSeconds& aDuration);
void ConstructL();
TBool iFreeToPrepare;
TBool iFreeToPlay;
TInt iFrequency;
TTimeIntervalMicroSeconds iDuration;
CMdaAudioToneUtility* iMdaAudioToneUtility;
};
In the main .cpp file:
CToneAdapter::CToneAdapter(TInt aFrequency, const TTimeIntervalMicroSeconds& aDuration)
: iFrequency(aFrequency), iDuration(aDuration)
{
}
CToneAdapter* CToneAdapter::NewL(TInt aFrequency, const TTimeIntervalMicroSeconds& aDuration)
{
CToneAdapter* self = NewLC(aFrequency, aDuration);
CleanupStack::Pop(); // self
return self;
}
CToneAdapter* CToneAdapter::NewLC(TInt aFrequency, const TTimeIntervalMicroSeconds& aDuration)
{
CToneAdapter* self = new(ELeave)CToneAdapter(aFrequency, aDuration);
CleanupStack::PushL(self);
self->ConstructL();
return self;
}
void CToneAdapter::ConstructL()
{
iMdaAudioToneUtility = CMdaAudioToneUtility::NewL(*this);
// Configure the audio tone utility to play a single tone
iMdaAudioToneUtility->PrepareToPlayTone(iFrequency, TTimeIntervalMicroSeconds(iDuration));
}
CToneAdapter::~CToneAdapter()
{
delete iMdaAudioToneUtility;
iMdaAudioToneUtility = NULL;
}
// Note that this implementation of the virtual function does not leave.
void CToneAdapter::PlayL()
{
if(iFreeToPlay) {
iFreeToPlay = FALSE;
iMdaAudioToneUtility->Play();
}
}
// Note that this implementation of the virtual function does not leave.
void CToneAdapter::StopL()
{
iMdaAudioToneUtility->CancelPlay();
}
// from MMdaAudioToneObserver
void CToneAdapter::MatoPrepareComplete(TInt /*aError*/)
{
iFreeToPlay = TRUE;
}
// from MMdaAudioToneObserver
void CToneAdapter::MatoPlayComplete(TInt /*aError*/)
{
iFreeToPlay = TRUE;
}
This code is invoked from your program as described in the int main(void) lines in Part I.
Appendix: Panic Codes
EMdaTonePlayer panics are as follows:
| Panic Number | Name | Description |
|---|---|---|
| 0 | Not Ready | The program attempted to call one of the tone player's methods before it was ready to process the attempt. |
| 1 | Busy Preparing | The program attempted to call one of the tone player's methods (typically Play() ) whilst the tone player was still in the middle of performing a PrepareToPlayTone() action. |
| 2 | Busy Playing | The program attempted to call one of the tone player's methods (typically Play() or Prepare() ) whilst the tone player was still in the middle of playing a tone. |
| 3 | Invalid Sequence Number | A number referring to a non-existent sequence was passed to one of the sequence playing methods. |
| 4 | Volume Out Of Range | A call to SetVolume() was made with a value greater than the permitted maximum volume (or a negative number). |
| 5 | Invalid Duration | One of the methods which required a duration (e.g. SetRepeats() ) was passed a negative or otherwise out-of-range value. |
| 6 | Invalid Repeat Count | SetRepeats() was called with a negative number other than KMdaRepeatForever. |
Glossary
Panic: [verb] The system's act of aborting a program because it has encountered a runtime error. [noun] An error code displayed by the system when a panic has occurred.
Callback: A method (function) written by the user, which is called by the system. An example from this tutorial is MatoPlayComplete(), which is called automatically when a sound has finished playing, and which the user must define.
Asynchronous Function (/Object/Operation): In the context of this tutorial, an "asynchronous operation" refers to the fact that a sound needs to continue playing whilst the rest of the function it is triggered from is executed. When we call Play(), we don't want the whole program to stop dead until the sound has stopped playing. We might, however, need to know when it has stopped - and so we define a callback method which the sound player calls at its discretion.






> Using Sound with Symbian
> Using Sound with Symbian
Thanks for that example, it helps me understanding the tone utility.
But you can improve (or correct) this example:
1. Why didn't you use the Status function of the ToneObject ? You don't need the iFreeToPlay and iFreeToPrepare members to check the state.
2. I've found a bug in that utility:After you called the cancelPlay function for one tone, the next tone you play never evokes the MatoPlayComplete function. Try this, with the sound example in the series60ex folder. Maybe this only happens to 7650s. I don't know.
You have to recreate the UtilityObject after called the cancelPlay function. Add this in StopL an it works fine:
// reset the tone utility by recreating it
delete iMdaAudioToneUtility; iMdaAudioToneUtility = CMdaAudioToneUtility::NewL(*this); iMdaAudioToneUtility->PrepareToPlayTone(iFrequency, TTimeIntervalMicroSeconds(iDuration));
> Using Sound with Symbian
In order: 1. You're right, in a real-world program it generally would be better to use State(). However, I'm hoping that my (bad) use of iFreeToPlay and iFreeToPrepare will have served as a guide to understanding the state-based system.
2. Interesting. The docs do note that MatoPlayComplete isn't called for the tone that's been cancelled - are _all_ subsequent tones defective, or just the one immediately following it? Do Nokia know about it?
> Using Sound with Symbian
to 1) Of course, this helps understanding the states of the utility. But during my tests, this states get a little bit "messed up" (my fault). Thats the problem with your own states.
to 2) It's the following tone. Try this:
Bingo. This is the bug. Now you can select "stop" (that calls cancelPlay) and go on playing. But thats still a bug.
Its impossible to loop a sound (for example an alarm) and stop it immediatly (the user reacts to the alarm) and start it later again with the same ToneObject. You have to create a new ToneObject every time you cancel the Playing.
I've posted this in the nokia-forum and in the newsgroup (i've copied the way to reproduce this bug from my news entry, by the way). No response so far...
> Using Sound with Symbian
> Using Sound with Symbian
hi if u've got the solution so please assist me as i'm facing same problem.
Xymbian
> Using Sound with Symbian
Thanks for providing such a conspicuous example.
Can I turn the keypad beeps off, whenevr i want. Whenever a user will press a key (lets menu Key), there should be no beep in response to that.
Is it possible without switching to Silent mode.
Aficianado