devsound/devsoundrefplugin/src/swcodecwrapper/mmfswaudioinput.cpp
author hgs
Fri, 08 Oct 2010 19:40:43 +0100
changeset 0 79dd3e2336a0
permissions -rw-r--r--
2010wk36_01

// Copyright (c) 2003-2009 Nokia Corporation and/or its subsidiary(-ies).
// All rights reserved.
// This component and the accompanying materials are made available
// under the terms of "Eclipse Public License v1.0"
// which accompanies this distribution, and is available
// at the URL "http://www.eclipse.org/legal/epl-v10.html".
//
// Initial Contributors:
// Nokia Corporation - initial contribution.
//
// Contributors:
//
// Description:
// mmfswaudioinput.cpp
// 
//
#include "mmfswaudioinput.h"
#include "mmfswaudioinputpriv.h"
#include <d32soundsc.h>
#include <e32debug.h>
#include "mmf/utils/rateconvert.h" // if we need to resample
_LIT(KPddFileName,"SOUNDSC.PDD");
_LIT(KLddFileName,"ESOUNDSC.LDD");

#ifdef SYMBIAN_SWCODEC_LOGGING

const TText* const KStateNames[] = // must agree with TState
            {
            _S("EStateCreated2"), 
            _S("EStateInitialized2"), 
            _S("EStateRecordWait2"), 
            _S("EStateRecordWaitAck2"), 
            };

static const TText* StateName(TInt aState)
    {
    return KStateNames[aState];
    }

const TText* const KRStateNames[] = // must agree with TRunningState
            {
            _S("ERStateRunning"), 
            _S("ERStatePaused"), 
            _S("ERStateFinishing"), 
            _S("ERStateFinished"), 
            _S("ERStateFailed"), 
            };

static const TText* RStateName(TInt aState)
    {
    return KRStateNames[aState];
    }

#endif // SYMBIAN_SWCODEC_LOGGING

#ifdef _DEBUG

static void Panic(TInt aPanic)
    {
    _LIT(KPanicString, "SwAudioInput");
    User::Panic(KPanicString, aPanic);
    }

void CAudioInput::CheckFullInvariant()
    {
    CheckInvariant();
    CheckActiveRecorders();
    }

void CAudioInput::CheckInvariant(TBool aKnownConstructed)
    {
    // full check would be that each recorder is in one, and only one, queue.
    // However, since the queues share the same infrastructure, checking the overall length of queues
    // is correct should suffice. During construction or deletion this may obviously vary
    TInt totalLength = QLength(iIdleQueue) + QLength(iRecordingQueue) + 
           QLength(iPendingQueue) + QLength(iBusyQueue);
    if (aKnownConstructed)
        {
        __ASSERT_DEBUG(totalLength==KNumRecorders, Panic(KPanicBadTotalQueueLength));
        }
    else
        {
        __ASSERT_DEBUG(totalLength<=KNumRecorders, Panic(KPanicBadTotalQueueLength2));        
        }
    __ASSERT_DEBUG(QLength(iBusyQueue)<=1, Panic(KPanicBadTotalQueueLength));
    }

#else // _DEBUG

// inline versions that do nothing...

inline void CAudioInput::CheckFullInvariant()
	{
	}
	
inline void CAudioInput::CheckInvariant(TBool /*aKnownConstructed*/)
	{
	}

#endif // _DEBUG

const TInt KMinBufferSize = 4; // assume a good default?
//Shared chunk driver does not support max. buffer size. 16K is given in order to simulate the old driver behavior.
const TInt KMaxBufferSize = 0x4000;

//Table that maps given linear value of volume to the corresponding decibel value.
const TUint8 KLinearToDbConstantLookup[] =
                        {
                            0,            // 0
                            158,
                            170,
                            177,
                            182,
                            186,
                            189,
                            192,
                            194,
                            196,
                            198,          // 10
                            200,
                            201,
                            203,
                            204,
                            205,
                            206,
                            207,
                            208,
                            209,
                            210,          // 20
                            211,
                            212,
                            213,
                            213,
                            214,
                            215,
                            215,
                            216,
                            217,
                            217,          // 30
                            218,
                            218,
                            219,
                            219,
                            220,
                            220,
                            221,
                            221,
                            222,
                            222,          // 40
                            223,
                            223,
                            224,
                            224,
                            224,
                            225,
                            225,
                            225,
                            226,
                            226,          // 50
                            226,
                            227,
                            227,
                            227,
                            228,
                            228,
                            228,
                            229,
                            229,
                            229,          // 60
                            230,
                            230,
                            230,
                            230,
                            231,
                            231,
                            231,
                            231,
                            232,
                            232,          // 70
                            232,
                            232,
                            233,
                            233,
                            233,
                            233,
                            234,
                            234,
                            234,
                            234,          // 80
                            235,
                            235,
                            235,
                            235,
                            235,
                            236,
                            236,
                            236,
                            236,
                            236,          // 90
                            237,
                            237,
                            237,
                            237,
                            237,
                            237,
                            238,
                            238,
                            238,
                            238,          // 100
                            238,
                            239,
                            239,
                            239,
                            239,
                            239,
                            239,
                            240,
                            240,
                            240,          // 110
                            240,
                            240,
                            240,
                            240,
                            241,
                            241,
                            241,
                            241,
                            241,
                            241,          // 120
                            241,
                            242,
                            242,
                            242,
                            242,
                            242,
                            242,
                            242,
                            243,
                            243,          // 130
                            243,
                            243,
                            243,
                            243,
                            243,
                            244,
                            244,
                            244,
                            244,
                            244,          // 140
                            244,
                            244,
                            244,
                            245,
                            245,
                            245,
                            245,
                            245,
                            245,
                            245,          // 150
                            245,
                            245,
                            246,
                            246,
                            246,
                            246,
                            246,
                            246,
                            246,
                            246,          // 160
                            246,
                            247,
                            247,
                            247,
                            247,
                            247,
                            247,
                            247,
                            247,
                            247,          // 170
                            247,
                            248,
                            248,
                            248,
                            248,
                            248,
                            248,
                            248,
                            248,
                            248,          // 180
                            248,
                            249,
                            249,
                            249,
                            249,
                            249,
                            249,
                            249,
                            249,
                            249,          // 190
                            249,
                            250,
                            250,
                            250,
                            250,
                            250,
                            250,
                            250,
                            250,
                            250,          // 200
                            250,
                            250,
                            250,
                            251,
                            251,
                            251,
                            251,
                            251,
                            251,
                            251,          // 210
                            251,
                            251,
                            251,
                            251,
                            251,
                            252,
                            252,
                            252,
                            252,
                            252,          // 220
                            252,
                            252,
                            252,
                            252,
                            252,
                            252,
                            252,
                            252,
                            253,
                            253,          // 230
                            253,
                            253,
                            253,
                            253,
                            253,
                            253,
                            253,
                            253,
                            253,
                            253,          // 240
                            253,
                            254,
                            254,
                            254,
                            254,
                            254,
                            254,
                            254,
                            254,
                            254,          // 250
                            254,
                            254,
                            254,
                            254,
                            254
                        };

