/[pcsx2_0.9.7]/trunk/3rdparty/portaudio/src/hostapi/wasapi/pa_win_wasapi.c
ViewVC logotype

Contents of /trunk/3rdparty/portaudio/src/hostapi/wasapi/pa_win_wasapi.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 401 - (show annotations) (download)
Fri Feb 25 17:31:09 2011 UTC (9 years, 5 months ago) by william
File MIME type: text/plain
File size: 159203 byte(s)
Auto Commited Import of: pcsx2-0.9.7-DEBUG (upstream: v0.9.7.4358 local: v0.9.7.313-latest) in ./trunk
1 /*
2 * Portable Audio I/O Library WASAPI implementation
3 * Copyright (c) 2006-2010 David Viens, Dmitry Kostjuchenko
4 *
5 * Based on the Open Source API proposed by Ross Bencina
6 * Copyright (c) 1999-2002 Ross Bencina, Phil Burk
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining
9 * a copy of this software and associated documentation files
10 * (the "Software"), to deal in the Software without restriction,
11 * including without limitation the rights to use, copy, modify, merge,
12 * publish, distribute, sublicense, and/or sell copies of the Software,
13 * and to permit persons to whom the Software is furnished to do so,
14 * subject to the following conditions:
15 *
16 * The above copyright notice and this permission notice shall be
17 * included in all copies or substantial portions of the Software.
18 *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
22 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
23 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
24 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
25 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26 */
27
28 /*
29 * The text above constitutes the entire PortAudio license; however,
30 * the PortAudio community also makes the following non-binding requests:
31 *
32 * Any person wishing to distribute modifications to the Software is
33 * requested to send the modifications to the original developer so that
34 * they can be incorporated into the canonical version. It is also
35 * requested that these non-binding requests be included along with the
36 * license above.
37 */
38
39 /** @file
40 @ingroup hostapi_src
41 @brief WASAPI implementation of support for a host API.
42 @note pa_wasapi currently requires minimum VC 2005, and the latest Vista SDK
43 */
44
45 #define WIN32_LEAN_AND_MEAN // exclude rare headers
46 #include <windows.h>
47 #include <stdio.h>
48 #include <process.h>
49 #include <assert.h>
50 #include <mmsystem.h>
51 #include <mmreg.h> // must be before other Wasapi headers
52 #if defined(_MSC_VER) && (_MSC_VER >= 1400)
53 #include <Avrt.h>
54 #define COBJMACROS
55 #include <Audioclient.h>
56 #include <endpointvolume.h>
57 #define INITGUID // Avoid additional linkage of static libs, excessive code will be optimized out by the compiler
58 #include <mmdeviceapi.h>
59 #include <functiondiscoverykeys.h>
60 #include <devicetopology.h> // Used to get IKsJackDescription interface
61 #undef INITGUID
62 #endif
63 #ifndef __MWERKS__
64 #include <malloc.h>
65 #include <memory.h>
66 #endif /* __MWERKS__ */
67
68 #include "pa_util.h"
69 #include "pa_allocation.h"
70 #include "pa_hostapi.h"
71 #include "pa_stream.h"
72 #include "pa_cpuload.h"
73 #include "pa_process.h"
74 #include "pa_debugprint.h"
75 #include "pa_win_wasapi.h"
76
77 #ifndef NTDDI_VERSION
78
79 #undef WINVER
80 #undef _WIN32_WINNT
81 #define WINVER 0x0600 // VISTA
82 #define _WIN32_WINNT WINVER
83
84 #ifndef _AVRT_ //<< fix MinGW dummy compile by defining missing type: AVRT_PRIORITY
85 typedef enum _AVRT_PRIORITY
86 {
87 AVRT_PRIORITY_LOW = -1,
88 AVRT_PRIORITY_NORMAL,
89 AVRT_PRIORITY_HIGH,
90 AVRT_PRIORITY_CRITICAL
91 } AVRT_PRIORITY, *PAVRT_PRIORITY;
92 #endif
93
94 #include <basetyps.h> // << for IID/CLSID
95 #include <rpcsal.h>
96 #include <sal.h>
97
98 #ifndef __LPCGUID_DEFINED__
99 #define __LPCGUID_DEFINED__
100 typedef const GUID *LPCGUID;
101 #endif
102
103 #ifndef PROPERTYKEY_DEFINED
104 #define PROPERTYKEY_DEFINED
105 typedef struct _tagpropertykey
106 {
107 GUID fmtid;
108 DWORD pid;
109 } PROPERTYKEY;
110 #endif
111
112 #ifdef __midl_proxy
113 #define __MIDL_CONST
114 #else
115 #define __MIDL_CONST const
116 #endif
117
118 #ifdef WIN64
119 #include <wtypes.h>
120 typedef LONG NTSTATUS;
121 #define FASTCALL
122 #include <oleidl.h>
123 #include <objidl.h>
124 #else
125 typedef struct _BYTE_BLOB
126 {
127 unsigned long clSize;
128 unsigned char abData[ 1 ];
129 } BYTE_BLOB;
130 typedef /* [unique] */ __RPC_unique_pointer BYTE_BLOB *UP_BYTE_BLOB;
131 typedef LONGLONG REFERENCE_TIME;
132 #define NONAMELESSUNION
133 #endif
134
135 #ifndef WAVE_FORMAT_IEEE_FLOAT
136 #define WAVE_FORMAT_IEEE_FLOAT 0x0003 // 32-bit floating-point
137 #endif
138
139 #ifndef __MINGW_EXTENSION
140 #if defined(__GNUC__) || defined(__GNUG__)
141 #define __MINGW_EXTENSION __extension__
142 #else
143 #define __MINGW_EXTENSION
144 #endif
145 #endif
146
147 #include <sdkddkver.h>
148 #include <propkeydef.h>
149 #define COBJMACROS
150 #define INITGUID // Avoid additional linkage of static libs, excessive code will be optimized out by the compiler
151 #include <audioclient.h>
152 #include <mmdeviceapi.h>
153 #include <endpointvolume.h>
154 #include <functiondiscoverykeys.h>
155 #include <devicetopology.h> // Used to get IKsJackDescription interface
156 #undef INITGUID
157
158 #endif // NTDDI_VERSION
159
160 #ifndef GUID_SECT
161 #define GUID_SECT
162 #endif
163
164 #define __DEFINE_GUID(n,l,w1,w2,b1,b2,b3,b4,b5,b6,b7,b8) static const GUID n GUID_SECT = {l,w1,w2,{b1,b2,b3,b4,b5,b6,b7,b8}}
165 #define __DEFINE_IID(n,l,w1,w2,b1,b2,b3,b4,b5,b6,b7,b8) static const IID n GUID_SECT = {l,w1,w2,{b1,b2,b3,b4,b5,b6,b7,b8}}
166 #define __DEFINE_CLSID(n,l,w1,w2,b1,b2,b3,b4,b5,b6,b7,b8) static const CLSID n GUID_SECT = {l,w1,w2,{b1,b2,b3,b4,b5,b6,b7,b8}}
167 #define PA_DEFINE_CLSID(className, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \
168 __DEFINE_CLSID(pa_CLSID_##className, 0x##l, 0x##w1, 0x##w2, 0x##b1, 0x##b2, 0x##b3, 0x##b4, 0x##b5, 0x##b6, 0x##b7, 0x##b8)
169 #define PA_DEFINE_IID(interfaceName, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \
170 __DEFINE_IID(pa_IID_##interfaceName, 0x##l, 0x##w1, 0x##w2, 0x##b1, 0x##b2, 0x##b3, 0x##b4, 0x##b5, 0x##b6, 0x##b7, 0x##b8)
171
172 // "1CB9AD4C-DBFA-4c32-B178-C2F568A703B2"
173 PA_DEFINE_IID(IAudioClient, 1cb9ad4c, dbfa, 4c32, b1, 78, c2, f5, 68, a7, 03, b2);
174 // "1BE09788-6894-4089-8586-9A2A6C265AC5"
175 PA_DEFINE_IID(IMMEndpoint, 1be09788, 6894, 4089, 85, 86, 9a, 2a, 6c, 26, 5a, c5);
176 // "A95664D2-9614-4F35-A746-DE8DB63617E6"
177 PA_DEFINE_IID(IMMDeviceEnumerator, a95664d2, 9614, 4f35, a7, 46, de, 8d, b6, 36, 17, e6);
178 // "BCDE0395-E52F-467C-8E3D-C4579291692E"
179 PA_DEFINE_CLSID(IMMDeviceEnumerator,bcde0395, e52f, 467c, 8e, 3d, c4, 57, 92, 91, 69, 2e);
180 // "F294ACFC-3146-4483-A7BF-ADDCA7C260E2"
181 PA_DEFINE_IID(IAudioRenderClient, f294acfc, 3146, 4483, a7, bf, ad, dc, a7, c2, 60, e2);
182 // "C8ADBD64-E71E-48a0-A4DE-185C395CD317"
183 PA_DEFINE_IID(IAudioCaptureClient, c8adbd64, e71e, 48a0, a4, de, 18, 5c, 39, 5c, d3, 17);
184 // *2A07407E-6497-4A18-9787-32F79BD0D98F* Or this??
185 PA_DEFINE_IID(IDeviceTopology, 2A07407E, 6497, 4A18, 97, 87, 32, f7, 9b, d0, d9, 8f);
186 // *AE2DE0E4-5BCA-4F2D-AA46-5D13F8FDB3A9*
187 PA_DEFINE_IID(IPart, AE2DE0E4, 5BCA, 4F2D, aa, 46, 5d, 13, f8, fd, b3, a9);
188 // *4509F757-2D46-4637-8E62-CE7DB944F57B*
189 PA_DEFINE_IID(IKsJackDescription, 4509F757, 2D46, 4637, 8e, 62, ce, 7d, b9, 44, f5, 7b);
190 // Media formats:
191 __DEFINE_GUID(pa_KSDATAFORMAT_SUBTYPE_PCM, 0x00000001, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 );
192 __DEFINE_GUID(pa_KSDATAFORMAT_SUBTYPE_ADPCM, 0x00000002, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 );
193 __DEFINE_GUID(pa_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, 0x00000003, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 );
194
195 /* use CreateThread for CYGWIN/Windows Mobile, _beginthreadex for all others */
196 #if !defined(__CYGWIN__) && !defined(_WIN32_WCE)
197 #define CREATE_THREAD(PROC) (HANDLE)_beginthreadex( NULL, 0, (PROC), stream, 0, &stream->dwThreadId )
198 #define PA_THREAD_FUNC static unsigned WINAPI
199 #define PA_THREAD_ID unsigned
200 #else
201 #define CREATE_THREAD(PROC) CreateThread( NULL, 0, (PROC), stream, 0, &stream->dwThreadId )
202 #define PA_THREAD_FUNC static DWORD WINAPI
203 #define PA_THREAD_ID DWORD
204 #endif
205
206 // Thread function forward decl.
207 PA_THREAD_FUNC ProcThreadEvent(void *param);
208 PA_THREAD_FUNC ProcThreadPoll(void *param);
209
210 // Availabe from Windows 7
211 #ifndef AUDCLNT_E_BUFFER_ERROR
212 #define AUDCLNT_E_BUFFER_ERROR AUDCLNT_ERR(0x018)
213 #endif
214 #ifndef AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED
215 #define AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED AUDCLNT_ERR(0x019)
216 #endif
217 #ifndef AUDCLNT_E_INVALID_DEVICE_PERIOD
218 #define AUDCLNT_E_INVALID_DEVICE_PERIOD AUDCLNT_ERR(0x020)
219 #endif
220
221 #define MAX_STR_LEN 512
222
223 enum { S_INPUT = 0, S_OUTPUT = 1, S_COUNT = 2, S_FULLDUPLEX = 0 };
224
225 #define STATIC_ARRAY_SIZE(array) (sizeof(array)/sizeof(array[0]))
226
227 #define PRINT(x) PA_DEBUG(x);
228
229 #define PA_SKELETON_SET_LAST_HOST_ERROR( errorCode, errorText ) \
230 PaUtil_SetLastHostErrorInfo( paWASAPI, errorCode, errorText )
231
232 #define PA_WASAPI__IS_FULLDUPLEX(STREAM) ((STREAM)->in.client && (STREAM)->out.client)
233
234 #ifndef IF_FAILED_JUMP
235 #define IF_FAILED_JUMP(hr, label) if(FAILED(hr)) goto label;
236 #endif
237
238 #define SAFE_CLOSE(h) if ((h) != NULL) { CloseHandle((h)); (h) = NULL; }
239 #define SAFE_RELEASE(punk) if ((punk) != NULL) { (punk)->lpVtbl->Release((punk)); (punk) = NULL; }
240
241 // Mixer function
242 typedef void (*MixMonoToStereoF) (void *__to, void *__from, UINT32 count);
243
244 // AVRT is the new "multimedia schedulling stuff"
245 typedef BOOL (WINAPI *FAvRtCreateThreadOrderingGroup) (PHANDLE,PLARGE_INTEGER,GUID*,PLARGE_INTEGER);
246 typedef BOOL (WINAPI *FAvRtDeleteThreadOrderingGroup) (HANDLE);
247 typedef BOOL (WINAPI *FAvRtWaitOnThreadOrderingGroup) (HANDLE);
248 typedef HANDLE (WINAPI *FAvSetMmThreadCharacteristics) (LPCTSTR,LPDWORD);
249 typedef BOOL (WINAPI *FAvRevertMmThreadCharacteristics)(HANDLE);
250 typedef BOOL (WINAPI *FAvSetMmThreadPriority) (HANDLE,AVRT_PRIORITY);
251
252 static HMODULE hDInputDLL = 0;
253 FAvRtCreateThreadOrderingGroup pAvRtCreateThreadOrderingGroup = NULL;
254 FAvRtDeleteThreadOrderingGroup pAvRtDeleteThreadOrderingGroup = NULL;
255 FAvRtWaitOnThreadOrderingGroup pAvRtWaitOnThreadOrderingGroup = NULL;
256 FAvSetMmThreadCharacteristics pAvSetMmThreadCharacteristics = NULL;
257 FAvRevertMmThreadCharacteristics pAvRevertMmThreadCharacteristics = NULL;
258 FAvSetMmThreadPriority pAvSetMmThreadPriority = NULL;
259
260 #define _GetProc(fun, type, name) { \
261 fun = (type) GetProcAddress(hDInputDLL,name); \
262 if (fun == NULL) { \
263 PRINT(("GetProcAddr failed for %s" ,name)); \
264 return FALSE; \
265 } \
266 } \
267
268 // ------------------------------------------------------------------------------------------
269 /* prototypes for functions declared in this file */
270 #ifdef __cplusplus
271 extern "C"
272 {
273 #endif /* __cplusplus */
274 PaError PaWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
275 #ifdef __cplusplus
276 }
277 #endif /* __cplusplus */
278 // dummy entry point for other compilers and sdks
279 // currently built using RC1 SDK (5600)
280 //#if _MSC_VER < 1400
281 //PaError PaWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex )
282 //{
283 //return paNoError;
284 //}
285 //#else
286
287 // ------------------------------------------------------------------------------------------
288 static void Terminate( struct PaUtilHostApiRepresentation *hostApi );
289 static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
290 const PaStreamParameters *inputParameters,
291 const PaStreamParameters *outputParameters,
292 double sampleRate );
293 static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
294 PaStream** s,
295 const PaStreamParameters *inputParameters,
296 const PaStreamParameters *outputParameters,
297 double sampleRate,
298 unsigned long framesPerBuffer,
299 PaStreamFlags streamFlags,
300 PaStreamCallback *streamCallback,
301 void *userData );
302 static PaError CloseStream( PaStream* stream );
303 static PaError StartStream( PaStream *stream );
304 static PaError StopStream( PaStream *stream );
305 static PaError AbortStream( PaStream *stream );
306 static PaError IsStreamStopped( PaStream *s );
307 static PaError IsStreamActive( PaStream *stream );
308 static PaTime GetStreamTime( PaStream *stream );
309 static double GetStreamCpuLoad( PaStream* stream );
310 static PaError ReadStream( PaStream* stream, void *buffer, unsigned long frames );
311 static PaError WriteStream( PaStream* stream, const void *buffer, unsigned long frames );
312 static signed long GetStreamReadAvailable( PaStream* stream );
313 static signed long GetStreamWriteAvailable( PaStream* stream );
314
315 // ------------------------------------------------------------------------------------------
316 /*
317 These are fields that can be gathered from IDevice and IAudioDevice PRIOR to Initialize, and
318 done in first pass i assume that neither of these will cause the Driver to "load", but again,
319 who knows how they implement their stuff
320 */
321 typedef struct PaWasapiDeviceInfo
322 {
323 // Device
324 IMMDevice *device;
325
326 // from GetId
327 WCHAR szDeviceID[MAX_STR_LEN];
328
329 // from GetState
330 DWORD state;
331
332 // Fields filled from IMMEndpoint'sGetDataFlow
333 EDataFlow flow;
334
335 // Fields filled from IAudioDevice (_prior_ to Initialize)
336 // from GetDevicePeriod(
337 REFERENCE_TIME DefaultDevicePeriod;
338 REFERENCE_TIME MinimumDevicePeriod;
339
340 // from GetMixFormat
341 // WAVEFORMATEX *MixFormat;//needs to be CoTaskMemFree'd after use!
342
343 // Default format (setup through Control Panel by user)
344 WAVEFORMATEXTENSIBLE DefaultFormat;
345
346 // Formfactor
347 EndpointFormFactor formFactor;
348 }
349 PaWasapiDeviceInfo;
350
351 // ------------------------------------------------------------------------------------------
352 /* PaWasapiHostApiRepresentation - host api datastructure specific to this implementation */
353 typedef struct
354 {
355 PaUtilHostApiRepresentation inheritedHostApiRep;
356 PaUtilStreamInterface callbackStreamInterface;
357 PaUtilStreamInterface blockingStreamInterface;
358
359 PaUtilAllocationGroup *allocations;
360
361 /* implementation specific data goes here */
362
363 //in case we later need the synch
364 IMMDeviceEnumerator *enumerator;
365
366 //this is the REAL number of devices, whether they are usefull to PA or not!
367 UINT32 deviceCount;
368
369 WCHAR defaultRenderer [MAX_STR_LEN];
370 WCHAR defaultCapturer [MAX_STR_LEN];
371
372 PaWasapiDeviceInfo *devInfo;
373
374 // Is true when WOW64 Vista/7 Workaround is needed
375 BOOL useWOW64Workaround;
376 }
377 PaWasapiHostApiRepresentation;
378
379 // ------------------------------------------------------------------------------------------
380 /* PaWasapiAudioClientParams - audio client parameters */
381 typedef struct PaWasapiAudioClientParams
382 {
383 PaWasapiDeviceInfo *device_info;
384 PaStreamParameters stream_params;
385 PaWasapiStreamInfo wasapi_params;
386 UINT32 frames_per_buffer;
387 double sample_rate;
388 BOOL blocking;
389 BOOL full_duplex;
390 BOOL wow64_workaround;
391 }
392 PaWasapiAudioClientParams;
393
394 // ------------------------------------------------------------------------------------------
395 /* PaWasapiStream - a stream data structure specifically for this implementation */
396 typedef struct PaWasapiSubStream
397 {
398 IAudioClient *client;
399 WAVEFORMATEXTENSIBLE wavex;
400 UINT32 bufferSize;
401 REFERENCE_TIME device_latency;
402 REFERENCE_TIME period;
403 double latency_seconds;
404 UINT32 framesPerHostCallback;
405 AUDCLNT_SHAREMODE shareMode;
406 UINT32 streamFlags; // AUDCLNT_STREAMFLAGS_EVENTCALLBACK, ...
407 UINT32 flags;
408 PaWasapiAudioClientParams params; //!< parameters
409
410 // Buffers
411 UINT32 buffers; //!< number of buffers used (from host side)
412 UINT32 framesPerBuffer; //!< number of frames per 1 buffer
413 BOOL userBufferAndHostMatch;
414
415 // Used by blocking interface:
416 UINT32 prevTime; // time ms between calls of WriteStream
417 UINT32 prevSleep; // time ms to sleep from frames written in previous call
418
419 // Used for Mono >> Stereo workaround, if driver does not support it
420 // (in Exclusive mode WASAPI usually refuses to operate with Mono (1-ch)
421 void *monoBuffer; //!< pointer to buffer
422 UINT32 monoBufferSize; //!< buffer size in bytes
423 MixMonoToStereoF monoMixer; //!< pointer to mixer function
424 }
425 PaWasapiSubStream;
426
427 // ------------------------------------------------------------------------------------------
428 /* PaWasapiHostProcessor - redirects processing data */
429 typedef struct PaWasapiHostProcessor
430 {
431 PaWasapiHostProcessorCallback processor;
432 void *userData;
433 }
434 PaWasapiHostProcessor;
435
436 // ------------------------------------------------------------------------------------------
437 typedef struct PaWasapiStream
438 {
439 /* IMPLEMENT ME: rename this */
440 PaUtilStreamRepresentation streamRepresentation;
441 PaUtilCpuLoadMeasurer cpuLoadMeasurer;
442 PaUtilBufferProcessor bufferProcessor;
443
444 // input
445 PaWasapiSubStream in;
446 IAudioCaptureClient *cclient;
447 IAudioEndpointVolume *inVol;
448
449 // output
450 PaWasapiSubStream out;
451 IAudioRenderClient *rclient;
452 IAudioEndpointVolume *outVol;
453
454 // event handles for event-driven processing mode
455 HANDLE event[S_COUNT];
456
457 // buffer mode
458 PaUtilHostBufferSizeMode bufferMode;
459
460 // must be volatile to avoid race condition on user query while
461 // thread is being started
462 volatile BOOL running;
463
464 PA_THREAD_ID dwThreadId;
465 HANDLE hThread;
466 HANDLE hCloseRequest;
467 HANDLE hThreadStart; //!< signalled by thread on start
468 HANDLE hThreadExit; //!< signalled by thread on exit
469 HANDLE hBlockingOpStreamRD;
470 HANDLE hBlockingOpStreamWR;
471
472 // Host callback Output overrider
473 PaWasapiHostProcessor hostProcessOverrideOutput;
474
475 // Host callback Input overrider
476 PaWasapiHostProcessor hostProcessOverrideInput;
477
478 // Defines blocking/callback interface used
479 BOOL bBlocking;
480
481 // Av Task (MM thread management)
482 HANDLE hAvTask;
483
484 // Thread priority level
485 PaWasapiThreadPriority nThreadPriority;
486 }
487 PaWasapiStream;
488
489 // Local stream methods
490 static void _OnStreamStop(PaWasapiStream *stream);
491 static void _FinishStream(PaWasapiStream *stream);
492 static void _CleanupStream(PaWasapiStream *stream);
493
494 // Local statics
495 static volatile BOOL g_WasapiCOMInit = FALSE;
496 static volatile DWORD g_WasapiInitThread = 0;
497
498 // ------------------------------------------------------------------------------------------
499 #define LogHostError(HRES) __LogHostError(HRES, __FUNCTION__, __FILE__, __LINE__)
500 static HRESULT __LogHostError(HRESULT res, const char *func, const char *file, int line)
501 {
502 const char *text = NULL;
503 switch (res)
504 {
505 case S_OK: return res;
506 case E_POINTER :text ="E_POINTER"; break;
507 case E_INVALIDARG :text ="E_INVALIDARG"; break;
508
509 case AUDCLNT_E_NOT_INITIALIZED :text ="AUDCLNT_E_NOT_INITIALIZED"; break;
510 case AUDCLNT_E_ALREADY_INITIALIZED :text ="AUDCLNT_E_ALREADY_INITIALIZED"; break;
511 case AUDCLNT_E_WRONG_ENDPOINT_TYPE :text ="AUDCLNT_E_WRONG_ENDPOINT_TYPE"; break;
512 case AUDCLNT_E_DEVICE_INVALIDATED :text ="AUDCLNT_E_DEVICE_INVALIDATED"; break;
513 case AUDCLNT_E_NOT_STOPPED :text ="AUDCLNT_E_NOT_STOPPED"; break;
514 case AUDCLNT_E_BUFFER_TOO_LARGE :text ="AUDCLNT_E_BUFFER_TOO_LARGE"; break;
515 case AUDCLNT_E_OUT_OF_ORDER :text ="AUDCLNT_E_OUT_OF_ORDER"; break;
516 case AUDCLNT_E_UNSUPPORTED_FORMAT :text ="AUDCLNT_E_UNSUPPORTED_FORMAT"; break;
517 case AUDCLNT_E_INVALID_SIZE :text ="AUDCLNT_E_INVALID_SIZE"; break;
518 case AUDCLNT_E_DEVICE_IN_USE :text ="AUDCLNT_E_DEVICE_IN_USE"; break;
519 case AUDCLNT_E_BUFFER_OPERATION_PENDING :text ="AUDCLNT_E_BUFFER_OPERATION_PENDING"; break;
520 case AUDCLNT_E_THREAD_NOT_REGISTERED :text ="AUDCLNT_E_THREAD_NOT_REGISTERED"; break;
521 case AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED :text ="AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED"; break;
522 case AUDCLNT_E_ENDPOINT_CREATE_FAILED :text ="AUDCLNT_E_ENDPOINT_CREATE_FAILED"; break;
523 case AUDCLNT_E_SERVICE_NOT_RUNNING :text ="AUDCLNT_E_SERVICE_NOT_RUNNING"; break;
524 case AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED :text ="AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED"; break;
525 case AUDCLNT_E_EXCLUSIVE_MODE_ONLY :text ="AUDCLNT_E_EXCLUSIVE_MODE_ONLY"; break;
526 case AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL :text ="AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL"; break;
527 case AUDCLNT_E_EVENTHANDLE_NOT_SET :text ="AUDCLNT_E_EVENTHANDLE_NOT_SET"; break;
528 case AUDCLNT_E_INCORRECT_BUFFER_SIZE :text ="AUDCLNT_E_INCORRECT_BUFFER_SIZE"; break;
529 case AUDCLNT_E_BUFFER_SIZE_ERROR :text ="AUDCLNT_E_BUFFER_SIZE_ERROR"; break;
530 case AUDCLNT_E_CPUUSAGE_EXCEEDED :text ="AUDCLNT_E_CPUUSAGE_EXCEEDED"; break;
531 case AUDCLNT_E_BUFFER_ERROR :text ="AUDCLNT_E_BUFFER_ERROR"; break;
532 case AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED :text ="AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED"; break;
533 case AUDCLNT_E_INVALID_DEVICE_PERIOD :text ="AUDCLNT_E_INVALID_DEVICE_PERIOD"; break;
534
535 case AUDCLNT_S_BUFFER_EMPTY :text ="AUDCLNT_S_BUFFER_EMPTY"; break;
536 case AUDCLNT_S_THREAD_ALREADY_REGISTERED :text ="AUDCLNT_S_THREAD_ALREADY_REGISTERED"; break;
537 case AUDCLNT_S_POSITION_STALLED :text ="AUDCLNT_S_POSITION_STALLED"; break;
538
539 // other windows common errors:
540 case CO_E_NOTINITIALIZED :text ="CO_E_NOTINITIALIZED: you must call CoInitialize() before Pa_OpenStream()"; break;
541
542 default:
543 text = "UNKNOWN ERROR";
544 }
545 PRINT(("WASAPI ERROR HRESULT: 0x%X : %s\n [FUNCTION: %s FILE: %s {LINE: %d}]\n", res, text, func, file, line));
546 PA_SKELETON_SET_LAST_HOST_ERROR(res, text);
547 return res;
548 }
549
550 // ------------------------------------------------------------------------------------------
551 #define LogPaError(PAERR) __LogPaError(PAERR, __FUNCTION__, __FILE__, __LINE__)
552 static PaError __LogPaError(PaError err, const char *func, const char *file, int line)
553 {
554 if (err == paNoError)
555 return err;
556 PRINT(("WASAPI ERROR PAERROR: %i : %s\n [FUNCTION: %s FILE: %s {LINE: %d}]\n", err, Pa_GetErrorText(err), func, file, line));
557 return err;
558 }
559
560 // ------------------------------------------------------------------------------------------
561 /*static double nano100ToMillis(REFERENCE_TIME ref)
562 {
563 // 1 nano = 0.000000001 seconds
564 //100 nano = 0.0000001 seconds
565 //100 nano = 0.0001 milliseconds
566 return ((double)ref)*0.0001;
567 }*/
568
569 // ------------------------------------------------------------------------------------------
570 static double nano100ToSeconds(REFERENCE_TIME ref)
571 {
572 // 1 nano = 0.000000001 seconds
573 //100 nano = 0.0000001 seconds
574 //100 nano = 0.0001 milliseconds
575 return ((double)ref)*0.0000001;
576 }
577
578 // ------------------------------------------------------------------------------------------
579 /*static REFERENCE_TIME MillisTonano100(double ref)
580 {
581 // 1 nano = 0.000000001 seconds
582 //100 nano = 0.0000001 seconds
583 //100 nano = 0.0001 milliseconds
584 return (REFERENCE_TIME)(ref/0.0001);
585 }*/
586
587 // ------------------------------------------------------------------------------------------
588 static REFERENCE_TIME SecondsTonano100(double ref)
589 {
590 // 1 nano = 0.000000001 seconds
591 //100 nano = 0.0000001 seconds
592 //100 nano = 0.0001 milliseconds
593 return (REFERENCE_TIME)(ref/0.0000001);
594 }
595
596 // ------------------------------------------------------------------------------------------
597 // Makes Hns period from frames and sample rate
598 static REFERENCE_TIME MakeHnsPeriod(UINT32 nFrames, DWORD nSamplesPerSec)
599 {
600 return (REFERENCE_TIME)((10000.0 * 1000 / nSamplesPerSec * nFrames) + 0.5);
601 }
602
603 // ------------------------------------------------------------------------------------------
604 // Converts PaSampleFormat to bits per sample value
605 static WORD PaSampleFormatToBitsPerSample(PaSampleFormat format_id)
606 {
607 switch (format_id & ~paNonInterleaved)
608 {
609 case paFloat32:
610 case paInt32: return 32;
611 case paInt24: return 24;
612 case paInt16: return 16;
613 case paInt8:
614 case paUInt8: return 8;
615 }
616 return 0;
617 }
618
619 // ------------------------------------------------------------------------------------------
620 // Converts PaSampleFormat to bits per sample value
621 /*static WORD PaSampleFormatToBytesPerSample(PaSampleFormat format_id)
622 {
623 return PaSampleFormatToBitsPerSample(format_id) >> 3; // 'bits/8'
624 }*/
625
626 // ------------------------------------------------------------------------------------------
627 // Converts Hns period into number of frames
628 static UINT32 MakeFramesFromHns(REFERENCE_TIME hnsPeriod, UINT32 nSamplesPerSec)
629 {
630 UINT32 nFrames = (UINT32)( // frames =
631 1.0 * hnsPeriod * // hns *
632 nSamplesPerSec / // (frames / s) /
633 1000 / // (ms / s) /
634 10000 // (hns / s) /
635 + 0.5 // rounding
636 );
637 return nFrames;
638 }
639
640 // Aligning function type
641 typedef UINT32 (*ALIGN_FUNC) (UINT32 v, UINT32 align);
642
643 // ------------------------------------------------------------------------------------------
644 // Aligns 'v' backwards
645 static UINT32 ALIGN_BWD(UINT32 v, UINT32 align)
646 {
647 return ((v - (align ? v % align : 0)));
648 }
649
650 // ------------------------------------------------------------------------------------------
651 // Aligns 'v' forward
652 static UINT32 ALIGN_FWD(UINT32 v, UINT32 align)
653 {
654 UINT32 remainder = (align ? (v % align) : 0);
655 if (remainder == 0)
656 return v;
657 return v + (align - remainder);
658 }
659
660 // ------------------------------------------------------------------------------------------
661 // Aligns WASAPI buffer to 128 byte packet boundary. HD Audio will fail to play if buffer
662 // is misaligned. This problem was solved in Windows 7 were AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED
663 // is thrown although we must align for Vista anyway.
664 static UINT32 AlignFramesPerBuffer(UINT32 nFrames, UINT32 nSamplesPerSec, UINT32 nBlockAlign,
665 ALIGN_FUNC pAlignFunc)
666 {
667 #define HDA_PACKET_SIZE (128)
668
669 long frame_bytes = nFrames * nBlockAlign;
670 long packets;
671
672 // align to packet size
673 frame_bytes = pAlignFunc(frame_bytes, HDA_PACKET_SIZE); // use ALIGN_FWD if bigger but safer period is more desired
674
675 // atlest 1 frame must be available
676 if (frame_bytes < HDA_PACKET_SIZE)
677 frame_bytes = HDA_PACKET_SIZE;
678
679 nFrames = frame_bytes / nBlockAlign;
680 packets = frame_bytes / HDA_PACKET_SIZE;
681
682 frame_bytes = packets * HDA_PACKET_SIZE;
683 nFrames = frame_bytes / nBlockAlign;
684
685 return nFrames;
686
687 #undef HDA_PACKET_SIZE
688 }
689
690 // ------------------------------------------------------------------------------------------
691 static UINT32 GetFramesSleepTime(UINT32 nFrames, UINT32 nSamplesPerSec)
692 {
693 REFERENCE_TIME nDuration;
694 if (nSamplesPerSec == 0)
695 return 0;
696 #define REFTIMES_PER_SEC 10000000
697 #define REFTIMES_PER_MILLISEC 10000
698 // Calculate the actual duration of the allocated buffer.
699 nDuration = (REFERENCE_TIME)((double)REFTIMES_PER_SEC * nFrames / nSamplesPerSec);
700 return (UINT32)(nDuration/REFTIMES_PER_MILLISEC/2);
701 }
702
703 // ------------------------------------------------------------------------------------------
704 static UINT32 GetFramesSleepTimeMicroseconds(UINT32 nFrames, UINT32 nSamplesPerSec)
705 {
706 REFERENCE_TIME nDuration;
707 if (nSamplesPerSec == 0)
708 return 0;
709 #define REFTIMES_PER_SEC 10000000
710 #define REFTIMES_PER_MILLISEC 10000
711 // Calculate the actual duration of the allocated buffer.
712 nDuration = (REFERENCE_TIME)((double)REFTIMES_PER_SEC * nFrames / nSamplesPerSec);
713 return (UINT32)(nDuration/10/2);
714 }
715
716 // ------------------------------------------------------------------------------------------
717 static BOOL SetupAVRT()
718 {
719 hDInputDLL = LoadLibraryA("avrt.dll");
720 if (hDInputDLL == NULL)
721 return FALSE;
722
723 _GetProc(pAvRtCreateThreadOrderingGroup, FAvRtCreateThreadOrderingGroup, "AvRtCreateThreadOrderingGroup");
724 _GetProc(pAvRtDeleteThreadOrderingGroup, FAvRtDeleteThreadOrderingGroup, "AvRtDeleteThreadOrderingGroup");
725 _GetProc(pAvRtWaitOnThreadOrderingGroup, FAvRtWaitOnThreadOrderingGroup, "AvRtWaitOnThreadOrderingGroup");
726 _GetProc(pAvSetMmThreadCharacteristics, FAvSetMmThreadCharacteristics, "AvSetMmThreadCharacteristicsA");
727 _GetProc(pAvRevertMmThreadCharacteristics,FAvRevertMmThreadCharacteristics,"AvRevertMmThreadCharacteristics");
728 _GetProc(pAvSetMmThreadPriority, FAvSetMmThreadPriority, "AvSetMmThreadPriority");
729
730 return pAvRtCreateThreadOrderingGroup &&
731 pAvRtDeleteThreadOrderingGroup &&
732 pAvRtWaitOnThreadOrderingGroup &&
733 pAvSetMmThreadCharacteristics &&
734 pAvRevertMmThreadCharacteristics &&
735 pAvSetMmThreadPriority;
736 }
737
738 // ------------------------------------------------------------------------------------------
739 static void CloseAVRT()
740 {
741 if (hDInputDLL != NULL)
742 FreeLibrary(hDInputDLL);
743 hDInputDLL = NULL;
744 }
745
746 // ------------------------------------------------------------------------------------------
747 static BOOL IsWow64()
748 {
749 // http://msdn.microsoft.com/en-us/library/ms684139(VS.85).aspx
750
751 typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS) (HANDLE, PBOOL);
752 LPFN_ISWOW64PROCESS fnIsWow64Process;
753
754 BOOL bIsWow64 = FALSE;
755
756 // IsWow64Process is not available on all supported versions of Windows.
757 // Use GetModuleHandle to get a handle to the DLL that contains the function
758 // and GetProcAddress to get a pointer to the function if available.
759
760 fnIsWow64Process = (LPFN_ISWOW64PROCESS) GetProcAddress(
761 GetModuleHandle(TEXT("kernel32")), TEXT("IsWow64Process"));
762
763 if (fnIsWow64Process == NULL)
764 return FALSE;
765
766 if (!fnIsWow64Process(GetCurrentProcess(), &bIsWow64))
767 return FALSE;
768
769 return bIsWow64;
770 }
771
772 // ------------------------------------------------------------------------------------------
773 typedef enum EWindowsVersion
774 {
775 WINDOWS_UNKNOWN = 0,
776 WINDOWS_VISTA_SERVER2008 = (1 << 0),
777 WINDOWS_7_SERVER2008R2 = (1 << 1),
778 WINDOWS_FUTURE = (1 << 2)
779 }
780 EWindowsVersion;
781 // Defines Windows 7/Windows Server 2008 R2 and up (future versions)
782 #define WINDOWS_7_SERVER2008R2_AND_UP (WINDOWS_7_SERVER2008R2|WINDOWS_FUTURE)
783 // The function is limited to Vista/7 mostly as we need just to find out Vista/WOW64 combination
784 // in order to use WASAPI WOW64 workarounds.
785 static UINT32 GetWindowsVersion()
786 {
787 static UINT32 version = WINDOWS_UNKNOWN;
788
789 if (version == WINDOWS_UNKNOWN)
790 {
791 DWORD dwVersion = 0;
792 DWORD dwMajorVersion = 0;
793 DWORD dwMinorVersion = 0;
794 DWORD dwBuild = 0;
795
796 typedef DWORD (WINAPI *LPFN_GETVERSION)(VOID);
797 LPFN_GETVERSION fnGetVersion;
798
799 fnGetVersion = (LPFN_GETVERSION) GetProcAddress(GetModuleHandle(TEXT("kernel32")), TEXT("GetVersion"));
800 if (fnGetVersion == NULL)
801 return WINDOWS_UNKNOWN;
802
803 dwVersion = fnGetVersion();
804
805 // Get the Windows version
806 dwMajorVersion = (DWORD)(LOBYTE(LOWORD(dwVersion)));
807 dwMinorVersion = (DWORD)(HIBYTE(LOWORD(dwVersion)));
808
809 // Get the build number
810 if (dwVersion < 0x80000000)
811 dwBuild = (DWORD)(HIWORD(dwVersion));
812
813 switch (dwMajorVersion)
814 {
815 case 0:
816 case 1:
817 case 2:
818 case 3:
819 case 4:
820 case 5:
821 break; // skip lower
822 case 6:
823 switch (dwMinorVersion)
824 {
825 case 0:
826 version |= WINDOWS_VISTA_SERVER2008;
827 break;
828 case 1:
829 version |= WINDOWS_7_SERVER2008R2;
830 break;
831 default:
832 version |= WINDOWS_FUTURE;
833 }
834 break;
835 default:
836 version |= WINDOWS_FUTURE;
837 }
838 }
839
840 return version;
841 }
842
843 // ------------------------------------------------------------------------------------------
844 static BOOL UseWOW64Workaround()
845 {
846 // note: WOW64 bug is common to Windows Vista x64, thus we fall back to safe Poll-driven
847 // method. Windows 7 x64 seems has WOW64 bug fixed.
848
849 return (IsWow64() && (GetWindowsVersion() & WINDOWS_VISTA_SERVER2008));
850 }
851
852 // ------------------------------------------------------------------------------------------
853 typedef enum EMixerDir { MIX_DIR__1TO2, MIX_DIR__2TO1, MIX_DIR__2TO1_L } EMixerDir;
854
855 // ------------------------------------------------------------------------------------------
856 #define _WASAPI_MONO_TO_STEREO_MIXER_1_TO_2(TYPE)\
857 TYPE * __restrict to = __to;\
858 TYPE * __restrict from = __from;\
859 TYPE * __restrict end = from + count;\
860 while (from != end)\
861 {\
862 *to ++ = *from;\
863 *to ++ = *from;\
864 ++ from;\
865 }
866
867 // ------------------------------------------------------------------------------------------
868 #define _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1_FLT32(TYPE)\
869 TYPE * __restrict to = (TYPE *)__to;\
870 TYPE * __restrict from = (TYPE *)__from;\
871 TYPE * __restrict end = to + count;\
872 while (to != end)\
873 {\
874 *to ++ = (TYPE)((float)(from[0] + from[1]) * 0.5f);\
875 from += 2;\
876 }
877
878 // ------------------------------------------------------------------------------------------
879 #define _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1_INT32(TYPE)\
880 TYPE * __restrict to = (TYPE *)__to;\
881 TYPE * __restrict from = (TYPE *)__from;\
882 TYPE * __restrict end = to + count;\
883 while (to != end)\
884 {\
885 *to ++ = (TYPE)(((INT32)from[0] + (INT32)from[1]) >> 1);\
886 from += 2;\
887 }
888
889 // ------------------------------------------------------------------------------------------
890 #define _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1_INT64(TYPE)\
891 TYPE * __restrict to = (TYPE *)__to;\
892 TYPE * __restrict from = (TYPE *)__from;\
893 TYPE * __restrict end = to + count;\
894 while (to != end)\
895 {\
896 *to ++ = (TYPE)(((INT64)from[0] + (INT64)from[1]) >> 1);\
897 from += 2;\
898 }
899
900 // ------------------------------------------------------------------------------------------
901 #define _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1_L(TYPE)\
902 TYPE * __restrict to = (TYPE *)__to;\
903 TYPE * __restrict from = (TYPE *)__from;\
904 TYPE * __restrict end = to + count;\
905 while (to != end)\
906 {\
907 *to ++ = from[0];\
908 from += 2;\
909 }
910
911 // ------------------------------------------------------------------------------------------
912 static void _MixMonoToStereo_1TO2_8(void *__to, void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_1_TO_2(BYTE); }
913 static void _MixMonoToStereo_1TO2_16(void *__to, void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_1_TO_2(short); }
914 static void _MixMonoToStereo_1TO2_24(void *__to, void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_1_TO_2(int); /* !!! int24 data is contained in 32-bit containers*/ }
915 static void _MixMonoToStereo_1TO2_32(void *__to, void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_1_TO_2(int); }
916 static void _MixMonoToStereo_1TO2_32f(void *__to, void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_1_TO_2(float); }
917
918 // ------------------------------------------------------------------------------------------
919 static void _MixMonoToStereo_2TO1_8(void *__to, void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1_INT32(BYTE); }
920 static void _MixMonoToStereo_2TO1_16(void *__to, void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1_INT32(short); }
921 static void _MixMonoToStereo_2TO1_24(void *__to, void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1_INT32(int); /* !!! int24 data is contained in 32-bit containers*/ }
922 static void _MixMonoToStereo_2TO1_32(void *__to, void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1_INT64(int); }
923 static void _MixMonoToStereo_2TO1_32f(void *__to, void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1_FLT32(float); }
924
925 // ------------------------------------------------------------------------------------------
926 static void _MixMonoToStereo_2TO1_8_L(void *__to, void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1_L(BYTE); }
927 static void _MixMonoToStereo_2TO1_16_L(void *__to, void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1_L(short); }
928 static void _MixMonoToStereo_2TO1_24_L(void *__to, void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1_L(int); /* !!! int24 data is contained in 32-bit containers*/ }
929 static void _MixMonoToStereo_2TO1_32_L(void *__to, void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1_L(int); }
930 static void _MixMonoToStereo_2TO1_32f_L(void *__to, void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1_L(float); }
931
932 // ------------------------------------------------------------------------------------------
933 static MixMonoToStereoF _GetMonoToStereoMixer(PaSampleFormat format, EMixerDir dir)
934 {
935 switch (dir)
936 {
937 case MIX_DIR__1TO2:
938 switch (format & ~paNonInterleaved)
939 {
940 case paUInt8: return _MixMonoToStereo_1TO2_8;
941 case paInt16: return _MixMonoToStereo_1TO2_16;
942 case paInt24: return _MixMonoToStereo_1TO2_24;
943 case paInt32: return _MixMonoToStereo_1TO2_32;
944 case paFloat32: return _MixMonoToStereo_1TO2_32f;
945 }
946 break;
947
948 case MIX_DIR__2TO1:
949 switch (format & ~paNonInterleaved)
950 {
951 case paUInt8: return _MixMonoToStereo_2TO1_8;
952 case paInt16: return _MixMonoToStereo_2TO1_16;
953 case paInt24: return _MixMonoToStereo_2TO1_24;
954 case paInt32: return _MixMonoToStereo_2TO1_32;
955 case paFloat32: return _MixMonoToStereo_2TO1_32f;
956 }
957 break;
958
959 case MIX_DIR__2TO1_L:
960 switch (format & ~paNonInterleaved)
961 {
962 case paUInt8: return _MixMonoToStereo_2TO1_8_L;
963 case paInt16: return _MixMonoToStereo_2TO1_16_L;
964 case paInt24: return _MixMonoToStereo_2TO1_24_L;
965 case paInt32: return _MixMonoToStereo_2TO1_32_L;
966 case paFloat32: return _MixMonoToStereo_2TO1_32f_L;
967 }
968 break;
969 }
970
971 return NULL;
972 }
973
974 // ------------------------------------------------------------------------------------------
975 PaError PaWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex )
976 {
977 PaError result = paNoError;
978 PaWasapiHostApiRepresentation *paWasapi;
979 PaDeviceInfo *deviceInfoArray;
980 HRESULT hr = S_OK;
981 IMMDeviceCollection* pEndPoints = NULL;
982 UINT i;
983
984 if (!SetupAVRT())
985 {
986 PRINT(("WASAPI: No AVRT! (not VISTA?)"));
987 return paNoError;
988 }
989
990 /*
991 If COM is already initialized CoInitialize will either return
992 FALSE, or RPC_E_CHANGED_MODE if it was initialised in a different
993 threading mode. In either case we shouldn't consider it an error
994 but we need to be careful to not call CoUninitialize() if
995 RPC_E_CHANGED_MODE was returned.
996 */
997 hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
998 if (FAILED(hr) && (hr != RPC_E_CHANGED_MODE))
999 {
1000 PRINT(("WASAPI: failed CoInitialize"));
1001 return paUnanticipatedHostError;
1002 }
1003 if (hr != RPC_E_CHANGED_MODE)
1004 g_WasapiCOMInit = TRUE;
1005
1006 // Memorize calling thread id and report warning on Uninitialize if calling thread is different
1007 // as CoInitialize must match CoUninitialize in the same thread.
1008 g_WasapiInitThread = GetCurrentThreadId();
1009
1010 paWasapi = (PaWasapiHostApiRepresentation *)PaUtil_AllocateMemory( sizeof(PaWasapiHostApiRepresentation) );
1011 if (paWasapi == NULL)
1012 {
1013 result = paInsufficientMemory;
1014 goto error;
1015 }
1016
1017 paWasapi->allocations = PaUtil_CreateAllocationGroup();
1018 if (paWasapi->allocations == NULL)
1019 {
1020 result = paInsufficientMemory;
1021 goto error;
1022 }
1023
1024 *hostApi = &paWasapi->inheritedHostApiRep;
1025 (*hostApi)->info.structVersion = 1;
1026 (*hostApi)->info.type = paWASAPI;
1027 (*hostApi)->info.name = "Windows WASAPI";
1028 (*hostApi)->info.deviceCount = 0;
1029 (*hostApi)->info.defaultInputDevice = paNoDevice;
1030 (*hostApi)->info.defaultOutputDevice = paNoDevice;
1031
1032 paWasapi->enumerator = NULL;
1033 hr = CoCreateInstance(&pa_CLSID_IMMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER,
1034 &pa_IID_IMMDeviceEnumerator, (void **)&paWasapi->enumerator);
1035 IF_FAILED_JUMP(hr, error);
1036
1037 // getting default device ids in the eMultimedia "role"
1038 {
1039 {
1040 IMMDevice *defaultRenderer = NULL;
1041 hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(paWasapi->enumerator, eRender, eMultimedia, &defaultRenderer);
1042 if (hr != S_OK)
1043 {
1044 if (hr != E_NOTFOUND)
1045 IF_FAILED_JUMP(hr, error);
1046 }
1047 else
1048 {
1049 WCHAR *pszDeviceId = NULL;
1050 hr = IMMDevice_GetId(defaultRenderer, &pszDeviceId);
1051 IF_FAILED_JUMP(hr, error);
1052 wcsncpy(paWasapi->defaultRenderer, pszDeviceId, MAX_STR_LEN-1);
1053 CoTaskMemFree(pszDeviceId);
1054 IMMDevice_Release(defaultRenderer);
1055 }
1056 }
1057
1058 {
1059 IMMDevice *defaultCapturer = NULL;
1060 hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(paWasapi->enumerator, eCapture, eMultimedia, &defaultCapturer);
1061 if (hr != S_OK)
1062 {
1063 if (hr != E_NOTFOUND)
1064 IF_FAILED_JUMP(hr, error);
1065 }
1066 else
1067 {
1068 WCHAR *pszDeviceId = NULL;
1069 hr = IMMDevice_GetId(defaultCapturer, &pszDeviceId);
1070 IF_FAILED_JUMP(hr, error);
1071 wcsncpy(paWasapi->defaultCapturer, pszDeviceId, MAX_STR_LEN-1);
1072 CoTaskMemFree(pszDeviceId);
1073 IMMDevice_Release(defaultCapturer);
1074 }
1075 }
1076 }
1077
1078 hr = IMMDeviceEnumerator_EnumAudioEndpoints(paWasapi->enumerator, eAll, DEVICE_STATE_ACTIVE, &pEndPoints);
1079 IF_FAILED_JUMP(hr, error);
1080
1081 hr = IMMDeviceCollection_GetCount(pEndPoints, &paWasapi->deviceCount);
1082 IF_FAILED_JUMP(hr, error);
1083
1084 paWasapi->devInfo = (PaWasapiDeviceInfo *)malloc(sizeof(PaWasapiDeviceInfo) * paWasapi->deviceCount);
1085 for (i = 0; i < paWasapi->deviceCount; ++i)
1086 memset(&paWasapi->devInfo[i], 0, sizeof(PaWasapiDeviceInfo));
1087
1088 if (paWasapi->deviceCount > 0)
1089 {
1090 (*hostApi)->deviceInfos = (PaDeviceInfo **)PaUtil_GroupAllocateMemory(
1091 paWasapi->allocations, sizeof(PaDeviceInfo *) * paWasapi->deviceCount);
1092 if ((*hostApi)->deviceInfos == NULL)
1093 {
1094 result = paInsufficientMemory;
1095 goto error;
1096 }
1097
1098 /* allocate all device info structs in a contiguous block */
1099 deviceInfoArray = (PaDeviceInfo *)PaUtil_GroupAllocateMemory(
1100 paWasapi->allocations, sizeof(PaDeviceInfo) * paWasapi->deviceCount);
1101 if (deviceInfoArray == NULL)
1102 {
1103 result = paInsufficientMemory;
1104 goto error;
1105 }
1106
1107 for (i = 0; i < paWasapi->deviceCount; ++i)
1108 {
1109 DWORD state = 0;
1110 PaDeviceInfo *deviceInfo = &deviceInfoArray[i];
1111 deviceInfo->structVersion = 2;
1112 deviceInfo->hostApi = hostApiIndex;
1113
1114 PA_DEBUG(("WASAPI: device idx: %02d\n", i));
1115 PA_DEBUG(("WASAPI: ---------------\n"));
1116
1117 hr = IMMDeviceCollection_Item(pEndPoints, i, &paWasapi->devInfo[i].device);
1118 IF_FAILED_JUMP(hr, error);
1119
1120 // getting ID
1121 {
1122 WCHAR *pszDeviceId = NULL;
1123 hr = IMMDevice_GetId(paWasapi->devInfo[i].device, &pszDeviceId);
1124 IF_FAILED_JUMP(hr, error);
1125 wcsncpy(paWasapi->devInfo[i].szDeviceID, pszDeviceId, MAX_STR_LEN-1);
1126 CoTaskMemFree(pszDeviceId);
1127
1128 if (lstrcmpW(paWasapi->devInfo[i].szDeviceID, paWasapi->defaultCapturer) == 0)
1129 {// we found the default input!
1130 (*hostApi)->info.defaultInputDevice = (*hostApi)->info.deviceCount;
1131 }
1132 if (lstrcmpW(paWasapi->devInfo[i].szDeviceID, paWasapi->defaultRenderer) == 0)
1133 {// we found the default output!
1134 (*hostApi)->info.defaultOutputDevice = (*hostApi)->info.deviceCount;
1135 }
1136 }
1137
1138 hr = IMMDevice_GetState(paWasapi->devInfo[i].device, &paWasapi->devInfo[i].state);
1139 IF_FAILED_JUMP(hr, error);
1140
1141 if (paWasapi->devInfo[i].state != DEVICE_STATE_ACTIVE)
1142 {
1143 PRINT(("WASAPI device: %d is not currently available (state:%d)\n",i,state));
1144 }
1145
1146 {
1147 IPropertyStore *pProperty;
1148 hr = IMMDevice_OpenPropertyStore(paWasapi->devInfo[i].device, STGM_READ, &pProperty);
1149 IF_FAILED_JUMP(hr, error);
1150
1151 // "Friendly" Name
1152 {
1153 char *deviceName;
1154 PROPVARIANT value;
1155 PropVariantInit(&value);
1156 hr = IPropertyStore_GetValue(pProperty, &PKEY_Device_FriendlyName, &value);
1157 IF_FAILED_JUMP(hr, error);
1158 deviceInfo->name = NULL;
1159 deviceName = (char *)PaUtil_GroupAllocateMemory(paWasapi->allocations, MAX_STR_LEN + 1);
1160 if (deviceName == NULL)
1161 {
1162 result = paInsufficientMemory;
1163 goto error;
1164 }
1165 if (value.pwszVal)
1166 wcstombs(deviceName, value.pwszVal, MAX_STR_LEN-1);
1167 else
1168 _snprintf(deviceName, MAX_STR_LEN-1, "baddev%d", i);
1169 deviceInfo->name = deviceName;
1170 PropVariantClear(&value);
1171 PA_DEBUG(("WASAPI:%d| name[%s]\n", i, deviceInfo->name));
1172 }
1173
1174 // Default format
1175 {
1176 PROPVARIANT value;
1177 PropVariantInit(&value);
1178 hr = IPropertyStore_GetValue(pProperty, &PKEY_AudioEngine_DeviceFormat, &value);
1179 IF_FAILED_JUMP(hr, error);
1180 memcpy(&paWasapi->devInfo[i].DefaultFormat, value.blob.pBlobData, min(sizeof(paWasapi->devInfo[i].DefaultFormat), value.blob.cbSize));
1181 // cleanup
1182 PropVariantClear(&value);
1183 }
1184
1185 // Formfactor
1186 {
1187 PROPVARIANT value;
1188 PropVariantInit(&value);
1189 hr = IPropertyStore_GetValue(pProperty, &PKEY_AudioEndpoint_FormFactor, &value);
1190 IF_FAILED_JUMP(hr, error);
1191 // set
1192 #if defined(DUMMYUNIONNAME) && defined(NONAMELESSUNION)
1193 // avoid breaking strict-aliasing rules in such line: (EndpointFormFactor)(*((UINT *)(((WORD *)&value.wReserved3)+1)));
1194 UINT v;
1195 memcpy(&v, (((WORD *)&value.wReserved3)+1), sizeof(v));
1196 paWasapi->devInfo[i].formFactor = (EndpointFormFactor)v;
1197 #else
1198 paWasapi->devInfo[i].formFactor = (EndpointFormFactor)value.uintVal;
1199 #endif
1200 PA_DEBUG(("WASAPI:%d| form-factor[%d]\n", i, paWasapi->devInfo[i].formFactor));
1201 // cleanup
1202 PropVariantClear(&value);
1203 }
1204
1205 SAFE_RELEASE(pProperty);
1206 }
1207
1208
1209 // Endpoint data
1210 {
1211 IMMEndpoint *endpoint = NULL;
1212 hr = IMMDevice_QueryInterface(paWasapi->devInfo[i].device, &pa_IID_IMMEndpoint, (void **)&endpoint);
1213 if (SUCCEEDED(hr))
1214 {
1215 hr = IMMEndpoint_GetDataFlow(endpoint, &paWasapi->devInfo[i].flow);
1216 SAFE_RELEASE(endpoint);
1217 }
1218 }
1219
1220 // Getting a temporary IAudioClient for more fields
1221 // we make sure NOT to call Initialize yet!
1222 {
1223 IAudioClient *tmpClient = NULL;
1224
1225 hr = IMMDevice_Activate(paWasapi->devInfo[i].device, &pa_IID_IAudioClient,
1226 CLSCTX_INPROC_SERVER, NULL, (void **)&tmpClient);
1227 IF_FAILED_JUMP(hr, error);
1228
1229 hr = IAudioClient_GetDevicePeriod(tmpClient,
1230 &paWasapi->devInfo[i].DefaultDevicePeriod,
1231 &paWasapi->devInfo[i].MinimumDevicePeriod);
1232 IF_FAILED_JUMP(hr, error);
1233
1234 //hr = tmpClient->GetMixFormat(&paWasapi->devInfo[i].MixFormat);
1235
1236 // Release client
1237 SAFE_RELEASE(tmpClient);
1238
1239 if (hr != S_OK)
1240 {
1241 //davidv: this happened with my hardware, previously for that same device in DirectSound:
1242 //Digital Output (Realtek AC'97 Audio)'s GUID: {0x38f2cf50,0x7b4c,0x4740,0x86,0xeb,0xd4,0x38,0x66,0xd8,0xc8, 0x9f}
1243 //so something must be _really_ wrong with this device, TODO handle this better. We kind of need GetMixFormat
1244 LogHostError(hr);
1245 goto error;
1246 }
1247 }
1248
1249 // we can now fill in portaudio device data
1250 deviceInfo->maxInputChannels = 0;
1251 deviceInfo->maxOutputChannels = 0;
1252 deviceInfo->defaultSampleRate = paWasapi->devInfo[i].DefaultFormat.Format.nSamplesPerSec;
1253 switch (paWasapi->devInfo[i].flow)
1254 {
1255 case eRender: {
1256 deviceInfo->maxOutputChannels = paWasapi->devInfo[i].DefaultFormat.Format.nChannels;
1257 deviceInfo->defaultHighOutputLatency = nano100ToSeconds(paWasapi->devInfo[i].DefaultDevicePeriod);
1258 deviceInfo->defaultLowOutputLatency = nano100ToSeconds(paWasapi->devInfo[i].MinimumDevicePeriod);
1259 PA_DEBUG(("WASAPI:%d| def.SR[%d] max.CH[%d] latency{hi[%f] lo[%f]}\n", i, (UINT32)deviceInfo->defaultSampleRate,
1260 deviceInfo->maxOutputChannels, (float)deviceInfo->defaultHighOutputLatency, (float)deviceInfo->defaultLowOutputLatency));
1261 break;}
1262 case eCapture: {
1263 deviceInfo->maxInputChannels = paWasapi->devInfo[i].DefaultFormat.Format.nChannels;
1264 deviceInfo->defaultHighInputLatency = nano100ToSeconds(paWasapi->devInfo[i].DefaultDevicePeriod);
1265 deviceInfo->defaultLowInputLatency = nano100ToSeconds(paWasapi->devInfo[i].MinimumDevicePeriod);
1266 PA_DEBUG(("WASAPI:%d| def.SR[%d] max.CH[%d] latency{hi[%f] lo[%f]}\n", i, (UINT32)deviceInfo->defaultSampleRate,
1267 deviceInfo->maxInputChannels, (float)deviceInfo->defaultHighInputLatency, (float)deviceInfo->defaultLowInputLatency));
1268 break; }
1269 default:
1270 PRINT(("WASAPI:%d| bad Data Flow!\n", i));
1271 //continue; // do not skip from list, allow to initialize
1272 break;
1273 }
1274
1275 (*hostApi)->deviceInfos[i] = deviceInfo;
1276 ++(*hostApi)->info.deviceCount;
1277 }
1278 }
1279
1280 (*hostApi)->Terminate = Terminate;
1281 (*hostApi)->OpenStream = OpenStream;
1282 (*hostApi)->IsFormatSupported = IsFormatSupported;
1283
1284 PaUtil_InitializeStreamInterface( &paWasapi->callbackStreamInterface, CloseStream, StartStream,
1285 StopStream, AbortStream, IsStreamStopped, IsStreamActive,
1286 GetStreamTime, GetStreamCpuLoad,
1287 PaUtil_DummyRead, PaUtil_DummyWrite,
1288 PaUtil_DummyGetReadAvailable, PaUtil_DummyGetWriteAvailable );
1289
1290 PaUtil_InitializeStreamInterface( &paWasapi->blockingStreamInterface, CloseStream, StartStream,
1291 StopStream, AbortStream, IsStreamStopped, IsStreamActive,
1292 GetStreamTime, PaUtil_DummyGetCpuLoad,
1293 ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable );
1294
1295
1296 // findout if platform workaround is required
1297 paWasapi->useWOW64Workaround = UseWOW64Workaround();
1298
1299 SAFE_RELEASE(pEndPoints);
1300
1301 PRINT(("WASAPI: initialized ok\n"));
1302
1303 return paNoError;
1304
1305 error:
1306
1307 PRINT(("WASAPI: failed %s error[%d|%s]\n", __FUNCTION__, result, Pa_GetErrorText(result)));
1308
1309 SAFE_RELEASE(pEndPoints);
1310
1311 Terminate((PaUtilHostApiRepresentation *)paWasapi);
1312
1313 return result;
1314 }
1315
1316 // ------------------------------------------------------------------------------------------
1317 static void Terminate( PaUtilHostApiRepresentation *hostApi )
1318 {
1319 UINT i;
1320 PaWasapiHostApiRepresentation *paWasapi = (PaWasapiHostApiRepresentation*)hostApi;
1321 if (paWasapi == NULL)
1322 return;
1323
1324 // Release IMMDeviceEnumerator
1325 SAFE_RELEASE(paWasapi->enumerator);
1326
1327 for (i = 0; i < paWasapi->deviceCount; ++i)
1328 {
1329 PaWasapiDeviceInfo *info = &paWasapi->devInfo[i];
1330 SAFE_RELEASE(info->device);
1331
1332 //if (info->MixFormat)
1333 // CoTaskMemFree(info->MixFormat);
1334 }
1335 free(paWasapi->devInfo);
1336
1337 if (paWasapi->allocations)
1338 {
1339 PaUtil_FreeAllAllocations(paWasapi->allocations);
1340 PaUtil_DestroyAllocationGroup(paWasapi->allocations);
1341 }
1342
1343 PaUtil_FreeMemory(paWasapi);
1344
1345 // Close AVRT
1346 CloseAVRT();
1347
1348 // Uninit COM (checking calling thread we won't unitialize user's COM if one is calling
1349 // Pa_Unitialize by mistake from not initializing thread)
1350 if (g_WasapiCOMInit)
1351 {
1352 DWORD calling_thread_id = GetCurrentThreadId();
1353 if (g_WasapiInitThread != calling_thread_id)
1354 {
1355 PRINT(("WASAPI: failed CoUninitializes calling thread[%d] does not match initializing thread[%d]\n",
1356 calling_thread_id, g_WasapiInitThread));
1357 }
1358 else
1359 {
1360 CoUninitialize();
1361 }
1362 }
1363 }
1364
1365 // ------------------------------------------------------------------------------------------
1366 static PaWasapiHostApiRepresentation *_GetHostApi(PaError *_error)
1367 {
1368 PaError error;
1369
1370 PaUtilHostApiRepresentation *pApi;
1371 if ((error = PaUtil_GetHostApiRepresentation(&pApi, paWASAPI)) != paNoError)
1372 {
1373 if (_error != NULL)
1374 (*_error) = error;
1375
1376 return NULL;
1377 }
1378 return (PaWasapiHostApiRepresentation *)pApi;
1379 }
1380
1381 // ------------------------------------------------------------------------------------------
1382 int PaWasapi_GetDeviceDefaultFormat( void *pFormat, unsigned int nFormatSize, PaDeviceIndex nDevice )
1383 {
1384 PaError ret;
1385 PaWasapiHostApiRepresentation *paWasapi;
1386 UINT32 size;
1387 PaDeviceIndex index;
1388
1389 if (pFormat == NULL)
1390 return paBadBufferPtr;
1391 if (nFormatSize <= 0)
1392 return paBufferTooSmall;
1393
1394 // Get API
1395 paWasapi = _GetHostApi(&ret);
1396 if (paWasapi == NULL)
1397 return ret;
1398
1399 // Get device index
1400 ret = PaUtil_DeviceIndexToHostApiDeviceIndex(&index, nDevice, &paWasapi->inheritedHostApiRep);
1401 if (ret != paNoError)
1402 return ret;
1403
1404 // Validate index
1405 if ((UINT32)index >= paWasapi->deviceCount)
1406 return paInvalidDevice;
1407
1408 size = min(nFormatSize, (UINT32)sizeof(paWasapi->devInfo[ index ].DefaultFormat));
1409 memcpy(pFormat, &paWasapi->devInfo[ index ].DefaultFormat, size);
1410
1411 return size;
1412 }
1413
1414 // ------------------------------------------------------------------------------------------
1415 int PaWasapi_GetDeviceRole( PaDeviceIndex nDevice )
1416 {
1417 PaError ret;
1418 PaDeviceIndex index;
1419
1420 // Get API
1421 PaWasapiHostApiRepresentation *paWasapi = _GetHostApi(&ret);
1422 if (paWasapi == NULL)
1423 return paNotInitialized;
1424
1425 // Get device index
1426 ret = PaUtil_DeviceIndexToHostApiDeviceIndex(&index, nDevice, &paWasapi->inheritedHostApiRep);
1427 if (ret != paNoError)
1428 return ret;
1429
1430 // Validate index
1431 if ((UINT32)index >= paWasapi->deviceCount)
1432 return paInvalidDevice;
1433
1434 return paWasapi->devInfo[ index ].formFactor;
1435 }
1436
1437 // ------------------------------------------------------------------------------------------
1438 PaError PaWasapi_GetFramesPerHostBuffer( PaStream *pStream, unsigned int *nInput, unsigned int *nOutput )
1439 {
1440 PaWasapiStream *stream = (PaWasapiStream *)pStream;
1441 if (stream == NULL)
1442 return paBadStreamPtr;
1443
1444 if (nInput != NULL)
1445 (*nInput) = stream->in.framesPerHostCallback;
1446
1447 if (nOutput != NULL)
1448 (*nOutput) = stream->out.framesPerHostCallback;
1449
1450 return paNoError;
1451 }
1452
1453 // ------------------------------------------------------------------------------------------
1454 static void LogWAVEFORMATEXTENSIBLE(const WAVEFORMATEXTENSIBLE *in)
1455 {
1456 const WAVEFORMATEX *old = (WAVEFORMATEX *)in;
1457 switch (old->wFormatTag)
1458 {
1459 case WAVE_FORMAT_EXTENSIBLE: {
1460
1461 PRINT(("wFormatTag =WAVE_FORMAT_EXTENSIBLE\n"));
1462
1463 if (IsEqualGUID(&in->SubFormat, &pa_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))
1464 {
1465 PRINT(("SubFormat =KSDATAFORMAT_SUBTYPE_IEEE_FLOAT\n"));
1466 }
1467 else
1468 if (IsEqualGUID(&in->SubFormat, &pa_KSDATAFORMAT_SUBTYPE_PCM))
1469 {
1470 PRINT(("SubFormat =KSDATAFORMAT_SUBTYPE_PCM\n"));
1471 }
1472 else
1473 {
1474 PRINT(("SubFormat =CUSTOM GUID{%d:%d:%d:%d%d%d%d%d%d%d%d}\n",
1475 in->SubFormat.Data1,
1476 in->SubFormat.Data2,
1477 in->SubFormat.Data3,
1478 (int)in->SubFormat.Data4[0],
1479 (int)in->SubFormat.Data4[1],
1480 (int)in->SubFormat.Data4[2],
1481 (int)in->SubFormat.Data4[3],
1482 (int)in->SubFormat.Data4[4],
1483 (int)in->SubFormat.Data4[5],
1484 (int)in->SubFormat.Data4[6],
1485 (int)in->SubFormat.Data4[7]));
1486 }
1487 PRINT(("Samples.wValidBitsPerSample =%d\n", in->Samples.wValidBitsPerSample));
1488 PRINT(("dwChannelMask =0x%X\n",in->dwChannelMask));
1489
1490 break; }
1491
1492 case WAVE_FORMAT_PCM: PRINT(("wFormatTag =WAVE_FORMAT_PCM\n")); break;
1493 case WAVE_FORMAT_IEEE_FLOAT: PRINT(("wFormatTag =WAVE_FORMAT_IEEE_FLOAT\n")); break;
1494 default:
1495 PRINT(("wFormatTag =UNKNOWN(%d)\n",old->wFormatTag)); break;
1496 }
1497
1498 PRINT(("nChannels =%d\n",old->nChannels));
1499 PRINT(("nSamplesPerSec =%d\n",old->nSamplesPerSec));
1500 PRINT(("nAvgBytesPerSec=%d\n",old->nAvgBytesPerSec));
1501 PRINT(("nBlockAlign =%d\n",old->nBlockAlign));
1502 PRINT(("wBitsPerSample =%d\n",old->wBitsPerSample));
1503 PRINT(("cbSize =%d\n",old->cbSize));
1504 }
1505
1506 // ------------------------------------------------------------------------------------------
1507 static PaSampleFormat WaveToPaFormat(const WAVEFORMATEXTENSIBLE *in)
1508 {
1509 const WAVEFORMATEX *old = (WAVEFORMATEX *)in;
1510
1511 switch (old->wFormatTag)
1512 {
1513 case WAVE_FORMAT_EXTENSIBLE: {
1514 if (IsEqualGUID(&in->SubFormat, &pa_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))
1515 {
1516 if (in->Samples.wValidBitsPerSample == 32)
1517 return paFloat32;
1518 }
1519 else
1520 if (IsEqualGUID(&in->SubFormat, &pa_KSDATAFORMAT_SUBTYPE_PCM))
1521 {
1522 switch (old->wBitsPerSample)
1523 {
1524 case 32: return paInt32;
1525 case 24: return paInt24;
1526 case 8: return paUInt8;
1527 case 16: return paInt16;
1528 }
1529 }
1530 break; }
1531
1532 case WAVE_FORMAT_IEEE_FLOAT:
1533 return paFloat32;
1534
1535 case WAVE_FORMAT_PCM: {
1536 switch (old->wBitsPerSample)
1537 {
1538 case 32: return paInt32;
1539 case 24: return paInt24;
1540 case 8: return paUInt8;
1541 case 16: return paInt16;
1542 }
1543 break; }
1544 }
1545
1546 return paCustomFormat;
1547 }
1548
1549 // ------------------------------------------------------------------------------------------
1550 static PaError MakeWaveFormatFromParams(WAVEFORMATEXTENSIBLE *wavex, const PaStreamParameters *params,
1551 double sampleRate)
1552 {
1553 WORD bitsPerSample;
1554 WAVEFORMATEX *old;
1555 DWORD channelMask = 0;
1556 PaWasapiStreamInfo *streamInfo = (PaWasapiStreamInfo *)params->hostApiSpecificStreamInfo;
1557
1558 // Get user assigned channel mask
1559 if ((streamInfo != NULL) && (streamInfo->flags & paWinWasapiUseChannelMask))
1560 channelMask = streamInfo->channelMask;
1561
1562 // Convert PaSampleFormat to bits per sample
1563 if ((bitsPerSample = PaSampleFormatToBitsPerSample(params->sampleFormat)) == 0)
1564 return paSampleFormatNotSupported;
1565
1566 memset(wavex, 0, sizeof(*wavex));
1567
1568 old = (WAVEFORMATEX *)wavex;
1569 old->nChannels = (WORD)params->channelCount;
1570 old->nSamplesPerSec = (DWORD)sampleRate;
1571 if ((old->wBitsPerSample = bitsPerSample) > 16)
1572 {
1573 old->wBitsPerSample = 32; // 20 or 24 bits must go in 32 bit containers (ints)
1574 }
1575 old->nBlockAlign = (old->nChannels * (old->wBitsPerSample/8));
1576 old->nAvgBytesPerSec = (old->nSamplesPerSec * old->nBlockAlign);
1577
1578 // WAVEFORMATEX
1579 if ((params->channelCount <= 2) && ((bitsPerSample == 16) || (bitsPerSample == 8)))
1580 {
1581 old->cbSize = 0;
1582 old->wFormatTag = WAVE_FORMAT_PCM;
1583 }
1584 // WAVEFORMATEXTENSIBLE
1585 else
1586 {
1587 old->wFormatTag = WAVE_FORMAT_EXTENSIBLE;
1588 old->cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
1589
1590 if ((params->sampleFormat & ~paNonInterleaved) == paFloat32)
1591 wavex->SubFormat = pa_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
1592 else
1593 wavex->SubFormat = pa_KSDATAFORMAT_SUBTYPE_PCM;
1594
1595 wavex->Samples.wValidBitsPerSample = bitsPerSample; //no extra padding!
1596
1597 // Set channel mask
1598 if (channelMask != 0)
1599 {
1600 wavex->dwChannelMask = channelMask;
1601 }
1602 else
1603 {
1604 switch (params->channelCount)
1605 {
1606 case 1: wavex->dwChannelMask = KSAUDIO_SPEAKER_MONO; break;
1607 case 2: wavex->dwChannelMask = KSAUDIO_SPEAKER_STEREO; break;
1608 case 3: wavex->dwChannelMask = KSAUDIO_SPEAKER_STEREO|SPEAKER_LOW_FREQUENCY; break;
1609 case 4: wavex->dwChannelMask = KSAUDIO_SPEAKER_QUAD; break;
1610 case 5: wavex->dwChannelMask = KSAUDIO_SPEAKER_QUAD|SPEAKER_LOW_FREQUENCY; break;
1611 #ifdef KSAUDIO_SPEAKER_5POINT1_SURROUND
1612 case 6: wavex->dwChannelMask = KSAUDIO_SPEAKER_5POINT1_SURROUND; break;
1613 #else
1614 case 6: wavex->dwChannelMask = KSAUDIO_SPEAKER_5POINT1; break;
1615 #endif
1616 #ifdef KSAUDIO_SPEAKER_5POINT1_SURROUND
1617 case 7: wavex->dwChannelMask = KSAUDIO_SPEAKER_5POINT1_SURROUND|SPEAKER_BACK_CENTER; break;
1618 #else
1619 case 7: wavex->dwChannelMask = KSAUDIO_SPEAKER_5POINT1|SPEAKER_BACK_CENTER; break;
1620 #endif
1621 #ifdef KSAUDIO_SPEAKER_7POINT1_SURROUND
1622 case 8: wavex->dwChannelMask = KSAUDIO_SPEAKER_7POINT1_SURROUND; break;
1623 #else
1624 case 8: wavex->dwChannelMask = KSAUDIO_SPEAKER_7POINT1; break;
1625 #endif
1626
1627 default: wavex->dwChannelMask = 0;
1628 }
1629 }
1630 }
1631 return paNoError;
1632 }
1633
1634 // ------------------------------------------------------------------------------------------
1635 /*static void wasapiFillWFEXT( WAVEFORMATEXTENSIBLE* pwfext, PaSampleFormat sampleFormat, double sampleRate, int channelCount)
1636 {
1637 PA_DEBUG(( "sampleFormat = %lx\n" , sampleFormat ));
1638 PA_DEBUG(( "sampleRate = %f\n" , sampleRate ));
1639 PA_DEBUG(( "chanelCount = %d\n", channelCount ));
1640
1641 pwfext->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
1642 pwfext->Format.nChannels = (WORD)channelCount;
1643 pwfext->Format.nSamplesPerSec = (DWORD)sampleRate;
1644 if(channelCount == 1)
1645 pwfext->dwChannelMask = KSAUDIO_SPEAKER_DIRECTOUT;
1646 else
1647 pwfext->dwChannelMask = KSAUDIO_SPEAKER_STEREO;
1648 if(sampleFormat == paFloat32)
1649 {
1650 pwfext->Format.nBlockAlign = (WORD)(channelCount * 4);
1651 pwfext->Format.wBitsPerSample = 32;
1652 pwfext->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX);
1653 pwfext->Samples.wValidBitsPerSample = 32;
1654 pwfext->SubFormat = pa_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
1655 }
1656 else if(sampleFormat == paInt32)
1657 {
1658 pwfext->Format.nBlockAlign = (WORD)(channelCount * 4);
1659 pwfext->Format.wBitsPerSample = 32;
1660 pwfext->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX);
1661 pwfext->Samples.wValidBitsPerSample = 32;
1662 pwfext->SubFormat = pa_KSDATAFORMAT_SUBTYPE_PCM;
1663 }
1664 else if(sampleFormat == paInt24)
1665 {
1666 pwfext->Format.nBlockAlign = (WORD)(channelCount * 4);
1667 pwfext->Format.wBitsPerSample = 32; // 24-bit in 32-bit int container
1668 pwfext->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX);
1669 pwfext->Samples.wValidBitsPerSample = 24;
1670 pwfext->SubFormat = pa_KSDATAFORMAT_SUBTYPE_PCM;
1671 }
1672 else if(sampleFormat == paInt16)
1673 {
1674 pwfext->Format.nBlockAlign = (WORD)(channelCount * 2);
1675 pwfext->Format.wBitsPerSample = 16;
1676 pwfext->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX);
1677 pwfext->Samples.wValidBitsPerSample = 16;
1678 pwfext->SubFormat = pa_KSDATAFORMAT_SUBTYPE_PCM;
1679 }
1680 pwfext->Format.nAvgBytesPerSec = pwfext->Format.nSamplesPerSec * pwfext->Format.nBlockAlign;
1681 }*/
1682
1683 // ------------------------------------------------------------------------------------------
1684 static PaError GetClosestFormat(IAudioClient *myClient, double sampleRate,
1685 const PaStreamParameters *_params, AUDCLNT_SHAREMODE shareMode, WAVEFORMATEXTENSIBLE *outWavex,
1686 BOOL output)
1687 {
1688 PaError answer = paInvalidSampleRate;
1689 WAVEFORMATEX *sharedClosestMatch = NULL;
1690 HRESULT hr = !S_OK;
1691 PaStreamParameters params = (*_params);
1692
1693 /* It was not noticed that 24-bit Input producing no output while device accepts this format.
1694 To fix this issue let's ask for 32-bits and let PA converters convert host 32-bit data
1695 to 24-bit for user-space. The bug concerns Vista, if Windows 7 supports 24-bits for Input
1696 please report to PortAudio developers to exclude Windows 7.
1697 */
1698 /*if ((params.sampleFormat == paInt24) && (output == FALSE))
1699 params.sampleFormat = paFloat32;*/ // <<< The silence was due to missing Int32_To_Int24_Dither implementation
1700
1701 MakeWaveFormatFromParams(outWavex, &params, sampleRate);
1702
1703 hr = IAudioClient_IsFormatSupported(myClient, shareMode, &outWavex->Format, (shareMode == AUDCLNT_SHAREMODE_SHARED ? &sharedClosestMatch : NULL));
1704 if (hr == S_OK)
1705 answer = paFormatIsSupported;
1706 else
1707 if (sharedClosestMatch)
1708 {
1709 WORD bitsPerSample;
1710 WAVEFORMATEXTENSIBLE *ext = (WAVEFORMATEXTENSIBLE*)sharedClosestMatch;
1711
1712 GUID subf_guid = GUID_NULL;
1713 if (sharedClosestMatch->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
1714 {
1715 memcpy(outWavex, sharedClosestMatch, sizeof(WAVEFORMATEXTENSIBLE));
1716 subf_guid = ext->SubFormat;
1717 }
1718 else
1719 memcpy(outWavex, sharedClosestMatch, sizeof(WAVEFORMATEX));
1720
1721 CoTaskMemFree(sharedClosestMatch);
1722
1723 // Make supported by default
1724 answer = paFormatIsSupported;
1725
1726 // Validate SampleRate
1727 if ((DWORD)sampleRate != outWavex->Format.nSamplesPerSec)
1728 return paInvalidSampleRate;
1729
1730 // Validate Channel count
1731 if ((WORD)params.channelCount != outWavex->Format.nChannels)
1732 {
1733 // If mono, then driver does not support 1 channel, we use internal workaround
1734 // of tiny software mixing functionality, e.g. we provide to user buffer 1 channel
1735 // but then mix into 2 for device buffer
1736 if ((params.channelCount == 1) && (outWavex->Format.nChannels == 2))
1737 return paFormatIsSupported;
1738 else
1739 return paInvalidChannelCount;
1740 }
1741
1742 // Validate Sample format
1743 if ((bitsPerSample = PaSampleFormatToBitsPerSample(params.sampleFormat)) == 0)
1744 return paSampleFormatNotSupported;
1745
1746 // Validate Sample format: bit size (WASAPI does not limit 'bit size')
1747 //if (bitsPerSample != outWavex->Format.wBitsPerSample)
1748 // return paSampleFormatNotSupported;
1749
1750 // Validate Sample format: paFloat32 (WASAPI does not limit 'bit type')
1751 //if ((params->sampleFormat == paFloat32) && (subf_guid != KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))
1752 // return paSampleFormatNotSupported;
1753
1754 // Validate Sample format: paInt32 (WASAPI does not limit 'bit type')
1755 //if ((params->sampleFormat == paInt32) && (subf_guid != KSDATAFORMAT_SUBTYPE_PCM))
1756 // return paSampleFormatNotSupported;
1757 }
1758 else
1759 {
1760 static const int BestToWorst[] = { paFloat32, paInt24, paInt16 };
1761 int i;
1762
1763 // Try combination stereo and we will use built-in mono-stereo mixer then
1764 if (params.channelCount == 1)
1765 {
1766 WAVEFORMATEXTENSIBLE stereo = { 0 };
1767
1768 PaStreamParameters stereo_params = params;
1769 stereo_params.channelCount = 2;
1770
1771 MakeWaveFormatFromParams(&stereo, &stereo_params, sampleRate);
1772
1773 hr = IAudioClient_IsFormatSupported(myClient, shareMode, &stereo.Format, (shareMode == AUDCLNT_SHAREMODE_SHARED ? &sharedClosestMatch : NULL));
1774 if (hr == S_OK)
1775 {
1776 memcpy(outWavex, &stereo, sizeof(WAVEFORMATEXTENSIBLE));
1777 CoTaskMemFree(sharedClosestMatch);
1778 return (answer = paFormatIsSupported);
1779 }
1780
1781 // Try selecting suitable sample type
1782 for (i = 0; i < STATIC_ARRAY_SIZE(BestToWorst); ++i)
1783 {
1784 WAVEFORMATEXTENSIBLE sample = { 0 };
1785
1786 PaStreamParameters sample_params = stereo_params;
1787 sample_params.sampleFormat = BestToWorst[i];
1788
1789 MakeWaveFormatFromParams(&sample, &sample_params, sampleRate);
1790
1791 hr = IAudioClient_IsFormatSupported(myClient, shareMode, &sample.Format, (shareMode == AUDCLNT_SHAREMODE_SHARED ? &sharedClosestMatch : NULL));
1792 if (hr == S_OK)
1793 {
1794 memcpy(outWavex, &sample, sizeof(WAVEFORMATEXTENSIBLE));
1795 CoTaskMemFree(sharedClosestMatch);
1796 return (answer = paFormatIsSupported);
1797 }
1798 }
1799 }
1800
1801 // Try selecting suitable sample type
1802 for (i = 0; i < STATIC_ARRAY_SIZE(BestToWorst); ++i)
1803 {
1804 WAVEFORMATEXTENSIBLE spfmt = { 0 };
1805
1806 PaStreamParameters spfmt_params = params;
1807 spfmt_params.sampleFormat = BestToWorst[i];
1808
1809 MakeWaveFormatFromParams(&spfmt, &spfmt_params, sampleRate);
1810
1811 hr = IAudioClient_IsFormatSupported(myClient, shareMode, &spfmt.Format, (shareMode == AUDCLNT_SHAREMODE_SHARED ? &sharedClosestMatch : NULL));
1812 if (hr == S_OK)
1813 {
1814 memcpy(outWavex, &spfmt, sizeof(WAVEFORMATEXTENSIBLE));
1815 CoTaskMemFree(sharedClosestMatch);
1816 answer = paFormatIsSupported;
1817 break;
1818 }
1819 }
1820
1821 // Nothing helped
1822 LogHostError(hr);
1823 }
1824
1825 return answer;
1826 }
1827
1828 // ------------------------------------------------------------------------------------------
1829 static PaError IsStreamParamsValid(struct PaUtilHostApiRepresentation *hostApi,
1830 const PaStreamParameters *inputParameters,
1831 const PaStreamParameters *outputParameters,
1832 double sampleRate)
1833 {
1834 if (hostApi == NULL)
1835 return paHostApiNotFound;
1836 if ((UINT32)sampleRate == 0)
1837 return paInvalidSampleRate;
1838
1839 if (inputParameters != NULL)
1840 {
1841 /* all standard sample formats are supported by the buffer adapter,
1842 this implementation doesn't support any custom sample formats */
1843 if (inputParameters->sampleFormat & paCustomFormat)
1844 return paSampleFormatNotSupported;
1845
1846 /* unless alternate device specification is supported, reject the use of
1847 paUseHostApiSpecificDeviceSpecification */
1848 if (inputParameters->device == paUseHostApiSpecificDeviceSpecification)
1849 return paInvalidDevice;
1850
1851 /* check that input device can support inputChannelCount */
1852 if (inputParameters->channelCount > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels)
1853 return paInvalidChannelCount;
1854
1855 /* validate inputStreamInfo */
1856 if (inputParameters->hostApiSpecificStreamInfo)
1857 {
1858 PaWasapiStreamInfo *inputStreamInfo = (PaWasapiStreamInfo *)inputParameters->hostApiSpecificStreamInfo;
1859 if ((inputStreamInfo->size != sizeof(PaWasapiStreamInfo)) ||
1860 (inputStreamInfo->version != 1) ||
1861 (inputStreamInfo->hostApiType != paWASAPI))
1862 {
1863 return paIncompatibleHostApiSpecificStreamInfo;
1864 }
1865 }
1866
1867 return paNoError;
1868 }
1869
1870 if (outputParameters != NULL)
1871 {
1872 /* all standard sample formats are supported by the buffer adapter,
1873 this implementation doesn't support any custom sample formats */
1874 if (outputParameters->sampleFormat & paCustomFormat)
1875 return paSampleFormatNotSupported;
1876
1877 /* unless alternate device specification is supported, reject the use of
1878 paUseHostApiSpecificDeviceSpecification */
1879 if (outputParameters->device == paUseHostApiSpecificDeviceSpecification)
1880 return paInvalidDevice;
1881
1882 /* check that output device can support outputChannelCount */
1883 if (outputParameters->channelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels)
1884 return paInvalidChannelCount;
1885
1886 /* validate outputStreamInfo */
1887 if(outputParameters->hostApiSpecificStreamInfo)
1888 {
1889 PaWasapiStreamInfo *outputStreamInfo = (PaWasapiStreamInfo *)outputParameters->hostApiSpecificStreamInfo;
1890 if ((outputStreamInfo->size != sizeof(PaWasapiStreamInfo)) ||
1891 (outputStreamInfo->version != 1) ||
1892 (outputStreamInfo->hostApiType != paWASAPI))
1893 {
1894 return paIncompatibleHostApiSpecificStreamInfo;
1895 }
1896 }
1897
1898 return paNoError;
1899 }
1900
1901 return (inputParameters || outputParameters ? paNoError : paInternalError);
1902 }
1903
1904 // ------------------------------------------------------------------------------------------
1905 static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
1906 const PaStreamParameters *inputParameters,
1907 const PaStreamParameters *outputParameters,
1908 double sampleRate )
1909 {
1910 IAudioClient *tmpClient = NULL;
1911 PaWasapiHostApiRepresentation *paWasapi = (PaWasapiHostApiRepresentation*)hostApi;
1912 PaWasapiStreamInfo *inputStreamInfo = NULL, *outputStreamInfo = NULL;
1913
1914 // Validate PaStreamParameters
1915 PaError error;
1916 if ((error = IsStreamParamsValid(hostApi, inputParameters, outputParameters, sampleRate)) != paNoError)
1917 return error;
1918
1919 if (inputParameters != NULL)
1920 {
1921 WAVEFORMATEXTENSIBLE wavex;
1922 HRESULT hr;
1923 PaError answer;
1924 AUDCLNT_SHAREMODE shareMode = AUDCLNT_SHAREMODE_SHARED;
1925 inputStreamInfo = (PaWasapiStreamInfo *)inputParameters->hostApiSpecificStreamInfo;
1926
1927 if (inputStreamInfo && (inputStreamInfo->flags & paWinWasapiExclusive))
1928 shareMode = AUDCLNT_SHAREMODE_EXCLUSIVE;
1929
1930 hr = IMMDevice_Activate(paWasapi->devInfo[inputParameters->device].device,
1931 &pa_IID_IAudioClient, CLSCTX_INPROC_SERVER, NULL, (void **)&tmpClient);
1932 if (hr != S_OK)
1933 {
1934 LogHostError(hr);
1935 return paInvalidDevice;
1936 }
1937
1938 answer = GetClosestFormat(tmpClient, sampleRate, inputParameters, shareMode, &wavex, FALSE);
1939 SAFE_RELEASE(tmpClient);
1940
1941 if (answer != paFormatIsSupported)
1942 return answer;
1943 }
1944
1945 if (outputParameters != NULL)
1946 {
1947 HRESULT hr;
1948 WAVEFORMATEXTENSIBLE wavex;
1949 PaError answer;
1950 AUDCLNT_SHAREMODE shareMode = AUDCLNT_SHAREMODE_SHARED;
1951 outputStreamInfo = (PaWasapiStreamInfo *)outputParameters->hostApiSpecificStreamInfo;
1952
1953 if (outputStreamInfo && (outputStreamInfo->flags & paWinWasapiExclusive))
1954 shareMode = AUDCLNT_SHAREMODE_EXCLUSIVE;
1955
1956 hr = IMMDevice_Activate(paWasapi->devInfo[outputParameters->device].device,
1957 &pa_IID_IAudioClient, CLSCTX_INPROC_SERVER, NULL, (void **)&tmpClient);
1958 if (hr != S_OK)
1959 {
1960 LogHostError(hr);
1961 return paInvalidDevice;
1962 }
1963
1964 answer = GetClosestFormat(tmpClient, sampleRate, outputParameters, shareMode, &wavex, TRUE);
1965 SAFE_RELEASE(tmpClient);
1966
1967 if (answer != paFormatIsSupported)
1968 return answer;
1969 }
1970
1971 return paFormatIsSupported;
1972 }
1973
1974 // ------------------------------------------------------------------------------------------
1975 static PaUint32 PaUtil_GetFramesPerHostBuffer(PaUint32 userFramesPerBuffer, PaTime suggestedLatency, double sampleRate, PaUint32 TimerJitterMs)
1976 {
1977 PaUint32 frames = userFramesPerBuffer + max( userFramesPerBuffer, (PaUint32)(suggestedLatency * sampleRate) );
1978 frames += (PaUint32)((sampleRate * 0.001) * TimerJitterMs);
1979 return frames;
1980 }
1981
1982 // ------------------------------------------------------------------------------------------
1983 static void _RecalculateBuffersCount(PaWasapiSubStream *sub, UINT32 userFramesPerBuffer, UINT32 framesPerLatency, BOOL fullDuplex)
1984 {
1985 // Count buffers (must be at least 1)
1986 sub->buffers = (userFramesPerBuffer ? framesPerLatency / userFramesPerBuffer : 0);
1987 if (sub->buffers == 0)
1988 sub->buffers = 1;
1989
1990 // Determine amount of buffers used:
1991 // - Full-duplex mode will lead to period difference, thus only 1.
1992 // - Input mode, only 1, as WASAPI allows extraction of only 1 packet.
1993 // - For Shared mode we use double buffering.
1994 if ((sub->shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE) || fullDuplex)
1995 {
1996 // Exclusive mode does not allow >1 buffers be used for Event interface, e.g. GetBuffer
1997 // call must acquire max buffer size and it all must be processed.
1998 if (sub->streamFlags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK)
1999 sub->userBufferAndHostMatch = 1;
2000
2001 // Use paUtilBoundedHostBufferSize because exclusive mode will starve and produce
2002 // bad quality of audio
2003 sub->buffers = 1;
2004 }
2005 }
2006
2007 // ------------------------------------------------------------------------------------------
2008 static void _CalculateAlignedPeriod(PaWasapiSubStream *pSub, UINT32 *nFramesPerLatency,
2009 ALIGN_FUNC pAlignFunc)
2010 {
2011 // Align frames to HD Audio packet size of 128 bytes for Exclusive mode only.
2012 // Not aligning on Windows Vista will cause Event timeout, although Windows 7 will
2013 // return AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED error to realign buffer. Aligning is necessary
2014 // for Exclusive mode only! when audio data is feeded directly to hardware.
2015 if (pSub->shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE)
2016 {
2017 (*nFramesPerLatency) = AlignFramesPerBuffer((*nFramesPerLatency),
2018 pSub->wavex.Format.nSamplesPerSec, pSub->wavex.Format.nBlockAlign, pAlignFunc);
2019 }
2020
2021 // Calculate period
2022 pSub->period = MakeHnsPeriod((*nFramesPerLatency), pSub->wavex.Format.nSamplesPerSec);
2023 }
2024
2025 // ------------------------------------------------------------------------------------------
2026 static HRESULT CreateAudioClient(PaWasapiStream *pStream, PaWasapiSubStream *pSub, BOOL output, PaError *pa_error)
2027 {
2028 PaError error;
2029 HRESULT hr;
2030
2031 const PaWasapiDeviceInfo *pInfo = pSub->params.device_info;
2032 const PaStreamParameters *params = &pSub->params.stream_params;
2033 UINT32 framesPerLatency = pSub->params.frames_per_buffer;
2034 double sampleRate = pSub->params.sample_rate;
2035 BOOL blocking = pSub->params.blocking;
2036 BOOL fullDuplex = pSub->params.full_duplex;
2037
2038 const UINT32 userFramesPerBuffer = framesPerLatency;
2039 IAudioClient *audioClient = NULL;
2040
2041
2042 // Validate parameters
2043 if (!pSub || !pInfo || !params)
2044 return E_POINTER;
2045 if ((UINT32)sampleRate == 0)
2046 return E_INVALIDARG;
2047
2048 // Get the audio client
2049 hr = IMMDevice_Activate(pInfo->device, &pa_IID_IAudioClient, CLSCTX_ALL, NULL, (void **)&audioClient);
2050 if (hr != S_OK)
2051 {
2052 LogHostError(hr);
2053 goto done;
2054 }
2055
2056 // Get closest format
2057 if ((error = GetClosestFormat(audioClient, sampleRate, params, pSub->shareMode, &pSub->wavex, output)) != paFormatIsSupported)
2058 {
2059 if (pa_error)
2060 (*pa_error) = error;
2061
2062 LogHostError(hr = AUDCLNT_E_UNSUPPORTED_FORMAT);
2063 goto done; // fail, format not supported
2064 }
2065
2066 // Check for Mono <<>> Stereo workaround
2067 if ((params->channelCount == 1) && (pSub->wavex.Format.nChannels == 2))
2068 {
2069 /*if (blocking)
2070 {
2071 LogHostError(hr = AUDCLNT_E_UNSUPPORTED_FORMAT);
2072 goto done; // fail, blocking mode not supported
2073 }*/
2074
2075 // select mixer
2076 pSub->monoMixer = _GetMonoToStereoMixer(WaveToPaFormat(&pSub->wavex), (pInfo->flow == eRender ? MIX_DIR__1TO2 : MIX_DIR__2TO1_L));
2077 if (pSub->monoMixer == NULL)
2078 {
2079 LogHostError(hr = AUDCLNT_E_UNSUPPORTED_FORMAT);
2080 goto done; // fail, no mixer for format
2081 }
2082 }
2083
2084 #if 0
2085 // Add suggestd latency
2086 framesPerLatency += MakeFramesFromHns(SecondsTonano100(params->suggestedLatency), pSub->wavex.Format.nSamplesPerSec);
2087 #else
2088 // Calculate host buffer size
2089 if ((pSub->shareMode != AUDCLNT_SHAREMODE_EXCLUSIVE) &&
2090 (!pSub->streamFlags || ((pSub->streamFlags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK) == 0)))
2091 {
2092 framesPerLatency = PaUtil_GetFramesPerHostBuffer(userFramesPerBuffer,
2093 params->suggestedLatency, pSub->wavex.Format.nSamplesPerSec, 0/*,
2094 (pSub->streamFlags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK ? 0 : 1)*/);
2095 }
2096 else
2097 {
2098 REFERENCE_TIME overall;
2099
2100 // Work 1:1 with user buffer (only polling allows to use >1)
2101 framesPerLatency += MakeFramesFromHns(SecondsTonano100(params->suggestedLatency), pSub->wavex.Format.nSamplesPerSec);
2102
2103 // Use Polling if overall latency is > 5ms as it allows to use 100% CPU in a callback,
2104 // or user specified latency parameter
2105 overall = MakeHnsPeriod(framesPerLatency, pSub->wavex.Format.nSamplesPerSec);
2106 if ((overall >= (106667*2)/*21.33ms*/) || ((INT32)(params->suggestedLatency*100000.0) != 0/*0.01 msec granularity*/))
2107 {
2108 framesPerLatency = PaUtil_GetFramesPerHostBuffer(userFramesPerBuffer,
2109 params->suggestedLatency, pSub->wavex.Format.nSamplesPerSec, 0/*,
2110 (streamFlags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK ? 0 : 1)*/);
2111
2112 // Use Polling interface
2113 pSub->streamFlags &= ~AUDCLNT_STREAMFLAGS_EVENTCALLBACK;
2114 PRINT(("WASAPI: CreateAudioClient: forcing POLL mode\n"));
2115 }
2116 }
2117 #endif
2118
2119 // For full-duplex output resize buffer to be the same as for input
2120 if (output && fullDuplex)
2121 framesPerLatency = pStream->in.framesPerHostCallback;
2122
2123 // Avoid 0 frames
2124 if (framesPerLatency == 0)
2125 framesPerLatency = MakeFramesFromHns(pInfo->DefaultDevicePeriod, pSub->wavex.Format.nSamplesPerSec);
2126
2127 // Calculate aligned period
2128 _CalculateAlignedPeriod(pSub, &framesPerLatency, ALIGN_BWD);
2129
2130 /*! Enforce min/max period for device in Shared mode to avoid bad audio quality.
2131 Avoid doing so for Exclusive mode as alignment will suffer.
2132 */
2133 if (pSub->shareMode == AUDCLNT_SHAREMODE_SHARED)
2134 {
2135 if (pSub->period < pInfo->DefaultDevicePeriod)
2136 {
2137 pSub->period = pInfo->DefaultDevicePeriod;
2138 // Recalculate aligned period
2139 framesPerLatency = MakeFramesFromHns(pSub->period, pSub->wavex.Format.nSamplesPerSec);
2140 _CalculateAlignedPeriod(pSub, &framesPerLatency, ALIGN_BWD);
2141 }
2142 }
2143 else
2144 {
2145 if (pSub->period < pInfo->MinimumDevicePeriod)
2146 {
2147 pSub->period = pInfo->MinimumDevicePeriod;
2148 // Recalculate aligned period
2149 framesPerLatency = MakeFramesFromHns(pSub->period, pSub->wavex.Format.nSamplesPerSec);
2150 _CalculateAlignedPeriod(pSub, &framesPerLatency, ALIGN_FWD);
2151 }
2152 }
2153
2154 /*! Windows 7 does not allow to set latency lower than minimal device period and will
2155 return error: AUDCLNT_E_INVALID_DEVICE_PERIOD. Under Vista we enforce the same behavior
2156 manually for unified behavior on all platforms.
2157 */
2158 {
2159 /*! AUDCLNT_E_BUFFER_SIZE_ERROR: Applies to Windows 7 and later.
2160 Indicates that the buffer duration value requested by an exclusive-mode client is
2161 out of range. The requested duration value for pull mode must not be greater than
2162 500 milliseconds; for push mode the duration value must not be greater than 2 seconds.
2163 */
2164 if (pSub->shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE)
2165 {
2166 static const REFERENCE_TIME MAX_BUFFER_EVENT_DURATION = 500 * 10000;
2167 static const REFERENCE_TIME MAX_BUFFER_POLL_DURATION = 2000 * 10000;
2168
2169 if (pSub->streamFlags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK) // pull mode, max 500ms
2170 {
2171 if (pSub->period > MAX_BUFFER_EVENT_DURATION)
2172 {
2173 pSub->period = MAX_BUFFER_EVENT_DURATION;
2174 // Recalculate aligned period
2175 framesPerLatency = MakeFramesFromHns(pSub->period, pSub->wavex.Format.nSamplesPerSec);
2176 _CalculateAlignedPeriod(pSub, &framesPerLatency, ALIGN_BWD);
2177 }
2178 }
2179 else // push mode, max 2000ms
2180 {
2181 if (pSub->period > MAX_BUFFER_POLL_DURATION)
2182 {
2183 pSub->period = MAX_BUFFER_POLL_DURATION;
2184 // Recalculate aligned period
2185 framesPerLatency = MakeFramesFromHns(pSub->period, pSub->wavex.Format.nSamplesPerSec);
2186 _CalculateAlignedPeriod(pSub, &framesPerLatency, ALIGN_BWD);
2187 }
2188 }
2189 }
2190 }
2191
2192 // Open the stream and associate it with an audio session
2193 hr = IAudioClient_Initialize(audioClient,
2194 pSub->shareMode,
2195 pSub->streamFlags,
2196 pSub->period,
2197 (pSub->shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE ? pSub->period : 0),
2198 &pSub->wavex.Format,
2199 NULL);
2200
2201 /*! WASAPI is tricky on large device buffer, sometimes 2000ms can be allocated sometimes
2202 less. There is no known guaranteed level thus we make subsequent tries by decreasing
2203 buffer by 100ms per try.
2204 */
2205 while ((hr == E_OUTOFMEMORY) && (pSub->period > (100 * 10000)))
2206 {
2207 PRINT(("WASAPI: CreateAudioClient: decreasing buffer size to %d milliseconds\n", (pSub->period / 10000)));
2208
2209 // Decrease by 100ms and try again
2210 pSub->period -= (100 * 10000);
2211
2212 // Recalculate aligned period
2213 framesPerLatency = MakeFramesFromHns(pSub->period, pSub->wavex.Format.nSamplesPerSec);
2214 _CalculateAlignedPeriod(pSub, &framesPerLatency, ALIGN_BWD);
2215
2216 // Release the previous allocations
2217 SAFE_RELEASE(audioClient);
2218
2219 // Create a new audio client
2220 hr = IMMDevice_Activate(pInfo->device, &pa_IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&audioClient);
2221 if (hr != S_OK)
2222 {
2223 LogHostError(hr);
2224 goto done;
2225 }
2226
2227 // Open the stream and associate it with an audio session
2228 hr = IAudioClient_Initialize(audioClient,
2229 pSub->shareMode,
2230 pSub->streamFlags,
2231 pSub->period,
2232 (pSub->shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE ? pSub->period : 0),
2233 &pSub->wavex.Format,
2234 NULL);
2235 }
2236
2237 /*! WASAPI buffer size failure. Fallback to using default size.
2238 */
2239 if (hr == AUDCLNT_E_BUFFER_SIZE_ERROR)
2240 {
2241 // Use default
2242 pSub->period = pInfo->DefaultDevicePeriod;
2243
2244 PRINT(("WASAPI: CreateAudioClient: correcting buffer size to device default\n"));
2245
2246 // Release the previous allocations
2247 SAFE_RELEASE(audioClient);
2248
2249 // Create a new audio client
2250 hr = IMMDevice_Activate(pInfo->device, &pa_IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&audioClient);
2251 if (hr != S_OK)
2252 {
2253 LogHostError(hr);
2254 goto done;
2255 }
2256
2257 // Open the stream and associate it with an audio session
2258 hr = IAudioClient_Initialize(audioClient,
2259 pSub->shareMode,
2260 pSub->streamFlags,
2261 pSub->period,
2262 (pSub->shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE ? pSub->period : 0),
2263 &pSub->wavex.Format,
2264 NULL);
2265 }
2266
2267 /*! If the requested buffer size is not aligned. Can be triggered by Windows 7 and up.
2268 Should not be be triggered ever as we do align buffers always with _CalculateAlignedPeriod.
2269 */
2270 if (hr == AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED)
2271 {
2272 UINT32 frames = 0;
2273
2274 // Get the next aligned frame
2275 hr = IAudioClient_GetBufferSize(audioClient, &frames);
2276 if (hr != S_OK)
2277 {
2278 LogHostError(hr);
2279 goto done;
2280 }
2281
2282 PRINT(("WASAPI: CreateAudioClient: aligning buffer size to % frames\n", frames));
2283
2284 // Release the previous allocations
2285 SAFE_RELEASE(audioClient);
2286
2287 // Create a new audio client
2288 hr = IMMDevice_Activate(pInfo->device, &pa_IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&audioClient);
2289 if (hr != S_OK)
2290 {
2291 LogHostError(hr);
2292 goto done;
2293 }
2294
2295 // Get closest format
2296 if ((error = GetClosestFormat(audioClient, sampleRate, params, pSub->shareMode, &pSub->wavex, output)) != paFormatIsSupported)
2297 {
2298 if (pa_error)
2299 (*pa_error) = error;
2300
2301 LogHostError(hr = AUDCLNT_E_UNSUPPORTED_FORMAT); // fail, format not supported
2302 goto done;
2303 }
2304
2305 // Check for Mono >> Stereo workaround
2306 if ((params->channelCount == 1) && (pSub->wavex.Format.nChannels == 2))
2307 {
2308 /*if (blocking)
2309 {
2310 LogHostError(hr = AUDCLNT_E_UNSUPPORTED_FORMAT);
2311 goto done; // fail, blocking mode not supported
2312 }*/
2313
2314 // Select mixer
2315 pSub->monoMixer = _GetMonoToStereoMixer(WaveToPaFormat(&pSub->wavex), (pInfo->flow == eRender ? MIX_DIR__1TO2 : MIX_DIR__2TO1_L));
2316 if (pSub->monoMixer == NULL)
2317 {
2318 LogHostError(hr = AUDCLNT_E_UNSUPPORTED_FORMAT);
2319 goto done; // fail, no mixer for format
2320 }
2321 }
2322
2323 // Calculate period
2324 pSub->period = MakeHnsPeriod(frames, pSub->wavex.Format.nSamplesPerSec);
2325
2326 // Open the stream and associate it with an audio session
2327 hr = IAudioClient_Initialize(audioClient,
2328 pSub->shareMode,
2329 pSub->streamFlags,
2330 pSub->period,
2331 (pSub->shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE ? pSub->period : 0),
2332 &pSub->wavex.Format,
2333 NULL);
2334 if (hr != S_OK)
2335 {
2336 LogHostError(hr);
2337 goto done;
2338 }
2339 }
2340 else
2341 if (hr != S_OK)
2342 {
2343 LogHostError(hr);
2344 goto done;
2345 }
2346
2347 // Set client
2348 pSub->client = audioClient;
2349 IAudioClient_AddRef(pSub->client);
2350
2351 // Recalculate buffers count
2352 _RecalculateBuffersCount(pSub,
2353 userFramesPerBuffer,
2354 MakeFramesFromHns(pSub->period, pSub->wavex.Format.nSamplesPerSec),
2355 fullDuplex);
2356
2357 done:
2358
2359 // Clean up
2360 SAFE_RELEASE(audioClient);
2361 return hr;
2362 }
2363
2364 // ------------------------------------------------------------------------------------------
2365 static PaError ActivateAudioClientOutput(PaWasapiStream *stream)
2366 {
2367 HRESULT hr;
2368 PaError result;
2369
2370 UINT32 maxBufferSize = 0;
2371 PaTime buffer_latency = 0;
2372 UINT32 framesPerBuffer = stream->out.params.frames_per_buffer;
2373
2374 // Create Audio client
2375 hr = CreateAudioClient(stream, &stream->out, TRUE, &result);
2376 if (hr != S_OK)
2377 {
2378 LogPaError(result = paInvalidDevice);
2379 goto error;
2380 }
2381 LogWAVEFORMATEXTENSIBLE(&stream->out.wavex);
2382
2383 // Activate volume
2384 stream->outVol = NULL;
2385 /*hr = info->device->Activate(
2386 __uuidof(IAudioEndpointVolume), CLSCTX_INPROC_SERVER, NULL,
2387 (void**)&stream->outVol);
2388 if (hr != S_OK)
2389 return paInvalidDevice;*/
2390
2391 // Get max possible buffer size to check if it is not less than that we request
2392 hr = IAudioClient_GetBufferSize(stream->out.client, &maxBufferSize);
2393 if (hr != S_OK)
2394 {
2395 LogHostError(hr);
2396 LogPaError(result = paInvalidDevice);
2397 goto error;
2398 }
2399
2400 // Correct buffer to max size if it maxed out result of GetBufferSize
2401 stream->out.bufferSize = maxBufferSize;
2402
2403 // Get interface latency (actually uneeded as we calculate latency from the size
2404 // of maxBufferSize).
2405 hr = IAudioClient_GetStreamLatency(stream->out.client, &stream->out.device_latency);
2406 if (hr != S_OK)
2407 {
2408 LogHostError(hr);
2409 LogPaError(result = paInvalidDevice);
2410 goto error;
2411 }
2412 //stream->out.latency_seconds = nano100ToSeconds(stream->out.device_latency);
2413
2414 // Number of frames that are required at each period
2415 stream->out.framesPerHostCallback = maxBufferSize;
2416
2417 // Calculate frames per single buffer, if buffers > 1 then always framesPerBuffer
2418 stream->out.framesPerBuffer =
2419 (stream->out.userBufferAndHostMatch ? stream->out.framesPerHostCallback : framesPerBuffer);
2420
2421 // Calculate buffer latency
2422 buffer_latency = (PaTime)maxBufferSize / stream->out.wavex.Format.nSamplesPerSec;
2423
2424 // Append buffer latency to interface latency in shared mode (see GetStreamLatency notes)
2425 stream->out.latency_seconds = buffer_latency;
2426
2427 PRINT(("WASAPI::OpenStream(output): framesPerUser[ %d ] framesPerHost[ %d ] latency[ %.02fms ] exclusive[ %s ] wow64_fix[ %s ] mode[ %s ]\n", (UINT32)framesPerBuffer, (UINT32)stream->out.framesPerHostCallback, (float)(stream->out.latency_seconds*1000.0f), (stream->out.shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE ? "YES" : "NO"), (stream->out.params.wow64_workaround ? "YES" : "NO"), (stream->out.streamFlags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK ? "EVENT" : "POLL")));
2428
2429 return paNoError;
2430
2431 error:
2432
2433 return result;
2434 }
2435
2436 // ------------------------------------------------------------------------------------------
2437 static PaError ActivateAudioClientInput(PaWasapiStream *stream)
2438 {
2439 HRESULT hr;
2440 PaError result;
2441
2442 UINT32 maxBufferSize = 0;
2443 PaTime buffer_latency = 0;
2444 UINT32 framesPerBuffer = stream->out.params.frames_per_buffer;
2445
2446 // Create Audio client
2447 hr = CreateAudioClient(stream, &stream->in, FALSE, &result);
2448 if (hr != S_OK)
2449 {
2450 LogPaError(result = paInvalidDevice);
2451 goto error;
2452 }
2453 LogWAVEFORMATEXTENSIBLE(&stream->in.wavex);
2454
2455 // Create volume mgr
2456 stream->inVol = NULL;
2457 /*hr = info->device->Activate(
2458 __uuidof(IAudioEndpointVolume), CLSCTX_INPROC_SERVER, NULL,
2459 (void**)&stream->inVol);
2460 if (hr != S_OK)
2461 return paInvalidDevice;*/
2462
2463 // Get max possible buffer size to check if it is not less than that we request
2464 hr = IAudioClient_GetBufferSize(stream->in.client, &maxBufferSize);
2465 if (hr != S_OK)
2466 {
2467 LogHostError(hr);
2468 LogPaError(result = paInvalidDevice);
2469 goto error;
2470 }
2471
2472 // Correct buffer to max size if it maxed out result of GetBufferSize
2473 stream->in.bufferSize = maxBufferSize;
2474
2475 // Get interface latency (actually uneeded as we calculate latency from the size
2476 // of maxBufferSize).
2477 hr = IAudioClient_GetStreamLatency(stream->in.client, &stream->in.device_latency);
2478 if (hr != S_OK)
2479 {
2480 LogHostError(hr);
2481 LogPaError(result = paInvalidDevice);
2482 goto error;
2483 }
2484 //stream->in.latency_seconds = nano100ToSeconds(stream->in.device_latency);
2485
2486 // Number of frames that are required at each period
2487 stream->in.framesPerHostCallback = maxBufferSize;
2488
2489 // Calculate frames per single buffer, if buffers > 1 then always framesPerBuffer
2490 stream->in.framesPerBuffer =
2491 (stream->in.userBufferAndHostMatch ? stream->in.framesPerHostCallback : framesPerBuffer);
2492
2493 // Calculate buffer latency
2494 buffer_latency = (PaTime)maxBufferSize / stream->in.wavex.Format.nSamplesPerSec;
2495
2496 // Append buffer latency to interface latency in shared mode (see GetStreamLatency notes)
2497 stream->in.latency_seconds = buffer_latency;
2498
2499 PRINT(("WASAPI::OpenStream(input): framesPerUser[ %d ] framesPerHost[ %d ] latency[ %.02fms ] exclusive[ %s ] wow64_fix[ %s ] mode[ %s ]\n", (UINT32)framesPerBuffer, (UINT32)stream->in.framesPerHostCallback, (float)(stream->in.latency_seconds*1000.0f), (stream->in.shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE ? "YES" : "NO"), (stream->in.params.wow64_workaround ? "YES" : "NO"), (stream->in.streamFlags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK ? "EVENT" : "POLL")));
2500
2501 return paNoError;
2502
2503 error:
2504
2505 return result;
2506 }
2507
2508 // ------------------------------------------------------------------------------------------
2509 static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
2510 PaStream** s,
2511 const PaStreamParameters *inputParameters,
2512 const PaStreamParameters *outputParameters,
2513 double sampleRate,
2514 unsigned long framesPerBuffer,
2515 PaStreamFlags streamFlags,
2516 PaStreamCallback *streamCallback,
2517 void *userData )
2518 {
2519 PaError result = paNoError;
2520 HRESULT hr;
2521 PaWasapiHostApiRepresentation *paWasapi = (PaWasapiHostApiRepresentation*)hostApi;
2522 PaWasapiStream *stream = NULL;
2523 int inputChannelCount, outputChannelCount;
2524 PaSampleFormat inputSampleFormat, outputSampleFormat;
2525 PaSampleFormat hostInputSampleFormat, hostOutputSampleFormat;
2526 PaWasapiStreamInfo *inputStreamInfo = NULL, *outputStreamInfo = NULL;
2527 PaWasapiDeviceInfo *info = NULL;
2528 ULONG framesPerHostCallback;
2529 PaUtilHostBufferSizeMode bufferMode;
2530 const BOOL fullDuplex = ((inputParameters != NULL) && (outputParameters != NULL));
2531
2532 // validate PaStreamParameters
2533 if ((result = IsStreamParamsValid(hostApi, inputParameters, outputParameters, sampleRate)) != paNoError)
2534 return LogPaError(result);
2535
2536 // Validate platform specific flags
2537 if ((streamFlags & paPlatformSpecificFlags) != 0)
2538 {
2539 LogPaError(result = paInvalidFlag); /* unexpected platform specific flag */
2540 goto error;
2541 }
2542
2543 // Allocate memory for PaWasapiStream
2544 if ((stream = (PaWasapiStream *)PaUtil_AllocateMemory(sizeof(PaWasapiStream))) == NULL)
2545 {
2546 LogPaError(result = paInsufficientMemory);
2547 goto error;
2548 }
2549
2550 // Default thread priority is Audio: for exclusive mode we will use Pro Audio.
2551 stream->nThreadPriority = eThreadPriorityAudio;
2552
2553 // Set default number of frames: paFramesPerBufferUnspecified
2554 if (framesPerBuffer == paFramesPerBufferUnspecified)
2555 {
2556 UINT32 framesPerBufferIn = 0, framesPerBufferOut = 0;
2557 if (inputParameters != NULL)
2558 {
2559 info = &paWasapi->devInfo[inputParameters->device];
2560 framesPerBufferIn = MakeFramesFromHns(info->DefaultDevicePeriod, (UINT32)sampleRate);
2561 }
2562 if (outputParameters != NULL)
2563 {
2564 info = &paWasapi->devInfo[outputParameters->device];
2565 framesPerBufferOut = MakeFramesFromHns(info->DefaultDevicePeriod, (UINT32)sampleRate);
2566 }
2567 // choosing maximum default size
2568 framesPerBuffer = max(framesPerBufferIn, framesPerBufferOut);
2569 }
2570 if (framesPerBuffer == 0)
2571 framesPerBuffer = ((UINT32)sampleRate / 100) * 2;
2572
2573 // Try create device: Input
2574 if (inputParameters != NULL)
2575 {
2576 inputChannelCount = inputParameters->channelCount;
2577 inputSampleFormat = inputParameters->sampleFormat;
2578 inputStreamInfo = (PaWasapiStreamInfo *)inputParameters->hostApiSpecificStreamInfo;
2579 info = &paWasapi->devInfo[inputParameters->device];
2580 stream->in.flags = (inputStreamInfo ? inputStreamInfo->flags : 0);
2581
2582 // Select Exclusive/Shared mode
2583 stream->in.shareMode = AUDCLNT_SHAREMODE_SHARED;
2584 if ((inputStreamInfo != NULL) && (inputStreamInfo->flags & paWinWasapiExclusive))
2585 {
2586 // Boost thread priority
2587 stream->nThreadPriority = eThreadPriorityProAudio;
2588 // Make Exclusive
2589 stream->in.shareMode = AUDCLNT_SHAREMODE_EXCLUSIVE;
2590 }
2591
2592 // If user provided explicit thread priority level, use it
2593 if ((inputStreamInfo != NULL) && (inputStreamInfo->flags & paWinWasapiThreadPriority))
2594 {
2595 if ((inputStreamInfo->threadPriority > eThreadPriorityNone) &&
2596 (inputStreamInfo->threadPriority <= eThreadPriorityWindowManager))
2597 stream->nThreadPriority = inputStreamInfo->threadPriority;
2598 }
2599
2600 // Choose processing mode
2601 stream->in.streamFlags = (stream->in.shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE ? AUDCLNT_STREAMFLAGS_EVENTCALLBACK : 0);
2602 if (paWasapi->useWOW64Workaround)
2603 stream->in.streamFlags = 0; // polling interface
2604 else
2605 if (streamCallback == NULL)
2606 stream->in.streamFlags = 0; // polling interface
2607 else
2608 if ((inputStreamInfo != NULL) && (inputStreamInfo->flags & paWinWasapiPolling))
2609 stream->in.streamFlags = 0; // polling interface
2610 else
2611 if (fullDuplex)
2612 stream->in.streamFlags = 0; // polling interface is implemented for full-duplex mode also
2613
2614 // Fill parameters for Audio Client creation
2615 stream->in.params.device_info = info;
2616 stream->in.params.stream_params = (*inputParameters);
2617 if (inputStreamInfo != NULL)
2618 {
2619 stream->in.params.wasapi_params = (*inputStreamInfo);
2620 stream->in.params.stream_params.hostApiSpecificStreamInfo = &stream->in.params.wasapi_params;
2621 }
2622 stream->in.params.frames_per_buffer = framesPerBuffer;
2623 stream->in.params.sample_rate = sampleRate;
2624 stream->in.params.blocking = (streamCallback == NULL);
2625 stream->in.params.full_duplex = fullDuplex;
2626 stream->in.params.wow64_workaround = paWasapi->useWOW64Workaround;
2627
2628 // Create and activate audio client
2629 hr = ActivateAudioClientInput(stream);
2630 if (hr != S_OK)
2631 {
2632 LogPaError(result = paInvalidDevice);
2633 goto error;
2634 }
2635
2636 // Get closest format
2637 hostInputSampleFormat = PaUtil_SelectClosestAvailableFormat( WaveToPaFormat(&stream->in.wavex), inputSampleFormat );
2638
2639 // Set user-side custom host processor
2640 if ((inputStreamInfo != NULL) &&
2641 (inputStreamInfo->flags & paWinWasapiRedirectHostProcessor))
2642 {
2643 stream->hostProcessOverrideInput.processor = inputStreamInfo->hostProcessorInput;
2644 stream->hostProcessOverrideInput.userData = userData;
2645 }
2646 }
2647 else
2648 {
2649 inputChannelCount = 0;
2650 inputSampleFormat = hostInputSampleFormat = paInt16; /* Surpress 'uninitialised var' warnings. */
2651 }
2652
2653 // Try create device: Output
2654 if (outputParameters != NULL)
2655 {
2656 outputChannelCount = outputParameters->channelCount;
2657 outputSampleFormat = outputParameters->sampleFormat;
2658 outputStreamInfo = (PaWasapiStreamInfo *)outputParameters->hostApiSpecificStreamInfo;
2659 info = &paWasapi->devInfo[outputParameters->device];
2660 stream->out.flags = (outputStreamInfo ? outputStreamInfo->flags : 0);
2661
2662 // Select Exclusive/Shared mode
2663 stream->out.shareMode = AUDCLNT_SHAREMODE_SHARED;
2664 if ((outputStreamInfo != NULL) && (outputStreamInfo->flags & paWinWasapiExclusive))
2665 {
2666 // Boost thread priority
2667 stream->nThreadPriority = eThreadPriorityProAudio;
2668 // Make Exclusive
2669 stream->out.shareMode = AUDCLNT_SHAREMODE_EXCLUSIVE;
2670 }
2671
2672 // If user provided explicit thread priority level, use it
2673 if ((outputStreamInfo != NULL) && (outputStreamInfo->flags & paWinWasapiThreadPriority))
2674 {
2675 if ((outputStreamInfo->threadPriority > eThreadPriorityNone) &&
2676 (outputStreamInfo->threadPriority <= eThreadPriorityWindowManager))
2677 stream->nThreadPriority = outputStreamInfo->threadPriority;
2678 }
2679
2680 // Choose processing mode
2681 stream->out.streamFlags = (stream->out.shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE ? AUDCLNT_STREAMFLAGS_EVENTCALLBACK : 0);
2682 if (paWasapi->useWOW64Workaround)
2683 stream->out.streamFlags = 0; // polling interface
2684 else
2685 if (streamCallback == NULL)
2686 stream->out.streamFlags = 0; // polling interface
2687 else
2688 if ((outputStreamInfo != NULL) && (outputStreamInfo->flags & paWinWasapiPolling))
2689 stream->out.streamFlags = 0; // polling interface
2690 else
2691 if (fullDuplex)
2692 stream->out.streamFlags = 0; // polling interface is implemented for full-duplex mode also
2693
2694 // Fill parameters for Audio Client creation
2695 stream->out.params.device_info = info;
2696 stream->out.params.stream_params = (*outputParameters);
2697 if (inputStreamInfo != NULL)
2698 {
2699 stream->out.params.wasapi_params = (*outputStreamInfo);
2700 stream->out.params.stream_params.hostApiSpecificStreamInfo = &stream->out.params.wasapi_params;
2701 }
2702 stream->out.params.frames_per_buffer = framesPerBuffer;
2703 stream->out.params.sample_rate = sampleRate;
2704 stream->out.params.blocking = (streamCallback == NULL);
2705 stream->out.params.full_duplex = fullDuplex;
2706 stream->out.params.wow64_workaround = paWasapi->useWOW64Workaround;
2707
2708 // Create and activate audio client
2709 hr = ActivateAudioClientOutput(stream);
2710 if (hr != S_OK)
2711 {
2712 LogPaError(result = paInvalidDevice);
2713 goto error;
2714 }
2715
2716 // Get closest format
2717 hostOutputSampleFormat = PaUtil_SelectClosestAvailableFormat( WaveToPaFormat(&stream->out.wavex), outputSampleFormat );
2718
2719 // Set user-side custom host processor
2720 if ((outputStreamInfo != NULL) &&
2721 (outputStreamInfo->flags & paWinWasapiRedirectHostProcessor))
2722 {
2723 stream->hostProcessOverrideOutput.processor = outputStreamInfo->hostProcessorOutput;
2724 stream->hostProcessOverrideOutput.userData = userData;
2725 }
2726 }
2727 else
2728 {
2729 outputChannelCount = 0;
2730 outputSampleFormat = hostOutputSampleFormat = paInt16; /* Surpress 'uninitialized var' warnings. */
2731 }
2732
2733 // log full-duplex
2734 if (fullDuplex)
2735 PRINT(("WASAPI::OpenStream: full-duplex mode\n"));
2736
2737 // paWinWasapiPolling must be on/or not on both streams
2738 if ((inputParameters != NULL) && (outputParameters != NULL))
2739 {
2740 if ((inputStreamInfo != NULL) && (outputStreamInfo != NULL))
2741 {
2742 if (((inputStreamInfo->flags & paWinWasapiPolling) &&
2743 !(outputStreamInfo->flags & paWinWasapiPolling))
2744 ||
2745 (!(inputStreamInfo->flags & paWinWasapiPolling) &&
2746 (outputStreamInfo->flags & paWinWasapiPolling)))
2747 {
2748 LogPaError(result = paInvalidFlag);
2749 goto error;
2750 }
2751 }
2752 }
2753
2754 // Initialize stream representation
2755 if (streamCallback)
2756 {
2757 stream->bBlocking = FALSE;
2758 PaUtil_InitializeStreamRepresentation(&stream->streamRepresentation,
2759 &paWasapi->callbackStreamInterface,
2760 streamCallback, userData);
2761 }
2762 else
2763 {
2764 stream->bBlocking = TRUE;
2765 PaUtil_InitializeStreamRepresentation(&stream->streamRepresentation,
2766 &paWasapi->blockingStreamInterface,
2767 streamCallback, userData);
2768 }
2769
2770 // Initialize CPU measurer
2771 PaUtil_InitializeCpuLoadMeasurer(&stream->cpuLoadMeasurer, sampleRate);
2772
2773 if (outputParameters && inputParameters)
2774 {
2775 // serious problem #1 - No, Not a problem, especially concerning Exclusive mode.
2776 // Input device in exclusive mode somehow is getting large buffer always, thus we
2777 // adjust Output latency to reflect it, thus period will differ but playback will be
2778 // normal.
2779 /*if (stream->in.period != stream->out.period)
2780 {
2781 PRINT(("WASAPI: OpenStream: period discrepancy\n"));
2782 LogPaError(result = paBadIODeviceCombination);
2783 goto error;
2784 }*/
2785
2786 // serious problem #2 - No, Not a problem, as framesPerHostCallback take into account
2787 // sample size while it is not a problem for PA full-duplex, we must care of
2788 // preriod only!
2789 /*if (stream->out.framesPerHostCallback != stream->in.framesPerHostCallback)
2790 {
2791 PRINT(("WASAPI: OpenStream: framesPerHostCallback discrepancy\n"));
2792 goto error;
2793 }*/
2794 }
2795
2796 // Calculate frames per host for processor
2797 framesPerHostCallback = (outputParameters ? stream->out.framesPerBuffer : stream->in.framesPerBuffer);
2798
2799 // Choose correct mode of buffer processing:
2800 // Exclusive/Shared non paWinWasapiPolling mode: paUtilFixedHostBufferSize - always fixed
2801 // Exclusive/Shared paWinWasapiPolling mode: paUtilBoundedHostBufferSize - may vary for Exclusive or Full-duplex
2802 bufferMode = paUtilFixedHostBufferSize;
2803 if (inputParameters) // !!! WASAPI IAudioCaptureClient::GetBuffer extracts not number of frames but 1 packet, thus we always must adapt
2804 bufferMode = paUtilBoundedHostBufferSize;
2805 else
2806 if (outputParameters)
2807 {
2808 if ((stream->out.buffers == 1) &&
2809 (!stream->out.streamFlags || ((stream->out.streamFlags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK) == 0)))
2810 bufferMode = paUtilBoundedHostBufferSize;
2811 }
2812 stream->bufferMode = bufferMode;
2813
2814 // Initialize buffer processor
2815 result = PaUtil_InitializeBufferProcessor(
2816 &stream->bufferProcessor,
2817 inputChannelCount,
2818 inputSampleFormat,
2819 hostInputSampleFormat,
2820 outputChannelCount,
2821 outputSampleFormat,
2822 hostOutputSampleFormat,
2823 sampleRate,
2824 streamFlags,
2825 framesPerBuffer,
2826 framesPerHostCallback,
2827 bufferMode,
2828 streamCallback,
2829 userData);
2830 if (result != paNoError)
2831 {
2832 LogPaError(result);
2833 goto error;
2834 }
2835
2836 // Set Input latency
2837 stream->streamRepresentation.streamInfo.inputLatency =
2838 ((double)PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor) / sampleRate)
2839 + ((inputParameters)?stream->in.latency_seconds : 0);
2840
2841 // Set Output latency
2842 stream->streamRepresentation.streamInfo.outputLatency =
2843 ((double)PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor) / sampleRate)
2844 + ((outputParameters)?stream->out.latency_seconds : 0);
2845
2846 // Set SR
2847 stream->streamRepresentation.streamInfo.sampleRate = sampleRate;
2848
2849 (*s) = (PaStream *)stream;
2850 return result;
2851
2852 error:
2853
2854 if (stream != NULL)
2855 CloseStream(stream);
2856
2857 return result;
2858 }
2859
2860 // ------------------------------------------------------------------------------------------
2861 static PaError CloseStream( PaStream* s )
2862 {
2863 PaError result = paNoError;
2864 PaWasapiStream *stream = (PaWasapiStream*)s;
2865
2866 // abort active stream
2867 if (IsStreamActive(s))
2868 {
2869 if ((result = AbortStream(s)) != paNoError)
2870 return result;
2871 }
2872
2873 SAFE_RELEASE(stream->cclient);
2874 SAFE_RELEASE(stream->rclient);
2875 SAFE_RELEASE(stream->out.client);
2876 SAFE_RELEASE(stream->in.client);
2877 SAFE_RELEASE(stream->inVol);
2878 SAFE_RELEASE(stream->outVol);
2879
2880 CloseHandle(stream->event[S_INPUT]);
2881 CloseHandle(stream->event[S_OUTPUT]);
2882
2883 _CleanupStream(stream);
2884
2885 free(stream->in.monoBuffer);
2886 free(stream->out.monoBuffer);
2887
2888 PaUtil_TerminateBufferProcessor(&stream->bufferProcessor);
2889 PaUtil_TerminateStreamRepresentation(&stream->streamRepresentation);
2890 PaUtil_FreeMemory(stream);
2891
2892 return result;
2893 }
2894
2895 // ------------------------------------------------------------------------------------------
2896 static PaError StartStream( PaStream *s )
2897 {
2898 HRESULT hr;
2899 PaWasapiStream *stream = (PaWasapiStream*)s;
2900
2901 // check if stream is active already
2902 if (IsStreamActive(s))
2903 return paStreamIsNotStopped;
2904
2905 PaUtil_ResetBufferProcessor(&stream->bufferProcessor);
2906
2907 // Cleanup handles (may be necessary if stream was stopped by itself due to error)
2908 _CleanupStream(stream);
2909
2910 // Create close event
2911 stream->hCloseRequest = CreateEvent(NULL, TRUE, FALSE, NULL);
2912
2913 // Create thread
2914 if (!stream->bBlocking)
2915 {
2916 // Create thread events
2917 stream->hThreadStart = CreateEvent(NULL, TRUE, FALSE, NULL);
2918 stream->hThreadExit = CreateEvent(NULL, TRUE, FALSE, NULL);
2919
2920 if ((stream->in.client && (stream->in.streamFlags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK)) ||
2921 (stream->out.client && (stream->out.streamFlags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK)))
2922 {
2923 if ((stream->hThread = CREATE_THREAD(ProcThreadEvent)) == NULL)
2924 return paUnanticipatedHostError;
2925 }
2926 else
2927 {
2928 if ((stream->hThread = CREATE_THREAD(ProcThreadPoll)) == NULL)
2929 return paUnanticipatedHostError;
2930 }
2931
2932 // Wait for thread to start
2933 if (WaitForSingleObject(stream->hThreadStart, 60*1000) == WAIT_TIMEOUT)
2934 return paUnanticipatedHostError;
2935 }
2936 else
2937 {
2938 // Create blocking operation events (non-signaled event means - blocking operation is pending)
2939 if (stream->out.client)
2940 stream->hBlockingOpStreamWR = CreateEvent(NULL, TRUE, TRUE, NULL);
2941 if (stream->in.client)
2942 stream->hBlockingOpStreamRD = CreateEvent(NULL, TRUE, TRUE, NULL);
2943
2944 // Initialize event & start INPUT stream
2945 if (stream->in.client)
2946 {
2947 if ((hr = IAudioClient_GetService(stream->in.client, &pa_IID_IAudioCaptureClient, (void **)&stream->cclient)) != S_OK)
2948 {
2949 LogHostError(hr);
2950 return paUnanticipatedHostError;
2951 }
2952
2953 if ((hr = IAudioClient_Start(stream->in.client)) != S_OK)
2954 {
2955 LogHostError(hr);
2956 return paUnanticipatedHostError;
2957 }
2958 }
2959
2960
2961 // Initialize event & start OUTPUT stream
2962 if (stream->out.client)
2963 {
2964 if ((hr = IAudioClient_GetService(stream->out.client, &pa_IID_IAudioRenderClient, (void **)&stream->rclient)) != S_OK)
2965 {
2966 LogHostError(hr);
2967 return paUnanticipatedHostError;
2968 }
2969
2970 // Start
2971 if ((hr = IAudioClient_Start(stream->out.client)) != S_OK)
2972 {
2973 LogHostError(hr);
2974 return paUnanticipatedHostError;
2975 }
2976 }
2977
2978 // Signal: stream running
2979 stream->running = TRUE;
2980
2981 // Set current time
2982 stream->out.prevTime = timeGetTime();
2983 stream->out.prevSleep = 0;
2984 }
2985
2986 return paNoError;
2987 }
2988
2989 // ------------------------------------------------------------------------------------------
2990 static void _FinishStream(PaWasapiStream *stream)
2991 {
2992 // Issue command to thread to stop processing and wait for thread exit
2993 if (!stream->bBlocking)
2994 {
2995 SignalObjectAndWait(stream->hCloseRequest, stream->hThreadExit, INFINITE, FALSE);
2996 }
2997 else
2998 // Blocking mode does not own thread
2999 {
3000 // Signal close event and wait for each of 2 blocking operations to complete
3001 if (stream->out.client)
3002 SignalObjectAndWait(stream->hCloseRequest, stream->hBlockingOpStreamWR, INFINITE, TRUE);
3003 if (stream->out.client)
3004 SignalObjectAndWait(stream->hCloseRequest, stream->hBlockingOpStreamRD, INFINITE, TRUE);
3005
3006 // Process stop
3007 _OnStreamStop(stream);
3008 }
3009
3010 // Cleanup handles
3011 _CleanupStream(stream);
3012
3013 stream->running = FALSE;
3014 }
3015
3016 // ------------------------------------------------------------------------------------------
3017 static void _CleanupStream(PaWasapiStream *stream)
3018 {
3019 // Close thread handles to allow restart
3020 SAFE_CLOSE(stream->hThread);
3021 SAFE_CLOSE(stream->hThreadStart);
3022 SAFE_CLOSE(stream->hThreadExit);
3023 SAFE_CLOSE(stream->hCloseRequest);
3024 SAFE_CLOSE(stream->hBlockingOpStreamRD);
3025 SAFE_CLOSE(stream->hBlockingOpStreamWR);
3026 }
3027
3028 // ------------------------------------------------------------------------------------------
3029 static PaError StopStream( PaStream *s )
3030 {
3031 // Finish stream
3032 _FinishStream((PaWasapiStream *)s);
3033 return paNoError;
3034 }
3035
3036 // ------------------------------------------------------------------------------------------
3037 static PaError AbortStream( PaStream *s )
3038 {
3039 // Finish stream
3040 _FinishStream((PaWasapiStream *)s);
3041 return paNoError;
3042 }
3043
3044 // ------------------------------------------------------------------------------------------
3045 static PaError IsStreamStopped( PaStream *s )
3046 {
3047 return !((PaWasapiStream *)s)->running;
3048 }
3049
3050 // ------------------------------------------------------------------------------------------
3051 static PaError IsStreamActive( PaStream *s )
3052 {
3053 return ((PaWasapiStream *)s)->running;
3054 }
3055
3056 // ------------------------------------------------------------------------------------------
3057 static PaTime GetStreamTime( PaStream *s )
3058 {
3059 PaWasapiStream *stream = (PaWasapiStream*)s;
3060
3061 /* suppress unused variable warnings */
3062 (void) stream;
3063
3064 /* IMPLEMENT ME, see portaudio.h for required behavior*/
3065
3066 //this is lame ds and mme does the same thing, quite useless method imho
3067 //why dont we fetch the time in the pa callbacks?
3068 //at least its doing to be clocked to something
3069 return PaUtil_GetTime();
3070 }
3071
3072 // ------------------------------------------------------------------------------------------
3073 static double GetStreamCpuLoad( PaStream* s )
3074 {
3075 return PaUtil_GetCpuLoad(&((PaWasapiStream *)s)->cpuLoadMeasurer);
3076 }
3077
3078 // ------------------------------------------------------------------------------------------
3079 /* NOT TESTED */
3080 static PaError ReadStream( PaStream* s, void *_buffer, unsigned long _frames )
3081 {
3082 PaWasapiStream *stream = (PaWasapiStream*)s;
3083
3084 HRESULT hr = S_OK;
3085 UINT32 frames;
3086 BYTE *user_buffer = (BYTE *)_buffer;
3087 BYTE *wasapi_buffer = NULL;
3088 DWORD flags = 0;
3089 UINT32 i;
3090
3091 // validate
3092 if (!stream->running)
3093 return paStreamIsStopped;
3094 if (stream->cclient == NULL)
3095 return paBadStreamPtr;
3096
3097 // Notify blocking op has begun
3098 ResetEvent(stream->hBlockingOpStreamRD);
3099
3100 // make a local copy of the user buffer pointer(s), this is necessary
3101 // because PaUtil_CopyOutput() advances these pointers every time it is called
3102 if (!stream->bufferProcessor.userInputIsInterleaved)
3103 {
3104 user_buffer = (BYTE *)alloca(sizeof(BYTE *) * stream->bufferProcessor.inputChannelCount);
3105 if (user_buffer == NULL)
3106 return paInsufficientMemory;
3107
3108 for (i = 0; i < stream->bufferProcessor.inputChannelCount; ++i)
3109 ((BYTE **)user_buffer)[i] = ((BYTE **)_buffer)[i];
3110 }
3111
3112 while (_frames != 0)
3113 {
3114 UINT32 processed, processed_size;
3115
3116 // Get the available data in the shared buffer.
3117 if ((hr = IAudioCaptureClient_GetBuffer(stream->cclient, &wasapi_buffer, &frames, &flags, NULL, NULL)) != S_OK)
3118 {
3119 if (hr == AUDCLNT_S_BUFFER_EMPTY)
3120 {
3121 // Check if blocking call must be interrupted
3122 if (WaitForSingleObject(stream->hCloseRequest, 1) != WAIT_TIMEOUT)
3123 break;
3124 }
3125
3126 return LogHostError(hr);
3127 goto stream_rd_end;
3128 }
3129
3130 // Detect silence
3131 // if (flags & AUDCLNT_BUFFERFLAGS_SILENT)
3132 // data = NULL;
3133
3134 // Check if frames <= _frames
3135 if (frames > _frames)
3136 frames = _frames;
3137
3138 // Register available frames to processor
3139 PaUtil_SetInputFrameCount(&stream->bufferProcessor, frames);
3140
3141 // Register host buffer pointer to processor
3142 PaUtil_SetInterleavedInputChannels(&stream->bufferProcessor, 0, wasapi_buffer, stream->bufferProcessor.inputChannelCount);
3143
3144 // Copy user data to host buffer (with conversion if applicable)
3145 processed = PaUtil_CopyInput(&stream->bufferProcessor, (void **)&user_buffer, frames);
3146
3147 // Advance user buffer to consumed portion
3148 processed_size = processed * stream->in.wavex.Format.nBlockAlign;
3149 if (stream->bufferProcessor.userInputIsInterleaved)
3150 {
3151 user_buffer += processed_size;
3152 }
3153 else
3154 {
3155 for (i = 0; i < stream->bufferProcessor.inputChannelCount; ++i)
3156 ((BYTE **)user_buffer)[i] = ((BYTE **)user_buffer)[i] + processed_size;
3157 }
3158
3159 // Release host buffer
3160 if ((hr = IAudioCaptureClient_ReleaseBuffer(stream->cclient, processed)) != S_OK)
3161 {
3162 LogHostError(hr);
3163 goto stream_rd_end;
3164 }
3165
3166 _frames -= processed;
3167 }
3168
3169 stream_rd_end:
3170
3171 // Notify blocking op has ended
3172 SetEvent(stream->hBlockingOpStreamRD);
3173
3174 return (hr != S_OK ? paUnanticipatedHostError : paNoError);
3175 }
3176
3177 // ------------------------------------------------------------------------------------------
3178 static PaError WriteStream( PaStream* s, const void *_buffer, unsigned long _frames )
3179 {
3180 PaWasapiStream *stream = (PaWasapiStream*)s;
3181
3182 UINT32 frames;
3183 const BYTE *user_buffer = (const BYTE *)_buffer;
3184 BYTE *wasapi_buffer;
3185 HRESULT hr = S_OK;
3186 UINT32 next_rev_sleep, blocks, block_sleep_ms;
3187 UINT32 i;
3188
3189 // validate
3190 if (!stream->running)
3191 return paStreamIsStopped;
3192 if (stream->rclient == NULL)
3193 return paBadStreamPtr;
3194
3195 // Notify blocking op has begun
3196 ResetEvent(stream->hBlockingOpStreamWR);
3197
3198 // Calculate sleep time for next call
3199 {
3200 UINT32 remainder = 0;
3201 UINT32 sleep_ms = 0;
3202 DWORD elapsed_ms;
3203 blocks = _frames / stream->out.framesPerHostCallback;
3204 block_sleep_ms = GetFramesSleepTime(stream->out.framesPerHostCallback, stream->out.wavex.Format.nSamplesPerSec);
3205 if (blocks == 0)
3206 {
3207 blocks = 1;
3208 sleep_ms = GetFramesSleepTime(_frames, stream->out.wavex.Format.nSamplesPerSec); // partial
3209 }
3210 else
3211 {
3212 remainder = _frames - blocks * stream->out.framesPerHostCallback;
3213 sleep_ms = block_sleep_ms; // full
3214 }
3215
3216 // Sleep for remainder
3217 elapsed_ms = timeGetTime() - stream->out.prevTime;
3218 if (sleep_ms >= elapsed_ms)
3219 sleep_ms -= elapsed_ms;
3220
3221 next_rev_sleep = sleep_ms;
3222 }
3223
3224 // Sleep diff from last call
3225 if (stream->out.prevSleep)
3226 Sleep(stream->out.prevSleep);
3227 stream->out.prevSleep = next_rev_sleep;
3228
3229 // make a local copy of the user buffer pointer(s), this is necessary
3230 // because PaUtil_CopyOutput() advances these pointers every time it is called
3231 if (!stream->bufferProcessor.userOutputIsInterleaved)
3232 {
3233 user_buffer = (const BYTE *)alloca(sizeof(const BYTE *) * stream->bufferProcessor.outputChannelCount);
3234 if (user_buffer == NULL)
3235 return paInsufficientMemory;
3236
3237 for (i = 0; i < stream->bufferProcessor.outputChannelCount; ++i)
3238 ((const BYTE **)user_buffer)[i] = ((const BYTE **)_buffer)[i];
3239 }
3240
3241 // Feed engine
3242 for (i = 0; i < blocks; ++i)
3243 {
3244 UINT32 available, processed;
3245
3246 // Get block frames
3247 frames = stream->out.framesPerHostCallback;
3248 if (frames > _frames)
3249 frames = _frames;
3250
3251 if (i)
3252 Sleep(block_sleep_ms);
3253
3254 while (frames != 0)
3255 {
3256 UINT32 padding = 0;
3257 UINT32 processed_size;
3258
3259 // Check if blocking call must be interrupted
3260 if (WaitForSingleObject(stream->hCloseRequest, 0) != WAIT_TIMEOUT)
3261 break;
3262
3263 // Get Read position
3264 hr = IAudioClient_GetCurrentPadding(stream->out.client, &padding);
3265 if (hr != S_OK)
3266 {
3267 LogHostError(hr);
3268 goto stream_wr_end;
3269 }
3270
3271 // Calculate frames available
3272 if (frames >= padding)
3273 available = frames - padding;
3274 else
3275 available = frames;
3276
3277 // Get pointer to host buffer
3278 if ((hr = IAudioRenderClient_GetBuffer(stream->rclient, available, &wasapi_buffer)) != S_OK)
3279 {
3280 // Buffer size is too big, waiting
3281 if (hr == AUDCLNT_E_BUFFER_TOO_LARGE)
3282 continue;
3283 LogHostError(hr);
3284 goto stream_wr_end;
3285 }
3286
3287 // Register available frames to processor
3288 PaUtil_SetOutputFrameCount(&stream->bufferProcessor, available);
3289
3290 // Register host buffer pointer to processor
3291 PaUtil_SetInterleavedOutputChannels(&stream->bufferProcessor, 0, wasapi_buffer, stream->bufferProcessor.outputChannelCount);
3292
3293 // Copy user data to host buffer (with conversion if applicable)
3294 processed = PaUtil_CopyOutput(&stream->bufferProcessor, (const void **)&user_buffer, available);
3295
3296 // Advance user buffer to consumed portion
3297 processed_size = processed * stream->out.wavex.Format.nBlockAlign;
3298 if (stream->bufferProcessor.userOutputIsInterleaved)
3299 {
3300 user_buffer += processed_size;
3301 }
3302 else
3303 {
3304 for (i = 0; i < stream->bufferProcessor.outputChannelCount; ++i)
3305 ((const BYTE **)user_buffer)[i] = ((const BYTE **)user_buffer)[i] + processed_size;
3306 }
3307
3308 // Release host buffer
3309 if ((hr = IAudioRenderClient_ReleaseBuffer(stream->rclient, processed, 0)) != S_OK)
3310 {
3311 LogHostError(hr);
3312 goto stream_wr_end;
3313 }
3314
3315 // Deduct frames
3316 frames -= processed;
3317 }
3318
3319 _frames -= frames;
3320 }
3321
3322 stream_wr_end:
3323
3324 // Set prev time
3325 stream->out.prevTime = timeGetTime();
3326
3327 // Notify blocking op has ended
3328 SetEvent(stream->hBlockingOpStreamWR);
3329
3330 return (hr != S_OK ? paUnanticipatedHostError : paNoError);
3331 }
3332
3333 // ------------------------------------------------------------------------------------------
3334 /* NOT TESTED */
3335 static signed long GetStreamReadAvailable( PaStream* s )
3336 {
3337 PaWasapiStream *stream = (PaWasapiStream*)s;
3338 HRESULT hr;
3339 UINT32 pending = 0;
3340
3341 // validate
3342 if (!stream->running)
3343 return paStreamIsStopped;
3344 if (stream->cclient == NULL)
3345 return paBadStreamPtr;
3346
3347 hr = IAudioClient_GetCurrentPadding(stream->in.client, &pending);
3348 if (hr != S_OK)
3349 {
3350 LogHostError(hr);
3351 return paUnanticipatedHostError;
3352 }
3353
3354 return (long)pending;
3355 }
3356
3357 // ------------------------------------------------------------------------------------------
3358 static signed long GetStreamWriteAvailable( PaStream* s )
3359 {
3360 PaWasapiStream *stream = (PaWasapiStream*)s;
3361
3362 UINT32 frames = stream->out.framesPerHostCallback;
3363 HRESULT hr;
3364 UINT32 padding = 0;
3365
3366 // validate
3367 if (!stream->running)
3368 return paStreamIsStopped;
3369 if (stream->rclient == NULL)
3370 return paBadStreamPtr;
3371
3372 hr = IAudioClient_GetCurrentPadding(stream->out.client, &padding);
3373 if (hr != S_OK)
3374 {
3375 LogHostError(hr);
3376 return paUnanticipatedHostError;
3377 }
3378
3379 // Calculate
3380 frames -= padding;
3381
3382 return frames;
3383 }
3384
3385 // ------------------------------------------------------------------------------------------
3386 static void WaspiHostProcessingLoop( void *inputBuffer, long inputFrames,
3387 void *outputBuffer, long outputFrames,
3388 void *userData )
3389 {
3390 PaWasapiStream *stream = (PaWasapiStream*)userData;
3391 PaStreamCallbackTimeInfo timeInfo = {0,0,0};
3392 PaStreamCallbackFlags flags = 0;
3393 int callbackResult;
3394 unsigned long framesProcessed;
3395 HRESULT hr;
3396 UINT32 pending;
3397
3398 PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer );
3399
3400 /*
3401 Pa_GetStreamTime:
3402 - generate timing information
3403 - handle buffer slips
3404 */
3405 timeInfo.currentTime = PaUtil_GetTime();
3406 // Query input latency
3407 if (stream->in.client != NULL)
3408 {
3409 PaTime pending_time;
3410 if ((hr = IAudioClient_GetCurrentPadding(stream->in.client, &pending)) == S_OK)
3411 pending_time = (PaTime)pending / (PaTime)stream->in.wavex.Format.nSamplesPerSec;
3412 else
3413 pending_time = (PaTime)stream->in.latency_seconds;
3414
3415 timeInfo.inputBufferAdcTime = timeInfo.currentTime + pending_time;
3416 }
3417 // Query output current latency
3418 if (stream->out.client != NULL)
3419 {
3420 PaTime pending_time;
3421 if ((hr = IAudioClient_GetCurrentPadding(stream->out.client, &pending)) == S_OK)
3422 pending_time = (PaTime)pending / (PaTime)stream->out.wavex.Format.nSamplesPerSec;
3423 else
3424 pending_time = (PaTime)stream->out.latency_seconds;
3425
3426 timeInfo.outputBufferDacTime = timeInfo.currentTime + pending_time;
3427 }
3428
3429 /*
3430 If you need to byte swap or shift inputBuffer to convert it into a
3431 portaudio format, do it here.
3432 */
3433
3434 PaUtil_BeginBufferProcessing( &stream->bufferProcessor, &timeInfo, flags );
3435
3436 /*
3437 depending on whether the host buffers are interleaved, non-interleaved
3438 or a mixture, you will want to call PaUtil_SetInterleaved*Channels(),
3439 PaUtil_SetNonInterleaved*Channel() or PaUtil_Set*Channel() here.
3440 */
3441
3442 if (stream->bufferProcessor.inputChannelCount > 0)
3443 {
3444 PaUtil_SetInputFrameCount( &stream->bufferProcessor, inputFrames );
3445 PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor,
3446 0, /* first channel of inputBuffer is channel 0 */
3447 inputBuffer,
3448 0 ); /* 0 - use inputChannelCount passed to init buffer processor */
3449 }
3450
3451 if (stream->bufferProcessor.outputChannelCount > 0)
3452 {
3453 PaUtil_SetOutputFrameCount( &stream->bufferProcessor, outputFrames);
3454 PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor,
3455 0, /* first channel of outputBuffer is channel 0 */
3456 outputBuffer,
3457 0 ); /* 0 - use outputChannelCount passed to init buffer processor */
3458 }
3459
3460 /* you must pass a valid value of callback result to PaUtil_EndBufferProcessing()
3461 in general you would pass paContinue for normal operation, and
3462 paComplete to drain the buffer processor's internal output buffer.
3463 You can check whether the buffer processor's output buffer is empty
3464 using PaUtil_IsBufferProcessorOuputEmpty( bufferProcessor )
3465 */
3466 callbackResult = paContinue;
3467 framesProcessed = PaUtil_EndBufferProcessing( &stream->bufferProcessor, &callbackResult );
3468
3469 /*
3470 If you need to byte swap or shift outputBuffer to convert it to
3471 host format, do it here.
3472 */
3473
3474 PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesProcessed );
3475
3476 if (callbackResult == paContinue)
3477 {
3478 /* nothing special to do */
3479 }
3480 else
3481 if (callbackResult == paAbort)
3482 {
3483 // stop stream
3484 SetEvent(stream->hCloseRequest);
3485 }
3486 else
3487 {
3488 // stop stream
3489 SetEvent(stream->hCloseRequest);
3490 }
3491 }
3492
3493 // ------------------------------------------------------------------------------------------
3494 HANDLE MMCSS_activate(const char *name)
3495 {
3496 DWORD task_idx = 0;
3497 HANDLE hTask = pAvSetMmThreadCharacteristics(name, &task_idx);
3498 if (hTask == NULL)
3499 {
3500 PRINT(("WASAPI: AvSetMmThreadCharacteristics failed!\n"));
3501 }
3502
3503 /*BOOL priority_ok = pAvSetMmThreadPriority(hTask, AVRT_PRIORITY_NORMAL);
3504 if (priority_ok == FALSE)
3505 {
3506 PRINT(("WASAPI: AvSetMmThreadPriority failed!\n"));
3507 }*/
3508
3509 // debug
3510 {
3511 int cur_priority = GetThreadPriority(GetCurrentThread());
3512 DWORD cur_priority_class = GetPriorityClass(GetCurrentProcess());
3513 PRINT(("WASAPI: thread[ priority-0x%X class-0x%X ]\n", cur_priority, cur_priority_class));
3514 }
3515
3516 return hTask;
3517 }
3518
3519 // ------------------------------------------------------------------------------------------
3520 void MMCSS_deactivate(HANDLE hTask)
3521 {
3522 if (!hTask)
3523 return;
3524
3525 if (pAvRevertMmThreadCharacteristics(hTask) == FALSE)
3526 {
3527 PRINT(("WASAPI: AvRevertMmThreadCharacteristics failed!\n"));
3528 }
3529 }
3530
3531 // ------------------------------------------------------------------------------------------
3532 PaError PaWasapi_ThreadPriorityBoost(void **hTask, PaWasapiThreadPriority nPriorityClass)
3533 {
3534 static const char *mmcs_name[] =
3535 {
3536 NULL,
3537 "Audio",
3538 "Capture",
3539 "Distribution",
3540 "Games",
3541 "Playback",
3542 "Pro Audio",
3543 "Window Manager"
3544 };
3545 HANDLE task;
3546
3547 if (hTask == NULL)
3548 return paUnanticipatedHostError;
3549
3550 if ((UINT32)nPriorityClass >= STATIC_ARRAY_SIZE(mmcs_name))
3551 return paUnanticipatedHostError;
3552
3553 task = MMCSS_activate(mmcs_name[nPriorityClass]);
3554 if (task == NULL)
3555 return paUnanticipatedHostError;
3556
3557 (*hTask) = task;
3558 return paNoError;
3559 }
3560
3561 // ------------------------------------------------------------------------------------------
3562 PaError PaWasapi_ThreadPriorityRevert(void *hTask)
3563 {
3564 if (hTask == NULL)
3565 return paUnanticipatedHostError;
3566
3567 MMCSS_deactivate((HANDLE)hTask);
3568
3569 return paNoError;
3570 }
3571
3572 // ------------------------------------------------------------------------------------------
3573 // Described at:
3574 // http://msdn.microsoft.com/en-us/library/dd371387(v=VS.85).aspx
3575
3576 PaError PaWasapi_GetJackCount(PaDeviceIndex nDevice, int *jcount)
3577 {
3578 PaError ret;
3579 HRESULT hr = S_OK;
3580 PaDeviceIndex index;
3581 IDeviceTopology *pDeviceTopology = NULL;
3582 IConnector *pConnFrom = NULL;
3583 IConnector *pConnTo = NULL;
3584 IPart *pPart = NULL;
3585 IKsJackDescription *pJackDesc = NULL;
3586 UINT jackCount = 0;
3587
3588 PaWasapiHostApiRepresentation *paWasapi = _GetHostApi(&ret);
3589 if (paWasapi == NULL)
3590 return paNotInitialized;
3591
3592 // Get device index.
3593 ret = PaUtil_DeviceIndexToHostApiDeviceIndex(&index, nDevice, &paWasapi->inheritedHostApiRep);
3594 if (ret != paNoError)
3595 return ret;
3596
3597 // Validate index.
3598 if ((UINT32)index >= paWasapi->deviceCount)
3599 return paInvalidDevice;
3600
3601 // Get the endpoint device's IDeviceTopology interface.
3602 hr = IMMDevice_Activate(paWasapi->devInfo[index].device, &pa_IID_IDeviceTopology,
3603 CLSCTX_INPROC_SERVER, NULL, (void**)&pDeviceTopology);
3604 IF_FAILED_JUMP(hr, error);
3605
3606 // The device topology for an endpoint device always contains just one connector (connector number 0).
3607 hr = IDeviceTopology_GetConnector(pDeviceTopology, 0, &pConnFrom);
3608 IF_FAILED_JUMP(hr, error);
3609
3610 // Step across the connection to the jack on the adapter.
3611 hr = IConnector_GetConnectedTo(pConnFrom, &pConnTo);
3612 if (HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == hr)
3613 {
3614 // The adapter device is not currently active.
3615 hr = E_NOINTERFACE;
3616 }
3617 IF_FAILED_JUMP(hr, error);
3618
3619 // Get the connector's IPart interface.
3620 hr = IConnector_QueryInterface(pConnTo, &pa_IID_IPart, (void**)&pPart);
3621 IF_FAILED_JUMP(hr, error);
3622
3623 // Activate the connector's IKsJackDescription interface.
3624 hr = IPart_Activate(pPart, CLSCTX_INPROC_SERVER, &pa_IID_IKsJackDescription, (void**)&pJackDesc);
3625 IF_FAILED_JUMP(hr, error);
3626
3627 // Return jack count for this device.
3628 hr = IKsJackDescription_GetJackCount(pJackDesc, &jackCount);
3629 IF_FAILED_JUMP(hr, error);
3630
3631 // Set.
3632 (*jcount) = jackCount;
3633
3634 // Ok.
3635 ret = paNoError;
3636
3637 error:
3638
3639 SAFE_RELEASE(pDeviceTopology);
3640 SAFE_RELEASE(pConnFrom);
3641 SAFE_RELEASE(pConnTo);
3642 SAFE_RELEASE(pPart);
3643 SAFE_RELEASE(pJackDesc);
3644
3645 LogHostError(hr);
3646 return paNoError;
3647 }
3648
3649 // ------------------------------------------------------------------------------------------
3650 static PaWasapiJackConnectionType ConvertJackConnectionTypeWASAPIToPA(int connType)
3651 {
3652 switch (connType)
3653 {
3654 case eConnTypeUnknown: return eJackConnTypeUnknown;
3655 #ifdef _KS_
3656 case eConnType3Point5mm: return eJackConnType3Point5mm;
3657 #else
3658 case eConnTypeEighth: return eJackConnType3Point5mm;
3659 #endif
3660 case eConnTypeQuarter: return eJackConnTypeQuarter;
3661 case eConnTypeAtapiInternal: return eJackConnTypeAtapiInternal;
3662 case eConnTypeRCA: return eJackConnTypeRCA;
3663 case eConnTypeOptical: return eJackConnTypeOptical;
3664 case eConnTypeOtherDigital: return eJackConnTypeOtherDigital;
3665 case eConnTypeOtherAnalog: return eJackConnTypeOtherAnalog;
3666 case eConnTypeMultichannelAnalogDIN: return eJackConnTypeMultichannelAnalogDIN;
3667 case eConnTypeXlrProfessional: return eJackConnTypeXlrProfessional;
3668 case eConnTypeRJ11Modem: return eJackConnTypeRJ11Modem;
3669 case eConnTypeCombination: return eJackConnTypeCombination;
3670 }
3671 return eJackConnTypeUnknown;
3672 }
3673
3674 // ------------------------------------------------------------------------------------------
3675 static PaWasapiJackGeoLocation ConvertJackGeoLocationWASAPIToPA(int geoLoc)
3676 {
3677 switch (geoLoc)
3678 {
3679 case eGeoLocRear: return eJackGeoLocRear;
3680 case eGeoLocFront: return eJackGeoLocFront;
3681 case eGeoLocLeft: return eJackGeoLocLeft;
3682 case eGeoLocRight: return eJackGeoLocRight;
3683 case eGeoLocTop: return eJackGeoLocTop;
3684 case eGeoLocBottom: return eJackGeoLocBottom;
3685 #ifdef _KS_
3686 case eGeoLocRearPanel: return eJackGeoLocRearPanel;
3687 #else
3688 case eGeoLocRearOPanel: return eJackGeoLocRearPanel;
3689 #endif
3690 case eGeoLocRiser: return eJackGeoLocRiser;
3691 case eGeoLocInsideMobileLid: return eJackGeoLocInsideMobileLid;
3692 case eGeoLocDrivebay: return eJackGeoLocDrivebay;
3693 case eGeoLocHDMI: return eJackGeoLocHDMI;
3694 case eGeoLocOutsideMobileLid: return eJackGeoLocOutsideMobileLid;
3695 case eGeoLocATAPI: return eJackGeoLocATAPI;
3696 }
3697 return eJackGeoLocUnk;
3698 }
3699
3700 // ------------------------------------------------------------------------------------------
3701 static PaWasapiJackGenLocation ConvertJackGenLocationWASAPIToPA(int genLoc)
3702 {
3703 switch (genLoc)
3704 {
3705 case eGenLocPrimaryBox: return eJackGenLocPrimaryBox;
3706 case eGenLocInternal: return eJackGenLocInternal;
3707 #ifdef _KS_
3708 case eGenLocSeparate: return eJackGenLocSeparate;
3709 #else
3710 case eGenLocSeperate: return eJackGenLocSeparate;
3711 #endif
3712 case eGenLocOther: return eJackGenLocOther;
3713 }
3714 return eJackGenLocPrimaryBox;
3715 }
3716
3717 // ------------------------------------------------------------------------------------------
3718 static PaWasapiJackPortConnection ConvertJackPortConnectionWASAPIToPA(int portConn)
3719 {
3720 switch (portConn)
3721 {
3722 case ePortConnJack: return eJackPortConnJack;
3723 case ePortConnIntegratedDevice: return eJackPortConnIntegratedDevice;
3724 case ePortConnBothIntegratedAndJack:return eJackPortConnBothIntegratedAndJack;
3725 case ePortConnUnknown: return eJackPortConnUnknown;
3726 }
3727 return eJackPortConnJack;
3728 }
3729
3730 // ------------------------------------------------------------------------------------------
3731 // Described at:
3732 // http://msdn.microsoft.com/en-us/library/dd371387(v=VS.85).aspx
3733
3734 PaError PaWasapi_GetJackDescription(PaDeviceIndex nDevice, int jindex, PaWasapiJackDescription *pJackDescription)
3735 {
3736 PaError ret;
3737 HRESULT hr = S_OK;
3738 PaDeviceIndex index;
3739 IDeviceTopology *pDeviceTopology = NULL;
3740 IConnector *pConnFrom = NULL;
3741 IConnector *pConnTo = NULL;
3742 IPart *pPart = NULL;
3743 IKsJackDescription *pJackDesc = NULL;
3744 KSJACK_DESCRIPTION jack = { 0 };
3745
3746 PaWasapiHostApiRepresentation *paWasapi = _GetHostApi(&ret);
3747 if (paWasapi == NULL)
3748 return paNotInitialized;
3749
3750 // Get device index.
3751 ret = PaUtil_DeviceIndexToHostApiDeviceIndex(&index, nDevice, &paWasapi->inheritedHostApiRep);
3752 if (ret != paNoError)
3753 return ret;
3754
3755 // Validate index.
3756 if ((UINT32)index >= paWasapi->deviceCount)
3757 return paInvalidDevice;
3758
3759 // Get the endpoint device's IDeviceTopology interface.
3760 hr = IMMDevice_Activate(paWasapi->devInfo[index].device, &pa_IID_IDeviceTopology,
3761 CLSCTX_INPROC_SERVER, NULL, (void**)&pDeviceTopology);
3762 IF_FAILED_JUMP(hr, error);
3763
3764 // The device topology for an endpoint device always contains just one connector (connector number 0).
3765 hr = IDeviceTopology_GetConnector(pDeviceTopology, 0, &pConnFrom);
3766 IF_FAILED_JUMP(hr, error);
3767
3768 // Step across the connection to the jack on the adapter.
3769 hr = IConnector_GetConnectedTo(pConnFrom, &pConnTo);
3770 if (HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == hr)
3771 {
3772 // The adapter device is not currently active.
3773 hr = E_NOINTERFACE;
3774 }
3775 IF_FAILED_JUMP(hr, error);
3776
3777 // Get the connector's IPart interface.
3778 hr = IConnector_QueryInterface(pConnTo, &pa_IID_IPart, (void**)&pPart);
3779 IF_FAILED_JUMP(hr, error);
3780
3781 // Activate the connector's IKsJackDescription interface.
3782 hr = IPart_Activate(pPart, CLSCTX_INPROC_SERVER, &pa_IID_IKsJackDescription, (void**)&pJackDesc);
3783 IF_FAILED_JUMP(hr, error);
3784
3785 // Test to return jack description struct for index 0.
3786 hr = IKsJackDescription_GetJackDescription(pJackDesc, jindex, &jack);
3787 IF_FAILED_JUMP(hr, error);
3788
3789 // Convert WASAPI values to PA format.
3790 pJackDescription->channelMapping = jack.ChannelMapping;
3791 pJackDescription->color = jack.Color;
3792 pJackDescription->connectionType = ConvertJackConnectionTypeWASAPIToPA(jack.ConnectionType);
3793 pJackDescription->genLocation = ConvertJackGenLocationWASAPIToPA(jack.GenLocation);
3794 pJackDescription->geoLocation = ConvertJackGeoLocationWASAPIToPA(jack.GeoLocation);
3795 pJackDescription->isConnected = jack.IsConnected;
3796 pJackDescription->portConnection = ConvertJackPortConnectionWASAPIToPA(jack.PortConnection);
3797
3798 // Ok.
3799 ret = paNoError;
3800
3801 error:
3802
3803 SAFE_RELEASE(pDeviceTopology);
3804 SAFE_RELEASE(pConnFrom);
3805 SAFE_RELEASE(pConnTo);
3806 SAFE_RELEASE(pPart);
3807 SAFE_RELEASE(pJackDesc);
3808
3809 LogHostError(hr);
3810 return ret;
3811 }
3812
3813 // ------------------------------------------------------------------------------------------
3814 static HRESULT ProcessOutputBuffer(PaWasapiStream *stream, PaWasapiHostProcessor *processor, UINT32 frames)
3815 {
3816 HRESULT hr;
3817 BYTE *data = NULL;
3818
3819 // Get buffer
3820 if ((hr = IAudioRenderClient_GetBuffer(stream->rclient, frames, &data)) != S_OK)
3821 {
3822 if (stream->out.shareMode == AUDCLNT_SHAREMODE_SHARED)
3823 {
3824 // Using GetCurrentPadding to overcome AUDCLNT_E_BUFFER_TOO_LARGE in
3825 // shared mode results in no sound in Event-driven mode (MSDN does not
3826 // document this, or is it WASAPI bug?), thus we better
3827 // try to acquire buffer next time when GetBuffer allows to do so.
3828 #if 0
3829 // Get Read position
3830 UINT32 padding = 0;
3831 hr = stream->out.client->GetCurrentPadding(&padding);
3832 if (hr != S_OK)
3833 return LogHostError(hr);
3834
3835 // Get frames to write
3836 frames -= padding;
3837 if (frames == 0)
3838 return S_OK;
3839
3840 if ((hr = stream->rclient->GetBuffer(frames, &data)) != S_OK)
3841 return LogHostError(hr);
3842 #else
3843 if (hr == AUDCLNT_E_BUFFER_TOO_LARGE)
3844 return S_OK; // be silent in shared mode, try again next time
3845 #endif
3846 }
3847 else
3848 return LogHostError(hr);
3849 }
3850
3851 // Process data
3852 if (stream->out.monoMixer != NULL)
3853 {
3854 // expand buffer (one way only for better performancedue to no calls to realloc)
3855 UINT32 mono_frames_size = frames * (stream->out.wavex.Format.wBitsPerSample / 8);
3856 if (mono_frames_size > stream->out.monoBufferSize)
3857 stream->out.monoBuffer = realloc(stream->out.monoBuffer, (stream->out.monoBufferSize = mono_frames_size));
3858
3859 // process
3860 processor[S_OUTPUT].processor(NULL, 0, (BYTE *)stream->out.monoBuffer, frames, processor[S_OUTPUT].userData);
3861
3862 // mix 1 to 2 channels
3863 stream->out.monoMixer(data, stream->out.monoBuffer, frames);
3864 }
3865 else
3866 {
3867 processor[S_OUTPUT].processor(NULL, 0, data, frames, processor[S_OUTPUT].userData);
3868 }
3869
3870 // Release buffer
3871 if ((hr = IAudioRenderClient_ReleaseBuffer(stream->rclient, frames, 0)) != S_OK)
3872 LogHostError(hr);
3873
3874 return hr;
3875 }
3876
3877 // ------------------------------------------------------------------------------------------
3878 static HRESULT ProcessInputBuffer(PaWasapiStream *stream, PaWasapiHostProcessor *processor)
3879 {
3880 HRESULT hr = S_OK;
3881 UINT32 frames;
3882 BYTE *data = NULL;
3883 DWORD flags = 0;
3884
3885 for (;;)
3886 {
3887 // Check if blocking call must be interrupted
3888 if (WaitForSingleObject(stream->hCloseRequest, 0) != WAIT_TIMEOUT)
3889 break;
3890
3891 // Get the available data in the shared buffer.
3892 if ((hr = IAudioCaptureClient_GetBuffer(stream->cclient, &data, &frames, &flags, NULL, NULL)) != S_OK)
3893 {
3894 if (hr == AUDCLNT_S_BUFFER_EMPTY)
3895 {
3896 hr = S_OK;
3897 break; // capture buffer exhausted
3898 }
3899
3900 return LogHostError(hr);
3901 break;
3902 }
3903
3904 // Detect silence
3905 // if (flags & AUDCLNT_BUFFERFLAGS_SILENT)
3906 // data = NULL;
3907
3908 // Process data
3909 if (stream->in.monoMixer != NULL)
3910 {
3911 // expand buffer (one way only for better performancedue to no calls to realloc)
3912 UINT32 mono_frames_size = frames * (stream->in.wavex.Format.wBitsPerSample / 8);
3913 if (mono_frames_size > stream->in.monoBufferSize)
3914 stream->in.monoBuffer = realloc(stream->in.monoBuffer, (stream->in.monoBufferSize = mono_frames_size));
3915
3916 // mix 1 to 2 channels
3917 stream->in.monoMixer(stream->in.monoBuffer, data, frames);
3918
3919 // process
3920 processor[S_INPUT].processor((BYTE *)stream->in.monoBuffer, frames, NULL, 0, processor[S_INPUT].userData);
3921 }
3922 else
3923 {
3924 processor[S_INPUT].processor(data, frames, NULL, 0, processor[S_INPUT].userData);
3925 }
3926
3927 // Release buffer
3928 if ((hr = IAudioCaptureClient_ReleaseBuffer(stream->cclient, frames)) != S_OK)
3929 return LogHostError(hr);
3930
3931 break;
3932 }
3933
3934 return hr;
3935 }
3936
3937 // ------------------------------------------------------------------------------------------
3938 void _OnStreamStop(PaWasapiStream *stream)
3939 {
3940 // Stop INPUT client
3941 if (stream->in.client != NULL)
3942 IAudioClient_Stop(stream->in.client);
3943
3944 // Stop OUTPUT client
3945 if (stream->out.client != NULL)
3946 IAudioClient_Stop(stream->out.client);
3947
3948 // Restore thread priority
3949 if (stream->hAvTask != NULL)
3950 {
3951 PaWasapi_ThreadPriorityRevert(stream->hAvTask);
3952 stream->hAvTask = NULL;
3953 }
3954
3955 // Release Render/Capture clients (if Exclusive mode was used it will release devices to other applications)
3956 SAFE_RELEASE(stream->cclient);
3957 SAFE_RELEASE(stream->rclient);
3958
3959 // Notify
3960 if (stream->streamRepresentation.streamFinishedCallback != NULL)
3961 stream->streamRepresentation.streamFinishedCallback(stream->streamRepresentation.userData);
3962 }
3963
3964 // ------------------------------------------------------------------------------------------
3965 PA_THREAD_FUNC ProcThreadEvent(void *param)
3966 {
3967 PaWasapiHostProcessor processor[S_COUNT];
3968 HRESULT hr;
3969 DWORD dwResult;
3970 PaWasapiStream *stream = (PaWasapiStream *)param;
3971 PaWasapiHostProcessor defaultProcessor;
3972 BOOL set_event[S_COUNT] = { FALSE, FALSE };
3973
3974 // Waiting on all events in case of Full-Duplex/Exclusive mode.
3975 BOOL bWaitAllEvents = FALSE;
3976 if ((stream->in.client != NULL) && (stream->out.client != NULL))
3977 {
3978 bWaitAllEvents = (stream->in.shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE) &&
3979 (stream->out.shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE);
3980 }
3981
3982 // Setup data processors
3983 defaultProcessor.processor = WaspiHostProcessingLoop;
3984 defaultProcessor.userData = stream;
3985 processor[S_INPUT] = (stream->hostProcessOverrideInput.processor != NULL ? stream->hostProcessOverrideInput : defaultProcessor);
3986 processor[S_OUTPUT] = (stream->hostProcessOverrideOutput.processor != NULL ? stream->hostProcessOverrideOutput : defaultProcessor);
3987
3988 // Boost thread priority
3989 PaWasapi_ThreadPriorityBoost((void **)&stream->hAvTask, stream->nThreadPriority);
3990
3991 // Create events
3992 if (stream->event[S_OUTPUT] == NULL)
3993 {
3994 stream->event[S_OUTPUT] = CreateEvent(NULL, FALSE, FALSE, NULL);
3995 set_event[S_OUTPUT] = TRUE;
3996 }
3997 if (stream->event[S_INPUT] == NULL)
3998 {
3999 stream->event[S_INPUT] = CreateEvent(NULL, FALSE, FALSE, NULL);
4000 set_event[S_INPUT] = TRUE;
4001 }
4002 if ((stream->event[S_OUTPUT] == NULL) || (stream->event[S_INPUT] == NULL))
4003 {
4004 PRINT(("WASAPI Thread: failed creating Input/Output event handle\n"));
4005 goto thread_error;
4006 }
4007
4008 // Initialize event & start INPUT stream
4009 if (stream->in.client)
4010 {
4011 // Create & set handle
4012 if (set_event[S_INPUT])
4013 {
4014 if ((hr = IAudioClient_SetEventHandle(stream->in.client, stream->event[S_INPUT])) != S_OK)
4015 {
4016 LogHostError(hr);
4017 goto thread_error;
4018 }
4019 }
4020
4021 // Create Capture client
4022 if (stream->cclient == NULL)
4023 {
4024 if ((hr = IAudioClient_GetService(stream->in.client, &pa_IID_IAudioCaptureClient, (void **)&stream->cclient)) != S_OK)
4025 {
4026 LogHostError(hr);
4027 goto thread_error;
4028 }
4029 }
4030
4031 // Start
4032 if ((hr = IAudioClient_Start(stream->in.client)) != S_OK)
4033 {
4034 LogHostError(hr);
4035 goto thread_error;
4036 }
4037 }
4038
4039 // Initialize event & start OUTPUT stream
4040 if (stream->out.client)
4041 {
4042 // Create & set handle
4043 if (set_event[S_OUTPUT])
4044 {
4045 if ((hr = IAudioClient_SetEventHandle(stream->out.client, stream->event[S_OUTPUT])) != S_OK)
4046 {
4047 LogHostError(hr);
4048 goto thread_error;
4049 }
4050 }
4051
4052 // Create Render client
4053 if (stream->rclient == NULL)
4054 {
4055 if ((hr = IAudioClient_GetService(stream->out.client, &pa_IID_IAudioRenderClient, (void **)&stream->rclient)) != S_OK)
4056 {
4057 LogHostError(hr);
4058 goto thread_error;
4059 }
4060 }
4061
4062 // Preload buffer before start
4063 if ((hr = ProcessOutputBuffer(stream, processor, stream->out.framesPerBuffer)) != S_OK)
4064 {
4065 LogHostError(hr);
4066 goto thread_error;
4067 }
4068
4069 // Start
4070 if ((hr = IAudioClient_Start(stream->out.client)) != S_OK)
4071 {
4072 LogHostError(hr);
4073 goto thread_error;
4074 }
4075
4076 }
4077
4078 // Signal: stream running
4079 stream->running = TRUE;
4080
4081 // Notify: thread started
4082 SetEvent(stream->hThreadStart);
4083
4084 // Processing Loop
4085 for (;;)
4086 {
4087 // 10 sec timeout (on timeout stream will auto-stop when processed by WAIT_TIMEOUT case)
4088 dwResult = WaitForMultipleObjects(S_COUNT, stream->event, bWaitAllEvents, 10*1000);
4089
4090 // Check for close event (after wait for buffers to avoid any calls to user
4091 // callback when hCloseRequest was set)
4092 if (WaitForSingleObject(stream->hCloseRequest, 0) != WAIT_TIMEOUT)
4093 break;
4094
4095 // Process S_INPUT/S_OUTPUT
4096 switch (dwResult)
4097 {
4098 case WAIT_TIMEOUT: {
4099 PRINT(("WASAPI Thread: WAIT_TIMEOUT - probably bad audio driver or Vista x64 bug: use paWinWasapiPolling instead\n"));
4100 goto thread_end;
4101 break; }
4102
4103 // Input stream
4104 case WAIT_OBJECT_0 + S_INPUT: {
4105
4106 if (stream->cclient == NULL)
4107 break;
4108
4109 if ((hr = ProcessInputBuffer(stream, processor)) != S_OK)
4110 {
4111 LogHostError(hr);
4112 goto thread_error;
4113 }
4114
4115 break; }
4116
4117 // Output stream
4118 case WAIT_OBJECT_0 + S_OUTPUT: {
4119
4120 if (stream->rclient == NULL)
4121 break;
4122
4123 if ((hr = ProcessOutputBuffer(stream, processor, stream->out.framesPerBuffer)) != S_OK)
4124 {
4125 LogHostError(hr);
4126 goto thread_error;
4127 }
4128
4129 break; }
4130 }
4131 }
4132
4133 thread_end:
4134
4135 // Process stop
4136 _OnStreamStop(stream);
4137
4138 // Notify: thread exited
4139 SetEvent(stream->hThreadExit);
4140
4141 // Notify: not running
4142 stream->running = FALSE;
4143
4144 return 0;
4145
4146 thread_error:
4147
4148 // Prevent deadlocking in Pa_StreamStart
4149 SetEvent(stream->hThreadStart);
4150
4151 // Exit
4152 goto thread_end;
4153 }
4154
4155 // ------------------------------------------------------------------------------------------
4156 static HRESULT PollGetOutputFramesAvailable(PaWasapiStream *stream, UINT32 *available)
4157 {
4158 HRESULT hr;
4159 UINT32 frames = stream->out.framesPerHostCallback,
4160 padding = 0;
4161
4162 (*available) = 0;
4163
4164 // get read position
4165 if ((hr = IAudioClient_GetCurrentPadding(stream->out.client, &padding)) != S_OK)
4166 return LogHostError(hr);
4167
4168 // get available
4169 frames -= padding;
4170
4171 // set
4172 (*available) = frames;
4173 return hr;
4174 }
4175
4176 // ------------------------------------------------------------------------------------------
4177 /*! \class ThreadSleepScheduler
4178 Allows to emulate thread sleep of less than 1 millisecond under Windows. Scheduler
4179 calculates number of times the thread must run untill next sleep of 1 millisecond.
4180 It does not make thread sleeping for real number of microseconds but rather controls
4181 how many of imaginary microseconds the thread task can allow thread to sleep.
4182 */
4183 typedef struct ThreadIdleScheduler
4184 {
4185 UINT32 m_idle_microseconds; //!< number of microseconds to sleep
4186 UINT32 m_next_sleep; //!< next sleep round
4187 UINT32 m_i; //!< current round iterator position
4188 UINT32 m_resolution; //!< resolution in number of milliseconds
4189 }
4190 ThreadIdleScheduler;
4191 //! Setup scheduler.
4192 static void ThreadIdleScheduler_Setup(ThreadIdleScheduler *sched, UINT32 resolution, UINT32 microseconds)
4193 {
4194 assert(microseconds != 0);
4195 assert(resolution != 0);
4196 assert((resolution * 1000) >= microseconds);
4197
4198 memset(sched, 0, sizeof(*sched));
4199
4200 sched->m_idle_microseconds = microseconds;
4201 sched->m_resolution = resolution;
4202 sched->m_next_sleep = (resolution * 1000) / microseconds;
4203 }
4204 //! Iterate and check if can sleep.
4205 static UINT32 ThreadIdleScheduler_NextSleep(ThreadIdleScheduler *sched)
4206 {
4207 // advance and check if thread can sleep
4208 if (++ sched->m_i == sched->m_next_sleep)
4209 {
4210 sched->m_i = 0;
4211 return sched->m_resolution;
4212 }
4213 return 0;
4214 }
4215
4216 // ------------------------------------------------------------------------------------------
4217 PA_THREAD_FUNC ProcThreadPoll(void *param)
4218 {
4219 PaWasapiHostProcessor processor[S_COUNT];
4220 HRESULT hr;
4221 PaWasapiStream *stream = (PaWasapiStream *)param;
4222 PaWasapiHostProcessor defaultProcessor;
4223 INT32 i;
4224 ThreadIdleScheduler scheduler;
4225
4226 // Calculate the actual duration of the allocated buffer.
4227 DWORD sleep_ms = 0;
4228 DWORD sleep_ms_in = GetFramesSleepTime(stream->in.framesPerBuffer, stream->in.wavex.Format.nSamplesPerSec);
4229 DWORD sleep_ms_out = GetFramesSleepTime(stream->out.framesPerBuffer, stream->out.wavex.Format.nSamplesPerSec);
4230
4231 // Adjust polling time
4232 if (stream->bufferMode != paUtilFixedHostBufferSize)
4233 {
4234 sleep_ms_in = GetFramesSleepTime(stream->bufferProcessor.framesPerUserBuffer, stream->in.wavex.Format.nSamplesPerSec);
4235 sleep_ms_out = GetFramesSleepTime(stream->bufferProcessor.framesPerUserBuffer, stream->out.wavex.Format.nSamplesPerSec);
4236 }
4237
4238 // Choose smallest
4239 if ((sleep_ms_in != 0) && (sleep_ms_out != 0))
4240 sleep_ms = min(sleep_ms_in, sleep_ms_out);
4241 else
4242 {
4243 sleep_ms = (sleep_ms_in ? sleep_ms_in : sleep_ms_out);
4244 }
4245 // Make sure not 0, othervise use ThreadIdleScheduler
4246 if (sleep_ms == 0)
4247 {
4248 sleep_ms_in = GetFramesSleepTimeMicroseconds(stream->bufferProcessor.framesPerUserBuffer, stream->in.wavex.Format.nSamplesPerSec);
4249 sleep_ms_out = GetFramesSleepTimeMicroseconds(stream->bufferProcessor.framesPerUserBuffer, stream->out.wavex.Format.nSamplesPerSec);
4250
4251 // Choose smallest
4252 if ((sleep_ms_in != 0) && (sleep_ms_out != 0))
4253 sleep_ms = min(sleep_ms_in, sleep_ms_out);
4254 else
4255 {
4256 sleep_ms = (sleep_ms_in ? sleep_ms_in : sleep_ms_out);
4257 }
4258
4259 // Setup thread sleep scheduler
4260 ThreadIdleScheduler_Setup(&scheduler, 1, sleep_ms/* microseconds here */);
4261 sleep_ms = 0;
4262 }
4263
4264 // Setup data processors
4265 defaultProcessor.processor = WaspiHostProcessingLoop;
4266 defaultProcessor.userData = stream;
4267 processor[S_INPUT] = (stream->hostProcessOverrideInput.processor != NULL ? stream->hostProcessOverrideInput : defaultProcessor);
4268 processor[S_OUTPUT] = (stream->hostProcessOverrideOutput.processor != NULL ? stream->hostProcessOverrideOutput : defaultProcessor);
4269
4270 // Boost thread priority
4271 PaWasapi_ThreadPriorityBoost((void **)&stream->hAvTask, stream->nThreadPriority);
4272
4273 // Initialize event & start INPUT stream
4274 if (stream->in.client)
4275 {
4276 if (stream->cclient == NULL)
4277 {
4278 if ((hr = IAudioClient_GetService(stream->in.client, &pa_IID_IAudioCaptureClient, (void **)&stream->cclient)) != S_OK)
4279 {
4280 LogHostError(hr);
4281 goto thread_error;
4282 }
4283 }
4284
4285 if ((hr = IAudioClient_Start(stream->in.client)) != S_OK)
4286 {
4287 LogHostError(hr);
4288 goto thread_error;
4289 }
4290 }
4291
4292
4293 // Initialize event & start OUTPUT stream
4294 if (stream->out.client)
4295 {
4296 if (stream->rclient == NULL)
4297 {
4298 if ((hr = IAudioClient_GetService(stream->out.client, &pa_IID_IAudioRenderClient, (void **)&stream->rclient)) != S_OK)
4299 {
4300 LogHostError(hr);
4301 goto thread_error;
4302 }
4303 }
4304
4305 // Preload buffer (obligatory, othervise ->Start() will fail), avoid processing
4306 // when in full-duplex mode as it requires input processing as well
4307 if (!PA_WASAPI__IS_FULLDUPLEX(stream))
4308 {
4309 UINT32 frames = 0;
4310 if ((hr = PollGetOutputFramesAvailable(stream, &frames)) == S_OK)
4311 {
4312 if (stream->bufferMode == paUtilFixedHostBufferSize)
4313 {
4314 if (frames >= stream->out.framesPerBuffer)
4315 frames = stream->out.framesPerBuffer;
4316 }
4317
4318 if (frames != 0)
4319 {
4320 if ((hr = ProcessOutputBuffer(stream, processor, frames)) != S_OK)
4321 {
4322 LogHostError(hr); // not fatal, just log
4323 }
4324 }
4325 }
4326 else
4327 {
4328 LogHostError(hr); // not fatal, just log
4329 }
4330 }
4331
4332 // Start
4333 if ((hr = IAudioClient_Start(stream->out.client)) != S_OK)
4334 {
4335 LogHostError(hr);
4336 goto thread_error;
4337 }
4338 }
4339
4340 // Signal: stream running
4341 stream->running = TRUE;
4342
4343 // Notify: thread started
4344 SetEvent(stream->hThreadStart);
4345
4346 if (!PA_WASAPI__IS_FULLDUPLEX(stream))
4347 {
4348 // Processing Loop
4349 UINT32 next_sleep = sleep_ms;
4350 while (WaitForSingleObject(stream->hCloseRequest, next_sleep) == WAIT_TIMEOUT)
4351 {
4352 // Get next sleep time
4353 if (sleep_ms == 0)
4354 {
4355 next_sleep = ThreadIdleScheduler_NextSleep(&scheduler);
4356 }
4357
4358 for (i = 0; i < S_COUNT; ++i)
4359 {
4360 // Process S_INPUT/S_OUTPUT
4361 switch (i)
4362 {
4363 // Input stream
4364 case S_INPUT: {
4365
4366 if (stream->cclient == NULL)
4367 break;
4368
4369 if ((hr = ProcessInputBuffer(stream, processor)) != S_OK)
4370 {
4371 LogHostError(hr);
4372 goto thread_error;
4373 }
4374
4375 break; }
4376
4377 // Output stream
4378 case S_OUTPUT: {
4379
4380 UINT32 frames;
4381 if (stream->rclient == NULL)
4382 break;
4383
4384 // get available frames
4385 if ((hr = PollGetOutputFramesAvailable(stream, &frames)) != S_OK)
4386 {
4387 LogHostError(hr);
4388 goto thread_error;
4389 }
4390
4391 // output
4392 if (stream->bufferMode == paUtilFixedHostBufferSize)
4393 {
4394 if (frames >= stream->out.framesPerBuffer)
4395 {
4396 if ((hr = ProcessOutputBuffer(stream, processor, stream->out.framesPerBuffer)) != S_OK)
4397 {
4398 LogHostError(hr);
4399 goto thread_error;
4400 }
4401 }
4402 }
4403 else
4404 {
4405 if (frames != 0)
4406 {
4407 if ((hr = ProcessOutputBuffer(stream, processor, frames)) != S_OK)
4408 {
4409 LogHostError(hr);
4410 goto thread_error;
4411 }
4412 }
4413 }
4414
4415 break; }
4416 }
4417 }
4418 }
4419 }
4420 else
4421 {
4422 #if 0
4423 // Processing Loop
4424 while (WaitForSingleObject(stream->hCloseRequest, 1) == WAIT_TIMEOUT)
4425 {
4426 UINT32 i_frames = 0, i_processed = 0;
4427 BYTE *i_data = NULL, *o_data = NULL, *o_data_host = NULL;
4428 DWORD i_flags = 0;
4429 UINT32 o_frames = 0;
4430
4431 // get host input buffer
4432 if ((hr = IAudioCaptureClient_GetBuffer(stream->cclient, &i_data, &i_frames, &i_flags, NULL, NULL)) != S_OK)
4433 {
4434 if (hr == AUDCLNT_S_BUFFER_EMPTY)
4435 continue; // no data in capture buffer
4436
4437 LogHostError(hr);
4438 break;
4439 }
4440
4441 // get available frames
4442 if ((hr = PollGetOutputFramesAvailable(stream, &o_frames)) != S_OK)
4443 {
4444 LogHostError(hr);
4445 break;
4446 }
4447
4448 // process equal ammount of frames
4449 if (o_frames >= i_frames)
4450 {
4451 // process input ammount of frames
4452 UINT32 o_processed = i_frames;
4453
4454 // get host output buffer
4455 if ((hr = IAudioRenderClient_GetBuffer(stream->rclient, o_processed, &o_data)) == S_OK)
4456 {
4457 // processed amount of i_frames
4458 i_processed = i_frames;
4459 o_data_host = o_data;
4460
4461 // convert output mono
4462 if (stream->out.monoMixer)
4463 {
4464 UINT32 mono_frames_size = o_processed * (stream->out.wavex.Format.wBitsPerSample / 8);
4465 // expand buffer (one way only for better performance due to no calls to realloc)
4466 if (mono_frames_size > stream->out.monoBufferSize)
4467 {
4468 stream->out.monoBuffer = realloc(stream->out.monoBuffer, (stream->out.monoBufferSize = mono_frames_size));
4469 if (stream->out.monoBuffer == NULL)
4470 {
4471 LogPaError(paInsufficientMemory);
4472 break;
4473 }
4474 }
4475
4476 // replace buffer pointer
4477 o_data = (BYTE *)stream->out.monoBuffer;
4478 }
4479
4480 // convert input mono
4481 if (stream->in.monoMixer)
4482 {
4483 UINT32 mono_frames_size = i_processed * (stream->in.wavex.Format.wBitsPerSample / 8);
4484 // expand buffer (one way only for better performance due to no calls to realloc)
4485 if (mono_frames_size > stream->in.monoBufferSize)
4486 {
4487 stream->in.monoBuffer = realloc(stream->in.monoBuffer, (stream->in.monoBufferSize = mono_frames_size));
4488 if (stream->in.monoBuffer == NULL)
4489 {
4490 LogPaError(paInsufficientMemory);
4491 break;
4492 }
4493 }
4494
4495 // mix 2 to 1 input channels
4496 stream->in.monoMixer(stream->in.monoBuffer, i_data, i_processed);
4497
4498 // replace buffer pointer
4499 i_data = (BYTE *)stream->in.monoBuffer;
4500 }
4501
4502 // process
4503 processor[S_FULLDUPLEX].processor(i_data, i_processed, o_data, o_processed, processor[S_FULLDUPLEX].userData);
4504
4505 // mix 1 to 2 output channels
4506 if (stream->out.monoBuffer)
4507 stream->out.monoMixer(o_data_host, stream->out.monoBuffer, o_processed);
4508
4509 // release host output buffer
4510 if ((hr = IAudioRenderClient_ReleaseBuffer(stream->rclient, o_processed, 0)) != S_OK)
4511 LogHostError(hr);
4512 }
4513 else
4514 {
4515 if (stream->out.shareMode != AUDCLNT_SHAREMODE_SHARED)
4516 LogHostError(hr); // be silent in shared mode, try again next time
4517 }
4518 }
4519
4520 // release host input buffer
4521 if ((hr = IAudioCaptureClient_ReleaseBuffer(stream->cclient, i_processed)) != S_OK)
4522 {
4523 LogHostError(hr);
4524 break;
4525 }
4526 }
4527 #else
4528 // Processing Loop
4529 UINT32 next_sleep = sleep_ms;
4530 while (WaitForSingleObject(stream->hCloseRequest, next_sleep) == WAIT_TIMEOUT)
4531 {
4532 UINT32 i_frames = 0, i_processed = 0;
4533 BYTE *i_data = NULL, *o_data = NULL, *o_data_host = NULL;
4534 DWORD i_flags = 0;
4535 UINT32 o_frames = 0;
4536 //BOOL repeat = FALSE;
4537
4538 // going below 1 msec resolution, switching between 1 ms and no waiting
4539 //if (stream->in.shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE)
4540 // sleep_ms = !sleep_ms;
4541
4542 // Get next sleep time
4543 if (sleep_ms == 0)
4544 {
4545 next_sleep = ThreadIdleScheduler_NextSleep(&scheduler);
4546 }
4547
4548 // get available frames
4549 if ((hr = PollGetOutputFramesAvailable(stream, &o_frames)) != S_OK)
4550 {
4551 LogHostError(hr);
4552 break;
4553 }
4554
4555 while (o_frames != 0)
4556 {
4557 // get host input buffer
4558 if ((hr = IAudioCaptureClient_GetBuffer(stream->cclient, &i_data, &i_frames, &i_flags, NULL, NULL)) != S_OK)
4559 {
4560 if (hr == AUDCLNT_S_BUFFER_EMPTY)
4561 break; // no data in capture buffer
4562
4563 LogHostError(hr);
4564 break;
4565 }
4566
4567 //PA_DEBUG(("full-duplex: o_frames[%d] i_frames[%d] repeat[%d]\n", o_frames, i_frames, repeat));
4568 //repeat = TRUE;
4569
4570 // process equal ammount of frames
4571 if (o_frames >= i_frames)
4572 {
4573 // process input ammount of frames
4574 UINT32 o_processed = i_frames;
4575
4576 // get host output buffer
4577 if ((hr = IAudioRenderClient_GetBuffer(stream->rclient, o_processed, &o_data)) == S_OK)
4578 {
4579 // processed amount of i_frames
4580 i_processed = i_frames;
4581 o_data_host = o_data;
4582
4583 // convert output mono
4584 if (stream->out.monoMixer)
4585 {
4586 UINT32 mono_frames_size = o_processed * (stream->out.wavex.Format.wBitsPerSample / 8);
4587 // expand buffer (one way only for better performance due to no calls to realloc)
4588 if (mono_frames_size > stream->out.monoBufferSize)
4589 {
4590 stream->out.monoBuffer = realloc(stream->out.monoBuffer, (stream->out.monoBufferSize = mono_frames_size));
4591 if (stream->out.monoBuffer == NULL)
4592 {
4593 LogPaError(paInsufficientMemory);
4594 goto thread_error;
4595 }
4596 }
4597
4598 // replace buffer pointer
4599 o_data = (BYTE *)stream->out.monoBuffer;
4600 }
4601
4602 // convert input mono
4603 if (stream->in.monoMixer)
4604 {
4605 UINT32 mono_frames_size = i_processed * (stream->in.wavex.Format.wBitsPerSample / 8);
4606 // expand buffer (one way only for better performance due to no calls to realloc)
4607 if (mono_frames_size > stream->in.monoBufferSize)
4608 {
4609 stream->in.monoBuffer = realloc(stream->in.monoBuffer, (stream->in.monoBufferSize = mono_frames_size));
4610 if (stream->in.monoBuffer == NULL)
4611 {
4612 LogPaError(paInsufficientMemory);
4613 goto thread_error;
4614 }
4615 }
4616
4617 // mix 2 to 1 input channels
4618 stream->in.monoMixer(stream->in.monoBuffer, i_data, i_processed);
4619
4620 // replace buffer pointer
4621 i_data = (BYTE *)stream->in.monoBuffer;
4622 }
4623
4624 // process
4625 processor[S_FULLDUPLEX].processor(i_data, i_processed, o_data, o_processed, processor[S_FULLDUPLEX].userData);
4626
4627 // mix 1 to 2 output channels
4628 if (stream->out.monoBuffer)
4629 stream->out.monoMixer(o_data_host, stream->out.monoBuffer, o_processed);
4630
4631 // release host output buffer
4632 if ((hr = IAudioRenderClient_ReleaseBuffer(stream->rclient, o_processed, 0)) != S_OK)
4633 LogHostError(hr);
4634
4635 o_frames -= o_processed;
4636 }
4637 else
4638 {
4639 if (stream->out.shareMode != AUDCLNT_SHAREMODE_SHARED)
4640 LogHostError(hr); // be silent in shared mode, try again next time
4641 }
4642 }
4643 else
4644 {
4645 i_processed = 0;
4646 goto fd_release_buffer_in;
4647 }
4648
4649 fd_release_buffer_in:
4650
4651 // release host input buffer
4652 if ((hr = IAudioCaptureClient_ReleaseBuffer(stream->cclient, i_processed)) != S_OK)
4653 {
4654 LogHostError(hr);
4655 break;
4656 }
4657
4658 // break processing, input hasn't been accumulated yet
4659 if (i_processed == 0)
4660 break;
4661 }
4662 }
4663 #endif
4664 }
4665
4666 thread_end:
4667
4668 // Process stop
4669 _OnStreamStop(stream);
4670
4671 // Notify: not running
4672 stream->running = FALSE;
4673
4674 // Notify: thread exited
4675 SetEvent(stream->hThreadExit);
4676
4677 return 0;
4678
4679 thread_error:
4680
4681 // Prevent deadlocking in Pa_StreamStart
4682 SetEvent(stream->hThreadStart);
4683
4684 // Exit
4685 goto thread_end;
4686 }
4687
4688 //#endif //VC 2005
4689
4690
4691
4692
4693 #if 0
4694 if(bFirst) {
4695 float masteur;
4696 hr = stream->outVol->GetMasterVolumeLevelScalar(&masteur);
4697 if (hr != S_OK)
4698 LogHostError(hr);
4699 float chan1, chan2;
4700 hr = stream->outVol->GetChannelVolumeLevelScalar(0, &chan1);
4701 if (hr != S_OK)
4702 LogHostError(hr);
4703 hr = stream->outVol->GetChannelVolumeLevelScalar(1, &chan2);
4704 if (hr != S_OK)
4705 LogHostError(hr);
4706
4707 BOOL bMute;
4708 hr = stream->outVol->GetMute(&bMute);
4709 if (hr != S_OK)
4710 LogHostError(hr);
4711
4712 stream->outVol->SetMasterVolumeLevelScalar(0.5, NULL);
4713 stream->outVol->SetChannelVolumeLevelScalar(0, 0.5, NULL);
4714 stream->outVol->SetChannelVolumeLevelScalar(1, 0.5, NULL);
4715 stream->outVol->SetMute(FALSE, NULL);
4716 bFirst = FALSE;
4717 }
4718 #endif

  ViewVC Help
Powered by ViewVC 1.1.22