|
1 |
diff -r 72f180130a42 build/temp.win32-2.6/Release/pjsip/third_party/portaudio/src/hostapi/wmme/pa_win_wmme.c
|
|
2 |
--- src/hostapi/wmme/pa_win_wmme.c Wed Aug 11 19:38:40 2010 +0200
|
|
3 |
+++ src/hostapi/wmme/pa_win_wmme.c Sat Aug 14 11:42:36 2010 +0200
|
|
4 |
@@ -117,6 +117,7 @@
|
|
5 |
#include <stdlib.h>
|
|
6 |
#include <math.h>
|
|
7 |
#include <windows.h>
|
|
8 |
+#include <dbt.h>
|
|
9 |
#include <mmsystem.h>
|
|
10 |
#ifndef UNDER_CE
|
|
11 |
#include <process.h>
|
|
12 |
@@ -153,9 +154,9 @@
|
|
13 |
|
|
14 |
/* use CreateThread for CYGWIN, _beginthreadex for all others */
|
|
15 |
#ifndef __CYGWIN__
|
|
16 |
-#define CREATE_THREAD (HANDLE)_beginthreadex( 0, 0, ProcessingThreadProc, stream, 0, &stream->processingThreadId )
|
|
17 |
+#define CREATE_THREAD(ThreadProc, arg, threadId) (HANDLE)_beginthreadex( 0, 0, ThreadProc, arg, 0, threadId )
|
|
18 |
#else
|
|
19 |
-#define CREATE_THREAD CreateThread( 0, 0, ProcessingThreadProc, stream, 0, &stream->processingThreadId )
|
|
20 |
+#define CREATE_THREAD(ThreadProc, arg, threadId) CreateThread( 0, 0, ThreadProc, arg, 0, threadId )
|
|
21 |
#endif
|
|
22 |
|
|
23 |
#if (defined(UNDER_CE))
|
|
24 |
@@ -223,6 +224,8 @@
|
|
25 |
#endif /* __cplusplus */
|
|
26 |
|
|
27 |
static void Terminate( struct PaUtilHostApiRepresentation *hostApi );
|
|
28 |
+static PaError RescanDevices( struct PaUtilHostApiRepresentation *hostApi );
|
|
29 |
+static DWORD WINAPI DeviceDetectionThreadProc( void *pArg );
|
|
30 |
static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
|
|
31 |
PaStream** stream,
|
|
32 |
const PaStreamParameters *inputParameters,
|
|
33 |
@@ -1076,7 +1079,7 @@
|
|
34 |
InitializeDefaultDeviceIdsFromEnv( winMmeHostApi );
|
|
35 |
|
|
36 |
(*hostApi)->Terminate = Terminate;
|
|
37 |
- (*hostApi)->RescanDevices = NULL;
|
|
38 |
+ (*hostApi)->RescanDevices = RescanDevices;
|
|
39 |
(*hostApi)->OpenStream = OpenStream;
|
|
40 |
(*hostApi)->IsFormatSupported = IsFormatSupported;
|
|
41 |
|
|
42 |
@@ -1091,6 +1094,9 @@
|
|
43 |
GetStreamTime, PaUtil_DummyGetCpuLoad,
|
|
44 |
ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable );
|
|
45 |
|
|
46 |
+ HANDLE deviceDetectionThread;
|
|
47 |
+ deviceDetectionThread = CREATE_THREAD(DeviceDetectionThreadProc, (void*)(winMmeHostApi), NULL);
|
|
48 |
+
|
|
49 |
return result;
|
|
50 |
|
|
51 |
error:
|
|
52 |
@@ -2758,6 +2764,302 @@
|
|
53 |
return result;
|
|
54 |
}
|
|
55 |
|
|
56 |
+/* compare stored vs actual list of devices, and update the list if there are
|
|
57 |
+ new or removed devices.
|
|
58 |
+*/
|
|
59 |
+static PaError RescanDevices( struct PaUtilHostApiRepresentation *hostApi )
|
|
60 |
+{
|
|
61 |
+ PaError result = paNoError;
|
|
62 |
+ int i;
|
|
63 |
+ PaWinMmeHostApiRepresentation *winMmeHostApi = (PaWinMmeHostApiRepresentation*)hostApi;
|
|
64 |
+ int inputDeviceCount, outputDeviceCount, maximumPossibleDeviceCount;
|
|
65 |
+ PaWinMmeDeviceInfo *deviceInfoArray;
|
|
66 |
+ int deviceInfoInitializationSucceeded;
|
|
67 |
+ PaTime defaultLowLatency, defaultHighLatency;
|
|
68 |
+ PaHostApiIndex hostApiIndex = Pa_HostApiTypeIdToHostApiIndex(hostApi->info.type);
|
|
69 |
+
|
|
70 |
+ maximumPossibleDeviceCount = 0;
|
|
71 |
+
|
|
72 |
+ inputDeviceCount = waveInGetNumDevs();
|
|
73 |
+ if( inputDeviceCount > 0 ) maximumPossibleDeviceCount += inputDeviceCount + 1;
|
|
74 |
+
|
|
75 |
+ outputDeviceCount = waveOutGetNumDevs();
|
|
76 |
+ if( outputDeviceCount > 0 ) maximumPossibleDeviceCount += outputDeviceCount + 1;
|
|
77 |
+
|
|
78 |
+ if( inputDeviceCount + 1 == winMmeHostApi->inputDeviceCount && outputDeviceCount + 1 == winMmeHostApi->outputDeviceCount)
|
|
79 |
+ {
|
|
80 |
+ /* No new devices in list */
|
|
81 |
+ return paNoError;
|
|
82 |
+ }
|
|
83 |
+ hostApi->info.deviceCount = 0;
|
|
84 |
+ hostApi->info.defaultInputDevice = paNoDevice;
|
|
85 |
+ hostApi->info.defaultOutputDevice = paNoDevice;
|
|
86 |
+ winMmeHostApi->inputDeviceCount = 0;
|
|
87 |
+ winMmeHostApi->outputDeviceCount = 0;
|
|
88 |
+
|
|
89 |
+ /* WARN: doc says this operation is time consuming */
|
|
90 |
+ PaUtil_GroupFreeMemory( winMmeHostApi->allocations, hostApi->deviceInfos );
|
|
91 |
+ if( maximumPossibleDeviceCount > 0 )
|
|
92 |
+ {
|
|
93 |
+ hostApi->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory(
|
|
94 |
+ winMmeHostApi->allocations, sizeof(PaDeviceInfo*) * maximumPossibleDeviceCount );
|
|
95 |
+ if( !hostApi->deviceInfos ) return paInsufficientMemory;
|
|
96 |
+
|
|
97 |
+ /* allocate all device info structs in a contiguous block */
|
|
98 |
+ deviceInfoArray = (PaWinMmeDeviceInfo*)PaUtil_GroupAllocateMemory(
|
|
99 |
+ winMmeHostApi->allocations, sizeof(PaWinMmeDeviceInfo) * maximumPossibleDeviceCount );
|
|
100 |
+ if( !deviceInfoArray ) return paInsufficientMemory;
|
|
101 |
+
|
|
102 |
+
|
|
103 |
+ winMmeHostApi->winMmeDeviceIds = (UINT*)PaUtil_GroupAllocateMemory(
|
|
104 |
+ winMmeHostApi->allocations, sizeof(int) * maximumPossibleDeviceCount );
|
|
105 |
+ if( !winMmeHostApi->winMmeDeviceIds ) return paInsufficientMemory;
|
|
106 |
+
|
|
107 |
+ GetDefaultLatencies( &defaultLowLatency, &defaultHighLatency );
|
|
108 |
+
|
|
109 |
+ /* Rescan input devices */
|
|
110 |
+ if( inputDeviceCount > 0 )
|
|
111 |
+ {
|
|
112 |
+ /* -1 is the WAVE_MAPPER */
|
|
113 |
+ for( i = -1; i < inputDeviceCount; ++i )
|
|
114 |
+ {
|
|
115 |
+ UINT winMmeDeviceId = (UINT)((i==-1) ? WAVE_MAPPER : i);
|
|
116 |
+ PaWinMmeDeviceInfo *wmmeDeviceInfo = &deviceInfoArray[ hostApi->info.deviceCount ];
|
|
117 |
+ PaDeviceInfo *deviceInfo = &wmmeDeviceInfo->inheritedDeviceInfo;
|
|
118 |
+ deviceInfo->structVersion = 2;
|
|
119 |
+ deviceInfo->hostApi = hostApiIndex;
|
|
120 |
+
|
|
121 |
+ deviceInfo->maxInputChannels = 0;
|
|
122 |
+ deviceInfo->maxOutputChannels = 0;
|
|
123 |
+
|
|
124 |
+ deviceInfo->defaultLowInputLatency = defaultLowLatency;
|
|
125 |
+ deviceInfo->defaultLowOutputLatency = defaultLowLatency;
|
|
126 |
+ deviceInfo->defaultHighInputLatency = defaultHighLatency;
|
|
127 |
+ deviceInfo->defaultHighOutputLatency = defaultHighLatency;
|
|
128 |
+
|
|
129 |
+ result = InitializeInputDeviceInfo( winMmeHostApi, wmmeDeviceInfo,
|
|
130 |
+ winMmeDeviceId, &deviceInfoInitializationSucceeded );
|
|
131 |
+ if( result != paNoError )
|
|
132 |
+ return result;
|
|
133 |
+
|
|
134 |
+ if( deviceInfoInitializationSucceeded )
|
|
135 |
+ {
|
|
136 |
+ if( hostApi->info.defaultInputDevice == paNoDevice )
|
|
137 |
+ hostApi->info.defaultInputDevice = hostApi->info.deviceCount;
|
|
138 |
+
|
|
139 |
+ winMmeHostApi->winMmeDeviceIds[ hostApi->info.deviceCount ] = winMmeDeviceId;
|
|
140 |
+ hostApi->deviceInfos[ hostApi->info.deviceCount ] = deviceInfo;
|
|
141 |
+
|
|
142 |
+ winMmeHostApi->inputDeviceCount++;
|
|
143 |
+ hostApi->info.deviceCount++;
|
|
144 |
+ }
|
|
145 |
+ }
|
|
146 |
+ }
|
|
147 |
+
|
|
148 |
+ /* Rescan output devices */
|
|
149 |
+ if( outputDeviceCount > 0 )
|
|
150 |
+ {
|
|
151 |
+ /* -1 is the WAVE_MAPPER */
|
|
152 |
+ for( i = -1; i < outputDeviceCount; ++i )
|
|
153 |
+ {
|
|
154 |
+ UINT winMmeDeviceId = (UINT)((i==-1) ? WAVE_MAPPER : i);
|
|
155 |
+ PaWinMmeDeviceInfo *wmmeDeviceInfo = &deviceInfoArray[ hostApi->info.deviceCount ];
|
|
156 |
+ PaDeviceInfo *deviceInfo = &wmmeDeviceInfo->inheritedDeviceInfo;
|
|
157 |
+ deviceInfo->structVersion = 2;
|
|
158 |
+ deviceInfo->hostApi = hostApiIndex;
|
|
159 |
+
|
|
160 |
+ deviceInfo->maxInputChannels = 0;
|
|
161 |
+ deviceInfo->maxOutputChannels = 0;
|
|
162 |
+
|
|
163 |
+ deviceInfo->defaultLowInputLatency = defaultLowLatency;
|
|
164 |
+ deviceInfo->defaultLowOutputLatency = defaultLowLatency;
|
|
165 |
+ deviceInfo->defaultHighInputLatency = defaultHighLatency;
|
|
166 |
+ deviceInfo->defaultHighOutputLatency = defaultHighLatency;
|
|
167 |
+
|
|
168 |
+ result = InitializeOutputDeviceInfo( winMmeHostApi, wmmeDeviceInfo,
|
|
169 |
+ winMmeDeviceId, &deviceInfoInitializationSucceeded );
|
|
170 |
+ if( result != paNoError )
|
|
171 |
+ return result;
|
|
172 |
+
|
|
173 |
+ if( deviceInfoInitializationSucceeded )
|
|
174 |
+ {
|
|
175 |
+ if( hostApi->info.defaultOutputDevice == paNoDevice )
|
|
176 |
+ hostApi->info.defaultOutputDevice = hostApi->info.deviceCount;
|
|
177 |
+
|
|
178 |
+ winMmeHostApi->winMmeDeviceIds[ hostApi->info.deviceCount ] = winMmeDeviceId;
|
|
179 |
+ hostApi->deviceInfos[ hostApi->info.deviceCount ] = deviceInfo;
|
|
180 |
+
|
|
181 |
+ winMmeHostApi->outputDeviceCount++;
|
|
182 |
+ hostApi->info.deviceCount++;
|
|
183 |
+ }
|
|
184 |
+ }
|
|
185 |
+ }
|
|
186 |
+ }
|
|
187 |
+ return paNoError;
|
|
188 |
+}
|
|
189 |
+
|
|
190 |
+/* Processes OS messages arriving at the hWnd window */
|
|
191 |
+INT_PTR WINAPI ProcessOSMessage( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam )
|
|
192 |
+{
|
|
193 |
+ /* winMmeHostApi is used in order to query the number of audio devices currently handled by PA */
|
|
194 |
+ static PaWinMmeHostApiRepresentation *winMmeHostApi_ = NULL;
|
|
195 |
+ switch( message )
|
|
196 |
+ {
|
|
197 |
+ case WM_CREATE:
|
|
198 |
+ /* Initialize hostApi pointer on the first run. */
|
|
199 |
+ if (winMmeHostApi_ == NULL)
|
|
200 |
+ {
|
|
201 |
+ CREATESTRUCT *CrtStrPtr = (CREATESTRUCT *) lParam;
|
|
202 |
+ winMmeHostApi_ = (PaWinMmeHostApiRepresentation*)(CrtStrPtr->lpCreateParams);
|
|
203 |
+ }
|
|
204 |
+ break;
|
|
205 |
+ case WM_DEVICECHANGE:
|
|
206 |
+ /* Possible insertion or removal of device. There's some issues:
|
|
207 |
+
|
|
208 |
+ - Some devices/drivers does not trigger arrival nor
|
|
209 |
+ removecomplete events, but only devnodes_changed events.
|
|
210 |
+ Therefore, we process all of those type of events.
|
|
211 |
+
|
|
212 |
+ - Some hardware can send many devnodes_changed events at the
|
|
213 |
+ same time (up to ~15 of such events). These batches are
|
|
214 |
+ detected using temporal locality, using constMaxBatchPeriod_
|
|
215 |
+ and processedTimeStamp_. Once the device is detected, the
|
|
216 |
+ rest of redundant events are discarded. In order to know if
|
|
217 |
+ there's a new device or not, actual audio devices count is
|
|
218 |
+ compared to stored audio devices count (via winMmeHostApi_).
|
|
219 |
+ A possible improvement would be to process each message in a
|
|
220 |
+ separate thread.
|
|
221 |
+
|
|
222 |
+ - Hardware takes some time to settle and be recognized by
|
|
223 |
+ drivers. A small window of time is given in order to account
|
|
224 |
+ for this (constMaxSettleTime_);
|
|
225 |
+
|
|
226 |
+ Settle time should be slightly lower than batch period.
|
|
227 |
+ */
|
|
228 |
+ if ( wParam == DBT_DEVICEARRIVAL || wParam == DBT_DEVICEREMOVECOMPLETE || wParam == DBT_DEVNODES_CHANGED )
|
|
229 |
+ {
|
|
230 |
+ const int constMaxBatchPeriod_ = 3; /* seconds */
|
|
231 |
+ const int constMaxSettleTime_ = (constMaxBatchPeriod_ * 1000) - 500; /* milliseconds */
|
|
232 |
+
|
|
233 |
+ /* Initialize reference timestamp on first run, using a past
|
|
234 |
+ time, so that first event belongs to a new batch. */
|
|
235 |
+ static time_t processedTimeStamp_ = 0;
|
|
236 |
+ if ( processedTimeStamp_ == 0 ) processedTimeStamp_ = time( NULL ) - ( constMaxBatchPeriod_ * 2 );
|
|
237 |
+
|
|
238 |
+ /* Loop that allows hardware to settle */
|
|
239 |
+ int settleTimeLeft = constMaxSettleTime_;
|
|
240 |
+ while ( settleTimeLeft > 0 )
|
|
241 |
+ {
|
|
242 |
+ /* Check if actual devices lists (I/O) sizes have actually
|
|
243 |
+ changed before notifying upper levels */
|
|
244 |
+ if( waveInGetNumDevs() + 1 != winMmeHostApi_->inputDeviceCount || waveOutGetNumDevs() + 1 != winMmeHostApi_->outputDeviceCount)
|
|
245 |
+ {
|
|
246 |
+ /* Hardware actually changed */
|
|
247 |
+ PaUtil_DevicesChanged( paUtilHardwareDevicesChanged );
|
|
248 |
+ processedTimeStamp_ = time( NULL );
|
|
249 |
+ break;
|
|
250 |
+ }
|
|
251 |
+ else
|
|
252 |
+ {
|
|
253 |
+ /* Hardware hasn't changed [yet] */
|
|
254 |
+ //if ( difftime( time( NULL ), processedTimeStamp_ ) < constMaxBatchPeriod_ )
|
|
255 |
+ {
|
|
256 |
+ /* We're still in the same batch of messages, disregard */
|
|
257 |
+ //processedTimeStamp_ = time( NULL );
|
|
258 |
+ }
|
|
259 |
+ /* Hardware settling pass... */
|
|
260 |
+ Sleep(250);
|
|
261 |
+ settleTimeLeft -= 250;
|
|
262 |
+ }
|
|
263 |
+ }
|
|
264 |
+ }
|
|
265 |
+ break;
|
|
266 |
+ case WM_CLOSE:
|
|
267 |
+ if ( ! DestroyWindow( hWnd ) )
|
|
268 |
+ {
|
|
269 |
+ PA_DEBUG(("ProcessOSMessage: Couldn't destroy message window.\n"));
|
|
270 |
+ }
|
|
271 |
+ break;
|
|
272 |
+ case WM_DESTROY:
|
|
273 |
+ PostQuitMessage( 0 );
|
|
274 |
+ break;
|
|
275 |
+ default:
|
|
276 |
+ break;
|
|
277 |
+ }
|
|
278 |
+ return 1;
|
|
279 |
+}
|
|
280 |
+
|
|
281 |
+
|
|
282 |
+/* Creates the window that will receive OS messages */
|
|
283 |
+static PaError CreateOSMessagesWindow( PaWinMmeHostApiRepresentation *winMmeHostApi )
|
|
284 |
+{
|
|
285 |
+ PaError result = paUnanticipatedHostError;
|
|
286 |
+
|
|
287 |
+ /* Set up and register window class */
|
|
288 |
+ WNDCLASSEX wndClass;
|
|
289 |
+ ZeroMemory( &wndClass, sizeof(WNDCLASSEX) );
|
|
290 |
+ wndClass.cbSize = sizeof(WNDCLASSEX);
|
|
291 |
+ wndClass.style = CS_OWNDC;
|
|
292 |
+ wndClass.lpfnWndProc = (WNDPROC)(ProcessOSMessage);
|
|
293 |
+ wndClass.hInstance = (HINSTANCE)(GetModuleHandle( 0 ));
|
|
294 |
+ wndClass.lpszClassName = "DeviceChangeMessageWindow";
|
|
295 |
+
|
|
296 |
+ if ( RegisterClassEx(&wndClass) )
|
|
297 |
+ {
|
|
298 |
+ /* Create the window that will receive OS messages */
|
|
299 |
+ HWND hWnd = CreateWindowEx( 0, "DeviceChangeMessageWindow", NULL, 0, 0, 0, 0, 0, NULL, NULL, NULL, (LPVOID)(winMmeHostApi) );
|
|
300 |
+ if ( hWnd != NULL )
|
|
301 |
+ {
|
|
302 |
+ if ( UpdateWindow( hWnd ) != 0 )
|
|
303 |
+ {
|
|
304 |
+ result = paNoError;
|
|
305 |
+ }
|
|
306 |
+ }
|
|
307 |
+ }
|
|
308 |
+
|
|
309 |
+ return result;
|
|
310 |
+}
|
|
311 |
+
|
|
312 |
+static PaError DispatchOSMessages()
|
|
313 |
+{
|
|
314 |
+ PaError result = paNoError;
|
|
315 |
+ MSG msg;
|
|
316 |
+ int retVal;
|
|
317 |
+
|
|
318 |
+ /* Process OS messages with low cpu-usage wait loop */
|
|
319 |
+ while( (retVal = GetMessage( &msg, NULL, 0, 0 ) ) != 0 )
|
|
320 |
+ {
|
|
321 |
+ if ( retVal == -1 )
|
|
322 |
+ {
|
|
323 |
+ PA_DEBUG("DispatchOSMessages: Couldn't process OS message.\n");
|
|
324 |
+ result = paUnanticipatedHostError;
|
|
325 |
+ break;
|
|
326 |
+ }
|
|
327 |
+ else
|
|
328 |
+ {
|
|
329 |
+ TranslateMessage( &msg );
|
|
330 |
+ DispatchMessage( &msg );
|
|
331 |
+ }
|
|
332 |
+ }
|
|
333 |
+
|
|
334 |
+ return result;
|
|
335 |
+}
|
|
336 |
+static DWORD WINAPI DeviceDetectionThreadProc( void *pArg )
|
|
337 |
+{
|
|
338 |
+ PaWinMmeHostApiRepresentation *winMmeHostApi = (PaWinMmeHostApiRepresentation*)(pArg);
|
|
339 |
+ DWORD result = -1;
|
|
340 |
+
|
|
341 |
+ if ( CreateOSMessagesWindow(winMmeHostApi) == paNoError )
|
|
342 |
+ {
|
|
343 |
+ if ( DispatchOSMessages() == paNoError )
|
|
344 |
+ {
|
|
345 |
+ result = 0;
|
|
346 |
+ }
|
|
347 |
+ }
|
|
348 |
+
|
|
349 |
+ return result;
|
|
350 |
+}
|
|
351 |
+
|
|
352 |
|
|
353 |
static DWORD WINAPI ProcessingThreadProc( void *pArg )
|
|
354 |
{
|
|
355 |
@@ -3312,7 +3614,7 @@
|
|
356 |
if( result != paNoError ) goto error;
|
|
357 |
|
|
358 |
/* Create thread that waits for audio buffers to be ready for processing. */
|
|
359 |
- stream->processingThread = CREATE_THREAD;
|
|
360 |
+ stream->processingThread = CREATE_THREAD(ProcessingThreadProc, stream, &stream->processingThreadId);
|
|
361 |
if( !stream->processingThread )
|
|
362 |
{
|
|
363 |
result = paUnanticipatedHostError;
|