// rate lookup table

const TInt KNumSampleRates = 9;

struct TSampleRateEnumTable
    {
    TInt iRate;
    TSoundRate iRateEnum;
    TUint iRateConstant;
    };
//Table that maps given samples per second to the corresponding enums in RSoundSc
const TSampleRateEnumTable KRateEnumLookup[] =
    {
        {48000,ESoundRate48000Hz,KSoundRate48000Hz},
        {44100,ESoundRate44100Hz,KSoundRate44100Hz},
        {32000,ESoundRate32000Hz,KSoundRate32000Hz},
        {24000,ESoundRate24000Hz,KSoundRate24000Hz},
        {22050,ESoundRate22050Hz,KSoundRate22050Hz},
        {16000,ESoundRate16000Hz,KSoundRate16000Hz},
        {12000,ESoundRate12000Hz,KSoundRate12000Hz},
        {11025,ESoundRate11025Hz,KSoundRate11025Hz},
        {8000, ESoundRate8000Hz, KSoundRate8000Hz}
    };

// TAudioInputParams

EXPORT_C TAudioInputParams::TAudioInputParams() :
    iSampleRate(0), iNominalBufferSize(0)
    {
    // none	
    }

// CAudioInput

EXPORT_C MAudioInput* MAudioInput::CreateL(MAudioInputObserver& aObserver)
    {
    MAudioInput* result = CAudioInput::NewL(aObserver);
    return result;
    }

CAudioInput* CAudioInput::NewL(MAudioInputObserver& aObserver)
    {
    CAudioInput* result = new CAudioInput(aObserver);
    CleanupStack::PushL(result);
    result->ConstructL();
    CleanupStack::Pop(result);
    return result;
    }

CAudioInput::CAudioInput(MAudioInputObserver& aObserver) :
    iObserver(aObserver),
    iIdleQueue(_FOFF(CRecorder,iLink)),
    iRecordingQueue(_FOFF(CRecorder,iLink)),
    iPendingQueue(_FOFF(CRecorder,iLink)),
    iBusyQueue(_FOFF(CRecorder,iLink))
    {
    ASSERT(iState == EStateCreated2); // assume zero'ing initialises correctly
    }

void CAudioInput::Release()
// effective destructor call
    {
    delete this;
    }

TAny* CAudioInput::Interface(TUid aInterfaceUid)
    {
    if (aInterfaceUid == KUidAIParamInterface)
        {
        MAIParamInterface* self = this;
        return self;
        }
    return NULL;
    }

RSoundSc& CAudioInput::RecordSoundDevice()
    {
    ASSERT(iRecordSoundDevice.Handle()!=0); // should be open
    return iRecordSoundDevice;
    }

CAudioInput::~CAudioInput()
    {
    CheckInvariant(EFalse); // may not be constructed
    Cancel();
    for (TInt i = 0; i < KNumRecorders; i++)
        {
        // just in case, call cancel directly from this point too
        // Cancel depends on the active queue, and might not be quite the same.
        CRecorder* recorder = iRecorders[i];
        if (recorder)
            {
            recorder->Cancel();
            }
        delete recorder;
        }
    delete iAsyncCallBack;
    iConvBuff.Close();
    iRecordSoundDevice.Close();
    iChunk.Close();
    }

void CAudioInput::Cancel()
    {
#ifdef SYMBIAN_SWCODEC_LOGGING
    RDebug::Print(_L("--->CAudioInput::Cancel()"));
#endif
    CancelRecorders();
    if (iAsyncCallBack)
        {
        iAsyncCallBack->Cancel();
        }
#ifdef SYMBIAN_SWCODEC_LOGGING
    RDebug::Print(_L("<---CAudioInput::Cancel()"));
#endif
    }

void CAudioInput::CancelRecorders()
// if a recorder is active, then cancel it. Also move the list if required.
    {
    CheckInvariant(); // semi-invariant check - this is called from destructor
    
    CRecorder* recorder;
    while (QPop(recorder, iRecordingQueue))
        {
        recorder->Cancel();
        iIdleQueue.AddLast(*recorder);
        }
    CheckFullInvariant();
    }

