Symbian
Symbian Developer Library

SYMBIAN OS V9.4

Feedback

[Index] [Previous] [Next]


AudioClientEx: Multimedia audio example code

The example code is found in: Examples\MultiMedia\AudioClientEx\

Note: This code is designed to work only with Techview and is not guaranteed to work with other interfaces

These are the main files contained in the examples. Some extra files may be needed to run the examples, and these will be found in the appropriate examples directory.

// CMediaClientView.h
//
// Copyright (c) 2001 Symbian Ltd.  All rights reserved.
//


#ifndef CMEDIACLIENTVIEW
#define CMEDIACLIENTVIEW

#include <coecntrl.h>

class CMediaClientEngine;

/** 
Application view. 

It simply writes a string describing the current state of the application.
 */

class CMediaClientView : public CCoeControl 
    {
public:
/**
Allocates and constructs a new view.
@cat Construction and destruction
@return New view object
@param aRect App UI area
@param aEngine Application engine
*/
    static CMediaClientView* NewL(const TRect& aRect, CMediaClientEngine& aEngine);
/**
Destructor.
@cat Construction and destruction
*/
    ~CMediaClientView();

    // 

/**
Sets the view's text.

@param aDescription View's new text
*/
    void SetDescription(const TDesC& aDescription);

private: 

/**
Redraws the view.

From CCoeControl.
@param aRect Redraw area
*/
    void Draw(const TRect& /*aRect*/) const;

private:
/**
Constructor.
@cat Construction and destruction
@param aEngine Application engine
*/
    CMediaClientView(CMediaClientEngine& aEngine);
/**
Second phase constructor.
@cat Construction and destruction
@param aRect App UI area
*/    void ConstructL(const TRect& aRect);

private:
    /** Application engine*/  
    CMediaClientEngine& iEngine;
    /** Current view text */
    TBufC<25> iDescription;
    };

#endif // CMEDIACLIENTVIEW
// CMediaClientEngine.h
//
// Copyright (c) 2001 Symbian Ltd.  All rights reserved.
//

// The engine interfaces to the MMF audio APIs
// to play tones, files, and streams, and record and convert files

#ifndef CMEDIACLIENTENGINE
#define CMEDIACLIENTENGINE

#include <e32std.h>
#include <f32file.h>
#include <badesca.h>

#include <MdaAudioSamplePlayer.h>
#include <MdaAudioTonePlayer.h>
#include <MdaAudioSampleEditor.h>
#include <MdaAudioOutputStream.h>

class MExEngineObserver;

// Defines type for array of media file meta data
typedef RPointerArray<CMMFMetaDataEntry> RMetaDataArray;

/** Conversion format options */
class TFormatOptions
    {
public:
    TFormatOptions() {Reset();}
    virtual void Reset() {i4CCSet = iBitRateSet = iSampleRateSet = iChannelSet = EFalse;}
public:
    // Conversion format option values
    TFourCC i4CC;
    TUint iBitRate;
    TUint iSampleRate;
    TUint iChannel;
    // Flags indicating which options have been set
    TBool i4CCSet;
    TBool iBitRateSet;
    TBool iSampleRateSet;
    TBool iChannelSet;
    };

/** Record format options 
    These are the same as conversion options, plus gain and balance settings */
class TRecordFormatOptions: public TFormatOptions
    {
public:
    TRecordFormatOptions() {Reset();}
    void Reset()
        {
        TFormatOptions::Reset();
        iBalanceSet = iGainSet = EFalse;
        }
public: 
    // recording options
    TInt iBalance;  // recording balance
    TInt iGain;    // gain

public:
    // recording option flags
    TBool iBalanceSet;
    TBool iGainSet;
    };

/** Application engine to play tones, files, & streams, and record to file.
  It inherits:
     MMdaAudioPlayerCallback  to handles call backs from audio player;
     MMdaAudioToneObserver  to handles call backs from tone player;
     MMdaObjectStateChangeObserver  to handles call backs from recorder;
     MMdaAudioOutputStreamCallback  to handles call backs from stream player.*/
class CMediaClientEngine : public CBase, 
    public MMdaAudioPlayerCallback,    
    public MMdaAudioToneObserver,  
    public MMdaObjectStateChangeObserver, 
    public MMdaAudioOutputStreamCallback
    {
public:
    /** Volume levels */
    enum TExVolume
        {
        /** Loud volume */
        ELoud=1,
        /** Medium volume  */
        EMedium,
        /** Low volume */
        EQuiet
        };
    
    /** Gain levels */
    enum TExRecordGain
        {
        /** High gain */
        EGainMax,
        /** Medium gain */
        EGainMedium,
        /** Low gain */
        EGainLow
        };
        
    /** Engine status */
    enum TState
        {
        /** No operation */
        EIdle,
        /** Preparing to play a tone */
        ETonePrepare,
        /** Playing a tone */
        ETonePlaying,
        /** Preparing to play a file*/
        EPlayFilePrepare,
        /** Playing a file */
        EPlayFilePlaying,
        /** Preparing to record */
        ERecordPrepare,
        /** Recording */
        ERecording,
        /** Getting meta data */
        EGetMetaData1,
        /** Retrieved meta data */
        EGetMetaData2,
        /** Converting file */
        EConvert,
        /** Completed conversion */
        EConvertComplete,
        /** Preparing to play a stream */
        EStreamPrepare,
        /** Stream initiated */
        EStreamStarted,
        /** Requires new stream data */
        ENextStreamBuf,
        /** Error in playing stream */
        EStreamError,
        /** Stream closing down */
        EStreamStopping
        };

public:
    /** Constructor.
    @param File server session */
    CMediaClientEngine(RFs& aFs);
    /** Destructor.*/
    ~CMediaClientEngine();
    /** Set the engine observer.
    @param aObserver The engine observer */
    void SetObserver(MExEngineObserver& aObserver);
    /** Plays a sound file.
    @param aFile File to play
    @panic MEDIA_AUDIO_EX 0 Engine is already playing a sound */
    void PlayL(const TDesC &aFile);
    /** Plays a sound tone.
    @param aFrequency Frequency in Hz 
    @param aDuration Duration in ms
    @panic MEDIA_AUDIO_EX 0 Engine is already playing a sound */
    void PlayL(TInt aFrequency, const TTimeIntervalMicroSeconds& aDuration);
    /** Plays a stream.
    @panic MEDIA_AUDIO_EX 0 Engine is already playing a sound */
    void PlayL();
    /** Writes data to the stream.
    @param aData Data to write */
    void WriteToStreamL(const TDesC8& aData);
    /** Gets a recorder for specified target type */
    CMdaAudioRecorderUtility* NewRecorderL(const TDesC& aTargetType);
    /** Get possible record types */
    void RecordTypesL(CDesCArray& aTypeArray);
    /** Records sound to a file.
    @param aFile File to record to */
    void RecordL(const TDesC &aFile, const TRecordFormatOptions& aRecordOptions);
    /** Terminates any current operation. */
    void Stop();
    /** Gets meta data for a file */
    void GetMetaDataL(const TFileName& aFile, RMetaDataArray& aMetaData);
    /** Gets an array of possible conversion types for specified file */
    static void DestinationTypesL(const TFileName& aFile, CDesCArray& aExt);
    /** Gets a converter for specified file and target */
    CMdaAudioConvertUtility* NewConverterL(const TFileName& aFile, const TDesC& aTargetType, 
        TFileName& aTargetFile);
    /** Does conversion for specified file and target */
    void ConvertL(const TFileName& aFile, const TDesC& aTargetType, const TFormatOptions& aOptions);
    /** Gets the volume. */
    TExVolume Volume() const;
    /** Sets the volume. 
    @param aVolume The new volume */
    void SetVolume(TExVolume aVolume);

    /** Gets the engine state 
    @return The engine state*/
    TState Status() const;

private:
    /** Engine panic codes */
    enum TPanics
        {
        /** Sound is already playing */
        EInProgress,
        /** No observer set */
        ENullObserver,
        /** Sound preparation is not complete. */
        ENotReady,
        /** No tone player constructed */
        ENullTonePlayer,
        /** Engine is in the wrong state */
        EWrongState,
        /** No file player constructed  */
        ENullPlayerUtility
        };

private:

    /** File is ready to play.
    @param aError Any error in setup
    @param aDuration The duration of the audio sample */
    void MapcInitComplete(TInt aError, const TTimeIntervalMicroSeconds& aDuration);

    /** File has completed playing.
    @param aError Any error in playing */
    void MapcPlayComplete(TInt aError);
    /** Tone is ready to play.
    @param aError Any error in setup */
    void MatoPrepareComplete(TInt aError);
    /** Tone has completed playing.
    @param aError Any error in playing */
    void MatoPlayComplete(TInt aError);

    // from MMdaObjectStateChangeObserver
    /** Change in sound recorder state.
    @param aObject Recorder object
    @param aPreviousState Last state
    @param aCurrentState Current state
    @param aErrorCode Any error that has occurred */
    void MoscoStateChangeEvent(CBase* aObject, TInt aPreviousState, 
        TInt aCurrentState, TInt aErrorCode);

    /** Stream has completed playing.
    @param aError Any error in playing */
    void MaoscPlayComplete(TInt aError);
    /** Stream has completed use of specified data slice.
    @param aError Any error in handling the slice */
    void MaoscBufferCopied(TInt aError, const TDesC8& aBuffer);
    /** Stream is ready to play.
    @param aError Any error in setup */
    void MaoscOpenComplete(TInt aError);

    /** Conversion stage 2*/
    void Convert2L();
    /** Recording stage 2*/
    void Record2L();

    /** Clean up after tone playing */
    void ToneCleanup();
    /** Clean up after file playing */
    void PlayCleanup();
    /** Clean up after recording */
    void RecordCleanup();
    /** Clean up after stream playing */
    void StreamCleanup();
    /** Clean up after convert */
    void ConvertCleanup();

    /** Panic engine 
    @param aPanic Panic code */
    void Panic(TPanics aPanic);

private:
    /** File to play */
    TFileName iFileName;
    /** Target conversion file */
    TFileName iTargetFile;
    /** File server session */
    RFs& iFs;
    /** Engine state */
    TState iState;
    /** Sound volume */
    TExVolume iVolume;
    /** Sound gain */
    TExRecordGain iGain;
    /** Engine observer */
    MExEngineObserver* iEngineObserver;
        
    /** Tone player */
    CMdaAudioToneUtility* iAudioToneUtility;
    /** File player */
    CMdaAudioPlayerUtility* iAudioPlayUtility;
    /** File recorder */
    CMdaAudioRecorderUtility* iAudioRecorderUtility;
    /** Stream player */
    CMdaAudioOutputStream* iAudioStreamPlayer;
    /** Converter */
    CMdaAudioConvertUtility* iConvertUtility;

    /** Additional sound settings */
    TMdaAudioDataSettings iSettings;
    /** Meta data array */
    RMetaDataArray* iMetaData;
    /** Conversion format options */
    const TFormatOptions* iOptions;
    /** Record format options */
    const TRecordFormatOptions* iRecordFormatOptions;
    };

/** Engine observer for the UI to implement */
class MExEngineObserver
    {
public:
/** Reports an engine event.
@param aState New engine state
@param aError Error condition */
    virtual void HandleEngineState(CMediaClientEngine::TState aState, TInt aError) = 0;
    };

/** Engine helper to create recorder object in a seeming synchronous way */
class CRecorderCreator: public CActive, public MMdaObjectStateChangeObserver
    {
public:
    static CMdaAudioRecorderUtility* NewRecorderL(const TDesC& aTargetType);
    
private:
    CRecorderCreator();
    CMdaAudioRecorderUtility* ConstructL(const TDesC& aTargetType);
    void RunL();
    void DoCancel();
    void MoscoStateChangeEvent(CBase* aObject, TInt aPreviousState, TInt aCurrentState, TInt aErrorCode);

private:
    CMdaAudioRecorderUtility* iRecorderUtility;
    };


