- implemented support for full-duplex mode (it always uses polling(pull) WASAPI behavior, if latency <15msec needed then event interface must be done)
 - fixed mono-to-stereo converter for input stream
 - mono-to-stereo converter will also automatically operate in shared mode (if WASAPI driver does not support mono for output, or only stereo for input)
This commit is contained in:
dmitrykos 2010-06-01 17:26:37 +00:00
commit d2568e4ae7

View file

@ -308,7 +308,7 @@ PA_THREAD_FUNC ProcThreadPoll(void *param);
#define MAX_STR_LEN 512 #define MAX_STR_LEN 512
enum { S_INPUT = 0, S_OUTPUT, S_COUNT }; enum { S_INPUT = 0, S_OUTPUT = 1, S_COUNT = 2, S_FULLDUPLEX = 0 };
#define STATIC_ARRAY_SIZE(array) (sizeof(array)/sizeof(array[0])) #define STATIC_ARRAY_SIZE(array) (sizeof(array)/sizeof(array[0]))
@ -317,6 +317,8 @@ enum { S_INPUT = 0, S_OUTPUT, S_COUNT };
#define PA_SKELETON_SET_LAST_HOST_ERROR( errorCode, errorText ) \ #define PA_SKELETON_SET_LAST_HOST_ERROR( errorCode, errorText ) \
PaUtil_SetLastHostErrorInfo( paWASAPI, errorCode, errorText ) PaUtil_SetLastHostErrorInfo( paWASAPI, errorCode, errorText )
#define PA_WASAPI__IS_FULLDUPLEX(STREAM) ((STREAM)->in.client && (STREAM)->out.client)
#ifndef IF_FAILED_JUMP #ifndef IF_FAILED_JUMP
#define IF_FAILED_JUMP(hr, label) if(FAILED(hr)) goto label; #define IF_FAILED_JUMP(hr, label) if(FAILED(hr)) goto label;
#endif #endif
@ -884,7 +886,10 @@ static BOOL UseWOW64Workaround()
} }
// ------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------
#define _WASAPI_MONO_TO_STEREO_MIXER(TYPE)\ typedef enum EMixerDir { MIX_DIR__1TO2, MIX_DIR__2TO1 } EMixerDir;
// ------------------------------------------------------------------------------------------
#define _WASAPI_MONO_TO_STEREO_MIXER_1_TO_2(TYPE)\
TYPE * __restrict to = __to;\ TYPE * __restrict to = __to;\
TYPE * __restrict from = __from;\ TYPE * __restrict from = __from;\
TYPE * __restrict end = from + count;\ TYPE * __restrict end = from + count;\
@ -896,46 +901,58 @@ static BOOL UseWOW64Workaround()
} }
// ------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------
static void _MixMonoToStereo_8(void *__to, void *__from, UINT32 count) #define _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1(TYPE)\
{ TYPE * __restrict to = (TYPE *)__to;\
_WASAPI_MONO_TO_STEREO_MIXER(BYTE); TYPE * __restrict from = (TYPE *)__from;\
} TYPE * __restrict end = to + count;\
while (to != end)\
// ------------------------------------------------------------------------------------------ {\
static void _MixMonoToStereo_16(void *__to, void *__from, UINT32 count) *to ++ = (TYPE)((float)(from[0] + from[1]) * 0.5f);\
{ from += 2;\
_WASAPI_MONO_TO_STEREO_MIXER(short);
}
// ------------------------------------------------------------------------------------------
static void _MixMonoToStereo_24(void *__to, void *__from, UINT32 count)
{
_WASAPI_MONO_TO_STEREO_MIXER(int); // !!! int24 data is contained in 32-bit containers
}
// ------------------------------------------------------------------------------------------
static void _MixMonoToStereo_32(void *__to, void *__from, UINT32 count)
{
_WASAPI_MONO_TO_STEREO_MIXER(int);
}
// ------------------------------------------------------------------------------------------
static void _MixMonoToStereo_32f(void *__to, void *__from, UINT32 count)
{
_WASAPI_MONO_TO_STEREO_MIXER(float);
}
// ------------------------------------------------------------------------------------------
static MixMonoToStereoF _GetMonoToStereoMixer(PaSampleFormat format)
{
switch (format)
{
case paUInt8: return _MixMonoToStereo_8;
case paInt16: return _MixMonoToStereo_16;
case paInt24: return _MixMonoToStereo_24;
case paInt32: return _MixMonoToStereo_32;
case paFloat32: return _MixMonoToStereo_32f;
} }
// ------------------------------------------------------------------------------------------
static void _MixMonoToStereo_1TO2_8(void *__to, void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_1_TO_2(BYTE); }
static void _MixMonoToStereo_1TO2_16(void *__to, void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_1_TO_2(short); }
static void _MixMonoToStereo_1TO2_24(void *__to, void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_1_TO_2(int); /* !!! int24 data is contained in 32-bit containers*/ }
static void _MixMonoToStereo_1TO2_32(void *__to, void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_1_TO_2(int); }
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 MixMonoToStereoF _GetMonoToStereoMixer(PaSampleFormat format, EMixerDir dir)
{
switch (dir)
{
case MIX_DIR__1TO2:
switch (format)
{
case paUInt8: return _MixMonoToStereo_1TO2_8;
case paInt16: return _MixMonoToStereo_1TO2_16;
case paInt24: return _MixMonoToStereo_1TO2_24;
case paInt32: return _MixMonoToStereo_1TO2_32;
case paFloat32: return _MixMonoToStereo_1TO2_32f;
}
break;
case MIX_DIR__2TO1:
switch (format)
{
case paUInt8: return _MixMonoToStereo_2TO1_8;
case paInt16: return _MixMonoToStereo_2TO1_16;
case paInt24: return _MixMonoToStereo_2TO1_24;
case paInt32: return _MixMonoToStereo_2TO1_32;
case paFloat32: return _MixMonoToStereo_2TO1_32f;
}
break;
}
return NULL; return NULL;
} }
@ -1670,7 +1687,15 @@ static PaError GetClosestFormat(IAudioClient *myClient, double sampleRate,
// Validate Channel count // Validate Channel count
if ((WORD)params->channelCount != outWavex->Format.nChannels) if ((WORD)params->channelCount != outWavex->Format.nChannels)
return paInvalidChannelCount; {
// If mono, then driver does not support 1 channel, we use internal workaround
// of tiny software mixing functionality, e.g. we provide to user buffer 1 channel
// but then mix into 2 for device buffer
if ((params->channelCount == 1) && (outWavex->Format.nChannels == 2))
return paFormatIsSupported;
else
return paInvalidChannelCount;
}
// Validate Sample format // Validate Sample format
if ((bitsPerSample = PaSampleFormatToBitsPerSample(params->sampleFormat)) == 0) if ((bitsPerSample = PaSampleFormatToBitsPerSample(params->sampleFormat)) == 0)
@ -1924,7 +1949,7 @@ static HRESULT CreateAudioClient(PaWasapiSubStream *pSubStream, PaWasapiDeviceIn
} }
// select mixer // select mixer
pSubStream->monoMixer = _GetMonoToStereoMixer(params->sampleFormat); pSubStream->monoMixer = _GetMonoToStereoMixer(params->sampleFormat, (info->flow == eRender ? MIX_DIR__1TO2 : MIX_DIR__2TO1));
if (pSubStream->monoMixer == NULL) if (pSubStream->monoMixer == NULL)
{ {
LogHostError(hr = AUDCLNT_E_UNSUPPORTED_FORMAT); LogHostError(hr = AUDCLNT_E_UNSUPPORTED_FORMAT);
@ -2074,7 +2099,7 @@ static const REFERENCE_TIME MAX_BUFFER_POLL_DURATION = 2000 * 10000;
} }
// select mixer // select mixer
pSubStream->monoMixer = _GetMonoToStereoMixer(params->sampleFormat); pSubStream->monoMixer = _GetMonoToStereoMixer(params->sampleFormat, (info->flow == eRender ? MIX_DIR__1TO2 : MIX_DIR__2TO1));
if (pSubStream->monoMixer == NULL) if (pSubStream->monoMixer == NULL)
{ {
LogHostError(hr = AUDCLNT_E_UNSUPPORTED_FORMAT); LogHostError(hr = AUDCLNT_E_UNSUPPORTED_FORMAT);
@ -2223,10 +2248,15 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
stream->in.streamFlags = AUDCLNT_STREAMFLAGS_EVENTCALLBACK; stream->in.streamFlags = AUDCLNT_STREAMFLAGS_EVENTCALLBACK;
if (paWasapi->useWOW64Workaround) if (paWasapi->useWOW64Workaround)
stream->in.streamFlags = 0; // polling interface stream->in.streamFlags = 0; // polling interface
else
if (streamCallback == NULL) if (streamCallback == NULL)
stream->in.streamFlags = 0; // polling interface stream->in.streamFlags = 0; // polling interface
else
if ((inputStreamInfo != NULL) && (inputStreamInfo->flags & paWinWasapiPolling)) if ((inputStreamInfo != NULL) && (inputStreamInfo->flags & paWinWasapiPolling))
stream->in.streamFlags = 0; // polling interface stream->in.streamFlags = 0; // polling interface
else
if (outputParameters != NULL)
stream->in.streamFlags = 0; // polling interface is implemented for full-duplex mode also
// Create Audio client // Create Audio client
hr = CreateAudioClient(&stream->in, info, inputParameters, 0/*framesPerLatency*/, hr = CreateAudioClient(&stream->in, info, inputParameters, 0/*framesPerLatency*/,
@ -2293,7 +2323,7 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
// Append buffer latency to interface latency in shared mode (see GetStreamLatency notes) // Append buffer latency to interface latency in shared mode (see GetStreamLatency notes)
stream->in.latency_seconds += buffer_latency; stream->in.latency_seconds += buffer_latency;
PRINT(("PaWASAPIOpenStream(input): framesPerHostCallback[ %d ] latency[ %.02fms ] exclusive[ %s ] wow64_fix[ %s ]\n", (UINT32)stream->in.framesPerHostCallback, (float)(stream->in.latency_seconds*1000.0f), (stream->in.shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE ? "YES" : "NO"), (paWasapi->useWOW64Workaround ? "YES" : "NO"))); PRINT(("WASAPI::OpenStream(input): framesPerHostCallback[ %d ] latency[ %.02fms ] exclusive[ %s ] wow64_fix[ %s ]\n", (UINT32)stream->in.framesPerHostCallback, (float)(stream->in.latency_seconds*1000.0f), (stream->in.shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE ? "YES" : "NO"), (paWasapi->useWOW64Workaround ? "YES" : "NO")));
} }
else else
{ {
@ -2332,10 +2362,15 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
stream->out.streamFlags = AUDCLNT_STREAMFLAGS_EVENTCALLBACK; stream->out.streamFlags = AUDCLNT_STREAMFLAGS_EVENTCALLBACK;
if (paWasapi->useWOW64Workaround) if (paWasapi->useWOW64Workaround)
stream->out.streamFlags = 0; // polling interface stream->out.streamFlags = 0; // polling interface
else
if (streamCallback == NULL) if (streamCallback == NULL)
stream->out.streamFlags = 0; // polling interface stream->out.streamFlags = 0; // polling interface
else
if ((outputStreamInfo != NULL) && (outputStreamInfo->flags & paWinWasapiPolling)) if ((outputStreamInfo != NULL) && (outputStreamInfo->flags & paWinWasapiPolling))
stream->out.streamFlags = 0; // polling interface stream->out.streamFlags = 0; // polling interface
else
if (inputParameters != NULL)
stream->out.streamFlags = 0; // polling interface is implemented for full-duplex mode also
// Create Audio client // Create Audio client
hr = CreateAudioClient(&stream->out, info, outputParameters, 0/*framesPerLatency*/, hr = CreateAudioClient(&stream->out, info, outputParameters, 0/*framesPerLatency*/,
@ -2402,7 +2437,7 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
// Append buffer latency to interface latency in shared mode (see GetStreamLatency notes) // Append buffer latency to interface latency in shared mode (see GetStreamLatency notes)
stream->out.latency_seconds += buffer_latency; stream->out.latency_seconds += buffer_latency;
PRINT(("PaWASAPIOpenStream(output): framesPerHostCallback[ %d ] latency[ %.02fms ] exclusive[ %s ] wow64_fix[ %s ]\n", (UINT32)stream->out.framesPerHostCallback, (float)(stream->out.latency_seconds*1000.0f), (stream->out.shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE ? "YES" : "NO"), (paWasapi->useWOW64Workaround ? "YES" : "NO"))); PRINT(("WASAPI::OpenStream(output): framesPerHostCallback[ %d ] latency[ %.02fms ] exclusive[ %s ] wow64_fix[ %s ]\n", (UINT32)stream->out.framesPerHostCallback, (float)(stream->out.latency_seconds*1000.0f), (stream->out.shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE ? "YES" : "NO"), (paWasapi->useWOW64Workaround ? "YES" : "NO")));
} }
else else
{ {
@ -2410,6 +2445,10 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
outputSampleFormat = hostOutputSampleFormat = paInt16; /* Surpress 'uninitialized var' warnings. */ outputSampleFormat = hostOutputSampleFormat = paInt16; /* Surpress 'uninitialized var' warnings. */
} }
// og full-duplex
if ((inputParameters != NULL) && (outputParameters != NULL))
PRINT(("WASAPI::OpenStream: full-duplex mode\n"));
// paWinWasapiPolling must be on/or not on both streams // paWinWasapiPolling must be on/or not on both streams
if ((inputParameters != NULL) && (outputParameters != NULL)) if ((inputParameters != NULL) && (outputParameters != NULL))
{ {
@ -3232,7 +3271,7 @@ PaError PaWasapi_ThreadPriorityRevert(void *hTask)
} }
// ------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------
static HRESULT FillOutputBuffer(PaWasapiStream *stream, PaWasapiHostProcessor *processor, UINT32 frames) static HRESULT ProcessOutputBuffer(PaWasapiStream *stream, PaWasapiHostProcessor *processor, UINT32 frames)
{ {
HRESULT hr; HRESULT hr;
BYTE *data = NULL; BYTE *data = NULL;
@ -3277,11 +3316,9 @@ static HRESULT FillOutputBuffer(PaWasapiStream *stream, PaWasapiHostProcessor *p
// expand buffer (one way only for better performancedue to no calls to realloc) // expand buffer (one way only for better performancedue to no calls to realloc)
UINT32 mono_frames_size = frames * __DIV_8(stream->out.wavex.Format.wBitsPerSample); UINT32 mono_frames_size = frames * __DIV_8(stream->out.wavex.Format.wBitsPerSample);
if (mono_frames_size > stream->out.monoBufferSize) if (mono_frames_size > stream->out.monoBufferSize)
{ stream->out.monoBuffer = realloc(stream->out.monoBuffer, (stream->out.monoBufferSize = mono_frames_size));
stream->out.monoBuffer = realloc(stream->out.monoBuffer, mono_frames_size);
}
// feed to user // process
processor[S_OUTPUT].processor(NULL, 0, (BYTE *)stream->out.monoBuffer, frames, processor[S_OUTPUT].userData); processor[S_OUTPUT].processor(NULL, 0, (BYTE *)stream->out.monoBuffer, frames, processor[S_OUTPUT].userData);
// mix 1 to 2 channels // mix 1 to 2 channels
@ -3302,7 +3339,7 @@ static HRESULT FillOutputBuffer(PaWasapiStream *stream, PaWasapiHostProcessor *p
} }
// ------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------
static HRESULT PollInputBuffer(PaWasapiStream *stream, PaWasapiHostProcessor *processor) static HRESULT ProcessInputBuffer(PaWasapiStream *stream, PaWasapiHostProcessor *processor)
{ {
HRESULT hr; HRESULT hr;
UINT32 frames; UINT32 frames;
@ -3333,15 +3370,13 @@ static HRESULT PollInputBuffer(PaWasapiStream *stream, PaWasapiHostProcessor *pr
// expand buffer (one way only for better performancedue to no calls to realloc) // expand buffer (one way only for better performancedue to no calls to realloc)
UINT32 mono_frames_size = frames * __DIV_8(stream->in.wavex.Format.wBitsPerSample); UINT32 mono_frames_size = frames * __DIV_8(stream->in.wavex.Format.wBitsPerSample);
if (mono_frames_size > stream->in.monoBufferSize) if (mono_frames_size > stream->in.monoBufferSize)
{ stream->in.monoBuffer = realloc(stream->in.monoBuffer, (stream->in.monoBufferSize = mono_frames_size));
stream->in.monoBuffer = realloc(stream->in.monoBuffer, mono_frames_size);
}
// feed to user
processor[S_INPUT].processor((BYTE *)stream->in.monoBuffer, frames, NULL, 0, processor[S_INPUT].userData);
// mix 1 to 2 channels // mix 1 to 2 channels
stream->in.monoMixer(data, stream->in.monoBuffer, frames); stream->in.monoMixer(stream->in.monoBuffer, data, frames);
// process
processor[S_INPUT].processor((BYTE *)stream->in.monoBuffer, frames, NULL, 0, processor[S_INPUT].userData);
#undef __DIV_8 #undef __DIV_8
} }
@ -3480,7 +3515,7 @@ PA_THREAD_FUNC ProcThreadEvent(void *param)
} }
// Preload buffer before start // Preload buffer before start
if ((hr = FillOutputBuffer(stream, processor, stream->out.framesPerHostCallback)) != S_OK) if ((hr = ProcessOutputBuffer(stream, processor, stream->out.framesPerHostCallback)) != S_OK)
{ {
LogHostError(hr); LogHostError(hr);
goto thread_error; goto thread_error;
@ -3524,14 +3559,14 @@ PA_THREAD_FUNC ProcThreadEvent(void *param)
case WAIT_OBJECT_0 + S_INPUT: { case WAIT_OBJECT_0 + S_INPUT: {
if (stream->cclient == NULL) if (stream->cclient == NULL)
break; break;
PollInputBuffer(stream, processor); ProcessInputBuffer(stream, processor);
break; } break; }
// Output stream // Output stream
case WAIT_OBJECT_0 + S_OUTPUT: { case WAIT_OBJECT_0 + S_OUTPUT: {
if (stream->rclient == NULL) if (stream->rclient == NULL)
break; break;
FillOutputBuffer(stream, processor, stream->out.framesPerHostCallback); ProcessOutputBuffer(stream, processor, stream->out.framesPerHostCallback);
break; } break; }
} }
} }
@ -3558,6 +3593,27 @@ thread_error:
goto thread_end; goto thread_end;
} }
// ------------------------------------------------------------------------------------------
static HRESULT PollGetOutputFramesAvailable(PaWasapiStream *stream, UINT32 *available)
{
HRESULT hr;
UINT32 frames = stream->out.framesPerHostCallback,
padding = 0;
(*available) = 0;
// get read position
if ((hr = IAudioClient_GetCurrentPadding(stream->out.client, &padding)) != S_OK)
return LogHostError(hr);
// get available
frames -= padding;
// set
(*available) = frames;
return hr;
}
// ------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------
PA_THREAD_FUNC ProcThreadPoll(void *param) PA_THREAD_FUNC ProcThreadPoll(void *param)
{ {
@ -3619,11 +3675,15 @@ PA_THREAD_FUNC ProcThreadPoll(void *param)
} }
} }
// Preload buffer (obligatory, othervise ->Start() will fail) // Preload buffer (obligatory, othervise ->Start() will fail), avoid processing
if ((hr = FillOutputBuffer(stream, processor, stream->out.framesPerHostCallback)) != S_OK) // when in full-duplex mode as it requires input processing as well
if (!PA_WASAPI__IS_FULLDUPLEX(stream))
{ {
LogHostError(hr); if ((hr = ProcessOutputBuffer(stream, processor, stream->out.framesPerHostCallback)) != S_OK)
goto thread_error; {
LogHostError(hr);
goto thread_error;
}
} }
// Start // Start
@ -3640,46 +3700,136 @@ PA_THREAD_FUNC ProcThreadPoll(void *param)
// Notify: thread started // Notify: thread started
SetEvent(stream->hThreadStart); SetEvent(stream->hThreadStart);
// Processing Loop if (!PA_WASAPI__IS_FULLDUPLEX(stream))
while (WaitForSingleObject(stream->hCloseRequest, sleep_ms) == WAIT_TIMEOUT) {
{ // Processing Loop
for (i = 0; i < S_COUNT; ++i) while (WaitForSingleObject(stream->hCloseRequest, sleep_ms) == WAIT_TIMEOUT)
{ {
// Process S_INPUT/S_OUTPUT for (i = 0; i < S_COUNT; ++i)
switch (i)
{ {
// Input stream // Process S_INPUT/S_OUTPUT
case S_INPUT: { switch (i)
if (stream->cclient == NULL)
break;
PollInputBuffer(stream, processor);
break; }
// Output stream
case S_OUTPUT: {
UINT32 frames, padding;
if (stream->rclient == NULL)
break;
frames = stream->out.framesPerHostCallback;
// Get Read position
padding = 0;
hr = IAudioClient_GetCurrentPadding(stream->out.client, &padding);
if (hr != S_OK)
{ {
LogHostError(hr); // Input stream
break; case S_INPUT: {
if (stream->cclient == NULL)
break;
ProcessInputBuffer(stream, processor);
break; }
// Output stream
case S_OUTPUT: {
UINT32 frames;
if (stream->rclient == NULL)
break;
// get available frames
if ((hr = PollGetOutputFramesAvailable(stream, &frames)) != S_OK)
{
LogHostError(hr);
break;
}
// output
if (frames != 0)
ProcessOutputBuffer(stream, processor, frames);
break; }
} }
}
}
}
else
{
// Processing Loop
while (WaitForSingleObject(stream->hCloseRequest, sleep_ms) == WAIT_TIMEOUT)
{
UINT32 i_frames = 0, i_processed = 0;
BYTE *i_data = NULL, *o_data = NULL;
DWORD i_flags = 0;
UINT32 o_frames = 0;
// Fill buffer // get host input buffer
frames -= padding; if ((hr = IAudioCaptureClient_GetBuffer(stream->cclient, &i_data, &i_frames, &i_flags, NULL, NULL)) != S_OK)
if (frames != 0) {
FillOutputBuffer(stream, processor, frames); if (hr == AUDCLNT_S_BUFFER_EMPTY)
continue; // no data in capture buffer
break; } LogHostError(hr);
break;
}
// get available frames
if ((hr = PollGetOutputFramesAvailable(stream, &o_frames)) != S_OK)
{
LogHostError(hr);
break;
}
// process equal ammount of frames
if (o_frames >= i_frames)
{
// process input ammount of frames
UINT32 o_processed = i_frames;
// get host output buffer
if ((hr = IAudioRenderClient_GetBuffer(stream->rclient, o_processed, &o_data)) == S_OK)
{
// processed amount of i_frames
i_processed = i_frames;
// convert output mono
if (stream->out.monoMixer)
{
#define __DIV_8(v) ((v) >> 3) //!< (v / 8)
UINT32 mono_frames_size = o_processed * __DIV_8(stream->out.wavex.Format.wBitsPerSample);
#undef __DIV_8
// expand buffer (one way only for better performance due to no calls to realloc)
if (mono_frames_size > stream->out.monoBufferSize)
stream->out.monoBuffer = realloc(stream->out.monoBuffer, (stream->out.monoBufferSize = mono_frames_size));
}
// convert input mono
if (stream->in.monoMixer)
{
#define __DIV_8(v) ((v) >> 3) //!< (v / 8)
UINT32 mono_frames_size = i_processed * __DIV_8(stream->in.wavex.Format.wBitsPerSample);
#undef __DIV_8
// expand buffer (one way only for better performance due to no calls to realloc)
if (mono_frames_size > stream->in.monoBufferSize)
stream->in.monoBuffer = realloc(stream->in.monoBuffer, (stream->in.monoBufferSize = mono_frames_size));
// mix 2 to 1 input channels
stream->in.monoMixer(stream->in.monoBuffer, i_data, i_processed);
// replace buffer pointer
i_data = (BYTE *)stream->in.monoBuffer;
}
// process
processor[S_FULLDUPLEX].processor(i_data, i_processed, o_data, o_processed, processor[S_FULLDUPLEX].userData);
// mix 1 to 2 output channels
if (stream->out.monoBuffer)
stream->out.monoMixer(o_data, stream->out.monoBuffer, o_processed);
// release host output buffer
if ((hr = IAudioRenderClient_ReleaseBuffer(stream->rclient, o_processed, 0)) != S_OK)
LogHostError(hr);
}
else
{
if (stream->out.shareMode != AUDCLNT_SHAREMODE_SHARED)
LogHostError(hr); // be silent in shared mode, try again next time
}
}
// release host input buffer
if ((hr = IAudioCaptureClient_ReleaseBuffer(stream->cclient, i_processed)) != S_OK)
{
LogHostError(hr);
break;
} }
} }
} }