void CAudioInput::CancelPendingRecorders()
// take any recorder in the pending queue. ack the buffer and send to idle
    {
    CheckFullInvariant();
    
    CRecorder* recorder;
    while (QPop(recorder, iPendingQueue))
        {
        recorder->ReleaseBuffer();
        iIdleQueue.AddLast(*recorder);
        }
    CheckFullInvariant();
    }

void CAudioInput::CancelBusyRecorder()
// take busy recorder. ack the buffer and send to idle
    {
    CheckFullInvariant();
    
    CRecorder* recorder;
    if (QPop(recorder, iBusyQueue))
        {
        recorder->ReleaseBuffer();
        iIdleQueue.AddLast(*recorder);
        }
    CheckFullInvariant();
    }

void CAudioInput::RecordAllIdle()
// take any recorder in idle queue and set recording
    {
    CheckFullInvariant();
    
    CRecorder* recorder;
    while (QPop(recorder, iIdleQueue))
        {
        recorder->RecordData();
        iRecordingQueue.AddLast(*recorder);
        }
    CheckFullInvariant();    
    }

void CAudioInput::ConstructL()
    {
    for (TInt i = 0; i < KNumRecorders; i++)
        {
        iRecorders[i] = new (ELeave) CRecorder(*this, i);
        iIdleQueue.AddLast(*(iRecorders[i]));
        }
    iAsyncCallBack = new (ELeave) CAsyncCallBack(CActive::EPriorityStandard);
    TCallBack callback(Callback, this);
    iAsyncCallBack->Set(callback);
    User::LoadPhysicalDevice(KPddFileName);
    User::LoadLogicalDevice(KLddFileName);
    CheckFullInvariant();
    }

TInt CAudioInput::Initialize(const TAudioInputParams& aParams)
    {
#ifdef SYMBIAN_SWCODEC_LOGGING
    RDebug::Print(_L("--->CAudioInput::Initialize() state=%s rstate=%s"), StateName(iState), RStateName(iRState));
#endif
    CheckFullInvariant();
    TInt error = KErrNone;
    if (iState == EStateCreated2)
        {
        if (!iRecordSoundDevice.Handle())
            {
            error = iRecordSoundDevice.Open(KSoundScRxUnit0);
            if (error)
                {
                Close(); // TODO Close() required?
                }
            }
        if (!error)
            {
            iBufferLength = aParams.iNominalBufferSize; // will be updated by SetFormat() if required
            error = SetFormat(aParams);
            if (!error)
                {
                iRecordBufferConfig.iNumBuffers = KNumRecorders*2; // for each AO we create two buffers
                iRecordBufferConfig.iFlags = 0;
                iRecordBufferConfig.iBufferSizeInBytes = iBufferLength;
                ASSERT(iChunk.Handle()==0); // should not be already open
                TPckg<TRecordSharedChunkBufConfig> bufferConfigBuf(
                        iRecordBufferConfig);
                error = iRecordSoundDevice.SetBufferChunkCreate(
                        bufferConfigBuf, iChunk);
#ifdef SYMBIAN_SWCODEC_LOGGING
                RDebug::Print(
                        _L("iRecordBufferConfig.iNumBuffers =  [%d]"),iRecordBufferConfig.iNumBuffers);
                RDebug::Print(
                        _L("iRecordBufferConfig.iFlags =  [%d]"),iRecordBufferConfig.iFlags);
                RDebug::Print(
                        _L("iRecordBufferConfig.iBufferSizeInBytes =  [%d]"),iRecordBufferConfig.iBufferSizeInBytes);
#endif			
                if (error == KErrNone)
                    {
					ASSERT(iChunk.Handle()); // should now be open
                    iRecordSoundDevice.GetBufferConfig(bufferConfigBuf); // overwrite iRecordBufferConfig
					SetGain(aParams.iInitialGain);
                    iState = EStateInitialized2;
                    }
                }
            }
        }
    else
        {
        error = KErrNotReady;
        }
#ifdef SYMBIAN_SWCODEC_LOGGING
    RDebug::Print(_L("<---CAudioInput::Initialize(%d) state=%s rstate=%s"), error, StateName(
            iState), RStateName(iRState));
#endif
    CheckFullInvariant();
    return error;
    }

void CAudioInput::Close()
    {
#ifdef SYMBIAN_SWCODEC_LOGGING
    RDebug::Print(_L("--->CAudioInput::Close() state=%s rstate=%s"), StateName(iState), RStateName(iRState));
#endif
    CheckFullInvariant();
    InternalStop(); // Technically this should not be required, as client should Stop() first, but just in case
    if (iState == EStateInitialized2)
        {
        iRecordSoundDevice.Close();
        iChunk.Close();
        iConvBuff.Close();
        iState = EStateCreated2;
        }
    ASSERT(iState==EStateCreated2);
    ASSERT(QLength(iIdleQueue)==KNumRecorders);
#ifdef SYMBIAN_SWCODEC_LOGGING
    RDebug::Print(_L("<---CAudioInput::Close() state=%s rstate=%s"), StateName(iState), RStateName(iRState));
#endif
    CheckFullInvariant();
    }

TInt CAudioInput::Start()
    {
#ifdef SYMBIAN_SWCODEC_LOGGING
    RDebug::Print(_L("--->CAudioInput::Start() state=%s rstate=%s"), StateName(iState), RStateName(iRState));
#endif
    CheckFullInvariant();
    TInt error = KErrNone;
    if (iState == EStateInitialized2)
        {
        RecordAllIdle();
        iState = EStateRecordWait2;
        iRState = ERStateRunning;
        }
    else
        {
        error = KErrNotReady;
        }
#ifdef SYMBIAN_SWCODEC_LOGGING
    RDebug::Print(_L("<---CAudioInput::Start(%d) state=%s rstate=%s"), 
            error, StateName(iState), RStateName(iRState));
#endif
    CheckFullInvariant();
    return error;
    }

void CAudioInput::BufferAck()
    {
#ifdef SYMBIAN_SWCODEC_LOGGING
    RDebug::Print(_L("--->CAudioInput::BufferAck() state=%s rstate=%s"), StateName(iState), RStateName(iRState));
#endif
    CheckFullInvariant();
    ASSERT(iState==EStateRecordWaitAck2);
    HandleBufferAck();
    iState = EStateRecordWait2;
    RequestCallback();
#ifdef SYMBIAN_SWCODEC_LOGGING
    RDebug::Print(_L("<---CAudioInput::BufferAck() state=%s rstate=%s"), StateName(iState), RStateName(iRState));
#endif
    CheckFullInvariant();
    }

void CAudioInput::HandleBufferAck()
    {
    CRecorder* recorder = QPop(iBusyQueue);
    recorder->ReleaseBuffer();
    if (iRState == ERStateRunning)
        {
        recorder->RecordData();
        iRecordingQueue.AddLast(*recorder);
        }
    else
        {
        iIdleQueue.AddLast(*recorder);
        if (iRState == ERStatePaused && (QLength(iRecordingQueue)+QLength(iPendingQueue) == 0))
            {
            iRState = ERStateFinishing;
            }
        }
    }

TInt CAudioInput::Pause()
    {
#ifdef SYMBIAN_SWCODEC_LOGGING
    RDebug::Print(_L("--->CAudioInput::Pause() state=%s rstate=%s"), StateName(iState), RStateName(iRState));
#endif
    CheckFullInvariant();
    TInt err = KErrNone; // note we are silent if called in wrong state
    
#ifdef SYMBIAN_SWCODEC_LOGGING
    RDebug::Print(
                _L("***!pause irecordingquelength %d pendingquelength %d ibusyquelength=%d"), QLength(iRecordingQueue),
                QLength(iPendingQueue), QLength(iBusyQueue));
#endif
    if ((iState == EStateRecordWait2 || iState == EStateRecordWaitAck2) && iRState==ERStateRunning)
        {
        iRecordSoundDevice.Pause();

        iRState = ERStatePaused;
        }
#ifdef SYMBIAN_SWCODEC_LOGGING
    RDebug::Print(_L("<---CAudioInput::Pause(%d) state=%s err=%d"), err, StateName(iState), RStateName(iRState));
#endif
    CheckFullInvariant();
    return err; 
    }

TInt CAudioInput::Resume()
    {
#ifdef SYMBIAN_SWCODEC_LOGGING
    RDebug::Print(_L("--->CAudioInput::Resume() state=%s rstate=%s"), StateName(iState), RStateName(iRState));
#endif
    CheckFullInvariant();
    TInt err = KErrNone; // note we are silent if called in the wrong state
    if ((iState == EStateRecordWait2 || iState == EStateRecordWaitAck2) && 
            ((iRState==ERStatePaused || iRState==ERStateFinishing || iRState==ERStateFinished)))
        {
        err = RecordSoundDevice().Resume();
        
        RecordAllIdle();
        
        iRState = ERStateRunning;
        }
#ifdef SYMBIAN_SWCODEC_LOGGING
    RDebug::Print(_L("<---CAudioInput::Resume(%d) state=%s rstate=%s"), err, StateName(iState), RStateName(iRState));
#endif
    CheckFullInvariant();
    return err; 
    }

TInt CAudioInput::Flush()
    {
#ifdef SYMBIAN_SWCODEC_LOGGING
    RDebug::Print(_L("--->CAudioInput::Flush() state=%s rstate=%s"), StateName(iState), RStateName(iRState));
#endif
    CheckFullInvariant();
    TInt error = KErrNotReady;
    if (iRState==ERStatePaused)
        {
        if (iState == EStateRecordWait2)
            {
            InternalFlush();
            ASSERT(iState == EStateRecordWait2); // stay put
            error = KErrNone;
            }
        else if (iState == EStateRecordWaitAck2)
            {
            InternalFlush();
            iState = EStateRecordWait2;
            error = KErrNone;
            }       
        }
 #ifdef SYMBIAN_SWCODEC_LOGGING
    RDebug::Print(_L("--->CAudioInput::Flush(%d) state=%s rstate=%s"), error, StateName(iState), RStateName(iRState));
#endif
    CheckFullInvariant();
    return error;
    }

void CAudioInput::Stop()
    {
#ifdef SYMBIAN_SWCODEC_LOGGING
    RDebug::Print(_L("--->CAudioInput::Stop() state=%s rstate=%s"), StateName(iState), RStateName(iRState));
#endif
    CheckFullInvariant();
    InternalStop();
#ifdef SYMBIAN_SWCODEC_LOGGING
    RDebug::Print(_L("<---CAudioInput::Stop() state=%s rstate=%s"), StateName(iState), RStateName(iRState));
#endif
    }

void CAudioInput::InternalStop()
// This stops all recording and returns pending and busy buffers to idle. Must be called when
// client knows the buffer has been grabbed (e.g. _not_ before error callback)
    {
    CheckInvariant(); // Can be called from buffer error, so can't check full invariant.
    if (iState != EStateInitialized2 && iState != EStateCreated2)
        {
        InternalFlush();
        iState = EStateInitialized2;
        }
    CheckFullInvariant();
    ASSERT((QLength(iRecordingQueue) + QLength(iPendingQueue) + 
					QLength(iBusyQueue))==0); // everything is stopped 
    }

void CAudioInput::InternalFlush()
    {
    CancelRecorders();
    CancelPendingRecorders();
    CancelBusyRecorder();
    }

void CAudioInput::BufferArrives(CRecorder* aRecorder)
    {
#ifdef SYMBIAN_SWCODEC_LOGGING
    RDebug::Print(
            _L("--->CAudioInput::BufferArrives(%d,%d) state=%s rstate=%s"), aRecorder->Index(),
            aRecorder->StatusOrOffset(), StateName(iState), RStateName(iRState));
#endif
    CheckInvariant(); // Can't use CheckFullInvariant() from RunL
    ASSERT(iState==EStateRecordWait2 || iState==EStateRecordWaitAck2);
    ASSERT(aRecorder->Offset()>=0); // assert we're not here due to an error
    iRecordingQueue.Remove(*aRecorder);
    iPendingQueue.AddLast(*aRecorder);
    if (iState==EStateRecordWait2)
        {
        RequestCallback();
        }
#ifdef SYMBIAN_SWCODEC_LOGGING
    RDebug::Print(_L("<---CAudioInput::BufferArrives() state=%s rstate=%s"), 
            StateName(iState), RStateName(iRState));
#endif
    CheckFullInvariant();
    }

void CAudioInput::UseBuffer(CRecorder* aRecorder)
// incomming buffer is pointed to by iBufPtr. Either directly or via convert, use for callback
    {
    iBufPtr.Set(iChunk.Base() + aRecorder->Offset(), aRecorder->Length());
    if (iConverter)
        {
#ifdef SYMBIAN_SWCODEC_LOGGING
        RDebug::Print(_L("iBufPtr length [%d] iconvbuff length [%d,%d]"),
                iBufPtr.Length(), iConvBuff.Length(), iConvBuff.MaxLength());
#endif
        __DEBUG_ONLY(TInt converted =) iConverter->Convert(iBufPtr, iConvBuff);
        // the following assert should check we convert the log. 
        // Actually we sometimes fail at the end of the operation with what is effectively
        // the last buffer. Arguably a driver fault, but there we are
        // ASSERT(converted==iBufPtr.Length());  
#ifdef SYMBIAN_SWCODEC_LOGGING
        RDebug::Print(_L("iBufPtr length [%d] iconvbuff length after [%d,%d]"),
        iBufPtr.Length(), iConvBuff.Length(), iConvBuff.MaxLength());
#endif
        iObserver.InputBufferAvailable(iConvBuff);
        }
    else
        {
        iObserver.InputBufferAvailable(iBufPtr);
        }
#ifdef SYMBIAN_SWCODEC_LOGGING
    RDebug::Print(_L("12345 ibufptr =  [0x%x]"),iBufPtr.Ptr());
#endif
    }

void CAudioInput::BufferError(CRecorder* aRecorder, TInt aError)
    {
#ifdef SYMBIAN_SWCODEC_LOGGING
    RDebug::Print(
            _L("--->CAudioInput::BufferError(%d,%d) state=%s rstate=%s"), aRecorder->Index(),
            aError, StateName(iState), RStateName(iRState));
#endif
    CheckInvariant(); // Can't use CheckFullInvariant() from RunL
    if (aError==KErrCancel || aError==KErrOverflow)
        {
        // Cancel: sign of a Pause operation. If paused etc, then merely add to idle list. potentially generate finished signal
        //         if not paused, then not clear but just in case request record again
        // Overflow: basically try again, but if paused merely add to idle. Check for last buffer just in case
        if (iRState!=ERStateRunning)
            {
            iRecordingQueue.Remove(*aRecorder);
            iIdleQueue.AddLast(*aRecorder);
#ifdef SYMBIAN_SWCODEC_LOGGING
            RDebug::Print(
                        _L("***! irecordingquelength %d pendingquelength %d ibusyquelength=%d"), QLength(iRecordingQueue),
                        QLength(iPendingQueue), QLength(iBusyQueue));
#endif
            if (iRState == ERStatePaused && (QLength(iRecordingQueue)+QLength(iPendingQueue)+QLength(iBusyQueue) == 0))
                {
                iRState = ERStateFinishing;
                RequestCallback();
                }
            }
        else
            {
            aRecorder->RecordData();
            }
        }
    else
        {
        iRecordingQueue.Remove(*aRecorder);
        iIdleQueue.AddLast(*aRecorder);
        iRState = ERStateFailed;
        iObserver.InputError(aError);        
        }
#ifdef SYMBIAN_SWCODEC_LOGGING
    RDebug::Print(_L("<---CAudioInput::BufferError() state=%s rstate=%s"), 
            StateName(iState), RStateName(iRState));
#endif
    CheckFullInvariant();
    }

TInt CAudioInput::Callback(TAny* aPtr)
    {
    CAudioInput* self = static_cast<CAudioInput*> (aPtr);
    TRAPD(error,self->AsyncCallbackL());
    return error; // TODO really have to handle error
    }

void CAudioInput::RequestCallback()
    {
    // ensure iAsyncCallBack is active
    if (!iAsyncCallBack->IsActive())
        {
        iAsyncCallBack->Call();
        }
    }

