wasapi: allow mono input/output streams be created when Blocking interface is used. Thanks to Chris (c319chris@aol.com) for providing the patch.

This commit is contained in:
dmitrykos 2011-01-03 18:33:32 +00:00
commit 272adbefc7

View file

@ -376,6 +376,21 @@ typedef struct
}
PaWasapiHostApiRepresentation;
// ------------------------------------------------------------------------------------------
/* PaWasapiAudioClientParams - audio client parameters */
typedef struct PaWasapiAudioClientParams
{
PaWasapiDeviceInfo *device_info;
PaStreamParameters stream_params;
PaWasapiStreamInfo wasapi_params;
UINT32 frames_per_buffer;
double sample_rate;
BOOL blocking;
BOOL full_duplex;
BOOL wow64_workaround;
}
PaWasapiAudioClientParams;
// ------------------------------------------------------------------------------------------
/* PaWasapiStream - a stream data structure specifically for this implementation */
typedef struct PaWasapiSubStream
@ -390,6 +405,7 @@ typedef struct PaWasapiSubStream
AUDCLNT_SHAREMODE shareMode;
UINT32 streamFlags; // AUDCLNT_STREAMFLAGS_EVENTCALLBACK, ...
UINT32 flags;
PaWasapiAudioClientParams params; //!< parameters
// Buffers
UINT32 buffers; //!< number of buffers used (from host side)
@ -849,7 +865,7 @@ typedef enum EMixerDir { MIX_DIR__1TO2, MIX_DIR__2TO1, MIX_DIR__2TO1_L } EMixerD
}
// ------------------------------------------------------------------------------------------
#define _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1(TYPE)\
#define _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1_FLT32(TYPE)\
TYPE * __restrict to = (TYPE *)__to;\
TYPE * __restrict from = (TYPE *)__from;\
TYPE * __restrict end = to + count;\
@ -859,6 +875,28 @@ typedef enum EMixerDir { MIX_DIR__1TO2, MIX_DIR__2TO1, MIX_DIR__2TO1_L } EMixerD
from += 2;\
}
// ------------------------------------------------------------------------------------------
#define _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1_INT32(TYPE)\
TYPE * __restrict to = (TYPE *)__to;\
TYPE * __restrict from = (TYPE *)__from;\
TYPE * __restrict end = to + count;\
while (to != end)\
{\
*to ++ = (TYPE)(((INT32)from[0] + (INT32)from[1]) >> 1);\
from += 2;\
}
// ------------------------------------------------------------------------------------------
#define _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1_INT64(TYPE)\
TYPE * __restrict to = (TYPE *)__to;\
TYPE * __restrict from = (TYPE *)__from;\
TYPE * __restrict end = to + count;\
while (to != end)\
{\
*to ++ = (TYPE)(((INT64)from[0] + (INT64)from[1]) >> 1);\
from += 2;\
}
// ------------------------------------------------------------------------------------------
#define _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1_L(TYPE)\
TYPE * __restrict to = (TYPE *)__to;\
@ -878,11 +916,11 @@ static void _MixMonoToStereo_1TO2_32(void *__to, void *__from, UINT32 count) { _
static void _MixMonoToStereo_1TO2_32f(void *__to, void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_1_TO_2(float); }
// ------------------------------------------------------------------------------------------
static void _MixMonoToStereo_2TO1_8(void *__to, void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1(BYTE); }
static void _MixMonoToStereo_2TO1_16(void *__to, void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1(short); }
static void _MixMonoToStereo_2TO1_24(void *__to, void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1(int); /* !!! int24 data is contained in 32-bit containers*/ }
static void _MixMonoToStereo_2TO1_32(void *__to, void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1(int); }
static void _MixMonoToStereo_2TO1_32f(void *__to, void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1(float); }
static void _MixMonoToStereo_2TO1_8(void *__to, void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1_INT32(BYTE); }
static void _MixMonoToStereo_2TO1_16(void *__to, void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1_INT32(short); }
static void _MixMonoToStereo_2TO1_24(void *__to, void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1_INT32(int); /* !!! int24 data is contained in 32-bit containers*/ }
static void _MixMonoToStereo_2TO1_32(void *__to, void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1_INT64(int); }
static void _MixMonoToStereo_2TO1_32f(void *__to, void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1_FLT32(float); }
// ------------------------------------------------------------------------------------------
static void _MixMonoToStereo_2TO1_8_L(void *__to, void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1_L(BYTE); }
@ -1985,15 +2023,22 @@ static void _CalculateAlignedPeriod(PaWasapiSubStream *pSub, UINT32 *nFramesPerL
}
// ------------------------------------------------------------------------------------------
static HRESULT CreateAudioClient(PaWasapiStream *pStream, PaWasapiSubStream *pSub,
PaWasapiDeviceInfo *pInfo, const PaStreamParameters *params, UINT32 framesPerLatency,
double sampleRate, BOOL blocking, BOOL output, BOOL fullDuplex, PaError *pa_error)
static HRESULT CreateAudioClient(PaWasapiStream *pStream, PaWasapiSubStream *pSub, BOOL output, PaError *pa_error)
{
PaError error;
HRESULT hr;
const PaWasapiDeviceInfo *pInfo = pSub->params.device_info;
const PaStreamParameters *params = &pSub->params.stream_params;
UINT32 framesPerLatency = pSub->params.frames_per_buffer;
double sampleRate = pSub->params.sample_rate;
BOOL blocking = pSub->params.blocking;
BOOL fullDuplex = pSub->params.full_duplex;
const UINT32 userFramesPerBuffer = framesPerLatency;
IAudioClient *audioClient = NULL;
// Validate parameters
if (!pSub || !pInfo || !params)
return E_POINTER;
@ -2021,11 +2066,11 @@ static HRESULT CreateAudioClient(PaWasapiStream *pStream, PaWasapiSubStream *pSu
// Check for Mono <<>> Stereo workaround
if ((params->channelCount == 1) && (pSub->wavex.Format.nChannels == 2))
{
if (blocking)
/*if (blocking)
{
LogHostError(hr = AUDCLNT_E_UNSUPPORTED_FORMAT);
goto done; // fail, blocking mode not supported
}
}*/
// select mixer
pSub->monoMixer = _GetMonoToStereoMixer(WaveToPaFormat(&pSub->wavex), (pInfo->flow == eRender ? MIX_DIR__1TO2 : MIX_DIR__2TO1_L));
@ -2260,11 +2305,11 @@ static HRESULT CreateAudioClient(PaWasapiStream *pStream, PaWasapiSubStream *pSu
// Check for Mono >> Stereo workaround
if ((params->channelCount == 1) && (pSub->wavex.Format.nChannels == 2))
{
if (blocking)
/*if (blocking)
{
LogHostError(hr = AUDCLNT_E_UNSUPPORTED_FORMAT);
goto done; // fail, blocking mode not supported
}
}*/
// Select mixer
pSub->monoMixer = _GetMonoToStereoMixer(WaveToPaFormat(&pSub->wavex), (pInfo->flow == eRender ? MIX_DIR__1TO2 : MIX_DIR__2TO1_L));
@ -2316,6 +2361,150 @@ done:
return hr;
}
// ------------------------------------------------------------------------------------------
static PaError ActivateAudioClientOutput(PaWasapiStream *stream)
{
HRESULT hr;
PaError result;
UINT32 maxBufferSize = 0;
PaTime buffer_latency = 0;
UINT32 framesPerBuffer = stream->out.params.frames_per_buffer;
// Create Audio client
hr = CreateAudioClient(stream, &stream->out, TRUE, &result);
if (hr != S_OK)
{
LogPaError(result = paInvalidDevice);
goto error;
}
LogWAVEFORMATEXTENSIBLE(&stream->out.wavex);
// Activate volume
stream->outVol = NULL;
/*hr = info->device->Activate(
__uuidof(IAudioEndpointVolume), CLSCTX_INPROC_SERVER, NULL,
(void**)&stream->outVol);
if (hr != S_OK)
return paInvalidDevice;*/
// Get max possible buffer size to check if it is not less than that we request
hr = IAudioClient_GetBufferSize(stream->out.client, &maxBufferSize);
if (hr != S_OK)
{
LogHostError(hr);
LogPaError(result = paInvalidDevice);
goto error;
}
// Correct buffer to max size if it maxed out result of GetBufferSize
stream->out.bufferSize = maxBufferSize;
// Get interface latency (actually uneeded as we calculate latency from the size
// of maxBufferSize).
hr = IAudioClient_GetStreamLatency(stream->out.client, &stream->out.device_latency);
if (hr != S_OK)
{
LogHostError(hr);
LogPaError(result = paInvalidDevice);
goto error;
}
//stream->out.latency_seconds = nano100ToSeconds(stream->out.device_latency);
// Number of frames that are required at each period
stream->out.framesPerHostCallback = maxBufferSize;
// Calculate frames per single buffer, if buffers > 1 then always framesPerBuffer
stream->out.framesPerBuffer =
(stream->out.userBufferAndHostMatch ? stream->out.framesPerHostCallback : framesPerBuffer);
// Calculate buffer latency
buffer_latency = (PaTime)maxBufferSize / stream->out.wavex.Format.nSamplesPerSec;
// Append buffer latency to interface latency in shared mode (see GetStreamLatency notes)
stream->out.latency_seconds = buffer_latency;
PRINT(("WASAPI::OpenStream(output): framesPerUser[ %d ] framesPerHost[ %d ] latency[ %.02fms ] exclusive[ %s ] wow64_fix[ %s ] mode[ %s ]\n", (UINT32)framesPerBuffer, (UINT32)stream->out.framesPerHostCallback, (float)(stream->out.latency_seconds*1000.0f), (stream->out.shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE ? "YES" : "NO"), (stream->out.params.wow64_workaround ? "YES" : "NO"), (stream->out.streamFlags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK ? "EVENT" : "POLL")));
return paNoError;
error:
return result;
}
// ------------------------------------------------------------------------------------------
static PaError ActivateAudioClientInput(PaWasapiStream *stream)
{
HRESULT hr;
PaError result;
UINT32 maxBufferSize = 0;
PaTime buffer_latency = 0;
UINT32 framesPerBuffer = stream->out.params.frames_per_buffer;
// Create Audio client
hr = CreateAudioClient(stream, &stream->in, FALSE, &result);
if (hr != S_OK)
{
LogPaError(result = paInvalidDevice);
goto error;
}
LogWAVEFORMATEXTENSIBLE(&stream->in.wavex);
// Create volume mgr
stream->inVol = NULL;
/*hr = info->device->Activate(
__uuidof(IAudioEndpointVolume), CLSCTX_INPROC_SERVER, NULL,
(void**)&stream->inVol);
if (hr != S_OK)
return paInvalidDevice;*/
// Get max possible buffer size to check if it is not less than that we request
hr = IAudioClient_GetBufferSize(stream->in.client, &maxBufferSize);
if (hr != S_OK)
{
LogHostError(hr);
LogPaError(result = paInvalidDevice);
goto error;
}
// Correct buffer to max size if it maxed out result of GetBufferSize
stream->in.bufferSize = maxBufferSize;
// Get interface latency (actually uneeded as we calculate latency from the size
// of maxBufferSize).
hr = IAudioClient_GetStreamLatency(stream->in.client, &stream->in.device_latency);
if (hr != S_OK)
{
LogHostError(hr);
LogPaError(result = paInvalidDevice);
goto error;
}
//stream->in.latency_seconds = nano100ToSeconds(stream->in.device_latency);
// Number of frames that are required at each period
stream->in.framesPerHostCallback = maxBufferSize;
// Calculate frames per single buffer, if buffers > 1 then always framesPerBuffer
stream->in.framesPerBuffer =
(stream->in.userBufferAndHostMatch ? stream->in.framesPerHostCallback : framesPerBuffer);
// Calculate buffer latency
buffer_latency = (PaTime)maxBufferSize / stream->in.wavex.Format.nSamplesPerSec;
// Append buffer latency to interface latency in shared mode (see GetStreamLatency notes)
stream->in.latency_seconds = buffer_latency;
PRINT(("WASAPI::OpenStream(input): framesPerUser[ %d ] framesPerHost[ %d ] latency[ %.02fms ] exclusive[ %s ] wow64_fix[ %s ] mode[ %s ]\n", (UINT32)framesPerBuffer, (UINT32)stream->in.framesPerHostCallback, (float)(stream->in.latency_seconds*1000.0f), (stream->in.shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE ? "YES" : "NO"), (stream->in.params.wow64_workaround ? "YES" : "NO"), (stream->in.streamFlags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK ? "EVENT" : "POLL")));
return paNoError;
error:
return result;
}
// ------------------------------------------------------------------------------------------
static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
PaStream** s,
@ -2336,8 +2525,6 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
PaSampleFormat hostInputSampleFormat, hostOutputSampleFormat;
PaWasapiStreamInfo *inputStreamInfo = NULL, *outputStreamInfo = NULL;
PaWasapiDeviceInfo *info = NULL;
UINT32 maxBufferSize;
PaTime buffer_latency;
ULONG framesPerHostCallback;
PaUtilHostBufferSizeMode bufferMode;
const BOOL fullDuplex = ((inputParameters != NULL) && (outputParameters != NULL));
@ -2424,27 +2611,31 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
if (fullDuplex)
stream->in.streamFlags = 0; // polling interface is implemented for full-duplex mode also
// Create Audio client
hr = CreateAudioClient(stream, &stream->in, info, inputParameters, framesPerBuffer/*framesPerLatency*/,
sampleRate, (streamCallback == NULL), FALSE, fullDuplex, &result);
// Fill parameters for Audio Client creation
stream->in.params.device_info = info;
stream->in.params.stream_params = (*inputParameters);
if (inputStreamInfo != NULL)
{
stream->in.params.wasapi_params = (*inputStreamInfo);
stream->in.params.stream_params.hostApiSpecificStreamInfo = &stream->in.params.wasapi_params;
}
stream->in.params.frames_per_buffer = framesPerBuffer;
stream->in.params.sample_rate = sampleRate;
stream->in.params.blocking = (streamCallback == NULL);
stream->in.params.full_duplex = fullDuplex;
stream->in.params.wow64_workaround = paWasapi->useWOW64Workaround;
// Create and activate audio client
hr = ActivateAudioClientInput(stream);
if (hr != S_OK)
{
LogPaError(result = paInvalidDevice);
goto error;
}
LogWAVEFORMATEXTENSIBLE(&stream->in.wavex);
// Get closest format
hostInputSampleFormat = PaUtil_SelectClosestAvailableFormat( WaveToPaFormat(&stream->in.wavex), inputSampleFormat );
// Create volume mgr
stream->inVol = NULL;
/*hr = info->device->Activate(
__uuidof(IAudioEndpointVolume), CLSCTX_INPROC_SERVER, NULL,
(void**)&stream->inVol);
if (hr != S_OK)
return paInvalidDevice;*/
// Set user-side custom host processor
if ((inputStreamInfo != NULL) &&
(inputStreamInfo->flags & paWinWasapiRedirectHostProcessor))
@ -2452,44 +2643,6 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
stream->hostProcessOverrideInput.processor = inputStreamInfo->hostProcessorInput;
stream->hostProcessOverrideInput.userData = userData;
}
// Get max possible buffer size to check if it is not less than that we request
hr = IAudioClient_GetBufferSize(stream->in.client, &maxBufferSize);
if (hr != S_OK)
{
LogHostError(hr);
LogPaError(result = paInvalidDevice);
goto error;
}
// Correct buffer to max size if it maxed out result of GetBufferSize
stream->in.bufferSize = maxBufferSize;
// Get interface latency (actually uneeded as we calculate latency from the size
// of maxBufferSize).
hr = IAudioClient_GetStreamLatency(stream->in.client, &stream->in.device_latency);
if (hr != S_OK)
{
LogHostError(hr);
LogPaError(result = paInvalidDevice);
goto error;
}
//stream->in.latency_seconds = nano100ToSeconds(stream->in.device_latency);
// Number of frames that are required at each period
stream->in.framesPerHostCallback = maxBufferSize;
// Calculate frames per single buffer, if buffers > 1 then always framesPerBuffer
stream->in.framesPerBuffer =
(stream->in.userBufferAndHostMatch ? stream->in.framesPerHostCallback : framesPerBuffer);
// Calculate buffer latency
buffer_latency = (PaTime)maxBufferSize / stream->in.wavex.Format.nSamplesPerSec;
// Append buffer latency to interface latency in shared mode (see GetStreamLatency notes)
stream->in.latency_seconds += buffer_latency;
PRINT(("WASAPI::OpenStream(input): framesPerUser[ %d ] framesPerHost[ %d ] latency[ %.02fms ] exclusive[ %s ] wow64_fix[ %s ] mode[ %s ]\n", (UINT32)framesPerBuffer, (UINT32)stream->in.framesPerHostCallback, (float)(stream->in.latency_seconds*1000.0f), (stream->in.shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE ? "YES" : "NO"), (paWasapi->useWOW64Workaround ? "YES" : "NO"), (stream->in.streamFlags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK ? "EVENT" : "POLL")));
}
else
{
@ -2538,72 +2691,38 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
if (fullDuplex)
stream->out.streamFlags = 0; // polling interface is implemented for full-duplex mode also
// Create Audio client
hr = CreateAudioClient(stream, &stream->out, info, outputParameters, framesPerBuffer/*framesPerLatency*/,
sampleRate, (streamCallback == NULL), TRUE, fullDuplex, &result);
// Fill parameters for Audio Client creation
stream->out.params.device_info = info;
stream->out.params.stream_params = (*outputParameters);
if (inputStreamInfo != NULL)
{
stream->out.params.wasapi_params = (*outputStreamInfo);
stream->out.params.stream_params.hostApiSpecificStreamInfo = &stream->out.params.wasapi_params;
}
stream->out.params.frames_per_buffer = framesPerBuffer;
stream->out.params.sample_rate = sampleRate;
stream->out.params.blocking = (streamCallback == NULL);
stream->out.params.full_duplex = fullDuplex;
stream->out.params.wow64_workaround = paWasapi->useWOW64Workaround;
// Create and activate audio client
hr = ActivateAudioClientOutput(stream);
if (hr != S_OK)
{
LogPaError(result = paInvalidDevice);
goto error;
}
LogWAVEFORMATEXTENSIBLE(&stream->out.wavex);
// Get closest format
// Get closest format
hostOutputSampleFormat = PaUtil_SelectClosestAvailableFormat( WaveToPaFormat(&stream->out.wavex), outputSampleFormat );
// Activate volume
stream->outVol = NULL;
/*hr = info->device->Activate(
__uuidof(IAudioEndpointVolume), CLSCTX_INPROC_SERVER, NULL,
(void**)&stream->outVol);
if (hr != S_OK)
return paInvalidDevice;*/
// Set user-side custom host processor
// Set user-side custom host processor
if ((outputStreamInfo != NULL) &&
(outputStreamInfo->flags & paWinWasapiRedirectHostProcessor))
{
stream->hostProcessOverrideOutput.processor = outputStreamInfo->hostProcessorOutput;
stream->hostProcessOverrideOutput.userData = userData;
}
// Get max possible buffer size to check if it is not less than that we request
hr = IAudioClient_GetBufferSize(stream->out.client, &maxBufferSize);
if (hr != S_OK)
{
LogHostError(hr);
LogPaError(result = paInvalidDevice);
goto error;
}
// Correct buffer to max size if it maxed out result of GetBufferSize
stream->out.bufferSize = maxBufferSize;
// Get interface latency (actually uneeded as we calculate latency from the size
// of maxBufferSize).
hr = IAudioClient_GetStreamLatency(stream->out.client, &stream->out.device_latency);
if (hr != S_OK)
{
LogHostError(hr);
LogPaError(result = paInvalidDevice);
goto error;
}
//stream->out.latency_seconds = nano100ToSeconds(stream->out.device_latency);
// Number of frames that are required at each period
stream->out.framesPerHostCallback = maxBufferSize;
// Calculate frames per single buffer, if buffers > 1 then always framesPerBuffer
stream->out.framesPerBuffer =
(stream->out.userBufferAndHostMatch ? stream->out.framesPerHostCallback : framesPerBuffer);
// Calculate buffer latency
buffer_latency = (PaTime)maxBufferSize / stream->out.wavex.Format.nSamplesPerSec;
// Append buffer latency to interface latency in shared mode (see GetStreamLatency notes)
stream->out.latency_seconds += buffer_latency;
PRINT(("WASAPI::OpenStream(output): framesPerUser[ %d ] framesPerHost[ %d ] latency[ %.02fms ] exclusive[ %s ] wow64_fix[ %s ] mode[ %s ]\n", (UINT32)framesPerBuffer, (UINT32)stream->out.framesPerHostCallback, (float)(stream->out.latency_seconds*1000.0f), (stream->out.shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE ? "YES" : "NO"), (paWasapi->useWOW64Workaround ? "YES" : "NO"), (stream->out.streamFlags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK ? "EVENT" : "POLL")));
}
else
{
@ -4138,7 +4257,7 @@ PA_THREAD_FUNC ProcThreadPoll(void *param)
}
// Setup thread sleep scheduler
ThreadIdleScheduler_Setup(&scheduler, 1, sleep_ms/* microseconds here actually */);
ThreadIdleScheduler_Setup(&scheduler, 1, sleep_ms/* microseconds here */);
sleep_ms = 0;
}
@ -4549,12 +4668,12 @@ thread_end:
// Process stop
_OnStreamStop(stream);
// Notify: thread exited
SetEvent(stream->hThreadExit);
// Notify: not running
stream->running = FALSE;
// Notify: thread exited
SetEvent(stream->hThreadExit);
return 0;
thread_error: