omxilcomp/omxilaudioemulator/pcmrenderer/src/mdasoundadapterbody.cpp
changeset 0 58be5850fb6c
equal deleted inserted replaced
-1:000000000000 0:58be5850fb6c
       
     1 // Copyright (c) 2008-2009 Nokia Corporation and/or its subsidiary(-ies).
       
     2 // All rights reserved.
       
     3 // This component and the accompanying materials are made available
       
     4 // under the terms of "Eclipse Public License v1.0"
       
     5 // which accompanies this distribution, and is available
       
     6 // at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     7 //
       
     8 // Initial Contributors:
       
     9 // Nokia Corporation - initial contribution.
       
    10 //
       
    11 // Contributors:
       
    12 //
       
    13 // Description:
       
    14 //
       
    15 #include "mdasoundadapterbody.h"
       
    16 #include <e32debug.h>
       
    17 
       
    18 #include "rateconvert.h" // if we need to resample
       
    19 
       
    20 #include <hal.h>
       
    21 
       
    22 _LIT(KPddFileName,"SOUNDSC.PDD");
       
    23 _LIT(KLddFileName,"ESOUNDSC.LDD");
       
    24 
       
    25 
       
    26 const TInt KBytesPerSample = 2;
       
    27 const TInt KMinBufferSize = 2;
       
    28 
       
    29 /**
       
    30 This function raises a panic
       
    31 EDeviceNotOpened is raised when any of the RMdaDevSound APIs are called before opening the device. 
       
    32 */
       
    33 GLDEF_C void Panic(TSoundAdapterPanicCodes aPanicCode)
       
    34 	{
       
    35 	User::Panic(KSoundAdapterPanicCategory, aPanicCode);
       
    36 	}
       
    37 
       
    38 
       
    39 const TText8 *RMdaDevSound::CBody::TState::Name() const
       
    40 	{
       
    41 	#ifdef SYMBIAN_SOUNDADAPTER_DEBUG	 
       
    42 	switch(iState)
       
    43 		{
       
    44 		case ENotReady:				return _S8("ENotReady");
       
    45 		case EStopped:				return _S8("EStopped");
       
    46 		case ERecording:			return _S8("ERecording");
       
    47 		case ERecordingPausedInHw:	return _S8("ERecordingPausedInHw");
       
    48 		case ERecordingPausedInSw:	return _S8("ERecordingPausedInSw");
       
    49 		case EPlaying:				return _S8("EPlaying");
       
    50 		case EPlayingPausedInHw: 	return _S8("EPlayingPausedInHw");
       
    51 		case EPlayingPausedInSw:	return _S8("EPlayingPausedInSw");
       
    52 		case EPlayingUnderrun:		return _S8("EPlayingUnderrun");
       
    53 		}
       
    54 	return _S8("CorruptState");
       
    55 	#else
       
    56 	return _S8("");
       
    57 	#endif
       
    58 	}
       
    59 
       
    60 	
       
    61 
       
    62 RMdaDevSound::CBody::TState &RMdaDevSound::CBody::TState::operator=(TStateEnum aNewState)
       
    63 	{
       
    64     if(iState != aNewState)
       
    65         {
       
    66         #ifdef SYMBIAN_SOUNDADAPTER_DEBUG    
       
    67         RDebug::Printf("RMdaDevSound state %s -> %s", Name(), TState(aNewState).Name());
       
    68         #endif
       
    69         iState = aNewState;
       
    70         }
       
    71 	return *this;
       
    72 	}
       
    73 
       
    74 RMdaDevSound::CBody* RMdaDevSound::CBody::NewL()
       
    75 	{
       
    76 	CBody* self = new(ELeave) CBody();
       
    77 	CleanupStack::PushL(self);
       
    78 	self->ConstructL();
       
    79 	CleanupStack::Pop();
       
    80 	return self;
       
    81 	}
       
    82 
       
    83 RMdaDevSound::CBody::~CBody()
       
    84 	{
       
    85 	for(TInt i = 0; i < KPlaySharedChunkBuffers; i++)
       
    86 		{
       
    87 		delete iPlayers[i];
       
    88 		iPlayers[i] = NULL;
       
    89 		}
       
    90 	delete iRecorder;
       
    91 	iRecorder = NULL;
       
    92 	delete iPlayFormatData.iConverter;
       
    93 	delete iRecordFormatData.iConverter;
       
    94 	iPlayChunk.Close();
       
    95 	iPlaySoundDevice.Close();
       
    96 	iRecordChunk.Close();
       
    97 	iRecordSoundDevice.Close();
       
    98 	iConvertedPlayData.Close();
       
    99 	iSavedTrailingData.Close();
       
   100 	iBufferedRecordData.Close();
       
   101 	}
       
   102 	
       
   103 RMdaDevSound::CBody::CBody()
       
   104 	:iState(ENotReady), iBufferOffset(-1)
       
   105 	{
       
   106 	
       
   107 	}
       
   108 
       
   109 TVersion RMdaDevSound::CBody::VersionRequired() const
       
   110 	{
       
   111 	if(iPlaySoundDevice.Handle())
       
   112 		{
       
   113 		return iPlaySoundDevice.VersionRequired();
       
   114 		}
       
   115 	else
       
   116 		{
       
   117 		return TVersion();
       
   118 		}
       
   119 	}
       
   120 
       
   121 TInt RMdaDevSound::CBody::IsMdaSound()
       
   122 	{
       
   123 	return ETrue;
       
   124 	}
       
   125 	
       
   126 void RMdaDevSound::CBody::ConstructL()
       
   127 	{
       
   128 	// Try to load the audio physical driver
       
   129     TInt err = User::LoadPhysicalDevice(KPddFileName);
       
   130 	if ((err!=KErrNone) && (err!=KErrAlreadyExists))
       
   131 		{
       
   132 		User::Leave(err);
       
   133 		}
       
   134     // Try to load the audio logical driver
       
   135 	err = User::LoadLogicalDevice(KLddFileName);
       
   136     if ((err!=KErrNone) && (err!=KErrAlreadyExists))
       
   137     	{
       
   138     	User::Leave(err);
       
   139     	}
       
   140 	for(TInt i=0; i<KPlaySharedChunkBuffers; i++)
       
   141 		{
       
   142 		iPlayers[i] = new(ELeave) CPlayer(CActive::EPriorityUserInput, *this, i);
       
   143 		iFreePlayers.Push(iPlayers[i]);
       
   144 		}
       
   145 	
       
   146 	iRecorder = new(ELeave) CRecorder(CActive::EPriorityUserInput, *this);
       
   147 	
       
   148 	TInt tmp;
       
   149 	User::LeaveIfError(HAL::Get(HAL::ENanoTickPeriod, tmp));
       
   150 	iNTickPeriodInUsec = tmp;
       
   151 	}
       
   152 
       
   153 TInt RMdaDevSound::CBody::Open(TInt /*aUnit*/)
       
   154 	{
       
   155     #ifdef SYMBIAN_SOUNDADAPTER_DEBUG	
       
   156         RDebug::Print(_L("RMdaDevSound::CBody::Open "));
       
   157     #endif	
       
   158 	TInt err = KErrNone;
       
   159 	//Default behavior of this method is to open both the play and record audio devices.
       
   160 	if(!iPlaySoundDevice.Handle() && !iRecordSoundDevice.Handle())
       
   161         {
       
   162 		err = iPlaySoundDevice.Open(KSoundScTxUnit0);
       
   163     	if(err == KErrNone)
       
   164     		{
       
   165     		err = iRecordSoundDevice.Open(KSoundScRxUnit0);
       
   166     		}
       
   167 		}
       
   168 	if(err != KErrNone)
       
   169 		{
       
   170 		Close();
       
   171 		}
       
   172 	else
       
   173 	    {
       
   174 		TSoundFormatsSupportedV02Buf capsBuf;
       
   175 		iPlaySoundDevice.Caps(capsBuf);
       
   176 		TInt minBufferSize = KMinBufferSize;
       
   177 		#ifdef SYMBIAN_FORCE_32BIT_LENGTHS
       
   178 		minBufferSize = Max(minBufferSize, 4); // force to 32-bit buffer align
       
   179 		#endif
       
   180 		iRequestMinSize = Max(capsBuf().iRequestMinSize, minBufferSize); 
       
   181 		// work out mask so that x&iRequestMinMask is equiv to x/iRequestMinSize*iRequestMinSize
       
   182 		iRequestMinMask = ~(iRequestMinSize-1); // assume iRequestMinSize is power of 2
       
   183 		iSavedTrailingData.Close();
       
   184 		iSavedTrailingData.Create(iRequestMinSize);
       
   185 	
       
   186 	    iState = EStopped;
       
   187 		iBytesPlayed = 0;
       
   188 	    }
       
   189 
       
   190 	return err;
       
   191 	}
       
   192 		
       
   193 TInt RMdaDevSound::CBody::PlayVolume()
       
   194 	{
       
   195 	__ASSERT_DEBUG(iPlaySoundDevice.Handle(), Panic(EDeviceNotOpened));
       
   196 	return iPlaySoundDevice.Volume();	
       
   197 	}
       
   198 	
       
   199 void RMdaDevSound::CBody::SetPlayVolume(TInt aVolume)
       
   200 	{
       
   201 	__ASSERT_DEBUG(iPlaySoundDevice.Handle(), Panic(EDeviceNotOpened));
       
   202 	if(aVolume >=0 && aVolume<=KSoundMaxVolume)
       
   203 		{
       
   204 		iPlaySoundDevice.SetVolume(KLinerToDbConstantLookup[aVolume].iDBValue);
       
   205 		}
       
   206 	}
       
   207 void RMdaDevSound::CBody::SetVolume(TInt aLogarithmicVolume)
       
   208 	{
       
   209 	__ASSERT_DEBUG(iPlaySoundDevice.Handle(), Panic(EDeviceNotOpened));
       
   210 	if(aLogarithmicVolume >= 0 && aLogarithmicVolume <= KSoundMaxVolume)
       
   211 		{
       
   212 		iPlaySoundDevice.SetVolume(aLogarithmicVolume);
       
   213 		}
       
   214 	}
       
   215 	
       
   216 void RMdaDevSound::CBody::CancelPlayData()
       
   217 	{
       
   218     #ifdef SYMBIAN_SOUNDADAPTER_DEBUG	
       
   219     RDebug::Printf("RMdaDevSound::CBody::CancelPlayData: state %s", iState.Name());
       
   220     #endif	
       
   221 	__ASSERT_DEBUG(iPlaySoundDevice.Handle(), Panic(EDeviceNotOpened));
       
   222 
       
   223     // If there is a client request, cancel it
       
   224     // Must do this before canceling players because otherwise they may just restart!
       
   225     if(iClientPlayStatus)
       
   226         {
       
   227         #ifdef SYMBIAN_SOUNDADAPTER_DEBUG    
       
   228         RDebug::Printf("msp PlayCancelled complete iClientPlayStatus");
       
   229 		#endif
       
   230         User::RequestComplete(iClientPlayStatus, KErrCancel); // Call also sets iClientPlayStatus to NULL
       
   231         }
       
   232     
       
   233     // Discard any buffered data
       
   234     iClientPlayData.Set(0,0);
       
   235 	// Discard any saved trailing data (ie. data saved due driver requiring all requests to be a multiple of iRequestMinSize).
       
   236 	iSavedTrailingData.SetLength(0);
       
   237 
       
   238     // Emulator RSoundSc PDD when running without a soundcard has a major
       
   239     // issue with cancelling whilst paused. It will not clear the pending
       
   240     // list (because the timer is not active) and therefore this list will
       
   241     // later overflow causing hep corruption.
       
   242     // This means that, for now, we MUST Resume before calling CancelPlayData
       
   243     // to avoid kernel panics...
       
   244     
       
   245     // The device driver will not cancel a request which is in progress...
       
   246     // So, if we are paused in hw, we must resume before cancelling the
       
   247     // player otherwise it will hang in CActive::Cancel
       
   248     if(iState == EPlayingPausedInHw)
       
   249         {
       
   250         #ifdef SYMBIAN_SOUNDADAPTER_DEBUG    
       
   251         RDebug::Printf("msp Resume to avoid hang");
       
   252         #endif
       
   253         (void) iPlaySoundDevice.Resume();
       
   254         }
       
   255     
       
   256     // Update state
       
   257 	iState = EStopped;
       
   258 	
       
   259 
       
   260     // The RSoundSc driver will not cancel a request which is in progress (or paused).
       
   261     // If we just loop across the players, cancelling each individual request and waiting for it to complete,
       
   262     // several of them will actually play, which is both wrong and time consuming....
       
   263     // Issue a block cancel upfront to avoid this
       
   264     iPlaySoundDevice.CancelPlayData();
       
   265  
       
   266 	// Cancel all players
       
   267 	for (TUint playerIndex=0; playerIndex<KPlaySharedChunkBuffers; ++playerIndex)
       
   268 	    {
       
   269 	    // If the player is active it will call PlayRequestCompleted with aDueToCancelCommand true
       
   270 	    // to update the iFreePlayers and iActivePlayRequestSizes FIFOs.
       
   271         iPlayers[playerIndex]->Cancel();
       
   272 	    }
       
   273 	
       
   274 	iBufferOffset = -1;
       
   275 	iBufferLength = 0;
       
   276 	
       
   277 	return;
       
   278 	}
       
   279 	
       
   280 TInt RMdaDevSound::CBody::RecordLevel()
       
   281 	{
       
   282 	__ASSERT_DEBUG(iRecordSoundDevice.Handle(), Panic(EDeviceNotOpened));
       
   283 	return iRecordSoundDevice.Volume();
       
   284 	}
       
   285 	
       
   286 void RMdaDevSound::CBody::SetRecordLevel(TInt aLevel)
       
   287 	{
       
   288 	__ASSERT_DEBUG(iRecordSoundDevice.Handle(), Panic(EDeviceNotOpened));
       
   289 	iRecordSoundDevice.SetVolume(aLevel);	
       
   290 	}
       
   291 	
       
   292 void RMdaDevSound::CBody::CancelRecordData()
       
   293 	{
       
   294 	__ASSERT_DEBUG(iRecordSoundDevice.Handle(), Panic(EDeviceNotOpened));
       
   295     #ifdef SYMBIAN_SOUNDADAPTER_DEBUG	
       
   296     RDebug::Printf("RMdaDevSound::CBody::CancelRecordData: state %s", iState.Name());
       
   297     #endif
       
   298 
       
   299     // Stop recorder object (and its request)
       
   300     iRecorder->Cancel();
       
   301     
       
   302     // Stop driver from recording
       
   303     iRecordSoundDevice.CancelRecordData();
       
   304              
       
   305     // If there is a client request, cancel it
       
   306     if(iClientRecordStatus)
       
   307    		{
       
   308         User::RequestComplete(iClientRecordStatus, KErrNone); // Call also sets iClientPlayStatus to NULL
       
   309         }
       
   310 
       
   311     iState = EStopped;
       
   312     return;
       
   313 	}
       
   314 	
       
   315 void RMdaDevSound::CBody::FlushRecordBuffer()
       
   316 	{
       
   317 	__ASSERT_DEBUG(iRecordSoundDevice.Handle(), Panic(EDeviceNotOpened));
       
   318     #ifdef SYMBIAN_SOUNDADAPTER_DEBUG	
       
   319         RDebug::Print(_L("RMdaDevSound::CBody::FlushRecordBuffer - implemented by calling PauseRecordBuffer"));
       
   320     #endif
       
   321 
       
   322 	PauseRecordBuffer();
       
   323 	}
       
   324 	
       
   325 TInt RMdaDevSound::CBody::BytesPlayed()
       
   326 	{
       
   327     #ifdef SYMBIAN_SOUNDADAPTER_DEBUG    
       
   328     RDebug::Printf("RMdaDevSound::BytesPlayed %s", iState.Name());
       
   329 	#endif
       
   330 
       
   331 	return I64LOW(BytesPlayed64());
       
   332 	}
       
   333 
       
   334 
       
   335 TUint64 RMdaDevSound::CBody::BytesPlayed64()
       
   336 	{
       
   337 	__ASSERT_DEBUG(iPlaySoundDevice.Handle(), Panic(EDeviceNotOpened));
       
   338 
       
   339 	TUint64 currentBytesPlayed = KMaxTUint64;
       
   340 
       
   341 	switch(iState)
       
   342 	{
       
   343 	case ENotReady:
       
   344 		Panic(EDeviceNotOpened);
       
   345 		break;
       
   346 
       
   347 	case EStopped:
       
   348 		currentBytesPlayed = iBytesPlayed;
       
   349 		break;
       
   350 
       
   351 	case ERecording:
       
   352 	case ERecordingPausedInHw:
       
   353 	case ERecordingPausedInSw:
       
   354 		Panic(EBadState);
       
   355 		break;
       
   356 
       
   357 	case EPlayingPausedInHw: // ie. Play request pending on h/w and paused
       
   358 		// Paused, so use pause time
       
   359         #ifdef SYMBIAN_SOUNDADAPTER_DEBUG    
       
   360 		RDebug::Printf("EPlayingPausedInHw: iPausedBytes %x %x", I64HIGH(iPausedBytesPlayed), I64LOW(iPausedBytesPlayed));
       
   361 		#endif
       
   362 		currentBytesPlayed = iPausedBytesPlayed;
       
   363 		break;
       
   364 
       
   365 	case EPlayingPausedInSw: // ie. Driver not playing or paused
       
   366 		#ifdef SYMBIAN_SOUNDADAPTER_DEBUG	 
       
   367 		RDebug::Printf("EPlayingPausedInSw: iPausedBytesPlayed %x %x", I64HIGH(iPausedBytesPlayed), I64LOW(iPausedBytesPlayed));
       
   368 		#endif
       
   369 		currentBytesPlayed = iPausedBytesPlayed;
       
   370 		break;
       
   371 
       
   372 	case EPlayingUnderrun:
       
   373 		#ifdef SYMBIAN_SOUNDADAPTER_DEBUG	 
       
   374 		RDebug::Printf("EPlayingUnderrun: iBytesPlayed %x %x", I64HIGH(iBytesPlayed), I64LOW(iBytesPlayed));
       
   375 		#endif
       
   376 		currentBytesPlayed = iBytesPlayed;
       
   377 		break;
       
   378 
       
   379 	case EPlaying:
       
   380 		{
       
   381 		// Playing so calculate time since last update to iBytesPlayed
       
   382 		TUint32 curTime = CurrentTimeInMsec();
       
   383 		TUint32 curRequestSize = iActivePlayRequestSizes.Peek();
       
   384 
       
   385 		TUint32 extraPlayTime = (curTime >= iStartTime) ? (curTime-iStartTime) : (KMaxTUint32 - (iStartTime-curTime));
       
   386         #ifdef SYMBIAN_SOUNDADAPTER_DEBUG	 
       
   387 		RDebug::Printf("iStartTime %d curTime %d extraPlayTime %d", iStartTime, curTime, extraPlayTime);
       
   388 		
       
   389 		RDebug::Printf("iPlayFormatData.iSampleRate %d KBytesPerSample %d iNTickPeriodInUsec %d",
       
   390 					   iPlayFormatData.iSampleRate, KBytesPerSample, iNTickPeriodInUsec);
       
   391         #endif
       
   392 		TUint32 extraBytesPlayed = TUint32((TUint64(extraPlayTime) * iPlayFormatData.iSampleRate * iPlayFormatData.iRequestedChannels * KBytesPerSample)/1000);
       
   393 		if(extraBytesPlayed > curRequestSize)
       
   394 			{
       
   395             #ifdef SYMBIAN_SOUNDADAPTER_DEBUG	 
       
   396 			RDebug::Printf("caping extraBytes played from %d to %d", extraBytesPlayed, curRequestSize);
       
   397             #endif
       
   398 			extraBytesPlayed = curRequestSize;
       
   399 			}
       
   400 
       
   401 		#ifdef SYMBIAN_SOUNDADAPTER_DEBUG
       
   402 		RDebug::Printf("iBytesPlayed %d extraBytesPlayed %d (curRequestSize %d) -> currentBytesPlayed %x %x",
       
   403                 iBytesPlayed, extraBytesPlayed, curRequestSize, I64HIGH(currentBytesPlayed), I64LOW(currentBytesPlayed));
       
   404         #endif
       
   405 
       
   406 		currentBytesPlayed = iBytesPlayed + extraBytesPlayed;
       
   407 		break;
       
   408 		}
       
   409 	
       
   410 	default:
       
   411 		Panic(EBadState);
       
   412 		break;
       
   413 	}
       
   414  
       
   415 
       
   416     #ifdef SYMBIAN_SOUNDADAPTER_DEBUG
       
   417 	RDebug::Printf("iPlayFormatData.iConverter %x", iPlayFormatData.iConverter);
       
   418     #endif
       
   419 
       
   420 	if (iPlayFormatData.iConverter)
       
   421 		{
       
   422 		// need to scale bytes played to fit with requested rate and channels, not actual
       
   423 		if (iPlayFormatData.iActualChannels != iPlayFormatData.iRequestedChannels)
       
   424 			{
       
   425 			if (iPlayFormatData.iActualChannels == 2)
       
   426 				{
       
   427 				// requested was mono, we have stereo
       
   428 				currentBytesPlayed /= 2;
       
   429 				}
       
   430 			else 
       
   431 				{
       
   432 				// requested was stereo, we have mono
       
   433 				currentBytesPlayed *= 2;
       
   434 				}
       
   435 			}
       
   436 		if (iPlayFormatData.iSampleRate != iPlayFormatData.iActualRate)
       
   437 			{
       
   438 			currentBytesPlayed = TUint64(currentBytesPlayed*
       
   439 					TReal(iPlayFormatData.iSampleRate)/TReal(iPlayFormatData.iActualRate)); // don't round
       
   440 			}
       
   441 		}
       
   442 
       
   443     #ifdef SYMBIAN_SOUNDADAPTER_DEBUG
       
   444 	RDebug::Printf("currentBytesPlayed %x %x", I64HIGH(currentBytesPlayed), I64LOW(currentBytesPlayed));
       
   445     #endif
       
   446 	return currentBytesPlayed;
       
   447 	}
       
   448 
       
   449 void RMdaDevSound::CBody::ResetBytesPlayed()
       
   450 	{
       
   451     #ifdef SYMBIAN_SOUNDADAPTER_DEBUG    
       
   452     RDebug::Printf("RMdaDevSound::CBody::ResetBytesPlayed %s", iState.Name());
       
   453 	#endif
       
   454 	__ASSERT_DEBUG(iPlaySoundDevice.Handle(), Panic(EDeviceNotOpened));
       
   455 	iBytesPlayed = 0;
       
   456 	iPlaySoundDevice.ResetBytesTransferred();
       
   457 	return;
       
   458 	}
       
   459 	
       
   460 void RMdaDevSound::CBody::PausePlayBuffer()
       
   461 	{
       
   462 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG   
       
   463     RDebug::Printf("RMdaDevSound::CBody::PausePlayBuffer %s", iState.Name());
       
   464 #endif  
       
   465 	switch(iState)
       
   466 		{
       
   467 		case ENotReady:
       
   468 			Panic(EDeviceNotOpened);
       
   469 			break;
       
   470 
       
   471 		case EStopped:
       
   472 			// Can not move to EPlayingPausedInSw because it provokes a datapath panic....
       
   473 			break;
       
   474 
       
   475 		case ERecording:
       
   476 		case ERecordingPausedInHw:
       
   477 		case ERecordingPausedInSw:
       
   478 			Panic(EBadState);
       
   479 			break;
       
   480 
       
   481 		case EPlayingPausedInHw: // ie. Play request pending on h/w and paused
       
   482 		case EPlayingPausedInSw: // ie. Driver not playing or paused
       
   483 			// Already paused so nothing to do.
       
   484 			break;
       
   485 
       
   486 		case EPlayingUnderrun:
       
   487 			iState = EPlayingPausedInSw;
       
   488 			break;
       
   489 			
       
   490 		case EPlaying:
       
   491 			{
       
   492 			iPauseTime = CurrentTimeInMsec();
       
   493 			iPausedBytesPlayed = BytesPlayed64();
       
   494 			TInt res = iPlaySoundDevice.Pause();
       
   495 			#ifdef SYMBIAN_SOUNDADAPTER_DEBUG   
       
   496 			RDebug::Printf("iPlaySoundDevice.Pause res = %d", res);
       
   497 			#endif
       
   498  			if(res == KErrNone)
       
   499 				{
       
   500 				iState = EPlayingPausedInHw;
       
   501 				}
       
   502 			else
       
   503 				{
       
   504 			    #ifdef SYMBIAN_SOUNDADAPTER_DEBUG    
       
   505 				RDebug::Printf("msp PausePlayBuffer hw pause unexpectedly failed, doing sw pause");
       
   506 				#endif
       
   507 				iState = EPlayingPausedInSw;
       
   508 				}
       
   509 			break;
       
   510 			}
       
   511 		
       
   512 		default:
       
   513 			Panic(EBadState);
       
   514 			break;
       
   515 		}
       
   516 	
       
   517 	return;
       
   518 	}
       
   519 	
       
   520 void RMdaDevSound::CBody::ResumePlaying()
       
   521 	{
       
   522 	#ifdef SYMBIAN_SOUNDADAPTER_DEBUG   
       
   523 	RDebug::Printf("RMdaDevSound::CBody::ResumePlaying %s", iState.Name());
       
   524 	#endif	
       
   525     __ASSERT_DEBUG(iPlaySoundDevice.Handle(), Panic(EDeviceNotOpened));
       
   526 
       
   527 	switch(iState)
       
   528 		{
       
   529 		case ENotReady:
       
   530 			Panic(EDeviceNotOpened);
       
   531 			break;
       
   532 				
       
   533 		case EStopped:
       
   534 			// No change
       
   535 			break;
       
   536 	
       
   537 		case ERecording:
       
   538 		case ERecordingPausedInHw:
       
   539 		case ERecordingPausedInSw:
       
   540 			Panic(EBadState);
       
   541 			break;
       
   542 			
       
   543 		case EPlaying:
       
   544 			// No change
       
   545 			break;
       
   546 
       
   547 		case EPlayingPausedInHw: // ie. Play request pending on h/w and paused
       
   548 			{
       
   549 			// Re-enable reporting of KErrUnderflow (will re-raise KErrUnderflow if nothing to start playing).
       
   550 			iUnderFlowReportedSinceLastPlayOrRecordRequest = EFalse;
       
   551 
       
   552 			TInt res = iPlaySoundDevice.Resume();
       
   553 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG	
       
   554 			RDebug::Printf("ResumePlayBuffer EPlayingPausedInHw res = %d", res);
       
   555 #endif
       
   556 			if(res == KErrNone)
       
   557 				{
       
   558 				// Resume ok so a pending request will complete
       
   559 				iState = EPlaying;
       
   560 	            // Update iStartTime to allow for time spent paused
       
   561 	            TUint32 curTime = CurrentTimeInMsec();
       
   562 	            TUint32 timePaused = (curTime >= iPauseTime) ? (curTime-iPauseTime) : (KMaxTUint32 - (iPauseTime-curTime));
       
   563 	            iStartTime += timePaused; // nb. It is harmless if this wraps.
       
   564 				}
       
   565 			else
       
   566 				{
       
   567 				// Resume failed, therefore driver is not playing
       
   568                 // No need to update iStartTime/iPauseTime because these are only used within a driver request
       
   569                 // Change state to Stopped
       
   570                 iState = EStopped;
       
   571                 //  Attempt to start a new (pending) request.
       
   572                 StartPlayersAndUpdateState();
       
   573 				}
       
   574 			break;
       
   575 			}
       
   576 
       
   577 		case EPlayingPausedInSw: // ie. Driver not playing/paused
       
   578 			{
       
   579 			// Driver not playing
       
   580 			// Re-enable reporting of KErrUnderflow (will re-raise KErrUnderflow if nothing to start playing).
       
   581 			iUnderFlowReportedSinceLastPlayOrRecordRequest = EFalse;
       
   582 			// No need to update iStartTime/iPauseTime because these are only used within a driver request
       
   583 			// Change state to Stopped
       
   584             iState = EStopped;
       
   585             //	Attempt to start a new (pending) request.
       
   586 			StartPlayersAndUpdateState();
       
   587 			break;
       
   588 			}
       
   589 	
       
   590 		case EPlayingUnderrun:
       
   591 			// Not paused, therefore no change
       
   592 			break;
       
   593 			
       
   594 		default:
       
   595 			Panic(EBadState);
       
   596 			break;
       
   597 		}
       
   598 	
       
   599 	return;	
       
   600 	}
       
   601 
       
   602 void RMdaDevSound::CBody::PauseRecordBuffer()
       
   603 	{
       
   604     #ifdef SYMBIAN_SOUNDADAPTER_DEBUG   
       
   605     RDebug::Printf("RMdaDevSound::CBody::PauseRecordBuffer %s", iState.Name());
       
   606     #endif
       
   607 	
       
   608 	switch(iState)
       
   609 		{
       
   610 		case ENotReady:
       
   611 			Panic(EDeviceNotOpened);
       
   612 			break;
       
   613 			
       
   614 		case EStopped:
       
   615 			// Driver is not recording
       
   616 		    // Do not pause because that will cause problems when CAudioDevice::Pause calls
       
   617 		    // PausePlayBuffer and PauseRecordBuffer in state EStopped...
       
   618 			break;
       
   619 
       
   620 		case ERecording:
       
   621 			{
       
   622 			TInt res = iRecordSoundDevice.Pause();
       
   623 			#ifdef SYMBIAN_SOUNDADAPTER_DEBUG	
       
   624 			RDebug::Printf("PauseRecordBuffer EPlaying res = %d", res);
       
   625 			#endif
       
   626 			if(res == KErrNone)
       
   627 				{
       
   628 				iState = ERecordingPausedInHw;
       
   629 				}
       
   630 			else
       
   631 				{
       
   632 				#ifdef SYMBIAN_SOUNDADAPTER_DEBUG	
       
   633 				RDebug::Printf("PauseRecordBuffer hw pause unexpectedly failed, doing sw pause");
       
   634 				#endif
       
   635 				iState = ERecordingPausedInSw;
       
   636 				}
       
   637 			break;
       
   638 			}
       
   639 		
       
   640 		case ERecordingPausedInHw:
       
   641 		case ERecordingPausedInSw:
       
   642 			// Already paused so nothing to do.
       
   643 			break;
       
   644 			
       
   645 		case EPlaying:
       
   646 		case EPlayingPausedInHw: // ie. Play request pending on h/w and paused
       
   647             Panic(EBadState);
       
   648             break;
       
   649 		    
       
   650 		case EPlayingPausedInSw: 
       
   651 		    // This is an ugly hack to maintain compatibility with CAudioDevice::Pause which
       
   652 		    // calls both PausePlayBuffer and PauseRecordBuffer whilst in stopped, then later calls ResumePlaying
       
   653 		    break;
       
   654 
       
   655 		case EPlayingUnderrun: // ie. Play request pending on h/w and paused
       
   656 			Panic(EBadState);
       
   657 			break;
       
   658 		    
       
   659 		default:
       
   660 			Panic(EBadState);
       
   661 			break;
       
   662 		}
       
   663 
       
   664 	return;	
       
   665 	}
       
   666 
       
   667 void RMdaDevSound::CBody::ResumeRecording()
       
   668 	{
       
   669     #ifdef SYMBIAN_SOUNDADAPTER_DEBUG   
       
   670     RDebug::Printf("RMdaDevSound::CBody::ResumeRecording %s", iState.Name());
       
   671     #endif
       
   672 	__ASSERT_DEBUG(iPlaySoundDevice.Handle(), Panic(EDeviceNotOpened));
       
   673 
       
   674 	switch(iState)
       
   675 		{
       
   676 		case ENotReady:
       
   677 			Panic(EDeviceNotOpened);
       
   678 			break;
       
   679 				
       
   680 		case EStopped:
       
   681 			// No change
       
   682 			break;
       
   683 	
       
   684 		case ERecording:
       
   685 			// No change
       
   686 			break;
       
   687 
       
   688 		case ERecordingPausedInHw:
       
   689 			{
       
   690 			TInt res = iRecordSoundDevice.Resume();
       
   691 			#ifdef SYMBIAN_SOUNDADAPTER_DEBUG	
       
   692 			RDebug::Printf("ResumeRecordBuffer ERecordingPausedInHw res = %d", res);
       
   693 			#endif
       
   694 			if(res == KErrNone)
       
   695 				{
       
   696 				// Resume ok so a pending request will complete
       
   697 				iState = ERecording;
       
   698 				}
       
   699 			else
       
   700 				{
       
   701 				iState = EStopped;
       
   702 				// Resume failed, so attempt to start a new (pending) request.
       
   703 				// If this works, it will update the state to ERecording.
       
   704 				StartRecordRequest();
       
   705 				}
       
   706 			break;
       
   707 			}
       
   708 		case ERecordingPausedInSw:
       
   709 			{
       
   710 			// Update state to stopped and attempt to start any pending request
       
   711 			iState = EStopped;
       
   712 			// If this works, it will update the state to ERecording.
       
   713 			StartRecordRequest();
       
   714 			break;
       
   715 			}
       
   716 
       
   717 		case EPlaying:
       
   718 		case EPlayingPausedInHw: // ie. Play request pending on h/w and paused
       
   719 		case EPlayingPausedInSw: // ie. Driver not playing/paused
       
   720 		case EPlayingUnderrun:
       
   721 		default:
       
   722 			Panic(EBadState);
       
   723 			break;
       
   724 		}
       
   725 		
       
   726 		return; 
       
   727 
       
   728 
       
   729 	}
       
   730 
       
   731 TInt RMdaDevSound::CBody::GetTimePlayed(TTimeIntervalMicroSeconds& aTimePlayed)
       
   732 	{
       
   733 	__ASSERT_DEBUG(iPlaySoundDevice.Handle(), Panic(EDeviceNotOpened));
       
   734 
       
   735 
       
   736 	TUint64 bytesPlayed = BytesPlayed64();
       
   737 
       
   738 	TUint64 timePlayed = 1000 * 1000 * bytesPlayed / (iPlayFormatData.iSampleRate * iPlayFormatData.iRequestedChannels * KBytesPerSample);
       
   739 
       
   740 	aTimePlayed = TTimeIntervalMicroSeconds(timePlayed);
       
   741 
       
   742 	return KErrNone;
       
   743 	}
       
   744 
       
   745 	
       
   746 void RMdaDevSound::CBody::FormatsSupported(TSoundFormatsSupportedBuf& aFormatsSupported, RSoundSc& aSoundDevice)
       
   747 	{
       
   748 	TSoundFormatsSupportedV02Buf supportedFormat;
       
   749 	aSoundDevice.Caps(supportedFormat);
       
   750 	TUint32 rates = supportedFormat().iRates;
       
   751 	
       
   752 	for(TInt i = KNumSampleRates-1; i > 0 ;i--)//min to max
       
   753 		{
       
   754 		if(rates & KRateEnumLookup[i].iRateConstant)
       
   755 			{
       
   756 			aFormatsSupported().iMinRate = KRateEnumLookup[i].iRate;
       
   757 			break;
       
   758 			}
       
   759 		}
       
   760 	for(TInt i = 0; i < KNumSampleRates; i++)//max to min
       
   761 		{
       
   762 		if(rates & KRateEnumLookup[i].iRateConstant)
       
   763 			{
       
   764 			aFormatsSupported().iMaxRate = KRateEnumLookup[i].iRate;
       
   765 			break;
       
   766 			}
       
   767 		}
       
   768 	TUint32 enc = supportedFormat().iEncodings;
       
   769 	
       
   770 	if (enc & KSoundEncoding16BitPCM)
       
   771 		{
       
   772 		aFormatsSupported().iEncodings = EMdaSoundEncoding16BitPCM;// Always defaults to this
       
   773 		}
       
   774 	if (enc & KSoundEncoding8BitPCM)
       
   775 		{
       
   776 		aFormatsSupported().iEncodings |= EMdaSoundEncoding8BitPCM;
       
   777 		}
       
   778 	TUint32 channels = supportedFormat().iChannels;
       
   779 	
       
   780 	if (channels & KSoundStereoChannel)
       
   781 		{
       
   782 		aFormatsSupported().iChannels = 2;
       
   783 		}
       
   784 	else
       
   785 		{
       
   786 		aFormatsSupported().iChannels = 1;
       
   787 		}
       
   788 	aFormatsSupported().iMinBufferSize = supportedFormat().iRequestMinSize;
       
   789 	aFormatsSupported().iMaxBufferSize = KMaxBufferSize;
       
   790 	aFormatsSupported().iMinVolume = 0;
       
   791 	aFormatsSupported().iMaxVolume = KSoundMaxVolume;
       
   792 	}
       
   793 	
       
   794 void RMdaDevSound::CBody::GetFormat(TCurrentSoundFormatBuf& aFormat, 
       
   795 									RSoundSc& /*aSoundDevice*/,
       
   796 									const TFormatData &aFormatData)
       
   797 	{
       
   798 	// always return the requested, or the initial, not current device setting
       
   799 	aFormat().iChannels = aFormatData.iRequestedChannels; // never clear if this is bitmap or value, but effectively the same
       
   800 	aFormat().iRate = aFormatData.iSampleRate;
       
   801 	}
       
   802 	
       
   803 void RMdaDevSound::CBody::StartPlayersAndUpdateState()
       
   804 	{
       
   805 	__ASSERT_DEBUG(iPlaySoundDevice.Handle(), Panic(EDeviceNotOpened));
       
   806 
       
   807 	switch(iState)
       
   808 		{
       
   809 		case ENotReady:
       
   810 			Panic(EDeviceNotOpened);
       
   811 			break;
       
   812 				
       
   813 		case EStopped:
       
   814  			// Allow following code to queue more driver play requests and check for stopped
       
   815 			break;
       
   816 	
       
   817 		case ERecording:
       
   818 		case ERecordingPausedInHw:
       
   819 		case ERecordingPausedInSw:
       
   820 			Panic(EBadState);
       
   821 			break;
       
   822 			
       
   823 		case EPlaying:
       
   824             // Allow following code to queue more driver play requests  and check for underrun
       
   825 		    break;
       
   826 			
       
   827 		case EPlayingPausedInHw: // ie. Play request pending on h/w and paused
       
   828 			// Allow following code to queue more driver play requests
       
   829 			break;
       
   830 		
       
   831 		case EPlayingPausedInSw:
       
   832 			// Paused but driver not playing+paused, therefore do not queue new requests until ResumePlaying
       
   833 			return;
       
   834 
       
   835 		case EPlayingUnderrun:
       
   836 			// Allow following code to queue more driver play requests		
       
   837 			break;
       
   838 			
       
   839 		default:
       
   840 			Panic(EBadState);
       
   841 			break;
       
   842 		}
       
   843 
       
   844 	// iState is now either EStopped, EPlaying, EPlayingPausedInHw or EPlayingUnderrun
       
   845     __ASSERT_DEBUG(((iState == EStopped) || (iState == EPlaying) || (iState == EPlayingPausedInHw) || (iState == EPlayingUnderrun)), Panic(EBadState));
       
   846 
       
   847 	while( (iClientPlayData.Length() != 0) && (! iFreePlayers.IsEmpty()))
       
   848 		{
       
   849 		// More data to play and more players,  so issue another request 
       
   850 
       
   851 		bool wasIdle = iFreePlayers.IsFull();
       
   852 		// Get a free player		
       
   853 		CPlayer *player = iFreePlayers.Pop();
       
   854 		// Calculate length of request
       
   855 		TUint32 lengthToPlay = iClientPlayData.Length();
       
   856 		if(lengthToPlay > iDeviceBufferLength)
       
   857 		    {
       
   858             lengthToPlay = iDeviceBufferLength;
       
   859 		    }
       
   860 
       
   861 		// Remember request length, so we can update bytes played when it finishes
       
   862 		iActivePlayRequestSizes.Push(lengthToPlay);
       
   863 
       
   864 		// Find offset to copy data to
       
   865 		TUint playerIndex = player->GetPlayerIndex();
       
   866 		ASSERT(playerIndex < KPlaySharedChunkBuffers);
       
   867 		TUint chunkOffset = iPlayBufferConfig.iBufferOffsetList[playerIndex];
       
   868 
       
   869 		// Copy data
       
   870 		TPtr8 destPtr(iPlayChunk.Base()+ chunkOffset, 0, iDeviceBufferLength);
       
   871 		destPtr.Copy(iClientPlayData.Mid(0, lengthToPlay));
       
   872 
       
   873 		// Update iClientPlayData to remove the data just queued
       
   874 		iClientPlayData.Set(iClientPlayData.Right(iClientPlayData.Length()-lengthToPlay));
       
   875 
       
   876 		// Start the CPlayer
       
   877 		player->PlayData(chunkOffset, lengthToPlay);
       
   878 		if(wasIdle)
       
   879 			{
       
   880 			// HW was not active so update state and start time
       
   881 			iState = EPlaying;
       
   882 			iStartTime = CurrentTimeInMsec();
       
   883 			}
       
   884 		}
       
   885 
       
   886 	// Check if the client request is now complete
       
   887 	if(iClientPlayData.Length() == 0 && iClientPlayStatus)
       
   888 		{
       
   889 		// We have queued all the client play data to the driver so we can now complete the client request.
       
   890 		// If actual playback fails, we will notify the client via the Play Error notification mechanism.
       
   891 		#ifdef SYMBIAN_SOUNDADAPTER_DEBUG	
       
   892 		RDebug::Printf("RMdaDevSound::CBody::StartPlayersAndUpdateState completing client request");
       
   893 		#endif
       
   894 		User::RequestComplete(iClientPlayStatus, KErrNone); // This call also sets iClientPlayStatus to NULL
       
   895 		}
       
   896 	
       
   897     //nb. iState is now either EStopped, EPlaying, EPlayingPausedInHw or EPlayingUnderrun (see previous switch and assert)
       
   898 	if(iState != EPlayingPausedInHw)
       
   899 	    {
       
   900         if(iFreePlayers.IsFull())
       
   901             {
       
   902             // Free fifo is full, therefore there are no active players
       
   903             iState = EPlayingUnderrun;
       
   904 			if(! iUnderFlowReportedSinceLastPlayOrRecordRequest)
       
   905 				{
       
   906 				// We report KErrUnderflow if we have not already reported it since the last PlayData call.
       
   907 				// Note that 
       
   908 				// i) We do NOT report driver underflows.
       
   909 				// ii) We report underflow when we run out of data to pass to the driver.
       
   910 				// iii) We throttle this reporting
       
   911 				// iv) We WILL report KErrUnderflow if already stopped and asked to play a zero length buffer
       
   912 				// The last point is required because the client maps a manual stop command into a devsound play with a 
       
   913 				// zero length buffer and the last buffer flag set, this in turn is mapped to a Playdata calll with an empty buffer
       
   914 				// which is expected to complete ok and cause a KErrUnderflow error to be reported...
       
   915 				iUnderFlowReportedSinceLastPlayOrRecordRequest = ETrue;
       
   916 				#ifdef SYMBIAN_SOUNDADAPTER_DEBUG	
       
   917 		        RDebug::Printf("RMdaDevSound::CBody::StartPlayersAndUpdateState stopped and iUnderFlowReportedSinceLastPlayOrRecordRequest false so raising KErrUnderflow");
       
   918 				#endif
       
   919 				
       
   920 				// Discard any saved trailing data (ie. data saved due driver requiring all requests to be a multiple of iRequestMinSize).
       
   921 				// This maybe because client is delibrately letting us underflow to play silence. In that case we do not want to
       
   922 				// play the trailing data at the beginning of the new data issued after the silence...
       
   923 				iSavedTrailingData.SetLength(0);
       
   924 
       
   925 				SoundDeviceError(KErrUnderflow);
       
   926 				}
       
   927             }
       
   928         else
       
   929             {
       
   930             // Free fifo not full, therefore there are active players
       
   931             iState = EPlaying;
       
   932             }
       
   933 	    }
       
   934 	return;
       
   935 	}
       
   936 
       
   937 TInt RMdaDevSound::CBody::SetFormat(const TCurrentSoundFormatBuf& aFormat, 
       
   938 									RSoundSc& aSoundDevice,
       
   939 									TFormatData &aFormatData)
       
   940 	{
       
   941 	TInt err = KErrNotFound;
       
   942 	TCurrentSoundFormatV02Buf formatBuf;
       
   943 	
       
   944 	delete aFormatData.iConverter; 
       
   945 	aFormatData.iConverter = NULL; // setting this to NULL indicates we are not using converter. No other flag
       
   946 	iConvertedPlayData.Close();
       
   947 	
       
   948 	TInt wantedRate = aFormat().iRate;
       
   949 	for(TInt index = 0; index < KNumSampleRates; index++ )
       
   950 		{
       
   951 		if(wantedRate == KRateEnumLookup[index].iRate)
       
   952 			{
       
   953 			formatBuf().iRate = KRateEnumLookup[index].iRateEnum;
       
   954 			aFormatData.iSampleRate = wantedRate;
       
   955 			err = KErrNone;
       
   956 			break;
       
   957 			}
       
   958 		}
       
   959 	
       
   960 	if(err == KErrNone)
       
   961 		{
       
   962 		// Assume, for now, we support the requested channels and rate
       
   963 		aFormatData.iActualChannels = aFormatData.iRequestedChannels;
       
   964 		aFormatData.iActualRate = aFormatData.iSampleRate;
       
   965 
       
   966 		// Attempt to configure driver
       
   967 		formatBuf().iChannels = aFormat().iChannels;
       
   968 		formatBuf().iEncoding = ESoundEncoding16BitPCM;
       
   969 		formatBuf().iDataFormat = ESoundDataFormatInterleaved;
       
   970 		err = aSoundDevice.SetAudioFormat(formatBuf);
       
   971         #if defined(SYMBIAN_SOUNDADAPTER_FORCECDRATES) || defined (SYMBIAN_SOUNDADAPTER_FORCESTEREO)
       
   972             err = KErrNotSupported; // force Negotiate - for debugging
       
   973         #endif
       
   974 		if (err==KErrNotSupported)
       
   975 			{
       
   976 			// don't support directly. Perhaps can rate convert?
       
   977 			err = NegotiateFormat(aFormat, aSoundDevice, aFormatData);
       
   978 			}
       
   979 		}
       
   980 	return err;	
       
   981 	}
       
   982 	
       
   983 TInt RMdaDevSound::CBody::NegotiateFormat(const TCurrentSoundFormatBuf& aFormat, 
       
   984 										  RSoundSc& aSoundDevice, 
       
   985 										  TFormatData &aFormatData)
       
   986 	{
       
   987 	ASSERT(!aFormatData.iConverter); // we don't clear on fail - so assuming NULL to start with
       
   988 	
       
   989 	TInt err = KErrNotFound;
       
   990 	TCurrentSoundFormatV02Buf formatBuf;
       
   991 
       
   992 	// find out first what the driver supports
       
   993 	TSoundFormatsSupportedV02Buf supportedFormat;
       
   994 	aSoundDevice.Caps(supportedFormat);
       
   995 	TUint32 supportedRates = supportedFormat().iRates;
       
   996     #ifdef SYMBIAN_SOUNDADAPTER_FORCECDRATES
       
   997         supportedRates &= KSoundRate11025Hz| KSoundRate22050Hz | KSoundRate44100Hz; // only use CD rates - for debugging
       
   998     #endif
       
   999 	
       
  1000 	// For PlayCase:
       
  1001 	// 		first try to find the first rate below or equal to the requested that is supported
       
  1002 	// 		initially go down and be fussy, but if we pass the requested rate find the first that
       
  1003 	// 		is supported
       
  1004 	// For RecordCase:
       
  1005 	//		We want the next rate above consistently - we go down from this to the requested rate.
       
  1006 	//		If there is one, we don't support - we _never_ upsample.
       
  1007 	// note that the table is given in descending order, so we start with the highest
       
  1008 	TInt wantedRate = aFormat().iRate;
       
  1009 	TInt takeTheFirst = EFalse;
       
  1010 	TInt nextUpValidIndex = -1;
       
  1011 	for(TInt index = 0; index < KNumSampleRates; index++ )
       
  1012 		{
       
  1013 		TBool lookingAtRequestedRate = wantedRate == KRateEnumLookup[index].iRate;
       
  1014 		TSoundRate wantedEnum = KRateEnumLookup[index].iRateEnum;
       
  1015 		TUint32 equivBitmap = KRateEnumLookup[index].iRateConstant;
       
  1016 		TBool isSupported = (equivBitmap & supportedRates) != EFalse;
       
  1017 		if (lookingAtRequestedRate || takeTheFirst)
       
  1018 			{
       
  1019 			if (isSupported)
       
  1020 				{
       
  1021 				// this rate is supported
       
  1022 				formatBuf().iRate = wantedEnum;
       
  1023 				aFormatData.iActualRate = KRateEnumLookup[index].iRate;
       
  1024 				err = KErrNone;
       
  1025 				break;				
       
  1026 				}
       
  1027 			}
       
  1028 		else if (!takeTheFirst)
       
  1029 			{
       
  1030 			// while we are still looking for the rate, want to cache any supported index
       
  1031 			// at end of loop, this will be the first rate above ours that is supported
       
  1032 			// use for fallback if required
       
  1033 			if (isSupported)
       
  1034 				{
       
  1035 				nextUpValidIndex = index;
       
  1036 				}
       
  1037 			}
       
  1038 		if (lookingAtRequestedRate)
       
  1039 			{
       
  1040 			// if we get this far we've gone passed the wanted rate. For play we enable
       
  1041 			// "takeTheFirst". For record we just abort.
       
  1042 			if (&aSoundDevice==&iPlaySoundDevice)
       
  1043 				{
       
  1044 				takeTheFirst = ETrue;
       
  1045 				}
       
  1046 			else
       
  1047 				{
       
  1048 				break;
       
  1049 				}
       
  1050 			}
       
  1051 		}
       
  1052 		
       
  1053 	if (err)
       
  1054 		{
       
  1055 		// if there is one above the requested rate, use that
       
  1056 		if (nextUpValidIndex>=0)
       
  1057 			{
       
  1058 			TSoundRate wantedEnum = KRateEnumLookup[nextUpValidIndex].iRateEnum;
       
  1059 			formatBuf().iRate = wantedEnum;
       
  1060 			aFormatData.iActualRate = KRateEnumLookup[nextUpValidIndex].iRate;
       
  1061 			err = KErrNone;		
       
  1062 			}
       
  1063 		}
       
  1064 		
       
  1065 	if (err)
       
  1066 		{
       
  1067 		// should have something!
       
  1068 		return err;
       
  1069 		}
       
  1070 		
       
  1071 	aFormatData.iSampleRate = wantedRate; // iSampleRate is our requested/apparent rate, not the device rate.
       
  1072 	
       
  1073 	TUint32 channelsSupported = supportedFormat().iChannels;
       
  1074     #ifdef SYMBIAN_SOUNDADAPTER_FORCESTEREO
       
  1075         channelsSupported &= KSoundStereoChannel; // don't use mono - for debugging
       
  1076     #endif
       
  1077 
       
  1078 	if (aFormat().iChannels == 1)
       
  1079 		{
       
  1080 		aFormatData.iRequestedChannels = 1;
       
  1081 		// want mono
       
  1082 		if (channelsSupported & KSoundMonoChannel)
       
  1083 			{
       
  1084 			// mono is supported, as usual
       
  1085 			aFormatData.iActualChannels = 1;
       
  1086 			}
       
  1087 		else if (channelsSupported & KSoundStereoChannel)
       
  1088 			{
       
  1089 			aFormatData.iActualChannels = 2;
       
  1090 			}
       
  1091 		else
       
  1092 			{
       
  1093 			return KErrNotSupported; // should not get this far for real
       
  1094 			}
       
  1095 		}
       
  1096 	else if (aFormat().iChannels == 2)
       
  1097 		{
       
  1098 		aFormatData.iRequestedChannels = 2;
       
  1099 		// want stereo
       
  1100 		if (channelsSupported & KSoundStereoChannel)
       
  1101 			{
       
  1102 			// stereo is supported, as usual
       
  1103 			aFormatData.iActualChannels = 2;
       
  1104 			}
       
  1105 		else if (channelsSupported & KSoundMonoChannel)
       
  1106 			{
       
  1107 			aFormatData.iActualChannels = 1;
       
  1108 			}
       
  1109 		else
       
  1110 			{
       
  1111 			return KErrNotSupported; // should not get this far for real
       
  1112 			}
       
  1113 		}
       
  1114 	else
       
  1115 		{
       
  1116 		return KErrNotSupported; // unknown number of channels requested!
       
  1117 		}
       
  1118 	
       
  1119 	formatBuf().iChannels = aFormatData.iActualChannels;
       
  1120 	
       
  1121 	formatBuf().iEncoding = ESoundEncoding16BitPCM;
       
  1122 	formatBuf().iDataFormat = ESoundDataFormatInterleaved;
       
  1123 	err = aSoundDevice.SetAudioFormat(formatBuf);
       
  1124 	
       
  1125 	if (!err)
       
  1126 		{
       
  1127 		ASSERT(!aFormatData.iConverter); // pre-condition at top of function anyway
       
  1128 		if (&aSoundDevice==&iPlaySoundDevice)
       
  1129 			{
       
  1130             #ifdef SYMBIAN_SOUNDADAPTER_DEBUG
       
  1131                 RDebug::Print(_L("RMdaDevSound::CBody::NegotiateFormat: Convert:CreateL from %d/%d to %d/%d"), 
       
  1132                             aFormatData.iSampleRate, aFormatData.iRequestedChannels, 
       
  1133                             aFormatData.iActualRate, aFormatData.iActualChannels);
       
  1134             #endif																	       
       
  1135 			// when playing we convert from requested to actual
       
  1136 			TRAP(err, aFormatData.iConverter = CChannelAndSampleRateConverter::CreateL(aFormatData.iSampleRate, 
       
  1137 																		   aFormatData.iRequestedChannels, 
       
  1138 																	       aFormatData.iActualRate, 
       
  1139 																	       aFormatData.iActualChannels));
       
  1140 			}
       
  1141 		else
       
  1142 			{
       
  1143 			// when recording we convert from actual to requested
       
  1144 			TInt outputRateToUse = aFormatData.iSampleRate;
       
  1145             #ifdef SYMBIAN_SKIP_RESAMPLE_ON_RECORD
       
  1146                 // with this macro just channel convert at most
       
  1147                 outputRateToUse = aFormatData.iActualRate;
       
  1148             #endif
       
  1149             #ifdef SYMBIAN_SOUNDADAPTER_DEBUG
       
  1150                 RDebug::Print(_L("RMdaDevSound::CBody::NegotiateFormat: Convert:CreateL from %d/%d to %d/%d"), 
       
  1151                             aFormatData.iActualRate, aFormatData.iActualChannels,
       
  1152                             aFormatData.iSampleRate, aFormatData.iRequestedChannels); 
       
  1153             #endif																	       
       
  1154 			TRAP(err, aFormatData.iConverter = CChannelAndSampleRateConverter::CreateL(aFormatData.iActualRate, 
       
  1155 																	       aFormatData.iActualChannels,
       
  1156 																	       outputRateToUse, 
       
  1157 																		   aFormatData.iRequestedChannels));
       
  1158 			}
       
  1159 		}
       
  1160 	if(err != KErrNone)
       
  1161 		{
       
  1162 		delete aFormatData.iConverter;
       
  1163 		aFormatData.iConverter= NULL;
       
  1164 		iConvertedPlayData.Close();
       
  1165 		}
       
  1166 	
       
  1167 	return err;
       
  1168 	}
       
  1169 
       
  1170 void RMdaDevSound::CBody::StartRecordRequest()
       
  1171 	{
       
  1172 	__ASSERT_DEBUG(iRecordSoundDevice.Handle(), Panic(EDeviceNotOpened));
       
  1173 	
       
  1174 	iRecorder->RecordData(iBufferLength);
       
  1175 	}
       
  1176 
       
  1177 // Note both InRecordMode and InPlayMode return EFalse for ENotReady and EStopped
       
  1178 TBool RMdaDevSound::CBody::InRecordMode()const
       
  1179 	{
       
  1180 	switch(iState)
       
  1181 		{
       
  1182 		case ENotReady:
       
  1183 		case EStopped:
       
  1184 			return EFalse;
       
  1185 			
       
  1186 		case ERecording:
       
  1187 		case ERecordingPausedInHw:
       
  1188 		case ERecordingPausedInSw:
       
  1189 			return ETrue;
       
  1190 			
       
  1191 		case EPlaying:
       
  1192 		case EPlayingPausedInHw: 
       
  1193 		case EPlayingPausedInSw:
       
  1194 		case EPlayingUnderrun:
       
  1195 			return EFalse;
       
  1196 			
       
  1197 		default:
       
  1198 			Panic(EBadState);
       
  1199 			break;
       
  1200 		}
       
  1201 	return EFalse;
       
  1202 	}
       
  1203 
       
  1204 TBool RMdaDevSound::CBody::InPlayMode() const
       
  1205 	{
       
  1206 	switch(iState)
       
  1207 		{
       
  1208 		case ENotReady:
       
  1209 		case EStopped:
       
  1210 			return EFalse;
       
  1211 			
       
  1212 		case ERecording:
       
  1213 		case ERecordingPausedInHw:
       
  1214 		case ERecordingPausedInSw:
       
  1215 			return EFalse;
       
  1216 			
       
  1217 		case EPlaying:
       
  1218 		case EPlayingPausedInHw: 
       
  1219 		case EPlayingPausedInSw:
       
  1220 		case EPlayingUnderrun:
       
  1221 			return ETrue;
       
  1222 			
       
  1223 		default:
       
  1224 			Panic(EBadState);
       
  1225 			break;
       
  1226 		}
       
  1227 	
       
  1228 	return EFalse;
       
  1229 	}
       
  1230 
       
  1231 
       
  1232 TUint32 RMdaDevSound::CBody::CurrentTimeInMsec() const
       
  1233 	{
       
  1234 	TUint64 tmp = User::NTickCount();
       
  1235 	tmp *= iNTickPeriodInUsec;
       
  1236 	tmp /= 1000;
       
  1237 	return TUint32(tmp);
       
  1238 	}
       
  1239 
       
  1240 void RMdaDevSound::CBody::PlayFormatsSupported(TSoundFormatsSupportedBuf& aFormatsSupported)
       
  1241 	{
       
  1242 	__ASSERT_DEBUG(iPlaySoundDevice.Handle(), Panic(EDeviceNotOpened));
       
  1243 	FormatsSupported(aFormatsSupported, iPlaySoundDevice);
       
  1244 	}
       
  1245 	
       
  1246 void RMdaDevSound::CBody::GetPlayFormat(TCurrentSoundFormatBuf& aFormat)
       
  1247 	{
       
  1248 	__ASSERT_DEBUG(iPlaySoundDevice.Handle(), Panic(EDeviceNotOpened));
       
  1249 	GetFormat(aFormat, iPlaySoundDevice, iPlayFormatData);
       
  1250 	}
       
  1251 	
       
  1252 TInt RMdaDevSound::CBody::SetPlayFormat(const TCurrentSoundFormatBuf& aFormat)
       
  1253 	{
       
  1254 	__ASSERT_DEBUG(iPlaySoundDevice.Handle(), Panic(EDeviceNotOpened));
       
  1255 	return SetFormat(aFormat, iPlaySoundDevice, iPlayFormatData);
       
  1256 	}
       
  1257 
       
  1258 void RMdaDevSound::CBody::RecordFormatsSupported(TSoundFormatsSupportedBuf& aFormatsSupported)
       
  1259 	{
       
  1260 	__ASSERT_DEBUG(iRecordSoundDevice.Handle(), Panic(EDeviceNotOpened));
       
  1261 	FormatsSupported(aFormatsSupported, iRecordSoundDevice);
       
  1262 	}
       
  1263 
       
  1264 void RMdaDevSound::CBody::GetRecordFormat(TCurrentSoundFormatBuf& aFormat)
       
  1265 	{
       
  1266 	__ASSERT_DEBUG(iRecordSoundDevice.Handle(), Panic(EDeviceNotOpened));
       
  1267 	GetFormat(aFormat, iRecordSoundDevice, iRecordFormatData);	
       
  1268 	}
       
  1269 
       
  1270 TInt RMdaDevSound::CBody::SetRecordFormat(const TCurrentSoundFormatBuf& aFormat)
       
  1271 	{
       
  1272 	__ASSERT_DEBUG(iRecordSoundDevice.Handle(), Panic(EDeviceNotOpened));
       
  1273 	return SetFormat(aFormat, iRecordSoundDevice, iRecordFormatData);
       
  1274 	}
       
  1275 	
       
  1276 void RMdaDevSound::CBody::Close()
       
  1277 	{
       
  1278     #ifdef SYMBIAN_SOUNDADAPTER_DEBUG
       
  1279         RDebug::Printf("void RMdaDevSound::CBody::Close() started");
       
  1280     #endif
       
  1281 	iBufferOffset = -1;
       
  1282 	iBufferLength = 0;
       
  1283 
       
  1284 	if(iPlaySoundDevice.Handle() != KNullHandle)
       
  1285 	    {
       
  1286         // Make sure all player objects are idle
       
  1287         CancelPlayData();
       
  1288         iPlayChunk.Close();
       
  1289         iPlaySoundDevice.Close();
       
  1290 	    }
       
  1291 
       
  1292     if(iRecordSoundDevice.Handle() != KNullHandle)
       
  1293         {
       
  1294         CancelRecordData();
       
  1295         iRecordChunk.Close();
       
  1296         iRecordSoundDevice.Close();
       
  1297         }
       
  1298 	
       
  1299 	iState = ENotReady;
       
  1300     #ifdef SYMBIAN_SOUNDADAPTER_DEBUG
       
  1301         RDebug::Printf("void RMdaDevSound::CBody::Close() ended");
       
  1302     #endif
       
  1303 	}
       
  1304 
       
  1305 TInt RMdaDevSound::CBody::Handle()
       
  1306 	{//This method is actually used to check whether the device is opened. Below logic should work
       
  1307 	if(iPlaySoundDevice.Handle())
       
  1308 		{
       
  1309 		return iPlaySoundDevice.Handle();
       
  1310 		}
       
  1311 	if(iRecordSoundDevice.Handle())
       
  1312 		{
       
  1313 		return iRecordSoundDevice.Handle();
       
  1314 		}
       
  1315 	return 0;
       
  1316 	}
       
  1317 
       
  1318 
       
  1319 void RMdaDevSound::CBody::PlayData(TRequestStatus& aStatus, const TDesC8& aData)
       
  1320 	{
       
  1321 	#ifdef SYMBIAN_SOUNDADAPTER_DEBUG
       
  1322     RDebug::Printf("RMdaDevSound::CBody::PlayData(0x%x,%d) State=%s Handle=%d.",&aStatus, 
       
  1323                    aData.Length(), iState.Name(), iPlayChunk.Handle());
       
  1324 	#endif
       
  1325 	
       
  1326 	__ASSERT_DEBUG(iPlaySoundDevice.Handle(), Panic(EDeviceNotOpened));
       
  1327 	aStatus = KRequestPending;
       
  1328 
       
  1329 	if((iClientPlayStatus != NULL) || InRecordMode())
       
  1330 		{
       
  1331 		// We only support one outstanding request
       
  1332 		// No support for simultaneous play and record in RMdaDevSound
       
  1333 		TRequestStatus *pRequest = &aStatus;
       
  1334 		User::RequestComplete(pRequest, KErrInUse);
       
  1335 		return;
       
  1336 		}
       
  1337 	iClientPlayStatus = &aStatus;//store the status of datapath player
       
  1338 
       
  1339 	if(iPlayFormatData.iConverter || iSavedTrailingData.Length() != 0)
       
  1340 		{
       
  1341 		// Need a conversion buffer
       
  1342         // Needs to hold any trailing data truncated from the previous request (due
       
  1343         // to alignment requirements) and either the new data, or the new rate adapted data
       
  1344 		TUint32 spaceRequired = iSavedTrailingData.Length();
       
  1345 		if(iPlayFormatData.iConverter)
       
  1346 			{
       
  1347 			// Doing rate conversion so also need space for the converted data
       
  1348 			spaceRequired += iPlayFormatData.iConverter->MaxConvertBufferSize(aData.Length());
       
  1349 			}
       
  1350 		else
       
  1351 			{
       
  1352 			// Not doing rate adaptation therefore only need to allow for the new incoming data
       
  1353 			spaceRequired += aData.Length();
       
  1354 			}
       
  1355 		// Check if existing buffer exists and is big enough
       
  1356 		if(iConvertedPlayData.MaxLength() < spaceRequired)
       
  1357 			{
       
  1358 			iConvertedPlayData.Close();
       
  1359 			TInt err = iConvertedPlayData.Create(spaceRequired);
       
  1360 			if(err)
       
  1361 				{
       
  1362 				User::RequestComplete(iClientPlayStatus, err);
       
  1363 				return;
       
  1364 				}
       
  1365 			}
       
  1366 
       
  1367 		// Truncate iConvertedPlayData and copy in saved trailing data (if any)
       
  1368 		iConvertedPlayData = iSavedTrailingData;
       
  1369 		iSavedTrailingData.SetLength(0);
       
  1370 		
       
  1371 		// Now append rate adapted data or incoming data
       
  1372 		if (iPlayFormatData.iConverter)
       
  1373 			{
       
  1374             // The convertor will panic if it fails to convert any data, therefore
       
  1375             // we avoid passing it an empty source buffer
       
  1376 			if(aData.Length() != 0)
       
  1377 				{
       
  1378                 TPtr8 destPtr((TUint8 *)iConvertedPlayData.Ptr()+iConvertedPlayData.Length(), 0, iConvertedPlayData.MaxLength()-iConvertedPlayData.Length());
       
  1379 				TInt len = iPlayFormatData.iConverter->Convert(aData, destPtr);
       
  1380 				iConvertedPlayData.SetLength(iConvertedPlayData.Length() + destPtr.Length());
       
  1381 				if(len != aData.Length())
       
  1382 					{
       
  1383 					#ifdef SYMBIAN_SOUNDADAPTER_DEBUG
       
  1384 					RDebug::Printf("RMdaDevSound::CBody::PlayData converted %d	but expected to convert %d", len, aData.Length());
       
  1385 					#endif
       
  1386 					}
       
  1387 				}
       
  1388 			}
       
  1389 		else
       
  1390 			{
       
  1391 			iConvertedPlayData.Append(aData);
       
  1392 			}
       
  1393 		iClientPlayData.Set(iConvertedPlayData);
       
  1394 		}
       
  1395 	else
       
  1396 		{
       
  1397 		// Do not need a conversion buffer so just aim the descriptor at the data
       
  1398 		iClientPlayData.Set(aData);
       
  1399 		}
       
  1400 	iUnderFlowReportedSinceLastPlayOrRecordRequest = EFalse;
       
  1401 
       
  1402 	// All driver requests must be an exact multiple of iRequestMinSize
       
  1403 	TUint32 trailingDataLen = iClientPlayData.Length() % iRequestMinSize;
       
  1404 	if(trailingDataLen)
       
  1405 		{
       
  1406 		// Not a multiple of iRequestMinSize, so need to truncate current request, and save trailing bytes for 
       
  1407 		// inclusion at the beginning of the next request
       
  1408 		iSavedTrailingData = iClientPlayData.Right(trailingDataLen);
       
  1409 		iClientPlayData.Set(iClientPlayData.Left(iClientPlayData.Length()-trailingDataLen));
       
  1410 		}
       
  1411 
       
  1412     #ifdef SYMBIAN_FORCE_32BIT_LENGTHS
       
  1413 	if (iClientPlayData.Length()%4 != 0)
       
  1414 	    {
       
  1415         // simulate the limitation of some hardware, where -6 is generated if the
       
  1416         // buffer length is not divisible by 4.
       
  1417         TRequestStatus *pRequest = &aStatus;
       
  1418         User::RequestComplete(pRequest, KErrArgument);
       
  1419 	}
       
  1420     #endif
       
  1421 
       
  1422 	iRecordChunk.Close();
       
  1423 	if(!iPlayChunk.Handle())
       
  1424 		{
       
  1425 		//This is where we setup to play. 
       
  1426 		//Configure the shared chunk for two buffers with iBufferSize each
       
  1427 		iPlayBufferConfig.iNumBuffers = KPlaySharedChunkBuffers;
       
  1428 		iDeviceBufferLength = KPlaySharedChunkBufferSize;
       
  1429 		#ifdef SYMBIAN_SOUNDADAPTER_DEBUG	
       
  1430 		RDebug::Printf("iDeviceBufferLength %d", iDeviceBufferLength);
       
  1431 		#endif
       
  1432 		iPlayBufferConfig.iFlags = 0;//data will be continuous
       
  1433 		// If required, use rate converter etc
       
  1434 		iPlayBufferConfig.iBufferSizeInBytes = iDeviceBufferLength;
       
  1435         #ifdef SYMBIAN_SOUNDADAPTER_DEBUG
       
  1436             RDebug::Printf("number of buffers: [%d]",iPlayBufferConfig.iNumBuffers);
       
  1437             RDebug::Printf("BufferSize in Bytes [%d]",iPlayBufferConfig.iBufferSizeInBytes);
       
  1438         #endif
       
  1439 		TPckg<TPlaySharedChunkBufConfig> bufferConfigBuf(iPlayBufferConfig);
       
  1440 		TInt error = iPlaySoundDevice.SetBufferChunkCreate(bufferConfigBuf,iPlayChunk);
       
  1441 		if(error == KErrNone)
       
  1442 			{
       
  1443 			iPlaySoundDevice.GetBufferConfig(bufferConfigBuf);
       
  1444 			}
       
  1445 		if (error)
       
  1446 			{
       
  1447 			SoundDeviceError(error);
       
  1448 			return;
       
  1449 			}
       
  1450 		}
       
  1451 
       
  1452     StartPlayersAndUpdateState();
       
  1453 
       
  1454 	return;	
       
  1455 	}
       
  1456 
       
  1457 void RMdaDevSound::CBody::RecordData(TRequestStatus& aStatus, TDes8& aData)
       
  1458 	{
       
  1459 	__ASSERT_DEBUG(iRecordSoundDevice.Handle(), Panic(EDeviceNotOpened));
       
  1460 	aStatus = KRequestPending;
       
  1461 	if((iClientPlayStatus != NULL) || InPlayMode())
       
  1462 		{
       
  1463 		// We only support one outstanding request
       
  1464 		// No support for simultaneous play and record in RMdaDevSound
       
  1465 		TRequestStatus *pRequest = &aStatus;
       
  1466 		User::RequestComplete(pRequest, KErrInUse);
       
  1467 		return;
       
  1468 		}
       
  1469 	iClientRecordStatus = &aStatus;
       
  1470 	iClientRecordData = &aData;
       
  1471 	iUnderFlowReportedSinceLastPlayOrRecordRequest = EFalse;
       
  1472 
       
  1473 	iPlayChunk.Close();
       
  1474 	if(!iRecordChunk.Handle())
       
  1475 		{
       
  1476 		//Configure the shared chunk for two buffers with iBufferSize each
       
  1477 		iRecordBufferConfig.iNumBuffers = KRecordMaxSharedChunkBuffers;
       
  1478 		iDeviceBufferLength = KRecordSharedChunkBufferSize; // initial size - resize if needs be
       
  1479 		if (iRecordFormatData.iConverter)
       
  1480 			{
       
  1481 			// if number of channels used differs from request, resize buffer
       
  1482 			// assume we have nice rounded values for buffer.
       
  1483 			if (iRecordFormatData.iActualChannels>iRecordFormatData.iRequestedChannels)
       
  1484 				{
       
  1485 				iDeviceBufferLength *= 2; // will record at stereo and convert to mono 
       
  1486 				}
       
  1487 			else if (iRecordFormatData.iActualChannels<iRecordFormatData.iRequestedChannels)
       
  1488 				{
       
  1489 				iDeviceBufferLength /= 2; // will record at mono and convert to stereo 
       
  1490 				}
       
  1491 			}
       
  1492 		iRecordBufferConfig.iBufferSizeInBytes = iDeviceBufferLength;
       
  1493 		iRecordBufferConfig.iFlags = 0;
       
  1494 		TPckg<TRecordSharedChunkBufConfig> bufferConfigBuf(iRecordBufferConfig);
       
  1495 		TInt error = iRecordSoundDevice.SetBufferChunkCreate(bufferConfigBuf,iRecordChunk);
       
  1496 		if(error == KErrNone)
       
  1497 			{
       
  1498 			iRecordSoundDevice.GetBufferConfig(bufferConfigBuf);
       
  1499 			}
       
  1500 		else
       
  1501 			{
       
  1502 			SoundDeviceError(error);
       
  1503 			return;
       
  1504 			}
       
  1505 		iState = ERecording;
       
  1506 		}		
       
  1507     #ifdef SYMBIAN_SOUNDADAPTER_DEBUG
       
  1508         RDebug::Printf("RMdaDevSound::CBody::RecordData,iBufferOffset[%d]",iBufferOffset);
       
  1509     #endif
       
  1510 
       
  1511 	switch(iState)
       
  1512 		{
       
  1513 		case ENotReady:
       
  1514 			Panic(EBadState);
       
  1515 			break;
       
  1516 
       
  1517 		case EStopped:
       
  1518 		case ERecording:
       
  1519 			// Either idle or recording is in progress, therefore we can issue another request			
       
  1520 			StartRecordRequest();
       
  1521 			break;
       
  1522 			
       
  1523 		case ERecordingPausedInHw:
       
  1524 			// Driver is paused, therefore we can issue a request which will immediately return buffered data
       
  1525 			// or be aborted (in the driver) with KErrCancelled if there is no more data). nb. That KErrCancelled should not be
       
  1526 			// returned to the client because the old RMdaDevSound driver would have completed with KErrNone and zero data length.
       
  1527 			StartRecordRequest();
       
  1528 			break;
       
  1529 
       
  1530 		case ERecordingPausedInSw:
       
  1531 			// Paused in s/w but driver is not paused, therefore can not issue a new request to driver because
       
  1532 			// it would re-start recording.
       
  1533 			// This implies we were paused whilst the h/w was not recording, so there is no buffered data.
       
  1534 			
       
  1535 			// Complete the request with KErrNone and no data.
       
  1536 			iClientRecordData->SetLength(0);
       
  1537 			User::RequestComplete(iClientRecordStatus, KErrNone);
       
  1538 			break;
       
  1539 			
       
  1540 		case EPlaying:
       
  1541 		case EPlayingPausedInHw:
       
  1542 		case EPlayingPausedInSw:
       
  1543 		case EPlayingUnderrun:
       
  1544 			Panic(EBadState);
       
  1545 			break;
       
  1546 			
       
  1547 		default:
       
  1548 			Panic(EBadState);
       
  1549 			break;
       
  1550 		}
       
  1551 	}
       
  1552 	
       
  1553 /**
       
  1554 	Notify client of error.
       
  1555 	
       
  1556 	Note that we continue playing/recording if possible.
       
  1557 	
       
  1558 	We do not maintain information which could map the error back to a particular client play/record request
       
  1559 	and therefore we have to notify the client of error every time it happens.
       
  1560 	
       
  1561 	nb. A client play/record request is completed with KErrNone if it queues ok - All errors are reported via the Notify*Error
       
  1562 	mechanism.
       
  1563  */
       
  1564 void RMdaDevSound::CBody::SoundDeviceError(TInt aError)
       
  1565 	{
       
  1566     #ifdef SYMBIAN_SOUNDADAPTER_DEBUG
       
  1567 	RDebug::Printf("RMdaDevSound::CBody::SoundDeviceError: Error[%d] state %s", aError, iState.Name());
       
  1568     #endif
       
  1569 
       
  1570 	ASSERT(aError != KErrNone);
       
  1571 	
       
  1572 	if(iClientPlayErrorStatus)
       
  1573 		{
       
  1574         #ifdef SYMBIAN_SOUNDADAPTER_DEBUG
       
  1575             RDebug::Printf("RMdaDevSound::CBody::SoundDeviceError Completing iPlayerErrorStatus");
       
  1576         #endif
       
  1577 
       
  1578 		User::RequestComplete(iClientPlayErrorStatus, aError); // nb call also sets iClientPlayErrorStatus to NULL
       
  1579 		}
       
  1580 
       
  1581   	if(iClientRecordErrorStatus)
       
  1582 		{
       
  1583         #ifdef SYMBIAN_SOUNDADAPTER_DEBUG
       
  1584             RDebug::Printf("RMdaDevSound::CBody::SoundDeviceError Completing iClientRecordErrorStatus");
       
  1585         #endif
       
  1586 		User::RequestComplete(iClientRecordErrorStatus, aError); // nb call also sets iClientRecordErrorStatus to NULL
       
  1587 		}
       
  1588 
       
  1589 	return;
       
  1590 	}
       
  1591 
       
  1592 void RMdaDevSound::CBody::NotifyRecordError(TRequestStatus& aStatus)
       
  1593 	{
       
  1594 	aStatus = KRequestPending;
       
  1595 	iClientRecordErrorStatus = &aStatus;
       
  1596 	}
       
  1597 
       
  1598 void RMdaDevSound::CBody::NotifyPlayError(TRequestStatus& aStatus)
       
  1599 	{
       
  1600 	aStatus = KRequestPending;
       
  1601 	iClientPlayErrorStatus = &aStatus;
       
  1602 	}
       
  1603 
       
  1604 void RMdaDevSound::CBody::CancelNotifyPlayError()
       
  1605 	{
       
  1606 	if(iClientPlayErrorStatus)
       
  1607 		{
       
  1608 		User::RequestComplete(iClientPlayErrorStatus, KErrCancel);
       
  1609 		}
       
  1610 	}
       
  1611 
       
  1612 void RMdaDevSound::CBody::CancelNotifyRecordError()
       
  1613 	{
       
  1614 	if(iClientRecordErrorStatus)
       
  1615 		{
       
  1616 		User::RequestComplete(iClientRecordErrorStatus, KErrCancel);
       
  1617 		}
       
  1618 	else
       
  1619 	    {
       
  1620 		#ifdef SYMBIAN_SOUNDADAPTER_DEBUG	
       
  1621         RDebug::Printf("msp BufferEmptied but iClientPlayStatus==NULL");
       
  1622 		#endif
       
  1623 	    }
       
  1624 	}
       
  1625 
       
  1626 void RMdaDevSound::CBody::FlushPlayBuffer()
       
  1627 	{
       
  1628 	#ifdef SYMBIAN_SOUNDADAPTER_DEBUG	
       
  1629     RDebug::Printf("RMdaDevSound::CBody::FlushPlayBuffer calling CancelPlayData");
       
  1630 	#endif	
       
  1631 	CancelPlayData();
       
  1632 	}
       
  1633 
       
  1634 RSoundSc& RMdaDevSound::CBody::PlaySoundDevice()
       
  1635 	{
       
  1636 	return iPlaySoundDevice;
       
  1637 	}
       
  1638 
       
  1639 RSoundSc& RMdaDevSound::CBody::RecordSoundDevice()
       
  1640 	{
       
  1641 	return iRecordSoundDevice;
       
  1642 	}
       
  1643 	
       
  1644 const RMdaDevSound::CBody::TState &RMdaDevSound::CBody::State() const
       
  1645 	{
       
  1646 	return iState;
       
  1647 	}
       
  1648 
       
  1649 
       
  1650 void RMdaDevSound::CBody::BufferFilled(TInt aBufferOffset)
       
  1651 	{
       
  1652     #ifdef SYMBIAN_SOUNDADAPTER_DEBUG	
       
  1653         RDebug::Print(_L("RMdaDevSound::CBody::BufferFilled:"));
       
  1654     #endif	
       
  1655 
       
  1656 	ASSERT(aBufferOffset>=0 || aBufferOffset==KErrCancel);
       
  1657 	ASSERT(iClientRecordData); // request should not get this without
       
  1658 
       
  1659 	if(aBufferOffset==KErrCancel)
       
  1660 		{
       
  1661 		//we can get KErrCancel when we call pause and there is no more data left with the driver
       
  1662 		//we send the empty buffer to the HwDevice, where this should trigger the shutdown mechanism
       
  1663 		iClientRecordData->SetLength(0);
       
  1664 		User::RequestComplete(iClientRecordStatus, KErrNone);
       
  1665 		iClientRecordStatus = NULL;
       
  1666 		return;
       
  1667 		}
       
  1668 		
       
  1669 	iBufferOffset = aBufferOffset;
       
  1670 	//when last buffer is flushed, new driver sometimes gives buffer size of odd number. One of our codecs
       
  1671 	//expects that the buffer size should always be even. Base suggested that we fix in multimedia
       
  1672 	//as it is quite complicated to fix in overthere.
       
  1673 	iBufferLength = iBufferLength & 0xfffffffe;
       
  1674 	TPtr8 dataPtr(iRecordChunk.Base()+ iBufferOffset, iBufferLength, iClientRecordData->MaxLength());
       
  1675 	if (iRecordFormatData.iConverter)
       
  1676 		{
       
  1677 		iRecordFormatData.iConverter->Convert(dataPtr, *iClientRecordData);
       
  1678 		}
       
  1679 	else
       
  1680 		{
       
  1681 		iClientRecordData->Copy(dataPtr);
       
  1682 		}
       
  1683     #ifdef SYMBIAN_SOUNDADAPTER_DEBUG
       
  1684         RDebug::Print(_L("RMdaDevSound::CBody::BufferFilled: BufferOffset[%d] BufferLen[%d]"), iBufferOffset, iBufferLength);
       
  1685     #endif
       
  1686 	if(iBufferOffset >= 0)
       
  1687 		{
       
  1688 		iRecordSoundDevice.ReleaseBuffer(iBufferOffset);
       
  1689 		}
       
  1690 	if(iClientRecordStatus)
       
  1691 		{
       
  1692 		User::RequestComplete(iClientRecordStatus, KErrNone);
       
  1693 		iClientRecordStatus = NULL;
       
  1694 		}
       
  1695 	else
       
  1696 	    {
       
  1697         RDebug::Printf("msp PlayCancelled but iClientPlayStatus==NULL");
       
  1698 	    }
       
  1699 	}
       
  1700 		
       
  1701 /*
       
  1702 	This function is called to notify us that a CPlayer's request has completed and what its status was.
       
  1703 
       
  1704 	It is important to note that:-
       
  1705 	1) RSoundSc driver PlayData requests are guaranteed to complete in order, oldest first
       
  1706 	2) If we are overloaded, it is possible for more than one request to complete before any CPlayer::RunL is ran. In
       
  1707 	this situation the CPlayer::RunL functions, and hence this callback, maybe invoked in non-oldest first order
       
  1708 
       
  1709 	but
       
  1710 
       
  1711 	a) It is impossible for callback for the second oldest CPlayer to occur before the driver request for the oldest has
       
  1712 	been complete (because of 1)
       
  1713 	b) We will always get exactly one callback for every complete request.
       
  1714 
       
  1715 	Therefore this callback notifies us of two subtly separate things:-
       
  1716 
       
  1717 	i) The oldest request has been completed (so we can reduce can increase the bytes played counter by its length
       
  1718 	ii) CPlayer aPlayerIndex is free for re-use
       
  1719 
       
  1720 	but we can not assume that aPlayerIndex is the oldest request, therefore we save the play request lengths outside of
       
  1721 	the CPlayer object.
       
  1722 */
       
  1723 void RMdaDevSound::CBody::PlayRequestHasCompleted(CPlayer *aPlayer, TInt aStatus, TBool aDueToCancelCommand)
       
  1724 	{
       
  1725 	// CPlayer is done so put it on the free queue
       
  1726 	iFreePlayers.Push(aPlayer);
       
  1727 
       
  1728 	TUint32 bytesPlayed = iActivePlayRequestSizes.Pop();
       
  1729 	// Request has finished therefore now timing the following request to simulate bytes played
       
  1730     iStartTime = CurrentTimeInMsec();
       
  1731 	if(aDueToCancelCommand)
       
  1732 	    {
       
  1733         // Callback due to CPlayer::Cancel/DoCancel being called, therefore we
       
  1734         // do not want to update bytes played, process state, report a error or start new players
       
  1735         return;
       
  1736 	    }
       
  1737 	
       
  1738 	// Update iBytesPlayed by the length of the oldest request (which might not be the one that CPlayer was 
       
  1739 	// handling).
       
  1740 	iBytesPlayed += bytesPlayed;
       
  1741 	#ifdef SYMBIAN_SOUNDADAPTER_DEBUG	
       
  1742     RDebug::Printf("PlayRequestHasCompleted increasing iBytesPlayed by %d to %d", bytesPlayed, iBytesPlayed);
       
  1743 	#endif
       
  1744 	
       
  1745     // Process state
       
  1746 	switch(iState)
       
  1747 		{
       
  1748 		case ENotReady:
       
  1749 			Panic(EDeviceNotOpened);
       
  1750 			break;
       
  1751 				
       
  1752 		case EStopped:
       
  1753 			// Will happen if we are doing CancelPlayData processing with active players
       
  1754 			break;
       
  1755 		
       
  1756 		case ERecording:
       
  1757 		case ERecordingPausedInHw:
       
  1758 		case ERecordingPausedInSw:
       
  1759 			Panic(EBadState);
       
  1760 			break;
       
  1761 			
       
  1762 		case EPlaying:
       
  1763 			// Normal situation
       
  1764 			break;
       
  1765 
       
  1766 		case EPlayingPausedInHw: 
       
  1767 			// H/W was/is paused, but there must have been an already complete request that we had not 
       
  1768 			// processed yet.
       
  1769 			// There must be at least one more pending request on h/w, otherwise the h/w would have refused to pause
       
  1770 			// I would expect this be rare, but it happens quite often...
       
  1771             #ifdef SYMBIAN_SOUNDADAPTER_DEBUG
       
  1772 			ASSERT(iActivePlayRequestSizes.Length() != 0);
       
  1773             #endif
       
  1774 			// Need to update the start and pause time to now because we have just updated the actual iBytesPlayed
       
  1775 			// and logically the h/w is paused at the beginning of the next request
       
  1776 			iStartTime = CurrentTimeInMsec();
       
  1777 			iPauseTime = iStartTime;
       
  1778 			break;
       
  1779 		
       
  1780 		case EPlayingPausedInSw:
       
  1781 			// This will happen if there is only a single hw request outstanding, and the hardware has finished it, but the
       
  1782 			// corresponding RunL has not run yet (in which case PausePlayBuffer will have attempted to use h/w pause,
       
  1783 			// but the driver call would have failed, and the state changed to EPlayingPausedInSw).
       
  1784 			iStartTime = CurrentTimeInMsec();
       
  1785 			iPauseTime = iStartTime;
       
  1786 			return;
       
  1787 
       
  1788 		case EPlayingUnderrun:
       
  1789 			// Probably can not happen...
       
  1790 			break;
       
  1791 				
       
  1792 		default:
       
  1793 			Panic(EBadState);
       
  1794 			break;
       
  1795 		}
       
  1796 
       
  1797 
       
  1798 	// If we have an error, report it to the client
       
  1799 	// We NEVER report driver underflow, instead we report KErrUnderflow if we run out of data to pass to driver.
       
  1800 	if( (aStatus != KErrNone) && (aStatus != KErrUnderflow) )
       
  1801 		{
       
  1802 		SoundDeviceError(aStatus);
       
  1803 		}
       
  1804 
       
  1805     // If appropriate start more players
       
  1806 	StartPlayersAndUpdateState();
       
  1807 	return;
       
  1808 	}
       
  1809 
       
  1810 RMdaDevSound::CBody::CPlayer::CPlayer(TInt aPriority, RMdaDevSound::CBody& aParent, TInt aIndex):
       
  1811 	CActive(aPriority), iParent(aParent), iIndex(aIndex), iBufferOffset(-1), iBufferLength(0)
       
  1812 	{
       
  1813 	CActiveScheduler::Add(this);
       
  1814 	}
       
  1815 
       
  1816 RMdaDevSound::CBody::CPlayer::~CPlayer()
       
  1817 	{
       
  1818 	Cancel();
       
  1819 	}
       
  1820 
       
  1821 
       
  1822 void RMdaDevSound::CBody::CPlayer::RunL()
       
  1823 	{
       
  1824     #ifdef SYMBIAN_SOUNDADAPTER_DEBUG	
       
  1825     RDebug::Printf("****RMdaDevSound::CBody::CPlayer(%d)::RunL: Error[%d] ParentState[%s]", 
       
  1826                      iIndex, iStatus.Int(), iParent.State().Name());
       
  1827 	RDebug::Printf("iActivePlayRequestSizes.Length() = %d iFreePlayers.Length() = %d (including this one as active)", 
       
  1828 					iParent.iActivePlayRequestSizes.Length(), 
       
  1829 					iParent.iFreePlayers.Length());
       
  1830     #endif
       
  1831 	iParent.PlayRequestHasCompleted(this, iStatus.Int(), EFalse);
       
  1832 	return;
       
  1833 	}
       
  1834 
       
  1835 TInt RMdaDevSound::CBody::CPlayer::RunError(TInt aError)
       
  1836 	{
       
  1837 	iParent.PlayRequestHasCompleted(this, aError, EFalse);
       
  1838 	return KErrNone;
       
  1839 	}
       
  1840 
       
  1841 void RMdaDevSound::CBody::CPlayer::DoCancel()
       
  1842 	{
       
  1843 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG
       
  1844 	RDebug::Printf("RMdaDevSound::CBody::CPlayer(%d)::DoCancel", iIndex);
       
  1845 #endif
       
  1846 	if(iStatus == KRequestPending)
       
  1847 	    {
       
  1848         // Avoid cancelling requests which have already completed.
       
  1849         // It wastes time, and might provoke a sound driver problem
       
  1850 	    #ifdef SYMBIAN_SOUNDADAPTER_DEBUG	
       
  1851         RDebug::Printf("RMdaDevSound::CBody::CPlayer::DoCancel - would have cancelled driver request");
       
  1852 		#endif
       
  1853         iParent.PlaySoundDevice().Cancel(iStatus);
       
  1854 	    }
       
  1855 	iParent.PlayRequestHasCompleted(this, KErrCancel, ETrue);
       
  1856 	}
       
  1857 
       
  1858 void RMdaDevSound::CBody::CPlayer::PlayData(TUint aChunkOffset, TInt aLength)
       
  1859 	{
       
  1860     #ifdef SYMBIAN_SOUNDADAPTER_DEBUG	
       
  1861 	RDebug::Print(_L("RMdaDevSound::CBody::CPlayer(%d)::PlayData : IsActive[%d]"),
       
  1862 				  iIndex,    IsActive());
       
  1863 	RDebug::Printf("iActivePlayRequestSizes.Length() = %d iFreePlayers.Length() = %d (inc this player)", 
       
  1864 					iParent.iActivePlayRequestSizes.Length(), 
       
  1865 					iParent.iFreePlayers.Length());
       
  1866     #endif	
       
  1867 	
       
  1868 	iBufferOffset = aChunkOffset;
       
  1869 	iBufferLength = aLength;
       
  1870 
       
  1871     //Make sure the length is a multiple of 4 to work around an h6 limitation.
       
  1872 	iBufferLength = iBufferLength & 0xfffffffc;
       
  1873 
       
  1874 	// Issue the RSoundSc request
       
  1875 	iParent.PlaySoundDevice().PlayData(iStatus, iBufferOffset, iBufferLength, EFalse);
       
  1876 	SetActive();
       
  1877 	return;
       
  1878 	}
       
  1879 	
       
  1880 TUint RMdaDevSound::CBody::CPlayer::GetPlayerIndex() const
       
  1881 	{
       
  1882 	return iIndex;
       
  1883 	}
       
  1884 
       
  1885 RMdaDevSound::CBody::CRecorder::CRecorder(TInt aPriority, RMdaDevSound::CBody& aParent):
       
  1886     CActive(aPriority), iParent(aParent), iBufferOffset(-1), iBufferLength(0)
       
  1887     {
       
  1888     CActiveScheduler::Add(this);
       
  1889     }
       
  1890 
       
  1891 RMdaDevSound::CBody::CRecorder::~CRecorder()
       
  1892     {
       
  1893     Cancel();
       
  1894     }
       
  1895 
       
  1896 void RMdaDevSound::CBody::CRecorder::RecordData(TInt& aBufferLength)
       
  1897 	{
       
  1898 	if (!IsActive())
       
  1899 	    {
       
  1900 	    iStatus = KRequestPending;
       
  1901         #ifdef SYMBIAN_SOUNDADAPTER_DEBUG
       
  1902             RDebug::Printf("Recording request: BufferLength[%d]", aBufferLength);
       
  1903         #endif
       
  1904 	    iParent.RecordSoundDevice().RecordData(iStatus, aBufferLength);
       
  1905 	    SetActive();
       
  1906 	    }
       
  1907 	}
       
  1908 
       
  1909 void RMdaDevSound::CBody::CRecorder::RunL()
       
  1910 	{
       
  1911     #ifdef SYMBIAN_SOUNDADAPTER_DEBUG	
       
  1912     RDebug::Printf("****RMdaDevSound::CBody::CRecorder()::RunL: Error[%d] ParentState[%s]", 
       
  1913                      iStatus.Int(), iParent.State().Name());
       
  1914     #endif
       
  1915 
       
  1916 	
       
  1917 	TInt error = iStatus.Int();
       
  1918 	
       
  1919 	if((error >= 0) || (error == KErrCancel))
       
  1920 		{//we can get KErrCancel when we call pause and there is no more data left with the driver
       
  1921 		iParent.BufferFilled(error);
       
  1922 		}
       
  1923 	else 
       
  1924 		{
       
  1925         #ifdef SYMBIAN_SOUNDADAPTER_DEBUG	
       
  1926             RDebug::Print(_L("RMdaDevSound::CBody::CPlayer()::RunL: Error[%d]"), error);
       
  1927         #endif
       
  1928 		iParent.SoundDeviceError(error);
       
  1929 		}
       
  1930 	}
       
  1931 
       
  1932 	
       
  1933 TInt RMdaDevSound::CBody::CRecorder::RunError(TInt aError)
       
  1934     {
       
  1935     iParent.SoundDeviceError(aError);
       
  1936     return KErrNone;
       
  1937     }
       
  1938 
       
  1939 void RMdaDevSound::CBody::CRecorder::DoCancel()
       
  1940     {
       
  1941 #ifdef SYMBIAN_SOUNDADAPTER_DEBUG
       
  1942     RDebug::Printf("RMdaDevSound::CBody::CRecorder()::DoCancel");
       
  1943 #endif
       
  1944     iParent.RecordSoundDevice().Cancel(iStatus);
       
  1945     }
       
  1946 
       
  1947 
       
  1948 // End of file