From: dmitrykos Date: Mon, 3 Jan 2011 18:33:32 +0000 (+0000) Subject: wasapi: allow mono input/output streams be created when Blocking interface is used... X-Git-Tag: pa_stable_v19_20110326_r1647~67 X-Git-Url: https://andrewgundersen.net/repos?a=commitdiff_plain;h=272adbefc7521be6b780161a72e5f60e6587adec;p=portaudio wasapi: allow mono input/output streams be created when Blocking interface is used. Thanks to Chris (c319chris@aol.com) for providing the patch. --- diff --git a/src/hostapi/wasapi/pa_win_wasapi.c b/src/hostapi/wasapi/pa_win_wasapi.c index cbd9dee..bf70d91 100644 --- a/src/hostapi/wasapi/pa_win_wasapi.c +++ b/src/hostapi/wasapi/pa_win_wasapi.c @@ -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: