1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/os/boardsupport/emulator/emulatorbsp/specific/soundsc_rx.cpp Fri Jun 15 03:10:57 2012 +0200
1.3 @@ -0,0 +1,1138 @@
1.4 +// Copyright (c) 2006-2009 Nokia Corporation and/or its subsidiary(-ies).
1.5 +// All rights reserved.
1.6 +// This component and the accompanying materials are made available
1.7 +// under the terms of "Eclipse Public License v1.0"
1.8 +// which accompanies this distribution, and is available
1.9 +// at the URL "http://www.eclipse.org/legal/epl-v10.html".
1.10 +//
1.11 +// Initial Contributors:
1.12 +// Nokia Corporation - initial contribution.
1.13 +//
1.14 +// Contributors:
1.15 +//
1.16 +// Description:
1.17 +// wins\specific\soundsc_rx.cpp
1.18 +// Emulator record functions for the shared chunk sound driver PDD.
1.19 +//
1.20 +//
1.21 +
1.22 +/**
1.23 + @file
1.24 + @internalTechnology
1.25 + @prototype
1.26 +*/
1.27 +
1.28 +#include "winssoundsc.h"
1.29 +
1.30 +/**
1.31 +The thread function for the record windows thread.
1.32 +This function is always executed in windows thread context.
1.33 +*/
1.34 +LOCAL_C TUint RecordThreadFunction(DWinsSoundScRxPdd *aSoundPdd)
1.35 + {
1.36 + aSoundPdd->RecordThread();
1.37 + return 0;
1.38 + }
1.39 +
1.40 +/**
1.41 +The waveform input callback function. This can receive the following messages:-
1.42 +WIM_OPEN when the input device is opened, WIM_CLOSE when the input device is closed,
1.43 +and WIM_DATA each time a record data block has been filled (i.e. completion of waveInAddBuffer).
1.44 +This function is always executed in windows thread context.
1.45 +*/
1.46 +LOCAL_C void CALLBACK WaveInProc(HWAVEIN /*hwi*/, UINT uMsg, DWORD dwInstance, DWORD dwParam1, DWORD /*dwParam2*/)
1.47 + {
1.48 + if (uMsg == WIM_DATA)
1.49 + {
1.50 + DWinsSoundScRxPdd * pdd = (DWinsSoundScRxPdd*)dwInstance;
1.51 + pdd->WaveInProc((WAVEHDR*)dwParam1);
1.52 + }
1.53 + }
1.54 +
1.55 +/**
1.56 +Constructor for the WINS shared chunk record PDD.
1.57 +This function is always executed in driver thread context.
1.58 +*/
1.59 +DWinsSoundScRxPdd::DWinsSoundScRxPdd()
1.60 + : iDfc(DWinsSoundScRxPdd::RecordDfc,this,2)
1.61 + {
1.62 + __KTRACE_SND(Kern::Printf(">DWinsSoundScRxPdd::DWinsSoundScRxPdd"));
1.63 +
1.64 +// iDriverThreadSem=0;
1.65 +// iRecordThread=0;
1.66 +// iRecordThreadMutex=0;
1.67 +// iRecordThreadSem=0;
1.68 +// iStopSemaphore=0;
1.69 +// iDeathSemaphore=0;
1.70 +// iRecordDeviceHandle=0;
1.71 +// iRecordCommand=ERecData;
1.72 +// iRecordCommandArg0=0;
1.73 +// iRecordCommandArg1=0;
1.74 +// iPendingRecord=0;
1.75 +// iRecordThreadError=0;
1.76 +/// iWaveformBufMgr=NULL;
1.77 +// iCompletedRecordBufHdrMask=0;
1.78 +// iRecordBufferSize=0;
1.79 +// iRecordEnabled=EFalse;
1.80 +// iNoHardware=EFalse;
1.81 +// iRecordTimerEvent=0;
1.82 +// iTimerID=0;
1.83 +// iTimerActive=EFalse;
1.84 + }
1.85 +
1.86 +/**
1.87 +Destructor for the WINS shared chunk record PDD.
1.88 +This function is always executed in driver thread context.
1.89 +*/
1.90 +DWinsSoundScRxPdd::~DWinsSoundScRxPdd()
1.91 + {
1.92 + // If the Windows thread started up successfully, signal it to shut down and wait for it to do so
1.93 + if (iRecordThreadRunning)
1.94 + {
1.95 + // Signal the windows thread to close down the record device and exit the windows thread.
1.96 + iDeathSemaphore = CreateSemaphore(NULL, 0, 2, NULL);
1.97 + RecordThreadCommand(EExit);
1.98 +
1.99 + // Wait for the record thread to terminate.
1.100 + if (iDeathSemaphore)
1.101 + {
1.102 + Emulator::Escape();
1.103 + WaitForSingleObject(iDeathSemaphore, INFINITE);
1.104 + Emulator::Reenter();
1.105 +
1.106 + __HOST_LOCK;
1.107 + CloseHandle(iDeathSemaphore);
1.108 + }
1.109 + }
1.110 +
1.111 + if (iRecordTimerEvent)
1.112 + CloseHandle(iRecordTimerEvent);
1.113 + if (iRecordThreadSem)
1.114 + CloseHandle(iRecordThreadSem);
1.115 + if (iRecordThread)
1.116 + CloseHandle(iRecordThread);
1.117 + if (iDriverThreadSem)
1.118 + CloseHandle(iDriverThreadSem);
1.119 +
1.120 + if (iWaveformBufMgr)
1.121 + delete iWaveformBufMgr;
1.122 + }
1.123 +
1.124 +/**
1.125 +Second stage constructor for the WINS shared chunk record PDD.
1.126 +Note that this constructor is called before the second stage constructor for the LDD so it is not
1.127 +possible to call methods on the LDD here.
1.128 +This function is always executed in driver thread context.
1.129 +@param aPhysicalDevice A pointer to the factory class that is creating this PDD.
1.130 +@return KErrNone if successful, otherwise one of the other system wide error codes.
1.131 +*/
1.132 +TInt DWinsSoundScRxPdd::DoCreate(DWinsSoundScPddFactory* aPhysicalDevice)
1.133 + {
1.134 + __KTRACE_SND(Kern::Printf(">DWinsSoundScRxPdd::DoCreate"));
1.135 +
1.136 + iPhysicalDevice=aPhysicalDevice;
1.137 +
1.138 + // Set up the correct DFC queue.
1.139 + iDfc.SetDfcQ(iPhysicalDevice->iDfcQ);
1.140 +
1.141 + SetCaps(); // Setup the capabilities of this device.
1.142 +
1.143 + // Setup the default audio configuration
1.144 + iSoundConfig.iChannels=2;
1.145 + iSoundConfig.iRate=ESoundRate48000Hz;
1.146 + iSoundConfig.iEncoding=ESoundEncoding16BitPCM;
1.147 + iSoundConfig.iDataFormat=ESoundDataFormatInterleaved;
1.148 +
1.149 + __HOST_LOCK;
1.150 +
1.151 + // Query the waveform device capabilities using the default device identifier in order
1.152 + // to check if there is a functioning waveform device present. Note that some versions of
1.153 + // Windows (such as Windows Server 2003) will actually return MMSYSERR_NOERROR when this is
1.154 + // called, even if there is no waveform device present, so we have a further check in
1.155 + // when waveInOpen() is called
1.156 + WAVEINCAPS waveInCaps;
1.157 + MMRESULT res = waveInGetDevCaps(WAVE_MAPPER,&waveInCaps,sizeof(WAVEINCAPS));
1.158 +#ifdef FORCE_NO_HARDWARE
1.159 + res=MMSYSERR_NOERROR+1;
1.160 +#endif
1.161 + if (res != MMSYSERR_NOERROR)
1.162 + iNoHardware = ETrue;
1.163 +
1.164 + __HOST_LOCK_OFF;
1.165 +
1.166 + // Create the windows waveform audio buffer manager.
1.167 + iWaveformBufMgr=new TWaveformBufMgr(ESoundDirRecord,!iNoHardware);
1.168 + if (!iWaveformBufMgr)
1.169 + return(KErrNoMemory);
1.170 +
1.171 + // Create the driver thread semaphore.
1.172 + iDriverThreadSem = CreateSemaphore(NULL,0,0x7fffffff,NULL);
1.173 + if (!iDriverThreadSem)
1.174 + return(KErrNoMemory);
1.175 +
1.176 + // Create the record windows thread.
1.177 + if ((iRecordThread=CreateWin32Thread(EThreadEvent,(LPTHREAD_START_ROUTINE)RecordThreadFunction,(void *)this, FALSE))==NULL)
1.178 + return(Emulator::LastError());
1.179 + SetThreadPriority(iRecordThread,THREAD_PRIORITY_HIGHEST);
1.180 + __ASSERT_ALWAYS( ResumeThread(iRecordThread) != 0xffffffff, PANIC()); //Windows Unexpected Error
1.181 +
1.182 + // Wait to be notified of successful thread initialization
1.183 + Emulator::Escape();
1.184 + WaitForSingleObject(iDriverThreadSem,INFINITE);
1.185 + Emulator::Reenter();
1.186 +
1.187 + // If the Windows thread started up successfully, indicate this fact so that when shutting down we know
1.188 + // to signal to the thread to exit
1.189 + if (iRecordThreadError == KErrNone)
1.190 + iRecordThreadRunning = ETrue;
1.191 +
1.192 + return(iRecordThreadError);
1.193 + }
1.194 +
1.195 +/**
1.196 +Called from the LDD to return the DFC queue to be used by this device.
1.197 +This function is always executed in driver thread context.
1.198 +@return The DFC queue to use.
1.199 +*/
1.200 +TDfcQue* DWinsSoundScRxPdd::DfcQ(TInt /*aUnit*/)
1.201 + {
1.202 + return(iPhysicalDevice->iDfcQ);
1.203 + }
1.204 +
1.205 +/**
1.206 +Called from the LDD to return the shared chunk create information to be used by this device.
1.207 +This function is always executed in driver thread context.
1.208 +@param aChunkCreateInfo A chunk create info. object to be to be filled with the settings
1.209 + required for this device.
1.210 +*/
1.211 +void DWinsSoundScRxPdd::GetChunkCreateInfo(TChunkCreateInfo& aChunkCreateInfo)
1.212 + {
1.213 + __KTRACE_SND(Kern::Printf(">DWinsSoundScRxPdd::GetChunkCreateInfo"));
1.214 +
1.215 + aChunkCreateInfo.iType=TChunkCreateInfo::ESharedKernelMultiple;
1.216 + aChunkCreateInfo.iMapAttr=0;
1.217 + aChunkCreateInfo.iOwnsMemory=ETrue; // Using RAM pages.
1.218 + aChunkCreateInfo.iDestroyedDfc=NULL; // No chunk destroy DFC.
1.219 + }
1.220 +
1.221 +/**
1.222 +Called from the LDD to return the capabilities of this device.
1.223 +This function is always executed in driver thread context.
1.224 +@param aCapsBuf A packaged TSoundFormatsSupportedV02 object to be filled with the record
1.225 + capabilities of this device. This descriptor is in kernel memory and can be accessed directly.
1.226 +@see TSoundFormatsSupportedV02.
1.227 +*/
1.228 +void DWinsSoundScRxPdd::Caps(TDes8& aCapsBuf) const
1.229 + {
1.230 + __KTRACE_SND(Kern::Printf(">DWinsSoundScRxPdd::Caps"));
1.231 +
1.232 + // Copy iCaps back.
1.233 + TPtrC8 ptr((const TUint8*)&iCaps,sizeof(iCaps));
1.234 + aCapsBuf.FillZ(aCapsBuf.MaxLength());
1.235 + aCapsBuf=ptr.Left(Min(ptr.Length(),aCapsBuf.MaxLength()));
1.236 + }
1.237 +
1.238 +/**
1.239 +Called from the LDD to return the maximum transfer length in bytes that this device can support in a single data transfer.
1.240 +@return The maximum transfer length in bytes.
1.241 +*/
1.242 +TInt DWinsSoundScRxPdd::MaxTransferLen() const
1.243 + {
1.244 + return(KWinsMaxAudioTransferLen); // 32K
1.245 + }
1.246 +
1.247 +/**
1.248 +Called from the LDD to power up the sound device.
1.249 +This function is always executed in driver thread context.
1.250 +@return KErrNone if successful, otherwise one of the other system wide error codes.
1.251 +*/
1.252 +TInt DWinsSoundScRxPdd::PowerUp()
1.253 + {
1.254 + return(KErrNone);
1.255 + }
1.256 +
1.257 +/**
1.258 +Called from the LDD to configure or reconfigure the device using the the configuration supplied.
1.259 +This function is always executed in driver thread context.
1.260 +@param aConfigBuf A packaged TCurrentSoundFormatV02 object which contains the new configuration settings.
1.261 + This descriptor is in kernel memory and can be accessed directly.
1.262 +@return KErrNone if successful, otherwise one of the other system wide error codes.
1.263 +@see TCurrentSoundFormatV02.
1.264 +*/
1.265 +TInt DWinsSoundScRxPdd::SetConfig(const TDesC8& aConfigBuf)
1.266 + {
1.267 + __KTRACE_SND(Kern::Printf(">DWinsSoundScRxPdd::SetConfig"));
1.268 +
1.269 + // Cannot change the configuration while the device is open and recording. (LDD should prevent
1.270 + // this anyway but better safe than sorry).
1.271 + if (iRecordDeviceHandle)
1.272 + return(KErrInUse);
1.273 +
1.274 + // Save the current settings so we can restore them if there is a problem with the new ones.
1.275 + TCurrentSoundFormatV02 saved=iSoundConfig;
1.276 +
1.277 + // Read the new configuration from the LDD.
1.278 + TPtr8 ptr((TUint8*)&iSoundConfig,sizeof(iSoundConfig));
1.279 + Kern::InfoCopy(ptr,aConfigBuf);
1.280 +
1.281 + // Open the record device with the new settings to check they are supported. Then close it
1.282 + // again - don't leave it open yet.
1.283 + TInt r = CreateRecordDevice(ETrue);
1.284 + if (r==KErrNone)
1.285 + CloseRecordDevice();
1.286 + else
1.287 + iSoundConfig=saved; // Restore the previous settings
1.288 +
1.289 + return(r);
1.290 + }
1.291 +
1.292 +/**
1.293 +Called from the LDD to set the record level.
1.294 +This function is always executed in driver thread context.
1.295 +@param aLevel The record level to be set - a value in the range 0 to 255. The value 255 equates
1.296 + to the maximum record level and each value below this equates to a 0.5dB step below it.
1.297 +@return KErrNone if successful, otherwise one of the other system wide error codes.
1.298 +*/
1.299 +TInt DWinsSoundScRxPdd::SetVolume(TInt /*aVolume*/)
1.300 + {
1.301 + __KTRACE_SND(Kern::Printf(">DWinsSoundScRxPdd::SetVolume"));
1.302 +
1.303 + // There's no adjustment of the record level on the wave in device.
1.304 +
1.305 + return(KErrNone);
1.306 + }
1.307 +
1.308 +/**
1.309 +Called from the LDD to prepare the audio device for recording.
1.310 +This function is always executed in driver thread context.
1.311 +@return KErrNone if successful, otherwise one of the other system wide error codes.
1.312 +*/
1.313 +TInt DWinsSoundScRxPdd::StartTransfer()
1.314 + {
1.315 + __KTRACE_SND(Kern::Printf(">DWinsSoundScRxPdd::StartTransfer"));
1.316 +
1.317 + // Convert the enum representing the current sample rate into an integer
1.318 + TInt samplesPerSecond=RateInSamplesPerSecond(iSoundConfig.iRate);
1.319 + if (samplesPerSecond==0)
1.320 + return(KErrNotSupported);
1.321 +
1.322 + // Now convert the sample rate into the number of bytes per second and save for later use
1.323 + iBytesPerSecond=samplesPerSecond;
1.324 + if (iSoundConfig.iChannels==2)
1.325 + iBytesPerSecond*=2;
1.326 + if (iSoundConfig.iEncoding==ESoundEncoding16BitPCM)
1.327 + iBytesPerSecond*=2;
1.328 +
1.329 + iBytesRecordedBeforeLastPause = 0;
1.330 + iBytesSincePauseReportedToLdd = 0;
1.331 +
1.332 + iRecordEnabled=ETrue;
1.333 +
1.334 + // Open the record device with the current settings.
1.335 + iPendingRecord=0;
1.336 + iCompletedRecordBufHdrMask=0; // Reset the completion status mask
1.337 + TInt r = CreateRecordDevice();
1.338 + return(r);
1.339 + }
1.340 +
1.341 +/**
1.342 +Called from the LDD to initiate the recording of a portion of data from the audio device.
1.343 +When the transfer is complete, the PDD signals this event using the LDD function RecordCallback().
1.344 +This function is always executed in driver thread context.
1.345 +@param aTransferID A value assigned by the LDD to allow it to uniquely identify a particular transfer fragment.
1.346 +@param aLinAddr The linear address within the shared chunk for storing the recorded data.
1.347 +@param aPhysAddr The physical address within the shared chunk for storing the recorded data.
1.348 +@param aNumBytes The number of bytes to be recorded.
1.349 +@return KErrNone if the transfer has been initiated successfully;
1.350 + KErrNotReady if the device is unable to accept the transfer for the moment;
1.351 + otherwise one of the other system-wide error codes.
1.352 +*/
1.353 +TInt DWinsSoundScRxPdd::TransferData(TUint aTransferID,TLinAddr aLinAddr,TPhysAddr /*aPhysAddr*/,TInt aNumBytes)
1.354 + {
1.355 + __KTRACE_SND(Kern::Printf(">DWinsSoundScRxPdd::TransferData(ID:%xH)",aTransferID));
1.356 +
1.357 + // Check that we can accept the request
1.358 + if (aNumBytes>KWinsMaxAudioTransferLen)
1.359 + return(KErrArgument);
1.360 + if (iPendingRecord>=iWaveformBufMgr->iNumWaveformBufs) // LDD may issue multiple data transfers per buffer.
1.361 + return(KErrNotReady);
1.362 +
1.363 + // Signal the windows thread to initiate the recording of a buffers worth of data from the wavein device.
1.364 + iPendingRecord++;
1.365 + RecordThreadCommand(ERecData,aTransferID,aLinAddr,aNumBytes);
1.366 +
1.367 + // Although the windows thread runs at a higher priority, its not safe to assume we will always get pre-empted at this
1.368 + // point while the the higher priority thread processes and completes the request. Instead we need to wait until it
1.369 + // signals back completion of the command.
1.370 + Emulator::Escape();
1.371 + WaitForSingleObject(iDriverThreadSem,INFINITE);
1.372 + Emulator::Reenter();
1.373 +
1.374 + return(iRecordThreadError);
1.375 + }
1.376 +
1.377 +/**
1.378 +Called from the LDD to terminate the recording of a data from the device and to release any resources necessary for
1.379 +recording.
1.380 +The LDD will leave the audio device capturing record data even when there are no record requests pending from the client.
1.381 +Transfer will only be terminated when the client either issues RSoundSc::CancelRecordData() or closes the channel. Once
1.382 +this function had been called, the LDD will not issue any further TransferData() commands without first issueing a
1.383 +StartTransfer() command.
1.384 +This function is always executed in driver thread context.
1.385 +*/
1.386 +void DWinsSoundScRxPdd::StopTransfer()
1.387 + {
1.388 + __KTRACE_SND(Kern::Printf(">DWinsSoundScRxPdd::StopTransfer"));
1.389 +
1.390 + // Signal the windows thread to stop it from sending any more buffers to wavein device.
1.391 + iStopSemaphore = CreateSemaphore(NULL, 0, 1, NULL);
1.392 + RecordThreadCommand(EStop);
1.393 +
1.394 + // Need to wait for the record thread to finish using the record handle before it's safe to close the device and
1.395 + // set the handle to NULL.
1.396 + if (iStopSemaphore)
1.397 + {
1.398 + // Wait for the record thread to stop.
1.399 + Emulator::Escape();
1.400 + WaitForSingleObject(iStopSemaphore, INFINITE);
1.401 + Emulator::Reenter();
1.402 +
1.403 + __HOST_LOCK;
1.404 + CloseHandle(iStopSemaphore);
1.405 + iStopSemaphore = NULL;
1.406 + }
1.407 +
1.408 + // Make sure the DFC is not queued.
1.409 + iDfc.Cancel();
1.410 +
1.411 + CloseRecordDevice(); // Close down the record device.
1.412 + iPendingRecord=0;
1.413 + iCompletedRecordBufHdrMask=0; // Reset the completion status mask
1.414 + }
1.415 +
1.416 +/**
1.417 +Called from the LDD to halt the recording of data from the sound device but not to release any resources necessary for
1.418 +recording.
1.419 +All active transfers should be aborted. When recording is halted the PDD signals this event with a single call of the LDD
1.420 +function RecordCallback() - reporting back any partial data already received. If transfer is resumed later, the LDD will
1.421 +issue a new TransferData() request to re-commence data transfer.
1.422 +This function is always executed in driver thread context.
1.423 +@return KErrNone if successful, otherwise one of the other system wide error codes.
1.424 +*/
1.425 +TInt DWinsSoundScRxPdd::PauseTransfer()
1.426 + {
1.427 + __KTRACE_SND(Kern::Printf(">DWinsSoundScRxPdd::PauseTransfer"));
1.428 +
1.429 + // Signal the windows thread to stop recording on the wavein device - aborting any transfers queued.
1.430 + RecordThreadCommand(EPause);
1.431 +
1.432 + // Wait for the windows thread to complete the request
1.433 + Emulator::Escape();
1.434 + WaitForSingleObject(iDriverThreadSem,INFINITE);
1.435 + Emulator::Reenter();
1.436 +
1.437 + // Make sure the DFC is not queued.
1.438 + iDfc.Cancel();
1.439 +
1.440 + // The windows thread returns the total bytes recorded since the last pause (as reported by windows).
1.441 + TUint totalRecordedSincePause = iRecordThreadError;
1.442 + TUint lastTransferLength = totalRecordedSincePause - iBytesSincePauseReportedToLdd;
1.443 + Kern::Printf("totalRecordedSincePause %d - iBytesSincePauseReportedToLdd %d = lastTransferLength %d\n",
1.444 + totalRecordedSincePause, iBytesSincePauseReportedToLdd, lastTransferLength);
1.445 +
1.446 + iBytesRecordedBeforeLastPause += totalRecordedSincePause;
1.447 + iBytesSincePauseReportedToLdd = 0;
1.448 +
1.449 + if (iPendingRecord)
1.450 + {
1.451 + Ldd()->RecordCallback(0, KErrNone, lastTransferLength); // We can use a NULL tranfer ID when pausing.
1.452 +
1.453 + // The LDD will abandon any other transfers queued so we can mark all buffers as not in use.
1.454 + for (TInt i=0 ; i<iWaveformBufMgr->iNumWaveformBufs ; i++)
1.455 + {
1.456 + TWaveformAudioBuf* buf=&iWaveformBufMgr->iWaveformAudioBuf[i];
1.457 + if (buf->iIsInUse)
1.458 + buf->iIsInUse=EFalse;
1.459 + }
1.460 + iPendingRecord=0;
1.461 + }
1.462 +
1.463 + // Indicate that all request to Windows for recording have been cancelled
1.464 + iCompletedRecordBufHdrMask=0;
1.465 +
1.466 + return(KErrNone);
1.467 + }
1.468 +
1.469 +/**
1.470 +Called from the LDD to resume the recording of data from the sound device following a request to halt recording.
1.471 +Any active transfer would have been aborted when the device was halted so its just a case of re-creating the same setup
1.472 +acheived following StartTransfer().
1.473 +This function is always executed in driver thread context.
1.474 +@return KErrNone if successful, otherwise one of the other system wide error codes.
1.475 +*/
1.476 +TInt DWinsSoundScRxPdd::ResumeTransfer()
1.477 + {
1.478 + __KTRACE_SND(Kern::Printf(">DWinsSoundScRxPdd::ResumeTransfer"));
1.479 +
1.480 + iPendingRecord=0;
1.481 + iCompletedRecordBufHdrMask=0; // All buffers would have completed during pausing (waveInReset) so clear mask.
1.482 + iRecordEnabled=ETrue; // Simply set the flag to enable the windows thread to restart sending buffers to wavein device.
1.483 +
1.484 + // Signal the windows thread to resume recording on the wavein device.
1.485 + RecordThreadCommand(EResume);
1.486 +
1.487 + return(KErrNone);
1.488 + }
1.489 +
1.490 +/**
1.491 +Called from the LDD to power down the sound device.
1.492 +This function is always executed in driver thread context.
1.493 +*/
1.494 +void DWinsSoundScRxPdd::PowerDown()
1.495 + {
1.496 +
1.497 + }
1.498 +
1.499 +/**
1.500 +Called from the LDD to handle a custom configuration request.
1.501 +@param aFunction A number identifying the request.
1.502 +@param aParam A 32-bit value passed to the driver. Its meaning depends on the request.
1.503 +@return KErrNone if successful, otherwise one of the other system wide error codes.
1.504 +*/
1.505 +TInt DWinsSoundScRxPdd::CustomConfig(TInt /*aFunction*/,TAny* /*aParam*/)
1.506 + {
1.507 + return(KErrNotSupported);
1.508 + }
1.509 +
1.510 +/**
1.511 +Called from the LDD to find out how many microseconds of data have been recorded. This is called
1.512 +in the context of the DFC thread.
1.513 +@param aTimeTransferred A reference to a variable into which to place the number of microseconds of audio.
1.514 +@param aStatus The current status of this channel
1.515 +@return KErrNone if time is valid or KErrNotSupported.
1.516 +*/
1.517 +TInt DWinsSoundScRxPdd::TimeTransferred(TInt64& aTimeRecorded, TInt aState)
1.518 + {
1.519 + TInt r=KErrGeneral;
1.520 + TInt64 ms=0;
1.521 +
1.522 + if(iRecordDeviceHandle == 0)
1.523 + {
1.524 + // Recording not started yet
1.525 + aTimeRecorded = 0;
1.526 + return KErrNone;
1.527 + }
1.528 +
1.529 + // Kern::Printf("DWinsSoundScRxPdd::TimeTransferred - (iBytesSincePauseReportedToLdd=%d)\n", iBytesSincePauseReportedToLdd);
1.530 + if (aState == DSoundScLdd::EPaused)
1.531 + {
1.532 + // Kern::Printf("DWinsSoundScRxPdd::TimeTransferred (paused) - iBytesRecordedBeforeLastPause %d\n", iBytesRecordedBeforeLastPause);
1.533 + // Just use the paused number of bytes
1.534 + ms=((iBytesRecordedBeforeLastPause/iBytesPerSecond)*1000);
1.535 + TUint remainder=(iBytesRecordedBeforeLastPause%iBytesPerSecond);
1.536 + ms+=((remainder*1000)/iBytesPerSecond);
1.537 + ms*=1000;
1.538 + aTimeRecorded=ms;
1.539 + r=KErrNone;
1.540 + }
1.541 +
1.542 + TInt64 bytesTransferredSincePause = 0;
1.543 + // If no hardware is present then we need to use iBytesSincePauseReportedToLdd + a fudge factor to allow
1.544 + // the number of bytes processed by the "hardware" within the current transfer.
1.545 + if(iNoHardware)
1.546 + {
1.547 + // Determine the # of milliseconds that have passed since the last timer triggered
1.548 + DWORD currentTime = timeGetTime();
1.549 + DWORD timeSinceLastEvent = (currentTime - iLastTimerEventTime);
1.550 +
1.551 + // Clamp the resulting value to the duration of the timer, to prevent the millisecond count
1.552 + // going backwards if Windows is busy and latency becomes an issue
1.553 + if (timeSinceLastEvent > iSimulatedMsecDuration)
1.554 + timeSinceLastEvent = iSimulatedMsecDuration;
1.555 +
1.556 + bytesTransferredSincePause = iBytesSincePauseReportedToLdd;
1.557 + WAVEHDR *buf = iWaveformBufMgr->iPendingBufList[0];
1.558 + if(buf)
1.559 + {
1.560 + // Add on an estimate of the progress of the current transfer
1.561 + bytesTransferredSincePause += ((buf->dwBufferLength * timeSinceLastEvent) / iSimulatedMsecDuration);
1.562 + }
1.563 + }
1.564 + else
1.565 + {
1.566 + // Get the number of bytes recorded by the Windows audio system
1.567 + MMTIME time;
1.568 + time.wType=TIME_BYTES;
1.569 + if ((waveInGetPosition(iRecordDeviceHandle,&time,sizeof(time)) != MMSYSERR_NOERROR) ||
1.570 + (time.wType != TIME_BYTES))
1.571 + {
1.572 + // If requesting the number of bytes recorded is not supported, wType will be
1.573 + // changed to what was actually returned, so check for this and don't continue
1.574 + // if we got anything other than bytes
1.575 + return KErrNotSupported;
1.576 + }
1.577 + bytesTransferredSincePause = time.u.cb;
1.578 + }
1.579 +
1.580 + // Kern::Printf("DWinsSoundScRxPdd::TimeTransferred - iBytesRecordedBeforeLastPause %d + bytesTransferredSincePause %d total %d (iNoHardware %d)\n",
1.581 + // iBytesRecordedBeforeLastPause, TUint32(bytesTransferredSincePause), TUint32(bytesTransferredSincePause + iBytesRecordedBeforeLastPause), iNoHardware);
1.582 + // Convert the number of bytes recorded into microseconds and return it
1.583 + ms=(((bytesTransferredSincePause + iBytesRecordedBeforeLastPause)/iBytesPerSecond)*1000);
1.584 + TUint64 remainder=((bytesTransferredSincePause + iBytesRecordedBeforeLastPause)%iBytesPerSecond);
1.585 + ms+=((remainder*1000)/iBytesPerSecond);
1.586 + ms*=1000;
1.587 + aTimeRecorded=ms;
1.588 + r=KErrNone;
1.589 +
1.590 + return(r);
1.591 + }
1.592 +
1.593 +/**
1.594 +Prepare the waveform audio buffer for record.
1.595 +@param aRecordDeviceHandle The handle to the waveform audio input device.
1.596 +*/
1.597 +void TWaveformAudioBuf::DoPrepareIn(HWAVEIN aRecordDeviceHandle)
1.598 + {
1.599 + MMRESULT res = waveInPrepareHeader(aRecordDeviceHandle,&iBufHdr,sizeof(WAVEHDR));
1.600 + __KTRACE_SND(Kern::Printf(" waveInPrepareHeader(BufNo:%d Pos:%x Len:%d)-%d",iBufNum,iBufHdr.lpData,iBufHdr.dwBufferLength,res));
1.601 + __ASSERT_ALWAYS(res==MMSYSERR_NOERROR,Kern::Fault("DWinsSoundScTxPddWIPH", res)); //WaveInPrepareHeader error.
1.602 + }
1.603 +
1.604 +/**
1.605 +Cleanup the preparation performed when the waveform audio buffer was prepared for record.
1.606 +@param aRecordDeviceHandle The handle to the waveform audio input device.
1.607 +*/
1.608 +void TWaveformAudioBuf::DoUnprepareIn(HWAVEIN aRecordDeviceHandle)
1.609 + {
1.610 + MMRESULT res = waveInUnprepareHeader(aRecordDeviceHandle,&iBufHdr,sizeof(WAVEHDR));
1.611 + __KTRACE_SND(Kern::Printf(" waveInUnprepareHeader(BufNo:%d)-%d",iBufNum,res));
1.612 + __ASSERT_ALWAYS(res==MMSYSERR_NOERROR,Kern::Fault("DWinsSoundScTxPddWIUH",res)); //WaveInUnprepareHeader error.
1.613 + }
1.614 +
1.615 +/**
1.616 +The waveform input callback function to handle data block transfer completion.
1.617 +This function is always executed in windows thread context.
1.618 +@param aHdr A pointer to the header for the waveform audio buffer just transferred.
1.619 +*/
1.620 +void DWinsSoundScRxPdd::WaveInProc(WAVEHDR* aHdr)
1.621 + {
1.622 + TInt waveBufId=aHdr->dwUser; // Work out which waveform audio buffer is completing.
1.623 + // Kern::Printf("DWinsSoundScRxPdd::WaveInProc waveBufId %d", waveBufId);
1.624 +
1.625 + StartOfInterrupt();
1.626 + iCompletedRecordBufHdrMask|=(1<<waveBufId); // Update the completion status mask
1.627 + iDfc.Add(); // Queue RecordDfc().
1.628 + EndOfInterrupt();
1.629 + }
1.630 +
1.631 +/**
1.632 +The DFC used to handle data block record completion.
1.633 +This function is always executed in driver thread context.
1.634 +@param aPtr A pointer to the physical channel object.
1.635 +*/
1.636 +void DWinsSoundScRxPdd::RecordDfc(TAny* aPtr)
1.637 + {
1.638 + TInt i;
1.639 + DWinsSoundScRxPdd& drv=*(DWinsSoundScRxPdd*)aPtr;
1.640 +
1.641 + // More than 1 transfer may have completed so loop until all completions are handled
1.642 + while (drv.iCompletedRecordBufHdrMask)
1.643 + {
1.644 + // Find the buffer ID of the next transfer that has completed
1.645 + for (i=0 ; i<32 && !(drv.iCompletedRecordBufHdrMask&(1<<i)) ; i++) {}
1.646 + __ASSERT_ALWAYS(i<drv.iWaveformBufMgr->iNumWaveformBufs,PANIC());
1.647 + __e32_atomic_and_ord32(&drv.iCompletedRecordBufHdrMask, ~(1u<<i)); // Clear this bit in the mask
1.648 +
1.649 + // Update the status of the waveform audio buffer which is completing
1.650 + TWaveformAudioBuf& buf=drv.iWaveformBufMgr->iWaveformAudioBuf[i];
1.651 + buf.iIsInUse=EFalse;
1.652 +
1.653 + // Callback the LDD passing the information for the transfer that has completed
1.654 + drv.iPendingRecord--;
1.655 + __KTRACE_SND(Kern::Printf(" Read complete(BufNo:%x Pos:%x Len:%d)",i,buf.iBufHdr.lpData,buf.iBufHdr.dwBufferLength));
1.656 + drv.iBytesSincePauseReportedToLdd += buf.iBufHdr.dwBufferLength;
1.657 + drv.Ldd()->RecordCallback(buf.iTransferID,KErrNone,buf.iBufHdr.dwBufferLength);
1.658 + }
1.659 + }
1.660 +
1.661 +/**
1.662 +Issue a request from the driver thread to the windows thread to execute a command.
1.663 +@param aCommand The identifier of the command to be executed.
1.664 +@param aArg0 A first command argument, its meaning depends on the command.
1.665 +@param aArg1 A second command argument, its meaning depends on the command.
1.666 +@param aArg2 A third command argument, its meaning depends on the command.
1.667 +This function is always executed in driver thread context.
1.668 +*/
1.669 +void DWinsSoundScRxPdd::RecordThreadCommand(TThreadCommand aCommand,TInt aArg0,TInt aArg1,TInt aArg2)
1.670 + {
1.671 + __KTRACE_SND(Kern::Printf(">DWinsSoundScRxPdd:RecordThreadCommand"));
1.672 + iRecordCommand = aCommand;
1.673 + iRecordCommandArg0 = aArg0;
1.674 + iRecordCommandArg1 = aArg1;
1.675 + iRecordCommandArg2 = aArg2;
1.676 +
1.677 + __HOST_LOCK;
1.678 +
1.679 + ReleaseSemaphore(iRecordThreadSem,1,NULL);
1.680 + }
1.681 +
1.682 +/**
1.683 +Pass a value from the windows thread to the driver thread.
1.684 +This function is always executed in windows thread context.
1.685 +@param aError The value to the passed to the driver thread.
1.686 +*/
1.687 +void DWinsSoundScRxPdd::RecordThreadNotifyDriver(TInt aError)
1.688 + {
1.689 + iRecordThreadError = aError;
1.690 + BOOL ret = ReleaseSemaphore(iDriverThreadSem,1,NULL);
1.691 + __ASSERT_ALWAYS(ret == TRUE,PANIC()); //Unexpected Windows Error
1.692 + }
1.693 +
1.694 +#pragma warning(disable : 4702) // unreachable code
1.695 +/**
1.696 +Open the waveform input device for record. Use a default device identifier in order to select a device
1.697 +capable of meeting the current audio configuration.
1.698 +This function can be executed in either driver thread or windows thread context.
1.699 +@pre The data member DWinsSoundScRxPdd::iSoundConfig must be setup with the current audio configuration.
1.700 +*/
1.701 +TInt DWinsSoundScRxPdd::OpenWaveInDevice()
1.702 + {
1.703 + WAVEFORMATEX format;
1.704 + format.wFormatTag = WAVE_FORMAT_PCM;
1.705 + TUint16 bitsPerSample = 8;
1.706 +
1.707 + switch (iSoundConfig.iEncoding)
1.708 + {
1.709 + case ESoundEncoding8BitPCM:
1.710 + break;
1.711 + case ESoundEncoding16BitPCM:
1.712 + bitsPerSample = 16;
1.713 + break;
1.714 + default:
1.715 + return KErrNotSupported;
1.716 + };
1.717 +
1.718 + TInt rateInSamplesPerSecond=RateInSamplesPerSecond(iSoundConfig.iRate);
1.719 + format.nChannels = TUint16(iSoundConfig.iChannels);
1.720 + format.nSamplesPerSec = rateInSamplesPerSecond;
1.721 + format.nAvgBytesPerSec = rateInSamplesPerSecond * iSoundConfig.iChannels * bitsPerSample / 8;
1.722 + format.nBlockAlign = TUint16(iSoundConfig.iChannels * bitsPerSample / 8);
1.723 + format.wBitsPerSample = bitsPerSample;
1.724 + format.cbSize = 0;
1.725 +
1.726 + MMRESULT res = MMSYSERR_NOERROR;
1.727 +
1.728 + __COND_HOST_LOCK;
1.729 + if (iNoHardware)
1.730 + {
1.731 + timeBeginPeriod(KMMTimerRes);
1.732 + iRecordDeviceHandle = (HWAVEIN)1;
1.733 + }
1.734 + else
1.735 + {
1.736 + res = waveInOpen(&iRecordDeviceHandle, WAVE_MAPPER, &format, (DWORD)::WaveInProc, (DWORD)this, CALLBACK_FUNCTION);
1.737 +
1.738 + // On some builds of Windows (such as Windows Server 2003), the waveInGetDevCaps() trick in
1.739 + // DoCreate() won't work, so we have another special check for missing hardware here
1.740 + if ((res == MMSYSERR_NODRIVER) || (res == MMSYSERR_BADDEVICEID))
1.741 + {
1.742 + // Pretend there was no error and switch into hardware emulation mode
1.743 + res = MMSYSERR_NOERROR;
1.744 + iNoHardware = ETrue;
1.745 + iRecordDeviceHandle = (HWAVEIN)1;
1.746 + iWaveformBufMgr->iIsHardware = EFalse;
1.747 + timeBeginPeriod(KMMTimerRes);
1.748 + }
1.749 + }
1.750 +
1.751 + switch (res)
1.752 + {
1.753 + case MMSYSERR_NOERROR: // No error
1.754 + return(KErrNone);
1.755 + case MMSYSERR_ALLOCATED: // Specified resource is already allocated.
1.756 + return(KErrInUse);
1.757 + case WAVERR_BADFORMAT: // Attempted to open with an unsupported waveform-audio format
1.758 + return(KErrNotSupported);
1.759 + case MMSYSERR_NOMEM: // Unable to allocate or lock memory.
1.760 + return(KErrNoMemory);
1.761 + default:
1.762 + return(KErrUnknown);
1.763 + }
1.764 + }
1.765 +#pragma warning(default : 4702) // unreachable code
1.766 +
1.767 +/**
1.768 +Open the audio input device.
1.769 +This function is always executed in driver thread context.
1.770 +@pre The data members DWinsSoundScRxPdd::iSoundConfig must be setup with the current audio configuration.
1.771 +*/
1.772 +TInt DWinsSoundScRxPdd::CreateRecordDevice(TBool aCheckDevice)
1.773 + {
1.774 + // Check if the waveform input device is already open.
1.775 + if (iRecordDeviceHandle)
1.776 + return(KErrNone);
1.777 +
1.778 + __HOST_LOCK;
1.779 +
1.780 + // Open the waveform input device for recording.
1.781 + TInt err = OpenWaveInDevice();
1.782 + if (err != KErrNone)
1.783 + return(err);
1.784 +
1.785 + __HOST_LOCK_OFF;
1.786 +
1.787 + if (!aCheckDevice)
1.788 + {
1.789 + // Now, re-allocate a set of the waveform audio blocks in advance of any recording. Also, prepare one of these
1.790 + // for each buffer within the shared chunk. Need to be in critical section while re-allocating the audio blocks.
1.791 + NKern::ThreadEnterCS();
1.792 + err=iWaveformBufMgr->ReAllocAndUpdate(Ldd()->BufConfig(),Ldd()->ChunkBase(),(TInt)iRecordDeviceHandle);
1.793 + NKern::ThreadLeaveCS();
1.794 + }
1.795 +
1.796 + return(err);
1.797 + }
1.798 +
1.799 +/**
1.800 +Close down the record device.
1.801 +This function is always executed in driver thread context.
1.802 +*/
1.803 +void DWinsSoundScRxPdd::CloseRecordDevice()
1.804 + {
1.805 + __COND_HOST_LOCK;
1.806 +
1.807 + if (iNoHardware)
1.808 + timeEndPeriod(KMMTimerRes);
1.809 +
1.810 + HWAVEIN handle = iRecordDeviceHandle;
1.811 +
1.812 + if (handle)
1.813 + {
1.814 + if (!iNoHardware)
1.815 + waveInReset(handle); // Stop recording.
1.816 +
1.817 + // Un-prepare all the waveform audio buffers.
1.818 + for (TInt i=0 ; i<iWaveformBufMgr->iNumWaveformBufs ; i++)
1.819 + iWaveformBufMgr->iWaveformAudioBuf[i].Unprepare((TInt)handle);
1.820 +
1.821 + if (!iNoHardware)
1.822 + waveInClose(handle); // Close the wavein device.
1.823 +
1.824 + iRecordDeviceHandle = NULL;
1.825 + }
1.826 + }
1.827 +
1.828 +/**
1.829 +The thread function for the record windows thread.
1.830 +This function is always executed in windows thread context.
1.831 +@pre The data members DWinsSoundScRxPdd::iSoundConfig must be setup with the current audio configuration.
1.832 +*/
1.833 +void DWinsSoundScRxPdd::RecordThread()
1.834 + {
1.835 + iRecordThreadSem = CreateSemaphore(NULL,0,0x7fffffff,NULL);
1.836 + __ASSERT_ALWAYS(iRecordThreadSem,PANIC()); //No Windows Memory
1.837 + iRecordTimerEvent=CreateEvent(NULL,TRUE,FALSE,NULL);
1.838 + HANDLE objects[2];
1.839 + objects[0]=iRecordThreadSem; // Indicates command from driver thread
1.840 + objects[1]=iRecordTimerEvent;
1.841 +
1.842 + // Signal driver of successful setup
1.843 + RecordThreadNotifyDriver(KErrNone);
1.844 + ResetEvent(iRecordTimerEvent);
1.845 + FOREVER
1.846 + {
1.847 + DWORD ret=WaitForMultipleObjectsEx(2,objects,FALSE,INFINITE,TRUE);
1.848 +
1.849 + switch (ret)
1.850 + {
1.851 + case WAIT_OBJECT_0: // Command received from the driver thread.
1.852 + if (ProcessRecordCommand(iRecordCommand,iRecordCommandArg0,iRecordCommandArg1,iRecordCommandArg2)==KErrCompletion)
1.853 + return; // ********* Exit thread **************
1.854 + break;
1.855 + case WAIT_OBJECT_0+1:
1.856 + HandleRecordTimerEvent();
1.857 + break;
1.858 + }
1.859 + }
1.860 + }
1.861 +
1.862 +/**
1.863 +Process a request from the driver thread to execute a command.
1.864 +This function is always executed in windows thread context.
1.865 +@param aCommand The identifier of the command to be executed.
1.866 +@param aArg0 A first command argument, its meaning depends on the command.
1.867 +@param aArg1 A second command argument, its meaning depends on the command.
1.868 +@param aArg2 A third command argument, its meaning depends on the command.
1.869 +@return KErrCompletion if the command to exit the windows thread has been received;
1.870 + KErrNone otherwise;
1.871 +*/
1.872 +TInt DWinsSoundScRxPdd::ProcessRecordCommand(TThreadCommand aCommand,TInt aArg0,TInt aArg1,TInt aArg2)
1.873 + {
1.874 + __KTRACE_SND(Kern::Printf(">DWinsSoundScRxPdd:ProcessRecordCommand(%d)",aCommand));
1.875 + switch(aCommand)
1.876 + {
1.877 + case ERecData: // Initiate the recording of a buffers worth of data from the wavein device.
1.878 + {
1.879 + if (iRecordEnabled)
1.880 + {
1.881 + // Acquire a windows waveform audio buffer for the transfer.
1.882 + char* startAddress=(char*)aArg1;
1.883 + TInt bytesToRecord=aArg2;
1.884 + __ASSERT_ALWAYS(bytesToRecord>0,PANIC());
1.885 + TWaveformAudioBuf* waveformAudioBuf=iWaveformBufMgr->AcquireBuf(startAddress,bytesToRecord,(TInt)iRecordDeviceHandle);
1.886 + waveformAudioBuf->iTransferID=(TUint)aArg0;
1.887 + waveformAudioBuf->iBufHdr.dwBufferLength=bytesToRecord;
1.888 +
1.889 + if (!iNoHardware)
1.890 + {
1.891 + // This machine has a wavein device present. Send the buffer to the wavein device.
1.892 + waveInStart(iRecordDeviceHandle); // Start input on the wavein device - safe to call this when already started.
1.893 + MMRESULT res = waveInAddBuffer(iRecordDeviceHandle,&waveformAudioBuf->iBufHdr,sizeof(WAVEHDR));
1.894 + __KTRACE_SND(Kern::Printf(" waveInAddBuffer(ID:%x Pos:%x Len:%d)-%d",aArg0,startAddress,bytesToRecord,res));
1.895 + __ASSERT_ALWAYS(res == MMSYSERR_NOERROR,PANIC()); //WaveInAddBuffer Error
1.896 + }
1.897 + else
1.898 + {
1.899 + // This machine has no audio hardware present so simulate the wavein device using a timer.
1.900 + AddToPendingList(&waveformAudioBuf->iBufHdr,iWaveformBufMgr->iPendingBufList); // Queue the buffer on the pending list
1.901 +
1.902 + // Check if the timer needs starting/re-starting
1.903 + if (!iTimerActive)
1.904 + {
1.905 + iLastTimerEventTime = timeGetTime();
1.906 + StartTimer(&waveformAudioBuf->iBufHdr);
1.907 + }
1.908 + }
1.909 + }
1.910 +
1.911 + // Signal the driver thread that we have completed the command.
1.912 + RecordThreadNotifyDriver(KErrNone);
1.913 + break;
1.914 + }
1.915 +
1.916 +
1.917 + case EStop: // Terminate the recording of data from the wavein device.
1.918 + {
1.919 + iRecordEnabled=EFalse; // Stop the windows thread from sending any more buffers to wavein device.
1.920 +
1.921 + if (iNoHardware)
1.922 + {
1.923 + // This machine has no audio hardware present so simulates the waveout device using a timer.
1.924 + StopTimer(ETrue); // Stop the timer and cancel any buffers pending
1.925 + }
1.926 +
1.927 + // Leave the driver thread to close down the record device.
1.928 +
1.929 + // Signal the driver thread that we have completed the command.
1.930 + if (iStopSemaphore)
1.931 + {
1.932 + LONG prev;
1.933 + ReleaseSemaphore(iStopSemaphore,1,&prev);
1.934 + }
1.935 + break;
1.936 + }
1.937 +
1.938 + case EExit: // Close down the record device and exit the windows thread.
1.939 + {
1.940 + if (!iNoHardware)
1.941 + {
1.942 + // This machine has a wavein device present.
1.943 + if (iRecordDeviceHandle)
1.944 + {
1.945 + waveInReset(iRecordDeviceHandle); // Stop recording on the wavein device.
1.946 + waveInClose(iRecordDeviceHandle); // Close the wavein device.
1.947 + }
1.948 + }
1.949 + else
1.950 + {
1.951 + // This machine has no audio hardware present so simulates the waveout device using a timer.
1.952 + StopTimer(ETrue); // Stop the timer and cancel any buffers pending.
1.953 + }
1.954 + // Logically the record device is now shut so clear the handle.
1.955 + iRecordDeviceHandle = 0;
1.956 +
1.957 + // Signal the driver thread that we have completed the command.
1.958 + if (iDeathSemaphore)
1.959 + {
1.960 + LONG prev;
1.961 + ReleaseSemaphore(iDeathSemaphore,1,&prev);
1.962 + }
1.963 + return(KErrCompletion); // ********* Exit thread **************
1.964 + }
1.965 +
1.966 + case EPause: // Halt the recording of data from the wavein device.
1.967 + iRecordEnabled=EFalse;
1.968 +
1.969 + DWORD position;
1.970 + if (!iNoHardware)
1.971 + {
1.972 + // Need to try to work out how much of the current audio buffer has been filled.
1.973 + MMTIME time;
1.974 + time.wType = TIME_BYTES;
1.975 + HWAVEIN handle = iRecordDeviceHandle;
1.976 + waveInGetPosition(handle,&time,sizeof(MMTIME));
1.977 + position = time.u.cb;
1.978 +
1.979 + // Stop recording. (Windows will mark all pending audio buffers as done).
1.980 + waveInReset(handle);
1.981 + }
1.982 + else
1.983 + {
1.984 + // This machine has no audio hardware present so simulates the waveout device using a timer.
1.985 +
1.986 + // Determine the # of milliseconds that have passed since the last timer triggered
1.987 + DWORD currentTime = timeGetTime();
1.988 + DWORD timeSinceLastEvent = (currentTime - iLastTimerEventTime);
1.989 +
1.990 + // Clamp the resulting value to the duration of the timer, to prevent the millisecond count
1.991 + // going backwards if Windows is busy and latency becomes an issue
1.992 + if (timeSinceLastEvent > iSimulatedMsecDuration)
1.993 + timeSinceLastEvent = iSimulatedMsecDuration;
1.994 +
1.995 + TUint bytesTransferredSincePause = iBytesSincePauseReportedToLdd;
1.996 + WAVEHDR *buf = iWaveformBufMgr->iPendingBufList[0];
1.997 + if(buf)
1.998 + {
1.999 + // Add on an estimate of the progress of the current transfer
1.1000 + bytesTransferredSincePause += ((buf->dwBufferLength * timeSinceLastEvent) / iSimulatedMsecDuration);
1.1001 + }
1.1002 +
1.1003 + position = bytesTransferredSincePause;
1.1004 +
1.1005 + StopTimer(ETrue); // Stop the timer and cancel any buffers pending
1.1006 + }
1.1007 +
1.1008 + // Signal the driver thread that we have stopped recording - returning info. on any partially filled buffer.
1.1009 + RecordThreadNotifyDriver(position);
1.1010 + break;
1.1011 +
1.1012 + case EResume:
1.1013 + if (iNoHardware)
1.1014 + {
1.1015 + // Determine how long we were paused for and add that time to the time the timer last
1.1016 + // triggered. This will allow us to continue as though we had never been paused
1.1017 + iLastTimerEventTime = timeGetTime();
1.1018 + }
1.1019 +
1.1020 + break;
1.1021 + }
1.1022 + return(KErrNone);
1.1023 + }
1.1024 +
1.1025 +/**
1.1026 +Handle a timer expiry event. This is only used when no audio hardware is present, with a timer expiry corresponding
1.1027 +to the end of a data block transfer.
1.1028 +This function is always executed in windows thread context.
1.1029 +*/
1.1030 +void DWinsSoundScRxPdd::HandleRecordTimerEvent()
1.1031 + {
1.1032 + __KTRACE_SND(Kern::Printf(">DWinsSoundScRxPdd:HandleRecordTimerEvent"));
1.1033 + ResetEvent(iRecordTimerEvent); // Reset the event
1.1034 +
1.1035 + // Remove the audio buffer just filled from the pending list and save it for the driver thread.
1.1036 + WAVEHDR* buf=RemoveFromPendingList(iWaveformBufMgr->iPendingBufList);
1.1037 + __ASSERT_ALWAYS(buf != NULL,PANIC());
1.1038 + TInt waveBufId=buf->dwUser; // Work out which waveform audio buffer is completing.
1.1039 +
1.1040 + // Check if there are more audio buffers waiting to be played
1.1041 + buf=iWaveformBufMgr->iPendingBufList[0];
1.1042 + if (buf)
1.1043 + {
1.1044 + iLastTimerEventTime = timeGetTime();
1.1045 + StartTimer(buf); // Re-start the timer
1.1046 + }
1.1047 + else
1.1048 + iTimerActive=EFalse;
1.1049 +
1.1050 + // Notify that another audio buffer has been filled.
1.1051 + StartOfInterrupt();
1.1052 + iCompletedRecordBufHdrMask|=(1<<waveBufId); // Update the completion status mask
1.1053 + iDfc.Add();
1.1054 + EndOfInterrupt();
1.1055 + return;
1.1056 + }
1.1057 +
1.1058 +/**
1.1059 +Initialise the data member DWinsSoundScRxPdd::iCaps with the capabilities of this audio device.
1.1060 +*/
1.1061 +void DWinsSoundScRxPdd::SetCaps()
1.1062 + {
1.1063 + __KTRACE_SND(Kern::Printf(">DWinsSoundScRxPdd::SetCaps"));
1.1064 +
1.1065 + // The data transfer direction for this unit is record.
1.1066 + iCaps.iDirection=ESoundDirRecord;
1.1067 +
1.1068 + // Assume this unit supports mono or stereo.
1.1069 + iCaps.iChannels=KSoundMonoChannel|KSoundStereoChannel;
1.1070 +
1.1071 + // Assume this unit supports all sample rates.
1.1072 + iCaps.iRates=(KSoundRate7350Hz|KSoundRate8000Hz|KSoundRate8820Hz|KSoundRate9600Hz|KSoundRate11025Hz|
1.1073 + KSoundRate12000Hz|KSoundRate14700Hz|KSoundRate16000Hz|KSoundRate22050Hz|KSoundRate24000Hz|
1.1074 + KSoundRate29400Hz|KSoundRate32000Hz|KSoundRate44100Hz|KSoundRate48000Hz);
1.1075 +
1.1076 + // Assume this unit supports 8bit and 16bit PCM encoding.
1.1077 + iCaps.iEncodings=(KSoundEncoding8BitPCM|KSoundEncoding16BitPCM);
1.1078 +
1.1079 + // This unit only supports interleaved data format
1.1080 + iCaps.iDataFormats=KSoundDataFormatInterleaved;
1.1081 +
1.1082 + // The minimum request size that the device can support.
1.1083 + iCaps.iRequestMinSize=0; // No restriction
1.1084 +
1.1085 + // The request alignment that this device requires.
1.1086 + iCaps.iRequestAlignment=0; // No restriction
1.1087 +
1.1088 + // This unit is not capable of detecting changes in hardware configuration.
1.1089 + iCaps.iHwConfigNotificationSupport=EFalse;
1.1090 + }
1.1091 +/**
1.1092 +Start the audio timer.
1.1093 +The timer is only used when no audio hardware is present on the machine. This is in order to introduce a delay which is
1.1094 +equivelent to that incurred when transferring audio data over a real audio device.
1.1095 +@param aBuffer The audio buffer which would have been transferred had a real audio device been present. This contains
1.1096 + information on the number of bytes to transfer.
1.1097 +*/
1.1098 +void DWinsSoundScRxPdd::StartTimer(WAVEHDR* aBuffer)
1.1099 + {
1.1100 + // First, need to calculate the duration of the timer in milliseconds.
1.1101 + TInt bytesToPlay=aBuffer->dwBufferLength;
1.1102 + iSimulatedMsecDuration = bytesToPlay*1000;
1.1103 + iSimulatedMsecDuration /= (RateInSamplesPerSecond(iSoundConfig.iRate) * iSoundConfig.iChannels);
1.1104 + if (iSoundConfig.iEncoding==ESoundEncoding16BitPCM)
1.1105 + iSimulatedMsecDuration /= 2;
1.1106 + if (iSoundConfig.iEncoding==ESoundEncoding24BitPCM)
1.1107 + iSimulatedMsecDuration /= 3;
1.1108 + if (iSimulatedMsecDuration<=0)
1.1109 + iSimulatedMsecDuration=1; // Round up to 1ms or timeSetEvent() will return an error.
1.1110 +
1.1111 + MMRESULT res = timeSetEvent(iSimulatedMsecDuration, KMMTimerRes, (LPTIMECALLBACK)iRecordTimerEvent, 0, TIME_ONESHOT | TIME_CALLBACK_EVENT_SET);
1.1112 + __ASSERT_ALWAYS(res != NULL,PANIC()); // timeSetEvent error.
1.1113 + iTimerID = res; // Save the identifier for the new timer event.
1.1114 + iTimerActive=ETrue;
1.1115 + }
1.1116 +
1.1117 +/**
1.1118 +Stop the audio timer.
1.1119 +The timer is only used when no audio hardware is present on the machine. This is in order to introduce a delay which is
1.1120 +equivelent to that incurred when transferring audio data over a real audio device.
1.1121 +@param aCancellAll Set to ETrue in order to discard any buffers queued on the pending buffer list. EFalse otherwise.
1.1122 +*/
1.1123 +void DWinsSoundScRxPdd::StopTimer(TBool aCancelAll)
1.1124 + {
1.1125 + if (iTimerActive)
1.1126 + {
1.1127 + MMRESULT res = timeKillEvent(iTimerID);
1.1128 + __ASSERT_ALWAYS(res == TIMERR_NOERROR,PANIC()); // timeKillEvent error
1.1129 +
1.1130 + if (aCancelAll)
1.1131 + {
1.1132 + WAVEHDR* b;
1.1133 + do
1.1134 + b=RemoveFromPendingList(iWaveformBufMgr->iPendingBufList);
1.1135 + while(b);
1.1136 + }
1.1137 + }
1.1138 + iTimerActive=EFalse;
1.1139 + }
1.1140 +
1.1141 +