Using Sound with Symbian

Part II: The Code

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:

#include <MdaAudioTonePlayer.h>

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:

// Frequency and duration of the tone to be played
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 NumberNameDescription
0Not ReadyThe program attempted to call one of the tone player's methods before it was ready to process the attempt.
1Busy PreparingThe 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.
2Busy PlayingThe 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.
3Invalid Sequence NumberA number referring to a non-existent sequence was passed to one of the sequence playing methods.
4Volume Out Of RangeA call to SetVolume() was made with a value greater than the permitted maximum volume (or a negative number).
5Invalid DurationOne of the methods which required a duration (e.g. SetRepeats() ) was passed a negative or otherwise out-of-range value.
6Invalid Repeat CountSetRepeats() 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.

Back to Part I


> Using Sound with Symbian

i have tried the code by implementing example code from the SDK. i have succeded testing in emulator. but when I put it in the real device, i received system error. why is that? For your information, I implement the player not the tone player. by the way, friend of mine has tried the code to play midi file. it couldnt play in emulator, but worked in sony ericson P800 (UIQ SDK) device. Can I do the samething with Nokia 7650 (Symbian 60 SDK) device?

> 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:

-  Install the "sound" example out of the series60Ex folder and start it on the phone (not the emulator)
-  Check, if the Utility "Tone" is selected
-  Play the tone (very ugly sound by the way)
-  Stop the Playing, before the tone runs out by selecting "stop" from the context menu
-  Play the tone again, this time in its whole length
-  Try to play it again, but this time the context menu still shows you "stop"/"exit"

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

Hi Zeep, I am currently using this audio player to SetRepeats to my sound file. But I need to implement 2 sounds looping one after another. Is that any methods that can allow me to do so?

> 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