/*\r
* Portable Audio I/O Library WASAPI implementation\r
- * Copyright (c) 2006-2007 David Viens\r
+ * Copyright (c) 2006-2010 David Viens, Dmitry Kostjuchenko\r
*\r
* Based on the Open Source API proposed by Ross Bencina\r
* Copyright (c) 1999-2002 Ross Bencina, Phil Burk\r
*/\r
\r
/*\r
- * The text above constitutes the entire PortAudio license; however, \r
+ * The text above constitutes the entire PortAudio license; however,\r
* the PortAudio community also makes the following non-binding requests:\r
*\r
* Any person wishing to distribute modifications to the Software is\r
* requested to send the modifications to the original developer so that\r
- * they can be incorporated into the canonical version. It is also \r
- * requested that these non-binding requests be included along with the \r
+ * they can be incorporated into the canonical version. It is also\r
+ * requested that these non-binding requests be included along with the\r
* license above.\r
*/\r
\r
/** @file\r
@ingroup hostapi_src\r
@brief WASAPI implementation of support for a host API.\r
-\r
- @note pa_wasapi currently requires VC 2005, and the latest Vista SDK\r
+ @note pa_wasapi currently requires minimum VC 2005, and the latest Vista SDK\r
*/\r
\r
-#if _MSC_VER >= 1400\r
+#define WIN32_LEAN_AND_MEAN // exclude rare headers\r
#include <windows.h>\r
-#include <MMReg.h> //must be before other Wasapi headers\r
-#include <strsafe.h>\r
-#include <mmdeviceapi.h>\r
-#include <Avrt.h>\r
-#include <audioclient.h>\r
-#include <Endpointvolume.h>\r
-\r
-#include <KsMedia.h>\r
-#include <functiondiscoverykeys.h> // PKEY_Device_FriendlyName\r
-#endif\r
-\r
#include <process.h>\r
+#include <mmsystem.h>\r
+#include <MMReg.h> // must be before other Wasapi headers\r
+#if _MSC_VER >= 1400\r
+ #include <strsafe.h>\r
+ #include <Avrt.h>\r
+ #include <audioclient.h>\r
+ #include <Endpointvolume.h>\r
+ #define INITGUID //<< avoid additional linkage of static libs, uneeded code will be optimized out\r
+ #include <mmdeviceapi.h>\r
+ #include <functiondiscoverykeys.h>\r
+ #undef INITGUID\r
+#endif\r
\r
#include "pa_util.h"\r
#include "pa_allocation.h"\r
#include "pa_cpuload.h"\r
#include "pa_process.h"\r
#include "pa_debugprint.h"\r
+#include "pa_win_wasapi.h"\r
+\r
+/* use CreateThread for CYGWIN/Windows Mobile, _beginthreadex for all others */\r
+#if !defined(__CYGWIN__) && !defined(_WIN32_WCE)\r
+#define CREATE_THREAD(PROC) (HANDLE)_beginthreadex( NULL, 0, (PROC), stream, 0, &stream->dwThreadId )\r
+#define PA_THREAD_FUNC static unsigned WINAPI\r
+#define PA_THREAD_ID unsigned\r
+#else\r
+#define CREATE_THREAD(PROC) CreateThread( NULL, 0, (PROC), stream, 0, &stream->dwThreadId )\r
+#define PA_THREAD_FUNC static DWORD WINAPI\r
+#define PA_THREAD_ID DWORD\r
+#endif\r
\r
+// Thread function forward decl.\r
+PA_THREAD_FUNC ProcThreadEvent(void *param);\r
+PA_THREAD_FUNC ProcThreadPoll(void *param);\r
\r
-/*\r
- davidv : work in progress. try using with 48000 , then 44100\r
- and shared mode FIRST.\r
- */\r
+// Availabe from Windows 7\r
+#ifndef AUDCLNT_E_BUFFER_ERROR\r
+ #define AUDCLNT_E_BUFFER_ERROR AUDCLNT_ERR(0x018)\r
+#endif\r
+#ifndef AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED\r
+ #define AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED AUDCLNT_ERR(0x019)\r
+#endif\r
+#ifndef AUDCLNT_E_INVALID_DEVICE_PERIOD\r
+ #define AUDCLNT_E_INVALID_DEVICE_PERIOD AUDCLNT_ERR(0x020)\r
+#endif\r
\r
-#define PORTAUDIO_SHAREMODE AUDCLNT_SHAREMODE_SHARED\r
-//#define PORTAUDIO_SHAREMODE AUDCLNT_SHAREMODE_EXCLUSIVE\r
+#define MAX_STR_LEN 512\r
\r
-/* use CreateThread for CYGWIN, _beginthreadex for all others */\r
-#ifndef __CYGWIN__\r
-#define CREATE_THREAD (HANDLE) _beginthreadex(NULL, 0, (unsigned (_stdcall *)(void *))ProcThread, (LPVOID) stream, 0, (unsigned *)&stream->dwThreadId)\r
-#else\r
-#define CREATE_THREAD CreateThread(NULL, 0, ProcThread, (LPVOID) stream, 0, &stream->dwThreadId)\r
+enum { S_INPUT = 0, S_OUTPUT, S_COUNT };\r
+\r
+#define PRINT(x) PA_DEBUG(x);\r
+\r
+#define PA_SKELETON_SET_LAST_HOST_ERROR( errorCode, errorText ) \\r
+ PaUtil_SetLastHostErrorInfo( paInDevelopment, errorCode, errorText )\r
+\r
+#ifndef IF_FAILED_JUMP\r
+#define IF_FAILED_JUMP(hr, label) if(FAILED(hr)) goto label;\r
#endif\r
\r
-/* prototypes for functions declared in this file */\r
+#define SAFE_CLOSE(h) if ((h) != NULL) { CloseHandle((h)); (h) = NULL; }\r
+#define SAFE_RELEASE(punk) if ((punk) != NULL) { (punk)->Release(); (punk) = NULL; }\r
+\r
+// Local methods\r
+void _OnStreamStop(struct PaWasapiStream *stream);\r
+void _FinishStream(struct PaWasapiStream *stream);\r
+\r
+// AVRT is the new "multimedia schedulling stuff"\r
+typedef BOOL (WINAPI *FAvRtCreateThreadOrderingGroup) (PHANDLE,PLARGE_INTEGER,GUID*,PLARGE_INTEGER);\r
+typedef BOOL (WINAPI *FAvRtDeleteThreadOrderingGroup) (HANDLE);\r
+typedef BOOL (WINAPI *FAvRtWaitOnThreadOrderingGroup) (HANDLE);\r
+typedef HANDLE (WINAPI *FAvSetMmThreadCharacteristics) (LPCTSTR,LPDWORD);\r
+typedef BOOL (WINAPI *FAvRevertMmThreadCharacteristics)(HANDLE);\r
+typedef BOOL (WINAPI *FAvSetMmThreadPriority) (HANDLE,AVRT_PRIORITY);\r
+\r
+static HMODULE hDInputDLL = 0;\r
+FAvRtCreateThreadOrderingGroup pAvRtCreateThreadOrderingGroup=0;\r
+FAvRtDeleteThreadOrderingGroup pAvRtDeleteThreadOrderingGroup=0;\r
+FAvRtWaitOnThreadOrderingGroup pAvRtWaitOnThreadOrderingGroup=0;\r
+FAvSetMmThreadCharacteristics pAvSetMmThreadCharacteristics=0;\r
+FAvRevertMmThreadCharacteristics pAvRevertMmThreadCharacteristics=0;\r
+FAvSetMmThreadPriority pAvSetMmThreadPriority=0;\r
+\r
+#define setupPTR(fun, type, name) { \\r
+ fun = (type) GetProcAddress(hDInputDLL,name); \\r
+ if(fun == NULL) { \\r
+ PRINT(("GetProcAddr failed for %s" ,name)); \\r
+ return false; \\r
+ } \\r
+ } \\r
\r
+// ------------------------------------------------------------------------------------------\r
+/* prototypes for functions declared in this file */\r
#ifdef __cplusplus\r
extern "C"\r
{\r
#endif /* __cplusplus */\r
-\r
-PaError PaWinWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );\r
-\r
+PaError PaWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );\r
#ifdef __cplusplus\r
}\r
#endif /* __cplusplus */\r
+// dummy entry point for other compilers and sdks\r
+// currently built using RC1 SDK (5600)\r
+#if _MSC_VER < 1400\r
+PaError PaWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex )\r
+{\r
+ return paNoError;\r
+}\r
+#else\r
\r
-\r
-\r
-\r
+// ------------------------------------------------------------------------------------------\r
static void Terminate( struct PaUtilHostApiRepresentation *hostApi );\r
static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,\r
const PaStreamParameters *inputParameters,\r
static signed long GetStreamReadAvailable( PaStream* stream );\r
static signed long GetStreamWriteAvailable( PaStream* stream );\r
\r
-\r
-/* IMPLEMENT ME: a macro like the following one should be used for reporting\r
- host errors */\r
-#define PA_SKELETON_SET_LAST_HOST_ERROR( errorCode, errorText ) \\r
- PaUtil_SetLastHostErrorInfo( paInDevelopment, errorCode, errorText )\r
-\r
-/* PaWinWasapiHostApiRepresentation - host api datastructure specific to this implementation */\r
-\r
-\r
-\r
-//dummy entry point for other compilers and sdks\r
-//currently built using RC1 SDK (5600)\r
-#if _MSC_VER < 1400\r
-\r
-PaError PaWinWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex ){\r
- return paNoError;\r
-}\r
-\r
-#else\r
-\r
-\r
-\r
-\r
-#define MAX_STR_LEN 512\r
-\r
+// ------------------------------------------------------------------------------------------\r
/*\r
- These are fields that can be gathered from IDevice\r
- and IAudioDevice PRIOR to Initialize, and done in first pass\r
- i assume that neither of these will cause the Driver to "load",\r
- but again, who knows how they implement their stuff\r
+ These are fields that can be gathered from IDevice and IAudioDevice PRIOR to Initialize, and \r
+ done in first pass i assume that neither of these will cause the Driver to "load", but again, \r
+ who knows how they implement their stuff\r
*/\r
-typedef struct PaWinWasapiDeviceInfo\r
+typedef struct PaWasapiDeviceInfo\r
{\r
- //hmm is it wise to keep a reference until Terminate?\r
- //TODO Check if that interface requires the driver to be loaded!\r
- IMMDevice * device;\r
+ // Device\r
+ IMMDevice *device;\r
\r
- //Fields filled from IDevice\r
- //from GetId\r
+ // from GetId\r
WCHAR szDeviceID[MAX_STR_LEN];\r
- //from GetState\r
+ \r
+ // from GetState\r
DWORD state;\r
\r
- //Fields filled from IMMEndpoint'sGetDataFlow\r
- EDataFlow flow;\r
+ // Fields filled from IMMEndpoint'sGetDataFlow\r
+ EDataFlow flow;\r
\r
- //Fields filled from IAudioDevice (_prior_ to Initialize)\r
- //from GetDevicePeriod(\r
- REFERENCE_TIME DefaultDevicePeriod;\r
- REFERENCE_TIME MinimumDevicePeriod;\r
- //from GetMixFormat\r
- WAVEFORMATEX *MixFormat;//needs to be CoTaskMemFree'd after use!\r
+ // Fields filled from IAudioDevice (_prior_ to Initialize)\r
+ // from GetDevicePeriod(\r
+ REFERENCE_TIME DefaultDevicePeriod;\r
+ REFERENCE_TIME MinimumDevicePeriod;\r
\r
-} PaWinWasapiDeviceInfo;\r
+ // from GetMixFormat\r
+ // WAVEFORMATEX *MixFormat;//needs to be CoTaskMemFree'd after use!\r
\r
+ // Default format (setup through Control Panel by user)\r
+ WAVEFORMATEXTENSIBLE DefaultFormat;\r
\r
+ // Formfactor\r
+ EndpointFormFactor formFactor;\r
+} \r
+PaWasapiDeviceInfo;\r
+\r
+// ------------------------------------------------------------------------------------------\r
+/* PaWasapiHostApiRepresentation - host api datastructure specific to this implementation */\r
typedef struct\r
{\r
PaUtilHostApiRepresentation inheritedHostApiRep;\r
/* implementation specific data goes here */\r
\r
//in case we later need the synch\r
- IMMDeviceEnumerator * enumerator;\r
+ IMMDeviceEnumerator *enumerator;\r
\r
//this is the REAL number of devices, whether they are usefull to PA or not!\r
UINT deviceCount;\r
WCHAR defaultRenderer [MAX_STR_LEN];\r
WCHAR defaultCapturer [MAX_STR_LEN];\r
\r
- PaWinWasapiDeviceInfo *devInfo;\r
-}PaWinWasapiHostApiRepresentation;\r
-\r
-\r
-/* PaWinWasapiStream - a stream data structure specifically for this implementation */\r
+ PaWasapiDeviceInfo *devInfo;\r
+}\r
+PaWasapiHostApiRepresentation;\r
\r
-typedef struct PaWinWasapiSubStream{\r
+// ------------------------------------------------------------------------------------------\r
+/* PaWasapiStream - a stream data structure specifically for this implementation */\r
+typedef struct PaWasapiSubStream\r
+{\r
IAudioClient *client;\r
WAVEFORMATEXTENSIBLE wavex;\r
UINT32 bufferSize;\r
REFERENCE_TIME latency;\r
REFERENCE_TIME period;\r
- unsigned long framesPerHostCallback; /* just an example */\r
-}PaWinWasapiSubStream;\r
+ double latency_seconds;\r
+ UINT32 framesPerHostCallback;\r
+ AUDCLNT_SHAREMODE shareMode;\r
+ UINT32 streamFlags; // AUDCLNT_STREAMFLAGS_EVENTCALLBACK, ...\r
+ UINT32 flags;\r
+\r
+ // Used by blocking interface:\r
+ UINT32 prevTime; // time ms between calls of WriteStream\r
+ UINT32 prevSleep; // time ms to sleep from frames written in previous call\r
+}\r
+PaWasapiSubStream;\r
+\r
+// ------------------------------------------------------------------------------------------\r
+/* PaWasapiHostProcessor - redirects processing data */\r
+typedef struct PaWasapiHostProcessor\r
+{\r
+ PaWasapiHostProcessorCallback processor;\r
+ void *userData;\r
+}\r
+PaWasapiHostProcessor;\r
\r
-typedef struct PaWinWasapiStream\r
-{ /* IMPLEMENT ME: rename this */\r
+// ------------------------------------------------------------------------------------------\r
+typedef struct PaWasapiStream\r
+{ \r
+ /* IMPLEMENT ME: rename this */\r
PaUtilStreamRepresentation streamRepresentation;\r
PaUtilCpuLoadMeasurer cpuLoadMeasurer;\r
PaUtilBufferProcessor bufferProcessor;\r
\r
- /* IMPLEMENT ME:\r
- - implementation specific data goes here\r
- */\r
-\r
-\r
- //input\r
- PaWinWasapiSubStream in;\r
+ // input\r
+ PaWasapiSubStream in;\r
IAudioCaptureClient *cclient;\r
IAudioEndpointVolume *inVol;\r
- //output\r
- PaWinWasapiSubStream out;\r
+ \r
+ // output\r
+ PaWasapiSubStream out;\r
IAudioRenderClient *rclient;\r
IAudioEndpointVolume *outVol;\r
\r
- bool running;\r
- bool closeRequest;\r
+ // must be volatile to avoid race condition on user query while\r
+ // thread is being started\r
+ volatile bool running;\r
\r
- DWORD dwThreadId;\r
+ PA_THREAD_ID dwThreadId;\r
HANDLE hThread;\r
- HANDLE hNotificationEvent; \r
+ HANDLE hCloseRequest;\r
+ HANDLE hThreadDone;\r
+ HANDLE hBlockingOpStreamRD;\r
+ HANDLE hBlockingOpStreamWR;\r
\r
- GUID session;\r
+ // Host callback Output overrider\r
+ PaWasapiHostProcessor hostProcessOverrideOutput;\r
\r
-}PaWinWasapiStream;\r
+ // Host callback Input overrider\r
+ PaWasapiHostProcessor hostProcessOverrideInput;\r
\r
-#define PRINT(x) PA_DEBUG(x);\r
+ // Defines blocking/callback interface used\r
+ bool bBlocking;\r
\r
-void\r
-logAUDCLNT_E(HRESULT res){\r
-\r
- char *text = 0;\r
- switch(res){\r
- case S_OK: return; break;\r
- case E_POINTER :text ="E_POINTER"; break;\r
- case E_INVALIDARG :text ="E_INVALIDARG"; break;\r
-\r
- case AUDCLNT_E_NOT_INITIALIZED :text ="AUDCLNT_E_NOT_INITIALIZED"; break;\r
- case AUDCLNT_E_ALREADY_INITIALIZED :text ="AUDCLNT_E_ALREADY_INITIALIZED"; break;\r
- case AUDCLNT_E_WRONG_ENDPOINT_TYPE :text ="AUDCLNT_E_WRONG_ENDPOINT_TYPE"; break;\r
- case AUDCLNT_E_DEVICE_INVALIDATED :text ="AUDCLNT_E_DEVICE_INVALIDATED"; break;\r
- case AUDCLNT_E_NOT_STOPPED :text ="AUDCLNT_E_NOT_STOPPED"; break;\r
- case AUDCLNT_E_BUFFER_TOO_LARGE :text ="AUDCLNT_E_BUFFER_TOO_LARGE"; break;\r
- case AUDCLNT_E_OUT_OF_ORDER :text ="AUDCLNT_E_OUT_OF_ORDER"; break;\r
- case AUDCLNT_E_UNSUPPORTED_FORMAT :text ="AUDCLNT_E_UNSUPPORTED_FORMAT"; break;\r
- case AUDCLNT_E_INVALID_SIZE :text ="AUDCLNT_E_INVALID_SIZE"; break;\r
- case AUDCLNT_E_DEVICE_IN_USE :text ="AUDCLNT_E_DEVICE_IN_USE"; break;\r
- case AUDCLNT_E_BUFFER_OPERATION_PENDING :text ="AUDCLNT_E_BUFFER_OPERATION_PENDING"; break;\r
- case AUDCLNT_E_THREAD_NOT_REGISTERED :text ="AUDCLNT_E_THREAD_NOT_REGISTERED"; break; \r
- case AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED :text ="AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED"; break;\r
- case AUDCLNT_E_ENDPOINT_CREATE_FAILED :text ="AUDCLNT_E_ENDPOINT_CREATE_FAILED"; break;\r
- case AUDCLNT_E_SERVICE_NOT_RUNNING :text ="AUDCLNT_E_SERVICE_NOT_RUNNING"; break;\r
- // case AUDCLNT_E_CPUUSAGE_EXCEEDED :text ="AUDCLNT_E_CPUUSAGE_EXCEEDED"; break;\r
- //Header error?\r
- case AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED :text ="AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED"; break;\r
- case AUDCLNT_E_EXCLUSIVE_MODE_ONLY :text ="AUDCLNT_E_EXCLUSIVE_MODE_ONLY"; break;\r
- case AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL :text ="AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL"; break;\r
- case AUDCLNT_E_EVENTHANDLE_NOT_SET :text ="AUDCLNT_E_EVENTHANDLE_NOT_SET"; break;\r
- case AUDCLNT_E_INCORRECT_BUFFER_SIZE :text ="AUDCLNT_E_INCORRECT_BUFFER_SIZE"; break;\r
- case AUDCLNT_E_BUFFER_SIZE_ERROR :text ="AUDCLNT_E_BUFFER_SIZE_ERROR"; break;\r
- case AUDCLNT_S_BUFFER_EMPTY :text ="AUDCLNT_S_BUFFER_EMPTY"; break;\r
- case AUDCLNT_S_THREAD_ALREADY_REGISTERED :text ="AUDCLNT_S_THREAD_ALREADY_REGISTERED"; break;\r
- default:\r
- text =" dunno!";\r
- return ;\r
- break;\r
+ // Av Task (MM thread management)\r
+ HANDLE hAvTask;\r
+}\r
+PaWasapiStream;\r
\r
+// ------------------------------------------------------------------------------------------\r
+#define LogHostError(HRES) __LogHostError(HRES, __FILE__, __LINE__)\r
+static HRESULT __LogHostError(HRESULT res, const char *file, int line)\r
+{\r
+ const char *text = NULL;\r
+ switch (res)\r
+ {\r
+ case S_OK: return res;\r
+ case E_POINTER :text ="E_POINTER"; break;\r
+ case E_INVALIDARG :text ="E_INVALIDARG"; break;\r
+\r
+ case AUDCLNT_E_NOT_INITIALIZED :text ="AUDCLNT_E_NOT_INITIALIZED"; break;\r
+ case AUDCLNT_E_ALREADY_INITIALIZED :text ="AUDCLNT_E_ALREADY_INITIALIZED"; break;\r
+ case AUDCLNT_E_WRONG_ENDPOINT_TYPE :text ="AUDCLNT_E_WRONG_ENDPOINT_TYPE"; break;\r
+ case AUDCLNT_E_DEVICE_INVALIDATED :text ="AUDCLNT_E_DEVICE_INVALIDATED"; break;\r
+ case AUDCLNT_E_NOT_STOPPED :text ="AUDCLNT_E_NOT_STOPPED"; break;\r
+ case AUDCLNT_E_BUFFER_TOO_LARGE :text ="AUDCLNT_E_BUFFER_TOO_LARGE"; break;\r
+ case AUDCLNT_E_OUT_OF_ORDER :text ="AUDCLNT_E_OUT_OF_ORDER"; break;\r
+ case AUDCLNT_E_UNSUPPORTED_FORMAT :text ="AUDCLNT_E_UNSUPPORTED_FORMAT"; break;\r
+ case AUDCLNT_E_INVALID_SIZE :text ="AUDCLNT_E_INVALID_SIZE"; break;\r
+ case AUDCLNT_E_DEVICE_IN_USE :text ="AUDCLNT_E_DEVICE_IN_USE"; break;\r
+ case AUDCLNT_E_BUFFER_OPERATION_PENDING :text ="AUDCLNT_E_BUFFER_OPERATION_PENDING"; break;\r
+ case AUDCLNT_E_THREAD_NOT_REGISTERED :text ="AUDCLNT_E_THREAD_NOT_REGISTERED"; break;\r
+ case AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED :text ="AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED"; break;\r
+ case AUDCLNT_E_ENDPOINT_CREATE_FAILED :text ="AUDCLNT_E_ENDPOINT_CREATE_FAILED"; break;\r
+ case AUDCLNT_E_SERVICE_NOT_RUNNING :text ="AUDCLNT_E_SERVICE_NOT_RUNNING"; break;\r
+ case AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED :text ="AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED"; break;\r
+ case AUDCLNT_E_EXCLUSIVE_MODE_ONLY :text ="AUDCLNT_E_EXCLUSIVE_MODE_ONLY"; break;\r
+ case AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL :text ="AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL"; break;\r
+ case AUDCLNT_E_EVENTHANDLE_NOT_SET :text ="AUDCLNT_E_EVENTHANDLE_NOT_SET"; break;\r
+ case AUDCLNT_E_INCORRECT_BUFFER_SIZE :text ="AUDCLNT_E_INCORRECT_BUFFER_SIZE"; break;\r
+ case AUDCLNT_E_BUFFER_SIZE_ERROR :text ="AUDCLNT_E_BUFFER_SIZE_ERROR"; break;\r
+ case AUDCLNT_E_CPUUSAGE_EXCEEDED :text ="AUDCLNT_E_CPUUSAGE_EXCEEDED"; break; \r
+ case AUDCLNT_E_BUFFER_ERROR :text ="AUDCLNT_E_BUFFER_ERROR"; break;\r
+ case AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED :text ="AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED"; break;\r
+ case AUDCLNT_E_INVALID_DEVICE_PERIOD :text ="AUDCLNT_E_INVALID_DEVICE_PERIOD"; break;\r
+\r
+ case AUDCLNT_S_BUFFER_EMPTY :text ="AUDCLNT_S_BUFFER_EMPTY"; break;\r
+ case AUDCLNT_S_THREAD_ALREADY_REGISTERED :text ="AUDCLNT_S_THREAD_ALREADY_REGISTERED"; break;\r
+ case AUDCLNT_S_POSITION_STALLED :text ="AUDCLNT_S_POSITION_STALLED"; break;\r
+\r
+ default:\r
+ text = "UNKNOWN ERROR";\r
}\r
- PRINT(("WASAPI ERROR HRESULT: 0x%X : %s\n",res,text));\r
+ PRINT(("WASAPI ERROR HRESULT: 0x%X : %s\n [FILE: %s {LINE: %d}]\n",res, text, file, line));\r
+ PA_SKELETON_SET_LAST_HOST_ERROR(res, text);\r
+ return res;\r
}\r
\r
-inline double\r
-nano100ToMillis(const REFERENCE_TIME &ref){\r
+// ------------------------------------------------------------------------------------------\r
+#define LogPaError(PAERR) __LogPaError(PAERR, __FILE__, __LINE__)\r
+static PaError __LogPaError(PaError err, const char *file, int line)\r
+{\r
+ if (err == paNoError) \r
+ return err;\r
+ const char *text = Pa_GetErrorText(err);\r
+ PRINT(("WASAPI ERROR PAERROR: %i : %s\n [FILE: %s {LINE: %d}]\n",err,text,file,line));\r
+ return err;\r
+}\r
+\r
+// ------------------------------------------------------------------------------------------\r
+static inline double nano100ToMillis(const REFERENCE_TIME &ref)\r
+{\r
// 1 nano = 0.000000001 seconds\r
//100 nano = 0.0000001 seconds\r
//100 nano = 0.0001 milliseconds\r
return ((double)ref)*0.0001;\r
}\r
\r
-inline double\r
-nano100ToSeconds(const REFERENCE_TIME &ref){\r
+// ------------------------------------------------------------------------------------------\r
+static inline double nano100ToSeconds(const REFERENCE_TIME &ref)\r
+{\r
// 1 nano = 0.000000001 seconds\r
//100 nano = 0.0000001 seconds\r
//100 nano = 0.0001 milliseconds\r
return ((double)ref)*0.0000001;\r
}\r
\r
-#ifndef IF_FAILED_JUMP\r
-#define IF_FAILED_JUMP(hr, label) if(FAILED(hr)) goto label;\r
-#endif\r
+// ------------------------------------------------------------------------------------------\r
+static inline REFERENCE_TIME MillisTonano100(const double &ref)\r
+{\r
+ // 1 nano = 0.000000001 seconds\r
+ //100 nano = 0.0000001 seconds\r
+ //100 nano = 0.0001 milliseconds\r
+ return (REFERENCE_TIME)(ref/0.0001);\r
+}\r
+\r
+// ------------------------------------------------------------------------------------------\r
+static inline REFERENCE_TIME SecondsTonano100(const double &ref)\r
+{\r
+ // 1 nano = 0.000000001 seconds\r
+ //100 nano = 0.0000001 seconds\r
+ //100 nano = 0.0001 milliseconds\r
+ return (REFERENCE_TIME)(ref/0.0000001);\r
+}\r
\r
+// ------------------------------------------------------------------------------------------\r
+// Makes Hns period from frames and sample rate\r
+static inline REFERENCE_TIME MakeHnsPeriod(UINT32 nFrames, DWORD nSamplesPerSec)\r
+{\r
+ return (REFERENCE_TIME)((10000.0 * 1000 / nSamplesPerSec * nFrames) + 0.5);\r
+}\r
\r
+// ------------------------------------------------------------------------------------------\r
+// Converts PaSampleFormat to bits per sample value\r
+static inline WORD PaSampleFormatToBitsPerSample(PaSampleFormat format_id)\r
+{\r
+ switch (format_id & ~paNonInterleaved)\r
+ {\r
+ case paFloat32:\r
+ case paInt32: return 32;\r
+ case paInt24: return 24;\r
+ case paInt16: return 16;\r
+ case paInt8:\r
+ case paUInt8: return 8;\r
+ }\r
+ return 0;\r
+}\r
\r
-//AVRT is the new "multimedia schedulling stuff"\r
+// ------------------------------------------------------------------------------------------\r
+// Converts PaSampleFormat to bits per sample value\r
+static inline WORD PaSampleFormatToBytesPerSample(PaSampleFormat format_id)\r
+{\r
+ return PaSampleFormatToBitsPerSample(format_id) >> 3; // 'bits/8'\r
+}\r
\r
-typedef BOOL (WINAPI *FAvRtCreateThreadOrderingGroup) (PHANDLE,PLARGE_INTEGER,GUID*,PLARGE_INTEGER);\r
-typedef BOOL (WINAPI *FAvRtDeleteThreadOrderingGroup) (HANDLE);\r
-typedef BOOL (WINAPI *FAvRtWaitOnThreadOrderingGroup) (HANDLE);\r
-typedef HANDLE (WINAPI *FAvSetMmThreadCharacteristics) (LPCTSTR,LPDWORD);\r
-typedef BOOL (WINAPI *FAvSetMmThreadPriority) (HANDLE,AVRT_PRIORITY);\r
+// ------------------------------------------------------------------------------------------\r
+// Converts Hns period into number of frames\r
+static inline UINT32 MakeFramesFromHns(REFERENCE_TIME hnsPeriod, UINT32 nSamplesPerSec)\r
+{\r
+ UINT32 nFrames = (UINT32)( // frames =\r
+ 1.0 * hnsPeriod * // hns *\r
+ nSamplesPerSec / // (frames / s) /\r
+ 1000 / // (ms / s) /\r
+ 10000 // (hns / s) /\r
+ + 0.5 // rounding\r
+ );\r
+ return nFrames;\r
+}\r
\r
-HMODULE hDInputDLL = 0;\r
-FAvRtCreateThreadOrderingGroup pAvRtCreateThreadOrderingGroup=0;\r
-FAvRtDeleteThreadOrderingGroup pAvRtDeleteThreadOrderingGroup=0;\r
-FAvRtWaitOnThreadOrderingGroup pAvRtWaitOnThreadOrderingGroup=0;\r
-FAvSetMmThreadCharacteristics pAvSetMmThreadCharacteristics=0;\r
-FAvSetMmThreadPriority pAvSetMmThreadPriority=0;\r
+// ------------------------------------------------------------------------------------------\r
+// Aligns v backwards\r
+static inline UINT32 ALIGN_BWD(UINT32 v, UINT32 align)\r
+{\r
+ return ((v - (align ? v % align : 0)));\r
+}\r
\r
+// ------------------------------------------------------------------------------------------\r
+// Aligns WASAPI buffer to 128 byte packet boundary. HD Audio will fail to play if buffer\r
+// is misaligned. This problem was solved in Windows 7 were AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED\r
+// is thrown although we must align for Vista anyway.\r
+static UINT32 AlignFramesPerBuffer(UINT32 nFrames, UINT32 nSamplesPerSec, UINT32 nBlockAlign)\r
+{\r
+#define HDA_PACKET_SIZE 128\r
\r
+ long packets_total = nSamplesPerSec * nBlockAlign / HDA_PACKET_SIZE;\r
+ long frame_bytes = nFrames * nBlockAlign;\r
+ \r
+ // align to packet size\r
+ frame_bytes = ALIGN_BWD(frame_bytes, HDA_PACKET_SIZE);\r
+ nFrames = frame_bytes / nBlockAlign;\r
+ long packets = frame_bytes / HDA_PACKET_SIZE;\r
+ \r
+ // align to packets count\r
+ while (packets && ((packets_total % packets) != 0))\r
+ {\r
+ --packets;\r
+ }\r
+ \r
+ frame_bytes = packets * HDA_PACKET_SIZE;\r
+ nFrames = frame_bytes / nBlockAlign;\r
\r
-#define setupPTR(fun, type, name) { \\r
- fun = (type) GetProcAddress(hDInputDLL,name); \\r
- if(fun == NULL) { \\r
- PRINT(("GetProcAddr failed for %s" ,name)); \\r
- return false; \\r
- } \\r
- } \\r
+ return nFrames;\r
+}\r
\r
-bool\r
-setupAVRT(){\r
+// ------------------------------------------------------------------------------------------\r
+static __forceinline UINT32 GetFramesSleepTime(UINT32 nFrames, UINT32 nSamplesPerSec)\r
+{\r
+#define REFTIMES_PER_SEC 10000000\r
+#define REFTIMES_PER_MILLISEC 10000\r
+ // Calculate the actual duration of the allocated buffer.\r
+ REFERENCE_TIME nDuration = (REFERENCE_TIME)((double)REFTIMES_PER_SEC * nFrames / nSamplesPerSec);\r
+ return (UINT32)(nDuration/REFTIMES_PER_MILLISEC/2);\r
+}\r
\r
+// ------------------------------------------------------------------------------------------\r
+static bool SetupAVRT()\r
+{\r
hDInputDLL = LoadLibraryA("avrt.dll");\r
if(hDInputDLL == NULL)\r
return false;\r
\r
- setupPTR(pAvRtCreateThreadOrderingGroup, FAvRtCreateThreadOrderingGroup, "AvRtCreateThreadOrderingGroup");\r
- setupPTR(pAvRtDeleteThreadOrderingGroup, FAvRtDeleteThreadOrderingGroup, "AvRtDeleteThreadOrderingGroup");\r
- setupPTR(pAvRtWaitOnThreadOrderingGroup, FAvRtWaitOnThreadOrderingGroup, "AvRtWaitOnThreadOrderingGroup");\r
- setupPTR(pAvSetMmThreadCharacteristics, FAvSetMmThreadCharacteristics, "AvSetMmThreadCharacteristicsA");\r
- setupPTR(pAvSetMmThreadPriority, FAvSetMmThreadPriority, "AvSetMmThreadPriority");\r
-\r
- return true;\r
+ setupPTR(pAvRtCreateThreadOrderingGroup, FAvRtCreateThreadOrderingGroup, "AvRtCreateThreadOrderingGroup");\r
+ setupPTR(pAvRtDeleteThreadOrderingGroup, FAvRtDeleteThreadOrderingGroup, "AvRtDeleteThreadOrderingGroup");\r
+ setupPTR(pAvRtWaitOnThreadOrderingGroup, FAvRtWaitOnThreadOrderingGroup, "AvRtWaitOnThreadOrderingGroup");\r
+ setupPTR(pAvSetMmThreadCharacteristics, FAvSetMmThreadCharacteristics, "AvSetMmThreadCharacteristicsA");\r
+ setupPTR(pAvRevertMmThreadCharacteristics,FAvRevertMmThreadCharacteristics,"AvRevertMmThreadCharacteristics");\r
+ setupPTR(pAvSetMmThreadPriority, FAvSetMmThreadPriority, "AvSetMmThreadPriority");\r
+ \r
+ return pAvRtCreateThreadOrderingGroup && \r
+ pAvRtDeleteThreadOrderingGroup && \r
+ pAvRtWaitOnThreadOrderingGroup && \r
+ pAvSetMmThreadCharacteristics && \r
+ pAvRevertMmThreadCharacteristics && \r
+ pAvSetMmThreadPriority;\r
}\r
\r
+// ------------------------------------------------------------------------------------------\r
+static void CloseAVRT()\r
+{\r
+ if (hDInputDLL != NULL)\r
+ FreeLibrary(hDInputDLL);\r
+ hDInputDLL = NULL;\r
+}\r
\r
-\r
-PaError PaWinWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex )\r
+// ------------------------------------------------------------------------------------------\r
+PaError PaWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex )\r
{\r
- if (!setupAVRT()){\r
+ if (!SetupAVRT())\r
+ {\r
PRINT(("Windows WASAPI : No AVRT! (not VISTA?)"));\r
return paNoError;\r
}\r
CoInitialize(NULL);\r
\r
PaError result = paNoError;\r
- PaWinWasapiHostApiRepresentation *paWasapi;\r
+ PaWasapiHostApiRepresentation *paWasapi;\r
PaDeviceInfo *deviceInfoArray;\r
\r
- paWasapi = (PaWinWasapiHostApiRepresentation*)PaUtil_AllocateMemory( sizeof(PaWinWasapiHostApiRepresentation) );\r
- if( !paWasapi ){\r
+ paWasapi = (PaWasapiHostApiRepresentation*)PaUtil_AllocateMemory( sizeof(PaWasapiHostApiRepresentation) );\r
+ if (paWasapi == NULL)\r
+ {\r
result = paInsufficientMemory;\r
goto error;\r
}\r
\r
paWasapi->allocations = PaUtil_CreateAllocationGroup();\r
- if( !paWasapi->allocations ){\r
+ if (paWasapi->allocations == NULL)\r
+ {\r
result = paInsufficientMemory;\r
goto error;\r
}\r
\r
*hostApi = &paWasapi->inheritedHostApiRep;\r
- (*hostApi)->info.structVersion = 1;\r
- (*hostApi)->info.type = paWASAPI;\r
- (*hostApi)->info.name = "Windows WASAPI";\r
- (*hostApi)->info.deviceCount = 0; //so far, we must investigate each\r
- (*hostApi)->info.defaultInputDevice = paNoDevice; /* IMPLEMENT ME */\r
- (*hostApi)->info.defaultOutputDevice = paNoDevice; /* IMPLEMENT ME */\r
-\r
+ (*hostApi)->info.structVersion = 1;\r
+ (*hostApi)->info.type = paWASAPI;\r
+ (*hostApi)->info.name = "Windows WASAPI";\r
+ (*hostApi)->info.deviceCount = 0;\r
+ (*hostApi)->info.defaultInputDevice = paNoDevice;\r
+ (*hostApi)->info.defaultOutputDevice = paNoDevice;\r
\r
HRESULT hResult = S_OK;\r
- IMMDeviceCollection* spEndpoints=0;\r
+ IMMDeviceCollection* pEndPoints=0;\r
paWasapi->enumerator = 0;\r
\r
- hResult = CoCreateInstance(\r
- __uuidof(MMDeviceEnumerator), NULL,CLSCTX_INPROC_SERVER,\r
+ hResult = CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL,CLSCTX_INPROC_SERVER,\r
__uuidof(IMMDeviceEnumerator),\r
(void**)&paWasapi->enumerator);\r
-\r
IF_FAILED_JUMP(hResult, error);\r
\r
- //getting default device ids in the eMultimedia "role"\r
+ // getting default device ids in the eMultimedia "role"\r
{\r
{\r
IMMDevice* defaultRenderer=0;\r
hResult = paWasapi->enumerator->GetDefaultAudioEndpoint(eRender, eMultimedia, &defaultRenderer);\r
- IF_FAILED_JUMP(hResult, error);\r
- WCHAR* pszDeviceId = NULL;\r
- hResult = defaultRenderer->GetId(&pszDeviceId);\r
- IF_FAILED_JUMP(hResult, error);\r
- StringCchCopyW(paWasapi->defaultRenderer, MAX_STR_LEN-1, pszDeviceId);\r
- CoTaskMemFree(pszDeviceId);\r
- defaultRenderer->Release();\r
+ if (hResult != S_OK)\r
+ {\r
+ if (hResult != E_NOTFOUND)\r
+ IF_FAILED_JUMP(hResult, error);\r
+ }\r
+ else\r
+ {\r
+ WCHAR* pszDeviceId = NULL;\r
+ hResult = defaultRenderer->GetId(&pszDeviceId);\r
+ IF_FAILED_JUMP(hResult, error);\r
+ StringCchCopyW(paWasapi->defaultRenderer, MAX_STR_LEN-1, pszDeviceId);\r
+ CoTaskMemFree(pszDeviceId);\r
+ defaultRenderer->Release();\r
+ }\r
}\r
\r
{\r
IMMDevice* defaultCapturer=0;\r
hResult = paWasapi->enumerator->GetDefaultAudioEndpoint(eCapture, eMultimedia, &defaultCapturer);\r
- IF_FAILED_JUMP(hResult, error);\r
- WCHAR* pszDeviceId = NULL;\r
- hResult = defaultCapturer->GetId(&pszDeviceId);\r
- IF_FAILED_JUMP(hResult, error);\r
- StringCchCopyW(paWasapi->defaultCapturer, MAX_STR_LEN-1, pszDeviceId);\r
- CoTaskMemFree(pszDeviceId);\r
- defaultCapturer->Release();\r
+ if (hResult != S_OK)\r
+ {\r
+ if (hResult != E_NOTFOUND)\r
+ IF_FAILED_JUMP(hResult, error);\r
+ }\r
+ else\r
+ {\r
+ WCHAR* pszDeviceId = NULL;\r
+ hResult = defaultCapturer->GetId(&pszDeviceId);\r
+ IF_FAILED_JUMP(hResult, error);\r
+ StringCchCopyW(paWasapi->defaultCapturer, MAX_STR_LEN-1, pszDeviceId);\r
+ CoTaskMemFree(pszDeviceId);\r
+ defaultCapturer->Release();\r
+ }\r
}\r
}\r
\r
-\r
- hResult = paWasapi->enumerator->EnumAudioEndpoints(eAll, DEVICE_STATE_ACTIVE, &spEndpoints);\r
+ hResult = paWasapi->enumerator->EnumAudioEndpoints(eAll, DEVICE_STATE_ACTIVE, &pEndPoints);\r
IF_FAILED_JUMP(hResult, error);\r
\r
- hResult = spEndpoints->GetCount(&paWasapi->deviceCount);\r
+ hResult = pEndPoints->GetCount(&paWasapi->deviceCount);\r
IF_FAILED_JUMP(hResult, error);\r
\r
- paWasapi->devInfo = new PaWinWasapiDeviceInfo[paWasapi->deviceCount];\r
- {\r
- for (size_t step=0;step<paWasapi->deviceCount;++step)\r
- memset(&paWasapi->devInfo[step],0,sizeof(PaWinWasapiDeviceInfo));\r
- }\r
-\r
-\r
+ paWasapi->devInfo = new PaWasapiDeviceInfo[paWasapi->deviceCount];\r
+ for (UINT i = 0; i < paWasapi->deviceCount; ++i)\r
+ memset(&paWasapi->devInfo[i], 0, sizeof(PaWasapiDeviceInfo));\r
\r
- if( paWasapi->deviceCount > 0 )\r
+ if (paWasapi->deviceCount > 0)\r
{\r
(*hostApi)->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory(\r
- paWasapi->allocations, sizeof(PaDeviceInfo*) * paWasapi->deviceCount );\r
- if( !(*hostApi)->deviceInfos ){\r
+ paWasapi->allocations, sizeof(PaDeviceInfo*) * paWasapi->deviceCount);\r
+ if (!(*hostApi)->deviceInfos)\r
+ {\r
result = paInsufficientMemory;\r
goto error;\r
}\r
\r
/* allocate all device info structs in a contiguous block */\r
deviceInfoArray = (PaDeviceInfo*)PaUtil_GroupAllocateMemory(\r
- paWasapi->allocations, sizeof(PaDeviceInfo) * paWasapi->deviceCount );\r
- if( !deviceInfoArray ){\r
+ paWasapi->allocations, sizeof(PaDeviceInfo) * paWasapi->deviceCount);\r
+ if (!deviceInfoArray)\r
+ {\r
result = paInsufficientMemory;\r
goto error;\r
}\r
\r
- for( UINT i=0; i < paWasapi->deviceCount; ++i ){\r
-\r
+ for (UINT i = 0; i < paWasapi->deviceCount; ++i)\r
+ {\r
PA_DEBUG(("i:%d\n",i));\r
- PaDeviceInfo *deviceInfo = &deviceInfoArray[i];\r
+ PaDeviceInfo *deviceInfo = &deviceInfoArray[i];\r
deviceInfo->structVersion = 2;\r
- deviceInfo->hostApi = hostApiIndex;\r
+ deviceInfo->hostApi = hostApiIndex;\r
\r
- hResult = spEndpoints->Item(i, &paWasapi->devInfo[i].device);\r
+ hResult = pEndPoints->Item(i, &paWasapi->devInfo[i].device);\r
IF_FAILED_JUMP(hResult, error);\r
\r
- //getting ID\r
+ // getting ID\r
{\r
WCHAR* pszDeviceId = NULL;\r
hResult = paWasapi->devInfo[i].device->GetId(&pszDeviceId);\r
StringCchCopyW(paWasapi->devInfo[i].szDeviceID, MAX_STR_LEN-1, pszDeviceId);\r
CoTaskMemFree(pszDeviceId);\r
\r
- if (lstrcmpW(paWasapi->devInfo[i].szDeviceID, paWasapi->defaultCapturer)==0){\r
- //we found the default input!\r
+ if (lstrcmpW(paWasapi->devInfo[i].szDeviceID, paWasapi->defaultCapturer) == 0)\r
+ {// we found the default input!\r
(*hostApi)->info.defaultInputDevice = (*hostApi)->info.deviceCount;\r
}\r
- if (lstrcmpW(paWasapi->devInfo[i].szDeviceID, paWasapi->defaultRenderer)==0){\r
- //we found the default output!\r
+ if (lstrcmpW(paWasapi->devInfo[i].szDeviceID, paWasapi->defaultRenderer) == 0)\r
+ {// we found the default output!\r
(*hostApi)->info.defaultOutputDevice = (*hostApi)->info.deviceCount;\r
}\r
}\r
\r
- DWORD state=0;\r
+ DWORD state = 0;\r
hResult = paWasapi->devInfo[i].device->GetState(&paWasapi->devInfo[i].state);\r
IF_FAILED_JUMP(hResult, error);\r
\r
- if (paWasapi->devInfo[i].state != DEVICE_STATE_ACTIVE){\r
+ if (paWasapi->devInfo[i].state != DEVICE_STATE_ACTIVE)\r
+ {\r
PRINT(("WASAPI device:%d is not currently available (state:%d)\n",i,state));\r
- //spDevice->Release();\r
- //continue;\r
}\r
\r
{\r
- IPropertyStore* spProperties;\r
- hResult = paWasapi->devInfo[i].device->OpenPropertyStore(STGM_READ, &spProperties);\r
+ IPropertyStore* pProperty;\r
+ hResult = paWasapi->devInfo[i].device->OpenPropertyStore(STGM_READ, &pProperty);\r
IF_FAILED_JUMP(hResult, error);\r
\r
- //getting "Friendly" Name\r
+ // "Friendly" Name\r
{\r
PROPVARIANT value;\r
PropVariantInit(&value);\r
- hResult = spProperties->GetValue(PKEY_Device_FriendlyName, &value);\r
+ hResult = pProperty->GetValue(PKEY_Device_FriendlyName, &value);\r
IF_FAILED_JUMP(hResult, error);\r
- deviceInfo->name = 0;\r
- char* deviceName = (char*)PaUtil_GroupAllocateMemory( paWasapi->allocations, MAX_STR_LEN + 1 );\r
- if( !deviceName ){\r
+ deviceInfo->name = NULL;\r
+ char *deviceName = (char *)PaUtil_GroupAllocateMemory( paWasapi->allocations, MAX_STR_LEN + 1 );\r
+ if (!deviceName)\r
+ {\r
result = paInsufficientMemory;\r
goto error;\r
}\r
if (value.pwszVal)\r
- wcstombs(deviceName, value.pwszVal,MAX_STR_LEN-1); //todo proper size \r
- else{\r
+ wcstombs(deviceName, value.pwszVal,MAX_STR_LEN-1);\r
+ else\r
_snprintf_s(deviceName,MAX_STR_LEN-1,MAX_STR_LEN-1,"baddev%d",i);\r
- }\r
\r
deviceInfo->name = deviceName;\r
PropVariantClear(&value);\r
}\r
\r
-#if 0\r
- DWORD numProps = 0;\r
- hResult = spProperties->GetCount(&numProps);\r
- IF_FAILED_JUMP(hResult, error);\r
+ // Default format\r
{\r
- for (DWORD i=0;i<numProps;++i){\r
- PROPERTYKEY pkey;\r
- hResult = spProperties->GetAt(i,&pkey);\r
-\r
- PROPVARIANT value;\r
- PropVariantInit(&value);\r
- hResult = spProperties->GetValue(pkey, &value);\r
-\r
- switch(value.vt){\r
- case 11:\r
- PRINT(("property*%u*\n",value.ulVal));\r
- break;\r
- case 19:\r
- PRINT(("property*%d*\n",value.boolVal));\r
- break;\r
- case 31:\r
- {\r
- char temp[512];\r
- wcstombs(temp, value.pwszVal,MAX_STR_LEN-1);\r
- PRINT(("property*%s*\n",temp));\r
- }\r
- break;\r
- default:break;\r
- }\r
-\r
- PropVariantClear(&value);\r
- }\r
+ PROPVARIANT value;\r
+ PropVariantInit(&value);\r
+ hResult = pProperty->GetValue(PKEY_AudioEngine_DeviceFormat, &value);\r
+ IF_FAILED_JUMP(hResult, error);\r
+ // get\r
+ WAVEFORMATEXTENSIBLE format = { 0 };\r
+ memcpy(&format, value.blob.pBlobData, min(sizeof(format), value.blob.cbSize));\r
+ // set\r
+ paWasapi->devInfo[i].DefaultFormat = format;\r
+ // cleanup\r
+ PropVariantClear(&value);\r
+ }\r
+\r
+ // Formfactor\r
+ {\r
+ PROPVARIANT value;\r
+ PropVariantInit(&value);\r
+ hResult = pProperty->GetValue(PKEY_AudioEndpoint_FormFactor, &value);\r
+ IF_FAILED_JUMP(hResult, error);\r
+ // set\r
+ paWasapi->devInfo[i].formFactor = (EndpointFormFactor)value.uintVal;\r
+ // cleanup\r
+ PropVariantClear(&value);\r
}\r
-#endif\r
\r
- /* These look interresting... but they are undocumented\r
- PKEY_AudioEndpoint_FormFactor\r
- PKEY_AudioEndpoint_ControlPanelPageProvider\r
- PKEY_AudioEndpoint_Association\r
- PKEY_AudioEndpoint_PhysicalSpeakerConfig\r
- PKEY_AudioEngine_DeviceFormat\r
- */\r
- spProperties->Release();\r
+ pProperty->Release();\r
}\r
\r
\r
- //getting the Endpoint data\r
+ // Endpoint data\r
{\r
- IMMEndpoint *endpoint=0;\r
- hResult = paWasapi->devInfo[i].device->QueryInterface(__uuidof(IMMEndpoint),(void **)&endpoint);\r
- if (SUCCEEDED(hResult)){\r
+ IMMEndpoint *endpoint = NULL;\r
+ hResult = paWasapi->devInfo[i].device->QueryInterface(__uuidof(IMMEndpoint), (void **)&endpoint);\r
+ if (SUCCEEDED(hResult))\r
+ {\r
hResult = endpoint->GetDataFlow(&paWasapi->devInfo[i].flow);\r
endpoint->Release();\r
}\r
}\r
\r
- //Getting a temporary IAudioDevice for more fields\r
- //we make sure NOT to call Initialize yet!\r
+ // Getting a temporary IAudioClient for more fields\r
+ // we make sure NOT to call Initialize yet!\r
{\r
- IAudioClient *myClient=0;\r
+ IAudioClient *tmpClient = NULL;\r
\r
- hResult = paWasapi->devInfo[i].device->Activate(__uuidof(IAudioClient), CLSCTX_INPROC_SERVER, NULL, (void**)&myClient);\r
+ hResult = paWasapi->devInfo[i].device->Activate(__uuidof(IAudioClient), \r
+ CLSCTX_INPROC_SERVER, NULL, (void**)&tmpClient);\r
IF_FAILED_JUMP(hResult, error);\r
\r
- hResult = myClient->GetDevicePeriod(\r
+ hResult = tmpClient->GetDevicePeriod(\r
&paWasapi->devInfo[i].DefaultDevicePeriod,\r
&paWasapi->devInfo[i].MinimumDevicePeriod);\r
IF_FAILED_JUMP(hResult, error);\r
\r
- hResult = myClient->GetMixFormat(&paWasapi->devInfo[i].MixFormat);\r
+ //hResult = tmpClient->GetMixFormat(&paWasapi->devInfo[i].MixFormat);\r
\r
- if (hResult != S_OK){\r
- /*davidv: this happened with my hardware, previously for that same device in DirectSound:\r
- Digital Output (Realtek AC'97 Audio)'s GUID: {0x38f2cf50,0x7b4c,0x4740,0x86,0xeb,0xd4,0x38,0x66,0xd8,0xc8, 0x9f} \r
- so something must be _really_ wrong with this device, TODO handle this better. We kind of need GetMixFormat*/\r
- logAUDCLNT_E(hResult);\r
+ // Release client\r
+ tmpClient->Release();\r
+\r
+ if (hResult != S_OK)\r
+ {\r
+ //davidv: this happened with my hardware, previously for that same device in DirectSound:\r
+ //Digital Output (Realtek AC'97 Audio)'s GUID: {0x38f2cf50,0x7b4c,0x4740,0x86,0xeb,0xd4,0x38,0x66,0xd8,0xc8, 0x9f}\r
+ //so something must be _really_ wrong with this device, TODO handle this better. We kind of need GetMixFormat\r
+ LogHostError(hResult);\r
goto error;\r
}\r
-\r
- myClient->Release();\r
}\r
\r
- //we can now fill in portaudio device data\r
+ // we can now fill in portaudio device data\r
deviceInfo->maxInputChannels = 0; //for now\r
deviceInfo->maxOutputChannels = 0; //for now\r
-\r
- switch(paWasapi->devInfo[i].flow){\r
- case eRender:\r
- //hum not exaclty maximum, more like "default"\r
- deviceInfo->maxOutputChannels = paWasapi->devInfo[i].MixFormat->nChannels;\r
-\r
- deviceInfo->defaultHighOutputLatency = nano100ToSeconds(paWasapi->devInfo[i].DefaultDevicePeriod);\r
- deviceInfo->defaultLowOutputLatency = nano100ToSeconds(paWasapi->devInfo[i].MinimumDevicePeriod);\r
- break;\r
- case eCapture:\r
- //hum not exaclty maximum, more like "default"\r
- deviceInfo->maxInputChannels = paWasapi->devInfo[i].MixFormat->nChannels;\r
-\r
- deviceInfo->defaultHighInputLatency = nano100ToSeconds(paWasapi->devInfo[i].DefaultDevicePeriod);\r
- deviceInfo->defaultLowInputLatency = nano100ToSeconds(paWasapi->devInfo[i].MinimumDevicePeriod);\r
- break;\r
- default:\r
- PRINT(("WASAPI device:%d bad Data FLow! \n",i));\r
- goto error;\r
- break;\r
+ switch (paWasapi->devInfo[i].flow)\r
+ {\r
+ case eRender:\r
+ // WASAPI accepts exact channels count\r
+ deviceInfo->maxOutputChannels = paWasapi->devInfo[i].DefaultFormat.Format.nChannels;\r
+ deviceInfo->defaultHighOutputLatency = nano100ToSeconds(paWasapi->devInfo[i].DefaultDevicePeriod);\r
+ deviceInfo->defaultLowOutputLatency = nano100ToSeconds(paWasapi->devInfo[i].MinimumDevicePeriod);\r
+ break;\r
+ case eCapture:\r
+ // WASAPI accepts exact channels count\r
+ deviceInfo->maxInputChannels = paWasapi->devInfo[i].DefaultFormat.Format.nChannels;\r
+ deviceInfo->defaultHighInputLatency = nano100ToSeconds(paWasapi->devInfo[i].DefaultDevicePeriod);\r
+ deviceInfo->defaultLowInputLatency = nano100ToSeconds(paWasapi->devInfo[i].MinimumDevicePeriod);\r
+ break;\r
+ default:\r
+ PRINT(("WASAPI device:%d bad Data FLow! \n",i));\r
+ goto error;\r
+ break;\r
}\r
\r
- deviceInfo->defaultSampleRate = (double)paWasapi->devInfo[i].MixFormat->nSamplesPerSec;\r
+ deviceInfo->defaultSampleRate = paWasapi->devInfo[i].DefaultFormat.Format.nSamplesPerSec;\r
\r
(*hostApi)->deviceInfos[i] = deviceInfo;\r
++(*hostApi)->info.deviceCount;\r
}\r
}\r
\r
- spEndpoints->Release();\r
-\r
(*hostApi)->Terminate = Terminate;\r
(*hostApi)->OpenStream = OpenStream;\r
(*hostApi)->IsFormatSupported = IsFormatSupported;\r
GetStreamTime, PaUtil_DummyGetCpuLoad,\r
ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable );\r
\r
+ SAFE_RELEASE(pEndPoints);\r
+\r
return result;\r
\r
error:\r
\r
- if (spEndpoints)\r
- spEndpoints->Release();\r
-\r
- if (paWasapi->enumerator)\r
- paWasapi->enumerator->Release();\r
-\r
- if( paWasapi )\r
- {\r
- if( paWasapi->allocations )\r
- {\r
- PaUtil_FreeAllAllocations( paWasapi->allocations );\r
- PaUtil_DestroyAllocationGroup( paWasapi->allocations );\r
- }\r
-\r
- PaUtil_FreeMemory( paWasapi );\r
- }\r
+ SAFE_RELEASE(pEndPoints);\r
+ \r
+ Terminate((PaUtilHostApiRepresentation *)paWasapi);\r
+ \r
return result;\r
}\r
\r
-\r
-static void Terminate( struct PaUtilHostApiRepresentation *hostApi )\r
+// ------------------------------------------------------------------------------------------\r
+static void Terminate( PaUtilHostApiRepresentation *hostApi )\r
{\r
- PaWinWasapiHostApiRepresentation *paWasapi = (PaWinWasapiHostApiRepresentation*)hostApi;\r
-\r
- paWasapi->enumerator->Release();\r
+ PaWasapiHostApiRepresentation *paWasapi = (PaWasapiHostApiRepresentation*)hostApi;\r
+ if (paWasapi == NULL)\r
+ return;\r
\r
- for (UINT i=0;i<paWasapi->deviceCount;++i){\r
- PaWinWasapiDeviceInfo *info = &paWasapi->devInfo[i];\r
+ // Release IMMDeviceEnumerator\r
+ SAFE_RELEASE(paWasapi->enumerator);\r
\r
- if (info->device)\r
- info->device->Release();\r
+ for (UINT i = 0; i < paWasapi->deviceCount; ++i)\r
+ {\r
+ PaWasapiDeviceInfo *info = &paWasapi->devInfo[i];\r
+ SAFE_RELEASE(info->device);\r
\r
- if (info->MixFormat)\r
- CoTaskMemFree(info->MixFormat);\r
+ //if (info->MixFormat)\r
+ // CoTaskMemFree(info->MixFormat);\r
}\r
delete [] paWasapi->devInfo;\r
\r
CoUninitialize();\r
\r
- if( paWasapi->allocations ){\r
- PaUtil_FreeAllAllocations( paWasapi->allocations );\r
- PaUtil_DestroyAllocationGroup( paWasapi->allocations );\r
+ if (paWasapi->allocations)\r
+ {\r
+ PaUtil_FreeAllAllocations(paWasapi->allocations);\r
+ PaUtil_DestroyAllocationGroup(paWasapi->allocations);\r
}\r
\r
- PaUtil_FreeMemory( paWasapi );\r
+ PaUtil_FreeMemory(paWasapi);\r
+\r
+ // Close AVRT\r
+ CloseAVRT();\r
}\r
\r
-static void\r
-LogWAVEFORMATEXTENSIBLE(const WAVEFORMATEXTENSIBLE *in){\r
+// ------------------------------------------------------------------------------------------\r
+static inline PaWasapiHostApiRepresentation *GetHostApi(PaError *_error = NULL)\r
+{\r
+ PaError error;\r
+\r
+ PaUtilHostApiRepresentation *pApi;\r
+ if ((error = PaUtil_GetHostApiRepresentation(&pApi, paWASAPI)) != paNoError)\r
+ {\r
+ if (_error != NULL)\r
+ (*_error) = error;\r
\r
- const WAVEFORMATEX *old = (WAVEFORMATEX *)in;\r
+ return NULL;\r
+ }\r
+ return (PaWasapiHostApiRepresentation *)pApi;\r
+}\r
\r
- switch (old->wFormatTag){\r
- case WAVE_FORMAT_EXTENSIBLE:{\r
+// ------------------------------------------------------------------------------------------\r
+int PaWasapi_GetDeviceDefaultFormat( void *pFormat, int nFormatSize, PaDeviceIndex nDevice )\r
+{\r
+ if (pFormat == NULL)\r
+ return paBadBufferPtr;\r
+ if (nFormatSize <= 0)\r
+ return paBufferTooSmall;\r
+ \r
+ PaError ret;\r
\r
- PRINT(("wFormatTag=WAVE_FORMAT_EXTENSIBLE\n"));\r
+ // Get API\r
+ PaWasapiHostApiRepresentation *paWasapi = GetHostApi(&ret);\r
+ if (paWasapi == NULL)\r
+ return ret;\r
\r
- if (in->SubFormat == KSDATAFORMAT_SUBTYPE_IEEE_FLOAT){\r
- PRINT(("SubFormat=KSDATAFORMAT_SUBTYPE_IEEE_FLOAT\n"));\r
- }\r
- else if (in->SubFormat == KSDATAFORMAT_SUBTYPE_PCM){\r
- PRINT(("SubFormat=KSDATAFORMAT_SUBTYPE_PCM\n"));\r
- }\r
- else{\r
- PRINT(("SubFormat=CUSTOM GUID{%d:%d:%d:%d%d%d%d%d%d%d%d}\n", \r
- in->SubFormat.Data1,\r
- in->SubFormat.Data2,\r
- in->SubFormat.Data3,\r
- (int)in->SubFormat.Data4[0],\r
- (int)in->SubFormat.Data4[1],\r
- (int)in->SubFormat.Data4[2],\r
- (int)in->SubFormat.Data4[3],\r
- (int)in->SubFormat.Data4[4],\r
- (int)in->SubFormat.Data4[5],\r
- (int)in->SubFormat.Data4[6],\r
- (int)in->SubFormat.Data4[7]));\r
- }\r
- PRINT(("Samples.wValidBitsPerSample=%d\n", in->Samples.wValidBitsPerSample));\r
- PRINT(("dwChannelMask=0x%X\n",in->dwChannelMask));\r
- }break;\r
- \r
- case WAVE_FORMAT_PCM: PRINT(("wFormatTag=WAVE_FORMAT_PCM\n")); break;\r
- case WAVE_FORMAT_IEEE_FLOAT: PRINT(("wFormatTag=WAVE_FORMAT_IEEE_FLOAT\n")); break;\r
- default : PRINT(("wFormatTag=UNKNOWN(%d)\n",old->wFormatTag)); break;\r
- }\r
+ if (nDevice >= (int)paWasapi->deviceCount)\r
+ return paInvalidDevice;\r
\r
- PRINT(("nChannels =%d\n",old->nChannels)); \r
- PRINT(("nSamplesPerSec =%d\n",old->nSamplesPerSec)); \r
- PRINT(("nAvgBytesPerSec=%d\n",old->nAvgBytesPerSec)); \r
- PRINT(("nBlockAlign =%d\n",old->nBlockAlign)); \r
- PRINT(("wBitsPerSample =%d\n",old->wBitsPerSample)); \r
- PRINT(("cbSize =%d\n",old->cbSize)); \r
+ int size = min(nFormatSize, sizeof(paWasapi->devInfo[ nDevice ].DefaultFormat));\r
+ memcpy(pFormat, &paWasapi->devInfo[ nDevice ].DefaultFormat, size);\r
+\r
+ return size;\r
}\r
\r
+// ------------------------------------------------------------------------------------------\r
+int PaWasapi_GetDeviceRole( PaDeviceIndex nDevice )\r
+{\r
+ PaError ret;\r
\r
+ // Get API\r
+ PaWasapiHostApiRepresentation *paWasapi = GetHostApi(&ret);\r
+ if (paWasapi == NULL)\r
+ return ret;\r
\r
-/*\r
- WAVEFORMATXXX is always interleaved\r
- */\r
-static PaSampleFormat\r
-waveformatToPaFormat(const WAVEFORMATEXTENSIBLE *in){\r
+ if (nDevice >= (int)paWasapi->deviceCount)\r
+ return paInvalidDevice;\r
\r
- const WAVEFORMATEX *old = (WAVEFORMATEX*)in;\r
+ return paWasapi->devInfo[ nDevice ].formFactor;\r
+}\r
\r
- switch (old->wFormatTag){\r
+// ------------------------------------------------------------------------------------------\r
+static void LogWAVEFORMATEXTENSIBLE(const WAVEFORMATEXTENSIBLE *in)\r
+{\r
+ const WAVEFORMATEX *old = (WAVEFORMATEX *)in;\r
\r
- case WAVE_FORMAT_EXTENSIBLE:\r
- {\r
- if (in->SubFormat == KSDATAFORMAT_SUBTYPE_IEEE_FLOAT){\r
- if (in->Samples.wValidBitsPerSample == 32)\r
- return paFloat32;\r
- else\r
- return paCustomFormat;\r
- }\r
- else if (in->SubFormat == KSDATAFORMAT_SUBTYPE_PCM){\r
- switch (old->wBitsPerSample){\r
- case 32: return paInt32; break;\r
- case 24: return paInt24;break;\r
- case 8: return paUInt8;break;\r
- case 16: return paInt16;break;\r
- default: return paCustomFormat;break;\r
- }\r
- }\r
+ switch (old->wFormatTag)\r
+ {\r
+ case WAVE_FORMAT_EXTENSIBLE: {\r
+\r
+ PRINT(("wFormatTag=WAVE_FORMAT_EXTENSIBLE\n"));\r
+\r
+ if (in->SubFormat == KSDATAFORMAT_SUBTYPE_IEEE_FLOAT){\r
+ PRINT(("SubFormat=KSDATAFORMAT_SUBTYPE_IEEE_FLOAT\n"));\r
+ }\r
+ else if (in->SubFormat == KSDATAFORMAT_SUBTYPE_PCM){\r
+ PRINT(("SubFormat=KSDATAFORMAT_SUBTYPE_PCM\n"));\r
+ }\r
+ else{\r
+ PRINT(("SubFormat=CUSTOM GUID{%d:%d:%d:%d%d%d%d%d%d%d%d}\n",\r
+ in->SubFormat.Data1,\r
+ in->SubFormat.Data2,\r
+ in->SubFormat.Data3,\r
+ (int)in->SubFormat.Data4[0],\r
+ (int)in->SubFormat.Data4[1],\r
+ (int)in->SubFormat.Data4[2],\r
+ (int)in->SubFormat.Data4[3],\r
+ (int)in->SubFormat.Data4[4],\r
+ (int)in->SubFormat.Data4[5],\r
+ (int)in->SubFormat.Data4[6],\r
+ (int)in->SubFormat.Data4[7]));\r
+ }\r
+ PRINT(("Samples.wValidBitsPerSample=%d\n", in->Samples.wValidBitsPerSample));\r
+ PRINT(("dwChannelMask =0x%X\n",in->dwChannelMask));\r
+\r
+ break; }\r
+\r
+ case WAVE_FORMAT_PCM: PRINT(("wFormatTag=WAVE_FORMAT_PCM\n")); break;\r
+ case WAVE_FORMAT_IEEE_FLOAT: PRINT(("wFormatTag=WAVE_FORMAT_IEEE_FLOAT\n")); break;\r
+ default : PRINT(("wFormatTag=UNKNOWN(%d)\n",old->wFormatTag)); break;\r
+ }\r
+\r
+ PRINT(("nChannels =%d\n",old->nChannels));\r
+ PRINT(("nSamplesPerSec =%d\n",old->nSamplesPerSec));\r
+ PRINT(("nAvgBytesPerSec=%d\n",old->nAvgBytesPerSec));\r
+ PRINT(("nBlockAlign =%d\n",old->nBlockAlign));\r
+ PRINT(("wBitsPerSample =%d\n",old->wBitsPerSample));\r
+ PRINT(("cbSize =%d\n",old->cbSize));\r
+}\r
+\r
+// ------------------------------------------------------------------------------------------\r
+static PaSampleFormat waveformatToPaFormat(const WAVEFORMATEXTENSIBLE *in)\r
+{\r
+ const WAVEFORMATEX *old = (WAVEFORMATEX*)in;\r
+\r
+ switch (old->wFormatTag)\r
+ {\r
+ case WAVE_FORMAT_EXTENSIBLE:\r
+ {\r
+ if (in->SubFormat == KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)\r
+ {\r
+ if (in->Samples.wValidBitsPerSample == 32)\r
+ return paFloat32;\r
else\r
return paCustomFormat;\r
}\r
- break;\r
-\r
- case WAVE_FORMAT_IEEE_FLOAT:\r
- return paFloat32;\r
- break;\r
-\r
- case WAVE_FORMAT_PCM:\r
- {\r
+ else \r
+ if (in->SubFormat == KSDATAFORMAT_SUBTYPE_PCM)\r
+ {\r
switch (old->wBitsPerSample){\r
case 32: return paInt32; break;\r
case 24: return paInt24;break;\r
default: return paCustomFormat;break;\r
}\r
}\r
- break;\r
-\r
- default:\r
+ else\r
return paCustomFormat;\r
- break;\r
+ }\r
+ break;\r
+\r
+ case WAVE_FORMAT_IEEE_FLOAT:\r
+ return paFloat32;\r
+ break;\r
+\r
+ case WAVE_FORMAT_PCM:\r
+ {\r
+ switch (old->wBitsPerSample)\r
+ {\r
+ case 32: return paInt32; break;\r
+ case 24: return paInt24;break;\r
+ case 8: return paUInt8;break;\r
+ case 16: return paInt16;break;\r
+ default: return paCustomFormat;break;\r
+ }\r
+ }\r
+ break;\r
+\r
+ default:\r
+ return paCustomFormat;\r
+ break;\r
}\r
\r
return paCustomFormat;\r
}\r
\r
+// ------------------------------------------------------------------------------------------\r
+static PaError MakeWaveFormatFromParams(WAVEFORMATEXTENSIBLE *wavex, const PaStreamParameters *params,\r
+ double sampleRate)\r
+{\r
+ PaWasapiStreamInfo *streamInfo = (PaWasapiStreamInfo *)params->hostApiSpecificStreamInfo;\r
\r
+ // Get user assigned channel mask\r
+ DWORD channelMask = 0;\r
+ if ((streamInfo != NULL) && (streamInfo->flags & paWinWasapiUseChannelMask))\r
+ channelMask = streamInfo->channelMask;\r
\r
-static PaError\r
-waveformatFromParams(WAVEFORMATEXTENSIBLE*wavex,\r
- const PaStreamParameters * params,\r
- double sampleRate){\r
-\r
- size_t bytesPerSample = 0;\r
- switch( params->sampleFormat & ~paNonInterleaved ){\r
- case paFloat32:\r
- case paInt32: bytesPerSample=4;break;\r
- case paInt16: bytesPerSample=2;break;\r
- case paInt24: bytesPerSample=3;break;\r
- case paInt8:\r
- case paUInt8: bytesPerSample=1;break;\r
- case paCustomFormat:\r
- default: return paSampleFormatNotSupported;break;\r
- }\r
+ // Convert PaSampleFormat to bits per sample\r
+ WORD bitsPerSample;\r
+ if ((bitsPerSample = PaSampleFormatToBitsPerSample(params->sampleFormat)) == 0)\r
+ return paSampleFormatNotSupported;\r
\r
- memset(wavex,0,sizeof(WAVEFORMATEXTENSIBLE));\r
+ memset(wavex, 0, sizeof(*wavex));\r
\r
WAVEFORMATEX *old = (WAVEFORMATEX *)wavex;\r
- old->nChannels = (WORD)params->channelCount;\r
+ old->nChannels = params->channelCount;\r
old->nSamplesPerSec = (DWORD)sampleRate;\r
- old->wBitsPerSample = (WORD)(bytesPerSample*8);\r
- old->nAvgBytesPerSec = (DWORD)(old->nSamplesPerSec * old->nChannels * bytesPerSample);\r
- old->nBlockAlign = (WORD)(old->nChannels * bytesPerSample);\r
+ if ((old->wBitsPerSample = bitsPerSample) > 16)\r
+ {\r
+ old->wBitsPerSample = 32; // 20 or 24 bits must go in 32 bit containers (ints)\r
+ }\r
+ old->nBlockAlign = (old->nChannels * (old->wBitsPerSample/8));\r
+ old->nAvgBytesPerSec = (old->nSamplesPerSec * old->nBlockAlign);\r
\r
//WAVEFORMATEX\r
- if (params->channelCount <=2 && (bytesPerSample == 2 || bytesPerSample == 1)){\r
- old->cbSize = 0;\r
- old->wFormatTag = WAVE_FORMAT_PCM;\r
+ /*if ((params->channelCount <= 2) && ((bitsPerSample == 16) || (bitsPerSample == 8)))\r
+ {\r
+ old->cbSize = 0;\r
+ old->wFormatTag = WAVE_FORMAT_PCM;\r
}\r
//WAVEFORMATEXTENSIBLE\r
- else{\r
+ else*/\r
+ {\r
old->wFormatTag = WAVE_FORMAT_EXTENSIBLE;\r
-\r
- old->cbSize = sizeof (WAVEFORMATEXTENSIBLE) - sizeof (WAVEFORMATEX);\r
+ old->cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);\r
\r
if ((params->sampleFormat & ~paNonInterleaved) == paFloat32)\r
wavex->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;\r
else\r
wavex->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;\r
\r
- wavex->Samples.wValidBitsPerSample = old->wBitsPerSample; //no extra padding!\r
-\r
- switch(params->channelCount){\r
- case 1: wavex->dwChannelMask = SPEAKER_FRONT_CENTER; break;\r
- case 2: wavex->dwChannelMask = 0x1 | 0x2; break;\r
- case 4: wavex->dwChannelMask = 0x1 | 0x2 | 0x10 | 0x20; break;\r
- case 6: wavex->dwChannelMask = 0x1 | 0x2 | 0x4 | 0x8 | 0x10 | 0x20; break;\r
- case 8: wavex->dwChannelMask = 0x1 | 0x2 | 0x4 | 0x8 | 0x10 | 0x20 | 0x40 | 0x80; break;\r
- default: wavex->dwChannelMask = 0; break;\r
- }\r
- }\r
+ wavex->Samples.wValidBitsPerSample = bitsPerSample; //no extra padding!\r
\r
+ // Set channel mask\r
+ if (channelMask != 0)\r
+ {\r
+ wavex->dwChannelMask = channelMask;\r
+ }\r
+ else\r
+ {\r
+ switch (params->channelCount)\r
+ {\r
+ case 1: wavex->dwChannelMask = KSAUDIO_SPEAKER_MONO; break;\r
+ case 2: wavex->dwChannelMask = KSAUDIO_SPEAKER_STEREO; break;\r
+ case 4: wavex->dwChannelMask = KSAUDIO_SPEAKER_QUAD; break;\r
+ case 6: wavex->dwChannelMask = KSAUDIO_SPEAKER_5POINT1; break;\r
+ case 8: wavex->dwChannelMask = KSAUDIO_SPEAKER_7POINT1; break;\r
+ default: wavex->dwChannelMask = 0; break;\r
+ }\r
+ }\r
+ }\r
return paNoError;\r
}\r
\r
-\r
-\r
-\r
-\r
-\r
-/*\r
-#define paFloat32 ((PaSampleFormat) 0x00000001) \r
-#define paInt32 ((PaSampleFormat) 0x00000002)\r
-#define paInt24 ((PaSampleFormat) 0x00000004) \r
-#define paInt16 ((PaSampleFormat) 0x00000008) \r
-*/\r
-//lifted from pa_wdmks\r
-static void wasapiFillWFEXT( WAVEFORMATEXTENSIBLE* pwfext, PaSampleFormat sampleFormat, double sampleRate, int channelCount)\r
-{\r
- PA_DEBUG(( "sampleFormat = %lx\n" , sampleFormat ));\r
- PA_DEBUG(( "sampleRate = %f\n" , sampleRate ));\r
- PA_DEBUG(( "chanelCount = %d\n", channelCount ));\r
+// ------------------------------------------------------------------------------------------\r
+static void wasapiFillWFEXT( WAVEFORMATEXTENSIBLE* pwfext, PaSampleFormat sampleFormat, double sampleRate, int channelCount)\r
+{\r
+ PA_DEBUG(( "sampleFormat = %lx\n" , sampleFormat ));\r
+ PA_DEBUG(( "sampleRate = %f\n" , sampleRate ));\r
+ PA_DEBUG(( "chanelCount = %d\n", channelCount ));\r
\r
pwfext->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;\r
pwfext->Format.nChannels = channelCount;\r
}\r
else if(sampleFormat == paInt24)\r
{\r
- pwfext->Format.nBlockAlign = channelCount * 3;\r
- pwfext->Format.wBitsPerSample = 24;\r
+ pwfext->Format.nBlockAlign = channelCount * 4;\r
+ pwfext->Format.wBitsPerSample = 32; // 24-bit in 32-bit int container\r
pwfext->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX);\r
pwfext->Samples.wValidBitsPerSample = 24;\r
pwfext->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;\r
pwfext->Format.nAvgBytesPerSec = pwfext->Format.nSamplesPerSec * pwfext->Format.nBlockAlign;\r
}\r
\r
-\r
-\r
-/*\r
-#define FORMATTESTS 4\r
-const int BestToWorst[FORMATTESTS]={paFloat32,paInt32,paInt24,paInt16};\r
-*/\r
-\r
-#define FORMATTESTS 3\r
-const int BestToWorst[FORMATTESTS]={paFloat32,paInt24,paInt16};\r
-\r
-\r
-static PaError\r
-GetClosestFormat(IAudioClient * myClient, double sampleRate,const PaStreamParameters * params, \r
- AUDCLNT_SHAREMODE *shareMode, WAVEFORMATEXTENSIBLE *outWavex)\r
+// ------------------------------------------------------------------------------------------\r
+static PaError GetClosestFormat(IAudioClient *myClient, double sampleRate, \r
+ const PaStreamParameters *params, AUDCLNT_SHAREMODE shareMode, WAVEFORMATEXTENSIBLE *outWavex)\r
{\r
- //TODO we should try exclusive first and shared after\r
- *shareMode = PORTAUDIO_SHAREMODE;\r
-\r
PaError answer = paInvalidSampleRate;\r
\r
- waveformatFromParams(outWavex,params,sampleRate);\r
- WAVEFORMATEX *sharedClosestMatch=0;\r
- HRESULT hResult=!S_OK;\r
-\r
- if (*shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE)\r
- hResult = myClient->IsFormatSupported(AUDCLNT_SHAREMODE_EXCLUSIVE,&outWavex->Format,NULL);\r
- else\r
- hResult = myClient->IsFormatSupported(AUDCLNT_SHAREMODE_SHARED, &outWavex->Format,&sharedClosestMatch);\r
+ MakeWaveFormatFromParams(outWavex,params,sampleRate);\r
+ WAVEFORMATEX *sharedClosestMatch = 0;\r
+ HRESULT hResult = !S_OK;\r
\r
+ hResult = myClient->IsFormatSupported(shareMode, &outWavex->Format, (shareMode == AUDCLNT_SHAREMODE_SHARED ? &sharedClosestMatch : NULL));\r
if (hResult == S_OK)\r
answer = paFormatIsSupported;\r
- else if (sharedClosestMatch){\r
- WAVEFORMATEXTENSIBLE* ext = (WAVEFORMATEXTENSIBLE*)sharedClosestMatch;\r
- \r
- int closestMatchSR = (int)sharedClosestMatch->nSamplesPerSec;\r
+ else\r
+ if (sharedClosestMatch)\r
+ {\r
+ WAVEFORMATEXTENSIBLE *ext = (WAVEFORMATEXTENSIBLE*)sharedClosestMatch;\r
\r
+ GUID subf_guid = GUID_NULL;\r
if (sharedClosestMatch->wFormatTag == WAVE_FORMAT_EXTENSIBLE)\r
- memcpy(outWavex,sharedClosestMatch,sizeof(WAVEFORMATEXTENSIBLE));\r
+ {\r
+ memcpy(outWavex, sharedClosestMatch, sizeof(WAVEFORMATEXTENSIBLE));\r
+ subf_guid = ext->SubFormat;\r
+ }\r
else\r
- memcpy(outWavex,sharedClosestMatch,sizeof(WAVEFORMATEX));\r
+ memcpy(outWavex, sharedClosestMatch, sizeof(WAVEFORMATEX));\r
\r
CoTaskMemFree(sharedClosestMatch);\r
\r
- if ((int)sampleRate == closestMatchSR)\r
+ // Make supported by default\r
answer = paFormatIsSupported;\r
- else\r
- answer = paInvalidSampleRate;\r
- \r
- }else {\r
\r
+ // Validate SampleRate\r
+ if ((DWORD)sampleRate != outWavex->Format.nSamplesPerSec)\r
+ return paInvalidSampleRate;\r
+\r
+ // Validate Channel count\r
+ if ((WORD)params->channelCount != outWavex->Format.nChannels)\r
+ return paInvalidChannelCount;\r
+\r
+ // Validate Sample format\r
+ WORD bitsPerSample;\r
+ if ((bitsPerSample = PaSampleFormatToBitsPerSample(params->sampleFormat)) == 0)\r
+ return paSampleFormatNotSupported;\r
+\r
+ // Validate Sample format: bit size (WASAPI does not limit 'bit size')\r
+ //if (bitsPerSample != outWavex->Format.wBitsPerSample)\r
+ // return paSampleFormatNotSupported;\r
+\r
+ // Validate Sample format: paFloat32 (WASAPI does not limit 'bit type')\r
+ //if ((params->sampleFormat == paFloat32) && (subf_guid != KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))\r
+ // return paSampleFormatNotSupported;\r
+\r
+ // Validate Sample format: paInt32 (WASAPI does not limit 'bit type')\r
+ //if ((params->sampleFormat == paInt32) && (subf_guid != KSDATAFORMAT_SUBTYPE_PCM))\r
+ // return paSampleFormatNotSupported;\r
+ }\r
+ else\r
+ {\r
//it doesnt suggest anything?? ok lets show it the MENU!\r
\r
+#define FORMATTESTS 3\r
+static const int BestToWorst[FORMATTESTS]={ paFloat32, paInt24, paInt16 };\r
+\r
//ok fun time as with pa_win_mme, we know only a refusal of the user-requested\r
//sampleRate+num Channel is disastrous, as the portaudio buffer processor converts between anything\r
- //so lets only use the number \r
- for (int i=0;i<FORMATTESTS;++i){\r
- WAVEFORMATEXTENSIBLE ext;\r
- wasapiFillWFEXT(&ext,BestToWorst[i],sampleRate,params->channelCount); \r
- if (*shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE)\r
- hResult = myClient->IsFormatSupported(AUDCLNT_SHAREMODE_EXCLUSIVE,&ext.Format,NULL);\r
- else\r
- hResult = myClient->IsFormatSupported(AUDCLNT_SHAREMODE_SHARED, &ext.Format,&sharedClosestMatch);\r
-\r
- if (hResult == S_OK){\r
+ //so lets only use the number\r
+ for (int i = 0; i < FORMATTESTS; ++i)\r
+ {\r
+ WAVEFORMATEXTENSIBLE ext = { 0 };\r
+ wasapiFillWFEXT(&ext,BestToWorst[i],sampleRate,params->channelCount);\r
+ \r
+ hResult = myClient->IsFormatSupported(shareMode, &ext.Format, (shareMode == AUDCLNT_SHAREMODE_SHARED ? &sharedClosestMatch : NULL));\r
+ if (hResult == S_OK)\r
+ {\r
memcpy(outWavex,&ext,sizeof(WAVEFORMATEXTENSIBLE));\r
answer = paFormatIsSupported;\r
break;\r
}\r
}\r
\r
- if (answer!=paFormatIsSupported) {\r
- //try MIX format?\r
- //why did it HAVE to come to this ....\r
- WAVEFORMATEX pcm16WaveFormat;\r
- memset(&pcm16WaveFormat,0,sizeof(WAVEFORMATEX));\r
- pcm16WaveFormat.wFormatTag = WAVE_FORMAT_PCM; \r
- pcm16WaveFormat.nChannels = 2; \r
- pcm16WaveFormat.nSamplesPerSec = (DWORD)sampleRate; \r
- pcm16WaveFormat.nBlockAlign = 4; \r
- pcm16WaveFormat.nAvgBytesPerSec = pcm16WaveFormat.nSamplesPerSec*pcm16WaveFormat.nBlockAlign; \r
- pcm16WaveFormat.wBitsPerSample = 16; \r
- pcm16WaveFormat.cbSize = 0;\r
-\r
- if (*shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE)\r
- hResult = myClient->IsFormatSupported(AUDCLNT_SHAREMODE_EXCLUSIVE,&pcm16WaveFormat,NULL);\r
- else\r
- hResult = myClient->IsFormatSupported(AUDCLNT_SHAREMODE_SHARED, &pcm16WaveFormat,&sharedClosestMatch);\r
-\r
- if (hResult == S_OK){\r
+ if (answer != paFormatIsSupported)\r
+ {\r
+ // try MIX format?\r
+ // why did it HAVE to come to this ....\r
+ WAVEFORMATEX pcm16WaveFormat = { 0 };\r
+ pcm16WaveFormat.wFormatTag = WAVE_FORMAT_PCM;\r
+ pcm16WaveFormat.nChannels = 2;\r
+ pcm16WaveFormat.nSamplesPerSec = (DWORD)sampleRate;\r
+ pcm16WaveFormat.nBlockAlign = 4;\r
+ pcm16WaveFormat.nAvgBytesPerSec = pcm16WaveFormat.nSamplesPerSec*pcm16WaveFormat.nBlockAlign;\r
+ pcm16WaveFormat.wBitsPerSample = 16;\r
+ pcm16WaveFormat.cbSize = 0;\r
+\r
+ hResult = myClient->IsFormatSupported(shareMode, &pcm16WaveFormat, (shareMode == AUDCLNT_SHAREMODE_SHARED ? &sharedClosestMatch : NULL));\r
+ if (hResult == S_OK)\r
+ {\r
memcpy(outWavex,&pcm16WaveFormat,sizeof(WAVEFORMATEX));\r
answer = paFormatIsSupported;\r
}\r
}\r
\r
- logAUDCLNT_E(hResult);\r
+ LogHostError(hResult);\r
}\r
\r
return answer;\r
}\r
\r
-\r
-static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,\r
- const PaStreamParameters *inputParameters,\r
- const PaStreamParameters *outputParameters,\r
- double sampleRate )\r
+// ------------------------------------------------------------------------------------------\r
+static PaError IsStreamParamsValid(struct PaUtilHostApiRepresentation *hostApi,\r
+ const PaStreamParameters *inputParameters,\r
+ const PaStreamParameters *outputParameters,\r
+ double sampleRate)\r
{\r
-\r
- int inputChannelCount, outputChannelCount;\r
- PaSampleFormat inputSampleFormat, outputSampleFormat;\r
-\r
- if( inputParameters )\r
+ if (inputParameters != NULL)\r
{\r
- inputChannelCount = inputParameters->channelCount;\r
- inputSampleFormat = inputParameters->sampleFormat;\r
-\r
/* all standard sample formats are supported by the buffer adapter,\r
this implementation doesn't support any custom sample formats */\r
- if( inputSampleFormat & paCustomFormat )\r
+ if (inputParameters->sampleFormat & paCustomFormat)\r
return paSampleFormatNotSupported;\r
\r
/* unless alternate device specification is supported, reject the use of\r
paUseHostApiSpecificDeviceSpecification */\r
-\r
- if( inputParameters->device == paUseHostApiSpecificDeviceSpecification )\r
+ if (inputParameters->device == paUseHostApiSpecificDeviceSpecification)\r
return paInvalidDevice;\r
\r
/* check that input device can support inputChannelCount */\r
- if( inputChannelCount > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels )\r
+ if (inputParameters->channelCount > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels)\r
return paInvalidChannelCount;\r
\r
/* validate inputStreamInfo */\r
- if( inputParameters->hostApiSpecificStreamInfo )\r
- return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */\r
-\r
-\r
- PaWinWasapiHostApiRepresentation *paWasapi = (PaWinWasapiHostApiRepresentation*)hostApi;\r
-\r
-\r
- IAudioClient *myClient=0;\r
- HRESULT hResult = paWasapi->devInfo[inputParameters->device].device->Activate(\r
- __uuidof(IAudioClient), CLSCTX_INPROC_SERVER, NULL, (void**)&myClient);\r
- if (hResult != S_OK){\r
- logAUDCLNT_E(hResult);\r
- return paInvalidDevice;\r
+ if (inputParameters->hostApiSpecificStreamInfo)\r
+ {\r
+ PaWasapiStreamInfo *inputStreamInfo = (PaWasapiStreamInfo *)inputParameters->hostApiSpecificStreamInfo;\r
+ if ((inputStreamInfo->size != sizeof(PaWasapiStreamInfo)) ||\r
+ (inputStreamInfo->version != 1) ||\r
+ (inputStreamInfo->hostApiType != paWASAPI))\r
+ {\r
+ return paIncompatibleHostApiSpecificStreamInfo;\r
+ }\r
}\r
\r
- WAVEFORMATEXTENSIBLE wavex;\r
- AUDCLNT_SHAREMODE shareMode;\r
- PaError answer = GetClosestFormat(myClient,sampleRate,inputParameters,&shareMode,&wavex);\r
- myClient->Release();\r
-\r
- if (answer !=paFormatIsSupported)\r
- return answer;\r
- }\r
- else\r
- {\r
- inputChannelCount = 0;\r
+ return paNoError;\r
}\r
\r
- if( outputParameters )\r
+ if (outputParameters != NULL)\r
{\r
- outputChannelCount = outputParameters->channelCount;\r
- outputSampleFormat = outputParameters->sampleFormat;\r
-\r
/* all standard sample formats are supported by the buffer adapter,\r
this implementation doesn't support any custom sample formats */\r
- if( outputSampleFormat & paCustomFormat )\r
+ if (outputParameters->sampleFormat & paCustomFormat)\r
return paSampleFormatNotSupported;\r
\r
/* unless alternate device specification is supported, reject the use of\r
paUseHostApiSpecificDeviceSpecification */\r
-\r
- if( outputParameters->device == paUseHostApiSpecificDeviceSpecification )\r
+ if (outputParameters->device == paUseHostApiSpecificDeviceSpecification)\r
return paInvalidDevice;\r
\r
/* check that output device can support outputChannelCount */\r
- if( outputChannelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels )\r
+ if (outputParameters->channelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels)\r
return paInvalidChannelCount;\r
\r
/* validate outputStreamInfo */\r
- if( outputParameters->hostApiSpecificStreamInfo )\r
- return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */\r
+ if(outputParameters->hostApiSpecificStreamInfo)\r
+ {\r
+ PaWasapiStreamInfo *outputStreamInfo = (PaWasapiStreamInfo *)outputParameters->hostApiSpecificStreamInfo;\r
+ if ((outputStreamInfo->size != sizeof(PaWasapiStreamInfo)) ||\r
+ (outputStreamInfo->version != 1) ||\r
+ (outputStreamInfo->hostApiType != paWASAPI))\r
+ {\r
+ return paIncompatibleHostApiSpecificStreamInfo;\r
+ }\r
+ }\r
\r
+ return paNoError;\r
+ }\r
\r
- PaWinWasapiHostApiRepresentation *paWasapi = (PaWinWasapiHostApiRepresentation*)hostApi;\r
+ return (inputParameters || outputParameters ? paNoError : paInternalError);\r
+}\r
\r
- IAudioClient *myClient=0;\r
- HRESULT hResult = paWasapi->devInfo[outputParameters->device].device->Activate(\r
- __uuidof(IAudioClient), CLSCTX_INPROC_SERVER, NULL, (void**)&myClient);\r
- if (hResult != S_OK){\r
- logAUDCLNT_E(hResult);\r
+// ------------------------------------------------------------------------------------------\r
+static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,\r
+ const PaStreamParameters *inputParameters,\r
+ const PaStreamParameters *outputParameters,\r
+ double sampleRate )\r
+{\r
+ IAudioClient *tmpClient = NULL;\r
+ PaWasapiHostApiRepresentation *paWasapi = (PaWasapiHostApiRepresentation*)hostApi;\r
+ PaWasapiStreamInfo *inputStreamInfo = NULL, *outputStreamInfo = NULL;\r
+\r
+ // Validate PaStreamParameters\r
+ PaError error;\r
+ if ((error = IsStreamParamsValid(hostApi, inputParameters, outputParameters, sampleRate)) != paNoError)\r
+ return error;\r
+\r
+ if (inputParameters != NULL)\r
+ {\r
+ inputStreamInfo = (PaWasapiStreamInfo *)inputParameters->hostApiSpecificStreamInfo;\r
+\r
+ AUDCLNT_SHAREMODE shareMode = AUDCLNT_SHAREMODE_SHARED;\r
+ if (inputStreamInfo && (inputStreamInfo->flags & paWinWasapiExclusive))\r
+ shareMode = AUDCLNT_SHAREMODE_EXCLUSIVE;\r
+\r
+ HRESULT hResult = paWasapi->devInfo[inputParameters->device].device->Activate(\r
+ __uuidof(IAudioClient), CLSCTX_INPROC_SERVER, NULL, (void**)&tmpClient);\r
+ if (hResult != S_OK)\r
+ {\r
+ LogHostError(hResult);\r
return paInvalidDevice;\r
}\r
\r
WAVEFORMATEXTENSIBLE wavex;\r
- AUDCLNT_SHAREMODE shareMode;\r
- PaError answer = GetClosestFormat(myClient,sampleRate,outputParameters,&shareMode,&wavex);\r
- myClient->Release();\r
+ PaError answer = GetClosestFormat(tmpClient, sampleRate, inputParameters, shareMode, &wavex);\r
+ tmpClient->Release();\r
\r
- if (answer !=paFormatIsSupported)\r
- return answer; \r
+ if (answer != paFormatIsSupported)\r
+ return answer;\r
}\r
- else\r
+\r
+ if (outputParameters != NULL)\r
{\r
- outputChannelCount = 0;\r
- }\r
+ outputStreamInfo = (PaWasapiStreamInfo *)outputParameters->hostApiSpecificStreamInfo;\r
+\r
+ AUDCLNT_SHAREMODE shareMode = AUDCLNT_SHAREMODE_SHARED;\r
+ if (outputStreamInfo && (outputStreamInfo->flags & paWinWasapiExclusive))\r
+ shareMode = AUDCLNT_SHAREMODE_EXCLUSIVE;\r
+\r
+ HRESULT hResult = paWasapi->devInfo[outputParameters->device].device->Activate(\r
+ __uuidof(IAudioClient), CLSCTX_INPROC_SERVER, NULL, (void**)&tmpClient);\r
+ if (hResult != S_OK)\r
+ {\r
+ LogHostError(hResult);\r
+ return paInvalidDevice;\r
+ }\r
\r
+ WAVEFORMATEXTENSIBLE wavex;\r
+ PaError answer = GetClosestFormat(tmpClient, sampleRate, outputParameters, shareMode, &wavex);\r
+ tmpClient->Release();\r
+\r
+ if (answer != paFormatIsSupported)\r
+ return answer;\r
+ }\r
\r
return paFormatIsSupported;\r
}\r
\r
+// ------------------------------------------------------------------------------------------\r
+static HRESULT CreateAudioClient(PaWasapiSubStream *pSubStream, PaWasapiDeviceInfo *info,\r
+ const PaStreamParameters *params, UINT32 framesPerBuffer, double sampleRate, UINT32 streamFlags, \r
+ PaError *pa_error)\r
+{\r
+ if (!pSubStream || !info || !params)\r
+ return E_POINTER;\r
+ if ((sampleRate == 0) || (framesPerBuffer == 0))\r
+ return E_INVALIDARG;\r
+\r
+ PaError error;\r
+ HRESULT hr = S_OK;\r
+ UINT32 nFrames = 0;\r
+ IAudioClient *pAudioClient = NULL;\r
+\r
+ // Get the audio client\r
+ hr = info->device->Activate(__uuidof(IAudioClient), CLSCTX_ALL, NULL, (void**)&pAudioClient);\r
+ if (hr != S_OK)\r
+ {\r
+ LogHostError(hr);\r
+ goto done;\r
+ }\r
+\r
+ // Get closest format\r
+ if ((error = GetClosestFormat(pAudioClient, sampleRate, params, pSubStream->shareMode, &pSubStream->wavex)) != paFormatIsSupported)\r
+ {\r
+ if (pa_error)\r
+ (*pa_error) = error;\r
+ return AUDCLNT_E_UNSUPPORTED_FORMAT;\r
+ }\r
\r
+ // Align frames\r
+ framesPerBuffer = AlignFramesPerBuffer(framesPerBuffer, \r
+ pSubStream->wavex.Format.nSamplesPerSec, pSubStream->wavex.Format.nBlockAlign);\r
\r
-/* see pa_hostapi.h for a list of validity guarantees made about OpenStream parameters */\r
+ // Calculate period\r
+ pSubStream->period = MakeHnsPeriod(framesPerBuffer, pSubStream->wavex.Format.nSamplesPerSec);\r
+ pSubStream->period += ::SecondsTonano100(params->suggestedLatency);\r
+ \r
+ // Let's do not enforce min, it is even a bug as MinimumDevicePeriod will make buffer not aligned\r
+ // on Vista\r
+ //pSubStream->period = (pSubStream->period < info->MinimumDevicePeriod ? info->MinimumDevicePeriod : pSubStream->period);\r
+\r
+ // Open the stream and associate it with an audio session\r
+ hr = pAudioClient->Initialize( \r
+ pSubStream->shareMode,\r
+ streamFlags/*AUDCLNT_STREAMFLAGS_EVENTCALLBACK*/, \r
+ pSubStream->period,\r
+ (pSubStream->shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE ? pSubStream->period : 0),\r
+ &pSubStream->wavex.Format, \r
+ NULL);\r
+\r
+ // If the requested buffer size is not aligned...\r
+ if (hr == AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED)\r
+ { \r
+ PRINT(("WASAPI: CreateAudioClient: aligning buffer size"));\r
+\r
+ // Get the next aligned frame\r
+ hr = pAudioClient->GetBufferSize(&nFrames);\r
+ if (hr != S_OK)\r
+ {\r
+ LogHostError(hr);\r
+ goto done;\r
+ }\r
+ \r
+ // Release the previous allocations\r
+ SAFE_RELEASE(pAudioClient);\r
+ \r
+ // Create a new audio client\r
+ hr = info->device->Activate(__uuidof(IAudioClient), CLSCTX_ALL, NULL, (void**)&pAudioClient);\r
+ if (hr != S_OK)\r
+ {\r
+ LogHostError(hr);\r
+ goto done;\r
+ }\r
+\r
+ // Get closest format\r
+ if (GetClosestFormat(pAudioClient, sampleRate, params, pSubStream->shareMode, &pSubStream->wavex) != paFormatIsSupported)\r
+ return AUDCLNT_E_UNSUPPORTED_FORMAT;\r
\r
+ // Calculate period\r
+ pSubStream->period = MakeHnsPeriod(nFrames, pSubStream->wavex.Format.nSamplesPerSec);\r
+ \r
+ // Open the stream and associate it with an audio session\r
+ hr = pAudioClient->Initialize( \r
+ pSubStream->shareMode,\r
+ streamFlags/*AUDCLNT_STREAMFLAGS_EVENTCALLBACK*/, \r
+ pSubStream->period,\r
+ (pSubStream->shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE ? pSubStream->period : 0), \r
+ &pSubStream->wavex.Format, \r
+ NULL);\r
+ if (hr != S_OK)\r
+ {\r
+ LogHostError(hr);\r
+ goto done;\r
+ }\r
+ }\r
+ else\r
+ if (hr != S_OK)\r
+ {\r
+ LogHostError(hr);\r
+ goto done;\r
+ }\r
+\r
+ // Get the next aligned frame\r
+ hr = pAudioClient->GetBufferSize(&nFrames);\r
+ if (hr != S_OK)\r
+ {\r
+ LogHostError(hr);\r
+ goto done;\r
+ }\r
+ \r
+ // Set result\r
+ pSubStream->client = pAudioClient;\r
+ pSubStream->client->AddRef();\r
+\r
+done:\r
+\r
+ // Clean up\r
+ SAFE_RELEASE(pAudioClient);\r
+ return hr;\r
+}\r
+\r
+// ------------------------------------------------------------------------------------------\r
static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,\r
PaStream** s,\r
const PaStreamParameters *inputParameters,\r
void *userData )\r
{\r
PaError result = paNoError;\r
- PaWinWasapiHostApiRepresentation *paWasapi = (PaWinWasapiHostApiRepresentation*)hostApi;\r
- PaWinWasapiStream *stream = 0;\r
+ PaWasapiHostApiRepresentation *paWasapi = (PaWasapiHostApiRepresentation*)hostApi;\r
+ PaWasapiStream *stream;\r
int inputChannelCount, outputChannelCount;\r
PaSampleFormat inputSampleFormat, outputSampleFormat;\r
PaSampleFormat hostInputSampleFormat, hostOutputSampleFormat;\r
+ PaWasapiStreamInfo *inputStreamInfo = NULL, *outputStreamInfo = NULL;\r
\r
+ // validate PaStreamParameters\r
+ if ((result = IsStreamParamsValid(hostApi, inputParameters, outputParameters, sampleRate)) != paNoError)\r
+ return LogPaError(result);\r
\r
- stream = (PaWinWasapiStream*)PaUtil_AllocateMemory( sizeof(PaWinWasapiStream) );\r
- if( !stream ){\r
- result = paInsufficientMemory;\r
+ // Validate platform specific flags\r
+ if ((streamFlags & paPlatformSpecificFlags) != 0) \r
+ {\r
+ LogPaError(result = paInvalidFlag); /* unexpected platform specific flag */\r
+ goto error;\r
+ }\r
+\r
+ // Allocate memory for PaWasapiStream\r
+ if ((stream = (PaWasapiStream *)PaUtil_AllocateMemory(sizeof(PaWasapiStream))) == NULL)\r
+ {\r
+ LogPaError(result = paInsufficientMemory);\r
goto error;\r
}\r
\r
- if( inputParameters )\r
+ // Try create device: Input\r
+ if (inputParameters != NULL)\r
{\r
inputChannelCount = inputParameters->channelCount;\r
inputSampleFormat = inputParameters->sampleFormat;\r
+ inputStreamInfo = (PaWasapiStreamInfo *)inputParameters->hostApiSpecificStreamInfo;\r
+ PaWasapiDeviceInfo *info = &paWasapi->devInfo[inputParameters->device];\r
+ stream->in.flags = inputStreamInfo->flags;\r
+\r
+ // Select Exclusive/Shared mode\r
+ stream->in.shareMode = AUDCLNT_SHAREMODE_SHARED;\r
+ if ((inputStreamInfo != NULL) && (inputStreamInfo->flags & paWinWasapiExclusive))\r
+ stream->in.shareMode = AUDCLNT_SHAREMODE_EXCLUSIVE;\r
+\r
+ // Choose processing mode\r
+ stream->in.streamFlags = AUDCLNT_STREAMFLAGS_EVENTCALLBACK;\r
+ if (streamCallback == NULL)\r
+ stream->in.streamFlags = 0; // polling interface\r
+ if ((inputStreamInfo != NULL) && (inputStreamInfo->flags & paWinWasapiPolling))\r
+ stream->in.streamFlags = 0; // polling interface\r
+\r
+ // Create Audio client\r
+ HRESULT hr = CreateAudioClient(&stream->in, info, inputParameters, framesPerBuffer, \r
+ sampleRate, stream->in.streamFlags, &result);\r
+ if (hr != S_OK)\r
+ {\r
+ LogHostError(hr);\r
+ if (hr != AUDCLNT_E_UNSUPPORTED_FORMAT)\r
+ result = paInvalidDevice;\r
+\r
+ LogPaError(result);\r
+ goto error;\r
+ }\r
+ LogWAVEFORMATEXTENSIBLE(&stream->in.wavex);\r
\r
- /* unless alternate device specification is supported, reject the use of\r
- paUseHostApiSpecificDeviceSpecification */\r
-\r
- if( inputParameters->device == paUseHostApiSpecificDeviceSpecification )\r
- return paInvalidDevice;\r
-\r
- /* check that input device can support inputChannelCount */\r
- if( inputChannelCount > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels )\r
- return paInvalidChannelCount;\r
-\r
- /* validate inputStreamInfo */\r
- if( inputParameters->hostApiSpecificStreamInfo )\r
- return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */\r
-\r
-\r
- PaWinWasapiDeviceInfo &info = paWasapi->devInfo[inputParameters->device];\r
-\r
- HRESULT hResult = info.device->Activate(\r
- __uuidof(IAudioClient), CLSCTX_INPROC_SERVER, NULL,\r
- (void**)&stream->in.client);\r
-\r
- if (hResult != S_OK)\r
- return paInvalidDevice;\r
-\r
- hResult = info.device->Activate(\r
+ // Create volume mgr\r
+ stream->inVol = NULL;\r
+ /*hResult = info->device->Activate(\r
__uuidof(IAudioEndpointVolume), CLSCTX_INPROC_SERVER, NULL,\r
(void**)&stream->inVol);\r
-\r
if (hResult != S_OK)\r
- return paInvalidDevice;\r
- \r
- AUDCLNT_SHAREMODE shareMode;\r
- PaError answer = GetClosestFormat(stream->in.client,sampleRate,inputParameters,&shareMode,&stream->in.wavex);\r
- \r
- if (answer !=paFormatIsSupported)\r
- return answer;\r
-\r
- //stream->out.period = info.DefaultDevicePeriod;\r
- stream->in.period = info.MinimumDevicePeriod;\r
+ return paInvalidDevice;*/\r
\r
- hResult = stream->in.client->Initialize(\r
- shareMode,\r
- 0, //no flags\r
- stream->in.period,\r
- 0,//stream->out.period,\r
- (WAVEFORMATEX*)&stream->in.wavex,\r
- &stream->session\r
- );\r
-\r
- if (hResult != S_OK){\r
- logAUDCLNT_E(hResult);\r
- return paInvalidDevice;\r
+ // Set user-side custom host processor\r
+ if ((inputStreamInfo != NULL) &&\r
+ (inputStreamInfo->flags & paWinWasapiRedirectHostProcessor))\r
+ {\r
+ stream->hostProcessOverrideInput.processor = inputStreamInfo->hostProcessorInput;\r
+ stream->hostProcessOverrideInput.userData = userData;\r
}\r
\r
- hResult = stream->in.client->GetBufferSize(&stream->in.bufferSize);\r
- if (hResult != S_OK)\r
+ // Get max possible buffer size to check if it is not less than that we request\r
+ UINT32 maxBufferSize;\r
+ hr = stream->in.client->GetBufferSize(&maxBufferSize);\r
+ if (hr != S_OK)\r
+ {\r
+ LogHostError(hr);\r
return paInvalidDevice;\r
+ }\r
\r
- hResult = stream->in.client->GetStreamLatency(&stream->in.latency);\r
- if (hResult != S_OK)\r
+ // Correct buffer to max size if it maxed out result of GetBufferSize\r
+ stream->in.bufferSize = (framesPerBuffer > maxBufferSize ? maxBufferSize : framesPerBuffer);\r
+\r
+ // Get stream latency\r
+ hr = stream->in.client->GetStreamLatency(&stream->in.latency);\r
+ if (hr != S_OK)\r
+ {\r
+ LogHostError(hr);\r
return paInvalidDevice;\r
+ }\r
+ stream->in.latency_seconds = nano100ToSeconds(stream->in.latency);\r
\r
- double periodsPerSecond = 1.0/nano100ToSeconds(stream->in.period);\r
- double samplesPerPeriod = (double)(stream->in.wavex.Format.nSamplesPerSec)/periodsPerSecond;\r
+ // Number of samples that are required at each period\r
+ stream->in.framesPerHostCallback = \r
+ (stream->in.shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE ? maxBufferSize : stream->in.bufferSize);\r
\r
- //this is the number of samples that are required at each period\r
- stream->in.framesPerHostCallback = (unsigned long)samplesPerPeriod;//unrelated to channels\r
+ // Get closest format\r
+ hostInputSampleFormat = PaUtil_SelectClosestAvailableFormat( waveformatToPaFormat(&stream->in.wavex), inputSampleFormat );\r
\r
- /* IMPLEMENT ME - establish which host formats are available */\r
- hostInputSampleFormat =\r
- PaUtil_SelectClosestAvailableFormat( waveformatToPaFormat(&stream->in.wavex), inputSampleFormat );\r
+ UINT32 latency = (1000 * maxBufferSize) / stream->in.wavex.Format.nSamplesPerSec + (UINT32)nano100ToMillis(stream->in.latency);\r
+ PRINT(("PaWASAPIOpenStream: (input) framesPerHostCallback: %d\n", (UINT32)stream->in.framesPerHostCallback));\r
+ PRINT(("PaWASAPIOpenStream: (input) latency: %d\n", stream->in.latency));\r
}\r
else\r
{\r
inputSampleFormat = hostInputSampleFormat = paInt16; /* Surpress 'uninitialised var' warnings. */\r
}\r
\r
- if( outputParameters )\r
+ // Try create device: Output\r
+ if (outputParameters != NULL)\r
{\r
outputChannelCount = outputParameters->channelCount;\r
outputSampleFormat = outputParameters->sampleFormat;\r
-\r
- /* unless alternate device specification is supported, reject the use of\r
- paUseHostApiSpecificDeviceSpecification */\r
-\r
- if( outputParameters->device == paUseHostApiSpecificDeviceSpecification )\r
- return paInvalidDevice;\r
-\r
- /* check that output device can support inputChannelCount */\r
- if( outputChannelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels )\r
- return paInvalidChannelCount;\r
-\r
- /* validate outputStreamInfo */\r
- if( outputParameters->hostApiSpecificStreamInfo )\r
- return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */\r
-\r
-\r
- PaWinWasapiDeviceInfo &info = paWasapi->devInfo[outputParameters->device];\r
-\r
- HRESULT hResult = info.device->Activate(\r
- __uuidof(IAudioClient), CLSCTX_INPROC_SERVER, NULL,\r
- (void**)&stream->out.client);\r
-\r
- if (hResult != S_OK)\r
- return paInvalidDevice;\r
-\r
- AUDCLNT_SHAREMODE shareMode;\r
- PaError answer = GetClosestFormat(stream->out.client,sampleRate,outputParameters,&shareMode,&stream->out.wavex);\r
- \r
- if (answer !=paFormatIsSupported)\r
- return answer;\r
- LogWAVEFORMATEXTENSIBLE(&stream->out.wavex);\r
-\r
- // stream->out.period = info.DefaultDevicePeriod;\r
- stream->out.period = info.MinimumDevicePeriod;\r
-\r
- /*For an exclusive-mode stream that uses event-driven buffering, \r
- the caller must specify nonzero values for hnsPeriodicity and hnsBufferDuration, \r
- and the values of these two parameters must be equal */\r
- if (shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE){\r
- hResult = stream->out.client->Initialize(\r
- shareMode,\r
- AUDCLNT_STREAMFLAGS_EVENTCALLBACK, \r
- stream->out.period,\r
- stream->out.period,\r
- (WAVEFORMATEX*)&stream->out.wavex,\r
- &stream->session\r
- );\r
- }\r
- else{\r
- hResult = stream->out.client->Initialize(\r
- shareMode,\r
- AUDCLNT_STREAMFLAGS_EVENTCALLBACK, \r
- 0,\r
- 0,\r
- (WAVEFORMATEX*)&stream->out.wavex,\r
- &stream->session\r
- );\r
- }\r
- \r
-\r
- if (hResult != S_OK){\r
- logAUDCLNT_E(hResult);\r
- return paInvalidDevice;\r
+ outputStreamInfo = (PaWasapiStreamInfo *)outputParameters->hostApiSpecificStreamInfo;\r
+ PaWasapiDeviceInfo *info = &paWasapi->devInfo[outputParameters->device];\r
+ stream->out.flags = outputStreamInfo->flags;\r
+\r
+ // Select Exclusive/Shared mode\r
+ stream->out.shareMode = AUDCLNT_SHAREMODE_SHARED;\r
+ if ((outputStreamInfo != NULL) && (outputStreamInfo->flags & paWinWasapiExclusive))\r
+ stream->out.shareMode = AUDCLNT_SHAREMODE_EXCLUSIVE;\r
+\r
+ // Choose processing mode\r
+ stream->out.streamFlags = AUDCLNT_STREAMFLAGS_EVENTCALLBACK;\r
+ if (streamCallback == NULL)\r
+ stream->out.streamFlags = 0; // polling interface\r
+ if ((outputStreamInfo != NULL) && (outputStreamInfo->flags & paWinWasapiPolling))\r
+ stream->out.streamFlags = 0; // polling interface\r
+\r
+ // Create Audio client\r
+ HRESULT hr = CreateAudioClient(&stream->out, info, outputParameters, framesPerBuffer, \r
+ sampleRate, stream->out.streamFlags, &result);\r
+ if (hr != S_OK)\r
+ {\r
+ LogHostError(hr);\r
+ if (hr != AUDCLNT_E_UNSUPPORTED_FORMAT)\r
+ result = paInvalidDevice;\r
+\r
+ LogPaError(result);\r
+ goto error;\r
}\r
+ LogWAVEFORMATEXTENSIBLE(&stream->out.wavex);\r
\r
- hResult = info.device->Activate(\r
+ // Activate volume\r
+ stream->outVol = NULL;\r
+ /*hResult = info->device->Activate(\r
__uuidof(IAudioEndpointVolume), CLSCTX_INPROC_SERVER, NULL,\r
(void**)&stream->outVol);\r
-\r
if (hResult != S_OK)\r
- return paInvalidDevice;\r
+ return paInvalidDevice;*/\r
\r
- hResult = stream->out.client->GetBufferSize(&stream->out.bufferSize);\r
- if (hResult != S_OK)\r
- return paInvalidDevice;\r
+ // Set user-side custom host processor\r
+ if ((outputStreamInfo != NULL) &&\r
+ (outputStreamInfo->flags & paWinWasapiRedirectHostProcessor))\r
+ {\r
+ stream->hostProcessOverrideOutput.processor = outputStreamInfo->hostProcessorOutput;\r
+ stream->hostProcessOverrideOutput.userData = userData;\r
+ }\r
\r
- hResult = stream->out.client->GetStreamLatency(&stream->out.latency);\r
- if (hResult != S_OK)\r
- return paInvalidDevice;\r
- \r
- double periodsPerSecond = 1.0/nano100ToSeconds(stream->out.period);\r
- double samplesPerPeriod = (double)(stream->out.wavex.Format.nSamplesPerSec)/periodsPerSecond;\r
+ // Get max possible buffer size to check if it is not less than that we request\r
+ UINT32 maxBufferSize;\r
+ hr = stream->out.client->GetBufferSize(&maxBufferSize);\r
+ if (hr != S_OK)\r
+ {\r
+ LogHostError(hr);\r
+ LogPaError(result = paInvalidDevice);\r
+ goto error;\r
+ }\r
+\r
+ // Correct buffer to max size if it maxed out result of GetBufferSize\r
+ stream->out.bufferSize = (framesPerBuffer > maxBufferSize ? maxBufferSize : framesPerBuffer);\r
+\r
+ // Get stream latency\r
+ hr = stream->out.client->GetStreamLatency(&stream->out.latency);\r
+ if (hr != S_OK)\r
+ {\r
+ LogHostError(hr);\r
+ LogPaError(result = paInvalidDevice);\r
+ goto error;\r
+ }\r
+ stream->out.latency_seconds = nano100ToSeconds(stream->out.latency);\r
\r
- //this is the number of samples that are required at each period\r
- stream->out.framesPerHostCallback = stream->out.bufferSize; //(unsigned long)samplesPerPeriod;//unrelated to channels\r
+ // Number of samples that are required at each period\r
+ stream->out.framesPerHostCallback = \r
+ (stream->out.shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE ? maxBufferSize : stream->out.bufferSize);\r
\r
- /* IMPLEMENT ME - establish which host formats are available */\r
+ // Get closest format\r
hostOutputSampleFormat = PaUtil_SelectClosestAvailableFormat( waveformatToPaFormat(&stream->out.wavex), outputSampleFormat );\r
+\r
+ UINT32 latency = (1000 * maxBufferSize) / stream->out.wavex.Format.nSamplesPerSec + (UINT32)nano100ToMillis(stream->out.latency);\r
+ PRINT(("PaWASAPIOpenStream: (output) framesPerHostCallback: %d\n", (UINT32)stream->out.framesPerHostCallback));\r
+ PRINT(("PaWASAPIOpenStream: (output) latency: %d\n", latency));\r
}\r
else\r
{\r
outputSampleFormat = hostOutputSampleFormat = paInt16; /* Surpress 'uninitialized var' warnings. */\r
}\r
\r
+ // paWinWasapiPolling must be on/or not on both streams\r
+ if ((inputParameters != NULL) && (outputParameters != NULL))\r
+ {\r
+ if ((inputStreamInfo != NULL) && (outputStreamInfo != NULL))\r
+ {\r
+ if (((inputStreamInfo->flags & paWinWasapiPolling) && \r
+ !(outputStreamInfo->flags & paWinWasapiPolling)) \r
+ ||\r
+ (!(inputStreamInfo->flags & paWinWasapiPolling) && \r
+ (outputStreamInfo->flags & paWinWasapiPolling)))\r
+ {\r
+ LogPaError(result = paInvalidFlag);\r
+ goto error;\r
+ }\r
+ }\r
+ }\r
\r
-\r
- /*\r
- IMPLEMENT ME:\r
-\r
- ( the following two checks are taken care of by PaUtil_InitializeBufferProcessor() FIXME - checks needed? )\r
-\r
- - check that input device can support inputSampleFormat, or that\r
- we have the capability to convert from outputSampleFormat to\r
- a native format\r
-\r
- - check that output device can support outputSampleFormat, or that\r
- we have the capability to convert from outputSampleFormat to\r
- a native format\r
-\r
- - if a full duplex stream is requested, check that the combination\r
- of input and output parameters is supported\r
-\r
- - check that the device supports sampleRate\r
-\r
- - alter sampleRate to a close allowable rate if possible / necessary\r
-\r
- - validate suggestedInputLatency and suggestedOutputLatency parameters,\r
- use default values where necessary\r
- */\r
-\r
-\r
-\r
- /* validate platform specific flags */\r
- if( (streamFlags & paPlatformSpecificFlags) != 0 )\r
- return paInvalidFlag; /* unexpected platform specific flag */\r
-\r
-\r
-\r
- if( streamCallback )\r
+ if (streamCallback)\r
{\r
- PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,\r
- &paWasapi->callbackStreamInterface, streamCallback, userData );\r
+ stream->bBlocking = false;\r
+ PaUtil_InitializeStreamRepresentation(&stream->streamRepresentation,\r
+ &paWasapi->callbackStreamInterface, streamCallback, userData);\r
}\r
else\r
{\r
- PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,\r
- &paWasapi->blockingStreamInterface, streamCallback, userData );\r
+ stream->bBlocking = true;\r
+ PaUtil_InitializeStreamRepresentation(&stream->streamRepresentation,\r
+ &paWasapi->blockingStreamInterface, streamCallback, userData);\r
}\r
\r
- PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate );\r
-\r
+ PaUtil_InitializeCpuLoadMeasurer(&stream->cpuLoadMeasurer, sampleRate);\r
\r
- if (outputParameters && inputParameters){\r
-\r
- //serious problem #1\r
- if (stream->in.period != stream->out.period){\r
- PRINT(("OpenStream: period discrepancy\n"));\r
+ if (outputParameters && inputParameters)\r
+ {\r
+ // serious problem #1\r
+ if (stream->in.period != stream->out.period)\r
+ {\r
+ PRINT(("WASAPI: OpenStream: period discrepancy\n"));\r
goto error;\r
}\r
\r
- //serious problem #2\r
- if (stream->out.framesPerHostCallback != stream->in.framesPerHostCallback){\r
- PRINT(("OpenStream: framesPerHostCallback discrepancy\n"));\r
+ // serious problem #2\r
+ if (stream->out.framesPerHostCallback != stream->in.framesPerHostCallback)\r
+ {\r
+ PRINT(("WASAPI: OpenStream: framesPerHostCallback discrepancy\n"));\r
goto error;\r
}\r
}\r
\r
- unsigned long framesPerHostCallback = (outputParameters)?\r
- stream->out.framesPerHostCallback: \r
+ ULONG framesPerHostCallback = (outputParameters) ? stream->out.framesPerHostCallback:\r
stream->in.framesPerHostCallback;\r
\r
/* we assume a fixed host buffer size in this example, but the buffer processor\r
can also support bounded and unknown host buffer sizes by passing\r
paUtilBoundedHostBufferSize or paUtilUnknownHostBufferSize instead of\r
- paUtilFixedHostBufferSize below. */\r
-\r
- result = PaUtil_InitializeBufferProcessor( &stream->bufferProcessor,\r
+ paUtilFixedHostBufferSize below.\r
+ */\r
+ result = PaUtil_InitializeBufferProcessor(&stream->bufferProcessor,\r
inputChannelCount, inputSampleFormat, hostInputSampleFormat,\r
outputChannelCount, outputSampleFormat, hostOutputSampleFormat,\r
sampleRate, streamFlags, framesPerBuffer,\r
framesPerHostCallback, paUtilFixedHostBufferSize,\r
- streamCallback, userData );\r
- if( result != paNoError )\r
+ streamCallback, userData);\r
+ if (result != paNoError)\r
+ {\r
+ LogPaError(result);\r
goto error;\r
+ }\r
\r
-\r
- /*\r
- IMPLEMENT ME: initialise the following fields with estimated or actual\r
- values.\r
- */\r
+ // Set Input latency\r
stream->streamRepresentation.streamInfo.inputLatency =\r
PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor)\r
- + ((inputParameters)?nano100ToSeconds(stream->in.latency) :0);\r
+ + ((inputParameters)?stream->in.latency_seconds :0);\r
\r
+ // Set Output latency\r
stream->streamRepresentation.streamInfo.outputLatency =\r
PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor)\r
- + ((outputParameters)?nano100ToSeconds(stream->out.latency) :0);\r
+ + ((outputParameters)?stream->out.latency_seconds :0);\r
\r
stream->streamRepresentation.streamInfo.sampleRate = sampleRate;\r
\r
-\r
- *s = (PaStream*)stream;\r
-\r
+ (*s) = (PaStream *)stream;\r
\r
return result;\r
\r
error:\r
- if( stream )\r
- PaUtil_FreeMemory( stream );\r
+\r
+ if (stream)\r
+ PaUtil_FreeMemory(stream);\r
\r
return result;\r
}\r
\r
-\r
-\r
-/*\r
- When CloseStream() is called, the multi-api layer ensures that\r
- the stream has already been stopped or aborted.\r
-*/\r
-\r
-#define SAFE_RELEASE(punk) \\r
- if ((punk) != NULL) \\r
- { (punk)->Release(); (punk) = NULL; }\r
-\r
+// ------------------------------------------------------------------------------------------\r
static PaError CloseStream( PaStream* s )\r
{\r
PaError result = paNoError;\r
- PaWinWasapiStream *stream = (PaWinWasapiStream*)s;\r
+ PaWasapiStream *stream = (PaWasapiStream*)s;\r
\r
- /*\r
- IMPLEMENT ME:\r
- - additional stream closing + cleanup\r
- */\r
+ // abort active stream\r
+ if (IsStreamActive(s))\r
+ {\r
+ if ((result = AbortStream(s)) != paNoError)\r
+ return result;\r
+ }\r
\r
SAFE_RELEASE(stream->out.client);\r
SAFE_RELEASE(stream->in.client);\r
SAFE_RELEASE(stream->rclient);\r
SAFE_RELEASE(stream->inVol);\r
SAFE_RELEASE(stream->outVol);\r
- CloseHandle(stream->hThread);\r
- CloseHandle(stream->hNotificationEvent);\r
\r
- PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );\r
- PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation );\r
- PaUtil_FreeMemory( stream );\r
+ SAFE_CLOSE(stream->hThread);\r
+ SAFE_CLOSE(stream->hThreadDone);\r
+ SAFE_CLOSE(stream->hCloseRequest);\r
+ SAFE_CLOSE(stream->hBlockingOpStreamRD);\r
+ SAFE_CLOSE(stream->hBlockingOpStreamWR);\r
+\r
+ PaUtil_TerminateBufferProcessor(&stream->bufferProcessor);\r
+ PaUtil_TerminateStreamRepresentation(&stream->streamRepresentation);\r
+ PaUtil_FreeMemory(stream);\r
\r
return result;\r
}\r
\r
-DWORD WINAPI ProcThread(void *client);\r
-\r
+// ------------------------------------------------------------------------------------------\r
static PaError StartStream( PaStream *s )\r
{\r
PaError result = paNoError;\r
- PaWinWasapiStream *stream = (PaWinWasapiStream*)s;\r
+ HRESULT hr;\r
+ PaWasapiStream *stream = (PaWasapiStream*)s;\r
\r
- PaUtil_ResetBufferProcessor( &stream->bufferProcessor );\r
- \r
- HRESULT hResult=S_OK;\r
+ // check if stream is active already\r
+ if (IsStreamActive(s))\r
+ return paStreamIsNotStopped;\r
\r
- if (stream->out.client){\r
- hResult = stream->out.client->GetService(__uuidof(IAudioRenderClient),(void**)&stream->rclient);\r
- logAUDCLNT_E(hResult);\r
- if (hResult!=S_OK)\r
- return paUnanticipatedHostError;\r
- }\r
- \r
- if (stream->in.client){\r
- hResult = stream->in.client->GetService(__uuidof(IAudioCaptureClient),(void**)&stream->cclient);\r
- logAUDCLNT_E(hResult);\r
- if (hResult!=S_OK)\r
- return paUnanticipatedHostError;\r
+ PaUtil_ResetBufferProcessor(&stream->bufferProcessor);\r
+\r
+ // Create close event\r
+ stream->hCloseRequest = CreateEvent(NULL, TRUE, FALSE, NULL);\r
+\r
+ // Create thread\r
+ if (!stream->bBlocking)\r
+ {\r
+ // Create thread done event\r
+ stream->hThreadDone = CreateEvent(NULL, TRUE, FALSE, NULL);\r
+\r
+ if ((stream->in.client && stream->in.streamFlags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK) || \r
+ (stream->out.client && stream->out.streamFlags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK))\r
+ {\r
+ if ((stream->hThread = CREATE_THREAD(ProcThreadEvent)) == NULL)\r
+ return paUnanticipatedHostError;\r
+ }\r
+ else\r
+ {\r
+ if ((stream->hThread = CREATE_THREAD(ProcThreadPoll)) == NULL)\r
+ return paUnanticipatedHostError;\r
+ }\r
}\r
+ else\r
+ {\r
+ // Create blocking operation events (non-signaled event means - blocking operation is pending)\r
+ if (stream->out.client)\r
+ stream->hBlockingOpStreamWR = CreateEvent(NULL, TRUE, TRUE, NULL);\r
+ if (stream->in.client)\r
+ stream->hBlockingOpStreamRD = CreateEvent(NULL, TRUE, TRUE, NULL);\r
+\r
+ // Initialize event & start INPUT stream\r
+ if (stream->in.client)\r
+ {\r
+ if ((hr = stream->in.client->GetService(__uuidof(IAudioCaptureClient), (void**)&stream->cclient)) != S_OK)\r
+ {\r
+ LogHostError(hr);\r
+ return paUnanticipatedHostError;\r
+ }\r
\r
- // Create a thread for this client.\r
- stream->hThread = CREATE_THREAD;\r
- \r
- if (stream->hThread == NULL)\r
- return paUnanticipatedHostError;\r
+ if ((hr = stream->in.client->Start()) != S_OK)\r
+ {\r
+ LogHostError(hr);\r
+ return paUnanticipatedHostError;\r
+ }\r
+ }\r
+\r
+\r
+ // Initialize event & start OUTPUT stream\r
+ if (stream->out.client)\r
+ {\r
+ if ((hr = stream->out.client->GetService(__uuidof(IAudioRenderClient), (void**)&stream->rclient)) != S_OK)\r
+ {\r
+ LogHostError(hr);\r
+ return paUnanticipatedHostError;\r
+ }\r
+\r
+ // Start\r
+ if ((hr = stream->out.client->Start()) != S_OK)\r
+ {\r
+ LogHostError(hr);\r
+ return paUnanticipatedHostError;\r
+ }\r
+ }\r
+\r
+ // Signal: stream running\r
+ stream->running = true;\r
+\r
+ // Set current time\r
+ stream->out.prevTime = ::timeGetTime();\r
+ stream->out.prevSleep = 0;\r
+ }\r
\r
return paNoError;\r
}\r
\r
+// ------------------------------------------------------------------------------------------\r
+static void _FinishStream(PaWasapiStream *stream)\r
+{\r
+ // Issue command to thread to stop processing and wait for thread exit\r
+ if (!stream->bBlocking)\r
+ {\r
+ SignalObjectAndWait(stream->hCloseRequest, stream->hThreadDone, INFINITE, FALSE);\r
+ }\r
+ else\r
+ // Blocking mode does not own thread\r
+ {\r
+ // Signal close event and wait for each of 2 blocking operations to complete\r
+ if (stream->out.client)\r
+ SignalObjectAndWait(stream->hCloseRequest, stream->hBlockingOpStreamWR, INFINITE, TRUE);\r
+ if (stream->out.client)\r
+ SignalObjectAndWait(stream->hCloseRequest, stream->hBlockingOpStreamRD, INFINITE, TRUE);\r
+\r
+ // Process stop\r
+ _OnStreamStop(stream);\r
+ }\r
+\r
+ // Close thread handles to allow restart\r
+ SAFE_CLOSE(stream->hThread);\r
+ SAFE_CLOSE(stream->hThreadDone);\r
+ SAFE_CLOSE(stream->hCloseRequest);\r
+ SAFE_CLOSE(stream->hBlockingOpStreamRD);\r
+ SAFE_CLOSE(stream->hBlockingOpStreamWR);\r
\r
+ stream->running = false;\r
+}\r
+\r
+// ------------------------------------------------------------------------------------------\r
static PaError StopStream( PaStream *s )\r
{\r
PaError result = paNoError;\r
- PaWinWasapiStream *stream = (PaWinWasapiStream*)s;\r
\r
- /* suppress unused variable warnings */\r
- stream->closeRequest = true;\r
- //todo something MUCH better than this\r
- while(stream->closeRequest)\r
- Sleep(100);\r
-\r
- /* IMPLEMENT ME, see portaudio.h for required behavior */\r
-\r
- stream->running = false;\r
+ // Finish stream\r
+ _FinishStream((PaWasapiStream *)s);\r
\r
return result;\r
}\r
\r
-\r
+// ------------------------------------------------------------------------------------------\r
static PaError AbortStream( PaStream *s )\r
{\r
PaError result = paNoError;\r
- PaWinWasapiStream *stream = (PaWinWasapiStream*)s;\r
-\r
- /* suppress unused variable warnings */\r
- stream->closeRequest = true;\r
- //todo something MUCH better than this\r
- while(stream->closeRequest)\r
- Sleep(100);\r
\r
- /* IMPLEMENT ME, see portaudio.h for required behavior */\r
+ // Finish stream\r
+ _FinishStream((PaWasapiStream *)s);\r
\r
return result;\r
}\r
\r
-\r
+// ------------------------------------------------------------------------------------------\r
static PaError IsStreamStopped( PaStream *s )\r
{\r
- PaWinWasapiStream *stream = (PaWinWasapiStream*)s;\r
-\r
- return !stream->running;\r
+ PaWasapiStream *stream = (PaWasapiStream*)s;\r
+ return (stream ? !stream->running : paBadStreamPtr);\r
}\r
\r
-\r
+// ------------------------------------------------------------------------------------------\r
static PaError IsStreamActive( PaStream *s )\r
{\r
- PaWinWasapiStream *stream = (PaWinWasapiStream*)s;\r
- return stream->running;\r
+ PaWasapiStream *stream = (PaWasapiStream*)s;\r
+ return (stream ? stream->running : paBadStreamPtr);\r
}\r
\r
-\r
+// ------------------------------------------------------------------------------------------\r
static PaTime GetStreamTime( PaStream *s )\r
{\r
- PaWinWasapiStream *stream = (PaWinWasapiStream*)s;\r
+ PaWasapiStream *stream = (PaWasapiStream*)s;\r
+ if (stream == NULL)\r
+ return paBadStreamPtr;\r
\r
/* suppress unused variable warnings */\r
(void) stream;\r
return PaUtil_GetTime();\r
}\r
\r
-\r
+// ------------------------------------------------------------------------------------------\r
static double GetStreamCpuLoad( PaStream* s )\r
{\r
- PaWinWasapiStream *stream = (PaWinWasapiStream*)s;\r
-\r
- return PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer );\r
+ PaWasapiStream *stream = (PaWasapiStream *)s;\r
+ return (stream ? PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer ) : 0.0);\r
}\r
\r
+// ------------------------------------------------------------------------------------------\r
+/* NOT TESTED */\r
+static PaError ReadStream( PaStream* s, void *_buffer, unsigned long _frames )\r
+{\r
+ PaWasapiStream *stream = (PaWasapiStream*)s;\r
+ if (stream == NULL)\r
+ return paBadStreamPtr;\r
+\r
+ HRESULT hr = S_OK;\r
+ UINT32 frames;\r
+ BYTE *buffer = (BYTE *)_buffer;\r
+ BYTE *data = NULL;\r
+ DWORD flags = 0;\r
+\r
+ // Notify blocking op has begun\r
+ ResetEvent(stream->hBlockingOpStreamRD);\r
+\r
+ while (_frames != 0)\r
+ {\r
+ // Get the available data in the shared buffer.\r
+ if ((hr = stream->cclient->GetBuffer(&data, &frames, &flags, NULL, NULL)) != S_OK)\r
+ {\r
+ if (hr == AUDCLNT_S_BUFFER_EMPTY)\r
+ {\r
+ // Check if blocking call must be interrupted\r
+ if (WaitForSingleObject(stream->hCloseRequest, 1) != WAIT_TIMEOUT)\r
+ break;\r
+ }\r
\r
-/*\r
- As separate stream interfaces are used for blocking and callback\r
- streams, the following functions can be guaranteed to only be called\r
- for blocking streams.\r
-*/\r
+ return LogHostError(hr);\r
+ goto stream_rd_end;\r
+ }\r
\r
-static PaError ReadStream( PaStream* s,\r
- void *buffer,\r
- unsigned long frames )\r
-{\r
- PaWinWasapiStream *stream = (PaWinWasapiStream*)s;\r
+ // Detect silence\r
+ // if (flags & AUDCLNT_BUFFERFLAGS_SILENT)\r
+ // data = NULL;\r
\r
- /* suppress unused variable warnings */\r
- (void) buffer;\r
- (void) frames;\r
- (void) stream;\r
+ // Check if frames <= _frames\r
+ if (frames > _frames)\r
+ frames = _frames;\r
\r
- /* IMPLEMENT ME, see portaudio.h for required behavior*/\r
+ // Copy\r
+ UINT32 buffer_size = frames * stream->in.wavex.Format.nBlockAlign;\r
+ memcpy(buffer, data, buffer_size);\r
+ buffer += buffer_size;\r
+ \r
+ // Release buffer\r
+ if ((hr = stream->cclient->ReleaseBuffer(frames)) != S_OK)\r
+ {\r
+ LogHostError(hr);\r
+ goto stream_rd_end;\r
+ }\r
\r
- return paNoError;\r
-}\r
+ _frames -= frames;\r
+ }\r
+\r
+stream_rd_end:\r
\r
+ // Notify blocking op has ended\r
+ SetEvent(stream->hBlockingOpStreamRD);\r
\r
-static PaError WriteStream( PaStream* s,\r
- const void *buffer,\r
- unsigned long frames )\r
+ return (hr != S_OK ? paUnanticipatedHostError : paNoError);\r
+}\r
+\r
+// ------------------------------------------------------------------------------------------\r
+static PaError WriteStream( PaStream* s, const void *_buffer, unsigned long _frames )\r
{\r
- PaWinWasapiStream *stream = (PaWinWasapiStream*)s;\r
+ PaWasapiStream *stream = (PaWasapiStream*)s;\r
+\r
+ if (stream == NULL)\r
+ return paBadStreamPtr;\r
+\r
+ if (!stream->running)\r
+ return paStreamIsStopped;\r
+\r
+ if (stream->rclient == NULL)\r
+ return paBadStreamPtr;\r
+\r
+ UINT32 frames;\r
+ const BYTE *buffer = (BYTE *)_buffer;\r
+ BYTE *data;\r
+ HRESULT hr;\r
+\r
+ // Notify blocking op has begun\r
+ ResetEvent(stream->hBlockingOpStreamWR);\r
+\r
+ // Calculate sleep time for next call\r
+ UINT32 next_rev_sleep, blocks, block_sleep_ms;\r
+ {\r
+ UINT32 remainder = 0;\r
+ UINT32 sleep_ms = 0;\r
+ blocks = _frames / stream->out.framesPerHostCallback;\r
+ block_sleep_ms = GetFramesSleepTime(stream->out.framesPerHostCallback, stream->out.wavex.Format.nSamplesPerSec);\r
+ if (blocks == 0) \r
+ {\r
+ blocks = 1;\r
+ sleep_ms = GetFramesSleepTime(_frames, stream->out.wavex.Format.nSamplesPerSec); // partial\r
+ }\r
+ else\r
+ {\r
+ remainder = _frames - blocks * stream->out.framesPerHostCallback;\r
+ sleep_ms = block_sleep_ms; // full\r
+ }\r
\r
- /* suppress unused variable warnings */\r
- (void) buffer;\r
- (void) frames;\r
- (void) stream;\r
+ // Sleep for remainder\r
+ DWORD elapsed_ms = ::timeGetTime() - stream->out.prevTime;\r
+ if (sleep_ms >= elapsed_ms)\r
+ sleep_ms -= elapsed_ms;\r
\r
- /* IMPLEMENT ME, see portaudio.h for required behavior*/\r
+ next_rev_sleep = sleep_ms;\r
+ }\r
\r
- return paNoError;\r
-}\r
+ // Sleep diff from last call\r
+ if (stream->out.prevSleep)\r
+ Sleep(stream->out.prevSleep);\r
+ stream->out.prevSleep = next_rev_sleep;\r
+\r
+ // Feed engine\r
+ for (UINT32 i = 0; i < blocks; ++i)\r
+ {\r
+ // Get block frames\r
+ frames = stream->out.framesPerHostCallback;\r
+ if (frames > _frames)\r
+ frames = _frames;\r
+\r
+ if (i)\r
+ Sleep(block_sleep_ms);\r
+\r
+ UINT32 available;\r
+ while (frames != 0)\r
+ {\r
+ // Check if blocking call must be interrupted\r
+ if (WaitForSingleObject(stream->hCloseRequest, 0) != WAIT_TIMEOUT)\r
+ break;\r
+\r
+ // Get Read position\r
+ UINT32 padding = 0;\r
+ hr = stream->out.client->GetCurrentPadding(&padding);\r
+ if (hr != S_OK)\r
+ {\r
+ LogHostError(hr);\r
+ goto stream_wr_end;\r
+ }\r
+\r
+ // Get frames available\r
+ if (frames >= padding)\r
+ available = frames - padding;\r
+ else\r
+ available = frames;\r
+\r
+ // Get buffer\r
+ if ((hr = stream->rclient->GetBuffer(available, &data)) != S_OK)\r
+ {\r
+ // Buffer size is too big, waiting\r
+ if (hr == AUDCLNT_E_BUFFER_TOO_LARGE)\r
+ continue; \r
+ LogHostError(hr);\r
+ goto stream_wr_end;\r
+ }\r
+\r
+ // Copy\r
+ UINT32 buffer_size = available * stream->out.wavex.Format.nBlockAlign;\r
+ memcpy(data, buffer, buffer_size);\r
+ buffer += buffer_size;\r
+\r
+ // Release buffer\r
+ if ((hr = stream->rclient->ReleaseBuffer(available, 0)) != S_OK)\r
+ {\r
+ LogHostError(hr);\r
+ goto stream_wr_end;\r
+ }\r
+\r
+ frames -= available;\r
+ }\r
+\r
+ _frames -= frames;\r
+ }\r
\r
+stream_wr_end:\r
\r
+ // Set prev time\r
+ stream->out.prevTime = ::timeGetTime();\r
+\r
+ // Notify blocking op has ended\r
+ SetEvent(stream->hBlockingOpStreamWR);\r
+\r
+ return (hr != S_OK ? paUnanticipatedHostError : paNoError);\r
+}\r
+\r
+// ------------------------------------------------------------------------------------------\r
+/* NOT TESTED */\r
static signed long GetStreamReadAvailable( PaStream* s )\r
{\r
- PaWinWasapiStream *stream = (PaWinWasapiStream*)s;\r
+ PaWasapiStream *stream = (PaWasapiStream*)s;\r
\r
- /* suppress unused variable warnings */\r
- (void) stream;\r
+ if (stream == NULL)\r
+ return paBadStreamPtr;\r
\r
- /* IMPLEMENT ME, see portaudio.h for required behavior*/\r
+ if (!stream->running)\r
+ return paStreamIsStopped;\r
\r
- return 0;\r
-}\r
+ if (stream->cclient == NULL)\r
+ return paBadStreamPtr;\r
+\r
+ HRESULT hr;\r
\r
+ UINT32 pending = 0;\r
+ hr = stream->in.client->GetCurrentPadding(&pending);\r
+ if (hr != S_OK)\r
+ {\r
+ LogHostError(hr);\r
+ return paUnanticipatedHostError;\r
+ }\r
\r
+ return (long)pending;\r
+}\r
+\r
+// ------------------------------------------------------------------------------------------\r
static signed long GetStreamWriteAvailable( PaStream* s )\r
{\r
- PaWinWasapiStream *stream = (PaWinWasapiStream*)s;\r
+ PaWasapiStream *stream = (PaWasapiStream*)s;\r
\r
- /* suppress unused variable warnings */\r
- (void) stream;\r
+ if (stream == NULL)\r
+ return paBadStreamPtr;\r
\r
- /* IMPLEMENT ME, see portaudio.h for required behavior*/\r
+ if (!stream->running)\r
+ return paStreamIsStopped;\r
\r
- return 0;\r
-}\r
+ if (stream->rclient == NULL)\r
+ return paBadStreamPtr;\r
\r
+ UINT32 frames = stream->out.framesPerHostCallback;\r
+ HRESULT hr;\r
\r
+ UINT32 padding = 0;\r
+ hr = stream->out.client->GetCurrentPadding(&padding);\r
+ if (hr != S_OK)\r
+ {\r
+ LogHostError(hr);\r
+ return paUnanticipatedHostError;\r
+ }\r
\r
-/*\r
- ExampleHostProcessingLoop() illustrates the kind of processing which may\r
- occur in a host implementation.\r
+ // Calculate\r
+ frames -= padding;\r
\r
-*/\r
+ return frames;\r
+}\r
+\r
+// ------------------------------------------------------------------------------------------\r
static void WaspiHostProcessingLoop( void *inputBuffer, long inputFrames,\r
void *outputBuffer, long outputFrames,\r
void *userData )\r
{\r
- PaWinWasapiStream *stream = (PaWinWasapiStream*)userData;\r
- PaStreamCallbackTimeInfo timeInfo = {0,0,0}; /* IMPLEMENT ME */\r
+ PaWasapiStream *stream = (PaWasapiStream*)userData;\r
+ PaStreamCallbackTimeInfo timeInfo = {0,0,0};\r
+ PaStreamCallbackFlags flags = 0;\r
int callbackResult;\r
unsigned long framesProcessed;\r
+ HRESULT hResult;\r
+ UINT32 pending;\r
\r
PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer );\r
\r
-\r
/*\r
- IMPLEMENT ME:\r
+ Pa_GetStreamTime:\r
- generate timing information\r
- handle buffer slips\r
*/\r
+ timeInfo.currentTime = PaUtil_GetTime();\r
+ // Query input latency\r
+ if (stream->in.client != NULL)\r
+ {\r
+ PaTime pending_time;\r
+ if ((hResult = stream->in.client->GetCurrentPadding(&pending)) == S_OK)\r
+ pending_time = ((PaTime)pending / (PaTime)stream->in.wavex.Format.nSamplesPerSec) + stream->in.latency_seconds;\r
+ else\r
+ pending_time = (PaTime)stream->in.framesPerHostCallback / (PaTime)stream->in.wavex.Format.nSamplesPerSec;\r
+\r
+ timeInfo.inputBufferAdcTime = timeInfo.currentTime + pending_time;\r
+ }\r
+ // Query output current latency\r
+ if (stream->out.client != NULL)\r
+ {\r
+ PaTime pending_time;\r
+ if ((hResult = stream->out.client->GetCurrentPadding(&pending)) == S_OK)\r
+ pending_time = ((PaTime)pending / (PaTime)stream->out.wavex.Format.nSamplesPerSec) + stream->out.latency_seconds;\r
+ else\r
+ pending_time = (PaTime)stream->out.framesPerHostCallback / (PaTime)stream->out.wavex.Format.nSamplesPerSec;\r
+\r
+ timeInfo.outputBufferDacTime = timeInfo.currentTime + pending_time;\r
+ }\r
\r
/*\r
If you need to byte swap or shift inputBuffer to convert it into a\r
portaudio format, do it here.\r
*/\r
\r
-\r
-\r
- PaUtil_BeginBufferProcessing( &stream->bufferProcessor, &timeInfo, 0 /* IMPLEMENT ME: pass underflow/overflow flags when necessary */ );\r
+ PaUtil_BeginBufferProcessing( &stream->bufferProcessor, &timeInfo, flags );\r
\r
/*\r
depending on whether the host buffers are interleaved, non-interleaved\r
PaUtil_SetNonInterleaved*Channel() or PaUtil_Set*Channel() here.\r
*/\r
\r
- if( stream->bufferProcessor.inputChannelCount > 0 )\r
+ if (stream->bufferProcessor.inputChannelCount > 0)\r
{\r
PaUtil_SetInputFrameCount( &stream->bufferProcessor, inputFrames );\r
PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor,\r
0 ); /* 0 - use inputChannelCount passed to init buffer processor */\r
}\r
\r
- if( stream->bufferProcessor.outputChannelCount > 0 )\r
+ if (stream->bufferProcessor.outputChannelCount > 0)\r
{\r
PaUtil_SetOutputFrameCount( &stream->bufferProcessor, outputFrames);\r
PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor,\r
callbackResult = paContinue;\r
framesProcessed = PaUtil_EndBufferProcessing( &stream->bufferProcessor, &callbackResult );\r
\r
-\r
/*\r
If you need to byte swap or shift outputBuffer to convert it to\r
host format, do it here.\r
*/\r
\r
- PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesProcessed );\r
-\r
+ PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesProcessed );\r
\r
- if( callbackResult == paContinue )\r
+ if (callbackResult == paContinue)\r
{\r
/* nothing special to do */\r
}\r
- else if( callbackResult == paAbort )\r
+ else \r
+ if (callbackResult == paAbort)\r
{\r
- /* IMPLEMENT ME - finish playback immediately */\r
-\r
- /* once finished, call the finished callback */\r
- if( stream->streamRepresentation.streamFinishedCallback != 0 )\r
- stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData );\r
+ // stop stream\r
+ SetEvent(stream->hCloseRequest);\r
}\r
else\r
{\r
- /* User callback has asked us to stop with paComplete or other non-zero value */\r
-\r
- /* IMPLEMENT ME - finish playback once currently queued audio has completed */\r
-\r
- /* once finished, call the finished callback */\r
- if( stream->streamRepresentation.streamFinishedCallback != 0 )\r
- stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData );\r
+ // stop stream\r
+ SetEvent(stream->hCloseRequest);\r
}\r
}\r
\r
+// ------------------------------------------------------------------------------------------\r
+HANDLE MMCSS_activate(const char *name)\r
+{\r
+ DWORD task_idx = 0;\r
+ HANDLE hTask = pAvSetMmThreadCharacteristics(name, &task_idx);\r
+ if (hTask == NULL)\r
+ {\r
+ PRINT(("WASAPI: AvSetMmThreadCharacteristics failed!\n"));\r
+ }\r
\r
-void \r
-MMCSS_activate(){\r
+ /*BOOL priority_ok = pAvSetMmThreadPriority(hTask, AVRT_PRIORITY_NORMAL);\r
+ if (priority_ok == FALSE)\r
+ {\r
+ PRINT(("WASAPI: AvSetMmThreadPriority failed!\n"));\r
+ }*/\r
\r
- DWORD stuff=0;\r
- HANDLE thCarac = pAvSetMmThreadCharacteristics("Pro Audio",&stuff);\r
- if (!thCarac){\r
- PRINT(("AvSetMmThreadCharacteristics failed!\n"));\r
+ // debug\r
+ {\r
+ int cur_priority = GetThreadPriority(GetCurrentThread());\r
+ DWORD cur_priority_class = GetPriorityClass(GetCurrentProcess());\r
+ PRINT(("WASAPI: thread[ priority-0x%X class-0x%X ]\n", cur_priority, cur_priority_class));\r
}\r
\r
- BOOL prio = pAvSetMmThreadPriority(thCarac,AVRT_PRIORITY_NORMAL);\r
- if (!prio){\r
- PRINT(("AvSetMmThreadPriority failed!\n"));\r
- }\r
+ return hTask;\r
+}\r
\r
- //debug\r
- {\r
- HANDLE hh = GetCurrentThread();\r
- int currprio = GetThreadPriority(hh);\r
- DWORD currclass = GetPriorityClass(GetCurrentProcess());\r
- PRINT(("currprio 0x%X currclass 0x%X\n",currprio,currclass));\r
+// ------------------------------------------------------------------------------------------\r
+void MMCSS_deactivate(HANDLE hTask)\r
+{\r
+ if (!hTask)\r
+ return;\r
+\r
+ if (pAvRevertMmThreadCharacteristics(hTask) == FALSE)\r
+ {\r
+ PRINT(("WASAPI: AvRevertMmThreadCharacteristics failed!\n"));\r
}\r
}\r
\r
+// ------------------------------------------------------------------------------------------\r
+PaError PaWasapi_ThreadPriorityBoost(void **hTask, int nPriorityClass)\r
+{\r
+ if (hTask == NULL)\r
+ return paUnanticipatedHostError;\r
+\r
+ static const char *mmcs_name[7] = \r
+ {\r
+ "Audio",\r
+ "Capture",\r
+ "Distribution",\r
+ "Games",\r
+ "Playback",\r
+ "Pro Audio",\r
+ "Window Manager"\r
+ };\r
+ if ((UINT32)nPriorityClass >= 7)\r
+ return paUnanticipatedHostError;\r
+\r
+ HANDLE task = MMCSS_activate(mmcs_name[nPriorityClass]);\r
+ if (task == NULL)\r
+ return paUnanticipatedHostError;\r
+\r
+ (*hTask) = task;\r
+ return paNoError;\r
+}\r
\r
-DWORD WINAPI\r
-ProcThread(void* param){\r
- HRESULT hResult;\r
- MMCSS_activate();\r
+// ------------------------------------------------------------------------------------------\r
+PaError PaWasapi_ThreadPriorityRevert(void *hTask)\r
+{\r
+ if (hTask == NULL)\r
+ return paUnanticipatedHostError;\r
\r
- PaWinWasapiStream *stream = (PaWinWasapiStream*)param;\r
+ MMCSS_deactivate((HANDLE)hTask);\r
\r
- stream->hNotificationEvent = CreateEvent(NULL, \r
- FALSE, //bManualReset are we sure??\r
- FALSE, \r
- "PAWASA");\r
- hResult = stream->out.client->SetEventHandle(stream->hNotificationEvent);\r
- if (hResult != S_OK)\r
- logAUDCLNT_E(hResult);\r
+ return paNoError;\r
+}\r
\r
- if (stream->out.client){\r
- hResult = stream->out.client->Start();\r
- if (hResult != S_OK)\r
- logAUDCLNT_E(hResult);\r
+// ------------------------------------------------------------------------------------------\r
+static __forceinline HRESULT FillOutputBuffer(PaWasapiStream *stream, PaWasapiHostProcessor *processor, UINT32 frames)\r
+{\r
+ HRESULT hr;\r
+ BYTE *data = NULL;\r
+ DWORD flags = 0;\r
+\r
+ // Get buffer\r
+ if ((hr = stream->rclient->GetBuffer(frames, &data)) != S_OK)\r
+ return LogHostError(hr);\r
+\r
+ // Process data\r
+ processor[S_OUTPUT].processor(NULL, 0, data, frames, processor[S_OUTPUT].userData);\r
+\r
+ // Release buffer\r
+ if ((hr = stream->rclient->ReleaseBuffer(frames, 0)) != S_OK)\r
+ LogHostError(hr);\r
+\r
+ return hr;\r
+}\r
+\r
+// ------------------------------------------------------------------------------------------\r
+static __forceinline HRESULT PollInputBuffer(PaWasapiStream *stream, PaWasapiHostProcessor *processor)\r
+{\r
+ HRESULT hr;\r
+ UINT32 frames;\r
+ BYTE *data = NULL;\r
+ DWORD flags = 0;\r
+\r
+ for (;;)\r
+ {\r
+ // Get the available data in the shared buffer.\r
+ if ((hr = stream->cclient->GetBuffer(&data, &frames, &flags, NULL, NULL)) != S_OK)\r
+ {\r
+ if (hr == AUDCLNT_S_BUFFER_EMPTY)\r
+ break; // capture buffer exhausted\r
+\r
+ return LogHostError(hr);\r
+ break;\r
+ }\r
+\r
+ // Detect silence\r
+ // if (flags & AUDCLNT_BUFFERFLAGS_SILENT)\r
+ // data = NULL;\r
+\r
+ // Process data\r
+ processor[S_INPUT].processor(data, frames, NULL, 0, processor[S_INPUT].userData);\r
+\r
+ // Release buffer\r
+ if ((hr = stream->cclient->ReleaseBuffer(frames)) != S_OK)\r
+ return LogHostError(hr);\r
+ }\r
+\r
+ return hr;\r
+}\r
+\r
+// ------------------------------------------------------------------------------------------\r
+void _OnStreamStop(PaWasapiStream *stream)\r
+{\r
+ // Stop INPUT client\r
+ if (stream->in.client != NULL)\r
+ stream->in.client->Stop();\r
+ // Stop OUTPUT client\r
+ if (stream->out.client != NULL)\r
+ stream->out.client->Stop();\r
+\r
+ // Restore thread priority\r
+ if (stream->hAvTask != NULL)\r
+ {\r
+ MMCSS_deactivate(stream->hAvTask);\r
+ stream->hAvTask = NULL;\r
}\r
\r
+ // Notify\r
+ if (stream->streamRepresentation.streamFinishedCallback != NULL)\r
+ stream->streamRepresentation.streamFinishedCallback(stream->streamRepresentation.userData);\r
+}\r
+\r
+// ------------------------------------------------------------------------------------------\r
+PA_THREAD_FUNC ProcThreadEvent(void *param)\r
+{\r
+ HANDLE event[S_COUNT];\r
+ PaWasapiHostProcessor processor[S_COUNT];\r
+ HRESULT hr;\r
+ DWORD dwResult;\r
+ BOOL bInitOutput = FALSE;\r
+ PaWasapiStream *stream = (PaWasapiStream *)param;\r
+\r
+ // Setup data processors\r
+ PaWasapiHostProcessor defaultProcessor;\r
+ defaultProcessor.processor = WaspiHostProcessingLoop;\r
+ defaultProcessor.userData = stream;\r
+ processor[S_INPUT] = (stream->hostProcessOverrideInput.processor != NULL ? stream->hostProcessOverrideInput : defaultProcessor);\r
+ processor[S_OUTPUT] = (stream->hostProcessOverrideOutput.processor != NULL ? stream->hostProcessOverrideOutput : defaultProcessor);\r
+\r
+ // Initialize event to NULL first\r
+ event[S_INPUT] = CreateEvent(NULL, FALSE, FALSE, NULL);\r
+ event[S_OUTPUT] = CreateEvent(NULL, FALSE, FALSE, NULL);\r
+\r
+ // Check on low resources and disallow any processing further\r
+ if (!event[S_INPUT] || !event[S_OUTPUT])\r
+ {\r
+ PRINT(("WASAPI Thread: failed creating one or two event handles\n"));\r
+ goto thread_end;\r
+ }\r
+\r
+ // Boost thread priority\r
+ stream->hAvTask = MMCSS_activate("Pro Audio");\r
+\r
+ // Initialize event & start INPUT stream\r
+ if (stream->in.client)\r
+ {\r
+ if ((hr = stream->in.client->SetEventHandle(event[S_INPUT])) != S_OK)\r
+ LogHostError(hr);\r
+\r
+ if ((hr = stream->in.client->GetService(__uuidof(IAudioCaptureClient),(void**)&stream->cclient)) != S_OK)\r
+ LogHostError(hr);\r
+\r
+ if ((hr = stream->in.client->Start()) != S_OK)\r
+ LogHostError(hr);\r
+ }\r
+\r
+ // Initialize event & start OUTPUT stream\r
+ if (stream->out.client)\r
+ {\r
+ if ((hr = stream->out.client->SetEventHandle(event[S_OUTPUT])) != S_OK)\r
+ LogHostError(hr);\r
+\r
+ if ((hr = stream->out.client->GetService(__uuidof(IAudioRenderClient),(void**)&stream->rclient)) != S_OK)\r
+ LogHostError(hr);\r
+\r
+ // Preload buffer before start\r
+ if ((hr = FillOutputBuffer(stream, processor, stream->out.framesPerHostCallback)) != S_OK)\r
+ LogHostError(hr);\r
+\r
+ // Start\r
+ if ((hr = stream->out.client->Start()) != S_OK)\r
+ LogHostError(hr);\r
+ }\r
+\r
+ // Signal: stream running\r
stream->running = true;\r
- bool bOne = false;\r
-\r
- while( !stream->closeRequest ) \r
- { \r
- //lets wait but have a 1 second timeout\r
- DWORD dwResult = WaitForSingleObject(stream->hNotificationEvent, 1000);\r
- switch( dwResult ) {\r
- case WAIT_OBJECT_0: {\r
-\r
- unsigned long usingBS = stream->out.framesPerHostCallback;\r
-\r
- BYTE* indata = 0;\r
- BYTE* outdata = 0;\r
-\r
- hResult = stream->rclient->GetBuffer(usingBS, &outdata);\r
-\r
- if (hResult != S_OK || !outdata) {\r
- //logAUDCLNT_E(hResult);\r
- //most probably shared mode and hResult=AUDCLNT_E_BUFFER_TOO_LARGE\r
- UINT32 padding = 0;\r
- hResult = stream->out.client->GetCurrentPadding(&padding);\r
- if (padding == 0)\r
- break; \r
- usingBS = usingBS-padding;\r
- if (usingBS == 0)\r
- break;//huh?\r
- hResult = stream->rclient->GetBuffer(usingBS, &outdata);\r
- if (hResult != S_OK)//what can we do NOW??\r
- break;\r
- //logAUDCLNT_E(hResult); \r
- }\r
- \r
- WaspiHostProcessingLoop(indata, usingBS ,outdata, usingBS, stream);\r
\r
- hResult = stream->rclient->ReleaseBuffer(usingBS, 0);\r
- if (hResult != S_OK)\r
- logAUDCLNT_E(hResult);\r
+ // Processing Loop\r
+ for (;;)\r
+ {\r
+ // 2 sec timeout\r
+ dwResult = WaitForMultipleObjects(S_COUNT, event, FALSE, 2000);\r
+\r
+ // Check for close event (after wait for buffers to avoid any calls to user\r
+ // callback when hCloseRequest was set)\r
+ if (WaitForSingleObject(stream->hCloseRequest, 0) != WAIT_TIMEOUT)\r
+ break;\r
+\r
+ // Process S_INPUT/S_OUTPUT\r
+ switch (dwResult)\r
+ {\r
+ case WAIT_TIMEOUT: {\r
+ PRINT(("WASAPI Thread: WAIT_TIMEOUT - probably bad audio driver or Vista x64 bug: use paWinWasapiPolling instead\n"));\r
+ goto processing_stop;\r
+ //SetEvent(stream->hCloseRequest);\r
+ break; }\r
+\r
+ // Input stream\r
+ case WAIT_OBJECT_0 + S_INPUT: {\r
+ if (stream->cclient == NULL)\r
+ break;\r
+ PollInputBuffer(stream, processor);\r
+ break; }\r
\r
- /* This was suggested, but in my tests it doesnt seem to improve the \r
- locking behaviour some drivers have running in exclusive mode.\r
- if(!ResetEvent(stream->hNotificationEvent)){\r
- logAUDCLNT_E(hResult);\r
- }\r
- */\r
+ // Output stream\r
+ case WAIT_OBJECT_0 + S_OUTPUT: {\r
+ if (stream->rclient == NULL)\r
+ break;\r
+ FillOutputBuffer(stream, processor, stream->out.framesPerHostCallback);\r
+ break; }\r
+ }\r
+ }\r
\r
- } \r
- break;\r
+processing_stop:\r
\r
- }\r
- }\r
- stream->out.client->Stop();\r
- stream->closeRequest = false;\r
- \r
- return 0; \r
+ // Process stop\r
+ _OnStreamStop(stream);\r
+\r
+thread_end:\r
+\r
+ // Close event\r
+ CloseHandle(event[S_INPUT]);\r
+ CloseHandle(event[S_OUTPUT]);\r
+\r
+ // Notify thread is exiting\r
+ SetEvent(stream->hThreadDone);\r
+\r
+ return 0;\r
}\r
\r
+// ------------------------------------------------------------------------------------------\r
+PA_THREAD_FUNC ProcThreadPoll(void *param)\r
+{\r
+ PaWasapiHostProcessor processor[S_COUNT];\r
+ HRESULT hr;\r
+ DWORD dwResult = S_OUTPUT;\r
+ BOOL bInitOutput = FALSE;\r
+ PaWasapiStream *stream = (PaWasapiStream *)param;\r
+\r
+ // Calculate the actual duration of the allocated buffer.\r
+ DWORD sleep_ms = GetFramesSleepTime(stream->out.framesPerHostCallback, stream->out.wavex.Format.nSamplesPerSec);\r
+\r
+ // Setup data processors\r
+ PaWasapiHostProcessor defaultProcessor;\r
+ defaultProcessor.processor = WaspiHostProcessingLoop;\r
+ defaultProcessor.userData = stream;\r
+ processor[S_INPUT] = (stream->hostProcessOverrideInput.processor != NULL ? stream->hostProcessOverrideInput : defaultProcessor);\r
+ processor[S_OUTPUT] = (stream->hostProcessOverrideOutput.processor != NULL ? stream->hostProcessOverrideOutput : defaultProcessor);\r
+\r
+ // Boost thread priority\r
+ stream->hAvTask = MMCSS_activate("Pro Audio");\r
+\r
+ // Initialize event & start INPUT stream\r
+ if (stream->in.client)\r
+ {\r
+ if ((hr = stream->in.client->GetService(__uuidof(IAudioCaptureClient),(void**)&stream->cclient)) != S_OK)\r
+ LogHostError(hr);\r
+\r
+ if ((hr = stream->in.client->Start()) != S_OK)\r
+ LogHostError(hr);\r
+ }\r
+\r
+\r
+ // Initialize event & start OUTPUT stream\r
+ if (stream->out.client)\r
+ {\r
+ if ((hr = stream->out.client->GetService(__uuidof(IAudioRenderClient),(void**)&stream->rclient)) != S_OK)\r
+ LogHostError(hr);\r
+\r
+ // Preload buffer (obligatory, othervise ->Start() will fail)\r
+ if ((hr = FillOutputBuffer(stream, processor, stream->out.framesPerHostCallback)) != S_OK)\r
+ LogHostError(hr);\r
+\r
+ // Start\r
+ if ((hr = stream->out.client->Start()) != S_OK)\r
+ LogHostError(hr);\r
+ }\r
+\r
+ // Signal: stream running\r
+ stream->running = true;\r
+\r
+ // Processing Loop\r
+ while (WaitForSingleObject(stream->hCloseRequest, sleep_ms) == WAIT_TIMEOUT)\r
+ {\r
+ // Process S_INPUT/S_OUTPUT\r
+ switch (dwResult)\r
+ {\r
+ // Input stream\r
+ case S_INPUT: {\r
+ if (stream->cclient == NULL)\r
+ break;\r
+ PollInputBuffer(stream, processor);\r
+ break; }\r
+\r
+ // Output stream\r
+ case S_OUTPUT: {\r
+\r
+ if (stream->rclient == NULL)\r
+ break;\r
+\r
+ UINT32 frames = stream->out.framesPerHostCallback;\r
+ BYTE *data = NULL;\r
+ DWORD flags = 0;\r
+\r
+ // Get Read position\r
+ UINT32 padding = 0;\r
+ hr = stream->out.client->GetCurrentPadding(&padding);\r
+ if (hr != S_OK || padding == 0)\r
+ break;\r
+\r
+ // Fill buffer\r
+ frames -= padding;\r
+ if (frames != 0)\r
+ FillOutputBuffer(stream, processor, frames);\r
+\r
+ break; }\r
+ }\r
+ }\r
+\r
+ // Process stop\r
+ _OnStreamStop(stream);\r
\r
+ // Notify thread is exiting\r
+ SetEvent(stream->hThreadDone);\r
\r
+ return 0;\r
+}\r
\r
#endif //VC 2005\r
\r
\r
\r
#if 0\r
- if(bFirst) { \r
+ if(bFirst) {\r
float masteur;\r
- hResult = stream->outVol->GetMasterVolumeLevelScalar(&masteur);\r
- if (hResult != S_OK)\r
- logAUDCLNT_E(hResult);\r
+ hr = stream->outVol->GetMasterVolumeLevelScalar(&masteur);\r
+ if (hr != S_OK)\r
+ LogHostError(hr);\r
float chan1, chan2;\r
- hResult = stream->outVol->GetChannelVolumeLevelScalar(0, &chan1);\r
- if (hResult != S_OK)\r
- logAUDCLNT_E(hResult);\r
- hResult = stream->outVol->GetChannelVolumeLevelScalar(1, &chan2);\r
- if (hResult != S_OK)\r
- logAUDCLNT_E(hResult);\r
+ hr = stream->outVol->GetChannelVolumeLevelScalar(0, &chan1);\r
+ if (hr != S_OK)\r
+ LogHostError(hr);\r
+ hr = stream->outVol->GetChannelVolumeLevelScalar(1, &chan2);\r
+ if (hr != S_OK)\r
+ LogHostError(hr);\r
\r
BOOL bMute;\r
- hResult = stream->outVol->GetMute(&bMute);\r
- if (hResult != S_OK)\r
- logAUDCLNT_E(hResult);\r
+ hr = stream->outVol->GetMute(&bMute);\r
+ if (hr != S_OK)\r
+ LogHostError(hr);\r
\r
stream->outVol->SetMasterVolumeLevelScalar(0.5, NULL);\r
stream->outVol->SetChannelVolumeLevelScalar(0, 0.5, NULL);\r
stream->outVol->SetMute(FALSE, NULL);\r
bFirst = false;\r
}\r
-#endif
\ No newline at end of file
+#endif\r