symba (779893), страница 27
Текст из файла (страница 27)
Ifthe file is successfully opened, just call Play() and the clip is played,eventually leading to a MapcPlayComplete() callback:void CPlayAudio::MapcPlayComplete(TInt aError){// play has completediObserver->PlayComplete(aError);}You can normally just pass this on. Success is indicated via KErrUnderflow, which perhaps you’d want to change to KErrNone.5.3.2 VariationsBased on these examples, there are quite a few variations you canimplement:• Display the duration of the clip and track progress.
The best way toget the duration is by the following call:TMMFDurationInfo Duration(TTimeIntervalMicroSeconds& aDuration);This is better than the alternatives as it also returns a TMMFDurationInfo value that can indicate if the duration is ‘valid’ (so thereturned aDuration6 is important), is unknown for some reason oris ‘infinite’ – the latter makes more sense for URL clips and relates toreal-time streamed audio, or similar, where there is no real conceptof duration.Position is returned by:TInt GetPosition(TTimeIntervalMicroSeconds& aPosition);This returns the current playing position, at least to a good approximation. Call on a timer, say every 100 ms, to show progress – every50 ms, if you want really smooth progression.• Stop before getting to the end. Use CMdaAudioPlayerUtility::Stop() to stop playing and no further callbacks are generated untilyou request something else.6 If your file has a variable bit rate then the returned duration is calculated and may notbe 100% accurate.120MULTIMEDIA FRAMEWORK: AUDIO• Support end users in pause–play–pause scenarios.
For pause, useCMdaAudioPlayerUtility::Pause() and, to resume, CMdaAudioPlayerUtility::Play(). We tend to track the state andnot call Pause() when it is already paused or Play() when playing – it is not hard to do, just maintain a flag.• Set the play position. Call CMdaAudioPlayerUtility::SetPosition()7 with the appropriate position given in microseconds.Basically, this is the same as seeking a file – new data is read from thestated position.
This is a ‘goto’ operation and is commonly used forthe equivalents of dragging the position marker on a control.Note that SetPosition() should jump to the new positionimmediately, but if you listen very carefully you might hear a smalldelay in switching to the new position – it depends on the internalimplementation. Also note that although positions are expressed inmicroseconds, the actual positioning will never be that accurate.
Howaccurate it is depends on the encoding method – many compressionschemes break the sound stream into frames of a specific length, eachof which represents many samples of data, and you can often only‘seek’ to a frame boundary.• Play a specific window. The scenario is quite simple – your application wants to start playing a bit after the start and stop before theend is reached. For example, in a 10-second clip, you might playseconds 3 to 6. This typically relates to the user moving some form ofslider to show where play should start and another showing where itfinishes.
You can do this yourself, using SetPosition() and pollingPosition(), but there is explicit support using:TInt SetPlayWindow(const TTimeIntervalMicroSeconds& aStart,const TTimeIntervalMicroSeconds& aEnd)This basically works as expected – if the position is less thanaStart, it is set to aStart; if it is greater than aEnd, it is setto aEnd. Otherwise it does not change position.
The clip stops whenyou reach the end of the window. Be careful of the fact that theposition is not always moved and then not always to the beginningYou may set the play window and then wonder why nothing happenswhen you play the clip; you may actually be at the end. Also notethat SetPosition() calls are always relative to the start of the clipand not to the play window.7For safety it is better to call Pause(), SetPosition() and then Play() as somecontrollers do not correctly support a SetPosition() call during playback.AUDIO PLAYER UTILITY121You can change the play window or clear it using:TInt CMdaAudioPlayerUtility::ClearPlayWindow();• Repeat playing the clip.
You can do this yourself but not as cleanly orefficiently as MMF.void CMdaAudioPlayerUtility::SetRepeats(TInt aRepeatNumberOfTimes,const TTimeIntervalMicroSeconds& aTrailingSilence)The parameter aRepeatNumberOfTimes requests a number ofrepeats – so 0 means play once, the default behavior. A specialvalue, KMdaRepeatForever, can be used to request indefiniterepeats – you have to use Stop() to end it. Otherwise use an explicitvalue.
The gap between two iterations can be set with aTrailingSilence – as with most timings on Symbian OS, it is approximate,so don’t over-rely on its accuracy. This is good for background music,but take care – some clips are more appropriate than others.• Play an RFile object. It is common in Symbian OS now to be givenan open RFile object and to have to use that. There is an alternativeCMdaAudioPlayerUtility overload for this:void OpenFileL(const RFile& aFile);This takes RFile instead of the filename, but otherwise thesequence is the same.It is also possible to use TMMSource,8 as in:TMMFileSource filesource(aFileName, KDefaultContentObject, EPlay);iUtility->OpenFileL(filesource);This uses a slightly different overload. This code is intended for usewith the Symbian OS DRM subsystem – if you have sufficient rights itdecrypts the file and plays it.
This is possibly outside your use cases,but there are specialist uses for the subsystem other than standardDRM ones.8 SeeSection 2.3 for more on TMMSource and DRM support.122MULTIMEDIA FRAMEWORK: AUDIO• Set the balance. For stereo output (such as through headphones), youcan set the balance anywhere between KMMFBalanceMaxLeft andKMMFBalanceMaxRight using CMdaAudioPlayerUtility::SetBalanceL().
Most applications will want to stick with thedefault value KMMFBalanceCenter. This setting is also available forall of the other client and stream utilities that produce audio output.• Set the volume. You can set the volume anywhere between 0 and thevalue returned by CMdaAudioPlayerUtility::MaxVolume()using CMdaAudioPlayerUtility::SetVolume(). The actualmaximum volume value varies between platforms and devices.
Again,this functionality is available for all of the audio output APIs. SeeSection 8.13 for more on volume control.5.4 Audio Recorder UtilitySince CMdaAudioPlayerUtility is used for playing, you’d assumethat CMdaAudioRecorderUtility was used for recording, and ingeneral terms you’d be right. However, things are a bit more complexthan that. For historical reasons, the utility was deliberated designedto cover the ‘voice recorder’ use case – where an end user recordssomething, possibly listens to it, records some more, etc. Hence, despiteits name, this API both records and plays.An additional complication is that CMdaAudioRecorderUtilityforms a slightly different API pattern to CMdaAudioPlayerUtility – rather than specific callbacks to state that something has finished, asingle callback function is called multiple times showing progress.
Whichyou prefer is probably a matter of choice. CMdaAudioRecorderUtility potentially gives more progress info, but the callbacks have to beimplemented with care.The fact that CMdaAudioRecorderUtility also plays means thatsome programmers have used it almost exclusively – preferring it toCMdaAudioPlayerUtility. We’d advise against that – CMdaAudioPlayerUtility has more features and is generally more efficient.CMdaAudioRecorderUtility requires an observer to be supplied on creation of type MMdaObjectStateChangeObserver. Thisis defined as follows:class MMdaObjectStateChangeObserver{public:virtual void MoscoStateChangeEvent(CBase* aObject, TInt aPreviousState,TInt aCurrentState, TInt aErrorCode) = 0;};AUDIO RECORDER UTILITY123The MoscoStateChangeEvent() call is key to the client – it effectively forms a central state machine.
You are given both previous andcurrent (latest) states – at least the states of the underlying recorder utility. You don’t normally have to maintain your own states in the client,which simplifies things. The states returned are defined in CMdaAudioClipUtility::TState:enum TState{ENotReady = 0,EOpen,EPlaying,ERecording};Remember that you normally have to prefix these with CMdaAudioClipUtility:: in the source. You must say CMdaAudioClipUtility::ENotReady or CMdaAudioRecorderUtility::ENotReady,depending on the level you are dealing with.5.4.1 Recording to a Named FileWe are going to look at code that does something similar to the audioinput–output stream example; it records something and then plays itback.
For this API, we record into a file and the file remains afterwards.Skipping the declaration, the construction is fairly predictable. The keyConstructL() is:void CRecordAudio::ConstructL(){iUtility = CMdaAudioRecorderUtility::NewL(*this);}To start, the client calls RecordL(), which is implemented as:void CRecordAudio::RecordL(const TDesC& aFileName){iUtility->OpenFileL(aFileName);}This requests the opening of a file. We’ll return later to some of thesubtleties of this call. The key is that it is asynchronous and leads to asubsequent MoscoStateChangeEvent callback. The implementationof this callback becomes the heart of the client code:124MULTIMEDIA FRAMEWORK: AUDIOvoid CRecordAudio::MoscoStateChangeEvent(CBase* aObject,TInt aPreviousState, TInt aCurrentState, TInt aErrorCode){aObject = aObject; // to stop warningTInt error = aErrorCode;if (error == KErrNone){// only interested in some state transitions ...if (aPreviousState == CMdaAudioRecorderUtility::ENotReady &&aCurrentState == CMdaAudioRecorderUtility::EOpen){// have opened the fileTRAP(error, OnOpenL());}else if (aPreviousState == CMdaAudioRecorderUtility::EPlaying &&aCurrentState==CMdaAudioRecorderUtility::EOpen){// normal termination of playiObserver->PlayOrRecordComplete(KErrNone);}}if (error != KErrNone){// stop nowiUtility->Stop();iObserver->PlayOrRecordComplete(aErrorCode);}}This handles two sets of scenarios: what to do on errors, includingones it generates and some key use cases.