devsound/devsoundrefplugin/src/swcodecwrapper/mmfswaudioinput.cpp
changeset 0 79dd3e2336a0
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/devsound/devsoundrefplugin/src/swcodecwrapper/mmfswaudioinput.cpp	Fri Oct 08 19:40:43 2010 +0100
@@ -0,0 +1,1430 @@
+// 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;    
+    }