void CAudioInput::AsyncCallbackL()
    {
#ifdef SYMBIAN_SWCODEC_LOGGING
    RDebug::Print(_L("--->CAudioInput::AsyncCallbackL() state=%s rstate=%s"), 
            StateName(iState), RStateName(iRState));
#endif
    CheckFullInvariant();
    ASSERT(iState==EStateRecordWait2 || iState==EStateRecordWaitAck2); // should not occur in other states. Actually ignore in 2nd
    if (iState==EStateRecordWait2)
        {
        if (QLength(iPendingQueue)>0)
            {
            ASSERT(QLength(iBusyQueue)==0);
            iState = EStateRecordWaitAck2; // change state prior to callback, in case sync call from callback
            CRecorder* recorder = QPop(iPendingQueue);
            iBusyQueue.AddLast(*recorder);
            UseBuffer(recorder);
            }
        else 
            {
            if (iRState == ERStateFinishing)
                {
                ASSERT(QLength(iRecordingQueue)+QLength(iPendingQueue)+QLength(iBusyQueue) == 0); // should be true
                iRState = ERStateFinished;
                iObserver.InputFinished();
                }
            }
        }
#ifdef SYMBIAN_SWCODEC_LOGGING
    RDebug::Print(_L("<---CAudioInput::AsyncCallbackL() state=%s rstate=%s"), 
            StateName(iState), RStateName(iRState));
#endif
    CheckFullInvariant();
    }

TInt CAudioInput::GetBufferSizes(TInt& aMinSize, TInt& aMaxSize)
    {
    aMinSize = KMinBufferSize;
    aMaxSize = KMaxBufferSize;
    return KErrNone;
    }

TInt CAudioInput::SetGain(TInt aGain)
    {
    TInt error = KErrNone; // note: silent if in wrong state
    if (iRecordSoundDevice.Handle())
        {
        // we have to switch from level to dB value
        if(aGain >=0 && aGain<=KSoundMaxVolume)
            {
            error = iRecordSoundDevice.SetVolume(KLinearToDbConstantLookup[aGain]);
            }
        else
            {
            error = KErrArgument;
            }
        }
    return error;
    }

TInt CAudioInput::SetFormat(const TAudioInputParams& aFormat)
    {
    TInt err = KErrNotFound;
    TCurrentSoundFormatV02Buf formatBuf;
    TFormatData formatData;

    delete iConverter;
    iConverter = NULL; // setting this to NULL indicates we are not using converter. No other flag

    TInt wantedRate = aFormat.iSampleRate;
    for (TInt index = 0; index < KNumSampleRates; index++)
        {
        if (wantedRate == KRateEnumLookup[index].iRate)
            {
            formatBuf().iRate = KRateEnumLookup[index].iRateEnum;
            formatData.iSampleRate = wantedRate;
            err = KErrNone;
            break;
            }
        }

    if (err == KErrNone)
        {
        formatBuf().iChannels = aFormat.iNumChannels;
        formatBuf().iEncoding = ESoundEncoding16BitPCM;
        formatBuf().iDataFormat = ESoundDataFormatInterleaved;
        err = iRecordSoundDevice.SetAudioFormat(formatBuf);
#if defined(SYMBIAN_SOUNDADAPTER_FORCECDRATES) || defined (SYMBIAN_SOUNDADAPTER_FORCESTEREO)
        err = KErrNotSupported; // force Negotiate - for debugging
#endif
        if (err == KErrNotSupported)
            {
            // don't support directly. Perhaps can rate convert?
            err = NegotiateFormat(aFormat, formatData);
            }
        }
    return err;
    }

