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