symba (779893), страница 28
Текст из файла (страница 28)
When an error occurs, wemerely call Stop() on the utility and respond to the conceptual mainapplication with a callback. The Stop() is usually not required – if theclient told you it had an error, it should have stopped anyway, but it’ssafer always to call it to cover other failure cases.Returning to positive situations, if you traced through the code with adebugger or added logging, you’d find that this call is made several times.It turns out you generally only need to trace some of the transitions – inthis case we need to know when we’ve opened the file and when playcompletes.
Other KErrNone transitions can be ignored. For example,we need do nothing on the transition from EOpen to ERecording, sowe just ignore it. You may want to add code for other scenarios – or,perhaps, remove it.The transition between ENotReady and EOpen follow on from anOpenFileL() call on the utility. The structure here is to call the OnOpenL() method:void CRecordAudio::OnOpenL(){// let's record to ADPCMAUDIO RECORDER UTILITY125iUtility->SetDestinationDataTypeL(KMMFFourCCCodeIMAD);iUtility->RecordL();}This is quite simple, it sets the data type and then starts recording – asyou might guess RecordL() is the request to record itself.
If you wantedto set other parameters, such as sample rate to use, then this is donebefore RecordL().This particular setting requests (IMA) ADPCM encoding. If you arevery observant, you may be wondering how we control the file type – thisAPI is typically used to write to file types such as WAV and we’ve noteven mentioned that! We are exploiting a feature of the API, wherethe file type to use is taken from the suffix of the filename given tothe OpenFileL() statement. It turns out that this is the easiest way ofarranging for a particular file type and is usually the most convenient ina GUI environment. The statement here basically assumes it has a WAVfile – other formats that also support this ADPCM setting would also work.More likely, the formats supported are highly specific to the underlyingimplementation and you need to be careful about what values to use.From a programming view point you have a number of choices here:• Do not call SetDestinationDataTypeL() – every file type hasa default.• Call SetDestinationDataTypeL() explicitly – as here, but itcould come through from the main GUI rather than being hardwired.• Discover from the CMdaAudioRecorderUtility object, once ithas been opened, a list of the formats supported, choose one andset that.
Discovery is via the call GetSupportedDestinationDataTypesL().It is tempting to write your code to negotiate – checking to see ifsomething is supported before setting it. In practice, most applicationsdon’t need to do this – if you know what you want, set it and handlethe leave appropriately.
This is almost certainly quicker and easier. Theexceptions are usually test code or middleware.Variations on this scheme include something like:TRAP_IGNORE(iUtility->SetDestinationDataTypeL(KMMFFourCCCodeIMAD));This says ‘try to use IMA ADPCM, but if you can’t just use the default’.That is a fairly sensible approach – if you get your guess wrong, treat it asnon-fatal.126MULTIMEDIA FRAMEWORK: AUDIOA slightly more complex version might look at the filename suffix anduse a version based on that. In reality, that is beyond the needs of manyapplications.Returning to the example program, at some point the main programdecides to stop recording and plays back the sound, using StopAndPlayL():void CRecordAudio::StopAndPlayL(){iUtility->Stop();iObserver->RecordingStopped(); // just let the calling code knowiUtility->PlayL();}This plays the file from the beginning and at the end you geta MoscoStateChangeEvent() callback to show the switch fromEPlaying to EOpen.
If you look back to the function above, you’llremember this is handled and just tells the client. The RecordingStopped() and PlayOrRecordComplete() callbacks mentioned arearbitrary – they make sense in this particular test – and you are free to dowhat makes sense for your application.One issue we’ve skipped over a bit is that of Position().
We’re ableto play the content just by calling PlayL() because the Stop() calldoes an implied rewind to the beginning. This is useful in some scenarios,such as this simple one, but it is not always quite what you want. Thereis no pause functionality in CMdaAudioRecordUtility, so if youneed to support pause, use Stop() or an alternative technique – such asusing CMdaAudioPlayUtility for the play functions.
Forgetting thelatter, at least for the moment, you’ll find that you can get a reasonableapproximation to play–pause–play but you have to handle positionyourself. Thus a ‘pause’ sequence looks something like:iCachedPosition = iUtility->Position();iUtility->Stop();A ‘resume’ sequence looks something like:iUtility->SetPosition(iCachedPosition);iUtility->PlayL();5.4.2 VariationsBased on this example, there are variations you can implement:• Record some more audio to an existing file. You can use this in twoways – record soon after the original or open an already open file andFILE CONVERSION127record to that.
It helps to use the following call routinely, even thoughyou don’t always need to:iUtility->SetPosition(iUtility->Duration());Note that append is not supported for all formats nor for allfiles – most non-trivial compression consists of several ‘frames’ andthere can be problems if the format does not consist of an integralnumber of frames. Also, it is perhaps best not to change the variousformat parameters (e.g. encoding type and sampling rate) on thesecond recording.
The system reads these parameters from the existingpart of the file and uses the same values. Technically it should allowyou to ‘set’ the same ones but whether it does depends on the plug-insconcerned.• Change format (mono to stereo, sample rate, or bit rate). Note that thebit rate relates to some formats and is more about the compressionfactor. In most cases, a good approach is to ignore errors – if yourrequest does not work, use the default.• Crop from the beginning to the current position or from the current position to the end, using CropFromTheBeginningL() orCropL() respectively.
This really needs to be used as part of aninteractive application but it allows the user to remove silence oraccidentally recorded sound at the start or end of a file.5.5 File ConversionCMdaAudioConvertUtility shares many of the calls of CMdaAudioRecorderUtility. They both derive from CMdaAudioClipUtility, although the reasoning for the common base class is now notvery clear. The two classes cover very different use cases – CMdaAudioConvertUtility is typically used to copy from one file to another,with some format conversion action. It is different from the other APIsin this section in that it does not involve using the microphone or thespeaker.Because of their shared base class, using CMdaAudioConvertUtility is very similar to using CMdaAudioRecorderUtility.Opening looks like:void CConvertAudio::ConvertL(const TDesC& aFromFileName,const TDesC& aToFileName){128MULTIMEDIA FRAMEWORK: AUDIOiUtility->OpenL(aFromFileName, aToFileName);}Again, the suffix for the filename is the main way of selecting theformat – if you state that the output is c:\temp.wav, then the file will bea WAV file.Because it shares many API features with CMdaAudioRecorderUtility, the client code that drives this class is again based onMoscoStateChangeEvent callbacks, with very similar parameters.This example is typical:void CConvertAudio::MoscoStateChangeEvent(CBase* aObject,TInt aPreviousState,TInt aCurrentState,TInt aErrorCode){__ASSERT_DEBUG(aObject == iUtility, User::Invariant());aObject = aObject; // to stop warningTInt error = aErrorCode;if (error == KErrNone){if (aPreviousState == CMdaAudioConvertUtility::ENotReady &&aCurrentState == CMdaAudioConvertUtility::EOpen){// have opened the fileTRAP(error, OnOpenL());}else if (aPreviousState == CMdaAudioConvertUtility::EPlaying &&aCurrentState == CMdaAudioConvertUtility::EOpen){// normal terminationiObserver->ConvertComplete(KErrNone);}}if (error != KErrNone){// stop nowiObserver->ConvertComplete(aErrorCode);}}The structure is very similar to that of the recorder.
We’ll assume youhave read Section 5.4 and look at the bit that differs – OnOpenL(). Againthis is virtually the same as for recording, for example:void CConvertAudio::OnOpenL(){// let's convert to 16-bit PCMiUtility->SetDestinationDataTypeL(KMMFFourCCCodePCM16);iUtility->ConvertL();}TONE UTILITY129In this example, we explicitly try to request 16-bit PCM.
If this is notsupported, we treat it as a fatal error. Again, as with recording, we coulddecide to ignore the error, say, and just use the default. However, therequirements for conversion are often different – this is often used whensending a file to another end user to ensure that a known, ‘common’file format is used. In that case, we usually want to be fussy about theformat and treat problems as an error.
As with recording, if you want tooverride the sample rate, the mono–stereo setting, etc. from the default,do it before you call ConvertL(). The calls are very similar.You can use conversion sequences to append to an existing file as wellas copying. If you want to ensure you are making a copy, delete the filefirst:iFs.Delete(iToFileName); // Ignore errorsIf you want to use the append functionality, be careful about settingdata types, sample rates, etc. As with recording, it is best not to do thiswhen you are appending, as the values are read from the existing file.You can use a play window to convert only a portion of the original. If you only want part of the file, a play window is preferred tousing CropL() or CropFromTheBeginningL() to remove part of theoutput – generating a file and then removing some of it is less efficient.Conversion potentially uses different sets of plug-ins to playing orrecording – don’t assume that, just because you can play format A andrecord format B, you can convert from format A to format B.5.6 Tone UtilityThis section is about playing tones – simple tones, DTMF and tonesequences.