wasapi:
- reimplemented full-duplex processing to address changes to latency calculations (please note Exclusive mode due to some unknown reason will likely not be able to provide good audio quality yet) - fixed Pa_GetStreamInfo for WASAPI device if stream is opened as full-duplex (thanks to Reid Bishop for pointing to the bug)
This commit is contained in:
parent
1c289f924e
commit
a243ad431c
1 changed files with 150 additions and 7 deletions
|
|
@ -924,7 +924,7 @@ typedef enum EMixerDir { MIX_DIR__1TO2, MIX_DIR__2TO1 } EMixerDir;
|
|||
TYPE * __restrict end = to + count;\
|
||||
while (to != end)\
|
||||
{\
|
||||
*to ++ = (TYPE)((float)(from[0] + from[1]) * 0.70710678118654752440084436210485f/*1/sqrt(2)*/);\
|
||||
*to ++ = (TYPE)((float)(from[0] + from[1]) * 0.5f);\
|
||||
from += 2;\
|
||||
}
|
||||
|
||||
|
|
@ -2093,6 +2093,10 @@ static HRESULT CreateAudioClient(PaWasapiStream *pStream, PaWasapiSubStream *pSu
|
|||
}
|
||||
#endif
|
||||
|
||||
// For full-duplex output resize buffer to be the same as for input
|
||||
if (output && fullDuplex)
|
||||
framesPerLatency = pStream->in.framesPerHostCallback;
|
||||
|
||||
// Avoid 0 frames
|
||||
if (framesPerLatency == 0)
|
||||
framesPerLatency = MakeFramesFromHns(pInfo->DefaultDevicePeriod, pSub->wavex.Format.nSamplesPerSec);
|
||||
|
|
@ -2663,13 +2667,16 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
|
|||
|
||||
if (outputParameters && inputParameters)
|
||||
{
|
||||
// serious problem #1
|
||||
if (stream->in.period != stream->out.period)
|
||||
// serious problem #1 - No, Not a problem, especially concerning Exclusive mode.
|
||||
// Input device in exclusive mode somehow is getting large buffer always, thus we
|
||||
// adjust Output latency to reflect it, thus period will differ but playback will be
|
||||
// normal.
|
||||
/*if (stream->in.period != stream->out.period)
|
||||
{
|
||||
PRINT(("WASAPI: OpenStream: period discrepancy\n"));
|
||||
LogPaError(result = paBadIODeviceCombination);
|
||||
goto error;
|
||||
}
|
||||
}*/
|
||||
|
||||
// serious problem #2 - No, Not a problem, as framesPerHostCallback take into account
|
||||
// sample size while it is not a problem for PA full-duplex, we must care of
|
||||
|
|
@ -2723,12 +2730,12 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
|
|||
|
||||
// Set Input latency
|
||||
stream->streamRepresentation.streamInfo.inputLatency =
|
||||
PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor)
|
||||
((double)PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor) / sampleRate)
|
||||
+ ((inputParameters)?stream->in.latency_seconds : 0);
|
||||
|
||||
// Set Output latency
|
||||
stream->streamRepresentation.streamInfo.outputLatency =
|
||||
PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor)
|
||||
((double)PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor) / sampleRate)
|
||||
+ ((outputParameters)?stream->out.latency_seconds : 0);
|
||||
|
||||
// Set SR
|
||||
|
|
@ -3948,8 +3955,9 @@ PA_THREAD_FUNC ProcThreadPoll(void *param)
|
|||
}
|
||||
else
|
||||
{
|
||||
#if 0
|
||||
// Processing Loop
|
||||
while (WaitForSingleObject(stream->hCloseRequest, sleep_ms) == WAIT_TIMEOUT)
|
||||
while (WaitForSingleObject(stream->hCloseRequest, 1) == WAIT_TIMEOUT)
|
||||
{
|
||||
UINT32 i_frames = 0, i_processed = 0;
|
||||
BYTE *i_data = NULL, *o_data = NULL, *o_data_host = NULL;
|
||||
|
|
@ -4056,6 +4064,141 @@ PA_THREAD_FUNC ProcThreadPoll(void *param)
|
|||
break;
|
||||
}
|
||||
}
|
||||
#else
|
||||
// Processing Loop
|
||||
//sleep_ms = 1;
|
||||
while (WaitForSingleObject(stream->hCloseRequest, sleep_ms) == WAIT_TIMEOUT)
|
||||
{
|
||||
UINT32 i_frames = 0, i_processed = 0;
|
||||
BYTE *i_data = NULL, *o_data = NULL, *o_data_host = NULL;
|
||||
DWORD i_flags = 0;
|
||||
UINT32 o_frames = 0;
|
||||
//BOOL repeat = FALSE;
|
||||
|
||||
// going below 1 msec resolution, switching between 1 ms and no waiting
|
||||
//if (stream->in.shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE)
|
||||
// sleep_ms = !sleep_ms;
|
||||
|
||||
// get available frames
|
||||
if ((hr = PollGetOutputFramesAvailable(stream, &o_frames)) != S_OK)
|
||||
{
|
||||
LogHostError(hr);
|
||||
break;
|
||||
}
|
||||
|
||||
while (o_frames != 0)
|
||||
{
|
||||
// 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)
|
||||
break; // no data in capture buffer
|
||||
|
||||
LogHostError(hr);
|
||||
break;
|
||||
}
|
||||
|
||||
//PA_DEBUG(("full-duplex: o_frames[%d] i_frames[%d] repeat[%d]\n", o_frames, i_frames, repeat));
|
||||
//repeat = TRUE;
|
||||
|
||||
// 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;
|
||||
o_data_host = o_data;
|
||||
|
||||
// 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));
|
||||
if (stream->out.monoBuffer == NULL)
|
||||
{
|
||||
LogPaError(paInsufficientMemory);
|
||||
goto thread_error;
|
||||
}
|
||||
}
|
||||
|
||||
// replace buffer pointer
|
||||
o_data = (BYTE *)stream->out.monoBuffer;
|
||||
}
|
||||
|
||||
// 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));
|
||||
if (stream->in.monoBuffer == NULL)
|
||||
{
|
||||
LogPaError(paInsufficientMemory);
|
||||
goto thread_error;
|
||||
}
|
||||
}
|
||||
|
||||
// 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_host, stream->out.monoBuffer, o_processed);
|
||||
|
||||
// release host output buffer
|
||||
if ((hr = IAudioRenderClient_ReleaseBuffer(stream->rclient, o_processed, 0)) != S_OK)
|
||||
LogHostError(hr);
|
||||
|
||||
o_frames -= o_processed;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (stream->out.shareMode != AUDCLNT_SHAREMODE_SHARED)
|
||||
LogHostError(hr); // be silent in shared mode, try again next time
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
i_processed = 0;
|
||||
goto fd_release_buffer_in;
|
||||
}
|
||||
|
||||
fd_release_buffer_in:
|
||||
|
||||
// release host input buffer
|
||||
if ((hr = IAudioCaptureClient_ReleaseBuffer(stream->cclient, i_processed)) != S_OK)
|
||||
{
|
||||
LogHostError(hr);
|
||||
break;
|
||||
}
|
||||
|
||||
// break processing, input hasn't been accumulated yet
|
||||
if (i_processed == 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
thread_end:
|
||||
|
|
|
|||
Loading…
Reference in a new issue