TInt CAudioInput::NegotiateFormat(const TAudioInputParams& aFormat, TFormatData &aFormatData)
    {
    TInt err = KErrNotFound;
    TCurrentSoundFormatV02Buf formatBuf;
    
    TInt origBufferLength = iBufferLength; // cache in case we change

    // find out first what the driver supports
    TSoundFormatsSupportedV02Buf supportedFormat;
    iRecordSoundDevice.Caps(supportedFormat);
    TUint32 supportedRates = supportedFormat().iRates;
#ifdef SYMBIAN_SOUNDADAPTER_FORCECDRATES
    supportedRates &= KSoundRate11025Hz | KSoundRate22050Hz
            | KSoundRate44100Hz; // only use CD rates - for debugging
#endif

    // For RecordCase:
    //		We want the next rate above consistently - we go down from this to the requested rate.
    //		If there is one, we don't support - we _never_ upsample.
    // note that the table is given in descending order, so we start with the highest
    TInt wantedRate = aFormat.iSampleRate;
    TInt takeTheFirst = EFalse;
    TInt nextUpValidIndex = -1;
    for (TInt index = 0; index < KNumSampleRates; index++)
        {
        TBool lookingAtRequestedRate = wantedRate
                == KRateEnumLookup[index].iRate;
        TSoundRate wantedEnum = KRateEnumLookup[index].iRateEnum;
        TUint32 equivBitmap = KRateEnumLookup[index].iRateConstant;
        TBool isSupported = (equivBitmap & supportedRates) != EFalse;
        if (lookingAtRequestedRate || takeTheFirst)
            {
            if (isSupported)
                {
                // this rate is supported
                formatBuf().iRate = wantedEnum;
                aFormatData.iActualRate = KRateEnumLookup[index].iRate;
                err = KErrNone;
                break;
                }
            }
        else if (!takeTheFirst)
            {
            // while we are still looking for the rate, want to cache any supported index
            // at end of loop, this will be the first rate above ours that is supported
            // use for fallback if required
            if (isSupported)
                {
                nextUpValidIndex = index;
                }
            }
        if (lookingAtRequestedRate)
            {
            // For record we just abort.
            break;
            }
        }

    if (err)
        {
        // if there is one above the requested rate, use that
        if (nextUpValidIndex >= 0)
            {
            TSoundRate wantedEnum =
                    KRateEnumLookup[nextUpValidIndex].iRateEnum;
            formatBuf().iRate = wantedEnum;
            aFormatData.iActualRate = KRateEnumLookup[nextUpValidIndex].iRate;
            err = KErrNone;
            }
        }

    if (err)
        {
        // should have something!
        return err;
        }

    aFormatData.iSampleRate = wantedRate; // iSampleRate is our requested/apparent rate, not the device rate.

    TUint32 channelsSupported = supportedFormat().iChannels;
#ifdef SYMBIAN_SOUNDADAPTER_FORCESTEREO
    channelsSupported &= KSoundStereoChannel; // don't use mono - for debugging
#endif

    if (aFormat.iNumChannels == 1)
        {
        aFormatData.iRequestedChannels = 1;
        // want mono
        if (channelsSupported & KSoundMonoChannel)
            {
            // mono is supported, as usual
            aFormatData.iActualChannels = 1;
            }
        else if (channelsSupported & KSoundStereoChannel)
            {
            aFormatData.iActualChannels = 2;
            iBufferLength *= 2; // double size, will do stereo->mono
            }
        else
            {
            return KErrNotSupported; // should not get this far for real
            }
        }
    else if (aFormat.iNumChannels == 2)
        {
        aFormatData.iRequestedChannels = 2;
        // want stereo
        if (channelsSupported & KSoundStereoChannel)
            {
            // stereo is supported, as usual
            aFormatData.iActualChannels = 2;
            }
        else if (channelsSupported & KSoundMonoChannel)
            {
            aFormatData.iActualChannels = 1;
            iBufferLength /= 2; // halve size, will do mono->stereo
            }
        else
            {
            return KErrNotSupported; // should not get this far for real
            }
        }
    else
        {
        return KErrNotSupported; // unknown number of channels requested!
        }

    formatBuf().iChannels = aFormatData.iActualChannels;

    formatBuf().iEncoding = ESoundEncoding16BitPCM;
    formatBuf().iDataFormat = ESoundDataFormatInterleaved;
    err = iRecordSoundDevice.SetAudioFormat(formatBuf);

    if (!err)
        {
        ASSERT(!iConverter); // pre-condition at top of function anyway
        // when recording we convert from actual to requested
        TInt outputRateToUse = aFormatData.iSampleRate;
#ifdef SYMBIAN_SKIP_RESAMPLE_ON_RECORD
		// with this macro just channel convert at most
        outputRateToUse = aFormatData.iActualRate;
#endif
#ifdef SYMBIAN_SOUNDADAPTER_DEBUG
		RDebug::Print(_L("RMdaDevSound::CBody::NegotiateFormat: Convert:CreateL from %d/%d to %d/%d"),
                        aFormatData.iActualRate, aFormatData.iActualChannels,
                        aFormatData.iSampleRate, aFormatData.iRequestedChannels);
#endif																	       
        TRAP(err, iConverter = CChannelAndSampleRateConverter::CreateL(aFormatData.iActualRate,
                                aFormatData.iActualChannels,
                                outputRateToUse,
                                aFormatData.iRequestedChannels));
        }
    if (!err && iConverter)
        {
        err = iConvBuff.Create(origBufferLength);
#ifdef SYMBIAN_SWCODEC_LOGGING
        RDebug::Print(_L("iBufferLength length [%d] iconvbuff length [%d,%d]"),
        iBufferLength, iConvBuff.Length(), iConvBuff.MaxLength());
#endif
        }

    return err;
    }

TInt CAudioInput::GetSupportedSampleRates(RArray<TInt>& aSupportedSampleRates)
    {
    TInt err = KErrNone;

    if (iRecordSoundDevice.Handle())
        {
        GetSupportedSampleRates(aSupportedSampleRates, iRecordSoundDevice);
        }
    else
        {//temporarily open the device if we can
        RSoundSc tempsound;
        err = tempsound.Open(KSoundScRxUnit0);
        if (!err)
            {
            err = GetSupportedSampleRates(aSupportedSampleRates, tempsound);
            tempsound.Close();
            }
        }
    return err;
    }

TInt CAudioInput::GetSupportedSampleRates(
        RArray<TInt>& aSupportedSampleRates, RSoundSc& aSoundDevice)
    {
    ASSERT(aSoundDevice.Handle()); // parent to ensure this is open

    TInt err = KErrNone;

    TSoundFormatsSupportedV02Buf supportedFormat;
    aSoundDevice.Caps(supportedFormat);
    TUint32 rates = supportedFormat().iRates;

    for (TInt i = KNumSampleRates - 1; i > 0; i--)//min to max
        {
        if (rates & KRateEnumLookup[i].iRateConstant)
            {
            err = aSupportedSampleRates.Append(KRateEnumLookup[i].iRate);
            if (err)
                {
                break;
                }
            }
        }
    return err;
    }

TInt CAudioInput::QLength(TSglQue<CRecorder>& aQueue)
// count elements in List/Q. Have to use iterator to do this - it seems.
    {
    TSglQueIter<CRecorder> iter(aQueue);
    TInt count=0;
    while (iter++)
        {
        // like old-fashioned C string manipulations. iterate through all members
        count++;
        }
    return count;
    }

CAudioInput::CRecorder* CAudioInput::QPop(TSglQue<CRecorder>& aQueue)
    {
    CRecorder* recorder = NULL;
    if (! aQueue.IsEmpty())
        {
        recorder = aQueue.First();
        aQueue.Remove(*recorder);
        }
    return recorder;
    }
	
#ifdef _DEBUG

// these functions are used in invariant checking only

void CAudioInput::CheckActiveRecorders(TSglQue<CRecorder>& aQueue, TBool aExpected, TInt aPanicCode)
// check that all the elements in the given Q are IsActive() or vice-versa
    {
    TSglQueIter<CRecorder> iter(aQueue);
    
    CRecorder* recorder;
    while ((recorder=iter++)!=NULL)
        {
        TBool expected = aExpected != EFalse; // ensure these are either true or false
        TBool active = recorder->IsActive() != EFalse;
        __ASSERT_DEBUG(expected == active, Panic(aPanicCode));
        }
    }