/** Engine helper to create converter object in a seeming synchronous way */
class CConverterCreator: public CActive, public MMdaObjectStateChangeObserver
    {
public:
    static CMdaAudioConvertUtility* NewConverterL(const TFileName& aFile, const TDesC& aTargetType,
        TFileName& aTargetFile);
    
private:
    CConverterCreator();
    CMdaAudioConvertUtility* ConstructL(const TFileName& aFile, const TDesC& aTargetType,
        TFileName& aTargetFile);
    void RunL();
    void DoCancel();
    void MoscoStateChangeEvent(CBase* aObject, TInt aPreviousState, TInt aCurrentState, TInt aErrorCode);

private:
    CMdaAudioConvertUtility* iConvertUtility;
    };


#endif // CMEDIACLIENTENGINE
// CMediaClientDocument.h
//
// Copyright (c) 2001 Symbian Ltd.  All rights reserved.
//

#ifndef CMEDIACLIENTDOCUMENT
#define CMEDIACLIENTDOCUMENT

#include <eikdoc.h>

#include "CMediaClientEngine.h"

/** Simple document class to own the engine */
class CMediaClientDocument : public CEikDocument
    {
public:
    // construct/destruct
/**
Allocates and constructs a new document.
@cat Construction and destruction
@return New document object
@param aApp Application
*/
    static CMediaClientDocument* NewL(CEikApplication& aApp);
/**
Destructor.
@cat Construction and destruction
*/
~CMediaClientDocument();


/**
Gets the application engine.

@return The application engine
*/
    CMediaClientEngine& Engine();

private:
/**
Constructor.
@cat Construction and destruction
@param aApp Application
*/
    CMediaClientDocument(CEikApplication& aApp);
/**
Second phase constructor.
@cat Construction and destruction
*/  void ConstructL();


/**
Creates an app UI.
From <code>CEikDocument</code>.
@return A new <code>CMediaClientAppUI</code> object
*/
    CEikAppUi*  CreateAppUiL();

private: 
    /** The application engine */
    CMediaClientEngine* iEngine; 
    };

#endif // CMEDIACLIENTDOCUMENT
// CMediaClientAppUi.h
//
// Copyright (c) 2001 Symbian Ltd.  All rights reserved.
//

#ifndef CMEDIACLIENTAPPUI
#define CMEDIACLIENTAPPUI

#include <eikappui.h>
#include <eikmsg.h>
#include <badesca.h>

#include "CMediaClientEngine.h"

class CMediaClientView;
class CAudioStreamSource;
class CAudioStreamSourceWithQueue;

/** Handles user commands to drive the engine.
  It implements the <code>MExEngineObserver</code> interface to get
  events back from the engine. */
class CMediaClientAppUi : public CEikAppUi, public MExEngineObserver
    {
public:
    /** Performs second-phase construction.*/
    void ConstructL();
    /** Destructor. */
    ~CMediaClientAppUi();
    /** Exits the application. 
    This is a static callback function, which is called back asynchronously
    to allow the application to exit after any media server active objects
    have completed running.
    @return Unused error code
    @param aUI Pointer to the app UI object itself */
    static TInt DoExit(TAny* aUI);

private:
    /** Handles user commands.
    @param aCommand */
    void HandleCommandL(TInt aCommand);
    /** Dynamically modifies menu pane so that only appropriate commands
    are available.
    @param aResourceId ID of the menu pane
    @param aMenuPane Pointer to the menu pane */
    void DynInitMenuPaneL(TInt aResourceId, CEikMenuPane* aMenuPane);
    /** Gets the application engine.
    @return The application engine */
    CMediaClientEngine& MediaClientEngine() const;
    /** Plays a tone. */
    void ToneDialogL();
    /** Plays a file. */
    void PlayFileDialogL();
    /** Records a file. */
    void RecordFileDialogL();
    /** Requests file metadata */
    void FileMetaDataL();
    /** Displays retrieved file metadata */
    void DisplayMetaDataL();
    /** Converts a file to another format */
    void ConvertFileDialogL();

    // From MExEngineObserver
    /** Handles engine events.
    @param aState The current engine state
    @param aError The error code produced by the last engine operation */
    void HandleEngineState(CMediaClientEngine::TState aState, TInt aError);

    /** Cleans up <code>iAudioStreamSource</code> and <code>iAudioStreamSourceWithQueue</code>. */
    void StreamSourceCleanUp();

private:
    /** The application view */
    CMediaClientView* iAppView;
    /** File name buffer */
    TFileName iFileName;
    /** Synchronous stream data source */
    CAudioStreamSource* iAudioStreamSource;
    /** Aynchronous stream data source */
    CAudioStreamSourceWithQueue* iAudioStreamSourceWithQueue;
    /** Stores meta data about a file */
    RMetaDataArray iMetaData;
    /** The engine state */
    CMediaClientEngine::TState iState;
    /** The callback to exit function */
    CAsyncCallBack* iExitCallBack;
    /** Conversion format options */
    TFormatOptions iFormatOptions;
    /** Record format options */
    TRecordFormatOptions iRecordFormatOptions;
    /** Conversion format type */
    TBuf<5> iTargetType;
    };


#include <eikdialg.h>

/** A dialog to allow the user to the set a tone to play. */
class CToneDialog: public CEikDialog
    {
public:
/** Runs the dialog to get the frequency and duration of the tone to play.
@return True if the dialog was OK'ed, false if cancelled
@param aFrequency On return, frequency in Hz 
@param aDuration On return, duration in ms */
    static TBool RunDlgLD(TInt& aFrequency, TTimeIntervalMicroSeconds& aDuration);

private:
/** Constructor.
@param aFrequency Frequency
@param aDuration Duration */
    CToneDialog(TInt& aFrequency, TTimeIntervalMicroSeconds& aDuration);
/** Called when user pressses a button. From CEikDialog.
@return True if can now leave, false to retain the dialog
@param aKeycode Button presssed */
    TBool OkToExitL(TInt aKeycode);

private:
    /** Frequency */
    TInt& iFrequency;
    /** Duration */
    TTimeIntervalMicroSeconds& iDuration;
    };


/** A dialog to allow the user to set how to record a sound file */
class CRecordDialog: public CEikDialog
    {
public:
/** Runs the dialog to get the target file name, format, and options
@return True if the dialog was OK'ed, false if cancelled */
    static TBool RunDlgLD(
        TFileName& aFileName, CMediaClientEngine& aMediaClientEngine,
        TRecordFormatOptions& aFormatOptions);
    ~CRecordDialog();

private:
    // construct/destruct
    CRecordDialog(TFileName& aFileName, CMediaClientEngine& aMediaClientEngine,
        TRecordFormatOptions& aFormatOptions);
    // from CEikDialog
    TBool OkToExitL(TInt aKeycode);
    void PreLayoutDynInitL();
    void HandleControlStateChangeL(TInt aControlId);

private:
    TFileName& iFileName;
    CMediaClientEngine& iMediaClientEngine;
    // Format options
    TRecordFormatOptions& iRecordFormatOptions;
    // Flag for options set
    TInt iOptionsSetFor;
    };


/** A dialog to allow the user to set how to convert a file */
class CConverterDialog: public CEikDialog
    {
public:
/** Runs the dialog to get the target file name, format, and options
@return True if the dialog was OK'ed, false if cancelled */
    static TBool RunDlgLD(
        // input parameters
        const TFileName& aFileName, CMediaClientEngine& aMediaClientEngine,
        TFormatOptions& aFormatOptions, TDes& aTargetType);
    ~CConverterDialog();

private:
    // construct/destruct
    CConverterDialog(const TFileName& aFileName, CMediaClientEngine& aMediaClientEngine,
        TFormatOptions& aFormatOptions, TDes& aTargetType);
    // from CEikDialog
    TBool OkToExitL(TInt aKeycode);
    void PreLayoutDynInitL();
    void HandleControlStateChangeL(TInt aControlId);

private:
    // Input params
    const TFileName& iFileName;
    CMediaClientEngine& iMediaClientEngine;
    // Format options
    TFormatOptions& iFormatOptions;
    // Format type
    TDes& iTargetType;
    // Flag for options set
    TInt iOptionsSetFor;
    };


// Format options dialog
class CFormatOptionsDialog: public CEikDialog
    {
public:
    static TBool RunDlgLD(const TFileName& aInputFile, const TDesC& aTargetType, 
        CMediaClientEngine& aMediaClientEngine, TFormatOptions& aFormatOptions);
    ~CFormatOptionsDialog();

protected:
    // construct/destruct
    CFormatOptionsDialog(const TFileName& aInputFile, const TDesC& aTargetType, 
        CMediaClientEngine& aMediaClientEngine, TFormatOptions& aFormatOptions);
    void ConstructL();
    void TUintChoiceList(TInt aResourceId, const RArray<TUint>& aArray);
    // from CEikDialog
    TBool OkToExitL(TInt aKeycode);
    void PreLayoutDynInitL();

protected:
    // Input params
    const TFileName& iFileName;
    const TDesC& iTargetType;
    CMediaClientEngine& iMediaClientEngine;
    // Output
    TFormatOptions& iFormatOptions;

    // Cached info
    RArray<TFourCC> iDataTypes;
    RArray<TUint> iBitRates;
    RArray<TUint> iSampleRates;
    RArray<TUint> iChannels;
    };

// Recording format options dialog
class CRecordFormatOptionsDialog: public CFormatOptionsDialog
    {
public:
    static TBool RunDlgLD(const TDesC& aTargetType, 
        CMediaClientEngine& aMediaClientEngine, TRecordFormatOptions& aFormatOptions);
    ~CRecordFormatOptionsDialog();

private:
    // construct/destruct
    CRecordFormatOptionsDialog(const TDesC& aTargetType, 
        CMediaClientEngine& aMediaClientEngine, TRecordFormatOptions& aFormatOptions);
    void ConstructL();
    // from CEikDialog
    TBool OkToExitL(TInt aKeycode);
    void PreLayoutDynInitL();

private:
    // Cached info
    TInt iMaxGain;
    // Output
    TRecordFormatOptions& iRecordFormatOptions;
    // 
    TBuf<1> iBuf;
    };


/** Size of each slice read from the file during streaming */
const TInt KSliceSize = 4096;
/** Buffer to  */
typedef TBuf8<KSliceSize> TSoundSlice;

/** Provides an effectively synchronous artificial audio stream source.
  It gets data in slices from a file as soon as the stream player
  is ready for them. */
class CAudioStreamSource: public CActive
    {
public:

/** Allocates and constructs a new synchronous artificial audio stream source.
@cat Construction and destruction
@return New synchronous artificial audio stream source object
@param aFs File server session
@param aEngine Application engine */
    static CAudioStreamSource* NewL(RFs& aFs,CMediaClientEngine& aEngine);

/** Asynchronously feeds the next slice to the stream player. */
    void Next();

/** Destructor.
@cat Construction and destruction */
    ~CAudioStreamSource();

private:
    /** Second phase constructor.
@cat Construction and destruction */
    void ConstructL();

/** Constructor.
@cat Construction and destruction
@param aFs File server session
@param aEngine Application engine */
    CAudioStreamSource(RFs& aFs, CMediaClientEngine& aEngine);

/** Gets the next slice of data and feeds it to the stream player.
@cat Active object protocol */
    void RunL();
/** Cancels the operation.
@cat Active object protocol */
    void DoCancel();

private:
    /** The file from which to read the stream data */
    RFile iSourceFile;
    /** File server session */
    RFs& iFs;
    /** Application engine */
    CMediaClientEngine& iEngine;
    /** Data slice to feed to stream player */
    TSoundSlice iBuf;
    };

/**
A sound slice read in from the file.

  These objects are held in a queue. They are removed from the queue in the callback 
  function MaoscBufferCopied().
*/
class CSoundSlice: public CBase
    {
public:

/**
Allocates and constructs a new synchronous artificial audio stream source.
@cat Construction and destruction
@return New synchronous artificial audio stream source object
@param aData Data for the slice
*/
    static CSoundSlice* NewL(const TDesC8& aData);
/**
Destructor.
*/
    ~CSoundSlice();
    const TDesC8& GetData();
public:
    static const TInt iOffset; 
private:
/**
Constructor.
@cat Construction and destruction
*/
    CSoundSlice();
/**
Second phase constructor.
@param aData Data for the slice
*/
    void ConstructL(const TDesC8& aData);
private:
    /** Data for the slice */
    HBufC8* iData;
    /** Allows objects to be part of a linked list of slices */
    TSglQueLink iLink;
    };


