// 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_LOGGINGconst 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 _DEBUGstatic 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 // _DEBUGconst 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 tableconst TInt KNumSampleRates = 9;struct TSampleRateEnumTable { TInt iRate; TSoundRate iRateEnum; TUint iRateConstant; };//Table that maps given samples per second to the corresponding enums in RSoundScconst 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} };// TAudioInputParamsEXPORT_C TAudioInputParams::TAudioInputParams() : iSampleRate(0), iNominalBufferSize(0) { // none }// CAudioInputEXPORT_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 onlyvoid 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; }