void CAudioInput::CheckActiveRecorders()
// check that all the elements in the recordingQueue are IsActive() etc
// can't be used as CRecorder::RunL() pre-condition
    {
    CheckActiveRecorders(iRecordingQueue, ETrue, EPanicBusyRecorderNotActive);
    CheckActiveRecorders(iIdleQueue, EFalse, EPanicNonBusyRecorderActive);
    CheckActiveRecorders(iPendingQueue, EFalse, EPanicNonBusyRecorderActive);
    CheckActiveRecorders(iBusyQueue, EFalse, EPanicNonBusyRecorderActive);
    }

#endif // _DEBUG

//
// CRecorder
//	


CAudioInput::CRecorder::CRecorder(CAudioInput& aParent, TInt aIndex) :
    CActive(EPriorityStandard), iParent(aParent), iIndex(aIndex)
    {
    CActiveScheduler::Add(this);
    }

CAudioInput::CRecorder::~CRecorder()
    {
    Cancel();
    }

void CAudioInput::CRecorder::Cancel()
    {
    // this override takes into account that ReleaseBuffer must be called - this is not the
    // normal pattern where following Cancel() we're not concerned with the results
    if (IsActive())
        {
        ASSERT(!BufferHeld()); // if active then buffer held should be clear. don't reset then
        CActive::Cancel();
        ReleaseBuffer(ETrue); // release - might have been a successful run!
        }
    else
        {
        ReleaseBuffer(); // this will release buffer if still outstanding	
        }
    }

void CAudioInput::CRecorder::RunL()
    {
#ifdef SYMBIAN_SWCODEC_LOGGING
    RDebug::Print(_L("--->CAudioInput::CRecorder::RunL(%d, %d)"), Index(),
            iStatus.Int());
#endif
    TInt errorOrOffset = iStatus.Int(); // negative -> error. non-negative is offset in chunk

    if (errorOrOffset < 0)
        {
#ifdef SYMBIAN_SWCODEC_LOGGING
        RDebug::Print(_L("errorOrOffset =  [%d]"),errorOrOffset);
#endif
        // ReleaseBuffer(ETrue); // calls ReleaseBuffer() on error code. Driver requires this, even though seems wrong
        iParent.BufferError(this, errorOrOffset);
        }
    else
        {
        ASSERT(!iBufferHeld);
        iBufferHeld = ETrue;

#ifdef SYMBIAN_SWCODEC_LOGGING
        RDebug::Print(_L("errorOrOffset =  [%d]"),errorOrOffset);
#endif
        // If a buffer larger than expected arrives truncate it.
        iLength = Min(iLength,iParent.iBufferLength);
        iParent.BufferArrives(this);
        }
#ifdef SYMBIAN_SWCODEC_LOGGING
    RDebug::Print(_L("<---CAudioInput::CRecorder::RunL(%d)"), Index());
#endif
    }

void CAudioInput::CRecorder::RecordData()
    {
#ifdef SYMBIAN_SWCODEC_LOGGING
    RDebug::Print(_L("--->CAudioInput::CRecorder::RecordData(%d)"), Index());
#endif
    ASSERT(!iBufferHeld);
    Deque(); // ensure we append to the AO queue, so if it comes to it we process oldest request first
    CActiveScheduler::Add(this);
    iLength = iParent.BufferLength(); // TODO do we have to set this first or is it an OUT param purely
    iParent.RecordSoundDevice().RecordData(iStatus, iLength);
    SetActive();

#ifdef SYMBIAN_SWCODEC_LOGGING
    RDebug::Print(_L("###****#####!!!! Buffer length [%d], status [%d] "), iLength,
            iStatus.Int());
#endif
#ifdef SYMBIAN_SWCODEC_LOGGING
    RDebug::Print(_L("<---CAudioInput::CRecorder::RecordData(%d)"), Index());
#endif
    }

void CAudioInput::CRecorder::DoCancel()
    {
#ifdef SYMBIAN_SWCODEC_LOGGING
    RDebug::Print(_L("--->CAudioInput::CRecorder::DoCancel(%d)"), Index());
#endif
    iParent.RecordSoundDevice().Cancel(iStatus);
#ifdef SYMBIAN_SWCODEC_LOGGING
    RDebug::Print(_L("<---CAudioInput::CRecorder::DoCancel(%d)"), Index());
#endif
    }

void CAudioInput::CRecorder::ReleaseBuffer(TBool aDoAnyway)
    {
    if (iBufferHeld || aDoAnyway)
        {
        iParent.RecordSoundDevice().ReleaseBuffer(iStatus.Int());
        iBufferHeld = EFalse;
        }
    }

TInt CAudioInput::CRecorder::Index() const
    {
    return iIndex;
    }

TInt CAudioInput::CRecorder::Length() const
    {
    return iLength;
    }

TBool CAudioInput::CRecorder::IsBusy() const
    {
    return IsActive() || BufferHeld();
    }

TBool CAudioInput::CRecorder::BufferHeld() const
// BufferHeld() means we're in control of a passed buffer
    {
    return iBufferHeld;
    }

TInt CAudioInput::CRecorder::Offset() const
// If we call this, we've discounted errors so can assert non-negative
    {
    TInt result = StatusOrOffset();
    ASSERT(result>=0); 
    return result;
    }

TInt CAudioInput::CRecorder::StatusOrOffset() const
// The iStatus assuming is valid
    {
    ASSERT(!IsActive()); // or would not be valid
    TInt result = iStatus.Int();
    return result;    
    }