diff --git a/include/pa_win_wasapi.h b/include/pa_win_wasapi.h index 1d26a2e..9023ff1 100644 --- a/include/pa_win_wasapi.h +++ b/include/pa_win_wasapi.h @@ -68,12 +68,17 @@ typedef enum PaWasapiFlags Note: WASAPI Event driven core is capable of 2ms latency!!!, but Polling method can only provide 15-20ms latency. */ paWinWasapiPolling = (1 << 3), + + /* forces custom thread priority setting. must be used if PaWasapiStreamInfo::threadPriority + is set to custom value. */ + paWinWasapiThreadPriority = (1 << 4) } PaWasapiFlags; #define paWinWasapiExclusive (paWinWasapiExclusive) #define paWinWasapiRedirectHostProcessor (paWinWasapiRedirectHostProcessor) #define paWinWasapiUseChannelMask (paWinWasapiUseChannelMask) #define paWinWasapiPolling (paWinWasapiPolling) +#define paWinWasapiThreadPriority (paWinWasapiThreadPriority) /* Host processor. Allows to skip internal PA processing completely. @@ -86,46 +91,6 @@ typedef void (*PaWasapiHostProcessorCallback) (void *inputBuffer, long inputFra void *outputBuffer, long outputFrames, void *userData); -/* Stream descriptor. */ -typedef struct PaWasapiStreamInfo -{ - unsigned long size; /**< sizeof(PaWasapiStreamInfo) */ - PaHostApiTypeId hostApiType; /**< paWASAPI */ - unsigned long version; /**< 1 */ - - unsigned long flags; /**< collection of PaWasapiFlags */ - - /* Support for WAVEFORMATEXTENSIBLE channel masks. If flags contains - paWinWasapiUseChannelMask this allows you to specify which speakers - to address in a multichannel stream. Constants for channelMask - are specified in pa_win_waveformat.h - */ - PaWinWaveFormatChannelMask channelMask; - - /* Delivers raw data to callback obtained from GetBuffer() methods skipping - internal PortAudio processing inventory completely. userData parameter will - be the same that was passed to Pa_OpenStream method. - */ - PaWasapiHostProcessorCallback hostProcessorOutput; - PaWasapiHostProcessorCallback hostProcessorInput; -} -PaWasapiStreamInfo; - - -/** Returns default sound format for device. Format is represented by PaWinWaveFormat or - WAVEFORMATEXTENSIBLE structure. - - @param pFormat pointer to PaWinWaveFormat or WAVEFORMATEXTENSIBLE structure. - @param nFormatSize pize of PaWinWaveFormat or WAVEFORMATEXTENSIBLE structure in bytes. - @param nDevice device index. - - @return A non-negative value indicating the number of bytes copied into format decriptor - or, a PaErrorCode (which are always negative) if PortAudio is not initialized - or an error is encountered. -*/ -int PaWasapi_GetDeviceDefaultFormat( void *pFormat, int nFormatSize, PaDeviceIndex nDevice ); - - /* Device role */ typedef enum PaWasapiDeviceRole { @@ -144,6 +109,72 @@ typedef enum PaWasapiDeviceRole PaWasapiDeviceRole; +/* Thread priority */ +typedef enum PaWasapiThreadPriority +{ + eThreadPriorityNone = 0, + eThreadPriorityAudio, //!< Default for Shared mode. + eThreadPriorityCapture, + eThreadPriorityDistribution, + eThreadPriorityGames, + eThreadPriorityPlayback, + eThreadPriorityProAudio, //!< Default for Exclusive mode. + eThreadPriorityWindowManager +} +PaWasapiThreadPriority; + + +/* Stream descriptor. */ +typedef struct PaWasapiStreamInfo +{ + unsigned long size; /**< sizeof(PaWasapiStreamInfo) */ + PaHostApiTypeId hostApiType; /**< paWASAPI */ + unsigned long version; /**< 1 */ + + unsigned long flags; /**< collection of PaWasapiFlags */ + + /* Support for WAVEFORMATEXTENSIBLE channel masks. If flags contains + paWinWasapiUseChannelMask this allows you to specify which speakers + to address in a multichannel stream. Constants for channelMask + are specified in pa_win_waveformat.h. Will be used only if + paWinWasapiUseChannelMask flag is specified. + */ + PaWinWaveFormatChannelMask channelMask; + + /* Delivers raw data to callback obtained from GetBuffer() methods skipping + internal PortAudio processing inventory completely. userData parameter will + be the same that was passed to Pa_OpenStream method. Will be used only if + paWinWasapiRedirectHostProcessor flag is specified. + */ + PaWasapiHostProcessorCallback hostProcessorOutput; + PaWasapiHostProcessorCallback hostProcessorInput; + + /* Specifies thread priority explicitly. Will be used only if paWinWasapiThreadPriority flag + is specified. + + Please note, if Input/Output streams are opened simultaniously (Full-Duplex mode) + you shall specify same value for threadPriority or othervise one of the values will be used + to setup thread priority. + */ + PaWasapiThreadPriority threadPriority; +} +PaWasapiStreamInfo; + + +/** Returns default sound format for device. Format is represented by PaWinWaveFormat or + WAVEFORMATEXTENSIBLE structure. + + @param pFormat pointer to PaWinWaveFormat or WAVEFORMATEXTENSIBLE structure. + @param nFormatSize pize of PaWinWaveFormat or WAVEFORMATEXTENSIBLE structure in bytes. + @param nDevice device index. + + @return A non-negative value indicating the number of bytes copied into format decriptor + or, a PaErrorCode (which are always negative) if PortAudio is not initialized + or an error is encountered. +*/ +int PaWasapi_GetDeviceDefaultFormat( void *pFormat, unsigned int nFormatSize, PaDeviceIndex nDevice ); + + /** Returns device role (PaWasapiDeviceRole enum). @param nDevice device index. @@ -160,18 +191,13 @@ int/*PaWasapiDeviceRole*/ PaWasapi_GetDeviceRole( PaDeviceIndex nDevice ); @param hTask a handle to pointer to priority task. Must be used with PaWasapi_RevertThreadPriority method to revert thread priority to initial state. - @param nPriorityClass an Id of thread priority: 1 - Audio - 2 - Capture - 3 - Distribution - 4 - Games - 5 - Playback - 6 - Pro Audio - 7 - Window Manager + @param nPriorityClass an Id of thread priority of PaWasapiThreadPriority type. Specifying + eThreadPriorityNone does nothing. @return Error code indicating success or failure. @see PaWasapi_RevertThreadPriority */ -PaError PaWasapi_ThreadPriorityBoost( void **hTask, int nPriorityClass ); +PaError PaWasapi_ThreadPriorityBoost( void **hTask, PaWasapiThreadPriority nPriorityClass ); /** Boost thread priority of calling thread (MMCSS). Use it for Blocking Interface only for thread diff --git a/src/hostapi/wasapi/pa_win_wasapi.cpp b/src/hostapi/wasapi/pa_win_wasapi.cpp index 33ae296..86b0267 100644 --- a/src/hostapi/wasapi/pa_win_wasapi.cpp +++ b/src/hostapi/wasapi/pa_win_wasapi.cpp @@ -97,6 +97,8 @@ PA_THREAD_FUNC ProcThreadPoll(void *param); enum { S_INPUT = 0, S_OUTPUT, S_COUNT }; +#define STATIC_ARRAY_SIZE(array) (sizeof(array)/sizeof(array[0])) + #define PRINT(x) PA_DEBUG(x); #define PA_SKELETON_SET_LAST_HOST_ERROR( errorCode, errorText ) \ @@ -315,6 +317,9 @@ typedef struct PaWasapiStream // Av Task (MM thread management) HANDLE hAvTask; + + // Thread priority level + PaWasapiThreadPriority nThreadPriority; } PaWasapiStream; @@ -893,7 +898,7 @@ static inline PaWasapiHostApiRepresentation *GetHostApi(PaError *_error = NULL) } // ------------------------------------------------------------------------------------------ -int PaWasapi_GetDeviceDefaultFormat( void *pFormat, int nFormatSize, PaDeviceIndex nDevice ) +int PaWasapi_GetDeviceDefaultFormat( void *pFormat, unsigned int nFormatSize, PaDeviceIndex nDevice ) { if (pFormat == NULL) return paBadBufferPtr; @@ -907,10 +912,10 @@ int PaWasapi_GetDeviceDefaultFormat( void *pFormat, int nFormatSize, PaDeviceInd if (paWasapi == NULL) return ret; - if (nDevice >= (int)paWasapi->deviceCount) + if ((UINT32)nDevice >= paWasapi->deviceCount) return paInvalidDevice; - int size = min(nFormatSize, sizeof(paWasapi->devInfo[ nDevice ].DefaultFormat)); + UINT32 size = min(nFormatSize, (UINT32)sizeof(paWasapi->devInfo[ nDevice ].DefaultFormat)); memcpy(pFormat, &paWasapi->devInfo[ nDevice ].DefaultFormat, size); return size; @@ -1560,6 +1565,9 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, goto error; } + // Default thread priority is Audio: for exclusive mode we will use Pro Audio. + stream->nThreadPriority = eThreadPriorityAudio; + // Try create device: Input if (inputParameters != NULL) { @@ -1572,7 +1580,20 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, // Select Exclusive/Shared mode stream->in.shareMode = AUDCLNT_SHAREMODE_SHARED; if ((inputStreamInfo != NULL) && (inputStreamInfo->flags & paWinWasapiExclusive)) + { + // Boost thread priority + stream->nThreadPriority = eThreadPriorityProAudio; + // Make Exclusive stream->in.shareMode = AUDCLNT_SHAREMODE_EXCLUSIVE; + } + + // If user provided explicit thread priority level, use it + if ((inputStreamInfo != NULL) && (inputStreamInfo->flags & paWinWasapiThreadPriority)) + { + if ((inputStreamInfo->threadPriority > eThreadPriorityNone) && + (inputStreamInfo->threadPriority <= eThreadPriorityWindowManager)) + stream->nThreadPriority = inputStreamInfo->threadPriority; + } // Choose processing mode stream->in.streamFlags = AUDCLNT_STREAMFLAGS_EVENTCALLBACK; @@ -1661,7 +1682,20 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, // Select Exclusive/Shared mode stream->out.shareMode = AUDCLNT_SHAREMODE_SHARED; if ((outputStreamInfo != NULL) && (outputStreamInfo->flags & paWinWasapiExclusive)) + { + // Boost thread priority + stream->nThreadPriority = eThreadPriorityProAudio; + // Make Exclusive stream->out.shareMode = AUDCLNT_SHAREMODE_EXCLUSIVE; + } + + // If user provided explicit thread priority level, use it + if ((outputStreamInfo != NULL) && (outputStreamInfo->flags & paWinWasapiThreadPriority)) + { + if ((outputStreamInfo->threadPriority > eThreadPriorityNone) && + (outputStreamInfo->threadPriority <= eThreadPriorityWindowManager)) + stream->nThreadPriority = outputStreamInfo->threadPriority; + } // Choose processing mode stream->out.streamFlags = AUDCLNT_STREAMFLAGS_EVENTCALLBACK; @@ -2440,13 +2474,14 @@ void MMCSS_deactivate(HANDLE hTask) } // ------------------------------------------------------------------------------------------ -PaError PaWasapi_ThreadPriorityBoost(void **hTask, int nPriorityClass) +PaError PaWasapi_ThreadPriorityBoost(void **hTask, PaWasapiThreadPriority nPriorityClass) { if (hTask == NULL) return paUnanticipatedHostError; - static const char *mmcs_name[7] = + static const char *mmcs_name[] = { + NULL, "Audio", "Capture", "Distribution", @@ -2455,7 +2490,7 @@ PaError PaWasapi_ThreadPriorityBoost(void **hTask, int nPriorityClass) "Pro Audio", "Window Manager" }; - if ((UINT32)nPriorityClass >= 7) + if ((UINT32)nPriorityClass >= STATIC_ARRAY_SIZE(mmcs_name)) return paUnanticipatedHostError; HANDLE task = MMCSS_activate(mmcs_name[nPriorityClass]); @@ -2546,7 +2581,7 @@ void _OnStreamStop(PaWasapiStream *stream) // Restore thread priority if (stream->hAvTask != NULL) { - MMCSS_deactivate(stream->hAvTask); + PaWasapi_ThreadPriorityRevert(stream->hAvTask); stream->hAvTask = NULL; } @@ -2584,7 +2619,8 @@ PA_THREAD_FUNC ProcThreadEvent(void *param) } // Boost thread priority - stream->hAvTask = MMCSS_activate("Pro Audio"); + stream->hAvTask = NULL; + PaWasapi_ThreadPriorityBoost((void **)&stream->hAvTask, stream->nThreadPriority); // Initialize event & start INPUT stream if (stream->in.client) @@ -2693,7 +2729,8 @@ PA_THREAD_FUNC ProcThreadPoll(void *param) processor[S_OUTPUT] = (stream->hostProcessOverrideOutput.processor != NULL ? stream->hostProcessOverrideOutput : defaultProcessor); // Boost thread priority - stream->hAvTask = MMCSS_activate("Pro Audio"); + stream->hAvTask = NULL; + PaWasapi_ThreadPriorityBoost((void **)&stream->hAvTask, stream->nThreadPriority); // Initialize event & start INPUT stream if (stream->in.client)