/** Provides an artificial audio stream source.

Gets data in slices from a file and stores them in a queue
until they are safe to be deleted.
A timer is used to indicate when to read the next slice.*/
class CAudioStreamSourceWithQueue: public CTimer
    {
public:
/**
Allocates and constructs a new asynchronous artificial audio stream source.
@return New asynchronous artificial audio stream source object
@param aFs File server session
@param aEngine Application engine
*/  static CAudioStreamSourceWithQueue* NewL(RFs& aFs,CMediaClientEngine& aEngine);
/**
Destructor.
*/
    ~CAudioStreamSourceWithQueue();
/**
Asynchronously feeds the next slice to the stream player.
*/
    void Next();

/**
Removes a used slice from the queue.

*/
    void RemoveFromQueue();

private:
/**
Constructor.
@param aFs File server session
@param aEngine Application engine
*/
    CAudioStreamSourceWithQueue(RFs& aFs, CMediaClientEngine& aEngine);
/**
Second phase constructor.
*/
    void ConstructL();
/**
Gets the next slice of data and feeds it to the stream player.
*/
    void RunL();
/**
Cancels the operation.
*/
    void DoCancel();

private:
    /** The file from which to read the stream data */
    RFile iSourceFile;
    /** File server session */
    RFs& iFs;
    /** Application engine */
    CMediaClientEngine& iEngine;
    /** Data slice to feed to stream player */
    TSoundSlice iBuf;
    /** Header for the linked list of slices */
    TSglQue<CSoundSlice> iQueue;
    /** List iterator - needed to delete all the items */
    TSglQueIter<CSoundSlice> iQueueIter;
    };


#endif // CMEDIACLIENTAPPUI
// CMediaClientApplication.h
//
// Copyright (c) 2001 Symbian Ltd.  All rights reserved.
//

#ifndef CMEDIACLIENTAPPLICATION
#define CMEDIACLIENTAPPLICATION

#include <eikapp.h>


/**

Application class derived from Uikon framework.

*/
class CMediaClientApplication : public CEikApplication 
    {
private: // from CApaApplication
/**

Creates the application's document.

@return A new document       
*/
    CApaDocument* CreateDocumentL();

/**

Gets the application's UID

@return The application's UID
*/
    TUid AppDllUid() const;
    };

#endif // CMEDIACLIENTAPPLICATION
// MediaClientView.cpp
//
// Copyright (c) 2001 Symbian Ltd.  All rights reserved.
//

// The view is very simple: it simply writes a string
// describing the current state of the application
#include <AudioClientEx.rsg>
#include "CMediaClientView.h"
#include "CMediaClientDocument.h"
#include "CMediaClientEngine.h"

#include <eikenv.h>


CMediaClientView* CMediaClientView::NewL(const TRect& aRect, CMediaClientEngine& aEngine)
    {
    CMediaClientView* new_object = new (ELeave) CMediaClientView(aEngine);
    CleanupStack::PushL(new_object);
    new_object->ConstructL(aRect);
    CleanupStack::Pop();

    return new_object;
    }

// Set up
void CMediaClientView::ConstructL(const TRect& aRect) 
    {    
    CreateWindowL();
    Window().SetShadowDisabled(ETrue);
    SetRect(aRect);

    ActivateL();
    }

CMediaClientView::CMediaClientView(CMediaClientEngine& aEngine)
:iEngine(aEngine)
    {
    }

CMediaClientView::~CMediaClientView()
    {
    }

void CMediaClientView::SetDescription(const TDesC& aDescription)
    {
    iDescription = aDescription;
    }

// Update the view
void CMediaClientView::Draw(const TRect& /*aRect*/) const
    {
    CWindowGc& gc = SystemGc();

    TInt boxHeight=Rect().Height(); // get height of text box
    const CFont* font=iEikonEnv->TitleFont();
    TInt textHeight(font->HeightInPixels()); // system font height
    TInt offset=(textHeight+boxHeight)/2; // 1/2 font ht below halfway down box
    TInt margin=0; // margin is zero
    
    gc.SetBrushStyle(CGraphicsContext::ESolidBrush);    
    gc.SetBrushColor(KRgbWhite);
    gc.SetPenColor(KRgbBlack);    
    gc.UseFont(font);

    gc.DrawRect(Rect());
    gc.DrawText(iDescription, Rect(), offset, CGraphicsContext::ECenter, margin);
    }
// CMediaClientEngine.cpp
//
// Copyright (c) 2001-2005 Symbian Software Ltd.  All rights reserved.

#include "CMediaClientEngine.h"

// Global helper function to change extension on a file
void ChangeExtension(const TFileName& aFile, const TDesC& aTargetType,
    TFileName& aTargetFile);
//
/************** CONSTRUCTION AND DESTRUCTION ******************/
//

CMediaClientEngine::CMediaClientEngine(RFs& aFs)
    : iFs(aFs),
    iState(CMediaClientEngine::EIdle),
    iVolume(CMediaClientEngine::EMedium)
    {
    }

CMediaClientEngine::~CMediaClientEngine()
    {
    iEngineObserver = 0;
    Stop();
    }

//
/************** GENERAL FUNCTIONS ******************/
//

void CMediaClientEngine::SetObserver(MExEngineObserver& aObserver)
    {
    iEngineObserver = &aObserver;
    }


// Terminate current operation
void CMediaClientEngine::Stop()
    {
    // Take appropriate action depending on what the state is
    switch (iState)
        {
        case ETonePrepare:
            iAudioToneUtility->CancelPrepare();
            ToneCleanup();
            break;
        case ETonePlaying:
            iAudioToneUtility->CancelPlay();
            ToneCleanup();
            break;
        case EPlayFilePrepare:
            PlayCleanup();
            break;
        case EPlayFilePlaying:
            iAudioPlayUtility->Stop();
            PlayCleanup();
            break;
        case ERecordPrepare:
            RecordCleanup();
            break;
        case ERecording:
            iAudioRecorderUtility -> Stop();
            RecordCleanup();
            break;
        case EConvert:
            ConvertCleanup();
            break;
        case EStreamPrepare:
            StreamCleanup();
            break;
        case EStreamStarted:
        case ENextStreamBuf:
            iAudioStreamPlayer -> Stop(); // Calls MaoscBufferCopied() with KErrAbort
            iState = EStreamStopping;
            break;
        default:
            break;
        };
    // And tell the UI about the state change
    if (iEngineObserver)
        iEngineObserver -> HandleEngineState(iState,KErrNone);
    }

// Get volume
CMediaClientEngine::TExVolume CMediaClientEngine::Volume() const
    {
    return iVolume;
    }

// Set volume
void CMediaClientEngine::SetVolume(CMediaClientEngine::TExVolume aVolume)
    {
    iVolume = aVolume;
    if (iAudioStreamPlayer) // Streaming is in progress
        {
        iAudioStreamPlayer -> SetVolume(iAudioStreamPlayer -> MaxVolume()/iVolume);
        }   
    }

// Get state
CMediaClientEngine::TState CMediaClientEngine::Status() const
    {
    return iState;
    }

// Panic
void CMediaClientEngine::Panic(CMediaClientEngine::TPanics aPanic)
    {
    _LIT(KPanic,"MEDIA_AUDIO_EX");
    User::Panic(KPanic,aPanic);
    }


//
/************** TONE PLAYING ******************/
//

// Play a tone of specified frequency and duration
void CMediaClientEngine::PlayL(TInt aFrequency, 
                               const TTimeIntervalMicroSeconds& aDuration)
    {
    __ASSERT_DEBUG(iState == CMediaClientEngine::EIdle, Panic(CMediaClientEngine::EInProgress));
    
    iAudioToneUtility = CMdaAudioToneUtility::NewL(*this);
    iState = ETonePrepare;
    iAudioToneUtility -> PrepareToPlayTone(aFrequency, aDuration);
    // This will call back MatoPrepareComplete() when preparation is complete
    }

// Audio is now prepared to play a tone
// from MMdaAudioToneObserver
void CMediaClientEngine::MatoPrepareComplete(TInt aError)
    {
    __ASSERT_DEBUG(iEngineObserver, Panic(CMediaClientEngine::ENullObserver));
    __ASSERT_DEBUG(iAudioToneUtility, Panic(CMediaClientEngine::ENullTonePlayer));
    __ASSERT_DEBUG(iState == ETonePrepare, Panic(CMediaClientEngine::EWrongState));
    
    // If preparation was sucessful, play the tone
    if (aError)
        ToneCleanup();
    else
        {
        iState = ETonePlaying;
        iAudioToneUtility -> SetVolume(iAudioToneUtility -> MaxVolume()/iVolume);
        iAudioToneUtility -> Play();
        }
    iEngineObserver -> HandleEngineState(iState,aError);
    }

// Audio has completed playing the tone
// from MMdaAudioToneObserver
void CMediaClientEngine::MatoPlayComplete(TInt aError)
    {
    __ASSERT_DEBUG(iState == ETonePlaying, Panic(CMediaClientEngine::ENotReady));
    __ASSERT_DEBUG(iEngineObserver, Panic(CMediaClientEngine::ENullObserver));
    
    // Tell observer sound is complete, and clean up
    ToneCleanup();
    iEngineObserver -> HandleEngineState(iState,aError);
    }

// Clean up
void CMediaClientEngine::ToneCleanup()
    {
    iState = EIdle;
    delete iAudioToneUtility;
    iAudioToneUtility = NULL;
    }


//
/************** SOUND FILE PLAYING ******************/
//

// Play a sound file
void CMediaClientEngine::PlayL(const TDesC &aFile)
    {
    __ASSERT_DEBUG(iState == CMediaClientEngine::EIdle, Panic(CMediaClientEngine::EInProgress));
    
    iAudioPlayUtility = CMdaAudioPlayerUtility::NewFilePlayerL(aFile, *this);
    iState = EPlayFilePrepare;
    // This will call back MapcInitComplete() when preparation is complete
    }

// Audio is now prepared to play a file
// from MMdaAudioPlayerCallback
void CMediaClientEngine::MapcInitComplete(TInt aError, 
                                          const TTimeIntervalMicroSeconds& /*aDuration*/)
    {
    __ASSERT_DEBUG(iEngineObserver, Panic(CMediaClientEngine::ENullObserver));
    __ASSERT_DEBUG(iAudioPlayUtility, Panic(CMediaClientEngine::ENullPlayerUtility));
    __ASSERT_DEBUG(iState == EPlayFilePrepare || iState == EGetMetaData1, Panic(CMediaClientEngine::EWrongState));
    
    // If preparation was sucessful, play the tone
    if (aError)
        {
        PlayCleanup();
        iEngineObserver -> HandleEngineState(iState,aError);
        return;
        }
    if (iState == EPlayFilePrepare)
        {
        iState = EPlayFilePlaying;
        iAudioPlayUtility -> SetVolume(iAudioPlayUtility -> MaxVolume()/iVolume);
        iAudioPlayUtility->Play();       
        iEngineObserver -> HandleEngineState(iState,aError);
        return;
        }
    if (iState == EGetMetaData1)
        {
        TInt numEntries = 0;
        iAudioPlayUtility -> GetNumberOfMetaDataEntries(numEntries);
        for (TInt i=0; i<numEntries; i++)
            iMetaData->Append(iAudioPlayUtility->GetMetaDataEntryL(i));
        PlayCleanup();
        iEngineObserver -> HandleEngineState(EGetMetaData2,aError);
        iState = EIdle;
        return;
        }
    }

// Audio has completed playing the tone
// from MMdaAudioPlayerCallback
void CMediaClientEngine::MapcPlayComplete(TInt aError)
    {
    __ASSERT_DEBUG(iState == EPlayFilePlaying, Panic(CMediaClientEngine::ENotReady));
    __ASSERT_DEBUG(iEngineObserver, Panic(CMediaClientEngine::ENullObserver));
    
    // Tell observer sound is complete, and clean up
    PlayCleanup();
    iEngineObserver -> HandleEngineState(iState,aError);
    }

// Clean up
void CMediaClientEngine::PlayCleanup()
    {
    iState = EIdle;
    delete iAudioPlayUtility;
    iAudioPlayUtility = NULL;
    }


//
/************** SOUND RECORDING/CONVERSION ******************/
//

