First public contribution.
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_rx.cpp
15 // Emulator record functions for the shared chunk sound driver PDD.
25 #include "winssoundsc.h"
28 The thread function for the record windows thread.
29 This function is always executed in windows thread context.
31 LOCAL_C TUint RecordThreadFunction(DWinsSoundScRxPdd *aSoundPdd)
33 aSoundPdd->RecordThread();
38 The waveform input callback function. This can receive the following messages:-
39 WIM_OPEN when the input device is opened, WIM_CLOSE when the input device is closed,
40 and WIM_DATA each time a record data block has been filled (i.e. completion of waveInAddBuffer).
41 This function is always executed in windows thread context.
43 LOCAL_C void CALLBACK WaveInProc(HWAVEIN /*hwi*/, UINT uMsg, DWORD dwInstance, DWORD dwParam1, DWORD /*dwParam2*/)
47 DWinsSoundScRxPdd * pdd = (DWinsSoundScRxPdd*)dwInstance;
48 pdd->WaveInProc((WAVEHDR*)dwParam1);
53 Constructor for the WINS shared chunk record PDD.
54 This function is always executed in driver thread context.
56 DWinsSoundScRxPdd::DWinsSoundScRxPdd()
57 : iDfc(DWinsSoundScRxPdd::RecordDfc,this,2)
59 __KTRACE_SND(Kern::Printf(">DWinsSoundScRxPdd::DWinsSoundScRxPdd"));
61 // iDriverThreadSem=0;
63 // iRecordThreadMutex=0;
64 // iRecordThreadSem=0;
67 // iRecordDeviceHandle=0;
68 // iRecordCommand=ERecData;
69 // iRecordCommandArg0=0;
70 // iRecordCommandArg1=0;
72 // iRecordThreadError=0;
73 /// iWaveformBufMgr=NULL;
74 // iCompletedRecordBufHdrMask=0;
75 // iRecordBufferSize=0;
76 // iRecordEnabled=EFalse;
77 // iNoHardware=EFalse;
78 // iRecordTimerEvent=0;
80 // iTimerActive=EFalse;
84 Destructor for the WINS shared chunk record PDD.
85 This function is always executed in driver thread context.
87 DWinsSoundScRxPdd::~DWinsSoundScRxPdd()
89 // If the Windows thread started up successfully, signal it to shut down and wait for it to do so
90 if (iRecordThreadRunning)
92 // Signal the windows thread to close down the record device and exit the windows thread.
93 iDeathSemaphore = CreateSemaphore(NULL, 0, 2, NULL);
94 RecordThreadCommand(EExit);
96 // Wait for the record thread to terminate.
100 WaitForSingleObject(iDeathSemaphore, INFINITE);
104 CloseHandle(iDeathSemaphore);
108 if (iRecordTimerEvent)
109 CloseHandle(iRecordTimerEvent);
110 if (iRecordThreadSem)
111 CloseHandle(iRecordThreadSem);
113 CloseHandle(iRecordThread);
114 if (iDriverThreadSem)
115 CloseHandle(iDriverThreadSem);
118 delete iWaveformBufMgr;
122 Second stage constructor for the WINS shared chunk record PDD.
123 Note that this constructor is called before the second stage constructor for the LDD so it is not
124 possible to call methods on the LDD here.
125 This function is always executed in driver thread context.
126 @param aPhysicalDevice A pointer to the factory class that is creating this PDD.
127 @return KErrNone if successful, otherwise one of the other system wide error codes.
129 TInt DWinsSoundScRxPdd::DoCreate(DWinsSoundScPddFactory* aPhysicalDevice)
131 __KTRACE_SND(Kern::Printf(">DWinsSoundScRxPdd::DoCreate"));
133 iPhysicalDevice=aPhysicalDevice;
135 // Set up the correct DFC queue.
136 iDfc.SetDfcQ(iPhysicalDevice->iDfcQ);
138 SetCaps(); // Setup the capabilities of this device.
140 // Setup the default audio configuration
141 iSoundConfig.iChannels=2;
142 iSoundConfig.iRate=ESoundRate48000Hz;
143 iSoundConfig.iEncoding=ESoundEncoding16BitPCM;
144 iSoundConfig.iDataFormat=ESoundDataFormatInterleaved;
148 // Query the waveform device capabilities using the default device identifier in order
149 // to check if there is a functioning waveform device present. Note that some versions of
150 // Windows (such as Windows Server 2003) will actually return MMSYSERR_NOERROR when this is
151 // called, even if there is no waveform device present, so we have a further check in
152 // when waveInOpen() is called
153 WAVEINCAPS waveInCaps;
154 MMRESULT res = waveInGetDevCaps(WAVE_MAPPER,&waveInCaps,sizeof(WAVEINCAPS));
155 #ifdef FORCE_NO_HARDWARE
156 res=MMSYSERR_NOERROR+1;
158 if (res != MMSYSERR_NOERROR)
163 // Create the windows waveform audio buffer manager.
164 iWaveformBufMgr=new TWaveformBufMgr(ESoundDirRecord,!iNoHardware);
165 if (!iWaveformBufMgr)
166 return(KErrNoMemory);
168 // Create the driver thread semaphore.
169 iDriverThreadSem = CreateSemaphore(NULL,0,0x7fffffff,NULL);
170 if (!iDriverThreadSem)
171 return(KErrNoMemory);
173 // Create the record windows thread.
174 if ((iRecordThread=CreateWin32Thread(EThreadEvent,(LPTHREAD_START_ROUTINE)RecordThreadFunction,(void *)this, FALSE))==NULL)
175 return(Emulator::LastError());
176 SetThreadPriority(iRecordThread,THREAD_PRIORITY_HIGHEST);
177 __ASSERT_ALWAYS( ResumeThread(iRecordThread) != 0xffffffff, PANIC()); //Windows Unexpected Error
179 // Wait to be notified of successful thread initialization
181 WaitForSingleObject(iDriverThreadSem,INFINITE);
184 // If the Windows thread started up successfully, indicate this fact so that when shutting down we know
185 // to signal to the thread to exit
186 if (iRecordThreadError == KErrNone)
187 iRecordThreadRunning = ETrue;
189 return(iRecordThreadError);
193 Called from the LDD to return the DFC queue to be used by this device.
194 This function is always executed in driver thread context.
195 @return The DFC queue to use.
197 TDfcQue* DWinsSoundScRxPdd::DfcQ(TInt /*aUnit*/)
199 return(iPhysicalDevice->iDfcQ);
203 Called from the LDD to return the shared chunk create information to be used by this device.
204 This function is always executed in driver thread context.
205 @param aChunkCreateInfo A chunk create info. object to be to be filled with the settings
206 required for this device.
208 void DWinsSoundScRxPdd::GetChunkCreateInfo(TChunkCreateInfo& aChunkCreateInfo)
210 __KTRACE_SND(Kern::Printf(">DWinsSoundScRxPdd::GetChunkCreateInfo"));
212 aChunkCreateInfo.iType=TChunkCreateInfo::ESharedKernelMultiple;
213 aChunkCreateInfo.iMapAttr=0;
214 aChunkCreateInfo.iOwnsMemory=ETrue; // Using RAM pages.
215 aChunkCreateInfo.iDestroyedDfc=NULL; // No chunk destroy DFC.
219 Called from the LDD to return the capabilities of this device.
220 This function is always executed in driver thread context.
221 @param aCapsBuf A packaged TSoundFormatsSupportedV02 object to be filled with the record
222 capabilities of this device. This descriptor is in kernel memory and can be accessed directly.
223 @see TSoundFormatsSupportedV02.
225 void DWinsSoundScRxPdd::Caps(TDes8& aCapsBuf) const
227 __KTRACE_SND(Kern::Printf(">DWinsSoundScRxPdd::Caps"));
230 TPtrC8 ptr((const TUint8*)&iCaps,sizeof(iCaps));
231 aCapsBuf.FillZ(aCapsBuf.MaxLength());
232 aCapsBuf=ptr.Left(Min(ptr.Length(),aCapsBuf.MaxLength()));
236 Called from the LDD to return the maximum transfer length in bytes that this device can support in a single data transfer.
237 @return The maximum transfer length in bytes.
239 TInt DWinsSoundScRxPdd::MaxTransferLen() const
241 return(KWinsMaxAudioTransferLen); // 32K
245 Called from the LDD to power up the sound device.
246 This function is always executed in driver thread context.
247 @return KErrNone if successful, otherwise one of the other system wide error codes.
249 TInt DWinsSoundScRxPdd::PowerUp()
255 Called from the LDD to configure or reconfigure the device using the the configuration supplied.
256 This function is always executed in driver thread context.
257 @param aConfigBuf A packaged TCurrentSoundFormatV02 object which contains the new configuration settings.
258 This descriptor is in kernel memory and can be accessed directly.
259 @return KErrNone if successful, otherwise one of the other system wide error codes.
260 @see TCurrentSoundFormatV02.
262 TInt DWinsSoundScRxPdd::SetConfig(const TDesC8& aConfigBuf)
264 __KTRACE_SND(Kern::Printf(">DWinsSoundScRxPdd::SetConfig"));
266 // Cannot change the configuration while the device is open and recording. (LDD should prevent
267 // this anyway but better safe than sorry).
268 if (iRecordDeviceHandle)
271 // Save the current settings so we can restore them if there is a problem with the new ones.
272 TCurrentSoundFormatV02 saved=iSoundConfig;
274 // Read the new configuration from the LDD.
275 TPtr8 ptr((TUint8*)&iSoundConfig,sizeof(iSoundConfig));
276 Kern::InfoCopy(ptr,aConfigBuf);
278 // Open the record device with the new settings to check they are supported. Then close it
279 // again - don't leave it open yet.
280 TInt r = CreateRecordDevice(ETrue);
284 iSoundConfig=saved; // Restore the previous settings
290 Called from the LDD to set the record level.
291 This function is always executed in driver thread context.
292 @param aLevel The record level to be set - a value in the range 0 to 255. The value 255 equates
293 to the maximum record level and each value below this equates to a 0.5dB step below it.
294 @return KErrNone if successful, otherwise one of the other system wide error codes.
296 TInt DWinsSoundScRxPdd::SetVolume(TInt /*aVolume*/)
298 __KTRACE_SND(Kern::Printf(">DWinsSoundScRxPdd::SetVolume"));
300 // There's no adjustment of the record level on the wave in device.
306 Called from the LDD to prepare the audio device for recording.
307 This function is always executed in driver thread context.
308 @return KErrNone if successful, otherwise one of the other system wide error codes.
310 TInt DWinsSoundScRxPdd::StartTransfer()
312 __KTRACE_SND(Kern::Printf(">DWinsSoundScRxPdd::StartTransfer"));
314 // Convert the enum representing the current sample rate into an integer
315 TInt samplesPerSecond=RateInSamplesPerSecond(iSoundConfig.iRate);
316 if (samplesPerSecond==0)
317 return(KErrNotSupported);
319 // Now convert the sample rate into the number of bytes per second and save for later use
320 iBytesPerSecond=samplesPerSecond;
321 if (iSoundConfig.iChannels==2)
323 if (iSoundConfig.iEncoding==ESoundEncoding16BitPCM)
326 iBytesRecordedBeforeLastPause = 0;
327 iBytesSincePauseReportedToLdd = 0;
329 iRecordEnabled=ETrue;
331 // Open the record device with the current settings.
333 iCompletedRecordBufHdrMask=0; // Reset the completion status mask
334 TInt r = CreateRecordDevice();
339 Called from the LDD to initiate the recording of a portion of data from the audio device.
340 When the transfer is complete, the PDD signals this event using the LDD function RecordCallback().
341 This function is always executed in driver thread context.
342 @param aTransferID A value assigned by the LDD to allow it to uniquely identify a particular transfer fragment.
343 @param aLinAddr The linear address within the shared chunk for storing the recorded data.
344 @param aPhysAddr The physical address within the shared chunk for storing the recorded data.
345 @param aNumBytes The number of bytes to be recorded.
346 @return KErrNone if the transfer has been initiated successfully;
347 KErrNotReady if the device is unable to accept the transfer for the moment;
348 otherwise one of the other system-wide error codes.
350 TInt DWinsSoundScRxPdd::TransferData(TUint aTransferID,TLinAddr aLinAddr,TPhysAddr /*aPhysAddr*/,TInt aNumBytes)
352 __KTRACE_SND(Kern::Printf(">DWinsSoundScRxPdd::TransferData(ID:%xH)",aTransferID));
354 // Check that we can accept the request
355 if (aNumBytes>KWinsMaxAudioTransferLen)
356 return(KErrArgument);
357 if (iPendingRecord>=iWaveformBufMgr->iNumWaveformBufs) // LDD may issue multiple data transfers per buffer.
358 return(KErrNotReady);
360 // Signal the windows thread to initiate the recording of a buffers worth of data from the wavein device.
362 RecordThreadCommand(ERecData,aTransferID,aLinAddr,aNumBytes);
364 // Although the windows thread runs at a higher priority, its not safe to assume we will always get pre-empted at this
365 // point while the the higher priority thread processes and completes the request. Instead we need to wait until it
366 // signals back completion of the command.
368 WaitForSingleObject(iDriverThreadSem,INFINITE);
371 return(iRecordThreadError);
375 Called from the LDD to terminate the recording of a data from the device and to release any resources necessary for
377 The LDD will leave the audio device capturing record data even when there are no record requests pending from the client.
378 Transfer will only be terminated when the client either issues RSoundSc::CancelRecordData() or closes the channel. Once
379 this function had been called, the LDD will not issue any further TransferData() commands without first issueing a
380 StartTransfer() command.
381 This function is always executed in driver thread context.
383 void DWinsSoundScRxPdd::StopTransfer()
385 __KTRACE_SND(Kern::Printf(">DWinsSoundScRxPdd::StopTransfer"));
387 // Signal the windows thread to stop it from sending any more buffers to wavein device.
388 iStopSemaphore = CreateSemaphore(NULL, 0, 1, NULL);
389 RecordThreadCommand(EStop);
391 // Need to wait for the record thread to finish using the record handle before it's safe to close the device and
392 // set the handle to NULL.
395 // Wait for the record thread to stop.
397 WaitForSingleObject(iStopSemaphore, INFINITE);
401 CloseHandle(iStopSemaphore);
402 iStopSemaphore = NULL;
405 // Make sure the DFC is not queued.
408 CloseRecordDevice(); // Close down the record device.
410 iCompletedRecordBufHdrMask=0; // Reset the completion status mask
414 Called from the LDD to halt the recording of data from the sound device but not to release any resources necessary for
416 All active transfers should be aborted. When recording is halted the PDD signals this event with a single call of the LDD
417 function RecordCallback() - reporting back any partial data already received. If transfer is resumed later, the LDD will
418 issue a new TransferData() request to re-commence data transfer.
419 This function is always executed in driver thread context.
420 @return KErrNone if successful, otherwise one of the other system wide error codes.
422 TInt DWinsSoundScRxPdd::PauseTransfer()
424 __KTRACE_SND(Kern::Printf(">DWinsSoundScRxPdd::PauseTransfer"));
426 // Signal the windows thread to stop recording on the wavein device - aborting any transfers queued.
427 RecordThreadCommand(EPause);
429 // Wait for the windows thread to complete the request
431 WaitForSingleObject(iDriverThreadSem,INFINITE);
434 // Make sure the DFC is not queued.
437 // The windows thread returns the total bytes recorded since the last pause (as reported by windows).
438 TUint totalRecordedSincePause = iRecordThreadError;
439 TUint lastTransferLength = totalRecordedSincePause - iBytesSincePauseReportedToLdd;
440 Kern::Printf("totalRecordedSincePause %d - iBytesSincePauseReportedToLdd %d = lastTransferLength %d\n",
441 totalRecordedSincePause, iBytesSincePauseReportedToLdd, lastTransferLength);
443 iBytesRecordedBeforeLastPause += totalRecordedSincePause;
444 iBytesSincePauseReportedToLdd = 0;
448 Ldd()->RecordCallback(0, KErrNone, lastTransferLength); // We can use a NULL tranfer ID when pausing.
450 // The LDD will abandon any other transfers queued so we can mark all buffers as not in use.
451 for (TInt i=0 ; i<iWaveformBufMgr->iNumWaveformBufs ; i++)
453 TWaveformAudioBuf* buf=&iWaveformBufMgr->iWaveformAudioBuf[i];
455 buf->iIsInUse=EFalse;
460 // Indicate that all request to Windows for recording have been cancelled
461 iCompletedRecordBufHdrMask=0;
467 Called from the LDD to resume the recording of data from the sound device following a request to halt recording.
468 Any active transfer would have been aborted when the device was halted so its just a case of re-creating the same setup
469 acheived following StartTransfer().
470 This function is always executed in driver thread context.
471 @return KErrNone if successful, otherwise one of the other system wide error codes.
473 TInt DWinsSoundScRxPdd::ResumeTransfer()
475 __KTRACE_SND(Kern::Printf(">DWinsSoundScRxPdd::ResumeTransfer"));
478 iCompletedRecordBufHdrMask=0; // All buffers would have completed during pausing (waveInReset) so clear mask.
479 iRecordEnabled=ETrue; // Simply set the flag to enable the windows thread to restart sending buffers to wavein device.
481 // Signal the windows thread to resume recording on the wavein device.
482 RecordThreadCommand(EResume);
488 Called from the LDD to power down the sound device.
489 This function is always executed in driver thread context.
491 void DWinsSoundScRxPdd::PowerDown()
497 Called from the LDD to handle a custom configuration request.
498 @param aFunction A number identifying the request.
499 @param aParam A 32-bit value passed to the driver. Its meaning depends on the request.
500 @return KErrNone if successful, otherwise one of the other system wide error codes.
502 TInt DWinsSoundScRxPdd::CustomConfig(TInt /*aFunction*/,TAny* /*aParam*/)
504 return(KErrNotSupported);
508 Called from the LDD to find out how many microseconds of data have been recorded. This is called
509 in the context of the DFC thread.
510 @param aTimeTransferred A reference to a variable into which to place the number of microseconds of audio.
511 @param aStatus The current status of this channel
512 @return KErrNone if time is valid or KErrNotSupported.
514 TInt DWinsSoundScRxPdd::TimeTransferred(TInt64& aTimeRecorded, TInt aState)
519 if(iRecordDeviceHandle == 0)
521 // Recording not started yet
526 // Kern::Printf("DWinsSoundScRxPdd::TimeTransferred - (iBytesSincePauseReportedToLdd=%d)\n", iBytesSincePauseReportedToLdd);
527 if (aState == DSoundScLdd::EPaused)
529 // Kern::Printf("DWinsSoundScRxPdd::TimeTransferred (paused) - iBytesRecordedBeforeLastPause %d\n", iBytesRecordedBeforeLastPause);
530 // Just use the paused number of bytes
531 ms=((iBytesRecordedBeforeLastPause/iBytesPerSecond)*1000);
532 TUint remainder=(iBytesRecordedBeforeLastPause%iBytesPerSecond);
533 ms+=((remainder*1000)/iBytesPerSecond);
539 TInt64 bytesTransferredSincePause = 0;
540 // If no hardware is present then we need to use iBytesSincePauseReportedToLdd + a fudge factor to allow
541 // the number of bytes processed by the "hardware" within the current transfer.
544 // Determine the # of milliseconds that have passed since the last timer triggered
545 DWORD currentTime = timeGetTime();
546 DWORD timeSinceLastEvent = (currentTime - iLastTimerEventTime);
548 // Clamp the resulting value to the duration of the timer, to prevent the millisecond count
549 // going backwards if Windows is busy and latency becomes an issue
550 if (timeSinceLastEvent > iSimulatedMsecDuration)
551 timeSinceLastEvent = iSimulatedMsecDuration;
553 bytesTransferredSincePause = iBytesSincePauseReportedToLdd;
554 WAVEHDR *buf = iWaveformBufMgr->iPendingBufList[0];
557 // Add on an estimate of the progress of the current transfer
558 bytesTransferredSincePause += ((buf->dwBufferLength * timeSinceLastEvent) / iSimulatedMsecDuration);
563 // Get the number of bytes recorded by the Windows audio system
565 time.wType=TIME_BYTES;
566 if ((waveInGetPosition(iRecordDeviceHandle,&time,sizeof(time)) != MMSYSERR_NOERROR) ||
567 (time.wType != TIME_BYTES))
569 // If requesting the number of bytes recorded is not supported, wType will be
570 // changed to what was actually returned, so check for this and don't continue
571 // if we got anything other than bytes
572 return KErrNotSupported;
574 bytesTransferredSincePause = time.u.cb;
577 // Kern::Printf("DWinsSoundScRxPdd::TimeTransferred - iBytesRecordedBeforeLastPause %d + bytesTransferredSincePause %d total %d (iNoHardware %d)\n",
578 // iBytesRecordedBeforeLastPause, TUint32(bytesTransferredSincePause), TUint32(bytesTransferredSincePause + iBytesRecordedBeforeLastPause), iNoHardware);
579 // Convert the number of bytes recorded into microseconds and return it
580 ms=(((bytesTransferredSincePause + iBytesRecordedBeforeLastPause)/iBytesPerSecond)*1000);
581 TUint64 remainder=((bytesTransferredSincePause + iBytesRecordedBeforeLastPause)%iBytesPerSecond);
582 ms+=((remainder*1000)/iBytesPerSecond);
591 Prepare the waveform audio buffer for record.
592 @param aRecordDeviceHandle The handle to the waveform audio input device.
594 void TWaveformAudioBuf::DoPrepareIn(HWAVEIN aRecordDeviceHandle)
596 MMRESULT res = waveInPrepareHeader(aRecordDeviceHandle,&iBufHdr,sizeof(WAVEHDR));
597 __KTRACE_SND(Kern::Printf(" waveInPrepareHeader(BufNo:%d Pos:%x Len:%d)-%d",iBufNum,iBufHdr.lpData,iBufHdr.dwBufferLength,res));
598 __ASSERT_ALWAYS(res==MMSYSERR_NOERROR,Kern::Fault("DWinsSoundScTxPddWIPH", res)); //WaveInPrepareHeader error.
602 Cleanup the preparation performed when the waveform audio buffer was prepared for record.
603 @param aRecordDeviceHandle The handle to the waveform audio input device.
605 void TWaveformAudioBuf::DoUnprepareIn(HWAVEIN aRecordDeviceHandle)
607 MMRESULT res = waveInUnprepareHeader(aRecordDeviceHandle,&iBufHdr,sizeof(WAVEHDR));
608 __KTRACE_SND(Kern::Printf(" waveInUnprepareHeader(BufNo:%d)-%d",iBufNum,res));
609 __ASSERT_ALWAYS(res==MMSYSERR_NOERROR,Kern::Fault("DWinsSoundScTxPddWIUH",res)); //WaveInUnprepareHeader error.
613 The waveform input callback function to handle data block transfer completion.
614 This function is always executed in windows thread context.
615 @param aHdr A pointer to the header for the waveform audio buffer just transferred.
617 void DWinsSoundScRxPdd::WaveInProc(WAVEHDR* aHdr)
619 TInt waveBufId=aHdr->dwUser; // Work out which waveform audio buffer is completing.
620 // Kern::Printf("DWinsSoundScRxPdd::WaveInProc waveBufId %d", waveBufId);
623 iCompletedRecordBufHdrMask|=(1<<waveBufId); // Update the completion status mask
624 iDfc.Add(); // Queue RecordDfc().
629 The DFC used to handle data block record completion.
630 This function is always executed in driver thread context.
631 @param aPtr A pointer to the physical channel object.
633 void DWinsSoundScRxPdd::RecordDfc(TAny* aPtr)
636 DWinsSoundScRxPdd& drv=*(DWinsSoundScRxPdd*)aPtr;
638 // More than 1 transfer may have completed so loop until all completions are handled
639 while (drv.iCompletedRecordBufHdrMask)
641 // Find the buffer ID of the next transfer that has completed
642 for (i=0 ; i<32 && !(drv.iCompletedRecordBufHdrMask&(1<<i)) ; i++) {}
643 __ASSERT_ALWAYS(i<drv.iWaveformBufMgr->iNumWaveformBufs,PANIC());
644 __e32_atomic_and_ord32(&drv.iCompletedRecordBufHdrMask, ~(1u<<i)); // Clear this bit in the mask
646 // Update the status of the waveform audio buffer which is completing
647 TWaveformAudioBuf& buf=drv.iWaveformBufMgr->iWaveformAudioBuf[i];
650 // Callback the LDD passing the information for the transfer that has completed
651 drv.iPendingRecord--;
652 __KTRACE_SND(Kern::Printf(" Read complete(BufNo:%x Pos:%x Len:%d)",i,buf.iBufHdr.lpData,buf.iBufHdr.dwBufferLength));
653 drv.iBytesSincePauseReportedToLdd += buf.iBufHdr.dwBufferLength;
654 drv.Ldd()->RecordCallback(buf.iTransferID,KErrNone,buf.iBufHdr.dwBufferLength);
659 Issue a request from the driver thread to the windows thread to execute a command.
660 @param aCommand The identifier of the command to be executed.
661 @param aArg0 A first command argument, its meaning depends on the command.
662 @param aArg1 A second command argument, its meaning depends on the command.
663 @param aArg2 A third command argument, its meaning depends on the command.
664 This function is always executed in driver thread context.
666 void DWinsSoundScRxPdd::RecordThreadCommand(TThreadCommand aCommand,TInt aArg0,TInt aArg1,TInt aArg2)
668 __KTRACE_SND(Kern::Printf(">DWinsSoundScRxPdd:RecordThreadCommand"));
669 iRecordCommand = aCommand;
670 iRecordCommandArg0 = aArg0;
671 iRecordCommandArg1 = aArg1;
672 iRecordCommandArg2 = aArg2;
676 ReleaseSemaphore(iRecordThreadSem,1,NULL);
680 Pass a value from the windows thread to the driver thread.
681 This function is always executed in windows thread context.
682 @param aError The value to the passed to the driver thread.
684 void DWinsSoundScRxPdd::RecordThreadNotifyDriver(TInt aError)
686 iRecordThreadError = aError;
687 BOOL ret = ReleaseSemaphore(iDriverThreadSem,1,NULL);
688 __ASSERT_ALWAYS(ret == TRUE,PANIC()); //Unexpected Windows Error
691 #pragma warning(disable : 4702) // unreachable code
693 Open the waveform input device for record. Use a default device identifier in order to select a device
694 capable of meeting the current audio configuration.
695 This function can be executed in either driver thread or windows thread context.
696 @pre The data member DWinsSoundScRxPdd::iSoundConfig must be setup with the current audio configuration.
698 TInt DWinsSoundScRxPdd::OpenWaveInDevice()
701 format.wFormatTag = WAVE_FORMAT_PCM;
702 TUint16 bitsPerSample = 8;
704 switch (iSoundConfig.iEncoding)
706 case ESoundEncoding8BitPCM:
708 case ESoundEncoding16BitPCM:
712 return KErrNotSupported;
715 TInt rateInSamplesPerSecond=RateInSamplesPerSecond(iSoundConfig.iRate);
716 format.nChannels = TUint16(iSoundConfig.iChannels);
717 format.nSamplesPerSec = rateInSamplesPerSecond;
718 format.nAvgBytesPerSec = rateInSamplesPerSecond * iSoundConfig.iChannels * bitsPerSample / 8;
719 format.nBlockAlign = TUint16(iSoundConfig.iChannels * bitsPerSample / 8);
720 format.wBitsPerSample = bitsPerSample;
723 MMRESULT res = MMSYSERR_NOERROR;
728 timeBeginPeriod(KMMTimerRes);
729 iRecordDeviceHandle = (HWAVEIN)1;
733 res = waveInOpen(&iRecordDeviceHandle, WAVE_MAPPER, &format, (DWORD)::WaveInProc, (DWORD)this, CALLBACK_FUNCTION);
735 // On some builds of Windows (such as Windows Server 2003), the waveInGetDevCaps() trick in
736 // DoCreate() won't work, so we have another special check for missing hardware here
737 if ((res == MMSYSERR_NODRIVER) || (res == MMSYSERR_BADDEVICEID))
739 // Pretend there was no error and switch into hardware emulation mode
740 res = MMSYSERR_NOERROR;
742 iRecordDeviceHandle = (HWAVEIN)1;
743 iWaveformBufMgr->iIsHardware = EFalse;
744 timeBeginPeriod(KMMTimerRes);
750 case MMSYSERR_NOERROR: // No error
752 case MMSYSERR_ALLOCATED: // Specified resource is already allocated.
754 case WAVERR_BADFORMAT: // Attempted to open with an unsupported waveform-audio format
755 return(KErrNotSupported);
756 case MMSYSERR_NOMEM: // Unable to allocate or lock memory.
757 return(KErrNoMemory);
762 #pragma warning(default : 4702) // unreachable code
765 Open the audio input device.
766 This function is always executed in driver thread context.
767 @pre The data members DWinsSoundScRxPdd::iSoundConfig must be setup with the current audio configuration.
769 TInt DWinsSoundScRxPdd::CreateRecordDevice(TBool aCheckDevice)
771 // Check if the waveform input device is already open.
772 if (iRecordDeviceHandle)
777 // Open the waveform input device for recording.
778 TInt err = OpenWaveInDevice();
786 // Now, re-allocate a set of the waveform audio blocks in advance of any recording. Also, prepare one of these
787 // for each buffer within the shared chunk. Need to be in critical section while re-allocating the audio blocks.
788 NKern::ThreadEnterCS();
789 err=iWaveformBufMgr->ReAllocAndUpdate(Ldd()->BufConfig(),Ldd()->ChunkBase(),(TInt)iRecordDeviceHandle);
790 NKern::ThreadLeaveCS();
797 Close down the record device.
798 This function is always executed in driver thread context.
800 void DWinsSoundScRxPdd::CloseRecordDevice()
805 timeEndPeriod(KMMTimerRes);
807 HWAVEIN handle = iRecordDeviceHandle;
812 waveInReset(handle); // Stop recording.
814 // Un-prepare all the waveform audio buffers.
815 for (TInt i=0 ; i<iWaveformBufMgr->iNumWaveformBufs ; i++)
816 iWaveformBufMgr->iWaveformAudioBuf[i].Unprepare((TInt)handle);
819 waveInClose(handle); // Close the wavein device.
821 iRecordDeviceHandle = NULL;
826 The thread function for the record windows thread.
827 This function is always executed in windows thread context.
828 @pre The data members DWinsSoundScRxPdd::iSoundConfig must be setup with the current audio configuration.
830 void DWinsSoundScRxPdd::RecordThread()
832 iRecordThreadSem = CreateSemaphore(NULL,0,0x7fffffff,NULL);
833 __ASSERT_ALWAYS(iRecordThreadSem,PANIC()); //No Windows Memory
834 iRecordTimerEvent=CreateEvent(NULL,TRUE,FALSE,NULL);
836 objects[0]=iRecordThreadSem; // Indicates command from driver thread
837 objects[1]=iRecordTimerEvent;
839 // Signal driver of successful setup
840 RecordThreadNotifyDriver(KErrNone);
841 ResetEvent(iRecordTimerEvent);
844 DWORD ret=WaitForMultipleObjectsEx(2,objects,FALSE,INFINITE,TRUE);
848 case WAIT_OBJECT_0: // Command received from the driver thread.
849 if (ProcessRecordCommand(iRecordCommand,iRecordCommandArg0,iRecordCommandArg1,iRecordCommandArg2)==KErrCompletion)
850 return; // ********* Exit thread **************
852 case WAIT_OBJECT_0+1:
853 HandleRecordTimerEvent();
860 Process a request from the driver thread to execute a command.
861 This function is always executed in windows thread context.
862 @param aCommand The identifier of the command to be executed.
863 @param aArg0 A first command argument, its meaning depends on the command.
864 @param aArg1 A second command argument, its meaning depends on the command.
865 @param aArg2 A third command argument, its meaning depends on the command.
866 @return KErrCompletion if the command to exit the windows thread has been received;
869 TInt DWinsSoundScRxPdd::ProcessRecordCommand(TThreadCommand aCommand,TInt aArg0,TInt aArg1,TInt aArg2)
871 __KTRACE_SND(Kern::Printf(">DWinsSoundScRxPdd:ProcessRecordCommand(%d)",aCommand));
874 case ERecData: // Initiate the recording of a buffers worth of data from the wavein device.
878 // Acquire a windows waveform audio buffer for the transfer.
879 char* startAddress=(char*)aArg1;
880 TInt bytesToRecord=aArg2;
881 __ASSERT_ALWAYS(bytesToRecord>0,PANIC());
882 TWaveformAudioBuf* waveformAudioBuf=iWaveformBufMgr->AcquireBuf(startAddress,bytesToRecord,(TInt)iRecordDeviceHandle);
883 waveformAudioBuf->iTransferID=(TUint)aArg0;
884 waveformAudioBuf->iBufHdr.dwBufferLength=bytesToRecord;
888 // This machine has a wavein device present. Send the buffer to the wavein device.
889 waveInStart(iRecordDeviceHandle); // Start input on the wavein device - safe to call this when already started.
890 MMRESULT res = waveInAddBuffer(iRecordDeviceHandle,&waveformAudioBuf->iBufHdr,sizeof(WAVEHDR));
891 __KTRACE_SND(Kern::Printf(" waveInAddBuffer(ID:%x Pos:%x Len:%d)-%d",aArg0,startAddress,bytesToRecord,res));
892 __ASSERT_ALWAYS(res == MMSYSERR_NOERROR,PANIC()); //WaveInAddBuffer Error
896 // This machine has no audio hardware present so simulate the wavein device using a timer.
897 AddToPendingList(&waveformAudioBuf->iBufHdr,iWaveformBufMgr->iPendingBufList); // Queue the buffer on the pending list
899 // Check if the timer needs starting/re-starting
902 iLastTimerEventTime = timeGetTime();
903 StartTimer(&waveformAudioBuf->iBufHdr);
908 // Signal the driver thread that we have completed the command.
909 RecordThreadNotifyDriver(KErrNone);
914 case EStop: // Terminate the recording of data from the wavein device.
916 iRecordEnabled=EFalse; // Stop the windows thread from sending any more buffers to wavein device.
920 // This machine has no audio hardware present so simulates the waveout device using a timer.
921 StopTimer(ETrue); // Stop the timer and cancel any buffers pending
924 // Leave the driver thread to close down the record device.
926 // Signal the driver thread that we have completed the command.
930 ReleaseSemaphore(iStopSemaphore,1,&prev);
935 case EExit: // Close down the record device and exit the windows thread.
939 // This machine has a wavein device present.
940 if (iRecordDeviceHandle)
942 waveInReset(iRecordDeviceHandle); // Stop recording on the wavein device.
943 waveInClose(iRecordDeviceHandle); // Close the wavein device.
948 // This machine has no audio hardware present so simulates the waveout device using a timer.
949 StopTimer(ETrue); // Stop the timer and cancel any buffers pending.
951 // Logically the record device is now shut so clear the handle.
952 iRecordDeviceHandle = 0;
954 // Signal the driver thread that we have completed the command.
958 ReleaseSemaphore(iDeathSemaphore,1,&prev);
960 return(KErrCompletion); // ********* Exit thread **************
963 case EPause: // Halt the recording of data from the wavein device.
964 iRecordEnabled=EFalse;
969 // Need to try to work out how much of the current audio buffer has been filled.
971 time.wType = TIME_BYTES;
972 HWAVEIN handle = iRecordDeviceHandle;
973 waveInGetPosition(handle,&time,sizeof(MMTIME));
974 position = time.u.cb;
976 // Stop recording. (Windows will mark all pending audio buffers as done).
981 // This machine has no audio hardware present so simulates the waveout device using a timer.
983 // Determine the # of milliseconds that have passed since the last timer triggered
984 DWORD currentTime = timeGetTime();
985 DWORD timeSinceLastEvent = (currentTime - iLastTimerEventTime);
987 // Clamp the resulting value to the duration of the timer, to prevent the millisecond count
988 // going backwards if Windows is busy and latency becomes an issue
989 if (timeSinceLastEvent > iSimulatedMsecDuration)
990 timeSinceLastEvent = iSimulatedMsecDuration;
992 TUint bytesTransferredSincePause = iBytesSincePauseReportedToLdd;
993 WAVEHDR *buf = iWaveformBufMgr->iPendingBufList[0];
996 // Add on an estimate of the progress of the current transfer
997 bytesTransferredSincePause += ((buf->dwBufferLength * timeSinceLastEvent) / iSimulatedMsecDuration);
1000 position = bytesTransferredSincePause;
1002 StopTimer(ETrue); // Stop the timer and cancel any buffers pending
1005 // Signal the driver thread that we have stopped recording - returning info. on any partially filled buffer.
1006 RecordThreadNotifyDriver(position);
1012 // Determine how long we were paused for and add that time to the time the timer last
1013 // triggered. This will allow us to continue as though we had never been paused
1014 iLastTimerEventTime = timeGetTime();
1023 Handle a timer expiry event. This is only used when no audio hardware is present, with a timer expiry corresponding
1024 to the end of a data block transfer.
1025 This function is always executed in windows thread context.
1027 void DWinsSoundScRxPdd::HandleRecordTimerEvent()
1029 __KTRACE_SND(Kern::Printf(">DWinsSoundScRxPdd:HandleRecordTimerEvent"));
1030 ResetEvent(iRecordTimerEvent); // Reset the event
1032 // Remove the audio buffer just filled from the pending list and save it for the driver thread.
1033 WAVEHDR* buf=RemoveFromPendingList(iWaveformBufMgr->iPendingBufList);
1034 __ASSERT_ALWAYS(buf != NULL,PANIC());
1035 TInt waveBufId=buf->dwUser; // Work out which waveform audio buffer is completing.
1037 // Check if there are more audio buffers waiting to be played
1038 buf=iWaveformBufMgr->iPendingBufList[0];
1041 iLastTimerEventTime = timeGetTime();
1042 StartTimer(buf); // Re-start the timer
1045 iTimerActive=EFalse;
1047 // Notify that another audio buffer has been filled.
1049 iCompletedRecordBufHdrMask|=(1<<waveBufId); // Update the completion status mask
1056 Initialise the data member DWinsSoundScRxPdd::iCaps with the capabilities of this audio device.
1058 void DWinsSoundScRxPdd::SetCaps()
1060 __KTRACE_SND(Kern::Printf(">DWinsSoundScRxPdd::SetCaps"));
1062 // The data transfer direction for this unit is record.
1063 iCaps.iDirection=ESoundDirRecord;
1065 // Assume this unit supports mono or stereo.
1066 iCaps.iChannels=KSoundMonoChannel|KSoundStereoChannel;
1068 // Assume this unit supports all sample rates.
1069 iCaps.iRates=(KSoundRate7350Hz|KSoundRate8000Hz|KSoundRate8820Hz|KSoundRate9600Hz|KSoundRate11025Hz|
1070 KSoundRate12000Hz|KSoundRate14700Hz|KSoundRate16000Hz|KSoundRate22050Hz|KSoundRate24000Hz|
1071 KSoundRate29400Hz|KSoundRate32000Hz|KSoundRate44100Hz|KSoundRate48000Hz);
1073 // Assume this unit supports 8bit and 16bit PCM encoding.
1074 iCaps.iEncodings=(KSoundEncoding8BitPCM|KSoundEncoding16BitPCM);
1076 // This unit only supports interleaved data format
1077 iCaps.iDataFormats=KSoundDataFormatInterleaved;
1079 // The minimum request size that the device can support.
1080 iCaps.iRequestMinSize=0; // No restriction
1082 // The request alignment that this device requires.
1083 iCaps.iRequestAlignment=0; // No restriction
1085 // This unit is not capable of detecting changes in hardware configuration.
1086 iCaps.iHwConfigNotificationSupport=EFalse;
1089 Start the audio timer.
1090 The timer is only used when no audio hardware is present on the machine. This is in order to introduce a delay which is
1091 equivelent to that incurred when transferring audio data over a real audio device.
1092 @param aBuffer The audio buffer which would have been transferred had a real audio device been present. This contains
1093 information on the number of bytes to transfer.
1095 void DWinsSoundScRxPdd::StartTimer(WAVEHDR* aBuffer)
1097 // First, need to calculate the duration of the timer in milliseconds.
1098 TInt bytesToPlay=aBuffer->dwBufferLength;
1099 iSimulatedMsecDuration = bytesToPlay*1000;
1100 iSimulatedMsecDuration /= (RateInSamplesPerSecond(iSoundConfig.iRate) * iSoundConfig.iChannels);
1101 if (iSoundConfig.iEncoding==ESoundEncoding16BitPCM)
1102 iSimulatedMsecDuration /= 2;
1103 if (iSoundConfig.iEncoding==ESoundEncoding24BitPCM)
1104 iSimulatedMsecDuration /= 3;
1105 if (iSimulatedMsecDuration<=0)
1106 iSimulatedMsecDuration=1; // Round up to 1ms or timeSetEvent() will return an error.
1108 MMRESULT res = timeSetEvent(iSimulatedMsecDuration, KMMTimerRes, (LPTIMECALLBACK)iRecordTimerEvent, 0, TIME_ONESHOT | TIME_CALLBACK_EVENT_SET);
1109 __ASSERT_ALWAYS(res != NULL,PANIC()); // timeSetEvent error.
1110 iTimerID = res; // Save the identifier for the new timer event.
1115 Stop the audio timer.
1116 The timer is only used when no audio hardware is present on the machine. This is in order to introduce a delay which is
1117 equivelent to that incurred when transferring audio data over a real audio device.
1118 @param aCancellAll Set to ETrue in order to discard any buffers queued on the pending buffer list. EFalse otherwise.
1120 void DWinsSoundScRxPdd::StopTimer(TBool aCancelAll)
1124 MMRESULT res = timeKillEvent(iTimerID);
1125 __ASSERT_ALWAYS(res == TIMERR_NOERROR,PANIC()); // timeKillEvent error
1131 b=RemoveFromPendingList(iWaveformBufMgr->iPendingBufList);
1135 iTimerActive=EFalse;