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