sl@0
|
1 |
// Copyright (c) 2006-2009 Nokia Corporation and/or its subsidiary(-ies).
|
sl@0
|
2 |
// All rights reserved.
|
sl@0
|
3 |
// This component and the accompanying materials are made available
|
sl@0
|
4 |
// under the terms of "Eclipse Public License v1.0"
|
sl@0
|
5 |
// which accompanies this distribution, and is available
|
sl@0
|
6 |
// at the URL "http://www.eclipse.org/legal/epl-v10.html".
|
sl@0
|
7 |
//
|
sl@0
|
8 |
// Initial Contributors:
|
sl@0
|
9 |
// Nokia Corporation - initial contribution.
|
sl@0
|
10 |
//
|
sl@0
|
11 |
// Contributors:
|
sl@0
|
12 |
//
|
sl@0
|
13 |
// Description:
|
sl@0
|
14 |
// wins\specific\soundsc_rx.cpp
|
sl@0
|
15 |
// Emulator record functions for the shared chunk sound driver PDD.
|
sl@0
|
16 |
//
|
sl@0
|
17 |
//
|
sl@0
|
18 |
|
sl@0
|
19 |
/**
|
sl@0
|
20 |
@file
|
sl@0
|
21 |
@internalTechnology
|
sl@0
|
22 |
@prototype
|
sl@0
|
23 |
*/
|
sl@0
|
24 |
|
sl@0
|
25 |
#include "winssoundsc.h"
|
sl@0
|
26 |
|
sl@0
|
27 |
/**
|
sl@0
|
28 |
The thread function for the record windows thread.
|
sl@0
|
29 |
This function is always executed in windows thread context.
|
sl@0
|
30 |
*/
|
sl@0
|
31 |
LOCAL_C TUint RecordThreadFunction(DWinsSoundScRxPdd *aSoundPdd)
|
sl@0
|
32 |
{
|
sl@0
|
33 |
aSoundPdd->RecordThread();
|
sl@0
|
34 |
return 0;
|
sl@0
|
35 |
}
|
sl@0
|
36 |
|
sl@0
|
37 |
/**
|
sl@0
|
38 |
The waveform input callback function. This can receive the following messages:-
|
sl@0
|
39 |
WIM_OPEN when the input device is opened, WIM_CLOSE when the input device is closed,
|
sl@0
|
40 |
and WIM_DATA each time a record data block has been filled (i.e. completion of waveInAddBuffer).
|
sl@0
|
41 |
This function is always executed in windows thread context.
|
sl@0
|
42 |
*/
|
sl@0
|
43 |
LOCAL_C void CALLBACK WaveInProc(HWAVEIN /*hwi*/, UINT uMsg, DWORD dwInstance, DWORD dwParam1, DWORD /*dwParam2*/)
|
sl@0
|
44 |
{
|
sl@0
|
45 |
if (uMsg == WIM_DATA)
|
sl@0
|
46 |
{
|
sl@0
|
47 |
DWinsSoundScRxPdd * pdd = (DWinsSoundScRxPdd*)dwInstance;
|
sl@0
|
48 |
pdd->WaveInProc((WAVEHDR*)dwParam1);
|
sl@0
|
49 |
}
|
sl@0
|
50 |
}
|
sl@0
|
51 |
|
sl@0
|
52 |
/**
|
sl@0
|
53 |
Constructor for the WINS shared chunk record PDD.
|
sl@0
|
54 |
This function is always executed in driver thread context.
|
sl@0
|
55 |
*/
|
sl@0
|
56 |
DWinsSoundScRxPdd::DWinsSoundScRxPdd()
|
sl@0
|
57 |
: iDfc(DWinsSoundScRxPdd::RecordDfc,this,2)
|
sl@0
|
58 |
{
|
sl@0
|
59 |
__KTRACE_SND(Kern::Printf(">DWinsSoundScRxPdd::DWinsSoundScRxPdd"));
|
sl@0
|
60 |
|
sl@0
|
61 |
// iDriverThreadSem=0;
|
sl@0
|
62 |
// iRecordThread=0;
|
sl@0
|
63 |
// iRecordThreadMutex=0;
|
sl@0
|
64 |
// iRecordThreadSem=0;
|
sl@0
|
65 |
// iStopSemaphore=0;
|
sl@0
|
66 |
// iDeathSemaphore=0;
|
sl@0
|
67 |
// iRecordDeviceHandle=0;
|
sl@0
|
68 |
// iRecordCommand=ERecData;
|
sl@0
|
69 |
// iRecordCommandArg0=0;
|
sl@0
|
70 |
// iRecordCommandArg1=0;
|
sl@0
|
71 |
// iPendingRecord=0;
|
sl@0
|
72 |
// iRecordThreadError=0;
|
sl@0
|
73 |
/// iWaveformBufMgr=NULL;
|
sl@0
|
74 |
// iCompletedRecordBufHdrMask=0;
|
sl@0
|
75 |
// iRecordBufferSize=0;
|
sl@0
|
76 |
// iRecordEnabled=EFalse;
|
sl@0
|
77 |
// iNoHardware=EFalse;
|
sl@0
|
78 |
// iRecordTimerEvent=0;
|
sl@0
|
79 |
// iTimerID=0;
|
sl@0
|
80 |
// iTimerActive=EFalse;
|
sl@0
|
81 |
}
|
sl@0
|
82 |
|
sl@0
|
83 |
/**
|
sl@0
|
84 |
Destructor for the WINS shared chunk record PDD.
|
sl@0
|
85 |
This function is always executed in driver thread context.
|
sl@0
|
86 |
*/
|
sl@0
|
87 |
DWinsSoundScRxPdd::~DWinsSoundScRxPdd()
|
sl@0
|
88 |
{
|
sl@0
|
89 |
// If the Windows thread started up successfully, signal it to shut down and wait for it to do so
|
sl@0
|
90 |
if (iRecordThreadRunning)
|
sl@0
|
91 |
{
|
sl@0
|
92 |
// Signal the windows thread to close down the record device and exit the windows thread.
|
sl@0
|
93 |
iDeathSemaphore = CreateSemaphore(NULL, 0, 2, NULL);
|
sl@0
|
94 |
RecordThreadCommand(EExit);
|
sl@0
|
95 |
|
sl@0
|
96 |
// Wait for the record thread to terminate.
|
sl@0
|
97 |
if (iDeathSemaphore)
|
sl@0
|
98 |
{
|
sl@0
|
99 |
Emulator::Escape();
|
sl@0
|
100 |
WaitForSingleObject(iDeathSemaphore, INFINITE);
|
sl@0
|
101 |
Emulator::Reenter();
|
sl@0
|
102 |
|
sl@0
|
103 |
__HOST_LOCK;
|
sl@0
|
104 |
CloseHandle(iDeathSemaphore);
|
sl@0
|
105 |
}
|
sl@0
|
106 |
}
|
sl@0
|
107 |
|
sl@0
|
108 |
if (iRecordTimerEvent)
|
sl@0
|
109 |
CloseHandle(iRecordTimerEvent);
|
sl@0
|
110 |
if (iRecordThreadSem)
|
sl@0
|
111 |
CloseHandle(iRecordThreadSem);
|
sl@0
|
112 |
if (iRecordThread)
|
sl@0
|
113 |
CloseHandle(iRecordThread);
|
sl@0
|
114 |
if (iDriverThreadSem)
|
sl@0
|
115 |
CloseHandle(iDriverThreadSem);
|
sl@0
|
116 |
|
sl@0
|
117 |
if (iWaveformBufMgr)
|
sl@0
|
118 |
delete iWaveformBufMgr;
|
sl@0
|
119 |
}
|
sl@0
|
120 |
|
sl@0
|
121 |
/**
|
sl@0
|
122 |
Second stage constructor for the WINS shared chunk record PDD.
|
sl@0
|
123 |
Note that this constructor is called before the second stage constructor for the LDD so it is not
|
sl@0
|
124 |
possible to call methods on the LDD here.
|
sl@0
|
125 |
This function is always executed in driver thread context.
|
sl@0
|
126 |
@param aPhysicalDevice A pointer to the factory class that is creating this PDD.
|
sl@0
|
127 |
@return KErrNone if successful, otherwise one of the other system wide error codes.
|
sl@0
|
128 |
*/
|
sl@0
|
129 |
TInt DWinsSoundScRxPdd::DoCreate(DWinsSoundScPddFactory* aPhysicalDevice)
|
sl@0
|
130 |
{
|
sl@0
|
131 |
__KTRACE_SND(Kern::Printf(">DWinsSoundScRxPdd::DoCreate"));
|
sl@0
|
132 |
|
sl@0
|
133 |
iPhysicalDevice=aPhysicalDevice;
|
sl@0
|
134 |
|
sl@0
|
135 |
// Set up the correct DFC queue.
|
sl@0
|
136 |
iDfc.SetDfcQ(iPhysicalDevice->iDfcQ);
|
sl@0
|
137 |
|
sl@0
|
138 |
SetCaps(); // Setup the capabilities of this device.
|
sl@0
|
139 |
|
sl@0
|
140 |
// Setup the default audio configuration
|
sl@0
|
141 |
iSoundConfig.iChannels=2;
|
sl@0
|
142 |
iSoundConfig.iRate=ESoundRate48000Hz;
|
sl@0
|
143 |
iSoundConfig.iEncoding=ESoundEncoding16BitPCM;
|
sl@0
|
144 |
iSoundConfig.iDataFormat=ESoundDataFormatInterleaved;
|
sl@0
|
145 |
|
sl@0
|
146 |
__HOST_LOCK;
|
sl@0
|
147 |
|
sl@0
|
148 |
// Query the waveform device capabilities using the default device identifier in order
|
sl@0
|
149 |
// to check if there is a functioning waveform device present. Note that some versions of
|
sl@0
|
150 |
// Windows (such as Windows Server 2003) will actually return MMSYSERR_NOERROR when this is
|
sl@0
|
151 |
// called, even if there is no waveform device present, so we have a further check in
|
sl@0
|
152 |
// when waveInOpen() is called
|
sl@0
|
153 |
WAVEINCAPS waveInCaps;
|
sl@0
|
154 |
MMRESULT res = waveInGetDevCaps(WAVE_MAPPER,&waveInCaps,sizeof(WAVEINCAPS));
|
sl@0
|
155 |
#ifdef FORCE_NO_HARDWARE
|
sl@0
|
156 |
res=MMSYSERR_NOERROR+1;
|
sl@0
|
157 |
#endif
|
sl@0
|
158 |
if (res != MMSYSERR_NOERROR)
|
sl@0
|
159 |
iNoHardware = ETrue;
|
sl@0
|
160 |
|
sl@0
|
161 |
__HOST_LOCK_OFF;
|
sl@0
|
162 |
|
sl@0
|
163 |
// Create the windows waveform audio buffer manager.
|
sl@0
|
164 |
iWaveformBufMgr=new TWaveformBufMgr(ESoundDirRecord,!iNoHardware);
|
sl@0
|
165 |
if (!iWaveformBufMgr)
|
sl@0
|
166 |
return(KErrNoMemory);
|
sl@0
|
167 |
|
sl@0
|
168 |
// Create the driver thread semaphore.
|
sl@0
|
169 |
iDriverThreadSem = CreateSemaphore(NULL,0,0x7fffffff,NULL);
|
sl@0
|
170 |
if (!iDriverThreadSem)
|
sl@0
|
171 |
return(KErrNoMemory);
|
sl@0
|
172 |
|
sl@0
|
173 |
// Create the record windows thread.
|
sl@0
|
174 |
if ((iRecordThread=CreateWin32Thread(EThreadEvent,(LPTHREAD_START_ROUTINE)RecordThreadFunction,(void *)this, FALSE))==NULL)
|
sl@0
|
175 |
return(Emulator::LastError());
|
sl@0
|
176 |
SetThreadPriority(iRecordThread,THREAD_PRIORITY_HIGHEST);
|
sl@0
|
177 |
__ASSERT_ALWAYS( ResumeThread(iRecordThread) != 0xffffffff, PANIC()); //Windows Unexpected Error
|
sl@0
|
178 |
|
sl@0
|
179 |
// Wait to be notified of successful thread initialization
|
sl@0
|
180 |
Emulator::Escape();
|
sl@0
|
181 |
WaitForSingleObject(iDriverThreadSem,INFINITE);
|
sl@0
|
182 |
Emulator::Reenter();
|
sl@0
|
183 |
|
sl@0
|
184 |
// If the Windows thread started up successfully, indicate this fact so that when shutting down we know
|
sl@0
|
185 |
// to signal to the thread to exit
|
sl@0
|
186 |
if (iRecordThreadError == KErrNone)
|
sl@0
|
187 |
iRecordThreadRunning = ETrue;
|
sl@0
|
188 |
|
sl@0
|
189 |
return(iRecordThreadError);
|
sl@0
|
190 |
}
|
sl@0
|
191 |
|
sl@0
|
192 |
/**
|
sl@0
|
193 |
Called from the LDD to return the DFC queue to be used by this device.
|
sl@0
|
194 |
This function is always executed in driver thread context.
|
sl@0
|
195 |
@return The DFC queue to use.
|
sl@0
|
196 |
*/
|
sl@0
|
197 |
TDfcQue* DWinsSoundScRxPdd::DfcQ(TInt /*aUnit*/)
|
sl@0
|
198 |
{
|
sl@0
|
199 |
return(iPhysicalDevice->iDfcQ);
|
sl@0
|
200 |
}
|
sl@0
|
201 |
|
sl@0
|
202 |
/**
|
sl@0
|
203 |
Called from the LDD to return the shared chunk create information to be used by this device.
|
sl@0
|
204 |
This function is always executed in driver thread context.
|
sl@0
|
205 |
@param aChunkCreateInfo A chunk create info. object to be to be filled with the settings
|
sl@0
|
206 |
required for this device.
|
sl@0
|
207 |
*/
|
sl@0
|
208 |
void DWinsSoundScRxPdd::GetChunkCreateInfo(TChunkCreateInfo& aChunkCreateInfo)
|
sl@0
|
209 |
{
|
sl@0
|
210 |
__KTRACE_SND(Kern::Printf(">DWinsSoundScRxPdd::GetChunkCreateInfo"));
|
sl@0
|
211 |
|
sl@0
|
212 |
aChunkCreateInfo.iType=TChunkCreateInfo::ESharedKernelMultiple;
|
sl@0
|
213 |
aChunkCreateInfo.iMapAttr=0;
|
sl@0
|
214 |
aChunkCreateInfo.iOwnsMemory=ETrue; // Using RAM pages.
|
sl@0
|
215 |
aChunkCreateInfo.iDestroyedDfc=NULL; // No chunk destroy DFC.
|
sl@0
|
216 |
}
|
sl@0
|
217 |
|
sl@0
|
218 |
/**
|
sl@0
|
219 |
Called from the LDD to return the capabilities of this device.
|
sl@0
|
220 |
This function is always executed in driver thread context.
|
sl@0
|
221 |
@param aCapsBuf A packaged TSoundFormatsSupportedV02 object to be filled with the record
|
sl@0
|
222 |
capabilities of this device. This descriptor is in kernel memory and can be accessed directly.
|
sl@0
|
223 |
@see TSoundFormatsSupportedV02.
|
sl@0
|
224 |
*/
|
sl@0
|
225 |
void DWinsSoundScRxPdd::Caps(TDes8& aCapsBuf) const
|
sl@0
|
226 |
{
|
sl@0
|
227 |
__KTRACE_SND(Kern::Printf(">DWinsSoundScRxPdd::Caps"));
|
sl@0
|
228 |
|
sl@0
|
229 |
// Copy iCaps back.
|
sl@0
|
230 |
TPtrC8 ptr((const TUint8*)&iCaps,sizeof(iCaps));
|
sl@0
|
231 |
aCapsBuf.FillZ(aCapsBuf.MaxLength());
|
sl@0
|
232 |
aCapsBuf=ptr.Left(Min(ptr.Length(),aCapsBuf.MaxLength()));
|
sl@0
|
233 |
}
|
sl@0
|
234 |
|
sl@0
|
235 |
/**
|
sl@0
|
236 |
Called from the LDD to return the maximum transfer length in bytes that this device can support in a single data transfer.
|
sl@0
|
237 |
@return The maximum transfer length in bytes.
|
sl@0
|
238 |
*/
|
sl@0
|
239 |
TInt DWinsSoundScRxPdd::MaxTransferLen() const
|
sl@0
|
240 |
{
|
sl@0
|
241 |
return(KWinsMaxAudioTransferLen); // 32K
|
sl@0
|
242 |
}
|
sl@0
|
243 |
|
sl@0
|
244 |
/**
|
sl@0
|
245 |
Called from the LDD to power up the sound device.
|
sl@0
|
246 |
This function is always executed in driver thread context.
|
sl@0
|
247 |
@return KErrNone if successful, otherwise one of the other system wide error codes.
|
sl@0
|
248 |
*/
|
sl@0
|
249 |
TInt DWinsSoundScRxPdd::PowerUp()
|
sl@0
|
250 |
{
|
sl@0
|
251 |
return(KErrNone);
|
sl@0
|
252 |
}
|
sl@0
|
253 |
|
sl@0
|
254 |
/**
|
sl@0
|
255 |
Called from the LDD to configure or reconfigure the device using the the configuration supplied.
|
sl@0
|
256 |
This function is always executed in driver thread context.
|
sl@0
|
257 |
@param aConfigBuf A packaged TCurrentSoundFormatV02 object which contains the new configuration settings.
|
sl@0
|
258 |
This descriptor is in kernel memory and can be accessed directly.
|
sl@0
|
259 |
@return KErrNone if successful, otherwise one of the other system wide error codes.
|
sl@0
|
260 |
@see TCurrentSoundFormatV02.
|
sl@0
|
261 |
*/
|
sl@0
|
262 |
TInt DWinsSoundScRxPdd::SetConfig(const TDesC8& aConfigBuf)
|
sl@0
|
263 |
{
|
sl@0
|
264 |
__KTRACE_SND(Kern::Printf(">DWinsSoundScRxPdd::SetConfig"));
|
sl@0
|
265 |
|
sl@0
|
266 |
// Cannot change the configuration while the device is open and recording. (LDD should prevent
|
sl@0
|
267 |
// this anyway but better safe than sorry).
|
sl@0
|
268 |
if (iRecordDeviceHandle)
|
sl@0
|
269 |
return(KErrInUse);
|
sl@0
|
270 |
|
sl@0
|
271 |
// Save the current settings so we can restore them if there is a problem with the new ones.
|
sl@0
|
272 |
TCurrentSoundFormatV02 saved=iSoundConfig;
|
sl@0
|
273 |
|
sl@0
|
274 |
// Read the new configuration from the LDD.
|
sl@0
|
275 |
TPtr8 ptr((TUint8*)&iSoundConfig,sizeof(iSoundConfig));
|
sl@0
|
276 |
Kern::InfoCopy(ptr,aConfigBuf);
|
sl@0
|
277 |
|
sl@0
|
278 |
// Open the record device with the new settings to check they are supported. Then close it
|
sl@0
|
279 |
// again - don't leave it open yet.
|
sl@0
|
280 |
TInt r = CreateRecordDevice(ETrue);
|
sl@0
|
281 |
if (r==KErrNone)
|
sl@0
|
282 |
CloseRecordDevice();
|
sl@0
|
283 |
else
|
sl@0
|
284 |
iSoundConfig=saved; // Restore the previous settings
|
sl@0
|
285 |
|
sl@0
|
286 |
return(r);
|
sl@0
|
287 |
}
|
sl@0
|
288 |
|
sl@0
|
289 |
/**
|
sl@0
|
290 |
Called from the LDD to set the record level.
|
sl@0
|
291 |
This function is always executed in driver thread context.
|
sl@0
|
292 |
@param aLevel The record level to be set - a value in the range 0 to 255. The value 255 equates
|
sl@0
|
293 |
to the maximum record level and each value below this equates to a 0.5dB step below it.
|
sl@0
|
294 |
@return KErrNone if successful, otherwise one of the other system wide error codes.
|
sl@0
|
295 |
*/
|
sl@0
|
296 |
TInt DWinsSoundScRxPdd::SetVolume(TInt /*aVolume*/)
|
sl@0
|
297 |
{
|
sl@0
|
298 |
__KTRACE_SND(Kern::Printf(">DWinsSoundScRxPdd::SetVolume"));
|
sl@0
|
299 |
|
sl@0
|
300 |
// There's no adjustment of the record level on the wave in device.
|
sl@0
|
301 |
|
sl@0
|
302 |
return(KErrNone);
|
sl@0
|
303 |
}
|
sl@0
|
304 |
|
sl@0
|
305 |
/**
|
sl@0
|
306 |
Called from the LDD to prepare the audio device for recording.
|
sl@0
|
307 |
This function is always executed in driver thread context.
|
sl@0
|
308 |
@return KErrNone if successful, otherwise one of the other system wide error codes.
|
sl@0
|
309 |
*/
|
sl@0
|
310 |
TInt DWinsSoundScRxPdd::StartTransfer()
|
sl@0
|
311 |
{
|
sl@0
|
312 |
__KTRACE_SND(Kern::Printf(">DWinsSoundScRxPdd::StartTransfer"));
|
sl@0
|
313 |
|
sl@0
|
314 |
// Convert the enum representing the current sample rate into an integer
|
sl@0
|
315 |
TInt samplesPerSecond=RateInSamplesPerSecond(iSoundConfig.iRate);
|
sl@0
|
316 |
if (samplesPerSecond==0)
|
sl@0
|
317 |
return(KErrNotSupported);
|
sl@0
|
318 |
|
sl@0
|
319 |
// Now convert the sample rate into the number of bytes per second and save for later use
|
sl@0
|
320 |
iBytesPerSecond=samplesPerSecond;
|
sl@0
|
321 |
if (iSoundConfig.iChannels==2)
|
sl@0
|
322 |
iBytesPerSecond*=2;
|
sl@0
|
323 |
if (iSoundConfig.iEncoding==ESoundEncoding16BitPCM)
|
sl@0
|
324 |
iBytesPerSecond*=2;
|
sl@0
|
325 |
|
sl@0
|
326 |
iBytesRecordedBeforeLastPause = 0;
|
sl@0
|
327 |
iBytesSincePauseReportedToLdd = 0;
|
sl@0
|
328 |
|
sl@0
|
329 |
iRecordEnabled=ETrue;
|
sl@0
|
330 |
|
sl@0
|
331 |
// Open the record device with the current settings.
|
sl@0
|
332 |
iPendingRecord=0;
|
sl@0
|
333 |
iCompletedRecordBufHdrMask=0; // Reset the completion status mask
|
sl@0
|
334 |
TInt r = CreateRecordDevice();
|
sl@0
|
335 |
return(r);
|
sl@0
|
336 |
}
|
sl@0
|
337 |
|
sl@0
|
338 |
/**
|
sl@0
|
339 |
Called from the LDD to initiate the recording of a portion of data from the audio device.
|
sl@0
|
340 |
When the transfer is complete, the PDD signals this event using the LDD function RecordCallback().
|
sl@0
|
341 |
This function is always executed in driver thread context.
|
sl@0
|
342 |
@param aTransferID A value assigned by the LDD to allow it to uniquely identify a particular transfer fragment.
|
sl@0
|
343 |
@param aLinAddr The linear address within the shared chunk for storing the recorded data.
|
sl@0
|
344 |
@param aPhysAddr The physical address within the shared chunk for storing the recorded data.
|
sl@0
|
345 |
@param aNumBytes The number of bytes to be recorded.
|
sl@0
|
346 |
@return KErrNone if the transfer has been initiated successfully;
|
sl@0
|
347 |
KErrNotReady if the device is unable to accept the transfer for the moment;
|
sl@0
|
348 |
otherwise one of the other system-wide error codes.
|
sl@0
|
349 |
*/
|
sl@0
|
350 |
TInt DWinsSoundScRxPdd::TransferData(TUint aTransferID,TLinAddr aLinAddr,TPhysAddr /*aPhysAddr*/,TInt aNumBytes)
|
sl@0
|
351 |
{
|
sl@0
|
352 |
__KTRACE_SND(Kern::Printf(">DWinsSoundScRxPdd::TransferData(ID:%xH)",aTransferID));
|
sl@0
|
353 |
|
sl@0
|
354 |
// Check that we can accept the request
|
sl@0
|
355 |
if (aNumBytes>KWinsMaxAudioTransferLen)
|
sl@0
|
356 |
return(KErrArgument);
|
sl@0
|
357 |
if (iPendingRecord>=iWaveformBufMgr->iNumWaveformBufs) // LDD may issue multiple data transfers per buffer.
|
sl@0
|
358 |
return(KErrNotReady);
|
sl@0
|
359 |
|
sl@0
|
360 |
// Signal the windows thread to initiate the recording of a buffers worth of data from the wavein device.
|
sl@0
|
361 |
iPendingRecord++;
|
sl@0
|
362 |
RecordThreadCommand(ERecData,aTransferID,aLinAddr,aNumBytes);
|
sl@0
|
363 |
|
sl@0
|
364 |
// Although the windows thread runs at a higher priority, its not safe to assume we will always get pre-empted at this
|
sl@0
|
365 |
// point while the the higher priority thread processes and completes the request. Instead we need to wait until it
|
sl@0
|
366 |
// signals back completion of the command.
|
sl@0
|
367 |
Emulator::Escape();
|
sl@0
|
368 |
WaitForSingleObject(iDriverThreadSem,INFINITE);
|
sl@0
|
369 |
Emulator::Reenter();
|
sl@0
|
370 |
|
sl@0
|
371 |
return(iRecordThreadError);
|
sl@0
|
372 |
}
|
sl@0
|
373 |
|
sl@0
|
374 |
/**
|
sl@0
|
375 |
Called from the LDD to terminate the recording of a data from the device and to release any resources necessary for
|
sl@0
|
376 |
recording.
|
sl@0
|
377 |
The LDD will leave the audio device capturing record data even when there are no record requests pending from the client.
|
sl@0
|
378 |
Transfer will only be terminated when the client either issues RSoundSc::CancelRecordData() or closes the channel. Once
|
sl@0
|
379 |
this function had been called, the LDD will not issue any further TransferData() commands without first issueing a
|
sl@0
|
380 |
StartTransfer() command.
|
sl@0
|
381 |
This function is always executed in driver thread context.
|
sl@0
|
382 |
*/
|
sl@0
|
383 |
void DWinsSoundScRxPdd::StopTransfer()
|
sl@0
|
384 |
{
|
sl@0
|
385 |
__KTRACE_SND(Kern::Printf(">DWinsSoundScRxPdd::StopTransfer"));
|
sl@0
|
386 |
|
sl@0
|
387 |
// Signal the windows thread to stop it from sending any more buffers to wavein device.
|
sl@0
|
388 |
iStopSemaphore = CreateSemaphore(NULL, 0, 1, NULL);
|
sl@0
|
389 |
RecordThreadCommand(EStop);
|
sl@0
|
390 |
|
sl@0
|
391 |
// Need to wait for the record thread to finish using the record handle before it's safe to close the device and
|
sl@0
|
392 |
// set the handle to NULL.
|
sl@0
|
393 |
if (iStopSemaphore)
|
sl@0
|
394 |
{
|
sl@0
|
395 |
// Wait for the record thread to stop.
|
sl@0
|
396 |
Emulator::Escape();
|
sl@0
|
397 |
WaitForSingleObject(iStopSemaphore, INFINITE);
|
sl@0
|
398 |
Emulator::Reenter();
|
sl@0
|
399 |
|
sl@0
|
400 |
__HOST_LOCK;
|
sl@0
|
401 |
CloseHandle(iStopSemaphore);
|
sl@0
|
402 |
iStopSemaphore = NULL;
|
sl@0
|
403 |
}
|
sl@0
|
404 |
|
sl@0
|
405 |
// Make sure the DFC is not queued.
|
sl@0
|
406 |
iDfc.Cancel();
|
sl@0
|
407 |
|
sl@0
|
408 |
CloseRecordDevice(); // Close down the record device.
|
sl@0
|
409 |
iPendingRecord=0;
|
sl@0
|
410 |
iCompletedRecordBufHdrMask=0; // Reset the completion status mask
|
sl@0
|
411 |
}
|
sl@0
|
412 |
|
sl@0
|
413 |
/**
|
sl@0
|
414 |
Called from the LDD to halt the recording of data from the sound device but not to release any resources necessary for
|
sl@0
|
415 |
recording.
|
sl@0
|
416 |
All active transfers should be aborted. When recording is halted the PDD signals this event with a single call of the LDD
|
sl@0
|
417 |
function RecordCallback() - reporting back any partial data already received. If transfer is resumed later, the LDD will
|
sl@0
|
418 |
issue a new TransferData() request to re-commence data transfer.
|
sl@0
|
419 |
This function is always executed in driver thread context.
|
sl@0
|
420 |
@return KErrNone if successful, otherwise one of the other system wide error codes.
|
sl@0
|
421 |
*/
|
sl@0
|
422 |
TInt DWinsSoundScRxPdd::PauseTransfer()
|
sl@0
|
423 |
{
|
sl@0
|
424 |
__KTRACE_SND(Kern::Printf(">DWinsSoundScRxPdd::PauseTransfer"));
|
sl@0
|
425 |
|
sl@0
|
426 |
// Signal the windows thread to stop recording on the wavein device - aborting any transfers queued.
|
sl@0
|
427 |
RecordThreadCommand(EPause);
|
sl@0
|
428 |
|
sl@0
|
429 |
// Wait for the windows thread to complete the request
|
sl@0
|
430 |
Emulator::Escape();
|
sl@0
|
431 |
WaitForSingleObject(iDriverThreadSem,INFINITE);
|
sl@0
|
432 |
Emulator::Reenter();
|
sl@0
|
433 |
|
sl@0
|
434 |
// Make sure the DFC is not queued.
|
sl@0
|
435 |
iDfc.Cancel();
|
sl@0
|
436 |
|
sl@0
|
437 |
// The windows thread returns the total bytes recorded since the last pause (as reported by windows).
|
sl@0
|
438 |
TUint totalRecordedSincePause = iRecordThreadError;
|
sl@0
|
439 |
TUint lastTransferLength = totalRecordedSincePause - iBytesSincePauseReportedToLdd;
|
sl@0
|
440 |
Kern::Printf("totalRecordedSincePause %d - iBytesSincePauseReportedToLdd %d = lastTransferLength %d\n",
|
sl@0
|
441 |
totalRecordedSincePause, iBytesSincePauseReportedToLdd, lastTransferLength);
|
sl@0
|
442 |
|
sl@0
|
443 |
iBytesRecordedBeforeLastPause += totalRecordedSincePause;
|
sl@0
|
444 |
iBytesSincePauseReportedToLdd = 0;
|
sl@0
|
445 |
|
sl@0
|
446 |
if (iPendingRecord)
|
sl@0
|
447 |
{
|
sl@0
|
448 |
Ldd()->RecordCallback(0, KErrNone, lastTransferLength); // We can use a NULL tranfer ID when pausing.
|
sl@0
|
449 |
|
sl@0
|
450 |
// The LDD will abandon any other transfers queued so we can mark all buffers as not in use.
|
sl@0
|
451 |
for (TInt i=0 ; i<iWaveformBufMgr->iNumWaveformBufs ; i++)
|
sl@0
|
452 |
{
|
sl@0
|
453 |
TWaveformAudioBuf* buf=&iWaveformBufMgr->iWaveformAudioBuf[i];
|
sl@0
|
454 |
if (buf->iIsInUse)
|
sl@0
|
455 |
buf->iIsInUse=EFalse;
|
sl@0
|
456 |
}
|
sl@0
|
457 |
iPendingRecord=0;
|
sl@0
|
458 |
}
|
sl@0
|
459 |
|
sl@0
|
460 |
// Indicate that all request to Windows for recording have been cancelled
|
sl@0
|
461 |
iCompletedRecordBufHdrMask=0;
|
sl@0
|
462 |
|
sl@0
|
463 |
return(KErrNone);
|
sl@0
|
464 |
}
|
sl@0
|
465 |
|
sl@0
|
466 |
/**
|
sl@0
|
467 |
Called from the LDD to resume the recording of data from the sound device following a request to halt recording.
|
sl@0
|
468 |
Any active transfer would have been aborted when the device was halted so its just a case of re-creating the same setup
|
sl@0
|
469 |
acheived following StartTransfer().
|
sl@0
|
470 |
This function is always executed in driver thread context.
|
sl@0
|
471 |
@return KErrNone if successful, otherwise one of the other system wide error codes.
|
sl@0
|
472 |
*/
|
sl@0
|
473 |
TInt DWinsSoundScRxPdd::ResumeTransfer()
|
sl@0
|
474 |
{
|
sl@0
|
475 |
__KTRACE_SND(Kern::Printf(">DWinsSoundScRxPdd::ResumeTransfer"));
|
sl@0
|
476 |
|
sl@0
|
477 |
iPendingRecord=0;
|
sl@0
|
478 |
iCompletedRecordBufHdrMask=0; // All buffers would have completed during pausing (waveInReset) so clear mask.
|
sl@0
|
479 |
iRecordEnabled=ETrue; // Simply set the flag to enable the windows thread to restart sending buffers to wavein device.
|
sl@0
|
480 |
|
sl@0
|
481 |
// Signal the windows thread to resume recording on the wavein device.
|
sl@0
|
482 |
RecordThreadCommand(EResume);
|
sl@0
|
483 |
|
sl@0
|
484 |
return(KErrNone);
|
sl@0
|
485 |
}
|
sl@0
|
486 |
|
sl@0
|
487 |
/**
|
sl@0
|
488 |
Called from the LDD to power down the sound device.
|
sl@0
|
489 |
This function is always executed in driver thread context.
|
sl@0
|
490 |
*/
|
sl@0
|
491 |
void DWinsSoundScRxPdd::PowerDown()
|
sl@0
|
492 |
{
|
sl@0
|
493 |
|
sl@0
|
494 |
}
|
sl@0
|
495 |
|
sl@0
|
496 |
/**
|
sl@0
|
497 |
Called from the LDD to handle a custom configuration request.
|
sl@0
|
498 |
@param aFunction A number identifying the request.
|
sl@0
|
499 |
@param aParam A 32-bit value passed to the driver. Its meaning depends on the request.
|
sl@0
|
500 |
@return KErrNone if successful, otherwise one of the other system wide error codes.
|
sl@0
|
501 |
*/
|
sl@0
|
502 |
TInt DWinsSoundScRxPdd::CustomConfig(TInt /*aFunction*/,TAny* /*aParam*/)
|
sl@0
|
503 |
{
|
sl@0
|
504 |
return(KErrNotSupported);
|
sl@0
|
505 |
}
|
sl@0
|
506 |
|
sl@0
|
507 |
/**
|
sl@0
|
508 |
Called from the LDD to find out how many microseconds of data have been recorded. This is called
|
sl@0
|
509 |
in the context of the DFC thread.
|
sl@0
|
510 |
@param aTimeTransferred A reference to a variable into which to place the number of microseconds of audio.
|
sl@0
|
511 |
@param aStatus The current status of this channel
|
sl@0
|
512 |
@return KErrNone if time is valid or KErrNotSupported.
|
sl@0
|
513 |
*/
|
sl@0
|
514 |
TInt DWinsSoundScRxPdd::TimeTransferred(TInt64& aTimeRecorded, TInt aState)
|
sl@0
|
515 |
{
|
sl@0
|
516 |
TInt r=KErrGeneral;
|
sl@0
|
517 |
TInt64 ms=0;
|
sl@0
|
518 |
|
sl@0
|
519 |
if(iRecordDeviceHandle == 0)
|
sl@0
|
520 |
{
|
sl@0
|
521 |
// Recording not started yet
|
sl@0
|
522 |
aTimeRecorded = 0;
|
sl@0
|
523 |
return KErrNone;
|
sl@0
|
524 |
}
|
sl@0
|
525 |
|
sl@0
|
526 |
// Kern::Printf("DWinsSoundScRxPdd::TimeTransferred - (iBytesSincePauseReportedToLdd=%d)\n", iBytesSincePauseReportedToLdd);
|
sl@0
|
527 |
if (aState == DSoundScLdd::EPaused)
|
sl@0
|
528 |
{
|
sl@0
|
529 |
// Kern::Printf("DWinsSoundScRxPdd::TimeTransferred (paused) - iBytesRecordedBeforeLastPause %d\n", iBytesRecordedBeforeLastPause);
|
sl@0
|
530 |
// Just use the paused number of bytes
|
sl@0
|
531 |
ms=((iBytesRecordedBeforeLastPause/iBytesPerSecond)*1000);
|
sl@0
|
532 |
TUint remainder=(iBytesRecordedBeforeLastPause%iBytesPerSecond);
|
sl@0
|
533 |
ms+=((remainder*1000)/iBytesPerSecond);
|
sl@0
|
534 |
ms*=1000;
|
sl@0
|
535 |
aTimeRecorded=ms;
|
sl@0
|
536 |
r=KErrNone;
|
sl@0
|
537 |
}
|
sl@0
|
538 |
|
sl@0
|
539 |
TInt64 bytesTransferredSincePause = 0;
|
sl@0
|
540 |
// If no hardware is present then we need to use iBytesSincePauseReportedToLdd + a fudge factor to allow
|
sl@0
|
541 |
// the number of bytes processed by the "hardware" within the current transfer.
|
sl@0
|
542 |
if(iNoHardware)
|
sl@0
|
543 |
{
|
sl@0
|
544 |
// Determine the # of milliseconds that have passed since the last timer triggered
|
sl@0
|
545 |
DWORD currentTime = timeGetTime();
|
sl@0
|
546 |
DWORD timeSinceLastEvent = (currentTime - iLastTimerEventTime);
|
sl@0
|
547 |
|
sl@0
|
548 |
// Clamp the resulting value to the duration of the timer, to prevent the millisecond count
|
sl@0
|
549 |
// going backwards if Windows is busy and latency becomes an issue
|
sl@0
|
550 |
if (timeSinceLastEvent > iSimulatedMsecDuration)
|
sl@0
|
551 |
timeSinceLastEvent = iSimulatedMsecDuration;
|
sl@0
|
552 |
|
sl@0
|
553 |
bytesTransferredSincePause = iBytesSincePauseReportedToLdd;
|
sl@0
|
554 |
WAVEHDR *buf = iWaveformBufMgr->iPendingBufList[0];
|
sl@0
|
555 |
if(buf)
|
sl@0
|
556 |
{
|
sl@0
|
557 |
// Add on an estimate of the progress of the current transfer
|
sl@0
|
558 |
bytesTransferredSincePause += ((buf->dwBufferLength * timeSinceLastEvent) / iSimulatedMsecDuration);
|
sl@0
|
559 |
}
|
sl@0
|
560 |
}
|
sl@0
|
561 |
else
|
sl@0
|
562 |
{
|
sl@0
|
563 |
// Get the number of bytes recorded by the Windows audio system
|
sl@0
|
564 |
MMTIME time;
|
sl@0
|
565 |
time.wType=TIME_BYTES;
|
sl@0
|
566 |
if ((waveInGetPosition(iRecordDeviceHandle,&time,sizeof(time)) != MMSYSERR_NOERROR) ||
|
sl@0
|
567 |
(time.wType != TIME_BYTES))
|
sl@0
|
568 |
{
|
sl@0
|
569 |
// If requesting the number of bytes recorded is not supported, wType will be
|
sl@0
|
570 |
// changed to what was actually returned, so check for this and don't continue
|
sl@0
|
571 |
// if we got anything other than bytes
|
sl@0
|
572 |
return KErrNotSupported;
|
sl@0
|
573 |
}
|
sl@0
|
574 |
bytesTransferredSincePause = time.u.cb;
|
sl@0
|
575 |
}
|
sl@0
|
576 |
|
sl@0
|
577 |
// Kern::Printf("DWinsSoundScRxPdd::TimeTransferred - iBytesRecordedBeforeLastPause %d + bytesTransferredSincePause %d total %d (iNoHardware %d)\n",
|
sl@0
|
578 |
// iBytesRecordedBeforeLastPause, TUint32(bytesTransferredSincePause), TUint32(bytesTransferredSincePause + iBytesRecordedBeforeLastPause), iNoHardware);
|
sl@0
|
579 |
// Convert the number of bytes recorded into microseconds and return it
|
sl@0
|
580 |
ms=(((bytesTransferredSincePause + iBytesRecordedBeforeLastPause)/iBytesPerSecond)*1000);
|
sl@0
|
581 |
TUint64 remainder=((bytesTransferredSincePause + iBytesRecordedBeforeLastPause)%iBytesPerSecond);
|
sl@0
|
582 |
ms+=((remainder*1000)/iBytesPerSecond);
|
sl@0
|
583 |
ms*=1000;
|
sl@0
|
584 |
aTimeRecorded=ms;
|
sl@0
|
585 |
r=KErrNone;
|
sl@0
|
586 |
|
sl@0
|
587 |
return(r);
|
sl@0
|
588 |
}
|
sl@0
|
589 |
|
sl@0
|
590 |
/**
|
sl@0
|
591 |
Prepare the waveform audio buffer for record.
|
sl@0
|
592 |
@param aRecordDeviceHandle The handle to the waveform audio input device.
|
sl@0
|
593 |
*/
|
sl@0
|
594 |
void TWaveformAudioBuf::DoPrepareIn(HWAVEIN aRecordDeviceHandle)
|
sl@0
|
595 |
{
|
sl@0
|
596 |
MMRESULT res = waveInPrepareHeader(aRecordDeviceHandle,&iBufHdr,sizeof(WAVEHDR));
|
sl@0
|
597 |
__KTRACE_SND(Kern::Printf(" waveInPrepareHeader(BufNo:%d Pos:%x Len:%d)-%d",iBufNum,iBufHdr.lpData,iBufHdr.dwBufferLength,res));
|
sl@0
|
598 |
__ASSERT_ALWAYS(res==MMSYSERR_NOERROR,Kern::Fault("DWinsSoundScTxPddWIPH", res)); //WaveInPrepareHeader error.
|
sl@0
|
599 |
}
|
sl@0
|
600 |
|
sl@0
|
601 |
/**
|
sl@0
|
602 |
Cleanup the preparation performed when the waveform audio buffer was prepared for record.
|
sl@0
|
603 |
@param aRecordDeviceHandle The handle to the waveform audio input device.
|
sl@0
|
604 |
*/
|
sl@0
|
605 |
void TWaveformAudioBuf::DoUnprepareIn(HWAVEIN aRecordDeviceHandle)
|
sl@0
|
606 |
{
|
sl@0
|
607 |
MMRESULT res = waveInUnprepareHeader(aRecordDeviceHandle,&iBufHdr,sizeof(WAVEHDR));
|
sl@0
|
608 |
__KTRACE_SND(Kern::Printf(" waveInUnprepareHeader(BufNo:%d)-%d",iBufNum,res));
|
sl@0
|
609 |
__ASSERT_ALWAYS(res==MMSYSERR_NOERROR,Kern::Fault("DWinsSoundScTxPddWIUH",res)); //WaveInUnprepareHeader error.
|
sl@0
|
610 |
}
|
sl@0
|
611 |
|
sl@0
|
612 |
/**
|
sl@0
|
613 |
The waveform input callback function to handle data block transfer completion.
|
sl@0
|
614 |
This function is always executed in windows thread context.
|
sl@0
|
615 |
@param aHdr A pointer to the header for the waveform audio buffer just transferred.
|
sl@0
|
616 |
*/
|
sl@0
|
617 |
void DWinsSoundScRxPdd::WaveInProc(WAVEHDR* aHdr)
|
sl@0
|
618 |
{
|
sl@0
|
619 |
TInt waveBufId=aHdr->dwUser; // Work out which waveform audio buffer is completing.
|
sl@0
|
620 |
// Kern::Printf("DWinsSoundScRxPdd::WaveInProc waveBufId %d", waveBufId);
|
sl@0
|
621 |
|
sl@0
|
622 |
StartOfInterrupt();
|
sl@0
|
623 |
iCompletedRecordBufHdrMask|=(1<<waveBufId); // Update the completion status mask
|
sl@0
|
624 |
iDfc.Add(); // Queue RecordDfc().
|
sl@0
|
625 |
EndOfInterrupt();
|
sl@0
|
626 |
}
|
sl@0
|
627 |
|
sl@0
|
628 |
/**
|
sl@0
|
629 |
The DFC used to handle data block record completion.
|
sl@0
|
630 |
This function is always executed in driver thread context.
|
sl@0
|
631 |
@param aPtr A pointer to the physical channel object.
|
sl@0
|
632 |
*/
|
sl@0
|
633 |
void DWinsSoundScRxPdd::RecordDfc(TAny* aPtr)
|
sl@0
|
634 |
{
|
sl@0
|
635 |
TInt i;
|
sl@0
|
636 |
DWinsSoundScRxPdd& drv=*(DWinsSoundScRxPdd*)aPtr;
|
sl@0
|
637 |
|
sl@0
|
638 |
// More than 1 transfer may have completed so loop until all completions are handled
|
sl@0
|
639 |
while (drv.iCompletedRecordBufHdrMask)
|
sl@0
|
640 |
{
|
sl@0
|
641 |
// Find the buffer ID of the next transfer that has completed
|
sl@0
|
642 |
for (i=0 ; i<32 && !(drv.iCompletedRecordBufHdrMask&(1<<i)) ; i++) {}
|
sl@0
|
643 |
__ASSERT_ALWAYS(i<drv.iWaveformBufMgr->iNumWaveformBufs,PANIC());
|
sl@0
|
644 |
__e32_atomic_and_ord32(&drv.iCompletedRecordBufHdrMask, ~(1u<<i)); // Clear this bit in the mask
|
sl@0
|
645 |
|
sl@0
|
646 |
// Update the status of the waveform audio buffer which is completing
|
sl@0
|
647 |
TWaveformAudioBuf& buf=drv.iWaveformBufMgr->iWaveformAudioBuf[i];
|
sl@0
|
648 |
buf.iIsInUse=EFalse;
|
sl@0
|
649 |
|
sl@0
|
650 |
// Callback the LDD passing the information for the transfer that has completed
|
sl@0
|
651 |
drv.iPendingRecord--;
|
sl@0
|
652 |
__KTRACE_SND(Kern::Printf(" Read complete(BufNo:%x Pos:%x Len:%d)",i,buf.iBufHdr.lpData,buf.iBufHdr.dwBufferLength));
|
sl@0
|
653 |
drv.iBytesSincePauseReportedToLdd += buf.iBufHdr.dwBufferLength;
|
sl@0
|
654 |
drv.Ldd()->RecordCallback(buf.iTransferID,KErrNone,buf.iBufHdr.dwBufferLength);
|
sl@0
|
655 |
}
|
sl@0
|
656 |
}
|
sl@0
|
657 |
|
sl@0
|
658 |
/**
|
sl@0
|
659 |
Issue a request from the driver thread to the windows thread to execute a command.
|
sl@0
|
660 |
@param aCommand The identifier of the command to be executed.
|
sl@0
|
661 |
@param aArg0 A first command argument, its meaning depends on the command.
|
sl@0
|
662 |
@param aArg1 A second command argument, its meaning depends on the command.
|
sl@0
|
663 |
@param aArg2 A third command argument, its meaning depends on the command.
|
sl@0
|
664 |
This function is always executed in driver thread context.
|
sl@0
|
665 |
*/
|
sl@0
|
666 |
void DWinsSoundScRxPdd::RecordThreadCommand(TThreadCommand aCommand,TInt aArg0,TInt aArg1,TInt aArg2)
|
sl@0
|
667 |
{
|
sl@0
|
668 |
__KTRACE_SND(Kern::Printf(">DWinsSoundScRxPdd:RecordThreadCommand"));
|
sl@0
|
669 |
iRecordCommand = aCommand;
|
sl@0
|
670 |
iRecordCommandArg0 = aArg0;
|
sl@0
|
671 |
iRecordCommandArg1 = aArg1;
|
sl@0
|
672 |
iRecordCommandArg2 = aArg2;
|
sl@0
|
673 |
|
sl@0
|
674 |
__HOST_LOCK;
|
sl@0
|
675 |
|
sl@0
|
676 |
ReleaseSemaphore(iRecordThreadSem,1,NULL);
|
sl@0
|
677 |
}
|
sl@0
|
678 |
|
sl@0
|
679 |
/**
|
sl@0
|
680 |
Pass a value from the windows thread to the driver thread.
|
sl@0
|
681 |
This function is always executed in windows thread context.
|
sl@0
|
682 |
@param aError The value to the passed to the driver thread.
|
sl@0
|
683 |
*/
|
sl@0
|
684 |
void DWinsSoundScRxPdd::RecordThreadNotifyDriver(TInt aError)
|
sl@0
|
685 |
{
|
sl@0
|
686 |
iRecordThreadError = aError;
|
sl@0
|
687 |
BOOL ret = ReleaseSemaphore(iDriverThreadSem,1,NULL);
|
sl@0
|
688 |
__ASSERT_ALWAYS(ret == TRUE,PANIC()); //Unexpected Windows Error
|
sl@0
|
689 |
}
|
sl@0
|
690 |
|
sl@0
|
691 |
#pragma warning(disable : 4702) // unreachable code
|
sl@0
|
692 |
/**
|
sl@0
|
693 |
Open the waveform input device for record. Use a default device identifier in order to select a device
|
sl@0
|
694 |
capable of meeting the current audio configuration.
|
sl@0
|
695 |
This function can be executed in either driver thread or windows thread context.
|
sl@0
|
696 |
@pre The data member DWinsSoundScRxPdd::iSoundConfig must be setup with the current audio configuration.
|
sl@0
|
697 |
*/
|
sl@0
|
698 |
TInt DWinsSoundScRxPdd::OpenWaveInDevice()
|
sl@0
|
699 |
{
|
sl@0
|
700 |
WAVEFORMATEX format;
|
sl@0
|
701 |
format.wFormatTag = WAVE_FORMAT_PCM;
|
sl@0
|
702 |
TUint16 bitsPerSample = 8;
|
sl@0
|
703 |
|
sl@0
|
704 |
switch (iSoundConfig.iEncoding)
|
sl@0
|
705 |
{
|
sl@0
|
706 |
case ESoundEncoding8BitPCM:
|
sl@0
|
707 |
break;
|
sl@0
|
708 |
case ESoundEncoding16BitPCM:
|
sl@0
|
709 |
bitsPerSample = 16;
|
sl@0
|
710 |
break;
|
sl@0
|
711 |
default:
|
sl@0
|
712 |
return KErrNotSupported;
|
sl@0
|
713 |
};
|
sl@0
|
714 |
|
sl@0
|
715 |
TInt rateInSamplesPerSecond=RateInSamplesPerSecond(iSoundConfig.iRate);
|
sl@0
|
716 |
format.nChannels = TUint16(iSoundConfig.iChannels);
|
sl@0
|
717 |
format.nSamplesPerSec = rateInSamplesPerSecond;
|
sl@0
|
718 |
format.nAvgBytesPerSec = rateInSamplesPerSecond * iSoundConfig.iChannels * bitsPerSample / 8;
|
sl@0
|
719 |
format.nBlockAlign = TUint16(iSoundConfig.iChannels * bitsPerSample / 8);
|
sl@0
|
720 |
format.wBitsPerSample = bitsPerSample;
|
sl@0
|
721 |
format.cbSize = 0;
|
sl@0
|
722 |
|
sl@0
|
723 |
MMRESULT res = MMSYSERR_NOERROR;
|
sl@0
|
724 |
|
sl@0
|
725 |
__COND_HOST_LOCK;
|
sl@0
|
726 |
if (iNoHardware)
|
sl@0
|
727 |
{
|
sl@0
|
728 |
timeBeginPeriod(KMMTimerRes);
|
sl@0
|
729 |
iRecordDeviceHandle = (HWAVEIN)1;
|
sl@0
|
730 |
}
|
sl@0
|
731 |
else
|
sl@0
|
732 |
{
|
sl@0
|
733 |
res = waveInOpen(&iRecordDeviceHandle, WAVE_MAPPER, &format, (DWORD)::WaveInProc, (DWORD)this, CALLBACK_FUNCTION);
|
sl@0
|
734 |
|
sl@0
|
735 |
// On some builds of Windows (such as Windows Server 2003), the waveInGetDevCaps() trick in
|
sl@0
|
736 |
// DoCreate() won't work, so we have another special check for missing hardware here
|
sl@0
|
737 |
if ((res == MMSYSERR_NODRIVER) || (res == MMSYSERR_BADDEVICEID))
|
sl@0
|
738 |
{
|
sl@0
|
739 |
// Pretend there was no error and switch into hardware emulation mode
|
sl@0
|
740 |
res = MMSYSERR_NOERROR;
|
sl@0
|
741 |
iNoHardware = ETrue;
|
sl@0
|
742 |
iRecordDeviceHandle = (HWAVEIN)1;
|
sl@0
|
743 |
iWaveformBufMgr->iIsHardware = EFalse;
|
sl@0
|
744 |
timeBeginPeriod(KMMTimerRes);
|
sl@0
|
745 |
}
|
sl@0
|
746 |
}
|
sl@0
|
747 |
|
sl@0
|
748 |
switch (res)
|
sl@0
|
749 |
{
|
sl@0
|
750 |
case MMSYSERR_NOERROR: // No error
|
sl@0
|
751 |
return(KErrNone);
|
sl@0
|
752 |
case MMSYSERR_ALLOCATED: // Specified resource is already allocated.
|
sl@0
|
753 |
return(KErrInUse);
|
sl@0
|
754 |
case WAVERR_BADFORMAT: // Attempted to open with an unsupported waveform-audio format
|
sl@0
|
755 |
return(KErrNotSupported);
|
sl@0
|
756 |
case MMSYSERR_NOMEM: // Unable to allocate or lock memory.
|
sl@0
|
757 |
return(KErrNoMemory);
|
sl@0
|
758 |
default:
|
sl@0
|
759 |
return(KErrUnknown);
|
sl@0
|
760 |
}
|
sl@0
|
761 |
}
|
sl@0
|
762 |
#pragma warning(default : 4702) // unreachable code
|
sl@0
|
763 |
|
sl@0
|
764 |
/**
|
sl@0
|
765 |
Open the audio input device.
|
sl@0
|
766 |
This function is always executed in driver thread context.
|
sl@0
|
767 |
@pre The data members DWinsSoundScRxPdd::iSoundConfig must be setup with the current audio configuration.
|
sl@0
|
768 |
*/
|
sl@0
|
769 |
TInt DWinsSoundScRxPdd::CreateRecordDevice(TBool aCheckDevice)
|
sl@0
|
770 |
{
|
sl@0
|
771 |
// Check if the waveform input device is already open.
|
sl@0
|
772 |
if (iRecordDeviceHandle)
|
sl@0
|
773 |
return(KErrNone);
|
sl@0
|
774 |
|
sl@0
|
775 |
__HOST_LOCK;
|
sl@0
|
776 |
|
sl@0
|
777 |
// Open the waveform input device for recording.
|
sl@0
|
778 |
TInt err = OpenWaveInDevice();
|
sl@0
|
779 |
if (err != KErrNone)
|
sl@0
|
780 |
return(err);
|
sl@0
|
781 |
|
sl@0
|
782 |
__HOST_LOCK_OFF;
|
sl@0
|
783 |
|
sl@0
|
784 |
if (!aCheckDevice)
|
sl@0
|
785 |
{
|
sl@0
|
786 |
// Now, re-allocate a set of the waveform audio blocks in advance of any recording. Also, prepare one of these
|
sl@0
|
787 |
// for each buffer within the shared chunk. Need to be in critical section while re-allocating the audio blocks.
|
sl@0
|
788 |
NKern::ThreadEnterCS();
|
sl@0
|
789 |
err=iWaveformBufMgr->ReAllocAndUpdate(Ldd()->BufConfig(),Ldd()->ChunkBase(),(TInt)iRecordDeviceHandle);
|
sl@0
|
790 |
NKern::ThreadLeaveCS();
|
sl@0
|
791 |
}
|
sl@0
|
792 |
|
sl@0
|
793 |
return(err);
|
sl@0
|
794 |
}
|
sl@0
|
795 |
|
sl@0
|
796 |
/**
|
sl@0
|
797 |
Close down the record device.
|
sl@0
|
798 |
This function is always executed in driver thread context.
|
sl@0
|
799 |
*/
|
sl@0
|
800 |
void DWinsSoundScRxPdd::CloseRecordDevice()
|
sl@0
|
801 |
{
|
sl@0
|
802 |
__COND_HOST_LOCK;
|
sl@0
|
803 |
|
sl@0
|
804 |
if (iNoHardware)
|
sl@0
|
805 |
timeEndPeriod(KMMTimerRes);
|
sl@0
|
806 |
|
sl@0
|
807 |
HWAVEIN handle = iRecordDeviceHandle;
|
sl@0
|
808 |
|
sl@0
|
809 |
if (handle)
|
sl@0
|
810 |
{
|
sl@0
|
811 |
if (!iNoHardware)
|
sl@0
|
812 |
waveInReset(handle); // Stop recording.
|
sl@0
|
813 |
|
sl@0
|
814 |
// Un-prepare all the waveform audio buffers.
|
sl@0
|
815 |
for (TInt i=0 ; i<iWaveformBufMgr->iNumWaveformBufs ; i++)
|
sl@0
|
816 |
iWaveformBufMgr->iWaveformAudioBuf[i].Unprepare((TInt)handle);
|
sl@0
|
817 |
|
sl@0
|
818 |
if (!iNoHardware)
|
sl@0
|
819 |
waveInClose(handle); // Close the wavein device.
|
sl@0
|
820 |
|
sl@0
|
821 |
iRecordDeviceHandle = NULL;
|
sl@0
|
822 |
}
|
sl@0
|
823 |
}
|
sl@0
|
824 |
|
sl@0
|
825 |
/**
|
sl@0
|
826 |
The thread function for the record windows thread.
|
sl@0
|
827 |
This function is always executed in windows thread context.
|
sl@0
|
828 |
@pre The data members DWinsSoundScRxPdd::iSoundConfig must be setup with the current audio configuration.
|
sl@0
|
829 |
*/
|
sl@0
|
830 |
void DWinsSoundScRxPdd::RecordThread()
|
sl@0
|
831 |
{
|
sl@0
|
832 |
iRecordThreadSem = CreateSemaphore(NULL,0,0x7fffffff,NULL);
|
sl@0
|
833 |
__ASSERT_ALWAYS(iRecordThreadSem,PANIC()); //No Windows Memory
|
sl@0
|
834 |
iRecordTimerEvent=CreateEvent(NULL,TRUE,FALSE,NULL);
|
sl@0
|
835 |
HANDLE objects[2];
|
sl@0
|
836 |
objects[0]=iRecordThreadSem; // Indicates command from driver thread
|
sl@0
|
837 |
objects[1]=iRecordTimerEvent;
|
sl@0
|
838 |
|
sl@0
|
839 |
// Signal driver of successful setup
|
sl@0
|
840 |
RecordThreadNotifyDriver(KErrNone);
|
sl@0
|
841 |
ResetEvent(iRecordTimerEvent);
|
sl@0
|
842 |
FOREVER
|
sl@0
|
843 |
{
|
sl@0
|
844 |
DWORD ret=WaitForMultipleObjectsEx(2,objects,FALSE,INFINITE,TRUE);
|
sl@0
|
845 |
|
sl@0
|
846 |
switch (ret)
|
sl@0
|
847 |
{
|
sl@0
|
848 |
case WAIT_OBJECT_0: // Command received from the driver thread.
|
sl@0
|
849 |
if (ProcessRecordCommand(iRecordCommand,iRecordCommandArg0,iRecordCommandArg1,iRecordCommandArg2)==KErrCompletion)
|
sl@0
|
850 |
return; // ********* Exit thread **************
|
sl@0
|
851 |
break;
|
sl@0
|
852 |
case WAIT_OBJECT_0+1:
|
sl@0
|
853 |
HandleRecordTimerEvent();
|
sl@0
|
854 |
break;
|
sl@0
|
855 |
}
|
sl@0
|
856 |
}
|
sl@0
|
857 |
}
|
sl@0
|
858 |
|
sl@0
|
859 |
/**
|
sl@0
|
860 |
Process a request from the driver thread to execute a command.
|
sl@0
|
861 |
This function is always executed in windows thread context.
|
sl@0
|
862 |
@param aCommand The identifier of the command to be executed.
|
sl@0
|
863 |
@param aArg0 A first command argument, its meaning depends on the command.
|
sl@0
|
864 |
@param aArg1 A second command argument, its meaning depends on the command.
|
sl@0
|
865 |
@param aArg2 A third command argument, its meaning depends on the command.
|
sl@0
|
866 |
@return KErrCompletion if the command to exit the windows thread has been received;
|
sl@0
|
867 |
KErrNone otherwise;
|
sl@0
|
868 |
*/
|
sl@0
|
869 |
TInt DWinsSoundScRxPdd::ProcessRecordCommand(TThreadCommand aCommand,TInt aArg0,TInt aArg1,TInt aArg2)
|
sl@0
|
870 |
{
|
sl@0
|
871 |
__KTRACE_SND(Kern::Printf(">DWinsSoundScRxPdd:ProcessRecordCommand(%d)",aCommand));
|
sl@0
|
872 |
switch(aCommand)
|
sl@0
|
873 |
{
|
sl@0
|
874 |
case ERecData: // Initiate the recording of a buffers worth of data from the wavein device.
|
sl@0
|
875 |
{
|
sl@0
|
876 |
if (iRecordEnabled)
|
sl@0
|
877 |
{
|
sl@0
|
878 |
// Acquire a windows waveform audio buffer for the transfer.
|
sl@0
|
879 |
char* startAddress=(char*)aArg1;
|
sl@0
|
880 |
TInt bytesToRecord=aArg2;
|
sl@0
|
881 |
__ASSERT_ALWAYS(bytesToRecord>0,PANIC());
|
sl@0
|
882 |
TWaveformAudioBuf* waveformAudioBuf=iWaveformBufMgr->AcquireBuf(startAddress,bytesToRecord,(TInt)iRecordDeviceHandle);
|
sl@0
|
883 |
waveformAudioBuf->iTransferID=(TUint)aArg0;
|
sl@0
|
884 |
waveformAudioBuf->iBufHdr.dwBufferLength=bytesToRecord;
|
sl@0
|
885 |
|
sl@0
|
886 |
if (!iNoHardware)
|
sl@0
|
887 |
{
|
sl@0
|
888 |
// This machine has a wavein device present. Send the buffer to the wavein device.
|
sl@0
|
889 |
waveInStart(iRecordDeviceHandle); // Start input on the wavein device - safe to call this when already started.
|
sl@0
|
890 |
MMRESULT res = waveInAddBuffer(iRecordDeviceHandle,&waveformAudioBuf->iBufHdr,sizeof(WAVEHDR));
|
sl@0
|
891 |
__KTRACE_SND(Kern::Printf(" waveInAddBuffer(ID:%x Pos:%x Len:%d)-%d",aArg0,startAddress,bytesToRecord,res));
|
sl@0
|
892 |
__ASSERT_ALWAYS(res == MMSYSERR_NOERROR,PANIC()); //WaveInAddBuffer Error
|
sl@0
|
893 |
}
|
sl@0
|
894 |
else
|
sl@0
|
895 |
{
|
sl@0
|
896 |
// This machine has no audio hardware present so simulate the wavein device using a timer.
|
sl@0
|
897 |
AddToPendingList(&waveformAudioBuf->iBufHdr,iWaveformBufMgr->iPendingBufList); // Queue the buffer on the pending list
|
sl@0
|
898 |
|
sl@0
|
899 |
// Check if the timer needs starting/re-starting
|
sl@0
|
900 |
if (!iTimerActive)
|
sl@0
|
901 |
{
|
sl@0
|
902 |
iLastTimerEventTime = timeGetTime();
|
sl@0
|
903 |
StartTimer(&waveformAudioBuf->iBufHdr);
|
sl@0
|
904 |
}
|
sl@0
|
905 |
}
|
sl@0
|
906 |
}
|
sl@0
|
907 |
|
sl@0
|
908 |
// Signal the driver thread that we have completed the command.
|
sl@0
|
909 |
RecordThreadNotifyDriver(KErrNone);
|
sl@0
|
910 |
break;
|
sl@0
|
911 |
}
|
sl@0
|
912 |
|
sl@0
|
913 |
|
sl@0
|
914 |
case EStop: // Terminate the recording of data from the wavein device.
|
sl@0
|
915 |
{
|
sl@0
|
916 |
iRecordEnabled=EFalse; // Stop the windows thread from sending any more buffers to wavein device.
|
sl@0
|
917 |
|
sl@0
|
918 |
if (iNoHardware)
|
sl@0
|
919 |
{
|
sl@0
|
920 |
// This machine has no audio hardware present so simulates the waveout device using a timer.
|
sl@0
|
921 |
StopTimer(ETrue); // Stop the timer and cancel any buffers pending
|
sl@0
|
922 |
}
|
sl@0
|
923 |
|
sl@0
|
924 |
// Leave the driver thread to close down the record device.
|
sl@0
|
925 |
|
sl@0
|
926 |
// Signal the driver thread that we have completed the command.
|
sl@0
|
927 |
if (iStopSemaphore)
|
sl@0
|
928 |
{
|
sl@0
|
929 |
LONG prev;
|
sl@0
|
930 |
ReleaseSemaphore(iStopSemaphore,1,&prev);
|
sl@0
|
931 |
}
|
sl@0
|
932 |
break;
|
sl@0
|
933 |
}
|
sl@0
|
934 |
|
sl@0
|
935 |
case EExit: // Close down the record device and exit the windows thread.
|
sl@0
|
936 |
{
|
sl@0
|
937 |
if (!iNoHardware)
|
sl@0
|
938 |
{
|
sl@0
|
939 |
// This machine has a wavein device present.
|
sl@0
|
940 |
if (iRecordDeviceHandle)
|
sl@0
|
941 |
{
|
sl@0
|
942 |
waveInReset(iRecordDeviceHandle); // Stop recording on the wavein device.
|
sl@0
|
943 |
waveInClose(iRecordDeviceHandle); // Close the wavein device.
|
sl@0
|
944 |
}
|
sl@0
|
945 |
}
|
sl@0
|
946 |
else
|
sl@0
|
947 |
{
|
sl@0
|
948 |
// This machine has no audio hardware present so simulates the waveout device using a timer.
|
sl@0
|
949 |
StopTimer(ETrue); // Stop the timer and cancel any buffers pending.
|
sl@0
|
950 |
}
|
sl@0
|
951 |
// Logically the record device is now shut so clear the handle.
|
sl@0
|
952 |
iRecordDeviceHandle = 0;
|
sl@0
|
953 |
|
sl@0
|
954 |
// Signal the driver thread that we have completed the command.
|
sl@0
|
955 |
if (iDeathSemaphore)
|
sl@0
|
956 |
{
|
sl@0
|
957 |
LONG prev;
|
sl@0
|
958 |
ReleaseSemaphore(iDeathSemaphore,1,&prev);
|
sl@0
|
959 |
}
|
sl@0
|
960 |
return(KErrCompletion); // ********* Exit thread **************
|
sl@0
|
961 |
}
|
sl@0
|
962 |
|
sl@0
|
963 |
case EPause: // Halt the recording of data from the wavein device.
|
sl@0
|
964 |
iRecordEnabled=EFalse;
|
sl@0
|
965 |
|
sl@0
|
966 |
DWORD position;
|
sl@0
|
967 |
if (!iNoHardware)
|
sl@0
|
968 |
{
|
sl@0
|
969 |
// Need to try to work out how much of the current audio buffer has been filled.
|
sl@0
|
970 |
MMTIME time;
|
sl@0
|
971 |
time.wType = TIME_BYTES;
|
sl@0
|
972 |
HWAVEIN handle = iRecordDeviceHandle;
|
sl@0
|
973 |
waveInGetPosition(handle,&time,sizeof(MMTIME));
|
sl@0
|
974 |
position = time.u.cb;
|
sl@0
|
975 |
|
sl@0
|
976 |
// Stop recording. (Windows will mark all pending audio buffers as done).
|
sl@0
|
977 |
waveInReset(handle);
|
sl@0
|
978 |
}
|
sl@0
|
979 |
else
|
sl@0
|
980 |
{
|
sl@0
|
981 |
// This machine has no audio hardware present so simulates the waveout device using a timer.
|
sl@0
|
982 |
|
sl@0
|
983 |
// Determine the # of milliseconds that have passed since the last timer triggered
|
sl@0
|
984 |
DWORD currentTime = timeGetTime();
|
sl@0
|
985 |
DWORD timeSinceLastEvent = (currentTime - iLastTimerEventTime);
|
sl@0
|
986 |
|
sl@0
|
987 |
// Clamp the resulting value to the duration of the timer, to prevent the millisecond count
|
sl@0
|
988 |
// going backwards if Windows is busy and latency becomes an issue
|
sl@0
|
989 |
if (timeSinceLastEvent > iSimulatedMsecDuration)
|
sl@0
|
990 |
timeSinceLastEvent = iSimulatedMsecDuration;
|
sl@0
|
991 |
|
sl@0
|
992 |
TUint bytesTransferredSincePause = iBytesSincePauseReportedToLdd;
|
sl@0
|
993 |
WAVEHDR *buf = iWaveformBufMgr->iPendingBufList[0];
|
sl@0
|
994 |
if(buf)
|
sl@0
|
995 |
{
|
sl@0
|
996 |
// Add on an estimate of the progress of the current transfer
|
sl@0
|
997 |
bytesTransferredSincePause += ((buf->dwBufferLength * timeSinceLastEvent) / iSimulatedMsecDuration);
|
sl@0
|
998 |
}
|
sl@0
|
999 |
|
sl@0
|
1000 |
position = bytesTransferredSincePause;
|
sl@0
|
1001 |
|
sl@0
|
1002 |
StopTimer(ETrue); // Stop the timer and cancel any buffers pending
|
sl@0
|
1003 |
}
|
sl@0
|
1004 |
|
sl@0
|
1005 |
// Signal the driver thread that we have stopped recording - returning info. on any partially filled buffer.
|
sl@0
|
1006 |
RecordThreadNotifyDriver(position);
|
sl@0
|
1007 |
break;
|
sl@0
|
1008 |
|
sl@0
|
1009 |
case EResume:
|
sl@0
|
1010 |
if (iNoHardware)
|
sl@0
|
1011 |
{
|
sl@0
|
1012 |
// Determine how long we were paused for and add that time to the time the timer last
|
sl@0
|
1013 |
// triggered. This will allow us to continue as though we had never been paused
|
sl@0
|
1014 |
iLastTimerEventTime = timeGetTime();
|
sl@0
|
1015 |
}
|
sl@0
|
1016 |
|
sl@0
|
1017 |
break;
|
sl@0
|
1018 |
}
|
sl@0
|
1019 |
return(KErrNone);
|
sl@0
|
1020 |
}
|
sl@0
|
1021 |
|
sl@0
|
1022 |
/**
|
sl@0
|
1023 |
Handle a timer expiry event. This is only used when no audio hardware is present, with a timer expiry corresponding
|
sl@0
|
1024 |
to the end of a data block transfer.
|
sl@0
|
1025 |
This function is always executed in windows thread context.
|
sl@0
|
1026 |
*/
|
sl@0
|
1027 |
void DWinsSoundScRxPdd::HandleRecordTimerEvent()
|
sl@0
|
1028 |
{
|
sl@0
|
1029 |
__KTRACE_SND(Kern::Printf(">DWinsSoundScRxPdd:HandleRecordTimerEvent"));
|
sl@0
|
1030 |
ResetEvent(iRecordTimerEvent); // Reset the event
|
sl@0
|
1031 |
|
sl@0
|
1032 |
// Remove the audio buffer just filled from the pending list and save it for the driver thread.
|
sl@0
|
1033 |
WAVEHDR* buf=RemoveFromPendingList(iWaveformBufMgr->iPendingBufList);
|
sl@0
|
1034 |
__ASSERT_ALWAYS(buf != NULL,PANIC());
|
sl@0
|
1035 |
TInt waveBufId=buf->dwUser; // Work out which waveform audio buffer is completing.
|
sl@0
|
1036 |
|
sl@0
|
1037 |
// Check if there are more audio buffers waiting to be played
|
sl@0
|
1038 |
buf=iWaveformBufMgr->iPendingBufList[0];
|
sl@0
|
1039 |
if (buf)
|
sl@0
|
1040 |
{
|
sl@0
|
1041 |
iLastTimerEventTime = timeGetTime();
|
sl@0
|
1042 |
StartTimer(buf); // Re-start the timer
|
sl@0
|
1043 |
}
|
sl@0
|
1044 |
else
|
sl@0
|
1045 |
iTimerActive=EFalse;
|
sl@0
|
1046 |
|
sl@0
|
1047 |
// Notify that another audio buffer has been filled.
|
sl@0
|
1048 |
StartOfInterrupt();
|
sl@0
|
1049 |
iCompletedRecordBufHdrMask|=(1<<waveBufId); // Update the completion status mask
|
sl@0
|
1050 |
iDfc.Add();
|
sl@0
|
1051 |
EndOfInterrupt();
|
sl@0
|
1052 |
return;
|
sl@0
|
1053 |
}
|
sl@0
|
1054 |
|
sl@0
|
1055 |
/**
|
sl@0
|
1056 |
Initialise the data member DWinsSoundScRxPdd::iCaps with the capabilities of this audio device.
|
sl@0
|
1057 |
*/
|
sl@0
|
1058 |
void DWinsSoundScRxPdd::SetCaps()
|
sl@0
|
1059 |
{
|
sl@0
|
1060 |
__KTRACE_SND(Kern::Printf(">DWinsSoundScRxPdd::SetCaps"));
|
sl@0
|
1061 |
|
sl@0
|
1062 |
// The data transfer direction for this unit is record.
|
sl@0
|
1063 |
iCaps.iDirection=ESoundDirRecord;
|
sl@0
|
1064 |
|
sl@0
|
1065 |
// Assume this unit supports mono or stereo.
|
sl@0
|
1066 |
iCaps.iChannels=KSoundMonoChannel|KSoundStereoChannel;
|
sl@0
|
1067 |
|
sl@0
|
1068 |
// Assume this unit supports all sample rates.
|
sl@0
|
1069 |
iCaps.iRates=(KSoundRate7350Hz|KSoundRate8000Hz|KSoundRate8820Hz|KSoundRate9600Hz|KSoundRate11025Hz|
|
sl@0
|
1070 |
KSoundRate12000Hz|KSoundRate14700Hz|KSoundRate16000Hz|KSoundRate22050Hz|KSoundRate24000Hz|
|
sl@0
|
1071 |
KSoundRate29400Hz|KSoundRate32000Hz|KSoundRate44100Hz|KSoundRate48000Hz);
|
sl@0
|
1072 |
|
sl@0
|
1073 |
// Assume this unit supports 8bit and 16bit PCM encoding.
|
sl@0
|
1074 |
iCaps.iEncodings=(KSoundEncoding8BitPCM|KSoundEncoding16BitPCM);
|
sl@0
|
1075 |
|
sl@0
|
1076 |
// This unit only supports interleaved data format
|
sl@0
|
1077 |
iCaps.iDataFormats=KSoundDataFormatInterleaved;
|
sl@0
|
1078 |
|
sl@0
|
1079 |
// The minimum request size that the device can support.
|
sl@0
|
1080 |
iCaps.iRequestMinSize=0; // No restriction
|
sl@0
|
1081 |
|
sl@0
|
1082 |
// The request alignment that this device requires.
|
sl@0
|
1083 |
iCaps.iRequestAlignment=0; // No restriction
|
sl@0
|
1084 |
|
sl@0
|
1085 |
// This unit is not capable of detecting changes in hardware configuration.
|
sl@0
|
1086 |
iCaps.iHwConfigNotificationSupport=EFalse;
|
sl@0
|
1087 |
}
|
sl@0
|
1088 |
/**
|
sl@0
|
1089 |
Start the audio timer.
|
sl@0
|
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
|
sl@0
|
1091 |
equivelent to that incurred when transferring audio data over a real audio device.
|
sl@0
|
1092 |
@param aBuffer The audio buffer which would have been transferred had a real audio device been present. This contains
|
sl@0
|
1093 |
information on the number of bytes to transfer.
|
sl@0
|
1094 |
*/
|
sl@0
|
1095 |
void DWinsSoundScRxPdd::StartTimer(WAVEHDR* aBuffer)
|
sl@0
|
1096 |
{
|
sl@0
|
1097 |
// First, need to calculate the duration of the timer in milliseconds.
|
sl@0
|
1098 |
TInt bytesToPlay=aBuffer->dwBufferLength;
|
sl@0
|
1099 |
iSimulatedMsecDuration = bytesToPlay*1000;
|
sl@0
|
1100 |
iSimulatedMsecDuration /= (RateInSamplesPerSecond(iSoundConfig.iRate) * iSoundConfig.iChannels);
|
sl@0
|
1101 |
if (iSoundConfig.iEncoding==ESoundEncoding16BitPCM)
|
sl@0
|
1102 |
iSimulatedMsecDuration /= 2;
|
sl@0
|
1103 |
if (iSoundConfig.iEncoding==ESoundEncoding24BitPCM)
|
sl@0
|
1104 |
iSimulatedMsecDuration /= 3;
|
sl@0
|
1105 |
if (iSimulatedMsecDuration<=0)
|
sl@0
|
1106 |
iSimulatedMsecDuration=1; // Round up to 1ms or timeSetEvent() will return an error.
|
sl@0
|
1107 |
|
sl@0
|
1108 |
MMRESULT res = timeSetEvent(iSimulatedMsecDuration, KMMTimerRes, (LPTIMECALLBACK)iRecordTimerEvent, 0, TIME_ONESHOT | TIME_CALLBACK_EVENT_SET);
|
sl@0
|
1109 |
__ASSERT_ALWAYS(res != NULL,PANIC()); // timeSetEvent error.
|
sl@0
|
1110 |
iTimerID = res; // Save the identifier for the new timer event.
|
sl@0
|
1111 |
iTimerActive=ETrue;
|
sl@0
|
1112 |
}
|
sl@0
|
1113 |
|
sl@0
|
1114 |
/**
|
sl@0
|
1115 |
Stop the audio timer.
|
sl@0
|
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
|
sl@0
|
1117 |
equivelent to that incurred when transferring audio data over a real audio device.
|
sl@0
|
1118 |
@param aCancellAll Set to ETrue in order to discard any buffers queued on the pending buffer list. EFalse otherwise.
|
sl@0
|
1119 |
*/
|
sl@0
|
1120 |
void DWinsSoundScRxPdd::StopTimer(TBool aCancelAll)
|
sl@0
|
1121 |
{
|
sl@0
|
1122 |
if (iTimerActive)
|
sl@0
|
1123 |
{
|
sl@0
|
1124 |
MMRESULT res = timeKillEvent(iTimerID);
|
sl@0
|
1125 |
__ASSERT_ALWAYS(res == TIMERR_NOERROR,PANIC()); // timeKillEvent error
|
sl@0
|
1126 |
|
sl@0
|
1127 |
if (aCancelAll)
|
sl@0
|
1128 |
{
|
sl@0
|
1129 |
WAVEHDR* b;
|
sl@0
|
1130 |
do
|
sl@0
|
1131 |
b=RemoveFromPendingList(iWaveformBufMgr->iPendingBufList);
|
sl@0
|
1132 |
while(b);
|
sl@0
|
1133 |
}
|
sl@0
|
1134 |
}
|
sl@0
|
1135 |
iTimerActive=EFalse;
|
sl@0
|
1136 |
}
|
sl@0
|
1137 |
|
sl@0
|
1138 |
|