sl@0: // Copyright (c) 2005-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: // source\server\MmfBtSwCodecRecordDatapath.cpp sl@0: // sl@0: // sl@0: sl@0: #include "mmfBtSwCodecRecordDataPath.h" sl@0: #include "mmfbtswcodecwrapper.h" sl@0: #include sl@0: #include "MMFBtRoutingSoundDevice.h" sl@0: sl@0: sl@0: CMMFSwCodecRecordDataPath* CMMFSwCodecRecordDataPath::NewL(CRoutingSoundRecordDevice* aSoundDevice) sl@0: { sl@0: CMMFSwCodecRecordDataPath* self = new(ELeave) CMMFSwCodecRecordDataPath(aSoundDevice); sl@0: CleanupStack::PushL(self); sl@0: self->ConstructL(); sl@0: CleanupStack::Pop(); sl@0: return self; sl@0: } sl@0: sl@0: sl@0: void CMMFSwCodecRecordDataPath::ConstructL() sl@0: { sl@0: iAudioRecorder = new (ELeave) CDataPathRecorder(*this,CActive::EPriorityUserInput); sl@0: iSoundDeviceErrorReceiver = new (ELeave) CSoundDevRecordErrorReceiver(*this, CActive::EPriorityUserInput); sl@0: } sl@0: sl@0: sl@0: CMMFSwCodecRecordDataPath::~CMMFSwCodecRecordDataPath() sl@0: { sl@0: delete iAudioRecorder; sl@0: delete iSoundDeviceErrorReceiver; sl@0: sl@0: TRequestStatus status; sl@0: iSoundDevice->CloseDevice(iDeviceUid, status); sl@0: //TODO there should be a timeout for the line below sl@0: User::WaitForRequest(status); sl@0: sl@0: if (iCodec) sl@0: { sl@0: delete iSoundDeviceBuffer; sl@0: if (!iCodec->IsNullCodec()) sl@0: { sl@0: delete iSinkBuffer; sl@0: } sl@0: } sl@0: sl@0: sl@0: #ifdef __USE_MMF_TRANSFERBUFFERS__ sl@0: delete iTransferWindow; sl@0: sl@0: if(iTransferBuffer) sl@0: { sl@0: iTransferBuffer->Close(); sl@0: delete iTransferBuffer; sl@0: } sl@0: #endif sl@0: sl@0: #ifdef __USE_MMF_PTRBUFFERS__ sl@0: delete iPtrBufferMemoryBlock; sl@0: #endif sl@0: sl@0: } sl@0: sl@0: sl@0: TInt CMMFSwCodecRecordDataPath::SetObserver(MMMFHwDeviceObserver& aObserver) sl@0: { sl@0: TInt error; sl@0: if (iHwDeviceObserver) sl@0: { sl@0: error = KErrAlreadyExists; sl@0: } sl@0: else sl@0: { sl@0: iHwDeviceObserver = &aObserver; sl@0: error = KErrNone; sl@0: } sl@0: return error; sl@0: } sl@0: sl@0: sl@0: TInt CMMFSwCodecRecordDataPath::AddCodec(CMMFSwCodec& aCodec) sl@0: { sl@0: if (iCodec) sl@0: { sl@0: return KErrNotSupported; //doesn't support multiple codecs sl@0: } sl@0: sl@0: TInt err = KErrNone; sl@0: iCodec = &aCodec; sl@0: sl@0: iSinkBufferSize = iCodec->SinkBufferSize(); sl@0: iSoundDevBufferSize = iCodec->SourceBufferSize(); sl@0: sl@0: if ((!iSinkBufferSize)||(!iSoundDevBufferSize)) sl@0: { sl@0: err = KErrArgument; //codec plugin has not specified buffer size sl@0: } sl@0: sl@0: if (err == KErrNone) sl@0: { sl@0: #ifdef __USE_MMF_TRANSFERBUFFERS__ sl@0: TRAP(err,iSoundDeviceBuffer = CreateTransferBufferL(iSoundDevBufferSize, static_cast(iSoundDeviceBuffer))); sl@0: #endif sl@0: sl@0: #ifdef __USE_MMF_PTRBUFFERS__ sl@0: TRAP(err,iSoundDeviceBuffer = CreatePtrBufferL(iSoundDevBufferSize)); sl@0: #else sl@0: TRAP(err,iSoundDeviceBuffer = CMMFDataBuffer::NewL(iSoundDevBufferSize)); sl@0: #endif sl@0: } sl@0: sl@0: if (err == KErrNone) sl@0: { sl@0: // Allocate data buffer sl@0: if (iCodec->IsNullCodec()) sl@0: {//don't need a separate sink buffer if null codec sl@0: iSinkBuffer = NULL; //sink buffer is the sound device buffer sl@0: } sl@0: else sl@0: {//need a separate sink buffer for the codec sl@0: TRAP(err,iSinkBuffer = CMMFDataBuffer::NewL(iSinkBufferSize)); sl@0: } sl@0: } sl@0: return err; sl@0: } sl@0: sl@0: sl@0: TInt CMMFSwCodecRecordDataPath::Start() sl@0: { sl@0: TInt startError = KErrNone; sl@0: if (!iCodec || !(iSoundDevice->Handle())) sl@0: {//check that a codec has been added and the sound device has been opened sl@0: startError = KErrNotReady; sl@0: } sl@0: sl@0: if (!startError) sl@0: { sl@0: // Start the player objects sl@0: iAudioRecorder->Start(); sl@0: iSoundDeviceErrorReceiver->Start(); sl@0: iSoundDeviceBuffer->SetLastBuffer(EFalse); sl@0: TRAP(startError, FillSoundDeviceBufferL()); //get audio recorder to fill buffer sl@0: if (startError == KErrNone) sl@0: { sl@0: iRecordedBytesCount = 0; //used for debug purposes sl@0: iState = EPlaying; sl@0: } sl@0: } sl@0: return startError; sl@0: } sl@0: sl@0: sl@0: // *** Main Play Loop *** sl@0: void CMMFSwCodecRecordDataPath::FillSoundDeviceBufferL() sl@0: { sl@0: #ifdef __CYCLE_MMF_DATABUFFERS__ sl@0: // Create a new buffer to replicate INC021405 Play-EOF-Play on HwAccelerated solution Panics sl@0: // If the creation fails, we carry on regardless as the original buffer will not have been sl@0: // destroyed. Must do this as alloc fail tests will not run. sl@0: if(iSoundDeviceBuffer) sl@0: { sl@0: iSoundDeviceBuffer = CycleAudioBuffer(iSoundDeviceBuffer); sl@0: } sl@0: #endif // __CYCLE_MMF_DATABUFFERS__ sl@0: iAudioRecorder->RecordData(*iSoundDeviceBuffer); sl@0: } sl@0: sl@0: sl@0: void CMMFSwCodecRecordDataPath::BufferFilledL(CMMFDataBuffer& aBuffer) sl@0: { sl@0: iSoundDeviceBuffer = &aBuffer; sl@0: iSoundDeviceBuffer->SetStatus(EFull); sl@0: // Store this to avoid casting several times sl@0: TUint length = iSoundDeviceBuffer->BufferSize(); sl@0: // Update bytes recorded sl@0: iRecordedBytesCount += length; sl@0: sl@0: //If paused then sound driver will keep sending buffers till sl@0: //its flushed - if last buffer then set last buffer flag sl@0: if (length < iSoundDevBufferSize) sl@0: {//assume it's the last buffer sl@0: iSoundDeviceBuffer->SetLastBuffer(ETrue); sl@0: } sl@0: sl@0: //buffer ok can send to sink sl@0: FillSinkBufferL(); //convert to sink data type using codec sl@0: User::LeaveIfError(iHwDeviceObserver->EmptyThisHwBuffer(*iSinkBuffer)); //pass onto sink sl@0: } sl@0: sl@0: sl@0: /* sl@0: * FillSinkBufferL sl@0: * sl@0: * Function to take the data from an already full source buffer and by using sl@0: * a codec if necessary fills the sink buffer sl@0: * @internalComponent sl@0: */ sl@0: void CMMFSwCodecRecordDataPath::FillSinkBufferL() sl@0: { sl@0: CMMFSwCodec::TCodecProcessResult codecProcessResult; sl@0: sl@0: if (iCodec->IsNullCodec()) sl@0: {//no codec so sound device buffer can be used directly as sink buffer sl@0: iSinkBuffer = iSoundDeviceBuffer; sl@0: iSinkBuffer->SetStatus(EFull); //sink buffer is full sl@0: } sl@0: else sl@0: { sl@0: //pass buffer to codec for processing sl@0: codecProcessResult = iCodec->ProcessL(*iSoundDeviceBuffer, *iSinkBuffer); sl@0: if (iSoundDeviceBuffer->LastBuffer()) //if sound device is last buffer so is sound dev sl@0: { sl@0: iSinkBuffer->SetLastBuffer(ETrue); sl@0: } sl@0: if ((!iSinkBuffer->BufferSize())&&(codecProcessResult.iDstBytesAdded)) sl@0: {//the codec has added data but not set the buffer length sl@0: iSinkBuffer->Data().SetLength(codecProcessResult.iDstBytesAdded); sl@0: } sl@0: //only supports EProcessComplete sl@0: switch (codecProcessResult.iCodecProcessStatus) sl@0: { sl@0: case CMMFSwCodec::TCodecProcessResult::EProcessComplete: sl@0: //finished procesing source data - all data in sink buffer sl@0: { sl@0: iSoundDeviceBuffer->SetStatus(EAvailable); //source buffer is now avaialble sl@0: iSinkBuffer->SetStatus(EFull); //sink buffer is full sl@0: } sl@0: break; sl@0: case CMMFSwCodec::TCodecProcessResult::EDstNotFilled: sl@0: //finished procesing source data - sink buffer not full could be EOF sl@0: { sl@0: iSoundDeviceBuffer->SetStatus(EAvailable); //source buffer is now avaialble sl@0: iSinkBuffer->SetStatus(EFull); //sink buffer may not really be full however sl@0: } sl@0: break; sl@0: case CMMFSwCodec::TCodecProcessResult::EEndOfData: sl@0: //no more data - send what we've got to the sink sl@0: //note we can't always rely on this - in many cases the codec will not know when sl@0: //it has reached the end of data. sl@0: { sl@0: iSoundDeviceBuffer->SetStatus(EAvailable); //source buffer is now avaialble sl@0: iSinkBuffer->SetStatus(EFull);//sink buffer may not really be 'full' but its as full as it going to get sl@0: //doesn't matter if sink buffer is not full sl@0: } sl@0: break; sl@0: default: sl@0: Panic(EMMFSwCodecWrapperBadCodec); //should never get here - bad codec sl@0: } sl@0: } sl@0: } sl@0: sl@0: sl@0: void CMMFSwCodecRecordDataPath::BufferEmptiedL(const CMMFDataBuffer& aBuffer) sl@0: { sl@0: if (&aBuffer != iSinkBuffer) sl@0: { sl@0: Panic(EMMFSwCodecWrapperBadBuffer); sl@0: } sl@0: if (!aBuffer.LastBuffer()) sl@0: { sl@0: FillSoundDeviceBufferL(); sl@0: } sl@0: //else the last buffer has been emptied - the observer should stop the sl@0: //hw device. sl@0: } sl@0: sl@0: //*** End of Main Play Loop *** sl@0: sl@0: sl@0: void CMMFSwCodecRecordDataPath::Stop() sl@0: { sl@0: iAudioRecorder->Stop();//stop audio reocrder sl@0: sl@0: iSoundDeviceErrorReceiver->Cancel(); //stop receiving events sl@0: sl@0: TRequestStatus status; sl@0: iSoundDevice->CloseDevice(iDeviceUid, status); sl@0: User::WaitForRequest(status); sl@0: sl@0: #ifdef __CYCLE_MMF_DATABUFFERS__ sl@0: // Create a new buffer to replicate INC021405 Play-EOF-Play on HwAccelerated solution Panics sl@0: // If the creation fails, we carry on regardless as the original buffer will not have been sl@0: // destroyed. Must do this as alloc fail tests will not run. sl@0: if(iSoundDeviceBuffer) sl@0: { sl@0: iSoundDeviceBuffer = CycleAudioBuffer(iSoundDeviceBuffer); sl@0: } sl@0: #endif // __CYCLE_MMF_DATABUFFERS__ sl@0: sl@0: iState = EStopped; sl@0: } sl@0: sl@0: sl@0: void CMMFSwCodecRecordDataPath::Pause() sl@0: { sl@0: // flush it anyway, whether we're active or not sl@0: // if we are active, then this should result in a call to RunL() pretty soon sl@0: //note that the Pause() in the context of record means buffers are sl@0: //continued to be obtained from the sound driver that have already sl@0: //been recorded - it just doesn't record any new audio data sl@0: #ifdef _SCW_DEBUG sl@0: RDebug::Print(_L("CMMFSwcodecRecordDataPath::Pause")); sl@0: #endif sl@0: iSoundDevice->FlushBuffer(); sl@0: } sl@0: sl@0: CRoutingSoundRecordDevice* CMMFSwCodecRecordDataPath::Device() sl@0: { sl@0: return iSoundDevice; sl@0: } sl@0: sl@0: sl@0: void CMMFSwCodecRecordDataPath::SoundDeviceException(TInt aError) sl@0: { sl@0: //this closes RMdaDevSound. sl@0: Stop(); sl@0: sl@0: //inform devsound so it can update policy sl@0: iHwDeviceObserver->Stopped(); sl@0: sl@0: // Inform the observer of the exception condition sl@0: // We inform the hw device observer after the policy has been sl@0: // updated incase the observer relied on the error to assume sl@0: // the policy has been updated sl@0: iHwDeviceObserver->Error(aError); sl@0: } sl@0: sl@0: TUint CMMFSwCodecRecordDataPath::RecordedBytesCount() sl@0: { sl@0: return iRecordedBytesCount; sl@0: } sl@0: sl@0: sl@0: /************************************************************************ sl@0: * CDataPathRecorder * sl@0: ************************************************************************/ sl@0: sl@0: CDataPathRecorder::CDataPathRecorder(CMMFSwCodecRecordDataPath& aParent, TInt aPriority) sl@0: : CActive(aPriority), iParent(aParent) sl@0: { sl@0: CActiveScheduler::Add(this); sl@0: } sl@0: sl@0: sl@0: CDataPathRecorder::~CDataPathRecorder() sl@0: { sl@0: Cancel(); sl@0: } sl@0: sl@0: sl@0: void CDataPathRecorder::Start() sl@0: { sl@0: // No implementation sl@0: } sl@0: sl@0: sl@0: void CDataPathRecorder::RecordData(CMMFDataBuffer& aData) sl@0: { sl@0: iDataFromSource = &aData; sl@0: if (!IsActive()) sl@0: { sl@0: iParent.Device()->RecordData(aData.Data(), iStatus); sl@0: SetActive(); sl@0: } sl@0: } sl@0: sl@0: sl@0: void CDataPathRecorder::Stop() sl@0: { sl@0: #ifdef _SCW_DEBUG sl@0: RDebug::Print(_L("CDataPathRecorder Stop")); sl@0: #endif sl@0: Cancel(); sl@0: } sl@0: sl@0: sl@0: void CDataPathRecorder::RunL() sl@0: { sl@0: #ifdef _SCW_DEBUG sl@0: RDebug::Print(_L("CDataPathRecorder::RunL error[%d]"), iStatus.Int()); sl@0: #endif sl@0: if (!iStatus.Int()) sl@0: { sl@0: iParent.BufferFilledL((CMMFDataBuffer&)*iDataFromSource); sl@0: } sl@0: //if we don't have a sound driver handle then we have stopped sl@0: //but the client still thinks we are recording so swallow error sl@0: else if (iStatus.Int() != KErrBadHandle) sl@0: { sl@0: iParent.SoundDeviceException(iStatus.Int()); sl@0: } sl@0: } sl@0: sl@0: sl@0: TInt CDataPathRecorder::RunError(TInt aError) sl@0: { sl@0: Error(aError); sl@0: return KErrNone; sl@0: } sl@0: sl@0: sl@0: void CDataPathRecorder::DoCancel() sl@0: { sl@0: #ifdef _SCW_DEBUG sl@0: RDebug::Print(_L("CDataPathRecorder Cancel")); sl@0: #endif sl@0: if (iParent.Device()->Handle()) sl@0: { sl@0: iParent.Device()->CancelRecordData(); sl@0: iParent.Device()->FlushBuffer(); sl@0: } sl@0: } sl@0: sl@0: sl@0: void CDataPathRecorder::Error(TInt aError) sl@0: { sl@0: iParent.SoundDeviceException(aError); sl@0: } sl@0: sl@0: sl@0: /************************************************************************ sl@0: * CSoundDevRecordErrorReceiver * sl@0: ************************************************************************/ sl@0: sl@0: CSoundDevRecordErrorReceiver::CSoundDevRecordErrorReceiver(CMMFSwCodecRecordDataPath& aParent, TInt aPriority) sl@0: : CActive(aPriority), iParent(aParent) sl@0: { sl@0: CActiveScheduler::Add(this); sl@0: } sl@0: sl@0: sl@0: CSoundDevRecordErrorReceiver::~CSoundDevRecordErrorReceiver() sl@0: { sl@0: Cancel(); sl@0: } sl@0: sl@0: sl@0: void CSoundDevRecordErrorReceiver::Start() sl@0: { sl@0: if (!IsActive()) sl@0: { sl@0: iParent.Device()->NotifyError(iStatus); sl@0: SetActive(); sl@0: } sl@0: } sl@0: sl@0: sl@0: void CSoundDevRecordErrorReceiver::Stop() sl@0: { sl@0: Cancel(); sl@0: } sl@0: sl@0: sl@0: void CSoundDevRecordErrorReceiver::RunL() sl@0: { sl@0: // An error has been returned--Flush to release mic. sl@0: iParent.Device()->FlushBuffer(); sl@0: iParent.SoundDeviceException(iStatus.Int()); sl@0: } sl@0: sl@0: sl@0: void CSoundDevRecordErrorReceiver::DoCancel() sl@0: { sl@0: iParent.Device()->CancelNotifyError(); sl@0: } sl@0: sl@0: