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".
8 // Initial Contributors:
9 // Nokia Corporation - initial contribution.
14 // wins\specific\soundsc_tx.cpp
15 // Emulator playback functions for the shared chunk sound driver PDD.
25 #include "winssoundsc.h"
27 _LIT(KSoundScPddName,"SoundSc.Wins");
28 _LIT(KTxSoundDriverThreadName,"SoundDriverPlayThread");
29 const TInt KSoundDriverThreadPriority=26; // One less than DFC thread 0
31 GLDEF_C TInt RateInSamplesPerSecond(TSoundRate aRate)
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);
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)
59 TBool epocThread = (NKern::CurrentContext() == NKern::EInterrupt)?EFalse:ETrue;
62 DWORD dwRet = WaitForSingleObject(hHandle, dwMilliseconds);
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.
74 GLDEF_C WAVEHDR* RemoveFromPendingList(WAVEHDR** aList)
81 // Move any remaining up one in the list.
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).
99 GLDEF_C void AddToPendingList(WAVEHDR* aBuffer,WAVEHDR** aList)
107 The thread function for the play windows thread.
108 This function is always executed in windows thread context.
110 LOCAL_C TUint PlayThreadFunction(DWinsSoundScTxPdd *aSoundPdd)
112 aSoundPdd->PlayThread();
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.
122 LOCAL_C void CALLBACK WaveOutProc(HWAVEOUT /*hwo*/, UINT uMsg, DWORD dwInstance, DWORD dwParam1, DWORD /*dwParam2*/)
124 if (uMsg == WOM_DONE)
126 DWinsSoundScTxPdd* pdd = (DWinsSoundScTxPdd*)dwInstance;
127 pdd->WaveOutProc((WAVEHDR*)dwParam1);
131 THostLock::THostLock() : iLocked(EFalse)
136 THostLock::THostLock(TBool /*aLock*/) : iLocked(EFalse)
140 THostLock::~THostLock()
146 void THostLock::Lock()
148 __ASSERT_DEBUG(!iLocked, PANIC());
153 void THostLock::Unlock()
155 __ASSERT_DEBUG(iLocked, PANIC());
160 TCondHostLock::TCondHostLock() : THostLock(EFalse)
162 iEpocThread = (NKern::CurrentContext() == NKern::EInterrupt)?EFalse:ETrue;
166 void TCondHostLock::Lock()
172 void TCondHostLock::Unlock()
179 Standard export function for PDDs. This creates a DPhysicalDevice derived object,
180 in this case - DWinsSoundScPddFactory.
182 DECLARE_STANDARD_PDD()
184 return new DWinsSoundScPddFactory;
188 Constructor for the sound PDD factory class.
190 DWinsSoundScPddFactory::DWinsSoundScPddFactory()
192 __KTRACE_SND(Kern::Printf(">DWinsSoundScPddFactory::DWinsSoundScPddFactory"));
196 // Support units KSoundScTxUnit0 & KSoundScRxUnit0.
197 iUnitsMask=(1<<KSoundScRxUnit0)|(1<<KSoundScTxUnit0);
199 // Set version number for this device.
200 iVersion=RSoundSc::VersionRequired();
204 Destructor for the sound PDD factory class.
206 DWinsSoundScPddFactory::~DWinsSoundScPddFactory()
208 __KTRACE_SND(Kern::Printf(">DWinsSoundScPddFactory::~DWinsSoundScPddFactory"));
210 // Destroy the kernel thread.
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.
220 TInt DWinsSoundScPddFactory::Install()
225 // Create a new sound driver DFC queue (and associated kernel thread).
226 r=Kern::DynamicDfcQCreate(iDfcQ,KSoundDriverThreadPriority,KTxSoundDriverThreadName);
231 r=SetName(&KSoundScPddName); // Set the name of the driver object
234 __KTRACE_SND(Kern::Printf("<DWinsSoundScPddFactory::Install - %d",r));
239 Returns the PDD's capabilities. This is not used by the Symbian OS device driver framework
241 @param aDes A descriptor to write capabilities information into.
243 void DWinsSoundScPddFactory::GetCaps(TDes8& /*aDes*/) const
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.
257 TInt DWinsSoundScPddFactory::Validate(TInt aUnit, const TDesC8* /*aInfo*/, const TVersion& aVer)
259 // Check that the version specified is compatible.
260 if (!Kern::QueryVersionSupported(RSoundSc::VersionRequired(),aVer))
261 return(KErrNotSupported);
263 // Check the unit number is compatible
264 if (aUnit!=KSoundScTxUnit0 && aUnit!=KSoundScRxUnit0)
265 return(KErrNotSupported);
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.
281 TInt DWinsSoundScPddFactory::Create(DBase*& aChannel, TInt aUnit, const TDesC8* /*anInfo*/, const TVersion& /*aVer*/)
283 __KTRACE_SND(Kern::Printf(">DWinsSoundScPddFactory::Create"));
285 // Create the appropriate PDD channel object.
287 if (aUnit==KSoundScRxUnit0)
289 DWinsSoundScRxPdd* pD=new DWinsSoundScRxPdd;
292 r=pD->DoCreate(this);
297 DWinsSoundScTxPdd* pD=new DWinsSoundScTxPdd;
300 r=pD->DoCreate(this);
306 Constructor for the WINS shared chunk playback PDD.
307 This function is always executed in driver thread context.
309 DWinsSoundScTxPdd::DWinsSoundScTxPdd()
310 : iDfc(DWinsSoundScTxPdd::PlayDfc,this,2)
312 __KTRACE_SND(Kern::Printf(">DWinsSoundScTxPdd::DWinsSoundScTxPdd"));
315 // iDriverThreadSem=0;
317 // iPlayThreadMutex=0;
320 // iDeathSemaphore=0;
321 // iPlayCommand=ESendData;
322 // iPlayCommandArg0=0;
323 // iPlayCommandArg1=0;
325 // iPlayThreadError=0;
326 // iWaveformBufMgr=NULL;
327 // iCompletedPlayBufHdrMask=0;
328 // iPlayBufferSize=0;
329 // iNoHardware=EFalse;
330 // iPlayTimerEvent=0;
332 // iTimerActive=EFalse;
337 Destructor for the WINS shared chunk playback PDD.
338 This function is always executed in driver thread context.
340 DWinsSoundScTxPdd::~DWinsSoundScTxPdd()
342 // If the Windows thread started up successfully, signal it to shut down and wait for it to do so
343 if (iPlayThreadRunning)
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);
349 // Wait for the play thread to terminate.
353 WaitForSingleObject(iDeathSemaphore, INFINITE);
357 CloseHandle(iDeathSemaphore);
362 CloseHandle(iPlayTimerEvent);
364 CloseHandle(iPlayThreadSem);
366 CloseHandle(iPlayThread);
367 if (iDriverThreadSem)
368 CloseHandle(iDriverThreadSem);
371 delete iWaveformBufMgr;
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.
382 TInt DWinsSoundScTxPdd::DoCreate(DWinsSoundScPddFactory* aPhysicalDevice)
384 __KTRACE_SND(Kern::Printf(">DWinsSoundScTxPdd::DoCreate"));
386 iPhysicalDevice=aPhysicalDevice;
388 // Set up the correct DFC queue.
389 iDfc.SetDfcQ(iPhysicalDevice->iDfcQ);
391 SetCaps(); // Setup the capabilities of this device.
393 // Setup the default audio configuration
394 iSoundConfig.iChannels=2;
395 iSoundConfig.iRate=ESoundRate48000Hz;
396 iSoundConfig.iEncoding=ESoundEncoding16BitPCM;
397 iSoundConfig.iDataFormat=ESoundDataFormatInterleaved;
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;
411 if (res != MMSYSERR_NOERROR)
416 // Create the windows waveform audio buffer manager.
417 iWaveformBufMgr=new TWaveformBufMgr(ESoundDirPlayback,!iNoHardware);
418 if (!iWaveformBufMgr)
419 return(KErrNoMemory);
421 // Create the driver thread semaphore.
422 iDriverThreadSem = CreateSemaphore(NULL,0,0x7fffffff,NULL);
423 if (!iDriverThreadSem)
424 return(KErrNoMemory);
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
432 // Wait to be notified of successful thread initialization
434 WaitForSingleObject(iDriverThreadSem,INFINITE);
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;
442 return(iPlayThreadError);
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.
450 TDfcQue* DWinsSoundScTxPdd::DfcQ(TInt /*aUnit*/)
452 return(iPhysicalDevice->iDfcQ);
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.
461 void DWinsSoundScTxPdd::GetChunkCreateInfo(TChunkCreateInfo& aChunkCreateInfo)
463 __KTRACE_SND(Kern::Printf(">DWinsSoundScTxPdd::GetChunkCreateInfo"));
465 aChunkCreateInfo.iType=TChunkCreateInfo::ESharedKernelMultiple;
466 aChunkCreateInfo.iMapAttr=0;
467 aChunkCreateInfo.iOwnsMemory=ETrue; // Using RAM pages.
468 aChunkCreateInfo.iDestroyedDfc=NULL; // No chunk destroy DFC.
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.
478 void DWinsSoundScTxPdd::Caps(TDes8& aCapsBuf) const
480 __KTRACE_SND(Kern::Printf(">DWinsSoundScTxPdd::Caps"));
483 TPtrC8 ptr((const TUint8*)&iCaps,sizeof(iCaps));
484 aCapsBuf.FillZ(aCapsBuf.MaxLength());
485 aCapsBuf=ptr.Left(Min(ptr.Length(),aCapsBuf.MaxLength()));
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.
492 TInt DWinsSoundScTxPdd::MaxTransferLen() const
494 return(KWinsMaxAudioTransferLen); // 32K
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.
502 TInt DWinsSoundScTxPdd::PowerUp()
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.
515 TInt DWinsSoundScTxPdd::SetConfig(const TDesC8& aConfigBuf)
517 __KTRACE_SND(Kern::Printf(">DWinsSoundScTxPdd::SetConfig"));
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)
524 // Save the current settings so we can restore them if there is a problem with the new ones.
525 TCurrentSoundFormatV02 saved=iSoundConfig;
527 // Read the new configuration from the LDD.
528 TPtr8 ptr((TUint8*)&iSoundConfig,sizeof(iSoundConfig));
529 Kern::InfoCopy(ptr,aConfigBuf);
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);
537 iSoundConfig=saved; // Restore the previous settings
542 const TInt KdBToLinear[KSoundMaxVolume+1] =
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,
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.
569 TInt DWinsSoundScTxPdd::SetVolume(TInt aVolume)
571 __KTRACE_SND(Kern::Printf(">DWinsSoundScTxPdd::SetVolume"));
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];
578 // Only update the volume on the output device itself if this is currently open.
579 if (iPlayDeviceHandle)
580 waveOutSetVolume(iPlayDeviceHandle, iVolume | (iVolume << 16));
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.
589 TInt DWinsSoundScTxPdd::StartTransfer()
591 __KTRACE_SND(Kern::Printf(">DWinsSoundScTxPdd::StartTransfer"));
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);
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)
602 if (iSoundConfig.iEncoding==ESoundEncoding16BitPCM)
605 // Open the play device with the current settings.
607 iCompletedPlayBufHdrMask=0; // Reset the completion status mask
608 TInt r = CreatePlayDevice(EStartTransfer);
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.
624 TInt DWinsSoundScTxPdd::TransferData(TUint aTransferID,TLinAddr aLinAddr,TPhysAddr /*aPhysAddr*/,TInt aNumBytes)
626 __KTRACE_SND(Kern::Printf(">DWinsSoundScTxPdd::TransferData(ID:%xH)",aTransferID));
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);
634 // Signal the windows thread to initiate the playback of a buffers worth of data to the waveout device.
636 PlayThreadCommand(ESendData,aTransferID,aLinAddr,aNumBytes);
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.
642 WaitForSingleObject(iDriverThreadSem,INFINITE);
645 return(iPlayThreadError);
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.
654 void DWinsSoundScTxPdd::StopTransfer()
656 __KTRACE_SND(Kern::Printf(">DWinsSoundScTxPdd::StopTransfer"));
658 // Signal the windows thread to stop and close the device.
659 iStopSemaphore = CreateSemaphore(NULL, 0, 1, NULL);
660 PlayThreadCommand(EStop);
662 // Wait for the windows thread to stop and close the device.
666 WaitForSingleObject(iStopSemaphore, INFINITE);
670 CloseHandle(iStopSemaphore);
671 iStopSemaphore = NULL;
674 iCompletedPlayBufHdrMask=0; // Reset the completion status mask
676 // Make sure the DFC is not queued.
681 Called from the LDD to halt the playback of data to the sound device but not to release any resources necessary for
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.
688 TInt DWinsSoundScTxPdd::PauseTransfer()
690 __KTRACE_SND(Kern::Printf(">DWinsSoundScTxPdd::PauseTransfer"));
692 // Signal the windows thread to pause playback on the waveout device.
693 PlayThreadCommand(EPause);
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()
703 This function is always executed in driver thread context.
704 @return KErrNone if successful, otherwise one of the other system wide error codes.
706 TInt DWinsSoundScTxPdd::ResumeTransfer()
708 __KTRACE_SND(Kern::Printf(">DWinsSoundScTxPdd::ResumeTransfer"));
710 // Signal the windows thread to resume playback on the waveout device.
711 PlayThreadCommand(EResume);
717 Called from the LDD to power down the sound device.
718 This function is always executed in driver thread context.
720 void DWinsSoundScTxPdd::PowerDown()
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.
729 TInt DWinsSoundScTxPdd::CustomConfig(TInt /*aFunction*/,TAny* /*aParam*/)
731 return(KErrNotSupported);
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.
741 TInt DWinsSoundScTxPdd::TimeTransferred(TInt64& aTimePlayed, TInt /*aStatus*/)
747 time.wType=TIME_BYTES;
749 if(iPlayDeviceHandle == 0)
751 // Have not started playback yet, or have stopped.
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
763 // Determine the # of milliseconds that have passed since the last timer triggered
764 DWORD currentTime = timeGetTime();
765 DWORD timeSinceLastEvent = (currentTime - iLastTimerEventTime);
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
770 timeSinceLastEvent -= (currentTime - iPauseTime);
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;
777 // Now we know the value of the time passed down to a millisecond accuracy
778 aTimePlayed = (iSimulatedUSecPlayed + (timeSinceLastEvent * 1000));
782 // Get the number of bytes played by the Windows audio system
783 if (waveOutGetPosition(iPlayDeviceHandle,&time,sizeof(time))==MMSYSERR_NOERROR)
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)
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);
804 Constructor for the windows playback waveform audio buffer abstraction.
806 TWaveformAudioBuf::TWaveformAudioBuf()
808 memclr(&iBufHdr,sizeof(WAVEHDR));
811 iWaveformBufMgr=NULL;
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.
821 void TWaveformAudioBuf::Prepare(char* aBufAddr,TInt aBufLength,TInt aDeviceHandle)
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.
832 iBufHdr.lpNext = NULL;
833 iBufHdr.reserved = 0;
835 if (iWaveformBufMgr->iIsHardware)
837 if (iWaveformBufMgr->iDirection==ESoundDirPlayback)
838 DoPrepareOut((HWAVEOUT)aDeviceHandle);
840 DoPrepareIn((HWAVEIN)aDeviceHandle);
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.
850 void TWaveformAudioBuf::Unprepare(TInt aDeviceHandle)
852 if (iWaveformBufMgr->iIsHardware && iIsPrepared)
854 if (iWaveformBufMgr->iDirection==ESoundDirPlayback)
855 DoUnprepareOut((HWAVEOUT)aDeviceHandle);
857 DoUnprepareIn((HWAVEIN)aDeviceHandle);
864 Prepare the waveform audio buffer for playback.
865 @param aPlayDeviceHandle The handle to the waveform audio output device.
867 void TWaveformAudioBuf::DoPrepareOut(HWAVEOUT aPlayDeviceHandle)
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.
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.
878 void TWaveformAudioBuf::DoUnprepareOut(HWAVEOUT aPlayDeviceHandle)
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.
886 Constructor for the waveform audio buffer manager.
888 TWaveformBufMgr::TWaveformBufMgr(TSoundDirection aDirection,TBool aIsHardware)
889 : iDirection(aDirection), iIsHardware(aIsHardware)
891 iWaveformAudioBuf=NULL;
894 iPendingBufList=NULL;
898 Destructor for the waveform audio buffer manager.
900 TWaveformBufMgr::~TWaveformBufMgr()
902 if (iWaveformAudioBuf)
903 delete[] iWaveformAudioBuf;
905 delete iPendingBufList;
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.
918 TInt TWaveformBufMgr::ReAllocAndUpdate(TSoundSharedChunkBufConfig* aBufConfig,TLinAddr aChunkBase,TInt aDeviceHandle)
920 __KTRACE_SND(Kern::Printf(">TWaveformBufMgr::ReAllocAndUpdate"));
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)
926 // The number has changed. First, re-allocate the required number of windows waveform data blocks.
927 if (iWaveformAudioBuf)
929 delete[] iWaveformAudioBuf;
930 iWaveformAudioBuf=NULL;
933 // If we are emulating an audio device then delete any pending buffer list previously created.
934 if (!iIsHardware && iPendingBufList)
936 delete iPendingBufList;
937 iPendingBufList=NULL;
939 iNumWaveformBufs = 0;
941 iWaveformAudioBuf=new TWaveformAudioBuf[required];
942 if (!iWaveformAudioBuf)
943 return(KErrNoMemory);
944 for (TInt i=0; i<required ; i++)
946 iWaveformAudioBuf[i].SetWaveformBufMgr(this);
947 iWaveformAudioBuf[i].SetBufNum(i);
950 // If we are emulating an audio device then allocate a new pending buffer list.
953 iPendingBufList=(WAVEHDR**)Kern::AllocZ((required+1)*sizeof(WAVEHDR*));
954 if (!iPendingBufList)
955 return(KErrNoMemory);
957 iNumWaveformBufs = required;
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++)
966 char* bufAddr=(char*)(aChunkBase+bufOffsetList[i]);
967 iWaveformAudioBuf[i].Prepare(bufAddr,aBufConfig->iBufferSizeInBytes,aDeviceHandle);
969 iWaveformBufSize=aBufConfig->iBufferSizeInBytes;
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.
982 TWaveformAudioBuf* TWaveformBufMgr::AcquireBuf(char* aStartAddress,TInt aBufLength,TInt aDeviceHandle)
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.
987 for (i=0; i<iNumWaveformBufs ; i++)
989 if (iWaveformAudioBuf[i].iIsPrepared && iWaveformAudioBuf[i].iBufHdr.lpData==aStartAddress && !iWaveformAudioBuf[i].iIsInUse)
992 if (i>=iNumWaveformBufs)
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++)
997 if (!iWaveformAudioBuf[i].iIsPrepared)
999 iWaveformAudioBuf[i].Prepare(aStartAddress,aBufLength,aDeviceHandle);
1004 // All are prepared already so we need to re-prepare one specially.
1005 if (i>=iNumWaveformBufs)
1007 for (i=0; i<iNumWaveformBufs ; i++)
1009 if (!iWaveformAudioBuf[i].iIsInUse)
1011 iWaveformAudioBuf[i].Unprepare(aDeviceHandle);
1012 iWaveformAudioBuf[i].Prepare(aStartAddress,aBufLength,aDeviceHandle);
1016 __ASSERT_ALWAYS(i>=0,PANIC());
1020 __KTRACE_SND(Kern::Printf("<TWaveformBufMgr:AcquireBuf - BufNo:%d",i));
1021 iWaveformAudioBuf[i].iIsInUse=ETrue;
1022 return(&iWaveformAudioBuf[i]);
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.
1030 void DWinsSoundScTxPdd::WaveOutProc(WAVEHDR* aHdr)
1032 TInt waveBufId=aHdr->dwUser; // Work out which waveform audio buffer is completing.
1035 iCompletedPlayBufHdrMask|=(1<<waveBufId); // Update the completion status mask
1036 iDfc.Add(); // Queue PlayDfc().
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.
1045 void DWinsSoundScTxPdd::PlayDfc(TAny* aPtr)
1048 DWinsSoundScTxPdd& drv=*(DWinsSoundScTxPdd*)aPtr;
1050 // More than 1 transfer may have completed so loop until all completions are handled
1051 while (drv.iCompletedPlayBufHdrMask)
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
1058 // Update the status of the waveform audio buffer which is completing
1059 TWaveformAudioBuf& buf=drv.iWaveformBufMgr->iWaveformAudioBuf[i];
1060 buf.iIsInUse=EFalse;
1062 // Callback the LDD passing the information for the transfer that has completed
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);
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.
1077 void DWinsSoundScTxPdd::PlayThreadCommand(TThreadCommand aCommand,TInt aArg0,TInt aArg1,TInt aArg2)
1079 __KTRACE_SND(Kern::Printf(">DWinsSoundScTxPdd:PlayThreadCommand"));
1080 iPlayCommand = aCommand;
1081 iPlayCommandArg0 = aArg0;
1082 iPlayCommandArg1 = aArg1;
1083 iPlayCommandArg2 = aArg2;
1087 ReleaseSemaphore(iPlayThreadSem,1,NULL);
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.
1095 void DWinsSoundScTxPdd::PlayThreadNotifyDriver(TInt aError)
1097 iPlayThreadError = aError;
1098 BOOL ret = ReleaseSemaphore(iDriverThreadSem,1,NULL);
1099 __ASSERT_ALWAYS(ret == TRUE, PANIC()); //Unexpected Windows Error
1102 #pragma warning(disable : 4702) // unreachable code
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.
1109 TInt DWinsSoundScTxPdd::OpenWaveOutDevice()
1111 WAVEFORMATEX format;
1112 format.wFormatTag = WAVE_FORMAT_PCM;
1113 TUint16 bitsPerSample = 8;
1115 switch (iSoundConfig.iEncoding)
1117 case ESoundEncoding8BitPCM:
1119 case ESoundEncoding16BitPCM:
1123 return KErrNotSupported;
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;
1134 MMRESULT res = MMSYSERR_NOERROR;
1139 timeBeginPeriod(KMMTimerRes);
1140 iPlayDeviceHandle = (HWAVEOUT)1;
1144 res = waveOutOpen(&iPlayDeviceHandle, WAVE_MAPPER, &format, (DWORD)::WaveOutProc, (DWORD)this, CALLBACK_FUNCTION);
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))
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);
1161 iSimulatedUSecPlayed = 0;
1166 case MMSYSERR_NOERROR: // No error
1168 case MMSYSERR_ALLOCATED: // Specified resource is already allocated.
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);
1175 return(KErrUnknown);
1178 #pragma warning(default : 4702) // unreachable code
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.
1186 TInt DWinsSoundScTxPdd::CreatePlayDevice(TCreatePlayDeviceMode aMode)
1188 // Check if the waveform output device is already open.
1189 if (iPlayDeviceHandle)
1192 WaitForSingleObjectDualThread(iPlayThreadMutex, INFINITE);
1196 // Open the waveform output device for playback.
1197 TInt err = OpenWaveOutDevice();
1198 if (err != KErrNone)
1200 ReleaseMutex(iPlayThreadMutex);
1206 if (aMode==EInit && !iNoHardware)
1208 // Remember the existing volume setting and use this as the default.
1209 waveOutGetVolume(iPlayDeviceHandle,&iWinWaveVolume);
1210 iVolume = iWinWaveVolume;
1213 if (aMode==EStartTransfer)
1217 // Set the volume of the waveform output device.
1218 waveOutSetVolume(iPlayDeviceHandle, iVolume | (iVolume << 16));
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();
1227 ReleaseMutex(iPlayThreadMutex);
1233 Close down the play device.
1234 This function can be executed in either driver thread or windows thread context.
1236 void DWinsSoundScTxPdd::ClosePlayDevice()
1238 __KTRACE_SND(Kern::Printf(">DWinsSoundScTxPdd:ClosePlayDevice"));
1240 WaitForSingleObjectDualThread(iPlayThreadMutex, INFINITE);
1242 HWAVEOUT handle = iPlayDeviceHandle;
1247 timeEndPeriod(KMMTimerRes);
1251 // Stop playback on the waveout device. (Windows resets the current position to zero and marks all pending
1252 // playback buffers as done).
1254 waveOutReset(handle);
1256 // Un-prepare all the waveform audio buffers.
1257 for (TInt i=0 ; i<iWaveformBufMgr->iNumWaveformBufs ; i++)
1258 iWaveformBufMgr->iWaveformAudioBuf[i].Unprepare((TInt)handle);
1260 // Close the waveout device.
1263 waveOutSetVolume(handle,iWinWaveVolume); // Restore the original volume setting before closing the driver.
1264 waveOutClose(handle);
1267 iPlayDeviceHandle = NULL;
1270 ReleaseMutex(iPlayThreadMutex);
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.
1279 void DWinsSoundScTxPdd::PlayThread()
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
1287 objects[0] = iPlayThreadSem; // Indicates command from driver thread
1288 objects[1] = iPlayTimerEvent; // Indicates timer gone off
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)
1295 PlayThreadNotifyDriver(ret);
1300 // Signal driver of successful setup
1301 PlayThreadNotifyDriver(KErrNone);
1302 ResetEvent(iPlayTimerEvent);
1304 // Wait for the timer to expire or a command.
1307 DWORD ret = WaitForMultipleObjectsEx(2,objects,FALSE,INFINITE,TRUE);
1308 __KTRACE_SND(Kern::Printf(" PlayThread resumed"));
1311 case WAIT_OBJECT_0: // Command received from the driver thread.
1312 if (ProcessPlayCommand(iPlayCommand,iPlayCommandArg0,iPlayCommandArg1,iPlayCommandArg2)==KErrCompletion)
1313 return; // ********* Exit thread **************
1315 case WAIT_OBJECT_0+1:
1316 HandlePlayTimerEvent();
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;
1332 TInt DWinsSoundScTxPdd::ProcessPlayCommand(TThreadCommand aCommand,TInt aArg0,TInt aArg1,TInt aArg2)
1334 __KTRACE_SND(Kern::Printf(">DWinsSoundScTxPdd:ProcessPlayCommand(%d)",aCommand));
1337 case ESendData: // Initiate the playback of a buffers worth of data to the waveout device.
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;
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
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
1360 // Check if the timer needs starting/re-starting
1363 iLastTimerEventTime = timeGetTime();
1364 StartTimer(&waveformAudioBuf->iBufHdr);
1368 // Signal the driver thread that we have started playing the next buffer.
1369 PlayThreadNotifyDriver(KErrNone);
1374 case EStop: // Terminate the playing of data to the waveout device.
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
1380 ClosePlayDevice(); // Close down the play device.
1382 // Signal the driver thread that we have completed the command.
1386 ReleaseSemaphore(iStopSemaphore,1,&prev);
1390 case EExit: // Close down the play device and exit the windows thread.
1394 // This machine has a waveout device present.
1395 if (iPlayDeviceHandle)
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.
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
1407 // Logically the playback device is now shut so clear the handle.
1408 iPlayDeviceHandle = 0;
1410 // Signal the driver thread that we have completed the command.
1411 if (iDeathSemaphore)
1414 ReleaseSemaphore(iDeathSemaphore,1,&prev);
1416 return(KErrCompletion); // ********* Exit thread **************
1419 case EPause: // Halt the playback of data to the waveout device.
1421 waveOutPause(iPlayDeviceHandle); // Pause playback on the waveout device. Windows saves current position.
1424 StopTimer(EFalse); // Just stop the timer. Don't cancel any pending buffers.
1425 iPauseTime = timeGetTime();
1429 case EResume: // Resume the playback of data to the waveout device.
1431 waveOutRestart(iPlayDeviceHandle); // Resume playback on the waveout device.
1434 // Check if there are more audio buffers waiting to be resumed
1435 WAVEHDR* buf=iWaveformBufMgr->iPendingBufList[0];
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);
1444 StartTimer(buf); // Re-start the timer
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.
1457 void DWinsSoundScTxPdd::HandlePlayTimerEvent()
1459 __KTRACE_SND(Kern::Printf(">DWinsSoundScTxPdd:HandlePlayTimerEvent"));
1460 ResetEvent(iPlayTimerEvent); // Reset the event
1462 iSimulatedUSecPlayed += (1000 * iSimulatedMsecDuration);
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.
1469 // Check if there are more audio buffers waiting to be played
1470 buf=iWaveformBufMgr->iPendingBufList[0];
1473 iLastTimerEventTime = timeGetTime();
1474 StartTimer(buf); // Re-start the timer
1477 iTimerActive=EFalse;
1479 // Notify that another audio buffer has been completed.
1481 iCompletedPlayBufHdrMask|=(1<<waveBufId); // Update the completion status mask
1488 Initialise the data member DWinsSoundScTxPdd::iCaps with the capabilities of this audio device.
1490 void DWinsSoundScTxPdd::SetCaps()
1492 __KTRACE_SND(Kern::Printf(">DWinsSoundScTxPdd::SetCaps"));
1494 // The data transfer direction for this unit is play.
1495 iCaps.iDirection=ESoundDirPlayback;
1497 // Assume this unit supports mono or stereo.
1498 iCaps.iChannels=KSoundMonoChannel|KSoundStereoChannel;
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);
1505 // Assume this unit supports 8bit and 16bit PCM encoding.
1506 iCaps.iEncodings=(KSoundEncoding8BitPCM|KSoundEncoding16BitPCM);
1508 // This unit only supports interleaved data format
1509 iCaps.iDataFormats=KSoundDataFormatInterleaved;
1511 // The minimum request size that the device can support.
1512 iCaps.iRequestMinSize=0; // No restriction
1514 // The request alignment that this device requires.
1515 iCaps.iRequestAlignment=0; // No restriction
1517 // This unit is not capable of detecting changes in hardware configuration.
1518 iCaps.iHwConfigNotificationSupport=EFalse;
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.
1528 void DWinsSoundScTxPdd::StartTimer(WAVEHDR* aBuffer)
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.
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.
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.
1557 void DWinsSoundScTxPdd::StopTimer(TBool aCancelAll)
1561 MMRESULT res = timeKillEvent(iTimerID);
1562 __ASSERT_ALWAYS(res == TIMERR_NOERROR,PANIC()); // timeKillEvent error
1568 b=RemoveFromPendingList(iWaveformBufMgr->iPendingBufList);
1572 iTimerActive=EFalse;