symba (779893), страница 26
Текст из файла (страница 26)
This is left as an exercise for youto do yourself. . .Playing is often simpler than recording for a key reason: if you giveCMdaAudioOutputStream a large buffer, it plays until completion,rather than you observing it as short buffer copies. However, opening andclosing the audio output object is virtually the same as for the audio inputobject. The CMdaAudioOutputStream::Open() call is made, usingdefault parameters, and there is a subsequent MaoscOpenComplete()callback generated.void CIOStreamAudio::MaoscOpenComplete(TInt aError){ASSERT(iState == EStateOpeningOutput);TInt error = aError;AUDIO INPUT AND OUTPUT STREAMS115if (error == KErrNone){iState = EStateWriting;iOutputStream->SetVolume((iOutputStream->MaxVolume() + 1) / 2);TRAP(error, iOutputStream->WriteL(iMainBuffer));}if (error != KErrNone){Complete(aError);}}This version sets the volume and plays the data.
(MaxVolume()+1)/2is a good default volume but, arguably, this should have been supplied bythe application.) Note there is no ‘global volume’ concept – the volumefor each instance is set independently and then the adaptation worksthings out.Playing the data is very simple: CMdaAudioOutputStream::WriteL() is passed the data you have recorded in iMainBuffer.This asynchronously plays the data to completion, so that you get a singlecallback at the end, MaoscBufferCopied():void CIOStreamAudio::MaoscBufferCopied(TInt aError,const TDesC8& IFDEBUG(aBuffer)){ASSERT(iState == EStateWriting);if (aError != KErrNone){// ignore any KErrAbort returns - this would happen during a Stop()// call if we were playingif (aError != KErrAbort){Stop();Complete(aError);}}else{ASSERT(aBuffer.Length() == iMainBuffer.Length());// output almost complete - have been asked for more dataiState = EStateWritingPostBuffer;}}The normal behavior is for this to be called with KErrNone and thebuffer name – in a similar way to CMdaAudioInputStream.
KErrAbort occurs during Stop() scenarios and usually needs to be deliberately ignored. Otherwise, this callback implementation does not actuallydo anything in this instance.Following this call, because there is no other data, you should geta MaoscPlayComplete() call with KErrUnderflow – for historical116MULTIMEDIA FRAMEWORK: AUDIOreasons, successful plays are usually indicated with the KErrUnderflowresult:void CIOStreamAudio::MaoscPlayComplete(TInt aError){ASSERT(iState == EStateWriting || iState == EStateWritingPostBuffer);TInt error = aError;if (aError == KErrUnderflow && iState == EStateWritingPostBuffer){error = KErrNone; // normal termination is underflow following a// buffer request}Complete(error);}Changing KErrUnderflow to KErrNone, as shown here, is usuallythe best thing to do – so it looks like your engine completes with KErrNone.
Note that other errors are possible and again indicate run-timeerrors or throw-off. You should pass them back to the client for handling (see Section 5.8 for a discussion of how the client should handlesuch errors).5.2.4 VariationsBased on these examples, there are quite a few variations you canimplement:• Run the audio input and output streams concurrently. This is fairlyeasy to see how to do: just use different buffers.• Process the input, instead of just saving it – run the input through somefunction.
As mentioned, you can have a couple of input buffers thatare used alternately and can process the data of the read buffer afteryou’ve called ReadL() on the other function – do it that way aroundto read the new data concurrently with processing the previous data.• Use formats other than PCM16. Both CMdaAudioInputStreamand CMdaAudioOutputStream have a call, SetDataTypeL(),which is called prior to Open(). These take a parameter, TFourCc,which is nominally a 32-bit value but which is constructed from fourcharacters and is (semi-)readable.
The standard ones are defined inmmffourcc.h.For example, PCM16 is represented by the named literal KMMFFourCCCodePCM16, which has the associated value " P16", whileeight-bit unsigned PCM is represented by the literal KMMFFourCCCodePCMU8 with value " PU8". You are encouraged to use thenamed literals but, as usual, it is the value that matters.
If you dooverride it, you should normally use the same the format for bothinput and output, unless you want some strange effects!AUDIO PLAYER UTILITY117• Use non-default sample rate and mono–stereo settings. They are setusing calls to SetAudioPropertiesL(), which is called in thecallback after the Open() call – either MaiscOpenComplete() orMaoscOpenComplete().4 In practice, only some sample rates aresupported. We suggest you use the standard set of 8000, 16000,11025 or 22050.
Others are supported, including CD quality 44100,but they start to have more ‘real-time’ issues. Not all sample rates aresupported by all format types.• Use continuously. Rather than recording into a fixed-length buffer,extend the buffer as you progress – possibly using RBuf8 and associated ReAlloc() and Append() calls.There are many other calls, including those to set the bit rate and give‘audio priorities’, which you do not need to use most of the time andpossibly never.5.3 Audio Player UtilityIf CMdaAudioOutputStream is about playing blocks of encoded audiodata, CMdaAudioPlayerUtility is primarily about playing files.
Insome ways, that is misleading: it can play descriptors and content givenas a URL.5 Nevertheless, its primary use case is the playing of files and itessentially treats these other sources as if they were files.The difference from streams is fairly straightforward – audio files areusually formatted in a structure that contains ‘headers’ and other datastructures, in addition to the audio data itself. With CMdaAudioInputStream, parameters such as the format to use and the sample rate mustbe supplied by additional calls, but this information is usually obtainedfrom an audio file and thus CMdaAudioPlayerUtility is able to playvirtually all files without additional information – the exceptions are veryspecial cases and can usually be ignored.There are actually more subtle differences: when playing from a file,there is the concept of a ‘position’ at which it is currently playing.
Theplayer utility supports calls to find out the current position and the overalllength and to set the current position. These enable the implementationof a progress dialog or a conceptual drag and go to a position interface.The API also supports other related concepts such as repeat play and‘play window’.In its basic form, CMdaAudioPlayerUtility is easier to use thanCMdaAudioOutputStream – if you want to play a file, then it is quite4 In case you are wondering, the Open() operation selects the lower-level adaptationobjects and SetAudioPropertiesL() is viewed as passing parameters to them.5 Assuming they are supported by the controller plug-in being used.
Many audio controllerplug-ins don’t support playback from a URL.118MULTIMEDIA FRAMEWORK: AUDIOsimple to use. Looking at the source or the documentation, you mightnot see it that way but a lot of the API calls are redundant – Symbiantends to keep on supporting old calls where possible, so as APIs getolder there are often calls supported for existing software that you’d notuse for new code. CMdaAudioPlayerUtility is an old API – it hasbeen supported on almost all versions of Symbian OS – and now containsmany such calls. If you strip it down, it is a fairly simple API.5.3.1 Playing a Named FileThis example plays a file given a filename – the simplest thing todo, although not always the most flexible approach (as we’ll see inSection 5.3.2). You can also reference a file via an RFile object or aTMMSource.
The basic construction and destruction code follows thesame pattern as that in the audio stream calls in Section 5.2. The primarydifference is the call in ConstructL():void CPlayAudio::ConstructL(){iUtility = CMdaAudioPlayerUtility::NewL(*this);}The basic operation is quite simple – the utility is opened, you wait fora callback to confirm opening, request play and are then told that playis complete. Going through that in more detail, the first command is thePlayL() call provided in this example for use by the client application:void CPlayAudio::PlayL(const TDesC& aFileName){iUtility->OpenFileL(aFileName);}This opens the filename passed and (unless it leaves) leads to aMapcInitComplete() callback:void CPlayAudio::MapcInitComplete(TInt aError,const TTimeIntervalMicroSeconds& /*aDuration*/){if (aError != KErrNone){iObserver->PlayComplete(aError);}else{iUtility->Play();}}AUDIO PLAYER UTILITY119aDuration is the duration of the clip; it is often ignored, as here.The observer mentioned here is a local observer interface back to theclient application, but the basic point holds true – if an error is returnedthe cycle is stopped and you just notify the client or user appropriately.