// Get a list of possible record formats
void CMediaClientEngine::RecordTypesL(CDesCArray& aTypeArray)
    {
    // get a list of mmf plug-ins that can read the file's format
    RMMFControllerImplInfoArray controllers;
    CleanupResetAndDestroyPushL(controllers);

    CMMFControllerPluginSelectionParameters* cSelect = CMMFControllerPluginSelectionParameters::NewLC();
    CMMFFormatSelectionParameters* fSelectRead = CMMFFormatSelectionParameters::NewLC();
    CMMFFormatSelectionParameters* fSelectWrite = CMMFFormatSelectionParameters::NewLC();

    RArray<TUid> mediaIds;
    mediaIds.Append(KUidMediaTypeAudio);
    CleanupClosePushL(mediaIds);
    cSelect->SetMediaIdsL(mediaIds, CMMFPluginSelectionParameters::EAllowOnlySuppliedMediaIds);
    cSelect->SetRequiredPlayFormatSupportL(*fSelectRead);
    cSelect->SetRequiredRecordFormatSupportL(*fSelectWrite); // need to specify this as otherwise the supported record formats won't be retrieved
    cSelect->ListImplementationsL(controllers);

    TBuf<10> buf;
    // Loop through each returned plug-in and get their record formats
    for (TInt i=0; i<controllers.Count(); i++)
        {
        const RMMFFormatImplInfoArray& recordInfo =  controllers[i]->RecordFormats();
        // Get array of supported file extensions.
        for (TInt j=0; j<recordInfo.Count(); j++)
            {
            const CDesC8Array& extensions = recordInfo[j]->SupportedFileExtensions();
            // and finally add each extension to the array
            for (TInt k=0; k<extensions.Count(); k++)
                {
                buf.Copy(extensions[k]);
                aTypeArray.AppendL(buf);
                } // end of k
            } // end of j
        } // end of i
    CleanupStack::PopAndDestroy(5);
    } // end of function


// Record into a file
void CMediaClientEngine::RecordL(const TDesC &aFile, const TRecordFormatOptions& aRecordFormatOptions)
    {
    __ASSERT_DEBUG(iState == CMediaClientEngine::EIdle, Panic(CMediaClientEngine::EInProgress));
    
    iRecordFormatOptions = &aRecordFormatOptions;
    // Set up file to record to, the recording format, codec, and settings
    iAudioRecorderUtility = CMdaAudioRecorderUtility::NewL(*this);
    iAudioRecorderUtility -> OpenFileL(aFile);
    iState = ERecordPrepare;
    iEngineObserver -> HandleEngineState(iState,KErrNone);
    // This will call back MoscoStateChangeEvent() when preparation is complete
    }

// Set options and start recording
void CMediaClientEngine::Record2L()
    {
    // Set the options
    iAudioRecorderUtility -> SetAudioDeviceMode(CMdaAudioRecorderUtility::EDefault);
    if (iRecordFormatOptions->iGainSet)
        iAudioRecorderUtility -> SetGain(iRecordFormatOptions->iGain);
    if (iRecordFormatOptions->iBalanceSet)
        iAudioRecorderUtility -> SetPlaybackBalance(iRecordFormatOptions->iBalance);
    if (iRecordFormatOptions->i4CCSet)
        iAudioRecorderUtility -> SetDestinationDataTypeL(iRecordFormatOptions->i4CC);
    if (iRecordFormatOptions->iBitRateSet)
        iAudioRecorderUtility -> SetDestinationBitRateL(iRecordFormatOptions->iBitRate);
    if (iRecordFormatOptions->iSampleRateSet)
        iAudioRecorderUtility -> SetDestinationSampleRateL(iRecordFormatOptions->iSampleRate);
    if (iRecordFormatOptions->iChannelSet)
        iAudioRecorderUtility -> SetDestinationNumberOfChannelsL(iRecordFormatOptions->iChannel);

    // and start recording
    iAudioRecorderUtility -> RecordL();
    iState = ERecording;
    }

// Clean up recorder
void CMediaClientEngine::RecordCleanup()
    {
    iState = EIdle;
    delete iAudioRecorderUtility;
    iAudioRecorderUtility = NULL;
    }

// Gets a recorder for specified target type
CMdaAudioRecorderUtility* CMediaClientEngine::NewRecorderL(const TDesC& aTargetType)
    {
    __ASSERT_DEBUG(iState == CMediaClientEngine::EIdle, Panic(CMediaClientEngine::EInProgress));

    return CRecorderCreator::NewRecorderL(aTargetType);
    }

// ************** CRecorderCreator **************
//

CRecorderCreator::CRecorderCreator()
:CActive(EPriorityStandard)
    {}

CMdaAudioRecorderUtility* CRecorderCreator::NewRecorderL(const TDesC& aTargetType)
    {
    CRecorderCreator* creator = new (ELeave) CRecorderCreator();
    CleanupStack::PushL(creator);
    CMdaAudioRecorderUtility* record = creator->ConstructL(aTargetType);
    CleanupStack::PopAndDestroy(creator);
    return record;
    }

CMdaAudioRecorderUtility* CRecorderCreator::ConstructL(const TDesC& aTargetType)
    {
    _LIT(KDummyName, "\\dummy");
    TFileName name(KDummyName);
    name.Append(aTargetType);

    // create converter
    iRecorderUtility = CMdaAudioRecorderUtility::NewL(*this);
    iRecorderUtility->OpenFileL(name);
    CActiveScheduler::Add(this);
    SetActive();
    CActiveScheduler::Start();
    return iRecorderUtility;
    }

void CRecorderCreator::RunL()
    {
    if (iStatus != KErrNone)
        {
        delete iRecorderUtility;
        iRecorderUtility = NULL;
        }
    CActiveScheduler::Stop();
    }

void CRecorderCreator::DoCancel()
    {
    delete iRecorderUtility;
    iRecorderUtility = NULL;
    CActiveScheduler::Stop();
    }

void CRecorderCreator::MoscoStateChangeEvent(CBase* /*aObject*/, TInt /*aPreviousState*/, 
    TInt /*aCurrentState*/, TInt aErrorCode)
    {
    TRequestStatus* status = &iStatus;
    User::RequestComplete(status, aErrorCode);
    }

// Record/convert events handler
// from MMdaObjectStateChangeObserver
void CMediaClientEngine::MoscoStateChangeEvent(CBase* aObject, 
        TInt /*aPreviousState*/, TInt aCurrentState, TInt aErrorCode)
    {
    __ASSERT_ALWAYS(aObject == iAudioRecorderUtility || aObject == iConvertUtility, Panic(CMediaClientEngine::EWrongState));
    if (aObject == iAudioRecorderUtility)
        {
        __ASSERT_DEBUG(iState == CMediaClientEngine::ERecordPrepare || iState == CMediaClientEngine::ERecording, 
            Panic(CMediaClientEngine::EWrongState));

        if (aErrorCode)
            RecordCleanup();
        else if (aCurrentState == CMdaAudioClipUtility::EOpen)
            Record2L();
        iEngineObserver -> HandleEngineState(iState,aErrorCode);
        }
    // Handle converter event
    if (aObject == iConvertUtility)
        {
        __ASSERT_DEBUG(iState == CMediaClientEngine::EConvert || iState == CMediaClientEngine::EConvertComplete, 
            Panic(CMediaClientEngine::EWrongState));
        // converter has finished
        if (aErrorCode || iState == CMediaClientEngine::EConvertComplete)
            ConvertCleanup();
        else // converter has opened file, move to next stage
            Convert2L();
        iEngineObserver -> HandleEngineState(iState,aErrorCode);
        }
    }

// Get a list of possible destination types
void CMediaClientEngine::DestinationTypesL(const TFileName& aFile, CDesCArray& aTypeArray)
    {
    // get a list of mmf plug-ins that can read the file's format
    RMMFControllerImplInfoArray controllers;
    CleanupResetAndDestroyPushL(controllers);
    CMMFControllerPluginSelectionParameters* cSelect = CMMFControllerPluginSelectionParameters::NewLC();
    CMMFFormatSelectionParameters* fSelectRead = CMMFFormatSelectionParameters::NewLC();
    CMMFFormatSelectionParameters* fSelectWrite = CMMFFormatSelectionParameters::NewLC();
    fSelectRead->SetMatchToFileNameL(aFile); // Specify the source file format
    cSelect->SetRequiredPlayFormatSupportL(*fSelectRead);
    cSelect->SetRequiredRecordFormatSupportL(*fSelectWrite); // need to specify this as otherwise the supported record formats won't be retrieved
    cSelect->ListImplementationsL(controllers);

    TBuf<10> buf;
    TParsePtrC parse(aFile);
    TPtrC ext = parse.Ext();

    // Loop through each returned plug-in and get their record formats
    for (TInt i=0; i<controllers.Count(); i++)
        {
        const RMMFFormatImplInfoArray& recordInfo =  controllers[i]->RecordFormats();
        // Get array of supported file extensions.
        for (TInt j=0; j<recordInfo.Count(); j++)
            {
            const CDesC8Array& extensions = recordInfo[j]->SupportedFileExtensions();
            // and finally add each extension to the array
            for (TInt k=0; k<extensions.Count(); k++)
                {
                buf.Copy(extensions[k]);
                if (buf != ext)
                    aTypeArray.AppendL(buf);
                } // end of k
            } // end of j
        } // end of i
    CleanupStack::PopAndDestroy(4);
    } // end of function

// Create and open a converter
void CMediaClientEngine::ConvertL(const TFileName& aFile, const TDesC& aTargetType, const TFormatOptions& aOptions)
    {
    __ASSERT_DEBUG(iState == CMediaClientEngine::EIdle, Panic(CMediaClientEngine::EInProgress));
    
    iOptions = &aOptions;
    ChangeExtension(aFile, aTargetType, iTargetFile);
    // create converter
    iConvertUtility = CMdaAudioConvertUtility::NewL(*this);
    iConvertUtility->OpenL(aFile, iTargetFile);
    iState = EConvert;
    // This will call back MoscoStateChangeEvent() when converter is open
    }

// Set conversion options and start conversion
void CMediaClientEngine::Convert2L()
    {
    if (iOptions->i4CCSet) iConvertUtility->SetDestinationDataTypeL(iOptions->i4CC);
    if (iOptions->iBitRateSet) iConvertUtility->SetDestinationBitRateL(iOptions->iBitRate);
    if (iOptions->iSampleRateSet) iConvertUtility->SetDestinationSampleRateL(iOptions->iSampleRate);
    if (iOptions->iChannelSet) iConvertUtility->SetDestinationNumberOfChannelsL(iOptions->iChannel);
    iConvertUtility->ConvertL();
    iState = EConvertComplete;
    // This will call back MoscoStateChangeEvent() when conversion is complete
    }

// Clean up converter
void CMediaClientEngine::ConvertCleanup()
    {
    iState = EIdle;
    delete iConvertUtility;
    iConvertUtility = NULL;
    }

//
/************** STREAM PLAYING ******************/
//

// Play a stream
void CMediaClientEngine::PlayL()
    {
    __ASSERT_DEBUG(iState == CMediaClientEngine::EIdle, Panic(CMediaClientEngine::EInProgress));
    iAudioStreamPlayer = CMdaAudioOutputStream::NewL(*this);
    iSettings.iVolume = iAudioStreamPlayer -> MaxVolume()/iVolume;
    iSettings.iChannels = TMdaAudioDataSettings::EChannelsMono;
    iSettings.iSampleRate = TMdaAudioDataSettings::ESampleRate8000Hz;

    iAudioStreamPlayer -> Open(&iSettings);
        // indicates whether the example will use a list to store the sound fragments
    iState = EStreamPrepare;
    iEngineObserver -> HandleEngineState(iState,KErrNone);
    // This will call back MaoscOpenComplete() when preparation is complete
    }

// Audio is now prepared to play a stream
void CMediaClientEngine::MaoscOpenComplete(TInt aError)
    {
    if (aError)
        StreamCleanup();
    else
        iState = EStreamStarted;

    iEngineObserver -> HandleEngineState(iState,aError);
    }

// Audio has accepted data: call back observer so they know to write more data.
// If using a list, just delete the first element
void CMediaClientEngine::MaoscBufferCopied(TInt aError, const TDesC8& /*aBuffer*/)
    {
    // Check for error
    if (aError)
        iState = EStreamError;
    else
        iState = ENextStreamBuf;

    iEngineObserver -> HandleEngineState(iState,aError);
    }

// Stream has finished
void CMediaClientEngine::MaoscPlayComplete(TInt aError)
    {
    if (aError == KErrUnderflow) aError=KErrNone;
    StreamCleanup();
    iEngineObserver -> HandleEngineState(iState,aError);
    }

// Write data to the stream
void CMediaClientEngine::WriteToStreamL(const TDesC8& aData)
    {
    if (iState == CMediaClientEngine::EStreamStarted 
        || iState == CMediaClientEngine::ENextStreamBuf) 
        iAudioStreamPlayer -> WriteL(aData);
    // This will call back MaoscBufferCopied() when the data is copied
    }


// Clean up
void CMediaClientEngine::StreamCleanup()
    {
    iState = EIdle;
    delete iAudioStreamPlayer;
    iAudioStreamPlayer = NULL;
    }


//
// ************** METADATA FOR A FILE **************
//

void CMediaClientEngine::GetMetaDataL(const TFileName& aFile, RMetaDataArray& aMetaData)
    {
    __ASSERT_DEBUG(iState == CMediaClientEngine::EIdle, Panic(CMediaClientEngine::EInProgress));
    
    iAudioPlayUtility = CMdaAudioPlayerUtility::NewFilePlayerL(aFile, *this);
    iMetaData = &aMetaData;
    iState = EGetMetaData1;
    // This will call back MapcInitComplete() when preparation is complete
    }


// Helper classes & functions

//
// ************** CConverterCreator **************
//

CMdaAudioConvertUtility* CMediaClientEngine::NewConverterL(const TFileName& aFile, const TDesC& aTargetType,
    TFileName& aTargetFile)
    {
    __ASSERT_DEBUG(iState == CMediaClientEngine::EIdle, Panic(CMediaClientEngine::EInProgress));

    return CConverterCreator::NewConverterL(aFile, aTargetType, aTargetFile);
    }

CConverterCreator::CConverterCreator()
:CActive(EPriorityStandard)
    {}

CMdaAudioConvertUtility* CConverterCreator::NewConverterL(const TFileName& aFile, const TDesC& aTargetType,
    TFileName& aTargetFile)
    {
    CConverterCreator* creator = new (ELeave) CConverterCreator();
    CleanupStack::PushL(creator);
    CMdaAudioConvertUtility* convert = creator->ConstructL(aFile, aTargetType, aTargetFile);
    CleanupStack::PopAndDestroy(creator);
    return convert;
    }

CMdaAudioConvertUtility* CConverterCreator::ConstructL(const TFileName& aFile, const TDesC& aTargetType,
    TFileName& aTargetFile)
    {
    ChangeExtension(aFile, aTargetType, aTargetFile);
    // create converter
    iConvertUtility = CMdaAudioConvertUtility::NewL(*this);
    iConvertUtility->OpenL(aFile, aTargetFile);
    CActiveScheduler::Add(this);
    SetActive();
    CActiveScheduler::Start();
    return iConvertUtility;
    }

void CConverterCreator::MoscoStateChangeEvent(CBase* /*aObject*/, TInt /*aPreviousState*/, TInt /*aCurrentState*/, TInt aErrorCode)
    {
    TRequestStatus* status = &iStatus;
    User::RequestComplete(status, aErrorCode);
    }

void CConverterCreator::RunL()
    {
    if (iStatus != KErrNone)
        {
        delete iConvertUtility;
        iConvertUtility = NULL;
        }
    CActiveScheduler::Stop();
    }

void CConverterCreator::DoCancel()
    {
    delete iConvertUtility;
    iConvertUtility = NULL;
    CActiveScheduler::Stop();
    }

//
// ************* ChangeExtension *************
//

void ChangeExtension(const TFileName& aFile, const TDesC& aTargetType,
    TFileName& aTargetFile)
    {
    // create target file name as existing file + plus target extension
    aTargetFile = aFile;
    TParsePtr parse(aTargetFile);
    TPtrC ext = parse.Ext();
    aTargetFile.Replace(aTargetFile.Length()-ext.Length(),ext.Length(),aTargetType);
    }
// CMediaClientDocument.cpp
//
// Copyright (c) 2001 Symbian Ltd.  All rights reserved.
//

#include "CMediaClientDocument.h"
#include "CMediaClientAppUi.h"
#include "CMediaClientEngine.h"

#include <eikenv.h>
#include <eikproc.h>

// The document's only job is to own the engine

CMediaClientDocument* CMediaClientDocument::NewL(CEikApplication& aApp)
    {
    CMediaClientDocument* self=new (ELeave) CMediaClientDocument(aApp);
    CleanupStack::PushL(self);
    self->ConstructL();
    CleanupStack::Pop();
    return self;
    }

CMediaClientDocument::CMediaClientDocument(CEikApplication& aApp)
: CEikDocument(aApp)
    {
    }

void CMediaClientDocument::ConstructL()
    {
    iEngine = new (ELeave) CMediaClientEngine(CEikonEnv::Static()->FsSession());
    }

CMediaClientDocument::~CMediaClientDocument()
    {
    delete iEngine;
    }

CMediaClientEngine& CMediaClientDocument::Engine()
    {
    return *iEngine;
    }

//
// CreateAppUiL
//
// Called by the framework to get our AppUi object
CEikAppUi* CMediaClientDocument::CreateAppUiL() 
    {
    return new (ELeave) CMediaClientAppUi;
    }
// MediaClientAppUI.cpp
//
// Copyright (c) 2001 Symbian Ltd.  All rights reserved.
//

#include <AudioClientEx.rsg>
#include "MediaClient.hrh"
#include "CMediaClientAppUi.h"
#include "CMediaClientView.h"
#include "CMediaClientDocument.h"
#include "CMediaClientEngine.h"

#include <eikenv.h>
#include <eikproc.h>
#include <eikfnlab.h>
#include <eiktbar.h>
#include <eikapp.h>
#include <eikdialg.h>
#include <eikcfdlg.h>
#include <eikon.rsg>
#include <coeutils.h>
#include <datastor.h>
#include <eikmfne.h>
#include <eikmenup.h>
#include <eiklabel.h>
#include <eikchlst.h>
#include <badesca.h>

/** View text for error state */
_LIT(KError, "Error");
/** View text for idle state */
_LIT(KIdle, "Ready");
/** View text for tone preparation state  */
_LIT(KTonePrepare,"Initiating");
/** View text for tone playing state  */
_LIT(KTonePlaying, "Playing tone");
/** View text for file preparation state  */
_LIT(KPlayFilePrepare, "Initiating");
/** View text for file playing state  */
_LIT(KPlayFilePlaying, "Playing file");
/** View text for recording preparation state  */
_LIT(KRecordPrepare, "Initiating");
/** View text for recording state  */
_LIT(KRecording, "Recording file");
/** View text for stream preparation state  */
_LIT(KPlayStreamPrepare, "Initiating");
/** View text for stream playing state */
_LIT(KStreamPlaying, "Playing stream");
/** View text for convert state */
_LIT(KConverting, "Converting file");

// Initial object setup
void CMediaClientAppUi::ConstructL() 
    {
    BaseConstructL();

    // set up view
    iAppView = CMediaClientView::NewL(ClientRect(), MediaClientEngine());
    iAppView -> SetDescription(KIdle);
    AddToStackL(iAppView); // app view should go onto control stack

    // set up filename buffer
    _LIT(KDefault,"\\");
    
    TDriveUnit sysDrive (RFs::GetSystemDrive());
    TDriveName sysDriveName (sysDrive.Name());    
    TFileName fileName(sysDriveName);
    fileName.Append(KDefault);
    iFileName = fileName;

    // set up link to engine
    MediaClientEngine().SetObserver(*this);
    iState = CMediaClientEngine::EIdle;

    // An asychronous call back object used to exit when a stream is playing
    // This allows media server time to clean up stream
    TCallBack callBack(CMediaClientAppUi::DoExit, this);
    iExitCallBack = new (ELeave) CAsyncCallBack(callBack,CActive::EPriorityLow);
    }


CMediaClientAppUi::~CMediaClientAppUi() 
    {
    iMetaData.ResetAndDestroy();
    iMetaData.Close();
    StreamSourceCleanUp();
    RemoveFromStack(iAppView);
    delete iAppView;
    delete iExitCallBack;
    }

TInt CMediaClientAppUi::DoExit(TAny* aUI)
    {
    static_cast<CMediaClientAppUi*>(aUI)->Exit();
    return 0;
    }

// Gets engine
CMediaClientEngine& CMediaClientAppUi::MediaClientEngine() const 
    { 
    return (static_cast<CMediaClientDocument*>(Document()))->Engine(); 
    }

// Handle command requests
void CMediaClientAppUi::HandleCommandL(TInt aCommand) 
    {
    switch (aCommand) 
        {
    case EMPCmdPlayTone:
        ToneDialogL();
        break;

    case EMPCmdPlayFile:
        PlayFileDialogL();
        break;

    case EMPCmdRecordFile:
        RecordFileDialogL();
        break;

    case EMPCmdPlayStream:
        StreamSourceCleanUp();
        iAudioStreamSource = CAudioStreamSource::NewL(iEikonEnv->FsSession(),
            MediaClientEngine());
        MediaClientEngine().PlayL();
        break;

    case EMPCmdPlayStreamWithQueue:
        StreamSourceCleanUp();
        iAudioStreamSourceWithQueue = CAudioStreamSourceWithQueue::NewL(
            iEikonEnv->FsSession(),
            MediaClientEngine());
        MediaClientEngine().PlayL();
        break;

    case EMPCmdShowMetaData:
        FileMetaDataL();
        break;

    case EMPCmdConvertFile:
        ConvertFileDialogL();
        break;

    case EMPCmdVolumeLow:
        MediaClientEngine().SetVolume(CMediaClientEngine::EQuiet);
        break;

    case EMPCmdVolumeMedium:
        MediaClientEngine().SetVolume(CMediaClientEngine::EMedium);
        break;

    case EMPCmdVolumeHigh:
        MediaClientEngine().SetVolume(CMediaClientEngine::ELoud);
        break;

    case EMPCmdStop:
        MediaClientEngine().Stop();
        StreamSourceCleanUp();
        break;

    case EEikCmdExit: 
        MediaClientEngine().Stop();
        if ( (iState == CMediaClientEngine::EStreamStarted)
            || (iState == CMediaClientEngine::ENextStreamBuf)
            || (iState == CMediaClientEngine::EStreamError)
            || (iState == CMediaClientEngine::EStreamStopping) )
            iExitCallBack->CallBack();
        else
            Exit();
        break;
        }
    }

void CMediaClientAppUi::StreamSourceCleanUp()
    {
    delete iAudioStreamSource;
    iAudioStreamSource = NULL;
    delete iAudioStreamSourceWithQueue;
    iAudioStreamSourceWithQueue = NULL;
    }

// Dialog to select tone to play
void CMediaClientAppUi::ToneDialogL()
    {
    TInt frequency;
    TTimeIntervalMicroSeconds duration;

    if (CToneDialog::RunDlgLD(frequency,duration))
        MediaClientEngine().PlayL(frequency,duration);
    }

// Dialog to select file to play
void CMediaClientAppUi::PlayFileDialogL()
    {
    CEikFileOpenDialog* dialog = new (ELeave) CEikFileOpenDialog(&iFileName);
    if (dialog->ExecuteLD(R_EIK_DIALOG_FILE_OPEN))
        MediaClientEngine().PlayL(iFileName);
    }

// Dialog to select file to record to
void CMediaClientAppUi::RecordFileDialogL()
    {
    if (CRecordDialog::RunDlgLD(iFileName, MediaClientEngine(), iRecordFormatOptions))
        // start conversion
        MediaClientEngine().RecordL(iFileName, iRecordFormatOptions);
        // callback to HandleEngineState when conversion complete
    }

// Query for file metadata
void CMediaClientAppUi::FileMetaDataL()
    {
    // get meta data about the file, which we'll display afterwards
    iMetaData.ResetAndDestroy();
    CEikFileOpenDialog* dialog = new (ELeave) CEikFileOpenDialog(&iFileName);
    if (dialog->ExecuteLD(R_EIK_DIALOG_FILE_OPEN))
        MediaClientEngine().GetMetaDataL(iFileName,iMetaData);
    // callback to HandleEngineState when data got
    }

// Display retrieved metadata
void CMediaClientAppUi::DisplayMetaDataL()
    {
    // set string with metadata information
    _LIT(KSeparator, "\t");
    _LIT(KNewLine, "\n");
    _LIT(KNoMetaData, "No metadata retrieved");
    const TInt KMaxDataField = 1024;
    HBufC* labelText = HBufC::NewLC(KMaxDataField);
    TPtr labelTextPtr = labelText->Des();
    for (TInt i=0; i<iMetaData.Count(); i++)
        {
        // be careful not to overrun descriptor end
        TInt fieldLen = iMetaData[i]->Name().Length() + 2 + iMetaData[i]->Value().Length();
        if (labelTextPtr.Length()+fieldLen < KMaxDataField)
            {
            labelTextPtr.Append(iMetaData[i]->Name());
            labelTextPtr.Append(KSeparator);
            labelTextPtr.Append(iMetaData[i]->Value());
            labelTextPtr.Append(KNewLine);
            }
        }
    if (iMetaData.Count() == 0) *labelText = KNoMetaData;
    iAppView -> SetDescription(*labelText);
    CleanupStack::PopAndDestroy(); // labelText
    }

// File format conversion dialog
void CMediaClientAppUi::ConvertFileDialogL()
    {
    CEikFileOpenDialog* dialog = new (ELeave) CEikFileOpenDialog(&iFileName);
    if (dialog->ExecuteLD(R_EIK_DIALOG_FILE_OPEN))
        if (CConverterDialog::RunDlgLD(iFileName, MediaClientEngine(), iFormatOptions, iTargetType))
            // start conversion
            MediaClientEngine().ConvertL(iFileName, iTargetType, iFormatOptions);
            // callback to HandleEngineState when conversion complete
    }

// Dim menu commands when sound is playing
void CMediaClientAppUi::DynInitMenuPaneL(TInt aResourceId, CEikMenuPane* aMenuPane)
    {
    if (aResourceId == R_AUDIOEX_ACTIONS_MENU)
        {
        TBool dim = (iState != CMediaClientEngine::EIdle);
        aMenuPane -> SetItemDimmed(EMPCmdPlayTone,dim);
        aMenuPane -> SetItemDimmed(EMPCmdRecordFile,dim);
        aMenuPane -> SetItemDimmed(EMPCmdPlayStream,dim);
        aMenuPane -> SetItemDimmed(EMPCmdPlayStreamWithQueue,dim);
        aMenuPane -> SetItemDimmed(EMPCmdPlayFile,dim);
        aMenuPane -> SetItemDimmed(EMPCmdStop,!dim);
        aMenuPane -> SetItemDimmed(EMPCmdConvertFile,dim);
        aMenuPane -> SetItemDimmed(EMPCmdShowMetaData,dim);    
        }
    }

// Call back from the engine to inform the UI of progress
void CMediaClientAppUi::HandleEngineState(CMediaClientEngine::TState aState, 
                                          TInt aError)
    {
    iState = aState;
    if (aError) 
        iAppView -> SetDescription(KError);
    else
        switch(aState)
            {
        case CMediaClientEngine::EIdle:
            iAppView -> SetDescription(KIdle);
            break;
        case CMediaClientEngine::ETonePrepare:
            iAppView -> SetDescription(KTonePrepare);
            break;
        case CMediaClientEngine::ETonePlaying:
            iAppView -> SetDescription(KTonePlaying);
            break;
        case CMediaClientEngine::EPlayFilePrepare:
            iAppView -> SetDescription(KPlayFilePrepare);
            break;
        case CMediaClientEngine::EPlayFilePlaying:
            iAppView -> SetDescription(KPlayFilePlaying);
            break;
        case CMediaClientEngine::EGetMetaData2:
            DisplayMetaDataL();
            iState = CMediaClientEngine::EIdle;
            break;
        case CMediaClientEngine::ERecordPrepare:
            iAppView -> SetDescription(KRecordPrepare);
            break;
        case CMediaClientEngine::EConvert:
            iAppView -> SetDescription(KConverting);
            break;
        case CMediaClientEngine::EConvertComplete:
            iAppView -> SetDescription(KIdle);
            break;
        case CMediaClientEngine::ERecording:
            iAppView -> SetDescription(KRecording);
            break;
        case CMediaClientEngine::EStreamPrepare:
            iAppView -> SetDescription(KPlayStreamPrepare);
            break;
        case CMediaClientEngine::EStreamStarted:
            if (iAudioStreamSourceWithQueue)
                iAudioStreamSourceWithQueue->Next();
            else
                iAudioStreamSource->Next();
            iAppView -> SetDescription(KStreamPlaying);
            break;
        case CMediaClientEngine::ENextStreamBuf:
            if (iAudioStreamSourceWithQueue)
                iAudioStreamSourceWithQueue->RemoveFromQueue();
            else
                iAudioStreamSource->Next();
            break;
        default:
            break;
            }

    // update the UI to reflect the state
    iAppView -> DrawNow();
    }

// ********************************
// CRecordDialog - sets conversion format
// ********************************

const TInt KFormatOptionsNotSet = -1;

TBool CRecordDialog::RunDlgLD(
    TFileName& aFileName, CMediaClientEngine& aMediaClientEngine, // input parameters
    TRecordFormatOptions& aRecordFormatOptions) 
    {
    CRecordDialog* dialog = new (ELeave) CRecordDialog(aFileName, aMediaClientEngine, aRecordFormatOptions);
    return (dialog->ExecuteLD(R_FORMAT_DIALOG));
    }

CRecordDialog::CRecordDialog(TFileName& aFileName, CMediaClientEngine& aMediaClientEngine,
    TRecordFormatOptions& aRecordFormatOptions)
    :iFileName(aFileName), 
    iMediaClientEngine(aMediaClientEngine),
    iRecordFormatOptions(aRecordFormatOptions),
    iOptionsSetFor(KFormatOptionsNotSet)
    {
    }

CRecordDialog::~CRecordDialog()
    {
    }

void CRecordDialog::PreLayoutDynInitL()
    {
    // set choice list with destination types
    CDesCArrayFlat* array = new (ELeave) CDesCArrayFlat(4);
    CleanupStack::PushL(array);
    // get array of conversion types
    iMediaClientEngine.RecordTypesL(*array);
    // set choicelist
    CEikChoiceList* typesList = static_cast<CEikChoiceList*>(Control(ECvtTypeField));
    typesList->SetArrayL(array);
    CleanupStack::Pop(array);
    }

void CRecordDialog::HandleControlStateChangeL(TInt aControlId)
    {
    // Unset format options if the format type changes
    if (aControlId == ECvtTypeField)
        iOptionsSetFor = KFormatOptionsNotSet;
    }

TBool CRecordDialog::OkToExitL(TInt aKeycode)
    {
    _LIT(KRecordFile, "\\record-output");
    CEikChoiceList* typesList = static_cast<CEikChoiceList*>(Control(ECvtTypeField));
    
    TDriveUnit sysDrive (RFs::GetSystemDrive());
    TDriveName sysDriveName (sysDrive.Name());    
    TFileName fileName(sysDriveName);
    fileName.Append(KRecordFile);
    iFileName = fileName;
    
    TPtrC type = typesList->Array()->MdcaPoint(typesList->CurrentItem());
    iFileName.Append(type);

    // if format options button pressed, show dialog
    if (aKeycode == EFormatOptions)
        {
        if (CRecordFormatOptionsDialog::RunDlgLD(type, iMediaClientEngine, iRecordFormatOptions)) 
            iOptionsSetFor = typesList->CurrentItem();
        return EFalse;
        }
    
    return ETrue;
    }


// ********************************
// CRecordFormatOptionsDialog - sets conversion format options
// ********************************

TBool CRecordFormatOptionsDialog::RunDlgLD(const TDesC& aTargetType, 
    CMediaClientEngine& aMediaClientEngine, TRecordFormatOptions& aFormatOptions)
    {
    CRecordFormatOptionsDialog* dialog = new (ELeave) CRecordFormatOptionsDialog(aTargetType, 
        aMediaClientEngine, aFormatOptions);
    dialog->ConstructL();
    return (dialog->ExecuteLD(R_RECORD_OPTIONS_DIALOG));
    }

CRecordFormatOptionsDialog::CRecordFormatOptionsDialog(const TDesC& aTargetType, 
    CMediaClientEngine& aMediaClientEngine, TRecordFormatOptions& aFormatOptions)
    :CFormatOptionsDialog(iBuf, aTargetType, aMediaClientEngine, aFormatOptions),
    iRecordFormatOptions(aFormatOptions)
    {
    aFormatOptions.Reset();
    }

void CRecordFormatOptionsDialog::ConstructL()
    {
    // Get a recorder for the format
    CMdaAudioRecorderUtility* recorder = iMediaClientEngine.NewRecorderL(iTargetType);
    
    if (recorder == NULL) User::Leave(KErrNotSupported);
    // Read available options
    CleanupStack::PushL(recorder);
    iMaxGain = recorder -> MaxGain();

    TRAPD(err,recorder -> GetSupportedDestinationDataTypesL(iDataTypes));
    if (err != KErrNotSupported) User::LeaveIfError(err);
    TRAP(err,recorder -> GetSupportedBitRatesL(iBitRates));
    if (err != KErrNotSupported) User::LeaveIfError(err);
    TRAP(err,recorder -> GetSupportedSampleRatesL(iSampleRates));
    if (err != KErrNotSupported) User::LeaveIfError(err);
    TRAP(err,recorder -> GetSupportedNumberOfChannelsL(iChannels));
    if (err != KErrNotSupported) User::LeaveIfError(err);
    CleanupStack::PopAndDestroy(recorder);
    }

CRecordFormatOptionsDialog::~CRecordFormatOptionsDialog()
    {
    }

void CRecordFormatOptionsDialog::PreLayoutDynInitL()
    {
    // set up bit rate, sample, channel, codec lists
    CFormatOptionsDialog::PreLayoutDynInitL();
    // add gain and balance, which are recording specific parameters
    CEikNumberEditor* gain = static_cast<CEikNumberEditor*>(Control(EGain));
    gain->SetMinimumAndMaximum(0,iMaxGain);
    gain->SetNumber(iMaxGain/2);
    CEikNumberEditor* balance = static_cast<CEikNumberEditor*>(Control(EBalance));
    balance->SetMinimumAndMaximum(KMMFBalanceMaxLeft,KMMFBalanceMaxRight);
    balance->SetNumber(KMMFBalanceCenter);
    }

TBool CRecordFormatOptionsDialog::OkToExitL(TInt /*aKeycode*/)
    {
    // get bit rate, sample, channel, codec settings
    CFormatOptionsDialog::OkToExitL(0);
    // get gain and balance, which are recording specific parameters
    CEikNumberEditor* gain = static_cast<CEikNumberEditor*>(Control(EGain));
    iRecordFormatOptions.iGain = gain->Number();
    CEikNumberEditor* balance = static_cast<CEikNumberEditor*>(Control(EBalance));
    iRecordFormatOptions.iBalance = balance->Number();
    iRecordFormatOptions.iGainSet = iRecordFormatOptions.iBalanceSet = ETrue;

    return ETrue;
    }


// ********************************
// CConverterDialog - sets conversion format
// ********************************

TBool CConverterDialog::RunDlgLD(
    const TFileName& aFileName, CMediaClientEngine& aMediaClientEngine, // input parameters
    TFormatOptions& aFormatOptions, TDes& aTargetType) 
    {
    CConverterDialog* dialog = new (ELeave) CConverterDialog(aFileName, aMediaClientEngine, aFormatOptions, aTargetType);
    return (dialog->ExecuteLD(R_FORMAT_DIALOG));
    }

CConverterDialog::CConverterDialog(const TFileName& aFileName, CMediaClientEngine& aMediaClientEngine,
    TFormatOptions& aFormatOptions, TDes& aTargetType)
    :iFileName(aFileName), 
    iMediaClientEngine(aMediaClientEngine),
    iFormatOptions(aFormatOptions),
    iTargetType(aTargetType),
    iOptionsSetFor(KFormatOptionsNotSet)
    {
    }

CConverterDialog::~CConverterDialog()
    {
    }

void CConverterDialog::PreLayoutDynInitL()
    {
    // set choice list with destination types
    CDesCArrayFlat* array = new (ELeave) CDesCArrayFlat(4);
    CleanupStack::PushL(array);
    // get array of conversion types
    iMediaClientEngine.DestinationTypesL(iFileName, *array);
    // set choicelist
    CEikChoiceList* typesList = static_cast<CEikChoiceList*>(Control(ECvtTypeField));
    typesList->SetArrayL(array);
    CleanupStack::Pop(array);
    }

void CConverterDialog::HandleControlStateChangeL(TInt aControlId)
    {
    // Unset format options if the format type changes
    if (aControlId == ECvtTypeField)
        iOptionsSetFor = KFormatOptionsNotSet;
    }

TBool CConverterDialog::OkToExitL(TInt aKeycode)
    {
    CEikChoiceList* typesList = static_cast<CEikChoiceList*>(Control(ECvtTypeField));
    iTargetType = typesList->Array()->MdcaPoint(typesList->CurrentItem());

    // if format options button pressed, show dialog
    if (aKeycode == EFormatOptions)
        {
        if (CFormatOptionsDialog::RunDlgLD(iFileName, iTargetType, iMediaClientEngine, iFormatOptions)) 
            iOptionsSetFor = typesList->CurrentItem();
        return EFalse;
        }
    
    return ETrue;
    }


// ********************************
// CFormatOptionsDialog - sets conversion format options
// ********************************

TBool CFormatOptionsDialog::RunDlgLD(const TFileName& aInputFile, const TDesC& aTargetType, 
    CMediaClientEngine& aMediaClientEngine, TFormatOptions& aFormatOptions)
    {
    CFormatOptionsDialog* dialog = new (ELeave) CFormatOptionsDialog(aInputFile, aTargetType, 
        aMediaClientEngine, aFormatOptions);
    dialog->ConstructL();
    return (dialog->ExecuteLD(R_CONVERT_OPTIONS_DIALOG));
    }

CFormatOptionsDialog::CFormatOptionsDialog(const TFileName& aInputFile, const TDesC& aTargetType, 
    CMediaClientEngine& aMediaClientEngine, TFormatOptions& aFormatOptions)
    :iFileName(aInputFile), iTargetType(aTargetType), iMediaClientEngine(aMediaClientEngine),
    iFormatOptions(aFormatOptions)
    {
    iFormatOptions.Reset();
    }

void CFormatOptionsDialog::ConstructL()
    {
    // Get a converter for the format
    TFileName targetFile; // don't actually use this
    CMdaAudioConvertUtility* converter = iMediaClientEngine.NewConverterL(iFileName, iTargetType, targetFile);
    if (converter == NULL) User::Leave(KErrNotSupported);
    // Read available options
    CleanupStack::PushL(converter);
    TRAPD(err,converter -> GetSupportedDestinationDataTypesL(iDataTypes));
    if (err != KErrNotSupported) User::LeaveIfError(err);
    TRAP(err,converter -> GetSupportedConversionBitRatesL(iBitRates));
    if (err != KErrNotSupported) User::LeaveIfError(err);
    TRAP(err,converter -> GetSupportedConversionSampleRatesL(iSampleRates));
    if (err != KErrNotSupported) User::LeaveIfError(err);
    TRAP(err,converter -> GetSupportedConversionNumberOfChannelsL(iChannels));
    if (err != KErrNotSupported) User::LeaveIfError(err);
    CleanupStack::PopAndDestroy(converter);
    }

CFormatOptionsDialog::~CFormatOptionsDialog()
    {
    iDataTypes.Close();
    iBitRates.Close();
    iSampleRates.Close();
    iChannels.Close();
    }

void CFormatOptionsDialog::PreLayoutDynInitL()
    {
    // Set dialog fields - these 3 are all TUint lists
    TUintChoiceList(EBitRates, iBitRates);
    TUintChoiceList(ESampleRates, iSampleRates);
    TUintChoiceList(EChannels, iChannels);
    // Codecs list is more complicated
    CDesCArrayFlat* array = new (ELeave) CDesCArrayFlat(4);
    CleanupStack::PushL(array);
    HBufC8* buf8 = HBufC8::NewLC(10);
    TPtr8 ptr = buf8->Des();
    TBuf<5> buf;
    for (TInt i=0; i<iDataTypes.Count(); i++)
        {
        iDataTypes[i].FourCC(&ptr);
        buf.Copy(ptr);
        array->AppendL(buf);
        }
    CEikChoiceList* list = static_cast<CEikChoiceList*>(Control(ECodecs));
    list->SetArrayL(array);  
    CleanupStack::PopAndDestroy(); //buf8
    CleanupStack::Pop(); //array
    }

void CFormatOptionsDialog::TUintChoiceList(TInt aResourceId, const RArray<TUint>& aArray)
    {
    CEikChoiceList* list = static_cast<CEikChoiceList*>(Control(aResourceId));
    if (aArray.Count()==0) 
        {
        list->MakeVisible(EFalse);
        return;
        }
    TBuf <10> buf;
    CDesCArrayFlat* array = new (ELeave) CDesCArrayFlat(4);
    CleanupStack::PushL(array);
    for (TInt i=0; i<aArray.Count(); i++)
        {
        buf.Num(aArray[i]);
        array->AppendL(buf);
        }
    list->SetArrayL(array);  
    CleanupStack::Pop(); //array
    }

TBool CFormatOptionsDialog::OkToExitL(TInt /*aKeycode*/)
    {
    CEikChoiceList* list = static_cast<CEikChoiceList*>(Control(ECodecs));
    if (list->IsVisible()) 
        {iFormatOptions.i4CC = iDataTypes[list->CurrentItem()]; iFormatOptions.i4CCSet = ETrue; }
    list = static_cast<CEikChoiceList*>(Control(EBitRates));
    if (list->IsVisible()) 
        {iFormatOptions.iBitRate = iBitRates[list->CurrentItem()]; iFormatOptions.iBitRateSet = ETrue; }
    list = static_cast<CEikChoiceList*>(Control(ESampleRates));
    if (list->IsVisible()) 
        {iFormatOptions.iSampleRate = iSampleRates[list->CurrentItem()]; iFormatOptions.iSampleRateSet = ETrue; }
    list = static_cast<CEikChoiceList*>(Control(EChannels));
    if (list->IsVisible()) 
        {iFormatOptions.iChannel = iChannels[list->CurrentItem()]; iFormatOptions.iChannelSet = ETrue; }
    return ETrue;
    }


// ********************************
// CToneDialog - sets tone to play
// ********************************

TBool CToneDialog::RunDlgLD(TInt& aFrequency, TTimeIntervalMicroSeconds& aDuration)
    {
    CEikDialog* dialog = new (ELeave) CToneDialog(aFrequency,aDuration);
    return (dialog->ExecuteLD(R_TONE_DIALOG));
    }

CToneDialog::CToneDialog(TInt& aFrequency, TTimeIntervalMicroSeconds& aDuration)
    :iFrequency(aFrequency), 
    iDuration(aDuration)
    {
    }

TBool CToneDialog::OkToExitL(TInt /*aKeycode*/)
    {
    iFrequency = STATIC_CAST(CEikNumberEditor*,Control(EFrequencyField))->Number();
    iDuration = STATIC_CAST(CEikNumberEditor*,Control(EDurationField))->Number() * 1000000;
    return ETrue;
    }

// ********************************
// CAudioStreamSource - an artificial audio stream source
// It asynchronously gets slices from a file and feeds them to the engine
// ********************************

// File to read
_LIT(KSourceName, "\\testing123.raw");

CAudioStreamSource* CAudioStreamSource::NewL(RFs& aFs,CMediaClientEngine& aEngine)
    {
    CAudioStreamSource* self=new(ELeave) CAudioStreamSource(aFs,aEngine);
    CleanupStack::PushL(self);
    self->ConstructL();
    CleanupStack::Pop();
    return self;
    }

CAudioStreamSource::CAudioStreamSource(RFs& aFs, CMediaClientEngine& aEngine)
    :
    CActive(CActive::EPriorityStandard),
    iFs(aFs),
    iEngine(aEngine)
    {
    }

void CAudioStreamSource::ConstructL()
    {
    // Base class second-phase construction.
    // Open the source file
    TDriveUnit sysDrive (RFs::GetSystemDrive());
    TDriveName sysDriveName (sysDrive.Name());    
    TFileName fileName(sysDriveName);
    fileName.Append(KSourceName);
    
    User::LeaveIfError(iSourceFile.Open(iFs,fileName,EFileRead));
    CActiveScheduler::Add(this);
    }

void CAudioStreamSource::RunL()
    {
    // Read a slice from the source file
    User::LeaveIfError(iSourceFile.Read(iBuf));
    // If not at end of file, write the slice to the stream
    if (iBuf.Length())
        iEngine.WriteToStreamL(iBuf);
    }

void CAudioStreamSource::Next()
    {
    TRequestStatus * status = &iStatus;
    User::RequestComplete(status, KErrNone);
    SetActive();
    }

void CAudioStreamSource::DoCancel()
    {
    }

CAudioStreamSource::~CAudioStreamSource()
    {
    Cancel();
    // Close the source file
    iSourceFile.Close();
    }

// ********************************
// CAudioStreamSourceWithQueue - an artificial audio stream source.
// It gets slices from a file at a constant rate, using a timer,
// stores them in a queue then feeds them to the engine.
// They are deleted from the queue after they have been received by 
// the media framework
// ********************************

CAudioStreamSourceWithQueue* CAudioStreamSourceWithQueue::NewL(RFs& aFs,CMediaClientEngine& aEngine)
    {
    CAudioStreamSourceWithQueue* self=new(ELeave) CAudioStreamSourceWithQueue(aFs,aEngine);
    CleanupStack::PushL(self);
    self->ConstructL();
    CleanupStack::Pop();
    return self;
    }

CAudioStreamSourceWithQueue::CAudioStreamSourceWithQueue(RFs& aFs, CMediaClientEngine& aEngine)
    :
    CTimer(CActive::EPriorityStandard),
    iFs(aFs),
    iEngine(aEngine),
    iQueue(CSoundSlice::iOffset),
    iQueueIter(iQueue)
    {
    }

void CAudioStreamSourceWithQueue::ConstructL()
    {
    // Base class second-phase construction.
    CTimer::ConstructL(); 
    // Open the source file
    User::LeaveIfError(iSourceFile.Open(iFs,KSourceName,EFileRead));
    CActiveScheduler::Add(this);
    }


CAudioStreamSourceWithQueue::~CAudioStreamSourceWithQueue()
    {
    Cancel();
    CSoundSlice* soundSlice;
    iQueueIter.SetToFirst();
    while ((soundSlice=iQueueIter++) != NULL)
            {
            iQueue.Remove(*soundSlice);
            delete soundSlice;
            };
    iSourceFile.Close();
    }
    
void CAudioStreamSourceWithQueue::Next()
    {
    // Request a 1/10 second wait
    After(100000);
    }
    

void CAudioStreamSourceWithQueue::DoCancel()
    {
    CTimer::DoCancel();
    }
    
void CAudioStreamSourceWithQueue::RunL()
    {
    // Read a slice from the source file
    User::LeaveIfError(iSourceFile.Read(iBuf));
    // If not at end of file, store the slice until it is safe to delete it
    // and write to the stream.
    // It can be deleted when MaoscBufferCopied() is called, 
    // to indicate that it has been copied to the server.
    if (iBuf.Length())
        {
        iQueue.AddLast(*CSoundSlice::NewL(iBuf)); // Store it in the linked list, at end
        iEngine.WriteToStreamL(iQueue.Last()->GetData());
        // This will call back MaoscBufferCopied() when the data is copied
        // and reissue the wait request
        After(100000); // wait 1/10 second
        }
    }

void CAudioStreamSourceWithQueue::RemoveFromQueue()
    {
    if (!(iQueue.IsEmpty()))
        {
        CSoundSlice* soundSlice=iQueue.First();
        iQueue.Remove(*soundSlice);
        delete soundSlice;
        }
    }

// ********************************
// CSoundSlice
// ********************************

const TInt CSoundSlice::iOffset = _FOFF(CSoundSlice,iLink);

CSoundSlice* CSoundSlice::NewL(const TDesC8& aData)
    {
    CSoundSlice* self=new(ELeave) CSoundSlice;
    CleanupStack::PushL(self);
    self->ConstructL(aData);
    CleanupStack::Pop();
    return self;
    }

void CSoundSlice::ConstructL(const TDesC8& aData)
    {
    iData = aData.AllocL();
    }

CSoundSlice::CSoundSlice()
    {}

CSoundSlice::~CSoundSlice()
    {
    delete iData;
    }


const TDesC8& CSoundSlice::GetData()
    {
    return *iData;
    }
// CMediaClientApplication.cpp
//
// Copyright (c) 2001-2005 Symbian Software Ltd.  All rights reserved.
//

#include "CMediaClientApplication.h"
#include "CMediaClientDocument.h"
#include <eikstart.h>

/** Application UID 
@private*/
const TUid KUidMediaClientApp = { 0x101F81D8 };


TUid CMediaClientApplication::AppDllUid() const 
    {
    return KUidMediaClientApp;
    }

CApaDocument* CMediaClientApplication::CreateDocumentL() 
    {
    //User::SetJustInTime(EFalse);
    return CMediaClientDocument::NewL(*this);
    }


//
// EXPORTed functions
//
// Globals the App architecture/OS use to access this application


EXPORT_C CApaApplication* NewApplication() 
    {
    return new CMediaClientApplication;
    }

GLDEF_C TInt E32Main()
    {
    return EikStart::RunApplication(NewApplication);
    }
/ MediaClient.hrh
//
// Copyright (c) 2001-2005 Symbian Software Ltd.  All rights reserved.

#ifndef MEDIACLIENTHRH
#define MEDIACLIENTHRH

/** Menu commands */
enum TMediaClientCommands
    {
    /** Play tone command */
    EMPCmdPlayTone=100,
    /** Stop command */
    EMPCmdStop,
    /** Play file command */
    EMPCmdPlayFile,
    /** Record command */
    EMPCmdRecordFile,
    /** Play stream command */
    EMPCmdPlayStream,
    /** Play stream with queue command */
    EMPCmdPlayStreamWithQueue,
    /** Show meta data */
    EMPCmdShowMetaData,
    /** Convert file command */
    EMPCmdConvertFile,
    /** Volume high command */
    EMPCmdVolumeHigh,
    /** Volume medium command */
    EMPCmdVolumeMedium,
    /** Volume low command */
    EMPCmdVolumeLow,
    /** Gain high command */
    EMPCmdGainHigh,
    /** Gain medium command */
    EMPCmdGainMedium,
    /** Gain low command */
    EMPCmdGainLow
    };

/** Tone dialog fields */
enum {
    /** Frequency field */
    EFrequencyField  = 1000,
    /** Duration field */
    EDurationField
    };

/** Record dialog fields */
enum {
    ECvtTypeField  = 1000
    };

/** Format options dialog fields */
enum {
    ECodecs = 1000,
    EBitRates,
    ESampleRates,
    EChannels,
    EGain,
    EBalance
    };

// Button ID
enum {
    EFormatOptions = 1000
    };

#endif
// AudioClientEx.mmp
//
// Copyright (c) 2001-2005 Symbian Software Ltd.  All rights reserved.
//

TARGET          AudioClientEx.exe
TARGETTYPE      exe
UID             0x100039CE 0x101F81D8
VENDORID 0x70000001

CAPABILITY UserEnvironment

sourcepath  .
SOURCE          CMediaClientApplication.cpp
SOURCE          CMediaClientAppUI.cpp 
SOURCE          CMediaClientDocument.cpp 
SOURCE          CMediaClientView.cpp
SOURCE          CMediaClientEngine.cpp

START RESOURCE AudioClientEx.rss
TARGETPATH     \resource\apps
HEADER
END

SYSTEMINCLUDE   \epoc32\include
SYSTEMINCLUDE \epoc32\include\techview

// core libraries
LIBRARY         euser.lib 
LIBRARY         apparc.lib 
LIBRARY         ws32.lib 
LIBRARY         cone.lib 
LIBRARY         efsrv.lib 
LIBRARY         bafl.lib
// UI libraries
LIBRARY         eikcore.lib
LIBRARY         eikdlg.lib 
LIBRARY         eikcoctl.lib 
LIBRARY         eikfile.lib
LIBRARY         eikctl.lib
// media libaries
LIBRARY         mediaclientaudio.lib
LIBRARY         mediaclientaudiostream.lib
LIBRARY         MmfControllerFramework.lib

START RESOURCE AudioClientEx_reg.rss
TARGETPATH     \private\10003a3f\apps
END
Bld.inf

PRJ_MMPFILES
AudioClientEx.mmp

PRJ_EXPORTS
testing123.raw ..\winscw\c\testing123.raw
note.wav ..\winscw\c\note.wav
/ AudioClientEx.rss
//
// Copyright (c) 2001 Symbian Ltd.  All rights reserved.
//

NAME MEDI

#include <eikon.rh>
#include <eikon.rsg>

#include "MediaClient.hrh"


RESOURCE RSS_SIGNATURE { }

RESOURCE TBUF { buf="Audio"; }

RESOURCE EIK_APP_INFO
    {
    menubar=r_mediaplay_menubar;
    }

// The menubar
RESOURCE MENU_BAR r_mediaplay_menubar
    {
    titles=
        {
        MENU_TITLE { menu_pane=r_audioex_actions_menu; txt="Actions"; },
        MENU_TITLE { menu_pane=r_audioex_options_menu; txt="Volume"; }
        };
    }

// Actions menu
RESOURCE MENU_PANE r_audioex_actions_menu
    {
    items=
        {
        MENU_ITEM { command=EMPCmdPlayTone; txt="Play tone"<KEllipsis>;},
        MENU_ITEM { command=EMPCmdPlayFile; txt="Play file"<KEllipsis>;},
        MENU_ITEM { command=EMPCmdRecordFile; txt="Record file"<KEllipsis>;},
        MENU_ITEM { command=EMPCmdPlayStream; txt="Play stream";},
        MENU_ITEM { command=EMPCmdPlayStreamWithQueue; txt="Play stream with queue";},
        MENU_ITEM { command=EMPCmdShowMetaData; txt="Show meta data"<KEllipsis>;},
        MENU_ITEM { command=EMPCmdConvertFile; txt="Convert file"<KEllipsis>;},
        MENU_ITEM { command=EMPCmdStop; txt="Stop"; flags = EEikMenuItemSeparatorAfter;},
        MENU_ITEM { command=EEikCmdExit; txt="Close"; }
        };
    }

// Options menu
RESOURCE MENU_PANE r_audioex_options_menu
    {
    items=
        {
        MENU_ITEM { command=EMPCmdVolumeHigh; txt="High volume";},
        MENU_ITEM { command=EMPCmdVolumeMedium; txt="Medium volume";},
        MENU_ITEM { command=EMPCmdVolumeLow; txt="Low volume";}
        };
    }

// The file record\conversion dialog
RESOURCE DIALOG r_format_dialog
    {
    title="Target file type";
    buttons=r_option_dialog_buttons;
    flags=EEikDialogFlagWait;
    items=
        {
        DLG_LINE
            {
            type=EEikCtChoiceList;
            prompt="Target type:";
            id=ECvtTypeField;
            control=CHOICELIST
                {
                };
            }
        };
    }

// The conversion format options dialog
RESOURCE DIALOG r_record_options_dialog
    {
    title="Format options";
    buttons=R_EIK_BUTTONS_CANCEL_OK;
    flags=EEikDialogFlagWait;
    items=
        {
        DLG_LINE
            {
            type=EEikCtChoiceList;
            prompt="Codecs:";
            id=ECodecs;
            control=CHOICELIST
                {
                };
            },
        DLG_LINE
            {
            type=EEikCtChoiceList;
            prompt="Bit rates:";
            id=EBitRates;
            control=CHOICELIST
                {
                };
            },
        DLG_LINE
            {
            type=EEikCtChoiceList;
            prompt="Sample rates:";
            id=ESampleRates;
            control=CHOICELIST
                {
                };
            },
        DLG_LINE
            {
            type=EEikCtChoiceList;
            prompt="Channels:";
            id=EChannels;
            control=CHOICELIST
                {
                };
            },
        DLG_LINE
            {
            type=EEikCtNumberEditor;
            prompt="Gain:";
            id=EGain;
            control=NUMBER_EDITOR
                {
                min = -1000;
                max = 1000;
                };
            },
        DLG_LINE
            {
            type=EEikCtNumberEditor;
            prompt="Balance:";
            id=EBalance;
            control=NUMBER_EDITOR
                {
                min = -1000;
                max = 1000;
                };
            }
        };
    }

// The conversion format options dialog
RESOURCE DIALOG r_convert_options_dialog
    {
    title="Target options";
    buttons=R_EIK_BUTTONS_CANCEL_OK;
    flags=EEikDialogFlagWait;
    items=
        {
        DLG_LINE
            {
            type=EEikCtChoiceList;
            prompt="Codecs:";
            id=ECodecs;
            control=CHOICELIST
                {
                };
            },
        DLG_LINE
            {
            type=EEikCtChoiceList;
            prompt="Bit rates:";
            id=EBitRates;
            control=CHOICELIST
                {
                };
            },
        DLG_LINE
            {
            type=EEikCtChoiceList;
            prompt="Sample rates:";
            id=ESampleRates;
            control=CHOICELIST
                {
                };
            },
        DLG_LINE
            {
            type=EEikCtChoiceList;
            prompt="Channels:";
            id=EChannels;
            control=CHOICELIST
                {
                };
            }
        };
    }


RESOURCE DLG_BUTTONS r_option_dialog_buttons
    {
    buttons=
        {
        DLG_BUTTON { id=EFormatOptions; button=CMBUT {txt="Options";}; flags=0; },
        DLG_BUTTON { id=EEikBidCancel; button=CMBUT {txt="Cancel";}; hotkey=EEikBidCancel; flags=0; },
        DLG_BUTTON { id=EEikBidOk; button=CMBUT {txt="OK";}; hotkey=EEikBidOk; flags=0; }
        };
    }

// The tone select dialog
RESOURCE DIALOG r_tone_dialog
    {
    title="Select tone";
    buttons=R_EIK_BUTTONS_CANCEL_OK;
    flags=EEikDialogFlagWait;
    items=
        {
        DLG_LINE
            {
            type=EEikCtNumberEditor;
            prompt="Frequency";
            id=EFrequencyField;
            control=NUMBER_EDITOR
                {
                min = 0;
                max = 1000;
                };
            },
        DLG_LINE
            {
            type=EEikCtNumberEditor;
            prompt="Duration (seconds)";
            id=EDurationField;
            control=NUMBER_EDITOR
                {
                min = 1;
                max = 10;
                };
            }
        };
    }
// Copyright (c) 2001-2005 Symbian Software Ltd.  All rights reserved.

#include <appinfo.rh>

UID2 KUidAppRegistrationResourceFile
UID3 0x101F81D8 // application UID
RESOURCE APP_REGISTRATION_INFO
    {
    app_file = "AudioClientEx";
    }

[Top]


Example Code

[Top]


Description

AudioClientEx demonstrates the use of the Multimedia Framework's client audio interfaces. It allows the user to:

The Multimedia Framework APIs are called by the example's engine class, CMediaClientEngine. This also implements the various callback interfaces required by the APIs in order to report progress and error conditions. Each of the play options (tone, file, stream) is handled by a separate overload of the PlayL() function. Recording is started through RecordL(). The current operation can always be terminated through Stop().

The engine is driven through the example's UI class CMediaClientAppUi. The engine notifies the UI of significant events by calling its HandleEngineState() function. The UI uses this to update text describing the current activity appropriately.

Interaction between the UI and engine is in most cases simple. Streaming however is more complex. To simulate a streaming data source (e.g. an audio stream being received over the Internet), the UI creates a timer object that periodically passes slices of data (read from a file) to the engine.

[Top]


Usage

Commands are given to the example through its Actions and Options menus. For tone play, the user is asked to specify a pitch and duration; for file play, conversion, and recording, the user is asked to specify a file, and any available format options. The audio data source for streaming in the example is a preset file, so this command does not require further input.

A sample file, note.wav is put by the example on the emulator's C drive for file play.