mmdevicefw/mdf/src/audio/mdasoundadapter/mdasoundadapterbody.cpp
changeset 0 79dd3e2336a0
child 3 28bdc4aca325
equal deleted inserted replaced
-1:000000000000 0:79dd3e2336a0
       
     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