wasapi:
- 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:
parent
d031474d95
commit
d2568e4ae7
1 changed files with 238 additions and 88 deletions
|
|
@ -308,7 +308,7 @@ PA_THREAD_FUNC ProcThreadPoll(void *param);
|
|||
|
||||
#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]))
|
||||
|
||||
|
|
@ -317,6 +317,8 @@ enum { S_INPUT = 0, S_OUTPUT, S_COUNT };
|
|||
#define PA_SKELETON_SET_LAST_HOST_ERROR( errorCode, errorText ) \
|
||||
PaUtil_SetLastHostErrorInfo( paWASAPI, errorCode, errorText )
|
||||
|
||||
#define PA_WASAPI__IS_FULLDUPLEX(STREAM) ((STREAM)->in.client && (STREAM)->out.client)
|
||||
|
||||
#ifndef IF_FAILED_JUMP
|
||||
#define IF_FAILED_JUMP(hr, label) if(FAILED(hr)) goto label;
|
||||
#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 from = __from;\
|
||||
TYPE * __restrict end = from + count;\
|
||||
|
|
@ -896,46 +901,58 @@ static BOOL UseWOW64Workaround()
|
|||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------
|
||||
static void _MixMonoToStereo_8(void *__to, void *__from, UINT32 count)
|
||||
{
|
||||
_WASAPI_MONO_TO_STEREO_MIXER(BYTE);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------
|
||||
static void _MixMonoToStereo_16(void *__to, void *__from, UINT32 count)
|
||||
{
|
||||
_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;
|
||||
#define _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1(TYPE)\
|
||||
TYPE * __restrict to = (TYPE *)__to;\
|
||||
TYPE * __restrict from = (TYPE *)__from;\
|
||||
TYPE * __restrict end = to + count;\
|
||||
while (to != end)\
|
||||
{\
|
||||
*to ++ = (TYPE)((float)(from[0] + from[1]) * 0.5f);\
|
||||
from += 2;\
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
@ -1670,7 +1687,15 @@ static PaError GetClosestFormat(IAudioClient *myClient, double sampleRate,
|
|||
|
||||
// Validate Channel count
|
||||
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
|
||||
if ((bitsPerSample = PaSampleFormatToBitsPerSample(params->sampleFormat)) == 0)
|
||||
|
|
@ -1924,7 +1949,7 @@ static HRESULT CreateAudioClient(PaWasapiSubStream *pSubStream, PaWasapiDeviceIn
|
|||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
LogHostError(hr = AUDCLNT_E_UNSUPPORTED_FORMAT);
|
||||
|
|
@ -2074,7 +2099,7 @@ static const REFERENCE_TIME MAX_BUFFER_POLL_DURATION = 2000 * 10000;
|
|||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
LogHostError(hr = AUDCLNT_E_UNSUPPORTED_FORMAT);
|
||||
|
|
@ -2223,10 +2248,15 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
|
|||
stream->in.streamFlags = AUDCLNT_STREAMFLAGS_EVENTCALLBACK;
|
||||
if (paWasapi->useWOW64Workaround)
|
||||
stream->in.streamFlags = 0; // polling interface
|
||||
else
|
||||
if (streamCallback == NULL)
|
||||
stream->in.streamFlags = 0; // polling interface
|
||||
else
|
||||
if ((inputStreamInfo != NULL) && (inputStreamInfo->flags & paWinWasapiPolling))
|
||||
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
|
||||
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)
|
||||
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
|
||||
{
|
||||
|
|
@ -2332,10 +2362,15 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
|
|||
stream->out.streamFlags = AUDCLNT_STREAMFLAGS_EVENTCALLBACK;
|
||||
if (paWasapi->useWOW64Workaround)
|
||||
stream->out.streamFlags = 0; // polling interface
|
||||
else
|
||||
if (streamCallback == NULL)
|
||||
stream->out.streamFlags = 0; // polling interface
|
||||
else
|
||||
if ((outputStreamInfo != NULL) && (outputStreamInfo->flags & paWinWasapiPolling))
|
||||
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
|
||||
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)
|
||||
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
|
||||
{
|
||||
|
|
@ -2410,6 +2445,10 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
|
|||
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
|
||||
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;
|
||||
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)
|
||||
UINT32 mono_frames_size = frames * __DIV_8(stream->out.wavex.Format.wBitsPerSample);
|
||||
if (mono_frames_size > stream->out.monoBufferSize)
|
||||
{
|
||||
stream->out.monoBuffer = realloc(stream->out.monoBuffer, mono_frames_size);
|
||||
}
|
||||
stream->out.monoBuffer = realloc(stream->out.monoBuffer, (stream->out.monoBufferSize = mono_frames_size));
|
||||
|
||||
// feed to user
|
||||
// process
|
||||
processor[S_OUTPUT].processor(NULL, 0, (BYTE *)stream->out.monoBuffer, frames, processor[S_OUTPUT].userData);
|
||||
|
||||
// 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;
|
||||
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)
|
||||
UINT32 mono_frames_size = frames * __DIV_8(stream->in.wavex.Format.wBitsPerSample);
|
||||
if (mono_frames_size > stream->in.monoBufferSize)
|
||||
{
|
||||
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);
|
||||
stream->in.monoBuffer = realloc(stream->in.monoBuffer, (stream->in.monoBufferSize = mono_frames_size));
|
||||
|
||||
// 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
|
||||
}
|
||||
|
|
@ -3480,7 +3515,7 @@ PA_THREAD_FUNC ProcThreadEvent(void *param)
|
|||
}
|
||||
|
||||
// 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);
|
||||
goto thread_error;
|
||||
|
|
@ -3524,14 +3559,14 @@ PA_THREAD_FUNC ProcThreadEvent(void *param)
|
|||
case WAIT_OBJECT_0 + S_INPUT: {
|
||||
if (stream->cclient == NULL)
|
||||
break;
|
||||
PollInputBuffer(stream, processor);
|
||||
ProcessInputBuffer(stream, processor);
|
||||
break; }
|
||||
|
||||
// Output stream
|
||||
case WAIT_OBJECT_0 + S_OUTPUT: {
|
||||
if (stream->rclient == NULL)
|
||||
break;
|
||||
FillOutputBuffer(stream, processor, stream->out.framesPerHostCallback);
|
||||
ProcessOutputBuffer(stream, processor, stream->out.framesPerHostCallback);
|
||||
break; }
|
||||
}
|
||||
}
|
||||
|
|
@ -3558,6 +3593,27 @@ thread_error:
|
|||
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)
|
||||
{
|
||||
|
|
@ -3619,11 +3675,15 @@ PA_THREAD_FUNC ProcThreadPoll(void *param)
|
|||
}
|
||||
}
|
||||
|
||||
// Preload buffer (obligatory, othervise ->Start() will fail)
|
||||
if ((hr = FillOutputBuffer(stream, processor, stream->out.framesPerHostCallback)) != S_OK)
|
||||
// Preload buffer (obligatory, othervise ->Start() will fail), avoid processing
|
||||
// when in full-duplex mode as it requires input processing as well
|
||||
if (!PA_WASAPI__IS_FULLDUPLEX(stream))
|
||||
{
|
||||
LogHostError(hr);
|
||||
goto thread_error;
|
||||
if ((hr = ProcessOutputBuffer(stream, processor, stream->out.framesPerHostCallback)) != S_OK)
|
||||
{
|
||||
LogHostError(hr);
|
||||
goto thread_error;
|
||||
}
|
||||
}
|
||||
|
||||
// Start
|
||||
|
|
@ -3640,46 +3700,136 @@ PA_THREAD_FUNC ProcThreadPoll(void *param)
|
|||
// Notify: thread started
|
||||
SetEvent(stream->hThreadStart);
|
||||
|
||||
// Processing Loop
|
||||
while (WaitForSingleObject(stream->hCloseRequest, sleep_ms) == WAIT_TIMEOUT)
|
||||
{
|
||||
for (i = 0; i < S_COUNT; ++i)
|
||||
if (!PA_WASAPI__IS_FULLDUPLEX(stream))
|
||||
{
|
||||
// Processing Loop
|
||||
while (WaitForSingleObject(stream->hCloseRequest, sleep_ms) == WAIT_TIMEOUT)
|
||||
{
|
||||
// Process S_INPUT/S_OUTPUT
|
||||
switch (i)
|
||||
for (i = 0; i < S_COUNT; ++i)
|
||||
{
|
||||
// Input stream
|
||||
case S_INPUT: {
|
||||
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)
|
||||
// Process S_INPUT/S_OUTPUT
|
||||
switch (i)
|
||||
{
|
||||
LogHostError(hr);
|
||||
break;
|
||||
// Input stream
|
||||
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
|
||||
frames -= padding;
|
||||
if (frames != 0)
|
||||
FillOutputBuffer(stream, processor, frames);
|
||||
// get host input buffer
|
||||
if ((hr = IAudioCaptureClient_GetBuffer(stream->cclient, &i_data, &i_frames, &i_flags, NULL, NULL)) != S_OK)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue