os/boardsupport/emulator/emulatorbsp/specific/soundsc_tx.cpp
author sl
Tue, 10 Jun 2014 14:32:02 +0200
changeset 1 260cb5ec6c19
permissions -rw-r--r--
Update contrib.
     1 // Copyright (c) 2006-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 // wins\specific\soundsc_tx.cpp
    15 // Emulator playback functions for the shared chunk sound driver PDD.
    16 // 
    17 //
    18 
    19 /**
    20  @file
    21  @internalTechnology
    22  @prototype
    23 */
    24 
    25 #include "winssoundsc.h"
    26 	
    27 _LIT(KSoundScPddName,"SoundSc.Wins");
    28 _LIT(KTxSoundDriverThreadName,"SoundDriverPlayThread");
    29 const TInt KSoundDriverThreadPriority=26;		// One less than DFC thread 0
    30 
    31 GLDEF_C TInt RateInSamplesPerSecond(TSoundRate aRate)
    32 	{
    33 	switch(aRate)
    34 		{
    35 		case ESoundRate7350Hz: 	return(7350);
    36 		case ESoundRate8000Hz: 	return(8000);
    37 		case ESoundRate8820Hz: 	return(8820);
    38 		case ESoundRate9600Hz: 	return(9600);
    39 		case ESoundRate11025Hz: return(11025);
    40 		case ESoundRate12000Hz: return(12000);
    41 		case ESoundRate14700Hz:	return(14700);
    42 		case ESoundRate16000Hz: return(16000);
    43 		case ESoundRate22050Hz: return(22050);
    44 		case ESoundRate24000Hz: return(24000);
    45 		case ESoundRate29400Hz: return(29400);
    46 		case ESoundRate32000Hz: return(32000);
    47 		case ESoundRate44100Hz: return(44100);
    48 		case ESoundRate48000Hz: return(48000);
    49 		default: return(0);
    50 		};
    51 	}
    52 
    53 // This utility function is used instead of WaitForSingleObject() for places
    54 // where the API call is made from either the driver thread or the play 
    55 // thread.If the call is made from the driver thread, the thread is removed from 
    56 // the kernel for the duration of the WaitForSingleObject() call.
    57 GLDEF_C DWORD WaitForSingleObjectDualThread(HANDLE hHandle,DWORD dwMilliseconds)
    58 	{
    59 	TBool epocThread = (NKern::CurrentContext() == NKern::EInterrupt)?EFalse:ETrue;
    60 	if (epocThread)
    61 		Emulator::Escape();
    62 	DWORD dwRet = WaitForSingleObject(hHandle, dwMilliseconds);
    63 	if (epocThread)
    64 		Emulator::Reenter();
    65 	return dwRet;
    66 	}
    67 	
    68 /**
    69 Remove an audio buffer from the head of the specified buffer list.
    70 Each list holds buffers which are waiting to be transferred. These lists are only used when no audio hardware is present.
    71 @param aList The pending buffer list from which the buffer should be removed (either record or playback).
    72 @return A pointer to the audio buffer reoved or NULL if the list is empty.
    73 */
    74 GLDEF_C WAVEHDR* RemoveFromPendingList(WAVEHDR** aList)
    75 	{
    76 	WAVEHDR* buffer;
    77 	
    78 	buffer=aList[0];
    79 	if (buffer)
    80 		{
    81 		// Move any remaining up one in the list.
    82 		WAVEHDR* b;
    83 		do
    84 			{
    85 			b=aList[1];
    86 			*aList++=b;
    87 			}
    88 		while(b);
    89 		}
    90 	return(buffer);
    91 	}
    92 
    93 /**
    94 Add an audio buffer to the tail of the the specified buffer list.
    95 Each list holds buffers which are waiting to be transferred. These lists are only used when no audio hardware is present.
    96 @param aBuffer The audio buffer to be added.
    97 @param aList The pending buffer list into which the buffer should be added (either record or playback).
    98 */
    99 GLDEF_C void AddToPendingList(WAVEHDR* aBuffer,WAVEHDR** aList)
   100 	{
   101 	while (*aList)
   102 		aList++;
   103 	*aList=aBuffer;
   104 	}
   105 
   106 /**
   107 The thread function for the play windows thread.
   108 This function is always executed in windows thread context.
   109 */
   110 LOCAL_C TUint PlayThreadFunction(DWinsSoundScTxPdd *aSoundPdd)
   111 	{
   112 	aSoundPdd->PlayThread();
   113 	return 0;
   114 	}
   115 	
   116 /**
   117 The waveform output callback function. This can receive the following messages:-
   118 WOM_OPEN when the output device is opened, WOM_CLOSE when the output device is closed,
   119 and WOM_DONE each time a data block play transfer is completed (i.e. completion of waveOutWrite).
   120 This function is always executed in windows thread context.
   121 */
   122 LOCAL_C void CALLBACK WaveOutProc(HWAVEOUT /*hwo*/, UINT uMsg, DWORD dwInstance, DWORD dwParam1, DWORD /*dwParam2*/)
   123 	{
   124 	if (uMsg == WOM_DONE)
   125 		{
   126 		DWinsSoundScTxPdd* pdd = (DWinsSoundScTxPdd*)dwInstance;
   127 		pdd->WaveOutProc((WAVEHDR*)dwParam1);
   128 		}
   129 	}
   130 		
   131 THostLock::THostLock() : iLocked(EFalse)
   132 	{
   133 	Lock();
   134 	}
   135 
   136 THostLock::THostLock(TBool /*aLock*/) : iLocked(EFalse)
   137 	{
   138 	}
   139 
   140 THostLock::~THostLock()
   141 	{
   142 	if (iLocked)
   143 		Unlock();
   144 	}
   145 
   146 void THostLock::Lock()
   147 	{
   148 	__ASSERT_DEBUG(!iLocked, PANIC());
   149 	Emulator::Lock();
   150 	iLocked = ETrue;
   151 	}
   152 
   153 void THostLock::Unlock()
   154 	{
   155 	__ASSERT_DEBUG(iLocked, PANIC());
   156 	Emulator::Unlock();
   157 	iLocked = EFalse;
   158 	}
   159 
   160 TCondHostLock::TCondHostLock() : THostLock(EFalse)
   161 	{
   162 	iEpocThread = (NKern::CurrentContext() == NKern::EInterrupt)?EFalse:ETrue;
   163 	Lock();
   164 	}
   165 
   166 void TCondHostLock::Lock()
   167 	{
   168 	if (iEpocThread)
   169 		THostLock::Lock();	
   170 	}
   171 
   172 void TCondHostLock::Unlock()
   173 	{
   174 	if (iEpocThread)
   175 		THostLock::Unlock();	
   176 	}
   177 	
   178 /**
   179 Standard export function for PDDs. This creates a DPhysicalDevice derived object,
   180 in this case - DWinsSoundScPddFactory.
   181 */
   182 DECLARE_STANDARD_PDD()
   183 	{
   184 	return new DWinsSoundScPddFactory;
   185 	}
   186 	
   187 /**
   188 Constructor for the sound PDD factory class.
   189 */
   190 DWinsSoundScPddFactory::DWinsSoundScPddFactory()
   191 	{	
   192 	__KTRACE_SND(Kern::Printf(">DWinsSoundScPddFactory::DWinsSoundScPddFactory"));
   193 
   194 //	iDfcQ=NULL;
   195 	
   196 	// Support units KSoundScTxUnit0 & KSoundScRxUnit0.
   197     iUnitsMask=(1<<KSoundScRxUnit0)|(1<<KSoundScTxUnit0);
   198 
   199     // Set version number for this device.
   200 	iVersion=RSoundSc::VersionRequired();
   201 	}
   202 
   203 /**
   204 Destructor for the sound PDD factory class.
   205 */
   206 DWinsSoundScPddFactory::~DWinsSoundScPddFactory()
   207 	{
   208 	__KTRACE_SND(Kern::Printf(">DWinsSoundScPddFactory::~DWinsSoundScPddFactory"));
   209 	
   210 	// Destroy the kernel thread.
   211 	if (iDfcQ)
   212 		iDfcQ->Destroy();
   213 	}
   214 	
   215 /**
   216 Second stage constructor for the sound PDD factory class.
   217 This must at least set a name for the driver object.
   218 @return KErrNone if successful, otherwise one of the other system wide error codes.
   219 */
   220 TInt DWinsSoundScPddFactory::Install()
   221 	{
   222 	TInt r=KErrNone;
   223 	if (iDfcQ==NULL)
   224 		{
   225 		// Create a new sound driver DFC queue (and associated kernel thread). 
   226 		r=Kern::DynamicDfcQCreate(iDfcQ,KSoundDriverThreadPriority,KTxSoundDriverThreadName);
   227 		}
   228 	
   229 	if (r==KErrNone)
   230 		{
   231 		r=SetName(&KSoundScPddName); 			// Set the name of the driver object
   232 		}
   233 	
   234 	__KTRACE_SND(Kern::Printf("<DWinsSoundScPddFactory::Install - %d",r));
   235 	return(r);
   236 	}
   237 	
   238 /**
   239 Returns the PDD's capabilities. This is not used by the Symbian OS device driver framework
   240 or by the LDD.
   241 @param aDes A descriptor to write capabilities information into.
   242 */
   243 void DWinsSoundScPddFactory::GetCaps(TDes8& /*aDes*/) const
   244 	{}
   245 
   246 /**
   247 Called by the kernel's device driver framework to check if this PDD is suitable for use
   248 with a logical channel.
   249 This is called in the context of the client thread which requested the creation of a logical
   250 channel - through a call to RBusLogicalChannel::DoCreate().
   251 The thread is in a critical section.
   252 @param aUnit The unit argument supplied by the client to RBusLogicalChannel::DoCreate().
   253 @param aInfo The info argument supplied by the client to RBusLogicalChannel::DoCreate() - not used.
   254 @param aVer The version number of the logical channel which will use this physical channel. 
   255 @return KErrNone if successful, otherwise one of the other system wide error codes.
   256 */
   257 TInt DWinsSoundScPddFactory::Validate(TInt aUnit, const TDesC8* /*aInfo*/, const TVersion& aVer)
   258 	{
   259 	// Check that the version specified is compatible.
   260 	if (!Kern::QueryVersionSupported(RSoundSc::VersionRequired(),aVer))
   261 		return(KErrNotSupported);
   262 	
   263 	// Check the unit number is compatible
   264 	if (aUnit!=KSoundScTxUnit0 && aUnit!=KSoundScRxUnit0)
   265 		return(KErrNotSupported);
   266 		
   267 	return(KErrNone);
   268 	}
   269 
   270 /**
   271 Called by the kernel's device driver framework to create a physical channel object.
   272 This is called in the context of the client thread which requested the creation of a logical
   273 channel - through a call to RBusLogicalChannel::DoCreate().
   274 The thread is in a critical section.
   275 @param aChannel Set by this function to point to the created physical channel object.
   276 @param aUnit The unit argument supplied by the client to RBusLogicalChannel::DoCreate().
   277 @param aInfo The info argument supplied by the client to RBusLogicalChannel::DoCreate().
   278 @param aVer The version number of the logical channel which will use this physical channel. 
   279 @return KErrNone if successful, otherwise one of the other system wide error codes.
   280 */
   281 TInt DWinsSoundScPddFactory::Create(DBase*& aChannel, TInt aUnit, const TDesC8* /*anInfo*/, const TVersion& /*aVer*/)
   282 	{
   283 	__KTRACE_SND(Kern::Printf(">DWinsSoundScPddFactory::Create"));
   284 
   285 	// Create the appropriate PDD channel object.
   286 	TInt r=KErrNoMemory;
   287 	if (aUnit==KSoundScRxUnit0)
   288 		{
   289 		DWinsSoundScRxPdd* pD=new DWinsSoundScRxPdd;
   290 		aChannel=pD;
   291 		if (pD)
   292 			r=pD->DoCreate(this);
   293 		}
   294 		
   295 	else
   296 		{
   297 		DWinsSoundScTxPdd* pD=new DWinsSoundScTxPdd;
   298 		aChannel=pD;
   299 		if (pD)
   300 			r=pD->DoCreate(this);
   301 		}
   302 	return(r);
   303 	}
   304 	
   305 /**
   306 Constructor for the WINS shared chunk playback PDD.
   307 This function is always executed in driver thread context.
   308 */
   309 DWinsSoundScTxPdd::DWinsSoundScTxPdd()
   310 	: iDfc(DWinsSoundScTxPdd::PlayDfc,this,2)
   311 	{		
   312 	__KTRACE_SND(Kern::Printf(">DWinsSoundScTxPdd::DWinsSoundScTxPdd"));
   313 	
   314 //	iVolume=0;
   315 //	iDriverThreadSem=0;
   316 //	iPlayThread=0;
   317 //	iPlayThreadMutex=0;
   318 //	iPlayThreadSem=0;
   319 //	iStopSemaphore=0;
   320 //	iDeathSemaphore=0;
   321 //	iPlayCommand=ESendData;
   322 //	iPlayCommandArg0=0;
   323 //	iPlayCommandArg1=0;
   324 //	iPendingPlay=0;
   325 //	iPlayThreadError=0;
   326 //	iWaveformBufMgr=NULL;
   327 //	iCompletedPlayBufHdrMask=0;	
   328 //	iPlayBufferSize=0;
   329 //	iNoHardware=EFalse;
   330 //	iPlayTimerEvent=0;
   331 //	iTimerID=0;
   332 //	iTimerActive=EFalse;
   333 //	iWinWaveVolume=0;
   334 	}
   335 	
   336 /**
   337 Destructor for the WINS shared chunk playback PDD.
   338 This function is always executed in driver thread context.
   339 */
   340 DWinsSoundScTxPdd::~DWinsSoundScTxPdd()
   341 	{
   342 	// If the Windows thread started up successfully, signal it to shut down and wait for it to do so
   343 	if (iPlayThreadRunning)
   344 		{
   345 		// Signal the windows thread to close down the play device and exit the windows thread.
   346 		iDeathSemaphore = CreateSemaphore(NULL, 0, 2, NULL);
   347 		PlayThreadCommand(EExit);
   348 
   349 		// Wait for the play thread to terminate.
   350 		if (iDeathSemaphore)
   351 			{
   352 			Emulator::Escape();
   353 			WaitForSingleObject(iDeathSemaphore, INFINITE); 
   354 			Emulator::Reenter();
   355 
   356 			__HOST_LOCK;
   357 			CloseHandle(iDeathSemaphore);
   358 			}
   359 		}
   360 
   361 	if (iPlayTimerEvent)
   362 		CloseHandle(iPlayTimerEvent); 
   363 	if (iPlayThreadSem)
   364 		CloseHandle(iPlayThreadSem); 
   365 	if (iPlayThread)
   366 		CloseHandle(iPlayThread);
   367 	if (iDriverThreadSem)
   368 		CloseHandle(iDriverThreadSem); 	
   369 	
   370 	if (iWaveformBufMgr)
   371 		delete iWaveformBufMgr;
   372 	}
   373 	
   374 /**
   375 Second stage constructor for the WINS shared chunk playback PDD.
   376 Note that this constructor is called before the second stage constructor for the LDD so it is not
   377 possible to call methods on the LDD here.
   378 This function is always executed in driver thread context.
   379 @param aPhysicalDevice A pointer to the factory class that is creating this PDD.
   380 @return KErrNone if successful, otherwise one of the other system wide error codes.
   381 */	
   382 TInt DWinsSoundScTxPdd::DoCreate(DWinsSoundScPddFactory* aPhysicalDevice)
   383 	{
   384 	__KTRACE_SND(Kern::Printf(">DWinsSoundScTxPdd::DoCreate"));
   385 
   386 	iPhysicalDevice=aPhysicalDevice;
   387 	
   388 	// Set up the correct DFC queue.
   389 	iDfc.SetDfcQ(iPhysicalDevice->iDfcQ);
   390 	
   391 	SetCaps();	// Setup the capabilities of this device.
   392 	
   393 	// Setup the default audio configuration
   394 	iSoundConfig.iChannels=2;
   395 	iSoundConfig.iRate=ESoundRate48000Hz;
   396 	iSoundConfig.iEncoding=ESoundEncoding16BitPCM;
   397 	iSoundConfig.iDataFormat=ESoundDataFormatInterleaved;
   398 	
   399 	__HOST_LOCK;
   400 
   401 	// Query the waveform device capabilities using the default device identifier in order
   402 	// to check if there is a functioning waveform device present.  Note that some versions of
   403 	// Windows (such as Windows Server 2003) will actually return MMSYSERR_NOERROR when this is
   404 	// called, even if there is no waveform device present, so we have a further check in
   405 	// when waveOutOpen() is called
   406 	WAVEOUTCAPS waveOutCaps;
   407 	MMRESULT res = waveOutGetDevCaps(WAVE_MAPPER,&waveOutCaps,sizeof(WAVEOUTCAPS));
   408 #ifdef FORCE_NO_HARDWARE
   409 	res=MMSYSERR_NOERROR+1;
   410 #endif
   411 	if (res != MMSYSERR_NOERROR)
   412 		iNoHardware = ETrue;
   413 	
   414 	__HOST_LOCK_OFF;
   415 	
   416 	// Create the windows waveform audio buffer manager.
   417 	iWaveformBufMgr=new TWaveformBufMgr(ESoundDirPlayback,!iNoHardware);
   418 	if (!iWaveformBufMgr)
   419 		return(KErrNoMemory);
   420 	
   421 	// Create the driver thread semaphore.
   422 	iDriverThreadSem = CreateSemaphore(NULL,0,0x7fffffff,NULL);
   423 	if (!iDriverThreadSem)
   424 		return(KErrNoMemory);
   425 	
   426 	// Create the play windows thread.
   427 	if ((iPlayThread=CreateWin32Thread(EThreadEvent,(LPTHREAD_START_ROUTINE)PlayThreadFunction,(void *)this, FALSE))==NULL)
   428 		return(KErrNoMemory);
   429 	SetThreadPriority(iPlayThread,THREAD_PRIORITY_HIGHEST);
   430 	__ASSERT_ALWAYS(ResumeThread(iPlayThread) != 0xffffffff, PANIC()); // Windows Unexpected Error
   431 
   432 	// Wait to be notified of successful thread initialization
   433 	Emulator::Escape();
   434 	WaitForSingleObject(iDriverThreadSem,INFINITE);
   435 	Emulator::Reenter();
   436 
   437 	// If the Windows thread started up successfully, indicate this fact so that when shutting down we know
   438 	// to signal to the thread to exit
   439 	if (iPlayThreadError == KErrNone)
   440 		iPlayThreadRunning = ETrue;
   441 
   442 	return(iPlayThreadError);
   443 	}
   444 
   445 /**
   446 Called from the LDD to return the DFC queue to be used by this device.
   447 This function is always executed in driver thread context.
   448 @return The DFC queue to use.
   449 */	
   450 TDfcQue* DWinsSoundScTxPdd::DfcQ(TInt /*aUnit*/)
   451 	{
   452 	return(iPhysicalDevice->iDfcQ);
   453 	}
   454 
   455 /** 
   456 Called from the LDD to return the shared chunk create information to be used by this device.
   457 This function is always executed in driver thread context.
   458 @param aChunkCreateInfo A chunk create info. object to be to be filled with the settings
   459 						required for this device.
   460 */		
   461 void DWinsSoundScTxPdd::GetChunkCreateInfo(TChunkCreateInfo& aChunkCreateInfo)
   462 	{
   463 	__KTRACE_SND(Kern::Printf(">DWinsSoundScTxPdd::GetChunkCreateInfo"));
   464 
   465 	aChunkCreateInfo.iType=TChunkCreateInfo::ESharedKernelMultiple;
   466 	aChunkCreateInfo.iMapAttr=0;
   467 	aChunkCreateInfo.iOwnsMemory=ETrue; 				// Using RAM pages.
   468 	aChunkCreateInfo.iDestroyedDfc=NULL; 				// No chunk destroy DFC.
   469 	}
   470 	
   471 /**
   472 Called from the LDD to return the capabilities of this device.
   473 This function is always executed in driver thread context.
   474 @param aCapsBuf A packaged TSoundFormatsSupportedV02 object to be filled with the play
   475 				capabilities of this device. This descriptor is in kernel memory and can be accessed directly.
   476 @see TSoundFormatsSupportedV02.
   477 */
   478 void DWinsSoundScTxPdd::Caps(TDes8& aCapsBuf) const
   479 	{
   480 	__KTRACE_SND(Kern::Printf(">DWinsSoundScTxPdd::Caps"));
   481 	
   482 	// Copy iCaps back.
   483 	TPtrC8 ptr((const TUint8*)&iCaps,sizeof(iCaps));
   484 	aCapsBuf.FillZ(aCapsBuf.MaxLength());
   485 	aCapsBuf=ptr.Left(Min(ptr.Length(),aCapsBuf.MaxLength()));	
   486 	}
   487 	
   488 /**
   489 Called from the LDD to return the maximum transfer length in bytes that this device can support in a single data transfer.
   490 @return The maximum transfer length in bytes.
   491 */
   492 TInt DWinsSoundScTxPdd::MaxTransferLen() const
   493 	{
   494 	return(KWinsMaxAudioTransferLen);		// 32K
   495 	}		
   496 	
   497 /**
   498 Called from the LDD to power up the sound device.
   499 This function is always executed in driver thread context.
   500 @return KErrNone if successful, otherwise one of the other system wide error codes.
   501 */
   502 TInt DWinsSoundScTxPdd::PowerUp()
   503 	{
   504 	return(KErrNone);
   505 	}
   506 	
   507 /**
   508 Called from the LDD to configure or reconfigure the device using the the configuration supplied.
   509 This function is always executed in driver thread context.
   510 @param aConfigBuf A packaged TCurrentSoundFormatV02 object which contains the new configuration settings.
   511 				  This descriptor is in kernel memory and can be accessed directly.
   512 @return KErrNone if successful, otherwise one of the other system wide error codes.
   513 @see TCurrentSoundFormatV02.
   514 */	
   515 TInt DWinsSoundScTxPdd::SetConfig(const TDesC8& aConfigBuf)
   516 	{
   517 	__KTRACE_SND(Kern::Printf(">DWinsSoundScTxPdd::SetConfig"));
   518 
   519 	// Cannot change the configuration while the device is open and playing. (LDD should prevent
   520 	// this anyway but better safe than sorry).
   521 	if (iPlayDeviceHandle)
   522 		return(KErrInUse);
   523 	
   524 	// Save the current settings so we can restore them if there is a problem with the new ones.
   525 	TCurrentSoundFormatV02 saved=iSoundConfig;
   526 	
   527 	// Read the new configuration from the LDD.
   528 	TPtr8 ptr((TUint8*)&iSoundConfig,sizeof(iSoundConfig));
   529 	Kern::InfoCopy(ptr,aConfigBuf);
   530 	
   531 	// Open the play device with the new settings to check they are supported. Then close it
   532 	// again - don't leave it open yet.
   533 	TInt r = CreatePlayDevice(ESetConfig);
   534 	if (r==KErrNone)
   535 		ClosePlayDevice();
   536 	else
   537 		iSoundConfig=saved;	// Restore the previous settings
   538 	
   539 	return(r);
   540 	}
   541 	
   542 const TInt KdBToLinear[KSoundMaxVolume+1] = 
   543 	{
   544 	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
   545 	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
   546 	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
   547 	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
   548 	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
   549 	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
   550 	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
   551 	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
   552 	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
   553 	0x0000,0x0000,0x0000,0x0101,0x0101,0x0101,0x0101,0x0101,0x0101,0x0101,0x0101,0x0101,0x0101,0x0101,0x0101,0x0101,
   554 	0x0101,0x0101,0x0101,0x0101,0x0101,0x0101,0x0202,0x0202,0x0202,0x0202,0x0202,0x0202,0x0202,0x0202,0x0202,0x0303,
   555 	0x0303,0x0303,0x0303,0x0303,0x0303,0x0404,0x0404,0x0404,0x0404,0x0505,0x0505,0x0505,0x0505,0x0606,0x0606,0x0606,
   556 	0x0707,0x0707,0x0808,0x0808,0x0909,0x0909,0x0a0a,0x0a0a,0x0b0b,0x0b0b,0x0c0c,0x0d0d,0x0e0e,0x0e0e,0x0f0f,0x1010,
   557 	0x1111,0x1212,0x1313,0x1414,0x1616,0x1717,0x1818,0x1a1a,0x1b1b,0x1d1d,0x1e1e,0x2020,0x2222,0x2424,0x2626,0x2929,
   558 	0x2b2b,0x2e2e,0x3030,0x3333,0x3636,0x3939,0x3d3d,0x4040,0x4444,0x4848,0x4c4c,0x5151,0x5656,0x5b5b,0x6060,0x6666,
   559 	0x6c6c,0x7272,0x7979,0x8080,0x8888,0x9090,0x9898,0xa2a2,0xabab,0xb5b5,0xc0c0,0xcbcb,0xd7d7,0xe4e4,0xf2f2,0xffff,	
   560 	};	
   561 	
   562 /**
   563 Called from the LDD to set the play volume.
   564 This function is always executed in driver thread context.
   565 @param aVolume The play volume to be set - a value in the range 0 to 255. The value 255 equates
   566 	to the maximum volume and each value below this equates to a 0.5dB step below it.
   567 @return KErrNone if successful, otherwise one of the other system wide error codes.
   568 */
   569 TInt DWinsSoundScTxPdd::SetVolume(TInt aVolume)
   570 	{
   571 	__KTRACE_SND(Kern::Printf(">DWinsSoundScTxPdd::SetVolume"));
   572 	
   573 	// The documentation for waveOutSetVolume() says: "Volume settings are interpreted logarithmically" but evidence suggests these are really
   574 	// interpreted linearly. Hence, use a lookup table to convert from dB to linear. At the same time convert from an 8-bit wide range to a 
   575 	// 16-bit wide range.
   576 	iVolume=KdBToLinear[aVolume];
   577 	
   578 	// Only update the volume on the output device itself if this is currently open.
   579 	if (iPlayDeviceHandle)
   580 		waveOutSetVolume(iPlayDeviceHandle, iVolume | (iVolume << 16));	
   581 	return(KErrNone);
   582 	}	
   583 		
   584 /**
   585 Called from the LDD to prepare the audio device for playback.
   586 This function is always executed in driver thread context.
   587 @return KErrNone if successful, otherwise one of the other system wide error codes.
   588 */
   589 TInt DWinsSoundScTxPdd::StartTransfer()
   590 	{
   591 	__KTRACE_SND(Kern::Printf(">DWinsSoundScTxPdd::StartTransfer"));
   592 	
   593 	// Convert the enum representing the current sample rate into an integer
   594 	TInt samplesPerSecond=RateInSamplesPerSecond(iSoundConfig.iRate);
   595 	if (samplesPerSecond==0)
   596 		return(KErrNotSupported);
   597 
   598 	// Now convert the sample rate into the number of bytes per second and save for later use
   599 	iBytesPerSecond=samplesPerSecond;
   600 	if (iSoundConfig.iChannels==2)
   601 		iBytesPerSecond*=2;
   602 	if (iSoundConfig.iEncoding==ESoundEncoding16BitPCM)
   603 		iBytesPerSecond*=2;
   604 
   605 	// Open the play device with the current settings.
   606 	iPendingPlay=0;
   607 	iCompletedPlayBufHdrMask=0;					// Reset the completion status mask
   608 	TInt r = CreatePlayDevice(EStartTransfer);
   609 	return(r);
   610 	}
   611 	
   612 /**
   613 Called from the LDD to initiate the playback of a portion of data to the audio device. 
   614 When the transfer is complete, the PDD signals this event using the LDD function PlayCallback().
   615 This function is always executed in driver thread context.
   616 @param aTransferID A value assigned by the LDD to allow it to uniquely identify a particular transfer fragment.
   617 @param aLinAddr The linear address within the shared chunk of the start of the data to be played.
   618 @param aPhysAddr The physical address within the shared chunk of the start of the data to be played.
   619 @param aNumBytes The number of bytes to be played. 
   620 @return KErrNone if the transfer has been initiated successfully;
   621   		KErrNotReady if the device is unable to accept the transfer for the moment;
   622 		otherwise one of the other system-wide error codes.
   623 */
   624 TInt DWinsSoundScTxPdd::TransferData(TUint aTransferID,TLinAddr aLinAddr,TPhysAddr /*aPhysAddr*/,TInt aNumBytes)
   625 	{
   626 	__KTRACE_SND(Kern::Printf(">DWinsSoundScTxPdd::TransferData(ID:%xH)",aTransferID));
   627 	
   628 	// Check that we can accept the request
   629 	if (aNumBytes>KWinsMaxAudioTransferLen)
   630 		return(KErrArgument);
   631 	if (iPendingPlay>=iWaveformBufMgr->iNumWaveformBufs)	// LDD may issue multiple data transfers per buffer.
   632 		return(KErrNotReady);
   633 	
   634 	// Signal the windows thread to initiate the playback of a buffers worth of data to the waveout device.
   635 	iPendingPlay++;
   636 	PlayThreadCommand(ESendData,aTransferID,aLinAddr,aNumBytes);
   637 	
   638 	// Although the windows thread runs at a higher priority, its not safe to assume we will always get pre-empted at this
   639 	// point while the the higher priority thread processes and completes the request. Instead we need to wait until it
   640 	// signals back completion of the command.
   641 	Emulator::Escape();
   642 	WaitForSingleObject(iDriverThreadSem,INFINITE);
   643 	Emulator::Reenter();	
   644 
   645 	return(iPlayThreadError);
   646 	}	
   647 	
   648 /**
   649 Called from the LDD to terminate the playback of a data to the device and to release any resources necessary for playback.
   650 This is called soon after the last pending play request from the client has been completed. Once this function had been
   651 called, the LDD will not issue any further TransferData() commands without first issueing a StartTransfer() command.
   652 This function is always executed in driver thread context.
   653 */	
   654 void DWinsSoundScTxPdd::StopTransfer()
   655 	{
   656 	__KTRACE_SND(Kern::Printf(">DWinsSoundScTxPdd::StopTransfer"));
   657 	
   658 	// Signal the windows thread to stop and close the device.
   659 	iStopSemaphore = CreateSemaphore(NULL, 0, 1, NULL);
   660 	PlayThreadCommand(EStop);
   661 
   662 	// Wait for the windows thread to stop and close the device.
   663 	if (iStopSemaphore)
   664 		{
   665 		Emulator::Escape();
   666 		WaitForSingleObject(iStopSemaphore, INFINITE);  
   667 		Emulator::Reenter();
   668 
   669 		__HOST_LOCK;
   670 		CloseHandle(iStopSemaphore);
   671 		iStopSemaphore = NULL;
   672 		}
   673 	iPendingPlay=0;
   674 	iCompletedPlayBufHdrMask=0;					// Reset the completion status mask
   675 	
   676 	// Make sure the DFC is not queued.
   677 	iDfc.Cancel();
   678 	}
   679 	
   680 /**
   681 Called from the LDD to halt the playback of data to the sound device but not to release any resources necessary for
   682 playback.
   683 If possible, any active transfer should be suspended in such a way that it can be resumed later - starting from next
   684 sample following the one last played.
   685 This function is always executed in driver thread context.
   686 @return KErrNone if successful, otherwise one of the other system wide error codes.
   687 */
   688 TInt DWinsSoundScTxPdd::PauseTransfer()
   689 	{
   690 	__KTRACE_SND(Kern::Printf(">DWinsSoundScTxPdd::PauseTransfer"));
   691 	
   692 	// Signal the windows thread to pause playback on the waveout device.
   693 	PlayThreadCommand(EPause);
   694 	    
   695 	return(KErrNone);
   696 	}
   697 	
   698 /**
   699 Called from the LDD to resume the playback of data to the sound device following a request to halt playback.
   700 If possible, any transfer which was active when the device was halted should be resumed - starting from next sample
   701 following the one last played. Once complete, it should be reported using PlayCallback()
   702 as normal. 
   703 This function is always executed in driver thread context.
   704 @return KErrNone if successful, otherwise one of the other system wide error codes.
   705 */
   706 TInt DWinsSoundScTxPdd::ResumeTransfer()
   707 	{
   708 	__KTRACE_SND(Kern::Printf(">DWinsSoundScTxPdd::ResumeTransfer"));
   709 	
   710 	// Signal the windows thread to resume playback on the waveout device.
   711 	PlayThreadCommand(EResume);
   712 	
   713 	return(KErrNone);
   714 	}
   715 			
   716 /**
   717 Called from the LDD to power down the sound device.
   718 This function is always executed in driver thread context.
   719 */
   720 void DWinsSoundScTxPdd::PowerDown()
   721 	{}
   722 	
   723 /**
   724 Called from the LDD to handle a custom configuration request.
   725 @param aFunction A number identifying the request.
   726 @param aParam A 32-bit value passed to the driver. Its meaning depends on the request.
   727 @return KErrNone if successful, otherwise one of the other system wide error codes.
   728 */
   729 TInt DWinsSoundScTxPdd::CustomConfig(TInt /*aFunction*/,TAny* /*aParam*/)
   730 	{
   731 	return(KErrNotSupported);
   732 	}
   733 
   734 /**
   735 Called from the LDD to find out how many microseconds of data have been played.  This is called
   736 in the context of the DFC thread.
   737 @param aTimeTransferred	A reference to a variable into which to place the number of microseconds of audio.
   738 @param aStatus			The current status of this channel
   739 @return KErrNone if time is valid or KErrNotSupported.
   740 */
   741 TInt DWinsSoundScTxPdd::TimeTransferred(TInt64& aTimePlayed, TInt /*aStatus*/)
   742 	{
   743 	TInt r=KErrGeneral;
   744 	TInt64 ms=0;
   745 
   746 	MMTIME time;
   747 	time.wType=TIME_BYTES;
   748 
   749 	if(iPlayDeviceHandle == 0)
   750 		{
   751 		// Have not started playback yet, or have stopped.
   752 		aTimePlayed = 0;
   753 		return KErrNone;
   754 		}
   755 
   756 	// If no hardware is present then we need to simulate the amount of time that has passed during
   757 	// playback.  The # of microseconds can be found in the iSimulatedUSecPlayed member, but this is
   758 	// only updated when the emulation timer triggers.  To improve the accuracy of the time returned,
   759 	// we use the Windows system timer to determine the # of milliseconds that have passed since the
   760 	// last time the timer triggered
   761 	if(iNoHardware)
   762 		{
   763 		// Determine the # of milliseconds that have passed since the last timer triggered
   764 		DWORD currentTime = timeGetTime();
   765 		DWORD timeSinceLastEvent = (currentTime - iLastTimerEventTime);
   766 
   767 		// If playback is paused then the Windows system timer will continue, so take this into
   768 		// account and subtract the # of milliseconds we have been paused for
   769 		if (iPauseTime)
   770 			timeSinceLastEvent -= (currentTime - iPauseTime);
   771 
   772 		// Clamp the resulting value to the duration of the timer, to prevent the millisecond count
   773 		// going backwards if Windows is busy and latency becomes an issue
   774 		if (timeSinceLastEvent > iSimulatedMsecDuration)
   775 			timeSinceLastEvent = iSimulatedMsecDuration;
   776 
   777 		// Now we know the value of the time passed down to a millisecond accuracy
   778 		aTimePlayed = (iSimulatedUSecPlayed + (timeSinceLastEvent * 1000));
   779 		return KErrNone;
   780 		}
   781 	
   782 	// Get the number of bytes played by the Windows audio system
   783 	if (waveOutGetPosition(iPlayDeviceHandle,&time,sizeof(time))==MMSYSERR_NOERROR)
   784 		{
   785 		// If requesting the number of bytes played is not supported, wType will be
   786 		// changed to what was actually returned, so check for this and don't continue
   787 		// if we got anything other than bytes
   788 		if (time.wType==TIME_BYTES)
   789 			{
   790 			// It's all good.  Convert the number of bytes played into microseconds and return it
   791 			ms=((time.u.cb/iBytesPerSecond)*1000);
   792 			TUint remainder=(time.u.cb%iBytesPerSecond);
   793 			ms+=((remainder*1000)/iBytesPerSecond);
   794 			ms*=1000;
   795 			aTimePlayed=ms;
   796 			r=KErrNone;
   797 			}
   798 		}
   799 
   800 	return(r);
   801 	}
   802 
   803 /** 
   804 Constructor for the windows playback waveform audio buffer abstraction.
   805 */
   806 TWaveformAudioBuf::TWaveformAudioBuf()
   807 	{
   808 	memclr(&iBufHdr,sizeof(WAVEHDR));
   809 	iIsPrepared=EFalse;
   810 	iIsInUse=EFalse;
   811 	iWaveformBufMgr=NULL;
   812 	iBufNum=0;
   813 	}
   814 		
   815 /** 
   816 Prepare the waveform audio buffer for playback or record.
   817 @param aBufAddr A pointer to the address of the waveform buffer.
   818 @param aBufLength The length in bytes of the waveform buffer.
   819 @param aDeviceHandle The handle to the waveform audio device.
   820 */		
   821 void TWaveformAudioBuf::Prepare(char* aBufAddr,TInt aBufLength,TInt aDeviceHandle)
   822 	{
   823 	iBufHdr.lpData = aBufAddr;
   824 	iBufHdr.dwBufferLength = aBufLength;
   825 	iBufHdr.dwBytesRecorded = 0;
   826     iBufHdr.dwUser = iBufNum;
   827     if (iWaveformBufMgr->iDirection==ESoundDirPlayback)
   828 		iBufHdr.dwFlags = WHDR_DONE;					// Initialise all to done so we can check for underflow.
   829 	else
   830 		iBufHdr.dwFlags = 0;
   831     iBufHdr.dwLoops = 0;
   832     iBufHdr.lpNext = NULL;
   833 	iBufHdr.reserved = 0;
   834 
   835 	if (iWaveformBufMgr->iIsHardware)
   836 		{
   837 		if (iWaveformBufMgr->iDirection==ESoundDirPlayback)
   838 			DoPrepareOut((HWAVEOUT)aDeviceHandle);
   839 		else
   840 			DoPrepareIn((HWAVEIN)aDeviceHandle);
   841 		}
   842 	iIsPrepared=ETrue;
   843 	iIsInUse=EFalse;	
   844 	}
   845 	
   846 /**
   847 Cleanup the preparation performed when the waveform audio buffer was prepared for playback or record.
   848 @param aDeviceHandle The handle to the waveform audio device.
   849 */	
   850 void TWaveformAudioBuf::Unprepare(TInt aDeviceHandle)
   851 	{
   852 	if (iWaveformBufMgr->iIsHardware && iIsPrepared)
   853 		{
   854 		if (iWaveformBufMgr->iDirection==ESoundDirPlayback)
   855 			DoUnprepareOut((HWAVEOUT)aDeviceHandle);
   856 		else
   857 			DoUnprepareIn((HWAVEIN)aDeviceHandle);
   858 		}
   859 	iIsPrepared=EFalse;	
   860 	iIsInUse=EFalse;
   861 	}	
   862 
   863 /** 
   864 Prepare the waveform audio buffer for playback.
   865 @param aPlayDeviceHandle The handle to the waveform audio output device.
   866 */		
   867 void TWaveformAudioBuf::DoPrepareOut(HWAVEOUT aPlayDeviceHandle)
   868 	{
   869 	MMRESULT res = waveOutPrepareHeader(aPlayDeviceHandle,&iBufHdr,sizeof(WAVEHDR));
   870 	__KTRACE_SND(Kern::Printf("   waveOutPrepareHeader(BufNo:%d Pos:%x Len:%d)-%d",iBufNum,iBufHdr.lpData,iBufHdr.dwBufferLength,res));
   871 	__ASSERT_ALWAYS(res==MMSYSERR_NOERROR,Kern::Fault("DWinsSoundScTxPddWOPH", res)); //WaveOutPrepareHeader error.
   872 	}
   873 	
   874 /**
   875 Cleanup the preparation performed when the waveform audio buffer was prepared for playback.
   876 @param aPlayDeviceHandle The handle to the waveform audio output device.
   877 */	
   878 void TWaveformAudioBuf::DoUnprepareOut(HWAVEOUT aPlayDeviceHandle)
   879 	{
   880 	MMRESULT res = waveOutUnprepareHeader(aPlayDeviceHandle,&iBufHdr,sizeof(WAVEHDR));
   881 	__KTRACE_SND(Kern::Printf("   waveOutUnprepareHeader(BufNo:%d)-%d",iBufNum,res));
   882 	__ASSERT_ALWAYS(res==MMSYSERR_NOERROR,Kern::Fault("DWinsSoundScTxPddWOUH",res)); //WaveOutUnprepareHeader error.	
   883 	}
   884 
   885 /**
   886 Constructor for the waveform audio buffer manager. 
   887 */
   888 TWaveformBufMgr::TWaveformBufMgr(TSoundDirection aDirection,TBool aIsHardware)
   889 	: iDirection(aDirection), iIsHardware(aIsHardware)
   890 	{
   891 	iWaveformAudioBuf=NULL;
   892 	iNumWaveformBufs=0;
   893 	iWaveformBufSize=0;
   894 	iPendingBufList=NULL;
   895 	}
   896 	
   897 /**
   898 Destructor for the waveform audio buffer manager.
   899 */	
   900 TWaveformBufMgr::~TWaveformBufMgr()
   901 	{
   902 	if (iWaveformAudioBuf)
   903 		delete[] iWaveformAudioBuf;
   904 	if (iPendingBufList)
   905 		delete iPendingBufList;
   906 	}	
   907 
   908 /*
   909 Re-allocate the number of waveform audio buffers that are available for data transfer according to the
   910 current shared chunk configuration. Then, for each buffer that exists within the shared chunk, prepare one of the waveform audio
   911 buffers just created so that it is aligned with the shared chunk buffer. 
   912 @param aBufConfig A buffer configuration object specifying the geometry of the current shared chunk buffer configuration.
   913 @param aChunkBase The address in the kernel process for the start of the shared chunk.
   914 @param aDeviceHandle The handle to the waveform audio device.
   915 @return KErrNone if successful, otherwise one of the other system wide error codes. 
   916 @pre The thread must be in a critical section.    
   917 */	
   918 TInt TWaveformBufMgr::ReAllocAndUpdate(TSoundSharedChunkBufConfig* aBufConfig,TLinAddr aChunkBase,TInt aDeviceHandle)
   919 	{
   920 	__KTRACE_SND(Kern::Printf(">TWaveformBufMgr::ReAllocAndUpdate"));
   921 	
   922 	// Check if the number of windows waveform audio buffers that are required has changed.
   923 	TInt required=Max(aBufConfig->iNumBuffers,KMinWaveHdrBufCount);
   924 	if (iNumWaveformBufs != required)
   925 		{
   926 		// The number has changed. First, re-allocate the required number of windows waveform data blocks.
   927 		if (iWaveformAudioBuf)
   928 			{			
   929 			delete[] iWaveformAudioBuf;
   930 			iWaveformAudioBuf=NULL;
   931 			}
   932 			
   933 		// If we are emulating an audio device then delete any pending buffer list previously created.	
   934 		if (!iIsHardware && iPendingBufList)
   935 			{
   936 			delete iPendingBufList;
   937 			iPendingBufList=NULL;
   938 			}
   939 		iNumWaveformBufs = 0;	
   940 		
   941 		iWaveformAudioBuf=new TWaveformAudioBuf[required];
   942 		if (!iWaveformAudioBuf)
   943 			return(KErrNoMemory);
   944 		for (TInt i=0; i<required ; i++)
   945 			{
   946 			iWaveformAudioBuf[i].SetWaveformBufMgr(this);
   947 			iWaveformAudioBuf[i].SetBufNum(i);
   948 			}
   949 			
   950 		// If we are emulating an audio device then allocate a new pending buffer list.	
   951 		if (!iIsHardware)
   952 			{
   953 			iPendingBufList=(WAVEHDR**)Kern::AllocZ((required+1)*sizeof(WAVEHDR*));
   954 			if (!iPendingBufList)
   955 				return(KErrNoMemory);
   956 			}
   957 		iNumWaveformBufs = required;		
   958 		}
   959 	
   960 	// The most common situation is that request start offsets coincide with the start of one of the
   961 	// shared chunk buffers. Hence, begin by preparing a windows waveform audio buffer for each shared chunk
   962 	// buffer - aligned with this start address.				
   963 	TInt* bufOffsetList=&aBufConfig->iBufferOffsetListStart;	// The buffer offset list.
   964 	for (TInt i=0; i<aBufConfig->iNumBuffers ; i++) 
   965 		{
   966 		char* bufAddr=(char*)(aChunkBase+bufOffsetList[i]);
   967 		iWaveformAudioBuf[i].Prepare(bufAddr,aBufConfig->iBufferSizeInBytes,aDeviceHandle); 
   968 		}
   969 	iWaveformBufSize=aBufConfig->iBufferSizeInBytes;		
   970 	return(KErrNone);
   971 	}
   972 
   973 /**
   974 Acquire an appropriate waveform audio buffer to be used either to send a data block to the waveform output device or
   975 receive a data block from the waveform input device.
   976 This function is always executed in windows thread context.
   977 @param aStartAddress A pointer to the address of the data block to be played/recorded.
   978 @param aBufLength The length in bytes of the data block to be played/recorded.
   979 @param aDeviceHandle The handle to the waveform audio device.
   980 @return A pointer to an appropriate waveform audio buffer to be used to transfer the data block.
   981 */
   982 TWaveformAudioBuf* TWaveformBufMgr::AcquireBuf(char* aStartAddress,TInt aBufLength,TInt aDeviceHandle)
   983 	{
   984 	// See if there's a appropriate waveform audio buffer already prepared. We only need to worry about the start address,
   985 	// the length can be adjusted later if necessary.
   986 	TInt i;
   987 	for (i=0; i<iNumWaveformBufs ; i++)
   988 		{
   989 		if (iWaveformAudioBuf[i].iIsPrepared && iWaveformAudioBuf[i].iBufHdr.lpData==aStartAddress && !iWaveformAudioBuf[i].iIsInUse)
   990 			break;
   991 		}
   992 	if (i>=iNumWaveformBufs)
   993 		{
   994 		// None already prepared which are appropriate so prepare one now. See if there are any not yet prepared.
   995 		for (i=0; i<iNumWaveformBufs ; i++)
   996 			{
   997 			if (!iWaveformAudioBuf[i].iIsPrepared)
   998 				{
   999 				iWaveformAudioBuf[i].Prepare(aStartAddress,aBufLength,aDeviceHandle);
  1000 				break;
  1001 				}
  1002 			}
  1003 			
  1004 		// All are prepared already so we need to re-prepare one specially.
  1005 		if (i>=iNumWaveformBufs)
  1006 			{
  1007 			for (i=0; i<iNumWaveformBufs ; i++)
  1008 				{
  1009 				if (!iWaveformAudioBuf[i].iIsInUse)
  1010 					{
  1011 					iWaveformAudioBuf[i].Unprepare(aDeviceHandle);
  1012 					iWaveformAudioBuf[i].Prepare(aStartAddress,aBufLength,aDeviceHandle);
  1013 					break;
  1014 					}
  1015 				}
  1016 			__ASSERT_ALWAYS(i>=0,PANIC());
  1017 			}
  1018 		}
  1019 		
  1020 	__KTRACE_SND(Kern::Printf("<TWaveformBufMgr:AcquireBuf - BufNo:%d",i));
  1021 	iWaveformAudioBuf[i].iIsInUse=ETrue;	
  1022 	return(&iWaveformAudioBuf[i]);	
  1023 	}
  1024 	
  1025 /**
  1026 The waveform output callback function to handle data block transfer completion.
  1027 This function is always executed in windows thread context.
  1028 @param aHdr A pointer to the header for the waveform audio buffer just transferred.
  1029 */	
  1030 void DWinsSoundScTxPdd::WaveOutProc(WAVEHDR* aHdr)
  1031 	{
  1032 	TInt waveBufId=aHdr->dwUser;				// Work out which waveform audio buffer is completing.
  1033 
  1034 	StartOfInterrupt();
  1035 	iCompletedPlayBufHdrMask|=(1<<waveBufId);	// Update the completion status mask
  1036 	iDfc.Add();									// Queue PlayDfc().
  1037 	EndOfInterrupt();
  1038 	}
  1039 	
  1040 /**
  1041 The DFC used to handle data block transfer completion.
  1042 This function is always executed in driver thread context.
  1043 @param aPtr A pointer to the physical channel object.
  1044 */	
  1045 void DWinsSoundScTxPdd::PlayDfc(TAny* aPtr)
  1046 	{	
  1047 	TInt i;
  1048 	DWinsSoundScTxPdd& drv=*(DWinsSoundScTxPdd*)aPtr;
  1049 	
  1050 	// More than 1 transfer may have completed so loop until all completions are handled
  1051 	while (drv.iCompletedPlayBufHdrMask)
  1052 		{
  1053 		// Find the buffer ID of the next transfer that has completed
  1054 		for (i=0 ; i<32 && !(drv.iCompletedPlayBufHdrMask&(1<<i)) ; i++) {}
  1055 		__ASSERT_ALWAYS(i<drv.iWaveformBufMgr->iNumWaveformBufs,PANIC());
  1056 		__e32_atomic_and_ord32(&drv.iCompletedPlayBufHdrMask, ~(1u<<i)); // Clear this bit in the mask
  1057 		
  1058 		// Update the status of the waveform audio buffer which is completing
  1059 		TWaveformAudioBuf& buf=drv.iWaveformBufMgr->iWaveformAudioBuf[i];
  1060 		buf.iIsInUse=EFalse;
  1061 	
  1062 		// Callback the LDD passing the information for the transfer that has completed
  1063 		drv.iPendingPlay--;
  1064 		__KTRACE_SND(Kern::Printf("   Write complete(BufNo:%x Pos:%x Len:%d)",i,buf.iBufHdr.lpData,buf.iBufHdr.dwBufferLength));
  1065 		drv.Ldd()->PlayCallback(buf.iTransferID,KErrNone,buf.iBufHdr.dwBufferLength);
  1066 		}
  1067 	}
  1068 		
  1069 /**
  1070 Issue a request from the driver thread to the windows thread to execute a command.
  1071 @param aCommand The identifier of the command to be executed.
  1072 @param aArg0 A first command argument, its meaning depends on the command.
  1073 @param aArg1 A second command argument, its meaning depends on the command.
  1074 @param aArg2 A third command argument, its meaning depends on the command.
  1075 This function is always executed in driver thread context.
  1076 */
  1077 void DWinsSoundScTxPdd::PlayThreadCommand(TThreadCommand aCommand,TInt aArg0,TInt aArg1,TInt aArg2)
  1078 	{
  1079 	__KTRACE_SND(Kern::Printf(">DWinsSoundScTxPdd:PlayThreadCommand"));
  1080 	iPlayCommand = aCommand;
  1081 	iPlayCommandArg0 = aArg0;
  1082 	iPlayCommandArg1 = aArg1;
  1083 	iPlayCommandArg2 = aArg2;
  1084 
  1085 	__HOST_LOCK;
  1086 
  1087 	ReleaseSemaphore(iPlayThreadSem,1,NULL);
  1088 	}
  1089 
  1090 /**
  1091 Pass a value from the windows thread to the driver thread.
  1092 This function is always executed in windows thread context.
  1093 @param aError The value to the passed to the driver thread.
  1094 */
  1095 void DWinsSoundScTxPdd::PlayThreadNotifyDriver(TInt aError)
  1096 	{
  1097 	iPlayThreadError = aError;
  1098 	BOOL ret = ReleaseSemaphore(iDriverThreadSem,1,NULL);
  1099 	__ASSERT_ALWAYS(ret == TRUE, PANIC()); //Unexpected Windows Error
  1100 	}
  1101 
  1102 #pragma warning(disable : 4702) // unreachable code
  1103 /**
  1104 Open the waveform output device for playback. Use a default device identifier in order to select a device
  1105 capable of meeting the current audio configuration. 
  1106 This function can be executed in either driver thread or windows thread context.
  1107 @pre The data member DWinsSoundScTxPdd::iSoundConfig must be setup with the current audio configuration.
  1108 */
  1109 TInt DWinsSoundScTxPdd::OpenWaveOutDevice()
  1110 	{
  1111 	WAVEFORMATEX format;
  1112 	format.wFormatTag = WAVE_FORMAT_PCM;
  1113 	TUint16 bitsPerSample = 8;
  1114 
  1115 	switch (iSoundConfig.iEncoding)
  1116 		{
  1117 		case ESoundEncoding8BitPCM:
  1118 			break;
  1119 		case ESoundEncoding16BitPCM:
  1120 			bitsPerSample = 16;
  1121 			break;
  1122 		default:
  1123 			return KErrNotSupported;
  1124 		};
  1125 
  1126 	TInt rateInSamplesPerSecond=RateInSamplesPerSecond(iSoundConfig.iRate);
  1127 	format.nChannels = TUint16(iSoundConfig.iChannels);
  1128 	format.nSamplesPerSec = rateInSamplesPerSecond;
  1129 	format.nAvgBytesPerSec = rateInSamplesPerSecond * iSoundConfig.iChannels * bitsPerSample / 8;
  1130 	format.nBlockAlign = TUint16(iSoundConfig.iChannels * bitsPerSample / 8);
  1131 	format.wBitsPerSample = bitsPerSample;
  1132 	format.cbSize = 0;
  1133 
  1134 	MMRESULT res = MMSYSERR_NOERROR;
  1135 
  1136 	__COND_HOST_LOCK;		
  1137 	if (iNoHardware)
  1138 		{
  1139 		timeBeginPeriod(KMMTimerRes);
  1140 		iPlayDeviceHandle = (HWAVEOUT)1;	
  1141 		}
  1142 	else
  1143 		{
  1144 		res = waveOutOpen(&iPlayDeviceHandle, WAVE_MAPPER, &format, (DWORD)::WaveOutProc, (DWORD)this, CALLBACK_FUNCTION);
  1145 
  1146 		// On some builds of Windows (such as Windows Server 2003), the waveOutGetDevCaps() trick in
  1147 		// DoCreate() won't work, so we have another special check for missing hardware here
  1148 		if ((res == MMSYSERR_NODRIVER) || (res == MMSYSERR_BADDEVICEID))
  1149 			{
  1150 			// Pretend there was no error and switch into hardware emulation mode
  1151 			res = MMSYSERR_NOERROR;
  1152 			iNoHardware = ETrue;
  1153 			iPlayDeviceHandle = (HWAVEOUT)1;	
  1154 			iWaveformBufMgr->iIsHardware = EFalse;
  1155 			timeBeginPeriod(KMMTimerRes);
  1156 			}
  1157 		}
  1158 
  1159 	if(iNoHardware)
  1160 		{
  1161 		iSimulatedUSecPlayed = 0;
  1162 		}
  1163 
  1164 	switch (res)
  1165 		{
  1166 		case MMSYSERR_NOERROR: // No error
  1167 			return(KErrNone);
  1168 		case MMSYSERR_ALLOCATED: // Specified resource is already allocated.
  1169 			return(KErrInUse);
  1170 		case WAVERR_BADFORMAT: // Attempted to open with an unsupported waveform-audio format
  1171 			return(KErrNotSupported);
  1172 		case MMSYSERR_NOMEM: // Unable to allocate or lock memory.
  1173 			return(KErrNoMemory);
  1174 		default:
  1175 			return(KErrUnknown);
  1176 		}
  1177 	}
  1178 #pragma warning(default : 4702) // unreachable code
  1179 	
  1180 /**
  1181 Open the audio output device.
  1182 This function can be executed in either driver thread or windows thread context.
  1183 @pre The data members DWinsSoundScTxPdd::iSoundConfig and DWinsSoundScTxPdd::iVolume must be setup with the current
  1184 audio configuration.
  1185 */
  1186 TInt DWinsSoundScTxPdd::CreatePlayDevice(TCreatePlayDeviceMode aMode)
  1187 	{
  1188 	// Check if the waveform output device is already open.
  1189 	if (iPlayDeviceHandle)
  1190 		return(KErrNone);
  1191 	
  1192 	WaitForSingleObjectDualThread(iPlayThreadMutex, INFINITE);
  1193 
  1194 	__COND_HOST_LOCK;
  1195 
  1196 	// Open the waveform output device for playback.
  1197 	TInt err = OpenWaveOutDevice();
  1198 	if (err != KErrNone)
  1199 		{
  1200 		ReleaseMutex(iPlayThreadMutex);
  1201 		return(err);
  1202 		}
  1203 		
  1204 	__HOST_LOCK_OFF;
  1205 	
  1206 	if (aMode==EInit && !iNoHardware)
  1207 		{
  1208 		// Remember the existing volume setting and use this as the default.
  1209  		waveOutGetVolume(iPlayDeviceHandle,&iWinWaveVolume);
  1210  		iVolume = iWinWaveVolume;
  1211 		}
  1212 
  1213 	if (aMode==EStartTransfer)
  1214 		{
  1215 		if (!iNoHardware)
  1216 			{
  1217 			// Set the volume of the waveform output device.
  1218 			waveOutSetVolume(iPlayDeviceHandle, iVolume | (iVolume << 16));
  1219 			}
  1220 		
  1221 		// Now, re-allocate a set of the waveform audio blocks in advance of playing any data. Also, prepare one of these
  1222 		// for each buffer within the shared chunk. Need to be in critical section while re-allocating the audio blocks.
  1223 		NKern::ThreadEnterCS();
  1224 		err=iWaveformBufMgr->ReAllocAndUpdate(Ldd()->BufConfig(),Ldd()->ChunkBase(),(TInt)iPlayDeviceHandle);
  1225 		NKern::ThreadLeaveCS(); 
  1226 		}
  1227 	ReleaseMutex(iPlayThreadMutex);
  1228 
  1229 	return(err);
  1230 	}
  1231 
  1232 /**
  1233 Close down the play device.
  1234 This function can be executed in either driver thread or windows thread context.
  1235 */
  1236 void DWinsSoundScTxPdd::ClosePlayDevice()
  1237 	{
  1238 	__KTRACE_SND(Kern::Printf(">DWinsSoundScTxPdd:ClosePlayDevice"));
  1239 	
  1240 	WaitForSingleObjectDualThread(iPlayThreadMutex, INFINITE);
  1241 
  1242 	HWAVEOUT handle = iPlayDeviceHandle;
  1243 
  1244 	__COND_HOST_LOCK;
  1245 
  1246 	if (iNoHardware)
  1247 		timeEndPeriod(KMMTimerRes);
  1248 		
  1249 	if (handle)
  1250 		{
  1251 		// Stop playback on the waveout device. (Windows resets the current position to zero and marks all pending
  1252 		// playback buffers as done).
  1253 		if (!iNoHardware)
  1254 			waveOutReset(handle);
  1255 		
  1256 		// Un-prepare all the waveform audio buffers.
  1257 		for (TInt i=0 ; i<iWaveformBufMgr->iNumWaveformBufs ; i++)
  1258 			iWaveformBufMgr->iWaveformAudioBuf[i].Unprepare((TInt)handle);
  1259 		
  1260 		// Close the waveout device.
  1261 		if (!iNoHardware)
  1262 			{
  1263 			waveOutSetVolume(handle,iWinWaveVolume);	// Restore the original volume setting before closing the driver.
  1264 			waveOutClose(handle);
  1265 			}
  1266 		
  1267 		iPlayDeviceHandle = NULL;
  1268 		}
  1269 		
  1270 	ReleaseMutex(iPlayThreadMutex);
  1271 	}
  1272 		
  1273 /**
  1274 The thread function for the play windows thread.
  1275 This function is always executed in windows thread context.
  1276 @pre The data members DWinsSoundScTxPdd::iSoundConfig and DWinsSoundScTxPdd::iVolume must be setup with the current
  1277 audio configuration.
  1278 */
  1279 void DWinsSoundScTxPdd::PlayThread()
  1280 	{
  1281 	iPlayThreadSem = CreateSemaphore(NULL,0,0x7FFFFFFF,NULL);
  1282 	iPlayTimerEvent = CreateEvent(NULL,TRUE, FALSE, NULL);
  1283 	iPlayThreadMutex = CreateMutex(NULL,FALSE,NULL);
  1284 	__ASSERT_ALWAYS(iPlayThreadSem && iPlayTimerEvent, PANIC());  //no windows memory
  1285 
  1286 	HANDLE objects[2];
  1287 	objects[0] = iPlayThreadSem;		// Indicates command from driver thread
  1288 	objects[1] = iPlayTimerEvent;		// Indicates timer gone off
  1289 	
  1290 	// Open the play device, then close it again. This is so we can return an error early to the client
  1291 	// if there is a problem.
  1292 	TInt ret = CreatePlayDevice(EInit);
  1293 	if (ret != KErrNone)
  1294 		{
  1295 		PlayThreadNotifyDriver(ret);
  1296 		return;
  1297 		}
  1298 	ClosePlayDevice();
  1299 
  1300 	// Signal driver of successful setup
  1301 	PlayThreadNotifyDriver(KErrNone);
  1302 	ResetEvent(iPlayTimerEvent);
  1303 
  1304 	// Wait for the timer to expire or a command.
  1305 	FOREVER
  1306 		{
  1307 		DWORD ret = WaitForMultipleObjectsEx(2,objects,FALSE,INFINITE,TRUE);
  1308 		__KTRACE_SND(Kern::Printf("   PlayThread resumed"));
  1309 		switch (ret)
  1310 			{
  1311 			case WAIT_OBJECT_0:	// Command received from the driver thread.
  1312 				if (ProcessPlayCommand(iPlayCommand,iPlayCommandArg0,iPlayCommandArg1,iPlayCommandArg2)==KErrCompletion)
  1313 					return; // ********* Exit thread **************
  1314 				break;
  1315 			case WAIT_OBJECT_0+1:
  1316 				HandlePlayTimerEvent();
  1317 				break;
  1318 			}
  1319 		}
  1320 	}
  1321 	
  1322 /**
  1323 Process a request from the driver thread to execute a command.
  1324 This function is always executed in windows thread context.
  1325 @param aCommand The identifier of the command to be executed.
  1326 @param aArg0 A first command argument, its meaning depends on the command.
  1327 @param aArg1 A second command argument, its meaning depends on the command.
  1328 @param aArg2 A third command argument, its meaning depends on the command.
  1329 @return KErrCompletion if the command to exit the windows thread has been received;
  1330 		KErrNone otherwise;
  1331 */	
  1332 TInt DWinsSoundScTxPdd::ProcessPlayCommand(TThreadCommand aCommand,TInt aArg0,TInt aArg1,TInt aArg2)
  1333 	{
  1334 	__KTRACE_SND(Kern::Printf(">DWinsSoundScTxPdd:ProcessPlayCommand(%d)",aCommand));
  1335 	switch(aCommand)
  1336 		{
  1337 		case ESendData:	// Initiate the playback of a buffers worth of data to the waveout device.
  1338 			{
  1339 			// Acquire a windows waveform audio buffer for the transfer.
  1340 			char* startAddress=(char*)aArg1;
  1341 			TInt bytesToPlay=aArg2; 
  1342 			__ASSERT_ALWAYS(bytesToPlay>0,PANIC());
  1343 			TWaveformAudioBuf* waveformAudioBuf=iWaveformBufMgr->AcquireBuf(startAddress,bytesToPlay,(TInt)iPlayDeviceHandle);
  1344 			waveformAudioBuf->iTransferID=(TUint)aArg0;
  1345 			waveformAudioBuf->iBufHdr.dwBufferLength=bytesToPlay;
  1346 			
  1347 			if (!iNoHardware)
  1348 				{
  1349 				// This machine has a waveout device present. Send the buffer to the waveout device.
  1350 				waveformAudioBuf->iBufHdr.dwFlags &= ~WHDR_DONE;	// Clear the done flag
  1351 				MMRESULT res = waveOutWrite(iPlayDeviceHandle,&waveformAudioBuf->iBufHdr,sizeof(WAVEHDR));
  1352 				__KTRACE_SND(Kern::Printf("   WaveOutWrite(ID:%x Pos:%x Len:%d)-%d",aArg0,aArg1,aArg2,res));
  1353 				__ASSERT_ALWAYS(res == MMSYSERR_NOERROR,PANIC()); // WaveOutWrite Error	
  1354 				}
  1355 			else 
  1356 				{
  1357 				// This machine has no audio hardware present so simulate the waveout device using a timer.
  1358 				AddToPendingList(&waveformAudioBuf->iBufHdr,iWaveformBufMgr->iPendingBufList);		// Queue the buffer on the pending list
  1359 				
  1360 				// Check if the timer needs starting/re-starting
  1361 				if (!iTimerActive)
  1362 					{
  1363 					iLastTimerEventTime = timeGetTime();
  1364 					StartTimer(&waveformAudioBuf->iBufHdr);
  1365 					}
  1366 				}
  1367 			
  1368 			// Signal the driver thread that we have started playing the next buffer.
  1369 			PlayThreadNotifyDriver(KErrNone);
  1370 		
  1371 			break;
  1372 			}
  1373 			
  1374 		case EStop:	// Terminate the playing of data to the waveout device.
  1375 			if (iNoHardware)
  1376 				{
  1377 				// This machine has no audio hardware present so simulates the waveout device using a timer.
  1378 				StopTimer(ETrue);	// Stop the timer and cancel any buffers pending
  1379 				}
  1380 			ClosePlayDevice();		// Close down the play device.	
  1381 			
  1382 			// Signal the driver thread that we have completed the command.
  1383 			if (iStopSemaphore)
  1384 				{
  1385 				LONG prev;
  1386 				ReleaseSemaphore(iStopSemaphore,1,&prev);
  1387 				}
  1388 			break;
  1389 
  1390 		case EExit:	// Close down the play device and exit the windows thread.
  1391 			{
  1392 			if (!iNoHardware)
  1393 				{
  1394 				// This machine has a waveout device present.
  1395 				if (iPlayDeviceHandle)
  1396 					{
  1397 					waveOutReset(iPlayDeviceHandle);   					// Stop playback on the waveout device.
  1398 					waveOutSetVolume(iPlayDeviceHandle,iWinWaveVolume);	// Restore the original volume setting before closing the driver.
  1399 					waveOutClose(iPlayDeviceHandle);					// Close the waveout device.
  1400 					}
  1401 				}
  1402 			else
  1403 				{
  1404 				// This machine has no audio hardware present so simulates the waveout device using a timer.
  1405 				StopTimer(ETrue);	// Stop the timer and cancel any buffers pending
  1406 				}
  1407 			// Logically the playback device is now shut so clear the handle.
  1408 			iPlayDeviceHandle = 0;
  1409 				
  1410 			// Signal the driver thread that we have completed the command.	
  1411 			if (iDeathSemaphore)
  1412 				{
  1413 				LONG prev;
  1414 				ReleaseSemaphore(iDeathSemaphore,1,&prev);
  1415 				}
  1416 			return(KErrCompletion); 		// ********* Exit thread **************
  1417 			}
  1418 
  1419 		case EPause:	// Halt the playback of data to the waveout device.
  1420 			if (!iNoHardware)
  1421 				waveOutPause(iPlayDeviceHandle); // Pause playback on the waveout device. Windows saves current position.
  1422 			else
  1423 				{
  1424 				StopTimer(EFalse);				 // Just stop the timer. Don't cancel any pending buffers.
  1425 				iPauseTime = timeGetTime();
  1426 				}
  1427 			break;
  1428 
  1429 		case EResume:	// Resume the playback of data to the waveout device.
  1430 			if (!iNoHardware)
  1431 				waveOutRestart(iPlayDeviceHandle);	// Resume playback on the waveout device.
  1432 			else
  1433 				{
  1434 				// Check if there are more audio buffers waiting to be resumed
  1435 				WAVEHDR* buf=iWaveformBufMgr->iPendingBufList[0];
  1436 				if (buf)
  1437 					{
  1438 					// Before restarting the emulation timer, determine how long we were paused for and
  1439 					// add that time to the time the timer last triggered.  This will allow us to continue
  1440 					// as though we had never been paused
  1441 					DWORD currentTime = timeGetTime();
  1442 					iLastTimerEventTime += (currentTime - iPauseTime);
  1443 					iPauseTime = 0;
  1444 					StartTimer(buf);			// Re-start the timer
  1445 					}
  1446 				}
  1447 			break;
  1448 		} 
  1449 	return(KErrNone);
  1450 	}
  1451 	
  1452 /**
  1453 Handle a timer expiry event. This is only used when no audio hardware is present, with a timer expiry corresponding
  1454 to the end of a data block transfer.
  1455 This function is always executed in windows thread context.
  1456 */
  1457 void DWinsSoundScTxPdd::HandlePlayTimerEvent()
  1458 	{
  1459 	__KTRACE_SND(Kern::Printf(">DWinsSoundScTxPdd:HandlePlayTimerEvent"));
  1460 	ResetEvent(iPlayTimerEvent);	// Reset the event 
  1461 
  1462 	iSimulatedUSecPlayed += (1000 * iSimulatedMsecDuration);
  1463 	
  1464 	// Remove the audio buffer just completed from the pending list and save it for the driver thread.
  1465 	WAVEHDR* buf=RemoveFromPendingList(iWaveformBufMgr->iPendingBufList);
  1466 	__ASSERT_ALWAYS(buf != NULL,PANIC());	
  1467 	TInt waveBufId=buf->dwUser;					// Work out which waveform audio buffer is completing.
  1468 
  1469 	// Check if there are more audio buffers waiting to be played
  1470 	buf=iWaveformBufMgr->iPendingBufList[0];
  1471 	if (buf)
  1472 		{
  1473 		iLastTimerEventTime = timeGetTime();
  1474 		StartTimer(buf);						// Re-start the timer
  1475 		}
  1476 	else
  1477 		iTimerActive=EFalse;	
  1478 		
  1479 	// Notify that another audio buffer has been completed.
  1480 	StartOfInterrupt();
  1481 	iCompletedPlayBufHdrMask|=(1<<waveBufId);	// Update the completion status mask
  1482 	iDfc.Add();
  1483 	EndOfInterrupt();
  1484 	return;
  1485 	}
  1486 			
  1487 /**
  1488 Initialise the data member DWinsSoundScTxPdd::iCaps with the capabilities of this audio device.
  1489 */	
  1490 void DWinsSoundScTxPdd::SetCaps()
  1491 	{
  1492 	__KTRACE_SND(Kern::Printf(">DWinsSoundScTxPdd::SetCaps"));
  1493 	
  1494 	// The data transfer direction for this unit is play.
  1495 	iCaps.iDirection=ESoundDirPlayback;
  1496 	
  1497 	// Assume this unit supports mono or stereo.
  1498 	iCaps.iChannels=KSoundMonoChannel|KSoundStereoChannel;
  1499 	
  1500 	// Assume this unit supports all sample rates.
  1501 	iCaps.iRates=(KSoundRate7350Hz|KSoundRate8000Hz|KSoundRate8820Hz|KSoundRate9600Hz|KSoundRate11025Hz|
  1502 				  KSoundRate12000Hz|KSoundRate14700Hz|KSoundRate16000Hz|KSoundRate22050Hz|KSoundRate24000Hz|
  1503 				  KSoundRate29400Hz|KSoundRate32000Hz|KSoundRate44100Hz|KSoundRate48000Hz);
  1504 	
  1505 	// Assume this unit supports 8bit and 16bit PCM encoding.
  1506 	iCaps.iEncodings=(KSoundEncoding8BitPCM|KSoundEncoding16BitPCM);
  1507 	
  1508 	// This unit only supports interleaved data format
  1509 	iCaps.iDataFormats=KSoundDataFormatInterleaved;
  1510 	
  1511 	// The minimum request size that the device can support. 
  1512 	iCaps.iRequestMinSize=0;	// No restriction
  1513 	
  1514 	// The request alignment that this device requires. 
  1515 	iCaps.iRequestAlignment=0;	// No restriction
  1516 	
  1517 	// This unit is not capable of detecting changes in hardware configuration.
  1518 	iCaps.iHwConfigNotificationSupport=EFalse;
  1519 	}
  1520 	
  1521 /**
  1522 Start the audio timer.
  1523 The timer is only used when no audio hardware is present on the machine. This is in order to introduce a delay which is
  1524 equivelent to that incurred when transferring audio data over a real audio device.
  1525 @param aBuffer The audio buffer which would have been transferred had a real audio device been present. This contains
  1526 	information on the number of bytes to transfer.
  1527 */	
  1528 void DWinsSoundScTxPdd::StartTimer(WAVEHDR* aBuffer)
  1529 	{
  1530 	// First, need to calculate the duration of the timer in milliseconds.
  1531 	TInt bytesToPlay=aBuffer->dwBufferLength;
  1532 	iSimulatedMsecDuration = bytesToPlay*1000;
  1533 	iSimulatedMsecDuration /= (RateInSamplesPerSecond(iSoundConfig.iRate) * iSoundConfig.iChannels);
  1534 	if (iSoundConfig.iEncoding==ESoundEncoding16BitPCM)
  1535 		iSimulatedMsecDuration /= 2;
  1536 	if (iSoundConfig.iEncoding==ESoundEncoding24BitPCM)
  1537 		iSimulatedMsecDuration /= 3;
  1538 	if (iSimulatedMsecDuration<=0)
  1539 		iSimulatedMsecDuration=1;	// Round up to 1ms or timeSetEvent() will return an error.
  1540 
  1541 	// If we have been paused and are now restarting, determine the amount of time that we were paused
  1542 	// and subtract that from the time until the next timer trigger.  If this is not done then the time
  1543 	// played or recorded will not be calculated correctly
  1544 	DWORD pauseTime = (timeGetTime() - iLastTimerEventTime);
  1545 	MMRESULT res = timeSetEvent((iSimulatedMsecDuration - pauseTime), KMMTimerRes, (LPTIMECALLBACK)iPlayTimerEvent, 0, TIME_ONESHOT | TIME_CALLBACK_EVENT_SET);
  1546 	__ASSERT_ALWAYS(res != NULL,PANIC()); 	// timeSetEvent error.	
  1547 	iTimerID = res;							// Save the identifier for the new timer event.
  1548 	iTimerActive=ETrue;
  1549 	}
  1550 
  1551 /**
  1552 Stop the audio timer.
  1553 The timer is only used when no audio hardware is present on the machine. This is in order to introduce a delay which is
  1554 equivelent to that incurred when transferring audio data over a real audio device.
  1555 @param aCancellAll Set to ETrue in order to discard any buffers queued on the pending buffer list. EFalse otherwise.
  1556 */	
  1557 void DWinsSoundScTxPdd::StopTimer(TBool aCancelAll)
  1558 	{
  1559 	if (iTimerActive)
  1560 		{
  1561 		MMRESULT res = timeKillEvent(iTimerID);
  1562 		__ASSERT_ALWAYS(res == TIMERR_NOERROR,PANIC()); // timeKillEvent error	
  1563 		
  1564 		if (aCancelAll)
  1565 			{
  1566 			WAVEHDR* b;
  1567 			do
  1568 				b=RemoveFromPendingList(iWaveformBufMgr->iPendingBufList);
  1569 			while(b);
  1570 			}
  1571 		}
  1572 	iTimerActive=EFalse;
  1573 	}	
  1574