sl@0: // Copyright (c) 2003-2009 Nokia Corporation and/or its subsidiary(-ies). sl@0: // All rights reserved. sl@0: // This component and the accompanying materials are made available sl@0: // under the terms of "Eclipse Public License v1.0" sl@0: // which accompanies this distribution, and is available sl@0: // at the URL "http://www.eclipse.org/legal/epl-v10.html". sl@0: // sl@0: // Initial Contributors: sl@0: // Nokia Corporation - initial contribution. sl@0: // sl@0: // Contributors: sl@0: // sl@0: // Description: sl@0: // mmfswaudioinput.cpp sl@0: // sl@0: // sl@0: #include "mmfswaudioinput.h" sl@0: #include "mmfswaudioinputpriv.h" sl@0: #include <d32soundsc.h> sl@0: #include <e32debug.h> sl@0: #include "mmf/utils/rateconvert.h" // if we need to resample sl@0: _LIT(KPddFileName,"SOUNDSC.PDD"); sl@0: _LIT(KLddFileName,"ESOUNDSC.LDD"); sl@0: sl@0: #ifdef SYMBIAN_SWCODEC_LOGGING sl@0: sl@0: const TText* const KStateNames[] = // must agree with TState sl@0: { sl@0: _S("EStateCreated2"), sl@0: _S("EStateInitialized2"), sl@0: _S("EStateRecordWait2"), sl@0: _S("EStateRecordWaitAck2"), sl@0: }; sl@0: sl@0: static const TText* StateName(TInt aState) sl@0: { sl@0: return KStateNames[aState]; sl@0: } sl@0: sl@0: const TText* const KRStateNames[] = // must agree with TRunningState sl@0: { sl@0: _S("ERStateRunning"), sl@0: _S("ERStatePaused"), sl@0: _S("ERStateFinishing"), sl@0: _S("ERStateFinished"), sl@0: _S("ERStateFailed"), sl@0: }; sl@0: sl@0: static const TText* RStateName(TInt aState) sl@0: { sl@0: return KRStateNames[aState]; sl@0: } sl@0: sl@0: #endif // SYMBIAN_SWCODEC_LOGGING sl@0: sl@0: #ifdef _DEBUG sl@0: sl@0: static void Panic(TInt aPanic) sl@0: { sl@0: _LIT(KPanicString, "SwAudioInput"); sl@0: User::Panic(KPanicString, aPanic); sl@0: } sl@0: sl@0: void CAudioInput::CheckFullInvariant() sl@0: { sl@0: CheckInvariant(); sl@0: CheckActiveRecorders(); sl@0: } sl@0: sl@0: void CAudioInput::CheckInvariant(TBool aKnownConstructed) sl@0: { sl@0: // full check would be that each recorder is in one, and only one, queue. sl@0: // However, since the queues share the same infrastructure, checking the overall length of queues sl@0: // is correct should suffice. During construction or deletion this may obviously vary sl@0: TInt totalLength = QLength(iIdleQueue) + QLength(iRecordingQueue) + sl@0: QLength(iPendingQueue) + QLength(iBusyQueue); sl@0: if (aKnownConstructed) sl@0: { sl@0: __ASSERT_DEBUG(totalLength==KNumRecorders, Panic(KPanicBadTotalQueueLength)); sl@0: } sl@0: else sl@0: { sl@0: __ASSERT_DEBUG(totalLength<=KNumRecorders, Panic(KPanicBadTotalQueueLength2)); sl@0: } sl@0: __ASSERT_DEBUG(QLength(iBusyQueue)<=1, Panic(KPanicBadTotalQueueLength)); sl@0: } sl@0: sl@0: #else // _DEBUG sl@0: sl@0: // inline versions that do nothing... sl@0: sl@0: inline void CAudioInput::CheckFullInvariant() sl@0: { sl@0: } sl@0: sl@0: inline void CAudioInput::CheckInvariant(TBool /*aKnownConstructed*/) sl@0: { sl@0: } sl@0: sl@0: #endif // _DEBUG sl@0: sl@0: const TInt KMinBufferSize = 4; // assume a good default? sl@0: //Shared chunk driver does not support max. buffer size. 16K is given in order to simulate the old driver behavior. sl@0: const TInt KMaxBufferSize = 0x4000; sl@0: sl@0: //Table that maps given linear value of volume to the corresponding decibel value. sl@0: const TUint8 KLinearToDbConstantLookup[] = sl@0: { sl@0: 0, // 0 sl@0: 158, sl@0: 170, sl@0: 177, sl@0: 182, sl@0: 186, sl@0: 189, sl@0: 192, sl@0: 194, sl@0: 196, sl@0: 198, // 10 sl@0: 200, sl@0: 201, sl@0: 203, sl@0: 204, sl@0: 205, sl@0: 206, sl@0: 207, sl@0: 208, sl@0: 209, sl@0: 210, // 20 sl@0: 211, sl@0: 212, sl@0: 213, sl@0: 213, sl@0: 214, sl@0: 215, sl@0: 215, sl@0: 216, sl@0: 217, sl@0: 217, // 30 sl@0: 218, sl@0: 218, sl@0: 219, sl@0: 219, sl@0: 220, sl@0: 220, sl@0: 221, sl@0: 221, sl@0: 222, sl@0: 222, // 40 sl@0: 223, sl@0: 223, sl@0: 224, sl@0: 224, sl@0: 224, sl@0: 225, sl@0: 225, sl@0: 225, sl@0: 226, sl@0: 226, // 50 sl@0: 226, sl@0: 227, sl@0: 227, sl@0: 227, sl@0: 228, sl@0: 228, sl@0: 228, sl@0: 229, sl@0: 229, sl@0: 229, // 60 sl@0: 230, sl@0: 230, sl@0: 230, sl@0: 230, sl@0: 231, sl@0: 231, sl@0: 231, sl@0: 231, sl@0: 232, sl@0: 232, // 70 sl@0: 232, sl@0: 232, sl@0: 233, sl@0: 233, sl@0: 233, sl@0: 233, sl@0: 234, sl@0: 234, sl@0: 234, sl@0: 234, // 80 sl@0: 235, sl@0: 235, sl@0: 235, sl@0: 235, sl@0: 235, sl@0: 236, sl@0: 236, sl@0: 236, sl@0: 236, sl@0: 236, // 90 sl@0: 237, sl@0: 237, sl@0: 237, sl@0: 237, sl@0: 237, sl@0: 237, sl@0: 238, sl@0: 238, sl@0: 238, sl@0: 238, // 100 sl@0: 238, sl@0: 239, sl@0: 239, sl@0: 239, sl@0: 239, sl@0: 239, sl@0: 239, sl@0: 240, sl@0: 240, sl@0: 240, // 110 sl@0: 240, sl@0: 240, sl@0: 240, sl@0: 240, sl@0: 241, sl@0: 241, sl@0: 241, sl@0: 241, sl@0: 241, sl@0: 241, // 120 sl@0: 241, sl@0: 242, sl@0: 242, sl@0: 242, sl@0: 242, sl@0: 242, sl@0: 242, sl@0: 242, sl@0: 243, sl@0: 243, // 130 sl@0: 243, sl@0: 243, sl@0: 243, sl@0: 243, sl@0: 243, sl@0: 244, sl@0: 244, sl@0: 244, sl@0: 244, sl@0: 244, // 140 sl@0: 244, sl@0: 244, sl@0: 244, sl@0: 245, sl@0: 245, sl@0: 245, sl@0: 245, sl@0: 245, sl@0: 245, sl@0: 245, // 150 sl@0: 245, sl@0: 245, sl@0: 246, sl@0: 246, sl@0: 246, sl@0: 246, sl@0: 246, sl@0: 246, sl@0: 246, sl@0: 246, // 160 sl@0: 246, sl@0: 247, sl@0: 247, sl@0: 247, sl@0: 247, sl@0: 247, sl@0: 247, sl@0: 247, sl@0: 247, sl@0: 247, // 170 sl@0: 247, sl@0: 248, sl@0: 248, sl@0: 248, sl@0: 248, sl@0: 248, sl@0: 248, sl@0: 248, sl@0: 248, sl@0: 248, // 180 sl@0: 248, sl@0: 249, sl@0: 249, sl@0: 249, sl@0: 249, sl@0: 249, sl@0: 249, sl@0: 249, sl@0: 249, sl@0: 249, // 190 sl@0: 249, sl@0: 250, sl@0: 250, sl@0: 250, sl@0: 250, sl@0: 250, sl@0: 250, sl@0: 250, sl@0: 250, sl@0: 250, // 200 sl@0: 250, sl@0: 250, sl@0: 250, sl@0: 251, sl@0: 251, sl@0: 251, sl@0: 251, sl@0: 251, sl@0: 251, sl@0: 251, // 210 sl@0: 251, sl@0: 251, sl@0: 251, sl@0: 251, sl@0: 251, sl@0: 252, sl@0: 252, sl@0: 252, sl@0: 252, sl@0: 252, // 220 sl@0: 252, sl@0: 252, sl@0: 252, sl@0: 252, sl@0: 252, sl@0: 252, sl@0: 252, sl@0: 252, sl@0: 253, sl@0: 253, // 230 sl@0: 253, sl@0: 253, sl@0: 253, sl@0: 253, sl@0: 253, sl@0: 253, sl@0: 253, sl@0: 253, sl@0: 253, sl@0: 253, // 240 sl@0: 253, sl@0: 254, sl@0: 254, sl@0: 254, sl@0: 254, sl@0: 254, sl@0: 254, sl@0: 254, sl@0: 254, sl@0: 254, // 250 sl@0: 254, sl@0: 254, sl@0: 254, sl@0: 254, sl@0: 254 sl@0: }; sl@0: sl@0: // rate lookup table sl@0: sl@0: const TInt KNumSampleRates = 9; sl@0: sl@0: struct TSampleRateEnumTable sl@0: { sl@0: TInt iRate; sl@0: TSoundRate iRateEnum; sl@0: TUint iRateConstant; sl@0: }; sl@0: //Table that maps given samples per second to the corresponding enums in RSoundSc sl@0: const TSampleRateEnumTable KRateEnumLookup[] = sl@0: { sl@0: {48000,ESoundRate48000Hz,KSoundRate48000Hz}, sl@0: {44100,ESoundRate44100Hz,KSoundRate44100Hz}, sl@0: {32000,ESoundRate32000Hz,KSoundRate32000Hz}, sl@0: {24000,ESoundRate24000Hz,KSoundRate24000Hz}, sl@0: {22050,ESoundRate22050Hz,KSoundRate22050Hz}, sl@0: {16000,ESoundRate16000Hz,KSoundRate16000Hz}, sl@0: {12000,ESoundRate12000Hz,KSoundRate12000Hz}, sl@0: {11025,ESoundRate11025Hz,KSoundRate11025Hz}, sl@0: {8000, ESoundRate8000Hz, KSoundRate8000Hz} sl@0: }; sl@0: sl@0: // TAudioInputParams sl@0: sl@0: EXPORT_C TAudioInputParams::TAudioInputParams() : sl@0: iSampleRate(0), iNominalBufferSize(0) sl@0: { sl@0: // none sl@0: } sl@0: sl@0: // CAudioInput sl@0: sl@0: EXPORT_C MAudioInput* MAudioInput::CreateL(MAudioInputObserver& aObserver) sl@0: { sl@0: MAudioInput* result = CAudioInput::NewL(aObserver); sl@0: return result; sl@0: } sl@0: sl@0: CAudioInput* CAudioInput::NewL(MAudioInputObserver& aObserver) sl@0: { sl@0: CAudioInput* result = new CAudioInput(aObserver); sl@0: CleanupStack::PushL(result); sl@0: result->ConstructL(); sl@0: CleanupStack::Pop(result); sl@0: return result; sl@0: } sl@0: sl@0: CAudioInput::CAudioInput(MAudioInputObserver& aObserver) : sl@0: iObserver(aObserver), sl@0: iIdleQueue(_FOFF(CRecorder,iLink)), sl@0: iRecordingQueue(_FOFF(CRecorder,iLink)), sl@0: iPendingQueue(_FOFF(CRecorder,iLink)), sl@0: iBusyQueue(_FOFF(CRecorder,iLink)) sl@0: { sl@0: ASSERT(iState == EStateCreated2); // assume zero'ing initialises correctly sl@0: } sl@0: sl@0: void CAudioInput::Release() sl@0: // effective destructor call sl@0: { sl@0: delete this; sl@0: } sl@0: sl@0: TAny* CAudioInput::Interface(TUid aInterfaceUid) sl@0: { sl@0: if (aInterfaceUid == KUidAIParamInterface) sl@0: { sl@0: MAIParamInterface* self = this; sl@0: return self; sl@0: } sl@0: return NULL; sl@0: } sl@0: sl@0: RSoundSc& CAudioInput::RecordSoundDevice() sl@0: { sl@0: ASSERT(iRecordSoundDevice.Handle()!=0); // should be open sl@0: return iRecordSoundDevice; sl@0: } sl@0: sl@0: CAudioInput::~CAudioInput() sl@0: { sl@0: CheckInvariant(EFalse); // may not be constructed sl@0: Cancel(); sl@0: for (TInt i = 0; i < KNumRecorders; i++) sl@0: { sl@0: // just in case, call cancel directly from this point too sl@0: // Cancel depends on the active queue, and might not be quite the same. sl@0: CRecorder* recorder = iRecorders[i]; sl@0: if (recorder) sl@0: { sl@0: recorder->Cancel(); sl@0: } sl@0: delete recorder; sl@0: } sl@0: delete iAsyncCallBack; sl@0: iConvBuff.Close(); sl@0: iRecordSoundDevice.Close(); sl@0: iChunk.Close(); sl@0: } sl@0: sl@0: void CAudioInput::Cancel() sl@0: { sl@0: #ifdef SYMBIAN_SWCODEC_LOGGING sl@0: RDebug::Print(_L("--->CAudioInput::Cancel()")); sl@0: #endif sl@0: CancelRecorders(); sl@0: if (iAsyncCallBack) sl@0: { sl@0: iAsyncCallBack->Cancel(); sl@0: } sl@0: #ifdef SYMBIAN_SWCODEC_LOGGING sl@0: RDebug::Print(_L("<---CAudioInput::Cancel()")); sl@0: #endif sl@0: } sl@0: sl@0: void CAudioInput::CancelRecorders() sl@0: // if a recorder is active, then cancel it. Also move the list if required. sl@0: { sl@0: CheckInvariant(); // semi-invariant check - this is called from destructor sl@0: sl@0: CRecorder* recorder; sl@0: while (QPop(recorder, iRecordingQueue)) sl@0: { sl@0: recorder->Cancel(); sl@0: iIdleQueue.AddLast(*recorder); sl@0: } sl@0: CheckFullInvariant(); sl@0: } sl@0: sl@0: void CAudioInput::CancelPendingRecorders() sl@0: // take any recorder in the pending queue. ack the buffer and send to idle sl@0: { sl@0: CheckFullInvariant(); sl@0: sl@0: CRecorder* recorder; sl@0: while (QPop(recorder, iPendingQueue)) sl@0: { sl@0: recorder->ReleaseBuffer(); sl@0: iIdleQueue.AddLast(*recorder); sl@0: } sl@0: CheckFullInvariant(); sl@0: } sl@0: sl@0: void CAudioInput::CancelBusyRecorder() sl@0: // take busy recorder. ack the buffer and send to idle sl@0: { sl@0: CheckFullInvariant(); sl@0: sl@0: CRecorder* recorder; sl@0: if (QPop(recorder, iBusyQueue)) sl@0: { sl@0: recorder->ReleaseBuffer(); sl@0: iIdleQueue.AddLast(*recorder); sl@0: } sl@0: CheckFullInvariant(); sl@0: } sl@0: sl@0: void CAudioInput::RecordAllIdle() sl@0: // take any recorder in idle queue and set recording sl@0: { sl@0: CheckFullInvariant(); sl@0: sl@0: CRecorder* recorder; sl@0: while (QPop(recorder, iIdleQueue)) sl@0: { sl@0: recorder->RecordData(); sl@0: iRecordingQueue.AddLast(*recorder); sl@0: } sl@0: CheckFullInvariant(); sl@0: } sl@0: sl@0: void CAudioInput::ConstructL() sl@0: { sl@0: for (TInt i = 0; i < KNumRecorders; i++) sl@0: { sl@0: iRecorders[i] = new (ELeave) CRecorder(*this, i); sl@0: iIdleQueue.AddLast(*(iRecorders[i])); sl@0: } sl@0: iAsyncCallBack = new (ELeave) CAsyncCallBack(CActive::EPriorityStandard); sl@0: TCallBack callback(Callback, this); sl@0: iAsyncCallBack->Set(callback); sl@0: User::LoadPhysicalDevice(KPddFileName); sl@0: User::LoadLogicalDevice(KLddFileName); sl@0: CheckFullInvariant(); sl@0: } sl@0: sl@0: TInt CAudioInput::Initialize(const TAudioInputParams& aParams) sl@0: { sl@0: #ifdef SYMBIAN_SWCODEC_LOGGING sl@0: RDebug::Print(_L("--->CAudioInput::Initialize() state=%s rstate=%s"), StateName(iState), RStateName(iRState)); sl@0: #endif sl@0: CheckFullInvariant(); sl@0: TInt error = KErrNone; sl@0: if (iState == EStateCreated2) sl@0: { sl@0: if (!iRecordSoundDevice.Handle()) sl@0: { sl@0: error = iRecordSoundDevice.Open(KSoundScRxUnit0); sl@0: if (error) sl@0: { sl@0: Close(); // TODO Close() required? sl@0: } sl@0: } sl@0: if (!error) sl@0: { sl@0: iBufferLength = aParams.iNominalBufferSize; // will be updated by SetFormat() if required sl@0: error = SetFormat(aParams); sl@0: if (!error) sl@0: { sl@0: iRecordBufferConfig.iNumBuffers = KNumRecorders*2; // for each AO we create two buffers sl@0: iRecordBufferConfig.iFlags = 0; sl@0: iRecordBufferConfig.iBufferSizeInBytes = iBufferLength; sl@0: ASSERT(iChunk.Handle()==0); // should not be already open sl@0: TPckg<TRecordSharedChunkBufConfig> bufferConfigBuf( sl@0: iRecordBufferConfig); sl@0: error = iRecordSoundDevice.SetBufferChunkCreate( sl@0: bufferConfigBuf, iChunk); sl@0: #ifdef SYMBIAN_SWCODEC_LOGGING sl@0: RDebug::Print( sl@0: _L("iRecordBufferConfig.iNumBuffers = [%d]"),iRecordBufferConfig.iNumBuffers); sl@0: RDebug::Print( sl@0: _L("iRecordBufferConfig.iFlags = [%d]"),iRecordBufferConfig.iFlags); sl@0: RDebug::Print( sl@0: _L("iRecordBufferConfig.iBufferSizeInBytes = [%d]"),iRecordBufferConfig.iBufferSizeInBytes); sl@0: #endif sl@0: if (error == KErrNone) sl@0: { sl@0: ASSERT(iChunk.Handle()); // should now be open sl@0: iRecordSoundDevice.GetBufferConfig(bufferConfigBuf); // overwrite iRecordBufferConfig sl@0: SetGain(aParams.iInitialGain); sl@0: iState = EStateInitialized2; sl@0: } sl@0: } sl@0: } sl@0: } sl@0: else sl@0: { sl@0: error = KErrNotReady; sl@0: } sl@0: #ifdef SYMBIAN_SWCODEC_LOGGING sl@0: RDebug::Print(_L("<---CAudioInput::Initialize(%d) state=%s rstate=%s"), error, StateName( sl@0: iState), RStateName(iRState)); sl@0: #endif sl@0: CheckFullInvariant(); sl@0: return error; sl@0: } sl@0: sl@0: void CAudioInput::Close() sl@0: { sl@0: #ifdef SYMBIAN_SWCODEC_LOGGING sl@0: RDebug::Print(_L("--->CAudioInput::Close() state=%s rstate=%s"), StateName(iState), RStateName(iRState)); sl@0: #endif sl@0: CheckFullInvariant(); sl@0: InternalStop(); // Technically this should not be required, as client should Stop() first, but just in case sl@0: if (iState == EStateInitialized2) sl@0: { sl@0: iRecordSoundDevice.Close(); sl@0: iChunk.Close(); sl@0: iConvBuff.Close(); sl@0: iState = EStateCreated2; sl@0: } sl@0: ASSERT(iState==EStateCreated2); sl@0: ASSERT(QLength(iIdleQueue)==KNumRecorders); sl@0: #ifdef SYMBIAN_SWCODEC_LOGGING sl@0: RDebug::Print(_L("<---CAudioInput::Close() state=%s rstate=%s"), StateName(iState), RStateName(iRState)); sl@0: #endif sl@0: CheckFullInvariant(); sl@0: } sl@0: sl@0: TInt CAudioInput::Start() sl@0: { sl@0: #ifdef SYMBIAN_SWCODEC_LOGGING sl@0: RDebug::Print(_L("--->CAudioInput::Start() state=%s rstate=%s"), StateName(iState), RStateName(iRState)); sl@0: #endif sl@0: CheckFullInvariant(); sl@0: TInt error = KErrNone; sl@0: if (iState == EStateInitialized2) sl@0: { sl@0: RecordAllIdle(); sl@0: iState = EStateRecordWait2; sl@0: iRState = ERStateRunning; sl@0: } sl@0: else sl@0: { sl@0: error = KErrNotReady; sl@0: } sl@0: #ifdef SYMBIAN_SWCODEC_LOGGING sl@0: RDebug::Print(_L("<---CAudioInput::Start(%d) state=%s rstate=%s"), sl@0: error, StateName(iState), RStateName(iRState)); sl@0: #endif sl@0: CheckFullInvariant(); sl@0: return error; sl@0: } sl@0: sl@0: void CAudioInput::BufferAck() sl@0: { sl@0: #ifdef SYMBIAN_SWCODEC_LOGGING sl@0: RDebug::Print(_L("--->CAudioInput::BufferAck() state=%s rstate=%s"), StateName(iState), RStateName(iRState)); sl@0: #endif sl@0: CheckFullInvariant(); sl@0: ASSERT(iState==EStateRecordWaitAck2); sl@0: HandleBufferAck(); sl@0: iState = EStateRecordWait2; sl@0: RequestCallback(); sl@0: #ifdef SYMBIAN_SWCODEC_LOGGING sl@0: RDebug::Print(_L("<---CAudioInput::BufferAck() state=%s rstate=%s"), StateName(iState), RStateName(iRState)); sl@0: #endif sl@0: CheckFullInvariant(); sl@0: } sl@0: sl@0: void CAudioInput::HandleBufferAck() sl@0: { sl@0: CRecorder* recorder = QPop(iBusyQueue); sl@0: recorder->ReleaseBuffer(); sl@0: if (iRState == ERStateRunning) sl@0: { sl@0: recorder->RecordData(); sl@0: iRecordingQueue.AddLast(*recorder); sl@0: } sl@0: else sl@0: { sl@0: iIdleQueue.AddLast(*recorder); sl@0: if (iRState == ERStatePaused && (QLength(iRecordingQueue)+QLength(iPendingQueue) == 0)) sl@0: { sl@0: iRState = ERStateFinishing; sl@0: } sl@0: } sl@0: } sl@0: sl@0: TInt CAudioInput::Pause() sl@0: { sl@0: #ifdef SYMBIAN_SWCODEC_LOGGING sl@0: RDebug::Print(_L("--->CAudioInput::Pause() state=%s rstate=%s"), StateName(iState), RStateName(iRState)); sl@0: #endif sl@0: CheckFullInvariant(); sl@0: TInt err = KErrNone; // note we are silent if called in wrong state sl@0: sl@0: #ifdef SYMBIAN_SWCODEC_LOGGING sl@0: RDebug::Print( sl@0: _L("***!pause irecordingquelength %d pendingquelength %d ibusyquelength=%d"), QLength(iRecordingQueue), sl@0: QLength(iPendingQueue), QLength(iBusyQueue)); sl@0: #endif sl@0: if ((iState == EStateRecordWait2 || iState == EStateRecordWaitAck2) && iRState==ERStateRunning) sl@0: { sl@0: iRecordSoundDevice.Pause(); sl@0: sl@0: iRState = ERStatePaused; sl@0: } sl@0: #ifdef SYMBIAN_SWCODEC_LOGGING sl@0: RDebug::Print(_L("<---CAudioInput::Pause(%d) state=%s err=%d"), err, StateName(iState), RStateName(iRState)); sl@0: #endif sl@0: CheckFullInvariant(); sl@0: return err; sl@0: } sl@0: sl@0: TInt CAudioInput::Resume() sl@0: { sl@0: #ifdef SYMBIAN_SWCODEC_LOGGING sl@0: RDebug::Print(_L("--->CAudioInput::Resume() state=%s rstate=%s"), StateName(iState), RStateName(iRState)); sl@0: #endif sl@0: CheckFullInvariant(); sl@0: TInt err = KErrNone; // note we are silent if called in the wrong state sl@0: if ((iState == EStateRecordWait2 || iState == EStateRecordWaitAck2) && sl@0: ((iRState==ERStatePaused || iRState==ERStateFinishing || iRState==ERStateFinished))) sl@0: { sl@0: err = RecordSoundDevice().Resume(); sl@0: sl@0: RecordAllIdle(); sl@0: sl@0: iRState = ERStateRunning; sl@0: } sl@0: #ifdef SYMBIAN_SWCODEC_LOGGING sl@0: RDebug::Print(_L("<---CAudioInput::Resume(%d) state=%s rstate=%s"), err, StateName(iState), RStateName(iRState)); sl@0: #endif sl@0: CheckFullInvariant(); sl@0: return err; sl@0: } sl@0: sl@0: TInt CAudioInput::Flush() sl@0: { sl@0: #ifdef SYMBIAN_SWCODEC_LOGGING sl@0: RDebug::Print(_L("--->CAudioInput::Flush() state=%s rstate=%s"), StateName(iState), RStateName(iRState)); sl@0: #endif sl@0: CheckFullInvariant(); sl@0: TInt error = KErrNotReady; sl@0: if (iRState==ERStatePaused) sl@0: { sl@0: if (iState == EStateRecordWait2) sl@0: { sl@0: InternalFlush(); sl@0: ASSERT(iState == EStateRecordWait2); // stay put sl@0: error = KErrNone; sl@0: } sl@0: else if (iState == EStateRecordWaitAck2) sl@0: { sl@0: InternalFlush(); sl@0: iState = EStateRecordWait2; sl@0: error = KErrNone; sl@0: } sl@0: } sl@0: #ifdef SYMBIAN_SWCODEC_LOGGING sl@0: RDebug::Print(_L("--->CAudioInput::Flush(%d) state=%s rstate=%s"), error, StateName(iState), RStateName(iRState)); sl@0: #endif sl@0: CheckFullInvariant(); sl@0: return error; sl@0: } sl@0: sl@0: void CAudioInput::Stop() sl@0: { sl@0: #ifdef SYMBIAN_SWCODEC_LOGGING sl@0: RDebug::Print(_L("--->CAudioInput::Stop() state=%s rstate=%s"), StateName(iState), RStateName(iRState)); sl@0: #endif sl@0: CheckFullInvariant(); sl@0: InternalStop(); sl@0: #ifdef SYMBIAN_SWCODEC_LOGGING sl@0: RDebug::Print(_L("<---CAudioInput::Stop() state=%s rstate=%s"), StateName(iState), RStateName(iRState)); sl@0: #endif sl@0: } sl@0: sl@0: void CAudioInput::InternalStop() sl@0: // This stops all recording and returns pending and busy buffers to idle. Must be called when sl@0: // client knows the buffer has been grabbed (e.g. _not_ before error callback) sl@0: { sl@0: CheckInvariant(); // Can be called from buffer error, so can't check full invariant. sl@0: if (iState != EStateInitialized2 && iState != EStateCreated2) sl@0: { sl@0: InternalFlush(); sl@0: iState = EStateInitialized2; sl@0: } sl@0: CheckFullInvariant(); sl@0: ASSERT((QLength(iRecordingQueue) + QLength(iPendingQueue) + sl@0: QLength(iBusyQueue))==0); // everything is stopped sl@0: } sl@0: sl@0: void CAudioInput::InternalFlush() sl@0: { sl@0: CancelRecorders(); sl@0: CancelPendingRecorders(); sl@0: CancelBusyRecorder(); sl@0: } sl@0: sl@0: void CAudioInput::BufferArrives(CRecorder* aRecorder) sl@0: { sl@0: #ifdef SYMBIAN_SWCODEC_LOGGING sl@0: RDebug::Print( sl@0: _L("--->CAudioInput::BufferArrives(%d,%d) state=%s rstate=%s"), aRecorder->Index(), sl@0: aRecorder->StatusOrOffset(), StateName(iState), RStateName(iRState)); sl@0: #endif sl@0: CheckInvariant(); // Can't use CheckFullInvariant() from RunL sl@0: ASSERT(iState==EStateRecordWait2 || iState==EStateRecordWaitAck2); sl@0: ASSERT(aRecorder->Offset()>=0); // assert we're not here due to an error sl@0: iRecordingQueue.Remove(*aRecorder); sl@0: iPendingQueue.AddLast(*aRecorder); sl@0: if (iState==EStateRecordWait2) sl@0: { sl@0: RequestCallback(); sl@0: } sl@0: #ifdef SYMBIAN_SWCODEC_LOGGING sl@0: RDebug::Print(_L("<---CAudioInput::BufferArrives() state=%s rstate=%s"), sl@0: StateName(iState), RStateName(iRState)); sl@0: #endif sl@0: CheckFullInvariant(); sl@0: } sl@0: sl@0: void CAudioInput::UseBuffer(CRecorder* aRecorder) sl@0: // incomming buffer is pointed to by iBufPtr. Either directly or via convert, use for callback sl@0: { sl@0: iBufPtr.Set(iChunk.Base() + aRecorder->Offset(), aRecorder->Length()); sl@0: if (iConverter) sl@0: { sl@0: #ifdef SYMBIAN_SWCODEC_LOGGING sl@0: RDebug::Print(_L("iBufPtr length [%d] iconvbuff length [%d,%d]"), sl@0: iBufPtr.Length(), iConvBuff.Length(), iConvBuff.MaxLength()); sl@0: #endif sl@0: __DEBUG_ONLY(TInt converted =) iConverter->Convert(iBufPtr, iConvBuff); sl@0: // the following assert should check we convert the log. sl@0: // Actually we sometimes fail at the end of the operation with what is effectively sl@0: // the last buffer. Arguably a driver fault, but there we are sl@0: // ASSERT(converted==iBufPtr.Length()); sl@0: #ifdef SYMBIAN_SWCODEC_LOGGING sl@0: RDebug::Print(_L("iBufPtr length [%d] iconvbuff length after [%d,%d]"), sl@0: iBufPtr.Length(), iConvBuff.Length(), iConvBuff.MaxLength()); sl@0: #endif sl@0: iObserver.InputBufferAvailable(iConvBuff); sl@0: } sl@0: else sl@0: { sl@0: iObserver.InputBufferAvailable(iBufPtr); sl@0: } sl@0: #ifdef SYMBIAN_SWCODEC_LOGGING sl@0: RDebug::Print(_L("12345 ibufptr = [0x%x]"),iBufPtr.Ptr()); sl@0: #endif sl@0: } sl@0: sl@0: void CAudioInput::BufferError(CRecorder* aRecorder, TInt aError) sl@0: { sl@0: #ifdef SYMBIAN_SWCODEC_LOGGING sl@0: RDebug::Print( sl@0: _L("--->CAudioInput::BufferError(%d,%d) state=%s rstate=%s"), aRecorder->Index(), sl@0: aError, StateName(iState), RStateName(iRState)); sl@0: #endif sl@0: CheckInvariant(); // Can't use CheckFullInvariant() from RunL sl@0: if (aError==KErrCancel || aError==KErrOverflow) sl@0: { sl@0: // Cancel: sign of a Pause operation. If paused etc, then merely add to idle list. potentially generate finished signal sl@0: // if not paused, then not clear but just in case request record again sl@0: // Overflow: basically try again, but if paused merely add to idle. Check for last buffer just in case sl@0: if (iRState!=ERStateRunning) sl@0: { sl@0: iRecordingQueue.Remove(*aRecorder); sl@0: iIdleQueue.AddLast(*aRecorder); sl@0: #ifdef SYMBIAN_SWCODEC_LOGGING sl@0: RDebug::Print( sl@0: _L("***! irecordingquelength %d pendingquelength %d ibusyquelength=%d"), QLength(iRecordingQueue), sl@0: QLength(iPendingQueue), QLength(iBusyQueue)); sl@0: #endif sl@0: if (iRState == ERStatePaused && (QLength(iRecordingQueue)+QLength(iPendingQueue)+QLength(iBusyQueue) == 0)) sl@0: { sl@0: iRState = ERStateFinishing; sl@0: RequestCallback(); sl@0: } sl@0: } sl@0: else sl@0: { sl@0: aRecorder->RecordData(); sl@0: } sl@0: } sl@0: else sl@0: { sl@0: iRecordingQueue.Remove(*aRecorder); sl@0: iIdleQueue.AddLast(*aRecorder); sl@0: iRState = ERStateFailed; sl@0: iObserver.InputError(aError); sl@0: } sl@0: #ifdef SYMBIAN_SWCODEC_LOGGING sl@0: RDebug::Print(_L("<---CAudioInput::BufferError() state=%s rstate=%s"), sl@0: StateName(iState), RStateName(iRState)); sl@0: #endif sl@0: CheckFullInvariant(); sl@0: } sl@0: sl@0: TInt CAudioInput::Callback(TAny* aPtr) sl@0: { sl@0: CAudioInput* self = static_cast<CAudioInput*> (aPtr); sl@0: TRAPD(error,self->AsyncCallbackL()); sl@0: return error; // TODO really have to handle error sl@0: } sl@0: sl@0: void CAudioInput::RequestCallback() sl@0: { sl@0: // ensure iAsyncCallBack is active sl@0: if (!iAsyncCallBack->IsActive()) sl@0: { sl@0: iAsyncCallBack->Call(); sl@0: } sl@0: } sl@0: sl@0: void CAudioInput::AsyncCallbackL() sl@0: { sl@0: #ifdef SYMBIAN_SWCODEC_LOGGING sl@0: RDebug::Print(_L("--->CAudioInput::AsyncCallbackL() state=%s rstate=%s"), sl@0: StateName(iState), RStateName(iRState)); sl@0: #endif sl@0: CheckFullInvariant(); sl@0: ASSERT(iState==EStateRecordWait2 || iState==EStateRecordWaitAck2); // should not occur in other states. Actually ignore in 2nd sl@0: if (iState==EStateRecordWait2) sl@0: { sl@0: if (QLength(iPendingQueue)>0) sl@0: { sl@0: ASSERT(QLength(iBusyQueue)==0); sl@0: iState = EStateRecordWaitAck2; // change state prior to callback, in case sync call from callback sl@0: CRecorder* recorder = QPop(iPendingQueue); sl@0: iBusyQueue.AddLast(*recorder); sl@0: UseBuffer(recorder); sl@0: } sl@0: else sl@0: { sl@0: if (iRState == ERStateFinishing) sl@0: { sl@0: ASSERT(QLength(iRecordingQueue)+QLength(iPendingQueue)+QLength(iBusyQueue) == 0); // should be true sl@0: iRState = ERStateFinished; sl@0: iObserver.InputFinished(); sl@0: } sl@0: } sl@0: } sl@0: #ifdef SYMBIAN_SWCODEC_LOGGING sl@0: RDebug::Print(_L("<---CAudioInput::AsyncCallbackL() state=%s rstate=%s"), sl@0: StateName(iState), RStateName(iRState)); sl@0: #endif sl@0: CheckFullInvariant(); sl@0: } sl@0: sl@0: TInt CAudioInput::GetBufferSizes(TInt& aMinSize, TInt& aMaxSize) sl@0: { sl@0: aMinSize = KMinBufferSize; sl@0: aMaxSize = KMaxBufferSize; sl@0: return KErrNone; sl@0: } sl@0: sl@0: TInt CAudioInput::SetGain(TInt aGain) sl@0: { sl@0: TInt error = KErrNone; // note: silent if in wrong state sl@0: if (iRecordSoundDevice.Handle()) sl@0: { sl@0: // we have to switch from level to dB value sl@0: if(aGain >=0 && aGain<=KSoundMaxVolume) sl@0: { sl@0: error = iRecordSoundDevice.SetVolume(KLinearToDbConstantLookup[aGain]); sl@0: } sl@0: else sl@0: { sl@0: error = KErrArgument; sl@0: } sl@0: } sl@0: return error; sl@0: } sl@0: sl@0: TInt CAudioInput::SetFormat(const TAudioInputParams& aFormat) sl@0: { sl@0: TInt err = KErrNotFound; sl@0: TCurrentSoundFormatV02Buf formatBuf; sl@0: TFormatData formatData; sl@0: sl@0: delete iConverter; sl@0: iConverter = NULL; // setting this to NULL indicates we are not using converter. No other flag sl@0: sl@0: TInt wantedRate = aFormat.iSampleRate; sl@0: for (TInt index = 0; index < KNumSampleRates; index++) sl@0: { sl@0: if (wantedRate == KRateEnumLookup[index].iRate) sl@0: { sl@0: formatBuf().iRate = KRateEnumLookup[index].iRateEnum; sl@0: formatData.iSampleRate = wantedRate; sl@0: err = KErrNone; sl@0: break; sl@0: } sl@0: } sl@0: sl@0: if (err == KErrNone) sl@0: { sl@0: formatBuf().iChannels = aFormat.iNumChannels; sl@0: formatBuf().iEncoding = ESoundEncoding16BitPCM; sl@0: formatBuf().iDataFormat = ESoundDataFormatInterleaved; sl@0: err = iRecordSoundDevice.SetAudioFormat(formatBuf); sl@0: #if defined(SYMBIAN_SOUNDADAPTER_FORCECDRATES) || defined (SYMBIAN_SOUNDADAPTER_FORCESTEREO) sl@0: err = KErrNotSupported; // force Negotiate - for debugging sl@0: #endif sl@0: if (err == KErrNotSupported) sl@0: { sl@0: // don't support directly. Perhaps can rate convert? sl@0: err = NegotiateFormat(aFormat, formatData); sl@0: } sl@0: } sl@0: return err; sl@0: } sl@0: sl@0: TInt CAudioInput::NegotiateFormat(const TAudioInputParams& aFormat, TFormatData &aFormatData) sl@0: { sl@0: TInt err = KErrNotFound; sl@0: TCurrentSoundFormatV02Buf formatBuf; sl@0: sl@0: TInt origBufferLength = iBufferLength; // cache in case we change sl@0: sl@0: // find out first what the driver supports sl@0: TSoundFormatsSupportedV02Buf supportedFormat; sl@0: iRecordSoundDevice.Caps(supportedFormat); sl@0: TUint32 supportedRates = supportedFormat().iRates; sl@0: #ifdef SYMBIAN_SOUNDADAPTER_FORCECDRATES sl@0: supportedRates &= KSoundRate11025Hz | KSoundRate22050Hz sl@0: | KSoundRate44100Hz; // only use CD rates - for debugging sl@0: #endif sl@0: sl@0: // For RecordCase: sl@0: // We want the next rate above consistently - we go down from this to the requested rate. sl@0: // If there is one, we don't support - we _never_ upsample. sl@0: // note that the table is given in descending order, so we start with the highest sl@0: TInt wantedRate = aFormat.iSampleRate; sl@0: TInt takeTheFirst = EFalse; sl@0: TInt nextUpValidIndex = -1; sl@0: for (TInt index = 0; index < KNumSampleRates; index++) sl@0: { sl@0: TBool lookingAtRequestedRate = wantedRate sl@0: == KRateEnumLookup[index].iRate; sl@0: TSoundRate wantedEnum = KRateEnumLookup[index].iRateEnum; sl@0: TUint32 equivBitmap = KRateEnumLookup[index].iRateConstant; sl@0: TBool isSupported = (equivBitmap & supportedRates) != EFalse; sl@0: if (lookingAtRequestedRate || takeTheFirst) sl@0: { sl@0: if (isSupported) sl@0: { sl@0: // this rate is supported sl@0: formatBuf().iRate = wantedEnum; sl@0: aFormatData.iActualRate = KRateEnumLookup[index].iRate; sl@0: err = KErrNone; sl@0: break; sl@0: } sl@0: } sl@0: else if (!takeTheFirst) sl@0: { sl@0: // while we are still looking for the rate, want to cache any supported index sl@0: // at end of loop, this will be the first rate above ours that is supported sl@0: // use for fallback if required sl@0: if (isSupported) sl@0: { sl@0: nextUpValidIndex = index; sl@0: } sl@0: } sl@0: if (lookingAtRequestedRate) sl@0: { sl@0: // For record we just abort. sl@0: break; sl@0: } sl@0: } sl@0: sl@0: if (err) sl@0: { sl@0: // if there is one above the requested rate, use that sl@0: if (nextUpValidIndex >= 0) sl@0: { sl@0: TSoundRate wantedEnum = sl@0: KRateEnumLookup[nextUpValidIndex].iRateEnum; sl@0: formatBuf().iRate = wantedEnum; sl@0: aFormatData.iActualRate = KRateEnumLookup[nextUpValidIndex].iRate; sl@0: err = KErrNone; sl@0: } sl@0: } sl@0: sl@0: if (err) sl@0: { sl@0: // should have something! sl@0: return err; sl@0: } sl@0: sl@0: aFormatData.iSampleRate = wantedRate; // iSampleRate is our requested/apparent rate, not the device rate. sl@0: sl@0: TUint32 channelsSupported = supportedFormat().iChannels; sl@0: #ifdef SYMBIAN_SOUNDADAPTER_FORCESTEREO sl@0: channelsSupported &= KSoundStereoChannel; // don't use mono - for debugging sl@0: #endif sl@0: sl@0: if (aFormat.iNumChannels == 1) sl@0: { sl@0: aFormatData.iRequestedChannels = 1; sl@0: // want mono sl@0: if (channelsSupported & KSoundMonoChannel) sl@0: { sl@0: // mono is supported, as usual sl@0: aFormatData.iActualChannels = 1; sl@0: } sl@0: else if (channelsSupported & KSoundStereoChannel) sl@0: { sl@0: aFormatData.iActualChannels = 2; sl@0: iBufferLength *= 2; // double size, will do stereo->mono sl@0: } sl@0: else sl@0: { sl@0: return KErrNotSupported; // should not get this far for real sl@0: } sl@0: } sl@0: else if (aFormat.iNumChannels == 2) sl@0: { sl@0: aFormatData.iRequestedChannels = 2; sl@0: // want stereo sl@0: if (channelsSupported & KSoundStereoChannel) sl@0: { sl@0: // stereo is supported, as usual sl@0: aFormatData.iActualChannels = 2; sl@0: } sl@0: else if (channelsSupported & KSoundMonoChannel) sl@0: { sl@0: aFormatData.iActualChannels = 1; sl@0: iBufferLength /= 2; // halve size, will do mono->stereo sl@0: } sl@0: else sl@0: { sl@0: return KErrNotSupported; // should not get this far for real sl@0: } sl@0: } sl@0: else sl@0: { sl@0: return KErrNotSupported; // unknown number of channels requested! sl@0: } sl@0: sl@0: formatBuf().iChannels = aFormatData.iActualChannels; sl@0: sl@0: formatBuf().iEncoding = ESoundEncoding16BitPCM; sl@0: formatBuf().iDataFormat = ESoundDataFormatInterleaved; sl@0: err = iRecordSoundDevice.SetAudioFormat(formatBuf); sl@0: sl@0: if (!err) sl@0: { sl@0: ASSERT(!iConverter); // pre-condition at top of function anyway sl@0: // when recording we convert from actual to requested sl@0: TInt outputRateToUse = aFormatData.iSampleRate; sl@0: #ifdef SYMBIAN_SKIP_RESAMPLE_ON_RECORD sl@0: // with this macro just channel convert at most sl@0: outputRateToUse = aFormatData.iActualRate; sl@0: #endif sl@0: #ifdef SYMBIAN_SOUNDADAPTER_DEBUG sl@0: RDebug::Print(_L("RMdaDevSound::CBody::NegotiateFormat: Convert:CreateL from %d/%d to %d/%d"), sl@0: aFormatData.iActualRate, aFormatData.iActualChannels, sl@0: aFormatData.iSampleRate, aFormatData.iRequestedChannels); sl@0: #endif sl@0: TRAP(err, iConverter = CChannelAndSampleRateConverter::CreateL(aFormatData.iActualRate, sl@0: aFormatData.iActualChannels, sl@0: outputRateToUse, sl@0: aFormatData.iRequestedChannels)); sl@0: } sl@0: if (!err && iConverter) sl@0: { sl@0: err = iConvBuff.Create(origBufferLength); sl@0: #ifdef SYMBIAN_SWCODEC_LOGGING sl@0: RDebug::Print(_L("iBufferLength length [%d] iconvbuff length [%d,%d]"), sl@0: iBufferLength, iConvBuff.Length(), iConvBuff.MaxLength()); sl@0: #endif sl@0: } sl@0: sl@0: return err; sl@0: } sl@0: sl@0: TInt CAudioInput::GetSupportedSampleRates(RArray<TInt>& aSupportedSampleRates) sl@0: { sl@0: TInt err = KErrNone; sl@0: sl@0: if (iRecordSoundDevice.Handle()) sl@0: { sl@0: GetSupportedSampleRates(aSupportedSampleRates, iRecordSoundDevice); sl@0: } sl@0: else sl@0: {//temporarily open the device if we can sl@0: RSoundSc tempsound; sl@0: err = tempsound.Open(KSoundScRxUnit0); sl@0: if (!err) sl@0: { sl@0: err = GetSupportedSampleRates(aSupportedSampleRates, tempsound); sl@0: tempsound.Close(); sl@0: } sl@0: } sl@0: return err; sl@0: } sl@0: sl@0: TInt CAudioInput::GetSupportedSampleRates( sl@0: RArray<TInt>& aSupportedSampleRates, RSoundSc& aSoundDevice) sl@0: { sl@0: ASSERT(aSoundDevice.Handle()); // parent to ensure this is open sl@0: sl@0: TInt err = KErrNone; sl@0: sl@0: TSoundFormatsSupportedV02Buf supportedFormat; sl@0: aSoundDevice.Caps(supportedFormat); sl@0: TUint32 rates = supportedFormat().iRates; sl@0: sl@0: for (TInt i = KNumSampleRates - 1; i > 0; i--)//min to max sl@0: { sl@0: if (rates & KRateEnumLookup[i].iRateConstant) sl@0: { sl@0: err = aSupportedSampleRates.Append(KRateEnumLookup[i].iRate); sl@0: if (err) sl@0: { sl@0: break; sl@0: } sl@0: } sl@0: } sl@0: return err; sl@0: } sl@0: sl@0: TInt CAudioInput::QLength(TSglQue<CRecorder>& aQueue) sl@0: // count elements in List/Q. Have to use iterator to do this - it seems. sl@0: { sl@0: TSglQueIter<CRecorder> iter(aQueue); sl@0: TInt count=0; sl@0: while (iter++) sl@0: { sl@0: // like old-fashioned C string manipulations. iterate through all members sl@0: count++; sl@0: } sl@0: return count; sl@0: } sl@0: sl@0: CAudioInput::CRecorder* CAudioInput::QPop(TSglQue<CRecorder>& aQueue) sl@0: { sl@0: CRecorder* recorder = NULL; sl@0: if (! aQueue.IsEmpty()) sl@0: { sl@0: recorder = aQueue.First(); sl@0: aQueue.Remove(*recorder); sl@0: } sl@0: return recorder; sl@0: } sl@0: sl@0: #ifdef _DEBUG sl@0: sl@0: // these functions are used in invariant checking only sl@0: sl@0: void CAudioInput::CheckActiveRecorders(TSglQue<CRecorder>& aQueue, TBool aExpected, TInt aPanicCode) sl@0: // check that all the elements in the given Q are IsActive() or vice-versa sl@0: { sl@0: TSglQueIter<CRecorder> iter(aQueue); sl@0: sl@0: CRecorder* recorder; sl@0: while ((recorder=iter++)!=NULL) sl@0: { sl@0: TBool expected = aExpected != EFalse; // ensure these are either true or false sl@0: TBool active = recorder->IsActive() != EFalse; sl@0: __ASSERT_DEBUG(expected == active, Panic(aPanicCode)); sl@0: } sl@0: } sl@0: sl@0: void CAudioInput::CheckActiveRecorders() sl@0: // check that all the elements in the recordingQueue are IsActive() etc sl@0: // can't be used as CRecorder::RunL() pre-condition sl@0: { sl@0: CheckActiveRecorders(iRecordingQueue, ETrue, EPanicBusyRecorderNotActive); sl@0: CheckActiveRecorders(iIdleQueue, EFalse, EPanicNonBusyRecorderActive); sl@0: CheckActiveRecorders(iPendingQueue, EFalse, EPanicNonBusyRecorderActive); sl@0: CheckActiveRecorders(iBusyQueue, EFalse, EPanicNonBusyRecorderActive); sl@0: } sl@0: sl@0: #endif // _DEBUG sl@0: sl@0: // sl@0: // CRecorder sl@0: // sl@0: sl@0: sl@0: CAudioInput::CRecorder::CRecorder(CAudioInput& aParent, TInt aIndex) : sl@0: CActive(EPriorityStandard), iParent(aParent), iIndex(aIndex) sl@0: { sl@0: CActiveScheduler::Add(this); sl@0: } sl@0: sl@0: CAudioInput::CRecorder::~CRecorder() sl@0: { sl@0: Cancel(); sl@0: } sl@0: sl@0: void CAudioInput::CRecorder::Cancel() sl@0: { sl@0: // this override takes into account that ReleaseBuffer must be called - this is not the sl@0: // normal pattern where following Cancel() we're not concerned with the results sl@0: if (IsActive()) sl@0: { sl@0: ASSERT(!BufferHeld()); // if active then buffer held should be clear. don't reset then sl@0: CActive::Cancel(); sl@0: ReleaseBuffer(ETrue); // release - might have been a successful run! sl@0: } sl@0: else sl@0: { sl@0: ReleaseBuffer(); // this will release buffer if still outstanding sl@0: } sl@0: } sl@0: sl@0: void CAudioInput::CRecorder::RunL() sl@0: { sl@0: #ifdef SYMBIAN_SWCODEC_LOGGING sl@0: RDebug::Print(_L("--->CAudioInput::CRecorder::RunL(%d, %d)"), Index(), sl@0: iStatus.Int()); sl@0: #endif sl@0: TInt errorOrOffset = iStatus.Int(); // negative -> error. non-negative is offset in chunk sl@0: sl@0: if (errorOrOffset < 0) sl@0: { sl@0: #ifdef SYMBIAN_SWCODEC_LOGGING sl@0: RDebug::Print(_L("errorOrOffset = [%d]"),errorOrOffset); sl@0: #endif sl@0: // ReleaseBuffer(ETrue); // calls ReleaseBuffer() on error code. Driver requires this, even though seems wrong sl@0: iParent.BufferError(this, errorOrOffset); sl@0: } sl@0: else sl@0: { sl@0: ASSERT(!iBufferHeld); sl@0: iBufferHeld = ETrue; sl@0: sl@0: #ifdef SYMBIAN_SWCODEC_LOGGING sl@0: RDebug::Print(_L("errorOrOffset = [%d]"),errorOrOffset); sl@0: #endif sl@0: // If a buffer larger than expected arrives truncate it. sl@0: iLength = Min(iLength,iParent.iBufferLength); sl@0: iParent.BufferArrives(this); sl@0: } sl@0: #ifdef SYMBIAN_SWCODEC_LOGGING sl@0: RDebug::Print(_L("<---CAudioInput::CRecorder::RunL(%d)"), Index()); sl@0: #endif sl@0: } sl@0: sl@0: void CAudioInput::CRecorder::RecordData() sl@0: { sl@0: #ifdef SYMBIAN_SWCODEC_LOGGING sl@0: RDebug::Print(_L("--->CAudioInput::CRecorder::RecordData(%d)"), Index()); sl@0: #endif sl@0: ASSERT(!iBufferHeld); sl@0: Deque(); // ensure we append to the AO queue, so if it comes to it we process oldest request first sl@0: CActiveScheduler::Add(this); sl@0: iLength = iParent.BufferLength(); // TODO do we have to set this first or is it an OUT param purely sl@0: iParent.RecordSoundDevice().RecordData(iStatus, iLength); sl@0: SetActive(); sl@0: sl@0: #ifdef SYMBIAN_SWCODEC_LOGGING sl@0: RDebug::Print(_L("###****#####!!!! Buffer length [%d], status [%d] "), iLength, sl@0: iStatus.Int()); sl@0: #endif sl@0: #ifdef SYMBIAN_SWCODEC_LOGGING sl@0: RDebug::Print(_L("<---CAudioInput::CRecorder::RecordData(%d)"), Index()); sl@0: #endif sl@0: } sl@0: sl@0: void CAudioInput::CRecorder::DoCancel() sl@0: { sl@0: #ifdef SYMBIAN_SWCODEC_LOGGING sl@0: RDebug::Print(_L("--->CAudioInput::CRecorder::DoCancel(%d)"), Index()); sl@0: #endif sl@0: iParent.RecordSoundDevice().Cancel(iStatus); sl@0: #ifdef SYMBIAN_SWCODEC_LOGGING sl@0: RDebug::Print(_L("<---CAudioInput::CRecorder::DoCancel(%d)"), Index()); sl@0: #endif sl@0: } sl@0: sl@0: void CAudioInput::CRecorder::ReleaseBuffer(TBool aDoAnyway) sl@0: { sl@0: if (iBufferHeld || aDoAnyway) sl@0: { sl@0: iParent.RecordSoundDevice().ReleaseBuffer(iStatus.Int()); sl@0: iBufferHeld = EFalse; sl@0: } sl@0: } sl@0: sl@0: TInt CAudioInput::CRecorder::Index() const sl@0: { sl@0: return iIndex; sl@0: } sl@0: sl@0: TInt CAudioInput::CRecorder::Length() const sl@0: { sl@0: return iLength; sl@0: } sl@0: sl@0: TBool CAudioInput::CRecorder::IsBusy() const sl@0: { sl@0: return IsActive() || BufferHeld(); sl@0: } sl@0: sl@0: TBool CAudioInput::CRecorder::BufferHeld() const sl@0: // BufferHeld() means we're in control of a passed buffer sl@0: { sl@0: return iBufferHeld; sl@0: } sl@0: sl@0: TInt CAudioInput::CRecorder::Offset() const sl@0: // If we call this, we've discounted errors so can assert non-negative sl@0: { sl@0: TInt result = StatusOrOffset(); sl@0: ASSERT(result>=0); sl@0: return result; sl@0: } sl@0: sl@0: TInt CAudioInput::CRecorder::StatusOrOffset() const sl@0: // The iStatus assuming is valid sl@0: { sl@0: ASSERT(!IsActive()); // or would not be valid sl@0: TInt result = iStatus.Int(); sl@0: return result; sl@0: }