--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/devsound/devsoundrefplugin/src/swcodecwrapper/mmfswaudioinput.cpp Tue Feb 02 01:56:55 2010 +0200
@@ -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;
+ }